Commit Graph
100 Commits
Author SHA1 Message Date
7b9d32b765 [BT] Update Theengs Decoder to v2.5.0 (#2409)
Co-authored-by: Florian <1technophile@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 08:21:23 -05:00
FlorianandClaude Opus 5 827c37542a [OTA] Report the version actually requested after a successful update (#2374)
On HTTP_UPDATE_OK the pre-reboot RLStoMQTT reported
`installed_version = latestVersion` unconditionally. That is only correct for
a "latest" request: "dev" installs from RELEASE_LINK_DEV, "vX.Y.Z" installs
that tag, and a caller-supplied URL installs an arbitrary image whose
`version` field is just a label (the WebUI sends "test"). On ESP8266
latestVersion is never populated, so every successful OTA there published an
empty installed_version.

Report the version that was actually requested when the URL was derived from
it, and omit the field for a caller-supplied URL rather than publish a label
as a version. The decision is keyed on systemUrl (non-empty only on the
derived branches) rather than on `version`, because `{"url": ...,
"version": "latest"}` takes the url branch and must not be reported as the
latest release. The value is stored as a String so ArduinoJson 6.18.5 copies
it into the document instead of referencing the caller's buffer.

Scope and evidence, stated plainly: this corrects the message when it lands,
and on current development it usually does not. ESPRestart(6) clears
jsonQueue before the drain task publishes, so the pre-reboot "Update success
!" message was dropped on 3 of 3 successful OTAs on a Theengs Bridge,
including one where this code was certified to be the firmware performing the
update. The change is therefore verified by inspection and by clean builds
(theengs-bridge-v11 on ESP32, nodemcuv2-rf on ESP8266, no new warnings), not
by observing the corrected field on hardware. The authoritative
installed_version is republished from OMG_VERSION after the reboot, which was
correct in every run. Making the pre-reboot message survive the restart, or
dropping it in favour of that republish, is a separate change.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-19 19:46:59 -05:00
FlorianandClaude Opus 5 050ba3e827 [SYS] Fix erase command leaving MQTT credentials in SPIFFS (#2372)
eraseConfig() passed SPIFFS.format() as an argument to THEENGS_LOG_TRACE,
which expands to ((void)0) below LOG_LEVEL_TRACE and discards its arguments
along with the call. On every build using the default LOG_LEVEL_NOTICE the
format therefore never ran, so {"cmd":"erase"} only performed
nvs_flash_erase(): the WiFi credentials were cleared but /config.json
survived and restored the MQTT server, port, user, password, broker and
client certificates, connection index, topic, gateway name, OTA password
and BLE AES keys on the next boot.

Call SPIFFS.format() on its own statement and report a failed format at
ERROR level, so the failure is visible at the default log level.

This was introduced in #2232, which converted the original runtime-checked
Log.trace() call to the compile-time macro. It affects the SYS erase
command, the WebUI erase button and the GPIO factory-reset hold alike,
since all three go through eraseConfig().

Reproduced on a Theengs Plug built at the default log level: after
{"cmd":"erase"} the NVS namespaces are gone ("SYS config not found",
"ONOFF config not found") and the saved APs are empty, yet the same boot
still logs "Config loaded from flash" and restores the OTA server cert.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-12 16:36:47 -05:00
3b8496e323 [BT] Update Theengs Decoder to v2.4.5 (#2371)
Pin the decoder library to the v2.4.5 release archive instead of the
moving development branch.

Co-authored-by: Florian <1technophile@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-12 12:56:54 -05:00
7945a42d06 [RF] Update rtl_433_ESP to v0.6.1 (#2366)
Co-authored-by: Florian <1technophile@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-07 17:53:36 -05:00
4e3ed19827 [NET] ESP32 Ethernet: PHY cold-cycle at boot, default on Theengs Bridge (#2364)
Power ETH_PHY_POWER down for 200 ms and back up 200 ms before ETH.begin()
so the PHY always gets a true cold start. A warm PHY left in a stale state
(reproduced on Theengs Bridge v1.1 with PSRAM enabled after an NVS erase)
can flap the link until a manual power cycle: it reports up for one poll,
then down for several seconds, forever, so DHCP never completes. The short
reset the Ethernet driver performs on its own does not clear that state; a
power cycle with a settle window before begin() does. On a hardware-in-the-
loop bench the same provocation went from flapping every trial to flapping
in none of them (4/4).

Enabled via -DETH_PHY_COLD_CYCLE, only active when ETH_PHY_POWER is a valid
pin, at the cost of ~400 ms added boot time. Set by default on both Theengs
Bridge environments.

Co-authored-by: Florian <1technophile@users.noreply.github.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-08-31 08:39:39 -05:00
FlorianandClaude Opus 5 77754a1fe9 [RF] Deselect a secondary CC1101 chip-select on shared-SPI boards (#2363)
Boards that carry two radios on one SPI bus need the unused radio's CS held
high, or it answers on MISO and corrupts every transfer to the radio that is
actually in use. Add an optional RF_MODULE_SECONDARY_CS: when defined, drive
that pin high before probing the CC1101.

Bench-proven on a dual-CC1101 ESP32 board where one radio is faulty and the
other is the working one: without the deselect the shared bus is unreliable;
with it, the working module probes cleanly on the first attempt and decodes
live 433 MHz sensors.

No effect on single-radio boards: the whole block compiles out unless
RF_MODULE_SECONDARY_CS is set.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-08-24 08:17:15 -05:00
Florian 98ae55cd7b [DOCS] Modify README for Theengs bridge and plug information (#2362)
Updated image path for Theengs bridge and removed Theengs plug details.
2026-08-24 07:26:27 -05:00
47da455246 [BLE] Fix esp32dev-ble-idf: bump esp-nimble-cpp 2.1.1 -> 2.5.0 (#2361)
esp-nimble-cpp 2.1.1 no longer exists: the PlatformIO registry pruned it
(2.3.2 is now the oldest available) and the upstream 2.1.1 git tag was
deleted as well. Dependency resolution therefore fails before any
compilation, breaking every build of esp32dev-ble-idf:

  UnknownPackageError: Could not find the package with
  'h2zero/esp-nimble-cpp @ 2.1.1' requirements

Pin 2.5.0, matching the NimBLE-Arduino 2.5.0 already used by the Arduino
ESP32 environments, and reference the GitHub archive rather than the
registry so future registry pruning cannot break the build again.

No source changes are needed: esp32dev-ble-idf builds clean with the
newer API, blufi.cpp included.

Co-authored-by: Florian <1technophile@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-08-23 17:25:34 -05:00
FlorianandClaude Opus 5 b6b23bd423 [RF] Move RTL_433 / SX127x envs to the shipped ESP32 platform (#2360)
Eight environments were still pinned to espressif32@6.1.0 (arduino-esp32
2.0.7 / IDF 4.4) while the rest of the tree had moved to the shared
${com.esp32_platform} definition. Align them so they build against the
platform that actually ships, rather than silently keeping an old
toolchain alive:

  esp32doitv1-aithinker-r01-sx1278
  esp32dev-rtl_433, esp32dev-rtl_433-fsk
  heltec-rtl_433, heltec-rtl_433-fsk
  lilygo-rtl_433, lilygo-rtl_433-fsk
  esp32dev-multi_receiver

The stray "* copy.c" duplicate in rtl_433_ESP that previously blocked
these envs on a modern toolchain is gone as of rtl_433_ESP v0.6.0, which
development already pulls in, so no build workaround is needed.

The move alone is not sufficient: the newer core leaves ~35 KB less heap
on BT-less ESP32 builds, which pushes the memory-constrained RTL_433
boards into a boot loop at the first TLS handshake. That is fixed here
too, in main.cpp.

On ESP32-classic builds without ZgatewayBT, arduino-esp32 3.x leaves most
of the Bluetooth controller's reserved DRAM (CONFIG_BTDM_RESERVE_DRAM =
0xdb5c, 56156 B) stranded. arduino-esp32 2.x released the whole BTDM
region unconditionally in initArduino():

    if (!btInUse()) esp_bt_controller_mem_release(ESP_BT_MODE_BTDM);

3.x releases CLASSIC and BLE separately, and the CLASSIC pass silently
short-circuits. cores/esp32/esp32-hal-bt.c has:

    #if defined(CONFIG_BT_CLASSIC_ENABLED)
    static bool _classicMemReleased = false;
    #else
    static bool _classicMemReleased = true;  // "No Classic BT on this chip"
    #endif

With a BLE-only controller build CONFIG_BT_CLASSIC_ENABLED is unset, so
_classicMemReleased starts true and btMemRelease(BT_MODE_CLASSIC_BT)
returns "already released" without reaching IDF. Only the ~5 KB BLE slice
is freed. The guard is wrong for this part: ESP32 does have Classic BT
hardware (CONFIG_SOC_BT_CLASSIC_SUPPORTED) and the DRAM is reserved
regardless of the controller's configured mode; the correct guard would be
CONFIG_SOC_BT_CLASSIC_SUPPORTED. Reported upstream separately.

Release the whole BTDM region explicitly. esp_bt_mem_release() is used
rather than esp_bt_controller_mem_release() because it is a superset that
also frees the linker _bt_data / _bt_controller_* sections. The guard is
compile-time and authoritative: OMG knows via ZgatewayBT whether it uses
BT, whereas the core infers it from link-time flags that NimBLE-Arduino
does not set. Safe no-op if the memory was already released or once the
core is fixed. Note this makes btStart() permanently unavailable in these
builds, which is already the case for a !ZgatewayBT build.

Measured on a Heltec WiFi LoRa32 V2 running heltec-rtl_433 (WiFi + MQTT +
TLS + rtl_433 + SSD1306, no BT), pioarduino 55.03.39 / esp32-arduino-libs
0.1.8:

  BT DRAM reclaimed: 209928 -> 245048        (+35120 B)
  Update check, free heap: 49868 -> 89484

Without it, the boot-time HTTPS update check fails its TLS handshake
("Dynamic Impl: alloc(4437 bytes) failed", then X509 -9984) because the
largest free block is too small, and the MinimumMemory watchdog then
reboots the board in a loop. With it: no allocation failure, handshake
succeeds, freemem ~85 KB / minmem 53432 against the 40000 threshold,
0 reboots over a 3 min soak with live RTL_433 decodes.

The memory fix affects every ESP32-classic env without ZgatewayBT, not
just the ones moved here. The RTL_433 envs fail loudly because they carry
the MinimumMemory watchdog; others (esp32dev-ir, esp32dev-pilight,
ttgo-lora32-v21, heltec-wifi-lora-32, ...) simply run ~35 KB short.
Builds with ZgatewayBT are unaffected.

Hardware validation, two boards:

  heltec-rtl_433 (OOK 433.92 MHz, Heltec WiFi LoRa32 V2)
    boots, joins WiFi/MQTT, RadioLib/SX127x RSSI calibration completes,
    HA discovery publishes, decodes live ambient sensors
    (Ambientweather-F007TH, Prologue-TH, WEC-2103).

  heltec-rtl_433-fsk (FSK 915 MHz, second Heltec)
    same boot path, decodes a live Fineoffset-WH51 soil sensor.

The other six envs have no matching board on hand and are compile-only.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-08-23 14:15:48 -05:00
FlorianandClaude Opus 5 d8ffa3f1dd [SYS] Migrate M5 boards to M5Unified + M5GFX (modern platform) (#2355)
The per-board libs (M5Stack/M5StickC/M5StickC-Plus/M5Tough) don't build on
Arduino core 3.x, which kept the five M5 BLE envs pinned to espressif32@6.1.0.
Port board support to M5Unified + M5GFX so they run ${com.esp32_platform} like
the rest of the tree.

- environments.ini: 5 M5 envs -> modern platform + m5unified. m5stick-c uses the
  pico32 variant (m5stick_c variant absent upstream) and no_coredump partitions
  (M5Unified overflows min_spiffs).
- boardM5.cpp / config_M5.h: single <M5Unified.h>; M5.Lcd aliases M5.Display;
  unified brightness/sleep/wake.
- main.cpp / mqttDiscovery.cpp: battery telemetry via M5.Power; AXP192-only
  Vin/VBus/temp fields dropped (no portable equivalent).

All five M5 envs build; M5Stack Core hardware-verified.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-15 16:45:35 -05:00
FlorianandClaude Opus 5 5fdaddadaf [RF] Fix CC1101 on Arduino core 3.x: bump SmartRC to V3.0.2 + init before probe (#2353)
On the platform we ship (pioarduino 55.03.39 / arduino-esp32 3.3.9) the pinned
SmartRC-CC1101-Driver-Lib v2.5.7 does not work with the CC1101 at all. Core 3.x's
peripheral manager owns SCK/MISO/MOSI once SPI.begin() has run, so the driver's
digitalWrite() calls on those pins become silent no-ops, and it starts and stops
the SPI bus around every single register access (LSatan/SmartRC-CC1101-Driver-Lib#176).

Bench result on an ESP32 DevKit + CC1101 running esp32dev-pilight-cc1101: setup
hangs in the driver's unbounded `while(digitalRead(MISO_PIN));` before any C1101
log line is emitted, and TG1WDT reboots the board in a loop. The same build on
espressif32@6.8.1 (core 2.0.17) initialises normally, so this is the core, not
the wiring. Every ZradioCC1101 ESP32 environment is affected: Pilight, RF, RF2
and Somfy. The RTL_433 CC1101 path goes through RadioLib and is unaffected.

V3.0.x re-engineered the SPI core for ESP32: gpio_get_level() with a timeout
instead of the unbounded digitalRead() wait, SPI.begin() once instead of per
transaction, and no digitalWrite() on peripheral-owned pins. It also moved
SPI.begin() out of getCC1101() and into Init(), so the connection probe has to
run after Init() rather than before it — reported in #2348.

Both changes are needed together: the reorder alone does not help on core 3.x,
and V3 cannot work without it.

Verified on the bench, core 3.3.9, esp32dev-pilight-cc1101:
  N: C1101 SPI connection OK on attempt 1
  N: C1101 tuned RX to 433.92 MHz
plus a live round trip — an arctech_switch frame published to MQTTtoPilight was
transmitted and decoded back by the board's own receiver on PilighttoMQTT.

Note for review: this bumps the pin for the ESP8266 CC1101 environments too.
They are not affected by the core 3.x breakage and V3 is untested there, so the
pin may be worth splitting per environment before this lands.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-15 14:10:00 -05:00
FlorianandClaude Opus 5 4462ac94bf [SYS] esp32-arduino-libs: bump 0.1.7 -> 0.1.8 (restores RISC-V -march) (#2352)
0.1.7 shipped esp32c3 flags with no -march at all, so every ESP32-C2/C3 build
fell back to the compiler default (rv32imafdc) and emitted atomic and
hardware-FP instructions the core does not implement. The firmware panicked
with "Illegal instruction" during C++ static init — on the first
std::shared_ptr refcount, inside the Arduino core's global SPIFFS constructor —
so the board boot-looped before printing a single line of OMG output. That is
issue #2336: esp32c3-dev-c2-ble dev builds boot-loop while 1.8.1 (built on
arduino-esp32 2.x, which passed -march=rv32imc explicitly) is fine.

The flag was lost in packaging, not in the compiler: ESP-IDF >= 5.5.2 moved the
toolchain base flags into @response files and the lib-builder's flag extraction
silently dropped them. The precompiled libs were always correct RV32IMC code.
0.1.8 restores -march across c_flags/cpp_flags/S_flags/ld_flags and
pioarduino-build.py, and fails the lib build if a target's ISA flag ever goes
missing again (theengs/esp32-arduino-lib-builder#9).

Xtensa targets were never affected — each core has its own GCC driver whose
default multilib already matches — but they are rebuilt from the same bundle,
so esp32/esp32s3 want a smoke test alongside the C3.

Bench-verified on an ESP32-C3 rev v0.4: boot-loops on 0.1.7 (30 panics in 20 s,
MTVAL 0x06f7202f), boots and decodes BLE on 0.1.8 with no project-level flags.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-15 14:03:47 -05:00
FlorianandClaude Opus 5 65d0998278 [OTA] Resolve the offered version per build environment (#2350)
checkForUpdates() reads an optional "envs" map from the update manifest and,
when it holds an entry for this build's ENV_NAME, offers that version instead
of the global latest_version. Boards with no entry are unaffected.

This lets the OTA server hold a specific product at an older release while the
rest of the fleet advances, with no per-board build flags. The resolved value
is republished as latest_version so the Home Assistant update entity compares
against it, and the map itself is dropped from the retained state topic to
keep it small.

Backward compatible both ways: old firmware ignores "envs", and a manifest
without "envs" behaves exactly as before.

OTA_JSON_URL is also guarded with #ifndef so a build environment can point the
update check at another OTA server. It was defined unconditionally, so a -D
build flag could not retarget it (the header definition always won). This is
needed to exercise the check against a test manifest, and is useful for
self-hosted OTA servers. No behaviour change without an override.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-08-10 06:33:13 -05:00
b9e8821b17 [DOCS] Fix responsive layout on phones and tablets (#2349)
The home hero image overlapped the hero title on phones: the default theme
pulls the stacked image up with negative margins (-76px -24px -48px), which
only works because it normally sits absolutely positioned inside a fixed
320px box. custom.css lets the image flow to enlarge it, so those margins
made it bleed off-screen and collide with the title. Reset them below 960px,
and hide the image entirely under 640px -- at phone size it is a wall of
unreadable logos that pushes the title, tagline and call-to-action buttons
below the fold.

Also fixes, found while sweeping every page at 360/400/900px:

- Nav bar: the shared Theengs nav has seven top-level entries and the theme
  swaps to the desktop nav bar at 768px, where it does not fit. From 768px to
  ~1180px, Shop, Sponsor, the appearance switch and the GitHub link were
  clipped or off-screen and the page scrolled sideways. Keep the hamburger +
  full-screen menu until the full menu genuinely fits.
- upload/binaries: indented (non-fenced) code blocks render as a bare <pre>,
  which the theme only makes scrollable inside a language-* wrapper, so the
  long esptool.py commands pushed the page sideways.
- use/lora: the YouTube embed is raw HTML with width="560", wider than a
  phone. Make video embeds fluid at a 16/9 ratio.
- OmgPipeline: stack below a 680px container instead of 480px -- in between,
  the "OpenMQTTGateway" label spilled out of its node and over the arrows.
  Drop the media query in index.md that meant to do this: it was scoped to
  index.md and could never match an element inside the child component.

No horizontal overflow remains on any of the 50 pages at 360px, 400px or
900px; desktop is unchanged.


Claude-Session: https://claude.ai/code/session_01Nj6xa5Mk5wi8XWtgJG7w5D

Co-authored-by: Florian <1technophile@users.noreply.github.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-08 21:22:35 -05:00
f5b33104ef [DOCS] Overhaul new-user onboarding, home page, and upload flow (#2347)
Getting started:
- New 15-minute Getting Started guide featuring the two most popular
  tracks (BLE on esp32dev-ble, RF on lilygo-rtl_433), with broker setup
  and a verification step
- Sidebar restructured around the new-user journey; upload options
  numbered by ease; Gitpod upload method removed

Home page:
- VitePress hero with clear actions, larger hero image
- How-it-works pipeline diagram shared as a component (responsive via
  container queries), use-case grid, bigger video tiles without the
  image lightbox, refreshed press coverage list, functions pills
- Support section pointing to Theengs Bridge and
  github.com/sponsors/1technophile

Upload flow:
- Decision-oriented upload landing page (journey strip, method cards,
  comparison table)
- Pre-flight checklist and driver troubleshooting on the web installer
- Guided environment picker (bridge + hardware wizard) replacing the
  filter chips, with manifest-verified recommendations
- Post-flash next-steps card linking to the portal and verification

Board images:
- custom_img key in environments.ini and board photos for all
  web-flashable environments (licenses recorded); private prod_env.ini
  environments excluded from boards-info.json

Corrections:
- RF frequency coverage stated as 433/315/868/915MHz
- Removed references to the retired compatible.openmqttgateway.com
  site (BLE list is decoder.theengs.io), the discontinued Theengs
  Plug, and the private sponsor forum


Claude-Session: https://claude.ai/code/session_01TYysQW8tpmt1dWWuzKhTLi

Co-authored-by: Florian <1technophile@users.noreply.github.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-08-03 10:23:43 -05:00
Florian 382aae8353 [DOCS] Fix image link in README.md (#2346) 2026-08-01 07:45:03 -05:00
a33010c068 [FIX] Restore GET route for /wi so the WiFi scan link works (#2345)
handleWI() dispatches internally on the "scan" query arg, so the same
handler serves both the scan link and the save form. #2037 narrowed the
route to POST-only, which orphaned the scan hyperlink (a GET) in
config_WebContent.h. Re-register /wi for HTTP_GET alongside HTTP_POST;
the credential Save form stays method=post, so no secrets move to GET.

Co-authored-by: Florian <1technophile@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-01 07:30:26 -05:00
FlorianandClaude Opus 4.8 77b588fb60 Keep WiFiManager portal available when saved WiFi credentials are lost (#2340)
setupWiFiManager() disables the config portal whenever /config.json exists
(a security measure to avoid exposing the config data + OTA page). But if the
saved WiFi credentials are lost while config.json remains -- e.g. the NVS
partition is erased ({"cmd":"erase"}, or corruption) -- the gateway can no
longer connect AND the portal is suppressed: autoConnect() fails immediately
and the failure path calls ESPRestart(3). The device then reboot-loops
forever, un-onboardable, its SoftAP flickering up ~1s per cycle.

Only suppress the portal when config.json exists AND saved STA credentials
are still present (WiFi.SSID() non-empty). With no credentials the portal
stays enabled, so the gateway raises a stable onboarding AP and can be
re-provisioned instead of reboot-looping. The original security intent is
preserved for provisioned gateways (which always have saved credentials).

Reproduced + verified on a Theengs Plug (esp32dev, min_spiffs): with NVS
erased and config.json present, the SoftAP is now stable and accepts
provisioning via the captive portal, rejoining the broker normally.


Claude-Session: https://claude.ai/code/session_01MbmZRv67fnNvzpyvqPi6vx

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-25 09:55:28 -05:00
FlorianandClaude Opus 4.8 b493dddb97 [SYS] esp32-arduino-libs: bump 0.1.6 -> 0.1.7 for no-PSRAM TLS (MBEDTLS_DYNAMIC_BUFFER) (#2335)
The 0.1.7 bundle enables MBEDTLS_DYNAMIC_BUFFER (dynamic TX/RX TLS record
buffers) by disabling the unused DTLS proto that was gating it. mbedTLS now
sizes the ~32 KB record buffers per-record and frees them between records
instead of holding them for the whole session lifetime.

On no-PSRAM ESP32 (e.g. the Theengs Plug) this is the difference between MQTT
TLS (8883) being viable or not: steady-state free heap goes from ~36 KB to
~71 KB (minmem ~18 KB -> ~30 KB), the boot HTTPS update-check and the HA
discovery burst no longer OOM/crash-loop, and flash drops ~19 KB from removing
the DTLS code. Boards with PSRAM (Theengs Bridge) are unaffected.

Bundle: theengs/esp32-arduino-lib-builder 0.1.6 -> 0.1.7 (libs-only rebuild,
same IDF v5.5.4 / arduino-esp32 pin; no platform-espressif32 bump).

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-06 03:53:33 -05:00
FlorianandClaude Opus 4.8 b01240330a [ONOFF] Fix ACTUATOR_TRIGGER build flag: missing -D prefix (#2334)
Several envs pass '-ACTUATOR_TRIGGER=true' instead of
'-DACTUATOR_TRIGGER=true', so the ACTUATOR_TRIGGER macro is never defined
and the physical-button -> relay toggle in sensorGPIOInput.cpp
(#if defined(ZactuatorONOFF) && defined(ACTUATOR_TRIGGER)) is compiled out.

Verified on a Theengs Plug: with the -D added, a button press now toggles the
relay and emits ONOFFtoMQTT. Same one-character fix applied to the other envs
carrying the identical typo.

Affected envs: theengs-plug, esp32dev-all-test, sonoff-basic, sonoff-basic-rfr3.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 06:09:04 -05:00
FlorianandClaude Opus 4.8 268b879239 [BT] Bump NimBLE-Arduino 2.3.2 -> 2.5.0 to fix BLE scan regression on Arduino core 3.3.7+ (#2333)
The esp32_platform 55.03.39 bump (#2329) ships Arduino-ESP32 core >=3.3.7,
whose NimBLE integration enables mesh scan-filtering. NimBLE-Arduino 2.3.2
(pinned since 2025-06-28) handles this incorrectly and stops reporting
advertisements on scan restart, so continuously-scanning gateways (e.g. the
Theengs Bridge) decode nothing.

Upstream fix: h2zero/NimBLE-Arduino 98548470f "[Bugfix] Scan duplicate filter
not resetting on scan restart" (released in 2.3.9). Bumping to the latest 2.5.0
also picks up the Arduino 3.3.7 "controller invalid state" init fix (2.3.8/2.3.9)
and a scan-response timer for long-duration scans.

HIL-validated on a Theengs Bridge v11 with esp32_platform 55.03.39:
  - NimBLE 2.3.2: BLE decode 0/3
  - NimBLE 2.5.0: BLE decode 3/3, reachability 2/2, builds clean

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-28 16:22:53 -05:00
FlorianandClaude 4b7d3f6589 [RTL_433] Update rtl_433_ESP library to v0.5.1 (#2328)
Updates the NorthernMan54/rtl_433_ESP dependency from v0.5.0 to v0.5.1,
which guards vdata_make() against strdup(NULL) for DATA_FORMAT.


Co-authored-by: Claude <noreply@anthropic.com>
2026-06-20 20:27:14 -05:00
FlorianandClaude Opus 4.8 94aa6ae443 [SYS] Update esp32-arduino-libs to 0.1.4 (BLE-only controller) (#2327)
Bumps the precompiled framework-arduinoespressif32-libs bundle from 0.1.3
to 0.1.4, which builds the esp32 BT controller in BLE-only mode (classic
BR/EDR + A2DP/SPP/HFP dropped). OMG's BT stack is NimBLE (a BLE-only host)
and never uses classic Bluetooth, so it was unreachable dead weight.

Measured on a no-PSRAM Theengs Plug (esp32, NimBLE, MQTT over TLS):
- flash 89.5% -> 86.7% (-54 KB)
- runtime freemem +~10 KB: the dual-mode controller reserved heap for the
  classic BR/EDR baseband + 2 classic ACL connections at
  esp_bt_controller_init(); BLE-only skips it
- BLE scan/decode and GATT connect (BM2/BM6 battery reads, SwitchBot
  control) validated working

esp32-arduino-lib-builder change: theengs/esp32-arduino-lib-builder
mem/esp32-ble-only-controller (in 0.1.4).

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 20:18:55 -05:00
FlorianandClaude Opus 4.7 0b6780678f [SYS] Bound remaining MQTT/HTTP/BLE/RF inputs into fixed buffers (#2324)
Follow-up to #2321. That PR fixed five strcpy sites in XtoSYS/createOrUpdateDevice
but left adjacent attack surfaces using the same pattern (strncpy with the
destination's own size, leaving the buffer non-NUL-terminated when the source
is at the limit; or strcpy of WiFiManager/HTTP form values that bypass the
client-side maxlength). The over-long string then flows into saveConfig() and
back into a strcpy on next boot, corrupting heap/.bss on every reload.

main/main.cpp - XtoSYS: length-check before copying mqtt_topic, discovery_prefix,
  gateway_name, gw_pass (ota_pass), and ble_aes. Reject over-length with
  THEENGS_LOG_WARNING, preserving existing values.
main/main.cpp - loadConfigFromFlash: new loadStrField() helper applies the same
  guard to every SPIFFS-loaded string (mqtt_server/port/user/pass per slot,
  mqtt_topic, discovery_prefix, gateway_name, ota_pass, ble_aes). Defense in
  depth in case a pre-fix build left an over-length value in /config.json.
main/main.cpp - WiFiManager save: bound mqtt_topic against the 150-byte portal
  buffer overflowing the 66-byte destination (HTML maxlength is client-side
  only; a raw POST bypasses it).
main/webUI.cpp - handleCG /gw save: bound gateway-password form input before
  the strncpy that previously left ota_pass non-NUL-terminated.
main/blufi.cpp - ESP_BLUFI_EVENT_RECV_STA_SSID/PASSWD: clamp the wire-supplied
  ssid_len/passwd_len before the memcpy and the trailing NUL so a crafted
  BluFi provisioning frame cannot overrun gl_sta_ssid[32]/gl_sta_passwd[64].
main/gatewayBT.cpp - BLEDecryptor (Victron, encr==3): remove `nonce[16] = {0}`,
  a one-byte stack-OOB write reachable from any BLE adv. The line was dead
  (the memset two lines later zeroes the whole array); deleting it eliminates
  the OOB write.
main/gatewaySRFB.cpp - SRFBtoX: bound the _uartbuf writer so a 433 MHz frame
  with no STOP can no longer let _uartpos grow past sizeof(_uartbuf).
main/gatewaySRFB.cpp - _rfbSend: the hex output buffer was sized RF_MESSAGE_SIZE
  (9) but _rawToHex writes 2*N+2 bytes per call. Resize to fit.

Tested on a Theengs Bridge: built theengs-bridge-v11 env, OTA-flashed, and
verified that publishing {"discovery_prefix":"Z"*200} to MQTTtoSYS/config
leaves discovery_prefix unchanged (no Z*65 truncation in subsequent discovery
topics), bridge keeps decoding BLE adverts, and SYStoMQTT/LWT stay healthy.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-14 17:20:24 -05:00
Florian 2459e1c29b [RF] Change ESP8266 default RF_RECEIVER_GPIO from 0 to 4 (#2240) (#2323)
GPIO0 (D3) is the ESP8266 boot strapping pin: an RF receiver holding the
data line low at reset forces the board into flash mode, so a stock
nodemcuv2-rf build never starts after a restart with the receiver wired
to the documented default pin. Default to GPIO4 (D2) instead, which the
docs already recommend as the workaround. Environments that need other
pins (srfb-direct, sonoff-rfr3, rf-wifi-gateway) already override the
value explicitly and are unaffected.
2026-06-14 16:28:01 -05:00
Florian 9f5bf3ec57 [DOCS] Remove dead compatible.openmqttgateway.com links (#2205) (#2322)
The compatible.openmqttgateway.com site is offline (TLS connection reset),
leaving 38 dead links across the docs and README. Repoint BLE device lists
to the canonical Theengs Decoder devices page already used by the navbar,
point boards/devices/parts references to the corresponding docs pages, and
drop the affiliate-site mentions and 'Where to Buy' links that have no live
equivalent. Also fix a malformed markdown link in docs/use/ble.md.
2026-06-14 15:26:02 -05:00
c027dd225d [SYS] Fix heap overflows from unbounded strcpy of MQTT input (#2321)
Guard createOrUpdateDevice() in gatewayBT.cpp against null or >17-char MAC
strings before the strcpy into the 18-byte BLEdevice::macAdr buffer. An
MQTT client could previously publish a crafted white-list/black-list/id
field to MQTTtoBT/config (and related topics) and corrupt the heap.

Apply the same defense to the XtoSYS config branch in main.cpp: mqtt_user,
mqtt_pass, mqtt_server, and mqtt_port are now length-checked against their
destination buffer sizes before strcpy. The mqtt_port destination is only
6 bytes, so this path was particularly easy to overflow via MQTTtoSYS/config.

Oversized fields are now rejected with a warning log; existing values are
preserved and the device does not crash.

Co-authored-by: Florian <1technophile@users.noreply.github.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-06 09:35:27 -05:00
FlorianandClaude 4d945851ce [SYS] Add WiFi reconnect watchdog to self-heal from wedged WiFi stack (#2319)
When the network drops at runtime and the WiFi stack is wedged (e.g. the
ESP32 driver getting stuck after a long uptime, see #2276), the main loop
kept calling wifi_reconnect_bypass() forever with no recovery: the device
stayed stale until a manual reboot. The reboot watchdog only existed in the
ESPWifiManualSetup path, which the default WiFiManager build doesn't compile.

Count consecutive reconnection failures in the runtime loop and ESPRestart(2)
once they exceed maxRetryWatchDog, mirroring the existing manual-setup
watchdog. Gated behind the new WifiReconnectWatchDog flag (default on) so
environments where an unexpected reboot is undesirable can opt out; disabled
for theengs-plug to avoid interrupting the relay state.

Co-authored-by: Claude <noreply@anthropic.com>
2026-06-01 08:50:12 -05:00
FlorianandClaude Opus 4.7 cf27d0e3e7 [BT] Omit state_class/unit_of_measurement for enum sensors in HA discovery (#2316)
Home Assistant rejects an MQTT discovery payload that carries `options`
(an enum sensor) together with `state_class` or `unit_of_measurement`:
these fields are mutually exclusive. createDiscovery() was emitting all
three for Mi Body Composition Scale (XMTZC0x) weighing_mode/unit sensors,
so HA discarded the discovery message.

Gate the stat_cla and unit_of_meas writes on enum_options == nullptr so
enum sensors no longer carry the conflicting fields.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 15:33:44 -05:00
FlorianandClaude Opus 4.7 6d64dbcd8c [BT] Emit hex-valued BLE properties as text sensors (no state_class) (#2315)
iBeacon uuid/mfid (and any decoder property with unit "hex") were built
as HA sensors with state_class "measurement" and unit "hex". Home
Assistant rejects a measurement sensor whose value is non-numeric, so it
logged "non-numeric value" errors on every iBeacon decode. Route "hex"
units through the same path as "string" units: plain text sensor, no
state_class, no unit_of_measurement.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 15:33:26 -05:00
FlorianandClaude Opus 4.7 e7411d2000 [BT] Reset BTProcessLock when immediateBTAction can't take the device semaphore (#2314)
immediateBTAction() sets BTProcessLock=true and stops the scan before an
outbound BLE action, and resets it on the success path and the
"BLE busy" path — but the branch where semaphoreCreateOrUpdateDevice
can't be taken left it true. Under scan contention that wedges the BLE
scanner/connect subsystem until a restart (same BTProcessLock-mishandling
family as the long-uptime NimBLE scanner hang). Reset it on that branch
too.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 15:33:06 -05:00
FlorianandClaude Opus 4.7 7904d49dbe Fix/wifimanager portal defaults (#2312)
* [SYS] Don't clobber MQTT/portal defaults with empty form fields

The /wifisave handler unconditionally strcpy'd every form value into the
persisted config, even when WiFiManager surfaced an empty string for a
field the caller omitted. A partial provisioning POST (e.g. only s/p)
silently wiped the compile-time MQTT defaults so the DUT joined WiFi but
never reached the broker.

Mirror the mqtt_pass sentinel guard with strlen() > 0 checks for the
remaining textual fields (server, port, user, topic, name, ota). The
boolean checkboxes and certificate fields already had non-empty guards.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* [DOCS] Document /wifisave provisioning portal API

Add a reference page enumerating the HTTP form fields accepted by the
on-device configuration portal so companion apps and provisioning scripts
can integrate without reading firmware source. Documents the empty-field
ignore semantics introduced in the previous commit and the AP-teardown
response race that callers must tolerate.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* [SYS] Also length-guard the MQTT password sentinel branch

The sentinel-only check (form-value != MQTT_PASS) was meant to protect
the stored password against accidental re-saves of the form default,
but it also persists an empty string from a partial / programmatic POST
that omits the `pass` field — empty differs from the sentinel, so the
broker password gets wiped. Add a strlen() > 0 precondition so partial
POSTs preserve the existing password.

Verified on the HIL bench (test_sys/test_wifimanager_portal_partial):
without this commit the DUT joined WiFi but never reached the broker;
with it, LWT-online lands within the reconnect budget.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* [DOCS] Reflect length-guard in /wifisave mqtt_pass semantics

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 14:01:48 -05:00
FlorianandClaude Opus 4.7 319c05af5c [BT] Default BLE AES key empty, skip decryption without a key (#2310)
* [BT] Default BLE AES key empty, skip decryption without a key

Shipping a non-empty default key caused fresh installs to attempt
decryption of every encrypted PVVX/BTHome v2/Victron advertisement with
the placeholder string from User_config.h. Frames that didn't happen to
share that key were silently dropped at the CCM auth step, masking
otherwise-decodable devices (notably Victron MPPT) until users found and
entered a per-MAC key.

Initialise ble_aes[] to "" and treat an empty default as "no default
configured" in the decryption path - mirroring Theengs Gateway, which
has no default and skips frames whose MAC has no bindkey. Per-MAC keys
in ble_aes_keys still take precedence; the BLE_AES macro remains as the
WebUI placeholder hint.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* [BT] clang-format: move BLE_AES default comment above #define

clang-format-9 rejected the trailing-comment continuation on the empty
BLE_AES default line. Move the explanation to a leading // block above
the #ifndef so the directive line is comment-free.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* [BT] Use THEENGS_LOG_TRACE for missing-AES-key skip log

Previous commit referenced THEENGS_LOG_DEBUG, which OMG does not define
(only VERBOSE/TRACE/NOTICE/WARNING/ERROR exist), breaking every BLE
firmware build. Switch to THEENGS_LOG_TRACE — same level as the adjacent
"Custom AES key" / "Default AES key" trace lines.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 10:26:49 -05:00
FlorianandClaude Opus 4.7 9e93e37506 [DOCS] Restore dev-version banner gate after VitePress migration (#2305)
The VuePress->VitePress migration replaced the `themeConfig.mode === 'dev'`
gate with a heuristic on `site.title`. CI overrides version with a short
SHA (dev) or git tag (prod), so the title never contains "edge"/"dev" and
the banner never renders on live deploys. Surface `meta.mode` in
`themeConfig` and check it directly, mirroring the original behaviour.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 07:31:13 -05:00
ddd05ebe50 [CI] Pin SmartRC-CC1101-Driver-Lib via GitHub URL (#2304)
The PlatformIO library registry can no longer resolve
SmartRC-CC1101-Driver-Lib@2.5.7:

  Library Manager: Installing SmartRC-CC1101-Driver-Lib @ 2.5.7
  Warning! Could not find the package with 'SmartRC-CC1101-Driver-Lib @ 2.5.7'
    requirements for your system 'linux_x86_64'

This breaks every CC1101-based environment in the nightly build:
esp32dev-rtl_433, esp32dev-pilight-cc1101, esp32dev-multi_receiver,
esp32dev-somfy-cc1101, nodemcuv2-rf2-cc1101, and others fail with:

  main/commonRF.cpp:30:14: fatal error: ELECHOUSE_CC1101_SRC_DRV.h: No such file

Switch to the upstream GitHub release URL, matching how other
removed-from-registry libs in this file are pinned (m5tough, stl,
shtc3, etc). The v2.5.7 tag still exists in the upstream repo.

Co-authored-by: Florian <1technophile@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-27 06:20:50 -05:00
7551ed9e22 [MEM] Enable PSRAM on Theengs Bridge v1.1 (ESP32-WROVER-IE) (#2303)
- Switch Ethernet RMII clock from GPIO16 to GPIO0 output mode,
  freeing GPIO16 for PSRAM chip select (SPICS1) on WROVER-IE
- Enable PSRAM with QIO flash mode and cache workaround flags
- Add PHY pre-power support for ETH_CLOCK_GPIO0_IN mode
- Report PSRAM status and free memory in SYS MQTT topic

Requires Theengs lib-builder update: remove LIBTIME from IRAM
cache workaround (CONFIG_SPIRAM_CACHE_LIBTIME_IN_IRAM=n) to
free ~3.5KB IRAM for the -mfix-esp32-psram-cache-issue flag.

Tested on Theengs Bridge v1.1: 4MB PSRAM detected, Ethernet
100Mbps on GPIO0, BLE scanning nominal.

Co-authored-by: Florian <1technophile@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-26 15:59:12 -05:00
FlorianandClaude Opus 4.7 23fc99f0fe [MEM] Heap-allocate createDiscovery JsonDocuments (#2302)
createDiscovery sits at the bottom of the discovery call chain
(loop → launchBTDiscovery → <device>Discovery → createDiscoveryFromList
(9-col, VLA) → createDiscoveryFromList(13-col) → here). Keeping
2×1024 B StaticJsonDocuments on the stack burned loopTask headroom
down to ~100 B free under a full BLE + WebUI load. Moving the two
buffers to the heap drops that 2 KB off the stack peak.

Measured over the full test_perf + test_webui suite on
esp32dev-ble-hil-fastsys (dev HEAD):

  freestck_min_bytes: 292 → 2324 B   (+2032, target was ≥1500)
  minmem_soak_bytes: 18200 → 20652 B (+2452)
  all 17 tests still pass

The docs are function-scoped, so net heap usage is unchanged long-term
(free-on-return), and the soak heap floor actually improved.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 12:00:13 -05:00
9e8098964c [MEM] Reduce NimBLE max connections from 3 to 1 (#2300)
The BLE gateway only connects to one device at a time sequentially,
so 3 connection slots are unnecessary. This saves ~4-8KB of RAM.

Co-authored-by: Florian <1technophile@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-25 11:59:49 -05:00
8273617c17 [DOCS] Migrate documentation from VuePress 1.x to VitePress (#2299)
- Replace VuePress 1.9.10 with VitePress 1.6.4
- Convert config from CommonJS to ESM TypeScript (config.mts)
- Convert shared Theengs nav config to ESM (commonConfig.mts)
- Rewrite BoardEnvironmentTable in Vue 3 Composition API
- Rewrite FlashEnvironmentSelector in Vue 3 Composition API
- Enable built-in local search and dark mode
- Add medium-zoom for image zoom
- Use VitePress CSS variables for dark mode compatibility
- Update CI script paths from .vuepress to .vitepress
- Update .gitignore for VitePress build artifacts
- Rename README.md files to index.md (VitePress convention)
- Fix markdown compatibility (angle-bracket escaping, image paths)
- Drop PWA plugin (not needed)

Co-authored-by: Florian <1technophile@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 07:00:20 -05:00
3b2b8a333a [WebUI] Fix BLE config page connection reset on load (#2295)
ble_script is defined as "" (empty string). Passing it to
server.sendContent("") signals end of HTTP chunked transfer,
closing the response prematurely. All subsequent chunks then
fail with errno 104 "Connection reset by peer".

Guard the call with a length check so the empty string is
never sent as a chunk terminator.

Co-authored-by: Florian <1technophile@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 07:23:06 -05:00
3a4eb55a79 [MEM] Reduce ESP32 BLE firmware RAM usage (#2294)
- Fix addLog buffer overflow; reduce LOG_BUFFER_SIZE 6096→3072, ROW_LENGTH 1024→512
- Lazy-allocate web console log buffer (saves ~3.5KB when console not active)
- Stream WebUI responses in chunks to eliminate String heap allocations
- NimBLE buffer tuning: ACL bufs 20→12, HCI hi bufs 30→15, MSYS1 blocks 12→8, MTU 256→185
- BLE adv report flow control 100→50, LWIP sockets 16→10
- BLE scan duplicate cache 200→100 (align with sdkconfig)
- procBLETask stack 9500→8000 (788B headroom over measured peak)
- Cap BLE device vector at 80 with LRU eviction of non-listed devices
- Disable unused LWIP components: PPP, IPv6 autoconfig/DHCPv6
- Disable unused Arduino libs: SD, SD_MMC, FFat, LittleFS, NetBIOS

Measured over 30 min A/B test vs development:
  freemem: 58,805 → 68,755 avg (+10KB)
  minmem:  5,988 → 15,780 (+9.8KB)
  msgprc/scnct: identical (656 msgs, 26 scans)
  msgblck: 0 on both branches
  No crashes or errors

Co-authored-by: Florian <1technophile@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-22 19:05:44 -05:00
7d6a019699 [WebUI] Add full BLE configuration page and MQTT Discovery toggle (#2293)
Expand the BLE config page from encryption-only (BLEDecryptor) to a
comprehensive settings page available for all ZgatewayBT builds, covering
scan, publish, presence, external decoder, and encryption settings.
Add MQTT Discovery checkbox to the gateway configuration page.

Co-authored-by: Florian <1technophile@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 18:12:31 -05:00
8b3aa1d981 [DOCS] Add search and filter bar to board selection page (#2291)
Add a search input and filter chips to the web-install board grid,
allowing users to quickly find boards by name, MCU family (ESP32,
ESP32-S3, ESP32-C3, ESP8266), or gateway type (BLE, RF, IR, LoRa,
RTL_433, Pilight). Filters are derived dynamically from the board
data. Shows result count and a clear-all button when filters are
active.

Co-authored-by: Florian <1technophile@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 07:54:11 -05:00
c58ead0a35 [CI] Skip nightly dev build when no changes in the last 24 hours (#2290)
The prepare job now checks for commits in the last 24 hours before
proceeding. If there are no changes, the firmware build and docs
deploy are skipped, saving CI minutes. Manual triggers always run.

Co-authored-by: Florian <1technophile@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 07:37:28 -05:00
883e9318cf [DOCS] Fix dev docs deploying to /dev/dev/ instead of /dev/ (#2289)
The ci_site.sh script was overriding the VuePress dest to
"generated/site/dev" in dev mode. Since task-docs.yml already sets
destination_dir: "dev" in the GitHub Pages deploy step, this caused
the site to be nested at /dev/dev/ on gh-pages, leaving
docs.openmqttgateway.com/dev/ empty.

Remove the dest override and let the deploy action handle the
subdirectory placement.

Co-authored-by: Florian <1technophile@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 09:31:27 -05:00
Florian cd9d6ecb96 [DOCS] Remove build and code format badges (#2284)
Removed build and code format badges from README.
2026-02-15 15:58:27 -06:00
5fe77e9c85 [CI] Remove Claude Code GitHub Actions workflows (#2283)
* [CI] Fix Claude review action failing on fork PRs

Use pull_request_target instead of pull_request so OIDC tokens and
secrets are available when reviewing PRs from external contributors.
Explicitly checkout the PR head SHA to review the actual changes.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* [CI] Harden review workflow against prompt injection from forks

Checkout the base branch instead of the PR head to prevent CLAUDE.md
poisoning and code-level prompt injection. Claude uses gh pr diff to
review changes without exposing the runner to untrusted fork code.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* [CI] Remove Claude auto-review workflow on PRs

Remove the pull_request_target review workflow due to prompt injection
risks from fork PRs. Claude can still be invoked on-demand via @claude
mentions through the claude.yml workflow, which is safer as it requires
a trusted maintainer to trigger.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* [CI] Restrict @claude trigger to repo owner only

Add actor check so only 1technophile can invoke Claude via @claude
mentions. Prevents untrusted users from triggering the action and
consuming API credits or attempting prompt injection via comments.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* [CI] Remove Claude @mention workflow

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Florian <1technophile@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 15:56:44 -06:00
fd433c220e [BLE] Add BM6 Battery Monitor connection support (#2274)
* [BLE] Add BM6 Battery Monitor connection support

Implement BLE connection support for BM6 Battery Monitor devices,
following the BM2 pattern with BM6-specific protocol handling.

Key features:
- Encrypted command/response using AES-128 CBC
- Read voltage, temperature, and State of Charge (SoC)
- Home Assistant MQTT Discovery support
- Device tracker integration

Technical implementation:
- Add BM6_connect class with encrypted command handling
- Encryption key (static const): "leagend\xff\xfe0100009"
- Parse hex string positions for voltage (15-17), temp (8-9), SoC (12-13)
- Add discovery with 3 sensors (voltage, temperature, battery %)
- Validate message signature (D15507)
- Add canWrite() check before writing to characteristic
- Empty stub for non-ESP32 builds

Code quality improvements:
- Extract AES key as static constant to avoid duplication
- Use shared jsonVoltBM template for both BM2 and BM6
- Clean up commented-out reasoning in parsing logic

Based on reverse engineering from:
https://github.com/JeffWDH/bm6-battery-monitor

Files modified:
- main/gatewayBLEConnect.h: Add BM6_connect class
- main/gatewayBLEConnect.cpp: Implement BM6 connection logic
- main/gatewayBT.cpp: Add detection, connection, and discovery handlers
- main/config_mqttDiscovery.h: Rename jsonVoltBM2 to jsonVoltBM

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* Add BM6 to exceptions

---------

Co-authored-by: Florian <1technophile@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-24 17:16:03 -06:00
f52bc6c11e [SYS] Fix: Set WiFi hostname to gateway name (#2272)
* Fix: Set WiFi hostname to gateway name

Add WiFi.setHostname() calls to ensure the ESP's WiFi hostname matches
the configured gateway name instead of using the default ESP hostname.

- Add wifiManager.setHostname(gateway_name) in setupWiFiManager() for
  WiFiManager-based setups
- Add WiFi.setHostname(gateway_name) in setupWiFiFromBuild() for manual
  WiFi setup configurations (ESPWifiManualSetup)

This matches the existing behavior for Ethernet connections where
ETH.setHostname(gateway_name) is already called.

Fixes #2150

Co-authored-by: Florian <1technophile@users.noreply.github.com>

* Change WiFi mode position

* Fix setHostname when using WiFi Manager

---------

Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Co-authored-by: Florian <1technophile@users.noreply.github.com>
2026-01-21 08:22:00 -06:00
32664499f3 [LORA] Fix syncword not persisting after reset (#2271)
The bug was a race condition where saved configuration was applied
to the LoRa hardware BEFORE LoRa.begin() was called, causing settings
like syncword to be lost after reset/power cycle.

Changes:
- Created LORAConfig_apply() to centralize hardware configuration
- Removed hardware application from LORAConfig_fromJson() (now only
  updates struct values)
- Call LORAConfig_apply() after LoRa.begin() in setupLORA()
- Call LORAConfig_apply() after LORAConfig_fromJson() in runtime
  handlers (MQTT/WebUI) since hardware is already initialized
- Fixed syncword logging to properly show 'unchanged' vs 'changed'

Fixes #2270

Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Co-authored-by: Florian <1technophile@users.noreply.github.com>
2026-01-10 11:59:41 -06:00
e01b22695a [RF] Fix CC1101 initialization on boot with saved RF config (#2269)
When a saved RF configuration exists in NVS, loadFromStorage() was loading
the config but not calling iRFReceiver.enable(), which meant initCC1101()
was never invoked during boot. This caused the CC1101 to not receive signals
until the user manually toggled the RF receiver via the WebUI.

This fix adds a reinitReceiver parameter to loadFromStorage() that controls
whether to disable/enable the receiver after loading config. By default it's
true (for boot-time initialization), but loadFromMessage() passes false to
avoid double initialization when loading config from MQTT messages.

Fixes #2264

Co-authored-by: Florian <1technophile@users.noreply.github.com>
Co-authored-by: Odyno <odyno@users.noreply.github.com>

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

Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
2026-01-06 05:47:49 -06:00
0c2884c392 [CI] Configure Claude GitHub Actions to use Opus model (#2267)
* [CI] Configure Claude GitHub Actions to use Opus model

- Add --model opus flag to claude.yml workflow
- Add --model opus flag to claude-code-review.yml workflow
- Ensures advanced reasoning capabilities for complex firmware codebase

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* Fix model ID to use full Claude Opus identifier

---------

Co-authored-by: Florian <1technophile@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-05 07:58:05 -06:00
FlorianandFlorian 5c401f6d02 [CI] Add Claude GitHub actions (#2266)
* "Claude PR Assistant workflow"

* "Claude Code Review workflow"

* Add CLAUDE md for context

---------

Co-authored-by: Florian <1technophile@users.noreply.github.com>
2026-01-03 10:54:31 -06:00
e91086a9bd [DOC] Sort firmware dropdown lists alphabetically (#2258)
Sort the web uploader firmware selection dropdowns alphabetically
for better user experience. Applies to both dev and release builds.

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

Co-authored-by: Florian <1technophile@users.noreply.github.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-07 16:45:07 -06:00
5f969bf26b [CI] Fix nightly build failures (#2257)
- Convert nightly workflow to matrix builds to fix FRAMEWORK_DIR issues
  caused by platform_packages conflicts when building sequentially
- Each environment now builds on its own runner, matching build.yml
- Add deploy job that collects all artifacts and deploys docs
- Add extra_scripts to esp32dev-pilight environment to fix implicit
  function declaration error with newer GCC toolchain

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

Co-authored-by: Florian <1technophile@users.noreply.github.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-07 16:03:30 -06:00
FlorianandFlorian e495b61216 [SYS] Fix ESP8266 local builds (#2234)
Following #2232

Co-authored-by: Florian <1technophile@users.noreply.github.com>
2025-08-20 06:16:47 -05:00
FlorianandFlorian 32174e37a5 [SYS] Reduce Logs flash footprint (#2232)
By building only the log level required into LOG_LEVEL

Co-authored-by: Florian <1technophile@users.noreply.github.com>
2025-08-19 09:15:41 -05:00
FlorianandFlorian 6e6b54b05e [DISC] Ram usage and flash size optimization (#2223)
Fix duplicate W and keep units and classes in flash
Use macro for discovery classes and units
[BREAKING] change gateway sensor unique id
module - parameter
Standardize the use of createDiscoveryFromList

Co-authored-by: Florian <1technophile@users.noreply.github.com>
2025-08-17 06:27:42 -05:00
FlorianandFlorian 6d7e4fd8f1 [BOARD] Remove Theengs Bridge self test (#2225)
Not needed for web or OTA update. It will enable to save flash

Co-authored-by: Florian <1technophile@users.noreply.github.com>
2025-08-13 07:14:30 -05:00
Florian bb135aa0bf [DOCS] Remove hits (#2175) 2025-04-09 05:36:23 -05:00
FlorianandFlorian cd35520bb0 [BT] Increase again procBLEtask stack size (#2152)
Co-authored-by: Florian <1technophile@users.noreply.github.com>
2025-01-14 07:23:57 -06:00
FlorianandFlorian 5b6cf44662 [CI] Update python version to 3.11 (#2151)
Co-authored-by: Florian <1technophile@users.noreply.github.com>
2025-01-14 07:23:41 -06:00
FlorianandFlorian bead01347e [BT] Increase procBLEtask stack size to avoid reset when adapting the scan parameters (#2148)
Co-authored-by: Florian <1technophile@users.noreply.github.com>
2025-01-10 13:47:34 -06:00
FlorianandFlorian 1f45a7711b [SYS] Avoid queue overloading at start (#2114)
Give one cycle of the loop without starting BT

Co-authored-by: Florian <1technophile@users.noreply.github.com>
2024-11-15 10:37:20 -06:00
FlorianandFlorian 8bad846d94 [BT] Update Theengs decoder to v1.8.0 (#2113)
Co-authored-by: Florian <1technophile@users.noreply.github.com>
2024-11-15 09:47:38 -06:00
FlorianandFlorian b5adf1f5ac [BROKER] Fix BT start (#2112)
Co-authored-by: Florian <1technophile@users.noreply.github.com>
2024-11-13 07:42:44 -06:00
FlorianandFlorian bbea094438 [LORA][BREAKING] Update LoRa env partition (#2110)
Not enough flash memory with the regular one

Co-authored-by: Florian <1technophile@users.noreply.github.com>
2024-11-13 07:28:56 -06:00
FlorianandFlorian 18cfbb759a [SYS] Remove Multi Gateway Sync from idf example env (#2109)
To avoid consuming procBLEtask stack memory when using esp idf framework

Co-authored-by: Florian <1technophile@users.noreply.github.com>
2024-11-12 09:55:17 -06:00
FlorianandFlorian 3bcfdee712 [BLUFI] Fix SSID return (#2108)
The filter on NTWK_CONNECTED was too restrictive when asking for WiFi status.
Get the WiFi name and BSSID from the WiFi object

Co-authored-by: Florian <1technophile@users.noreply.github.com>
2024-11-12 08:50:24 -06:00
FlorianandFlorian 379312a127 [BLUFI] Enable larger notifications (#2107)
by replacing esp_blufi_send_notify by esp_blufi_send_custom_data

Co-authored-by: Florian <1technophile@users.noreply.github.com>
2024-11-12 08:37:51 -06:00
FlorianandFlorian fdc7435079 [SYS] Fix capacity to onboard ethernet board without WiFi AP (#2106)
Co-authored-by: Florian <1technophile@users.noreply.github.com>
2024-11-12 08:26:55 -06:00
FlorianandFlorian f5fe3ed223 [DISC] Remove unnecessary discovery publication (#2105)
They are not anymore needed as we keep the scan and presence parameters always displayed

Co-authored-by: Florian <1technophile@users.noreply.github.com>
2024-11-12 08:26:29 -06:00
FlorianandFlorian 921488359e [BT] Fix BT command triggering a stack trace due to conflict (#2104)
related to the vector between BLE scanning and BLE control

Co-authored-by: Florian <1technophile@users.noreply.github.com>
2024-11-12 08:26:15 -06:00
FlorianandFlorian bb05011bdc [SYS] Fix webUI OTA update free memory (#2103)
When doing an OTA update we deinit BT to free enough memory for the update

Co-authored-by: Florian <1technophile@users.noreply.github.com>
2024-11-11 17:27:26 -06:00
FlorianandFlorian 87228b5f29 [SYS] Bump ESP8266 framework version to 4.2.1 (#2102)
Co-authored-by: Florian <1technophile@users.noreply.github.com>
2024-11-10 08:24:31 -06:00
FlorianandFlorian 3cd7c92d8f [SYS] Fix ESP8266 discovery publications (#2100)
Co-authored-by: Florian <1technophile@users.noreply.github.com>
2024-11-10 08:24:13 -06:00
FlorianandFlorian 742945906d [SYS] Fix erase function for ESP8266 (#2101)
And remove restart parameter as it is not used (the ESP always restart after erase for our use cases)

Co-authored-by: Florian <1technophile@users.noreply.github.com>
2024-11-10 08:23:53 -06:00
FlorianandFlorian 952a65dd0f [DISC] Fix device name discovery (#2093)
Fix issue introduced by #1936

Co-authored-by: Florian <1technophile@users.noreply.github.com>
2024-10-25 13:24:31 -05:00
FlorianandFlorian 898b5e6213 [ONB] Fix failsafe mode (#2080)
Erasing the nvs before the start of WM prevent WM from starting, revert a part of #2075
Also remove the erase from setupWiFiManager method

Co-authored-by: Florian <1technophile@users.noreply.github.com>
2024-10-04 17:02:50 -05:00
FlorianandFlorian 4e090eda3c [SERIAL] Improve communication robustness (#2079)
-Add a semaphore to avoid msg overlap
-Clean partial buffer
-Enable erase and restart of the secondary module from the primary

Co-authored-by: Florian <1technophile@users.noreply.github.com>
2024-10-03 09:47:22 -05:00
FlorianandFlorian fcc09df02c [DISC] Fix via device discovery when using secondary module(#2077)
Co-authored-by: Florian <1technophile@users.noreply.github.com>
2024-10-01 20:02:18 -05:00
FlorianandFlorian e390aca43b [SYS] Erase secondary module flash when button is pressed on primary module (#2076)
Co-authored-by: Florian <1technophile@users.noreply.github.com>
2024-10-01 16:29:03 -05:00
FlorianandFlorian c234afa9c8 [SYS] Fix connection validation on unknown indexes and improve erase method (#2075)
Before this fix, connections with Index 1 and 2 were supposed valid when not present in the configuration, generating attempts to connect with those indexes.
Also improve the erase config by adding an erase when the config file is not present (ESP is connected to WiFi without the config file). This way the ESP will not reconnect to the WiFi AP.

Co-authored-by: Florian <1technophile@users.noreply.github.com>
2024-10-01 11:48:39 -05:00
FlorianandFlorian 9e5f56580d [SYS] Detect IDF environnement with USE_ESP_IDF (#2074)
Rather than USE_BLUFI only, as IDF can be used without BLUFI

Co-authored-by: Florian <1technophile@users.noreply.github.com>
2024-10-01 11:42:11 -05:00
FlorianandFlorian 88e09092d6 [SERIAL] Add Serial Heartbeat ack (#2073)
And enable to launch the loop when offline: false and serial communication layer is activated

Co-authored-by: Florian <1technophile@users.noreply.github.com>
2024-10-01 10:47:55 -05:00
FlorianandFlorian 3187ed6154 [SERIAL] Enable SERIAL to X communication (#2068)
Add Serial as a communication layer so as to transmit data to/from serial
Enable discovery of BT sensors through serial
Add serial heartbeat

Change discovery principle for interval and intervalacts, keep them always discovered and deactivate adaptivescan when changed. Instead of removing their discovery when adaptive scan is activated

Increase JSON_MSG_BUFFER_MAX as we are now using const char* and it requires more memory for Arduino Json Deserialization

Co-authored-by: Florian <1technophile@users.noreply.github.com>
2024-09-26 06:55:26 -05:00
FlorianandFlorian 026fb4a337 [BOARD] Add Theengs Plug environment (#2065)
Co-authored-by: Florian <1technophile@users.noreply.github.com>
2024-09-24 10:11:05 -05:00
FlorianandFlorian 0d338cc5c5 [LED] Add LED STRIP macros and a third Addressable pin (#2063)
* [LED] Add LED STRIP macros and a third Addressable pin

---------

Co-authored-by: Florian <1technophile@users.noreply.github.com>
2024-09-19 20:36:46 -05:00
FlorianandFlorian 0b4c6ef107 [BT] Clean KnownBTAction function (#2062)
Co-authored-by: Florian <1technophile@users.noreply.github.com>
2024-09-17 09:58:21 -05:00
FlorianandFlorian 4b20976a08 [BLUFI] Add Blufi activation and deactivation at runtime (#2058)
Co-authored-by: Florian <1technophile@users.noreply.github.com>
2024-09-17 09:32:20 -05:00
FlorianandFlorian 776ed6568d [BT] Fix crash when sending multiple BT commands at the same time (#2059)
Co-authored-by: Florian <1technophile@users.noreply.github.com>
2024-09-17 07:29:36 -05:00
FlorianandFlorian ed0b683754 [LED] Add power pin to addressable LED (#2056)
Co-authored-by: Florian <1technophile@users.noreply.github.com>
2024-09-13 11:21:41 -05:00
FlorianandFlorian 435b8ca3d9 [LED] Refactor LED handling (#2051)
Refactor LED handling into a library
Use a task instead of relying on the loop for precise timing

Co-authored-by: Florian <1technophile@users.noreply.github.com>
2024-09-13 09:55:56 -05:00
FlorianandFlorian 5d6f735058 [SYS} Improve queue performance whenb Using Ethernet and low interval between scan (#2052)
Add also a msgrcv (messages received) indicators into SYStoMQTT to measure the performances

Co-authored-by: Florian <1technophile@users.noreply.github.com>
2024-09-10 16:33:46 -05:00
FlorianandFlorian 00fe36f426 [BT] Add Switchbot curtain (#2049)
Add SB curtain commands and add stop to SB tilt

Co-authored-by: Florian <1technophile@users.noreply.github.com>
2024-09-05 08:54:53 -05:00
FlorianandFlorian 2b958ae363 [SYS] Externalize utils function keep cmpToMainTopic (#2048)
And fill generateHash

Co-authored-by: Florian <1technophile@users.noreply.github.com>
2024-09-04 13:18:41 -05:00
FlorianandFlorian 5e65e807b7 [SYS] Externalize utils functions (#2046)
To declutter main.ino code

Co-authored-by: Florian <1technophile@users.noreply.github.com>
2024-09-04 10:31:51 -05:00
FlorianandFlorian 0999563095 [CI] Update Github actions versions (#2047)
* [CI] Update Github actions

* Update setup-node

---------

Co-authored-by: Florian <1technophile@users.noreply.github.com>
2024-09-04 10:30:05 -05:00
FlorianandFlorian 220e2fe164 [BLUFI] Add Bluetooth timer, notifications and AP scan list (#2045)
Add a Bluetooth timer to automatically disconnect the Bluetooth client
Add custom data notifications
Add AP Scan list
Change MQTTtoSYS API to accept server or port alone

Co-authored-by: Florian <1technophile@users.noreply.github.com>
2024-09-04 08:37:17 -05:00