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

Освещение

LightEntity

Управление освещением: яркость, цвет (HSV), цветовая температура.

Sber Light entity — maps HA light to Sber light category.

Supports brightness, color temperature, RGB color (HSV), and light mode. Uses LinearConverter for value range mapping and ColorConverter for HSV.

LIGHT_ENTITY_CATEGORY module-attribute

LIGHT_ENTITY_CATEGORY = 'light'

Sber device category for light entities.

COLOR_MODES module-attribute

COLOR_MODES = {'hs', 'rgb', 'rgbw', 'rgbww', 'xy'}

HA color modes that map to Sber colour features.

LightEntity

LightEntity(ha_entity_data)

Bases: BaseEntity

Sber light entity with brightness, color, and color temperature support.

Maps HA light entities to the Sber 'light' category with support for: - On/off control - Brightness (scaled 0-255 HA ↔ 100-900 Sber) - Color temperature (mireds ↔ 0-1000 Sber, reversed) - RGB color via HSV conversion - Light mode (white / colour)

Initialize light entity from HA entity data.

Parameters:

Name Type Description Default
ha_entity_data dict

HA entity registry dict.

required
Source code in custom_components/sber_mqtt_bridge/devices/light.py
def __init__(self, ha_entity_data: dict) -> None:
    """Initialize light entity from HA entity data.

    Args:
        ha_entity_data: HA entity registry dict.
    """
    super().__init__(LIGHT_ENTITY_CATEGORY, ha_entity_data)
    self.supported_features: int = 0
    self.max_mireds: int = 500
    self.min_mireds: int = 153
    self.supported_color_modes: list[str] = []
    self.current_state: bool = False
    self._ha_brightness_raw: int = 0
    self.current_sber_brightness: int = 0
    self.current_sber_color_temp: int | None = 0
    self.current_color_mode: str | None = None
    self.hs_color: list[float] | None = None
    self.rgb_color: list[int] | None = None
    self.xy_color: list[float] | None = None

    self.brightness_converter = LinearConverter()
    self.brightness_converter.set_ha_limits(0, 255)
    self.brightness_converter.set_sber_limits(100, 900)

    self.color_temp_converter = LinearConverter()
    self.color_temp_converter.set_reversed(True)
    self.color_temp_converter.set_ha_limits(153, 500)
    self.color_temp_converter.set_sber_limits(0, 1000)

fill_by_ha_state

fill_by_ha_state(ha_state)

Parse HA state and update all light 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/light.py
def fill_by_ha_state(self, ha_state: dict) -> None:
    """Parse HA state and update all light 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.max_mireds = attrs.get("max_mireds", 500)
    self.min_mireds = attrs.get("min_mireds", 153)
    if self.max_mireds is not None and self.min_mireds is not None:
        self.color_temp_converter.set_ha_limits(self.min_mireds, self.max_mireds)

    self.current_state = ha_state.get("state", "off") == "on"
    ha_brightness = attrs.get("brightness", 0)
    ha_brightness = int(ha_brightness) if ha_brightness is not None else 0
    self._ha_brightness_raw = ha_brightness

    self.current_sber_brightness = self.brightness_converter.ha_to_sber(ha_brightness)

    ha_color_temp = attrs.get("color_temp", 0)
    if ha_color_temp is not None:
        self.current_sber_color_temp = self.color_temp_converter.ha_to_sber(ha_color_temp)
    else:
        self.current_sber_color_temp = None

    self.current_color_mode = attrs.get("color_mode")
    self.supported_features = attrs.get("supported_features", 0)
    self.supported_color_modes = attrs.get("supported_color_modes") or []

    self.hs_color = attrs.get("hs_color")
    self.rgb_color = attrs.get("rgb_color")
    self.xy_color = attrs.get("xy_color")

create_features_list

create_features_list()

Return Sber feature list based on available light capabilities.

Dynamically includes color, brightness, and color temperature features only when the HA entity supports the corresponding color modes.

Returns:

Type Description
list[str]

List of Sber feature strings supported by this entity.

Source code in custom_components/sber_mqtt_bridge/devices/light.py
def create_features_list(self) -> list[str]:
    """Return Sber feature list based on available light capabilities.

    Dynamically includes color, brightness, and color temperature features
    only when the HA entity supports the corresponding color modes.

    Returns:
        List of Sber feature strings supported by this entity.
    """
    features = [*super().create_features_list(), "on_off"]

    if COLOR_MODES & set(self.supported_color_modes):
        features += ["light_colour", "light_mode", "light_brightness"]
    elif "brightness" in self.supported_color_modes:
        features.append("light_brightness")
    if "color_temp" in self.supported_color_modes:
        features.append("light_colour_temp")

    return features

create_allowed_values_list

create_allowed_values_list()

Build allowed values map for light 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/light.py
def create_allowed_values_list(self) -> dict[str, dict]:
    """Build allowed values map for light features.

    Returns:
        Dict mapping feature key to its allowed values descriptor.
    """
    allowed_values: dict[str, dict] = {}

    if COLOR_MODES & set(self.supported_color_modes):
        allowed_values["light_brightness"] = {
            "type": "INTEGER",
            "integer_values": {"min": "100", "max": "900", "step": "1"},
        }
        allowed_values["light_colour"] = {"type": "COLOUR"}
        allowed_values["light_mode"] = {"type": "ENUM", "enum_values": {"values": ["white", "colour"]}}
    elif "brightness" in self.supported_color_modes:
        allowed_values["light_brightness"] = {
            "type": "INTEGER",
            "integer_values": {"min": "100", "max": "900", "step": "1"},
        }

    if "color_temp" in self.supported_color_modes:
        allowed_values["light_colour_temp"] = {
            "type": "INTEGER",
            "integer_values": {"min": "0", "max": "1000", "step": "1"},
        }

    return allowed_values

create_dependencies

create_dependencies()

Return light_colour → light_mode dependency when both features exist.

Returns:

Type Description
dict[str, dict]

Dependencies dict for Sber model descriptor.

Source code in custom_components/sber_mqtt_bridge/devices/light.py
def create_dependencies(self) -> dict[str, dict]:
    """Return light_colour → light_mode dependency when both features exist.

    Returns:
        Dependencies dict for Sber model descriptor.
    """
    features = self.create_features_list()
    if "light_colour" in features and "light_mode" in features:
        return {
            "light_colour": {
                "key": "light_mode",
                "values": [{"type": "ENUM", "enum_value": "colour"}],
            },
        }
    return {}

to_sber_current_state

to_sber_current_state()

Build Sber current state payload with all light attributes.

Includes online, on_off, brightness, color/color_temp, and light_mode depending on the current state and color mode.

Per Sber C2C specification, integer_value is serialized as a string.

Returns:

Type Description
dict[str, dict]

Dict mapping entity_id to its Sber state representation.

Source code in custom_components/sber_mqtt_bridge/devices/light.py
def to_sber_current_state(self) -> dict[str, dict]:
    """Build Sber current state payload with all light attributes.

    Includes online, on_off, brightness, color/color_temp, and light_mode
    depending on the current state and color mode.

    Per Sber C2C specification, ``integer_value`` is serialized as a string.

    Returns:
        Dict mapping entity_id to its Sber state representation.
    """
    states = [
        make_state(SberFeature.ONLINE, make_bool_value(self._is_online)),
        make_state(SberFeature.ON_OFF, make_bool_value(self.current_state)),
    ]

    if self.current_sber_brightness != 0:
        states.append(
            make_state(SberFeature.LIGHT_BRIGHTNESS, make_integer_value(self.current_sber_brightness))
        )

    if self.current_state:
        if self._is_current_color_mode_colored() and isinstance(self.hs_color, (list, tuple)) and len(self.hs_color) >= 2:
            current_color_sber = ColorConverter.ha_to_sber_hsv(
                self.hs_color[0], self.hs_color[1], self._ha_brightness_raw
            )
            states.append(
                make_state(
                    SberFeature.LIGHT_COLOUR,
                    make_colour_value(current_color_sber[0], current_color_sber[1], current_color_sber[2]),
                )
            )
            states.append(make_state(SberFeature.LIGHT_MODE, make_enum_value("colour")))
        else:
            if self.current_sber_color_temp is not None:
                states.append(
                    make_state(SberFeature.LIGHT_COLOUR_TEMP, make_integer_value(self.current_sber_color_temp))
                )
            states.append(make_state(SberFeature.LIGHT_MODE, make_enum_value("white")))

    return {self.entity_id: {"states": states}}

process_cmd

process_cmd(cmd_data)

Process Sber light commands and produce HA service calls.

Handles the following Sber keys: - on_off: turn_on / turn_off - light_brightness: set brightness via turn_on - light_colour: set HSV color via turn_on - light_mode: switch between white/colour mode (local state only) - light_colour_temp: set color temperature via turn_on

Note: light_mode is tracked locally and triggers a state update to Sber without a HA service call (HA does not have a mode concept).

State is NOT mutated here for on/off — it will be updated when HA fires a state_changed event. However, light_mode is a Sber-only concept and must be tracked locally.

Parameters:

Name Type Description Default
cmd_data dict

Sber command dict with 'states' list.

required

Returns:

Type Description
list[dict]

List of HA service call dicts to execute.

Source code in custom_components/sber_mqtt_bridge/devices/light.py
def process_cmd(self, cmd_data: dict) -> list[dict]:
    """Process Sber light commands and produce HA service calls.

    Handles the following Sber keys:
    - ``on_off``: turn_on / turn_off
    - ``light_brightness``: set brightness via turn_on
    - ``light_colour``: set HSV color via turn_on
    - ``light_mode``: switch between white/colour mode (local state only)
    - ``light_colour_temp``: set color temperature via turn_on

    Note: ``light_mode`` is tracked locally and triggers a state update
    to Sber without a HA service call (HA does not have a mode concept).

    State is NOT mutated here for on/off — it will be updated when HA fires
    a ``state_changed`` event. However, ``light_mode`` is a Sber-only concept
    and must be tracked locally.

    Args:
        cmd_data: Sber command dict with 'states' list.

    Returns:
        List of HA service call dicts to execute.
    """
    if cmd_data is None:
        return []

    processing_result: list[dict] = []

    for data_item in cmd_data.get("states", []):
        cmd_key = data_item.get("key", "")
        cmd_value = data_item.get("value", {})

        if cmd_key == "on_off" and cmd_value.get("type", "") == "BOOL":
            new_state = cmd_value.get("bool_value", False)
            processing_result.append(
                {
                    "url": {
                        "type": "call_service",
                        "domain": "light",
                        "service": "turn_on" if new_state else "turn_off",
                        "target": {"entity_id": self.entity_id},
                    }
                }
            )

        elif cmd_key == "light_brightness":
            sber_br_value = self._safe_int(cmd_value.get("integer_value"))
            if sber_br_value is None:
                continue
            ha_br_value = self.brightness_converter.sber_to_ha(sber_br_value)
            brightness = max(0, min(int(ha_br_value), 255))
            processing_result.append(
                {
                    "url": {
                        "type": "call_service",
                        "domain": "light",
                        "service": "turn_on",
                        "service_data": {"brightness": brightness},
                        "target": {"entity_id": self.entity_id},
                    }
                }
            )

        elif cmd_key == "light_colour":
            hsv_color = cmd_value.get("colour_value")
            if hsv_color is not None:
                color = ColorConverter.sber_to_ha_hsv(
                    max(0, min(hsv_color.get("h", 0), 360)),
                    max(0, min(hsv_color.get("s", 0), 1000)),
                    max(0, min(hsv_color.get("v", 0), 1000)),
                )
            else:
                color = (0, 0, 0)

            # Ensure brightness >= 1 to avoid turning off the lamp
            brightness = max(color[2], 1)
            processing_result.append(
                {
                    "url": {
                        "type": "call_service",
                        "domain": "light",
                        "service": "turn_on",
                        "service_data": {
                            "hs_color": [color[0], color[1]],
                            "brightness": brightness,
                        },
                        "target": {"entity_id": self.entity_id},
                    }
                }
            )

        elif cmd_key == "light_mode":
            # light_mode is a Sber-only concept — HA doesn't have it.
            # To actually switch the lamp's mode, send current color or
            # color_temp to HA so it transitions into the requested mode.
            # NOTE: Do NOT mutate self.current_color_mode here — the actual
            # mode will be updated by fill_by_ha_state when HA confirms
            # the state change.  Premature mutation creates a window where
            # the debounced publish can send stale/wrong mode to Sber.
            mode_value = cmd_value.get("enum_value")
            if mode_value == "colour":
                # Force HA into color mode by sending current hs_color
                if isinstance(self.hs_color, (list, tuple)) and len(self.hs_color) >= 2:
                    processing_result.append(
                        {
                            "url": {
                                "type": "call_service",
                                "domain": "light",
                                "service": "turn_on",
                                "service_data": {
                                    "hs_color": [self.hs_color[0], self.hs_color[1]],
                                },
                                "target": {"entity_id": self.entity_id},
                            }
                        }
                    )
                else:
                    processing_result.append({"update_state": True})
            else:
                # Force HA into white mode by sending current color_temp
                if self.current_sber_color_temp is not None:
                    ha_mireds = self.color_temp_converter.sber_to_ha(self.current_sber_color_temp)
                    ha_kelvin = int(1_000_000 / max(ha_mireds, 1))
                    processing_result.append(
                        {
                            "url": {
                                "type": "call_service",
                                "domain": "light",
                                "service": "turn_on",
                                "service_data": {"color_temp_kelvin": ha_kelvin},
                                "target": {"entity_id": self.entity_id},
                            }
                        }
                    )
                else:
                    processing_result.append({"update_state": True})

        elif cmd_key == "light_colour_temp":
            sber_color_temp = self._safe_int(cmd_value.get("integer_value"))
            if sber_color_temp is None:
                continue
            ha_mireds = self.color_temp_converter.sber_to_ha(sber_color_temp)
            ha_kelvin = int(1_000_000 / max(ha_mireds, 1))
            processing_result.append(
                {
                    "url": {
                        "type": "call_service",
                        "domain": "light",
                        "service": "turn_on",
                        "service_data": {"color_temp_kelvin": ha_kelvin},
                        "target": {"entity_id": self.entity_id},
                    }
                }
            )

    _LOGGER.debug("(LightEntity.process_cmd) processing res: %s", processing_result)
    return processing_result

LedStripEntity

Светодиодная лента с поддержкой цвета и эффектов.

Sber LED Strip entity -- maps HA light entities to Sber led_strip category.

Identical to light in features and behavior, but uses the led_strip Sber category for LED strip devices.

LED_STRIP_CATEGORY module-attribute

LED_STRIP_CATEGORY = 'led_strip'

Sber device category for LED strip entities.

LedStripEntity

LedStripEntity(entity_data)

Bases: LightEntity

Sber LED strip entity.

Inherits all light behavior (on/off, brightness, color, color temperature) but registers under the Sber 'led_strip' category.

Initialize LED strip 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/led_strip.py
def __init__(self, entity_data: dict) -> None:
    """Initialize LED strip entity.

    Args:
        entity_data: HA entity registry dict containing entity metadata.
    """
    super().__init__(entity_data)
    self.category = LED_STRIP_CATEGORY