Gate AC tropical night mode and air-quality sensors on real capability signals (#166)
Issue #166 (ARxxTXFCAWKNEU, board ARTIK051_PRAC_20K) reported tropical night mode, clean level, dust, fine dust, odor, and super fine dust entities showing up even though the reporter's units have no such physical features. All six were added in #164. The Sleep_<N> options token backing tropical_night_mode is present in every AC dump on record regardless of confirmed reality, so there's no usable signal at boot time -- it's now registered but disabled by default (matching the precedent already set by fridge.rack_count / cooktop.paired_hood_model), letting units that do have it opt in. /sensors/vs/0's item-type list has the same problem (all five types always listed, permanently zero on this board), but there turned out to be a real tell: a top-level x.com.samsung.da.cleanLevel scalar is present only alongside genuinely populated readings on every dump on record (tp1x_da_ac_rac_01011, the tp1x_da_ac_air air purifier fixture) and absent on every all-zero ARTIK051_PRAC_20K dump, including both #166 units and the original windfree/#17 fixtures this capability was first verified against -- which, per their /information/vs/0, turn out to be the same board revision as #166's units, so that "verification" never actually proved a real sensor either. AIR_QUALITY's exists_fn now requires that scalar, and the windfree/airconditioner golden fixtures are updated to match (those five entities no longer bind there).
This commit is contained in:
4 files changed
+109
-20
No files matched your search
@@ -128,7 +128,27 @@ def _sensor_item_value(items, type_):
|
||||
|
||||
|
||||
def _has_sensor_type(type_):
|
||||
"""Item-type presence AND a corroborating top-level
|
||||
x.com.samsung.da.cleanLevel scalar on the same /sensors/vs/0 rep.
|
||||
|
||||
Item-type presence alone isn't a real capability signal: issue #166
|
||||
(ARxxTXFCAWKNEU, board ARTIK051_PRAC_20K) lists all five item types with
|
||||
permanent zero values on both its units, the same shape as this repo's
|
||||
other ARTIK051_PRAC_20K dumps (the original issue #17 dump and the
|
||||
windfree fixture -- verified against the *same* board revision, per its
|
||||
/information/vs/0) -- yet the #166 reporter confirmed none of these
|
||||
sensors are physically present. The top-level cleanLevel scalar (separate
|
||||
from the CleanLevel item inside items[]) is only ever present alongside
|
||||
genuinely populated readings in every dump on record: present on
|
||||
tp1x_da_ac_rac_01011 (real AC, clean_level=1) and the tp1x_da_ac_air air
|
||||
purifier fixture (all five types real/nonzero), absent on every
|
||||
ARTIK051_PRAC_20K dump (all zero, including both #166 units). A small
|
||||
sample, but a consistent one and the only signal found that actually
|
||||
explains the #166 report -- gate on it rather than leaving the always-
|
||||
present item type to imply a capability that may not exist."""
|
||||
def fn(rep, resources):
|
||||
if 'x.com.samsung.da.cleanLevel' not in rep:
|
||||
return False
|
||||
return any(isinstance(i, dict) and i.get('x.com.samsung.da.type') == type_
|
||||
for i in (rep.get('x.com.samsung.da.items') or []))
|
||||
return fn
|
||||
@@ -441,12 +461,26 @@ CLIMATE = Capability(
|
||||
# Single-token option_write. Cloud: custom.airConditionerTropicalNightMode.
|
||||
# Gated off the legacy board for the same reason as beep above -- its
|
||||
# Sleep_ token is already the good_sleep Number below.
|
||||
#
|
||||
# exists_fn only proves the Sleep_ token slot is present, not that
|
||||
# tropical night mode is a real feature of the unit: issue #166
|
||||
# (ARxxTXFCAWKNEU) reports Sleep_0 in every dump -- the exact same
|
||||
# always-there-at-zero shape as the issue #17 dump #164 was verified
|
||||
# against -- yet the reporter confirmed their remote/app has no
|
||||
# tropical night mode control at all. Samsung's OCF options[] blob
|
||||
# carries this scaffolding token regardless of physical capability,
|
||||
# so there's no reliable signal here to gate on (same 'don't guess'
|
||||
# rule as elsewhere in this file, just with no signal to guess from).
|
||||
# Registered but disabled by default, same precedent as
|
||||
# fridge.rack_count / cooktop.paired_hood_* -- units that do have the
|
||||
# feature can enable it themselves.
|
||||
NumberDesc(key='tropical_night_mode', rep_fn=_tropical_night_value,
|
||||
exists_fn=lambda rep, resources: (
|
||||
not is_legacy_board(resources)
|
||||
and _option_token(rep, 'Sleep') is not None),
|
||||
write_fn=_tropical_night_write,
|
||||
native_min=0, native_max=16, step=1,
|
||||
enabled_default=False,
|
||||
icon='mdi:weather-night', entity_category='config'),
|
||||
# Settings that this board generation keeps as options[] tokens.
|
||||
SwitchDesc(key='spi', rep_fn=_option_token_on('Spi'),
|
||||
@@ -702,6 +736,21 @@ HUMIDITY = Capability(
|
||||
# are 1- or 2-element arrays with no corroborating scalar, so they stay string
|
||||
# diagnostics (see _sensor_item_value for the 2-element ambiguity and why only
|
||||
# v[0] is taken). No unit is advertised on the resource, so no device_class.
|
||||
#
|
||||
# _has_sensor_type requires that same top-level cleanLevel scalar, not just
|
||||
# item-type presence: issue #166 (ARxxTXFCAWKNEU, board ARTIK051_PRAC_20K)
|
||||
# reports all five item types on both its units, values permanently
|
||||
# '0'/['0','0'] -- the exact same shape as the issue #17 dump AIR_QUALITY was
|
||||
# first verified against and the WindFree fixture (see
|
||||
# test_air_quality_sensors_from_sensors_vs_items) -- both the *same board
|
||||
# revision* per /information/vs/0, so that "verification" never actually
|
||||
# proved a real sensor either. Item-type presence alone is Samsung's OCF
|
||||
# scaffolding listing every known sensor type regardless of physical
|
||||
# capability, not a capability signal. The top-level scalar is: it's present,
|
||||
# with genuinely populated readings, on every dump with a confirmed-real
|
||||
# sensor (tp1x_da_ac_rac_01011, and the tp1x_da_ac_air air purifier fixture),
|
||||
# and absent on every all-zero ARTIK051_PRAC_20K dump on record, including
|
||||
# both #166 units. Gate on it.
|
||||
AIR_QUALITY = Capability(
|
||||
href='/sensors/vs/0',
|
||||
poll_tier='cold',
|
||||
|
||||
-5
@@ -7,19 +7,14 @@
|
||||
"alarm_code",
|
||||
"auto_clean",
|
||||
"beep",
|
||||
"clean_level",
|
||||
"climate",
|
||||
"current_temperature_c",
|
||||
"diagnosis_status",
|
||||
"display_light",
|
||||
"dust",
|
||||
"energy_kwh",
|
||||
"energy_saved_kwh",
|
||||
"fine_dust",
|
||||
"humidity",
|
||||
"odor",
|
||||
"power_watts",
|
||||
"super_fine_dust",
|
||||
"tropical_night_mode"
|
||||
]
|
||||
}
|
||||
@@ -7,18 +7,13 @@
|
||||
"alarm_code",
|
||||
"auto_clean",
|
||||
"beep",
|
||||
"clean_level",
|
||||
"climate",
|
||||
"current_temperature_c",
|
||||
"diagnosis_status",
|
||||
"display_light",
|
||||
"dust",
|
||||
"energy_kwh",
|
||||
"fine_dust",
|
||||
"humidity",
|
||||
"odor",
|
||||
"power_watts",
|
||||
"super_fine_dust",
|
||||
"tropical_night_mode"
|
||||
]
|
||||
}
|
||||
@@ -564,6 +564,19 @@ def test_tropical_night_write_is_single_token_options_merge():
|
||||
['mode', 'vs', '0'], {'x.com.samsung.da.options': ['Sleep_6']})
|
||||
|
||||
|
||||
def test_tropical_night_disabled_by_default():
|
||||
"""Issue #166: Sleep_0 is present in /mode/vs/0's options on every dump
|
||||
seen, including the original issue #17 dump this capability was verified
|
||||
against (see airconditioner_device.json / _ac()) -- yet the #166 reporter
|
||||
confirmed their unit (also an ARTIK051_PRAC_20K board, per its own
|
||||
/information/vs/0) has no tropical night mode feature at all. The token
|
||||
slot's presence proves nothing about the physical feature, so this can't
|
||||
be existence-gated any tighter than it already is -- registered but
|
||||
disabled by default instead, same precedent as fridge.rack_count /
|
||||
cooktop.paired_hood_model."""
|
||||
assert _tropical_desc().enabled_default is False
|
||||
|
||||
|
||||
def test_tropical_night_absent_when_no_sleep_token():
|
||||
"""TP1X_DA-AC-WAC (window AC) carries no Sleep_ option -- tropical night
|
||||
mode must not bind."""
|
||||
@@ -661,19 +674,56 @@ def test_air_quality_sensors_from_sensors_vs_items():
|
||||
by a top-level cleanLevel scalar, so it's an int measurement; the others
|
||||
are string diagnostics. Dust/FineDust/SuperFineDust carry a 2-element
|
||||
array whose second element is unconfirmed -- v[0] is taken as the reading
|
||||
(see _sensor_item_value)."""
|
||||
(see _sensor_item_value). Exercised on tp1x_da_ac_rac_01011, the fixture
|
||||
proven real by the corroborating scalar (see
|
||||
test_air_quality_present_with_corroborating_clean_level_scalar) --
|
||||
windfree no longer applies here post-#166 fix, since it lacks that
|
||||
scalar and binds no air-quality entities at all."""
|
||||
reg, resources = _ac_tp1x()
|
||||
state = flatten(
|
||||
discover(resources, reg.capabilities, reg.pattern_capabilities), resources)
|
||||
assert state['clean_level'] == 1 # numeric (int), corroborated
|
||||
for key in ('dust', 'fine_dust', 'super_fine_dust'):
|
||||
assert state[key] == '0' # string diagnostic
|
||||
|
||||
|
||||
def test_air_quality_absent_without_corroborating_clean_level_scalar():
|
||||
"""Issue #166 (ARxxTXFCAWKNEU, board ARTIK051_PRAC_20K): /sensors/vs/0
|
||||
lists all five item types, values permanently '0'/['0', '0'] -- the exact
|
||||
same shape as the WindFree fixture, which is the *same board revision*
|
||||
(see /information/vs/0: both report modelNum
|
||||
'ARTIK051_PRAC_20K|10217841|...') that this capability was originally
|
||||
verified against. The reporter confirmed none of these sensors are
|
||||
physically present on their unit, which means that original
|
||||
"verification" never actually proved a real sensor either -- item-type
|
||||
presence is Samsung's OCF scaffolding, not a capability signal (see
|
||||
_has_sensor_type). The tell that's actually reliable: a top-level
|
||||
x.com.samsung.da.cleanLevel scalar (separate from the CleanLevel item),
|
||||
present only alongside genuinely populated readings on every dump on
|
||||
record. WindFree lacks it -- so post-fix, none of the five entities
|
||||
should bind there at all, matching the #166 report."""
|
||||
reg, resources = _ac_windfree()
|
||||
state = flatten(
|
||||
discover(resources, reg.capabilities, reg.pattern_capabilities), resources)
|
||||
assert state['clean_level'] == 0 # numeric (int), corroborated
|
||||
for key in ('odor', 'dust', 'fine_dust', 'super_fine_dust'):
|
||||
assert state[key] == '0' # string diagnostic
|
||||
# tp1x_da_ac_rac_01011 is the only fixture with a non-zero air-quality
|
||||
# reading -- the one that catches a value_fn regression.
|
||||
reg2, resources2 = _ac_tp1x()
|
||||
state2 = flatten(
|
||||
discover(resources2, reg2.capabilities, reg2.pattern_capabilities), resources2)
|
||||
assert state2['clean_level'] == 1
|
||||
assert 'x.com.samsung.da.cleanLevel' not in resources['/sensors/vs/0']
|
||||
for key in ('clean_level', 'odor', 'dust', 'fine_dust', 'super_fine_dust'):
|
||||
assert key not in state, key
|
||||
|
||||
|
||||
def test_air_quality_present_with_corroborating_clean_level_scalar():
|
||||
"""tp1x_da_ac_rac_01011 carries the top-level cleanLevel scalar alongside
|
||||
a genuinely populated CleanLevel reading -- the one fixture in this repo
|
||||
proven real rather than placeholder, so its air-quality entities must
|
||||
still bind post-fix. It has no Odor item at all (unrelated to the scalar
|
||||
gate -- item-type absence, not zero-value ambiguity)."""
|
||||
reg, resources = _ac_tp1x()
|
||||
assert resources['/sensors/vs/0']['x.com.samsung.da.cleanLevel'] == '1'
|
||||
state = flatten(
|
||||
discover(resources, reg.capabilities, reg.pattern_capabilities), resources)
|
||||
assert state['clean_level'] == 1
|
||||
for key in ('dust', 'fine_dust', 'super_fine_dust'):
|
||||
assert key in state
|
||||
assert 'odor' not in state
|
||||
|
||||
|
||||
def test_air_quality_absent_when_no_sensor_items():
|
||||
|
||||
Reference in new issue
Block a user