Skip to content

Node API Reference

This section documents the node management classes and functions in pyMC_Core.

MeshNode

pymc_core.node.node.MeshNode

MeshNode(radio, local_identity, config=None, *, contacts=None, channel_db=None, logger=None, event_service=None)

Represents a node in a mesh network for radio communication.

Manages radio communication, message routing, and protocol handling within a mesh network. Provides high-level APIs for sending messages, telemetry requests, and commands to other nodes and repeaters.

The node integrates with various components like contact storage, channel databases, and event services for comprehensive mesh functionality.

Initialise a mesh network node instance.

Sets up the node's core components including radio interface, identity management, and communication handlers.

Parameters:

Name Type Description Default
radio Optional[Any]

Radio hardware interface for transmission/reception.

required
local_identity LocalIdentity

Node's cryptographic identity for secure communication.

required
config Optional[dict]

Optional configuration dictionary with node settings.

None
contacts Optional[Any]

Optional contact storage for managing known nodes.

None
channel_db Optional[Any]

Optional channel database for group communication.

None
logger Optional[Logger]

Optional logger instance; defaults to module logger.

None
event_service Optional[Any]

Optional event service for broadcasting mesh events.

None

send_group_text async

send_group_text(group_name, message)

Broadcast a text message to all members of a group.

Sends a group datagram that will be received by all nodes configured for the specified group. Group messages are fire-and-forget with no acknowledgements expected.

Parameters:

Name Type Description Default
group_name str

Name of the group to broadcast to.

required
message str

Text content to broadcast.

required

Returns:

Name Type Description
dict

Dictionary with transmission results and signal metrics.

Note dict

Group messages don't wait for acknowledgements.

Example
result = await node.send_group_text("team_alpha", "Meeting at 15:00")
print(f"Broadcast to {result['group']}: {result['success']}")

send_login async

send_login(repeater_name, password)

Authenticate with a repeater node.

Sends login credentials to a repeater and waits for authentication response. Successful login may grant administrative privileges.

Parameters:

Name Type Description Default
repeater_name str

Name of the repeater to authenticate with.

required
password str

Authentication password for the repeater.

required

Returns:

Type Description
dict

Dictionary with login results including success status,

dict

admin privileges, and keep-alive intervals.

Raises:

Type Description
RuntimeError

If repeater contact not found.

Example
result = await node.send_login("repeater_01", "secret123")
if result["success"] and result["is_admin"]:
    print("Admin access granted")

send_logout async

send_logout(repeater_name)

Terminate authentication session with a repeater.

Sends a logout command to end the current session with a repeater. This should be called when finished with repeater operations.

Parameters:

Name Type Description Default
repeater_name str

Name of the repeater to logout from.

required

Returns:

Type Description
dict

Dictionary with logout results and performance metrics.

Raises:

Type Description
RuntimeError

If repeater contact not found.

Example
result = await node.send_logout("repeater_01")
print(f"Logout {'successful' if result['success'] else 'failed'}")

send_protocol_request async

send_protocol_request(repeater_name, protocol_code, data=b'')

Send a protocol-specific request to a repeater.

Transmits a custom protocol request with optional data payload and waits for the repeater's response.

Parameters:

Name Type Description Default
repeater_name str

Name of the repeater to send request to.

required
protocol_code int

Protocol operation code (0-255).

required
data bytes

Optional binary data payload for the request.

b''

Returns:

Type Description
dict

Dictionary with protocol response, parsed data, and timing metrics.

Raises:

Type Description
RuntimeError

If repeater contact or protocol handler not found.

Example
result = await node.send_protocol_request("repeater_01", 0x10, b"config")
if result["success"]:
    print(f"Response: {result['response']}")

send_repeater_command async

send_repeater_command(repeater_name, command, parameters=None)

Send a text-based command to a repeater and await response.

Transmits a command string to a repeater using the text message protocol and waits for a response. Useful for administrative operations and status queries.

Parameters:

Name Type Description Default
repeater_name str

Name of the repeater to send command to.

required
command str

Command string to execute on the repeater.

required
parameters Optional[str]

Optional parameters for the command.

None

Returns:

Type Description
dict

Dictionary with command results, response text, and timing data.

Raises:

Type Description
RuntimeError

If repeater contact not found.

Example
result = await node.send_repeater_command("repeater_01", "status")
if result["success"]:
    print(f"Response: {result['response']}")

send_status_request async

send_status_request(repeater_name)

Request status information from a repeater.

Queries a repeater for its current operational status and configuration. This is a convenience method that uses the text command interface.

Parameters:

Name Type Description Default
repeater_name str

Name of the repeater to query.

required

Returns:

Type Description
dict

Dictionary with status information and response metrics.

Raises:

Type Description
RuntimeError

