Commit Graph
289 Commits
Author SHA1 Message Date
Marc Billow 3b0f4f8b66 Merge branch 'main' into claude/device-support-issue-90-water-purifier 2026-07-26 22:06:20 -05:00
Marc Billow 4719e3aa80 Merge pull request #97 from mbillow/claude/device-support-issue-87-window-ac
Add device support for Bespoke Window AC (issue #87)
2026-07-26 22:03:36 -05:00
Marc Billow d0e03b026c Merge pull request #99 from mbillow/claude/device-support-issue-83-fridge-detection
Catch the 'Nothing(SVC)' placeholder serial in both fallback sites (i…
2026-07-26 21:55:33 -05:00
Marc Billow d2f0a5c976 Merge pull request #100 from mbillow/claude/device-support-issue-80-cycle-labels
Add confirmed washer/dryer cycle code labels (issue #80)
2026-07-26 21:53:21 -05:00
Marc Billow 48b34c9e31 Delete test_issue_80_cycle_codes_are_labelled
Removed test for issue 80 regarding cycle codes.
2026-07-26 21:53:06 -05:00
Marc Billow 0b6eda0cdc Merge branch 'main' into claude/device-support-issue-80-cycle-labels 2026-07-26 21:51:23 -05:00
Marc Billow 285d2de397 Merge pull request #101 from mbillow/claude/device-support-issue-79-dryer
Fix dryer detection when description pairs two model numbers (issue #79)
2026-07-26 21:45:12 -05:00
Marc Billow f3b0ef4733 Merge pull request #103 from mbillow/claude/device-support-issue-75-windfree-ac
Add humidity/temperature sensors and horizontal swing for AC (issue #75)
2026-07-26 21:37:59 -05:00
Marc Billow d20d4e2158 Merge pull request #104 from mbillow/claude/device-support-issue-74-range
Add range/oven detection for boards missing /information/vs/0 (issue …
2026-07-26 21:33:25 -05:00
Marc Billow d321fe72cd Merge pull request #94 from mbillow/claude/device-support-issue-93-ac-modes
Device support issue 93: ac modes
2026-07-26 21:30:46 -05:00
Marc Billow 9b09f1aee3 Merge pull request #70 from vmvarga/feat/wd7000b-fridge-fixes
feat: laundry firmware-flag gating, fridge vendor temp writes, course…
2026-07-26 21:25:26 -05:00
Marc Billow 45931332d4 Rework AIComfort as an HVACMode.AUTO + preset overlay, add unmapped-mode warning (issue #93)
AIComfort isn't a distinct thermodynamic operation like Cool/Dry/Heat --
it's an AI-driven overlay on top of the device's own 'Auto' behavior,
confirmed by A-CAWW-TP2-20-COMMON reporting both 'Auto' and 'AIComfort'
as separate, mutually-exclusive entries in /mode/vs/0's supportedModes.
Modeled the idiomatic HA way instead of a flat _DEVICE_TO_HVAC entry:
hvac_mode reports AUTO and a new 'ai_comfort' preset carries the
distinction. Entered/left only via the preset (writes the primary mode
resource, not the convenient one) -- there's no dedicated HVACMode
value for it, so it's not offered in the hvac_mode dropdown directly.

Also adds a once-per-(href, code) warning log when a device-reported
mode has no entry in the relevant map, so a future gap like this one
surfaces in the log instead of silently vanishing -- the exact failure
mode issue #93 called out ("this class of gap is invisible without
diffing against supportedModes").
2026-07-27 02:01:51 +00:00
Marc Billow 8c3250b163 Map AC's AIComfort mode to HVACMode.AUTO (issue #93)
A-CAWW-TP2-20-COMMON (and likely other CAWW/TP2X-class boards) reports
'AIComfort' as a distinct entry in /mode/vs/0's supportedModes,
alongside 'Auto' (already mapped to HEAT_COOL). _read_modes() silently
drops any code missing from _DEVICE_TO_HVAC, so AIComfort was
unreachable -- one of HA's five other AC hvac_modes, unused by this
device family until now.

The other two gaps in issue #93 are already addressed elsewhere and
not duplicated here:
- Fan-only via the 'Fan' device code, and the WindFree/LongWind/
  NanoSleep preset codes, are covered by the still-open PR #91, which
  replaces the static preset table with a fully dynamic resolver over
  the device's own supportedModes.
- The 'Left_And_Right' -> horizontal swing mapping is already on the
  still-open claude/device-support-issue-75-windfree-ac branch.
2026-07-27 01:43:22 +00:00
Marc Billow d53459d047 Add device support for Samsung water purifiers (issue #90)
The TP2X_WATERPURIFIER_20K water purifier reports no oneUiVersion and
its modelNum/description don't match any consumer-prefix or existing
board-family token, so it fell into the unknown-device-type fallback
with only common capabilities. Add a new water_purifier registry,
routed via a 'WATERPURIFIER' modelNum/description fallback rule.

Models dispense settings (type/temperature/capacity/pouring status),
sterilize and filter status, favorite-capacity presets, and the three
water/buzzer locks. Per the adding-device-support skill's "never
hard-code the one dump's values" rule: dispense-capacity bounds and
step come live from the device's own desiredCapacityRange/
capacityResolution fields (range_field/step_fn), not a hardcoded
constant, and the hot-water-temperature control is a select over the
live supportedHotTemperatures list rather than a number with invented
bounds, since only a few discrete temperatures are selectable.

/mode/vs/0 and /automation/waterpurifier/vs/0 are left unmodeled: the
former carries an opaque wizard-workflow token with no coherent
current-value contract, the latter is a static support-flags blob with
no live setting to expose.

Confirmed against the issue #90 diagnostics dump with zero unbound
hrefs. Updates the README's supported-appliance-types table for the
new device type.
2026-07-27 01:40:05 +00:00
Marc Billow b47eeaf7bc Add device support for Bespoke Window AC (issue #87)
The AW06C7155EWAZ window air conditioner (modelNum
TP1X_DA_AC_WAC_01001_0000) reports no oneUiVersion and uses the '_WAC_'
(Window Air Conditioner) modelNum token instead of the '_RAC_'/'_PRAC_'
tokens already handled by for_device_by_model. Add a fallback rule for
that token, routing it to the existing airconditioner registry.

The device's resource surface (mode/convenient/wind/temperature/power/
filter/humidity) is already fully modeled by the airconditioner
capability set, so this is purely a detection-routing fix -- confirmed
against the issue #87 diagnostics dump with zero unbound hrefs.
2026-07-27 01:17:45 +00:00
Marc Billow 2c9fda3db1 Catch the 'Nothing(SVC)' placeholder serial in both fallback sites (issue #83)
The ARTIK051_DONGLE_REF firmware family reports the literal string
"Nothing(SVC)" as serialNum on every unit -- non-empty, so the existing
`if not serial` checks in config_flow.py's _probe_and_validate and
coordinator.py's _run_discovery don't catch it. Two such appliances on
one install (a fridge and a freezer, each its own dongle) then collide:
config_flow gives both the same unique_id and rejects the second as
"already configured" (bug 2), and even once that's worked around,
device_serial feeds every entity's unique_id too, so the second
appliance's entities get silently dropped with "does not generate
unique IDs" log lines (bug 4).

Add _is_placeholder_serial (duplicated in both modules rather than
imported, to avoid pulling config_flow into the runtime coordinator's
import graph or vice versa for a two-line check) and treat it the same
as an empty serial: fall back to host/port.

Type detection (bug 1) and the door sensor's field-name gap (bug 3),
also reported in this issue, are already fixed via the
claude/device-support-issue-77-freezer branch, which hit the same
ARTIK051_DONGLE_REF family from a different report -- not duplicated
here.
2026-07-27 01:00:05 +00:00
Marc Billow 5d7789eb4e Add confirmed washer/dryer cycle code labels (issue #80)
Five raw course codes were rendering unlabeled because no translation
entry existed for them, confirmed by the reporter selecting each cycle
on the physical appliance and reading back the raw code from the
entity's state:

- washer_cycle_table_02: '52' Eco Cold, '54' Towels, '60' Self Clean+
  (a WF50A8600AV/US). '54' shares a display name with the existing '24'
  Towels -- a different code on the same table legitimately landing on
  the same label, matching the existing '21'/'65' Colors and
  '27'/'5E' Rinse+Spin pairs, not a duplicate-in-error.
- dryer_cycle_table_03: '01' Normal, '06' Time dry (a DVE50A8600V/A3,
  the same model added in the previous commit's detection fix).

No code changes -- select.py already derives which raw values it
normalizes from the shipped catalog, so labelling a code is purely a
translations/en.json (mirrored to nl.json) addition.
2026-07-27 00:47:16 +00:00
Marc Billow bae1ac4337 Fix dryer detection when description pairs two model numbers (issue #79)
DVE50A8600V/A3 reports description
'DA_WM_TP1_21_COMMON_DVE50A8800_8600/DC92-02835A_0080' -- a paired
listing of two related model numbers (DVE50A8800 and DVE50A8600) joined
by an underscore, rather than the usual single trailing consumer-model
token. for_device_by_model only ever checked the literal last
underscore segment ('8600', which has no recognizable 2-letter prefix
on its own), so the real 'DV' token one segment earlier was never
reached and the device fell back to 'unknown' with only common
capabilities -- no dry level, cycle, or wrinkle-prevent entities.

Replace the single last-segment extraction with _consumer_model_key,
which scans segments from the end and returns the first one that
resolves. Behavior is unchanged for every existing single-token
description (the last segment still matches first); it just keeps
looking when that segment doesn't.
2026-07-27 00:44:23 +00:00
Marc Billow 3afbe6c6c1 Add humidity/temperature sensors and horizontal swing for AC (issue #75)
Three gaps reported against an ARTIK051_PRAC_20K WindFree unit vs. the
SmartThings integration:

1. Missing WindFree/motion convenient-mode presets -- left alone here.
   PR #91 replaces climate.py's static _DEVICE_TO_PRESET table with a
   generic resolver that reads any preset code straight off the unit's
   own supportedModes, which already covers this (and more generically
   than a per-model dict would) -- adding one here would just conflict.

2. No horizontal oscillation: /wind/direction/vs/0's supportedModes
   includes Left_And_Right, which _DEVICE_TO_SWING had no mapping for.
   Add it to HA's standard 'horizontal' swing constant.

3. No standalone humidity/current-temperature sensors: the climate card
   already reads both internally, but nothing exposed them as entities
   for history/automations. Add CURRENT_TEMPERATURE (OCF
   /temperature/current/0) with a CURRENT_TEMPERATURE_VS vendor fallback
   (same match_fn-gated pair shape as common.py's POWER_GENERIC/
   POWER_VS_FALLBACK), and a HUMIDITY sensor reading /humidity/vs/0's
   fivepercentHumidity field -- the only one of the three
   humidity-shaped fields across /humidity/0 and /humidity/vs/0 that
   isn't permanently stuck at 0 on every dump seen.

Regenerates the five existing AC goldens (all pick up
current_temperature_c; most pick up humidity) and adds a dedicated
fixture from the issue's WindFree dump, whose /humidity/vs/0 actually
has live fivepercentHumidity data.
2026-07-27 00:33:35 +00:00
Marc Billow c72b5a988e Add range/oven detection for boards missing /information/vs/0 (issue #74)
NE63B8411SS reports no oneUiVersion and no /information/vs/0 resource at
all, so neither for_device nor for_device_by_model's modelNum tokens have
anything to key off -- it fell through to the unknown-device fallback,
which also mis-binds /temperatures/vs/0 against fridge.py's setpoint
capability (a collision the oven family's own capabilities are normally
excluded from the global registry specifically to avoid).

Add a resources-based signature to for_device_by_resources(): 'Bake' in
/mode/vs/0's supportedModes is oven/range-exclusive vocabulary, and paired
with the /oven/vs/0 cavity resource it reliably identifies this family
even with no model info at all. Route to 'range' when a cooktop-status
resource is also present, else plain 'oven'.

This board's cooktop half also doesn't expose the per-burner
/cooktop/status/vs/0 array range.py already models -- only the coarser
/cooktopmonitoring/vs/0 summary resource. Add a read-only
COOKTOP_MONITORING capability for it (cooktop running state, warming
center state) rather than leaving it unbound.
2026-07-27 00:23:04 +00:00
vmvarga 2fd8ff8260 merge temp_setpoint 2026-07-26 11:05:13 +02:00
vmvarga 00cf8d676d feat: laundry firmware-flag gating, fridge vendor temp writes, course names 2026-07-25 10:01:56 +02:00
Marc Billow aff7647460 Merge pull request #65 from Metal-Eagle/feature/add-completion-time
Feature/add completion time
2026-07-24 23:46:47 -05:00
Jeroen Hof 705da781f1 Merge branch 'main' into feature/add-completion-time 2026-07-25 06:41:17 +02:00
Jeroen Hof 6de1a4090e i18n: simplify completion time translation in English and Dutch 2026-07-25 06:02:45 +02:00
Jeroen Hof ef75db6697 i18n: update completion time translation in English and Dutch 2026-07-25 05:55:47 +02:00
Jeroen Hof 9e290f27ee refactor: update sensor descriptions for completion time and delay start 2026-07-25 05:24:10 +02:00
Jeroen Hof 37bfff152d refactor: remove unused strings.json file 2026-07-25 05:15:34 +02:00
Jeroen Hof 317b7d0c1b fix: add missing newline at end of en.json file 2026-07-25 05:09:55 +02:00
Jeroen Hof ba8e41d310 Implement code changes to enhance functionality and improve performance 2026-07-25 05:08:18 +02:00
Jeroen Hof 724686c42c i18n: add translations for AI Wash and Jeans 2026-07-25 05:01:40 +02:00
Jeroen Hof edccb6ae3f i18n: add new wash options for AI Wash and Jeans 2026-07-25 04:59:37 +02:00
Jeroen Hof 4a91f62a36 i18n: add new translations for AI Wash and Spijkerbroek 2026-07-25 04:49:40 +02:00
Jeroen Hof 1d4d38db14 Merge branch 'main' into feature/add-completion-time 2026-07-25 04:47:22 +02:00
Marc Billow 35e2c79b14 brand: use updated localthing logo v0.12.0 2026-07-24 20:23:48 -05:00
Marc Billow f185951a8a i18n: missed a quick wash translation 2026-07-24 15:42:24 -05:00
Marc Billow adcb8a0ecb chore: add additional translations provided in #1 dutch 2026-07-24 15:33:31 -05:00
Marc Billow dc86c7f0ef Merge pull request #72 from mbillow/claude/home-assistant-i18n-iwwyvr
feat (i18n): refactor and improve translation support
2026-07-24 15:23:39 -05:00
Marc Billow 30d38d855c Merge branch 'main' into claude/home-assistant-i18n-iwwyvr 2026-07-24 15:23:10 -05:00
Marc Billow ee77e3bda8 chore: add additional translations provided in #1 2026-07-24 15:20:19 -05:00
Marc Billow ea986fe8e5 refactor: adopt cleaner write pattern for options arrays 2026-07-24 15:05:26 -05:00
Marc Billow 10c850f2b9 refactor(i18n): drop the vestigial descriptor name field
Since entity.py started routing named descriptors through the catalog
under desc.key, SamsungEntityDescription.name has been read for its
value nowhere -- only twice as a flag, to decide whether an entity was
translated at all. That left 148 English names duplicated between Python
and translations/en.json with nothing keeping them honest: six had
already drifted, invisibly, because editing the Python side changes
nothing a user sees.

So the field is gone, and translation_key defaults to desc.key. A
descriptor now sets translation_key only to share one catalog entry
across descriptors or to point at a differently-named one, and the
catalog is the only place an entity name exists.

Every descriptor resolves to exactly the translation key, icon, entity
category, enabled-default and gating it did before -- with one
deliberate exception: the hood fan, previously the sole descriptor with
no key at all, now resolves to 'fan'. That is inert, because fan.py sets
_attr_name = None so the entity presents as the device itself.

The three helpers that forwarded a name into a descriptor
(laundry.bool_option_switch, washer._bool_option_switch, air_purifier's
sensor table) lose that parameter. test_translations.py now requires a
catalog entry for every descriptor rather than only translated ones.

Claude-Session: https://claude.ai/code/session_01GiibJZZLWVvyxq7mc7EDNp
2026-07-24 19:45:37 +00:00
Marc Billow 6281b40549 refactor(i18n): make the shipped catalog the single source of truth
PR #68 restated its own translation data in Python: a 60-line
TRANSLATED_SELECT_STATES table of frozensets duplicating every
entity.select.*.state key, a second _TRANSLATED_COURSE_TABLES table
naming which course tables have translations, and a strings.json that
was a 835-line byte-for-byte copy of translations/en.json save 43
[%key:...%] references. Each needed hand-syncing, and one was already
drifting.

Home Assistant loads exactly one file per language for a custom
integration -- translations/<lang>.json. It never reads strings.json and
never resolves [%key:...%]; both belong to Core's build tooling, which
custom integrations don't run through (hassfest skips a missing
strings.json and validates translations/en.json instead). So en.json is
the source, and the new catalog.py reads the keys and states back out of
it for the two decisions Python genuinely has to make:

  - select._display() normalizes a raw Samsung option to a lowercase
    state key only when the catalog knows it, else leaves the vendor's
    casing alone. Derived sets are identical to the removed literals.
  - laundry.cycle_select() keys off a device-reported course table only
    when that table has an entry, else falls back to the name-only
    'cycle' key. Translating Table_00 is now a translations-only change.

Also fixes six names that had already drifted between the Python
descriptors and the catalog, restoring HA's sentence case for two
generic ones (Auto release dry, Bubble soak) and taking the catalog's
wording for the rest, and adds a test so the vestigial descriptor names
can't silently disagree with the UI again.

Claude-Session: https://claude.ai/code/session_01GiibJZZLWVvyxq7mc7EDNp
2026-07-24 19:32:09 +00:00
Marc Billow 0b6cc6aa9f Merge branch 'pr68' into claude/home-assistant-i18n-iwwyvr 2026-07-24 19:24:59 +00:00
Hmmbob 5d86dbe7e8 Resolve runtime translation references 2026-07-24 21:13:07 +02:00
Marc Billow e47a71689d Merge pull request #71 from splitsec2/fix/switch-off-inversion
fix(fridge): switch turn-off silently sends 'On' (truthy-string guard)
2026-07-24 13:50:53 -05:00
Rob Martin 9cf1e46bf6 fix(fridge): stop switch turn-off silently sending 'On'
Six fridge SwitchDesc write_fns built their payload with
`'On' if p else 'Off'`. The switch platform passes the literal
string 'Off' on turn-off, which is truthy, so the guard always
produced 'On' -- turning these switches off silently re-sent On
and they could never be turned off:

  - ICEMAKER_NIGHTTIME (ice.night.status)
  - STATUS_LOCK helper (devicecontrol + device.sound)
  - DEFROST_DELAY (delayDefrost)
  - WELCOME_LIGHTING (status)
  - CABINET_LIGHT dim (light.dimming.status)
  - ICEMAKER_STATUS_FALLBACK (iceMaker)

Compare `p == 'On'` instead, matching the pattern the other
capability files already use. Adds a regression test asserting
every affected write_fn sends 'Off' on 'Off' and 'On' on 'On'.
2026-07-24 12:11:53 -06:00
Hmmbob 82acc05d38 Limit changes to translation support 2026-07-24 18:40:44 +02:00
Hmmbob da49825e2e Harden unknown vendor value fallbacks 2026-07-24 18:12:01 +02:00
Hmmbob b85af10ae1 Test translation coverage and dynamic fallbacks 2026-07-24 18:08:29 +02:00