From d34753a96514310d32f654fcd9534b5c96bc80ef Mon Sep 17 00:00:00 2001 From: xrh0905 <1014930533@qq.com> Date: Fri, 30 Jun 2023 23:50:37 +0800 Subject: [PATCH 1/3] Small improvements Add author info Update VOC Device Class Support Temperature Control at 0.5 Remove air quality Update AUTO Mode Logic Fix AC & Sensors Device info Allow fallback to AUTO or RELAX or COLD when exit SLEEP Try to refactor config flow --- custom_components/ds_air/air_quality.py | 55 --------- custom_components/ds_air/climate.py | 111 ++++++++++++------ custom_components/ds_air/config_flow.py | 101 ++++++++++++---- custom_components/ds_air/const.py | 16 +-- .../ds_air/ds_air_service/ctrl_enum.py | 5 +- custom_components/ds_air/manifest.json | 2 +- custom_components/ds_air/sensor.py | 2 +- custom_components/ds_air/strings.json | 74 ++++++++---- custom_components/ds_air/translations/en.json | 35 +++++- .../ds_air/translations/zh-Hans.json | 31 ++++- 10 files changed, 280 insertions(+), 152 deletions(-) delete mode 100644 custom_components/ds_air/air_quality.py diff --git a/custom_components/ds_air/air_quality.py b/custom_components/ds_air/air_quality.py deleted file mode 100644 index 656e222..0000000 --- a/custom_components/ds_air/air_quality.py +++ /dev/null @@ -1,55 +0,0 @@ -"""Demo platform that offers fake air quality data.""" -from homeassistant.components.air_quality import AirQualityEntity - - -async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): - """Set up the Air Quality.""" - async_add_entities( - [DemoAirQuality("Home", 14, 23, 100), DemoAirQuality("Office", 4, 16, None)] - ) - - -async def async_setup_entry(hass, config_entry, async_add_entities): - """Set up the Demo config entry.""" - await async_setup_platform(hass, {}, async_add_entities) - - -class DemoAirQuality(AirQualityEntity): - """Representation of Air Quality data.""" - - def __init__(self, name, pm_2_5, pm_10, n2o): - """Initialize the Demo Air Quality.""" - self._name = name - self._pm_2_5 = pm_2_5 - self._pm_10 = pm_10 - self._n2o = n2o - - @property - def name(self): - """Return the name of the sensor.""" - return f"Demo Air Quality {self._name}" - - @property - def should_poll(self): - """No polling needed for Demo Air Quality.""" - return False - - @property - def particulate_matter_2_5(self): - """Return the particulate matter 2.5 level.""" - return self._pm_2_5 - - @property - def particulate_matter_10(self): - """Return the particulate matter 10 level.""" - return self._pm_10 - - @property - def nitrogen_oxide(self): - """Return the nitrogen oxide (N2O) level.""" - return self._n2o - - @property - def attribution(self): - """Return the attribution.""" - return "Powered by Home Assistant" diff --git a/custom_components/ds_air/climate.py b/custom_components/ds_air/climate.py index c554093..5f808a3 100644 --- a/custom_components/ds_air/climate.py +++ b/custom_components/ds_air/climate.py @@ -1,5 +1,5 @@ """ -Demo platform that offers a fake climate device. +Daikin platform that offers climate devices. For more details about this platform, please refer to the documentation https://home-assistant.io/components/demo/ @@ -11,13 +11,22 @@ 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, +""" 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, + 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) + FAN_AUTO, FAN_LOW, FAN_MEDIUM, FAN_HIGH) """ +from homeassistant.components.climate import ( + ClimateEntity, + ClimateEntityFeature, + HVACMode, + PRESET_NONE, PRESET_SLEEP, + 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.core import HomeAssistant, Event @@ -32,10 +41,9 @@ from .ds_air_service.dao import AirCon, AirConStatus from .ds_air_service.display import display -SUPPORT_FLAGS = SUPPORT_TARGET_TEMPERATURE | SUPPORT_FAN_MODE | SUPPORT_SWING_MODE \ - | SUPPORT_SWING_MODE | SUPPORT_TARGET_HUMIDITY -#FAN_LIST = ['最弱', '稍弱', '中等', '稍强', '最强', '自动'] -FAN_LIST = [FAN_LOW, '稍弱', FAN_MEDIUM, '稍强', FAN_HIGH, FAN_AUTO] +_SUPPORT_FLAGS = ClimateEntityFeature.TARGET_TEMPERATURE | ClimateEntityFeature.FAN_MODE | ClimateEntityFeature.PRESET_MODE +# | ClimateEntityFeature.SWING_MODE | ClimateEntityFeature.TARGET_HUMIDITY +FAN_LIST = [ FAN_LOW, '稍弱', FAN_MEDIUM, '稍强', FAN_HIGH, FAN_AUTO] SWING_LIST = ['➡️', '↘️', '⬇️', '↙️', '⬅️', '↔️', '🔄'] PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ @@ -88,7 +96,7 @@ async def listener(event: Event): class DsAir(ClimateEntity): - """Representation of a demo climate device.""" + """Representation of a Daikin climate device.""" def __init__(self, aircon: AirCon): _log('create aircon:') @@ -195,7 +203,7 @@ def hvac_mode(self) -> str: Need to be one of HVAC_MODE_*. """ if self._device_info.status.switch == EnumControl.Switch.OFF: - return HVAC_MODE_OFF + return HVACMode.OFF else: return EnumControl.get_mode_name(self._device_info.status.mode.value) @@ -205,18 +213,16 @@ def hvac_modes(self): li = [] aircon = self._device_info if aircon.cool_mode: - li.append(HVAC_MODE_COOL) + li.append(HVACMode.COOL) if aircon.heat_mode or aircon.pre_heat_mode: - li.append(HVAC_MODE_HEAT) + li.append(HVACMode.HEAT) if aircon.auto_dry_mode or aircon.dry_mode or aircon.more_dry_mode: - li.append(HVAC_MODE_DRY) + li.append(HVACMode.DRY) if aircon.ventilation_mode: - li.append(HVAC_MODE_FAN_ONLY) - if aircon.relax_mode or aircon.auto_mode: - li.append(HVAC_MODE_AUTO) - if aircon.sleep_mode: - li.append(HVAC_MODE_HEAT_COOL) - li.append(HVAC_MODE_OFF) + li.append(HVACMode.FAN_ONLY) + if aircon.relax_mode or aircon.sleep_mode or aircon.auto_mode: + li.append(HVACMode.AUTO) + li.append(HVACMode.OFF) return li @property @@ -238,7 +244,7 @@ def target_temperature(self): @property def target_temperature_step(self): """Return the supported step of target temperature.""" - return 1 + return 0.5 @property def target_temperature_high(self): @@ -264,7 +270,10 @@ def preset_mode(self) -> Optional[str]: Requires SUPPORT_PRESET_MODE. """ - return None + if self._device_info.status.mode == EnumControl.Mode.SLEEP: + return PRESET_SLEEP + else: + return PRESET_NONE @property def preset_modes(self) -> Optional[List[str]]: @@ -272,7 +281,12 @@ def preset_modes(self) -> Optional[List[str]]: Requires SUPPORT_PRESET_MODE. """ - return None + result = [] + aircon = self._device_info + if aircon.sleep_mode: + result.append(PRESET_SLEEP) + result.append(PRESET_NONE) + return result @property def is_aux_heat(self): @@ -312,8 +326,8 @@ def set_temperature(self, **kwargs): new_status = AirConStatus() if status.switch == EnumControl.Switch.ON \ and status.mode not in [EnumControl.Mode.VENTILATION, EnumControl.Mode.MOREDRY]: - status.setted_temp = round(kwargs.get(ATTR_TEMPERATURE)) * 10 - new_status.setted_temp = round(kwargs.get(ATTR_TEMPERATURE)) * 10 + status.setted_temp = round(kwargs.get(ATTR_TEMPERATURE) * 10.0) + new_status.setted_temp = round(kwargs.get(ATTR_TEMPERATURE) * 10.0) from .ds_air_service.service import Service Service.control(self._device_info, new_status) self.schedule_update_ha_state() @@ -347,7 +361,7 @@ def set_hvac_mode(self, hvac_mode: str) -> None: aircon = self._device_info status = aircon.status new_status = AirConStatus() - if hvac_mode == HVAC_MODE_OFF: + if hvac_mode == HVACMode.OFF: status.switch = EnumControl.Switch.OFF new_status.switch = EnumControl.Switch.OFF from .ds_air_service.service import Service @@ -357,29 +371,29 @@ def set_hvac_mode(self, hvac_mode: str) -> None: new_status.switch = EnumControl.Switch.ON m = EnumControl.Mode mode = None - if hvac_mode == HVAC_MODE_COOL: + if hvac_mode == HVACMode.COOL: mode = m.COLD - elif hvac_mode == HVAC_MODE_HEAT: + elif hvac_mode == HVACMode.HEAT: if aircon.heat_mode: mode = m.HEAT else: mode = m.PREHEAT - elif hvac_mode == HVAC_MODE_DRY: + elif hvac_mode == HVACMode.DRY: if aircon.auto_dry_mode: mode = m.AUTODRY elif aircon.more_dry_mode: mode = m.MOREDRY else: mode = m.DRY - elif hvac_mode == HVAC_MODE_FAN_ONLY: + elif hvac_mode == HVACMode.FAN_ONLY: mode = m.VENTILATION - elif hvac_mode == HVAC_MODE_AUTO: + elif hvac_mode == HVACMode.AUTO: if aircon.auto_mode: mode = m.AUTO - else: + elif aircon.relax_mode: mode = m.RELAX - elif hvac_mode == HVAC_MODE_HEAT_COOL: - mode = m.SLEEP + else: + mode = m.SLEEP status.mode = mode new_status.mode = mode from .ds_air_service.service import Service @@ -400,7 +414,26 @@ def set_swing_mode(self, swing_mode): self.schedule_update_ha_state() def set_preset_mode(self, preset_mode: str) -> None: - pass + aircon = self._device_info + status = aircon.status + new_status = AirConStatus() + m = EnumControl.Mode + mode = None + if preset_mode == PRESET_NONE: + if aircon.auto_mode: + mode = m.AUTO + elif aircon.relax_mode: + mode = m.RELAX + else: + mode = m.COLD + else: + if preset_mode == PRESET_SLEEP: + mode = m.SLEEP + status.mode = mode + new_status.mode = mode + from .ds_air_service.service import Service + Service.control(self._device_info, new_status) + self.schedule_update_ha_state() def turn_aux_heat_on(self) -> None: pass @@ -411,6 +444,12 @@ def turn_aux_heat_off(self) -> None: @property def supported_features(self) -> int: """Return the list of supported features.""" + SUPPORT_FLAGS = _SUPPORT_FLAGS + aircon = self._device_info + if self._device_info.status.fan_direction1.value > 0: + SUPPORT_FLAGS = SUPPORT_FLAGS | ClimateEntityFeature.SWING_MODE + if aircon.relax_mode: + SUPPORT_FLAGS = SUPPORT_FLAGS | ClimateEntityFeature.TARGET_HUMIDITY return SUPPORT_FLAGS @property @@ -436,7 +475,7 @@ def device_info(self) -> Optional[DeviceInfo]: return { "identifiers": {(DOMAIN, self.unique_id)}, "name": "空调%s" % self._name, - "manufacturer": "DAIKIN INDUSTRIES, Ltd." + "manufacturer": "Daikin Industries, Ltd." } @property diff --git a/custom_components/ds_air/config_flow.py b/custom_components/ds_air/config_flow.py index 4debb10..2b37261 100644 --- a/custom_components/ds_air/config_flow.py +++ b/custom_components/ds_air/config_flow.py @@ -80,15 +80,13 @@ def async_get_options_flow( """Options callback for DS-AIR.""" return DsAirOptionsFlowHandler(config_entry) - class DsAirOptionsFlowHandler(config_entries.OptionsFlow): - """Config flow options for sensors binding.""" - - def __init__(self, entry: ConfigEntry) -> None: - """Initialize DSAir options flow.""" - self.config_entry = entry - self._len = 3 - self._cur = 0 + """Config flow options for intergration""" + + 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") @@ -96,31 +94,86 @@ def __init__(self, entry: ConfigEntry) -> None: 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._config_data = [] - + 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 + self, user_input: dict[str, Any] | None = None ) -> FlowResult: """Manage the options.""" - self._len = len(self._climates) - self._cur = 0 - return await self.async_step_user() + 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: - async def async_step_user( + errors = {} + if user_input is not None: + self.user_input.update(user_input) + 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={}) + else: + 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 + ) + 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 + ) + + async def async_step_bind_sensors( self, user_input: dict[str, Any] | None = None ) -> FlowResult: - """Handle a flow initialized by the user.""" + """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") }) - if self._cur == self._len: + self._cur = self._cur + 1 + if self._cur > (self._len - 1): return self.async_create_entry(title="", data={"link": self._config_data}) - - form = self.async_show_form( - step_id="user", + return self.async_show_form( + step_id="bind_sensors", data_schema=vol.Schema( { vol.Required( @@ -133,6 +186,8 @@ async def async_step_user( ) ) - self._cur = self._cur + 1 - - return form + 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) diff --git a/custom_components/ds_air/const.py b/custom_components/ds_air/const.py index 8a4be4c..600eb71 100644 --- a/custom_components/ds_air/const.py +++ b/custom_components/ds_air/const.py @@ -1,6 +1,6 @@ from homeassistant.const import TEMP_CELSIUS, PERCENTAGE, CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, \ - CONCENTRATION_PARTS_PER_MILLION, CONCENTRATION_MILLIGRAMS_PER_CUBIC_METER, DEVICE_CLASS_HUMIDITY, \ - DEVICE_CLASS_TEMPERATURE, DEVICE_CLASS_CO2, DEVICE_CLASS_PM25 + CONCENTRATION_PARTS_PER_MILLION, CONCENTRATION_MILLIGRAMS_PER_CUBIC_METER +from homeassistant.components.sensor import SensorDeviceClass from .ds_air_service.ctrl_enum import EnumSensor @@ -11,11 +11,11 @@ DEFAULT_GW = "DTA117C611" GW_LIST = ["DTA117C611", "DTA117B611"] SENSOR_TYPES = { - "temp": [TEMP_CELSIUS, None, DEVICE_CLASS_TEMPERATURE, 10], - "humidity": [PERCENTAGE, None, DEVICE_CLASS_HUMIDITY, 10], - "pm25": [CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, None, DEVICE_CLASS_PM25, 1], - "co2": [CONCENTRATION_PARTS_PER_MILLION, None, DEVICE_CLASS_CO2, 1], - "tvoc": [CONCENTRATION_MILLIGRAMS_PER_CUBIC_METER, None, None, 100], - "voc": [None, None, None, EnumSensor.Voc], + "temp": [TEMP_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], + "tvoc": [CONCENTRATION_MILLIGRAMS_PER_CUBIC_METER, None, SensorDeviceClass.VOLATILE_ORGANIC_COMPOUNDS_PARTS, 100], + "voc": [None, None, SensorDeviceClass.VOLATILE_ORGANIC_COMPOUNDS_PARTS, EnumSensor.Voc], "hcho": [CONCENTRATION_MILLIGRAMS_PER_CUBIC_METER, None, None, 100], } diff --git a/custom_components/ds_air/ds_air_service/ctrl_enum.py b/custom_components/ds_air/ds_air_service/ctrl_enum.py index 7e66f61..a919c5a 100644 --- a/custom_components/ds_air/ds_air_service/ctrl_enum.py +++ b/custom_components/ds_air/ds_air_service/ctrl_enum.py @@ -292,9 +292,10 @@ class Mode(IntEnum): MOREDRY = 9 +#_MODE_NAME_LIST = [HVAC_MODE_COOL, HVAC_MODE_DRY, HVAC_MODE_FAN_ONLY, HVAC_MODE_AUTO, HVAC_MODE_HEAT, +# HVAC_MODE_DRY, HVAC_MODE_AUTO, HVAC_MODE_HEAT_COOL, HVAC_MODE_HEAT, HVAC_MODE_DRY] _MODE_NAME_LIST = [HVAC_MODE_COOL, HVAC_MODE_DRY, HVAC_MODE_FAN_ONLY, HVAC_MODE_AUTO, HVAC_MODE_HEAT, - HVAC_MODE_DRY, HVAC_MODE_AUTO, HVAC_MODE_HEAT_COOL, HVAC_MODE_HEAT, HVAC_MODE_DRY] - + HVAC_MODE_DRY, HVAC_MODE_AUTO, HVAC_MODE_AUTO, HVAC_MODE_HEAT, HVAC_MODE_DRY] class Switch(IntEnum): OFF = 0 diff --git a/custom_components/ds_air/manifest.json b/custom_components/ds_air/manifest.json index c4c630e..89dd009 100644 --- a/custom_components/ds_air/manifest.json +++ b/custom_components/ds_air/manifest.json @@ -3,7 +3,7 @@ "name": "DS-AIR", "documentation": "https://github.com/mypal/ha-dsair", "dependencies": [], - "codeowners": [], + "codeowners": ["@mypal"], "requirements": [], "version": "1.3.3", "config_flow": true diff --git a/custom_components/ds_air/sensor.py b/custom_components/ds_air/sensor.py index fe96bdb..d59ed49 100644 --- a/custom_components/ds_air/sensor.py +++ b/custom_components/ds_air/sensor.py @@ -45,7 +45,7 @@ def device_info(self) -> Optional[DeviceInfo]: return { "identifiers": {(DOMAIN, self._unique_id)}, "name": "传感器%s" % self._name, - "manufacturer": "DAIKIN INDUSTRIES, Ltd." + "manufacturer": "Daikin Industries, Ltd." } @property diff --git a/custom_components/ds_air/strings.json b/custom_components/ds_air/strings.json index 5d3f8c3..2f4b115 100644 --- a/custom_components/ds_air/strings.json +++ b/custom_components/ds_air/strings.json @@ -2,41 +2,71 @@ "config": { "step": { "user": { - "title": "\u91d1\u5236\u7a7a\u6c14", - "description": "\u672c\u63d2\u4ef6\u652f\u6301\u5927\u91d1DTA117B611/DTA117C611\u4e24\u6b3e\u7a7a\u8c03\u7f51\u5173", + "title": "DS-AIR", + "description": "Support DTA117B611/DTA117C611", "data": { - "host": "host", - "port": "port", - "gw": "\u7f51\u5173\u578b\u53f7", - "scan_interval": "\u4f20\u611f\u5668\u66f4\u65b0\u9891\u7387\uff08\u5355\u4f4d\uff1a\u5206\u949f\uff09", - "sensors": "\u662f\u5426\u6709\u4f20\u611f\u5668", - "temp": "\u521b\u5efatemperature\u5b9e\u4f53", - "humidity": "\u521b\u5efahumidity\u5b9e\u4f53", - "pm25": "\u521b\u5efapm25\u5b9e\u4f53", - "co2": "\u521b\u5efaco2\u5b9e\u4f53", - "tvoc": "\u521b\u5efatvoc\u5b9e\u4f53", - "voc": "\u521b\u5efavoc\u5b9e\u4f53", - "hcho": "\u521b\u5efahcho\u5b9e\u4f53" + "host": "Host", + "port": "Port", + "gw": "Gateway model", + "scan_interval": "Sensor update frequency (minutes)", + "sensors": "Has sensor", + "temp": "Create Temperature entity", + "humidity": "Create Humidity entity", + "pm25": "Create PM2.5 entity", + "co2": "Create CO2 entity", + "tvoc": "Create TVOC entity", + "voc": "Create VOC entity", + "hcho": "Create HCHO entity" } } }, "error": { }, "abort": { - "single_instance_allowed": "\u53ea\u5141\u8bb8\u521b\u5efa\u4e00\u4e2a" + "single_instance_allowed": "Only one instance is allowed." }, - "flow_title": "\u91d1\u5236\u7a7a\u6c14" + "flow_title": "DS-AIR" }, "options": { "step": { - "user": { - "title": "\u6e29\u5ea6\u4f20\u611f\u5668\u5173\u8054", - "description": "\u53ef\u4ee5\u4e3a\u7a7a\u8c03\u5173\u8054\u6e29\u5ea6\u4f20\u611f\u5668", + "init": { + "title": "DS-AIR", + "menu_options": { + "adjust_config": "Adjust config", + "bind_sensors": "Link sensors" + } + }, + "adjust_config": { + "title": "Adjust config", + "description": "", + "data": { + "host": "Gateway IP", + "port": "Gateway Port", + "gw": "Gateway model", + "scan_interval": "Sensor update frequency(min)", + "sensors": "Has sensor", + "temp": "Create Temperature entity", + "humidity": "Create Humidity entity", + "pm25": "Create PM2.5 entity", + "co2": "Create CO2 entity", + "tvoc": "Create TVOC entity", + "voc": "Create VOC entity", + "hcho": "Create HCHO entity" + } + }, + "bind_sensors": { + "title": "Link sensor", + "description": "Link sensor for AC", "data": { - "climate": "climate name", - "sensor": "sensor entity_id" + "climate": "Climate name", + "sensor_temp": "Temperature sensor entity_id", + "sensor_humi": "Humidity sensor entity_id" + }, + "empty": { + "title": "No data", + "description": "No AC for link" } } } } -} +} \ No newline at end of file diff --git a/custom_components/ds_air/translations/en.json b/custom_components/ds_air/translations/en.json index 097ef2d..2f4b115 100644 --- a/custom_components/ds_air/translations/en.json +++ b/custom_components/ds_air/translations/en.json @@ -29,13 +29,42 @@ }, "options": { "step": { - "user": { - "title": "Binding sensors", - "description": "Bind sensors to climate", + "init": { + "title": "DS-AIR", + "menu_options": { + "adjust_config": "Adjust config", + "bind_sensors": "Link sensors" + } + }, + "adjust_config": { + "title": "Adjust config", + "description": "", + "data": { + "host": "Gateway IP", + "port": "Gateway Port", + "gw": "Gateway model", + "scan_interval": "Sensor update frequency(min)", + "sensors": "Has sensor", + "temp": "Create Temperature entity", + "humidity": "Create Humidity entity", + "pm25": "Create PM2.5 entity", + "co2": "Create CO2 entity", + "tvoc": "Create TVOC entity", + "voc": "Create VOC entity", + "hcho": "Create HCHO entity" + } + }, + "bind_sensors": { + "title": "Link sensor", + "description": "Link sensor for AC", "data": { "climate": "Climate name", "sensor_temp": "Temperature sensor entity_id", "sensor_humi": "Humidity sensor entity_id" + }, + "empty": { + "title": "No data", + "description": "No AC for link" } } } diff --git a/custom_components/ds_air/translations/zh-Hans.json b/custom_components/ds_air/translations/zh-Hans.json index 6bb0206..96c2a03 100644 --- a/custom_components/ds_air/translations/zh-Hans.json +++ b/custom_components/ds_air/translations/zh-Hans.json @@ -29,13 +29,42 @@ }, "options": { "step": { - "user": { + "init": { + "title": "金制空气", + "menu_options": { + "adjust_config": "调整设置", + "bind_sensors": "关联传感器" + } + }, + "adjust_config": { + "title": "修改设置", + "description": "", + "data": { + "host": "网关IP", + "port": "网关端口", + "gw": "网关型号", + "scan_interval": "传感器更新频率(单位:分钟)", + "sensors": "是否有传感器", + "temp": "创建温度实体", + "humidity": "创建湿度实体", + "pm25": "创建PM2.5实体", + "co2": "创建CO2实体", + "tvoc": "创建tvoc实体", + "voc": "创建voc实体", + "hcho": "创建hcho实体" + } + }, + "bind_sensors": { "title": "传感器关联", "description": "为空调关联温湿度传感器", "data": { "climate": "空调名称", "sensor_temp": "温度传感器实体ID", "sensor_humi": "湿度传感器实体ID" + }, + "empty": { + "title": "无数据", + "description": "没有可操作的空调" } } } From b26d700613d4a98a488994da29cc90ac6dcb9022 Mon Sep 17 00:00:00 2001 From: xrh0905 <1014930533@qq.com> Date: Sat, 1 Jul 2023 09:35:05 +0800 Subject: [PATCH 2/3] Make RELAX matchs Preset_Comfort --- custom_components/ds_air/climate.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/custom_components/ds_air/climate.py b/custom_components/ds_air/climate.py index 5f808a3..cd0013a 100644 --- a/custom_components/ds_air/climate.py +++ b/custom_components/ds_air/climate.py @@ -24,7 +24,7 @@ ClimateEntity, ClimateEntityFeature, HVACMode, - PRESET_NONE, PRESET_SLEEP, + PRESET_NONE, PRESET_SLEEP, PRESET_COMFORT, FAN_AUTO, FAN_LOW, FAN_MEDIUM, FAN_HIGH ) from homeassistant.config_entries import ConfigEntry @@ -272,6 +272,8 @@ def preset_mode(self) -> Optional[str]: """ if self._device_info.status.mode == EnumControl.Mode.SLEEP: return PRESET_SLEEP + elif self._device_info.status.mode == EnumControl.Mode.RELAX: + return PRESET_COMFORT else: return PRESET_NONE @@ -285,6 +287,8 @@ def preset_modes(self) -> Optional[List[str]]: aircon = self._device_info if aircon.sleep_mode: result.append(PRESET_SLEEP) + if aircon.relax_mode: + result.append(PRESET_COMFORT) result.append(PRESET_NONE) return result @@ -429,6 +433,8 @@ def set_preset_mode(self, preset_mode: str) -> None: else: if preset_mode == PRESET_SLEEP: mode = m.SLEEP + elif preset_mode == PRESET_COMFORT: + mode = m.RELAX status.mode = mode new_status.mode = mode from .ds_air_service.service import Service From 7207cbd6c7be4a44b1781dba5286f99524ba63af Mon Sep 17 00:00:00 2001 From: xrh0905 <1014930533@qq.com> Date: Sun, 16 Jul 2023 16:04:39 +0800 Subject: [PATCH 3/3] Add support for HVAC Actions. --- custom_components/ds_air/climate.py | 7 +++++-- custom_components/ds_air/ds_air_service/ctrl_enum.py | 11 +++++++++-- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/custom_components/ds_air/climate.py b/custom_components/ds_air/climate.py index cd0013a..382c273 100644 --- a/custom_components/ds_air/climate.py +++ b/custom_components/ds_air/climate.py @@ -23,7 +23,7 @@ from homeassistant.components.climate import ( ClimateEntity, ClimateEntityFeature, - HVACMode, + HVACMode, HVACAction, PRESET_NONE, PRESET_SLEEP, PRESET_COMFORT, FAN_AUTO, FAN_LOW, FAN_MEDIUM, FAN_HIGH ) @@ -194,7 +194,10 @@ def target_humidity(self): @property def hvac_action(self): """Return current operation ie. heat, cool, idle.""" - return None + if self._device_info.status.switch == EnumControl.Switch.OFF: + return HVACAction.OFF + else: + return EnumControl.get_action_name(self._device_info.status.mode.value) @property def hvac_mode(self) -> str: diff --git a/custom_components/ds_air/ds_air_service/ctrl_enum.py b/custom_components/ds_air/ds_air_service/ctrl_enum.py index a919c5a..a0212d4 100644 --- a/custom_components/ds_air/ds_air_service/ctrl_enum.py +++ b/custom_components/ds_air/ds_air_service/ctrl_enum.py @@ -2,7 +2,8 @@ from homeassistant.components.climate.const import \ HVAC_MODE_COOL, HVAC_MODE_FAN_ONLY, HVAC_MODE_HEAT, HVAC_MODE_DRY, HVAC_MODE_AUTO, HVAC_MODE_HEAT_COOL, \ - FAN_AUTO, FAN_LOW, FAN_MEDIUM, FAN_HIGH + FAN_AUTO, FAN_LOW, FAN_MEDIUM, FAN_HIGH, \ + HVACAction class EnumCmdType(IntEnum): @@ -291,11 +292,14 @@ class Mode(IntEnum): PREHEAT = 8 MOREDRY = 9 - +# Legacy Mode Mapping #_MODE_NAME_LIST = [HVAC_MODE_COOL, HVAC_MODE_DRY, HVAC_MODE_FAN_ONLY, HVAC_MODE_AUTO, HVAC_MODE_HEAT, # HVAC_MODE_DRY, HVAC_MODE_AUTO, HVAC_MODE_HEAT_COOL, HVAC_MODE_HEAT, HVAC_MODE_DRY] + _MODE_NAME_LIST = [HVAC_MODE_COOL, HVAC_MODE_DRY, HVAC_MODE_FAN_ONLY, HVAC_MODE_AUTO, HVAC_MODE_HEAT, HVAC_MODE_DRY, HVAC_MODE_AUTO, HVAC_MODE_AUTO, HVAC_MODE_HEAT, HVAC_MODE_DRY] +_MODE_ACTION_LIST = [HVACAction.COOLING, HVACAction.DRYING, HVACAction.FAN, None, HVACAction.HEATING, + HVACAction.DRYING, None, None, HVACAction.PREHEATING, HVACAction.DRYING] class Switch(IntEnum): OFF = 0 @@ -329,6 +333,9 @@ class EnumControl: @staticmethod def get_mode_name(idx): return _MODE_NAME_LIST[idx] + + def get_action_name(idx): + return _MODE_ACTION_LIST[idx] @staticmethod def get_mode_enum(name):