719 Commits
Author SHA1 Message Date
Marc Billow cdaff4a1ca Merge pull request #272 from mbillow/claude/issue-triaging-fuq5sn
Issue triage batch: dehumidifier, AC, dryer, fridge, dishwasher, climate fixes
2026-08-03 15:45:12 -04:00
Marc Billow 6a6eef25b8 Fix ty type error in test_dryer_drum_clean.py
for_device_by_model returns DeviceRegistry | None; accessing .capabilities
directly off the inline call result left the None case unnarrowed. Switched
to the same reg/resources-tuple helper pattern every other by-model test
file in this suite already uses, which ty resolves cleanly.
2026-08-03 19:38:57 +00:00
Marc Billow da25d567cb Remove pointless catalog-literal tests; document the anti-pattern
Two tests added while triaging #244/#226 just re-asserted a translation
string against the catalog entry that had been written moments earlier
(dryer_cycle_table_03's '51'/'53'/'4e', dishwasher_cycle's '83'/'86').
Neither exercises any code path -- they pass by construction and only
break when someone later edits the label text for wording, not when the
actual code/value mapping regresses. tests/test_translations.py already
holds the invariants that matter for catalog data.

Documents the anti-pattern in the adding-device-support skill so future
translation-only fixes don't reach for this pattern again.
2026-08-03 19:32:44 +00:00
Marc Billow e89aa4bab5 Fix transposed Normal/Express 60 dishwasher cycle labels (issue #226)
'83' and '86' were swapped in the dishwasher_cycle catalog. Both the
original DW9000F-class fixture this table was built from and the issue
#226 reporter's board share the identical DeviceType_0812, and the
original fixture's own editCourseList puts the two codes back to back
(positions 4-5) -- a plausible adjacent-pair transcription slip. The
reporter's live confirmation (selecting 'Normal' ran the physical Express
60 program and vice versa) settles which way: '86' is Express 60, '83' is
Normal.

