from functools import lru_cache
class JciHitachiStatus: # pragma: no cover
idx = {}
def __init__(self, status, default) -> None:
self._status = status
self._default = default
@property
def status(self):
"""All status.
Returns
-------
dict
All status.
"""
return dict((key, getattr(self, key)) for key in self.idx)
[docs]
class JciHitachiAC(JciHitachiStatus): # pragma: no cover
"""Data class representing air conditioner status.
Parameters
----------
status : dict
Status retrieved from JciHitachiStatusInterpreter.decode_status().
default : int, optional
Default value when a status doesn't exist, by default -1.
"""
idx = {
"power": 0,
"mode": 1,
"air_speed": 2,
"target_temp": 3,
"indoor_temp": 4,
"sleep_timer": 6,
"vertical_wind_swingable": 14,
"vertical_wind_direction": 15,
"horizontal_wind_direction": 17,
"mold_prev": 23,
"fast_op": 26,
"energy_save": 27,
"sound_prompt": 30,
"outdoor_temp": 33,
"power_kwh": 40,
"freeze_clean": 57,
}
def __init__(self, status, default=-1):
super().__init__(status, default)
@property
def power(self):
"""Power. Controllable.
Returns
-------
str
One of ("unsupported", "off", "on", "unknown").
"""
v = self._status.get(self.idx["power"], self._default)
if v == -1:
return "unsupported"
elif v == 0:
return "off"
elif v == 1:
return "on"
else:
return "unknown"
@property
def mode(self):
"""Mode. Controllable.
Returns
-------
str
One of ("unsupported", "cool", "dry", "fan", "auto", "heat", "unknown").
"""
v = self._status.get(self.idx["mode"], self._default)
if v == -1:
return "unsupported"
elif v == 0:
return "cool"
elif v == 1:
return "dry"
elif v == 2:
return "fan"
elif v == 3:
return "auto"
elif v == 4:
return "heat"
else:
return "unknown"
@property
def air_speed(self):
"""Air speed. Controllable.
Returns
-------
str
One of ("unsupported", "auto", "silent", "low", "moderate", "high", "unknown").
"""
v = self._status.get(self.idx["air_speed"], self._default)
if v == -1:
return "unsupported"
elif v == 0:
return "auto"
elif v == 1:
return "silent"
elif v == 2:
return "low"
elif v == 3:
return "moderate"
elif v == 4:
return "high"
else:
return "unknown"
@property
def target_temp(self):
"""Target temperature. Controllable.
Returns
-------
int
Celsius temperature.
"""
v = self._status.get(self.idx["target_temp"], self._default)
return v
@property
def indoor_temp(self):
"""Indoor temperature.
Returns
-------
int
Celsius temperature.
"""
v = self._status.get(self.idx["indoor_temp"], self._default)
return v
@property
def max_temp(self):
"""Maximum target temperature.
Returns
-------
int
Celsius temperature.
"""
return 32
@property
def min_temp(self):
"""Minimum target temperature.
Returns
-------
int
Celsius temperature.
"""
return 16
@property
def sleep_timer(self):
"""Sleep timer. Controllable.
Returns
-------
int
Sleep timer (hours).
"""
v = self._status.get(self.idx["sleep_timer"], self._default)
return v
@property
def vertical_wind_swingable(self):
"""Vertical wind swingable. Controllable.
Returns
-------
str
One of ("unsupported", "disabled", "enabled", "unknown").
"""
v = self._status.get(self.idx["vertical_wind_swingable"], self._default)
if v == -1:
return "unsupported"
elif v == 0:
return "disabled"
elif v == 1:
return "enabled"
else:
return "unknown"
@property
def vertical_wind_direction(self):
"""Vertical wind direction. Controllable.
Returns
-------
int
Value between 0 to 15.
"""
v = self._status.get(self.idx["vertical_wind_direction"], self._default)
return v
@property
def horizontal_wind_direction(self):
"""Horizontal wind direction. Controllable.
Returns
-------
str
One of ("unsupported", "auto", "leftmost", "middleleft", "central", "middleright", "rightmost", "unknown").
"""
v = self._status.get(self.idx["horizontal_wind_direction"], self._default)
if v > 0:
v = 6 - v
if v == -1:
return "unsupported"
elif v == 0:
return "auto"
elif v == 1:
return "leftmost"
elif v == 2:
return "middleleft"
elif v == 3:
return "central"
elif v == 4:
return "middleright"
elif v == 5:
return "rightmost"
else:
return "unknown"
@property
def mold_prev(self):
"""Mold prevention. Controllable.
Returns
-------
str
One of ("unsupported", "disabled", "enabled", "unknown").
"""
v = self._status.get(self.idx["mold_prev"], self._default)
if v == -1:
return "unsupported"
elif v == 0:
return "disabled"
elif v == 1:
return "enabled"
else:
return "unknown"
@property
def fast_op(self):
"""Fast operation. Controllable.
Returns
-------
str
One of ("unsupported", "disabled", "enabled", "unknown").
"""
v = self._status.get(self.idx["fast_op"], self._default)
if v == -1:
return "unsupported"
elif v == 0:
return "disabled"
elif v == 1:
return "enabled"
else:
return "unknown"
@property
def energy_save(self):
"""Energy saving. Controllable.
Returns
-------
str
One of ("unsupported", "disabled", "enabled", "unknown").
"""
v = self._status.get(self.idx["energy_save"], self._default)
if v == -1:
return "unsupported"
elif v == 0:
return "disabled"
elif v == 1:
return "enabled"
else:
return "unknown"
@property
def sound_prompt(self):
"""Sound prompt. Controllable.
Returns
-------
str
One of ("unsupported", "enabled", "disabled", "unknown").
"""
v = self._status.get(self.idx["sound_prompt"], self._default)
if v == -1:
return "unsupported"
elif v == 0:
return "enabled"
elif v == 1:
return "disabled"
else:
return "unknown"
@property
def outdoor_temp(self):
"""Outdoor temperature.
Returns
-------
int
Celsius temperature.
"""
v = self._status.get(self.idx["outdoor_temp"], self._default)
return v
@property
def power_kwh(self):
"""Accumulated Kwh in a day.
Returns
-------
float
Kwh.
"""
v = self._status.get(self.idx["power_kwh"], self._default)
if v == -1:
return v
return v / 10.0
@property
def freeze_clean(self):
"""Freeze clean. Controllable.
Returns
-------
str
One of ("unsupported", "off", "on", "unknown").
"""
v = self._status.get(self.idx["freeze_clean"], self._default)
if v == -1:
return "unsupported"
elif v == 0:
return "off"
elif v == 1:
return "on"
else:
return "unknown"
[docs]
class JciHitachiDH(JciHitachiStatus): # pragma: no cover
"""Data class representing dehumidifier status.
Parameters
----------
status : dict
Status retrieved from JciHitachiStatusInterpreter.decode_status().
default : int, optional
Default value when a status doesn't exist, by default -1.
"""
idx = {
"power": 0,
"mode": 1,
"target_humidity": 3,
"indoor_humidity": 7,
"wind_swingable": 8,
"water_full_warning": 10,
"clean_filter_notify": 11,
"air_purify_level": 13,
"air_speed": 14,
"side_vent": 15,
"sound_control": 16,
"error_code": 18,
"mold_prev": 19,
"power_kwh": 29,
"air_quality_value": 35,
"air_quality_level": 36,
"pm25_value": 37,
"display_brightness": 39,
"odor_level": 40,
"air_cleaning_filter": 41,
}
def __init__(self, status, default=-1):
super().__init__(status, default)
@property
def power(self):
"""Power. Controllable.
Returns
-------
str
One of ("unsupported", "off", "on", "unknown").
"""
v = self._status.get(self.idx["power"], self._default)
if v == -1:
return "unsupported"
elif v == 0:
return "off"
elif v == 1:
return "on"
else:
return "unknown"
@property
def mode(self):
"""Mode. Controllable.
Returns
-------
str
One of (
"unsupported", "auto", "custom", "continuous", "clothes_dry",
"air_purify", "mold_prev", "low_humidity", "eco_comfort", "unknown"
).
"""
v = self._status.get(self.idx["mode"], self._default)
if v == -1:
return "unsupported"
elif v == 0:
return "auto"
elif v == 1:
return "custom"
elif v == 2:
return "continuous"
elif v == 3:
return "clothes_dry"
elif v == 4:
return "air_purify"
elif v == 5:
return "mold_prev"
elif v == 8:
return "low_humidity"
elif v == 9:
return "eco_comfort"
else:
return "unknown"
@property
def target_humidity(self):
"""Target humidity. Controllable.
Returns
-------
int
Relative humidity.
"""
v = self._status.get(self.idx["target_humidity"], self._default)
return v
@property
def indoor_humidity(self):
"""Indoor humidity.
Returns
-------
int
Relative humidity.
"""
v = self._status.get(self.idx["indoor_humidity"], self._default)
return v
@property
def max_humidity(self):
"""Maximum target humidity.
Returns
-------
int
Relative humidity.
"""
return 70
@property
def min_humidity(self):
"""Minimum target humidity.
Returns
-------
int
Relative humidity.
"""
return 40
@property
def wind_swingable(self):
"""Wind swingable. Controllable.
Returns
-------
str
One of ("unsupported", "off", "on", "unknown").
"""
v = self._status.get(self.idx["wind_swingable"], self._default)
if v == -1:
return "unsupported"
elif v == 0:
return "off"
elif v == 1:
return "on"
else:
return "unknown"
@property
def water_full_warning(self):
"""Water full warning.
Returns
-------
str
One of ("unsupported", "off", "on", "unknown").
"""
v = self._status.get(self.idx["water_full_warning"], self._default)
if v == -1:
return "unsupported"
elif v == 0:
return "off" # not activated
elif v == 1:
return "on" # activated
else:
return "unknown"
@property
def clean_filter_notify(self):
"""Clean filter notify control. Controllable.
Returns
-------
str
One of ("unsupported", "disabled", "enabled", "unknown").
"""
v = self._status.get(self.idx["clean_filter_notify"], self._default)
if v == -1:
return "unsupported"
elif v == 0:
return "disabled"
elif v == 1:
return "enabled"
else:
return "unknown"
@property
def air_purify_level(self):
"""Air purify level. Not implemented.
Returns
-------
str
Not implemented.
"""
return "unsupported"
@property
def air_speed(self):
"""Air speed. Controllable.
Returns
-------
str
One of ("unsupported", "auto", "silent", "low", "moderate", "high", "unknown").
"""
v = self._status.get(self.idx["air_speed"], self._default)
if v == -1:
return "unsupported"
elif v == 0:
return "auto"
elif v == 1:
return "silent"
elif v == 2:
return "low"
elif v == 3:
return "moderate"
elif v == 4:
return "high"
else:
return "unknown"
@property
def side_vent(self):
"""Side vent.
Returns
-------
str
One of ("unsupported", "off", "on", "unknown").
"""
v = self._status.get(self.idx["side_vent"], self._default)
if v == -1:
return "unsupported"
elif v == 0:
return "off"
elif v == 1:
return "on"
else:
return "unknown"
@property
def sound_control(self):
"""Sound control. Controllable.
Returns
-------
str
One of ("unsupported", "silent", "button", "button+waterfull", "unknown").
"""
v = self._status.get(self.idx["sound_control"], self._default)
if v == -1:
return "unsupported"
elif v == 0:
return "silent"
elif v == 1:
return "button"
elif v == 2:
return "button+waterfull"
else:
return "unknown"
@property
def error_code(self):
"""Error code.
Returns
-------
int
Error code.
"""
v = self._status.get(self.idx["error_code"], self._default)
return v
@property
def mold_prev(self):
"""Mold prevention. Controllable.
Returns
-------
str
One of ("unsupported", "off", "on", "unknown").
"""
v = self._status.get(self.idx["mold_prev"], self._default)
if v == -1:
return "unsupported"
elif v == 0:
return "off"
elif v == 1:
return "on"
else:
return "unknown"
@property
def power_kwh(self):
"""Accumulated Kwh in a day.
Returns
-------
float
Kwh.
"""
v = self._status.get(self.idx["power_kwh"], self._default)
if v == -1:
return v
return v / 10.0
@property
def air_quality_value(self):
"""Air quality value. Not implemented.
Returns
-------
int
Not implemented.
"""
return self._default
@property
def air_quality_level(self):
"""Air quality level. Not implemented.
Returns
-------
str
Not implemented.
"""
return "unsupported"
@property
def pm25_value(self):
"""PM2.5 value.
Returns
-------
int
PM2.5 value.
"""
v = self._status.get(self.idx["pm25_value"], self._default)
return v
@property
def display_brightness(self):
"""Display brightness. Controllable.
Returns
-------
str
One of ("unsupported", "bright", "dark", "off", "all_off" "unknown").
"""
v = self._status.get(self.idx["display_brightness"], self._default)
if v == -1:
return "unsupported"
elif v == 0:
return "bright"
elif v == 1:
return "dark"
elif v == 2:
return "off"
elif v == 3:
return "all_off"
else:
return "unknown"
@property
def odor_level(self):
"""Odor level.
Returns
-------
str
One of ("unsupported", "low", "middle", "high", "unknown").
"""
v = self._status.get(self.idx["odor_level"], self._default)
if v == -1:
return "unsupported"
elif v == 0:
return "low"
elif v == 1:
return "middle"
elif v == 2:
return "high"
else:
return "unknown"
@property
def air_cleaning_filter(self):
"""Air cleaning filter setting.
Returns
-------
str
One of ("unsupported", "disabled", "enabled", "unknown").
"""
v = self._status.get(self.idx["air_cleaning_filter"], self._default)
if v == -1:
return "unsupported"
elif v == 0:
return "disabled"
elif v == 1:
return "enabled"
else:
return "unknown"
[docs]
class JciHitachiHE(JciHitachiStatus): # pragma: no cover
"""Data class representing heat exchanger status. Not implemented.
Parameters
----------
status : dict
Status retrieved from JciHitachiStatusInterpreter.decode_status().
default : int, optional
Default value when a status doesn't exist, by default -1.
"""
idx = {}
def __init__(self, status, default=-1):
super().__init__(status, default)
class JciHitachiStatusSupport(JciHitachiStatus): # pragma: no cover
supported_type = {}
def __init__(self, status, default=0):
super().__init__(status, default)
@property
def brand(self):
"""Device brand.
Returns
-------
str
Device brand.
"""
v = self._status.get(self.idx["brand"], self._default)
return v
@property
def model(self):
"""Device model.
Returns
-------
str
Device model.
"""
v = self._status.get(self.idx["model"], self._default)
return v
def _uni_v(self, v):
return (((v >> 0x10) & 0xFF) << 8) | (v >> 0x18)
def _dual_v(self, v):
low = (v >> 0x10) & 0xFF
high = (v >> 0x18) & 0xFF
return low, high
def _functional_v(self, v):
uni_v = self._uni_v(v)
return ((uni_v >> i) & 0x1 for i in range(16))
def limit(self, status_name, status_value):
"""Limit status_value within an acceptable range.
Parameters
----------
status_name : str
Status name, which has to be in idx dict. E.g. JciHitachiAC.idx
status_value : int
Status value.
Returns
-------
int or None
If the status_value can be limited with an acceptable raneg, return int.
Otherwise, if the status_value is invalid, return None.
"""
is_support, *v = getattr(self, status_name)
if not is_support:
return None
supported_type = self.supported_type[status_name]
if supported_type == "uni":
return min(status_value, v[0])
elif supported_type == "dual":
return min(v[1], max(status_value, v[0]))
elif supported_type == "functional":
if v[status_value]:
return status_value
return None
[docs]
class JciHitachiACSupport(JciHitachiStatusSupport): # pragma: no cover
"""Data model representing supported air conditioner status.
Parameters
----------
status : dict
Supported status retrieved from JciHitachiStatusInterpreter.decode_support().
default : int, optional
Default value when a status doesn't exist, by default 0.
"""
idx = {
"brand": "brand",
"model": "model",
"power": 0,
"mode": 1,
"air_speed": 2,
"target_temp": 3,
"indoor_temp": 4,
"sleep_timer": 6,
"vertical_wind_swingable": 14,
"vertical_wind_direction": 15,
"horizontal_wind_direction": 17,
"mold_prev": 23,
"fast_op": 26,
"energy_save": 27,
"sound_prompt": 30,
"outdoor_temp": 33,
"power_kwh": 40,
}
supported_type = {
"brand": "str",
"model": "str",
"power": "functional",
"mode": "functional",
"air_speed": "functional",
"target_temp": "dual",
"indoor_temp": "dual",
"sleep_timer": "uni",
"vertical_wind_swingable": "functional",
"vertical_wind_direction": "functional",
"horizontal_wind_direction": "functional",
"mold_prev": "functional",
"fast_op": "functional",
"energy_save": "functional",
"sound_prompt": "functional",
"outdoor_temp": "dual",
"power_kwh": "uni",
}
def __init__(self, status, default=0):
super().__init__(status, default)
@property
def power(self):
"""Power. Controllable.
Returns
-------
(bool, int, int)
(is_support, off, on).
"""
v = self._status.get(self.idx["power"], self._default)
supports = self._functional_v(v)
return (v != 0, *supports)
@property
def mode(self):
"""Mode. Controllable.
Returns
-------
(bool, Tuple[int])
is_support, (cool, dry, fan, auto, heat, 0...).
"""
v = self._status.get(self.idx["mode"], self._default)
supports = self._functional_v(v)
return (v != 0, *supports)
@property
def air_speed(self):
"""Air speed. Controllable.
Returns
-------
(bool, Tuple[int])
is_support, ("auto", "silent", "low", "moderate", "high", 0...).
"""
v = self._status.get(self.idx["air_speed"], self._default)
supports = self._functional_v(v)
return (v != 0, *supports)
@property
def target_temp(self):
"""Target temperature. Controllable.
Returns
-------
(bool, int, int)
(is_support, minimum, maximum)
"""
v = self._status.get(self.idx["target_temp"], self._default)
supports = self._dual_v(v)
return (v != 0, *supports)
@property
def indoor_temp(self):
"""Indoor temperature.
Returns
-------
(bool, int, int)
(is_support, minimum, maximum)
"""
v = self._status.get(self.idx["indoor_temp"], self._default)
supports = self._dual_v(v)
return (v != 0, *supports)
@property
def sleep_timer(self):
"""Sleep timer. Controllable.
Returns
-------
(bool, int)
(is_support, maximum).
"""
v = self._status.get(self.idx["sleep_timer"], self._default)
support = self._uni_v(v)
return (v != 0, support)
@property
def vertical_wind_swingable(self):
"""Vertical wind swingable. Controllable.
Returns
-------
(bool, Tuple[int])
is_support, ("enabled", "disabled", 0...).
"""
v = self._status.get(self.idx["vertical_wind_swingable"], self._default)
supports = self._functional_v(v)
return (v != 0, *supports)
@property
def vertical_wind_direction(self):
"""Vertical wind direction. Controllable.
Returns
-------
(bool, Tuple[int])
is_support, ("auto", "level1", "level2", "level3", "level4", "level5", "level6", "level7", 0...).
"""
v = self._status.get(self.idx["vertical_wind_direction"], self._default)
supports = self._functional_v(v)
return (v != 0, *supports)
@property
def horizontal_wind_direction(self):
"""Horizontal wind direction. Controllable.
Returns
-------
(bool, Tuple[int])
is_support, ("auto", "leftmost", "middleleft", "central", "middleright", "rightmost", 0...).
"""
v = self._status.get(self.idx["horizontal_wind_direction"], self._default)
if v > 0:
v = 6 - v
supports = self._functional_v(v)
return (v != 0, *supports)
@property
def mold_prev(self):
"""Mold prevention. Controllable.
Returns
-------
(bool, Tuple[int])
is_support, ("enabled", "disabled", 0...).
"""
v = self._status.get(self.idx["mold_prev"], self._default)
supports = self._functional_v(v)
return (v != 0, *supports)
@property
def fast_op(self):
"""Fast operation. Controllable.
Returns
-------
(bool, Tuple[int])
is_support, ("disabled", "enabled", 0...).
"""
v = self._status.get(self.idx["fast_op"], self._default)
supports = self._functional_v(v)
return (v != 0, *supports)
@property
def energy_save(self):
"""Energy saving. Controllable.
Returns
-------
(bool, Tuple[int])
is_support, ("enabled", "disabled", 0...).
"""
v = self._status.get(self.idx["energy_save"], self._default)
supports = self._functional_v(v)
return (v != 0, *supports)
@property
def sound_prompt(self):
"""Sound prompt. Controllable.
Returns
-------
(bool, Tuple[int])
is_support, ("enabled", "disabled", 0...).
"""
v = self._status.get(self.idx["sound_prompt"], self._default)
supports = self._functional_v(v)
return (v != 0, *supports)
@property
def outdoor_temp(self):
"""Outdoor temperature.
Returns
-------
(bool, int, int)
(is_support, minimum, maximum)
"""
v = self._status.get(self.idx["outdoor_temp"], self._default)
supports = self._dual_v(v)
return (v != 0, *supports)
@property
def power_kwh(self):
"""Accumulated Kwh in a day.
Returns
-------
(bool, int)
(is_support, maximum).
"""
v = self._status.get(self.idx["power_kwh"], self._default)
supports = self._uni_v(v)
return (v != 0, supports)
[docs]
class JciHitachiDHSupport(JciHitachiStatusSupport): # pragma: no cover
"""Data model representing supported dehumidifier status.
Parameters
----------
status : dict
Supported status retrieved from JciHitachiStatusInterpreter.decode_support().
default : int, optional
Default value when a status doesn't exist, by default 0.
"""
idx = {
"brand": "brand",
"model": "model",
"power": 0,
"mode": 1,
"target_humidity": 3,
"indoor_humidity": 7,
"wind_swingable": 8,
"water_full_warning": 10,
"clean_filter_notify": 11,
"air_purify_level": 13,
"air_speed": 14,
"side_vent": 15,
"sound_control": 16,
"error_code": 18,
"mold_prev": 19,
"power_kwh": 29,
"air_quality_value": 35,
"air_quality_level": 36,
"pm25_value": 37,
"display_brightness": 39,
"odor_level": 40,
"air_cleaning_filter": 41,
}
supported_type = {
"brand": "str",
"model": "str",
"power": "functional",
"mode": "functional",
"target_humidity": "dual",
"indoor_humidity": "dual",
"wind_swingable": "functional",
"water_full_warning": "functional",
"clean_filter_notify": "functional",
"air_purify_level": "functional", # not implemented
"air_speed": "functional",
"side_vent": "functional",
"sound_control": "functional",
"error_code": "uni",
"mold_prev": "functional",
"power_kwh": "uni",
"air_quality_value": "uni",
"air_quality_level": "functional",
"pm25_value": "uni",
"display_brightness": "functional",
"odor_level": "functional",
"air_cleaning_filter": "functional",
}
def __init__(self, status, default=0):
super().__init__(status, default)
@property
def power(self):
"""Power. Controllable.
Returns
-------
(bool, Tuple[int])
(is_support, off, on).
"""
v = self._status.get(self.idx["power"], self._default)
supports = self._functional_v(v)
return (v != 0, *supports)
@property
def mode(self):
"""Mode. Controllable.
Returns
-------
(bool, Tuple[int])
is_support, (auto, custom, continuous, clothes_dry, air_purify, mold_prev, air_supply, human_comfort, low_humidity, eco_comfort, 0...).
"""
v = self._status.get(self.idx["mode"], self._default)
supports = self._functional_v(v)
return (v != 0, *supports)
@property
def target_humidity(self):
"""Target humidity. Controllable.
Returns
-------
(bool, int, int)
(is_support, minimum, maximum)
"""
v = self._status.get(self.idx["target_humidity"], self._default)
supports = self._dual_v(v)
return (v != 0, *supports)
@property
def indoor_humidity(self):
"""Indoor humidity.
Returns
-------
(bool, int, int)
(is_support, minimum, maximum)
"""
v = self._status.get(self.idx["indoor_humidity"], self._default)
supports = self._dual_v(v)
return (v != 0, *supports)
@property
def wind_swingable(self):
"""Wind swingable. Controllable.
Returns
-------
(bool, Tuple[int])
is_support, ("off", "on", 0...).
"""
v = self._status.get(self.idx["wind_swingable"], self._default)
supports = self._functional_v(v)
return (v != 0, *supports)
@property
def water_full_warning(self):
"""Water full warning.
Returns
-------
(bool, Tuple[int])
is_support, ("off", "on", 0...).
"""
v = self._status.get(self.idx["water_full_warning"], self._default)
supports = self._functional_v(v)
return (v != 0, *supports)
@property
def clean_filter_notify(self):
"""Clean filter notify control. Controllable.
Returns
-------
str
One of ("unsupported", "disabled", "enabled", "unknown").
"""
v = self._status.get(self.idx["clean_filter_notify"], self._default)
supports = self._functional_v(v)
return (v != 0, *supports)
@property
def air_purify_level(self):
"""Air purify level. Not implemented.
Returns
-------
str
Not implemented.
"""
return "unsupported"
@property
def air_speed(self):
"""Air speed. Controllable.
Returns
-------
(bool, Tuple[int])
is_support, ("auto", "silent", "low", "moderate", "high", 0...).
"""
v = self._status.get(self.idx["air_speed"], self._default)
supports = self._functional_v(v)
return (v != 0, *supports)
@property
def side_vent(self):
"""Side vent.
Returns
-------
(bool, Tuple[int])
is_support, ("off", "on", 0...).
"""
v = self._status.get(self.idx["side_vent"], self._default)
supports = self._functional_v(v)
return (v != 0, *supports)
@property
def sound_control(self):
"""Sound control. Controllable.
Returns
-------
(bool, Tuple[int])
is_support, ("silent", "button", "button+waterfull", 0...).
"""
v = self._status.get(self.idx["sound_control"], self._default)
supports = self._functional_v(v)
return (v != 0, *supports)
@property
def error_code(self):
"""Error code.
Returns
-------
(bool,)
(is_support,).
"""
v = self._status.get(self.idx["error_code"], self._default)
return (v != 0,)
@property
def mold_prev(self):
"""Mold prevention. Controllable.
Returns
-------
(bool, Tuple[int])
is_support, ("silent", "off", "on",, 0...).
"""
v = self._status.get(self.idx["mold_prev"], self._default)
supports = self._functional_v(v)
return (v != 0, *supports)
@property
def power_kwh(self):
"""Accumulated Kwh in a day.
Returns
-------
(bool, int)
(is_support, maximum).
"""
v = self._status.get(self.idx["power_kwh"], self._default)
supports = self._uni_v(v)
return (v != 0, supports)
@property
def air_quality_value(self):
"""Air quality value. Not implemented.
Returns
-------
int
Not implemented.
"""
return self._default
@property
def air_quality_level(self):
"""Air quality level. Not implemented.
Returns
-------
str
Not implemented.
"""
return "unsupported"
@property
def pm25_value(self):
"""PM2.5 value.
Returns
-------
(bool, int)
(is_support, maximum).
"""
v = self._status.get(self.idx["pm25_value"], self._default)
supports = self._uni_v(v)
return (v != 0, supports)
@property
def display_brightness(self):
"""Display brightness. Controllable.
Returns
-------
(bool, Tuple[int])
is_support, ("bright", "dark", "off", 0...).
"""
v = self._status.get(self.idx["display_brightness"], self._default)
supports = self._functional_v(v)
return (v != 0, *supports)
@property
def odor_level(self):
"""Odor level.
Returns
-------
(bool, Tuple[int])
is_support, ("low", "middle", "high", 0...).
"""
v = self._status.get(self.idx["odor_level"], self._default)
supports = self._functional_v(v)
return (v != 0, *supports)
@property
def air_cleaning_filter(self):
"""Air cleaning filter setting.
Returns
-------
(bool, Tuple[int])
is_support, ("enabled", "disabled", 0...). status's reversed order.
"""
v = self._status.get(self.idx["air_cleaning_filter"], self._default)
supports = self._functional_v(v)
return (v != 0, *supports)
[docs]
class JciHitachiHESupport(JciHitachiStatusSupport): # pragma: no cover
"""Data model representing supported heat exchanger status. Not implemented.
Parameters
----------
status : dict
Supported status retrieved from JciHitachiStatusInterpreter.decode_support().
default : int, optional
Default value when a status doesn't exist, by default 0.
"""
idx = {}
def __init__(self, status, default=0):
super().__init__(status, default)
STATUS_DICT = {
"AC": {
"DeviceType": {
"controllable": False,
"is_numeric": False,
"legacy_name": "DeviceType",
"id2str": {
1: "AC",
2: "DH",
3: "HE",
4: "PM25_PANEL",
},
},
"Switch": {
"controllable": True,
"is_numeric": False,
"legacy_name": "power",
"id2str": {
0: "off",
1: "on",
},
},
"Mode": {
"controllable": True,
"is_numeric": False,
"legacy_name": "mode",
"id2str": {
0: "cool",
1: "dry",
2: "fan",
3: "auto",
4: "heat",
},
},
"FanSpeed": {
"controllable": True,
"is_numeric": False,
"legacy_name": "air_speed",
"id2str": {
0: "auto",
1: "silent",
2: "low",
3: "moderate",
4: "high",
5: "rapid",
6: "express",
},
},
"TemperatureSetting": {
"controllable": True,
"is_numeric": True,
"legacy_name": "target_temp",
},
"IndoorTemperature": {
"controllable": False,
"is_numeric": True,
"legacy_name": "indoor_temp",
},
"SleepModeRemainingTime": {
"controllable": True,
"is_numeric": True,
"legacy_name": "sleep_timer",
},
"VerticalWindDirectionSwitch": {
"controllable": True,
"is_numeric": False,
"legacy_name": "vertical_wind_swingable",
"id2str": {
0: "disabled",
1: "enabled",
},
},
"VerticalWindDirectionSetting": {
"controllable": True,
"is_numeric": True,
"legacy_name": "vertical_wind_direction",
},
"HorizontalWindDirectionSetting": {
"controllable": True,
"is_numeric": False,
"legacy_name": "horizontal_wind_direction",
"id2str": {
0: "auto",
1: "leftmost",
2: "middleleft",
3: "central",
4: "middleright",
5: "rightmost",
},
},
"MildewProof": {
"controllable": True,
"is_numeric": False,
"legacy_name": "mold_prev",
"id2str": {
0: "disabled",
1: "enabled",
},
},
"QuickMode": {
"controllable": True,
"is_numeric": False,
"legacy_name": "fast_op",
"id2str": {
0: "disabled",
1: "enabled",
},
},
"PowerSaving": {
"controllable": True,
"is_numeric": False,
"legacy_name": "energy_save",
"id2str": {
0: "disabled",
1: "enabled",
},
},
"ControlTone": {
"controllable": True,
"is_numeric": False,
"legacy_name": "sound_prompt",
"id2str": {
0: "enabled",
1: "disabled",
},
},
"PowerConsumption": {
"controllable": False,
"is_numeric": True,
"legacy_name": "power_kwh",
},
"TaiseiaError": {
"controllable": False,
"is_numeric": True,
"legacy_name": None,
},
"FilterElapsedHour": {
"controllable": False,
"is_numeric": True,
"legacy_name": None,
},
"CleanSwitch": {
"controllable": True,
"is_numeric": False,
"legacy_name": "freeze_clean",
"id2str": {
0: "off",
1: "on",
},
},
"CleanNotification": {
"controllable": False,
"is_numeric": True,
"legacy_name": None,
},
"CleanStatus": {
"controllable": False,
"is_numeric": True,
"legacy_name": None,
},
"Error": {
"controllable": False,
"is_numeric": True,
"legacy_name": None,
},
"max_temp": {
"controllable": False,
"is_numeric": True,
"legacy_name": "max_temp",
},
"min_temp": {
"controllable": False,
"is_numeric": True,
"legacy_name": "min_temp",
},
"Panel": {
"controllable": True,
"is_numeric": False,
"legacy_name": None,
"id2str": {
0: "bright",
1: "dark",
2: "off",
3: "all_off",
},
},
},
"DH": {
"DeviceType": {
"controllable": False,
"is_numeric": False,
"legacy_name": "DeviceType",
"id2str": {
1: "AC",
2: "DH",
3: "HE",
4: "PM25_PANEL",
},
},
"Switch": {
"controllable": True,
"is_numeric": False,
"legacy_name": "power",
"id2str": {
0: "off",
1: "on",
},
},
"Mode": {
"controllable": True,
"is_numeric": False,
"legacy_name": "mode",
"id2str": {
0: "auto",
1: "custom",
2: "continuous",
3: "clothes_dry",
4: "air_purify",
5: "mold_prev",
8: "low_humidity",
9: "eco_comfort",
},
},
"FanSpeed": {
"controllable": True,
"is_numeric": False,
"legacy_name": "air_speed",
"id2str": {
0: "auto",
1: "silent",
2: "low",
3: "moderate",
4: "high",
},
},
"MildewProof": {
"controllable": True,
"is_numeric": False,
"legacy_name": "mold_prev",
"id2str": {
0: "disabled",
1: "enabled",
},
},
"ControlTone": {
"controllable": True,
"is_numeric": False,
"legacy_name": "sound_control",
"id2str": {
0: "silent",
1: "button",
2: "button+waterfull",
},
},
"SaaControlTone": { # currently not supported
"controllable": False,
"is_numeric": False,
"legacy_name": None,
"id2str": {},
},
"PowerConsumption": {
"controllable": False,
"is_numeric": True,
"legacy_name": "power_kwh",
},
"Ion": {
"controllable": True,
"is_numeric": False,
"legacy_name": None,
"id2str": {
0: "disabled",
1: "enabled",
},
},
"HumiditySetting": {
"controllable": True,
"is_numeric": True,
"legacy_name": "target_humidity",
},
"AutoWindDirection": {
"controllable": True,
"is_numeric": False,
"legacy_name": "wind_swingable",
"id2str": {
0: "disabled",
1: "enabled",
},
},
"KeypadLock": {
"controllable": True,
"is_numeric": False,
"legacy_name": None,
"id2str": {
0: "disabled",
1: "enabled",
},
},
"DisplayBrightness": {
"controllable": True,
"is_numeric": False,
"legacy_name": "display_brightness",
"id2str": {
0: "bright",
1: "dark",
2: "off",
3: "all_off",
},
},
"FilterControl": {
"controllable": True,
"is_numeric": False,
"legacy_name": "air_cleaning_filter",
"id2str": {
0: "disabled",
1: "enabled",
},
},
"PM25": {
"controllable": False,
"is_numeric": True,
"legacy_name": "pm25_value",
},
"IndoorHumidity": {
"controllable": False,
"is_numeric": True,
"legacy_name": "indoor_humidity",
},
"SideAirOutlet": {
"controllable": False,
"is_numeric": False,
"legacy_name": "side_vent",
"id2str": {
0: "off",
1: "on",
},
},
"Defrost": {
"controllable": True,
"is_numeric": False,
"legacy_name": None,
"id2str": {},
},
"SmellIndex": {
"controllable": False,
"is_numeric": False,
"legacy_name": "odor_level",
"id2str": {
0: "low",
1: "middle",
2: "high",
},
},
"CleanFilterNotification": {
"controllable": True,
"is_numeric": False,
"legacy_name": "clean_filter_notify",
"id2str": {
0: "disabled",
1: "enabled",
},
},
"TankFullNotification": {
"controllable": False,
"is_numeric": False,
"legacy_name": "water_full_warning",
"id2str": {
0: "off", # not activated
1: "on", # activated
},
},
"TaiseiaError": {
"controllable": False,
"is_numeric": True,
"legacy_name": None,
},
"Error": {
"controllable": False,
"is_numeric": True,
"legacy_name": "error_code",
},
"max_humidity": {
"controllable": False,
"is_numeric": True,
"legacy_name": "max_humidity",
},
"min_humidity": {
"controllable": False,
"is_numeric": True,
"legacy_name": "min_humidity",
},
},
"HE": {
"DeviceType": {
"controllable": False,
"is_numeric": False,
"legacy_name": "DeviceType",
"id2str": {
1: "AC",
2: "DH",
3: "HE",
4: "PM25_PANEL",
},
},
"Switch": {
"controllable": True,
"is_numeric": False,
"legacy_name": None,
"id2str": {
0: "off",
1: "on",
},
},
"Mode": {
"controllable": True,
"is_numeric": False,
"legacy_name": None,
"id2str": {
0: "air_condition",
1: "dehumidification",
2: "air_supply",
3: "auto",
4: "heater",
},
},
"FanSpeed": {
"controllable": True,
"is_numeric": False,
"legacy_name": None,
"id2str": {
0: "auto",
1: "silent",
2: "low",
3: "moderate",
4: "high",
},
},
"IndoorTemperature": {
"controllable": False,
"is_numeric": True,
"legacy_name": None,
},
"TaiseiaError": {
"controllable": False,
"is_numeric": True,
"legacy_name": None,
},
"CleanFilterNotification": {
"controllable": False,
"is_numeric": False,
"legacy_name": None,
"id2str": {
0: "disabled",
1: "enabled",
},
},
"BreathMode": {
"controllable": True,
"is_numeric": False,
"legacy_name": None,
"id2str": {0: "auto", 1: "energy_recovery", 2: "normal"},
},
"FrontFilterNotification": {
"controllable": False,
"is_numeric": False,
"legacy_name": None,
"id2str": {
0: "disabled",
1: "enabled",
},
},
"Pm25FilterNotification": {
"controllable": False,
"is_numeric": False,
"legacy_name": None,
"id2str": {
0: "disabled",
1: "enabled",
},
},
"Error": {
"controllable": False,
"is_numeric": True,
"legacy_name": None,
},
},
"PM25_PANEL": {},
}
[docs]
class JciHitachiAWSStatus:
"""Data class representing `AWSThing` status.
Parameters
----------
raw_status : dict
Status retrieved from `JciHitachiAWSMqttConnection` _on_publish() callback.
legacy : bool, optional
Whether the raw_status is a legacy status, i.e. derived from subclasses of `JciHitachiStatus`, by default False.
"""
device_type_mapping = {
1: "AC",
2: "DH",
3: "HE",
4: "PM25_PANEL",
}
def __init__(self, raw_status: dict, legacy=False) -> None:
self._raw_status: dict = raw_status
self._status: dict = raw_status if legacy else self._preprocess(raw_status)
self._device_type: str = self._status["DeviceType"]
def __getattr__(self, name):
return self._status.get(name, "unsupported")
def __repr__(self) -> str:
return str(self._status)
def _preprocess(self, raw_status):
# device type
if "DeviceType" not in raw_status or (
"DeviceType" in raw_status
and raw_status["DeviceType"] not in self.device_type_mapping
):
raise AttributeError(
"`DeviceType` isn't in the raw status or has an invalid value."
)
if "PowerConsumption" in raw_status:
raw_status["PowerConsumption"] /= 10.0
status = {}
device_type = self.device_type_mapping[raw_status["DeviceType"]]
for key, value in raw_status.items():
if key in STATUS_DICT[device_type]:
if STATUS_DICT[device_type][key]["is_numeric"]:
status[key] = value
else:
status[key] = STATUS_DICT[device_type][key]["id2str"].get(
value, "unknown"
)
return status
@property
def status(self):
"""All status.
Returns
-------
dict
All status.
"""
return self._status
@property
def legacy_status(self):
"""All legacy status name used by the old API.
Returns
-------
JciHitachiAWSStatus
Status with legacy name.
"""
status = {}
device_type = self._status["DeviceType"]
for status_name, status_value in self._status.items():
if status_name in STATUS_DICT[device_type]:
if STATUS_DICT[device_type][status_name]["legacy_name"] is None:
# no legacy name
status.update({status_name: status_value})
else:
status.update(
{
STATUS_DICT[device_type][status_name][
"legacy_name"
]: status_value
}
)
return JciHitachiAWSStatus(status, legacy=True)
@staticmethod
@lru_cache
def str2id(
device_type: str,
status_name: str,
status_value: int = None,
status_str_value: str = None,
support_code: int = None,
):
is_valid = (status_value is not None) ^ (status_str_value is not None)
# Name check
if is_valid:
if status_name not in STATUS_DICT[device_type]:
legacy2new = {
specs["legacy_name"]: new_status_name
for new_status_name, specs in STATUS_DICT[device_type].items()
}
if status_name in legacy2new:
status_name = legacy2new[status_name]
else:
is_valid = False
# Value check
if is_valid:
if status_str_value is not None:
str2id_dict = {
value: key
for key, value in STATUS_DICT[device_type][status_name][
"id2str"
].items()
}
if status_str_value in str2id_dict:
status_value = str2id_dict[status_str_value]
else:
is_valid = False
else:
if (
not STATUS_DICT[device_type][status_name]["is_numeric"]
and status_value
not in STATUS_DICT[device_type][status_name]["id2str"]
):
is_valid = False
# if support_code is specified, we check whether the given status value is valid for the device
if is_valid and support_code is not None:
if STATUS_DICT[device_type][status_name]["is_numeric"]:
if status_value > support_code.status[status_name]:
is_valid = False
else:
if 2**status_value & support_code.status[status_name] == 0:
is_valid = False
return is_valid, status_name, status_value
def set_new_status(self, name: str, value: int):
if STATUS_DICT[self._device_type][name]["is_numeric"]:
self._status[name] = value
else:
self._status[name] = STATUS_DICT[self._device_type][name]["id2str"].get(
value, "unknown"
)
[docs]
class JciHitachiAWSStatusSupport:
"""Data class representing `AWSThing` status support.
Parameters
----------
raw_status : dict
Status retrieved from `JciHitachiAWSMqttConnection` _on_publish() callback.
"""
device_type_mapping = JciHitachiAWSStatus.device_type_mapping
def __init__(self, raw_status: dict) -> None:
self._raw_status: dict = raw_status
self._status: dict = self._preprocess(raw_status)
def __getattr__(self, name):
return self._status.get(name, "unsupported")
def __repr__(self) -> str:
return str(self._status)
def _preprocess(self, status):
status = status.copy()
if status.get("Error", 0) != 0:
return status
# device type
status["DeviceType"] = self.device_type_mapping[status["DeviceType"]]
status["Brand"] = "HITACHI"
if status["DeviceType"] == "AC":
status["max_temp"] = (
status["TemperatureSetting"] & 255
if "TemperatureSetting" in status
else 32
)
status["min_temp"] = (
status["TemperatureSetting"] >> 8 & 255
if "TemperatureSetting" in status
else 16
)
elif status["DeviceType"] == "DH":
status["max_humidity"] = (
status["HumiditySetting"] & 255 if "HumiditySetting" in status else 70
)
status["min_humidity"] = (
status["HumiditySetting"] >> 8 & 255
if "HumiditySetting" in status
else 40
)
return status
@property
def status(self):
"""All status.
Returns
-------
dict
All status.
"""
return self._status