Files
John Ogle 14fadbe44e Add custom mode tutorial and update docs for pure blueprint architecture
Add ADDING_MODES.md tutorial covering:
- UI-created modes (recommended) and package-defined modes
- Complete schema reference with nested al_config
- Behavior types (adaptive_lighting, scene, script)
- Manual control support and advanced examples

Update all documentation to reflect:
- Zero YAML editing for room setup (UI-driven workflow)
- Convention-based mode lookup pattern
- Pure blueprint architecture (3 core + 3 optional blueprints)
- Extensibility via helpers instead of template copying
2025-12-21 17:44:47 -08:00

15 KiB

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Repository Overview

This is a Home Assistant blueprints repository containing automation blueprints and a global package for Adaptive Lighting mode management. The repository uses a pure blueprint architecture with convention-based entity lookup for extensibility.

Architecture

Blueprint Structure

  • 3 core blueprints for Adaptive Lighting mode system (mode application + 2 weekend mode blueprints)
  • 3 optional blueprints for Inovelli switch integration and presence-based automation
  • Convention-based entity lookup: Blueprints dynamically discover mode settings using naming convention (adaptive_lighting_settings_{{mode}})
  • Optional input handling: Blueprints use default: {} pattern for optional inputs

Package Structure

  • 1 global package with 8 mode definitions (adaptive_lighting_global.yaml)
  • Nested JSON schema: Each mode contains behavior, manual_control, led_color, and al_config fields
  • No room templates: Room configuration done entirely via Home Assistant UI

User Workflow

  • Zero YAML editing for room setup
  • Create helpers via Settings → Helpers (dropdowns, toggles, text inputs)
  • Import blueprints via Settings → Blueprints
  • Create automations from blueprints via UI
  • Add custom modes by creating helpers following naming convention

Core Blueprints

Apply Lighting Mode Blueprint (apply_lighting_mode.yaml)

Purpose: Data-driven mode application using convention-based entity lookup

Key features:

  • Convention-based entity lookup: adaptive_lighting_settings_{{mode | lower | replace(' ', '_')}}
  • Behavior polymorphism: Supports "adaptive_lighting", "scene", and "script" behaviors
  • Manual control support: Top-level manual_control attribute controls AL manual control state
  • Optional LED color integration for Inovelli switches
  • Mode: restart for debouncing rapid state changes

Architecture patterns:

  • Dynamic entity ID construction from mode name
  • Nested al_config extraction to avoid service validation errors
  • Conditional execution based on behavior type
  • Optional input handling with default: {}

Weekend Mode Schedule Blueprint (weekend_mode_schedule.yaml)

Purpose: Auto-enable/disable weekend mode based on day of week

Key features:

  • Single automation combines enable + disable logic using choose action
  • Time trigger checks day of week
  • Toggles input_boolean based on weekday condition
  • Mode: single to prevent overlapping executions

Weekend Mode Apply Settings Blueprint (weekend_mode_apply_settings.yaml)

Purpose: Apply AL adjustments when weekend mode toggles

Key features:

  • Delayed sunrise time for sleeping in on weekends
  • Extended sunset time for staying up later
  • Reduced max brightness
  • Reset to defaults when weekend mode turns off
  • Triggers on boolean state change + HA restart
  • Mode: restart for debouncing

Development Workflow

Adding New Modes

Two methods:

  1. UI-created modes (recommended):

    • Create input_text helper via Settings → Helpers
    • Follow naming convention: input_text.adaptive_lighting_settings_{{mode}}
    • Use nested schema: {"behavior":"adaptive_lighting","manual_control":false,"led_color":170,"al_config":{...}}
    • No restart required
    • Reference: ADDING_MODES.md tutorial
  2. Package-defined modes (version controlled):

    • Edit adaptive_lighting_global.yaml
    • Add new input_text entity following existing patterns
    • Use nested schema with behavior, manual_control, led_color, al_config
    • Reload YAML configuration

Key principle: Modes are discovered by convention, not hardcoded. Adding a mode doesn't require blueprint changes.

Blueprint Testing

Manual testing workflow (no automated testing framework for HA blueprints):

  1. Import blueprint to Home Assistant
  2. Create test helpers via UI
  3. Create automation from blueprint
  4. Test all input combinations
  5. Verify automation traces for debugging
  6. Check Home Assistant logs for errors

Blueprint Best Practices

  • Convention-based entity naming for extensibility (modes, devices)
  • Optional input handling with default: {} for flexible configuration
  • Mode: restart for debouncing state changes
  • Mode: single for preventing overlapping executions
  • Nested JSON schema to satisfy service validation constraints
  • Integration filters (domain: switch, integration: adaptive_lighting) for user-friendly entity selection
  • Use descriptive names and clear descriptions for inputs
  • Provide sensible defaults for optional parameters
  • Include comprehensive documentation in blueprint metadata

