Midea/midea_ac_lan/midea/devices/ac/device.py
2023-09-22 07:45:40 +02:00

353 lines
16 KiB
Python

import logging
import json
from .message import (
MessageQuery,
MessageSwitchDisplay,
MessageNewProtocolQuery,
MessageACResponse,
MessageGeneralSet,
MessageNewProtocolSet,
MessagePowerQuery,
MessageSubProtocolQuery,
MessageSubProtocolSet
)
try:
from enum import StrEnum
except ImportError:
from ...backports.enum import StrEnum
from ...core.device import MiedaDevice
_LOGGER = logging.getLogger(__name__)
class DeviceAttributes(StrEnum):
prompt_tone = "prompt_tone"
power = "power"
mode = "mode"
target_temperature = "target_temperature"
fan_speed = "fan_speed"
swing_vertical = "swing_vertical"
swing_horizontal = "swing_horizontal"
boost_mode = "boost_mode"
smart_eye = "smart_eye"
dry = "dry"
eco_mode = "eco_mode"
aux_heating = "aux_heating"
sleep_mode = "sleep_mode"
natural_wind = "natural_wind"
temp_fahrenheit = "temp_fahrenheit"
screen_display = "screen_display"
screen_display_new = "screen_display_new"
full_dust = "full_dust"
frost_protect = "frost_protect"
comfort_mode = "comfort_mode"
indoor_temperature = "indoor_temperature"
outdoor_temperature = "outdoor_temperature"
indirect_wind = "indirect_wind"
indoor_humidity = "indoor_humidity"
breezeless = "breezeless"
fresh_air_power = "fresh_air_power"
fresh_air_fan_speed = "fresh_air_fan_speed"
fresh_air_mode = "fresh_air_mode"
fresh_air_1 = "fresh_air_1"
fresh_air_2 = "fresh_air_2"
total_energy_consumption = "total_energy_consumption"
current_energy_consumption = "current_energy_consumption"
realtime_power = "realtime_power"
class MideaACDevice(MiedaDevice):
_fresh_air_fan_speeds = {
0: "Off", 20: "Silent", 40: "Low", 60: "Medium", 80: "High", 100: "Full"
}
_fresh_air_fan_speeds_rev = dict(reversed(_fresh_air_fan_speeds.items()))
def __init__(
self,
name: str,
device_id: int,
ip_address: str,
port: int,
token: str,
key: str,
protocol: int,
model: str,
customize: str
):
super().__init__(
name=name,
device_id=device_id,
device_type=0xAC,
ip_address=ip_address,
port=port,
token=token,
key=key,
protocol=protocol,
model=model,
attributes={
DeviceAttributes.prompt_tone: True,
DeviceAttributes.power: False,
DeviceAttributes.mode: 0,
DeviceAttributes.target_temperature: 24.0,
DeviceAttributes.fan_speed: 102,
DeviceAttributes.swing_vertical: False,
DeviceAttributes.swing_horizontal: False,
DeviceAttributes.smart_eye: False,
DeviceAttributes.dry: False,
DeviceAttributes.aux_heating: False,
DeviceAttributes.boost_mode: False,
DeviceAttributes.sleep_mode: False,
DeviceAttributes.frost_protect: False,
DeviceAttributes.comfort_mode: False,
DeviceAttributes.eco_mode: False,
DeviceAttributes.natural_wind: False,
DeviceAttributes.temp_fahrenheit: False,
DeviceAttributes.screen_display: False,
DeviceAttributes.screen_display_new: False,
DeviceAttributes.full_dust: False,
DeviceAttributes.indoor_temperature: None,
DeviceAttributes.outdoor_temperature: None,
DeviceAttributes.indirect_wind: False,
DeviceAttributes.indoor_humidity: None,
DeviceAttributes.breezeless: False,
DeviceAttributes.total_energy_consumption: None,
DeviceAttributes.current_energy_consumption: None,
DeviceAttributes.realtime_power: None,
DeviceAttributes.fresh_air_power: False,
DeviceAttributes.fresh_air_fan_speed: 0,
DeviceAttributes.fresh_air_mode: None,
DeviceAttributes.fresh_air_1: None,
DeviceAttributes.fresh_air_2: None,
})
self._fresh_air_version = None
self._default_temperature_step = 0.5
self._temperature_step = None
self._used_subprotocol = False
self._bb_sn8_flag = False
self._bb_timer = False
self._power_analysis_method = None
self._default_power_analysis_method = 1
self.set_customize(customize)
@property
def temperature_step(self):
return self._temperature_step
@property
def fresh_air_fan_speeds(self):
return list(MideaACDevice._fresh_air_fan_speeds.values())
def build_query(self):
if self._used_subprotocol:
return [
MessageSubProtocolQuery(self._device_protocol_version, 0x10),
MessageSubProtocolQuery(self._device_protocol_version, 0x11),
MessageSubProtocolQuery(self._device_protocol_version, 0x30)
]
return [
MessageQuery(self._device_protocol_version),
MessageNewProtocolQuery(self._device_protocol_version),
MessagePowerQuery(self._device_protocol_version)
]
def process_message(self, msg):
message = MessageACResponse(msg, self._power_analysis_method)
_LOGGER.debug(f"[{self.device_id}] Received: {message}")
new_status = {}
has_fresh_air = False
if hasattr(message, "used_subprotocol"):
self._used_subprotocol = True
if hasattr(message, "sn8_flag"):
self._bb_sn8_flag = message.sn8_flag
if hasattr(message, "timer"):
self._bb_timer = message.timer
for status in self._attributes.keys():
if hasattr(message, str(status)):
value = getattr(message, str(status))
if status == DeviceAttributes.fresh_air_power:
has_fresh_air = True
self._attributes[status] = value
new_status[str(status)] = self._attributes[status]
if has_fresh_air:
if self._attributes[DeviceAttributes.fresh_air_power]:
for k, v in MideaACDevice._fresh_air_fan_speeds_rev.items():
if self._attributes[DeviceAttributes.fresh_air_fan_speed] > k:
break
else:
self._attributes[DeviceAttributes.fresh_air_mode] = v
else:
self._attributes[DeviceAttributes.fresh_air_mode] = "Off"
new_status[DeviceAttributes.fresh_air_mode.value] = self._attributes[DeviceAttributes.fresh_air_mode]
if not self._attributes[DeviceAttributes.power] or \
(DeviceAttributes.swing_vertical in new_status and self._attributes[DeviceAttributes.swing_vertical]):
self._attributes[DeviceAttributes.indirect_wind] = False
new_status[DeviceAttributes.indirect_wind.value] = False
if not self._attributes[DeviceAttributes.power]:
self._attributes[DeviceAttributes.screen_display] = False
new_status[DeviceAttributes.screen_display.value] = False
if self._attributes[DeviceAttributes.fresh_air_1] is not None:
self._fresh_air_version = DeviceAttributes.fresh_air_1
elif self._attributes[DeviceAttributes.fresh_air_2] is not None:
self._fresh_air_version = DeviceAttributes.fresh_air_2
return new_status
def make_message_set(self):
message = MessageGeneralSet(self._device_protocol_version)
message.power = self._attributes[DeviceAttributes.power]
message.prompt_tone = self._attributes[DeviceAttributes.prompt_tone]
message.mode = self._attributes[DeviceAttributes.mode]
message.target_temperature = self._attributes[DeviceAttributes.target_temperature]
message.fan_speed = self._attributes[DeviceAttributes.fan_speed]
message.swing_vertical = self._attributes[DeviceAttributes.swing_vertical]
message.swing_horizontal = self._attributes[DeviceAttributes.swing_horizontal]
message.boost_mode = self._attributes[DeviceAttributes.boost_mode]
message.smart_eye = self._attributes[DeviceAttributes.smart_eye]
message.dry = self._attributes[DeviceAttributes.dry]
message.eco_mode = self._attributes[DeviceAttributes.eco_mode]
message.aux_heating = self._attributes[DeviceAttributes.aux_heating]
message.sleep_mode = self._attributes[DeviceAttributes.sleep_mode]
message.natural_wind = self._attributes[DeviceAttributes.natural_wind]
message.temp_fahrenheit = self._attributes[DeviceAttributes.temp_fahrenheit]
message.frost_protect = self._attributes[DeviceAttributes.frost_protect]
message.comfort_mode = self._attributes[DeviceAttributes.comfort_mode]
return message
def make_subptotocol_message_set(self):
message = MessageSubProtocolSet(self._device_protocol_version)
message.power = self._attributes[DeviceAttributes.power]
message.prompt_tone = self._attributes[DeviceAttributes.prompt_tone]
message.aux_heating = self._attributes[DeviceAttributes.aux_heating]
message.mode = self._attributes[DeviceAttributes.mode]
message.target_temperature = self._attributes[DeviceAttributes.target_temperature]
message.fan_speed = self._attributes[DeviceAttributes.fan_speed]
message.boost_mode = self._attributes[DeviceAttributes.boost_mode]
message.dry = self._attributes[DeviceAttributes.dry]
message.eco_mode = self._attributes[DeviceAttributes.eco_mode]
message.sleep_mode = self._attributes[DeviceAttributes.sleep_mode]
message.sn8_flag = self._bb_sn8_flag
message.timer = self._bb_timer
return message
def make_message_uniq_set(self):
if self._used_subprotocol:
message = self.make_subptotocol_message_set()
else:
message = self.make_message_set()
return message
def set_attribute(self, attr, value):
# if nat a sensor
message = None
if attr not in [DeviceAttributes.indoor_temperature,
DeviceAttributes.outdoor_temperature,
DeviceAttributes.indoor_humidity,
DeviceAttributes.full_dust,
DeviceAttributes.total_energy_consumption,
DeviceAttributes.current_energy_consumption,
DeviceAttributes.realtime_power]:
if attr == DeviceAttributes.prompt_tone:
self._attributes[DeviceAttributes.prompt_tone] = value
self.update_all({DeviceAttributes.prompt_tone.value: value})
elif attr == DeviceAttributes.screen_display:
if self._attributes[DeviceAttributes.screen_display_new]:
message = MessageNewProtocolSet(self._device_protocol_version)
message.screen_display = value
message.prompt_tone = self._attributes[DeviceAttributes.prompt_tone]
else:
message = MessageSwitchDisplay(self._device_protocol_version)
elif attr in [
DeviceAttributes.indirect_wind,
DeviceAttributes.breezeless
]:
message = MessageNewProtocolSet(self._device_protocol_version)
setattr(message, str(attr), value)
message.prompt_tone = self._attributes[DeviceAttributes.prompt_tone]
elif attr == DeviceAttributes.fresh_air_power:
if self._fresh_air_version is not None:
message = MessageNewProtocolSet(self._device_protocol_version)
setattr(
message,
str(self._fresh_air_version),
[value, self._attributes[DeviceAttributes.fresh_air_fan_speed]]
)
elif attr == DeviceAttributes.fresh_air_mode:
if value in MideaACDevice._fresh_air_fan_speeds.values():
speed = list(MideaACDevice._fresh_air_fan_speeds.keys())[
list(MideaACDevice._fresh_air_fan_speeds.values()).index(value)
]
fresh_air = [True, speed] if speed > 0 else \
[False, self._attributes[DeviceAttributes.fresh_air_fan_speed]]
message = MessageNewProtocolSet(self._device_protocol_version)
setattr(
message,
str(self._fresh_air_version),
fresh_air
)
elif not value:
message = MessageNewProtocolSet(self._device_protocol_version)
setattr(
message,
str(self._fresh_air_version),
[False, self._attributes[DeviceAttributes.fresh_air_fan_speed]]
)
elif attr == DeviceAttributes.fresh_air_fan_speed:
if self._fresh_air_version is not None:
message = MessageNewProtocolSet(self._device_protocol_version)
fresh_air = [True, value] if value > 0 else \
[False, self._attributes[DeviceAttributes.fresh_air_fan_speed]]
setattr(
message,
str(self._fresh_air_version),
fresh_air
)
elif attr in self._attributes.keys():
message = self.make_message_uniq_set()
if attr in [
DeviceAttributes.boost_mode,
DeviceAttributes.sleep_mode,
DeviceAttributes.frost_protect,
DeviceAttributes.comfort_mode,
DeviceAttributes.eco_mode
]:
message.boost_mode = False
message.sleep_mode = False
message.comfort_mode = False
message.eco_mode = False
message.frost_protect = False
setattr(message, str(attr), value)
if attr == DeviceAttributes.mode:
setattr(message, DeviceAttributes.power.value, True)
if message is not None:
self.build_send(message)
def set_target_temperature(self, target_temperature, mode):
message = self.make_message_uniq_set()
message.target_temperature = target_temperature
if mode is not None:
message.power = True
message.mode = mode
self.build_send(message)
def set_swing(self, swing_vertical, swing_horizontal):
message = self.make_message_uniq_set()
message.swing_vertical = swing_vertical
message.swing_horizontal = swing_horizontal
self.build_send(message)
def set_customize(self, customize):
self._temperature_step = self._default_temperature_step
self._power_analysis_method = self._default_power_analysis_method
if customize and len(customize) > 0:
try:
params = json.loads(customize)
if params and "temperature_step" in params:
self._temperature_step = params.get("temperature_step")
if params and "power_analysis_method" in params:
self._power_analysis_method = params.get("power_analysis_method")
except Exception as e:
_LOGGER.error(f"[{self.device_id}] Set customize error: {repr(e)}")
self.update_all({"temperature_step": self._temperature_step})
class MideaAppliance(MideaACDevice):
pass