Skip to content

Commit 6a4ac32

Browse files
committed
Implement functions for getting/setting device properties
1 parent a5b9059 commit 6a4ac32

File tree

4 files changed

+260
-69
lines changed

4 files changed

+260
-69
lines changed

zbnt/AxiDevice.py

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,14 @@
1919
from .Enums import *
2020

2121
class AxiDevice:
22-
def __init__(self, dev_id, initial_props):
22+
_property_encoding = dict()
23+
24+
def __init__(self, parent, dev_id, initial_props):
2325
self.id = dev_id
2426
self.ports = []
27+
self.client = parent
28+
self.measurement_handler = None
29+
self.valid_properties = list(self._property_encoding)
2530

2631
for prop_id, prop_bytes in initial_props:
2732
if prop_id == Properties.PROP_PORTS:
@@ -31,4 +36,36 @@ def __repr__(self):
3136
return "zbnt.{0}(dev_id={1}, ports={2})".format(self.__class__.__name__, self.id, str(self.ports))
3237

3338
def receive_measurement(self, data):
34-
pass
39+
if self.measurement_handler != None:
40+
self.measurement_handler(self.id, data)
41+
42+
async def set_property(self, prop_id, value):
43+
prop_encoding = self._property_encoding.get(prop_id, None)
44+
45+
if prop_encoding == None:
46+
raise Exception("Property {0} is invalid for {1}".format(prop_id, self.__class__.__name__))
47+
48+
encoder, _ = prop_encoding
49+
50+
if encoder == None:
51+
raise Exception("Property " + str(prop_id) + " is read-only")
52+
53+
return await self.client.set_raw_property(self.id, prop_id, encoder(value))
54+
55+
async def get_property(self, prop_id):
56+
prop_encoding = self._property_encoding.get(prop_id, None)
57+
58+
if prop_encoding == None:
59+
raise Exception("Property {0} is invalid for {1}".format(prop_id, self.__class__.__name__))
60+
61+
_, decoder = prop_encoding
62+
63+
if decoder == None:
64+
raise Exception("Property " + str(prop_id) + " is write-only")
65+
66+
success, value = await self.client.get_raw_property(self.id, prop_id)
67+
68+
if not success:
69+
return (False, None)
70+
71+
return (success, decoder(value))

zbnt/DiscoveryClient.py

Lines changed: 35 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -21,40 +21,62 @@
2121
import netifaces
2222
import ipaddress
2323

24+
from .Encoding import *
2425
from .MessageReceiver import *
2526

2627
class DiscoveryClient(MessageReceiver):
2728
MSG_DISCOVERY_PORT = 5466
2829

2930
def __init__(self, address_list, on_device_discovered):
30-
super().__init__(self.send_broadcast, self.message_received, None)
31+
super().__init__()
3132

3233
self.address_list = address_list
3334
self.on_device_discovered = on_device_discovered
3435

35-
def datagram_received(self, data, addr):
36-
self.remote_addr = addr
37-
super().bytes_received(data)
36+
@staticmethod
37+
async def create(addr, callback):
38+
loop = asyncio.get_running_loop()
3839

39-
self.status = MsgRxStatus.MSG_RX_MAGIC
40-
self.buffer = b"\x00\x00\x00\x00"
40+
_, protocol = await loop.create_datagram_endpoint(
41+
lambda: DiscoveryClient(addr, callback),
42+
remote_addr=None,
43+
family=socket.AF_INET,
44+
allow_broadcast=True
45+
)
46+
47+
return protocol
48+
49+
def connection_made(self, transport):
50+
super().connection_made(transport)
4151

42-
def send_broadcast(self):
4352
self.validator = random.randint(0, 2**64 - 1)
4453

4554
message = MessageReceiver.MSG_MAGIC_IDENTIFIER
46-
message += Messages.MSG_ID_DISCOVERY.to_bytes(2, byteorder="little")
47-
message += b"\x08\x00"
48-
message += self.validator.to_bytes(8, byteorder="little")
55+
message += encode_u16(Messages.MSG_ID_DISCOVERY)
56+
message += encode_u16(8)
57+
message += encode_u64(self.validator)
4958

5059
for address in self.address_list:
5160
self.transport.sendto(message, (address, DiscoveryClient.MSG_DISCOVERY_PORT))
5261

62+
def datagram_received(self, data, addr):
63+
self.remote_addr = addr
64+
super().bytes_received(data)
65+
66+
self.status = MsgRxStatus.MSG_RX_MAGIC
67+
self.buffer = b"\x00\x00\x00\x00"
68+
69+
def error_received(self, err):
70+
pass
71+
72+
def connection_lost(self, exc):
73+
pass
74+
5375
def message_received(self, msg_id, msg_payload):
5476
if msg_id != Messages.MSG_ID_DISCOVERY or len(msg_payload) <= 67:
5577
return
5678

