meshcore 2.1.17__py3-none-any.whl → 2.1.24__py3-none-any.whl

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.

Potentially problematic release.


This version of meshcore might be problematic. Click here for more details.

Binary file
@@ -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, ContactCommands, MessagingCommands, BinaryCommandHandler
14
+ DeviceCommands,
15
+ ContactCommands,
16
+ MessagingCommands,
17
+ BinaryCommandHandler,
18
+ ControlDataCommandHandler
14
19
  ):
15
20
  pass
16
21
 
meshcore/commands/base.py CHANGED
@@ -163,7 +163,7 @@ class CommandHandlerBase:
163
163
  return Event(EventType.OK, {})
164
164
 
165
165
  # attached at base because its a common method
166
- async def send_binary_req(self, dst: DestinationType, request_type: BinaryReqType, data: Optional[bytes] = None, timeout=None, min_timeout=0) -> Event:
166
+ async def send_binary_req(self, dst: DestinationType, request_type: BinaryReqType, data: Optional[bytes] = None, context={}, timeout=None, min_timeout=0) -> Event:
167
167
  dst_bytes = _validate_destination(dst, prefix_length=32)
168
168
  pubkey_prefix = _validate_destination(dst, prefix_length=6)
169
169
  logger.debug(f"Binary request to {dst_bytes.hex()}")
@@ -180,6 +180,6 @@ class CommandHandlerBase:
180
180
  # Use provided timeout or fallback to suggested timeout (with 5s default)
181
181
  actual_timeout = timeout if timeout is not None and timeout > 0 else result.payload.get("suggested_timeout", 4000) / 800.0
182
182
  actual_timeout = min_timeout if actual_timeout < min_timeout else actual_timeout
183
- self._reader.register_binary_request(pubkey_prefix.hex(), exp_tag, request_type, actual_timeout)
183
+ self._reader.register_binary_request(pubkey_prefix.hex(), exp_tag, request_type, actual_timeout, context=context)
184
184
 
185
185
  return result
@@ -1,4 +1,6 @@
1
+ import asyncio
1
2
  import logging
3
+ import random
2
4
 
3
5
  from .base import CommandHandlerBase
4
6
  from ..events import EventType
@@ -131,3 +133,112 @@ class BinaryCommandHandler(CommandHandlerBase):
131
133
  )
132
134
 
133
135
  return acl_event.payload["acl_data"] if acl_event else None
136
+
137
+ async def req_neighbours_async(self,
138
+ contact,
139
+ count=255,
140
+ offset=0,
141
+ order_by=0,
142
+ pubkey_prefix_length=4,
143
+ timeout=0,
144
+ min_timeout=0
145
+ ):
146
+ req = (b"\x00" # version : 0
147
+ + count.to_bytes(1, "little", signed=False)
148
+ + offset.to_bytes(2, "little", signed=False)
149
+ + order_by.to_bytes(1, "little", signed=False)
150
+ + pubkey_prefix_length.to_bytes(1, "little", signed=False)
151
+ + random.randint(1, 0xFFFFFFFF).to_bytes(4, "little", signed=False)
152
+ )
153
+
154
+ logger.debug(f"Sending binary neighbours req, count: {count}, offset: {offset} {req.hex()}")
155
+
156
+ return await self.send_binary_req (
157
+ contact,
158
+ BinaryReqType.NEIGHBOURS,
159
+ data=req,
160
+ timeout=timeout,
161
+ context={"pubkey_prefix_length": pubkey_prefix_length}
162
+ )
163
+
164
+ async def req_neighbours_sync(self,
165
+ contact,
166
+ count=255,
167
+ offset=0,
168
+ order_by=0,
169
+ pubkey_prefix_length=4,
170
+ timeout=0,
171
+ min_timeout=0
172
+ ):
173
+
174
+ res = await self.req_neighbours_async(contact,
175
+ count=count,
176
+ offset=offset,
177
+ order_by=order_by,
178
+ pubkey_prefix_length=pubkey_prefix_length,
179
+ timeout=timeout,
180
+ min_timeout=min_timeout)
181
+
182
+ if res is None or res.type == EventType.ERROR:
183
+ return None
184
+
185
+ timeout = res.payload["suggested_timeout"] / 800 if timeout == 0 else timeout
186
+ timeout = timeout if min_timeout < timeout else min_timeout
187
+
188
+ if self.dispatcher is None:
189
+ return None
190
+
191
+ # Listen for NEIGHBOUR_RESPONSE
192
+ neighbours_event = await self.dispatcher.wait_for_event(
193
+ EventType.NEIGHBOURS_RESPONSE,
194
+ attribute_filters={"tag": res.payload["expected_ack"].hex()},
195
+ timeout=timeout,
196
+ )
197
+
198
+ return neighbours_event.payload if neighbours_event else None
199
+
200
+ # do several queries if not all neighbours have been obtained
201
+ async def fetch_all_neighbours(self,
202
+ contact,
203
+ order_by=0,
204
+ pubkey_prefix_length=4,
205
+ timeout=0,
206
+ min_timeout=0
207
+ ):
208
+
209
+ # Initial request
210
+ res = await self.req_neighbours_sync(contact,
211
+ count=255,
212
+ offset=0,
213
+ order_by=order_by,
214
+ pubkey_prefix_length=pubkey_prefix_length,
215
+ timeout=timeout,
216
+ min_timeout=min_timeout)
217
+
218
+ if res is None:
219
+ return None
220
+
221
+ neighbours_count = res["neighbours_count"] # total neighbours
222
+ results_count = res["results_count"] # obtained neighbours
223
+
224
+ del res["tag"]
225
+
226
+ while results_count < neighbours_count:
227
+ #await asyncio.sleep(2) # wait 2s before next fetch
228
+ next_res = await self.req_neighbours_sync(contact,
229
+ count=255,
230
+ offset=results_count,
231
+ order_by=order_by,
232
+ pubkey_prefix_length=pubkey_prefix_length,
233
+ timeout=timeout,
234
+ min_timeout=min_timeout+5) # requests are close, so let's have some more timeout
235
+
236
+ if next_res is None :
237
+ return res # caller should check it has everything
238
+
239
+ results_count = results_count + next_res["results_count"]
240
+
241
+ res["results_count"] = results_count
242
+ res["neighbours"] += next_res["neighbours"]
243
+
244
+ return res
@@ -0,0 +1,50 @@
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: int, payload: bytes) -> Event:
14
+ data = bytearray([PacketType.SEND_CONTROL_DATA.value])
15
+ data.extend(control_type.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
+ prefix_only: bool=True,
25
+ tag: int=None,
26
+ since: int=None
27
+ ) -> Event:
28
+
29
+ if tag is None:
30
+ tag = random.randint(1, 0xFFFFFFFF)
31
+
32
+ data = bytearray()
33
+ data.extend(filter.to_bytes(1, "little", signed=False))
34
+ data.extend(tag.to_bytes(4, "little"))
35
+ if not since is None:
36
+ data.extend(since.to_bytes(4, "little", signed=False))
37
+
38
+ logger.debug(f"sending node discover req {data.hex()}")
39
+
40
+ flags = 0
41
+ flags = flags | 1 if prefix_only else flags
42
+
43
+ res = await self.send_control_data(
44
+ ControlType.NODE_DISCOVER_REQ.value|flags, data)
45
+
46
+ if res is None:
47
+ return None
48
+ else:
49
+ res.payload["tag"] = tag
50
+ 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
- self,
92
- manual_add_contacts: bool,
93
- telemetry_mode_base: int,
94
- telemetry_mode_loc: int,
95
- telemetry_mode_env: int,
96
- advert_loc_policy: int,
97
- ) -> Event:
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
- return await self.set_other_params(
114
- infos["manual_add_contacts"],
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
- return await self.set_other_params(
124
- infos["manual_add_contacts"],
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
- return await self.set_other_params(
134
- infos["manual_add_contacts"],
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
- return await self.set_other_params(
144
- manual_add_contacts,
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
- return await self.set_other_params(
154
- infos["manual_add_contacts"],
155
- infos["telemetry_mode_base"],
156
- infos["telemetry_mode_loc"],
157
- infos["telemetry_mode_env"],
158
- advert_loc_policy,
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])
meshcore/events.py CHANGED
@@ -44,6 +44,9 @@ class EventType(Enum):
44
44
  PATH_RESPONSE = "path_response"
45
45
  PRIVATE_KEY = "private_key"
46
46
  DISABLED = "disabled"
47
+ CONTROL_DATA = "control_data"
48
+ DISCOVER_RESPONSE = "discover_response"
49
+ NEIGHBOURS_RESPONSE = "neighbours_response"
47
50
 
48
51
  # Command response types
49
52
  OK = "command_ok"
meshcore/packets.py CHANGED
@@ -6,6 +6,11 @@ class BinaryReqType(Enum):
6
6
  TELEMETRY = 0x03
7
7
  MMA = 0x04
8
8
  ACL = 0x05
9
+ NEIGHBOURS = 0x06
10
+
11
+ class ControlType(Enum):
12
+ NODE_DISCOVER_REQ = 0x80
13
+ NODE_DISCOVER_RESP = 0x90
9
14
 
10
15
  # Packet prefixes for the protocol
11
16
  class PacketType(Enum):
@@ -33,6 +38,9 @@ class PacketType(Enum):
33
38
  CUSTOM_VARS = 21
34
39
  BINARY_REQ = 50
35
40
  FACTORY_RESET = 51
41
+ PATH_DISCOVERY = 52
42
+ SET_FLOOD_SCOPE = 54
43
+ SEND_CONTROL_DATA = 55
36
44
 
37
45
  # Push notifications
38
46
  ADVERTISEMENT = 0x80
@@ -49,3 +57,4 @@ class PacketType(Enum):
49
57
  TELEMETRY_RESPONSE = 0x8B
50
58
  BINARY_RESPONSE = 0x8C
51
59
  PATH_DISCOVERY_RESPONSE = 0x8D
60
+ CONTROL_DATA = 0x8E
meshcore/reader.py CHANGED
@@ -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,20 +19,21 @@ 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
 
25
- def register_binary_request(self, prefix: str, tag: str, request_type: BinaryReqType, timeout_seconds: float):
26
+ def register_binary_request(self, prefix: str, tag: str, request_type: BinaryReqType, timeout_seconds: float, context={}):
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,
33
34
  "pubkey_prefix": prefix,
34
- "expires_at": expires_at
35
+ "expires_at": expires_at,
36
+ "context": context # optional info we want to keep from req to resp
35
37
  }
36
38
  logger.debug(f"Registered binary request: tag={tag}, type={request_type}, expires in {timeout_seconds}s")
37
39
 
@@ -42,13 +44,14 @@ class MessageReader:
42
44
  tag for tag, info in self.pending_binary_requests.items()
43
45
  if current_time > info["expires_at"]
44
46
  ]
45
-
47
+
46
48
  for tag in expired_tags:
47
49
  logger.debug(f"Cleaning up expired binary request: tag={tag}")
48
50
  del self.pending_binary_requests[tag]
49
-
51
+
50
52
  async def handle_rx(self, data: bytearray):
51
- packet_type_value = data[0]
53
+ dbuf = io.BytesIO(data)
54
+ packet_type_value = dbuf.read(1)[0]
52
55
  logger.debug(f"Received data: {data.hex()}")
53
56
 
54
57
  # Handle command responses
@@ -78,23 +81,24 @@ class MessageReader:
78
81
  or packet_type_value == PacketType.PUSH_CODE_NEW_ADVERT.value
79
82
  ):
80
83
  c = {}
81
- c["public_key"] = data[1:33].hex()
82
- c["type"] = data[33]
83
- c["flags"] = data[34]
84
- c["out_path_len"] = int.from_bytes(data[35:36], signed=True, byteorder="little")
85
- plen = int.from_bytes(data[35:36], signed=True, byteorder="little")
84
+ c["public_key"] = dbuf.read(32).hex()
85
+ c["type"] = dbuf.read(1)[0]
86
+ c["flags"] = dbuf.read(1)[0]
87
+ plen = int.from_bytes(dbuf.read(1), signed=True, byteorder="little")
88
+ c["out_path_len"] = plen
86
89
  if plen == -1:
87
90
  plen = 0
88
- c["out_path"] = data[36 : 36 + plen].hex()
89
- c["adv_name"] = data[100:132].decode("utf-8", "ignore").replace("\0", "")
90
- c["last_advert"] = int.from_bytes(data[132:136], byteorder="little")
91
+ path = dbuf.read(64)
92
+ c["out_path"] = path[0:plen].hex()
93
+ c["adv_name"] = dbuf.read(32).decode("utf-8", "ignore").replace("\0", "")
94
+ c["last_advert"] = int.from_bytes(dbuf.read(4), byteorder="little")
91
95
  c["adv_lat"] = (
92
- int.from_bytes(data[136:140], byteorder="little", signed=True) / 1e6
96
+ int.from_bytes(dbuf.read(4), byteorder="little", signed=True) / 1e6
93
97
  )
94
98
  c["adv_lon"] = (
95
- int.from_bytes(data[140:144], byteorder="little", signed=True) / 1e6
99
+ int.from_bytes(dbuf.read(4), byteorder="little", signed=True) / 1e6
96
100
  )
97
- c["lastmod"] = int.from_bytes(data[144:148], byteorder="little")
101
+ c["lastmod"] = int.from_bytes(dbuf.read(4), byteorder="little")
98
102
 
99
103
  if packet_type_value == PacketType.PUSH_CODE_NEW_ADVERT.value:
100
104
  await self.dispatcher.dispatch(Event(EventType.NEW_CONTACT, c))
@@ -103,7 +107,7 @@ class MessageReader:
103
107
  self.contacts[c["public_key"]] = c
104
108
 
105
109
  elif packet_type_value == PacketType.CONTACT_END.value:
106
- lastmod = int.from_bytes(data[1:5], byteorder="little")
110
+ lastmod = int.from_bytes(dbuf.read(4), byteorder="little")
107
111
  attributes = {
108
112
  "lastmod": lastmod,
109
113
  }
@@ -113,37 +117,39 @@ class MessageReader:
113
117
 
114
118
  elif packet_type_value == PacketType.SELF_INFO.value:
115
119
  self_info = {}
116
- self_info["adv_type"] = data[1]
117
- self_info["tx_power"] = data[2]
118
- self_info["max_tx_power"] = data[3]
119
- self_info["public_key"] = data[4:36].hex()
120
+ self_info["adv_type"] = dbuf.read(1)[0]
121
+ self_info["tx_power"] = dbuf.read(1)[0]
122
+ self_info["max_tx_power"] = dbuf.read(1)[0]
123
+ self_info["public_key"] = dbuf.read(32).hex()
120
124
  self_info["adv_lat"] = (
121
- int.from_bytes(data[36:40], byteorder="little", signed=True) / 1e6
125
+ int.from_bytes(dbuf.read(4), byteorder="little", signed=True) / 1e6
122
126
  )
123
127
  self_info["adv_lon"] = (
124
- int.from_bytes(data[40:44], byteorder="little", signed=True) / 1e6
128
+ int.from_bytes(dbuf.read(4), byteorder="little", signed=True) / 1e6
125
129
  )
126
- self_info["adv_loc_policy"] = data[45]
127
- self_info["telemetry_mode_env"] = (data[46] >> 4) & 0b11
128
- self_info["telemetry_mode_loc"] = (data[46] >> 2) & 0b11
129
- self_info["telemetry_mode_base"] = (data[46]) & 0b11
130
- self_info["manual_add_contacts"] = data[47] > 0
130
+ self_info["multi_acks"] = dbuf.read(1)[0]
131
+ self_info["adv_loc_policy"] = dbuf.read(1)[0]
132
+ telemetry_mode = dbuf.read(1)[0]
133
+ self_info["telemetry_mode_env"] = (telemetry_mode >> 4) & 0b11
134
+ self_info["telemetry_mode_loc"] = (telemetry_mode >> 2) & 0b11
135
+ self_info["telemetry_mode_base"] = (telemetry_mode) & 0b11
136
+ self_info["manual_add_contacts"] = dbuf.read(1)[0] > 0
131
137
  self_info["radio_freq"] = (
132
- int.from_bytes(data[48:52], byteorder="little") / 1000
138
+ int.from_bytes(dbuf.read(4), byteorder="little") / 1000
133
139
  )
134
140
  self_info["radio_bw"] = (
135
- int.from_bytes(data[52:56], byteorder="little") / 1000
141
+ int.from_bytes(dbuf.read(4), byteorder="little") / 1000
136
142
  )
137
- self_info["radio_sf"] = data[56]
138
- self_info["radio_cr"] = data[57]
139
- self_info["name"] = data[58:].decode("utf-8", "ignore")
143
+ self_info["radio_sf"] = dbuf.read(1)[0]
144
+ self_info["radio_cr"] = dbuf.read(1)[0]
145
+ self_info["name"] = dbuf.read().decode("utf-8", "ignore")
140
146
  await self.dispatcher.dispatch(Event(EventType.SELF_INFO, self_info))
141
147
 
142
148
  elif packet_type_value == PacketType.MSG_SENT.value:
143
149
  res = {}
144
- res["type"] = data[1]
145
- res["expected_ack"] = bytes(data[2:6])
146
- res["suggested_timeout"] = int.from_bytes(data[6:10], byteorder="little")
150
+ res["type"] = dbuf.read(1)[0]
151
+ res["expected_ack"] = dbuf.read(4)
152
+ res["suggested_timeout"] = int.from_bytes(dbuf.read(4), byteorder="little")
147
153
 
