Home Assistant Unofficial Reference 2024.12.1
__init__.py
Go to the documentation of this file.
1 """Support for scripts."""
2 
3 from __future__ import annotations
4 
5 from abc import ABC, abstractmethod
6 import asyncio
7 from dataclasses import dataclass
8 import logging
9 from typing import TYPE_CHECKING, Any, cast
10 
11 from propcache import cached_property
12 import voluptuous as vol
13 
14 from homeassistant.components import websocket_api
15 from homeassistant.components.blueprint import CONF_USE_BLUEPRINT
16 from homeassistant.const import (
17  ATTR_ENTITY_ID,
18  ATTR_MODE,
19  ATTR_NAME,
20  CONF_ALIAS,
21  CONF_DESCRIPTION,
22  CONF_ICON,
23  CONF_MODE,
24  CONF_NAME,
25  CONF_PATH,
26  CONF_SEQUENCE,
27  CONF_VARIABLES,
28  SERVICE_RELOAD,
29  SERVICE_TOGGLE,
30  SERVICE_TURN_OFF,
31  SERVICE_TURN_ON,
32  STATE_ON,
33 )
34 from homeassistant.core import (
35  Context,
36  HomeAssistant,
37  ServiceCall,
38  ServiceResponse,
39  SupportsResponse,
40  callback,
41 )
43 from homeassistant.helpers.config_validation import make_entity_service_schema
44 from homeassistant.helpers.entity import ToggleEntity
45 from homeassistant.helpers.entity_component import EntityComponent
47  IssueSeverity,
48  async_create_issue,
49  async_delete_issue,
50 )
51 from homeassistant.helpers.restore_state import RestoreEntity
52 from homeassistant.helpers.script import (
53  ATTR_CUR,
54  ATTR_MAX,
55  CONF_MAX,
56  CONF_MAX_EXCEEDED,
57  Script,
58  ScriptRunResult,
59  script_stack_cv,
60 )
61 from homeassistant.helpers.service import async_set_service_schema
62 from homeassistant.helpers.trace import trace_get, trace_path
63 from homeassistant.helpers.typing import ConfigType
64 from homeassistant.loader import bind_hass
65 from homeassistant.util.async_ import create_eager_task
66 from homeassistant.util.dt import parse_datetime
67 
68 from .config import ScriptConfig, ValidationStatus
69 from .const import (
70  ATTR_LAST_ACTION,
71  ATTR_LAST_TRIGGERED,
72  ATTR_VARIABLES,
73  CONF_FIELDS,
74  CONF_TRACE,
75  DOMAIN,
76  ENTITY_ID_FORMAT,
77  EVENT_SCRIPT_STARTED,
78  LOGGER,
79 )
80 from .helpers import async_get_blueprints
81 from .trace import trace_script
82 
83 SCRIPT_SERVICE_SCHEMA = vol.Schema(dict)
84 SCRIPT_TURN_ONOFF_SCHEMA = make_entity_service_schema(
85  {vol.Optional(ATTR_VARIABLES): {str: cv.match_all}}
86 )
87 RELOAD_SERVICE_SCHEMA = vol.Schema({})
88 
89 
90 @bind_hass
91 def is_on(hass: HomeAssistant, entity_id: str) -> bool:
92  """Return if the script is on based on the statemachine."""
93  return hass.states.is_state(entity_id, STATE_ON)
94 
95 
97  hass: HomeAssistant, referenced_id: str, property_name: str
98 ) -> list[str]:
99  """Return all scripts that reference the x."""
100  if DOMAIN not in hass.data:
101  return []
102 
103  component: EntityComponent[BaseScriptEntity] = hass.data[DOMAIN]
104 
105  return [
106  script_entity.entity_id
107  for script_entity in component.entities
108  if referenced_id in getattr(script_entity, property_name)
109  ]
110 
111 
112 def _x_in_script(hass: HomeAssistant, entity_id: str, property_name: str) -> list[str]:
113  """Return all x in a script."""
114  if DOMAIN not in hass.data:
115  return []
116 
117  component: EntityComponent[BaseScriptEntity] = hass.data[DOMAIN]
118 
119  if (script_entity := component.get_entity(entity_id)) is None:
120  return []
121 
122  return list(getattr(script_entity, property_name))
123 
124 
125 @callback
126 def scripts_with_entity(hass: HomeAssistant, entity_id: str) -> list[str]:
127  """Return all scripts that reference the entity."""
128  return _scripts_with_x(hass, entity_id, "referenced_entities")
129 
130 
131 @callback
132 def entities_in_script(hass: HomeAssistant, entity_id: str) -> list[str]:
133  """Return all entities in script."""
134  return _x_in_script(hass, entity_id, "referenced_entities")
135 
136 
137 @callback
138 def scripts_with_device(hass: HomeAssistant, device_id: str) -> list[str]:
139  """Return all scripts that reference the device."""
140  return _scripts_with_x(hass, device_id, "referenced_devices")
141 
142 
143 @callback
144 def devices_in_script(hass: HomeAssistant, entity_id: str) -> list[str]:
145  """Return all devices in script."""
146  return _x_in_script(hass, entity_id, "referenced_devices")
147 
148 
149 @callback
150 def scripts_with_area(hass: HomeAssistant, area_id: str) -> list[str]:
151  """Return all scripts that reference the area."""
152  return _scripts_with_x(hass, area_id, "referenced_areas")
153 
154 
155 @callback
156 def areas_in_script(hass: HomeAssistant, entity_id: str) -> list[str]:
157  """Return all areas in a script."""
158  return _x_in_script(hass, entity_id, "referenced_areas")
159 
160 
161 @callback
162 def scripts_with_floor(hass: HomeAssistant, floor_id: str) -> list[str]:
163  """Return all scripts that reference the floor."""
164  return _scripts_with_x(hass, floor_id, "referenced_floors")
165 
166 
167 @callback
168 def floors_in_script(hass: HomeAssistant, entity_id: str) -> list[str]:
169  """Return all floors in a script."""
170  return _x_in_script(hass, entity_id, "referenced_floors")
171 
172 
173 @callback
174 def scripts_with_label(hass: HomeAssistant, label_id: str) -> list[str]:
175  """Return all scripts that reference the label."""
176  return _scripts_with_x(hass, label_id, "referenced_labels")
177 
178 
179 @callback
180 def labels_in_script(hass: HomeAssistant, entity_id: str) -> list[str]:
181  """Return all labels in a script."""
182  return _x_in_script(hass, entity_id, "referenced_labels")
183 
184 
185 @callback
186 def scripts_with_blueprint(hass: HomeAssistant, blueprint_path: str) -> list[str]:
187  """Return all scripts that reference the blueprint."""
188  if DOMAIN not in hass.data:
189  return []
190 
191  component: EntityComponent[BaseScriptEntity] = hass.data[DOMAIN]
192 
193  return [
194  script_entity.entity_id
195  for script_entity in component.entities
196  if script_entity.referenced_blueprint == blueprint_path
197  ]
198 
199 
200 @callback
201 def blueprint_in_script(hass: HomeAssistant, entity_id: str) -> str | None:
202  """Return the blueprint the script is based on or None."""
203  if DOMAIN not in hass.data:
204  return None
205 
206  component: EntityComponent[BaseScriptEntity] = hass.data[DOMAIN]
207 
208  if (script_entity := component.get_entity(entity_id)) is None:
209  return None
210 
211  return script_entity.referenced_blueprint
212 
213 
214 async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
215  """Load the scripts from the configuration."""
216  hass.data[DOMAIN] = component = EntityComponent[BaseScriptEntity](
217  LOGGER, DOMAIN, hass
218  )
219 
220  # Register script as valid domain for Blueprint
222 
223  await _async_process_config(hass, config, component)
224 
225  # Add some default blueprints to blueprints/script, does nothing
226  # if blueprints/script already exists but still has to create
227  # an executor job to check if the folder exists so we run it in a
228  # separate task to avoid waiting for it to finish setting up
229  # since a tracked task will be waited at the end of startup
230  hass.async_create_task(
231  async_get_blueprints(hass).async_populate(), eager_start=True
232  )
233 
234  async def reload_service(service: ServiceCall) -> None:
235  """Call a service to reload scripts."""
236  await async_get_blueprints(hass).async_reset_cache()
237  if (conf := await component.async_prepare_reload(skip_reset=True)) is None:
238  return
239  await _async_process_config(hass, conf, component)
240 
241  async def turn_on_service(service: ServiceCall) -> None:
242  """Call a service to turn script on."""
243  variables = service.data.get(ATTR_VARIABLES)
244  script_entities = await component.async_extract_from_service(service)
245  for script_entity in script_entities:
246  await script_entity.async_turn_on(
247  variables=variables, context=service.context, wait=False
248  )
249 
250  async def turn_off_service(service: ServiceCall) -> None:
251  """Cancel a script."""
252  # Stopping a script is ok to be done in parallel
253  script_entities = await component.async_extract_from_service(service)
254 
255  if not script_entities:
256  return
257 
258  await asyncio.wait(
259  [
260  create_eager_task(script_entity.async_turn_off())
261  for script_entity in script_entities
262  ]
263  )
264 
265  async def toggle_service(service: ServiceCall) -> None:
266  """Toggle a script."""
267  script_entities = await component.async_extract_from_service(service)
268  for script_entity in script_entities:
269  await script_entity.async_toggle(context=service.context, wait=False)
270 
271  hass.services.async_register(
272  DOMAIN, SERVICE_RELOAD, reload_service, schema=RELOAD_SERVICE_SCHEMA
273  )
274  hass.services.async_register(
275  DOMAIN, SERVICE_TURN_ON, turn_on_service, schema=SCRIPT_TURN_ONOFF_SCHEMA
276  )
277  hass.services.async_register(
278  DOMAIN, SERVICE_TURN_OFF, turn_off_service, schema=SCRIPT_TURN_ONOFF_SCHEMA
279  )
280  hass.services.async_register(
281  DOMAIN, SERVICE_TOGGLE, toggle_service, schema=SCRIPT_TURN_ONOFF_SCHEMA
282  )
283  websocket_api.async_register_command(hass, websocket_config)
284 
285  return True
286 
287 
288 @dataclass(slots=True)
290  """Container for prepared script entity configuration."""
291 
292  config_block: ConfigType
293  key: str
294  raw_blueprint_inputs: ConfigType | None
295  raw_config: ConfigType | None
296  validation_error: str | None
297  validation_status: ValidationStatus
298 
299 
301  hass: HomeAssistant,
302  config: ConfigType,
303 ) -> list[ScriptEntityConfig]:
304  """Parse configuration and prepare script entity configuration."""
305  script_configs: list[ScriptEntityConfig] = []
306 
307  conf: dict[str, ConfigType] = config[DOMAIN]
308 
309  for key, config_block in conf.items():
310  raw_config = cast(ScriptConfig, config_block).raw_config
311  raw_blueprint_inputs = cast(ScriptConfig, config_block).raw_blueprint_inputs
312  validation_error = cast(ScriptConfig, config_block).validation_error
313  validation_status = cast(ScriptConfig, config_block).validation_status
314 
315  script_configs.append(
317  config_block,
318  key,
319  raw_blueprint_inputs,
320  raw_config,
321  validation_error,
322  validation_status,
323  )
324  )
325 
326  return script_configs
327 
328 
330  hass: HomeAssistant, script_configs: list[ScriptEntityConfig]
331 ) -> list[BaseScriptEntity]:
332  """Create script entities from prepared configuration."""
333  entities: list[BaseScriptEntity] = []
334 
335  for script_config in script_configs:
336  if script_config.validation_status != ValidationStatus.OK:
337  entities.append(
339  script_config.key,
340  script_config.raw_config,
341  cast(str, script_config.validation_error),
342  script_config.validation_status,
343  )
344  )
345  continue
346 
347  entity = ScriptEntity(
348  hass,
349  script_config.key,
350  script_config.config_block,
351  script_config.raw_config,
352  script_config.raw_blueprint_inputs,
353  )
354  entities.append(entity)
355 
356  return entities
357 
358 
360  hass: HomeAssistant,
361  config: ConfigType,
362  component: EntityComponent[BaseScriptEntity],
363 ) -> None:
364  """Process script configuration."""
365  entities = []
366 
367  def script_matches_config(
368  script: BaseScriptEntity, config: ScriptEntityConfig
369  ) -> bool:
370  return script.unique_id == config.key and script.raw_config == config.raw_config
371 
372  def find_matches(
373  scripts: list[BaseScriptEntity],
374  script_configs: list[ScriptEntityConfig],
375  ) -> tuple[set[int], set[int]]:
376  """Find matches between a list of script entities and a list of configurations.
377 
378  A script or configuration is only allowed to match at most once to handle
379  the case of multiple scripts with identical configuration.
380 
381  Returns a tuple of sets of indices: ({script_matches}, {config_matches})
382  """
383  script_matches: set[int] = set()
384  config_matches: set[int] = set()
385 
386  for script_idx, script in enumerate(scripts):
387  for config_idx, script_config in enumerate(script_configs):
388  if config_idx in config_matches:
389  # Only allow a script config to match at most once
390  continue
391  if script_matches_config(script, script_config):
392  script_matches.add(script_idx)
393  config_matches.add(config_idx)
394  # Only allow a script to match at most once
395  break
396 
397  return script_matches, config_matches
398 
399  script_configs = await _prepare_script_config(hass, config)
400  scripts: list[BaseScriptEntity] = list(component.entities)
401 
402  # Find scripts and configurations which have matches
403  script_matches, config_matches = find_matches(scripts, script_configs)
404 
405  # Remove scripts which have changed config or no longer exist
406  tasks = [
407  script.async_remove()
408  for idx, script in enumerate(scripts)
409  if idx not in script_matches
410  ]
411  await asyncio.gather(*tasks)
412 
413  # Create scripts which have changed config or have been added
414  updated_script_configs = [
415  config for idx, config in enumerate(script_configs) if idx not in config_matches
416  ]
417  entities = await _create_script_entities(hass, updated_script_configs)
418  await component.async_add_entities(entities)
419 
420 
422  """Base class for script entities."""
423 
424  _entity_component_unrecorded_attributes = frozenset(
425  {ATTR_LAST_TRIGGERED, ATTR_MODE, ATTR_CUR, ATTR_MAX, ATTR_LAST_ACTION}
426  )
427 
428  raw_config: ConfigType | None
429 
430  @cached_property
431  @abstractmethod
432  def referenced_labels(self) -> set[str]:
433  """Return a set of referenced labels."""
434 
435  @cached_property
436  @abstractmethod
437  def referenced_floors(self) -> set[str]:
438  """Return a set of referenced floors."""
439 
440  @cached_property
441  @abstractmethod
442  def referenced_areas(self) -> set[str]:
443  """Return a set of referenced areas."""
444 
445  @property
446  @abstractmethod
447  def referenced_blueprint(self) -> str | None:
448  """Return referenced blueprint or None."""
449 
450  @cached_property
451  @abstractmethod
452  def referenced_devices(self) -> set[str]:
453  """Return a set of referenced devices."""
454 
455  @cached_property
456  @abstractmethod
457  def referenced_entities(self) -> set[str]:
458  """Return a set of referenced entities."""
459 
460 
461 class UnavailableScriptEntity(BaseScriptEntity):
462  """A non-functional script entity with its state set to unavailable.
463 
464  This class is instantiated when an script fails to validate.
465  """
466 
467  _attr_should_poll = False
468  _attr_available = False
469 
470  def __init__(
471  self,
472  key: str,
473  raw_config: ConfigType | None,
474  validation_error: str,
475  validation_status: ValidationStatus,
476  ) -> None:
477  """Initialize a script entity."""
478  self._attr_name_attr_name = raw_config.get(CONF_ALIAS, key) if raw_config else key
479  self._attr_unique_id_attr_unique_id = key
480  self.raw_configraw_config = raw_config
481  self._validation_error_validation_error = validation_error
482  self._validation_status_validation_status = validation_status
483 
484  @cached_property
485  def referenced_labels(self) -> set[str]:
486  """Return a set of referenced labels."""
487  return set()
488 
489  @cached_property
490  def referenced_floors(self) -> set[str]:
491  """Return a set of referenced floors."""
492  return set()
493 
494  @cached_property
495  def referenced_areas(self) -> set[str]:
496  """Return a set of referenced areas."""
497  return set()
498 
499  @property
500  def referenced_blueprint(self) -> str | None:
501  """Return referenced blueprint or None."""
502  return None
503 
504  @cached_property
505  def referenced_devices(self) -> set[str]:
506  """Return a set of referenced devices."""
507  return set()
508 
509  @cached_property
510  def referenced_entities(self) -> set[str]:
511  """Return a set of referenced entities."""
512  return set()
513 
514  async def async_added_to_hass(self) -> None:
515  """Create a repair issue to notify the user the automation has errors."""
516  await super().async_added_to_hass()
518  self.hasshass,
519  DOMAIN,
520  f"{self.entity_id}_validation_{self._validation_status}",
521  is_fixable=False,
522  severity=IssueSeverity.ERROR,
523  translation_key=f"validation_{self._validation_status}",
524  translation_placeholders={
525  "edit": f"/config/script/edit/{self.unique_id}",
526  "entity_id": self.entity_identity_id,
527  "error": self._validation_error_validation_error,
528  "name": self._attr_name_attr_name or self.entity_identity_id,
529  },
530  )
531 
532  async def async_will_remove_from_hass(self) -> None:
533  """Run when entity will be removed from hass."""
534  await super().async_will_remove_from_hass()
536  self.hasshass, DOMAIN, f"{self.entity_id}_validation_{self._validation_status}"
537  )
538 
539 
541  """Representation of a script entity."""
542 
543  icon = None
544  _attr_should_poll = False
545  _attr_unique_id: str
546 
547  def __init__(
548  self,
549  hass: HomeAssistant,
550  key: str,
551  cfg: ConfigType,
552  raw_config: ConfigType | None,
553  blueprint_inputs: ConfigType | None,
554  ) -> None:
555  """Initialize the script."""
556  self.iconiconicon = cfg.get(CONF_ICON)
557  self.descriptiondescription = cfg[CONF_DESCRIPTION]
558  self.fieldsfields = cfg[CONF_FIELDS]
559 
560  # The key of scripts are unique and cannot be changed from the UI after creating
561  self._attr_unique_id_attr_unique_id = key
562 
563  self.entity_identity_identity_id = ENTITY_ID_FORMAT.format(key)
564  self.scriptscript = Script(
565  hass,
566  cfg[CONF_SEQUENCE],
567  cfg.get(CONF_ALIAS, key),
568  DOMAIN,
569  running_description="script sequence",
570  change_listener=self.async_change_listenerasync_change_listener,
571  script_mode=cfg[CONF_MODE],
572  max_runs=cfg[CONF_MAX],
573  max_exceeded=cfg[CONF_MAX_EXCEEDED],
574  logger=logging.getLogger(f"{__name__}.{key}"),
575  variables=cfg.get(CONF_VARIABLES),
576  )
577  self._changed_changed = asyncio.Event()
578  self.raw_configraw_config = raw_config
579  self._trace_config_trace_config = cfg[CONF_TRACE]
580  self._blueprint_inputs_blueprint_inputs = blueprint_inputs
581  self._attr_name_attr_name = self.scriptscript.name
582 
583  @property
584  def extra_state_attributes(self) -> dict[str, Any]:
585  """Return the state attributes."""
586  script = self.scriptscript
587  attrs = {
588  ATTR_LAST_TRIGGERED: script.last_triggered,
589  ATTR_MODE: script.script_mode,
590  ATTR_CUR: script.runs,
591  }
592  if script.supports_max:
593  attrs[ATTR_MAX] = script.max_runs
594  if script.last_action:
595  attrs[ATTR_LAST_ACTION] = script.last_action
596  return attrs
597 
598  @property
599  def is_on(self) -> bool:
600  """Return true if script is on."""
601  return self.scriptscript.is_running
602 
603  @cached_property
604  def referenced_labels(self) -> set[str]:
605  """Return a set of referenced labels."""
606  return self.scriptscript.referenced_labels
607 
608  @cached_property
609  def referenced_floors(self) -> set[str]:
610  """Return a set of referenced floors."""
611  return self.scriptscript.referenced_floors
612 
613  @cached_property
614  def referenced_areas(self) -> set[str]:
615  """Return a set of referenced areas."""
616  return self.scriptscript.referenced_areas
617 
618  @property
619  def referenced_blueprint(self) -> str | None:
620  """Return referenced blueprint or None."""
621  if self._blueprint_inputs_blueprint_inputs is None:
622  return None
623  path: str = self._blueprint_inputs_blueprint_inputs[CONF_USE_BLUEPRINT][CONF_PATH]
624  return path
625 
626  @cached_property
627  def referenced_devices(self) -> set[str]:
628  """Return a set of referenced devices."""
629  return self.scriptscript.referenced_devices
630 
631  @cached_property
632  def referenced_entities(self) -> set[str]:
633  """Return a set of referenced entities."""
634  return self.scriptscript.referenced_entities
635 
636  @callback
637  def async_change_listener(self) -> None:
638  """Update state."""
639  self.async_write_ha_stateasync_write_ha_state()
640  self._changed_changed.set()
641 
642  async def async_turn_on(self, **kwargs: Any) -> None:
643  """Run the script.
644 
645  Depending on the script's run mode, this may do nothing, restart the script or
646  fire an additional parallel run.
647  """
648  variables: dict[str, Any] | None = kwargs.get("variables")
649  context: Context = kwargs["context"]
650  wait: bool = kwargs.get("wait", True)
651  await self._async_start_run_async_start_run(variables, context, wait)
652 
653  async def _async_start_run(
654  self, variables: dict[str, Any] | None, context: Context, wait: bool
655  ) -> ServiceResponse:
656  """Start the run of a script."""
657  self.async_set_contextasync_set_context(context)
658  self.hasshass.bus.async_fire(
659  EVENT_SCRIPT_STARTED,
660  {ATTR_NAME: self.scriptscript.name, ATTR_ENTITY_ID: self.entity_identity_identity_id},
661  context=context,
662  )
663  coro = self._async_run_async_run(variables, context)
664  if wait:
665  # If we are executing in parallel, we need to copy the script stack so
666  # that if this script is called in parallel, it will not be seen in the
667  # stack of the other parallel calls and hit the disallowed recursion
668  # check as each parallel call would otherwise be appending to the same
669  # stack. We do not wipe the stack in this case because we still want to
670  # be able to detect if there is a disallowed recursion.
671  if script_stack := script_stack_cv.get():
672  script_stack_cv.set(script_stack.copy())
673 
674  script_result = await coro
675  return script_result.service_response if script_result else None
676 
677  # Caller does not want to wait for called script to finish so let script run in
678  # separate Task. Make a new empty script stack; scripts are allowed to
679  # recursively turn themselves on when not waiting.
680  script_stack_cv.set([])
681 
682  self._changed_changed.clear()
683  self.hasshass.async_create_task(coro, eager_start=True)
684  # Wait for first state change so we can guarantee that
685  # it is written to the State Machine before we return.
686  await self._changed_changed.wait()
687  return None
688 
689  async def _async_run(
690  self, variables: dict[str, Any] | None, context: Context
691  ) -> ScriptRunResult | None:
692  with trace_script(
693  self.hasshass,
694  self._attr_unique_id_attr_unique_id,
695  self.raw_configraw_config,
696  self._blueprint_inputs_blueprint_inputs,
697  context,
698  self._trace_config_trace_config,
699  ) as script_trace:
700  # Prepare tracing the execution of the script's sequence
701  script_trace.set_trace(trace_get())
702  with trace_path("sequence"):
703  this = None
704  if state := self.hasshass.states.get(self.entity_identity_identity_id):
705  this = state.as_dict()
706  script_vars = {"this": this, **(variables or {})}
707  return await self.scriptscript.async_run(script_vars, context)
708 
709  async def async_turn_off(self, **kwargs: Any) -> None:
710  """Stop running the script.
711 
712  If multiple runs are in progress, all will be stopped.
713  """
714  await self.scriptscript.async_stop()
715 
716  async def _service_handler(self, service: ServiceCall) -> ServiceResponse:
717  """Execute a service call to script.<script name>."""
718  response = await self._async_start_run_async_start_run(
719  variables=service.data, context=service.context, wait=True
720  )
721  if service.return_response:
722  return response or {}
723  return None
724 
725  async def async_added_to_hass(self) -> None:
726  """Restore last triggered on startup and register service."""
727  if TYPE_CHECKING:
728  assert self.unique_idunique_id is not None
729  assert self.registry_entryregistry_entry is not None
730 
731  unique_id = self.unique_idunique_id
732  hass = self.hasshass
733  hass.services.async_register(
734  DOMAIN,
735  unique_id,
736  self._service_handler_service_handler,
737  schema=SCRIPT_SERVICE_SCHEMA,
738  supports_response=SupportsResponse.OPTIONAL,
739  )
740 
741  # Register the service description
742  service_desc = {
743  CONF_NAME: self.registry_entryregistry_entry.name or self.namename,
744  CONF_DESCRIPTION: self.descriptiondescription,
745  CONF_FIELDS: self.fieldsfields,
746  }
747  async_set_service_schema(hass, DOMAIN, unique_id, service_desc)
748 
749  if (state := await self.async_get_last_stateasync_get_last_state()) and (
750  last_triggered := state.attributes.get("last_triggered")
751  ):
752  self.scriptscript.last_triggered = parse_datetime(last_triggered)
753 
754  async def async_will_remove_from_hass(self) -> None:
755  """Stop script and remove service when it will be removed from HA."""
756  await self.scriptscript.async_stop()
757 
758  # remove service
759  self.hasshass.services.async_remove(DOMAIN, self._attr_unique_id_attr_unique_id)
760 
761 
762 @websocket_api.websocket_command({"type": "script/config", "entity_id": str})
764  hass: HomeAssistant,
765  connection: websocket_api.ActiveConnection,
766  msg: dict[str, Any],
767 ) -> None:
768  """Get script config."""
769  component: EntityComponent[BaseScriptEntity] = hass.data[DOMAIN]
770 
771  script = component.get_entity(msg["entity_id"])
772 
773  if script is None:
774  connection.send_error(
775  msg["id"], websocket_api.ERR_NOT_FOUND, "Entity not found"
776  )
777  return
778 
779  connection.send_result(
780  msg["id"],
781  {
782  "config": script.raw_config,
783  },
784  )
ServiceResponse _async_start_run(self, dict[str, Any]|None variables, Context context, bool wait)
Definition: __init__.py:655
None async_turn_on(self, **Any kwargs)
Definition: __init__.py:642
ScriptRunResult|None _async_run(self, dict[str, Any]|None variables, Context context)
Definition: __init__.py:691
dict[str, Any] extra_state_attributes(self)
Definition: __init__.py:584
ServiceResponse _service_handler(self, ServiceCall service)
Definition: __init__.py:716
None async_turn_off(self, **Any kwargs)
Definition: __init__.py:709
None __init__(self, HomeAssistant hass, str key, ConfigType cfg, ConfigType|None raw_config, ConfigType|None blueprint_inputs)
Definition: __init__.py:554
None __init__(self, str key, ConfigType|None raw_config, str validation_error, ValidationStatus validation_status)
Definition: __init__.py:476
str|UndefinedType|None name(self)
Definition: entity.py:738
None async_set_context(self, Context context)
Definition: entity.py:937
blueprint.DomainBlueprints async_get_blueprints(HomeAssistant hass)
Definition: helpers.py:29
datetime|None parse_datetime(str|None value)
Definition: sensor.py:138
None async_stop(HomeAssistant hass)
Definition: discovery.py:694
None async_create_issue(HomeAssistant hass, str entry_id)
Definition: repairs.py:69
None async_delete_issue(HomeAssistant hass, str entry_id)
Definition: repairs.py:85
Iterator[ScriptTrace] trace_script(HomeAssistant hass, str item_id, dict[str, Any]|None config, dict[str, Any]|None blueprint_inputs, Context context, dict[str, Any] trace_config)
Definition: trace.py:33
list[str] scripts_with_floor(HomeAssistant hass, str floor_id)
Definition: __init__.py:162
list[str] scripts_with_device(HomeAssistant hass, str device_id)
Definition: __init__.py:138
None websocket_config(HomeAssistant hass, websocket_api.ActiveConnection connection, dict[str, Any] msg)
Definition: __init__.py:767
list[str] _scripts_with_x(HomeAssistant hass, str referenced_id, str property_name)
Definition: __init__.py:98
list[str] labels_in_script(HomeAssistant hass, str entity_id)
Definition: __init__.py:180
bool async_setup(HomeAssistant hass, ConfigType config)
Definition: __init__.py:214
bool is_on(HomeAssistant hass, str entity_id)
Definition: __init__.py:91
str|None blueprint_in_script(HomeAssistant hass, str entity_id)
Definition: __init__.py:201
None _async_process_config(HomeAssistant hass, ConfigType config, EntityComponent[BaseScriptEntity] component)
Definition: __init__.py:363
list[str] areas_in_script(HomeAssistant hass, str entity_id)
Definition: __init__.py:156
list[str] _x_in_script(HomeAssistant hass, str entity_id, str property_name)
Definition: __init__.py:112
list[str] devices_in_script(HomeAssistant hass, str entity_id)
Definition: __init__.py:144
list[str] scripts_with_blueprint(HomeAssistant hass, str blueprint_path)
Definition: __init__.py:186
list[str] scripts_with_label(HomeAssistant hass, str label_id)
Definition: __init__.py:174
list[str] floors_in_script(HomeAssistant hass, str entity_id)
Definition: __init__.py:168
list[str] scripts_with_area(HomeAssistant hass, str area_id)
Definition: __init__.py:150
list[str] scripts_with_entity(HomeAssistant hass, str entity_id)
Definition: __init__.py:126
list[ScriptEntityConfig] _prepare_script_config(HomeAssistant hass, ConfigType config)
Definition: __init__.py:303
list[BaseScriptEntity] _create_script_entities(HomeAssistant hass, list[ScriptEntityConfig] script_configs)
Definition: __init__.py:331
list[str] entities_in_script(HomeAssistant hass, str entity_id)
Definition: __init__.py:132
VolSchemaType make_entity_service_schema(dict|None schema, *int extra=vol.PREVENT_EXTRA)
None async_set_service_schema(HomeAssistant hass, str domain, str service, dict[str, Any] schema)
Definition: service.py:844
Generator[None] trace_path(str|list[str] suffix)
Definition: trace.py:251
dict[str, deque[TraceElement]]|None trace_get(bool clear=True)
Definition: trace.py:194