mirror of
https://github.com/meshcore-dev/meshcore_py.git
synced 2026-06-11 11:56:18 +00:00
Merge pull request #5 from fdlamotte/return-full-event
Change contract for commands to return full event
This commit is contained in:
126
README.md
126
README.md
@@ -21,7 +21,12 @@ async def main():
|
|||||||
meshcore = await MeshCore.create_serial("/dev/ttyUSB0")
|
meshcore = await MeshCore.create_serial("/dev/ttyUSB0")
|
||||||
|
|
||||||
# Get your contacts
|
# Get your contacts
|
||||||
contacts = await meshcore.commands.get_contacts()
|
result = await meshcore.commands.get_contacts()
|
||||||
|
if result.type == EventType.ERROR:
|
||||||
|
print(f"Error getting contacts: {result.payload}")
|
||||||
|
return
|
||||||
|
|
||||||
|
contacts = result.payload
|
||||||
print(f"Found {len(contacts)} contacts")
|
print(f"Found {len(contacts)} contacts")
|
||||||
|
|
||||||
# Send a message to the first contact
|
# Send a message to the first contact
|
||||||
@@ -30,7 +35,12 @@ async def main():
|
|||||||
contact = next(iter(contacts.items()))[1]
|
contact = next(iter(contacts.items()))[1]
|
||||||
|
|
||||||
# Pass the contact object directly to send_msg
|
# Pass the contact object directly to send_msg
|
||||||
await meshcore.commands.send_msg(contact, "Hello from Python!")
|
result = await meshcore.commands.send_msg(contact, "Hello from Python!")
|
||||||
|
|
||||||
|
if result.type == EventType.ERROR:
|
||||||
|
print(f"Error sending message: {result.payload}")
|
||||||
|
else:
|
||||||
|
print("Message sent successfully!")
|
||||||
|
|
||||||
await meshcore.disconnect()
|
await meshcore.disconnect()
|
||||||
|
|
||||||
@@ -55,6 +65,37 @@ python examples/pubsub_example.py -p /dev/ttyUSB0
|
|||||||
|
|
||||||
## Usage Guide
|
## Usage Guide
|
||||||
|
|
||||||
|
### Command Return Values
|
||||||
|
|
||||||
|
All command methods in MeshCore return an `Event` object that contains both the event type and its payload. This allows for consistent error handling and type checking:
|
||||||
|
|
||||||
|
```python
|
||||||
|
# Command result structure
|
||||||
|
result = await meshcore.commands.some_command()
|
||||||
|
|
||||||
|
# Check if the command was successful or resulted in an error
|
||||||
|
if result.type == EventType.ERROR:
|
||||||
|
# Handle error case
|
||||||
|
print(f"Command failed: {result.payload}")
|
||||||
|
else:
|
||||||
|
# Handle success case - the event type will be specific to the command
|
||||||
|
# (e.g., EventType.DEVICE_INFO, EventType.CONTACTS, EventType.MSG_SENT)
|
||||||
|
print(f"Command succeeded with event type: {result.type}")
|
||||||
|
# Access the payload data
|
||||||
|
data = result.payload
|
||||||
|
```
|
||||||
|
|
||||||
|
Common error handling pattern:
|
||||||
|
|
||||||
|
```python
|
||||||
|
result = await meshcore.commands.send_msg(contact, "Hello!")
|
||||||
|
if result.type == EventType.ERROR:
|
||||||
|
print(f"Error sending message: {result.payload}")
|
||||||
|
else:
|
||||||
|
# For send_msg, a successful result will have type EventType.MSG_SENT
|
||||||
|
print(f"Message sent with expected ack: {result.payload['expected_ack'].hex()}")
|
||||||
|
```
|
||||||
|
|
||||||
### Connecting to Your Device
|
### Connecting to Your Device
|
||||||
|
|
||||||
Connect via Serial, BLE, or TCP:
|
Connect via Serial, BLE, or TCP:
|
||||||
@@ -76,20 +117,34 @@ Send commands and wait for responses:
|
|||||||
|
|
||||||
```python
|
```python
|
||||||
# Get device information
|
# Get device information
|
||||||
device_info = await meshcore.commands.send_device_query()
|
result = await meshcore.commands.send_device_query()
|
||||||
print(f"Device model: {device_info['model']}")
|
if result.type == EventType.ERROR:
|
||||||
|
print(f"Error getting device info: {result.payload}")
|
||||||
|
else:
|
||||||
|
print(f"Device model: {result.payload['model']}")
|
||||||
|
|
||||||
# Get list of contacts
|
# Get list of contacts
|
||||||
contacts = await meshcore.commands.get_contacts()
|
result = await meshcore.commands.get_contacts()
|
||||||
for contact_id, contact in contacts.items():
|
if result.type == EventType.ERROR:
|
||||||
print(f"Contact: {contact['adv_name']} ({contact_id})")
|
print(f"Error getting contacts: {result.payload}")
|
||||||
|
else:
|
||||||
|
contacts = result.payload
|
||||||
|
for contact_id, contact in contacts.items():
|
||||||
|
print(f"Contact: {contact['adv_name']} ({contact_id})")
|
||||||
|
|
||||||
# Send a message (destination key in bytes)
|
# Send a message (destination key in bytes)
|
||||||
await meshcore.commands.send_msg(dst_key, "Hello!")
|
result = await meshcore.commands.send_msg(dst_key, "Hello!")
|
||||||
|
if result.type == EventType.ERROR:
|
||||||
|
print(f"Error sending message: {result.payload}")
|
||||||
|
|
||||||
# Setting device parameters
|
# Setting device parameters
|
||||||
await meshcore.commands.set_name("My Device")
|
result = await meshcore.commands.set_name("My Device")
|
||||||
await meshcore.commands.set_tx_power(20) # Set transmit power
|
if result.type == EventType.ERROR:
|
||||||
|
print(f"Error setting name: {result.payload}")
|
||||||
|
|
||||||
|
result = await meshcore.commands.set_tx_power(20) # Set transmit power
|
||||||
|
if result.type == EventType.ERROR:
|
||||||
|
print(f"Error setting TX power: {result.payload}")
|
||||||
```
|
```
|
||||||
|
|
||||||
### Finding Contacts
|
### Finding Contacts
|
||||||
@@ -151,7 +206,11 @@ async def send_and_confirm_message(meshcore, dst_key, message):
|
|||||||
sent_result = await meshcore.commands.send_msg(dst_key, message)
|
sent_result = await meshcore.commands.send_msg(dst_key, message)
|
||||||
|
|
||||||
# Extract the expected acknowledgment code from the message sent event
|
# Extract the expected acknowledgment code from the message sent event
|
||||||
expected_ack = sent_result["expected_ack"].hex()
|
if sent_result.type == EventType.ERROR:
|
||||||
|
print(f"Error sending message: {sent_result.payload}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
expected_ack = sent_result.payload["expected_ack"].hex()
|
||||||
print(f"Message sent, waiting for ack with code: {expected_ack}")
|
print(f"Message sent, waiting for ack with code: {expected_ack}")
|
||||||
|
|
||||||
# Wait specifically for this acknowledgment
|
# Wait specifically for this acknowledgment
|
||||||
@@ -196,7 +255,10 @@ async def main():
|
|||||||
while True:
|
while True:
|
||||||
# Send command (returns battery level)
|
# Send command (returns battery level)
|
||||||
result = await meshcore.commands.get_bat()
|
result = await meshcore.commands.get_bat()
|
||||||
print(f"Battery check initiated, response: {result}")
|
if result.type == EventType.ERROR:
|
||||||
|
print(f"Error checking battery: {result.payload}")
|
||||||
|
else:
|
||||||
|
print(f"Battery level: {result.payload.get('level', 'unknown')}%")
|
||||||
await asyncio.sleep(60) # Wait 60 seconds between checks
|
await asyncio.sleep(60) # Wait 60 seconds between checks
|
||||||
|
|
||||||
# Start the background task
|
# Start the background task
|
||||||
@@ -257,24 +319,36 @@ Commands that require a destination (`send_msg`, `send_login`, `send_statusreq`,
|
|||||||
|
|
||||||
```python
|
```python
|
||||||
# Get contacts and send to a specific one
|
# Get contacts and send to a specific one
|
||||||
contacts = await meshcore.commands.get_contacts()
|
result = await meshcore.commands.get_contacts()
|
||||||
for key, contact in contacts.items():
|
if result.type == EventType.ERROR:
|
||||||
if contact["adv_name"] == "Alice":
|
print(f"Error getting contacts: {result.payload}")
|
||||||
# Option 1: Pass the contact object directly
|
else:
|
||||||
await meshcore.commands.send_msg(contact, "Hello Alice!")
|
contacts = result.payload
|
||||||
|
for key, contact in contacts.items():
|
||||||
# Option 2: Use the public key string
|
if contact["adv_name"] == "Alice":
|
||||||
await meshcore.commands.send_msg(contact["public_key"], "Hello again Alice!")
|
# Option 1: Pass the contact object directly
|
||||||
|
result = await meshcore.commands.send_msg(contact, "Hello Alice!")
|
||||||
# Option 3 (backward compatible): Convert the hex key to bytes
|
if result.type == EventType.ERROR:
|
||||||
dst_key = bytes.fromhex(contact["public_key"])
|
print(f"Error sending message: {result.payload}")
|
||||||
await meshcore.commands.send_msg(dst_key, "Hello once more Alice!")
|
|
||||||
break
|
# Option 2: Use the public key string
|
||||||
|
result = await meshcore.commands.send_msg(contact["public_key"], "Hello again Alice!")
|
||||||
|
if result.type == EventType.ERROR:
|
||||||
|
print(f"Error sending message: {result.payload}")
|
||||||
|
|
||||||
|
# Option 3 (backward compatible): Convert the hex key to bytes
|
||||||
|
dst_key = bytes.fromhex(contact["public_key"])
|
||||||
|
result = await meshcore.commands.send_msg(dst_key, "Hello once more Alice!")
|
||||||
|
if result.type == EventType.ERROR:
|
||||||
|
print(f"Error sending message: {result.payload}")
|
||||||
|
break
|
||||||
|
|
||||||
# You can also directly use a contact found by name
|
# You can also directly use a contact found by name
|
||||||
contact = meshcore.get_contact_by_name("Bob")
|
contact = meshcore.get_contact_by_name("Bob")
|
||||||
if contact:
|
if contact:
|
||||||
await meshcore.commands.send_msg(contact, "Hello Bob!")
|
result = await meshcore.commands.send_msg(contact, "Hello Bob!")
|
||||||
|
if result.type == EventType.ERROR:
|
||||||
|
print(f"Error sending message: {result.payload}")
|
||||||
```
|
```
|
||||||
|
|
||||||
### Monitoring Channel Messages
|
### Monitoring Channel Messages
|
||||||
|
|||||||
@@ -69,8 +69,12 @@ async def main () :
|
|||||||
else :
|
else :
|
||||||
if line.startswith("send") :
|
if line.startswith("send") :
|
||||||
line = line[5:]
|
line = line[5:]
|
||||||
ret = await mc.commands.send_msg(contact , line)
|
result = await mc.commands.send_msg(contact, line)
|
||||||
exp_ack = ret["expected_ack"].hex()
|
if result.type == EventType.ERROR:
|
||||||
|
print(f"⚠️ Failed to send message: {result.payload}")
|
||||||
|
continue
|
||||||
|
|
||||||
|
exp_ack = result.payload["expected_ack"].hex()
|
||||||
print(" Sent ... ", end="", flush=True)
|
print(" Sent ... ", end="", flush=True)
|
||||||
res = await mc.wait_for_event(EventType.ACK, attribute_filters={"code": exp_ack}, timeout=5)
|
res = await mc.wait_for_event(EventType.ACK, attribute_filters={"code": exp_ack}, timeout=5)
|
||||||
if res is None :
|
if res is None :
|
||||||
|
|||||||
@@ -45,7 +45,11 @@ async def main():
|
|||||||
print("Connected to MeshCore device")
|
print("Connected to MeshCore device")
|
||||||
|
|
||||||
# Get contacts
|
# Get contacts
|
||||||
contacts = await meshcore.commands.get_contacts()
|
result = await meshcore.commands.get_contacts()
|
||||||
|
if result.type == EventType.ERROR:
|
||||||
|
print(f"Error fetching contacts: {result.payload}")
|
||||||
|
return
|
||||||
|
contacts = result.payload
|
||||||
if contacts:
|
if contacts:
|
||||||
print(f"\nFound {len(contacts)} contacts:")
|
print(f"\nFound {len(contacts)} contacts:")
|
||||||
for name, contact in contacts.items():
|
for name, contact in contacts.items():
|
||||||
|
|||||||
@@ -17,7 +17,9 @@ async def handle_message(event):
|
|||||||
data = event.payload
|
data = event.payload
|
||||||
|
|
||||||
contact = mc.get_contact_by_key_prefix(data['pubkey_prefix'])
|
contact = mc.get_contact_by_key_prefix(data['pubkey_prefix'])
|
||||||
|
if contact is None:
|
||||||
|
print(f"Unknown contact with pubkey prefix: {data['pubkey_prefix']}")
|
||||||
|
return
|
||||||
print(f"{contact['adv_name']}: {data['text']}")
|
print(f"{contact['adv_name']}: {data['text']}")
|
||||||
|
|
||||||
async def main () :
|
async def main () :
|
||||||
@@ -54,7 +56,7 @@ async def main () :
|
|||||||
if line.startswith("to ") :
|
if line.startswith("to ") :
|
||||||
dest = line[3:]
|
dest = line[3:]
|
||||||
nc = mc.get_contact_by_name(dest)
|
nc = mc.get_contact_by_name(dest)
|
||||||
if mc is None:
|
if nc is None:
|
||||||
print(f"Contact '{DEST}' not found in contacts.")
|
print(f"Contact '{DEST}' not found in contacts.")
|
||||||
return
|
return
|
||||||
else :
|
else :
|
||||||
@@ -72,8 +74,12 @@ async def main () :
|
|||||||
else :
|
else :
|
||||||
if line.startswith("send") :
|
if line.startswith("send") :
|
||||||
line = line[5:]
|
line = line[5:]
|
||||||
ret = await mc.commands.send_msg(contact , line)
|
result = await mc.commands.send_msg(contact, line)
|
||||||
exp_ack = ret["expected_ack"].hex()
|
if result.type == EventType.ERROR:
|
||||||
|
print(f"⚠️ Failed to send message: {result.payload}")
|
||||||
|
continue
|
||||||
|
|
||||||
|
exp_ack = result.payload["expected_ack"].hex()
|
||||||
print(" Sent ... ", end="", flush=True)
|
print(" Sent ... ", end="", flush=True)
|
||||||
res = await mc.wait_for_event(EventType.ACK, attribute_filters={"code": exp_ack}, timeout=5)
|
res = await mc.wait_for_event(EventType.ACK, attribute_filters={"code": exp_ack}, timeout=5)
|
||||||
if res is None :
|
if res is None :
|
||||||
@@ -82,7 +88,7 @@ async def main () :
|
|||||||
print ("Ack")
|
print ("Ack")
|
||||||
|
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
meshcore.stop()
|
mc.stop()
|
||||||
print("\nExiting...")
|
print("\nExiting...")
|
||||||
except asyncio.CancelledError:
|
except asyncio.CancelledError:
|
||||||
# Handle task cancellation from KeyboardInterrupt in asyncio.run()
|
# Handle task cancellation from KeyboardInterrupt in asyncio.run()
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import json
|
|||||||
|
|
||||||
from meshcore import MeshCore
|
from meshcore import MeshCore
|
||||||
from meshcore import SerialConnection
|
from meshcore import SerialConnection
|
||||||
|
from meshcore import EventType
|
||||||
|
|
||||||
PORT = "/dev/ttyUSB0"
|
PORT = "/dev/ttyUSB0"
|
||||||
BAUDRATE = 115200
|
BAUDRATE = 115200
|
||||||
@@ -16,6 +17,10 @@ async def main () :
|
|||||||
mc = MeshCore(con)
|
mc = MeshCore(con)
|
||||||
await mc.connect()
|
await mc.connect()
|
||||||
|
|
||||||
print(json.dumps(await mc.commands.get_contacts(),indent=4))
|
result = await mc.commands.get_contacts()
|
||||||
|
if result.type == EventType.ERROR:
|
||||||
|
print(f"Error getting contacts: {result.payload}")
|
||||||
|
else:
|
||||||
|
print(json.dumps(result.payload, indent=4))
|
||||||
|
|
||||||
asyncio.run(main())
|
asyncio.run(main())
|
||||||
|
|||||||
@@ -37,13 +37,17 @@ async def main():
|
|||||||
|
|
||||||
# Send the message and get the MSG_SENT event
|
# Send the message and get the MSG_SENT event
|
||||||
print(f"Sending message: '{args.message}'")
|
print(f"Sending message: '{args.message}'")
|
||||||
send_result = await mc.commands.send_msg(
|
result = await mc.commands.send_msg(
|
||||||
contact,
|
contact,
|
||||||
args.message
|
args.message
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if result.type == EventType.ERROR:
|
||||||
|
print(f"⚠️ Failed to send message: {result.payload}")
|
||||||
|
return
|
||||||
|
|
||||||
# Extract the expected ACK code
|
# Extract the expected ACK code
|
||||||
expected_ack = send_result["expected_ack"].hex()
|
expected_ack = result.payload["expected_ack"].hex()
|
||||||
print(f"Message sent, waiting for ACK with code: {expected_ack}")
|
print(f"Message sent, waiting for ACK with code: {expected_ack}")
|
||||||
|
|
||||||
# Wait for the specific ACK that matches our message
|
# Wait for the specific ACK that matches our message
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ async def main():
|
|||||||
print(f"Logging in to repeater '{args.repeater}'...")
|
print(f"Logging in to repeater '{args.repeater}'...")
|
||||||
login_event = await mc.commands.send_login(repeater, args.password)
|
login_event = await mc.commands.send_login(repeater, args.password)
|
||||||
|
|
||||||
if login_event and login_event.get("success") != False:
|
if login_event.type != EventType.ERROR:
|
||||||
print("Login successful")
|
print("Login successful")
|
||||||
|
|
||||||
# Send status request
|
# Send status request
|
||||||
|
|||||||
@@ -34,10 +34,10 @@ async def main():
|
|||||||
tag = random.randint(1, 0xFFFFFFFF)
|
tag = random.randint(1, 0xFFFFFFFF)
|
||||||
result = await mc.commands.send_trace(path=args.path, tag=tag)
|
result = await mc.commands.send_trace(path=args.path, tag=tag)
|
||||||
|
|
||||||
# Check if the result has a success indicator
|
# Check if the result is an error
|
||||||
if result.get("success") == False:
|
if result.type == EventType.ERROR:
|
||||||
print(f"Failed to send trace packet: {result.get('reason', 'unknown error')}")
|
print(f"Failed to send trace packet: {result.payload.get('reason', 'unknown error')}")
|
||||||
elif result:
|
elif result.type == EventType.MSG_SENT:
|
||||||
print(f"Trace packet sent successfully with tag={tag}")
|
print(f"Trace packet sent successfully with tag={tag}")
|
||||||
print("Waiting for trace response matching our tag...")
|
print("Waiting for trace response matching our tag...")
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,9 @@ async def handle_message(event):
|
|||||||
data = event.payload
|
data = event.payload
|
||||||
|
|
||||||
contact = mc.get_contact_by_key_prefix(data['pubkey_prefix'])
|
contact = mc.get_contact_by_key_prefix(data['pubkey_prefix'])
|
||||||
|
if contact is None:
|
||||||
|
print(f"Unknown contact with pubkey prefix: {data['pubkey_prefix']}")
|
||||||
|
return
|
||||||
print(f"{contact['adv_name']}: {data['text']}")
|
print(f"{contact['adv_name']}: {data['text']}")
|
||||||
|
|
||||||
async def main () :
|
async def main () :
|
||||||
@@ -54,7 +56,7 @@ async def main () :
|
|||||||
if line.startswith("to ") :
|
if line.startswith("to ") :
|
||||||
dest = line[3:]
|
dest = line[3:]
|
||||||
nc = mc.get_contact_by_name(dest)
|
nc = mc.get_contact_by_name(dest)
|
||||||
if mc is None:
|
if nc is None:
|
||||||
print(f"Contact '{DEST}' not found in contacts.")
|
print(f"Contact '{DEST}' not found in contacts.")
|
||||||
return
|
return
|
||||||
else :
|
else :
|
||||||
@@ -72,8 +74,12 @@ async def main () :
|
|||||||
else :
|
else :
|
||||||
if line.startswith("send") :
|
if line.startswith("send") :
|
||||||
line = line[5:]
|
line = line[5:]
|
||||||
ret = await mc.commands.send_msg(contact , line)
|
result = await mc.commands.send_msg(contact, line)
|
||||||
exp_ack = ret["expected_ack"].hex()
|
if result.type == EventType.ERROR:
|
||||||
|
print(f"⚠️ Failed to send message: {result.payload}")
|
||||||
|
continue
|
||||||
|
|
||||||
|
exp_ack = result.payload["expected_ack"].hex()
|
||||||
print(" Sent ... ", end="", flush=True)
|
print(" Sent ... ", end="", flush=True)
|
||||||
res = await mc.wait_for_event(EventType.ACK, attribute_filters={"code": exp_ack}, timeout=5)
|
res = await mc.wait_for_event(EventType.ACK, attribute_filters={"code": exp_ack}, timeout=5)
|
||||||
if res is None :
|
if res is None :
|
||||||
@@ -82,7 +88,7 @@ async def main () :
|
|||||||
print ("Ack")
|
print ("Ack")
|
||||||
|
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
meshcore.stop()
|
mc.stop()
|
||||||
print("\nExiting...")
|
print("\nExiting...")
|
||||||
except asyncio.CancelledError:
|
except asyncio.CancelledError:
|
||||||
# Handle task cancellation from KeyboardInterrupt in asyncio.run()
|
# Handle task cancellation from KeyboardInterrupt in asyncio.run()
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import asyncio
|
|||||||
import json
|
import json
|
||||||
from meshcore import TCPConnection
|
from meshcore import TCPConnection
|
||||||
from meshcore import MeshCore
|
from meshcore import MeshCore
|
||||||
|
from meshcore import EventType
|
||||||
|
|
||||||
HOSTNAME = "mchome"
|
HOSTNAME = "mchome"
|
||||||
PORT = 5000
|
PORT = 5000
|
||||||
@@ -14,5 +15,9 @@ async def main () :
|
|||||||
mc = MeshCore(con)
|
mc = MeshCore(con)
|
||||||
await mc.connect()
|
await mc.connect()
|
||||||
|
|
||||||
print(json.dumps(await mc.commands.get_contacts(),indent=4))
|
result = await mc.commands.get_contacts()
|
||||||
|
if result.type == EventType.ERROR:
|
||||||
|
print(f"Error getting contacts: {result.payload}")
|
||||||
|
else:
|
||||||
|
print(json.dumps(result.payload, indent=4))
|
||||||
asyncio.run(main())
|
asyncio.run(main())
|
||||||
|
|||||||
@@ -22,9 +22,14 @@ async def main () :
|
|||||||
if contact is None:
|
if contact is None:
|
||||||
print(f"Contact '{DEST}' not found in contacts.")
|
print(f"Contact '{DEST}' not found in contacts.")
|
||||||
return
|
return
|
||||||
ret = await mc.commands.send_msg(contact ,MSG)
|
result = await mc.commands.send_msg(contact, MSG)
|
||||||
print (ret)
|
print(result)
|
||||||
exp_ack = ret["expected_ack"].hex()
|
|
||||||
|
if result.type == EventType.ERROR:
|
||||||
|
print(f"⚠️ Failed to send message: {result.payload}")
|
||||||
|
return
|
||||||
|
|
||||||
|
exp_ack = result.payload["expected_ack"].hex()
|
||||||
print(await mc.wait_for_event(EventType.ACK, attribute_filters={"code": exp_ack}, timeout=5))
|
print(await mc.wait_for_event(EventType.ACK, attribute_filters={"code": exp_ack}, timeout=5))
|
||||||
|
|
||||||
asyncio.run(main())
|
asyncio.run(main())
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import asyncio
|
|||||||
import json
|
import json
|
||||||
from meshcore import TCPConnection
|
from meshcore import TCPConnection
|
||||||
from meshcore import MeshCore
|
from meshcore import MeshCore
|
||||||
|
from meshcore import EventType
|
||||||
|
|
||||||
HOSTNAME = "mchome"
|
HOSTNAME = "mchome"
|
||||||
PORT = 5000
|
PORT = 5000
|
||||||
@@ -19,9 +20,12 @@ async def main () :
|
|||||||
res = True
|
res = True
|
||||||
while res:
|
while res:
|
||||||
result = await mc.commands.get_msg()
|
result = await mc.commands.get_msg()
|
||||||
if result.get("success") == False:
|
if result.type == EventType.NO_MORE_MSGS:
|
||||||
res = False
|
res = False
|
||||||
print("No more messages")
|
print("No more messages")
|
||||||
print (result)
|
elif result.type == EventType.ERROR:
|
||||||
|
res = False
|
||||||
|
print(f"Error retrieving messages: {result.payload}")
|
||||||
|
print(result)
|
||||||
|
|
||||||
asyncio.run(main())
|
asyncio.run(main())
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import asyncio
|
import asyncio
|
||||||
import logging
|
import logging
|
||||||
from typing import Any, Dict, List, Optional, Union
|
|
||||||
from .events import EventType
|
|
||||||
import random
|
import random
|
||||||
|
from typing import Any, Dict, List, Optional, Union
|
||||||
|
from .events import Event, EventType
|
||||||
|
|
||||||
# Define types for destination parameters
|
# Define types for destination parameters
|
||||||
DestinationType = Union[bytes, str, Dict[str, Any]]
|
DestinationType = Union[bytes, str, Dict[str, Any]]
|
||||||
@@ -66,7 +66,7 @@ class CommandHandler:
|
|||||||
self.dispatcher = dispatcher
|
self.dispatcher = dispatcher
|
||||||
|
|
||||||
async def send(self, data: bytes, expected_events: Optional[Union[EventType, List[EventType]]] = None,
|
async def send(self, data: bytes, expected_events: Optional[Union[EventType, List[EventType]]] = None,
|
||||||
timeout: Optional[float] = None) -> Dict[str, Any]:
|
timeout: Optional[float] = None) -> Event:
|
||||||
"""
|
"""
|
||||||
Send a command and wait for expected event responses.
|
Send a command and wait for expected event responses.
|
||||||
|
|
||||||
@@ -76,7 +76,7 @@ class CommandHandler:
|
|||||||
timeout: Timeout in seconds, or None to use default_timeout
|
timeout: Timeout in seconds, or None to use default_timeout
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Dict[str, Any]: Dictionary containing the response data or status
|
Event: The full event object that was received in response to the command
|
||||||
"""
|
"""
|
||||||
if not self.dispatcher:
|
if not self.dispatcher:
|
||||||
raise RuntimeError("Dispatcher not set, cannot send commands")
|
raise RuntimeError("Dispatcher not set, cannot send commands")
|
||||||
@@ -119,66 +119,68 @@ class CommandHandler:
|
|||||||
for future in done:
|
for future in done:
|
||||||
event = await future
|
event = await future
|
||||||
if event:
|
if event:
|
||||||
return event.payload
|
return event
|
||||||
|
|
||||||
return {"success": False, "reason": "no_event_received"}
|
# Create an error event when no event is received
|
||||||
|
return Event(EventType.ERROR, {"reason": "no_event_received"})
|
||||||
except asyncio.TimeoutError:
|
except asyncio.TimeoutError:
|
||||||
logger.debug(f"Command timed out {data}")
|
logger.debug(f"Command timed out {data}")
|
||||||
return {"success": False, "reason": "timeout"}
|
return Event(EventType.ERROR, {"reason": "timeout"})
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.debug(f"Command error: {e}")
|
logger.debug(f"Command error: {e}")
|
||||||
return {"error": str(e)}
|
return Event(EventType.ERROR, {"error": str(e)})
|
||||||
return {"success": True}
|
# For commands that don't expect events, return a success event
|
||||||
|
return Event(EventType.OK, {})
|
||||||
|
|
||||||
|
|
||||||
async def send_appstart(self) -> Dict[str, Any]:
|
async def send_appstart(self) -> Event:
|
||||||
logger.debug("Sending appstart command")
|
logger.debug("Sending appstart command")
|
||||||
b1 = bytearray(b'\x01\x03 mccli')
|
b1 = bytearray(b'\x01\x03 mccli')
|
||||||
return await self.send(b1, [EventType.SELF_INFO])
|
return await self.send(b1, [EventType.SELF_INFO])
|
||||||
|
|
||||||
async def send_device_query(self) -> Dict[str, Any]:
|
async def send_device_query(self) -> Event:
|
||||||
logger.debug("Sending device query command")
|
logger.debug("Sending device query command")
|
||||||
return await self.send(b"\x16\x03", [EventType.DEVICE_INFO, EventType.ERROR])
|
return await self.send(b"\x16\x03", [EventType.DEVICE_INFO, EventType.ERROR])
|
||||||
|
|
||||||
async def send_advert(self, flood: bool = False) -> Dict[str, Any]:
|
async def send_advert(self, flood: bool = False) -> Event:
|
||||||
logger.debug(f"Sending advertisement command (flood={flood})")
|
logger.debug(f"Sending advertisement command (flood={flood})")
|
||||||
if flood:
|
if flood:
|
||||||
return await self.send(b"\x07\x01", [EventType.OK, EventType.ERROR])
|
return await self.send(b"\x07\x01", [EventType.OK, EventType.ERROR])
|
||||||
else:
|
else:
|
||||||
return await self.send(b"\x07", [EventType.OK, EventType.ERROR])
|
return await self.send(b"\x07", [EventType.OK, EventType.ERROR])
|
||||||
|
|
||||||
async def set_name(self, name: str) -> Dict[str, Any]:
|
async def set_name(self, name: str) -> Event:
|
||||||
logger.debug(f"Setting device name to: {name}")
|
logger.debug(f"Setting device name to: {name}")
|
||||||
return await self.send(b'\x08' + name.encode("ascii"), [EventType.OK, EventType.ERROR])
|
return await self.send(b'\x08' + name.encode("ascii"), [EventType.OK, EventType.ERROR])
|
||||||
|
|
||||||
async def set_coords(self, lat: float, lon: float) -> Dict[str, Any]:
|
async def set_coords(self, lat: float, lon: float) -> Event:
|
||||||
logger.debug(f"Setting coordinates to: lat={lat}, lon={lon}")
|
logger.debug(f"Setting coordinates to: lat={lat}, lon={lon}")
|
||||||
return await self.send(b'\x0e'\
|
return await self.send(b'\x0e'\
|
||||||
+ int(lat*1e6).to_bytes(4, 'little', signed=True)\
|
+ int(lat*1e6).to_bytes(4, 'little', signed=True)\
|
||||||
+ int(lon*1e6).to_bytes(4, 'little', signed=True)\
|
+ int(lon*1e6).to_bytes(4, 'little', signed=True)\
|
||||||
+ int(0).to_bytes(4, 'little'), [EventType.OK, EventType.ERROR])
|
+ int(0).to_bytes(4, 'little'), [EventType.OK, EventType.ERROR])
|
||||||
|
|
||||||
async def reboot(self) -> Dict[str, Any]:
|
async def reboot(self) -> Event:
|
||||||
logger.debug("Sending reboot command")
|
logger.debug("Sending reboot command")
|
||||||
return await self.send(b'\x13reboot')
|
return await self.send(b'\x13reboot')
|
||||||
|
|
||||||
async def get_bat(self) -> Dict[str, Any]:
|
async def get_bat(self) -> Event:
|
||||||
logger.debug("Getting battery information")
|
logger.debug("Getting battery information")
|
||||||
return await self.send(b'\x14', [EventType.BATTERY, EventType.ERROR])
|
return await self.send(b'\x14', [EventType.BATTERY, EventType.ERROR])
|
||||||
|
|
||||||
async def get_time(self) -> Dict[str, Any]:
|
async def get_time(self) -> Event:
|
||||||
logger.debug("Getting device time")
|
logger.debug("Getting device time")
|
||||||
return await self.send(b"\x05", [EventType.CURRENT_TIME, EventType.ERROR])
|
return await self.send(b"\x05", [EventType.CURRENT_TIME, EventType.ERROR])
|
||||||
|
|
||||||
async def set_time(self, val: int) -> Dict[str, Any]:
|
async def set_time(self, val: int) -> Event:
|
||||||
logger.debug(f"Setting device time to: {val}")
|
logger.debug(f"Setting device time to: {val}")
|
||||||
return await self.send(b"\x06" + int(val).to_bytes(4, 'little'), [EventType.OK, EventType.ERROR])
|
return await self.send(b"\x06" + int(val).to_bytes(4, 'little'), [EventType.OK, EventType.ERROR])
|
||||||
|
|
||||||
async def set_tx_power(self, val: int) -> Dict[str, Any]:
|
async def set_tx_power(self, val: int) -> Event:
|
||||||
logger.debug(f"Setting TX power to: {val}")
|
logger.debug(f"Setting TX power to: {val}")
|
||||||
return await self.send(b"\x0c" + int(val).to_bytes(4, 'little'), [EventType.OK, EventType.ERROR])
|
return await self.send(b"\x0c" + int(val).to_bytes(4, 'little'), [EventType.OK, EventType.ERROR])
|
||||||
|
|
||||||
async def set_radio(self, freq: float, bw: float, sf: int, cr: int) -> Dict[str, Any]:
|
async def set_radio(self, freq: float, bw: float, sf: int, cr: int) -> Event:
|
||||||
logger.debug(f"Setting radio params: freq={freq}, bw={bw}, sf={sf}, cr={cr}")
|
logger.debug(f"Setting radio params: freq={freq}, bw={bw}, sf={sf}, cr={cr}")
|
||||||
return await self.send(b"\x0b" \
|
return await self.send(b"\x0b" \
|
||||||
+ int(float(freq)*1000).to_bytes(4, 'little')\
|
+ int(float(freq)*1000).to_bytes(4, 'little')\
|
||||||
@@ -186,7 +188,7 @@ class CommandHandler:
|
|||||||
+ int(sf).to_bytes(1, 'little')\
|
+ int(sf).to_bytes(1, 'little')\
|
||||||
+ int(cr).to_bytes(1, 'little'), [EventType.OK, EventType.ERROR])
|
+ int(cr).to_bytes(1, 'little'), [EventType.OK, EventType.ERROR])
|
||||||
|
|
||||||
async def set_tuning(self, rx_dly: int, af: int) -> Dict[str, Any]:
|
async def set_tuning(self, rx_dly: int, af: int) -> Event:
|
||||||
logger.debug(f"Setting tuning params: rx_dly={rx_dly}, af={af}")
|
logger.debug(f"Setting tuning params: rx_dly={rx_dly}, af={af}")
|
||||||
return await self.send(b"\x15" \
|
return await self.send(b"\x15" \
|
||||||
+ int(rx_dly).to_bytes(4, 'little')\
|
+ int(rx_dly).to_bytes(4, 'little')\
|
||||||
@@ -194,28 +196,28 @@ class CommandHandler:
|
|||||||
+ int(0).to_bytes(1, 'little')\
|
+ int(0).to_bytes(1, 'little')\
|
||||||
+ int(0).to_bytes(1, 'little'), [EventType.OK, EventType.ERROR])
|
+ int(0).to_bytes(1, 'little'), [EventType.OK, EventType.ERROR])
|
||||||
|
|
||||||
async def set_devicepin(self, pin: int) -> Dict[str, Any]:
|
async def set_devicepin(self, pin: int) -> Event:
|
||||||
logger.debug(f"Setting device PIN to: {pin}")
|
logger.debug(f"Setting device PIN to: {pin}")
|
||||||
return await self.send(b"\x25" \
|
return await self.send(b"\x25" \
|
||||||
+ int(pin).to_bytes(4, 'little'), [EventType.OK, EventType.ERROR])
|
+ int(pin).to_bytes(4, 'little'), [EventType.OK, EventType.ERROR])
|
||||||
|
|
||||||
async def get_contacts(self) -> Dict[str, Any]:
|
async def get_contacts(self) -> Event:
|
||||||
logger.debug("Getting contacts")
|
logger.debug("Getting contacts")
|
||||||
return await self.send(b"\x04", [EventType.CONTACTS, EventType.ERROR])
|
return await self.send(b"\x04", [EventType.CONTACTS, EventType.ERROR])
|
||||||
|
|
||||||
async def reset_path(self, key: DestinationType) -> Dict[str, Any]:
|
async def reset_path(self, key: DestinationType) -> Event:
|
||||||
key_bytes = _validate_destination(key, prefix_length=32)
|
key_bytes = _validate_destination(key, prefix_length=32)
|
||||||
logger.debug(f"Resetting path for contact: {key_bytes.hex()}")
|
logger.debug(f"Resetting path for contact: {key_bytes.hex()}")
|
||||||
data = b"\x0D" + key_bytes
|
data = b"\x0D" + key_bytes
|
||||||
return await self.send(data, [EventType.OK, EventType.ERROR])
|
return await self.send(data, [EventType.OK, EventType.ERROR])
|
||||||
|
|
||||||
async def share_contact(self, key: DestinationType) -> Dict[str, Any]:
|
async def share_contact(self, key: DestinationType) -> Event:
|
||||||
key_bytes = _validate_destination(key, prefix_length=32)
|
key_bytes = _validate_destination(key, prefix_length=32)
|
||||||
logger.debug(f"Sharing contact: {key_bytes.hex()}")
|
logger.debug(f"Sharing contact: {key_bytes.hex()}")
|
||||||
data = b"\x10" + key_bytes
|
data = b"\x10" + key_bytes
|
||||||
return await self.send(data, [EventType.CONTACT_SHARE, EventType.ERROR])
|
return await self.send(data, [EventType.CONTACT_SHARE, EventType.ERROR])
|
||||||
|
|
||||||
async def export_contact(self, key: Optional[DestinationType] = None) -> Dict[str, Any]:
|
async def export_contact(self, key: Optional[DestinationType] = None) -> Event:
|
||||||
if key:
|
if key:
|
||||||
key_bytes = _validate_destination(key, prefix_length=32)
|
key_bytes = _validate_destination(key, prefix_length=32)
|
||||||
logger.debug(f"Exporting contact: {key_bytes.hex()}")
|
logger.debug(f"Exporting contact: {key_bytes.hex()}")
|
||||||
@@ -225,13 +227,13 @@ class CommandHandler:
|
|||||||
data = b"\x11"
|
data = b"\x11"
|
||||||
return await self.send(data, [EventType.OK, EventType.ERROR])
|
return await self.send(data, [EventType.OK, EventType.ERROR])
|
||||||
|
|
||||||
async def remove_contact(self, key: DestinationType) -> Dict[str, Any]:
|
async def remove_contact(self, key: DestinationType) -> Event:
|
||||||
key_bytes = _validate_destination(key, prefix_length=32)
|
key_bytes = _validate_destination(key, prefix_length=32)
|
||||||
logger.debug(f"Removing contact: {key_bytes.hex()}")
|
logger.debug(f"Removing contact: {key_bytes.hex()}")
|
||||||
data = b"\x0f" + key_bytes
|
data = b"\x0f" + key_bytes
|
||||||
return await self.send(data, [EventType.OK, EventType.ERROR])
|
return await self.send(data, [EventType.OK, EventType.ERROR])
|
||||||
|
|
||||||
async def change_contact_path (self, contact, path) -> Dict[str, Any]:
|
async def change_contact_path (self, contact, path) -> Event:
|
||||||
out_path_hex = path
|
out_path_hex = path
|
||||||
out_path_len = int(len(path) / 2)
|
out_path_len = int(len(path) / 2)
|
||||||
out_path_hex = out_path_hex + (128-len(out_path_hex)) * "0"
|
out_path_hex = out_path_hex + (128-len(out_path_hex)) * "0"
|
||||||
@@ -249,29 +251,29 @@ class CommandHandler:
|
|||||||
+ int(contact["adv_lon"]*1e6).to_bytes(4, 'little', signed=True)
|
+ int(contact["adv_lon"]*1e6).to_bytes(4, 'little', signed=True)
|
||||||
return await self.send(data, [EventType.OK, EventType.ERROR])
|
return await self.send(data, [EventType.OK, EventType.ERROR])
|
||||||
|
|
||||||
async def get_msg(self, timeout: Optional[float] = 1) -> Dict[str, Any]:
|
async def get_msg(self, timeout: Optional[float] = 1) -> Event:
|
||||||
logger.debug("Requesting pending messages")
|
logger.debug("Requesting pending messages")
|
||||||
return await self.send(b"\x0A", [EventType.CONTACT_MSG_RECV, EventType.CHANNEL_MSG_RECV, EventType.ERROR], timeout)
|
return await self.send(b"\x0A", [EventType.CONTACT_MSG_RECV, EventType.CHANNEL_MSG_RECV, EventType.ERROR], timeout)
|
||||||
|
|
||||||
async def send_login(self, dst: DestinationType, pwd: str) -> Dict[str, Any]:
|
async def send_login(self, dst: DestinationType, pwd: str) -> Event:
|
||||||
dst_bytes = _validate_destination(dst, prefix_length=32)
|
dst_bytes = _validate_destination(dst, prefix_length=32)
|
||||||
logger.debug(f"Sending login request to: {dst_bytes.hex()}")
|
logger.debug(f"Sending login request to: {dst_bytes.hex()}")
|
||||||
data = b"\x1a" + dst_bytes + pwd.encode("ascii")
|
data = b"\x1a" + dst_bytes + pwd.encode("ascii")
|
||||||
return await self.send(data, [EventType.MSG_SENT, EventType.ERROR])
|
return await self.send(data, [EventType.MSG_SENT, EventType.ERROR])
|
||||||
|
|
||||||
async def send_logout(self, dst: DestinationType) -> Dict[str, Any]:
|
async def send_logout(self, dst: DestinationType) -> Event:
|
||||||
dst_bytes = _validate_destination(dst)
|
dst_bytes = _validate_destination(dst)
|
||||||
self.login_resp = asyncio.Future()
|
self.login_resp = asyncio.Future()
|
||||||
data = b"\x1d" + dst_bytes
|
data = b"\x1d" + dst_bytes
|
||||||
return await self.send(data, [EventType.MSG_SENT, EventType.ERROR])
|
return await self.send(data, [EventType.MSG_SENT, EventType.ERROR])
|
||||||
|
|
||||||
async def send_statusreq(self, dst: DestinationType) -> Dict[str, Any]:
|
async def send_statusreq(self, dst: DestinationType) -> Event:
|
||||||
dst_bytes = _validate_destination(dst, prefix_length=32)
|
dst_bytes = _validate_destination(dst, prefix_length=32)
|
||||||
logger.debug(f"Sending status request to: {dst_bytes.hex()}")
|
logger.debug(f"Sending status request to: {dst_bytes.hex()}")
|
||||||
data = b"\x1b" + dst_bytes
|
data = b"\x1b" + dst_bytes
|
||||||
return await self.send(data, [EventType.MSG_SENT, EventType.ERROR])
|
return await self.send(data, [EventType.MSG_SENT, EventType.ERROR])
|
||||||
|
|
||||||
async def send_cmd(self, dst: DestinationType, cmd: str, timestamp: Optional[int] = None) -> Dict[str, Any]:
|
async def send_cmd(self, dst: DestinationType, cmd: str, timestamp: Optional[int] = None) -> Event:
|
||||||
dst_bytes = _validate_destination(dst)
|
dst_bytes = _validate_destination(dst)
|
||||||
logger.debug(f"Sending command to {dst_bytes.hex()}: {cmd}")
|
logger.debug(f"Sending command to {dst_bytes.hex()}: {cmd}")
|
||||||
|
|
||||||
@@ -282,7 +284,7 @@ class CommandHandler:
|
|||||||
data = b"\x02\x01\x00" + timestamp.to_bytes(4, 'little') + dst_bytes + cmd.encode("ascii")
|
data = b"\x02\x01\x00" + timestamp.to_bytes(4, 'little') + dst_bytes + cmd.encode("ascii")
|
||||||
return await self.send(data, [EventType.OK, EventType.ERROR])
|
return await self.send(data, [EventType.OK, EventType.ERROR])
|
||||||
|
|
||||||
async def send_msg(self, dst: DestinationType, msg: str, timestamp: Optional[int] = None) -> Dict[str, Any]:
|
async def send_msg(self, dst: DestinationType, msg: str, timestamp: Optional[int] = None) -> Event:
|
||||||
dst_bytes = _validate_destination(dst)
|
dst_bytes = _validate_destination(dst)
|
||||||
logger.debug(f"Sending message to {dst_bytes.hex()}: {msg}")
|
logger.debug(f"Sending message to {dst_bytes.hex()}: {msg}")
|
||||||
|
|
||||||
@@ -293,7 +295,7 @@ class CommandHandler:
|
|||||||
data = b"\x02\x00\x00" + timestamp.to_bytes(4, 'little') + dst_bytes + msg.encode("ascii")
|
data = b"\x02\x00\x00" + timestamp.to_bytes(4, 'little') + dst_bytes + msg.encode("ascii")
|
||||||
return await self.send(data, [EventType.MSG_SENT, EventType.ERROR])
|
return await self.send(data, [EventType.MSG_SENT, EventType.ERROR])
|
||||||
|
|
||||||
async def send_chan_msg(self, chan, msg, timestamp=None):
|
async def send_chan_msg(self, chan, msg, timestamp=None) -> Event:
|
||||||
logger.debug(f"Sending channel message to channel {chan}: {msg}")
|
logger.debug(f"Sending channel message to channel {chan}: {msg}")
|
||||||
|
|
||||||
# Default to current time if timestamp not provided
|
# Default to current time if timestamp not provided
|
||||||
@@ -304,13 +306,13 @@ class CommandHandler:
|
|||||||
data = b"\x03\x00" + chan.to_bytes(1, 'little') + timestamp + msg.encode("ascii")
|
data = b"\x03\x00" + chan.to_bytes(1, 'little') + timestamp + msg.encode("ascii")
|
||||||
return await self.send(data, [EventType.MSG_SENT, EventType.ERROR])
|
return await self.send(data, [EventType.MSG_SENT, EventType.ERROR])
|
||||||
|
|
||||||
async def send_cli(self, cmd):
|
async def send_cli(self, cmd) -> Event:
|
||||||
logger.debug(f"Sending CLI command: {cmd}")
|
logger.debug(f"Sending CLI command: {cmd}")
|
||||||
data = b"\x32" + cmd.encode('ascii')
|
data = b"\x32" + cmd.encode('ascii')
|
||||||
return await self.send(data, [EventType.CLI_RESPONSE, EventType.ERROR])
|
return await self.send(data, [EventType.CLI_RESPONSE, EventType.ERROR])
|
||||||
|
|
||||||
async def send_trace(self, auth_code: int = 0, tag: Optional[int] = None,
|
async def send_trace(self, auth_code: int = 0, tag: Optional[int] = None,
|
||||||
flags: int = 0, path: Optional[Union[str, bytes, bytearray]] = None) -> Dict[str, Any]:
|
flags: int = 0, path: Optional[Union[str, bytes, bytearray]] = None) -> Event:
|
||||||
"""
|
"""
|
||||||
Send a trace packet to test routing through specific repeaters
|
Send a trace packet to test routing through specific repeaters
|
||||||
|
|
||||||
@@ -322,7 +324,7 @@ class CommandHandler:
|
|||||||
or a bytes/bytearray object with the raw path data
|
or a bytes/bytearray object with the raw path data
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Dictionary with sent status, tag, and estimated timeout in milliseconds, or False if command failed
|
Event object with sent status, tag, and estimated timeout in milliseconds
|
||||||
"""
|
"""
|
||||||
# Generate random tag if not provided
|
# Generate random tag if not provided
|
||||||
if tag is None:
|
if tag is None:
|
||||||
@@ -350,11 +352,11 @@ class CommandHandler:
|
|||||||
cmd_data.extend(path_bytes)
|
cmd_data.extend(path_bytes)
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
logger.error(f"Invalid path format: {e}")
|
logger.error(f"Invalid path format: {e}")
|
||||||
return { "success": False, "reason": "invalid_path_format" }
|
return Event(EventType.ERROR, {"reason": "invalid_path_format"})
|
||||||
elif isinstance(path, (bytes, bytearray)):
|
elif isinstance(path, (bytes, bytearray)):
|
||||||
cmd_data.extend(path)
|
cmd_data.extend(path)
|
||||||
else:
|
else:
|
||||||
logger.error(f"Unsupported path type: {type(path)}")
|
logger.error(f"Unsupported path type: {type(path)}")
|
||||||
return { "success": False, "reason": "unsupported_path_type" }
|
return Event(EventType.ERROR, {"reason": "unsupported_path_type"})
|
||||||
|
|
||||||
return await self.send(cmd_data, [EventType.MSG_SENT, EventType.ERROR])
|
return await self.send(cmd_data, [EventType.MSG_SENT, EventType.ERROR])
|
||||||
|
|||||||
@@ -262,7 +262,8 @@ class MeshCore:
|
|||||||
result = await self.commands.get_msg()
|
result = await self.commands.get_msg()
|
||||||
|
|
||||||
# If we got a NO_MORE_MSGS event or an error, stop fetching
|
# If we got a NO_MORE_MSGS event or an error, stop fetching
|
||||||
if not result.get("success") or isinstance(result, dict) and "error" in result:
|
if result.type == EventType.NO_MORE_MSGS or result.type == EventType.ERROR:
|
||||||
|
logger.debug("No more messages or error occurred, stopping auto-fetch.")
|
||||||
break
|
break
|
||||||
|
|
||||||
# Small delay to prevent overwhelming the device
|
# Small delay to prevent overwhelming the device
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ class MessageReader:
|
|||||||
|
|
||||||
# Handle command responses
|
# Handle command responses
|
||||||
if packet_type_value == PacketType.OK.value:
|
if packet_type_value == PacketType.OK.value:
|
||||||
result: Dict[str, Any] = {"success": True}
|
result: Dict[str, Any] = {}
|
||||||
if len(data) == 5:
|
if len(data) == 5:
|
||||||
result["value"] = int.from_bytes(data[1:5], byteorder='little')
|
result["value"] = int.from_bytes(data[1:5], byteorder='little')
|
||||||
|
|
||||||
@@ -31,9 +31,9 @@ class MessageReader:
|
|||||||
|
|
||||||
elif packet_type_value == PacketType.ERROR.value:
|
elif packet_type_value == PacketType.ERROR.value:
|
||||||
if len(data) > 1:
|
if len(data) > 1:
|
||||||
result = {"success": False, "error_code": data[1]}
|
result = {"error_code": data[1]}
|
||||||
else:
|
else:
|
||||||
result = {"success": False}
|
result = {}
|
||||||
|
|
||||||
# Dispatch event for the ERROR response
|
# Dispatch event for the ERROR response
|
||||||
await self.dispatcher.dispatch(Event(EventType.ERROR, result))
|
await self.dispatcher.dispatch(Event(EventType.ERROR, result))
|
||||||
|
|||||||
@@ -45,7 +45,10 @@ class TCPConnection:
|
|||||||
self.host, self.port)
|
self.host, self.port)
|
||||||
|
|
||||||
logger.info("TCP Connection started")
|
logger.info("TCP Connection started")
|
||||||
return self.host
|
future = asyncio.Future()
|
||||||
|
future.set_result(self.host)
|
||||||
|
|
||||||
|
return future
|
||||||
|
|
||||||
def set_reader(self, reader) :
|
def set_reader(self, reader) :
|
||||||
self.reader = reader
|
self.reader = reader
|
||||||
|
|||||||
@@ -47,22 +47,25 @@ def setup_event_response(mock_dispatcher, event_type, payload, attribute_filters
|
|||||||
async def test_send_basic(command_handler, mock_connection):
|
async def test_send_basic(command_handler, mock_connection):
|
||||||
result = await command_handler.send(b"test_data")
|
result = await command_handler.send(b"test_data")
|
||||||
mock_connection.send.assert_called_once_with(b"test_data")
|
mock_connection.send.assert_called_once_with(b"test_data")
|
||||||
assert result == {"success": True}
|
assert result.type == EventType.OK
|
||||||
|
assert result.payload == {}
|
||||||
|
|
||||||
async def test_send_with_event(command_handler, mock_connection, mock_dispatcher):
|
async def test_send_with_event(command_handler, mock_connection, mock_dispatcher):
|
||||||
expected_payload = {"success": True, "value": 42}
|
expected_payload = {"value": 42}
|
||||||
setup_event_response(mock_dispatcher, EventType.OK, expected_payload)
|
setup_event_response(mock_dispatcher, EventType.OK, expected_payload)
|
||||||
|
|
||||||
result = await command_handler.send(b"test_command", [EventType.OK])
|
result = await command_handler.send(b"test_command", [EventType.OK])
|
||||||
|
|
||||||
mock_connection.send.assert_called_once_with(b"test_command")
|
mock_connection.send.assert_called_once_with(b"test_command")
|
||||||
assert result == expected_payload
|
assert result.type == EventType.OK
|
||||||
|
assert result.payload == expected_payload
|
||||||
|
|
||||||
async def test_send_timeout(command_handler, mock_connection, mock_dispatcher):
|
async def test_send_timeout(command_handler, mock_connection, mock_dispatcher):
|
||||||
mock_dispatcher.wait_for_event.side_effect = asyncio.TimeoutError
|
mock_dispatcher.wait_for_event.side_effect = asyncio.TimeoutError
|
||||||
|
|
||||||
result = await command_handler.send(b"test_command", [EventType.OK], timeout=0.1)
|
result = await command_handler.send(b"test_command", [EventType.OK], timeout=0.1)
|
||||||
assert result == {"success": False, "reason": "timeout"}
|
assert result.type == EventType.ERROR
|
||||||
|
assert result.payload == {"reason": "timeout"}
|
||||||
|
|
||||||
# Destination validation tests
|
# Destination validation tests
|
||||||
async def test_validate_destination_bytes(command_handler, mock_connection):
|
async def test_validate_destination_bytes(command_handler, mock_connection):
|
||||||
@@ -235,7 +238,7 @@ async def test_send_trace(command_handler, mock_connection):
|
|||||||
|
|
||||||
async def test_send_with_multiple_expected_events_returns_first_completed(command_handler, mock_connection, mock_dispatcher):
|
async def test_send_with_multiple_expected_events_returns_first_completed(command_handler, mock_connection, mock_dispatcher):
|
||||||
# Setup the dispatcher to return an ERROR event
|
# Setup the dispatcher to return an ERROR event
|
||||||
error_payload = {"success": False, "reason": "command_failed"}
|
error_payload = {"reason": "command_failed"}
|
||||||
|
|
||||||
async def simulate_error_event(*args, **kwargs):
|
async def simulate_error_event(*args, **kwargs):
|
||||||
# Simulate an ERROR event being returned
|
# Simulate an ERROR event being returned
|
||||||
@@ -251,4 +254,5 @@ async def test_send_with_multiple_expected_events_returns_first_completed(comman
|
|||||||
mock_connection.send.assert_called_once_with(b"test_command")
|
mock_connection.send.assert_called_once_with(b"test_command")
|
||||||
|
|
||||||
# Verify that even though OK was listed first, the ERROR event was returned
|
# Verify that even though OK was listed first, the ERROR event was returned
|
||||||
assert result == error_payload
|
assert result.type == EventType.ERROR
|
||||||
|
assert result.payload == error_payload
|
||||||
Reference in New Issue
Block a user