ha_gehome/entities/advantium/ge_advantium.py

284 lines
11 KiB
Python

"""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()