vnode 0.1.7__tar.gz → 0.1.10__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: vnode
3
- Version: 0.1.7
3
+ Version: 0.1.10
4
4
  Summary: Virtual Meshtastic node using mudp transport and meshdb storage.
5
5
  License-Expression: GPL-3.0-only
6
6
  License-File: LICENSE
@@ -17,7 +17,7 @@ Classifier: Programming Language :: Python :: 3.14
17
17
  Requires-Dist: cryptography (>=43,<45)
18
18
  Requires-Dist: meshdb (>=0.1.9)
19
19
  Requires-Dist: meshtastic (>=2.7,<3.0)
20
- Requires-Dist: mudp (>=1.1.0)
20
+ Requires-Dist: mudp (>=1.1.5)
21
21
  Project-URL: Homepage, https://github.com/pdxlocations/vnode
22
22
  Project-URL: Issues, https://github.com/pdxlocations/vnode/issues
23
23
  Description-Content-Type: text/markdown
@@ -106,6 +106,10 @@ Supported `VirtualNode` calls:
106
106
  - MeshInterface-compatible sends: `sendText()`, `sendAlert()`, `sendData()`, `sendPosition()`
107
107
  - Packet helpers: `is_direct_message_for_me()`, `is_text_message()`, `get_text_message()`, `reply_to_packet()`
108
108
 
109
+ `receive(callback)` is deduplicated. Repeated multicast copies stay on `mudp`'s wire-level
110
+ topics, while `vnode` publishes `meshtastic.receive` from `mesh.rx.unique_packet` so
111
+ application callbacks run once per logical packet.
112
+
109
113
  ## Examples
110
114
 
111
115
  Use these as small runnable references for common tasks:
@@ -117,7 +121,7 @@ Use these as small runnable references for common tasks:
117
121
  - `examples/serial_or_vnode.py`: try a serial Meshtastic node first, then fall back to `VirtualNode`
118
122
  - `examples/send_dm.py`: minimal one-shot direct-message sender
119
123
  - `examples/library_embed.py`: minimal application-style embedding example using `VirtualNode` directly
120
- - `examples/watch_reliability.py`: watcher for ACK, NAK, retry, and retransmit-failure events
124
+ - `examples/watch_reliability.py`: watcher for unique ACK, NAK, retry, and retransmit-failure events
121
125
 
122
126
  ```bash
123
127
  .venv/bin/python examples/autoresponder.py
@@ -82,6 +82,10 @@ Supported `VirtualNode` calls:
82
82
  - MeshInterface-compatible sends: `sendText()`, `sendAlert()`, `sendData()`, `sendPosition()`
83
83
  - Packet helpers: `is_direct_message_for_me()`, `is_text_message()`, `get_text_message()`, `reply_to_packet()`
84
84
 
85
+ `receive(callback)` is deduplicated. Repeated multicast copies stay on `mudp`'s wire-level
86
+ topics, while `vnode` publishes `meshtastic.receive` from `mesh.rx.unique_packet` so
87
+ application callbacks run once per logical packet.
88
+
85
89
  ## Examples
86
90
 
87
91
  Use these as small runnable references for common tasks:
@@ -93,7 +97,7 @@ Use these as small runnable references for common tasks:
93
97
  - `examples/serial_or_vnode.py`: try a serial Meshtastic node first, then fall back to `VirtualNode`
94
98
  - `examples/send_dm.py`: minimal one-shot direct-message sender
95
99
  - `examples/library_embed.py`: minimal application-style embedding example using `VirtualNode` directly
96
- - `examples/watch_reliability.py`: watcher for ACK, NAK, retry, and retransmit-failure events
100
+ - `examples/watch_reliability.py`: watcher for unique ACK, NAK, retry, and retransmit-failure events
97
101
 
98
102
  ```bash
99
103
  .venv/bin/python examples/autoresponder.py
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "vnode"
3
- version = "0.1.7"
3
+ version = "0.1.10"
4
4
  description = "Virtual Meshtastic node using mudp transport and meshdb storage."
