Add edge-lighting and indicator-light support for TP1X_DA-AC-CAC-01001 (issue #288)

Six System A/C cassette units on the same board test_airconditioner_cac.py
already documented as having an incomplete coverage gap gave real dump
evidence for two of its remaining unbound hrefs:

- /edgelighting/vs/0: an accent-light strip with on/off, a Smart/High/Low
  mode, and a Kelvin color-temperature select (3000K/4000K/6500K), all read
  from the device's own live supported-value lists.
- /light/stateful/vs/0: a second, distinct light resource with its own
  on/off and Smart/Low/High mode -- not to be confused with EDGE_LIGHTING
  or DISPLAY_LIGHT's ambient mood light.

convenientMode/operatingOption on /edgelighting/vs/0 stay unexposed: present
on every dump but no evidence of what either actually controls.

Only three hrefs remain in test_airconditioner_cac.py's documented gap now
(absence-clean, sound-optimization, smart-sensing-cooling).
This commit is contained in:
Marc Billow
2026-08-07 14:27:25 +00:00
parent 42fd9c2aa4
commit c203bd42c5
11 changed files with 191 additions and 12 deletions
@@ -55,6 +55,8 @@ REGISTRY = DeviceRegistry(
airconditioner.SOUND_MODE, airconditioner.SOUND_MODE,
airconditioner.ABSENCE_CLEAN, airconditioner.ABSENCE_CLEAN,
airconditioner.ENERGY_SAVING, airconditioner.ENERGY_SAVING,
airconditioner.EDGE_LIGHTING,
airconditioner.LIGHT_STATEFUL,
*airconditioner.COVERAGE, *airconditioner.COVERAGE,
] ]
), ),
@@ -1252,6 +1252,86 @@ ENERGY_SAVING = Capability(
), ),
) )
# TP1X_DA-AC-CAC-01001-class additions (issue #288, six System A/C cassette
# units on the same board test_airconditioner_cac.py's coverage-gap test
# documents). `convenientMode`/`operatingOption` stay unexposed -- present
# on every dump seen but no evidence of what either actually controls.
EDGE_LIGHTING = Capability(
href="/edgelighting/vs/0",
poll_tier="cold",
entities=(
SwitchDesc(
key="edge_lighting",
field="status",
icon="mdi:led-strip",
entity_category="config",
value_fn=lambda v: v == "On",
write_fn=lambda p, rep, href=None: (
["edgelighting", "vs", "0"],
{"status": "On" if p == "On" else "Off"},
),
),
SelectDesc(
key="edge_lighting_mode",
field="mode",
icon="mdi:led-strip-variant",
entity_category="config",
options_field="modeSupportedList",
write_fn=lambda p, rep, href=None: (
["edgelighting", "vs", "0"],
{"mode": p},
),
),
# Color temperature in Kelvin (3000K/4000K/6500K), not a hue -- a
# select over the live-reported codes rather than a light color_temp
# entity, consistent with this project's other Kelvin-coded selects.
SelectDesc(
key="edge_lighting_color",
field="colorOption",
icon="mdi:palette",
entity_category="config",
options_field="colorSupportedList",
write_fn=lambda p, rep, href=None: (
["edgelighting", "vs", "0"],
{"colorOption": p},
),
),
),
)
# Second, distinct light resource on this board generation -- an
# always-on-style indicator light with its own status/mode, not to be
# confused with EDGE_LIGHTING (a different href/rep entirely) or
# DISPLAY_LIGHT (/light/vs/0's ambient mood light).
LIGHT_STATEFUL = Capability(
href="/light/stateful/vs/0",
poll_tier="cold",
entities=(
SwitchDesc(
key="indicator_light",
field="status",
icon="mdi:led-on",
entity_category="config",
value_fn=lambda v: v == "On",
write_fn=lambda p, rep, href=None: (
["light", "stateful", "vs", "0"],
{"status": "On" if p == "On" else "Off"},
),
),
SelectDesc(
key="indicator_light_mode",
field="mode",
icon="mdi:led-variant-on",
entity_category="config",
options_field="supportedModes",
write_fn=lambda p, rep, href=None: (
["light", "stateful", "vs", "0"],
{"mode": p},
),
),
),
)
# /sensors/vs/0 items[] carry live air-quality readings. CleanLevel is # /sensors/vs/0 items[] carry live air-quality readings. CleanLevel is
# corroborated as numeric by a top-level x.com.samsung.da.cleanLevel scalar, # corroborated as numeric by a top-level x.com.samsung.da.cleanLevel scalar,
# so it's a measurement; the others stay string diagnostics (see # so it's a measurement; the others stay string diagnostics (see
@@ -668,6 +668,15 @@
}, },
"freezer_temperature_setpoint": { "freezer_temperature_setpoint": {
"name": "Teplota mrazicí zóny" "name": "Teplota mrazicí zóny"
},
"edge_lighting_mode": {
"name": "Režim okrajového osvětlení"
},
"edge_lighting_color": {
"name": "Barva okrajového osvětlení"
},
"indicator_light_mode": {
"name": "Režim kontrolky"
} }
}, },
"sensor": { "sensor": {
@@ -1216,6 +1225,12 @@
}, },
"ventilation_alarm": { "ventilation_alarm": {
"name": "Alarm větrání" "name": "Alarm větrání"
},
"edge_lighting": {
"name": "Okrajové osvětlení"
},
"indicator_light": {
"name": "Kontrolka"
} }
}, },
"time": { "time": {
@@ -668,6 +668,15 @@
}, },
"freezer_temperature_setpoint": { "freezer_temperature_setpoint": {
"name": "Freezer temperature" "name": "Freezer temperature"
},
"edge_lighting_mode": {
"name": "Edge lighting mode"
},
"edge_lighting_color": {
"name": "Edge lighting color"
},
"indicator_light_mode": {
"name": "Indicator light mode"
} }
}, },
"sensor": { "sensor": {
@@ -1216,6 +1225,12 @@
}, },
"ventilation_alarm": { "ventilation_alarm": {
"name": "Ventilation alarm" "name": "Ventilation alarm"
},
"edge_lighting": {
"name": "Edge lighting"
},
"indicator_light": {
"name": "Indicator light"
} }
}, },
"time": { "time": {
@@ -793,6 +793,15 @@
}, },
"freezer_temperature_setpoint": { "freezer_temperature_setpoint": {
"name": "Temperatura del congelador" "name": "Temperatura del congelador"
},
"edge_lighting_mode": {
"name": "Modo de iluminación perimetral"
},
"edge_lighting_color": {
"name": "Color de iluminación perimetral"
},
"indicator_light_mode": {
"name": "Modo de luz indicadora"
} }
}, },
"sensor": { "sensor": {
@@ -1341,6 +1350,12 @@
}, },
"ventilation_alarm": { "ventilation_alarm": {
"name": "Alarma de ventilación" "name": "Alarma de ventilación"
},
"edge_lighting": {
"name": "Iluminación perimetral"
},
"indicator_light": {
"name": "Luz indicadora"
} }
}, },
"time": { "time": {
@@ -668,6 +668,15 @@
}, },
"freezer_temperature_setpoint": { "freezer_temperature_setpoint": {
"name": "Temperatura freezer" "name": "Temperatura freezer"
},
"edge_lighting_mode": {
"name": "Modalità illuminazione perimetrale"
},
"edge_lighting_color": {
"name": "Colore illuminazione perimetrale"
},
"indicator_light_mode": {
"name": "Modalità spia luminosa"
} }
}, },
"sensor": { "sensor": {
@@ -1216,6 +1225,12 @@
}, },
"ventilation_alarm": { "ventilation_alarm": {
"name": "Allarme ventilazione" "name": "Allarme ventilazione"
},
"edge_lighting": {
"name": "Illuminazione perimetrale"
},
"indicator_light": {
"name": "Spia luminosa"
} }
}, },
"time": { "time": {
@@ -668,6 +668,15 @@
}, },
"freezer_temperature_setpoint": { "freezer_temperature_setpoint": {
"name": "냉동실 온도" "name": "냉동실 온도"
},
"edge_lighting_mode": {
"name": "엣지 라이팅 모드"
},
"edge_lighting_color": {
"name": "엣지 라이팅 색상"
},
"indicator_light_mode": {
"name": "표시등 모드"
} }
}, },
"sensor": { "sensor": {
@@ -1216,6 +1225,12 @@
}, },
"ventilation_alarm": { "ventilation_alarm": {
"name": "환기 알림" "name": "환기 알림"
},
"edge_lighting": {
"name": "엣지 라이팅"
},
"indicator_light": {
"name": "표시등"
} }
}, },
"time": { "time": {
@@ -668,6 +668,15 @@
}, },
"freezer_temperature_setpoint": { "freezer_temperature_setpoint": {
"name": "Temperatuur vriesgedeelte" "name": "Temperatuur vriesgedeelte"
},
"edge_lighting_mode": {
"name": "Randverlichtingsmodus"
},
"edge_lighting_color": {
"name": "Randverlichtingskleur"
},
"indicator_light_mode": {
"name": "Indicatorlampjemodus"
} }
}, },
"sensor": { "sensor": {
@@ -1216,6 +1225,12 @@
}, },
"ventilation_alarm": { "ventilation_alarm": {
"name": "Ventilatiealarm" "name": "Ventilatiealarm"
},
"edge_lighting": {
"name": "Randverlichting"
},
"indicator_light": {
"name": "Indicatorlampje"
} }
}, },
"time": { "time": {
+5
View File
@@ -17,11 +17,16 @@
"climate", "climate",
"current_temperature_c", "current_temperature_c",
"dust", "dust",
"edge_lighting",
"edge_lighting_color",
"edge_lighting_mode",
"energy_kwh", "energy_kwh",
"energy_saved_kwh", "energy_saved_kwh",
"fine_dust", "fine_dust",
"firmware_update", "firmware_update",
"humidity", "humidity",
"indicator_light",
"indicator_light_mode",
"motion_detect_wind_active", "motion_detect_wind_active",
"motion_detect_wind_mode", "motion_detect_wind_mode",
"mute_once", "mute_once",
+10 -8
View File
@@ -6,12 +6,11 @@ token -- this board was the one exception (its oneUiVersion self-reports
"7.0 Air conditioner", but 'CAC' had never been added to the board-token "7.0 Air conditioner", but 'CAC' had never been added to the board-token
table), so it silently fell back to common caps and lost its climate entity. table), so it silently fell back to common caps and lost its climate entity.
This dump is NOT fully covered yet -- five hrefs remain unbound (edge This dump is NOT fully covered yet -- three hrefs remain unbound
lighting, a second stateful light resource, absence-clean, (absence-clean, `/settings/sound/optimization/vs/0`, smart-sensing-cooling),
`/settings/sound/optimization/vs/0`, smart-sensing-cooling), all genuinely all genuinely new to this board generation. That's a real device-support
new to this board generation. That's a real device-support gap, left gap, left documented here rather than guessed at, per the 'don't guess'
documented here rather than guessed at, per the 'don't guess' rule -- rule -- fixing the routing regression was the scope of #191.
fixing the routing regression was the scope of #191.
/settings/sound/mode/vs/0, /settings/sound/output/vs/0 and /settings/sound/mode/vs/0, /settings/sound/output/vs/0 and
/settings/sound/volume/vs/0 used to be on this list too, until issue #319 /settings/sound/volume/vs/0 used to be on this list too, until issue #319
@@ -19,6 +18,11 @@ fixing the routing regression was the scope of #191.
airconditioner.SOUND_MODE and the reused air_purifier.SOUND_OUTPUT/ airconditioner.SOUND_MODE and the reused air_purifier.SOUND_OUTPUT/
SOUND_VOLUME now cover all three here as well. SOUND_VOLUME now cover all three here as well.
/edgelighting/vs/0 and /light/stateful/vs/0 used to be on this list too,
until issue #288 (six System A/C cassette units on this same board) gave
real dump evidence for both -- airconditioner.EDGE_LIGHTING and
LIGHT_STATEFUL now cover them.
/uvled/vs/0 and /filter/airdustPM1filter/vs/0 used to be on this list too, /uvled/vs/0 and /filter/airdustPM1filter/vs/0 used to be on this list too,
until issue #270 (TP1X_FAC_TIME_23K) added real capabilities for both -- until issue #270 (TP1X_FAC_TIME_23K) added real capabilities for both --
this board's own live filterUsage/filterStatus data on the PM1 filter binds this board's own live filterUsage/filterStatus data on the PM1 filter binds
@@ -33,8 +37,6 @@ from tests.conftest import _load_device
_STILL_UNBOUND = frozenset( _STILL_UNBOUND = frozenset(
{ {
"/edgelighting/vs/0",
"/light/stateful/vs/0",
"/mds/absenceclean/vs/0", "/mds/absenceclean/vs/0",
"/settings/sound/optimization/vs/0", "/settings/sound/optimization/vs/0",
"/smartsensingcooling/vs/0", "/smartsensingcooling/vs/0",
+4 -4
View File
@@ -1173,10 +1173,10 @@ def test_registry_reproduces_golden_state_keys_for_airconditioner_cac():
0.16.0 when oneUiVersion detection was dropped, since 'CAC' had never 0.16.0 when oneUiVersion detection was dropped, since 'CAC' had never
been added to the modelNum board-token table. Resolved via the new 'CAC' been added to the modelNum board-token table. Resolved via the new 'CAC'
token onto the existing airconditioner registry. Not fully covered yet -- token onto the existing airconditioner registry. Not fully covered yet --
five hrefs remain unbound (edge lighting, stateful light, absence-clean, three hrefs remain unbound (absence-clean, sound-optimization,
sound-optimization, smart-sensing-cooling), all genuinely new to this smart-sensing-cooling), all genuinely new to this board generation and
board generation and out of scope for the routing fix; see out of scope for the routing fix; see test_airconditioner_cac.py for
test_airconditioner_cac.py for the documented gap.""" the documented gap."""
from tests.conftest import _load_device from tests.conftest import _load_device
resources = _load_device("airconditioner_cac") resources = _load_device("airconditioner_cac")