If repeater contact not found.

Example
status = await node.send_status_request("repeater_01")
if status["success"]:
    print(f"Status: {status['response']}")

send_telemetry_request async

send_telemetry_request(contact_name, want_base=True, want_location=True, want_environment=True, timeout=10.0)

Request telemetry data from a contact node.

Sends a telemetry request and waits for the target node to respond with requested sensor data including base metrics, location, and environmental readings.

Parameters:

Name Type Description Default
contact_name str

Name of the contact to query.

required
want_base bool

Include basic telemetry metrics in request.

True
want_location bool

Include GPS/location data in request.

True
want_environment bool

Include environmental sensors in request.

True
timeout float

Maximum time to wait for response in seconds.

10.0

Returns:

Type Description
dict

Dictionary with request results, telemetry data, and performance

dict

metrics including round-trip time.

Raises:

Type Description
RuntimeError

If contact not found or protocol handler unavailable.

Example
result = await node.send_telemetry_request("sensor_node")
if result["success"]:
    print(f"Temperature: {result['telemetry_data'].get('temp')}")

send_text async

send_text(contact_name, message, attempt=1, message_type='direct', out_path=None)

Send a text message to a specified contact.

Transmits a text message to another node in the mesh network, with optional routing and retry configuration.

Parameters:

Name Type Description Default
contact_name str

Name of the target contact in the contact book.

required
message str

Text content to send.

required
attempt int

Message attempt number for retry logic (default: 1).

1
message_type str

Routing type - "direct" or other supported types.

'direct'
out_path Optional[list]

Optional list of intermediate nodes for routing.

None

Returns:

Type Description
dict

Dictionary containing transmission results including success status,

dict

signal strength metrics (SNR/RSSI), and routing information.

Raises:

Type Description
RuntimeError

If the specified contact is not found.

Example
result = await node.send_text("alice", "Hello, world!")
print(result["success"])  # True if message sent successfully

send_trace_packet async

send_trace_packet(contact_name, tag, auth_code, flags=0, path=None, timeout=5.0)

Send a diagnostic trace packet for network analysis.

Transmits a trace packet to analyse routing paths and network performance. Always expects a response with trace data, signal metrics, and routing information.

Parameters:

Name Type Description Default
contact_name str

Name of the target contact for tracing.

required
tag int

Unique identifier for this trace operation.

required
auth_code int

Authentication code for the trace request.

required
flags int

Optional flags to modify trace behaviour.

0
path Optional[list]

Optional custom routing path for the trace.

None
timeout float

Maximum time to wait for trace response.

5.0

Returns:

Type Description
dict

Dictionary with trace results, routing data, and signal metrics.

Raises:

Type Description
RuntimeError

If contact not found or trace handler unavailable.

Example
trace = await node.send_trace_packet("target_node", 0x12345678, 0xABCD)
if trace["success"]:
    print(f"RTT: {trace['rtt_ms']}ms")

set_event_service

set_event_service(event_service)

Set the event service for broadcasting mesh events.

start async

start()

Start the mesh node and begin processing radio communications.

Initialises the radio interface and dispatcher, then enters the main event loop for handling incoming/outgoing messages. This method blocks until the node is stopped.

Note

This is an asynchronous operation that runs indefinitely until cancelled or the node is stopped.

stop

stop()

Stop the mesh node and clean up associated services.

Terminates radio communications and shuts down all active handlers. This method is synchronous and should be called to gracefully shut down the node.

Event System

pymc_core.node.events

Mesh event system - event service and event definitions

EventService

EventService(logger=None)

Generic event broadcasting service for the mesh library. Allows multiple subscribers to listen for different types of events.

publish async

publish(event_type, data)

Publish an event to all subscribers.

publish_sync

publish_sync(event_type, data)

Publish an event synchronously (creates async task).

subscribe

subscribe(event_type, subscriber)

Subscribe to a specific event type.

subscribe_all

subscribe_all(subscriber)

Subscribe to all events (global subscriber).

unsubscribe

unsubscribe(event_type, subscriber)

Unsubscribe from a specific event type.

unsubscribe_all

unsubscribe_all(subscriber)

Unsubscribe from all events.

EventSubscriber

Bases: ABC

Abstract base class for event subscribers.

handle_event abstractmethod async

handle_event(event_type, data)

Handle an event with the given type and data.

MeshEvents

Standard mesh event types for the mesh library.

Packet Handlers

Base Handler

pymc_core.node.handlers.base

BaseHandler

Bases: ABC

payload_type abstractmethod staticmethod

payload_type()

Return the payload type this handler processes

ACK Handler

pymc_core.node.handlers.ack

AckHandler

AckHandler(log_fn, dispatcher=None)

Bases: BaseHandler