57-
validator = int.from_bytes(msg_payload[0:8], byteorder='little', signed=False)
79+
validator = decode_u64(msg_payload[0:8])
5880

5981
if validator != self.validator:
6082
return
@@ -64,7 +86,7 @@ def message_received(self, msg_id, msg_payload):
6486
device["version"] = (
6587
msg_payload[11],
6688
msg_payload[10],
67-
int.from_bytes(msg_payload[8:10], byteorder='little', signed=False),
89+
decode_u16(msg_payload[8:10]),
6890
msg_payload[12:28].strip(b"\x00").decode("UTF-8"),
6991
msg_payload[28:44].strip(b"\x00").decode("UTF-8"),
7092
"d" if msg_payload[44] else ""
@@ -101,24 +123,11 @@ def message_received(self, msg_id, msg_payload):
101123
return
102124

103125
device["address"] = address_list
104-
device["port"] = int.from_bytes(msg_payload[65:67], byteorder='little', signed=False)
126+
device["port"] = decode_u16(msg_payload[65:67])
105127
device["name"] = msg_payload[67:].decode("UTF-8")
106128

107129
self.on_device_discovered(device)
108130

109-
@staticmethod
110-
async def create(addr, callback):
111-
loop = asyncio.get_running_loop()
112-
113-
_, protocol = await loop.create_datagram_endpoint(
114-
lambda: DiscoveryClient(addr, callback),
115-
remote_addr=None,
116-
family=socket.AF_INET,
117-
allow_broadcast=True
118-
)
119-
120-
return protocol
121-
122131
async def discover_devices(timeout):
123132
address_list = []
124133
device_list = []

zbnt/Encoding.py

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
"""
2+
zbnt/python-client
3+
Copyright (C) 2020 Oscar R.
4+
5+
This program is free software: you can redistribute it and/or modify
6+
it under the terms of the GNU General Public License as published by
7+
the Free Software Foundation, either version 3 of the License, or
8+
(at your option) any later version.
9+
10+
This program is distributed in the hope that it will be useful,
11+
but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
GNU General Public License for more details.
14+
15+
You should have received a copy of the GNU General Public License
16+
along with this program. If not, see <https://www.gnu.org/licenses/>.
17+
"""
18+
19+
# encode_x : number to bytes
20+
21+
def encode_bool(value):
22+
return bool(value).to_bytes(1, byteorder="little", signed=False)
23+
24+
def encode_u8(value):
25+
return value.to_bytes(1, byteorder="little", signed=False)
26+
27+
def encode_u16(value):
28+
return value.to_bytes(2, byteorder="little", signed=False)
29+
30+
def encode_u32(value):
31+
return value.to_bytes(4, byteorder="little", signed=False)
32+
33+
def encode_u64(value):
34+
return value.to_bytes(8, byteorder="little", signed=False)
35+
36+
def encode_s8(value):
37+
return value.to_bytes(1, byteorder="little", signed=True)
38+
39+
def encode_s16(value):
40+
return value.to_bytes(2, byteorder="little", signed=True)
41+
42+
def encode_s32(value):
43+
return value.to_bytes(4, byteorder="little", signed=True)
44+
45+
def encode_s64(value):
46+
return value.to_bytes(8, byteorder="little", signed=True)
47+
48+
# decode_x : bytes to number
49+
50+
def decode_bool(value):
51+
return bool(value[0])
52+
53+
def decode_u8(value):
54+
return int.from_bytes(value[:1], byteorder="little", signed=False)
55+
56+
def decode_u16(value):
57+
return int.from_bytes(value[:2], byteorder="little", signed=False)
58+
59+
def decode_u32(value):
60+
return int.from_bytes(value[:4], byteorder="little", signed=False)
61+
62+
def decode_u64(value):
63+
return int.from_bytes(value[:8], byteorder="little", signed=False)
64+
65+
def decode_s8(value):
66+
return int.from_bytes(value[:1], byteorder="little", signed=True)
67+
68+
def decode_s16(value):
69+
return int.from_bytes(value[:2], byteorder="little", signed=True)
70+
71+
def decode_s32(value):
72+
return int.from_bytes(value[:4], byteorder="little", signed=True)
73+
74+
def decode_s64(value):
75+
return int.from_bytes(value[:8], byteorder="little", signed=True)

0 commit comments

Comments
 (0)