Initialize docker stack repo

This commit is contained in:
g4st3r
2026-03-07 17:28:59 +00:00
commit 319d4eeb3e
458 changed files with 67215 additions and 0 deletions

View File

@@ -0,0 +1,42 @@
"""The Polaris IQ Home component."""
import logging
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers import device_registry as dr
# Import global values.
from .const import PLATFORMS
_LOGGER = logging.getLogger(__name__)
_LOGGER.setLevel(logging.DEBUG)
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Trigger the creation of sensors."""
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
# Активация всех деактивированных устройств при загрузке
await async_enable_disabled_devices(hass, entry)
return True
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Unload all sensor entities and services if integration is removed via UI.
No restart of home assistant is required.
"""
unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
return unload_ok
async def async_enable_disabled_devices(hass: HomeAssistant, entry: ConfigEntry):
"""Enable all disabled devices associated with this integration."""
device_registry = dr.async_get(hass)
devices = dr.async_entries_for_config_entry(device_registry, entry.entry_id)
for device in devices:
if device.disabled:
_LOGGER.debug("Enabling disabled device: %s (%s)", device.name, device.id)
device_registry.async_update_device(device.id, disabled_by = None) # Убираем disabled

View File

@@ -0,0 +1,324 @@
"""The Polaris IQ Home component."""
from __future__ import annotations
import json
import re
import logging
from typing import Iterable, List
import copy
from datetime import datetime
from homeassistant.components import mqtt
from homeassistant.components.mqtt.models import ReceiveMessage
from homeassistant.components.binary_sensor import (
DOMAIN,
BinarySensorDeviceClass,
BinarySensorEntity,
BinarySensorEntityDescription,
)
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
# Device
from homeassistant.helpers.device_registry import DeviceEntry, DeviceEntryDisabler
from homeassistant.helpers import device_registry as dev_reg
from homeassistant.helpers.entity import Entity
from homeassistant.helpers import entity_registry as ent_reg
from homeassistant.const import STATE_UNAVAILABLE
from .common import PolarisBaseEntity
# Import global values.
from .const import (
MANUFACTURER,
MQTT_ROOT_TOPIC,
DEVICEID,
DEVICETYPE,
POLARIS_DEVICE,
BINARYSENSOR_KETTLE,
BINARYSENSOR_LID,
BINARYSENSOR_WATER_TANK,
BINARYSENSOR_CAPPUCCINATOR,
BINARYSENSOR_AVAILABLE,
BINARYSENSOR_THERMOSTAT,
PolarisBinarySensorEntityDescription,
POLARIS_KETTLE_TYPE,
POLARIS_KETTLE_WITH_WEIGHT_TYPE,
POLARIS_HUMIDDIFIER_TYPE,
POLARIS_COOKER_TYPE,
POLARIS_COOKER_WITH_LID_TYPE,
POLARIS_COFFEEMAKER_TYPE,
POLARIS_COFFEEMAKER_ROG_TYPE,
POLARIS_CLIMATE_TYPE,
POLARIS_AIRCLEANER_TYPE,
POLARIS_AIRCLEANER_EAP_TYPE,
POLARIS_BOILER_TYPE,
POLARIS_VACUUM_TYPE,
POLARIS_IRRIGATOR_TYPE,
POLARIS_HEATER_TYPE,
POLARIS_AIRCONDITIONER_TYPE,
POLARIS_THERMOSTAT_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"]
binarysensorList = []
if (device_type in POLARIS_KETTLE_WITH_WEIGHT_TYPE):
BINARYSENSOR_KETTLE_LC = copy.deepcopy(BINARYSENSOR_KETTLE)
for description in BINARYSENSOR_KETTLE_LC:
description.mqttTopicStatus = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicStatus}"
description.device_prefix_topic = device_prefix_topic
binarysensorList.append(
PolarisBinarySensor(
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_WITH_LID_TYPE):
BINARYSENSOR_LID_LC = copy.deepcopy(BINARYSENSOR_LID)
for description in BINARYSENSOR_LID_LC:
description.mqttTopicStatus = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicStatus}"
description.device_prefix_topic = device_prefix_topic
binarysensorList.append(
PolarisBinarySensor(
description=description,
device_friendly_name=device_id,
mqtt_root=mqtt_root,
device_type=device_type,
device_id=device_id
)
)
if (device_type in POLARIS_HUMIDDIFIER_TYPE and device_type not in {"835","881"}):
BINARYSENSOR_WATER_TANK_LC = copy.deepcopy(BINARYSENSOR_WATER_TANK)
for description in BINARYSENSOR_WATER_TANK_LC:
description.mqttTopicStatus = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicStatus}"
description.device_prefix_topic = device_prefix_topic
binarysensorList.append(
PolarisBinarySensor(
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):
BINARYSENSOR_CAPPUCCINATOR_LC = copy.deepcopy(BINARYSENSOR_CAPPUCCINATOR)
for description in BINARYSENSOR_CAPPUCCINATOR_LC:
description.mqttTopicStatus = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicStatus}"
description.device_prefix_topic = device_prefix_topic
binarysensorList.append(
PolarisBinarySensor(
description=description,
device_friendly_name=device_id,
mqtt_root=mqtt_root,
device_type=device_type,
device_id=device_id
)
)
if (device_type in POLARIS_THERMOSTAT_TYPE):
BINARYSENSOR_THERMOSTAT_LC = copy.deepcopy(BINARYSENSOR_THERMOSTAT)
for description in BINARYSENSOR_THERMOSTAT_LC:
description.mqttTopicStatus = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicStatus}"
description.device_prefix_topic = device_prefix_topic
binarysensorList.append(
PolarisBinarySensor(
description=description,
device_friendly_name=device_id,
mqtt_root=mqtt_root,
device_type=device_type,
device_id=device_id
)
)
if (device_type in POLARIS_KETTLE_TYPE or
device_type in POLARIS_KETTLE_WITH_WEIGHT_TYPE or
device_type in POLARIS_HUMIDDIFIER_TYPE or
device_type in POLARIS_COOKER_TYPE or
device_type in POLARIS_COFFEEMAKER_ROG_TYPE or
device_type in POLARIS_COFFEEMAKER_TYPE or
device_type in POLARIS_CLIMATE_TYPE or
device_type in POLARIS_AIRCLEANER_TYPE or
device_type in POLARIS_AIRCLEANER_EAP_TYPE or
device_type in POLARIS_BOILER_TYPE or
device_type in POLARIS_VACUUM_TYPE or
device_type in POLARIS_IRRIGATOR_TYPE or
device_type in POLARIS_HEATER_TYPE or
device_type in POLARIS_AIRCONDITIONER_TYPE or
device_type in POLARIS_THERMOSTAT_TYPE):
BINARYSENSOR_AVAILABLE_LC = copy.deepcopy(BINARYSENSOR_AVAILABLE)
for description in BINARYSENSOR_AVAILABLE_LC:
description.mqttTopicStatus = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicStatus}"
description.device_prefix_topic = device_prefix_topic
binarysensorList.append(
PolarisBinarySensor(
description=description,
device_friendly_name=device_id,
mqtt_root=mqtt_root,
device_type=device_type,
device_id=device_id
)
)
async_add_entities(binarysensorList, update_before_add=True)
class PolarisBinarySensor(PolarisBaseEntity, BinarySensorEntity, ConfigEntry):
entity_description: PolarisBinarySensorEntityDescription
def __init__(
self,
device_friendly_name: str,
description: PolarisBinarySensorEntityDescription,
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
self._attr_is_on = False
self.device_entities = []
if self.entity_description.name != "available":
self._attr_available = False
async def async_added_to_hass(self):
@callback
async def message_received_base(message):
if int(self.device_type) == 45 and self.entity_description.key == "cappuccinator":
if int(message.payload) == 255:
self._attr_is_on = False
service_data = {}
service_data["entity_id"] = f"number.{POLARIS_DEVICE[int(self.device_type)]['class']}_{POLARIS_DEVICE[int(self.device_type)]['model'].replace('-', '_')}_tank"
service_data["value"] = 7.777
await self.hass.services.async_call("number", "set_value", service_data)
else:
self._attr_is_on = True
elif str(message.payload).lower() in ("1", "true"):
if self.entity_description.name == "available":
self._attr_is_on = False
# await self.update_device_availability(False)
# await self.get_device_entities(False)
else:
self._attr_is_on = True
elif str(message.payload).lower() in ("0", "false"):
if self.entity_description.name == "available":
self._attr_is_on = True
# await self.update_device_availability(True)
# await self.get_device_entities(True)
else:
self._attr_is_on = False
self.async_write_ha_state()
await mqtt.async_subscribe(
self.hass,
self.entity_description.mqttTopicStatus,
message_received_base,
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)
async def get_device_entities(self, available: bool): # -> List[Entity]:
"""Return a list of entities that belong to the same device as this binary sensor"""
entities = []
entity_registry = ent_reg.async_get(self.hass)
entity_entry = entity_registry.async_get(self.entity_id)
_LOGGER.debug("-------------------------------------------------")
_LOGGER.debug("entity_id 0 %s", self.entity_id)
if entity_entry:
_LOGGER.debug("entity_entry 0 %s", entity_entry)
dev_id = entity_entry.device_id
_LOGGER.debug("dev_id 0 %s", dev_id)
entity_ids = [
entry.entity_id for entry in entity_registry.entities.values()
if entry.device_id == dev_id
]
_LOGGER.debug("entity_ids 0 %s", entity_ids)
for entity_id_av in entity_ids:
entity_av = self.hass.states.get(entity_id_av)
if entity_av:
_LOGGER.debug("entity off: %s", entity_av)
# entity_av.attributes["_attr_available"] = available
self.hass.states.async_set(entity_id_av, entity_av.state, entity_av.attributes)
# return entities
# async def get_device_entities(self) -> List[Entity]:
# """Return a list of entities that belong to the same device as this binary sensor"""
# entities = []
# for entity in self.hass.entities:
# dev_find = f"{POLARIS_DEVICE[int(self.device_type)]['class'].replace('-', '_').lower()}_{POLARIS_DEVICE[int(self.device_type)]['model'].replace('-', '_').lower()}"
# if dev_find in entity.entity_id:
# entities.append(entity.entity_id)
# return entities
# async def update_device_availability(self, available: bool):
# """Обновляет статус устройства в Device Registry."""
# dev_registry = dev_reg.async_get(self.hass)
# device = dev_registry.async_get_device(self.device_info["identifiers"])
# if device:
# for entry in self.hass.config_entries.async_entries():
# if entry.domain == "polaris" and entry.unique_id == f"{POLARIS_DEVICE[int(self.device_type)]['class']}-{POLARIS_DEVICE[int(self.device_type)]['model']}-{self.device_id}":
# config_entries = entry
# if available:
# dev_registry.async_update_device(device.id, disabled_by = None)
# state = self.hass.states.get(self.entity_id)
# if state == None:
# delta_time = 1000
# else:
# self._new_time_available = datetime.now().timestamp()
# delta_time = self._new_time_available - state.last_changed.timestamp()
# if delta_time > 5:
# await self.hass.config_entries.async_reload(config_entries.entry_id)
# else:
# dev_registry.async_update_device(device.id, disabled_by = DeviceEntryDisabler.INTEGRATION)

View File

@@ -0,0 +1,364 @@
"""The Polaris IQ Home component."""
from __future__ import annotations
import json
import re
import logging
from typing import Iterable
import copy
from datetime import datetime
import os
from homeassistant.components import mqtt
from homeassistant.components.mqtt.models import ReceiveMessage
from homeassistant.components.button import (
DOMAIN,
ButtonDeviceClass,
ButtonEntity,
ButtonEntityDescription,
)
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 homeassistant.helpers import entity_registry as er
from .common import PolarisBaseEntity
# Import global values.
from .const import (
MANUFACTURER,
MQTT_ROOT_TOPIC,
DEVICEID,
DEVICETYPE,
POLARIS_DEVICE,
CUSTOM_SELECT_FILE_PATH,
BUTTON_HUMIDIFIER,
BUTTON_COOKER,
SELECT_COOKER,
BUTTON_COFFEEMAKER,
SELECT_COFFEEMAKER,
SELECT_COFFEEMAKER_ROG,
BUTTON_CLIMATES,
BUTTON_CLIMATES_200,
BUTTON_AIRCLEANER,
PolarisButtonEntityDescription,
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_AIRCLEANER_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"]
buttonList = []
if (device_type in POLARIS_HUMIDDIFIER_TYPE and device_type not in {"835","881"}):
BUTTON_HUMIDIFIER_LC = copy.deepcopy(BUTTON_HUMIDIFIER)
for description in BUTTON_HUMIDIFIER_LC:
description.mqttTopicCommand = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCommand}"
description.device_prefix_topic = device_prefix_topic
buttonList.append(
PolarisButton(
description=description,
device_friendly_name=device_id,
mqtt_root=mqtt_root,
device_type=device_type,
device_id=device_id,
device_prefix_topic=device_prefix_topic,
)
)
if (device_type in POLARIS_COOKER_TYPE):
BUTTON_COOKER_LC = copy.deepcopy(BUTTON_COOKER)
for description in BUTTON_COOKER_LC:
description.mqttTopicCommand = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCommand}"
description.device_prefix_topic = device_prefix_topic
buttonList.append(
PolarisButton(
description=description,
device_friendly_name=device_id,
mqtt_root=mqtt_root,
device_type=device_type,
device_id=device_id,
device_prefix_topic=device_prefix_topic,
)
)
if (device_type in POLARIS_COFFEEMAKER_TYPE):
BUTTON_COFFEEMAKER_LC = copy.deepcopy(BUTTON_COFFEEMAKER)
for description in BUTTON_COFFEEMAKER_LC:
description.mqttTopicCommand = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCommand}"
description.device_prefix_topic = device_prefix_topic
buttonList.append(
PolarisButton(
description=description,
device_friendly_name=device_id,
mqtt_root=mqtt_root,
device_type=device_type,
device_id=device_id,
device_prefix_topic=device_prefix_topic,
)
)
if (device_type in POLARIS_COFFEEMAKER_ROG_TYPE):
BUTTON_COFFEEMAKER_LC = copy.deepcopy(BUTTON_COFFEEMAKER)
for description in BUTTON_COFFEEMAKER_LC:
description.mqttTopicCommand = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCommand}"
description.device_prefix_topic = device_prefix_topic
buttonList.append(
PolarisButton(
description=description,
device_friendly_name=device_id,
mqtt_root=mqtt_root,
device_type=device_type,
device_id=device_id,
device_prefix_topic=device_prefix_topic,
)
)
if (device_type in POLARIS_CLIMATE_TYPE):
if (device_type == "859"):
BUTTON_CLIMATES_200_LC = copy.deepcopy(BUTTON_CLIMATES_200)
for description in BUTTON_CLIMATES_200_LC:
description.mqttTopicCommand = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCommand}"
description.device_prefix_topic = device_prefix_topic
buttonList.append(
PolarisButton(
description=description,
device_friendly_name=device_id,
mqtt_root=mqtt_root,
device_type=device_type,
device_id=device_id,
device_prefix_topic=device_prefix_topic,
)
)
else:
BUTTON_CLIMATES_LC = copy.deepcopy(BUTTON_CLIMATES)
for description in BUTTON_CLIMATES_LC:
description.mqttTopicCommand = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCommand}"
description.device_prefix_topic = device_prefix_topic
buttonList.append(
PolarisButton(
description=description,
device_friendly_name=device_id,
mqtt_root=mqtt_root,
device_type=device_type,
device_id=device_id,
device_prefix_topic=device_prefix_topic,
)
)
if (device_type in POLARIS_AIRCLEANER_TYPE):
BUTTON_AIRCLEANER_LC = copy.deepcopy(BUTTON_AIRCLEANER)
for description in BUTTON_AIRCLEANER_LC:
description.mqttTopicCommand = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCommand}"
description.device_prefix_topic = device_prefix_topic
buttonList.append(
PolarisButton(
description=description,
device_friendly_name=device_id,
mqtt_root=mqtt_root,
device_type=device_type,
device_id=device_id,
device_prefix_topic=device_prefix_topic,
)
)
async_add_entities(buttonList, update_before_add=True)
class PolarisButton(PolarisBaseEntity, ButtonEntity):
entity_description: PolarisButtonDescription
def __init__(
self,
device_friendly_name: str,
description: PolarisButtonEntityDescription,
mqtt_root: str,
device_id: str | None=None,
device_type: str | None=None,
device_prefix_topic: 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_available = False
self._attr_has_entity_name = True
self.device_prefix_topic = device_prefix_topic
if POLARIS_DEVICE[int(self.device_type)]['class'] == "cooker":
self._select_options = json.loads(json.dumps(SELECT_COOKER[0].options))
if POLARIS_DEVICE[int(self.device_type)]['class'] == "coffeemaker":
if int(self.device_type) == 45:
self._select_options = json.loads(json.dumps(SELECT_COFFEEMAKER_ROG[0].options))
else:
self._select_options = json.loads(json.dumps(SELECT_COFFEEMAKER[0].options))
self._custom_data_select = self._read_file()
if self._custom_data_select is not None:
if POLARIS_DEVICE[int(self.device_type)]['class'] == "cooker" and "SELECT_COOKER_options" in self._custom_data_select:
# self._select_options = json.loads(json.dumps(SELECT_COOKER[0].options))
for key, value in self._custom_data_select["SELECT_COOKER_options"].items():
self._select_options[key] = json.dumps([value])
# _LOGGER.debug("cooker %s", self._select_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._select_options = json.loads(json.dumps(SELECT_COFFEEMAKER_ROG[0].options))
for key, value in self._custom_data_select["SELECT_COFFEEMAKER_ROG_options"].items():
self._select_options[key] = json.dumps([value])
# _LOGGER.debug("coffee_rog %s", self._select_options)
elif "SELECT_COFFEEMAKER_options" in self._custom_data_select:
# self._select_options = json.loads(json.dumps(SELECT_COFFEEMAKER[0].options))
for key, value in self._custom_data_select["SELECT_COFFEEMAKER_options"].items():
self._select_options[key] = json.dumps([value])
# _LOGGER.debug("coffee %s", self._select_options)
# self._attr_options = list(self._select_options.keys())
# self._attr_current_option = self._attr_options[0]
async def async_added_to_hass(self):
@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)
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
def get_state_by_unique_id(self, entity_domain, entity_name):
entity_unique_id = f"{self.device_id}_{entity_name}"
entity_registry = er.async_get(self.hass)
entity_id = entity_registry.async_get_entity_id(entity_domain, "polaris", entity_unique_id)
return self.hass.states.get(entity_id).state
async def async_press(self) -> None:
if (self.device_type in POLARIS_COFFEEMAKER_TYPE):
if self.entity_description.key == "button_stop":
mqtt.publish(self.hass, self.entity_description.mqttTopicCommand+"mode", "0")
# Get entity_id by unique_id
# Работает
zzzz = self.get_state_by_unique_id("number", "amount")
_LOGGER.debug("state %s", zzzz)
else:
state_amount = self.hass.states.get(f"number.{POLARIS_DEVICE[int(self.device_type)]['class']}_{POLARIS_DEVICE[int(self.device_type)]['model'].replace('-', '_')}_amount").state
state_weight = self.hass.states.get(f"number.{POLARIS_DEVICE[int(self.device_type)]['class']}_{POLARIS_DEVICE[int(self.device_type)]['model'].replace('-', '_')}_weight").state
state_tank = self.hass.states.get(f"number.{POLARIS_DEVICE[int(self.device_type)]['class']}_{POLARIS_DEVICE[int(self.device_type)]['model'].replace('-', '_')}_tank").state
state_pressure = self.hass.states.get(f"number.{POLARIS_DEVICE[int(self.device_type)]['class']}_{POLARIS_DEVICE[int(self.device_type)]['model'].replace('-', '_')}_pressure").state
state_speed = self.hass.states.get(f"number.{POLARIS_DEVICE[int(self.device_type)]['class']}_{POLARIS_DEVICE[int(self.device_type)]['model'].replace('-', '_')}_speed").state
state_temp = self.hass.states.get(f"number.{POLARIS_DEVICE[int(self.device_type)]['class']}_{POLARIS_DEVICE[int(self.device_type)]['model'].replace('-', '_')}_temperature").state
state_mode = self.hass.states.get(f"select.{POLARIS_DEVICE[int(self.device_type)]['class']}_{POLARIS_DEVICE[int(self.device_type)]['model'].replace('-', '_')}_select_mode_cofeemaker").state
state_coffee_maker = self.hass.states.get(f"switch.{POLARIS_DEVICE[int(self.device_type)]['class']}_{POLARIS_DEVICE[int(self.device_type)]['model'].replace('-', '_')}_power").state
if state_coffee_maker != "off":
if state_amount != "unavailable":
mqtt.publish(self.hass, self.entity_description.mqttTopicCommand+"amount", state_amount)
if state_weight != "unavailable":
mqtt.publish(self.hass, self.entity_description.mqttTopicCommand+"weight", state_weight)
if state_tank != "unavailable":
mqtt.publish(self.hass, self.entity_description.mqttTopicCommand+"tank", state_tank)
if state_pressure != "unavailable":
mqtt.publish(self.hass, self.entity_description.mqttTopicCommand+"pressure", state_pressure)
if state_speed != "unavailable":
mqtt.publish(self.hass, self.entity_description.mqttTopicCommand+"speed", state_speed)
if state_temp != "unavailable":
mqtt.publish(self.hass, self.entity_description.mqttTopicCommand+"temperature", state_temp)
if state_mode != "not_selected":
# !!!
command_mode = self._select_options[state_mode]
coffee_mode = json.loads(command_mode)
mqtt.publish(self.hass, self.entity_description.mqttTopicCommand+"mode", coffee_mode[0]["mode"])
if (self.device_type in POLARIS_COFFEEMAKER_ROG_TYPE):
if self.entity_description.key == "button_stop":
mqtt.publish(self.hass, self.entity_description.mqttTopicCommand+"mode", "0")
else:
state_amount = self.hass.states.get(f"number.{POLARIS_DEVICE[int(self.device_type)]['class']}_{POLARIS_DEVICE[int(self.device_type)]['model'].replace('-', '_')}_amount").state
state_tank = self.hass.states.get(f"number.{POLARIS_DEVICE[int(self.device_type)]['class']}_{POLARIS_DEVICE[int(self.device_type)]['model'].replace('-', '_')}_tank").state
state_temp = self.hass.states.get(f"number.{POLARIS_DEVICE[int(self.device_type)]['class']}_{POLARIS_DEVICE[int(self.device_type)]['model'].replace('-', '_')}_temperature").state
state_mode = self.hass.states.get(f"select.{POLARIS_DEVICE[int(self.device_type)]['class']}_{POLARIS_DEVICE[int(self.device_type)]['model'].replace('-', '_')}_select_mode_cofeemaker").state
state_cappuccinator = self.hass.states.get(f"binary_sensor.{POLARIS_DEVICE[int(self.device_type)]['class']}_{POLARIS_DEVICE[int(self.device_type)]['model'].replace('-', '_')}_cappuccinator").state
state_power = self.hass.states.get(f"switch.{POLARIS_DEVICE[int(self.device_type)]['class']}_{POLARIS_DEVICE[int(self.device_type)]['model'].replace('-', '_')}_power").state
if state_power == "off":
mqtt.publish(self.hass, f"{self.mqtt_root}/{self.device_prefix_topic}/control/mode", 5)
if state_cappuccinator == "off" and state_mode in ("cappuccino", "double_cappuccino", "latte", "double_latte", "flat_white", "hot_milk"):
mqtt.publish(self.hass, f"{self.mqtt_root}/{self.device_prefix_topic}/state/error/code", "99")
return
elif state_mode == "not_selected":
mqtt.publish(self.hass, f"{self.mqtt_root}/{self.device_prefix_topic}/state/error/code", "98")
return
else:
if state_amount == "unavailable":
state_amount = "0"
if state_tank == "unavailable":
state_tank = "0"
mqtt.publish(self.hass, self.entity_description.mqttTopicCommand+"amount", state_amount)
mqtt.publish(self.hass, self.entity_description.mqttTopicCommand+"temperature", state_temp)
mqtt.publish(self.hass, self.entity_description.mqttTopicCommand+"tank", state_tank)
# !!!
command_mode = self._select_options[state_mode]
coffee_mode = json.loads(command_mode)
mqtt.publish(self.hass, self.entity_description.mqttTopicCommand+"mode", coffee_mode[0]["mode"])
mqtt.publish(self.hass, f"{self.mqtt_root}/{self.device_prefix_topic}/state/error/code", "00")
if POLARIS_DEVICE[int(self.device_type)]['class'] == "cooker":
if self.entity_description.key == "button_stop":
mqtt.publish(self.hass, self.entity_description.mqttTopicCommand, "[]")
else:
state_temp = self.hass.states.get(f"number.{POLARIS_DEVICE[int(self.device_type)]['class']}_{POLARIS_DEVICE[int(self.device_type)]['model'].replace('-', '_')}_set_temperature").state
state_time = self.hass.states.get(f"time.{POLARIS_DEVICE[int(self.device_type)]['class']}_{POLARIS_DEVICE[int(self.device_type)]['model'].replace('-', '_')}_cooking_time").state
state_time_obj = datetime.strptime(state_time, "%H:%M:%S")
state_time_seconds = state_time_obj.hour * 3600 + state_time_obj.minute * 60 + state_time_obj.second
state_mode = self.hass.states.get(f"select.{POLARIS_DEVICE[int(self.device_type)]['class']}_{POLARIS_DEVICE[int(self.device_type)]['model'].replace('-', '_')}_select_mode_cooker").state
# !!!
command_mode = self._select_options[state_mode]
cook_mode = json.loads(command_mode)
payload = "[{" + f'"mode":{cook_mode[0]["mode"]}, "time":{state_time_seconds}, "temperature":{state_temp}' + "}]"
state_time_delay = self.hass.states.get(f"time.{POLARIS_DEVICE[int(self.device_type)]['class']}_{POLARIS_DEVICE[int(self.device_type)]['model'].replace('-', '_')}_delay_start").state
state_time_delay_obj = datetime.strptime(state_time_delay, "%H:%M:%S")
state_time_delay_seconds = state_time_delay_obj.hour * 3600 + state_time_delay_obj.minute * 60 + state_time_delay_obj.second
if state_time_delay_seconds > 59:
mqtt.publish(self.hass, f"{self.mqtt_root}/{self.device_prefix_topic}/control/delay_start", state_time_delay_seconds)
mqtt.publish(self.hass, self.entity_description.mqttTopicCommand, payload)
if POLARIS_DEVICE[int(self.device_type)]['class'] == "humidifier":
mqtt.publish(self.hass, self.entity_description.mqttTopicCommand, self.entity_description.payloads)
if (self.device_type in POLARIS_CLIMATE_TYPE):
mqtt.publish(self.hass, self.entity_description.mqttTopicCommand, self.entity_description.payloads)
if (self.device_type in POLARIS_AIRCLEANER_TYPE):
mqtt.publish(self.hass, self.entity_description.mqttTopicCommand, self.entity_description.payloads)

