Files
localthings/tests/test_entities.py
T
Marc Billow 296535d0f2 feat: air conditioner support with a composite climate entity (#17)
Add support for Samsung room air conditioners (ARTIK051_PRAC-class), the
first device whose core controls map onto a single Home Assistant `climate`
entity rather than a scatter of switches/selects/numbers.

- New `climate` platform + `ClimateDesc`: one composite entity that reads
  power, HVAC mode, current/target temperature, fan (wind) strength, swing
  (wind direction) and the convenient-mode preset across several OCF
  resources and writes back to each. On/off folds into HVACMode.OFF /
  TURN_ON/OFF; convenient mode folds into preset_mode. The entity binds one
  primary resource (/mode/vs/0) and reads its siblings from the coordinator
  snapshot, reusing the cross-resource read pattern from number/select.
- New `airconditioner` capability module + by_type registry, routed via the
  `_PRAC_` modelNum token. Reuses common ALARMS/ENERGY_METER,
  fridge.FIRMWARE_UPDATE and dishwasher.DIAGNOSIS; air purify and auto clean
  as config switches; air dust filter status/usage as diagnostics (usage
  normalized to a percentage of rated capacity).
- Climate-consumed and all-zero/ambiguous resources (temperature/wind,
  /sensors, /humidity) are declared as AC-scoped coverage so every href in
  the dump binds or is covered -- no coverage-gap repair.
- Fan/swing modes map onto HA standard constants (auto-localized); the
  custom fan `turbo`, presets `quiet/smart/speed`, and the filter-status
  enum get translations in strings.json + translations/en.json.
- Scrubbed fixture, golden, and tests: registry routing, zero unbound
  hrefs, the climate write contract, and filter-% normalization.
2026-07-21 14:38:14 +00:00

37 lines
1.4 KiB
Python

from custom_components.localthings.registry.entities import (
SensorDesc, BinarySensorDesc, SelectDesc, SwitchDesc, ButtonDesc,
NumberDesc, TimeDesc, ClimateDesc, PLATFORM_OF,
)
def test_value_fn_defaults_to_identity():
d = SensorDesc(key='power', field='x.com.samsung.da.instantaneousPower')
assert d.value_fn(42) == 42
def test_descriptions_are_frozen():
d = SensorDesc(key='power', field='f')
try:
d.key = 'other'
except Exception as e:
assert 'frozen' in str(type(e)).lower() or 'cannot' in str(e).lower()
else:
raise AssertionError("expected frozen dataclass")
def test_platform_mapping_covers_all_subclasses():
assert PLATFORM_OF[SensorDesc] == 'sensor'
assert PLATFORM_OF[BinarySensorDesc] == 'binary_sensor'
assert PLATFORM_OF[SelectDesc] == 'select'
assert PLATFORM_OF[SwitchDesc] == 'switch'
assert PLATFORM_OF[ButtonDesc] == 'button'
assert PLATFORM_OF[NumberDesc] == 'number'
assert PLATFORM_OF[TimeDesc] == 'time'
assert PLATFORM_OF[ClimateDesc] == 'climate'
def test_select_carries_options_and_write_fn():
d = SelectDesc(key='sound', field='mode', options=('voice', 'tone', 'mute'),
write_fn=lambda p, rep: (['settings', 'sound', 'mode', 'vs', '0'], {'mode': p}))
assert d.write_fn('tone', {}) == (['settings', 'sound', 'mode', 'vs', '0'], {'mode': 'tone'})