197 lines
6.8 KiB
Python
197 lines
6.8 KiB
Python
"""Services for ScreenLogic integration."""
|
|
|
|
import logging
|
|
|
|
from screenlogicpy import ScreenLogicError
|
|
from screenlogicpy.device_const.system import EQUIPMENT_FLAG
|
|
import voluptuous as vol
|
|
|
|
from homeassistant.config_entries import ConfigEntry, ConfigEntryState
|
|
from homeassistant.core import HomeAssistant, ServiceCall, callback
|
|
from homeassistant.exceptions import HomeAssistantError, ServiceValidationError
|
|
from homeassistant.helpers import (
|
|
config_validation as cv,
|
|
issue_registry as ir,
|
|
selector,
|
|
)
|
|
from homeassistant.helpers.service import async_extract_config_entry_ids
|
|
|
|
from .const import (
|
|
ATTR_COLOR_MODE,
|
|
ATTR_CONFIG_ENTRY,
|
|
ATTR_RUNTIME,
|
|
DOMAIN,
|
|
MAX_RUNTIME,
|
|
MIN_RUNTIME,
|
|
SERVICE_SET_COLOR_MODE,
|
|
SERVICE_START_SUPER_CHLORINATION,
|
|
SERVICE_STOP_SUPER_CHLORINATION,
|
|
SUPPORTED_COLOR_MODES,
|
|
)
|
|
from .coordinator import ScreenlogicDataUpdateCoordinator
|
|
|
|
_LOGGER = logging.getLogger(__name__)
|
|
|
|
BASE_SERVICE_SCHEMA = vol.Schema(
|
|
{
|
|
vol.Required(ATTR_CONFIG_ENTRY): selector.ConfigEntrySelector(
|
|
{
|
|
"integration": DOMAIN,
|
|
}
|
|
)
|
|
}
|
|
)
|
|
|
|
SET_COLOR_MODE_SCHEMA = vol.All(
|
|
vol.Schema(
|
|
{
|
|
vol.Optional(ATTR_CONFIG_ENTRY): selector.ConfigEntrySelector(
|
|
{
|
|
"integration": DOMAIN,
|
|
}
|
|
),
|
|
**cv.ENTITY_SERVICE_FIELDS,
|
|
vol.Required(ATTR_COLOR_MODE): vol.In(SUPPORTED_COLOR_MODES),
|
|
}
|
|
),
|
|
cv.has_at_least_one_key(ATTR_CONFIG_ENTRY, *cv.ENTITY_SERVICE_FIELDS),
|
|
)
|
|
|
|
TURN_ON_SUPER_CHLOR_SCHEMA = BASE_SERVICE_SCHEMA.extend(
|
|
{
|
|
vol.Optional(ATTR_RUNTIME, default=24): vol.All(
|
|
vol.Coerce(int), vol.Clamp(min=MIN_RUNTIME, max=MAX_RUNTIME)
|
|
),
|
|
}
|
|
)
|
|
|
|
|
|
@callback
|
|
def async_load_screenlogic_services(hass: HomeAssistant):
|
|
"""Set up services for the ScreenLogic integration."""
|
|
|
|
async def extract_screenlogic_config_entry_ids(service_call: ServiceCall):
|
|
if not (
|
|
screenlogic_entry_ids := await async_extract_config_entry_ids(
|
|
hass, service_call
|
|
)
|
|
):
|
|
raise ServiceValidationError(
|
|
f"Failed to call service '{service_call.service}'. Config entry for "
|
|
"target not found"
|
|
)
|
|
return screenlogic_entry_ids
|
|
|
|
async def get_coordinators(
|
|
service_call: ServiceCall,
|
|
) -> list[ScreenlogicDataUpdateCoordinator]:
|
|
entry_ids: set[str]
|
|
if entry_id := service_call.data.get(ATTR_CONFIG_ENTRY):
|
|
entry_ids = {entry_id}
|
|
else:
|
|
ir.async_create_issue(
|
|
hass,
|
|
DOMAIN,
|
|
"service_target_deprecation",
|
|
breaks_in_ha_version="2024.8.0",
|
|
is_fixable=True,
|
|
is_persistent=True,
|
|
severity=ir.IssueSeverity.WARNING,
|
|
translation_key="service_target_deprecation",
|
|
)
|
|
entry_ids = await extract_screenlogic_config_entry_ids(service_call)
|
|
|
|
coordinators: list[ScreenlogicDataUpdateCoordinator] = []
|
|
for entry_id in entry_ids:
|
|
config_entry: ConfigEntry | None = hass.config_entries.async_get_entry(
|
|
entry_id
|
|
)
|
|
if not config_entry:
|
|
raise ServiceValidationError(
|
|
f"Failed to call service '{service_call.service}'. Config entry "
|
|
f"'{entry_id}' not found"
|
|
)
|
|
if not config_entry.domain == DOMAIN:
|
|
raise ServiceValidationError(
|
|
f"Failed to call service '{service_call.service}'. Config entry "
|
|
f"'{entry_id}' is not a {DOMAIN} config"
|
|
)
|
|
if not config_entry.state == ConfigEntryState.LOADED:
|
|
raise ServiceValidationError(
|
|
f"Failed to call service '{service_call.service}'. Config entry "
|
|
f"'{entry_id}' not loaded"
|
|
)
|
|
coordinators.append(hass.data[DOMAIN][entry_id])
|
|
|
|
return coordinators
|
|
|
|
async def async_set_color_mode(service_call: ServiceCall) -> None:
|
|
color_num = SUPPORTED_COLOR_MODES[service_call.data[ATTR_COLOR_MODE]]
|
|
coordinator: ScreenlogicDataUpdateCoordinator
|
|
for coordinator in await get_coordinators(service_call):
|
|
_LOGGER.debug(
|
|
"Service %s called on %s with mode %s",
|
|
SERVICE_SET_COLOR_MODE,
|
|
coordinator.gateway.name,
|
|
color_num,
|
|
)
|
|
try:
|
|
await coordinator.gateway.async_set_color_lights(color_num)
|
|
# Debounced refresh to catch any secondary changes in the device
|
|
await coordinator.async_request_refresh()
|
|
except ScreenLogicError as error:
|
|
raise HomeAssistantError(error) from error
|
|
|
|
async def async_set_super_chlor(
|
|
service_call: ServiceCall,
|
|
is_on: bool,
|
|
runtime: int | None = None,
|
|
) -> None:
|
|
coordinator: ScreenlogicDataUpdateCoordinator
|
|
for coordinator in await get_coordinators(service_call):
|
|
if EQUIPMENT_FLAG.CHLORINATOR not in coordinator.gateway.equipment_flags:
|
|
raise ServiceValidationError(
|
|
f"Equipment configuration for {coordinator.gateway.name} does not"
|
|
f" support {service_call.service}"
|
|
)
|
|
rt_log = f" with runtime {runtime}" if runtime else ""
|
|
_LOGGER.debug(
|
|
"Service %s called on %s%s",
|
|
service_call.service,
|
|
coordinator.gateway.name,
|
|
rt_log,
|
|
)
|
|
try:
|
|
await coordinator.gateway.async_set_scg_config(
|
|
super_chlor_timer=runtime, super_chlorinate=is_on
|
|
)
|
|
# Debounced refresh to catch any secondary changes in the device
|
|
await coordinator.async_request_refresh()
|
|
except ScreenLogicError as error:
|
|
raise HomeAssistantError(error) from error
|
|
|
|
async def async_start_super_chlor(service_call: ServiceCall) -> None:
|
|
runtime = service_call.data[ATTR_RUNTIME]
|
|
await async_set_super_chlor(service_call, True, runtime)
|
|
|
|
async def async_stop_super_chlor(service_call: ServiceCall) -> None:
|
|
await async_set_super_chlor(service_call, False)
|
|
|
|
hass.services.async_register(
|
|
DOMAIN, SERVICE_SET_COLOR_MODE, async_set_color_mode, SET_COLOR_MODE_SCHEMA
|
|
)
|
|
|
|
hass.services.async_register(
|
|
DOMAIN,
|
|
SERVICE_START_SUPER_CHLORINATION,
|
|
async_start_super_chlor,
|
|
TURN_ON_SUPER_CHLOR_SCHEMA,
|
|
)
|
|
|
|
hass.services.async_register(
|
|
DOMAIN,
|
|
SERVICE_STOP_SUPER_CHLORINATION,
|
|
async_stop_super_chlor,
|
|
BASE_SERVICE_SCHEMA,
|
|
)
|