mirror of
https://github.com/home-assistant/core.git
synced 2025-07-22 20:57:21 +00:00
Refactored device components
This commit is contained in:
parent
f0c6ac1aa3
commit
89100d14c8
@ -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)
|
||||||
|
@ -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
|
||||||
|
@ -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):
|
||||||
|
@ -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()]
|
|
||||||
|
@ -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)]
|
|
||||||
|
@ -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)
|
||||||
|
@ -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):
|
||||||
|
@ -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):
|
||||||
|
@ -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):
|
||||||
|
@ -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()]
|
|
||||||
|
@ -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. """
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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):
|
||||||
|
107
homeassistant/helpers/device_component.py
Normal file
107
homeassistant/helpers/device_component.py
Normal 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)
|
@ -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
|
|
||||||
|
@ -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)
|
||||||
|
@ -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')
|
||||||
|
|
||||||
|
@ -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. """
|
||||||
|
Loading…
x
Reference in New Issue
Block a user