Home Assistant Blueprint Format

Blueprints follow this structure:

blueprint:
  name: Blueprint Name
  description: Clear description of what this blueprint does
  domain: automation

input:
  input_name:
    name: User-Facing Name
    description: Clear description of this input
    default: {}  # For optional inputs
    selector:
      entity:
        domain: switch
        integration: adaptive_lighting  # Optional filter

mode: restart  # or single, queued, parallel
max_exceeded: silent

trigger:
  - platform: state
    entity_id: !input input_name

variables:
  var_name: !input input_name
  derived_var: "{{ template_expression }}"

action:
  - service: some.service
    target:
      entity_id: "{{ var_name }}"
    data:
      setting: "{{ derived_var }}"

Input Selectors

Common selector types used in this repository:

Entity selectors:

selector:
  entity:
    domain: input_select  # Dropdown helpers
    # or
    domain: input_boolean  # Toggle helpers
    # or
    domain: switch
    integration: adaptive_lighting  # Filter by integration
    # or
    domain: number  # Number entities (LED color)

Time selector:

selector:
  time:  # HH:MM:SS time picker

Number selector:

selector:
  number:
    min: 1
    max: 100
    step: 1
    unit_of_measurement: "%"

Action selector:

selector:
  action:  # Full action sequence builder

Mode Settings Schema

Each mode uses this nested JSON structure:

{
  "behavior": "adaptive_lighting",  // or "scene", "script"
  "manual_control": false,  // Controls AL manual control state
  "led_color": 170,  // 0-255 hue value for Inovelli LED
  "al_config": {  // Nested object for AL settings
    "min_brightness": 20,
    "max_brightness": 80,
    "min_color_temp": 2000,
    "max_color_temp": 4000,
    "transition": 5
  }
}

Why nested?: The adaptive_lighting.change_switch_settings service performs strict key validation, rejecting unknown keys. The nested structure isolates AL-specific settings in al_config, while metadata like behavior and led_color remain at the top level.

Manual control: The manual_control attribute (defaults to false) controls whether AL manual control is enabled for the mode. When true, AL is paused and won't adjust lights. The blueprint always calls adaptive_lighting.set_manual_control with this value.

File Organization

/
├── blueprints/
│   └── automation/
│       ├── apply_lighting_mode.yaml               # Core: Data-driven mode application
│       ├── weekend_mode_schedule.yaml             # Core: Auto-enable/disable weekend mode
│       ├── weekend_mode_apply_settings.yaml       # Core: Apply weekend AL adjustments
│       ├── inovelli_mode_cycling.yaml             # Optional: Config button mode cycling
│       ├── inovelli_button_actions.yaml           # Optional: Multi-tap button actions
│       ├── presence_mode_reset.yaml               # Optional: Auto-reset on room exit
│       └── occupancy_controlled_lights.yaml       # Standalone: Occupancy-based lighting
├── packages/
│   └── adaptive_lighting_global.yaml              # Global mode definitions (8 modes)
├── hardware/
│   └── inovelli/
│       └── blue-dimmer-2-in-1-vzt31.md           # Inovelli Blue Dimmer documentation
├── README.md                                      # Main documentation
├── PACKAGE_SETUP_GUIDE.md                        # Global package installation
├── ROOM_CONFIGURATION_GUIDE.md                   # UI-driven room setup
├── ADDING_MODES.md                               # Custom mode creation tutorial
└── CLAUDE.md                                     # This file

Home Assistant Blueprint Conventions

Input Types

  • entity: Entity selector for picking Home Assistant entities
  • action: Action sequence selector for automation actions
  • number: Numeric input with min/max/step validation
  • boolean: True/false toggle
  • text: String input fields
  • time: Time picker (HH:MM:SS format)

Template Usage

  • Use !input to reference blueprint inputs
  • Template conditions with {{ }} syntax for dynamic logic
  • Variable assignment and reference within action sequences
  • Jinja2 filters for string manipulation (lower, replace, from_json)

Automation Modes

  • restart: Stops current execution and restarts on new trigger (used for debouncing)
  • single: Ignores new triggers while running
  • parallel: Allows multiple simultaneous executions
  • queued: Queues triggers and executes sequentially

Pattern References

Optional input handling (presence_mode_reset.yaml:98-116):

input:
  optional_entity:
    default: {}
    selector:
      entity:
        domain: input_select

# Later in action:
- if:
    - condition: template
      value_template: "{{ optional_entity not in [none, {}, ''] }}"
  then:
    - service: some.service
      target:
        entity_id: "{{ optional_entity }}"

Integration filter (inovelli_button_actions.yaml:36-42):

selector:
  entity:
    domain: switch
    integration: adaptive_lighting

Choose action pattern (for behavior polymorphism):

