Skip to content

Commit

Permalink
Merge pull request #68 from Necroneco/patch
Browse files Browse the repository at this point in the history
Show friendly name when bind sensors & Fix warning in logs
  • Loading branch information
mypal authored Jun 26, 2024
2 parents 359ef35 + bc0032c commit 76be4b9
Show file tree
Hide file tree
Showing 4 changed files with 128 additions and 117 deletions.
21 changes: 8 additions & 13 deletions custom_components/ds_air/climate.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,26 +9,16 @@
from typing import Optional, List

import voluptuous as vol
from homeassistant.components.climate import ClimateEntity
from homeassistant.components.climate import PLATFORM_SCHEMA
""" from homeassistant.components.climate.const import (
SUPPORT_TARGET_TEMPERATURE,
SUPPORT_FAN_MODE,
SUPPORT_SWING_MODE,
SUPPORT_TARGET_HUMIDITY,
HVAC_MODE_OFF, HVAC_MODE_HEAT, HVAC_MODE_COOL, HVAC_MODE_HEAT_COOL, HVAC_MODE_AUTO,
HVAC_MODE_DRY,
HVAC_MODE_FAN_ONLY,
FAN_AUTO, FAN_LOW, FAN_MEDIUM, FAN_HIGH) """
from homeassistant.components.climate import (
PLATFORM_SCHEMA,
ClimateEntity,
ClimateEntityFeature,
HVACMode, HVACAction,
PRESET_NONE, PRESET_SLEEP, PRESET_COMFORT,
FAN_AUTO, FAN_LOW, FAN_MEDIUM, FAN_HIGH
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import TEMP_CELSIUS, ATTR_TEMPERATURE, CONF_HOST, CONF_PORT
from homeassistant.const import MAJOR_VERSION, MINOR_VERSION, UnitOfTemperature, ATTR_TEMPERATURE, CONF_HOST, CONF_PORT
from homeassistant.core import HomeAssistant, Event
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.entity import DeviceInfo
Expand All @@ -43,6 +33,9 @@

_SUPPORT_FLAGS = ClimateEntityFeature.TARGET_TEMPERATURE | ClimateEntityFeature.FAN_MODE | ClimateEntityFeature.PRESET_MODE
# | ClimateEntityFeature.SWING_MODE | ClimateEntityFeature.TARGET_HUMIDITY
if (MAJOR_VERSION, MINOR_VERSION) >= (2024, 2):
_SUPPORT_FLAGS |= ClimateEntityFeature.TURN_ON | ClimateEntityFeature.TURN_OFF

FAN_LIST = [ FAN_LOW, '稍弱', FAN_MEDIUM, '稍强', FAN_HIGH, FAN_AUTO]
SWING_LIST = ['➡️', '↘️', '⬇️', '↙️', '⬅️', '↔️', '🔄']

Expand Down Expand Up @@ -98,6 +91,8 @@ async def listener(event: Event):
class DsAir(ClimateEntity):
"""Representation of a Daikin climate device."""

_enable_turn_on_off_backwards_compatibility = False # used in 2024.2~2024.12

def __init__(self, aircon: AirCon):
_log('create aircon:')
_log(str(aircon.__dict__))
Expand Down Expand Up @@ -184,7 +179,7 @@ def name(self):
@property
def temperature_unit(self):
"""Return the unit of measurement."""
return TEMP_CELSIUS
return UnitOfTemperature.CELSIUS

@property
def target_humidity(self):
Expand Down
198 changes: 105 additions & 93 deletions custom_components/ds_air/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,27 @@

import voluptuous as vol
from homeassistant import config_entries
from homeassistant.components.sensor import SensorDeviceClass
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_HOST, CONF_PORT, CONF_SCAN_INTERVAL, CONF_SENSORS
from homeassistant.core import callback, HomeAssistant
from homeassistant.const import (
ATTR_DEVICE_CLASS,
ATTR_FRIENDLY_NAME,
CONF_HOST,
CONF_PORT,
CONF_SCAN_INTERVAL,
CONF_SENSORS,
)
from homeassistant.core import HomeAssistant, callback
from homeassistant.data_entry_flow import FlowResult

from .const import DOMAIN, CONF_GW, DEFAULT_GW, DEFAULT_PORT, GW_LIST, DEFAULT_HOST
from .const import CONF_GW, DEFAULT_GW, DEFAULT_HOST, DEFAULT_PORT, DOMAIN, GW_LIST
from .ds_air_service.service import Service
from .hass_inst import GetHass

_LOGGER = logging.getLogger(__name__)


def _log(s: str) -> object:
def _log(s: str) -> None:
s = str(s)
for i in s.split("\n"):
_LOGGER.debug(i)
Expand All @@ -33,161 +41,165 @@ def __init__(self):
self.sensor_check = {}
self.user_input = {}

async def async_step_user(
self, user_input: dict[str, Any] | None = None
) -> FlowResult:

async def async_step_user(self, user_input: dict[str, Any] | None = None) -> FlowResult:
if self._async_current_entries():
return self.async_abort(reason="single_instance_allowed")

errors = {}
if user_input is not None:
self.user_input.update(user_input)
if user_input.get(CONF_SENSORS) == False or user_input.get("temp") is not None:
return self.async_create_entry(
title="金制空气", data=self.user_input
)
return self.async_create_entry(title="金制空气", data=self.user_input)
else:
return self.async_show_form(
step_id="user",
data_schema=vol.Schema({
vol.Required("temp", default=True): bool,
vol.Required("humidity", default=True): bool,
vol.Required("pm25", default=True): bool,
vol.Required("co2", default=True): bool,
vol.Required("tvoc", default=True): bool,
vol.Required("voc", default=False): bool,
vol.Required("hcho", default=False): bool,
}), errors=errors
data_schema=vol.Schema(
{
vol.Required("temp", default=True): bool,
vol.Required("humidity", default=True): bool,
vol.Required("pm25", default=True): bool,
vol.Required("co2", default=True): bool,
vol.Required("tvoc", default=True): bool,
vol.Required("voc", default=False): bool,
vol.Required("hcho", default=False): bool,
}
),
errors=errors,
)

return self.async_show_form(
step_id="user",
data_schema=vol.Schema({
vol.Required(CONF_HOST, default=DEFAULT_HOST): str,
vol.Required(CONF_PORT, default=DEFAULT_PORT): int,
vol.Required(CONF_GW, default=DEFAULT_GW): vol.In(GW_LIST),
vol.Required(CONF_SCAN_INTERVAL, default=5): int,
vol.Required(CONF_SENSORS, default=True): bool
}), errors=errors
data_schema=vol.Schema(
{
vol.Required(CONF_HOST, default=DEFAULT_HOST): str,
vol.Required(CONF_PORT, default=DEFAULT_PORT): int,
vol.Required(CONF_GW, default=DEFAULT_GW): vol.In(GW_LIST),
vol.Required(CONF_SCAN_INTERVAL, default=5): int,
vol.Required(CONF_SENSORS, default=True): bool,
}
),
errors=errors,
)

@staticmethod
@callback
def async_get_options_flow(
config_entry: ConfigEntry,
) -> DsAirOptionsFlowHandler:
def async_get_options_flow(config_entry: ConfigEntry) -> DsAirOptionsFlowHandler:
"""Options callback for DS-AIR."""
return DsAirOptionsFlowHandler(config_entry)


class DsAirOptionsFlowHandler(config_entries.OptionsFlow):
"""Config flow options for intergration"""
"""Config flow options for integration"""

def __init__(self, config_entry: config_entries.ConfigEntry) -> None:
"""Initialize options flow."""
self.config_entry = config_entry
self._config_data = []
hass: HomeAssistant = GetHass.get_hash()
self._climates = list(map(lambda state: state.alias, Service.get_aircons()))
sensors = hass.states.async_all("sensor")
self._sensors_temp = list(map(lambda state: state.entity_id,
filter(lambda state: state.attributes.get("device_class") == "temperature", sensors)))
self._sensors_humi = list(map(lambda state: state.entity_id,
filter(lambda state: state.attributes.get("device_class") == "humidity", sensors)))
self._sensors_temp = {
state.entity_id: f"{state.attributes.get(ATTR_FRIENDLY_NAME, state.entity_id)} ({state.entity_id})"
for state in sensors
if state.attributes.get(ATTR_DEVICE_CLASS) == SensorDeviceClass.TEMPERATURE
}
self._sensors_humi = {
state.entity_id: f"{state.attributes.get(ATTR_FRIENDLY_NAME, state.entity_id)} ({state.entity_id})"
for state in sensors
if state.attributes.get(ATTR_DEVICE_CLASS) == SensorDeviceClass.HUMIDITY
}
self._len = len(self._climates)
self._cur = -1
self.host = CONF_HOST
self.port = CONF_PORT
self.gw = CONF_GW
self.sensor_check = CONF_SENSORS
self.user_input = {}

async def async_step_init(
self, user_input: dict[str, Any] | None = None
) -> FlowResult:

async def async_step_init(self, user_input: dict[str, Any] | None = None) -> FlowResult:
"""Manage the options."""
return self.async_show_menu(
step_id="init",
menu_options=[
"adjust_config",
"bind_sensors"
],
)

async def async_step_adjust_config(
self, user_input: dict[str, Any] | None = None
) -> FlowResult:
menu_options=["adjust_config", "bind_sensors"],
)

async def async_step_adjust_config(self, user_input: dict[str, Any] | None = None) -> FlowResult:
errors = {}
if user_input is not None:
self.user_input.update(user_input)
if self.user_input.get('_invaild'):
self.user_input['_invaild'] = False
if self.user_input.get("_invaild"):
self.user_input["_invaild"] = False
self.hass.config_entries.async_update_entry(self.config_entry, data=self.user_input)
return self.async_create_entry(title='', data={})
return self.async_create_entry(title="", data={})
else:
self.user_input['_invaild'] = True
self.user_input["_invaild"] = True
if CONF_SENSORS:
return self.async_show_form(
step_id="adjust_config",
data_schema=vol.Schema({
vol.Required(CONF_HOST, default=self.config_entry.data[CONF_HOST]): str,
vol.Required(CONF_PORT, default=self.config_entry.data[CONF_PORT]): int,
vol.Required(CONF_GW, default=self.config_entry.data[CONF_GW]): vol.In(GW_LIST),
vol.Required(CONF_SCAN_INTERVAL, default=self.config_entry.data[CONF_SCAN_INTERVAL]): int,
vol.Required(CONF_SENSORS, default=True): bool,
vol.Required("temp", default=self.config_entry.data["temp"]): bool,
vol.Required("humidity", default=self.config_entry.data["humidity"]): bool,
vol.Required("pm25", default=self.config_entry.data["pm25"]): bool,
vol.Required("co2", default=self.config_entry.data["co2"]): bool,
vol.Required("tvoc", default=self.config_entry.data["tvoc"]): bool,
vol.Required("voc", default=self.config_entry.data["voc"]): bool,
vol.Required("hcho", default=self.config_entry.data["hcho"]): bool,
}), errors=errors
data_schema=vol.Schema(
{
vol.Required(CONF_HOST, default=self.config_entry.data[CONF_HOST]): str,
vol.Required(CONF_PORT, default=self.config_entry.data[CONF_PORT]): int,
vol.Required(CONF_GW, default=self.config_entry.data[CONF_GW]): vol.In(GW_LIST),
vol.Required(CONF_SCAN_INTERVAL, default=self.config_entry.data[CONF_SCAN_INTERVAL]): int,
vol.Required(CONF_SENSORS, default=True): bool,
vol.Required("temp", default=self.config_entry.data["temp"]): bool,
vol.Required("humidity", default=self.config_entry.data["humidity"]): bool,
vol.Required("pm25", default=self.config_entry.data["pm25"]): bool,
vol.Required("co2", default=self.config_entry.data["co2"]): bool,
vol.Required("tvoc", default=self.config_entry.data["tvoc"]): bool,
vol.Required("voc", default=self.config_entry.data["voc"]): bool,
vol.Required("hcho", default=self.config_entry.data["hcho"]): bool,
}
),
errors=errors,
)
else:
return self.async_show_form(
step_id="adjust_config",
data_schema=vol.Schema({
vol.Required(CONF_HOST, default=self.config_entry.data[CONF_HOST]): str,
vol.Required(CONF_PORT, default=self.config_entry.data[CONF_PORT]): int,
vol.Required(CONF_GW, default=self.config_entry.data[CONF_GW]): vol.In(GW_LIST),
vol.Required(CONF_SCAN_INTERVAL, default=self.config_entry.data[CONF_SCAN_INTERVAL]): int,
vol.Required(CONF_SENSORS, default=False): bool
}), errors=errors
data_schema=vol.Schema(
{
vol.Required(CONF_HOST, default=self.config_entry.data[CONF_HOST]): str,
vol.Required(CONF_PORT, default=self.config_entry.data[CONF_PORT]): int,
vol.Required(CONF_GW, default=self.config_entry.data[CONF_GW]): vol.In(GW_LIST),
vol.Required(CONF_SCAN_INTERVAL, default=self.config_entry.data[CONF_SCAN_INTERVAL]): int,
vol.Required(CONF_SENSORS, default=False): bool,
}
),
errors=errors,
)

async def async_step_bind_sensors(
self, user_input: dict[str, Any] | None = None
) -> FlowResult:

async def async_step_bind_sensors(self, user_input: dict[str, Any] | None = None) -> FlowResult:
"""Handle bind flow."""
if self._len == 0:
return self.async_show_form(step_id="empty", last_step=False)
if user_input is not None:
self._config_data.append({
"climate": user_input.get("climate"),
"sensor_temp": user_input.get("sensor_temp"),
"sensor_humi": user_input.get("sensor_humi")
})
self._config_data.append(
{
"climate": user_input.get("climate"),
"sensor_temp": user_input.get("sensor_temp"),
"sensor_humi": user_input.get("sensor_humi"),
}
)
self._cur = self._cur + 1
if self._cur > (self._len - 1):
return self.async_create_entry(title="", data={"link": self._config_data})
cur_climate: str = self._climates[self._cur]
cur_links = self.config_entry.options.get("link", [])
cur_link = next(link for link in cur_links if link["climate"] == cur_climate)
cur_sensor_temp = cur_link.get("sensor_temp") if cur_link else None
cur_sensor_humi = cur_link.get("sensor_humi") if cur_link else None
return self.async_show_form(
step_id="bind_sensors",
data_schema=vol.Schema(
{
vol.Required(
"climate",
default=self._climates[self._cur]
): vol.In([self._climates[self._cur]]),
vol.Optional("sensor_temp"): vol.In(self._sensors_temp),
vol.Optional("sensor_humi"): vol.In(self._sensors_humi)
vol.Required("climate", default=cur_climate): vol.In([cur_climate]),
vol.Optional("sensor_temp", default=cur_sensor_temp): vol.In(self._sensors_temp),
vol.Optional("sensor_humi", default=cur_sensor_humi): vol.In(self._sensors_humi),
}
)
),
)