5
5
  authors = [
6
6
  {name = "Ben Lipsey",email = "ben@pdxlocations.com"}
@@ -12,7 +12,7 @@ dependencies = [
12
12
  "cryptography>=43,<45",
13
13
  "meshdb>=0.1.9",
14
14
  "meshtastic>=2.7,<3.0",
15
- "mudp>=1.1.0",
15
+ "mudp>=1.1.5",
16
16
  ]
17
17
 
18
18
  [project.urls]
@@ -63,7 +63,10 @@ def resolve_role(value: Union[str, int]) -> int:
63
63
 
64
64
 
65
65
  class VirtualNode:
66
+ RAW_PACKET_TOPIC = "mesh.rx.packet"
66
67
  PACKET_TOPIC = "mesh.rx.unique_packet"
68
+ ACK_TOPIC = "mesh.rx.ack"
69
+ NAK_TOPIC = "mesh.rx.nak"
67
70
  DUPLICATE_TOPIC = "mesh.rx.duplicate"
68
71
  RECEIVE_TOPIC = "meshtastic.receive"
69
72
 
@@ -142,11 +145,11 @@ class VirtualNode:
142
145
  def start(self) -> None:
143
146
  if self.stream is not None:
144
147
  return
145
- pub.subscribe(self._handle_raw_packet, "mesh.rx.packet")
148
+ pub.subscribe(self._handle_raw_packet, self.RAW_PACKET_TOPIC)
146
149
  pub.subscribe(self._handle_unique_packet, self.PACKET_TOPIC)
147
150
  pub.subscribe(self._handle_compat_response_packet, self.RECEIVE_TOPIC)
148
- pub.subscribe(self._handle_compat_ack, "mesh.rx.ack")
149
- pub.subscribe(self._handle_compat_nak, "mesh.rx.nak")
151
+ pub.subscribe(self._handle_compat_ack, self.ACK_TOPIC)
152
+ pub.subscribe(self._handle_compat_nak, self.NAK_TOPIC)
150
153
  self.stream = UDPPacketStream(
151
154
  self.config.udp.mcast_group,
152
155
  int(self.config.udp.mcast_port),
@@ -169,7 +172,7 @@ class VirtualNode:
169
172
  self.stream.stop()
170
173
  self.stream = None
171
174
  try:
172
- pub.unsubscribe(self._handle_raw_packet, "mesh.rx.packet")
175
+ pub.unsubscribe(self._handle_raw_packet, self.RAW_PACKET_TOPIC)
173
176
  except KeyError:
174
177
  pass
175
178
  try:
@@ -178,8 +181,8 @@ class VirtualNode:
178
181
  pass
179
182
  for topic, listener in (
180
183
  (self.RECEIVE_TOPIC, self._handle_compat_response_packet),
181
- ("mesh.rx.ack", self._handle_compat_ack),
182
- ("mesh.rx.nak", self._handle_compat_nak),
184
+ (self.ACK_TOPIC, self._handle_compat_ack),
185
+ (self.NAK_TOPIC, self._handle_compat_nak),
183
186
  ):
184
187
  try:
185
188
  pub.unsubscribe(listener, topic)
@@ -364,8 +367,13 @@ class VirtualNode:
364
367
  if getattr(conn, "socket", None) is None:
365
368
  conn.setup_multicast(self.config.udp.mcast_group, int(self.config.udp.mcast_port))
366
369
 
367
- def send_nodeinfo(self, destination: int = BROADCAST_NUM) -> int:
368
- return self._send_nodeinfo(destination)
370
+ def send_nodeinfo(
371
+ self,
372
+ destination: int = BROADCAST_NUM,
373
+ *,
374
+ want_response: bool = False,
375
+ ) -> int:
376
+ return self._send_nodeinfo(destination, want_response=want_response)
369
377
 
370
378
  def _send_nodeinfo(
371
379
  self,
@@ -373,6 +381,7 @@ class VirtualNode:
373
381
  *,
374
382
  request_id: Optional[int] = None,
375
383
  want_ack: bool = False,
384
+ want_response: bool = False,
376
385
  ) -> int:
377
386
  user = mesh_pb2.User(
378
387
  id=self.config.node_id,
@@ -389,6 +398,7 @@ class VirtualNode:
389
398
  data.payload = user.SerializeToString()
390
399
  data.source = self.node_num
391
400
  data.dest = int(destination)
401
+ data.want_response = bool(want_response)
392
402
  if request_id is not None:
393
403
  data.request_id = int(request_id)
394
404
  packet_id = self._send_data(
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes