meshcore 2.1.17__tar.gz → 2.1.21__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {meshcore-2.1.17 → meshcore-2.1.21}/PKG-INFO +1 -1
- {meshcore-2.1.17 → meshcore-2.1.21}/pyproject.toml +1 -1
- {meshcore-2.1.17 → meshcore-2.1.21}/src/meshcore/commands/__init__.py +6 -1
- meshcore-2.1.21/src/meshcore/commands/control_data.py +45 -0
- {meshcore-2.1.17 → meshcore-2.1.21}/src/meshcore/commands/device.py +41 -42
- {meshcore-2.1.17 → meshcore-2.1.21}/src/meshcore/commands/messaging.py +19 -0
- {meshcore-2.1.17 → meshcore-2.1.21}/src/meshcore/events.py +2 -0
- {meshcore-2.1.17 → meshcore-2.1.21}/src/meshcore/packets.py +8 -0
- {meshcore-2.1.17 → meshcore-2.1.21}/src/meshcore/reader.py +167 -121
- {meshcore-2.1.17 → meshcore-2.1.21}/.github/python-test.yml +0 -0
- {meshcore-2.1.17 → meshcore-2.1.21}/.gitignore +0 -0
- {meshcore-2.1.17 → meshcore-2.1.21}/LICENSE +0 -0
- {meshcore-2.1.17 → meshcore-2.1.21}/README.md +0 -0
- {meshcore-2.1.17 → meshcore-2.1.21}/examples/ble_chat.py +0 -0
- {meshcore-2.1.17 → meshcore-2.1.21}/examples/ble_pin_pairing_example.py +0 -0
- {meshcore-2.1.17 → meshcore-2.1.21}/examples/ble_private_key_export.py +0 -0
- {meshcore-2.1.17 → meshcore-2.1.21}/examples/ble_t1000_chan_msg.py +0 -0
- {meshcore-2.1.17 → meshcore-2.1.21}/examples/ble_t1000_custom_vars.py +0 -0
- {meshcore-2.1.17 → meshcore-2.1.21}/examples/ble_t1000_infos.py +0 -0
- {meshcore-2.1.17 → meshcore-2.1.21}/examples/ble_t1000_msg.py +0 -0
- {meshcore-2.1.17 → meshcore-2.1.21}/examples/ble_t1000_msg_retries.py +0 -0
- {meshcore-2.1.17 → meshcore-2.1.21}/examples/ble_t1000_set_cv.py +0 -0
- {meshcore-2.1.17 → meshcore-2.1.21}/examples/connection_events_example.py +0 -0
- {meshcore-2.1.17 → meshcore-2.1.21}/examples/mepo_mc_gps.py +0 -0
- {meshcore-2.1.17 → meshcore-2.1.21}/examples/pubsub_example.py +0 -0
- {meshcore-2.1.17 → meshcore-2.1.21}/examples/rf_packet_monitor.py +0 -0
- {meshcore-2.1.17 → meshcore-2.1.21}/examples/serial_battery_monitor.py +0 -0
- {meshcore-2.1.17 → meshcore-2.1.21}/examples/serial_channel_manager.py +0 -0
- {meshcore-2.1.17 → meshcore-2.1.21}/examples/serial_chat.py +0 -0
- {meshcore-2.1.17 → meshcore-2.1.21}/examples/serial_contacts.py +0 -0
- {meshcore-2.1.17 → meshcore-2.1.21}/examples/serial_infos.py +0 -0
- {meshcore-2.1.17 → meshcore-2.1.21}/examples/serial_msg.py +0 -0
- {meshcore-2.1.17 → meshcore-2.1.21}/examples/serial_repeater_status.py +0 -0
- {meshcore-2.1.17 → meshcore-2.1.21}/examples/serial_repeater_telemetry.py +0 -0
- {meshcore-2.1.17 → meshcore-2.1.21}/examples/serial_trace.py +0 -0
- {meshcore-2.1.17 → meshcore-2.1.21}/examples/tcp_chat.py +0 -0
- {meshcore-2.1.17 → meshcore-2.1.21}/examples/tcp_login_status.py +0 -0
- {meshcore-2.1.17 → meshcore-2.1.21}/examples/tcp_mchome_contacts.py +0 -0
- {meshcore-2.1.17 → meshcore-2.1.21}/examples/tcp_mchome_infos.py +0 -0
- {meshcore-2.1.17 → meshcore-2.1.21}/examples/tcp_mchome_msg.py +0 -0
- {meshcore-2.1.17 → meshcore-2.1.21}/examples/tcp_mchome_readmsgs.py +0 -0
- {meshcore-2.1.17 → meshcore-2.1.21}/pytest.ini +0 -0
- {meshcore-2.1.17 → meshcore-2.1.21}/src/meshcore/__init__.py +0 -0
- {meshcore-2.1.17 → meshcore-2.1.21}/src/meshcore/ble_cx.py +0 -0
- {meshcore-2.1.17 → meshcore-2.1.21}/src/meshcore/commands/base.py +0 -0
- {meshcore-2.1.17 → meshcore-2.1.21}/src/meshcore/commands/binary.py +0 -0
- {meshcore-2.1.17 → meshcore-2.1.21}/src/meshcore/commands/contact.py +0 -0
- {meshcore-2.1.17 → meshcore-2.1.21}/src/meshcore/connection_manager.py +0 -0
- {meshcore-2.1.17 → meshcore-2.1.21}/src/meshcore/lpp_json_encoder.py +0 -0
- {meshcore-2.1.17 → meshcore-2.1.21}/src/meshcore/meshcore.py +0 -0
- {meshcore-2.1.17 → meshcore-2.1.21}/src/meshcore/parsing.py +0 -0
- {meshcore-2.1.17 → meshcore-2.1.21}/src/meshcore/serial_cx.py +0 -0
- {meshcore-2.1.17 → meshcore-2.1.21}/src/meshcore/tcp_cx.py +0 -0
- {meshcore-2.1.17 → meshcore-2.1.21}/tests/README.md +0 -0
- {meshcore-2.1.17 → meshcore-2.1.21}/tests/test_ble_connection.py +0 -0
- {meshcore-2.1.17 → meshcore-2.1.21}/tests/test_ble_pin_pairing.py +0 -0
- {meshcore-2.1.17 → meshcore-2.1.21}/tests/test_meshcore_ble_pin.py +0 -0
- {meshcore-2.1.17 → meshcore-2.1.21}/tests/unit/test_commands.py +0 -0
- {meshcore-2.1.17 → meshcore-2.1.21}/tests/unit/test_events.py +0 -0
- {meshcore-2.1.17 → meshcore-2.1.21}/tests/unit/test_private_key_export.py +0 -0
- {meshcore-2.1.17 → meshcore-2.1.21}/tests/unit/test_reader.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: meshcore
|
|
3
|
-
Version: 2.1.
|
|
3
|
+
Version: 2.1.21
|
|
4
4
|
Summary: Base classes for communicating with meshcore companion radios
|
|
5
5
|
Project-URL: Homepage, https://github.com/fdlamotte/meshcore_py
|
|
6
6
|
Project-URL: Issues, https://github.com/fdlamotte/meshcore_py/issues
|
|
@@ -7,10 +7,15 @@ from .binary import BinaryCommandHandler
|
|
|
7
7
|
from .contact import ContactCommands
|
|
8
8
|
from .device import DeviceCommands
|
|
9
9
|
from .messaging import MessagingCommands
|
|
10
|
+
from .control_data import ControlDataCommandHandler
|
|
10
11
|
|
|
11
12
|
|
|
12
13
|
class CommandHandler(
|
|
13
|
-
DeviceCommands,
|
|
14
|
+
DeviceCommands,
|
|
15
|
+
ContactCommands,
|
|
16
|
+
MessagingCommands,
|
|
17
|
+
BinaryCommandHandler,
|
|
18
|
+
ControlDataCommandHandler
|
|
14
19
|
):
|
|
15
20
|
pass
|
|
16
21
|
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import random
|
|
3
|
+
|
|
4
|
+
from .base import CommandHandlerBase
|
|
5
|
+
from ..events import EventType, Event
|
|
6
|
+
from ..packets import ControlType, PacketType
|
|
7
|
+
|
|
8
|
+
logger = logging.getLogger("meshcore")
|
|
9
|
+
|
|
10
|
+
class ControlDataCommandHandler(CommandHandlerBase):
|
|
11
|
+
"""Helper functions to handle binary requests through binary commands"""
|
|
12
|
+
|
|
13
|
+
async def send_control_data (self, control_type: ControlType, payload: bytes) -> Event:
|
|
14
|
+
data = bytearray([PacketType.SEND_CONTROL_DATA.value])
|
|
15
|
+
data.extend(control_type.value.to_bytes(1, "little", signed = False))
|
|
16
|
+
data.extend(payload)
|
|
17
|
+
|
|
18
|
+
result = await self.send(data, [EventType.OK, EventType.ERROR])
|
|
19
|
+
return result
|
|
20
|
+
|
|
21
|
+
async def send_node_discover_req (
|
|
22
|
+
self,
|
|
23
|
+
filter: int,
|
|
24
|
+
tag: int=None,
|
|
25
|
+
since: int=None
|
|
26
|
+
) -> Event:
|
|
27
|
+
|
|
28
|
+
if tag is None:
|
|
29
|
+
tag = random.randint(1, 0xFFFFFFFF)
|
|
30
|
+
|
|
31
|
+
data = bytearray()
|
|
32
|
+
data.extend(filter.to_bytes(1, "little", signed=False))
|
|
33
|
+
data.extend(tag.to_bytes(4, "little"))
|
|
34
|
+
if not since is None:
|
|
35
|
+
data.extend(since.to_bytes(4, "little", signed=False))
|
|
36
|
+
|
|
37
|
+
logger.debug(f"sending node discover req {data.hex()}")
|
|
38
|
+
|
|
39
|
+
res = await self.send_control_data(ControlType.NODE_DISCOVER_REQ, data)
|
|
40
|
+
|
|
41
|
+
if res is None:
|
|
42
|
+
return None
|
|
43
|
+
else:
|
|
44
|
+
res.payload["tag"] = tag
|
|
45
|
+
return res
|
|
@@ -87,14 +87,18 @@ class DeviceCommands(CommandHandlerBase):
|
|
|
87
87
|
[EventType.OK, EventType.ERROR],
|
|
88
88
|
)
|
|
89
89
|
|
|
90
|
+
# the old set_other_params function has been replaced in
|
|
91
|
+
# favour of set_other_params_from_infos to be more generic
|
|
92
|
+
# stays here for backward compatibility but does not support
|
|
93
|
+
# multi_acks for instance
|
|
90
94
|
async def set_other_params(
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
95
|
+
self,
|
|
96
|
+
manual_add_contacts: bool,
|
|
97
|
+
telemetry_mode_base: int,
|
|
98
|
+
telemetry_mode_loc: int,
|
|
99
|
+
telemetry_mode_env: int,
|
|
100
|
+
advert_loc_policy: int,
|
|
101
|
+
) -> Event:
|
|
98
102
|
telemetry_mode = (
|
|
99
103
|
(telemetry_mode_base & 0b11)
|
|
100
104
|
| ((telemetry_mode_loc & 0b11) << 2)
|
|
@@ -108,55 +112,50 @@ class DeviceCommands(CommandHandlerBase):
|
|
|
108
112
|
)
|
|
109
113
|
return await self.send(data, [EventType.OK, EventType.ERROR])
|
|
110
114
|
|
|
115
|
+
async def set_other_params_from_infos(self, infos) -> Event:
|
|
116
|
+
telemetry_mode = (
|
|
117
|
+
(infos["telemetry_mode_base"] & 0b11)
|
|
118
|
+
| ((infos["telemetry_mode_loc"] & 0b11) << 2)
|
|
119
|
+
| ((infos["telemetry_mode_env"] & 0b11) << 4)
|
|
120
|
+
)
|
|
121
|
+
data = (
|
|
122
|
+
b"\x26"
|
|
123
|
+
+ infos["manual_add_contacts"].to_bytes(1)
|
|
124
|
+
+ telemetry_mode.to_bytes(1)
|
|
125
|
+
+ infos["adv_loc_policy"].to_bytes(1)
|
|
126
|
+
+ infos["multi_acks"].to_bytes(1)
|
|
127
|
+
)
|
|
128
|
+
return await self.send(data, [EventType.OK, EventType.ERROR])
|
|
129
|
+
|
|
111
130
|
async def set_telemetry_mode_base(self, telemetry_mode_base: int) -> Event:
|
|
112
131
|
infos = (await self.send_appstart()).payload
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
telemetry_mode_base,
|
|
116
|
-
infos["telemetry_mode_loc"],
|
|
117
|
-
infos["telemetry_mode_env"],
|
|
118
|
-
infos["adv_loc_policy"],
|
|
119
|
-
)
|
|
132
|
+
infos["telemetry_mode_base"] = telemetry_mode_base
|
|
133
|
+
return await self.set_other_params_from_infos(infos)
|
|
120
134
|
|
|
121
135
|
async def set_telemetry_mode_loc(self, telemetry_mode_loc: int) -> Event:
|
|
122
136
|
infos = (await self.send_appstart()).payload
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
infos["telemetry_mode_base"],
|
|
126
|
-
telemetry_mode_loc,
|
|
127
|
-
infos["telemetry_mode_env"],
|
|
128
|
-
infos["adv_loc_policy"],
|
|
129
|
-
)
|
|
137
|
+
infos["telemetry_mode_loc"] = telemetry_mode_loc
|
|
138
|
+
return await self.set_other_params_from_infos(infos)
|
|
130
139
|
|
|
131
140
|
async def set_telemetry_mode_env(self, telemetry_mode_env: int) -> Event:
|
|
132
141
|
infos = (await self.send_appstart()).payload
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
infos["telemetry_mode_base"],
|
|
136
|
-
infos["telemetry_mode_loc"],
|
|
137
|
-
telemetry_mode_env,
|
|
138
|
-
infos["adv_loc_policy"],
|
|
139
|
-
)
|
|
142
|
+
infos["telemetry_mode_env"] = telemetry_mode_env
|
|
143
|
+
return await self.set_other_params_from_infos(infos)
|
|
140
144
|
|
|
141
145
|
async def set_manual_add_contacts(self, manual_add_contacts: bool) -> Event:
|
|
142
146
|
infos = (await self.send_appstart()).payload
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
infos["telemetry_mode_base"],
|
|
146
|
-
infos["telemetry_mode_loc"],
|
|
147
|
-
infos["telemetry_mode_env"],
|
|
148
|
-
infos["adv_loc_policy"],
|
|
149
|
-
)
|
|
147
|
+
infos["manual_add_contacts"] = manual_add_contacts
|
|
148
|
+
return await self.set_other_params_from_infos(infos)
|
|
150
149
|
|
|
151
150
|
async def set_advert_loc_policy(self, advert_loc_policy: int) -> Event:
|
|
152
151
|
infos = (await self.send_appstart()).payload
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
)
|
|
152
|
+
infos["adv_loc_policy"] = advert_loc_policy
|
|
153
|
+
return await self.set_other_params_from_infos(infos)
|
|
154
|
+
|
|
155
|
+
async def set_multi_acks(self, multi_acks: int) -> Event:
|
|
156
|
+
infos = (await self.send_appstart()).payload
|
|
157
|
+
infos["multi_acks"] = multi_acks
|
|
158
|
+
return await self.set_other_params_from_infos(infos)
|
|
160
159
|
|
|
161
160
|
async def set_devicepin(self, pin: int) -> Event:
|
|
162
161
|
logger.debug(f"Setting device PIN to: {pin}")
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
import random
|
|
3
3
|
from typing import Optional, Union
|
|
4
|
+
from hashlib import sha256
|
|
4
5
|
|
|
5
6
|
from ..events import Event, EventType
|
|
7
|
+
from ..packets import PacketType
|
|
6
8
|
from .base import CommandHandlerBase, DestinationType, _validate_destination
|
|
7
9
|
|
|
8
10
|
logger = logging.getLogger("meshcore")
|
|
@@ -209,3 +211,20 @@ class MessagingCommands(CommandHandlerBase):
|
|
|
209
211
|
return Event(EventType.ERROR, {"reason": "unsupported_path_type"})
|
|
210
212
|
|
|
211
213
|
return await self.send(cmd_data, [EventType.MSG_SENT, EventType.ERROR])
|
|
214
|
+
|
|
215
|
+
async def set_flood_scope(self, scope):
|
|
216
|
+
if scope.startswith("#"): # an hash
|
|
217
|
+
logger.debug(f"Setting scope from hash {scope}")
|
|
218
|
+
scope_key = sha256(scope.encode("utf-8")).digest()[0:16]
|
|
219
|
+
elif scope == "0" or scope == "None" or scope == "*" or scope == "": # disable
|
|
220
|
+
scope_key = b"\0"*16
|
|
221
|
+
else: # assume the key has been sent directly
|
|
222
|
+
scope_key = scope.encode("utf-8")
|
|
223
|
+
|
|
224
|
+
logger.debug(f"Setting scope to {scope_key.hex()}")
|
|
225
|
+
|
|
226
|
+
cmd_data = bytearray([PacketType.SET_FLOOD_SCOPE.value])
|
|
227
|
+
cmd_data.extend(b"\0")
|
|
228
|
+
cmd_data.extend(scope_key)
|
|
229
|
+
|
|
230
|
+
return await self.send(cmd_data, [EventType.OK, EventType.ERROR])
|
|
@@ -7,6 +7,10 @@ class BinaryReqType(Enum):
|
|
|
7
7
|
MMA = 0x04
|
|
8
8
|
ACL = 0x05
|
|
9
9
|
|
|
10
|
+
class ControlType(Enum):
|
|
11
|
+
NODE_DISCOVER_REQ = 0x80
|
|
12
|
+
NODE_DISCOVER_RESP = 0x90
|
|
13
|
+
|
|
10
14
|
# Packet prefixes for the protocol
|
|
11
15
|
class PacketType(Enum):
|
|
12
16
|
OK = 0
|
|
@@ -33,6 +37,9 @@ class PacketType(Enum):
|
|
|
33
37
|
CUSTOM_VARS = 21
|
|
34
38
|
BINARY_REQ = 50
|
|
35
39
|
FACTORY_RESET = 51
|
|
40
|
+
PATH_DISCOVERY = 52
|
|
41
|
+
SET_FLOOD_SCOPE = 54
|
|
42
|
+
SEND_CONTROL_DATA = 55
|
|
36
43
|
|
|
37
44
|
# Push notifications
|
|
38
45
|
ADVERTISEMENT = 0x80
|
|
@@ -49,3 +56,4 @@ class PacketType(Enum):
|
|
|
49
56
|
TELEMETRY_RESPONSE = 0x8B
|
|
50
57
|
BINARY_RESPONSE = 0x8C
|
|
51
58
|
PATH_DISCOVERY_RESPONSE = 0x8D
|
|
59
|
+
CONTROL_DATA = 0x8E
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
import json
|
|
3
3
|
import time
|
|
4
|
+
import io
|
|
4
5
|
from typing import Any, Dict
|
|
5
6
|
from .events import Event, EventType, EventDispatcher
|
|
6
|
-
from .packets import BinaryReqType, PacketType
|
|
7
|
+
from .packets import BinaryReqType, PacketType, ControlType
|
|
7
8
|
from .parsing import lpp_parse, lpp_parse_mma, parse_acl, parse_status
|
|
8
9
|
from cayennelpp import LppFrame, LppData
|
|
9
10
|
from meshcore.lpp_json_encoder import lpp_json_encoder
|
|
@@ -18,7 +19,7 @@ class MessageReader:
|
|
|
18
19
|
# before events are dispatched
|
|
19
20
|
self.contacts = {} # Temporary storage during contact list building
|
|
20
21
|
self.contact_nb = 0 # Used for contact processing
|
|
21
|
-
|
|
22
|
+
|
|
22
23
|
# Track pending binary requests by tag for proper response parsing
|
|
23
24
|
self.pending_binary_requests: Dict[str, Dict[str, Any]] = {} # tag -> {request_type, expires_at}
|
|
24
25
|
|
|
@@ -26,7 +27,7 @@ class MessageReader:
|
|
|
26
27
|
"""Register a pending binary request for proper response parsing"""
|
|
27
28
|
# Clean up expired requests before adding new one
|
|
28
29
|
self.cleanup_expired_requests()
|
|
29
|
-
|
|
30
|
+
|
|
30
31
|
expires_at = time.time() + timeout_seconds
|
|
31
32
|
self.pending_binary_requests[tag] = {
|
|
32
33
|
"request_type": request_type,
|
|
@@ -42,13 +43,14 @@ class MessageReader:
|
|
|
42
43
|
tag for tag, info in self.pending_binary_requests.items()
|
|
43
44
|
if current_time > info["expires_at"]
|
|
44
45
|
]
|
|
45
|
-
|
|
46
|
+
|
|
46
47
|
for tag in expired_tags:
|
|
47
48
|
logger.debug(f"Cleaning up expired binary request: tag={tag}")
|
|
48
49
|
del self.pending_binary_requests[tag]
|
|
49
|
-
|
|
50
|
+
|
|
50
51
|
async def handle_rx(self, data: bytearray):
|
|
51
|
-
|
|
52
|
+
dbuf = io.BytesIO(data)
|
|
53
|
+
packet_type_value = dbuf.read(1)[0]
|
|
52
54
|
logger.debug(f"Received data: {data.hex()}")
|
|
53
55
|
|
|
54
56
|
# Handle command responses
|
|
@@ -78,23 +80,24 @@ class MessageReader:
|
|
|
78
80
|
or packet_type_value == PacketType.PUSH_CODE_NEW_ADVERT.value
|
|
79
81
|
):
|
|
80
82
|
c = {}
|
|
81
|
-
c["public_key"] =
|
|
82
|
-
c["type"] =
|
|
83
|
-
c["flags"] =
|
|
84
|
-
|
|
85
|
-
|
|
83
|
+
c["public_key"] = dbuf.read(32).hex()
|
|
84
|
+
c["type"] = dbuf.read(1)[0]
|
|
85
|
+
c["flags"] = dbuf.read(1)[0]
|
|
86
|
+
plen = int.from_bytes(dbuf.read(1), signed=True, byteorder="little")
|
|
87
|
+
c["out_path_len"] = plen
|
|
86
88
|
if plen == -1:
|
|
87
89
|
plen = 0
|
|
88
|
-
|
|
89
|
-
c["
|
|
90
|
-
c["
|
|
90
|
+
path = dbuf.read(64)
|
|
91
|
+
c["out_path"] = path[0:plen].hex()
|
|
92
|
+
c["adv_name"] = dbuf.read(32).decode("utf-8", "ignore").replace("\0", "")
|
|
93
|
+
c["last_advert"] = int.from_bytes(dbuf.read(4), byteorder="little")
|
|
91
94
|
c["adv_lat"] = (
|
|
92
|
-
int.from_bytes(
|
|
95
|
+
int.from_bytes(dbuf.read(4), byteorder="little", signed=True) / 1e6
|
|
93
96
|
)
|
|
94
97
|
c["adv_lon"] = (
|
|
95
|
-
int.from_bytes(
|
|
98
|
+
int.from_bytes(dbuf.read(4), byteorder="little", signed=True) / 1e6
|
|
96
99
|
)
|
|
97
|
-
c["lastmod"] = int.from_bytes(
|
|
100
|
+
c["lastmod"] = int.from_bytes(dbuf.read(4), byteorder="little")
|
|
98
101
|
|
|
99
102
|
if packet_type_value == PacketType.PUSH_CODE_NEW_ADVERT.value:
|
|
100
103
|
await self.dispatcher.dispatch(Event(EventType.NEW_CONTACT, c))
|
|
@@ -103,7 +106,7 @@ class MessageReader:
|
|
|
103
106
|
self.contacts[c["public_key"]] = c
|
|
104
107
|
|
|
105
108
|
elif packet_type_value == PacketType.CONTACT_END.value:
|
|
106
|
-
lastmod = int.from_bytes(
|
|
109
|
+
lastmod = int.from_bytes(dbuf.read(4), byteorder="little")
|
|
107
110
|
attributes = {
|
|
108
111
|
"lastmod": lastmod,
|
|
109
112
|
}
|
|
@@ -113,37 +116,39 @@ class MessageReader:
|
|
|
113
116
|
|
|
114
117
|
elif packet_type_value == PacketType.SELF_INFO.value:
|
|
115
118
|
self_info = {}
|
|
116
|
-
self_info["adv_type"] =
|
|
117
|
-
self_info["tx_power"] =
|
|
118
|
-
self_info["max_tx_power"] =
|
|
119
|
-
self_info["public_key"] =
|
|
119
|
+
self_info["adv_type"] = dbuf.read(1)[0]
|
|
120
|
+
self_info["tx_power"] = dbuf.read(1)[0]
|
|
121
|
+
self_info["max_tx_power"] = dbuf.read(1)[0]
|
|
122
|
+
self_info["public_key"] = dbuf.read(32).hex()
|
|
120
123
|
self_info["adv_lat"] = (
|
|
121
|
-
int.from_bytes(
|
|
124
|
+
int.from_bytes(dbuf.read(4), byteorder="little", signed=True) / 1e6
|
|
122
125
|
)
|
|
123
126
|
self_info["adv_lon"] = (
|
|
124
|
-
int.from_bytes(
|
|
127
|
+
int.from_bytes(dbuf.read(4), byteorder="little", signed=True) / 1e6
|
|
125
128
|
)
|
|
126
|
-
self_info["
|
|
127
|
-
self_info["
|
|
128
|
-
|
|
129
|
-
self_info["
|
|
130
|
-
self_info["
|
|
129
|
+
self_info["multi_acks"] = dbuf.read(1)[0]
|
|
130
|
+
self_info["adv_loc_policy"] = dbuf.read(1)[0]
|
|
131
|
+
telemetry_mode = dbuf.read(1)[0]
|
|
132
|
+
self_info["telemetry_mode_env"] = (telemetry_mode >> 4) & 0b11
|
|
133
|
+
self_info["telemetry_mode_loc"] = (telemetry_mode >> 2) & 0b11
|
|
134
|
+
self_info["telemetry_mode_base"] = (telemetry_mode) & 0b11
|
|
135
|
+
self_info["manual_add_contacts"] = dbuf.read(1)[0] > 0
|
|
131
136
|
self_info["radio_freq"] = (
|
|
132
|
-
int.from_bytes(
|
|
137
|
+
int.from_bytes(dbuf.read(4), byteorder="little") / 1000
|
|
133
138
|
)
|
|
134
139
|
self_info["radio_bw"] = (
|
|
135
|
-
int.from_bytes(
|
|
140
|
+
int.from_bytes(dbuf.read(4), byteorder="little") / 1000
|
|
136
141
|
)
|
|
137
|
-
self_info["radio_sf"] =
|
|
138
|
-
self_info["radio_cr"] =
|
|
139
|
-
self_info["name"] =
|
|
142
|
+
self_info["radio_sf"] = dbuf.read(1)[0]
|
|
143
|
+
self_info["radio_cr"] = dbuf.read(1)[0]
|
|
144
|
+
self_info["name"] = dbuf.read().decode("utf-8", "ignore")
|
|
140
145
|
await self.dispatcher.dispatch(Event(EventType.SELF_INFO, self_info))
|
|
141
146
|
|
|
142
147
|
elif packet_type_value == PacketType.MSG_SENT.value:
|
|
143
148
|
res = {}
|
|
144
|
-
res["type"] =
|
|
145
|
-
res["expected_ack"] =
|
|
146
|
-
res["suggested_timeout"] = int.from_bytes(
|
|
149
|
+
res["type"] = dbuf.read(1)[0]
|
|
150
|
+
res["expected_ack"] = dbuf.read(4)
|
|
151
|
+
res["suggested_timeout"] = int.from_bytes(dbuf.read(4), byteorder="little")
|
|
147
152
|
|
|
148
153
|
attributes = {
|
|
149
154
|
"type": res["type"],
|
|
@@ -155,15 +160,14 @@ class MessageReader:
|
|
|
155
160
|
elif packet_type_value == PacketType.CONTACT_MSG_RECV.value:
|
|
156
161
|
res = {}
|
|
157
162
|
res["type"] = "PRIV"
|
|
158
|
-
res["pubkey_prefix"] =
|
|
159
|
-
res["path_len"] =
|
|
160
|
-
|
|
161
|
-
res["
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
res["
|
|
165
|
-
|
|
166
|
-
res["text"] = data[13:].decode("utf-8", "ignore")
|
|
163
|
+
res["pubkey_prefix"] = dbuf.read(6).hex()
|
|
164
|
+
res["path_len"] = dbuf.read(1)[0]
|
|
165
|
+
txt_type = dbuf.read(1)[0]
|
|
166
|
+
res["txt_type"] = txt_type
|
|
167
|
+
res["sender_timestamp"] = int.from_bytes(dbuf.read(4), byteorder="little")
|
|
168
|
+
if txt_type == 2:
|
|
169
|
+
res["signature"] = dbuf.read(4).hex()
|
|
170
|
+
res["text"] = dbuf.read().decode("utf-8", "ignore")
|
|
167
171
|
|
|
168
172
|
attributes = {
|
|
169
173
|
"pubkey_prefix": res["pubkey_prefix"],
|
|
@@ -177,16 +181,16 @@ class MessageReader:
|
|
|
177
181
|
elif packet_type_value == 16: # A reply to CMD_SYNC_NEXT_MESSAGE (ver >= 3)
|
|
178
182
|
res = {}
|
|
179
183
|
res["type"] = "PRIV"
|
|
180
|
-
res["SNR"] = int.from_bytes(
|
|
181
|
-
|
|
182
|
-
res["
|
|
183
|
-
res["
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
184
|
+
res["SNR"] = int.from_bytes(dbuf.read(2), byteorder="little", signed=True) / 4
|
|
185
|
+
dbuf.read(1) # reserved
|
|
186
|
+
res["pubkey_prefix"] = dbuf.read(6).hex()
|
|
187
|
+
res["path_len"] = dbuf.read(1)[0]
|
|
188
|
+
txt_type = dbuf.read(1)[0]
|
|
189
|
+
res["txt_type"] = txt_type
|
|
190
|
+
res["sender_timestamp"] = int.from_bytes(dbuf.read(4), byteorder="little")
|
|
191
|
+
if txt_type == 2:
|
|
192
|
+
res["signature"] = dbuf.read(4).hex()
|
|
193
|
+
res["text"] = dbuf.read().decode("utf-8", "ignore")
|
|
190
194
|
|
|
191
195
|
attributes = {
|
|
192
196
|
"pubkey_prefix": res["pubkey_prefix"],
|
|
@@ -200,11 +204,11 @@ class MessageReader:
|
|
|
200
204
|
elif packet_type_value == PacketType.CHANNEL_MSG_RECV.value:
|
|
201
205
|
res = {}
|
|
202
206
|
res["type"] = "CHAN"
|
|
203
|
-
res["channel_idx"] =
|
|
204
|
-
res["path_len"] =
|
|
205
|
-
res["txt_type"] =
|
|
206
|
-
res["sender_timestamp"] = int.from_bytes(
|
|
207
|
-
res["text"] =
|
|
207
|
+
res["channel_idx"] = dbuf.read(1)[0]
|
|
208
|
+
res["path_len"] = dbuf.read(1)[0]
|
|
209
|
+
res["txt_type"] = dbuf.read(1)[0]
|
|
210
|
+
res["sender_timestamp"] = int.from_bytes(dbuf.read(4), byteorder="little")
|
|
211
|
+
res["text"] = dbuf.read().decode("utf-8", "ignore")
|
|
208
212
|
|
|
209
213
|
attributes = {
|
|
210
214
|
"channel_idx": res["channel_idx"],
|
|
@@ -218,12 +222,13 @@ class MessageReader:
|
|
|
218
222
|
elif packet_type_value == 17: # A reply to CMD_SYNC_NEXT_MESSAGE (ver >= 3)
|
|
219
223
|
res = {}
|
|
220
224
|
res["type"] = "CHAN"
|
|
221
|
-
res["SNR"] = int.from_bytes(
|
|
222
|
-
|
|
223
|
-
res["
|
|
224
|
-
res["
|
|
225
|
-
res["
|
|
226
|
-
res["
|
|
225
|
+
res["SNR"] = int.from_bytes(dbuf.read(2), byteorder="little", signed=True) / 4
|
|
226
|
+
dbuf.read(1) # reserved
|
|
227
|
+
res["channel_idx"] = dbuf.read(1)[0]
|
|
228
|
+
res["path_len"] = dbuf.read(1)[0]
|
|
229
|
+
res["txt_type"] = dbuf.read(1)[0]
|
|
230
|
+
res["sender_timestamp"] = int.from_bytes(dbuf.read(4), byteorder="little")
|
|
231
|
+
res["text"] = dbuf.read().decode("utf-8", "ignore")
|
|
227
232
|
|
|
228
233
|
attributes = {
|
|
229
234
|
"channel_idx": res["channel_idx"],
|
|
@@ -235,7 +240,7 @@ class MessageReader:
|
|
|
235
240
|
)
|
|
236
241
|
|
|
237
242
|
elif packet_type_value == PacketType.CURRENT_TIME.value:
|
|
238
|
-
time_value = int.from_bytes(
|
|
243
|
+
time_value = int.from_bytes(dbuf.read(4), byteorder="little")
|
|
239
244
|
result = {"time": time_value}
|
|
240
245
|
await self.dispatcher.dispatch(Event(EventType.CURRENT_TIME, result))
|
|
241
246
|
|
|
@@ -244,34 +249,34 @@ class MessageReader:
|
|
|
244
249
|
await self.dispatcher.dispatch(Event(EventType.NO_MORE_MSGS, result))
|
|
245
250
|
|
|
246
251
|
elif packet_type_value == PacketType.CONTACT_URI.value:
|
|
247
|
-
contact_uri = "meshcore://" +
|
|
252
|
+
contact_uri = "meshcore://" + dbuf.read().hex()
|
|
248
253
|
result = {"uri": contact_uri}
|
|
249
254
|
await self.dispatcher.dispatch(Event(EventType.CONTACT_URI, result))
|
|
250
255
|
|
|
251
256
|
elif packet_type_value == PacketType.BATTERY.value:
|
|
252
|
-
battery_level = int.from_bytes(
|
|
257
|
+
battery_level = int.from_bytes(dbuf.read(2), byteorder="little")
|
|
253
258
|
result = {"level": battery_level}
|
|
254
259
|
if len(data) > 3: # has storage info as well
|
|
255
|
-
result["used_kb"] = int.from_bytes(
|
|
256
|
-
result["total_kb"] = int.from_bytes(
|
|
260
|
+
result["used_kb"] = int.from_bytes(dbuf.read(4), byteorder="little")
|
|
261
|
+
result["total_kb"] = int.from_bytes(dbuf.read(4), byteorder="little")
|
|
257
262
|
await self.dispatcher.dispatch(Event(EventType.BATTERY, result))
|
|
258
263
|
|
|
259
264
|
elif packet_type_value == PacketType.DEVICE_INFO.value:
|
|
260
265
|
res = {}
|
|
261
|
-
res["fw ver"] =
|
|
266
|
+
res["fw ver"] = dbuf.read(1)[0]
|
|
262
267
|
if data[1] >= 3:
|
|
263
|
-
res["max_contacts"] =
|
|
264
|
-
res["max_channels"] =
|
|
265
|
-
res["ble_pin"] = int.from_bytes(
|
|
266
|
-
res["fw_build"] =
|
|
267
|
-
res["model"] =
|
|
268
|
-
res["ver"] =
|
|
268
|
+
res["max_contacts"] = dbuf.read(1)[0] * 2
|
|
269
|
+
res["max_channels"] = dbuf.read(1)[0]
|
|
270
|
+
res["ble_pin"] = int.from_bytes(dbuf.read(4), byteorder="little")
|
|
271
|
+
res["fw_build"] = dbuf.read(12).decode("utf-8", "ignore").replace("\0", "")
|
|
272
|
+
res["model"] = dbuf.read(40).decode("utf-8", "ignore").replace("\0", "")
|
|
273
|
+
res["ver"] = dbuf.read(20).decode("utf-8", "ignore").replace("\0", "")
|
|
269
274
|
await self.dispatcher.dispatch(Event(EventType.DEVICE_INFO, res))
|
|
270
275
|
|
|
271
276
|
elif packet_type_value == PacketType.CUSTOM_VARS.value:
|
|
272
277
|
logger.debug(f"received custom vars response: {data.hex()}")
|
|
273
278
|
res = {}
|
|
274
|
-
rawdata =
|
|
279
|
+
rawdata = dbuf.read().decode("utf-8", "ignore")
|
|
275
280
|
if not rawdata == "":
|
|
276
281
|
pairs = rawdata.split(",")
|
|
277
282
|
for p in pairs:
|
|
@@ -283,30 +288,30 @@ class MessageReader:
|
|
|
283
288
|
elif packet_type_value == PacketType.CHANNEL_INFO.value:
|
|
284
289
|
logger.debug(f"received channel info response: {data.hex()}")
|
|
285
290
|
res = {}
|
|
286
|
-
res["channel_idx"] =
|
|
291
|
+
res["channel_idx"] = dbuf.read(1)[0]
|
|
287
292
|
|
|
288
293
|
# Channel name is null-terminated, so find the first null byte
|
|
289
|
-
name_bytes =
|
|
294
|
+
name_bytes = dbuf.read(32)
|
|
290
295
|
null_pos = name_bytes.find(0)
|
|
291
296
|
if null_pos >= 0:
|
|
292
297
|
res["channel_name"] = name_bytes[:null_pos].decode("utf-8", "ignore")
|
|
293
298
|
else:
|
|
294
299
|
res["channel_name"] = name_bytes.decode("utf-8", "ignore")
|
|
295
300
|
|
|
296
|
-
res["channel_secret"] =
|
|
301
|
+
res["channel_secret"] = dbuf.read(16)
|
|
297
302
|
await self.dispatcher.dispatch(Event(EventType.CHANNEL_INFO, res, res))
|
|
298
303
|
|
|
299
304
|
# Push notifications
|
|
300
305
|
elif packet_type_value == PacketType.ADVERTISEMENT.value:
|
|
301
306
|
logger.debug("Advertisement received")
|
|
302
307
|
res = {}
|
|
303
|
-
res["public_key"] =
|
|
308
|
+
res["public_key"] = dbuf.read(32).hex()
|
|
304
309
|
await self.dispatcher.dispatch(Event(EventType.ADVERTISEMENT, res, res))
|
|
305
310
|
|
|
306
311
|
elif packet_type_value == PacketType.PATH_UPDATE.value:
|
|
307
312
|
logger.debug("Code path update")
|
|
308
313
|
res = {}
|
|
309
|
-
res["public_key"] =
|
|
314
|
+
res["public_key"] = dbuf.read(32).hex()
|
|
310
315
|
await self.dispatcher.dispatch(Event(EventType.PATH_UPDATE, res, res))
|
|
311
316
|
|
|
312
317
|
elif packet_type_value == PacketType.ACK.value:
|
|
@@ -314,7 +319,7 @@ class MessageReader:
|
|
|
314
319
|
ack_data = {}
|
|
315
320
|
|
|
316
321
|
if len(data) >= 5:
|
|
317
|
-
ack_data["code"] =
|
|
322
|
+
ack_data["code"] = dbuf.read(4).hex()
|
|
318
323
|
|
|
319
324
|
attributes = {"code": ack_data.get("code", "")}
|
|
320
325
|
|
|
@@ -326,23 +331,24 @@ class MessageReader:
|
|
|
326
331
|
|
|
327
332
|
elif packet_type_value == PacketType.RAW_DATA.value:
|
|
328
333
|
res = {}
|
|
329
|
-
res["SNR"] =
|
|
330
|
-
res["RSSI"] =
|
|
331
|
-
res["payload"] =
|
|
334
|
+
res["SNR"] = int.from_bytes(dbuf.read(1), byteorder="little", signed=True) / 4
|
|
335
|
+
res["RSSI"] = int.from_bytes(dbuf.read(1), byteorder="little", signed=True)
|
|
336
|
+
res["payload"] = dbuf.read(4).hex()
|
|
332
337
|
logger.debug("Received raw data")
|
|
333
338
|
print(res)
|
|
334
339
|
await self.dispatcher.dispatch(Event(EventType.RAW_DATA, res))
|
|
335
340
|
|
|
336
341
|
elif packet_type_value == PacketType.LOGIN_SUCCESS.value:
|
|
337
342
|
res = {}
|
|
343
|
+
attributes = {}
|
|
338
344
|
if len(data) > 1:
|
|
339
|
-
|
|
340
|
-
res["
|
|
345
|
+
perms = dbuf.read(1)[0]
|
|
346
|
+
res["permissions"] = perms
|
|
347
|
+
res["is_admin"] = (perms & 1) == 1 # Check if admin bit is set
|
|
341
348
|
|
|
342
|
-
|
|
343
|
-
res["pubkey_prefix"] = data[2:8].hex()
|
|
349
|
+
res["pubkey_prefix"] = dbuf.read(6).hex()
|
|
344
350
|
|
|
345
|
-
|
|
351
|
+
attributes = {"pubkey_prefix": res.get("pubkey_prefix")}
|
|
346
352
|
|
|
347
353
|
await self.dispatcher.dispatch(
|
|
348
354
|
Event(EventType.LOGIN_SUCCESS, res, attributes)
|
|
@@ -350,11 +356,14 @@ class MessageReader:
|
|
|
350
356
|
|
|
351
357
|
elif packet_type_value == PacketType.LOGIN_FAILED.value:
|
|
352
358
|
res = {}
|
|
359
|
+
attributes = {}
|
|
360
|
+
|
|
361
|
+
pbuf.read(1)
|
|
353
362
|
|
|
354
363
|
if len(data) > 7:
|
|
355
|
-
res["pubkey_prefix"] =
|
|
364
|
+
res["pubkey_prefix"] = pbuf.read(6).hex()
|
|
356
365
|
|
|
357
|
-
|
|
366
|
+
attributes = {"pubkey_prefix": res.get("pubkey_prefix")}
|
|
358
367
|
|
|
359
368
|
await self.dispatcher.dispatch(
|
|
360
369
|
Event(EventType.LOGIN_FAILED, res, attributes)
|
|
@@ -368,12 +377,7 @@ class MessageReader:
|
|
|
368
377
|
attributes = {
|
|
369
378
|
"pubkey_prefix": res["pubkey_pre"],
|
|
370
379
|
}
|
|
371
|
-
data_hex = data[8:].hex()
|
|
372
|
-
logger.debug(f"Status response: {data_hex}")
|
|
373
380
|
|
|
374
|
-
attributes = {
|
|
375
|
-
"pubkey_prefix": res["pubkey_pre"],
|
|
376
|
-
}
|
|
377
381
|
await self.dispatcher.dispatch(
|
|
378
382
|
Event(EventType.STATUS_RESPONSE, res, attributes)
|
|
379
383
|
)
|
|
@@ -386,22 +390,23 @@ class MessageReader:
|
|
|
386
390
|
|
|
387
391
|
# First byte is SNR (signed byte, multiplied by 4)
|
|
388
392
|
if len(data) > 1:
|
|
389
|
-
snr_byte =
|
|
393
|
+
snr_byte = dbuf.read(1)[0]
|
|
390
394
|
# Convert to signed value
|
|
391
395
|
snr = (snr_byte if snr_byte < 128 else snr_byte - 256) / 4.0
|
|
392
396
|
log_data["snr"] = snr
|
|
393
397
|
|
|
394
398
|
# Second byte is RSSI (signed byte)
|
|
395
399
|
if len(data) > 2:
|
|
396
|
-
rssi_byte =
|
|
400
|
+
rssi_byte = dbuf.read(1)[0]
|
|
397
401
|
# Convert to signed value
|
|
398
402
|
rssi = rssi_byte if rssi_byte < 128 else rssi_byte - 256
|
|
399
403
|
log_data["rssi"] = rssi
|
|
400
404
|
|
|
401
405
|
# Remaining bytes are the raw data payload
|
|
402
406
|
if len(data) > 3:
|
|
403
|
-
|
|
404
|
-
log_data["
|
|
407
|
+
payload=dbuf.read()
|
|
408
|
+
log_data["payload"] = payload.hex()
|
|
409
|
+
log_data["payload_length"] = len(payload)
|
|
405
410
|
|
|
406
411
|
attributes = {
|
|
407
412
|
"pubkey_prefix": log_data["raw_hex"],
|
|
@@ -470,8 +475,10 @@ class MessageReader:
|
|
|
470
475
|
logger.debug(f"Received telemetry data: {data.hex()}")
|
|
471
476
|
res = {}
|
|
472
477
|
|
|
473
|
-
|
|
474
|
-
|
|
478
|
+
dbuf.read(1)
|
|
479
|
+
|
|
480
|
+
res["pubkey_pre"] = dbuf.read(6).hex()
|
|
481
|
+
buf = dbuf.read()
|
|
475
482
|
|
|
476
483
|
"""Parse a given byte string and return as a LppFrame object."""
|
|
477
484
|
i = 0
|
|
@@ -498,29 +505,30 @@ class MessageReader:
|
|
|
498
505
|
|
|
499
506
|
elif packet_type_value == PacketType.BINARY_RESPONSE.value:
|
|
500
507
|
logger.debug(f"Received binary data: {data.hex()}")
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
508
|
+
dbuf.read(1)
|
|
509
|
+
tag = dbuf.read(4).hex()
|
|
510
|
+
response_data = dbuf.read()
|
|
511
|
+
|
|
504
512
|
# Always dispatch generic BINARY_RESPONSE
|
|
505
513
|
binary_res = {"tag": tag, "data": response_data.hex()}
|
|
506
514
|
await self.dispatcher.dispatch(
|
|
507
515
|
Event(EventType.BINARY_RESPONSE, binary_res, {"tag": tag})
|
|
508
516
|
)
|
|
509
|
-
|
|
517
|
+
|
|
510
518
|
# Check for tracked request type and dispatch specific response
|
|
511
519
|
if tag in self.pending_binary_requests:
|
|
512
520
|
request_type = self.pending_binary_requests[tag]["request_type"]
|
|
513
521
|
pubkey_prefix = self.pending_binary_requests[tag]["pubkey_prefix"]
|
|
514
522
|
del self.pending_binary_requests[tag]
|
|
515
523
|
logger.debug(f"Processing binary response for tag {tag}, type {request_type}, pubkey_prefix {pubkey_prefix}")
|
|
516
|
-
|
|
524
|
+
|
|
517
525
|
if request_type == BinaryReqType.STATUS and len(response_data) >= 52:
|
|
518
526
|
res = {}
|
|
519
527
|
res = parse_status(response_data, pubkey_prefix=pubkey_prefix)
|
|
520
528
|
await self.dispatcher.dispatch(
|
|
521
529
|
Event(EventType.STATUS_RESPONSE, res, {"pubkey_prefix": res["pubkey_pre"], "tag": tag})
|
|
522
530
|
)
|
|
523
|
-
|
|
531
|
+
|
|
524
532
|
elif request_type == BinaryReqType.TELEMETRY:
|
|
525
533
|
try:
|
|
526
534
|
lpp = lpp_parse(response_data)
|
|
@@ -530,7 +538,7 @@ class MessageReader:
|
|
|
530
538
|
)
|
|
531
539
|
except Exception as e:
|
|
532
540
|
logger.error(f"Error parsing binary telemetry response: {e}")
|
|
533
|
-
|
|
541
|
+
|
|
534
542
|
elif request_type == BinaryReqType.MMA:
|
|
535
543
|
try:
|
|
536
544
|
mma_result = lpp_parse_mma(response_data[4:]) # Skip 4-byte header
|
|
@@ -540,7 +548,7 @@ class MessageReader:
|
|
|
540
548
|
)
|
|
541
549
|
except Exception as e:
|
|
542
550
|
logger.error(f"Error parsing binary MMA response: {e}")
|
|
543
|
-
|
|
551
|
+
|
|
544
552
|
elif request_type == BinaryReqType.ACL:
|
|
545
553
|
try:
|
|
546
554
|
acl_result = parse_acl(response_data)
|
|
@@ -556,13 +564,14 @@ class MessageReader:
|
|
|
556
564
|
elif packet_type_value == PacketType.PATH_DISCOVERY_RESPONSE.value:
|
|
557
565
|
logger.debug(f"Received path discovery response: {data.hex()}")
|
|
558
566
|
res = {}
|
|
559
|
-
|
|
560
|
-
|
|
567
|
+
dbuf.read(1)
|
|
568
|
+
res["pubkey_pre"] = dbuf.read(6).hex()
|
|
569
|
+
opl = dbuf.read(1)[0]
|
|
561
570
|
res["out_path_len"] = opl
|
|
562
|
-
res["out_path"] =
|
|
563
|
-
ipl =
|
|
571
|
+
res["out_path"] = dbuf.read(opl).hex()
|
|
572
|
+
ipl = dbuf.read(1)[0]
|
|
564
573
|
res["in_path_len"] = ipl
|
|
565
|
-
res["in_path"] =
|
|
574
|
+
res["in_path"] = dbuf.read(ipl).hex()
|
|
566
575
|
|
|
567
576
|
attributes = {"pubkey_pre": res["pubkey_pre"]}
|
|
568
577
|
|
|
@@ -573,7 +582,7 @@ class MessageReader:
|
|
|
573
582
|
elif packet_type_value == PacketType.PRIVATE_KEY.value:
|
|
574
583
|
logger.debug(f"Received private key response: {data.hex()}")
|
|
575
584
|
if len(data) >= 65: # 1 byte response code + 64 bytes private key
|
|
576
|
-
private_key =
|
|
585
|
+
private_key = dbuf.read(64) # Extract 64-byte private key
|
|
577
586
|
res = {"private_key": private_key}
|
|
578
587
|
await self.dispatcher.dispatch(Event(EventType.PRIVATE_KEY, res))
|
|
579
588
|
else:
|
|
@@ -584,6 +593,43 @@ class MessageReader:
|
|
|
584
593
|
res = {"reason": "private_key_export_disabled"}
|
|
585
594
|
await self.dispatcher.dispatch(Event(EventType.DISABLED, res))
|
|
586
595
|
|
|
596
|
+
elif packet_type_value == PacketType.CONTROL_DATA.value:
|
|
597
|
+
logger.debug("Received control data packet")
|
|
598
|
+
res={}
|
|
599
|
+
res["SNR"] = int.from_bytes(dbuf.read(1), byteorder="little", signed=True) / 4
|
|
600
|
+
res["RSSI"] = int.from_bytes(dbuf.read(1), byteorder="little", signed=True)
|
|
601
|
+
res["path_len"] = dbuf.read(1)[0]
|
|
602
|
+
payload = dbuf.read()
|
|
603
|
+
payload_type = payload[0]
|
|
604
|
+
res["payload_type"] = payload_type
|
|
605
|
+
res["payload"] = payload
|
|
606
|
+
|
|
607
|
+
attributes = {"payload_type": payload_type}
|
|
608
|
+
await self.dispatcher.dispatch(
|
|
609
|
+
Event(EventType.CONTROL_DATA, res, attributes)
|
|
610
|
+
)
|
|
611
|
+
|
|
612
|
+
# decode NODE_DISCOVER_RESP
|
|
613
|
+
if payload_type & 0xF0 == ControlType.NODE_DISCOVER_RESP.value:
|
|
614
|
+
pbuf = io.BytesIO(payload[1:])
|
|
615
|
+
ndr = dict(res)
|
|
616
|
+
del ndr["payload_type"]
|
|
617
|
+
del ndr["payload"]
|
|
618
|
+
ndr["node_type"] = payload_type & 0x0F
|
|
619
|
+
ndr["SNR_in"] = int.from_bytes(pbuf.read(1), byteorder="little", signed=True)/4
|
|
620
|
+
ndr["tag"] = pbuf.read(4).hex()
|
|
621
|
+
ndr["pubkey"] = pbuf.read(32).hex()
|
|
622
|
+
|
|
623
|
+
attributes = {
|
|
624
|
+
"node_type" : ndr["node_type"],
|
|
625
|
+
"tag" : ndr["tag"],
|
|
626
|
+
"pubkey" : ndr["pubkey"],
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
await self.dispatcher.dispatch(
|
|
630
|
+
Event(EventType.DISCOVER_RESPONSE, ndr, attributes)
|
|
631
|
+
)
|
|
632
|
+
|
|
587
633
|
else:
|
|
588
634
|
logger.debug(f"Unhandled data received {data}")
|
|
589
635
|
logger.debug(f"Unhandled packet type: {packet_type_value}")
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|