From 9d391becc130bdede3cd08865fea9fd90cdf975a Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Tue, 19 Apr 2016 21:00:56 -0700 Subject: [PATCH] Add mysensors tcp ethernet gateway (#1861) * Bump version of pymysensors to 0.6, which includes the tcp gateway. * Update requirements_all.txt. * Replace CONF_PORT with CONF_DEVICE and ATTR_PORT with ATTR_DEVICE. * Add tcp_port in config. * Try to guess if tcp or serial gateway is configured, by validating device name as an ip address. If successful setup tcp gateway, if it fails, setup serial gateway. * Update device_state_attributes to show correct device, ethernet or serial. --- .../components/binary_sensor/mysensors.py | 14 +++-- homeassistant/components/light/mysensors.py | 17 +++--- homeassistant/components/mysensors.py | 54 +++++++++++-------- homeassistant/components/sensor/mysensors.py | 11 ++-- homeassistant/components/switch/mysensors.py | 7 ++- .../components/thermostat/eq3btsmart.py | 2 +- requirements_all.txt | 4 +- script/gen_requirements_all.py | 1 + 8 files changed, 71 insertions(+), 39 deletions(-) diff --git a/homeassistant/components/binary_sensor/mysensors.py b/homeassistant/components/binary_sensor/mysensors.py index cada64bc78f..3cc9798f288 100644 --- a/homeassistant/components/binary_sensor/mysensors.py +++ b/homeassistant/components/binary_sensor/mysensors.py @@ -6,10 +6,9 @@ https://home-assistant.io/components/binary_sensor.mysensors/ """ import logging -from homeassistant.const import ( - ATTR_BATTERY_LEVEL, STATE_OFF, STATE_ON) -from homeassistant.components.binary_sensor import ( - BinarySensorDevice, SENSOR_CLASSES) +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 _LOGGER = logging.getLogger(__name__) @@ -101,8 +100,13 @@ class MySensorsBinarySensor(BinarySensorDevice): @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_PORT: self.gateway.port, + 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, diff --git a/homeassistant/components/light/mysensors.py b/homeassistant/components/light/mysensors.py index bd74ef350f8..5a85b047be8 100644 --- a/homeassistant/components/light/mysensors.py +++ b/homeassistant/components/light/mysensors.py @@ -6,8 +6,8 @@ https://home-assistant.io/components/light.mysensors/ """ import logging -from homeassistant.components.light import ( - ATTR_BRIGHTNESS, ATTR_RGB_COLOR, Light) +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.util.color import rgb_hex_to_rgb_list @@ -100,15 +100,20 @@ class MySensorsLight(Light): @property def device_state_attributes(self): """Return device specific state attributes.""" - device_attr = { - self.mysensors.ATTR_PORT: self.gateway.port, + 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(): - device_attr[self.gateway.const.SetReq(value_type).name] = value - return device_attr + attr[self.gateway.const.SetReq(value_type).name] = value + return attr @property def available(self): diff --git a/homeassistant/components/mysensors.py b/homeassistant/components/mysensors.py index d620c2acb6a..204b2818de6 100644 --- a/homeassistant/components/mysensors.py +++ b/homeassistant/components/mysensors.py @@ -5,33 +5,36 @@ For more details about this platform, please refer to the documentation at https://home-assistant.io/components/sensor.mysensors/ """ import logging +import socket import homeassistant.bootstrap as bootstrap -from homeassistant.const import ( - ATTR_DISCOVERED, ATTR_SERVICE, EVENT_HOMEASSISTANT_START, - EVENT_HOMEASSISTANT_STOP, EVENT_PLATFORM_DISCOVERED, TEMP_CELSIUS, - CONF_OPTIMISTIC) +from homeassistant.const import (ATTR_DISCOVERED, ATTR_SERVICE, + CONF_OPTIMISTIC, EVENT_HOMEASSISTANT_START, + EVENT_HOMEASSISTANT_STOP, + EVENT_PLATFORM_DISCOVERED, TEMP_CELSIUS) from homeassistant.helpers import validate_config CONF_GATEWAYS = 'gateways' -CONF_PORT = 'port' +CONF_DEVICE = 'device' CONF_DEBUG = 'debug' CONF_PERSISTENCE = 'persistence' CONF_PERSISTENCE_FILE = 'persistence_file' CONF_VERSION = 'version' CONF_BAUD_RATE = 'baud_rate' +CONF_TCP_PORT = 'tcp_port' DEFAULT_VERSION = '1.4' DEFAULT_BAUD_RATE = 115200 +DEFAULT_TCP_PORT = 5003 DOMAIN = 'mysensors' DEPENDENCIES = [] REQUIREMENTS = [ 'https://github.com/theolind/pymysensors/archive/' - 'f0c928532167fb24823efa793ec21ca646fd37a6.zip#pymysensors==0.5'] + 'cc5d0b325e13c2b623fa934f69eea7cd4555f110.zip#pymysensors==0.6'] _LOGGER = logging.getLogger(__name__) ATTR_NODE_ID = 'node_id' ATTR_CHILD_ID = 'child_id' -ATTR_PORT = 'port' +ATTR_DEVICE = 'device' GATEWAYS = None @@ -49,30 +52,39 @@ DISCOVERY_COMPONENTS = [ ] -def setup(hass, config): +def setup(hass, config): # pylint: disable=too-many-locals """Setup the MySensors component.""" if not validate_config(config, {DOMAIN: [CONF_GATEWAYS]}, _LOGGER): return False - if not all(CONF_PORT in gateway + if not all(CONF_DEVICE in gateway for gateway in config[DOMAIN][CONF_GATEWAYS]): _LOGGER.error('Missing required configuration items ' - 'in %s: %s', DOMAIN, CONF_PORT) + 'in %s: %s', DOMAIN, CONF_DEVICE) return False import mysensors.mysensors as mysensors version = str(config[DOMAIN].get(CONF_VERSION, DEFAULT_VERSION)) is_metric = (hass.config.temperature_unit == TEMP_CELSIUS) + persistence = config[DOMAIN].get(CONF_PERSISTENCE, True) - def setup_gateway(port, persistence, persistence_file, version, baud_rate): + def setup_gateway(device, persistence_file, baud_rate, tcp_port): """Return gateway after setup of the gateway.""" - gateway = mysensors.SerialGateway(port, event_callback=None, - persistence=persistence, - persistence_file=persistence_file, - protocol_version=version, - baud=baud_rate) + 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) @@ -93,22 +105,22 @@ def setup(hass, config): return gateway - # Setup all ports from config + # Setup all devices from config global GATEWAYS GATEWAYS = {} conf_gateways = config[DOMAIN][CONF_GATEWAYS] if isinstance(conf_gateways, dict): conf_gateways = [conf_gateways] - persistence = config[DOMAIN].get(CONF_PERSISTENCE, True) for index, gway in enumerate(conf_gateways): - port = gway[CONF_PORT] + device = gway[CONF_DEVICE] persistence_file = gway.get( CONF_PERSISTENCE_FILE, hass.config.path('mysensors{}.pickle'.format(index + 1))) baud_rate = gway.get(CONF_BAUD_RATE, DEFAULT_BAUD_RATE) - GATEWAYS[port] = setup_gateway( - port, persistence, persistence_file, version, baud_rate) + tcp_port = gway.get(CONF_TCP_PORT, DEFAULT_TCP_PORT) + GATEWAYS[device] = setup_gateway( + device, persistence_file, baud_rate, tcp_port) for (component, discovery_service) in DISCOVERY_COMPONENTS: # Ensure component is loaded diff --git a/homeassistant/components/sensor/mysensors.py b/homeassistant/components/sensor/mysensors.py index ee1fcdcba95..ae959af3ac6 100644 --- a/homeassistant/components/sensor/mysensors.py +++ b/homeassistant/components/sensor/mysensors.py @@ -6,8 +6,8 @@ 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.const import (ATTR_BATTERY_LEVEL, STATE_OFF, STATE_ON, + TEMP_CELSIUS, TEMP_FAHRENHEIT) from homeassistant.helpers.entity import Entity from homeassistant.loader import get_component @@ -157,8 +157,13 @@ class MySensorsSensor(Entity): @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_PORT: self.gateway.port, + 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, diff --git a/homeassistant/components/switch/mysensors.py b/homeassistant/components/switch/mysensors.py index 2a8f683b938..3101f68322d 100644 --- a/homeassistant/components/switch/mysensors.py +++ b/homeassistant/components/switch/mysensors.py @@ -99,8 +99,13 @@ class MySensorsSwitch(SwitchDevice): @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_PORT: self.gateway.port, + 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, diff --git a/homeassistant/components/thermostat/eq3btsmart.py b/homeassistant/components/thermostat/eq3btsmart.py index 4165153c0dd..34c164f2c0d 100644 --- a/homeassistant/components/thermostat/eq3btsmart.py +++ b/homeassistant/components/thermostat/eq3btsmart.py @@ -31,7 +31,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): return True -# pylint: disable=too-many-instance-attributes +# pylint: disable=too-many-instance-attributes, import-error class EQ3BTSmartThermostat(ThermostatDevice): """Representation of a EQ3 Bluetooth Smart thermostat.""" diff --git a/requirements_all.txt b/requirements_all.txt index 7dae3df1b85..f83d4bc8779 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -35,7 +35,7 @@ blinkstick==1.1.7 blockchain==1.3.1 # homeassistant.components.thermostat.eq3btsmart -bluepy_devices>=0.2.0 +# bluepy_devices>=0.2.0 # homeassistant.components.notify.xmpp dnspython3==1.12.0 @@ -117,7 +117,7 @@ https://github.com/robbiet480/pygtfs/archive/432414b720c580fb2667a0a48f539118a2d https://github.com/sander76/powerviewApi/archive/master.zip#powerviewApi==0.2 # homeassistant.components.mysensors -https://github.com/theolind/pymysensors/archive/f0c928532167fb24823efa793ec21ca646fd37a6.zip#pymysensors==0.5 +https://github.com/theolind/pymysensors/archive/cc5d0b325e13c2b623fa934f69eea7cd4555f110.zip#pymysensors==0.6 # homeassistant.components.notify.googlevoice https://github.com/w1ll1am23/pygooglevoice-sms/archive/7c5ee9969b97a7992fc86a753fe9f20e3ffa3f7c.zip#pygooglevoice-sms==0.0.1 diff --git a/script/gen_requirements_all.py b/script/gen_requirements_all.py index 2530a27930f..32e7dc01dac 100755 --- a/script/gen_requirements_all.py +++ b/script/gen_requirements_all.py @@ -11,6 +11,7 @@ COMMENT_REQUIREMENTS = [ 'Adafruit_Python_DHT', 'fritzconnection', 'pybluez', + 'bluepy', ]