Skip to content

Commit 8135d43

Browse files
authored
Handle Battery CC notifications (#1365)
* Handle Battery CC notification and use enums elsewhere * revert some changes * revert changes
1 parent 67c91fd commit 8135d43

File tree

4 files changed

+92
-12
lines changed

4 files changed

+92
-12
lines changed

test/model/test_node.py

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,18 @@
2424
SupervisionStatus,
2525
Weekday,
2626
)
27+
from zwave_js_server.const.command_class.battery import BatteryReplacementStatus
2728
from zwave_js_server.const.command_class.entry_control import (
2829
EntryControlDataType,
2930
EntryControlEventType,
3031
)
3132
from zwave_js_server.const.command_class.multilevel_switch import (
3233
MultilevelSwitchCommand,
3334
)
35+
from zwave_js_server.const.command_class.notification import (
36+
AccessControlNotificationEvent,
37+
NotificationType,
38+
)
3439
from zwave_js_server.const.command_class.power_level import PowerLevelTestStatus
3540
from zwave_js_server.event import Event
3641
from zwave_js_server.exceptions import (
@@ -1039,8 +1044,11 @@ async def test_notification(lock_schlage_be469: node_pkg.Node):
10391044
assert event.data["notification"].command_class == CommandClass.NOTIFICATION
10401045
assert event.data["notification"].node_id == 23
10411046
assert event.data["notification"].endpoint_idx == 0
1042-
assert event.data["notification"].type_ == 6
1043-
assert event.data["notification"].event == 5
1047+
assert event.data["notification"].type_ == NotificationType.ACCESS_CONTROL
1048+
assert (
1049+
event.data["notification"].event
1050+
== AccessControlNotificationEvent.KEYPAD_LOCK_OPERATION
1051+
)
10441052
assert event.data["notification"].label == "Access Control"
10451053
assert event.data["notification"].event_label == "Keypad lock operation"
10461054
assert event.data["notification"].parameters == {"userId": 1}
@@ -1053,7 +1061,7 @@ async def test_notification(lock_schlage_be469: node_pkg.Node):
10531061
"event": "notification",
10541062
"nodeId": 23,
10551063
"endpointIndex": 0,
1056-
"ccId": CommandClass.POWERLEVEL.value,
1064+
"ccId": 115,
10571065
"args": {"testNodeId": 1, "status": 0, "acknowledgedFrames": 2},
10581066
},
10591067
)
@@ -1074,7 +1082,7 @@ async def test_notification(lock_schlage_be469: node_pkg.Node):
10741082
"event": "notification",
10751083
"nodeId": 23,
10761084
"endpointIndex": 0,
1077-
"ccId": CommandClass.SWITCH_MULTILEVEL.value,
1085+
"ccId": 38,
10781086
"args": {"direction": "up", "eventType": 4, "eventTypeLabel": "c"},
10791087
},
10801088
)
@@ -1098,7 +1106,7 @@ async def test_notification(lock_schlage_be469: node_pkg.Node):
10981106
"event": "notification",
10991107
"nodeId": 23,
11001108
"endpointIndex": 0,
1101-
"ccId": CommandClass.SWITCH_MULTILEVEL.value,
1109+
"ccId": 38,
11021110
"args": {"eventType": 4, "eventTypeLabel": "c"},
11031111
},
11041112
)
@@ -1114,6 +1122,29 @@ async def test_notification(lock_schlage_be469: node_pkg.Node):
11141122
)
11151123
assert event.data["notification"].event_type_label == "c"
11161124

1125+
# Validate that Battery CC notification event is received as expected
1126+
event = Event(
1127+
type="notification",
1128+
data={
1129+
"source": "node",
1130+
"event": "notification",
1131+
"nodeId": 23,
1132+
"ccId": 128,
1133+
"endpointIndex": 0,
1134+
"args": {
1135+
"eventType": "battery low",
1136+
"urgency": 1,
1137+
},
1138+
},
1139+
)
1140+
1141+
node.handle_notification(event)
1142+
assert event.data["notification"].command_class == CommandClass.BATTERY
1143+
assert event.data["notification"].node_id == 23
1144+
assert event.data["notification"].endpoint_idx == 0
1145+
assert event.data["notification"].event_type == "battery low"
1146+
assert event.data["notification"].urgency == BatteryReplacementStatus.SOON
1147+
11171148

11181149
async def test_notification_unknown(lock_schlage_be469: node_pkg.Node, caplog):
11191150
"""Test unrecognized command class notification events."""
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
"""Constants for the Battery CC."""
2+
3+
from __future__ import annotations
4+
5+
from enum import IntEnum
6+
7+
8+
class BatteryReplacementStatus(IntEnum):
9+
"""Enum with all (known/used) Z-Wave Battery Replacement Statuses."""
10+
11+
# https://github.com/zwave-js/node-zwave-js/blob/master/packages/cc/src/lib/_Types.ts#L328
12+
NO = 0
13+
SOON = 1
14+
NOW = 2

zwave_js_server/model/node/__init__.py

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
from ..device_config import DeviceConfig
2626
from ..endpoint import Endpoint, EndpointDataType
2727
from ..notification import (
28+
BatteryNotification,
29+
BatteryNotificationDataType,
2830
EntryControlNotification,
2931
EntryControlNotificationDataType,
3032
MultilevelSwitchNotification,
@@ -1105,22 +1107,26 @@ def handle_metadata_updated(self, event: Event) -> None:
11051107
def handle_notification(self, event: Event) -> None:
11061108
"""Process a node notification event."""
11071109
match command_class := CommandClass(event.data["ccId"]):
1108-
case CommandClass.NOTIFICATION:
1109-
event.data["notification"] = NotificationNotification(
1110-
self, cast(NotificationNotificationDataType, event.data)
1111-
)
1112-
case CommandClass.SWITCH_MULTILEVEL:
1113-
event.data["notification"] = MultilevelSwitchNotification(
1114-
self, cast(MultilevelSwitchNotificationDataType, event.data)
1110+
case CommandClass.BATTERY:
1111+
event.data["notification"] = BatteryNotification(
1112+
self, cast(BatteryNotificationDataType, event.data)
11151113
)
11161114
case CommandClass.ENTRY_CONTROL:
11171115
event.data["notification"] = EntryControlNotification(
11181116
self, cast(EntryControlNotificationDataType, event.data)
11191117
)
1118+
case CommandClass.NOTIFICATION:
1119+
event.data["notification"] = NotificationNotification(
1120+
self, cast(NotificationNotificationDataType, event.data)
1121+
)
11201122
case CommandClass.POWERLEVEL:
11211123
event.data["notification"] = PowerLevelNotification(
11221124
self, cast(PowerLevelNotificationDataType, event.data)
11231125
)
1126+
case CommandClass.SWITCH_MULTILEVEL:
1127+
event.data["notification"] = MultilevelSwitchNotification(
1128+
self, cast(MultilevelSwitchNotificationDataType, event.data)
1129+
)
11241130
case _:
11251131
_LOGGER.info(
11261132
"Unhandled notification command class: %s", command_class.name

zwave_js_server/model/notification.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from dataclasses import dataclass, field
1010
from typing import TYPE_CHECKING, Any, Literal, TypedDict
1111

12+
from ..const.command_class.battery import BatteryReplacementStatus
1213
from ..const.command_class.multilevel_switch import MultilevelSwitchCommand
1314
from ..const.command_class.power_level import PowerLevelTestStatus
1415
from ..util.helpers import parse_buffer
@@ -44,6 +45,34 @@ def __post_init__(self) -> None:
4445
self.command_class = self.data["ccId"]
4546

4647

48+
class BatteryNotificationArgsDataType(TypedDict):
49+
"""Represent args for a Battery CC notification event data dict type."""
50+
51+
eventType: Literal["battery low"] # required
52+
urgency: int # required
53+
54+
55+
class BatteryNotificationDataType(BaseNotificationDataType):
56+
"""Represent a Battery CC notification event data dict type."""
57+
58+
args: BatteryNotificationArgsDataType # required
59+
60+
61+
@dataclass
62+
class BatteryNotification(BaseNotification):
63+
"""Model for a Zwave Node's Battery CC notification event."""
64+
65+
data: BatteryNotificationDataType = field(repr=False)
66+
event_type: str = field(init=False)
67+
urgency: BatteryReplacementStatus = field(init=False)
68+
69+
def __post_init__(self) -> None:
70+
"""Post initialize."""
71+
super().__post_init__()
72+
self.event_type = self.data["args"]["eventType"]
73+
self.urgency = BatteryReplacementStatus(self.data["args"]["urgency"])
74+
75+
4776
class EntryControlNotificationArgsDataType(TypedDict, total=False):
4877
"""Represent args for a Entry Control CC notification event data dict type."""
4978

0 commit comments

Comments
 (0)