forked from platypush/platypush
zwave.mqtt additions
- Infer entity types on the basis of their semantic type (bool, decimal, list) and read-only attribute (read-only bool is `BinarySensor`, read-write bool is `Switch`, read-only decimal is `NumericSensor`, read-write decimal is `Dimmer`, etc.) instead of trying to infer it from the command class. Only a small set of command classes (like configurations, vendor-specific or internal values) will be excluded. This should greatly increase the number of supported values. - Added support for `EnumSwitch` entities. - Added inference for illuminance and humidity sensors.
This commit is contained in:
parent
78c59f437a
commit
0e0c90f0f2
1 changed files with 51 additions and 27 deletions
|
@ -26,8 +26,9 @@ from platypush.entities.electricity import (
|
||||||
VoltageSensor,
|
VoltageSensor,
|
||||||
)
|
)
|
||||||
from platypush.entities.humidity import HumiditySensor
|
from platypush.entities.humidity import HumiditySensor
|
||||||
|
from platypush.entities.illuminance import IlluminanceSensor
|
||||||
from platypush.entities.sensors import BinarySensor, EnumSensor, NumericSensor
|
from platypush.entities.sensors import BinarySensor, EnumSensor, NumericSensor
|
||||||
from platypush.entities.switches import Switch
|
from platypush.entities.switches import EnumSwitch, Switch
|
||||||
from platypush.entities.temperature import TemperatureSensor
|
from platypush.entities.temperature import TemperatureSensor
|
||||||
from platypush.message.event.zwave import ZwaveNodeRenamedEvent, ZwaveNodeEvent
|
from platypush.message.event.zwave import ZwaveNodeRenamedEvent, ZwaveNodeEvent
|
||||||
|
|
||||||
|
@ -73,6 +74,33 @@ class ZwaveMqttPlugin(MqttPlugin, ZwaveBasePlugin):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# These classes are ignored by the entity parsing logic
|
||||||
|
_ignored_entity_classes = {
|
||||||
|
'application_status',
|
||||||
|
'association_command_configuration',
|
||||||
|
'configuration',
|
||||||
|
'controller_replication',
|
||||||
|
'crc16_encap',
|
||||||
|
'firmware_update_md',
|
||||||
|
'grouping_name',
|
||||||
|
'ip_configuration',
|
||||||
|
'manufacturer_proprietary',
|
||||||
|
'manufacturer_specific',
|
||||||
|
'multi_cmd',
|
||||||
|
'multi_instance',
|
||||||
|
'multi_instance_association',
|
||||||
|
'no_operation',
|
||||||
|
'node_naming',
|
||||||
|
'non_interoperable',
|
||||||
|
'proprietary',
|
||||||
|
'scene_actuator_conf',
|
||||||
|
'scene_controller_conf',
|
||||||
|
'sensor_configuration',
|
||||||
|
'version',
|
||||||
|
'wake_up',
|
||||||
|
'zwave_plus_info',
|
||||||
|
}
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
name: str,
|
name: str,
|
||||||
|
@ -488,43 +516,27 @@ class ZwaveMqttPlugin(MqttPlugin, ZwaveBasePlugin):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _matches_classes(value: Mapping, *names: str):
|
def _matches_classes(value: Mapping, *names: str):
|
||||||
classes = {command_class_by_name[name] for name in names}
|
classes = {command_class_by_name[name] for name in names}
|
||||||
|
|
||||||
return value.get('command_class', '') in classes if value else False
|
return value.get('command_class', '') in classes if value else False
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _is_switch(cls, value: Mapping):
|
def _is_switch(cls, value: Mapping):
|
||||||
return cls._matches_classes(
|
return value.get('type') == 'Bool' and not value.get('is_read_only')
|
||||||
value, 'switch_binary', 'switch_toggle_binary', 'switch_all'
|
|
||||||
) and not value.get('is_read_only')
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _is_dimmer(cls, value: Mapping):
|
def _is_dimmer(cls, value: Mapping):
|
||||||
return cls._matches_classes(
|
return value.get('type') == 'Decimal' and not value.get('is_read_only')
|
||||||
value, 'switch_multilevel', 'switch_toggle_multilevel'
|
|
||||||
) and not value.get('is_read_only')
|
@classmethod
|
||||||
|
def _is_enum_switch(cls, value: Mapping):
|
||||||
|
return value.get('type') == 'List' and not value.get('is_read_only')
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _get_sensor_args(
|
def _get_sensor_args(
|
||||||
cls, value: Mapping
|
cls, value: Mapping
|
||||||
) -> Tuple[Optional[Type], Optional[Mapping]]:
|
) -> Tuple[Optional[Type], Optional[Mapping]]:
|
||||||
sensor_type, args = None, None
|
sensor_type, args = None, None
|
||||||
known_sensor_classes = {
|
|
||||||
'battery',
|
|
||||||
'door_lock',
|
|
||||||
'lock',
|
|
||||||
'meter',
|
|
||||||
'notification',
|
|
||||||
'sensor_alarm',
|
|
||||||
'sensor_binary',
|
|
||||||
'sensor_multilevel',
|
|
||||||
'thermostat_fan_mode',
|
|
||||||
'thermostat_fan_state',
|
|
||||||
'thermostat_heating',
|
|
||||||
}
|
|
||||||
|
|
||||||
if value.get('is_read_only') and cls._matches_classes(
|
if value.get('is_read_only'):
|
||||||
value, *known_sensor_classes
|
|
||||||
):
|
|
||||||
if value.get('type') == 'Bool':
|
if value.get('type') == 'Bool':
|
||||||
sensor_type, args = (
|
sensor_type, args = (
|
||||||
BinarySensor,
|
BinarySensor,
|
||||||
|
@ -554,8 +566,14 @@ class ZwaveMqttPlugin(MqttPlugin, ZwaveBasePlugin):
|
||||||
sensor_type = EnergySensor
|
sensor_type = EnergySensor
|
||||||
elif re.search(r'\s*temperature$', value['property_id'], re.IGNORECASE):
|
elif re.search(r'\s*temperature$', value['property_id'], re.IGNORECASE):
|
||||||
sensor_type = TemperatureSensor
|
sensor_type = TemperatureSensor
|
||||||
elif re.search(r'\s*humidity$', value['property_id'], re.IGNORECASE):
|
elif re.search(
|
||||||
|
r'\s*(humidity|moisture)$', value['property_id'], re.IGNORECASE
|
||||||
|
):
|
||||||
sensor_type = HumiditySensor
|
sensor_type = HumiditySensor
|
||||||
|
elif re.search(
|
||||||
|
r'\s*(illuminance|luminosity)$', value['property_id'], re.IGNORECASE
|
||||||
|
):
|
||||||
|
sensor_type = IlluminanceSensor
|
||||||
|
|
||||||
args = {
|
args = {
|
||||||
'value': value.get('data'),
|
'value': value.get('data'),
|
||||||
|
@ -571,7 +589,7 @@ class ZwaveMqttPlugin(MqttPlugin, ZwaveBasePlugin):
|
||||||
return (
|
return (
|
||||||
cls._matches_classes(value, 'battery')
|
cls._matches_classes(value, 'battery')
|
||||||
and value.get('is_read_only')
|
and value.get('is_read_only')
|
||||||
and not value['id'].endswith('-isLow')
|
and value.get('type') == 'Decimal'
|
||||||
)
|
)
|
||||||
|
|
||||||
def _to_entity_args(self, value: Mapping) -> dict:
|
def _to_entity_args(self, value: Mapping) -> dict:
|
||||||
|
@ -611,7 +629,7 @@ class ZwaveMqttPlugin(MqttPlugin, ZwaveBasePlugin):
|
||||||
entities = []
|
entities = []
|
||||||
|
|
||||||
for value in values:
|
for value in values:
|
||||||
if not value:
|
if not value or self._matches_classes(value, *self._ignored_entity_classes):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
entity_type = None
|
entity_type = None
|
||||||
|
@ -623,6 +641,12 @@ class ZwaveMqttPlugin(MqttPlugin, ZwaveBasePlugin):
|
||||||
entity_args['value'] = value['data']
|
entity_args['value'] = value['data']
|
||||||
entity_args['min'] = value['min']
|
entity_args['min'] = value['min']
|
||||||
entity_args['max'] = value['max']
|
entity_args['max'] = value['max']
|
||||||
|
elif self._is_enum_switch(value):
|
||||||
|
entity_type = EnumSwitch
|
||||||
|
entity_args['value'] = value['data']
|
||||||
|
entity_args['values'] = {
|
||||||
|
i['value']: i['text'] for i in value.get('data_items', [])
|
||||||
|
}
|
||||||
elif self._is_battery(value):
|
elif self._is_battery(value):
|
||||||
entity_type = Battery
|
entity_type = Battery
|
||||||
entity_args['value'] = value['data']
|
entity_args['value'] = value['data']
|
||||||
|
|
Loading…
Reference in a new issue