Configuration
AppDaemon splits configuration across two YAML files: appdaemon.yaml for global settings and apps.yaml for per-app arguments. Hassette uses a single hassette.toml for everything, and replaces raw argument dictionaries with typed AppConfig models — AppConfig is a class you define once per app, declaring each setting's name and type; Hassette fills it from hassette.toml and hands it to your app as self.app_config.
Global Configuration
appdaemon:
time_zone: America/Chicago
latitude: 51.725
longitude: 14.3434
elevation: 0
use_dictionary_unpacking: true
plugins:
HASS:
type: hass
ha_url: http://192.168.1.179:8123
token: !env_var HOME_ASSISTANT_TOKEN
[hassette]
base_url = "http://192.168.1.179:8123"
# Token read from HASSETTE__TOKEN env var or .env file
[hassette.apps.my_app]
filename = "my_app.py"
class_name = "MyApp"
# [[double brackets]] = TOML array-of-tables; supports running the same app with multiple configs
[[hassette.apps.my_app.config]]
entity = "light.kitchen"
brightness = 200
The Home Assistant token is read from the HASSETTE__TOKEN environment variable or a .env file. It does not go in hassette.toml.
Per-App Configuration
This is the bigger change. In AppDaemon, you declare app arguments in apps.yaml and read them through a nested dictionary. In Hassette, arguments go in hassette.toml and you define an AppConfig subclass to describe them.
my_app:
module: my_app
class: MyApp
args:
entity: light.kitchen
brightness: 200
Arguments are accessible via self.args["args"]["key"], a nested dictionary with no type information:
from appdaemon.plugins.hass import Hass
class MyApp(Hass):
def initialize(self):
self.log(f"{self.args=}")
entity = self.args["args"]["entity"]
brightness = self.args["args"]["brightness"]
self.log(f"My configured entity is {entity!r} (type {type(entity)})")
self.log(f"My configured brightness is {brightness!r} (type {type(brightness)})")
# 2025-10-13 18:59:04.820599 INFO my_app: self.args={'name': 'my_app', 'config_path': PosixPath('./apps.yaml'), 'module': 'my_app', 'class': 'MyApp', 'args': {'entity': 'light.kitchen', 'brightness': 200}}
# 2025-10-13 18:40:23.676650 INFO my_app: My configured entity is 'light.kitchen' (type <class 'str'>)
# 2025-10-13 18:40:23.677422 INFO my_app: My configured brightness is 200 (type <class 'int'>)
[hassette]
base_url = "http://127.0.0.1:8123"
[hassette.apps.my_app]
filename = "my_app.py"
class_name = "MyApp"
[[hassette.apps.my_app.config]]
entity = "light.kitchen"
brightness = 200
You define a subclass of AppConfig to declare each parameter with a type and optional default. Access configuration through self.app_config:
from pydantic import Field
from hassette import App, AppConfig
class MyAppConfig(AppConfig):
entity: str = Field(..., description="The entity to monitor")
brightness: int = Field(100, ge=0, le=255, description="Brightness level (0-255)")
class MyApp(App[MyAppConfig]):
async def on_initialize(self):
self.logger.info("app_manifest=%r", self.app_manifest)
self.logger.info("app_config=%r", self.app_config)
entity = self.app_config.entity
self.logger.info("My configured entity is %r (type %s)", entity, type(entity))
brightness = self.app_config.brightness
self.logger.info("My configured brightness is %r (type %s)", brightness, type(brightness))
# 2025-10-13 18:57:45.495 INFO hassette.MyApp.0.on_initialize:13 - self.app_manifest=<AppManifest MyApp (MyApp) - enabled=True file=my_app.py>
# 2025-10-13 18:57:45.495 INFO hassette.MyApp.0.on_initialize:14 - self.app_config=MyAppConfig(instance_name='MyApp.0', log_level='INFO', entity='light.kitchen', brightness=200)
# 2025-10-13 18:57:45.495 INFO hassette.MyApp.0.on_initialize:17 - My configured entity is 'light.kitchen' (type <class 'str'>)
# 2025-10-13 18:57:45.495 INFO hassette.MyApp.0.on_initialize:19 - My configured brightness is 200 (type <class 'int'>)
Missing required fields raise a validation error at startup, before any handler runs — the error names the missing key, so a failed config migration surfaces immediately rather than as a KeyError mid-automation. self.app_config.entity carries a type your IDE can check.
[[double brackets]]: TOML array-of-tables
[[hassette.apps.my_app.config]] is a TOML array-of-tables. You can repeat the block to run the same app class with multiple independent configurations. Use [...] for a single instance; use [[...]] when you want a list.
Multi-Instance Apps
To run the same class in multiple rooms, add another [[hassette.apps.my_app.config]] block:
[hassette.apps.motion_lights]
filename = "motion_lights.py"
class_name = "MotionLights"
[[hassette.apps.motion_lights.config]]
motion_sensor = "binary_sensor.living_room_motion"
light = "light.living_room"
off_delay = 300
[[hassette.apps.motion_lights.config]]
motion_sensor = "binary_sensor.bedroom_motion"
light = "light.bedroom"
off_delay = 120
Each block becomes a separate app instance. Both run the same MotionLights class with different config values, and each instance's self.app_config holds the values from its own block — the Python class needs no changes. See App Configuration for the full reference.
See Also
- App Configuration: app registration, multi-instance config, and TOML syntax
- Configuration Overview:
hassette.tomlstructure