Files
srv-docker/homeassistant/config/custom_components/polaris/select.py
2026-03-07 17:28:59 +00:00

483 lines
26 KiB
Python

"""The Polaris IQ Home component."""
from __future__ import annotations
import json
import re
import logging
from typing import Iterable
import copy
import datetime
import os
from homeassistant.components import mqtt
from homeassistant.components.mqtt.models import ReceiveMessage
from homeassistant.components.select import DOMAIN, SelectEntity
from homeassistant.helpers.entity import DeviceInfo, EntityCategory
from homeassistant.util import slugify
from homeassistant.core import HomeAssistant, callback
from homeassistant.config_entries import ConfigEntry
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .common import PolarisBaseEntity
# Import global values.
from .const import (
MANUFACTURER,
MQTT_ROOT_TOPIC,
DEVICEID,
DEVICETYPE,
POLARIS_DEVICE,
CUSTOM_SELECT_FILE_PATH,
SELECT_KETTLE,
SELECT_COOKER,
SELECT_COFFEEMAKER,
SELECT_COFFEEMAKER_ROG,
SELECT_CLIMATE,
SELECT_VACUUM,
SELECT_IRRIGATOR,
SELECT_AIRCLEANER_EAP,
SELECT_AIRCONDITIONER_SWING_HORIZONTAL,
SELECT_AIRCONDITIONER_SWING_VERTICAL,
PolarisSelectEntityDescription,
POLARIS_KETTLE_TYPE,
POLARIS_KETTLE_WITH_WEIGHT_TYPE,
POLARIS_HUMIDDIFIER_TYPE,
POLARIS_COOKER_TYPE,
POLARIS_COFFEEMAKER_TYPE,
POLARIS_COFFEEMAKER_ROG_TYPE,
POLARIS_CLIMATE_TYPE,
POLARIS_VACUUM_TYPE,
POLARIS_IRRIGATOR_TYPE,
POLARIS_AIRCLEANER_EAP_TYPE,
POLARIS_AIRCONDITIONER_TYPE,
)
_LOGGER = logging.getLogger(__name__)
_LOGGER.setLevel(logging.DEBUG)
async def async_setup_entry(
hass: HomeAssistant, config: ConfigEntry, async_add_entities: AddEntitiesCallback,
) -> None:
integrationUniqueID = config.unique_id
mqtt_root = config.data[MQTT_ROOT_TOPIC]
device_id = config.data["DEVICEID"]
device_type = config.data[DEVICETYPE]
device_prefix_topic = config.data["DEVPREFIXTOPIC"]
selectList = []
if (device_type in POLARIS_KETTLE_TYPE) or (device_type in POLARIS_KETTLE_WITH_WEIGHT_TYPE):
SELECT_KETTLE_LC = copy.deepcopy(SELECT_KETTLE)
for description in SELECT_KETTLE_LC:
description.mqttTopicCurrentMode = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCurrentMode}"
description.mqttTopicCommandMode = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCommandMode}"
description.mqttTopicCommandTemperature = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCommandTemperature}"
description.device_prefix_topic = device_prefix_topic
selectList.append(
PolarisSelect(
description=description,
device_friendly_name=device_id,
mqtt_root=mqtt_root,
device_type=device_type,
device_id=device_id
)
)
if (device_type in POLARIS_COOKER_TYPE):
SELECT_COOKER_LC = copy.deepcopy(SELECT_COOKER)
for description in SELECT_COOKER_LC:
description.mqttTopicCurrentMode = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCurrentMode}"
description.mqttTopicCommandMode = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCommandMode}"
description.mqttTopicCommandTemperature = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCommandTemperature}"
description.device_prefix_topic = device_prefix_topic
selectList.append(
PolarisSelect(
description=description,
device_friendly_name=device_id,
mqtt_root=mqtt_root,
device_type=device_type,
device_id=device_id
)
)
if (device_type in POLARIS_COFFEEMAKER_TYPE):
SELECT_COFFEEMAKER_LC = copy.deepcopy(SELECT_COFFEEMAKER)
for description in SELECT_COFFEEMAKER_LC:
description.mqttTopicCurrentMode = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCurrentMode}"
description.mqttTopicCommandMode = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCommandMode}"
description.device_prefix_topic = device_prefix_topic
selectList.append(
PolarisSelect(
description=description,
device_friendly_name=device_id,
mqtt_root=mqtt_root,
device_type=device_type,
device_id=device_id
)
)
if (device_type in POLARIS_COFFEEMAKER_ROG_TYPE):
SELECT_COFFEEMAKER_ROG_LC = copy.deepcopy(SELECT_COFFEEMAKER_ROG)
for description in SELECT_COFFEEMAKER_ROG_LC:
description.mqttTopicCurrentMode = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCurrentMode}"
description.mqttTopicCommandMode = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCommandMode}"
description.device_prefix_topic = device_prefix_topic
selectList.append(
PolarisSelect(
description=description,
device_friendly_name=device_id,
mqtt_root=mqtt_root,
device_type=device_type,
device_id=device_id
)
)
if (device_type in POLARIS_CLIMATE_TYPE):
SELECT_CLIMATE_LC = copy.deepcopy(SELECT_CLIMATE)
for description in SELECT_CLIMATE_LC:
description.mqttTopicCurrentMode = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCurrentMode}"
description.mqttTopicCommandMode = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCommandMode}"
description.device_prefix_topic = device_prefix_topic
selectList.append(
PolarisSelect(
description=description,
device_friendly_name=device_id,
mqtt_root=mqtt_root,
device_type=device_type,
device_id=device_id
)
)
if (device_type in POLARIS_VACUUM_TYPE):
SELECT_VACUUM_LC = copy.deepcopy(SELECT_VACUUM)
for description in SELECT_VACUUM_LC:
description.mqttTopicCurrentMode = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCurrentMode}"
description.mqttTopicCommandMode = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCommandMode}"
description.device_prefix_topic = device_prefix_topic
selectList.append(
PolarisSelect(
description=description,
device_friendly_name=device_id,
mqtt_root=mqtt_root,
device_type=device_type,
device_id=device_id
)
)
if (device_type in POLARIS_IRRIGATOR_TYPE):
SELECT_IRRIGATOR_LC = copy.deepcopy(SELECT_IRRIGATOR)
for description in SELECT_IRRIGATOR_LC:
description.mqttTopicCurrentMode = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCurrentMode}"
description.mqttTopicCommandMode = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCommandMode}"
description.device_prefix_topic = device_prefix_topic
selectList.append(
PolarisSelect(
description=description,
device_friendly_name=device_id,
mqtt_root=mqtt_root,
device_type=device_type,
device_id=device_id
)
)
if (device_type in POLARIS_AIRCLEANER_EAP_TYPE):
SELECT_AIRCLEANER_EAP_LC = copy.deepcopy(SELECT_AIRCLEANER_EAP)
for description in SELECT_AIRCLEANER_EAP_LC:
description.mqttTopicCurrentMode = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCurrentMode}"
description.mqttTopicCommandMode = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCommandMode}"
description.device_prefix_topic = device_prefix_topic
selectList.append(
PolarisSelect(
description=description,
device_friendly_name=device_id,
mqtt_root=mqtt_root,
device_type=device_type,
device_id=device_id
)
)
if (device_type in POLARIS_AIRCONDITIONER_TYPE) and (device_type == "813"):
SELECT_AIRCONDITIONER_SWING_HORIZONTAL_LC = copy.deepcopy(SELECT_AIRCONDITIONER_SWING_HORIZONTAL)
for description in SELECT_AIRCONDITIONER_SWING_HORIZONTAL_LC:
description.mqttTopicCurrentMode = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCurrentMode}"
description.mqttTopicCommandMode = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCommandMode}"
description.device_prefix_topic = device_prefix_topic
selectList.append(
PolarisSelect(
description=description,
device_friendly_name=device_id,
mqtt_root=mqtt_root,
device_type=device_type,
device_id=device_id
)
)
if (device_type in POLARIS_AIRCONDITIONER_TYPE) and (device_type == "813"):
SELECT_AIRCONDITIONER_SWING_VERTICAL_LC = copy.deepcopy(SELECT_AIRCONDITIONER_SWING_VERTICAL)
for description in SELECT_AIRCONDITIONER_SWING_VERTICAL_LC:
description.mqttTopicCurrentMode = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCurrentMode}"
description.mqttTopicCommandMode = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCommandMode}"
description.device_prefix_topic = device_prefix_topic
selectList.append(
PolarisSelect(
description=description,
device_friendly_name=device_id,
mqtt_root=mqtt_root,
device_type=device_type,
device_id=device_id
)
)
async_add_entities(selectList, update_before_add=True)
class PolarisSelect(PolarisBaseEntity, SelectEntity):
entity_description: PolarisSelectEntityDescription
def __init__(
self,
device_friendly_name: str,
description: PolarisSelectEntityDescription,
mqtt_root: str,
device_id: str | None=None,
device_type: str | None=None,
) -> None:
super().__init__(
device_friendly_name=device_friendly_name,
mqtt_root=mqtt_root,
device_type=device_type,
device_id=device_id,
)
self.entity_description = description
self._attr_unique_id = slugify(f"{device_id}_{description.name}")
self.entity_id = f"{DOMAIN}.{POLARIS_DEVICE[int(device_type)]['class']}_{POLARIS_DEVICE[int(device_type)]['model']}_{description.name}"
self._attr_has_entity_name = True
if POLARIS_DEVICE[int(self.device_type)]['class'] == "kettle" or POLARIS_DEVICE[int(self.device_type)]['class'] == "cooker" or POLARIS_DEVICE[int(self.device_type)]['class'] == "coffeemaker" or POLARIS_DEVICE[int(self.device_type)]['class'] == "cleaner":
self._select_options = json.loads(json.dumps(SELECT_COFFEEMAKER[0].options))
if device_type == "826":
self._EAP_data0 = "0000"
if device_type == "813":
self._conditioner_data0 = "0000"
self._custom_data_select = self._read_file()
if self._custom_data_select is not None:
if POLARIS_DEVICE[int(self.device_type)]['class'] == "kettle" and "SELECT_KETTLE_options" in self._custom_data_select:
# self.entity_description.options = json.loads(json.dumps(self.entity_description.options))
for key, value in self._custom_data_select["SELECT_KETTLE_options"].items():
self.entity_description.options[key] = value
# _LOGGER.debug("kettle %s", self.entity_description.options)
if POLARIS_DEVICE[int(self.device_type)]['class'] == "cooker" and "SELECT_COOKER_options" in self._custom_data_select:
# self.entity_description.options = json.loads(json.dumps(self.entity_description.options))
for key, value in self._custom_data_select["SELECT_COOKER_options"].items():
self.entity_description.options[key] = json.dumps([value])
# _LOGGER.debug("cooker %s", self.entity_description.options)
if POLARIS_DEVICE[int(self.device_type)]['class'] == "coffeemaker":
if int(self.device_type) == 45 and "SELECT_COFFEEMAKER_ROG_options" in self._custom_data_select:
# self.entity_description.options = json.loads(json.dumps(self.entity_description.options))
for key, value in self._custom_data_select["SELECT_COFFEEMAKER_ROG_options"].items():
self.entity_description.options[key] = json.dumps([value])
# _LOGGER.debug("coffee_rog %s", self.entity_description.options)
elif "SELECT_COFFEEMAKER_options" in self._custom_data_select:
# self.entity_description.options = json.loads(json.dumps(self.entity_description.options))
for key, value in self._custom_data_select["SELECT_COFFEEMAKER_options"].items():
self.entity_description.options[key] = json.dumps([value])
# _LOGGER.debug("coffee %s", self.entity_description.options)
if POLARIS_DEVICE[int(self.device_type)]['class'] == "cleaner" and "SELECT_VACUUM_rooms" in self._custom_data_select and self.entity_description.key == "select_room":
# self.entity_description.options = json.loads(json.dumps(self.entity_description.options))
for key, value in self._custom_data_select["SELECT_VACUUM_rooms"].items():
self.entity_description.options[key] = json.dumps([value])
self._attr_options = list(self.entity_description.options.keys())
self._attr_current_option = self._attr_options[0]
self._attr_available = False
def _read_file(self):
file_path = CUSTOM_SELECT_FILE_PATH
if os.path.exists(file_path):
with open(file_path, 'r', encoding='utf-8') as file:
content = json.loads(file.read())
else:
content = None
return content
# @property
# def available(self):
# return self._attr_current_option is not None
def key_from_option(self, option: str):
try:
return next(
key
for key, value in self.entity_description.options.items()
if json.loads(value)[0]["mode"] == option
)
except StopIteration:
return None
async def async_select_option(self, option: str) -> None:
self._attr_current_option = option
if POLARIS_DEVICE[int(self.device_type)]['class'] == "cooker":
cook_time = json.loads(self.entity_description.options[option])
service_data = {}
service_data["entity_id"] = f"time.{POLARIS_DEVICE[int(self.device_type)]['class']}_{POLARIS_DEVICE[int(self.device_type)]['model'].replace('-', '_')}_cooking_time"
service_data["time"] = str(datetime.timedelta(seconds=cook_time[0]["time"]))
await self.hass.services.async_call("time", "set_value", service_data)
service_data = {}
service_data["entity_id"] = f"number.{POLARIS_DEVICE[int(self.device_type)]['class']}_{POLARIS_DEVICE[int(self.device_type)]['model'].replace('-', '_')}_set_temperature"
service_data["value"] = cook_time[0]["temperature"]
await self.hass.services.async_call("number", "set_value", service_data)
if POLARIS_DEVICE[int(self.device_type)]['class'] == "kettle":
mqtt.publish(self.hass, self.entity_description.mqttTopicCommandTemperature, self.entity_description.options[option])
mqtt.publish(self.hass, self.entity_description.mqttTopicCommandMode, 3)
if POLARIS_DEVICE[int(self.device_type)]['class'] == "coffeemaker":
if int(self.device_type) == 45:
coffee_mode = json.loads(self.entity_description.options[option])
service_data = {}
service_data["entity_id"] = f"number.{POLARIS_DEVICE[int(self.device_type)]['class']}_{POLARIS_DEVICE[int(self.device_type)]['model'].replace('-', '_')}_tank"
if coffee_mode[0]["tank"] != 0:
service_data["value"] = str(coffee_mode[0]["tank"])
else:
service_data["value"] = 7.777
await self.hass.services.async_call("number", "set_value", service_data)
service_data = {}
service_data["entity_id"] = f"number.{POLARIS_DEVICE[int(self.device_type)]['class']}_{POLARIS_DEVICE[int(self.device_type)]['model'].replace('-', '_')}_amount"
if coffee_mode[0]["amount"] != 0:
service_data["value"] = str(coffee_mode[0]["amount"])
else:
service_data["value"] = 100.777
await self.hass.services.async_call("number", "set_value", service_data)
service_data = {}
service_data["entity_id"] = f"number.{POLARIS_DEVICE[int(self.device_type)]['class']}_{POLARIS_DEVICE[int(self.device_type)]['model'].replace('-', '_')}_temperature"
if coffee_mode[0]["temperature"] != 0:
service_data["value"] = str(coffee_mode[0]["temperature"])
else:
service_data["value"] = 100.777
await self.hass.services.async_call("number", "set_value", service_data)
else:
coffee_mode = json.loads(self.entity_description.options[option])
for key, val in coffee_mode[0].items():
if key == "mode":
mode = val
elif val != 0:
service_data = {}
service_data["entity_id"] = f"number.{POLARIS_DEVICE[int(self.device_type)]['class']}_{POLARIS_DEVICE[int(self.device_type)]['model'].replace('-', '_')}_{key}"
service_data["value"] = str(val)
await self.hass.services.async_call("number", "set_value", service_data)
else:
if key == "weight":
val = "7.777"
else:
val = "55.777"
service_data = {}
service_data["entity_id"] = f"number.{POLARIS_DEVICE[int(self.device_type)]['class']}_{POLARIS_DEVICE[int(self.device_type)]['model'].replace('-', '_')}_{key}"
service_data["value"] = val
await self.hass.services.async_call("number", "set_value", service_data)
if self.device_type in POLARIS_CLIMATE_TYPE:
mqtt.publish(self.hass, self.entity_description.mqttTopicCommandMode, self.entity_description.options[option])
if self.device_type in POLARIS_AIRCONDITIONER_TYPE:
mqtt.publish(self.hass, self.entity_description.mqttTopicCommandMode, self.entity_description.options[option])
if (int(self.entity_description.options[option]) > 0):
if self.entity_description.key == "select_swing_horizontal":
mqtt.publish(self.hass, self.entity_description.mqttTopicCommandMode[:-1] + "0", self._conditioner_data0[:2] + "00" + self._conditioner_data0[-4:])
if self.entity_description.key == "select_swing_vertical":
mqtt.publish(self.hass, self.entity_description.mqttTopicCommandMode[:-1] + "0", "00" + self._conditioner_data0[-6:])
else:
if self.entity_description.key == "select_swing_horizontal":
mqtt.publish(self.hass, self.entity_description.mqttTopicCommandMode[:-1] + "0", self._conditioner_data0[:2] + "01" + self._conditioner_data0[-4:])
if self.entity_description.key == "select_swing_vertical":
mqtt.publish(self.hass, self.entity_description.mqttTopicCommandMode[:-1] + "0", "01" + self._conditioner_data0[-6:])
if POLARIS_DEVICE[int(self.device_type)]['class'] == "irrigator":
if self._attr_current_option == "preset3":
mqtt.publish(self.hass, self.entity_description.mqttTopicCommandMode+"speed", "10" if self._preset_3[0] == "a" else self._preset_3[0])
mqtt.publish(self.hass, self.entity_description.mqttTopicCommandMode+"ioniser", self._preset_3[1])
mqtt.publish(self.hass, self.entity_description.mqttTopicCommandMode+"smart_mode", self._preset_3[2])
elif self._attr_current_option == "preset2":
mqtt.publish(self.hass, self.entity_description.mqttTopicCommandMode+"speed", "10" if self._preset_2[0] == "a" else self._preset_2[0])
mqtt.publish(self.hass, self.entity_description.mqttTopicCommandMode+"ioniser", self._preset_2[1])
mqtt.publish(self.hass, self.entity_description.mqttTopicCommandMode+"smart_mode", self._preset_2[2])
else:
mqtt.publish(self.hass, self.entity_description.mqttTopicCommandMode+"speed", "10" if self._preset_1[0] == "a" else self._preset_1[0])
mqtt.publish(self.hass, self.entity_description.mqttTopicCommandMode+"ioniser", self._preset_1[1])
mqtt.publish(self.hass, self.entity_description.mqttTopicCommandMode+"smart_mode", self._preset_1[2])
if (self.device_type == "826" and self._EAP_data0[:2] == "02"):
mqtt.publish(self.hass, self.entity_description.mqttTopicCommandMode, self._EAP_data0[:3] + str(self.entity_description.options[option]))
async def async_added_to_hass(self):
@callback
def message_received_sel(message):
payload = message.payload
if payload in ("0", "[]"):
self._attr_current_option = self._attr_options[0]
self.async_write_ha_state()
elif POLARIS_DEVICE[int(self.device_type)]['class'] == "cooker":
sel_mode = json.loads(payload)[0]["mode"]
# if int(sel_mode)>0:
# self.switch.set_available(True)
# else:
# self.switch.set_available(False)
sel_opt = self.key_from_option(sel_mode)
self._attr_current_option = sel_opt
self.async_write_ha_state()
elif int(self.device_type) == 45:
sel_opt = self.key_from_option(int(payload))
self._attr_current_option = sel_opt
self.async_write_ha_state()
elif int(self.device_type) == 69:
self._attr_current_option = self._attr_options[int(payload)]
self.async_write_ha_state()
elif int(self.device_type) == 813:
self._attr_current_option = self._attr_options[int(payload)]
self.async_write_ha_state()
elif POLARIS_DEVICE[int(self.device_type)]['class'] == "irrigator":
self._preset_1 = [payload[1:2], payload[7:8], payload[13:14]]
self._preset_2 = [payload[3:4], payload[9:10], payload[15:16]]
self._preset_3 = [payload[5:6], payload[11:12], payload[17:]]
if self._attr_current_option == "preset3":
mqtt.publish(self.hass, self.entity_description.mqttTopicCommandMode+"speed", "10" if self._preset_3[0] == "a" else self._preset_3[0])
mqtt.publish(self.hass, self.entity_description.mqttTopicCommandMode+"ioniser", self._preset_3[1])
mqtt.publish(self.hass, self.entity_description.mqttTopicCommandMode+"smart_mode", self._preset_3[2])
elif self._attr_current_option == "preset2":
mqtt.publish(self.hass, self.entity_description.mqttTopicCommandMode+"speed", "10" if self._preset_2[0] == "a" else self._preset_2[0])
mqtt.publish(self.hass, self.entity_description.mqttTopicCommandMode+"ioniser", self._preset_2[1])
mqtt.publish(self.hass, self.entity_description.mqttTopicCommandMode+"smart_mode", self._preset_2[2])
else:
mqtt.publish(self.hass, self.entity_description.mqttTopicCommandMode+"speed", "10" if self._preset_1[0] == "a" else self._preset_1[0])
mqtt.publish(self.hass, self.entity_description.mqttTopicCommandMode+"ioniser", self._preset_1[1])
mqtt.publish(self.hass, self.entity_description.mqttTopicCommandMode+"smart_mode", self._preset_1[2])
await mqtt.async_subscribe(self.hass, self.entity_description.mqttTopicCurrentMode, message_received_sel, 1)
@callback
def EAP_data_message_received(message):
self._EAP_data0 = message.payload
# _LOGGER.debug("EAP data0 message select %s", self._EAP_data0)
if self.device_type == "826":
await mqtt.async_subscribe(
self.hass,
self.entity_description.mqttTopicCurrentMode,
EAP_data_message_received,
1,
)
@callback
def conditioner_data_message_received(message):
self._conditioner_data0 = message.payload
if self.device_type == "813":
await mqtt.async_subscribe(
self.hass,
self.entity_description.mqttTopicCurrentMode[:-1]+"0",
conditioner_data_message_received,
1,
)
@callback
async def entity_availability(message):
if self.entity_description.name != "available":
if str(message.payload).lower() in ("1", "true"):
self._attr_available = False
else:
self._attr_available = True
self.async_write_ha_state()
await mqtt.async_subscribe(self.hass, f"{self.mqtt_root}/{self.entity_description.device_prefix_topic}/state/error/connection", entity_availability, 1)