More functions

This commit is contained in:
Andre Basche
2023-03-03 18:23:30 +01:00
parent 31c30b1670
commit 136a1810fc
12 changed files with 140 additions and 46 deletions

View File

@ -0,0 +1,40 @@
import logging
import voluptuous as vol
from pyhon import HonConnection
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_EMAIL, CONF_PASSWORD
from homeassistant.helpers import config_validation as cv, aiohttp_client
from homeassistant.helpers.typing import HomeAssistantType
from .const import DOMAIN, PLATFORMS
_LOGGER = logging.getLogger(__name__)
HON_SCHEMA = vol.Schema(
{
vol.Required(CONF_EMAIL): cv.string,
vol.Required(CONF_PASSWORD): cv.string,
}
)
CONFIG_SCHEMA = vol.Schema(
{DOMAIN: vol.Schema(vol.All(cv.ensure_list, [HON_SCHEMA]))},
extra=vol.ALLOW_EXTRA,
)
async def async_setup_entry(hass: HomeAssistantType, entry: ConfigEntry):
session = aiohttp_client.async_get_clientsession(hass)
hon = HonConnection(entry.data["email"], entry.data["password"], session)
await hon.setup()
hass.data.setdefault(DOMAIN, {})
hass.data[DOMAIN][entry.unique_id] = hon
hass.data[DOMAIN]["coordinators"] = {}
for platform in PLATFORMS:
hass.async_create_task(
hass.config_entries.async_forward_entry_setup(entry, platform)
)

View File

@ -0,0 +1,67 @@
from pyhon import HonConnection
from pyhon.device import HonDevice
from homeassistant.components.button import ButtonEntityDescription, ButtonEntity
from homeassistant.config_entries import ConfigEntry
from .const import DOMAIN
from .hon import HonCoordinator, HonEntity
BUTTONS: dict[str, tuple[ButtonEntityDescription, ...]] = {
"WM": (
ButtonEntityDescription(
key="startProgram",
name="Start Program",
icon="mdi:play",
),
ButtonEntityDescription(
key="stopProgram",
name="Stop Program",
icon="mdi:stop",
),
ButtonEntityDescription(
key="pauseProgram",
name="Pause Program",
icon="mdi:pause",
),
ButtonEntityDescription(
key="resumeProgram",
name="Resume Program",
icon="mdi:play-pause",
),
),
}
async def async_setup_entry(hass, entry: ConfigEntry, async_add_entities) -> None:
hon: HonConnection = hass.data[DOMAIN][entry.unique_id]
coordinators = hass.data[DOMAIN]["coordinators"]
appliances = []
for device in hon.devices:
if device.mac_address in coordinators:
coordinator = hass.data[DOMAIN]["coordinators"][device.mac_address]
else:
coordinator = HonCoordinator(hass, device)
hass.data[DOMAIN]["coordinators"][device.mac_address] = coordinator
await coordinator.async_config_entry_first_refresh()
if descriptions := BUTTONS.get(device.appliance_type_name):
for description in descriptions:
appliances.extend([
HonButtonEntity(hass, coordinator, entry, device, description)]
)
async_add_entities(appliances)
class HonButtonEntity(HonEntity, ButtonEntity):
def __init__(self, hass, coordinator, entry, device: HonDevice, description) -> None:
super().__init__(hass, entry, coordinator, device)
self._coordinator = coordinator
self._device = device
self.entity_description = description
self._attr_unique_id = f"{super().unique_id}{description.key}"
async def async_press(self) -> None:
await self._device.commands[self.entity_description.key].send()

View File

@ -0,0 +1,43 @@
import logging
import voluptuous as vol
from homeassistant import config_entries
from homeassistant.const import CONF_EMAIL, CONF_PASSWORD
from .const import DOMAIN
_LOGGER = logging.getLogger(__name__)
class HonFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
VERSION = 1
CONNECTION_CLASS = config_entries.CONN_CLASS_LOCAL_POLL
def __init__(self):
self._email = None
self._password = None
async def async_step_user(self, user_input=None):
if user_input is None:
return self.async_show_form(step_id="user", data_schema=vol.Schema(
{vol.Required(CONF_EMAIL): str, vol.Required(CONF_PASSWORD): str}))
self._email = user_input[CONF_EMAIL]
self._password = user_input[CONF_PASSWORD]
# Check if already configured
await self.async_set_unique_id(self._email)
self._abort_if_unique_id_configured()
return self.async_create_entry(
title=self._email,
data={
CONF_EMAIL: self._email,
CONF_PASSWORD: self._password,
},
)
async def async_step_import(self, user_input=None):
return await self.async_step_user(user_input)

8
custom_components/hOn/const.py Executable file
View File

@ -0,0 +1,8 @@
DOMAIN = "hon"
PLATFORMS = [
"sensor",
"select",
"number",
"button"
]

45
custom_components/hOn/hon.py Executable file
View File

@ -0,0 +1,45 @@
import logging
from datetime import timedelta
from pyhon.device import HonDevice
from homeassistant.helpers.entity import DeviceInfo
from homeassistant.helpers.update_coordinator import CoordinatorEntity
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
from .const import DOMAIN
_LOGGER = logging.getLogger(__name__)
class HonEntity(CoordinatorEntity):
_attr_has_entity_name = True
def __init__(self, hass, entry, coordinator, device: HonDevice) -> None:
super().__init__(coordinator)
self._hon = hass.data[DOMAIN][entry.unique_id]
self._hass = hass
self._device = device
self._attr_unique_id = self._device.mac_address
@property
def device_info(self):
return DeviceInfo(
identifiers={(DOMAIN, self._device.mac_address)},
manufacturer=self._device.brand,
name=self._device.nick_name if self._device.nick_name else self._device.model_name,
model=self._device.model_name,
sw_version=self._device.fw_version,
)
class HonCoordinator(DataUpdateCoordinator):
def __init__(self, hass, device: HonDevice):
"""Initialize my coordinator."""
super().__init__(hass, _LOGGER, name=device.mac_address, update_interval=timedelta(seconds=30))
self._device = device
async def _async_update_data(self):
await self._device.update()

View File

@ -0,0 +1,10 @@
{
"domain": "hon",
"name": "hOn",
"config_flow": true,
"version": "0.0.1",
"codeowners": ["@Andre0512"],
"iot_class": "cloud_polling",
"requirements": ["pyhon"],
"documentation": "https://github.com/Andre0512/hOn/"
}

View File

