Bind AI energy level on washer; switch/select split, drop translations
Issue #40: /energy/ailevel/vs/0 was unbound on a plain washer. The capability already existed for fridges but was gated off entirely on single-level hardware (the common case), so it's moved to common.py (cross-family, like fridge + washer now) and split into two entities: a switch when supportedAiLevel has exactly one entry (aiLevel is really just an on/off toggle there), and a select otherwise, with '0' (off) synthesized back into the select's options since supportedAiLevel never lists it but it's a real observed value. Also drops the translation_key/strings.json entries -- aiLevel's raw digit values already render fine untranslated, and translating a handful of levels can't cover devices with more.
This commit is contained in:
12 files changed
+212
-80
No files matched your search
@@ -20,7 +20,7 @@ REGISTRY = DeviceRegistry(
|
||||
dishwasher.DIAGNOSIS,
|
||||
fridge.ICEMAKER_NIGHTTIME,
|
||||
fridge.FLEX_ZONE,
|
||||
fridge.AI_ENERGY_LEVEL,
|
||||
common.AI_ENERGY_LEVEL,
|
||||
fridge.REFRIGERATION,
|
||||
fridge.AUTOFILL,
|
||||
fridge.WELCOME_LIGHTING,
|
||||
|
||||
@@ -19,6 +19,7 @@ REGISTRY = DeviceRegistry(
|
||||
common.ALARMS,
|
||||
common.ENERGY_METER,
|
||||
common.WATER_METER,
|
||||
common.AI_ENERGY_LEVEL,
|
||||
operational.OPERATIONAL_STATE,
|
||||
dishwasher.DIAGNOSIS,
|
||||
fridge.FIRMWARE_UPDATE,
|
||||
|
||||
@@ -11,7 +11,7 @@ against live device dumps:
|
||||
/filter/waterfilter/vs/0 -> x.com.samsung.da.filterUsage / filterStatus
|
||||
"""
|
||||
from ..capability import Capability
|
||||
from ..entities import BinarySensorDesc, SensorDesc, SwitchDesc
|
||||
from ..entities import BinarySensorDesc, SelectDesc, SensorDesc, SwitchDesc
|
||||
|
||||
|
||||
def _num(v):
|
||||
@@ -231,3 +231,63 @@ WATER_FILTER = Capability(
|
||||
name='Filter status', icon='mdi:filter-check'),
|
||||
),
|
||||
)
|
||||
|
||||
# AI energy-saving level -- '0' is off, and supportedAiLevel lists the
|
||||
# additional level(s) the device offers ('1' meaning just "on" on most
|
||||
# hardware, but multi-level boards have been reported). Verified cross-family:
|
||||
# fridge (issue #21) and washer (issue #40) both expose this href.
|
||||
#
|
||||
# supportedAiLevel is a single-entry list on most captured hardware, where a
|
||||
# select would offer only one real choice against an implicit "off" -- shown
|
||||
# as a switch instead. '0' itself is never in supportedAiLevel but has been
|
||||
# observed live as the off value of aiLevel, so the select synthesizes it
|
||||
# back in as an explicit option rather than leaving no way to turn off.
|
||||
#
|
||||
# No translation_key: aiLevel's values are plain digit strings, and
|
||||
# select.py's _display() already renders an untranslated numeric string
|
||||
# as-is -- there's nothing a strings.json entry adds that's worth maintaining
|
||||
# against an unknown, growing number of future levels.
|
||||
|
||||
|
||||
def _ai_energy_supported_levels(rep):
|
||||
"""supportedAiLevel as a list -- a stray scalar (e.g. a string) must not
|
||||
be len()-checked as if it were a list."""
|
||||
sl = rep.get('supportedAiLevel')
|
||||
return list(sl) if isinstance(sl, (list, tuple)) else []
|
||||
|
||||
|
||||
def _ai_energy_level_options(resources):
|
||||
rep = resources.get('/energy/ailevel/vs/0') or {}
|
||||
return ['0', *_ai_energy_supported_levels(rep)]
|
||||
|
||||
|
||||
def _ai_energy_level_write(p, rep, href=None):
|
||||
return ['energy', 'ailevel', 'vs', '0'], {'aiLevel': p}
|
||||
|
||||
|
||||
def _ai_energy_level_switch_write(p, rep, href=None):
|
||||
levels = _ai_energy_supported_levels(rep)
|
||||
on_level = levels[0] if levels else '1'
|
||||
return ['energy', 'ailevel', 'vs', '0'], {'aiLevel': on_level if p == 'On' else '0'}
|
||||
|
||||
|
||||
AI_ENERGY_LEVEL = Capability(
|
||||
href='/energy/ailevel/vs/0',
|
||||
poll_tier='cold',
|
||||
entities=(
|
||||
SwitchDesc(key='ai_energy_level', field='aiLevel',
|
||||
name='AI energy level', icon='mdi:leaf',
|
||||
entity_category='config',
|
||||
value_fn=lambda v: v != '0',
|
||||
exists_fn=lambda rep, resources: (
|
||||
len(_ai_energy_supported_levels(rep)) == 1),
|
||||
write_fn=_ai_energy_level_switch_write),
|
||||
SelectDesc(key='ai_energy_level', field='aiLevel',
|
||||
name='AI energy level', icon='mdi:leaf',
|
||||
entity_category='config',
|
||||
options=_ai_energy_level_options,
|
||||
exists_fn=lambda rep, resources: (
|
||||
not rep or len(_ai_energy_supported_levels(rep)) > 1),
|
||||
write_fn=_ai_energy_level_write),
|
||||
),
|
||||
)
|
||||
@@ -588,33 +588,6 @@ FLEX_ZONE = Capability(
|
||||
),
|
||||
)
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# AI energy-saving level
|
||||
#
|
||||
# supportedAiLevel is a single-entry list ('1' only) on some hardware, where
|
||||
# a select would offer no real choice — gated to >1 supported level.
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
AI_ENERGY_LEVEL = Capability(
|
||||
href='/energy/ailevel/vs/0',
|
||||
poll_tier='cold',
|
||||
entities=(
|
||||
# Only levels '1'/'2' have translated labels in strings.json/translations;
|
||||
# a higher supportedAiLevel entry renders as its raw value until added.
|
||||
SelectDesc(key='ai_energy_level', field='aiLevel',
|
||||
name='AI energy level', icon='mdi:leaf',
|
||||
translation_key='ai_energy_level',
|
||||
entity_category='config',
|
||||
options_field='supportedAiLevel',
|
||||
exists_fn=lambda rep, resources: (
|
||||
not rep or (
|
||||
isinstance((sl := rep.get('supportedAiLevel')), (list, tuple))
|
||||
and len(sl) > 1)),
|
||||
write_fn=lambda p, rep, href=None: (
|
||||
['energy', 'ailevel', 'vs', '0'], {'aiLevel': p})),
|
||||
),
|
||||
)
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Firmware update
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@@ -148,12 +148,6 @@
|
||||
"180": "3 hr",
|
||||
"240": "4 hr"
|
||||
}
|
||||
},
|
||||
"ai_energy_level": {
|
||||
"state": {
|
||||
"1": "Level 1",
|
||||
"2": "Level 2"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sensor": {
|
||||
|
||||
@@ -148,12 +148,6 @@
|
||||
"180": "3 hr",
|
||||
"240": "4 hr"
|
||||
}
|
||||
},
|
||||
"ai_energy_level": {
|
||||
"state": {
|
||||
"1": "Level 1",
|
||||
"2": "Level 2"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sensor": {
|
||||
|
||||
Reference in new issue
Block a user