Skip to content

API Calls

This page covers how to migrate AppDaemon API access to Hassette's self.api and self.states attributes.

Overview

AppDaemon provides synchronous API access via methods on self: self.get_state(), self.call_service(), self.set_state(). Responses are raw strings or dicts.

Hassette provides two ways to access state:

  1. self.states — a local state cache that stays up-to-date via WebSocket events. This is the preferred way to read entity state and is most similar to AppDaemon's self.get_state() behavior.
  2. self.api — direct async API calls to Home Assistant. Use this for writes (call_service, set_state) and for cases where you specifically need a fresh read from HA.

Getting Entity State

AppDaemon

AppDaemon maintains an internal cache and self.get_state() reads from it. You can get just the state string or the full dict with attributes:

from appdaemon.plugins.hass import Hass


class StateGetter(Hass):
    def initialize(self):
        office_light_state = self.get_state("light.office_light_1", attribute="all")
        self.log(f"{office_light_state=}")

Returns a raw dict with string values. No type safety.

The self.states attribute provides immediate access to all entity states without API calls. It is updated automatically via state change events — no await needed:

from hassette import App, states


class StateGetter(App):
    async def on_initialize(self):
        # Access via domain-specific property (no await needed)
        office_light = self.states.light.get("light.office_light_1")

        if office_light:
            self.logger.info("Light state: %s", office_light.value)
            self.logger.info("Brightness: %s", office_light.attributes.brightness)

        # Iterate over all lights
        for entity_id, light in self.states.light:
            self.logger.info("%s: %s", entity_id, light.value)
        # Typed access for any domain
        my_light = self.states[states.LightState].get("light.office_light_1")

Access patterns:

Pattern What it returns
self.states.light.get("light.kitchen") A LightState object, or None if not found
for entity_id, state in self.states.light Iterates over all light entities in cache
self.states[states.LightState].get("light.kitchen") Typed access for any domain

Hassette: Direct API Call

For cases where you need to force a fresh read from Home Assistant (rare):

from hassette import App


class StateGetter(App):
    async def on_initialize(self):
        # Force fresh read from HA (requires await)
        office_light_state = await self.api.get_state("light.office_light_1")
        self.logger.info("office_light_state=%r", office_light_state)

Type narrowing

In Hassette, get_state() is annotated as returning BaseState. Use type narrowing or casting to tell the type checker the specific state type you expect.

When to use each approach:

  • self.states (recommended): For reading current state in event handlers, scheduled tasks, or any time you need quick access to entity state. The cache is automatically kept up-to-date via state change events.
  • self.api.get_state(): Only when you specifically need a fresh read from Home Assistant (rare) or if you're outside the normal app lifecycle.

Calling Services

def my_callback(self, **kwargs):
    self.call_service("light/turn_on", entity_id="light.kitchen", brightness=200)

    # or use the helper
    self.turn_on("light.kitchen", brightness=200)

AppDaemon uses a domain/service string format. The call is synchronous.

from hassette import App


class MyApp(App):
    async def my_callback(self):
        await self.api.call_service(
            "light",
            "turn_on",
            target={"entity_id": "light.kitchen"},
            brightness=200,
        )
        # Or use the helper
        await self.api.turn_on("light.kitchen", brightness=200)

Hassette uses separate domain and service arguments. The call is async. Helpers like turn_on() are also available.

Don't forget await

Forgetting await on an API call returns a coroutine object instead of executing the call. If your service calls appear to do nothing, check that you have await on each one.

Setting States

self.set_state("sensor.custom", state="42", attributes={"unit": "widgets"})
from hassette import App


class MyApp(App):
    async def my_callback(self):
        await self.api.set_state("sensor.custom", state=42, attributes={"unit": "widgets"})

Logging

AppDaemon provides self.log() and self.error(). Hassette uses Python's standard logging module via self.logger:

self.log("This is a log message")
self.log(f"Value: {value}")
self.error("Something went wrong")
from hassette import App


class MyApp(App):
    async def on_initialize(self):
        value = "example"

        self.logger.info("This is a log message")
        self.logger.info("Value: %s", value)
        self.logger.error("Something went wrong")

The Hassette logger automatically includes the instance name, calling method, and line number in every log line. Use %s-style formatting rather than f-strings to defer string construction until needed.

Full State Migration Example

The following example shows the complete migration of a state-reading pattern:

from hassette import App, states


class MyApp(App):
    async def on_initialize(self):
        # PREFERRED: Use local state cache (no await, no API call)
        # This is most similar to AppDaemon's behavior
        light = self.states.light.get("light.kitchen")
        if light:
            brightness = light.attributes.brightness  # Type-safe access
            value = light.value  # State value as string

        # Iterate over all lights in cache
        for entity_id, light in self.states.light:
            self.logger.info("%s: %s", entity_id, light.value)

        # Typed access for any domain
        my_light = self.states[states.LightState].get("light.kitchen")

        # ALTERNATIVE: Force fresh read from Home Assistant API
        fresh_light = await self.api.get_state("light.kitchen")
        brightness = fresh_light.attributes.brightness  # pyright: ignore[reportAttributeAccessIssue]

        # Or get just the value
        value = await self.api.get_state_value("light.kitchen")  # Returns string

See Also