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.
This commit is contained in:
Paulus Schoutsen 2016-04-19 21:00:56 -07:00
parent 2e79e9d5bb
commit 9d391becc1
8 changed files with 71 additions and 39 deletions

View File

@ -6,10 +6,9 @@ https://home-assistant.io/components/binary_sensor.mysensors/
""" """
import logging import logging
from homeassistant.const import ( from homeassistant.components.binary_sensor import (SENSOR_CLASSES,
ATTR_BATTERY_LEVEL, STATE_OFF, STATE_ON) BinarySensorDevice)
from homeassistant.components.binary_sensor import ( from homeassistant.const import ATTR_BATTERY_LEVEL, STATE_OFF, STATE_ON
BinarySensorDevice, SENSOR_CLASSES)
from homeassistant.loader import get_component from homeassistant.loader import get_component
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -101,8 +100,13 @@ class MySensorsBinarySensor(BinarySensorDevice):
@property @property
def device_state_attributes(self): def device_state_attributes(self):
"""Return device specific state attributes.""" """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 = {
self.mysensors.ATTR_PORT: self.gateway.port, self.mysensors.ATTR_DEVICE: device,
self.mysensors.ATTR_NODE_ID: self.node_id, self.mysensors.ATTR_NODE_ID: self.node_id,
self.mysensors.ATTR_CHILD_ID: self.child_id, self.mysensors.ATTR_CHILD_ID: self.child_id,
ATTR_BATTERY_LEVEL: self.battery_level, ATTR_BATTERY_LEVEL: self.battery_level,

View File

@ -6,8 +6,8 @@ https://home-assistant.io/components/light.mysensors/
""" """
import logging import logging
from homeassistant.components.light import ( from homeassistant.components.light import (ATTR_BRIGHTNESS, ATTR_RGB_COLOR,
ATTR_BRIGHTNESS, ATTR_RGB_COLOR, Light) Light)
from homeassistant.const import ATTR_BATTERY_LEVEL, STATE_OFF, STATE_ON from homeassistant.const import ATTR_BATTERY_LEVEL, STATE_OFF, STATE_ON
from homeassistant.loader import get_component from homeassistant.loader import get_component
from homeassistant.util.color import rgb_hex_to_rgb_list from homeassistant.util.color import rgb_hex_to_rgb_list
@ -100,15 +100,20 @@ class MySensorsLight(Light):
@property @property
def device_state_attributes(self): def device_state_attributes(self):
"""Return device specific state attributes.""" """Return device specific state attributes."""
device_attr = { address = getattr(self.gateway, 'server_address', None)
self.mysensors.ATTR_PORT: self.gateway.port, 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_NODE_ID: self.node_id,
self.mysensors.ATTR_CHILD_ID: self.child_id, self.mysensors.ATTR_CHILD_ID: self.child_id,
ATTR_BATTERY_LEVEL: self.battery_level, ATTR_BATTERY_LEVEL: self.battery_level,
} }
for value_type, value in self._values.items(): for value_type, value in self._values.items():
device_attr[self.gateway.const.SetReq(value_type).name] = value attr[self.gateway.const.SetReq(value_type).name] = value
return device_attr return attr
@property @property
def available(self): def available(self):

View File

@ -5,33 +5,36 @@ 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
import socket
import homeassistant.bootstrap as bootstrap import homeassistant.bootstrap as bootstrap
from homeassistant.const import ( from homeassistant.const import (ATTR_DISCOVERED, ATTR_SERVICE,
ATTR_DISCOVERED, ATTR_SERVICE, EVENT_HOMEASSISTANT_START, CONF_OPTIMISTIC, EVENT_HOMEASSISTANT_START,
EVENT_HOMEASSISTANT_STOP, EVENT_PLATFORM_DISCOVERED, TEMP_CELSIUS, EVENT_HOMEASSISTANT_STOP,
CONF_OPTIMISTIC) EVENT_PLATFORM_DISCOVERED, TEMP_CELSIUS)
from homeassistant.helpers import validate_config from homeassistant.helpers import validate_config
CONF_GATEWAYS = 'gateways' CONF_GATEWAYS = 'gateways'
CONF_PORT = 'port' CONF_DEVICE = 'device'
CONF_DEBUG = 'debug' CONF_DEBUG = 'debug'
CONF_PERSISTENCE = 'persistence' CONF_PERSISTENCE = 'persistence'
CONF_PERSISTENCE_FILE = 'persistence_file' CONF_PERSISTENCE_FILE = 'persistence_file'
CONF_VERSION = 'version' CONF_VERSION = 'version'
CONF_BAUD_RATE = 'baud_rate' CONF_BAUD_RATE = 'baud_rate'
CONF_TCP_PORT = 'tcp_port'
DEFAULT_VERSION = '1.4' DEFAULT_VERSION = '1.4'
DEFAULT_BAUD_RATE = 115200 DEFAULT_BAUD_RATE = 115200
DEFAULT_TCP_PORT = 5003
DOMAIN = 'mysensors' DOMAIN = 'mysensors'
DEPENDENCIES = [] DEPENDENCIES = []
REQUIREMENTS = [ REQUIREMENTS = [
'https://github.com/theolind/pymysensors/archive/' 'https://github.com/theolind/pymysensors/archive/'
'f0c928532167fb24823efa793ec21ca646fd37a6.zip#pymysensors==0.5'] 'cc5d0b325e13c2b623fa934f69eea7cd4555f110.zip#pymysensors==0.6']
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
ATTR_NODE_ID = 'node_id' ATTR_NODE_ID = 'node_id'
ATTR_CHILD_ID = 'child_id' ATTR_CHILD_ID = 'child_id'
ATTR_PORT = 'port' ATTR_DEVICE = 'device'
GATEWAYS = None 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.""" """Setup the MySensors component."""
if not validate_config(config, if not validate_config(config,
{DOMAIN: [CONF_GATEWAYS]}, {DOMAIN: [CONF_GATEWAYS]},
_LOGGER): _LOGGER):
return False return False
if not all(CONF_PORT in gateway if not all(CONF_DEVICE in gateway
for gateway in config[DOMAIN][CONF_GATEWAYS]): for gateway in config[DOMAIN][CONF_GATEWAYS]):
_LOGGER.error('Missing required configuration items ' _LOGGER.error('Missing required configuration items '
'in %s: %s', DOMAIN, CONF_PORT) 'in %s: %s', DOMAIN, CONF_DEVICE)
return False return False
import mysensors.mysensors as mysensors import mysensors.mysensors as mysensors
version = str(config[DOMAIN].get(CONF_VERSION, DEFAULT_VERSION)) version = str(config[DOMAIN].get(CONF_VERSION, DEFAULT_VERSION))
is_metric = (hass.config.temperature_unit == TEMP_CELSIUS) 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.""" """Return gateway after setup of the gateway."""
gateway = mysensors.SerialGateway(port, event_callback=None, try:
persistence=persistence, socket.inet_aton(device)
persistence_file=persistence_file, # valid ip address
protocol_version=version, gateway = mysensors.TCPGateway(
baud=baud_rate) 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.metric = is_metric
gateway.debug = config[DOMAIN].get(CONF_DEBUG, False) gateway.debug = config[DOMAIN].get(CONF_DEBUG, False)
optimistic = config[DOMAIN].get(CONF_OPTIMISTIC, False) optimistic = config[DOMAIN].get(CONF_OPTIMISTIC, False)
@ -93,22 +105,22 @@ def setup(hass, config):
return gateway return gateway
# Setup all ports from config # Setup all devices from config
global GATEWAYS global GATEWAYS
GATEWAYS = {} GATEWAYS = {}
conf_gateways = config[DOMAIN][CONF_GATEWAYS] conf_gateways = config[DOMAIN][CONF_GATEWAYS]
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)
for index, gway in enumerate(conf_gateways): for index, gway in enumerate(conf_gateways):
port = gway[CONF_PORT] device = gway[CONF_DEVICE]
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)))
baud_rate = gway.get(CONF_BAUD_RATE, DEFAULT_BAUD_RATE) baud_rate = gway.get(CONF_BAUD_RATE, DEFAULT_BAUD_RATE)
GATEWAYS[port] = setup_gateway( tcp_port = gway.get(CONF_TCP_PORT, DEFAULT_TCP_PORT)
port, persistence, persistence_file, version, baud_rate) GATEWAYS[device] = setup_gateway(
device, persistence_file, baud_rate, tcp_port)
for (component, discovery_service) in DISCOVERY_COMPONENTS: for (component, discovery_service) in DISCOVERY_COMPONENTS:
# Ensure component is loaded # Ensure component is loaded

View File

@ -6,8 +6,8 @@ https://home-assistant.io/components/sensor.mysensors/
""" """
import logging import logging
from homeassistant.const import ( from homeassistant.const import (ATTR_BATTERY_LEVEL, STATE_OFF, STATE_ON,
ATTR_BATTERY_LEVEL, STATE_OFF, STATE_ON, TEMP_CELSIUS, TEMP_FAHRENHEIT) TEMP_CELSIUS, TEMP_FAHRENHEIT)
from homeassistant.helpers.entity import Entity from homeassistant.helpers.entity import Entity
from homeassistant.loader import get_component from homeassistant.loader import get_component
@ -157,8 +157,13 @@ class MySensorsSensor(Entity):
@property @property
def device_state_attributes(self): def device_state_attributes(self):
"""Return device specific state attributes.""" """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 = {
self.mysensors.ATTR_PORT: self.gateway.port, self.mysensors.ATTR_DEVICE: device,
self.mysensors.ATTR_NODE_ID: self.node_id, self.mysensors.ATTR_NODE_ID: self.node_id,
self.mysensors.ATTR_CHILD_ID: self.child_id, self.mysensors.ATTR_CHILD_ID: self.child_id,
ATTR_BATTERY_LEVEL: self.battery_level, ATTR_BATTERY_LEVEL: self.battery_level,

View File

@ -99,8 +99,13 @@ class MySensorsSwitch(SwitchDevice):
@property @property
def device_state_attributes(self): def device_state_attributes(self):
"""Return device specific state attributes.""" """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 = {
self.mysensors.ATTR_PORT: self.gateway.port, self.mysensors.ATTR_DEVICE: device,
self.mysensors.ATTR_NODE_ID: self.node_id, self.mysensors.ATTR_NODE_ID: self.node_id,
self.mysensors.ATTR_CHILD_ID: self.child_id, self.mysensors.ATTR_CHILD_ID: self.child_id,
ATTR_BATTERY_LEVEL: self.battery_level, ATTR_BATTERY_LEVEL: self.battery_level,

View File

@ -31,7 +31,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
return True return True
# pylint: disable=too-many-instance-attributes # pylint: disable=too-many-instance-attributes, import-error
class EQ3BTSmartThermostat(ThermostatDevice): class EQ3BTSmartThermostat(ThermostatDevice):
"""Representation of a EQ3 Bluetooth Smart thermostat.""" """Representation of a EQ3 Bluetooth Smart thermostat."""

View File

@ -35,7 +35,7 @@ blinkstick==1.1.7
blockchain==1.3.1 blockchain==1.3.1
# homeassistant.components.thermostat.eq3btsmart # homeassistant.components.thermostat.eq3btsmart
bluepy_devices>=0.2.0 # bluepy_devices>=0.2.0
# homeassistant.components.notify.xmpp # homeassistant.components.notify.xmpp
dnspython3==1.12.0 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 https://github.com/sander76/powerviewApi/archive/master.zip#powerviewApi==0.2
# homeassistant.components.mysensors # 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 # homeassistant.components.notify.googlevoice
https://github.com/w1ll1am23/pygooglevoice-sms/archive/7c5ee9969b97a7992fc86a753fe9f20e3ffa3f7c.zip#pygooglevoice-sms==0.0.1 https://github.com/w1ll1am23/pygooglevoice-sms/archive/7c5ee9969b97a7992fc86a753fe9f20e3ffa3f7c.zip#pygooglevoice-sms==0.0.1

View File

@ -11,6 +11,7 @@ COMMENT_REQUIREMENTS = [
'Adafruit_Python_DHT', 'Adafruit_Python_DHT',
'fritzconnection', 'fritzconnection',
'pybluez', 'pybluez',
'bluepy',
] ]