* Matter: add Electrical Power Measurement cluster to On/Off Plug-in Unit
Adds a new "relay_power" plugin type (Matter_Plugin_OnOff_Power) that extends
the existing relay/OnOff Plug-in Unit (0x010A) with the Electrical Power
Measurement cluster (0x0090), reporting Voltage, Current, ActivePower,
ApparentPower, ReactivePower, Frequency and PowerFactor from Tasmota's ENERGY
sensor JSON, so Matter controllers can display live power usage for smart
plugs with energy monitoring.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* Matter: add v_relay_power virtual variant for Electrical Power Measurement
Adds Matter_Plugin_Virt_OnOff_Power ("v_relay_power") so the new
ElectricalPowerMeasurement-enabled plug can be exercised without physical
hardware, driven entirely via `MtrUpdate` (e.g. Voltage/Current/ActivePower/
ApparentPower/ReactivePower/Factor/Frequency).
Also guards Matter_Plugin_OnOff_Power.parse_sensors() against VIRTUAL
instances (it must not read the local ENERGY sensor) and declares
UPDATE_COMMANDS so MtrUpdate accepts the new measurement keys. The wattage
key is named `ActivePower` rather than `Power` since `Power` is already the
inherited On/Off boolean command.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* Matter: register new plugin headers in be_matter_module.c
be_matter_module.c maintains an explicit, hand-written list of solidified
Berry header includes (no auto-discovery). The new OnOff_Power and
Virt_OnOff_Power plugin files were missing from that list, so their classes
were never defined, causing the "undeclared" build failures reported by CI
(be_class_Matter_Plugin_OnOff_Power / be_class_Matter_Plugin_Virt_OnOff_Power)
even though matter.plugins_classes referenced them by name.
* Matter: add http_relay_power bridge variant for Electrical Power Measurement
Adds Matter_Plugin_Bridge_OnOff_Power ("http_relay_power") so the
ElectricalPowerMeasurement-enabled plug can also be bridged from a remote
Tasmota device over HTTP (polling `Status 10`), completing the local/virtual/
bridge trio already used by other plugins (relay, relay_power now included).
Refactors the ENERGY-object parsing in Matter_Plugin_OnOff_Power into a
shared `_parse_energy()` helper used by both `parse_sensors()` (local device)
and the new `parse_status()` override (bridge, index 10 / Status 10), and
guards `parse_sensors()` against BRIDGE instances so a bridge endpoint never
reads the local gateway's own ENERGY sensor by mistake.
Registers the new solidified header in be_matter_module.c (missed this for
the previous two plugin files, which broke CI) and adds http_relay_power to
the bridge type dropdown in Matter_UI.be.
Verified with a full PlatformIO build for tasmota32s3 (SUCCESS).
* Matter: auto-detect relay_power on remote bridge devices with an ENERGY sensor
The "Add Remote sensor or device" auto-detection always suggested plain
light0/relay for every detected relay channel, even when the remote device
reports an ENERGY sensor (e.g. a NOUS A5T 4-outlet power strip with a single
shared CSE7766 meter) - so relay+power devices showed up as plain "Light 0
OnOff" with no way to pick relay_power from that screen (the type column is
static text for already-detected rows, only editable for a brand new blank
row).
generate_config_from_status() now tags the first detected relay as
relay_power when ENERGY is present, leaving the rest as plain relays: most
Tasmota energy-monitoring boards have a single meter for the whole device,
not one per relay, so duplicating the cluster across every channel would
misleadingly show the same aggregate reading on each endpoint.
* Matter: compose Electrical Sensor device type onto the power-monitoring plug
Adds device type 0x0510 (Electrical Sensor) alongside 0x010A (On/Off
Plug-in Unit) on the same endpoint for relay_power/v_relay_power/
http_relay_power, plus the mandatory Power Topology cluster (0x009C) with
the NODE feature (this single ENERGY sensor meters the whole device, not a
subset of endpoints - matches Tasmota's non-indexed ENERGY object).
Matches the Matter 1.4.1 Device Library requirement that Electrical Power
Measurement lives on an Electrical Sensor device type, while keeping the
cluster on the plug's own endpoint (composed DeviceTypeList) rather than a
separate endpoint, since controllers only recognize the measurement
capability when the Electrical Sensor device type is actually declared.
Verified on real ESP32-S3 hardware via chip-tool: endpoint 2 now reports
DeviceTypeList = [266 (On/Off Plug-in Unit), 1296/0x0510 (Electrical
Sensor), 19 (Bridged Node)], and Power Topology FeatureMap = 1 (NODE).
---------
Co-authored-by: Ludovic BOUÉ <938089+lboue@users.noreply.github.com>
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
* 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
Two independent reliability fixes for the MFRC522 driver, found while
debugging a real device:
- LoadAvg climbing toward its ceiling: idle polling every 250ms was
hitting the reader's own ~25ms internal REQA timeout every time
(PCD_CommunicateWithPICC busy-waits synchronously with no yield()
until TReloadReg's on-chip timer fires), i.e. a permanent ~10% duty
cycle from polling alone. Throttles idle polling to roughly once a
second and shortens the on-chip poll timeout via TReloadReg
(RC522ShortenPollTimeout, ~2.5ms instead of 25ms - still well above
a real tag's few-hundred-microsecond ISO14443A response time), an
order of magnitude cheaper without hurting detection reliability.
Also added exponential backoff as a safety net if a poll is ever
genuinely slow (e.g. RF noise triggering the anticollision path).
- Reader silently stopping card detection after a few reads, only
recoverable with a manual reset: traced to MIFARE_Authenticate
(used by USE_RC522_DATA_FUNCTION) occasionally leaving the crypto1
cipher engine stuck engaged, which garbles all further card
communication even though simple register reads keep succeeding.
Added a 1s watchdog that checks two registers that reveal this
broken-but-alive state - Status2Reg's MFCrypto1On bit and
TxControlReg's antenna-field bits - and does a full PCD_Reset()+
PCD_Init() the moment either looks wrong, rather than waiting for a
user to notice and run RC522Reset. Toggle with RC522Watchdog 0/1
(on by default); new RC522Status field, "Healthy", reports the same
check on demand.
Also stopped calling PCD_PerformSelfTest() at init - it resets the
chip and writes test patterns into several registers, needing a full
re-init to undo (the library's own docs note "self-test blows init").
A plain VersionReg read is already a reliable presence check without
that risk.
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Decode the 13-byte body composition record the scale broadcasts as
service data for UUID 0x181B (also sent as 0x2A9C notifications), and
register the scale as MI32 device type MIBCS with fake product ID 0x181b.
Per weighing session the driver publishes the weight once when the
scale's stable flag is set and the impedance once when the impedance
stable flag is set (repeating the weight so the message is self
contained). If the user steps off before a usable impedance arrives,
ImpedanceFailed:1 is published instead. A session ends on the finished
flag or after 10 s of silence, so repeated broadcasts of the final
record do not produce duplicate messages. Overload readings (0xFFF0)
and the impedance sentinels 0xFFFE/0xFFFD are ignored. The unit (kg,
lb, jin) is derived from the flag bits and published alongside.
Web UI: weight and impedance rows on the main page; with USE_MI_EXT_GUI
a dashboard tile with a 24 h weight history graph (kept in kg).
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
* MiELHVAC: trim the state table to what the controls do not show, add Error
Set Temp, Mode, Fan, Vane V / H, Air Direction and Prohibit are dropped
from the read-only state table below the panel controls, they duplicated
the selector directly above each of them. Room Temp stays first, and an
Error row is added last, showing the same packed-BCD code SENSOR's
ErrorCode carries, with the spec's 8000 no-error code shown as "no"
instead of the number.
Also swapped the remaining semicolons in the driver's head doc block and
the state-table function comment for commas, per house style.
* MiELHVAC: add Timer On/Off to the state table, split Compressor and Frequency
Timer On and Timer Off (0x62 0x05, read-only) show as the last two rows of
the state table, the clock time each side fires at, the unit only reports
minutes remaining, so this adds Tasmota's local clock.
Compressor now shows on/off and Frequency (Hz) is its own row right after
it, instead of one row showing only the frequency.
* 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>
* 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').
* MiEL HVAC Modbus: mirror sensor values on FC03 holding registers
Add holding registers 0x0010..0x0017 as read-only mirrors of room
temperature, compressor, instantaneous power, stage operation/fan/mode
and the requests/CRC-error diagnostic counters, so a master that only
speaks FC03 can read them. Writes to these addresses still return
exception 0x02.
* MiEL HVAC Modbus: start FC03 mirror block at 0x000f, no gap after control block
* MiEL HVAC Modbus: queue writes when HVAC link is not up yet
Return to the master with a normal response and queue the update
(same as the HVACSet* console commands) instead of Modbus exception
0x04, so a PLC can pre-load settings and bench testing works without
the AC connected.
* MiEL HVAC Modbus: frame incoming requests by length, not the T3.5 gap
A FUNC_LOOP poll cannot measure the 3.5-char inter-frame gap reliably
above 9600 baud, so a frame split across two polls was processed as two
truncated frames (CRC error count exceeded the request count on a busy
19200 8E1 bus). Compute the expected request length from the function
code and dispatch as soon as it is buffered; keep the idle gap only to
flush a stuck partial frame or retry an unknown function code. Also skip
requests addressed to other slaves without a CRC check.
* MiEL HVAC Modbus: byte-level resync, larger RX buffer, no flush on auto-direction
CrcErrors was still ~1:1 with Requests on a busy 19200 8E1 multi-drop bus.
- resync one byte at a time on a framing/CRC mismatch instead of dropping
the whole assumed frame and waiting for bus idle
- only count a CRC error when the frame claims our own address
- 1 kB RX buffer so a late FUNC_LOOP poll does not lose bytes to a UART
FIFO overflow (TasmotaSerial cannot report the overflow on ESP32)
- bulk serial read instead of byte-at-a-time
- skip flush() when no DE pin is configured - on ESP32 flush() also
discards the RX buffer, dropping the next request on a shared bus
* MiEL HVAC Modbus: add link status to the FC03 sensor mirror
Holding register 0x0017 (PLC 40024) mirrors input 0x0001 - connected to
the HVAC unit (0/1).
* MiEL HVAC Modbus: wait the RTU turnaround silence before answering
Intermittent corrupted responses (master saw e.g. 80 C8 ...) on an
auto-direction transceiver at 19200: the slave started transmitting
before the master's line had turned around. Hold quiet for up to 3.5
char times after the last received byte before sending the response.
Track the last-byte time per read so the wait is accurate.
* MiEL HVAC Modbus: do not send a late reply once the master has re-polled
When a FUNC_LOOP stall makes the response miss the master's timeout
(Siemens S7 MB_MASTER STATUS 0x80C8), a newer request is already
buffered behind the stale one. Apply the stale request but skip its
response so it cannot land on the master's next transaction and make
that one time out too.
* MiEL HVAC Modbus: strip the i-See bit from the swingH register
Input 0x0015 / holding 0x0005 (40006) returned the raw wide-vane byte,
so a unit with an i-See sensor reported e.g. 131 (0x83 = 0x80 | center).
Report the position only (1..8, 12), or 0x80 when the byte encodes
i-See direction mode - matching the SwingH field in the SENSOR JSON.
* MiEL HVAC: document the driver and Modbus slave in the file header
Move the Modbus RTU slave detail out of the changelog into a doc block
at the head of xdrv_44_miel_hvac.ino, per the PR #24993 review.
* MiELHVAC: add Home Assistant MQTT discovery
Publishes a single retained "climate" entity covering mode, current/target
temperature, fan speed, action, vertical swing, horizontal swing/widevane
and air direction (as a preset), the same layout as a manually written
climate: YAML config, so the device appears in Home Assistant without any
manual configuration. Republishes on every MQTT (re)connect and honors
SetOption19 like the rest of Tasmota's HA discovery.
Fan speed, both swing controls and air direction use Jinja dict templates
to show capitalized display labels (e.g. "Up Middle") while still sending
the driver's own lower_snake_case values over MQTT. The web control panel's
own labels are updated to match, it previously used "Up-Middle" style
hyphens for these, inconsistent with the discovery config.
Widevane's "I-See" position is a read-back of air direction control being
active, not a directly settable vane position, so it's only listed once
the unit is known to support it, and selecting it now re-engages air
direction at the last active direction instead of sending a widevane byte
the unit won't act on. The same option and behavior are now available in
the web control panel, which previously hid "I-See" unconditionally.
Also adds a computed "HAAction" SENSOR field (heating/cooling/drying/fan/
idle/defrosting/preheating/off), using the Stage packet's OperationStage
(standby/defrost/preheat) as well as HAMode, so the discovery config
doesn't need to derive it with a template and HA gets the more specific
action whenever the unit reports one.
The combined discovery payload does not fit under Tasmota's default
MQTT_MAX_PACKET_SIZE (1200 bytes), so my_user_config.h raises it to 4096.
* MiELHVAC: expose HA discovery capabilities dynamically
Build fan_modes from cap_fan_auto and the C9-reported fan count instead
of a fixed list, drop Quiet from MQTT since some CN105 units report an
IR-selected Quiet back as fan speed 1, and gate the AirDirection preset
on isee_capable so it is only advertised on units that actually support
it. Republish discovery once C9 capabilities and i-See are known, not
just on MQTT connect, since TasRediscover only re-runs Tasmota's native
discovery and never this driver's custom HA payload.
Based on a suggestion from mamrai1 in grzegorz914/Tasmota#2.
* MiELHVAC: also hide Quiet in the web panel fan speed selector
The web control panel's Fan speed dropdown still offered Quiet whenever
the unit reported 5 fan speeds. Since some CN105 units report an
IR-selected Quiet back as fan speed 1, hide it unconditionally there
too, matching the HA discovery fan_modes list.
* MiELHVAC: make web panel and MQTT discovery use the same capability gates
The web panel's AirDirection control, its widevane I-See option, the
readout row and the SENSOR JSON AirDirection field all defaulted to
shown until capabilities proved otherwise, while HA discovery's
preset_modes/isee_capable gate defaulted to hidden until proven
supported. Standardize all of them on the stricter confirmed-capable
gate so MQTT and the web panel never disagree about whether AirDirection
is available.
Also cap the web panel's Fan speed 2 and 3 options to the C9-reported
fan count, matching the numbered-speed cap already applied to the HA
discovery fan_modes list (only speed 4 was capped there before).
* MiELHVAC: order horizontal vane positions left-to-right
swing_horizontal_modes (HA discovery) and the web panel's Vane
horizontal select listed positions in raw protocol byte order
(left, left_middle, center, right_middle, right, left_center,
right_center), which reads oddly in a dropdown. Reorder both, and
the backing miel_hvac_widevane_map, to the physical left-to-right
order: left, left_middle, left_center, center, right_center,
right_middle, right, then split/swing/isee.
* MiELHVAC: restore capability-gated Quiet instead of removing it outright
Unconditionally dropping Quiet from fan_modes assumed CN105 units
universally can't distinguish a Quiet selection from fan speed 1 on
readback. Testing on a real unit that reports 5 fan speeds (Auto,
Quiet, 1-4) showed Quiet works correctly and reports back reliably,
so removing it lost a real, working mode.
Gate Quiet on fan_count == 5 instead, the same threshold SetFanSpeed
already enforces when rejecting the command -- both HA discovery's
fan_modes/fan_mode_state_template/fan_mode_command_template and the
web panel's Fan speed selector now only expose Quiet once the C9
capabilities response confirms the unit actually has it.
* MiELHVAC: add Home Assistant MQTT discovery for Prohibit
Publish the Prohibit / remote-control lock as a separate select entity
(homeassistant/select/<id>_prohibit/config), mirroring the web panel's
Prohibit dropdown -- climate entities have no native lockout concept.
Clear its retained config alongside the climate entity's when SetOption19
disables discovery.
Based on a suggestion from mamrai1 in grzegorz914/Tasmota#2.
* MiELHVAC: document why Prohibit's entity_category hides it from Area cards
Note in the code that entity_category "config" is a deliberate choice,
not an oversight -- HA excludes config/diagnostic entities from the
Area dashboard's auto-generated card even once an Area is assigned,
by design. The entity stays reachable from the device page and
Settings > Areas.
* MiELHVAC: document confirmed dual-vane widevane behavior, no renames
Tested against a real MSZ-LN unit: left/center/right/split/swing move
both horizontal vanes together, while the *_middle/*_center values
move only one vane independently. Considered relabeling raw 4/5 and
6/7 based on mUART's protocol docs (which describe a single continuous
left-right axis plus generic "Split" combinations), but that scheme
does not match this unit's actual behavior, and there is no capability
bit indicating vane count or per-side behavior to detect which scheme
applies on a given unit. Document the confirmed behavior as
model-specific instead of renaming, to avoid baking in an assumption
that breaks a different model.
* MiELHVAC: swap right/right_middle labels to match actual vane extremes
Confirmed by testing: WIDEVANE_RR (0x05) drives the right vane to its
furthest-right extreme (single vane only), while WIDEVANE_R (0x04)
moves both vanes together to a lesser right-ward position. The
previous naming had this backwards -- "right" pointed at the lesser,
synchronized position and "right_middle" at the extreme one.
Swap the two labels in miel_hvac_widevane_map so "right" means the
same thing intuitively on both sides: the furthest position, matching
how "left" already refers to the full-left extreme. HA discovery and
the web panel need no changes -- they key off these string names, not
the raw byte values, so they pick up the corrected mapping
automatically.
* MiELHVAC: fix web panel never showing I-See as the current Vane horizontal
The web panel's "Vane horizontal" selector passed set->widevane masked
with MIEL_HVAC_SETTINGS_WIDEVANE_MASK (0x0f) as the current value, but
i-See is signaled by the high bit (0x80) or one of two odd raw values
(0x28/0xaa) seen in the wild -- none of which survive that mask. The
dropdown could therefore never show I-See as selected, even right
after engaging AirDirection (which sets widevane to exactly 0x80).
Compute the same wv_isee check already used correctly elsewhere in
this function (readout rows, Air Direction control) before masking,
and share it between the Vane horizontal selector and the Air
Direction block below it.
* MiELHVAC: fix live-sync never updating Vane horizontal when I-See engages
Same class of bug as the earlier "Vane horizontal never shows I-See as
selected" fix, in a different spot: the JS live-sync path in
miel_hvac_web_readout() (which pushes S('hvh', ...) updates into the
already-rendered web panel without a full reload, skipping controls
the user has focused) computed wn as NULL whenever i-See was active,
so the S('hvh', ...) call was skipped entirely and the selector was
left showing its last non-i-See value instead of updating live.
* MiELHVAC: gate MQTT_MAX_PACKET_SIZE bump on USE_MIEL_HVAC
Raising MQTT_MAX_PACKET_SIZE to 4096 in my_user_config.h applied to
every build regardless of whether MiELHVAC was even enabled, costing
every other user extra RAM for a buffer only this driver's Home
Assistant discovery payload needs. Wrap it in #ifdef USE_MIEL_HVAC,
placed after the user_config_override.h include so the driver's own
enable flag is already visible, keeping the 1200 byte default for
everyone else.
Addresses maintainer review feedback from arendst on PR #25027.
* Update xdrv_44_miel_hvac.ino
* MiELHVAC: document confirmed vaneH split/sync behavior precisely
Update the widevane_map comment with the fully confirmed picture from
testing: left/left_middle/center/right_middle/right/split/swing move
both vanes together to that position, while left_center and
right_center are asymmetric splits (left_center puts the left vane at
the left_middle angle and the right vane at center, right_center
mirrors that). Earlier revisions of this comment described
left_middle/left_center as left-vane-only, which testing has now
superseded.
Also notes a second real unit (from a PR reviewer) showed yet another
variant, reinforcing that this is model-specific.
* MiELHVAC: no semicolons in prose comments
Replace two semicolons in the widevane_map doc comment, one with a
comma (continuing a single flowing clause), one with a period (a true
independent-clause boundary).
* MiELHVAC: fix cap_run_state gating, confirm Purifier/NightMode/EconoCool per option
cap_run_state (0xC9 byte 9 bit 0x10) turned out to be undocumented and
unreliable per mUART's own docs ("???", observed both true and false),
and this file's own struct comment even pointed at the wrong byte (6
instead of 9) for it. It was wrongly used to block the HVACSetPurify/
HVACSetNightMode/HVACSetEconoCool commands, the matching web panel
controls, and 0x42 polling itself outright, hiding these features even
on units that do support them.
Removed all cap_run_state-based gating. Purifier/NightMode/EconoCool
are now only reported once independently proven via a round trip: a Set
Run State request that actually asks for a different value than last
confirmed is armed, and the option is marked confirmed only if the next
genuine 0x42 response reads back exactly that requested value (not all
units that answer 0x42 support all three, so a single shared confirmed
flag was too coarse). No optimistic apply — these fields reflect the
unit's own confirmed state only, never what was just requested, since
on units where 0x42 never responds an optimistic guess would never get
corrected. The web panel only shows a button once its option is
confirmed; *Supported (capabilities) is the single source of truth for
support status, so the Settings block doesn't repeat "not_supported".
Also adds a debug-only HVACProbeRunState command (MIEL_HVAC_DEBUG-gated)
for sending an arbitrary flags/byte/value combination on the 0x08 Set
Run State surface, to correlate a raw packet with a physically observed
effect on real hardware rather than a 0x42 read-back that may never
arrive.
Verified against a real MSZ-LN unit whose 0xC9 fingerprint (byte 6/9 =
0x07/0x25) independently matches the "non-supporting" signature reported
in https://github.com/echavet/MitsubishiCN105ESPHome/discussions/366 —
0x42 never responds, Set Run State commands are ACKed but produce no
observable effect (including a buzzer test that works on other units),
and toggling these options via the physical remote does not leak into
any other polled response type either.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* Update xdrv_44_miel_hvac.ino
Added a sensor for horizontal wide-vane support.
Horizontal wide-vane support is assumed to be available by default. If a requested horizontal vane position change is rejected by the unit, support is marked as unavailable and the horizontal wide-vane controls are hidden.
* MiELHVAC: add Home Assistant MQTT discovery for Purifier/NightMode/EconoCool
Publishes each as a separate switch entity, climate has no slot for
arbitrary on/off toggles. Each is only published once its own
round-trip-confirmed flag is set (sc_purifier_confirmed et al), matching
the SENSOR JSON and the web panel, both of which already gate on these
per-feature flags instead of the unreliable cap_run_state bit. Cleared
along with the climate entity and Prohibit select when SetOption19
disables discovery.
* MiELHVAC: document that SwingV only reports the right vertical vane
On dual-vertical-vane units (confirmed on MSZ-LN, remote has separate
Left/Right vertical vane buttons) the vane byte tracks only the right
vane. Confirmed by testing: setting left=up/right=down then
left=down/right=up gave the same final vane byte both times (matches
the right vane, not a combined state), changing only the right vane
moved the byte, changing only the left vane three times in a row left
every byte of the Settings packet unchanged. No other field in the
packet exposes the left vane either, this is the same class of gap as
Purifier/NightMode/EconoCool being settable from the remote but
unreported over CN105.
Documentation only, no behavior change.
---------
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Co-authored-by: mamrai1 <57288344+mamrai1@users.noreply.github.com>
On ESP32-S3, C3 and C6 the command console falls back to UART0 when no
USB host is detected at boot. Those SoCs expose UART0 on ordinary GPIOs
(S3 43/44, C3/C6 20/21), so a template can assign them to another
function. UART0 then keeps driving those pads and silently corrupts
whatever the template put there.
Found on a KinCony AG8, where GPIO43 and GPIO44 are the MOSI and MISO of
the W5500. The ethernet chip is detected, since esp_eth_driver_install
reads its version register over SPI and succeeds, but the link never
comes up. Plugging a USB cable moves the console to CDC and the link
closes in 2.1 seconds, which isolates the console as the only variable.
GpioInit now releases the console with Serial.end() in the window after
SetPin has built the pin map and before SPI is configured, instead of
letting UART0 keep pads the template already owns. SerialLog 0 is not a
workaround: it only silences logging without freeing the pins, and
Serial.begin runs before the settings are loaded.
Without this, threshold = usage * 2 (default steprate) lets PSRAM
heaps grow unbounded until a GC sweeps tens of thousands of objects
out of a nearly-full pool in one go, costing multiple seconds
(observed: 4.4s freeing 25k objects from a 2.6MB heap). Cap growth to
fixed increments instead, same as the non-PSRAM case, just scaled up
for the bigger heap.
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
- Fix regression from PR #25024, which accidentally broke the layout and rendering of the WiFi configuration page,
- The WiFi status line now includes richer tooltip text with SSID, RSSI, and AP details.
* Fix MI32 battery polling and period handling
- Add a dedicated hourly battery polling cycle for BLE devices, independent of the normal `MI32Period` / `TelePeriod` interval. This extends the battery life of the BLE devices, especially the LYWSD02MMC.
- Avoid redundant active reads for LYWSD02/03 sensors.
- `lastTime` is updated whenever temperature, humidity, or battery data is received.
- `MI32Period 1` correctly resets internal counters and trigger an immediate active polling cycle.
- `MI32Period 0` now correctly disables the active polling.
- Add device type to the title.
* Fix pollSeconds
* Multiline tooltip formatting
Split tooltip in two lines.
Adjust tooltip CSS to allow manual line breaks on mobile devices.
* Optimize tooltip position
* Update HTTP_HEAD_STYLE_TOOLTIP.h