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

Code before:
async def async_setup_entry(
    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"

    async def async_turn_on(self, **kwargs: Any) -> None:
        """Turn switch on."""
        # Start with True (HA wants to turn on)
        # Apply ha_to_device conversion if needed (e.g., for inverted logic like mute)
        value = True
        if value_convert := getattr(self.entity_description, "ha_to_device", None):
            value = value_convert(value)
        command = (
            clusters.OnOff.Commands.On() if value else clusters.OnOff.Commands.Off()
        )
        await self.send_device_command(command)

    async def async_turn_off(self, **kwargs: Any) -> None:
        """Turn switch off."""
        # Start with False (HA wants to turn off)
        # Apply ha_to_device conversion if needed (e.g., for inverted logic like mute)
        value = False
        if value_convert := getattr(self.entity_description, "ha_to_device", None):
            value = value_convert(value)
        command = (
            clusters.OnOff.Commands.On() if value else clusters.OnOff.Commands.Off()
        )
        await self.send_device_command(command)

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

Review comment (refactor):
Why do we need to use `getattr` with `default=None` for `ha_to_device` and `device_to_ha` below?
These should be `None` by default, set in the base [`MatterEntityDescription`](https://github.com/home-assistant/core/blob/27117c9d17380712faa861a6c0e6f3e9a0d4caca/homeassistant/components/matter/entity.py#L77-L78).

So, we should be able to use something like this directly, similar to `MatterNumericSwitch`:
```python
# `async_turn_on` and `async_turn_off` (*)
if value_convert := self.entity_description.ha_to_device:
    send_value = value_convert(value)

# _update_from_device
if value_convert := self.entity_description.device_to_ha:
    value = value_convert(value)
```
(*): It might even make sense to introduce a third method that actually sends the correct command, now that both could call `Commands.On()` and `Commands.Off()`. So, similar to `_async_set_native_value` in `MatterNumericSwitch`, just for commands.

Right now, the logic to determine which command to send is duplicated in both `async_turn_on` and `async_turn_off`.

Code after:
async def async_setup_entry(
    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