148
154
  attributes = {
149
155
  "type": res["type"],
@@ -155,15 +161,14 @@ class MessageReader:
155
161
  elif packet_type_value == PacketType.CONTACT_MSG_RECV.value:
156
162
  res = {}
157
163
  res["type"] = "PRIV"
158
- res["pubkey_prefix"] = data[1:7].hex()
159
- res["path_len"] = data[7]
160
- res["txt_type"] = data[8]
161
- res["sender_timestamp"] = int.from_bytes(data[9:13], byteorder="little")
162
- if data[8] == 2:
163
- res["signature"] = data[13:17].hex()
164
- res["text"] = data[17:].decode("utf-8", "ignore")
165
- else:
166
- res["text"] = data[13:].decode("utf-8", "ignore")
164
+ res["pubkey_prefix"] = dbuf.read(6).hex()
165
+ res["path_len"] = dbuf.read(1)[0]
166
+ txt_type = dbuf.read(1)[0]
167
+ res["txt_type"] = txt_type
168
+ res["sender_timestamp"] = int.from_bytes(dbuf.read(4), byteorder="little")
169
+ if txt_type == 2:
170
+ res["signature"] = dbuf.read(4).hex()
171
+ res["text"] = dbuf.read().decode("utf-8", "ignore")
167
172
 
168
173
  attributes = {
169
174
  "pubkey_prefix": res["pubkey_prefix"],
@@ -177,16 +182,16 @@ class MessageReader:
177
182
  elif packet_type_value == 16: # A reply to CMD_SYNC_NEXT_MESSAGE (ver >= 3)
178
183
  res = {}
179
184
  res["type"] = "PRIV"
180
- res["SNR"] = int.from_bytes(data[1:2], byteorder="little", signed=True) / 4
181
- res["pubkey_prefix"] = data[4:10].hex()
182
- res["path_len"] = data[10]
183
- res["txt_type"] = data[11]
184
- res["sender_timestamp"] = int.from_bytes(data[12:16], byteorder="little")
185
- if data[11] == 2:
186
- res["signature"] = data[16:20].hex()
187
- res["text"] = data[20:].decode("utf-8", "ignore")
188
- else:
189
- res["text"] = data[16:].decode("utf-8", "ignore")
185
+ res["SNR"] = int.from_bytes(dbuf.read(1), byteorder="little", signed=True) / 4
186
+ dbuf.read(2) # reserved
187
+ res["pubkey_prefix"] = dbuf.read(6).hex()
188
+ res["path_len"] = dbuf.read(1)[0]
189
+ txt_type = dbuf.read(1)[0]
190
+ res["txt_type"] = txt_type
191
+ res["sender_timestamp"] = int.from_bytes(dbuf.read(4), byteorder="little")
192
+ if txt_type == 2:
193
+ res["signature"] = dbuf.read(4).hex()
194
+ res["text"] = dbuf.read().decode("utf-8", "ignore")
190
195
 
191
196
  attributes = {
192
197
  "pubkey_prefix": res["pubkey_prefix"],
@@ -200,11 +205,11 @@ class MessageReader:
200
205
  elif packet_type_value == PacketType.CHANNEL_MSG_RECV.value:
201
206
  res = {}
202
207
  res["type"] = "CHAN"
203
- res["channel_idx"] = data[1]
204
- res["path_len"] = data[2]
205
- res["txt_type"] = data[3]
206
- res["sender_timestamp"] = int.from_bytes(data[4:8], byteorder="little")
207
- res["text"] = data[8:].decode("utf-8", "ignore")
208
+ res["channel_idx"] = dbuf.read(1)[0]
209
+ res["path_len"] = dbuf.read(1)[0]
210
+ res["txt_type"] = dbuf.read(1)[0]
211
+ res["sender_timestamp"] = int.from_bytes(dbuf.read(4), byteorder="little")
212
+ res["text"] = dbuf.read().decode("utf-8", "ignore")
208
213
 
209
214
  attributes = {
210
215
  "channel_idx": res["channel_idx"],
@@ -218,12 +223,13 @@ class MessageReader:
218
223
  elif packet_type_value == 17: # A reply to CMD_SYNC_NEXT_MESSAGE (ver >= 3)
219
224
  res = {}
220
225
  res["type"] = "CHAN"
221
- res["SNR"] = int.from_bytes(data[1:2], byteorder="little", signed=True) / 4
222
- res["channel_idx"] = data[4]
223
- res["path_len"] = data[5]
224
- res["txt_type"] = data[6]
225
- res["sender_timestamp"] = int.from_bytes(data[7:11], byteorder="little")
226
- res["text"] = data[11:].decode("utf-8", "ignore")
226
+ res["SNR"] = int.from_bytes(dbuf.read(1), byteorder="little", signed=True) / 4
227
+ dbuf.read(2) # reserved
228
+ res["channel_idx"] = dbuf.read(1)[0]
229
+ res["path_len"] = dbuf.read(1)[0]
230
+ res["txt_type"] = dbuf.read(1)[0]
231
+ res["sender_timestamp"] = int.from_bytes(dbuf.read(4), byteorder="little")
232
+ res["text"] = dbuf.read().decode("utf-8", "ignore")
227
233
 
228
234
  attributes = {
229
235
  "channel_idx": res["channel_idx"],
@@ -235,7 +241,7 @@ class MessageReader:
235
241
  )
236
242
 
237
243
  elif packet_type_value == PacketType.CURRENT_TIME.value:
238
- time_value = int.from_bytes(data[1:5], byteorder="little")
244
+ time_value = int.from_bytes(dbuf.read(4), byteorder="little")
239
245
  result = {"time": time_value}
240
246
  await self.dispatcher.dispatch(Event(EventType.CURRENT_TIME, result))
241
247
 
@@ -244,34 +250,34 @@ class MessageReader:
244
250
  await self.dispatcher.dispatch(Event(EventType.NO_MORE_MSGS, result))
245
251
 
246
252
  elif packet_type_value == PacketType.CONTACT_URI.value:
247
- contact_uri = "meshcore://" + data[1:].hex()
253
+ contact_uri = "meshcore://" + dbuf.read().hex()
248
254
  result = {"uri": contact_uri}
249
255
  await self.dispatcher.dispatch(Event(EventType.CONTACT_URI, result))
250
256
 
251
257
  elif packet_type_value == PacketType.BATTERY.value:
252
- battery_level = int.from_bytes(data[1:3], byteorder="little")
258
+ battery_level = int.from_bytes(dbuf.read(2), byteorder="little")
253
259
  result = {"level": battery_level}
254
260
  if len(data) > 3: # has storage info as well
255
- result["used_kb"] = int.from_bytes(data[3:7], byteorder="little")
256
- result["total_kb"] = int.from_bytes(data[7:11], byteorder="little")
261
+ result["used_kb"] = int.from_bytes(dbuf.read(4), byteorder="little")
262
+ result["total_kb"] = int.from_bytes(dbuf.read(4), byteorder="little")
257
263
  await self.dispatcher.dispatch(Event(EventType.BATTERY, result))
258
264
 
259
265
  elif packet_type_value == PacketType.DEVICE_INFO.value:
260
266
  res = {}
261
- res["fw ver"] = data[1]
267
+ res["fw ver"] = dbuf.read(1)[0]
262
268
  if data[1] >= 3:
263
- res["max_contacts"] = data[2] * 2
264
- res["max_channels"] = data[3]
265
- res["ble_pin"] = int.from_bytes(data[4:8], byteorder="little")
266
- res["fw_build"] = data[8:20].decode("utf-8", "ignore").replace("\0", "")
267
- res["model"] = data[20:60].decode("utf-8", "ignore").replace("\0", "")
268
- res["ver"] = data[60:80].decode("utf-8", "ignore").replace("\0", "")
269
+ res["max_contacts"] = dbuf.read(1)[0] * 2
270
+ res["max_channels"] = dbuf.read(1)[0]
271
+ res["ble_pin"] = int.from_bytes(dbuf.read(4), byteorder="little")
272
+ res["fw_build"] = dbuf.read(12).decode("utf-8", "ignore").replace("\0", "")
273
+ res["model"] = dbuf.read(40).decode("utf-8", "ignore").replace("\0", "")
274
+ res["ver"] = dbuf.read(20).decode("utf-8", "ignore").replace("\0", "")
269
275
  await self.dispatcher.dispatch(Event(EventType.DEVICE_INFO, res))
270
276
 
271
277
  elif packet_type_value == PacketType.CUSTOM_VARS.value:
272
278
  logger.debug(f"received custom vars response: {data.hex()}")
273
279
  res = {}
274
- rawdata = data[1:].decode("utf-8", "ignore")
280
+ rawdata = dbuf.read().decode("utf-8", "ignore")
275
281
  if not rawdata == "":
276
282
  pairs = rawdata.split(",")
277
283
  for p in pairs:
@@ -283,30 +289,30 @@ class MessageReader:
283
289
  elif packet_type_value == PacketType.CHANNEL_INFO.value:
284
290
  logger.debug(f"received channel info response: {data.hex()}")
285
291
  res = {}
286
- res["channel_idx"] = data[1]
292
+ res["channel_idx"] = dbuf.read(1)[0]
287
293
 
288
294
  # Channel name is null-terminated, so find the first null byte
289
- name_bytes = data[2:34]
295
+ name_bytes = dbuf.read(32)
290
296
  null_pos = name_bytes.find(0)
291
297
  if null_pos >= 0:
292
298
  res["channel_name"] = name_bytes[:null_pos].decode("utf-8", "ignore")
293
299
  else:
294
300
  res["channel_name"] = name_bytes.decode("utf-8", "ignore")
295
301
 
296
- res["channel_secret"] = data[34:50]
302
+ res["channel_secret"] = dbuf.read(16)
297
303
  await self.dispatcher.dispatch(Event(EventType.CHANNEL_INFO, res, res))
298
304
 
299
305
  # Push notifications
300
306
  elif packet_type_value == PacketType.ADVERTISEMENT.value:
301
307
  logger.debug("Advertisement received")
302
308
  res = {}
303
- res["public_key"] = data[1:33].hex()
309
+ res["public_key"] = dbuf.read(32).hex()
304
310
  await self.dispatcher.dispatch(Event(EventType.ADVERTISEMENT, res, res))
305
311
 
306
312
  elif packet_type_value == PacketType.PATH_UPDATE.value:
307
313
  logger.debug("Code path update")
308
314
  res = {}
309
- res["public_key"] = data[1:33].hex()
315
+ res["public_key"] = dbuf.read(32).hex()
310
316
  await self.dispatcher.dispatch(Event(EventType.PATH_UPDATE, res, res))
311
317
 
312
318
  elif packet_type_value == PacketType.ACK.value:
@@ -314,7 +320,7 @@ class MessageReader:
314
320
  ack_data = {}
315
321
 
316
322
  if len(data) >= 5:
317
- ack_data["code"] = bytes(data[1:5]).hex()
323
+ ack_data["code"] = dbuf.read(4).hex()
318
324
 
319
325
  attributes = {"code": ack_data.get("code", "")}
320
326
 
@@ -326,23 +332,24 @@ class MessageReader:
326
332
 
327
333
  elif packet_type_value == PacketType.RAW_DATA.value:
328
334
  res = {}
329
- res["SNR"] = data[1] / 4
330
- res["RSSI"] = data[2]
331
- res["payload"] = data[4:].hex()
335
+ res["SNR"] = int.from_bytes(dbuf.read(1), byteorder="little", signed=True) / 4
336
+ res["RSSI"] = int.from_bytes(dbuf.read(1), byteorder="little", signed=True)
337
+ res["payload"] = dbuf.read(4).hex()
332
338
  logger.debug("Received raw data")
333
339
  print(res)
334
340
  await self.dispatcher.dispatch(Event(EventType.RAW_DATA, res))
335
341
 
336
342
  elif packet_type_value == PacketType.LOGIN_SUCCESS.value:
337
343
  res = {}
344
+ attributes = {}
338
345
  if len(data) > 1:
339
- res["permissions"] = data[1]
340
- res["is_admin"] = (data[1] & 1) == 1 # Check if admin bit is set
346
+ perms = dbuf.read(1)[0]
347
+ res["permissions"] = perms
348
+ res["is_admin"] = (perms & 1) == 1 # Check if admin bit is set
341
349
 
342
- if len(data) > 7:
343
- res["pubkey_prefix"] = data[2:8].hex()
350
+ res["pubkey_prefix"] = dbuf.read(6).hex()
344
351
 
345
- attributes = {"pubkey_prefix": res.get("pubkey_prefix")}
352
+ attributes = {"pubkey_prefix": res.get("pubkey_prefix")}
346
353
 
347
354
  await self.dispatcher.dispatch(
348
355
  Event(EventType.LOGIN_SUCCESS, res, attributes)
@@ -350,11 +357,14 @@ class MessageReader:
350
357
 
351
358
  elif packet_type_value == PacketType.LOGIN_FAILED.value:
352
359
  res = {}
360
+ attributes = {}
361
+
362
+ pbuf.read(1)
353
363
 
354
364
  if len(data) > 7:
355
- res["pubkey_prefix"] = data[2:8].hex()
365
+ res["pubkey_prefix"] = pbuf.read(6).hex()
356
366
 
357
- attributes = {"pubkey_prefix": res.get("pubkey_prefix")}
367
+ attributes = {"pubkey_prefix": res.get("pubkey_prefix")}
358
368
 
359
369
  await self.dispatcher.dispatch(
360
370
  Event(EventType.LOGIN_FAILED, res, attributes)
@@ -368,12 +378,7 @@ class MessageReader:
368
378
  attributes = {
369
379
  "pubkey_prefix": res["pubkey_pre"],
370
380
  }
371
- data_hex = data[8:].hex()
372
- logger.debug(f"Status response: {data_hex}")
373
381
 
374
- attributes = {
375
- "pubkey_prefix": res["pubkey_pre"],
376
- }
377
382
  await self.dispatcher.dispatch(
378
383
  Event(EventType.STATUS_RESPONSE, res, attributes)
379
384
  )
@@ -386,22 +391,23 @@ class MessageReader:
386
391
 
387
392
  # First byte is SNR (signed byte, multiplied by 4)
388
393
  if len(data) > 1:
389
- snr_byte = data[1]
394
+ snr_byte = dbuf.read(1)[0]
390
395
  # Convert to signed value
391
396
  snr = (snr_byte if snr_byte < 128 else snr_byte - 256) / 4.0
392
397
  log_data["snr"] = snr
393
398
 
394
399
  # Second byte is RSSI (signed byte)
395
400
  if len(data) > 2:
396
- rssi_byte = data[2]
401
+ rssi_byte = dbuf.read(1)[0]
397
402
  # Convert to signed value
398
403
  rssi = rssi_byte if rssi_byte < 128 else rssi_byte - 256
399
404
  log_data["rssi"] = rssi
400
405
 
401
406
  # Remaining bytes are the raw data payload
402
407
  if len(data) > 3:
403
- log_data["payload"] = data[3:].hex()
404
- log_data["payload_length"] = len(data) - 3
408
+ payload=dbuf.read()
409
+ log_data["payload"] = payload.hex()
410
+ log_data["payload_length"] = len(payload)
405
411
 
406
412
  attributes = {
407
413
  "pubkey_prefix": log_data["raw_hex"],
@@ -470,8 +476,10 @@ class MessageReader:
470
476
  logger.debug(f"Received telemetry data: {data.hex()}")
471
477
  res = {}
472
478
 
473
- res["pubkey_pre"] = data[2:8].hex()
474
- buf = data[8:]
479
+ dbuf.read(1)
480
+
481
+ res["pubkey_pre"] = dbuf.read(6).hex()
482
+ buf = dbuf.read()
475
483
 
476
484
  """Parse a given byte string and return as a LppFrame object."""
477
485
  i = 0
@@ -498,29 +506,31 @@ class MessageReader:
498
506
 
499
507
  elif packet_type_value == PacketType.BINARY_RESPONSE.value:
500
508
  logger.debug(f"Received binary data: {data.hex()}")
501
- tag = data[2:6].hex()
502
- response_data = data[6:]
503
-
509
+ dbuf.read(1)
510
+ tag = dbuf.read(4).hex()
511
+ response_data = dbuf.read()
512
+
504
513
  # Always dispatch generic BINARY_RESPONSE
505
514
  binary_res = {"tag": tag, "data": response_data.hex()}
506
515
  await self.dispatcher.dispatch(
507
516
  Event(EventType.BINARY_RESPONSE, binary_res, {"tag": tag})
508
517
  )
509
-
518
+
510
519
  # Check for tracked request type and dispatch specific response
511
520
  if tag in self.pending_binary_requests:
512
521
  request_type = self.pending_binary_requests[tag]["request_type"]
513
522
  pubkey_prefix = self.pending_binary_requests[tag]["pubkey_prefix"]
523
+ context = self.pending_binary_requests[tag]["context"]
514
524
  del self.pending_binary_requests[tag]
515
525
  logger.debug(f"Processing binary response for tag {tag}, type {request_type}, pubkey_prefix {pubkey_prefix}")
516
-
526
+
517
527
  if request_type == BinaryReqType.STATUS and len(response_data) >= 52:
518
528
  res = {}
519
529
  res = parse_status(response_data, pubkey_prefix=pubkey_prefix)
520
530
  await self.dispatcher.dispatch(
521
531
  Event(EventType.STATUS_RESPONSE, res, {"pubkey_prefix": res["pubkey_pre"], "tag": tag})
522
532
  )
523
-
533
+
524
534
  elif request_type == BinaryReqType.TELEMETRY:
525
535
  try:
526
536
  lpp = lpp_parse(response_data)
@@ -530,7 +540,7 @@ class MessageReader:
530
540
  )
531
541
  except Exception as e:
532
542
  logger.error(f"Error parsing binary telemetry response: {e}")
533
-
543
+
534
544
  elif request_type == BinaryReqType.MMA:
535
545
  try:
536
546
  mma_result = lpp_parse_mma(response_data[4:]) # Skip 4-byte header
@@ -540,7 +550,7 @@ class MessageReader:
540
550
  )
541
551
  except Exception as e:
542
552
  logger.error(f"Error parsing binary MMA response: {e}")
543
-
553
+
544
554
  elif request_type == BinaryReqType.ACL:
545
555
  try:
546
556
  acl_result = parse_acl(response_data)
@@ -550,19 +560,54 @@ class MessageReader:
550
560
  )
551
561
  except Exception as e:
552
562
  logger.error(f"Error parsing binary ACL response: {e}")
563
+
564
+ elif request_type == BinaryReqType.NEIGHBOURS:
565
+ try:
566
+ pk_plen = context["pubkey_prefix_length"]
567
+ bbuf = io.BytesIO(response_data)
568
+
569
+ res = {
570
+ "pubkey_prefix": pubkey_prefix,
571
+ "tag": tag
572
+ }
573
+ res.update(context) # add context in result
574
+
575
+ res["neighbours_count"] = int.from_bytes(bbuf.read(2), "little", signed=True)
576
+ results_count = int.from_bytes(bbuf.read(2), "little", signed=True)
577
+ res["results_count"] = results_count
578
+
579
+ neighbours_list = []
580
+
581
+ for _ in range (results_count):
582
+ neighb = {}
583
+ neighb["pubkey"] = bbuf.read(pk_plen).hex()
584
+ neighb["secs_ago"] = int.from_bytes(bbuf.read(4), "little", signed=True)
585
+ neighb["snr"] = int.from_bytes(bbuf.read(1), "little", signed=True) / 4
586
+ neighbours_list.append(neighb)
587
+
588
+ res["neighbours"] = neighbours_list
589
+
590
+ await self.dispatcher.dispatch(
591
+ Event(EventType.NEIGHBOURS_RESPONSE, res, {"tag": tag, "pubkey_prefix": pubkey_prefix})
592
+ )
593
+
594
+ except Exception as e:
595
+ logger.error(f"Error parsing binary NEIGHBOURS response: {e}")
596
+
553
597
  else:
554
598
  logger.debug(f"No tracked request found for binary response tag {tag}")
555
599
 
556
600
  elif packet_type_value == PacketType.PATH_DISCOVERY_RESPONSE.value:
557
601
  logger.debug(f"Received path discovery response: {data.hex()}")
558
602
  res = {}
559
- res["pubkey_pre"] = data[2:8].hex()
560
- opl = data[8]
603
+ dbuf.read(1)
604
+ res["pubkey_pre"] = dbuf.read(6).hex()
605
+ opl = dbuf.read(1)[0]
561
606
  res["out_path_len"] = opl
562
- res["out_path"] = data[9 : 9 + opl].hex()
563
- ipl = data[9 + opl]
607
+ res["out_path"] = dbuf.read(opl).hex()
608
+ ipl = dbuf.read(1)[0]
564
609
  res["in_path_len"] = ipl
565
- res["in_path"] = data[10 + opl : 10 + opl + ipl].hex()
610
+ res["in_path"] = dbuf.read(ipl).hex()
566
611
 
567
612
  attributes = {"pubkey_pre": res["pubkey_pre"]}
568
613
 
@@ -573,7 +618,7 @@ class MessageReader:
573
618
  elif packet_type_value == PacketType.PRIVATE_KEY.value:
574
619
  logger.debug(f"Received private key response: {data.hex()}")
575
620
  if len(data) >= 65: # 1 byte response code + 64 bytes private key
576
- private_key = data[1:65] # Extract 64-byte private key
621
+ private_key = dbuf.read(64) # Extract 64-byte private key
577
622
  res = {"private_key": private_key}
578
623
  await self.dispatcher.dispatch(Event(EventType.PRIVATE_KEY, res))
579
624
  else:
@@ -584,6 +629,50 @@ class MessageReader:
584
629
  res = {"reason": "private_key_export_disabled"}
585
630
  await self.dispatcher.dispatch(Event(EventType.DISABLED, res))
586
631
 
632
+ elif packet_type_value == PacketType.CONTROL_DATA.value:
633
+ logger.debug("Received control data packet")
634
+ res={}
635
+ res["SNR"] = int.from_bytes(dbuf.read(1), byteorder="little", signed=True) / 4
636
+ res["RSSI"] = int.from_bytes(dbuf.read(1), byteorder="little", signed=True)
637
+ res["path_len"] = dbuf.read(1)[0]
638
+ payload = dbuf.read()
639
+ payload_type = payload[0]
640
+ res["payload_type"] = payload_type
641
+ res["payload"] = payload
642
+
643
+ attributes = {"payload_type": payload_type}
644
+ await self.dispatcher.dispatch(
645
+ Event(EventType.CONTROL_DATA, res, attributes)
646
+ )
647
+
648
+ # decode NODE_DISCOVER_RESP
649
+ if payload_type & 0xF0 == ControlType.NODE_DISCOVER_RESP.value:
650
+ pbuf = io.BytesIO(payload[1:])
651
+ ndr = dict(res)
652
+ del ndr["payload_type"]
653
+ del ndr["payload"]
654
+ ndr["node_type"] = payload_type & 0x0F
655
+ ndr["SNR_in"] = int.from_bytes(pbuf.read(1), byteorder="little", signed=True)/4
656
+ ndr["tag"] = pbuf.read(4).hex()
657
+
658
+ pubkey = pbuf.read()
659
+ if len(pubkey) < 32:
660
+ pubkey = pubkey[0:8]
661
+ else:
662
+ pubkey = pubkey[0:32]
663
+
664
+ ndr["pubkey"] = pubkey.hex()
665
+
666
+ attributes = {
667
+ "node_type" : ndr["node_type"],
668
+ "tag" : ndr["tag"],
669
+ "pubkey" : ndr["pubkey"],
670
+ }
671
+
672
+ await self.dispatcher.dispatch(
673
+ Event(EventType.DISCOVER_RESPONSE, ndr, attributes)
674
+ )
675
+
587
676
  else:
588
677
  logger.debug(f"Unhandled data received {data}")
589
678
  logger.debug(f"Unhandled packet type: {packet_type_value}")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: meshcore
3
- Version: 2.1.17
3
+ Version: 2.1.24
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
@@ -0,0 +1,23 @@
1
+ meshcore/__init__.py,sha256=55PdT8gZ9Lf727s7BdSuCt3lIBlAhRzX28A8i0DHaRc,602
2
+ meshcore/ble_cx.py,sha256=S5uanBI88Mxwcs1zWbdQGVJ6GC4kdWz8-Z_2aQVpeGI,6864
3
+ meshcore/connection_manager.py,sha256=3U0TWuHDvL5FfwEwXRy5HjlCyV3nsWhhrEC7TVxFzZI,5899
4
+ meshcore/events.py,sha256=P40oq3C9j2cEg6iD9_-SxWK927C3ykWBTpjL8ixygFQ,8140
5
+ meshcore/lpp_json_encoder.py,sha256=vyn7z3VYWOo_B9xmCzqblh5xCa0QKWKcMi2eOqw18RE,1875
6
+ meshcore/meshcore.py,sha256=n8fH7AEx7DR8BHOYL_Qex9ns6ACKklNNqYbPnNnvqyE,16110
7
+ meshcore/packets.py,sha256=FT6GwH8rveLBWuFVMkbqnAuaymrk6uhd_t46Qj98uf4,1279
8
+ meshcore/parsing.py,sha256=48PQkig-sqvsRlkF9zkvWhJoSq6ERCbGb_aRuCND5NI,4044
9
+ meshcore/reader.py,sha256=inhn0Xsc7J0VKKyPVWwoM1LXfAKO-SJ4Th1c3sN4yy8,28645
10
+ meshcore/serial_cx.py,sha256=-kaqnqk7Ydufu2DECFfPDv4Xs-7gHUBuz8v0xf8fvvE,3969
11
+ meshcore/tcp_cx.py,sha256=05YRVMnjY5aVBTJcHa0uG4VfFKGbV6hQ1pPIsJg4CDI,4227
12
+ meshcore/commands/.binary.py.swp,sha256=SXeYOAsKheJnezxgvzUUHL43qUQWEPyc-VZUMBRsGV8,24576
13
+ meshcore/commands/__init__.py,sha256=qfGPzrha7pZQYHOXLZyDsK-QVRjiz5zxAEj8kMxO9uU,536
14
+ meshcore/commands/base.py,sha256=jO6KWSxBciEMaP0weE8OAbN6Tf-kWSaljb9py3gvihg,7126
15
+ meshcore/commands/binary.py,sha256=MihRjG4IppPYPeu2KMpctcBPac_hdClp6PgGirQfydg,8412
16
+ meshcore/commands/contact.py,sha256=-dRY68xZkDFP6-i53WujyWhEsXZsFRk98B3j3zBzaL4,5860
17
+ meshcore/commands/control_data.py,sha256=sXp1RoEw6Z0zPr0Nn5XBovEY6r9sePbWQwsbY0iYyXc,1512
18
+ meshcore/commands/device.py,sha256=V-gxA88We1KLDtzJ-rsmxv5LH04sUOacqnBc6TwB_gg,8662
19
+ meshcore/commands/messaging.py,sha256=5h0JecOT3KJV1AFdasVLs889xtXY4J5gguJ7KbUoZOE,9073
20
+ meshcore-2.1.24.dist-info/METADATA,sha256=9Ks9G4ws_jzHVCUps3JJmpWjWZ7LoKj9pbCpSViIA2c,25317
21
+ meshcore-2.1.24.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
22
+ meshcore-2.1.24.dist-info/licenses/LICENSE,sha256=o62-JWT_C-ZqEtzb1Gl_PPtPr0pVT8KDmgji_Y_bejI,1075
23
+ meshcore-2.1.24.dist-info/RECORD,,
@@ -1,21 +0,0 @@
1
- meshcore/__init__.py,sha256=55PdT8gZ9Lf727s7BdSuCt3lIBlAhRzX28A8i0DHaRc,602
2
- meshcore/ble_cx.py,sha256=S5uanBI88Mxwcs1zWbdQGVJ6GC4kdWz8-Z_2aQVpeGI,6864
3
- meshcore/connection_manager.py,sha256=3U0TWuHDvL5FfwEwXRy5HjlCyV3nsWhhrEC7TVxFzZI,5899
4
- meshcore/events.py,sha256=ZaVy2cCjujM3--xUDoUZqHZTq09R1rR8y3-_rLL8-fQ,8014
5
- meshcore/lpp_json_encoder.py,sha256=vyn7z3VYWOo_B9xmCzqblh5xCa0QKWKcMi2eOqw18RE,1875
6
- meshcore/meshcore.py,sha256=n8fH7AEx7DR8BHOYL_Qex9ns6ACKklNNqYbPnNnvqyE,16110
7
- meshcore/packets.py,sha256=2soo3H6FEbVdYk-ZQANIM3YpyCwsaIOrB4QIEJLLw0c,1072
8
- meshcore/parsing.py,sha256=48PQkig-sqvsRlkF9zkvWhJoSq6ERCbGb_aRuCND5NI,4044
9
- meshcore/reader.py,sha256=mIewSrfsgt-qe-acoxGQncHZhEhRr_3CEZuN1afnr7E,24784
10
- meshcore/serial_cx.py,sha256=-kaqnqk7Ydufu2DECFfPDv4Xs-7gHUBuz8v0xf8fvvE,3969
11
- meshcore/tcp_cx.py,sha256=05YRVMnjY5aVBTJcHa0uG4VfFKGbV6hQ1pPIsJg4CDI,4227
12
- meshcore/commands/__init__.py,sha256=NNmkTEcL-DLyuwKLUagEzpqe3C6ui2tETbu_mUd4p-I,441
13
- meshcore/commands/base.py,sha256=0bDHcNGlKoej03xJqzo_wL5ctoPQU5kcK1Ca5sZh1pk,7097
14
- meshcore/commands/binary.py,sha256=MVKXrT4pFSlzFkVFLBX988Eh57tRwunw4XPKBumbQNU,4890
15
- meshcore/commands/contact.py,sha256=-dRY68xZkDFP6-i53WujyWhEsXZsFRk98B3j3zBzaL4,5860
16
- meshcore/commands/device.py,sha256=vstyy1crvhGbDgCAtyCWyjj3X8F2CpMw1LFTOwoHh6M,8273
17
- meshcore/commands/messaging.py,sha256=MIVGM5G8J16qimKjCHdrlEiVJ4g_MsBaZKPvRJHJcLU,8306
18
- meshcore-2.1.17.dist-info/METADATA,sha256=ImPYOzshM7CKAhB-wRgqE3ZoN_0wIwE4Ksk3mRykENg,25317
19
- meshcore-2.1.17.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
20
- meshcore-2.1.17.dist-info/licenses/LICENSE,sha256=o62-JWT_C-ZqEtzb1Gl_PPtPr0pVT8KDmgji_Y_bejI,1075
21
- meshcore-2.1.17.dist-info/RECORD,,