mirror of https://github.com/simbaja/ha_gehome.git
v0.6.8 Develop -> Main (#194)
* - updated water heater naming * - initial support for built-in AC units * Add support for Built-In AC Unit Built-In AC unit operation and function is similar to a WAC. `gehomesdk` version bumped from 0.4.25 to 0.4.27 to include latest ErdApplianceType enum. * Update README.md Update README.md to include Built-In AC as supported device * - updated zero serial number detection (resolves #89) * - updated version - updated changelog * - hopefully fixed recursion bug with numbers * - added cooktop support * - fixed circular reference * Add support for fridges with reduced support for ERD codes (no turbo mode, no current temperature reporting, no temperature setpoint limit reporting, no door status reporting). This change has been tested on a Fisher & Paykel RF610AA. * - added dual dishwasher support - updated documentation - version bumps * - added water heater support * - added basic espresso maker device * - bugfixes * - rewrote initialization (resolves #99) * - added logic to prevent double registration of entities * - added correct min/max temps for water heaters * Fix CoffeeMaker after the NumberEntity refactoring * - added fix for CGY366P2TS1 (#116) to try to get the cooktop status, but fail more gracefully if it isn't supported * - fixed region setting in update coordinator * - version bump - doc update - string fixes * - updated the temperature conversion to use non-deprecated HASS methods * - updated documentation (@gleepwurp) * - more documentation updates * - updated dishwasher for new functionality - updated documentation * updated uom for liquid volume per HA specifications * - fixed typo in oven (#149) * - updated change log - fixed oven light control (#144) * - fixed issues with dishwasher (#155) - added oim descaling sensor (#154) - version bump * - updated change log * updated dual dishwasher for changes to gehomesdk (#161) Co-authored-by: na4ma4 <na4ma4@users.noreply.github.com> * await disconnect when unloading (#169) Fixes simbaja#164. * Check for upper oven light when there is a lower oven (#174) Resolves issue #121 * - added oven warming drawers - simplified oven entity logic * - fixed issues with the new oven initialization logic * - fixed bad type import for warming drawer * -added iot class (#181) * - updated the gehomesdk version requirement * - gehomesdk version bump * - added dehumidifier (#114) * - dehumidifier appliance type fix * - added oven state sensors (#175) * - updated change logs - updated sdk version requirement * - removed target select - added dehumidifier entity - sdk version bump * - updated dehumidifier icon * - added humidifier platform * - fixed typos in humidifier class * - fixed copy/paste error * - sdk version requirement bump * - sdk version bump * - updated dehumidifier to handle target precision - updated dehumidifier sensor value conversion * - missed a commit * - SDK version bump --------- Co-authored-by: Rob Schmidt <rwschmidt26@gmail.com> Co-authored-by: Federico Sevilla <federico@sevilla.id.au> Co-authored-by: alexanv1 <44785744+alexanv1@users.noreply.github.com> Co-authored-by: na4ma4 <25967676+na4ma4@users.noreply.github.com> Co-authored-by: na4ma4 <na4ma4@users.noreply.github.com> Co-authored-by: Alex Peters <alex@peters.net> Co-authored-by: Chris Petersen <154074+ex-nerd@users.noreply.github.com>
This commit is contained in:
parent
3ad1350ee0
commit
3b6e657272
11
CHANGELOG.md
11
CHANGELOG.md
|
@ -1,6 +1,17 @@
|
|||
|
||||
# GE Home Appliances (SmartHQ) Changelog
|
||||
|
||||
## 0.6.8
|
||||
|
||||
- Added Dehumidifier [#114]
|
||||
- Added oven drawer sensors
|
||||
- Added oven current state sensors [#175]
|
||||
- Added descriptors to manifest [#181]
|
||||
- Bugfix: Fixed issue with oven lights [#174]
|
||||
- Bugfix: Fixed issues with dual dishwasher [#161]
|
||||
- Bugfix: Fixed disconnection issue [#169]
|
||||
|
||||
|
||||
## 0.6.7
|
||||
|
||||
- Bugfix: fixed issues with dishwasher [#155]
|
||||
|
|
|
@ -25,7 +25,7 @@ from .oim import OimApi
|
|||
from .coffee_maker import CcmApi
|
||||
from .dual_dishwasher import DualDishwasherApi
|
||||
from .espresso_maker import EspressoMakerApi
|
||||
|
||||
from .dehumidifier import DehumidifierApi
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
@ -77,6 +77,8 @@ def get_appliance_api_type(appliance_type: ErdApplianceType) -> Type:
|
|||
return CcmApi
|
||||
if appliance_type == ErdApplianceType.ESPRESSO_MAKER:
|
||||
return EspressoMakerApi
|
||||
if appliance_type == ErdApplianceType.DEHUMIDIFIER:
|
||||
return DehumidifierApi
|
||||
|
||||
# Fallback
|
||||
return ApplianceApi
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
import logging
|
||||
from typing import List
|
||||
|
||||
from homeassistant.helpers.entity import Entity
|
||||
from gehomesdk import (
|
||||
ErdCode,
|
||||
ErdApplianceType,
|
||||
ErdOnOff
|
||||
)
|
||||
|
||||
from .base import ApplianceApi
|
||||
from ..entities import (
|
||||
GeErdSensor,
|
||||
GeErdSelect,
|
||||
GeErdPropertySensor,
|
||||
GeErdSwitch,
|
||||
ErdOnOffBoolConverter,
|
||||
GeDehumidifierFanSpeedSensor,
|
||||
GeDehumidifier
|
||||
)
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class DehumidifierApi(ApplianceApi):
|
||||
"""API class for Dehumidifier objects"""
|
||||
APPLIANCE_TYPE = ErdApplianceType.DEHUMIDIFIER
|
||||
|
||||
def get_all_entities(self) -> List[Entity]:
|
||||
base_entities = super().get_all_entities()
|
||||
|
||||
dhum_entities = [
|
||||
GeErdSwitch(self, ErdCode.AC_POWER_STATUS, bool_converter=ErdOnOffBoolConverter(), icon_on_override="mdi:power-on", icon_off_override="mdi:power-off"),
|
||||
GeDehumidifierFanSpeedSensor(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"),
|
||||
GeDehumidifier(self)
|
||||
]
|
||||
|
||||
entities = base_entities + dhum_entities
|
||||
return entities
|
||||
|
|
@ -19,18 +19,23 @@ class DualDishwasherApi(ApplianceApi):
|
|||
|
||||
lower_entities = [
|
||||
GeErdSensor(self, ErdCode.DISHWASHER_CYCLE_STATE, erd_override="lower_cycle_state", icon_override="mdi:state-machine"),
|
||||
GeErdSensor(self, ErdCode.DISHWASHER_RINSE_AGENT, erd_override="lower_rinse_agent", icon_override="mdi:shimmer"),
|
||||
GeErdSensor(self, ErdCode.DISHWASHER_TIME_REMAINING, erd_override="lower_time_remaining"),
|
||||
GeErdBinarySensor(self, ErdCode.DISHWASHER_DOOR_STATUS, erd_override="lower_door_status"),
|
||||
|
||||
#Reminders
|
||||
GeErdPropertySensor(self, ErdCode.DISHWASHER_REMINDERS, "add_rinse_aid", erd_override="lower_reminder", icon_override="mdi:shimmer"),
|
||||
GeErdPropertySensor(self, ErdCode.DISHWASHER_REMINDERS, "clean_filter", erd_override="lower_reminder", icon_override="mdi:dishwasher-alert"),
|
||||
GeErdPropertySensor(self, ErdCode.DISHWASHER_REMINDERS, "sanitized", erd_override="lower_reminder", icon_override="mdi:silverware-clean"),
|
||||
|
||||
#User Setttings
|
||||
GeErdPropertySensor(self, ErdCode.DISHWASHER_USER_SETTING, "sound", erd_override="lower_setting", icon_override="mdi:volume-high"),
|
||||
GeErdPropertySensor(self, ErdCode.DISHWASHER_USER_SETTING, "mute", erd_override="lower_setting", icon_override="mdi:volume-mute"),
|
||||
GeErdPropertySensor(self, ErdCode.DISHWASHER_USER_SETTING, "lock_control", erd_override="lower_setting", icon_override="mdi:lock"),
|
||||
GeErdPropertySensor(self, ErdCode.DISHWASHER_USER_SETTING, "sabbath", erd_override="lower_setting", icon_override="mdi:star-david"),
|
||||
GeErdPropertySensor(self, ErdCode.DISHWASHER_USER_SETTING, "cycle_mode", erd_override="lower_setting", icon_override="mdi:state-machine"),
|
||||
GeErdPropertySensor(self, ErdCode.DISHWASHER_USER_SETTING, "presoak", erd_override="lower_setting", icon_override="mdi:water"),
|
||||
GeErdPropertySensor(self, ErdCode.DISHWASHER_USER_SETTING, "bottle_jet", erd_override="lower_setting", icon_override="mdi:bottle-tonic-outline"),
|
||||
GeErdPropertySensor(self, ErdCode.DISHWASHER_USER_SETTING, "wash_temp", erd_override="lower_setting", icon_override="mdi:coolant-temperature"),
|
||||
GeErdPropertySensor(self, ErdCode.DISHWASHER_USER_SETTING, "rinse_aid", erd_override="lower_setting", icon_override="mdi:shimmer"),
|
||||
GeErdPropertySensor(self, ErdCode.DISHWASHER_USER_SETTING, "dry_option", erd_override="lower_setting", icon_override="mdi:fan"),
|
||||
GeErdPropertySensor(self, ErdCode.DISHWASHER_USER_SETTING, "wash_zone", erd_override="lower_setting", icon_override="mdi:dock-top"),
|
||||
GeErdPropertySensor(self, ErdCode.DISHWASHER_USER_SETTING, "delay_hours", erd_override="lower_setting", icon_override="mdi:clock-fast")
|
||||
|
@ -39,18 +44,23 @@ class DualDishwasherApi(ApplianceApi):
|
|||
upper_entities = [
|
||||
#GeDishwasherControlLockedSwitch(self, ErdCode.USER_INTERFACE_LOCKED),
|
||||
GeErdSensor(self, ErdCode.DISHWASHER_UPPER_CYCLE_STATE, erd_override="upper_cycle_state", icon_override="mdi:state-machine"),
|
||||
GeErdSensor(self, ErdCode.DISHWASHER_UPPER_RINSE_AGENT, erd_override="upper_rinse_agent", icon_override="mdi:shimmer"),
|
||||
GeErdSensor(self, ErdCode.DISHWASHER_UPPER_TIME_REMAINING, erd_override="upper_time_remaining"),
|
||||
GeErdBinarySensor(self, ErdCode.DISHWASHER_UPPER_DOOR_STATUS, erd_override="upper_door_status"),
|
||||
|
||||
#Reminders
|
||||
GeErdPropertySensor(self, ErdCode.DISHWASHER_UPPER_REMINDERS, "add_rinse_aid", erd_override="upper_reminder", icon_override="mdi:shimmer"),
|
||||
GeErdPropertySensor(self, ErdCode.DISHWASHER_UPPER_REMINDERS, "clean_filter", erd_override="upper_reminder", icon_override="mdi:dishwasher-alert"),
|
||||
GeErdPropertySensor(self, ErdCode.DISHWASHER_UPPER_REMINDERS, "sanitized", erd_override="upper_reminder", icon_override="mdi:silverware-clean"),
|
||||
|
||||
#User Setttings
|
||||
GeErdPropertySensor(self, ErdCode.DISHWASHER_UPPER_USER_SETTING, "sound", erd_override="upper_setting", icon_override="mdi:volume-high"),
|
||||
GeErdPropertySensor(self, ErdCode.DISHWASHER_UPPER_USER_SETTING, "mute", erd_override="upper_setting", icon_override="mdi:volume-mute"),
|
||||
GeErdPropertySensor(self, ErdCode.DISHWASHER_UPPER_USER_SETTING, "lock_control", erd_override="upper_setting", icon_override="mdi:lock"),
|
||||
GeErdPropertySensor(self, ErdCode.DISHWASHER_UPPER_USER_SETTING, "sabbath", erd_override="upper_setting", icon_override="mdi:star-david"),
|
||||
GeErdPropertySensor(self, ErdCode.DISHWASHER_UPPER_USER_SETTING, "cycle_mode", erd_override="upper_setting", icon_override="mdi:state-machine"),
|
||||
GeErdPropertySensor(self, ErdCode.DISHWASHER_UPPER_USER_SETTING, "presoak", erd_override="upper_setting", icon_override="mdi:water"),
|
||||
GeErdPropertySensor(self, ErdCode.DISHWASHER_UPPER_USER_SETTING, "bottle_jet", erd_override="upper_setting", icon_override="mdi:bottle-tonic-outline"),
|
||||
GeErdPropertySensor(self, ErdCode.DISHWASHER_UPPER_USER_SETTING, "wash_temp", erd_override="upper_setting", icon_override="mdi:coolant-temperature"),
|
||||
GeErdPropertySensor(self, ErdCode.DISHWASHER_UPPER_USER_SETTING, "rinse_aid", erd_override="upper_setting", icon_override="mdi:shimmer"),
|
||||
GeErdPropertySensor(self, ErdCode.DISHWASHER_UPPER_USER_SETTING, "dry_option", erd_override="upper_setting", icon_override="mdi:fan"),
|
||||
GeErdPropertySensor(self, ErdCode.DISHWASHER_UPPER_USER_SETTING, "wash_zone", erd_override="upper_setting", icon_override="mdi:dock-top"),
|
||||
GeErdPropertySensor(self, ErdCode.DISHWASHER_UPPER_USER_SETTING, "delay_hours", erd_override="upper_setting", icon_override="mdi:clock-fast")
|
||||
|
|
|
@ -11,7 +11,8 @@ from gehomesdk import (
|
|||
ErdCooktopConfig,
|
||||
CooktopStatus,
|
||||
ErdOvenLightLevel,
|
||||
ErdOvenLightLevelAvailability
|
||||
ErdOvenLightLevelAvailability,
|
||||
ErdOvenWarmingState
|
||||
)
|
||||
|
||||
from .base import ApplianceApi
|
||||
|
@ -23,6 +24,7 @@ from ..entities import (
|
|||
GeErdPropertyBinarySensor,
|
||||
GeOven,
|
||||
GeOvenLightLevelSelect,
|
||||
GeOvenWarmingStateSelect,
|
||||
UPPER_OVEN,
|
||||
LOWER_OVEN
|
||||
)
|
||||
|
@ -49,6 +51,10 @@ class OvenApi(ApplianceApi):
|
|||
lower_light : ErdOvenLightLevel = self.try_get_erd_value(ErdCode.LOWER_OVEN_LIGHT)
|
||||
lower_light_availability: ErdOvenLightLevelAvailability = self.try_get_erd_value(ErdCode.LOWER_OVEN_LIGHT_AVAILABILITY)
|
||||
|
||||
upper_warm_drawer : ErdOvenWarmingState = self.try_get_erd_value(ErdCode.UPPER_OVEN_WARMING_DRAWER_STATE)
|
||||
lower_warm_drawer : ErdOvenWarmingState = self.try_get_erd_value(ErdCode.LOWER_OVEN_WARMING_DRAWER_STATE)
|
||||
warm_drawer : ErdOvenWarmingState = self.try_get_erd_value(ErdCode.WARMING_DRAWER_STATE)
|
||||
|
||||
_LOGGER.debug(f"Oven Config: {oven_config}")
|
||||
_LOGGER.debug(f"Cooktop Config: {cooktop_config}")
|
||||
oven_entities = []
|
||||
|
@ -56,44 +62,43 @@ class OvenApi(ApplianceApi):
|
|||
|
||||
if oven_config.has_lower_oven:
|
||||
oven_entities.extend([
|
||||
GeErdSensor(self, ErdCode.UPPER_OVEN_COOK_MODE),
|
||||
GeErdSensor(self, ErdCode.UPPER_OVEN_COOK_TIME_REMAINING),
|
||||
GeErdTimerSensor(self, ErdCode.UPPER_OVEN_KITCHEN_TIMER),
|
||||
GeErdSensor(self, ErdCode.UPPER_OVEN_USER_TEMP_OFFSET),
|
||||
GeErdSensor(self, ErdCode.UPPER_OVEN_DISPLAY_TEMPERATURE),
|
||||
GeErdBinarySensor(self, ErdCode.UPPER_OVEN_REMOTE_ENABLED),
|
||||
|
||||
GeErdSensor(self, ErdCode.LOWER_OVEN_COOK_MODE),
|
||||
GeErdSensor(self, ErdCode.LOWER_OVEN_CURRENT_STATE),
|
||||
GeErdSensor(self, ErdCode.LOWER_OVEN_COOK_TIME_REMAINING),
|
||||
GeErdTimerSensor(self, ErdCode.LOWER_OVEN_KITCHEN_TIMER),
|
||||
GeErdSensor(self, ErdCode.LOWER_OVEN_USER_TEMP_OFFSET),
|
||||
GeErdSensor(self, ErdCode.LOWER_OVEN_DISPLAY_TEMPERATURE),
|
||||
GeErdBinarySensor(self, ErdCode.LOWER_OVEN_REMOTE_ENABLED),
|
||||
|
||||
GeOven(self, LOWER_OVEN, True, self._temperature_code(has_lower_raw_temperature)),
|
||||
GeOven(self, UPPER_OVEN, True, self._temperature_code(has_upper_raw_temperature)),
|
||||
GeOven(self, LOWER_OVEN, True, self._temperature_code(has_lower_raw_temperature))
|
||||
])
|
||||
if has_upper_raw_temperature:
|
||||
oven_entities.append(GeErdSensor(self, ErdCode.UPPER_OVEN_RAW_TEMPERATURE))
|
||||
if has_lower_raw_temperature:
|
||||
oven_entities.append(GeErdSensor(self, ErdCode.LOWER_OVEN_RAW_TEMPERATURE))
|
||||
if lower_light_availability is None or lower_light_availability.is_available or lower_light is not None:
|
||||
oven_entities.append(GeOvenLightLevelSelect(self, ErdCode.LOWER_OVEN_LIGHT))
|
||||
else:
|
||||
oven_entities.extend([
|
||||
GeErdSensor(self, ErdCode.UPPER_OVEN_COOK_MODE, self._single_name(ErdCode.UPPER_OVEN_COOK_MODE)),
|
||||
GeErdSensor(self, ErdCode.UPPER_OVEN_COOK_TIME_REMAINING, self._single_name(ErdCode.UPPER_OVEN_COOK_TIME_REMAINING)),
|
||||
GeErdTimerSensor(self, ErdCode.UPPER_OVEN_KITCHEN_TIMER, self._single_name(ErdCode.UPPER_OVEN_KITCHEN_TIMER)),
|
||||
GeErdSensor(self, ErdCode.UPPER_OVEN_USER_TEMP_OFFSET, self._single_name(ErdCode.UPPER_OVEN_USER_TEMP_OFFSET)),
|
||||
GeErdSensor(self, ErdCode.UPPER_OVEN_DISPLAY_TEMPERATURE, self._single_name(ErdCode.UPPER_OVEN_DISPLAY_TEMPERATURE)),
|
||||
GeErdBinarySensor(self, ErdCode.UPPER_OVEN_REMOTE_ENABLED, self._single_name(ErdCode.UPPER_OVEN_REMOTE_ENABLED)),
|
||||
GeOven(self, UPPER_OVEN, False, self._temperature_code(has_upper_raw_temperature))
|
||||
])
|
||||
if has_upper_raw_temperature:
|
||||
oven_entities.append(GeErdSensor(self, ErdCode.UPPER_OVEN_RAW_TEMPERATURE, self._single_name(ErdCode.UPPER_OVEN_RAW_TEMPERATURE)))
|
||||
if upper_light_availability is None or upper_light_availability.is_available or upper_light is not None:
|
||||
oven_entities.append(GeOvenLightLevelSelect(self, ErdCode.UPPER_OVEN_LIGHT, self._single_name(ErdCode.UPPER_OVEN_LIGHT)))
|
||||
if lower_warm_drawer is not None:
|
||||
oven_entities.append(GeOvenWarmingStateSelect(self, ErdCode.LOWER_OVEN_WARMING_DRAWER_STATE))
|
||||
|
||||
oven_entities.extend([
|
||||
GeErdSensor(self, ErdCode.UPPER_OVEN_COOK_MODE, self._single_name(ErdCode.UPPER_OVEN_COOK_MODE, ~oven_config.has_lower_oven)),
|
||||
GeErdSensor(self, ErdCode.UPPER_OVEN_CURRENT_STATE, self._single_name(ErdCode.UPPER_OVEN_CURRENT_STATE, ~oven_config.has_lower_oven)),
|
||||
GeErdSensor(self, ErdCode.UPPER_OVEN_COOK_TIME_REMAINING, self._single_name(ErdCode.UPPER_OVEN_COOK_TIME_REMAINING, ~oven_config.has_lower_oven)),
|
||||
GeErdTimerSensor(self, ErdCode.UPPER_OVEN_KITCHEN_TIMER, self._single_name(ErdCode.UPPER_OVEN_KITCHEN_TIMER, ~oven_config.has_lower_oven)),
|
||||
GeErdSensor(self, ErdCode.UPPER_OVEN_USER_TEMP_OFFSET, self._single_name(ErdCode.UPPER_OVEN_USER_TEMP_OFFSET, ~oven_config.has_lower_oven)),
|
||||
GeErdSensor(self, ErdCode.UPPER_OVEN_DISPLAY_TEMPERATURE, self._single_name(ErdCode.UPPER_OVEN_DISPLAY_TEMPERATURE, ~oven_config.has_lower_oven)),
|
||||
GeErdBinarySensor(self, ErdCode.UPPER_OVEN_REMOTE_ENABLED, self._single_name(ErdCode.UPPER_OVEN_REMOTE_ENABLED, ~oven_config.has_lower_oven)),
|
||||
|
||||
GeOven(self, UPPER_OVEN, False, self._temperature_code(has_upper_raw_temperature))
|
||||
])
|
||||
if has_upper_raw_temperature:
|
||||
oven_entities.append(GeErdSensor(self, ErdCode.UPPER_OVEN_RAW_TEMPERATURE, self._single_name(ErdCode.UPPER_OVEN_RAW_TEMPERATURE, ~oven_config.has_lower_oven)))
|
||||
if upper_light_availability is None or upper_light_availability.is_available or upper_light is not None:
|
||||
oven_entities.append(GeOvenLightLevelSelect(self, ErdCode.UPPER_OVEN_LIGHT, self._single_name(ErdCode.UPPER_OVEN_LIGHT, ~oven_config.has_lower_oven)))
|
||||
if upper_warm_drawer is not None:
|
||||
oven_entities.append(GeOvenWarmingStateSelect(self, ErdCode.UPPER_OVEN_WARMING_DRAWER_STATE, self._single_name(ErdCode.UPPER_OVEN_WARMING_DRAWER_STATE, ~oven_config.has_lower_oven)))
|
||||
|
||||
if oven_config.has_warming_drawer and warm_drawer is not None:
|
||||
oven_entities.append(GeErdSensor(self, ErdCode.WARMING_DRAWER_STATE))
|
||||
|
||||
if cooktop_config == ErdCooktopConfig.PRESENT:
|
||||
cooktop_status: CooktopStatus = self.try_get_erd_value(ErdCode.COOKTOP_STATUS)
|
||||
|
@ -110,11 +115,16 @@ class OvenApi(ApplianceApi):
|
|||
|
||||
return base_entities + oven_entities + cooktop_entities
|
||||
|
||||
def _single_name(self, erd_code: ErdCode):
|
||||
return erd_code.name.replace(UPPER_OVEN+"_","").replace("_", " ").title()
|
||||
def _single_name(self, erd_code: ErdCode, make_single: bool):
|
||||
name = erd_code.name
|
||||
|
||||
if make_single:
|
||||
name = name.replace(UPPER_OVEN+"_","")
|
||||
|
||||
return name.replace("_", " ").title()
|
||||
|
||||
def _camel_to_snake(self, s):
|
||||
return ''.join(['_'+c.lower() if c.isupper() else c for c in s]).lstrip('_')
|
||||
|
||||
def _temperature_code(self, has_raw: bool):
|
||||
return "RAW_TEMPERATURE" if has_raw else "DISPLAY_TEMPERATURE"
|
||||
return "RAW_TEMPERATURE" if has_raw else "DISPLAY_TEMPERATURE"
|
||||
|
|
|
@ -9,4 +9,5 @@ from .hood import *
|
|||
from .water_softener import *
|
||||
from .water_heater import *
|
||||
from .opal_ice_maker import *
|
||||
from .ccm import *
|
||||
from .ccm import *
|
||||
from .dehumidifier import *
|
|
@ -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
|
||||
from .ge_climate import GeClimate
|
||||
from .ge_humidifier import GeHumidifier
|
|
@ -145,5 +145,9 @@ 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"
|
||||
if self.erd_code_class == ErdCodeClass.DEHUMIDIFIER_SENSOR:
|
||||
return "mdi:air-humidifier"
|
||||
|
||||
return None
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -0,0 +1,105 @@
|
|||
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
|
||||
|
||||
DEFAULT_TARGET_PRECISION = 5
|
||||
|
||||
_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,
|
||||
target_precision = DEFAULT_TARGET_PRECISION
|
||||
):
|
||||
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
|
||||
self._target_precision = target_precision
|
||||
|
||||
@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]:
|
||||
# round to target precision
|
||||
target = round(humidity / self._target_precision) * self._target_precision
|
||||
|
||||
# if it's the same, just exit
|
||||
if self.target_humidity == target:
|
||||
return
|
||||
|
||||
_LOGGER.debug(f"Setting Target Humidity from {self.target_humidity} to {target}")
|
||||
|
||||
# make sure we're on
|
||||
if not self.is_on:
|
||||
await self.async_turn_on()
|
||||
|
||||
# set the target humidity
|
||||
await self.appliance.async_set_erd_value(
|
||||
self._target_humidity_erd_code,
|
||||
target,
|
||||
)
|
||||
|
||||
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
|
||||
)
|
|
@ -0,0 +1,2 @@
|
|||
from .dehumidifier import GeDehumidifier
|
||||
from .dehumidifier_fan_speed_sensor import GeDehumidifierFanSpeedSensor
|
|
@ -0,0 +1,3 @@
|
|||
SMART_DRY = "Smart Dry"
|
||||
DEFAULT_MIN_HUMIDITY = 35
|
||||
DEFAULT_MAX_HUMIDITY = 80
|
|
@ -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(ErdCode.AC_FAN_SETTING, new_state)
|
|
@ -0,0 +1,29 @@
|
|||
import logging
|
||||
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 [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()}")
|
||||
return ErdAcFanSetting.DEFAULT
|
||||
def to_option_string(self, value: ErdAcFanSetting) -> Optional[str]:
|
||||
try:
|
||||
if value is not None:
|
||||
return SMART_DRY if value == ErdAcFanSetting.DEFAULT else value.stringify()
|
||||
except:
|
||||
pass
|
||||
return SMART_DRY
|
|
@ -0,0 +1,40 @@
|
|||
from ...devices import ApplianceApi
|
||||
from ..common import GeErdSensor
|
||||
from .dehumidifier_fan_options import DehumidifierFanSettingOptionsConverter
|
||||
from gehomesdk import ErdCodeType, ErdCodeClass, ErdDataType, ErdAcFanSetting
|
||||
|
||||
class GeDehumidifierFanSpeedSensor(GeErdSensor):
|
||||
def __init__(
|
||||
self,
|
||||
api: ApplianceApi,
|
||||
erd_code: ErdCodeType,
|
||||
erd_override: str = None,
|
||||
icon_override: str = None,
|
||||
device_class_override: str = None,
|
||||
state_class_override: str = None,
|
||||
uom_override: str = None,
|
||||
data_type_override: ErdDataType = None
|
||||
):
|
||||
|
||||
super().__init__(
|
||||
api,
|
||||
erd_code,
|
||||
erd_override,
|
||||
icon_override,
|
||||
device_class_override,
|
||||
state_class_override,
|
||||
uom_override,
|
||||
data_type_override
|
||||
)
|
||||
|
||||
self._converter = DehumidifierFanSettingOptionsConverter()
|
||||
|
||||
@property
|
||||
def native_value(self):
|
||||
try:
|
||||
value: ErdAcFanSetting = self.appliance.get_erd_value(self.erd_code)
|
||||
return self._converter.to_option_string(value)
|
||||
except KeyError:
|
||||
return None
|
||||
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
from .ge_oven import GeOven
|
||||
from .ge_oven_light_level_select import GeOvenLightLevelSelect
|
||||
from .ge_oven_warming_state_select import GeOvenWarmingStateSelect
|
||||
from .const import UPPER_OVEN, LOWER_OVEN
|
|
@ -0,0 +1,56 @@
|
|||
import logging
|
||||
from typing import List, Any, Optional
|
||||
|
||||
from gehomesdk import ErdCodeType, ErdOvenWarmingState
|
||||
from ...devices import ApplianceApi
|
||||
from ..common import GeErdSelect, OptionsConverter
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
class OvenWarmingStateOptionsConverter(OptionsConverter):
|
||||
@property
|
||||
def options(self) -> List[str]:
|
||||
return [i.stringify() for i in ErdOvenWarmingState]
|
||||
def from_option_string(self, value: str) -> Any:
|
||||
try:
|
||||
return ErdOvenWarmingState[value.upper()]
|
||||
except:
|
||||
_LOGGER.warn(f"Could not set Oven warming state to {value.upper()}")
|
||||
return ErdOvenWarmingState.OFF
|
||||
def to_option_string(self, value: ErdOvenWarmingState) -> Optional[str]:
|
||||
try:
|
||||
if value is not None:
|
||||
return value.stringify()
|
||||
except:
|
||||
pass
|
||||
return ErdOvenWarmingState.OFF.stringify()
|
||||
|
||||
class GeOvenWarmingStateSelect(GeErdSelect):
|
||||
|
||||
def __init__(self, api: ApplianceApi, erd_code: ErdCodeType, erd_override: str = None):
|
||||
#check to see if we have a status
|
||||
value: ErdOvenWarmingState = api.try_get_erd_value(erd_code)
|
||||
self._has_status = value is not None and value != ErdOvenWarmingState.NOT_AVAILABLE
|
||||
self._assumed_state = ErdOvenWarmingState.OFF
|
||||
|
||||
super().__init__(api, erd_code, OvenWarmingStateOptionsConverter(self._availability), erd_override=erd_override)
|
||||
|
||||
@property
|
||||
def assumed_state(self) -> bool:
|
||||
return not self._has_status
|
||||
|
||||
@property
|
||||
def current_option(self):
|
||||
if self.assumed_state:
|
||||
return self._assumed_state
|
||||
|
||||
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)
|
||||
self._assumed_state = new_state
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
"""GE Home Humidifier Entities"""
|
||||
import logging
|
||||
from typing import Callable
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
|
||||
from .entities import GeHumidifier
|
||||
from .const import DOMAIN
|
||||
from .devices import ApplianceApi
|
||||
from .update_coordinator import GeHomeUpdateCoordinator
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry, async_add_entities: Callable):
|
||||
"""GE Home Water Heaters."""
|
||||
_LOGGER.debug('Adding GE "Humidifiers"')
|
||||
coordinator: GeHomeUpdateCoordinator = hass.data[DOMAIN][config_entry.entry_id]
|
||||
registry = er.async_get(hass)
|
||||
|
||||
@callback
|
||||
def async_devices_discovered(apis: list[ApplianceApi]):
|
||||
_LOGGER.debug(f'Found {len(apis):d} appliance APIs')
|
||||
entities = [
|
||||
entity
|
||||
for api in apis
|
||||
for entity in api.entities
|
||||
if isinstance(entity, GeHumidifier)
|
||||
if not registry.async_is_registered(entity.entity_id)
|
||||
]
|
||||
_LOGGER.debug(f'Found {len(entities):d} unregistered humidifiers')
|
||||
async_add_entities(entities)
|
||||
|
||||
async_dispatcher_connect(hass, coordinator.signal_ready, async_devices_discovered)
|
|
@ -2,8 +2,10 @@
|
|||
"domain": "ge_home",
|
||||
"name": "GE Home (SmartHQ)",
|
||||
"config_flow": true,
|
||||
"integration_type": "hub",
|
||||
"iot_class": "cloud_push",
|
||||
"documentation": "https://github.com/simbaja/ha_gehome",
|
||||
"requirements": ["gehomesdk==0.5.10","magicattr==0.1.6","slixmpp==1.8.3"],
|
||||
"requirements": ["gehomesdk==0.5.23","magicattr==0.1.6","slixmpp==1.8.3"],
|
||||
"codeowners": ["@simbaja"],
|
||||
"version": "0.6.7"
|
||||
"version": "0.6.8"
|
||||
}
|
||||
|
|
|
@ -35,7 +35,18 @@ from .const import (
|
|||
)
|
||||
from .devices import ApplianceApi, get_appliance_api_type
|
||||
|
||||
PLATFORMS = ["binary_sensor", "sensor", "switch", "water_heater", "select", "climate", "light", "button", "number"]
|
||||
PLATFORMS = [
|
||||
"binary_sensor",
|
||||
"sensor",
|
||||
"switch",
|
||||
"water_heater",
|
||||
"select",
|
||||
"climate",
|
||||
"light",
|
||||
"button",
|
||||
"number",
|
||||
"humidifier"
|
||||
]
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
|
@ -252,7 +263,7 @@ class GeHomeUpdateCoordinator(DataUpdateCoordinator):
|
|||
_LOGGER.info("ge_home shutting down")
|
||||
if self.client:
|
||||
self.client.clear_event_handlers()
|
||||
self.client.disconnect()
|
||||
self._hass.loop.create_task(self.client.disconnect())
|
||||
|
||||
async def on_device_update(self, data: Tuple[GeAppliance, Dict[ErdCodeType, Any]]):
|
||||
"""Let HA know there's new state."""
|
||||
|
|
13
info.md
13
info.md
|
@ -69,6 +69,13 @@ A/C Controls:
|
|||
|
||||
#### Features
|
||||
|
||||
{% if version_installed.split('.') | map('int') < '0.6.8'.split('.') | map('int') %}
|
||||
- Added Dehumidifier (#114)
|
||||
- Added oven drawer sensors
|
||||
- Added oven current state sensors (#175)
|
||||
- Added descriptors to manifest (#181)
|
||||
{% endif %}
|
||||
|
||||
{% if version_installed.split('.') | map('int') < '0.6.7'.split('.') | map('int') %}
|
||||
- Added OIM descaling sensor (#154)
|
||||
{% endif %}
|
||||
|
@ -116,6 +123,12 @@ A/C Controls:
|
|||
|
||||
#### Bugfixes
|
||||
|
||||
{% if version_installed.split('.') | map('int') < '0.6.8'.split('.') | map('int') %}
|
||||
- Bugfix: Fixed issue with oven lights (#174)
|
||||
- Bugfix: Fixed issues with dual dishwasher (#161)
|
||||
- Bugfix: Fixed disconnection issue (#169)
|
||||
{% endif %}
|
||||
|
||||
{% if version_installed.split('.') | map('int') < '0.6.7'.split('.') | map('int') %}
|
||||
- Bugfix: fixed issues with dishwasher (#155)
|
||||
{% endif %}
|
||||
|
|
Loading…
Reference in New Issue