Merge remote-tracking branch 'origin/main' into claude/code-comments-guidelines-jiv4ta
This commit is contained in:
@@ -194,6 +194,8 @@ Samsung's firmware occasionally drops the DTLS session briefly — this is norma
|
||||
|
||||
If reconnects become persistent (more than a handful per minute), something's actually wrong. Check the appliance's Wi-Fi link first, then look for a competing DTLS client on the LAN — only one active session per appliance is allowed at a time.
|
||||
|
||||
Deregistering a device in SmartThings causes a reset of its network settings as soon as it accesses Samsung's servers, dropping it off Wi-Fi until it's re-onboarded through the SmartThings app. As such, consider keeping devices registered even if egress-blocked, to avoid them resetting upon brief internet access.
|
||||
|
||||
### Multi-subdevice ("2-in-1") air conditioner systems
|
||||
|
||||
Some Samsung installs run more than one indoor subdevice off a single outdoor unit, all reachable over the *one* IP/DTLS session your config entry connects to (a floor-standing + wall-mounted 2-in-1 is a common shape). The integration discovers any sibling subdevices automatically, once, right after the first successful poll — there's nothing to configure. Each discovered subdevice gets its own HA device (linked to the main one via "via device") and its own `climate` card, so it lands in its own room in the dashboard instead of being invisible or mixed into the master's state.
|
||||
|
||||
@@ -14,6 +14,7 @@ from dataclasses import replace
|
||||
from ..capability import Capability
|
||||
from ..entities import (
|
||||
BinarySensorDesc,
|
||||
ButtonDesc,
|
||||
ClimateDesc,
|
||||
NumberDesc,
|
||||
SelectDesc,
|
||||
@@ -480,6 +481,45 @@ CLIMATE = Capability(
|
||||
icon="mdi:fan-auto",
|
||||
entity_category="config",
|
||||
),
|
||||
# A drying cycle the unit runs after cooling, to keep the coil from going
|
||||
# mouldy. Three tokens describe it and the switch above only covered the
|
||||
# first: Autoclean_ is the setting, AutocleanProgress_ is how far a
|
||||
# running cycle has got, and StopAutoClean_ is a channel for ending one
|
||||
# early -- its presence is what says the appliance takes that at all
|
||||
# (the app gates its own stop button on exactly that), and the value it
|
||||
# reports while nothing is running is Idle.
|
||||
#
|
||||
# The percentage scale is the app's own: `<progress max="100">` with the
|
||||
# token rendered as "{{value}}%" beside it. An idle unit here reports 1
|
||||
# rather than 0, the same floor the laundry firmware's progressPercentage
|
||||
# sits at when Ready, so 0-vs-1 is not a reliable "is it running" test --
|
||||
# which is why the button below is not gated on it.
|
||||
#
|
||||
# The sensor shares AUTO_CLEAN's catalog entry, like auto_clean_legacy
|
||||
# above: same figure, different board generation. Distinct key, so
|
||||
# nothing collides if a board ever reported both.
|
||||
SensorDesc(
|
||||
key="auto_clean_progress_legacy",
|
||||
translation_key="auto_clean_progress",
|
||||
rep_fn=_option_token_num("AutocleanProgress"),
|
||||
exists_fn=_has_option_token("AutocleanProgress"),
|
||||
unit="%",
|
||||
state_class="measurement",
|
||||
icon="mdi:progress-check",
|
||||
entity_category="diagnostic",
|
||||
),
|
||||
ButtonDesc(
|
||||
key="auto_clean_stop",
|
||||
field="",
|
||||
payload="StopAutoClean_Set",
|
||||
icon="mdi:fan-off",
|
||||
entity_category="config",
|
||||
exists_fn=_has_option_token("StopAutoClean"),
|
||||
write_fn=lambda p, rep, href=None: (
|
||||
["mode", "vs", "0"],
|
||||
{"x.com.samsung.da.options": [p]},
|
||||
),
|
||||
),
|
||||
SwitchDesc(
|
||||
key="air_monitoring",
|
||||
rep_fn=_option_token_on("AirMonitoring"),
|
||||
|
||||
@@ -117,6 +117,9 @@
|
||||
"diagnosis_start": {
|
||||
"name": "Spustit diagnostiku"
|
||||
},
|
||||
"auto_clean_stop": {
|
||||
"name": "Zastavit samočištění"
|
||||
},
|
||||
"pause": {
|
||||
"name": "Pozastavit"
|
||||
},
|
||||
|
||||
@@ -117,6 +117,9 @@
|
||||
"diagnosis_start": {
|
||||
"name": "Start diagnosis"
|
||||
},
|
||||
"auto_clean_stop": {
|
||||
"name": "Stop auto clean"
|
||||
},
|
||||
"pause": {
|
||||
"name": "Pause"
|
||||
},
|
||||
|
||||
@@ -239,6 +239,9 @@
|
||||
"diagnosis_start": {
|
||||
"name": "Iniciar diagnóstico"
|
||||
},
|
||||
"auto_clean_stop": {
|
||||
"name": "Detener la autolimpieza"
|
||||
},
|
||||
"pause": {
|
||||
"name": "Pausar"
|
||||
},
|
||||
|
||||
@@ -117,6 +117,9 @@
|
||||
"diagnosis_start": {
|
||||
"name": "Avvia diagnosi"
|
||||
},
|
||||
"auto_clean_stop": {
|
||||
"name": "Arresta autopulizia"
|
||||
},
|
||||
"pause": {
|
||||
"name": "Pausa"
|
||||
},
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -117,6 +117,9 @@
|
||||
"diagnosis_start": {
|
||||
"name": "Diagnose starten"
|
||||
},
|
||||
"auto_clean_stop": {
|
||||
"name": "Zelfreiniging stoppen"
|
||||
},
|
||||
"pause": {
|
||||
"name": "Pauzeren"
|
||||
},
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
"state_keys": [
|
||||
"alarm_code",
|
||||
"auto_clean_legacy",
|
||||
"auto_clean_progress_legacy",
|
||||
"beep",
|
||||
"clean_level",
|
||||
"climate",
|
||||
@@ -18,6 +19,7 @@
|
||||
"super_fine_dust",
|
||||
"subdevice1_alarm_code",
|
||||
"subdevice1_auto_clean_legacy",
|
||||
"subdevice1_auto_clean_progress_legacy",
|
||||
"subdevice1_beep",
|
||||
"subdevice1_climate",
|
||||
"subdevice1_current_temperature_c",
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
"air_monitoring",
|
||||
"alarm_code",
|
||||
"auto_clean_legacy",
|
||||
"auto_clean_progress_legacy",
|
||||
"beep",
|
||||
"climate",
|
||||
"current_temperature_c",
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
"air_monitoring",
|
||||
"alarm_code",
|
||||
"auto_clean_legacy",
|
||||
"auto_clean_progress_legacy",
|
||||
"beep",
|
||||
"climate",
|
||||
"current_temperature_c",
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
"air_monitoring",
|
||||
"alarm_code",
|
||||
"auto_clean_legacy",
|
||||
"auto_clean_progress_legacy",
|
||||
"beep",
|
||||
"climate",
|
||||
"current_temperature_c",
|
||||
|
||||
@@ -235,6 +235,32 @@ def test_filter_alarm_time_reads_the_threshold_and_writes_one_token():
|
||||
)
|
||||
|
||||
|
||||
def test_auto_clean_progress_and_stop_come_off_their_own_tokens():
|
||||
"""Three tokens describe the drying cycle and the switch only covered the
|
||||
first. AutocleanProgress_ is a percentage -- the app renders it into a
|
||||
`<progress max="100">` beside a "{{value}}%" label -- and StopAutoClean_ is
|
||||
the channel for ending a cycle early, whose presence is what says the
|
||||
appliance accepts that at all (the fixture reports Idle, this unit's
|
||||
resting value)."""
|
||||
assert _state()["auto_clean_progress_legacy"] == 1.0 # the fixture's own value
|
||||
|
||||
desc = _desc(_load_device(FIXTURE), "auto_clean_stop")
|
||||
assert desc is not None
|
||||
assert desc.write_fn(desc.payload, {}) == (
|
||||
["mode", "vs", "0"],
|
||||
{"x.com.samsung.da.options": ["StopAutoClean_Set"]},
|
||||
)
|
||||
|
||||
|
||||
def test_auto_clean_stop_stays_off_boards_without_the_token():
|
||||
"""Newer boards run the same cycle off /option/autoclean/vs/0 and say
|
||||
nothing about stopping it, so writing a legacy token there would be a
|
||||
guess."""
|
||||
newer = _load_device("airconditioner_tp1x_rac")
|
||||
assert _desc(newer, "auto_clean_stop") is None
|
||||
assert "auto_clean_progress_legacy" not in _state("airconditioner_tp1x_rac")
|
||||
|
||||
|
||||
def test_filter_alarm_time_stays_off_boards_with_a_real_threshold_resource():
|
||||
"""Newer boards carry air_filter_threshold off supportedFilterDesiredUsage;
|
||||
two thresholds on one device would be a coin flip for the user.
|
||||
|
||||
Reference in New Issue
Block a user