meshcore 2.1.12__py3-none-any.whl → 2.1.14__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
meshcore/commands/base.py CHANGED
@@ -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
@@ -1,19 +1,82 @@
1
1
  import logging
2
+ import asyncio
2
3
  from typing import Optional
3
4
 
4
- from ..events import Event, EventType
5
+ from ..events import Event, EventDispatcher, EventType
5
6
  from .base import CommandHandlerBase, DestinationType, _validate_destination
6
7
 
7
8
  logger = logging.getLogger("meshcore")
8
9
 
9
10
 
10
11
  class ContactCommands(CommandHandlerBase):
11
- async def get_contacts(self, lastmod=0) -> Event:
12
+ async def get_contacts(self, lastmod=0, anim=False) -> Event:
12
13
  logger.debug("Getting contacts")
13
14
  data = b"\x04"
14
15
  if lastmod > 0:
15
16
  data = data + lastmod.to_bytes(4, "little")
16
- return await self.send(data, [EventType.CONTACTS, EventType.ERROR], timeout=30)
17
+ if anim:
18
+ print("Fetching contacts ", end="", flush=True)
19
+ # wait first event
20
+ res = await self.send(data)
21
+ timeout = 5
22
+ # Inline wait for events to continue waiting for CONTACTS event
23
+ # while receiving NEXT_CONTACTs (or it might be missed over serial)
24
+ try:
25
+ # Create futures for all expected events
26
+ futures = []
27
+ for event_type in [EventType.ERROR, EventType.NEXT_CONTACT, EventType.CONTACTS] :
28
+ future = asyncio.create_task(
29
+ self.dispatcher.wait_for_event(event_type, {}, timeout)
30
+ )
31
+ futures.append(future)
32
+
33
+ while True:
34
+
35
+ # Wait for the first event to complete or all to timeout
36
+ done, pending = await asyncio.wait(
37
+ futures, timeout=timeout, return_when=asyncio.FIRST_COMPLETED
38
+ )
39
+
40
+ # Check if any future completed successfully
41
+ if len(done) == 0:
42
+ print(" Timeout")
43
+ for future in pending: # cancel all futures
44
+ future.cancel()
45
+ return None
46
+
47
+ for future in done:
48
+ event = await future
49
+
50
+ if event:
51
+ if event.type == EventType.NEXT_CONTACT:
52
+ if anim:
53
+ print(".", end="", flush=True)
54
+ else: # Done or Error ... cancel pending and return
55
+ if anim:
56
+ print(" Done" if event.type == EventType.CONTACTS else " Error")
57
+ for future in pending:
58
+ future.cancel()
59
+ return event
60
+
61
+ futures = []
62
+ for future in pending: # put back pending
63
+ futures.append(future)
64
+
65
+ future = asyncio.create_task( # and recreate NEXT_CONTACT
66
+ self.dispatcher.wait_for_event(EventType.NEXT_CONTACT, {}, timeout)
67
+ )
68
+ futures.append(future)
69
+
70
+ except asyncio.TimeoutError:
71
+ logger.debug(f"Timeout receiving contacts")
72
+ if anim:
73
+ print(" Timeout")
74
+ return None
75
+ except Exception as e:
76
+ logger.debug(f"Command error: {e}")
77
+ if anim:
78
+ print(" Error")
79
+ return Event(EventType.ERROR, {"error": str(e)})
17
80
 
18
81
  async def reset_path(self, key: DestinationType) -> Event:
19
82
  key_bytes = _validate_destination(key, prefix_length=32)
meshcore/events.py CHANGED
@@ -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"
meshcore/reader.py CHANGED
@@ -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:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: meshcore
3
- Version: 2.1.12
3
+ Version: 2.1.14
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
@@ -1,21 +1,22 @@
1
1
  meshcore/__init__.py,sha256=55PdT8gZ9Lf727s7BdSuCt3lIBlAhRzX28A8i0DHaRc,602
2
2
  meshcore/ble_cx.py,sha256=GSMRFbgiluCbukx_xs6THx77bgIxH3l2DWPZ7TKY7JM,6591
3
3
  meshcore/connection_manager.py,sha256=3U0TWuHDvL5FfwEwXRy5HjlCyV3nsWhhrEC7TVxFzZI,5899
4
- meshcore/events.py,sha256=bSldqCicLENGuu5AhUN1-QYi9qn3kOBYULNKm6YaJfI,7980
4
+ meshcore/events.py,sha256=ZaVy2cCjujM3--xUDoUZqHZTq09R1rR8y3-_rLL8-fQ,8014
5
5
  meshcore/lpp_json_encoder.py,sha256=vyn7z3VYWOo_B9xmCzqblh5xCa0QKWKcMi2eOqw18RE,1875
6
6
  meshcore/meshcore.py,sha256=bQ6PIMfu3rJ1hrewyKFeKUyNvdUJlyTHD5tz_e5xgT0,16069
7
7
  meshcore/packets.py,sha256=2soo3H6FEbVdYk-ZQANIM3YpyCwsaIOrB4QIEJLLw0c,1072
8
8
  meshcore/parsing.py,sha256=48PQkig-sqvsRlkF9zkvWhJoSq6ERCbGb_aRuCND5NI,4044
9
- meshcore/reader.py,sha256=nr-obnx-XVjnRpwVT2q0kr-D3rI4NEdCR2q_oCc8yKk,24703
9
+ meshcore/reader.py,sha256=mIewSrfsgt-qe-acoxGQncHZhEhRr_3CEZuN1afnr7E,24784
10
10
  meshcore/serial_cx.py,sha256=-kaqnqk7Ydufu2DECFfPDv4Xs-7gHUBuz8v0xf8fvvE,3969
11
11
  meshcore/tcp_cx.py,sha256=05YRVMnjY5aVBTJcHa0uG4VfFKGbV6hQ1pPIsJg4CDI,4227
12
+ meshcore/commands/.contact.py.swp,sha256=ukLHeYjDX9dKOGMyuN3BNJCFrTvgD5y1X0vj1Lp0j5U,24576
12
13
  meshcore/commands/__init__.py,sha256=NNmkTEcL-DLyuwKLUagEzpqe3C6ui2tETbu_mUd4p-I,441
13
- meshcore/commands/base.py,sha256=23fKZZtG9ScWUQWruZZbre-_t5fqJsSemh-pffRNniY,6928
14
+ meshcore/commands/base.py,sha256=0bDHcNGlKoej03xJqzo_wL5ctoPQU5kcK1Ca5sZh1pk,7097
14
15
  meshcore/commands/binary.py,sha256=MVKXrT4pFSlzFkVFLBX988Eh57tRwunw4XPKBumbQNU,4890
15
- meshcore/commands/contact.py,sha256=FUXYWBGAJQxwXNLKRcY2c3b5JCdL0D9UjvH1yA8chdk,3761
16
+ meshcore/commands/contact.py,sha256=fmOW3CrDZFuvDHhqP7WpZT_vps7PK9LUvra2CYm0El8,6273
16
17
  meshcore/commands/device.py,sha256=vstyy1crvhGbDgCAtyCWyjj3X8F2CpMw1LFTOwoHh6M,8273
17
18
  meshcore/commands/messaging.py,sha256=MIVGM5G8J16qimKjCHdrlEiVJ4g_MsBaZKPvRJHJcLU,8306
18
- meshcore-2.1.12.dist-info/METADATA,sha256=WzrhwW5Sc9fRwPHRpmRfH0B9saQvniZvHs8M5P4k65Y,25317
19
- meshcore-2.1.12.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
20
- meshcore-2.1.12.dist-info/licenses/LICENSE,sha256=o62-JWT_C-ZqEtzb1Gl_PPtPr0pVT8KDmgji_Y_bejI,1075
21
- meshcore-2.1.12.dist-info/RECORD,,
19
+ meshcore-2.1.14.dist-info/METADATA,sha256=e_MjaWO0VyG1-ZTsL-hj8vWfvm6gIzUIVu42dhPHOAc,25317
20
+ meshcore-2.1.14.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
21
+ meshcore-2.1.14.dist-info/licenses/LICENSE,sha256=o62-JWT_C-ZqEtzb1Gl_PPtPr0pVT8KDmgji_Y_bejI,1075
22
+ meshcore-2.1.14.dist-info/RECORD,,