mirror of
https://github.com/home-assistant/core.git
synced 2025-07-19 03:07:37 +00:00
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:
parent
be25ea4f09
commit
69ed6fe6e7
@ -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)
|
||||||
gateway.start()
|
|
||||||
|
|
||||||
def persistence_update(event):
|
def gw_start(event):
|
||||||
"""Callback to trigger update from persistence file."""
|
"""Callback to trigger start of gateway and any persistence."""
|
||||||
for nid in gateway.sensors:
|
gateway.start()
|
||||||
gateway.event_callback('persistence', nid)
|
hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP,
|
||||||
|
lambda event: gateway.stop())
|
||||||
|
if persistence:
|
||||||
|
for node_id in gateway.sensors:
|
||||||
|
gateway.event_callback('persistence', node_id)
|
||||||
|
|
||||||
if persistence:
|
hass.bus.listen_once(EVENT_HOMEASSISTANT_START, gw_start)
|
||||||
hass.bus.listen_once(
|
|
||||||
EVENT_HOMEASSISTANT_START, persistence_update)
|
|
||||||
|
|
||||||
hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP,
|
|
||||||
lambda event: gateway.stop())
|
|
||||||
|
|
||||||
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():
|
name = '{} {}.{}'.format(
|
||||||
if child_id not in node:
|
gateway.sensors[node_id].sketch_name, node_id, child.id)
|
||||||
node[child_id] = {}
|
|
||||||
for value_type in child.values.keys():
|
for value_type in child.values.keys():
|
||||||
if (value_type not in node[child_id] and
|
if child.type not in s_types or value_type not in v_types:
|
||||||
child.type in platform_s_types and
|
continue
|
||||||
value_type in platform_v_types):
|
|
||||||
name = '{} {}.{}'.format(
|
devices[node_id].append(
|
||||||
gateway.sensors[nid].sketch_name, nid, child.id)
|
entity_class(gateway, node_id, child.id, name, value_type))
|
||||||
node[child_id][value_type] = platform_class(
|
if devices[node_id]:
|
||||||
port, nid, child_id, name, value_type)
|
_LOGGER.info('adding new devices: %s', devices[node_id])
|
||||||
_LOGGER.info('adding new device: %s',
|
add_devices(devices[node_id])
|
||||||
node[child_id][value_type])
|
for entity in devices[node_id]:
|
||||||
add_devices([node[child_id][value_type]])
|
entity.update_ha_state(True)
|
||||||
if (child.type in platform_s_types and
|
return mysensors_callback
|
||||||
value_type in platform_v_types):
|
|
||||||
node[child_id][value_type].update_sensor(
|
|
||||||
child.values, gateway.sensors[nid].battery_level)
|
class GatewayWrapper(mysensors.SerialGateway):
|
||||||
return wrapper
|
"""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
|
||||||
|
@ -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',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -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,
|
||||||
not_v_types = [
|
gateway.const.Presentation.S_CUSTOM,
|
||||||
mysensors.CONST.SetReq.V_ARMED,
|
gateway.const.Presentation.S_DUST,
|
||||||
mysensors.CONST.SetReq.V_LIGHT,
|
gateway.const.Presentation.S_SCENE_CONTROLLER,
|
||||||
mysensors.CONST.SetReq.V_LOCK_STATUS,
|
]
|
||||||
]
|
not_v_types = [
|
||||||
if float(mysensors.VERSION) >= 1.5:
|
gateway.const.SetReq.V_ARMED,
|
||||||
S_TYPES.extend([
|
gateway.const.SetReq.V_LIGHT,
|
||||||
mysensors.CONST.Presentation.S_COLOR_SENSOR,
|
gateway.const.SetReq.V_LOCK_STATUS,
|
||||||
mysensors.CONST.Presentation.S_MULTIMETER,
|
]
|
||||||
])
|
if float(gateway.version) >= 1.5:
|
||||||
not_v_types.extend([mysensors.CONST.SetReq.V_STATUS, ])
|
s_types.extend([
|
||||||
global V_TYPES
|
gateway.const.Presentation.S_COLOR_SENSOR,
|
||||||
V_TYPES = [member for member in mysensors.CONST.SetReq
|
gateway.const.Presentation.S_MULTIMETER,
|
||||||
if member.value not in not_v_types]
|
])
|
||||||
|
not_v_types.extend([gateway.const.SetReq.V_STATUS, ])
|
||||||
|
v_types = [member for member in gateway.const.SetReq
|
||||||
|
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()
|
|
||||||
|
@ -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 = {
|
||||||
|
@ -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,
|
||||||
global V_TYPES
|
gateway.const.Presentation.S_LIGHT,
|
||||||
V_TYPES = [
|
gateway.const.Presentation.S_BINARY,
|
||||||
mysensors.CONST.SetReq.V_ARMED,
|
gateway.const.Presentation.S_LOCK,
|
||||||
mysensors.CONST.SetReq.V_LIGHT,
|
]
|
||||||
mysensors.CONST.SetReq.V_LOCK_STATUS,
|
v_types = [
|
||||||
]
|
gateway.const.SetReq.V_ARMED,
|
||||||
if float(mysensors.VERSION) >= 1.5:
|
gateway.const.SetReq.V_LIGHT,
|
||||||
S_TYPES.extend([
|
gateway.const.SetReq.V_LOCK_STATUS,
|
||||||
mysensors.CONST.Presentation.S_SPRINKLER,
|
]
|
||||||
mysensors.CONST.Presentation.S_WATER_LEAK,
|
if float(gateway.version) >= 1.5:
|
||||||
mysensors.CONST.Presentation.S_SOUND,
|
s_types.extend([
|
||||||
mysensors.CONST.Presentation.S_VIBRATION,
|
gateway.const.Presentation.S_SPRINKLER,
|
||||||
mysensors.CONST.Presentation.S_MOISTURE,
|
gateway.const.Presentation.S_WATER_LEAK,
|
||||||
])
|
gateway.const.Presentation.S_SOUND,
|
||||||
V_TYPES.extend([mysensors.CONST.SetReq.V_STATUS, ])
|
gateway.const.Presentation.S_VIBRATION,
|
||||||
|
gateway.const.Presentation.S_MOISTURE,
|
||||||
|
])
|
||||||
|
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()
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user