Skip to content

Commit d3c9c8d

Browse files
committed
control codes support: node_discover_req
1 parent 057051c commit d3c9c8d

File tree

5 files changed

+99
-4
lines changed

5 files changed

+99
-4
lines changed

src/meshcore/commands/__init__.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,15 @@
77
from .contact import ContactCommands
88
from .device import DeviceCommands
99
from .messaging import MessagingCommands
10+
from .control_data import ControlDataCommandHandler
1011

1112

1213
class CommandHandler(
13-
DeviceCommands, ContactCommands, MessagingCommands, BinaryCommandHandler
14+
DeviceCommands,
15+
ContactCommands,
16+
MessagingCommands,
17+
BinaryCommandHandler,
18+
ControlDataCommandHandler
1419
):
1520
pass
1621

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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

src/meshcore/events.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ class EventType(Enum):
4444
PATH_RESPONSE = "path_response"
4545
PRIVATE_KEY = "private_key"
4646
DISABLED = "disabled"
47+
CONTROL_DATA = "control_data"
48+
DISCOVER_RESPONSE = "discover_response"
4749

4850
# Command response types
4951
OK = "command_ok"

src/meshcore/packets.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ class BinaryReqType(Enum):
77
MMA = 0x04
88
ACL = 0x05
99

10+
class ControlType(Enum):
11+
NODE_DISCOVER_REQ = 0x80
12+
NODE_DISCOVER_RESP = 0x90
13+
1014
# Packet prefixes for the protocol
1115
class PacketType(Enum):
1216
OK = 0
@@ -35,6 +39,7 @@ class PacketType(Enum):
3539
FACTORY_RESET = 51
3640
PATH_DISCOVERY = 52
3741
SET_FLOOD_SCOPE = 54
42+
SEND_CONTROL_DATA = 55
3843

3944
# Push notifications
4045
ADVERTISEMENT = 0x80
@@ -51,3 +56,4 @@ class PacketType(Enum):
5156
TELEMETRY_RESPONSE = 0x8B
5257
BINARY_RESPONSE = 0x8C
5358
PATH_DISCOVERY_RESPONSE = 0x8D
59+
CONTROL_DATA = 0x8E

src/meshcore/reader.py

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import io
55
from typing import Any, Dict
66
from .events import Event, EventType, EventDispatcher
7-
from .packets import BinaryReqType, PacketType
7+
from .packets import BinaryReqType, PacketType, ControlType
88
from .parsing import lpp_parse, lpp_parse_mma, parse_acl, parse_status
99
from cayennelpp import LppFrame, LppData
1010
from meshcore.lpp_json_encoder import lpp_json_encoder
@@ -331,8 +331,8 @@ async def handle_rx(self, data: bytearray):
331331

332332
elif packet_type_value == PacketType.RAW_DATA.value:
333333
res = {}
334-
res["SNR"] = dbuf.read(1)[0] / 4
335-
res["RSSI"] = dbuf.read(1)[0]
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)
336336
res["payload"] = dbuf.read(4).hex()
337337
logger.debug("Received raw data")
338338
print(res)
@@ -593,6 +593,43 @@ async def handle_rx(self, data: bytearray):
593593
res = {"reason": "private_key_export_disabled"}
594594
await self.dispatcher.dispatch(Event(EventType.DISABLED, res))
595595

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+
596633
else:
597634
logger.debug(f"Unhandled data received {data}")
598635
logger.debug(f"Unhandled packet type: {packet_type_value}")

0 commit comments

Comments
 (0)