View File

@@ -0,0 +1,493 @@
"""The Polaris IQ Home component."""
from __future__ import annotations
import json
import re
import logging
from typing import Iterable
import copy
from homeassistant.components import mqtt
from homeassistant.components.mqtt.models import ReceiveMessage
from homeassistant.const import ATTR_TEMPERATURE, UnitOfTemperature
from homeassistant.components.climate import (
DOMAIN,
ATTR_HVAC_MODE,
ATTR_TARGET_TEMP_HIGH,
ATTR_TARGET_TEMP_LOW,
ClimateEntity,
ClimateEntityFeature,
HVACAction,
HVACMode,
)
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,
CLIMATES,
CLIMATES_200,
AIRCLEANER,
AIRCLEANER_EAP,
CLIMATES_HEATER,
CLIMATES_AIRCONDITIONER,
CLIMATES_THERMOSTAT,
PolarisClimateEntityDescription,
POLARIS_CLIMATE_TYPE,
POLARIS_AIRCLEANER_TYPE,
POLARIS_AIRCLEANER_EAP_TYPE,
POLARIS_HEATER_TYPE,
POLARIS_AIRCONDITIONER_TYPE,
POLARIS_THERMOSTAT_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"]
climateList = []
if (device_type in POLARIS_CLIMATE_TYPE):
# Create humidifier
if (device_type == "859"):
CLIMATES_LC = copy.deepcopy(CLIMATES_200)
else:
CLIMATES_LC = copy.deepcopy(CLIMATES)
for description in CLIMATES_LC:
description.mqttTopicStateTemperature = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicStateTemperature}"
description.mqttTopicCommandTemperature = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCommandTemperature}"
description.mqttTopicCurrentTemperature = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCurrentTemperature}"
description.mqttTopicStateFanMode = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicStateFanMode}"
description.mqttTopicCommandFanMode = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCommandFanMode}"
description.mqttTopicCommandPower = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCommandPower}"
description.mqttTopicCurrentPresetMode = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCurrentPresetMode}"
description.mqttTopicCommandPresetMode = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCommandPresetMode}"
description.device_prefix_topic = device_prefix_topic
climateList.append(
PolarisClimate(
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_TYPE):
# Create humidifier
AIRCLEANER_LC = copy.deepcopy(AIRCLEANER)
for description in AIRCLEANER_LC:
description.mqttTopicStateFanMode = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicStateFanMode}"
description.mqttTopicCommandFanMode = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCommandFanMode}"
description.mqttTopicCommandPower = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCommandPower}"
description.mqttTopicCurrentPresetMode = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCurrentPresetMode}"
description.mqttTopicCommandPresetMode = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCommandPresetMode}"
description.device_prefix_topic = device_prefix_topic
climateList.append(
PolarisClimate(
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):
# Create aircleaner
AIRCLEANER_EAP_LC = copy.deepcopy(AIRCLEANER_EAP)
for description in AIRCLEANER_EAP_LC:
description.mqttTopicStateFanMode = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicStateFanMode}"
description.mqttTopicCommandFanMode = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCommandFanMode}"
description.mqttTopicCommandPower = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCommandPower}"
description.mqttTopicCurrentPresetMode = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCurrentPresetMode}"
description.mqttTopicCommandPresetMode = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCommandPresetMode}"
description.device_prefix_topic = device_prefix_topic
climateList.append(
PolarisClimate(
description=description,
device_friendly_name=device_id,
mqtt_root=mqtt_root,
device_type=device_type,
device_id=device_id
)
)
if (device_type in POLARIS_HEATER_TYPE):
# Create Heater
CLIMATES_HEATER_LC = copy.deepcopy(CLIMATES_HEATER)
for description in CLIMATES_HEATER_LC:
description.mqttTopicStateTemperature = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicStateTemperature}"
description.mqttTopicCommandTemperature = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCommandTemperature}"
description.mqttTopicCurrentTemperature = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCurrentTemperature}"
description.mqttTopicStateFanMode = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicStateFanMode}"
description.mqttTopicCommandFanMode = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCommandFanMode}"
description.mqttTopicCommandPower = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCommandPower}"
description.mqttTopicCurrentPresetMode = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCurrentPresetMode}"
description.mqttTopicCommandPresetMode = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCommandPresetMode}"
description.device_prefix_topic = device_prefix_topic
climateList.append(
PolarisClimate(
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):
# Create AIRCONDITIONER
CLIMATES_AIRCONDITIONER_LC = copy.deepcopy(CLIMATES_AIRCONDITIONER)
for description in CLIMATES_AIRCONDITIONER_LC:
description.mqttTopicStateTemperature = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicStateTemperature}"
description.mqttTopicCommandTemperature = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCommandTemperature}"
description.mqttTopicCurrentTemperature = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCurrentTemperature}"
description.mqttTopicStateFanMode = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicStateFanMode}"
description.mqttTopicCommandFanMode = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCommandFanMode}"
description.mqttTopicCommandPower = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCommandPower}"
description.mqttTopicCurrentPresetMode = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCurrentPresetMode}"
description.mqttTopicCommandPresetMode = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCommandPresetMode}"
description.mqttTopicStateSwingMode = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicStateSwingMode}"
description.mqttTopicCommandSwingMode = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCommandSwingMode}"
description.device_prefix_topic = device_prefix_topic
climateList.append(
PolarisClimate(
description=description,
device_friendly_name=device_id,
mqtt_root=mqtt_root,
device_type=device_type,
device_id=device_id
)
)
if (device_type in POLARIS_THERMOSTAT_TYPE):
# Create THERMOSTAT
CLIMATES_THERMOSTAT_LC = copy.deepcopy(CLIMATES_THERMOSTAT)
for description in CLIMATES_THERMOSTAT_LC:
description.mqttTopicStateTemperature = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicStateTemperature}"
description.mqttTopicCommandTemperature = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCommandTemperature}"
description.mqttTopicCurrentTemperature = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCurrentTemperature}"
description.mqttTopicCommandPower = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCommandPower}"
description.mqttTopicCurrentPresetMode = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCurrentPresetMode}"
description.mqttTopicCommandPresetMode = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCommandPresetMode}"
description.device_prefix_topic = device_prefix_topic
climateList.append(
PolarisClimate(
description=description,
device_friendly_name=device_id,
mqtt_root=mqtt_root,
device_type=device_type,
device_id=device_id
)
)
async_add_entities(climateList, update_before_add=True)
class PolarisClimate(PolarisBaseEntity, ClimateEntity):
entity_description: PolarisClimateEntityDescription
def __init__(
self,
device_friendly_name: str,
description: PolarisClimateEntityDescription,
mqtt_root: str,
device_id: str | None=None,
device_type: str | None=None,
device_class: ClimateDeviceClass | 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.payload_on=description.payload_on
self.payload_off=description.payload_off
self._attr_has_entity_name = True
self._attr_precision = 1.0
self._attr_target_temperature_step = 1.0
self._attr_hvac_modes = self.entity_description.hvac_modes
self._attr_preset_modes = list(self.entity_description.preset_modes.keys())
if device_type in {"806","847"}:
self.entity_description.fan_modes = {"auto": "0", "20_5_percent": "1", "40_5_percent": "2", "60_5_percent": "3", "80_5_percent": "4", "100_5_percent": "5"}
if device_type == "820":
self.entity_description.fan_modes = {"auto": "0", "low": "1", "middle": "2", "high": "3"}
if self.entity_description.fan_modes is not None:
self._attr_fan_modes = list(self.entity_description.fan_modes.keys())
self._attr_fan_mode = self.entity_description.fan_mode
self._attr_supported_features = self.entity_description.supported_features
self._enable_turn_on_off_backwards_compatibility = False
self._attr_temperature_unit = UnitOfTemperature.CELSIUS
self._attr_precision = self.entity_description.temp_step
self._attr_target_temperature = 20
if device_type == "820":
self._attr_max_temp = 32
else:
self._attr_max_temp = self.entity_description.max_temp
self._attr_min_temp = self.entity_description.min_temp
self._attr_preset_mode = self.entity_description.preset_mode
self._attr_hvac_mode = HVACMode.OFF
self._attr_available = False
if self.entity_description.swing_mode is not None:
self._attr_swing_mode = self.entity_description.swing_mode
self._attr_swing_modes = list(self.entity_description.swing_modes.keys())
# self._attr_supported_features |= ClimateEntityFeature.SWING_MODE
self._swing_message = "00000000"
if device_type == "826":
self._EAP_data0 = "0000"
if device_type == "882":
self._swing_message = "000000000000"
async def async_added_to_hass(self):
@callback
def message_received_curr_temp(message):
self._attr_current_temperature = float(message.payload)
self.async_write_ha_state()
@callback
def message_received_targ_temp(message):
if float(message.payload) < self._attr_min_temp:
self._attr_target_temperature = self._attr_min_temp
else:
self._attr_target_temperature = float(message.payload)
self.async_write_ha_state()
@callback
def message_received_mode(message):
payload = message.payload
if int(payload)==0:
self._attr_hvac_mode = HVACMode.OFF
elif (self.device_type in POLARIS_AIRCLEANER_TYPE):
self._attr_hvac_mode = HVACMode.DRY
elif (self.device_type in POLARIS_AIRCLEANER_EAP_TYPE):
self._attr_hvac_mode = HVACMode.DRY
elif (self.device_type in POLARIS_HEATER_TYPE):
self._attr_hvac_mode = HVACMode.HEAT
elif (self.device_type in POLARIS_THERMOSTAT_TYPE):
self._attr_hvac_mode = HVACMode.HEAT
elif (self.device_type in POLARIS_AIRCONDITIONER_TYPE):
match int(payload):
case 1:
self._attr_hvac_mode = HVACMode.AUTO
case 3:
self._attr_hvac_mode = HVACMode.DRY
case 5:
self._attr_hvac_mode = HVACMode.FAN_ONLY
case 4:
self._attr_hvac_mode = HVACMode.HEAT
case 2:
self._attr_hvac_mode = HVACMode.COOL
else:
self._attr_hvac_mode = HVACMode.FAN_ONLY
self.async_write_ha_state()
@callback
def message_received_preset_mode(message):
payload = message.payload
if int(payload) > 0:
self._attr_preset_mode = list(self.entity_description.preset_modes.keys())[list(self.entity_description.preset_modes.values()).index(payload)]
if self.device_type == "826":
self._EAP_data0 = "0" + payload + self._EAP_data0[-2:]
mqtt.publish(self.hass, self.entity_description.mqttTopicCommandPresetMode.replace("mode", "program_data/0"), self._EAP_data0)
self.async_write_ha_state()
@callback
def message_received_fan_mode(message):
self._attr_fan_mode = list(self.entity_description.fan_modes.keys())[list(self.entity_description.fan_modes.values()).index(message.payload)]
self.async_write_ha_state()
@callback
def message_received_swing_mode(message):
# _LOGGER.debug("swing message %s", message.payload)
self._swing_message = message.payload # 00112233 00-качание верх/низ 11-качание влево/вправо 22-эко режим охлаждения 33-автообогрев
match self._swing_message[:4]:
case "0000":
swmode = "off"
case "0001":
swmode = "horizontal"
case "0100":
swmode = "vertical"
case "0101":
swmode = "both"
# _LOGGER.debug("swing mode %s", swmode)
self._attr_swing_mode = swmode
self.async_write_ha_state()
@callback
def EAP_data_message_received(message):
self._EAP_data0 = message.payload
# _LOGGER.debug("EAP mode data0 message %s", self._EAP_data0)
if self.device_type == "826":
await mqtt.async_subscribe(
self.hass,
self.entity_description.mqttTopicCurrentPresetMode.replace("mode", "program_data/0"),
EAP_data_message_received,
1,
)
if self.entity_description.mqttTopicCurrentTemperature is not None:
await mqtt.async_subscribe(
self.hass,
self.entity_description.mqttTopicCurrentTemperature,
message_received_curr_temp,
1,
)
if self.entity_description.mqttTopicStateTemperature is not None:
await mqtt.async_subscribe(
self.hass,
self.entity_description.mqttTopicStateTemperature,
message_received_targ_temp,
1,
)
await mqtt.async_subscribe(
self.hass,
self.entity_description.mqttTopicCurrentPresetMode,
message_received_mode,
1,
)
await mqtt.async_subscribe(
self.hass,
self.entity_description.mqttTopicCurrentPresetMode,
message_received_preset_mode,
1,
)
if self.entity_description.mqttTopicStateFanMode is not None:
await mqtt.async_subscribe(
self.hass,
self.entity_description.mqttTopicStateFanMode,
message_received_fan_mode,
1,
)
if self.entity_description.mqttTopicStateSwingMode is not None:
await mqtt.async_subscribe(
self.hass,
self.entity_description.mqttTopicStateSwingMode,
message_received_swing_mode,
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)
async def async_turn_on(self) -> None:
if (self.device_type in POLARIS_AIRCLEANER_TYPE):
mqtt.publish(self.hass, self.entity_description.mqttTopicCommandPower, 1)
await self.async_set_hvac_mode(HVACMode.DRY)
elif (self.device_type in POLARIS_AIRCLEANER_EAP_TYPE):
mqtt.publish(self.hass, self.entity_description.mqttTopicCommandPower, 1)
await self.async_set_hvac_mode(HVACMode.DRY)
elif (self.device_type in POLARIS_HEATER_TYPE):
mqtt.publish(self.hass, self.entity_description.mqttTopicCommandPower, 1)
await self.async_set_hvac_mode(HVACMode.HEAT)
elif (self.device_type in POLARIS_THERMOSTAT_TYPE):
mqtt.publish(self.hass, self.entity_description.mqttTopicCommandPower, 1)
await self.async_set_hvac_mode(HVACMode.HEAT)
else:
mqtt.publish(self.hass, self.entity_description.mqttTopicCommandPower, 5)
await self.async_set_hvac_mode(HVACMode.FAN_ONLY)
async def async_turn_off(self) -> None:
mqtt.publish(self.hass, self.entity_description.mqttTopicCommandPower, 0)
await self.async_set_hvac_mode(HVACMode.OFF)
async def async_set_temperature(self, **kwargs: Any) -> None:
"""Set new target temperatures."""
if kwargs.get(ATTR_TEMPERATURE) is not None:
self._attr_target_temperature = kwargs.get(ATTR_TEMPERATURE)
if (kwargs.get(ATTR_TARGET_TEMP_HIGH) is not None and kwargs.get(ATTR_TARGET_TEMP_LOW) is not None):
self._attr_target_temperature_high = kwargs.get(ATTR_TARGET_TEMP_HIGH)
self._attr_target_temperature_low = kwargs.get(ATTR_TARGET_TEMP_LOW)
if (hvac_mode := kwargs.get(ATTR_HVAC_MODE)) is not None:
self._attr_hvac_mode = hvac_mode
mqtt.publish(self.hass, self.entity_description.mqttTopicCommandTemperature, int(self._attr_target_temperature))
self.async_write_ha_state()
async def async_set_fan_mode(self, fan_mode: str) -> None:
"""Set new fan mode."""
if (fan_mode == "9_speed"):
fan_mode = "8_speed"
if (self.device_type == "826" and fan_mode == "off"):
fan_mode = "low"
self._attr_fan_mode = fan_mode
mqtt.publish(self.hass, self.entity_description.mqttTopicCommandFanMode, self.entity_description.fan_modes[fan_mode])
self.async_write_ha_state()
async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None:
"""Set new operation mode."""
self._attr_hvac_mode = hvac_mode
if hvac_mode == "off":
mqtt.publish(self.hass, self.entity_description.mqttTopicCommandPresetMode, 0)
elif ((self.device_type in POLARIS_AIRCLEANER_TYPE) or (self.device_type in POLARIS_HEATER_TYPE) or (self.device_type in POLARIS_AIRCLEANER_EAP_TYPE)):
mqtt.publish(self.hass, self.entity_description.mqttTopicCommandPower, 1)
mqtt.publish(self.hass, self.entity_description.mqttTopicCommandFanMode, self.entity_description.fan_modes[self._attr_fan_mode]) # add for on fan after off heater
elif (self.device_type in POLARIS_AIRCONDITIONER_TYPE):
match hvac_mode:
case "auto":
command = 1
case "dry":
command = 3
case "fan_only":
command = 5
case "heat":
command = 4
case "cool":
command = 2
mqtt.publish(self.hass, self.entity_description.mqttTopicCommandPresetMode, command)
else:
mqtt.publish(self.hass, self.entity_description.mqttTopicCommandPresetMode, 5) # 5 = FAN_ONLY for AIRCLEANER and HEATER_TYPE
self.async_write_ha_state()
async def async_set_preset_mode(self, preset_mode: str) -> None:
"""Update preset_mode on."""
self._attr_preset_mode = preset_mode
mqtt.publish(self.hass, self.entity_description.mqttTopicCommandPresetMode, self.entity_description.preset_modes[preset_mode])
if self.device_type == "826":
if (preset_mode != "night" and int(self._EAP_data0[-2:]) > 1):
self._EAP_data0 = "0" + self.entity_description.preset_modes[preset_mode] + "01"
else:
self._EAP_data0 = "0" + self.entity_description.preset_modes[preset_mode] + self._EAP_data0[-2:]
# _LOGGER.debug("EAP data0 mode select %s", self._EAP_data0) # отправить в prog_data
mqtt.publish(self.hass, self.entity_description.mqttTopicCommandPresetMode.replace("mode", "program_data/0"), self._EAP_data0)
self.async_write_ha_state()
async def async_set_swing_mode(self, swing_mode: str) -> None:
"""Set new swing mode."""
self._attr_swing_mode = swing_mode
# _LOGGER.debug("set swing mode %s", swing_mode)
match swing_mode:
case "off":
swmessage = "0000"
case "horizontal":
swmessage = "0001"
case "vertical":
swmessage = "0100"
case "both":
swmessage = "0101"
mqtt.publish(self.hass, self.entity_description.mqttTopicCommandSwingMode, swmessage + self._swing_message[4:])
self.async_write_ha_state()

View File

@@ -0,0 +1,35 @@
"""The Polaris IQ Home component."""
import logging
from homeassistant.helpers.device_registry import DeviceInfo
from .const import DOMAIN, MANUFACTURER, POLARIS_DEVICE
#_LOGGER = logging.getLogger(__name__)
#_LOGGER.setLevel(logging.DEBUG)
class PolarisBaseEntity:
deviceID: str | None = None
def __init__(
self,
device_friendly_name: str,
mqtt_root: str,
device_type: str,
device_id: str
) -> None:
self.device_friendly_name = device_friendly_name
self.mqtt_root = mqtt_root
self.device_type=device_type
self.device_id=device_id
@property
def device_info(self) -> DeviceInfo:
return DeviceInfo(
name=POLARIS_DEVICE[int(self.device_type)]["class"]+" "+ POLARIS_DEVICE[int(self.device_type)]["model"],
identifiers={(DOMAIN, self.device_id, self.mqtt_root)},
manufacturer=MANUFACTURER,
model=POLARIS_DEVICE[int(self.device_type)]["class"]+" - "+ POLARIS_DEVICE[int(self.device_type)]["model"],
)

View File

@@ -0,0 +1,194 @@
"""The Polaris IQ Home component."""
from __future__ import annotations
import asyncio
import logging
import time
import voluptuous as vol
from homeassistant.data_entry_flow import FlowResult
from homeassistant.config_entries import ConfigFlow
from homeassistant.core import HomeAssistant, callback
from homeassistant.components import mqtt
from homeassistant.helpers.translation import async_get_translations
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.selector import (
SelectOptionDict,
SelectSelector,
SelectSelectorConfig,
SelectSelectorMode,
)
from .const import DEVICEID, DEVICETYPE, DOMAIN, MQTT_ROOT_TOPIC, MQTT_ROOT_TOPIC_DEFAULT, POLARIS_DEVICE
from homeassistant.helpers.service_info.mqtt import MqttServiceInfo
_LOGGER = logging.getLogger(__name__)
_LOGGER.setLevel(logging.DEBUG)
class PolarisConfigFlow(ConfigFlow, domain=DOMAIN):
VERSION = 1
def __init__(self) -> None:
"""Initialize flow."""
self._serial_number = None
self._topic_prefix = {}
self._device_found = {}
self._device_prefix_topic = {}
self._unknown_devtype = 0
async def _get_devtypes_from_mqtt(self):
await mqtt.async_subscribe(self.hass, "polaris/+/state/mac", self._mqtt_message_newdev)
await mqtt.async_subscribe(self.hass, "polaris/+/+/state/mac", self._mqtt_message_olddev)
await mqtt.async_subscribe(self.hass, "rusclimate/+/+/state/mac", self._mqtt_message_rusclidev)
@callback
async def _mqtt_message_newdev(self, message: ReceiveMessage):
topic = message.topic
device_id = topic.split("/")[1]
topic_check = f"polaris/{device_id}/state/devtype"
topic_bool = await self.topic_exists(topic_check)
if not topic_bool:
self._device_type = "0"
if int(self._device_type) not in POLARIS_DEVICE:
# _LOGGER.debug("newdevice unknown - %s", self._device_type)
self._unknown_devtype = int(self._device_type)
if device_id not in self._device_found:
self._device_found[device_id] = self._device_type
self._device_prefix_topic[device_id] = device_id
self._topic_prefix[device_id] = "polaris"
@callback
async def _mqtt_message_olddev(self, message: ReceiveMessage):
topic = message.topic
device_id = message.payload
device_type = topic.split("/")[1]
device_oldid = topic.split("/")[2]
if int(device_type) not in POLARIS_DEVICE:
# _LOGGER.debug("olddevice unknown - %s", device_type)
self._unknown_devtype = int(device_type)
if device_id not in self._device_found:
self._device_found[device_id] = device_type
self._device_prefix_topic[device_id] = f"{device_type}/{device_oldid}"
self._topic_prefix[device_id] = "polaris"
@callback
async def _mqtt_message_rusclidev(self, message: ReceiveMessage):
topic = message.topic
device_id = message.payload
device_type = str(int(topic.split("/")[1]) + 800)
device_oldid = topic.split("/")[2]
if int(device_type) not in POLARIS_DEVICE:
# _LOGGER.debug("rusclidevice unknown - %s", device_type)
self._unknown_devtype = int(device_type)
if device_id not in self._device_found:
self._device_found[device_id] = device_type
self._device_prefix_topic[device_id] = f"{topic.split("/")[1]}/{device_oldid}"
self._topic_prefix[device_id] = "rusclimate"
async def topic_exists(self, topic: str, timeout: float = 1.0) -> bool:
loop = asyncio.get_running_loop()
future: asyncio.Future = loop.create_future()
def message_received(msg):
if not future.done():
future.set_result(msg)
unsub = await mqtt.async_subscribe(self.hass, topic, message_received, qos=0)
try:
msg = await asyncio.wait_for(future, timeout)
self._device_type = msg.payload
except asyncio.TimeoutError:
return False
finally:
unsub()
return bool(msg.retain)
async def get_translated_type(self, device_type_lang):
language = self.hass.config.language
translations = await async_get_translations(self.hass, language, "common", {DOMAIN})
key = f"component.polaris.common.{device_type_lang}"
return translations.get(key, device_type_lang)
async def async_step_user(self, user_input=None):
await self._get_devtypes_from_mqtt()
await self.hass.async_add_executor_job(time.sleep, 2)
errors = {}
if user_input is None:
return self.async_show_form(
step_id="user",
data_schema=vol.Schema(
{
vol.Required(DEVICEID): SelectSelector(
SelectSelectorConfig(
options=[
SelectOptionDict(
value=dev_mac,
label=f"{POLARIS_DEVICE[int(dev_type) if int(dev_type) in POLARIS_DEVICE else 0]["model"]} (mac: {dev_mac})",
)
for dev_mac, dev_type in self._device_found.items()
],
mode=SelectSelectorMode.DROPDOWN,
translation_key="config_selector_devicetype",
)
),
}
),
errors=errors,
)
user_input[MQTT_ROOT_TOPIC] = self._topic_prefix[user_input[DEVICEID]]
user_input[DEVICETYPE] = self._device_found[user_input[DEVICEID]]
user_input["DEVPREFIXTOPIC"] = self._device_prefix_topic[user_input[DEVICEID]]
self.oldstep = user_input
# _LOGGER.debug("input1 %s", user_input)
if user_input[DEVICETYPE] == "0" or self._unknown_devtype > 0:
return self.async_show_form(
step_id="undef",
data_schema=vol.Schema(
{
vol.Required(DEVICETYPE): SelectSelector(
SelectSelectorConfig(
options=[
SelectOptionDict(
value=str(dev_id),
label=f'{str(dev_id)} {await self.get_translated_type(POLARIS_DEVICE[int(dev_id)]["class"])} ({POLARIS_DEVICE[int(dev_id)]["model"]})',
)
for dev_id in POLARIS_DEVICE
],
mode=SelectSelectorMode.DROPDOWN,
)
)
}
),
)
# _LOGGER.debug("input3 %s", user_input)
title = f"{POLARIS_DEVICE[int(user_input[DEVICETYPE])]['class']}-{POLARIS_DEVICE[int(user_input[DEVICETYPE])]['model']}-{user_input[DEVICEID]}"
await self.async_set_unique_id(title)
self._abort_if_unique_id_configured(error="already_configured")
# Create entities
return self.async_create_entry(
title=title,
data=user_input,
)
async def async_step_undef(self, user_input):
if user_input[DEVICETYPE] == "0":
error = "no_dev"
return self.async_abort(reason="not_supported")
self.oldstep[DEVICETYPE] = user_input[DEVICETYPE]
user_input = self.oldstep
user_input[MQTT_ROOT_TOPIC] = self._topic_prefix[user_input[DEVICEID]]
# _LOGGER.debug("input2 %s", user_input)
mqtt.publish(self.hass, f'{user_input[MQTT_ROOT_TOPIC]}/{user_input["DEVPREFIXTOPIC"]}/state/devtype', user_input[DEVICETYPE], 0, True)
title = f"{POLARIS_DEVICE[int(user_input[DEVICETYPE])]['class']}-{POLARIS_DEVICE[int(user_input[DEVICETYPE])]['model']}-{user_input[DEVICEID]}"
await self.async_set_unique_id(title)
self._abort_if_unique_id_configured(error="already_configured")
# Create entities
return self.async_create_entry(
title=title,
data=user_input,
)

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,224 @@
"""The Polaris IQ Home component."""
from __future__ import annotations
import json
import re
import logging
from typing import Iterable
import copy
from homeassistant.components import mqtt
from homeassistant.components.mqtt.models import ReceiveMessage
from homeassistant.const import ATTR_TEMPERATURE, UnitOfTemperature # ?????
from homeassistant.components.humidifier import (
DOMAIN,
HumidifierAction,
HumidifierDeviceClass,
HumidifierEntity,
HumidifierEntityFeature,
)
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,
HUMIDIFIERS,
PolarisHumidifierEntityDescription,
POLARIS_HUMIDDIFIER_TYPE,
POLARIS_HUMIDDIFIER_7_MODE_TYPE,
POLARIS_HUMIDDIFIER_5A_MODE_TYPE,
POLARIS_HUMIDDIFIER_5B_MODE_TYPE,
POLARIS_HUMIDDIFIER_4_MODE_TYPE,
POLARIS_HUMIDDIFIER_3A_MODE_TYPE,
POLARIS_HUMIDDIFIER_3B_MODE_TYPE,
POLARIS_HUMIDDIFIER_2_MODE_TYPE,
POLARIS_HUMIDDIFIER_1_MODE_TYPE,
POLARIS_HUMIDDIFIER_11_MODE_TYPE,
HUMIDDIFIER_5A_AVAILABLE_MODES,
HUMIDDIFIER_5B_AVAILABLE_MODES,
HUMIDDIFIER_4_AVAILABLE_MODES,
HUMIDDIFIER_3A_AVAILABLE_MODES,
HUMIDDIFIER_3B_AVAILABLE_MODES,
HUMIDDIFIER_2_AVAILABLE_MODES,
HUMIDDIFIER_1_AVAILABLE_MODES,
HUMIDDIFIER_11_AVAILABLE_MODES,
)
SUPPORT_FLAGS = HumidifierEntityFeature(1)
#_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"]
humidifierList = []
if (device_type in POLARIS_HUMIDDIFIER_TYPE):
# Create humidifier
HUMIDIFIERS_LC = copy.deepcopy(HUMIDIFIERS)
for description in HUMIDIFIERS_LC:
description.mqttTopicCurrentState = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCurrentState}"
description.mqttTopicCommandState = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCommandState}"
description.mqttTopicCurrentMode = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCurrentMode}"
description.mqttTopicCommandMode = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCommandMode}"
description.mqttTopicCurrentHumidity = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCurrentHumidity}"
description.mqttTopicCurrentTargetHumidity = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCurrentTargetHumidity}"
description.mqttTopicCommandTargetHumidity = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCommandTargetHumidity}"
description.device_prefix_topic = device_prefix_topic
humidifierList.append(
PolarisHumidifier(
description=description,
device_friendly_name=device_id,
mqtt_root=mqtt_root,
device_type=device_type,
device_id=device_id
)
)
async_add_entities(humidifierList, update_before_add=True)
class PolarisHumidifier(PolarisBaseEntity, HumidifierEntity):
entity_description: PolarisHumidifierEntityDescription
_attr_supported_features = HumidifierEntityFeature.MODES
def __init__(
self,
device_friendly_name: str,
description: PolarisHumidifierEntityDescription,
mqtt_root: str,
device_id: str | None=None,
device_type: str | None=None,
device_class: HumidifierDeviceClass | 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_is_on = True
self._attr_max_humidity = description.max_humidity
self._attr_min_humidity = description.min_humidity
if device_type in POLARIS_HUMIDDIFIER_7_MODE_TYPE:
self.my_operation_list = description.available_modes
elif device_type in POLARIS_HUMIDDIFIER_5A_MODE_TYPE:
self.my_operation_list = HUMIDDIFIER_5A_AVAILABLE_MODES
elif device_type in POLARIS_HUMIDDIFIER_5B_MODE_TYPE:
self.my_operation_list = HUMIDDIFIER_5B_AVAILABLE_MODES
elif device_type in POLARIS_HUMIDDIFIER_4_MODE_TYPE:
self.my_operation_list = HUMIDDIFIER_4_AVAILABLE_MODES
elif device_type in POLARIS_HUMIDDIFIER_3A_MODE_TYPE:
self.my_operation_list = HUMIDDIFIER_3A_AVAILABLE_MODES
elif device_type in POLARIS_HUMIDDIFIER_3B_MODE_TYPE:
self.my_operation_list = HUMIDDIFIER_3B_AVAILABLE_MODES
elif device_type in POLARIS_HUMIDDIFIER_2_MODE_TYPE:
self.my_operation_list = HUMIDDIFIER_2_AVAILABLE_MODES
elif device_type in POLARIS_HUMIDDIFIER_1_MODE_TYPE:
self.my_operation_list = HUMIDDIFIER_1_AVAILABLE_MODES
elif device_type in POLARIS_HUMIDDIFIER_11_MODE_TYPE:
self.my_operation_list = HUMIDDIFIER_11_AVAILABLE_MODES
self._attr_available_modes = list(self.my_operation_list.keys())
self._attr_mode = self._attr_available_modes[0]
self.payload_on=description.payload_on
self.payload_off=description.payload_off
self._attr_has_entity_name = True
self._attr_available = False
self._attr_current_humidity = self._attr_min_humidity
self._attr_target_humidity = self._attr_min_humidity
async def async_added_to_hass(self):
@callback
def message_received_curr_humid(message):
self._attr_current_humidity = float(message.payload)
self.async_write_ha_state()
@callback
def message_received_targ_humid(message):
if float(message.payload) < self._attr_min_humidity:
self._attr_target_humidity = self._attr_min_humidity
else:
self._attr_target_humidity = float(message.payload)
self.async_write_ha_state()
@callback
def message_received_mode(message):
payload = message.payload
if int(payload)==0:
self._attr_is_on = 0
else:
self._attr_is_on = 1
self._attr_mode = list(self.my_operation_list.keys())[list(self.my_operation_list.values()).index(message.payload)]
self.async_write_ha_state()
await mqtt.async_subscribe(
self.hass,
self.entity_description.mqttTopicCurrentHumidity,
message_received_curr_humid,
1,
)
await mqtt.async_subscribe(
self.hass,
self.entity_description.mqttTopicCurrentTargetHumidity,
message_received_targ_humid,
1,
)
await mqtt.async_subscribe(
self.hass,
self.entity_description.mqttTopicCurrentState,
message_received_mode,
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)
def turn_on(self, **kwargs):
self._attr_is_on = self.payload_on
topic = f"{self.entity_description.mqttTopicCommandState}"
self.publishToMQTT(topic)
def turn_off(self, **kwargs):
self._attr_is_on = self.payload_off
topic = f"{self.entity_description.mqttTopicCommandState}"
self.publishToMQTT(topic)
def publishToMQTT(self, topic: str):
mqtt.publish(self.hass, topic, str(self._attr_is_on))
def set_humidity(self, humidity: int):
self._attr_target_humidity = humidity
topic = f"{self.entity_description.mqttTopicCommandTargetHumidity}"
mqtt.publish(self.hass, topic, str(humidity))
def set_mode(self, mode: str):
self._attr_mode = mode
topic = f"{self.entity_description.mqttTopicCommandMode}"
mqtt.publish(self.hass, topic, self.my_operation_list[mode])

View File

@@ -0,0 +1,274 @@
{
"entity": {
"humidifier": {
"humidifier": {
"default": "mdi:air-humidifier",
"state": {
"off": "mdi:air-humidifier-off"
},
"state_attributes": {
"mode": {
"state": {
"auto": "mdi:refresh-auto",
"baby": "mdi:baby-carriage",
"boost": "mdi:rocket-launch",
"comfort": "mdi:heart-pulse",
"eco": "mdi:cloud-percent",
"home": "mdi:gesture-tap",
"sleep": "mdi:power-sleep",
"fitnes": "mdi:walk",
"yoga": "mdi:yoga",
"meditation": "mdi:meditation",
"prana_hand": "mdi:human-handsdown",
"prana_auto": "mdi:human-handsup",
"aroma": "mdi:scent"
}
}
}
}
},
"water_heater": {
"water_heater": {
"default": "mdi:kettle",
"state": {
"off": "mdi:kettle-off"
},
"state_attributes": {
"operation_mode": {
"default": "mdi:circle-medium",
"state": {
"eco": "mdi:water-plus",
"electric": "mdi:heat-wave",
"gas": "mdi:fire-circle",
"heat_pump": "mdi:water-thermometer",
"high_demand": "mdi:finance",
"off": "mdi:power",
"performance": "mdi:chart-bubble"
}
}
}
},
"water_boiler": {
"default": "mdi:water-boiler",
"state": {
"off": "mdi:water-boiler-off"
},
"state_attributes": {
"operation_mode": {
"state": {
"off": "mdi:power",
"performance": "mdi:chart-bubble",
"electric": "mdi:heat-wave",
"heat_pump": "mdi:water-thermometer",
"eco": "mdi:water-plus"
}
}
}
}
},
"switch": {
"sound_switch": {
"default": "mdi:volume-high",
"state": {
"off": "mdi:volume-off"
}
},
"child_lock_switch": {
"default": "mdi:lock-open-variant",
"state": {
"on": "mdi:lock"
}
},
"backlight_switch": {
"default": "mdi:alarm-light",
"state": {
"off": "mdi:alarm-light-off"
}
},
"backlight_bottom_switch": {
"default": "mdi:lightbulb-on",
"state": {
"off": "mdi:lightbulb-on-50"
}
},
"backlight_bright": {
"default": "mdi:lightbulb-on",
"state": {
"off": "mdi:lightbulb-on-50"
}
},
"damper_heater": {
"default": "mdi:window-closed-variant",
"state": {
"on": "mdi:window-open-variant"
}
},
"display_off_heater": {
"default": "mdi:lightbulb",
"state": {
"on": "mdi:lightbulb-auto"
}
},
"half_power_heater": {
"default": "mdi:fraction-one-half",
"state": {
"off": "mdi:fraction-one-half"
}
},
"auto_heater_switch": {
"default": "mdi:snowflake",
"state": {
"off": "mdi:snowflake-off"
}
},
"self_cleaning": {
"default": "mdi:autorenew",
"state": {
"off": "mdi:autorenew-off"
}
},
"eco_mode_switch": {
"default": "mdi:leaf",
"state": {
"off": "mdi:leaf-off"
}
}
},
"binary_sensor": {
"base_binary_sensor": {
"default": "mdi:home",
"state": {
"on": "mdi:home-off"
}
},
"water_tank_binary_sensor": {
"default": "mdi:cup",
"state": {
"on": "mdi:cup-off"
}
},
"lid_binary_sensor": {
"default": "mdi:pot",
"state": {
"on": "mdi:pot-steam"
}
},
"cappuccinator_binary_sensor": {
"default": "mdi:cup-off",
"state": {
"on": "mdi:cup"
}
}
},
"climate": {
"climate": {
"default": "mdi:hvac",
"state": {
"off": "mdi:hvac-off"
},
"state_attributes": {
"fan_mode": {
"state": {
"1_speed": "mdi:fan",
"2_speed": "mdi:fan",
"3_speed": "mdi:fan",
"4_speed": "mdi:fan",
"5_speed": "mdi:fan",
"6_speed": "mdi:fan",
"7_speed": "mdi:fan",
"8_speed": "mdi:fan",
"9_speed": "mdi:fan",
"off": "mdi:fan-off"
}
},
"preset_mode": {
"default": "mdi:hvac",
"state": {
"hands": "mdi:gesture-tap",
"auto": "mdi:molecule-co2",
"night": "mdi:weather-night",
"turbo": "mdi:rocket-launch",
"passive": "mdi:air-filter"
}
}
}
},
"aircleaner": {
"default": "mdi:air-purifier",
"state": {
"off": "mdi:air-purifier-off"
},
"state_attributes": {
"preset_mode": {
"default": "mdi:hvac",
"state": {
"hands": "mdi:gesture-tap",
"auto": "mdi:refresh-auto",
"night": "mdi:weather-night"
}
},
"fan_mode": {
"state": {
"top": "mdi:rocket-launch"
}
}
}
},
"heater": {
"default": "mdi:radiator",
"state": {
"off": "mdi:radiator-off"
},
"state_attributes": {
"fan_mode": {
"state": {
"auto": "mdi:flash-auto",
"10_percent": "mdi:numeric-1-box",
"20_percent": "mdi:numeric-2-box",
"30_percent": "mdi:numeric-3-box",
"40_percent": "mdi:numeric-4-box",
"50_percent": "mdi:numeric-5-box",
"60_percent": "mdi:numeric-6-box",
"70_percent": "mdi:numeric-7-box",
"80_percent": "mdi:numeric-8-box",
"90_percent": "mdi:numeric-9-box",
"100_percent": "mdi:numeric-10-box",
"20_5_percent": "mdi:numeric-1-box",
"40_5_percent": "mdi:numeric-2-box",
"60_5_percent": "mdi:numeric-3-box",
"80_5_percent": "mdi:numeric-4-box",
"100_5_percent": "mdi:numeric-5-box"
}
}
}
},
"conditioner": {
"default": "mdi:air-conditioner",
"state_attributes": {
"fan_mode": {
"state": {
"min": "mdi:fan-minus",
"low": "mdi:fan-speed-1",
"middle": "mdi:fan-speed-2",
"high": "mdi:fan-speed-3",
"max": "mdi:fan-plus"
}
}
}
},
"thermostat": {
"state_attributes": {
"preset_mode": {
"state": {
"turbo": "mdi:rocket-launch",
"antifrost": "mdi:snowflake-off",
"schedule": "mdi:calendar-blank",
"vacation": "mdi:plane-car",
"manual": "mdi:cog"
}
}
}
}
}
}
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,114 @@
"""The Polaris IQ Home component."""
from __future__ import annotations
import json
import re
import logging
from typing import Iterable
import copy
from homeassistant.components import mqtt
from homeassistant.components.mqtt.models import ReceiveMessage
from datetime import time
from homeassistant.components.time import DOMAIN, TimeEntity, TimeEntityDescription
#from homeassistant.components.datetime import DOMAIN, DateTimeEntity, DateTimeEntityDescription
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,
TIME_COOKER,
PolarisTimeEntityDescription,
POLARIS_COOKER_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"]
timeList = []
if (device_type in POLARIS_COOKER_TYPE):
TIME_COOKER_LC = copy.deepcopy(TIME_COOKER)
for description in TIME_COOKER_LC:
description.mqttTopicCurrentTime = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCurrentTime}"
description.mqttTopicCommandTime = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCommandTime}"
description.device_prefix_topic = device_prefix_topic
timeList.append(
PolarisTime(
description=description,
device_friendly_name=device_id,
mqtt_root=mqtt_root,
device_type=device_type,
device_id=device_id
)
)
async_add_entities(timeList, update_before_add=True)
class PolarisTime(PolarisBaseEntity, TimeEntity):
entity_description: PolarisTimeDescription
def __init__(
self,
device_friendly_name: str,
description: PolarisTimeEntityDescription,
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_available = False
self._attr_has_entity_name = True
self._attr_native_value = time(0, self.entity_description.default_time, 0)
async def async_added_to_hass(self):
@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)
async def async_set_value(self, value: time) -> None:
"""Update the time."""
value_hour=value.hour
value_minute=value.minute
value_second=0
value_in_seconds = value.hour * 3600 + value.minute * 60
if (value_in_seconds > (self.entity_description.max_time * 60)):
value_hour = int(self.entity_description.max_time / 60) - 1
self._attr_native_value = time(value_hour,value_minute,value_second)
value_in_seconds = value_hour * 3600 + value_minute * 60
mqtt.publish(self.hass, self.entity_description.mqttTopicCommandTime, str(value_in_seconds))

