Commit Graph

479 Commits

Author SHA1 Message Date
Zindello d597ab2ea8 fix: replace datetime.UTC attribute access in repeater_cli
Also extend the compat scanner to catch datetime.UTC used as an
attribute (datetime.datetime.now(datetime.UTC)) in addition to
direct imports, so this form cannot be reintroduced undetected.

Co-Authored-By: Zindello <josh@zindello.com.au>
2026-05-27 12:16:56 +10:00
Zindello a1c66100f5 fix: remove datetime.UTC from mqtt_handler and add 3.10 compat test
Replace the try/except UTC shim in mqtt_handler.py with timezone.utc
directly, consistent with the api_endpoints.py fix. Add a static AST
scan that fails if datetime.UTC is reintroduced anywhere in the codebase,
guarding against future regressions on Python 3.10 (LuckFox Pico Ultra).

Co-Authored-By: Zindello <josh@zindello.com.au>
2026-05-27 11:57:45 +10:00
Zindello 9fe0142fa5 fix: replace datetime.UTC with timezone.utc for Python 3.10 compat
datetime.UTC was introduced in Python 3.11. timezone.utc is available
since Python 3.2 and is functionally identical.

Co-Authored-By: Zindello <josh@zindello.com.au>
2026-05-27 11:03:15 +10:00
Zindello 7db6535a26 fix: Python 3.10 compat for datetime.UTC in api_endpoints
datetime.UTC was added in Python 3.11. Fall back to timezone.utc on
older interpreters, matching the existing pattern in mqtt_handler.py.

Co-Authored-By: Zindello <josh@zindello.com.au>
2026-05-27 09:58:10 +10:00
Lloyd 31edaa9c76 fix: update installation scripts to use the correct branch name 2026-05-26 15:42:59 +01:00
Lloyd 7b86716e06 Add unit tests for HTTP server, main daemon, service utilities, SQLite handler, and update endpoints 2026-05-26 14:59:31 +01:00
Lloyd 37ee0e892a Add more unit tests for handler helpers, identity manager, CLI, key generation, and main functionality
- Introduced tests for TraceHelper and DiscoveryHelper to validate packet forwarding and discovery request handling.
- Implemented tests for LoginHelper to ensure identity registration and login packet processing.
- Added tests for IdentityManager to cover identity registration, lookup, and filtering.
- Created tests for MeshCLI to verify command handling, configuration setting, and error paths.
2026-05-26 13:01:38 +01:00
Lloyd dc317b6568 Merge pull request #261 from CarlsonCustoms/feat/lafvin-ups-3s-sensor
feat: add LAFVIN UPS Module 3S sensor plugin (lafvin_ups_3s)
2026-05-26 08:57:37 +01:00
Rightup ab55748f3f fix: prevent advert echos in the packet table 2026-05-25 20:25:39 +01:00
Zack Carlson e20eaa7f02 feat: add LAFVIN UPS Module 3S sensor plugin (lafvin_ups_3s)
Adds a new sensor type for the LAFVIN UPS Module 3S, an INA219-based
uninterruptible power supply for Raspberry Pi. Reports pack voltage,
current, power, estimated state-of-charge, and charge state for a
3S Li-ion/LiPo battery configuration (9.0–12.6 V range).

The INA219 address defaults to 0x41 (as wired on the LAFVIN board)
and shunt resistance is configurable for accurate current readings.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-23 11:09:47 -07:00
Lloyd 85f282357c feat: expand allowed sections for configuration imports to include additional radio types 2026-05-22 15:19:06 +01:00
Lloyd a48b29837a feat: pre-restart config validation and site identification
- Add pre-restart config preflight to RestartModal with pass/warning/fail result panel and "Restart Anyway" confirmation for warnings
- Add Site Identification (site_name) config field shown as browser title and login page caption
- Add GET /api/validate_config and GET /api/site_info backend endpoints
- Sync document.title with site_name via system store watchEffect
- Fix authRegression.test.ts TS2367 type narrowing error
- add new radio hardware and radio settings tabs
2026-05-22 14:44:15 +01:00
Lloyd 78648f2af9 feat: add site_info endpoint to return site identification name without authentication 2026-05-22 14:35:49 +01:00
Lloyd 2a031b790f feat: add validate_config endpoint to check config.yaml syntax and required settings 2026-05-22 13:59:48 +01:00
Lloyd 3244f7b9bc feat: add validation for TX power settings and update API endpoint for serial ports 2026-05-22 12:47:02 +01:00
Lloyd 5c687076f1 feat: add endpoint to discover available serial/USB modem device paths 2026-05-22 10:21:07 +01:00
Lloyd 5b93d1023d fix: update loop detection thresholds and improve path hash handling in API endpoints 2026-05-21 12:29:21 +01:00
Lloyd d25e97af3c feat: implement setup status check and reject subsequent setups after completion 2026-05-21 11:32:08 +01:00
Zack Carlson 6e89272bce Add Waveshare UPS HAT (E) sensor plug-in
The HAT (E) uses a dedicated BMS MCU (not an INA219) at I2C address 0x2D.
It exposes charge state, pack voltage/current, per-cell voltages, remaining
capacity in mAh, and time-to-empty/full estimates directly via I2C registers.

