Refactored device components

This commit is contained in:
Paulus Schoutsen 2015-03-01 01:35:58 -08:00
parent f0c6ac1aa3
commit 89100d14c8
20 changed files with 239 additions and 339 deletions

View File

@ -52,17 +52,18 @@ import logging
import os import os
import csv import csv
from homeassistant.loader import get_component from homeassistant.helpers.device_component import DeviceComponent
import homeassistant.util as util import homeassistant.util as util
from homeassistant.const import ( from homeassistant.const import (
STATE_ON, SERVICE_TURN_ON, SERVICE_TURN_OFF, ATTR_ENTITY_ID) STATE_ON, SERVICE_TURN_ON, SERVICE_TURN_OFF, ATTR_ENTITY_ID)
from homeassistant.helpers import ( from homeassistant.helpers import extract_entity_ids
generate_entity_id, extract_entity_ids, config_per_platform)
from homeassistant.components import group, discovery, wink from homeassistant.components import group, discovery, wink
DOMAIN = "light" DOMAIN = "light"
DEPENDENCIES = [] DEPENDENCIES = []
SCAN_INTERVAL = 30
GROUP_NAME_ALL_LIGHTS = 'all lights' GROUP_NAME_ALL_LIGHTS = 'all lights'
ENTITY_ID_ALL_LIGHTS = group.ENTITY_ID_FORMAT.format('all_lights') ENTITY_ID_ALL_LIGHTS = group.ENTITY_ID_FORMAT.format('all_lights')
@ -140,6 +141,13 @@ def turn_off(hass, entity_id=None, transition=None):
def setup(hass, config): def setup(hass, config):
""" Exposes light control via statemachine and services. """ """ Exposes light control via statemachine and services. """
component = DeviceComponent(
_LOGGER, DOMAIN, hass, SCAN_INTERVAL, DISCOVERY_PLATFORMS,
GROUP_NAME_ALL_LIGHTS)
component.setup(config)
lights = component.devices
# Load built-in profiles and custom profiles # Load built-in profiles and custom profiles
profile_paths = [os.path.join(os.path.dirname(__file__), profile_paths = [os.path.join(os.path.dirname(__file__),
LIGHT_PROFILES_FILE), LIGHT_PROFILES_FILE),
@ -168,55 +176,6 @@ def setup(hass, config):
return False return False
# Dict to track entity_id -> lights
lights = {}
# Track all lights in a group
light_group = group.Group(hass, GROUP_NAME_ALL_LIGHTS, user_defined=False)
def add_lights(new_lights):
""" Add lights to the component to track. """
for light in new_lights:
if light is not None and light not in lights.values():
light.hass = hass
light.entity_id = generate_entity_id(
ENTITY_ID_FORMAT, light.name, lights.keys())
lights[light.entity_id] = light
light.update_ha_state()
light_group.update_tracked_entity_ids(lights.keys())
for p_type, p_config in config_per_platform(config, DOMAIN, _LOGGER):
platform = get_component(ENTITY_ID_FORMAT.format(p_type))
if platform is None:
_LOGGER.error("Unknown type specified: %s", p_type)
platform.setup_platform(hass, p_config, add_lights)
def update_lights_state(now):
""" Update the states of all the lights. """
if lights:
_LOGGER.info("Updating light states")
for light in lights.values():
if light.should_poll:
light.update_ha_state(True)
update_lights_state(None)
def light_discovered(service, info):
""" Called when a light is discovered. """
platform = get_component(
ENTITY_ID_FORMAT.format(DISCOVERY_PLATFORMS[service]))
platform.setup_platform(hass, {}, add_lights, info)
discovery.listen(hass, DISCOVERY_PLATFORMS.keys(), light_discovered)
def handle_light_service(service): def handle_light_service(service):
""" Hande a turn light on or off service call. """ """ Hande a turn light on or off service call. """
# Get and validate data # Get and validate data
@ -303,9 +262,6 @@ def setup(hass, config):
for light in target_lights: for light in target_lights:
light.update_ha_state(True) light.update_ha_state(True)
# Update light state every 30 seconds
hass.track_time_change(update_lights_state, second=[0, 30])
# Listen for light on and light off service calls # Listen for light on and light off service calls
hass.services.register(DOMAIN, SERVICE_TURN_ON, hass.services.register(DOMAIN, SERVICE_TURN_ON,
handle_light_service) handle_light_service)

View File

@ -4,68 +4,29 @@ homeassistant.components.sensor
Component to interface with various sensors that can be monitored. Component to interface with various sensors that can be monitored.
""" """
import logging import logging
from datetime import timedelta
from homeassistant.loader import get_component from homeassistant.helpers.device_component import DeviceComponent
import homeassistant.util as util from homeassistant.components import wink, zwave
from homeassistant.helpers import (
platform_devices_from_config, generate_entity_id)
from homeassistant.components import discovery, wink, zwave
DOMAIN = 'sensor' DOMAIN = 'sensor'
DEPENDENCIES = [] DEPENDENCIES = []
SCAN_INTERVAL = 30
ENTITY_ID_FORMAT = DOMAIN + '.{}' ENTITY_ID_FORMAT = DOMAIN + '.{}'
MIN_TIME_BETWEEN_SCANS = timedelta(seconds=1)
# Maps discovered services to their platforms # Maps discovered services to their platforms
DISCOVERY_PLATFORMS = { DISCOVERY_PLATFORMS = {
wink.DISCOVER_SENSORS: 'wink', wink.DISCOVER_SENSORS: 'wink',
zwave.DISCOVER_SENSORS: 'zwave', zwave.DISCOVER_SENSORS: 'zwave',
} }
_LOGGER = logging.getLogger(__name__)
def setup(hass, config): def setup(hass, config):
""" Track states and offer events for sensors. """ """ Track states and offer events for sensors. """
logger = logging.getLogger(__name__) component = DeviceComponent(
logging.getLogger(__name__), DOMAIN, hass, SCAN_INTERVAL,
DISCOVERY_PLATFORMS)
sensors = platform_devices_from_config( component.setup(config)
config, DOMAIN, hass, ENTITY_ID_FORMAT, logger)
@util.Throttle(MIN_TIME_BETWEEN_SCANS)
def update_sensor_states(now):
""" Update states of all sensors. """
if sensors:
for sensor in sensors.values():
if sensor.should_poll:
sensor.update_ha_state(True)
update_sensor_states(None)
def sensor_discovered(service, info):
""" Called when a sensor is discovered. """
platform = get_component("{}.{}".format(
DOMAIN, DISCOVERY_PLATFORMS[service]))
discovered = platform.devices_discovered(hass, config, info)
for sensor in discovered:
if sensor is not None and sensor not in sensors.values():
sensor.hass = hass
sensor.entity_id = generate_entity_id(
ENTITY_ID_FORMAT, sensor.name, sensors.keys())
sensors[sensor.entity_id] = sensor
sensor.update_ha_state()
discovery.listen(hass, DISCOVERY_PLATFORMS.keys(), sensor_discovered)
# Fire every 3 seconds
hass.track_time_change(update_sensor_states, second=range(0, 60, 3))
return True return True

View File

@ -4,23 +4,13 @@ from homeassistant.const import (
TEMP_CELCIUS, ATTR_UNIT_OF_MEASUREMENT, ATTR_FRIENDLY_NAME) TEMP_CELCIUS, ATTR_UNIT_OF_MEASUREMENT, ATTR_FRIENDLY_NAME)
def get_devices(hass, config): # pylint: disable=unused-argument
""" Find and return Wink sensors. """ def setup_platform(hass, config, add_devices, discovery_info=None):
""" Sets up the Demo sensors. """
return get_sensors() add_devices([
def devices_discovered(hass, config, info):
""" Called when a device is discovered. """
return get_sensors()
def get_sensors():
""" Returns the Wink sensors. """
return [
DemoSensor('Outside Temperature', 15.6, TEMP_CELCIUS), DemoSensor('Outside Temperature', 15.6, TEMP_CELCIUS),
DemoSensor('Outside Humidity', 54, '%'), DemoSensor('Outside Humidity', 54, '%'),
] ])
class DemoSensor(Device): class DemoSensor(Device):

View File

@ -8,26 +8,17 @@ from homeassistant.components.wink import WinkSensorDevice
from homeassistant.const import CONF_ACCESS_TOKEN from homeassistant.const import CONF_ACCESS_TOKEN
def get_devices(hass, config): def setup_platform(hass, config, add_devices, discovery_info=None):
""" Find and return Wink sensors. """ """ Sets up the Wink platform. """
token = config.get(CONF_ACCESS_TOKEN) if discovery_info is None:
token = config.get(CONF_ACCESS_TOKEN)
if token is None: if token is None:
logging.getLogger(__name__).error( logging.getLogger(__name__).error(
"Missing wink access_token - " "Missing wink access_token - "
"get one at https://winkbearertoken.appspot.com/") "get one at https://winkbearertoken.appspot.com/")
return [] return
pywink.set_bearer_token(token) pywink.set_bearer_token(token)
return get_sensors() add_devices(WinkSensorDevice(sensor) for sensor in pywink.get_sensors())
def devices_discovered(hass, config, info):
""" Called when a device is discovered. """
return get_sensors()
def get_sensors():
""" Returns the Wink sensors. """
return [WinkSensorDevice(sensor) for sensor in pywink.get_sensors()]

View File

@ -15,6 +15,23 @@ from homeassistant.const import (
TEMP_CELCIUS, TEMP_FAHRENHEIT, ATTR_LOCATION) TEMP_CELCIUS, TEMP_FAHRENHEIT, ATTR_LOCATION)
def setup_platform(hass, config, add_devices, discovery_info=None):
""" Sets up Z-Wave sensors. """
node = zwave.NETWORK.nodes[discovery_info[zwave.ATTR_NODE_ID]]
value = node.values[discovery_info[zwave.ATTR_VALUE_ID]]
value.set_change_verified(False)
if zwave.NETWORK.controller.node_id not in node.groups[1].associations:
node.groups[1].add_association(zwave.NETWORK.controller.node_id)
if value.command_class == zwave.COMMAND_CLASS_SENSOR_BINARY:
return [ZWaveBinarySensor(value)]
elif value.command_class == zwave.COMMAND_CLASS_SENSOR_MULTILEVEL:
return [ZWaveMultilevelSensor(value)]
class ZWaveSensor(Device): class ZWaveSensor(Device):
""" Represents a Z-Wave sensor. """ """ Represents a Z-Wave sensor. """
def __init__(self, sensor_value): def __init__(self, sensor_value):
@ -116,20 +133,3 @@ class ZWaveMultilevelSensor(ZWaveSensor):
return TEMP_FAHRENHEIT return TEMP_FAHRENHEIT
else: else:
return unit return unit
def devices_discovered(hass, config, info):
""" Called when a device is discovered. """
node = zwave.NETWORK.nodes[info[zwave.ATTR_NODE_ID]]
value = node.values[info[zwave.ATTR_VALUE_ID]]
value.set_change_verified(False)
if zwave.NETWORK.controller.node_id not in node.groups[1].associations:
node.groups[1].add_association(zwave.NETWORK.controller.node_id)
if value.command_class == zwave.COMMAND_CLASS_SENSOR_BINARY:
return [ZWaveBinarySensor(value)]
elif value.command_class == zwave.COMMAND_CLASS_SENSOR_MULTILEVEL:
return [ZWaveMultilevelSensor(value)]

View File

@ -6,16 +6,16 @@ Component to interface with various switches that can be controlled remotely.
import logging import logging
from datetime import timedelta from datetime import timedelta
from homeassistant.loader import get_component from homeassistant.helpers.device_component import DeviceComponent
import homeassistant.util as util
from homeassistant.const import ( from homeassistant.const import (
STATE_ON, SERVICE_TURN_ON, SERVICE_TURN_OFF, ATTR_ENTITY_ID) STATE_ON, SERVICE_TURN_ON, SERVICE_TURN_OFF, ATTR_ENTITY_ID)
from homeassistant.helpers import ( from homeassistant.helpers import extract_entity_ids
generate_entity_id, extract_entity_ids, platform_devices_from_config)
from homeassistant.components import group, discovery, wink from homeassistant.components import group, discovery, wink
DOMAIN = 'switch' DOMAIN = 'switch'
DEPENDENCIES = [] DEPENDENCIES = []
SCAN_INTERVAL = 30
GROUP_NAME_ALL_SWITCHES = 'all switches' GROUP_NAME_ALL_SWITCHES = 'all switches'
ENTITY_ID_ALL_SWITCHES = group.ENTITY_ID_FORMAT.format('all_switches') ENTITY_ID_ALL_SWITCHES = group.ENTITY_ID_FORMAT.format('all_switches')
@ -59,47 +59,12 @@ def turn_off(hass, entity_id=None):
def setup(hass, config): def setup(hass, config):
""" Track states and offer events for switches. """ """ Track states and offer events for switches. """
logger = logging.getLogger(__name__) component = DeviceComponent(
_LOGGER, DOMAIN, hass, SCAN_INTERVAL, DISCOVERY_PLATFORMS,
GROUP_NAME_ALL_SWITCHES)
component.setup(config)
switches = platform_devices_from_config( switches = component.devices
config, DOMAIN, hass, ENTITY_ID_FORMAT, logger)
@util.Throttle(MIN_TIME_BETWEEN_SCANS)
def update_states(now):
""" Update states of all switches. """
if switches:
logger.info("Updating switch states")
for switch in switches.values():
switch.update_ha_state(True)
update_states(None)
# Track all switches in a group
switch_group = group.Group(
hass, GROUP_NAME_ALL_SWITCHES, switches.keys(), False)
def switch_discovered(service, info):
""" Called when a switch is discovered. """
platform = get_component("{}.{}".format(
DOMAIN, DISCOVERY_PLATFORMS[service]))
discovered = platform.devices_discovered(hass, config, info)
for switch in discovered:
if switch is not None and switch not in switches.values():
switch.hass = hass
switch.entity_id = generate_entity_id(
ENTITY_ID_FORMAT, switch.name, switches.keys())
switches[switch.entity_id] = switch
switch.update_ha_state()
switch_group.update_tracked_entity_ids(switches.keys())
discovery.listen(hass, DISCOVERY_PLATFORMS.keys(), switch_discovered)
def handle_switch_service(service): def handle_switch_service(service):
""" Handles calls to the switch services. """ """ Handles calls to the switch services. """
@ -118,9 +83,6 @@ def setup(hass, config):
switch.update_ha_state(True) switch.update_ha_state(True)
# Update state every 30 seconds
hass.track_time_change(update_states, second=[0, 30])
hass.services.register(DOMAIN, SERVICE_TURN_OFF, handle_switch_service) hass.services.register(DOMAIN, SERVICE_TURN_OFF, handle_switch_service)
hass.services.register(DOMAIN, SERVICE_TURN_ON, handle_switch_service) hass.services.register(DOMAIN, SERVICE_TURN_ON, handle_switch_service)

View File

@ -3,22 +3,13 @@ from homeassistant.helpers import ToggleDevice
from homeassistant.const import STATE_ON, STATE_OFF, DEVICE_DEFAULT_NAME from homeassistant.const import STATE_ON, STATE_OFF, DEVICE_DEFAULT_NAME
def get_devices(hass, config): # pylint: disable=unused-argument
def setup_platform(hass, config, add_devices_callback, discovery_info=None):
""" Find and return demo switches. """ """ Find and return demo switches. """
return get_switches() add_devices_callback([
def devices_discovered(hass, config, info):
""" Called when a device is discovered. """
return get_switches()
def get_switches():
""" Returns the Wink switches. """
return [
DemoSwitch('Ceiling', STATE_ON), DemoSwitch('Ceiling', STATE_ON),
DemoSwitch('AC', STATE_OFF) DemoSwitch('AC', STATE_OFF)
] ])
class DemoSwitch(ToggleDevice): class DemoSwitch(ToggleDevice):

View File

@ -7,14 +7,15 @@ from homeassistant.helpers import ToggleDevice
import tellcore.constants as tellcore_constants import tellcore.constants as tellcore_constants
def get_devices(hass, config): # pylint: disable=unused-argument
""" Find and return Tellstick switches. """ def setup_platform(hass, config, add_devices_callback, discovery_info=None):
""" Find and return tellstick switches. """
try: try:
import tellcore.telldus as telldus import tellcore.telldus as telldus
except ImportError: except ImportError:
logging.getLogger(__name__).exception( logging.getLogger(__name__).exception(
"Failed to import tellcore") "Failed to import tellcore")
return [] return
core = telldus.TelldusCore() core = telldus.TelldusCore()
switches_and_lights = core.devices() switches_and_lights = core.devices()
@ -25,7 +26,7 @@ def get_devices(hass, config):
if not switch.methods(tellcore_constants.TELLSTICK_DIM): if not switch.methods(tellcore_constants.TELLSTICK_DIM):
switches.append(TellstickSwitchDevice(switch)) switches.append(TellstickSwitchDevice(switch))
return switches add_devices_callback(switches)
class TellstickSwitchDevice(ToggleDevice): class TellstickSwitchDevice(ToggleDevice):

