RulesProcessEvent() in xdrv_10_system_events.ino (which owns the Xdrv10
slot when neither USE_RULES nor USE_SCRIPT is compiled) returned true
unconditionally. SendKey() interprets true as "event serviced", so the
default ButtonN -> ExecuteCommandPower action never executed: the press
was logged (BTN: ButtonN multi-press 1) but the relay never toggled.
Return false instead, mirroring xdrv_10_rules.ino's 'serviced' semantics
where true means a rule actually consumed the event.
Recent core refactoring removed standard support for float variables in printf family functions, resulting in eQ-3 telemetry messages showing '*float' instead of actual float values. Updated the formatting specifier to %1_f and passed the reference to the float parameter to ResponseAppend_P function
Co-authored-by: Jens.Heilig <jens.heilig@esystems-mtg.de>
* feat: add qemu emulator
* chore: use current dir instead of cloning repo
* Add QEMU emulator tooling under tools/qemu
tools/qemu/tasmota-qemu.sh builds the current Tasmota checkout for an ESP32
PlatformIO env and boots it under an Espressif QEMU fork on an amd64 host:
serial console, and a browsable web UI via the WiFi-capable fork.
NET_WIFI=1 tools/qemu/tasmota-qemu.sh # build -> image -> wifi QEMU -> web UI
Notes:
- Builds the local working tree (no clone); artifacts and QEMU forks live under
a gitignored tasmota-qemu/ at the repo root.
- ESP32-only helper; no Tasmota core source is changed.
See tools/qemu/README.md for commands, env vars, and panic-debugging notes.
* chore: cleanup
* MIELHVAC New features and bug fixes
## Changelog
### Bug Fixes
**Memory leak in `miel_hvac_pre_init()`** — `goto del` jumped past the `free(sc)` label, leaking the allocated struct on serial init failure. Replaced with explicit `delete`/`free`/`return`.
**`remotetemp_clear` semantics inverted** — variable was `true` on boot causing a CLR frame to be sent before any sensor registered. Renamed to `remotetemp_active`, initialised `false`.
**`HVACSetPurify` used wrong map** — `airdirection_map` was passed instead of `purifier_map`.
**`0x08` Set Run State flags wrong byte order** — flags are little-endian on the wire (ref: muart-group/muart-group.github.io#17). Removed `htons()` from all `0x08` flag assignments.
**`widevane_isee` false positives** — values like `0x84`/`0x85`/`0x8c` (ISEE bit + position nibble) incorrectly reported `AirDirection:"even"`. Reverted to exact-match for `0x80`, `0x28`, `0xaa`.
---
### New Features
**`0x42` HVAC Options polling** — added `miel_hvac_data_hvac_options` struct and `MIEL_HVAC_REQUEST_HVAC_OPTIONS` request. The unit is polled for Purifier, NightMode and EconoCool state. Requires short request form (len=1). Results stored in `sc_hvac_options` and published in SENSOR and HVACSETTINGS when `cap_run_state=true`.
**Run State commands (`0x41 0x08`)** — new commands `HVACSetPurify`, `HVACSetNightMode`, `HVACSetEconoCool` and `HVACSetAirDirection` sent via `0x08` on units that report `cap_run_state=true`. Optimistic update to `sc_hvac_options` applied before confirmation.
**EconoCool** — `0x08` byte 14, flag `0x10`, COOL mode only. Command `HVACSetEconoCool on|off`.
**Base Capabilities (`0x5B 0xC9`)** — queried once after connecting. Parsed into `miel_hvac_capabilities`. Results published flat inside `MiElHVAC` as `*Supported` fields. Temperature ranges published as °C. Raw packet in `CapabilitiesHex`.
**Capabilities-aware command validation** — commands blocked when unit reports feature unavailable: modes (heat/dry/fan), temperature range from capabilities, fan speeds, run state commands return `NotSupported` or `ControlNotSupported`.
**`0x42` polling skip** — skipped on units with `cap_run_state=false`, eliminating recurring timeouts.
**`miel_hvac_append_settings_json()`** — shared helper replacing ~80 lines of duplicated JSON code between SENSOR and HVACSETTINGS topics.
---
### JSON Changes
- `Power` (W) and `Energy` (kWh) published inside `MiElHVAC` and also in a separate `ENERGY{}` object outside `MiElHVAC` for Home Assistant auto-discovery
- `Purifier`, `NightMode`, `EconoCool` shown in SENSOR and HVACSETTINGS when `cap_run_state=true`; otherwise omitted
- `AirDirection` always shows current state from `0x62 0x02` regardless of control support
- `*Supported` capability fields published flat inside `MiElHVAC`: `HeatSupported`, `DrySupported`, `FanSupported`, `VaneVSupported`, `SwingSupported`, `AutoFanSupported`, `OutdoorTempSupported`, `AirDirectionSupported`, `PurifierSupported`, `NightModeSupported`, `EconoCoolSupported`
---
### Tested on
MSZ-LN25VG2W — `cap_run_state=false`, temp ranges 16–31 °C cool/auto, 10–31 °C heat, 5 fan speeds.
* Extend AirDirection capability, added web ui energy sensor, cleanup
### AirDirection — respect i-See sensor presence
`AirDirection` now requires three conditions instead of just the vertical vane capability:
- `cap_vane_v` — unit has a vertical vane
- `sc_has_isee` — i-See sensor observed at runtime (widevane ever seen with bit `0x80`, or value `0x28`/`0xaa`)
- `cap_run_state` — unit supports `0x08` for control
Resulting behaviour:
| `cap_vane_v` | `sc_has_isee` | `cap_run_state` | `AirDirection` visible | `AirDirectionSupported` |
|:-:|:-:|:-:|:-:|:-:|
| ❌ | — | — | hidden | `not_supported` |
| ✅ | ❌ | — | hidden | `not_supported` |
| ✅ | ✅ | ❌ | shown | `control_not_supported` |
| ✅ | ✅ | ✅ | shown | `on` |
Units with vertical + horizontal vanes but no i-See sensor now correctly report `AirDirectionSupported:"not_supported"` and omit `AirDirection` from both `MiElHVAC` and `HVACSettings` JSON.
### Capability field renames
For naming consistency across the `*Supported` fields:
- `cap_heat` → `cap_mode_heat`, `HeatSupported` → `ModeHeatSupported`
- `cap_dry` → `cap_mode_dry`, `DrySupported` → `ModeDrySupported`
- `cap_fan_mode` → `cap_mode_fan`, `FanSupported` → `ModeFanSupported`
- `cap_auto_fan` → `cap_fan_auto`, `AutoFanSupported` → `FanAutoSupported`
- `OutdoorTempSupported` → `OutdoorTemperatureSupported`
- `TempCool` / `TempHeat` / `TempAuto` → `SetTemperatureCoolMinMax` / `SetTemperatureHeatMinMax` / `SetTemperatureAutoMinMax`
### Energy values
- `Power` (W) and `Energy` (kWh) published both inside `MiElHVAC` and in a standard Tasmota `ENERGY{}` sub-object (`Power`/`Total`) for Home Assistant auto-discovery.
- Added Web UI rows via `FUNC_WEB_SENSOR` showing instantaneous power (W) and cumulative total (kWh) on the Tasmota main page.
### Runtime i-See detection
New `sc_has_isee` flag in `miel_hvac_softc`, set in `miel_hvac_input_settings` on the first widevane value indicating i-See state. Once set, stays set for the session.
**Practical note:** after boot, the flag starts `false`. Units with i-See that has never been activated since Tasmota started will show `AirDirection` as hidden until i-See is first used (via IR remote or `HVACSetAirDirection`). Safe default — prevents showing misleading values on units without i-See.
* MiElHVAC Full support of AirDirection control
* MiELHVAC fix globals, VLA, duplicate JSON fields and float formatting
## Summary
- **Remove file-scope globals** (`temp_type`, `remotetemp_active`,
`remotetemp_auto_clear_time`, `remotetemp_last_call_time`,
`remotetemp_half`): moved into `miel_hvac_softc` as `sc_temp_type`
and `sc_remotetemp_*`. All references updated. Eliminates potential
conflict with other drivers sharing the same translation unit.
- **Fix VLA in `miel_hvac_send`**: `char hex_d[(len + 1) * 2]` replaced
with fixed-size `(MIEL_HVAC_DATABUFLEN + 1) * 2`. Variable-length
arrays on the stack are disallowed by `-Wvla` and unreliable on
embedded targets.
- **Make `temp_type` explicit**: helper functions `miel_hvac_deg2temp`,
`miel_hvac_temp2deg`, `miel_hvac_roomtemp2deg` now take `bool
temp_type` as a parameter. Detection of the extended encoding moved
from render functions to `miel_hvac_input_data()`, removing a hidden
side effect inside JSON serialisation code.
- **Remove duplicate JSON fields**: `Purifier`, `NightMode`, `EconoCool`
were emitted twice (from both the settings block and the options
block). Options block now only emits `OptionsHex`.
- **Fix `RemoteTemperatureSensorAutoClearTime` JSON type**: was
serialised as a string (`"10000"`), now a proper JSON number (`10000`).
- **Bool consistency**: assignments `= 1` / `= 0` on `bool` fields
replaced with `= true` / `= false`.
* MiElHVAC auto-enable i-See widevane when setting AirDirection
### Problem
`HVACSetAirDirection` (`indirect` / `direct` / `even`) sets the i-See airflow
direction via a `0x41 0x08` runstate packet (flag `0x2000`). The unit only
honors that value once i-See airflow control is enabled, which requires
`widevane=0x80` sent in a `0x01` settings packet.
Until now the user had to manually run `HVACSetWideVane isee` **before**
`HVACSetAirDirection`, otherwise the direction change was silently ignored.
### Change
`HVACSetAirDirection` now sets the widevane to
`MIEL_HVAC_SETTINGS_WIDEVANE_ISEE` (`0x80`) in the same call.
The dispatcher always sends the settings (`0x01`) packet before the runstate
(`0x41`) packet, so the unit receives the i-See enable first and the direction
value on the following tick — no manual ordering required.
### Notes
- `off` is unchanged (still expressed as `widevane=0x8c` via `0x01`).
- The existing capability guard (`cap_vane_v` + `sc_has_isee`) is unchanged.
Noticed a small inconsistency of the default light WebColor palette not fully matching the light palette example in my_user_config.h and the docs WebUI page. The color for COLOR_BUTTON_OFF was a long time ago changed from dark blue #08405e to the better-working pale blue #a1d9f7 without this being reflected in the defaults used in the absence of another #define value.
Of course, this normally makes no difference as the standard my_user_config.h defines a full dark palette anyway, but still....
New post-build script pio-tools/timestamp-firmware.py copies firmware
artifacts to timestamped filenames when append_timestamp = 1 is set in
the [tasmota] section of platformio_override.ini. Default off; standard
builds unaffected.
The timestamp is extracted from the ELF symbol table via nm: named PROGMEM
symbols mdate_P and mtime_P in GetBuildDateAndTime() carry __DATE__ and
__TIME__ respectively. Reading them from the ELF guarantees the filename
timestamp matches exactly the one the firmware reports at boot. No -g flag required.
Artifacts written with timestamp suffix (e.g. tasmota-4M-2026-05-01T19-02-06):
.bin, .bin.gz, .factory.bin (ESP32) -> build_output/firmware/
.elf -> build_output/firmware/
.map.gz -> build_output/map/
support_rtc.ino: mtime_P introduced as a named PROGMEM variable so nm can
locate __TIME__ by symbol name on all platforms, including ESP32 where
PSTR() is a no-op. Flash impact: +4 B; all other memory unchanged.
* MIELHVAC New features and bug fixes
## Changelog
### Bug Fixes
**Memory leak in `miel_hvac_pre_init()`** — `goto del` jumped past the `free(sc)` label, leaking the allocated struct on serial init failure. Replaced with explicit `delete`/`free`/`return`.
**`remotetemp_clear` semantics inverted** — variable was `true` on boot causing a CLR frame to be sent before any sensor registered. Renamed to `remotetemp_active`, initialised `false`.
**`HVACSetPurify` used wrong map** — `airdirection_map` was passed instead of `purifier_map`.
**`0x08` Set Run State flags wrong byte order** — flags are little-endian on the wire (ref: muart-group/muart-group.github.io#17). Removed `htons()` from all `0x08` flag assignments.
**`widevane_isee` false positives** — values like `0x84`/`0x85`/`0x8c` (ISEE bit + position nibble) incorrectly reported `AirDirection:"even"`. Reverted to exact-match for `0x80`, `0x28`, `0xaa`.
---
### New Features
**`0x42` HVAC Options polling** — added `miel_hvac_data_hvac_options` struct and `MIEL_HVAC_REQUEST_HVAC_OPTIONS` request. The unit is polled for Purifier, NightMode and EconoCool state. Requires short request form (len=1). Results stored in `sc_hvac_options` and published in SENSOR and HVACSETTINGS when `cap_run_state=true`.
**Run State commands (`0x41 0x08`)** — new commands `HVACSetPurify`, `HVACSetNightMode`, `HVACSetEconoCool` and `HVACSetAirDirection` sent via `0x08` on units that report `cap_run_state=true`. Optimistic update to `sc_hvac_options` applied before confirmation.
**EconoCool** — `0x08` byte 14, flag `0x10`, COOL mode only. Command `HVACSetEconoCool on|off`.
**Base Capabilities (`0x5B 0xC9`)** — queried once after connecting. Parsed into `miel_hvac_capabilities`. Results published flat inside `MiElHVAC` as `*Supported` fields. Temperature ranges published as °C. Raw packet in `CapabilitiesHex`.
**Capabilities-aware command validation** — commands blocked when unit reports feature unavailable: modes (heat/dry/fan), temperature range from capabilities, fan speeds, run state commands return `NotSupported` or `ControlNotSupported`.
**`0x42` polling skip** — skipped on units with `cap_run_state=false`, eliminating recurring timeouts.
**`miel_hvac_append_settings_json()`** — shared helper replacing ~80 lines of duplicated JSON code between SENSOR and HVACSETTINGS topics.
---
### JSON Changes
- `Power` (W) and `Energy` (kWh) published inside `MiElHVAC` and also in a separate `ENERGY{}` object outside `MiElHVAC` for Home Assistant auto-discovery
- `Purifier`, `NightMode`, `EconoCool` shown in SENSOR and HVACSETTINGS when `cap_run_state=true`; otherwise omitted
- `AirDirection` always shows current state from `0x62 0x02` regardless of control support
- `*Supported` capability fields published flat inside `MiElHVAC`: `HeatSupported`, `DrySupported`, `FanSupported`, `VaneVSupported`, `SwingSupported`, `AutoFanSupported`, `OutdoorTempSupported`, `AirDirectionSupported`, `PurifierSupported`, `NightModeSupported`, `EconoCoolSupported`
---
### Tested on
MSZ-LN25VG2W — `cap_run_state=false`, temp ranges 16–31 °C cool/auto, 10–31 °C heat, 5 fan speeds.
* Extend AirDirection capability, added web ui energy sensor, cleanup
### AirDirection — respect i-See sensor presence
`AirDirection` now requires three conditions instead of just the vertical vane capability:
- `cap_vane_v` — unit has a vertical vane
- `sc_has_isee` — i-See sensor observed at runtime (widevane ever seen with bit `0x80`, or value `0x28`/`0xaa`)
- `cap_run_state` — unit supports `0x08` for control
Resulting behaviour:
| `cap_vane_v` | `sc_has_isee` | `cap_run_state` | `AirDirection` visible | `AirDirectionSupported` |
|:-:|:-:|:-:|:-:|:-:|
| ❌ | — | — | hidden | `not_supported` |
| ✅ | ❌ | — | hidden | `not_supported` |
| ✅ | ✅ | ❌ | shown | `control_not_supported` |
| ✅ | ✅ | ✅ | shown | `on` |
Units with vertical + horizontal vanes but no i-See sensor now correctly report `AirDirectionSupported:"not_supported"` and omit `AirDirection` from both `MiElHVAC` and `HVACSettings` JSON.
### Capability field renames
For naming consistency across the `*Supported` fields:
- `cap_heat` → `cap_mode_heat`, `HeatSupported` → `ModeHeatSupported`
- `cap_dry` → `cap_mode_dry`, `DrySupported` → `ModeDrySupported`
- `cap_fan_mode` → `cap_mode_fan`, `FanSupported` → `ModeFanSupported`
- `cap_auto_fan` → `cap_fan_auto`, `AutoFanSupported` → `FanAutoSupported`
- `OutdoorTempSupported` → `OutdoorTemperatureSupported`
- `TempCool` / `TempHeat` / `TempAuto` → `SetTemperatureCoolMinMax` / `SetTemperatureHeatMinMax` / `SetTemperatureAutoMinMax`
### Energy values
- `Power` (W) and `Energy` (kWh) published both inside `MiElHVAC` and in a standard Tasmota `ENERGY{}` sub-object (`Power`/`Total`) for Home Assistant auto-discovery.
- Added Web UI rows via `FUNC_WEB_SENSOR` showing instantaneous power (W) and cumulative total (kWh) on the Tasmota main page.
### Runtime i-See detection
New `sc_has_isee` flag in `miel_hvac_softc`, set in `miel_hvac_input_settings` on the first widevane value indicating i-See state. Once set, stays set for the session.
**Practical note:** after boot, the flag starts `false`. Units with i-See that has never been activated since Tasmota started will show `AirDirection` as hidden until i-See is first used (via IR remote or `HVACSetAirDirection`). Safe default — prevents showing misleading values on units without i-See.
* MiElHVAC Full support of AirDirection control
* MiELHVAC fix globals, VLA, duplicate JSON fields and float formatting
## Summary
- **Remove file-scope globals** (`temp_type`, `remotetemp_active`,
`remotetemp_auto_clear_time`, `remotetemp_last_call_time`,
`remotetemp_half`): moved into `miel_hvac_softc` as `sc_temp_type`
and `sc_remotetemp_*`. All references updated. Eliminates potential
conflict with other drivers sharing the same translation unit.
- **Fix VLA in `miel_hvac_send`**: `char hex_d[(len + 1) * 2]` replaced
with fixed-size `(MIEL_HVAC_DATABUFLEN + 1) * 2`. Variable-length
arrays on the stack are disallowed by `-Wvla` and unreliable on
embedded targets.
- **Make `temp_type` explicit**: helper functions `miel_hvac_deg2temp`,
`miel_hvac_temp2deg`, `miel_hvac_roomtemp2deg` now take `bool
temp_type` as a parameter. Detection of the extended encoding moved
from render functions to `miel_hvac_input_data()`, removing a hidden
side effect inside JSON serialisation code.
- **Remove duplicate JSON fields**: `Purifier`, `NightMode`, `EconoCool`
were emitted twice (from both the settings block and the options
block). Options block now only emits `OptionsHex`.
- **Fix `RemoteTemperatureSensorAutoClearTime` JSON type**: was
serialised as a string (`"10000"`), now a proper JSON number (`10000`).
- **Bool consistency**: assignments `= 1` / `= 0` on `bool` fields
replaced with `= true` / `= false`.
* MiEl HVAC: sync SENSOR, HVACSettings and discovery on state change
When settings (power, mode, temp, fan, vane, etc.) are confirmed by
the unit, publish both tele/+/HVACSettings and tele/+/SENSOR so the
two topics stay consistent. Also call TasRediscover() to refresh the
retained tasmota/discovery/+/sensors snapshot used by HA on reconnect.
Apply the same treatment to Options (Purifier, NightMode, EconoCool):
when they change, update both tele/+/SENSOR and tele/+/HVACSettings
in addition to refreshing the discovery snapshot.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>