Tested on Raspberry Pi 4B with pyMC_Repeater running pymc-battery-writer.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-19 09:25:38 -07:00
Lloyd 22adbd1a84 feat: add setup usb/tcp details on setup 2026-05-19 14:45:26 +01:00
Lloyd 6aab7ec676 fix:update-restart-functions 2026-05-19 11:23:46 +01:00
Zack Carlson f88d3c52be Revert "Add NMEA GPS sensor plug-in"
This reverts commit 8b0607aa1c.
2026-05-18 22:55:05 -07:00
Zack Carlson 8b0607aa1c Add NMEA GPS sensor plug-in
nmea_gps.py reads NMEA 0183 sentences directly from a serial GPS receiver
(/dev/serial0 by default) and exposes fix status, position, motion, accuracy,
and satellite fields as sensor readings.

Parses GGA, RMC, and GSA sentence types using stdlib only (no pynmea2
dependency) — pyserial is already required by the repeater.

Designed for use when the repeater's built-in GPS service is disabled
(gps.enabled: false). Both cannot share the serial port simultaneously.
With gps.api_fallback_to_config_location: true the repeater continues
advertising the manually-configured location while the sensor plugin handles
raw GPS data.

Returns: fix_valid, fix_quality, fix_type, latitude, longitude, altitude_m,
speed_kmh, course_degrees, hdop, pdop, vdop, satellites_used, utc_datetime.
Position and motion fields are null when fix_valid is false to avoid
reporting config-fallback coordinates as real GPS data.

Tested on Raspberry Pi 3B+ (DietPi) with a u-blox GPS module on /dev/serial0.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 22:50:17 -07:00
Zack Carlson 9c1661f097 Add SHTC3 and Waveshare UPS HAT (D) sensor plug-ins
- shtc3.py: SHTC3 temperature/humidity sensor (RAK1901 WisBlock module,
  I2C 0x70). Uses smbus2 i2c_rdwr for raw I2C reads since SHTC3 requires
  16-bit command words with no register-byte prefix. Returns temperature_c,
  temperature_f, humidity_pct.

- waveshare_ups_d.py: Waveshare UPS HAT (D) battery monitor via INA219 at
  I2C 0x43. Uses the HAT's actual shunt (0.01 Ω, CAL=26868) rather than the
  generic INA219 defaults. Returns bus_voltage_v, shunt_voltage_mv,
  current_ma, power_mw, battery_percent (piecewise-linear SoC for 21700
  cell), and charge_state (charging/discharging/idle). Sign convention
  matches Waveshare sample code: negative current = charging.

Both plug-ins tested on Raspberry Pi 3B+ (DietPi) with RAK1901 WisBlock
sensor and Waveshare UPS HAT (D).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 22:32:20 -07:00
Yellowcooln ab8ae30e7d web: clarify docker restart update messaging 2026-05-18 12:40:43 -04:00
Yellowcooln 7b6babd9f5 service: restart containers by exiting process 2026-05-18 12:12:18 -04:00
Lloyd 0e1031227e update ui for setup 2026-05-18 14:50:45 +01:00
Lloyd 56113c22ec feat: update radio status handling to show radio errors 2026-05-18 14:34:42 +01:00
Lloyd 052474cf48 feat: add connection type for KISS and pymc modems in radio settings 2026-05-18 14:15:46 +01:00
itk80 d7f2d2cc66 setup wizard: pymc_tcp / pymc_usb hardware tiles
Lets a fresh repeater install pick the pymc_usb (USB-CDC) or pymc_tcp
(Wi-Fi/Ethernet) external modem from the first-run /setup wizard
instead of requiring the user to hand-edit config.yaml after install.

radio-settings.json gets two new hardware entries; setup_wizard()
in api_endpoints.py handles them in dedicated branches that mirror
the existing KISS pattern (placeholders if the SPA doesn't yet send
modem-specific inputs, request body overrides if it does).

For pymc_tcp the wizard writes a sentinel host placeholder
('REPLACE_WITH_MODEM_HOST') so the YAML stays valid; on startup
get_radio_for_board() then errors with a clear pointer at
pymc_tcp.host (existing behavior from the PR #240 branch). pymc_usb
defaults to /dev/ttyACM0 at 921600 baud — matches the USB-CDC
device path documented in pymc_usb's README + pymc_driver.

Five new tests in tests/test_setup_wizard_pymc.py verify both
default and overridden code paths plus a KISS regression guard.
2026-05-18 13:00:00 +01:00
Lloyd 13b8004ad5 wip: null-radio defaults and needs_setup updates 2026-05-18 13:00:00 +01:00
Lloyd ce1acabd34 fix: parse sync_word as integer in get_radio_for_board function 2026-05-15 14:04:18 +01:00
dmduran12 7a0aec7b60 feat(presets): expose bundled broker presets via GET /api/broker_presets
Adds a new read-only endpoint that serves the bundled `repeater/presets/*.yaml`
catalogue so the admin UI can render a network picker without bundling its own
copy of the broker dicts. The UI side of this is paired with
pyMC-dev/pyMC-RepeaterUI#TBD which retires src/assets/broker-templates.json
in favour of authClient.get('/api/broker_presets').

Why
The UI previously shipped a separate JSON snapshot of every supported MC2MQTT
network. The JSON and these YAML files drifted: the Waev entry on the UI side
pointed at mqtt-a.waev.app with audience mqtt.waev.app (single primary, no
failover) while the YAML side here listed two brokers (A + B). The result was
that operators picking 'Waev' from the dropdown silently lost the redundancy
this preset is meant to provide.

What changes

repeater/presets/*.yaml
- Add optional top-level `display_name` and `website` fields. The loader
  treats them as advisory metadata for the UI; the runtime connection code
  never reads them. `display_name` falls back to the titlecased filename
  stem if absent so existing third-party presets keep rendering.

repeater/presets/waev.yaml
- Collapse from two broker entries (waev-a, waev-b) to a single broker on
  `mqtt.waev.app`. The Waev edge Worker (see waev/src/router.ts:
  MQTT_PRIMARY_FAILOVER_TIMEOUT_MS) already does server-side A/B failover on
  the alias host with a 1500 ms timeout. Two independent client connections
  would defeat the dedup-on-pubkey-hash contract on the waev ingest side.
  Operators who want to pin to a specific container can edit host/audience
  after import.

repeater/presets/meshmapper.yaml (new)
- Port of the historical MeshMapper entry from the UI's deprecated JSON.
  Single broker on mqtt.meshmapper.cc, format: letsmesh (matches the
  published wire contract; bump to a dedicated value if/when wire-level
  differentiation lands).

repeater/web/api_endpoints.py
- New `broker_presets` CherryPy handler at `GET /api/broker_presets`.
  Unauthenticated to match the existing `mqtt_status` precedent — the
  response carries only public hostnames + TLS hints, no PII. Imports the
  presets module lazily so a broken YAML never blocks process startup.
  Response shape:
    {
      success: true,
      data: [{ id, name, website?, brokers: [ ... raw YAML dicts ... ] }, …]
    }

tests/test_presets.py
- Locks the new metadata fields (display_name, website) on all three presets.
- Locks the Waev single-alias-broker design with an explicit comment tying
  the test to the waev Worker failover code.
- Adds MeshMapper coverage parallel to the other public-network presets.
- Adds a stub-instance test that drives the new `broker_presets` method on
  an APIEndpoints stand-in (bypassing the heavyweight `__init__`) and
  asserts the UI-ready response shape.

Verification
- New endpoint serves the expected three presets (letsmesh: 2 brokers,
  meshmapper: 1, waev: 1) when exercised end-to-end against a local mock
  that imports the real preset loader.
- Existing legacy-config migration tests (broker_index 0/1/-1 → preset +
  overrides) still pass — the override pipeline is untouched.

Co-Authored-By: Oz <oz-agent@warp.dev>
2026-05-14 15:14:10 -07:00
Lloyd 2510bed9a4 feat: ui speed enhance 2026-05-14 13:54:59 +01:00
itk80 541b25b47c config: add pymc_tcp / pymc_usb radio_type branches
Wires the TCPLoRaRadio and USBLoRaRadio drivers that landed in pyMC_core
on 2026-05-13 (PR pyMC-dev/pyMC_core#68) into get_radio_for_board() so
they can be selected from a repeater config file without any code change
in main.py / api_endpoints.

Both branches follow the existing pattern: read host/port (TCP) or
serial port (USB) plus auth/LBT options from their own config section,
share the LoRa parameters from the common `radio` section, fall back to
the firmware-default sync word 0x12, and surface ImportError as a clear
RuntimeError if the installed pymc_core is too old to ship the drivers.

config.yaml.example documents both sections and updates the radio_type
header comment with the full supported list. Five new tests in
tests/test_radio_config.py monkeypatch the radio classes and verify the
section/parameter wiring + missing-required-field errors.

No web UI / endpoint changes — the deployment this targets edits the
config file directly. A GUI wizard for these radio types can land
separately if there's appetite.
2026-05-13 17:30:09 +02:00
Lloyd 0e7bb05208 Refactor INA219 sensor integration. 2026-05-13 12:34:05 +01:00
Joshua Mesilane a01d59381b fix(ens210): check correct VALID bit in T_VAL/H_VAL polling
Bit 0 of byte 2 is the T_VALID/H_VALID flag (datasheet Figure 32/33,
page 23 example: t_valid = (t_val>>16) & 0x1). The previous code checked
bit 1 (CRC LSB), which caused sporadic timeouts when the CRC happened to
have a 0 in that position.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 17:07:28 +10:00
Joshua Mesilane 7865e9cb4b fix: standardise sensor module structure and docs
- Use multi-line ensure_python_modules list format in ens210.py,
  matching the established pattern from ina219.py
- Fix auto_install_packages indentation in ina219.py docstring
- Remove smbus2 from pyproject.toml core dependencies; sensor packages
  are handled at runtime via ensure_python_modules/auto_install_packages
- Update docs/adding_sensors.md template and guidance to match

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 16:33:37 +10:00
Joshua Mesilane 9bfe1259da feat: add ENS210 temperature/humidity sensor plug-in
Adds support for the ENS210 relative humidity and temperature sensor
as a new plug-in under repeater/sensors/ens210.py. Also adds a
commented configuration example to config.yaml.example and a
contributor guide at docs/adding_sensors.md explaining how to add
further sensor plug-ins.

## Implementation notes

### Why smbus2 instead of an Adafruit/CircuitPython library

The ENS210 has no maintained Adafruit CircuitPython driver. The
available third-party options are either unmaintained or bring in the
full Blinka/CircuitPython hardware-abstraction stack as a dependency.
smbus2 is a thin, widely-packaged wrapper around the Linux i2c-dev
kernel interface that is already present on Raspberry Pi OS and most
Debian-based systems. It has no transitive dependencies and adds no
abstraction cost.

The ENS210 protocol is simple enough that direct register access is
preferable: two writes to start a measurement (REG_SENS_RUN + REG_SENS_START)
and two three-byte block reads to retrieve temperature and humidity.
The status/validity bit is checked inline rather than relying on a
library to surface it. There is no value a higher-level driver would
add here.

### Read strategy

A fixed post-trigger delay is unreliable — the sensor datasheet quotes
~130 ms typical conversion time but the actual ready time varies. The
implementation instead polls the data-valid status bit (bit 1 of the
third byte in each register block) every 50 ms for up to
read_timeout_seconds (default 1.0 s), breaking as soon as both T and
H report valid data. This is the same approach used in the validated
reference script.

The I2C bus is opened and closed on every read rather than kept open
across poll cycles. Keeping a persistent SMBus handle caused subsequent
reads to time out, consistent with the Linux i2c-dev file descriptor
accumulating state between transactions.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 11:46:26 +10:00
Lloyd 66532a0647 feat: Add sensor plugin framework and Sensors 2026-05-12 14:18:33 +01:00
Lloyd eb5d971c72 fix: filter neighbors to include only zero-hop repeaters in response 2026-05-11 16:26:15 +01:00
Lloyd 43648da939 fix:add Pagination to contacts table 2026-05-11 14:27:28 +01:00
Lloyd f04f581163 fix:Neighbors get function 2026-05-11 13:59:12 +01:00
Lloyd 941c355deb feat: add pagination support and count retrieval for adverts by contact type 2026-05-11 13:54:55 +01:00
Lloyd f3946685c2 bug/further fixes to ui initla loading 2026-05-11 13:39:48 +01:00
Lloyd 4d710a0210 fix/ui-tidy-dev 2026-05-08 17:08:01 +01:00
Lloyd 68656fccdd Merge pull request #231 from pyMC-dev/pr-227
Pr 227
2026-05-06 16:13:30 +01:00
Lloyd d250828197 feat: enhance MQTT connection handling with JWT refresh and error logging improvements 2026-05-06 13:53:12 +01:00
Lloyd bd7a305d7b feat: improve MQTT connection handling with enhanced reconnect logic and logging 2026-05-06 10:05:11 +01:00
Lloyd 5b20f5580a feat: enhance MQTT logging and error handling with payload summaries and improved disconnect messages 2026-05-06 09:53:44 +01:00