Перейти к содержанию

Бытовая техника

KettleEntity

Умный чайник: управление температурой нагрева.

Sber Kettle entity -- maps HA water_heater entities to Sber kettle category.

Supports on/off control, water temperature reading, and target temperature setting.

Two very different kinds of HA entity end up in this category (see CATEGORY_DOMAIN_MAP):

  • a plain switch — a dumb kettle on a smart socket. It has turn_on / turn_off and nothing else.
  • a water_heater — a real smart kettle. Here turn_on / turn_off are optional in Home Assistant: an integration only gets them by declaring WaterHeaterEntityFeature.ON_OFF, and the SkyKettle-style integrations do not. Such a kettle is driven purely through set_operation_mode with model-specific mode names taken from its operation_list attribute ("Boil", "Heat", "off", …), and a bare set_temperature only moves the setpoint without ever starting the heater.

:class:KettleEntity therefore prefers operation modes whenever a water_heater advertises an operation_list, and falls back to the historical turn_on / turn_off / set_temperature calls otherwise — for a switch always, since no switch.set_operation_mode service exists in Home Assistant.

KETTLE_CATEGORY module-attribute

KETTLE_CATEGORY = 'kettle'

Sber device category for kettle entities.

KETTLE_TEMPERATURE_MIN module-attribute

KETTLE_TEMPERATURE_MIN = 60

Lowest target temperature offered to Sber, in °C.

KETTLE_TEMPERATURE_MAX module-attribute

KETTLE_TEMPERATURE_MAX = 100

Highest target temperature offered to Sber, in °C (i.e. "boil").

KETTLE_TEMPERATURE_STEP module-attribute

KETTLE_TEMPERATURE_STEP = 10

Step of the Sber target-temperature slider, in °C.

MODE_DRIVEN_DOMAIN module-attribute

MODE_DRIVEN_DOMAIN = 'water_heater'

The only HA domain that can be driven through set_operation_mode.

water_heater is the sole domain in Home Assistant that both publishes an operation_list attribute and registers a set_operation_mode service. The kettle category also accepts a plain switch (a dumb kettle on a smart socket, see CATEGORY_DOMAIN_MAP), and a template switch is free to carry an operation_list attribute of its own — routing that entity through switch.set_operation_mode would raise ServiceNotFound on every single Sber command.

KETTLE_OPTION_OFF_MODE module-attribute

KETTLE_OPTION_OFF_MODE = 'off_mode'

Entity-option key naming the HA operation mode that switches the kettle off.

KETTLE_OPTION_BOIL_MODE module-attribute

KETTLE_OPTION_BOIL_MODE = 'boil_mode'

Entity-option key naming the HA operation mode that boils the water.

KETTLE_OPTION_HEAT_MODE module-attribute

KETTLE_OPTION_HEAT_MODE = 'heat_mode'

Entity-option key naming the HA operation mode that heats to a setpoint.

OFF_MODE_CANDIDATES module-attribute

OFF_MODE_CANDIDATES = ('off',)

Mode names auto-detected as "switch the kettle off" (case-insensitive).

BOIL_MODE_CANDIDATES module-attribute

BOIL_MODE_CANDIDATES = ('boil',)

Mode names auto-detected as "boil" (case-insensitive).

HEAT_MODE_CANDIDATES module-attribute

HEAT_MODE_CANDIDATES = ('heat', 'electric', 'eco', 'gas', 'heat_pump', 'high_demand', 'performance')

Mode names auto-detected as "heat to the setpoint", best match first.

heat is what SkyKettle-style kettles use; the rest are Home Assistant's own water_heater constants, which a generic integration is likely to reuse. Order is preference order, not alphabetical.

KettleEntity

KettleEntity(entity_data)

Bases: BaseEntity

Sber kettle entity for smart kettle devices.

Maps HA water_heater entities to the Sber 'kettle' category with support for: - On/off control - Current water temperature reading - Target temperature setting (60-100, step 10) - Child lock (read-only from HA attributes) - Water level and low water level indicators

The Sber kettle spec has no notion of a "mode": the whole mapping from Sber's on_off + kitchen_water_temperature_set onto a kettle's own operation modes lives here.

Initialize kettle entity.

Parameters:

Name Type Description Default
entity_data dict

HA entity registry dict containing entity metadata.

required
Source code in custom_components/sber_mqtt_bridge/devices/kettle.py
def __init__(self, entity_data: dict) -> None:
    """Initialize kettle entity.

    Args:
        entity_data: HA entity registry dict containing entity metadata.
    """
    super().__init__(KETTLE_CATEGORY, entity_data)
    self.current_state: bool = False
    self._current_temperature: int | None = None
    self._target_temperature: int | None = None
    self._child_lock: bool = False
    self._water_level: int | None = None
    self._operation_list: tuple[str, ...] = ()
    self._operation_mode: str | None = None
    self._ha_max_temperature: int | None = None
    # User-chosen mode names; None means "auto-detect from operation_list".
    self._off_mode: str | None = None
    self._boil_mode: str | None = None
    self._heat_mode: str | None = None
    self._missing_mode_logged: set[str] = set()

available_operation_modes property

available_operation_modes

Operation modes this entity can actually be driven with.

The raw operation_list attribute filtered by the one thing it cannot tell us: whether a set_operation_mode service exists for this entity at all. Only :data:MODE_DRIVEN_DOMAIN has one, so for every other domain the answer is "no modes", no matter what the attribute says.

Returns:

Type Description
str

Mode names in the order the integration reported them, or an

...

empty tuple when this entity is not mode-driven.

supports_operation_modes property

supports_operation_modes

True when the HA entity is driven through set_operation_mode.

Decided by the entity itself: a switch kettle (and any water_heater that reports no operation_list) keeps the historical turn_on / turn_off path untouched.

apply_entity_options

apply_entity_options(options)

Apply per-entity kettle options from entry.options.

Parameters:

Name Type Description Default
options dict

Mapping with optional off_mode / boil_mode / heat_mode keys, each naming one entry of the entity's HA operation_list. An empty value restores auto-detection. Invalid values are ignored here (a hand-edited config must not break entity loading) — the WebSocket command validates them up front instead, see :meth:validate_entity_options.

required
Source code in custom_components/sber_mqtt_bridge/devices/kettle.py
def apply_entity_options(self, options: dict) -> None:
    """Apply per-entity kettle options from ``entry.options``.

    Args:
        options: Mapping with optional ``off_mode`` / ``boil_mode`` /
            ``heat_mode`` keys, each naming one entry of the entity's
            HA ``operation_list``.  An empty value restores
            auto-detection.  Invalid values are ignored here (a
            hand-edited config must not break entity loading) — the
            WebSocket command validates them up front instead, see
            :meth:`validate_entity_options`.
    """
    if not options:
        return
    if KETTLE_OPTION_OFF_MODE in options:
        self._off_mode = _optional_str(options[KETTLE_OPTION_OFF_MODE])
    if KETTLE_OPTION_BOIL_MODE in options:
        self._boil_mode = _optional_str(options[KETTLE_OPTION_BOIL_MODE])
    if KETTLE_OPTION_HEAT_MODE in options:
        self._heat_mode = _optional_str(options[KETTLE_OPTION_HEAT_MODE])
    self._missing_mode_logged.clear()
    _LOGGER.debug(
        "Kettle options for %s: off=%r boil=%r heat=%r",
        self.entity_id,
        self._off_mode,
        self._boil_mode,
        self._heat_mode,
    )

validate_entity_options

validate_entity_options(options)

Reject mode names this entity does not actually offer.

A mode that is not in the entity's operation_list would be silently dropped by the HA service call, leaving the user with a kettle that acknowledges commands and never heats — so the mismatch is reported at the moment they save it.

Parameters:

Name Type Description Default
options dict

Mapping submitted by the panel.

required

Raises:

Type Description
ValueError

When a key is unknown to this class, when a value is not a string, or when it names a mode the entity does not report.

Source code in custom_components/sber_mqtt_bridge/devices/kettle.py
def validate_entity_options(self, options: dict) -> None:
    """Reject mode names this entity does not actually offer.

    A mode that is not in the entity's ``operation_list`` would be
    silently dropped by the HA service call, leaving the user with a
    kettle that acknowledges commands and never heats — so the
    mismatch is reported at the moment they save it.

    Args:
        options: Mapping submitted by the panel.

    Raises:
        ValueError: When a key is unknown to this class, when a value
            is not a string, or when it names a mode the entity does
            not report.
    """
    super().validate_entity_options(options)
    chosen = {key: options[key] for key in self.ENTITY_OPTION_KEYS if key in options}
    wanted = {key: _optional_str(value) for key, value in chosen.items()}
    for key, value in chosen.items():
        if value is not None and not isinstance(value, str):
            raise ValueError(f"{self.entity_id}: {key} must be a mode name, got {value!r}")
    if not any(wanted.values()):
        return
    available = self.available_operation_modes
    if not available:
        raise ValueError(
            f"{self.entity_id} reports no operation modes (HA attribute 'operation_list' is empty) — "
            "leave the mode fields empty; the bridge will use turn_on / turn_off instead"
        )
    for key, mode in wanted.items():
        if mode is not None and mode not in available:
            raise ValueError(
                f"{self.entity_id}: '{mode}' is not one of this kettle's operation modes "
                f"({', '.join(available)}) — pick one of them for {key}"
            )

entity_options_state

entity_options_state()

Return the kettle option block rendered by the panel.

The panel needs three things: what the user picked (empty string means "auto"), what the entity actually offers so the dropdowns are not free text, and what the bridge resolved — the last one is what a user who configured nothing must be able to check.

Returns:

Type Description
dict[str, object]

Explicit off_mode / boil_mode / heat_mode choices,

dict[str, object]

the entity's operation_list, and the resolved_*

dict[str, object]

counterparts (empty string when nothing could be resolved).

Source code in custom_components/sber_mqtt_bridge/devices/kettle.py
def entity_options_state(self) -> dict[str, object]:
    """Return the kettle option block rendered by the panel.

    The panel needs three things: what the user picked (empty string
    means "auto"), what the entity actually offers so the dropdowns
    are not free text, and what the bridge resolved — the last one is
    what a user who configured nothing must be able to check.

    Returns:
        Explicit ``off_mode`` / ``boil_mode`` / ``heat_mode`` choices,
        the entity's ``operation_list``, and the ``resolved_*``
        counterparts (empty string when nothing could be resolved).
    """
    return {
        KETTLE_OPTION_OFF_MODE: self._off_mode or "",
        KETTLE_OPTION_BOIL_MODE: self._boil_mode or "",
        KETTLE_OPTION_HEAT_MODE: self._heat_mode or "",
        "operation_list": list(self.available_operation_modes),
        "resolved_off_mode": self._resolve_mode(self._off_mode, OFF_MODE_CANDIDATES) or "",
        "resolved_boil_mode": self._resolve_mode(self._boil_mode, BOIL_MODE_CANDIDATES) or "",
        "resolved_heat_mode": self._resolve_mode(self._heat_mode, HEAT_MODE_CANDIDATES) or "",
    }

fill_by_ha_state

fill_by_ha_state(ha_state)

Parse HA state and update kettle attributes.

Parameters:

Name Type Description Default
ha_state dict

HA state dict with 'state' and 'attributes' keys.

required
Source code in custom_components/sber_mqtt_bridge/devices/kettle.py
def fill_by_ha_state(self, ha_state: dict) -> None:
    """Parse HA state and update kettle attributes.

    Args:
        ha_state: HA state dict with 'state' and 'attributes' keys.
    """
    super().fill_by_ha_state(ha_state)
    attrs = ha_state.get("attributes", {})
    self._apply_attr_specs(attrs)
    state_str = ha_state.get("state", "")
    self.current_state = state_str not in ("off", "idle", "unavailable", "unknown")
    # A mode-driven kettle reports its *mode* as the HA state, and an
    # "off" mode may be named anything ("Выключен"), so the generic
    # word list above cannot recognise it.
    off_mode = self._resolve_mode(self._off_mode, OFF_MODE_CANDIDATES)
    if off_mode is not None and self._current_mode == off_mode:
        self.current_state = False

create_allowed_values_list

create_allowed_values_list()

Build allowed values map for temperature setting.

The range is deliberately not derived from the entity's HA min_temp / max_temp: model.id is a digest of the advertised capabilities, so making the range device-specific would hand every existing user a brand-new Sber model for a kettle that did not change.

Returns:

Type Description
dict[str, dict]

Dict mapping feature key to its allowed INTEGER values descriptor.

Source code in custom_components/sber_mqtt_bridge/devices/kettle.py
def create_allowed_values_list(self) -> dict[str, dict]:
    """Build allowed values map for temperature setting.

    The range is deliberately **not** derived from the entity's HA
    ``min_temp`` / ``max_temp``: ``model.id`` is a digest of the
    advertised capabilities, so making the range device-specific would
    hand every existing user a brand-new Sber model for a kettle that
    did not change.

    Returns:
        Dict mapping feature key to its allowed INTEGER values descriptor.
    """
    return {
        "kitchen_water_temperature_set": {
            "type": "INTEGER",
            "integer_values": {
                "min": str(KETTLE_TEMPERATURE_MIN),
                "max": str(KETTLE_TEMPERATURE_MAX),
                "step": str(KETTLE_TEMPERATURE_STEP),
            },
        }
    }

process_cmd

process_cmd(cmd_data)

Turn a Sber command into HA service calls.

Mode-driven kettles are handled as a whole payload rather than key by key, because on_off and kitchen_water_temperature_set describe one intent when they arrive together ("heat this water to 80") and would otherwise produce two contradictory mode switches.

Falls back to :meth:BaseEntity.process_cmd — i.e. to the historical turn_on / turn_off / set_temperature calls — for a kettle without operation modes, and for a mode-driven one whose relevant mode could not be resolved.

Parameters:

Name Type Description Default
cmd_data dict

Command payload with a states list.

required

Returns:

Type Description
list[CommandResult]

List of HA service call dicts to execute.

Source code in custom_components/sber_mqtt_bridge/devices/kettle.py
def process_cmd(self, cmd_data: dict) -> list[CommandResult]:
    """Turn a Sber command into HA service calls.

    Mode-driven kettles are handled as a whole payload rather than
    key by key, because ``on_off`` and
    ``kitchen_water_temperature_set`` describe **one** intent when
    they arrive together ("heat this water to 80") and would
    otherwise produce two contradictory mode switches.

    Falls back to :meth:`BaseEntity.process_cmd` — i.e. to the
    historical ``turn_on`` / ``turn_off`` / ``set_temperature`` calls
    — for a kettle without operation modes, and for a mode-driven one
    whose relevant mode could not be resolved.

    Args:
        cmd_data: Command payload with a ``states`` list.

    Returns:
        List of HA service call dicts to execute.
    """
    if not self.supports_operation_modes:
        return super().process_cmd(cmd_data)

    on_off: bool | None = None
    temperature: int | None = None
    understood = False
    for item in cmd_data.get("states", []):
        key = item.get("key", "")
        value = normalize_sber_value(item.get("value", {}))
        if key == SberFeature.ON_OFF and value.get("type") == SberValueType.BOOL:
            on_off = bool(value.get("bool_value", False))
            understood = True
        elif key == SberFeature.KITCHEN_WATER_TEMPERATURE_SET and value.get("type") == SberValueType.INTEGER:
            parsed = _safe_int_parser(value.get("integer_value"))
            if parsed is not None and self._is_requestable_temperature(parsed):
                temperature = parsed
                understood = True

    if not understood:
        return super().process_cmd(cmd_data)
    plan = self._plan_mode_calls(on_off, temperature)
    if plan is None:
        return super().process_cmd(cmd_data)
    return plan

VacuumCleanerEntity

Робот-пылесос: режимы уборки, управление.

Sber Vacuum Cleaner entity -- maps HA vacuum entities to Sber vacuum_cleaner category.

Supports start/resume/pause/return_to_dock commands, status reporting, cleaning program (derived from the HA mode list) and battery level. Every ENUM value crossing to the cloud comes from Sber's documented vocabulary in _generated/reference_values.py; HA names that denote nothing Sber knows are dropped rather than translated by guesswork. Battery is sourced from the deprecated HA vacuum battery_level attribute (legacy fallback, removal planned in HA 2026.8) or from a linked battery sensor entity via the battery link role.

VACUUM_CLEANER_CATEGORY module-attribute

VACUUM_CLEANER_CATEGORY = 'vacuum_cleaner'

Sber device category for vacuum cleaner entities.

PROGRAM_VALUES module-attribute

PROGRAM_VALUES = FEATURE_ENUM_VALUES['vacuum_cleaner_program']

Cleaning routes Sber documents: perimeter, spot, smart, random_route.

Known mismatch, deliberately accepted. These are cleaning routes, but the only list Home Assistant's vacuum entity offers is fan_speed_list, which is suction power. The two coincide only by name, so:

  • a robot whose modes read Silent / Standard / Turbo — the common case — matches nothing and gets no route control at all, which is honest but means the Sber app shows no program selector;
  • a robot that happens to spell a mode Spot or Smart gets the control, and choosing that route in the app calls vacuum.set_fan_speed — i.e. it changes suction, not route.

Both beat the previous behaviour (publishing raw HA fan-speed names, which Sber cannot route at all), and neither can be fixed here: HA has no route-carrying attribute to read. Fixing it properly needs a per-device mapping the user configures, which is why no synonym is invented for auto — that is a common fan-speed name and would silently hand the route control to every robot that has it.

CLEANING_TYPE_VALUES module-attribute

CLEANING_TYPE_VALUES = FEATURE_ENUM_VALUES['vacuum_cleaner_cleaning_type']

Cleaning types Sber documents: dry, wet, mixed.

VacuumCleanerEntity

VacuumCleanerEntity(entity_data)

Bases: BaseEntity

Sber vacuum cleaner entity for robot vacuum devices.

Maps HA vacuum entities to the Sber 'vacuum_cleaner' category with support for: - start / resume / pause / return_to_dock commands - Status reporting, folded onto Sber's four documented values (see :data:_HA_STATE_TO_SBER_STATUS) - Cleaning program, when the HA mode list names a documented Sber route (:data:PROGRAM_VALUES) - Battery percentage (legacy battery_level attribute or linked battery sensor via the battery link role)

Every ENUM this class emits is taken from :data:~custom_components.sber_mqtt_bridge._generated.reference_values.FEATURE_ENUM_VALUES. HA's own vocabulary (fan-speed names, STATE_* constants) overlaps it only by accident, and a value outside the documented set is one the cloud cannot route — it renders a control that never works.

Command handlers address the entity in its own HA domain (:meth:get_entity_domain) rather than a hard-coded vacuum, so an entity forced into this category by a user type override is driven through services that actually exist for it. For a vacuum.* entity — the only domain this category maps to — the emitted calls are unchanged.

Initialize vacuum cleaner entity.

Parameters:

Name Type Description Default
entity_data dict

HA entity registry dict containing entity metadata.

required
Source code in custom_components/sber_mqtt_bridge/devices/vacuum_cleaner.py
def __init__(self, entity_data: dict) -> None:
    """Initialize vacuum cleaner entity.

    Args:
        entity_data: HA entity registry dict containing entity metadata.
    """
    super().__init__(VACUUM_CLEANER_CATEGORY, entity_data)
    self._status: str = _DEFAULT_SBER_STATUS
    self._fan_speed: str | None = None
    self._fan_speed_list: list[str] = []
    self._battery_level: int | None = None
    self._cleaning_type: str | None = None
    self._program_to_sber: dict[str, str] = {}
    self._program_to_ha: dict[str, str] = {}

LINKABLE_ROLES class-attribute instance-attribute

LINKABLE_ROLES = (ROLE_BATTERY,)

Linked companion roles: a battery sensor supplies battery_percentage.

HA deprecated the vacuum battery_level attribute (removal in 2026.8); migrated integrations expose battery as a separate sensor entity, which users link here.

fill_by_ha_state

fill_by_ha_state(ha_state)

Parse HA state and update vacuum cleaner attributes.

Parameters:

Name Type Description Default
ha_state dict

HA state dict with 'state' and 'attributes' keys.

required
Source code in custom_components/sber_mqtt_bridge/devices/vacuum_cleaner.py
def fill_by_ha_state(self, ha_state: dict) -> None:
    """Parse HA state and update vacuum cleaner attributes.

    Args:
        ha_state: HA state dict with 'state' and 'attributes' keys.
    """
    super().fill_by_ha_state(ha_state)
    attrs = ha_state.get("attributes", {})
    self._apply_attr_specs(attrs)
    self._program_to_sber = map_ha_values(self._fan_speed_list, PROGRAM_VALUES, synonyms=_PROGRAM_SYNONYMS)
    self._program_to_ha = invert_value_map(self._program_to_sber)
    ha_status = ha_state.get("state", "")
    self._status = _HA_STATE_TO_SBER_STATUS.get(ha_status, _DEFAULT_SBER_STATUS)

update_linked_data

update_linked_data(role, ha_state)

Inject battery percentage from a linked battery sensor entity.

HA deprecated the vacuum battery_level attribute; migrated integrations publish battery as a separate sensor entity. When such a sensor is linked with the battery role, its state feeds the Sber battery_percentage feature.

Parameters:

Name Type Description Default
role str

Link role name (only battery is handled).

required
ha_state dict

HA state dict with 'state' containing the reading.

required
Source code in custom_components/sber_mqtt_bridge/devices/vacuum_cleaner.py
def update_linked_data(self, role: str, ha_state: dict) -> None:
    """Inject battery percentage from a linked battery sensor entity.

    HA deprecated the vacuum ``battery_level`` attribute; migrated
    integrations publish battery as a separate sensor entity. When
    such a sensor is linked with the ``battery`` role, its state
    feeds the Sber ``battery_percentage`` feature.

    Args:
        role: Link role name (only ``battery`` is handled).
        ha_state: HA state dict with 'state' containing the reading.
    """
    if role == "battery":
        state_val = ha_state.get("state")
        if state_val not in (None, "unknown", "unavailable"):
            with contextlib.suppress(TypeError, ValueError):
                self._battery_level = int(float(state_val))

create_allowed_values_list

create_allowed_values_list()

Build allowed values map for vacuum features.

Both entries carry Sber's own vocabulary, never HA's: the app renders exactly what is declared and echoes it back as a command, so an undocumented value is a dead button.

Returns:

Type Description
dict[str, dict]

Dict mapping feature key to its allowed ENUM values descriptor.

Source code in custom_components/sber_mqtt_bridge/devices/vacuum_cleaner.py
def create_allowed_values_list(self) -> dict[str, dict]:
    """Build allowed values map for vacuum features.

    Both entries carry Sber's own vocabulary, never HA's: the app
    renders exactly what is declared and echoes it back as a command,
    so an undocumented value is a dead button.

    Returns:
        Dict mapping feature key to its allowed ENUM values descriptor.
    """
    allowed: dict[str, dict] = {
        "vacuum_cleaner_command": {
            "type": "ENUM",
            "enum_values": {"values": list(_SBER_CMD_TO_HA_SERVICE.keys())},
        },
    }
    if self._program_to_sber:
        allowed["vacuum_cleaner_program"] = {
            "type": "ENUM",
            "enum_values": {"values": list(self._program_to_sber.values())},
        }
    # vacuum_cleaner_status and vacuum_cleaner_cleaning_type are read-only:
    # not included in allowed_values to prevent Sber from sending commands
    # for features that have no HA service handler.
    return allowed

HumidifierEntity

Увлажнитель воздуха.

Sber Humidifier entity -- maps HA humidifier entities to Sber hvac_humidifier.

HUMIDIFIER_CATEGORY module-attribute

HUMIDIFIER_CATEGORY = 'hvac_humidifier'

Sber device category for humidifier entities.

HA_TO_SBER_HUMIDIFIER_MODE module-attribute

HA_TO_SBER_HUMIDIFIER_MODE = {'auto': 'auto', 'low': 'low', 'mid': 'medium', 'medium': 'medium', 'normal': 'medium', 'comfort': 'medium', 'high': 'high', 'silent': 'quiet', 'sleep': 'quiet', 'night': 'quiet', 'eco': 'quiet', 'strong': 'turbo', 'boost': 'turbo'}

Map HA humidifier modes to Sber-standard enum values (case-insensitive lookup).

Sber hvac_air_flow_power accepts only auto/low/medium/high/turbo/quiet — standard HA modes (MODE_NORMAL, MODE_ECO, MODE_COMFORT) map to the semantically closest value; unmapped device-specific modes are dropped (issue #44 audit — raw HA strings must not leak into Sber enums).

HumidifierEntity

HumidifierEntity(entity_data)

Bases: BaseEntity

Sber humidifier entity for humidity control devices.

Maps HA humidifier entities to the Sber 'hvac_humidifier' category with support for: - On/off control - Target humidity setting - Work mode selection (when supported by the device)

Command handlers address the entity in its own HA domain (:meth:get_entity_domain) rather than a hard-coded humidifier, so an entity forced into this category by a user type override is driven through services that actually exist for it. For a humidifier.* entity the emitted calls are unchanged.

Initialize humidifier entity.

Parameters:

Name Type Description Default
entity_data dict

HA entity registry dict containing entity metadata.

required
Source code in custom_components/sber_mqtt_bridge/devices/humidifier.py
def __init__(self, entity_data: dict) -> None:
    """Initialize humidifier entity.

    Args:
        entity_data: HA entity registry dict containing entity metadata.
    """
    super().__init__(HUMIDIFIER_CATEGORY, entity_data)
    self.current_state = False
    self.target_humidity = None
    self.current_humidity = None
    self.available_modes: list[str] = []
    self.mode: str | None = None
    self._min_humidity: int = 35
    self._max_humidity: int = 85
    self._water_percentage: int | None = None
    self._water_low_level: bool | None = None
    self._child_lock: bool | None = None

fill_by_ha_state

fill_by_ha_state(ha_state)

Parse HA state and update all humidifier attributes.

Parameters:

Name Type Description Default
ha_state dict

HA state dict with 'state' and 'attributes' keys. Attributes may include humidity, current_humidity, available_modes, and mode.

required
Source code in custom_components/sber_mqtt_bridge/devices/humidifier.py
def fill_by_ha_state(self, ha_state: dict) -> None:
    """Parse HA state and update all humidifier attributes.

    Args:
        ha_state: HA state dict with 'state' and 'attributes' keys.
            Attributes may include humidity, current_humidity,
            available_modes, and mode.
    """
    super().fill_by_ha_state(ha_state)
    attrs = ha_state.get("attributes", {})
    self._apply_attr_specs(attrs)
    self.current_state = ha_state.get("state") == "on"

update_linked_data

update_linked_data(role, ha_state)

Inject current humidity from a linked sensor entity.

When the HA humidifier entity does not provide current_humidity in its attributes, an external humidity sensor can be linked to supply the value for the Sber humidity feature.

Parameters:

Name Type Description Default
role str

Link role name (only humidity is handled).

required
ha_state dict

HA state dict with 'state' containing the reading.

required
Source code in custom_components/sber_mqtt_bridge/devices/humidifier.py
def update_linked_data(self, role: str, ha_state: dict) -> None:
    """Inject current humidity from a linked sensor entity.

    When the HA humidifier entity does not provide ``current_humidity``
    in its attributes, an external humidity sensor can be linked to
    supply the value for the Sber ``humidity`` feature.

    Args:
        role: Link role name (only ``humidity`` is handled).
        ha_state: HA state dict with 'state' containing the reading.
    """
    if role == "humidity":
        state_val = ha_state.get("state")
        if state_val not in (None, "unknown", "unavailable"):
            with contextlib.suppress(TypeError, ValueError):
                self.current_humidity = float(state_val)

create_allowed_values_list

create_allowed_values_list()

Build allowed values map for enum-based and integer-based features.

Returns:

Type Description
dict[str, dict]

Dict mapping feature key to its allowed values descriptor.

Source code in custom_components/sber_mqtt_bridge/devices/humidifier.py
def create_allowed_values_list(self) -> dict[str, dict]:
    """Build allowed values map for enum-based and integer-based features.

    Returns:
        Dict mapping feature key to its allowed values descriptor.
    """
    allowed: dict[str, dict] = {}
    sber_modes = self._mapped_air_flow_values()
    if sber_modes:
        allowed["hvac_air_flow_power"] = {
            "type": "ENUM",
            "enum_values": {"values": sber_modes},
        }
    allowed["hvac_humidity_set"] = {
        "type": "INTEGER",
        "integer_values": {
            "min": str(self._min_humidity),
            "max": str(self._max_humidity),
            "step": "5",
        },
    }
    return allowed