diff --git a/custom_components/localthings/registry/by_type/airconditioner.py b/custom_components/localthings/registry/by_type/airconditioner.py index 9792766..0ca07c3 100644 --- a/custom_components/localthings/registry/by_type/airconditioner.py +++ b/custom_components/localthings/registry/by_type/airconditioner.py @@ -55,6 +55,8 @@ REGISTRY = DeviceRegistry( airconditioner.SOUND_MODE, airconditioner.ABSENCE_CLEAN, airconditioner.ENERGY_SAVING, + airconditioner.EDGE_LIGHTING, + airconditioner.LIGHT_STATEFUL, *airconditioner.COVERAGE, ] ), diff --git a/custom_components/localthings/registry/capabilities/airconditioner.py b/custom_components/localthings/registry/capabilities/airconditioner.py index 5ce5adc..482f4e7 100644 --- a/custom_components/localthings/registry/capabilities/airconditioner.py +++ b/custom_components/localthings/registry/capabilities/airconditioner.py @@ -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 diff --git a/custom_components/localthings/translations/cs.json b/custom_components/localthings/translations/cs.json index b98538e..6853ed7 100644 --- a/custom_components/localthings/translations/cs.json +++ b/custom_components/localthings/translations/cs.json @@ -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": { diff --git a/custom_components/localthings/translations/en.json b/custom_components/localthings/translations/en.json index 0efa41c..b1f3e08 100644 --- a/custom_components/localthings/translations/en.json +++ b/custom_components/localthings/translations/en.json @@ -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": { diff --git a/custom_components/localthings/translations/es.json b/custom_components/localthings/translations/es.json index 470ef17..84a86ae 100644 --- a/custom_components/localthings/translations/es.json +++ b/custom_components/localthings/translations/es.json @@ -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": { diff --git a/custom_components/localthings/translations/it.json b/custom_components/localthings/translations/it.json index 17412a0..df2c7b8 100644 --- a/custom_components/localthings/translations/it.json +++ b/custom_components/localthings/translations/it.json @@ -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": { diff --git a/custom_components/localthings/translations/ko.json b/custom_components/localthings/translations/ko.json index 62a0886..c018827 100644 --- a/custom_components/localthings/translations/ko.json +++ b/custom_components/localthings/translations/ko.json @@ -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": { diff --git a/custom_components/localthings/translations/nl.json b/custom_components/localthings/translations/nl.json index b9d03a5..8bb5965 100644 --- a/custom_components/localthings/translations/nl.json +++ b/custom_components/localthings/translations/nl.json @@ -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": { diff --git a/tests/fixtures/golden/airconditioner_cac.json b/tests/fixtures/golden/airconditioner_cac.json index 585c344..2426c0e 100644 --- a/tests/fixtures/golden/airconditioner_cac.json +++ b/tests/fixtures/golden/airconditioner_cac.json @@ -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", diff --git a/tests/test_airconditioner_cac.py b/tests/test_airconditioner_cac.py index 8887ea1..7aa0a64 100644 --- a/tests/test_airconditioner_cac.py +++ b/tests/test_airconditioner_cac.py @@ -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", diff --git a/tests/test_golden_regression.py b/tests/test_golden_regression.py index 568169f..dd6932d 100644 --- a/tests/test_golden_regression.py +++ b/tests/test_golden_regression.py @@ -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")