agentsclimarketplace

Ha automation tdd

Skill jsnyder/homeassist/skills/ha-automation-tdd

Use when implementing Home Assistant automations - test YAML automations using pyscript as a test harness with script-based architectureFrom its SKILL.md

Install
npx -y skills add jsnyder/homeassist --skill ha-automation-tdd

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

SKILL.md

4.7 KB, as published. Nobody here has run it

Home Assistant Automation TDD

Test YAML automations using pyscript as a test harness.

Core Principles

  1. Scripts contain testable logic — automations are thin trigger wrappers
  2. Never use automation.trigger — it doesn't populate trigger.* variables
  3. Explicit inputs via fields: — no global state reads inside scripts
  4. Every branch emits observation — test mode observation for verification

Architecture

Script (testable logic)    ← receives explicit inputs via fields:
    ↑
Automation (thin wrapper)  ← wires trigger → script with data mapping
    ↑
Trigger (state change, time, event)

When to Use

Use TDD for: conditional logic, state machines, HVAC/presence control, long-lived automations.

Skip for: single-action automations, UI-created, temporary/experimental.

Implementation Steps

1. Write the Script (testable logic)

script:
  my_automation_logic:
    alias: "My Automation Logic"
    fields:
      sensor_state: { description: "Current sensor state" }
      threshold: { description: "Threshold value" }
    sequence:
      - choose:
          - conditions:
              - condition: template
                value_template: "{{ sensor_state == 'on' and threshold > 50 }}"
            sequence:
              - service: light.turn_on
                target: { entity_id: light.living_room }
              - if:
                  - condition: state
                    entity_id: input_boolean.pyscript_test_mode
                    state: "on"
                then:
                  - service: input_text.set_value
                    target: { entity_id: input_text.aut_test_observed }
                    data: { value: "branch_1_executed" }
        default:
          - if:
              - condition: state
                entity_id: input_boolean.pyscript_test_mode
                state: "on"
            then:
              - service: input_text.set_value
                target: { entity_id: input_text.aut_test_observed }
                data: { value: "default_branch" }

2. Write the Automation Wrapper

automation:
  - id: 'my_automation_trigger'
    alias: 'My Automation: Trigger'
    mode: single
    trigger:
      - platform: state
        entity_id: binary_sensor.motion
        to: 'on'
    action:
      - service: script.my_automation_logic
        data:
          sensor_state: "{{ states('binary_sensor.motion') }}"
          threshold: "{{ states('sensor.threshold') | int }}"

3. Write Tests

# pyscript/test_my_automation.py
@service
def run_my_automation_tests():
    service.call("pyscript", "reset_test_counters")
    test_branch_1()
    test_default()
    task.sleep(1)
    service.call("pyscript", "get_test_summary")

def test_branch_1():
    service.call("pyscript", "run_script_test",
        script_id="my_automation_logic",
        scenario_name="on + threshold 60 → branch 1",
        script_data={"sensor_state": "on", "threshold": 60},
        expected_branch="branch_1_executed",
        timeout_seconds=5.0)

4. Run and Verify

homeassist services call pyscript.run_my_automation_tests
homeassist entities get counter.aut_test_pass --compact
homeassist entities get counter.aut_test_fail --compact

Red-Green-Refactor Cycle

  1. RED — Write test with expected branch → fails (script doesn't exist)
  2. GREEN — Create script with choose: + observations → passes
  3. REFACTOR — Clean up, keep tests green

Prerequisites

Test infrastructure package must be deployed:

# packages/pyscript_test_infrastructure.yaml
input_boolean:
  pyscript_test_mode: { name: "PyScript Test Mode" }
input_text:
  aut_test_observed: { name: "Automation Test Observation", max: 255 }
counter:
  aut_test_pass: { name: "Tests Passed" }
  aut_test_fail: { name: "Tests Failed" }
  aut_test_total: { name: "Tests Run" }

Plus the test runner at pyscript/automation_test_runner.py.

Anti-Patterns

Don'tDo Instead
automation.trigger for testingCall script with explicit inputs
Read global state in scriptPass via fields: parameters
task.sleep() for waitingtask.wait_until() with state trigger
Skip test observationEvery branch emits observation when test mode on

Completion Checklist

  • Script has explicit fields: for all inputs
  • Every choose: branch has test observation
  • Default branch has test observation
  • Thin automation wrapper calls script with explicit data
  • Test file covers all branches
  • All tests pass

Keep looking

Skills are one crate of 627,801. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.