API Calls
Coming from synchronous AppDaemon?
Every Hassette lifecycle method and handler is async def, and calls to self.api need await in front — await pauses the handler until the network call finishes. Reads from self.states (the local cache) are plain synchronous calls. Migration Concepts covers the model.
Getting Entity State
AppDaemon's self.get_state() reads from an internal cache and returns raw strings or dicts. Hassette provides two options. self.states is a local cache for most reads. self.api.get_state() forces a fresh read from Home Assistant when the cache is not enough.
AppDaemon
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. No type information. No autocomplete.
Hassette: State Cache (recommended)
self.states holds a local copy of all entity states, kept current via WebSocket events. No await needed. The returned object is typed to the entity domain.
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")
Three access patterns:
| Pattern | Returns |
|---|---|
self.states.light.get("light.kitchen") |
LightState \| None |
self.states[states.LightState].get("light.kitchen") |
LightState \| None, for any domain |
for entity_id, state in self.states.light |
Iterates all cached lights |
Use self.states for any read inside a handler or scheduled task. The cache is always up-to-date.
Hassette: Direct API Call
For a guaranteed fresh read from Home Assistant:
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)
self.api.get_state() hits the HA REST API and requires await. Use it when the cache is not reliable, such as during initialization before the first state change event.
Calling Services
AppDaemon uses a single domain/service string. Hassette splits them into two arguments.
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)
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)
Don't forget await
Without await, the call appears to succeed but the service never runs — Python just hands back an unexecuted coroutine. If service calls have no effect, check that every call site has await.
Two signature differences from AppDaemon: the entity belongs in the target dict (target={"entity_id": "light.kitchen"}) rather than AppDaemon's bare entity_id= keyword, and Hassette handlers don't take **kwargs — event data arrives through typed parameters instead (see Bus & Events).
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"})
Firing Events
self.fire_event("custom_event", entity_id="sensor.test", value=42)
await self.api.fire_event("custom_event", {"entity_id": "sensor.test", "value": 42})
fire_event sends an event to Home Assistant's event bus. The event data is a dict in Hassette (AppDaemon accepts kwargs). For broadcasting between apps in the same Hassette process without leaving the framework, use self.bus.emit() instead.
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")
self.logger automatically includes the app instance name, calling method, and line number in every log line. Use %s-style formatting rather than f-strings. The string is only constructed if the log level is active.
See Also
- States, state cache and state models
- API Methods, reading state and calling services
- API Overview, full API reference