Files
localthings/tests/test_microwave_capabilities.py
T
Marc Billow afea1fd9ca Clean up microwave PR after main merge: dedupe skill doc, derive cook modes live
The merge from main had left the adding-device-support skill with a stale
duplicate "Enum selects need translation support" section (referencing a
nonexistent strings.json) sitting alongside the canonical version, and
main's new "never hard-code the dump" guidance hadn't landed on this
branch at all -- restore it and drop the duplicate.

Apply that guidance to the microwave capability itself: MICROWAVE_MODE's
cook_mode select was carrying a hardcoded _MICROWAVE_MODES tuple lifted
from the single issue #66 dump, even though /mode/vs/0 reports its own
live supportedModes list. Switch to options_field and validate writes
against the live rep instead of the static vocabulary, so a microwave
with a narrower or wider mode set (grill-less models, etc.) isn't stuck
with this one unit's options.

The microwave entities also shipped with no translations/en.json entries
at all (cook_mode, cavity_state, power_level, cavity_temp) -- add them,
mirrored into nl.json, and update the affected tests.
2026-07-27 00:03:55 +00:00

137 lines
5.5 KiB
Python

"""Unit tests for microwave-family capabilities (issue #66)."""
from custom_components.localthings.registry.by_type import for_device_by_model
from custom_components.localthings.registry.capabilities import microwave
from custom_components.localthings.registry.discovery import discover
# ---------------------------------------------------------------------------
# Device-type detection + full-dump coverage
# ---------------------------------------------------------------------------
def test_microwave_fixture_resolves_and_has_no_unbound_hrefs():
"""The issue #66 dump previously came back device_type='unknown' with
/connected/vs/0 and /recipe/cook/vs/0 unbound -- resolving via the
'-MICROWAVE-' modelNum token fallback must leave every href in the
microwave registry bound or ignored."""
from tests.conftest import _load_device
resources = _load_device('microwave')
info = resources['/information/vs/0']
reg = for_device_by_model(
info['x.com.samsung.da.modelNum'], info['x.com.samsung.da.description'])
assert reg is not None
assert reg.name == 'microwave'
unbound = []
discover(resources, reg.capabilities, reg.pattern_capabilities, log=unbound.append)
assert unbound == []
# ---------------------------------------------------------------------------
# MICROWAVE_MODE -- SelectDesc reads its options live from supportedModes
# ---------------------------------------------------------------------------
_SUPPORTED_MODES_REP = {'x.com.samsung.da.supportedModes': [
'NoOperation', 'MicroWave', 'MicroWaveGrill', 'Grill', 'Autocook', 'AutocookCustom',
]}
def test_microwave_mode_options_field():
desc = microwave.MICROWAVE_MODE.entities[0]
assert desc.options_field == 'x.com.samsung.da.supportedModes'
def test_microwave_mode_write_round_trips():
desc = microwave.MICROWAVE_MODE.entities[0]
path, body = desc.write_fn('MicroWave', _SUPPORTED_MODES_REP)
assert path == ['mode', 'vs', '0']
assert body['x.com.samsung.da.modes'] == ['MicroWave']
def test_microwave_mode_rejects_unknown():
desc = microwave.MICROWAVE_MODE.entities[0]
assert desc.write_fn('SpaghettiMode', _SUPPORTED_MODES_REP) is None
def test_microwave_mode_rejects_when_not_in_live_supported_list():
"""A mode absent from *this* device's live supportedModes is rejected
even if another microwave model supports it -- the write must not fall
back to a static vocabulary."""
desc = microwave.MICROWAVE_MODE.entities[0]
narrower_rep = {'x.com.samsung.da.supportedModes': ['NoOperation', 'MicroWave']}
assert desc.write_fn('Grill', narrower_rep) is None
# ---------------------------------------------------------------------------
# MICROWAVE_MODE -- sound options-array RMW
# ---------------------------------------------------------------------------
def _mode_rep(*extra_opts):
return {'x.com.samsung.da.options': [
'DeviceType_MW5300A-/KO0', 'ScreenTimeOut_10min', 'Sound_On', *extra_opts,
]}
def test_sound_write_preserves_other_options():
desc = next(e for e in microwave.MICROWAVE_MODE.entities if e.key == 'sound')
path, body = desc.write_fn('Off', _mode_rep())
opts = body['x.com.samsung.da.options']
assert 'Sound_Off' in opts
assert 'ScreenTimeOut_10min' in opts # other slot unchanged
def test_sound_write_requires_existing_options():
desc = next(e for e in microwave.MICROWAVE_MODE.entities if e.key == 'sound')
assert desc.write_fn('On', {}) is None
def test_sound_write_rejects_invalid_payload():
desc = next(e for e in microwave.MICROWAVE_MODE.entities if e.key == 'sound')
assert desc.write_fn('Loud', _mode_rep()) is None
# ---------------------------------------------------------------------------
# MICROWAVE_CAVITY -- plain sensors, no write path
# ---------------------------------------------------------------------------
def test_cavity_state_reads_state_field():
desc = next(e for e in microwave.MICROWAVE_CAVITY.entities if e.key == 'cavity_state')
assert desc.value_fn('Ready') == 'Ready'
def test_power_level_parses_watts():
desc = next(e for e in microwave.MICROWAVE_CAVITY.entities if e.key == 'power_level')
assert desc.value_fn('700W') == 700
assert desc.value_fn('0W') == 0
def test_power_level_rejects_unparseable():
desc = next(e for e in microwave.MICROWAVE_CAVITY.entities if e.key == 'power_level')
assert desc.value_fn('') is None
assert desc.value_fn(None) is None
# ---------------------------------------------------------------------------
# MICROWAVE_TEMPERATURE -- read-only current-temperature sensor
# ---------------------------------------------------------------------------
def test_cavity_temp_reads_current_field():
desc = microwave.MICROWAVE_TEMPERATURE.entities[0]
items = [{'x.com.samsung.da.current': '1', 'x.com.samsung.da.desired': '0'}]
assert desc.value_fn(items) == 1
def test_cavity_temp_missing_items_is_none():
desc = microwave.MICROWAVE_TEMPERATURE.entities[0]
assert desc.value_fn([]) is None
def test_cavity_temp_unit_defaults_celsius():
from custom_components.localthings.registry.capabilities.microwave import _cavity_temp_unit
rep = {'x.com.samsung.da.items': [{'x.com.samsung.da.unit': 'Celsius'}]}
assert _cavity_temp_unit(rep) == '°C'
def test_cavity_temp_unit_falls_back_when_absent():
from custom_components.localthings.registry.capabilities.microwave import _cavity_temp_unit
assert _cavity_temp_unit({'x.com.samsung.da.items': []}) == '°C'