diff --git a/custom_components/localthings/registry/capabilities/common.py b/custom_components/localthings/registry/capabilities/common.py index 69a9c2c..edc3f6c 100644 --- a/custom_components/localthings/registry/capabilities/common.py +++ b/custom_components/localthings/registry/capabilities/common.py @@ -177,29 +177,36 @@ ENERGY_METER = Capability( not rep or 'x.com.samsung.da.cumulativePower' in rep)), # cumulativeConsumption is a second, independently-varying running # total alongside cumulativePower -- some fridges (issue #26) report - # both. Self-gates off where only cumulativePower is present. + # both. Self-gates off where only cumulativePower is present. `not + # rep or` keeps the same empty-{} stub carve-out as power_watts/ + # energy_kwh above -- without it, an exists_fn permanently drops the + # entity if setup happens to land on a not-yet-fetched stub. SensorDesc(key='power_energy_kwh', field='x.com.samsung.da.cumulativeConsumption', name='Power energy', device_class='energy', state_class='total_increasing', unit='kWh', value_fn=wh_to_kwh, - exists_fn=lambda rep, resources: 'x.com.samsung.da.cumulativeConsumption' in rep), + exists_fn=lambda rep, resources: ( + not rep or 'x.com.samsung.da.cumulativeConsumption' in rep)), # AI Energy Mode's lifetime savings estimate vs. an unoptimized # baseline -- present on some models (e.g. TP1X_REF_21K, issue #21/ # #27) and absent on others (issue #20/#26), unlike cumulativePower. SensorDesc(key='energy_saved_kwh', field='x.com.samsung.da.cumulativeSavedPower', name='Energy saved', device_class='energy', state_class='total_increasing', unit='kWh', value_fn=wh_to_kwh, - exists_fn=lambda rep, resources: 'x.com.samsung.da.cumulativeSavedPower' in rep), + exists_fn=lambda rep, resources: ( + not rep or 'x.com.samsung.da.cumulativeSavedPower' in rep)), # Monthly billing-cycle totals -- the completed prior month and the # in-progress current month. Not ever-increasing (each resets at # month boundary), so no state_class. SensorDesc(key='energy_last_month_kwh', field='x.com.samsung.da.monthlyConsumption', name='Energy (last month)', device_class='energy', unit='kWh', value_fn=wh_to_kwh, - exists_fn=lambda rep, resources: 'x.com.samsung.da.monthlyConsumption' in rep), + exists_fn=lambda rep, resources: ( + not rep or 'x.com.samsung.da.monthlyConsumption' in rep)), SensorDesc(key='energy_this_month_kwh', field='x.com.samsung.da.thismonthlyConsumption', name='Energy (this month)', device_class='energy', unit='kWh', value_fn=wh_to_kwh, - exists_fn=lambda rep, resources: 'x.com.samsung.da.thismonthlyConsumption' in rep), + exists_fn=lambda rep, resources: ( + not rep or 'x.com.samsung.da.thismonthlyConsumption' in rep)), ), ) diff --git a/custom_components/localthings/registry/capabilities/fridge.py b/custom_components/localthings/registry/capabilities/fridge.py index 94fabd2..a258fa2 100644 --- a/custom_components/localthings/registry/capabilities/fridge.py +++ b/custom_components/localthings/registry/capabilities/fridge.py @@ -543,14 +543,22 @@ PANTRY_ZONE = Capability( # Write replaces only that item; other flags are preserved. # --------------------------------------------------------------------------- +def _flex_zone_supported(rep): + return set(rep.get('x.com.samsung.da.supportedOptions') or ()) + + def _flex_zone_current(rep): + # Every dump seen has at most one modes/supportedOptions overlap, so + # "first match" and "strip all matches" (in the write below) agree. If a + # future device ever reports two, this reads the first and the write + # would drop both -- revisit if that turns up. modes = rep.get('x.com.samsung.da.modes') or [] - supported = set(rep.get('x.com.samsung.da.supportedOptions') or ()) + supported = _flex_zone_supported(rep) return next((m for m in modes if m in supported), None) def _flex_zone_write(p, rep, href=None): - supported = set(rep.get('x.com.samsung.da.supportedOptions') or ()) + supported = _flex_zone_supported(rep) modes = [m for m in (rep.get('x.com.samsung.da.modes') or []) if m not in supported] modes.append(p) return ['mode', 'vs', '0'], {'x.com.samsung.da.modes': modes} diff --git a/tests/test_washer_capabilities.py b/tests/test_washer_capabilities.py index 580704d..ac143d8 100644 --- a/tests/test_washer_capabilities.py +++ b/tests/test_washer_capabilities.py @@ -271,7 +271,8 @@ class TestFlexWashAndComboFixturesHaveCompleteCoverage: log=unbound.append, ) assert unbound == [] - flatten(bound, resources) # doesn't raise + state = flatten(bound, resources) + assert 'dry_level' not in state # plain washer -- no supportedDryLevel field def test_dryer_combo(self): from custom_components.localthings.registry.adapter import flatten