View File

@ -6,50 +6,36 @@ from homeassistant.components.switch import (
ATTR_TODAY_MWH, ATTR_CURRENT_POWER_MWH) ATTR_TODAY_MWH, ATTR_CURRENT_POWER_MWH)
def get_devices(hass, config): # pylint: disable=unused-argument
""" Find and return WeMo switches. """ def setup_platform(hass, config, add_devices_callback, discovery_info=None):
""" Find and return wemo switches. """
pywemo, _ = get_pywemo()
if pywemo is None:
return []
logging.getLogger(__name__).info("Scanning for WeMo devices")
switches = pywemo.discover_devices()
# Filter out the switches and wrap in WemoSwitch object
return [WemoSwitch(switch) for switch in switches
if isinstance(switch, pywemo.Switch)]
def devices_discovered(hass, config, info):
""" Called when a device is discovered. """
_, discovery = get_pywemo()
if discovery is None:
return []
device = discovery.device_from_description(info)
return [] if device is None else [WemoSwitch(device)]
def get_pywemo():
""" Tries to import PyWemo. """
try: try:
# pylint: disable=no-name-in-module, import-error # pylint: disable=no-name-in-module, import-error
import homeassistant.external.pywemo.pywemo as pywemo import homeassistant.external.pywemo.pywemo as pywemo
import homeassistant.external.pywemo.pywemo.discovery as discovery import homeassistant.external.pywemo.pywemo.discovery as discovery
return pywemo, discovery
except ImportError: except ImportError:
logging.getLogger(__name__).exception(( logging.getLogger(__name__).exception((
"Failed to import pywemo. " "Failed to import pywemo. "
"Did you maybe not run `git submodule init` " "Did you maybe not run `git submodule init` "
"and `git submodule update`?")) "and `git submodule update`?"))
return None, None return
if discovery_info is not None:
device = discovery.device_from_description(discovery_info)
if device:
add_devices_callback([device])
return
logging.getLogger(__name__).info("Scanning for WeMo devices")
switches = pywemo.discover_devices()
# Filter out the switches and wrap in WemoSwitch object
add_devices_callback(
[WemoSwitch(switch) for switch in switches
if isinstance(switch, pywemo.Switch)])
class WemoSwitch(ToggleDevice): class WemoSwitch(ToggleDevice):

