diff --git a/custom_components/localthings/registry/by_type/__init__.py b/custom_components/localthings/registry/by_type/__init__.py index 5c1b98f..25165e2 100644 --- a/custom_components/localthings/registry/by_type/__init__.py +++ b/custom_components/localthings/registry/by_type/__init__.py @@ -226,12 +226,14 @@ _OIC_TYPE_TO_KEY: dict[str, str] = { "oic.d.dryer": "dryer", "oic.d.oven": "oven", "oic.d.refrigerator": "refrigerator", + "oic.d.krefrigerator": "refrigerator", # issue #328 -- kimchi refrigerator "oic.d.washer": "washer", "x.com.st.d.airqualitysensor": "air_monitor", "x.com.st.d.dehumidifier": "dehumidifier", "x.com.st.d.hood": "range_hood", # AHD-WW-TP1-22-COMMON "x.com.st.d.stickcleaner": "vacuum_station", "x.com.st.d.steamcloset": "air_dresser", + "x.com.st.d.winecellar": "refrigerator", # issue #328 -- same TP1X_REF_21K board } diff --git a/custom_components/localthings/registry/by_type/refrigerator.py b/custom_components/localthings/registry/by_type/refrigerator.py index bd3e52c..fad8516 100644 --- a/custom_components/localthings/registry/by_type/refrigerator.py +++ b/custom_components/localthings/registry/by_type/refrigerator.py @@ -14,6 +14,13 @@ REGISTRY = DeviceRegistry( fridge.DOOR_ALERT, common.WATER_FILTER, fridge.AIR_FILTER, + fridge.DEODOR_FILTER, + fridge.AUTO_DOOR_TIMER, + fridge.AUTO_DOOR_SINGLE, + fridge.AUTO_DOOR_KIMCHI, + fridge.AUTO_DOOR_WINECELLAR, + fridge.WINECELLAR_PANTRY_ZONE, + fridge.WINECELLAR_INFO, dishwasher.DIAGNOSIS, fridge.ICEMAKER_NIGHTTIME, fridge.FLEX_ZONE, diff --git a/custom_components/localthings/registry/capabilities/fridge.py b/custom_components/localthings/registry/capabilities/fridge.py index 52a42e5..03e4780 100644 --- a/custom_components/localthings/registry/capabilities/fridge.py +++ b/custom_components/localthings/registry/capabilities/fridge.py @@ -326,9 +326,118 @@ STATUS_LOCK = Capability( value_fn=lambda v: v == "On", write_fn=_status_lock_write("x.com.samsung.da.device.sound"), ), + # Auto Door Open's own voice/sound feedback toggles (issue #328, + # TP1X_REF_21K family) -- siblings of auto_door_opener above, not + # duplicates of fridge_sound (device.sound is the general appliance + # beep, these two gate ado's own prompts). Only seen on the + # auto-door-equipped variants (single/kimchi/winecellar), not the + # earlier TP1X_REF_21K dumps that predate that feature -- gated on + # each field's own presence rather than assumed universal. + SwitchDesc( + key="auto_door_voice_control", + field="x.com.samsung.da.ado.voicecontrol", + icon="mdi:microphone", + entity_category="config", + value_fn=lambda v: v == "On", + write_fn=_status_lock_write("x.com.samsung.da.ado.voicecontrol"), + exists_fn=lambda rep, resources: "x.com.samsung.da.ado.voicecontrol" in rep, + ), + SwitchDesc( + key="auto_door_sound_control", + field="x.com.samsung.da.ado.soundcontrol", + icon="mdi:volume-medium", + entity_category="config", + value_fn=lambda v: v == "On", + write_fn=_status_lock_write("x.com.samsung.da.ado.soundcontrol"), + exists_fn=lambda rep, resources: "x.com.samsung.da.ado.soundcontrol" in rep, + ), ), ) +# Auto Door Open's paired delay setting (issue #328, TP1X_REF_21K family): +# how long the door stays held open before it re-closes, on/off itself is +# STATUS_LOCK.auto_door_opener above. Same discrete-options-select shape as +# DEFINITE_TEMPERATURE_COOLER/FREEZER. Unit unconfirmed -- no supportedList +# field states it and the report carried no app screenshot -- so this is a +# raw-code select rather than a guessed seconds/minutes NumberDesc. + + +def _auto_door_timer_write(p, rep, href=None): + return ["autodoor", "timer", "vs", "0"], {"x.com.samsung.da.time.desired": p} + + +AUTO_DOOR_TIMER = Capability( + href="/autodoor/timer/vs/0", + poll_tier="warm", + entities=( + SelectDesc( + key="auto_door_timer", + field="x.com.samsung.da.time.desired", + icon="mdi:timer-outline", + translation_key="auto_door_timer", + entity_category="config", + options_field="x.com.samsung.da.time.supportedOptions", + write_fn=_auto_door_timer_write, + ), + ), +) + +# /autodoor//vs/0 -- one per fridge sub-type sharing the Auto Door +# Open feature (single-door, kimchi, winecellar; issue #328). Each reports +# only x.com.samsung.da.ado.openOptions, declaring which open styles that +# variant supports -- every dump seen so far carries exactly one option +# ('Single') with no paired desired/current field to make a choice against, +# the same "no real choice to expose yet" shape as ignored.py's /mode/0. +# Bound with no entities to record the three hrefs as coverage; revisit if +# a device ever reports more than one option. +AUTO_DOOR_SINGLE = Capability(href="/autodoor/single/vs/0") +AUTO_DOOR_KIMCHI = Capability(href="/autodoor/kimchi/vs/0") +AUTO_DOOR_WINECELLAR = Capability(href="/autodoor/winecellar/vs/0") + +# Wine-cellar variant (x.com.st.d.winecellar, issue #328) of the same +# deodorizing filter AIR_FILTER models -- filterUsage/filterStatus at a +# different href, same 0-100-percentage-already shape (see AIR_FILTER's own +# comment). filterUsage reads '-1' on the only dump seen (filterStatus +# 'normal'), relayed as-is rather than special-cased -- no second dump to +# confirm whether that's a real sentinel or this unit just not tracking it. +DEODOR_FILTER = Capability( + href="/filter/deodorfilter/vs/0", + poll_tier="cold", + entities=AIR_FILTER.entities, +) + +# Wine-cellar multi-compartment pantry select (issue #328): same +# mode/supportedOptions shape as PANTRY_ZONE, at its own href with a 5-way +# option set (Processed_Meat/Cheese/Nuts/Fruit/Wine) instead of PANTRY_ZONE's. +# Only a "one" instance seen -- not generalized to a pattern cap, same +# discipline as PANTRY_ZONE itself. + + +def _winecellar_pantry_write(p, rep, href=None): + return ["status", "winecellar", "pantry", "one", "vs", "0"], {"x.com.samsung.da.mode": p} + + +WINECELLAR_PANTRY_ZONE = Capability( + href="/status/winecellar/pantry/one/vs/0", + poll_tier="warm", + entities=( + SelectDesc( + key="winecellar_pantry_zone_mode", + field="x.com.samsung.da.mode", + icon="mdi:glass-wine", + translation_key="winecellar_pantry_zone_mode", + entity_category="config", + options_field="x.com.samsung.da.supportedOptions", + write_fn=_winecellar_pantry_write, + ), + ), +) + +# Wine-cellar internal table-revision marker (issue #328) -- versioning +# metadata, not appliance state. Same treatment as ignored.py's +# /wm/setinfo/vs/0. +WINECELLAR_INFO = Capability(href="/information/winecellar/vs/0") + # /defrost/delay/vs/0 is the writable toggle to postpone a scheduled # defrost. /defrost/block/vs/0 is unrelated: despite the "block" naming, # live dumps confirm DEFROST_BLOCK_ON means the defrost cycle is actively diff --git a/custom_components/localthings/translations/cs.json b/custom_components/localthings/translations/cs.json index e83ea00..b488ae0 100644 --- a/custom_components/localthings/translations/cs.json +++ b/custom_components/localthings/translations/cs.json @@ -225,6 +225,9 @@ "air_filter_threshold": { "name": "Práh upozornění na filtr" }, + "auto_door_timer": { + "name": "Časovač automatického otevírání dvířek" + }, "beverage_zone_mode": { "name": "Režim zóny nápojů", "state": { @@ -472,9 +475,11 @@ "kimchi_storage_crunfch": "Křupavé kimči", "kimchi_storage_buy": "Kupované kimči", "storage_fridge_normal": "Chlazení", + "storage_fridge": "Chlazení", "storage_fridge_cold": "Chlazení, silné", "storage_fridge_warm": "Chlazení, slabé", "storage_freezer_normal": "Mražení", + "storage_freezer": "Mražení", "storage_freezer_cold": "Mražení, silné", "storage_freezer_warm": "Mražení, slabé", "kimchi_ripe_low_temp": "Zrání kimči, nízká teplota", @@ -487,7 +492,8 @@ "storage_fresh_cereal": "Obiloviny", "storage_fridge_drink": "Nápoje", "storage_fresh_wine": "Víno", - "storage_fresh_potato_banana": "Brambory a banány" + "storage_fresh_potato_banana": "Brambory a banány", + "newmode_kimchi_0000": "Nový režim" } }, "pantry_zone_mode": { @@ -498,6 +504,16 @@ "fdr_drinks": "Nápoje" } }, + "winecellar_pantry_zone_mode": { + "name": "Režim spíže", + "state": { + "processed_meat": "Zpracované maso", + "cheese": "Sýr", + "nuts": "Ořechy", + "fruit": "Ovoce", + "wine": "Víno" + } + }, "range_burner_power_level": { "name": "Výkon hořáku {number}", "state": { @@ -1147,6 +1163,12 @@ "auto_door_opener": { "name": "Automatické otevírání dvířek" }, + "auto_door_sound_control": { + "name": "Zvuk automatického otevírání dvířek" + }, + "auto_door_voice_control": { + "name": "Hlasové ovládání automatického otevírání dvířek" + }, "auto_release_dry": { "name": "Automatické uvolnění při sušení" }, diff --git a/custom_components/localthings/translations/de.json b/custom_components/localthings/translations/de.json index 910e2d8..c34423f 100644 --- a/custom_components/localthings/translations/de.json +++ b/custom_components/localthings/translations/de.json @@ -225,6 +225,9 @@ "air_filter_threshold": { "name": "Filteralarm-Schwellenwert" }, + "auto_door_timer": { + "name": "Timer für automatischen Türöffner" + }, "beverage_zone_mode": { "name": "Getränkezonen-Modus", "state": { @@ -467,6 +470,16 @@ "fdr_drinks": "Getränke" } }, + "winecellar_pantry_zone_mode": { + "name": "Vorratszonen-Modus", + "state": { + "processed_meat": "Wurst/Aufschnitt", + "cheese": "Käse", + "nuts": "Nüsse", + "fruit": "Obst", + "wine": "Wein" + } + }, "range_burner_power_level": { "name": "Brenner {number} Leistungsstufe", "state": { @@ -678,9 +691,11 @@ "kimchi_storage_crunfch": "Knackiges Kimchi", "kimchi_storage_buy": "Gekauftes Kimchi", "storage_fridge_normal": "Kühlen", + "storage_fridge": "Kühlen", "storage_fridge_cold": "Kühlen, stark", "storage_fridge_warm": "Kühlen, schwach", "storage_freezer_normal": "Gefrieren", + "storage_freezer": "Gefrieren", "storage_freezer_cold": "Gefrieren, stark", "storage_freezer_warm": "Gefrieren, schwach", "kimchi_ripe_low_temp": "Kimchi-Reifung, niedrige Temperatur", @@ -693,7 +708,8 @@ "storage_fresh_cereal": "Getreide", "storage_fridge_drink": "Getränke", "storage_fresh_wine": "Wein", - "storage_fresh_potato_banana": "Kartoffeln und Bananen" + "storage_fresh_potato_banana": "Kartoffeln und Bananen", + "newmode_kimchi_0000": "Neuer Modus" } }, "energy_saving_mode": { @@ -1144,6 +1160,12 @@ "auto_door_opener": { "name": "Automatischer Türöffner" }, + "auto_door_sound_control": { + "name": "Ton des automatischen Türöffners" + }, + "auto_door_voice_control": { + "name": "Sprachsteuerung des automatischen Türöffners" + }, "auto_release_dry": { "name": "Automatische Trocknungsfreigabe" }, diff --git a/custom_components/localthings/translations/en.json b/custom_components/localthings/translations/en.json index 9830e8f..bf47645 100644 --- a/custom_components/localthings/translations/en.json +++ b/custom_components/localthings/translations/en.json @@ -225,6 +225,9 @@ "air_filter_threshold": { "name": "Filter alarm threshold" }, + "auto_door_timer": { + "name": "Auto door open timer" + }, "beverage_zone_mode": { "name": "Beverage zone mode", "state": { @@ -474,9 +477,11 @@ "storage_fridge_normal": "Fridge", "storage_fridge_cold": "Fridge, strong", "storage_fridge_warm": "Fridge, weak", + "storage_fridge": "Fridge", "storage_freezer_normal": "Freezer", "storage_freezer_cold": "Freezer, strong", "storage_freezer_warm": "Freezer, weak", + "storage_freezer": "Freezer", "kimchi_ripe_low_temp": "Kimchi ripening, low temperature", "kimchi_ripe_normal_temp": "Kimchi ripening, room temperature", "kimchi_ripe_kkakdugi": "Kkakdugi ripening", @@ -487,7 +492,8 @@ "storage_fresh_cereal": "Grains", "storage_fridge_drink": "Beverages", "storage_fresh_wine": "Wine", - "storage_fresh_potato_banana": "Potato & banana" + "storage_fresh_potato_banana": "Potato & banana", + "newmode_kimchi_0000": "New mode" } }, "pantry_zone_mode": { @@ -498,6 +504,16 @@ "fdr_drinks": "Drinks" } }, + "winecellar_pantry_zone_mode": { + "name": "Pantry zone mode", + "state": { + "processed_meat": "Processed meat", + "cheese": "Cheese", + "nuts": "Nuts", + "fruit": "Fruit", + "wine": "Wine" + } + }, "range_burner_power_level": { "name": "Burner {number} power level", "state": { @@ -1147,6 +1163,12 @@ "auto_door_opener": { "name": "Auto door opener" }, + "auto_door_sound_control": { + "name": "Auto door sound" + }, + "auto_door_voice_control": { + "name": "Auto door voice control" + }, "auto_release_dry": { "name": "Auto release dry" }, diff --git a/custom_components/localthings/translations/es.json b/custom_components/localthings/translations/es.json index dceaa23..fea0e9a 100644 --- a/custom_components/localthings/translations/es.json +++ b/custom_components/localthings/translations/es.json @@ -374,6 +374,9 @@ "air_filter_threshold": { "name": "Umbral de alarma del filtro" }, + "auto_door_timer": { + "name": "Temporizador de apertura automática de puerta" + }, "beverage_zone_mode": { "name": "Modo de zona de bebidas", "state": { @@ -621,9 +624,11 @@ "kimchi_storage_crunfch": "Kimchi crujiente", "kimchi_storage_buy": "Kimchi comprado", "storage_fridge_normal": "Frigorífico", + "storage_fridge": "Frigorífico", "storage_fridge_cold": "Frigorífico, fuerte", "storage_fridge_warm": "Frigorífico, suave", "storage_freezer_normal": "Congelador", + "storage_freezer": "Congelador", "storage_freezer_cold": "Congelador, fuerte", "storage_freezer_warm": "Congelador, suave", "kimchi_ripe_low_temp": "Maduración kimchi, baja temperatura", @@ -636,7 +641,8 @@ "storage_fresh_cereal": "Cereales", "storage_fridge_drink": "Bebidas", "storage_fresh_wine": "Vino", - "storage_fresh_potato_banana": "Patata y plátano" + "storage_fresh_potato_banana": "Patata y plátano", + "newmode_kimchi_0000": "Modo nuevo" } }, "pantry_zone_mode": { @@ -647,6 +653,16 @@ "fdr_drinks": "Bebidas" } }, + "winecellar_pantry_zone_mode": { + "name": "Modo de zona de despensa", + "state": { + "processed_meat": "Embutidos", + "cheese": "Queso", + "nuts": "Frutos secos", + "fruit": "Fruta", + "wine": "Vino" + } + }, "range_burner_power_level": { "name": "Nivel de potencia del fuego {number}", "state": { @@ -1296,6 +1312,12 @@ "auto_door_opener": { "name": "Apertura automática de puerta" }, + "auto_door_sound_control": { + "name": "Sonido de apertura automática de puerta" + }, + "auto_door_voice_control": { + "name": "Control por voz de apertura automática de puerta" + }, "auto_release_dry": { "name": "Liberación automática de secado" }, diff --git a/custom_components/localthings/translations/it.json b/custom_components/localthings/translations/it.json index 85d5248..a8100c6 100644 --- a/custom_components/localthings/translations/it.json +++ b/custom_components/localthings/translations/it.json @@ -225,6 +225,9 @@ "air_filter_threshold": { "name": "Soglia allarme filtro" }, + "auto_door_timer": { + "name": "Timer apertura automatica sportello" + }, "beverage_zone_mode": { "name": "Modalità zona bevande", "state": { @@ -472,9 +475,11 @@ "kimchi_storage_crunfch": "Kimchi croccante", "kimchi_storage_buy": "Kimchi acquistato", "storage_fridge_normal": "Frigorifero", + "storage_fridge": "Frigorifero", "storage_fridge_cold": "Frigorifero, forte", "storage_fridge_warm": "Frigorifero, debole", "storage_freezer_normal": "Congelatore", + "storage_freezer": "Congelatore", "storage_freezer_cold": "Congelatore, forte", "storage_freezer_warm": "Congelatore, debole", "kimchi_ripe_low_temp": "Maturazione kimchi, bassa temperatura", @@ -487,7 +492,8 @@ "storage_fresh_cereal": "Cereali", "storage_fridge_drink": "Bevande", "storage_fresh_wine": "Vino", - "storage_fresh_potato_banana": "Patate e banane" + "storage_fresh_potato_banana": "Patate e banane", + "newmode_kimchi_0000": "Nuova modalità" } }, "pantry_zone_mode": { @@ -498,6 +504,16 @@ "fdr_drinks": "Bevande" } }, + "winecellar_pantry_zone_mode": { + "name": "Modalità zona dispensa", + "state": { + "processed_meat": "Salumi", + "cheese": "Formaggio", + "nuts": "Frutta secca", + "fruit": "Frutta", + "wine": "Vino" + } + }, "range_burner_power_level": { "name": "Livello potenza fornello {number}", "state": { @@ -1147,6 +1163,12 @@ "auto_door_opener": { "name": "Apertura automatica sportello" }, + "auto_door_sound_control": { + "name": "Suono apertura automatica sportello" + }, + "auto_door_voice_control": { + "name": "Controllo vocale apertura automatica sportello" + }, "auto_release_dry": { "name": "Apertura automatica per asciugatura" }, diff --git a/custom_components/localthings/translations/ko.json b/custom_components/localthings/translations/ko.json index 3fed8be..61f7696 100644 --- a/custom_components/localthings/translations/ko.json +++ b/custom_components/localthings/translations/ko.json @@ -225,6 +225,9 @@ "air_filter_threshold": { "name": "필터 알림 기준" }, + "auto_door_timer": { + "name": "오토 오픈 도어 타이머" + }, "beverage_zone_mode": { "name": "음료 보관 모드", "state": { @@ -472,9 +475,11 @@ "kimchi_storage_crunfch": "아삭 김치", "kimchi_storage_buy": "구입 김치", "storage_fridge_normal": "냉장", + "storage_fridge": "냉장", "storage_fridge_cold": "냉장 강", "storage_fridge_warm": "냉장 약", "storage_freezer_normal": "냉동", + "storage_freezer": "냉동", "storage_freezer_cold": "냉동 강", "storage_freezer_warm": "냉동 약", "kimchi_ripe_low_temp": "김치 저온 숙성", @@ -487,7 +492,8 @@ "storage_fresh_cereal": "곡류", "storage_fridge_drink": "음료", "storage_fresh_wine": "와인", - "storage_fresh_potato_banana": "감자 및 바나나" + "storage_fresh_potato_banana": "감자 및 바나나", + "newmode_kimchi_0000": "새 모드" } }, "pantry_zone_mode": { @@ -498,6 +504,16 @@ "fdr_drinks": "음료" } }, + "winecellar_pantry_zone_mode": { + "name": "팬트리실 모드", + "state": { + "processed_meat": "가공육", + "cheese": "치즈", + "nuts": "견과류", + "fruit": "과일", + "wine": "와인" + } + }, "range_burner_power_level": { "name": "{number}번 화구 화력", "state": { @@ -1147,6 +1163,12 @@ "auto_door_opener": { "name": "오토 오픈 도어" }, + "auto_door_sound_control": { + "name": "오토 오픈 도어 소리" + }, + "auto_door_voice_control": { + "name": "오토 오픈 도어 음성 안내" + }, "auto_release_dry": { "name": "자동 문 열림 건조" }, diff --git a/custom_components/localthings/translations/nl.json b/custom_components/localthings/translations/nl.json index c135796..5b66704 100644 --- a/custom_components/localthings/translations/nl.json +++ b/custom_components/localthings/translations/nl.json @@ -225,6 +225,9 @@ "air_filter_threshold": { "name": "Filteralarmdrempel" }, + "auto_door_timer": { + "name": "Timer automatische deuropener" + }, "beverage_zone_mode": { "name": "Modus drankenzone", "state": { @@ -472,9 +475,11 @@ "kimchi_storage_crunfch": "Knapperige kimchi", "kimchi_storage_buy": "Gekochte kimchi", "storage_fridge_normal": "Koelkast", + "storage_fridge": "Koelkast", "storage_fridge_cold": "Koelkast, sterk", "storage_fridge_warm": "Koelkast, zwak", "storage_freezer_normal": "Vriezer", + "storage_freezer": "Vriezer", "storage_freezer_cold": "Vriezer, sterk", "storage_freezer_warm": "Vriezer, zwak", "kimchi_ripe_low_temp": "Kimchi rijpen, lage temperatuur", @@ -487,7 +492,8 @@ "storage_fresh_cereal": "Granen", "storage_fridge_drink": "Dranken", "storage_fresh_wine": "Wijn", - "storage_fresh_potato_banana": "Aardappelen en bananen" + "storage_fresh_potato_banana": "Aardappelen en bananen", + "newmode_kimchi_0000": "Nieuwe modus" } }, "pantry_zone_mode": { @@ -498,6 +504,16 @@ "fdr_drinks": "Dranken" } }, + "winecellar_pantry_zone_mode": { + "name": "Modus voorraadzone", + "state": { + "processed_meat": "Vleeswaren", + "cheese": "Kaas", + "nuts": "Noten", + "fruit": "Fruit", + "wine": "Wijn" + } + }, "range_burner_power_level": { "name": "Vermogensniveau brander {number}", "state": { @@ -1147,6 +1163,12 @@ "auto_door_opener": { "name": "Automatische deuropener" }, + "auto_door_sound_control": { + "name": "Geluid automatische deuropener" + }, + "auto_door_voice_control": { + "name": "Spraakbediening automatische deuropener" + }, "auto_release_dry": { "name": "Auto Release Dry" }, diff --git a/tests/fixtures/golden/refrigerator_tp1x_ref_21k_autodoor.json b/tests/fixtures/golden/refrigerator_tp1x_ref_21k_autodoor.json new file mode 100644 index 0000000..dd4c5ba --- /dev/null +++ b/tests/fixtures/golden/refrigerator_tp1x_ref_21k_autodoor.json @@ -0,0 +1,24 @@ +{ + "state_keys": [ + "ai_energy_level", + "alarm_code", + "auto_door_opener", + "auto_door_timer", + "auto_door_voice_control", + "defrost_active", + "door_onedoorfreezer_open", + "energy_kwh", + "energy_saved_kwh", + "firmware_update", + "freezer_setpoint", + "freezer_temperature", + "fridge_sound", + "power_energy_kwh", + "power_watts", + "rapid_freezing", + "rapid_fridge", + "selfcheck_error", + "selfcheck_result", + "selfcheck_status" + ] +} \ No newline at end of file diff --git a/tests/fixtures/golden/refrigerator_tp1x_ref_21k_kimchi.json b/tests/fixtures/golden/refrigerator_tp1x_ref_21k_kimchi.json new file mode 100644 index 0000000..0f91cfd --- /dev/null +++ b/tests/fixtures/golden/refrigerator_tp1x_ref_21k_kimchi.json @@ -0,0 +1,22 @@ +{ + "state_keys": [ + "alarm_code", + "auto_door_opener", + "auto_door_timer", + "auto_door_voice_control", + "door_onedoorkimchi_open", + "energy_kwh", + "energy_saved_kwh", + "firmware_update", + "fridge_sound", + "onedoor_mode", + "onedoor_rack_count", + "onedoor_ripening_remaining", + "onedoor_ripening_status", + "power_energy_kwh", + "power_watts", + "selfcheck_error", + "selfcheck_result", + "selfcheck_status" + ] +} \ No newline at end of file diff --git a/tests/fixtures/golden/refrigerator_winecellar.json b/tests/fixtures/golden/refrigerator_winecellar.json new file mode 100644 index 0000000..b8a770b --- /dev/null +++ b/tests/fixtures/golden/refrigerator_winecellar.json @@ -0,0 +1,26 @@ +{ + "state_keys": [ + "air_filter_status", + "air_filter_usage", + "alarm_code", + "auto_door_opener", + "auto_door_sound_control", + "auto_door_timer", + "auto_door_voice_control", + "cabinet_light_dim", + "cabinet_light_switch", + "door_winecellar_open", + "energy_kwh", + "energy_saved_kwh", + "firmware_update", + "fridge_sound", + "power_energy_kwh", + "power_watts", + "selfcheck_error", + "selfcheck_result", + "selfcheck_status", + "winecellar_bottom_setpoint", + "winecellar_pantry_zone_mode", + "winecellar_top_setpoint" + ] +} \ No newline at end of file diff --git a/tests/fixtures/refrigerator_tp1x_ref_21k_autodoor_device.json b/tests/fixtures/refrigerator_tp1x_ref_21k_autodoor_device.json new file mode 100644 index 0000000..27a453b --- /dev/null +++ b/tests/fixtures/refrigerator_tp1x_ref_21k_autodoor_device.json @@ -0,0 +1,343 @@ +{ + "device0": [ + { + "rt": [ + "x.com.samsung.devcol", + "oic.wk.col" + ], + "if": [ + "oic.if.baseline", + "oic.if.ll", + "oic.if.b" + ] + }, + { + "href": "/alarms/vs/0", + "rep": {} + }, + { + "href": "/temperatures/vs/0", + "rep": { + "x.com.samsung.da.items": [ + { + "x.com.samsung.da.id": "0", + "x.com.samsung.da.description": "Freezer", + "x.com.samsung.da.desired": "-19", + "x.com.samsung.da.current": "-19", + "x.com.samsung.da.maximum": "-17", + "x.com.samsung.da.minimum": "-23", + "x.com.samsung.da.unit": "Celsius" + } + ] + } + }, + { + "href": "/temperature/current/freezer/0", + "rep": { + "range": [ + -23.0, + -17.0 + ], + "units": "C", + "temperature": -19.0 + } + }, + { + "href": "/temperature/desired/freezer/0", + "rep": { + "range": [ + -23.0, + -17.0 + ], + "units": "C", + "temperature": -19.0 + } + }, + { + "href": "/selfcheck/vs/0", + "rep": { + "x.com.samsung.da.status": "Ready", + "x.com.samsung.da.result": "Success", + "x.com.samsung.da.error": [ + "DA_ERROR_NONE" + ], + "x.com.samsung.da.supportedActions": [ + "Start" + ] + } + }, + { + "href": "/energy/consumption/vs/0", + "rep": { + "x.com.samsung.da.cumulativeConsumption": "41042", + "x.com.samsung.da.cumulativePower": "466708", + "x.com.samsung.da.cumulativeUnit": "Wh", + "x.com.samsung.da.instantaneousPower": "4", + "x.com.samsung.da.instantaneousPowerUnit": "W", + "x.com.samsung.da.cumulativeSavedPower": "15759" + } + }, + { + "href": "/mode/vs/0", + "rep": { + "x.com.samsung.da.supportedModes": [ + "HOMECARE_WIZARD_V2", + "ENERGY_REPORT_MODEL", + "18K_REF_OUTDOOR_CONTROL_V2" + ] + } + }, + { + "href": "/mode/0", + "rep": { + "supportedModes": [ + "HOMECARE_WIZARD_V2", + "ENERGY_REPORT_MODEL", + "18K_REF_OUTDOOR_CONTROL_V2" + ] + } + }, + { + "href": "/realtimenotiforclient/vs/0", + "rep": { + "x.com.samsung.da.timeforshortnoti": "0", + "x.com.samsung.da.periodicnotisubscription": "true" + } + }, + { + "href": "/information/vs/0", + "rep": { + "x.com.samsung.da.modelNum": "TP1X_REF_21K|00168541|00080023001713104100000041010000", + "x.com.samsung.da.description": "TP1X_REF_21K", + "x.com.samsung.da.serialNum": "**REDACTED**", + "x.com.samsung.da.otnDUID": "**REDACTED**", + "x.com.samsung.da.diagProtocolType": "BLE_OCF", + "x.com.samsung.da.diagLogType": [ + "errCode", + "dump" + ], + "x.com.samsung.da.diagDumpType": "file", + "x.com.samsung.da.diagEndPoint": "SSM", + "x.com.samsung.da.diagMnid": "0AJT", + "x.com.samsung.da.diagSetupid": "RO6", + "x.com.samsung.da.diagMinVersion": "3.0", + "x.com.samsung.da.diagTsId": "DA01", + "x.com.samsung.da.items": [ + { + "x.com.samsung.da.id": "0", + "x.com.samsung.da.description": "WiFi Module", + "x.com.samsung.da.type": "Software", + "x.com.samsung.da.number": "250422", + "x.com.samsung.da.newVersionAvailable": "0" + }, + { + "x.com.samsung.da.id": "1", + "x.com.samsung.da.description": "Micom", + "x.com.samsung.da.type": "Firmware", + "x.com.samsung.da.number": "24101709, 23031302, 24062000, FFFFFFFF", + "x.com.samsung.da.newVersionAvailable": "0" + } + ] + } + }, + { + "href": "/file/information/vs/0", + "rep": { + "x.com.samsung.timeoffset": "+09:00", + "x.com.samsung.supprtedtype": 1 + } + }, + { + "href": "/doors/vs/0", + "rep": { + "x.com.samsung.da.items": [ + { + "x.com.samsung.da.id": "3", + "x.com.samsung.da.description": "Door", + "x.com.samsung.da.openState": "Close" + } + ] + } + }, + { + "href": "/door/onedoorfreezer/vs/0", + "rep": { + "x.com.samsung.da.openState": "Close" + } + }, + { + "href": "/configuration/vs/0", + "rep": { + "x.com.samsung.da.countryCode": "", + "x.com.samsung.da.region": "" + } + }, + { + "href": "/refrigeration/0", + "rep": { + "defrost": false, + "rapidFreeze": false, + "rapidCool": false + } + }, + { + "href": "/refrigeration/vs/0", + "rep": { + "x.com.samsung.da.rapidFreezing": "Off" + } + }, + { + "href": "/drlc/0", + "rep": { + "DRLevel": 2, + "override": false + } + }, + { + "href": "/drlc/vs/0", + "rep": { + "x.com.samsung.da.drlcLevel": "2", + "x.com.samsung.da.override": "Not_Supported", + "x.com.samsung.da.durationminutes": "1441", + "x.com.samsung.da.start": "2026-08-06T00:34:47Z", + "x.com.samsung.da.realSaving": "On" + } + }, + { + "href": "/bespoke/vs/0", + "rep": { + "x.com.samsung.da.BespokeProduct": "On" + } + }, + { + "href": "/otninformation/vs/0", + "rep": { + "x.com.samsung.da.target": "", + "x.com.samsung.da.newVersionAvailable": "false", + "otnStatus": "None", + "flashingProgress": "", + "otnList": [ + { + "type": "WIFI", + "modelId": "A-RFWW-TP1-23-COMMON", + "versions": [ + "20250422" + ], + "visVersion": "250422" + }, + { + "type": "Micom", + "modelId": "02800016854100168441", + "versions": [ + "24101709", + "23031302" + ], + "visVersion": "241017" + }, + { + "type": "Micom", + "modelId": "028070655341FFFFFFFF", + "versions": [ + "24062000", + "FFFFFFFF" + ], + "visVersion": "240620" + } + ] + } + }, + { + "href": "/status/lock/vs/0", + "rep": { + "x.com.samsung.da.ado.voicecontrol": "Off", + "x.com.samsung.da.device.sound": "On" + } + }, + { + "href": "/energy/ailevel/vs/0", + "rep": { + "aiLevel": "1", + "supportedAiLevel": [ + "1", + "2" + ] + } + }, + { + "href": "/autodoor/timer/vs/0", + "rep": { + "x.com.samsung.da.time.desired": "1", + "x.com.samsung.da.time.supportedOptions": [ + "1", + "2", + "3", + "4", + "5", + "6" + ] + } + }, + { + "href": "/autodoor/single/vs/0", + "rep": { + "x.com.samsung.da.ado.openOptions": [ + "Single" + ] + } + }, + { + "href": "/connectionconfig/vs/0", + "rep": { + "autoReconnectionMinVersion": "1.0", + "autoReconnection": "true", + "autoReconnectionProtocolType": [ + "ble_ocf", + null + ], + "supportedWiFiAuthType": [ + "OPEN", + "WEP", + "WPA-PSK", + "WPA2-PSK", + "SAE" + ], + "supportedWiFiCryptoType": [ + "TKIP", + "AES", + "WEP-64", + "WEP-128" + ], + "supportedWiFiFreq": [ + "2.4G" + ], + "calmConnectionCare": { + "version": "1.0", + "role": [ + "things" + ] + } + } + }, + { + "href": "/timezone/vs/0", + "rep": { + "timezoneid": "Asia/Seoul", + "offset": "+09:00", + "DST": "OFF" + } + }, + { + "href": "/wirelessinfo/vs/0", + "rep": { + "macaddressWiFi": "**REDACTED**", + "macaddressBLE": "**REDACTED**" + } + }, + { + "href": "/quickcontrol/info/vs/0", + "rep": { + "supportedVersion": "1.0" + } + } + ] +} \ No newline at end of file diff --git a/tests/fixtures/refrigerator_tp1x_ref_21k_kimchi_device.json b/tests/fixtures/refrigerator_tp1x_ref_21k_kimchi_device.json new file mode 100644 index 0000000..7f454c5 --- /dev/null +++ b/tests/fixtures/refrigerator_tp1x_ref_21k_kimchi_device.json @@ -0,0 +1,332 @@ +{ + "device0": [ + { + "rt": [ + "x.com.samsung.devcol", + "oic.wk.col" + ], + "if": [ + "oic.if.baseline", + "oic.if.ll", + "oic.if.b" + ] + }, + { + "href": "/alarms/vs/0", + "rep": {} + }, + { + "href": "/selfcheck/vs/0", + "rep": { + "x.com.samsung.da.status": "Ready", + "x.com.samsung.da.result": "Success", + "x.com.samsung.da.error": [ + "DA_ERROR_NONE" + ], + "x.com.samsung.da.supportedActions": [ + "Start" + ] + } + }, + { + "href": "/energy/consumption/vs/0", + "rep": { + "x.com.samsung.da.cumulativeConsumption": "53539", + "x.com.samsung.da.cumulativePower": "174590", + "x.com.samsung.da.cumulativeUnit": "Wh", + "x.com.samsung.da.instantaneousPower": "4", + "x.com.samsung.da.instantaneousPowerUnit": "W", + "x.com.samsung.da.cumulativeSavedPower": "0" + } + }, + { + "href": "/mode/vs/0", + "rep": { + "x.com.samsung.da.supportedModes": [ + "HOMECARE_WIZARD_V2", + "ENERGY_REPORT_MODEL", + "18K_KIMCHI_OUTDOOR_CONTROL" + ], + "x.com.samsung.da.modes": [ + "KIMCHI_KIMCHI_STORAGE_NORMAL", + "KIMCHI_RIPE_REMAIN_[0]:[0]", + "KIMCHIT_BOX_COUNT_[0]", + "KIMCHIM_BOX_COUNT_[0]", + "KIMCHIB_BOX_COUNT_[0]", + "KIMCHI_BOX_COUNT_[8]" + ], + "x.com.samsung.da.supportedOptions": [ + "KIMCHI_KIMCHI_STORAGE_NORMAL_[0]:[0]", + "KIMCHI_KIMCHI_STORAGE_COLD_[0]:[0]", + "KIMCHI_KIMCHI_STORAGE_WARM_[0]:[0]", + "KIMCHI_KIMCHI_STORAGE_CRUNFCH_[0]:[0]", + "KIMCHI_KIMCHI_STORAGE_BUY_[0]:[0]", + "KIMCHI_STORAGE_FRIDGE_[0]:[0]", + "KIMCHI_STORAGE_FREEZER_[0]:[0]", + "KIMCHI_KIMCHI_RIPE_NORMAL_TEMP_[2]:[12]", + "KIMCHI_KIMCHI_RIPE_LOW_TEMP_[5]:[17]" + ] + } + }, + { + "href": "/mode/0", + "rep": { + "supportedModes": [ + "HOMECARE_WIZARD_V2", + "ENERGY_REPORT_MODEL", + "18K_KIMCHI_OUTDOOR_CONTROL" + ], + "modes": [ + "KIMCHI_KIMCHI_STORAGE_NORMAL", + "KIMCHI_RIPE_REMAIN_[0]:[0]", + "KIMCHIT_BOX_COUNT_[0]", + "KIMCHIM_BOX_COUNT_[0]", + "KIMCHIB_BOX_COUNT_[0]", + "KIMCHI_BOX_COUNT_[8]" + ] + } + }, + { + "href": "/realtimenotiforclient/vs/0", + "rep": { + "x.com.samsung.da.timeforshortnoti": "0", + "x.com.samsung.da.periodicnotisubscription": "true" + } + }, + { + "href": "/information/vs/0", + "rep": { + "x.com.samsung.da.modelNum": "TP1X_REF_21K|00168041|10010022011713004101800031010000", + "x.com.samsung.da.description": "TP1X_REF_21K", + "x.com.samsung.da.serialNum": "**REDACTED**", + "x.com.samsung.da.otnDUID": "**REDACTED**", + "x.com.samsung.da.diagProtocolType": "BLE_OCF", + "x.com.samsung.da.diagLogType": [ + "errCode", + "dump" + ], + "x.com.samsung.da.diagDumpType": "file", + "x.com.samsung.da.diagEndPoint": "SSM", + "x.com.samsung.da.diagMnid": "0AJT", + "x.com.samsung.da.diagSetupid": "CO0", + "x.com.samsung.da.diagMinVersion": "3.0", + "x.com.samsung.da.diagTsId": "DA01", + "x.com.samsung.da.items": [ + { + "x.com.samsung.da.id": "0", + "x.com.samsung.da.description": "WiFi Module", + "x.com.samsung.da.type": "Software", + "x.com.samsung.da.number": "250422", + "x.com.samsung.da.newVersionAvailable": "0" + }, + { + "x.com.samsung.da.id": "1", + "x.com.samsung.da.description": "Micom", + "x.com.samsung.da.type": "Firmware", + "x.com.samsung.da.number": "24101006, 23031302, 24102200, FFFFFFFF", + "x.com.samsung.da.newVersionAvailable": "0" + } + ] + } + }, + { + "href": "/file/information/vs/0", + "rep": { + "x.com.samsung.timeoffset": "+00:00", + "x.com.samsung.supprtedtype": 1 + } + }, + { + "href": "/doors/vs/0", + "rep": { + "x.com.samsung.da.items": [ + { + "x.com.samsung.da.id": "8", + "x.com.samsung.da.description": "Door", + "x.com.samsung.da.openState": "Close" + } + ] + } + }, + { + "href": "/door/onedoorkimchi/vs/0", + "rep": { + "x.com.samsung.da.openState": "Close" + } + }, + { + "href": "/configuration/vs/0", + "rep": { + "x.com.samsung.da.countryCode": "", + "x.com.samsung.da.region": "" + } + }, + { + "href": "/drlc/0", + "rep": { + "DRLevel": 2, + "override": false + } + }, + { + "href": "/drlc/vs/0", + "rep": { + "x.com.samsung.da.drlcLevel": "2", + "x.com.samsung.da.override": "Not_Supported", + "x.com.samsung.da.durationminutes": "1441", + "x.com.samsung.da.start": "2026-08-06T00:34:47Z", + "x.com.samsung.da.realSaving": "Off" + } + }, + { + "href": "/bespoke/vs/0", + "rep": { + "x.com.samsung.da.BespokeProduct": "On" + } + }, + { + "href": "/otninformation/vs/0", + "rep": { + "x.com.samsung.da.target": "", + "x.com.samsung.da.newVersionAvailable": "false", + "otnStatus": "None", + "flashingProgress": "", + "otnList": [ + { + "type": "WIFI", + "modelId": "A-RFWW-TP1-23-COMMON", + "versions": [ + "20250422" + ], + "visVersion": "250422" + }, + { + "type": "Micom", + "modelId": "02120016804100168441", + "versions": [ + "24101006", + "23031302" + ], + "visVersion": "241010" + }, + { + "type": "Micom", + "modelId": "021270659241FFFFFFFF", + "versions": [ + "24102200", + "FFFFFFFF" + ], + "visVersion": "241022" + } + ] + } + }, + { + "href": "/status/lock/vs/0", + "rep": { + "x.com.samsung.da.ado.voicecontrol": "Off", + "x.com.samsung.da.device.sound": "On" + } + }, + { + "href": "/status/kimchi/onedoor/vs/0", + "rep": { + "x.com.samsung.da.currentMode": "KIMCHI_STORAGE_NORMAL", + "x.com.samsung.da.ripeStatus": "Off", + "x.com.samsung.da.ripeRemaintime": "0", + "x.com.samsung.da.rackCount": "8", + "x.com.samsung.da.ripeTotaltime": "0", + "x.com.samsung.da.supportMode": [ + "KIMCHI_STORAGE_NORMAL", + "KIMCHI_STORAGE_COLD", + "KIMCHI_STORAGE_WARM", + "KIMCHI_STORAGE_CRUNFCH", + "KIMCHI_STORAGE_BUY", + "STORAGE_FRIDGE", + "STORAGE_FREEZER", + "KIMCHI_RIPE_NORMAL_TEMP", + "KIMCHI_RIPE_LOW_TEMP", + "NEWMODE_KIMCHI_0000", + "NEWMODE_KIMCHI_0000", + "NEWMODE_KIMCHI_0000" + ] + } + }, + { + "href": "/autodoor/timer/vs/0", + "rep": { + "x.com.samsung.da.time.desired": "1", + "x.com.samsung.da.time.supportedOptions": [ + "1", + "2", + "3", + "4", + "5", + "6" + ] + } + }, + { + "href": "/autodoor/kimchi/vs/0", + "rep": { + "x.com.samsung.da.ado.openOptions": [ + "Single" + ] + } + }, + { + "href": "/connectionconfig/vs/0", + "rep": { + "autoReconnectionMinVersion": "1.0", + "autoReconnection": "true", + "autoReconnectionProtocolType": [ + "ble_ocf", + null + ], + "supportedWiFiAuthType": [ + "OPEN", + "WEP", + "WPA-PSK", + "WPA2-PSK", + "SAE" + ], + "supportedWiFiCryptoType": [ + "TKIP", + "AES", + "WEP-64", + "WEP-128" + ], + "supportedWiFiFreq": [ + "2.4G" + ], + "calmConnectionCare": { + "version": "1.0", + "role": [ + "things" + ] + } + } + }, + { + "href": "/timezone/vs/0", + "rep": { + "timezoneid": "Asia/Seoul", + "offset": "+09:00", + "DST": "OFF" + } + }, + { + "href": "/wirelessinfo/vs/0", + "rep": { + "macaddressWiFi": "**REDACTED**", + "macaddressBLE": "**REDACTED**" + } + }, + { + "href": "/quickcontrol/info/vs/0", + "rep": { + "supportedVersion": "1.0" + } + } + ] +} \ No newline at end of file diff --git a/tests/fixtures/refrigerator_winecellar_device.json b/tests/fixtures/refrigerator_winecellar_device.json new file mode 100644 index 0000000..c790bda --- /dev/null +++ b/tests/fixtures/refrigerator_winecellar_device.json @@ -0,0 +1,466 @@ +{ + "device0": [ + { + "rt": [ + "x.com.samsung.devcol", + "oic.wk.col" + ], + "if": [ + "oic.if.baseline", + "oic.if.ll", + "oic.if.b" + ] + }, + { + "href": "/alarms/vs/0", + "rep": { + "href": "/alarms/vs/0", + "rt": [ + "x.com.samsung.da.alarms" + ], + "if": [ + "oic.if.baseline", + "oic.if.a" + ] + } + }, + { + "href": "/autodoor/winecellar/vs/0", + "rep": { + "x.com.samsung.da.ado.openOptions": [ + "Single" + ], + "href": "/autodoor/winecellar/vs/0" + } + }, + { + "href": "/autodoor/timer/vs/0", + "rep": { + "x.com.samsung.da.time.desired": "1", + "x.com.samsung.da.time.supportedOptions": [ + "1", + "2", + "3", + "4", + "5", + "6" + ], + "href": "/autodoor/timer/vs/0" + } + }, + { + "href": "/bespoke/vs/0", + "rep": { + "x.com.samsung.da.BespokeProduct": "On", + "href": "/bespoke/vs/0" + } + }, + { + "href": "/cabinet/light/total/vs/0", + "rep": { + "x.com.samsung.da.lightLevel": "100", + "x.com.samsung.da.lightResolution": "1", + "x.com.samsung.da.lightControl.off.include": "Off", + "x.com.samsung.da.lightControl": "Off", + "x.com.samsung.da.timeout.desired": "0", + "x.com.samsung.da.timeout.supportedList": [ + "0", + "15", + "30", + "60" + ], + "href": "/cabinet/light/total/vs/0", + "rt": [ + "x.com.samsung.da.cabinetlight" + ], + "if": [ + "oic.if.baseline", + "oic.if.a" + ] + } + }, + { + "href": "/configuration/vs/0", + "rep": { + "x.com.samsung.da.region": "", + "x.com.samsung.da.countryCode": "", + "href": "/configuration/vs/0" + } + }, + { + "href": "/door/winecellar/vs/0", + "rep": { + "x.com.samsung.da.openState": "Close", + "href": "/door/winecellar/vs/0", + "rt": [ + "x.com.samsung.da.doorwinecellar" + ], + "if": [ + "oic.if.baseline", + "oic.if.s" + ] + } + }, + { + "href": "/doors/vs/0", + "rep": { + "x.com.samsung.da.items": [ + { + "x.com.samsung.da.openState": "Close", + "x.com.samsung.da.id": "9", + "x.com.samsung.da.description": "Door" + } + ], + "href": "/doors/vs/0" + } + }, + { + "href": "/drlc/vs/0", + "rep": { + "x.com.samsung.da.drlcLevel": "2", + "x.com.samsung.da.override": "Not_Supported", + "x.com.samsung.da.durationminutes": "1345", + "x.com.samsung.da.start": "2026-08-06T02:08:48Z", + "x.com.samsung.da.realSaving": "On", + "href": "/drlc/vs/0" + } + }, + { + "href": "/energy/consumption/vs/0", + "rep": { + "x.com.samsung.da.cumulativeConsumption": "19073", + "x.com.samsung.da.instantaneousPower": "5", + "x.com.samsung.da.cumulativePower": "210483", + "x.com.samsung.da.cumulativeSavedPower": "15696", + "x.com.samsung.da.cumulativeUnit": "Wh", + "x.com.samsung.da.instantaneousPowerUnit": "W", + "href": "/energy/consumption/vs/0" + } + }, + { + "href": "/file/information/vs/0", + "rep": { + "x.com.samsung.timeoffset": "+00:00", + "x.com.samsung.supprtedtype": 1, + "href": "/file/information/vs/0" + } + }, + { + "href": "/filter/deodorfilter/vs/0", + "rep": { + "x.com.samsung.da.filterUsage": "-1", + "x.com.samsung.da.filterUsageResolution": "1", + "x.com.samsung.da.filterResetType": [ + "replaceable" + ], + "x.com.samsung.da.filterStatus": "normal", + "href": "/filter/deodorfilter/vs/0" + } + }, + { + "href": "/information/vs/0", + "rep": { + "x.com.samsung.da.modelNum": "TP1X_REF_21K|00146141|000B0020001513924100000011000000", + "x.com.samsung.da.description": "TP1X_REF_21K", + "x.com.samsung.da.serialNum": "**REDACTED**", + "x.com.samsung.da.otnDUID": "**REDACTED**", + "x.com.samsung.da.diagDumpType": "file", + "x.com.samsung.da.diagEndPoint": "SSM", + "x.com.samsung.da.diagLogType": [ + "errCode", + "dump" + ], + "x.com.samsung.da.diagMnid": "0AJT", + "x.com.samsung.da.diagSetupid": "531", + "x.com.samsung.da.diagProtocolType": "WIFI_HTTPS", + "x.com.samsung.da.diagMinVersion": "1.0", + "x.com.samsung.da.items": [ + { + "x.com.samsung.da.id": "0", + "x.com.samsung.da.description": "WiFi Module", + "x.com.samsung.da.type": "Software", + "x.com.samsung.da.number": "260619", + "x.com.samsung.da.newVersionAvailable": "0" + }, + { + "x.com.samsung.da.id": "1", + "x.com.samsung.da.description": "Micom", + "x.com.samsung.da.type": "Firmware", + "x.com.samsung.da.number": "24102313, 2201030E", + "x.com.samsung.da.newVersionAvailable": "0" + } + ], + "href": "/information/vs/0" + } + }, + { + "href": "/status/lock/vs/0", + "rep": { + "x.com.samsung.da.ado.voicecontrol": "Off", + "x.com.samsung.da.ado.soundcontrol": "On", + "href": "/status/lock/vs/0", + "rt": [ + "x.com.samsung.da.lockstatus" + ], + "if": [ + "oic.if.baseline", + "oic.if.a" + ] + } + }, + { + "href": "/mode/vs/0", + "rep": { + "x.com.samsung.da.modes": [ + "AIRFILTER_DISABLE" + ], + "x.com.samsung.da.supportedModes": [ + "HOMECARE_WIZARD_V2", + "ENERGY_REPORT_MODEL", + "WINE_T_TOTRACK_[6]", + "WINE_T_BOTTLE_RACK_[7]", + "WINE_B_TOTRACK_[4]", + "WINE_B_BOTTLE_RACK_[7]", + "WINE_PRESENTATION_[6]", + "WINE_BTM_BOTTLE_[20]", + "WINE_PANTRY_BOTTLE_[5]" + ], + "href": "/mode/vs/0", + "rt": [ + "x.com.samsung.da.mode" + ], + "if": [ + "oic.if.baseline", + "oic.if.a" + ] + } + }, + { + "href": "/realtimenotiforclient/vs/0", + "rep": { + "x.com.samsung.da.timeforshortnoti": "0", + "x.com.samsung.da.periodicnotisubscription": "true", + "href": "/realtimenotiforclient/vs/0" + } + }, + { + "href": "/runningmode/vs/0", + "rep": { + "x.com.samsung.da.runningMode": 0, + "href": "/runningmode/vs/0" + } + }, + { + "href": "/selfcheck/vs/0", + "rep": { + "x.com.samsung.da.supportedActions": [ + "Start" + ], + "x.com.samsung.da.status": "Ready", + "x.com.samsung.da.result": "Success", + "x.com.samsung.da.error": [ + "ErrorCode_None" + ], + "href": "/selfcheck/vs/0" + } + }, + { + "href": "/temperature/desired/winecellar/top/vs/0", + "rep": { + "temperature": 13.0, + "range": [ + 4.0, + 18.0 + ], + "units": "C", + "href": "/temperature/desired/winecellar/top/vs/0", + "rt": [ + "oic.r.temperature" + ], + "if": [ + "oic.if.baseline", + "oic.if.a" + ] + } + }, + { + "href": "/temperature/desired/winecellar/bottom/vs/0", + "rep": { + "temperature": 7.0, + "range": [ + 4.0, + 18.0 + ], + "units": "C", + "href": "/temperature/desired/winecellar/bottom/vs/0", + "rt": [ + "oic.r.temperature" + ], + "if": [ + "oic.if.baseline", + "oic.if.a" + ] + } + }, + { + "href": "/temperatures/vs/0", + "rep": { + "x.com.samsung.da.items": [ + { + "x.com.samsung.da.id": "2", + "x.com.samsung.da.description": "WineCellar-top", + "x.com.samsung.da.desired": "13", + "x.com.samsung.da.current": "13", + "x.com.samsung.da.maximum": "18", + "x.com.samsung.da.minimum": "4", + "x.com.samsung.da.unit": "Celsius" + }, + { + "x.com.samsung.da.id": "3", + "x.com.samsung.da.description": "WineCellar-bottom", + "x.com.samsung.da.desired": "7", + "x.com.samsung.da.current": "7", + "x.com.samsung.da.maximum": "18", + "x.com.samsung.da.minimum": "4", + "x.com.samsung.da.unit": "Celsius" + } + ], + "href": "/temperatures/vs/0" + } + }, + { + "href": "/information/winecellar/vs/0", + "rep": { + "x.com.samsung.da.tbl.revision": "1", + "href": "/information/winecellar/vs/0" + } + }, + { + "href": "/status/winecellar/pantry/one/vs/0", + "rep": { + "x.com.samsung.da.mode": "Fruit", + "x.com.samsung.range": [ + "4", + "13" + ], + "x.com.samsung.da.room": "0x10", + "x.com.samsung.da.name": "MULTI_PANTRY", + "x.com.samsung.da.unit": "Celsius", + "x.com.samsung.da.supportedOptions": [ + "Processed_Meat", + "Cheese", + "Nuts", + "Fruit", + "Wine" + ], + "href": "/status/winecellar/pantry/one/vs/0" + } + }, + { + "href": "/otninformation/vs/0", + "rep": { + "x.com.samsung.da.target": "", + "x.com.samsung.da.newVersionAvailable": "false", + "otnStatus": "None", + "flashingProgress": "0", + "otnCompleteDate": "noHistory", + "scheduledTime": "None", + "swVersionInfo": { + "platform": "Tizen Lite", + "oneUiVersion": "7.0 Refrigerator", + "osVersion": "4.0" + }, + "otnList": [ + { + "type": "WIFI", + "modelId": "A-RFWW-TP1-24-T4-RE2", + "versions": [ + "20260619" + ], + "visVersion": "260619" + }, + { + "type": "Micom", + "modelId": "02900014614100146441", + "versions": [ + "24102313", + "2201030E" + ], + "visVersion": "241023" + } + ] + } + }, + { + "href": "/timezone/vs/0", + "rep": { + "timezoneid": "Asia/Seoul", + "offset": "+09:00", + "DST": "OFF" + } + }, + { + "href": "/connectionconfig/vs/0", + "rep": { + "autoReconnectionMinVersion": "1.0", + "autoReconnection": "true", + "autoReconnectionProtocolType": [ + "helper_hotspot", + "ble_ocf" + ], + "supportedWiFiAuthType": [ + "OPEN", + "WEP", + "WPA-PSK", + "WPA2-PSK", + "SAE" + ], + "supportedWiFiCryptoType": [ + "TKIP", + "AES", + "WEP-64", + "WEP-128" + ], + "supportedWiFiFreq": [ + "2.4G" + ], + "calmConnectionCare": { + "version": "1.0", + "role": [ + "things" + ] + } + } + }, + { + "href": "/wirelessinfo/vs/0", + "rep": { + "macaddressWiFi": "**REDACTED**", + "macaddressBLE": "**REDACTED**", + "connectedApSsid": "Home_IoT" + } + }, + { + "href": "/quickcontrol/info/vs/0", + "rep": { + "supportedVersion": "1.0" + } + }, + { + "href": "/dginformation/vs/0", + "rep": { + "enrolmentstatus": "Unknown", + "devicestate": "Unknown", + "lockstatus": "Unknown", + "nextduedate": "", + "workingminutes": 0, + "paymentinfo": { + "emiplan": "Unknown", + "currency": "Unknown", + "totalemi": 0, + "totalemipaid": 0 + } + } + } + ] +} \ No newline at end of file diff --git a/tests/test_golden_regression.py b/tests/test_golden_regression.py index 1d453f0..e626e1d 100644 --- a/tests/test_golden_regression.py +++ b/tests/test_golden_regression.py @@ -1407,6 +1407,64 @@ def test_registry_reproduces_golden_state_keys_for_refrigerator_tp1x_ref_21k_def ) +def test_registry_reproduces_golden_state_keys_for_refrigerator_tp1x_ref_21k_autodoor(): + """TP1X_REF_21K, single-freezer-door Auto Door Open variant (issue + #328): /autodoor/single/vs/0 + /autodoor/timer/vs/0, plus + STATUS_LOCK's ado.voicecontrol sibling toggle.""" + from tests.conftest import _load_device + + resources = _load_device("refrigerator_tp1x_ref_21k_autodoor") + golden = json.loads((GOLDEN / "refrigerator_tp1x_ref_21k_autodoor.json").read_text()) + state_keys = _new_state_keys("refrigerator_tp1x_ref_21k_autodoor", resources) + assert set(state_keys) == set(golden["state_keys"]), ( + f"state_keys mismatch:\n" + f" extra: {sorted(set(state_keys) - set(golden['state_keys']))}\n" + f" missing: {sorted(set(golden['state_keys']) - set(state_keys))}" + ) + + +def test_registry_reproduces_golden_state_keys_for_refrigerator_tp1x_ref_21k_kimchi(): + """Kimchi-refrigerator Auto Door Open variant (issue #328) -- resolves + via /oic/d's oic.d.krefrigerator, not modelNum (the board is the same + TP1X_REF_21K as the regular fridge, so only the OCF type tells them + apart before checking hrefs).""" + from tests.conftest import _load_device + + resources = _load_device("refrigerator_tp1x_ref_21k_kimchi") + golden = json.loads((GOLDEN / "refrigerator_tp1x_ref_21k_kimchi.json").read_text()) + state_keys = _new_state_keys( + "refrigerator_tp1x_ref_21k_kimchi", + resources, + device_types=("oic.wk.d", "oic.d.krefrigerator"), + ) + assert set(state_keys) == set(golden["state_keys"]), ( + f"state_keys mismatch:\n" + f" extra: {sorted(set(state_keys) - set(golden['state_keys']))}\n" + f" missing: {sorted(set(golden['state_keys']) - set(state_keys))}" + ) + + +def test_registry_reproduces_golden_state_keys_for_refrigerator_winecellar(): + """Wine-cellar refrigerator variant (issue #328) -- resolves via /oic/d's + x.com.st.d.winecellar, same TP1X_REF_21K board as the other two. Adds + the deodorizing filter at its own href, the multi-compartment pantry + select, and the table-revision info resource.""" + from tests.conftest import _load_device + + resources = _load_device("refrigerator_winecellar") + golden = json.loads((GOLDEN / "refrigerator_winecellar.json").read_text()) + state_keys = _new_state_keys( + "refrigerator_winecellar", + resources, + device_types=("oic.wk.d", "x.com.st.d.winecellar"), + ) + assert set(state_keys) == set(golden["state_keys"]), ( + f"state_keys mismatch:\n" + f" extra: {sorted(set(state_keys) - set(golden['state_keys']))}\n" + f" missing: {sorted(set(golden['state_keys']) - set(state_keys))}" + ) + + def test_resources_from_batch_preferred_over_flat(): from tests.conftest import _resources_from_dump diff --git a/tests/test_refrigerator_tp1x_ref_21k_autodoor_capabilities.py b/tests/test_refrigerator_tp1x_ref_21k_autodoor_capabilities.py new file mode 100644 index 0000000..69b49b7 --- /dev/null +++ b/tests/test_refrigerator_tp1x_ref_21k_autodoor_capabilities.py @@ -0,0 +1,71 @@ +"""Auto Door Open (issue #328, TP1X_REF_21K single-freezer-door variant). + +`/status/lock/vs/0`'s ado.devicecontrol switch was already modeled +(STATUS_LOCK.auto_door_opener); this dump adds the paired voice-feedback +toggle, the door-hold timer, and the per-variant capability declaration +href -- see fridge.py's AUTO_DOOR_TIMER/AUTO_DOOR_SINGLE comments. +""" + +from custom_components.localthings.registry.adapter import flatten +from custom_components.localthings.registry.by_type import for_device_by_model +from custom_components.localthings.registry.capabilities import fridge +from custom_components.localthings.registry.discovery import discover +from tests.conftest import _load_device + +FIXTURE = "refrigerator_tp1x_ref_21k_autodoor" + + +def _fridge(): + resources = _load_device(FIXTURE) + info = resources["/information/vs/0"] + reg = for_device_by_model( + info["x.com.samsung.da.modelNum"], info["x.com.samsung.da.description"] + ) + return reg, resources + + +def _state(): + reg, resources = _fridge() + bound = discover(resources, reg.capabilities, reg.pattern_capabilities) + return flatten(bound, resources) + + +def test_resolves_to_refrigerator_registry(): + reg, _ = _fridge() + assert reg is not None and reg.name == "refrigerator" + + +def test_no_unbound_hrefs(): + reg, resources = _fridge() + unbound = [] + discover(resources, reg.capabilities, reg.pattern_capabilities, log=unbound.append) + assert unbound == [] + + +def test_auto_door_voice_control_reads_status_lock(): + state = _state() + assert state["auto_door_opener"] is False + assert state["auto_door_voice_control"] is False + # This variant's dump has no ado.soundcontrol field -- gated absent. + assert "auto_door_sound_control" not in state + + +def test_auto_door_timer_reads_its_own_options(): + desc = next(e for e in fridge.AUTO_DOOR_TIMER.entities if e.key == "auto_door_timer") + rep = { + "x.com.samsung.da.time.desired": "1", + "x.com.samsung.da.time.supportedOptions": ["1", "2", "3", "4", "5", "6"], + } + assert desc.value_fn(rep["x.com.samsung.da.time.desired"]) == "1" + assert desc.options_field == "x.com.samsung.da.time.supportedOptions" + assert desc.write_fn is not None + assert desc.write_fn("3", rep) == ( + ["autodoor", "timer", "vs", "0"], + {"x.com.samsung.da.time.desired": "3"}, + ) + + +def test_auto_door_single_variant_href_bound_with_no_entities(): + """/autodoor/single/vs/0 only ever declares openOptions=['Single'] -- + no paired current/desired field to expose, so it's coverage-only.""" + assert fridge.AUTO_DOOR_SINGLE.entities == () diff --git a/tests/test_refrigerator_tp1x_ref_21k_kimchi_capabilities.py b/tests/test_refrigerator_tp1x_ref_21k_kimchi_capabilities.py new file mode 100644 index 0000000..39c20c3 --- /dev/null +++ b/tests/test_refrigerator_tp1x_ref_21k_kimchi_capabilities.py @@ -0,0 +1,54 @@ +"""Kimchi-refrigerator Auto Door Open variant (issue #328, +oic.d.krefrigerator device type -- TP1X_REF_21K board, single kimchi +compartment). Same STATUS_LOCK/AUTO_DOOR_TIMER shape as the regular +TP1X_REF_21K, at the kimchi-specific /autodoor/kimchi/vs/0 declaration +href. +""" + +from custom_components.localthings.registry.adapter import flatten +from custom_components.localthings.registry.by_type import for_device_by_oic_type, resolve +from custom_components.localthings.registry.capabilities import fridge +from custom_components.localthings.registry.discovery import discover +from tests.conftest import _load_device + +FIXTURE = "refrigerator_tp1x_ref_21k_kimchi" +DEVICE_TYPES = ("oic.wk.d", "oic.d.krefrigerator") + + +def _fridge(): + resources = _load_device(FIXTURE) + reg = resolve(resources, device_types=DEVICE_TYPES) + return reg, resources + + +def _state(): + reg, resources = _fridge() + bound = discover(resources, reg.capabilities, reg.pattern_capabilities) + return flatten(bound, resources) + + +def test_resolves_via_oic_type(): + reg = for_device_by_oic_type(DEVICE_TYPES) + assert reg is not None and reg.name == "refrigerator" + + +def test_resolves_to_refrigerator_registry(): + reg, _ = _fridge() + assert reg is not None and reg.name == "refrigerator" + + +def test_no_unbound_hrefs(): + reg, resources = _fridge() + unbound = [] + discover(resources, reg.capabilities, reg.pattern_capabilities, log=unbound.append) + assert unbound == [] + + +def test_auto_door_opener_present(): + state = _state() + assert "auto_door_opener" in state + assert "auto_door_timer" in state + + +def test_auto_door_kimchi_variant_href_bound_with_no_entities(): + assert fridge.AUTO_DOOR_KIMCHI.entities == () diff --git a/tests/test_refrigerator_winecellar_capabilities.py b/tests/test_refrigerator_winecellar_capabilities.py new file mode 100644 index 0000000..895896b --- /dev/null +++ b/tests/test_refrigerator_winecellar_capabilities.py @@ -0,0 +1,78 @@ +"""Wine-cellar refrigerator variant (issue #328, x.com.st.d.winecellar +device type -- RW33C99B1TFG-class, TP1X_REF_21K board). Auto Door Open's +winecellar-specific declaration href, a deodorizing filter reported at a +different href than the regular AIR_FILTER, a multi-compartment pantry +select, and a table-revision info resource -- all new coverage this dump +surfaced. +""" + +from custom_components.localthings.registry.adapter import flatten +from custom_components.localthings.registry.by_type import for_device_by_oic_type, resolve +from custom_components.localthings.registry.capabilities import fridge +from custom_components.localthings.registry.discovery import discover +from tests.conftest import _load_device + +FIXTURE = "refrigerator_winecellar" +DEVICE_TYPES = ("oic.wk.d", "x.com.st.d.winecellar") + + +def _fridge(): + resources = _load_device(FIXTURE) + reg = resolve(resources, device_types=DEVICE_TYPES) + return reg, resources + + +def _state(): + reg, resources = _fridge() + bound = discover(resources, reg.capabilities, reg.pattern_capabilities) + return flatten(bound, resources) + + +def test_resolves_via_oic_type(): + reg = for_device_by_oic_type(DEVICE_TYPES) + assert reg is not None and reg.name == "refrigerator" + + +def test_no_unbound_hrefs(): + reg, resources = _fridge() + unbound = [] + discover(resources, reg.capabilities, reg.pattern_capabilities, log=unbound.append) + assert unbound == [] + + +def test_auto_door_sound_control_present_here_only(): + """This variant's dump is the only one that carries + ado.soundcontrol -- the other TP1X_REF_21K auto-door fixtures don't.""" + state = _state() + assert state["auto_door_sound_control"] is True + assert state["auto_door_voice_control"] is False + + +def test_deodor_filter_reuses_air_filter_shape(): + """Same entities as fridge.AIR_FILTER (see its own already-confirmed + percentage-not-raw-count reasoning), bound at this variant's own href.""" + assert fridge.DEODOR_FILTER.entities is fridge.AIR_FILTER.entities + state = _state() + assert state["air_filter_status"] == "normal" + # The device's own sentinel, relayed as-is -- not a real 0-100 reading + # on this unit, and no second dump to say what else -1 could mean. + assert state["air_filter_usage"] == -1 + + +def test_winecellar_pantry_zone_mode_options_and_write(): + desc = next( + e for e in fridge.WINECELLAR_PANTRY_ZONE.entities if e.key == "winecellar_pantry_zone_mode" + ) + assert desc.options_field == "x.com.samsung.da.supportedOptions" + assert desc.write_fn is not None + assert desc.write_fn("Cheese", {}) == ( + ["status", "winecellar", "pantry", "one", "vs", "0"], + {"x.com.samsung.da.mode": "Cheese"}, + ) + state = _state() + assert state["winecellar_pantry_zone_mode"] == "Fruit" + + +def test_winecellar_variant_and_info_hrefs_bound_with_no_entities(): + assert fridge.AUTO_DOOR_WINECELLAR.entities == () + assert fridge.WINECELLAR_INFO.entities == ()