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
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,

View File

@ -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):

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/
"""
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

View File

@ -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,

View File

@ -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,

View File

@ -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."""

View File

@ -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

View File

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