1
mirror of https://github.com/home-assistant/core synced 2024-08-31 05:57:13 +02:00
ha-core/homeassistant/components/homekit/type_switches.py
2021-03-08 14:19:05 -08:00

226 lines
7.9 KiB
Python

"""Class to hold all switch accessories."""
import logging
from pyhap.const import (
CATEGORY_FAUCET,
CATEGORY_OUTLET,
CATEGORY_SHOWER_HEAD,
CATEGORY_SPRINKLER,
CATEGORY_SWITCH,
)
from homeassistant.components.switch import DOMAIN
from homeassistant.components.vacuum import (
DOMAIN as VACUUM_DOMAIN,
SERVICE_RETURN_TO_BASE,
SERVICE_START,
STATE_CLEANING,
SUPPORT_RETURN_HOME,
SUPPORT_START,
)
from homeassistant.const import (
ATTR_ENTITY_ID,
ATTR_SUPPORTED_FEATURES,
CONF_TYPE,
SERVICE_TURN_OFF,
SERVICE_TURN_ON,
STATE_ON,
)
from homeassistant.core import callback, split_entity_id
from homeassistant.helpers.event import async_call_later
from .accessories import TYPES, HomeAccessory
from .const import (
CHAR_ACTIVE,
CHAR_IN_USE,
CHAR_ON,
CHAR_OUTLET_IN_USE,
CHAR_VALVE_TYPE,
SERV_OUTLET,
SERV_SWITCH,
SERV_VALVE,
TYPE_FAUCET,
TYPE_SHOWER,
TYPE_SPRINKLER,
TYPE_VALVE,
)
_LOGGER = logging.getLogger(__name__)
VALVE_TYPE = {
TYPE_FAUCET: (CATEGORY_FAUCET, 3),
TYPE_SHOWER: (CATEGORY_SHOWER_HEAD, 2),
TYPE_SPRINKLER: (CATEGORY_SPRINKLER, 1),
TYPE_VALVE: (CATEGORY_FAUCET, 0),
}
@TYPES.register("Outlet")
class Outlet(HomeAccessory):
"""Generate an Outlet accessory."""
def __init__(self, *args):
"""Initialize an Outlet accessory object."""
super().__init__(*args, category=CATEGORY_OUTLET)
state = self.hass.states.get(self.entity_id)
serv_outlet = self.add_preload_service(SERV_OUTLET)
self.char_on = serv_outlet.configure_char(
CHAR_ON, value=False, setter_callback=self.set_state
)
self.char_outlet_in_use = serv_outlet.configure_char(
CHAR_OUTLET_IN_USE, value=True
)
# Set the state so it is in sync on initial
# GET to avoid an event storm after homekit startup
self.async_update_state(state)
def set_state(self, value):
"""Move switch state to value if call came from HomeKit."""
_LOGGER.debug("%s: Set switch state to %s", self.entity_id, value)
params = {ATTR_ENTITY_ID: self.entity_id}
service = SERVICE_TURN_ON if value else SERVICE_TURN_OFF
self.async_call_service(DOMAIN, service, params)
@callback
def async_update_state(self, new_state):
"""Update switch state after state changed."""
current_state = new_state.state == STATE_ON
if self.char_on.value is not current_state:
_LOGGER.debug("%s: Set current state to %s", self.entity_id, current_state)
self.char_on.set_value(current_state)
@TYPES.register("Switch")
class Switch(HomeAccessory):
"""Generate a Switch accessory."""
def __init__(self, *args):
"""Initialize a Switch accessory object."""
super().__init__(*args, category=CATEGORY_SWITCH)
self._domain = split_entity_id(self.entity_id)[0]
state = self.hass.states.get(self.entity_id)
self.activate_only = self.is_activate(self.hass.states.get(self.entity_id))
serv_switch = self.add_preload_service(SERV_SWITCH)
self.char_on = serv_switch.configure_char(
CHAR_ON, value=False, setter_callback=self.set_state
)
# Set the state so it is in sync on initial
# GET to avoid an event storm after homekit startup
self.async_update_state(state)
def is_activate(self, state):
"""Check if entity is activate only."""
if self._domain == "scene":
return True
return False
def reset_switch(self, *args):
"""Reset switch to emulate activate click."""
_LOGGER.debug("%s: Reset switch to off", self.entity_id)
if self.char_on.value is not False:
self.char_on.set_value(False)
def set_state(self, value):
"""Move switch state to value if call came from HomeKit."""
_LOGGER.debug("%s: Set switch state to %s", self.entity_id, value)
if self.activate_only and not value:
_LOGGER.debug("%s: Ignoring turn_off call", self.entity_id)
return
params = {ATTR_ENTITY_ID: self.entity_id}
service = SERVICE_TURN_ON if value else SERVICE_TURN_OFF
self.async_call_service(self._domain, service, params)
if self.activate_only:
async_call_later(self.hass, 1, self.reset_switch)
@callback
def async_update_state(self, new_state):
"""Update switch state after state changed."""
self.activate_only = self.is_activate(new_state)
if self.activate_only:
_LOGGER.debug(
"%s: Ignore state change, entity is activate only", self.entity_id
)
return
current_state = new_state.state == STATE_ON
if self.char_on.value is not current_state:
_LOGGER.debug("%s: Set current state to %s", self.entity_id, current_state)
self.char_on.set_value(current_state)
@TYPES.register("Vacuum")
class Vacuum(Switch):
"""Generate a Switch accessory."""
def set_state(self, value):
"""Move switch state to value if call came from HomeKit."""
_LOGGER.debug("%s: Set switch state to %s", self.entity_id, value)
state = self.hass.states.get(self.entity_id)
features = state.attributes.get(ATTR_SUPPORTED_FEATURES, 0)
if value:
sup_start = features & SUPPORT_START
service = SERVICE_START if sup_start else SERVICE_TURN_ON
else:
sup_return_home = features & SUPPORT_RETURN_HOME
service = SERVICE_RETURN_TO_BASE if sup_return_home else SERVICE_TURN_OFF
self.async_call_service(
VACUUM_DOMAIN, service, {ATTR_ENTITY_ID: self.entity_id}
)
@callback
def async_update_state(self, new_state):
"""Update switch state after state changed."""
current_state = new_state.state in (STATE_CLEANING, STATE_ON)
if self.char_on.value is not current_state:
_LOGGER.debug("%s: Set current state to %s", self.entity_id, current_state)
self.char_on.set_value(current_state)
@TYPES.register("Valve")
class Valve(HomeAccessory):
"""Generate a Valve accessory."""
def __init__(self, *args):
"""Initialize a Valve accessory object."""
super().__init__(*args)
state = self.hass.states.get(self.entity_id)
valve_type = self.config[CONF_TYPE]
self.category = VALVE_TYPE[valve_type][0]
serv_valve = self.add_preload_service(SERV_VALVE)
self.char_active = serv_valve.configure_char(
CHAR_ACTIVE, value=False, setter_callback=self.set_state
)
self.char_in_use = serv_valve.configure_char(CHAR_IN_USE, value=False)
self.char_valve_type = serv_valve.configure_char(
CHAR_VALVE_TYPE, value=VALVE_TYPE[valve_type][1]
)
# Set the state so it is in sync on initial
# GET to avoid an event storm after homekit startup
self.async_update_state(state)
def set_state(self, value):
"""Move value state to value if call came from HomeKit."""
_LOGGER.debug("%s: Set switch state to %s", self.entity_id, value)
self.char_in_use.set_value(value)
params = {ATTR_ENTITY_ID: self.entity_id}
service = SERVICE_TURN_ON if value else SERVICE_TURN_OFF
self.async_call_service(DOMAIN, service, params)
@callback
def async_update_state(self, new_state):
"""Update switch state after state changed."""
current_state = 1 if new_state.state == STATE_ON else 0
if self.char_active.value != current_state:
_LOGGER.debug("%s: Set active state to %s", self.entity_id, current_state)
self.char_active.set_value(current_state)
if self.char_in_use.value != current_state:
_LOGGER.debug("%s: Set in_use state to %s", self.entity_id, current_state)
self.char_in_use.set_value(current_state)