View File

@ -8,26 +8,17 @@ from homeassistant.components.wink import WinkToggleDevice
from homeassistant.const import CONF_ACCESS_TOKEN from homeassistant.const import CONF_ACCESS_TOKEN
def get_devices(hass, config): def setup_platform(hass, config, add_devices, discovery_info=None):
""" Find and return Wink switches. """ """ Sets up the Wink platform. """
token = config.get(CONF_ACCESS_TOKEN) if discovery_info is None:
token = config.get(CONF_ACCESS_TOKEN)
if token is None: if token is None:
logging.getLogger(__name__).error( logging.getLogger(__name__).error(
"Missing wink access_token - " "Missing wink access_token - "
"get one at https://winkbearertoken.appspot.com/") "get one at https://winkbearertoken.appspot.com/")
return [] return
pywink.set_bearer_token(token) pywink.set_bearer_token(token)
return get_switches() add_devices(WinkToggleDevice(switch) for switch in pywink.get_switches())
def devices_discovered(hass, config, info):
""" Called when a device is discovered. """
return get_switches()
def get_switches():
""" Returns the Wink switches. """
return [WinkToggleDevice(switch) for switch in pywink.get_switches()]

View File

@ -5,23 +5,21 @@ homeassistant.components.thermostat
Provides functionality to interact with thermostats. Provides functionality to interact with thermostats.
""" """
import logging import logging
from datetime import timedelta
from homeassistant.helpers import ( from homeassistant.helpers.device_component import DeviceComponent
extract_entity_ids, platform_devices_from_config)
import homeassistant.util as util import homeassistant.util as util
from homeassistant.helpers import Device from homeassistant.helpers import Device, extract_entity_ids
from homeassistant.const import ( from homeassistant.const import (
ATTR_ENTITY_ID, ATTR_TEMPERATURE, ATTR_UNIT_OF_MEASUREMENT, ATTR_ENTITY_ID, ATTR_TEMPERATURE, ATTR_UNIT_OF_MEASUREMENT,
STATE_ON, STATE_OFF) STATE_ON, STATE_OFF)
DOMAIN = "thermostat" DOMAIN = "thermostat"
ENTITY_ID_FORMAT = DOMAIN + ".{}"
MIN_TIME_BETWEEN_SCANS = timedelta(seconds=10)
DEPENDENCIES = [] DEPENDENCIES = []
ENTITY_ID_FORMAT = DOMAIN + ".{}"
SCAN_INTERVAL = 60
SERVICE_SET_AWAY_MODE = "set_away_mode" SERVICE_SET_AWAY_MODE = "set_away_mode"
SERVICE_SET_TEMPERATURE = "set_temperature" SERVICE_SET_TEMPERATURE = "set_temperature"
@ -31,22 +29,10 @@ ATTR_AWAY_MODE = "away_mode"
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
def turn_away_mode_on(hass, entity_id=None): def set_away_mode(hass, away_mode, entity_id=None):
""" Turn all or specified thermostat away mode on. """ """ Turn all or specified thermostat away mode on. """
data = { data = {
ATTR_AWAY_MODE: True ATTR_AWAY_MODE: away_mode
}
if entity_id:
data[ATTR_ENTITY_ID] = entity_id
hass.services.call(DOMAIN, SERVICE_SET_AWAY_MODE, data)
def turn_away_mode_off(hass, entity_id=None):
""" Turn all or specified thermostat away mode off. """
data = {
ATTR_AWAY_MODE: False
} }
if entity_id: if entity_id:
@ -67,27 +53,10 @@ def set_temperature(hass, temperature, entity_id=None):
def setup(hass, config): def setup(hass, config):
""" Setup thermostats. """ """ Setup thermostats. """
component = DeviceComponent(_LOGGER, DOMAIN, hass, SCAN_INTERVAL)
component.setup(config)
logger = logging.getLogger(__name__) thermostats = component.devices
thermostats = platform_devices_from_config(
config, DOMAIN, hass, ENTITY_ID_FORMAT, _LOGGER)
if not thermostats:
return False
@util.Throttle(MIN_TIME_BETWEEN_SCANS)
def update_state(now):
""" Update thermostat state. """
logger.info("Updating thermostat state")
for thermostat in thermostats.values():
if thermostat.should_poll:
thermostat.update_ha_state(True)
# Update state every minute
hass.track_time_change(update_state, second=[0])
update_state(None)
def thermostat_service(service): def thermostat_service(service):
""" Handles calls to the services. """ """ Handles calls to the services. """

View File

@ -6,14 +6,12 @@ from homeassistant.components.thermostat import ThermostatDevice
from homeassistant.const import TEMP_CELCIUS, TEMP_FAHRENHEIT from homeassistant.const import TEMP_CELCIUS, TEMP_FAHRENHEIT
# pylint: disable=unused-argument def setup_platform(hass, config, add_devices, discovery_info=None):
def get_devices(hass, config): """ Sets up the Demo thermostats. """
""" Gets thermostats. """ add_devices([
return [
DemoThermostat("Nest", 21, TEMP_CELCIUS, False, 19), DemoThermostat("Nest", 21, TEMP_CELCIUS, False, 19),
DemoThermostat("Thermostat", 68, TEMP_FAHRENHEIT, True, 77), DemoThermostat("Thermostat", 68, TEMP_FAHRENHEIT, True, 77),
] ])
# pylint: disable=too-many-arguments # pylint: disable=too-many-arguments

View File

@ -69,11 +69,11 @@ TOL_TEMP = 0.3
# pylint: disable=unused-argument # pylint: disable=unused-argument
def get_devices(hass, config): def setup_platform(hass, config, add_devices, discovery_info=None):
""" Gets thermostats. """ """ Sets up the heat control thermostat. """
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
return [HeatControl(hass, config, logger)] add_devices([HeatControl(hass, config, logger)])
# pylint: disable=too-many-instance-attributes # pylint: disable=too-many-instance-attributes

