From 069a4b1706b5d4cb7be94e25c38ceb233d2a4ad5 Mon Sep 17 00:00:00 2001 From: Martin Hjelmare Date: Sat, 30 Apr 2016 15:27:59 +0200 Subject: [PATCH] Refactor mysensors component * Add MySensorsDeviceEntity class to hold the common attributes, properties and methods for mysensors entities. * Inherit from MySensorsDeviceEntity class in binary_sensor, light, sensor and switch mysensors platforms. * Remove not needed attribute and method for const in GatewayWrapper class. The const attribute is already set in the wrapped object. * Clean up state property for mysensors sensor entities. * Inherit from MySensorsLightRGB in MySensorsLightRGBW class. * Remove use of get_component in mysensors component and platforms. * Clean up update method in MySensorsDeviceEntity class. --- .../components/binary_sensor/mysensors.py | 104 +-------------- homeassistant/components/light/mysensors.py | 77 ++--------- homeassistant/components/mysensors.py | 120 +++++++++++++++--- homeassistant/components/sensor/mysensors.py | 105 +-------------- homeassistant/components/switch/mysensors.py | 100 +-------------- 5 files changed, 127 insertions(+), 379 deletions(-) diff --git a/homeassistant/components/binary_sensor/mysensors.py b/homeassistant/components/binary_sensor/mysensors.py index 3cc9798f288..d7b1a82188e 100644 --- a/homeassistant/components/binary_sensor/mysensors.py +++ b/homeassistant/components/binary_sensor/mysensors.py @@ -6,10 +6,10 @@ https://home-assistant.io/components/binary_sensor.mysensors/ """ import logging +from homeassistant.components import mysensors from homeassistant.components.binary_sensor import (SENSOR_CLASSES, BinarySensorDevice) -from homeassistant.const import ATTR_BATTERY_LEVEL, STATE_OFF, STATE_ON -from homeassistant.loader import get_component +from homeassistant.const import STATE_ON _LOGGER = logging.getLogger(__name__) DEPENDENCIES = [] @@ -22,8 +22,6 @@ def setup_platform(hass, config, add_devices, discovery_info=None): if discovery_info is None: return - mysensors = get_component('mysensors') - for gateway in mysensors.GATEWAYS.values(): # Define the S_TYPES and V_TYPES that the platform should handle as # states. Map them in a dict of lists. @@ -48,81 +46,9 @@ def setup_platform(hass, config, add_devices, discovery_info=None): map_sv_types, devices, add_devices, MySensorsBinarySensor)) -class MySensorsBinarySensor(BinarySensorDevice): - """Represent the value of a MySensors child node.""" - - # pylint: disable=too-many-arguments,too-many-instance-attributes - - def __init__( - self, gateway, node_id, child_id, name, value_type, child_type): - """ - Setup class attributes on instantiation. - - Args: - gateway (GatewayWrapper): Gateway object. - node_id (str): Id of node. - child_id (str): Id of child. - name (str): Entity name. - value_type (str): Value type of child. Value is entity state. - child_type (str): Child type of child. - - Attributes: - gateway (GatewayWrapper): Gateway object. - node_id (str): Id of node. - child_id (str): Id of child. - _name (str): Entity name. - value_type (str): Value type of child. Value is entity state. - child_type (str): Child type of child. - battery_level (int): Node battery level. - _values (dict): Child values. Non state values set as state attributes. - mysensors (module): Mysensors main component module. - """ - self.gateway = gateway - self.node_id = node_id - self.child_id = child_id - self._name = name - self.value_type = value_type - self.child_type = child_type - self.battery_level = 0 - self._values = {} - self.mysensors = get_component('mysensors') - - @property - def should_poll(self): - """Mysensor gateway pushes its state to HA.""" - return False - - @property - def name(self): - """The name of this entity.""" - return self._name - - @property - def device_state_attributes(self): - """Return device specific state attributes.""" - address = getattr(self.gateway, 'server_address', None) - if address: - device = '{}:{}'.format(address[0], address[1]) - else: - device = self.gateway.port - attr = { - self.mysensors.ATTR_DEVICE: device, - self.mysensors.ATTR_NODE_ID: self.node_id, - self.mysensors.ATTR_CHILD_ID: self.child_id, - ATTR_BATTERY_LEVEL: self.battery_level, - } - - set_req = self.gateway.const.SetReq - - for value_type, value in self._values.items(): - if value_type != self.value_type: - try: - attr[set_req(value_type).name] = value - except ValueError: - _LOGGER.error('value_type %s is not valid for mysensors ' - 'version %s', value_type, - self.gateway.version) - return attr +class MySensorsBinarySensor( + mysensors.MySensorsDeviceEntity, BinarySensorDevice): + """Represent the value of a MySensors Binary Sensor child node.""" @property def is_on(self): @@ -150,23 +76,3 @@ class MySensorsBinarySensor(BinarySensorDevice): }) if class_map.get(self.child_type) in SENSOR_CLASSES: return class_map.get(self.child_type) - - @property - def available(self): - """Return True if entity is available.""" - return self.value_type in self._values - - def update(self): - """Update the controller with the latest values from a sensor.""" - node = self.gateway.sensors[self.node_id] - child = node.children[self.child_id] - for value_type, value in child.values.items(): - _LOGGER.debug( - "%s: value_type %s, value = %s", self._name, value_type, value) - if value_type == self.gateway.const.SetReq.V_TRIPPED: - self._values[value_type] = STATE_ON if int( - value) == 1 else STATE_OFF - else: - self._values[value_type] = value - - self.battery_level = node.battery_level diff --git a/homeassistant/components/light/mysensors.py b/homeassistant/components/light/mysensors.py index 5a85b047be8..04c0942e1f6 100644 --- a/homeassistant/components/light/mysensors.py +++ b/homeassistant/components/light/mysensors.py @@ -6,10 +6,10 @@ https://home-assistant.io/components/light.mysensors/ """ import logging +from homeassistant.components import mysensors from homeassistant.components.light import (ATTR_BRIGHTNESS, ATTR_RGB_COLOR, Light) -from homeassistant.const import ATTR_BATTERY_LEVEL, STATE_OFF, STATE_ON -from homeassistant.loader import get_component +from homeassistant.const import STATE_OFF, STATE_ON from homeassistant.util.color import rgb_hex_to_rgb_list _LOGGER = logging.getLogger(__name__) @@ -25,8 +25,6 @@ def setup_platform(hass, config, add_devices, discovery_info=None): if discovery_info is None: return - mysensors = get_component('mysensors') - for gateway in mysensors.GATEWAYS.values(): # Define the S_TYPES and V_TYPES that the platform should handle as # states. Map them in a dict of lists. @@ -52,35 +50,16 @@ def setup_platform(hass, config, add_devices, discovery_info=None): map_sv_types, devices, add_devices, device_class_map)) -class MySensorsLight(Light): - """Represent the value of a MySensors child node.""" +class MySensorsLight(mysensors.MySensorsDeviceEntity, Light): + """Represent the value of a MySensors Light child node.""" - # pylint: disable=too-many-arguments,too-many-instance-attributes - def __init__( - self, gateway, node_id, child_id, name, value_type, child_type): + def __init__(self, *args): """Setup instance attributes.""" - self.gateway = gateway - self.node_id = node_id - self.child_id = child_id - self._name = name - self.value_type = value_type - self.battery_level = 0 - self._values = {} + mysensors.MySensorsDeviceEntity.__init__(self, *args) self._state = None self._brightness = None self._rgb = None self._white = None - self.mysensors = get_component('mysensors') - - @property - def should_poll(self): - """No polling needed.""" - return False - - @property - def name(self): - """Return the name of this entity.""" - return self._name @property def brightness(self): @@ -97,29 +76,6 @@ class MySensorsLight(Light): """Return the white value in RGBW, value between 0..255.""" return self._white - @property - def device_state_attributes(self): - """Return device specific state attributes.""" - address = getattr(self.gateway, 'server_address', None) - if address: - device = '{}:{}'.format(address[0], address[1]) - else: - device = self.gateway.port - attr = { - self.mysensors.ATTR_DEVICE: device, - self.mysensors.ATTR_NODE_ID: self.node_id, - self.mysensors.ATTR_CHILD_ID: self.child_id, - ATTR_BATTERY_LEVEL: self.battery_level, - } - for value_type, value in self._values.items(): - attr[self.gateway.const.SetReq(value_type).name] = value - return attr - - @property - def available(self): - """Return true if entity is available.""" - return self.value_type in self._values - @property def assumed_state(self): """Return true if unable to access real state of entity.""" @@ -319,28 +275,11 @@ class MySensorsLightRGB(MySensorsLight): self._update_rgb_or_w() -class MySensorsLightRGBW(MySensorsLight): - """RGBW child class to MySensorsLight.""" +class MySensorsLightRGBW(MySensorsLightRGB): + """RGBW child class to MySensorsLightRGB.""" def turn_on(self, **kwargs): """Turn the device on.""" self._turn_on_light() self._turn_on_dimmer(**kwargs) self._turn_on_rgb_and_w('%02x%02x%02x%02x', **kwargs) - - def turn_off(self, **kwargs): - """Turn the device off.""" - ret = self._turn_off_rgb_or_w() - ret = self._turn_off_dimmer( - value_type=ret[ATTR_VALUE_TYPE], value=ret[ATTR_VALUE]) - ret = self._turn_off_light( - value_type=ret[ATTR_VALUE_TYPE], value=ret[ATTR_VALUE]) - self._turn_off_main( - value_type=ret[ATTR_VALUE_TYPE], value=ret[ATTR_VALUE]) - - def update(self): - """Update the controller with the latest value from a sensor.""" - self._update_main() - self._update_light() - self._update_dimmer() - self._update_rgb_or_w() diff --git a/homeassistant/components/mysensors.py b/homeassistant/components/mysensors.py index 204b2818de6..9d42316d382 100644 --- a/homeassistant/components/mysensors.py +++ b/homeassistant/components/mysensors.py @@ -8,10 +8,12 @@ import logging import socket import homeassistant.bootstrap as bootstrap -from homeassistant.const import (ATTR_DISCOVERED, ATTR_SERVICE, - CONF_OPTIMISTIC, EVENT_HOMEASSISTANT_START, +from homeassistant.const import (ATTR_BATTERY_LEVEL, ATTR_DISCOVERED, + ATTR_SERVICE, CONF_OPTIMISTIC, + EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP, - EVENT_PLATFORM_DISCOVERED, TEMP_CELSIUS) + EVENT_PLATFORM_DISCOVERED, STATE_OFF, + STATE_ON, TEMP_CELSIUS) from homeassistant.helpers import validate_config CONF_GATEWAYS = 'gateways' @@ -170,6 +172,8 @@ def pf_callback_factory(map_sv_types, devices, add_devices, entity_class): class GatewayWrapper(object): """Gateway wrapper class.""" + # pylint: disable=too-few-public-methods + def __init__(self, gateway, version, optimistic): """Setup class attributes on instantiation. @@ -182,14 +186,12 @@ class GatewayWrapper(object): _wrapped_gateway (mysensors.SerialGateway): Wrapped gateway. version (str): Version of mysensors API. platform_callbacks (list): Callback functions, one per platform. - const (module): Mysensors API constants. optimistic (bool): Send values to actuators without feedback state. __initialised (bool): True if GatewayWrapper is initialised. """ self._wrapped_gateway = gateway self.version = version self.platform_callbacks = [] - self.const = self.get_const() self.optimistic = optimistic self.__initialised = True @@ -197,9 +199,9 @@ class GatewayWrapper(object): """See if this object has attribute name.""" # Do not use hasattr, it goes into infinite recurrsion if name in self.__dict__: - # this object has it + # This object has the attribute. return getattr(self, name) - # proxy to the wrapped object + # The wrapped object has the attribute. return getattr(self._wrapped_gateway, name) def __setattr__(self, name, value): @@ -211,14 +213,6 @@ class GatewayWrapper(object): else: object.__setattr__(self._wrapped_gateway, name, value) - 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): @@ -228,3 +222,99 @@ class GatewayWrapper(object): callback(self, node_id) return node_update + + +class MySensorsDeviceEntity(object): + """Represent a MySensors entity.""" + + # pylint: disable=too-many-arguments,too-many-instance-attributes + + def __init__( + self, gateway, node_id, child_id, name, value_type, child_type): + """ + Setup class attributes on instantiation. + + Args: + gateway (GatewayWrapper): Gateway object. + node_id (str): Id of node. + child_id (str): Id of child. + name (str): Entity name. + value_type (str): Value type of child. Value is entity state. + child_type (str): Child type of child. + + Attributes: + gateway (GatewayWrapper): Gateway object. + node_id (str): Id of node. + child_id (str): Id of child. + _name (str): Entity name. + value_type (str): Value type of child. Value is entity state. + child_type (str): Child type of child. + battery_level (int): Node battery level. + _values (dict): Child values. Non state values set as state attributes. + mysensors (module): Mysensors main component module. + """ + self.gateway = gateway + self.node_id = node_id + self.child_id = child_id + self._name = name + self.value_type = value_type + self.child_type = child_type + self.battery_level = 0 + self._values = {} + + @property + def should_poll(self): + """Mysensor gateway pushes its state to HA.""" + return False + + @property + def name(self): + """The name of this entity.""" + return self._name + + @property + def device_state_attributes(self): + """Return device specific state attributes.""" + address = getattr(self.gateway, 'server_address', None) + if address: + device = '{}:{}'.format(address[0], address[1]) + else: + device = self.gateway.port + attr = { + ATTR_DEVICE: device, + ATTR_NODE_ID: self.node_id, + ATTR_CHILD_ID: self.child_id, + ATTR_BATTERY_LEVEL: self.battery_level, + } + + set_req = self.gateway.const.SetReq + + for value_type, value in self._values.items(): + try: + attr[set_req(value_type).name] = value + except ValueError: + _LOGGER.error('value_type %s is not valid for mysensors ' + 'version %s', value_type, + self.gateway.version) + return attr + + @property + def available(self): + """Return True if entity is available.""" + return self.value_type in self._values + + def update(self): + """Update the controller with the latest value from a sensor.""" + node = self.gateway.sensors[self.node_id] + child = node.children[self.child_id] + self.battery_level = node.battery_level + set_req = self.gateway.const.SetReq + for value_type, value in child.values.items(): + _LOGGER.debug( + "%s: value_type %s, value = %s", self._name, value_type, value) + if value_type in (set_req.V_ARMED, set_req.V_LIGHT, + set_req.V_LOCK_STATUS, set_req.V_TRIPPED): + self._values[value_type] = ( + STATE_ON if int(value) == 1 else STATE_OFF) + else: + self._values[value_type] = value diff --git a/homeassistant/components/sensor/mysensors.py b/homeassistant/components/sensor/mysensors.py index ae959af3ac6..c1eaa913535 100644 --- a/homeassistant/components/sensor/mysensors.py +++ b/homeassistant/components/sensor/mysensors.py @@ -6,10 +6,9 @@ https://home-assistant.io/components/sensor.mysensors/ """ import logging -from homeassistant.const import (ATTR_BATTERY_LEVEL, STATE_OFF, STATE_ON, - TEMP_CELSIUS, TEMP_FAHRENHEIT) +from homeassistant.components import mysensors +from homeassistant.const import TEMP_CELSIUS, TEMP_FAHRENHEIT from homeassistant.helpers.entity import Entity -from homeassistant.loader import get_component _LOGGER = logging.getLogger(__name__) DEPENDENCIES = [] @@ -22,8 +21,6 @@ def setup_platform(hass, config, add_devices, discovery_info=None): if discovery_info is None: return - mysensors = get_component('mysensors') - for gateway in mysensors.GATEWAYS.values(): # Define the S_TYPES and V_TYPES that the platform should handle as # states. Map them in a dict of lists. @@ -74,58 +71,13 @@ def setup_platform(hass, config, add_devices, discovery_info=None): map_sv_types, devices, add_devices, MySensorsSensor)) -class MySensorsSensor(Entity): - """Represent the value of a MySensors child node.""" - - # pylint: disable=too-many-arguments,too-many-instance-attributes - - def __init__( - self, gateway, node_id, child_id, name, value_type, child_type): - """Setup class attributes on instantiation. - - Args: - gateway (GatewayWrapper): Gateway object. - node_id (str): Id of node. - child_id (str): Id of child. - name (str): Entity name. - value_type (str): Value type of child. Value is entity state. - child_type (str): Child type of child. - - Attributes: - gateway (GatewayWrapper): Gateway object. - node_id (str): Id of node. - child_id (str): Id of child. - _name (str): Entity name. - value_type (str): Value type of child. Value is entity state. - battery_level (int): Node battery level. - _values (dict): Child values. Non state values set as state attributes. - mysensors (module): Mysensors main component module. - """ - self.gateway = gateway - self.node_id = node_id - self.child_id = child_id - self._name = name - self.value_type = value_type - self.battery_level = 0 - self._values = {} - self.mysensors = get_component('mysensors') - - @property - def should_poll(self): - """No polling needed.""" - return False - - @property - def name(self): - """Return the name of this entity.""" - return self._name +class MySensorsSensor(mysensors.MySensorsDeviceEntity, Entity): + """Represent the value of a MySensors Sensor child node.""" @property def state(self): """Return the state of the device.""" - if not self._values: - return '' - return self._values[self.value_type] + return self._values.get(self.value_type) @property def unit_of_measurement(self): @@ -153,50 +105,3 @@ class MySensorsSensor(Entity): set_req.V_UNIT_PREFIX] unit_map.update({set_req.V_PERCENTAGE: '%'}) return unit_map.get(self.value_type) - - @property - def device_state_attributes(self): - """Return device specific state attributes.""" - address = getattr(self.gateway, 'server_address', None) - if address: - device = '{}:{}'.format(address[0], address[1]) - else: - device = self.gateway.port - attr = { - self.mysensors.ATTR_DEVICE: device, - self.mysensors.ATTR_NODE_ID: self.node_id, - self.mysensors.ATTR_CHILD_ID: self.child_id, - ATTR_BATTERY_LEVEL: self.battery_level, - } - - set_req = self.gateway.const.SetReq - - for value_type, value in self._values.items(): - if value_type != self.value_type: - try: - attr[set_req(value_type).name] = value - except ValueError: - _LOGGER.error('value_type %s is not valid for mysensors ' - 'version %s', value_type, - self.gateway.version) - return attr - - @property - def available(self): - """Return True if entity is available.""" - return self.value_type in self._values - - def update(self): - """Update the controller with the latest values from a sensor.""" - node = self.gateway.sensors[self.node_id] - child = node.children[self.child_id] - for value_type, value in child.values.items(): - _LOGGER.debug( - "%s: value_type %s, value = %s", self._name, value_type, value) - if value_type == self.gateway.const.SetReq.V_TRIPPED: - self._values[value_type] = STATE_ON if int( - value) == 1 else STATE_OFF - else: - self._values[value_type] = value - - self.battery_level = node.battery_level diff --git a/homeassistant/components/switch/mysensors.py b/homeassistant/components/switch/mysensors.py index 3101f68322d..25cf4945d97 100644 --- a/homeassistant/components/switch/mysensors.py +++ b/homeassistant/components/switch/mysensors.py @@ -6,9 +6,9 @@ https://home-assistant.io/components/switch.mysensors/ """ import logging +from homeassistant.components import mysensors from homeassistant.components.switch import SwitchDevice -from homeassistant.const import ATTR_BATTERY_LEVEL, STATE_OFF, STATE_ON -from homeassistant.loader import get_component +from homeassistant.const import STATE_OFF, STATE_ON _LOGGER = logging.getLogger(__name__) DEPENDENCIES = [] @@ -21,8 +21,6 @@ def setup_platform(hass, config, add_devices, discovery_info=None): if discovery_info is None: return - mysensors = get_component('mysensors') - for gateway in mysensors.GATEWAYS.values(): # Define the S_TYPES and V_TYPES that the platform should handle as # states. Map them in a dict of lists. @@ -51,77 +49,8 @@ def setup_platform(hass, config, add_devices, discovery_info=None): map_sv_types, devices, add_devices, MySensorsSwitch)) -class MySensorsSwitch(SwitchDevice): - """Representation of the value of a MySensors child node.""" - - # pylint: disable=too-many-arguments,too-many-instance-attributes - def __init__( - self, gateway, node_id, child_id, name, value_type, child_type): - """Setup class attributes on instantiation. - - Args: - gateway (GatewayWrapper): Gateway object. - node_id (str): Id of node. - child_id (str): Id of child. - name (str): Entity name. - value_type (str): Value type of child. Value is entity state. - child_type (str): Child type of child. - - Attributes: - gateway (GatewayWrapper): Gateway object - node_id (str): Id of node. - child_id (str): Id of child. - _name (str): Entity name. - value_type (str): Value type of child. Value is entity state. - battery_level (int): Node battery level. - _values (dict): Child values. Non state values set as state attributes. - mysensors (module): Mysensors main component module. - """ - self.gateway = gateway - self.node_id = node_id - self.child_id = child_id - self._name = name - self.value_type = value_type - self.battery_level = 0 - self._values = {} - self.mysensors = get_component('mysensors') - - @property - def should_poll(self): - """Mysensor gateway pushes its state to HA.""" - return False - - @property - def name(self): - """The name of this entity.""" - return self._name - - @property - def device_state_attributes(self): - """Return device specific state attributes.""" - address = getattr(self.gateway, 'server_address', None) - if address: - device = '{}:{}'.format(address[0], address[1]) - else: - device = self.gateway.port - attr = { - self.mysensors.ATTR_DEVICE: device, - self.mysensors.ATTR_NODE_ID: self.node_id, - self.mysensors.ATTR_CHILD_ID: self.child_id, - ATTR_BATTERY_LEVEL: self.battery_level, - } - - set_req = self.gateway.const.SetReq - - for value_type, value in self._values.items(): - if value_type != self.value_type: - try: - attr[set_req(value_type).name] = value - except ValueError: - _LOGGER.error('value_type %s is not valid for mysensors ' - 'version %s', value_type, - self.gateway.version) - return attr +class MySensorsSwitch(mysensors.MySensorsDeviceEntity, SwitchDevice): + """Representation of the value of a MySensors Switch child node.""" @property def is_on(self): @@ -148,28 +77,7 @@ class MySensorsSwitch(SwitchDevice): self._values[self.value_type] = STATE_OFF self.update_ha_state() - @property - def available(self): - """Return True if entity is available.""" - return self.value_type in self._values - @property def assumed_state(self): """Return True if unable to access real state of entity.""" return self.gateway.optimistic - - def update(self): - """Update the controller with the latest value from a sensor.""" - node = self.gateway.sensors[self.node_id] - child = node.children[self.child_id] - for value_type, value in child.values.items(): - _LOGGER.debug( - "%s: value_type %s, value = %s", self._name, value_type, value) - if value_type == self.gateway.const.SetReq.V_ARMED or \ - value_type == self.gateway.const.SetReq.V_LIGHT or \ - value_type == self.gateway.const.SetReq.V_LOCK_STATUS: - self._values[value_type] = ( - STATE_ON if int(value) == 1 else STATE_OFF) - else: - self._values[value_type] = value - self.battery_level = node.battery_level