Skip to content

Configuration

This page covers how to migrate AppDaemon configuration files to Hassette's hassette.toml and typed AppConfig models.

Overview

AppDaemon uses two YAML files:

  • appdaemon.yaml — global settings (timezone, HA connection details)
  • apps.yaml — per-app settings (module, class, arguments)

Hassette uses a single hassette.toml for everything. App arguments are defined as typed Pydantic models instead of raw dictionaries.

Global Configuration

AppDaemon (appdaemon.yaml)

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 (hassette.toml)

[hassette]
base_url = "http://192.168.1.179:8123"
# Token read from HASSETTE__TOKEN env var or .env file

[apps.my_app]
filename = "my_app.py"
class_name = "MyApp"

# [[double brackets]] = TOML array-of-tables; supports running the same app with multiple configs
[[apps.my_app.config]]
entity = "light.kitchen"
brightness = 200

The Home Assistant token is read from the HASSETTE__TOKEN environment variable or from a .env file — it is not stored in hassette.toml.

Per-App Configuration

AppDaemon (apps.yaml)

my_app:
  module: my_app
  class: MyApp
  args:
    entity: light.kitchen
    brightness: 200

Arguments are accessible in the app via self.args["args"]["entity"] — 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 (hassette.toml + AppConfig)

[hassette]
base_url = "http://127.0.0.1:8123"

[apps.my_app]
filename = "my_app.py"
class_name = "MyApp"

[[apps.my_app.config]]
entity = "light.kitchen"
brightness = 200

In Hassette, you define a subclass of AppConfig to declare the expected parameters with types and defaults. You access configuration via the typed self.app_config attribute:

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'>)

Migration Steps

Convert your appdaemon.yaml and apps.yaml to a single hassette.toml:

# appdaemon.yaml
appdaemon:
  plugins:
    HASS:
      type: hass
      ha_url: http://192.168.1.179:8123
      token: !env_var HOME_ASSISTANT_TOKEN

# apps.yaml
my_app:
  module: my_app
  class: MyApp
  args:
    entity: light.kitchen
    brightness: 200
[hassette]
base_url = "http://192.168.1.179:8123"
# Token read from HASSETTE__TOKEN env var or .env file

[apps.my_app]
filename = "my_app.py"
class_name = "MyApp"

# [[double brackets]] = TOML array-of-tables; supports running the same app with multiple configs
[[apps.my_app.config]]
entity = "light.kitchen"
brightness = 200

[[double brackets]] — TOML array-of-tables

The [[apps.my_app.config]] syntax uses TOML array-of-tables, which means you can repeat it to run the same app class with multiple independent configurations (for example, one instance per room). Change [[...]] to [...] and you get a single-item table — the double brackets signal a list.

Then replace dictionary access with typed config access:

def initialize(self):
    entity = self.args["args"]["entity"]
    brightness = self.args["args"]["brightness"]
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)

class MyApp(App[MyAppConfig]):
    async def on_initialize(self):
        entity = self.app_config.entity
        brightness = self.app_config.brightness

Benefits of Typed Configuration

  • Validation at startup — missing required fields raise a clear error before your app runs, not at runtime when a handler fires
  • IDE autocompleteself.app_config.entity gets type hints and autocomplete in any IDE with type-checking support
  • Default values — use Field(default_value) to declare defaults; Hassette applies them if the toml omits the key
  • Constraints — Pydantic validators like ge=0, le=255 catch invalid values before your app starts

See Also