ACK handler that processes all ACK variants: 1. Discrete ACK packets (payload type 1) 2. Bundled ACKs in PATH packets 3. Encrypted ACK responses (20-byte PATH packets)

__call__ async

__call__(packet)

Handle discrete ACK packets (payload type 1).

process_discrete_ack async

process_discrete_ack(packet)

Process a discrete ACK packet and return the CRC if valid.

process_path_ack_variants async

process_path_ack_variants(packet)

Process PATH packets that may contain ACKs in different forms. Returns CRC if ACK found, None otherwise.

set_ack_received_callback

set_ack_received_callback(callback)

Set callback to notify dispatcher when ACK is received.

set_dispatcher

set_dispatcher(dispatcher)

Set dispatcher reference for contact lookup and waiting ACKs.

pymc_core.node.handlers.advert

Text Handler

pymc_core.node.handlers.text

TextMessageHandler

TextMessageHandler(local_identity, contacts, log_fn, send_packet_fn, event_service=None)

Bases: BaseHandler

set_command_response_callback

set_command_response_callback(callback)

Set callback function for command responses.

Group Text Handler

pymc_core.node.handlers.group_text

GroupTextHandler

GroupTextHandler(local_identity, contacts, log_fn, send_packet_fn, channel_db=None, event_service=None, our_node_name=None)

Bases: BaseHandler

__call__ async

__call__(packet)

Handle incoming group text messages according to the specification.

Login Response Handler

pymc_core.node.handlers.login_response

AnonReqResponseHandler

AnonReqResponseHandler(local_identity, contacts, log_fn)

Bases: BaseHandler

Handler for ANON_REQ packets that might be login responses.

__call__ async

__call__(packet)

Check if this ANON_REQ is actually a login response.

LoginResponseHandler

LoginResponseHandler(local_identity, contacts, log_fn, login_callback=None)

Bases: BaseHandler

Handles PAYLOAD_TYPE_RESPONSE packets for login authentication responses.

Expected response format from C++ server: - timestamp (4 bytes): Server response timestamp - response_code (1 byte): RESP_SERVER_LOGIN_OK (0x80) for success - keep_alive_interval (1 byte): Recommended keep-alive interval (secs / 16) - is_admin (1 byte): 1 if admin, 0 if guest - reserved (1 byte): Reserved for future use - random_blob (4 bytes): Random data for packet uniqueness

__call__ async

__call__(packet)

Handle RESPONSE or ANON_REQ packets for login authentication.

clear_login_password

clear_login_password(dest_hash)

Clear stored password for destination hash.

set_login_callback

set_login_callback(callback)

Set callback to notify when login response is received.

Parameters:

Name Type Description Default
callback Callable[[bool, dict], None]

Function that accepts (success: bool, response_data: dict)

required

set_protocol_response_handler

set_protocol_response_handler(protocol_response_handler)

Set protocol response handler for forwarding telemetry responses.

store_login_password

store_login_password(dest_hash, password)

Store password for response decryption by destination hash.

Path Handler

pymc_core.node.handlers.path

Path packet handler for mesh network routing.

PathHandler

PathHandler(log_fn, ack_handler=None, protocol_response_handler=None)

Handler for PATH packets (payload type 0x08) - "Returned path" packets.

According to the official documentation, PATH packets are used for returning responses through the mesh network along discovered routing paths.

Official Packet Structure: - Header [1B]: Route type (0-1) + Payload type (2-5) + Version (6-7) - Path Length [1B]: Length of the path field - Path [up to 64B]: Routing path data (if applicable) - Payload [up to 184B]: The actual data being transmitted

For PATH packets, the payload typically contains: - [1B] dest_hash: Destination node hash - [1B] src_hash: Source node hash - [2B] MAC: Message Authentication Code (for payload version 0x00) - [NB] encrypted_data: Contains ACK or other response data

__call__ async

__call__(pkt)

Handle incoming PATH packet according to official specification.

set_ack_handler

set_ack_handler(ack_handler)

Set the ACK handler for processing bundled/encrypted ACKs in PATH packets.

Protocol Response Handler

pymc_core.node.handlers.protocol_response

Protocol response handler for mesh network protocol requests.

Handles responses to protocol requests (like stats, config, etc.) that come back as PATH packets with encrypted payloads.

ProtocolResponseHandler

ProtocolResponseHandler(log_fn, local_identity, contact_book)

Handler for protocol responses that come back as encrypted PATH packets.

This handler specifically deals with responses to protocol requests like: - Protocol 0x01: Get repeater stats - Protocol 0x02: Get configuration - etc.

__call__ async

__call__(pkt)

Handle incoming PATH packet that might be a protocol response.

clear_response_callback

clear_response_callback(contact_hash)