@ -0,0 +1,104 @@
from __future__ import annotations
from pyhon import HonConnection
from pyhon.parameter import HonParameterRange
from homeassistant.components.number import (
NumberEntity,
NumberEntityDescription,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import UnitOfTime
from homeassistant.core import callback
from homeassistant.helpers.entity import EntityCategory
from .const import DOMAIN
from .hon import HonEntity, HonCoordinator
NUMBERS: dict[str, tuple[NumberEntityDescription, ...]] = {
"WM": (
NumberEntityDescription(
key="startProgram.delayStatus",
name="Delay Status",
entity_category=EntityCategory.CONFIG
),
NumberEntityDescription(
key="startProgram.delayTime",
name="Delay Time",
icon="mdi:timer",
entity_category=EntityCategory.CONFIG,
native_unit_of_measurement=UnitOfTime.MINUTES
),
NumberEntityDescription(
key="startProgram.haier_SoakPrewashSelection",
name="Soak Prewash Selection",
entity_category=EntityCategory.CONFIG
),
NumberEntityDescription(
key="startProgram.rinseIterations",
name="Rinse Iterations",
entity_category=EntityCategory.CONFIG
),
NumberEntityDescription(
key="startProgram.mainWashTime",
name="Main Wash Time",
icon="mdi:timer",
entity_category=EntityCategory.CONFIG,
native_unit_of_measurement=UnitOfTime.MINUTES
),
),
}
async def async_setup_entry(hass, entry: ConfigEntry, async_add_entities) -> None:
hon: HonConnection = hass.data[DOMAIN][entry.unique_id]
coordinators = hass.data[DOMAIN]["coordinators"]
appliances = []
for device in hon.devices:
if device.mac_address in coordinators:
coordinator = hass.data[DOMAIN]["coordinators"][device.mac_address]
else:
coordinator = HonCoordinator(hass, device)
hass.data[DOMAIN]["coordinators"][device.mac_address] = coordinator
await coordinator.async_config_entry_first_refresh()
if descriptions := NUMBERS.get(device.appliance_type_name):
for description in descriptions:
appliances.extend([
HonNumberEntity(hass, coordinator, entry, device, description)]
)
async_add_entities(appliances)
class HonNumberEntity(HonEntity, NumberEntity):
def __init__(self, hass, coordinator, entry, device, description) -> None:
super().__init__(hass, entry, coordinator, device)
self._coordinator = coordinator
self._data = device.settings[description.key]
self.entity_description = description
self._attr_unique_id = f"{super().unique_id}{description.key}"
if isinstance(self._data, HonParameterRange):
self._attr_native_max_value = self._data.max
self._attr_native_min_value = self._data.min
self._attr_native_step = self._data.step
@property
def native_value(self) -> float | None:
return self._data.value
async def async_set_native_value(self, value: float) -> None:
self._data.value = value
await self.coordinator.async_request_refresh()
@callback
def _handle_coordinator_update(self):
self._data = self._device.settings[self.entity_description.key]
if isinstance(self._data, HonParameterRange):
self._attr_native_max_value = self._data.max
self._attr_native_min_value = self._data.min
self._attr_native_step = self._data.step
self._attr_native_value = self._data.value
self.async_write_ha_state()

View File

@ -0,0 +1,98 @@
"""Support for Tuya select."""
from __future__ import annotations
from pyhon import HonConnection
from pyhon.device import HonDevice
from pyhon.parameter import HonParameterFixed
from homeassistant.components.select import SelectEntity, SelectEntityDescription
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import UnitOfTemperature, REVOLUTIONS_PER_MINUTE
from homeassistant.core import callback
from homeassistant.helpers.entity import EntityCategory
from .hon import HonEntity, HonCoordinator
DOMAIN = "hon"
SELECTS = {
"WM": (
SelectEntityDescription(
key="startProgram.spinSpeed",
name="Spin speed",
entity_category=EntityCategory.CONFIG,
icon="mdi:numeric",
unit_of_measurement=REVOLUTIONS_PER_MINUTE
),
SelectEntityDescription(
key="startProgram.temp",
name="Temperature",
entity_category=EntityCategory.CONFIG,
icon="mdi:thermometer",
unit_of_measurement=UnitOfTemperature.CELSIUS
),
SelectEntityDescription(
key="startProgram.program",
name="Programme",
entity_category=EntityCategory.CONFIG
),
)
}
async def async_setup_entry(hass, entry: ConfigEntry, async_add_entities) -> None:
hon: HonConnection = hass.data[DOMAIN][entry.unique_id]
coordinators = hass.data[DOMAIN]["coordinators"]
appliances = []
for device in hon.devices:
if device.mac_address in coordinators:
coordinator = hass.data[DOMAIN]["coordinators"][device.mac_address]
else:
coordinator = HonCoordinator(hass, device)
hass.data[DOMAIN]["coordinators"][device.mac_address] = coordinator
await coordinator.async_config_entry_first_refresh()
if descriptions := SELECTS.get(device.appliance_type_name):
for description in descriptions:
appliances.extend([
HonSelectEntity(hass, coordinator, entry, device, description)]
)
async_add_entities(appliances)
class HonSelectEntity(HonEntity, SelectEntity):
def __init__(self, hass, coordinator, entry, device: HonDevice, description) -> None:
super().__init__(hass, entry, coordinator, device)
self._coordinator = coordinator
self._device = device
self._data = device.settings[description.key]
self.entity_description = description
self._attr_unique_id = f"{super().unique_id}{description.key}"
if not isinstance(self._data, HonParameterFixed):
self._attr_options: list[str] = self._data.values
else:
self._attr_options = [self._data.value]
@property
def current_option(self) -> str | None:
value = self._data.value
if value is None or value not in self._attr_options:
return None
return value
async def async_select_option(self, option: str) -> None:
self._data.value = option
await self.coordinator.async_request_refresh()
@callback
def _handle_coordinator_update(self):
self._data = self._device.settings[self.entity_description.key]
if not isinstance(self._data, HonParameterFixed):
self._attr_options: list[str] = self._data.values
else:
self._attr_options = [self._data.value]
self._attr_native_value = self._data.value
self.async_write_ha_state()

View File

@ -0,0 +1,105 @@
import logging
from pyhon import HonConnection
from homeassistant.components.sensor import (
SensorEntity,
SensorDeviceClass,
SensorStateClass,
SensorEntityDescription,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import UnitOfEnergy, UnitOfVolume, UnitOfMass
from homeassistant.core import callback
from homeassistant.helpers.entity import EntityCategory
from homeassistant.helpers.typing import StateType
from .const import DOMAIN
from .hon import HonCoordinator, HonEntity
_LOGGER = logging.getLogger(__name__)
SENSORS: dict[str, tuple[SensorEntityDescription, ...]] = {
"WM": (
SensorEntityDescription(
key="totalElectricityUsed",
name="Total Power",
device_class=SensorDeviceClass.ENERGY,
state_class=SensorStateClass.TOTAL_INCREASING,
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR
),
SensorEntityDescription(
key="totalWaterUsed",
name="Total Water",
device_class=SensorDeviceClass.WATER,
state_class=SensorStateClass.TOTAL_INCREASING,
native_unit_of_measurement=UnitOfVolume.LITERS
),
SensorEntityDescription(
key="totalWashCycle",
name="Total Wash Cycle",
state_class=SensorStateClass.TOTAL_INCREASING,
icon="mdi:counter"
),
SensorEntityDescription(
key="currentElectricityUsed",
name="Current Electricity Used",
state_class=SensorStateClass.MEASUREMENT,
icon="mdi:lightning-bolt"
),
SensorEntityDescription(
key="currentWaterUsed",
name="Current Water Used",
state_class=SensorStateClass.MEASUREMENT,
icon="mdi:water"
),
SensorEntityDescription(
key="startProgram.weight",
name="Weight",
state_class=SensorStateClass.MEASUREMENT,
entity_category=EntityCategory.CONFIG,
native_unit_of_measurement=UnitOfMass.KILOGRAMS,
icon="mdi:weight-kilogram"
),
)
}
async def async_setup_entry(hass, entry: ConfigEntry, async_add_entities) -> None:
hon: HonConnection = hass.data[DOMAIN][entry.unique_id]
coordinators = hass.data[DOMAIN]["coordinators"]
appliances = []
for device in hon.devices:
if device.mac_address in coordinators:
coordinator = hass.data[DOMAIN]["coordinators"][device.mac_address]
else:
coordinator = HonCoordinator(hass, device)
hass.data[DOMAIN]["coordinators"][device.mac_address] = coordinator
await coordinator.async_config_entry_first_refresh()
if descriptions := SENSORS.get(device.appliance_type_name):
for description in descriptions:
appliances.extend([
HonSensorEntity(hass, coordinator, entry, device, description)]
)
async_add_entities(appliances)
class HonSensorEntity(HonEntity, SensorEntity):
def __init__(self, hass, coordinator, entry, device, description) -> None:
super().__init__(hass, entry, coordinator, device)
self._coordinator = coordinator
self.entity_description = description
self._attr_unique_id = f"{super().unique_id}{description.key}"
@property
def native_value(self) -> StateType:
return self._device.data.get(self.entity_description.key, "")
@callback
def _handle_coordinator_update(self):
self._attr_native_value = self._device.data.get(self.entity_description.key, "")
self.async_write_ha_state()

View File

@ -0,0 +1,14 @@
{
"config": {
"step": {
"user": {
"title": "hOn",
"description": "Please enters your hOn credentials",
"data": {
"email": "Email Address",
"password": "Password"
}
}
}
}
}