View File

@ -7,8 +7,9 @@ from homeassistant.components.thermostat import ThermostatDevice
from homeassistant.const import (CONF_USERNAME, CONF_PASSWORD, TEMP_CELCIUS) from homeassistant.const import (CONF_USERNAME, CONF_PASSWORD, TEMP_CELCIUS)
def get_devices(hass, config): # pylint: disable=unused-argument
""" Gets Nest thermostats. """ def setup_platform(hass, config, add_devices, discovery_info=None):
""" Sets up the nest thermostat. """
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
username = config.get(CONF_USERNAME) username = config.get(CONF_USERNAME)
@ -17,7 +18,7 @@ def get_devices(hass, config):
if username is None or password is None: if username is None or password is None:
logger.error("Missing required configuration items %s or %s", logger.error("Missing required configuration items %s or %s",
CONF_USERNAME, CONF_PASSWORD) CONF_USERNAME, CONF_PASSWORD)
return [] return
try: try:
import nest import nest
@ -26,14 +27,15 @@ def get_devices(hass, config):
"Error while importing dependency nest. " "Error while importing dependency nest. "
"Did you maybe not install the python-nest dependency?") "Did you maybe not install the python-nest dependency?")
return [] return
napi = nest.Nest(username, password) napi = nest.Nest(username, password)
return [ add_devices([
NestThermostat(structure, device) NestThermostat(structure, device)
for structure in napi.structures for structure in napi.structures
for device in structure.devices] for device in structure.devices
])
class NestThermostat(ThermostatDevice): class NestThermostat(ThermostatDevice):

View File

@ -0,0 +1,107 @@
"""
Provides helpers for components that handle devices.
"""
from homeassistant.loader import get_component
from homeassistant.helpers import generate_entity_id, config_per_platform
from homeassistant.components import group, discovery
class DeviceComponent(object):
# pylint: disable=too-many-instance-attributes
# pylint: disable=too-many-arguments,too-few-public-methods
"""
Helper class that will help a device component manage its devices.
"""
def __init__(self, logger, domain, hass, scan_interval,
discovery_platforms=None, group_name=None):
self.logger = logger
self.hass = hass
self.domain = domain
self.entity_id_format = domain + '.{}'
self.scan_interval = scan_interval
self.discovery_platforms = discovery_platforms
self.group_name = group_name
self.devices = {}
self.group = None
def setup(self, config):
"""
Sets up a full device component:
- Loads the platforms from the config
- Will update devices on an interval
- Will listen for supported discovered platforms
"""
# only setup group if name is given
if self.group_name is None:
self.group = None
else:
self.group = group.Group(self.hass, self.group_name,
user_defined=False)
# Look in config for Domain, Domain 2, Domain 3 etc and load them
for p_type, p_config in \
config_per_platform(config, self.domain, self.logger):
self._setup_platform(p_type, p_config)
self.hass.track_time_change(self._update_device_states,
second=range(0, 60, self.scan_interval))
if self.discovery_platforms:
discovery.listen(self.hass, self.discovery_platforms.keys(),
self._device_discovered)
def _update_device_states(self, now):
""" Update the states of all the lights. """
self.logger.info("Updating %s states", self.domain)
for device in self.devices.values():
if device.should_poll:
device.update_ha_state(True)
def _device_discovered(self, service, info):
""" Called when a device is discovered. """
if service not in self.discovery_platforms:
return
self._setup_platform(self.discovery_platforms[service], {}, info)
def _add_devices(self, new_devices):
"""
Takes in a list of new devices. For each device will see if it already
exists. If not, will add it, set it up and push the first state.
"""
for device in new_devices:
if device is not None and device not in self.devices.values():
device.hass = self.hass
device.entity_id = generate_entity_id(
self.entity_id_format, device.name, self.devices.keys())
self.devices[device.entity_id] = device
device.update_ha_state()
if self.group is not None:
self.group.update_tracked_entity_ids(self.devices.keys())
def _setup_platform(self, platform_type, config, discovery_info=None):
""" Tries to setup a platform for this component. """
platform_name = '{}.{}'.format(self.domain, platform_type)
platform = get_component(platform_name)
if platform is None:
self.logger.error('Unable to find platform %s', platform_type)
return
try:
platform.setup_platform(
self.hass, config, self._add_devices, discovery_info)
except (AttributeError, TypeError):
# AttributeError if setup_platform does not exist
# TypeError if wrong number of argumnets for setup_platform
self.logger.exception(
"Error setting up %s", platform_type)

View File

@ -27,8 +27,3 @@ def init(empty=False):
def setup_platform(hass, config, add_devices_callback, discovery_info=None): def setup_platform(hass, config, add_devices_callback, discovery_info=None):
""" Returns mock devices. """ """ Returns mock devices. """
add_devices_callback(DEVICES) add_devices_callback(DEVICES)
def get_lights():
""" Helper method to get current light objects. """
return DEVICES

View File

@ -24,6 +24,6 @@ def init(empty=False):
] ]
def get_switches(hass, config): def setup_platform(hass, config, add_devices_callback, discovery_info=None):
""" Returns mock devices. """ """ Find and return test switches. """
return DEVICES add_devices_callback(DEVICES)

View File

@ -103,7 +103,7 @@ class TestLight(unittest.TestCase):
self.assertTrue( self.assertTrue(
light.setup(self.hass, {light.DOMAIN: {CONF_TYPE: 'test'}})) light.setup(self.hass, {light.DOMAIN: {CONF_TYPE: 'test'}}))
dev1, dev2, dev3 = platform.get_lights() dev1, dev2, dev3 = platform.DEVICES
# Test init # Test init
self.assertTrue(light.is_on(self.hass, dev1.entity_id)) self.assertTrue(light.is_on(self.hass, dev1.entity_id))
@ -244,7 +244,7 @@ class TestLight(unittest.TestCase):
self.hass, {light.DOMAIN: {CONF_TYPE: 'test'}} self.hass, {light.DOMAIN: {CONF_TYPE: 'test'}}
)) ))
dev1, dev2, dev3 = platform.get_lights() dev1, dev2, dev3 = platform.DEVICES
light.turn_on(self.hass, dev1.entity_id, profile='test') light.turn_on(self.hass, dev1.entity_id, profile='test')

View File

@ -30,7 +30,7 @@ class TestSwitch(unittest.TestCase):
# Switch 1 is ON, switch 2 is OFF # Switch 1 is ON, switch 2 is OFF
self.switch_1, self.switch_2, self.switch_3 = \ self.switch_1, self.switch_2, self.switch_3 = \
platform.get_switches(None, None) platform.DEVICES
def tearDown(self): # pylint: disable=invalid-name def tearDown(self): # pylint: disable=invalid-name
""" Stop down stuff we started. """ """ Stop down stuff we started. """