feat: Add MESHCORE_DEVICE_NAME config to set node name on startup

- Add meshcore_device_name field to InterfaceSettings
- Implement set_name() method in device interface (real and mock)
- Update receiver to set device name during initialization if configured
- Add --device-name CLI option with MESHCORE_DEVICE_NAME env var support
- Device name is set after time sync and before advertisement broadcast

Fixes #37

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: JingleManSweep <jinglemansweep@users.noreply.github.com>
This commit is contained in:
claude[bot]
2025-12-06 19:00:56 +00:00
parent b8c8284643
commit deb307c6ae
6 changed files with 101 additions and 6 deletions

View File

@@ -70,6 +70,11 @@ class InterfaceSettings(CommonSettings):
# Mock device
mock_device: bool = Field(default=False, description="Use mock device for testing")
# Device name
meshcore_device_name: Optional[str] = Field(
default=None, description="Device/node name (optional)"
)
class CollectorSettings(CommonSettings):
"""Settings for the Collector component."""

View File

@@ -51,6 +51,13 @@ def interface() -> None:
envvar="NODE_ADDRESS",
help="Override for device public key/address (hex string)",
)
@click.option(
"--device-name",
type=str,
default=None,
envvar="MESHCORE_DEVICE_NAME",
help="Device/node name (optional)",
)
@click.option(
"--mqtt-host",
type=str,
@@ -99,6 +106,7 @@ def run(
baud: int,
mock: bool,
node_address: str | None,
device_name: str | None,
mqtt_host: str,
mqtt_port: int,
mqtt_username: str | None,
@@ -139,6 +147,7 @@ def run(
baud=baud,
mock=mock,
node_address=node_address,
device_name=device_name,
mqtt_host=mqtt_host,
mqtt_port=mqtt_port,
mqtt_username=mqtt_username,
@@ -153,6 +162,7 @@ def run(
baud=baud,
mock=mock,
node_address=node_address,
device_name=device_name,
mqtt_host=mqtt_host,
mqtt_port=mqtt_port,
mqtt_username=mqtt_username,
@@ -193,6 +203,13 @@ def run(
envvar="NODE_ADDRESS",
help="Override for device public key/address (hex string)",
)
@click.option(
"--device-name",
type=str,
default=None,
envvar="MESHCORE_DEVICE_NAME",
help="Device/node name (optional)",
)
@click.option(
"--mqtt-host",
type=str,
@@ -233,6 +250,7 @@ def receiver(
baud: int,
mock: bool,
node_address: str | None,
device_name: str | None,
mqtt_host: str,
mqtt_port: int,
mqtt_username: str | None,
@@ -294,6 +312,13 @@ def receiver(
envvar="NODE_ADDRESS",
help="Override for device public key/address (hex string)",
)
@click.option(
"--device-name",
type=str,
default=None,
envvar="MESHCORE_DEVICE_NAME",
help="Device/node name (optional)",
)
@click.option(
"--mqtt-host",
type=str,
@@ -334,6 +359,7 @@ def sender(
baud: int,
mock: bool,
node_address: str | None,
device_name: str | None,
mqtt_host: str,
mqtt_port: int,
mqtt_username: str | None,

View File

@@ -164,6 +164,18 @@ class BaseMeshCoreDevice(ABC):
"""
pass
@abstractmethod
def set_name(self, name: str) -> bool:
"""Set the device's node name.
Args:
name: Node name to set
Returns:
True if name was set successfully
"""
pass
@abstractmethod
def start_message_fetching(self) -> bool:
"""Start automatic message fetching.
@@ -518,6 +530,24 @@ class MeshCoreDevice(BaseMeshCoreDevice):
logger.error(f"Failed to set device time: {e}")
return False
def set_name(self, name: str) -> bool:
"""Set the device's node name."""
if not self._connected or not self._mc:
logger.error("Cannot set name: not connected")
return False
try:
async def _set_name() -> None:
await self._mc.commands.set_name(name)
self._loop.run_until_complete(_set_name())
logger.info(f"Set device name to '{name}'")
return True
except Exception as e:
logger.error(f"Failed to set device name: {e}")
return False
def start_message_fetching(self) -> bool:
"""Start automatic message fetching."""
if not self._connected or not self._mc:

View File

@@ -271,6 +271,17 @@ class MockMeshCoreDevice(BaseMeshCoreDevice):
logger.info(f"Mock: Set device time to {timestamp}")
return True
def set_name(self, name: str) -> bool:
"""Set the mock device's node name."""
if not self._connected:
logger.error("Cannot set name: not connected")
return False
logger.info(f"Mock: Set device name to '{name}'")
# Update the mock config name
self.mock_config.name = name
return True
def start_message_fetching(self) -> bool:
"""Start automatic message fetching (mock)."""
if not self._connected:

View File

@@ -33,15 +33,18 @@ class Receiver:
self,
device: BaseMeshCoreDevice,
mqtt_client: MQTTClient,
device_name: Optional[str] = None,
):
"""Initialize receiver.
Args:
device: MeshCore device instance
mqtt_client: MQTT client instance
device_name: Optional device/node name to set on startup
"""
self.device = device
self.mqtt = mqtt_client
self.device_name = device_name
self._running = False
self._shutdown_event = threading.Event()
self._device_connected = False
@@ -71,11 +74,14 @@ class Receiver:
"device_public_key": self.device.public_key,
}
def _initialize_device(self) -> None:
def _initialize_device(self, device_name: Optional[str] = None) -> None:
"""Initialize device after connection.
Sets the hardware clock, sends a local advertisement, starts message fetching,
and syncs the contact database.
Sets the hardware clock, optionally sets device name, sends a local advertisement,
starts message fetching, and syncs the contact database.
Args:
device_name: Optional device/node name to set
"""
# Set device time to current Unix timestamp
current_time = int(time.time())
@@ -84,6 +90,13 @@ class Receiver:
else:
logger.warning("Failed to synchronize device clock")
# Set device name if provided
if device_name:
if self.device.set_name(device_name):
logger.info(f"Set device name to '{device_name}'")
else:
logger.warning(f"Failed to set device name to '{device_name}'")
# Send a flood advertisement to broadcast device name
if self.device.send_advertisement(flood=True):
logger.info("Sent flood advertisement")
@@ -211,8 +224,8 @@ class Receiver:
self._device_connected = True
# Initialize device: set time and send local advertisement
self._initialize_device()
# Initialize device: set time, optionally set name, and send local advertisement
self._initialize_device(device_name=self.device_name)
self._running = True
@@ -271,6 +284,7 @@ def create_receiver(
baud: int = 115200,
mock: bool = False,
node_address: Optional[str] = None,
device_name: Optional[str] = None,
mqtt_host: str = "localhost",
mqtt_port: int = 1883,
mqtt_username: Optional[str] = None,
@@ -284,6 +298,7 @@ def create_receiver(
baud: Baud rate
mock: Use mock device
node_address: Optional override for device public key/address
device_name: Optional device/node name to set on startup
mqtt_host: MQTT broker host
mqtt_port: MQTT broker port
mqtt_username: MQTT username
@@ -312,7 +327,7 @@ def create_receiver(
)
mqtt_client = MQTTClient(mqtt_config)
return Receiver(device, mqtt_client)
return Receiver(device, mqtt_client, device_name=device_name)
def run_receiver(
@@ -320,6 +335,7 @@ def run_receiver(
baud: int = 115200,
mock: bool = False,
node_address: Optional[str] = None,
device_name: Optional[str] = None,
mqtt_host: str = "localhost",
mqtt_port: int = 1883,
mqtt_username: Optional[str] = None,
@@ -335,6 +351,7 @@ def run_receiver(
baud: Baud rate
mock: Use mock device
node_address: Optional override for device public key/address
device_name: Optional device/node name to set on startup
mqtt_host: MQTT broker host
mqtt_port: MQTT broker port
mqtt_username: MQTT username
@@ -346,6 +363,7 @@ def run_receiver(
baud=baud,
mock=mock,
node_address=node_address,
device_name=device_name,
mqtt_host=mqtt_host,
mqtt_port=mqtt_port,
mqtt_username=mqtt_username,

View File

@@ -287,6 +287,7 @@ def create_sender(
baud: int = 115200,
mock: bool = False,
node_address: Optional[str] = None,
device_name: Optional[str] = None,
mqtt_host: str = "localhost",
mqtt_port: int = 1883,
mqtt_username: Optional[str] = None,
@@ -300,6 +301,7 @@ def create_sender(
baud: Baud rate
mock: Use mock device
node_address: Optional override for device public key/address
device_name: Optional device/node name (not used in SENDER mode)
mqtt_host: MQTT broker host
mqtt_port: MQTT broker port
mqtt_username: MQTT username
@@ -336,6 +338,7 @@ def run_sender(
baud: int = 115200,
mock: bool = False,
node_address: Optional[str] = None,
device_name: Optional[str] = None,
mqtt_host: str = "localhost",
mqtt_port: int = 1883,
mqtt_username: Optional[str] = None,
@@ -351,6 +354,7 @@ def run_sender(
baud: Baud rate
mock: Use mock device
node_address: Optional override for device public key/address
device_name: Optional device/node name (not used in SENDER mode)
mqtt_host: MQTT broker host
mqtt_port: MQTT broker port
mqtt_username: MQTT username
@@ -362,6 +366,7 @@ def run_sender(
baud=baud,
mock=mock,
node_address=node_address,
device_name=device_name,
mqtt_host=mqtt_host,
mqtt_port=mqtt_port,
mqtt_username=mqtt_username,