Refactor child validation (#23482)
* Try to make the process more readable and paritioned. * Validate child values using set message. * Only validate using relevant schemas. * Extract node validation. * Rework const types and schemas. * Rework child validator. * Enhance warning logging message.pull/23759/head
parent
c384adeef4
commit
c26af22edd
|
@ -1,5 +1,5 @@
|
|||
"""MySensors constants."""
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from collections import defaultdict
|
||||
|
||||
ATTR_DEVICES = 'devices'
|
||||
|
||||
|
@ -25,117 +25,102 @@ NODE_CALLBACK = 'mysensors_node_callback_{}_{}'
|
|||
TYPE = 'type'
|
||||
UPDATE_DELAY = 0.1
|
||||
|
||||
# MySensors const schemas
|
||||
BINARY_SENSOR_SCHEMA = {PLATFORM: 'binary_sensor', TYPE: 'V_TRIPPED'}
|
||||
CLIMATE_SCHEMA = {PLATFORM: 'climate', TYPE: 'V_HVAC_FLOW_STATE'}
|
||||
LIGHT_DIMMER_SCHEMA = {
|
||||
PLATFORM: 'light', TYPE: 'V_DIMMER',
|
||||
SCHEMA: {'V_DIMMER': cv.string, 'V_LIGHT': cv.string}}
|
||||
LIGHT_PERCENTAGE_SCHEMA = {
|
||||
PLATFORM: 'light', TYPE: 'V_PERCENTAGE',
|
||||
SCHEMA: {'V_PERCENTAGE': cv.string, 'V_STATUS': cv.string}}
|
||||
LIGHT_RGB_SCHEMA = {
|
||||
PLATFORM: 'light', TYPE: 'V_RGB', SCHEMA: {
|
||||
'V_RGB': cv.string, 'V_STATUS': cv.string}}
|
||||
LIGHT_RGBW_SCHEMA = {
|
||||
PLATFORM: 'light', TYPE: 'V_RGBW', SCHEMA: {
|
||||
'V_RGBW': cv.string, 'V_STATUS': cv.string}}
|
||||
NOTIFY_SCHEMA = {PLATFORM: 'notify', TYPE: 'V_TEXT'}
|
||||
DEVICE_TRACKER_SCHEMA = {PLATFORM: 'device_tracker', TYPE: 'V_POSITION'}
|
||||
DUST_SCHEMA = [
|
||||
{PLATFORM: 'sensor', TYPE: 'V_DUST_LEVEL'},
|
||||
{PLATFORM: 'sensor', TYPE: 'V_LEVEL'}]
|
||||
SWITCH_LIGHT_SCHEMA = {PLATFORM: 'switch', TYPE: 'V_LIGHT'}
|
||||
SWITCH_STATUS_SCHEMA = {PLATFORM: 'switch', TYPE: 'V_STATUS'}
|
||||
MYSENSORS_CONST_SCHEMA = {
|
||||
'S_DOOR': [BINARY_SENSOR_SCHEMA, {PLATFORM: 'switch', TYPE: 'V_ARMED'}],
|
||||
'S_MOTION': [BINARY_SENSOR_SCHEMA, {PLATFORM: 'switch', TYPE: 'V_ARMED'}],
|
||||
'S_SMOKE': [BINARY_SENSOR_SCHEMA, {PLATFORM: 'switch', TYPE: 'V_ARMED'}],
|
||||
'S_SPRINKLER': [
|
||||
BINARY_SENSOR_SCHEMA, {PLATFORM: 'switch', TYPE: 'V_STATUS'}],
|
||||
'S_WATER_LEAK': [
|
||||
BINARY_SENSOR_SCHEMA, {PLATFORM: 'switch', TYPE: 'V_ARMED'}],
|
||||
'S_SOUND': [
|
||||
BINARY_SENSOR_SCHEMA, {PLATFORM: 'sensor', TYPE: 'V_LEVEL'},
|
||||
{PLATFORM: 'switch', TYPE: 'V_ARMED'}],
|
||||
'S_VIBRATION': [
|
||||
BINARY_SENSOR_SCHEMA, {PLATFORM: 'sensor', TYPE: 'V_LEVEL'},
|
||||
{PLATFORM: 'switch', TYPE: 'V_ARMED'}],
|
||||
'S_MOISTURE': [
|
||||
BINARY_SENSOR_SCHEMA, {PLATFORM: 'sensor', TYPE: 'V_LEVEL'},
|
||||
{PLATFORM: 'switch', TYPE: 'V_ARMED'}],
|
||||
'S_HVAC': [CLIMATE_SCHEMA],
|
||||
'S_COVER': [
|
||||
{PLATFORM: 'cover', TYPE: 'V_DIMMER'},
|
||||
{PLATFORM: 'cover', TYPE: 'V_PERCENTAGE'},
|
||||
{PLATFORM: 'cover', TYPE: 'V_LIGHT'},
|
||||
{PLATFORM: 'cover', TYPE: 'V_STATUS'}],
|
||||
'S_DIMMER': [LIGHT_DIMMER_SCHEMA, LIGHT_PERCENTAGE_SCHEMA],
|
||||
'S_RGB_LIGHT': [LIGHT_RGB_SCHEMA],
|
||||
'S_RGBW_LIGHT': [LIGHT_RGBW_SCHEMA],
|
||||
'S_INFO': [NOTIFY_SCHEMA, {PLATFORM: 'sensor', TYPE: 'V_TEXT'}],
|
||||
'S_GPS': [
|
||||
DEVICE_TRACKER_SCHEMA, {PLATFORM: 'sensor', TYPE: 'V_POSITION'}],
|
||||
'S_TEMP': [{PLATFORM: 'sensor', TYPE: 'V_TEMP'}],
|
||||
'S_HUM': [{PLATFORM: 'sensor', TYPE: 'V_HUM'}],
|
||||
'S_BARO': [
|
||||
{PLATFORM: 'sensor', TYPE: 'V_PRESSURE'},
|
||||
{PLATFORM: 'sensor', TYPE: 'V_FORECAST'}],
|
||||
'S_WIND': [
|
||||
{PLATFORM: 'sensor', TYPE: 'V_WIND'},
|
||||
{PLATFORM: 'sensor', TYPE: 'V_GUST'},
|
||||
{PLATFORM: 'sensor', TYPE: 'V_DIRECTION'}],
|
||||
'S_RAIN': [
|
||||
{PLATFORM: 'sensor', TYPE: 'V_RAIN'},
|
||||
{PLATFORM: 'sensor', TYPE: 'V_RAINRATE'}],
|
||||
'S_UV': [{PLATFORM: 'sensor', TYPE: 'V_UV'}],
|
||||
'S_WEIGHT': [
|
||||
{PLATFORM: 'sensor', TYPE: 'V_WEIGHT'},
|
||||
{PLATFORM: 'sensor', TYPE: 'V_IMPEDANCE'}],
|
||||
'S_POWER': [
|
||||
{PLATFORM: 'sensor', TYPE: 'V_WATT'},
|
||||
{PLATFORM: 'sensor', TYPE: 'V_KWH'},
|
||||
{PLATFORM: 'sensor', TYPE: 'V_VAR'},
|
||||
{PLATFORM: 'sensor', TYPE: 'V_VA'},
|
||||
{PLATFORM: 'sensor', TYPE: 'V_POWER_FACTOR'}],
|
||||
'S_DISTANCE': [{PLATFORM: 'sensor', TYPE: 'V_DISTANCE'}],
|
||||
'S_LIGHT_LEVEL': [
|
||||
{PLATFORM: 'sensor', TYPE: 'V_LIGHT_LEVEL'},
|
||||
{PLATFORM: 'sensor', TYPE: 'V_LEVEL'}],
|
||||
'S_IR': [
|
||||
{PLATFORM: 'sensor', TYPE: 'V_IR_RECEIVE'},
|
||||
{PLATFORM: 'switch', TYPE: 'V_IR_SEND',
|
||||
SCHEMA: {'V_IR_SEND': cv.string, 'V_LIGHT': cv.string}}],
|
||||
'S_WATER': [
|
||||
{PLATFORM: 'sensor', TYPE: 'V_FLOW'},
|
||||
{PLATFORM: 'sensor', TYPE: 'V_VOLUME'}],
|
||||
'S_CUSTOM': [
|
||||
{PLATFORM: 'sensor', TYPE: 'V_VAR1'},
|
||||
{PLATFORM: 'sensor', TYPE: 'V_VAR2'},
|
||||
{PLATFORM: 'sensor', TYPE: 'V_VAR3'},
|
||||
{PLATFORM: 'sensor', TYPE: 'V_VAR4'},
|
||||
{PLATFORM: 'sensor', TYPE: 'V_VAR5'},
|
||||
{PLATFORM: 'sensor', TYPE: 'V_CUSTOM'}],
|
||||
'S_SCENE_CONTROLLER': [
|
||||
{PLATFORM: 'sensor', TYPE: 'V_SCENE_ON'},
|
||||
{PLATFORM: 'sensor', TYPE: 'V_SCENE_OFF'}],
|
||||
'S_COLOR_SENSOR': [{PLATFORM: 'sensor', TYPE: 'V_RGB'}],
|
||||
'S_MULTIMETER': [
|
||||
{PLATFORM: 'sensor', TYPE: 'V_VOLTAGE'},
|
||||
{PLATFORM: 'sensor', TYPE: 'V_CURRENT'},
|
||||
{PLATFORM: 'sensor', TYPE: 'V_IMPEDANCE'}],
|
||||
'S_GAS': [
|
||||
{PLATFORM: 'sensor', TYPE: 'V_FLOW'},
|
||||
{PLATFORM: 'sensor', TYPE: 'V_VOLUME'}],
|
||||
'S_WATER_QUALITY': [
|
||||
{PLATFORM: 'sensor', TYPE: 'V_TEMP'},
|
||||
{PLATFORM: 'sensor', TYPE: 'V_PH'},
|
||||
{PLATFORM: 'sensor', TYPE: 'V_ORP'},
|
||||
{PLATFORM: 'sensor', TYPE: 'V_EC'},
|
||||
{PLATFORM: 'switch', TYPE: 'V_STATUS'}],
|
||||
'S_AIR_QUALITY': DUST_SCHEMA,
|
||||
'S_DUST': DUST_SCHEMA,
|
||||
'S_LIGHT': [SWITCH_LIGHT_SCHEMA],
|
||||
'S_BINARY': [SWITCH_STATUS_SCHEMA],
|
||||
'S_LOCK': [{PLATFORM: 'switch', TYPE: 'V_LOCK_STATUS'}],
|
||||
BINARY_SENSOR_TYPES = {
|
||||
'S_DOOR': 'V_TRIPPED',
|
||||
'S_MOTION': 'V_TRIPPED',
|
||||
'S_SMOKE': 'V_TRIPPED',
|
||||
'S_SPRINKLER': 'V_TRIPPED',
|
||||
'S_WATER_LEAK': 'V_TRIPPED',
|
||||
'S_SOUND': 'V_TRIPPED',
|
||||
'S_VIBRATION': 'V_TRIPPED',
|
||||
'S_MOISTURE': 'V_TRIPPED',
|
||||
}
|
||||
|
||||
CLIMATE_TYPES = {
|
||||
'S_HVAC': 'V_HVAC_FLOW_STATE',
|
||||
}
|
||||
|
||||
COVER_TYPES = {
|
||||
'S_COVER': ['V_DIMMER', 'V_PERCENTAGE', 'V_LIGHT', 'V_STATUS'],
|
||||
}
|
||||
|
||||
DEVICE_TRACKER_TYPES = {
|
||||
'S_GPS': 'V_POSITION',
|
||||
}
|
||||
|
||||
LIGHT_TYPES = {
|
||||
'S_DIMMER': ['V_DIMMER', 'V_PERCENTAGE'],
|
||||
'S_RGB_LIGHT': 'V_RGB',
|
||||
'S_RGBW_LIGHT': 'V_RGBW',
|
||||
}
|
||||
|
||||
NOTIFY_TYPES = {
|
||||
'S_INFO': 'V_TEXT',
|
||||
}
|
||||
|
||||
SENSOR_TYPES = {
|
||||
'S_SOUND': 'V_LEVEL',
|
||||
'S_VIBRATION': 'V_LEVEL',
|
||||
'S_MOISTURE': 'V_LEVEL',
|
||||
'S_INFO': 'V_TEXT',
|
||||
'S_GPS': 'V_POSITION',
|
||||
'S_TEMP': 'V_TEMP',
|
||||
'S_HUM': 'V_HUM',
|
||||
'S_BARO': ['V_PRESSURE', 'V_FORECAST'],
|
||||
'S_WIND': ['V_WIND', 'V_GUST', 'V_DIRECTION'],
|
||||
'S_RAIN': ['V_RAIN', 'V_RAINRATE'],
|
||||
'S_UV': 'V_UV',
|
||||
'S_WEIGHT': ['V_WEIGHT', 'V_IMPEDANCE'],
|
||||
'S_POWER': ['V_WATT', 'V_KWH', 'V_VAR', 'V_VA', 'V_POWER_FACTOR'],
|
||||
'S_DISTANCE': 'V_DISTANCE',
|
||||
'S_LIGHT_LEVEL': ['V_LIGHT_LEVEL', 'V_LEVEL'],
|
||||
'S_IR': 'V_IR_RECEIVE',
|
||||
'S_WATER': ['V_FLOW', 'V_VOLUME'],
|
||||
'S_CUSTOM': ['V_VAR1', 'V_VAR2', 'V_VAR3', 'V_VAR4', 'V_VAR5', 'V_CUSTOM'],
|
||||
'S_SCENE_CONTROLLER': ['V_SCENE_ON', 'V_SCENE_OFF'],
|
||||
'S_COLOR_SENSOR': 'V_RGB',
|
||||
'S_MULTIMETER': ['V_VOLTAGE', 'V_CURRENT', 'V_IMPEDANCE'],
|
||||
'S_GAS': ['V_FLOW', 'V_VOLUME'],
|
||||
'S_WATER_QUALITY': ['V_TEMP', 'V_PH', 'V_ORP', 'V_EC'],
|
||||
'S_AIR_QUALITY': ['V_DUST_LEVEL', 'V_LEVEL'],
|
||||
'S_DUST': ['V_DUST_LEVEL', 'V_LEVEL'],
|
||||
}
|
||||
|
||||
SWITCH_TYPES = {
|
||||
'S_LIGHT': 'V_LIGHT',
|
||||
'S_BINARY': 'V_STATUS',
|
||||
'S_DOOR': 'V_ARMED',
|
||||
'S_MOTION': 'V_ARMED',
|
||||
'S_SMOKE': 'V_ARMED',
|
||||
'S_SPRINKLER': 'V_STATUS',
|
||||
'S_WATER_LEAK': 'V_ARMED',
|
||||
'S_SOUND': 'V_ARMED',
|
||||
'S_VIBRATION': 'V_ARMED',
|
||||
'S_MOISTURE': 'V_ARMED',
|
||||
'S_IR': 'V_IR_SEND',
|
||||
'S_LOCK': 'V_LOCK_STATUS',
|
||||
'S_WATER_QUALITY': 'V_STATUS',
|
||||
}
|
||||
|
||||
|
||||
PLATFORM_TYPES = {
|
||||
'binary_sensor': BINARY_SENSOR_TYPES,
|
||||
'climate': CLIMATE_TYPES,
|
||||
'cover': COVER_TYPES,
|
||||
'device_tracker': DEVICE_TRACKER_TYPES,
|
||||
'light': LIGHT_TYPES,
|
||||
'notify': NOTIFY_TYPES,
|
||||
'sensor': SENSOR_TYPES,
|
||||
'switch': SWITCH_TYPES,
|
||||
}
|
||||
|
||||
FLAT_PLATFORM_TYPES = {
|
||||
(platform, s_type_name): v_type_name
|
||||
for platform, platform_types in PLATFORM_TYPES.items()
|
||||
for s_type_name, v_type_name in platform_types.items()
|
||||
}
|
||||
|
||||
TYPE_TO_PLATFORMS = defaultdict(list)
|
||||
for platform, platform_types in PLATFORM_TYPES.items():
|
||||
for s_type_name in platform_types:
|
||||
TYPE_TO_PLATFORMS[s_type_name].append(platform)
|
||||
|
|
|
@ -20,7 +20,7 @@ from .const import (
|
|||
CONF_TOPIC_IN_PREFIX, CONF_TOPIC_OUT_PREFIX, CONF_VERSION, DOMAIN,
|
||||
MYSENSORS_GATEWAY_READY, MYSENSORS_GATEWAYS)
|
||||
from .handler import HANDLERS
|
||||
from .helpers import discover_mysensors_platform, validate_child
|
||||
from .helpers import discover_mysensors_platform, validate_child, validate_node
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
@ -161,6 +161,8 @@ async def _discover_persistent_devices(hass, hass_config, gateway):
|
|||
tasks = []
|
||||
new_devices = defaultdict(list)
|
||||
for node_id in gateway.sensors:
|
||||
if not validate_node(gateway, node_id):
|
||||
continue
|
||||
node = gateway.sensors[node_id]
|
||||
for child in node.children.values():
|
||||
validated = validate_child(gateway, node_id, child)
|
||||
|
|
|
@ -7,26 +7,17 @@ from homeassistant.util import decorator
|
|||
|
||||
from .const import MYSENSORS_GATEWAY_READY, CHILD_CALLBACK, NODE_CALLBACK
|
||||
from .device import get_mysensors_devices
|
||||
from .helpers import discover_mysensors_platform, validate_child
|
||||
from .helpers import discover_mysensors_platform, validate_set_msg
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
HANDLERS = decorator.Registry()
|
||||
|
||||
|
||||
@HANDLERS.register('presentation')
|
||||
async def handle_presentation(hass, hass_config, msg):
|
||||
"""Handle a mysensors presentation message."""
|
||||
# Handle both node and child presentation.
|
||||
from mysensors.const import SYSTEM_CHILD_ID
|
||||
if msg.child_id == SYSTEM_CHILD_ID:
|
||||
return
|
||||
_handle_child_update(hass, hass_config, msg)
|
||||
|
||||
|
||||
@HANDLERS.register('set')
|
||||
async def handle_set(hass, hass_config, msg):
|
||||
"""Handle a mysensors set message."""
|
||||
_handle_child_update(hass, hass_config, msg)
|
||||
validated = validate_set_msg(msg)
|
||||
_handle_child_update(hass, hass_config, validated)
|
||||
|
||||
|
||||
@HANDLERS.register('internal')
|
||||
|
@ -77,14 +68,12 @@ async def handle_gateway_ready(hass, hass_config, msg):
|
|||
|
||||
|
||||
@callback
|
||||
def _handle_child_update(hass, hass_config, msg):
|
||||
def _handle_child_update(hass, hass_config, validated):
|
||||
"""Handle a child update."""
|
||||
child = msg.gateway.sensors[msg.node_id].children[msg.child_id]
|
||||
signals = []
|
||||
|
||||
# Update all platforms for the device via dispatcher.
|
||||
# Add/update entity if schema validates to true.
|
||||
validated = validate_child(msg.gateway, msg.node_id, child)
|
||||
# Add/update entity for validated children.
|
||||
for platform, dev_ids in validated.items():
|
||||
devices = get_mysensors_devices(hass, platform)
|
||||
new_dev_ids = []
|
||||
|
|
|
@ -8,11 +8,12 @@ from homeassistant.const import CONF_NAME
|
|||
from homeassistant.core import callback
|
||||
from homeassistant.helpers import discovery
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.util.decorator import Registry
|
||||
|
||||
from .const import (
|
||||
ATTR_DEVICES, DOMAIN, MYSENSORS_CONST_SCHEMA, PLATFORM, SCHEMA, TYPE)
|
||||
from .const import ATTR_DEVICES, DOMAIN, FLAT_PLATFORM_TYPES, TYPE_TO_PLATFORMS
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
SCHEMAS = Registry()
|
||||
|
||||
|
||||
@callback
|
||||
|
@ -24,58 +25,116 @@ def discover_mysensors_platform(hass, hass_config, platform, new_devices):
|
|||
return task
|
||||
|
||||
|
||||
def validate_child(gateway, node_id, child):
|
||||
"""Validate that a child has the correct values according to schema.
|
||||
def default_schema(gateway, child, value_type_name):
|
||||
"""Return a default validation schema for value types."""
|
||||
schema = {value_type_name: cv.string}
|
||||
return get_child_schema(gateway, child, value_type_name, schema)
|
||||
|
||||
Return a dict of platform with a list of device ids for validated devices.
|
||||
"""
|
||||
validated = defaultdict(list)
|
||||
|
||||
if not child.values:
|
||||
_LOGGER.debug(
|
||||
"No child values for node %s child %s", node_id, child.id)
|
||||
return validated
|
||||
if gateway.sensors[node_id].sketch_name is None:
|
||||
_LOGGER.debug("Node %s is missing sketch name", node_id)
|
||||
return validated
|
||||
@SCHEMAS.register(('light', 'V_DIMMER'))
|
||||
def light_dimmer_schema(gateway, child, value_type_name):
|
||||
"""Return a validation schema for V_DIMMER."""
|
||||
schema = {'V_DIMMER': cv.string, 'V_LIGHT': cv.string}
|
||||
return get_child_schema(gateway, child, value_type_name, schema)
|
||||
|
||||
|
||||
@SCHEMAS.register(('light', 'V_PERCENTAGE'))
|
||||
def light_percentage_schema(gateway, child, value_type_name):
|
||||
"""Return a validation schema for V_PERCENTAGE."""
|
||||
schema = {'V_PERCENTAGE': cv.string, 'V_STATUS': cv.string}
|
||||
return get_child_schema(gateway, child, value_type_name, schema)
|
||||
|
||||
|
||||
@SCHEMAS.register(('light', 'V_RGB'))
|
||||
def light_rgb_schema(gateway, child, value_type_name):
|
||||
"""Return a validation schema for V_RGB."""
|
||||
schema = {'V_RGB': cv.string, 'V_STATUS': cv.string}
|
||||
return get_child_schema(gateway, child, value_type_name, schema)
|
||||
|
||||
|
||||
@SCHEMAS.register(('light', 'V_RGBW'))
|
||||
def light_rgbw_schema(gateway, child, value_type_name):
|
||||
"""Return a validation schema for V_RGBW."""
|
||||
schema = {'V_RGBW': cv.string, 'V_STATUS': cv.string}
|
||||
return get_child_schema(gateway, child, value_type_name, schema)
|
||||
|
||||
|
||||
@SCHEMAS.register(('switch', 'V_IR_SEND'))
|
||||
def switch_ir_send_schema(gateway, child, value_type_name):
|
||||
"""Return a validation schema for V_IR_SEND."""
|
||||
schema = {'V_IR_SEND': cv.string, 'V_LIGHT': cv.string}
|
||||
return get_child_schema(gateway, child, value_type_name, schema)
|
||||
|
||||
|
||||
def get_child_schema(gateway, child, value_type_name, schema):
|
||||
"""Return a child schema."""
|
||||
set_req = gateway.const.SetReq
|
||||
child_schema = child.get_schema(gateway.protocol_version)
|
||||
schema = child_schema.extend(
|
||||
{vol.Required(
|
||||
set_req[name].value, msg=invalid_msg(gateway, child, name)):
|
||||
child_schema.schema.get(set_req[name].value, valid)
|
||||
for name, valid in schema.items()},
|
||||
extra=vol.ALLOW_EXTRA)
|
||||
return schema
|
||||
|
||||
|
||||
def invalid_msg(gateway, child, value_type_name):
|
||||
"""Return a message for an invalid child during schema validation."""
|
||||
pres = gateway.const.Presentation
|
||||
set_req = gateway.const.SetReq
|
||||
s_name = next(
|
||||
return "{} requires value_type {}".format(
|
||||
pres(child.type).name, set_req[value_type_name].name)
|
||||
|
||||
|
||||
def validate_set_msg(msg):
|
||||
"""Validate a set message."""
|
||||
if not validate_node(msg.gateway, msg.node_id):
|
||||
return {}
|
||||
child = msg.gateway.sensors[msg.node_id].children[msg.child_id]
|
||||
return validate_child(msg.gateway, msg.node_id, child, msg.sub_type)
|
||||
|
||||
|
||||
def validate_node(gateway, node_id):
|
||||
"""Validate a node."""
|
||||
if gateway.sensors[node_id].sketch_name is None:
|
||||
_LOGGER.debug("Node %s is missing sketch name", node_id)
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def validate_child(gateway, node_id, child, value_type=None):
|
||||
"""Validate a child."""
|
||||
validated = defaultdict(list)
|
||||
pres = gateway.const.Presentation
|
||||
set_req = gateway.const.SetReq
|
||||
child_type_name = next(
|
||||
(member.name for member in pres if member.value == child.type), None)
|
||||
if s_name not in MYSENSORS_CONST_SCHEMA:
|
||||
_LOGGER.warning("Child type %s is not supported", s_name)
|
||||
value_types = [value_type] if value_type else [*child.values]
|
||||
value_type_names = [
|
||||
member.name for member in set_req if member.value in value_types]
|
||||
platforms = TYPE_TO_PLATFORMS.get(child_type_name, [])
|
||||
if not platforms:
|
||||
_LOGGER.warning("Child type %s is not supported", child.type)
|
||||
return validated
|
||||
child_schemas = MYSENSORS_CONST_SCHEMA[s_name]
|
||||
|
||||
def msg(name):
|
||||
"""Return a message for an invalid schema."""
|
||||
return "{} requires value_type {}".format(
|
||||
pres(child.type).name, set_req[name].name)
|
||||
for platform in platforms:
|
||||
v_names = FLAT_PLATFORM_TYPES[platform, child_type_name]
|
||||
if not isinstance(v_names, list):
|
||||
v_names = [v_names]
|
||||
v_names = [v_name for v_name in v_names if v_name in value_type_names]
|
||||
|
||||
for v_name in v_names:
|
||||
child_schema_gen = SCHEMAS.get((platform, v_name), default_schema)
|
||||
child_schema = child_schema_gen(gateway, child, v_name)
|
||||
try:
|
||||
child_schema(child.values)
|
||||
except vol.Invalid as exc:
|
||||
_LOGGER.warning(
|
||||
"Invalid %s on node %s, %s platform: %s",
|
||||
child, node_id, platform, exc)
|
||||
continue
|
||||
dev_id = id(gateway), node_id, child.id, set_req[v_name].value
|
||||
validated[platform].append(dev_id)
|
||||
|
||||
for schema in child_schemas:
|
||||
platform = schema[PLATFORM]
|
||||
v_name = schema[TYPE]
|
||||
value_type = next(
|
||||
(member.value for member in set_req if member.name == v_name),
|
||||
None)
|
||||
if value_type is None:
|
||||
continue
|
||||
_child_schema = child.get_schema(gateway.protocol_version)
|
||||
vol_schema = _child_schema.extend(
|
||||
{vol.Required(set_req[key].value, msg=msg(key)):
|
||||
_child_schema.schema.get(set_req[key].value, val)
|
||||
for key, val in schema.get(SCHEMA, {v_name: cv.string}).items()},
|
||||
extra=vol.ALLOW_EXTRA)
|
||||
try:
|
||||
vol_schema(child.values)
|
||||
except vol.Invalid as exc:
|
||||
level = (logging.WARNING if value_type in child.values
|
||||
else logging.DEBUG)
|
||||
_LOGGER.log(
|
||||
level,
|
||||
"Invalid values: %s: %s platform: node %s child %s: %s",
|
||||
child.values, platform, node_id, child.id, exc)
|
||||
continue
|
||||
dev_id = id(gateway), node_id, child.id, value_type
|
||||
validated[platform].append(dev_id)
|
||||
return validated
|
||||
|
|
Loading…
Reference in New Issue