Skip to content

Commit b7c3f68

Browse files
authored
Handle node.isSecure = unknown (#383)
* Handle node.isSecure = unknown * improve logic * fix typing
1 parent b585072 commit b7c3f68

File tree

5 files changed

+156
-3
lines changed

5 files changed

+156
-3
lines changed

test/conftest.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,12 @@ def ring_keypad_state_fixture():
102102
return json.loads(load_fixture("ring_keypad_state.json"))
103103

104104

105+
@pytest.fixture(name="is_secure_unknown_state", scope="session")
106+
def is_secure_unknown_state_fixture():
107+
"""Load the isSecure = `unknown` node state fixture data."""
108+
return json.loads(load_fixture("is_secure_unknown_state.json"))
109+
110+
105111
def create_ws_message(result):
106112
"""Return a mock WSMessage."""
107113
message = Mock(spec_set=WSMessage)
@@ -470,3 +476,11 @@ def invalid_multilevel_sensor_type_fixture(
470476
node = Node(driver.client, deepcopy(invalid_multilevel_sensor_type_state))
471477
driver.controller.nodes[node.node_id] = node
472478
return node
479+
480+
481+
@pytest.fixture(name="is_secure_unknown")
482+
def is_secure_unknown_fixture(driver, is_secure_unknown_state):
483+
"""Mock a node that has inSecure = `unknown`."""
484+
node = Node(driver.client, deepcopy(is_secure_unknown_state))
485+
driver.controller.nodes[node.node_id] = node
486+
return node
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
{
2+
"nodeId": 45,
3+
"index": 0,
4+
"installerIcon": 1536,
5+
"userIcon": 1536,
6+
"status": 0,
7+
"ready": false,
8+
"isListening": true,
9+
"isRouting": true,
10+
"isSecure": "unknown",
11+
"manufacturerId": 798,
12+
"productId": 1,
13+
"productType": 1,
14+
"firmwareVersion": "1.57",
15+
"zwavePlusVersion": 1,
16+
"deviceConfig": {
17+
"filename": "/data/db/devices/0x031e/lzw31-sn.json",
18+
"isEmbedded": true,
19+
"manufacturer": "Inovelli",
20+
"manufacturerId": 798,
21+
"label": "LZW31-SN",
22+
"description": "Red Series Dimmer",
23+
"devices": [{ "productType": 1, "productId": 1 }],
24+
"firmwareVersion": { "min": "0.0", "max": "255.255" },
25+
"paramInformation": { "_map": {} },
26+
"metadata": {
27+
"inclusion": "Start the inclusion process again on your HUB and tap the Configuration Button three (3) times.\\n\\nPlease Note: If this doesn't work, you can check to see if your switch is within Z-Wave Range by holding down the Configuration Button for 5-10 seconds (if it's not within range, the LED Bar will indicate Red and if it is within Range, the LED Bar will indicate Green). If your switch indicates Red, please move the switch closer to the HUB. If your switch indicates Green, please try running an Exclusion to reset your switch",
28+
"exclusion": "To Exclude your dimmer, put your HUB in exclusion mode and press the Configuration Button three (3) times",
29+
"reset": "You may factory reset the switch by holding down the Config Button for twenty (20) or more seconds. The LED Bar will turn Red and blink three (3) times to confirm. \\n\\nHowever, we recommend using a certified Z-Wave controller to remove the device from your network for factory resetting the switch. \\n\\nOnly use either of these procedures in the event that the network primary controller is missing or otherwise inoperable",
30+
"manual": "https://products.z-wavealliance.org/ProductManual/File?folder=&filename=product_documents/4206/LZW31-SN%20Manual%20-%20Inovelli%20Dimmer%20Switch%20(Red%20Series)%20-%2006.28.21.pdf"
31+
}
32+
},
33+
"label": "LZW31-SN",
34+
"interviewAttempts": 0,
35+
"endpoints": [
36+
{
37+
"nodeId": 45,
38+
"index": 0,
39+
"installerIcon": 1536,
40+
"userIcon": 1536,
41+
"deviceClass": {
42+
"basic": { "key": 4, "label": "Routing Slave" },
43+
"generic": { "key": 17, "label": "Multilevel Switch" },
44+
"specific": { "key": 1, "label": "Multilevel Power Switch" },
45+
"mandatorySupportedCCs": [32, 38, 39],
46+
"mandatoryControlledCCs": []
47+
},
48+
"commandClasses": [
49+
{
50+
"id": 38,
51+
"name": "Multilevel Switch",
52+
"version": 2,
53+
"isSecure": false
54+
},
55+
{ "id": 50, "name": "Meter", "version": 3, "isSecure": false },
56+
{
57+
"id": 85,
58+
"name": "Transport Service",
59+
"version": 2,
60+
"isSecure": false
61+
},
62+
{
63+
"id": 89,
64+
"name": "Association Group Information",
65+
"version": 1,
66+
"isSecure": false
67+
},
68+
{
69+
"id": 90,
70+
"name": "Device Reset Locally",
71+
"version": 1,
72+
"isSecure": false
73+
},
74+
{ "id": 91, "name": "Central Scene", "version": 3, "isSecure": false },
75+
{
76+
"id": 94,
77+
"name": "Z-Wave Plus Info",
78+
"version": 2,
79+
"isSecure": false
80+
},
81+
{ "id": 108, "name": "Supervision", "version": 1, "isSecure": false },
82+
{ "id": 112, "name": "Configuration", "version": 1, "isSecure": false },
83+
{
84+
"id": 114,
85+
"name": "Manufacturer Specific",
86+
"version": 2,
87+
"isSecure": false
88+
},
89+
{ "id": 115, "name": "Powerlevel", "version": 1, "isSecure": false },
90+
{ "id": 117, "name": "Protection", "version": 2, "isSecure": false },
91+
{
92+
"id": 122,
93+
"name": "Firmware Update Meta Data",
94+
"version": 4,
95+
"isSecure": false
96+
},
97+
{ "id": 133, "name": "Association", "version": 2, "isSecure": false },
98+
{ "id": 134, "name": "Version", "version": 2, "isSecure": false },
99+
{ "id": 152, "name": "Security", "version": 1, "isSecure": true },
100+
{ "id": 159, "name": "Security 2", "version": 1, "isSecure": true }
101+
]
102+
}
103+
],
104+
"values": [],
105+
"isFrequentListening": false,
106+
"maxDataRate": 100000,
107+
"supportedDataRates": [40000, 100000],
108+
"protocolVersion": 3,
109+
"supportsBeaming": true,
110+
"supportsSecurity": false,
111+
"nodeType": 1,
112+
"zwavePlusNodeType": 0,
113+
"zwavePlusRoleType": 5,
114+
"deviceClass": {
115+
"basic": { "key": 4, "label": "Routing Slave" },
116+
"generic": { "key": 17, "label": "Multilevel Switch" },
117+
"specific": { "key": 1, "label": "Multilevel Power Switch" },
118+
"mandatorySupportedCCs": [32, 38, 39],
119+
"mandatoryControlledCCs": []
120+
},
121+
"interviewStage": "Complete",
122+
"deviceDatabaseUrl": "https://devices.zwave-js.io/?jumpTo=0x031e:0x0001:0x0001:1.57",
123+
"statistics": {
124+
"commandsTX": 0,
125+
"commandsRX": 0,
126+
"commandsDroppedRX": 0,
127+
"commandsDroppedTX": 0,
128+
"timeoutResponse": 0
129+
},
130+
"isControllerNode": false,
131+
"keepAwake": false
132+
}

test/model/test_node.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1456,3 +1456,8 @@ async def test_unknown_event(multisensor_6: node_pkg.Node):
14561456
"""Test that an unknown event type causes an exception."""
14571457
with pytest.raises(KeyError):
14581458
assert multisensor_6.receive_event(Event("unknown_event", {"source": "node"}))
1459+
1460+
1461+
async def test_is_secure_unknown(is_secure_unknown: node_pkg.Node):
1462+
"""Test that a node with isSecure = `unknown` gets handled appropriately."""
1463+
assert not is_secure_unknown.is_secure

zwave_js_server/model/node/__init__.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,9 @@ def supported_data_rates(self) -> List[int]:
152152
@property
153153
def is_secure(self) -> Optional[bool]:
154154
"""Return the is_secure."""
155-
return self.data.get("isSecure")
155+
if (is_secure := self.data.get("isSecure")) == "unknown":
156+
return None
157+
return is_secure
156158

157159
@property
158160
def protocol_version(self) -> Optional[int]:

zwave_js_server/model/node/data_model.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
"""Data model for a Z-Wave JS node."""
2-
from typing import List, Optional, Union
2+
from typing import List, Literal, Optional, Union
33

44
from ...const import TYPING_EXTENSION_FOR_TYPEDDICT_REQUIRED
55
from ..device_class import DeviceClassDataType
@@ -33,7 +33,7 @@ class NodeDataType(TypedDict, total=False):
3333
isRouting: bool
3434
maxDataRate: int
3535
supportedDataRates: List[int]
36-
isSecure: bool
36+
isSecure: Union[bool, Literal["unknown"]]
3737
supportsBeaming: bool
3838
supportsSecurity: bool
3939
protocolVersion: int

0 commit comments

Comments
 (0)