mirror of
https://github.com/home-assistant/core.git
synced 2026-05-28 19:53:18 +02:00
Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| bfcbfb8725 | |||
| 675969e36b | |||
| ff43e12449 | |||
| 7dfef5c82a | |||
| b75cd0f6a7 | |||
| 7859aba432 |
Generated
+2
@@ -466,6 +466,8 @@ CLAUDE.md @home-assistant/core
|
||||
/tests/components/electrasmart/ @jafar-atili
|
||||
/homeassistant/components/electric_kiwi/ @mikey0000
|
||||
/tests/components/electric_kiwi/ @mikey0000
|
||||
/homeassistant/components/electrolux/ @electrolux-oss
|
||||
/tests/components/electrolux/ @electrolux-oss
|
||||
/homeassistant/components/elevenlabs/ @sorgfresser
|
||||
/tests/components/elevenlabs/ @sorgfresser
|
||||
/homeassistant/components/elgato/ @frenck
|
||||
|
||||
@@ -0,0 +1,225 @@
|
||||
"""The Electrolux integration."""
|
||||
|
||||
from asyncio import CancelledError
|
||||
from collections.abc import Awaitable, Callable
|
||||
import logging
|
||||
|
||||
from electrolux_group_developer_sdk.auth.token_manager import TokenManager
|
||||
from electrolux_group_developer_sdk.client.appliance_client import ApplianceClient
|
||||
from electrolux_group_developer_sdk.client.appliances.appliance_data import (
|
||||
ApplianceData,
|
||||
)
|
||||
from electrolux_group_developer_sdk.client.bad_credentials_exception import (
|
||||
BadCredentialsException,
|
||||
)
|
||||
from electrolux_group_developer_sdk.client.client_exception import (
|
||||
ApplianceClientException,
|
||||
)
|
||||
from electrolux_group_developer_sdk.client.failed_connection_exception import (
|
||||
FailedConnectionException,
|
||||
)
|
||||
|
||||
from homeassistant.const import CONF_ACCESS_TOKEN, CONF_API_KEY, Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import (
|
||||
ConfigEntryAuthFailed,
|
||||
ConfigEntryError,
|
||||
ConfigEntryNotReady,
|
||||
)
|
||||
from homeassistant.helpers import device_registry as dr
|
||||
from homeassistant.helpers.dispatcher import async_dispatcher_send
|
||||
|
||||
from .const import CONF_REFRESH_TOKEN, DOMAIN, NEW_APPLIANCE_SIGNAL, USER_AGENT
|
||||
from .coordinator import (
|
||||
ElectroluxConfigEntry,
|
||||
ElectroluxData,
|
||||
ElectroluxDataUpdateCoordinator,
|
||||
)
|
||||
|
||||
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
||||
|
||||
PLATFORMS = [
|
||||
Platform.BINARY_SENSOR,
|
||||
Platform.SENSOR,
|
||||
]
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ElectroluxConfigEntry) -> bool:
|
||||
"""Set up Electrolux integration entry."""
|
||||
|
||||
token_manager = create_token_manager(hass, entry)
|
||||
client = ApplianceClient(
|
||||
token_manager=token_manager, external_user_agent=USER_AGENT
|
||||
)
|
||||
|
||||
try:
|
||||
await client.test_connection()
|
||||
except BadCredentialsException as e:
|
||||
raise ConfigEntryError("Bad credentials detected.") from e
|
||||
except FailedConnectionException as e:
|
||||
raise ConfigEntryNotReady("Connection with client failed.") from e
|
||||
|
||||
try:
|
||||
appliances = await fetch_appliance_data(client)
|
||||
except ApplianceClientException as e:
|
||||
raise ConfigEntryNotReady from e
|
||||
|
||||
coordinators: dict[str, ElectroluxDataUpdateCoordinator] = {}
|
||||
on_livestream_opening_callback_list: list[Callable[[], Awaitable[None]]] = []
|
||||
|
||||
async def check_for_new_devices_callback() -> None:
|
||||
"""Trigger _check_for_new_devices asynchronously."""
|
||||
await _check_for_new_devices(
|
||||
hass, entry, client, on_livestream_opening_callback_list
|
||||
)
|
||||
|
||||
on_livestream_opening_callback_list.append(check_for_new_devices_callback)
|
||||
|
||||
for appliance in appliances:
|
||||
appliance_id = appliance.appliance.applianceId
|
||||
coordinator = ElectroluxDataUpdateCoordinator(
|
||||
hass, entry, client=client, appliance_id=appliance_id
|
||||
)
|
||||
|
||||
await coordinator.async_config_entry_first_refresh()
|
||||
|
||||
# Subscribe this coordinator to its appliance events
|
||||
coordinator.add_client_listener()
|
||||
|
||||
coordinators[appliance_id] = coordinator
|
||||
# Device state is refreshed whenever the SSE connection opens.
|
||||
on_livestream_opening_callback_list.append(coordinator.async_refresh)
|
||||
|
||||
sse_task = entry.async_create_background_task(
|
||||
hass,
|
||||
client.start_event_stream(on_livestream_opening_callback_list),
|
||||
"electrolux event listener",
|
||||
)
|
||||
|
||||
entry.runtime_data = ElectroluxData(
|
||||
client=client,
|
||||
appliances=appliances,
|
||||
coordinators=coordinators,
|
||||
sse_task=sse_task,
|
||||
)
|
||||
|
||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ElectroluxConfigEntry) -> bool:
|
||||
"""Unload a config entry."""
|
||||
# Remove SSE listeners
|
||||
coordinators = entry.runtime_data.coordinators
|
||||
for coordinator in coordinators.values():
|
||||
coordinator.remove_client_listeners()
|
||||
|
||||
# Cancel SSE task
|
||||
sse_task = entry.runtime_data.sse_task
|
||||
sse_task.cancel()
|
||||
try:
|
||||
await sse_task
|
||||
except CancelledError:
|
||||
_LOGGER.info("SSE stream cancelled for entry %s", entry.entry_id)
|
||||
|
||||
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||
|
||||
|
||||
def create_token_manager(
|
||||
hass: HomeAssistant,
|
||||
entry: ElectroluxConfigEntry,
|
||||
) -> TokenManager:
|
||||
"""Create a token manager for the Electrolux integration."""
|
||||
|
||||
def save_tokens(new_access: str, new_refresh: str, new_api_key: str) -> None:
|
||||
hass.config_entries.async_update_entry(
|
||||
entry,
|
||||
data={
|
||||
**entry.data,
|
||||
CONF_API_KEY: new_api_key,
|
||||
CONF_ACCESS_TOKEN: new_access,
|
||||
CONF_REFRESH_TOKEN: new_refresh,
|
||||
},
|
||||
)
|
||||
|
||||
api_key = entry.data.get(CONF_API_KEY)
|
||||
refresh_token = entry.data.get(CONF_REFRESH_TOKEN)
|
||||
access_token = entry.data.get(CONF_ACCESS_TOKEN)
|
||||
|
||||
if access_token and refresh_token and api_key:
|
||||
return TokenManager(access_token, refresh_token, api_key, save_tokens)
|
||||
raise ConfigEntryAuthFailed("Missing access token, refresh token or API key")
|
||||
|
||||
|
||||
async def _check_for_new_devices(
|
||||
hass: HomeAssistant,
|
||||
entry: ElectroluxConfigEntry,
|
||||
client: ApplianceClient,
|
||||
on_livestream_opening_callback_list: list[Callable[[], Awaitable[None]]],
|
||||
) -> None:
|
||||
"""Fetch appliances from API and trigger discovery for any new ones."""
|
||||
_LOGGER.info("Checking for new devices")
|
||||
|
||||
coordinators = entry.runtime_data.coordinators
|
||||
appliances = await fetch_appliance_data(client)
|
||||
entry.runtime_data.appliances = appliances
|
||||
|
||||
existing_ids = set(coordinators.keys())
|
||||
|
||||
for appliance in appliances:
|
||||
appliance_id = appliance.appliance.applianceId
|
||||
# Detect NEW appliances
|
||||
if appliance_id not in existing_ids:
|
||||
# Create coordinator for appliance
|
||||
coordinator = ElectroluxDataUpdateCoordinator(
|
||||
hass, entry, client=client, appliance_id=appliance_id
|
||||
)
|
||||
|
||||
await coordinator.async_refresh()
|
||||
|
||||
coordinator.add_client_listener()
|
||||
coordinators[appliance_id] = coordinator
|
||||
on_livestream_opening_callback_list.append(coordinator.async_refresh)
|
||||
|
||||
# Notify all platforms
|
||||
async_dispatcher_send(
|
||||
hass, f"{NEW_APPLIANCE_SIGNAL}_{entry.entry_id}", appliance
|
||||
)
|
||||
|
||||
# Detect MISSING appliances
|
||||
discovered_ids = {appliance.appliance.applianceId for appliance in appliances}
|
||||
missing_ids = existing_ids - discovered_ids
|
||||
device_registry = dr.async_get(hass)
|
||||
for missing_id in missing_ids:
|
||||
_LOGGER.warning("Appliance %s no longer found, removing", missing_id)
|
||||
|
||||
# Remove coordinator
|
||||
coordinator = coordinators.pop(missing_id)
|
||||
coordinator.remove_client_listeners()
|
||||
on_livestream_opening_callback_list.remove(coordinator.async_refresh)
|
||||
|
||||
device_entry = device_registry.async_get_device(
|
||||
identifiers={(DOMAIN, missing_id)}
|
||||
)
|
||||
|
||||
if device_entry:
|
||||
device_registry.async_update_device(
|
||||
device_entry.id, remove_config_entry_id=entry.entry_id
|
||||
)
|
||||
|
||||
|
||||
async def fetch_appliance_data(client: ApplianceClient) -> list[ApplianceData]:
|
||||
"""Helper method to retrieve all the appliances data from the Electrolux APIs."""
|
||||
try:
|
||||
appliances = await client.get_appliance_data()
|
||||
except ApplianceClientException as e:
|
||||
_LOGGER.warning("Failed to get appliances: %s", e)
|
||||
raise
|
||||
|
||||
# Filter out appliances where details or state is None
|
||||
return [
|
||||
appliance
|
||||
for appliance in appliances
|
||||
if appliance.details is not None and appliance.state is not None
|
||||
]
|
||||
@@ -0,0 +1,443 @@
|
||||
"""Binary sensor entity for Electrolux Integration."""
|
||||
|
||||
from collections.abc import Callable
|
||||
from dataclasses import dataclass
|
||||
import logging
|
||||
from typing import TYPE_CHECKING, Any, TypeVar
|
||||
|
||||
from electrolux_group_developer_sdk.client.appliances.ac_appliance import ACAppliance
|
||||
from electrolux_group_developer_sdk.client.appliances.ap_appliance import APAppliance
|
||||
from electrolux_group_developer_sdk.client.appliances.appliance_data import (
|
||||
ApplianceData,
|
||||
)
|
||||
from electrolux_group_developer_sdk.client.appliances.cr_appliance import CRAppliance
|
||||
from electrolux_group_developer_sdk.client.appliances.dam_ac_appliance import (
|
||||
DAMACAppliance,
|
||||
)
|
||||
from electrolux_group_developer_sdk.client.appliances.dh_appliance import DHAppliance
|
||||
from electrolux_group_developer_sdk.client.appliances.dw_appliance import DWAppliance
|
||||
from electrolux_group_developer_sdk.client.appliances.hb_appliance import HBAppliance
|
||||
from electrolux_group_developer_sdk.client.appliances.hd_appliance import HDAppliance
|
||||
from electrolux_group_developer_sdk.client.appliances.ov_appliance import OVAppliance
|
||||
from electrolux_group_developer_sdk.client.appliances.rvc_appliance import RVCAppliance
|
||||
from electrolux_group_developer_sdk.client.appliances.so_appliance import SOAppliance
|
||||
from electrolux_group_developer_sdk.client.appliances.td_appliance import TDAppliance
|
||||
from electrolux_group_developer_sdk.client.appliances.wd_appliance import WDAppliance
|
||||
from electrolux_group_developer_sdk.client.appliances.wm_appliance import WMAppliance
|
||||
from electrolux_group_developer_sdk.feature_constants import (
|
||||
DOOR_STATE,
|
||||
DRAWER_STATUS,
|
||||
HOOD_AUTO_SWITCH_OFF_EVENT,
|
||||
HOOD_FILTER_CHARC_ENABLE,
|
||||
UI_LOCK_MODE,
|
||||
ZONE_HOB_POT_DETECTED,
|
||||
)
|
||||
|
||||
from homeassistant.components.binary_sensor import (
|
||||
BinarySensorDeviceClass,
|
||||
BinarySensorEntity,
|
||||
BinarySensorEntityDescription,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from .coordinator import ElectroluxConfigEntry, ElectroluxDataUpdateCoordinator
|
||||
from .entity import ElectroluxBaseEntity
|
||||
from .entity_helper import async_setup_entities_helper
|
||||
from .util import convert_to_snake_case
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
T = TypeVar("T", bound=ApplianceData)
|
||||
|
||||
|
||||
@dataclass(frozen=True, kw_only=True)
|
||||
class ElectroluxBinarySensorDescription[T = ApplianceData](
|
||||
BinarySensorEntityDescription
|
||||
):
|
||||
"""Custom binary sensor description for Electrolux sensors."""
|
||||
|
||||
exists_fn: Callable[[T], bool] = lambda appliance: True
|
||||
value_fn: Callable[[T], Any]
|
||||
mapping: dict[Any, bool] | None = None
|
||||
|
||||
|
||||
@dataclass(frozen=True, kw_only=True)
|
||||
class ElectroluxSubmoduleBinarySensorDescription[T = ApplianceData](
|
||||
BinarySensorEntityDescription
|
||||
):
|
||||
"""Custom binary sensor description for Electrolux appliance submodule sensors."""
|
||||
|
||||
exists_fn: Callable[[T, str], bool] = lambda appliance, submodule: True
|
||||
value_fn: Callable[[T, str], Any]
|
||||
mapping: dict[Any, bool] | None = None
|
||||
|
||||
|
||||
def _connection_state_value_fn(appliance: ApplianceData):
|
||||
if TYPE_CHECKING:
|
||||
assert appliance.state
|
||||
|
||||
return appliance.state.connectionState
|
||||
|
||||
|
||||
GENERAL_ELECTROLUX_SENSORS: tuple[ElectroluxBinarySensorDescription, ...] = (
|
||||
ElectroluxBinarySensorDescription(
|
||||
key="connection_state",
|
||||
translation_key="connection_state",
|
||||
device_class=BinarySensorDeviceClass.CONNECTIVITY,
|
||||
value_fn=_connection_state_value_fn,
|
||||
mapping={"connected": True, "disconnected": False},
|
||||
),
|
||||
)
|
||||
|
||||
HOB_ELECTROLUX_SENSORS: tuple[ElectroluxBinarySensorDescription[HBAppliance], ...] = (
|
||||
ElectroluxBinarySensorDescription(
|
||||
key="ui_lock_mode",
|
||||
translation_key="ui_lock_mode",
|
||||
exists_fn=lambda appliance: appliance.is_feature_supported(UI_LOCK_MODE),
|
||||
value_fn=lambda appliance: appliance.get_current_ui_lock_mode(),
|
||||
mapping={True: False, False: True},
|
||||
),
|
||||
)
|
||||
|
||||
HOB_ZONE_ELECTROLUX_SENSORS: tuple[
|
||||
ElectroluxSubmoduleBinarySensorDescription[HBAppliance], ...
|
||||
] = (
|
||||
ElectroluxSubmoduleBinarySensorDescription[HBAppliance](
|
||||
key="pot_detected",
|
||||
translation_key="pot_detected",
|
||||
value_fn=lambda appliance, hob_zone: (
|
||||
appliance.get_current_zone_hob_pot_detected(hob_zone)
|
||||
),
|
||||
exists_fn=lambda appliance, hob_zone: appliance.is_hob_zone_feature_supported(
|
||||
hob_zone, ZONE_HOB_POT_DETECTED
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
HOOD_ELECTROLUX_SENSORS: tuple[ElectroluxBinarySensorDescription[HDAppliance], ...] = (
|
||||
ElectroluxBinarySensorDescription(
|
||||
key="drawer_status",
|
||||
translation_key="drawer_status",
|
||||
exists_fn=lambda appliance: appliance.is_feature_supported(DRAWER_STATUS),
|
||||
value_fn=lambda appliance: appliance.get_current_drawer_status(),
|
||||
),
|
||||
ElectroluxBinarySensorDescription(
|
||||
key="hood_auto_switch_off_event",
|
||||
translation_key="hood_auto_switch_off_event",
|
||||
exists_fn=lambda appliance: appliance.is_feature_supported(
|
||||
HOOD_AUTO_SWITCH_OFF_EVENT
|
||||
),
|
||||
value_fn=lambda appliance: appliance.get_current_hood_auto_switch_off_event(),
|
||||
),
|
||||
ElectroluxBinarySensorDescription(
|
||||
key="hood_filter_charcoal_enabled",
|
||||
translation_key="hood_filter_charcoal_enabled",
|
||||
exists_fn=lambda appliance: appliance.is_feature_supported(
|
||||
HOOD_FILTER_CHARC_ENABLE
|
||||
),
|
||||
value_fn=lambda appliance: appliance.get_current_hood_filter_charc_enable(),
|
||||
mapping={"on": True, "off": False},
|
||||
),
|
||||
)
|
||||
|
||||
CARE_ELECTROLUX_SENSORS: tuple[
|
||||
ElectroluxBinarySensorDescription[
|
||||
DWAppliance | TDAppliance | WDAppliance | WMAppliance
|
||||
],
|
||||
...,
|
||||
] = (
|
||||
ElectroluxBinarySensorDescription(
|
||||
key="door_state",
|
||||
translation_key="door_state",
|
||||
device_class=BinarySensorDeviceClass.DOOR,
|
||||
exists_fn=lambda appliance: appliance.is_feature_supported(DOOR_STATE),
|
||||
value_fn=lambda appliance: appliance.get_current_door_state(),
|
||||
mapping={"closed": False, "open": True},
|
||||
),
|
||||
ElectroluxBinarySensorDescription(
|
||||
key="ui_lock_mode",
|
||||
translation_key="ui_lock_mode",
|
||||
exists_fn=lambda appliance: appliance.is_feature_supported(UI_LOCK_MODE),
|
||||
value_fn=lambda appliance: appliance.get_current_ui_lock_mode(),
|
||||
mapping={True: False, False: True},
|
||||
),
|
||||
)
|
||||
|
||||
OVEN_ELECTROLUX_SENSORS: tuple[ElectroluxBinarySensorDescription[OVAppliance], ...] = (
|
||||
ElectroluxBinarySensorDescription(
|
||||
key="door_state",
|
||||
translation_key="door_state",
|
||||
device_class=BinarySensorDeviceClass.DOOR,
|
||||
exists_fn=lambda appliance: appliance.is_feature_supported(DOOR_STATE),
|
||||
value_fn=lambda appliance: appliance.get_current_door_state(),
|
||||
mapping={"closed": False, "open": True},
|
||||
),
|
||||
)
|
||||
|
||||
STRUCTURED_OVEN_CAVITY_ELECTROLUX_SENSORS: tuple[
|
||||
ElectroluxSubmoduleBinarySensorDescription[SOAppliance], ...
|
||||
] = (
|
||||
ElectroluxSubmoduleBinarySensorDescription[SOAppliance](
|
||||
key="door_state",
|
||||
translation_key="door_state",
|
||||
device_class=BinarySensorDeviceClass.DOOR,
|
||||
exists_fn=lambda appliance, cavity: appliance.is_cavity_feature_supported(
|
||||
cavity, DOOR_STATE
|
||||
),
|
||||
value_fn=lambda appliance, cavity: appliance.get_current_cavity_door_state(
|
||||
cavity
|
||||
),
|
||||
mapping={"closed": False, "open": True},
|
||||
),
|
||||
)
|
||||
|
||||
REFRIGERATOR_GENERIC_ELECTROLUX_SENSORS: tuple[
|
||||
ElectroluxBinarySensorDescription[CRAppliance], ...
|
||||
] = (
|
||||
ElectroluxBinarySensorDescription(
|
||||
key="ui_lock_mode",
|
||||
translation_key="ui_lock_mode",
|
||||
exists_fn=lambda appliance: appliance.is_feature_supported(UI_LOCK_MODE),
|
||||
value_fn=lambda appliance: appliance.get_current_ui_lock_mode(),
|
||||
mapping={True: False, False: True},
|
||||
),
|
||||
)
|
||||
|
||||
FREEZER_FRIDGE_ICE_MAKER_EXTRA_CAVITY_ELECTROLUX_SENSORS: tuple[
|
||||
ElectroluxSubmoduleBinarySensorDescription[CRAppliance], ...
|
||||
] = (
|
||||
ElectroluxSubmoduleBinarySensorDescription(
|
||||
key="door_state",
|
||||
translation_key="door_state",
|
||||
device_class=BinarySensorDeviceClass.DOOR,
|
||||
exists_fn=lambda appliance, cavity: appliance.is_cavity_feature_supported(
|
||||
cavity, DOOR_STATE
|
||||
),
|
||||
value_fn=lambda appliance, cavity: appliance.get_current_cavity_door_state(
|
||||
cavity
|
||||
),
|
||||
mapping={"closed": False, "open": True},
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
def build_entities_for_appliance(
|
||||
appliance_data: ApplianceData,
|
||||
coordinators: dict[str, ElectroluxDataUpdateCoordinator],
|
||||
) -> list[ElectroluxBaseEntity]:
|
||||
"""Return all entities for a single appliance."""
|
||||
appliance = appliance_data.appliance
|
||||
coordinator = coordinators[appliance.applianceId]
|
||||
entities: list[ElectroluxBaseEntity] = []
|
||||
|
||||
if isinstance(
|
||||
appliance_data,
|
||||
(
|
||||
ACAppliance,
|
||||
APAppliance,
|
||||
CRAppliance,
|
||||
DAMACAppliance,
|
||||
DHAppliance,
|
||||
DWAppliance,
|
||||
HBAppliance,
|
||||
HDAppliance,
|
||||
OVAppliance,
|
||||
RVCAppliance,
|
||||
SOAppliance,
|
||||
TDAppliance,
|
||||
WDAppliance,
|
||||
WMAppliance,
|
||||
),
|
||||
):
|
||||
entities.extend(
|
||||
ElectroluxSensor(appliance_data, coordinator, description)
|
||||
for description in GENERAL_ELECTROLUX_SENSORS
|
||||
)
|
||||
|
||||
if isinstance(appliance_data, HBAppliance):
|
||||
entities.extend(
|
||||
ElectroluxSensor(appliance_data, coordinator, description)
|
||||
for description in HOB_ELECTROLUX_SENSORS
|
||||
if description.exists_fn(appliance_data)
|
||||
)
|
||||
|
||||
entities.extend(
|
||||
ElectroluxSubmoduleSensor(
|
||||
appliance_data, coordinator, hob_zone, description
|
||||
)
|
||||
for hob_zone in appliance_data.get_available_hob_zone()
|
||||
for description in HOB_ZONE_ELECTROLUX_SENSORS
|
||||
if description.exists_fn(appliance_data, hob_zone)
|
||||
)
|
||||
|
||||
if isinstance(appliance_data, HDAppliance):
|
||||
entities.extend(
|
||||
ElectroluxSensor(appliance_data, coordinator, description)
|
||||
for description in HOOD_ELECTROLUX_SENSORS
|
||||
if description.exists_fn(appliance_data)
|
||||
)
|
||||
|
||||
if isinstance(appliance_data, (DWAppliance, TDAppliance, WDAppliance, WMAppliance)):
|
||||
entities.extend(
|
||||
ElectroluxSensor(appliance_data, coordinator, description)
|
||||
for description in CARE_ELECTROLUX_SENSORS
|
||||
if description.exists_fn(appliance_data)
|
||||
)
|
||||
|
||||
if isinstance(appliance_data, OVAppliance):
|
||||
entities.extend(
|
||||
ElectroluxSensor(appliance_data, coordinator, description)
|
||||
for description in OVEN_ELECTROLUX_SENSORS
|
||||
if description.exists_fn(appliance_data)
|
||||
)
|
||||
|
||||
if isinstance(appliance_data, SOAppliance):
|
||||
entities.extend(
|
||||
ElectroluxSubmoduleSensor(appliance_data, coordinator, cavity, description)
|
||||
for description in STRUCTURED_OVEN_CAVITY_ELECTROLUX_SENSORS
|
||||
for cavity in appliance_data.get_supported_cavities()
|
||||
if description.exists_fn(appliance_data, cavity)
|
||||
)
|
||||
|
||||
if isinstance(appliance_data, CRAppliance):
|
||||
entities.extend(
|
||||
ElectroluxSensor(appliance_data, coordinator, description)
|
||||
for description in REFRIGERATOR_GENERIC_ELECTROLUX_SENSORS
|
||||
if description.exists_fn(appliance_data)
|
||||
)
|
||||
|
||||
entities.extend(
|
||||
ElectroluxSubmoduleSensor(appliance_data, coordinator, cavity, description)
|
||||
for description in FREEZER_FRIDGE_ICE_MAKER_EXTRA_CAVITY_ELECTROLUX_SENSORS
|
||||
for cavity in appliance_data.get_supported_cavities()
|
||||
if description.exists_fn(appliance_data, cavity)
|
||||
)
|
||||
|
||||
return entities
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: ElectroluxConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set binary sensor for Electrolux Integration."""
|
||||
await async_setup_entities_helper(
|
||||
hass, entry, async_add_entities, build_entities_for_appliance
|
||||
)
|
||||
|
||||
|
||||
class ElectroluxSensor(ElectroluxBaseEntity[T], BinarySensorEntity):
|
||||
"""Representation of a generic binary sensor for Electrolux appliances."""
|
||||
|
||||
entity_description: ElectroluxBinarySensorDescription[T]
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
appliance_data: T,
|
||||
coordinator: ElectroluxDataUpdateCoordinator,
|
||||
description: ElectroluxBinarySensorDescription[T],
|
||||
) -> None:
|
||||
"""Initialize the binary sensor."""
|
||||
super().__init__(appliance_data, coordinator, description.key)
|
||||
self.entity_description = description
|
||||
|
||||
def _update_attr_state(self) -> bool:
|
||||
new_value = self._get_value()
|
||||
if self._attr_is_on != new_value:
|
||||
self._attr_is_on = new_value
|
||||
return True
|
||||
return False
|
||||
|
||||
def _get_value(self) -> bool | None:
|
||||
description = self.entity_description
|
||||
entity_key = description.key
|
||||
value = description.value_fn(self._appliance_data)
|
||||
if isinstance(value, str):
|
||||
value = convert_to_snake_case(value)
|
||||
|
||||
if description.mapping is not None:
|
||||
return _map_to_known_value(description.mapping, entity_key, value)
|
||||
|
||||
if not isinstance(value, bool):
|
||||
_LOGGER.warning(
|
||||
"A non-bool value was detected for a binary sensor of the Electrolux integration. "
|
||||
"Please report it for the integration, and include the following information: "
|
||||
'entity key="%s", reported value="%s"',
|
||||
entity_key,
|
||||
value,
|
||||
)
|
||||
return None
|
||||
return value
|
||||
|
||||
|
||||
class ElectroluxSubmoduleSensor(ElectroluxBaseEntity[T], BinarySensorEntity):
|
||||
"""Representation of a generic binary sensor for appliance submodules."""
|
||||
|
||||
entity_description: ElectroluxSubmoduleBinarySensorDescription[T]
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
appliance_data: T,
|
||||
coordinator: ElectroluxDataUpdateCoordinator,
|
||||
cavity: str,
|
||||
description: ElectroluxSubmoduleBinarySensorDescription[T],
|
||||
) -> None:
|
||||
"""Initialize the sensor."""
|
||||
entity_key = f"{convert_to_snake_case(cavity)}_{description.key}"
|
||||
translation_key = (
|
||||
f"{convert_to_snake_case(cavity)}_{description.translation_key}"
|
||||
)
|
||||
super().__init__(appliance_data, coordinator, entity_key)
|
||||
|
||||
self._cavity = cavity
|
||||
self.entity_description = description
|
||||
self._attr_translation_key = translation_key
|
||||
|
||||
def _update_attr_state(self) -> bool:
|
||||
new_value = self._get_value()
|
||||
if self._attr_is_on != new_value:
|
||||
self._attr_is_on = new_value
|
||||
return True
|
||||
return False
|
||||
|
||||
def _get_value(self) -> Any:
|
||||
description = self.entity_description
|
||||
entity_key = f"{convert_to_snake_case(self._cavity)}_{description.key}"
|
||||
value = description.value_fn(self._appliance_data, self._cavity)
|
||||
if isinstance(value, str):
|
||||
value = convert_to_snake_case(value)
|
||||
|
||||
if description.mapping is not None:
|
||||
return _map_to_known_value(description.mapping, entity_key, value)
|
||||
|
||||
if not isinstance(value, bool):
|
||||
_LOGGER.warning(
|
||||
"A non-bool value was detected for a binary sensor of the Electrolux integration. "
|
||||
"Please report it for the integration, and include the following information: "
|
||||
'entity key="%s", reported value="%s"',
|
||||
entity_key,
|
||||
value,
|
||||
)
|
||||
return None
|
||||
return value
|
||||
|
||||
|
||||
_valid_type = str | float | int
|
||||
|
||||
|
||||
def _map_to_known_value(
|
||||
mapping: dict[_valid_type, bool], entity_key: str, value: _valid_type
|
||||
) -> bool | None:
|
||||
"""Map to boolean based on mapping; log warn message if value is not found in mapping."""
|
||||
if value not in mapping:
|
||||
_LOGGER.warning(
|
||||
"An unknown value %s was reported for a binary sensor of the Electrolux integration. "
|
||||
"Please report it for the integration, and include the following information: "
|
||||
'entity key="%s", reported value="%s"',
|
||||
value,
|
||||
entity_key,
|
||||
value,
|
||||
)
|
||||
return mapping.get(value)
|
||||
@@ -0,0 +1,99 @@
|
||||
"""Config flow for Electrolux integration."""
|
||||
|
||||
from collections.abc import Mapping
|
||||
import logging
|
||||
from typing import Any
|
||||
|
||||
from electrolux_group_developer_sdk.auth.invalid_credentials_exception import (
|
||||
InvalidCredentialsException,
|
||||
)
|
||||
from electrolux_group_developer_sdk.auth.token_manager import TokenManager
|
||||
from electrolux_group_developer_sdk.client.appliance_client import ApplianceClient
|
||||
from electrolux_group_developer_sdk.client.bad_credentials_exception import (
|
||||
BadCredentialsException,
|
||||
)
|
||||
from electrolux_group_developer_sdk.client.failed_connection_exception import (
|
||||
FailedConnectionException,
|
||||
)
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
|
||||
from homeassistant.const import CONF_ACCESS_TOKEN, CONF_API_KEY
|
||||
|
||||
from .const import CONF_REFRESH_TOKEN, DOMAIN, USER_AGENT
|
||||
|
||||
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ElectroluxConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
"""Handle a config flow for the Electrolux integration."""
|
||||
|
||||
VERSION = 1
|
||||
MINOR_VERSION = 1
|
||||
|
||||
async def async_step_user(
|
||||
self, user_input: dict[str, Any] | None = None
|
||||
) -> ConfigFlowResult:
|
||||
"""Handle the initial step of the config flow."""
|
||||
|
||||
errors: dict[str, str] = {}
|
||||
|
||||
if user_input is not None:
|
||||
token_manager: TokenManager
|
||||
email: str
|
||||
try:
|
||||
token_manager = await _authenticate_user(user_input)
|
||||
client = ApplianceClient(
|
||||
token_manager=token_manager, external_user_agent=USER_AGENT
|
||||
)
|
||||
email = (await client.get_user_email()).email
|
||||
except InvalidCredentialsException, BadCredentialsException:
|
||||
errors["base"] = "invalid_auth"
|
||||
except FailedConnectionException:
|
||||
errors["base"] = "cannot_connect"
|
||||
else:
|
||||
await self.async_set_unique_id(token_manager.get_user_id())
|
||||
self._abort_if_unique_id_configured()
|
||||
|
||||
if not errors:
|
||||
return self.async_create_entry(
|
||||
title=f"Electrolux for {email}",
|
||||
data=user_input,
|
||||
)
|
||||
|
||||
return self._show_form(step_id="user", errors=errors)
|
||||
|
||||
def _show_form(self, step_id: str, errors: dict[str, str]) -> ConfigFlowResult:
|
||||
return self.async_show_form(
|
||||
step_id=step_id,
|
||||
data_schema=vol.Schema(
|
||||
{
|
||||
vol.Required(CONF_API_KEY): str,
|
||||
vol.Required(CONF_ACCESS_TOKEN): str,
|
||||
vol.Required(CONF_REFRESH_TOKEN): str,
|
||||
}
|
||||
),
|
||||
errors=errors,
|
||||
description_placeholders={
|
||||
"portal_link": "https://developer.electrolux.one/generateToken"
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
async def _authenticate_user(user_input: Mapping[str, Any]) -> TokenManager:
|
||||
token_manager = TokenManager(
|
||||
access_token=user_input[CONF_ACCESS_TOKEN],
|
||||
refresh_token=user_input[CONF_REFRESH_TOKEN],
|
||||
api_key=user_input[CONF_API_KEY],
|
||||
)
|
||||
|
||||
token_manager.ensure_credentials()
|
||||
|
||||
appliance_client = ApplianceClient(
|
||||
token_manager=token_manager, external_user_agent=USER_AGENT
|
||||
)
|
||||
|
||||
# Test a connection in the config flow
|
||||
await appliance_client.test_connection()
|
||||
|
||||
return token_manager
|
||||
@@ -0,0 +1,11 @@
|
||||
"""Constants for Electrolux integration."""
|
||||
|
||||
from homeassistant.const import __version__ as HA_VERSION
|
||||
|
||||
DOMAIN = "electrolux"
|
||||
|
||||
CONF_REFRESH_TOKEN = "refresh_token"
|
||||
|
||||
NEW_APPLIANCE_SIGNAL = "electrolux_new_appliance"
|
||||
|
||||
USER_AGENT = f"HomeAssistant/{HA_VERSION}"
|
||||
@@ -0,0 +1,94 @@
|
||||
"""Electrolux coordinator class."""
|
||||
|
||||
from asyncio import Task
|
||||
from dataclasses import dataclass
|
||||
import logging
|
||||
|
||||
from electrolux_group_developer_sdk.client.appliance_client import (
|
||||
ApplianceClient,
|
||||
apply_sse_update,
|
||||
)
|
||||
from electrolux_group_developer_sdk.client.appliances.appliance_data import (
|
||||
ApplianceData,
|
||||
)
|
||||
from electrolux_group_developer_sdk.client.client_exception import (
|
||||
ApplianceClientException,
|
||||
)
|
||||
from electrolux_group_developer_sdk.client.dto.appliance_state import ApplianceState
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
|
||||
|
||||
from .const import DOMAIN
|
||||
|
||||
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@dataclass(kw_only=True, slots=True)
|
||||
class ElectroluxData:
|
||||
"""Electrolux data type."""
|
||||
|
||||
client: ApplianceClient
|
||||
appliances: list[ApplianceData]
|
||||
coordinators: dict[str, ElectroluxDataUpdateCoordinator]
|
||||
sse_task: Task
|
||||
|
||||
|
||||
type ElectroluxConfigEntry = ConfigEntry[ElectroluxData]
|
||||
|
||||
|
||||
class ElectroluxDataUpdateCoordinator(DataUpdateCoordinator[ApplianceState]):
|
||||
"""Class for fetching appliance data from the API."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
hass: HomeAssistant,
|
||||
config_entry: ElectroluxConfigEntry,
|
||||
client: ApplianceClient,
|
||||
appliance_id: str,
|
||||
) -> None:
|
||||
"""Initialize."""
|
||||
self.client = client
|
||||
self._appliance_id = appliance_id
|
||||
super().__init__(
|
||||
hass,
|
||||
_LOGGER,
|
||||
config_entry=config_entry,
|
||||
name=f"{DOMAIN}_{config_entry.entry_id}_{appliance_id}",
|
||||
update_interval=None,
|
||||
always_update=False,
|
||||
)
|
||||
|
||||
async def _async_update_data(self) -> ApplianceState:
|
||||
"""Return the current appliance state (SSE keeps it updated)."""
|
||||
try:
|
||||
appliance_state = await self.client.get_appliance_state(self._appliance_id)
|
||||
except ValueError as exception:
|
||||
raise UpdateFailed(exception) from exception
|
||||
except ApplianceClientException as exception:
|
||||
raise UpdateFailed(exception) from exception
|
||||
else:
|
||||
return appliance_state
|
||||
|
||||
def add_client_listener(self) -> None:
|
||||
"""Register an SSE listener to the appliance client for appliance state updates."""
|
||||
self.client.add_listener(self._appliance_id, self.callback_handle_event)
|
||||
|
||||
def remove_client_listeners(self) -> None:
|
||||
"""Remove all SSE listeners."""
|
||||
self.client.remove_all_listeners_by_appliance_id(self._appliance_id)
|
||||
|
||||
def callback_handle_event(self, event: dict) -> None:
|
||||
"""Handle an incoming SSE event. Event will look like: {"userId": "...", "applianceId": "...", "property": "timeToEnd", "value": 720}."""
|
||||
|
||||
current_state = self.data
|
||||
if not current_state:
|
||||
return
|
||||
|
||||
updated_state = apply_sse_update(
|
||||
current_state,
|
||||
event,
|
||||
)
|
||||
|
||||
self.async_set_updated_data(updated_state)
|
||||
@@ -0,0 +1,80 @@
|
||||
"""Base entity for Electrolux integration."""
|
||||
|
||||
from abc import abstractmethod
|
||||
import logging
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from electrolux_group_developer_sdk.client.appliances.appliance_data import (
|
||||
ApplianceData,
|
||||
)
|
||||
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.helpers.device_registry import DeviceInfo
|
||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||
|
||||
from .const import DOMAIN
|
||||
from .coordinator import ElectroluxDataUpdateCoordinator
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ElectroluxBaseEntity[T: ApplianceData](
|
||||
CoordinatorEntity[ElectroluxDataUpdateCoordinator]
|
||||
):
|
||||
"""Base class for Electrolux entities."""
|
||||
|
||||
_attr_has_entity_name = True
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
appliance_data: T,
|
||||
coordinator: ElectroluxDataUpdateCoordinator,
|
||||
unique_id_suffix: str,
|
||||
) -> None:
|
||||
"""Initialize the base device."""
|
||||
super().__init__(coordinator)
|
||||
appliance_name = appliance_data.appliance.applianceName
|
||||
appliance_id = appliance_data.appliance.applianceId
|
||||
|
||||
if TYPE_CHECKING:
|
||||
assert appliance_data.details
|
||||
assert appliance_data.state
|
||||
|
||||
appliance_info = appliance_data.details.applianceInfo
|
||||
|
||||
self._appliance_data = appliance_data
|
||||
self._attr_unique_id = f"{appliance_id}_{unique_id_suffix}"
|
||||
self._appliance_id = appliance_id
|
||||
self._appliance_capabilities = appliance_data.details.capabilities
|
||||
|
||||
self._attr_device_info = DeviceInfo(
|
||||
identifiers={(DOMAIN, appliance_id)},
|
||||
name=appliance_name,
|
||||
manufacturer=appliance_info.brand,
|
||||
model=appliance_info.model,
|
||||
serial_number=appliance_info.serialNumber,
|
||||
)
|
||||
|
||||
async def async_added_to_hass(self) -> None:
|
||||
"""When entity is added to HA."""
|
||||
await super().async_added_to_hass()
|
||||
self._handle_coordinator_update()
|
||||
|
||||
@abstractmethod
|
||||
def _update_attr_state(self) -> bool:
|
||||
"""Update entity-specific attributes. Returns True if any attributes were changed, otherwise False."""
|
||||
|
||||
@callback
|
||||
def _handle_coordinator_update(self) -> None:
|
||||
"""When the coordinator updates."""
|
||||
appliance_state = self.coordinator.data
|
||||
if not appliance_state:
|
||||
_LOGGER.warning("Appliance %s not found in update", self._appliance_id)
|
||||
return
|
||||
|
||||
# Update state
|
||||
self._appliance_data.update_state(appliance_state)
|
||||
state_changed = self._update_attr_state()
|
||||
|
||||
if state_changed:
|
||||
self.async_write_ha_state()
|
||||
@@ -0,0 +1,49 @@
|
||||
"""Contains entity helper methods."""
|
||||
|
||||
from collections.abc import Callable
|
||||
|
||||
from electrolux_group_developer_sdk.client.appliances.appliance_data import (
|
||||
ApplianceData,
|
||||
)
|
||||
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from .const import NEW_APPLIANCE_SIGNAL
|
||||
from .coordinator import ElectroluxConfigEntry, ElectroluxDataUpdateCoordinator
|
||||
from .entity import ElectroluxBaseEntity
|
||||
|
||||
|
||||
async def async_setup_entities_helper(
|
||||
hass: HomeAssistant,
|
||||
entry: ElectroluxConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
build_entities_fn: Callable[
|
||||
[ApplianceData, dict[str, ElectroluxDataUpdateCoordinator]],
|
||||
list[ElectroluxBaseEntity],
|
||||
],
|
||||
) -> None:
|
||||
"""Provide async_setup_entry helper."""
|
||||
|
||||
appliances: list[ApplianceData] = entry.runtime_data.appliances
|
||||
coordinators = entry.runtime_data.coordinators
|
||||
|
||||
entities: list[ElectroluxBaseEntity] = []
|
||||
|
||||
for appliance_data in appliances:
|
||||
entities.extend(build_entities_fn(appliance_data, coordinators))
|
||||
|
||||
async_add_entities(entities)
|
||||
|
||||
# Listen for new/removed appliances
|
||||
async def _new_appliance(appliance_data: ApplianceData):
|
||||
new_entities = build_entities_fn(appliance_data, coordinators)
|
||||
if new_entities:
|
||||
async_add_entities(new_entities)
|
||||
|
||||
entry.async_on_unload(
|
||||
async_dispatcher_connect(
|
||||
hass, f"{NEW_APPLIANCE_SIGNAL}_{entry.entry_id}", _new_appliance
|
||||
)
|
||||
)
|
||||
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"entity": {
|
||||
"binary_sensor": {
|
||||
"drawer_status": {
|
||||
"default": "mdi:file-cabinet"
|
||||
},
|
||||
"hood_auto_switch_off_event": {
|
||||
"default": "mdi:power-sleep"
|
||||
}
|
||||
},
|
||||
"sensor": {
|
||||
"appliance_state": {
|
||||
"default": "mdi:information-outline"
|
||||
},
|
||||
"food_probe_state": {
|
||||
"default": "mdi:thermometer-probe"
|
||||
},
|
||||
"food_probe_temperature": {
|
||||
"default": "mdi:thermometer-probe"
|
||||
},
|
||||
"remote_control": {
|
||||
"default": "mdi:remote"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"domain": "electrolux",
|
||||
"name": "Electrolux",
|
||||
"codeowners": ["@electrolux-oss"],
|
||||
"config_flow": true,
|
||||
"documentation": "https://www.home-assistant.io/integrations/electrolux",
|
||||
"integration_type": "hub",
|
||||
"iot_class": "cloud_push",
|
||||
"quality_scale": "bronze",
|
||||
"requirements": ["electrolux-group-developer-sdk==0.5.0"]
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
rules:
|
||||
# Bronze
|
||||
action-setup:
|
||||
status: exempt
|
||||
comment: |
|
||||
No actions are implemented currently.
|
||||
appropriate-polling:
|
||||
status: exempt
|
||||
comment: |
|
||||
Polling is only performed on infrequent events (when the livestream of events is opened, in order to sync),
|
||||
otherwise the integration works via push
|
||||
brands: done
|
||||
common-modules: done
|
||||
config-flow-test-coverage: done
|
||||
config-flow: done
|
||||
dependency-transparency: done
|
||||
docs-actions:
|
||||
status: exempt
|
||||
comment: |
|
||||
No actions are implemented currently.
|
||||
docs-high-level-description: done
|
||||
docs-installation-instructions: done
|
||||
docs-removal-instructions: done
|
||||
entity-event-setup: done
|
||||
entity-unique-id: done
|
||||
has-entity-name: done
|
||||
runtime-data: done
|
||||
test-before-configure: done
|
||||
test-before-setup: done
|
||||
unique-config-entry: done
|
||||
|
||||
# Silver
|
||||
action-exceptions: todo
|
||||
config-entry-unloading: done
|
||||
docs-configuration-parameters: todo
|
||||
docs-installation-parameters: todo
|
||||
entity-unavailable: todo
|
||||
integration-owner: done
|
||||
log-when-unavailable: todo
|
||||
parallel-updates: todo
|
||||
reauthentication-flow: todo
|
||||
test-coverage: todo
|
||||
|
||||
# Gold
|
||||
devices: todo
|
||||
diagnostics: todo
|
||||
discovery-update-info: todo
|
||||
discovery: todo
|
||||
docs-data-update: todo
|
||||
docs-examples: todo
|
||||
docs-known-limitations: todo
|
||||
docs-supported-devices: todo
|
||||
docs-supported-functions: todo
|
||||
docs-troubleshooting: todo
|
||||
docs-use-cases: todo
|
||||
dynamic-devices: todo
|
||||
entity-category: todo
|
||||
entity-device-class: todo
|
||||
entity-disabled-by-default: todo
|
||||
entity-translations: todo
|
||||
exception-translations: todo
|
||||
icon-translations: done
|
||||
reconfiguration-flow: todo
|
||||
repair-issues: todo
|
||||
stale-devices: todo
|
||||
|
||||
# Platinum
|
||||
async-dependency: done
|
||||
inject-websession: todo
|
||||
strict-typing: todo
|
||||
@@ -0,0 +1,279 @@
|
||||
"""Sensor entity for Electrolux Integration."""
|
||||
|
||||
from collections.abc import Callable
|
||||
from dataclasses import dataclass
|
||||
import logging
|
||||
from typing import cast
|
||||
|
||||
from electrolux_group_developer_sdk.client.appliances.appliance_data import (
|
||||
ApplianceData,
|
||||
)
|
||||
from electrolux_group_developer_sdk.client.appliances.cr_appliance import CRAppliance
|
||||
from electrolux_group_developer_sdk.client.appliances.ov_appliance import OVAppliance
|
||||
from electrolux_group_developer_sdk.feature_constants import (
|
||||
APPLIANCE_STATE,
|
||||
DISPLAY_FOOD_PROBE_TEMPERATURE_C,
|
||||
DISPLAY_FOOD_PROBE_TEMPERATURE_F,
|
||||
DISPLAY_TEMPERATURE_C,
|
||||
DISPLAY_TEMPERATURE_F,
|
||||
FOOD_PROBE_STATE,
|
||||
REMOTE_CONTROL,
|
||||
)
|
||||
|
||||
from homeassistant.components.sensor import (
|
||||
SensorDeviceClass,
|
||||
SensorEntity,
|
||||
SensorEntityDescription,
|
||||
SensorStateClass,
|
||||
StateType,
|
||||
)
|
||||
from homeassistant.const import UnitOfTemperature
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
from homeassistant.util.unit_conversion import TemperatureConverter
|
||||
|
||||
from .coordinator import ElectroluxConfigEntry, ElectroluxDataUpdateCoordinator
|
||||
from .entity import ElectroluxBaseEntity
|
||||
from .entity_helper import async_setup_entities_helper
|
||||
from .util import convert_to_snake_case
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
ELECTROLUX_TO_HA_TEMPERATURE_UNIT = {
|
||||
"CELSIUS": UnitOfTemperature.CELSIUS,
|
||||
"FAHRENHEIT": UnitOfTemperature.FAHRENHEIT,
|
||||
}
|
||||
|
||||
|
||||
@dataclass(frozen=True, kw_only=True)
|
||||
class ElectroluxSensorDescription(SensorEntityDescription):
|
||||
"""Custom sensor description for Electrolux sensors."""
|
||||
|
||||
value_fn: Callable[..., StateType]
|
||||
exists_fn: Callable[[ApplianceData], bool] = lambda *args: True
|
||||
feature_name: str | None = None
|
||||
known_values: set[str] | None = None
|
||||
|
||||
|
||||
OVEN_ELECTROLUX_SENSORS: tuple[ElectroluxSensorDescription, ...] = (
|
||||
ElectroluxSensorDescription(
|
||||
key="appliance_state",
|
||||
translation_key="appliance_state",
|
||||
value_fn=lambda appliance: appliance.get_current_appliance_state(),
|
||||
device_class=SensorDeviceClass.ENUM,
|
||||
feature_name=APPLIANCE_STATE,
|
||||
exists_fn=lambda appliance: appliance.is_feature_supported(APPLIANCE_STATE),
|
||||
known_values={
|
||||
"alarm",
|
||||
"delayed_start",
|
||||
"end_of_cycle",
|
||||
"idle",
|
||||
"off",
|
||||
"paused",
|
||||
"ready_to_start",
|
||||
"running",
|
||||
},
|
||||
),
|
||||
ElectroluxSensorDescription(
|
||||
key="food_probe_state",
|
||||
translation_key="food_probe_state",
|
||||
value_fn=lambda appliance: appliance.get_current_food_probe_insertion_state(),
|
||||
device_class=SensorDeviceClass.ENUM,
|
||||
feature_name=FOOD_PROBE_STATE,
|
||||
exists_fn=lambda appliance: appliance.is_feature_supported(FOOD_PROBE_STATE),
|
||||
known_values={
|
||||
"inserted",
|
||||
"not_inserted",
|
||||
},
|
||||
),
|
||||
ElectroluxSensorDescription(
|
||||
key="remote_control",
|
||||
translation_key="remote_control",
|
||||
value_fn=lambda appliance: appliance.get_current_remote_control(),
|
||||
device_class=SensorDeviceClass.ENUM,
|
||||
feature_name=REMOTE_CONTROL,
|
||||
exists_fn=lambda appliance: appliance.is_feature_supported(REMOTE_CONTROL),
|
||||
known_values={
|
||||
"disabled",
|
||||
"enabled",
|
||||
"not_safety_relevant_enabled",
|
||||
"temporary_locked",
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
OVEN_TEMPERATURE_ELECTROLUX_SENSORS: tuple[ElectroluxSensorDescription, ...] = (
|
||||
ElectroluxSensorDescription(
|
||||
key="food_probe_temperature",
|
||||
translation_key="food_probe_temperature",
|
||||
device_class=SensorDeviceClass.TEMPERATURE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
value_fn=lambda appliance, temp_unit=None: (
|
||||
appliance.get_current_display_food_probe_temperature_f()
|
||||
if temp_unit == UnitOfTemperature.FAHRENHEIT
|
||||
else appliance.get_current_display_food_probe_temperature_c()
|
||||
),
|
||||
exists_fn=lambda appliance: appliance.is_feature_supported(
|
||||
[DISPLAY_FOOD_PROBE_TEMPERATURE_F, DISPLAY_FOOD_PROBE_TEMPERATURE_C]
|
||||
),
|
||||
),
|
||||
ElectroluxSensorDescription(
|
||||
key="display_temperature",
|
||||
translation_key="display_temperature",
|
||||
device_class=SensorDeviceClass.TEMPERATURE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
value_fn=lambda appliance, temp_unit=None: (
|
||||
appliance.get_current_display_temperature_f()
|
||||
if temp_unit == UnitOfTemperature.FAHRENHEIT
|
||||
else appliance.get_current_display_temperature_c()
|
||||
),
|
||||
exists_fn=lambda appliance: appliance.is_feature_supported(
|
||||
[DISPLAY_TEMPERATURE_C, DISPLAY_TEMPERATURE_F]
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
def build_entities_for_appliance(
|
||||
appliance_data: ApplianceData,
|
||||
coordinators: dict[str, ElectroluxDataUpdateCoordinator],
|
||||
) -> list[ElectroluxBaseEntity]:
|
||||
"""Return all entities for a single appliance."""
|
||||
appliance = appliance_data.appliance
|
||||
coordinator = coordinators[appliance.applianceId]
|
||||
entities: list[ElectroluxBaseEntity] = []
|
||||
|
||||
if isinstance(appliance_data, OVAppliance):
|
||||
entities.extend(
|
||||
ElectroluxSensor(appliance_data, coordinator, description)
|
||||
for description in OVEN_ELECTROLUX_SENSORS
|
||||
if description.exists_fn(appliance_data)
|
||||
)
|
||||
|
||||
entities.extend(
|
||||
ElectroluxTemperatureSensor(appliance_data, coordinator, description)
|
||||
for description in OVEN_TEMPERATURE_ELECTROLUX_SENSORS
|
||||
if description.exists_fn(appliance_data)
|
||||
)
|
||||
|
||||
return entities
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: ElectroluxConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set sensor for Electrolux Integration."""
|
||||
await async_setup_entities_helper(
|
||||
hass, entry, async_add_entities, build_entities_for_appliance
|
||||
)
|
||||
|
||||
|
||||
class ElectroluxSensor(ElectroluxBaseEntity[ApplianceData], SensorEntity):
|
||||
"""Representation of a generic sensor for Electrolux appliances."""
|
||||
|
||||
entity_description: ElectroluxSensorDescription
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
appliance_data: ApplianceData,
|
||||
coordinator: ElectroluxDataUpdateCoordinator,
|
||||
description: ElectroluxSensorDescription,
|
||||
) -> None:
|
||||
"""Initialize the sensor."""
|
||||
super().__init__(appliance_data, coordinator, description.key)
|
||||
|
||||
if (
|
||||
description.feature_name is not None
|
||||
and description.known_values is not None
|
||||
):
|
||||
options = appliance_data.get_feature_state_string_options(
|
||||
description.feature_name
|
||||
)
|
||||
snake_case_options = [
|
||||
snake_case_option
|
||||
for option in options
|
||||
if (snake_case_option := convert_to_snake_case(option))
|
||||
in description.known_values
|
||||
]
|
||||
|
||||
if len(snake_case_options) > 0:
|
||||
self._attr_options = snake_case_options
|
||||
|
||||
self.entity_description = description
|
||||
|
||||
def _update_attr_state(self) -> bool:
|
||||
new_value = self._get_value()
|
||||
if isinstance(new_value, str):
|
||||
new_value = convert_to_snake_case(new_value)
|
||||
|
||||
if self.entity_description.known_values:
|
||||
new_value = _map_to_known_value(
|
||||
self.entity_description.known_values,
|
||||
self.entity_description.key,
|
||||
new_value,
|
||||
)
|
||||
|
||||
if self._attr_native_value != new_value:
|
||||
self._attr_native_value = new_value
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def _get_value(self) -> StateType:
|
||||
return self.entity_description.value_fn(self._appliance_data)
|
||||
|
||||
|
||||
class ElectroluxTemperatureSensor(ElectroluxSensor):
|
||||
"""Representation of a temperature sensor for Electrolux appliances."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
appliance_data: ApplianceData,
|
||||
coordinator: ElectroluxDataUpdateCoordinator,
|
||||
description: ElectroluxSensorDescription,
|
||||
) -> None:
|
||||
"""Initialize the sensor."""
|
||||
self._appliance = cast(OVAppliance | CRAppliance, appliance_data)
|
||||
self._attr_native_unit_of_measurement = UnitOfTemperature.CELSIUS
|
||||
super().__init__(appliance_data, coordinator, description)
|
||||
|
||||
def _get_value(self) -> StateType:
|
||||
temp_unit = self._get_temperature_unit()
|
||||
temp_value: float | None = cast(
|
||||
float | None,
|
||||
self.entity_description.value_fn(self._appliance_data, temp_unit=temp_unit),
|
||||
)
|
||||
if temp_value is None:
|
||||
return None
|
||||
return TemperatureConverter.convert(
|
||||
temp_value, temp_unit, UnitOfTemperature.CELSIUS
|
||||
)
|
||||
|
||||
def _get_temperature_unit(self) -> UnitOfTemperature:
|
||||
temp_unit = self._appliance.get_current_temperature_unit()
|
||||
|
||||
if temp_unit is not None:
|
||||
temp_unit = temp_unit.upper()
|
||||
|
||||
return ELECTROLUX_TO_HA_TEMPERATURE_UNIT.get(
|
||||
temp_unit, UnitOfTemperature.CELSIUS
|
||||
)
|
||||
|
||||
|
||||
def _map_to_known_value(
|
||||
known_values: set[str], entity_key: str, value: str
|
||||
) -> str | None:
|
||||
"""Return provided value if it is known, otherwise log warn message and return None."""
|
||||
if value not in known_values:
|
||||
_LOGGER.warning(
|
||||
"An unknown value %s was reported for a sensor of the Electrolux integration. "
|
||||
"Please report it for the integration, and include the following information: "
|
||||
'entity key="%s", reported value="%s"',
|
||||
value,
|
||||
entity_key,
|
||||
value,
|
||||
)
|
||||
return None
|
||||
return value
|
||||
@@ -0,0 +1,125 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "This Electrolux account is already configured."
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "Unable to connect to the Electrolux API. Please check credentials and try again.",
|
||||
"invalid_auth": "Authentication failed. Please check your credentials."
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"access_token": "[%key:common::config_flow::data::access_token%]",
|
||||
"api_key": "[%key:common::config_flow::data::api_key%]",
|
||||
"refresh_token": "Refresh token"
|
||||
},
|
||||
"data_description": {
|
||||
"access_token": "The access token from Electrolux Group for Developer.",
|
||||
"api_key": "Your Electrolux Group for Developer API key.",
|
||||
"refresh_token": "The refresh token used to renew your access token."
|
||||
},
|
||||
"description": "Please go to the [developer portal]({portal_link}) to generate new access and refresh tokens, then paste them below.",
|
||||
"title": "Configure your Electrolux Group account"
|
||||
}
|
||||
}
|
||||
},
|
||||
"entity": {
|
||||
"binary_sensor": {
|
||||
"bottomoven_door_state": {
|
||||
"name": "Lower door state"
|
||||
},
|
||||
"connection_state": {
|
||||
"name": "Connection state"
|
||||
},
|
||||
"door_state": {
|
||||
"name": "Door state"
|
||||
},
|
||||
"drawer_status": {
|
||||
"name": "Drawer status"
|
||||
},
|
||||
"extracavity_door_state": {
|
||||
"name": "Extra cavity door"
|
||||
},
|
||||
"freezer_door_state": {
|
||||
"name": "Freezer door"
|
||||
},
|
||||
"fridge_door_state": {
|
||||
"name": "Fridge door"
|
||||
},
|
||||
"hobzone1_pot_detected": {
|
||||
"name": "Zone 1 pot detected"
|
||||
},
|
||||
"hobzone2_pot_detected": {
|
||||
"name": "Zone 2 pot detected"
|
||||
},
|
||||
"hobzone3_pot_detected": {
|
||||
"name": "Zone 3 pot detected"
|
||||
},
|
||||
"hobzone4_pot_detected": {
|
||||
"name": "Zone 4 pot detected"
|
||||
},
|
||||
"hobzone5_pot_detected": {
|
||||
"name": "Zone 5 pot detected"
|
||||
},
|
||||
"hobzone6_pot_detected": {
|
||||
"name": "Zone 6 pot detected"
|
||||
},
|
||||
"hobzone7_pot_detected": {
|
||||
"name": "Zone 7 pot detected"
|
||||
},
|
||||
"hobzone8_pot_detected": {
|
||||
"name": "Zone 8 pot detected"
|
||||
},
|
||||
"hood_auto_switch_off_event": {
|
||||
"name": "Auto switch off event"
|
||||
},
|
||||
"hood_filter_charcoal_enabled": {
|
||||
"name": "Charcoal filter"
|
||||
},
|
||||
"ui_lock_mode": {
|
||||
"name": "UI lock mode"
|
||||
},
|
||||
"upperoven_door_state": {
|
||||
"name": "Upper door state"
|
||||
}
|
||||
},
|
||||
"sensor": {
|
||||
"appliance_state": {
|
||||
"name": "Appliance state",
|
||||
"state": {
|
||||
"alarm": "Alarm",
|
||||
"delayed_start": "Delayed start",
|
||||
"end_of_cycle": "Cycle ended",
|
||||
"idle": "[%key:common::state::idle%]",
|
||||
"off": "[%key:common::state::off%]",
|
||||
"paused": "[%key:common::state::paused%]",
|
||||
"ready_to_start": "Ready to start",
|
||||
"running": "Running"
|
||||
}
|
||||
},
|
||||
"display_temperature": {
|
||||
"name": "Current temperature"
|
||||
},
|
||||
"food_probe_state": {
|
||||
"name": "Food probe state",
|
||||
"state": {
|
||||
"inserted": "Inserted",
|
||||
"not_inserted": "Not inserted"
|
||||
}
|
||||
},
|
||||
"food_probe_temperature": {
|
||||
"name": "Food probe temperature"
|
||||
},
|
||||
"remote_control": {
|
||||
"name": "Remote control",
|
||||
"state": {
|
||||
"disabled": "[%key:common::state::disabled%]",
|
||||
"enabled": "[%key:common::state::enabled%]",
|
||||
"not_safety_relevant_enabled": "Not safety relevant enabled",
|
||||
"temporary_locked": "Temporarily locked"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
"""Utility functions used by the Electrolux integration."""
|
||||
|
||||
|
||||
def convert_to_snake_case(x: str) -> str:
|
||||
"""Converts a string to snake case."""
|
||||
lower_case = x.lower()
|
||||
return "".join([_convert_char_to_snake_case(char) for char in lower_case])
|
||||
|
||||
|
||||
def _convert_char_to_snake_case(char: str) -> str:
|
||||
if char.isspace():
|
||||
return "_"
|
||||
return char
|
||||
Generated
+1
@@ -192,6 +192,7 @@ FLOWS = {
|
||||
"ekeybionyx",
|
||||
"electrasmart",
|
||||
"electric_kiwi",
|
||||
"electrolux",
|
||||
"elevenlabs",
|
||||
"elgato",
|
||||
"elkm1",
|
||||
|
||||
@@ -1708,6 +1708,12 @@
|
||||
"config_flow": true,
|
||||
"iot_class": "cloud_polling"
|
||||
},
|
||||
"electrolux": {
|
||||
"name": "Electrolux",
|
||||
"integration_type": "hub",
|
||||
"config_flow": true,
|
||||
"iot_class": "cloud_push"
|
||||
},
|
||||
"elevenlabs": {
|
||||
"name": "ElevenLabs",
|
||||
"integration_type": "service",
|
||||
|
||||
Generated
+3
@@ -891,6 +891,9 @@ ekey-bionyxpy==1.0.1
|
||||
# homeassistant.components.electric_kiwi
|
||||
electrickiwi-api==0.9.14
|
||||
|
||||
# homeassistant.components.electrolux
|
||||
electrolux-group-developer-sdk==0.5.0
|
||||
|
||||
# homeassistant.components.elevenlabs
|
||||
elevenlabs==2.3.0
|
||||
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
"""Tests for the electrolux integration."""
|
||||
|
||||
from functools import cache
|
||||
|
||||
from electrolux_group_developer_sdk.client.dto.appliance import Appliance
|
||||
from electrolux_group_developer_sdk.client.dto.appliance_details import ApplianceDetails
|
||||
from electrolux_group_developer_sdk.client.dto.appliance_state import ApplianceState
|
||||
|
||||
from homeassistant.components.electrolux.const import DOMAIN
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from tests.common import MockConfigEntry, load_fixture
|
||||
|
||||
APPLIANCE_FIXTURES = [
|
||||
"fenix_oven",
|
||||
"pux_oven",
|
||||
"peacock_hob",
|
||||
"tumble_dryer",
|
||||
"supex_structured_oven",
|
||||
"ayran_fridge",
|
||||
"hood",
|
||||
]
|
||||
|
||||
|
||||
async def setup_integration(
|
||||
hass: HomeAssistant, mock_config_entry: MockConfigEntry
|
||||
) -> None:
|
||||
"""Set up Electrolux integration for tests."""
|
||||
mock_config_entry.add_to_hass(hass)
|
||||
|
||||
await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
|
||||
@cache
|
||||
def get_fixture_name(appliance_id: str) -> str:
|
||||
"""Get the fixture name for the given appliance ID."""
|
||||
for name in APPLIANCE_FIXTURES:
|
||||
if load_appliance(name).applianceId == appliance_id:
|
||||
return name
|
||||
|
||||
raise KeyError(f"Fixture name for appliance ID {appliance_id} does not exist")
|
||||
|
||||
|
||||
def load_appliance(appliance_name: str) -> Appliance:
|
||||
"""Load an Appliance object from a fixture for the given appliance name."""
|
||||
json_string = load_fixture(f"appliances/{appliance_name}.json", DOMAIN)
|
||||
return Appliance.model_validate_json(json_string)
|
||||
|
||||
|
||||
def load_appliance_details(appliance_name: str) -> ApplianceDetails:
|
||||
"""Load an ApplianceDetails object from a fixture for the given appliance name."""
|
||||
json_string = load_fixture(f"appliance_details/{appliance_name}.json", DOMAIN)
|
||||
return ApplianceDetails.model_validate_json(json_string)
|
||||
|
||||
|
||||
def load_appliance_state(appliance_name: str) -> ApplianceState:
|
||||
"""Load an ApplianceState object from a fixture for the given appliance name."""
|
||||
json_string = load_fixture(f"appliance_states/{appliance_name}.json", DOMAIN)
|
||||
return ApplianceState.model_validate_json(json_string)
|
||||
@@ -0,0 +1,140 @@
|
||||
"""Common fixtures for the electrolux tests."""
|
||||
|
||||
from collections.abc import Generator
|
||||
from unittest.mock import AsyncMock, patch
|
||||
|
||||
from electrolux_group_developer_sdk.client.appliance_data_factory import (
|
||||
appliance_data_factory,
|
||||
)
|
||||
from electrolux_group_developer_sdk.client.dto.appliance_state import ApplianceState
|
||||
from electrolux_group_developer_sdk.client.dto.email import Email
|
||||
import pytest
|
||||
|
||||
from homeassistant.components.electrolux.const import CONF_REFRESH_TOKEN, DOMAIN
|
||||
from homeassistant.const import CONF_ACCESS_TOKEN, CONF_API_KEY
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from . import (
|
||||
APPLIANCE_FIXTURES,
|
||||
get_fixture_name,
|
||||
load_appliance,
|
||||
load_appliance_details,
|
||||
load_appliance_state,
|
||||
setup_integration,
|
||||
)
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_setup_entry() -> Generator[AsyncMock]:
|
||||
"""Override async_setup_entry."""
|
||||
with patch(
|
||||
"homeassistant.components.electrolux.async_setup_entry", return_value=True
|
||||
) as mock_setup_entry:
|
||||
yield mock_setup_entry
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
async def init_integration(
|
||||
hass: HomeAssistant, mock_config_entry: MockConfigEntry
|
||||
) -> None:
|
||||
"""Set up Electrolux integration for tests."""
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_config_entry() -> MockConfigEntry:
|
||||
"""Create mocked config entry."""
|
||||
return MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
title="Electrolux",
|
||||
unique_id="mock_user_id",
|
||||
data={
|
||||
CONF_API_KEY: "mock_api_key",
|
||||
CONF_ACCESS_TOKEN: "mock_access_token",
|
||||
CONF_REFRESH_TOKEN: "mock_refresh_token",
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_appliance_client() -> Generator[AsyncMock]:
|
||||
"""Mock the Electrolux Group Developer SDK client."""
|
||||
with (
|
||||
patch(
|
||||
"homeassistant.components.electrolux.ApplianceClient",
|
||||
autospec=True,
|
||||
) as mock_client,
|
||||
patch(
|
||||
"homeassistant.components.electrolux.config_flow.ApplianceClient",
|
||||
new=mock_client,
|
||||
),
|
||||
):
|
||||
client = mock_client.return_value
|
||||
|
||||
def get_appliance_state(appliance_id: str) -> ApplianceState | None:
|
||||
return load_appliance_state(get_fixture_name(appliance_id))
|
||||
|
||||
client.get_appliance_state.side_effect = get_appliance_state
|
||||
|
||||
client.get_user_email.return_value = Email(email="mock@email.com")
|
||||
|
||||
yield client
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_token_manager() -> Generator[AsyncMock]:
|
||||
"""Mock the Electrolux Group Developer SDK token manager."""
|
||||
with (
|
||||
patch(
|
||||
"homeassistant.components.electrolux.TokenManager",
|
||||
autospec=True,
|
||||
) as mock_token_manager,
|
||||
patch(
|
||||
"homeassistant.components.electrolux.config_flow.TokenManager",
|
||||
new=mock_token_manager,
|
||||
),
|
||||
):
|
||||
token_manager = mock_token_manager.return_value
|
||||
|
||||
token_manager.ensure_credentials.return_value = None
|
||||
token_manager.get_user_id.return_value = "mock_user_id"
|
||||
|
||||
yield token_manager
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def appliance_fixture() -> str | None:
|
||||
"""Return the appliance fixture that should be loaded, or None if all appliances should be loaded."""
|
||||
return None
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def appliances(
|
||||
mock_appliance_client: AsyncMock, appliance_fixture: str | None
|
||||
) -> AsyncMock:
|
||||
"""Mock the list of appliances."""
|
||||
appliance_names = []
|
||||
if appliance_fixture is not None:
|
||||
appliance_names.append(appliance_fixture)
|
||||
else:
|
||||
appliance_names.extend(APPLIANCE_FIXTURES)
|
||||
|
||||
appliance_data_list = []
|
||||
for appliance_name in appliance_names:
|
||||
appliance = load_appliance(appliance_name)
|
||||
details = load_appliance_details(appliance_name)
|
||||
state = load_appliance_state(appliance_name)
|
||||
|
||||
appliance_data = appliance_data_factory(
|
||||
appliance=appliance,
|
||||
details=details,
|
||||
state=state,
|
||||
)
|
||||
|
||||
appliance_data_list.append(appliance_data)
|
||||
|
||||
mock_appliance_client.get_appliance_data.return_value = appliance_data_list
|
||||
|
||||
return mock_appliance_client
|
||||
@@ -0,0 +1,331 @@
|
||||
{
|
||||
"applianceInfo": {
|
||||
"serialNumber": "20220412",
|
||||
"pnc": "999011403",
|
||||
"brand": "AEG",
|
||||
"deviceType": "FREESTANDING_BOTTOM_FREEZER",
|
||||
"model": "RCB732E9MX",
|
||||
"variant": "UNDEFINED",
|
||||
"colour": "GREY"
|
||||
},
|
||||
"capabilities": {
|
||||
"alerts": {
|
||||
"access": "read",
|
||||
"type": "alert",
|
||||
"values": {
|
||||
"AIR_SENSOR_BROKEN": {},
|
||||
"AIR_SENSOR_OPEN_CIRCUIT": {},
|
||||
"AIR_SENSOR_SHORT_CIRCUIT": {},
|
||||
"COMMUNICATION_ALARM": {},
|
||||
"DRINK_CHILL_EXPIRED": {},
|
||||
"HUMIDITY_SENSOR_BROKEN": {},
|
||||
"HUMIDITY_SENSOR_OPEN_CIRCUIT": {},
|
||||
"HUMIDITY_SENSOR_SHORT_CIRCUIT": {},
|
||||
"POWER_FAILURE_ALARM": {}
|
||||
}
|
||||
},
|
||||
"applianceMainBoardSwVersion": {
|
||||
"access": "read",
|
||||
"type": "string"
|
||||
},
|
||||
"applianceMode": {
|
||||
"access": "read",
|
||||
"type": "string",
|
||||
"values": {
|
||||
"DEMO": {},
|
||||
"DIAGNOSTIC": {},
|
||||
"NORMAL": {},
|
||||
"SERVICE": {}
|
||||
}
|
||||
},
|
||||
"applianceUiSwVersion": {
|
||||
"access": "read",
|
||||
"type": "string"
|
||||
},
|
||||
"compressorState": {
|
||||
"access": "read",
|
||||
"type": "string",
|
||||
"values": {
|
||||
"OFF": {},
|
||||
"ON": {}
|
||||
}
|
||||
},
|
||||
"coolingValveState": {
|
||||
"access": "read",
|
||||
"type": "string"
|
||||
},
|
||||
"defrostRoutineState": {
|
||||
"access": "read",
|
||||
"type": "string",
|
||||
"values": {
|
||||
"DRIP": {},
|
||||
"HEATER_ON": {},
|
||||
"POST_COOLING": {},
|
||||
"PRE_COOLING": {},
|
||||
"PRE_SOFT": {},
|
||||
"PULL_DOWN": {},
|
||||
"WAIT": {}
|
||||
}
|
||||
},
|
||||
"energySavingMode": {
|
||||
"access": "readwrite",
|
||||
"type": "string",
|
||||
"values": {
|
||||
"OFF": {},
|
||||
"ON": {}
|
||||
}
|
||||
},
|
||||
"fSPN_CRConnectionLost": {
|
||||
"access": "constant",
|
||||
"default": 1,
|
||||
"type": "int"
|
||||
},
|
||||
"freezer": {
|
||||
"alerts": {
|
||||
"access": "read",
|
||||
"type": "alert",
|
||||
"values": {
|
||||
"AIR_SENSOR_BROKEN": {},
|
||||
"AIR_SENSOR_OPEN_CIRCUIT": {},
|
||||
"AIR_SENSOR_SHORT_CIRCUIT": {},
|
||||
"DEFROST_SENSOR_BROKEN": {},
|
||||
"DEFROST_SENSOR_OPEN_CIRCUIT": {},
|
||||
"DEFROST_SENSOR_SHORT_CIRCUIT": {},
|
||||
"DOOR_ALARM": {},
|
||||
"TEMPERATURE_ALARM": {}
|
||||
}
|
||||
},
|
||||
"applianceState": {
|
||||
"access": "read",
|
||||
"type": "string",
|
||||
"values": {
|
||||
"ALARM": {},
|
||||
"DELAYED_START": {},
|
||||
"END_OF_CYCLE": {},
|
||||
"IDLE": {},
|
||||
"OFF": {},
|
||||
"PAUSED": {},
|
||||
"READY_TO_START": {},
|
||||
"RUNNING": {}
|
||||
}
|
||||
},
|
||||
"doorState": {
|
||||
"access": "read",
|
||||
"type": "string",
|
||||
"values": {
|
||||
"CLOSED": {},
|
||||
"OPEN": {}
|
||||
}
|
||||
},
|
||||
"fanState": {
|
||||
"access": "read",
|
||||
"type": "string",
|
||||
"values": {
|
||||
"OFF": {},
|
||||
"ON": {}
|
||||
}
|
||||
},
|
||||
"fastMode": {
|
||||
"access": "readwrite",
|
||||
"type": "string",
|
||||
"values": {
|
||||
"OFF": {},
|
||||
"ON": {}
|
||||
}
|
||||
},
|
||||
"fastModeTimeToEnd": {
|
||||
"access": "readwrite",
|
||||
"type": "number"
|
||||
},
|
||||
"sensorTemperatureC": {
|
||||
"access": "read",
|
||||
"type": "temperature"
|
||||
},
|
||||
"sensorTemperatureF": {
|
||||
"access": "read",
|
||||
"type": "temperature"
|
||||
},
|
||||
"targetTemperatureC": {
|
||||
"access": "readwrite",
|
||||
"default": -18,
|
||||
"max": -16,
|
||||
"min": -24,
|
||||
"step": 2,
|
||||
"type": "temperature"
|
||||
}
|
||||
},
|
||||
"fridge": {
|
||||
"alerts": {
|
||||
"access": "read",
|
||||
"type": "alert",
|
||||
"values": {
|
||||
"AIR_SENSOR_BROKEN": {},
|
||||
"AIR_SENSOR_OPEN_CIRCUIT": {},
|
||||
"AIR_SENSOR_SHORT_CIRCUIT": {},
|
||||
"DEFROST_SENSOR_BROKEN": {},
|
||||
"DEFROST_SENSOR_OPEN_CIRCUIT": {},
|
||||
"DEFROST_SENSOR_SHORT_CIRCUIT": {},
|
||||
"DOOR_ALARM": {},
|
||||
"TEMPERATURE_ALARM": {}
|
||||
}
|
||||
},
|
||||
"applianceState": {
|
||||
"access": "read",
|
||||
"type": "string",
|
||||
"values": {
|
||||
"ALARM": {},
|
||||
"DELAYED_START": {},
|
||||
"END_OF_CYCLE": {},
|
||||
"IDLE": {},
|
||||
"OFF": {},
|
||||
"PAUSED": {},
|
||||
"READY_TO_START": {},
|
||||
"RUNNING": {}
|
||||
}
|
||||
},
|
||||
"doorState": {
|
||||
"access": "read",
|
||||
"type": "string",
|
||||
"values": {
|
||||
"CLOSED": {},
|
||||
"OPEN": {}
|
||||
}
|
||||
},
|
||||
"fanState": {
|
||||
"access": "read",
|
||||
"type": "string",
|
||||
"values": {
|
||||
"OFF": {},
|
||||
"ON": {}
|
||||
}
|
||||
},
|
||||
"fastMode": {
|
||||
"access": "readwrite",
|
||||
"type": "string",
|
||||
"values": {
|
||||
"OFF": {},
|
||||
"ON": {}
|
||||
}
|
||||
},
|
||||
"fastModeTimeToEnd": {
|
||||
"access": "readwrite",
|
||||
"type": "number"
|
||||
},
|
||||
"sensorTemperatureC": {
|
||||
"access": "read",
|
||||
"type": "temperature"
|
||||
},
|
||||
"sensorTemperatureF": {
|
||||
"access": "read",
|
||||
"type": "temperature"
|
||||
},
|
||||
"targetTemperatureC": {
|
||||
"access": "readwrite",
|
||||
"default": 4,
|
||||
"max": 8,
|
||||
"min": 1,
|
||||
"step": 1,
|
||||
"type": "temperature"
|
||||
}
|
||||
},
|
||||
"networkInterface": {
|
||||
"command": {
|
||||
"access": "write",
|
||||
"type": "string",
|
||||
"values": {
|
||||
"APPLIANCE_AUTHORIZE": {},
|
||||
"START": {},
|
||||
"USER_AUTHORIZE": {},
|
||||
"USER_NOT_AUTHORIZE": {}
|
||||
}
|
||||
},
|
||||
"linkQualityIndicator": {
|
||||
"access": "read",
|
||||
"type": "string",
|
||||
"values": {
|
||||
"EXCELLENT": {},
|
||||
"GOOD": {},
|
||||
"POOR": {},
|
||||
"UNDEFINED": {},
|
||||
"VERY_GOOD": {},
|
||||
"VERY_POOR": {}
|
||||
}
|
||||
},
|
||||
"niuSwUpdateCurrentDescription": {
|
||||
"access": "read",
|
||||
"type": "string"
|
||||
},
|
||||
"otaState": {
|
||||
"access": "read",
|
||||
"type": "string",
|
||||
"values": {
|
||||
"DESCRIPTION_AVAILABLE": {},
|
||||
"DESCRIPTION_DOWNLOADING": {},
|
||||
"DESCRIPTION_READY": {},
|
||||
"FW_DOWNLOADING": {},
|
||||
"FW_DOWNLOAD_START": {},
|
||||
"FW_SIGNATURE_CHECK": {},
|
||||
"FW_UPDATE_IN_PROGRESS": {},
|
||||
"IDLE": {},
|
||||
"READY_TO_UPDATE": {},
|
||||
"UPDATE_ABORT": {},
|
||||
"UPDATE_CHECK": {},
|
||||
"UPDATE_ERROR": {},
|
||||
"UPDATE_OK": {},
|
||||
"WAITINGFORAUTHORIZATION": {}
|
||||
}
|
||||
},
|
||||
"startUpCommand": {
|
||||
"access": "write",
|
||||
"type": "string",
|
||||
"values": {
|
||||
"UNINSTALL": {}
|
||||
}
|
||||
},
|
||||
"swAncAndRevision": {
|
||||
"access": "read",
|
||||
"type": "string"
|
||||
},
|
||||
"swVersion": {
|
||||
"access": "read",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"reminderTime": {
|
||||
"access": "readwrite",
|
||||
"default": 1800,
|
||||
"max": 1800,
|
||||
"min": 300,
|
||||
"step": 300,
|
||||
"type": "number",
|
||||
"values": {
|
||||
"0": {},
|
||||
"-1": {}
|
||||
}
|
||||
},
|
||||
"sabbathMode": {
|
||||
"access": "readwrite",
|
||||
"type": "string",
|
||||
"values": {
|
||||
"OFF": {},
|
||||
"ON": {}
|
||||
}
|
||||
},
|
||||
"uiLockMode": {
|
||||
"access": "readwrite",
|
||||
"type": "boolean",
|
||||
"values": {
|
||||
"OFF": {},
|
||||
"ON": {}
|
||||
}
|
||||
},
|
||||
"vacationHolidayMode": {
|
||||
"access": "readwrite",
|
||||
"type": "string",
|
||||
"values": {
|
||||
"OFF": {},
|
||||
"ON": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,389 @@
|
||||
{
|
||||
"applianceInfo": {
|
||||
"serialNumber": "99010299",
|
||||
"pnc": "999008105",
|
||||
"brand": "AEG",
|
||||
"deviceType": "CEILING HOODS",
|
||||
"model": "DDC8271W",
|
||||
"variant": "SLIMWHGLASS8000",
|
||||
"colour": "WHITE"
|
||||
},
|
||||
"capabilities": {
|
||||
"alerts": {
|
||||
"access": "read",
|
||||
"type": "alert",
|
||||
"values": {
|
||||
"ABNORMAL_TEMPERATURE_FAIL_H253": {},
|
||||
"BRUSHLESS_MOTOR_CONTROL_BOARD_ALARMS_H103": {},
|
||||
"CHUI_BOARD_NTC_OUT_OF_RANGE_ALARM_H210": {},
|
||||
"COMM_ALARM_CHC_CHUI_H125": {},
|
||||
"COMM_ALARM_CHPE_CHUI_H348": {},
|
||||
"COMM_ALARM_CHUI_CHC_H201": {},
|
||||
"COMM_ALARM_CHUI_CPE_H202": {},
|
||||
"COMM_FAIL_CHC_HOOD_MOTOR_CONTROL_H101": {},
|
||||
"COOLING_DOWN_WARNING_H252": {},
|
||||
"HUMIDITY_TEMPERATURE_SENSOR_OUT_OF_RANGE_ALARM_H351": {},
|
||||
"MISSING_CONFIGURATION_ALARM_CHPE_H354": {},
|
||||
"MISSING_CONFIG_ALARM_H135": {},
|
||||
"STUCK_KEY_ALARM_H218": {},
|
||||
"SW_COMPAT_ALARM_H127": {},
|
||||
"TVOC_ALARM_H346": {},
|
||||
"TVOC_HUMIDITY_TEMPERATURE_I2C_ALARM_H350": {},
|
||||
"TVOC_R_MOX_OUT_OF_RANGE_ALARM_H349": {},
|
||||
"WRONG_CONFIG_CHECKSUM_PHE_ALARM_H341": {},
|
||||
"WRONG_CONFIG_CRC_ALARM_H133": {},
|
||||
"WRONG_CRC_ON_CONFIG_ALARM_CHPE_H347": {},
|
||||
"WRONG_CRC_ON_CONFIG_ALARM_CHUI_H204": {},
|
||||
"WRONG_SW_COMPAT_ALARM_CHUI_H205": {}
|
||||
}
|
||||
},
|
||||
"applianceLocalTimeOffset": {
|
||||
"access": "read",
|
||||
"type": "number"
|
||||
},
|
||||
"applianceMode": {
|
||||
"access": "read",
|
||||
"type": "string",
|
||||
"values": {
|
||||
"DEMO": {},
|
||||
"DIAGNOSTIC": {},
|
||||
"NORMAL": {}
|
||||
}
|
||||
},
|
||||
"applianceState": {
|
||||
"access": "read",
|
||||
"triggers": [
|
||||
{
|
||||
"action": {
|
||||
"hoodFilterCharcEnable": {
|
||||
"access": "readwrite"
|
||||
}
|
||||
},
|
||||
"condition": {
|
||||
"operand_1": {
|
||||
"operand_1": "value",
|
||||
"operand_2": "OFF",
|
||||
"operator": "eq"
|
||||
},
|
||||
"operand_2": {
|
||||
"operand_1": "value",
|
||||
"operand_2": "IDLE",
|
||||
"operator": "eq"
|
||||
},
|
||||
"operator": "or"
|
||||
}
|
||||
},
|
||||
{
|
||||
"action": {
|
||||
"hoodFilterCharcEnable": {
|
||||
"access": "read"
|
||||
}
|
||||
},
|
||||
"condition": {
|
||||
"operand_1": "value",
|
||||
"operand_2": "RUNNING",
|
||||
"operator": "eq"
|
||||
}
|
||||
},
|
||||
{
|
||||
"action": {
|
||||
"humanCentricLightEventSettings": {
|
||||
"access": "readwrite"
|
||||
}
|
||||
},
|
||||
"condition": {
|
||||
"operand_1": {
|
||||
"operand_1": "value",
|
||||
"operand_2": "OFF",
|
||||
"operator": "eq"
|
||||
},
|
||||
"operand_2": {
|
||||
"operand_1": "value",
|
||||
"operand_2": "IDLE",
|
||||
"operator": "eq"
|
||||
},
|
||||
"operator": "or"
|
||||
}
|
||||
},
|
||||
{
|
||||
"action": {
|
||||
"humanCentricLightEventSettings": {
|
||||
"access": "read"
|
||||
}
|
||||
},
|
||||
"condition": {
|
||||
"operand_1": "value",
|
||||
"operand_2": "RUNNING",
|
||||
"operator": "eq"
|
||||
}
|
||||
}
|
||||
],
|
||||
"type": "string",
|
||||
"values": {
|
||||
"IDLE": {},
|
||||
"OFF": {},
|
||||
"RUNNING": {}
|
||||
}
|
||||
},
|
||||
"autosenseEnabledDisable": {
|
||||
"access": "readwrite",
|
||||
"type": "boolean"
|
||||
},
|
||||
"charcoalFilterReminderThreshold": {
|
||||
"access": "constant",
|
||||
"default": 9600,
|
||||
"type": "int"
|
||||
},
|
||||
"greaseFilterReminderThreshold": {
|
||||
"access": "constant",
|
||||
"default": 2400,
|
||||
"type": "int"
|
||||
},
|
||||
"hoodAutoSwitchOffEvent": {
|
||||
"access": "read",
|
||||
"type": "boolean"
|
||||
},
|
||||
"hoodAutosenseControl": {
|
||||
"access": "readwrite",
|
||||
"type": "boolean"
|
||||
},
|
||||
"hoodCharcFilterTimer": {
|
||||
"access": "read",
|
||||
"type": "number"
|
||||
},
|
||||
"hoodFanLevel": {
|
||||
"access": "readwrite",
|
||||
"triggers": [
|
||||
{
|
||||
"action": {
|
||||
"targetDuration": {
|
||||
"access": "readwrite"
|
||||
}
|
||||
},
|
||||
"condition": {
|
||||
"operand_1": {
|
||||
"operand_1": "value",
|
||||
"operand_2": "BREEZE",
|
||||
"operator": "ge"
|
||||
},
|
||||
"operand_2": {
|
||||
"operand_1": "value",
|
||||
"operand_2": "STEP_3",
|
||||
"operator": "le"
|
||||
},
|
||||
"operator": "and"
|
||||
}
|
||||
},
|
||||
{
|
||||
"action": {
|
||||
"targetDuration": {
|
||||
"access": "read"
|
||||
}
|
||||
},
|
||||
"condition": {
|
||||
"operand_1": {
|
||||
"operand_1": "value",
|
||||
"operand_2": "BREEZE",
|
||||
"operator": "lt"
|
||||
},
|
||||
"operand_2": {
|
||||
"operand_1": "value",
|
||||
"operand_2": "STEP_3",
|
||||
"operator": "gt"
|
||||
},
|
||||
"operator": "or"
|
||||
}
|
||||
}
|
||||
],
|
||||
"type": "string",
|
||||
"values": {
|
||||
"BOOST": {},
|
||||
"BREEZE": {},
|
||||
"OFF": {},
|
||||
"STEP_1": {},
|
||||
"STEP_2": {},
|
||||
"STEP_3": {}
|
||||
}
|
||||
},
|
||||
"hoodFilterCharcEnable": {
|
||||
"access": "readwrite",
|
||||
"type": "string",
|
||||
"values": {
|
||||
"OFF": {},
|
||||
"ON": {}
|
||||
}
|
||||
},
|
||||
"hoodFilterCharcIndication": {
|
||||
"access": "readwrite",
|
||||
"type": "boolean",
|
||||
"values": {
|
||||
"FALSE": {},
|
||||
"TRUE": {}
|
||||
}
|
||||
},
|
||||
"hoodFilterGreaseIndication": {
|
||||
"access": "readwrite",
|
||||
"type": "string",
|
||||
"values": {
|
||||
"FALSE": {},
|
||||
"TRUE": {}
|
||||
}
|
||||
},
|
||||
"hoodGreaseFilterTimer": {
|
||||
"access": "read",
|
||||
"type": "number"
|
||||
},
|
||||
"humanCentricLightEventSettings": {
|
||||
"access": "readwrite",
|
||||
"type": "humanCentLightEnableSettings"
|
||||
},
|
||||
"humanCentricLightEventSettings/event": {
|
||||
"access": "readwrite",
|
||||
"type": "complex",
|
||||
"values": {}
|
||||
},
|
||||
"humanCentricLightEventSettings/number": {
|
||||
"access": "read",
|
||||
"type": "int",
|
||||
"values": {}
|
||||
},
|
||||
"lightColorTemperature": {
|
||||
"access": "readwrite",
|
||||
"max": 100,
|
||||
"min": 0,
|
||||
"step": 1,
|
||||
"type": "number",
|
||||
"values": {
|
||||
"NOT_AVAILABLE": {}
|
||||
}
|
||||
},
|
||||
"lightIntensity": {
|
||||
"access": "readwrite",
|
||||
"max": 100,
|
||||
"min": 0,
|
||||
"step": 1,
|
||||
"type": "number",
|
||||
"values": {
|
||||
"NOT_AVAILABLE": {}
|
||||
}
|
||||
},
|
||||
"localTimeAutomaticMode": {
|
||||
"access": "read",
|
||||
"default": "AUTOMATIC",
|
||||
"type": "string",
|
||||
"values": {
|
||||
"AUTOMATIC": {}
|
||||
}
|
||||
},
|
||||
"maxTimerDuration": {
|
||||
"access": "constant",
|
||||
"default": 60,
|
||||
"type": "number"
|
||||
},
|
||||
"networkInterface": {
|
||||
"autoLocalTimeOffset": {
|
||||
"access": "read",
|
||||
"max": 50400,
|
||||
"min": -43200,
|
||||
"step": 60,
|
||||
"type": "number"
|
||||
},
|
||||
"command": {
|
||||
"access": "write",
|
||||
"type": "string",
|
||||
"values": {
|
||||
"ABORT": {},
|
||||
"APPLIANCE_AUTHORIZE": {},
|
||||
"DOWNLOAD": {},
|
||||
"START": {},
|
||||
"USER_AUTHORIZE": {},
|
||||
"USER_NOT_AUTHORIZE": {}
|
||||
}
|
||||
},
|
||||
"linkQualityIndicator": {
|
||||
"access": "read",
|
||||
"type": "string",
|
||||
"values": {
|
||||
"EXCELLENT": {},
|
||||
"GOOD": {},
|
||||
"POOR": {},
|
||||
"UNDEFINED": {},
|
||||
"VERY_GOOD": {},
|
||||
"VERY_POOR": {}
|
||||
}
|
||||
},
|
||||
"niuSwUpdateCurrentDescription": {
|
||||
"access": "read",
|
||||
"type": "string"
|
||||
},
|
||||
"otaState": {
|
||||
"access": "read",
|
||||
"type": "string",
|
||||
"values": {
|
||||
"DESCRIPTION_AVAILABLE": {},
|
||||
"DESCRIPTION_DOWNLOADING": {},
|
||||
"DESCRIPTION_READY": {},
|
||||
"FW_DOWNLOADING": {},
|
||||
"FW_DOWNLOAD_START": {},
|
||||
"FW_SIGNATURE_CHECK": {},
|
||||
"FW_UPDATE_IN_PROGRESS": {},
|
||||
"IDLE": {},
|
||||
"READY_TO_UPDATE": {},
|
||||
"UPDATE_ABORT": {},
|
||||
"UPDATE_CHECK": {},
|
||||
"UPDATE_ERROR": {},
|
||||
"UPDATE_OK": {},
|
||||
"WAITINGFORAUTHORIZATION": {}
|
||||
}
|
||||
},
|
||||
"startUpCommand": {
|
||||
"access": "write",
|
||||
"type": "string",
|
||||
"values": {
|
||||
"UNINSTALL": {}
|
||||
}
|
||||
},
|
||||
"swAncAndRevision": {
|
||||
"access": "read",
|
||||
"type": "string"
|
||||
},
|
||||
"swVersion": {
|
||||
"access": "read",
|
||||
"type": "string"
|
||||
},
|
||||
"timeZoneDatabaseName": {
|
||||
"access": "readwrite",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"remoteControl": {
|
||||
"access": "read",
|
||||
"type": "string",
|
||||
"values": {
|
||||
"DISABLED": {},
|
||||
"ENABLED": {},
|
||||
"NOT_SAFETY_RELEVANT_ENABLED": {},
|
||||
"TEMPORARY_LOCKED": {}
|
||||
}
|
||||
},
|
||||
"soundVolume": {
|
||||
"access": "readwrite",
|
||||
"type": "number",
|
||||
"values": {
|
||||
"0": {},
|
||||
"1": {}
|
||||
}
|
||||
},
|
||||
"targetDuration": {
|
||||
"access": "readwrite",
|
||||
"max": 36000,
|
||||
"min": 0,
|
||||
"step": 60,
|
||||
"type": "number"
|
||||
},
|
||||
"timeToEnd": {
|
||||
"access": "read",
|
||||
"type": "number"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,672 @@
|
||||
{
|
||||
"applianceInfo": {
|
||||
"serialNumber": "99907833",
|
||||
"pnc": "999008120",
|
||||
"brand": "AEG",
|
||||
"deviceType": "BUILT IN HOB",
|
||||
"model": "TU84CF43CB",
|
||||
"variant": "N/A",
|
||||
"colour": "BLACK"
|
||||
},
|
||||
"capabilities": {
|
||||
"alerts": {
|
||||
"access": "read",
|
||||
"type": "alert",
|
||||
"values": {
|
||||
"CKG_ZONE_FAIL_E601": {},
|
||||
"CKG_ZONE_FAIL_E602": {},
|
||||
"CKG_ZONE_FAIL_E603": {},
|
||||
"CKG_ZONE_FAIL_E604": {},
|
||||
"CKG_ZONE_FAIL_E605": {},
|
||||
"CKG_ZONE_FAIL_E606": {},
|
||||
"CKG_ZONE_FAIL_E611": {},
|
||||
"CKG_ZONE_FAIL_E612": {},
|
||||
"CKG_ZONE_FAIL_E613": {},
|
||||
"CKG_ZONE_FAIL_E614": {},
|
||||
"CKG_ZONE_FAIL_E615": {},
|
||||
"CKG_ZONE_FAIL_E616": {},
|
||||
"CKG_ZONE_FAIL_E621": {},
|
||||
"CKG_ZONE_FAIL_E622": {},
|
||||
"CKG_ZONE_FAIL_E623": {},
|
||||
"CKG_ZONE_FAIL_E624": {},
|
||||
"CKG_ZONE_FAIL_E625": {},
|
||||
"CKG_ZONE_FAIL_E626": {},
|
||||
"CKG_ZONE_FAIL_E651": {},
|
||||
"CKG_ZONE_FAIL_E652": {},
|
||||
"CKG_ZONE_FAIL_E653": {},
|
||||
"CKG_ZONE_FAIL_E654": {},
|
||||
"CKG_ZONE_FAIL_E655": {},
|
||||
"CKG_ZONE_FAIL_E656": {},
|
||||
"CKG_ZONE_FAN_FAIL_E701": {},
|
||||
"CKG_ZONE_FAN_FAIL_E702": {},
|
||||
"CKG_ZONE_FAN_FAIL_E703": {},
|
||||
"CKG_ZONE_FAN_FAIL_E704": {},
|
||||
"CKG_ZONE_FAN_FAIL_E705": {},
|
||||
"CKG_ZONE_FAN_FAIL_E706": {},
|
||||
"CKG_ZONE_FAN_FAIL_E711": {},
|
||||
"CKG_ZONE_FAN_FAIL_E712": {},
|
||||
"CKG_ZONE_FAN_FAIL_E713": {},
|
||||
"CKG_ZONE_FAN_FAIL_E714": {},
|
||||
"CKG_ZONE_FAN_FAIL_E715": {},
|
||||
"CKG_ZONE_FAN_FAIL_E716": {},
|
||||
"CKG_ZONE_SW_ERROR_E011": {},
|
||||
"CKG_ZONE_SW_ERROR_E012": {},
|
||||
"CKG_ZONE_SW_ERROR_E013": {},
|
||||
"CKG_ZONE_SW_ERROR_E014": {},
|
||||
"CKG_ZONE_SW_ERROR_E015": {},
|
||||
"CKG_ZONE_SW_ERROR_E016": {},
|
||||
"CKG_ZONE_TEMP_SENS_FAIL_E401": {},
|
||||
"CKG_ZONE_TEMP_SENS_FAIL_E402": {},
|
||||
"CKG_ZONE_TEMP_SENS_FAIL_E403": {},
|
||||
"CKG_ZONE_TEMP_SENS_FAIL_E404": {},
|
||||
"CKG_ZONE_TEMP_SENS_FAIL_E405": {},
|
||||
"CKG_ZONE_TEMP_SENS_FAIL_E406": {},
|
||||
"CKG_ZONE_TEMP_SENS_FAIL_E411": {},
|
||||
"CKG_ZONE_TEMP_SENS_FAIL_E412": {},
|
||||
"CKG_ZONE_TEMP_SENS_FAIL_E413": {},
|
||||
"CKG_ZONE_TEMP_SENS_FAIL_E414": {},
|
||||
"CKG_ZONE_TEMP_SENS_FAIL_E415": {},
|
||||
"CKG_ZONE_TEMP_SENS_FAIL_E416": {},
|
||||
"CKG_ZONE_TEMP_SENS_FAIL_E431": {},
|
||||
"CKG_ZONE_TEMP_SENS_FAIL_E432": {},
|
||||
"CKG_ZONE_TEMP_SENS_FAIL_E433": {},
|
||||
"CKG_ZONE_TEMP_SENS_FAIL_E434": {},
|
||||
"CKG_ZONE_TEMP_SENS_FAIL_E435": {},
|
||||
"CKG_ZONE_TEMP_SENS_FAIL_E436": {},
|
||||
"CKG_ZONE_TEMP_SENS_FAIL_E441": {},
|
||||
"CKG_ZONE_TEMP_SENS_FAIL_E442": {},
|
||||
"CKG_ZONE_TEMP_SENS_FAIL_E443": {},
|
||||
"CKG_ZONE_TEMP_SENS_FAIL_E444": {},
|
||||
"CKG_ZONE_TEMP_SENS_FAIL_E445": {},
|
||||
"CKG_ZONE_TEMP_SENS_FAIL_E446": {},
|
||||
"CKG_ZONE_TEMP_TOO_HIGH_E421": {},
|
||||
"CKG_ZONE_TEMP_TOO_HIGH_E422": {},
|
||||
"CKG_ZONE_TEMP_TOO_HIGH_E423": {},
|
||||
"CKG_ZONE_TEMP_TOO_HIGH_E424": {},
|
||||
"CKG_ZONE_TEMP_TOO_HIGH_E425": {},
|
||||
"CKG_ZONE_TEMP_TOO_HIGH_E426": {},
|
||||
"DATA_CRC_ERROR_E661": {},
|
||||
"DATA_CRC_ERROR_E662": {},
|
||||
"DATA_CRC_ERROR_E663": {},
|
||||
"DISPLAY_ELECTRONIC_FAIL_E061": {},
|
||||
"DISPLAY_ELECTRONIC_FAIL_E081": {},
|
||||
"DISPLAY_ELECTRONIC_FAIL_E091": {},
|
||||
"DISPLAY_ELECTRONIC_FAIL_E0C1": {},
|
||||
"DISPLAY_NOT_PROPER_WORK_E9F1": {},
|
||||
"DISPLAY_TEMP_TOO_HIGH_E0A1": {},
|
||||
"HOOD_FAIL_E518": {},
|
||||
"HOOD_FAIL_E527": {},
|
||||
"HOOD_FAIL_E528": {},
|
||||
"HOOD_FAIL_E8B1": {},
|
||||
"HOOD_MC_BOARD_ALARM_E537": {},
|
||||
"HOOD_SW_FAIL_E517": {},
|
||||
"HUI_PIXIE_COMM_ALARM_E8B7": {},
|
||||
"INTERNAL_COMM_FAIL_E821": {},
|
||||
"INTERNAL_COMM_FAIL_E822": {},
|
||||
"INTERNAL_COMM_FAIL_E823": {},
|
||||
"INTERNAL_COMM_FAIL_E824": {},
|
||||
"INTERNAL_COMM_FAIL_E825": {},
|
||||
"INTERNAL_COMM_FAIL_E826": {},
|
||||
"INTERNAL_COMM_FAIL_E831": {},
|
||||
"INTERNAL_COMM_FAIL_E841": {},
|
||||
"INTERNAL_COMM_FAIL_E851": {},
|
||||
"INTERNAL_COMM_FAIL_E861": {},
|
||||
"INTERNAL_COMM_FAIL_E871": {},
|
||||
"KEY_ERROR_FMEA_E9D1": {},
|
||||
"KNOB_ERROR_E951": {},
|
||||
"MAINS_CONNECTION_ERROR_E311": {},
|
||||
"MAINS_CONNECTION_ERROR_E312": {},
|
||||
"MAINS_CONNECTION_ERROR_E313": {},
|
||||
"MAINS_CONNECTION_ERROR_E314": {},
|
||||
"MAINS_CONNECTION_ERROR_E315": {},
|
||||
"MAINS_CONNECTION_ERROR_E316": {},
|
||||
"MAINS_RELAY_FAIL_E641": {},
|
||||
"MAINS_RELAY_FAIL_E642": {},
|
||||
"MAINS_RELAY_FAIL_E643": {},
|
||||
"MAINS_RELAY_FAIL_E644": {},
|
||||
"MAINS_RELAY_FAIL_E645": {},
|
||||
"MAINS_RELAY_FAIL_E646": {},
|
||||
"MAINS_VOLTAGE_TOO_LOW_E321": {},
|
||||
"MAINS_VOLTAGE_TOO_LOW_E322": {},
|
||||
"MAINS_VOLTAGE_TOO_LOW_E323": {},
|
||||
"MAINS_VOLTAGE_TOO_LOW_E324": {},
|
||||
"MAINS_VOLTAGE_TOO_LOW_E325": {},
|
||||
"MAINS_VOLTAGE_TOO_LOW_E326": {},
|
||||
"SENSEBOIL_FAIL_E272": {},
|
||||
"SENSEBOIL_FAIL_E282": {},
|
||||
"SENSEBOIL_FAIL_E8A2": {},
|
||||
"SW_ERROR_E021": {},
|
||||
"SW_ERROR_E031": {},
|
||||
"SW_ERROR_E041": {},
|
||||
"SW_ERROR_E051": {},
|
||||
"TOUCH_NOT_PROPER_WORK_E911": {},
|
||||
"TOUCH_NOT_PROPER_WORK_E921": {},
|
||||
"TOUCH_NOT_PROPER_WORK_E931": {},
|
||||
"TOUCH_NOT_PROPER_WORK_E941": {},
|
||||
"TOUCH_NOT_PROPER_WORK_E961": {},
|
||||
"TOUCH_NOT_PROPER_WORK_E971": {},
|
||||
"TOUCH_NOT_PROPER_WORK_E981": {},
|
||||
"TOUCH_NOT_PROPER_WORK_E991": {},
|
||||
"TOUCH_NOT_PROPER_WORK_E9A1": {},
|
||||
"TOUCH_NOT_PROPER_WORK_E9B1": {},
|
||||
"TOUCH_NOT_PROPER_WORK_E9C1": {},
|
||||
"WI-FI_FAIL_E141": {},
|
||||
"WI-FI_FAIL_E181": {},
|
||||
"ZONE_ILLUMINATION_FAIL_E201": {},
|
||||
"ZONE_ILLUMINATION_FAIL_E211": {},
|
||||
"ZONE_ILLUMINATION_FAIL_E221": {},
|
||||
"ZONE_ILLUMINATION_FAIL_E231": {},
|
||||
"ZONE_ILLUMINATION_FAIL_E241": {}
|
||||
}
|
||||
},
|
||||
"applianceMode": {
|
||||
"access": "read",
|
||||
"type": "string",
|
||||
"values": {
|
||||
"DEMO": {},
|
||||
"DIAGNOSTIC": {},
|
||||
"NORMAL": {},
|
||||
"SERVICE": {}
|
||||
}
|
||||
},
|
||||
"applianceState": {
|
||||
"access": "read",
|
||||
"type": "string",
|
||||
"values": {
|
||||
"ALARM": {},
|
||||
"IDLE": {},
|
||||
"OFF": {},
|
||||
"PAUSED": {},
|
||||
"RUNNING": {}
|
||||
}
|
||||
},
|
||||
"childLock": {
|
||||
"access": "readwrite",
|
||||
"triggers": [
|
||||
{
|
||||
"action": {
|
||||
"$self": {
|
||||
"access": "read"
|
||||
}
|
||||
},
|
||||
"condition": {
|
||||
"operand_1": "value",
|
||||
"operand_2": "ENABLED",
|
||||
"operator": "eq"
|
||||
}
|
||||
},
|
||||
{
|
||||
"action": {
|
||||
"$self": {
|
||||
"access": "readwrite"
|
||||
}
|
||||
},
|
||||
"condition": {
|
||||
"operand_1": "value",
|
||||
"operand_2": "DISABLED",
|
||||
"operator": "eq"
|
||||
}
|
||||
}
|
||||
],
|
||||
"type": "boolean",
|
||||
"values": {
|
||||
"DISABLED": {},
|
||||
"ENABLED": {}
|
||||
}
|
||||
},
|
||||
"cpv": {
|
||||
"access": "read",
|
||||
"type": "string"
|
||||
},
|
||||
"hobHood": {
|
||||
"hobToHoodFanSpeed": {
|
||||
"access": "readwrite",
|
||||
"type": "string",
|
||||
"values": {
|
||||
"BOOST": {},
|
||||
"BREEZE": {},
|
||||
"DRYING_CYCLE": {},
|
||||
"OFF": {},
|
||||
"STEP_1": {},
|
||||
"STEP_2": {},
|
||||
"STEP_3": {}
|
||||
}
|
||||
},
|
||||
"hobToHoodMode": {
|
||||
"access": "readwrite",
|
||||
"type": "string",
|
||||
"values": {
|
||||
"H1": {},
|
||||
"H2": {},
|
||||
"H3": {},
|
||||
"H4": {}
|
||||
}
|
||||
},
|
||||
"hobToHoodState": {
|
||||
"access": "readwrite",
|
||||
"type": "string",
|
||||
"values": {
|
||||
"AUTOMATIC": {},
|
||||
"AUTO_SUSPEND": {
|
||||
"disabled": true
|
||||
},
|
||||
"MANUAL": {}
|
||||
}
|
||||
},
|
||||
"hoodDryingCycle": {
|
||||
"access": "readwrite",
|
||||
"type": "string",
|
||||
"values": {
|
||||
"KEY_ERROR": {
|
||||
"disabled": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"hoodFilterCharcIndication": {
|
||||
"access": "readwrite",
|
||||
"type": "boolean",
|
||||
"values": {
|
||||
"FALSE": {},
|
||||
"TRUE": {}
|
||||
}
|
||||
},
|
||||
"hoodFilterGreaseIndication": {
|
||||
"access": "readwrite",
|
||||
"type": "boolean",
|
||||
"values": {
|
||||
"FALSE": {},
|
||||
"TRUE": {}
|
||||
}
|
||||
},
|
||||
"targetDuration": {
|
||||
"access": "readwrite",
|
||||
"default": 0,
|
||||
"max": 5940,
|
||||
"min": 0,
|
||||
"step": 60,
|
||||
"type": "number"
|
||||
},
|
||||
"timeToEnd": {
|
||||
"access": "read",
|
||||
"type": "number"
|
||||
},
|
||||
"windowNotification": {
|
||||
"access": "read",
|
||||
"type": "string",
|
||||
"values": {
|
||||
"NONE": {},
|
||||
"OPENING_REQUIRED": {}
|
||||
}
|
||||
}
|
||||
},
|
||||
"hobZone1": {
|
||||
"heatingQualitativeLevel": {
|
||||
"access": "read",
|
||||
"default": 0,
|
||||
"max": 9,
|
||||
"min": 0,
|
||||
"step": 1,
|
||||
"type": "number",
|
||||
"values": {
|
||||
"POWER_LEVEL_P": {}
|
||||
}
|
||||
},
|
||||
"hobCoil": {
|
||||
"access": "read",
|
||||
"type": "number"
|
||||
},
|
||||
"hobMaxPowerLevel": {
|
||||
"access": "read",
|
||||
"type": "number"
|
||||
},
|
||||
"hobPotDetected": {
|
||||
"access": "read",
|
||||
"type": "string",
|
||||
"values": {
|
||||
"NO_POT_IDLE": {},
|
||||
"NO_POT_RUNNING": {},
|
||||
"POT_IDLE": {},
|
||||
"POT_RUNNING": {}
|
||||
}
|
||||
},
|
||||
"residualHeatState": {
|
||||
"access": "read",
|
||||
"type": "string",
|
||||
"values": {
|
||||
"HIGH": {},
|
||||
"LOW": {},
|
||||
"MIDDLE": {},
|
||||
"NO": {}
|
||||
}
|
||||
},
|
||||
"runningTime": {
|
||||
"access": "read",
|
||||
"default": 0,
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
"hobZone2": {
|
||||
"heatingQualitativeLevel": {
|
||||
"access": "read",
|
||||
"default": 0,
|
||||
"max": 9,
|
||||
"min": 0,
|
||||
"step": 1,
|
||||
"type": "number",
|
||||
"values": {
|
||||
"POWER_LEVEL_P": {}
|
||||
}
|
||||
},
|
||||
"hobCoil": {
|
||||
"access": "read",
|
||||
"type": "number"
|
||||
},
|
||||
"hobMaxPowerLevel": {
|
||||
"access": "read",
|
||||
"type": "number"
|
||||
},
|
||||
"hobPotDetected": {
|
||||
"access": "read",
|
||||
"type": "string",
|
||||
"values": {
|
||||
"NO_POT_IDLE": {},
|
||||
"NO_POT_RUNNING": {},
|
||||
"POT_IDLE": {},
|
||||
"POT_RUNNING": {}
|
||||
}
|
||||
},
|
||||
"residualHeatState": {
|
||||
"access": "read",
|
||||
"type": "string",
|
||||
"values": {
|
||||
"HIGH": {},
|
||||
"LOW": {},
|
||||
"MIDDLE": {},
|
||||
"NO": {}
|
||||
}
|
||||
},
|
||||
"runningTime": {
|
||||
"access": "read",
|
||||
"default": 0,
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
"hobZone3": {
|
||||
"heatingQualitativeLevel": {
|
||||
"access": "read",
|
||||
"default": 0,
|
||||
"max": 9,
|
||||
"min": 0,
|
||||
"step": 1,
|
||||
"type": "number",
|
||||
"values": {
|
||||
"POWER_LEVEL_P": {}
|
||||
}
|
||||
},
|
||||
"hobCoil": {
|
||||
"access": "read",
|
||||
"type": "number"
|
||||
},
|
||||
"hobMaxPowerLevel": {
|
||||
"access": "read",
|
||||
"type": "number"
|
||||
},
|
||||
"hobPotDetected": {
|
||||
"access": "read",
|
||||
"type": "string",
|
||||
"values": {
|
||||
"NO_POT_IDLE": {},
|
||||
"NO_POT_RUNNING": {},
|
||||
"POT_IDLE": {},
|
||||
"POT_RUNNING": {}
|
||||
}
|
||||
},
|
||||
"residualHeatState": {
|
||||
"access": "read",
|
||||
"type": "string",
|
||||
"values": {
|
||||
"HIGH": {},
|
||||
"LOW": {},
|
||||
"MIDDLE": {},
|
||||
"NO": {}
|
||||
}
|
||||
},
|
||||
"runningTime": {
|
||||
"access": "read",
|
||||
"default": 0,
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
"hobZone4": {
|
||||
"heatingQualitativeLevel": {
|
||||
"access": "read",
|
||||
"default": 0,
|
||||
"max": 9,
|
||||
"min": 0,
|
||||
"step": 1,
|
||||
"type": "number",
|
||||
"values": {
|
||||
"POWER_LEVEL_P": {}
|
||||
}
|
||||
},
|
||||
"hobCoil": {
|
||||
"access": "read",
|
||||
"type": "number"
|
||||
},
|
||||
"hobMaxPowerLevel": {
|
||||
"access": "read",
|
||||
"type": "number"
|
||||
},
|
||||
"hobPotDetected": {
|
||||
"access": "read",
|
||||
"type": "string",
|
||||
"values": {
|
||||
"NO_POT_IDLE": {},
|
||||
"NO_POT_RUNNING": {},
|
||||
"POT_IDLE": {},
|
||||
"POT_RUNNING": {}
|
||||
}
|
||||
},
|
||||
"residualHeatState": {
|
||||
"access": "read",
|
||||
"type": "string",
|
||||
"values": {
|
||||
"HIGH": {},
|
||||
"LOW": {},
|
||||
"MIDDLE": {},
|
||||
"NO": {}
|
||||
}
|
||||
},
|
||||
"runningTime": {
|
||||
"access": "read",
|
||||
"default": 0,
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
"hobZone7": {
|
||||
"heatingQualitativeLevel": {
|
||||
"access": "read",
|
||||
"default": 0,
|
||||
"max": 9,
|
||||
"min": 0,
|
||||
"step": 1,
|
||||
"type": "number",
|
||||
"values": {
|
||||
"POWER_LEVEL_ASSISTED": {},
|
||||
"POWER_LEVEL_MELTING": {},
|
||||
"POWER_LEVEL_P": {}
|
||||
}
|
||||
},
|
||||
"hobCoil": {
|
||||
"access": "read",
|
||||
"type": "number"
|
||||
},
|
||||
"hobMaxPowerLevel": {
|
||||
"access": "read",
|
||||
"type": "number"
|
||||
},
|
||||
"hobPotDetected": {
|
||||
"access": "read",
|
||||
"type": "string",
|
||||
"values": {
|
||||
"NO_POT_IDLE": {},
|
||||
"NO_POT_RUNNING": {},
|
||||
"POT_IDLE": {},
|
||||
"POT_RUNNING": {}
|
||||
}
|
||||
},
|
||||
"residualHeatState": {
|
||||
"access": "read",
|
||||
"type": "string",
|
||||
"values": {
|
||||
"HIGH": {},
|
||||
"LOW": {},
|
||||
"MIDDLE": {},
|
||||
"NO": {}
|
||||
}
|
||||
},
|
||||
"runningTime": {
|
||||
"access": "read",
|
||||
"default": 0,
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
"keyModel": {
|
||||
"access": "constant",
|
||||
"default": "285_145_030",
|
||||
"type": "string"
|
||||
},
|
||||
"networkInterface": {
|
||||
"command": {
|
||||
"access": "write",
|
||||
"type": "string",
|
||||
"values": {
|
||||
"APPLIANCE_AUTHORIZE": {},
|
||||
"START": {},
|
||||
"USER_AUTHORIZE": {},
|
||||
"USER_NOT_AUTHORIZE": {}
|
||||
}
|
||||
},
|
||||
"linkQualityIndicator": {
|
||||
"access": "read",
|
||||
"type": "string",
|
||||
"values": {
|
||||
"EXCELLENT": {},
|
||||
"GOOD": {},
|
||||
"POOR": {},
|
||||
"UNDEFINED": {},
|
||||
"VERY_GOOD": {},
|
||||
"VERY_POOR": {}
|
||||
}
|
||||
},
|
||||
"niuSwUpdateCurrentDescription": {
|
||||
"access": "read",
|
||||
"type": "string"
|
||||
},
|
||||
"oTA3CurrentVersion": {
|
||||
"access": "read",
|
||||
"type": "string"
|
||||
},
|
||||
"oTA3LastResult": {
|
||||
"access": "read",
|
||||
"type": "string",
|
||||
"values": {
|
||||
"BKP_BUNDLE_CRC_ERROR": {},
|
||||
"BKP_BUNDLE_INCOMPATIBLE": {},
|
||||
"BKP_BUNDLE_MALFORMED": {},
|
||||
"BKP_BUNDLE_MISSING": {},
|
||||
"BKP_BUNDLE_PROG_ATTEMPS_OVERRUN": {},
|
||||
"ERROR_APPLIANCE_ABORTED": {},
|
||||
"ERROR_BACKUP_RESTORED": {},
|
||||
"ERROR_TIMEOUT_FINALIZATION": {},
|
||||
"ERROR_TIMEOUT_GENERIC": {},
|
||||
"ERROR_TIMEOUT_MACHINE_READY_WAIT": {},
|
||||
"ERROR_TIMEOUT_PROGRAMMING": {},
|
||||
"ERROR_TIMEOUT_SYSTEM_READY_WAIT": {},
|
||||
"NEW_BUNDLE_CRC_ERROR": {},
|
||||
"NEW_BUNDLE_INCOMPATIBLE": {},
|
||||
"NEW_BUNDLE_MALFORMED": {},
|
||||
"NEW_BUNDLE_MISSING": {},
|
||||
"NEW_BUNDLE_PROG_ATTEMPS_OVERRUN": {},
|
||||
"NONE": {},
|
||||
"PROGRAMMING_EB_NOT_IN_BOOT": {},
|
||||
"PROGRAMMING_EXECUTION": {},
|
||||
"SUCCESS": {}
|
||||
}
|
||||
},
|
||||
"oTA3State": {
|
||||
"access": "read",
|
||||
"type": "string",
|
||||
"values": {
|
||||
"DOWNLOAD": {},
|
||||
"FINALIZATION": {},
|
||||
"IDLE": {},
|
||||
"PROGRAMMING": {},
|
||||
"SYSTEM_READY": {},
|
||||
"UNKNOWN": {},
|
||||
"UNSUPPORTED": {},
|
||||
"UPDATE_CHECK": {},
|
||||
"WAIT_MACHINE_READY": {},
|
||||
"WAIT_USER_AUTH": {}
|
||||
}
|
||||
},
|
||||
"oTA3TargetVersion": {
|
||||
"access": "read",
|
||||
"type": "string"
|
||||
},
|
||||
"otaState": {
|
||||
"access": "read",
|
||||
"type": "string",
|
||||
"values": {
|
||||
"DESCRIPTION_AVAILABLE": {},
|
||||
"DESCRIPTION_DOWNLOADING": {},
|
||||
"DESCRIPTION_READY": {},
|
||||
"FW_DOWNLOADING": {},
|
||||
"FW_DOWNLOAD_START": {},
|
||||
"FW_SIGNATURE_CHECK": {},
|
||||
"FW_UPDATE_IN_PROGRESS": {},
|
||||
"IDLE": {},
|
||||
"READY_TO_UPDATE": {},
|
||||
"UPDATE_ABORT": {},
|
||||
"UPDATE_CHECK": {},
|
||||
"UPDATE_ERROR": {},
|
||||
"UPDATE_OK": {},
|
||||
"WAITINGFORAUTHORIZATION": {}
|
||||
}
|
||||
},
|
||||
"startUpCommand": {
|
||||
"access": "write",
|
||||
"type": "string",
|
||||
"values": {
|
||||
"UNINSTALL": {}
|
||||
}
|
||||
},
|
||||
"swAncAndRevision": {
|
||||
"access": "read",
|
||||
"type": "string"
|
||||
},
|
||||
"swVersion": {
|
||||
"access": "read",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"remoteControl": {
|
||||
"access": "read",
|
||||
"type": "string",
|
||||
"values": {
|
||||
"DISABLED": {},
|
||||
"ENABLED": {},
|
||||
"NOT_SAFETY_RELEVANT_ENABLED": {},
|
||||
"TEMPORARY_LOCKED": {}
|
||||
}
|
||||
},
|
||||
"uiLockMode": {
|
||||
"access": "read",
|
||||
"type": "boolean",
|
||||
"values": {
|
||||
"OFF": {},
|
||||
"ON": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,874 @@
|
||||
{
|
||||
"applianceInfo": {
|
||||
"serialNumber": "11112225",
|
||||
"pnc": "949288049",
|
||||
"brand": "AEG",
|
||||
"deviceType": "BUILT-IN OVEN",
|
||||
"model": "NBX7P631SB",
|
||||
"variant": "PIZZA-HOTAIRFAN+RING",
|
||||
"colour": "BLACK"
|
||||
},
|
||||
"capabilities": {
|
||||
"alerts": {
|
||||
"access": "read",
|
||||
"type": "alert",
|
||||
"values": {
|
||||
"AD_CONVERTER_REFERENCE_ALARM": {},
|
||||
"BACKLIGHT_ALARM": {},
|
||||
"BOARD_TEMPERATURE_ALARM": {},
|
||||
"COMMUNICATION_ALARM_BETWEEN_TWO_CONTROLLERS": {},
|
||||
"COMMUNICATION_ALARM_OUI_OC": {},
|
||||
"CONFIGURATION_CHECKSUM_ALARM": {},
|
||||
"CONFIGURATION_COHERENT_ALARM": {},
|
||||
"CONFIGURATION_COMPATIBILITY_ALARM": {},
|
||||
"COOKING_FAN_CONFIG_ALARM": {},
|
||||
"DATA_FLASH_ALARM_UI": {},
|
||||
"DOOR_LOCK_ACTUATOR": {},
|
||||
"DOOR_LOCK_CONFIGURATION_ALARM": {},
|
||||
"DOOR_LOCK_SENSOR_ALARM": {},
|
||||
"ELECTRONIC_CLIXON_ALARM": {},
|
||||
"FIX_SENSOR_DETECTION_ALARM": {},
|
||||
"FOOD_PROBE_COMMUNICATION_ALARM": {},
|
||||
"FOOD_PROBE_CONFIGURATION_ALARM": {},
|
||||
"FUNCTION_SELECTOR_NOT_CONNECTED": {},
|
||||
"HMI-TOUCH_BOARD_FMEA_ALARM": {},
|
||||
"HMI-TOUCH_BOARD_SERIAL_COMMUNICATION_ALARM": {},
|
||||
"HOB_OVEN_COMMUNICATION_ALARM": {},
|
||||
"HOB_OVEN_POWER_MANAGEMENT_ALARM": {},
|
||||
"HUMIDITY_SENSOR_OUT_OF_RANGE_ALARM": {},
|
||||
"INTERNAL_ERROR": {},
|
||||
"LIB_FMEA_ALARM_ROTARY_GRAB": {},
|
||||
"MACS_COMMNUNICATION_ERROR": {},
|
||||
"MEAT_PROBE_OUT_OF_RANGE_ALARM": {},
|
||||
"NETVM_COMMUNICATION_ALARM": {},
|
||||
"NIUX_COMMUNICATION_ALARM": {},
|
||||
"NIUX_ONBOARDING_FAILED_ALARM": {},
|
||||
"NTC_OUT_OF_RANGE_ALARM": {},
|
||||
"OTA_FAILURE": {},
|
||||
"PERIPHERAL_INIT_ALARM_ROTARY_SERIAL_COMMUNICATION_ALARM": {},
|
||||
"PERIPHERAL_RUNTIME_ALARM_ROTARY_BIT_ENCODER_ALARM": {},
|
||||
"POWER_ALARM": {},
|
||||
"PT500_OUT_OF_RANGE_ALARM": {},
|
||||
"PT500_STEAM_OUT_OF_RANGE_ALARM": {},
|
||||
"PTO_COMMUNICATION_ALARM_OUI_PTO": {},
|
||||
"PTO_CONFIGURATION_CHECKSUM_ALARM": {},
|
||||
"PTO_KEY_ALARM": {},
|
||||
"PTO_LIB_FMEA_ALARM": {},
|
||||
"PYR_HOB_ALARM": {},
|
||||
"ROTARY_TOUCH_ALARM": {},
|
||||
"RTC_ALARM": {},
|
||||
"RTC_OUT_OF_RANGE_ALARM": {},
|
||||
"SMART_AD_CALIBRATION_RUNNING_ERROR": {},
|
||||
"SMART_ERROR_UNKNOWN": {},
|
||||
"SMART_INVALID_CONFIGURATION_ERROR": {},
|
||||
"SMART_NO_START_TEMPERATURE": {},
|
||||
"SMART_READ_FLASH_ERROR": {},
|
||||
"SOFTWARE_COMPATIBILITY_CODE_ALARM": {},
|
||||
"STEAM_MAGNETRON_NTC": {},
|
||||
"TOO_HIGH_TEMPERATURE_ALARM": {},
|
||||
"TOUCHSCREEN_DRIVER_ALARM": {},
|
||||
"TOUCH_KEY_1_ALARM": {},
|
||||
"TOUCH_KEY_ALARM": {},
|
||||
"TRIAC_ALARM": {},
|
||||
"UNKNOWN_STATE_ERROR": {},
|
||||
"USB_CAMERA_DISCONNECTED_ALARM": {},
|
||||
"WATER_LEVEL_SENSOR_IN_STEAMER_OUT_OF_RANGE_ALARM": {},
|
||||
"WATER_LEVEL_SENSOR_IN_STEAM_TANK_DRAWER_OUT_OF_RANGE_ALARM": {},
|
||||
"WIFI_SIGNAL_MISS_ALARM": {}
|
||||
}
|
||||
},
|
||||
"applianceState": {
|
||||
"access": "read",
|
||||
"triggers": [
|
||||
{
|
||||
"action": {
|
||||
"executeCommand": {
|
||||
"access": "write",
|
||||
"values": {
|
||||
"STOPRESET": {}
|
||||
}
|
||||
}
|
||||
},
|
||||
"condition": {
|
||||
"operand_1": "value",
|
||||
"operand_2": "RUNNING",
|
||||
"operator": "eq"
|
||||
}
|
||||
},
|
||||
{
|
||||
"action": {
|
||||
"executeCommand": {
|
||||
"access": "write",
|
||||
"values": {
|
||||
"STOPRESET": {}
|
||||
}
|
||||
}
|
||||
},
|
||||
"condition": {
|
||||
"operand_1": "value",
|
||||
"operand_2": "PAUSED",
|
||||
"operator": "eq"
|
||||
}
|
||||
},
|
||||
{
|
||||
"action": {
|
||||
"executeCommand": {
|
||||
"access": "write",
|
||||
"values": {
|
||||
"START": {}
|
||||
}
|
||||
}
|
||||
},
|
||||
"condition": {
|
||||
"operand_1": "value",
|
||||
"operand_2": "READY_TO_START",
|
||||
"operator": "eq"
|
||||
}
|
||||
},
|
||||
{
|
||||
"action": {
|
||||
"$self": {
|
||||
"access": "read"
|
||||
}
|
||||
},
|
||||
"condition": {
|
||||
"operand_1": "value",
|
||||
"operand_2": "ALARM",
|
||||
"operator": "eq"
|
||||
}
|
||||
},
|
||||
{
|
||||
"action": {
|
||||
"executeCommand": {
|
||||
"access": "write",
|
||||
"values": {
|
||||
"STOPRESET": {}
|
||||
}
|
||||
}
|
||||
},
|
||||
"condition": {
|
||||
"operand_1": "value",
|
||||
"operand_2": "DELAYED_START",
|
||||
"operator": "eq"
|
||||
}
|
||||
},
|
||||
{
|
||||
"action": {
|
||||
"executeCommand": {
|
||||
"access": "write",
|
||||
"values": {
|
||||
"START": {}
|
||||
}
|
||||
}
|
||||
},
|
||||
"condition": {
|
||||
"operand_1": "value",
|
||||
"operand_2": "END_OF_CYCLE",
|
||||
"operator": "eq"
|
||||
}
|
||||
}
|
||||
],
|
||||
"type": "string",
|
||||
"values": {
|
||||
"ALARM": {},
|
||||
"DELAYED_START": {},
|
||||
"END_OF_CYCLE": {},
|
||||
"IDLE": {},
|
||||
"OFF": {},
|
||||
"PAUSED": {},
|
||||
"READY_TO_START": {},
|
||||
"RUNNING": {}
|
||||
}
|
||||
},
|
||||
"cavityLight": {
|
||||
"access": "readwrite",
|
||||
"type": "boolean",
|
||||
"values": {
|
||||
"OFF": {},
|
||||
"ON": {}
|
||||
}
|
||||
},
|
||||
"displayTemperatureC": {
|
||||
"access": "read",
|
||||
"type": "temperature"
|
||||
},
|
||||
"doorState": {
|
||||
"access": "read",
|
||||
"type": "string",
|
||||
"values": {
|
||||
"CLOSED": {},
|
||||
"OPEN": {}
|
||||
}
|
||||
},
|
||||
"executeCommand": {
|
||||
"access": "write",
|
||||
"type": "string",
|
||||
"values": {
|
||||
"START": {},
|
||||
"STOPRESET": {}
|
||||
}
|
||||
},
|
||||
"hideExecuteCommand": {
|
||||
"access": "constant",
|
||||
"default": 0,
|
||||
"triggers": [
|
||||
{
|
||||
"action": {
|
||||
"executeCommand": {
|
||||
"disabled": false
|
||||
}
|
||||
},
|
||||
"condition": {
|
||||
"operand_1": "value",
|
||||
"operand_2": 0,
|
||||
"operator": "eq"
|
||||
}
|
||||
},
|
||||
{
|
||||
"action": {
|
||||
"executeCommand": {
|
||||
"disabled": true
|
||||
}
|
||||
},
|
||||
"condition": {
|
||||
"operand_1": "value",
|
||||
"operand_2": 1,
|
||||
"operator": "eq"
|
||||
}
|
||||
}
|
||||
],
|
||||
"type": "int"
|
||||
},
|
||||
"keyModel": {
|
||||
"access": "constant",
|
||||
"default": "PUX_SP_PYR_PE_A++",
|
||||
"type": "string"
|
||||
},
|
||||
"networkInterface": {
|
||||
"command": {
|
||||
"access": "write",
|
||||
"type": "string",
|
||||
"values": {
|
||||
"ABORT": {},
|
||||
"APPLIANCE_AUTHORIZE": {},
|
||||
"DOWNLOAD": {},
|
||||
"START": {},
|
||||
"USER_AUTHORIZE": {},
|
||||
"USER_NOT_AUTHORIZE": {}
|
||||
}
|
||||
},
|
||||
"linkQualityIndicator": {
|
||||
"access": "read",
|
||||
"type": "string",
|
||||
"values": {
|
||||
"EXCELLENT": {},
|
||||
"GOOD": {},
|
||||
"POOR": {},
|
||||
"UNDEFINED": {},
|
||||
"VERY_GOOD": {},
|
||||
"VERY_POOR": {}
|
||||
}
|
||||
},
|
||||
"niuSwUpdateCurrentDescription": {
|
||||
"access": "read",
|
||||
"type": "string"
|
||||
},
|
||||
"otaState": {
|
||||
"access": "read",
|
||||
"type": "string",
|
||||
"values": {
|
||||
"DESCRIPTION_AVAILABLE": {},
|
||||
"DESCRIPTION_DOWNLOADING": {},
|
||||
"DESCRIPTION_READY": {},
|
||||
"FW_DOWNLOADING": {},
|
||||
"FW_DOWNLOAD_START": {},
|
||||
"FW_SIGNATURE_CHECK": {},
|
||||
"FW_UPDATE_IN_PROGRESS": {},
|
||||
"IDLE": {},
|
||||
"READY_TO_UPDATE": {},
|
||||
"UPDATE_ABORT": {},
|
||||
"UPDATE_CHECK": {},
|
||||
"UPDATE_ERROR": {},
|
||||
"UPDATE_OK": {},
|
||||
"WAITINGFORAUTHORIZATION": {}
|
||||
}
|
||||
},
|
||||
"startUpCommand": {
|
||||
"access": "write",
|
||||
"type": "string",
|
||||
"values": {
|
||||
"UNINSTALL": {}
|
||||
}
|
||||
},
|
||||
"swAncAndRevision": {
|
||||
"access": "read",
|
||||
"type": "string"
|
||||
},
|
||||
"swVersion": {
|
||||
"access": "read",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"pizzaShieldState": {
|
||||
"access": "read",
|
||||
"type": "string",
|
||||
"values": {
|
||||
"INSERTED_COLD": {},
|
||||
"INSERTED_HOT": {},
|
||||
"NOT_INSERTED": {},
|
||||
"UNKNOWN": {}
|
||||
}
|
||||
},
|
||||
"preheatComplete": {
|
||||
"access": "read",
|
||||
"type": "string",
|
||||
"values": {
|
||||
"OFF": {},
|
||||
"PRE_HEAT_COMPLETED": {},
|
||||
"PRE_HEAT_RUNNING": {},
|
||||
"RE_HEAT_COMPLETED": {},
|
||||
"RE_HEAT_RUNNING": {}
|
||||
}
|
||||
},
|
||||
"program": {
|
||||
"access": "readwrite",
|
||||
"type": "string",
|
||||
"values": {
|
||||
"ASSIST_PIZZA_EXPERT_REHEAT": {
|
||||
"hideExecuteCommand": {
|
||||
"access": "readwrite",
|
||||
"default": 0,
|
||||
"disabled": true
|
||||
},
|
||||
"targetDuration": {
|
||||
"access": "readwrite",
|
||||
"disabled": false,
|
||||
"max": 180,
|
||||
"min": 150,
|
||||
"step": 1
|
||||
},
|
||||
"targetTemperatureC": {
|
||||
"access": "readwrite",
|
||||
"default": 340,
|
||||
"disabled": false,
|
||||
"max": 340,
|
||||
"min": 320,
|
||||
"step": 5,
|
||||
"type": "temperature"
|
||||
}
|
||||
},
|
||||
"AUGRATIN": {
|
||||
"hideExecuteCommand": {
|
||||
"access": "readwrite",
|
||||
"default": 0,
|
||||
"disabled": true
|
||||
},
|
||||
"targetDuration": {
|
||||
"access": "readwrite",
|
||||
"disabled": false,
|
||||
"max": 86340,
|
||||
"min": 0,
|
||||
"step": 60
|
||||
},
|
||||
"targetTemperatureC": {
|
||||
"access": "readwrite",
|
||||
"default": 300,
|
||||
"disabled": false,
|
||||
"max": 300,
|
||||
"min": 80,
|
||||
"step": 5,
|
||||
"type": "temperature"
|
||||
}
|
||||
},
|
||||
"BAKE_TRUE_FAN": {
|
||||
"hideExecuteCommand": {
|
||||
"access": "readwrite",
|
||||
"default": 0,
|
||||
"disabled": true
|
||||
},
|
||||
"targetDuration": {
|
||||
"access": "readwrite",
|
||||
"disabled": false,
|
||||
"max": 86340,
|
||||
"min": 0,
|
||||
"step": 60
|
||||
},
|
||||
"targetTemperatureC": {
|
||||
"access": "readwrite",
|
||||
"default": 200,
|
||||
"disabled": false,
|
||||
"max": 300,
|
||||
"min": 80,
|
||||
"step": 5,
|
||||
"type": "temperature"
|
||||
}
|
||||
},
|
||||
"BOTTOM": {
|
||||
"hideExecuteCommand": {
|
||||
"access": "readwrite",
|
||||
"default": 0,
|
||||
"disabled": true
|
||||
},
|
||||
"targetDuration": {
|
||||
"access": "readwrite",
|
||||
"disabled": false,
|
||||
"max": 86340,
|
||||
"min": 0,
|
||||
"step": 60
|
||||
},
|
||||
"targetTemperatureC": {
|
||||
"access": "readwrite",
|
||||
"default": 150,
|
||||
"disabled": false,
|
||||
"max": 300,
|
||||
"min": 80,
|
||||
"step": 5,
|
||||
"type": "temperature"
|
||||
}
|
||||
},
|
||||
"BREAD_BAKING": {
|
||||
"hideExecuteCommand": {
|
||||
"access": "readwrite",
|
||||
"default": 0,
|
||||
"disabled": true
|
||||
},
|
||||
"targetDuration": {
|
||||
"access": "readwrite",
|
||||
"disabled": false,
|
||||
"max": 86340,
|
||||
"min": 0,
|
||||
"step": 60
|
||||
},
|
||||
"targetTemperatureC": {
|
||||
"access": "readwrite",
|
||||
"default": 220,
|
||||
"disabled": false,
|
||||
"max": 300,
|
||||
"min": 80,
|
||||
"step": 5,
|
||||
"type": "temperature"
|
||||
}
|
||||
},
|
||||
"CONVENTIONAL_COOKING": {
|
||||
"hideExecuteCommand": {
|
||||
"access": "readwrite",
|
||||
"default": 0,
|
||||
"disabled": true
|
||||
},
|
||||
"targetDuration": {
|
||||
"access": "readwrite",
|
||||
"disabled": false,
|
||||
"max": 86340,
|
||||
"min": 0,
|
||||
"step": 60
|
||||
},
|
||||
"targetTemperatureC": {
|
||||
"access": "readwrite",
|
||||
"default": 200,
|
||||
"disabled": false,
|
||||
"max": 300,
|
||||
"min": 30,
|
||||
"step": 5,
|
||||
"type": "temperature"
|
||||
}
|
||||
},
|
||||
"DEFROST": {
|
||||
"hideExecuteCommand": {
|
||||
"access": "readwrite",
|
||||
"default": 0,
|
||||
"disabled": true
|
||||
},
|
||||
"targetDuration": {
|
||||
"access": "readwrite",
|
||||
"disabled": false,
|
||||
"max": 86340,
|
||||
"min": 0,
|
||||
"step": 60
|
||||
},
|
||||
"targetTemperatureC": {
|
||||
"access": "readwrite",
|
||||
"default": 30,
|
||||
"disabled": false,
|
||||
"max": 30,
|
||||
"min": 30,
|
||||
"step": 0,
|
||||
"type": "temperature"
|
||||
}
|
||||
},
|
||||
"DOUGH_PROVING": {
|
||||
"hideExecuteCommand": {
|
||||
"access": "readwrite",
|
||||
"default": 0,
|
||||
"disabled": true
|
||||
},
|
||||
"targetDuration": {
|
||||
"access": "readwrite",
|
||||
"disabled": false,
|
||||
"max": 86340,
|
||||
"min": 300,
|
||||
"step": 60
|
||||
},
|
||||
"targetTemperatureC": {
|
||||
"access": "readwrite",
|
||||
"default": 40,
|
||||
"disabled": false,
|
||||
"max": 40,
|
||||
"min": 40,
|
||||
"step": 0,
|
||||
"type": "temperature"
|
||||
}
|
||||
},
|
||||
"DRYING": {
|
||||
"hideExecuteCommand": {
|
||||
"access": "readwrite",
|
||||
"default": 0,
|
||||
"disabled": true
|
||||
},
|
||||
"targetDuration": {
|
||||
"access": "readwrite",
|
||||
"disabled": false,
|
||||
"max": 86340,
|
||||
"min": 0,
|
||||
"step": 60
|
||||
},
|
||||
"targetTemperatureC": {
|
||||
"access": "readwrite",
|
||||
"default": 60,
|
||||
"disabled": false,
|
||||
"max": 100,
|
||||
"min": 50,
|
||||
"step": 5,
|
||||
"type": "temperature"
|
||||
}
|
||||
},
|
||||
"FROZEN_FOOD": {
|
||||
"hideExecuteCommand": {
|
||||
"access": "readwrite",
|
||||
"default": 0,
|
||||
"disabled": true
|
||||
},
|
||||
"targetDuration": {
|
||||
"access": "readwrite",
|
||||
"disabled": false,
|
||||
"max": 86340,
|
||||
"min": 0,
|
||||
"step": 60
|
||||
},
|
||||
"targetTemperatureC": {
|
||||
"access": "readwrite",
|
||||
"default": 220,
|
||||
"disabled": false,
|
||||
"max": 300,
|
||||
"min": 80,
|
||||
"step": 5,
|
||||
"type": "temperature"
|
||||
}
|
||||
},
|
||||
"GRILL": {
|
||||
"hideExecuteCommand": {
|
||||
"access": "readwrite",
|
||||
"default": 0,
|
||||
"disabled": true
|
||||
},
|
||||
"targetDuration": {
|
||||
"access": "readwrite",
|
||||
"disabled": false,
|
||||
"max": 86340,
|
||||
"min": 0,
|
||||
"step": 60
|
||||
},
|
||||
"targetTemperatureC": {
|
||||
"access": "readwrite",
|
||||
"default": 300,
|
||||
"disabled": false,
|
||||
"max": 300,
|
||||
"min": 80,
|
||||
"step": 5,
|
||||
"type": "temperature"
|
||||
}
|
||||
},
|
||||
"GRILL_FAN": {
|
||||
"hideExecuteCommand": {
|
||||
"access": "readwrite",
|
||||
"default": 0,
|
||||
"disabled": true
|
||||
},
|
||||
"targetDuration": {
|
||||
"access": "readwrite",
|
||||
"disabled": false,
|
||||
"max": 86340,
|
||||
"min": 0,
|
||||
"step": 60
|
||||
},
|
||||
"targetTemperatureC": {
|
||||
"access": "readwrite",
|
||||
"default": 180,
|
||||
"disabled": false,
|
||||
"max": 300,
|
||||
"min": 80,
|
||||
"step": 5,
|
||||
"type": "temperature"
|
||||
}
|
||||
},
|
||||
"KEEP_WARM": {
|
||||
"hideExecuteCommand": {
|
||||
"access": "readwrite",
|
||||
"default": 0,
|
||||
"disabled": true
|
||||
},
|
||||
"targetDuration": {
|
||||
"access": "readwrite",
|
||||
"disabled": false,
|
||||
"max": 86340,
|
||||
"min": 0,
|
||||
"step": 60
|
||||
},
|
||||
"targetTemperatureC": {
|
||||
"access": "readwrite",
|
||||
"default": 80,
|
||||
"disabled": false,
|
||||
"max": 80,
|
||||
"min": 80,
|
||||
"step": 0,
|
||||
"type": "temperature"
|
||||
}
|
||||
},
|
||||
"MOIST_FAN_BAKING": {
|
||||
"hideExecuteCommand": {
|
||||
"access": "readwrite",
|
||||
"default": 0,
|
||||
"disabled": true
|
||||
},
|
||||
"targetDuration": {
|
||||
"access": "readwrite",
|
||||
"disabled": false,
|
||||
"max": 86340,
|
||||
"min": 0,
|
||||
"step": 60
|
||||
},
|
||||
"targetTemperatureC": {
|
||||
"access": "readwrite",
|
||||
"default": 160,
|
||||
"disabled": false,
|
||||
"max": 230,
|
||||
"min": 90,
|
||||
"step": 5,
|
||||
"type": "temperature"
|
||||
}
|
||||
},
|
||||
"PIZZA_EXPERT_EXTENSION": {
|
||||
"hideExecuteCommand": {
|
||||
"access": "readwrite",
|
||||
"default": 0,
|
||||
"disabled": true
|
||||
},
|
||||
"targetDuration": {
|
||||
"access": "readwrite",
|
||||
"disabled": false,
|
||||
"max": 15,
|
||||
"min": 15,
|
||||
"step": 0
|
||||
},
|
||||
"targetTemperatureC": {
|
||||
"access": "readwrite",
|
||||
"default": 340,
|
||||
"disabled": false,
|
||||
"max": 340,
|
||||
"min": 320,
|
||||
"step": 5,
|
||||
"type": "temperature"
|
||||
}
|
||||
},
|
||||
"PIZZA_EXPERT_MAIN": {
|
||||
"hideExecuteCommand": {
|
||||
"access": "readwrite",
|
||||
"default": 0,
|
||||
"disabled": true
|
||||
},
|
||||
"targetDuration": {
|
||||
"access": "readwrite",
|
||||
"disabled": false,
|
||||
"max": 180,
|
||||
"min": 150,
|
||||
"step": 1
|
||||
},
|
||||
"targetTemperatureC": {
|
||||
"access": "readwrite",
|
||||
"default": 340,
|
||||
"disabled": false,
|
||||
"max": 340,
|
||||
"min": 320,
|
||||
"step": 5,
|
||||
"type": "temperature"
|
||||
}
|
||||
},
|
||||
"PIZZA_EXPERT_PREHEAT": {
|
||||
"hideExecuteCommand": {
|
||||
"access": "readwrite",
|
||||
"default": 0,
|
||||
"disabled": true
|
||||
},
|
||||
"targetDuration": {
|
||||
"access": "readwrite",
|
||||
"disabled": false,
|
||||
"max": 86340,
|
||||
"min": 60,
|
||||
"step": 60
|
||||
},
|
||||
"targetTemperatureC": {
|
||||
"access": "readwrite",
|
||||
"default": 340,
|
||||
"disabled": false,
|
||||
"max": 340,
|
||||
"min": 320,
|
||||
"step": 5,
|
||||
"type": "temperature"
|
||||
}
|
||||
},
|
||||
"PLATE_WARMING": {
|
||||
"hideExecuteCommand": {
|
||||
"access": "readwrite",
|
||||
"default": 0,
|
||||
"disabled": true
|
||||
},
|
||||
"targetDuration": {
|
||||
"access": "readwrite",
|
||||
"disabled": false,
|
||||
"max": 86340,
|
||||
"min": 0,
|
||||
"step": 60
|
||||
},
|
||||
"targetTemperatureC": {
|
||||
"access": "readwrite",
|
||||
"default": 70,
|
||||
"disabled": false,
|
||||
"max": 70,
|
||||
"min": 70,
|
||||
"step": 0,
|
||||
"type": "temperature"
|
||||
}
|
||||
},
|
||||
"PRESERVING": {
|
||||
"hideExecuteCommand": {
|
||||
"access": "readwrite",
|
||||
"default": 0,
|
||||
"disabled": true
|
||||
},
|
||||
"targetDuration": {
|
||||
"access": "readwrite",
|
||||
"disabled": false,
|
||||
"max": 86340,
|
||||
"min": 0,
|
||||
"step": 60
|
||||
},
|
||||
"targetTemperatureC": {
|
||||
"access": "readwrite",
|
||||
"default": 160,
|
||||
"disabled": false,
|
||||
"max": 170,
|
||||
"min": 100,
|
||||
"step": 5,
|
||||
"type": "temperature"
|
||||
}
|
||||
},
|
||||
"PYRO_CLEAN_INTENSE": {
|
||||
"hideExecuteCommand": {
|
||||
"access": "readwrite",
|
||||
"default": 1,
|
||||
"disabled": true
|
||||
}
|
||||
},
|
||||
"PYRO_CLEAN_LIGHT": {
|
||||
"hideExecuteCommand": {
|
||||
"access": "readwrite",
|
||||
"default": 1,
|
||||
"disabled": true
|
||||
}
|
||||
},
|
||||
"PYRO_CLEAN_NORMAL": {
|
||||
"hideExecuteCommand": {
|
||||
"access": "readwrite",
|
||||
"default": 1,
|
||||
"disabled": true
|
||||
}
|
||||
},
|
||||
"SLOW_COOK": {
|
||||
"hideExecuteCommand": {
|
||||
"access": "readwrite",
|
||||
"default": 0,
|
||||
"disabled": true
|
||||
},
|
||||
"targetDuration": {
|
||||
"access": "readwrite",
|
||||
"disabled": false,
|
||||
"max": 86340,
|
||||
"min": 0,
|
||||
"step": 60
|
||||
},
|
||||
"targetTemperatureC": {
|
||||
"access": "readwrite",
|
||||
"default": 90,
|
||||
"disabled": false,
|
||||
"max": 150,
|
||||
"min": 80,
|
||||
"step": 5,
|
||||
"type": "temperature"
|
||||
}
|
||||
},
|
||||
"TRUE_FAN": {
|
||||
"hideExecuteCommand": {
|
||||
"access": "readwrite",
|
||||
"default": 0,
|
||||
"disabled": true
|
||||
},
|
||||
"targetDuration": {
|
||||
"access": "readwrite",
|
||||
"disabled": false,
|
||||
"max": 86340,
|
||||
"min": 0,
|
||||
"step": 60
|
||||
},
|
||||
"targetTemperatureC": {
|
||||
"access": "readwrite",
|
||||
"default": 150,
|
||||
"disabled": false,
|
||||
"max": 300,
|
||||
"min": 30,
|
||||
"step": 5,
|
||||
"type": "temperature"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"remoteControl": {
|
||||
"access": "read",
|
||||
"type": "string",
|
||||
"values": {
|
||||
"DISABLED": {},
|
||||
"ENABLED": {},
|
||||
"NOT_SAFETY_RELEVANT_ENABLED": {},
|
||||
"TEMPORARY_LOCKED": {}
|
||||
}
|
||||
},
|
||||
"runningTime": {
|
||||
"access": "read",
|
||||
"default": 0,
|
||||
"type": "number"
|
||||
},
|
||||
"targetDuration": {
|
||||
"access": "readwrite",
|
||||
"max": 86340,
|
||||
"min": 0,
|
||||
"step": 60,
|
||||
"type": "number"
|
||||
},
|
||||
"targetTemperatureC": {
|
||||
"access": "readwrite",
|
||||
"type": "temperature"
|
||||
},
|
||||
"timeToEnd": {
|
||||
"access": "read",
|
||||
"type": "number"
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,56 @@
|
||||
{
|
||||
"applianceId": "999011403_00:20220412-443E07022463",
|
||||
"connectionState": "disconnected",
|
||||
"status": "enabled",
|
||||
"properties": {
|
||||
"reported": {
|
||||
"applianceInfo": {
|
||||
"applianceType": "CR",
|
||||
"capabilityHash": "bf87e5b776236bf693664d892f1eeebef672df3ab1d0a56e7479ec1ece8932c4"
|
||||
},
|
||||
"fridge": {
|
||||
"applianceState": "RUNNING",
|
||||
"fanState": "ON",
|
||||
"doorState": "CLOSED",
|
||||
"targetTemperatureF": 39.2,
|
||||
"targetTemperatureC": 4,
|
||||
"fastMode": "OFF",
|
||||
"alerts": []
|
||||
},
|
||||
"applianceUiSwVersion": "v4.0.4",
|
||||
"uiLockMode": false,
|
||||
"cpv": "00",
|
||||
"vacationHolidayMode": "OFF",
|
||||
"applianceMode": "NORMAL",
|
||||
"applianceMainBoardSwVersion": "v39.00.00",
|
||||
"coolingValveState": "1",
|
||||
"alerts": [
|
||||
{
|
||||
"severity": "WARNING",
|
||||
"acknowledgeStatus": "NOT_NEEDED",
|
||||
"code": "AIR_SENSOR_BROKEN",
|
||||
"applianceCode": "6"
|
||||
}
|
||||
],
|
||||
"networkInterface": {
|
||||
"linkQualityIndicator": "VERY_GOOD",
|
||||
"otaState": "IDLE",
|
||||
"swVersion": "v1.9.2_hacl",
|
||||
"niuSwUpdateCurrentDescription": "A16323312A-S00006975A",
|
||||
"swAncAndRevision": "S00006975A"
|
||||
},
|
||||
"freezer": {
|
||||
"fastMode": "OFF",
|
||||
"applianceState": "RUNNING",
|
||||
"fanState": "OFF",
|
||||
"doorState": "CLOSED",
|
||||
"targetTemperatureF": -0.3999999999999986,
|
||||
"targetTemperatureC": -18,
|
||||
"alerts": []
|
||||
},
|
||||
"reminderTime": -1,
|
||||
"connectivityState": "disconnected",
|
||||
"energySavingMode": "OFF"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
{
|
||||
"applianceId": "900412569_00:43319382-443E0748CCD4",
|
||||
"connectionState": "connected",
|
||||
"status": "enabled",
|
||||
"properties": {
|
||||
"reported": {
|
||||
"cleaningReminder": false,
|
||||
"doorState": "CLOSED",
|
||||
"remoteControl": "ENABLED",
|
||||
"targetTemperatureF": 356,
|
||||
"targetTemperatureC": 180,
|
||||
"program": "KEY_ERROR",
|
||||
"targetMicrowavePower": 65535,
|
||||
"waterTrayInsertionState": "INSERTED",
|
||||
"waterTankEmpty": "STEAM_TANK_FULL",
|
||||
"targetDuration": 0,
|
||||
"startTime": -1,
|
||||
"applianceInfo": {
|
||||
"capabilityHash": "ab7b74c6ac8a74614980f77811389dfd47206456617a4d698ffcb97da1cc955b",
|
||||
"applianceType": "OV"
|
||||
},
|
||||
"preheatComplete": "OFF",
|
||||
"cpv": "00",
|
||||
"targetFoodProbeTemperatureC": 60,
|
||||
"targetFoodProbeTemperatureF": 140,
|
||||
"runningTime": 0,
|
||||
"applianceState": "READY_TO_START",
|
||||
"alerts": [],
|
||||
"networkInterface": {
|
||||
"swVersion": "v3.0.0S_argo",
|
||||
"otaState": "IDLE",
|
||||
"linkQualityIndicator": "VERY_GOOD",
|
||||
"niuSwUpdateCurrentDescription": "A23642201A-S00007645A",
|
||||
"swAncAndRevision": "S00007645A"
|
||||
},
|
||||
"foodProbeInsertionState": "INSERTED",
|
||||
"cavityLight": false,
|
||||
"processPhase": "NONE",
|
||||
"descalingReminderState": false,
|
||||
"connectivityState": "connected",
|
||||
"timeToEnd": -1,
|
||||
"displayTemperatureC": 30,
|
||||
"displayFoodProbeTemperatureC": 23,
|
||||
"displayTemperatureF": 86,
|
||||
"displayFoodProbeTemperatureF": 73.4
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
{
|
||||
"applianceId": "999008105_00:99010299-443E074B7A6E",
|
||||
"connectionState": "connected",
|
||||
"status": "enabled",
|
||||
"properties": {
|
||||
"reported": {
|
||||
"applianceInfo": {
|
||||
"applianceType": "HD",
|
||||
"capabilityHash": "5ac0f7e099cfaa217c694cb69edd604d94c31892c11f183049e10d1eebc91a1f"
|
||||
},
|
||||
"hoodCharcFilterTimer": 457200,
|
||||
"hoodFilterCharcEnable": "ON",
|
||||
"humanCentricLightEventState": "OFF",
|
||||
"hoodFanLevel": "OFF",
|
||||
"tvocFilterTime": 918000,
|
||||
"remoteControl": "ENABLED",
|
||||
"cpv": "00",
|
||||
"humanCentricLightEventSettings": {
|
||||
"0": {
|
||||
"lightIntensityPrc": 62,
|
||||
"startTimeMinutes": 5,
|
||||
"lightColorPrc": 100,
|
||||
"startTimeHour": 10
|
||||
},
|
||||
"1": {
|
||||
"lightIntensityPrc": 100,
|
||||
"startTimeMinutes": 20,
|
||||
"lightColorPrc": 40,
|
||||
"startTimeHour": 10
|
||||
},
|
||||
"2": {
|
||||
"lightIntensityPrc": 100,
|
||||
"startTimeMinutes": 35,
|
||||
"lightColorPrc": 100,
|
||||
"startTimeHour": 10
|
||||
},
|
||||
"3": {
|
||||
"lightIntensityPrc": 50,
|
||||
"startTimeMinutes": 0,
|
||||
"lightColorPrc": 30,
|
||||
"startTimeHour": 11
|
||||
},
|
||||
"4": {
|
||||
"lightIntensityPrc": 70,
|
||||
"startTimeMinutes": 0,
|
||||
"lightColorPrc": 70,
|
||||
"startTimeHour": 13
|
||||
},
|
||||
"5": {
|
||||
"lightIntensityPrc": 69,
|
||||
"startTimeMinutes": 30,
|
||||
"lightColorPrc": 40,
|
||||
"startTimeHour": 13
|
||||
},
|
||||
"6": {
|
||||
"lightIntensityPrc": 50,
|
||||
"startTimeMinutes": 0,
|
||||
"lightColorPrc": 69,
|
||||
"startTimeHour": 14
|
||||
}
|
||||
},
|
||||
"applianceState": "IDLE",
|
||||
"applianceMode": "NORMAL",
|
||||
"applianceLocalTimeOffset": 7200,
|
||||
"hoodGreaseFilterTimer": 424800,
|
||||
"lightIntensity": 0,
|
||||
"localTimeAutomaticMode": "AUTOMATIC",
|
||||
"alerts": [],
|
||||
"lightColorTemperature": 0,
|
||||
"soundVolume": 0,
|
||||
"hoodFilterCharcIndication": false,
|
||||
"networkInterface": {
|
||||
"swVersion": "v4.0.0S_argo",
|
||||
"otaState": "IDLE",
|
||||
"autoLocalTimeOffset": 7200,
|
||||
"niuSwUpdateCurrentDescription": "A23642205A-S00008458A",
|
||||
"timeZoneDatabaseName": "Europe/Stockholm",
|
||||
"swAncAndRevision": "S00008458A",
|
||||
"linkQualityIndicator": "EXCELLENT"
|
||||
},
|
||||
"targetDuration": 0,
|
||||
"hoodFilterGreaseIndication": "TRUE",
|
||||
"connectivityState": "connected",
|
||||
"hoodAutoSwitchOffEvent": false,
|
||||
"timeToEnd": 0
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
{
|
||||
"applianceId": "999008120_00:99907833-443E073D986E",
|
||||
"connectionState": "connected",
|
||||
"status": "enabled",
|
||||
"properties": {
|
||||
"reported": {
|
||||
"oTA3TargetVersion": "S000412012A\u0000",
|
||||
"applianceInfo": {
|
||||
"capabilityHash": "6e7da5a7b8b39c99b9b08dd5b2e0f834885fc473832414e05ef3d31da0a5440d",
|
||||
"applianceType": "HB"
|
||||
},
|
||||
"oTA3CurrentVersion": "S0004120121\u0000",
|
||||
"remoteControl": "NOT_SAFETY_RELEVANT_ENABLED",
|
||||
"oTA3State": "IDLE",
|
||||
"cpv": "00",
|
||||
"applianceState": "OFF",
|
||||
"hobHood": {
|
||||
"hoodDryingCycle": "OFF",
|
||||
"hobToHoodState": "MANUAL",
|
||||
"hoodFilterCharcIndication": false,
|
||||
"hobToHoodFanSpeed": "OFF",
|
||||
"windowNotification": "OPENING_REQUIRED",
|
||||
"hobToHoodMode": "H3",
|
||||
"hoodFilterGreaseIndication": false
|
||||
},
|
||||
"alerts": [],
|
||||
"oTA3LastResult": "NONE",
|
||||
"networkInterface": {
|
||||
"swVersion": "v4.8.06rc2_argo",
|
||||
"otaState": "IDLE",
|
||||
"autoLocalTimeOffset": 7200,
|
||||
"niuSwUpdateCurrentDescription": "A24559615A-S00031110B",
|
||||
"timeZoneDatabaseName": "Europe/Stockholm",
|
||||
"swAncAndRevision": "S00031110B",
|
||||
"linkQualityIndicator": "VERY_GOOD",
|
||||
"oTA3TargetVersion": "S000412012A\u0000",
|
||||
"oTA3LastResult": "NONE",
|
||||
"oTA3CurrentVersion": "S0004120121\u0000",
|
||||
"oTA3State": "IDLE"
|
||||
},
|
||||
"childLock": false,
|
||||
"hobZone2": {
|
||||
"residualHeatState": "NO",
|
||||
"heatingQualitativeLevel": 0,
|
||||
"hobPotDetected": "NO_POT_IDLE"
|
||||
},
|
||||
"hobZone1": {
|
||||
"residualHeatState": "NO",
|
||||
"heatingQualitativeLevel": 0,
|
||||
"hobPotDetected": "NO_POT_IDLE"
|
||||
},
|
||||
"hobZone4": {
|
||||
"residualHeatState": "NO",
|
||||
"heatingQualitativeLevel": 0,
|
||||
"hobPotDetected": "NO_POT_IDLE"
|
||||
},
|
||||
"hobZone3": {
|
||||
"residualHeatState": "NO",
|
||||
"heatingQualitativeLevel": 0,
|
||||
"hobPotDetected": "NO_POT_IDLE"
|
||||
},
|
||||
"connectivityState": "connected"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
{
|
||||
"applianceId": "949288049_00:11112225-443E076A37D6",
|
||||
"connectionState": "connected",
|
||||
"status": "enabled",
|
||||
"properties": {
|
||||
"reported": {
|
||||
"applianceInfo": {
|
||||
"capabilityHash": "212e55b62f19cfec40ff724301b07f80adef9a0ccf072693d45e38049708ef03",
|
||||
"applianceType": "OV"
|
||||
},
|
||||
"doorState": "CLOSED",
|
||||
"preheatComplete": "OFF",
|
||||
"remoteControl": "NOT_SAFETY_RELEVANT_ENABLED",
|
||||
"cpv": "00",
|
||||
"targetTemperatureF": 302,
|
||||
"targetTemperatureC": 150,
|
||||
"runningTime": 0,
|
||||
"program": "TRUE_FAN",
|
||||
"applianceState": "READY_TO_START",
|
||||
"targetMicrowavePower": 65535,
|
||||
"alerts": [],
|
||||
"pizzaShieldState": "UNKNOWN",
|
||||
"networkInterface": {
|
||||
"autoLocalTimeOffset": -2147483648,
|
||||
"niuSwUpdateCurrentDescription": "A23642205A-S00008458A",
|
||||
"swVersion": "v4.0.0S_argo",
|
||||
"otaState": "IDLE",
|
||||
"timeZoneDatabaseName": "Etc/UTC",
|
||||
"swAncAndRevision": "S00008458A",
|
||||
"linkQualityIndicator": "VERY_GOOD"
|
||||
},
|
||||
"foodProbeInsertionState": "NOT_INSERTED",
|
||||
"cavityLight": false,
|
||||
"waterTrayInsertionState": "INSERTED",
|
||||
"waterTankEmpty": "STEAM_TANK_FULL",
|
||||
"targetDuration": 0,
|
||||
"startTime": -1,
|
||||
"processPhase": "NONE",
|
||||
"connectivityState": "connected",
|
||||
"timeToEnd": -1,
|
||||
"displayTemperatureC": 30,
|
||||
"displayTemperatureF": 86
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
{
|
||||
"applianceId": "944035064_00:27032025-443E073D97B2",
|
||||
"connectionState": "connected",
|
||||
"status": "enabled",
|
||||
"properties": {
|
||||
"reported": {
|
||||
"cleaningReminder": true,
|
||||
"displayLight": "DISPLAY_LIGHT_3",
|
||||
"temperatureRepresentation": "CELSIUS",
|
||||
"remoteControl": "ENABLED",
|
||||
"language": "ENGLISH",
|
||||
"clockStyle": "NOT_SELECTED",
|
||||
"localTimeAutomaticMode": "MANUAL",
|
||||
"soundVolume": 2,
|
||||
"keySoundTone": "CLICK",
|
||||
"upperOven": {
|
||||
"applianceState": "READY_TO_START",
|
||||
"doorState": "CLOSED",
|
||||
"targetTemperatureF": 356,
|
||||
"targetTemperatureC": 180,
|
||||
"runningTime": -2147483648,
|
||||
"program": "STEAMIFY",
|
||||
"executeCommand": "STOPRESET",
|
||||
"targetMicrowavePower": 0,
|
||||
"waterTankLevel": "OK",
|
||||
"foodProbeInsertionState": "NOT_INSERTED",
|
||||
"cavityLight": false,
|
||||
"targetDuration": 0,
|
||||
"startTime": -2147483648,
|
||||
"waterTrayInsertionState": "INSERTED",
|
||||
"reminderTime": -2147483648,
|
||||
"fastHeatUpFeature": "DISABLED",
|
||||
"messageQueueSync": {
|
||||
"activeMessageIndex": 0,
|
||||
"messageBehaviour": "INVALID",
|
||||
"messageQueueId": 0,
|
||||
"messageQueueType": "INVALID"
|
||||
},
|
||||
"favoriteSelect": "255",
|
||||
"processPhase": "NONE",
|
||||
"displayTemperatureC": 30,
|
||||
"displayTemperatureF": 86,
|
||||
"displayFoodProbeTemperatureC": null,
|
||||
"displayFoodProbeTemperatureF": null,
|
||||
"timeToEnd": 3599
|
||||
},
|
||||
"waterHardness": "STEP_4",
|
||||
"oTA3TargetVersion": "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000",
|
||||
"applianceInfo": {
|
||||
"applianceType": "SO",
|
||||
"capabilityHash": "4d0d5929a62cb8772f346348875e59bd758a3a88e247f39fc1c607e258cbbc00"
|
||||
},
|
||||
"oTA3CurrentVersion": "S0005330604\u0000",
|
||||
"applianceUiConfigVersion": "Oven_C000000062_0.0.31",
|
||||
"cpv": "03",
|
||||
"oTA3State": "IDLE",
|
||||
"applianceState": "OFF",
|
||||
"applianceLocalTimeOffset": -417172230,
|
||||
"favoriteSelect": "255",
|
||||
"alerts": [],
|
||||
"oTA3LastResult": "NONE",
|
||||
"fastHeatUpFeature": "DISABLED",
|
||||
"networkInterface": {
|
||||
"swVersion": "v4.6.06_argo",
|
||||
"otaState": "IDLE",
|
||||
"autoLocalTimeOffset": 7200,
|
||||
"niuSwUpdateCurrentDescription": "A24559613B-S00031107A",
|
||||
"timeZoneDatabaseName": "Europe/Stockholm",
|
||||
"swAncAndRevision": "S00031107A",
|
||||
"linkQualityIndicator": "VERY_GOOD",
|
||||
"oTA3TargetVersion": "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000",
|
||||
"oTA3LastResult": "NONE",
|
||||
"oTA3CurrentVersion": "S0005330604\u0000",
|
||||
"oTA3State": "IDLE"
|
||||
},
|
||||
"childLock": false,
|
||||
"descalingReminderState": false,
|
||||
"connectivityState": "connected",
|
||||
"messageQueueSync": {
|
||||
"activeMessageIndex": 0,
|
||||
"messageBehaviour": "INVALID",
|
||||
"messageQueueId": 0,
|
||||
"messageQueueType": "INVALID"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
{
|
||||
"applianceId": "999120000_00:74852244-443E0704101F",
|
||||
"connectionState": "disconnected",
|
||||
"status": "enabled",
|
||||
"properties": {
|
||||
"reported": {
|
||||
"waterHardness": "MEDIUM",
|
||||
"applianceInfo": {
|
||||
"applianceType": "TD",
|
||||
"capabilityHash": "e3f20b48d86da2cc3f492406af903a7597349e7d5777c09612efadfcac84ab9d"
|
||||
},
|
||||
"timeToEnd": 2040,
|
||||
"doorState": "OPEN",
|
||||
"miscellaneous": {},
|
||||
"applianceUiSwVersion": "UDA1MC07",
|
||||
"uiLockMode": false,
|
||||
"applianceTotalWorkingTime": 0,
|
||||
"remoteControl": "NOT_SAFETY_RELEVANT_ENABLED",
|
||||
"cpv": "00",
|
||||
"language": "ENGLISH",
|
||||
"applianceState": "OFF",
|
||||
"dryingNominalLoadWeight": 65535,
|
||||
"applianceMode": "NORMAL",
|
||||
"applianceMainBoardSwVersion": "TRB40034",
|
||||
"totalCycleCounter": 0,
|
||||
"measuredLoadWeight": 65535,
|
||||
"alerts": [],
|
||||
"cyclePhase": "UNAVAILABLE",
|
||||
"networkInterface": {
|
||||
"otaState": "IDLE",
|
||||
"swVersion": "v5.4.0",
|
||||
"linkQualityIndicator": "VERY_GOOD",
|
||||
"niuSwUpdateCurrentDescription": "A07491702B-S00006963A",
|
||||
"swAncAndRevision": "S00006963A"
|
||||
},
|
||||
"endOfCycleSound": "NO_SOUND",
|
||||
"startTime": -1,
|
||||
"userSelections": {
|
||||
"reversePlus": false,
|
||||
"tDEconomy_Eco": true,
|
||||
"humidityTarget": "CUPBOARD",
|
||||
"refresh": false,
|
||||
"drynessValue": "MINIMUM",
|
||||
"programUID": "SILK_DRY_PR_SILK",
|
||||
"antiCreaseValue": 120,
|
||||
"tDEconomy_Night": false,
|
||||
"dryingTime": 0,
|
||||
"tDEconomy_Quick": false,
|
||||
"steamValue": "STEAM_OFF"
|
||||
},
|
||||
"connectivityState": "disconnected"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"applianceId": "999011403_00:20220412-443E07022463",
|
||||
"applianceName": "Ayran",
|
||||
"applianceType": "CR",
|
||||
"created": "2025-03-06T10:00:57.477+00:00"
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"applianceId": "900412569_00:43319382-443E0748CCD4",
|
||||
"applianceName": "Fenix",
|
||||
"applianceType": "OV",
|
||||
"created": "2024-10-30T14:00:00.000+00:00"
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"applianceId": "999008105_00:99010299-443E074B7A6E",
|
||||
"applianceName": "Ceiling Hood",
|
||||
"applianceType": "HD",
|
||||
"created": "2024-11-14T03:27:20.672+00:00"
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"applianceId": "999008120_00:99907833-443E073D986E",
|
||||
"applianceName": "Peacock hob",
|
||||
"applianceType": "HB",
|
||||
"created": "2025-08-12T07:29:55.249+00:00"
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"applianceId": "949288049_00:11112225-443E076A37D6",
|
||||
"applianceName": "PUX pizza oven",
|
||||
"applianceType": "OV",
|
||||
"created": "2026-01-15T14:59:00.000+00:00"
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"applianceId": "944035064_00:27032025-443E073D97B2",
|
||||
"applianceName": "Supex oven",
|
||||
"applianceType": "SO",
|
||||
"created": "2026-03-23T08:14:42.975+00:00"
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"applianceId": "999120000_00:74852244-443E0704101F",
|
||||
"applianceName": "Dryer",
|
||||
"applianceType": "TD",
|
||||
"created": "2025-12-10T15:14:32.043+00:00"
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,218 @@
|
||||
# serializer version: 1
|
||||
# name: test_all_appliances[ayran_fridge]
|
||||
DeviceRegistryEntrySnapshot({
|
||||
'area_id': None,
|
||||
'config_entries': <ANY>,
|
||||
'config_entries_subentries': <ANY>,
|
||||
'configuration_url': None,
|
||||
'connections': set({
|
||||
}),
|
||||
'disabled_by': None,
|
||||
'entry_type': None,
|
||||
'hw_version': None,
|
||||
'id': <ANY>,
|
||||
'identifiers': set({
|
||||
tuple(
|
||||
'electrolux',
|
||||
'999011403_00:20220412-443E07022463',
|
||||
),
|
||||
}),
|
||||
'labels': set({
|
||||
}),
|
||||
'manufacturer': 'AEG',
|
||||
'model': 'RCB732E9MX',
|
||||
'model_id': None,
|
||||
'name': 'Ayran',
|
||||
'name_by_user': None,
|
||||
'primary_config_entry': <ANY>,
|
||||
'serial_number': '20220412',
|
||||
'sw_version': None,
|
||||
'via_device_id': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_all_appliances[fenix_oven]
|
||||
DeviceRegistryEntrySnapshot({
|
||||
'area_id': None,
|
||||
'config_entries': <ANY>,
|
||||
'config_entries_subentries': <ANY>,
|
||||
'configuration_url': None,
|
||||
'connections': set({
|
||||
}),
|
||||
'disabled_by': None,
|
||||
'entry_type': None,
|
||||
'hw_version': None,
|
||||
'id': <ANY>,
|
||||
'identifiers': set({
|
||||
tuple(
|
||||
'electrolux',
|
||||
'900412569_00:43319382-443E0748CCD4',
|
||||
),
|
||||
}),
|
||||
'labels': set({
|
||||
}),
|
||||
'manufacturer': 'ELECTROLUX',
|
||||
'model': 'OE9XS',
|
||||
'model_id': None,
|
||||
'name': 'Fenix',
|
||||
'name_by_user': None,
|
||||
'primary_config_entry': <ANY>,
|
||||
'serial_number': '43319382',
|
||||
'sw_version': None,
|
||||
'via_device_id': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_all_appliances[hood]
|
||||
DeviceRegistryEntrySnapshot({
|
||||
'area_id': None,
|
||||
'config_entries': <ANY>,
|
||||
'config_entries_subentries': <ANY>,
|
||||
'configuration_url': None,
|
||||
'connections': set({
|
||||
}),
|
||||
'disabled_by': None,
|
||||
'entry_type': None,
|
||||
'hw_version': None,
|
||||
'id': <ANY>,
|
||||
'identifiers': set({
|
||||
tuple(
|
||||
'electrolux',
|
||||
'999008105_00:99010299-443E074B7A6E',
|
||||
),
|
||||
}),
|
||||
'labels': set({
|
||||
}),
|
||||
'manufacturer': 'AEG',
|
||||
'model': 'DDC8271W',
|
||||
'model_id': None,
|
||||
'name': 'Ceiling Hood',
|
||||
'name_by_user': None,
|
||||
'primary_config_entry': <ANY>,
|
||||
'serial_number': '99010299',
|
||||
'sw_version': None,
|
||||
'via_device_id': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_all_appliances[peacock_hob]
|
||||
DeviceRegistryEntrySnapshot({
|
||||
'area_id': None,
|
||||
'config_entries': <ANY>,
|
||||
'config_entries_subentries': <ANY>,
|
||||
'configuration_url': None,
|
||||
'connections': set({
|
||||
}),
|
||||
'disabled_by': None,
|
||||
'entry_type': None,
|
||||
'hw_version': None,
|
||||
'id': <ANY>,
|
||||
'identifiers': set({
|
||||
tuple(
|
||||
'electrolux',
|
||||
'999008120_00:99907833-443E073D986E',
|
||||
),
|
||||
}),
|
||||
'labels': set({
|
||||
}),
|
||||
'manufacturer': 'AEG',
|
||||
'model': 'TU84CF43CB',
|
||||
'model_id': None,
|
||||
'name': 'Peacock hob',
|
||||
'name_by_user': None,
|
||||
'primary_config_entry': <ANY>,
|
||||
'serial_number': '99907833',
|
||||
'sw_version': None,
|
||||
'via_device_id': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_all_appliances[pux_oven]
|
||||
DeviceRegistryEntrySnapshot({
|
||||
'area_id': None,
|
||||
'config_entries': <ANY>,
|
||||
'config_entries_subentries': <ANY>,
|
||||
'configuration_url': None,
|
||||
'connections': set({
|
||||
}),
|
||||
'disabled_by': None,
|
||||
'entry_type': None,
|
||||
'hw_version': None,
|
||||
'id': <ANY>,
|
||||
'identifiers': set({
|
||||
tuple(
|
||||
'electrolux',
|
||||
'949288049_00:11112225-443E076A37D6',
|
||||
),
|
||||
}),
|
||||
'labels': set({
|
||||
}),
|
||||
'manufacturer': 'AEG',
|
||||
'model': 'NBX7P631SB',
|
||||
'model_id': None,
|
||||
'name': 'PUX pizza oven',
|
||||
'name_by_user': None,
|
||||
'primary_config_entry': <ANY>,
|
||||
'serial_number': '11112225',
|
||||
'sw_version': None,
|
||||
'via_device_id': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_all_appliances[supex_structured_oven]
|
||||
DeviceRegistryEntrySnapshot({
|
||||
'area_id': None,
|
||||
'config_entries': <ANY>,
|
||||
'config_entries_subentries': <ANY>,
|
||||
'configuration_url': None,
|
||||
'connections': set({
|
||||
}),
|
||||
'disabled_by': None,
|
||||
'entry_type': None,
|
||||
'hw_version': None,
|
||||
'id': <ANY>,
|
||||
'identifiers': set({
|
||||
tuple(
|
||||
'electrolux',
|
||||
'944035064_00:27032025-443E073D97B2',
|
||||
),
|
||||
}),
|
||||
'labels': set({
|
||||
}),
|
||||
'manufacturer': 'AEG',
|
||||
'model': 'NBP9S831AB',
|
||||
'model_id': None,
|
||||
'name': 'Supex oven',
|
||||
'name_by_user': None,
|
||||
'primary_config_entry': <ANY>,
|
||||
'serial_number': '27032025',
|
||||
'sw_version': None,
|
||||
'via_device_id': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_all_appliances[tumble_dryer]
|
||||
DeviceRegistryEntrySnapshot({
|
||||
'area_id': None,
|
||||
'config_entries': <ANY>,
|
||||
'config_entries_subentries': <ANY>,
|
||||
'configuration_url': None,
|
||||
'connections': set({
|
||||
}),
|
||||
'disabled_by': None,
|
||||
'entry_type': None,
|
||||
'hw_version': None,
|
||||
'id': <ANY>,
|
||||
'identifiers': set({
|
||||
tuple(
|
||||
'electrolux',
|
||||
'999120000_00:74852244-443E0704101F',
|
||||
),
|
||||
}),
|
||||
'labels': set({
|
||||
}),
|
||||
'manufacturer': 'ELECTROLUX',
|
||||
'model': 'EW9H778P9',
|
||||
'model_id': None,
|
||||
'name': 'Dryer',
|
||||
'name_by_user': None,
|
||||
'primary_config_entry': <ANY>,
|
||||
'serial_number': '74852244',
|
||||
'sw_version': None,
|
||||
'via_device_id': None,
|
||||
})
|
||||
# ---
|
||||
@@ -0,0 +1,507 @@
|
||||
# serializer version: 1
|
||||
# name: test_sensor[sensor.fenix_appliance_state-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': list([
|
||||
None,
|
||||
]),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
'options': list([
|
||||
'alarm',
|
||||
'delayed_start',
|
||||
'end_of_cycle',
|
||||
'idle',
|
||||
'off',
|
||||
'paused',
|
||||
'ready_to_start',
|
||||
'running',
|
||||
]),
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'sensor',
|
||||
'entity_category': None,
|
||||
'entity_id': 'sensor.fenix_appliance_state',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'object_id_base': 'Appliance state',
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': <SensorDeviceClass.ENUM: 'enum'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'Appliance state',
|
||||
'platform': 'electrolux',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'appliance_state',
|
||||
'unique_id': '900412569_00:43319382-443E0748CCD4_appliance_state',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor[sensor.fenix_appliance_state-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'device_class': 'enum',
|
||||
'friendly_name': 'Fenix Appliance state',
|
||||
'options': list([
|
||||
'alarm',
|
||||
'delayed_start',
|
||||
'end_of_cycle',
|
||||
'idle',
|
||||
'off',
|
||||
'paused',
|
||||
'ready_to_start',
|
||||
'running',
|
||||
]),
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.fenix_appliance_state',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'ready_to_start',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor[sensor.fenix_current_temperature-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': list([
|
||||
None,
|
||||
]),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'sensor',
|
||||
'entity_category': None,
|
||||
'entity_id': 'sensor.fenix_current_temperature',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'object_id_base': 'Current temperature',
|
||||
'options': dict({
|
||||
'sensor': dict({
|
||||
'suggested_display_precision': 1,
|
||||
}),
|
||||
}),
|
||||
'original_device_class': <SensorDeviceClass.TEMPERATURE: 'temperature'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'Current temperature',
|
||||
'platform': 'electrolux',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'display_temperature',
|
||||
'unique_id': '900412569_00:43319382-443E0748CCD4_display_temperature',
|
||||
'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>,
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor[sensor.fenix_current_temperature-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'device_class': 'temperature',
|
||||
'friendly_name': 'Fenix Current temperature',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>,
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.fenix_current_temperature',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': '30',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor[sensor.fenix_food_probe_state-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': list([
|
||||
None,
|
||||
]),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
'options': list([
|
||||
'inserted',
|
||||
'not_inserted',
|
||||
]),
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'sensor',
|
||||
'entity_category': None,
|
||||
'entity_id': 'sensor.fenix_food_probe_state',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'object_id_base': 'Food probe state',
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': <SensorDeviceClass.ENUM: 'enum'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'Food probe state',
|
||||
'platform': 'electrolux',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'food_probe_state',
|
||||
'unique_id': '900412569_00:43319382-443E0748CCD4_food_probe_state',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor[sensor.fenix_food_probe_state-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'device_class': 'enum',
|
||||
'friendly_name': 'Fenix Food probe state',
|
||||
'options': list([
|
||||
'inserted',
|
||||
'not_inserted',
|
||||
]),
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.fenix_food_probe_state',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'inserted',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor[sensor.fenix_food_probe_temperature-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': list([
|
||||
None,
|
||||
]),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'sensor',
|
||||
'entity_category': None,
|
||||
'entity_id': 'sensor.fenix_food_probe_temperature',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'object_id_base': 'Food probe temperature',
|
||||
'options': dict({
|
||||
'sensor': dict({
|
||||
'suggested_display_precision': 1,
|
||||
}),
|
||||
}),
|
||||
'original_device_class': <SensorDeviceClass.TEMPERATURE: 'temperature'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'Food probe temperature',
|
||||
'platform': 'electrolux',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'food_probe_temperature',
|
||||
'unique_id': '900412569_00:43319382-443E0748CCD4_food_probe_temperature',
|
||||
'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>,
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor[sensor.fenix_food_probe_temperature-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'device_class': 'temperature',
|
||||
'friendly_name': 'Fenix Food probe temperature',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>,
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.fenix_food_probe_temperature',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': '23',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor[sensor.fenix_remote_control-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': list([
|
||||
None,
|
||||
]),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
'options': list([
|
||||
'disabled',
|
||||
'enabled',
|
||||
'not_safety_relevant_enabled',
|
||||
'temporary_locked',
|
||||
]),
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'sensor',
|
||||
'entity_category': None,
|
||||
'entity_id': 'sensor.fenix_remote_control',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'object_id_base': 'Remote control',
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': <SensorDeviceClass.ENUM: 'enum'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'Remote control',
|
||||
'platform': 'electrolux',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'remote_control',
|
||||
'unique_id': '900412569_00:43319382-443E0748CCD4_remote_control',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor[sensor.fenix_remote_control-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'device_class': 'enum',
|
||||
'friendly_name': 'Fenix Remote control',
|
||||
'options': list([
|
||||
'disabled',
|
||||
'enabled',
|
||||
'not_safety_relevant_enabled',
|
||||
'temporary_locked',
|
||||
]),
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.fenix_remote_control',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'enabled',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor[sensor.pux_pizza_oven_appliance_state-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': list([
|
||||
None,
|
||||
]),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
'options': list([
|
||||
'alarm',
|
||||
'delayed_start',
|
||||
'end_of_cycle',
|
||||
'idle',
|
||||
'off',
|
||||
'paused',
|
||||
'ready_to_start',
|
||||
'running',
|
||||
]),
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'sensor',
|
||||
'entity_category': None,
|
||||
'entity_id': 'sensor.pux_pizza_oven_appliance_state',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'object_id_base': 'Appliance state',
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': <SensorDeviceClass.ENUM: 'enum'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'Appliance state',
|
||||
'platform': 'electrolux',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'appliance_state',
|
||||
'unique_id': '949288049_00:11112225-443E076A37D6_appliance_state',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor[sensor.pux_pizza_oven_appliance_state-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'device_class': 'enum',
|
||||
'friendly_name': 'PUX pizza oven Appliance state',
|
||||
'options': list([
|
||||
'alarm',
|
||||
'delayed_start',
|
||||
'end_of_cycle',
|
||||
'idle',
|
||||
'off',
|
||||
'paused',
|
||||
'ready_to_start',
|
||||
'running',
|
||||
]),
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.pux_pizza_oven_appliance_state',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'ready_to_start',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor[sensor.pux_pizza_oven_current_temperature-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': list([
|
||||
None,
|
||||
]),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'sensor',
|
||||
'entity_category': None,
|
||||
'entity_id': 'sensor.pux_pizza_oven_current_temperature',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'object_id_base': 'Current temperature',
|
||||
'options': dict({
|
||||
'sensor': dict({
|
||||
'suggested_display_precision': 1,
|
||||
}),
|
||||
}),
|
||||
'original_device_class': <SensorDeviceClass.TEMPERATURE: 'temperature'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'Current temperature',
|
||||
'platform': 'electrolux',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'display_temperature',
|
||||
'unique_id': '949288049_00:11112225-443E076A37D6_display_temperature',
|
||||
'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>,
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor[sensor.pux_pizza_oven_current_temperature-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'device_class': 'temperature',
|
||||
'friendly_name': 'PUX pizza oven Current temperature',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>,
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.pux_pizza_oven_current_temperature',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': '30',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor[sensor.pux_pizza_oven_remote_control-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': list([
|
||||
None,
|
||||
]),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
'options': list([
|
||||
'disabled',
|
||||
'enabled',
|
||||
'not_safety_relevant_enabled',
|
||||
'temporary_locked',
|
||||
]),
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'sensor',
|
||||
'entity_category': None,
|
||||
'entity_id': 'sensor.pux_pizza_oven_remote_control',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'object_id_base': 'Remote control',
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': <SensorDeviceClass.ENUM: 'enum'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'Remote control',
|
||||
'platform': 'electrolux',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'remote_control',
|
||||
'unique_id': '949288049_00:11112225-443E076A37D6_remote_control',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor[sensor.pux_pizza_oven_remote_control-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'device_class': 'enum',
|
||||
'friendly_name': 'PUX pizza oven Remote control',
|
||||
'options': list([
|
||||
'disabled',
|
||||
'enabled',
|
||||
'not_safety_relevant_enabled',
|
||||
'temporary_locked',
|
||||
]),
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.pux_pizza_oven_remote_control',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'not_safety_relevant_enabled',
|
||||
})
|
||||
# ---
|
||||
@@ -0,0 +1,36 @@
|
||||
"""Binary sensor tests of Electrolux integration."""
|
||||
|
||||
from collections.abc import Generator
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
from syrupy.assertion import SnapshotAssertion
|
||||
|
||||
from homeassistant.const import Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
|
||||
from . import setup_integration
|
||||
|
||||
from tests.common import MockConfigEntry, snapshot_platform
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def override_platforms() -> Generator[None]:
|
||||
"""Override PLATFORMS."""
|
||||
with patch(
|
||||
"homeassistant.components.electrolux.PLATFORMS", [Platform.BINARY_SENSOR]
|
||||
):
|
||||
yield
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("appliances")
|
||||
async def test_binary_sensor(
|
||||
hass: HomeAssistant,
|
||||
entity_registry: er.EntityRegistry,
|
||||
snapshot: SnapshotAssertion,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
) -> None:
|
||||
"""Test states of the sensor."""
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
await snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id)
|
||||
@@ -0,0 +1,176 @@
|
||||
"""Unit test for Electrolux config flow."""
|
||||
|
||||
from unittest.mock import AsyncMock
|
||||
|
||||
from electrolux_group_developer_sdk.auth.invalid_credentials_exception import (
|
||||
InvalidCredentialsException,
|
||||
)
|
||||
from electrolux_group_developer_sdk.client.bad_credentials_exception import (
|
||||
BadCredentialsException,
|
||||
)
|
||||
from electrolux_group_developer_sdk.client.failed_connection_exception import (
|
||||
FailedConnectionException,
|
||||
)
|
||||
|
||||
from homeassistant import config_entries, data_entry_flow
|
||||
from homeassistant.components.electrolux.const import CONF_REFRESH_TOKEN, DOMAIN
|
||||
from homeassistant.const import CONF_ACCESS_TOKEN, CONF_API_KEY
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
valid_user_input = {
|
||||
CONF_API_KEY: "test_api_key",
|
||||
CONF_ACCESS_TOKEN: "test_access_token",
|
||||
CONF_REFRESH_TOKEN: "test_refresh_token",
|
||||
}
|
||||
|
||||
invalid_user_input = {
|
||||
CONF_API_KEY: "api_key",
|
||||
CONF_ACCESS_TOKEN: "invalid_token",
|
||||
CONF_REFRESH_TOKEN: "invalid_token",
|
||||
}
|
||||
|
||||
|
||||
async def test_user_flow_success(
|
||||
hass: HomeAssistant, appliances: AsyncMock, mock_token_manager: AsyncMock
|
||||
) -> None:
|
||||
"""Test a successful user config flow."""
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||
)
|
||||
|
||||
assert result["type"] is data_entry_flow.FlowResultType.FORM
|
||||
assert result["errors"] == {}
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], user_input=valid_user_input
|
||||
)
|
||||
|
||||
assert result["type"] is data_entry_flow.FlowResultType.CREATE_ENTRY
|
||||
assert result["title"] == "Electrolux for mock@email.com"
|
||||
assert result["data"] == valid_user_input
|
||||
assert result["result"].unique_id == "mock_user_id"
|
||||
|
||||
|
||||
async def test_user_flow_invalid_auth(
|
||||
hass: HomeAssistant, appliances: AsyncMock, mock_token_manager: AsyncMock
|
||||
) -> None:
|
||||
"""Test an invalid auth config flow."""
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||
)
|
||||
|
||||
assert result["type"] is data_entry_flow.FlowResultType.FORM
|
||||
|
||||
mock_token_manager.ensure_credentials.side_effect = InvalidCredentialsException
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
user_input=invalid_user_input,
|
||||
)
|
||||
|
||||
assert result["type"] is data_entry_flow.FlowResultType.FORM
|
||||
assert result["errors"] == {"base": "invalid_auth"}
|
||||
|
||||
mock_token_manager.ensure_credentials.side_effect = None
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], user_input=valid_user_input
|
||||
)
|
||||
|
||||
assert result["type"] is data_entry_flow.FlowResultType.CREATE_ENTRY
|
||||
assert result["title"] == "Electrolux for mock@email.com"
|
||||
assert result["data"] == valid_user_input
|
||||
assert result["result"].unique_id == "mock_user_id"
|
||||
|
||||
|
||||
async def test_user_flow_bad_credentials(
|
||||
hass: HomeAssistant, appliances: AsyncMock, mock_token_manager: AsyncMock
|
||||
) -> None:
|
||||
"""Test an invalid auth config flow."""
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||
)
|
||||
|
||||
assert result["type"] is data_entry_flow.FlowResultType.FORM
|
||||
|
||||
appliances.test_connection.side_effect = BadCredentialsException()
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
user_input=invalid_user_input,
|
||||
)
|
||||
|
||||
assert result["type"] is data_entry_flow.FlowResultType.FORM
|
||||
assert result["errors"] == {"base": "invalid_auth"}
|
||||
|
||||
appliances.test_connection.side_effect = None
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], user_input=valid_user_input
|
||||
)
|
||||
|
||||
assert result["type"] is data_entry_flow.FlowResultType.CREATE_ENTRY
|
||||
assert result["title"] == "Electrolux for mock@email.com"
|
||||
assert result["data"] == valid_user_input
|
||||
assert result["result"].unique_id == "mock_user_id"
|
||||
|
||||
|
||||
async def test_user_flow_failed_connection(
|
||||
hass: HomeAssistant, appliances: AsyncMock, mock_token_manager: AsyncMock
|
||||
) -> None:
|
||||
"""Test an invalid auth config flow."""
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||
)
|
||||
|
||||
assert result["type"] is data_entry_flow.FlowResultType.FORM
|
||||
|
||||
appliances.test_connection.side_effect = FailedConnectionException()
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
user_input=invalid_user_input,
|
||||
)
|
||||
|
||||
assert result["type"] is data_entry_flow.FlowResultType.FORM
|
||||
assert result["errors"] == {"base": "cannot_connect"}
|
||||
|
||||
appliances.test_connection.side_effect = None
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], user_input=valid_user_input
|
||||
)
|
||||
|
||||
assert result["type"] is data_entry_flow.FlowResultType.CREATE_ENTRY
|
||||
assert result["title"] == "Electrolux for mock@email.com"
|
||||
assert result["data"] == valid_user_input
|
||||
assert result["result"].unique_id == "mock_user_id"
|
||||
|
||||
|
||||
async def test_user_flow_duplicate_entry(
|
||||
hass: HomeAssistant,
|
||||
appliances: AsyncMock,
|
||||
mock_token_manager: AsyncMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
) -> None:
|
||||
"""Test an invalid auth config flow."""
|
||||
mock_config_entry.add_to_hass(hass)
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||
)
|
||||
|
||||
assert result["type"] is data_entry_flow.FlowResultType.FORM
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
user_input=valid_user_input,
|
||||
)
|
||||
|
||||
assert result["type"] is data_entry_flow.FlowResultType.ABORT
|
||||
assert result["reason"] == "already_configured"
|
||||
@@ -0,0 +1,189 @@
|
||||
"""Unit test for Electrolux init flow."""
|
||||
|
||||
from unittest.mock import AsyncMock
|
||||
|
||||
from electrolux_group_developer_sdk.client.appliance_data_factory import (
|
||||
appliance_data_factory,
|
||||
)
|
||||
from electrolux_group_developer_sdk.client.appliances.appliance_data import (
|
||||
ApplianceData,
|
||||
)
|
||||
from electrolux_group_developer_sdk.client.bad_credentials_exception import (
|
||||
BadCredentialsException,
|
||||
)
|
||||
from electrolux_group_developer_sdk.client.client_exception import (
|
||||
ApplianceClientException,
|
||||
)
|
||||
from electrolux_group_developer_sdk.client.failed_connection_exception import (
|
||||
FailedConnectionException,
|
||||
)
|
||||
import pytest
|
||||
from syrupy.assertion import SnapshotAssertion
|
||||
|
||||
from homeassistant.components.electrolux import ElectroluxData
|
||||
from homeassistant.components.electrolux.const import DOMAIN
|
||||
from homeassistant.config_entries import ConfigEntryState
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import device_registry as dr
|
||||
|
||||
from . import (
|
||||
APPLIANCE_FIXTURES,
|
||||
get_fixture_name,
|
||||
load_appliance,
|
||||
load_appliance_details,
|
||||
load_appliance_state,
|
||||
setup_integration,
|
||||
)
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
|
||||
async def test_async_setup_entry_success(
|
||||
hass: HomeAssistant,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
appliances: AsyncMock,
|
||||
mock_token_manager: AsyncMock,
|
||||
) -> None:
|
||||
"""Test successful setup of the Electrolux integration."""
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
|
||||
# Check integration is loaded
|
||||
assert mock_config_entry.state is ConfigEntryState.LOADED
|
||||
assert mock_config_entry.runtime_data is not None
|
||||
assert isinstance(mock_config_entry.runtime_data, ElectroluxData)
|
||||
|
||||
# Unload the config entry
|
||||
assert await hass.config_entries.async_unload(mock_config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert mock_config_entry.state is ConfigEntryState.NOT_LOADED
|
||||
|
||||
|
||||
def test_appliance_fixture_data() -> None:
|
||||
"""Test that all appliance fixtures are configured correctly."""
|
||||
appliance_id_set = set()
|
||||
for appliance_fixture in APPLIANCE_FIXTURES:
|
||||
appliance = load_appliance(appliance_fixture)
|
||||
appliance_id = appliance.applianceId
|
||||
assert appliance_id not in appliance_id_set, (
|
||||
f"Duplicate appliance ID {appliance_id} detected in fixture {appliance_fixture}"
|
||||
)
|
||||
|
||||
appliance_state = load_appliance_state(appliance_fixture)
|
||||
assert appliance_id == appliance_state.applianceId, (
|
||||
f"Appliance ID in state {appliance_state.applianceId} does not match appliance ID in appliance object {appliance_id}"
|
||||
)
|
||||
|
||||
appliance_id_set.add(appliance_id)
|
||||
|
||||
|
||||
async def test_all_appliances(
|
||||
hass: HomeAssistant,
|
||||
snapshot: SnapshotAssertion,
|
||||
appliances: AsyncMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
device_registry: dr.DeviceRegistry,
|
||||
) -> None:
|
||||
"""Test all entities for all appliance fixtures."""
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
|
||||
appliance_list: list[ApplianceData] = await appliances.get_appliance_data()
|
||||
for appliance in appliance_list:
|
||||
appliance_id = appliance.appliance.applianceId
|
||||
|
||||
device = device_registry.async_get_device({(DOMAIN, appliance_id)})
|
||||
|
||||
assert device is not None
|
||||
assert device == snapshot(name=get_fixture_name(appliance_id))
|
||||
|
||||
|
||||
async def test_check_for_dynamic_devices(
|
||||
hass: HomeAssistant,
|
||||
mock_appliance_client: AsyncMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
device_registry: dr.DeviceRegistry,
|
||||
) -> None:
|
||||
"""Test that the integration adds and removes devices correctly."""
|
||||
old_appliance_fixture = "fenix_oven"
|
||||
old_appliance_id = "900412569_00:43319382-443E0748CCD4"
|
||||
|
||||
new_appliance_fixture = "pux_oven"
|
||||
new_appliance_id = "949288049_00:11112225-443E076A37D6"
|
||||
|
||||
def set_appliance_fixture_mock(appliance_fixture: str):
|
||||
|
||||
appliance = load_appliance(appliance_fixture)
|
||||
details = load_appliance_details(appliance_fixture)
|
||||
state = load_appliance_state(appliance_fixture)
|
||||
|
||||
appliance_data = appliance_data_factory(
|
||||
appliance=appliance,
|
||||
details=details,
|
||||
state=state,
|
||||
)
|
||||
|
||||
mock_appliance_client.get_appliance_data.return_value = [appliance_data]
|
||||
|
||||
set_appliance_fixture_mock(old_appliance_fixture)
|
||||
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
|
||||
assert device_registry.async_get_device({(DOMAIN, old_appliance_id)}) is not None
|
||||
assert device_registry.async_get_device({(DOMAIN, new_appliance_id)}) is None
|
||||
|
||||
set_appliance_fixture_mock(new_appliance_fixture)
|
||||
|
||||
event_stream_call_args = mock_appliance_client.start_event_stream.call_args.args
|
||||
assert event_stream_call_args is not None and len(event_stream_call_args) > 0, (
|
||||
"start_event_stream method called without any callbacks specified"
|
||||
)
|
||||
|
||||
callback_list = event_stream_call_args[0]
|
||||
for callback in callback_list:
|
||||
await callback()
|
||||
|
||||
assert device_registry.async_get_device({(DOMAIN, old_appliance_id)}) is None
|
||||
assert device_registry.async_get_device({(DOMAIN, new_appliance_id)}) is not None
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("exception", "entry_state"),
|
||||
[
|
||||
(ApplianceClientException(), ConfigEntryState.SETUP_RETRY),
|
||||
],
|
||||
)
|
||||
async def test_appliance_client_exception(
|
||||
hass: HomeAssistant,
|
||||
mock_appliance_client: AsyncMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
exception: Exception,
|
||||
entry_state: ConfigEntryState,
|
||||
) -> None:
|
||||
"""Test for handling errors that occur while getting the data of all appliances."""
|
||||
mock_appliance_client.get_appliance_data.side_effect = exception
|
||||
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
|
||||
assert mock_config_entry.state is entry_state
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("exception", "entry_state"),
|
||||
[
|
||||
(BadCredentialsException(), ConfigEntryState.SETUP_ERROR),
|
||||
(FailedConnectionException(), ConfigEntryState.SETUP_RETRY),
|
||||
],
|
||||
)
|
||||
async def test_appliance_client_test_connection_bad_credentials(
|
||||
hass: HomeAssistant,
|
||||
mock_appliance_client: AsyncMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
exception: Exception,
|
||||
entry_state: ConfigEntryState,
|
||||
) -> None:
|
||||
"""Test for handling errors that occur while testing the connection."""
|
||||
mock_appliance_client.test_connection.side_effect = exception
|
||||
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
|
||||
assert mock_config_entry.state is entry_state
|
||||
@@ -0,0 +1,34 @@
|
||||
"""Sensor tests of Electrolux integration."""
|
||||
|
||||
from collections.abc import Generator
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
from syrupy.assertion import SnapshotAssertion
|
||||
|
||||
from homeassistant.const import Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
|
||||
from . import setup_integration
|
||||
|
||||
from tests.common import MockConfigEntry, snapshot_platform
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def override_platforms() -> Generator[None]:
|
||||
"""Override PLATFORMS."""
|
||||
with patch("homeassistant.components.electrolux.PLATFORMS", [Platform.SENSOR]):
|
||||
yield
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("appliances")
|
||||
async def test_sensor(
|
||||
hass: HomeAssistant,
|
||||
entity_registry: er.EntityRegistry,
|
||||
snapshot: SnapshotAssertion,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
) -> None:
|
||||
"""Test states of the sensor."""
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
await snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id)
|
||||
Reference in New Issue
Block a user