ha-core/homeassistant/components/broadlink/switch.py

283 lines
8.7 KiB
Python

"""Support for Broadlink switches."""
from abc import ABC, abstractmethod
from functools import partial
import logging
from broadlink.exceptions import BroadlinkException
import voluptuous as vol
from homeassistant.components.switch import (
DEVICE_CLASS_OUTLET,
DEVICE_CLASS_SWITCH,
PLATFORM_SCHEMA,
SwitchEntity,
)
from homeassistant.const import (
CONF_COMMAND_OFF,
CONF_COMMAND_ON,
CONF_FRIENDLY_NAME,
CONF_HOST,
CONF_MAC,
CONF_NAME,
CONF_SWITCHES,
CONF_TIMEOUT,
CONF_TYPE,
STATE_ON,
)
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.restore_state import RestoreEntity
from .const import DOMAIN, SWITCH_DOMAIN
from .entity import BroadlinkEntity
from .helpers import data_packet, import_device, mac_address
_LOGGER = logging.getLogger(__name__)
CONF_SLOTS = "slots"
SWITCH_SCHEMA = vol.Schema(
{
vol.Required(CONF_NAME): cv.string,
vol.Optional(CONF_COMMAND_OFF): data_packet,
vol.Optional(CONF_COMMAND_ON): data_packet,
}
)
OLD_SWITCH_SCHEMA = vol.Schema(
{
vol.Optional(CONF_COMMAND_OFF): data_packet,
vol.Optional(CONF_COMMAND_ON): data_packet,
vol.Optional(CONF_FRIENDLY_NAME): cv.string,
}
)
PLATFORM_SCHEMA = vol.All(
cv.deprecated(CONF_HOST),
cv.deprecated(CONF_SLOTS),
cv.deprecated(CONF_TIMEOUT),
cv.deprecated(CONF_TYPE),
PLATFORM_SCHEMA.extend(
{
vol.Required(CONF_MAC): mac_address,
vol.Optional(CONF_HOST): cv.string,
vol.Optional(CONF_SWITCHES, default=[]): vol.Any(
cv.schema_with_slug_keys(OLD_SWITCH_SCHEMA),
vol.All(cv.ensure_list, [SWITCH_SCHEMA]),
),
}
),
)
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
"""Import the device and set up custom switches.
This is for backward compatibility.
Do not use this method.
"""
mac_addr = config[CONF_MAC]
host = config.get(CONF_HOST)
switches = config.get(CONF_SWITCHES)
if not isinstance(switches, list):
switches = [
{CONF_NAME: switch.pop(CONF_FRIENDLY_NAME, name), **switch}
for name, switch in switches.items()
]
_LOGGER.warning(
"Your configuration for the switch platform is deprecated. "
"Please refer to the Broadlink documentation to catch up"
)
if switches:
platform_data = hass.data[DOMAIN].platforms.setdefault(SWITCH_DOMAIN, {})
platform_data.setdefault(mac_addr, []).extend(switches)
else:
_LOGGER.warning(
"The switch platform is deprecated, except for custom IR/RF "
"switches. Please refer to the Broadlink documentation to "
"catch up"
)
if host:
import_device(hass, host)
async def async_setup_entry(hass, config_entry, async_add_entities):
"""Set up the Broadlink switch."""
device = hass.data[DOMAIN].devices[config_entry.entry_id]
if device.api.type in {"RM4MINI", "RM4PRO", "RMMINI", "RMMINIB", "RMPRO"}:
platform_data = hass.data[DOMAIN].platforms.get(SWITCH_DOMAIN, {})
user_defined_switches = platform_data.get(device.api.mac, {})
switches = [
BroadlinkRMSwitch(device, config) for config in user_defined_switches
]
elif device.api.type == "SP1":
switches = [BroadlinkSP1Switch(device)]
elif device.api.type in {"SP2", "SP2S", "SP3", "SP3S", "SP4", "SP4B"}:
switches = [BroadlinkSP2Switch(device)]
elif device.api.type == "BG1":
switches = [BroadlinkBG1Slot(device, slot) for slot in range(1, 3)]
elif device.api.type == "MP1":
switches = [BroadlinkMP1Slot(device, slot) for slot in range(1, 5)]
async_add_entities(switches)
class BroadlinkSwitch(BroadlinkEntity, SwitchEntity, RestoreEntity, ABC):
"""Representation of a Broadlink switch."""
_attr_assumed_state = True
_attr_device_class = DEVICE_CLASS_SWITCH
def __init__(self, device, command_on, command_off):
"""Initialize the switch."""
super().__init__(device)
self._command_on = command_on
self._command_off = command_off
self._attr_name = f"{device.name} Switch"
async def async_added_to_hass(self):
"""Call when the switch is added to hass."""
state = await self.async_get_last_state()
self._attr_is_on = state is not None and state.state == STATE_ON
await super().async_added_to_hass()
async def async_turn_on(self, **kwargs):
"""Turn on the switch."""
if await self._async_send_packet(self._command_on):
self._attr_is_on = True
self.async_write_ha_state()
async def async_turn_off(self, **kwargs):
"""Turn off the switch."""
if await self._async_send_packet(self._command_off):
self._attr_is_on = False
self.async_write_ha_state()
@abstractmethod
async def _async_send_packet(self, packet):
"""Send a packet to the device."""
class BroadlinkRMSwitch(BroadlinkSwitch):
"""Representation of a Broadlink RM switch."""
def __init__(self, device, config):
"""Initialize the switch."""
super().__init__(
device, config.get(CONF_COMMAND_ON), config.get(CONF_COMMAND_OFF)
)
self._attr_name = config[CONF_NAME]
async def _async_send_packet(self, packet):
"""Send a packet to the device."""
if packet is None:
return True
try:
await self._device.async_request(self._device.api.send_data, packet)
except (BroadlinkException, OSError) as err:
_LOGGER.error("Failed to send packet: %s", err)
return False
return True
class BroadlinkSP1Switch(BroadlinkSwitch):
"""Representation of a Broadlink SP1 switch."""
def __init__(self, device):
"""Initialize the switch."""
super().__init__(device, 1, 0)
self._attr_unique_id = self._device.unique_id
async def _async_send_packet(self, packet):
"""Send a packet to the device."""
try:
await self._device.async_request(self._device.api.set_power, packet)
except (BroadlinkException, OSError) as err:
_LOGGER.error("Failed to send packet: %s", err)
return False
return True
class BroadlinkSP2Switch(BroadlinkSP1Switch):
"""Representation of a Broadlink SP2 switch."""
_attr_assumed_state = False
def __init__(self, device, *args, **kwargs):
"""Initialize the switch."""
super().__init__(device, *args, **kwargs)
self._attr_is_on = self._coordinator.data["pwr"]
def _update_state(self, data):
"""Update the state of the entity."""
self._attr_is_on = data["pwr"]
class BroadlinkMP1Slot(BroadlinkSwitch):
"""Representation of a Broadlink MP1 slot."""
_attr_assumed_state = False
def __init__(self, device, slot):
"""Initialize the switch."""
super().__init__(device, 1, 0)
self._slot = slot
self._attr_is_on = self._coordinator.data[f"s{slot}"]
self._attr_name = f"{device.name} S{slot}"
self._attr_unique_id = f"{device.unique_id}-s{slot}"
def _update_state(self, data):
"""Update the state of the entity."""
self._attr_is_on = data[f"s{self._slot}"]
async def _async_send_packet(self, packet):
"""Send a packet to the device."""
try:
await self._device.async_request(
self._device.api.set_power, self._slot, packet
)
except (BroadlinkException, OSError) as err:
_LOGGER.error("Failed to send packet: %s", err)
return False
return True
class BroadlinkBG1Slot(BroadlinkSwitch):
"""Representation of a Broadlink BG1 slot."""
_attr_assumed_state = False
def __init__(self, device, slot):
"""Initialize the switch."""
super().__init__(device, 1, 0)
self._slot = slot
self._attr_is_on = self._coordinator.data[f"pwr{slot}"]
self._attr_name = f"{device.name} S{slot}"
self._attr_device_class = DEVICE_CLASS_OUTLET
self._attr_unique_id = f"{device.unique_id}-s{slot}"
def _update_state(self, data):
"""Update the state of the entity."""
self._attr_is_on = data[f"pwr{self._slot}"]
async def _async_send_packet(self, packet):
"""Send a packet to the device."""
set_state = partial(self._device.api.set_state, **{f"pwr{self._slot}": packet})
try:
await self._device.async_request(set_state)
except (BroadlinkException, OSError) as err:
_LOGGER.error("Failed to send packet: %s", err)
return False
return True