From 12f3bca1e7f6e175c6560961e7952192d3ab3956 Mon Sep 17 00:00:00 2001 From: Jack Simbach Date: Sun, 17 Oct 2021 15:08:05 -0400 Subject: [PATCH 01/28] - documentation update --- CHANGELOG.md | 2 +- info.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1695382..e06ce42 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,7 @@ - Fixed device info when serial not present (@Xe138) - Fixed issue with ovens when raw temperature not available (@chadohalloran) - Fixed issue where Split A/C temperature sensors report UOM incorrectly (@RobertusIT) -- Added convertable drawer mode, proximity light, and interior lights to fridge (@grotto27, @elwing00) +- Added convertable drawer mode, proximity light, and interior lights to fridge (@groto27, @elwing00) ## 0.4.3 - Enabled support for appliances without serial numbers diff --git a/info.md b/info.md index 75ed07d..63cc22b 100644 --- a/info.md +++ b/info.md @@ -72,7 +72,7 @@ A/C Controls: - Fixed device info when serial not present (@Xe138) - Fixed issue with ovens when raw temperature not available (@chadohalloran) - Fixed issue where Split A/C temperature sensors report UOM incorrectly (@RobertusIT) -- Added convertable drawer mode, proximity light, and interior lights to fridge (@grotto27, @elwing00) +- Added convertable drawer mode, proximity light, and interior lights to fridge (@groto27, @elwing00) {% endif %} {% if version_installed.split('.') | map('int') < '0.4.3'.split('.') | map('int') %} From 65b6efc8269051ec091c0cae392699960da1251f Mon Sep 17 00:00:00 2001 From: Jack Simbach Date: Sat, 11 Dec 2021 00:18:28 -0500 Subject: [PATCH 02/28] - updated sensors to uses native value/uom - changed temp uoms to always report fahrenheit - updated version of sdk in manifest --- CHANGELOG.md | 5 ++ .../ge_home/devices/advantium.py | 6 +-- custom_components/ge_home/devices/fridge.py | 5 +- custom_components/ge_home/devices/oven.py | 3 +- custom_components/ge_home/devices/sac.py | 4 +- .../ge_home/entities/ac/ge_pac_climate.py | 5 -- .../ge_home/entities/ac/ge_sac_climate.py | 6 --- .../entities/ac/ge_sac_temperature_sensor.py | 15 ------ .../ge_home/entities/common/ge_climate.py | 8 +-- .../entities/common/ge_erd_property_sensor.py | 21 +++++--- .../ge_home/entities/common/ge_erd_sensor.py | 50 ++++++++++++++----- .../entities/common/ge_water_heater.py | 7 +-- custom_components/ge_home/manifest.json | 2 +- 13 files changed, 77 insertions(+), 60 deletions(-) delete mode 100644 custom_components/ge_home/entities/ac/ge_sac_temperature_sensor.py diff --git a/CHANGELOG.md b/CHANGELOG.md index e06ce42..c5cd65b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,11 @@ # GE Home Appliances (SmartHQ) Changelog +## 0.6.0 + +- Changed the sensors to use native value/uom +- Changed the temperatures to always be natively fahrenheit (API appears to always use this system) + ## 0.5.0 - Initial support for oven hoods (@digitalbites) diff --git a/custom_components/ge_home/devices/advantium.py b/custom_components/ge_home/devices/advantium.py index 828192a..23775ec 100644 --- a/custom_components/ge_home/devices/advantium.py +++ b/custom_components/ge_home/devices/advantium.py @@ -2,7 +2,7 @@ import logging from typing import List from homeassistant.helpers.entity import Entity -from gehomesdk.erd import ErdCode, ErdApplianceType +from gehomesdk.erd import ErdCode, ErdApplianceType, ErdDataType from .base import ApplianceApi from ..entities import GeAdvantium, GeErdSensor, GeErdBinarySensor, GeErdPropertySensor, GeErdPropertyBinarySensor, UPPER_OVEN @@ -29,8 +29,8 @@ class AdvantiumApi(ApplianceApi): GeErdPropertySensor(self, ErdCode.ADVANTIUM_COOK_STATUS, "cook_mode"), GeErdPropertySensor(self, ErdCode.ADVANTIUM_COOK_STATUS, "termination_reason", icon_override="mdi:information-outline"), GeErdPropertySensor(self, ErdCode.ADVANTIUM_COOK_STATUS, "preheat_status", icon_override="mdi:fire"), - GeErdPropertySensor(self, ErdCode.ADVANTIUM_COOK_STATUS, "temperature", icon_override="mdi:thermometer"), - GeErdPropertySensor(self, ErdCode.ADVANTIUM_COOK_STATUS, "power_level", icon_override="mdi:gauge"), + GeErdPropertySensor(self, ErdCode.ADVANTIUM_COOK_STATUS, "temperature", icon_override="mdi:thermometer", data_type_override=ErdDataType.INT), + GeErdPropertySensor(self, ErdCode.ADVANTIUM_COOK_STATUS, "power_level", icon_override="mdi:gauge", data_type_override=ErdDataType.INT), GeErdPropertySensor(self, ErdCode.ADVANTIUM_COOK_STATUS, "warm_status", icon_override="mdi:radiator"), GeErdPropertyBinarySensor(self, ErdCode.ADVANTIUM_COOK_STATUS, "door_status", device_class_override="door"), GeErdPropertyBinarySensor(self, ErdCode.ADVANTIUM_COOK_STATUS, "sensing_active", icon_on_override="mdi:flash-auto", icon_off_override="mdi:flash-off"), diff --git a/custom_components/ge_home/devices/fridge.py b/custom_components/ge_home/devices/fridge.py index 6dd2f4c..b2c42d2 100644 --- a/custom_components/ge_home/devices/fridge.py +++ b/custom_components/ge_home/devices/fridge.py @@ -14,7 +14,8 @@ from gehomesdk import ( ErdFilterStatus, HotWaterStatus, FridgeModelInfo, - ErdConvertableDrawerMode + ErdConvertableDrawerMode, + ErdDataType ) from .base import ApplianceApi @@ -110,7 +111,7 @@ class FridgeApi(ApplianceApi): GeErdSensor(self, ErdCode.HOT_WATER_SET_TEMP), GeErdPropertySensor(self, ErdCode.HOT_WATER_STATUS, "status", icon_override="mdi:information-outline"), GeErdPropertySensor(self, ErdCode.HOT_WATER_STATUS, "time_until_ready", icon_override="mdi:timer-outline"), - GeErdPropertySensor(self, ErdCode.HOT_WATER_STATUS, "current_temp", device_class_override=DEVICE_CLASS_TEMPERATURE), + GeErdPropertySensor(self, ErdCode.HOT_WATER_STATUS, "current_temp", device_class_override=DEVICE_CLASS_TEMPERATURE, data_type_override=ErdDataType.INT), GeErdPropertyBinarySensor(self, ErdCode.HOT_WATER_STATUS, "faulted", device_class_override=DEVICE_CLASS_PROBLEM), GeDispenser(self) ]) diff --git a/custom_components/ge_home/devices/oven.py b/custom_components/ge_home/devices/oven.py index f2a2074..0dde6d5 100644 --- a/custom_components/ge_home/devices/oven.py +++ b/custom_components/ge_home/devices/oven.py @@ -1,5 +1,6 @@ import logging from typing import List +from gehomesdk.erd.erd_data_type import ErdDataType from homeassistant.const import DEVICE_CLASS_POWER_FACTOR from homeassistant.helpers.entity import Entity @@ -92,7 +93,7 @@ class OvenApi(ApplianceApi): cooktop_entities.append(GeErdPropertyBinarySensor(self, ErdCode.COOKTOP_STATUS, prop+".on")) cooktop_entities.append(GeErdPropertyBinarySensor(self, ErdCode.COOKTOP_STATUS, prop+".synchronized")) if not v.on_off_only: - cooktop_entities.append(GeErdPropertySensor(self, ErdCode.COOKTOP_STATUS, prop+".power_pct", icon_override="mdi:fire", device_class_override=DEVICE_CLASS_POWER_FACTOR)) + cooktop_entities.append(GeErdPropertySensor(self, ErdCode.COOKTOP_STATUS, prop+".power_pct", icon_override="mdi:fire", device_class_override=DEVICE_CLASS_POWER_FACTOR, data_type_override=ErdDataType.INT)) return base_entities + oven_entities + cooktop_entities diff --git a/custom_components/ge_home/devices/sac.py b/custom_components/ge_home/devices/sac.py index 40f87e2..98686e0 100644 --- a/custom_components/ge_home/devices/sac.py +++ b/custom_components/ge_home/devices/sac.py @@ -19,8 +19,8 @@ class SacApi(ApplianceApi): sac_entities = [ GeSacClimate(self), - GeSacTemperatureSensor(self, ErdCode.AC_TARGET_TEMPERATURE), - GeSacTemperatureSensor(self, ErdCode.AC_AMBIENT_TEMPERATURE), + GeErdSensor(self, ErdCode.AC_TARGET_TEMPERATURE), + GeErdSensor(self, ErdCode.AC_AMBIENT_TEMPERATURE), GeErdSensor(self, ErdCode.AC_FAN_SETTING, icon_override="mdi:fan"), GeErdSensor(self, ErdCode.AC_OPERATION_MODE), GeErdSwitch(self, ErdCode.AC_POWER_STATUS, bool_converter=ErdOnOffBoolConverter(), icon_on_override="mdi:power-on", icon_off_override="mdi:power-off"), diff --git a/custom_components/ge_home/entities/ac/ge_pac_climate.py b/custom_components/ge_home/entities/ac/ge_pac_climate.py index 13659e8..ba8eb75 100644 --- a/custom_components/ge_home/entities/ac/ge_pac_climate.py +++ b/custom_components/ge_home/entities/ac/ge_pac_climate.py @@ -66,11 +66,6 @@ class GePacClimate(GeClimate): #construct the converter based on the available modes self._hvac_mode_converter = PacHvacModeOptionsConverter(self._modes) - @property - def temperature_unit(self): - #SAC appears to be hard coded to use Fahrenheit internally, no matter what the display shows - return TEMP_FAHRENHEIT - @property def min_temp(self) -> float: temp = 64 diff --git a/custom_components/ge_home/entities/ac/ge_sac_climate.py b/custom_components/ge_home/entities/ac/ge_sac_climate.py index 722a483..7c7ed54 100644 --- a/custom_components/ge_home/entities/ac/ge_sac_climate.py +++ b/custom_components/ge_home/entities/ac/ge_sac_climate.py @@ -69,12 +69,6 @@ class GeSacClimate(GeClimate): #construct the converter based on the available modes self._hvac_mode_converter = SacHvacModeOptionsConverter(self._modes) - - @property - def temperature_unit(self): - #SAC appears to be hard coded to use Fahrenheit internally, no matter what the display shows - return TEMP_FAHRENHEIT - @property def min_temp(self) -> float: temp = 60 diff --git a/custom_components/ge_home/entities/ac/ge_sac_temperature_sensor.py b/custom_components/ge_home/entities/ac/ge_sac_temperature_sensor.py deleted file mode 100644 index 854a239..0000000 --- a/custom_components/ge_home/entities/ac/ge_sac_temperature_sensor.py +++ /dev/null @@ -1,15 +0,0 @@ -import logging -from typing import Any, List, Optional - -from homeassistant.const import ( - TEMP_FAHRENHEIT -) -from ..common import GeErdSensor - -class GeSacTemperatureSensor(GeErdSensor): - """Class for Split A/C temperature sensors""" - - @property - def _temp_units(self) -> Optional[str]: - #SAC appears to be hard coded to use Fahrenheit internally, no matter what the display shows - return TEMP_FAHRENHEIT diff --git a/custom_components/ge_home/entities/common/ge_climate.py b/custom_components/ge_home/entities/common/ge_climate.py index 2b28532..fc48f93 100644 --- a/custom_components/ge_home/entities/common/ge_climate.py +++ b/custom_components/ge_home/entities/common/ge_climate.py @@ -82,10 +82,12 @@ class GeClimate(GeEntity, ClimateEntity): @property def temperature_unit(self): - measurement_system = self.appliance.get_erd_value(ErdCode.TEMPERATURE_UNIT) - if measurement_system == ErdMeasurementUnits.METRIC: - return TEMP_CELSIUS + #appears to always be Fahrenheit internally, hardcode this return TEMP_FAHRENHEIT + #measurement_system = self.appliance.get_erd_value(ErdCode.TEMPERATURE_UNIT) + #if measurement_system == ErdMeasurementUnits.METRIC: + # return TEMP_CELSIUS + #return TEMP_FAHRENHEIT @property def supported_features(self): diff --git a/custom_components/ge_home/entities/common/ge_erd_property_sensor.py b/custom_components/ge_home/entities/common/ge_erd_property_sensor.py index 53d9a92..70938d0 100644 --- a/custom_components/ge_home/entities/common/ge_erd_property_sensor.py +++ b/custom_components/ge_home/entities/common/ge_erd_property_sensor.py @@ -1,7 +1,7 @@ from typing import Optional import magicattr -from gehomesdk import ErdCode, ErdCodeType, ErdMeasurementUnits +from gehomesdk import ErdCode, ErdCodeType, ErdMeasurementUnits, ErdDataType from ...devices import ApplianceApi from .ge_erd_sensor import GeErdSensor @@ -11,13 +11,14 @@ class GeErdPropertySensor(GeErdSensor): def __init__( self, api: ApplianceApi, erd_code: ErdCodeType, erd_property: str, erd_override: str = None, icon_override: str = None, device_class_override: str = None, - state_class_override: str = None, uom_override: str = None + state_class_override: str = None, uom_override: str = None, data_type_override: ErdDataType = None ): super().__init__( api, erd_code, erd_override=erd_override, icon_override=icon_override, device_class_override=device_class_override, state_class_override=state_class_override, - uom_override=uom_override + uom_override=uom_override, + data_type_override=data_type_override ) self.erd_property = erd_property self._erd_property_cleansed = erd_property.replace(".","_").replace("[","_").replace("]","_") @@ -33,9 +34,17 @@ class GeErdPropertySensor(GeErdSensor): return f"{base_string} {property_name}" @property - def state(self) -> Optional[str]: + def native_value(self): try: value = magicattr.get(self.appliance.get_erd_value(self.erd_code), self.erd_property) + + # if it's a numeric data type, return it directly + if self._data_type in [ErdDataType.INT, ErdDataType.FLOAT]: + return value + + # otherwise, return a stringified version + # TODO: perhaps enhance so that there's a list of variables available + # for the stringify function to consume... + return self._stringify(value, temp_units=self._temp_units) except KeyError: - return None - return self._stringify(value, temp_units=self._temp_units) + return None \ No newline at end of file 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 b1b9163..264b86a 100644 --- a/custom_components/ge_home/entities/common/ge_erd_sensor.py +++ b/custom_components/ge_home/entities/common/ge_erd_sensor.py @@ -1,5 +1,6 @@ import logging from typing import Optional +from gehomesdk.erd.erd_data_type import ErdDataType from homeassistant.components.sensor import STATE_CLASS_MEASUREMENT from homeassistant.const import ( @@ -42,34 +43,54 @@ class GeErdSensor(GeErdEntity, Entity): 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) self._uom_override = uom_override self._state_class_override = state_class_override + self._data_type_override = data_type_override @property - def state(self) -> Optional[str]: + def native_value(self): try: value = self.appliance.get_erd_value(self.erd_code) + + # if it's a numeric data type, return it directly + if self._data_type in [ErdDataType.INT, ErdDataType.FLOAT]: + return value + + # otherwise, return a stringified version + # TODO: perhaps enhance so that there's a list of variables available + # for the stringify function to consume... + return self._stringify(value, temp_units=self._temp_units) except KeyError: return None - # TODO: perhaps enhance so that there's a list of variables available - # for the stringify function to consume... - return self._stringify(value, temp_units=self._temp_units) @property - def unit_of_measurement(self) -> Optional[str]: + def native_unit_of_measurement(self) -> Optional[str]: return self._get_uom() @property def state_class(self) -> Optional[str]: return self._get_state_class() + @property + def _data_type(self) -> ErdDataType: + if self._data_type_override is not None: + return self._data_type_override + + return self.appliance.get_erd_code_data_type(self.erd_code) + @property def _temp_units(self) -> Optional[str]: - if self._measurement_system == ErdMeasurementUnits.METRIC: - return TEMP_CELSIUS - return TEMP_FAHRENHEIT + #based on testing, all API values are in Fahrenheit, so we'll redefine + #this property to be the configured temperature unit and set the native + #unit differently + return self.api.hass.config.units.temperature_unit + + #if self._measurement_system == ErdMeasurementUnits.METRIC: + # return TEMP_CELSIUS + #return TEMP_FAHRENHEIT def _get_uom(self): """Select appropriate units""" @@ -83,7 +104,10 @@ class GeErdSensor(GeErdEntity, Entity): in [ErdCodeClass.RAW_TEMPERATURE, ErdCodeClass.NON_ZERO_TEMPERATURE] or self.device_class == DEVICE_CLASS_TEMPERATURE ): - return self._temp_units + #NOTE: it appears that the API only sets temperature in Fahrenheit, + #so we'll hard code this UOM instead of using the device configured + #settings + return TEMP_FAHRENHEIT if ( self.erd_code_class == ErdCodeClass.BATTERY or self.device_class == DEVICE_CLASS_BATTERY @@ -94,12 +118,12 @@ class GeErdSensor(GeErdEntity, Entity): if self.device_class == DEVICE_CLASS_POWER_FACTOR: return "%" if self.erd_code_class == ErdCodeClass.FLOW_RATE: - if self._measurement_system == ErdMeasurementUnits.METRIC: - return "lpm" + #if self._measurement_system == ErdMeasurementUnits.METRIC: + # return "lpm" return "gpm" if self.erd_code_class == ErdCodeClass.LIQUID_VOLUME: - if self._measurement_system == ErdMeasurementUnits.METRIC: - return "l" + #if self._measurement_system == ErdMeasurementUnits.METRIC: + # return "l" return "g" return None diff --git a/custom_components/ge_home/entities/common/ge_water_heater.py b/custom_components/ge_home/entities/common/ge_water_heater.py index e0d3e04..87bd091 100644 --- a/custom_components/ge_home/entities/common/ge_water_heater.py +++ b/custom_components/ge_home/entities/common/ge_water_heater.py @@ -34,9 +34,10 @@ class GeWaterHeater(GeEntity, WaterHeaterEntity, metaclass=abc.ABCMeta): @property def temperature_unit(self): - measurement_system = self.appliance.get_erd_value(ErdCode.TEMPERATURE_UNIT) - if measurement_system == ErdMeasurementUnits.METRIC: - return TEMP_CELSIUS + #It appears that the GE API is alwasy Fehrenheit + #measurement_system = self.appliance.get_erd_value(ErdCode.TEMPERATURE_UNIT) + #if measurement_system == ErdMeasurementUnits.METRIC: + # return TEMP_CELSIUS return TEMP_FAHRENHEIT @property diff --git a/custom_components/ge_home/manifest.json b/custom_components/ge_home/manifest.json index 9d6b89c..d670012 100644 --- a/custom_components/ge_home/manifest.json +++ b/custom_components/ge_home/manifest.json @@ -3,7 +3,7 @@ "name": "GE Home (SmartHQ)", "config_flow": true, "documentation": "https://github.com/simbaja/ha_gehome", - "requirements": ["gehomesdk==0.4.12","magicattr==0.1.5"], + "requirements": ["gehomesdk==0.4.16","magicattr==0.1.5"], "codeowners": ["@simbaja"], "version": "0.5.0" } From 468a8496db8728a0630655e68afbd2746e4ebf4b Mon Sep 17 00:00:00 2001 From: Jack Simbach Date: Sat, 11 Dec 2021 00:36:30 -0500 Subject: [PATCH 03/28] - initial support for water softeners --- CHANGELOG.md | 1 + custom_components/ge_home/devices/__init__.py | 4 ++ .../ge_home/devices/water_softener.py | 36 ++++++++++ .../ge_home/entities/__init__.py | 3 +- .../entities/water_softener/__init__.py | 1 + .../water_softener/shutoff_position.py | 65 +++++++++++++++++++ 6 files changed, 109 insertions(+), 1 deletion(-) create mode 100644 custom_components/ge_home/devices/water_softener.py create mode 100644 custom_components/ge_home/entities/water_softener/__init__.py create mode 100644 custom_components/ge_home/entities/water_softener/shutoff_position.py diff --git a/CHANGELOG.md b/CHANGELOG.md index c5cd65b..7640c63 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ - Changed the sensors to use native value/uom - Changed the temperatures to always be natively fahrenheit (API appears to always use this system) +- Initial support for Water Softeners (@npentell) ## 0.5.0 diff --git a/custom_components/ge_home/devices/__init__.py b/custom_components/ge_home/devices/__init__.py index 293fe3c..a7c2e00 100644 --- a/custom_components/ge_home/devices/__init__.py +++ b/custom_components/ge_home/devices/__init__.py @@ -3,6 +3,8 @@ from typing import Type from gehomesdk.erd import ErdApplianceType +from custom_components.ge_home.devices.water_softener import WaterSoftenerApi + from .base import ApplianceApi from .oven import OvenApi from .fridge import FridgeApi @@ -37,6 +39,8 @@ def get_appliance_api_type(appliance_type: ErdApplianceType) -> Type: return WasherDryerApi if appliance_type == ErdApplianceType.POE_WATER_FILTER: return WaterFilterApi + if appliance_type == ErdApplianceType.WATER_SOFTENER: + return WaterSoftenerApi if appliance_type == ErdApplianceType.ADVANTIUM: return AdvantiumApi if appliance_type == ErdApplianceType.AIR_CONDITIONER: diff --git a/custom_components/ge_home/devices/water_softener.py b/custom_components/ge_home/devices/water_softener.py new file mode 100644 index 0000000..5490082 --- /dev/null +++ b/custom_components/ge_home/devices/water_softener.py @@ -0,0 +1,36 @@ +import logging +from typing import List + +from homeassistant.helpers.entity import Entity +from gehomesdk import ErdCode, ErdApplianceType + +from .base import ApplianceApi +from ..entities import ( + GeErdSensor, + GeErdBinarySensor, + GeErdShutoffPositionSelect, +) + +_LOGGER = logging.getLogger(__name__) + + +class WaterSoftenerApi(ApplianceApi): + """API class for water softener objects""" + + APPLIANCE_TYPE = ErdApplianceType.WATER_SOFTENER + + def get_all_entities(self) -> List[Entity]: + base_entities = super().get_all_entities() + + ws_entities = [ + GeErdBinarySensor(self, ErdCode.WH_FILTER_MANUAL_MODE, icon_on_override="mdi:human", icon_off_override="mdi:robot"), + GeErdSensor(self, ErdCode.WH_FILTER_FLOW_RATE), + GeErdBinarySensor(self, ErdCode.WH_FILTER_FLOW_ALERT, device_class_override="moisture"), + GeErdSensor(self, ErdCode.WH_FILTER_DAY_USAGE), + GeErdSensor(self, ErdCode.WH_SOFTENER_ERROR_CODE, icon_override="mdi:alert-circle"), + GeErdSensor(self, ErdCode.WH_SOFTENER_LOW_SALT, icon_override="mdi:grain"), + GeErdSensor(self, ErdCode.WH_SOFTENER_SHUTOFF_VALVE_STATE, icon_override="mdi:state-machine"), + GeErdShutoffPositionSelect(self, ErdCode.WH_SOFTENER_SHUTOFF_VALVE_CONTROL), + ] + entities = base_entities + ws_entities + return entities diff --git a/custom_components/ge_home/entities/__init__.py b/custom_components/ge_home/entities/__init__.py index b03b590..0502aff 100644 --- a/custom_components/ge_home/entities/__init__.py +++ b/custom_components/ge_home/entities/__init__.py @@ -5,4 +5,5 @@ from .oven import * from .water_filter import * from .advantium import * from .ac import * -from .hood import * \ No newline at end of file +from .hood import * +from .water_softener import * \ No newline at end of file diff --git a/custom_components/ge_home/entities/water_softener/__init__.py b/custom_components/ge_home/entities/water_softener/__init__.py new file mode 100644 index 0000000..7ae738e --- /dev/null +++ b/custom_components/ge_home/entities/water_softener/__init__.py @@ -0,0 +1 @@ +from .shutoff_position import GeErdShutoffPositionSelect \ No newline at end of file diff --git a/custom_components/ge_home/entities/water_softener/shutoff_position.py b/custom_components/ge_home/entities/water_softener/shutoff_position.py new file mode 100644 index 0000000..38b0929 --- /dev/null +++ b/custom_components/ge_home/entities/water_softener/shutoff_position.py @@ -0,0 +1,65 @@ +import logging +from typing import List, Any, Optional + +from gehomesdk import ErdCodeType, ErdWaterSoftenerShutoffValveState, ErdCode +from ...devices import ApplianceApi +from ..common import GeErdSelect, OptionsConverter + +_LOGGER = logging.getLogger(__name__) + +class FilterPositionOptionsConverter(OptionsConverter): + @property + def options(self) -> List[str]: + return [i.name.title() + for i in ErdWaterSoftenerShutoffValveState + if i not in [ErdWaterSoftenerShutoffValveState.UNKNOWN, ErdWaterSoftenerShutoffValveState.TRANSITION]] + def from_option_string(self, value: str) -> Any: + try: + return ErdWaterSoftenerShutoffValveState[value.upper()] + except: + _LOGGER.warn(f"Could not set filter position to {value.upper()}") + return ErdWaterSoftenerShutoffValveState.UNKNOWN + def to_option_string(self, value: Any) -> Optional[str]: + try: + if value is not None: + return value.name.title() + except: + pass + return ErdWaterSoftenerShutoffValveState.UNKNOWN.name.title() + +class GeErdShutoffPositionSelect(GeErdSelect): + def __init__(self, api: ApplianceApi, erd_code: ErdCodeType): + super().__init__(api, erd_code, FilterPositionOptionsConverter(), icon_override="mdi:valve") + + @property + def current_option(self): + """Return the current selected option""" + + #if we're transitioning or don't know what the mode is, don't allow changes + mode: ErdWaterSoftenerShutoffValveState = self.appliance.get_erd_value(ErdCode.WH_SOFTENER_SHUTOFF_VALVE_STATE) + if mode in [ErdWaterSoftenerShutoffValveState.TRANSITION, ErdWaterSoftenerShutoffValveState.UNKNOWN]: + return mode.name.title() + + return self._converter.to_option_string(self.appliance.get_erd_value(self.erd_code)) + + @property + def options(self) -> List[str]: + """Return a list of options""" + + #if we're transitioning or don't know what the mode is, don't allow changes + mode: ErdWaterSoftenerShutoffValveState = self.appliance.get_erd_value(ErdCode.WH_SOFTENER_SHUTOFF_VALVE_STATE) + if mode in [ErdWaterSoftenerShutoffValveState.TRANSITION, ErdWaterSoftenerShutoffValveState.UNKNOWN]: + return mode.name.title() + + return self._converter.options + + async def async_select_option(self, option: str) -> None: + value = self._converter.from_option_string(option) + if value in [ErdWaterSoftenerShutoffValveState.UNKNOWN, ErdWaterSoftenerShutoffValveState.TRANSITION]: + _LOGGER.debug("Cannot set position to transition/unknown") + return + if self.appliance.get_erd_value(ErdCode.WH_SOFTENER_SHUTOFF_VALVE_STATE) == ErdWaterSoftenerShutoffValveState.TRANSITION: + _LOGGER.debug("Cannot set position if in transition") + return + + return await super().async_select_option(option) From 8c64ef78cecd0c0fcdc01cdf0314bf3feb53f636 Mon Sep 17 00:00:00 2001 From: Jack Simbach Date: Sat, 11 Dec 2021 00:57:41 -0500 Subject: [PATCH 04/28] - initial support for opal ice makers --- CHANGELOG.md | 1 + custom_components/ge_home/devices/__init__.py | 6 ++- custom_components/ge_home/devices/oim.py | 39 +++++++++++++++++++ .../ge_home/entities/__init__.py | 3 +- .../ge_home/entities/common/ge_erd_entity.py | 6 ++- .../entities/opal_ice_maker/__init__.py | 1 + .../opal_ice_maker/oim_light_level_options.py | 26 +++++++++++++ 7 files changed, 78 insertions(+), 4 deletions(-) create mode 100644 custom_components/ge_home/devices/oim.py create mode 100644 custom_components/ge_home/entities/opal_ice_maker/__init__.py create mode 100644 custom_components/ge_home/entities/opal_ice_maker/oim_light_level_options.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 7640c63..0938980 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ - Changed the sensors to use native value/uom - Changed the temperatures to always be natively fahrenheit (API appears to always use this system) - Initial support for Water Softeners (@npentell) +- Initial support for Opal Ice Makers (@mbcomer, @knobunc) ## 0.5.0 diff --git a/custom_components/ge_home/devices/__init__.py b/custom_components/ge_home/devices/__init__.py index a7c2e00..1208a0d 100644 --- a/custom_components/ge_home/devices/__init__.py +++ b/custom_components/ge_home/devices/__init__.py @@ -3,8 +3,6 @@ from typing import Type from gehomesdk.erd import ErdApplianceType -from custom_components.ge_home.devices.water_softener import WaterSoftenerApi - from .base import ApplianceApi from .oven import OvenApi from .fridge import FridgeApi @@ -18,6 +16,8 @@ from .wac import WacApi from .sac import SacApi from .pac import PacApi from .hood import HoodApi +from .water_softener import WaterSoftenerApi +from .oim import OimApi _LOGGER = logging.getLogger(__name__) @@ -51,6 +51,8 @@ def get_appliance_api_type(appliance_type: ErdApplianceType) -> Type: return PacApi if appliance_type == ErdApplianceType.HOOD: return HoodApi + if appliance_type == ErdApplianceType.OPAL_ICE_MAKER: + return OimApi # Fallback return ApplianceApi diff --git a/custom_components/ge_home/devices/oim.py b/custom_components/ge_home/devices/oim.py new file mode 100644 index 0000000..c2703cc --- /dev/null +++ b/custom_components/ge_home/devices/oim.py @@ -0,0 +1,39 @@ +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 ( + OimLightLevelOptionsConverter, + GeErdSensor, + GeErdSelect, + GeErdSwitch, + ErdOnOffBoolConverter +) + +_LOGGER = logging.getLogger(__name__) + + +class OimApi(ApplianceApi): + """API class for Oven Hood objects""" + APPLIANCE_TYPE = ErdApplianceType.HOOD + + def get_all_entities(self) -> List[Entity]: + base_entities = super().get_all_entities() + + oim_entities = [ + GeErdSensor(self, ErdCode.OIM_STATUS), + GeErdSensor(self, ErdCode.OIM_FILTER_STATUS), + GeErdSelect(self, ErdCode.OIM_LIGHT_LEVEL, OimLightLevelOptionsConverter()), + GeErdSwitch(self, ErdCode.OIM_POWER, bool_converter=ErdOnOffBoolConverter(), icon_on_override="mdi:power-on", icon_off_override="mdi:power-off"), + ] + + entities = base_entities + oim_entities + return entities + diff --git a/custom_components/ge_home/entities/__init__.py b/custom_components/ge_home/entities/__init__.py index 0502aff..b1da8ef 100644 --- a/custom_components/ge_home/entities/__init__.py +++ b/custom_components/ge_home/entities/__init__.py @@ -6,4 +6,5 @@ from .water_filter import * from .advantium import * from .ac import * from .hood import * -from .water_softener import * \ No newline at end of file +from .water_softener import * +from .opal_ice_maker import * \ 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 a23a36c..a93a1fb 100644 --- a/custom_components/ge_home/entities/common/ge_erd_entity.py +++ b/custom_components/ge_home/entities/common/ge_erd_entity.py @@ -138,6 +138,10 @@ class GeErdEntity(GeEntity): if self.erd_code_class == ErdCodeClass.FAN: return "mdi:fan" if self.erd_code_class == ErdCodeClass.LIGHT: - return "mdi:lightbulb" + return "mdi:lightbulb" + if self.erd_code_class == ErdCodeClass.OIM_SENSOR: + return "mdi:snowflake" + if self.erd_code_class == ErdCodeClass.WATERSOFTENER_SENSOR: + return "mdi:water" return None diff --git a/custom_components/ge_home/entities/opal_ice_maker/__init__.py b/custom_components/ge_home/entities/opal_ice_maker/__init__.py new file mode 100644 index 0000000..5ec3f31 --- /dev/null +++ b/custom_components/ge_home/entities/opal_ice_maker/__init__.py @@ -0,0 +1 @@ +from .oim_light_level_options import OimLightLevelOptionsConverter \ No newline at end of file diff --git a/custom_components/ge_home/entities/opal_ice_maker/oim_light_level_options.py b/custom_components/ge_home/entities/opal_ice_maker/oim_light_level_options.py new file mode 100644 index 0000000..019500d --- /dev/null +++ b/custom_components/ge_home/entities/opal_ice_maker/oim_light_level_options.py @@ -0,0 +1,26 @@ +import logging +from typing import List, Any, Optional + +from gehomesdk import ErdCodeType, ErdOimLightLevel, ErdCode +from ...devices import ApplianceApi +from ..common import GeErdSelect, OptionsConverter + +_LOGGER = logging.getLogger(__name__) + +class OimLightLevelOptionsConverter(OptionsConverter): + @property + def options(self) -> List[str]: + return [i.stringify() for i in ErdOimLightLevel] + def from_option_string(self, value: str) -> Any: + try: + return ErdOimLightLevel[value.upper()] + except: + _LOGGER.warn(f"Could not set hood light level to {value.upper()}") + return ErdOimLightLevel.OFF + def to_option_string(self, value: ErdOimLightLevel) -> Optional[str]: + try: + if value is not None: + return value.stringify() + except: + pass + return ErdOimLightLevel.OFF.stringify() From 52afe3c044d7fe3daba4287a67785356e028dc22 Mon Sep 17 00:00:00 2001 From: Jack Simbach Date: Sat, 11 Dec 2021 00:59:28 -0500 Subject: [PATCH 05/28] - updated deprecated icons --- custom_components/ge_home/devices/dishwasher.py | 4 ++-- custom_components/ge_home/entities/common/ge_erd_entity.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/custom_components/ge_home/devices/dishwasher.py b/custom_components/ge_home/devices/dishwasher.py index 4700c78..d51913e 100644 --- a/custom_components/ge_home/devices/dishwasher.py +++ b/custom_components/ge_home/devices/dishwasher.py @@ -23,14 +23,14 @@ class DishwasherApi(ApplianceApi): GeErdSensor(self, ErdCode.DISHWASHER_CYCLE_STATE, icon_override="mdi:state-machine"), GeErdSensor(self, ErdCode.DISHWASHER_OPERATING_MODE), GeErdSensor(self, ErdCode.DISHWASHER_PODS_REMAINING_VALUE, uom_override="pods"), - GeErdSensor(self, ErdCode.DISHWASHER_RINSE_AGENT, icon_override="mdi:sparkles"), + GeErdSensor(self, ErdCode.DISHWASHER_RINSE_AGENT, icon_override="mdi:shimmer"), GeErdSensor(self, ErdCode.DISHWASHER_TIME_REMAINING), GeErdBinarySensor(self, ErdCode.DISHWASHER_DOOR_STATUS), #User Setttings GeErdPropertySensor(self, ErdCode.DISHWASHER_USER_SETTING, "sound", icon_override="mdi:volume-high"), GeErdPropertySensor(self, ErdCode.DISHWASHER_USER_SETTING, "lock_control", icon_override="mdi:lock"), - GeErdPropertySensor(self, ErdCode.DISHWASHER_USER_SETTING, "sabbath", icon_override="mdi:judaism"), + GeErdPropertySensor(self, ErdCode.DISHWASHER_USER_SETTING, "sabbath", icon_override="mdi:star-david"), GeErdPropertySensor(self, ErdCode.DISHWASHER_USER_SETTING, "cycle_mode", icon_override="mdi:state-machine"), GeErdPropertySensor(self, ErdCode.DISHWASHER_USER_SETTING, "presoak", icon_override="mdi:water"), GeErdPropertySensor(self, ErdCode.DISHWASHER_USER_SETTING, "bottle_jet", icon_override="mdi:bottle-tonic-outline"), 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 a93a1fb..32f7b66 100644 --- a/custom_components/ge_home/entities/common/ge_erd_entity.py +++ b/custom_components/ge_home/entities/common/ge_erd_entity.py @@ -104,7 +104,7 @@ class GeErdEntity(GeEntity): if self.erd_code_class == ErdCodeClass.LOCK_CONTROL: return "mdi:lock-outline" if self.erd_code_class == ErdCodeClass.SABBATH_CONTROL: - return "mdi:judaism" + return "mdi:star-david" if self.erd_code_class == ErdCodeClass.COOLING_CONTROL: return "mdi:snowflake" if self.erd_code_class == ErdCodeClass.OVEN_SENSOR: From 739c272e41f6320cfa602d097a6fc67e5cb1a7b3 Mon Sep 17 00:00:00 2001 From: Jack Simbach Date: Sat, 11 Dec 2021 11:06:30 -0500 Subject: [PATCH 06/28] - updated versions and documentation --- CHANGELOG.md | 1 + README.md | 5 ++++- custom_components/ge_home/manifest.json | 4 ++-- hacs.json | 2 +- info.md | 21 ++++++++++++++++++++- 5 files changed, 28 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0938980..19f908e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ - Changed the temperatures to always be natively fahrenheit (API appears to always use this system) - Initial support for Water Softeners (@npentell) - Initial support for Opal Ice Makers (@mbcomer, @knobunc) +- Updated deprecated icons (@mjmeli, @schmittx) ## 0.5.0 diff --git a/README.md b/README.md index ad30d3d..59ec6e0 100644 --- a/README.md +++ b/README.md @@ -12,9 +12,12 @@ Integration for GE WiFi-enabled appliances into Home Assistant. This integratio - Dishwasher - Laundry (Washer/Dryer) - Whole Home Water Filter +- Whole Home Water Softener - A/C (Portable, Split, Window) -- Range Hoods +- Range Hood - Advantium +- Microwave +- Opal Ice Maker **Forked from Andrew Mark's [repository](https://github.com/ajmarks/ha_components).** diff --git a/custom_components/ge_home/manifest.json b/custom_components/ge_home/manifest.json index d670012..5d2e346 100644 --- a/custom_components/ge_home/manifest.json +++ b/custom_components/ge_home/manifest.json @@ -3,7 +3,7 @@ "name": "GE Home (SmartHQ)", "config_flow": true, "documentation": "https://github.com/simbaja/ha_gehome", - "requirements": ["gehomesdk==0.4.16","magicattr==0.1.5"], + "requirements": ["gehomesdk==0.4.17","magicattr==0.1.5"], "codeowners": ["@simbaja"], - "version": "0.5.0" + "version": "0.6.0" } diff --git a/hacs.json b/hacs.json index f508818..25fb813 100644 --- a/hacs.json +++ b/hacs.json @@ -1,6 +1,6 @@ { "name": "GE Home (SmartHQ)", - "homeassistant": "2021.7.1", + "homeassistant": "2021.11.0", "domains": ["binary_sensor", "sensor", "switch", "water_heater", "select"], "iot_class": "Cloud Polling" } diff --git a/info.md b/info.md index 63cc22b..e77e223 100644 --- a/info.md +++ b/info.md @@ -2,12 +2,17 @@ Integration for GE WiFi-enabled appliances into Home Assistant. This integration currently support the following devices: -- Fridge +- Fridge - Oven - Dishwasher - Laundry (Washer/Dryer) - Whole Home Water Filter +- Whole Home Water Softener +- A/C (Portable, Split, Window) +- Range Hood - Advantium +- Microwave +- Opal Ice Maker **Forked from Andrew Mark's [repository](https://github.com/ajmarks/ha_components).** @@ -34,6 +39,11 @@ A/C Controls: #### Breaking Changes +{% if version_installed.split('.') | map('int') < '0.6.0'.split('.') | map('int') %} +- Changed the sensors to use native value/uom +- Changed the temperatures to always be natively fahrenheit (API appears to always use this system) +{% endif %} + {% if version_installed.split('.') | map('int') < '0.4.0'.split('.') | map('int') %} - Laundry support changes will cause entity names to be different, you will need to fix in HA (uninstall, reboot, delete leftover entitites, install, reboot) {% endif %} @@ -46,6 +56,11 @@ A/C Controls: #### Features +{% if version_installed.split('.') | map('int') < '0.6.0'.split('.') | map('int') %} +- Initial support for Water Softeners (@npentell) +- Initial support for Opal Ice Makers (@mbcomer, @knobunc) +{% endif %} + {% if version_installed.split('.') | map('int') < '0.5.0'.split('.') | map('int') %} - Support for Oven Hood units (@digitalbites) - Added extended mode support for ovens @@ -67,6 +82,10 @@ A/C Controls: #### Bugfixes +{% if version_installed.split('.') | map('int') < '0.6.0'.split('.') | map('int') %} +- Updated deprecated icons (@mjmeli, @schmittx) +{% endif %} + {% if version_installed.split('.') | map('int') < '0.5.0'.split('.') | map('int') %} - Advantium fixes (@willhayslett) - Fixed device info when serial not present (@Xe138) From d0e46ed6077a0de7a9378709244eb1b213a1b340 Mon Sep 17 00:00:00 2001 From: Jack Simbach Date: Sat, 11 Dec 2021 11:10:26 -0500 Subject: [PATCH 07/28] - added water softener life remaining sensor --- custom_components/ge_home/devices/water_softener.py | 1 + 1 file changed, 1 insertion(+) diff --git a/custom_components/ge_home/devices/water_softener.py b/custom_components/ge_home/devices/water_softener.py index 5490082..fed8841 100644 --- a/custom_components/ge_home/devices/water_softener.py +++ b/custom_components/ge_home/devices/water_softener.py @@ -30,6 +30,7 @@ class WaterSoftenerApi(ApplianceApi): GeErdSensor(self, ErdCode.WH_SOFTENER_ERROR_CODE, icon_override="mdi:alert-circle"), GeErdSensor(self, ErdCode.WH_SOFTENER_LOW_SALT, icon_override="mdi:grain"), GeErdSensor(self, ErdCode.WH_SOFTENER_SHUTOFF_VALVE_STATE, icon_override="mdi:state-machine"), + GeErdSensor(self, ErdCode.WH_SOFTENER_SALT_LIFE_REMAINING, icon_override="mdi:wrench-clock"), GeErdShutoffPositionSelect(self, ErdCode.WH_SOFTENER_SHUTOFF_VALVE_CONTROL), ] entities = base_entities + ws_entities From 034d0df55885e25ba9e3f0be86abb9498d231c0d Mon Sep 17 00:00:00 2001 From: Jack Simbach Date: Sat, 11 Dec 2021 18:41:07 -0500 Subject: [PATCH 08/28] - added oven light selections --- custom_components/ge_home/devices/oven.py | 11 +++- .../ge_home/entities/oven/__init__.py | 1 + .../oven/ge_oven_light_level_select.py | 65 +++++++++++++++++++ 3 files changed, 76 insertions(+), 1 deletion(-) create mode 100644 custom_components/ge_home/entities/oven/ge_oven_light_level_select.py diff --git a/custom_components/ge_home/devices/oven.py b/custom_components/ge_home/devices/oven.py index 0dde6d5..ff2d077 100644 --- a/custom_components/ge_home/devices/oven.py +++ b/custom_components/ge_home/devices/oven.py @@ -9,7 +9,8 @@ from gehomesdk import ( ErdApplianceType, OvenConfiguration, ErdCooktopConfig, - CooktopStatus + CooktopStatus, + ErdOvenLightLevelAvailability ) from .base import ApplianceApi @@ -20,6 +21,7 @@ from ..entities import ( GeErdPropertySensor, GeErdPropertyBinarySensor, GeOven, + GeOvenLightLevelSelect, UPPER_OVEN, LOWER_OVEN ) @@ -41,6 +43,9 @@ class OvenApi(ApplianceApi): has_upper_raw_temperature = self.has_erd_code(ErdCode.UPPER_OVEN_RAW_TEMPERATURE) has_lower_raw_temperature = self.has_erd_code(ErdCode.LOWER_OVEN_RAW_TEMPERATURE) + upper_light_availability: ErdOvenLightLevelAvailability = self.try_get_erd_value(ErdCode.UPPER_OVEN_LIGHT_AVAILABILITY) + lower_light_availability: ErdOvenLightLevelAvailability = self.try_get_erd_value(ErdCode.LOWER_OVEN_LIGHT_AVAILABILITY) + _LOGGER.debug(f"Oven Config: {oven_config}") _LOGGER.debug(f"Cooktop Config: {cooktop_config}") oven_entities = [] @@ -69,6 +74,8 @@ class OvenApi(ApplianceApi): 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: + 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)), @@ -81,6 +88,8 @@ class OvenApi(ApplianceApi): ]) 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: + oven_entities.append(GeOvenLightLevelSelect(self, ErdCode.UPPER_OVEN_LIGHT, self._single_name(ErdCode.UPPER_OVEN_LIGHT))) if cooktop_config == ErdCooktopConfig.PRESENT: diff --git a/custom_components/ge_home/entities/oven/__init__.py b/custom_components/ge_home/entities/oven/__init__.py index a5e7f85..e4166e8 100644 --- a/custom_components/ge_home/entities/oven/__init__.py +++ b/custom_components/ge_home/entities/oven/__init__.py @@ -1,2 +1,3 @@ from .ge_oven import GeOven +from .ge_oven_light_level_select import GeOvenLightLevelSelect from .const import UPPER_OVEN, LOWER_OVEN \ No newline at end of file diff --git a/custom_components/ge_home/entities/oven/ge_oven_light_level_select.py b/custom_components/ge_home/entities/oven/ge_oven_light_level_select.py new file mode 100644 index 0000000..c94a3c0 --- /dev/null +++ b/custom_components/ge_home/entities/oven/ge_oven_light_level_select.py @@ -0,0 +1,65 @@ +import logging +from typing import List, Any, Optional + +from gehomesdk import ErdCodeType, ErdOvenLightLevelAvailability, ErdOvenLightLevel, ErdCode +from ...devices import ApplianceApi +from ..common import GeErdSelect, OptionsConverter + +_LOGGER = logging.getLogger(__name__) + +class OvenLightLevelOptionsConverter(OptionsConverter): + def __init__(self, availability: ErdOvenLightLevelAvailability): + super().__init__() + self.availability = availability + self.excluded_levels = [ErdOvenLightLevel.NOT_AVAILABLE] + + if not availability or not availability.has_dimmed: + self.excluded_levels.append(ErdOvenLightLevel.DIM) + + @property + def options(self) -> List[str]: + return [i.stringify() for i in ErdOvenLightLevel if i not in self.excluded_levels] + def from_option_string(self, value: str) -> Any: + try: + return ErdOvenLightLevel[value.upper()] + except: + _LOGGER.warn(f"Could not set Oven light level to {value.upper()}") + return ErdOvenLightLevel.OFF + def to_option_string(self, value: ErdOvenLightLevel) -> Optional[str]: + try: + if value is not None: + return value.stringify() + except: + pass + return ErdOvenLightLevel.OFF.stringify() + +class GeOvenLightLevelSelect(GeErdSelect): + + def __init__(self, api: ApplianceApi, erd_code: ErdCodeType): + self._availability: ErdOvenLightLevelAvailability = api.try_get_erd_value(ErdCode.LOWER_OVEN_LIGHT_AVAILABILITY) + + #check to see if we have a status + value: ErdOvenLightLevel = api.try_get_erd_value(erd_code) + self._has_status = value is not None and value != ErdOvenLightLevel.NOT_AVAILABLE + self._assumed_state = ErdOvenLightLevel.OFF + + super().__init__(api, erd_code, OvenLightLevelOptionsConverter(self._availability)) + + 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 + \ No newline at end of file From c6155d15b6c1c89367bc6e096e59c9a6ac4e82a9 Mon Sep 17 00:00:00 2001 From: Jack Simbach Date: Sat, 11 Dec 2021 18:42:07 -0500 Subject: [PATCH 09/28] - fixed issue with removed entity type --- custom_components/ge_home/devices/sac.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/custom_components/ge_home/devices/sac.py b/custom_components/ge_home/devices/sac.py index 98686e0..a1dfad5 100644 --- a/custom_components/ge_home/devices/sac.py +++ b/custom_components/ge_home/devices/sac.py @@ -5,7 +5,7 @@ from homeassistant.helpers.entity import Entity from gehomesdk.erd import ErdCode, ErdApplianceType from .base import ApplianceApi -from ..entities import GeSacClimate, GeSacTemperatureSensor, GeErdSensor, GeErdSwitch, ErdOnOffBoolConverter +from ..entities import GeSacClimate, GeErdSensor, GeErdSwitch, ErdOnOffBoolConverter _LOGGER = logging.getLogger(__name__) From b07e8f9dee9d394e417495f8c8f446cbd5a941cc Mon Sep 17 00:00:00 2001 From: Jack Simbach Date: Sat, 11 Dec 2021 19:07:21 -0500 Subject: [PATCH 10/28] - updated gehomesdk version requirement --- custom_components/ge_home/manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/custom_components/ge_home/manifest.json b/custom_components/ge_home/manifest.json index 5d2e346..4bf6cc5 100644 --- a/custom_components/ge_home/manifest.json +++ b/custom_components/ge_home/manifest.json @@ -3,7 +3,7 @@ "name": "GE Home (SmartHQ)", "config_flow": true, "documentation": "https://github.com/simbaja/ha_gehome", - "requirements": ["gehomesdk==0.4.17","magicattr==0.1.5"], + "requirements": ["gehomesdk==0.4.19","magicattr==0.1.5"], "codeowners": ["@simbaja"], "version": "0.6.0" } From 180b11f66727b2eb992378fef61a98ced106416b Mon Sep 17 00:00:00 2001 From: Jack Simbach Date: Sat, 11 Dec 2021 19:55:09 -0500 Subject: [PATCH 11/28] - fixed interitance and state classes in GeErdSensor --- .../ge_home/entities/common/ge_erd_sensor.py | 22 +++++-------------- 1 file changed, 5 insertions(+), 17 deletions(-) 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 264b86a..240de5b 100644 --- a/custom_components/ge_home/entities/common/ge_erd_sensor.py +++ b/custom_components/ge_home/entities/common/ge_erd_sensor.py @@ -1,7 +1,7 @@ import logging from typing import Optional from gehomesdk.erd.erd_data_type import ErdDataType -from homeassistant.components.sensor import STATE_CLASS_MEASUREMENT +from homeassistant.components.sensor import SensorEntity, SensorStateClass from homeassistant.const import ( DEVICE_CLASS_ENERGY, @@ -13,25 +13,13 @@ from homeassistant.const import ( TEMP_CELSIUS, TEMP_FAHRENHEIT, ) -#from homeassistant.components.sensor import ( -# STATE_CLASS_MEASUREMENT, -# STATE_CLASS_TOTAL_INCREASING -#) -# For now, let's not force the newer version, we'll use the same constants -# but it'll be optional. -# TODO: Force the usage of new HA -STATE_CLASS_MEASUREMENT = "measurement" -STATE_CLASS_TOTAL_INCREASING = 'total_increasing' - -from homeassistant.helpers.entity import Entity from gehomesdk import ErdCode, ErdCodeType, ErdCodeClass, ErdMeasurementUnits - from .ge_erd_entity import GeErdEntity from ...devices import ApplianceApi _LOGGER = logging.getLogger(__name__) -class GeErdSensor(GeErdEntity, Entity): +class GeErdSensor(GeErdEntity, SensorEntity): """GE Entity for sensors""" def __init__( @@ -149,11 +137,11 @@ class GeErdSensor(GeErdEntity, Entity): return self._state_class_override if self.device_class in [DEVICE_CLASS_TEMPERATURE, DEVICE_CLASS_ENERGY]: - return STATE_CLASS_MEASUREMENT + return SensorStateClass.MEASUREMENT if self.erd_code_class in [ErdCodeClass.FLOW_RATE, ErdCodeClass.PERCENTAGE]: - return STATE_CLASS_MEASUREMENT + return SensorStateClass.MEASUREMENT if self.erd_code_class in [ErdCodeClass.LIQUID_VOLUME]: - return STATE_CLASS_TOTAL_INCREASING + return SensorStateClass.TOTAL_INCREASING return None From 8fc846ebb7e319f33d5debc64b5a1a32782b84b7 Mon Sep 17 00:00:00 2001 From: Jack Simbach Date: Sat, 11 Dec 2021 20:28:53 -0500 Subject: [PATCH 12/28] - fixed issues with oven light control --- .../ge_home/entities/oven/ge_oven_light_level_select.py | 7 ++++--- custom_components/ge_home/manifest.json | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/custom_components/ge_home/entities/oven/ge_oven_light_level_select.py b/custom_components/ge_home/entities/oven/ge_oven_light_level_select.py index c94a3c0..8c63973 100644 --- a/custom_components/ge_home/entities/oven/ge_oven_light_level_select.py +++ b/custom_components/ge_home/entities/oven/ge_oven_light_level_select.py @@ -13,7 +13,7 @@ class OvenLightLevelOptionsConverter(OptionsConverter): self.availability = availability self.excluded_levels = [ErdOvenLightLevel.NOT_AVAILABLE] - if not availability or not availability.has_dimmed: + if not availability or not availability.dim_available: self.excluded_levels.append(ErdOvenLightLevel.DIM) @property @@ -35,7 +35,7 @@ class OvenLightLevelOptionsConverter(OptionsConverter): class GeOvenLightLevelSelect(GeErdSelect): - def __init__(self, api: ApplianceApi, erd_code: ErdCodeType): + def __init__(self, api: ApplianceApi, erd_code: ErdCodeType, erd_override: str = None): self._availability: ErdOvenLightLevelAvailability = api.try_get_erd_value(ErdCode.LOWER_OVEN_LIGHT_AVAILABILITY) #check to see if we have a status @@ -43,8 +43,9 @@ class GeOvenLightLevelSelect(GeErdSelect): self._has_status = value is not None and value != ErdOvenLightLevel.NOT_AVAILABLE self._assumed_state = ErdOvenLightLevel.OFF - super().__init__(api, erd_code, OvenLightLevelOptionsConverter(self._availability)) + super().__init__(api, erd_code, OvenLightLevelOptionsConverter(self._availability), erd_override=erd_override) + @property def assumed_state(self) -> bool: return not self._has_status diff --git a/custom_components/ge_home/manifest.json b/custom_components/ge_home/manifest.json index 4bf6cc5..af54f66 100644 --- a/custom_components/ge_home/manifest.json +++ b/custom_components/ge_home/manifest.json @@ -3,7 +3,7 @@ "name": "GE Home (SmartHQ)", "config_flow": true, "documentation": "https://github.com/simbaja/ha_gehome", - "requirements": ["gehomesdk==0.4.19","magicattr==0.1.5"], + "requirements": ["gehomesdk==0.4.20","magicattr==0.1.5"], "codeowners": ["@simbaja"], "version": "0.6.0" } From d77062cdf9960d253cab6ed275b6aa0a8b3c0c93 Mon Sep 17 00:00:00 2001 From: Jack Simbach Date: Sun, 12 Dec 2021 11:10:11 -0500 Subject: [PATCH 13/28] - fixes for flow rate and icons --- custom_components/ge_home/devices/water_filter.py | 3 ++- custom_components/ge_home/devices/water_softener.py | 5 +++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/custom_components/ge_home/devices/water_filter.py b/custom_components/ge_home/devices/water_filter.py index b926291..d938df1 100644 --- a/custom_components/ge_home/devices/water_filter.py +++ b/custom_components/ge_home/devices/water_filter.py @@ -7,6 +7,7 @@ from gehomesdk import ErdCode, ErdApplianceType from .base import ApplianceApi from ..entities import ( GeErdSensor, + GeErdPropertySensor, GeErdBinarySensor, GeErdFilterPositionSelect, ) @@ -28,7 +29,7 @@ class WaterFilterApi(ApplianceApi): GeErdFilterPositionSelect(self, ErdCode.WH_FILTER_POSITION), GeErdBinarySensor(self, ErdCode.WH_FILTER_MANUAL_MODE, icon_on_override="mdi:human", icon_off_override="mdi:robot"), GeErdBinarySensor(self, ErdCode.WH_FILTER_LEAK_VALIDITY, device_class_override="moisture"), - GeErdSensor(self, ErdCode.WH_FILTER_FLOW_RATE), + GeErdPropertySensor(self, ErdCode.WH_FILTER_FLOW_RATE, "flow_rate"), GeErdSensor(self, ErdCode.WH_FILTER_DAY_USAGE), GeErdSensor(self, ErdCode.WH_FILTER_LIFE_REMAINING), GeErdBinarySensor(self, ErdCode.WH_FILTER_FLOW_ALERT, device_class_override="moisture"), diff --git a/custom_components/ge_home/devices/water_softener.py b/custom_components/ge_home/devices/water_softener.py index fed8841..5e5feed 100644 --- a/custom_components/ge_home/devices/water_softener.py +++ b/custom_components/ge_home/devices/water_softener.py @@ -7,6 +7,7 @@ from gehomesdk import ErdCode, ErdApplianceType from .base import ApplianceApi from ..entities import ( GeErdSensor, + GeErdPropertySensor, GeErdBinarySensor, GeErdShutoffPositionSelect, ) @@ -24,13 +25,13 @@ class WaterSoftenerApi(ApplianceApi): ws_entities = [ GeErdBinarySensor(self, ErdCode.WH_FILTER_MANUAL_MODE, icon_on_override="mdi:human", icon_off_override="mdi:robot"), - GeErdSensor(self, ErdCode.WH_FILTER_FLOW_RATE), + GeErdPropertySensor(self, ErdCode.WH_FILTER_FLOW_RATE, "flow_rate"), GeErdBinarySensor(self, ErdCode.WH_FILTER_FLOW_ALERT, device_class_override="moisture"), GeErdSensor(self, ErdCode.WH_FILTER_DAY_USAGE), GeErdSensor(self, ErdCode.WH_SOFTENER_ERROR_CODE, icon_override="mdi:alert-circle"), GeErdSensor(self, ErdCode.WH_SOFTENER_LOW_SALT, icon_override="mdi:grain"), GeErdSensor(self, ErdCode.WH_SOFTENER_SHUTOFF_VALVE_STATE, icon_override="mdi:state-machine"), - GeErdSensor(self, ErdCode.WH_SOFTENER_SALT_LIFE_REMAINING, icon_override="mdi:wrench-clock"), + GeErdSensor(self, ErdCode.WH_SOFTENER_SALT_LIFE_REMAINING, icon_override="mdi:calendar-clock"), GeErdShutoffPositionSelect(self, ErdCode.WH_SOFTENER_SHUTOFF_VALVE_CONTROL), ] entities = base_entities + ws_entities From a9cd736d5d7daeeebaed2e9337fc18a87ee2f3e5 Mon Sep 17 00:00:00 2001 From: Jack Simbach Date: Sun, 12 Dec 2021 11:23:13 -0500 Subject: [PATCH 14/28] - documentation updates --- CHANGELOG.md | 1 + hacs.json | 2 +- info.md | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 19f908e..78ab853 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## 0.6.0 +- Requires HA 2021.12.x or later - Changed the sensors to use native value/uom - Changed the temperatures to always be natively fahrenheit (API appears to always use this system) - Initial support for Water Softeners (@npentell) diff --git a/hacs.json b/hacs.json index 25fb813..e9e1a0b 100644 --- a/hacs.json +++ b/hacs.json @@ -1,6 +1,6 @@ { "name": "GE Home (SmartHQ)", - "homeassistant": "2021.11.0", + "homeassistant": "2021.12.0", "domains": ["binary_sensor", "sensor", "switch", "water_heater", "select"], "iot_class": "Cloud Polling" } diff --git a/info.md b/info.md index e77e223..a096566 100644 --- a/info.md +++ b/info.md @@ -40,6 +40,7 @@ A/C Controls: #### Breaking Changes {% if version_installed.split('.') | map('int') < '0.6.0'.split('.') | map('int') %} +- Requires HA version 2021.12.0 or later - Changed the sensors to use native value/uom - Changed the temperatures to always be natively fahrenheit (API appears to always use this system) {% endif %} From b870725afbea10aba55c003b4995f86faa582c71 Mon Sep 17 00:00:00 2001 From: Jack Simbach Date: Sun, 12 Dec 2021 11:24:45 -0500 Subject: [PATCH 15/28] - documentation --- CHANGELOG.md | 2 +- info.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 78ab853..f2d32e0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ - Requires HA 2021.12.x or later - Changed the sensors to use native value/uom -- Changed the temperatures to always be natively fahrenheit (API appears to always use this system) +- Changed the temperatures to always be natively fahrenheit (API appears to always use this system) (@vignatyuk) - Initial support for Water Softeners (@npentell) - Initial support for Opal Ice Makers (@mbcomer, @knobunc) - Updated deprecated icons (@mjmeli, @schmittx) diff --git a/info.md b/info.md index a096566..e88b544 100644 --- a/info.md +++ b/info.md @@ -42,7 +42,7 @@ A/C Controls: {% if version_installed.split('.') | map('int') < '0.6.0'.split('.') | map('int') %} - Requires HA version 2021.12.0 or later - Changed the sensors to use native value/uom -- Changed the temperatures to always be natively fahrenheit (API appears to always use this system) +- Changed the temperatures to always be natively fahrenheit (API appears to always use this system) (@vignatyuk) {% endif %} {% if version_installed.split('.') | map('int') < '0.4.0'.split('.') | map('int') %} From 4c2cf74cb64143dc892e51d851497e1cebf0a1ff Mon Sep 17 00:00:00 2001 From: Jack Simbach Date: Sun, 12 Dec 2021 12:37:29 -0500 Subject: [PATCH 16/28] - sdk version bump - initial support for microwaves - updated support for hoods - documentation updates --- CHANGELOG.md | 3 +- custom_components/ge_home/devices/__init__.py | 3 + custom_components/ge_home/devices/hood.py | 4 +- .../ge_home/devices/microwave.py | 56 +++++++++++++++++++ custom_components/ge_home/manifest.json | 2 +- info.md | 3 +- 6 files changed, 66 insertions(+), 5 deletions(-) create mode 100644 custom_components/ge_home/devices/microwave.py diff --git a/CHANGELOG.md b/CHANGELOG.md index f2d32e0..7f1930d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,8 @@ - Requires HA 2021.12.x or later - Changed the sensors to use native value/uom - Changed the temperatures to always be natively fahrenheit (API appears to always use this system) (@vignatyuk) -- Initial support for Water Softeners (@npentell) +- Initial support for Microwaves (@mbcomer, @mnestor) +- Initial support for Water Softeners (@npentell, @drjeff) - Initial support for Opal Ice Makers (@mbcomer, @knobunc) - Updated deprecated icons (@mjmeli, @schmittx) diff --git a/custom_components/ge_home/devices/__init__.py b/custom_components/ge_home/devices/__init__.py index 1208a0d..9e17d3b 100644 --- a/custom_components/ge_home/devices/__init__.py +++ b/custom_components/ge_home/devices/__init__.py @@ -16,6 +16,7 @@ from .wac import WacApi from .sac import SacApi from .pac import PacApi from .hood import HoodApi +from .microwave import MicrowaveApi from .water_softener import WaterSoftenerApi from .oim import OimApi @@ -51,6 +52,8 @@ def get_appliance_api_type(appliance_type: ErdApplianceType) -> Type: return PacApi if appliance_type == ErdApplianceType.HOOD: return HoodApi + if appliance_type == ErdApplianceType.MICROWAVE: + return MicrowaveApi if appliance_type == ErdApplianceType.OPAL_ICE_MAKER: return OimApi diff --git a/custom_components/ge_home/devices/hood.py b/custom_components/ge_home/devices/hood.py index e57b590..439c775 100644 --- a/custom_components/ge_home/devices/hood.py +++ b/custom_components/ge_home/devices/hood.py @@ -14,7 +14,7 @@ from .base import ApplianceApi from ..entities import ( GeHoodLightLevelSelect, GeHoodFanSpeedSelect, - GeErdSensor, + GeErdTimerSensor, GeErdSwitch, ErdOnOffBoolConverter ) @@ -45,7 +45,7 @@ class HoodApi(ApplianceApi): if light_availability and light_availability.is_available: hood_entities.append(GeHoodLightLevelSelect(self, ErdCode.HOOD_LIGHT_LEVEL)) if timer_availability == ErdOnOff.ON: - hood_entities.append(GeErdSensor(self, ErdCode.HOOD_TIMER)) + hood_entities.append(GeErdTimerSensor(self, ErdCode.HOOD_TIMER)) entities = base_entities + hood_entities return entities diff --git a/custom_components/ge_home/devices/microwave.py b/custom_components/ge_home/devices/microwave.py new file mode 100644 index 0000000..ec943fa --- /dev/null +++ b/custom_components/ge_home/devices/microwave.py @@ -0,0 +1,56 @@ +import logging +from typing import List + +from homeassistant.helpers.entity import Entity +from gehomesdk import ( + ErdCode, + ErdApplianceType, + ErdHoodFanSpeedAvailability, + ErdHoodLightLevelAvailability, + ErdOnOff +) + +from .base import ApplianceApi +from ..entities import ( + GeHoodLightLevelSelect, + GeHoodFanSpeedSelect, + GeErdPropertySensor, + GeErdPropertyBinarySensor, + GeErdBinarySensor, + GeErdTimerSensor +) + +_LOGGER = logging.getLogger(__name__) + + +class MicrowaveApi(ApplianceApi): + """API class for Microwave objects""" + APPLIANCE_TYPE = ErdApplianceType.MICROWAVE + + def get_all_entities(self) -> List[Entity]: + base_entities = super().get_all_entities() + + #get the availabilities + fan_availability: ErdHoodFanSpeedAvailability = self.try_get_erd_value(ErdCode.HOOD_FAN_SPEED_AVAILABILITY) + light_availability: ErdHoodLightLevelAvailability = self.try_get_erd_value(ErdCode.HOOD_LIGHT_LEVEL_AVAILABILITY) + + mwave_entities = [ + GeErdBinarySensor(self, ErdCode.MICROWAVE_REMOTE_ENABLE), + GeErdPropertySensor(self, ErdCode.MICROWAVE_STATE, "status"), + GeErdPropertyBinarySensor(self, ErdCode.MICROWAVE_STATE, "door_status", device_class_override="door"), + GeErdPropertySensor(self, ErdCode.MICROWAVE_STATE, "cook_mode", icon_override="mdi:food-turkey"), + GeErdPropertySensor(self, ErdCode.MICROWAVE_STATE, "power_level", icon_override="mdi:gauge"), + GeErdPropertySensor(self, ErdCode.MICROWAVE_STATE, "temperature", icon_override="mdi:thermometer"), + GeErdTimerSensor(self, ErdCode.MICROWAVE_COOK_TIMER), + GeErdTimerSensor(self, ErdCode.MICROWAVE_KITCHEN_TIMER) + ] + + if fan_availability and fan_availability.is_available: + mwave_entities.append(GeHoodFanSpeedSelect(self, ErdCode.HOOD_FAN_SPEED)) + #for now, represent as a select + if light_availability and light_availability.is_available: + mwave_entities.append(GeHoodLightLevelSelect(self, ErdCode.HOOD_LIGHT_LEVEL)) + + entities = base_entities + mwave_entities + return entities + diff --git a/custom_components/ge_home/manifest.json b/custom_components/ge_home/manifest.json index af54f66..b821356 100644 --- a/custom_components/ge_home/manifest.json +++ b/custom_components/ge_home/manifest.json @@ -3,7 +3,7 @@ "name": "GE Home (SmartHQ)", "config_flow": true, "documentation": "https://github.com/simbaja/ha_gehome", - "requirements": ["gehomesdk==0.4.20","magicattr==0.1.5"], + "requirements": ["gehomesdk==0.4.21","magicattr==0.1.5"], "codeowners": ["@simbaja"], "version": "0.6.0" } diff --git a/info.md b/info.md index e88b544..ae9a5b2 100644 --- a/info.md +++ b/info.md @@ -58,8 +58,9 @@ A/C Controls: #### Features {% if version_installed.split('.') | map('int') < '0.6.0'.split('.') | map('int') %} -- Initial support for Water Softeners (@npentell) +- Initial support for Water Softeners (@npentell, @drjeff) - Initial support for Opal Ice Makers (@mbcomer, @knobunc) +- Initial support for Microwaves (@mbcomer, @mnestor) {% endif %} {% if version_installed.split('.') | map('int') < '0.5.0'.split('.') | map('int') %} From 42fc0c4e5ab4ef90cc6bcaf0b806fc752afd90ff Mon Sep 17 00:00:00 2001 From: Jack Simbach Date: Sun, 12 Dec 2021 12:45:04 -0500 Subject: [PATCH 17/28] - updated filter status entity type for oim --- custom_components/ge_home/devices/oim.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/custom_components/ge_home/devices/oim.py b/custom_components/ge_home/devices/oim.py index c2703cc..ad1bd06 100644 --- a/custom_components/ge_home/devices/oim.py +++ b/custom_components/ge_home/devices/oim.py @@ -12,6 +12,7 @@ from .base import ApplianceApi from ..entities import ( OimLightLevelOptionsConverter, GeErdSensor, + GeErdBinarySensor, GeErdSelect, GeErdSwitch, ErdOnOffBoolConverter @@ -29,7 +30,7 @@ class OimApi(ApplianceApi): oim_entities = [ GeErdSensor(self, ErdCode.OIM_STATUS), - GeErdSensor(self, ErdCode.OIM_FILTER_STATUS), + GeErdBinarySensor(self, ErdCode.OIM_FILTER_STATUS, device_class_override="problem"), GeErdSelect(self, ErdCode.OIM_LIGHT_LEVEL, OimLightLevelOptionsConverter()), GeErdSwitch(self, ErdCode.OIM_POWER, bool_converter=ErdOnOffBoolConverter(), icon_on_override="mdi:power-on", icon_off_override="mdi:power-off"), ] From 9d27a3ea365966d2ecba46ea469de0bfcc44d699 Mon Sep 17 00:00:00 2001 From: Jack Simbach Date: Sun, 12 Dec 2021 13:25:43 -0500 Subject: [PATCH 18/28] - fixed issue with missing GeSacTemperatureSensor - added initial coffee maker support --- custom_components/ge_home/devices/__init__.py | 3 ++ .../ge_home/devices/coffee_maker.py | 40 +++++++++++++++++++ .../ge_home/entities/__init__.py | 3 +- .../ge_home/entities/ac/__init__.py | 3 +- .../ge_home/entities/ccm/__init__.py | 1 + .../ge_ccm_pot_not_present_binary_sensor.py | 8 ++++ 6 files changed, 55 insertions(+), 3 deletions(-) create mode 100644 custom_components/ge_home/devices/coffee_maker.py create mode 100644 custom_components/ge_home/entities/ccm/__init__.py create mode 100644 custom_components/ge_home/entities/ccm/ge_ccm_pot_not_present_binary_sensor.py diff --git a/custom_components/ge_home/devices/__init__.py b/custom_components/ge_home/devices/__init__.py index 9e17d3b..1acc228 100644 --- a/custom_components/ge_home/devices/__init__.py +++ b/custom_components/ge_home/devices/__init__.py @@ -19,6 +19,7 @@ from .hood import HoodApi from .microwave import MicrowaveApi from .water_softener import WaterSoftenerApi from .oim import OimApi +from .coffee_maker import CcmApi _LOGGER = logging.getLogger(__name__) @@ -56,6 +57,8 @@ def get_appliance_api_type(appliance_type: ErdApplianceType) -> Type: return MicrowaveApi if appliance_type == ErdApplianceType.OPAL_ICE_MAKER: return OimApi + if appliance_type == ErdApplianceType.CAFE_COFFEE_MAKER: + return CcmApi # Fallback return ApplianceApi diff --git a/custom_components/ge_home/devices/coffee_maker.py b/custom_components/ge_home/devices/coffee_maker.py new file mode 100644 index 0000000..9fe9a62 --- /dev/null +++ b/custom_components/ge_home/devices/coffee_maker.py @@ -0,0 +1,40 @@ +import logging +from typing import List + +from homeassistant.helpers.entity import Entity +from gehomesdk import ( + ErdCode, + ErdApplianceType +) + +from .base import ApplianceApi +from ..entities import ( + GeCcmPotNotPresentBinarySensor, + GeErdSensor, + GeErdBinarySensor +) + +_LOGGER = logging.getLogger(__name__) + + +class CcmApi(ApplianceApi): + """API class for Cafe Coffee Maker objects""" + APPLIANCE_TYPE = ErdApplianceType.CAFE_COFFEE_MAKER + + def get_all_entities(self) -> List[Entity]: + base_entities = super().get_all_entities() + + ccm_entities = [ + GeErdBinarySensor(self, ErdCode.CCM_IS_BREWING, device_class_override="heat"), + GeErdBinarySensor(self, ErdCode.CCM_IS_DESCALING), + GeErdSensor(self, ErdCode.CCM_BREW_STRENGTH), + GeErdSensor(self, ErdCode.CCM_BREW_CUPS), + GeErdSensor(self, ErdCode.CCM_BREW_TEMPERATURE), + GeErdSensor(self, ErdCode.CCM_CURRENT_WATER_TEMPERATURE), + GeErdBinarySensor(self, ErdCode.CCM_OUT_OF_WATER, device_class_override="problem"), + GeCcmPotNotPresentBinarySensor(self, ErdCode.CCM_POT_PRESENT, device_class_override="problem"), + ] + + entities = base_entities + ccm_entities + return entities + diff --git a/custom_components/ge_home/entities/__init__.py b/custom_components/ge_home/entities/__init__.py index b1da8ef..eabcc59 100644 --- a/custom_components/ge_home/entities/__init__.py +++ b/custom_components/ge_home/entities/__init__.py @@ -7,4 +7,5 @@ from .advantium import * from .ac import * from .hood import * from .water_softener import * -from .opal_ice_maker import * \ No newline at end of file +from .opal_ice_maker import * +from .ccm import * \ No newline at end of file diff --git a/custom_components/ge_home/entities/ac/__init__.py b/custom_components/ge_home/entities/ac/__init__.py index 0b54100..0f2e6ad 100644 --- a/custom_components/ge_home/entities/ac/__init__.py +++ b/custom_components/ge_home/entities/ac/__init__.py @@ -1,4 +1,3 @@ from .ge_wac_climate import GeWacClimate from .ge_sac_climate import GeSacClimate -from .ge_pac_climate import GePacClimate -from .ge_sac_temperature_sensor import GeSacTemperatureSensor \ No newline at end of file +from .ge_pac_climate import GePacClimate \ No newline at end of file diff --git a/custom_components/ge_home/entities/ccm/__init__.py b/custom_components/ge_home/entities/ccm/__init__.py new file mode 100644 index 0000000..1bb8e10 --- /dev/null +++ b/custom_components/ge_home/entities/ccm/__init__.py @@ -0,0 +1 @@ +from .ge_ccm_pot_not_present_binary_sensor import GeCcmPotNotPresentBinarySensor \ No newline at end of file diff --git a/custom_components/ge_home/entities/ccm/ge_ccm_pot_not_present_binary_sensor.py b/custom_components/ge_home/entities/ccm/ge_ccm_pot_not_present_binary_sensor.py new file mode 100644 index 0000000..124914a --- /dev/null +++ b/custom_components/ge_home/entities/ccm/ge_ccm_pot_not_present_binary_sensor.py @@ -0,0 +1,8 @@ +from ..common import GeErdBinarySensor + +class GeCcmPotNotPresentBinarySensor(GeErdBinarySensor): + @property + def is_on(self) -> bool: + """Return True if entity is not pot present.""" + return not self._boolify(self.appliance.get_erd_value(self.erd_code)) + From 53dcf9b41940d090bf61f20439f3dae50e467d6a Mon Sep 17 00:00:00 2001 From: Jack Simbach Date: Sun, 12 Dec 2021 13:27:10 -0500 Subject: [PATCH 19/28] - changed the water softener low salt to a binary sensor --- custom_components/ge_home/devices/water_softener.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/custom_components/ge_home/devices/water_softener.py b/custom_components/ge_home/devices/water_softener.py index 5e5feed..a730471 100644 --- a/custom_components/ge_home/devices/water_softener.py +++ b/custom_components/ge_home/devices/water_softener.py @@ -29,7 +29,7 @@ class WaterSoftenerApi(ApplianceApi): GeErdBinarySensor(self, ErdCode.WH_FILTER_FLOW_ALERT, device_class_override="moisture"), GeErdSensor(self, ErdCode.WH_FILTER_DAY_USAGE), GeErdSensor(self, ErdCode.WH_SOFTENER_ERROR_CODE, icon_override="mdi:alert-circle"), - GeErdSensor(self, ErdCode.WH_SOFTENER_LOW_SALT, icon_override="mdi:grain"), + GeErdBinarySensor(self, ErdCode.WH_SOFTENER_LOW_SALT, icon_on_override="mdi:alert", icon_off_override="mdi:grain"), GeErdSensor(self, ErdCode.WH_SOFTENER_SHUTOFF_VALVE_STATE, icon_override="mdi:state-machine"), GeErdSensor(self, ErdCode.WH_SOFTENER_SALT_LIFE_REMAINING, icon_override="mdi:calendar-clock"), GeErdShutoffPositionSelect(self, ErdCode.WH_SOFTENER_SHUTOFF_VALVE_CONTROL), From 01e56ea6fec32b4bddec1f900affe18d04aed061 Mon Sep 17 00:00:00 2001 From: Jack Simbach Date: Sun, 12 Dec 2021 18:38:17 -0500 Subject: [PATCH 20/28] - added coffee maker control --- .../ge_home/devices/coffee_maker.py | 4 +- .../ge_home/entities/ccm/__init__.py | 3 +- .../ge_home/entities/ccm/const.py | 8 + .../ge_home/entities/ccm/ge_ccm.py | 149 ++++++++++++++++++ .../ge_home/entities/ccm/ge_ccm_options.py | 58 +++++++ 5 files changed, 220 insertions(+), 2 deletions(-) create mode 100644 custom_components/ge_home/entities/ccm/const.py create mode 100644 custom_components/ge_home/entities/ccm/ge_ccm.py create mode 100644 custom_components/ge_home/entities/ccm/ge_ccm_options.py diff --git a/custom_components/ge_home/devices/coffee_maker.py b/custom_components/ge_home/devices/coffee_maker.py index 9fe9a62..8d248da 100644 --- a/custom_components/ge_home/devices/coffee_maker.py +++ b/custom_components/ge_home/devices/coffee_maker.py @@ -11,7 +11,8 @@ from .base import ApplianceApi from ..entities import ( GeCcmPotNotPresentBinarySensor, GeErdSensor, - GeErdBinarySensor + GeErdBinarySensor, + GeCcm ) _LOGGER = logging.getLogger(__name__) @@ -33,6 +34,7 @@ class CcmApi(ApplianceApi): GeErdSensor(self, ErdCode.CCM_CURRENT_WATER_TEMPERATURE), GeErdBinarySensor(self, ErdCode.CCM_OUT_OF_WATER, device_class_override="problem"), GeCcmPotNotPresentBinarySensor(self, ErdCode.CCM_POT_PRESENT, device_class_override="problem"), + GeCcm(self) ] entities = base_entities + ccm_entities diff --git a/custom_components/ge_home/entities/ccm/__init__.py b/custom_components/ge_home/entities/ccm/__init__.py index 1bb8e10..dc2da57 100644 --- a/custom_components/ge_home/entities/ccm/__init__.py +++ b/custom_components/ge_home/entities/ccm/__init__.py @@ -1 +1,2 @@ -from .ge_ccm_pot_not_present_binary_sensor import GeCcmPotNotPresentBinarySensor \ No newline at end of file +from .ge_ccm_pot_not_present_binary_sensor import GeCcmPotNotPresentBinarySensor +from .ge_ccm import GeCcm \ No newline at end of file diff --git a/custom_components/ge_home/entities/ccm/const.py b/custom_components/ge_home/entities/ccm/const.py new file mode 100644 index 0000000..64ed669 --- /dev/null +++ b/custom_components/ge_home/entities/ccm/const.py @@ -0,0 +1,8 @@ +from homeassistant.components.water_heater import ( + SUPPORT_OPERATION_MODE, + SUPPORT_TARGET_TEMPERATURE +) + +GE_CCM = SUPPORT_OPERATION_MODE | SUPPORT_TARGET_TEMPERATURE +MAX_CUPS = 10 +MIN_CUPS = 2 \ No newline at end of file diff --git a/custom_components/ge_home/entities/ccm/ge_ccm.py b/custom_components/ge_home/entities/ccm/ge_ccm.py new file mode 100644 index 0000000..aaa5d9f --- /dev/null +++ b/custom_components/ge_home/entities/ccm/ge_ccm.py @@ -0,0 +1,149 @@ +"""GE Home Sensor Entities - Advantium""" +import logging +from typing import Any, Dict, List, Mapping, Optional, Set +from random import randrange + +from gehomesdk import ( + ErdCode, + ErdUnitType, + ErdCcmBrewSettings, + ErdCcmBrewStrength +) + +from homeassistant.const import ATTR_TEMPERATURE + +from ...const import DOMAIN +from ...devices import ApplianceApi +from ..common import GeWaterHeater +from .const import * +from .ge_ccm_options import CcmBrewOptionsConverter + +_LOGGER = logging.getLogger(__name__) + +class GeCcm(GeWaterHeater): + """GE Appliance Cafe Coffee Maker""" + + icon = "mdi:coffee-maker" + + def __init__(self, api: ApplianceApi): + super().__init__(api) + self._options = CcmBrewOptionsConverter() + + @property + def supported_features(self): + return GE_CCM + + @property + def unique_id(self) -> str: + return f"{DOMAIN}_{self.serial_number}" + + @property + def name(self) -> Optional[str]: + return f"{self.serial_number} Coffee Maker" + + @property + def unit_type(self) -> Optional[ErdUnitType]: + try: + return self.appliance.get_erd_value(ErdCode.UNIT_TYPE) + except: + return None + + @property + def current_temperature(self) -> Optional[int]: + return self.appliance.get_erd_value(ErdCode.CCM_CURRENT_WATER_TEMPERATURE) + + @property + def is_brewing(self) -> bool: + return self.appliance.get_erd_value(ErdCode.CCM_IS_BREWING) + + @property + def is_descaling(self) -> bool: + return self.appliance.get_erd_value(ErdCode.CCM_IS_DESCALING) + + @property + def current_operation(self) -> Optional[str]: + try: + settings: ErdCcmBrewSettings = self.appliance.get_erd_value(ErdCode.CCM_BREW_SETTINGS) + if self.is_descaling: + return "Descale" + if not self.is_brewing: + return "Off" + return self._options.to_option_string(settings) + except: + return None + + @property + def operation_list(self) -> List[str]: + return self._options.options + + @property + def current_brew_setting(self) -> ErdCcmBrewSettings: + """Get the current brew setting.""" + return self.appliance.get_erd_value(ErdCode.CCM_BREW_SETTINGS) + + @property + def target_temperature(self) -> Optional[int]: + """Return the temperature we try to reach.""" + try: + return self.appliance.get_erd_value(ErdCode.CCM_BREW_TEMPERATURE) + except: + pass + return None + + @property + def min_temp(self) -> int: + """Return the minimum temperature.""" + min_temp, _, _ = self.appliance.get_erd_value(ErdCode.CCM_BREW_TEMPERATURE_RANGE) + return min_temp + + @property + def max_temp(self) -> int: + """Return the maximum temperature.""" + _, max_temp, _ = self.appliance.get_erd_value(ErdCode.CCM_BREW_TEMPERATURE_RANGE) + return max_temp + + @property + def extra_state_attributes(self) -> Optional[Mapping[str, Any]]: + data = {} + + data["unit_type"] = self._stringify(self.unit_type) + + return data + + @property + def can_set_temperature(self) -> bool: + """Indicates whether we can set the temperature based on the current mode""" + return not self.is_descaling + + async def async_set_operation_mode(self, operation_mode: str): + """Set the operation mode.""" + + #try to get the mode/setting for the selection + try: + if operation_mode not in ["Off","Descale"]: + new_mode = self._options.from_option_string(operation_mode) + new_mode.brew_temperature = self.target_temperature + + await self.appliance.async_set_erd_value(ErdCode.CCM_BREW_SETTINGS, new_mode) + elif operation_mode == "Off": + await self.appliance.async_set_erd_value(ErdCode.CCM_CANCEL_BREWING, True) + await self.appliance.async_set_erd_value(ErdCode.CCM_CANCEL_DESCALING, True) + elif operation_mode == "Descale": + await self.appliance.async_set_erd_value(ErdCode.CCM_START_DESCALING, True) + except: + _LOGGER.debug(f"Error Attempting to set mode to {operation_mode}.") + + async def async_set_temperature(self, **kwargs): + """Set the brew temperature""" + + target_temp = kwargs.get(ATTR_TEMPERATURE) + if target_temp is None: + return + + # get the current strength/cups + strength: ErdCcmBrewStrength = self.appliance.get_erd_value(ErdCode.CCM_BREW_STRENGTH) + cups: int = self.appliance.get_erd_value(ErdCode.CCM_BREW_CUPS) + new_mode = ErdCcmBrewSettings(cups, strength, target_temp) + + await self.appliance.async_set_erd_value(ErdCode.CCM_BREW_SETTINGS, new_mode) + diff --git a/custom_components/ge_home/entities/ccm/ge_ccm_options.py b/custom_components/ge_home/entities/ccm/ge_ccm_options.py new file mode 100644 index 0000000..54c2f17 --- /dev/null +++ b/custom_components/ge_home/entities/ccm/ge_ccm_options.py @@ -0,0 +1,58 @@ +import logging +from typing import List, Any, NamedTuple, Optional + +from gehomesdk import ErdCcmBrewStrength, ErdCcmBrewSettings +from homeassistant.const import TEMP_FAHRENHEIT +from homeassistant.util.unit_system import UnitSystem + +from custom_components.ge_home.entities.ccm.const import MAX_CUPS, MIN_CUPS +from ..common import OptionsConverter + +_LOGGER = logging.getLogger(__name__) + +class CcmBrewOption(NamedTuple): + strength: ErdCcmBrewStrength + cups: int + + def stringify(self): + return f"{self.strength.stringify()} -- {self.cups} cups" + +class CcmBrewOptionsConverter(OptionsConverter): + def __init__(self, units: UnitSystem): + super().__init__() + self._units = units + self._options = self._build_options() + + @property + def options(self) -> List[str]: + return ( + ["Off"] + .extend([i.stringify() for i in self._options]) + .extend("Descale") + ) + + def from_option_string(self, value: str) -> Optional[ErdCcmBrewSettings]: + try: + if value in ["Off","Descale"]: + return None + s = value.split(" -- ")[0] + c = value.split(" -- ")[1].replace(" cups","") + return ErdCcmBrewSettings(int(c), ErdCcmBrewStrength(s), 200) + except: + #return a default if we can't interpret it + return ErdCcmBrewSettings(4, ErdCcmBrewStrength.MEDIUM, 200) + + def to_option_string(self, value: ErdCcmBrewSettings) -> Optional[str]: + try: + o = CcmBrewOption(value.brew_strength, value.number_of_cups) + return o.stringify() + except: + #return a default if we can't interpret it + return CcmBrewOption(ErdCcmBrewStrength.MEDIUM, 4) + + def _build_options(self) -> List[CcmBrewOption]: + options = [] + for s in ErdCcmBrewStrength: + for c in range(MIN_CUPS, MAX_CUPS, 2): + options.append(CcmBrewOption(s, c)) + return options From 807ee17110f2ba7bbd2871b166e5e356c729a468 Mon Sep 17 00:00:00 2001 From: alexanv1 <44785744+alexanv1@users.noreply.github.com> Date: Mon, 13 Dec 2021 02:43:00 -0800 Subject: [PATCH 21/28] CoffeeMaker fixes --- .../ge_home/devices/coffee_maker.py | 4 +++- .../ge_home/entities/ccm/ge_ccm.py | 9 +++++---- .../ge_home/entities/ccm/ge_ccm_options.py | 19 +++++++++++-------- 3 files changed, 19 insertions(+), 13 deletions(-) diff --git a/custom_components/ge_home/devices/coffee_maker.py b/custom_components/ge_home/devices/coffee_maker.py index 8d248da..a018a47 100644 --- a/custom_components/ge_home/devices/coffee_maker.py +++ b/custom_components/ge_home/devices/coffee_maker.py @@ -25,6 +25,8 @@ class CcmApi(ApplianceApi): def get_all_entities(self) -> List[Entity]: base_entities = super().get_all_entities() + units = self.hass.config.units + ccm_entities = [ GeErdBinarySensor(self, ErdCode.CCM_IS_BREWING, device_class_override="heat"), GeErdBinarySensor(self, ErdCode.CCM_IS_DESCALING), @@ -34,7 +36,7 @@ class CcmApi(ApplianceApi): GeErdSensor(self, ErdCode.CCM_CURRENT_WATER_TEMPERATURE), GeErdBinarySensor(self, ErdCode.CCM_OUT_OF_WATER, device_class_override="problem"), GeCcmPotNotPresentBinarySensor(self, ErdCode.CCM_POT_PRESENT, device_class_override="problem"), - GeCcm(self) + GeCcm(self, units) ] entities = base_entities + ccm_entities diff --git a/custom_components/ge_home/entities/ccm/ge_ccm.py b/custom_components/ge_home/entities/ccm/ge_ccm.py index aaa5d9f..9783e2a 100644 --- a/custom_components/ge_home/entities/ccm/ge_ccm.py +++ b/custom_components/ge_home/entities/ccm/ge_ccm.py @@ -11,6 +11,7 @@ from gehomesdk import ( ) from homeassistant.const import ATTR_TEMPERATURE +from homeassistant.util.unit_system import UnitSystem from ...const import DOMAIN from ...devices import ApplianceApi @@ -25,9 +26,9 @@ class GeCcm(GeWaterHeater): icon = "mdi:coffee-maker" - def __init__(self, api: ApplianceApi): + def __init__(self, api: ApplianceApi, units: UnitSystem): super().__init__(api) - self._options = CcmBrewOptionsConverter() + self._options = CcmBrewOptionsConverter(units) @property def supported_features(self): @@ -122,7 +123,7 @@ class GeCcm(GeWaterHeater): try: if operation_mode not in ["Off","Descale"]: new_mode = self._options.from_option_string(operation_mode) - new_mode.brew_temperature = self.target_temperature + new_mode = ErdCcmBrewSettings(new_mode.number_of_cups, new_mode.brew_strength, self.target_temperature) await self.appliance.async_set_erd_value(ErdCode.CCM_BREW_SETTINGS, new_mode) elif operation_mode == "Off": @@ -131,7 +132,7 @@ class GeCcm(GeWaterHeater): elif operation_mode == "Descale": await self.appliance.async_set_erd_value(ErdCode.CCM_START_DESCALING, True) except: - _LOGGER.debug(f"Error Attempting to set mode to {operation_mode}.") + _LOGGER.error(f"Error Attempting to set mode to {operation_mode}.", exc_info=True) async def async_set_temperature(self, **kwargs): """Set the brew temperature""" diff --git a/custom_components/ge_home/entities/ccm/ge_ccm_options.py b/custom_components/ge_home/entities/ccm/ge_ccm_options.py index 54c2f17..e3f17cf 100644 --- a/custom_components/ge_home/entities/ccm/ge_ccm_options.py +++ b/custom_components/ge_home/entities/ccm/ge_ccm_options.py @@ -25,20 +25,22 @@ class CcmBrewOptionsConverter(OptionsConverter): @property def options(self) -> List[str]: - return ( - ["Off"] - .extend([i.stringify() for i in self._options]) - .extend("Descale") - ) + options = ["Off"] + options.extend([i.stringify() for i in self._options]) + options.extend(["Descale"]) + + return options def from_option_string(self, value: str) -> Optional[ErdCcmBrewSettings]: try: if value in ["Off","Descale"]: return None - s = value.split(" -- ")[0] + s = value.split(" -- ")[0].upper() c = value.split(" -- ")[1].replace(" cups","") - return ErdCcmBrewSettings(int(c), ErdCcmBrewStrength(s), 200) + return ErdCcmBrewSettings(int(c), ErdCcmBrewStrength[s], 200) except: + _LOGGER.error(f"Could not convert brew options '{value}'", exc_info=True) + #return a default if we can't interpret it return ErdCcmBrewSettings(4, ErdCcmBrewStrength.MEDIUM, 200) @@ -52,7 +54,8 @@ class CcmBrewOptionsConverter(OptionsConverter): def _build_options(self) -> List[CcmBrewOption]: options = [] - for s in ErdCcmBrewStrength: + for s in filter(lambda x: x != ErdCcmBrewStrength.UNKNOWN, ErdCcmBrewStrength): for c in range(MIN_CUPS, MAX_CUPS, 2): options.append(CcmBrewOption(s, c)) + return options From 244778edc00166eb8b240a94acfaa574fba122b5 Mon Sep 17 00:00:00 2001 From: alexanv1 <44785744+alexanv1@users.noreply.github.com> Date: Sat, 29 Jan 2022 21:06:43 -0800 Subject: [PATCH 22/28] Updates to Coffee Maker support --- custom_components/ge_home/button.py | 33 ++++ custom_components/ge_home/const.py | 8 +- custom_components/ge_home/devices/base.py | 4 +- .../ge_home/devices/coffee_maker.py | 48 ++++-- .../ge_home/entities/ccm/__init__.py | 5 +- .../ge_home/entities/ccm/const.py | 8 - .../ge_home/entities/ccm/ge_ccm.py | 150 ------------------ .../ge_home/entities/ccm/ge_ccm_brew_cups.py | 19 +++ .../entities/ccm/ge_ccm_brew_settings.py | 13 ++ .../entities/ccm/ge_ccm_brew_strength.py | 47 ++++++ .../entities/ccm/ge_ccm_brew_temperature.py | 29 ++++ .../entities/ccm/ge_ccm_cached_value.py | 20 +++ .../ge_home/entities/ccm/ge_ccm_options.py | 61 ------- .../ge_home/entities/common/__init__.py | 2 + .../ge_home/entities/common/ge_erd_button.py | 17 ++ .../ge_home/entities/common/ge_erd_entity.py | 6 +- .../ge_home/entities/common/ge_erd_number.py | 139 ++++++++++++++++ .../ge_home/entities/common/ge_erd_sensor.py | 36 +++-- custom_components/ge_home/manifest.json | 2 +- custom_components/ge_home/number.py | 33 ++++ .../ge_home/update_coordinator.py | 3 +- 21 files changed, 426 insertions(+), 257 deletions(-) create mode 100644 custom_components/ge_home/button.py delete mode 100644 custom_components/ge_home/entities/ccm/const.py delete mode 100644 custom_components/ge_home/entities/ccm/ge_ccm.py create mode 100644 custom_components/ge_home/entities/ccm/ge_ccm_brew_cups.py create mode 100644 custom_components/ge_home/entities/ccm/ge_ccm_brew_settings.py create mode 100644 custom_components/ge_home/entities/ccm/ge_ccm_brew_strength.py create mode 100644 custom_components/ge_home/entities/ccm/ge_ccm_brew_temperature.py create mode 100644 custom_components/ge_home/entities/ccm/ge_ccm_cached_value.py delete mode 100644 custom_components/ge_home/entities/ccm/ge_ccm_options.py create mode 100644 custom_components/ge_home/entities/common/ge_erd_button.py create mode 100644 custom_components/ge_home/entities/common/ge_erd_number.py create mode 100644 custom_components/ge_home/number.py diff --git a/custom_components/ge_home/button.py b/custom_components/ge_home/button.py new file mode 100644 index 0000000..8e594bd --- /dev/null +++ b/custom_components/ge_home/button.py @@ -0,0 +1,33 @@ +"""GE Home Button Entities""" +import async_timeout +import logging +from typing import Callable + +from homeassistant.config_entries import ConfigEntry +from homeassistant.core import HomeAssistant + +from .const import DOMAIN +from .entities import GeErdButton +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 buttons.""" + + coordinator: GeHomeUpdateCoordinator = hass.data[DOMAIN][config_entry.entry_id] + + # This should be a NOP, but let's be safe + with async_timeout.timeout(20): + await coordinator.initialization_future + + apis = coordinator.appliance_apis.values() + _LOGGER.debug(f'Found {len(apis):d} appliance APIs') + entities = [ + entity + for api in apis + for entity in api.entities + if isinstance(entity, GeErdButton) + ] + _LOGGER.debug(f'Found {len(entities):d} buttons ') + async_add_entities(entities) diff --git a/custom_components/ge_home/const.py b/custom_components/ge_home/const.py index e8511f5..ac908dc 100644 --- a/custom_components/ge_home/const.py +++ b/custom_components/ge_home/const.py @@ -1,5 +1,4 @@ """Constants for the gehome integration.""" -from gehomesdk.clients.const import LOGIN_URL DOMAIN = "ge_home" @@ -13,4 +12,9 @@ RETRY_OFFLINE_COUNT = 5 SERVICE_SET_TIMER = "set_timer" SERVICE_CLEAR_TIMER = "clear_timer" -SERVICE_SET_INT_VALUE = "set_int_value" \ No newline at end of file +SERVICE_SET_INT_VALUE = "set_int_value" + +# Prevent Home Assistant automatic temperature conversions by overriding TEMP_CELCIUS, TEMP_FAHRENHEIT +# This makes sure that the values shows in the UI match device preferences bypassing the automatic conversion to whatever the Home Assistant default is set to +TEMP_CELSIUS = "\u2103" +TEMP_FAHRENHEIT = "\u2109" diff --git a/custom_components/ge_home/devices/base.py b/custom_components/ge_home/devices/base.py index bbc7a0d..54bbe35 100644 --- a/custom_components/ge_home/devices/base.py +++ b/custom_components/ge_home/devices/base.py @@ -121,10 +121,10 @@ class ApplianceApi: def build_entities_list(self) -> None: """Build the entities list, adding anything new.""" - from ..entities import GeErdEntity + from ..entities import GeErdEntity, GeErdButton entities = [ e for e in self.get_all_entities() - if not isinstance(e, GeErdEntity) or e.erd_code in self.appliance.known_properties + if not isinstance(e, GeErdEntity) or isinstance(e, GeErdButton) or e.erd_code in self.appliance.known_properties ] for entity in entities: diff --git a/custom_components/ge_home/devices/coffee_maker.py b/custom_components/ge_home/devices/coffee_maker.py index a018a47..d95af55 100644 --- a/custom_components/ge_home/devices/coffee_maker.py +++ b/custom_components/ge_home/devices/coffee_maker.py @@ -3,16 +3,23 @@ from typing import List from homeassistant.helpers.entity import Entity from gehomesdk import ( + GeAppliance, ErdCode, - ErdApplianceType + ErdApplianceType, + ErdCcmBrewSettings ) +from homeassistant.helpers.update_coordinator import DataUpdateCoordinator from .base import ApplianceApi from ..entities import ( GeCcmPotNotPresentBinarySensor, GeErdSensor, GeErdBinarySensor, - GeCcm + GeErdButton, + GeCcmBrewStrengthSelect, + GeCcmBrewTemperatureNumber, + GeCcmBrewCupsNumber, + GeCcmBrewSettingsButton ) _LOGGER = logging.getLogger(__name__) @@ -22,23 +29,38 @@ class CcmApi(ApplianceApi): """API class for Cafe Coffee Maker objects""" APPLIANCE_TYPE = ErdApplianceType.CAFE_COFFEE_MAKER + def __init__(self, coordinator: DataUpdateCoordinator, appliance: GeAppliance): + super().__init__(coordinator, appliance) + + self._brew_strengh_entity = GeCcmBrewStrengthSelect(self) + self._brew_temperature_entity = GeCcmBrewTemperatureNumber(self) + self._brew_cups_entity = GeCcmBrewCupsNumber(self) + def get_all_entities(self) -> List[Entity]: base_entities = super().get_all_entities() - units = self.hass.config.units - ccm_entities = [ - GeErdBinarySensor(self, ErdCode.CCM_IS_BREWING, device_class_override="heat"), - GeErdBinarySensor(self, ErdCode.CCM_IS_DESCALING), - GeErdSensor(self, ErdCode.CCM_BREW_STRENGTH), - GeErdSensor(self, ErdCode.CCM_BREW_CUPS), - GeErdSensor(self, ErdCode.CCM_BREW_TEMPERATURE), + GeErdBinarySensor(self, ErdCode.CCM_IS_BREWING), + GeErdBinarySensor(self, ErdCode.CCM_IS_DESCALING), + GeCcmBrewSettingsButton(self), + GeErdButton(self, ErdCode.CCM_CANCEL_DESCALING), + GeErdButton(self, ErdCode.CCM_START_DESCALING), + GeErdButton(self, ErdCode.CCM_CANCEL_BREWING), + self._brew_strengh_entity, + self._brew_temperature_entity, + self._brew_cups_entity, GeErdSensor(self, ErdCode.CCM_CURRENT_WATER_TEMPERATURE), - GeErdBinarySensor(self, ErdCode.CCM_OUT_OF_WATER, device_class_override="problem"), - GeCcmPotNotPresentBinarySensor(self, ErdCode.CCM_POT_PRESENT, device_class_override="problem"), - GeCcm(self, units) + GeErdBinarySensor(self, ErdCode.CCM_OUT_OF_WATER, device_class_override="problem"), + GeCcmPotNotPresentBinarySensor(self, ErdCode.CCM_POT_PRESENT, device_class_override="problem") ] entities = base_entities + ccm_entities return entities - + + async def start_brewing(self) -> None: + """Aggregate brew settings and start brewing.""" + + new_mode = ErdCcmBrewSettings(self._brew_cups_entity.value, + self._brew_strengh_entity.brew_strength, + self._brew_temperature_entity.brew_temperature) + await self.appliance.async_set_erd_value(ErdCode.CCM_BREW_SETTINGS, new_mode) \ No newline at end of file diff --git a/custom_components/ge_home/entities/ccm/__init__.py b/custom_components/ge_home/entities/ccm/__init__.py index dc2da57..614d130 100644 --- a/custom_components/ge_home/entities/ccm/__init__.py +++ b/custom_components/ge_home/entities/ccm/__init__.py @@ -1,2 +1,5 @@ from .ge_ccm_pot_not_present_binary_sensor import GeCcmPotNotPresentBinarySensor -from .ge_ccm import GeCcm \ No newline at end of file +from .ge_ccm_brew_strength import GeCcmBrewStrengthSelect +from .ge_ccm_brew_temperature import GeCcmBrewTemperatureNumber +from .ge_ccm_brew_cups import GeCcmBrewCupsNumber +from .ge_ccm_brew_settings import GeCcmBrewSettingsButton \ No newline at end of file diff --git a/custom_components/ge_home/entities/ccm/const.py b/custom_components/ge_home/entities/ccm/const.py deleted file mode 100644 index 64ed669..0000000 --- a/custom_components/ge_home/entities/ccm/const.py +++ /dev/null @@ -1,8 +0,0 @@ -from homeassistant.components.water_heater import ( - SUPPORT_OPERATION_MODE, - SUPPORT_TARGET_TEMPERATURE -) - -GE_CCM = SUPPORT_OPERATION_MODE | SUPPORT_TARGET_TEMPERATURE -MAX_CUPS = 10 -MIN_CUPS = 2 \ No newline at end of file diff --git a/custom_components/ge_home/entities/ccm/ge_ccm.py b/custom_components/ge_home/entities/ccm/ge_ccm.py deleted file mode 100644 index 9783e2a..0000000 --- a/custom_components/ge_home/entities/ccm/ge_ccm.py +++ /dev/null @@ -1,150 +0,0 @@ -"""GE Home Sensor Entities - Advantium""" -import logging -from typing import Any, Dict, List, Mapping, Optional, Set -from random import randrange - -from gehomesdk import ( - ErdCode, - ErdUnitType, - ErdCcmBrewSettings, - ErdCcmBrewStrength -) - -from homeassistant.const import ATTR_TEMPERATURE -from homeassistant.util.unit_system import UnitSystem - -from ...const import DOMAIN -from ...devices import ApplianceApi -from ..common import GeWaterHeater -from .const import * -from .ge_ccm_options import CcmBrewOptionsConverter - -_LOGGER = logging.getLogger(__name__) - -class GeCcm(GeWaterHeater): - """GE Appliance Cafe Coffee Maker""" - - icon = "mdi:coffee-maker" - - def __init__(self, api: ApplianceApi, units: UnitSystem): - super().__init__(api) - self._options = CcmBrewOptionsConverter(units) - - @property - def supported_features(self): - return GE_CCM - - @property - def unique_id(self) -> str: - return f"{DOMAIN}_{self.serial_number}" - - @property - def name(self) -> Optional[str]: - return f"{self.serial_number} Coffee Maker" - - @property - def unit_type(self) -> Optional[ErdUnitType]: - try: - return self.appliance.get_erd_value(ErdCode.UNIT_TYPE) - except: - return None - - @property - def current_temperature(self) -> Optional[int]: - return self.appliance.get_erd_value(ErdCode.CCM_CURRENT_WATER_TEMPERATURE) - - @property - def is_brewing(self) -> bool: - return self.appliance.get_erd_value(ErdCode.CCM_IS_BREWING) - - @property - def is_descaling(self) -> bool: - return self.appliance.get_erd_value(ErdCode.CCM_IS_DESCALING) - - @property - def current_operation(self) -> Optional[str]: - try: - settings: ErdCcmBrewSettings = self.appliance.get_erd_value(ErdCode.CCM_BREW_SETTINGS) - if self.is_descaling: - return "Descale" - if not self.is_brewing: - return "Off" - return self._options.to_option_string(settings) - except: - return None - - @property - def operation_list(self) -> List[str]: - return self._options.options - - @property - def current_brew_setting(self) -> ErdCcmBrewSettings: - """Get the current brew setting.""" - return self.appliance.get_erd_value(ErdCode.CCM_BREW_SETTINGS) - - @property - def target_temperature(self) -> Optional[int]: - """Return the temperature we try to reach.""" - try: - return self.appliance.get_erd_value(ErdCode.CCM_BREW_TEMPERATURE) - except: - pass - return None - - @property - def min_temp(self) -> int: - """Return the minimum temperature.""" - min_temp, _, _ = self.appliance.get_erd_value(ErdCode.CCM_BREW_TEMPERATURE_RANGE) - return min_temp - - @property - def max_temp(self) -> int: - """Return the maximum temperature.""" - _, max_temp, _ = self.appliance.get_erd_value(ErdCode.CCM_BREW_TEMPERATURE_RANGE) - return max_temp - - @property - def extra_state_attributes(self) -> Optional[Mapping[str, Any]]: - data = {} - - data["unit_type"] = self._stringify(self.unit_type) - - return data - - @property - def can_set_temperature(self) -> bool: - """Indicates whether we can set the temperature based on the current mode""" - return not self.is_descaling - - async def async_set_operation_mode(self, operation_mode: str): - """Set the operation mode.""" - - #try to get the mode/setting for the selection - try: - if operation_mode not in ["Off","Descale"]: - new_mode = self._options.from_option_string(operation_mode) - new_mode = ErdCcmBrewSettings(new_mode.number_of_cups, new_mode.brew_strength, self.target_temperature) - - await self.appliance.async_set_erd_value(ErdCode.CCM_BREW_SETTINGS, new_mode) - elif operation_mode == "Off": - await self.appliance.async_set_erd_value(ErdCode.CCM_CANCEL_BREWING, True) - await self.appliance.async_set_erd_value(ErdCode.CCM_CANCEL_DESCALING, True) - elif operation_mode == "Descale": - await self.appliance.async_set_erd_value(ErdCode.CCM_START_DESCALING, True) - except: - _LOGGER.error(f"Error Attempting to set mode to {operation_mode}.", exc_info=True) - - async def async_set_temperature(self, **kwargs): - """Set the brew temperature""" - - target_temp = kwargs.get(ATTR_TEMPERATURE) - if target_temp is None: - return - - # get the current strength/cups - strength: ErdCcmBrewStrength = self.appliance.get_erd_value(ErdCode.CCM_BREW_STRENGTH) - cups: int = self.appliance.get_erd_value(ErdCode.CCM_BREW_CUPS) - new_mode = ErdCcmBrewSettings(cups, strength, target_temp) - - await self.appliance.async_set_erd_value(ErdCode.CCM_BREW_SETTINGS, new_mode) - diff --git a/custom_components/ge_home/entities/ccm/ge_ccm_brew_cups.py b/custom_components/ge_home/entities/ccm/ge_ccm_brew_cups.py new file mode 100644 index 0000000..dc876b5 --- /dev/null +++ b/custom_components/ge_home/entities/ccm/ge_ccm_brew_cups.py @@ -0,0 +1,19 @@ +from gehomesdk import ErdCode +from ...devices import ApplianceApi +from ..common import GeErdNumber +from .ge_ccm_cached_value import GeCcmCachedValue + +class GeCcmBrewCupsNumber(GeErdNumber, GeCcmCachedValue): + def __init__(self, api: ApplianceApi): + GeErdNumber.__init__(self, api = api, erd_code = ErdCode.CCM_BREW_CUPS, min_value=1, max_value=10, mode="box") + GeCcmCachedValue.__init__(self) + + self._set_value = None + + async def async_set_value(self, value): + GeCcmCachedValue.set_value(self, value) + self.schedule_update_ha_state() + + @property + def value(self): + return self.get_value(device_value = super().value) \ No newline at end of file diff --git a/custom_components/ge_home/entities/ccm/ge_ccm_brew_settings.py b/custom_components/ge_home/entities/ccm/ge_ccm_brew_settings.py new file mode 100644 index 0000000..121392b --- /dev/null +++ b/custom_components/ge_home/entities/ccm/ge_ccm_brew_settings.py @@ -0,0 +1,13 @@ +from gehomesdk import ErdCode +from ...devices import ApplianceApi +from ..common import GeErdButton + +class GeCcmBrewSettingsButton(GeErdButton): + def __init__(self, api: ApplianceApi): + super().__init__(api, erd_code=ErdCode.CCM_BREW_SETTINGS) + + async def async_press(self) -> None: + """Handle the button press.""" + + # Forward the call up to the Coffee Maker device to handle + await self.api.start_brewing() \ No newline at end of file diff --git a/custom_components/ge_home/entities/ccm/ge_ccm_brew_strength.py b/custom_components/ge_home/entities/ccm/ge_ccm_brew_strength.py new file mode 100644 index 0000000..b6faa93 --- /dev/null +++ b/custom_components/ge_home/entities/ccm/ge_ccm_brew_strength.py @@ -0,0 +1,47 @@ +import logging +from typing import List, Any, Optional + +from gehomesdk import ErdCode, ErdCcmBrewStrength +from ...devices import ApplianceApi +from ..common import GeErdSelect, OptionsConverter +from .ge_ccm_cached_value import GeCcmCachedValue + +_LOGGER = logging.getLogger(__name__) + +class GeCcmBrewStrengthOptionsConverter(OptionsConverter): + def __init__(self): + self._default = ErdCcmBrewStrength.MEDIUM + + @property + def options(self) -> List[str]: + return [i.stringify() for i in [ErdCcmBrewStrength.LIGHT, ErdCcmBrewStrength.MEDIUM, ErdCcmBrewStrength.BOLD, ErdCcmBrewStrength.GOLD]] + + def from_option_string(self, value: str) -> Any: + try: + return ErdCcmBrewStrength[value.upper()] + except: + _LOGGER.warn(f"Could not set brew strength to {value.upper()}") + return self._default + + def to_option_string(self, value: ErdCcmBrewStrength) -> Optional[str]: + try: + return value.stringify() + except: + return self._default.stringify() + +class GeCcmBrewStrengthSelect(GeErdSelect, GeCcmCachedValue): + def __init__(self, api: ApplianceApi): + GeErdSelect.__init__(self, api = api, erd_code = ErdCode.CCM_BREW_STRENGTH, converter = GeCcmBrewStrengthOptionsConverter()) + GeCcmCachedValue.__init__(self) + + @property + def brew_strength(self) -> ErdCcmBrewStrength: + return self._converter.from_option_string(self.current_option) + + async def async_select_option(self, value): + GeCcmCachedValue.set_value(self, value) + self.schedule_update_ha_state() + + @property + def current_option(self): + return self.get_value(device_value = super().current_option) \ No newline at end of file diff --git a/custom_components/ge_home/entities/ccm/ge_ccm_brew_temperature.py b/custom_components/ge_home/entities/ccm/ge_ccm_brew_temperature.py new file mode 100644 index 0000000..bff5127 --- /dev/null +++ b/custom_components/ge_home/entities/ccm/ge_ccm_brew_temperature.py @@ -0,0 +1,29 @@ +from gehomesdk import ErdCode +from ...devices import ApplianceApi +from ..common import GeErdNumber +from ...const import TEMP_CELSIUS +from .ge_ccm_cached_value import GeCcmCachedValue + +class GeCcmBrewTemperatureNumber(GeErdNumber, GeCcmCachedValue): + def __init__(self, api: ApplianceApi): + min_temp, max_temp, _ = api.appliance.get_erd_value(ErdCode.CCM_BREW_TEMPERATURE_RANGE) + GeErdNumber.__init__(self, api = api, erd_code = ErdCode.CCM_BREW_TEMPERATURE, min_value=min_temp, max_value=max_temp, mode="slider") + GeCcmCachedValue.__init__(self) + + async def async_set_value(self, value): + GeCcmCachedValue.set_value(self, value) + self.schedule_update_ha_state() + + @property + def value(self): + return int(self.get_value(device_value = super().value)) + + @property + def brew_temperature(self) -> int: + + value = self.value + if self.unit_of_measurement == TEMP_CELSIUS: + # Convert to Fahrenheit + value = int(round(value * 9/5) + 32) + + return value \ No newline at end of file diff --git a/custom_components/ge_home/entities/ccm/ge_ccm_cached_value.py b/custom_components/ge_home/entities/ccm/ge_ccm_cached_value.py new file mode 100644 index 0000000..95c2b94 --- /dev/null +++ b/custom_components/ge_home/entities/ccm/ge_ccm_cached_value.py @@ -0,0 +1,20 @@ +class GeCcmCachedValue(): + def __init__(self): + self._set_value = None + self._last_device_value = None + + def get_value(self, device_value): + + # If the last device value is different from the current one, return the device value which overrides the set value + if self._last_device_value != device_value: + self._last_device_value = device_value + self._set_value = None + return device_value + + if self._set_value is not None: + return self._set_value + + return device_value + + def set_value(self, set_value): + self._set_value = set_value \ No newline at end of file diff --git a/custom_components/ge_home/entities/ccm/ge_ccm_options.py b/custom_components/ge_home/entities/ccm/ge_ccm_options.py deleted file mode 100644 index e3f17cf..0000000 --- a/custom_components/ge_home/entities/ccm/ge_ccm_options.py +++ /dev/null @@ -1,61 +0,0 @@ -import logging -from typing import List, Any, NamedTuple, Optional - -from gehomesdk import ErdCcmBrewStrength, ErdCcmBrewSettings -from homeassistant.const import TEMP_FAHRENHEIT -from homeassistant.util.unit_system import UnitSystem - -from custom_components.ge_home.entities.ccm.const import MAX_CUPS, MIN_CUPS -from ..common import OptionsConverter - -_LOGGER = logging.getLogger(__name__) - -class CcmBrewOption(NamedTuple): - strength: ErdCcmBrewStrength - cups: int - - def stringify(self): - return f"{self.strength.stringify()} -- {self.cups} cups" - -class CcmBrewOptionsConverter(OptionsConverter): - def __init__(self, units: UnitSystem): - super().__init__() - self._units = units - self._options = self._build_options() - - @property - def options(self) -> List[str]: - options = ["Off"] - options.extend([i.stringify() for i in self._options]) - options.extend(["Descale"]) - - return options - - def from_option_string(self, value: str) -> Optional[ErdCcmBrewSettings]: - try: - if value in ["Off","Descale"]: - return None - s = value.split(" -- ")[0].upper() - c = value.split(" -- ")[1].replace(" cups","") - return ErdCcmBrewSettings(int(c), ErdCcmBrewStrength[s], 200) - except: - _LOGGER.error(f"Could not convert brew options '{value}'", exc_info=True) - - #return a default if we can't interpret it - return ErdCcmBrewSettings(4, ErdCcmBrewStrength.MEDIUM, 200) - - def to_option_string(self, value: ErdCcmBrewSettings) -> Optional[str]: - try: - o = CcmBrewOption(value.brew_strength, value.number_of_cups) - return o.stringify() - except: - #return a default if we can't interpret it - return CcmBrewOption(ErdCcmBrewStrength.MEDIUM, 4) - - def _build_options(self) -> List[CcmBrewOption]: - options = [] - for s in filter(lambda x: x != ErdCcmBrewStrength.UNKNOWN, ErdCcmBrewStrength): - for c in range(MIN_CUPS, MAX_CUPS, 2): - options.append(CcmBrewOption(s, c)) - - return options diff --git a/custom_components/ge_home/entities/common/__init__.py b/custom_components/ge_home/entities/common/__init__.py index 7db556b..edd0b63 100644 --- a/custom_components/ge_home/entities/common/__init__.py +++ b/custom_components/ge_home/entities/common/__init__.py @@ -9,6 +9,8 @@ from .ge_erd_light import GeErdLight from .ge_erd_timer_sensor import GeErdTimerSensor from .ge_erd_property_sensor import GeErdPropertySensor from .ge_erd_switch import GeErdSwitch +from .ge_erd_button import GeErdButton +from .ge_erd_number import GeErdNumber from .ge_water_heater import GeWaterHeater from .ge_erd_select import GeErdSelect from .ge_climate import GeClimate \ No newline at end of file diff --git a/custom_components/ge_home/entities/common/ge_erd_button.py b/custom_components/ge_home/entities/common/ge_erd_button.py new file mode 100644 index 0000000..ef28295 --- /dev/null +++ b/custom_components/ge_home/entities/common/ge_erd_button.py @@ -0,0 +1,17 @@ +from typing import Optional + +from homeassistant.components.button import ButtonEntity + +from gehomesdk import ErdCodeType +from ...devices import ApplianceApi +from .ge_erd_entity import GeErdEntity + + +class GeErdButton(GeErdEntity, ButtonEntity): + def __init__(self, api: ApplianceApi, erd_code: ErdCodeType, erd_override: str = None): + super().__init__(api, erd_code, erd_override=erd_override) + + """GE Entity for buttons""" + async def async_press(self) -> None: + """Handle the button press.""" + await self.appliance.async_set_erd_value(self.erd_code, True) 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 32f7b66..36b7883 100644 --- a/custom_components/ge_home/entities/common/ge_erd_entity.py +++ b/custom_components/ge_home/entities/common/ge_erd_entity.py @@ -83,7 +83,7 @@ class GeErdEntity(GeEntity): try: value = self.appliance.get_erd_value(ErdCode.TEMPERATURE_UNIT) except KeyError: - return None + return ErdMeasurementUnits.Imperial return value def _get_icon(self): @@ -142,6 +142,8 @@ class GeErdEntity(GeEntity): if self.erd_code_class == ErdCodeClass.OIM_SENSOR: return "mdi:snowflake" if self.erd_code_class == ErdCodeClass.WATERSOFTENER_SENSOR: - return "mdi:water" + return "mdi:water" + if self.erd_code_class == ErdCodeClass.CCM_SENSOR: + return "mdi:coffee-maker" return None diff --git a/custom_components/ge_home/entities/common/ge_erd_number.py b/custom_components/ge_home/entities/common/ge_erd_number.py new file mode 100644 index 0000000..50fb63a --- /dev/null +++ b/custom_components/ge_home/entities/common/ge_erd_number.py @@ -0,0 +1,139 @@ +import logging +from typing import Optional +from gehomesdk.erd.erd_data_type import ErdDataType +from homeassistant.components.number import NumberEntity + +from homeassistant.const import ( + DEVICE_CLASS_TEMPERATURE, +) +from gehomesdk import ErdCodeType, ErdCodeClass, ErdMeasurementUnits +from .ge_erd_entity import GeErdEntity +from ...devices import ApplianceApi +from ...const import TEMP_CELSIUS, TEMP_FAHRENHEIT + +_LOGGER = logging.getLogger(__name__) + +class GeErdNumber(GeErdEntity, NumberEntity): + """GE Entity for numbers""" + + def __init__( + self, + api: ApplianceApi, + erd_code: ErdCodeType, + erd_override: str = None, + icon_override: str = None, + device_class_override: str = None, + uom_override: str = None, + data_type_override: ErdDataType = None, + min_value: float = 1, + max_value: float = 100, + step_value: float = 1, + mode: str = "auto" + ): + super().__init__(api, erd_code, erd_override, icon_override, device_class_override) + self._uom_override = uom_override + self._data_type_override = data_type_override + self._min_value = min_value + self._max_value = max_value + self._step_value = step_value + self._mode = mode + + @property + def value(self): + try: + value = self.appliance.get_erd_value(self.erd_code) + return self._convert_value_from_device(value) + except KeyError: + return None + + @property + def unit_of_measurement(self) -> Optional[str]: + return self._get_uom() + + @property + def _data_type(self) -> ErdDataType: + if self._data_type_override is not None: + return self._data_type_override + + return self.appliance.get_erd_code_data_type(self.erd_code) + + @property + def min_value(self) -> float: + return self._convert_value_from_device(self._min_value) + + @property + def max_value(self) -> float: + return self._convert_value_from_device(self._max_value) + + @property + def step(self) -> float: + return self._step_value + + @property + def mode(self) -> float: + return self._mode + + def _convert_value_from_device(self, value): + """Convert to expected temperature units and data type""" + + if (self._get_uom() == TEMP_CELSIUS): + # Convert to Celcius + value = (value - 32 ) * 5/9 + + if self._data_type == ErdDataType.INT: + return int(round(value)) + else: + return value + + def _get_uom(self): + """Select appropriate units""" + + #if we have an override, just use it + if self._uom_override: + return self._uom_override + + if self.device_class == DEVICE_CLASS_TEMPERATURE: + if self._measurement_system == ErdMeasurementUnits.METRIC: + + # Actual data from API is always in Fahrenhreit but since Device preferences are set to Celcius + # we return Celcius here and will do the conversion ourselves + return TEMP_CELSIUS + else: + return TEMP_FAHRENHEIT + + return None + + def _get_device_class(self) -> Optional[str]: + if self._device_class_override: + return self._device_class_override + + if self.erd_code_class in [ + ErdCodeClass.RAW_TEMPERATURE, + ErdCodeClass.NON_ZERO_TEMPERATURE, + ]: + return DEVICE_CLASS_TEMPERATURE + + return None + + def _get_icon(self): + if self.erd_code_class == ErdCodeClass.DOOR: + if self.state.lower().endswith("open"): + return "mdi:door-open" + if self.state.lower().endswith("closed"): + return "mdi:door-closed" + return super()._get_icon() + + async def async_set_value(self, value): + """Sets the ERD value, assumes that the data type is correct""" + + if self._get_uom() == TEMP_CELSIUS: + # Convert to Fahrenheit + value = (value * 9/5) + 32 + + if self._data_type == ErdDataType.INT: + value = int(round(value)) + + try: + await self.appliance.async_set_erd_value(self.erd_code, value) + except: + _LOGGER.warning(f"Could not set {self.name} to {value}") \ No newline at end of file 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 240de5b..061a4e8 100644 --- a/custom_components/ge_home/entities/common/ge_erd_sensor.py +++ b/custom_components/ge_home/entities/common/ge_erd_sensor.py @@ -9,13 +9,11 @@ from homeassistant.const import ( DEVICE_CLASS_TEMPERATURE, DEVICE_CLASS_BATTERY, DEVICE_CLASS_POWER_FACTOR, - DEVICE_CLASS_TIMESTAMP, - TEMP_CELSIUS, - TEMP_FAHRENHEIT, ) -from gehomesdk import ErdCode, ErdCodeType, ErdCodeClass, ErdMeasurementUnits +from gehomesdk import ErdCodeType, ErdCodeClass, ErdMeasurementUnits from .ge_erd_entity import GeErdEntity from ...devices import ApplianceApi +from ...const import TEMP_CELSIUS, TEMP_FAHRENHEIT _LOGGER = logging.getLogger(__name__) @@ -45,7 +43,7 @@ class GeErdSensor(GeErdEntity, SensorEntity): # if it's a numeric data type, return it directly if self._data_type in [ErdDataType.INT, ErdDataType.FLOAT]: - return value + return self._convert_numeric_value_from_device(value) # otherwise, return a stringified version # TODO: perhaps enhance so that there's a list of variables available @@ -71,14 +69,23 @@ class GeErdSensor(GeErdEntity, SensorEntity): @property def _temp_units(self) -> Optional[str]: - #based on testing, all API values are in Fahrenheit, so we'll redefine - #this property to be the configured temperature unit and set the native - #unit differently - return self.api.hass.config.units.temperature_unit + # Return the unit from device preferences + if self._measurement_system == ErdMeasurementUnits.METRIC: + return TEMP_CELSIUS - #if self._measurement_system == ErdMeasurementUnits.METRIC: - # return TEMP_CELSIUS - #return TEMP_FAHRENHEIT + return TEMP_FAHRENHEIT + + def _convert_numeric_value_from_device(self, value): + """Convert to expected temperature units and data type""" + + if (self._get_uom() == TEMP_CELSIUS): + # Convert to Celcius since device always returns temperature in Fahrenheit regardless of device preferences + value = (value - 32 ) * 5/9 + + if self._data_type == ErdDataType.INT: + return int(round(value)) + else: + return value def _get_uom(self): """Select appropriate units""" @@ -92,10 +99,7 @@ class GeErdSensor(GeErdEntity, SensorEntity): in [ErdCodeClass.RAW_TEMPERATURE, ErdCodeClass.NON_ZERO_TEMPERATURE] or self.device_class == DEVICE_CLASS_TEMPERATURE ): - #NOTE: it appears that the API only sets temperature in Fahrenheit, - #so we'll hard code this UOM instead of using the device configured - #settings - return TEMP_FAHRENHEIT + return self._temp_units if ( self.erd_code_class == ErdCodeClass.BATTERY or self.device_class == DEVICE_CLASS_BATTERY diff --git a/custom_components/ge_home/manifest.json b/custom_components/ge_home/manifest.json index b821356..3a68d43 100644 --- a/custom_components/ge_home/manifest.json +++ b/custom_components/ge_home/manifest.json @@ -3,7 +3,7 @@ "name": "GE Home (SmartHQ)", "config_flow": true, "documentation": "https://github.com/simbaja/ha_gehome", - "requirements": ["gehomesdk==0.4.21","magicattr==0.1.5"], + "requirements": ["gehomesdk==0.4.22","magicattr==0.1.5","slixmpp==1.7.1"], "codeowners": ["@simbaja"], "version": "0.6.0" } diff --git a/custom_components/ge_home/number.py b/custom_components/ge_home/number.py new file mode 100644 index 0000000..eed2189 --- /dev/null +++ b/custom_components/ge_home/number.py @@ -0,0 +1,33 @@ +"""GE Home Number Entities""" +import async_timeout +import logging +from typing import Callable + +from homeassistant.config_entries import ConfigEntry +from homeassistant.core import HomeAssistant + +from .const import DOMAIN +from .entities import GeErdNumber +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 numbers.""" + + coordinator: GeHomeUpdateCoordinator = hass.data[DOMAIN][config_entry.entry_id] + + # This should be a NOP, but let's be safe + with async_timeout.timeout(20): + await coordinator.initialization_future + + apis = coordinator.appliance_apis.values() + _LOGGER.debug(f'Found {len(apis):d} appliance APIs') + entities = [ + entity + for api in apis + for entity in api.entities + if isinstance(entity, GeErdNumber) + ] + _LOGGER.debug(f'Found {len(entities):d} numbers ') + async_add_entities(entities) diff --git a/custom_components/ge_home/update_coordinator.py b/custom_components/ge_home/update_coordinator.py index b3ee44b..8711d98 100644 --- a/custom_components/ge_home/update_coordinator.py +++ b/custom_components/ge_home/update_coordinator.py @@ -34,7 +34,7 @@ from .const import ( ) from .devices import ApplianceApi, get_appliance_api_type -PLATFORMS = ["binary_sensor", "sensor", "switch", "water_heater", "select", "climate", "light"] +PLATFORMS = ["binary_sensor", "sensor", "switch", "water_heater", "select", "climate", "light", "button", "number"] _LOGGER = logging.getLogger(__name__) @@ -261,6 +261,7 @@ class GeHomeUpdateCoordinator(DataUpdateCoordinator): api = self.appliance_apis[appliance.mac_addr] except KeyError: return + for entity in api.entities: if entity.enabled: _LOGGER.debug(f"Updating {entity} ({entity.unique_id}, {entity.entity_id})") From 5a1f2fc69f436ae894ea5e1bc2ddd1a27e4f52f7 Mon Sep 17 00:00:00 2001 From: alexanv1 <44785744+alexanv1@users.noreply.github.com> Date: Tue, 15 Feb 2022 23:26:24 -0800 Subject: [PATCH 23/28] Remove temperature unit changes --- custom_components/ge_home/const.py | 5 ---- .../entities/ccm/ge_ccm_brew_temperature.py | 9 +------ .../ge_home/entities/common/ge_erd_number.py | 25 +++++------------- .../ge_home/entities/common/ge_erd_sensor.py | 26 ++++++++++--------- 4 files changed, 22 insertions(+), 43 deletions(-) diff --git a/custom_components/ge_home/const.py b/custom_components/ge_home/const.py index ac908dc..76cef76 100644 --- a/custom_components/ge_home/const.py +++ b/custom_components/ge_home/const.py @@ -13,8 +13,3 @@ RETRY_OFFLINE_COUNT = 5 SERVICE_SET_TIMER = "set_timer" SERVICE_CLEAR_TIMER = "clear_timer" SERVICE_SET_INT_VALUE = "set_int_value" - -# Prevent Home Assistant automatic temperature conversions by overriding TEMP_CELCIUS, TEMP_FAHRENHEIT -# This makes sure that the values shows in the UI match device preferences bypassing the automatic conversion to whatever the Home Assistant default is set to -TEMP_CELSIUS = "\u2103" -TEMP_FAHRENHEIT = "\u2109" diff --git a/custom_components/ge_home/entities/ccm/ge_ccm_brew_temperature.py b/custom_components/ge_home/entities/ccm/ge_ccm_brew_temperature.py index bff5127..4dc20e9 100644 --- a/custom_components/ge_home/entities/ccm/ge_ccm_brew_temperature.py +++ b/custom_components/ge_home/entities/ccm/ge_ccm_brew_temperature.py @@ -1,7 +1,6 @@ from gehomesdk import ErdCode from ...devices import ApplianceApi from ..common import GeErdNumber -from ...const import TEMP_CELSIUS from .ge_ccm_cached_value import GeCcmCachedValue class GeCcmBrewTemperatureNumber(GeErdNumber, GeCcmCachedValue): @@ -20,10 +19,4 @@ class GeCcmBrewTemperatureNumber(GeErdNumber, GeCcmCachedValue): @property def brew_temperature(self) -> int: - - value = self.value - if self.unit_of_measurement == TEMP_CELSIUS: - # Convert to Fahrenheit - value = int(round(value * 9/5) + 32) - - return value \ No newline at end of file + return self.value \ No newline at end of file diff --git a/custom_components/ge_home/entities/common/ge_erd_number.py b/custom_components/ge_home/entities/common/ge_erd_number.py index 50fb63a..bea3f69 100644 --- a/custom_components/ge_home/entities/common/ge_erd_number.py +++ b/custom_components/ge_home/entities/common/ge_erd_number.py @@ -5,11 +5,11 @@ from homeassistant.components.number import NumberEntity from homeassistant.const import ( DEVICE_CLASS_TEMPERATURE, + TEMP_FAHRENHEIT, ) -from gehomesdk import ErdCodeType, ErdCodeClass, ErdMeasurementUnits +from gehomesdk import ErdCodeType, ErdCodeClass from .ge_erd_entity import GeErdEntity from ...devices import ApplianceApi -from ...const import TEMP_CELSIUS, TEMP_FAHRENHEIT _LOGGER = logging.getLogger(__name__) @@ -74,11 +74,7 @@ class GeErdNumber(GeErdEntity, NumberEntity): return self._mode def _convert_value_from_device(self, value): - """Convert to expected temperature units and data type""" - - if (self._get_uom() == TEMP_CELSIUS): - # Convert to Celcius - value = (value - 32 ) * 5/9 + """Convert to expected data type""" if self._data_type == ErdDataType.INT: return int(round(value)) @@ -93,13 +89,10 @@ class GeErdNumber(GeErdEntity, NumberEntity): return self._uom_override if self.device_class == DEVICE_CLASS_TEMPERATURE: - if self._measurement_system == ErdMeasurementUnits.METRIC: - - # Actual data from API is always in Fahrenhreit but since Device preferences are set to Celcius - # we return Celcius here and will do the conversion ourselves - return TEMP_CELSIUS - else: - return TEMP_FAHRENHEIT + #NOTE: it appears that the API only sets temperature in Fahrenheit, + #so we'll hard code this UOM instead of using the device configured + #settings + return TEMP_FAHRENHEIT return None @@ -126,10 +119,6 @@ class GeErdNumber(GeErdEntity, NumberEntity): async def async_set_value(self, value): """Sets the ERD value, assumes that the data type is correct""" - if self._get_uom() == TEMP_CELSIUS: - # Convert to Fahrenheit - value = (value * 9/5) + 32 - if self._data_type == ErdDataType.INT: value = int(round(value)) 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 061a4e8..ffb16f0 100644 --- a/custom_components/ge_home/entities/common/ge_erd_sensor.py +++ b/custom_components/ge_home/entities/common/ge_erd_sensor.py @@ -9,11 +9,11 @@ from homeassistant.const import ( DEVICE_CLASS_TEMPERATURE, DEVICE_CLASS_BATTERY, DEVICE_CLASS_POWER_FACTOR, + TEMP_FAHRENHEIT, ) -from gehomesdk import ErdCodeType, ErdCodeClass, ErdMeasurementUnits +from gehomesdk import ErdCodeType, ErdCodeClass from .ge_erd_entity import GeErdEntity from ...devices import ApplianceApi -from ...const import TEMP_CELSIUS, TEMP_FAHRENHEIT _LOGGER = logging.getLogger(__name__) @@ -69,18 +69,17 @@ class GeErdSensor(GeErdEntity, SensorEntity): @property def _temp_units(self) -> Optional[str]: - # Return the unit from device preferences - if self._measurement_system == ErdMeasurementUnits.METRIC: - return TEMP_CELSIUS + #based on testing, all API values are in Fahrenheit, so we'll redefine + #this property to be the configured temperature unit and set the native + #unit differently + return self.api.hass.config.units.temperature_unit - return TEMP_FAHRENHEIT + #if self._measurement_system == ErdMeasurementUnits.METRIC: + # return TEMP_CELSIUS + #return TEMP_FAHRENHEIT def _convert_numeric_value_from_device(self, value): - """Convert to expected temperature units and data type""" - - if (self._get_uom() == TEMP_CELSIUS): - # Convert to Celcius since device always returns temperature in Fahrenheit regardless of device preferences - value = (value - 32 ) * 5/9 + """Convert to expected data type""" if self._data_type == ErdDataType.INT: return int(round(value)) @@ -99,7 +98,10 @@ class GeErdSensor(GeErdEntity, SensorEntity): in [ErdCodeClass.RAW_TEMPERATURE, ErdCodeClass.NON_ZERO_TEMPERATURE] or self.device_class == DEVICE_CLASS_TEMPERATURE ): - return self._temp_units + #NOTE: it appears that the API only sets temperature in Fahrenheit, + #so we'll hard code this UOM instead of using the device configured + #settings + return TEMP_FAHRENHEIT if ( self.erd_code_class == ErdCodeClass.BATTERY or self.device_class == DEVICE_CLASS_BATTERY From 37cf69b99d23d72b88f81ed58428a70d598614b0 Mon Sep 17 00:00:00 2001 From: Jack Simbach Date: Fri, 18 Feb 2022 23:51:43 -0500 Subject: [PATCH 24/28] - documentation updates --- CHANGELOG.md | 1 + README.md | 1 + info.md | 2 ++ 3 files changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7f1930d..50d1833 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ - Initial support for Microwaves (@mbcomer, @mnestor) - Initial support for Water Softeners (@npentell, @drjeff) - Initial support for Opal Ice Makers (@mbcomer, @knobunc) +- Initial support for Coffee Makers (@alexanv1) - Updated deprecated icons (@mjmeli, @schmittx) ## 0.5.0 diff --git a/README.md b/README.md index 59ec6e0..f669bf3 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,7 @@ Integration for GE WiFi-enabled appliances into Home Assistant. This integratio - Advantium - Microwave - Opal Ice Maker +- Coffee Maker **Forked from Andrew Mark's [repository](https://github.com/ajmarks/ha_components).** diff --git a/info.md b/info.md index ae9a5b2..b449127 100644 --- a/info.md +++ b/info.md @@ -13,6 +13,7 @@ Integration for GE WiFi-enabled appliances into Home Assistant. This integratio - Advantium - Microwave - Opal Ice Maker +- Coffee Maker **Forked from Andrew Mark's [repository](https://github.com/ajmarks/ha_components).** @@ -61,6 +62,7 @@ A/C Controls: - Initial support for Water Softeners (@npentell, @drjeff) - Initial support for Opal Ice Makers (@mbcomer, @knobunc) - Initial support for Microwaves (@mbcomer, @mnestor) +- Initial support for Coffee Makers (@alexanv1) {% endif %} {% if version_installed.split('.') | map('int') < '0.5.0'.split('.') | map('int') %} From 9b901f82f786354690aa0107470006c967ba5074 Mon Sep 17 00:00:00 2001 From: Jack Simbach Date: Sat, 19 Feb 2022 15:03:14 -0500 Subject: [PATCH 25/28] - implemented us/eu authorization fixes --- custom_components/ge_home/__init__.py | 19 +++++++++++++++++++ custom_components/ge_home/config_flow.py | 20 +++++++++++++++----- custom_components/ge_home/manifest.json | 2 +- 3 files changed, 35 insertions(+), 6 deletions(-) diff --git a/custom_components/ge_home/__init__.py b/custom_components/ge_home/__init__.py index 36c2c0b..7b3f082 100644 --- a/custom_components/ge_home/__init__.py +++ b/custom_components/ge_home/__init__.py @@ -1,19 +1,38 @@ """The ge_home integration.""" +import logging from homeassistant.const import EVENT_HOMEASSISTANT_STOP import voluptuous as vol from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant +from homeassistant.const import CONF_REGION from .const import DOMAIN from .update_coordinator import GeHomeUpdateCoordinator +_LOGGER = logging.getLogger(__name__) CONFIG_SCHEMA = vol.Schema({DOMAIN: vol.Schema({})}, extra=vol.ALLOW_EXTRA) async def async_setup(hass: HomeAssistant, config: dict): return True +async def async_migrate_entry(hass: HomeAssistant, config_entry: ConfigEntry): + """Migrate old entry.""" + _LOGGER.debug("Migrating from version %s", config_entry.version) + + if config_entry.version == 1: + + new = {**config_entry.data} + new[CONF_REGION] = "US" + + config_entry.version = 2 + hass.config_entries.async_update_entry(config_entry, data=new) + + _LOGGER.info("Migration to version %s successful", config_entry.version) + + return True + async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry): """Set up the ge_home component.""" diff --git a/custom_components/ge_home/config_flow.py b/custom_components/ge_home/config_flow.py index e303312..3116627 100644 --- a/custom_components/ge_home/config_flow.py +++ b/custom_components/ge_home/config_flow.py @@ -7,11 +7,17 @@ import aiohttp import asyncio import async_timeout -from gehomesdk import GeAuthFailedError, GeNotAuthenticatedError, GeGeneralServerError, async_get_oauth2_token +from gehomesdk import ( + GeAuthFailedError, + GeNotAuthenticatedError, + GeGeneralServerError, + async_get_oauth2_token, + LOGIN_REGIONS +) import voluptuous as vol from homeassistant import config_entries, core -from homeassistant.const import CONF_PASSWORD, CONF_USERNAME +from homeassistant.const import CONF_PASSWORD, CONF_USERNAME, CONF_REGION from .const import DOMAIN # pylint:disable=unused-import from .exceptions import HaAuthError, HaCannotConnect, HaAlreadyConfigured @@ -19,7 +25,11 @@ from .exceptions import HaAuthError, HaCannotConnect, HaAlreadyConfigured _LOGGER = logging.getLogger(__name__) GEHOME_SCHEMA = vol.Schema( - {vol.Required(CONF_USERNAME): str, vol.Required(CONF_PASSWORD): str} + { + vol.Required(CONF_USERNAME): str, + vol.Required(CONF_PASSWORD): str, + vol.Required(CONF_REGION): vol.In(LOGIN_REGIONS.keys()) + } ) async def validate_input(hass: core.HomeAssistant, data): @@ -30,7 +40,7 @@ async def validate_input(hass: core.HomeAssistant, data): # noinspection PyBroadException try: with async_timeout.timeout(10): - _ = await async_get_oauth2_token(session, data[CONF_USERNAME], data[CONF_PASSWORD]) + _ = await async_get_oauth2_token(session, data[CONF_USERNAME], data[CONF_PASSWORD], data[CONF_REGION]) except (asyncio.TimeoutError, aiohttp.ClientError): raise HaCannotConnect('Connection failure') except (GeAuthFailedError, GeNotAuthenticatedError): @@ -47,7 +57,7 @@ async def validate_input(hass: core.HomeAssistant, data): class GeHomeConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): """Handle a config flow for GE Home.""" - VERSION = 1 + VERSION = 2 CONNECTION_CLASS = config_entries.CONN_CLASS_CLOUD_PUSH async def _async_validate_input(self, user_input): diff --git a/custom_components/ge_home/manifest.json b/custom_components/ge_home/manifest.json index 3a68d43..ec5a90e 100644 --- a/custom_components/ge_home/manifest.json +++ b/custom_components/ge_home/manifest.json @@ -3,7 +3,7 @@ "name": "GE Home (SmartHQ)", "config_flow": true, "documentation": "https://github.com/simbaja/ha_gehome", - "requirements": ["gehomesdk==0.4.22","magicattr==0.1.5","slixmpp==1.7.1"], + "requirements": ["gehomesdk==0.4.24","magicattr==0.1.5","slixmpp==1.7.1"], "codeowners": ["@simbaja"], "version": "0.6.0" } From d99dbe2a4bcf49576cf767e7f8b7c120422a054f Mon Sep 17 00:00:00 2001 From: Jack Simbach Date: Sat, 19 Feb 2022 15:05:53 -0500 Subject: [PATCH 26/28] - documentation updates --- CHANGELOG.md | 1 + hacs.json | 2 +- info.md | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 50d1833..18a3b52 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ ## 0.6.0 - Requires HA 2021.12.x or later +- Enabled authentication to both US and EU regions - Changed the sensors to use native value/uom - Changed the temperatures to always be natively fahrenheit (API appears to always use this system) (@vignatyuk) - Initial support for Microwaves (@mbcomer, @mnestor) diff --git a/hacs.json b/hacs.json index e9e1a0b..7c919dc 100644 --- a/hacs.json +++ b/hacs.json @@ -1,6 +1,6 @@ { "name": "GE Home (SmartHQ)", "homeassistant": "2021.12.0", - "domains": ["binary_sensor", "sensor", "switch", "water_heater", "select"], + "domains": ["binary_sensor", "sensor", "switch", "water_heater", "select", "button", "climate", "light", "number"], "iot_class": "Cloud Polling" } diff --git a/info.md b/info.md index b449127..d8d24e2 100644 --- a/info.md +++ b/info.md @@ -42,6 +42,7 @@ A/C Controls: {% if version_installed.split('.') | map('int') < '0.6.0'.split('.') | map('int') %} - Requires HA version 2021.12.0 or later +- Enabled authentication to both US and EU regions (may require re-auth) - Changed the sensors to use native value/uom - Changed the temperatures to always be natively fahrenheit (API appears to always use this system) (@vignatyuk) {% endif %} From 5cf7e6a67672007f0476313786a6e9ac0ef5f295 Mon Sep 17 00:00:00 2001 From: Jack Simbach Date: Sat, 30 Apr 2022 19:48:20 -0400 Subject: [PATCH 27/28] - bumped gehomesdk version --- custom_components/ge_home/manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/custom_components/ge_home/manifest.json b/custom_components/ge_home/manifest.json index ec5a90e..aca93d4 100644 --- a/custom_components/ge_home/manifest.json +++ b/custom_components/ge_home/manifest.json @@ -3,7 +3,7 @@ "name": "GE Home (SmartHQ)", "config_flow": true, "documentation": "https://github.com/simbaja/ha_gehome", - "requirements": ["gehomesdk==0.4.24","magicattr==0.1.5","slixmpp==1.7.1"], + "requirements": ["gehomesdk==0.4.25","magicattr==0.1.5","slixmpp==1.7.1"], "codeowners": ["@simbaja"], "version": "0.6.0" } From a02819589995bae68e3072eb8aea332384466e69 Mon Sep 17 00:00:00 2001 From: Jack Simbach Date: Sat, 30 Apr 2022 19:51:24 -0400 Subject: [PATCH 28/28] - updated readme --- README.md | 2 ++ info.md | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/README.md b/README.md index f669bf3..42ad817 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,9 @@ Integration for GE WiFi-enabled appliances into Home Assistant. This integratio - Coffee Maker **Forked from Andrew Mark's [repository](https://github.com/ajmarks/ha_components).** +## Updates +Unfortunately, I'm pretty much at the end of what I can do without assistance from others with these devices that can help provide logs. I'll do what I can to make updates if there's something broken, but I am not really able to add new functionality if I can't get a little help to do so. ## Home Assistant UI Examples Entities card: diff --git a/info.md b/info.md index d8d24e2..f99e99d 100644 --- a/info.md +++ b/info.md @@ -17,6 +17,10 @@ Integration for GE WiFi-enabled appliances into Home Assistant. This integratio **Forked from Andrew Mark's [repository](https://github.com/ajmarks/ha_components).** +## Updates + +Unfortunately, I'm pretty much at the end of what I can do without assistance from others with these devices that can help provide logs. I'll do what I can to make updates if there's something broken, but I am not really able to add new functionality if I can't get a little help to do so. + ## Home Assistant UI Examples Entities card: