meshcore 2.1.12__tar.gz → 2.1.13__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.

Potentially problematic release.


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

Files changed (60) hide show
  1. {meshcore-2.1.12 → meshcore-2.1.13}/PKG-INFO +1 -1
  2. {meshcore-2.1.12 → meshcore-2.1.13}/pyproject.toml +1 -1
  3. {meshcore-2.1.12 → meshcore-2.1.13}/src/meshcore/commands/base.py +49 -38
  4. {meshcore-2.1.12 → meshcore-2.1.13}/src/meshcore/commands/contact.py +25 -2
  5. {meshcore-2.1.12 → meshcore-2.1.13}/src/meshcore/events.py +1 -0
  6. {meshcore-2.1.12 → meshcore-2.1.13}/src/meshcore/reader.py +1 -0
  7. {meshcore-2.1.12 → meshcore-2.1.13}/.github/python-test.yml +0 -0
  8. {meshcore-2.1.12 → meshcore-2.1.13}/.gitignore +0 -0
  9. {meshcore-2.1.12 → meshcore-2.1.13}/LICENSE +0 -0
  10. {meshcore-2.1.12 → meshcore-2.1.13}/README.md +0 -0
  11. {meshcore-2.1.12 → meshcore-2.1.13}/examples/ble_chat.py +0 -0
  12. {meshcore-2.1.12 → meshcore-2.1.13}/examples/ble_pin_pairing_example.py +0 -0
  13. {meshcore-2.1.12 → meshcore-2.1.13}/examples/ble_private_key_export.py +0 -0
  14. {meshcore-2.1.12 → meshcore-2.1.13}/examples/ble_t1000_chan_msg.py +0 -0
  15. {meshcore-2.1.12 → meshcore-2.1.13}/examples/ble_t1000_custom_vars.py +0 -0
  16. {meshcore-2.1.12 → meshcore-2.1.13}/examples/ble_t1000_infos.py +0 -0
  17. {meshcore-2.1.12 → meshcore-2.1.13}/examples/ble_t1000_msg.py +0 -0
  18. {meshcore-2.1.12 → meshcore-2.1.13}/examples/ble_t1000_msg_retries.py +0 -0
  19. {meshcore-2.1.12 → meshcore-2.1.13}/examples/ble_t1000_set_cv.py +0 -0
  20. {meshcore-2.1.12 → meshcore-2.1.13}/examples/connection_events_example.py +0 -0
  21. {meshcore-2.1.12 → meshcore-2.1.13}/examples/mepo_mc_gps.py +0 -0
  22. {meshcore-2.1.12 → meshcore-2.1.13}/examples/pubsub_example.py +0 -0
  23. {meshcore-2.1.12 → meshcore-2.1.13}/examples/rf_packet_monitor.py +0 -0
  24. {meshcore-2.1.12 → meshcore-2.1.13}/examples/serial_battery_monitor.py +0 -0
  25. {meshcore-2.1.12 → meshcore-2.1.13}/examples/serial_channel_manager.py +0 -0
  26. {meshcore-2.1.12 → meshcore-2.1.13}/examples/serial_chat.py +0 -0
  27. {meshcore-2.1.12 → meshcore-2.1.13}/examples/serial_contacts.py +0 -0
  28. {meshcore-2.1.12 → meshcore-2.1.13}/examples/serial_infos.py +0 -0
  29. {meshcore-2.1.12 → meshcore-2.1.13}/examples/serial_msg.py +0 -0
  30. {meshcore-2.1.12 → meshcore-2.1.13}/examples/serial_repeater_status.py +0 -0
  31. {meshcore-2.1.12 → meshcore-2.1.13}/examples/serial_repeater_telemetry.py +0 -0
  32. {meshcore-2.1.12 → meshcore-2.1.13}/examples/serial_trace.py +0 -0
  33. {meshcore-2.1.12 → meshcore-2.1.13}/examples/tcp_chat.py +0 -0
  34. {meshcore-2.1.12 → meshcore-2.1.13}/examples/tcp_login_status.py +0 -0
  35. {meshcore-2.1.12 → meshcore-2.1.13}/examples/tcp_mchome_contacts.py +0 -0
  36. {meshcore-2.1.12 → meshcore-2.1.13}/examples/tcp_mchome_infos.py +0 -0
  37. {meshcore-2.1.12 → meshcore-2.1.13}/examples/tcp_mchome_msg.py +0 -0
  38. {meshcore-2.1.12 → meshcore-2.1.13}/examples/tcp_mchome_readmsgs.py +0 -0
  39. {meshcore-2.1.12 → meshcore-2.1.13}/pytest.ini +0 -0
  40. {meshcore-2.1.12 → meshcore-2.1.13}/src/meshcore/__init__.py +0 -0
  41. {meshcore-2.1.12 → meshcore-2.1.13}/src/meshcore/ble_cx.py +0 -0
  42. {meshcore-2.1.12 → meshcore-2.1.13}/src/meshcore/commands/__init__.py +0 -0
  43. {meshcore-2.1.12 → meshcore-2.1.13}/src/meshcore/commands/binary.py +0 -0
  44. {meshcore-2.1.12 → meshcore-2.1.13}/src/meshcore/commands/device.py +0 -0
  45. {meshcore-2.1.12 → meshcore-2.1.13}/src/meshcore/commands/messaging.py +0 -0
  46. {meshcore-2.1.12 → meshcore-2.1.13}/src/meshcore/connection_manager.py +0 -0
  47. {meshcore-2.1.12 → meshcore-2.1.13}/src/meshcore/lpp_json_encoder.py +0 -0
  48. {meshcore-2.1.12 → meshcore-2.1.13}/src/meshcore/meshcore.py +0 -0
  49. {meshcore-2.1.12 → meshcore-2.1.13}/src/meshcore/packets.py +0 -0
  50. {meshcore-2.1.12 → meshcore-2.1.13}/src/meshcore/parsing.py +0 -0
  51. {meshcore-2.1.12 → meshcore-2.1.13}/src/meshcore/serial_cx.py +0 -0
  52. {meshcore-2.1.12 → meshcore-2.1.13}/src/meshcore/tcp_cx.py +0 -0
  53. {meshcore-2.1.12 → meshcore-2.1.13}/tests/README.md +0 -0
  54. {meshcore-2.1.12 → meshcore-2.1.13}/tests/test_ble_connection.py +0 -0
  55. {meshcore-2.1.12 → meshcore-2.1.13}/tests/test_ble_pin_pairing.py +0 -0
  56. {meshcore-2.1.12 → meshcore-2.1.13}/tests/test_meshcore_ble_pin.py +0 -0
  57. {meshcore-2.1.12 → meshcore-2.1.13}/tests/unit/test_commands.py +0 -0
  58. {meshcore-2.1.12 → meshcore-2.1.13}/tests/unit/test_events.py +0 -0
  59. {meshcore-2.1.12 → meshcore-2.1.13}/tests/unit/test_private_key_export.py +0 -0
  60. {meshcore-2.1.12 → meshcore-2.1.13}/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.12
3
+ Version: 2.1.13
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
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "meshcore"
7
- version = "2.1.12"
7
+ version = "2.1.13"
8
8
  authors = [
9
9
  { name="Florent de Lamotte", email="florent@frizoncorrea.fr" },
10
10
  { name="Alex Wolden", email="awolden@gmail.com" },
@@ -80,6 +80,53 @@ class CommandHandlerBase:
80
80
  )-> None:
81
81
  self._get_contact_by_prefix = func
82
82
 
83
+ async def wait_for_events(
84
+ self,
85
+ expected_events: Optional[Union[EventType, List[EventType]]] = None,
86
+ timeout: Optional[float] = None,
87
+ ) -> Event:
88
+ try:
89
+ # Convert single event to list if needed
90
+ if not isinstance(expected_events, list):
91
+ expected_events = [expected_events]
92
+
93
+ logger.debug(f"Waiting for events {expected_events}, timeout={timeout}")
94
+
95
+ # Create futures for all expected events
96
+ futures = []
97
+ for event_type in expected_events:
98
+ future = asyncio.create_task(
99
+ self.dispatcher.wait_for_event(event_type, {}, timeout)
100
+ )
101
+ futures.append(future)
102
+
103
+ # Wait for the first event to complete or all to timeout
104
+ done, pending = await asyncio.wait(
105
+ futures, timeout=timeout, return_when=asyncio.FIRST_COMPLETED
106
+ )
107
+
108
+ # Cancel all pending futures
109
+ for future in pending:
110
+ future.cancel()
111
+
112
+ # Check if any future completed successfully
113
+ for future in done:
114
+ event = await future
115
+ if event:
116
+ return event
117
+
118
+ # Create an error event when no event is received
119
+ return Event(EventType.ERROR, {"reason": "no_event_received"})
120
+ except asyncio.TimeoutError:
121
+ logger.debug(f"Command timed out {data}")
122
+ return Event(EventType.ERROR, {"reason": "timeout"})
123
+ except Exception as e:
124
+ logger.debug(f"Command error: {e}")
125
+ return Event(EventType.ERROR, {"error": str(e)})
126
+
127
+ return Event(EventType.ERROR, {})
128
+
129
+
83
130
  async def send(
84
131
  self,
85
132
  data: bytes,
@@ -110,45 +157,9 @@ class CommandHandlerBase:
110
157
  await self._sender_func(data)
111
158
 
112
159
  if expected_events:
113
- try:
114
- # Convert single event to list if needed
115
- if not isinstance(expected_events, list):
116
- expected_events = [expected_events]
117
-
118
- logger.debug(f"Waiting for events {expected_events}, timeout={timeout}")
119
-
120
- # Create futures for all expected events
121
- futures = []
122
- for event_type in expected_events:
123
- future = asyncio.create_task(
124
- self.dispatcher.wait_for_event(event_type, {}, timeout)
125
- )
126
- futures.append(future)
127
-
128
- # Wait for the first event to complete or all to timeout
129
- done, pending = await asyncio.wait(
130
- futures, timeout=timeout, return_when=asyncio.FIRST_COMPLETED
131
- )
132
-
133
- # Cancel all pending futures
134
- for future in pending:
135
- future.cancel()
136
-
137
- # Check if any future completed successfully
138
- for future in done:
139
- event = await future
140
- if event:
141
- return event
142
-
143
- # Create an error event when no event is received
144
- return Event(EventType.ERROR, {"reason": "no_event_received"})
145
- except asyncio.TimeoutError:
146
- logger.debug(f"Command timed out {data}")
147
- return Event(EventType.ERROR, {"reason": "timeout"})
148
- except Exception as e:
149
- logger.debug(f"Command error: {e}")
150
- return Event(EventType.ERROR, {"error": str(e)})
151
160
  # For commands that don't expect events, return a success event
161
+ return await self.wait_for_events(expected_events, timeout)
162
+
152
163
  return Event(EventType.OK, {})
153
164
 
154
165
  # attached at base because its a common method
@@ -8,12 +8,35 @@ logger = logging.getLogger("meshcore")
8
8
 
9
9
 
10
10
  class ContactCommands(CommandHandlerBase):
11
- async def get_contacts(self, lastmod=0) -> Event:
11
+ async def get_contacts(self, lastmod=0, anim=False) -> Event:
12
12
  logger.debug("Getting contacts")
13
13
  data = b"\x04"
14
14
  if lastmod > 0:
15
15
  data = data + lastmod.to_bytes(4, "little")
16
- return await self.send(data, [EventType.CONTACTS, EventType.ERROR], timeout=30)
16
+ if anim:
17
+ print("Fetching contacts ", end="", flush=True)
18
+ # wait first event
19
+ res = await self.send(data)
20
+ while True:
21
+ # wait next event
22
+ res = await self.wait_for_events(
23
+ [EventType.NEXT_CONTACT, EventType.CONTACTS, EventType.ERROR],
24
+ timeout=5)
25
+ if res is None: # Timeout
26
+ if anim:
27
+ print(" Timeout")
28
+ return res
29
+ if res.type == EventType.ERROR:
30
+ if anim:
31
+ print(" Error")
32
+ return res
33
+ elif res.type == EventType.CONTACTS:
34
+ if anim:
35
+ print(" Done")
36
+ return res
37
+ elif res.type == EventType.NEXT_CONTACT:
38
+ if anim:
39
+ print(".", end="", flush=True)
17
40
 
18
41
  async def reset_path(self, key: DestinationType) -> Event:
19
42
  key_bytes = _validate_destination(key, prefix_length=32)
@@ -21,6 +21,7 @@ class EventType(Enum):
21
21
  DEVICE_INFO = "device_info"
22
22
  MSG_SENT = "message_sent"
23
23
  NEW_CONTACT = "new_contact"
24
+ NEXT_CONTACT = "next_contact"
24
25
 
25
26
  # Push notifications
26
27
  ADVERTISEMENT = "advertisement"
@@ -99,6 +99,7 @@ class MessageReader:
99
99
  if packet_type_value == PacketType.PUSH_CODE_NEW_ADVERT.value:
100
100
  await self.dispatcher.dispatch(Event(EventType.NEW_CONTACT, c))
101
101
  else:
102
+ await self.dispatcher.dispatch(Event(EventType.NEXT_CONTACT, c))
102
103
  self.contacts[c["public_key"]] = c
103
104
 
104
105
  elif packet_type_value == PacketType.CONTACT_END.value:
File without changes
File without changes
File without changes
File without changes
File without changes