Repository: home-assistant/core
File: homeassistant/components/matter/switch.py (Python)

Code before:
    hass: HomeAssistant,
    config_entry: ConfigEntry,
    async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
    """Set up Matter switches from Config Entry."""
    matter = get_matter(hass)
    matter.register_platform_handler(Platform.SWITCH, async_add_entities)


@dataclass(frozen=True, kw_only=True)
class MatterSwitchEntityDescription(SwitchEntityDescription, MatterEntityDescription):
    """Describe Matter Switch entities."""


class MatterSwitch(MatterEntity, SwitchEntity):
    """Representation of a Matter switch."""

    _platform_translation_key = "switch"

    def _get_command_for_value(self, value: bool) -> ClusterCommand:
        """Get the appropriate command for the desired value.

        Applies ha_to_device conversion if needed (e.g., for inverted logic like mute).
        """
        send_value = value
        if value_convert := self.entity_description.ha_to_device:  # type: ignore[attr-defined]
            send_value = value_convert(value)
        return (
            clusters.OnOff.Commands.On()
            if send_value
            else clusters.OnOff.Commands.Off()
        )

    async def async_turn_on(self, **kwargs: Any) -> None:
        """Turn switch on."""
        await self.send_device_command(self._get_command_for_value(True))

    async def async_turn_off(self, **kwargs: Any) -> None:
        """Turn switch off."""
        await self.send_device_command(self._get_command_for_value(False))

    @callback
    def _update_from_device(self) -> None:
        """Update from device."""
        value = self.get_matter_attribute_value(self._entity_info.primary_attribute)
        if value_convert := self.entity_description.device_to_ha:  # type: ignore[attr-defined]
            value = value_convert(value)
        self._attr_is_on = value


class MatterGenericCommandSwitch(MatterSwitch):

Review comment (suggestion):
We shouldn't ignore the mypy `attr-defined` errors. Instead, we should type the `entity_description` for this class, so add `entity_description: MatterSwitchEntityDescription`, similar to how it's done in `MatterGenericCommandSwitch` and `MatterNumericSwitch`. That should properly fix the error, as `MatterEntityDescription` contains `ha_to_device`.

Even that is a bit weird because `MatterGenericCommandSwitch` and `MatterNumericSwitch` inherit from `MatterSwitch` but their entity descriptions do not inherit from `MatterSwitchEntityDescription`. It should still be acceptable though.

Code after:
    hass: HomeAssistant,
    config_entry: ConfigEntry,
    async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
    """Set up Matter switches from Config Entry."""
    matter = get_matter(hass)
    matter.register_platform_handler(Platform.SWITCH, async_add_entities)


@dataclass(frozen=True, kw_only=True)
class MatterSwitchEntityDescription(SwitchEntityDescription, MatterEntityDescription):
    """Describe Matter Switch entities."""

    inverted: bool = False


class MatterSwitch(MatterEntity, SwitchEntity):
    """Representation of a Matter switch."""

    entity_description: MatterSwitchEntityDescription
    _platform_translation_key = "switch"

    def _get_command_for_value(self, value: bool) -> ClusterCommand:
        """Get the appropriate command for the desired value.

        Applies inversion if needed (e.g., for inverted logic like mute).
        """
        send_value = not value if self.entity_description.inverted else value
        return (
            clusters.OnOff.Commands.On()
            if send_value
            else clusters.OnOff.Commands.Off()
        )

    async def async_turn_on(self, **kwargs: Any) -> None:
        """Turn switch on."""
        await self.send_device_command(self._get_command_for_value(True))

    async def async_turn_off(self, **kwargs: Any) -> None:
        """Turn switch off."""
        await self.send_device_command(self._get_command_for_value(False))

    @callback
    def _update_from_device(self) -> None:
        """Update from device."""
        value = self.get_matter_attribute_value(self._entity_info.primary_attribute)
        if self.entity_description.inverted:
            value = not value
        self._attr_is_on = value