Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 3 additions & 6 deletions tests/test_application.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,6 @@ def zdo_packet(cluster_id: int, data: bytes, src: t.NWK = DEVICE_NWK) -> t.Zigbe
src_ep=t.uint8_t(0),
dst=t.AddrModeAddress(addr_mode=t.AddrMode.NWK, address=t.NWK(0x0000)),
dst_ep=t.uint8_t(0),
tsn=t.uint8_t(data[0]),
profile_id=t.uint16_t(0x0000),
cluster_id=t.uint16_t(cluster_id),
data=t.SerializableBytes(data),
Expand All @@ -84,7 +83,6 @@ def zdo_packet(cluster_id: int, data: bytes, src: t.NWK = DEVICE_NWK) -> t.Zigbe
def aps_packet(
dst: t.AddrModeAddress,
*,
tsn: int = 33,
src_ep: int = 1,
dst_ep: int = 1,
tx_options: t.TransmitOptions = t.TransmitOptions.NONE,
Expand All @@ -95,7 +93,6 @@ def aps_packet(
src_ep=t.uint8_t(src_ep),
dst=dst,
dst_ep=t.uint8_t(dst_ep),
tsn=t.uint8_t(tsn),
profile_id=t.uint16_t(0x0104),
cluster_id=t.uint16_t(0x0006),
data=t.SerializableBytes(data),
Expand Down Expand Up @@ -668,7 +665,7 @@ async def test_send_packet_unicast(
assert not send.aps_encryption
assert not send.sleepy_destination
assert send.profile_id == 0x0104
assert send.aps_seq == 33
assert send.aps_seq == 1
assert send.radius == 30
assert send.priority == 0
assert bytes(send.asdu) == b"\x01\x02\x03"
Expand Down Expand Up @@ -750,7 +747,7 @@ async def test_send_packet_broadcast(
send = server.sent(p.SendBroadcast)[-1]
assert send.destination == t.NWK(0xFFFC)
assert send.dst_ep == 255
assert send.aps_seq == 33
assert send.aps_seq == 1
assert bytes(send.asdu) == b"\x01\x02\x03"


Expand All @@ -766,7 +763,7 @@ async def test_send_packet_groupcast(

send = server.sent(p.SendGroupcast)[-1]
assert send.group_id == 0x0002
assert send.aps_seq == 33
assert send.aps_seq == 1
assert bytes(send.asdu) == b"\x01\x02\x03"


Expand Down
6 changes: 1 addition & 5 deletions tests/test_legacy.py
Original file line number Diff line number Diff line change
Expand Up @@ -577,7 +577,6 @@ async def test_legacy_send_packet(
src_ep=t.uint8_t(1),
dst=dst,
dst_ep=t.uint8_t(1),
tsn=t.uint8_t(33),
profile_id=t.uint16_t(0x0104),
cluster_id=t.uint16_t(0x0006),
data=t.SerializableBytes(b"\x01\x02\x03"),
Expand All @@ -587,7 +586,7 @@ async def test_legacy_send_packet(

request = legacy_server.sent(commands.SendAps)[-1]
assert request.data == b"\x01\x02\x03"
assert request.aps_seq == 33
assert request.aps_seq == 1
assert request.radius == 30
for field, value in expected.items():
assert getattr(request, field) == value
Expand All @@ -609,7 +608,6 @@ async def fail(command: commands.SendAps, request_id: int) -> commands.Status:
src_ep=t.uint8_t(1),
dst=t.AddrModeAddress(addr_mode=t.AddrMode.NWK, address=DEVICE_NWK),
dst_ep=t.uint8_t(1),
tsn=t.uint8_t(34),
profile_id=t.uint16_t(0x0104),
cluster_id=t.uint16_t(0x0006),
data=t.SerializableBytes(b"\x04"),
Expand Down Expand Up @@ -638,7 +636,6 @@ async def fail_confirm(
src_ep=t.uint8_t(1),
dst=t.AddrModeAddress(addr_mode=t.AddrMode.NWK, address=DEVICE_NWK),
dst_ep=t.uint8_t(1),
tsn=t.uint8_t(35),
profile_id=t.uint16_t(0x0104),
cluster_id=t.uint16_t(0x0006),
data=t.SerializableBytes(b"\x05"),
Expand All @@ -663,7 +660,6 @@ async def fail_ack(command: commands.SendAps, request_id: int) -> commands.Statu
src_ep=t.uint8_t(1),
dst=t.AddrModeAddress(addr_mode=t.AddrMode.NWK, address=DEVICE_NWK),
dst_ep=t.uint8_t(1),
tsn=t.uint8_t(36),
profile_id=t.uint16_t(0x0104),
cluster_id=t.uint16_t(0x0006),
data=t.SerializableBytes(b"\x06"),
Expand Down
16 changes: 13 additions & 3 deletions zigpy_ziggurat/zigbee/application.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ def __init__(self, config: dict[str, Any]) -> None:
super().__init__(config)
self._api: ZigguratApi | None = None
self._start_time: datetime | None = None
self._aps_counter = 0

async def connect(self) -> None:
# The device path is either the WebSocket URL of a ziggurat server or the
Expand Down Expand Up @@ -798,6 +799,11 @@ def _handle_received_aps_command(self, command: p.ReceivedAps) -> None:
)
self.packet_received(packet)

def _next_aps_counter(self) -> int:
"""Allocate the APS counter for the next outgoing frame."""
self._aps_counter = (self._aps_counter + 1) % 256
return self._aps_counter

async def send_packet(self, packet: t.ZigbeePacket) -> None:
dst = packet.dst
assert dst is not None and dst.address is not None
Expand Down Expand Up @@ -834,14 +840,18 @@ async def send_packet(self, packet: t.ZigbeePacket) -> None:

send: p.SendUnicast | p.SendBroadcast | p.SendGroupcast
async with self._limit_concurrency(priority=packet.priority):
# Allocated here rather than above the semaphore so counters advance in the
# order the server receives the frames
aps_seq = self._next_aps_counter()

if dst.addr_mode == t.AddrMode.Group:
assert destination is not None
send = p.SendGroupcast.build(
group_id=int(destination),
profile_id=packet.profile_id,
cluster_id=packet.cluster_id or 0x0000,
src_ep=packet.src_ep or 0,
aps_seq=packet.tsn,
aps_seq=aps_seq,
radius=radius,
priority=priority,
asdu=asdu,
Expand All @@ -854,7 +864,7 @@ async def send_packet(self, packet: t.ZigbeePacket) -> None:
cluster_id=packet.cluster_id or 0x0000,
src_ep=packet.src_ep or 0,
dst_ep=packet.dst_ep or 0,
aps_seq=packet.tsn,
aps_seq=aps_seq,
radius=radius,
priority=priority,
asdu=asdu,
Expand Down Expand Up @@ -899,7 +909,7 @@ async def send_packet(self, packet: t.ZigbeePacket) -> None:
cluster_id=packet.cluster_id or 0x0000,
src_ep=packet.src_ep or 0,
dst_ep=packet.dst_ep or 0,
aps_seq=packet.tsn,
aps_seq=aps_seq,
radius=radius,
priority=priority,
route_control=route_control,
Expand Down