Initial commit to new repo

This commit is contained in:
John-WE1DER 2025-05-17 05:50:27 -05:00
commit 1173160cfd
218 changed files with 6126 additions and 0 deletions

81
__init__.py Normal file
View File

@ -0,0 +1,81 @@
"""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 homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
from .const import DOMAIN
from .exceptions import HaAuthError, HaCannotConnect
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 ge_home from a config entry."""
hass.data.setdefault(DOMAIN, {})
#try to get existing coordinator
existing: GeHomeUpdateCoordinator = dict.get(hass.data[DOMAIN],entry.entry_id)
coordinator = GeHomeUpdateCoordinator(hass, entry)
hass.data[DOMAIN][entry.entry_id] = coordinator
# try to unload the existing coordinator
try:
if existing:
await coordinator.async_reset()
except:
_LOGGER.warning("Could not reset existing coordinator.")
try:
if not await coordinator.async_setup():
return False
except HaCannotConnect:
raise ConfigEntryNotReady("Could not connect to SmartHQ")
except HaAuthError:
raise ConfigEntryAuthFailed("Could not authenticate to SmartHQ")
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, coordinator.shutdown)
return True
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry):
"""Unload a config entry."""
coordinator: GeHomeUpdateCoordinator = hass.data[DOMAIN][entry.entry_id]
ok = await coordinator.async_reset()
if ok:
hass.data[DOMAIN].pop(entry.entry_id)
return ok
async def async_update_options(hass, config_entry):
"""Update options."""
await hass.config_entries.async_reload(config_entry.entry_id)

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

47
binary_sensor.py Normal file
View File

@ -0,0 +1,47 @@
"""GE Home Sensor Entities"""
import logging
from typing import Callable
from homeassistant.components.switch import SwitchEntity
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers import entity_registry as er
from .const import DOMAIN
from .devices import ApplianceApi
from .entities import GeErdBinarySensor
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 binary sensors."""
_LOGGER.debug('Adding GE Binary Sensor Entities')
coordinator: GeHomeUpdateCoordinator = hass.data[DOMAIN][config_entry.entry_id]
registry = er.async_get(hass)
@callback
def async_devices_discovered(apis: list[ApplianceApi]):
_LOGGER.debug(f'Found {len(apis):d} appliance APIs')
entities = [
entity
for api in apis
for entity in api.entities
if isinstance(entity, GeErdBinarySensor) and not isinstance(entity, SwitchEntity)
if not registry.async_is_registered(entity.entity_id)
]
_LOGGER.debug(f'Found {len(entities):d} unregistered binary sensors')
async_add_entities(entities)
#if we're already initialized at this point, call device
#discovery directly, otherwise add a callback based on the
#ready signal
if coordinator.initialized:
async_devices_discovered(coordinator.appliance_apis.values())
else:
# add the ready signal and register the remove callback
coordinator.add_signal_remove_callback(
async_dispatcher_connect(hass, coordinator.signal_ready, async_devices_discovered))

45
button.py Normal file
View File

@ -0,0 +1,45 @@
"""GE Home Button Entities"""
import logging
from typing import Callable
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers import entity_registry as er
from .const import DOMAIN
from .devices import ApplianceApi
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."""
_LOGGER.debug('Adding GE Button Entities')
coordinator: GeHomeUpdateCoordinator = hass.data[DOMAIN][config_entry.entry_id]
registry = er.async_get(hass)
@callback
def async_devices_discovered(apis: list[ApplianceApi]):
_LOGGER.debug(f'Found {len(apis):d} appliance APIs')
entities = [
entity
for api in apis
for entity in api.entities
if isinstance(entity, GeErdButton)
if not registry.async_is_registered(entity.entity_id)
]
_LOGGER.debug(f'Found {len(entities):d} unregistered buttons ')
async_add_entities(entities)
#if we're already initialized at this point, call device
#discovery directly, otherwise add a callback based on the
#ready signal
if coordinator.initialized:
async_devices_discovered(coordinator.appliance_apis.values())
else:
# add the ready signal and register the remove callback
coordinator.add_signal_remove_callback(
async_dispatcher_connect(hass, coordinator.signal_ready, async_devices_discovered))

47
climate.py Normal file
View File

@ -0,0 +1,47 @@
"""GE Home Climate Entities"""
import logging
from typing import Callable
from homeassistant.components.climate import ClimateEntity
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers import entity_registry as er
from .entities import GeClimate
from .const import DOMAIN
from .devices import ApplianceApi
from .update_coordinator import GeHomeUpdateCoordinator
_LOGGER = logging.getLogger(__name__)
async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry, async_add_entities: Callable):
"""GE Climate Devices."""
_LOGGER.debug('Adding GE Climate Entities')
coordinator: GeHomeUpdateCoordinator = hass.data[DOMAIN][config_entry.entry_id]
registry = er.async_get(hass)
@callback
def async_devices_discovered(apis: list[ApplianceApi]):
_LOGGER.debug(f'Found {len(apis):d} appliance APIs')
entities = [
entity
for api in apis
for entity in api.entities
if isinstance(entity, GeClimate)
if not registry.async_is_registered(entity.entity_id)
]
_LOGGER.debug(f'Found {len(entities):d} unregistered climate entities')
async_add_entities(entities)
#if we're already initialized at this point, call device
#discovery directly, otherwise add a callback based on the
#ready signal
if coordinator.initialized:
async_devices_discovered(coordinator.appliance_apis.values())
else:
# add the ready signal and register the remove callback
coordinator.add_signal_remove_callback(
async_dispatcher_connect(hass, coordinator.signal_ready, async_devices_discovered))

129
config_flow.py Normal file
View File

@ -0,0 +1,129 @@
"""Config flow for GE Home integration."""
import logging
from typing import Dict, Optional
import aiohttp
import asyncio
import async_timeout
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, CONF_REGION
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from .const import DOMAIN # pylint:disable=unused-import
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_REGION): vol.In(LOGIN_REGIONS.keys())
}
)
async def validate_input(hass: core.HomeAssistant, data):
"""Validate the user input allows us to connect."""
session = async_get_clientsession(hass)
# noinspection PyBroadException
try:
async with async_timeout.timeout(10):
_ = 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):
raise HaAuthError('Authentication failure')
except GeGeneralServerError:
raise HaCannotConnect('Cannot connect (server error)')
except Exception as exc:
_LOGGER.exception("Unknown connection failure", exc_info=exc)
raise HaCannotConnect('Unknown connection failure')
# Return info that you want to store in the config entry.
return {"title": f"{data[CONF_USERNAME]:s}"}
class GeHomeConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
"""Handle a config flow for GE Home."""
VERSION = 2
CONNECTION_CLASS = config_entries.CONN_CLASS_CLOUD_PUSH
async def _async_validate_input(self, user_input):
"""Validate form input."""
errors = {}
info = None
if user_input is not None:
# noinspection PyBroadException
try:
info = await validate_input(self.hass, user_input)
except HaCannotConnect:
errors["base"] = "cannot_connect"
except HaAuthError:
errors["base"] = "invalid_auth"
except Exception: # pylint: disable=broad-except
_LOGGER.exception("Unexpected exception")
errors["base"] = "unknown"
return info, errors
def _ensure_not_configured(self, username: str):
"""Ensure that we haven't configured this account"""
existing_accounts = {
entry.data[CONF_USERNAME] for entry in self._async_current_entries()
}
_LOGGER.debug(f"Existing accounts: {existing_accounts}")
if username in existing_accounts:
raise HaAlreadyConfigured
async def async_step_user(self, user_input: Optional[Dict] = None):
"""Handle the initial step."""
errors = {}
if user_input is not None:
try:
self._ensure_not_configured(user_input[CONF_USERNAME])
info, errors = await self._async_validate_input(user_input)
if info:
return self.async_create_entry(title=info["title"], data=user_input)
except HaAlreadyConfigured:
return self.async_abort(reason="already_configured_account")
return self.async_show_form(
step_id="user", data_schema=GEHOME_SCHEMA, errors=errors
)
async def async_step_reauth(self, user_input: Optional[dict] = None):
"""Handle re-auth if login is invalid."""
errors = {}
if user_input is not None:
_, errors = await self._async_validate_input(user_input)
if not errors:
for entry in self._async_current_entries():
if entry.unique_id == self.unique_id:
self.hass.config_entries.async_update_entry(
entry, data=user_input
)
await self.hass.config_entries.async_reload(entry.entry_id)
return self.async_abort(reason="reauth_successful")
if errors["base"] != "invalid_auth":
return self.async_abort(reason=errors["base"])
return self.async_show_form(
step_id="reauth", data_schema=GEHOME_SCHEMA, errors=errors,
)

15
const.py Normal file
View File

@ -0,0 +1,15 @@
"""Constants for the gehome integration."""
DOMAIN = "ge_home"
EVENT_ALL_APPLIANCES_READY = 'all_appliances_ready'
UPDATE_INTERVAL = 30
ASYNC_TIMEOUT = 30
MIN_RETRY_DELAY = 15
MAX_RETRY_DELAY = 1800
RETRY_OFFLINE_COUNT = 5
SERVICE_SET_TIMER = "set_timer"
SERVICE_CLEAR_TIMER = "clear_timer"
SERVICE_SET_INT_VALUE = "set_int_value"

66
devices/__init__.py Normal file
View File

@ -0,0 +1,66 @@
import logging
from typing import Type
from gehomesdk.erd import ErdApplianceType
from .base import ApplianceApi
from .oven import OvenApi
from .cooktop import CooktopApi
from .fridge import FridgeApi
from .dishwasher import DishwasherApi
from .washer import WasherApi
from .dryer import DryerApi
from .washer_dryer import WasherDryerApi
from .water_filter import WaterFilterApi
from .advantium import AdvantiumApi
from .wac import WacApi
from .sac import SacApi
from .pac import PacApi
from .biac import BiacApi
from .hood import HoodApi
from .microwave import MicrowaveApi
from .water_softener import WaterSoftenerApi
from .water_heater import WaterHeaterApi
from .oim import OimApi
from .ucim import UcimApi
from .coffee_maker import CcmApi
from .dual_dishwasher import DualDishwasherApi
from .espresso_maker import EspressoMakerApi
from .dehumidifier import DehumidifierApi
_LOGGER = logging.getLogger(__name__)
def get_appliance_api_type(appliance_type: ErdApplianceType) -> Type:
"""Get the appropriate appliance type"""
_LOGGER.debug(f"Found device type: {appliance_type}")
known_types = {
ErdApplianceType.OVEN: OvenApi,
ErdApplianceType.COOKTOP: CooktopApi,
ErdApplianceType.ELECTRIC_COOKTOP: CooktopApi,
ErdApplianceType.FRIDGE: FridgeApi,
ErdApplianceType.BEVERAGE_CENTER: FridgeApi,
ErdApplianceType.DISH_WASHER: DishwasherApi,
ErdApplianceType.DUAL_DISH_WASHER: DualDishwasherApi,
ErdApplianceType.WASHER: WasherApi,
ErdApplianceType.DRYER: DryerApi,
ErdApplianceType.COMBINATION_WASHER_DRYER: WasherDryerApi,
ErdApplianceType.POE_WATER_FILTER: WaterFilterApi,
ErdApplianceType.WATER_SOFTENER: WaterSoftenerApi,
ErdApplianceType.WATER_HEATER: WaterHeaterApi,
ErdApplianceType.ADVANTIUM: AdvantiumApi,
ErdApplianceType.AIR_CONDITIONER: WacApi,
ErdApplianceType.SPLIT_AIR_CONDITIONER: SacApi,
ErdApplianceType.PORTABLE_AIR_CONDITIONER: PacApi,
ErdApplianceType.BUILT_IN_AIR_CONDITIONER: BiacApi,
ErdApplianceType.HOOD: HoodApi,
ErdApplianceType.MICROWAVE: MicrowaveApi,
ErdApplianceType.OPAL_ICE_MAKER: OimApi,
ErdApplianceType.UNDER_COUNTER_ICE_MAKER: UcimApi,
ErdApplianceType.CAFE_COFFEE_MAKER: CcmApi,
ErdApplianceType.ESPRESSO_MAKER: EspressoMakerApi,
ErdApplianceType.DEHUMIDIFIER: DehumidifierApi
}
# Get the appliance type
return known_types.get(appliance_type, ApplianceApi)

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

45
devices/advantium.py Normal file
View File

@ -0,0 +1,45 @@
import logging
from typing import List
from homeassistant.helpers.entity import Entity
from gehomesdk.erd import ErdCode, ErdApplianceType, ErdDataType
from .base import ApplianceApi
from ..entities import GeAdvantium, GeErdSensor, GeErdBinarySensor, GeErdPropertySensor, GeErdPropertyBinarySensor, UPPER_OVEN
_LOGGER = logging.getLogger(__name__)
class AdvantiumApi(ApplianceApi):
"""API class for Advantium objects"""
APPLIANCE_TYPE = ErdApplianceType.ADVANTIUM
def get_all_entities(self) -> List[Entity]:
base_entities = super().get_all_entities()
advantium_entities = [
GeErdSensor(self, ErdCode.PERSONALITY),
GeErdBinarySensor(self, ErdCode.UPPER_OVEN_REMOTE_ENABLED, self._single_name(ErdCode.UPPER_OVEN_REMOTE_ENABLED)),
GeErdBinarySensor(self, ErdCode.MICROWAVE_REMOTE_ENABLE),
GeErdSensor(self, ErdCode.UPPER_OVEN_DISPLAY_TEMPERATURE, self._single_name(ErdCode.UPPER_OVEN_DISPLAY_TEMPERATURE)),
GeErdSensor(self, ErdCode.ADVANTIUM_KITCHEN_TIME_REMAINING),
GeErdSensor(self, ErdCode.ADVANTIUM_COOK_TIME_REMAINING),
GeAdvantium(self),
#Cook Status
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", 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"),
GeErdPropertyBinarySensor(self, ErdCode.ADVANTIUM_COOK_STATUS, "cooling_fan_status", icon_on_override="mdi:fan", icon_off_override="mdi:fan-off"),
GeErdPropertyBinarySensor(self, ErdCode.ADVANTIUM_COOK_STATUS, "oven_light_status", icon_on_override="mdi:lightbulb-on", icon_off_override="mdi:lightbulb-off"),
]
entities = base_entities + advantium_entities
return entities
def _single_name(self, erd_code: ErdCode):
return erd_code.name.replace(UPPER_OVEN+"_","").replace("_", " ").title()

154
devices/base.py Normal file
View File

@ -0,0 +1,154 @@
import asyncio
import logging
from typing import Dict, List, Optional
from gehomesdk import GeAppliance
from gehomesdk.erd import ErdCode, ErdCodeType, ErdApplianceType
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
from ..const import DOMAIN
_LOGGER = logging.getLogger(__name__)
class ApplianceApi:
"""
API class to represent a single physical device.
Since a physical device can have many entities, we"ll pool common elements here
"""
APPLIANCE_TYPE = None # type: Optional[ErdApplianceType]
def __init__(self, coordinator: DataUpdateCoordinator, appliance: GeAppliance):
if not appliance.initialized:
raise RuntimeError("Appliance not ready")
self._appliance = appliance
self._loop = appliance.client.loop
self._hass = coordinator.hass
self.coordinator = coordinator
self.initial_update = False
self._entities = {} # type: Optional[Dict[str, Entity]]
@property
def hass(self) -> HomeAssistant:
return self._hass
@property
def loop(self) -> Optional[asyncio.AbstractEventLoop]:
if self._loop is None:
self._loop = self._appliance.client.loop
return self._loop
@property
def appliance(self) -> GeAppliance:
return self._appliance
@appliance.setter
def appliance(self, value: GeAppliance):
self._appliance = value
@property
def available(self) -> bool:
#Note - online will be there since we're using the GE coordinator
#Didn't want to deal with the circular references to get the type hints
#working.
return self.appliance.available and self.coordinator.online
@property
def serial_number(self) -> str:
return self.appliance.get_erd_value(ErdCode.SERIAL_NUMBER)
@property
def mac_addr(self) -> str:
return self.appliance.mac_addr
@property
def serial_or_mac(self) -> str:
def is_zero(val: str) -> bool:
try:
intVal = int(val)
return intVal == 0
except:
return False
if (self.serial_number and not
self.serial_number.isspace() and not
is_zero(self.serial_number)):
return self.serial_number
return self.mac_addr
@property
def model_number(self) -> str:
return self.appliance.get_erd_value(ErdCode.MODEL_NUMBER)
@property
def sw_version(self) -> str:
appVer = self.try_get_erd_value(ErdCode.APPLIANCE_SW_VERSION)
wifiVer = self.try_get_erd_value(ErdCode.WIFI_MODULE_SW_VERSION)
return 'Appliance=' + str(appVer or 'Unknown') + '/Wifi=' + str(wifiVer or 'Unknown')
@property
def name(self) -> str:
appliance_type = self.appliance.appliance_type
if appliance_type is None or appliance_type == ErdApplianceType.UNKNOWN:
appliance_type = "Appliance"
else:
appliance_type = appliance_type.name.replace("_", " ").title()
return f"GE {appliance_type} {self.serial_or_mac}"
@property
def device_info(self) -> Dict:
"""Device info dictionary."""
return {
"identifiers": {(DOMAIN, self.serial_or_mac)},
"name": self.name,
"manufacturer": "GE",
"model": self.model_number,
"sw_version": self.sw_version
}
@property
def entities(self) -> List[Entity]:
return list(self._entities.values())
def get_all_entities(self) -> List[Entity]:
"""Create Entities for this device."""
return self.get_base_entities()
def get_base_entities(self) -> List[Entity]:
"""Create base entities (i.e. common between all appliances)."""
from ..entities import GeErdSensor, GeErdSwitch
entities = [
GeErdSensor(self, ErdCode.CLOCK_TIME),
GeErdSwitch(self, ErdCode.SABBATH_MODE),
]
return entities
def build_entities_list(self) -> None:
"""Build the entities list, adding anything new."""
from ..entities import GeErdEntity, GeErdButton
entities = [
e for e in self.get_all_entities()
if not isinstance(e, GeErdEntity) or isinstance(e, GeErdButton) or e.erd_code in self.appliance.known_properties
]
for entity in entities:
if entity.unique_id not in self._entities:
self._entities[entity.unique_id] = entity
def try_get_erd_value(self, code: ErdCodeType):
try:
return self.appliance.get_erd_value(code)
except:
return None
def has_erd_code(self, code: ErdCodeType):
try:
self.appliance.get_erd_value(code)
return True
except:
return False

35
devices/biac.py Normal file
View File

@ -0,0 +1,35 @@
import logging
from typing import List
from homeassistant.helpers.entity import Entity
from gehomesdk.erd import ErdCode, ErdApplianceType
from .base import ApplianceApi
from ..entities import GeSacClimate, GeErdSensor, GeErdSwitch, ErdOnOffBoolConverter, GeErdBinarySensor
_LOGGER = logging.getLogger(__name__)
class BiacApi(ApplianceApi):
"""API class for Built-In AC objects"""
APPLIANCE_TYPE = ErdApplianceType.BUILT_IN_AIR_CONDITIONER
def get_all_entities(self) -> List[Entity]:
base_entities = super().get_all_entities()
sac_entities = [
GeSacClimate(self),
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"),
GeErdBinarySensor(self, ErdCode.AC_FILTER_STATUS, device_class_override="problem"),
GeErdSensor(self, ErdCode.WAC_DEMAND_RESPONSE_STATE),
GeErdSensor(self, ErdCode.WAC_DEMAND_RESPONSE_POWER, uom_override="kW"),
]
entities = base_entities + sac_entities
return entities

66
devices/coffee_maker.py Normal file
View File

@ -0,0 +1,66 @@
import logging
from typing import List
from homeassistant.helpers.entity import Entity
from gehomesdk import (
GeAppliance,
ErdCode,
ErdApplianceType,
ErdCcmBrewSettings
)
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
from .base import ApplianceApi
from ..entities import (
GeCcmPotNotPresentBinarySensor,
GeErdSensor,
GeErdBinarySensor,
GeErdButton,
GeCcmBrewStrengthSelect,
GeCcmBrewTemperatureNumber,
GeCcmBrewCupsNumber,
GeCcmBrewSettingsButton
)
_LOGGER = logging.getLogger(__name__)
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()
ccm_entities = [
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")
]
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.native_value,
self._brew_strengh_entity.brew_strength,
self._brew_temperature_entity.native_value)
await self.appliance.async_set_erd_value(ErdCode.CCM_BREW_SETTINGS, new_mode)

62
devices/cooktop.py Normal file
View File

@ -0,0 +1,62 @@
import logging
from typing import List
from gehomesdk.erd.erd_data_type import ErdDataType
from homeassistant.components.sensor import SensorDeviceClass
from homeassistant.helpers.entity import Entity
from gehomesdk import (
ErdCode,
ErdApplianceType,
ErdCooktopConfig,
CooktopStatus
)
from .base import ApplianceApi
from ..entities import (
GeErdBinarySensor,
GeErdPropertySensor,
GeErdPropertyBinarySensor
)
_LOGGER = logging.getLogger(__name__)
class CooktopApi(ApplianceApi):
"""API class for cooktop objects"""
APPLIANCE_TYPE = ErdApplianceType.COOKTOP
def get_all_entities(self) -> List[Entity]:
base_entities = super().get_all_entities()
cooktop_config = ErdCooktopConfig.NONE
if self.has_erd_code(ErdCode.COOKTOP_CONFIG):
cooktop_config: ErdCooktopConfig = self.appliance.get_erd_value(ErdCode.COOKTOP_CONFIG)
_LOGGER.debug(f"Cooktop Config: {cooktop_config}")
cooktop_entities = []
if cooktop_config == ErdCooktopConfig.PRESENT:
# attempt to get the cooktop status using legacy status
cooktop_status_erd = ErdCode.COOKTOP_STATUS
cooktop_status: CooktopStatus = self.try_get_erd_value(ErdCode.COOKTOP_STATUS)
# if we didn't get it, try using the new version
if cooktop_status is None:
cooktop_status_erd = ErdCode.COOKTOP_STATUS_EXT
cooktop_status: CooktopStatus = self.try_get_erd_value(ErdCode.COOKTOP_STATUS_EXT)
# if we got a status through either mechanism, we can add the entities
if cooktop_status is not None:
cooktop_entities.append(GeErdBinarySensor(self, cooktop_status_erd))
for (k, v) in cooktop_status.burners.items():
if v.exists:
prop = self._camel_to_snake(k)
cooktop_entities.append(GeErdPropertyBinarySensor(self, cooktop_status_erd, prop+".on"))
cooktop_entities.append(GeErdPropertyBinarySensor(self, cooktop_status_erd, prop+".synchronized"))
if not v.on_off_only:
cooktop_entities.append(GeErdPropertySensor(self, cooktop_status_erd, prop+".power_pct", icon_override="mdi:fire", device_class_override=SensorDeviceClass.POWER_FACTOR, data_type_override=ErdDataType.INT))
return base_entities + cooktop_entities
def _camel_to_snake(self, s):
return ''.join(['_'+c.lower() if c.isupper() else c for c in s]).lstrip('_')

44
devices/dehumidifier.py Normal file
View File

@ -0,0 +1,44 @@
import logging
from typing import List
from homeassistant.helpers.entity import Entity
from gehomesdk import (
ErdCode,
ErdApplianceType,
ErdOnOff
)
from .base import ApplianceApi
from ..entities import (
GeErdSensor,
GeErdSelect,
GeErdPropertySensor,
GeErdSwitch,
ErdOnOffBoolConverter,
GeDehumidifierFanSpeedSensor,
GeDehumidifier
)
_LOGGER = logging.getLogger(__name__)
class DehumidifierApi(ApplianceApi):
"""API class for Dehumidifier objects"""
APPLIANCE_TYPE = ErdApplianceType.DEHUMIDIFIER
def get_all_entities(self) -> List[Entity]:
base_entities = super().get_all_entities()
dhum_entities = [
GeErdSwitch(self, ErdCode.AC_POWER_STATUS, bool_converter=ErdOnOffBoolConverter(), icon_on_override="mdi:power-on", icon_off_override="mdi:power-off"),
GeDehumidifierFanSpeedSensor(self, ErdCode.AC_FAN_SETTING, icon_override="mdi:fan"),
GeErdSensor(self, ErdCode.DHUM_CURRENT_HUMIDITY),
GeErdSensor(self, ErdCode.DHUM_TARGET_HUMIDITY),
GeErdPropertySensor(self, ErdCode.DHUM_MAINTENANCE, "empty_bucket", device_class_override="problem"),
GeErdPropertySensor(self, ErdCode.DHUM_MAINTENANCE, "clean_filter", device_class_override="problem"),
GeDehumidifier(self)
]
entities = base_entities + dhum_entities
return entities

55
devices/dishwasher.py Normal file
View File

@ -0,0 +1,55 @@
import logging
from typing import List
from homeassistant.helpers.entity import Entity
from gehomesdk.erd import ErdCode, ErdApplianceType
from .base import ApplianceApi
from ..entities import GeErdSensor, GeErdBinarySensor, GeErdPropertySensor, GeErdNumber
_LOGGER = logging.getLogger(__name__)
class DishwasherApi(ApplianceApi):
"""API class for dishwasher objects"""
APPLIANCE_TYPE = ErdApplianceType.DISH_WASHER
def get_all_entities(self) -> List[Entity]:
base_entities = super().get_all_entities()
dishwasher_entities = [
#GeDishwasherControlLockedSwitch(self, ErdCode.USER_INTERFACE_LOCKED),
GeErdSensor(self, ErdCode.DISHWASHER_CYCLE_NAME),
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"),
GeErdNumber(self, ErdCode.DISHWASHER_PODS_REMAINING_VALUE, uom_override="pods", min_value=0, max_value=255),
GeErdPropertySensor(self, ErdCode.DISHWASHER_REMINDERS, "add_rinse_aid", icon_override="mdi:shimmer"),
GeErdPropertySensor(self, ErdCode.DISHWASHER_REMINDERS, "clean_filter", icon_override="mdi:dishwasher-alert"),
GeErdPropertySensor(self, ErdCode.DISHWASHER_REMINDERS, "sanitized", icon_override="mdi:silverware-clean"),
GeErdSensor(self, ErdCode.DISHWASHER_TIME_REMAINING),
GeErdBinarySensor(self, ErdCode.DISHWASHER_DOOR_STATUS),
GeErdBinarySensor(self, ErdCode.DISHWASHER_IS_CLEAN),
GeErdBinarySensor(self, ErdCode.DISHWASHER_REMOTE_START_ENABLE),
#User Setttings
GeErdPropertySensor(self, ErdCode.DISHWASHER_USER_SETTING, "mute", icon_override="mdi:volume-mute"),
GeErdPropertySensor(self, ErdCode.DISHWASHER_USER_SETTING, "lock_control", icon_override="mdi:lock"),
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"),
GeErdPropertySensor(self, ErdCode.DISHWASHER_USER_SETTING, "wash_temp", icon_override="mdi:coolant-temperature"),
GeErdPropertySensor(self, ErdCode.DISHWASHER_USER_SETTING, "rinse_aid", icon_override="mdi:shimmer"),
GeErdPropertySensor(self, ErdCode.DISHWASHER_USER_SETTING, "dry_option", icon_override="mdi:fan"),
GeErdPropertySensor(self, ErdCode.DISHWASHER_USER_SETTING, "wash_zone", icon_override="mdi:dock-top"),
GeErdPropertySensor(self, ErdCode.DISHWASHER_USER_SETTING, "delay_hours", icon_override="mdi:clock-fast"),
#Cycle Counts
GeErdPropertySensor(self, ErdCode.DISHWASHER_CYCLE_COUNTS, "started", icon_override="mdi:counter"),
GeErdPropertySensor(self, ErdCode.DISHWASHER_CYCLE_COUNTS, "completed", icon_override="mdi:counter"),
GeErdPropertySensor(self, ErdCode.DISHWASHER_CYCLE_COUNTS, "reset", icon_override="mdi:counter")
]
entities = base_entities + dishwasher_entities
return entities

66
devices/dryer.py Normal file
View File

@ -0,0 +1,66 @@
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
_LOGGER = logging.getLogger(__name__)
class DryerApi(ApplianceApi):
"""API class for dryer objects"""
APPLIANCE_TYPE = ErdApplianceType.DRYER
def get_all_entities(self) -> List[Entity]:
base_entities = super().get_all_entities()
common_entities = [
GeErdSensor(self, ErdCode.LAUNDRY_MACHINE_STATE, icon_override="mdi:tumble-dryer"),
GeErdSensor(self, ErdCode.LAUNDRY_CYCLE, icon_override="mdi:state-machine"),
GeErdSensor(self, ErdCode.LAUNDRY_SUB_CYCLE, icon_override="mdi:state-machine"),
GeErdBinarySensor(self, ErdCode.LAUNDRY_END_OF_CYCLE, icon_on_override="mdi:tumble-dryer", icon_off_override="mdi:tumble-dryer"),
GeErdSensor(self, ErdCode.LAUNDRY_TIME_REMAINING),
GeErdSensor(self, ErdCode.LAUNDRY_DELAY_TIME_REMAINING),
GeErdBinarySensor(self, ErdCode.LAUNDRY_DOOR),
GeErdBinarySensor(self, ErdCode.LAUNDRY_REMOTE_STATUS, icon_on_override="mdi:tumble-dryer", icon_off_override="mdi:tumble-dryer"),
GeErdBinarySensor(self, ErdCode.LAUNDRY_DRYER_BLOCKED_VENT_FAULT, icon_on_override="mid:alert-circle", icon_off_override="mdi:alert-circle"),
]
dryer_entities = self.get_dryer_entities()
entities = base_entities + common_entities + dryer_entities
return entities
def get_dryer_entities(self):
#Not all options appear to exist on every dryer... we'll look for the presence of
#a code to figure out which sensors are applicable beyond the common ones.
dryer_entities = [
]
if self.has_erd_code(ErdCode.LAUNDRY_DRYER_DRYNESS_LEVEL):
dryer_entities.extend([GeErdSensor(self, ErdCode.LAUNDRY_DRYER_DRYNESS_LEVEL)])
if self.has_erd_code(ErdCode.LAUNDRY_DRYER_DRYNESSNEW_LEVEL):
dryer_entities.extend([GeErdSensor(self, ErdCode.LAUNDRY_DRYER_DRYNESSNEW_LEVEL)])
if self.has_erd_code(ErdCode.LAUNDRY_DRYER_TEMPERATURE_OPTION):
dryer_entities.extend([GeErdSensor(self, ErdCode.LAUNDRY_DRYER_TEMPERATURE_OPTION)])
if self.has_erd_code(ErdCode.LAUNDRY_DRYER_TEMPERATURENEW_OPTION):
dryer_entities.extend([GeErdSensor(self, ErdCode.LAUNDRY_DRYER_TEMPERATURENEW_OPTION)])
if self.has_erd_code(ErdCode.LAUNDRY_DRYER_TUMBLE_STATUS):
dryer_entities.extend([GeErdSensor(self, ErdCode.LAUNDRY_DRYER_TUMBLE_STATUS)])
if self.has_erd_code(ErdCode.LAUNDRY_DRYER_EXTENDED_TUMBLE_OPTION_SELECTION):
dryer_entities.extend([GeErdSensor(self, ErdCode.LAUNDRY_DRYER_EXTENDED_TUMBLE_OPTION_SELECTION)])
if self.has_erd_code(ErdCode.LAUNDRY_DRYER_WASHERLINK_STATUS):
dryer_entities.extend([GeErdBinarySensor(self, ErdCode.LAUNDRY_DRYER_WASHERLINK_STATUS)])
if self.has_erd_code(ErdCode.LAUNDRY_DRYER_LEVEL_SENSOR_DISABLED):
dryer_entities.extend([GeErdBinarySensor(self, ErdCode.LAUNDRY_DRYER_LEVEL_SENSOR_DISABLED)])
if self.has_erd_code(ErdCode.LAUNDRY_DRYER_SHEET_USAGE_CONFIGURATION):
dryer_entities.extend([GeErdSensor(self, ErdCode.LAUNDRY_DRYER_SHEET_USAGE_CONFIGURATION)])
if self.has_erd_code(ErdCode.LAUNDRY_DRYER_SHEET_INVENTORY):
dryer_entities.extend([GeErdSensor(self, ErdCode.LAUNDRY_DRYER_SHEET_INVENTORY, icon_override="mdi:tray-full", uom_override="sheets")])
if self.has_erd_code(ErdCode.LAUNDRY_DRYER_ECODRY_OPTION_SELECTION):
dryer_entities.extend([GeErdSensor(self, ErdCode.LAUNDRY_DRYER_ECODRY_OPTION_SELECTION)])
return dryer_entities

View File

@ -0,0 +1,71 @@
import logging
from typing import List
from homeassistant.helpers.entity import Entity
from gehomesdk.erd import ErdCode, ErdApplianceType
from .base import ApplianceApi
from ..entities import GeErdSensor, GeErdBinarySensor, GeErdPropertySensor
_LOGGER = logging.getLogger(__name__)
class DualDishwasherApi(ApplianceApi):
"""API class for dual dishwasher objects"""
APPLIANCE_TYPE = ErdApplianceType.DISH_WASHER
def get_all_entities(self) -> List[Entity]:
base_entities = super().get_all_entities()
lower_entities = [
GeErdSensor(self, ErdCode.DISHWASHER_CYCLE_STATE, erd_override="lower_cycle_state", icon_override="mdi:state-machine"),
GeErdSensor(self, ErdCode.DISHWASHER_TIME_REMAINING, erd_override="lower_time_remaining"),
GeErdBinarySensor(self, ErdCode.DISHWASHER_DOOR_STATUS, erd_override="lower_door_status"),
#Reminders
GeErdPropertySensor(self, ErdCode.DISHWASHER_REMINDERS, "add_rinse_aid", erd_override="lower_reminder", icon_override="mdi:shimmer"),
GeErdPropertySensor(self, ErdCode.DISHWASHER_REMINDERS, "clean_filter", erd_override="lower_reminder", icon_override="mdi:dishwasher-alert"),
GeErdPropertySensor(self, ErdCode.DISHWASHER_REMINDERS, "sanitized", erd_override="lower_reminder", icon_override="mdi:silverware-clean"),
#User Setttings
GeErdPropertySensor(self, ErdCode.DISHWASHER_USER_SETTING, "mute", erd_override="lower_setting", icon_override="mdi:volume-mute"),
GeErdPropertySensor(self, ErdCode.DISHWASHER_USER_SETTING, "lock_control", erd_override="lower_setting", icon_override="mdi:lock"),
GeErdPropertySensor(self, ErdCode.DISHWASHER_USER_SETTING, "sabbath", erd_override="lower_setting", icon_override="mdi:star-david"),
GeErdPropertySensor(self, ErdCode.DISHWASHER_USER_SETTING, "cycle_mode", erd_override="lower_setting", icon_override="mdi:state-machine"),
GeErdPropertySensor(self, ErdCode.DISHWASHER_USER_SETTING, "presoak", erd_override="lower_setting", icon_override="mdi:water"),
GeErdPropertySensor(self, ErdCode.DISHWASHER_USER_SETTING, "bottle_jet", erd_override="lower_setting", icon_override="mdi:bottle-tonic-outline"),
GeErdPropertySensor(self, ErdCode.DISHWASHER_USER_SETTING, "wash_temp", erd_override="lower_setting", icon_override="mdi:coolant-temperature"),
GeErdPropertySensor(self, ErdCode.DISHWASHER_USER_SETTING, "rinse_aid", erd_override="lower_setting", icon_override="mdi:shimmer"),
GeErdPropertySensor(self, ErdCode.DISHWASHER_USER_SETTING, "dry_option", erd_override="lower_setting", icon_override="mdi:fan"),
GeErdPropertySensor(self, ErdCode.DISHWASHER_USER_SETTING, "wash_zone", erd_override="lower_setting", icon_override="mdi:dock-top"),
GeErdPropertySensor(self, ErdCode.DISHWASHER_USER_SETTING, "delay_hours", erd_override="lower_setting", icon_override="mdi:clock-fast")
]
upper_entities = [
#GeDishwasherControlLockedSwitch(self, ErdCode.USER_INTERFACE_LOCKED),
GeErdSensor(self, ErdCode.DISHWASHER_UPPER_CYCLE_STATE, erd_override="upper_cycle_state", icon_override="mdi:state-machine"),
GeErdSensor(self, ErdCode.DISHWASHER_UPPER_TIME_REMAINING, erd_override="upper_time_remaining"),
GeErdBinarySensor(self, ErdCode.DISHWASHER_UPPER_DOOR_STATUS, erd_override="upper_door_status"),
#Reminders
GeErdPropertySensor(self, ErdCode.DISHWASHER_UPPER_REMINDERS, "add_rinse_aid", erd_override="upper_reminder", icon_override="mdi:shimmer"),
GeErdPropertySensor(self, ErdCode.DISHWASHER_UPPER_REMINDERS, "clean_filter", erd_override="upper_reminder", icon_override="mdi:dishwasher-alert"),
GeErdPropertySensor(self, ErdCode.DISHWASHER_UPPER_REMINDERS, "sanitized", erd_override="upper_reminder", icon_override="mdi:silverware-clean"),
#User Setttings
GeErdPropertySensor(self, ErdCode.DISHWASHER_UPPER_USER_SETTING, "mute", erd_override="upper_setting", icon_override="mdi:volume-mute"),
GeErdPropertySensor(self, ErdCode.DISHWASHER_UPPER_USER_SETTING, "lock_control", erd_override="upper_setting", icon_override="mdi:lock"),
GeErdPropertySensor(self, ErdCode.DISHWASHER_UPPER_USER_SETTING, "sabbath", erd_override="upper_setting", icon_override="mdi:star-david"),
GeErdPropertySensor(self, ErdCode.DISHWASHER_UPPER_USER_SETTING, "cycle_mode", erd_override="upper_setting", icon_override="mdi:state-machine"),
GeErdPropertySensor(self, ErdCode.DISHWASHER_UPPER_USER_SETTING, "presoak", erd_override="upper_setting", icon_override="mdi:water"),
GeErdPropertySensor(self, ErdCode.DISHWASHER_UPPER_USER_SETTING, "bottle_jet", erd_override="upper_setting", icon_override="mdi:bottle-tonic-outline"),
GeErdPropertySensor(self, ErdCode.DISHWASHER_UPPER_USER_SETTING, "wash_temp", erd_override="upper_setting", icon_override="mdi:coolant-temperature"),
GeErdPropertySensor(self, ErdCode.DISHWASHER_UPPER_USER_SETTING, "rinse_aid", erd_override="upper_setting", icon_override="mdi:shimmer"),
GeErdPropertySensor(self, ErdCode.DISHWASHER_UPPER_USER_SETTING, "dry_option", erd_override="upper_setting", icon_override="mdi:fan"),
GeErdPropertySensor(self, ErdCode.DISHWASHER_UPPER_USER_SETTING, "wash_zone", erd_override="upper_setting", icon_override="mdi:dock-top"),
GeErdPropertySensor(self, ErdCode.DISHWASHER_UPPER_USER_SETTING, "delay_hours", erd_override="upper_setting", icon_override="mdi:clock-fast")
]
entities = base_entities + lower_entities + upper_entities
return entities

34
devices/espresso_maker.py Normal file
View File

@ -0,0 +1,34 @@
import logging
from typing import List
from homeassistant.helpers.entity import Entity
from gehomesdk import (
ErdCode,
ErdApplianceType
)
from .base import ApplianceApi
from ..entities import (
GeErdBinarySensor,
GeErdButton
)
_LOGGER = logging.getLogger(__name__)
class EspressoMakerApi(ApplianceApi):
"""API class for Espresso Maker objects"""
APPLIANCE_TYPE = ErdApplianceType.ESPRESSO_MAKER
def get_all_entities(self) -> List[Entity]:
base_entities = super().get_all_entities()
em_entities = [
GeErdBinarySensor(self, ErdCode.CCM_IS_DESCALING),
GeErdButton(self, ErdCode.CCM_CANCEL_DESCALING),
GeErdButton(self, ErdCode.CCM_START_DESCALING),
GeErdBinarySensor(self, ErdCode.CCM_OUT_OF_WATER, device_class_override="problem"),
]
entities = base_entities + em_entities
return entities

138
devices/fridge.py Normal file
View File

@ -0,0 +1,138 @@
from homeassistant.components.binary_sensor import BinarySensorDeviceClass
from homeassistant.components.sensor import SensorDeviceClass
import logging
from typing import List
from homeassistant.helpers.entity import Entity
from gehomesdk import (
ErdCode,
ErdApplianceType,
ErdOnOff,
ErdHotWaterStatus,
FridgeIceBucketStatus,
IceMakerControlStatus,
ErdFilterStatus,
HotWaterStatus,
FridgeModelInfo,
ErdConvertableDrawerMode,
ErdDataType
)
from .base import ApplianceApi
from ..entities import (
ErdOnOffBoolConverter,
GeErdSensor,
GeErdBinarySensor,
GeErdSwitch,
GeErdSelect,
GeErdLight,
GeFridge,
GeFreezer,
GeDispenser,
GeErdPropertySensor,
GeErdPropertyBinarySensor,
ConvertableDrawerModeOptionsConverter,
GeFridgeIceControlSwitch
)
_LOGGER = logging.getLogger(__name__)
class FridgeApi(ApplianceApi):
"""API class for fridge objects"""
APPLIANCE_TYPE = ErdApplianceType.FRIDGE
def get_all_entities(self) -> List[Entity]:
base_entities = super().get_all_entities()
fridge_entities = []
freezer_entities = []
dispenser_entities = []
# Get the statuses used to determine presence
ice_maker_control: IceMakerControlStatus = self.try_get_erd_value(ErdCode.ICE_MAKER_CONTROL)
ice_bucket_status: FridgeIceBucketStatus = self.try_get_erd_value(ErdCode.ICE_MAKER_BUCKET_STATUS)
water_filter: ErdFilterStatus = self.try_get_erd_value(ErdCode.WATER_FILTER_STATUS)
air_filter: ErdFilterStatus = self.try_get_erd_value(ErdCode.AIR_FILTER_STATUS)
hot_water_status: HotWaterStatus = self.try_get_erd_value(ErdCode.HOT_WATER_STATUS)
fridge_model_info: FridgeModelInfo = self.try_get_erd_value(ErdCode.FRIDGE_MODEL_INFO)
convertable_drawer: ErdConvertableDrawerMode = self.try_get_erd_value(ErdCode.CONVERTABLE_DRAWER_MODE)
interior_light: int = self.try_get_erd_value(ErdCode.INTERIOR_LIGHT)
proximity_light: ErdOnOff = self.try_get_erd_value(ErdCode.PROXIMITY_LIGHT)
display_mode: ErdOnOff = self.try_get_erd_value(ErdCode.DISPLAY_MODE)
lockout_mode: ErdOnOff = self.try_get_erd_value(ErdCode.LOCKOUT_MODE)
turbo_cool: ErdOnOff = self.try_get_erd_value(ErdCode.TURBO_COOL_STATUS)
turbo_freeze: ErdOnOff = self.try_get_erd_value(ErdCode.TURBO_FREEZE_STATUS)
ice_boost: ErdOnOff = self.try_get_erd_value(ErdCode.FRIDGE_ICE_BOOST)
units = self.hass.config.units
# Common entities
common_entities = [
GeErdSensor(self, ErdCode.FRIDGE_MODEL_INFO),
GeErdSwitch(self, ErdCode.SABBATH_MODE),
GeErdSensor(self, ErdCode.DOOR_STATUS),
GeErdPropertyBinarySensor(self, ErdCode.DOOR_STATUS, "any_open")
]
if(ice_bucket_status and (ice_bucket_status.is_present_fridge or ice_bucket_status.is_present_freezer)):
common_entities.append(GeErdSensor(self, ErdCode.ICE_MAKER_BUCKET_STATUS))
# Fridge entities
if fridge_model_info is None or fridge_model_info.has_fridge:
fridge_entities.extend([
GeErdPropertySensor(self, ErdCode.CURRENT_TEMPERATURE, "fridge"),
GeFridge(self),
])
if turbo_cool is not None:
fridge_entities.append(GeErdSwitch(self, ErdCode.FRIDGE_ICE_BOOST))
if(ice_maker_control and ice_maker_control.status_fridge != ErdOnOff.NA):
fridge_entities.append(GeErdPropertyBinarySensor(self, ErdCode.ICE_MAKER_CONTROL, "status_fridge"))
fridge_entities.append(GeFridgeIceControlSwitch(self, "fridge"))
if(water_filter and water_filter != ErdFilterStatus.NA):
fridge_entities.append(GeErdSensor(self, ErdCode.WATER_FILTER_STATUS))
if(air_filter and air_filter != ErdFilterStatus.NA):
fridge_entities.append(GeErdSensor(self, ErdCode.AIR_FILTER_STATUS))
if(ice_bucket_status and ice_bucket_status.is_present_fridge):
fridge_entities.append(GeErdPropertySensor(self, ErdCode.ICE_MAKER_BUCKET_STATUS, "state_full_fridge"))
if(interior_light and interior_light != 255):
fridge_entities.append(GeErdLight(self, ErdCode.INTERIOR_LIGHT))
if(proximity_light and proximity_light != ErdOnOff.NA):
fridge_entities.append(GeErdSwitch(self, ErdCode.PROXIMITY_LIGHT, ErdOnOffBoolConverter(), icon_on_override="mdi:lightbulb-on", icon_off_override="mdi:lightbulb"))
if(convertable_drawer and convertable_drawer != ErdConvertableDrawerMode.NA):
fridge_entities.append(GeErdSelect(self, ErdCode.CONVERTABLE_DRAWER_MODE, ConvertableDrawerModeOptionsConverter(units)))
if(display_mode and display_mode != ErdOnOff.NA):
fridge_entities.append(GeErdSwitch(self, ErdCode.DISPLAY_MODE, ErdOnOffBoolConverter(), icon_on_override="mdi:lightbulb-on", icon_off_override="mdi:lightbulb"))
if(lockout_mode and lockout_mode != ErdOnOff.NA):
fridge_entities.append(GeErdSwitch(self, ErdCode.LOCKOUT_MODE, ErdOnOffBoolConverter(), icon_on_override="mdi:lock", icon_off_override="mdi:lock-open"))
# Freezer entities
if fridge_model_info is None or fridge_model_info.has_freezer:
freezer_entities.extend([
GeErdPropertySensor(self, ErdCode.CURRENT_TEMPERATURE, "freezer"),
GeFreezer(self),
])
if turbo_freeze is not None:
freezer_entities.append(GeErdSwitch(self, ErdCode.TURBO_FREEZE_STATUS))
if ice_boost is not None:
freezer_entities.append(GeErdSwitch(self, ErdCode.FRIDGE_ICE_BOOST))
if(ice_maker_control and ice_maker_control.status_freezer != ErdOnOff.NA):
freezer_entities.append(GeErdPropertyBinarySensor(self, ErdCode.ICE_MAKER_CONTROL, "status_freezer"))
freezer_entities.append(GeFridgeIceControlSwitch(self, "freezer"))
if(ice_bucket_status and ice_bucket_status.is_present_freezer):
freezer_entities.append(GeErdPropertySensor(self, ErdCode.ICE_MAKER_BUCKET_STATUS, "state_full_freezer"))
# Dispenser entities
if(hot_water_status and hot_water_status.status != ErdHotWaterStatus.NA):
dispenser_entities.extend([
GeErdBinarySensor(self, ErdCode.HOT_WATER_IN_USE),
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=SensorDeviceClass.TEMPERATURE, data_type_override=ErdDataType.INT),
GeErdPropertyBinarySensor(self, ErdCode.HOT_WATER_STATUS, "faulted", device_class_override=BinarySensorDeviceClass.PROBLEM),
GeDispenser(self)
])
entities = base_entities + common_entities + fridge_entities + freezer_entities + dispenser_entities
return entities

52
devices/hood.py Normal file
View File

@ -0,0 +1,52 @@
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,
GeErdTimerSensor,
GeErdSwitch,
ErdOnOffBoolConverter
)
_LOGGER = logging.getLogger(__name__)
class HoodApi(ApplianceApi):
"""API class for Oven Hood objects"""
APPLIANCE_TYPE = ErdApplianceType.HOOD
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)
timer_availability: ErdOnOff = self.try_get_erd_value(ErdCode.HOOD_TIMER_AVAILABILITY)
hood_entities = [
#looks like this is always available?
GeErdSwitch(self, ErdCode.HOOD_DELAY_OFF, bool_converter=ErdOnOffBoolConverter(), icon_on_override="mdi:power-on", icon_off_override="mdi:power-off"),
]
if fan_availability and fan_availability.is_available:
hood_entities.append(GeHoodFanSpeedSelect(self, ErdCode.HOOD_FAN_SPEED))
#for now, represent as a select
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(GeErdTimerSensor(self, ErdCode.HOOD_TIMER))
entities = base_entities + hood_entities
return entities

56
devices/microwave.py Normal file
View File

@ -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

41
devices/oim.py Normal file
View File

@ -0,0 +1,41 @@
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,
GeErdBinarySensor,
GeErdSelect,
GeErdSwitch,
ErdOnOffBoolConverter
)
_LOGGER = logging.getLogger(__name__)
class OimApi(ApplianceApi):
"""API class for Opal Ice Maker objects"""
APPLIANCE_TYPE = ErdApplianceType.OPAL_ICE_MAKER
def get_all_entities(self) -> List[Entity]:
base_entities = super().get_all_entities()
oim_entities = [
GeErdSensor(self, ErdCode.OIM_STATUS),
GeErdBinarySensor(self, ErdCode.OIM_FILTER_STATUS, device_class_override="problem"),
GeErdBinarySensor(self, ErdCode.OIM_NEEDS_DESCALING, 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"),
]
entities = base_entities + oim_entities
return entities

146
devices/oven.py Normal file
View File

@ -0,0 +1,146 @@
import logging
from typing import List
from gehomesdk.erd.erd_data_type import ErdDataType
from homeassistant.components.sensor import SensorDeviceClass
from homeassistant.helpers.entity import Entity
from gehomesdk import (
ErdCode,
ErdApplianceType,
OvenConfiguration,
ErdCooktopConfig,
CooktopStatus,
ErdOvenLightLevel,
ErdOvenLightLevelAvailability,
ErdOvenWarmingState
)
from .base import ApplianceApi
from ..entities import (
GeErdSensor,
GeErdTimerSensor,
GeErdBinarySensor,
GeErdPropertySensor,
GeErdPropertyBinarySensor,
GeOven,
GeOvenLightLevelSelect,
GeOvenWarmingStateSelect,
UPPER_OVEN,
LOWER_OVEN
)
_LOGGER = logging.getLogger(__name__)
class OvenApi(ApplianceApi):
"""API class for oven objects"""
APPLIANCE_TYPE = ErdApplianceType.OVEN
def get_all_entities(self) -> List[Entity]:
base_entities = super().get_all_entities()
oven_config: OvenConfiguration = self.appliance.get_erd_value(ErdCode.OVEN_CONFIGURATION)
cooktop_config = ErdCooktopConfig.NONE
if self.has_erd_code(ErdCode.COOKTOP_CONFIG):
cooktop_config: ErdCooktopConfig = self.appliance.get_erd_value(ErdCode.COOKTOP_CONFIG)
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)
has_upper_probe_temperature = self.has_erd_code(ErdCode.UPPER_OVEN_PROBE_DISPLAY_TEMP)
has_lower_probe_temperature = self.has_erd_code(ErdCode.LOWER_OVEN_PROBE_DISPLAY_TEMP)
upper_light : ErdOvenLightLevel = self.try_get_erd_value(ErdCode.UPPER_OVEN_LIGHT)
upper_light_availability: ErdOvenLightLevelAvailability = self.try_get_erd_value(ErdCode.UPPER_OVEN_LIGHT_AVAILABILITY)
lower_light : ErdOvenLightLevel = self.try_get_erd_value(ErdCode.LOWER_OVEN_LIGHT)
lower_light_availability: ErdOvenLightLevelAvailability = self.try_get_erd_value(ErdCode.LOWER_OVEN_LIGHT_AVAILABILITY)
upper_warm_drawer : ErdOvenWarmingState = self.try_get_erd_value(ErdCode.UPPER_OVEN_WARMING_DRAWER_STATE)
lower_warm_drawer : ErdOvenWarmingState = self.try_get_erd_value(ErdCode.LOWER_OVEN_WARMING_DRAWER_STATE)
warm_drawer : ErdOvenWarmingState = self.try_get_erd_value(ErdCode.WARMING_DRAWER_STATE)
_LOGGER.debug(f"Oven Config: {oven_config}")
_LOGGER.debug(f"Cooktop Config: {cooktop_config}")
oven_entities = []
cooktop_entities = []
if oven_config.has_lower_oven:
oven_entities.extend([
GeErdSensor(self, ErdCode.LOWER_OVEN_COOK_MODE),
GeErdSensor(self, ErdCode.LOWER_OVEN_CURRENT_STATE),
GeErdSensor(self, ErdCode.LOWER_OVEN_COOK_TIME_REMAINING),
GeErdTimerSensor(self, ErdCode.LOWER_OVEN_KITCHEN_TIMER),
GeErdSensor(self, ErdCode.LOWER_OVEN_USER_TEMP_OFFSET),
GeErdSensor(self, ErdCode.LOWER_OVEN_DISPLAY_TEMPERATURE),
GeErdBinarySensor(self, ErdCode.LOWER_OVEN_REMOTE_ENABLED),
GeOven(self, LOWER_OVEN, True, self._temperature_code(has_lower_raw_temperature))
])
if has_lower_raw_temperature:
oven_entities.append(GeErdSensor(self, ErdCode.LOWER_OVEN_RAW_TEMPERATURE))
if lower_light_availability is None or lower_light_availability.is_available or lower_light is not None:
oven_entities.append(GeOvenLightLevelSelect(self, ErdCode.LOWER_OVEN_LIGHT))
if lower_warm_drawer is not None:
oven_entities.append(GeOvenWarmingStateSelect(self, ErdCode.LOWER_OVEN_WARMING_DRAWER_STATE))
if has_lower_probe_temperature:
oven_entities.append(GeErdSensor(self, ErdCode.LOWER_OVEN_PROBE_DISPLAY_TEMP))
oven_entities.extend([
GeErdSensor(self, ErdCode.UPPER_OVEN_COOK_MODE, self._single_name(ErdCode.UPPER_OVEN_COOK_MODE, ~oven_config.has_lower_oven)),
GeErdSensor(self, ErdCode.UPPER_OVEN_CURRENT_STATE, self._single_name(ErdCode.UPPER_OVEN_CURRENT_STATE, ~oven_config.has_lower_oven)),
GeErdSensor(self, ErdCode.UPPER_OVEN_COOK_TIME_REMAINING, self._single_name(ErdCode.UPPER_OVEN_COOK_TIME_REMAINING, ~oven_config.has_lower_oven)),
GeErdTimerSensor(self, ErdCode.UPPER_OVEN_KITCHEN_TIMER, self._single_name(ErdCode.UPPER_OVEN_KITCHEN_TIMER, ~oven_config.has_lower_oven)),
GeErdSensor(self, ErdCode.UPPER_OVEN_USER_TEMP_OFFSET, self._single_name(ErdCode.UPPER_OVEN_USER_TEMP_OFFSET, ~oven_config.has_lower_oven)),
GeErdSensor(self, ErdCode.UPPER_OVEN_DISPLAY_TEMPERATURE, self._single_name(ErdCode.UPPER_OVEN_DISPLAY_TEMPERATURE, ~oven_config.has_lower_oven)),
GeErdBinarySensor(self, ErdCode.UPPER_OVEN_REMOTE_ENABLED, self._single_name(ErdCode.UPPER_OVEN_REMOTE_ENABLED, ~oven_config.has_lower_oven)),
GeOven(self, UPPER_OVEN, False, self._temperature_code(has_upper_raw_temperature))
])
if has_upper_raw_temperature:
oven_entities.append(GeErdSensor(self, ErdCode.UPPER_OVEN_RAW_TEMPERATURE, self._single_name(ErdCode.UPPER_OVEN_RAW_TEMPERATURE, ~oven_config.has_lower_oven)))
if upper_light_availability is None or upper_light_availability.is_available or upper_light is not None:
oven_entities.append(GeOvenLightLevelSelect(self, ErdCode.UPPER_OVEN_LIGHT, self._single_name(ErdCode.UPPER_OVEN_LIGHT, ~oven_config.has_lower_oven)))
if upper_warm_drawer is not None:
oven_entities.append(GeOvenWarmingStateSelect(self, ErdCode.UPPER_OVEN_WARMING_DRAWER_STATE, self._single_name(ErdCode.UPPER_OVEN_WARMING_DRAWER_STATE, ~oven_config.has_lower_oven)))
if has_upper_probe_temperature:
oven_entities.append(GeErdSensor(self, ErdCode.UPPER_OVEN_PROBE_DISPLAY_TEMP, self._single_name(ErdCode.UPPER_OVEN_PROBE_DISPLAY_TEMP, ~oven_config.has_lower_oven)))
if oven_config.has_warming_drawer and warm_drawer is not None:
oven_entities.append(GeErdSensor(self, ErdCode.WARMING_DRAWER_STATE))
if cooktop_config == ErdCooktopConfig.PRESENT:
# attempt to get the cooktop status using legacy status
cooktop_status_erd = ErdCode.COOKTOP_STATUS
cooktop_status: CooktopStatus = self.try_get_erd_value(ErdCode.COOKTOP_STATUS)
# if we didn't get it, try using the new version
if cooktop_status is None:
cooktop_status_erd = ErdCode.COOKTOP_STATUS_EXT
cooktop_status: CooktopStatus = self.try_get_erd_value(ErdCode.COOKTOP_STATUS_EXT)
# if we got a status through either mechanism, we can add the entities
if cooktop_status is not None:
cooktop_entities.append(GeErdBinarySensor(self, cooktop_status_erd))
for (k, v) in cooktop_status.burners.items():
if v.exists:
prop = self._camel_to_snake(k)
cooktop_entities.append(GeErdPropertyBinarySensor(self, cooktop_status_erd, prop+".on"))
cooktop_entities.append(GeErdPropertyBinarySensor(self, cooktop_status_erd, prop+".synchronized"))
if not v.on_off_only:
cooktop_entities.append(GeErdPropertySensor(self, cooktop_status_erd, prop+".power_pct", icon_override="mdi:fire", device_class_override=SensorDeviceClass.POWER_FACTOR, data_type_override=ErdDataType.INT))
return base_entities + oven_entities + cooktop_entities
def _single_name(self, erd_code: ErdCode, make_single: bool):
name = erd_code.name
if make_single:
name = name.replace(UPPER_OVEN+"_","")
return name.replace("_", " ").title()
def _camel_to_snake(self, s):
return ''.join(['_'+c.lower() if c.isupper() else c for c in s]).lstrip('_')
def _temperature_code(self, has_raw: bool):
return "RAW_TEMPERATURE" if has_raw else "DISPLAY_TEMPERATURE"

31
devices/pac.py Normal file
View File

@ -0,0 +1,31 @@
import logging
from typing import List
from homeassistant.helpers.entity import Entity
from gehomesdk.erd import ErdCode, ErdApplianceType
from .base import ApplianceApi
from ..entities import GePacClimate, GeErdSensor, GeErdSwitch, ErdOnOffBoolConverter
_LOGGER = logging.getLogger(__name__)
class PacApi(ApplianceApi):
"""API class for Portable AC objects"""
APPLIANCE_TYPE = ErdApplianceType.PORTABLE_AIR_CONDITIONER
def get_all_entities(self) -> List[Entity]:
base_entities = super().get_all_entities()
pac_entities = [
GePacClimate(self),
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"),
]
entities = base_entities + pac_entities
return entities

37
devices/sac.py Normal file
View File

@ -0,0 +1,37 @@
import logging
from typing import List
from homeassistant.helpers.entity import Entity
from gehomesdk.erd import ErdCode, ErdApplianceType
from .base import ApplianceApi
from ..entities import GeSacClimate, GeErdSensor, GeErdSwitch, ErdOnOffBoolConverter
_LOGGER = logging.getLogger(__name__)
class SacApi(ApplianceApi):
"""API class for Split AC objects"""
APPLIANCE_TYPE = ErdApplianceType.SPLIT_AIR_CONDITIONER
def get_all_entities(self) -> List[Entity]:
base_entities = super().get_all_entities()
sac_entities = [
GeSacClimate(self),
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"),
]
if self.has_erd_code(ErdCode.SAC_SLEEP_MODE):
sac_entities.append(GeErdSwitch(self, ErdCode.SAC_SLEEP_MODE, bool_converter=ErdOnOffBoolConverter(), icon_on_override="mdi:sleep", icon_off_override="mdi:sleep-off"))
if self.has_erd_code(ErdCode.SAC_AUTO_SWING_MODE):
sac_entities.append(GeErdSwitch(self, ErdCode.SAC_AUTO_SWING_MODE, bool_converter=ErdOnOffBoolConverter(), icon_on_override="mdi:arrow-decision-auto", icon_off_override="mdi:arrow-decision-auto-outline"))
entities = base_entities + sac_entities
return entities

45
devices/ucim.py Normal file
View File

