From 1351ddd11e312c42ce504569e0331ffd26b36cc8 Mon Sep 17 00:00:00 2001 From: Marcel van der Veldt Date: Tue, 12 May 2020 20:07:11 +0200 Subject: [PATCH] Add binary sensor platform to zwave_mqtt (#35519) * add binary sensor platform to zwave_mqtt * add tests for binary_sensor * fix tests * device class is required value * Update homeassistant/components/zwave_mqtt/binary_sensor.py use colon as separator Co-authored-by: Martin Hjelmare * extend tests * code optimize * add test for enabling a legacy binary sensor Co-authored-by: Martin Hjelmare --- .../components/zwave_mqtt/binary_sensor.py | 405 ++++++++++++++++++ homeassistant/components/zwave_mqtt/const.py | 3 +- .../components/zwave_mqtt/discovery.py | 30 +- tests/components/zwave_mqtt/conftest.py | 24 ++ .../zwave_mqtt/test_binary_sensor.py | 66 +++ tests/fixtures/zwave_mqtt/binary_sensor.json | 38 ++ .../zwave_mqtt/binary_sensor_alt.json | 25 ++ .../zwave_mqtt/generic_network_dump.csv | 4 +- 8 files changed, 589 insertions(+), 6 deletions(-) create mode 100644 homeassistant/components/zwave_mqtt/binary_sensor.py create mode 100644 tests/components/zwave_mqtt/test_binary_sensor.py create mode 100644 tests/fixtures/zwave_mqtt/binary_sensor.json create mode 100644 tests/fixtures/zwave_mqtt/binary_sensor_alt.json diff --git a/homeassistant/components/zwave_mqtt/binary_sensor.py b/homeassistant/components/zwave_mqtt/binary_sensor.py new file mode 100644 index 00000000000..64fd66394f8 --- /dev/null +++ b/homeassistant/components/zwave_mqtt/binary_sensor.py @@ -0,0 +1,405 @@ +"""Representation of Z-Wave binary_sensors.""" + +import logging + +from openzwavemqtt.const import CommandClass, ValueIndex, ValueType + +from homeassistant.components.binary_sensor import ( + DEVICE_CLASS_DOOR, + DEVICE_CLASS_GAS, + DEVICE_CLASS_HEAT, + DEVICE_CLASS_LOCK, + DEVICE_CLASS_MOISTURE, + DEVICE_CLASS_MOTION, + DEVICE_CLASS_POWER, + DEVICE_CLASS_PROBLEM, + DEVICE_CLASS_SAFETY, + DEVICE_CLASS_SMOKE, + DEVICE_CLASS_SOUND, + DOMAIN as BINARY_SENSOR_DOMAIN, + BinarySensorEntity, +) +from homeassistant.core import callback +from homeassistant.helpers.dispatcher import async_dispatcher_connect + +from .const import DATA_UNSUBSCRIBE, DOMAIN +from .entity import ZWaveDeviceEntity + +_LOGGER = logging.getLogger(__name__) + +NOTIFICATION_TYPE = "index" +NOTIFICATION_VALUES = "values" +NOTIFICATION_DEVICE_CLASS = "device_class" +NOTIFICATION_SENSOR_ENABLED = "enabled" +NOTIFICATION_OFF_VALUE = "off_value" + +NOTIFICATION_VALUE_CLEAR = 0 + +# Translation from values in Notification CC to binary sensors +# https://github.com/OpenZWave/open-zwave/blob/master/config/NotificationCCTypes.xml +NOTIFICATION_SENSORS = [ + { + # Index 1: Smoke Alarm - Value Id's 1 and 2 + # Assuming here that Value 1 and 2 are not present at the same time + NOTIFICATION_TYPE: ValueIndex.NOTIFICATION_SMOKE_ALARM, + NOTIFICATION_VALUES: [1, 2], + NOTIFICATION_DEVICE_CLASS: DEVICE_CLASS_SMOKE, + }, + { + # Index 1: Smoke Alarm - All other Value Id's + # Create as disabled sensors + NOTIFICATION_TYPE: ValueIndex.NOTIFICATION_SMOKE_ALARM, + NOTIFICATION_VALUES: [3, 4, 5, 6, 7, 8], + NOTIFICATION_DEVICE_CLASS: DEVICE_CLASS_SMOKE, + NOTIFICATION_SENSOR_ENABLED: False, + }, + { + # Index 2: Carbon Monoxide - Value Id's 1 and 2 + NOTIFICATION_TYPE: ValueIndex.NOTIFICATION_CARBON_MONOOXIDE, + NOTIFICATION_VALUES: [1, 2], + NOTIFICATION_DEVICE_CLASS: DEVICE_CLASS_GAS, + }, + { + # Index 2: Carbon Monoxide - All other Value Id's + NOTIFICATION_TYPE: ValueIndex.NOTIFICATION_CARBON_MONOOXIDE, + NOTIFICATION_VALUES: [4, 5, 7], + NOTIFICATION_DEVICE_CLASS: DEVICE_CLASS_GAS, + NOTIFICATION_SENSOR_ENABLED: False, + }, + { + # Index 3: Carbon Dioxide - Value Id's 1 and 2 + NOTIFICATION_TYPE: ValueIndex.NOTIFICATION_CARBON_DIOXIDE, + NOTIFICATION_VALUES: [1, 2], + NOTIFICATION_DEVICE_CLASS: DEVICE_CLASS_GAS, + }, + { + # Index 3: Carbon Dioxide - All other Value Id's + NOTIFICATION_TYPE: ValueIndex.NOTIFICATION_CARBON_DIOXIDE, + NOTIFICATION_VALUES: [4, 5, 7], + NOTIFICATION_DEVICE_CLASS: DEVICE_CLASS_GAS, + NOTIFICATION_SENSOR_ENABLED: False, + }, + { + # Index 4: Heat - Value Id's 1, 2, 5, 6 (heat/underheat) + NOTIFICATION_TYPE: ValueIndex.NOTIFICATION_HEAT, + NOTIFICATION_VALUES: [1, 2, 5, 6], + NOTIFICATION_DEVICE_CLASS: DEVICE_CLASS_HEAT, + }, + { + # Index 4: Heat - All other Value Id's + NOTIFICATION_TYPE: ValueIndex.NOTIFICATION_HEAT, + NOTIFICATION_VALUES: [3, 4, 8, 10, 11], + NOTIFICATION_DEVICE_CLASS: DEVICE_CLASS_HEAT, + NOTIFICATION_SENSOR_ENABLED: False, + }, + { + # Index 5: Water - Value Id's 1, 2, 3, 4 + NOTIFICATION_TYPE: ValueIndex.NOTIFICATION_WATER, + NOTIFICATION_VALUES: [1, 2, 3, 4], + NOTIFICATION_DEVICE_CLASS: DEVICE_CLASS_MOISTURE, + }, + { + # Index 5: Water - All other Value Id's + NOTIFICATION_TYPE: ValueIndex.NOTIFICATION_WATER, + NOTIFICATION_VALUES: [5], + NOTIFICATION_DEVICE_CLASS: DEVICE_CLASS_MOISTURE, + NOTIFICATION_SENSOR_ENABLED: False, + }, + { + # Index 6: Access Control - Value Id's 1, 2, 3, 4 (Lock) + NOTIFICATION_TYPE: ValueIndex.NOTIFICATION_ACCESS_CONTROL, + NOTIFICATION_VALUES: [1, 2, 3, 4], + NOTIFICATION_DEVICE_CLASS: DEVICE_CLASS_LOCK, + }, + { + # Index 6: Access Control - Value Id 22 (door/window open) + NOTIFICATION_TYPE: ValueIndex.NOTIFICATION_ACCESS_CONTROL, + NOTIFICATION_VALUES: [22], + NOTIFICATION_DEVICE_CLASS: DEVICE_CLASS_DOOR, + NOTIFICATION_OFF_VALUE: 23, + }, + { + # Index 7: Home Security - Value Id's 1, 2 (intrusion) + # Assuming that value 1 and 2 are not present at the same time + NOTIFICATION_TYPE: ValueIndex.NOTIFICATION_HOME_SECURITY, + NOTIFICATION_VALUES: [1, 2], + NOTIFICATION_DEVICE_CLASS: DEVICE_CLASS_SAFETY, + }, + { + # Index 7: Home Security - Value Id's 3, 4, 9 (tampering) + NOTIFICATION_TYPE: ValueIndex.NOTIFICATION_HOME_SECURITY, + NOTIFICATION_VALUES: [3, 4, 9], + NOTIFICATION_DEVICE_CLASS: DEVICE_CLASS_SAFETY, + }, + { + # Index 7: Home Security - Value Id's 5, 6 (glass breakage) + # Assuming that value 5 and 6 are not present at the same time + NOTIFICATION_TYPE: ValueIndex.NOTIFICATION_HOME_SECURITY, + NOTIFICATION_VALUES: [5, 6], + NOTIFICATION_DEVICE_CLASS: DEVICE_CLASS_SAFETY, + }, + { + # Index 7: Home Security - Value Id's 7, 8 (motion) + NOTIFICATION_TYPE: ValueIndex.NOTIFICATION_HOME_SECURITY, + NOTIFICATION_VALUES: [7, 8], + NOTIFICATION_DEVICE_CLASS: DEVICE_CLASS_MOTION, + }, + { + # Index 8: Power management - Values 1...9 + NOTIFICATION_TYPE: ValueIndex.NOTIFICATION_POWER_MANAGEMENT, + NOTIFICATION_VALUES: [1, 2, 3, 4, 5, 6, 7, 8, 9], + NOTIFICATION_DEVICE_CLASS: DEVICE_CLASS_POWER, + NOTIFICATION_SENSOR_ENABLED: False, + }, + { + # Index 8: Power management - Values 10...15 + # Battery values (mutually exclusive) + NOTIFICATION_TYPE: ValueIndex.NOTIFICATION_POWER_MANAGEMENT, + NOTIFICATION_VALUES: [10, 11, 12, 13, 14, 15], + NOTIFICATION_DEVICE_CLASS: DEVICE_CLASS_POWER, + NOTIFICATION_SENSOR_ENABLED: False, + NOTIFICATION_OFF_VALUE: None, + }, + { + # Index 9: System - Value Id's 1, 2, 6, 7 + NOTIFICATION_TYPE: ValueIndex.NOTIFICATION_SYSTEM, + NOTIFICATION_VALUES: [1, 2, 6, 7], + NOTIFICATION_DEVICE_CLASS: DEVICE_CLASS_PROBLEM, + NOTIFICATION_SENSOR_ENABLED: False, + }, + { + # Index 10: Emergency - Value Id's 1, 2, 3 + NOTIFICATION_TYPE: ValueIndex.NOTIFICATION_EMERGENCY, + NOTIFICATION_VALUES: [1, 2, 3], + NOTIFICATION_DEVICE_CLASS: DEVICE_CLASS_PROBLEM, + }, + { + # Index 11: Clock - Value Id's 1, 2 + NOTIFICATION_TYPE: ValueIndex.NOTIFICATION_CLOCK, + NOTIFICATION_VALUES: [1, 2], + NOTIFICATION_DEVICE_CLASS: None, + NOTIFICATION_SENSOR_ENABLED: False, + }, + { + # Index 12: Appliance - All Value Id's + NOTIFICATION_TYPE: ValueIndex.NOTIFICATION_APPLIANCE, + NOTIFICATION_VALUES: [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + ], + NOTIFICATION_DEVICE_CLASS: None, + }, + { + # Index 13: Home Health - Value Id's 1,2,3,4,5 + NOTIFICATION_TYPE: ValueIndex.NOTIFICATION_APPLIANCE, + NOTIFICATION_VALUES: [1, 2, 3, 4, 5], + NOTIFICATION_DEVICE_CLASS: None, + }, + { + # Index 14: Siren + NOTIFICATION_TYPE: ValueIndex.NOTIFICATION_SIREN, + NOTIFICATION_VALUES: [1], + NOTIFICATION_DEVICE_CLASS: DEVICE_CLASS_SOUND, + }, + { + # Index 15: Water valve + # ignore non-boolean values + NOTIFICATION_TYPE: ValueIndex.NOTIFICATION_WATER_VALVE, + NOTIFICATION_VALUES: [3, 4], + NOTIFICATION_DEVICE_CLASS: DEVICE_CLASS_PROBLEM, + }, + { + # Index 16: Weather + NOTIFICATION_TYPE: ValueIndex.NOTIFICATION_WEATHER, + NOTIFICATION_VALUES: [1, 2], + NOTIFICATION_DEVICE_CLASS: DEVICE_CLASS_PROBLEM, + }, + { + # Index 17: Irrigation + # ignore non-boolean values + NOTIFICATION_TYPE: ValueIndex.NOTIFICATION_IRRIGATION, + NOTIFICATION_VALUES: [1, 2, 3, 4, 5], + NOTIFICATION_DEVICE_CLASS: None, + }, + { + # Index 18: Gas + NOTIFICATION_TYPE: ValueIndex.NOTIFICATION_GAS, + NOTIFICATION_VALUES: [1, 2, 3, 4], + NOTIFICATION_DEVICE_CLASS: DEVICE_CLASS_GAS, + }, + { + # Index 18: Gas + NOTIFICATION_TYPE: ValueIndex.NOTIFICATION_GAS, + NOTIFICATION_VALUES: [6], + NOTIFICATION_DEVICE_CLASS: DEVICE_CLASS_PROBLEM, + }, +] + + +async def async_setup_entry(hass, config_entry, async_add_entities): + """Set up Z-Wave binary_sensor from config entry.""" + + @callback + def async_add_binary_sensor(values): + """Add Z-Wave Binary Sensor(s).""" + async_add_entities(VALUE_TYPE_SENSORS[values.primary.type](values)) + + hass.data[DOMAIN][config_entry.entry_id][DATA_UNSUBSCRIBE].append( + async_dispatcher_connect( + hass, f"{DOMAIN}_new_{BINARY_SENSOR_DOMAIN}", async_add_binary_sensor + ) + ) + + +@callback +def async_get_legacy_binary_sensors(values): + """Add Legacy/classic Z-Wave Binary Sensor.""" + return [ZWaveBinarySensor(values)] + + +@callback +def async_get_notification_sensors(values): + """Convert Notification values into binary sensors.""" + sensors_to_add = [] + for list_value in values.primary.value["List"]: + # check if we have a mapping for this value + for item in NOTIFICATION_SENSORS: + if item[NOTIFICATION_TYPE] != values.primary.index: + continue + if list_value["Value"] not in item[NOTIFICATION_VALUES]: + continue + sensors_to_add.append( + ZWaveListValueSensor( + # required values + values, + list_value["Value"], + item[NOTIFICATION_DEVICE_CLASS], + # optional values + item.get(NOTIFICATION_SENSOR_ENABLED, True), + item.get(NOTIFICATION_OFF_VALUE, NOTIFICATION_VALUE_CLEAR), + ) + ) + return sensors_to_add + + +VALUE_TYPE_SENSORS = { + ValueType.BOOL: async_get_legacy_binary_sensors, + ValueType.LIST: async_get_notification_sensors, +} + + +class ZWaveBinarySensor(ZWaveDeviceEntity, BinarySensorEntity): + """Representation of a Z-Wave binary_sensor.""" + + @property + def is_on(self): + """Return if the sensor is on or off.""" + return self.values.primary.value + + @property + def entity_registry_enabled_default(self) -> bool: + """Return if the entity should be enabled when first added to the entity registry.""" + # Legacy binary sensors are phased out (replaced by notification sensors) + # Disable by default to not confuse users + for item in self.values.primary.node.values(): + if item.command_class == CommandClass.NOTIFICATION: + # This device properly implements the Notification CC, legacy sensor can be disabled + return False + return True + + +class ZWaveListValueSensor(ZWaveDeviceEntity, BinarySensorEntity): + """Representation of a binary_sensor from values in the Z-Wave Notification CommandClass.""" + + def __init__( + self, + values, + on_value, + device_class=None, + default_enabled=True, + off_value=NOTIFICATION_VALUE_CLEAR, + ): + """Initialize a ZWaveListValueSensor entity.""" + super().__init__(values) + self._on_value = on_value + self._device_class = device_class + self._default_enabled = default_enabled + self._off_value = off_value + # make sure the correct value is selected at startup + self._state = False + self.on_value_update() + + @callback + def on_value_update(self): + """Call when a value is added/updated in the underlying EntityValues Collection.""" + if self.values.primary.value["Selected_id"] == self._on_value: + # Only when the active ID exactly matches our watched ON value, set sensor state to ON + self._state = True + elif self.values.primary.value["Selected_id"] == self._off_value: + # Only when the active ID exactly matches our watched OFF value, set sensor state to OFF + self._state = False + elif ( + self._off_value is None + and self.values.primary.value["Selected_id"] != self._on_value + ): + # Off value not explicitly specified + # Some values are reset by the simple fact they're overruled by another value coming in + # For example the battery charging values in Power Management Index + self._state = False + + @property + def name(self): + """Return the name of the entity.""" + # Append value label to base name + base_name = super().name + value_label = "" + for item in self.values.primary.value["List"]: + if item["Value"] == self._on_value: + value_label = item["Label"] + break + # Strip "on location" / "at location" from name + # Note: We're assuming that we don't retrieve 2 values with different location + value_label = value_label.split(" on ")[0] + value_label = value_label.split(" at ")[0] + return f"{base_name}: {value_label}" + + @property + def unique_id(self): + """Return the unique_id of the entity.""" + unique_id = super().unique_id + return f"{unique_id}.{self._on_value}" + + @property + def is_on(self): + """Return if the sensor is on or off.""" + return self._state + + @property + def device_class(self): + """Return the class of this device, from component DEVICE_CLASSES.""" + return self._device_class + + @property + def entity_registry_enabled_default(self) -> bool: + """Return if the entity should be enabled when first added to the entity registry.""" + # We hide the more advanced sensors by default to not overwhelm users + return self._default_enabled diff --git a/homeassistant/components/zwave_mqtt/const.py b/homeassistant/components/zwave_mqtt/const.py index f52239c28df..5b1f6dcbac7 100644 --- a/homeassistant/components/zwave_mqtt/const.py +++ b/homeassistant/components/zwave_mqtt/const.py @@ -1,11 +1,12 @@ """Constants for the zwave_mqtt integration.""" +from homeassistant.components.binary_sensor import DOMAIN as BINARY_SENSOR_DOMAIN from homeassistant.components.light import DOMAIN as LIGHT_DOMAIN from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN from homeassistant.components.switch import DOMAIN as SWITCH_DOMAIN DOMAIN = "zwave_mqtt" DATA_UNSUBSCRIBE = "unsubscribe" -PLATFORMS = [LIGHT_DOMAIN, SENSOR_DOMAIN, SWITCH_DOMAIN] +PLATFORMS = [BINARY_SENSOR_DOMAIN, LIGHT_DOMAIN, SENSOR_DOMAIN, SWITCH_DOMAIN] # MQTT Topics TOPIC_OPENZWAVE = "OpenZWave" diff --git a/homeassistant/components/zwave_mqtt/discovery.py b/homeassistant/components/zwave_mqtt/discovery.py index 86c227b6973..38940c9ed6e 100644 --- a/homeassistant/components/zwave_mqtt/discovery.py +++ b/homeassistant/components/zwave_mqtt/discovery.py @@ -5,6 +5,31 @@ from openzwavemqtt.const import CommandClass, ValueGenre, ValueIndex, ValueType from . import const DISCOVERY_SCHEMAS = ( + { # Binary sensors + const.DISC_COMPONENT: "binary_sensor", + const.DISC_VALUES: { + const.DISC_PRIMARY: { + const.DISC_COMMAND_CLASS: CommandClass.SENSOR_BINARY, + const.DISC_TYPE: ValueType.BOOL, + const.DISC_GENRE: ValueGenre.USER, + }, + "off_delay": { + const.DISC_COMMAND_CLASS: CommandClass.CONFIGURATION, + const.DISC_INDEX: 9, + const.DISC_OPTIONAL: True, + }, + }, + }, + { # Notification CommandClass translates to binary_sensor + const.DISC_COMPONENT: "binary_sensor", + const.DISC_VALUES: { + const.DISC_PRIMARY: { + const.DISC_COMMAND_CLASS: CommandClass.NOTIFICATION, + const.DISC_GENRE: ValueGenre.USER, + const.DISC_TYPE: (ValueType.BOOL, ValueType.LIST), + } + }, + }, { # Light const.DISC_COMPONENT: "light", const.DISC_GENERIC_DEVICE_CLASS: ( @@ -93,9 +118,8 @@ def check_node_schema(node, schema): def check_value_schema(value, schema): """Check if the value matches the passed value schema.""" - if ( - const.DISC_COMMAND_CLASS in schema - and value.parent.command_class_id not in schema[const.DISC_COMMAND_CLASS] + if const.DISC_COMMAND_CLASS in schema and not eq_or_in( + value.parent.command_class_id, schema[const.DISC_COMMAND_CLASS] ): return False if const.DISC_TYPE in schema and not eq_or_in(value.type, schema[const.DISC_TYPE]): diff --git a/tests/components/zwave_mqtt/conftest.py b/tests/components/zwave_mqtt/conftest.py index 1d3841350ea..2297cee3e4e 100644 --- a/tests/components/zwave_mqtt/conftest.py +++ b/tests/components/zwave_mqtt/conftest.py @@ -66,3 +66,27 @@ async def sensor_msg_fixture(hass): message = MQTTMessage(topic=sensor_json["topic"], payload=sensor_json["payload"]) message.encode() return message + + +@pytest.fixture(name="binary_sensor_msg") +async def binary_sensor_msg_fixture(hass): + """Return a mock MQTT msg with a binary_sensor change message.""" + sensor_json = json.loads( + await hass.async_add_executor_job(load_fixture, "zwave_mqtt/binary_sensor.json") + ) + message = MQTTMessage(topic=sensor_json["topic"], payload=sensor_json["payload"]) + message.encode() + return message + + +@pytest.fixture(name="binary_sensor_alt_msg") +async def binary_sensor_alt_msg_fixture(hass): + """Return a mock MQTT msg with a binary_sensor change message.""" + sensor_json = json.loads( + await hass.async_add_executor_job( + load_fixture, "zwave_mqtt/binary_sensor_alt.json" + ) + ) + message = MQTTMessage(topic=sensor_json["topic"], payload=sensor_json["payload"]) + message.encode() + return message diff --git a/tests/components/zwave_mqtt/test_binary_sensor.py b/tests/components/zwave_mqtt/test_binary_sensor.py new file mode 100644 index 00000000000..9e4fddbef75 --- /dev/null +++ b/tests/components/zwave_mqtt/test_binary_sensor.py @@ -0,0 +1,66 @@ +"""Test Z-Wave Sensors.""" +from homeassistant.components.binary_sensor import ( + DEVICE_CLASS_MOTION, + DOMAIN as BINARY_SENSOR_DOMAIN, +) +from homeassistant.components.zwave_mqtt.const import DOMAIN +from homeassistant.const import ATTR_DEVICE_CLASS + +from .common import setup_zwave + + +async def test_binary_sensor(hass, generic_data, binary_sensor_msg): + """Test setting up config entry.""" + receive_msg = await setup_zwave(hass, fixture=generic_data) + + # Test Legacy sensor (disabled by default) + registry = await hass.helpers.entity_registry.async_get_registry() + entity_id = "binary_sensor.trisensor_sensor" + state = hass.states.get(entity_id) + assert state is None + entry = registry.async_get(entity_id) + assert entry + assert entry.disabled + assert entry.disabled_by == "integration" + + # Test enabling legacy entity + updated_entry = registry.async_update_entity( + entry.entity_id, **{"disabled_by": None} + ) + assert updated_entry != entry + assert updated_entry.disabled is False + + # Test Sensor for Notification CC + state = hass.states.get("binary_sensor.trisensor_home_security_motion_detected") + assert state + assert state.state == "off" + assert state.attributes[ATTR_DEVICE_CLASS] == DEVICE_CLASS_MOTION + + # Test incoming state change + receive_msg(binary_sensor_msg) + await hass.async_block_till_done() + state = hass.states.get("binary_sensor.trisensor_home_security_motion_detected") + assert state.state == "on" + + +async def test_sensor_enabled(hass, generic_data, binary_sensor_alt_msg): + """Test enabling a legacy binary_sensor.""" + + registry = await hass.helpers.entity_registry.async_get_registry() + + entry = registry.async_get_or_create( + BINARY_SENSOR_DOMAIN, + DOMAIN, + "1-37-625737744", + suggested_object_id="trisensor_sensor_instance_1_sensor", + disabled_by=None, + ) + assert entry.disabled is False + + receive_msg = await setup_zwave(hass, fixture=generic_data) + receive_msg(binary_sensor_alt_msg) + await hass.async_block_till_done() + + state = hass.states.get(entry.entity_id) + assert state is not None + assert state.state == "on" diff --git a/tests/fixtures/zwave_mqtt/binary_sensor.json b/tests/fixtures/zwave_mqtt/binary_sensor.json new file mode 100644 index 00000000000..4d6317827d1 --- /dev/null +++ b/tests/fixtures/zwave_mqtt/binary_sensor.json @@ -0,0 +1,38 @@ +{ + "topic": "OpenZWave/1/node/37/instance/1/commandclass/113/value/1970325463777300/", + "payload": { + "Label": "Home Security", + "Value": { + "List": [ + { + "Value": 0, + "Label": "Clear" + }, + { + "Value": 8, + "Label": "Motion Detected at Unknown Location" + } + ], + "Selected": "Motion Detected at Unknown Location", + "Selected_id": 8 + }, + "Units": "", + "Min": 0, + "Max": 0, + "Type": "List", + "Instance": 1, + "CommandClass": "COMMAND_CLASS_NOTIFICATION", + "Index": 7, + "Node": 37, + "Genre": "User", + "Help": "Home Security Alerts", + "ValueIDKey": 1970325463777300, + "ReadOnly": false, + "WriteOnly": false, + "ValueSet": false, + "ValuePolled": false, + "ChangeVerified": false, + "Event": "valueAdded", + "TimeStamp": 1579566891 + } +} \ No newline at end of file diff --git a/tests/fixtures/zwave_mqtt/binary_sensor_alt.json b/tests/fixtures/zwave_mqtt/binary_sensor_alt.json new file mode 100644 index 00000000000..187028843ff --- /dev/null +++ b/tests/fixtures/zwave_mqtt/binary_sensor_alt.json @@ -0,0 +1,25 @@ +{ + "topic": "OpenZWave/1/node/37/instance/1/commandclass/48/value/625737744/", + "payload": { + "Label": "Sensor", + "Value": true, + "Units": "", + "Min": 0, + "Max": 0, + "Type": "Bool", + "Instance": 1, + "CommandClass": "COMMAND_CLASS_SENSOR_BINARY", + "Index": 0, + "Node": 37, + "Genre": "User", + "Help": "Binary Sensor State", + "ValueIDKey": 625737744, + "ReadOnly": false, + "WriteOnly": false, + "ValueSet": false, + "ValuePolled": false, + "ChangeVerified": false, + "Event": "valueAdded", + "TimeStamp": 1579566891 + } +} \ No newline at end of file diff --git a/tests/fixtures/zwave_mqtt/generic_network_dump.csv b/tests/fixtures/zwave_mqtt/generic_network_dump.csv index debb329a8f7..9214796759a 100644 --- a/tests/fixtures/zwave_mqtt/generic_network_dump.csv +++ b/tests/fixtures/zwave_mqtt/generic_network_dump.csv @@ -105,7 +105,7 @@ OpenZWave/1/node/36/instance/1/commandclass/112/value/56576470933045270/,{ "L OpenZWave/1/node/36/instance/1/commandclass/112/value/70931694745288724/,{ "Label": "Lock/Unlock Configuration", "Value": { "List": [ { "Value": 0, "Label": "Unlock" }, { "Value": 1, "Label": "Lock" } ], "Selected": "Unlock" }, "Units": "", "Min": 0, "Max": 1, "Type": "List", "Instance": 1, "CommandClass": "COMMAND_CLASS_CONFIGURATION", "Index": 252, "Node": 36, "Genre": "Config", "Help": "Lock/ unlock all configuration parameters", "ValueIDKey": 70931694745288724, "ReadOnly": false, "WriteOnly": false, "ValueSet": false, "ValuePolled": false, "ChangeVerified": false, "Event": "valueAdded", "TimeStamp": 1579566891} OpenZWave/1/node/36/instance/1/commandclass/112/value/71776119675420692/,{ "Label": "Reset To Factory Defaults", "Value": { "List": [ { "Value": 0, "Label": "Normal" }, { "Value": 1, "Label": "Reset to factory default setting" }, { "Value": 1431655765, "Label": "Reset to factory default setting and removed from the z-wave network" } ], "Selected": "Reset to factory default setting" }, "Units": "", "Min": 0, "Max": 0, "Type": "List", "Instance": 1, "CommandClass": "COMMAND_CLASS_CONFIGURATION", "Index": 255, "Node": 36, "Genre": "Config", "Help": "Reset to factory defaults", "ValueIDKey": 71776119675420692, "ReadOnly": false, "WriteOnly": false, "ValueSet": false, "ValuePolled": false, "ChangeVerified": false, "Event": "valueAdded", "TimeStamp": 1579566891} OpenZWave/1/node/36/instance/1/commandclass/113/,{ "Instance": 1, "CommandClassId": 113, "CommandClass": "COMMAND_CLASS_NOTIFICATION", "TimeStamp": 1579566891} -OpenZWave/1/node/36/instance/1/commandclass/113/value/1407375493578772/,{ "Label": "Instance 1: Water", "Value": { "List": [ { "Value": 0, "Label": "Clear" }, { "Value": 2, "Label": "Water Leak at Unknown Location" } ], "Selected": "Clear" }, "Units": "", "Min": 0, "Max": 0, "Type": "List", "Instance": 1, "CommandClass": "COMMAND_CLASS_NOTIFICATION", "Index": 5, "Node": 36, "Genre": "User", "Help": "Water Alerts", "ValueIDKey": 1407375493578772, "ReadOnly": false, "WriteOnly": false, "ValueSet": false, "ValuePolled": false, "ChangeVerified": false, "Event": "valueAdded", "TimeStamp": 1579566891} +OpenZWave/1/node/36/instance/1/commandclass/113/value/1407375493578772/,{ "Label": "Instance 1: Water", "Value": { "List": [ { "Value": 0, "Label": "Clear" }, { "Value": 2, "Label": "Water Leak at Unknown Location" } ], "Selected": "Clear", "Selected_id": 0 }, "Units": "", "Min": 0, "Max": 0, "Type": "List", "Instance": 1, "CommandClass": "COMMAND_CLASS_NOTIFICATION", "Index": 5, "Node": 36, "Genre": "User", "Help": "Water Alerts", "ValueIDKey": 1407375493578772, "ReadOnly": false, "WriteOnly": false, "ValueSet": false, "ValuePolled": false, "ChangeVerified": false, "Event": "valueAdded", "TimeStamp": 1579566891} OpenZWave/1/node/36/instance/1/commandclass/113/value/72057594647953425/,{ "Label": "Instance 1: Previous Event Cleared", "Value": 0, "Units": "", "Min": 0, "Max": 255, "Type": "Byte", "Instance": 1, "CommandClass": "COMMAND_CLASS_NOTIFICATION", "Index": 256, "Node": 36, "Genre": "User", "Help": "Previous Event that was sent", "ValueIDKey": 72057594647953425, "ReadOnly": false, "WriteOnly": false, "ValueSet": false, "ValuePolled": false, "ChangeVerified": false, "Event": "valueAdded", "TimeStamp": 1579566891} OpenZWave/1/node/36/instance/1/commandclass/114/,{ "Instance": 1, "CommandClassId": 114, "CommandClass": "COMMAND_CLASS_MANUFACTURER_SPECIFIC", "TimeStamp": 1579566891} OpenZWave/1/node/36/instance/1/commandclass/114/value/618430483/,{ "Label": "Loaded Config Revision", "Value": 10, "Units": "", "Min": -2147483648, "Max": 2147483647, "Type": "Int", "Instance": 1, "CommandClass": "COMMAND_CLASS_MANUFACTURER_SPECIFIC", "Index": 0, "Node": 36, "Genre": "System", "Help": "Revision of the Config file currently loaded", "ValueIDKey": 618430483, "ReadOnly": false, "WriteOnly": false, "ValueSet": false, "ValuePolled": false, "ChangeVerified": false, "Event": "valueAdded", "TimeStamp": 1579566891} @@ -142,7 +142,7 @@ OpenZWave/1/node/36/instance/2/commandclass/94/value/618102817/,{ "Label": "I OpenZWave/1/node/36/instance/2/commandclass/94/value/281475594813478/,{ "Label": "Instance 2: InstallerIcon", "Value": 3079, "Units": "", "Min": -32768, "Max": 32767, "Type": "Short", "Instance": 2, "CommandClass": "COMMAND_CLASS_ZWAVEPLUS_INFO", "Index": 1, "Node": 36, "Genre": "System", "Help": "Icon File to use for the Installer Application", "ValueIDKey": 281475594813478, "ReadOnly": false, "WriteOnly": false, "ValueSet": false, "ValuePolled": false, "ChangeVerified": false, "Event": "valueAdded", "TimeStamp": 1579566891} OpenZWave/1/node/36/instance/2/commandclass/94/value/562950571524134/,{ "Label": "Instance 2: UserIcon", "Value": 3079, "Units": "", "Min": -32768, "Max": 32767, "Type": "Short", "Instance": 2, "CommandClass": "COMMAND_CLASS_ZWAVEPLUS_INFO", "Index": 2, "Node": 36, "Genre": "System", "Help": "Icon File to use for the User Application", "ValueIDKey": 562950571524134, "ReadOnly": false, "WriteOnly": false, "ValueSet": false, "ValuePolled": false, "ChangeVerified": false, "Event": "valueAdded", "TimeStamp": 1579566891} OpenZWave/1/node/36/instance/2/commandclass/113/,{ "Instance": 2, "CommandClassId": 113, "CommandClass": "COMMAND_CLASS_NOTIFICATION", "TimeStamp": 1579566891} -OpenZWave/1/node/36/instance/2/commandclass/113/value/1407375493578788/,{ "Label": "Instance 2: Water", "Value": { "List": [ { "Value": 0, "Label": "Clear" }, { "Value": 2, "Label": "Water Leak at Unknown Location" } ], "Selected": "Clear" }, "Units": "", "Min": 0, "Max": 0, "Type": "List", "Instance": 2, "CommandClass": "COMMAND_CLASS_NOTIFICATION", "Index": 5, "Node": 36, "Genre": "User", "Help": "Water Alerts", "ValueIDKey": 1407375493578788, "ReadOnly": false, "WriteOnly": false, "ValueSet": false, "ValuePolled": false, "ChangeVerified": false, "Event": "valueAdded", "TimeStamp": 1579566891} +OpenZWave/1/node/36/instance/2/commandclass/113/value/1407375493578788/,{ "Label": "Instance 2: Water", "Value": { "List": [ { "Value": 0, "Label": "Clear" }, { "Value": 2, "Label": "Water Leak at Unknown Location" } ], "Selected": "Clear", "Selected_id": 0 }, "Units": "", "Min": 0, "Max": 0, "Type": "List", "Instance": 2, "CommandClass": "COMMAND_CLASS_NOTIFICATION", "Index": 5, "Node": 36, "Genre": "User", "Help": "Water Alerts", "ValueIDKey": 1407375493578788, "ReadOnly": false, "WriteOnly": false, "ValueSet": false, "ValuePolled": false, "ChangeVerified": false, "Event": "valueAdded", "TimeStamp": 1579566891} OpenZWave/1/node/36/instance/2/commandclass/113/value/72057594647953441/,{ "Label": "Instance 2: Previous Event Cleared", "Value": 0, "Units": "", "Min": 0, "Max": 255, "Type": "Byte", "Instance": 2, "CommandClass": "COMMAND_CLASS_NOTIFICATION", "Index": 256, "Node": 36, "Genre": "User", "Help": "Previous Event that was sent", "ValueIDKey": 72057594647953441, "ReadOnly": false, "WriteOnly": false, "ValueSet": false, "ValuePolled": false, "ChangeVerified": false, "Event": "valueAdded", "TimeStamp": 1579566891} OpenZWave/1/node/36/association/1/,{ "Name": "Lifeline", "Help": "", "MaxAssociations": 5, "Members": [ "1.1" ], "TimeStamp": 1579566891} OpenZWave/1/node/36/association/2/,{ "Name": "Send the configuration parameter 0x88", "Help": "", "MaxAssociations": 5, "Members": [], "TimeStamp": 1579566891}