AC: bind SmartCoolClean odor-controller option tokens; add Czech translation

- airconditioner.py: odor_controller_active binary_sensor + odor_controller_progress
  sensor, read from the /mode/vs/0 SmartCoolClean_/ProgressSmartClean_ option tokens
  (SmartThings cloud custom.airConditionerOdorController capability). Read-only --
  no confirmed write command.
- Reporter's TP1X_DA-AC-RAC-01001_0000 dump: fan speed and WindFree preset already
  bind via the existing composite climate entity, no code change needed there.
- en.json/nl.json: new entity names for the two additions.
- cs.json: new, full Czech translation catalog (mirrors en.json topology 1:1).
- New fixture + golden + tests locking in the odor-controller behavior.
This commit is contained in:
Petr Divis committed 2026-07-31 16:02:13 +02:00
1 parent e99a8a6b05
commit d0c68fb5ac
8 files changed
+1967

No files matched your search

@@ -380,6 +380,29 @@ def _option_number_write(prefix):
return write
def _odor_controller_active(rep):
"""Odor-controller self-clean on/off, from the `SmartCoolClean_<On/Off>`
/mode/vs/0 option token -- corroborated against the SmartThings cloud
custom.airConditionerOdorController capability's airConditionerOdorController
State field, same on/off vocabulary and a matching name ("Smart Cool
Clean" mirrors "odor controller"). Unconditional across board
generations, same as beep above -- no evidence ties this token to the
legacy-vs-newer split _has_option_token gates on. Read-only: no command
capability is confirmed, so this stays a sensor rather than a guessed
write (the 'don't guess' rule)."""
tok = _option_token(rep, 'SmartCoolClean')
if tok is None:
return None
return tok == 'On'
def _odor_controller_progress(rep):
"""0-100 progress of the odor-controller self-clean cycle, from the
`ProgressSmartClean_<N>` option token -- mirrors the cloud
airConditionerOdorControllerProgress field."""
return _int(_option_token(rep, 'ProgressSmartClean'))
def _humidity(rep):
"""Relative humidity, preferring the 5%-rounded field where it exists.
@@ -582,6 +605,21 @@ CLIMATE = Capability(
exists_fn=_has_option_token('FilterTime'),
device_class='duration', unit='h',
state_class='measurement', icon='mdi:air-filter'),
# Odor-controller ("Smart Cool Clean") state + progress -- see
# _odor_controller_active's docstring for the cloud-capability
# correspondence. Present on TP1X_DA-AC-RAC-01001 (issue reporter's
# dump); gating is token presence only, not board generation.
BinarySensorDesc(key='odor_controller_active',
rep_fn=_odor_controller_active,
exists_fn=lambda rep, resources: (
_option_token(rep, 'SmartCoolClean') is not None),
icon='mdi:air-filter', entity_category='diagnostic'),
SensorDesc(key='odor_controller_progress',
rep_fn=_odor_controller_progress,
exists_fn=lambda rep, resources: (
_option_token(rep, 'ProgressSmartClean') is not None),
unit='%', state_class='measurement',
icon='mdi:progress-check', entity_category='diagnostic'),
),
)
File diff suppressed because it is too large. Load diff
@@ -34,6 +34,9 @@
"overload_protection_active": {
"name": "Overload protection active"
},
"odor_controller_active": {
"name": "Odor controller active"
},
"cycle_active": {
"name": "Running"
},
@@ -646,6 +649,9 @@
"outdoor_temperature": {
"name": "Outdoor temperature"
},
"odor_controller_progress": {
"name": "Odor controller progress"
},
"panel_status": {
"name": "Panel status"
},
@@ -34,6 +34,9 @@
"overload_protection_active": {
"name": "Overbelastingsbeveiliging actief"
},
"odor_controller_active": {
"name": "Geurbeheersing actief"
},
"cycle_active": {
"name": "Actief"
},
@@ -646,6 +649,9 @@
"outdoor_temperature": {
"name": "Buitentemperatuur"
},
"odor_controller_progress": {
"name": "Voortgang geurbeheersing"
},
"panel_status": {
"name": "Paneelstatus"
},