Merge remote-tracking branch 'origin/main' into claude/device-support-issue-86-cooktop
# Conflicts: # custom_components/localthings/registry/by_type/__init__.py
This commit is contained in:
11 files changed
+689
-2
No files matched your search
@@ -3,7 +3,7 @@ from typing import Optional
|
||||
|
||||
from ._base import DeviceRegistry
|
||||
from . import (
|
||||
air_purifier, airconditioner, cooktop, dishwasher, dryer,
|
||||
air_purifier, airconditioner, cooktop, dehumidifier, dishwasher, dryer,
|
||||
induction_cooktop, oven, range as _range, range_hood, refrigerator,
|
||||
washer, water_purifier,
|
||||
)
|
||||
@@ -20,6 +20,7 @@ _REGISTRY_BY_KEY: dict[str, DeviceRegistry] = {
|
||||
'airconditioner': airconditioner.REGISTRY,
|
||||
'air_conditioner': airconditioner.REGISTRY,
|
||||
'cooktop': cooktop.REGISTRY,
|
||||
'dehumidifier': dehumidifier.REGISTRY,
|
||||
'dishwasher': dishwasher.REGISTRY,
|
||||
'dryer': dryer.REGISTRY,
|
||||
'induction_cooktop': induction_cooktop.REGISTRY,
|
||||
@@ -144,6 +145,13 @@ def for_device_by_model(model_num: str, description: str) -> Optional[DeviceRegi
|
||||
# resource (see airconditioner.py's _AC_IGNORED).
|
||||
if key is None and '-CAWW-' in (model_num or '').upper():
|
||||
key = 'airconditioner'
|
||||
# Dehumidifiers (e.g. TP1X_DA_AC_DHM_01001_0000, issue #88) share the
|
||||
# DA_AC_ board family with the room-AC models above but carry the
|
||||
# '_DHM_' (DeHuMidifier) token instead of '_RAC_'/'_PRAC_'. Distinct
|
||||
# registry: target humidity, not temperature, is the primary control,
|
||||
# and there's no climate composite.
|
||||
if key is None and '_DHM_' in (model_num or ''):
|
||||
key = 'dehumidifier'
|
||||
# Window air conditioners (e.g. TP1X_DA_AC_WAC_01001_0000, issue #87)
|
||||
# report no oneUiVersion and carry the '_WAC_' (Window Air Conditioner)
|
||||
# token instead of '_RAC_'/'_PRAC_'. Same TP1X-class resource surface
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
"""Dehumidifier device registry (Samsung TP1X_DA_AC_DHM-class, issue #88).
|
||||
|
||||
Shares the DA_AC_ board family with airconditioner.py (power, air filter,
|
||||
auto-clean, mute-once all use the identical resource shapes), so those three
|
||||
Capability objects are reused directly rather than duplicated.
|
||||
"""
|
||||
from ..capabilities import airconditioner, common, dehumidifier, ignored
|
||||
from ._base import DeviceRegistry, _build
|
||||
|
||||
REGISTRY = DeviceRegistry(
|
||||
name='dehumidifier',
|
||||
capabilities=_build([
|
||||
*ignored.IGNORED,
|
||||
*common.UNIVERSAL,
|
||||
*common.POWER,
|
||||
dehumidifier.MODE,
|
||||
dehumidifier.HUMIDITY,
|
||||
airconditioner.AUTO_CLEAN,
|
||||
airconditioner.AIR_FILTER,
|
||||
airconditioner.MUTE_ONCE,
|
||||
*dehumidifier.COVERAGE,
|
||||
]),
|
||||
)
|
||||
@@ -0,0 +1,85 @@
|
||||
"""Capabilities for the Samsung dehumidifier family (TP1X_DA_AC_DHM-class,
|
||||
issue #88, model AY18CG7500GED).
|
||||
|
||||
Same DA_AC_ board family as the room-AC models in airconditioner.py (shared
|
||||
power/energy/filter/auto-clean/mute-once resource shapes), but target
|
||||
humidity -- not temperature -- is this device's primary control, and there
|
||||
is no climate composite: power, mode, and humidity are exposed as separate
|
||||
entities rather than folded into one card.
|
||||
"""
|
||||
from ..capability import Capability
|
||||
from ..entities import NumberDesc, SelectDesc, SensorDesc
|
||||
from .common import int_or_none
|
||||
|
||||
|
||||
def _first_mode(rep):
|
||||
"""Representative scalar for the operating-mode select. `modes` is a
|
||||
single-element list on every dump seen so far, mirroring
|
||||
airconditioner._first_mode's handling of the same field shape."""
|
||||
modes = rep.get('x.com.samsung.da.modes')
|
||||
if isinstance(modes, (list, tuple)):
|
||||
return modes[0] if modes else None
|
||||
return modes
|
||||
|
||||
|
||||
MODE = Capability(
|
||||
href='/mode/vs/0',
|
||||
poll_tier='warm',
|
||||
entities=(
|
||||
SelectDesc(key='operating_mode', rep_fn=_first_mode,
|
||||
icon='mdi:tune-variant',
|
||||
options_field='x.com.samsung.da.supportedModes',
|
||||
write_fn=lambda p, rep, href=None: (
|
||||
['mode', 'vs', '0'], {'x.com.samsung.da.modes': [p]})),
|
||||
),
|
||||
)
|
||||
|
||||
# Target humidity is this device's primary control (the issue-#88 dump's
|
||||
# equivalent of a thermostat setpoint). No min/max range field is present in
|
||||
# any dump seen so far -- native_min/native_max are deliberately left unset
|
||||
# so the number entity falls back to HA's own 0-100 default, the natural
|
||||
# bound for a percentage field, rather than a bound guessed from one unit's
|
||||
# spec sheet (see the adding-device-support skill's "never hard-code the one
|
||||
# dump's values" section). Step comes live from the device's own `increment`
|
||||
# field.
|
||||
HUMIDITY = Capability(
|
||||
href='/humidity/vs/0',
|
||||
poll_tier='warm',
|
||||
entities=(
|
||||
SensorDesc(key='humidity', field='x.com.samsung.da.humidity',
|
||||
device_class='humidity', unit='%', state_class='measurement',
|
||||
value_fn=int_or_none),
|
||||
NumberDesc(key='target_humidity', field='x.com.samsung.da.desiredHumidity',
|
||||
device_class='humidity', unit='%',
|
||||
icon='mdi:water-percent',
|
||||
entity_category='config',
|
||||
value_fn=int_or_none,
|
||||
step_fn=lambda rep: int_or_none(rep.get('increment')) or 1,
|
||||
write_fn=lambda p, rep, href=None: (
|
||||
['humidity', 'vs', '0'],
|
||||
{'x.com.samsung.da.desiredHumidity': str(int(round(float(p))))})),
|
||||
),
|
||||
)
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Dehumidifier-scoped coverage: vendor plumbing with no user-actionable state
|
||||
# or no documented write contract, following the same 'don't guess' rule as
|
||||
# airconditioner._AC_IGNORED (this is the same DA_AC_ board family). Not in
|
||||
# the global ignored.IGNORED since some of these hrefs collide with other
|
||||
# families' schemas.
|
||||
# ---------------------------------------------------------------------------
|
||||
_DHM_IGNORED = [
|
||||
'/availablecontrolsets/vs/0', # opaque hex-encoded control-set bitmap (id: DHM)
|
||||
'/da/softreset/vs/0', # soft-reset trigger plumbing
|
||||
'/keepnormalstate/vs/0', # internal keep-normal flag
|
||||
'/personality/presence/vs/0', # presence-personalization plumbing (empty item value)
|
||||
'/reserverulesets/vs/0', # opaque hex-encoded schedule reservation blob
|
||||
'/sensors/vs/0', # empty {} on this dump
|
||||
'/welcome/humidity/vs/0', # welcome-mode plumbing (requestId/operatingStatus, inert)
|
||||
# Only supportedModes ([Off, Sleep]) is present -- no live "current
|
||||
# value" field on this dump to confirm the read/write contract, so per
|
||||
# the 'don't guess' rule this is left unmodeled rather than assumed.
|
||||
'/mode/convenient/vs/0',
|
||||
]
|
||||
|
||||
COVERAGE = [Capability(href=h) for h in _DHM_IGNORED]
|
||||
@@ -140,6 +140,9 @@
|
||||
},
|
||||
"sound_volume": {
|
||||
"name": "Sound volume"
|
||||
},
|
||||
"target_humidity": {
|
||||
"name": "Target humidity"
|
||||
}
|
||||
},
|
||||
"select": {
|
||||
@@ -319,6 +322,9 @@
|
||||
"air_fry": "Air fry"
|
||||
}
|
||||
},
|
||||
"operating_mode": {
|
||||
"name": "Operating mode"
|
||||
},
|
||||
"pantry_zone_mode": {
|
||||
"name": "Pantry zone mode",
|
||||
"state": {
|
||||
@@ -572,6 +578,9 @@
|
||||
"hood_filter_capacity": {
|
||||
"name": "Filter capacity"
|
||||
},
|
||||
"humidity": {
|
||||
"name": "Humidity"
|
||||
},
|
||||
"hood_filter_usage": {
|
||||
"name": "Filter usage"
|
||||
},
|
||||
|
||||
@@ -140,6 +140,9 @@
|
||||
},
|
||||
"sound_volume": {
|
||||
"name": "Geluidsvolume"
|
||||
},
|
||||
"target_humidity": {
|
||||
"name": "Doelvochtigheid"
|
||||
}
|
||||
},
|
||||
"select": {
|
||||
@@ -319,6 +322,9 @@
|
||||
"air_fry": "Airfryen"
|
||||
}
|
||||
},
|
||||
"operating_mode": {
|
||||
"name": "Bedrijfsmodus"
|
||||
},
|
||||
"pantry_zone_mode": {
|
||||
"name": "Modus voorraadzone",
|
||||
"state": {
|
||||
@@ -572,6 +578,9 @@
|
||||
"hood_filter_capacity": {
|
||||
"name": "Filtercapaciteit"
|
||||
},
|
||||
"humidity": {
|
||||
"name": "Luchtvochtigheid"
|
||||
},
|
||||
"hood_filter_usage": {
|
||||
"name": "Filterverbruik"
|
||||
},
|
||||
|
||||
Reference in new issue
Block a user