diff --git a/homeassistant/components/zwave_js/discovery.py b/homeassistant/components/zwave_js/discovery.py index 25c342cf87d..7030009f5ad 100644 --- a/homeassistant/components/zwave_js/discovery.py +++ b/homeassistant/components/zwave_js/discovery.py @@ -760,7 +760,7 @@ DISCOVERY_SCHEMAS = [ platform=Platform.SELECT, hint="multilevel_switch", manufacturer_id={0x0084}, - product_id={0x0107, 0x0108, 0x010B, 0x0205}, + product_id={0x0107, 0x0108, 0x0109, 0x010B, 0x0205}, product_type={0x0311, 0x0313, 0x0331, 0x0341, 0x0343}, primary_value=SWITCH_MULTILEVEL_CURRENT_VALUE_SCHEMA, data_template=BaseDiscoverySchemaDataTemplate( diff --git a/tests/components/zwave_js/conftest.py b/tests/components/zwave_js/conftest.py index eef92a7eb0a..f60c0169055 100644 --- a/tests/components/zwave_js/conftest.py +++ b/tests/components/zwave_js/conftest.py @@ -427,6 +427,12 @@ def fortrezz_ssa1_siren_state_fixture() -> dict[str, Any]: return load_json_object_fixture("fortrezz_ssa1_siren_state.json", DOMAIN) +@pytest.fixture(name="fortrezz_ssa2_siren_state", scope="package") +def fortrezz_ssa2_siren_state_fixture() -> dict[str, Any]: + """Load the fortrezz ssa2 siren node state fixture data.""" + return load_json_object_fixture("fortrezz_ssa2_siren_state.json", DOMAIN) + + @pytest.fixture(name="fortrezz_ssa3_siren_state", scope="package") def fortrezz_ssa3_siren_state_fixture() -> dict[str, Any]: """Load the fortrezz ssa3 siren node state fixture data.""" @@ -1218,6 +1224,14 @@ def fortrezz_ssa1_siren_fixture(client, fortrezz_ssa1_siren_state) -> Node: return node +@pytest.fixture(name="fortrezz_ssa2_siren") +def fortrezz_ssa2_siren_fixture(client, fortrezz_ssa2_siren_state) -> Node: + """Mock a fortrezz ssa2 siren node.""" + node = Node(client, copy.deepcopy(fortrezz_ssa2_siren_state)) + client.driver.controller.nodes[node.node_id] = node + return node + + @pytest.fixture(name="fortrezz_ssa3_siren") def fortrezz_ssa3_siren_fixture(client, fortrezz_ssa3_siren_state) -> Node: """Mock a fortrezz ssa3 siren node.""" diff --git a/tests/components/zwave_js/fixtures/fortrezz_ssa2_siren_state.json b/tests/components/zwave_js/fixtures/fortrezz_ssa2_siren_state.json new file mode 100644 index 00000000000..6fc7a89046e --- /dev/null +++ b/tests/components/zwave_js/fixtures/fortrezz_ssa2_siren_state.json @@ -0,0 +1,447 @@ +{ + "nodeId": 30, + "index": 0, + "status": 4, + "ready": true, + "isListening": true, + "isRouting": true, + "isSecure": false, + "manufacturerId": 132, + "productId": 265, + "productType": 785, + "firmwareVersion": "1.9", + "deviceConfig": { + "filename": "/data/db/devices/0x0084/ssa1_ssa2.json", + "isEmbedded": true, + "manufacturer": "FortrezZ LLC", + "manufacturerId": 132, + "label": "SSA1/SSA2", + "description": "Siren and Strobe Alarm", + "devices": [ + { + "productType": 785, + "productId": 267 + }, + { + "productType": 787, + "productId": 264 + }, + { + "productType": 787, + "productId": 267 + }, + { + "productType": 785, + "productId": 265 + }, + { + "productType": 787, + "productId": 265 + } + ], + "firmwareVersion": { + "min": "0.0", + "max": "255.255" + }, + "preferred": false, + "paramInformation": { + "_map": {} + } + }, + "label": "SSA1/SSA2", + "interviewAttempts": 0, + "isFrequentListening": false, + "maxDataRate": 40000, + "supportedDataRates": [40000], + "protocolVersion": 3, + "supportsBeaming": true, + "supportsSecurity": false, + "nodeType": 1, + "deviceClass": { + "basic": { + "key": 4, + "label": "Routing End Node" + }, + "generic": { + "key": 17, + "label": "Multilevel Switch" + }, + "specific": { + "key": 0, + "label": "Unused" + } + }, + "interviewStage": "Complete", + "deviceDatabaseUrl": "https://devices.zwave-js.io/?jumpTo=0x0084:0x0311:0x0109:1.9", + "statistics": { + "commandsTX": 23, + "commandsRX": 0, + "commandsDroppedRX": 0, + "commandsDroppedTX": 0, + "timeoutResponse": 22, + "rtt": 50.7, + "lastSeen": "2025-06-10T03:23:40.329Z" + }, + "highestSecurityClass": -1, + "isControllerNode": false, + "keepAwake": false, + "lastSeen": "2025-06-04T08:00:19.437Z", + "protocol": 0, + "values": [ + { + "endpoint": 0, + "commandClass": 38, + "commandClassName": "Multilevel Switch", + "property": "currentValue", + "propertyName": "currentValue", + "ccVersion": 1, + "metadata": { + "type": "number", + "readable": true, + "writeable": false, + "label": "Current value", + "min": 0, + "max": 99, + "stateful": true, + "secret": false + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 38, + "commandClassName": "Multilevel Switch", + "property": "Up", + "propertyName": "Up", + "ccVersion": 1, + "metadata": { + "type": "boolean", + "readable": false, + "writeable": true, + "label": "Perform a level change (Up)", + "ccSpecific": { + "switchType": 2 + }, + "valueChangeOptions": ["transitionDuration"], + "states": { + "true": "Start", + "false": "Stop" + }, + "stateful": true, + "secret": false + } + }, + { + "endpoint": 0, + "commandClass": 38, + "commandClassName": "Multilevel Switch", + "property": "Down", + "propertyName": "Down", + "ccVersion": 1, + "metadata": { + "type": "boolean", + "readable": false, + "writeable": true, + "label": "Perform a level change (Down)", + "ccSpecific": { + "switchType": 2 + }, + "valueChangeOptions": ["transitionDuration"], + "states": { + "true": "Start", + "false": "Stop" + }, + "stateful": true, + "secret": false + } + }, + { + "endpoint": 0, + "commandClass": 38, + "commandClassName": "Multilevel Switch", + "property": "duration", + "propertyName": "duration", + "ccVersion": 1, + "metadata": { + "type": "duration", + "readable": true, + "writeable": false, + "label": "Remaining duration", + "stateful": true, + "secret": false + } + }, + { + "endpoint": 0, + "commandClass": 38, + "commandClassName": "Multilevel Switch", + "property": "targetValue", + "propertyName": "targetValue", + "ccVersion": 1, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "label": "Target value", + "valueChangeOptions": ["transitionDuration"], + "min": 0, + "max": 99, + "stateful": true, + "secret": false + } + }, + { + "endpoint": 0, + "commandClass": 38, + "commandClassName": "Multilevel Switch", + "property": "restorePrevious", + "propertyName": "restorePrevious", + "ccVersion": 1, + "metadata": { + "type": "boolean", + "readable": false, + "writeable": true, + "label": "Restore previous value", + "states": { + "true": "Restore" + }, + "stateful": true, + "secret": false + } + }, + { + "endpoint": 0, + "commandClass": 112, + "commandClassName": "Configuration", + "property": 1, + "propertyName": "Delay Before Accept of Basic Set Off", + "ccVersion": 1, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "description": "Delay, from the time the siren-strobe turns on", + "label": "Delay Before Accept of Basic Set Off", + "default": 0, + "min": 0, + "max": 255, + "unit": "seconds", + "valueSize": 1, + "format": 1, + "allowManualEntry": true, + "isFromConfig": true + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 113, + "commandClassName": "Notification", + "property": "alarmType", + "propertyName": "alarmType", + "ccVersion": 2, + "metadata": { + "type": "number", + "readable": true, + "writeable": false, + "label": "Alarm Type", + "min": 0, + "max": 255, + "stateful": true, + "secret": false + } + }, + { + "endpoint": 0, + "commandClass": 113, + "commandClassName": "Notification", + "property": "alarmLevel", + "propertyName": "alarmLevel", + "ccVersion": 2, + "metadata": { + "type": "number", + "readable": true, + "writeable": false, + "label": "Alarm Level", + "min": 0, + "max": 255, + "stateful": true, + "secret": false + } + }, + { + "endpoint": 0, + "commandClass": 114, + "commandClassName": "Manufacturer Specific", + "property": "productId", + "propertyName": "productId", + "ccVersion": 1, + "metadata": { + "type": "number", + "readable": true, + "writeable": false, + "label": "Product ID", + "min": 0, + "max": 65535, + "stateful": true, + "secret": false + }, + "value": 265 + }, + { + "endpoint": 0, + "commandClass": 114, + "commandClassName": "Manufacturer Specific", + "property": "productType", + "propertyName": "productType", + "ccVersion": 1, + "metadata": { + "type": "number", + "readable": true, + "writeable": false, + "label": "Product type", + "min": 0, + "max": 65535, + "stateful": true, + "secret": false + }, + "value": 785 + }, + { + "endpoint": 0, + "commandClass": 114, + "commandClassName": "Manufacturer Specific", + "property": "manufacturerId", + "propertyName": "manufacturerId", + "ccVersion": 1, + "metadata": { + "type": "number", + "readable": true, + "writeable": false, + "label": "Manufacturer ID", + "min": 0, + "max": 65535, + "stateful": true, + "secret": false + }, + "value": 132 + }, + { + "endpoint": 0, + "commandClass": 134, + "commandClassName": "Version", + "property": "firmwareVersions", + "propertyName": "firmwareVersions", + "ccVersion": 1, + "metadata": { + "type": "string[]", + "readable": true, + "writeable": false, + "label": "Z-Wave chip firmware versions", + "stateful": true, + "secret": false + }, + "value": ["1.9"] + }, + { + "endpoint": 0, + "commandClass": 134, + "commandClassName": "Version", + "property": "protocolVersion", + "propertyName": "protocolVersion", + "ccVersion": 1, + "metadata": { + "type": "string", + "readable": true, + "writeable": false, + "label": "Z-Wave protocol version", + "stateful": true, + "secret": false + }, + "value": "2.97" + }, + { + "endpoint": 0, + "commandClass": 134, + "commandClassName": "Version", + "property": "libraryType", + "propertyName": "libraryType", + "ccVersion": 1, + "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" + }, + "stateful": true, + "secret": false + }, + "value": 6 + } + ], + "endpoints": [ + { + "nodeId": 30, + "index": 0, + "deviceClass": { + "basic": { + "key": 4, + "label": "Routing End Node" + }, + "generic": { + "key": 17, + "label": "Multilevel Switch" + }, + "specific": { + "key": 0, + "label": "Unused" + } + }, + "commandClasses": [ + { + "id": 38, + "name": "Multilevel Switch", + "version": 1, + "isSecure": false + }, + { + "id": 114, + "name": "Manufacturer Specific", + "version": 1, + "isSecure": false + }, + { + "id": 134, + "name": "Version", + "version": 1, + "isSecure": false + }, + { + "id": 113, + "name": "Notification", + "version": 2, + "isSecure": false + }, + { + "id": 112, + "name": "Configuration", + "version": 1, + "isSecure": false + } + ] + } + ] +} diff --git a/tests/components/zwave_js/test_discovery.py b/tests/components/zwave_js/test_discovery.py index 6a4752d536b..299c003aefe 100644 --- a/tests/components/zwave_js/test_discovery.py +++ b/tests/components/zwave_js/test_discovery.py @@ -157,6 +157,16 @@ async def test_lock_popp_electric_strike_lock_control( assert hass.states.get("select.node_62_current_lock_mode") is not None +async def test_fortrez_ssa2_siren( + hass: HomeAssistant, + client: MagicMock, + fortrezz_ssa2_siren: Node, + integration: MockConfigEntry, +) -> None: + """Test Fortrezz SSA2 siren gets discovered correctly.""" + assert hass.states.get("select.siren_and_strobe_alarm") is not None + + async def test_fortrez_ssa3_siren( hass: HomeAssistant, client, fortrezz_ssa3_siren, integration ) -> None: