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.ABSENCE_CLEAN,
airconditioner.ENERGY_SAVING,
airconditioner.EDGE_LIGHTING,
airconditioner.LIGHT_STATEFUL,
*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
# corroborated as numeric by a top-level x.com.samsung.da.cleanLevel scalar,
# so it's a measurement; the others stay string diagnostics (see
@@ -668,6 +668,15 @@
},
"freezer_temperature_setpoint": {
"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": {
@@ -1216,6 +1225,12 @@
},
"ventilation_alarm": {
"name": "Alarm větrání"
},
"edge_lighting": {
"name": "Okrajové osvětlení"
},
"indicator_light": {
"name": "Kontrolka"
}
},
"time": {
@@ -668,6 +668,15 @@
},
"freezer_temperature_setpoint": {
"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": {
@@ -1216,6 +1225,12 @@
},
"ventilation_alarm": {
"name": "Ventilation alarm"
},
"edge_lighting": {
"name": "Edge lighting"
},
"indicator_light": {
"name": "Indicator light"
}
},
"time": {
@@ -793,6 +793,15 @@
},
"freezer_temperature_setpoint": {
"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": {
@@ -1341,6 +1350,12 @@
},
"ventilation_alarm": {
"name": "Alarma de ventilación"
},
"edge_lighting": {
"name": "Iluminación perimetral"
},
"indicator_light": {
"name": "Luz indicadora"
}
},
"time": {
@@ -668,6 +668,15 @@
},
"freezer_temperature_setpoint": {
"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": {
@@ -1216,6 +1225,12 @@
},
"ventilation_alarm": {
"name": "Allarme ventilazione"
},
"edge_lighting": {
"name": "Illuminazione perimetrale"
},
"indicator_light": {
"name": "Spia luminosa"
}
},
"time": {
@@ -668,6 +668,15 @@
},
"freezer_temperature_setpoint": {
"name": "냉동실 온도"
},
"edge_lighting_mode": {
"name": "엣지 라이팅 모드"
},
"edge_lighting_color": {
"name": "엣지 라이팅 색상"
},
"indicator_light_mode": {
"name": "표시등 모드"
}
},
"sensor": {
@@ -1216,6 +1225,12 @@
},
"ventilation_alarm": {
"name": "환기 알림"
},
"edge_lighting": {
"name": "엣지 라이팅"
},
"indicator_light": {
"name": "표시등"
}
},
"time": {
@@ -668,6 +668,15 @@
},
"freezer_temperature_setpoint": {
"name": "Temperatuur vriesgedeelte"
},
"edge_lighting_mode": {
"name": "Randverlichtingsmodus"
},
"edge_lighting_color": {
"name": "Randverlichtingskleur"
},
"indicator_light_mode": {
"name": "Indicatorlampjemodus"
}
},
"sensor": {
@@ -1216,6 +1225,12 @@
},
"ventilation_alarm": {
"name": "Ventilatiealarm"
},
"edge_lighting": {
"name": "Randverlichting"
},
"indicator_light": {
"name": "Indicatorlampje"
}
},
"time": {
+5
View File
@@ -17,11 +17,16 @@
"climate",
"current_temperature_c",
"dust",
"edge_lighting",
"edge_lighting_color",
"edge_lighting_mode",
"energy_kwh",
"energy_saved_kwh",
"fine_dust",
"firmware_update",
"humidity",
"indicator_light",
"indicator_light_mode",
"motion_detect_wind_active",
"motion_detect_wind_mode",
"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
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
lighting, a second stateful light resource, absence-clean,
`/settings/sound/optimization/vs/0`, smart-sensing-cooling), all genuinely
new to this board generation. That's a real device-support gap, left
documented here rather than guessed at, per the 'don't guess' rule --
fixing the routing regression was the scope of #191.
This dump is NOT fully covered yet -- three hrefs remain unbound
(absence-clean, `/settings/sound/optimization/vs/0`, smart-sensing-cooling),
all genuinely new to this board generation. That's a real device-support
gap, left documented here rather than guessed at, per the 'don't guess'
rule -- fixing the routing regression was the scope of #191.
/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
@@ -19,6 +18,11 @@ fixing the routing regression was the scope of #191.
airconditioner.SOUND_MODE and the reused air_purifier.SOUND_OUTPUT/
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,
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
@@ -33,8 +37,6 @@ from tests.conftest import _load_device
_STILL_UNBOUND = frozenset(
{
"/edgelighting/vs/0",
"/light/stateful/vs/0",
"/mds/absenceclean/vs/0",
"/settings/sound/optimization/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
been added to the modelNum board-token table. Resolved via the new 'CAC'
token onto the existing airconditioner registry. Not fully covered yet --
five hrefs remain unbound (edge lighting, stateful light, absence-clean,
sound-optimization, smart-sensing-cooling), all genuinely new to this
board generation and out of scope for the routing fix; see
test_airconditioner_cac.py for the documented gap."""
three hrefs remain unbound (absence-clean, sound-optimization,
smart-sensing-cooling), all genuinely new to this board generation and
out of scope for the routing fix; see test_airconditioner_cac.py for
the documented gap."""
from tests.conftest import _load_device
resources = _load_device("airconditioner_cac")