diff --git a/custom_components/connectlife/__init__.py b/custom_components/connectlife/__init__.py index eb1c694..554d817 100644 --- a/custom_components/connectlife/__init__.py +++ b/custom_components/connectlife/__init__.py @@ -6,10 +6,12 @@ from homeassistant.config_entries import ConfigEntry from homeassistant.const import Platform, CONF_USERNAME, CONF_PASSWORD from homeassistant.core import HomeAssistant +from homeassistant.helpers import config_validation as cv from connectlife.api import ConnectLifeApi, LifeConnectAuthError from .const import CONF_DEVELOPMENT_MODE, CONF_TEST_SERVER_URL, DOMAIN from .coordinator import ConnectLifeCoordinator +from .services import async_setup_services PLATFORMS: list[Platform] = [ Platform.BINARY_SENSOR, @@ -22,6 +24,16 @@ Platform.WATER_HEATER, ] +CONFIG_SCHEMA = cv.config_entry_only_config_schema(DOMAIN) + + +async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: + """Set up ConnectLife.""" + + await async_setup_services(hass) + + return True + async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up ConnectLife from a config entry.""" diff --git a/custom_components/connectlife/services.py b/custom_components/connectlife/services.py new file mode 100644 index 0000000..4eaa21d --- /dev/null +++ b/custom_components/connectlife/services.py @@ -0,0 +1,81 @@ +"""Services for the Fully Kiosk Browser integration.""" + +from __future__ import annotations + +import logging +import voluptuous as vol + +from homeassistant.config_entries import ConfigEntry, ConfigEntryState +from homeassistant.const import ATTR_DEVICE_ID +from homeassistant.core import HomeAssistant, ServiceCall +from homeassistant.exceptions import HomeAssistantError +import homeassistant.helpers.config_validation as cv +import homeassistant.helpers.device_registry as dr + +from .coordinator import ConnectLifeCoordinator +from .const import DOMAIN + +ATTR_ACTION = "action" +SERVICE_SET_ACTION = "set_action" + +_LOGGER = logging.getLogger(__name__) + + +async def async_setup_services(hass: HomeAssistant) -> None: + """Set up the services for the Fully Kiosk Browser integration.""" + + async def collect_coordinators( + device_ids: list[str], + ) -> dict[str, ConnectLifeCoordinator]: + config_entries = dict[str, ConfigEntry]() + registry = dr.async_get(hass) + for target in device_ids: + device = registry.async_get(target) + if device: + device_entries = dict[str, ConfigEntry]() + for entry_id in device.config_entries: + entry = hass.config_entries.async_get_entry(entry_id) + if entry and entry.domain == DOMAIN: + for (domain, device_id) in device.identifiers: + if domain == DOMAIN: + _LOGGER.debug(f"device_id: {device_id}") + device_entries[device_id] = entry + break + if not device_entries: + raise HomeAssistantError( + f"Device '{target}' is not a {DOMAIN} device" + ) + config_entries.update(device_entries) + else: + raise HomeAssistantError( + f"Device '{target}' not found in device registry" + ) + coordinators = dict[str, ConnectLifeCoordinator]() + for device_id, config_entry in config_entries.items(): + if config_entry.state != ConfigEntryState.LOADED: + raise HomeAssistantError(f"{config_entry.title} is not loaded") + coordinators[device_id] = hass.data[DOMAIN][config_entry.entry_id] + return coordinators + + async def async_set_action(call: ServiceCall) -> None: + """Set action on device.""" + coordinators = await collect_coordinators(call.data[ATTR_DEVICE_ID]) + for device_id, coordinator in coordinators.items(): + _LOGGER.debug("Setting Actions to %d on %s", call.data[ATTR_ACTION], device_id) + # TODO: Consider trigging a data update to avoid waiting for next poll to update state. + # Make sure to only do this once per coordinater. + await coordinator.async_update_device(device_id, {"Actions": call.data[ATTR_ACTION]}, {}) + + hass.services.async_register( + DOMAIN, + SERVICE_SET_ACTION, + async_set_action, + schema=vol.Schema( + vol.All( + { + vol.Required(ATTR_DEVICE_ID): cv.ensure_list, + vol.Required(ATTR_ACTION): cv.positive_int, + } + ) + ), + ) diff --git a/custom_components/connectlife/services.yaml b/custom_components/connectlife/services.yaml index 06c7e2d..a7ff503 100644 --- a/custom_components/connectlife/services.yaml +++ b/custom_components/connectlife/services.yaml @@ -8,4 +8,18 @@ set_value: required: true selector: number: - +set_action: + target: + device: + integration: connectlife + fields: + action: + required: true + selector: + select: + translation_key: actions + options: + - "1" + - "2" + - "3" + - "4" diff --git a/custom_components/connectlife/strings.json b/custom_components/connectlife/strings.json index 26edd9e..5b912c2 100644 --- a/custom_components/connectlife/strings.json +++ b/custom_components/connectlife/strings.json @@ -74,6 +74,16 @@ "test_server_required": "Development mode requires test server URL" } }, + "selector": { + "actions": { + "options": { + "1": "Start", + "2": "Stop", + "3": "Pause", + "4": "Open door" + } + } + }, "services": { "set_value": { "name": "Set value", @@ -84,6 +94,16 @@ "description": "Value to set." } } + }, + "set_action": { + "name": "Set action", + "description": "Sets action for device. Use with care.", + "fields": { + "action": { + "name": "Action", + "description": "Action to set." + } + } } }, "entity": { diff --git a/custom_components/connectlife/translations/en.json b/custom_components/connectlife/translations/en.json index afbbd53..a2509f5 100644 --- a/custom_components/connectlife/translations/en.json +++ b/custom_components/connectlife/translations/en.json @@ -74,6 +74,16 @@ "test_server_required": "Development mode requires test server URL" } }, + "selector": { + "actions": { + "options": { + "1": "Start", + "2": "Stop", + "3": "Pause", + "4": "Open door" + } + } + }, "services": { "set_value": { "name": "Set value", @@ -84,6 +94,16 @@ "description": "Value to set." } } + }, + "set_action": { + "name": "Set action", + "description": "Sets action for device. Use with care.", + "fields": { + "action": { + "name": "Action", + "description": "Action to set." + } + } } }, "entity": {