From e7b206da0c8d391019e39662d666b99c05b86b81 Mon Sep 17 00:00:00 2001 From: MartinHjelmare Date: Thu, 14 Jul 2016 02:44:46 +0200 Subject: [PATCH 1/2] Add MQTT gateway for MySensors * Use mqtt component to enable a MySensors MQTT gateway. * Setup the MQTT gateway if mysensors config has mqtt as a value for the key device in the list of gateways. * Simplify two lines in the mqtt component. --- homeassistant/components/mqtt/__init__.py | 4 +- homeassistant/components/mysensors.py | 84 +++++++++++++++-------- 2 files changed, 58 insertions(+), 30 deletions(-) diff --git a/homeassistant/components/mqtt/__init__.py b/homeassistant/components/mqtt/__init__.py index 6db231f6bd7..e06f60b6e1a 100644 --- a/homeassistant/components/mqtt/__init__.py +++ b/homeassistant/components/mqtt/__init__.py @@ -203,14 +203,14 @@ def setup(hass, config): broker_config = _setup_server(hass, config) - broker_in_conf = True if CONF_BROKER in conf else False + broker_in_conf = CONF_BROKER in conf # Only auto config if no server config was passed in if broker_config and CONF_EMBEDDED not in conf: broker, port, username, password, certificate, protocol = broker_config # Embedded broker doesn't have some ssl variables client_key, client_cert, tls_insecure = None, None, None - elif not broker_config and CONF_BROKER not in conf: + elif not broker_config and not broker_in_conf: _LOGGER.error('Unable to start broker and auto-configure MQTT.') return False diff --git a/homeassistant/components/mysensors.py b/homeassistant/components/mysensors.py index d40806bdc31..8a53bc011cd 100644 --- a/homeassistant/components/mysensors.py +++ b/homeassistant/components/mysensors.py @@ -7,11 +7,12 @@ https://home-assistant.io/components/sensor.mysensors/ import logging import socket +from homeassistant.bootstrap import setup_component from homeassistant.const import (ATTR_BATTERY_LEVEL, CONF_OPTIMISTIC, EVENT_HOMEASSISTANT_START, - EVENT_HOMEASSISTANT_STOP, - STATE_OFF, STATE_ON) -from homeassistant.helpers import validate_config, discovery + EVENT_HOMEASSISTANT_STOP, STATE_OFF, STATE_ON) +from homeassistant.helpers import discovery, validate_config +from homeassistant.loader import get_component CONF_GATEWAYS = 'gateways' CONF_DEVICE = 'device' @@ -21,6 +22,9 @@ CONF_PERSISTENCE_FILE = 'persistence_file' CONF_VERSION = 'version' CONF_BAUD_RATE = 'baud_rate' CONF_TCP_PORT = 'tcp_port' +CONF_TOPIC_IN_PREFIX = 'topic_in_prefix' +CONF_TOPIC_OUT_PREFIX = 'topic_out_prefix' +CONF_RETAIN = 'retain' DEFAULT_VERSION = '1.4' DEFAULT_BAUD_RATE = 115200 DEFAULT_TCP_PORT = 5003 @@ -56,25 +60,47 @@ def setup(hass, config): # pylint: disable=too-many-locals is_metric = hass.config.units.is_metric persistence = config[DOMAIN].get(CONF_PERSISTENCE, True) - def setup_gateway(device, persistence_file, baud_rate, tcp_port): + def setup_gateway(device, persistence_file, baud_rate, tcp_port, in_prefix, + out_prefix): """Return gateway after setup of the gateway.""" - try: - socket.inet_aton(device) - # valid ip address - gateway = mysensors.TCPGateway( - device, event_callback=None, persistence=persistence, - persistence_file=persistence_file, protocol_version=version, - port=tcp_port) - except OSError: - # invalid ip address - gateway = mysensors.SerialGateway( - device, event_callback=None, persistence=persistence, - persistence_file=persistence_file, protocol_version=version, - baud=baud_rate) + # pylint: disable=too-many-arguments + if device in 'mqtt': + if not setup_component(hass, 'mqtt', config): + return + mqtt = get_component('mqtt') + retain = config[DOMAIN].get(CONF_RETAIN, True) + + def pub_callback(topic, payload, qos, retain): + """Call mqtt publish function.""" + mqtt.publish(hass, topic, payload, qos, retain) + + def sub_callback(topic, callback, qos): + """Call mqtt subscribe function.""" + mqtt.subscribe(hass, topic, callback, qos) + gateway = mysensors.MQTTGateway( + pub_callback, sub_callback, + event_callback=None, persistence=persistence, + persistence_file=persistence_file, + protocol_version=version, in_prefix=in_prefix, + out_prefix=out_prefix, retain=retain) + else: + try: + socket.inet_aton(device) + # valid ip address + gateway = mysensors.TCPGateway( + device, event_callback=None, persistence=persistence, + persistence_file=persistence_file, + protocol_version=version, port=tcp_port) + except OSError: + # invalid ip address + gateway = mysensors.SerialGateway( + device, event_callback=None, persistence=persistence, + persistence_file=persistence_file, + protocol_version=version, baud=baud_rate) gateway.metric = is_metric gateway.debug = config[DOMAIN].get(CONF_DEBUG, False) optimistic = config[DOMAIN].get(CONF_OPTIMISTIC, False) - gateway = GatewayWrapper(gateway, version, optimistic) + gateway = GatewayWrapper(gateway, version, optimistic, device) # pylint: disable=attribute-defined-outside-init gateway.event_callback = gateway.callback_factory() @@ -105,8 +131,13 @@ def setup(hass, config): # pylint: disable=too-many-locals hass.config.path('mysensors{}.pickle'.format(index + 1))) baud_rate = gway.get(CONF_BAUD_RATE, DEFAULT_BAUD_RATE) tcp_port = gway.get(CONF_TCP_PORT, DEFAULT_TCP_PORT) + in_prefix = gway.get(CONF_TOPIC_IN_PREFIX, '') + out_prefix = gway.get(CONF_TOPIC_OUT_PREFIX, '') GATEWAYS[device] = setup_gateway( - device, persistence_file, baud_rate, tcp_port) + device, persistence_file, baud_rate, tcp_port, in_prefix, + out_prefix) + if GATEWAYS[device] is None: + GATEWAYS.pop(device) for component in 'sensor', 'switch', 'light', 'binary_sensor': discovery.load_platform(hass, component, DOMAIN, {}, config) @@ -152,16 +183,17 @@ class GatewayWrapper(object): # pylint: disable=too-few-public-methods - def __init__(self, gateway, version, optimistic): + def __init__(self, gateway, version, optimistic, device): """Setup class attributes on instantiation. Args: - gateway (mysensors.SerialGateway): Gateway to wrap. + gateway (mysensors.Gateway): Gateway to wrap. version (str): Version of mysensors API. optimistic (bool): Send values to actuators without feedback state. + device (str): Path to serial port, ip adress or mqtt. Attributes: - _wrapped_gateway (mysensors.SerialGateway): Wrapped gateway. + _wrapped_gateway (mysensors.Gateway): Wrapped gateway. version (str): Version of mysensors API. platform_callbacks (list): Callback functions, one per platform. optimistic (bool): Send values to actuators without feedback state. @@ -171,6 +203,7 @@ class GatewayWrapper(object): self.version = version self.platform_callbacks = [] self.optimistic = optimistic + self.device = device self.__initialised = True def __getattr__(self, name): @@ -253,13 +286,8 @@ class MySensorsDeviceEntity(object): @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_DEVICE: self.gateway.device, ATTR_NODE_ID: self.node_id, ATTR_CHILD_ID: self.child_id, ATTR_BATTERY_LEVEL: self.battery_level, From d1e94b958f6c8ab004c3cf87f165c0881c671810 Mon Sep 17 00:00:00 2001 From: MartinHjelmare Date: Thu, 25 Aug 2016 19:07:22 +0200 Subject: [PATCH 2/2] Extract mqtt string into constant and add log --- homeassistant/components/mysensors.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/mysensors.py b/homeassistant/components/mysensors.py index 8a53bc011cd..5169e86ff04 100644 --- a/homeassistant/components/mysensors.py +++ b/homeassistant/components/mysensors.py @@ -31,6 +31,7 @@ DEFAULT_TCP_PORT = 5003 DOMAIN = 'mysensors' DEPENDENCIES = [] +MQTT_COMPONENT = 'mqtt' REQUIREMENTS = [ 'https://github.com/theolind/pymysensors/archive/' '8ce98b7fb56f7921a808eb66845ce8b2c455c81e.zip#pymysensors==0.7.1'] @@ -64,10 +65,10 @@ def setup(hass, config): # pylint: disable=too-many-locals out_prefix): """Return gateway after setup of the gateway.""" # pylint: disable=too-many-arguments - if device in 'mqtt': - if not setup_component(hass, 'mqtt', config): + if device == MQTT_COMPONENT: + if not setup_component(hass, MQTT_COMPONENT, config): return - mqtt = get_component('mqtt') + mqtt = get_component(MQTT_COMPONENT) retain = config[DOMAIN].get(CONF_RETAIN, True) def pub_callback(topic, payload, qos, retain): @@ -139,6 +140,11 @@ def setup(hass, config): # pylint: disable=too-many-locals if GATEWAYS[device] is None: GATEWAYS.pop(device) + if not GATEWAYS: + _LOGGER.error( + 'No devices could be setup as gateways, check your configuration') + return False + for component in 'sensor', 'switch', 'light', 'binary_sensor': discovery.load_platform(hass, component, DOMAIN, {}, config) @@ -228,7 +234,7 @@ class GatewayWrapper(object): """Return a new callback function.""" def node_update(update_type, node_id): """Callback for node updates from the MySensors gateway.""" - _LOGGER.debug('update %s: node %s', update_type, node_id) + _LOGGER.debug('Update %s: node %s', update_type, node_id) for callback in self.platform_callbacks: callback(self, node_id) @@ -299,7 +305,7 @@ class MySensorsDeviceEntity(object): try: attr[set_req(value_type).name] = value except ValueError: - _LOGGER.error('value_type %s is not valid for mysensors ' + _LOGGER.error('Value_type %s is not valid for mysensors ' 'version %s', value_type, self.gateway.version) return attr