The energy-sensor part of the same issue was already resolved per the
issue thread (the device genuinely doesn't report usage, so the sensor's
removal was correct) -- not touched here.
2026-08-03 19:30:02 +00:00
Marc Billow fb5ed32b0f Add discrete freezer setpoint support for TP1X_REF_21K (issue #229)
The reporter's fridge/freezer combo reports the issue #186 discrete
definite-setpoint pattern on both compartments, but only the cooler half
was modeled -- /temperature/definite/freezer/vs/0 was unbound. Adds
DEFINITE_TEMPERATURE_FREEZER, identical shape to the existing cooler
capability (same fields, just negative supportedList values).
2026-08-03 19:24:59 +00:00
Marc Billow 1becd85f6e Fix HOMECARE_WIZARD_V2 false-positive warning and None entity_id log (#235)
HOMECARE_WIZARD_V2 appears in /mode/vs/0's supportedModes on TP2X_RAC_20K
units but is a capability/option flag echoed from
/configuration/vs/0's airconOptionList, not a selectable HVAC mode -- the
unit's current mode never reports it. Added to a new
_NON_HVAC_OPTION_CODES set that's dropped silently, so hvac_mode/hvac_modes
stop tripping the issue #93 unmapped-mode warning for it on every start.

Also fixes _warn_unmapped logging "None: device mode ..." during setup's
first discovery pass, before the entity is added to hass and entity_id is
assigned -- falls back to unique_id (set eagerly in __init__), so multiple
same-type devices are distinguishable in the log.
2026-08-03 19:22:06 +00:00
Marc Billow 0cc9486ad5 Add missing dryer cycle labels for DV90DG6845LHU5 (issue #244)
Codes 51 (Eco Cotton), 53 (AI Dry+), and 4e (Self Dry) were confirmed by
the reporter selecting each program on the physical appliance and reading
back the resulting raw course code, same table (Table_03) as the existing
issue #80 confirmations.

Also fixes an import-sort lint error left over in by_type/dehumidifier.py
and a stale comment in dryer.py claiming codes 21/4c were still
unidentified when the catalog already had them.
2026-08-03 19:17:41 +00:00
Marc Billow e6d7dcddc8 Add dryer Drum Clean+ tracking; fix multi-entry DrumCleanLog parsing (#258)
Dryers report the same DrumCleanProposal_/WashingTimes_/DrumCleanLog_
options[] tokens washer.py already models for issue #9, so
drum_clean_cycles_remaining/drum_clean_last_cleaned move to laundry.py and
get bound on dryer's /course/vs/0 too.

DrumCleanLog_ on the reporter's dump is a '|'-joined history of every past
clean rather than washer's single bare timestamp -- the shared helper now
takes the last (most recent) entry, which turns out to also fix a latent
bug on four existing washer fixtures whose own DrumCleanLog_ was already
multi-entry and silently failing to parse into drum_clean_last_cleaned.

No heat-exchanger-clean tracking was found in either dump #258 supplied;
noted in dryer.py so a future report knows this was checked.
2026-08-03 19:14:29 +00:00
Marc Billow cf09247e39 Add AC UV LED, ventilation alarm, and PM1 filter support (issue #270)
TP1X_FAC_TIME_23K reports three previously unbound hrefs: a UV-C
sterilization LED and a ventilation-reminder alarm (both plain On/Off
toggles), and a second PM1-rated dust filter with no live usage/status
fields on this particular dump.

The PM1 filter capability gates each entity on its own field's presence
rather than a blanket ignore, since the TP1X_DA-AC-CAC-01001_0000 cassette
AC (issue #191) reports the same href with full live data -- this also
closes two of that device's ten documented coverage gaps (UV LED and the
PM1 filter) as a side effect.
2026-08-03 19:07:03 +00:00
Marc Billow 0994ca487a Restore CRLF line endings in cs.json
The previous commit's translation update rewrote this file with LF
endings; every other language file in the catalog already uses LF, but
this one was CRLF before that change.
2026-08-03 19:00:06 +00:00
Marc Billow 3ef64eae52 Add dehumidifier display switch and watertank lighting (issues #271, #231)
The TP1X_DA_AC_DHM_01001_0000 revision (model AY70H18100GTD) additionally
reports /display/vs/0 (same shape as air_purifier's screen toggle, reused
directly) and /watertank/lighting/vs/0 (on/off, color, and brightness for
the tank's ambient light, plus a diagnostic alarm-status flag). Both issues
submitted the identical dump, so one fix covers both reports.

Also adds x.com.st.d.dehumidifier to the /oic/d device-type routing table
now that a dump confirms it.
2026-08-03 18:59:22 +00:00
kkqq9320 69844d9829 fix(air_purifier): record long-term statistics for the particulate sensors
dust / fine_dust / super_fine_dust show live values fine but Home Assistant
keeps no long-term statistics for them, so once recorder's purge window passes
(10 days by default) the history is gone and they can't back a long-range
air-quality graph.

HA only writes long-term statistics for sensors that declare a state_class,
and AIR_QUALITY's descriptors set none -- the entities come up carrying just
an icon. The values were never the problem: common.sensor_item_value already
returns int. Three sensors in this same module (filter_progress,
fan_speed_level, hepa_filter_usage) already declare one, so this reads as an
oversight rather than a decision.

Only the three particulate readings are stamped. They fall monotonically with
particle size on three independent board families -- 11/9/5 on ARTIK051_TVTL
(issue #56), 10/9/6 on AVT-WW-TP1 (issue #190), 18/14/9 on the range hood --
which is concentration behaviour, and averaging it over time is meaningful.
Odor and CleanLevel read 0-2 on every fixture and look like graded indices,
where the mean of a grade isn't obviously meaningful, so they are left alone
rather than guessed into statistics.

Worth flagging for the review: air_monitor.SENSORS already stamps all five of
these, and its module docstring describes that as "matching
air_purifier.AIR_QUALITY's existing precedent" -- a precedent this module did
not actually set. Extending to all five here is a one-line change if
consistency is preferred over the grade/concentration split.

The state_class is carried in a separate key set rather than a fourth tuple
column because air_monitor.py imports _AIR_QUALITY_SENSORS and unpacks it as a
triple; widening it breaks that module's import outright. Two of the new tests
guard exactly that coupling.

No device_class or unit is asserted: pm1/pm25/pm10 with µg/m³ would claim the
reading is a mass concentration, which no dump states. That is a separate call
from making the series recordable at all.

Metadata only -- no key, name, value or unit changes, so no entity changes
identity and every golden is untouched. Statistics start accumulating from the
upgrade onward; existing short-term history is unaffected.
2026-08-03 14:34:57 +09:00
Marc Billow 51103fa341 Merge pull request #264 from mbillow/claude/ruff-pyright-ci-stage-gxph0t
Add ruff (lint + format) and ty (type checking) to the project
2026-08-02 20:30:08 -04:00
Marc Billow 9dc1facbfe Fix ty diagnostics from newer homeassistant/cryptography type stubs
requirements-dev.txt intentionally leaves homeassistant/cryptography
unpinned (always test against latest), so ty's view of their stubs can
drift between runs. SensorEntity._attr_state_class now requires
SensorStateClass rather than a bare str (same fix already applied to
_attr_device_class); NameAttribute.value is generic over str | bytes,
so narrow it before handing it to re.search.
2026-08-03 00:25:46 +00:00
Marc Billow 24d48d70b9 Reformat after merging main
main advanced past this branch (PR #263, entity-less-after-restart fix)
with unformatted changes to coordinator.py's tests; re-running ruff
format picks those up. Merge commit itself had no conflicts.
2026-08-03 00:21:34 +00:00
Marc Billow 18596f23ec Merge remote-tracking branch 'origin/main' into claude/ruff-pyright-ci-stage-gxph0t 2026-08-03 00:20:59 +00:00
Marc Billow 450f8ca933 Add lint/format/type-check CI job
New "lint" job in validate.yml runs ruff format --check, ruff check,
and ty check against custom_components/ and tests/ on the same
push/PR/schedule triggers as the existing hassfest/hacs/pytest jobs.
2026-08-03 00:16:34 +00:00
Marc Billow 679c3d2bce Fix remaining pre-existing ty diagnostics in test files
Completes the isinstance/cast narrowing + Optional-field assert pattern
across the last batch of test files. custom_components and tests are
now both fully clean under ruff check, ruff format --check, and ty check.
2026-08-03 00:14:58 +00:00
Marc Billow 36a642135b Fix pre-existing ty diagnostics in a second batch of test files
Same isinstance/cast narrowing and Optional-field assert pattern as the
prior commits, covering the airconditioner, fridge, washer, operational,
subdevices, sensor_hysteresis, laundry, select_options, identity and
entities test files.
2026-08-03 00:11:05 +00:00
Marc Billow 07061c734d Fix more pre-existing ty diagnostics in test files
Continues narrowing SamsungEntityDescription accesses to the correct
subclass and asserting Optional write_fn/match_fn/exists_fn fields are
set before calling them, per the pattern established in the previous
commit.
2026-08-03 00:03:08 +00:00
Marc Billow daf7e3787f Add ruff (lint + format) and ty (type checking) to the project
Adds [tool.ruff] and [tool.ty] config to pyproject.toml with a curated
ruff rule set (E, F, W, I, UP, B, C4, SIM, RUF, ASYNC, LOG, G, PIE, RET,
PERF, N), pins ruff/ty in requirements-dev.txt, reformats the whole tree
with `ruff format`, and fixes the pre-existing lint and type-check debt
those tools surfaced so both run clean.

Production-code type fixes include: HA's ConfigFlowResult vs. the
generic FlowResult in config_flow.py, narrowing BoundEntity.desc to its
platform-specific subclass (SelectDesc/NumberDesc/SensorDesc/etc.) via
cast() instead of an unchecked annotation, converting HA device_class
strings to their proper enum types, a resolve_registry callback typed
as `object` instead of `DeviceRegistry | None`, and a couple of other
narrow correctness fixes (CA key type validation, an index-out-of-bounds
false positive from an empty-tuple fallback, a bool/dict argument swap).

Test-file fixes are mechanical: narrowing SamsungEntityDescription to
the correct subclass via isinstance()/cast() before accessing
subclass-only fields, and asserting Optional write_fn/unit_fn fields
are set before calling them.
2026-08-02 23:56:38 +00:00
Marc Billow d9c84e765b Merge pull request #263 from mbillow/claude/issue-254-diagnosis-fix-a2t1y1
Fix devices coming up entity-less after a Core restart (#254)
2026-08-02 19:50:02 -04:00
Marc Billow 00abfb9770 Tighten the #254 fix after review
No behavior change to the fix itself; cleanup only.

Production:

- Trim the narrative that was told three times over (coordinator
  docstring, test docstring, inline comment) down to one telling in the
  docstring, where someone tempted to remove the gate will be standing.
- Guard the second degraded return in _async_update_data on
  self._discovered too. That arm is currently unreachable before
  discovery only because every _observe.apply() call site happens to be
  gated on post-discovery state -- a non-local accident across four call
  sites. Stating the precondition where it is relied on makes it the same
  explicit rule _defer_reconnect_for now applies.

Tests, 7 -> 4 with better discrimination:

- test_session_closed_when_first_refresh_fails asserted _close_session
  was called, which the reconnect path already does on its own -- so it
  passed with the fix removed. Merged into the persistent-timeout test
  and re-pointed at async_close, which only setup calls.
- Dropped the __new__-built unit test: it set one attribute on an
  otherwise uninitialized instance, so it asserted the gate's position in
  the function rather than any behavior, and would have errored rather
  than failed if reordered.
- Folded the timeout-budget test into the recovery test it was a
  byte-for-byte copy of, and replaced both hand-rolled call counters with
  the side_effect=[exc, resources] idiom already used in this file.

Each of the three production changes is now independently covered:
removing any one of them alone fails the suite.
2026-08-02 23:44:38 +00:00
Marc Billow 9fc04e179e Fix devices coming up entity-less after a Core restart
A failed DTLS handshake on the very first poll was being swallowed, so
the config entry loaded with no entities at all and stayed that way until
the user reloaded that device by hand (issue #254).

_poll_once() connects when there is no session yet, so connect()'s
handshake timeout reaches _async_update_data as a TimeoutError -- the
same type a slow blockwise transfer raises mid-session.
_defer_reconnect_for() only knew the mid-session meaning and deferred it,
making _async_update_data return flatten([], {}) == {} instead of
raising. DataUpdateCoordinator counts any non-raising return as success,
so async_config_entry_first_refresh saw a healthy first refresh and
skipped ConfigEntryNotReady, and setup forwarded the platforms with
`bound` still empty. Platforms enumerate `bound` once and have no dynamic
add-listener, so a later cycle repopulating it added nothing: every
restored entity sat unavailable until a manual reload.

Gate the deferral on self._discovered. Before the first discovery a poll
failure now takes the normal path -- one reconnect attempt, then
UpdateFailed -> ConfigEntryNotReady -- so HA retries on its own backoff
until the handshake goes through.

Two related fixes in the same failure path:

- Close the DTLS session on EVENT_HOMEASSISTANT_STOP, not only on entry
  unload. HA does not unload entries on a Core restart, so async_close()
  never ran and the previous run's association was left orphaned on the
  appliance -- which is what makes the next run's handshake time out in
  the first place. The fixed source port still covers the unclean-exit
  case where no close_notify can be sent.

- Close the session when first refresh fails. _poll_once deliberately
  leaves it open on a TimeoutError, so a failed setup abandoned a bound
  UDP socket on a port that is fixed per device by design, and each HA
  retry bound another socket to that same port.
2026-08-02 23:26:50 +00:00
Marc Billow b0eab93b58 Merge pull request #262 from mbillow/claude/pr-256-it-translation
Complete Italian translation, fix es.json parity
2026-08-02 18:07:12 -04:00
Marc Billow 547388cc2b Fix es.json translation-catalog parity with en.json
test_every_language_mirrors_the_english_catalog was failing on main
for es (PR #246) independent of this branch. Beyond the 32 keys en.json
gained since #246 merged (the AC/fan preset_mode and fan_mode state
blocks, and the new EHS/zone/auto-clean keys), the file had accumulated
several pre-existing bugs that also broke topology parity:

- climate.airconditioner and fan.air_purifier_fan carried a stray
  "name" key that doesn't exist in en.json's catalog for either (both
  entities are unnamed in code); removed, and their real
  state_attributes blocks added.
- select.buzzer_sound and select.finish_sound were keyed by literal
  on-wire device codes (Volume_Off/Low/Med/High, Finish Sound_1/2/3)
  instead of en.json's actual off/on states -- dead translations, never
  resolved at runtime. finish_sound's values were also unrelated song
  titles, not sound-toggle labels. Replaced both with real off/on
  entries.
- select.dryer_cycle_table_03 had codes 1c/1d/1e (Shirts/Towels/Outdoor)
  rotated by one slot, so a Shirts cycle displayed "Toallas"; realigned
  to the correct codes and added the 2 missing ones (2b, 4c).
- select.washer_cycle_table_02 carried 4 stray codes (06/08/74/A0) not
  present in en.json's table at all, duplicating already-correct
  translations under codes this device never reports; removed.

tests/test_translations.py now passes for every language, and the full
suite is green (1121 passed).
2026-08-02 22:04:32 +00:00
Marc Billow 81b83f4175 Complete Italian translation
Fills in the remaining entity names/states and fixes one broken
placeholder in the existing translation (issues.device_gap.description
used {nome_dispositivo} where the string is formatted with
{device_name}, which would have rendered the literal placeholder in
the UI instead of the device name).

Samsung-marketed cycle/feature names (WindFree, AI Wash/Comfort/Energy
Mode, Smart Control/Dry, Storm Wash+, Self Clean+, Drum Clean+, Frozen
Pizza+, Good Sleep, Super Speed) are left in English, matching how
nl.json treats the same set -- WindFree and AI Dry each get their
qualifier translated (WindFree sonno, Asciugatura AI) while the brand
word stays put, the same split nl.json makes.
2026-08-02 22:04:04 +00:00
g1za c5f37ea280 Partial Italian translation
Translates the custom integration's config/options/issues/exceptions
strings and the washing machine entity labels (select/binary_sensor
entries for cycle, spin speed, wash temperature, detergent/softener,
child lock, and related sensors).
2026-08-02 21:55:36 +00:00
Marc Billow b4550cc4b1 Merge pull request #246 from axelet85/feat/es-translation
feat: Spanish translation (es.json)
2026-08-02 17:43:21 -04:00
Marc Billow 42a1812967 Merge pull request #261 from mbillow/claude/pr-242-review-merge-nny8ug
feat: discover UUID-prefixed subdevices advertised only via /oic/res (Pattern C, #241)
2026-08-02 17:42:46 -04:00
Marc Billow e926905517 fix: dedupe Pattern C's UUID-prefix candidates against Pattern B (#242 review)
Both the /subdevices/vs/0 subdeviceIdList (Pattern B) and an /oic/res
link's UUID prefix (Pattern C) can name the same physical subdevice --
TP2X_FAC_BORA_21K, the Pattern B reporter's own board, does. Filtering
the two candidate lists against each other with a plain set difference
missed this when the two sources disagree on the UUID's case, letting
the same subdevice get probed and materialized twice under two
different keys.

Move the guard into _probe_prefixed itself, keyed on a
case-normalized id, so neither pattern can add a candidate the other
already claimed regardless of casing.
2026-08-02 21:40:34 +00:00
Hyunook 6bcf8f3bec feat: discover UUID-prefixed subdevices advertised only via /oic/res (Pattern C, #241)
AWM-WW-AID-26-ONEBODY (washer+dryer combo) reports numofsubdevice='2' on
/multidevice/vs/0 but carries no /subdevices/vs/0 (no subdeviceIdList --
Pattern B's signal) and 4.04s /device/1 and /device/2 (Pattern A's). The
washer subdevice's UUID appears only as the path prefix of the
x.com.samsung.da.multidevice link in /oic/res; GET /<uuid>/device/0
answers the washer's own full Collection batch (model ..._WF80H vs the
master's ..._DV80H27H).

Treat every UUID path prefix seen in /oic/res as a prefixed-subdevice
candidate (minus ones subdeviceIdList already named), probed with the
same tolerated-404 seed RETRIEVE as Pattern B -- the shared body is
factored into _probe_prefixed. discover_partitioned's entity-level
liveness gate still decides materialization, so a UUID link with no live
sibling behind it contributes nothing.

Fixture is a live capture from the reporting board (serials/MACs/di
scrubbed); tests cover discovery, probe hygiene, washer-side entity
binding, and that the master's own entity set is unchanged.
2026-08-02 21:40:34 +00:00
Marc Billow 9737684c9f Merge pull request #253 from pookey/feat/ehs-water-heater
Add a water_heater platform for the EHS DHW loop
2026-08-02 17:32:22 -04:00
Ian P. Christian 6efee761d9 Add Samsung EHS (Eco Heating System) heat pump support
Adds a device registry for the TP1X_DA_AC_EHS board family: separate
zone1 (space heating/cooling) and dhw (domestic hot water) loops.
zone1 is exposed as power switch + mode select + current/target
temperature sensor/number -- it's a leaving-water-temperature
setpoint, not a thermostat, so no HA platform fits it better. dhw
gets a composite water_heater entity, using the same
primary-resource-plus-sibling-reads shape climate.py already uses
for the AC (PR #247 review feedback: "Having water heaters
automatically leverage the right platform would be pretty cool!").
The unit's away mode is a device-wide switch, not the water_heater
AWAY_MODE feature -- /option/outgoing/vs/0 has no dhw-scoped sibling
and covers zone1 too, so presenting it on the DHW card would
misstate its scope.

Operation modes (Eco/Std/Force/Power) map onto HA's own standard
water_heater states, the same mapping HA core's smartthings
integration uses for this exact Samsung capability over the cloud
API. Device codes are matched case-insensitively on the read side.
The DHW entity takes a catalog name ("Hot water") rather than the
bare device name -- unlike the AC's climate card, it is one loop of
a two-loop device.

Both temperature ranges fall back as a pair: a resource reporting a
minimum but no maximum yields no range at all rather than mixing a
device bound with an invented default, matching climate._range().
Increment fallbacks test for None instead of using `or`, so a
genuine 0 survives.

set_temperature honours the optional operation_mode HA's
water_heater service schema forwards, setting the mode (and powering
the loop on) before the setpoint, the same way climate's
set_temperature handles hvac_mode.

Entity names are translated into Czech and Dutch following each
file's existing terminology conventions.

Verified against a real TP1X_DA_AC_EHS_01001_0000 diagnostics dump
(firmware AEH-WW-TP1-22-AE6000_17260402); golden-regression fixture
and full test coverage included.
2026-08-02 22:18:43 +01:00
Marc Billow e309d3e7b8 Merge pull request #225 from atc722/agent/qooker-support
Route Samsung Bespoke Qooker to microwave registry
2026-08-02 12:22:10 -05:00
Marc Billow 34b2291bef Merge pull request #255 from moKorean/autoclean-cycle-state
Report whether the auto-clean cycle is running, and how far through
2026-08-02 09:05:34 -05:00
Geunwon Mo 5c275753d4 Report whether the auto-clean cycle is running, and how far through
`/option/autoclean/vs/0` carries three fields and only `settingStatus` was read.
That one says the feature is enabled, which it is whether or not the unit is
drying right now, so nothing reported an actual cycle.

    settingStatus: On      <- the existing auto_clean switch
    status:        Stop    supportedStatus: [Start, Stop]
    progress:      0

Adds a binary sensor for `status` and a percentage sensor for `progress`.

Measured on a TP1X_DA-AC-CAC-01001, sampling the resource every eight seconds
across a cycle: `Start` with progress 98 while it ran, then `Stop` with progress 0
the moment it finished. The percentage matches the figure the appliance shows on
its own display, checked against 55% mid-run.

Golden state keys updated for the 18 fixtures that bind AUTO_CLEAN. The change is
additive — no key was removed from any of them.

Full suite passes (1070 tests, Python 3.13 via requirements-dev.txt).
2026-08-02 22:01:27 +09:00
galaxysj 71f2101d11 Bound subdevice discovery during setup 2026-08-02 12:29:55 +09:00
galaxysj ba5a3b529d Add dishwasher course labels 2026-08-02 11:53:09 +09:00
galaxysj f8849df8a6 Fix washer course enum display 2026-08-02 10:19:45 +09:00
axelet85 57eb3e80c7 feat: add Spanish translation (es.json)
Complete Spanish translation for LocalThings: 257 entity names across
all platforms + full UI strings (config flow, options, issues, exceptions).

Transparency: AI-assisted (Hermes Agent), reviewed and verified by the
owner against the official Samsung SmartThings app on real hardware
(washer DA_WM_TP1_21_COMMON). Translation files only, no code changes.

Washer cycles verified one-by-one against the official app; 4 cycle codes
missing from the catalog were added from real hardware (08, 74, 06, A0).
Washer options verified: volume, finish alarm, bubble soak.
2026-08-01 20:31:56 +02:00
hoon d544644e0e Clarify resource override comment 2026-08-01 11:29:30 +09:00
hoon f9cd857ced Support Samsung Bespoke Qooker routing 2026-08-01 10:47:00 +09:00
Marc Billow 93744e80f4 Merge pull request #227 from blka/observe-grace-early-exit-upstream
Observe grace-period early-exit (first-refresh ~15s -> ~0.4s)
2026-07-31 20:20:22 -05:00
Marc Billow a74b92f758 i18n: add czech translations to merged pr 2026-07-31 20:16:56 -05:00
Marc Billow d639aa693f Merge pull request #218 from perseus177/ac-filter-reset
feat(airconditioner): filter dust alarm interval on legacy ARTIK051 boards
2026-07-31 20:14:31 -05:00
Marc Billow 3c6c7f246f Merge branch 'main' into ac-filter-reset 2026-07-31 20:13:40 -05:00
Marc Billow 0d0844ea7f Merge pull request #230 from moKorean/oic-type-hood
Add x.com.st.d.hood to _OIC_TYPE_TO_KEY; document why oic.d.cooktop is not mapped
2026-07-31 20:10:24 -05:00
Geunwon Mo 618fc5fa51 Add x.com.st.d.hood to _OIC_TYPE_TO_KEY, and document why oic.d.cooktop is not
Measured on nine Samsung appliances on Korean-market firmware, every one of
which populates /oic/d with a concrete type:

    4x  oic.d.airconditioner   AJ023CN1UBC1 system A/C, "Samsung System A/C"
    3x  oic.d.refrigerator     "[refrigerator] Samsung"
    1x  oic.d.cooktop          TP1X_DA-KS-COOKTOP, "Samsung Cooktop"
    1x  x.com.st.d.hood        AHD-WW-TP1-22-COMMON, "Samsung Hood"

Every one agreed with what for_device_by_model already concluded from the board
token, so this is corroboration rather than a correction.

`x.com.st.d.hood` was the one type with a registry to point at and no row, so
this adds it.

`oic.d.cooktop` is left out on purpose, with a comment saying why: the induction
above reports it, but `cooktop` and `induction_cooktop` are unrelated registries
that happen to share the English word, and the OCF type cannot tell them apart.
Mapping it to either key would misroute the other, and since resolve() consults
this table first it would override a COOKTOP/CT board token that had it right.
Same shape of argument as the existing oic.d.robotcleaner note.

One incidental data point on the docstrings' "only ever helps a minority of
dumps": that may understate it. Nine out of nine here answer /oic/d with a usable
type, across four families. Not enough hardware to generalise from, but enough
that it looks less like a rare bonus than the comments assume.

Verified: the full suite passes (1024 tests, Python 3.13 via
requirements-dev.txt).
2026-08-01 09:02:49 +09:00
Marc Billow f36b3016cc chore: bump version to v0.18.0 v0.18.0 2026-07-31 15:44:24 -05:00