The Hass plugin connects to Home Assistant using the websocket API and maintains this connection while AppDaemon is running. In addition, it maintains an HTTP session because some functionality is only available via the REST API. If the connection is lost, the plugin will gracefully attempt to reconnect every 5s until it succeeds, any apps that are using the Hass API will be stopped and restarted when the connection is re-established.
The :py:class:`Hass <appdaemon.plugins.hass.hassapi.Hass>` API is an interface layer that makes it easy for users to
interact with the Hass plugin. In addition to all the methods of the ADAPI, it provides many methods that are
specific to Home Assistant. Most of these methods simply wrap calling services with some logic that make them more
convenient to use.
The Hass plugin must be configured in the appdaemon.yaml file under the plugins section in order for it to
connect to Home Assistant. This example shows where it fits into the overall configuration file.
# conf/appdaemon.yaml
appdaemon:
... # other AppDaemon config here
plugins:
HASS: # This is the name of the plugin, it can be anything
type: hass # required
ha_url: ... # required
token: ... # required
... # other Hass plugin config options hereThis is the full list of configuration options available for the Hass plugin.
| Key | Note | Description |
|---|---|---|
type |
required | This must be declared and it must be the exact value hass. |
ha_url |
required | URL to a Home Assistant instance, must include correct port and scheme (http:// or https://) |
token |
required | Long-lived token for for authentication with Home Assistant. See the section on authentication for more information on how to set it up. |
ha_key |
deprecated | Use token instead |
retry_secs |
optional | Time to sleep between connection attempts. Defaults to 5 seconds. |
cert_verify |
optional | Flag for adding an SSL context around the aiohttp.ClientSession. Set to False to disable (e.g., with internal IPs) |
cert_path |
optional | Path to the SSL certificate file. This is only used if cert_verify is set to True. |
api_port |
optional | Port the AppDaemon RESTful API will listen on. If not specified, API is disabled |
ws_timeout |
optional | Timeout for waiting for Home Assistant response from the websocket API. This is the time between when a websocket message is first sent and when Home Assistant responds with some kind of acknowledgement/result. Config values are parsed with :py:func:`parse_timedelta <appdaemon.utils.parse_timedelta>`. Defaults to 10 seconds. |
ws_max_msg_size |
optional | Maximum size in bytes for incoming websocket messages. Defaults to 4MB. Increase this if you have very large entities (e.g., many attributes) that cause messages to exceed this size. You can also allow any message size by setting this to 0, but that may cause other unforeseen issues. |
suppress_log_messages |
optional | If true, suppress log messages related to :py:meth:`call_service <appdaemon.plugins.hass.hassapi.Hass.call_service>`.
Defaults to false. |
appdaemon_startup_conditions |
optional | See the startup control section for more information. |
plugin_startup_conditions |
optional | See the startup control section for more information. |
The Hass plugin needs a long-lived access token to authenticate with Home Assistant over the websocket. This is
provided to AppDaemon by the token directive in the plugin configuration.
To create a long-lived access token, use the following steps:
1. Login as the user that you want to create the token for and open the user profile. The profile is found by clicking
the icon next to the Home Assistant label to the left of the web ui when the burger menu is clicked:
- At the bottom of the user profile is the Long-Lived Access Tokens section. Click on "Create Token"
This will pop up a dialog that asks you for the name of the token - this can be anything, it's just to remind you what
the token was created for - AppDaemon is as good a name as any. When you are done click OK
- A new dialog will popup with the token itself showing:
Copy this string and add it as the argument of the token directive in your HASS Plugin section:
token: ABCDEFA real token will be a lot longer than this and will consist of a string of random letters and numbers. For example:
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiIwZmRkYmE0YTM0MTY0...
4. A reference to your new token will be shown in the Long-Lived tokens section, and you can revoke access via this token at any time by pressing the delete icon. The token will last for 10 years.
The Hass plugin has the ability to pause startup until various criteria have been met. This can be useful for preventing apps that depend on certain entities or services from starting before they are available. AppDaemon will not mark the plugin as ready until all of these conditions have been met, which prevents any apps that depend on the plugin from being started. Each condition only has to be met once for it to be considered satisfied.
When the plugin first starts with AppDaemon itself, it will check the conditions in the appdaemon_startup_conditions
key before starting any apps. If the connection to Home Assistant is broken and re-established, it will check the
conditions in the plugin_startup_conditions key before starting any apps.
Starting Event
If/when the Hass plugin reconnects to Home Assistant, it will wait for the homeassistant_started event before
starting any of the apps that use the Hass API. Home Assistant will accept connections very early as it's
starting, even before some fundamental components have been loaded, which causes most apps to somehow fail without
waiting for this event. This is the same event that the Home Assistant web UI waits for to indicate readiness.
# conf/appdaemon.yaml
appdaemon:
... # other AppDaemon config here
plugins:
HASS:
type: hass # required
ha_url: ... # required
token: ... # required
... # other Hass plugin config options here
appdaemon_startup_conditions:
delay: ...
state: ...
event: ...
plugin_startup_conditions:
delay: ...
state: ...
event: ...Delay startup for a number of seconds, for example:
delay: 10 # delays for 10sWait until a specific state exists or has a specific value or set of values. The values can be specified as an inline dictionary as follows:
- wait until an entity exists -
state: {entity: <entity id>} - wait until an entity exists and has a specific value for its state:
state: {entity: <entity id>, value: {state: "on"}} - wait until an entity exists and has a specific value for an attribute:
state: {entity: <entity id>, value: {attributes: {attribute: value}}}
Example to wait for an input boolean:
state:
entity: input_boolean.appdaemon_enable # example entity name
value:
state: "on" # on needs to be in quotesExample to wait for a light to be on full brightness:
state:
entity: light.office_1 # example entity
value:
state: "on" # on needs to be in quotes
attributes:
brightness: 255 # full brightnessWait for an event or an event with specific data
- wait for an event of a given type:
{event_type: <event name>} - wait for an event with specific data:
{event_type: <event name>, data: {service_data: {entity_id: <some entity>}, service: <some service>}}
Example to wait for ZWave to complete initialization upon a HASS restart:
event:
event_type: zwave.network_readyExample to wait for an input button before starting AppDaemon
event:
event_type: call_service
data:
domain: input_button
service: press
service_data:
entity_id: input_button.start_appdaemon # example entityCreate apps using the Hass API by inheriting from the :py:class:`Hass <appdaemon.plugins.hass.hassapi.Hass>` class:
from appdaemon.plugins.hass import Hass
class MyApp(Hass):
def initialize(self):
... # Your initialization code hereRead the AppDaemon API Reference to learn other inherited helper functions that can be used by Hass applications.
Services are now called actions in Home Assistant, but are sometimes also referred to as service actions. Any of them can be called by using the :py:meth:`call_service <appdaemon.plugins.hass.hassapi.Hass.call_service>` method with their domain and service name.
The specific services available will vary depending on which integrations are installed in Home Assistant, but some
common ones would be light/toggle, switch/turn_off, etc. These services would control physical devices, but
services can do many other things as well.
Service Name Delimiter
AppDaemon uses the/delimiter to separate the domain and service name, instead of the.used by Home Assistant, solight.turn_onin Home Assistant becomeslight/turn_onin AppDaemon.
As of AppDaemon v4.5.0, service calls can return values. When services are registered with AppDaemon, Home Assistant
indicates whether they return values and whether doing so is optional. AppDaemon uses that information to automatically
insert "return_response": true into the message it sends to Home Assistant if necessary.
Home Assistant Responses
Home Assistant always responds with some kind of acknowledgement, even for services that don't otherwise return a value. AppDaemon includes whatever it gets from Home Assistant in the result dict.
| Key | Value |
|---|---|
id |
Sequential ID of the websocket request. This matches the one that AppDaemon used with the initial request. |
type |
This will always be result, as returned from Home Assistant. |
success |
Boolean representing whether the service call was successful or not. |
result |
Dict with the result of the service call if it was successful. |
error |
Dict with error information if the service call was not successful. |
ad_status |
Status from AppDaemon for the request. |
ad_duration |
Floating point number representing the round trip time of the request in seconds. |
| Status | Value |
|---|---|
OK |
Indicates that the process of calling the service didn't fail on the AppDaemon side. It could still have failed on the Home Assistant side. |
TIMEOUT |
The service call timed out while waiting for a response from Home Assistant. This can happen if the
ws_timeout is set too low or if Home Assistant is overloaded. |
TERMINATING |
Indicates that the task for the service call was cancelled while it was waiting for a response from Home Assistant. |
Revealed Errors
With service calls now returning values, it's possible for operations that were silently failing before to now produce warnings or errors. In most cases, this is beneficial/desired, but these can also be suppressed. For example, Z-Wave devices are known to take a long time to respond, which can cause timeouts. However, most services return nearly instantly.
These timeouts determine how long AppDaemon will wait for a response from Home Assistant before giving up and returning
with a TIMEOUT for ad_status in the result dict.
| Timeout | Explanation |
|---|---|
hass_timeout |
Provided with the service call and only affects that specific call. |
ws_timeout |
Provided in appdaemon.yaml and used as the default for all service calls. Can be overridden by
hass_timeout. |
internal_function_timeout |
Provided in appdaemon.yaml and controls how long the app will internally wait for a response from the main
AppDaemon thread. |
Setting the state of an entity only changes how it appears in Home Assistant, which is perfect for sensors, but not
devices like lights. To physically turn on a light, you should call the light/turn_on service. Merely setting the
state will not do that.
The Hass plugin registers the initial set of services immediately after it authenticates with Home Assistant.
Afterwards, AppDaemon will register services whenever Home Assistant emits service_registered events. This makes all
of the services/actions in Home Assistant available to AppDaemon apps. Users can still register their own services from
apps using the register_service method.
The Hass API ultimately wraps the calling a service action feature of the websocket API, so anything that can be done through that API, can be done with AppDaemon. Successfully calling the service action merely depends on formatting the arguments to :py:meth:`call_service <appdaemon.plugins.hass.hassapi.Hass.call_service>` correctly.
Here's one example
from appdaemon.plugins.hass import Hass
class MyApp(Hass):
def initialize(self) -> None:
self.call_service(
"notify/alexa_media",
service_data={
"target": "media_player.tom_office",
"data": {"type": "announce"}
},
# other kwargs also will be included in service_data
message="This is a test message"
)// JSON sent to Home Assistant over websocket API
{
"type": "call_service",
"domain": "notify",
"service": "alexa_media",
"service_data": {
"target": "media_player.tom_office",
"data": {
"type": "announce"
},
"message": "This is a test message"
},
"id": 7 // This ID is generated by AppDaemon and is used to match the response whenever it arrives
}The kwarg target can't be used directly because it would conflict with target being used in other services, like
this:
from appdaemon.plugins.hass import Hass
class SimpleApp(Hass):
def initialize(self) -> None:
self.call_service("light/turn_on", target="light.kitchen", brightness=255)// JSON sent to Home Assistant over websocket API
{
"type": "call_service",
"domain": "light",
"service": "turn_on",
"service_data": {
"brightness": 255
},
"target": "light.kitchen",
"id": 7 // This ID is generated by AppDaemon and is used to match the response whenever it arrives
}Some services require a complex set of data, which can require some tinkering to format correctly. There are a few techniques that are useful for determining how that data should be formatted, starting with looking at the log text. Unless suppressed, AppDaemon will log warnings for any failed service calls. These warnings will have whatever message Home Assistant returned with the error, which is often helpful.
Beyond that, it's possible to inspect the services more closely by using the :py:meth:`get_service_info <appdaemon.plugins.hass.hassapi.Hass.get_service_info>` method. This returns a nested dict of information about the service, which comes from a call to get_services that AppDaemon does internally. This contains a little more detail than is visible in the Home Assistant developer tools.
from appdaemon.plugins.hass import Hass
class MyApp(Hass):
def initialize(self):
service = "climate/set_temperature"
self.log_service_details(service)
def log_service_details(self, service: str) -> None:
if service_info := self.get_service_info(service):
self.log(f"{service}: {service_info['name']}")
for field, info in service_info['fields'].items():
self.log(f" {field}: {info['description']}")
match info:
case {'selector': {'number': {'min': min_value, 'max': max_value}}}:
self.log(f" {min_value} to {max_value}")
case {'selector': {'select': {'options': options}}}:
self.log(f" {', '.join(options)}")
case _:
passINFO my_app: climate/set_temperature: Set target temperature
INFO my_app: temperature: The temperature setpoint.
INFO my_app: 0 to 250
INFO my_app: target_temp_high: The max temperature setpoint.
INFO my_app: 0 to 250
INFO my_app: target_temp_low: The min temperature setpoint.
INFO my_app: 0 to 250
INFO my_app: hvac_mode: HVAC operation mode.
INFO my_app: off, auto, cool, dry, fan_only, heat_cool, heat
Turning up the logging level to DEBUG to inspect the raw JSON being sent over the websocket.
# conf/appdaemon.yaml
appdaemon:
... # rest of AppDaemon config here
module_debug:
HASS: DEBUG # This must match the name of the plugin
plugins:
HASS: # This is the name of the plugin, which can be changed
type: hass # This must be exactly "hass"
... # rest of Hass plugin config herePython got a :py:ref:`match/case structure in v3.10 <match>`, which has some very convenient patterns for handling the
results returned by service calls. See :py:ref:`this tutorial <tut-match>` for more information about the different ways
to use it. For example, this service call will fail because it has bogus_arg=42, which isn't allowed by the
light/turn_on service in Home Assistant.
from appdaemon.plugins.hass import Hass
from appdaemon.utils import format_timedelta
class MyApp(Hass):
def initialize(self):
service = "climate/set_temperature"
res = self.call_service(service, entity_id="climate.living_room", bogus_arg=42)
match res:
case {'success': True}:
self.log("Service call was successful")
case {'success': False, 'ad_status': 'OK', 'ad_duration': duration}:
time_str = format_timedelta(duration)
self.log(f"Service call failed on the Home Assistant side, and took {time_str}")
case _:
self.log(f"Unexpected response format from service call: {res}")WARNING HASS: Error with websocket result: invalid_format: must contain at least one of temperature, target_temp_high, target_temp_low.
INFO my_app: Service call failed on the Home Assistant side, and took 8.583ms
By default, AppDaemon will log warnings for any service call that fails. If you prefer to do error checking yourself on
a per-call basis you can set the suppress_log_messages argument to True when using
:py:meth:`call_service <appdaemon.plugins.hass.hassapi.Hass.call_service>`, or you can suppress log messages globally by
setting suppress_log_messages to true in the plugin configuration.
from appdaemon.plugins.hass import Hass
class MyApp(Hass):
def initialize(self):
service = "climate/set_temperature"
res = self.call_service(
service,
entity_id="light.kitchen",
bogus_arg=42,
suppress_log_messages=True,
)
match res:
case {'success': True}:
self.log("Service call was successful")
case {'success': False, 'ad_status': 'OK', 'error': error}:
self.handle_error(service, **error)
case _:
self.log(f"Unexpected response format from service call: {res}")
def handle_error(self, service: str, code: str, message: str) -> None:
self.log(f"Service call failed: {message}", level="ERROR")
match code:
case "invalid_format":
if service_info := self.get_service_info(service):
valid_fields = service_info.get("fields", {})
field_names = "\n".join(f" - {f}" for f in valid_fields)
self.log(
f"The service call had an invalid format. "
f"Valid fields are:\n{field_names}",
level="ERROR",
)
case _:
self.log(f"Unhandled error: {code}: {message}", level="ERROR")ERROR my_app: Service call failed: must contain at least one of temperature, target_temp_high, target_temp_low.
ERROR my_app: The service call had an invalid format. Valid fields are:
- temperature
- target_temp_high
- target_temp_low
- hvac_mode
Home Assistant has a powerful templating engine that can be used to render templates in your apps. The Hass API provides access to this with the :py:meth:`render_template <appdaemon.plugins.hass.hassapi.Hass.render_template>` method.
Home Assistant exposes a large number of WebSocket API message types beyond service calls. These include querying history, accessing long-term statistics, reading logbook events, and interacting with per-user server-side storage. The :py:meth:`call_ws <appdaemon.plugins.hass.hassapi.Hass.call_ws>` method provides a general purpose escape hatch to send any arbitrary WebSocket message to Home Assistant.
Unlike :py:meth:`call_service <appdaemon.plugins.hass.hassapi.Hass.call_service>`, which is specific to HA service
actions, call_ws accepts a raw message dict with a type key and forwards it directly over the WebSocket
connection. The id field is managed automatically by AppDaemon.
from appdaemon.plugins.hass import Hass
class MyApp(Hass):
async def initialize(self):
# Read per-user data from HA's server-side storage
result = self.call_ws({
"type": "frontend/get_user_data",
"key": "my_app",
})
match result:
case {"success": True, "result": {"value": value}}:
self.log(f"Loaded stored data: {value}")
case {"success": True}:
self.log("No data stored yet, initializing...")
self.call_ws({
"type": "frontend/set_user_data",
"key": "my_app",
"value": {"version": 1, "entries": []},
})The response format is consistent with
:py:meth:`call_service <appdaemon.plugins.hass.hassapi.Hass.call_service>` — the returned dict includes success,
result, ad_status, and ad_duration fields. Error handling follows the same patterns described in the
Error Handling section above.
Note
The call_ws method is a general purpose tool — it does not validate the message contents beyond requiring a
type key. Refer to the Home Assistant WebSocket API documentation for the expected message format for each message type.
.. autoclass:: appdaemon.plugins.hass.hassapi::Hass
:members:
:member-order: bysource




