diff --git a/custom_components/ge_home/devices/dehumidifier.py b/custom_components/ge_home/devices/dehumidifier.py index 1f1e0c0..e91dc27 100644 --- a/custom_components/ge_home/devices/dehumidifier.py +++ b/custom_components/ge_home/devices/dehumidifier.py @@ -15,8 +15,7 @@ from ..entities import ( GeErdPropertySensor, GeErdSwitch, ErdOnOffBoolConverter, - DehumidifierTargetHumiditySelect, - DehumidifierFanSettingOptionsConverter + GeDehumidifier ) _LOGGER = logging.getLogger(__name__) @@ -31,11 +30,12 @@ class DehumidifierApi(ApplianceApi): dhum_entities = [ GeErdSwitch(self, ErdCode.AC_POWER_STATUS, bool_converter=ErdOnOffBoolConverter(), icon_on_override="mdi:power-on", icon_off_override="mdi:power-off"), - GeErdSelect(self, ErdCode.AC_FAN_SETTING, DehumidifierFanSettingOptionsConverter(), icon_override="mdi:fan"), - GeErdSensor(self, ErdCode.DHUM_CURRENT_HUMIDITY, uom_override="%", icon_override="mdi:water-percent"), - DehumidifierTargetHumiditySelect(self, ErdCode.DHUM_TARGET_HUMIDITY, icon_override="mdi:water-percent"), + GeErdSensor(self, ErdCode.AC_FAN_SETTING, icon_override="mdi:fan"), + GeErdSensor(self, ErdCode.DHUM_CURRENT_HUMIDITY), + GeErdSensor(self, ErdCode.DHUM_TARGET_HUMIDITY), GeErdPropertySensor(self, ErdCode.DHUM_MAINTENANCE, "empty_bucket", device_class_override="problem"), - GeErdPropertySensor(self, ErdCode.DHUM_MAINTENANCE, "clean_filter", device_class_override="problem") + GeErdPropertySensor(self, ErdCode.DHUM_MAINTENANCE, "clean_filter", device_class_override="problem"), + GeDehumidifier(self) ] entities = base_entities + dhum_entities diff --git a/custom_components/ge_home/entities/common/__init__.py b/custom_components/ge_home/entities/common/__init__.py index 0b555ca..546ef73 100644 --- a/custom_components/ge_home/entities/common/__init__.py +++ b/custom_components/ge_home/entities/common/__init__.py @@ -13,4 +13,5 @@ from .ge_erd_button import GeErdButton from .ge_erd_number import GeErdNumber from .ge_water_heater import GeAbstractWaterHeater from .ge_erd_select import GeErdSelect -from .ge_climate import GeClimate \ No newline at end of file +from .ge_climate import GeClimate +from .ge_humidifier import GeHumidifier \ No newline at end of file diff --git a/custom_components/ge_home/entities/common/ge_erd_entity.py b/custom_components/ge_home/entities/common/ge_erd_entity.py index 36b7883..25afa2a 100644 --- a/custom_components/ge_home/entities/common/ge_erd_entity.py +++ b/custom_components/ge_home/entities/common/ge_erd_entity.py @@ -145,5 +145,7 @@ class GeErdEntity(GeEntity): return "mdi:water" if self.erd_code_class == ErdCodeClass.CCM_SENSOR: return "mdi:coffee-maker" + if self.erd_code_class == ErdCodeClass.HUMIDITY: + return "mdi:water-percent" return None diff --git a/custom_components/ge_home/entities/common/ge_erd_sensor.py b/custom_components/ge_home/entities/common/ge_erd_sensor.py index d161fee..9dc917a 100644 --- a/custom_components/ge_home/entities/common/ge_erd_sensor.py +++ b/custom_components/ge_home/entities/common/ge_erd_sensor.py @@ -9,6 +9,7 @@ from homeassistant.const import ( DEVICE_CLASS_TEMPERATURE, DEVICE_CLASS_BATTERY, DEVICE_CLASS_POWER_FACTOR, + DEVICE_CLASS_HUMIDITY, TEMP_FAHRENHEIT, ) from gehomesdk import ErdCodeType, ErdCodeClass @@ -111,6 +112,8 @@ class GeErdSensor(GeErdEntity, SensorEntity): return "%" if self.device_class == DEVICE_CLASS_POWER_FACTOR: return "%" + if self.erd_code_class == ErdCodeClass.HUMIDITY: + return "%" if self.erd_code_class == ErdCodeClass.FLOW_RATE: #if self._measurement_system == ErdMeasurementUnits.METRIC: # return "lpm" @@ -135,6 +138,8 @@ class GeErdSensor(GeErdEntity, SensorEntity): return DEVICE_CLASS_POWER if self.erd_code_class == ErdCodeClass.ENERGY: return DEVICE_CLASS_ENERGY + if self.erd_code_class == ErdCodeClass.HUMIDITY: + return DEVICE_CLASS_HUMIDITY return None @@ -144,7 +149,7 @@ class GeErdSensor(GeErdEntity, SensorEntity): if self.device_class in [DEVICE_CLASS_TEMPERATURE, DEVICE_CLASS_ENERGY]: return SensorStateClass.MEASUREMENT - if self.erd_code_class in [ErdCodeClass.FLOW_RATE, ErdCodeClass.PERCENTAGE]: + if self.erd_code_class in [ErdCodeClass.FLOW_RATE, ErdCodeClass.PERCENTAGE, ErdCodeClass.HUMIDITY]: return SensorStateClass.MEASUREMENT if self.erd_code_class in [ErdCodeClass.LIQUID_VOLUME]: return SensorStateClass.TOTAL_INCREASING diff --git a/custom_components/ge_home/entities/common/ge_humidifier.py b/custom_components/ge_home/entities/common/ge_humidifier.py new file mode 100644 index 0000000..2ddb469 --- /dev/null +++ b/custom_components/ge_home/entities/common/ge_humidifier.py @@ -0,0 +1,98 @@ +import abc +import logging +from typing import Coroutine, Any, Optional + +from homeassistant.components.humidifier import HumidifierEntity, HumidifierDeviceClass +from homeassistant.components.humidifier.const import HumidifierEntityFeature + +from gehomesdk import ErdCodeType, ErdOnOff + +from ...const import DOMAIN +from ...devices import ApplianceApi +from .ge_entity import GeEntity +from .options_converter import OptionsConverter + +_LOGGER = logging.getLogger(__name__) + +class GeHumidifier(GeEntity, HumidifierEntity, metaclass=abc.ABCMeta): + """GE Humidifier Abstract Entity """ + + def __init__( + self, + api: ApplianceApi, + device_class: HumidifierDeviceClass, + power_status_erd_code: ErdCodeType, + target_humidity_erd_code: ErdCodeType, + current_humidity_erd_code: ErdCodeType, + range_min: int, + range_max: int + ): + super().__init__(api) + self._device_class = device_class + self._power_status_erd_code = power_status_erd_code + self._target_humidity_erd_code = target_humidity_erd_code + self._current_humidity_erd_code = current_humidity_erd_code + self._range_min = range_min + self._range_max = range_max + + @property + def unique_id(self) -> str: + return f"{DOMAIN}_{self.serial_or_mac}_{self._device_class}" + + @property + def name(self) -> Optional[str]: + return f"{self.serial_or_mac} {self._device_class.title()}" + + @property + def target_humidity(self) -> int | None: + return int(self.appliance.get_erd_value(self._target_humidity_erd_code)) + + @property + def current_humidity(self) -> int | None: + return int(self.appliance.get_erd_value(self._current_humidity_erd_code)) + + @property + def min_humidity(self) -> int: + return self._range_min + + @property + def max_humidity(self) -> int: + return self._range_max + + @property + def supported_features(self) -> HumidifierEntityFeature: + return HumidifierEntityFeature(HumidifierEntityFeature.MODES) + + @property + def is_on(self) -> bool: + return self.appliance.get_erd_value(self.power_status_erd_code) == ErdOnOff.ON + + @property + def device_class(self): + return self.device_class + + async def async_set_humidity(self, humidity: int) -> Coroutine[Any, Any, None]: + if self.target_humidity == humidity: + return + + _LOGGER.debug(f"Setting Target Humidity from {self.target_humidity} to {humidity}") + + # make sure we're on + if not self.is_on: + await self.async_turn_on() + + # set the mode + await self.appliance.async_set_erd_value( + self.target_humidity_erd_code, + self.humidity, + ) + + async def async_turn_on(self): + await self.appliance.async_set_erd_value( + self.power_status_erd_code, ErdOnOff.ON + ) + + async def async_turn_off(self): + await self.appliance.async_set_erd_value( + self.power_status_erd_code, ErdOnOff.OFF + ) \ No newline at end of file diff --git a/custom_components/ge_home/entities/dehumidifier/__init__.py b/custom_components/ge_home/entities/dehumidifier/__init__.py index 282c7bd..f8b9506 100644 --- a/custom_components/ge_home/entities/dehumidifier/__init__.py +++ b/custom_components/ge_home/entities/dehumidifier/__init__.py @@ -1,2 +1 @@ -from .dehumidifier_target_select import DehumidifierTargetHumiditySelect -from .dehumidifier_fan_options import DehumidifierFanSettingOptionsConverter \ No newline at end of file +from .dehumidifier import GeDehumidifier \ No newline at end of file diff --git a/custom_components/ge_home/entities/dehumidifier/const.py b/custom_components/ge_home/entities/dehumidifier/const.py new file mode 100644 index 0000000..1e5a8b3 --- /dev/null +++ b/custom_components/ge_home/entities/dehumidifier/const.py @@ -0,0 +1,3 @@ +SMART_DRY = "Smart Dry" +DEFAULT_MIN_HUMIDITY = 35 +DEFAULT_MAX_HUMIDITY = 80 \ No newline at end of file diff --git a/custom_components/ge_home/entities/dehumidifier/dehumidifier.py b/custom_components/ge_home/entities/dehumidifier/dehumidifier.py new file mode 100644 index 0000000..bb6ad0e --- /dev/null +++ b/custom_components/ge_home/entities/dehumidifier/dehumidifier.py @@ -0,0 +1,74 @@ +"""GE Home Dehumidifier""" +import logging + +from homeassistant.components.humidifier import HumidifierDeviceClass +from homeassistant.components.humidifier.const import HumidifierEntityFeature + +from gehomesdk import ErdCode, DehumidifierTargetRange + +from ...devices import ApplianceApi +from ..common import GeHumidifier +from .const import * +from .dehumidifier_fan_options import DehumidifierFanSettingOptionsConverter + +_LOGGER = logging.getLogger(__name__) + +class GeDehumidifier(GeHumidifier): + """GE Dehumidifier""" + + icon = "mdi:air-humidifier" + + def __init__(self, api: ApplianceApi): + + #try to get the range + range: DehumidifierTargetRange = api.try_get_erd_value(ErdCode.DHUM_TARGET_HUMIDITY_RANGE) + low = DEFAULT_MIN_HUMIDITY if range is None else range.min_humidity + high = DEFAULT_MAX_HUMIDITY if range is None else range.max_humidity + + #try to get the fan mode and determine feature + mode = api.try_get_erd_value(ErdCode.AC_FAN_SETTING) + self._has_fan = mode is not None + self._mode_converter = DehumidifierFanSettingOptionsConverter() + + #initialize the dehumidifier + super().__init__(api, + HumidifierDeviceClass.DEHUMIDIFIER, + ErdCode.AC_POWER_STATUS, + ErdCode.DHUM_TARGET_HUMIDITY, + ErdCode.DHUM_CURRENT_HUMIDITY, + low, + high + ) + + @property + def supported_features(self) -> HumidifierEntityFeature: + if self._has_fan: + return HumidifierEntityFeature(HumidifierEntityFeature.MODES) + else: + return HumidifierEntityFeature(0) + + @property + def mode(self) -> str | None: + if not self._has_fan: + raise NotImplementedError() + + return self._mode_converter.to_option_string( + self.appliance.get_erd_value(ErdCode.AC_FAN_SETTING) + ) + + @property + def available_modes(self) -> list[str] | None: + if not self._has_fan: + raise NotImplementedError() + + return self._mode_converter.options + + async def async_set_mode(self, mode: str) -> None: + if not self._has_fan: + raise NotImplementedError() + + """Change the selected mode.""" + _LOGGER.debug(f"Setting mode from {self.mode} to {mode}") + + new_state = self._mode_converter.from_option_string(mode) + await self.appliance.async_set_erd_value(self.erd_code, new_state) diff --git a/custom_components/ge_home/entities/dehumidifier/dehumidifier_fan_options.py b/custom_components/ge_home/entities/dehumidifier/dehumidifier_fan_options.py index 8cec48e..2d14832 100644 --- a/custom_components/ge_home/entities/dehumidifier/dehumidifier_fan_options.py +++ b/custom_components/ge_home/entities/dehumidifier/dehumidifier_fan_options.py @@ -3,16 +3,19 @@ from typing import List, Any, Optional from gehomesdk import ErdAcFanSetting from ..common import OptionsConverter +from .const import SMART_DRY _LOGGER = logging.getLogger(__name__) class DehumidifierFanSettingOptionsConverter(OptionsConverter): @property def options(self) -> List[str]: - return [i.stringify() for i in [ErdAcFanSetting.DEFAULT, ErdAcFanSetting.LOW, ErdAcFanSetting.MED, ErdAcFanSetting.HIGH]] + return [SMART_DRY] + [i.stringify() for i in [ErdAcFanSetting.LOW, ErdAcFanSetting.MED, ErdAcFanSetting.HIGH]] def from_option_string(self, value: str) -> Any: try: + if value == SMART_DRY: + return ErdAcFanSetting.DEFAULT return ErdAcFanSetting[value.upper()] except: _LOGGER.warn(f"Could not set fan setting to {value.upper()}") @@ -20,7 +23,7 @@ class DehumidifierFanSettingOptionsConverter(OptionsConverter): def to_option_string(self, value: ErdAcFanSetting) -> Optional[str]: try: if value is not None: - return value.stringify() + return SMART_DRY if value == ErdAcFanSetting.DEFAULT else value.stringify() except: pass - return ErdAcFanSetting.DEFAULT.stringify() + return SMART_DRY diff --git a/custom_components/ge_home/entities/dehumidifier/dehumidifier_target_select.py b/custom_components/ge_home/entities/dehumidifier/dehumidifier_target_select.py deleted file mode 100644 index e0a4d8d..0000000 --- a/custom_components/ge_home/entities/dehumidifier/dehumidifier_target_select.py +++ /dev/null @@ -1,55 +0,0 @@ -import logging -from typing import List, Any, Optional - -from gehomesdk import ErdCodeType, DehumidifierTargetRange -from ...devices import ApplianceApi -from ..common import GeErdSelect, OptionsConverter - -_LOGGER = logging.getLogger(__name__) - -DEFAULT_MIN_HUMIDITY = 35 -DEFAULT_MAX_HUMIDITY = 80 - -class DehumidifierTargetOptionsConverter(OptionsConverter): - def __init__(self, min = DEFAULT_MIN_HUMIDITY, max = DEFAULT_MAX_HUMIDITY) -> None: - super().__init__() - self._min = min - self._max = max - - @property - def options(self) -> List[str]: - return [str(i) for i in range(min,max) if i % 5 == 0] - - def from_option_string(self, value: str) -> Any: - return int(value) - def to_option_string(self, value: int) -> Optional[str]: - try: - if value is not None: - return str(value) - except: - return self._min - -class DehumidifierTargetHumiditySelect(GeErdSelect): - - def __init__(self, api: ApplianceApi, erd_code: ErdCodeType, erd_override: str = None): - self._low = DEFAULT_MIN_HUMIDITY - self._high = DEFAULT_MAX_HUMIDITY - - #try to get the range - value: DehumidifierTargetRange = api.try_get_erd_value(erd_code) - if value is not None: - self._low = value.min_humidity - self._high = value.max_humidity - - super().__init__(api, erd_code, DehumidifierTargetOptionsConverter(self._low, self._high), erd_override=erd_override) - - @property - def current_option(self): - return self._converter.to_option_string(self.appliance.get_erd_value(self.erd_code)) - - async def async_select_option(self, option: str) -> None: - """Change the selected option.""" - _LOGGER.debug(f"Setting select from {self.current_option} to {option}") - - new_state = self._converter.from_option_string(option) - await self.appliance.async_set_erd_value(self.erd_code, new_state) diff --git a/custom_components/ge_home/manifest.json b/custom_components/ge_home/manifest.json index 653ef7f..cae7890 100644 --- a/custom_components/ge_home/manifest.json +++ b/custom_components/ge_home/manifest.json @@ -5,7 +5,7 @@ "integration_type": "hub", "iot_class": "cloud_push", "documentation": "https://github.com/simbaja/ha_gehome", - "requirements": ["gehomesdk==0.5.17","magicattr==0.1.6","slixmpp==1.8.3"], + "requirements": ["gehomesdk==0.5.18","magicattr==0.1.6","slixmpp==1.8.3"], "codeowners": ["@simbaja"], "version": "0.6.8" }