Clear callback for protocol responses from a specific contact.

set_response_callback

set_response_callback(contact_hash, callback)

Set callback for protocol responses from a specific contact.

Trace Handler

pymc_core.node.handlers.trace

Trace packet handler for mesh network diagnostics.

Handles trace packets that contain SNR and routing information for network diagnostics and analysis.

TraceHandler

TraceHandler(log_fn, protocol_response_handler=None)

Handler for trace packets (payload type 0x09).

Trace packets are used for network diagnostics and routing analysis. They contain tag, auth_code, flags, and trace path information with SNR data.

__call__ async

__call__(pkt)

Handle incoming trace packet.

clear_response_callback

clear_response_callback(contact_hash)

Clear callback for trace responses from a specific contact.

set_response_callback

set_response_callback(contact_hash, callback)

Set callback for trace responses from a specific contact.

def __init__(self):
    """Initialize the packet dispatcher."""

def register_handler(
    self,
    packet_type: PacketType,
    handler: Callable[[Packet], Awaitable[None]]
) -> None:
    """Register a handler for a specific packet type."""

def unregister_handler(self, packet_type: PacketType) -> None:
    """Remove handler for a packet type."""

async def dispatch_packet(self, packet: Packet) -> None:
    """Dispatch packet to registered handler."""

def get_registered_types(self) -> List[PacketType]:
    """Get list of registered packet types."""

```

Event System

python class EventEmitter: """Simple event emission system for node events.""" def on(self, event: str, callback: Callable) -> None: """Register event callback.""" def off(self, event: str, callback: Callable) -> None: """Remove event callback.""" def emit(self, event: str, *args, **kwargs) -> None: """Emit an event to all registered callbacks."""

Node Events

The mesh node emits the following events:

  • packet_received: When a packet is received
  • packet_sent: When a packet is successfully sent
  • node_discovered: When a new node is discovered
  • node_lost: When a node becomes unreachable
  • network_error: When a network error occurs
# Example event handling
node = MeshNode(radio, identity)

@node.on('packet_received')
async def handle_packet(packet: Packet):
    print(f"Received packet from {packet.source.hex()[:8]}")

@node.on('node_discovered')
async def handle_discovery(node_id: bytes):
    print(f"Discovered node {node_id.hex()[:8]}")

Packet Handlers

ACK Handler

class AckHandler:
    """Handles acknowledgment packets."""

    def __init__(self, node: MeshNode):
        """Initialize ACK handler."""

    async def handle_ack(self, packet: Packet) -> None:
        """Process incoming ACK packet."""
class AdvertHandler:
    """Handles node advertisement packets."""

    def __init__(self, node: MeshNode):
        """Initialize advert handler."""

    async def handle_advert(self, packet: Packet) -> None:
        """Process incoming advertisement."""

    def get_known_nodes(self) -> List[bytes]:
        """Get list of known node IDs."""

Text Handler

class TextHandler:
    """Handles text message packets."""

    def __init__(self, node: MeshNode):
        """Initialize text handler."""

    async def handle_text(self, packet: Packet) -> None:
        """Process incoming text message."""

    async def send_text(
        self,
        destination: bytes,
        message: str
    ) -> None:
        """Send a text message to destination."""

Group Text Handler

class GroupTextHandler:
    """Handles group text message packets."""

    def __init__(self, node: MeshNode):
        """Initialize group text handler."""

    async def handle_group_text(self, packet: Packet) -> None:
        """Process incoming group message."""

    async def send_group_text(
        self,
        group_id: bytes,
        message: str
    ) -> None:
        """Send message to group."""

    def create_group(self, group_name: str) -> bytes:
        """Create a new message group."""

Node Configuration

@dataclass
class NodeConfig:
    """Configuration options for mesh nodes."""

    max_hops: int = 16
    packet_timeout: float = 30.0
    ack_timeout: float = 5.0
    retransmit_attempts: int = 3
    broadcast_interval: float = 60.0
    keep_alive_interval: float = 300.0

Node Statistics

@dataclass
class NodeStats:
    """Runtime statistics for a mesh node."""

    packets_sent: int = 0
    packets_received: int = 0
    packets_forwarded: int = 0
    acks_sent: int = 0
    acks_received: int = 0
    retransmits: int = 0
    known_nodes: int = 0
    uptime: float = 0.0

    def reset(self) -> None:
        """Reset all statistics to zero."""

Error Handling

class NodeError(Exception):
    """Base exception for node-related errors."""
    pass

class NetworkTimeoutError(NodeError):
    """Raised when network operations timeout."""
    pass

class InvalidPacketError(NodeError):
    """Raised when an invalid packet is received."""
    pass

class RadioError(NodeError):
    """Raised when radio hardware errors occur."""
    pass