From 872cd47e87acfb932428422c7271533b19f0a274 Mon Sep 17 00:00:00 2001 From: Raman Gupta <7243222+raman325@users.noreply.github.com> Date: Wed, 24 May 2023 09:09:38 -0400 Subject: [PATCH] Add proper support for zwave_js Indicator CC (#90248) * Add proper support for zwave_js Indicator CC * remove stale test * Make all indicators diagnostic * only set entity category if it is specified * Only set properties from discovery if specified * Conditionally set assumed state as well * fix const name * Don't create task * Disable property keys 3-5 by default * add additional dispatcher_connects so we catch all signals * be consistent about order * rename new discovery parameter * comment * exclude property keys 3-5 * fix remove logic * add comment so I don't forget * Switch entity category to config where necessary * cut line * less lines * Update homeassistant/components/zwave_js/switch.py Co-authored-by: kpine * Move async_remove to respond to interview started event * Set up listener immediately so we don't wait for platform creation * remove dupe import * black * append --------- Co-authored-by: kpine --- homeassistant/components/zwave_js/__init__.py | 23 +- homeassistant/components/zwave_js/button.py | 20 + .../components/zwave_js/discovery.py | 93 +- homeassistant/components/zwave_js/entity.py | 23 +- homeassistant/components/zwave_js/number.py | 4 +- homeassistant/components/zwave_js/sensor.py | 3 + homeassistant/components/zwave_js/switch.py | 14 + homeassistant/components/zwave_js/update.py | 2 +- tests/components/zwave_js/conftest.py | 47 +- .../fixtures/indicator_test_state.json | 190 +++ .../fixtures/switch_zooz_zen72_state.json | 1299 +++++++++++++++++ tests/components/zwave_js/test_api.py | 51 +- tests/components/zwave_js/test_diagnostics.py | 2 +- tests/components/zwave_js/test_discovery.py | 154 +- tests/components/zwave_js/test_init.py | 4 +- tests/components/zwave_js/test_sensor.py | 13 - 16 files changed, 1862 insertions(+), 80 deletions(-) create mode 100644 tests/components/zwave_js/fixtures/indicator_test_state.json create mode 100644 tests/components/zwave_js/fixtures/switch_zooz_zen72_state.json diff --git a/homeassistant/components/zwave_js/__init__.py b/homeassistant/components/zwave_js/__init__.py index 66839026dd4..8d118fd89f5 100644 --- a/homeassistant/components/zwave_js/__init__.py +++ b/homeassistant/components/zwave_js/__init__.py @@ -321,6 +321,19 @@ class ControllerEvents: async def async_on_node_added(self, node: ZwaveNode) -> None: """Handle node added event.""" + # Remove stale entities that may exist from a previous interview when an + # interview is started. + base_unique_id = get_valueless_base_unique_id(self.driver_events.driver, node) + self.config_entry.async_on_unload( + node.on( + "interview started", + lambda _: async_dispatcher_send( + self.hass, + f"{DOMAIN}_{base_unique_id}_remove_entity_on_interview_started", + ), + ) + ) + # No need for a ping button or node status sensor for controller nodes if not node.is_controller_node: # Create a node status sensor for each device @@ -455,7 +468,6 @@ class NodeEvents: async def async_on_node_ready(self, node: ZwaveNode) -> None: """Handle node ready event.""" LOGGER.debug("Processing node %s", node) - driver = self.controller_events.driver_events.driver # register (or update) node in device registry device = self.controller_events.register_node_in_dev_reg(node) # We only want to create the defaultdict once, even on reinterviews @@ -464,15 +476,6 @@ class NodeEvents: # Remove any old value ids if this is a reinterview. self.controller_events.discovered_value_ids.pop(device.id, None) - # Remove stale entities that may exist from a previous interview. - async_dispatcher_send( - self.hass, - ( - f"{DOMAIN}_" - f"{get_valueless_base_unique_id(driver, node)}_" - "remove_entity_on_ready_node" - ), - ) value_updates_disc_info: dict[str, ZwaveDiscoveryInfo] = {} diff --git a/homeassistant/components/zwave_js/button.py b/homeassistant/components/zwave_js/button.py index ae6cca03c3d..7a6ec4c276c 100644 --- a/homeassistant/components/zwave_js/button.py +++ b/homeassistant/components/zwave_js/button.py @@ -36,6 +36,8 @@ async def async_setup_entry( entities: list[ZWaveBaseEntity] = [] if info.platform_hint == "notification idle": entities.append(ZWaveNotificationIdleButton(config_entry, driver, info)) + else: + entities.append(ZwaveBooleanNodeButton(config_entry, driver, info)) async_add_entities(entities) @@ -63,6 +65,21 @@ async def async_setup_entry( ) +class ZwaveBooleanNodeButton(ZWaveBaseEntity, ButtonEntity): + """Representation of a ZWave button entity for a boolean value.""" + + def __init__( + self, config_entry: ConfigEntry, driver: Driver, info: ZwaveDiscoveryInfo + ) -> None: + """Initialize entity.""" + super().__init__(config_entry, driver, info) + self._attr_name = self.generate_name(include_value_name=True) + + async def async_press(self) -> None: + """Press the button.""" + await self.info.node.async_set_value(self.info.primary_value, True) + + class ZWaveNodePingButton(ButtonEntity): """Representation of a ping button entity.""" @@ -98,6 +115,9 @@ class ZWaveNodePingButton(ButtonEntity): ) ) + # we don't listen for `remove_entity_on_ready_node` signal because this entity + # is created when the node is added which occurs before ready. It only needs to + # be removed if the node is removed from the network. self.async_on_remove( async_dispatcher_connect( self.hass, diff --git a/homeassistant/components/zwave_js/discovery.py b/homeassistant/components/zwave_js/discovery.py index 3d7139945fa..4e7d7f7748a 100644 --- a/homeassistant/components/zwave_js/discovery.py +++ b/homeassistant/components/zwave_js/discovery.py @@ -44,7 +44,7 @@ from zwave_js_server.model.node import Node as ZwaveNode from zwave_js_server.model.value import Value as ZwaveValue from homeassistant.backports.enum import StrEnum -from homeassistant.const import Platform +from homeassistant.const import EntityCategory, Platform from homeassistant.core import callback from homeassistant.helpers.device_registry import DeviceEntry @@ -108,7 +108,8 @@ class ZwaveDiscoveryInfo: node: ZwaveNode # the value object itself for primary value primary_value: ZwaveValue - # bool to specify whether state is assumed and events should be fired on value update + # bool to specify whether state is assumed and events should be fired on value + # update assumed_state: bool # the home assistant platform for which an entity should be created platform: Platform @@ -122,6 +123,8 @@ class ZwaveDiscoveryInfo: platform_data_template: BaseDiscoverySchemaDataTemplate | None = None # bool to specify whether entity should be enabled by default entity_registry_enabled_default: bool = True + # the entity category for the discovered entity + entity_category: EntityCategory | None = None @dataclass @@ -143,8 +146,14 @@ class ZWaveValueDiscoverySchema(DataclassMustHaveAtLeastOne): property_name: set[str] | None = None # [optional] the value's property key must match ANY of these values property_key: set[str | int | None] | None = None + # [optional] the value's property key must NOT match ANY of these values + not_property_key: set[str | int | None] | None = None # [optional] the value's metadata_type must match ANY of these values type: set[str] | None = None + # [optional] the value's metadata_readable must match this value + readable: bool | None = None + # [optional] the value's metadata_writeable must match this value + writeable: bool | None = None # [optional] the value's states map must include ANY of these key/value pairs any_available_states: set[tuple[int, str]] | None = None @@ -192,6 +201,8 @@ class ZWaveDiscoverySchema: assumed_state: bool = False # [optional] bool to specify whether entity should be enabled by default entity_registry_enabled_default: bool = True + # [optional] the entity category for the discovered entity + entity_category: EntityCategory | None = None def get_config_parameter_discovery_schema( @@ -695,6 +706,18 @@ DISCOVERY_SCHEMAS = [ ), allow_multi=True, ), + # binary sensor for Indicator CC + ZWaveDiscoverySchema( + platform=Platform.BINARY_SENSOR, + hint="boolean", + primary_value=ZWaveValueDiscoverySchema( + command_class={CommandClass.INDICATOR}, + type={ValueType.BOOLEAN}, + readable=True, + writeable=False, + ), + entity_category=EntityCategory.DIAGNOSTIC, + ), # generic text sensors ZWaveDiscoverySchema( platform=Platform.SENSOR, @@ -704,15 +727,6 @@ DISCOVERY_SCHEMAS = [ type={ValueType.STRING}, ), ), - ZWaveDiscoverySchema( - platform=Platform.SENSOR, - hint="string_sensor", - primary_value=ZWaveValueDiscoverySchema( - command_class={CommandClass.INDICATOR}, - type={ValueType.STRING}, - ), - entity_registry_enabled_default=False, - ), # generic numeric sensors ZWaveDiscoverySchema( platform=Platform.SENSOR, @@ -733,9 +747,11 @@ DISCOVERY_SCHEMAS = [ primary_value=ZWaveValueDiscoverySchema( command_class={CommandClass.INDICATOR}, type={ValueType.NUMBER}, + readable=True, + writeable=False, ), data_template=NumericSensorDataTemplate(), - entity_registry_enabled_default=False, + entity_category=EntityCategory.DIAGNOSTIC, ), # Meter sensors for Meter CC ZWaveDiscoverySchema( @@ -768,9 +784,7 @@ DISCOVERY_SCHEMAS = [ platform=Platform.NUMBER, hint="Basic", primary_value=ZWaveValueDiscoverySchema( - command_class={ - CommandClass.BASIC, - }, + command_class={CommandClass.BASIC}, type={ValueType.NUMBER}, property={CURRENT_VALUE_PROPERTY}, ), @@ -783,14 +797,48 @@ DISCOVERY_SCHEMAS = [ property={TARGET_VALUE_PROPERTY}, ) ], - data_template=NumericSensorDataTemplate(), entity_registry_enabled_default=False, ), + # number for Indicator CC (exclude property keys 3-5) + ZWaveDiscoverySchema( + platform=Platform.NUMBER, + primary_value=ZWaveValueDiscoverySchema( + command_class={CommandClass.INDICATOR}, + type={ValueType.NUMBER}, + not_property_key={3, 4, 5}, + readable=True, + writeable=True, + ), + entity_category=EntityCategory.CONFIG, + ), + # button for Indicator CC + ZWaveDiscoverySchema( + platform=Platform.BUTTON, + primary_value=ZWaveValueDiscoverySchema( + command_class={CommandClass.INDICATOR}, + type={ValueType.BOOLEAN}, + readable=False, + writeable=True, + ), + entity_category=EntityCategory.CONFIG, + ), # binary switches ZWaveDiscoverySchema( platform=Platform.SWITCH, primary_value=SWITCH_BINARY_CURRENT_VALUE_SCHEMA, ), + # switch for Indicator CC + ZWaveDiscoverySchema( + platform=Platform.SWITCH, + hint="indicator", + primary_value=ZWaveValueDiscoverySchema( + command_class={CommandClass.INDICATOR}, + type={ValueType.BOOLEAN}, + readable=True, + writeable=True, + ), + entity_category=EntityCategory.CONFIG, + ), # binary switch # barrier operator signaling states ZWaveDiscoverySchema( @@ -1023,6 +1071,7 @@ def async_discover_single_value( platform_data=resolved_data, additional_value_ids_to_watch=additional_value_ids_to_watch, entity_registry_enabled_default=schema.entity_registry_enabled_default, + entity_category=schema.entity_category, ) if not schema.allow_multi: @@ -1058,9 +1107,21 @@ def check_value(value: ZwaveValue, schema: ZWaveValueDiscoverySchema) -> bool: and value.property_key not in schema.property_key ): return False + # check property_key against not_property_key set + if ( + schema.not_property_key is not None + and value.property_key in schema.not_property_key + ): + return False # check metadata_type if schema.type is not None and value.metadata.type not in schema.type: return False + # check metadata_readable + if schema.readable is not None and value.metadata.readable != schema.readable: + return False + # check metadata_writeable + if schema.writeable is not None and value.metadata.writeable != schema.writeable: + return False # check available states if ( schema.any_available_states is not None diff --git a/homeassistant/components/zwave_js/entity.py b/homeassistant/components/zwave_js/entity.py index 379fe4a1825..08e997170d7 100644 --- a/homeassistant/components/zwave_js/entity.py +++ b/homeassistant/components/zwave_js/entity.py @@ -44,10 +44,12 @@ class ZWaveBaseEntity(Entity): # Entity class attributes self._attr_name = self.generate_name() self._attr_unique_id = get_unique_id(driver, self.info.primary_value.value_id) - self._attr_entity_registry_enabled_default = ( - self.info.entity_registry_enabled_default - ) - self._attr_assumed_state = self.info.assumed_state + if self.info.entity_registry_enabled_default is False: + self._attr_entity_registry_enabled_default = False + if self.info.entity_category is not None: + self._attr_entity_category = self.info.entity_category + if self.info.assumed_state: + self._attr_assumed_state = True # device is precreated in main handler self._attr_device_info = DeviceInfo( identifiers={get_device_id(driver, self.info.node)}, @@ -103,7 +105,18 @@ class ZWaveBaseEntity(Entity): ( f"{DOMAIN}_" f"{get_valueless_base_unique_id(self.driver, self.info.node)}_" - "remove_entity_on_ready_node" + "remove_entity" + ), + self.async_remove, + ) + ) + self.async_on_remove( + async_dispatcher_connect( + self.hass, + ( + f"{DOMAIN}_" + f"{get_valueless_base_unique_id(self.driver, self.info.node)}_" + "remove_entity_on_interview_started" ), self.async_remove, ) diff --git a/homeassistant/components/zwave_js/number.py b/homeassistant/components/zwave_js/number.py index a6a219fa7c6..52302c64b33 100644 --- a/homeassistant/components/zwave_js/number.py +++ b/homeassistant/components/zwave_js/number.py @@ -66,7 +66,9 @@ class ZwaveNumberEntity(ZWaveBaseEntity, NumberEntity): self._target_value = self.get_zwave_value(TARGET_VALUE_PROPERTY) # Entity class attributes - self._attr_name = self.generate_name(alternate_value_name=info.platform_hint) + self._attr_name = self.generate_name( + include_value_name=True, alternate_value_name=info.platform_hint + ) @property def native_min_value(self) -> float: diff --git a/homeassistant/components/zwave_js/sensor.py b/homeassistant/components/zwave_js/sensor.py index a9f83dcad42..469f66b792b 100644 --- a/homeassistant/components/zwave_js/sensor.py +++ b/homeassistant/components/zwave_js/sensor.py @@ -615,6 +615,9 @@ class ZWaveNodeStatusSensor(SensorEntity): self.async_poll_value, ) ) + # we don't listen for `remove_entity_on_ready_node` signal because this entity + # is created when the node is added which occurs before ready. It only needs to + # be removed if the node is removed from the network. self.async_on_remove( async_dispatcher_connect( self.hass, diff --git a/homeassistant/components/zwave_js/switch.py b/homeassistant/components/zwave_js/switch.py index 450a0cfd31d..ce640f92140 100644 --- a/homeassistant/components/zwave_js/switch.py +++ b/homeassistant/components/zwave_js/switch.py @@ -41,6 +41,8 @@ async def async_setup_entry( entities.append( ZWaveBarrierEventSignalingSwitch(config_entry, driver, info) ) + elif info.platform_hint == "indicator": + entities.append(ZWaveIndicatorSwitch(config_entry, driver, info)) else: entities.append(ZWaveSwitch(config_entry, driver, info)) @@ -85,6 +87,18 @@ class ZWaveSwitch(ZWaveBaseEntity, SwitchEntity): await self.info.node.async_set_value(self._target_value, False) +class ZWaveIndicatorSwitch(ZWaveSwitch): + """Representation of a Z-Wave Indicator CC switch.""" + + def __init__( + self, config_entry: ConfigEntry, driver: Driver, info: ZwaveDiscoveryInfo + ) -> None: + """Initialize the switch.""" + super().__init__(config_entry, driver, info) + self._target_value = self.info.primary_value + self._attr_name = self.generate_name(include_value_name=True) + + class ZWaveBarrierEventSignalingSwitch(ZWaveBaseEntity, SwitchEntity): """Switch is used to turn on/off a barrier device's event signaling subsystem.""" diff --git a/homeassistant/components/zwave_js/update.py b/homeassistant/components/zwave_js/update.py index 70d12b22ded..243642086d0 100644 --- a/homeassistant/components/zwave_js/update.py +++ b/homeassistant/components/zwave_js/update.py @@ -317,7 +317,7 @@ class ZWaveNodeFirmwareUpdate(UpdateEntity): self.async_on_remove( async_dispatcher_connect( self.hass, - f"{DOMAIN}_{self._base_unique_id}_remove_entity_on_ready_node", + f"{DOMAIN}_{self._base_unique_id}_remove_entity_on_interview_started", self.async_remove, ) ) diff --git a/tests/components/zwave_js/conftest.py b/tests/components/zwave_js/conftest.py index 32082a0bb85..d071d937915 100644 --- a/tests/components/zwave_js/conftest.py +++ b/tests/components/zwave_js/conftest.py @@ -11,6 +11,8 @@ from zwave_js_server.model.driver import Driver from zwave_js_server.model.node import Node from zwave_js_server.version import VersionInfo +from homeassistant.core import HomeAssistant + from tests.common import MockConfigEntry, load_fixture # Add-on fixtures @@ -109,7 +111,7 @@ def mock_addon_options(addon_info): def set_addon_options_side_effect_fixture(addon_options): """Return the set add-on options side effect.""" - async def set_addon_options(hass, slug, options): + async def set_addon_options(hass: HomeAssistant, slug, options): """Mock set add-on options.""" addon_options.update(options["options"]) @@ -130,7 +132,7 @@ def mock_set_addon_options(set_addon_options_side_effect): def install_addon_side_effect_fixture(addon_store_info, addon_info): """Return the install add-on side effect.""" - async def install_addon(hass, slug): + async def install_addon(hass: HomeAssistant, slug): """Mock install add-on.""" addon_store_info.return_value = { "available": True, @@ -168,7 +170,7 @@ def mock_update_addon(): def start_addon_side_effect_fixture(addon_store_info, addon_info): """Return the start add-on options side effect.""" - async def start_addon(hass, slug): + async def start_addon(hass: HomeAssistant, slug): """Mock start add-on.""" addon_store_info.return_value = { "available": True, @@ -365,7 +367,7 @@ def climate_adc_t3000_state_fixture(): @pytest.fixture(name="climate_danfoss_lc_13_state", scope="session") def climate_danfoss_lc_13_state_fixture(): - """Load the climate Danfoss (LC-13) electronic radiator thermostat node state fixture data.""" + """Load Danfoss (LC-13) electronic radiator thermostat node state fixture data.""" return json.loads(load_fixture("zwave_js/climate_danfoss_lc_13_state.json")) @@ -522,7 +524,7 @@ def climate_radio_thermostat_ct101_multiple_temp_units_state_fixture(): scope="session", ) def climate_radio_thermostat_ct100_mode_and_setpoint_on_different_endpoints_state_fixture(): - """Load the climate device with mode and setpoint on different endpoints node state fixture data.""" + """Load climate device w/ mode+setpoint on diff endpoints node state fixture data.""" return json.loads( load_fixture( "zwave_js/climate_radio_thermostat_ct100_mode_and_setpoint_on_different_endpoints_state.json" @@ -604,6 +606,18 @@ def lock_home_connect_620_state_fixture(): return json.loads(load_fixture("zwave_js/lock_home_connect_620_state.json")) +@pytest.fixture(name="switch_zooz_zen72_state", scope="session") +def switch_zooz_zen72_state_fixture(): + """Load the Zooz Zen72 switch node state fixture data.""" + return json.loads(load_fixture("zwave_js/switch_zooz_zen72_state.json")) + + +@pytest.fixture(name="indicator_test_state", scope="session") +def indicator_test_state_fixture(): + """Load the indicator CC test node state fixture data.""" + return json.loads(load_fixture("zwave_js/indicator_test_state.json")) + + # model fixtures @@ -612,7 +626,6 @@ def mock_client_fixture( controller_state, controller_node_state, version_state, log_config_state ): """Mock a client.""" - with patch( "homeassistant.components.zwave_js.ZwaveClient", autospec=True ) as client_class: @@ -722,7 +735,7 @@ def climate_radio_thermostat_ct100_plus_fixture( def climate_radio_thermostat_ct100_plus_different_endpoints_fixture( client, climate_radio_thermostat_ct100_plus_different_endpoints_state ): - """Mock a climate radio thermostat ct100 plus node with values on different endpoints.""" + """Mock climate radio thermostat ct100 plus node w/ values on diff endpoints.""" node = Node( client, copy.deepcopy(climate_radio_thermostat_ct100_plus_different_endpoints_state), @@ -773,7 +786,7 @@ def climate_adc_t3000_missing_mode_fixture(client, climate_adc_t3000_state): @pytest.fixture(name="climate_adc_t3000_missing_fan_mode_states") def climate_adc_t3000_missing_fan_mode_states_fixture(client, climate_adc_t3000_state): - """Mock a climate ADC-T3000 node with missing 'states' metadata on Thermostat Fan Mode.""" + """Mock ADC-T3000 node w/ missing 'states' metadata on Thermostat Fan Mode.""" data = copy.deepcopy(climate_adc_t3000_state) data["name"] = f"{data['name']} missing fan mode states" for value in data["values"]: @@ -872,7 +885,7 @@ def nortek_thermostat_removed_event_fixture(client): @pytest.fixture(name="integration") -async def integration_fixture(hass, client): +async def integration_fixture(hass: HomeAssistant, client): """Set up the zwave_js integration.""" entry = MockConfigEntry(domain="zwave_js", data={"url": "ws://test.org"}) entry.add_to_hass(hass) @@ -1146,3 +1159,19 @@ def lock_home_connect_620_fixture(client, lock_home_connect_620_state): node = Node(client, copy.deepcopy(lock_home_connect_620_state)) client.driver.controller.nodes[node.node_id] = node return node + + +@pytest.fixture(name="switch_zooz_zen72") +def switch_zooz_zen72_fixture(client, switch_zooz_zen72_state): + """Mock a Zooz Zen72 switch node.""" + node = Node(client, copy.deepcopy(switch_zooz_zen72_state)) + client.driver.controller.nodes[node.node_id] = node + return node + + +@pytest.fixture(name="indicator_test") +def indicator_test_fixture(client, indicator_test_state): + """Mock a indicator CC test node.""" + node = Node(client, copy.deepcopy(indicator_test_state)) + client.driver.controller.nodes[node.node_id] = node + return node diff --git a/tests/components/zwave_js/fixtures/indicator_test_state.json b/tests/components/zwave_js/fixtures/indicator_test_state.json new file mode 100644 index 00000000000..9e823b79109 --- /dev/null +++ b/tests/components/zwave_js/fixtures/indicator_test_state.json @@ -0,0 +1,190 @@ +{ + "nodeId": 43, + "index": 0, + "installerIcon": 1536, + "userIcon": 1540, + "status": 4, + "ready": true, + "isListening": true, + "isRouting": true, + "isSecure": true, + "manufacturerId": 634, + "productId": 40962, + "productType": 28672, + "firmwareVersion": "10.0.1", + "zwavePlusVersion": 2, + "location": "**REDACTED**", + "deviceConfig": { + "filename": "/usr/src/app/store/.config-db/devices/0x027a/zen72.json", + "isEmbedded": true, + "manufacturer": "Fake", + "manufacturerId": 634, + "label": "Device", + "description": "This is a fake device", + "devices": [ + { + "productType": 28672, + "productId": 40962 + } + ], + "firmwareVersion": { + "min": "0.0", + "max": "255.255" + }, + "associations": {}, + "paramInformation": { + "_map": {} + }, + "compat": { + "skipConfigurationNameQuery": true, + "skipConfigurationInfoQuery": true + }, + "metadata": { + "inclusion": "1. Initiate inclusion (pairing) in the app (or web interface).\n2. TAP UP 3 TIMES QUICKLY if using traditional Z-Wave inclusion.\n3. The LED indicator will blink blue to signal communication and turn green for 3 seconds if inclusion is successful or turn red for 3 seconds if the pairing attempt fails", + "exclusion": "1. Bring your Z-Wave gateway (hub) close to the switch if possible\n2. Put the Z-Wave hub into exclusion mode (not sure how to do that? ask@getzooz.com) \n3. Tap the lower paddle on the switch 3 times quickly (the LED indicator will start blinking blue)\n4. Your hub will confirm exclusion, the LED indicator on the switch will turn green for 3 seconds, and the device will disappear from your controller's device list", + "reset": "If your primary controller is missing or inoperable, you may need to reset the device to factory settings. To reset the switch, press and hold the lower paddle for 10 seconds until the LED indicator starts blinking. Release paddle, and immediately after, tap the lower paddle 5 times to complete the reset. The LED indicator will flash blue 3 times and turn red for 3 seconds to confirm successful reset", + "manual": "https://products.z-wavealliance.org/ProductManual/File?folder=&filename=product_documents/4108/zooz-700-series-z-wave-dimmer-zen72-manual.pdf" + } + }, + "label": "Device", + "interviewAttempts": 0, + "endpoints": [ + { + "nodeId": 43, + "index": 0, + "installerIcon": 1536, + "userIcon": 1540, + "deviceClass": { + "basic": { + "key": 4, + "label": "Routing Slave" + }, + "generic": { + "key": 17, + "label": "Multilevel Switch" + }, + "specific": { + "key": 1, + "label": "Multilevel Power Switch" + }, + "mandatorySupportedCCs": [32, 38, 39], + "mandatoryControlledCCs": [] + }, + "commandClasses": [ + { + "id": 135, + "name": "Indicator", + "version": 3, + "isSecure": true + } + ] + } + ], + "values": [ + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": "Test", + "propertyKey": "Sensor", + "propertyName": "Test", + "propertyKeyName": "Sensor", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": false, + "description": "Tests non-writeable Indicator CC number gets created as a sensor entity", + "label": "Sensor", + "ccSpecific": { + "indicatorId": 80, + "propertyId": 4 + } + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": "Test", + "propertyKey": "Switch", + "propertyName": "Test", + "propertyKeyName": "Switch", + "ccVersion": 3, + "metadata": { + "type": "boolean", + "readable": true, + "writeable": true, + "description": "Tests writeable Indicator CC boolean gets created as a switch entity", + "label": "Switch", + "ccSpecific": { + "indicatorId": 80, + "propertyId": 5 + } + }, + "value": false + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": "Test", + "propertyKey": "Binary Sensor", + "propertyName": "Test", + "propertyKeyName": "Binary Sensor", + "ccVersion": 3, + "metadata": { + "type": "boolean", + "readable": true, + "writeable": false, + "description": "Tests non-writeable Indicator CC boolean gets created as a binary sensor entity", + "label": "Binary Sensor", + "ccSpecific": { + "indicatorId": 80, + "propertyId": 5 + } + }, + "value": false + } + ], + "isFrequentListening": false, + "maxDataRate": 100000, + "supportedDataRates": [40000, 100000], + "protocolVersion": 3, + "supportsBeaming": true, + "supportsSecurity": false, + "nodeType": 1, + "zwavePlusNodeType": 0, + "zwavePlusRoleType": 5, + "deviceClass": { + "basic": { + "key": 4, + "label": "Routing Slave" + }, + "generic": { + "key": 17, + "label": "Multilevel Switch" + }, + "specific": { + "key": 1, + "label": "Multilevel Power Switch" + }, + "mandatorySupportedCCs": [32, 38, 39], + "mandatoryControlledCCs": [] + }, + "interviewStage": "Complete", + "deviceDatabaseUrl": "https://devices.zwave-js.io/?jumpTo=0x027a:0x7000:0xa002:10.0.1", + "statistics": { + "commandsTX": 64, + "commandsRX": 88, + "commandsDroppedRX": 0, + "commandsDroppedTX": 0, + "timeoutResponse": 0, + "rtt": 74.5, + "rssi": -60 + }, + "highestSecurityClass": 1, + "isControllerNode": false, + "keepAwake": false +} diff --git a/tests/components/zwave_js/fixtures/switch_zooz_zen72_state.json b/tests/components/zwave_js/fixtures/switch_zooz_zen72_state.json new file mode 100644 index 00000000000..04b3a336618 --- /dev/null +++ b/tests/components/zwave_js/fixtures/switch_zooz_zen72_state.json @@ -0,0 +1,1299 @@ +{ + "nodeId": 43, + "index": 0, + "installerIcon": 1536, + "userIcon": 1540, + "status": 4, + "ready": true, + "isListening": true, + "isRouting": true, + "isSecure": true, + "manufacturerId": 634, + "productId": 40962, + "productType": 28672, + "firmwareVersion": "10.0.1", + "zwavePlusVersion": 2, + "location": "**REDACTED**", + "deviceConfig": { + "filename": "/usr/src/app/store/.config-db/devices/0x027a/zen72.json", + "isEmbedded": true, + "manufacturer": "Zooz", + "manufacturerId": 634, + "label": "ZEN72", + "description": "Z-Wave Plus 700 Series Dimmer Switch", + "devices": [ + { + "productType": 28672, + "productId": 40962 + } + ], + "firmwareVersion": { + "min": "0.0", + "max": "255.255" + }, + "associations": {}, + "paramInformation": { + "_map": {} + }, + "compat": { + "skipConfigurationNameQuery": true, + "skipConfigurationInfoQuery": true + }, + "metadata": { + "inclusion": "1. Initiate inclusion (pairing) in the app (or web interface).\n2. TAP UP 3 TIMES QUICKLY if using traditional Z-Wave inclusion.\n3. The LED indicator will blink blue to signal communication and turn green for 3 seconds if inclusion is successful or turn red for 3 seconds if the pairing attempt fails", + "exclusion": "1. Bring your Z-Wave gateway (hub) close to the switch if possible\n2. Put the Z-Wave hub into exclusion mode (not sure how to do that? ask@getzooz.com) \n3. Tap the lower paddle on the switch 3 times quickly (the LED indicator will start blinking blue)\n4. Your hub will confirm exclusion, the LED indicator on the switch will turn green for 3 seconds, and the device will disappear from your controller's device list", + "reset": "If your primary controller is missing or inoperable, you may need to reset the device to factory settings. To reset the switch, press and hold the lower paddle for 10 seconds until the LED indicator starts blinking. Release paddle, and immediately after, tap the lower paddle 5 times to complete the reset. The LED indicator will flash blue 3 times and turn red for 3 seconds to confirm successful reset", + "manual": "https://products.z-wavealliance.org/ProductManual/File?folder=&filename=product_documents/4108/zooz-700-series-z-wave-dimmer-zen72-manual.pdf" + } + }, + "label": "ZEN72", + "interviewAttempts": 0, + "endpoints": [ + { + "nodeId": 43, + "index": 0, + "installerIcon": 1536, + "userIcon": 1540, + "deviceClass": { + "basic": { + "key": 4, + "label": "Routing Slave" + }, + "generic": { + "key": 17, + "label": "Multilevel Switch" + }, + "specific": { + "key": 1, + "label": "Multilevel Power Switch" + }, + "mandatorySupportedCCs": [32, 38, 39], + "mandatoryControlledCCs": [] + }, + "commandClasses": [ + { + "id": 38, + "name": "Multilevel Switch", + "version": 4, + "isSecure": true + }, + { + "id": 159, + "name": "Security 2", + "version": 1, + "isSecure": true + }, + { + "id": 94, + "name": "Z-Wave Plus Info", + "version": 2, + "isSecure": false + }, + { + "id": 85, + "name": "Transport Service", + "version": 2, + "isSecure": false + }, + { + "id": 108, + "name": "Supervision", + "version": 1, + "isSecure": false + }, + { + "id": 134, + "name": "Version", + "version": 3, + "isSecure": true + }, + { + "id": 112, + "name": "Configuration", + "version": 4, + "isSecure": true + }, + { + "id": 91, + "name": "Central Scene", + "version": 3, + "isSecure": true + }, + { + "id": 133, + "name": "Association", + "version": 3, + "isSecure": true + }, + { + "id": 142, + "name": "Multi Channel Association", + "version": 4, + "isSecure": true + }, + { + "id": 89, + "name": "Association Group Information", + "version": 3, + "isSecure": true + }, + { + "id": 114, + "name": "Manufacturer Specific", + "version": 2, + "isSecure": true + }, + { + "id": 90, + "name": "Device Reset Locally", + "version": 1, + "isSecure": true + }, + { + "id": 135, + "name": "Indicator", + "version": 3, + "isSecure": true + }, + { + "id": 115, + "name": "Powerlevel", + "version": 1, + "isSecure": true + }, + { + "id": 122, + "name": "Firmware Update Meta Data", + "version": 5, + "isSecure": true + } + ] + } + ], + "values": [ + { + "endpoint": 0, + "commandClass": 38, + "commandClassName": "Multilevel Switch", + "property": "targetValue", + "propertyName": "targetValue", + "ccVersion": 4, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "label": "Target value", + "valueChangeOptions": ["transitionDuration"], + "min": 0, + "max": 99 + }, + "value": 29 + }, + { + "endpoint": 0, + "commandClass": 38, + "commandClassName": "Multilevel Switch", + "property": "duration", + "propertyName": "duration", + "ccVersion": 4, + "metadata": { + "type": "duration", + "readable": true, + "writeable": false, + "label": "Remaining duration" + }, + "value": { + "value": 3, + "unit": "seconds" + } + }, + { + "endpoint": 0, + "commandClass": 38, + "commandClassName": "Multilevel Switch", + "property": "currentValue", + "propertyName": "currentValue", + "ccVersion": 4, + "metadata": { + "type": "number", + "readable": true, + "writeable": false, + "label": "Current value", + "min": 0, + "max": 99 + }, + "value": 99 + }, + { + "endpoint": 0, + "commandClass": 38, + "commandClassName": "Multilevel Switch", + "property": "Up", + "propertyName": "Up", + "ccVersion": 4, + "metadata": { + "type": "boolean", + "readable": false, + "writeable": true, + "label": "Perform a level change (Up)", + "ccSpecific": { + "switchType": 2 + }, + "valueChangeOptions": ["transitionDuration"] + } + }, + { + "endpoint": 0, + "commandClass": 38, + "commandClassName": "Multilevel Switch", + "property": "Down", + "propertyName": "Down", + "ccVersion": 4, + "metadata": { + "type": "boolean", + "readable": false, + "writeable": true, + "label": "Perform a level change (Down)", + "ccSpecific": { + "switchType": 2 + }, + "valueChangeOptions": ["transitionDuration"] + } + }, + { + "endpoint": 0, + "commandClass": 38, + "commandClassName": "Multilevel Switch", + "property": "restorePrevious", + "propertyName": "restorePrevious", + "ccVersion": 4, + "metadata": { + "type": "boolean", + "readable": false, + "writeable": true, + "label": "Restore previous value" + } + }, + { + "endpoint": 0, + "commandClass": 91, + "commandClassName": "Central Scene", + "property": "scene", + "propertyKey": "001", + "propertyName": "scene", + "propertyKeyName": "001", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": false, + "label": "Scene 001", + "min": 0, + "max": 255, + "states": { + "0": "KeyPressed", + "1": "KeyReleased", + "2": "KeyHeldDown", + "3": "KeyPressed2x", + "4": "KeyPressed3x", + "5": "KeyPressed4x", + "6": "KeyPressed5x" + } + } + }, + { + "endpoint": 0, + "commandClass": 91, + "commandClassName": "Central Scene", + "property": "scene", + "propertyKey": "002", + "propertyName": "scene", + "propertyKeyName": "002", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": false, + "label": "Scene 002", + "min": 0, + "max": 255, + "states": { + "0": "KeyPressed", + "1": "KeyReleased", + "2": "KeyHeldDown", + "3": "KeyPressed2x", + "4": "KeyPressed3x", + "5": "KeyPressed4x", + "6": "KeyPressed5x" + } + } + }, + { + "endpoint": 0, + "commandClass": 91, + "commandClassName": "Central Scene", + "property": "slowRefresh", + "propertyName": "slowRefresh", + "ccVersion": 3, + "metadata": { + "type": "boolean", + "readable": true, + "writeable": true, + "description": "When this is true, KeyHeldDown notifications are sent every 55s. When this is false, the notifications are sent every 200ms.", + "label": "Send held down notifications at a slow rate" + } + }, + { + "endpoint": 0, + "commandClass": 112, + "commandClassName": "Configuration", + "property": 1, + "propertyName": "Inverted Orientation", + "ccVersion": 4, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "label": "Inverted Orientation", + "default": 0, + "min": 0, + "max": 2, + "states": { + "0": "Disable", + "1": "Enable", + "2": "Momentary mode" + }, + "valueSize": 1, + "format": 1, + "allowManualEntry": false, + "isFromConfig": true + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 112, + "commandClassName": "Configuration", + "property": 2, + "propertyName": "LED Indicator", + "ccVersion": 4, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "label": "LED Indicator", + "default": 0, + "min": 0, + "max": 3, + "states": { + "0": "On when load is off", + "1": "On when load is on", + "2": "Always off", + "3": "Always on" + }, + "valueSize": 1, + "format": 0, + "allowManualEntry": false, + "isFromConfig": true + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 112, + "commandClassName": "Configuration", + "property": 23, + "propertyName": "LED Indicator Color", + "ccVersion": 4, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "label": "LED Indicator Color", + "default": 1, + "min": 0, + "max": 3, + "states": { + "0": "White", + "1": "Blue", + "2": "Green", + "3": "Red" + }, + "valueSize": 1, + "format": 1, + "allowManualEntry": false, + "isFromConfig": true + }, + "value": 2 + }, + { + "endpoint": 0, + "commandClass": 112, + "commandClassName": "Configuration", + "property": 24, + "propertyName": "LED Indicator Brightness", + "ccVersion": 4, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "label": "LED Indicator Brightness", + "default": 1, + "min": 0, + "max": 2, + "states": { + "0": "Bright (100%)", + "1": "Medium (60%)", + "2": "Low (30%)" + }, + "valueSize": 1, + "format": 1, + "allowManualEntry": false, + "isFromConfig": true + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 112, + "commandClassName": "Configuration", + "property": 3, + "propertyName": "Auto Turn-Off Timer", + "ccVersion": 4, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "label": "Auto Turn-Off Timer", + "default": 0, + "min": 0, + "max": 65535, + "states": { + "0": "Disable" + }, + "unit": "minutes", + "valueSize": 4, + "format": 1, + "allowManualEntry": true, + "isFromConfig": true + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 112, + "commandClassName": "Configuration", + "property": 5, + "propertyName": "Auto Turn-On Timer", + "ccVersion": 4, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "label": "Auto Turn-On Timer", + "default": 0, + "min": 0, + "max": 65535, + "states": { + "0": "Disable" + }, + "unit": "minutes", + "valueSize": 4, + "format": 1, + "allowManualEntry": true, + "isFromConfig": true + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 112, + "commandClassName": "Configuration", + "property": 8, + "propertyName": "State After Power Failure", + "ccVersion": 4, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "label": "State After Power Failure", + "default": 2, + "min": 0, + "max": 2, + "states": { + "0": "Always off", + "1": "Always on", + "2": "Previous state" + }, + "valueSize": 1, + "format": 0, + "allowManualEntry": false, + "isFromConfig": true + }, + "value": 2 + }, + { + "endpoint": 0, + "commandClass": 112, + "commandClassName": "Configuration", + "property": 9, + "propertyName": "Ramp Rate Off/On (Manual Control)", + "ccVersion": 4, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "label": "Ramp Rate Off/On (Manual Control)", + "default": 1, + "min": 0, + "max": 99, + "states": { + "0": "Instant on/off" + }, + "unit": "seconds", + "valueSize": 1, + "format": 1, + "allowManualEntry": true, + "isFromConfig": true + }, + "value": 1 + }, + { + "endpoint": 0, + "commandClass": 112, + "commandClassName": "Configuration", + "property": 10, + "propertyName": "Minimum Brightness", + "ccVersion": 4, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "label": "Minimum Brightness", + "default": 1, + "min": 1, + "max": 99, + "unit": "%", + "valueSize": 1, + "format": 1, + "allowManualEntry": true, + "isFromConfig": true + }, + "value": 10 + }, + { + "endpoint": 0, + "commandClass": 112, + "commandClassName": "Configuration", + "property": 11, + "propertyName": "Maximum Brightness", + "ccVersion": 4, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "label": "Maximum Brightness", + "default": 99, + "min": 1, + "max": 99, + "unit": "%", + "valueSize": 1, + "format": 1, + "allowManualEntry": true, + "isFromConfig": true + }, + "value": 99 + }, + { + "endpoint": 0, + "commandClass": 112, + "commandClassName": "Configuration", + "property": 12, + "propertyName": "Double-Tap Upper Paddle Behavior", + "ccVersion": 4, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "label": "Double-Tap Upper Paddle Behavior", + "default": 0, + "min": 0, + "max": 3, + "states": { + "0": "Full brightness", + "1": "Configured custom brightness (parameter 18)", + "2": "Configured maximum brightness", + "3": "Double tap disabled" + }, + "valueSize": 1, + "format": 1, + "allowManualEntry": false, + "isFromConfig": true + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 112, + "commandClassName": "Configuration", + "property": 25, + "propertyName": "Single-Tap Upper Paddle Behavior", + "ccVersion": 4, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "label": "Single-Tap Upper Paddle Behavior", + "default": 0, + "min": 0, + "max": 3, + "states": { + "0": "Previous brightness", + "1": "Configured custom brightness (parameter 18)", + "2": "Configured maximum brightness", + "3": "Full brightness" + }, + "valueSize": 1, + "format": 1, + "allowManualEntry": false, + "isFromConfig": true + }, + "value": 1 + }, + { + "endpoint": 0, + "commandClass": 112, + "commandClassName": "Configuration", + "property": 13, + "propertyName": "Enable/Disable Scene Control", + "ccVersion": 4, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Enable or disable scene control functionality for quick double tap triggers.", + "label": "Enable/Disable Scene Control", + "default": 0, + "min": 0, + "max": 1, + "states": { + "0": "Disable", + "1": "Enable" + }, + "valueSize": 1, + "format": 1, + "allowManualEntry": false, + "isFromConfig": true + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 112, + "commandClassName": "Configuration", + "property": 15, + "propertyName": "Smart Switch Mode", + "ccVersion": 4, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "label": "Smart Switch Mode", + "default": 1, + "min": 0, + "max": 2, + "states": { + "0": "Local control disabled", + "1": "Local and Z-Wave control enabled", + "2": "Local and Z-Wave control disabled" + }, + "valueSize": 1, + "format": 1, + "allowManualEntry": false, + "isFromConfig": true + }, + "value": 1 + }, + { + "endpoint": 0, + "commandClass": 112, + "commandClassName": "Configuration", + "property": 19, + "propertyName": "3-Way Switch Type", + "ccVersion": 4, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "label": "3-Way Switch Type", + "default": 0, + "min": 0, + "max": 3, + "states": { + "0": "Toggle switch", + "1": "Toggle switch (with dimming control)", + "2": "Momentary switch (with dimming control)", + "3": "Momentary switch (with dimming control, but reduce after double click)" + }, + "valueSize": 1, + "format": 0, + "allowManualEntry": false, + "isFromConfig": true + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 112, + "commandClassName": "Configuration", + "property": 20, + "propertyName": "Smart Bulb Mode: Dimming Reporting Behavior", + "ccVersion": 4, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Reporting behavior for local control and Z-Wave disabled / enabled, respectively. Final level for Z-Wave control actions is always sent as a Multilevel Switch report, regardless of setting.", + "label": "Smart Bulb Mode: Dimming Reporting Behavior", + "default": 0, + "min": 0, + "max": 1, + "states": { + "0": "Each brightness level / Final level", + "1": "Final brightness level only for local dimming, regardless of control setting" + }, + "valueSize": 1, + "format": 0, + "allowManualEntry": false, + "isFromConfig": true + }, + "value": 1 + }, + { + "endpoint": 0, + "commandClass": 112, + "commandClassName": "Configuration", + "property": 21, + "propertyName": "Smart Bulb Mode: Reporting Behavior", + "ccVersion": 4, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "label": "Smart Bulb Mode: Reporting Behavior", + "default": 0, + "min": 0, + "max": 1, + "states": { + "0": "Switch always reports status and changes LED state", + "1": "No status or LED state change when physical (and Z-Wave) control disabled" + }, + "valueSize": 1, + "format": 1, + "allowManualEntry": false, + "isFromConfig": true + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 112, + "commandClassName": "Configuration", + "property": 16, + "propertyName": "Local Dimming Speed", + "ccVersion": 4, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "The time it takes to get from 0% to 100% brightness when pressing and holding the paddle.", + "label": "Local Dimming Speed", + "default": 5, + "min": 1, + "max": 99, + "unit": "seconds", + "valueSize": 1, + "format": 1, + "allowManualEntry": true, + "isFromConfig": true + }, + "value": 3 + }, + { + "endpoint": 0, + "commandClass": 112, + "commandClassName": "Configuration", + "property": 18, + "propertyName": "Custom Brightness Level", + "ccVersion": 4, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "label": "Custom Brightness Level", + "default": 0, + "min": 0, + "max": 99, + "states": { + "0": "Previous brightness" + }, + "valueSize": 1, + "format": 1, + "allowManualEntry": true, + "isFromConfig": true + }, + "value": 50 + }, + { + "endpoint": 0, + "commandClass": 112, + "commandClassName": "Configuration", + "property": 22, + "propertyName": "Night Light Mode", + "ccVersion": 4, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Set the brightness level the dimmer will turn on to when off and when lower paddle is held down for one second.", + "label": "Night Light Mode", + "default": 20, + "min": 0, + "max": 99, + "states": { + "0": "Disable" + }, + "valueSize": 1, + "format": 0, + "allowManualEntry": true, + "isFromConfig": true + }, + "value": 25 + }, + { + "endpoint": 0, + "commandClass": 112, + "commandClassName": "Configuration", + "property": 26, + "propertyName": "Local Programming", + "ccVersion": 4, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Controls programming (eg. including/excluding) device through physical inputs, allowing the according input sequence to be used for scene control instead. Factory reset remains available.", + "label": "Local Programming", + "default": 1, + "min": 0, + "max": 1, + "states": { + "0": "Enable", + "1": "Disable" + }, + "valueSize": 1, + "format": 1, + "allowManualEntry": false, + "isFromConfig": true + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 114, + "commandClassName": "Manufacturer Specific", + "property": "manufacturerId", + "propertyName": "manufacturerId", + "ccVersion": 2, + "metadata": { + "type": "number", + "readable": true, + "writeable": false, + "label": "Manufacturer ID", + "min": 0, + "max": 65535 + }, + "value": 634 + }, + { + "endpoint": 0, + "commandClass": 114, + "commandClassName": "Manufacturer Specific", + "property": "productType", + "propertyName": "productType", + "ccVersion": 2, + "metadata": { + "type": "number", + "readable": true, + "writeable": false, + "label": "Product type", + "min": 0, + "max": 65535 + }, + "value": 28672 + }, + { + "endpoint": 0, + "commandClass": 114, + "commandClassName": "Manufacturer Specific", + "property": "productId", + "propertyName": "productId", + "ccVersion": 2, + "metadata": { + "type": "number", + "readable": true, + "writeable": false, + "label": "Product ID", + "min": 0, + "max": 65535 + }, + "value": 40962 + }, + { + "endpoint": 0, + "commandClass": 134, + "commandClassName": "Version", + "property": "libraryType", + "propertyName": "libraryType", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": false, + "label": "Library type", + "states": { + "0": "Unknown", + "1": "Static Controller", + "2": "Controller", + "3": "Enhanced Slave", + "4": "Slave", + "5": "Installer", + "6": "Routing Slave", + "7": "Bridge Controller", + "8": "Device under Test", + "9": "N/A", + "10": "AV Remote", + "11": "AV Device" + } + }, + "value": 3 + }, + { + "endpoint": 0, + "commandClass": 134, + "commandClassName": "Version", + "property": "protocolVersion", + "propertyName": "protocolVersion", + "ccVersion": 3, + "metadata": { + "type": "string", + "readable": true, + "writeable": false, + "label": "Z-Wave protocol version" + }, + "value": "7.13" + }, + { + "endpoint": 0, + "commandClass": 134, + "commandClassName": "Version", + "property": "firmwareVersions", + "propertyName": "firmwareVersions", + "ccVersion": 3, + "metadata": { + "type": "string[]", + "readable": true, + "writeable": false, + "label": "Z-Wave chip firmware versions" + }, + "value": ["10.0"] + }, + { + "endpoint": 0, + "commandClass": 134, + "commandClassName": "Version", + "property": "hardwareVersion", + "propertyName": "hardwareVersion", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": false, + "label": "Z-Wave chip hardware version" + }, + "value": 1 + }, + { + "endpoint": 0, + "commandClass": 134, + "commandClassName": "Version", + "property": "sdkVersion", + "propertyName": "sdkVersion", + "ccVersion": 3, + "metadata": { + "type": "string", + "readable": true, + "writeable": false, + "label": "SDK version" + }, + "value": "7.13.9" + }, + { + "endpoint": 0, + "commandClass": 134, + "commandClassName": "Version", + "property": "applicationFrameworkAPIVersion", + "propertyName": "applicationFrameworkAPIVersion", + "ccVersion": 3, + "metadata": { + "type": "string", + "readable": true, + "writeable": false, + "label": "Z-Wave application framework API version" + }, + "value": "10.13.9" + }, + { + "endpoint": 0, + "commandClass": 134, + "commandClassName": "Version", + "property": "applicationFrameworkBuildNumber", + "propertyName": "applicationFrameworkBuildNumber", + "ccVersion": 3, + "metadata": { + "type": "string", + "readable": true, + "writeable": false, + "label": "Z-Wave application framework API build number" + }, + "value": 405 + }, + { + "endpoint": 0, + "commandClass": 134, + "commandClassName": "Version", + "property": "hostInterfaceVersion", + "propertyName": "hostInterfaceVersion", + "ccVersion": 3, + "metadata": { + "type": "string", + "readable": true, + "writeable": false, + "label": "Serial API version" + }, + "value": "unused" + }, + { + "endpoint": 0, + "commandClass": 134, + "commandClassName": "Version", + "property": "hostInterfaceBuildNumber", + "propertyName": "hostInterfaceBuildNumber", + "ccVersion": 3, + "metadata": { + "type": "string", + "readable": true, + "writeable": false, + "label": "Serial API build number" + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 134, + "commandClassName": "Version", + "property": "zWaveProtocolVersion", + "propertyName": "zWaveProtocolVersion", + "ccVersion": 3, + "metadata": { + "type": "string", + "readable": true, + "writeable": false, + "label": "Z-Wave protocol version" + }, + "value": "7.13.9" + }, + { + "endpoint": 0, + "commandClass": 134, + "commandClassName": "Version", + "property": "zWaveProtocolBuildNumber", + "propertyName": "zWaveProtocolBuildNumber", + "ccVersion": 3, + "metadata": { + "type": "string", + "readable": true, + "writeable": false, + "label": "Z-Wave protocol build number" + }, + "value": 405 + }, + { + "endpoint": 0, + "commandClass": 134, + "commandClassName": "Version", + "property": "applicationVersion", + "propertyName": "applicationVersion", + "ccVersion": 3, + "metadata": { + "type": "string", + "readable": true, + "writeable": false, + "label": "Application version" + }, + "value": "10.0.1" + }, + { + "endpoint": 0, + "commandClass": 134, + "commandClassName": "Version", + "property": "applicationBuildNumber", + "propertyName": "applicationBuildNumber", + "ccVersion": 3, + "metadata": { + "type": "string", + "readable": true, + "writeable": false, + "label": "Application build number" + }, + "value": 43707 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 80, + "propertyKey": 3, + "propertyName": "Node Identify", + "propertyKeyName": "On/Off Period: Duration", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Sets the duration of an on/off period in 1/10th seconds. Must be set together with \"On/Off Cycle Count\"", + "label": "Node Identify - On/Off Period: Duration", + "ccSpecific": { + "indicatorId": 80, + "propertyId": 3 + } + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 80, + "propertyKey": 4, + "propertyName": "Node Identify", + "propertyKeyName": "On/Off Cycle Count", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Sets the number of on/off periods. 0xff means infinite. Must be set together with \"On/Off Period duration\"", + "label": "Node Identify - On/Off Cycle Count", + "ccSpecific": { + "indicatorId": 80, + "propertyId": 4 + } + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": 80, + "propertyKey": 5, + "propertyName": "Node Identify", + "propertyKeyName": "On/Off Period: On time", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "This property is used to set the length of the On time during an On/Off period. It allows asymmetric On/Off periods. The value 0x00 MUST represent symmetric On/Off period (On time equal to Off time)", + "label": "Node Identify - On/Off Period: On time", + "ccSpecific": { + "indicatorId": 80, + "propertyId": 5 + } + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": "value", + "propertyName": "value", + "ccVersion": 3, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "label": "Indicator value", + "ccSpecific": { + "indicatorId": 0 + }, + "min": 0, + "max": 255 + } + }, + { + "endpoint": 0, + "commandClass": 135, + "commandClassName": "Indicator", + "property": "identify", + "propertyName": "identify", + "ccVersion": 3, + "metadata": { + "type": "boolean", + "readable": false, + "writeable": true, + "label": "Identify" + } + } + ], + "isFrequentListening": false, + "maxDataRate": 100000, + "supportedDataRates": [40000, 100000], + "protocolVersion": 3, + "supportsBeaming": true, + "supportsSecurity": false, + "nodeType": 1, + "zwavePlusNodeType": 0, + "zwavePlusRoleType": 5, + "deviceClass": { + "basic": { + "key": 4, + "label": "Routing Slave" + }, + "generic": { + "key": 17, + "label": "Multilevel Switch" + }, + "specific": { + "key": 1, + "label": "Multilevel Power Switch" + }, + "mandatorySupportedCCs": [32, 38, 39], + "mandatoryControlledCCs": [] + }, + "interviewStage": "Complete", + "deviceDatabaseUrl": "https://devices.zwave-js.io/?jumpTo=0x027a:0x7000:0xa002:10.0.1", + "statistics": { + "commandsTX": 64, + "commandsRX": 88, + "commandsDroppedRX": 0, + "commandsDroppedTX": 0, + "timeoutResponse": 0, + "rtt": 74.5, + "rssi": -60 + }, + "highestSecurityClass": 1, + "isControllerNode": false, + "keepAwake": false +} diff --git a/tests/components/zwave_js/test_api.py b/tests/components/zwave_js/test_api.py index f8a7a68f139..b3dd4e34ee8 100644 --- a/tests/components/zwave_js/test_api.py +++ b/tests/components/zwave_js/test_api.py @@ -85,6 +85,8 @@ from homeassistant.helpers import device_registry as dr from tests.common import MockUser from tests.typing import ClientSessionGenerator, WebSocketGenerator +CONTROLLER_PATCH_PREFIX = "zwave_js_server.model.controller.Controller" + def get_device(hass: HomeAssistant, node): """Get device ID for a node.""" @@ -131,7 +133,7 @@ async def test_network_status( # Try API call with entry ID with patch( - "zwave_js_server.model.controller.Controller.async_get_state", + f"{CONTROLLER_PATCH_PREFIX}.async_get_state", return_value=controller_state["controller"], ): await ws_client.send_json( @@ -155,7 +157,7 @@ async def test_network_status( ) assert device with patch( - "zwave_js_server.model.controller.Controller.async_get_state", + f"{CONTROLLER_PATCH_PREFIX}.async_get_state", return_value=controller_state["controller"], ): await ws_client.send_json( @@ -410,7 +412,8 @@ async def test_node_metadata( "controller being inoperable or otherwise unavailable.)" ) assert result["manual"] == ( - "https://products.z-wavealliance.org/ProductManual/File?folder=&filename=MarketCertificationFiles/2479/ZP3111-5_R2_20170316.pdf" + "https://products.z-wavealliance.org/ProductManual/File?folder=&filename=" + "MarketCertificationFiles/2479/ZP3111-5_R2_20170316.pdf" ) assert not result["wakeup"] assert ( @@ -885,7 +888,7 @@ async def test_add_node( # Test FailedZWaveCommand is caught with patch( - "zwave_js_server.model.controller.Controller.async_begin_inclusion", + f"{CONTROLLER_PATCH_PREFIX}.async_begin_inclusion", side_effect=FailedZWaveCommand("failed_command", 1, "error message"), ): await ws_client.send_json( @@ -1155,7 +1158,7 @@ async def test_provision_smart_start_node( # Test FailedZWaveCommand is caught with patch( - "zwave_js_server.model.controller.Controller.async_provision_smart_start_node", + f"{CONTROLLER_PATCH_PREFIX}.async_provision_smart_start_node", side_effect=FailedZWaveCommand("failed_command", 1, "error message"), ): await ws_client.send_json( @@ -1163,7 +1166,9 @@ async def test_provision_smart_start_node( ID: 7, TYPE: "zwave_js/provision_smart_start_node", ENTRY_ID: entry.entry_id, - QR_CODE_STRING: "90testtesttesttesttesttesttesttesttesttesttesttesttest", + QR_CODE_STRING: ( + "90testtesttesttesttesttesttesttesttesttesttesttesttest" + ), } ) msg = await ws_client.receive_json() @@ -1259,7 +1264,7 @@ async def test_unprovision_smart_start_node( # Test FailedZWaveCommand is caught with patch( - "zwave_js_server.model.controller.Controller.async_unprovision_smart_start_node", + f"{CONTROLLER_PATCH_PREFIX}.async_unprovision_smart_start_node", side_effect=FailedZWaveCommand("failed_command", 1, "error message"), ): await ws_client.send_json( @@ -1332,7 +1337,7 @@ async def test_get_provisioning_entries( # Test FailedZWaveCommand is caught with patch( - "zwave_js_server.model.controller.Controller.async_get_provisioning_entries", + f"{CONTROLLER_PATCH_PREFIX}.async_get_provisioning_entries", side_effect=FailedZWaveCommand("failed_command", 1, "error message"), ): await ws_client.send_json( @@ -1432,7 +1437,9 @@ async def test_parse_qr_code_string( ID: 6, TYPE: "zwave_js/parse_qr_code_string", ENTRY_ID: entry.entry_id, - QR_CODE_STRING: "90testtesttesttesttesttesttesttesttesttesttesttesttest", + QR_CODE_STRING: ( + "90testtesttesttesttesttesttesttesttesttesttesttesttest" + ), } ) msg = await ws_client.receive_json() @@ -1497,7 +1504,9 @@ async def test_try_parse_dsk_from_qr_code_string( ID: 6, TYPE: "zwave_js/try_parse_dsk_from_qr_code_string", ENTRY_ID: entry.entry_id, - QR_CODE_STRING: "90testtesttesttesttesttesttesttesttesttesttesttesttest", + QR_CODE_STRING: ( + "90testtesttesttesttesttesttesttesttesttesttesttesttest" + ), } ) msg = await ws_client.receive_json() @@ -1572,7 +1581,7 @@ async def test_cancel_inclusion_exclusion( # Test FailedZWaveCommand is caught with patch( - "zwave_js_server.model.controller.Controller.async_stop_inclusion", + f"{CONTROLLER_PATCH_PREFIX}.async_stop_inclusion", side_effect=FailedZWaveCommand("failed_command", 1, "error message"), ): await ws_client.send_json( @@ -1590,7 +1599,7 @@ async def test_cancel_inclusion_exclusion( # Test FailedZWaveCommand is caught with patch( - "zwave_js_server.model.controller.Controller.async_stop_exclusion", + f"{CONTROLLER_PATCH_PREFIX}.async_stop_exclusion", side_effect=FailedZWaveCommand("failed_command", 1, "error message"), ): await ws_client.send_json( @@ -1709,7 +1718,7 @@ async def test_remove_node( # Test FailedZWaveCommand is caught with patch( - "zwave_js_server.model.controller.Controller.async_begin_exclusion", + f"{CONTROLLER_PATCH_PREFIX}.async_begin_exclusion", side_effect=FailedZWaveCommand("failed_command", 1, "error message"), ): await ws_client.send_json( @@ -2054,7 +2063,7 @@ async def test_replace_failed_node( # Test FailedZWaveCommand is caught with patch( - "zwave_js_server.model.controller.Controller.async_replace_failed_node", + f"{CONTROLLER_PATCH_PREFIX}.async_replace_failed_node", side_effect=FailedZWaveCommand("failed_command", 1, "error message"), ): await ws_client.send_json( @@ -2105,7 +2114,7 @@ async def test_remove_failed_node( # Test FailedZWaveCommand is caught with patch( - "zwave_js_server.model.controller.Controller.async_remove_failed_node", + f"{CONTROLLER_PATCH_PREFIX}.async_remove_failed_node", side_effect=FailedZWaveCommand("failed_command", 1, "error message"), ): await ws_client.send_json( @@ -2200,7 +2209,7 @@ async def test_begin_healing_network( # Test FailedZWaveCommand is caught with patch( - "zwave_js_server.model.controller.Controller.async_begin_healing_network", + f"{CONTROLLER_PATCH_PREFIX}.async_begin_healing_network", side_effect=FailedZWaveCommand("failed_command", 1, "error message"), ): await ws_client.send_json( @@ -2342,7 +2351,7 @@ async def test_stop_healing_network( # Test FailedZWaveCommand is caught with patch( - "zwave_js_server.model.controller.Controller.async_stop_healing_network", + f"{CONTROLLER_PATCH_PREFIX}.async_stop_healing_network", side_effect=FailedZWaveCommand("failed_command", 1, "error message"), ): await ws_client.send_json( @@ -2403,7 +2412,7 @@ async def test_heal_node( # Test FailedZWaveCommand is caught with patch( - "zwave_js_server.model.controller.Controller.async_heal_node", + f"{CONTROLLER_PATCH_PREFIX}.async_heal_node", side_effect=FailedZWaveCommand("failed_command", 1, "error message"), ): await ws_client.send_json( @@ -3279,7 +3288,7 @@ async def test_subscribe_log_updates( async def test_update_log_config( hass: HomeAssistant, client, integration, hass_ws_client: WebSocketGenerator ) -> None: - """Test that the update_log_config WS API call works and that schema validation works.""" + """Test that update_log_config WS API call and schema validation works.""" entry = integration ws_client = await hass_ws_client(hass) @@ -3841,7 +3850,7 @@ async def test_subscribe_firmware_update_status_initial_value( integration, hass_ws_client: WebSocketGenerator, ) -> None: - """Test subscribe_firmware_update_status websocket command with in progress update.""" + """Test subscribe_firmware_update_status WS command with in progress update.""" ws_client = await hass_ws_client(hass) device = get_device(hass, multisensor_6) @@ -4160,7 +4169,7 @@ async def test_is_any_ota_firmware_update_in_progress( # Test FailedZWaveCommand is caught with patch( - "zwave_js_server.model.controller.Controller.async_is_any_ota_firmware_update_in_progress", + f"{CONTROLLER_PATCH_PREFIX}.async_is_any_ota_firmware_update_in_progress", side_effect=FailedZWaveCommand("failed_command", 1, "error message"), ): await ws_client.send_json( diff --git a/tests/components/zwave_js/test_diagnostics.py b/tests/components/zwave_js/test_diagnostics.py index c7a711d1067..e7d7d9594bd 100644 --- a/tests/components/zwave_js/test_diagnostics.py +++ b/tests/components/zwave_js/test_diagnostics.py @@ -122,7 +122,7 @@ async def test_device_diagnostics_missing_primary_value( integration, hass_client: ClientSessionGenerator, ) -> None: - """Test that the device diagnostics handles an entity with a missing primary value.""" + """Test that device diagnostics handles an entity with a missing primary value.""" dev_reg = async_get_dev_reg(hass) device = dev_reg.async_get_device({get_device_id(client.driver, multisensor_6)}) assert device diff --git a/tests/components/zwave_js/test_discovery.py b/tests/components/zwave_js/test_discovery.py index 1840e4d7980..1c4a69d32e3 100644 --- a/tests/components/zwave_js/test_discovery.py +++ b/tests/components/zwave_js/test_discovery.py @@ -1,6 +1,19 @@ -"""Test discovery of entities for device-specific schemas for the Z-Wave JS integration.""" +"""Test entity discovery for device-specific schemas for the Z-Wave JS integration.""" import pytest +from homeassistant.components.binary_sensor import DOMAIN as BINARY_SENSOR_DOMAIN +from homeassistant.components.button import DOMAIN as BUTTON_DOMAIN, SERVICE_PRESS +from homeassistant.components.number import ( + ATTR_VALUE, + DOMAIN as NUMBER_DOMAIN, + SERVICE_SET_VALUE, +) +from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN +from homeassistant.components.switch import ( + DOMAIN as SWITCH_DOMAIN, + SERVICE_TURN_OFF, + SERVICE_TURN_ON, +) from homeassistant.components.zwave_js.discovery import ( FirmwareVersionRange, ZWaveDiscoverySchema, @@ -9,6 +22,7 @@ from homeassistant.components.zwave_js.discovery import ( from homeassistant.components.zwave_js.discovery_data_template import ( DynamicCurrentTempClimateDataTemplate, ) +from homeassistant.const import ATTR_ENTITY_ID, STATE_OFF, STATE_UNKNOWN, EntityCategory from homeassistant.core import HomeAssistant from homeassistant.helpers import entity_registry as er @@ -141,3 +155,141 @@ async def test_merten_507801_disabled_enitites( ) assert updated_entry != entry assert updated_entry.disabled is False + + +async def test_zooz_zen72( + hass: HomeAssistant, client, switch_zooz_zen72, integration +) -> None: + """Test that Zooz ZEN72 Indicators are discovered as number entities.""" + ent_reg = er.async_get(hass) + assert len(hass.states.async_entity_ids(NUMBER_DOMAIN)) == 1 + assert len(hass.states.async_entity_ids(BUTTON_DOMAIN)) == 2 # includes ping + entity_id = "number.z_wave_plus_700_series_dimmer_switch_indicator_value" + entry = ent_reg.async_get(entity_id) + assert entry + assert entry.entity_category == EntityCategory.CONFIG + state = hass.states.get(entity_id) + assert state + assert state.state == STATE_UNKNOWN + await hass.services.async_call( + NUMBER_DOMAIN, + SERVICE_SET_VALUE, + { + ATTR_ENTITY_ID: entity_id, + ATTR_VALUE: 5, + }, + blocking=True, + ) + assert len(client.async_send_command.call_args_list) == 1 + args = client.async_send_command.call_args[0][0] + assert args["command"] == "node.set_value" + assert args["nodeId"] == switch_zooz_zen72.node_id + assert args["valueId"] == { + "commandClass": 135, + "endpoint": 0, + "property": "value", + } + assert args["value"] == 5 + + client.async_send_command.reset_mock() + + entity_id = "button.z_wave_plus_700_series_dimmer_switch_identify" + entry = ent_reg.async_get(entity_id) + assert entry + assert entry.entity_category == EntityCategory.CONFIG + await hass.services.async_call( + BUTTON_DOMAIN, + SERVICE_PRESS, + {ATTR_ENTITY_ID: entity_id}, + blocking=True, + ) + assert len(client.async_send_command.call_args_list) == 1 + args = client.async_send_command.call_args[0][0] + assert args["command"] == "node.set_value" + assert args["nodeId"] == switch_zooz_zen72.node_id + assert args["valueId"] == { + "commandClass": 135, + "endpoint": 0, + "property": "identify", + } + assert args["value"] is True + + +async def test_indicator_test( + hass: HomeAssistant, client, indicator_test, integration +) -> None: + """Test that Indicators are discovered properly. + + This test covers indicators that we don't already have device fixtures for. + """ + ent_reg = er.async_get(hass) + assert len(hass.states.async_entity_ids(NUMBER_DOMAIN)) == 0 + assert len(hass.states.async_entity_ids(BUTTON_DOMAIN)) == 1 # only ping + assert len(hass.states.async_entity_ids(BINARY_SENSOR_DOMAIN)) == 1 + assert len(hass.states.async_entity_ids(SENSOR_DOMAIN)) == 2 # include node status + assert len(hass.states.async_entity_ids(SWITCH_DOMAIN)) == 1 + + entity_id = "binary_sensor.this_is_a_fake_device_binary_sensor" + entry = ent_reg.async_get(entity_id) + assert entry + assert entry.entity_category == EntityCategory.DIAGNOSTIC + state = hass.states.get(entity_id) + assert state + assert state.state == STATE_OFF + + client.async_send_command.reset_mock() + + entity_id = "sensor.this_is_a_fake_device_sensor" + entry = ent_reg.async_get(entity_id) + assert entry + assert entry.entity_category == EntityCategory.DIAGNOSTIC + state = hass.states.get(entity_id) + assert state + assert state.state == "0.0" + + client.async_send_command.reset_mock() + + entity_id = "switch.this_is_a_fake_device_switch" + entry = ent_reg.async_get(entity_id) + assert entry + assert entry.entity_category == EntityCategory.CONFIG + state = hass.states.get(entity_id) + assert state + assert state.state == STATE_OFF + await hass.services.async_call( + SWITCH_DOMAIN, + SERVICE_TURN_ON, + {ATTR_ENTITY_ID: entity_id}, + blocking=True, + ) + assert len(client.async_send_command.call_args_list) == 1 + args = client.async_send_command.call_args[0][0] + assert args["command"] == "node.set_value" + assert args["nodeId"] == indicator_test.node_id + assert args["valueId"] == { + "commandClass": 135, + "endpoint": 0, + "property": "Test", + "propertyKey": "Switch", + } + assert args["value"] is True + + client.async_send_command.reset_mock() + + await hass.services.async_call( + SWITCH_DOMAIN, + SERVICE_TURN_OFF, + {ATTR_ENTITY_ID: entity_id}, + blocking=True, + ) + assert len(client.async_send_command.call_args_list) == 1 + args = client.async_send_command.call_args[0][0] + assert args["command"] == "node.set_value" + assert args["nodeId"] == indicator_test.node_id + assert args["valueId"] == { + "commandClass": 135, + "endpoint": 0, + "property": "Test", + "propertyKey": "Switch", + } + assert args["value"] is False diff --git a/tests/components/zwave_js/test_init.py b/tests/components/zwave_js/test_init.py index a6fbebf4b64..45cf23a3f1e 100644 --- a/tests/components/zwave_js/test_init.py +++ b/tests/components/zwave_js/test_init.py @@ -101,7 +101,7 @@ async def test_disabled_statistics(hass: HomeAssistant, client) -> None: async def test_noop_statistics(hass: HomeAssistant, client) -> None: - """Test that we don't make any statistics calls if user hasn't provided preference.""" + """Test that we don't make statistics calls if user hasn't set preference.""" entry = MockConfigEntry(domain="zwave_js", data={"url": "ws://test.org"}) entry.add_to_hass(hass) @@ -1332,7 +1332,7 @@ async def test_node_model_change( async def test_disabled_node_status_entity_on_node_replaced( hass: HomeAssistant, zp3111_state, zp3111, client, integration ) -> None: - """Test that when a node replacement event is received the node status sensor is removed.""" + """Test when node replacement event is received, node status sensor is removed.""" node_status_entity = "sensor.4_in_1_sensor_node_status" state = hass.states.get(node_status_entity) assert state diff --git a/tests/components/zwave_js/test_sensor.py b/tests/components/zwave_js/test_sensor.py index d38466539e9..6056d639181 100644 --- a/tests/components/zwave_js/test_sensor.py +++ b/tests/components/zwave_js/test_sensor.py @@ -46,7 +46,6 @@ from .common import ( ENERGY_SENSOR, HUMIDITY_SENSOR, ID_LOCK_CONFIG_PARAMETER_SENSOR, - INDICATOR_SENSOR, METER_ENERGY_SENSOR, NOTIFICATION_MOTION_SENSOR, POWER_SENSOR, @@ -218,18 +217,6 @@ async def test_disabled_notification_sensor( assert state.state == STATE_UNKNOWN -async def test_disabled_indcator_sensor( - hass: HomeAssistant, climate_radio_thermostat_ct100_plus, integration -) -> None: - """Test sensor is created from Indicator CC and is disabled.""" - ent_reg = er.async_get(hass) - entity_entry = ent_reg.async_get(INDICATOR_SENSOR) - - assert entity_entry - assert entity_entry.disabled - assert entity_entry.disabled_by is er.RegistryEntryDisabler.INTEGRATION - - async def test_config_parameter_sensor( hass: HomeAssistant, lock_id_lock_as_id150, integration ) -> None: