forked from Andre0512/hon
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 31c30b1
Showing
12 changed files
with
450 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
__pycache__/ | ||
test.py |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
# hOn | ||
Home Assistant component supporting hOn cloud. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
import logging | ||
from datetime import timedelta | ||
|
||
import voluptuous as vol | ||
from pyhon import HonConnection | ||
from pyhon.device import HonDevice | ||
|
||
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 homeassistant.helpers.update_coordinator import DataUpdateCoordinator | ||
from custom_components.hon.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) | ||
) | ||
|
||
|
||
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.load_attributes() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
DOMAIN = "hon" | ||
|
||
PLATFORMS = [ | ||
"sensor", | ||
"select", | ||
"number" | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
from pyhon.device import HonDevice | ||
|
||
from .const import DOMAIN | ||
from homeassistant.helpers.entity import DeviceInfo | ||
from homeassistant.helpers.update_coordinator import CoordinatorEntity | ||
|
||
|
||
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 a device description for device registry.""" | ||
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, | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
{ | ||
"domain": "hon", | ||
"name": "hOn", | ||
"config_flow": true, | ||
"version": "0.0.1", | ||
"codeowners": ["@Andre0512"], | ||
"iot_class": "cloud_polling" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
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.core import callback | ||
from homeassistant.helpers.entity import EntityCategory | ||
from custom_components import DOMAIN, HonCoordinator | ||
from custom_components.hon import HonEntity | ||
|
||
NUMBERS: dict[str, tuple[NumberEntityDescription, ...]] = { | ||
"WM": ( | ||
NumberEntityDescription( | ||
key="delayStatus", | ||
name="delayStatus", | ||
entity_category=EntityCategory.CONFIG | ||
), | ||
NumberEntityDescription( | ||
key="delayTime", | ||
name="delayTime", | ||
icon="mdi:timer", | ||
entity_category=EntityCategory.CONFIG | ||
), | ||
NumberEntityDescription( | ||
key="haier_SoakPrewashSelection", | ||
name="haier_SoakPrewashSelection", | ||
entity_category=EntityCategory.CONFIG | ||
), | ||
NumberEntityDescription( | ||
key="rinseIterations", | ||
name="rinseIterations", | ||
entity_category=EntityCategory.CONFIG | ||
), | ||
NumberEntityDescription( | ||
key="mainWashTime", | ||
name="mainWashTime", | ||
icon="mdi:timer", | ||
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 := 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() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
"""Support for Tuya select.""" | ||
from __future__ import annotations | ||
|
||
from pyhon import HonConnection | ||
from pyhon.device import HonDevice | ||
from pyhon.parameter import HonParameterFixed | ||
|
||
from config.custom_components.hon import HonCoordinator | ||
from config.custom_components.hon.hon import HonEntity | ||
from homeassistant.components.select import SelectEntity, SelectEntityDescription | ||
from homeassistant.config_entries import ConfigEntry | ||
from homeassistant.core import callback | ||
from homeassistant.helpers.entity import EntityCategory | ||
|
||
DOMAIN = "hon" | ||
|
||
SELECTS = { | ||
"WM": ( | ||
SelectEntityDescription( | ||
key="spinSpeed", | ||
name="Spin speed", | ||
entity_category=EntityCategory.CONFIG, | ||
icon="mdi:numeric" | ||
), | ||
SelectEntityDescription( | ||
key="temp", | ||
name="Temperature", | ||
entity_category=EntityCategory.CONFIG, | ||
icon="mdi:thermometer" | ||
), | ||
SelectEntityDescription( | ||
key="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() |
Oops, something went wrong.