Skip to content

Commit 049a306

Browse files
committed
Added reply test cases, added keypad data
1 parent a8667c9 commit 049a306

5 files changed

Lines changed: 140 additions & 13 deletions

File tree

osdp/_bus.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ def send_command_and_receive_reply(data: bytearray, command: Command, device: De
137137
raise
138138
data.extend(command_data)
139139

140-
log.debug("Raw write data: %s", command_data.hex())
140+
log.debug("Raw command data: %s", command_data.hex())
141141

142142
self._connection.write(bytes(data))
143143

@@ -154,7 +154,7 @@ def send_command_and_receive_reply(data: bytearray, command: Command, device: De
154154

155155
log.debug("Raw reply data: %s", reply_buffer.hex())
156156

157-
return Reply.parse(reply_buffer, self.id, command, device)
157+
return Reply.parse(bytes(reply_buffer), self.id, command, device)
158158

159159
def extract_message_length(self, reply_buffer: bytearray) -> int:
160160
return int.from_bytes(bytes(reply_buffer[2:3]), byteorder='little')

osdp/_control_panel.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,9 @@ def on_reply_received(self, reply: Reply):
117117
elif reply.type==ReplyType.RawReaderData:
118118
self.on_raw_card_data_reply_received(reply.address, RawCardData.parse_data(reply))
119119

120+
elif reply.type==ReplyType.KeypadData:
121+
self.on_keypad_data_reply_received(reply.address, KeypadData.parse_data(reply))
122+
120123

121124
def on_nak_reply_received(self, address: int, nak: Nak):
122125
log.debug("%s < Nak received %s", address, nak)
@@ -139,4 +142,5 @@ def on_formatted_reader_data_reply_received(self, address: int, formatted_reader
139142
def on_raw_card_data_reply_received(self, address: int, raw_card_data: RawCardData):
140143
log.debug("%s < Raw reader data received %s", address, raw_card_data)
141144

142-
145+
def on_keypad_data_reply_received(self, address: int, keypad_data: KeypadData):
146+
log.debug("%s < Keypad data received %s", address, keypad_data)

osdp/_reply.py

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,10 @@ class Reply(Message):
1313
REPLY_TYPE_INDEX = 5
1414
MAC_SIZE = 4
1515
SecureSessionMessages = [
16-
SecurityBlockType.CommandMessageWithNoDataSecurity,
17-
SecurityBlockType.ReplyMessageWithNoDataSecurity,
18-
SecurityBlockType.CommandMessageWithDataSecurity,
19-
SecurityBlockType.ReplyMessageWithDataSecurity
16+
SecurityBlockType.CommandMessageWithNoDataSecurity.value,
17+
SecurityBlockType.ReplyMessageWithNoDataSecurity.value,
18+
SecurityBlockType.CommandMessageWithDataSecurity.value,
19+
SecurityBlockType.ReplyMessageWithDataSecurity.value
2020
]
2121

2222
def __init__(self, data: bytes, connection_id: UUID, issuing_command: Command, device: Device):
@@ -29,19 +29,19 @@ def __init__(self, data: bytes, connection_id: UUID, issuing_command: Command, d
2929
is_secure_control_block_present: bool = (data[4] & 0x08)!=0
3030
secure_block_size: int = (data[5] & 0xFF) if is_secure_control_block_present else 0
3131
self._security_block_type = (data[6] & 0xFF) if is_secure_control_block_present else 0
32-
self._secure_block_data = data[(self.REPLY_MESSAGE_HEADER_SIZE + 2):][:(secure_block_size-2)]
32+
self._secure_block_data = data[(self.REPLY_MESSAGE_HEADER_SIZE + 2):][:(secure_block_size-2)] if is_secure_control_block_present else b''
3333

3434
mac_size: int = self.MAC_SIZE if self.is_secure_message else 0
3535
message_length: int = len(data) - (reply_message_footer_size + mac_size)
3636

3737
self._mac = data[message_length:][:mac_size]
3838
self._type = ReplyType(data[self.REPLY_TYPE_INDEX + secure_block_size] & 0xFF)
3939

40-
data_start: int = self.REPLY_MESSAGE_HEADER_SIZE + secure_block_size
40+
data_start: int = self.REPLY_MESSAGE_HEADER_SIZE + secure_block_size + 1
4141
data_end: int = - reply_message_footer_size - mac_size
4242
self._extract_reply_data = data[data_start:data_end]
4343

44-
if SecurityBlockType(self.security_block_type)==SecurityBlockType.ReplyMessageWithDataSecurity:
44+
if self.security_block_type==SecurityBlockType.ReplyMessageWithDataSecurity.value:
4545
self._extract_reply_data = self.decrypt_data(device);
4646

4747
if is_using_crc:
@@ -92,10 +92,9 @@ def message_for_mac_generation(self) -> bytes:
9292

9393
@property
9494
def is_secure_message(self) -> bool:
95-
return SecurityBlockType(self.security_block_type) in self.SecureSessionMessages
95+
return self.security_block_type in self.SecureSessionMessages
9696

9797
@property
98-
@abstractmethod
9998
def reply_code(self) -> int:
10099
pass
101100

@@ -157,6 +156,7 @@ def decrypt_data(self, device: Device) -> bytes:
157156

158157
class AckReply(Reply):
159158

159+
@property
160160
def reply_code(self) -> int:
161161
return 0x40
162162

@@ -172,6 +172,7 @@ class UnknownReply(Reply):
172172
def __init__(self, data: bytes, connection_id: UUID, issuing_command: Command, device: Device):
173173
super().__init__(data, connection_id, issuing_command, device)
174174

175+
@property
175176
def reply_code(self) -> int:
176177
return self.type.value
177178

osdp/_types.py

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -420,7 +420,29 @@ def parse_data(reply) -> Nak:
420420
return RawCardData(reader_number, format_code, bit_count, data)
421421

422422
def __repr__(self):
423-
return "Reader Number: {0}\n Format Code: {1}\n Bit Count: {2}\n Data: {3}".format(self.reader_number, self.format_code.name, self.bit_count, self.data.hex())
423+
return "Reader Number: {0}\n Format Code: {1}\n Bit Count: {2}\n Data: {3}".format(self.reader_number, self.format_code.name, self.bit_count, self.data.hex().upper())
424+
425+
class KeypadData:
426+
427+
def __init__(self, reader_number: int, bit_count: int, data: bytes):
428+
self.reader_number = reader_number
429+
self.bit_count = bit_count
430+
self.data = data
431+
432+
@staticmethod
433+
def parse_data(reply) -> Nak:
434+
data = reply.extract_reply_data
435+
if len(data)<2:
436+
raise ValueError("Invalid size for the data")
437+
438+
reader_number = data[0]
439+
bit_count = int.from_bytes(data[1:2], byteorder='little')
440+
data = data[2:]
441+
return KeypadData(reader_number, bit_count, data)
442+
443+
def __repr__(self):
444+
return "Reader Number: {0}\n Bit Count: {1}\n Data: {2}".format(self.reader_number, self.bit_count, self.data.hex().upper())
445+
424446

425447
class DataEvent(Event):
426448

tests/test_reply.py

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
#!/usr/bin/env python
2+
# -*- coding: utf-8 -*-
3+
4+
"""Tests for OSDP Replies"""
5+
6+
import os
7+
import sys
8+
import unittest
9+
import datetime
10+
from uuid import UUID, uuid4
11+
12+
sys.path.insert(0, os.path.abspath('..'))
13+
from osdp import *
14+
15+
16+
class CommandTestCase(unittest.TestCase):
17+
18+
"""Test commands for OSDP Python Module."""
19+
20+
def setUp(self):
21+
"""Setup."""
22+
23+
def tearDown(self):
24+
"""Teardown."""
25+
26+
def test_poll_reply_no_data_checksum(self):
27+
bus_id = uuid4()
28+
device = Device(address=0x7F, use_crc=False, use_secure_channel=False)
29+
30+
device.message_control.increment_sequence(device.message_control.sequence)
31+
self.assertEqual(device.message_control.sequence, 1)
32+
33+
command = PollCommand(address=0x7F)
34+
data = bytes.fromhex('53 FF 07 00 01 40 66')
35+
reply = Reply.parse(data, bus_id, command, device)
36+
self.assertEqual(reply.type, ReplyType.Ack)
37+
self.assertEqual(reply.extract_reply_data, b'')
38+
39+
message = reply.build_reply(address=0x7F, control=device.message_control).hex().upper()
40+
self.assertEqual(message, data.hex().upper())
41+
42+
device.message_control.increment_sequence(device.message_control.sequence)
43+
self.assertEqual(device.message_control.sequence, 2)
44+
45+
device.message_control.increment_sequence(device.message_control.sequence)
46+
self.assertEqual(device.message_control.sequence, 3)
47+
48+
def test_poll_reply_card_data_checksum(self):
49+
bus_id = uuid4()
50+
device = Device(address=0x7F, use_crc=False, use_secure_channel=False)
51+
52+
device.message_control.increment_sequence(device.message_control.sequence)
53+
self.assertEqual(device.message_control.sequence, 1)
54+
55+
device.message_control.increment_sequence(device.message_control.sequence)
56+
self.assertEqual(device.message_control.sequence, 2)
57+
58+
command = PollCommand(address=0x7F)
59+
data = bytes.fromhex('53 FF 0F 00 02 50 FF 01 1A 00 CD 22 C7 16 67')
60+
reply = Reply.parse(data, bus_id, command, device)
61+
self.assertEqual(reply.type, ReplyType.RawReaderData)
62+
self.assertEqual(reply.extract_reply_data.hex().upper(), 'FF011A00CD22C716')
63+
64+
card_data = RawCardData.parse_data(reply)
65+
self.assertEqual(card_data.data.hex().upper(), 'CD22C716')
66+
67+
message = reply.build_reply(address=0x7F, control=device.message_control).hex().upper()
68+
self.assertEqual(message, data.hex().upper())
69+
70+
device.message_control.increment_sequence(device.message_control.sequence)
71+
self.assertEqual(device.message_control.sequence, 3)
72+
73+
def test_poll_reply_key_data_checksum(self):
74+
bus_id = uuid4()
75+
device = Device(address=0x7F, use_crc=False, use_secure_channel=False)
76+
77+
device.message_control.increment_sequence(device.message_control.sequence)
78+
self.assertEqual(device.message_control.sequence, 1)
79+
80+
device.message_control.increment_sequence(device.message_control.sequence)
81+
self.assertEqual(device.message_control.sequence, 2)
82+
83+
command = PollCommand(address=0x7F)
84+
data = bytes.fromhex('53 FF 0D 00 02 53 FF 04 31 32 33 34 7F')
85+
reply = Reply.parse(data, bus_id, command, device)
86+
self.assertEqual(reply.type, ReplyType.KeypadData)
87+
self.assertEqual(reply.extract_reply_data.hex().upper(), 'FF0431323334')
88+
89+
keypad_data = KeypadData.parse_data(reply)
90+
self.assertEqual(keypad_data.data.hex().upper(), '31323334')
91+
92+
message = reply.build_reply(address=0x7F, control=device.message_control).hex().upper()
93+
self.assertEqual(message, data.hex().upper())
94+
95+
device.message_control.increment_sequence(device.message_control.sequence)
96+
self.assertEqual(device.message_control.sequence, 3)
97+
98+
99+
if __name__ == '__main__':
100+
unittest.main()

0 commit comments

Comments
 (0)