Add gateway wrapper, fix discovery and callbacks

* Add gateway wrapper by subclassing serial gateway.
* Fix platform setup with discovery service.
* Fix platform callback functions with callback factory.
This commit is contained in:
MartinHjelmare 2015-12-31 05:48:23 +01:00
parent be25ea4f09
commit 69ed6fe6e7
5 changed files with 244 additions and 216 deletions

View File

@ -29,14 +29,19 @@ mysensors:
""" """
import logging import logging
try:
import mysensors.mysensors as mysensors
except ImportError:
mysensors = None
from homeassistant.helpers import validate_config from homeassistant.helpers import validate_config
import homeassistant.bootstrap as bootstrap import homeassistant.bootstrap as bootstrap
from homeassistant.const import ( from homeassistant.const import (
EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_START,
EVENT_HOMEASSISTANT_STOP, EVENT_HOMEASSISTANT_STOP,
TEMP_CELCIUS, EVENT_PLATFORM_DISCOVERED, ATTR_SERVICE, ATTR_DISCOVERED,
CONF_PLATFORM) TEMP_CELCIUS,)
CONF_GATEWAYS = 'gateways' CONF_GATEWAYS = 'gateways'
CONF_PORT = 'port' CONF_PORT = 'port'
@ -45,7 +50,6 @@ CONF_PERSISTENCE = 'persistence'
CONF_PERSISTENCE_FILE = 'persistence_file' CONF_PERSISTENCE_FILE = 'persistence_file'
CONF_VERSION = 'version' CONF_VERSION = 'version'
DEFAULT_VERSION = '1.4' DEFAULT_VERSION = '1.4'
VERSION = None
DOMAIN = 'mysensors' DOMAIN = 'mysensors'
DEPENDENCIES = [] DEPENDENCIES = []
@ -56,86 +60,54 @@ _LOGGER = logging.getLogger(__name__)
ATTR_NODE_ID = 'node_id' ATTR_NODE_ID = 'node_id'
ATTR_CHILD_ID = 'child_id' ATTR_CHILD_ID = 'child_id'
COMPONENTS_WITH_MYSENSORS_PLATFORM = [
'sensor',
'switch',
]
IS_METRIC = None
CONST = None
GATEWAYS = None GATEWAYS = None
SCAN_INTERVAL = 30
DISCOVER_SENSORS = "mysensors.sensors"
DISCOVER_SWITCHES = "mysensors.switches"
# Maps discovered services to their platforms
DISCOVERY_COMPONENTS = [
('sensor', DISCOVER_SENSORS),
('switch', DISCOVER_SWITCHES),
]
def setup(hass, config): def setup(hass, config):
"""Setup the MySensors component.""" """Setup the MySensors component."""
# pylint: disable=too-many-locals # pylint: disable=too-many-locals
import mysensors.mysensors as mysensors
if not validate_config(config, if not validate_config(config,
{DOMAIN: [CONF_GATEWAYS]}, {DOMAIN: [CONF_GATEWAYS]},
_LOGGER): _LOGGER):
return False return False
global VERSION global mysensors # pylint: disable=invalid-name
VERSION = config[DOMAIN].get(CONF_VERSION, DEFAULT_VERSION) if mysensors is None:
import mysensors.mysensors as _mysensors
mysensors = _mysensors
global CONST version = str(config[DOMAIN].get(CONF_VERSION, DEFAULT_VERSION))
if VERSION == '1.5': is_metric = (hass.config.temperature_unit == TEMP_CELCIUS)
import mysensors.const_15 as const
CONST = const
else:
import mysensors.const_14 as const
CONST = const
# Just assume celcius means that the user wants metric for now. def setup_gateway(port, persistence, persistence_file, version):
# It may make more sense to make this a global config option in the future.
global IS_METRIC
IS_METRIC = (hass.config.temperature_unit == TEMP_CELCIUS)
# Setup mysensors platforms
mysensors_config = config.copy()
for component in COMPONENTS_WITH_MYSENSORS_PLATFORM:
mysensors_config[component] = {CONF_PLATFORM: 'mysensors'}
if not bootstrap.setup_component(hass, component, mysensors_config):
return False
import homeassistant.components.sensor.mysensors as mysensors_sensor
import homeassistant.components.switch.mysensors as mysensors_switch
def callback_factory(gateway, port, devices):
"""Return a new callback function. Run once per gateway setup."""
def node_update(update_type, nid):
"""Callback for node updates from the MySensors gateway."""
_LOGGER.info('update %s: node %s', update_type, nid)
mysensors_sensor.sensor_update(gateway, port, devices, nid)
mysensors_switch.sensor_update(gateway, port, devices, nid)
return node_update
def setup_gateway(port, persistence, persistence_file):
"""Return gateway after setup of the gateway.""" """Return gateway after setup of the gateway."""
devices = {} # keep track of devices added to HA gateway = GatewayWrapper(
gateway = mysensors.SerialGateway(port, port, persistence, persistence_file, version)
persistence=persistence, # pylint: disable=attribute-defined-outside-init
persistence_file=persistence_file, gateway.metric = is_metric
protocol_version=VERSION)
gateway.event_callback = callback_factory(gateway, port, devices)
gateway.metric = IS_METRIC
gateway.debug = config[DOMAIN].get(CONF_DEBUG, False) gateway.debug = config[DOMAIN].get(CONF_DEBUG, False)
def gw_start(event):
"""Callback to trigger start of gateway and any persistence."""
gateway.start() gateway.start()
def persistence_update(event):
"""Callback to trigger update from persistence file."""
for nid in gateway.sensors:
gateway.event_callback('persistence', nid)
if persistence:
hass.bus.listen_once(
EVENT_HOMEASSISTANT_START, persistence_update)
hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP,
lambda event: gateway.stop()) lambda event: gateway.stop())
if persistence:
for node_id in gateway.sensors:
gateway.event_callback('persistence', node_id)
hass.bus.listen_once(EVENT_HOMEASSISTANT_START, gw_start)
return gateway return gateway
@ -146,48 +118,99 @@ def setup(hass, config):
if isinstance(conf_gateways, dict): if isinstance(conf_gateways, dict):
conf_gateways = [conf_gateways] conf_gateways = [conf_gateways]
persistence = config[DOMAIN].get(CONF_PERSISTENCE, True) persistence = config[DOMAIN].get(CONF_PERSISTENCE, True)
for index, gway in enumerate(conf_gateways): for index, gway in enumerate(conf_gateways):
port = gway[CONF_PORT] port = gway[CONF_PORT]
persistence_file = gway.get( persistence_file = gway.get(
CONF_PERSISTENCE_FILE, CONF_PERSISTENCE_FILE,
hass.config.path('mysensors{}.pickle'.format(index + 1))) hass.config.path('mysensors{}.pickle'.format(index + 1)))
GATEWAYS[port] = setup_gateway( GATEWAYS[port] = setup_gateway(
port, persistence, persistence_file) port, persistence, persistence_file, version)
for (component, discovery_service) in DISCOVERY_COMPONENTS:
# Ensure component is loaded
if not bootstrap.setup_component(hass, component, config):
return False
# Fire discovery event
hass.bus.fire(EVENT_PLATFORM_DISCOVERED, {
ATTR_SERVICE: discovery_service,
ATTR_DISCOVERED: {}})
return True return True
def mysensors_update(platform_type): def pf_callback_factory(
"""Decorator for callback function for mysensor updates.""" s_types, v_types, devices, add_devices, entity_class):
def wrapper(gateway, port, devices, nid): """Return a new callback for the platform."""
"""Wrapper function in the decorator.""" def mysensors_callback(gateway, node_id):
if gateway.sensors[nid].sketch_name is None: """Callback for mysensors platform."""
_LOGGER.info('No sketch_name: node %s', nid) if gateway.sensors[node_id].sketch_name is None:
_LOGGER.info('No sketch_name: node %s', node_id)
return return
if nid not in devices: # previously discovered, just update state with latest info
devices[nid] = {} if node_id in devices:
node = devices[nid] for entity in devices[node_id]:
# Get platform specific S_TYPES, V_TYPES, class and add_devices. entity.update_ha_state(True)
(platform_s_types, return
platform_v_types,
platform_class, # First time we see this node, detect sensors
add_devices) = platform_type(gateway, port, devices, nid) for child in gateway.sensors[node_id].children.values():
for child_id, child in gateway.sensors[nid].children.items():
if child_id not in node:
node[child_id] = {}
for value_type in child.values.keys():
if (value_type not in node[child_id] and
child.type in platform_s_types and
value_type in platform_v_types):
name = '{} {}.{}'.format( name = '{} {}.{}'.format(
gateway.sensors[nid].sketch_name, nid, child.id) gateway.sensors[node_id].sketch_name, node_id, child.id)
node[child_id][value_type] = platform_class(
port, nid, child_id, name, value_type) for value_type in child.values.keys():
_LOGGER.info('adding new device: %s', if child.type not in s_types or value_type not in v_types:
node[child_id][value_type]) continue
add_devices([node[child_id][value_type]])
if (child.type in platform_s_types and devices[node_id].append(
value_type in platform_v_types): entity_class(gateway, node_id, child.id, name, value_type))
node[child_id][value_type].update_sensor( if devices[node_id]:
child.values, gateway.sensors[nid].battery_level) _LOGGER.info('adding new devices: %s', devices[node_id])
return wrapper add_devices(devices[node_id])
for entity in devices[node_id]:
entity.update_ha_state(True)
return mysensors_callback
class GatewayWrapper(mysensors.SerialGateway):
"""Gateway wrapper class, by subclassing serial gateway."""
def __init__(self, port, persistence, persistence_file, version):
"""Setup class attributes on instantiation.
Args:
port: Port of gateway to wrap.
persistence: Persistence, true or false.
persistence_file: File to store persistence info.
version: Version of mysensors API.
Attributes:
version (str): Version of mysensors API.
platform_callbacks (list): Callback functions, one per platform.
const (module): Mysensors API constants.
"""
super().__init__(port, event_callback=self.callback_factory(),
persistence=persistence,
persistence_file=persistence_file,
protocol_version=version)
self.version = version
self.platform_callbacks = []
self.const = self.get_const()
def get_const(self):
"""Get mysensors API constants."""
if self.version == '1.5':
import mysensors.const_15 as const
else:
import mysensors.const_14 as const
return const
def callback_factory(self):
"""Return a new callback function."""
def node_update(update_type, node_id):
"""Callback for node updates from the MySensors gateway."""
_LOGGER.info('update %s: node %s', update_type, node_id)
for callback in self.platform_callbacks:
callback(self, node_id)
return node_update

View File

@ -9,7 +9,8 @@ https://home-assistant.io/components/sensor/
import logging import logging
from homeassistant.helpers.entity_component import EntityComponent from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.components import wink, zwave, isy994, verisure, ecobee from homeassistant.components import (
wink, zwave, isy994, verisure, ecobee, mysensors)
DOMAIN = 'sensor' DOMAIN = 'sensor'
SCAN_INTERVAL = 30 SCAN_INTERVAL = 30
@ -22,7 +23,8 @@ DISCOVERY_PLATFORMS = {
zwave.DISCOVER_SENSORS: 'zwave', zwave.DISCOVER_SENSORS: 'zwave',
isy994.DISCOVER_SENSORS: 'isy994', isy994.DISCOVER_SENSORS: 'isy994',
verisure.DISCOVER_SENSORS: 'verisure', verisure.DISCOVER_SENSORS: 'verisure',
ecobee.DISCOVER_SENSORS: 'ecobee' ecobee.DISCOVER_SENSORS: 'ecobee',
mysensors.DISCOVER_SENSORS: 'mysensors',
} }

View File

@ -7,6 +7,7 @@ For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/sensor.mysensors/ https://home-assistant.io/components/sensor.mysensors/
""" """
import logging import logging
from collections import defaultdict
from homeassistant.helpers.entity import Entity from homeassistant.helpers.entity import Entity
@ -20,74 +21,71 @@ import homeassistant.components.mysensors as mysensors
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
DEPENDENCIES = [] DEPENDENCIES = []
ADD_DEVICES = None
S_TYPES = None
V_TYPES = None
@mysensors.mysensors_update
def sensor_update(gateway, port, devices, nid):
"""Internal callback for sensor updates."""
return (S_TYPES, V_TYPES, MySensorsSensor, ADD_DEVICES)
def setup_platform(hass, config, add_devices, discovery_info=None): def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup the mysensors platform for sensors.""" """Setup the mysensors platform for sensors."""
# Define the S_TYPES and V_TYPES that the platform should handle as states. # Only act if loaded via mysensors by discovery event.
global ADD_DEVICES # Otherwise gateway is not setup.
ADD_DEVICES = add_devices if discovery_info is None:
global S_TYPES return
S_TYPES = [
mysensors.CONST.Presentation.S_TEMP, for gateway in mysensors.GATEWAYS.values():
mysensors.CONST.Presentation.S_HUM, # Define the S_TYPES and V_TYPES that the platform should handle as
mysensors.CONST.Presentation.S_BARO, # states.
mysensors.CONST.Presentation.S_WIND, s_types = [
mysensors.CONST.Presentation.S_RAIN, gateway.const.Presentation.S_TEMP,
mysensors.CONST.Presentation.S_UV, gateway.const.Presentation.S_HUM,
mysensors.CONST.Presentation.S_WEIGHT, gateway.const.Presentation.S_BARO,
mysensors.CONST.Presentation.S_POWER, gateway.const.Presentation.S_WIND,
mysensors.CONST.Presentation.S_DISTANCE, gateway.const.Presentation.S_RAIN,
mysensors.CONST.Presentation.S_LIGHT_LEVEL, gateway.const.Presentation.S_UV,
mysensors.CONST.Presentation.S_IR, gateway.const.Presentation.S_WEIGHT,
mysensors.CONST.Presentation.S_WATER, gateway.const.Presentation.S_POWER,
mysensors.CONST.Presentation.S_AIR_QUALITY, gateway.const.Presentation.S_DISTANCE,
mysensors.CONST.Presentation.S_CUSTOM, gateway.const.Presentation.S_LIGHT_LEVEL,
mysensors.CONST.Presentation.S_DUST, gateway.const.Presentation.S_IR,
mysensors.CONST.Presentation.S_SCENE_CONTROLLER, gateway.const.Presentation.S_WATER,
gateway.const.Presentation.S_AIR_QUALITY,
gateway.const.Presentation.S_CUSTOM,
gateway.const.Presentation.S_DUST,
gateway.const.Presentation.S_SCENE_CONTROLLER,
] ]
not_v_types = [ not_v_types = [
mysensors.CONST.SetReq.V_ARMED, gateway.const.SetReq.V_ARMED,
mysensors.CONST.SetReq.V_LIGHT, gateway.const.SetReq.V_LIGHT,
mysensors.CONST.SetReq.V_LOCK_STATUS, gateway.const.SetReq.V_LOCK_STATUS,
] ]
if float(mysensors.VERSION) >= 1.5: if float(gateway.version) >= 1.5:
S_TYPES.extend([ s_types.extend([
mysensors.CONST.Presentation.S_COLOR_SENSOR, gateway.const.Presentation.S_COLOR_SENSOR,
mysensors.CONST.Presentation.S_MULTIMETER, gateway.const.Presentation.S_MULTIMETER,
]) ])
not_v_types.extend([mysensors.CONST.SetReq.V_STATUS, ]) not_v_types.extend([gateway.const.SetReq.V_STATUS, ])
global V_TYPES v_types = [member for member in gateway.const.SetReq
V_TYPES = [member for member in mysensors.CONST.SetReq
if member.value not in not_v_types] if member.value not in not_v_types]
devices = defaultdict(list)
gateway.platform_callbacks.append(mysensors.pf_callback_factory(
s_types, v_types, devices, add_devices, MySensorsSensor))
class MySensorsSensor(Entity): class MySensorsSensor(Entity):
"""Represent the value of a MySensors child node.""" """Represent the value of a MySensors child node."""
# pylint: disable=too-many-arguments, too-many-instance-attributes # pylint: disable=too-many-arguments
def __init__(self, port, node_id, child_id, name, value_type): def __init__(self, gateway, node_id, child_id, name, value_type):
"""Setup class attributes on instantiation. """Setup class attributes on instantiation.
Args: Args:
port (str): Gateway port. gateway (str): Gateway.
node_id (str): Id of node. node_id (str): Id of node.
child_id (str): Id of child. child_id (str): Id of child.
name (str): Entity name. name (str): Entity name.
value_type (str): Value type of child. Value is entity state. value_type (str): Value type of child. Value is entity state.
Attributes: Attributes:
port (str): Gateway port. gateway (str): Gateway.
node_id (str): Id of node. node_id (str): Id of node.
child_id (str): Id of child. child_id (str): Id of child.
_name (str): Entity name. _name (str): Entity name.
@ -95,7 +93,7 @@ class MySensorsSensor(Entity):
battery_level (int): Node battery level. battery_level (int): Node battery level.
_values (dict): Child values. Non state values set as state attributes. _values (dict): Child values. Non state values set as state attributes.
""" """
self.port = port self.gateway = gateway
self.node_id = node_id self.node_id = node_id
self.child_id = child_id self.child_id = child_id
self._name = name self._name = name
@ -124,25 +122,25 @@ class MySensorsSensor(Entity):
def unit_of_measurement(self): def unit_of_measurement(self):
"""Unit of measurement of this entity.""" """Unit of measurement of this entity."""
# pylint:disable=too-many-return-statements # pylint:disable=too-many-return-statements
if self.value_type == mysensors.CONST.SetReq.V_TEMP: if self.value_type == self.gateway.const.SetReq.V_TEMP:
return TEMP_CELCIUS if mysensors.IS_METRIC else TEMP_FAHRENHEIT return TEMP_CELCIUS if self.gateway.metric else TEMP_FAHRENHEIT
elif self.value_type == mysensors.CONST.SetReq.V_HUM or \ elif self.value_type == self.gateway.const.SetReq.V_HUM or \
self.value_type == mysensors.CONST.SetReq.V_DIMMER or \ self.value_type == self.gateway.const.SetReq.V_DIMMER or \
self.value_type == mysensors.CONST.SetReq.V_PERCENTAGE or \ self.value_type == self.gateway.const.SetReq.V_PERCENTAGE or \
self.value_type == mysensors.CONST.SetReq.V_LIGHT_LEVEL: self.value_type == self.gateway.const.SetReq.V_LIGHT_LEVEL:
return '%' return '%'
elif self.value_type == mysensors.CONST.SetReq.V_WATT: elif self.value_type == self.gateway.const.SetReq.V_WATT:
return 'W' return 'W'
elif self.value_type == mysensors.CONST.SetReq.V_KWH: elif self.value_type == self.gateway.const.SetReq.V_KWH:
return 'kWh' return 'kWh'
elif self.value_type == mysensors.CONST.SetReq.V_VOLTAGE: elif self.value_type == self.gateway.const.SetReq.V_VOLTAGE:
return 'V' return 'V'
elif self.value_type == mysensors.CONST.SetReq.V_CURRENT: elif self.value_type == self.gateway.const.SetReq.V_CURRENT:
return 'A' return 'A'
elif self.value_type == mysensors.CONST.SetReq.V_IMPEDANCE: elif self.value_type == self.gateway.const.SetReq.V_IMPEDANCE:
return 'ohm' return 'ohm'
elif mysensors.CONST.SetReq.V_UNIT_PREFIX in self._values: elif self.gateway.const.SetReq.V_UNIT_PREFIX in self._values:
return self._values[mysensors.CONST.SetReq.V_UNIT_PREFIX] return self._values[self.gateway.const.SetReq.V_UNIT_PREFIX]
return None return None
@property @property
@ -168,16 +166,17 @@ class MySensorsSensor(Entity):
return data return data
def update_sensor(self, values, battery_level): def update(self):
"""Update the controller with the latest values from a sensor.""" """Update the controller with the latest values from a sensor."""
for value_type, value in values.items(): node = self.gateway.sensors[self.node_id]
child = node.children[self.child_id]
for value_type, value in child.values.items():
_LOGGER.info( _LOGGER.info(
"%s: value_type %s, value = %s", self._name, value_type, value) "%s: value_type %s, value = %s", self._name, value_type, value)
if value_type == mysensors.CONST.SetReq.V_TRIPPED: if value_type == self.gateway.const.SetReq.V_TRIPPED:
self._values[value_type] = STATE_ON if int( self._values[value_type] = STATE_ON if int(
value) == 1 else STATE_OFF value) == 1 else STATE_OFF
else: else:
self._values[value_type] = value self._values[value_type] = value
self.battery_level = battery_level self.battery_level = node.battery_level
self.update_ha_state()

View File

@ -17,7 +17,7 @@ from homeassistant.helpers.entity import ToggleEntity
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.components import ( from homeassistant.components import (
group, discovery, wink, isy994, verisure, zwave) group, discovery, wink, isy994, verisure, zwave, mysensors)
DOMAIN = 'switch' DOMAIN = 'switch'
SCAN_INTERVAL = 30 SCAN_INTERVAL = 30
@ -40,6 +40,7 @@ DISCOVERY_PLATFORMS = {
isy994.DISCOVER_SWITCHES: 'isy994', isy994.DISCOVER_SWITCHES: 'isy994',
verisure.DISCOVER_SWITCHES: 'verisure', verisure.DISCOVER_SWITCHES: 'verisure',
zwave.DISCOVER_SWITCHES: 'zwave', zwave.DISCOVER_SWITCHES: 'zwave',
mysensors.DISCOVER_SWITCHES: 'mysensors',
} }
PROP_TO_ATTR = { PROP_TO_ATTR = {

View File

@ -7,6 +7,7 @@ For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/sensor.mysensors.html https://home-assistant.io/components/sensor.mysensors.html
""" """
import logging import logging
from collections import defaultdict
from homeassistant.components.switch import SwitchDevice from homeassistant.components.switch import SwitchDevice
@ -24,49 +25,50 @@ S_TYPES = None
V_TYPES = None V_TYPES = None
@mysensors.mysensors_update
def sensor_update(gateway, port, devices, nid):
"""Internal callback for sensor updates."""
return (S_TYPES, V_TYPES, MySensorsSwitch, ADD_DEVICES)
def setup_platform(hass, config, add_devices, discovery_info=None): def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup the mysensors platform for switches.""" """Setup the mysensors platform for switches."""
# Define the S_TYPES and V_TYPES that the platform should handle as states. # Only act if loaded via mysensors by discovery event.
global ADD_DEVICES # Otherwise gateway is not setup.
ADD_DEVICES = add_devices if discovery_info is None:
global S_TYPES return
S_TYPES = [
mysensors.CONST.Presentation.S_DOOR, for gateway in mysensors.GATEWAYS.values():
mysensors.CONST.Presentation.S_MOTION, # Define the S_TYPES and V_TYPES that the platform should handle as
mysensors.CONST.Presentation.S_SMOKE, # states.
mysensors.CONST.Presentation.S_LIGHT, s_types = [
mysensors.CONST.Presentation.S_BINARY, gateway.const.Presentation.S_DOOR,
mysensors.CONST.Presentation.S_LOCK, gateway.const.Presentation.S_MOTION,
gateway.const.Presentation.S_SMOKE,
gateway.const.Presentation.S_LIGHT,
gateway.const.Presentation.S_BINARY,
gateway.const.Presentation.S_LOCK,
] ]
global V_TYPES v_types = [
V_TYPES = [ gateway.const.SetReq.V_ARMED,
mysensors.CONST.SetReq.V_ARMED, gateway.const.SetReq.V_LIGHT,
mysensors.CONST.SetReq.V_LIGHT, gateway.const.SetReq.V_LOCK_STATUS,
mysensors.CONST.SetReq.V_LOCK_STATUS,
] ]
if float(mysensors.VERSION) >= 1.5: if float(gateway.version) >= 1.5:
S_TYPES.extend([ s_types.extend([
mysensors.CONST.Presentation.S_SPRINKLER, gateway.const.Presentation.S_SPRINKLER,
mysensors.CONST.Presentation.S_WATER_LEAK, gateway.const.Presentation.S_WATER_LEAK,
mysensors.CONST.Presentation.S_SOUND, gateway.const.Presentation.S_SOUND,
mysensors.CONST.Presentation.S_VIBRATION, gateway.const.Presentation.S_VIBRATION,
mysensors.CONST.Presentation.S_MOISTURE, gateway.const.Presentation.S_MOISTURE,
]) ])
V_TYPES.extend([mysensors.CONST.SetReq.V_STATUS, ]) v_types.extend([gateway.const.SetReq.V_STATUS, ])
devices = defaultdict(list)
gateway.platform_callbacks.append(mysensors.pf_callback_factory(
s_types, v_types, devices, add_devices, MySensorsSwitch))
class MySensorsSwitch(SwitchDevice): class MySensorsSwitch(SwitchDevice):
"""Represent the value of a MySensors child node.""" """Represent the value of a MySensors child node."""
# pylint: disable=too-many-arguments, too-many-instance-attributes # pylint: disable=too-many-arguments
def __init__(self, port, node_id, child_id, name, value_type): def __init__(self, gateway, node_id, child_id, name, value_type):
"""Setup class attributes on instantiation. """Setup class attributes on instantiation.
Args: Args:
@ -85,7 +87,7 @@ class MySensorsSwitch(SwitchDevice):
battery_level (int): Node battery level. battery_level (int): Node battery level.
_values (dict): Child values. Non state values set as state attributes. _values (dict): Child values. Non state values set as state attributes.
""" """
self.port = port self.gateway = gateway
self.node_id = node_id self.node_id = node_id
self.child_id = child_id self.child_id = child_id
self._name = name self._name = name
@ -135,30 +137,31 @@ class MySensorsSwitch(SwitchDevice):
def turn_on(self): def turn_on(self):
"""Turn the switch on.""" """Turn the switch on."""
mysensors.GATEWAYS[self.port].set_child_value( self.gateway.set_child_value(
self.node_id, self.child_id, self.value_type, 1) self.node_id, self.child_id, self.value_type, 1)
self._values[self.value_type] = STATE_ON self._values[self.value_type] = STATE_ON
self.update_ha_state() self.update_ha_state()
def turn_off(self): def turn_off(self):
"""Turn the switch off.""" """Turn the switch off."""
mysensors.GATEWAYS[self.port].set_child_value( self.gateway.set_child_value(
self.node_id, self.child_id, self.value_type, 0) self.node_id, self.child_id, self.value_type, 0)
self._values[self.value_type] = STATE_OFF self._values[self.value_type] = STATE_OFF
self.update_ha_state() self.update_ha_state()
def update_sensor(self, values, battery_level): def update(self):
"""Update the controller with the latest value from a sensor.""" """Update the controller with the latest value from a sensor."""
for value_type, value in values.items(): node = self.gateway.sensors[self.node_id]
child = node.children[self.child_id]
for value_type, value in child.values.items():
_LOGGER.info( _LOGGER.info(
"%s: value_type %s, value = %s", self._name, value_type, value) "%s: value_type %s, value = %s", self._name, value_type, value)
if value_type == mysensors.CONST.SetReq.V_ARMED or \ if value_type == self.gateway.const.SetReq.V_ARMED or \
value_type == mysensors.CONST.SetReq.V_STATUS or \ value_type == self.gateway.const.SetReq.V_STATUS or \
value_type == mysensors.CONST.SetReq.V_LIGHT or \ value_type == self.gateway.const.SetReq.V_LIGHT or \
value_type == mysensors.CONST.SetReq.V_LOCK_STATUS: value_type == self.gateway.const.SetReq.V_LOCK_STATUS:
self._values[value_type] = ( self._values[value_type] = (
STATE_ON if int(value) == 1 else STATE_OFF) STATE_ON if int(value) == 1 else STATE_OFF)
else: else:
self._values[value_type] = value self._values[value_type] = value
self.battery_level = battery_level self.battery_level = node.battery_level
self.update_ha_state()