Use voluptuous for mysensors (#2992)
* Add voluptuous config validation for mysensors * Remove and clean up parts that are not needed for pymysensors 0.7.
This commit is contained in:
parent
6f1c97b9d3
commit
6acaf25b0d
5 changed files with 68 additions and 57 deletions
|
@ -32,7 +32,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||||
pres.S_MOTION: [set_req.V_TRIPPED],
|
pres.S_MOTION: [set_req.V_TRIPPED],
|
||||||
pres.S_SMOKE: [set_req.V_TRIPPED],
|
pres.S_SMOKE: [set_req.V_TRIPPED],
|
||||||
}
|
}
|
||||||
if float(gateway.version) >= 1.5:
|
if float(gateway.protocol_version) >= 1.5:
|
||||||
map_sv_types.update({
|
map_sv_types.update({
|
||||||
pres.S_SPRINKLER: [set_req.V_TRIPPED],
|
pres.S_SPRINKLER: [set_req.V_TRIPPED],
|
||||||
pres.S_WATER_LEAK: [set_req.V_TRIPPED],
|
pres.S_WATER_LEAK: [set_req.V_TRIPPED],
|
||||||
|
@ -66,7 +66,7 @@ class MySensorsBinarySensor(
|
||||||
pres.S_MOTION: 'motion',
|
pres.S_MOTION: 'motion',
|
||||||
pres.S_SMOKE: 'smoke',
|
pres.S_SMOKE: 'smoke',
|
||||||
}
|
}
|
||||||
if float(self.gateway.version) >= 1.5:
|
if float(self.gateway.protocol_version) >= 1.5:
|
||||||
class_map.update({
|
class_map.update({
|
||||||
pres.S_SPRINKLER: 'sprinkler',
|
pres.S_SPRINKLER: 'sprinkler',
|
||||||
pres.S_WATER_LEAK: 'leak',
|
pres.S_WATER_LEAK: 'leak',
|
||||||
|
|
|
@ -40,7 +40,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||||
device_class_map = {
|
device_class_map = {
|
||||||
pres.S_DIMMER: MySensorsLightDimmer,
|
pres.S_DIMMER: MySensorsLightDimmer,
|
||||||
}
|
}
|
||||||
if float(gateway.version) >= 1.5:
|
if float(gateway.protocol_version) >= 1.5:
|
||||||
# Add V_RGBW when rgb_white is implemented in the frontend
|
# Add V_RGBW when rgb_white is implemented in the frontend
|
||||||
map_sv_types.update({
|
map_sv_types.update({
|
||||||
pres.S_RGB_LIGHT: [set_req.V_RGB],
|
pres.S_RGB_LIGHT: [set_req.V_RGB],
|
||||||
|
@ -169,7 +169,7 @@ class MySensorsLight(mysensors.MySensorsDeviceEntity, Light):
|
||||||
|
|
||||||
def _turn_off_rgb_or_w(self, value_type=None, value=None):
|
def _turn_off_rgb_or_w(self, value_type=None, value=None):
|
||||||
"""Turn off RGB or RGBW child device."""
|
"""Turn off RGB or RGBW child device."""
|
||||||
if float(self.gateway.version) >= 1.5:
|
if float(self.gateway.protocol_version) >= 1.5:
|
||||||
set_req = self.gateway.const.SetReq
|
set_req = self.gateway.const.SetReq
|
||||||
if self.value_type == set_req.V_RGB:
|
if self.value_type == set_req.V_RGB:
|
||||||
value = '000000'
|
value = '000000'
|
||||||
|
@ -227,7 +227,6 @@ class MySensorsLight(mysensors.MySensorsDeviceEntity, Light):
|
||||||
"""Update the controller with the latest value from a sensor."""
|
"""Update the controller with the latest value from a sensor."""
|
||||||
node = self.gateway.sensors[self.node_id]
|
node = self.gateway.sensors[self.node_id]
|
||||||
child = node.children[self.child_id]
|
child = node.children[self.child_id]
|
||||||
self.battery_level = node.battery_level
|
|
||||||
for value_type, value in child.values.items():
|
for value_type, value in child.values.items():
|
||||||
_LOGGER.debug(
|
_LOGGER.debug(
|
||||||
'%s: value_type %s, value = %s', self._name, value_type, value)
|
'%s: value_type %s, value = %s', self._name, value_type, value)
|
||||||
|
|
|
@ -7,59 +7,74 @@ https://home-assistant.io/components/sensor.mysensors/
|
||||||
import logging
|
import logging
|
||||||
import socket
|
import socket
|
||||||
|
|
||||||
|
import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant.bootstrap import setup_component
|
from homeassistant.bootstrap import setup_component
|
||||||
|
import homeassistant.helpers.config_validation as cv
|
||||||
from homeassistant.const import (ATTR_BATTERY_LEVEL, CONF_OPTIMISTIC,
|
from homeassistant.const import (ATTR_BATTERY_LEVEL, CONF_OPTIMISTIC,
|
||||||
EVENT_HOMEASSISTANT_START,
|
EVENT_HOMEASSISTANT_START,
|
||||||
EVENT_HOMEASSISTANT_STOP, STATE_OFF, STATE_ON)
|
EVENT_HOMEASSISTANT_STOP, STATE_OFF, STATE_ON)
|
||||||
from homeassistant.helpers import discovery, validate_config
|
from homeassistant.helpers import discovery
|
||||||
from homeassistant.loader import get_component
|
from homeassistant.loader import get_component
|
||||||
|
|
||||||
CONF_GATEWAYS = 'gateways'
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
ATTR_NODE_ID = 'node_id'
|
||||||
|
ATTR_CHILD_ID = 'child_id'
|
||||||
|
ATTR_DESCRIPTION = 'description'
|
||||||
|
ATTR_DEVICE = 'device'
|
||||||
|
CONF_BAUD_RATE = 'baud_rate'
|
||||||
CONF_DEVICE = 'device'
|
CONF_DEVICE = 'device'
|
||||||
CONF_DEBUG = 'debug'
|
CONF_DEBUG = 'debug'
|
||||||
|
CONF_GATEWAYS = 'gateways'
|
||||||
CONF_PERSISTENCE = 'persistence'
|
CONF_PERSISTENCE = 'persistence'
|
||||||
CONF_PERSISTENCE_FILE = 'persistence_file'
|
CONF_PERSISTENCE_FILE = 'persistence_file'
|
||||||
CONF_VERSION = 'version'
|
|
||||||
CONF_BAUD_RATE = 'baud_rate'
|
|
||||||
CONF_TCP_PORT = 'tcp_port'
|
CONF_TCP_PORT = 'tcp_port'
|
||||||
CONF_TOPIC_IN_PREFIX = 'topic_in_prefix'
|
CONF_TOPIC_IN_PREFIX = 'topic_in_prefix'
|
||||||
CONF_TOPIC_OUT_PREFIX = 'topic_out_prefix'
|
CONF_TOPIC_OUT_PREFIX = 'topic_out_prefix'
|
||||||
CONF_RETAIN = 'retain'
|
CONF_RETAIN = 'retain'
|
||||||
DEFAULT_VERSION = '1.4'
|
CONF_VERSION = 'version'
|
||||||
|
DEFAULT_VERSION = 1.4
|
||||||
DEFAULT_BAUD_RATE = 115200
|
DEFAULT_BAUD_RATE = 115200
|
||||||
DEFAULT_TCP_PORT = 5003
|
DEFAULT_TCP_PORT = 5003
|
||||||
|
|
||||||
DOMAIN = 'mysensors'
|
DOMAIN = 'mysensors'
|
||||||
DEPENDENCIES = []
|
GATEWAYS = None
|
||||||
MQTT_COMPONENT = 'mqtt'
|
MQTT_COMPONENT = 'mqtt'
|
||||||
REQUIREMENTS = [
|
REQUIREMENTS = [
|
||||||
'https://github.com/theolind/pymysensors/archive/'
|
'https://github.com/theolind/pymysensors/archive/'
|
||||||
'8ce98b7fb56f7921a808eb66845ce8b2c455c81e.zip#pymysensors==0.7.1']
|
'8ce98b7fb56f7921a808eb66845ce8b2c455c81e.zip#pymysensors==0.7.1']
|
||||||
_LOGGER = logging.getLogger(__name__)
|
|
||||||
ATTR_NODE_ID = 'node_id'
|
|
||||||
ATTR_CHILD_ID = 'child_id'
|
|
||||||
ATTR_DEVICE = 'device'
|
|
||||||
|
|
||||||
GATEWAYS = None
|
CONFIG_SCHEMA = vol.Schema({
|
||||||
|
DOMAIN: vol.Schema({
|
||||||
|
vol.Required(CONF_GATEWAYS): vol.All(cv.ensure_list, [
|
||||||
|
{
|
||||||
|
vol.Required(CONF_DEVICE): cv.string,
|
||||||
|
vol.Optional(CONF_PERSISTENCE_FILE): cv.string,
|
||||||
|
vol.Optional(
|
||||||
|
CONF_BAUD_RATE,
|
||||||
|
default=DEFAULT_BAUD_RATE): cv.positive_int,
|
||||||
|
vol.Optional(
|
||||||
|
CONF_TCP_PORT,
|
||||||
|
default=DEFAULT_TCP_PORT): cv.port,
|
||||||
|
vol.Optional(CONF_TOPIC_IN_PREFIX, default=''): cv.string,
|
||||||
|
vol.Optional(CONF_TOPIC_OUT_PREFIX, default=''): cv.string,
|
||||||
|
},
|
||||||
|
]),
|
||||||
|
vol.Optional(CONF_DEBUG, default=False): cv.boolean,
|
||||||
|
vol.Optional(CONF_OPTIMISTIC, default=False): cv.boolean,
|
||||||
|
vol.Optional(CONF_PERSISTENCE, default=True): cv.boolean,
|
||||||
|
vol.Optional(CONF_RETAIN, default=True): cv.boolean,
|
||||||
|
vol.Optional(CONF_VERSION, default=DEFAULT_VERSION): vol.Coerce(float),
|
||||||
|
})
|
||||||
|
}, extra=vol.ALLOW_EXTRA)
|
||||||
|
|
||||||
|
|
||||||
def setup(hass, config): # pylint: disable=too-many-locals
|
def setup(hass, config): # pylint: disable=too-many-locals
|
||||||
"""Setup the MySensors component."""
|
"""Setup the MySensors component."""
|
||||||
if not validate_config(config,
|
|
||||||
{DOMAIN: [CONF_GATEWAYS]},
|
|
||||||
_LOGGER):
|
|
||||||
return False
|
|
||||||
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_DEVICE)
|
|
||||||
return False
|
|
||||||
|
|
||||||
import mysensors.mysensors as mysensors
|
import mysensors.mysensors as mysensors
|
||||||
|
|
||||||
version = str(config[DOMAIN].get(CONF_VERSION, DEFAULT_VERSION))
|
version = config[DOMAIN].get(CONF_VERSION)
|
||||||
is_metric = hass.config.units.is_metric
|
persistence = config[DOMAIN].get(CONF_PERSISTENCE)
|
||||||
persistence = config[DOMAIN].get(CONF_PERSISTENCE, True)
|
|
||||||
|
|
||||||
def setup_gateway(device, persistence_file, baud_rate, tcp_port, in_prefix,
|
def setup_gateway(device, persistence_file, baud_rate, tcp_port, in_prefix,
|
||||||
out_prefix):
|
out_prefix):
|
||||||
|
@ -69,7 +84,7 @@ def setup(hass, config): # pylint: disable=too-many-locals
|
||||||
if not setup_component(hass, MQTT_COMPONENT, config):
|
if not setup_component(hass, MQTT_COMPONENT, config):
|
||||||
return
|
return
|
||||||
mqtt = get_component(MQTT_COMPONENT)
|
mqtt = get_component(MQTT_COMPONENT)
|
||||||
retain = config[DOMAIN].get(CONF_RETAIN, True)
|
retain = config[DOMAIN].get(CONF_RETAIN)
|
||||||
|
|
||||||
def pub_callback(topic, payload, qos, retain):
|
def pub_callback(topic, payload, qos, retain):
|
||||||
"""Call mqtt publish function."""
|
"""Call mqtt publish function."""
|
||||||
|
@ -98,10 +113,10 @@ def setup(hass, config): # pylint: disable=too-many-locals
|
||||||
device, event_callback=None, persistence=persistence,
|
device, event_callback=None, persistence=persistence,
|
||||||
persistence_file=persistence_file,
|
persistence_file=persistence_file,
|
||||||
protocol_version=version, baud=baud_rate)
|
protocol_version=version, baud=baud_rate)
|
||||||
gateway.metric = is_metric
|
gateway.metric = hass.config.units.is_metric
|
||||||
gateway.debug = config[DOMAIN].get(CONF_DEBUG, False)
|
gateway.debug = config[DOMAIN].get(CONF_DEBUG)
|
||||||
optimistic = config[DOMAIN].get(CONF_OPTIMISTIC, False)
|
optimistic = config[DOMAIN].get(CONF_OPTIMISTIC)
|
||||||
gateway = GatewayWrapper(gateway, version, optimistic, device)
|
gateway = GatewayWrapper(gateway, optimistic, device)
|
||||||
# pylint: disable=attribute-defined-outside-init
|
# pylint: disable=attribute-defined-outside-init
|
||||||
gateway.event_callback = gateway.callback_factory()
|
gateway.event_callback = gateway.callback_factory()
|
||||||
|
|
||||||
|
@ -122,18 +137,16 @@ def setup(hass, config): # pylint: disable=too-many-locals
|
||||||
global GATEWAYS
|
global GATEWAYS
|
||||||
GATEWAYS = {}
|
GATEWAYS = {}
|
||||||
conf_gateways = config[DOMAIN][CONF_GATEWAYS]
|
conf_gateways = config[DOMAIN][CONF_GATEWAYS]
|
||||||
if isinstance(conf_gateways, dict):
|
|
||||||
conf_gateways = [conf_gateways]
|
|
||||||
|
|
||||||
for index, gway in enumerate(conf_gateways):
|
for index, gway in enumerate(conf_gateways):
|
||||||
device = gway[CONF_DEVICE]
|
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)
|
||||||
tcp_port = gway.get(CONF_TCP_PORT, DEFAULT_TCP_PORT)
|
tcp_port = gway.get(CONF_TCP_PORT)
|
||||||
in_prefix = gway.get(CONF_TOPIC_IN_PREFIX, '')
|
in_prefix = gway.get(CONF_TOPIC_IN_PREFIX)
|
||||||
out_prefix = gway.get(CONF_TOPIC_OUT_PREFIX, '')
|
out_prefix = gway.get(CONF_TOPIC_OUT_PREFIX)
|
||||||
GATEWAYS[device] = setup_gateway(
|
GATEWAYS[device] = setup_gateway(
|
||||||
device, persistence_file, baud_rate, tcp_port, in_prefix,
|
device, persistence_file, baud_rate, tcp_port, in_prefix,
|
||||||
out_prefix)
|
out_prefix)
|
||||||
|
@ -189,24 +202,22 @@ class GatewayWrapper(object):
|
||||||
|
|
||||||
# pylint: disable=too-few-public-methods
|
# pylint: disable=too-few-public-methods
|
||||||
|
|
||||||
def __init__(self, gateway, version, optimistic, device):
|
def __init__(self, gateway, optimistic, device):
|
||||||
"""Setup class attributes on instantiation.
|
"""Setup class attributes on instantiation.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
gateway (mysensors.Gateway): Gateway to wrap.
|
gateway (mysensors.SerialGateway): Gateway to wrap.
|
||||||
version (str): Version of mysensors API.
|
|
||||||
optimistic (bool): Send values to actuators without feedback state.
|
optimistic (bool): Send values to actuators without feedback state.
|
||||||
device (str): Path to serial port, ip adress or mqtt.
|
device (str): Path to serial port, ip adress or mqtt.
|
||||||
|
|
||||||
Attributes:
|
Attributes:
|
||||||
_wrapped_gateway (mysensors.Gateway): Wrapped gateway.
|
_wrapped_gateway (mysensors.SerialGateway): Wrapped gateway.
|
||||||
version (str): Version of mysensors API.
|
|
||||||
platform_callbacks (list): Callback functions, one per platform.
|
platform_callbacks (list): Callback functions, one per platform.
|
||||||
optimistic (bool): Send values to actuators without feedback state.
|
optimistic (bool): Send values to actuators without feedback state.
|
||||||
|
device (str): Device configured as gateway.
|
||||||
__initialised (bool): True if GatewayWrapper is initialised.
|
__initialised (bool): True if GatewayWrapper is initialised.
|
||||||
"""
|
"""
|
||||||
self._wrapped_gateway = gateway
|
self._wrapped_gateway = gateway
|
||||||
self.version = version
|
|
||||||
self.platform_callbacks = []
|
self.platform_callbacks = []
|
||||||
self.optimistic = optimistic
|
self.optimistic = optimistic
|
||||||
self.device = device
|
self.device = device
|
||||||
|
@ -244,7 +255,7 @@ class GatewayWrapper(object):
|
||||||
class MySensorsDeviceEntity(object):
|
class MySensorsDeviceEntity(object):
|
||||||
"""Represent a MySensors entity."""
|
"""Represent a MySensors entity."""
|
||||||
|
|
||||||
# pylint: disable=too-many-arguments,too-many-instance-attributes
|
# pylint: disable=too-many-arguments
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self, gateway, node_id, child_id, name, value_type, child_type):
|
self, gateway, node_id, child_id, name, value_type, child_type):
|
||||||
|
@ -276,7 +287,6 @@ class MySensorsDeviceEntity(object):
|
||||||
self._name = name
|
self._name = name
|
||||||
self.value_type = value_type
|
self.value_type = value_type
|
||||||
self.child_type = child_type
|
self.child_type = child_type
|
||||||
self.battery_level = 0
|
|
||||||
self._values = {}
|
self._values = {}
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -292,11 +302,14 @@ class MySensorsDeviceEntity(object):
|
||||||
@property
|
@property
|
||||||
def device_state_attributes(self):
|
def device_state_attributes(self):
|
||||||
"""Return device specific state attributes."""
|
"""Return device specific state attributes."""
|
||||||
|
node = self.gateway.sensors[self.node_id]
|
||||||
|
child = node.children[self.child_id]
|
||||||
attr = {
|
attr = {
|
||||||
|
ATTR_BATTERY_LEVEL: node.battery_level,
|
||||||
|
ATTR_CHILD_ID: self.child_id,
|
||||||
|
ATTR_DESCRIPTION: child.description,
|
||||||
ATTR_DEVICE: self.gateway.device,
|
ATTR_DEVICE: self.gateway.device,
|
||||||
ATTR_NODE_ID: self.node_id,
|
ATTR_NODE_ID: self.node_id,
|
||||||
ATTR_CHILD_ID: self.child_id,
|
|
||||||
ATTR_BATTERY_LEVEL: self.battery_level,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
set_req = self.gateway.const.SetReq
|
set_req = self.gateway.const.SetReq
|
||||||
|
@ -307,7 +320,7 @@ class MySensorsDeviceEntity(object):
|
||||||
except ValueError:
|
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,
|
'version %s', value_type,
|
||||||
self.gateway.version)
|
self.gateway.protocol_version)
|
||||||
return attr
|
return attr
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -319,7 +332,6 @@ class MySensorsDeviceEntity(object):
|
||||||
"""Update the controller with the latest value from a sensor."""
|
"""Update the controller with the latest value from a sensor."""
|
||||||
node = self.gateway.sensors[self.node_id]
|
node = self.gateway.sensors[self.node_id]
|
||||||
child = node.children[self.child_id]
|
child = node.children[self.child_id]
|
||||||
self.battery_level = node.battery_level
|
|
||||||
set_req = self.gateway.const.SetReq
|
set_req = self.gateway.const.SetReq
|
||||||
for value_type, value in child.values.items():
|
for value_type, value in child.values.items():
|
||||||
_LOGGER.debug(
|
_LOGGER.debug(
|
||||||
|
|
|
@ -47,12 +47,12 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||||
pres.S_SCENE_CONTROLLER: [set_req.V_SCENE_ON,
|
pres.S_SCENE_CONTROLLER: [set_req.V_SCENE_ON,
|
||||||
set_req.V_SCENE_OFF],
|
set_req.V_SCENE_OFF],
|
||||||
}
|
}
|
||||||
if float(gateway.version) < 1.5:
|
if float(gateway.protocol_version) < 1.5:
|
||||||
map_sv_types.update({
|
map_sv_types.update({
|
||||||
pres.S_AIR_QUALITY: [set_req.V_DUST_LEVEL],
|
pres.S_AIR_QUALITY: [set_req.V_DUST_LEVEL],
|
||||||
pres.S_DUST: [set_req.V_DUST_LEVEL],
|
pres.S_DUST: [set_req.V_DUST_LEVEL],
|
||||||
})
|
})
|
||||||
if float(gateway.version) >= 1.5:
|
if float(gateway.protocol_version) >= 1.5:
|
||||||
map_sv_types.update({
|
map_sv_types.update({
|
||||||
pres.S_COLOR_SENSOR: [set_req.V_RGB],
|
pres.S_COLOR_SENSOR: [set_req.V_RGB],
|
||||||
pres.S_MULTIMETER: [set_req.V_VOLTAGE,
|
pres.S_MULTIMETER: [set_req.V_VOLTAGE,
|
||||||
|
@ -99,7 +99,7 @@ class MySensorsSensor(mysensors.MySensorsDeviceEntity, Entity):
|
||||||
set_req.V_VOLTAGE: 'V',
|
set_req.V_VOLTAGE: 'V',
|
||||||
set_req.V_CURRENT: 'A',
|
set_req.V_CURRENT: 'A',
|
||||||
}
|
}
|
||||||
if float(self.gateway.version) >= 1.5:
|
if float(self.gateway.protocol_version) >= 1.5:
|
||||||
if set_req.V_UNIT_PREFIX in self._values:
|
if set_req.V_UNIT_PREFIX in self._values:
|
||||||
return self._values[
|
return self._values[
|
||||||
set_req.V_UNIT_PREFIX]
|
set_req.V_UNIT_PREFIX]
|
||||||
|
|
|
@ -55,7 +55,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||||
pres.S_LOCK: MySensorsSwitch,
|
pres.S_LOCK: MySensorsSwitch,
|
||||||
pres.S_IR: MySensorsIRSwitch,
|
pres.S_IR: MySensorsIRSwitch,
|
||||||
}
|
}
|
||||||
if float(gateway.version) >= 1.5:
|
if float(gateway.protocol_version) >= 1.5:
|
||||||
map_sv_types.update({
|
map_sv_types.update({
|
||||||
pres.S_BINARY: [set_req.V_STATUS, set_req.V_LIGHT],
|
pres.S_BINARY: [set_req.V_STATUS, set_req.V_LIGHT],
|
||||||
pres.S_SPRINKLER: [set_req.V_STATUS],
|
pres.S_SPRINKLER: [set_req.V_STATUS],
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue