support for run_cli_command

This commit is contained in:
Florent
2026-08-23 20:26:00 -04:00
parent c487efbe18
commit 7f614fe9b8
6 changed files with 49 additions and 11 deletions
+1 -1
View File
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
[project] [project]
name = "meshcore" name = "meshcore"
version = "2.3.8" version = "2.3.9"
authors = [ authors = [
{ name="Florent de Lamotte", email="florent@frizoncorrea.fr" }, { name="Florent de Lamotte", email="florent@frizoncorrea.fr" },
{ name="Alex Wolden", email="awolden@gmail.com" }, { name="Alex Wolden", email="awolden@gmail.com" },
+6
View File
@@ -16,6 +16,12 @@ class DeviceCommands(CommandHandlerBase):
b1 = bytearray(b"\x01\x03 mccli") b1 = bytearray(b"\x01\x03 mccli")
return await self.send(b1, [EventType.SELF_INFO, EventType.ERROR]) return await self.send(b1, [EventType.SELF_INFO, EventType.ERROR])
async def run_cli_command(self, cmd: str) -> Event:
logger.debug(f"Sending cli command: {cmd}")
cmd_data = bytearray([CommandType.RUN_CLI_COMMAND.value])
cmd_data.extend(cmd.encode("utf-8"))
return await self.send(cmd_data, [EventType.CLI_REPLY, EventType.ERROR])
async def send_device_query(self) -> Event: 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])
+20 -10
View File
@@ -4,7 +4,7 @@ from typing import Optional, Union
from hashlib import sha256 from hashlib import sha256
from ..events import Event, EventType from ..events import Event, EventType
from ..packets import CommandType from ..packets import CommandType, TxtType, AdvType
from .base import CommandHandlerBase, DestinationType, _validate_destination from .base import CommandHandlerBase, DestinationType, _validate_destination
logger = logging.getLogger("meshcore") logger = logging.getLogger("meshcore")
@@ -61,23 +61,33 @@ class MessagingCommands(CommandHandlerBase):
return await self.send(data, [EventType.MSG_SENT, EventType.ERROR]) return await self.send(data, [EventType.MSG_SENT, EventType.ERROR])
async def send_cmd( async def send_cmd(
self, dst: DestinationType, cmd: str, timestamp: Optional[int] = None self, dst: DestinationType, cmd: str, timestamp: Optional[int] = None,
dst_type = None # if None, will have to get a contact to know this
) -> Event: ) -> 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}")
if timestamp is None: if timestamp is None:
import time import time
timestamp = int(time.time()) timestamp = int(time.time())
data = ( if dst_type is None:
b"\x02\x01\x00" if isinstance(dst, dict) and "type" in dst :
+ timestamp.to_bytes(4, "little") dst_type = dst["type"]
+ dst_bytes else: # assume destination is a repeater
+ cmd.encode("utf-8") logger.warning("Can't determine destination type, please ensure contacts first or specify dst_type when calling `send_cmd`. Assuming it's a repeater.")
) dst_type = AdvType.REPEATER.value
return await self.send(data, [EventType.MSG_SENT, EventType.ERROR])
cmd_data = bytearray([CommandType.SEND_TXT_MSG.value])
if dst_type == AdvType.REPEATER.value :
cmd_data.append(TxtType.CLI_DATA.value)
else:
cmd_data.append(TxtType.CLI_CMD.value)
cmd_data.append(0) # first and only attempt
cmd_data.extend(timestamp.to_bytes(4, "little"))
cmd_data.extend(dst_bytes)
cmd_data.extend(cmd.encode("utf-8"))
return await self.send(cmd_data, [EventType.MSG_SENT, EventType.ERROR])
async def send_msg( async def send_msg(
self, dst: DestinationType, msg: str, timestamp: Optional[int] = None, self, dst: DestinationType, msg: str, timestamp: Optional[int] = None,
+1
View File
@@ -25,6 +25,7 @@ class EventType(Enum):
NEXT_CONTACT = "next_contact" NEXT_CONTACT = "next_contact"
AUTOADD_CONFIG = "autoadd_config" AUTOADD_CONFIG = "autoadd_config"
ADVERT_PATH = "advert_path" ADVERT_PATH = "advert_path"
CLI_REPLY = "cli_reply"
# Push notifications # Push notifications
ADVERTISEMENT = "advertisement" ADVERTISEMENT = "advertisement"
+16
View File
@@ -1,5 +1,18 @@
from enum import Enum from enum import Enum
class AdvType(Enum):
NONE = 0x00
CHAT = 0x01
REPEATER = 0x02
ROOM = 0x03
SENSOR = 0x04
class TxtType(Enum):
PLAIN = 0x00
CLI_DATA = 0x01
SIGNED_PLAIN = 0x02
CLI_CMD = 0x03
class AnonReqType(Enum): class AnonReqType(Enum):
REGIONS = 0x01 REGIONS = 0x01
OWNER = 0x02 OWNER = 0x02
@@ -75,6 +88,8 @@ class CommandType(Enum):
SET_PATH_HASH_MODE = 61 SET_PATH_HASH_MODE = 61
SET_DEFAULT_FLOOD_SCOPE = 63 SET_DEFAULT_FLOOD_SCOPE = 63
GET_DEFAULT_FLOOD_SCOPE = 64 GET_DEFAULT_FLOOD_SCOPE = 64
SEND_RAW_PACKET = 65
RUN_CLI_COMMAND = 66
# Packet prefixes for the protocol # Packet prefixes for the protocol
class PacketType(Enum): class PacketType(Enum):
@@ -107,6 +122,7 @@ class PacketType(Enum):
ALLOWED_REPEAT_FREQ = 26 ALLOWED_REPEAT_FREQ = 26
CHANNEL_DATA_RECV = 27 CHANNEL_DATA_RECV = 27
DEFAULT_FLOOD_SCOPE = 28 DEFAULT_FLOOD_SCOPE = 28
CLI_REPLY = 29
# Push notifications # Push notifications
ADVERTISEMENT = 0x80 ADVERTISEMENT = 0x80
+5
View File
@@ -1052,6 +1052,11 @@ class MessageReader:
res = {"rx_delay": rx_delay, "airtime_factor": airtime_factor} res = {"rx_delay": rx_delay, "airtime_factor": airtime_factor}
await self.dispatcher.dispatch(Event(EventType.TUNING_PARAMS, res)) await self.dispatcher.dispatch(Event(EventType.TUNING_PARAMS, res))
elif packet_type_value == PacketType.CLI_REPLY.value:
res = {}
res["text"] = data[1:].decode("utf-8", "ignore")
await self.dispatcher.dispatch(Event(EventType.CLI_REPLY, res))
else: else:
logger.debug(f"Unhandled data received {data}") logger.debug(f"Unhandled data received {data}")
logger.debug(f"Unhandled packet type: {packet_type_value}") logger.debug(f"Unhandled packet type: {packet_type_value}")