Multiple Script Instances / Generic Scripts

Hey,

I’d like to create something like ‘generic scripts’.
So let’s say I have some heatings controlled by HA and want to have a BOOST-Mode handled by a script.
Therefore I don’t want to create a BOOST-script for each heating entity. I’d like to create a generic script, that handles the BOOST mode for every entity.
The entity_id is passed as a parameter.

thermostat_generic_boost:
alias: 'Aktiviere Boost-Modus für generisches Thermostat'
sequence:
  # Turn on central heating
  - service: script.heizung_boost
  # Set temperature for thermostat
  - service: script.thermostat_generic_update
    data_template:
      entity_id: '{{entity_id}}'
      temperature: 30
  # cancel running timers, start new timer
  - service: script.turn_off
    entity_id: script.thermostat_generic_boost_timer
  - service: script.thermostat_generic_boost_timer
    data_template:
      entity_id: '{{entity_id}}'

  thermostat_generic_boost_timer:
    alias: 'Beende Boost-Modus für generisches Thermostat'
    sequence:
      - delay: 00:30:00
      - service: script.thermostat_generic_auto
        data_template:
          entity_id: '{{entity_id}}'

If I got it right, script.turn_off turns off a running script, which then means (if its possible to run the same script n parallel times) all scripts are stopped.
In the logs I’ve found the following entry:
17-02-04 21:22:30 WARNING (MainThread) [homeassistant.components.script] Script script.thermostat_generic_boost already running.

So finally my questions are:

  • Is it possible to run multiple instances of a script?
  • If so, can I turn off a specific one or what exactly does script.turn_off?

Thanks in advance!
Matthias

2 Likes

Have you ever found a way to avoid the Script script.name already running. warnings? I’m encountering the same issue with my following script:

notify_pushover:
  sequence:
    - condition: state
      entity_id: input_boolean.notify_pushover
      state: 'on'
    - service: notify.pushover
      data_template:
        target: 'xxx'
        title: "{{ title | default('HA automation') }} @ {{ now().strftime('%H:%M:%S') }}"
        message: "{{ message }}"
        data:
          priority: '{{ priority | default(states.input_slider.pushover_priority.state | int) }}'
          timestamp: '{{ as_timestamp(now()) }}'

I have all of my automations set up to send me Pushover notifications through the script above. If 2 or more automations trigger around the same time, only 1 notification comes through and the other ones cause the warning above in my log file.

No I haven’t… My workaround is to use Appdaemon for “parallel” scripts.

Guys out there, if someone does know a “clean” solution inside HASS -> let us know :slight_smile:

1 Like

I am having the same problem.

@MI8 could you please share your Appdaemoin solution? Thanks!

Yes sure:

See a generic timer for example. It can be activated with HASS-events.
Simply fire an event with event_data “entity_id” and “event_type” and a “duration” in seconds and the timer-script is going to fire the event after the time-out has reached.

So I active the timer in HASS by firing an event - then I listen to the event that will be fired from the timer.

def initialize(self):
	self.log("Starting Scripts...")

	# Global vars
	self.action_timers = dict()

	# Scripts can be called by firing its event identifier
	self.listen_event(self.generic_event_timer, "appdaemon_generic_event_timer")


def generic_event_timer(self, event_name, data, kwargs):
	#	data requirements:
	#	- entity_id
	#	- event_type (to fire)
	#   - duration (of timer in seconds)
	#   - Optional: kwargs for event

	def generic_timer_final_action(self, kwargs):
		# Stop running timers
		if kwargs['entity_id'] in self.action_timers:
			self.cancel_timer(self.action_timers[kwargs['entity_id']])
			self.action_timers.pop(kwargs['entity_id'], None)

		# Fire given event
		self.fire_event(kwargs['event_type'], **kwargs)

		# Log
		self.log("Generic timer ended and fired event %s for %s" % (kwargs['event_type'], kwargs['entity_id']))


	# If action is passed -> do this
	if 'action' in data and data['action'] == 'stop':
		kwargs.update(data)
		generic_timer_final_action(self, **kwargs)

	# else start 
	else:
		# Stop running timers
		if data['entity_id'] in self.action_timers:
			self.cancel_timer(self.action_timers[data['entity_id']])
			self.action_timers.pop(data['entity_id'], None)
		
		# Create and save new timer
		kwargs.update(data)
		timer = self.run_in(generic_timer_final_action, (int(data['duration'])), kwargs)
		self.action_timers[kwargs['entity_id']] = timer

		# Log
		self.log("Generic timer created for %s" % (kwargs['entity_id']))

The only stupid thing I noticed is:
Its not possible to use data_templates for event_data in hass… :frowning: So everything handled in hass must be static… (Fire Event with data_template)

Best, Matthias

For what it’s worth, it seems I found a workaround in using a python_script instead of a script. The script I shared above now looks like this:

# Get time and settings
time                = hass.states.get('sensor.time').state
sending             = hass.states.get('input_boolean.notify_pushover').state
default_priority    = hass.states.get('input_slider.pushover_priority').state

# Get script variables
target      = data.get('target') or 'xxx'
title       = (data.get('title') or 'HA automation') + ' @ ' + time
message     = data.get('message')
priority    = data.get('priority') or default_priority

# Call service
if sending == 'on' :
    data = { "target" : target, "title" : title, "message" : message , "data" : { "priority" : int(float(priority)) } }
    hass.services.call('notify', 'pushover', data)

It is not an exact rewrite of the script I was using before. For example: doing import time in the Python script is not supported, therefore I cannot use time.strftime('%H:%M:%S') to get a timestamp.

So it’s not a perfect solution, but it’s a workaround I can live with to not miss any notifications.