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:
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'sself.get_state()behavior.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.
Hassette: State Cache (recommended)
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
- API Overview — the full API reference
- Entities & States — typed entity state access
- Services — calling HA services
- States — state cache and state models