From ab7b7ef95f90d33483e7084c933b2cdbd36d8e48 Mon Sep 17 00:00:00 2001 From: Ed Leckert Date: Fri, 4 Oct 2024 15:33:01 -0600 Subject: [PATCH] Split run services by target type --- README.md | 143 ++++++++++-------- custom_components/opensprinkler/__init__.py | 63 +++++++- custom_components/opensprinkler/const.py | 18 +++ custom_components/opensprinkler/services.yaml | 39 +++++ custom_components/opensprinkler/strings.json | 42 +++++ .../opensprinkler/translations/en.json | 42 +++++ 6 files changed, 283 insertions(+), 64 deletions(-) diff --git a/README.md b/README.md index 9869c88..6c01c76 100755 --- a/README.md +++ b/README.md @@ -17,9 +17,9 @@ Last tested on OS API `2.2.0` and Home Assistant `2024.2.0` - Sensors for water level, last runtime and rain delay stop time - Switches for each program and station to enable/disable program or station - Switch to enable/disable OpenSprinkler controller operation -- Services to run and stop stations -- Service to run programs -- Services to pause, set a rain delay, and set the water level. +- Actions to run and stop stations +- Action to run programs +- Actions to pause, set a rain delay, and set the water level. To have a Lovelace card for the UI, [opensprinkler-card](https://github.com/rianadon/opensprinkler-card) can be used. @@ -47,51 +47,62 @@ Note: _1.0.0 has major breaking changes, you will need to update any automations - Program binary sensors now show running state instead of operation state. Please use the program switch states for program operation state. - Controller binary sensor is removed. Please use controller switch state for controller operation state. -- Station switches now enable/disable instead of run/stop stations. Please use `opensprinkler.run` and `opensprinkler.stop` services to run and stop stations. -- All scenes are removed. Please use the `opensprinkler.run` service to run programs. +- Station switches now enable/disable instead of run/stop stations. Please use `opensprinkler.run_station` and `opensprinkler.stop` actions to run and stop stations. +- All scenes are removed. Please use the `opensprinkler.run_program` action to run programs. -## Using Services +## Using Actions -Available services are `opensprinkler.run` for programs, stations and controllers (for running once program), and `opensprinkler.stop` for stations or controller (to stop all stations). +Available actions are `opensprinkler.run_program`, `opensprinkler.run_station`, and `opensprinkler.run_once` +to start a program, station, or controller (multiple stations) respectively, and `opensprinkler.stop` to stop one or all stations. + +Note: The action `opensprinkler.run` is deprecated and will be removed in a future release. Please migrate to one of the above actions, +which use the same parameters. ### Run Examples +Note: If using a version of Home Assistant prior to 2024.8, substitute the keyword `service` for `action` in the following examples. + #### Run Program Example ```yaml -service: opensprinkler.run -data: - entity_id: switch.program_name # Switches or sensors for programs +action: opensprinkler.run_program +target: + entity_id: switch.standard_schedule_program_enabled # Any program enabled switch +data: {} ``` #### Run Station Example ```yaml -service: opensprinkler.run +action: opensprinkler.run_station data: - entity_id: switch.station_name # Switches or sensors for stations - run_seconds: 60 # Seconds to run (optional, defaults to 60 seconds) -``` + run_seconds: 600 # Number of seconds to run the station. Optional, defaults to 60 seconds. +target: + entity_id: switch.front_yard_station_enabled # Any station enabled switch + ``` #### Run Once Program Example -To run an once program, the run seconds can either be a list of seconds per station or a list of index and second pairs. +To run a number of stations at once, use `opensprinkler.run_once`. The run seconds can either be a list of seconds per station +or a list or dict of index and seconds pairs. The following examples are all equivalent. ```yaml -service: opensprinkler.run +action: opensprinkler.run_once +target: + entity_id: switch.opensprinkler_enabled # Controller enabled switch data: - entity_id: switch.controller_name # Switches or sensors for controller - run_seconds: # Seconds to run for each station (required) + run_seconds: # List of seconds to run for each station (required) - 60 - 0 - 30 ``` ```yaml -service: opensprinkler.run +action: opensprinkler.run_once +target: + entity_id: switch.opensprinkler_enabled # Controller enabled switch data: - entity_id: switch.controller_name # Switches or sensors for controller run_seconds: # List of station index and run seconds pairs (required) - index: 0 run_seconds: 60 @@ -100,38 +111,41 @@ data: ``` ```yaml -service: opensprinkler.run +action: opensprinkler.run_once +target: + entity_id: switch.opensprinkler_enabled # Controller enabled switch data: - entity_id: switch.controller_name # Switches or sensors for controller run_seconds: # Dictionary of station index and run seconds key/value pairs (required) - 0: 60 - 2: 30 + "0": 60 + "2": 30 ``` -By default running once program will stop all other stations that are running, you can specify -`continue_running_stations` to true to allow the stations to continue running. This only works when -specifying the run seconds in index/second pairs. +Calling `opensprinkler.run_once` or `opensprinkler.run_program` will stop all other stations that are running. +When using `opensprinkler.run_once`, you can set `continue_running_stations` to true to allow the stations to +continue running. This only works when specifying the run seconds in index/seconds pairs. ```yaml -service: opensprinkler.run +action: opensprinkler.run_once +target: + entity_id: switch.opensprinkler_enabled # Controller enabled switch data: - entity_id: switch.controller_name # Switches or sensors for controller - run_seconds: # List of station index and run seconds pairs (required) + continue_running_stations: true # Keep running stations running (optional, defaults to false) + run_seconds: - index: 0 run_seconds: 60 - index: 2 run_seconds: 30 - continue_running_stations: True # Whether to keep running stations running (optional, defaults to False) ``` ```yaml -service: opensprinkler.run +action: opensprinkler.run_once +target: + entity_id: switch.opensprinkler_enabled # Controller enabled switch data: - entity_id: switch.controller_name # Switches or sensors for controller - run_seconds: # Dictionary of station index and run seconds key/value pairs (required) - 0: 60 - 2: 30 - continue_running_stations: True # Whether to keep running stations running (optional, defaults to False) + continue_running_stations: true # Keep running stations running (optional, defaults to false) + run_seconds: + "0": 60 + "2": 30 ``` ### Stop Examples @@ -139,28 +153,31 @@ data: #### Stop Station Example ```yaml -service: opensprinkler.stop -data: - entity_id: switch.station_name # Switches or sensors for stations +action: opensprinkler.stop +data: {} +target: + entity_id: switch.drip_station_enabled # Any station enabled switch ``` #### Stop All Stations Example ```yaml -service: opensprinkler.stop -data: - entity_id: switch.controller_name # Switches or sensors for controller -``` +action: opensprinkler.stop +data: {} +target: + entity_id: switch.opensprinkler_enabled # Controller enabled switch + ``` ### Set Water Level Example This sets the water level to 50%, i.e. all stations will run half of their normally configured time. ```yaml -service: opensprinkler.set_water_level +action: opensprinkler.set_water_level data: - entity_id: sensor.opensprinkler_water_level water_level: 50 +target: + entity_id: switch.opensprinkler_enabled # Controller enabled switch ``` ### Set Rain Delay Example @@ -168,10 +185,11 @@ data: This sets the rain delay of the controller to 6 hours, i.e. all stations will stop and programs will not run until the rain delay time is over. ```yaml -service: opensprinkler.set_rain_delay +action: opensprinkler.set_rain_delay data: - entity_id: sensor.opensprinkler_rain_delay_stop_time rain_delay: 6 +target: + entity_id: switch.opensprinkler_enabled # Controller enabled switch ``` ### Pause Example @@ -179,10 +197,11 @@ data: This pauses the station runs for 10 minutes (600 seconds), resuming afterwards. ```yaml -service: opensprinkler.pause_stations +action: opensprinkler.pause_stations data: - entity_id: sensor.opensprinkler_pause_end_time pause_duration: 600 +target: + entity_id: switch.opensprinkler_enabled # Controller enabled switch ``` ### Reboot Controller Example @@ -190,9 +209,10 @@ data: This reboots the controller. ```yaml -service: opensprinkler.reboot -data: - entity_id: switch.opensprinkler_enabled +action: opensprinkler.reboot +data: {} +target: + entity_id: switch.opensprinkler_enabled # Controller enabled switch ``` ## Creating a Station Switch @@ -205,20 +225,21 @@ switch: - platform: template switches: fruits_station: - value_template: "{{ is_state('binary_sensor.s01_station_running', 'on') }}" + value_template: "{{ is_state('binary_sensor.front_yard_station_running', 'on') }}" turn_on: - service: opensprinkler.run - data_template: - entity_id: binary_sensor.s01_station_running + action: opensprinkler.run_station + target: + entity_id: switch.front_yard_station_enabled # Run seconds uses the input_number below. - run_seconds: "{{ ((states('input_number.s01_station_minutes') | float) * 60) | int }}" + data: + run_seconds: "{{ ((states('input_number.front_yard_station_minutes') | float) * 60) | int }}" turn_off: service: opensprinkler.stop - data: - entity_id: binary_sensor.s01_station_running + target: + entity_id: switch.front_yard_station_enabled ​ input_number: - s01_station_minutes: + front_yard_station_minutes: initial: 1 min: 1 max: 10 diff --git a/custom_components/opensprinkler/__init__.py b/custom_components/opensprinkler/__init__.py index c5b9c98..52f168d 100755 --- a/custom_components/opensprinkler/__init__.py +++ b/custom_components/opensprinkler/__init__.py @@ -39,12 +39,18 @@ SCHEMA_SERVICE_PAUSE_STATIONS, SCHEMA_SERVICE_REBOOT, SCHEMA_SERVICE_RUN, + SCHEMA_SERVICE_RUN_ONCE, + SCHEMA_SERVICE_RUN_PROGRAM, + SCHEMA_SERVICE_RUN_STATION, SCHEMA_SERVICE_SET_RAIN_DELAY, SCHEMA_SERVICE_SET_WATER_LEVEL, SCHEMA_SERVICE_STOP, SERVICE_PAUSE_STATIONS, SERVICE_REBOOT, SERVICE_RUN, + SERVICE_RUN_ONCE, + SERVICE_RUN_PROGRAM, + SERVICE_RUN_STATION, SERVICE_SET_RAIN_DELAY, SERVICE_SET_WATER_LEVEL, SERVICE_STOP, @@ -129,6 +135,42 @@ async def _async_send_run_command(call: ServiceCall) -> None: service_func=_async_send_run_command, ) + async def _async_send_run_once_command(call: ServiceCall) -> None: + await entity_service_call( + hass, async_get_entities(hass), SERVICE_RUN_ONCE, call, None + ) + + hass.services.async_register( + domain=DOMAIN, + service=SERVICE_RUN_ONCE, + schema=cv.make_entity_service_schema(SCHEMA_SERVICE_RUN_ONCE), + service_func=_async_send_run_once_command, + ) + + async def _async_send_run_program_command(call: ServiceCall) -> None: + await entity_service_call( + hass, async_get_entities(hass), SERVICE_RUN_PROGRAM, call, None + ) + + hass.services.async_register( + domain=DOMAIN, + service=SERVICE_RUN_PROGRAM, + schema=cv.make_entity_service_schema(SCHEMA_SERVICE_RUN_PROGRAM), + service_func=_async_send_run_program_command, + ) + + async def _async_send_run_station_command(call: ServiceCall) -> None: + await entity_service_call( + hass, async_get_entities(hass), SERVICE_RUN_STATION, call, None + ) + + hass.services.async_register( + domain=DOMAIN, + service=SERVICE_RUN_STATION, + schema=cv.make_entity_service_schema(SCHEMA_SERVICE_RUN_STATION), + service_func=_async_send_run_station_command, + ) + async def _async_send_stop_command(call: ServiceCall) -> None: await entity_service_call(hass, async_get_entities(hass), SERVICE_STOP, call) @@ -294,8 +336,13 @@ class OpenSprinklerTime(OpenSprinklerEntity): class OpenSprinklerControllerEntity: - async def run(self, run_seconds=None, continue_running_stations=None): + async def run_once(self, run_seconds=None, continue_running_stations=None): """Run once program.""" + await self.run(run_seconds, continue_running_stations) + return + + async def run(self, run_seconds=None, continue_running_stations=None): + """Run controller program.""" if run_seconds is None or ( not isinstance(run_seconds, list) and not isinstance(run_seconds, dict) ): @@ -317,7 +364,7 @@ async def run(self, run_seconds=None, continue_running_stations=None): run_seconds_list.append( seconds if seconds is not None - else (0 if continue_running_stations else station.seconds_remaining) + else (station.seconds_remaining if continue_running_stations else 0) ) await self._controller.run_once_program(run_seconds_list) await self._coordinator.async_request_refresh() @@ -335,7 +382,7 @@ async def run(self, run_seconds=None, continue_running_stations=None): run_seconds_list.append( run_seconds_by_index.get(station.index) if run_seconds_by_index.get(station.index) is not None - else (0 if continue_running_stations else station.seconds_remaining) + else (station.seconds_remaining if continue_running_stations else 0) ) await self._controller.run_once_program(run_seconds_list) @@ -387,6 +434,11 @@ def extra_state_attributes(self): return attributes + async def run_program(self): + """Run program.""" + await self.run() + return + async def run(self): """Runs the program.""" await self._program.run() @@ -417,6 +469,11 @@ def extra_state_attributes(self): return attributes + async def run_station(self, run_seconds=None): + """Run station.""" + await self.run(run_seconds) + return + async def run(self, run_seconds=None): """Run station.""" if run_seconds is not None and not isinstance(run_seconds, int): diff --git a/custom_components/opensprinkler/const.py b/custom_components/opensprinkler/const.py index ad59c02..25329c6 100644 --- a/custom_components/opensprinkler/const.py +++ b/custom_components/opensprinkler/const.py @@ -21,6 +21,7 @@ vol.Required(CONF_INDEX): cv.positive_int, vol.Required(CONF_RUN_SECONDS): cv.positive_int, } + SCHEMA_SERVICE_RUN = { vol.Optional(CONF_RUN_SECONDS): vol.Or( cv.ensure_list(cv.positive_int), @@ -30,6 +31,20 @@ ), vol.Optional(CONF_CONTINUE_RUNNING_STATIONS): cv.boolean, } + +SCHEMA_SERVICE_RUN_ONCE = { + vol.Required(CONF_RUN_SECONDS): vol.Or( + cv.ensure_list(cv.positive_int), + cv.ensure_list(SCHEMA_SERVICE_RUN_SECONDS), + vol.Schema({}, extra=vol.ALLOW_EXTRA), + ), + vol.Optional(CONF_CONTINUE_RUNNING_STATIONS): cv.boolean, +} + +SCHEMA_SERVICE_RUN_PROGRAM = {} + +SCHEMA_SERVICE_RUN_STATION = {vol.Optional(CONF_RUN_SECONDS): cv.positive_int} + SCHEMA_SERVICE_STOP = {} SCHEMA_SERVICE_SET_RAIN_DELAY = { @@ -51,6 +66,9 @@ SCHEMA_SERVICE_REBOOT = {} SERVICE_RUN = "run" +SERVICE_RUN_ONCE = "run_once" +SERVICE_RUN_PROGRAM = "run_program" +SERVICE_RUN_STATION = "run_station" SERVICE_STOP = "stop" SERVICE_SET_WATER_LEVEL = "set_water_level" SERVICE_REBOOT = "reboot" diff --git a/custom_components/opensprinkler/services.yaml b/custom_components/opensprinkler/services.yaml index ae0d78a..9e2f7a9 100644 --- a/custom_components/opensprinkler/services.yaml +++ b/custom_components/opensprinkler/services.yaml @@ -20,6 +20,45 @@ run: selector: boolean: +run_once: + target: + entity: + device_class: + - controller + device: + fields: + run_seconds: + example: "0: 60" + selector: + object: + continue_running_stations: + example: False + selector: + boolean: + +run_program: + target: + entity: + device_class: + - program + device: + +run_station: + target: + entity: + device_class: + - station + device: + fields: + run_seconds: + example: 60 + selector: + number: + min: 0 + max: 64800 + mode: slider + unit_of_measurement: "s" + stop: target: entity: diff --git a/custom_components/opensprinkler/strings.json b/custom_components/opensprinkler/strings.json index a648e3b..6fc012b 100644 --- a/custom_components/opensprinkler/strings.json +++ b/custom_components/opensprinkler/strings.json @@ -51,6 +51,48 @@ } } }, + "run_once": { + "name": "Run Once", + "description": "Runs a controller.", + "fields": { + "entity_id": { + "name": "Entity", + "description": "Switch entity id for controller." + }, + "run_seconds": { + "name": "Run seconds", + "description": "List of seconds for each station (e.g. '- 60'), or key/value pairs (e.g. '0: 60'). One per line. See documentation for details." + }, + "continue_running_stations": { + "name": "Continue running stations", + "description": "Keeps running stations that are not specified (only used for controllers with index/seconds pairs, optional, defaults to False)." + } + } + }, + "run_program": { + "name": "Run Program", + "description": "Runs a Program.", + "fields": { + "entity_id": { + "name": "Entity", + "description": "Switch entity id of a program." + } + } + }, + "run_station": { + "name": "Run Station", + "description": "Runs a Station for the specified number of seconds.", + "fields": { + "entity_id": { + "name": "Entity", + "description": "Switch entity id of a station." + }, + "run_seconds": { + "name": "Run seconds", + "description": "Number of seconds to run the station. Optional, defaults to 60 seconds." + } + } + }, "stop": { "name": "Stop", "description": "Stops a station or all station (for controller).", diff --git a/custom_components/opensprinkler/translations/en.json b/custom_components/opensprinkler/translations/en.json index a648e3b..6fc012b 100644 --- a/custom_components/opensprinkler/translations/en.json +++ b/custom_components/opensprinkler/translations/en.json @@ -51,6 +51,48 @@ } } }, + "run_once": { + "name": "Run Once", + "description": "Runs a controller.", + "fields": { + "entity_id": { + "name": "Entity", + "description": "Switch entity id for controller." + }, + "run_seconds": { + "name": "Run seconds", + "description": "List of seconds for each station (e.g. '- 60'), or key/value pairs (e.g. '0: 60'). One per line. See documentation for details." + }, + "continue_running_stations": { + "name": "Continue running stations", + "description": "Keeps running stations that are not specified (only used for controllers with index/seconds pairs, optional, defaults to False)." + } + } + }, + "run_program": { + "name": "Run Program", + "description": "Runs a Program.", + "fields": { + "entity_id": { + "name": "Entity", + "description": "Switch entity id of a program." + } + } + }, + "run_station": { + "name": "Run Station", + "description": "Runs a Station for the specified number of seconds.", + "fields": { + "entity_id": { + "name": "Entity", + "description": "Switch entity id of a station." + }, + "run_seconds": { + "name": "Run seconds", + "description": "Number of seconds to run the station. Optional, defaults to 60 seconds." + } + } + }, "stop": { "name": "Stop", "description": "Stops a station or all station (for controller).",