This PR modernizes the pyMC_repeaters linting/formatting and tightens a few security-related behaviors while reformatting a large portion of the codebase and tests to match the new tooling.
Changes:
Replaced Black/isort/flake8 pre-commit setup with Ruff (+ Bandit) and added a pytest pre-commit gate plus a GitHub Actions “PR Checks” workflow.
Applied wide formatting and cleanup changes across tests and runtime code (imports, line wrapping, logging, minor refactors).
Introduced/adjusted several security-leaning behaviors (e.g., safer subprocess annotations, URL validation, replacing random usage with secrets in a few places).
- Updated byte representations in tests to use lowercase hex format for consistency.
- Reformatted code for better readability, including line breaks and indentation adjustments.
- Consolidated multiple lines into single lines where appropriate to enhance clarity.
- Ensured that all test cases maintain consistent formatting and style across the test suite.
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>
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>
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>
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>
- 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.
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>
- 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
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>
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>
- 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>