Fix C1 (switch discovery), C2 (power write_fn), I2 (golden oracle)

C1: SwitchDesc MQTT discovery now emits payload_on/off='On'/'Off',
state_on/off='On'/'Off', and a Jinja2 boolean-to-string value_template
so HA can correctly interpret and command switch entities.

C2: POWER write_fn corrected — path is ['power','vs','0'] segment list
(not a single slash-prefixed string), and payload comparison is
p == 'On' (not truthiness) so 'Off' can actually be sent.

I2: Golden regression oracle strengthened to exact equality for both
state_keys and discovery_unique_ids; prior subset-only check allowed
over-production to go undetected. Two golden tests now fail revealing
stale fixtures (dishwasher: +3 keys; refrigerator: +9 keys) — fixtures
intentionally NOT updated, human review required.
This commit is contained in:
Marc Billow
2026-06-30 16:32:42 -05:00
parent 5906aabd7c
commit 1a76bf6d47
4 changed files with 36 additions and 5 deletions
+5 -1
View File
@@ -204,7 +204,11 @@ def _discovery_payloads(bound, topic_prefix, ha_prefix, device_name, model):
cfg['options'] = list(opts) if isinstance(opts, (list, tuple)) else []
elif isinstance(d, SwitchDesc):
cfg['state_topic'] = state_topic
cfg['value_template'] = f"{{{{ value_json.{key} }}}}"
cfg['value_template'] = f"{{% if value_json.{key} %}}On{{% else %}}Off{{% endif %}}"
cfg['state_on'] = 'On'
cfg['state_off'] = 'Off'
cfg['payload_on'] = 'On'
cfg['payload_off'] = 'Off'
cfg['command_topic'] = f"{topic_prefix}/cmd/{key}"
elif isinstance(d, ButtonDesc):
cfg['command_topic'] = f"{topic_prefix}/cmd/{key}"
@@ -74,7 +74,7 @@ POWER = Capability(
SwitchDesc(key='power_switch', field='x.com.samsung.da.power',
name='Power',
value_fn=lambda v: v == 'On',
write_fn=lambda p, rep: (['/power/vs/0'], {'x.com.samsung.da.power': 'On' if p else 'Off'})),
write_fn=lambda p, rep: (['power', 'vs', '0'], {'x.com.samsung.da.power': 'On' if p == 'On' else 'Off'})),
SensorDesc(key='power_state', field='x.com.samsung.da.power',
name='Power state'),
BinarySensorDesc(key='power_state_binary', field='x.com.samsung.da.power',
+19
View File
@@ -10,6 +10,11 @@ def _bound(resources):
return discover(resources, reg)
def _bound_with_power(resources):
reg = {c.href: c for c in (common.KIDS_LOCK, common.ENERGY_METER, common.POWER)}
return discover(resources, reg)
def test_flatten_applies_value_fn(dishwasher_resources):
rd = build_runtime_descriptor(
_bound(dishwasher_resources), topic_prefix='t', ha_prefix='homeassistant',
@@ -38,3 +43,17 @@ def test_discovery_payloads_have_unique_ids(dishwasher_resources):
# payloads are JSON bytes carrying unique_id
cfg = json.loads(next(p for t, p in rd.discovery_payloads if 'child_lock' in t))
assert cfg['unique_id'] == 'samsung_dishwasher_child_lock'
def test_switch_discovery_config_has_correct_payloads(dishwasher_resources):
rd = build_runtime_descriptor(
_bound_with_power(dishwasher_resources), topic_prefix='samsung_dishwasher',
ha_prefix='homeassistant', device_name='Dishwasher', model='M',
name='dishwasher', default_port=49154)
cfg = json.loads(next(p for t, p in rd.discovery_payloads if 'power_switch' in t))
assert cfg['payload_on'] == 'On'
assert cfg['payload_off'] == 'Off'
assert cfg['state_on'] == 'On'
assert cfg['state_off'] == 'Off'
assert 'On' in cfg['value_template']
assert 'Off' in cfg['value_template']
+11 -3
View File
@@ -26,6 +26,14 @@ def _new_state_and_uids(name, resources):
def test_registry_reproduces_golden_state_keys(name, ip, request):
resources = json.load(open(f'local-tools/dumps/{ip}.json'))['resources']
golden = json.loads((GOLDEN / f'{name}.json').read_text())
state_keys, _ = _new_state_and_uids(name, resources)
missing = set(golden['state_keys']) - set(state_keys)
assert not missing, f"registry missing state keys: {sorted(missing)}"
state_keys, uids = _new_state_and_uids(name, resources)
assert set(state_keys) == set(golden['state_keys']), (
f"state_keys mismatch:\n"
f" extra: {sorted(set(state_keys) - set(golden['state_keys']))}\n"
f" missing: {sorted(set(golden['state_keys']) - set(state_keys))}"
)
assert set(uids) == set(golden['discovery_unique_ids']), (
f"unique_ids mismatch:\n"
f" extra: {sorted(set(uids) - set(golden['discovery_unique_ids']))}\n"
f" missing: {sorted(set(golden['discovery_unique_ids']) - set(uids))}"
)