This repository has been archived on 2023-06-10. You can view files and clone it, but cannot push or open issues or pull requests.
homeassistant-config/deps/lib/python3.10/site-packages/eq3bt/structures.py
darthsandmann c74c45998c updates
2022-11-29 19:10:30 +01:00

175 lines
4.5 KiB
Python

""" Contains construct adapters and structures. """
from datetime import datetime, time, timedelta
from construct import (
Adapter,
Bytes,
Const,
Enum,
FlagsEnum,
GreedyRange,
IfThenElse,
Int8ub,
Optional,
Struct,
)
PROP_ID_RETURN = 1
PROP_INFO_RETURN = 2
PROP_SCHEDULE_SET = 0x10
PROP_SCHEDULE_RETURN = 0x21
NAME_TO_DAY = {"sat": 0, "sun": 1, "mon": 2, "tue": 3, "wed": 4, "thu": 5, "fri": 6}
NAME_TO_CMD = {"write": PROP_SCHEDULE_SET, "response": PROP_SCHEDULE_RETURN}
HOUR_24_PLACEHOLDER = 1234
class TimeAdapter(Adapter):
"""Adapter to encode and decode schedule times."""
def _decode(self, obj, ctx, path):
h, m = divmod(obj * 10, 60)
if h == 24: # HACK, can we do better?
return HOUR_24_PLACEHOLDER
return time(hour=h, minute=m)
def _encode(self, obj, ctx, path):
# TODO: encode h == 24 hack
if obj == HOUR_24_PLACEHOLDER:
return int(24 * 60 / 10)
encoded = int((obj.hour * 60 + obj.minute) / 10)
return encoded
class TempAdapter(Adapter):
"""Adapter to encode and decode temperature."""
def _decode(self, obj, ctx, path):
return float(obj / 2.0)
def _encode(self, obj, ctx, path):
return int(obj * 2.0)
class WindowOpenTimeAdapter(Adapter):
"""Adapter to encode and decode window open times (5 min increments)."""
def _decode(self, obj, context, path):
return timedelta(minutes=float(obj * 5.0))
def _encode(self, obj, context, path):
if isinstance(obj, timedelta):
obj = obj.seconds
if 0 <= obj <= 3600.0:
return int(obj / 300.0)
raise ValueError(
"Window open time must be between 0 and 60 minutes "
"in intervals of 5 minutes."
)
class TempOffsetAdapter(Adapter):
"""Adapter to encode and decode the temperature offset."""
def _decode(self, obj, context, path):
return float((obj - 7) / 2.0)
def _encode(self, obj, context, path):
if -3.5 <= obj <= 3.5:
return int(obj * 2.0) + 7
raise ValueError(
"Temperature offset must be between -3.5 and 3.5 (in " "intervals of 0.5)."
)
ModeFlags = "ModeFlags" / FlagsEnum(
Int8ub,
AUTO=0x00, # always True, doesnt affect building
MANUAL=0x01,
AWAY=0x02,
BOOST=0x04,
DST=0x08,
WINDOW=0x10,
LOCKED=0x20,
UNKNOWN=0x40,
LOW_BATTERY=0x80,
)
class AwayDataAdapter(Adapter):
"""Adapter to encode and decode away data."""
def _decode(self, obj, ctx, path):
(day, year, hour_min, month) = obj
year += 2000
min = 0
if hour_min & 0x01:
min = 30
hour = int(hour_min / 2)
return datetime(year=year, month=month, day=day, hour=hour, minute=min)
def _encode(self, obj, ctx, path):
if obj.year < 2000 or obj.year > 2099:
raise Exception("Invalid year, possible [2000,2099]")
year = obj.year - 2000
hour = obj.hour * 2
if obj.minute: # we encode all minute values to h:30
hour |= 0x01
return (obj.day, year, hour, obj.month)
class DeviceSerialAdapter(Adapter):
"""Adapter to decode the device serial number."""
def _decode(self, obj, context, path):
return bytearray(n - 0x30 for n in obj).decode()
Status = "Status" / Struct(
"cmd" / Const(PROP_INFO_RETURN, Int8ub),
Const(0x01, Int8ub),
"mode" / ModeFlags,
"valve" / Int8ub,
Const(0x04, Int8ub),
"target_temp" / TempAdapter(Int8ub),
"away"
/ IfThenElse( # noqa: W503
lambda ctx: ctx.mode.AWAY, AwayDataAdapter(Bytes(4)), Optional(Bytes(4))
),
"presets"
/ Optional( # noqa: W503
Struct(
"window_open_temp" / TempAdapter(Int8ub),
"window_open_time" / WindowOpenTimeAdapter(Int8ub),
"comfort_temp" / TempAdapter(Int8ub),
"eco_temp" / TempAdapter(Int8ub),
"offset" / TempOffsetAdapter(Int8ub),
)
),
)
Schedule = "Schedule" / Struct(
"cmd" / Enum(Int8ub, **NAME_TO_CMD),
"day" / Enum(Int8ub, **NAME_TO_DAY),
"base_temp" / TempAdapter(Int8ub),
"next_change_at" / TimeAdapter(Int8ub),
"hours"
/ GreedyRange( # noqa: W503
Struct(
"target_temp" / TempAdapter(Int8ub),
"next_change_at" / TimeAdapter(Int8ub),
)
),
)
DeviceId = "DeviceId" / Struct(
"cmd" / Const(PROP_ID_RETURN, Int8ub),
"version" / Int8ub,
Int8ub,
Int8ub,
"serial" / DeviceSerialAdapter(Bytes(10)),
Int8ub,
)