async def async_step_empty(
self, user_input: dict[str, Any] | None = None
) -> FlowResult:
async def async_step_empty(self, user_input: dict[str, Any] | None = None) -> FlowResult:
"""No AC found."""
return await self.async_step_init(user_input)
4 changes: 2 additions & 2 deletions custom_components/ds_air/const.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from homeassistant.const import TEMP_CELSIUS, PERCENTAGE, CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, \
from homeassistant.const import UnitOfTemperature, PERCENTAGE, CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, \
CONCENTRATION_PARTS_PER_MILLION, CONCENTRATION_MILLIGRAMS_PER_CUBIC_METER
from homeassistant.components.sensor import SensorDeviceClass

Expand All @@ -11,7 +11,7 @@
DEFAULT_GW = "DTA117C611"
GW_LIST = ["DTA117C611", "DTA117B611"]
SENSOR_TYPES = {
"temp": [TEMP_CELSIUS, None, SensorDeviceClass.TEMPERATURE, 10],
"temp": [UnitOfTemperature.CELSIUS, None, SensorDeviceClass.TEMPERATURE, 10],
"humidity": [PERCENTAGE, None, SensorDeviceClass.HUMIDITY, 10],
"pm25": [CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, None, SensorDeviceClass.PM25, 1],
"co2": [CONCENTRATION_PARTS_PER_MILLION, None, SensorDeviceClass.CO2, 1],
Expand Down
Loading

0 comments on commit 76be4b9

Please sign in to comment.