View File

@@ -0,0 +1,260 @@
"""The Polaris IQ Home component."""
from __future__ import annotations
import json
import re
import logging
from typing import Iterable
import copy
from homeassistant.components import mqtt
from homeassistant.components.mqtt.models import ReceiveMessage
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
from homeassistant.util import color as color_util
from homeassistant.components.light import (
DOMAIN,
ATTR_BRIGHTNESS,
ATTR_RGB_COLOR,
ColorMode,
LightEntity,
LightEntityDescription,
)
# Import global values.
from .const import (
MANUFACTURER,
MQTT_ROOT_TOPIC,
DEVICEID,
DEVICETYPE,
POLARIS_DEVICE,
LIGHTS,
LIGHTS_DOUBLE,
PolarisLightEntityDescription,
POLARIS_KETTLE_TYPE,
POLARIS_KETTLE_WITH_WEIGHT_TYPE,
POLARIS_KETTLE_WITH_NIGHT_TYPE,
POLARIS_HUMIDDIFIER_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"]
lightList = []
if device_type in POLARIS_KETTLE_WITH_NIGHT_TYPE:
# Create water heater for kettle devices
LIGHTS_LC = copy.deepcopy(LIGHTS)
for description in LIGHTS_LC:
description.mqttTopicCurrentColor = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCurrentColor}"
description.mqttTopicCommandColor = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCommandColor}"
description.mqttTopicCurrentState = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCurrentState}"
description.mqttTopicCommandState = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCommandState}"
description.device_prefix_topic = device_prefix_topic
lightList.append(
PolarisLight(
description=description,
device_friendly_name=device_id,
mqtt_root=mqtt_root,
device_type=device_type,
device_id=device_id
)
)
if device_type == "835":
# Create humidifier double lights
LIGHTS_DOUBLE_LC = copy.deepcopy(LIGHTS_DOUBLE)
for description in LIGHTS_DOUBLE_LC:
description.mqttTopicCurrentColor = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCurrentColor}"
description.mqttTopicCommandColor = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCommandColor}"
description.mqttTopicCurrentState = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCurrentState}"
description.mqttTopicCommandState = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCommandState}"
description.device_prefix_topic = device_prefix_topic
lightList.append(
PolarisLight(
description=description,
device_friendly_name=device_id,
mqtt_root=mqtt_root,
device_type=device_type,
device_id=device_id
)
)
async_add_entities(lightList, update_before_add=True)
class PolarisLight(PolarisBaseEntity, LightEntity):
entity_description: PolarisLightEntityDescription
def __init__(
self,
device_friendly_name: str,
description: PolarisLightEntityDescription,
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_color_mode = ColorMode.RGB
self._attr_supported_color_modes = {ColorMode.RGB}
self._attr_has_entity_name = True
self._attr_rgb_color = [255, 255, 255]
self._attr_brightness = 100
self._attr_is_on = False
self._attr_available = False
self._double_color = [[255, 255, 255],[255, 255, 255]]
self._double_state = ["0","0"]
async def async_added_to_hass(self):
if self.device_type == "835":
@callback
def message_received_double_state(message):
if message.payload[1] == "0" and self.entity_description.key == "night_up":
self._attr_is_on = False
# self._double_state[0] = "0"
elif message.payload[3] == "0" and self.entity_description.key == "night_down":
self._attr_is_on = False
# self._double_state[1] = "0"
elif message.payload[1] in {"1","2","3"} and self.entity_description.key == "night_up":
self._attr_is_on = True
elif message.payload[3] in {"1","2","3"} and self.entity_description.key == "night_down":
self._attr_is_on = True
else:
self._attr_is_on = None
self._double_state[0] = message.payload[1]
self._double_state[1] = message.payload[3]
rgb_up = color_util.rgb_hex_to_rgb_list(message.payload[4:10])
rgb_down = color_util.rgb_hex_to_rgb_list(message.payload[10:])
self._double_color = [rgb_up, rgb_down]
if self.entity_description.key == "night_up":
rgb = rgb_up
else:
rgb = rgb_down
level = int(max(rgb)/255*100)
self._attr_brightness = level
rgb_color = rgb
bright_factor_old = max(rgb)/255
bright_factor_new = level / 100 / bright_factor_old
self._attr_rgb_color = [int(value / bright_factor_new) for value in rgb_color]
self.async_write_ha_state()
await mqtt.async_subscribe(
self.hass,
self.entity_description.mqttTopicCurrentState,
message_received_double_state,
1,
)
else:
@callback
def message_received_rgb(message):
rgb = color_util.rgb_hex_to_rgb_list(message.payload)
level = int(max(rgb)/255*100)
self._attr_brightness = level
if (self.device_type == "176" or self.device_type == "255"):
rgb_color = [rgb[0], rgb[1], rgb[2]]
else:
rgb_color = rgb
bright_factor_old = max(rgb)/255
bright_factor_new = level / 100 / bright_factor_old
self._attr_rgb_color = [int(value / bright_factor_new) for value in rgb_color]
self.async_write_ha_state()
@callback
def message_received_state(message):
if str(message.payload).lower() in ("1", "true"):
self._attr_is_on = True
elif str(message.payload).lower() in ("0", "false"):
self._attr_is_on = False
else:
self._attr_is_on = None
self.async_write_ha_state()
await mqtt.async_subscribe(
self.hass,
self.entity_description.mqttTopicCurrentColor,
message_received_rgb,
1,
)
await mqtt.async_subscribe(
self.hass,
self.entity_description.mqttTopicCurrentState,
message_received_state,
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)
async def async_turn_on(self, **kwargs: Any) -> None:
topic = self.entity_description.mqttTopicCommandColor
if ATTR_RGB_COLOR in kwargs:
color = color_util.color_rgb_to_hex(*kwargs[ATTR_RGB_COLOR])
self._attr_rgb_color = color_util.rgb_hex_to_rgb_list(color)
elif ATTR_BRIGHTNESS in kwargs:
level = int((kwargs.get(ATTR_BRIGHTNESS, 100) * 100) / 255)
self._attr_brightness = level
bright_factor_old = max(self._attr_rgb_color)/255
bright_factor_new = self._attr_brightness/ 100 / bright_factor_old
self._attr_rgb_color = [int(value * bright_factor_new) for value in self._attr_rgb_color]
if (self.device_type == "176" or self.device_type == "255"):
mqtt.publish(self.hass, topic, f"{self._attr_rgb_color[0]:02x}{self._attr_rgb_color[1]:02x}{self._attr_rgb_color[2]:02x}00")
elif self.device_type == "835":
if self.entity_description.key == "night_up":
mqtt.publish(self.hass, topic, f"030{self._double_state[1]}{color_util.color_rgb_to_hex(self._attr_rgb_color[0], self._attr_rgb_color[1],self._attr_rgb_color[2])}{self._double_color[1][0]:02x}{self._double_color[1][1]:02x}{self._double_color[1][2]:02x}")
if self.entity_description.key == "night_down":
mqtt.publish(self.hass, topic, f"0{self._double_state[0]}03{self._double_color[0][0]:02x}{self._double_color[0][1]:02x}{self._double_color[0][2]:02x}{color_util.color_rgb_to_hex(self._attr_rgb_color[0], self._attr_rgb_color[1],self._attr_rgb_color[2])}")
else:
mqtt.publish(self.hass, topic, color_util.color_rgb_to_hex(self._attr_rgb_color[0], self._attr_rgb_color[1],self._attr_rgb_color[2]))
if self.device_type != "835":
topic = self.entity_description.mqttTopicCommandState
mqtt.publish(self.hass, topic, "true")
self._attr_is_on = True
async def async_turn_off(self, **kwargs: Any) -> None:
topic = self.entity_description.mqttTopicCommandState
if self.device_type == "835":
if self.entity_description.key == "night_up":
mess = f"000{self._double_state[1]}{self._double_color[0][0]:02x}{self._double_color[0][1]:02x}{self._double_color[0][2]:02x}{self._double_color[1][0]:02x}{self._double_color[1][1]:02x}{self._double_color[1][2]:02x}"
if self.entity_description.key == "night_down":
mess = f"0{self._double_state[0]}00{self._double_color[0][0]:02x}{self._double_color[0][1]:02x}{self._double_color[0][2]:02x}{self._double_color[1][0]:02x}{self._double_color[1][1]:02x}{self._double_color[1][2]:02x}"
mqtt.publish(self.hass, topic, mess)
else:
mqtt.publish(self.hass, topic, "false")
self._attr_is_on = False
@property
def is_on(self) -> bool:
return self._attr_is_on
@property
def brightness(self) -> int:
return int((self._attr_brightness * 255) / 100)
@property
def rgb_color(self) -> tuple[int, int, int] | None:
return self._attr_rgb_color

View File

@@ -0,0 +1,19 @@
{
"domain": "polaris",
"name": "Polaris IQ Home MQTT",
"codeowners": ["@samoswall"],
"config_flow": true,
"dependencies": ["mqtt"],
"documentation": "https://github.com/samoswall/polaris-mqtt",
"iot_class": "local_push",
"issue_tracker": "https://github.com/samoswall/polaris-mqtt/issues",
"mqtt": [
"polaris/+/state/mac",
"/polaris/+/state/mac",
"polaris/+/+/state/mac",
"/polaris/+/+/state/mac",
"rusclimate/+/+/state/mac",
"/rusclimate/+/+/state/mac"
],
"version": "1.0.13"
}

View File

@@ -0,0 +1,291 @@
"""The Polaris IQ Home component."""
from __future__ import annotations
import json
import re
import logging
from typing import Iterable
import copy
from homeassistant.components import mqtt
from homeassistant.components.mqtt.models import ReceiveMessage
from homeassistant.components.number import NumberEntity, DOMAIN
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
from homeassistant.const import STATE_UNAVAILABLE
# Import global values.
from .const import (
MANUFACTURER,
MQTT_ROOT_TOPIC,
DEVICEID,
DEVICETYPE,
POLARIS_DEVICE,
NUMBER_HUMIDIFIER,
NUMBER_RUSCLIMATE_HUMIDIFIER,
NUMBER_COOKER,
NUMBERS_COFFEEMAKER,
NUMBERS_COFFEEMAKER_ROG,
NUMBERS_AIRCLEANER,
NUMBERS_IRRIGATOR,
NUMBERS_HEATER,
NUMBERS_THERMOSTAT,
PolarisNumberEntityDescription,
POLARIS_KETTLE_TYPE,
POLARIS_KETTLE_WITH_WEIGHT_TYPE,
POLARIS_HUMIDDIFIER_TYPE,
POLARIS_HUMIDDIFIER_LOW_FAN_TYPE,
POLARIS_COOKER_TYPE,
POLARIS_COFFEEMAKER_TYPE,
POLARIS_COFFEEMAKER_ROG_TYPE,
POLARIS_AIRCLEANER_TYPE,
POLARIS_IRRIGATOR_TYPE,
POLARIS_HEATER_TYPE,
POLARIS_THERMOSTAT_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"]
numberList = []
if (device_type in POLARIS_HUMIDDIFIER_TYPE):
# Create humidifier
if device_type in {"835","881"}:
NUMBER_HUMIDIFIER_LC = copy.deepcopy(NUMBER_RUSCLIMATE_HUMIDIFIER)
else:
NUMBER_HUMIDIFIER_LC = copy.deepcopy(NUMBER_HUMIDIFIER)
for description in NUMBER_HUMIDIFIER_LC:
description.mqttTopicCurrent = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCurrent}"
description.mqttTopicCommand = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCommand}"
description.device_prefix_topic = device_prefix_topic
numberList.append(
PolarisNumber(
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):
# Create cooker
NUMBER_COOKER_LC = copy.deepcopy(NUMBER_COOKER)
for description in NUMBER_COOKER_LC:
description.mqttTopicCurrent = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCurrent}"
description.mqttTopicCommand = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCommand}"
description.device_prefix_topic = device_prefix_topic
numberList.append(
PolarisNumber(
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):
# Create cooker
NUMBERS_COFFEEMAKER_LC = copy.deepcopy(NUMBERS_COFFEEMAKER)
for description in NUMBERS_COFFEEMAKER_LC:
# description.mqttTopicCurrent = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCurrent}"
description.mqttTopicCommand = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCommand}"
description.device_prefix_topic = device_prefix_topic
numberList.append(
PolarisNumber(
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):
# Create cooker
NUMBERS_COFFEEMAKER_ROG_LC = copy.deepcopy(NUMBERS_COFFEEMAKER_ROG)
for description in NUMBERS_COFFEEMAKER_ROG_LC:
description.mqttTopicCurrent = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCurrent}"
description.mqttTopicCommand = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCommand}"
description.device_prefix_topic = device_prefix_topic
numberList.append(
PolarisNumber(
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_TYPE):
# Create humidifier
NUMBERS_AIRCLEANER_LC = copy.deepcopy(NUMBERS_AIRCLEANER)
for description in NUMBERS_AIRCLEANER_LC:
description.mqttTopicCurrent = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCurrent}"
description.mqttTopicCommand = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCommand}"
description.device_prefix_topic = device_prefix_topic
numberList.append(
PolarisNumber(
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):
# Create irrigator
NUMBERS_IRRIGATOR_LC = copy.deepcopy(NUMBERS_IRRIGATOR)
for description in NUMBERS_IRRIGATOR_LC:
description.mqttTopicCurrent = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCurrent}"
description.mqttTopicCommand = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCommand}"
description.device_prefix_topic = device_prefix_topic
numberList.append(
PolarisNumber(
description=description,
device_friendly_name=device_id,
mqtt_root=mqtt_root,
device_type=device_type,
device_id=device_id
)
)
if (device_type in POLARIS_HEATER_TYPE):
# Create Heater
NUMBERS_HEATER_LC = copy.deepcopy(NUMBERS_HEATER)
for description in NUMBERS_HEATER_LC:
description.mqttTopicCurrent = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCurrent}"
description.mqttTopicCommand = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCommand}"
description.device_prefix_topic = device_prefix_topic
numberList.append(
PolarisNumber(
description=description,
device_friendly_name=device_id,
mqtt_root=mqtt_root,
device_type=device_type,
device_id=device_id
)
)
if (device_type in POLARIS_THERMOSTAT_TYPE):
# Create Heater
NUMBERS_THERMOSTAT_LC = copy.deepcopy(NUMBERS_THERMOSTAT)
for description in NUMBERS_THERMOSTAT_LC:
description.mqttTopicCurrent = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCurrent}"
description.mqttTopicCommand = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCommand}"
description.device_prefix_topic = device_prefix_topic
numberList.append(
PolarisNumber(
description=description,
device_friendly_name=device_id,
mqtt_root=mqtt_root,
device_type=device_type,
device_id=device_id
)
)
async_add_entities(numberList, update_before_add=True)
class PolarisNumber(PolarisBaseEntity, NumberEntity):
entity_description: PolarisNumberEntityDescription
def __init__(
self,
device_friendly_name: str,
description: PolarisNumberEntityDescription,
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_available = False
self._attr_has_entity_name = True
self._attr_native_value = self.entity_description.native_value
if POLARIS_DEVICE[int(self.device_type)]['class'] == "coffeemaker":
if self.entity_description.name != "display_time":
self._attr_native_value = STATE_UNAVAILABLE
if self.device_type in POLARIS_HUMIDDIFIER_LOW_FAN_TYPE:
self._attr_native_max_value = 3
if self.device_type in POLARIS_THERMOSTAT_TYPE:
self._data_zero = "00000000000000000000000000000000000000"
def set_native_value(self, value: float) -> None:
if value % 1 > 0:
self._attr_native_value = STATE_UNAVAILABLE
elif ((self.entity_description.key == "display_time") or
(self.entity_description.key == "temperature_difference_antifrost") or
(self.entity_description.key == "temperature_difference_eco")):
self._attr_native_value = int(value)
mqtt.publish(self.hass, self.entity_description.mqttTopicCommand, f"{int(value):02x}", 0, True)
elif self.entity_description.name == "time_timer":
self._attr_native_value = int(value)
mqtt.publish(self.hass, self.entity_description.mqttTopicCommand, int(value)*3600)
elif self.entity_description.translation_key == "power_cable":
self._attr_native_value = int(value)
mqtt.publish(self.hass, self.entity_description.mqttTopicCommand, f"{int(value):04x}"[-2:]+f"{int(value):04x}"[:2])
elif self.entity_description.translation_key == "bright_backlight":
self._attr_native_value = int(value)
mqtt.publish(self.hass, self.entity_description.mqttTopicCommand, f"{self._data_zero[:2]}{hex(int(value))[2:]}{self._data_zero[4:]}")
else:
self._attr_native_value = int(value)
mqtt.publish(self.hass, self.entity_description.mqttTopicCommand, int(value))
@property
def get_state (self) -> int | None:
return self._attr_native_value
async def async_added_to_hass(self):
@callback
def message_received_numb(message):
if self.entity_description.name == "display_time":
self._attr_native_value = int(message.payload, 16)
elif self.entity_description.name == "time_timer":
self._attr_native_value = int(int(message.payload) / 3600)
elif self.entity_description.translation_key == "bright_backlight":
self._data_zero = message.payload
self._attr_native_value = int(self._data_zero[2:4], 16)
elif self.entity_description.translation_key == "power_cable":
self._attr_native_value = int(message.payload[:2],16) + (int(message.payload[-2:],16)*256)
else:
if self.entity_description.native_min_value <= int(message.payload):
self._attr_native_value = message.payload
self.async_write_ha_state()
await mqtt.async_subscribe(
self.hass,
self.entity_description.mqttTopicCurrent,
message_received_numb,
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)

View File

@@ -0,0 +1,482 @@
"""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)

View File

@@ -0,0 +1,529 @@
"""The Polaris IQ Home component."""
from __future__ import annotations
import copy
import json
import logging
import struct
from homeassistant.components import mqtt
from homeassistant.components.sensor import DOMAIN, SensorEntity
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.device_registry import async_get as async_get_dev_reg
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.util import slugify
from .common import PolarisBaseEntity
# Import global values.
from .const import (
MANUFACTURER,
MQTT_ROOT_TOPIC,
DEVICEID,
DEVICETYPE,
POLARIS_DEVICE,
SENSORS_ALL_DEVICES,
SENSORS_WEIGHT,
SENSORS_HUMIDIFIER,
SENSORS_RUSCLIMATE_HUMIDIFIER,
SENSORS_COOKER,
SENSORS_COFFEEMAKER,
SENSORS_COFFEEMAKER_ROG,
SENSORS_CLIMATE,
SENSORS_CLIMATE_200,
SENSORS_AIRCLEANER,
SENSORS_AIRCLEANER_EAP,
SENSORS_VACUUM,
SENSORS_WATER_BOILER,
SENSORS_IRRIGATOR,
SENSORS_HEATER,
SENSORS_AIRCONDITIONER,
SENSORS_THERMOSTAT,
PolarisSensorEntityDescription,
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_AIRCLEANER_TYPE,
POLARIS_AIRCLEANER_EAP_TYPE,
POLARIS_VACUUM_TYPE,
POLARIS_BOILER_TYPE,
POLARIS_IRRIGATOR_TYPE,
POLARIS_HEATER_TYPE,
POLARIS_AIRCONDITIONER_TYPE,
POLARIS_THERMOSTAT_TYPE,
KETTLE_ERROR,
HUMIDDIFIER_ERROR,
COOKER_ERROR,
COFFEEMAKER_ERROR,
AIRCLEANER_ERROR,
VACUUM_ERROR,
)
_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
mqttRoot = config.data[MQTT_ROOT_TOPIC]
deviceID = config.data["DEVICEID"]
devicetype = config.data[DEVICETYPE]
device_prefix_topic = config.data["DEVPREFIXTOPIC"]
if len(device_prefix_topic)>15:
mqtt.publish(hass, f"{mqttRoot}/{device_prefix_topic}/state/devtype", devicetype, 0, True)
sensorList = []
#Kettle
if (devicetype in POLARIS_KETTLE_TYPE):
# Create sensors for all devices
SENSORS_ALL_DEVICES_CP = copy.deepcopy(SENSORS_ALL_DEVICES)
for description in SENSORS_ALL_DEVICES_CP:
description.mqttTopicCurrentValue = (f"{mqttRoot}/{device_prefix_topic}/state/{description.key}")
description.device_prefix_topic = device_prefix_topic
sensorList.append(
PolarisSensor(
description=description,
device_friendly_name=deviceID,
mqtt_root=mqttRoot,
device_type=devicetype,
device_id=deviceID,
)
)
#Kettle with weight
if (devicetype in POLARIS_KETTLE_WITH_WEIGHT_TYPE):
# Create sensors for all devices
SENSORS_ALL_DEVICES_CP = copy.deepcopy(SENSORS_ALL_DEVICES)
for description in SENSORS_ALL_DEVICES_CP:
description.mqttTopicCurrentValue = (f"{mqttRoot}/{device_prefix_topic}/state/{description.key}")
description.device_prefix_topic = device_prefix_topic
sensorList.append(
PolarisSensor(
description=description,
device_friendly_name=deviceID,
mqtt_root=mqttRoot,
device_type=devicetype,
device_id=deviceID,
)
)
SENSORS_WEIGHT_CP = copy.deepcopy(SENSORS_WEIGHT)
for description in SENSORS_WEIGHT_CP:
description.mqttTopicCurrentValue = (f"{mqttRoot}/{device_prefix_topic}/state/{description.key}")
description.device_prefix_topic = device_prefix_topic
sensorList.append(
PolarisSensor(
description=description,
device_friendly_name=deviceID,
mqtt_root=mqttRoot,
device_type=devicetype,
device_id=deviceID,
)
)
# Humidifier
if (devicetype in POLARIS_HUMIDDIFIER_TYPE):
if devicetype == "881":
SENSORS_RUSCLIMATE_HUMIDIFIER_CP = copy.deepcopy(SENSORS_RUSCLIMATE_HUMIDIFIER)
for description in SENSORS_RUSCLIMATE_HUMIDIFIER_CP:
description.mqttTopicCurrentValue = (f"{mqttRoot}/{device_prefix_topic}/state/{description.key}")
description.device_prefix_topic = device_prefix_topic
sensorList.append(
PolarisSensor(
description=description,
device_friendly_name=deviceID,
mqtt_root=mqttRoot,
device_type=devicetype,
device_id=deviceID,
)
)
else:
# Create sensors for all devices
SENSORS_ALL_DEVICES_CP = copy.deepcopy(SENSORS_ALL_DEVICES)
for description in SENSORS_ALL_DEVICES_CP:
description.mqttTopicCurrentValue = (f"{mqttRoot}/{device_prefix_topic}/state/{description.key}")
description.device_prefix_topic = device_prefix_topic
sensorList.append(
PolarisSensor(
description=description,
device_friendly_name=deviceID,
mqtt_root=mqttRoot,
device_type=devicetype,
device_id=deviceID,
)
)
SENSORS_HUMIDIFIER_CP = copy.deepcopy(SENSORS_HUMIDIFIER)
for description in SENSORS_HUMIDIFIER_CP:
if (devicetype != "835" or description.translation_key != "clean_retain"):
description.mqttTopicCurrentValue = (f"{mqttRoot}/{device_prefix_topic}/state/{description.key}")
description.device_prefix_topic = device_prefix_topic
sensorList.append(
PolarisSensor(
description=description,
device_friendly_name=deviceID,
mqtt_root=mqttRoot,
device_type=devicetype,
device_id=deviceID,
)
)
# Cooker
if (devicetype in POLARIS_COOKER_TYPE):
# Create sensors for all devices
SENSORS_ALL_DEVICES_CP = copy.deepcopy(SENSORS_ALL_DEVICES)
for description in SENSORS_ALL_DEVICES_CP:
description.mqttTopicCurrentValue = (f"{mqttRoot}/{device_prefix_topic}/state/{description.key}")
description.device_prefix_topic = device_prefix_topic
sensorList.append(
PolarisSensor(
description=description,
device_friendly_name=deviceID,
mqtt_root=mqttRoot,
device_type=devicetype,
device_id=deviceID,
)
)
SENSORS_COOKER_CP = copy.deepcopy(SENSORS_COOKER)
for description in SENSORS_COOKER_CP:
description.mqttTopicCurrentValue = (f"{mqttRoot}/{device_prefix_topic}/state/{description.key}")
description.device_prefix_topic = device_prefix_topic
sensorList.append(
PolarisSensor(
description=description,
device_friendly_name=deviceID,
mqtt_root=mqttRoot,
device_type=devicetype,
device_id=deviceID,
)
)
# Coffeemaker
if (devicetype in POLARIS_COFFEEMAKER_TYPE):
# Create sensors for all devices
SENSORS_ALL_DEVICES_CP = copy.deepcopy(SENSORS_ALL_DEVICES)
for description in SENSORS_ALL_DEVICES_CP:
description.mqttTopicCurrentValue = (f"{mqttRoot}/{device_prefix_topic}/state/{description.key}")
description.device_prefix_topic = device_prefix_topic
sensorList.append(
PolarisSensor(
description=description,
device_friendly_name=deviceID,
mqtt_root=mqttRoot,
device_type=devicetype,
device_id=deviceID,
)
)
SENSORS_COFFEEMAKER_CP = copy.deepcopy(SENSORS_COFFEEMAKER)
for description in SENSORS_COFFEEMAKER_CP:
description.mqttTopicCurrentValue = (f"{mqttRoot}/{device_prefix_topic}/state/{description.key}")
description.device_prefix_topic = device_prefix_topic
sensorList.append(
PolarisSensor(
description=description,
device_friendly_name=deviceID,
mqtt_root=mqttRoot,
device_type=devicetype,
device_id=deviceID,
)
)
if (devicetype in POLARIS_COFFEEMAKER_ROG_TYPE):
# Create sensors for coffeemaker
SENSORS_COFFEEMAKER_ROG_CP = copy.deepcopy(SENSORS_COFFEEMAKER_ROG)
for description in SENSORS_COFFEEMAKER_ROG_CP:
description.mqttTopicCurrentValue = (f"{mqttRoot}/{device_prefix_topic}/state/{description.key}")
description.device_prefix_topic = device_prefix_topic
sensorList.append(
PolarisSensor(
description=description,
device_friendly_name=deviceID,
mqtt_root=mqttRoot,
device_type=devicetype,
device_id=deviceID,
)
)
if (devicetype in POLARIS_CLIMATE_TYPE):
# Create sensors for climate asp-200 or asp-100
if (devicetype == "859"):
SENSORS_CLIMATE_CP = copy.deepcopy(SENSORS_CLIMATE_200)
else:
SENSORS_CLIMATE_CP = copy.deepcopy(SENSORS_CLIMATE)
for description in SENSORS_CLIMATE_CP:
description.mqttTopicCurrentValue = (f"{mqttRoot}/{device_prefix_topic}/state/{description.key}")
description.device_prefix_topic = device_prefix_topic
sensorList.append(
PolarisSensor(
description=description,
device_friendly_name=deviceID,
mqtt_root=mqttRoot,
device_type=devicetype,
device_id=deviceID,
)
)
SENSORS_ALL_DEVICES_CP = copy.deepcopy(SENSORS_ALL_DEVICES)
for description in SENSORS_ALL_DEVICES_CP:
description.mqttTopicCurrentValue = (f"{mqttRoot}/{device_prefix_topic}/state/{description.key}")
description.device_prefix_topic = device_prefix_topic
sensorList.append(
PolarisSensor(
description=description,
device_friendly_name=deviceID,
mqtt_root=mqttRoot,
device_type=devicetype,
device_id=deviceID,
)
)
if (devicetype in POLARIS_AIRCLEANER_TYPE):
SENSORS_AIRCLEANER_CP = copy.deepcopy(SENSORS_AIRCLEANER)
for description in SENSORS_AIRCLEANER_CP:
description.mqttTopicCurrentValue = (f"{mqttRoot}/{device_prefix_topic}/state/{description.key}")
description.device_prefix_topic = device_prefix_topic
sensorList.append(
PolarisSensor(
description=description,
device_friendly_name=deviceID,
mqtt_root=mqttRoot,
device_type=devicetype,
device_id=deviceID,
)
)
if (devicetype in POLARIS_AIRCLEANER_EAP_TYPE):
SENSORS_AIRCLEANER_EAP_CP = copy.deepcopy(SENSORS_AIRCLEANER_EAP)
for description in SENSORS_AIRCLEANER_EAP_CP:
description.mqttTopicCurrentValue = (f"{mqttRoot}/{device_prefix_topic}/state/{description.key}")
description.device_prefix_topic = device_prefix_topic
sensorList.append(
PolarisSensor(
description=description,
device_friendly_name=deviceID,
mqtt_root=mqttRoot,
device_type=devicetype,
device_id=deviceID,
)
)
if (devicetype in POLARIS_VACUUM_TYPE):
SENSORS_VACUUM_CP = copy.deepcopy(SENSORS_VACUUM)
for description in SENSORS_VACUUM_CP:
description.mqttTopicCurrentValue = (f"{mqttRoot}/{device_prefix_topic}/state/{description.key}")
description.device_prefix_topic = device_prefix_topic
sensorList.append(
PolarisSensor(
description=description,
device_friendly_name=deviceID,
mqtt_root=mqttRoot,
device_type=devicetype,
device_id=deviceID,
)
)
if (devicetype in POLARIS_BOILER_TYPE):
SENSORS_WATER_BOILER_CP = copy.deepcopy(SENSORS_WATER_BOILER)
for description in SENSORS_WATER_BOILER_CP:
if (devicetype not in {"833","802"} or description.translation_key != "anode_retain"):
description.mqttTopicCurrentValue = (f"{mqttRoot}/{device_prefix_topic}/state/{description.key}")
description.device_prefix_topic = device_prefix_topic
sensorList.append(
PolarisSensor(
description=description,
device_friendly_name=deviceID,
mqtt_root=mqttRoot,
device_type=devicetype,
device_id=deviceID,
)
)
if (devicetype in POLARIS_IRRIGATOR_TYPE):
SENSORS_IRRIGATOR_CP = copy.deepcopy(SENSORS_IRRIGATOR)
for description in SENSORS_IRRIGATOR_CP:
description.mqttTopicCurrentValue = (f"{mqttRoot}/{device_prefix_topic}/state/{description.key}")
description.device_prefix_topic = device_prefix_topic
sensorList.append(
PolarisSensor(
description=description,
device_friendly_name=deviceID,
mqtt_root=mqttRoot,
device_type=devicetype,
device_id=deviceID,
)
)
if (devicetype in POLARIS_HEATER_TYPE):
SENSORS_HEATER_CP = copy.deepcopy(SENSORS_HEATER)
for description in SENSORS_HEATER_CP:
description.mqttTopicCurrentValue = (f"{mqttRoot}/{device_prefix_topic}/state/{description.key}")
description.device_prefix_topic = device_prefix_topic
sensorList.append(
PolarisSensor(
description=description,
device_friendly_name=deviceID,
mqtt_root=mqttRoot,
device_type=devicetype,
device_id=deviceID,
)
)
if (devicetype in POLARIS_AIRCONDITIONER_TYPE):
SENSORS_AIRCONDITIONER_CP = copy.deepcopy(SENSORS_AIRCONDITIONER)
for description in SENSORS_AIRCONDITIONER_CP:
description.mqttTopicCurrentValue = (f"{mqttRoot}/{device_prefix_topic}/state/{description.key}")
description.device_prefix_topic = device_prefix_topic
sensorList.append(
PolarisSensor(
description=description,
device_friendly_name=deviceID,
mqtt_root=mqttRoot,
device_type=devicetype,
device_id=deviceID,
)
)
if (devicetype in POLARIS_THERMOSTAT_TYPE):
SENSORS_ALL_DEVICES_CP = copy.deepcopy(SENSORS_ALL_DEVICES)
for description in SENSORS_ALL_DEVICES_CP:
description.mqttTopicCurrentValue = (f"{mqttRoot}/{device_prefix_topic}/state/{description.key}")
description.device_prefix_topic = device_prefix_topic
sensorList.append(
PolarisSensor(
description=description,
device_friendly_name=deviceID,
mqtt_root=mqttRoot,
device_type=devicetype,
device_id=deviceID,
)
)
SENSORS_THERMOSTAT_CP = copy.deepcopy(SENSORS_THERMOSTAT)
for description in SENSORS_THERMOSTAT_CP:
description.mqttTopicCurrentValue = (f"{mqttRoot}/{device_prefix_topic}/state/{description.key}")
description.device_prefix_topic = device_prefix_topic
sensorList.append(
PolarisSensor(
description=description,
device_friendly_name=deviceID,
mqtt_root=mqttRoot,
device_type=devicetype,
device_id=deviceID,
)
)
async_add_entities(sensorList)
class PolarisSensor(PolarisBaseEntity, SensorEntity):
entity_description: PolarisSensorEntityDescription
def __init__(
self,
# uniqueID: str | None,
device_friendly_name: str,
mqtt_root: str,
description: PolarisSensorEntityDescription,
device_type: str,
device_id: str,
) -> 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
self._attr_available = False
def bytes_to_int16_array(self, byte_data, byteorder='little'):
"""
Преобразует байтовую строку в массив int16.
Аргументы:
byte_data: Байтовая строка для преобразования (bytes).
byteorder: Порядок байтов ('little' или 'big'), по умолчанию 'little'.
Возвращает:
Массив int16 значений (list of int).
Вызывает исключение ValueError:
Если длина byte_data нечетная.
Если byteorder не является 'little' или 'big'.
"""
if len(byte_data) % 2 != 0:
raise ValueError("Длина байтовой строки должна быть четной для int16 преобразования.")
if byteorder not in ('little', 'big'):
raise ValueError("Неверный порядок байтов. Допустимые значения: 'little', 'big'.")
endian_prefix = '<' if byteorder == 'little' else '>' # '<' - little-endian, '>' - big-endian
format_string = endian_prefix + 'h' * (len(byte_data) // 2) # 'h' - signed short (2 bytes - int16)
return list(struct.unpack(format_string, byte_data))
async def async_added_to_hass(self):
@callback
def message_received(message):
payload_message = message.payload
if self.entity_description.name == "error":
if POLARIS_DEVICE[int(self.device_type)]['class'] == "cooker":
payload_message = COOKER_ERROR[payload_message]
if POLARIS_DEVICE[int(self.device_type)]['class'] == "kettle":
payload_message = KETTLE_ERROR[payload_message]
if POLARIS_DEVICE[int(self.device_type)]['class'] == "humidifier":
payload_message = HUMIDDIFIER_ERROR[payload_message]
if POLARIS_DEVICE[int(self.device_type)]['class'] == "coffeemaker":
payload_message = COFFEEMAKER_ERROR[payload_message]
if POLARIS_DEVICE[int(self.device_type)]['class'] == "air_cleaner":
payload_message = AIRCLEANER_ERROR[payload_message]
if POLARIS_DEVICE[int(self.device_type)]['class'] == "cleaner":
payload_message = VACUUM_ERROR[payload_message]
if self.entity_description.name == "filter_retain":
payload_message = payload_message.replace("[","",1).replace("]","",1).split(",")[0]
if self.entity_description.name == "pre_filter_retain":
payload_message = payload_message.replace("[","",1).replace("]","",1).split(",")[1]
if self.entity_description.name == "anode_retain":
payload_message = payload_message.replace("[","",1).replace("]","",1).split(",")[0]
if self.entity_description.name == "clean_retain":
payload_message = payload_message.replace("[","",1).replace("]","",1).split(",")[1]
if self.entity_description.name == "mode":
payload_message = self.entity_description.valueMap[payload_message]
if self.entity_description.name == "power_state":
payload_message = self.entity_description.valueMap[payload_message]
if self.entity_description.name == "go_area":
# _LOGGER.debug("go_area %s", payload_message)
list_dubleint = self.bytes_to_int16_array(payload_message)
# _LOGGER.debug("bytes_to_int16 %s",list_dubleint)
# list_dubleint = self.bytes_to_int16_array(payload_message, byteorder='big')
# _LOGGER.debug("list_integers %s",list_dubleint)
payload_message = list_dubleint
if self.entity_description.name == "quality":
payload_message = str( int(payload_message) / 100 )
if self.entity_description.name == "сurrent_power":
if self.device_type == "806":
payload_message = str( int(payload_message[:2],16) * 20 )
else:
payload_message = str( int(payload_message[:2],16) * 10 )
self._attr_native_value = payload_message
self.async_write_ha_state()
if self.entity_description.name == "go_area":
await mqtt.async_subscribe(
self.hass,
self.entity_description.mqttTopicCurrentValue,
message_received,
1,
None,
)
else:
await mqtt.async_subscribe(
self.hass,
self.entity_description.mqttTopicCurrentValue,
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)

View File

@@ -0,0 +1,15 @@
select_rooms:
target:
entity:
integration: "polaris"
domain: "vacuum"
name: Select rooms
description: Select rooms to clean
fields:
select_rooms:
name: Rooms
description: Input of available rooms to clean
required: true
example: Kitchen
selector:
text:

View File

@@ -0,0 +1,752 @@
"""The Polaris IQ Home component."""
from __future__ import annotations
import json
import re
import logging
from typing import Iterable
import copy
from homeassistant.components import mqtt
from homeassistant.components.switch import DOMAIN, SwitchEntity
#from homeassistant.helpers.restore_state import RestoreEntity
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 homeassistant.helpers.device_registry import async_get as async_get_dev_reg
from .common import PolarisBaseEntity
# Import global values.
from .const import (
MANUFACTURER,
MQTT_ROOT_TOPIC,
DEVICEID,
DEVICETYPE,
POLARIS_DEVICE,
SWITCHES_ALL_DEVICES,
SWITCH_KETTLE_BACKLIGHT,
SWITCH_HUMIDIFIER_BACKLIGHT,
SWITCH_HUMIDIFIER_IONISER,
SWITCH_HUMIDIFIER_WARM_STREAM,
SWITCH_HUMIDIFIER_ULTRAVIOLET,
SWITCHES_RUSCLIMATE_HUMIDIFIER,
SWITCHES_COOKER,
SWITCHES_COFFEEMAKER,
SWITCHES_COFFEEMAKER_ROG,
SWITCHES_CLIMATE,
SWITCHES_CLIMATE_200,
SWITCHES_AIRCLEANER,
SWITCHES_AIRCLEANER_EAP,
SWITCHES_VACUUM,
SWITCHES_WATER_BOILER,
SWITCHES_WATER_BOILER_NO_FROST,
SWITCHES_WATER_BOILER_BACKLIGHT,
SWITCHES_IRRIGATOR,
SWITCHES_HEATER,
SWITCHES_AIRCONDITIONER,
SWITCHES_AIRCONDITIONER_820,
SWITCHES_AIRCONDITIONER_882,
SWITCHES_THERMOSTAT,
PolarisSwitchEntityDescription,
POLARIS_KETTLE_TYPE,
POLARIS_KETTLE_WITH_WEIGHT_TYPE,
POLARIS_KETTLE_WITH_BACKLIGHT_TYPE,
POLARIS_HUMIDDIFIER_TYPE,
POLARIS_HUMIDDIFIER_WITH_IONISER_TYPE,
POLARIS_HUMIDDIFIER_WITH_WARM_STREAM_TYPE,
POLARIS_COOKER_TYPE,
POLARIS_COFFEEMAKER_TYPE,
POLARIS_COFFEEMAKER_ROG_TYPE,
POLARIS_CLIMATE_TYPE,
POLARIS_AIRCLEANER_TYPE,
POLARIS_AIRCLEANER_EAP_TYPE,
POLARIS_VACUUM_TYPE,
POLARIS_BOILER_TYPE,
POLARIS_IRRIGATOR_TYPE,
POLARIS_HEATER_TYPE,
POLARIS_AIRCONDITIONER_TYPE,
POLARIS_THERMOSTAT_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"]
switchList = []
if (device_type in POLARIS_KETTLE_TYPE) or (device_type in POLARIS_KETTLE_WITH_WEIGHT_TYPE):
# Create sensors for all devices
SWITCHES_ALL_DEVICES_LC = copy.deepcopy(SWITCHES_ALL_DEVICES)
for description in SWITCHES_ALL_DEVICES_LC:
description.mqttTopicCommand = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCommand}"
description.mqttTopicCurrentValue = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCurrentValue}"
description.device_prefix_topic = device_prefix_topic
switchList.append(
PolarisSwitch(
description=description,
device_friendly_name=device_id,
mqtt_root=mqtt_root,
device_type=device_type,
device_id=device_id
)
)
if device_type in POLARIS_KETTLE_WITH_BACKLIGHT_TYPE:
# Create sensors for backlight devices
SWITCH_KETTLE_BACKLIGHT_LC = copy.deepcopy(SWITCH_KETTLE_BACKLIGHT)
for description in SWITCH_KETTLE_BACKLIGHT_LC:
description.mqttTopicCommand = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCommand}"
description.mqttTopicCurrentValue = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCurrentValue}"
description.device_prefix_topic = device_prefix_topic
switchList.append(
PolarisSwitch(
description=description,
device_friendly_name=device_id,
mqtt_root=mqtt_root,
device_type=device_type,
device_id=device_id
)
)
if (device_type in POLARIS_HUMIDDIFIER_TYPE):
if device_type == "881":
SWITCHES_RUSCLIMATE_HUMIDIFIER_LC = copy.deepcopy(SWITCHES_RUSCLIMATE_HUMIDIFIER)
for description in SWITCHES_RUSCLIMATE_HUMIDIFIER_LC:
description.mqttTopicCommand = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCommand}"
description.mqttTopicCurrentValue = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCurrentValue}"
description.device_prefix_topic = device_prefix_topic
switchList.append(
PolarisSwitch(
description=description,
device_friendly_name=device_id,
mqtt_root=mqtt_root,
device_type=device_type,
device_id=device_id
)
)
else:
# Create switches for all devices
SWITCHES_ALL_DEVICES_LC = copy.deepcopy(SWITCHES_ALL_DEVICES)
for description in SWITCHES_ALL_DEVICES_LC:
if (device_type != "835" or description.translation_key != "child_lock_switch"):
description.mqttTopicCommand = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCommand}"
description.mqttTopicCurrentValue = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCurrentValue}"
description.device_prefix_topic = device_prefix_topic
switchList.append(
PolarisSwitch(
description=description,
device_friendly_name=device_id,
mqtt_root=mqtt_root,
device_type=device_type,
device_id=device_id
)
)
SWITCH_HUMIDIFIER_BACKLIGHT_LC = copy.deepcopy(SWITCH_HUMIDIFIER_BACKLIGHT)
for description in SWITCH_HUMIDIFIER_BACKLIGHT_LC:
if (device_type != "835" or description.translation_key != "backlight_switch"):
description.mqttTopicCommand = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCommand}"
description.mqttTopicCurrentValue = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCurrentValue}"
description.device_prefix_topic = device_prefix_topic
switchList.append(
PolarisSwitch(
description=description,
device_friendly_name=device_id,
mqtt_root=mqtt_root,
device_type=device_type,
device_id=device_id
)
)
if (device_type in POLARIS_HUMIDDIFIER_WITH_IONISER_TYPE):
# Create switch ioniser for humidifiers
SWITCH_HUMIDIFIER_IONISER_LC = copy.deepcopy(SWITCH_HUMIDIFIER_IONISER)
for description in SWITCH_HUMIDIFIER_IONISER_LC:
description.mqttTopicCommand = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCommand}"
description.mqttTopicCurrentValue = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCurrentValue}"
description.device_prefix_topic = device_prefix_topic
switchList.append(
PolarisSwitch(
description=description,
device_friendly_name=device_id,
mqtt_root=mqtt_root,
device_type=device_type,
device_id=device_id
)
)
if (device_type in POLARIS_HUMIDDIFIER_WITH_WARM_STREAM_TYPE):
# Create switch stream warm for humidifiers
SWITCH_HUMIDIFIER_WARM_STREAM_LC = copy.deepcopy(SWITCH_HUMIDIFIER_WARM_STREAM)
for description in SWITCH_HUMIDIFIER_WARM_STREAM_LC:
description.mqttTopicCommand = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCommand}"
description.mqttTopicCurrentValue = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCurrentValue}"
description.device_prefix_topic = device_prefix_topic
switchList.append(
PolarisSwitch(
description=description,
device_friendly_name=device_id,
mqtt_root=mqtt_root,
device_type=device_type,
device_id=device_id
)
)
if (device_type in {"835","157"}):
# Create switch stream warm for humidifiers
SWITCH_HUMIDIFIER_ULTRAVIOLET_LC = copy.deepcopy(SWITCH_HUMIDIFIER_ULTRAVIOLET)
for description in SWITCH_HUMIDIFIER_ULTRAVIOLET_LC:
description.mqttTopicCommand = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCommand}"
description.mqttTopicCurrentValue = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCurrentValue}"
description.device_prefix_topic = device_prefix_topic
switchList.append(
PolarisSwitch(
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):
# Create switches for cooker
SWITCHES_COOKER_LC = copy.deepcopy(SWITCHES_COOKER)
for description in SWITCHES_COOKER_LC:
description.mqttTopicCommand = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCommand}"
description.mqttTopicCurrentValue = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCurrentValue}"
description.device_prefix_topic = device_prefix_topic
switchList.append(
PolarisSwitch(
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):
# Create switches for coffeemaker
SWITCHES_COFFEEMAKER_LC = copy.deepcopy(SWITCHES_COFFEEMAKER)
for description in SWITCHES_COFFEEMAKER_LC:
description.mqttTopicCommand = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCommand}"
description.mqttTopicCurrentValue = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCurrentValue}"
description.device_prefix_topic = device_prefix_topic
switchList.append(
PolarisSwitch(
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):
# Create switches for coffeemaker
SWITCHES_COFFEEMAKER_ROG_LC = copy.deepcopy(SWITCHES_COFFEEMAKER_ROG)
for description in SWITCHES_COFFEEMAKER_ROG_LC:
description.mqttTopicCommand = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCommand}"
description.mqttTopicCurrentValue = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCurrentValue}"
description.device_prefix_topic = device_prefix_topic
switchList.append(
PolarisSwitch(
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):
# Create switches for climate
SWITCHES_CLIMATE_LC = copy.deepcopy(SWITCHES_CLIMATE)
for description in SWITCHES_CLIMATE_LC:
description.mqttTopicCommand = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCommand}"
description.mqttTopicCurrentValue = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCurrentValue}"
description.device_prefix_topic = device_prefix_topic
switchList.append(
PolarisSwitch(
description=description,
device_friendly_name=device_id,
mqtt_root=mqtt_root,
device_type=device_type,
device_id=device_id
)
)
if (device_type == "859"):
SWITCHES_CLIMATE_200_LC = copy.deepcopy(SWITCHES_CLIMATE_200)
for description in SWITCHES_CLIMATE_200_LC:
description.mqttTopicCommand = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCommand}"
description.mqttTopicCurrentValue = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCurrentValue}"
description.device_prefix_topic = device_prefix_topic
switchList.append(
PolarisSwitch(
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_TYPE):
# Create switches for all devices
SWITCHES_ALL_DEVICES_LC = copy.deepcopy(SWITCHES_ALL_DEVICES)
for description in SWITCHES_ALL_DEVICES_LC:
description.mqttTopicCommand = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCommand}"
description.mqttTopicCurrentValue = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCurrentValue}"
description.device_prefix_topic = device_prefix_topic
switchList.append(
PolarisSwitch(
description=description,
device_friendly_name=device_id,
mqtt_root=mqtt_root,
device_type=device_type,
device_id=device_id
)
)
SWITCHES_AIRCLEANER_LC = copy.deepcopy(SWITCHES_AIRCLEANER)
for description in SWITCHES_AIRCLEANER_LC:
description.mqttTopicCommand = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCommand}"
description.mqttTopicCurrentValue = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCurrentValue}"
description.device_prefix_topic = device_prefix_topic
switchList.append(
PolarisSwitch(
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):
SWITCHES_AIRCLEANER_EAP_LC = copy.deepcopy(SWITCHES_AIRCLEANER_EAP)
for description in SWITCHES_AIRCLEANER_EAP_LC:
description.mqttTopicCommand = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCommand}"
description.mqttTopicCurrentValue = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCurrentValue}"
description.device_prefix_topic = device_prefix_topic
switchList.append(
PolarisSwitch(
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):
# Create switches for vacuum
SWITCHES_VACUUM_LC = copy.deepcopy(SWITCHES_VACUUM)
for description in SWITCHES_VACUUM_LC:
description.mqttTopicCommand = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCommand}"
description.mqttTopicCurrentValue = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCurrentValue}"
description.device_prefix_topic = device_prefix_topic
switchList.append(
PolarisSwitch(
description=description,
device_friendly_name=device_id,
mqtt_root=mqtt_root,
device_type=device_type,
device_id=device_id
)
)
if (device_type in POLARIS_BOILER_TYPE):
# Create switches for boiler
SWITCHES_WATER_BOILER_LC = copy.deepcopy(SWITCHES_WATER_BOILER)
for description in SWITCHES_WATER_BOILER_LC:
if (device_type not in {"802","833","844"} or description.translation_key != "child_lock_switch") and (device_type != "833" or description.translation_key != "smart_mode"):
description.mqttTopicCommand = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCommand}"
description.mqttTopicCurrentValue = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCurrentValue}"
description.device_prefix_topic = device_prefix_topic
switchList.append(
PolarisSwitch(
description=description,
device_friendly_name=device_id,
mqtt_root=mqtt_root,
device_type=device_type,
device_id=device_id
)
)
if (device_type == "844"):
# Create switches for boiler bright 50%
SWITCHES_WATER_BOILER_BACKLIGHT_LC = copy.deepcopy(SWITCHES_WATER_BOILER_BACKLIGHT)
for description in SWITCHES_WATER_BOILER_BACKLIGHT_LC:
description.mqttTopicCommand = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCommand}"
description.mqttTopicCurrentValue = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCurrentValue}"
description.device_prefix_topic = device_prefix_topic
switchList.append(
PolarisSwitch(
description=description,
device_friendly_name=device_id,
mqtt_root=mqtt_root,
device_type=device_type,
device_id=device_id
)
)
if (device_type == "802"):
# Create switches for boiler no frost
SWITCHES_WATER_BOILER_NO_FROST_LC = copy.deepcopy(SWITCHES_WATER_BOILER_NO_FROST)
for description in SWITCHES_WATER_BOILER_NO_FROST_LC:
description.mqttTopicCommand = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCommand}"
description.mqttTopicCurrentValue = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCurrentValue}"
description.device_prefix_topic = device_prefix_topic
switchList.append(
PolarisSwitch(
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):
# Create switches for irrigator
SWITCHES_IRRIGATOR_LC = copy.deepcopy(SWITCHES_IRRIGATOR)
for description in SWITCHES_IRRIGATOR_LC:
description.mqttTopicCommand = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCommand}"
description.mqttTopicCurrentValue = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCurrentValue}"
description.device_prefix_topic = device_prefix_topic
switchList.append(
PolarisSwitch(
description=description,
device_friendly_name=device_id,
mqtt_root=mqtt_root,
device_type=device_type,
device_id=device_id
)
)
if (device_type in POLARIS_HEATER_TYPE):
# Create switches for heater
SWITCHES_HEATER_LC = copy.deepcopy(SWITCHES_HEATER)
for description in SWITCHES_HEATER_LC:
if (device_type not in {"806","847"} or description.translation_key != "half_power_heater"):
description.mqttTopicCommand = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCommand}"
description.mqttTopicCurrentValue = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCurrentValue}"
description.device_prefix_topic = device_prefix_topic
switchList.append(
PolarisSwitch(
description=description,
device_friendly_name=device_id,
mqtt_root=mqtt_root,
device_type=device_type,
device_id=device_id
)
)
if (device_type == "820"):
SWITCHES_AIRCONDITIONER_820_LC = copy.deepcopy(SWITCHES_AIRCONDITIONER_820)
for description in SWITCHES_AIRCONDITIONER_820_LC:
description.mqttTopicCommand = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCommand}"
description.mqttTopicCurrentValue = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCurrentValue}"
description.device_prefix_topic = device_prefix_topic
switchList.append(
PolarisSwitch(
description=description,
device_friendly_name=device_id,
mqtt_root=mqtt_root,
device_type=device_type,
device_id=device_id
)
)
if (device_type == "813"):
SWITCHES_AIRCONDITIONER_LC = copy.deepcopy(SWITCHES_AIRCONDITIONER)
for description in SWITCHES_AIRCONDITIONER_LC:
description.mqttTopicCommand = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCommand}"
description.mqttTopicCurrentValue = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCurrentValue}"
description.device_prefix_topic = device_prefix_topic
switchList.append(
PolarisSwitch(
description=description,
device_friendly_name=device_id,
mqtt_root=mqtt_root,
device_type=device_type,
device_id=device_id
)
)
if (device_type == "882"):
SWITCHES_AIRCONDITIONER_882_LC = copy.deepcopy(SWITCHES_AIRCONDITIONER_882)
for description in SWITCHES_AIRCONDITIONER_882_LC:
description.mqttTopicCommand = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCommand}"
description.mqttTopicCurrentValue = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCurrentValue}"
description.device_prefix_topic = device_prefix_topic
switchList.append(
PolarisSwitch(
description=description,
device_friendly_name=device_id,
mqtt_root=mqtt_root,
device_type=device_type,
device_id=device_id
)
)
if (device_type in POLARIS_THERMOSTAT_TYPE):
SWITCHES_THERMOSTAT_LC = copy.deepcopy(SWITCHES_THERMOSTAT)
for description in SWITCHES_THERMOSTAT_LC:
description.mqttTopicCommand = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCommand}"
description.mqttTopicCurrentValue = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCurrentValue}"
description.device_prefix_topic = device_prefix_topic
switchList.append(
PolarisSwitch(
description=description,
device_friendly_name=device_id,
mqtt_root=mqtt_root,
device_type=device_type,
device_id=device_id
)
)
async_add_entities(switchList, update_before_add=True)
class PolarisSwitch(PolarisBaseEntity, SwitchEntity):
entity_description: PolarisSwitchEntityDescription
def __init__(
self,
device_friendly_name: str,
description: PolarisSwitchEntityDescription,
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.payload_on=description.payload_on
self.payload_off=description.payload_off
self._attr_has_entity_name = True
# self._old_mode = "0"
self._attr_available = False
# self._attr_assumed_state = False
# self._optimistic = False
# self._value_template = self.entity_description.mqttTopicCurrentValue
if device_type == "806":
self._heater_prog_data0 = "0000"
else:
self._heater_prog_data0 = "000000"
if device_type == "826":
self._EAP_data0 = "0000"
if device_type in POLARIS_AIRCONDITIONER_TYPE:
if device_type == "882":
self._swing_message = "000000000000"
else:
self._swing_message = "00000000"
self._aircond_data0 = "0000"
if self.entity_description.key == "turbo":
self._attr_available = False
if self.entity_description.key == "self_cleaning":
self._attr_available = False
if self.entity_description.key == "eco_mode_switch":
self._attr_available = False
if self.entity_description.key == "anti_fingus":
self._attr_available = False
if self.entity_description.key == "night":
self._attr_available = False
async def async_added_to_hass(self):
@callback
def message_received(message):
if POLARIS_DEVICE[int(self.device_type)]['class'] == "coffeemaker":
if int(self.device_type) in POLARIS_COFFEEMAKER_ROG_TYPE:
if str(message.payload) in ("1", "2", "3", "4", "5", "6"):
self._attr_is_on = True
else:
self._attr_is_on = False
elif str(message.payload) in ("01", "1", "03", "3"):
self._attr_is_on = True
else:
self._attr_is_on = False
elif self.entity_description.key == "display_off_heater":
self._heater_prog_data0 = str(message.payload)
self._attr_is_on = True if (self._heater_prog_data0[2:4] == "01") else False
elif self.entity_description.key == "half_power_heater":
self._heater_prog_data0 = str(message.payload)
self._attr_is_on = True if (self._heater_prog_data0[-2:] == "01") else False
elif self.entity_description.key == "quiet_mode":
self._aircond_data0 = str(message.payload)
self._attr_is_on = True if (self._aircond_data0[-2:] == "01") else False
elif self.entity_description.key == "self_cleaning":
self._aircond_data0 = str(message.payload)
self._attr_is_on = True if (self._aircond_data0[:2] == "01") else False
else:
if str(message.payload).lower() in ("1", "01", "2", "3", "4", "5", "true"):
self._attr_is_on = True
elif str(message.payload).lower() in ("0", "00", "false"):
self._attr_is_on = False
self.async_write_ha_state()
await mqtt.async_subscribe(
self.hass,
self.entity_description.mqttTopicCurrentValue,
message_received,
1,
)
@callback
def mode_message_received(message):
if self.entity_description.translation_key == "keepwarm_switch":
if str(message.payload) in ("[]", '[{"mode":1,"time":0,"temperature":0}]'):
self._attr_available = False
# self._attr_is_on = False
else:
self._attr_available = True
self._attr_is_on = True
self.async_write_ha_state()
await mqtt.async_subscribe(
self.hass,
self.entity_description.mqttTopicCurrentValue.replace("keepwarm", "steps"),
mode_message_received,
1,
)
@callback
def EAP_data_message_received(message):
self._EAP_data0 = message.payload
if self.device_type == "826":
await mqtt.async_subscribe(
self.hass,
self.entity_description.mqttTopicCurrentValue.replace("backlight", "program_data/0"),
EAP_data_message_received,
1,
)
@callback
def mode_conditioner_data_message_received(message):
if self.entity_description.key in ("auto_heater_switch", "eco_mode_switch", "turbo", "night", "self_cleaning", "anti_fingus"):
if (message.payload == "5"): #FAN
if self.entity_description.key == "night":
self._attr_available = False
elif self.entity_description.key == "night":
self._attr_available = True
if (message.payload == "4"): #HEAT
if self.entity_description.key in ("auto_heater_switch", "turbo"):
self._attr_available = True
elif self.entity_description.key == "auto_heater_switch":
self._attr_available = False
if (message.payload == "2"): #COOL
if self.entity_description.key in ("eco_mode_switch", "turbo"):
self._attr_available = True
elif self.entity_description.key == "eco_mode_switch":
self._attr_available = False
if (message.payload == "0"): #OFF
if self.entity_description.key in ("self_cleaning", "anti_fingus"):
self._attr_available = True
elif self.entity_description.key in ("self_cleaning", "anti_fingus"):
self._attr_available = False
if message.payload in ("0", "1", "3", "5"):
if self.entity_description.key == "turbo":
self._attr_available = False
self.async_write_ha_state()
if self.device_type in POLARIS_AIRCONDITIONER_TYPE:
await mqtt.async_subscribe(
self.hass,
f"{self.mqtt_root}/{self.entity_description.device_prefix_topic}/state/mode",
mode_conditioner_data_message_received,
1,
)
@callback
def swing_data_message_received(message):
self._swing_message = message.payload
if self.entity_description.key == "eco_mode_switch":
if message.payload[4:6] == "01":
self._attr_is_on = True
else:
self._attr_is_on = False
self.async_write_ha_state()
if self.entity_description.key == "auto_heater_switch":
if message.payload[-2:] == "01":
self._attr_is_on = True
else:
self._attr_is_on = False
self.async_write_ha_state()
if self.entity_description.key == "anti_fingus":
if message.payload[-2:] == "01":
self._attr_is_on = True
else:
self._attr_is_on = False
self.async_write_ha_state()
if self.device_type in POLARIS_AIRCONDITIONER_TYPE:
await mqtt.async_subscribe(
self.hass,
self.entity_description.mqttTopicCurrentValue,
swing_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:
if self.entity_description.key != "keepwarm":
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)
def turn_on(self, **kwargs):
# self._attr_is_on = True # optimistic mode
topic = f"{self.entity_description.mqttTopicCommand}"
if self.entity_description.key == "display_off_heater":
if self.device_type == "806":
send_message = self._heater_prog_data0[:2] + self.payload_on
else:
send_message = self._heater_prog_data0[:2] + self.payload_on + self._heater_prog_data0[-2:]
elif self.entity_description.key == "eco_mode_switch":
send_message = self._swing_message[:4] + self.payload_on + self._swing_message[-2:]
elif self.entity_description.key == "auto_heater_switch":
send_message = self._swing_message[:6] + self.payload_on
elif self.entity_description.key == "anti_fingus":
send_message = self._swing_message[:6] + self.payload_on
elif self.entity_description.key == "half_power_heater":
send_message = self._heater_prog_data0[:4] + self.payload_on
elif self.entity_description.key == "quiet_mode":
send_message = self._aircond_data0[:2] + self.payload_on
elif self.entity_description.key == "self_cleaning":
send_message = self.payload_on + self._aircond_data0[-2:]
elif (self.device_type == "826" and self.entity_description.key == "backlight"):
self._EAP_data0 = self._EAP_data0[:2] + "01"
mqtt.publish(self.hass, self.entity_description.mqttTopicCommand.replace("backlight", "program_data/0"), self._EAP_data0)
send_message = self.payload_on
else:
send_message = self.payload_on
mqtt.publish(self.hass, topic, send_message)
def turn_off(self, **kwargs):
# self._attr_is_on = False # optimistic mode
topic = f"{self.entity_description.mqttTopicCommand}"
if self.entity_description.key == "display_off_heater":
if self.device_type == "806":
send_message = self._heater_prog_data0[:2] + self.payload_off
else:
send_message = self._heater_prog_data0[:2] + self.payload_off + self._heater_prog_data0[-2:]
elif self.entity_description.key == "eco_mode_switch":
send_message = self._swing_message[:4] + self.payload_off + self._swing_message[-2:]
elif self.entity_description.key == "auto_heater_switch":
send_message = self._swing_message[:6] + self.payload_off
elif self.entity_description.key == "anti_fingus":
send_message = self._swing_message[:6] + self.payload_off
elif self.entity_description.key == "half_power_heater":
send_message = self._heater_prog_data0[:4] + self.payload_off
elif self.entity_description.key == "quiet_mode":
send_message = self._aircond_data0[:2] + self.payload_off
elif self.entity_description.key == "self_cleaning":
send_message = self.payload_off + self._aircond_data0[-2:]
elif (self.device_type == "826" and self.entity_description.key == "backlight"):
self._EAP_data0 = self._EAP_data0[:2] + "00"
mqtt.publish(self.hass, self.entity_description.mqttTopicCommand.replace("backlight", "program_data/0"), self._EAP_data0)
send_message = self.payload_off
else:
send_message = self.payload_off
mqtt.publish(self.hass, topic, send_message)

View File

@@ -0,0 +1,114 @@
"""The Polaris IQ Home component."""
from __future__ import annotations
import json
import re
import logging
from typing import Iterable
import copy
from homeassistant.components import mqtt
from homeassistant.components.mqtt.models import ReceiveMessage
from datetime import time
from homeassistant.components.time import DOMAIN, TimeEntity, TimeEntityDescription
#from homeassistant.components.datetime import DOMAIN, DateTimeEntity, DateTimeEntityDescription
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,
TIME_COOKER,
PolarisTimeEntityDescription,
POLARIS_COOKER_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"]
timeList = []
if (device_type in POLARIS_COOKER_TYPE):
TIME_COOKER_LC = copy.deepcopy(TIME_COOKER)
for description in TIME_COOKER_LC:
description.mqttTopicCurrentTime = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCurrentTime}"
description.mqttTopicCommandTime = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCommandTime}"
description.device_prefix_topic = device_prefix_topic
timeList.append(
PolarisTime(
description=description,
device_friendly_name=device_id,
mqtt_root=mqtt_root,
device_type=device_type,
device_id=device_id
)
)
async_add_entities(timeList, update_before_add=True)
class PolarisTime(PolarisBaseEntity, TimeEntity):
entity_description: PolarisTimeDescription
def __init__(
self,
device_friendly_name: str,
description: PolarisTimeEntityDescription,
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_available = False
self._attr_has_entity_name = True
self._attr_native_value = time(0, self.entity_description.default_time, 0)
async def async_added_to_hass(self):
@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)
async def async_set_value(self, value: time) -> None:
"""Update the time."""
value_hour=value.hour
value_minute=value.minute
value_second=0
value_in_seconds = value.hour * 3600 + value.minute * 60
if (value_in_seconds > (self.entity_description.max_time * 60)):
value_hour = int(self.entity_description.max_time / 60) - 1
self._attr_native_value = time(value_hour,value_minute,value_second)
value_in_seconds = value_hour * 3600 + value_minute * 60
mqtt.publish(self.hass, self.entity_description.mqttTopicCommandTime, str(value_in_seconds))

View File

@@ -0,0 +1,669 @@
{
"config": {
"abort": {
"already_configured": "This device has already been configured.",
"not_supported": "Error. Not selected device type."
},
"error": {
"cannot_connect": "Failed to connect, please try again.",
"invalid_auth": "Invalid authentication.",
"too_many_requests": "Too many requests, retry later.",
"unknown": "Unexpected error.",
"Unknown error occurred": "Unexpected error."
},
"step": {
"user": {
"description": "Configuring Polaris Local MQTT.",
"data": {
"DEVICEID": "Select a device (or all devices)",
"MQTT_ROOT_TOPIC": "Topic Prefix (usually 'polaris')"
},
"title": "Polaris Local MQTT"
},
"undef": {
"description": "Configuring Polaris device.",
"data": {
"DEVICETYPE": "Select device type"
},
"title": "Polaris Local MQTT"
}
}
},
"entity": {
"switch": {
"sound_switch": {
"name": "Sound"
},
"power_switch": {
"name": "Power"
},
"child_lock_switch": {
"name": "Child lock"
},
"backlight_switch": {
"name": "Backlight"
},
"backlight_bottom_switch": {
"name": "Bottom backlight"
},
"ioniser_switch": {
"name": "Ioniser"
},
"ozonation_switch": {
"name": "Ozonation"
},
"warm_stream_switch": {
"name": "Warm stream"
},
"keepwarm_switch": {
"name": "Keep warm"
},
"ultraviolet_switch": {
"name": "Ultraviolet"
},
"smart_mode": {
"name": "Smart"
},
"bss_mode": {
"name": "BSS"
},
"smart_mode_switch": {
"name": "Массаж"
},
"damper_switch": {
"name": "Recycling"
},
"damper_heater": {
"name": "Detecting open window"
},
"display_off_heater": {
"name": "Auto off display"
},
"half_power_heater": {
"name": "Half power"
},
"backlight_bright": {
"name": "Bright 50/100"
},
"turbo_switch": {
"name": "Turbo mode"
},
"night_switch": {
"name": "Night mode"
},
"night_light_switch": {
"name": "Night light"
},
"self_cleaning": {
"name": "Self cleaning"
},
"delicate_blowing": {
"name": "Delicate blowing"
},
"eco_mode_switch": {
"name": "Eco mode"
},
"auto_heater_switch": {
"name": "Auto heating"
},
"quiet_mode": {
"name": "Quiet mode"
},
"no_frost": {
"name": "No frost"
},
"anti_fingus_switch": {
"name": "Anti fingus"
}
},
"water_heater": {
"water_heater": {
"name": "Water heater",
"state": {
"off": "Off",
"performance": "Boiling",
"high_demand": "Keep with warm up",
"electric": "Warm up",
"heat_pump": "Warm up with keep",
"eco": "IQ boiling",
"gas": "Tea Time"
}
},
"water_boiler": {
"name": "Boiler",
"state": {
"off": "Off",
"performance": "Low",
"electric": "Mid",
"heat_pump": "Turbo",
"eco": "Eco"
}
}
},
"humidifier": {
"humidifier": {
"name": "Humidifier",
"state_attributes": {
"my_available_modes": {
"state": {
"auto": "Auto",
"comfort": "Health",
"baby": "Baby mode",
"sleep": "Night",
"boost": "Intensity",
"home": "Custom",
"eco": "Humidity",
"fitnes": "Fitnes",
"yoga": "Yoga",
"meditation": "Meditation",
"prana_hand": "Prana hand",
"prana_auto": "Prana auto",
"aroma": "Aroma"
}
}
}
}
},
"number": {
"intensity": {
"name": "Intensity"
},
"evaporation_rate": {
"name": "Evaporation rate"
},
"set_temperature": {
"name": "Set temperature"
},
"amount": {
"name": "Amount"
},
"weight": {
"name": "Weight"
},
"tank": {
"name": "Water tank"
},
"pressure": {
"name": "Pressure"
},
"speed": {
"name": "Speed"
},
"temperature": {
"name": "Temperature"
},
"display_time": {
"name": "Display time"
},
"time_timer": {
"name": "Timer"
},
"speed_irrigator": {
"name": "Speed irrigator"
},
"temperature_difference_eco": {
"name": "Temperature difference eco"
},
"temperature_difference_antifrost": {
"name": "Temperature difference antifrost"
},
"bright_backlight": {
"name": "Bright backlight"
},
"power_cable": {
"name": "Power cable"
}
},
"sensor": {
"temperature_sensor": {
"name": "Temperature"
},
"firmware_sensor": {
"name": "Firmware Version"
},
"type_sensor": {
"name": "Device Type"
},
"humidity_sensor": {
"name": "Humidity"
},
"time_to_end_sensor": {
"name": "Time to end"
},
"time_to_end_sensor_turbo": {
"name": "Time to end Turbo"
},
"quality": {
"name": "Качество чистки"
},
"power_consume": {
"name": "Power consume"
},
"error": {
"name": "Error",
"state": {
"no_error": "No",
"low_water": "Low water",
"kettle_out_of_base": "Kettle is out of base station",
"temperature_sensor_failure": "Temperature sensor failure",
"child_lock": "Child lock operated",
"recommended_to_change_water": "It is recommended to change the water in the kettle",
"changed_water_for_long_time": "You probably haven't changed water for a long time. It is desirable to boil it",
"replace_filter": "It is recommended to replace the filter",
"maximum_schedules": "Maximum number of schedules 4",
"clean_tank": "Clean the tank",
"cup_not_present": "Cup is not present",
"gesture_sensor_error": "Gesture sensor error",
"side_door_open": "Side door open",
"waste_container_not_installed": "Waste container not installed",
"drip_tray_not_installed": "Drip tray not installed",
"the_brewing_unit_not_installed": "The brewing unit isn't installed",
"missing_water_tank": "Missing water tank",
"waste_container_full": "Waste container is full",
"not_enough_coffee_beans": "Not enough coffee beans",
"water_supply_blocked": "Water supply is blocked",
"code_e001": "The coffee machine is faulty (code E001) Contact service center",
"code_e002": "The coffee machine is faulty (code E002) Contact service center",
"code_e003": "The coffee machine is faulty (code E003) Contact service center",
"code_e004": "The coffee machine is faulty (code E004) Contact service center",
"code_e005": "The coffee machine is faulty (code E005) Contact service center",
"decalcification_required": "Decalcification required",
"water_changed_for_long_time": "Water in the tank hasn't been changed for a long time. It's recommended to change the water",
"cleaning_milk_system": "Cleaning the milk system",
"cleaning_brewing_system": "Cleaning the brewing system",
"decalcification_progress": "Decalcification in progress",
"cleaning_hydraulic_system": "Cleaning the hydraulic system",
"check_water_tank": "Сheck the water tank",
"cappuccinator_false": "Cappuccinator false",
"nothing_is_selected": "Nothing is selected"
}
},
"filter_retain": {
"name": "Filter retain"
},
"pre_filter_retain": {
"name": "Pre-filter retain"
},
"clean_retain": {
"name": "Clean retain"
},
"anode_retain": {
"name": "Anode retain"
},
"mode_sensor": {
"name": "Mode",
"state": {
"off": "Off",
"espresso": "Espresso",
"ristretto": "Ristretto",
"long_espresso": "Long espresso",
"americano": "Americano",
"heating": "Heating",
"cappuccino": "Cappuccino",
"latte": "Latte",
"flat_white": "Flat white",
"cortado": "Cortado",
"double_espresso": "Double espresso",
"double_cappuccino": "Double cappuccino",
"double_latte": "Double latte",
"double_long_espresso": "Double long espresso",
"double_macchiato": "Double macchiato",
"milk_coffee": "Milk coffee",
"macchiato": "Macchiato",
"latte_macchiato": "Latte macchiato",
"hot_water": "Hot water",
"hot_milk_foam": "Hot milk foam",
"hot_milk": "Hot milk"
}
},
"power_state": {
"name": "Power",
"state": {
"power_on": "On",
"power_off": "Off",
"turns_on": "Turns on",
"turns_off": "Turns off"
}
},
"current_power": {
"name": "Current power"
},
"outdoor_unit_temperature": {
"name": "Temperature outdoor unit"
}
},
"select": {
"select_mode_kettle": {
"name": "Preset",
"state": {
"not_selected": "Not selected",
"black_tea": "Black tea",
"baby_bottle": "Baby bottle",
"instant_coffee": "Instant coffee",
"green_tea": "Green tea",
"flower_tea": "Flower tea",
"tea_bag": "Tea bag",
"red_tea": "Red tea",
"puerh_tea": "Puerh tea",
"oolong_tea": "Oolong tea",
"white_tea": "White tea",
"herbal_tea": "Herbal tea"
}
},
"select_mode_cooker": {
"name": "Recipes",
"state": {
"my_recipe_plus": "My recipe plus",
"reheat": "Reheat",
"cake": "Cake",
"soaked_rice": "Soaked rice",
"stew": "Stew",
"fry": "Fry",
"pilaf": "Pilaf",
"yogurt": "Yogurt",
"oatmeal": "Catmeal",
"milk_porridge": "Milk porridge",
"soup": "Soup",
"meat": "Meat",
"cottage_cheese": "Cottage cheese"
}
},
"select_mode_cofeemaker": {
"name": "Напиток",
"state": {
"not_selected": "Not selected",
"espresso": "Espresso",
"ristretto": "Ristretto",
"long_espresso": "Long espresso",
"americano": "Americano",
"cappuccino": "Cappuccino",
"latte": "Latte",
"flat_white": "Flat white",
"cortado": "Cortado",
"double_espresso": "Double espresso",
"double_cappuccino": "Double cappuccino",
"double_latte": "Double latte",
"double_long_espresso": "Double long espresso",
"double_macchiato": "Double macchiato",
"milk_coffee": "Milk coffee",
"macchiato": "Macchiato",
"latte_macchiato": "Latte macchiato",
"hot_water": "Hot water",
"hot_milk_foam": "Hot milk foam",
"hot_milk": "Hot milk",
"doppio": "Doppio",
"lungo": "Lungo",
"clearing": "Clearing",
"heating": "Heating"
}
},
"select_melody": {
"name": "Melody",
"state": {
"mute": "Without sound",
"rainstorm": "Rainstorm",
"surf": "Surf",
"forest": "In the forest",
"birdsong": "Birdsong",
"bonfire": "Bonfire"
}
},
"select_irrigator": {
"name": "Personal modes",
"state": {
"preset1": "Mode 1",
"preset2": "Mode 2",
"preset3": "Mode 3"
}
},
"select_night_backlight": {
"name": "Night backlight",
"state": {
"off": "Off",
"all_on": "All on",
"high_on": "Hight on",
"mid_on": "Mid on",
"low_on": "Low on"
}
},
"select_swing_horizontal": {
"name": "Swing horizontal",
"state": {
"off": "Swing",
"top": "Top",
"high": "High",
"middle": "Middle",
"low": "Low",
"bottom": "Bottom"
}
},
"select_swing_vertical": {
"name": "Swing vertical",
"state": {
"off": "Swing",
"left": "Left",
"center-left": "Center-left",
"center": "Center",
"center-right": "Center-right",
"right": "Right"
}
}
},
"light": {
"night_light": {
"name": "Night light"
},
"night_light_up": {
"name": "Top backlight"
},
"night_light_down": {
"name": "Bottom backlight"
}
},
"binary_sensor": {
"base_binary_sensor": {
"name": "Position",
"state": {
"on": "Not on the base",
"off": "On the base"
}
},
"lid_binary_sensor": {
"name": "Lid",
"state": {
"on": "Open",
"off": "Close"
}
},
"water_tank_binary_sensor": {
"name": "Tank is removed",
"state": {
"on": "Yes",
"off": "No"
}
},
"cappuccinator_binary_sensor": {
"name": "Cappuccinator",
"state": {
"on": "Installed",
"off": "Not installed"
}
},
"heating_binary_sensor": {
"name": "Heating",
"state": {
"on": "Heats",
"off": "Not heat"
}
},
"available_binary_sensor": {
"name": "LAN",
"state": {
"on": "Online",
"off": "Offline"
}
}
},
"time": {
"delay_start": {
"name": "Delay start"
},
"cooking_time": {
"name": "Cooking time"
}
},
"button": {
"button_stop": {
"name": "Stop"
},
"button_start": {
"name": "Start"
},
"button_stop_coffee": {
"name": "Stop"
},
"button_start_coffee": {
"name": "Start"
},
"button_reset_filter": {
"name": "Reset time filter"
},
"button_reset_prefilter": {
"name": "Reset time pre-filter"
},
"button_reset_tank": {
"name": "Reset time water tank"
}
},
"climate": {
"climate": {
"name": "Climate",
"state_attributes": {
"fan_mode": {
"state": {
"1_speed": "1 speed",
"2_speed": "2 speed",
"3_speed": "3 speed",
"4_speed": "4 speed",
"5_speed": "5 speed",
"6_speed": "6 speed",
"7_speed": "7 speed",
"8_speed": "8 speed",
"9_speed": "9 speed"
}
},
"preset_mode": {
"state": {
"hands": "Hands",
"auto": "Auto",
"night": "Night",
"turbo": "Turbo",
"passive": "Passive"
}
}
}
},
"heater": {
"name": "Heater",
"state_attributes": {
"fan_mode": {
"name": "Heating power",
"state": {
"10_percent": "10 %",
"20_percent": "20 %",
"30_percent": "30 %",
"40_percent": "40 %",
"50_percent": "50 %",
"60_percent": "60 %",
"70_percent": "70 %",
"80_percent": "80 %",
"90_percent": "90 %",
"100_percent": "100 %",
"20_5_percent": "20 %",
"40_5_percent": "40 %",
"60_5_percent": "60 %",
"80_5_percent": "80 %",
"100_5_percent": "100 %"
}
}
}
},
"aircleaner": {
"name": "Air cleaner",
"state": {
"dry": "Сlearing"
},
"state_attributes": {
"preset_mode": {
"state": {
"hands": "Hands",
"auto": "Auto",
"night": "Night"
}
},
"fan_mode": {
"state": {
"top": "Turbo"
}
}
}
},
"conditioner": {
"name": "Conditioner",
"state_attributes": {
"fan_mode": {
"state": {
"min": "Max",
"max": "Min"
}
}
}
},
"thermostat": {
"name": "Thermostat",
"state_attributes": {
"preset_mode": {
"state": {
"eco": "Eco",
"comfort": "Comfort",
"turbo": "Turbo",
"antifrost": "Antifrost",
"schedule": "Schedule",
"vacation": "Vacation",
"manual": "Manual"
}
}
}
}
}
},
"common": {
"all": "All devices",
"kettle": "Kettle",
"humidifier": "Humidifier",
"cooker": "Cooker",
"coffeemaker": "Coffeemaker",
"air_cleaner": "Air cleaner",
"irrigator": "Irrigator",
"air_fryer": "Air Fryer",
"boiler": "Boiler",
"heater": "Heater",
"cleaner": "Cleaner",
"blender": "Blender",
"cooktop": "Cooktop",
"cordless_cleaner": "Cordless cleaner",
"fan": "Fan",
"grill": "Grill",
"hair_care": "Hair care",
"hood": "Hood",
"iron": "Iron",
"kitchen_machine": "Kitchen machine",
"meat_grinder": "Meat grinder",
"other": "Smart Lid",
"oven": "Oven",
"steamer": "Steamer",
"toothbrush": "Toothbrush",
"air_conditioner": "Conditioner",
"thermostat": "Thermostat"
}
}

View File

@@ -0,0 +1,669 @@
{
"config": {
"abort": {
"already_configured": "Это устройство уже было настроено.",
"not_supported": "Ошибка. Не выбран тип устройства."
},
"error": {
"cannot_connect": "Не удалось подключиться, пожалуйста, повторите попытку.",
"invalid_auth": "Неверная аутентификация.",
"too_many_requests": "Слишком много запросов, повторите попытку позже.",
"unknown": "Неожиданная ошибка.",
"Unknown error occurred": "Неожиданная ошибка."
},
"step": {
"user": {
"description": "Конфигурация Polaris Local MQTT.",
"data": {
"DEVICEID": "Выберите устройство",
"MQTT_ROOT_TOPIC": "Префикс темы (обычно 'polaris')"
},
"title": "Polaris Local MQTT"
},
"undef": {
"description": "Конфигурация устройства Polaris.",
"data": {
"DEVICETYPE": "Выберите тип устройства"
},
"title": "Polaris Local MQTT"
}
}
},
"entity": {
"switch": {
"sound_switch": {
"name": "Звук"
},
"power_switch": {
"name": "Включить"
},
"child_lock_switch": {
"name": "Блокировка"
},
"backlight_switch": {
"name": "Подсветка"
},
"backlight_bottom_switch": {
"name": "Нижняя подсветка"
},
"ioniser_switch": {
"name": "Ионизация"
},
"ozonation_switch": {
"name": "Озонирование"
},
"warm_stream_switch": {
"name": "Теплый пар"
},
"keepwarm_switch": {
"name": "Поддержание температуры"
},
"ultraviolet_switch": {
"name": "Ультрафиолет"
},
"smart_mode": {
"name": "Умный режим"
},
"bss_mode": {
"name": "Антибактериальная очистка"
},
"smart_mode_switch": {
"name": "Массаж"
},
"damper_switch": {
"name": "Рециркуляция"
},
"damper_heater": {
"name": "Обнаружение открытого окна"
},
"display_off_heater": {
"name": "Автоотключение дисплея"
},
"half_power_heater": {
"name": "Половина мощности"
},
"backlight_bright": {
"name": "Яркость 50/100"
},
"turbo_switch": {
"name": "Турбо режим"
},
"night_switch": {
"name": "Ночной режим"
},
"night_light_switch": {
"name": "Ночная подсветка"
},
"self_cleaning": {
"name": "Самоочистка"
},
"delicate_blowing": {
"name": "Деликатный обдув"
},
"eco_mode_switch": {
"name": "Экономичный режим"
},
"auto_heater_switch": {
"name": "Автообогрев"
},
"quiet_mode": {
"name": "Тихий режим"
},
"no_frost": {
"name": "Антизамерзание"
},
"anti_fingus_switch": {
"name": "Профилактика грибка"
}
},
"water_heater": {
"water_heater": {
"name": "Нагрев",
"state": {
"off": "Выключен",
"performance": "Кипячение",
"high_demand": "Кипячение с удержанием",
"electric": "Разогрев",
"heat_pump": "Разогрев с удержанием",
"eco": "IQ Кипячение",
"gas": "Чайная церемония"
}
},
"water_boiler": {
"name": "Бойлер",
"state": {
"off": "Выключен",
"performance": "Низкий",
"electric": "Средний",
"heat_pump": "Высокий",
"eco": "Экономичный"
}
}
},
"humidifier": {
"humidifier": {
"name": "Увлажнитель",
"state_attributes": {
"mode": {
"state": {
"auto": "Авто",
"comfort": "Здоровье",
"baby": "Детский",
"sleep": "Ночной",
"boost": "Интенсивность",
"home": "Ручной",
"eco": "Увлажнение",
"fitnes": "Фитнес",
"yoga": "Йога",
"meditation": "Медитация",
"prana_hand": "Прана ручной",
"prana_auto": "Прана авто",
"aroma": "Арома"
}
}
}
}
},
"number": {
"intensity": {
"name": "Интенсивность"
},
"evaporation_rate": {
"name": "Скорость испарения"
},
"set_temperature": {
"name": "Установка температуры"
},
"amount": {
"name": "Объём кофе"
},
"weight": {
"name": "Крепкость напитка"
},
"tank": {
"name": "Объём напитка"
},
"pressure": {
"name": "Объём пенки"
},
"speed": {
"name": "Объём молока"
},
"temperature": {
"name": "Температура напитка"
},
"display_time": {
"name": "Отключение дисплея"
},
"time_timer": {
"name": "Таймер отключения"
},
"speed_irrigator": {
"name": "Скорость"
},
"temperature_difference_eco": {
"name": "Разница температур в режиме ECO"
},
"temperature_difference_antifrost": {
"name": "Разница температур в режиме Anti frost"
},
"bright_backlight": {
"name": "Яркость подсветки"
},
"power_cable": {
"name": "Мощность кабеля"
}
},
"sensor": {
"temperature_sensor": {
"name": "Температура"
},
"firmware_sensor": {
"name": "Версия прошивки"
},
"type_sensor": {
"name": "Тип устройства"
},
"humidity_sensor": {
"name": "Влажность"
},
"time_to_end_sensor": {
"name": "Оставшееся время"
},
"time_to_end_sensor_turbo": {
"name": "Оставшееся время Турбо"
},
"quality": {
"name": "Качество чистки"
},
"power_consume": {
"name": "Потребление энергии"
},
"error": {
"name": "Ошибка",
"state": {
"no_error": "Нет",
"low_water": "Мало воды",
"kettle_out_of_base": "Чайник снят с базы",
"temperature_sensor_failure": "Ошибка термодатчика",
"child_lock": "Сработала защита от случайного нажатия",
"recommended_to_change_water": "Рекомендуется сменить воду в чайнике",
"changed_water_for_long_time": "Вероятно, вы давно не меняли воду. Ее желательно прокипятить",
"replace_filter": "Рекомендуется заменить фильтр",
"maximum_schedules": "Максимальное количество расписаний 4",
"clean_tank": "Почистите бак",
"cup_not_present": "Чаша не установлена",
"gesture_sensor_error": "Ошибка датчика жестов",
"side_door_open": "Открыта боковая дверца",
"waste_container_not_installed": "Контейнер для отходов не установлен",
"drip_tray_not_installed": "Каплесборник не установлен",
"the_brewing_unit_not_installed": "Заварочный узел не установлен",
"missing_water_tank": "Отсутствует резервуар для воды",
"waste_container_full": "Контейнер для отходов полон",
"not_enough_coffee_beans": "Недостаточно кофейных зерен",
"water_supply_blocked": "Подача воды заблокирована",
"code_e001": "Кофемашина неисправна (код Е001) Обратитесь в сервисный центр",
"code_e002": "Кофемашина неисправна (код Е002) Обратитесь в сервисный центр",
"code_e003": "Кофемашина неисправна (код Е003) Обратитесь в сервисный центр",
"code_e004": "Кофемашина неисправна (код Е004) Обратитесь в сервисный центр",
"code_e005": "Кофемашина неисправна (код Е005) Обратитесь в сервисный центр",
"decalcification_required": "Необходима декальцинация",
"water_changed_for_long_time": "Вода в резервуаре давно не менялась, рекомендуем сменить воду",
"cleaning_milk_system": "Очистка молочной системы",
"cleaning_brewing_system": "Очистка заварочной системы",
"decalcification_progress": "Идет декальцинация",
"cleaning_hydraulic_system": "Очистка гидросистемы",
"check_water_tank": "Проверьте бак с водой",
"cappuccinator_false": "Капучинатор не установлен",
"nothing_is_selected": "Не выбран напиток"
}
},
"filter_retain": {
"name": "Остаток фильтра"
},
"pre_filter_retain": {
"name": "Остаток предфильтра"
},
"clean_retain": {
"name": "Остаток до очистки бака"
},
"anode_retain": {
"name": "Остаток до замены анода"
},
"mode_sensor": {
"name": "Режим работы",
"state": {
"off": "Выключен",
"espresso": "Эспрессо",
"ristretto": "Ристретто",
"long_espresso": "Лунго",
"americano": "Американо",
"heating": "Нагрев",
"cappuccino": "Капучино",
"latte": "Латте",
"flat_white": "Флэт уайт",
"cortado": "Кортадо",
"double_espresso": "Двойной эспрессо",
"double_cappuccino": "Двойной капучино",
"double_latte": "Двойной латте",
"double_long_espresso": "Двойной лунго",
"double_macchiato": "Двойной макиато",
"milk_coffee": "Кофе с молоком",
"macchiato": "Макиато",
"latte_macchiato": "Латте макиато",
"hot_water": "Горячая вода",
"hot_milk foam": "Молочная пена",
"hot_milk": "Горячее молоко"
}
},
"power_state": {
"name": "Питание",
"state": {
"power_on": "Включено",
"power_off": "Выключено",
"turns_on": "Включается",
"turns_off": "Выключается"
}
},
"сurrent_power": {
"name": "Текущая мощность"
},
"outdoor_unit_temperature": {
"name": "Температура внешнего блока"
}
},
"select": {
"select_mode_kettle": {
"name": "Предустановки",
"state": {
"not_selected": "Не выбрано",
"black_tea": "Черный чай",
"baby_bottle": "Детская смесь",
"instant_coffee": "Растворимый кофе",
"green_tea": "Зеленый чай",
"flower_tea": "Цветочный чай",
"tea_bag": "Пакетированный чай",
"red_tea": "Красный чай",
"puerh_tea": "Пуэр",
"oolong_tea": "Улун",
"white_tea": "Белый чай",
"herbal_tea": "Травяной чай"
}
},
"select_mode_cooker": {
"name": "Рецепты",
"state": {
"my_recipe_plus": "Мой рецепт +",
"reheat": "Разогрев",
"cake": "Выпечка",
"soaked_rice": "Крупа",
"stew": "Тушение",
"fry": "Жарка",
"pilaf": "Плов",
"yogurt": "Йогурт",
"oatmeal": "Овсянка",
"milk_porridge": "Молочная каша",
"soup": "Суп",
"meat": "Холодец",
"cottage_cheese": "Творог"
}
},
"select_mode_cofeemaker": {
"name": "Напиток",
"state": {
"not_selected": "Не выбрано",
"espresso": "Эспрессо",
"ristretto": "Ристретто",
"long_espresso": "Лунго",
"americano": "Американо",
"cappuccino": "Капучино",
"latte": "Латте",
"flat_white": "Флэт уайт",
"cortado": "Кортадо",
"double_espresso": "Двойной эспрессо",
"double_cappuccino": "Двойной капучино",
"double_latte": "Двойной латте",
"double_long_espresso": "Двойной лунго",
"double_macchiato": "Двойной макиато",
"milk_coffee": "Кофе с молоком",
"macchiato": "Макиато",
"latte_macchiato": "Латте макиато",
"hot_water": "Горячая вода",
"hot_milk_foam": "Молочная пена",
"hot_milk": "Горячее молоко",
"doppio": "Доппио",
"lungo": "Лунго",
"clearing": "Очистка",
"heating": "Подогрев"
}
},
"select_melody": {
"name": "Мелодия",
"state": {
"mute": "Без звука",
"rainstorm": "Ливень",
"surf": "Прибой",
"forest": "В лесу",
"birdsong": "Пение птиц",
"bonfire": "Костер"
}
},
"select_irrigator": {
"name": "Персональные режимы",
"state": {
"preset1": "Режим 1",
"preset2": "Режим 2",
"preset3": "Режим 3"
}
},
"select_night_backlight": {
"name": "Ночная подсветка",
"state": {
"off": "Выключена",
"all_on": "Весь свет включен",
"high_on": "Верхний свет включен",
"mid_on": "Средний свет включен",
"low_on": "Нижний свет включен"
}
},
"select_swing_horizontal": {
"name": "Вертикальный обдув",
"state": {
"off": "Качание",
"top": "Вверх",
"high": "Выше среднего",
"middle": "Среднее",
"low": "Ниже среднего",
"bottom": "Вниз"
}
},
"select_swing_vertical": {
"name": "Горизонтальный обдув",
"state": {
"off": "Качание",
"left": "Влево",
"center-left": "Левее центра",
"center": "Центр",
"center-right": "Правее центра",
"right": "Вправо"
}
}
},
"light": {
"night_light": {
"name": "Ночник"
},
"night_light_up": {
"name": "Верхняя подсветка"
},
"night_light_down": {
"name": "Нижняя подсветка"
}
},
"binary_sensor": {
"base_binary_sensor": {
"name": "Положение",
"state": {
"on": "Снят с базы",
"off": "На базе"
}
},
"lid_binary_sensor": {
"name": "Крышка",
"state": {
"on": "Открыта",
"off": "Закрыта"
}
},
"water_tank_binary_sensor": {
"name": "Бак снят",
"state": {
"on": "Да",
"off": "Нет"
}
},
"cappuccinator_binary_sensor": {
"name": "Капучинатор",
"state": {
"on": "Установлен",
"off": "Не установлен"
}
},
"heating_binary_sensor": {
"name": "Нагрев",
"state": {
"on": "Греет",
"off": "Не греет"
}
},
"available_binary_sensor": {
"name": "Локальная сеть",
"state": {
"on": "В сети",
"off": "Не в сети"
}
}
},
"time": {
"delay_start": {
"name": "Отложенный старт"
},
"cooking_time": {
"name": "Время приготовления"
}
},
"button": {
"button_stop": {
"name": "Остановить"
},
"button_start": {
"name": "Запустить"
},
"button_stop_coffee": {
"name": "Остановить"
},
"button_start_coffee": {
"name": "Приготовить"
},
"button_reset_filter": {
"name": "Сброс таймера фильтра"
},
"button_reset_prefilter": {
"name": "Сброс таймера префильтра"
},
"button_reset_tank": {
"name": "Сброс таймера очистки бака"
}
},
"climate": {
"climate": {
"name": "Климат",
"state_attributes": {
"fan_mode": {
"state": {
"1_speed": "1 скорость",
"2_speed": "2 скорость",
"3_speed": "3 скорость",
"4_speed": "4 скорость",
"5_speed": "5 скорость",
"6_speed": "6 скорость",
"7_speed": "7 скорость",
"8_speed": "8 скорость",
"9_speed": "9 скорость"
}
},
"preset_mode": {
"state": {
"hands": "Ручной",
"auto": "Автоматический",
"night": "Ночной",
"turbo": "Турбо",
"passive": "Пассивный"
}
}
}
},
"heater": {
"name": "Конвектор",
"state_attributes": {
"fan_mode": {
"name": "Мощность нагрева",
"state": {
"10_percent": "10 %",
"20_percent": "20 %",
"30_percent": "30 %",
"40_percent": "40 %",
"50_percent": "50 %",
"60_percent": "60 %",
"70_percent": "70 %",
"80_percent": "80 %",
"90_percent": "90 %",
"100_percent": "100 %",
"20_5_percent": "20 %",
"40_5_percent": "40 %",
"60_5_percent": "60 %",
"80_5_percent": "80 %",
"100_5_percent": "100 %"
}
}
}
},
"aircleaner": {
"name": "Очиститель воздуха",
"state": {
"dry": "Очистка"
},
"state_attributes": {
"preset_mode": {
"state": {
"hands": "Ручной",
"auto": "Автоматический",
"night": "Ночной"
}
},
"fan_mode": {
"state": {
"top": "Турбо"
}
}
}
},
"conditioner": {
"name": "Кондиционер",
"state_attributes": {
"fan_mode": {
"state": {
"min": "Минимум",
"max": "Максимум"
}
}
}
},
"thermostat": {
"name": "Термостат",
"state_attributes": {
"preset_mode": {
"state": {
"eco": "Экономичный",
"comfort": "Комфорт",
"turbo": "Турбо",
"antifrost": "Антизамерзание",
"schedule": "Расписание",
"vacation": "Отпуск",
"manual": "Настройка"
}
}
}
}
}
},
"common": {
"all": "Выберите тип устройства",
"kettle": "Чайник",
"humidifier": "Увлажнитель",
"cooker": "Мультиварка",
"coffeemaker": "Кофемашина",
"air_cleaner": "Очиститель воздуха",
"irrigator": "Ирригатор",
"air_fryer": "Воздухоочиститель",
"boiler": "Бойлер",
"heater": "Обогреватель",
"cleaner": "Робот пылесос",
"blender": "Блендер",
"cooktop": "Варочная панель",
"cordless_cleaner": "Ручной пылесос",
"fan": "Вентилятор",
"grill": "Гриль",
"hair_care": "Фен",
"hood": "Вытяжка",
"iron": "Утюг",
"kitchen_machine": "Кухонная машина",
"meat_grinder": "Мясорубка",
"other": "Умная крышка",
"oven": "Духовка",
"steamer": "Отпариватель",
"toothbrush": "Зубная щетка",
"air_conditioner": "Кондиционер",
"thermostat": "Термостат"
}
}

View File

@@ -0,0 +1,523 @@
"""The Polaris IQ Home component."""
from __future__ import annotations
import json
import re
import logging
from typing import Iterable, Final, Any
import copy
import datetime
import os
import voluptuous as vol
import struct
from homeassistant.components import mqtt
from homeassistant.components.mqtt.models import ReceiveMessage
from homeassistant.components.vacuum import (
DOMAIN,
ATTR_CLEANED_AREA,
StateVacuumEntity,
# VacuumActivity,
VacuumEntityFeature,
)
from homeassistant.helpers.entity import DeviceInfo, EntityCategory
from homeassistant.const import ATTR_ENTITY_ID, ATTR_ID
from homeassistant.util import slugify
from homeassistant.core import HomeAssistant, callback, ServiceCall
from homeassistant.config_entries import ConfigEntry
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers import config_validation as cv, entity_platform
from .common import PolarisBaseEntity
# Import global values.
from .const import (
MANUFACTURER,
MQTT_ROOT_TOPIC,
DEVICEID,
DEVICETYPE,
POLARIS_DEVICE,
VACUUM,
PolarisSelectEntityDescription,
POLARIS_VACUUM_TYPE,
CUSTOM_SELECT_FILE_PATH,
SELECT_VACUUM,
)
SERVICE_VACUUM_CLEANING_ROOM: Final = "vacuum_cleaning_room"
ATTR_SELECTED_ROOMS = "rooms"
SELECT_ROOMS = "select_rooms"
_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"]
vacuumList = []
# if rooms_js:
# available_rooms = list(rooms_js.keys())
file_path = CUSTOM_SELECT_FILE_PATH
if os.path.exists(file_path):
with open(file_path, 'r', encoding='utf-8') as file:
custom_data_rooms = json.loads(file.read())
else:
custom_data_rooms = None
if custom_data_rooms is not None and "SELECT_VACUUM_rooms" in custom_data_rooms:
custom_data_rooms = json.loads(json.dumps(custom_data_rooms))
rooms_js = custom_data_rooms["SELECT_VACUUM_rooms"]
# _LOGGER.debug("rooms_js %s", rooms_js)
else:
rooms_js = {"no_room": {"id": "00", "coordinates": []}}
available_rooms = list(rooms_js.keys())
# _LOGGER.debug("available_rooms read %s", available_rooms)
if (device_type in POLARIS_VACUUM_TYPE):
VACUUM_LC = copy.deepcopy(VACUUM)
for description in VACUUM_LC:
description.mqttTopicCurrentMode = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCurrentMode}"
description.mqttTopicCommandMode = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCommandMode}"
description.mqttTopicBatteryState = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicBatteryState}"
description.mqttTopicBatteryLevel = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicBatteryLevel}"
description.mqttTopicStateFanMode = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicStateFanMode}"
description.mqttTopicCommandFanMode = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCommandFanMode}"
description.mqttTopicCommandFindMe = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCommandFindMe}"
description.mqttTopicCommandGoArea = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCommandGoArea}"
description.mqttTopicCommandTest = f"{mqtt_root}/{device_prefix_topic}/state"
description.device_prefix_topic = device_prefix_topic
vacuumList.append(
PolarisVacuum(
description=description,
device_friendly_name=device_id,
mqtt_root=mqtt_root,
device_type=device_type,
device_id=device_id,
available_rooms=available_rooms,
rooms_js=rooms_js
)
)
async_add_entities(vacuumList, update_before_add=True)
platform = entity_platform.current_platform.get()
platform.async_register_entity_service(
"select_rooms",
{
vol.Required(ATTR_ENTITY_ID): cv.entity_ids,
vol.Required(SELECT_ROOMS): vol.All(cv.string, vol.In(available_rooms))
},
"sweep_rooms_wrapper",
)
class PolarisVacuum(PolarisBaseEntity, StateVacuumEntity):
entity_description: PolarisVacuumEntityDescription
# _unrecorded_attributes = frozenset({ATTR_ROOMS})
def __init__(
self,
device_friendly_name: str,
description: PolarisVacuumEntityDescription,
mqtt_root: str,
device_id: str | None=None,
device_type: str | None=None,
available_rooms: list | None=None,
rooms_js: 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
self._attr_fan_speed="min"
self._attr_fan_speed_list = ["min", "medium", "high", "max"]
self._attr_supported_features = (
VacuumEntityFeature.BATTERY
| VacuumEntityFeature.RETURN_HOME
| VacuumEntityFeature.CLEAN_SPOT
| VacuumEntityFeature.STOP
# | VacuumEntityFeature.PAUSE
| VacuumEntityFeature.START
| VacuumEntityFeature.LOCATE
| VacuumEntityFeature.STATE
| VacuumEntityFeature.SEND_COMMAND
| VacuumEntityFeature.FAN_SPEED
| VacuumEntityFeature.STATUS
| VacuumEntityFeature.MAP
)
# self._entity_component_unrecorded_attributes = frozenset({ATTR_FAN_SPEED_LIST})
self._attr_battery_icon="mdi:vacuum"
self._attr_battery_level=70
self._attr_state = "idle"
self._select_rooms = []
self._available_rooms = available_rooms
self._rooms_js = rooms_js
@property
def select_rooms(self) -> list | None:
"""Return a list of rooms available to clean."""
if self._select_rooms:
# _LOGGER.debug("select_rooms : %s", self._select_rooms)
return self._rooms
return []
@property
def extra_state_attributes(self):
"""Return a dictionary of device state attributes specific to sharkiq."""
data = {}
if self._available_rooms is not None:
data["available_rooms"] = self._available_rooms
if self._select_rooms is not None:
data["select_rooms"] = self._select_rooms
return data
def int16_array_to_bytes(self, int16_array, byteorder='little'):
"""
Преобразует массив int16 обратно в байтовую строку.
Аргументы:
int16_array: Массив int16 значений (list of int).
byteorder: Порядок байтов ('little' или 'big'), по умолчанию 'little'.
Возвращает:
Байтовая строка (bytes).
Вызывает исключение ValueError:
Если byteorder не является 'little' или 'big'.
"""
if byteorder not in ('little', 'big'):
raise ValueError("Неверный порядок байтов. Допустимые значения: 'little', 'big'.")
endian_prefix = '<' if byteorder == 'little' else '>'
format_string = endian_prefix + 'h' * len(int16_array)
return struct.pack(format_string, *int16_array) # * распаковывает массив в аргументы для pack
async def sweep_rooms_wrapper(self, select_rooms):
# for room in self._room_manager.rooms.keys():
# self._room_manager.rooms[room] = False
_LOGGER.debug("room in service %s ", select_rooms)
#for entry in mysel_rooms:
# name = self.hass.states.get(entry).attributes['room_name']
# _LOGGER.debug("target_rooms entry %s", entry)
# await self.sweep_rooms(target_rooms)
# self.async_schedule_update_ha_state(force_refresh=True)
def start(self) -> None:
"""Start or resume the cleaning task."""
if self._attr_state != "cleaning":
self._attr_state = "cleaning"
self.schedule_update_ha_state()
state_mode = self.hass.states.get(f"select.{POLARIS_DEVICE[int(self.device_type)]['class']}_{POLARIS_DEVICE[int(self.device_type)]['model'].replace('-', '_')}_select_mode_vacuum").state
mqtt.publish(self.hass, self.entity_description.mqttTopicCommandMode, json.loads(json.dumps(SELECT_VACUUM[0].options[state_mode])))
def stop(self, **kwargs: Any) -> None:
"""Stop the cleaning task, do not return to dock."""
self._attr_state = "idle"
self.schedule_update_ha_state()
mqtt.publish(self.hass, self.entity_description.mqttTopicCommandMode, "0")
def return_to_base(self, **kwargs: Any) -> None:
"""Return dock to charging base."""
self._attr_state = "returning"
self.schedule_update_ha_state()
mqtt.publish(self.hass, self.entity_description.mqttTopicCommandMode, "5")
def clean_spot(self, **kwargs: Any) -> None:
"""Perform a spot clean-up."""
self._attr_state = "cleaning"
self.schedule_update_ha_state()
select_room = self.hass.states.get(f"select.{POLARIS_DEVICE[int(self.device_type)]['class']}_{POLARIS_DEVICE[int(self.device_type)]['model'].replace('-', '_')}_select_room").state
# _LOGGER.debug("room in select %s ", select_room)
# _LOGGER.debug("room in select %s ", self._rooms_js[select_room]["coordinate"])
# _LOGGER.debug("int16_to_bytes %s",self.int16_array_to_bytes(self._rooms_js[select_room]["coordinate"]))
mqtt.publish(
self.hass, self.entity_description.mqttTopicCommandGoArea,
self.int16_array_to_bytes(self._rooms_js[select_room]["coordinate"]),
1,
None,
)
def set_fan_speed(self, fan_speed: str, **kwargs: Any) -> None:
"""Set the vacuum's fan speed."""
if fan_speed in self.fan_speed_list:
self._attr_fan_speed = fan_speed
self.schedule_update_ha_state()
mqtt.publish(self.hass, self.entity_description.mqttTopicCommandFanMode, self._attr_fan_speed_list.index(fan_speed)+1)
async def async_locate(self, **kwargs: Any) -> None:
"""Locate the vacuum's position."""
await self.hass.services.async_call(
"notify",
"persistent_notification",
service_data={"message": "I'm here!", "title": "Locate request"},
)
self._attr_state = "idle"
self.async_write_ha_state()
mqtt.publish(self.hass, self.entity_description.mqttTopicCommandFindMe, "true")
async def async_send_command(
self,
command: str,
params: dict[str, Any] | list[Any] | None = None,
**kwargs: Any,
) -> None:
"""Send a command to the vacuum."""
self._attr_state = "idle"
self.async_write_ha_state()
# def _save_log(self, message, topic) -> None:
# file_path = "vacuum_log.txt"
# with open(file_path, 'a+', encoding='utf-8') as file:
# file.write(f"{datetime.datetime.now().strftime("%d-%m-%Y %H:%M:%S")} {topic} {message}\n")
async def async_added_to_hass(self):
@callback
def message_received_batt_state(message):
payload = message.payload
self._attr_state = payload
await mqtt.async_subscribe(self.hass, self.entity_description.mqttTopicBatteryState, message_received_batt_state, 1)
@callback
def message_received_batt_level(message):
payload = message.payload
self._attr_battery_level = int(payload)
self.async_write_ha_state()
await mqtt.async_subscribe(self.hass, self.entity_description.mqttTopicBatteryLevel, message_received_batt_level, 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)
# @callback
# def message_received_contour(message):
# payload = message.payload
# self._save_log(payload, "Log contour:")
# await mqtt.async_subscribe(self.hass, f"{self.entity_description.mqttTopicCommandTest}/contour", message_received_contour, 1, None)
# @callback
# def message_received_go_area(message):
# payload = message.payload
# self._save_log(payload, "Log go_area:")
# await mqtt.async_subscribe(self.hass, f"{self.entity_description.mqttTopicCommandTest}/go_area", message_received_go_area, 1, None)
# @callback
# def message_received_mode(message):
# payload = message.payload
# self._save_log(payload, "Log mode:")
# await mqtt.async_subscribe(self.hass, f"{self.entity_description.mqttTopicCommandTest}/mode", message_received_mode, 1)
# @callback
# def message_received_map_angle(message):
# payload = message.payload
# self._save_log(payload, "Log map_angle:")
# await mqtt.async_subscribe(self.hass, f"{self.entity_description.mqttTopicCommandTest}/map_angle", message_received_map_angle, 1)
# @callback
# def message_received_clean_area(message):
# payload = message.payload
# self._save_log(payload, "Log clean_area:")
# await mqtt.async_subscribe(self.hass, f"{self.entity_description.mqttTopicCommandTest}/clean_area", message_received_clean_area, 1)
# @callback
# def message_received_program_data_0(message):
# payload = message.payload
# self._save_log(payload, "Log program_data_0:")
# await mqtt.async_subscribe(self.hass, f"{self.entity_description.mqttTopicCommandTest}/program_data/0", message_received_program_data_0, 1)
# @callback
# def message_received_program_data_1(message):
# payload = message.payload
# self._save_log(payload, "Log program_data_1:")
# await mqtt.async_subscribe(self.hass, f"{self.entity_description.mqttTopicCommandTest}/program_data/1", message_received_program_data_1, 1)
# @callback
# def message_received_program_data_2(message):
# payload = message.payload
# self._save_log(payload, "Log program_data_2:")
# await mqtt.async_subscribe(self.hass, f"{self.entity_description.mqttTopicCommandTest}/program_data/2", message_received_program_data_2, 1)
# @callback
# def message_received_program_data_3(message):
# payload = message.payload
# self._save_log(payload, "Log program_data_3:")
# await mqtt.async_subscribe(self.hass, f"{self.entity_description.mqttTopicCommandTest}/program_data/3", message_received_program_data_3, 1)
# @callback
# def message_received_program_data_4(message):
# payload = message.payload
# self._save_log(payload, "Log program_data_4:")
# await mqtt.async_subscribe(self.hass, f"{self.entity_description.mqttTopicCommandTest}/program_data/4", message_received_program_data_4, 1)
# @callback
# def message_received_location_current(message):
# payload = message.payload
# self._save_log(payload, "Log location_current:")
# await mqtt.async_subscribe(self.hass, f"{self.entity_description.mqttTopicCommandTest}/location_current", message_received_location_current, 1)
# @callback
# def message_received_map_0(message):
# payload = message.payload
# self._save_log(payload, "Log map_0:")
# await mqtt.async_subscribe(self.hass, f"{self.entity_description.mqttTopicCommandTest}/map/0", message_received_map_0, 1, None)
# @callback
# def message_received_map_1(message):
# payload = message.payload
# self._save_log(payload, "Log map_1:")
# await mqtt.async_subscribe(self.hass, f"{self.entity_description.mqttTopicCommandTest}/map/1", message_received_map_1, 1, None)
# @callback
# def message_received_virtual_wall(message):
# payload = message.payload
# self._save_log(payload, "Log virtual_wall:")
# await mqtt.async_subscribe(self.hass, f"{self.entity_description.mqttTopicCommandTest}/virtual_wall", message_received_virtual_wall, 1, None)
# @callback
# def message_received_no_go_area(message):
# payload = message.payload
# self._save_log(payload, "Log no_go_area:")
# await mqtt.async_subscribe(self.hass, f"{self.entity_description.mqttTopicCommandTest}/no_go_area", message_received_no_go_area, 1, None)
# @callback
# def message_received_map_image(message):
# payload = message.payload
# self._save_log(payload, "Log map_image:")
# await mqtt.async_subscribe(self.hass, f"{self.entity_description.mqttTopicCommandTest}/map_image", message_received_map_image, 1, None)
# @callback
# def message_received_map_long_0(message):
# payload = message.payload
# self._save_log(payload, "Log map_long_0:")
# await mqtt.async_subscribe(self.hass, f"{self.entity_description.mqttTopicCommandTest}/map_long/0", message_received_map_long_0, 1, None)
# @callback
# def message_received_map_long_1(message):
# payload = message.payload
# self._save_log(payload, "Log map_long_1:")
# await mqtt.async_subscribe(self.hass, f"{self.entity_description.mqttTopicCommandTest}/map_long/1", message_received_map_long_1, 1, None)
# @callback
# def message_received_map_long_2(message):
# payload = message.payload
# self._save_log(payload, "Log map_long_2:")
# await mqtt.async_subscribe(self.hass, f"{self.entity_description.mqttTopicCommandTest}/map_long/2", message_received_map_long_2, 1, None)
# @callback
# def message_received_map_long_3(message):
# payload = message.payload
# self._save_log(payload, "Log map_long_3:")
# await mqtt.async_subscribe(self.hass, f"{self.entity_description.mqttTopicCommandTest}/map_long/3", message_received_map_long_3, 1, None)
# @callback
# def message_received_map_long_4(message):
# payload = message.payload
# self._save_log(payload, "Log map_long_4:")
# await mqtt.async_subscribe(self.hass, f"{self.entity_description.mqttTopicCommandTest}/map_long/4", message_received_map_long_4, 1, None)
# @callback
# def message_received_map_long_5(message):
# payload = message.payload
# self._save_log(payload, "Log map_long_5:")
# await mqtt.async_subscribe(self.hass, f"{self.entity_description.mqttTopicCommandTest}/map_long/5", message_received_map_long_5, 1, None)
# @callback
# def message_received_map_long_6(message):
# payload = message.payload
# self._save_log(payload, "Log map_long_6:")
# await mqtt.async_subscribe(self.hass, f"{self.entity_description.mqttTopicCommandTest}/map_long/6", message_received_map_long_6, 1, None)
# @callback
# def message_received_map_long_7(message):
# payload = message.payload
# self._save_log(payload, "Log map_long_7:")
# await mqtt.async_subscribe(self.hass, f"{self.entity_description.mqttTopicCommandTest}/map_long/7", message_received_map_long_7, 1, None)
# @callback
# def message_received_map_long_8(message):
# payload = message.payload
# self._save_log(payload, "Log map_long_8:")
# await mqtt.async_subscribe(self.hass, f"{self.entity_description.mqttTopicCommandTest}/map_long/8", message_received_map_long_8, 1, None)
# @callback
# def message_received_map_long_9(message):
# payload = message.payload
# self._save_log(payload, "Log map_long_9:")
# await mqtt.async_subscribe(self.hass, f"{self.entity_description.mqttTopicCommandTest}/map_long/9", message_received_map_long_9, 1, None)
# @callback
# def message_received_map_location_0(message):
# payload = message.payload
# self._save_log(payload, "Log map_location_0:")
# await mqtt.async_subscribe(self.hass, f"{self.entity_description.mqttTopicCommandTest}/map_location/0", message_received_map_location_0, 1, None)
# @callback
# def message_received_map_location_1(message):
# payload = message.payload
# self._save_log(payload, "Log map_location_1:")
# await mqtt.async_subscribe(self.hass, f"{self.entity_description.mqttTopicCommandTest}/map_location/1", message_received_map_location_1, 1, None)
# @callback
# def message_received_map_location_2(message):
# payload = message.payload
# self._save_log(payload, "Log map_location_2:")
# await mqtt.async_subscribe(self.hass, f"{self.entity_description.mqttTopicCommandTest}/map_location/2", message_received_map_location_2, 1, None)
# @callback
# def message_received_map_location_3(message):
# payload = message.payload
# self._save_log(payload, "Log map_location_3:")
# await mqtt.async_subscribe(self.hass, f"{self.entity_description.mqttTopicCommandTest}/map_location/3", message_received_map_location_3, 1, None)
# @callback
# def message_received_map_location_4(message):
# payload = message.payload
# self._save_log(payload, "Log map_location_4:")
# await mqtt.async_subscribe(self.hass, f"{self.entity_description.mqttTopicCommandTest}/map_location/4", message_received_map_location_4, 1, None)
# @callback
# def message_received_map_location_5(message):
# payload = message.payload
# self._save_log(payload, "Log map_location_5:")
# await mqtt.async_subscribe(self.hass, f"{self.entity_description.mqttTopicCommandTest}/map_location/5", message_received_map_location_5, 1, None)

View File

@@ -0,0 +1,244 @@
"""The Polaris IQ Home component."""
from __future__ import annotations
import json
import re
import logging
from typing import Iterable
import copy
from homeassistant.components import mqtt
from homeassistant.components.mqtt.models import ReceiveMessage
from homeassistant.const import ATTR_TEMPERATURE, UnitOfTemperature
from homeassistant.components.water_heater import DOMAIN, WaterHeaterEntity, WaterHeaterEntityFeature
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,
WATER_HEATERS,
WATER_BOILERS,
PolarisWaterHeaterEntityDescription,
POLARIS_KETTLE_TYPE,
POLARIS_KETTLE_WITH_WEIGHT_TYPE,
POLARIS_BOILER_TYPE,
KETTLE_WITH_TEA_TIME_MODES,
KETTLE_WITH_KEEP_WITH_WARM_MODES,
POLARIS_KETTLE_WITH_TEA_TIME_MODE_TYPE,
POLARIS_KETTLE_WITH_KEEP_WITH_WARM_MODE_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"]
waterheaterList = []
if (device_type in POLARIS_KETTLE_TYPE) or (device_type in POLARIS_KETTLE_WITH_WEIGHT_TYPE):
# Create water heater for kettle devices
WATER_HEATERS_LC = copy.deepcopy(WATER_HEATERS)
for description in WATER_HEATERS_LC:
description.mqttTopicCommandTemperature = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCommandTemperature}"
description.mqttTopicCurrentTemperature = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCurrentTemperature}"
description.mqttTopicTargetTemperature = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicTargetTemperature}"
description.mqttTopicCommandMode = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCommandMode}"
description.mqttTopicCurrentMode = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCurrentMode}"
description.device_prefix_topic = device_prefix_topic
waterheaterList.append(
PolarisWaterHeater(
description=description,
device_friendly_name=device_id,
mqtt_root=mqtt_root,
device_type=device_type,
device_id=device_id
)
)
if (device_type in POLARIS_BOILER_TYPE):
# Create water boiler
WATER_BOILERS_LC = copy.deepcopy(WATER_BOILERS)
for description in WATER_BOILERS_LC:
description.mqttTopicCommandTemperature = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCommandTemperature}"
description.mqttTopicCurrentTemperature = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCurrentTemperature}"
description.mqttTopicTargetTemperature = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicTargetTemperature}"
description.mqttTopicCommandMode = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCommandMode}"
description.mqttTopicCurrentMode = f"{mqtt_root}/{device_prefix_topic}/{description.mqttTopicCurrentMode}"
description.device_prefix_topic = device_prefix_topic
waterheaterList.append(
PolarisWaterHeater(
description=description,
device_friendly_name=device_id,
mqtt_root=mqtt_root,
device_type=device_type,
device_id=device_id
)
)
async_add_entities(waterheaterList, update_before_add=True)
class PolarisWaterHeater(PolarisBaseEntity, WaterHeaterEntity):
entity_description: PolarisWaterHeaterEntityDescription
def __init__(
self,
device_friendly_name: str,
description: PolarisWaterHeaterEntityDescription,
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.payload_on = description.payload_on
self.payload_off = description.payload_off
self._attr_temperature_unit = UnitOfTemperature.CELSIUS
self._attr_min_temp = description.min_temp
self._attr_max_temp = description.max_temp
self._attr_target_temperature_high = description.max_temp
self._attr_target_temperature_low = description.min_temp
self._attr_target_temperature = description.max_temp
self._attr_target_temperature_step = 1.0
self._attr_is_away_mode_on = None
self._attr_is_on = True
self._attr_supported_features = WaterHeaterEntityFeature.OPERATION_MODE | WaterHeaterEntityFeature.TARGET_TEMPERATURE | WaterHeaterEntityFeature.ON_OFF
self._attr_current_operation = description.mode
self._modes = {}
if (self.device_type in POLARIS_KETTLE_WITH_KEEP_WITH_WARM_MODE_TYPE):
self._modes = KETTLE_WITH_KEEP_WITH_WARM_MODES
# self._attr_operation_list = list(KETTLE_WITH_KEEP_WITH_WARM_MODES.keys())
elif (self.device_type in POLARIS_KETTLE_WITH_TEA_TIME_MODE_TYPE):
self._modes = KETTLE_WITH_TEA_TIME_MODES
# self._attr_operation_list = list(KETTLE_WITH_TEA_TIME_MODES.keys())
elif (self.device_type in {"802","844"}):
self._modes = {"off": "0", "performance": "1", "electric": "2", "heat_pump": "3"}
else:
self._modes = description.operation_list
self._attr_operation_list = list(self._modes.keys())
#self.entity_picture = "https://images.cdn.polaris-iot.com/a/8c/aad08-4d13-489c-9b0f-028486297ac1/60.webp"
self._attr_has_entity_name = True
self._attr_available = False
# _attr_precision: float
# _attr_state: None = None
async def async_added_to_hass(self):
@callback
def message_received_temp(message):
self._attr_current_temperature = float(message.payload)
self.async_write_ha_state()
@callback
def message_received_mode(message):
self._attr_current_operation = list(self._modes.keys())[list(self._modes.values()).index(message.payload)]
self.async_write_ha_state()
@callback
def message_received_targtemp(message):
if float(message.payload) < self._attr_min_temp:
self._attr_target_temperature = self._attr_min_temp
else:
self._attr_target_temperature = float(message.payload)
self.async_write_ha_state()
await mqtt.async_subscribe(
self.hass,
self.entity_description.mqttTopicCurrentTemperature,
message_received_temp,
1,
)
await mqtt.async_subscribe(
self.hass,
self.entity_description.mqttTopicCurrentMode,
message_received_mode,
1,
)
await mqtt.async_subscribe(
self.hass,
self.entity_description.mqttTopicTargetTemperature,
message_received_targtemp,
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)
def async_turn_on(self, **kwargs):
self._attr_is_on = self.payload_on
self.publishToMQTT()
def async_turn_off(self, **kwargs):
self._attr_is_on = self.payload_off
self.publishToMQTT()
def publishToMQTT(self):
topic = f"{self.entity_description.mqttTopicCommandMode}"
mqtt.publish(self.hass, topic, str(self._attr_is_on))
def set_operation_mode(self, operation_mode):
topic = f"{self.entity_description.mqttTopicCommandTemperature}"
mqtt.publish(self.hass, topic, int(self._attr_target_temperature))
topic = f"{self.entity_description.mqttTopicCommandMode}"
self._attr_current_operation = operation_mode
mqtt.publish(self.hass, topic, int(self._modes[operation_mode]))
self.schedule_update_ha_state()
def set_temperature(self, **kwargs):
self._attr_target_temperature = kwargs.get(ATTR_TEMPERATURE)
topic = f"{self.entity_description.mqttTopicCommandTemperature}"
mqtt.publish(self.hass, topic, int(self._attr_target_temperature))
self.schedule_update_ha_state()
@property
def state(self) -> str | None:
return self.current_operation
@property
def operation_list(self) -> list[str] | None:
return self._attr_operation_list
@property
def current_operation(self) -> str | None:
return self._attr_current_operation
@property
def supported_features(self) -> WaterHeaterEntityFeature:
return self._attr_supported_features
@property
def min_temp(self) -> float:
return self._attr_min_temp
@property
def max_temp(self) -> float:
return self._attr_max_temp