fix: address code review feedback on issue-triage batch PR (#181, #183, #189, #196, #207, #208, #210)

common:
- Make KIDS_LOCK_GENERIC also a read-only BinarySensorDesc (device_class='lock'),
  flipping value_fn to not bool(v) so /kidslock/0 value=False and
  /kidslock/vs/0 kidsLock='Ready' render with the same polarity ('On'
  means open/unlocked per HA's lock device class). The old SwitchDesc
  form never honored device_class='lock' -- HA's switch platform only
  accepts 'outlet'/'switch' -- so the surface was a plain switch whose
  'On' meaning drifted across boards. Tests updated.

air_monitor:
- Add state_class='measurement' to dust/fine_dust/super_fine_dust so
  the readings feed HA long-term statistics (co2 already had it).
- Import _AIR_QUALITY_SENSORS from air_purifier instead of duplicating
  it byte-for-byte; update common.sensor_item_value's docstring to
  mention the third caller.

by_type/__init__: drop trailing whitespace on the new 'ASM' line.

translations/en.json + nl.json: move the new 'dnd' switch entry to its
correct alphabetical position (after display_light, before fast_preheat).

SKILL.md: add an explicit read-side rule to §5's educated-guesses
section -- guessed unit/device_class/state_class on a SensorDesc
silently mislabels readings forever with no 4.xx to catch it (unlike
guessed writes, which the device rejects). The prior air_monitor
docstring cited this carve-out as if it existed; now it does.

tests/water_purifier (issue #196): change the ailite fixture's
favorite.defaultTemperature from '85' to '50' so the test actually
reproduces the reported bug -- '50' is in showList only, so a
descriptor reading from supportedList would fail the assertion that
the current default is in its options list.
This commit is contained in:
Marc Billow committed 2026-07-31 15:17:18 -05:00
1 parent a7d57086e9
commit b7d3f86ec7
9 files changed
+83 -56

No files matched your search

+1 -1
View File
@@ -166,7 +166,7 @@
"href": "/favorite/hotwater/vs/0",
"rep": {
"x.com.samsung.da.favorite.revision": "0",
"x.com.samsung.da.favorite.defaultTemperature": "85",
"x.com.samsung.da.favorite.defaultTemperature": "50",
"x.com.samsung.da.favorite.showList": [
"40",
"50",
+11 -5
View File
@@ -232,13 +232,19 @@ class TestWmSetinfoFlags:
class TestKidsLockFallback:
def test_generic_read_write(self):
def test_generic_is_read_only(self):
"""Issues #181/#183: the kids-lock resource is read-only on real
hardware (writing the *correct* value still 4.05s, and no
SwitchDesc device_class='lock' is honored by HA -- the switch
platform only accepts 'outlet'/'switch'). KIDS_LOCK_GENERIC and
KIDS_LOCK_VS_FALLBACK now share the same shape (BinarySensorDesc,
device_class='lock', same key) so both surfaces render with the
same polarity: 'On' means open/unlocked."""
assert common.KIDS_LOCK_GENERIC.href == '/kidslock/0'
desc = common.KIDS_LOCK_GENERIC.entities[0]
assert desc.value_fn(True) is True
path, body = desc.write_fn('On', {})
assert path == ['kidslock', '0']
assert body == {'value': True}
assert isinstance(desc, BinarySensorDesc)
assert desc.value_fn(False) is True # value=False -> On=Unlocked
assert desc.value_fn(True) is False # value=True -> Off=Locked
def test_vs_fallback_gated(self):
assert common.KIDS_LOCK_VS_FALLBACK.match_fn({}, {'/kidslock/vs/0': {}}) is True
+14 -10
View File
@@ -370,21 +370,25 @@ def test_ailite_hot_water_temperature_gated_off_without_supported_list():
def test_ailite_favorite_hotwater_temperature_options_include_the_custom_value():
"""This board's real dump (issue #196) is the concrete case: the user
added a custom 50C value via the SmartThings app's "temperatures to
display" editor, so favorite.showList is
['40', '50', '75', '85', '90'] while favorite.supportedList stays the
fixed ['40', '75', '85', '90'] -- '50' only ever appears in showList.
Whichever of the two the descriptor reads from, a default temperature
of '50' would render as HA's 'unknown' state unless that field's raw
option list actually contains '50'."""
"""Issue #196's concrete failure case: the user added a custom 50C value
via the SmartThings app's "temperatures to display" editor, so the
board's defaultTemperature is now '50'. showList contains '50'
([40, 50, 75, 85, 90]) but supportedList does NOT (still the fixed
[40, 75, 85, 90]). Reading options_field='x.com.samsung.da.favorite.supportedList'
-- the old behavior -- would register a select whose options list
doesn't contain the current default, so HA would render the entity as
'unknown'. The descriptor must read from showList so '50' is in
options."""
desc = _desc_ailite('favorite_hotwater_temperature')
assert desc.options_field == 'x.com.samsung.da.favorite.showList'
_, resources = _water_purifier_ailite()
rep = resources['/favorite/hotwater/vs/0']
assert rep['x.com.samsung.da.favorite.defaultTemperature'] in rep[desc.options_field]
# defaultTemperature='50' must be a member of the field the descriptor
# actually reads -- this is the precise assertion that would fail under
# the old supportedList behavior.
assert rep['x.com.samsung.da.favorite.defaultTemperature'] == '50'
assert '50' in rep[desc.options_field]
assert '50' not in rep['x.com.samsung.da.favorite.supportedList']
assert '50' in rep['x.com.samsung.da.favorite.showList']
def test_ailite_sound_mode_options_come_from_live_supported_modes():