services.py 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. """Global services file.
  2. This needs to be viewed with the services.yaml file
  3. to demonstrate the different setup for using these services in the UI
  4. IMPORTANT NOTES:
  5. To ensure your service runs on the event loop, either make service function async
  6. or decorate with @callback. However, ensure that your function is non blocking or,
  7. if it is, run in the executor.
  8. Both examples are shown here. Running services on different threads can cause issues.
  9. https://developers.home-assistant.io/docs/dev_101_services/
  10. """
  11. import voluptuous as vol
  12. from homeassistant.config_entries import ConfigEntry
  13. from homeassistant.const import ATTR_DEVICE_ID, ATTR_NAME
  14. from homeassistant.core import HomeAssistant, ServiceCall, SupportsResponse, callback
  15. from homeassistant.exceptions import HomeAssistantError
  16. import homeassistant.helpers.device_registry as dr
  17. from .const import DOMAIN, RENAME_DEVICE_SERVICE_NAME, RESPONSE_SERVICE_NAME
  18. from .coordinator import ExampleCoordinator
  19. ATTR_TEXT = "text"
  20. # Services schemas
  21. RENAME_DEVICE_SERVICE_SCHEMA = vol.Schema(
  22. {
  23. vol.Required(ATTR_DEVICE_ID): int,
  24. vol.Required(ATTR_NAME): str,
  25. }
  26. )
  27. RESPONSE_SERVICE_SCHEMA = vol.Schema(
  28. {
  29. vol.Required(ATTR_DEVICE_ID): int,
  30. }
  31. )
  32. class ExampleServicesSetup:
  33. """Class to handle Integration Services."""
  34. def __init__(self, hass: HomeAssistant, config_entry: ConfigEntry) -> None:
  35. """Initialise services."""
  36. self.hass = hass
  37. self.config_entry = config_entry
  38. self.coordinator: ExampleCoordinator = config_entry.runtime_data.coordinator
  39. self.setup_services()
  40. def setup_services(self):
  41. """Initialise the services in Hass."""
  42. # ----------------------------------------------------------------------------
  43. # A simple definition of a service with 2 parameters, as denoted by the
  44. # RENAME_DEVICE_SERVICE_SCHEMA
  45. # ----------------------------------------------------------------------------
  46. self.hass.services.async_register(
  47. DOMAIN,
  48. RENAME_DEVICE_SERVICE_NAME,
  49. self.rename_device,
  50. schema=RENAME_DEVICE_SERVICE_SCHEMA,
  51. )
  52. # ----------------------------------------------------------------------------
  53. # The definition here for a response service is the same as before but you
  54. # must include supports_response = only/optional
  55. # https://developers.home-assistant.io/docs/dev_101_services/#supporting-response-data
  56. # ----------------------------------------------------------------------------
  57. self.hass.services.async_register(
  58. DOMAIN,
  59. RESPONSE_SERVICE_NAME,
  60. self.async_response_service,
  61. schema=RESPONSE_SERVICE_SCHEMA,
  62. supports_response=SupportsResponse.ONLY,
  63. )
  64. async def rename_device(self, service_call: ServiceCall) -> None:
  65. """Execute rename device service call function.
  66. This will send a command to the api which will rename the
  67. device and then update the device registry to match.
  68. Data from the service call will be in service_call.data
  69. as seen below.
  70. """
  71. device_id = service_call.data[ATTR_DEVICE_ID]
  72. device_name = service_call.data[ATTR_NAME]
  73. # check for valid device id
  74. try:
  75. assert self.coordinator.get_device(device_id) is not None
  76. except AssertionError as ex:
  77. raise HomeAssistantError(
  78. "Error calling service: The device ID does not exist"
  79. ) from ex
  80. else:
  81. result = await self.hass.async_add_executor_job(
  82. self.coordinator.api.set_data, device_id, "device_name", device_name
  83. )
  84. if result:
  85. # ----------------------------------------------------------------------------
  86. # In this scenario, we would need to update the device registry name here
  87. # as it will not automatically update.
  88. # ----------------------------------------------------------------------------
  89. # Get our device from coordinator data to retrieve its devie_uid as that is
  90. # what we used in the devices identifiers.
  91. device = self.coordinator.get_device(device_id)
  92. # Get the device registry
  93. device_registry = dr.async_get(self.hass)
  94. # Get the device entry in the registry by its identifiers. This is the same as
  95. # we used to set them in base.py
  96. device_entry = device_registry.async_get_device(
  97. [(DOMAIN, device["device_uid"])]
  98. )
  99. # Update our device entry with the new name. You will see this change in the UI
  100. device_registry.async_update_device(device_entry.id, name=device_name)
  101. await self.coordinator.async_request_refresh()
  102. @callback
  103. def async_response_service(self, service_call: ServiceCall) -> None:
  104. """Execute response service call function.
  105. This will take a device id and return json data for the
  106. devices info on the api.
  107. If the device does not exist, it will raise an error.
  108. """
  109. device_id = service_call.data[ATTR_DEVICE_ID]
  110. response = self.coordinator.get_device(device_id)
  111. try:
  112. assert response is not None
  113. except AssertionError as ex:
  114. raise HomeAssistantError(
  115. "Error calling service: The device ID does not exist"
  116. ) from ex
  117. else:
  118. return response