mirror of
https://github.com/pyMC-dev/pyMC_Repeater.git
synced 2026-06-12 01:04:48 +02:00
dfacfeade8
Introduces a 'set format and forget' workflow for MQTT brokers. Users
reference a bundled preset by name inside the existing brokers: list,
and the package supplies the endpoints, audiences, and TLS settings.
Endpoint changes ship via 'pip install -U' instead of manual edits.
What changes
- New repeater/presets/ package with a tiny lazy YAML loader and two
bundled presets: waev (mqtt-{a,b}.waev.app) and letsmesh (EU + US).
- New format-family constant MC2MQTT_FORMATS = ('meshcoretomqtt',
'letsmesh', 'waev') replaces the inline tuple in topic resolution.
The legacy 'mqtt' format keeps its custom-topic semantics unchanged.
- Two-pass broker assembly in mqtt_handler.py: pass 1 expands every
{preset: <name>} entry inline; pass 2 collapses duplicates by name
with later-wins semantics. Place override entries AFTER preset
entries.
- Hard-coded LETSMESH_BROKERS constant deleted; its data now lives in
repeater/presets/letsmesh.yaml.
- convert_letsmesh_to_broker_config() collapsed from ~70 to ~25 lines
by emitting {preset: letsmesh} plus disable overrides for unwanted
brokers. Honors broker_index in (-1, 0, 1), additional_brokers, and
enabled flag exactly as before.
- update_mqtt_config API endpoint accepts {preset: <name>} entries and
passes them through unchanged so the web UI can author them when the
frontend is updated.
- config.yaml.example documents the preset entry shape, the override
rule, and the format family hierarchy.
- pyproject.toml ships presets/*.yaml as package data.
How to use
mqtt_brokers:
iata_code: "LAX"
brokers:
- preset: waev
# Override a single preset broker:
brokers:
- preset: waev
- name: waev-b
enabled: false
Tests
- tests/test_presets.py: 9 tests covering loader, expand/merge,
MC2MQTT topic-family parity, and parametrized legacy migration.
Co-Authored-By: Oz <oz-agent@warp.dev>
61 lines
1.9 KiB
Python
61 lines
1.9 KiB
Python
"""Bundled MQTT broker presets.
|
|
|
|
Each sibling ``*.yaml`` file in this package defines a named preset - a
|
|
ready-to-use list of broker dicts for a known MeshCoreToMQTT (MC2MQTT)
|
|
network. Presets ship with the package, so a ``pip install -U`` is enough
|
|
to pick up new endpoints without editing user config.
|
|
|
|
Public API:
|
|
get_preset(name) -> dict (the parsed YAML, or {} if unknown)
|
|
list_presets() -> sorted list of preset names
|
|
|
|
The loader is lazy: nothing is read or parsed at import time. The first
|
|
call discovers sibling YAML files via ``importlib.resources`` and caches
|
|
the parsed dicts for the lifetime of the process.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import logging
|
|
from importlib.resources import files
|
|
from pathlib import Path
|
|
from typing import Dict, List
|
|
|
|
import yaml
|
|
|
|
logger = logging.getLogger("Presets")
|
|
|
|
# Cache of parsed presets, keyed by name (e.g. "waev"). Populated on first
|
|
# call to _load_all(); never cleared.
|
|
_CACHE: Dict[str, dict] = {}
|
|
_LOADED: bool = False
|
|
|
|
|
|
def _load_all() -> Dict[str, dict]:
|
|
"""Discover and parse every bundled ``*.yaml`` file once."""
|
|
global _LOADED
|
|
if _LOADED:
|
|
return _CACHE
|
|
for resource in files(__package__).iterdir():
|
|
# importlib.resources.Traversable: only consider real files ending in .yaml
|
|
name = getattr(resource, "name", "")
|
|
if not name.endswith(".yaml"):
|
|
continue
|
|
try:
|
|
with resource.open("r", encoding="utf-8") as f:
|
|
_CACHE[Path(name).stem] = yaml.safe_load(f) or {}
|
|
except Exception as e: # pragma: no cover - defensive
|
|
logger.warning(f"Failed to load preset '{name}': {e}")
|
|
_LOADED = True
|
|
return _CACHE
|
|
|
|
|
|
def get_preset(name: str) -> dict:
|
|
"""Return the parsed preset dict, or ``{}`` if no such preset exists."""
|
|
return _load_all().get(name, {})
|
|
|
|
|
|
def list_presets() -> List[str]:
|
|
"""Return the sorted list of bundled preset names."""
|
|
return sorted(_load_all().keys())
|