- choose:
    - conditions: "{{ behavior == 'adaptive_lighting' }}"
      sequence:
        - service: adaptive_lighting.change_switch_settings
          data: "{{ al_config }}"

    - conditions: "{{ behavior == 'scene' }}"
      sequence:
        - service: scene.turn_on
          target:
            entity_id: "{{ settings.scene_entity }}"

    - conditions: "{{ behavior == 'script' }}"
      sequence:
        - service: script.turn_on
          target:
            entity_id: "{{ settings.script_entity }}"

Import URLs

Blueprints are hosted at:

  • Base URL: https://git.johnogle.info/johno/home-assistant-blueprints/raw/branch/main/
  • Pattern: {base_url}/blueprints/automation/{blueprint_name}.yaml

Example: https://git.johnogle.info/johno/home-assistant-blueprints/raw/branch/main/blueprints/automation/apply_lighting_mode.yaml

Design Principles

Convention Over Configuration

Key insight: Extend the blueprint once to add a capability, then use that capability unlimited times via helpers.

Examples:

  • Want 100 adaptive_lighting modes? Create 100 helpers following naming convention (no blueprint changes)
  • Want scene-based modes? Blueprint already supports it (no changes needed)
  • Want custom "Bowling" mode? Create one helper with correct entity ID (no changes needed)

This is the power of convention-based design.

Zero YAML Editing for Users

Room configuration workflow:

  1. Create helpers via UI (dropdowns, toggles)
  2. Create automations from blueprints via UI
  3. Test by switching modes via UI
  4. Add custom modes by creating text helpers via UI

No template copying, no YAML editing, no search/replace, no package files per room.

Nested Schema for Service Validation

Why nested al_config?

  • Home Assistant services perform strict validation
  • Unknown keys cause errors
  • Nested structure separates metadata from service parameters
  • Only al_config is passed to adaptive_lighting.change_switch_settings
  • Top-level fields (behavior, manual_control, led_color) used by blueprint logic

Behavior Polymorphism

Modes can have different behaviors:

  • adaptive_lighting: Apply AL settings (default)
  • scene: Activate Home Assistant scene
  • script: Run Home Assistant script

This enables complex use cases (movie scenes, light shows) without modifying blueprint core logic.

Migration Notes

No migration needed - repository contains only blueprints and one global package, no deployed room configurations.

For future users: If this becomes public, users would migrate from template packages to blueprint + UI workflow by:

  1. Creating helpers via UI matching their room names
  2. Importing blueprints
  3. Creating automations from blueprints
  4. Deleting old package files
  5. Reloading YAML configuration

Performance Considerations

No performance concerns:

  • Automations trigger only on mode changes (user-initiated, infrequent)
  • JSON parsing is fast and cached by Home Assistant
  • Convention-based lookup is simple string templating
  • No polling, no external services, no heavy computation
  • Mode: restart prevents rapid-fire executions

Common Issues and Solutions

Service Validation Errors

Symptom: "extra keys not allowed" in logs

Cause: Passing top-level fields to adaptive_lighting.change_switch_settings

Solution: Only pass al_config nested object to service

Missing Mode Entities

Symptom: Blueprint fails to find mode settings

Cause: Entity ID doesn't match naming convention

Solution: Verify entity ID follows pattern input_text.adaptive_lighting_settings_{{mode | lower | replace(' ', '_')}}

LED Color Not Updating

Symptom: Mode changes but LED stays same color

Causes:

  1. LED entity not configured (left empty)
  2. Incorrect entity ID
  3. Inovelli switch firmware issue

Solution: Check automation configuration, verify entity ID, test LED manually

Testing Guidelines

Since Home Assistant blueprints have no automated testing framework, all testing is manual:

  1. Blueprint Import: Verify imports without errors in HA
  2. Helper Creation: Create test helpers via UI
  3. Automation Creation: Create test automations from blueprints
  4. Mode Switching: Test all mode combinations
  5. Custom Modes: Create custom mode and verify convention-based lookup
  6. LED Integration: Verify LED colors update (if configured)
  7. Weekend Mode: Test schedule and AL adjustments
  8. HA Restart: Verify persistence across restarts
  9. Error Handling: Test missing entities, invalid JSON, etc.
  10. Logs: Check for errors in Settings → System → Logs

Always check automation traces (Settings → Automations → [automation] → Traces) for debugging.

Key Differences from Traditional Approach

Old approach (template packages):

  • Copy package file per room
  • Search/replace entity IDs
  • Edit YAML to add modes
  • Hardcoded choose blocks for each mode
  • Adding mode requires editing every room

New approach (pure blueprints):

  • Create helpers via UI
  • Create automations via UI
  • Zero YAML editing for rooms
  • Convention-based mode lookup
  • Adding mode = create ONE helper

Benefits:

  • Lower friction for users
  • No YAML syntax errors from manual editing
  • Extensible without code changes
  • Easier to maintain
  • Better user experience

Resources