Commit Graph
2067 Commits
Author SHA1 Message Date
Theo Arends 40ed9e48ee Update change logs 2026-09-27 15:39:13 +02:00
John Murfett b0c8e4f53f DALI-2 push button bindings to control gear targets (DaliBind) (#25073)
* DALI: bind DALI-2 push button events to control gear targets

Add command DaliBind<1..8> <device>,<instance>,<target>,<mode> to link
push button events (IEC 62386-301) from an input device instance to a
control gear target. Modes are Toggle, On, Off, Up and Down; Up and Down
start a fade on button press and stop it on release. The first matching
binding is executed. DaliBind<x> 0 removes a binding and DaliBind alone
lists them all.

Bindings are stored in the DALI settings file, packed as one uint32 per
slot, so they survive a restart.

* Describe how start-up brightness stored differently per short addres vs dali groups
2026-09-27 15:33:18 +02:00
Theo Arends cadd9a5612 Update change logs 2026-09-27 15:24:00 +02:00
Theo Arends e0ee732bac Bump version v15.6.0.2 2026-09-26 17:24:39 +02:00
s-hadinger a8d91f4acb MQTT tolerate delayed PINGRESP (#25067)
* MQTT tolerate delayed PINGRESP

* Add default MQTT_MAX_PING_OUTSTANDING
2026-09-26 16:37:52 +02:00
s-hadinger f151ad8145 Fix Matter commissioning mDNS announcements (#25069) 2026-09-26 15:18:39 +02:00
Theo Arends cba5a24da6 Change command SetOption46 201..255 init wait 1 to 55 seconds instead of 2010 to 2550 msec (#25035) 2026-09-22 16:09:13 +02:00
Theo Arends fbe502840e Update change logs 2026-09-22 15:04:10 +02:00
John MurfettandClaude Fable 5.1 ae2c7f75ae Dali2 input device events (#25029)
* DALI: decode and publish DALI-2 input device event messages

Input devices such as push buttons (IEC 62386-301), occupancy sensors
(303) and light sensors (304) communicate with 24-bit event messages.
Tasmota logged these frames at log level 4 and discarded them, so it
could not react to any DALI-2 switch or sensor on the bus.

Decode the 24-bit frame according to the five IEC 62386-103 addressing
schemes and publish the result for rules and MQTT, for example

  {"DALI":{"Event":"0x82840B","Scheme":"Instance","Type":1,"Instance":1,
           "Info":11,"Name":"LongPressRepeat"}}

Push button events carry the part 301 event name, occupancy events the
Movement/Occupied/Repeat/Sensor flags and light sensor events the
Illuminance value. 24-bit frames with bit 16 set are commands from
another application controller and are ignored.

Tested with a zencontrol zc-switch rotary on an ESP32-S3-Pico with the
Waveshare Pico-DALI2, driving one channel of a DA4-T DT6 driver via
rules.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* DALI: commission DALI-2 control devices and query their instances

Control devices (IEC 62386-103) such as push buttons and sensors are
addressed with 24-bit frames and have their own short address space,
separate from control gear. Until now they could only be commissioned
by hand with raw DaliSend255 frames.

Add DaliDeviceScan 1|2[,<max_count>] which assigns short addresses using
the 24-bit INITIALISE / RANDOMISE / COMPARE / PROGRAM SHORT ADDRESS
sequence. The existing binary search used by DaliScan is shared through
a device flag so control gear commissioning is unchanged. Mode 2 probes
the bus first so addresses already in use are never reassigned.

Add DaliDevice which lists addressed control devices, reports one
device's version, status and instances (type, enabled, event scheme),
and sets the event scheme of all its instances, for example

  {"DaliDevice":{"Device":1,"Version":"2.0","Status":0,"Instances":[
    {"Instance":0,"Type":1,"Name":"PushButton","Enabled":1,"Scheme":"Instance"}]}}

Input devices default to the Instance event scheme which carries no
short address, so DaliDevice <device>,2 switches them to DeviceInstance
events that identify the sending device.

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-22 10:27:23 +02:00
s-hadinger f55fe444d7 MQTT 5.0 with focus on request/response (optional) (#25050) 2026-09-22 10:24:51 +02:00
Theo Arends cde8e62071 Restore default hostname %s functionality using topic name only, regression from v15.4.0.2 (#24731) 2026-09-12 15:15:11 +02:00
Theo Arends a245189808 Fix BLE MI32 unwanted dot/comma replacement (#25009) 2026-09-07 10:46:31 +02:00
s-hadinger 9fac74bac5 Berry rare register allocation bug (#25010) 2026-09-06 13:04:54 +02:00
Theo Arends 143409151c Update change logs 2026-09-05 14:22:44 +02:00
Theo Arends 40c24e193e Update change logs 2026-09-04 11:16:25 +02:00
Norbert RichterandTheo Arends 4b95302553 NeoPool add AuxMode (#24998)
Co-authored-by: Theo Arends <11044339+arendst@users.noreply.github.com>
2026-09-03 17:45:01 +02:00
Theo Arends a0656a911e Update change logs 2026-09-03 15:15:01 +02:00
s-hadinger a3c97ad00a Berry sortedmap support for json.dump (#24999) 2026-09-02 23:07:06 +02:00
Theo Arends 079718527b Add Berry virtual button support 2026-09-02 15:18:49 +02:00
Theo Arends 4b92474794 Update change log 2026-09-01 14:31:10 +02:00
Grzegorz 2b0f5fd650 MiEL HVAC: fix duplicate GPIO name entry, accept fan_only alias (#24992)
* 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.

* Fix GPIO name table offset from duplicate MODBUSRELAY entry

kSensorNames had the four D_MODBUSRELAY_* tokens twice while the
UserSelectablePins enum lists GPIO_MODBUSRELAY_* once, shifting every
following name by four - the GPIO dropdown showed MiEl HVAC MB Tx/Rx/DE
and CC1101 CS as 'MbsRelay ...'. Drop the duplicate row.

* MiEL HVAC accept fan_only as alias for fan mode

Home Assistant's climate integration uses 'fan_only'; map it to the same
value as 'fan' so HVACSetMode/HVACSetHAMode accept it. Reporting is
unchanged (miel_hvac_map_byval still returns 'fan').
2026-09-01 14:26:19 +02:00
Theo Arends b4e24df39c BLE MI32 display icons instead of data lines. disable by removing #define USE_SENSOR_ICON 2026-08-31 13:55:16 +02:00
Theo Arends 89ef175a3c Update change logs 2026-08-30 22:19:45 +02:00
GrzegorzandTheo Arends 2faea5078a Add HVAC control panel to the MiEL HVAC web UI (xdrv_44) (#24984)
Replace the on/off-only main-page presence with a full climate panel and
a live read-only state table, styled to a fixed dark palette scoped
under #hvacp so it is independent of the Tasmota theme.

- FUNC_WEB_ADD_MAIN_BUTTON renders the panel once: mode segment
  (Auto/Heat/Cool/Dry/Fan/Off), target-temperature stepper + slider, and
  Fan speed / Vane vertical / Vane horizontal / Air direction selects;
  which controls appear is gated by the 0x7B 0xC9 Base Capabilities
  (cap_mode_*, cap_vane_v, cap_vane_swing, cap_fan_auto, cap_run_state)
  and miel_hvac_get_fan_count()
- controls call la('&<key>=<value>'); FUNC_WEB_GET_ARG turns each key
  into the matching HVACSet* command (mode via HVACSetHAMode so a mode
  button also powers the unit), reusing the existing validated setters
- FUNC_WEB_SENSOR refreshes the panel in place every web_refresh via the
  <img onerror> exec trick: state-table cells, mode-button highlight and
  every select value, skipped while a change is still in flight
  (update_settings/runstate_pending) or the control is focused
- state table: Room/Set temp, Mode, Fan, Vane V/H, Air Direction,
  Operation, Compressor, Power/Energy; locale decimal separator applied

(cherry picked from commit 6e013484f168f3748f6d388f18ef69f8313fab97)

Co-authored-by: Theo Arends <11044339+arendst@users.noreply.github.com>
2026-08-30 22:17:06 +02:00
Theo Arends 41964bb72d Update change logs 2026-08-30 22:09:59 +02:00
Philipp SchröerandTheo Arends 2f08cd2fc8 Add support for TFA Marbella pool thermometer (#24959)
* Fix GPIO name list falling out of sync without USE_MODBUS_RELAY

The enum in kGpioNiceList guards the four MODBUSRELAY entries with
#ifdef USE_MODBUS_RELAY, the matching name list does not. In any build
without that define - which is the default - the two run four entries
apart, and every GPIO role added after that point inherits a name that
belongs to something else.

Nothing visible went wrong so far because no role after the Modbus block
is currently offered in the selection list. The next one added would have
shown up as "MbsRelay TX".

* Add support for TFA Marbella pool thermometer

Receives the TFA Dostmann Marbella (30.3066.01) floating pool thermometer
on 868 MHz with a CC1101 and reports water temperature, sensor id, rolling
counter and battery state as sensor telemetry.

Packet layout, checksum algorithm and temperature formula are taken from
the rtl_433 project (src/devices/tfa_marbella.c and the reflected reverse
Galois digest in src/bit_util.c), Copyright (C) 2021 Benjamin Larsson,
GPL-2.0-or-later.

Wiring is the shared SPI bus plus two pins that belong to the module:

  SCK / MISO / MOSI -> SPI CLK / SPI MISO / SPI MOSI
  CSN               -> CC1101 CS
  GDO0              -> CC1101 GDO0

CC1101 CS is added as a role of its own, like every other SPI device has
one (LORA_CS, RC522_CS, MCP2515_CS and so on). The three bus lines stay
generic because they are shared - a display and this receiver hang on the
same wires, and only chip select tells them apart. The name string for it
already existed in the language files, unused.

Built on RadioLib, which already ships with Tasmota and drives the CC1101
on both ESP8266 and ESP32. The older lib/lib_rf/cc1101 declares
"platforms": "espressif8266" for a reason: on ESP32 it collides with the
CC1101 class RadioLib itself declares.

Three settings are not obvious and were established against hardware:

  - The bit rate is 9.5238 kBit/s, not 9.6. rtl_433 documents a symbol
    width of 105 us, and the 0.8 percent difference accumulates to about
    0.7 bit over an 88 bit frame - enough that the first bytes arrive
    intact and the last ones come out shifted.
  - The sync word is 0xAAD2, although the frame itself begins with
    AA 2D D4. The radio matches in hardware and strips what it matched,
    so the two values describe the same transition from different sides.
  - disableAddressFiltering() is required. RadioLib leaves address
    checking on, and the chip then discards every frame whose first byte
    does not match the node address - which for this sensor is always
    0xAA. Without it reception runs and nothing ever arrives.

SPI is set up by the driver rather than left to RadioLib, whose ArduinoHal
calls SPI.begin() without arguments - on ESP32 that picks the default bus
pins and ignores the GPIO template, the same reason the LoRa drivers do it
themselves.

Reception follows the split the LoRa drivers use: the interrupt handler
sets a flag and nothing else, the packet is read in the main loop. The
flag is cleared after reception is restarted, not before, because
startReceive() puts GDO0 back to its idle level and that is an edge in
itself - clearing first makes the driver re-trigger on its own restart and
read the same stale buffer thousands of times per second. Both edges are
used: the pin does not reliably return to idle after a packet.

The driver binds to the first sensor with a valid checksum and ignores
every other one afterwards, so a neighbour's identical sensor cannot feed
readings into the same value. Marbella <id> rebinds, Marbella 0 relearns;
anything that is not a valid 24 bit id is refused rather than silently
turned into 0, which would look like a successful bind while clearing the
binding.

After TFA_MARBELLA_TIMEOUT seconds without a packet the reading disappears
from both the JSON payload and the web interface. A stale value looks
exactly like a measurement that simply does not change, which is the one
failure mode this driver must not produce.

No plausibility range is applied to the temperature. A range check of
0..50 degrees, as sometimes used in place of a checksum, would wrongly
discard a real reading of 51 degrees.

Verified against the sensor: frames decode with matching checksums, a
disturbed frame is rejected by the checksum, and the rolling counter
advances without gaps over consecutive receptions.

* Add SetOption166 to publish TFA Marbella readings on reception

Off by default, so readings go out with the regular TelePeriod telemetry
like those of every other sensor - a driver should not quietly behave
differently from the rest.

Worth turning on for this sensor: it transmits about once a minute, so
with the default TelePeriod of 300 seconds four out of five readings never
leave the device, and the one that does can be almost five minutes old.
With the option set, every reading becomes an MQTT message - useful for
rules, and more traffic on a busy broker, which is why it is a choice
rather than the default.

The bit is taken from the spare range of the SetOption146..177 block. If
the number collides with something merged in the meantime, it can be moved
without touching the driver.

* Add TFA Marbella build configuration

USE_TFA_MARBELLA is off by default and adds 12k6 of code on ESP8266 and
5k7 on ESP32, most of it RadioLib where it is not already in the build.
It sits with the other SPI peripherals because it needs USE_SPI: without
that, the CC1101 CS role cannot be assigned at all and the driver would be
silently dead.

USE_TFA_MARBELLA and USE_KEELOQ cannot be combined: both drive the same
CC1101, one transmitting on 433 MHz and one receiving on 868 MHz.
Whichever configured the chip last would win silently, so the combination
fails at compile time instead.

* Show the age of the last TFA Marbella reading in the web UI

The reading is still dropped from JSON after TFA_MARBELLA_TIMEOUT, so a
stale value never reaches MQTT. The web interface, however, showed nothing
at all in that case - and an empty page cannot be told apart from a driver
that is not running, a radio that never starts, or a sensor whose battery
died. Those need different answers.

The web interface now always shows a line: the age of the last packet
("12 sec ago"), or "no signal, last 4 Min ago" once the reading expired,
or "no signal yet" when nothing was ever received.

The age is shown rather than a timestamp: the sensor sends once a minute,
so what matters is whether the last packet is seconds or hours old - and a
clock time would be wrong anyway while the device has no NTP yet.

* Use SetOption147 for TFA Marbella and correct the radio parameters

Review feedback: drop the newly added SetOption166 and share the existing
SetOption147 instead. Readings are now published as they arrive, which is
what the sensor needs - it transmits once a minute while TelePeriod defaults
to 300 seconds - and SetOption147 suppresses that, the same handle the serial
bridge, IR, LoRaWAN and WizMote drivers already use. flag6 bit 20 goes back
to being spare.

Two radio parameters were wrong. Both came from the rtl_433 device
description and were never verified against a real sensor:

- Bit rate 9.4 kBit/s, not the 9523.8 Bd that rtl_433's 105 us per symbol
  implies. Measured from three SDR recordings by decoding each at every
  sampling phase and letting the sensor's own checksum decide: all three
  peak at 9400 Bd, and none yields a single valid frame at 9523.8 Bd. Over
  the 112 bits of a transmission a 1.3 % rate error drifts past a full bit,
  so the tail of every frame arrives shifted - which is why 9.6 kBit/s
  delivered nothing at all and 9.5238 only worked occasionally.
- Deviation 34.5 kHz, not 30, from the same recordings.

New setting: "Marbella <MHz>" tunes the receiver and is stored in
Settings->marbella_frequency (int16, 100 Hz steps, taken from the filler
after marbella_serial). The CC1101 derives its frequency from a 26 MHz
crystal, and one module measured here sat 27 kHz below what it was told.
That fits inside the 135 kHz receive filter but spends the margin the signal
needs, and it is a property of the board rather than of the driver - so it
is a setting instead of a constant, and nobody needs a private build for it.
A decimal point tells a frequency from a sensor id; ids now have to be the
full six hex digits, so "Marbella 868" is refused rather than silently
binding to sensor 000868.

Also documented at the sync word why 0xAAD2 is the right value: the sensor
sends an alternating preamble, then a single 0xD2, then the frame - so the
16 bits before AA 2D D4 read AA D2, which is exactly what the radio matches.
That was previously noted as unexplained.

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

* Update tasmota_types.h

Use union

---------

Co-authored-by: Theo Arends <11044339+arendst@users.noreply.github.com>
2026-08-30 21:56:29 +02:00
Theo Arends 9aa935c231 Update change logs 2026-08-30 21:41:19 +02:00
GrzegorzandTheo Arends 46794883aa Add Modbus RTU slave to MiEL HVAC driver (xdrv_44) (#24982)
* 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.

* Add Modbus RTU slave to MiEL HVAC driver (xdrv_44)

Optional second RS485 serial port that exposes every MiEL HVAC state as
Modbus read registers and maps every driver function to write
registers/coils, so the unit can be driven directly from a PLC.

- ESP32 only, opt-in via USE_MIEL_HVAC_MODBUS_SLAVE
- new GPIOs: MiEl HVAC MB Tx / Rx / DE (RS485 direction, optional)
- persisted config (address, baudrate, serial config) reclaimed from
  Settings free_eb0 + SBitfield1 spare bit; no settings version bump
- function codes 0x01-0x06, 0x0F, 0x10 with CRC-16, RTU inter-frame
  gap framing in FUNC_LOOP, and Modbus exception replies
- input registers (FC04) mirror settings, room/outdoor temp, timers,
  status/energy, stage, capabilities; holding registers/coils drive
  power, mode, temp, fan, vane, widevane, airdirection, prohibit,
  purifier, night mode, econocool, remote temp and clear time
- writes reuse new miel_hvac_apply_* typed setters (same capability
  gating as the console commands, which are unchanged)
- commands HVACModbus / HVACModbusAddress / HVACModbusBaudrate /
  HVACModbusConfig (all live), plus a "Modbus" block in SENSOR JSON

* Update tasmota_template.h

Fix duplicate GPIOs

* Update tasmota_types.h

Use union for parameters

---------

Co-authored-by: Theo Arends <11044339+arendst@users.noreply.github.com>
2026-08-30 21:31:05 +02:00
Theo Arends 44a6526523 Update change logs 2026-08-28 11:09:31 +02:00
Theo Arends d3cadb4cd7 Bump version v15.6.0.1 2026-08-25 11:38:08 +02:00
Theo Arends 8ce646231a Update change logs 2026-08-25 10:42:40 +02:00
Theo Arends 5dfbd59be9 Update meta data 2026-08-24 11:42:43 +02:00
Theo Arends 3c184e43d9 Update change logs 2026-08-24 09:56:04 +02:00
Rene Hexel c988ddfc2f Add Shelly emulation for ESP32 (#24952)
* Add Shelly emulation for ESP32 (EMUL_SHELLY)

Presents Tasmota as a generation 2 Shelly Pro 2PM to controllers
speaking the Shelly Gen2 RPC protocol, such as the Sigenergy
mySigen application.

The emulation follows Belkin WeMo and Hue Bridge: compiled in
with USE_EMULATION_SHELLY (ESP32 only, as it needs the IDF mDNS
component) and selected at runtime with `Emulation 3` or on the
Configure Other web page, where it appears as "Shelly single
device".

When selected, the driver advertises _shelly._tcp over mDNS with
the IPv4 and IPv6 addresses of all active interfaces and serves
/shelly and /rpc. Up to two switch, two input and two metering
channels are mapped onto Tasmota relays, switches and the energy
driver. Channels beyond the relays configured keep their state
in NVS so a controller still sees two working outputs.

Shelly controllers discard mDNS replies that do not carry the
RFC 6762 TTL of 255, which Espressif's mDNS component only sets
on multicast, so the driver raises the TTL on the mDNS sockets
after registration.

* Make Shelly emulation opt-in and renumber driver
2026-08-21 11:50:19 +02:00
Theo Arends 9978459714 Update change logs 2026-08-18 11:47:31 +02:00
Norbert Richter 168ec0de54 NeoPool delocalize all JSON outputs (#24965) 2026-08-18 11:10:34 +02:00
Theo Arends 63e278de74 Update change logs 2026-08-17 11:01:49 +02:00
Norbert Richter 1d682c823a NeoPool fix issue with localized JSON key (#24962) 2026-08-17 10:56:18 +02:00
Theo Arends 4999f2bd5e Update change logs 2026-08-13 14:02:12 +02:00
s-hadinger db56cd62aa Berry extend sortedmap constructor (#24955) 2026-08-12 17:02:22 +02:00
s-hadinger 650d525699 Berry json.dump() works with subclasses of map and list (#24954) 2026-08-12 15:41:17 +02:00
Theo Arends 58907cf579 Update change logs 2026-08-10 14:22:15 +02:00
s-hadinger 7e2157d591 HASPmota better support for textarea (#24946) 2026-08-08 17:10:40 +02:00
Theo Arends bb8ce1773e Add limited support for BLE BTHome (#20763) 2026-08-06 14:56:48 +02:00
Theo Arends 49f9be888d Add support for WiZ compatible IoTorero ESP-Now Remote Control additional buttons P5 to P7 2026-08-01 16:36:36 +02:00
Theo Arends 640b6c2d10 Update changelogs 2026-07-31 17:10:00 +02:00
s-hadinger b6590fef1c TLS fix public key fingerprint for ECDSA certificates (#24928) 2026-07-31 17:06:26 +02:00
s-hadinger fabc35f034 Minor fixes in LList (#24927) 2026-07-31 16:15:03 +02:00
Theo Arends 9c5b81f1d4 Update change logs 2026-07-31 16:12:32 +02:00
Theo Arends 63c2a3b466 Add support for baudrate 74880 replacing 74700 ((#24924) 2026-07-31 15:33:27 +02:00