mbillow's follow-up review: exposing read-only Software/Firmware (and
Outdoor/Touch IC) version strings is "data for the sake of exposing it" --
no user control, just clutter, and every other registry leaves
/information/vs/0 in the global ignore as identity plumbing. Conforming to
that stance rather than expanding the entity surface for no user story.
- by_type/airconditioner.py: revert to *ignored.IGNORED (no _IGNORED_LESS_INFO
filter); drop airconditioner.INFO from the registry.
- capabilities/airconditioner.py: remove the INFO capability, the _info_version
/_info_items_of_type /_has_info_version helpers, and HREF_INFORMATION.
- translations/{en,nl}.json: drop software_version, firmware_version,
firmware_version_2/3, outdoor_unit_version, touch_ic_version.
- tests: drop the six INFO tests; golden regenerated for 8 AC + dehumidifier.
625 tests pass.
Per mbillow's review on PR #129 (CHANGES_REQUESTED). Items 1-7 + smaller.
1. INFO: expose each /information/vs/0 version item per (type, ordinal) instead
of collapsing to a single first-Firmware value. Boards carry 1-3 Firmware
items (separate MCUs) plus an Outdoor unit and (window AC) a Touch IC item;
each is a distinct version string. _info_version(items, type_, ordinal)
+ _has_info_version exists_fn gate, so an item with no number (tp2x_rac_20k's
second Firmware) suppresses the entity rather than binding unknown.
2. air_filter_threshold is locally writable, not cloud-only -- confirmed live
on ARTIK051_PRAC: POST filterDesiredUsage=700 to /filter/airdustfilter/vs/0
-> 2.04, read-back 700, persists; restored to 500. Converted from a
read-only sensor to a SelectDesc keyed to the device's
supportedFilterDesiredUsage enum (options_field), with a write_fn that
POSTs the scalar field. Only binds where the enum is advertised; boards
without it leave this writable field unexposed rather than guess the valid
set (don't-guess).
3. air_filter_usage_hours uses unit_fn reading filterCapacityUnit ('Hour'->'h')
instead of a hardcoded unit, so a board advertising a different unit doesn't
mislabel a duration statistic.
4. air_filter_usage_hours state_class is total_increasing, not measurement --
filterUsage is a lifetime hour counter that resets on filter replacement.
5. INFO and air_filter_threshold now carry exists_fn (AIR_QUALITY already did),
so they don't bind a permanently-unknown entity when their item/field is
absent. Also fixes the caww_tp2 golden nuance (filterDesiredUsage absent
-> no threshold key, matching what HA would actually create).
6. AIR_QUALITY: CleanLevel is corroborated as numeric by a top-level
x.com.samsung.da.cleanLevel scalar (tp1x_da_ac_rac_01011 reports both as
'1'), so clean_level is now an int measurement; odor/dust/fine_dust stay
string diagnostics. Reinstated the 2-element-array ambiguity note (Dust/
FineDust/SuperFineDust report ['0','0']; v[1] meaning unconfirmed, v[0]
taken as the reading). SuperFineDust is now modeled for consistency with
Dust/FineDust (same shape), rather than skipped without reason.
7. _beep_write restores the last non-Mute Volume level on 'On' instead of
forcing Volume_100, so an intermediate setting (e.g. Volume_50 set via
the cloud) survives an off/on cycle; falls back to 100 when no prior level.
Smaller: test_air_quality now asserts the tp1x_da_ac_rac_01011 clean_level==1
non-zero reading (the one value_fn-regression catch in the corpus); renamed
by_type's _AC_IGNORED -> _IGNORED_LESS_INFO to resolve the two-meaning
collision with the capabilities module's _AC_IGNORED (href strings); golden
regenerated for the 8 AC fixtures + dehumidifier.
612 tests pass.
Layer the ARTIK051_PRAC additive entities onto the upstream registry on a
fresh branch (additive-only; the round-1 display-light/mute-once/WindFree
work was independently shipped upstream and is not redone here).
New entities, all driven by single-token option_write or item reads:
- beep (switch): Volume_Mute/Volume_100 option token; single-token merge --
a full options RMW reverts on ARTIK051_PRAC. Cloud counterpart:
samsungce.airConditionerAudioFeedback (on/off only; level is cloud-only).
- tropical_night_mode (number 0-16): Sleep_<N> option token. Cloud:
custom.airConditionerTropicalNightMode.
- air_filter_usage_hours / air_filter_threshold (duration sensors, h):
raw filterUsage count and filterDesiredUsage alarm threshold. Threshold
SET is cloud-only (samsungce.dustFilterAlarm); local read-only.
- clean_level / odor / dust / fine_dust (diagnostic sensors): /sensors/vs/0
items[]. No unit advertised on the resource, so no device_class until a
populated reading + unit is observed (don't-guess rule). SuperFineDust
intentionally not modeled.
- software_version / firmware_version (diagnostic sensors):
/information/vs/0 items[]. The href is globally ignored as identity
plumbing; the AC registry drops that ignore so INFO is the sole cap on it.
Translations: en.json + nl.json (every-language-mirrors-english invariant).
Golden regression: regenerated the 8 AC fixtures + dehumidifier (reuses
AIR_FILTER) for the new state keys.
608 tests pass.
Finishes PR #91's contribution (pedroperosin) with the requested review
changes applied, on our own branch:
- Preset resolution is now fully dynamic, read from each unit's own
/mode/convenient/vs/0 supportedModes instead of a static per-model
table -- any board's convenient modes (including WindFree's Nano/
NanoSleep) surface without code changes. Unlabelled codes across
existing fixtures (longwind, motionindirect, motiondirect, drycomfort)
plus the two new WindFree ones are added to en.json/nl.json.
- 'Auto' now maps to HVACMode.AUTO instead of HEAT_COOL: these are
single-setpoint "device decides" units, not two-setpoint heat+cool
ones. 'Fan' is added alongside 'Wind' as a second FAN_ONLY spelling;
a new _device_code_for_hvac() picks the code from the unit's own
supportedModes since the flat map can't disambiguate two device codes
mapping to one HA value.
- Detection gains a hyphenated '-RAC-' modelNum fallback (alongside the
existing '_RAC_') for cool-only global RAC variants whose
/otninformation/vs/0 ships no swVersionInfo block.
- Adds a display_light switch sourced from /mode/vs/0's opaque options
blob (inverted Light_On/Light_Off token) for boards with no dedicated
/light/vs/0 switch.
Write-path behavioural change (touches the path iterated on across
#9/#17/#27/#38/#54): power now targets the vendor /power/vs/0 instead of
the OCF /power/0, and _is_on() reads vendor-first. /power/0 is absent on
several known AC boards, so the previous OCF-first read/write pair could
report and act on stale state -- consistent with #53's "can turn on but
not off". Target temperature now picks its channel (OCF pair vs. vendor
items[]) based on which the unit actually reports, reading and writing
the same one.
The vendor temperature write and the new display-light write both carry
only the changed field(s), not the whole resource -- confirmed sufficient
on the wire, the device merges the rest itself. That requires the
coordinator's optimistic cache to do the same merge on the read side so a
setpoint change doesn't blank out current/min/max/unit for the settle
window: common.py gains merge_items_field() next to the existing
merge_options_field(), and async_send_command wires it in for any write
touching x.com.samsung.da.items.
New fixture/golden for the cool-only global RAC variant (TP1X_DA-AC-RAC-
01001, AI_RAC_GLOBAL_COOLONLY_3.0); display_light added to the goldens
for boards that gain the new options-based switch. Regenerated against
current main rather than copied from the original PR, since those
goldens had already shifted (current_temperature_c/humidity from issue
#75).
Three gaps reported against an ARTIK051_PRAC_20K WindFree unit vs. the
SmartThings integration:
1. Missing WindFree/motion convenient-mode presets -- left alone here.
PR #91 replaces climate.py's static _DEVICE_TO_PRESET table with a
generic resolver that reads any preset code straight off the unit's
own supportedModes, which already covers this (and more generically
than a per-model dict would) -- adding one here would just conflict.
2. No horizontal oscillation: /wind/direction/vs/0's supportedModes
includes Left_And_Right, which _DEVICE_TO_SWING had no mapping for.
Add it to HA's standard 'horizontal' swing constant.
3. No standalone humidity/current-temperature sensors: the climate card
already reads both internally, but nothing exposed them as entities
for history/automations. Add CURRENT_TEMPERATURE (OCF
/temperature/current/0) with a CURRENT_TEMPERATURE_VS vendor fallback
(same match_fn-gated pair shape as common.py's POWER_GENERIC/
POWER_VS_FALLBACK), and a HUMIDITY sensor reading /humidity/vs/0's
fivepercentHumidity field -- the only one of the three
humidity-shaped fields across /humidity/0 and /humidity/vs/0 that
isn't permanently stuck at 0 on every dump seen.
Regenerates the five existing AC goldens (all pick up
current_temperature_c; most pick up humidity) and adds a dedicated
fixture from the issue's WindFree dump, whose /humidity/vs/0 actually
has live fivepercentHumidity data.
New '-CAWW-' modelNum token for multi-indoor-unit commercial AC
installs -- these report no oneUiVersion, same as the other RAC/PRAC
boards. Once routed to the existing airconditioner registry, every
resource in the reporter's dump already binds except one new
SAC-specific installation-topology blob, now ignored.
Closes the coverage-gap repair for two more room-AC boards (issues #37,
- TP2X_RAC_20K reports no oneUiVersion and no '_PRAC_' modelNum token, so
its device type went unrecognized -- add a '_RAC_' fallback token to
for_device_by_model.
- Bind the resources both dumps actually populate: mute-once (switch),
display light (switch), self-check (reused from fridge.SELF_CHECK), and
the circuit-breaker current-limit setting (read-only diagnostic sensors,
since its write contract isn't confirmed from the dump).
- Ignore the rest as plumbing/opaque/unconfirmed: control-set descriptor,
keepnormalstate flag, software-reset trigger, AI Sleep handshake,
absence-monitoring stubs, remote-data-control, remote-temperature,
reservation ruleset, and the welcome-temperature tracker.
Adds scrubbed fixtures + goldens for both dumps and extends the AC
capability/golden-regression tests to assert zero unbound hrefs.
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.