"""GE Home Sensor Entities - Advantium""" import logging from typing import Any, Dict, List, Mapping, Optional, Set from random import randrange from gehomesdk import ( ErdCode, ErdPersonality, ErdAdvantiumCookStatus, ErdAdvantiumCookSetting, AdvantiumOperationMode, AdvantiumCookSetting, ErdAdvantiumRemoteCookModeConfig, ADVANTIUM_OPERATION_MODE_COOK_SETTING_MAPPING ) from gehomesdk.erd.values.advantium.advantium_enums import CookAction, CookMode from homeassistant.components.sensor import SensorDeviceClass from homeassistant.const import ATTR_TEMPERATURE from ...const import DOMAIN from ...devices import ApplianceApi from ..common import GeAbstractWaterHeater from .const import * _LOGGER = logging.getLogger(__name__) class GeAdvantium(GeAbstractWaterHeater): """GE Appliance Advantium""" icon = "mdi:microwave" def __init__(self, api: ApplianceApi): super().__init__(api) @property def supported_features(self): if self.remote_enabled: return GE_ADVANTIUM_WITH_TEMPERATURE if self.can_set_temperature else GE_ADVANTIUM else: return SUPPORT_NONE @property def unique_id(self) -> str: return f"{DOMAIN}_{self.serial_number}" @property def name(self) -> Optional[str]: return f"{self.serial_number} Advantium" @property def personality(self) -> Optional[ErdPersonality]: try: return self.appliance.get_erd_value(ErdCode.PERSONALITY) except: return None @property def remote_enabled(self) -> bool: """Returns whether the oven is remote enabled""" value = self.appliance.get_erd_value(ErdCode.UPPER_OVEN_REMOTE_ENABLED) return value == True @property def current_temperature(self) -> Optional[int]: return self.appliance.get_erd_value(ErdCode.UPPER_OVEN_DISPLAY_TEMPERATURE) @property def current_operation(self) -> Optional[str]: try: return self.current_operation_mode.stringify() except: return None @property def operation_list(self) -> List[str]: invalid = [] if not self._remote_config.broil_enable: invalid.append(CookMode.BROIL) if not self._remote_config.convection_bake_enable: invalid.append(CookMode.CONVECTION_BAKE) if not self._remote_config.proof_enable: invalid.append(CookMode.PROOF) if not self._remote_config.warm_enable: invalid.append(CookMode.WARM) return [ k.stringify() for k, v in ADVANTIUM_OPERATION_MODE_COOK_SETTING_MAPPING.items() if v.cook_mode not in invalid] @property def current_cook_setting(self) -> ErdAdvantiumCookSetting: """Get the current cook setting.""" return self.appliance.get_erd_value(ErdCode.ADVANTIUM_COOK_SETTING) @property def current_cook_status(self) -> ErdAdvantiumCookStatus: """Get the current status.""" return self.appliance.get_erd_value(ErdCode.ADVANTIUM_COOK_STATUS) @property def current_operation_mode(self) -> AdvantiumOperationMode: """Gets the current operation mode""" return self._current_operation_mode @property def current_operation_setting(self) -> Optional[AdvantiumCookSetting]: if self.current_operation_mode is None: return None try: return ADVANTIUM_OPERATION_MODE_COOK_SETTING_MAPPING[self.current_operation_mode] except: _LOGGER.debug(f"Unable to determine operation setting, mode = {self.current_operation_mode}") return None @property def can_set_temperature(self) -> bool: """Indicates whether we can set the temperature based on the current mode""" try: return self.current_operation_setting.allow_temperature_set except: return False @property def target_temperature(self) -> Optional[int]: """Return the temperature we try to reach.""" try: cook_mode = self.current_cook_setting if cook_mode.target_temperature and cook_mode.target_temperature > 0: return cook_mode.target_temperature except: pass return None @property def min_temp(self) -> int: """Return the minimum temperature.""" min_temp, _ = self.appliance.get_erd_value(ErdCode.OVEN_MODE_MIN_MAX_TEMP) return min_temp @property def max_temp(self) -> int: """Return the maximum temperature.""" _, max_temp = self.appliance.get_erd_value(ErdCode.OVEN_MODE_MIN_MAX_TEMP) return max_temp @property def extra_state_attributes(self) -> Optional[Mapping[str, Any]]: data = {} cook_time_remaining = self.appliance.get_erd_value(ErdCode.ADVANTIUM_COOK_TIME_REMAINING) kitchen_timer = self.appliance.get_erd_value(ErdCode.ADVANTIUM_KITCHEN_TIME_REMAINING) data["unit_type"] = self._stringify(self.personality) if cook_time_remaining: data["cook_time_remaining"] = self._stringify(cook_time_remaining) if kitchen_timer: data["kitchen_timer"] = self._stringify(kitchen_timer) return data @property def _remote_config(self) -> ErdAdvantiumRemoteCookModeConfig: return self.appliance.get_erd_value(ErdCode.ADVANTIUM_REMOTE_COOK_MODE_CONFIG) async def async_set_operation_mode(self, operation_mode: str): """Set the operation mode.""" #try to get the mode/setting for the selection try: mode = AdvantiumOperationMode(operation_mode) setting = ADVANTIUM_OPERATION_MODE_COOK_SETTING_MAPPING[mode] except: _LOGGER.debug(f"Attempted to set mode to {operation_mode}, unknown.") return #determine the target temp for this mode target_temp = self._convert_target_temperature(setting.target_temperature_120v_f, setting.target_temperature_240v_f) #if we allow temperature to be set in this mode, and already have a temperature, use it if setting.allow_temperature_set and self.target_temperature: target_temp = self.target_temperature #by default we will start an operation, but handle other actions too action = CookAction.START if mode == AdvantiumOperationMode.OFF: action = CookAction.STOP elif self.current_cook_setting.cook_action == CookAction.PAUSE: action = CookAction.RESUME elif self.current_cook_setting.cook_action in [CookAction.START, CookAction.RESUME]: action = CookAction.UPDATED #construct the new mode based on the existing mode new_cook_mode = self.current_cook_setting new_cook_mode.d = randrange(255) new_cook_mode.target_temperature = target_temp if(setting.target_power_level != 0): new_cook_mode.power_level = setting.target_power_level new_cook_mode.cook_mode = setting.cook_mode new_cook_mode.cook_action = action await self.appliance.async_set_erd_value(ErdCode.ADVANTIUM_COOK_SETTING, new_cook_mode) async def async_set_temperature(self, **kwargs): """Set the cook temperature""" target_temp = kwargs.get(ATTR_TEMPERATURE) if target_temp is None: return #get the current mode/operation mode = self.current_operation_mode setting = self.current_operation_setting #if we can't figure out the mode/setting, exit if mode is None or setting is None: return #if we're off or can't set temperature, just exit if mode == AdvantiumOperationMode.OFF or not setting.allow_temperature_set: return #should only need to update action = CookAction.UPDATED #construct the new mode based on the existing mode new_cook_mode = self.current_cook_setting new_cook_mode.d = randrange(255) new_cook_mode.target_temperature = target_temp new_cook_mode.cook_action = action await self.appliance.async_set_erd_value(ErdCode.ADVANTIUM_COOK_SETTING, new_cook_mode) async def _ensure_operation_mode(self): cook_setting = self.current_cook_setting cook_mode = cook_setting.cook_mode #if we have a current mode if(self._current_operation_mode is not None): #and the cook mode is the same as what the appliance says, we'll just leave things alone #and assume that things are in sync if ADVANTIUM_OPERATION_MODE_COOK_SETTING_MAPPING[self._current_operation_mode].cook_mode == cook_mode: return else: self._current_operation_mode = None #synchronize the operation mode with the device state if cook_mode == CookMode.MICROWAVE: #microwave matches on cook mode and power level if cook_setting.power_level == 3: self._current_operation_mode = AdvantiumOperationMode.MICROWAVE_PL3 elif cook_setting.power_level == 5: self._current_operation_mode = AdvantiumOperationMode.MICROWAVE_PL5 elif cook_setting.power_level == 7: self._current_operation_mode = AdvantiumOperationMode.MICROWAVE_PL7 else: self._current_operation_mode = AdvantiumOperationMode.MICROWAVE_PL10 elif cook_mode == CookMode.WARM: for key, value in ADVANTIUM_OPERATION_MODE_COOK_SETTING_MAPPING.items(): #warm matches on the mode, warm status, and target temp if (cook_mode == value.cook_mode and cook_setting.warm_status == value.warm_status and cook_setting.target_temperature == self._convert_target_temperature( value.target_temperature_120v_f, value.target_temperature_240v_f)): self._current_operation_mode = key return #just pick the first match based on cook mode if we made it here if self._current_operation_mode is None: for key, value in ADVANTIUM_OPERATION_MODE_COOK_SETTING_MAPPING.items(): if cook_mode == value.cook_mode: self._current_operation_mode = key return async def _convert_target_temperature(self, temp_120v: int, temp_240v: int): unit_type = self.personality target_temp_f = temp_240v if unit_type in [ErdPersonality.PERSONALITY_240V_MONOGRAM, ErdPersonality.PERSONALITY_240V_CAFE, ErdPersonality.PERSONALITY_240V_STANDALONE_CAFE] else temp_120v if self.temperature_unit == SensorDeviceClass.FAHRENHEIT: return float(target_temp_f) else: return (target_temp_f - 32.0) * (5/9) async def async_device_update(self, warning: bool) -> None: await super().async_device_update(warning=warning) await self._ensure_operation_mode()