From 02f7165ca50a3d10810ab9b1b443ffc3890d38fb Mon Sep 17 00:00:00 2001 From: Kevin Worrel <37058192+dieselrabbit@users.noreply.github.com> Date: Wed, 24 Jan 2024 14:28:27 -0800 Subject: [PATCH] Add super chlorination services to screenlogic (#108048) Co-authored-by: J. Nick Koston --- .../components/screenlogic/__init__.py | 4 +- homeassistant/components/screenlogic/const.py | 7 + .../components/screenlogic/services.py | 124 +++++++++++++----- .../components/screenlogic/services.yaml | 17 +++ .../components/screenlogic/strings.json | 14 ++ 5 files changed, 133 insertions(+), 33 deletions(-) diff --git a/homeassistant/components/screenlogic/__init__.py b/homeassistant/components/screenlogic/__init__.py index 7276ec28323..6d066f86072 100644 --- a/homeassistant/components/screenlogic/__init__.py +++ b/homeassistant/components/screenlogic/__init__.py @@ -56,14 +56,14 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: hass, config_entry=entry, gateway=gateway ) - async_load_screenlogic_services(hass) - await coordinator.async_config_entry_first_refresh() entry.async_on_unload(entry.add_update_listener(async_update_listener)) hass.data.setdefault(DOMAIN, {})[entry.entry_id] = coordinator + async_load_screenlogic_services(hass, entry) + await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) return True diff --git a/homeassistant/components/screenlogic/const.py b/homeassistant/components/screenlogic/const.py index 8181e0f612a..3125f52989e 100644 --- a/homeassistant/components/screenlogic/const.py +++ b/homeassistant/components/screenlogic/const.py @@ -24,6 +24,13 @@ SERVICE_SET_COLOR_MODE = "set_color_mode" ATTR_COLOR_MODE = "color_mode" SUPPORTED_COLOR_MODES = {slugify(cm.name): cm.value for cm in COLOR_MODE} +SERVICE_START_SUPER_CHLORINATION = "start_super_chlorination" +ATTR_RUNTIME = "runtime" +MAX_RUNTIME = 72 +MIN_RUNTIME = 0 + +SERVICE_STOP_SUPER_CHLORINATION = "stop_super_chlorination" + LIGHT_CIRCUIT_FUNCTIONS = { FUNCTION.COLOR_WHEEL, FUNCTION.DIMMER, diff --git a/homeassistant/components/screenlogic/services.py b/homeassistant/components/screenlogic/services.py index c9c66183daf..2c8e786491c 100644 --- a/homeassistant/components/screenlogic/services.py +++ b/homeassistant/components/screenlogic/services.py @@ -3,8 +3,10 @@ import logging from screenlogicpy import ScreenLogicError +from screenlogicpy.device_const.system import EQUIPMENT_FLAG import voluptuous as vol +from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant, ServiceCall, callback from homeassistant.exceptions import HomeAssistantError import homeassistant.helpers.config_validation as cv @@ -12,10 +14,16 @@ from homeassistant.helpers.service import async_extract_config_entry_ids from .const import ( ATTR_COLOR_MODE, + 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__) @@ -25,35 +33,38 @@ SET_COLOR_MODE_SCHEMA = cv.make_entity_service_schema( }, ) +TURN_ON_SUPER_CHLOR_SCHEMA = cv.make_entity_service_schema( + { + vol.Optional(ATTR_RUNTIME, default=24): vol.Clamp( + min=MIN_RUNTIME, max=MAX_RUNTIME + ), + } +) + @callback -def async_load_screenlogic_services(hass: HomeAssistant): +def async_load_screenlogic_services(hass: HomeAssistant, entry: ConfigEntry): """Set up services for the ScreenLogic integration.""" - if hass.services.has_service(DOMAIN, SERVICE_SET_COLOR_MODE): - # Integration-level services have already been added. Return. - return async def extract_screenlogic_config_entry_ids(service_call: ServiceCall): - return [ - entry_id - for entry_id in await async_extract_config_entry_ids(hass, service_call) - if (entry := hass.config_entries.async_get_entry(entry_id)) - and entry.domain == DOMAIN - ] - - async def async_set_color_mode(service_call: ServiceCall) -> None: if not ( - screenlogic_entry_ids := await extract_screenlogic_config_entry_ids( - service_call - ) + screenlogic_entry_ids := [ + entry_id + for entry_id in await async_extract_config_entry_ids(hass, service_call) + if (entry := hass.config_entries.async_get_entry(entry_id)) + and entry.domain == DOMAIN + ] ): raise HomeAssistantError( - f"Failed to call service '{SERVICE_SET_COLOR_MODE}'. Config entry for" + f"Failed to call service '{service_call.service}'. Config entry for" " target not found" ) + return screenlogic_entry_ids + + async def async_set_color_mode(service_call: ServiceCall) -> None: color_num = SUPPORTED_COLOR_MODES[service_call.data[ATTR_COLOR_MODE]] - for entry_id in screenlogic_entry_ids: - coordinator = hass.data[DOMAIN][entry_id] + for entry_id in await extract_screenlogic_config_entry_ids(service_call): + coordinator: ScreenlogicDataUpdateCoordinator = hass.data[DOMAIN][entry_id] _LOGGER.debug( "Service %s called on %s with mode %s", SERVICE_SET_COLOR_MODE, @@ -62,26 +73,77 @@ def async_load_screenlogic_services(hass: HomeAssistant): ) try: await coordinator.gateway.async_set_color_lights(color_num) - # Debounced refresh to catch any secondary - # changes in the device + # Debounced refresh to catch any secondary changes in the device await coordinator.async_request_refresh() except ScreenLogicError as error: raise HomeAssistantError(error) from error - hass.services.async_register( - DOMAIN, SERVICE_SET_COLOR_MODE, async_set_color_mode, SET_COLOR_MODE_SCHEMA - ) + async def async_set_super_chlor( + service_call: ServiceCall, + is_on: bool, + runtime: int | None = None, + ) -> None: + for entry_id in await extract_screenlogic_config_entry_ids(service_call): + coordinator: ScreenlogicDataUpdateCoordinator = hass.data[DOMAIN][entry_id] + _LOGGER.debug( + "Service %s called on %s with runtime %s", + SERVICE_START_SUPER_CHLORINATION, + coordinator.gateway.name, + runtime, + ) + 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) + + if not hass.services.has_service(DOMAIN, SERVICE_SET_COLOR_MODE): + hass.services.async_register( + DOMAIN, SERVICE_SET_COLOR_MODE, async_set_color_mode, SET_COLOR_MODE_SCHEMA + ) + + coordinator: ScreenlogicDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id] + equipment_flags = coordinator.gateway.equipment_flags + + if EQUIPMENT_FLAG.CHLORINATOR in equipment_flags: + if not hass.services.has_service(DOMAIN, SERVICE_START_SUPER_CHLORINATION): + hass.services.async_register( + DOMAIN, + SERVICE_START_SUPER_CHLORINATION, + async_start_super_chlor, + TURN_ON_SUPER_CHLOR_SCHEMA, + ) + + if not hass.services.has_service(DOMAIN, SERVICE_STOP_SUPER_CHLORINATION): + hass.services.async_register( + DOMAIN, + SERVICE_STOP_SUPER_CHLORINATION, + async_stop_super_chlor, + ) @callback def async_unload_screenlogic_services(hass: HomeAssistant): """Unload services for the ScreenLogic integration.""" - if hass.data[DOMAIN]: - # There is still another config entry for this domain, don't remove services. - return - if not hass.services.has_service(DOMAIN, SERVICE_SET_COLOR_MODE): - return - - _LOGGER.info("Unloading ScreenLogic Services") - hass.services.async_remove(domain=DOMAIN, service=SERVICE_SET_COLOR_MODE) + if not hass.data[DOMAIN]: + _LOGGER.debug("Unloading all ScreenLogic services") + for service in hass.services.async_services_for_domain(DOMAIN): + hass.services.async_remove(DOMAIN, service) + elif not any( + EQUIPMENT_FLAG.CHLORINATOR in coordinator.gateway.equipment_flags + for coordinator in hass.data[DOMAIN].values() + ): + _LOGGER.debug("Unloading ScreenLogic chlorination services") + hass.services.async_remove(DOMAIN, SERVICE_START_SUPER_CHLORINATION) + hass.services.async_remove(DOMAIN, SERVICE_STOP_SUPER_CHLORINATION) diff --git a/homeassistant/components/screenlogic/services.yaml b/homeassistant/components/screenlogic/services.yaml index 8e4a82a1079..7b51d1a21db 100644 --- a/homeassistant/components/screenlogic/services.yaml +++ b/homeassistant/components/screenlogic/services.yaml @@ -31,3 +31,20 @@ set_color_mode: - sunset - thumper - white +start_super_chlorination: + target: + device: + integration: screenlogic + fields: + runtime: + default: 24 + selector: + number: + min: 0 + max: 72 + unit_of_measurement: hours + mode: slider +stop_super_chlorination: + target: + device: + integration: screenlogic diff --git a/homeassistant/components/screenlogic/strings.json b/homeassistant/components/screenlogic/strings.json index 4894bc6437d..fcddbc1d415 100644 --- a/homeassistant/components/screenlogic/strings.json +++ b/homeassistant/components/screenlogic/strings.json @@ -46,6 +46,20 @@ "description": "The ScreenLogic color mode to set." } } + }, + "start_super_chlorination": { + "name": "Start Super Chlorination", + "description": "Begins super chlorination, running for the specified period or 24 hours if none is specified.", + "fields": { + "runtime": { + "name": "Run Time", + "description": "Number of hours for super chlorination to run." + } + } + }, + "stop_super_chlorination": { + "name": "Stop Super Chlorination", + "description": "Stops super chlorination." } } }