diff --git a/test/fixtures/wallmote_central_scene_state.json b/test/fixtures/wallmote_central_scene_state.json index ba58db615..bcd775495 100644 --- a/test/fixtures/wallmote_central_scene_state.json +++ b/test/fixtures/wallmote_central_scene_state.json @@ -67,7 +67,11 @@ "inclusion": "To add the ZP3111 to the Z-Wave network (inclusion), place the Z-Wave primary controller into inclusion mode. Press the Program Switch of ZP3111 for sending the NIF. After sending NIF, Z-Wave will send the auto inclusion, otherwise, ZP3111 will go to sleep after 20 seconds.", "exclusion": "To remove the ZP3111 from the Z-Wave network (exclusion), place the Z-Wave primary controller into \u201cexclusion\u201d mode, and following its instruction to delete the ZP3111 to the controller. Press the Program Switch of ZP3111 once to be excluded.", "reset": "Remove cover to trigged tamper switch, LED flash once & send out Alarm Report. Press Program Switch 10 times within 10 seconds, ZP3111 will send the \u201cDevice Reset Locally Notification\u201d command and reset to the factory default. (Remark: This is to be used only in the case of primary controller being inoperable or otherwise unavailable.)", - "manual": "https://products.z-wavealliance.org/ProductManual/File?folder=&filename=MarketCertificationFiles/2479/ZP3111-5_R2_20170316.pdf" + "manual": "https://products.z-wavealliance.org/ProductManual/File?folder=&filename=MarketCertificationFiles/2479/ZP3111-5_R2_20170316.pdf", + "comments": { + "level": "info", + "text": "test" + } }, "isEmbedded": true }, diff --git a/test/model/test_node.py b/test/model/test_node.py index 718887c86..1e6502f96 100644 --- a/test/model/test_node.py +++ b/test/model/test_node.py @@ -102,7 +102,9 @@ async def test_highest_security_value(lock_schlage_be469, ring_keypad): assert ring_keypad.highest_security_class is None -async def test_device_config(wallmote_central_scene): +async def test_device_config( + wallmote_central_scene, climate_radio_thermostat_ct100_plus +): """Test a device config.""" node: node_pkg.Node = wallmote_central_scene @@ -143,10 +145,13 @@ async def test_device_config(wallmote_central_scene): "https://products.z-wavealliance.org/ProductManual/File?folder=&filename=MarketCertificationFiles/2479/ZP3111-5_R2_20170316.pdf" ) assert device_config.metadata.wakeup is None + assert device_config.metadata.comments == [{"level": "info", "text": "test"}] assert device_config.associations == {} assert device_config.param_information == {"_map": {}} assert device_config.supports_zwave_plus is None + assert climate_radio_thermostat_ct100_plus.device_config.metadata.comments == [] + async def test_unknown_values(cover_qubino_shutter): """Test that values that are unknown return as None.""" diff --git a/zwave_js_server/model/device_config.py b/zwave_js_server/model/device_config.py index 87fe3396d..116a15a96 100644 --- a/zwave_js_server/model/device_config.py +++ b/zwave_js_server/model/device_config.py @@ -3,7 +3,7 @@ https://zwave-js.github.io/node-zwave-js/#/api/node?id=deviceconfig """ -from typing import Dict, List, Optional, TypedDict +from typing import Dict, List, Literal, Optional, TypedDict, Union class DeviceDeviceDataType(TypedDict, total=False): @@ -56,6 +56,15 @@ def max(self) -> Optional[str]: return self.data.get("max") +class CommentDataType(TypedDict): + """Represent a device config's comment data dict type.""" + + # See PR for suggested meanings of each level: + # https://github.com/zwave-js/node-zwave-js/pull/3947 + level: Literal["info", "warning", "error"] + text: str + + class DeviceMetadataDataType(TypedDict, total=False): """Represent a device metadata data dict type.""" @@ -64,6 +73,7 @@ class DeviceMetadataDataType(TypedDict, total=False): exclusion: str reset: str manual: str + comments: Union[CommentDataType, List[CommentDataType]] class DeviceMetadata: @@ -98,6 +108,14 @@ def manual(self) -> Optional[str]: """Return manual instructions.""" return self.data.get("manual") + @property + def comments(self) -> List[CommentDataType]: + """Return list of comments about device.""" + comments = self.data.get("comments", []) + if isinstance(comments, dict): + return [comments] + return comments + class DeviceConfigDataType(TypedDict, total=False): """Represent a device config data dict type."""