@ -0,0 +1,45 @@
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,
GeErdBinarySensor,
GeErdSelect,
GeErdSwitch,
ErdOnOffBoolConverter
)
_LOGGER = logging.getLogger(__name__)
class UcimApi(ApplianceApi):
"""API class for Opal Ice Maker objects"""
APPLIANCE_TYPE = ErdApplianceType.OPAL_ICE_MAKER
def get_all_entities(self) -> List[Entity]:
base_entities = super().get_all_entities()
oim_entities = [
GeErdSensor(self, ErdCode.OIM_STATUS),
GeErdBinarySensor(self, ErdCode.OIM_FILTER_STATUS, device_class_override="problem"),
GeErdBinarySensor(self, ErdCode.OIM_NEEDS_DESCALING, 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"),
GeErdSensor(self, ErdCode.OIM_PRODUCTION),
GeErdSensor(self, ErdCode.UCIM_CLEAN_STATUS),
GeErdSensor(self, ErdCode.UCIM_FILTER_PERCENTAGE_USED),
GeErdBinarySensor(self, ErdCode.UCIM_BIN_FULL, device_class_override="problem"),
]
entities = base_entities + oim_entities
return entities

33
devices/wac.py Normal file
View File

@ -0,0 +1,33 @@
import logging
from typing import List
from homeassistant.helpers.entity import Entity
from gehomesdk.erd import ErdCode, ErdApplianceType
from .base import ApplianceApi
from ..entities import GeWacClimate, GeErdSensor, GeErdBinarySensor, GeErdSwitch, ErdOnOffBoolConverter
_LOGGER = logging.getLogger(__name__)
class WacApi(ApplianceApi):
"""API class for Window AC objects"""
APPLIANCE_TYPE = ErdApplianceType.AIR_CONDITIONER
def get_all_entities(self) -> List[Entity]:
base_entities = super().get_all_entities()
wac_entities = [
GeWacClimate(self),
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"),
GeErdBinarySensor(self, ErdCode.AC_FILTER_STATUS, device_class_override="problem"),
GeErdSensor(self, ErdCode.WAC_DEMAND_RESPONSE_STATE),
GeErdSensor(self, ErdCode.WAC_DEMAND_RESPONSE_POWER, uom_override="kW"),
]
entities = base_entities + wac_entities
return entities

63
devices/washer.py Normal file
View File

@ -0,0 +1,63 @@
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, GeErdPropertySensor
_LOGGER = logging.getLogger(__name__)
class WasherApi(ApplianceApi):
"""API class for washer objects"""
APPLIANCE_TYPE = ErdApplianceType.WASHER
def get_all_entities(self) -> List[Entity]:
base_entities = super().get_all_entities()
common_entities = [
GeErdSensor(self, ErdCode.LAUNDRY_MACHINE_STATE),
GeErdSensor(self, ErdCode.LAUNDRY_CYCLE, icon_override="mdi:state-machine"),
GeErdSensor(self, ErdCode.LAUNDRY_SUB_CYCLE, icon_override="mdi:state-machine"),
GeErdBinarySensor(self, ErdCode.LAUNDRY_END_OF_CYCLE),
GeErdSensor(self, ErdCode.LAUNDRY_TIME_REMAINING),
GeErdSensor(self, ErdCode.LAUNDRY_DELAY_TIME_REMAINING),
GeErdBinarySensor(self, ErdCode.LAUNDRY_DOOR),
GeErdBinarySensor(self, ErdCode.LAUNDRY_REMOTE_STATUS),
]
washer_entities = self.get_washer_entities()
entities = base_entities + common_entities + washer_entities
return entities
def get_washer_entities(self) -> List[Entity]:
washer_entities = [
GeErdSensor(self, ErdCode.LAUNDRY_WASHER_SOIL_LEVEL, icon_override="mdi:emoticon-poop"),
GeErdSensor(self, ErdCode.LAUNDRY_WASHER_WASHTEMP_LEVEL),
GeErdSensor(self, ErdCode.LAUNDRY_WASHER_SPINTIME_LEVEL, icon_override="mdi:speedometer"),
GeErdSensor(self, ErdCode.LAUNDRY_WASHER_RINSE_OPTION, icon_override="mdi:shimmer"),
]
if self.has_erd_code(ErdCode.LAUNDRY_WASHER_DOOR_LOCK):
washer_entities.extend([GeErdBinarySensor(self, ErdCode.LAUNDRY_WASHER_DOOR_LOCK)])
if self.has_erd_code(ErdCode.LAUNDRY_WASHER_TANK_STATUS):
washer_entities.extend([GeErdSensor(self, ErdCode.LAUNDRY_WASHER_TANK_STATUS)])
if self.has_erd_code(ErdCode.LAUNDRY_WASHER_TANK_SELECTED):
washer_entities.extend([GeErdSensor(self, ErdCode.LAUNDRY_WASHER_TANK_SELECTED)])
if self.has_erd_code(ErdCode.LAUNDRY_WASHER_TIMESAVER):
washer_entities.extend([GeErdBinarySensor(self, ErdCode.LAUNDRY_WASHER_TIMESAVER, icon_on_override="mdi:sort-clock-ascending", icon_off_override="mdi:sort-clock-ascending-outline")])
if self.has_erd_code(ErdCode.LAUNDRY_WASHER_POWERSTEAM):
washer_entities.extend([GeErdBinarySensor(self, ErdCode.LAUNDRY_WASHER_POWERSTEAM, icon_on_override="mdi:kettle-steam", icon_off_override="mdi:kettle-steam-outline")])
if self.has_erd_code(ErdCode.LAUNDRY_WASHER_PREWASH):
washer_entities.extend([GeErdBinarySensor(self, ErdCode.LAUNDRY_WASHER_PREWASH, icon_on_override="mdi:water-plus", icon_off_override="mdi:water-remove-outline")])
if self.has_erd_code(ErdCode.LAUNDRY_WASHER_TUMBLECARE):
washer_entities.extend([GeErdBinarySensor(self, ErdCode.LAUNDRY_WASHER_TUMBLECARE)])
if self.has_erd_code(ErdCode.LAUNDRY_WASHER_SMART_DISPENSE):
washer_entities.extend([GeErdPropertySensor(self, ErdCode.LAUNDRY_WASHER_SMART_DISPENSE, "loads_left", uom_override="loads")])
if self.has_erd_code(ErdCode.LAUNDRY_WASHER_SMART_DISPENSE_TANK_STATUS):
washer_entities.extend([GeErdSensor(self, ErdCode.LAUNDRY_WASHER_SMART_DISPENSE_TANK_STATUS)])
return washer_entities

36
devices/washer_dryer.py Normal file
View File

@ -0,0 +1,36 @@
import logging
from typing import List
from homeassistant.helpers.entity import Entity
from gehomesdk import ErdCode, ErdApplianceType
from .washer import WasherApi
from .dryer import DryerApi
from ..entities import GeErdSensor, GeErdBinarySensor
_LOGGER = logging.getLogger(__name__)
class WasherDryerApi(WasherApi, DryerApi):
"""API class for washer/dryer objects"""
APPLIANCE_TYPE = ErdApplianceType.COMBINATION_WASHER_DRYER
def get_all_entities(self) -> List[Entity]:
base_entities = self.get_base_entities()
common_entities = [
GeErdSensor(self, ErdCode.LAUNDRY_MACHINE_STATE),
GeErdSensor(self, ErdCode.LAUNDRY_CYCLE),
GeErdSensor(self, ErdCode.LAUNDRY_SUB_CYCLE),
GeErdBinarySensor(self, ErdCode.LAUNDRY_END_OF_CYCLE),
GeErdSensor(self, ErdCode.LAUNDRY_TIME_REMAINING),
GeErdSensor(self, ErdCode.LAUNDRY_DELAY_TIME_REMAINING),
GeErdBinarySensor(self, ErdCode.LAUNDRY_DOOR),
GeErdBinarySensor(self, ErdCode.LAUNDRY_REMOTE_STATUS),
]
washer_entities = self.get_washer_entities()
dryer_entities = self.get_dryer_entities()
entities = base_entities + common_entities + washer_entities + dryer_entities
return entities

38
devices/water_filter.py Normal file
View File

@ -0,0 +1,38 @@
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,
GeErdPropertySensor,
GeErdBinarySensor,
GeErdFilterPositionSelect,
)
_LOGGER = logging.getLogger(__name__)
class WaterFilterApi(ApplianceApi):
"""API class for water filter objects"""
APPLIANCE_TYPE = ErdApplianceType.POE_WATER_FILTER
def get_all_entities(self) -> List[Entity]:
base_entities = super().get_all_entities()
wf_entities = [
GeErdSensor(self, ErdCode.WH_FILTER_MODE),
GeErdSensor(self, ErdCode.WH_FILTER_VALVE_STATE, icon_override="mdi:state-machine"),
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"),
GeErdPropertySensor(self, ErdCode.WH_FILTER_FLOW_RATE, "flow_rate"),
GeErdSensor(self, ErdCode.WH_FILTER_DAY_USAGE, device_class_override="water"),
GeErdPropertySensor(self, ErdCode.WH_FILTER_LIFE_REMAINING, "life_remaining"),
GeErdBinarySensor(self, ErdCode.WH_FILTER_FLOW_ALERT, device_class_override="moisture"),
]
entities = base_entities + wf_entities
return entities

54
devices/water_heater.py Normal file
View File

@ -0,0 +1,54 @@
import logging
from typing import List
from homeassistant.helpers.entity import Entity
from gehomesdk import (
ErdCode,
ErdApplianceType,
ErdOnOff
)
from custom_components.ge_home.entities.water_heater.ge_water_heater import GeWaterHeater
from .base import ApplianceApi
from ..entities import (
GeErdSensor,
GeErdBinarySensor,
GeErdSelect,
GeErdSwitch,
ErdOnOffBoolConverter
)
_LOGGER = logging.getLogger(__name__)
class WaterHeaterApi(ApplianceApi):
"""API class for Water Heater objects"""
APPLIANCE_TYPE = ErdApplianceType.WATER_HEATER
def get_all_entities(self) -> List[Entity]:
base_entities = super().get_all_entities()
boost_mode: ErdOnOff = self.try_get_erd_value(ErdCode.WH_HEATER_BOOST_STATE)
active: ErdOnOff = self.try_get_erd_value(ErdCode.WH_HEATER_ACTIVE_STATE)
wh_entities = [
GeErdSensor(self, ErdCode.WH_HEATER_TARGET_TEMPERATURE),
GeErdSensor(self, ErdCode.WH_HEATER_TEMPERATURE),
GeErdSensor(self, ErdCode.WH_HEATER_MODE_HOURS_REMAINING),
GeErdSensor(self, ErdCode.WH_HEATER_ELECTRIC_MODE_MAX_TIME),
GeErdSensor(self, ErdCode.WH_HEATER_VACATION_MODE_MAX_TIME),
GeWaterHeater(self)
]
if(boost_mode and boost_mode != ErdOnOff.NA):
wh_entities.append(GeErdSensor(self, ErdCode.WH_HEATER_BOOST_STATE))
wh_entities.append(GeErdSwitch(self, ErdCode.WH_HEATER_BOOST_CONTROL, ErdOnOffBoolConverter(), icon_on_override="mdi:rocket-launch", icon_off_override="mdi:rocket-launch-outline"))
if(active and active != ErdOnOff.NA):
wh_entities.append(GeErdSensor(self, ErdCode.WH_HEATER_ACTIVE_STATE))
wh_entities.append(GeErdSwitch(self, ErdCode.WH_HEATER_ACTIVE_CONTROL, ErdOnOffBoolConverter(), icon_on_override="mdi:power", icon_off_override="mdi:power-standby"))
entities = base_entities + wh_entities
return entities

38
devices/water_softener.py Normal file
View File

@ -0,0 +1,38 @@
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,
GeErdPropertySensor,
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"),
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, device_class_override="water"),
GeErdSensor(self, ErdCode.WH_SOFTENER_ERROR_CODE, icon_override="mdi:alert-circle"),
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),
]
entities = base_entities + ws_entities
return entities

13
entities/__init__.py Normal file
View File

@ -0,0 +1,13 @@
from .common import *
from .dishwasher import *
from .fridge import *
from .oven import *
from .water_filter import *
from .advantium import *
from .ac import *
from .hood import *
from .water_softener import *
from .water_heater import *
from .opal_ice_maker import *
from .ccm import *
from .dehumidifier import *

Binary file not shown.

4
entities/ac/__init__.py Normal file
View File

@ -0,0 +1,4 @@
from .ge_wac_climate import GeWacClimate
from .ge_sac_climate import GeSacClimate
from .ge_pac_climate import GePacClimate
from .ge_biac_climate import GeBiacClimate

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,45 @@
import logging
from typing import Any, List, Optional
from gehomesdk import ErdAcFanSetting
from ..common import OptionsConverter
_LOGGER = logging.getLogger(__name__)
class AcFanModeOptionsConverter(OptionsConverter):
def __init__(self, default_option: ErdAcFanSetting = ErdAcFanSetting.AUTO):
self._default = default_option
@property
def options(self) -> List[str]:
return [i.stringify() for i in [ErdAcFanSetting.AUTO, ErdAcFanSetting.LOW, ErdAcFanSetting.MED, ErdAcFanSetting.HIGH]]
def from_option_string(self, value: str) -> Any:
try:
return ErdAcFanSetting[value.upper().replace(" ","_")]
except:
_LOGGER.warning(f"Could not set fan mode to {value}")
return self._default
def to_option_string(self, value: Any) -> Optional[str]:
try:
return {
ErdAcFanSetting.AUTO: ErdAcFanSetting.AUTO,
ErdAcFanSetting.LOW: ErdAcFanSetting.LOW,
ErdAcFanSetting.LOW_AUTO: ErdAcFanSetting.AUTO,
ErdAcFanSetting.MED: ErdAcFanSetting.MED,
ErdAcFanSetting.MED_AUTO: ErdAcFanSetting.AUTO,
ErdAcFanSetting.HIGH: ErdAcFanSetting.HIGH,
ErdAcFanSetting.HIGH_AUTO: ErdAcFanSetting.HIGH
}.get(value).stringify()
except:
pass
return self._default.stringify()
class AcFanOnlyFanModeOptionsConverter(AcFanModeOptionsConverter):
def __init__(self):
super().__init__(ErdAcFanSetting.LOW)
@property
def options(self) -> List[str]:
return [i.stringify() for i in [ErdAcFanSetting.LOW, ErdAcFanSetting.MED, ErdAcFanSetting.HIGH]]

View File

@ -0,0 +1,41 @@
import logging
from typing import Any, List, Optional
from homeassistant.components.climate import HVACMode
from gehomesdk import ErdAcOperationMode
from ...devices import ApplianceApi
from ..common import GeClimate, OptionsConverter
from .fan_mode_options import AcFanModeOptionsConverter, AcFanOnlyFanModeOptionsConverter
_LOGGER = logging.getLogger(__name__)
class BiacHvacModeOptionsConverter(OptionsConverter):
@property
def options(self) -> List[str]:
return [HVACMode.AUTO, HVACMode.COOL, HVACMode.FAN_ONLY]
def from_option_string(self, value: str) -> Any:
try:
return {
HVACMode.AUTO: ErdAcOperationMode.ENERGY_SAVER,
HVACMode.COOL: ErdAcOperationMode.COOL,
HVACMode.FAN_ONLY: ErdAcOperationMode.FAN_ONLY
}.get(value)
except:
_LOGGER.warning(f"Could not set HVAC mode to {value.upper()}")
return ErdAcOperationMode.COOL
def to_option_string(self, value: Any) -> Optional[str]:
try:
return {
ErdAcOperationMode.ENERGY_SAVER: HVACMode.AUTO,
ErdAcOperationMode.AUTO: HVACMode.AUTO,
ErdAcOperationMode.COOL: HVACMode.COOL,
ErdAcOperationMode.FAN_ONLY: HVACMode.FAN_ONLY
}.get(value)
except:
_LOGGER.warning(f"Could not determine operation mode mapping for {value}")
return HVACMode.COOL
class GeBiacClimate(GeClimate):
"""Class for Built-In AC units"""
def __init__(self, api: ApplianceApi):
super().__init__(api, BiacHvacModeOptionsConverter(), AcFanModeOptionsConverter(), AcFanOnlyFanModeOptionsConverter())

View File

@ -0,0 +1,71 @@
import logging
from typing import Any, List, Optional
from homeassistant.components.climate import HVACMode
from gehomesdk import ErdCode, ErdAcOperationMode, ErdSacAvailableModes, ErdSacTargetTemperatureRange
from ...devices import ApplianceApi
from ..common import GeClimate, OptionsConverter
from .fan_mode_options import AcFanOnlyFanModeOptionsConverter
_LOGGER = logging.getLogger(__name__)
class PacHvacModeOptionsConverter(OptionsConverter):
def __init__(self, available_modes: ErdSacAvailableModes):
self._available_modes = available_modes
@property
def options(self) -> List[str]:
modes = [HVACMode.COOL, HVACMode.FAN_ONLY]
if self._available_modes and self._available_modes.has_heat:
modes.append(HVACMode.HEAT)
if self._available_modes and self._available_modes.has_dry:
modes.append(HVACMode.DRY)
return modes
def from_option_string(self, value: str) -> Any:
try:
return {
HVACMode.COOL: ErdAcOperationMode.COOL,
HVACMode.HEAT: ErdAcOperationMode.HEAT,
HVACMode.FAN_ONLY: ErdAcOperationMode.FAN_ONLY,
HVACMode.DRY: ErdAcOperationMode.DRY
}.get(value)
except:
_LOGGER.warning(f"Could not set HVAC mode to {value.upper()}")
return ErdAcOperationMode.COOL
def to_option_string(self, value: Any) -> Optional[str]:
try:
return {
ErdAcOperationMode.COOL: HVACMode.COOL,
ErdAcOperationMode.HEAT: HVACMode.HEAT,
ErdAcOperationMode.DRY: HVACMode.DRY,
ErdAcOperationMode.FAN_ONLY: HVACMode.FAN_ONLY
}.get(value)
except:
_LOGGER.warning(f"Could not determine operation mode mapping for {value}")
return HVACMode.COOL
class GePacClimate(GeClimate):
"""Class for Portable AC units"""
def __init__(self, api: ApplianceApi):
#initialize the climate control
super().__init__(api, None, AcFanOnlyFanModeOptionsConverter(), AcFanOnlyFanModeOptionsConverter())
#get a couple ERDs that shouldn't change if available
self._modes: ErdSacAvailableModes = self.api.try_get_erd_value(ErdCode.SAC_AVAILABLE_MODES)
self._temp_range: ErdSacTargetTemperatureRange = self.api.try_get_erd_value(ErdCode.SAC_TARGET_TEMPERATURE_RANGE)
#construct the converter based on the available modes
self._hvac_mode_converter = PacHvacModeOptionsConverter(self._modes)
@property
def min_temp(self) -> float:
temp = 64
if self._temp_range:
temp = self._temp_range.min
return self._convert_temp(temp)
@property
def max_temp(self) -> float:
temp = 86
if self._temp_range:
temp = self._temp_range.max
return self._convert_temp(temp)

View File

@ -0,0 +1,75 @@
import logging
from typing import Any, List, Optional
from homeassistant.components.climate import HVACMode
from gehomesdk import ErdCode, ErdAcOperationMode, ErdSacAvailableModes, ErdSacTargetTemperatureRange
from ...devices import ApplianceApi
from ..common import GeClimate, OptionsConverter
from .fan_mode_options import AcFanOnlyFanModeOptionsConverter, AcFanModeOptionsConverter
_LOGGER = logging.getLogger(__name__)
class SacHvacModeOptionsConverter(OptionsConverter):
def __init__(self, available_modes: ErdSacAvailableModes):
self._available_modes = available_modes
@property
def options(self) -> List[str]:
modes = [HVACMode.COOL, HVACMode.FAN_ONLY]
if self._available_modes and self._available_modes.has_heat:
modes.append(HVACMode.HEAT)
modes.append(HVACMode.AUTO)
if self._available_modes and self._available_modes.has_dry:
modes.append(HVACMode.DRY)
return modes
def from_option_string(self, value: str) -> Any:
try:
return {
HVACMode.AUTO: ErdAcOperationMode.AUTO,
HVACMode.COOL: ErdAcOperationMode.COOL,
HVACMode.HEAT: ErdAcOperationMode.HEAT,
HVACMode.FAN_ONLY: ErdAcOperationMode.FAN_ONLY,
HVACMode.DRY: ErdAcOperationMode.DRY
}.get(value)
except:
_LOGGER.warning(f"Could not set HVAC mode to {value.upper()}")
return ErdAcOperationMode.COOL
def to_option_string(self, value: Any) -> Optional[str]:
try:
return {
ErdAcOperationMode.ENERGY_SAVER: HVACMode.AUTO,
ErdAcOperationMode.AUTO: HVACMode.AUTO,
ErdAcOperationMode.COOL: HVACMode.COOL,
ErdAcOperationMode.HEAT: HVACMode.HEAT,
ErdAcOperationMode.DRY: HVACMode.DRY,
ErdAcOperationMode.FAN_ONLY: HVACMode.FAN_ONLY
}.get(value)
except:
_LOGGER.warning(f"Could not determine operation mode mapping for {value}")
return HVACMode.COOL
class GeSacClimate(GeClimate):
"""Class for Split AC units"""
def __init__(self, api: ApplianceApi):
#initialize the climate control
super().__init__(api, None, AcFanModeOptionsConverter(), AcFanOnlyFanModeOptionsConverter())
#get a couple ERDs that shouldn't change if available
self._modes: ErdSacAvailableModes = self.api.try_get_erd_value(ErdCode.SAC_AVAILABLE_MODES)
self._temp_range: ErdSacTargetTemperatureRange = self.api.try_get_erd_value(ErdCode.SAC_TARGET_TEMPERATURE_RANGE)
#construct the converter based on the available modes
self._hvac_mode_converter = SacHvacModeOptionsConverter(self._modes)
@property
def min_temp(self) -> float:
temp = 60
if self._temp_range:
temp = self._temp_range.min
return self._convert_temp(temp)
@property
def max_temp(self) -> float:
temp = 86
if self._temp_range:
temp = self._temp_range.max
return self._convert_temp(temp)

View File

@ -0,0 +1,41 @@
import logging
from typing import Any, List, Optional
from homeassistant.components.climate import HVACMode
from gehomesdk import ErdAcOperationMode
from ...devices import ApplianceApi
from ..common import GeClimate, OptionsConverter
from .fan_mode_options import AcFanModeOptionsConverter, AcFanOnlyFanModeOptionsConverter
_LOGGER = logging.getLogger(__name__)
class WacHvacModeOptionsConverter(OptionsConverter):
@property
def options(self) -> List[str]:
return [HVACMode.AUTO, HVACMode.COOL, HVACMode.FAN_ONLY]
def from_option_string(self, value: str) -> Any:
try:
return {
HVACMode.AUTO: ErdAcOperationMode.ENERGY_SAVER,
HVACMode.COOL: ErdAcOperationMode.COOL,
HVACMode.FAN_ONLY: ErdAcOperationMode.FAN_ONLY
}.get(value)
except:
_LOGGER.warning(f"Could not set HVAC mode to {value.upper()}")
return ErdAcOperationMode.COOL
def to_option_string(self, value: Any) -> Optional[str]:
try:
return {
ErdAcOperationMode.ENERGY_SAVER: HVACMode.AUTO,
ErdAcOperationMode.AUTO: HVACMode.AUTO,
ErdAcOperationMode.COOL: HVACMode.COOL,
ErdAcOperationMode.FAN_ONLY: HVACMode.FAN_ONLY
}.get(value)
except:
_LOGGER.warning(f"Could not determine operation mode mapping for {value}")
return HVACMode.COOL
class GeWacClimate(GeClimate):
"""Class for Window AC units"""
def __init__(self, api: ApplianceApi):
super().__init__(api, WacHvacModeOptionsConverter(), AcFanModeOptionsConverter(), AcFanOnlyFanModeOptionsConverter())

View File

@ -0,0 +1 @@
from .ge_advantium import GeAdvantium

Binary file not shown.

View File

@ -0,0 +1,5 @@
from homeassistant.components.water_heater import WaterHeaterEntityFeature
SUPPORT_NONE = WaterHeaterEntityFeature(0)
GE_ADVANTIUM_WITH_TEMPERATURE = (WaterHeaterEntityFeature.OPERATION_MODE | WaterHeaterEntityFeature.TARGET_TEMPERATURE)
GE_ADVANTIUM = WaterHeaterEntityFeature.OPERATION_MODE

View File

@ -0,0 +1,283 @@
"""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()

5
entities/ccm/__init__.py Normal file
View File

@ -0,0 +1,5 @@
from .ge_ccm_pot_not_present_binary_sensor import GeCcmPotNotPresentBinarySensor
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

Binary file not shown.

View File

@ -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_native_value(self, value):
GeCcmCachedValue.set_value(self, value)
self.schedule_update_ha_state()
@property
def native_value(self):
return self.get_value(device_value = super().native_value)

Some files were not shown because too many files have changed in this diff Show More