Merge pull request #296 from perseus177/ac-good-sleep-halfhours
fix(airconditioner): good_sleep is hours, but the token counts half hours
This commit is contained in:
@@ -311,11 +311,14 @@ def _option_switch_write(prefix):
|
||||
return write
|
||||
|
||||
|
||||
def _option_number_write(prefix):
|
||||
def _option_number_write(prefix, factor=1):
|
||||
"""Write a numeric options token. `factor` converts the entity's unit into the
|
||||
token's own: good_sleep is offered in hours while the token counts half hours."""
|
||||
|
||||
def write(payload, rep, href=None):
|
||||
return (
|
||||
["mode", "vs", "0"],
|
||||
{"x.com.samsung.da.options": option_write(prefix, str(round(float(payload))))},
|
||||
{"x.com.samsung.da.options": option_write(prefix, str(round(float(payload) * factor)))},
|
||||
)
|
||||
|
||||
return write
|
||||
@@ -528,16 +531,34 @@ CLIMATE = Capability(
|
||||
icon="mdi:air-filter",
|
||||
entity_category="config",
|
||||
),
|
||||
# "Good Sleep" timer. 0 = off; the upper bound is a guess (only 0 has
|
||||
# been observed on hardware), so a write above 0 is unverified.
|
||||
# "Good Sleep" timer, offered in hours. 0 = off.
|
||||
#
|
||||
# The token counts *half* hours, so it is halved on the way in and doubled
|
||||
# on the way out. The appliance's own app pairs a picker of durations with
|
||||
# the values it puts on the wire, one to one:
|
||||
#
|
||||
# 0:00 0:30 1:00 1:30 2:00 2:30 3:00 4:00 5:00 ... 12:00
|
||||
# 0 1 2 3 4 5 6 8 10 ... 24
|
||||
#
|
||||
# which also pins the maximum at 12 hours (its own help text says so:
|
||||
# "will be turned off after a selected period of time (Max. 12 hours)").
|
||||
# Before this the token was published as if it were hours: the entity
|
||||
# capped at 12, which set six, and twelve hours could not be asked for at
|
||||
# all.
|
||||
#
|
||||
# Half-hour steps are what the app offers below three hours; above that it
|
||||
# offers whole hours only, and a half hour up there is untested rather than
|
||||
# known-bad. A Number cannot change step part-way, and turning this into a
|
||||
# Select of the app's sixteen values would change the entity's domain on
|
||||
# every unit that already has it, so the step stays 0.5 throughout.
|
||||
NumberDesc(
|
||||
key="good_sleep",
|
||||
rep_fn=_option_token_num("Sleep"),
|
||||
rep_fn=_option_token_num("Sleep", divisor=2),
|
||||
exists_fn=_has_option_token("Sleep"),
|
||||
write_fn=_option_number_write("Sleep"),
|
||||
write_fn=_option_number_write("Sleep", factor=2),
|
||||
native_min=0,
|
||||
native_max=12,
|
||||
step=1,
|
||||
step=0.5,
|
||||
unit="h",
|
||||
icon="mdi:sleep",
|
||||
entity_category="config",
|
||||
|
||||
@@ -217,6 +217,32 @@ def _desc(resources, key):
|
||||
return None
|
||||
|
||||
|
||||
def test_good_sleep_is_hours_while_the_token_counts_half_hours():
|
||||
"""The appliance's own app pairs its duration picker with the values it sends
|
||||
one to one -- 0:30 -> 1, 1:00 -> 2, 3:00 -> 6, 12:00 -> 24 -- so the token is
|
||||
half hours and the entity, which is in hours, has to halve and double. The
|
||||
fixture's own Sleep_0 is the one value that reads the same either way, hence
|
||||
the injected token here."""
|
||||
resources = _load_device(FIXTURE)
|
||||
mode = resources["/mode/vs/0"]
|
||||
mode["x.com.samsung.da.options"] = [
|
||||
option for option in mode["x.com.samsung.da.options"] if option != "Sleep_0"
|
||||
] + ["Sleep_5"]
|
||||
bound, _ = _discover(resources)
|
||||
assert flatten(bound, resources)["good_sleep"] == 2.5
|
||||
|
||||
desc = _desc(resources, "good_sleep")
|
||||
assert (desc.native_max, desc.step) == (12, 0.5)
|
||||
assert desc.write_fn(2.5, {}) == (
|
||||
["mode", "vs", "0"],
|
||||
{"x.com.samsung.da.options": ["Sleep_5"]},
|
||||
)
|
||||
# 12 hours is the app's maximum and has to be reachable -- it was not while
|
||||
# the token was published as hours.
|
||||
assert desc.write_fn(12, {})[1]["x.com.samsung.da.options"] == ["Sleep_24"]
|
||||
assert desc.write_fn(0, {})[1]["x.com.samsung.da.options"] == ["Sleep_0"]
|
||||
|
||||
|
||||
def test_filter_alarm_time_reads_the_threshold_and_writes_one_token():
|
||||
"""The interval FilterTime_ is measured against, offered by the app as a
|
||||
180/300/500/700 hour radio. All four were walked on hardware while watching
|
||||
|
||||
Reference in New Issue
Block a user