Compare commits
3 Commits
4e2107f7f7
...
8fdcb128a3
| Author | SHA1 | Date | |
|---|---|---|---|
| 8fdcb128a3 | |||
| b9e9c98f7e | |||
| 6dc9b56ff7 |
+1
-1
@@ -1 +1 @@
|
|||||||
{"pid": 71, "version": 1, "ha_version": "2026.7.2", "start_ts": 1784593872.104367}
|
{"pid": 74, "version": 1, "ha_version": "2026.7.3", "start_ts": 1784711954.8835843}
|
||||||
@@ -1,820 +1,65 @@
|
|||||||
# Home Assistant OpenCode Rules
|
# Home Assistant OpenCode Rules
|
||||||
|
|
||||||
You are working directly within a Home Assistant installation. Your working directory is `/homeassistant`, which is the live Home Assistant configuration directory.
|
You are working directly inside a live Home Assistant installation (`/homeassistant`).
|
||||||
|
|
||||||
## CRITICAL: User Consent and Scope Rules
|
## Critical Mandates & Approval Rules
|
||||||
|
|
||||||
You MUST follow these rules strictly:
|
1. **Read-Only by Default**: Only read files and query APIs when investigating. Never modify files or call state-changing services without explicit user confirmation.
|
||||||
|
2. **Explicit Confirmation**: Show the exact diff or file content to be written before making any change. Wait for user approval ("yes", "go ahead").
|
||||||
1. **Never exceed the user's request** - Do exactly what the user asks, nothing more. Do not "improve" or "enhance" beyond the stated scope.
|
3. **One Change at a Time**: Apply minimal changes, report what changed, and verify before proceeding.
|
||||||
|
4. **No Unsolicited Work**: Do not refactor, clean up, or change unrelated code/files beyond the requested scope.
|
||||||
2. **Never make changes without explicit approval** - Before modifying ANY file:
|
5. **Project Context Protocol**:
|
||||||
- Show the user exactly what you plan to change
|
- Associate requests with a stable project ID in `PROJECTS.md` (e.g. `P013`).
|
||||||
- Wait for their explicit confirmation ("yes", "go ahead", "do it", etc.)
|
- Read `PROJECTS.md` before starting work. Record durable context under the project ID.
|
||||||
- If they haven't approved, DO NOT proceed
|
- Never mark a project `Complete` unless the user explicitly confirms completion. Ask: "Should I close Pxxx?"
|
||||||
|
|
||||||
3. **Ask, don't assume** - If the user's request is ambiguous:
|
## Off-Limits & Restricted Paths
|
||||||
- Ask clarifying questions first
|
|
||||||
- Present options and let them choose
|
Never read, write, or access:
|
||||||
- Never guess at their intent
|
- `.storage/`, `.cloud/`, `deps/`, `tts/`
|
||||||
|
- `home-assistant_v2.db*`, `home-assistant.log*`
|
||||||
4. **Read-only by default** - When investigating or troubleshooting:
|
- `secrets.yaml` (never expose or log secret contents)
|
||||||
- Only read files and gather information
|
|
||||||
- Present findings and recommendations
|
Use MCP tools (`get_devices`, `get_areas`, `get_entity_details`, `get_history`) or CLI tools (`hab`) instead of accessing internal storage or database files.
|
||||||
- Wait for user instruction before making any changes
|
|
||||||
|
## Configuration & Architecture Gotchas
|
||||||
5. **One change at a time** - When making approved changes:
|
|
||||||
- Make the minimum change needed
|
- **Directory Includes in `configuration.yaml`**:
|
||||||
- Show what was changed
|
- `automation`: Uses `!include_dir_merge_list automations/` -> Active automations are in `automations/`, **NOT** root `automations.yaml` (which is legacy/inactive).
|
||||||
- Let the user verify before proceeding to any next step
|
- `script`: Uses `!include_dir_merge_named scripts/` -> Active scripts are in `scripts/`, **NOT** root `scripts.yaml`.
|
||||||
|
- `group`: Uses `!include_dir_merge_named group/` -> Active groups are in `group/`, **NOT** root `groups.yaml`.
|
||||||
6. **No unsolicited modifications** - Never:
|
- `packages`: Uses `!include_dir_named packages/`.
|
||||||
- "Clean up" code the user didn't ask about
|
- `template`: Uses `!include_dir_merge_list templates/`.
|
||||||
- Add features they didn't request
|
- `sensor`: Uses `!include_dir_merge_list sensor/`.
|
||||||
- Refactor working configurations
|
|
||||||
- Fix issues they haven't mentioned
|
## CLI & Tooling Reference
|
||||||
|
|
||||||
7. **Respect "no"** - If a user declines a suggestion, do not:
|
### Shell YAML Queries (`yq`)
|
||||||
- Repeat the suggestion
|
- **Always use `yq`** (mikefarah/Go version) for CLI YAML processing. PyYAML and Ruby YAML crash on HA tags (`!include`, `!secret`).
|
||||||
- Make the change anyway
|
- Example: `yq '.homeassistant.latitude' configuration.yaml`
|
||||||
- Try to convince them otherwise
|
|
||||||
|
### Home Assistant Admin (`hab` CLI)
|
||||||
## Project Context Protocol
|
- Pre-authenticated CLI tool for managing HA via terminal (`hab <command> --json`).
|
||||||
|
- Common commands:
|
||||||
- Treat every user request as related to a tracked project.
|
- Entities & Devices: `hab entity list --domain light --json`, `hab device list --json`
|
||||||
- Identify the project by ID or name before beginning work.
|
- Automations/Scripts: `hab automation list --json`, `hab script list --json`
|
||||||
- If the project is unclear, ask the user which project applies.
|
- Dashboards: `hab dashboard list --json`, `hab dashboard get <id> --json`
|
||||||
- Read `PROJECTS.md` and the relevant project section before investigating or making recommendations.
|
- Operations: `hab system health --json`, `hab backup create`
|
||||||
- Keep project IDs stable.
|
|
||||||
- Record durable decisions, changes, verification results, blockers, and next steps under the relevant project.
|
### Zigbee Toolkit (`zigporter` CLI)
|
||||||
- Follow the existing approval rules before writing context updates.
|
- Handles cascade renames across automations, scripts, scenes, and Lovelace dashboards.
|
||||||
- Never mark a project `Complete` unless the user explicitly confirms completion.
|
- Command: `zigporter rename-entity light.old light.new --apply` (Defaults to dry-run if `--apply` omitted).
|
||||||
- When a project appears complete, ask: "Should I close Pxxx?"
|
- Note: `zigporter migrate` requires physical interaction and must NOT be run by AI.
|
||||||
- Do not store secrets, credentials, or large transient logs in project context.
|
|
||||||
|
### Safe Configuration Writing (MCP)
|
||||||
## Environment Context
|
- Prefer `write_config_safe(file_path, content, dry_run=true)` to pre-validate before writing.
|
||||||
|
- Always read the target file first and preserve all existing content when writing.
|
||||||
- You are running inside the OpenCode app
|
|
||||||
- The current directory (`/homeassistant`) contains the live Home Assistant configuration
|
## Mandatory YAML Style Rules
|
||||||
- Changes to YAML files here directly affect the Home Assistant instance
|
|
||||||
- If add-on folder access is enabled, `/addons` and `/addon_configs` are available for Home Assistant add-on development. Treat `/addon_configs` as sensitive and only inspect or modify these folders when the user explicitly asks.
|
- **Indentation**: 2 spaces (no tabs).
|
||||||
- You may have access to MCP tools for interacting with Home Assistant (check with the user)
|
- **Strings**: Double quotes (`"..."`). Exceptions: entity IDs, action names, area IDs, device classes, modes.
|
||||||
|
- **Booleans**: Lowercase `true`/`false` only. Never `on`/`off`/`yes`/`no` as YAML booleans.
|
||||||
## Home Assistant Interaction Model
|
- **Lists & Maps**: Block style only. Never flow style `[1, 2]` or `{key: val}`.
|
||||||
|
- **Action Targets**: Always use `target:` block (`target: entity_id: light.room`). Do not put `entity_id` inside `data:` or at root action level.
|
||||||
There are three primary, safe ways to interact with Home Assistant:
|
- **Templates**: Use `states('sensor.x')` and `is_state(...)` helpers. Never use direct state object access (`states.sensor.x.state`).
|
||||||
|
|
||||||
### 1. Configuration Files (YAML)
|
|
||||||
The standard way to define and customize Home Assistant behavior:
|
|
||||||
- Automations, scripts, scenes, and blueprints
|
|
||||||
- Integration and sensor configurations
|
|
||||||
- Templates, packages, and customizations
|
|
||||||
- Dashboard (Lovelace) definitions
|
|
||||||
|
|
||||||
These files are designed for user editing and are the source of truth for your Home Assistant setup.
|
|
||||||
|
|
||||||
### 2. MCP Tools (Runtime API)
|
|
||||||
Real-time interaction with the running Home Assistant instance:
|
|
||||||
- Query current entity states and history
|
|
||||||
- Control devices and call services
|
|
||||||
- Validate configurations
|
|
||||||
- Diagnose issues and detect anomalies
|
|
||||||
- Report OpenCode/HA agent capability status with `get_agent_capabilities`
|
|
||||||
|
|
||||||
### Native Home Assistant LLM Platform
|
|
||||||
Home Assistant is developing a native `llm` integration where Core integrations and custom integrations can expose curated tools through `<integration>/llm.py` and registered LLM APIs. New Home Assistant builds may also expose those APIs over native MCP endpoints such as `/api/mcp/<API ID>`; the built-in Assist API uses `/api/mcp/assist`. This is complementary to OpenCode MCP, not a replacement.
|
|
||||||
|
|
||||||
- If the optional `homeassistant_native` MCP server is available, prefer it for requests that fit the configured native Home Assistant LLM API because those tools are curated by Home Assistant.
|
|
||||||
- Use OpenCode MCP for configuration editing, safe writes, validation, admin/dev workflows, screenshots, updates, ESPHome, `hab`, Zigbee tasks, add-on development, and Home Assistant documentation lookup.
|
|
||||||
- Use `get_agent_capabilities` or `ha://agent/capabilities` to check whether the running HA instance reports the native `llm` component and native MCP endpoints.
|
|
||||||
- Use `get_home_context` for compact area/domain/entity understanding before broad state dumps.
|
|
||||||
- Use `get_ha_llm_development_guide` when helping develop or review a custom integration's native `<integration>/llm.py` provider.
|
|
||||||
- Do not assume this add-on can register tools directly with HA's native `llm` platform; native tool registration is internal to HA integrations/custom integrations. The add-on can consume configured native LLM APIs through native MCP when Home Assistant exposes them.
|
|
||||||
|
|
||||||
### 3. hab CLI (Home Assistant Builder)
|
|
||||||
A CLI tool designed for AI agents to manage Home Assistant. Run `hab` commands via the terminal:
|
|
||||||
- **Entity management**: `hab entity list`, `hab entity get light.living_room`, `hab entity logbook sensor.power --start 2h`
|
|
||||||
- **Service calls**: `hab action call light.turn_on --entity light.living_room --data '{"brightness": 200}'`
|
|
||||||
- **Automation CRUD**: `hab automation list`, `hab automation create`, `hab automation delete`
|
|
||||||
- **Dashboard management**: `hab dashboard list`, `hab dashboard view create`
|
|
||||||
- **Area/floor/zone/label**: `hab area list`, `hab area create "Kitchen"`
|
|
||||||
- **Helpers**: `hab helper list`, `hab helper create`
|
|
||||||
- **Scripts**: `hab script list`, `hab script create`
|
|
||||||
- **Scenes**: `hab scene list`, `hab scene create`, `hab scene activate "Movie Time"`
|
|
||||||
- **Blueprints**: `hab blueprint list`
|
|
||||||
- **Backups**: `hab backup list`, `hab backup create`
|
|
||||||
- **System**: `hab system info`, `hab system health`, `hab overview`
|
|
||||||
- **Devices**: `hab device list`
|
|
||||||
- **People**: `hab person list`, `hab person create`, `hab person update`
|
|
||||||
- **Categories**: `hab category list`, `hab category assign --entity light.kitchen`
|
|
||||||
- **To-do lists**: `hab todo list`, `hab todo item list todo.shopping`, `hab todo item add todo.shopping "Milk"`
|
|
||||||
- **Notifications**: `hab notification list`, `hab notification create --message "Hello" --title "Alert"`
|
|
||||||
- **Integrations**: `hab integration list`, `hab integration reload hue`, `hab integration disable mqtt`
|
|
||||||
- **Repairs**: `hab repairs list`, `hab repairs ignore <issue_id>`
|
|
||||||
- **Events**: `hab event list`, `hab event fire my_custom_event --data '{"key": "value"}'`
|
|
||||||
- **Templates**: `hab template render --expression "{{ states('sensor.temperature') }}"`
|
|
||||||
- **Search**: `hab search related`
|
|
||||||
|
|
||||||
`hab` outputs human-readable text by default. Use `--json` for structured JSON output (ideal for parsing).
|
|
||||||
`hab` is pre-authenticated via the Supervisor token - no login required.
|
|
||||||
Run `hab --help` or `hab <command> --help` for full usage details.
|
|
||||||
|
|
||||||
<!-- HAB_LIVE_HELP_START -->
|
|
||||||
```
|
|
||||||
Home Assistant Builder (hab) is a CLI utility designed for LLMs
|
|
||||||
to build and manage Home Assistant configurations.
|
|
||||||
|
|
||||||
Interactive sessions default to human-readable text. Non-interactive sessions default to JSON.
|
|
||||||
|
|
||||||
Start with 'hab guide' for workflow-level guidance optimized for LLM and agent usage.
|
|
||||||
|
|
||||||
Usage:
|
|
||||||
hab [command]
|
|
||||||
|
|
||||||
Getting Started:
|
|
||||||
auth Manage authentication
|
|
||||||
capability Inspect runtime capabilities
|
|
||||||
guide Display built-in usage guides
|
|
||||||
overview Show an overview of the Home Assistant instance
|
|
||||||
schema Show machine-readable command schema
|
|
||||||
|
|
||||||
Registry:
|
|
||||||
area Manage areas
|
|
||||||
device Manage devices
|
|
||||||
entity Manage entities
|
|
||||||
floor Manage floors
|
|
||||||
label Manage labels
|
|
||||||
person Manage persons
|
|
||||||
search Search for items and relationships
|
|
||||||
zone Manage zones
|
|
||||||
|
|
||||||
Automation:
|
|
||||||
action Call actions (services)
|
|
||||||
automation Manage automations
|
|
||||||
blueprint Manage blueprints
|
|
||||||
category Manage categories
|
|
||||||
helper Manage groups, templates, and other helpers
|
|
||||||
scene Manage scenes
|
|
||||||
script Manage scripts
|
|
||||||
|
|
||||||
Dashboard:
|
|
||||||
dashboard Manage dashboards
|
|
||||||
|
|
||||||
Other:
|
|
||||||
backup Manage backups
|
|
||||||
calendar Manage calendar events
|
|
||||||
diagnostics Manage diagnostics handlers
|
|
||||||
energy Manage energy dashboard settings
|
|
||||||
esphome Manage ESPHome devices
|
|
||||||
event Manage Home Assistant events
|
|
||||||
integration Manage integrations
|
|
||||||
network Manage network settings
|
|
||||||
notification Manage persistent notifications
|
|
||||||
repairs Manage Home Assistant repairs
|
|
||||||
system Manage system
|
|
||||||
template Work with Home Assistant templates
|
|
||||||
thread Manage Thread credentials
|
|
||||||
todo Manage to-do list items
|
|
||||||
update Update hab to the latest version
|
|
||||||
version Show version information
|
|
||||||
|
|
||||||
Additional Commands:
|
|
||||||
help Help about any command
|
|
||||||
|
|
||||||
Flags:
|
|
||||||
--config string Path to config directory (default: ~/.config/home-assistant-builder)
|
|
||||||
-h, --help help for hab
|
|
||||||
--json Use JSON output instead of human-readable text
|
|
||||||
--skip-update-check Skip automatic update check on startup
|
|
||||||
--text Use human-readable text output
|
|
||||||
--verbose Show verbose output
|
|
||||||
|
|
||||||
Use "hab [command] --help" for more information about a command.
|
|
||||||
```
|
|
||||||
<!-- HAB_LIVE_HELP_END -->
|
|
||||||
|
|
||||||
**Use configuration files when:** defining behavior, creating automations, setting up integrations
|
|
||||||
**Use MCP tools when:** checking current state, safe config writing, anomaly detection, entity diagnostics
|
|
||||||
**Use hab CLI when:** managing dashboards, areas, helpers, backups, blueprints, and bulk admin operations
|
|
||||||
|
|
||||||
### 4. zigporter CLI (Zigbee Toolkit)
|
|
||||||
A CLI for Zigbee device management in Home Assistant. Handles cascade renames (updating entity IDs across automations, scripts, scenes, and all Lovelace dashboards atomically), device inspection, stale device cleanup, and Zigbee mesh visualization.
|
|
||||||
|
|
||||||
**Cascade rename** — zigporter's unique value: when you rename an entity or device, it automatically patches every reference in automations, scripts, scenes, and Lovelace dashboards. `hab` can rename a single entity/device but does NOT cascade to references.
|
|
||||||
|
|
||||||
Key commands:
|
|
||||||
- **Cascade rename**: `zigporter rename-entity light.old_id light.new_id --apply`, `zigporter rename-device "Old Name" "New Name" --apply`
|
|
||||||
- **Device inventory**: `zigporter list-devices --json`, `zigporter list-z2m --json` (requires Z2M config)
|
|
||||||
- **Device inspection**: `zigporter inspect "Device Name" --json`, `zigporter inspect sensor.entity_id --json`
|
|
||||||
- **Stale device management**: `zigporter stale "Device" --action remove`, `zigporter stale "Device" --action ignore`
|
|
||||||
- **Post-migration cleanup**: `zigporter fix-device "Device" --apply`
|
|
||||||
- **Connectivity check**: `zigporter check`
|
|
||||||
- **ZHA export**: `zigporter export --output devices.json`
|
|
||||||
- **Mesh visualization**: `zigporter network-map --format table` (terminal), `zigporter network-map --output mesh.svg` (SVG file)
|
|
||||||
|
|
||||||
**Output format**: Use `--json` on listing/inspect commands for structured output (ideal for AI parsing). Rename commands output diffs and confirmation text.
|
|
||||||
|
|
||||||
zigporter is pre-authenticated via the Supervisor token. Z2M commands (`list-z2m`, `network-map --backend z2m`) require Z2M URL configuration in the add-on settings.
|
|
||||||
|
|
||||||
**Important limitations**:
|
|
||||||
- `rename-entity` / `rename-device` do NOT patch Jinja2 template expressions (e.g. `{{ states('old.id') }}`). A warning is printed listing affected files — inform the user these need manual review after renaming.
|
|
||||||
- The `migrate` command is inherently interactive (requires physical device actions) and must NOT be used by AI agents.
|
|
||||||
- Dry-run is the default for renames — always preview before using `--apply`.
|
|
||||||
|
|
||||||
<!-- ZIGPORTER_LIVE_HELP_START -->
|
|
||||||
```
|
|
||||||
|
|
||||||
Usage: zigporter [OPTIONS] COMMAND [ARGS]...
|
|
||||||
|
|
||||||
Migrate Zigbee devices between ZHA and Zigbee2MQTT. Supports both ZHA → Z2M
|
|
||||||
(default) and Z2M → ZHA (--direction z2m-to-zha).
|
|
||||||
|
|
||||||
╭─ Options ────────────────────────────────────────────────────────────────────╮
|
|
||||||
│ --version -v Show version and exit. │
|
|
||||||
│ --install-completion Install completion for the current shell. │
|
|
||||||
│ --show-completion Show completion for the current shell, to │
|
|
||||||
│ copy it or customize the installation. │
|
|
||||||
│ --help -h Show this message and exit. │
|
|
||||||
╰──────────────────────────────────────────────────────────────────────────────╯
|
|
||||||
╭─ Commands ───────────────────────────────────────────────────────────────────╮
|
|
||||||
│ setup Create or update the configuration file in the zigporter │
|
|
||||||
│ config directory. │
|
|
||||||
│ check Verify that all requirements are in place before migrating. │
|
|
||||||
│ export Export current ZHA devices, entities, areas, and automation │
|
|
||||||
│ references to JSON. │
|
|
||||||
│ export-z2m Export current Z2M devices, entities, areas, and automation │
|
|
||||||
│ references to JSON. │
|
|
||||||
│ list-z2m List all devices currently paired with Zigbee2MQTT. │
|
|
||||||
│ list-devices List all Home Assistant devices across all integrations. │
|
|
||||||
│ migrate Interactive wizard to migrate devices between ZHA and │
|
|
||||||
│ Zigbee2MQTT. │
|
|
||||||
│ inspect Show all automations, scripts, scenes, and dashboard cards │
|
|
||||||
│ that depend on a device. │
|
|
||||||
│ rename-entity Rename an entity ID and update all references in automations, │
|
|
||||||
│ scripts, scenes, and dashboards. │
|
|
||||||
│ rename-device Rename a device and cascade the change to all its entities, │
|
|
||||||
│ automations, scripts, scenes, and dashboards. │
|
|
||||||
│ stale Identify and manage offline/stale devices across all │
|
|
||||||
│ integrations. │
|
|
||||||
│ fix-device Remove stale ZHA device entries left behind after migration │
|
|
||||||
│ to Zigbee2MQTT. │
|
|
||||||
│ network-map Show Zigbee mesh topology with signal strength (LQI) for each │
|
|
||||||
│ device. │
|
|
||||||
╰──────────────────────────────────────────────────────────────────────────────╯
|
|
||||||
```
|
|
||||||
<!-- ZIGPORTER_LIVE_HELP_END -->
|
|
||||||
|
|
||||||
**Use zigporter CLI when:** renaming entities/devices with cascade updates, inspecting Zigbee devices across integrations, cleaning up stale or post-migration devices, visualizing the Zigbee mesh
|
|
||||||
|
|
||||||
### 5. Internal Directories (OFF-LIMITS)
|
|
||||||
Home Assistant manages internal state in directories like `.storage/`. These are:
|
|
||||||
- Not designed for direct access
|
|
||||||
- Subject to change without notice
|
|
||||||
- Potentially dangerous to modify
|
|
||||||
|
|
||||||
**Never access internal directories directly - use configuration files or MCP tools instead.**
|
|
||||||
|
|
||||||
## RESTRICTED: Internal Home Assistant Directories
|
|
||||||
|
|
||||||
**NEVER read, modify, or directly interact with these internal directories:**
|
|
||||||
|
|
||||||
| Directory | Contains | Use Instead |
|
|
||||||
|-----------|----------|-------------|
|
|
||||||
| `.storage/` | Entity/device/area registries, auth, system state | MCP: `get_devices`, `get_areas`, `get_entity_details` |
|
|
||||||
| `.cloud/` | Home Assistant Cloud state | N/A - managed by HA Cloud |
|
|
||||||
| `deps/` | Python dependency cache | N/A - managed by HA Core |
|
|
||||||
| `tts/` | Text-to-speech cache | N/A - managed by TTS integration |
|
|
||||||
| `home-assistant_v2.db` | History SQLite database | MCP: `get_history`, `get_logbook` |
|
|
||||||
| `home-assistant.log` | Raw system logs | MCP: `get_error_log` |
|
|
||||||
|
|
||||||
These contain internal Home Assistant state that:
|
|
||||||
1. Is managed exclusively by Home Assistant core
|
|
||||||
2. Can corrupt your installation if modified incorrectly
|
|
||||||
3. May be overwritten by Home Assistant at any time
|
|
||||||
4. Has no stable schema or format guarantees
|
|
||||||
|
|
||||||
**For information that seems to require internal access, there is always a proper alternative:**
|
|
||||||
- Need entity details? -> Read configuration files OR use `get_entity_details`
|
|
||||||
- Need device info? -> Use `get_devices` MCP tool
|
|
||||||
- Need to check history? -> Use `get_history` MCP tool
|
|
||||||
- Need to see errors? -> Use `get_error_log` MCP tool
|
|
||||||
|
|
||||||
## File Structure Knowledge
|
|
||||||
|
|
||||||
### Configuration Files (Primary Interface - Read/Write with User Approval)
|
|
||||||
These are the user-facing configuration files - the primary way to define Home Assistant behavior:
|
|
||||||
|
|
||||||
- `configuration.yaml` - Main configuration file
|
|
||||||
- `automations.yaml` - Automation definitions (if using UI or split config)
|
|
||||||
- `scripts.yaml` - Script definitions
|
|
||||||
- `scenes.yaml` - Scene definitions
|
|
||||||
- `secrets.yaml` - Sensitive values (NEVER commit or expose)
|
|
||||||
- `customize.yaml` - Entity customizations
|
|
||||||
- `groups.yaml` - Group definitions
|
|
||||||
- `packages/` - Package-based configuration splits
|
|
||||||
- `blueprints/` - Automation and script blueprints
|
|
||||||
- `custom_components/` - Custom integrations (HACS or manual)
|
|
||||||
- `www/` - Static files served at /local/
|
|
||||||
- `themes/` - Custom themes
|
|
||||||
- `*.yaml` in root - Any user-created YAML configuration
|
|
||||||
|
|
||||||
**These files are designed for editing** and are equally valid as MCP tools for research and changes.
|
|
||||||
|
|
||||||
### Internal Directories (OFF-LIMITS - Never Access Directly)
|
|
||||||
- `.storage/` - Internal registries and state (use MCP tools)
|
|
||||||
- `.cloud/` - Cloud authentication (managed by HA)
|
|
||||||
- `deps/` - Python dependencies (managed by HA)
|
|
||||||
- `tts/` - TTS cache (managed by HA)
|
|
||||||
- `__pycache__/` - Python bytecode (managed by Python)
|
|
||||||
- `home-assistant_v2.db` - History database (use MCP `get_history`)
|
|
||||||
- `home-assistant.log` - Logs (use MCP `get_error_log`)
|
|
||||||
|
|
||||||
## Working with YAML on the Command Line (`yq`)
|
|
||||||
|
|
||||||
To read, query, or convert YAML from the shell, use **`yq`** (the mikefarah/Go tool, pre-installed on `PATH`). It is the correct tool because it tolerates Home Assistant's custom tags — `!include`, `!secret`, `!env_var`, `!input`, and the `!include_dir_*` family.
|
|
||||||
|
|
||||||
**Do NOT reach for `python3 -c "import yaml"` (PyYAML) or Ruby's YAML for HA config** — both crash with a constructor error on the very first `!include`/`!secret`, because those tags are Home Assistant extensions, not standard YAML. `yq` parses them with no setup.
|
|
||||||
|
|
||||||
### Read / query (always safe — never errors on HA tags)
|
|
||||||
|
|
||||||
```
|
|
||||||
yq '.homeassistant.latitude' configuration.yaml # Print a nested value
|
|
||||||
yq '.automation | tag' configuration.yaml # Inspect the tag itself -> !include
|
|
||||||
yq 'keys' configuration.yaml # List top-level keys
|
|
||||||
yq -o=json '.' configuration.yaml | jq '.sensor' # Convert to JSON to pipe into jq
|
|
||||||
```
|
|
||||||
|
|
||||||
Note: output and JSON conversion strip the tag — `!secret home_latitude` prints as `home_latitude`. Use `| tag` when you need to see the tag. Never round-trip a file *through* JSON and back; that permanently loses every `!include`/`!secret`.
|
|
||||||
|
|
||||||
### Writing / editing
|
|
||||||
|
|
||||||
Prefer the sanctioned write path: **`write_config_safe`** (MCP — validates, backs up, and blocks accidental content loss) or read the full file and use the editor. Reserve `yq -i` for quick, low-risk edits, and only with these two caveats in mind:
|
|
||||||
|
|
||||||
1. **A custom tag sticks to the value you overwrite.** `yq -i '.homeassistant.latitude = 52.37'` on a `!secret`-tagged node produces the corrupt `latitude: !secret 52.37`. When replacing a tagged value, reset the tag in the *same* expression:
|
|
||||||
```
|
|
||||||
yq -i '(.homeassistant.latitude tag = "") | .homeassistant.latitude = 52.37' configuration.yaml
|
|
||||||
```
|
|
||||||
To *add* a secret reference, set the tag explicitly: `yq -i '.http.api_key = "my_api_key" | .http.api_key tag = "!secret"' configuration.yaml`
|
|
||||||
2. **`yq -i` strips blank separator lines** (and collapses inline-comment spacing) across the whole file. No data is lost, but diffs are noisier. When a clean, minimal diff matters, use the editor instead.
|
|
||||||
|
|
||||||
### Validation is not a syntax check
|
|
||||||
|
|
||||||
`yq` only confirms YAML *parses*. To validate a Home Assistant *configuration* (resolving `!include`/`!secret` and checking integration schemas), use `check_config_syntax` / `write_config_safe` (MCP), not `yq`.
|
|
||||||
|
|
||||||
## YAML Style Guide (MANDATORY)
|
|
||||||
|
|
||||||
All YAML written or modified MUST follow the official Home Assistant YAML Style Guide.
|
|
||||||
Reference: https://developers.home-assistant.io/docs/documenting/yaml-style-guide/
|
|
||||||
|
|
||||||
A Prettier formatter is configured for this environment and will auto-format files on save.
|
|
||||||
However, Prettier only enforces a subset of the rules below. You are responsible for following
|
|
||||||
ALL rules, especially those Prettier cannot enforce (marked with *).
|
|
||||||
|
|
||||||
### Indentation
|
|
||||||
|
|
||||||
2 spaces. Tabs are forbidden.
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
# Good
|
|
||||||
example:
|
|
||||||
one: 1
|
|
||||||
|
|
||||||
# Bad
|
|
||||||
example:
|
|
||||||
bad: 2
|
|
||||||
```
|
|
||||||
|
|
||||||
### Booleans *
|
|
||||||
|
|
||||||
Only `true` and `false` in lowercase. Never use `Yes`, `No`, `On`, `Off`, `TRUE`, etc.
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
# Good
|
|
||||||
one: true
|
|
||||||
two: false
|
|
||||||
|
|
||||||
# Bad
|
|
||||||
one: True
|
|
||||||
two: on
|
|
||||||
three: yes
|
|
||||||
```
|
|
||||||
|
|
||||||
### Strings
|
|
||||||
|
|
||||||
Double quotes for strings. Single quotes are not allowed.
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
# Good
|
|
||||||
example: "Hi there!"
|
|
||||||
|
|
||||||
# Bad
|
|
||||||
example: 'Hi there!'
|
|
||||||
```
|
|
||||||
|
|
||||||
**Exceptions** (no quotes needed): entity IDs, area IDs, device IDs, platform types,
|
|
||||||
trigger types, condition types, action names, device classes, event names, attribute names,
|
|
||||||
and values from a fixed set of options (e.g., `mode`).
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
# Good
|
|
||||||
actions:
|
|
||||||
- action: light.turn_on
|
|
||||||
target:
|
|
||||||
entity_id: light.living_room
|
|
||||||
area_id: living_room
|
|
||||||
data:
|
|
||||||
message: "Hello!"
|
|
||||||
transition: 10
|
|
||||||
|
|
||||||
# Bad - don't quote entity IDs and action names
|
|
||||||
actions:
|
|
||||||
- action: "light.turn_on"
|
|
||||||
target:
|
|
||||||
entity_id: "light.living_room"
|
|
||||||
```
|
|
||||||
|
|
||||||
### Sequences (Lists) *
|
|
||||||
|
|
||||||
Use block style. Flow style `[1, 2, 3]` must not be used.
|
|
||||||
Block sequences must be indented under their key.
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
# Good
|
|
||||||
options:
|
|
||||||
- 1
|
|
||||||
- 2
|
|
||||||
- 3
|
|
||||||
|
|
||||||
# Bad
|
|
||||||
options: [1, 2, 3]
|
|
||||||
|
|
||||||
# Bad - not indented under key
|
|
||||||
options:
|
|
||||||
- 1
|
|
||||||
- 2
|
|
||||||
```
|
|
||||||
|
|
||||||
### Mappings *
|
|
||||||
|
|
||||||
Block style only. Flow style `{ key: val }` must not be used.
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
# Good
|
|
||||||
example:
|
|
||||||
one: 1
|
|
||||||
two: 2
|
|
||||||
|
|
||||||
# Bad
|
|
||||||
example: { one: 1, two: 2 }
|
|
||||||
```
|
|
||||||
|
|
||||||
### Null Values *
|
|
||||||
|
|
||||||
Use implicit null (just `key:` with no value). Never use `null` or `~`.
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
# Good
|
|
||||||
initial:
|
|
||||||
|
|
||||||
# Bad
|
|
||||||
initial: null
|
|
||||||
initial: ~
|
|
||||||
```
|
|
||||||
|
|
||||||
### Comments
|
|
||||||
|
|
||||||
Capitalized, with a space after `#`, indented to match current level.
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
# Good
|
|
||||||
example:
|
|
||||||
# This is a comment
|
|
||||||
one: true
|
|
||||||
|
|
||||||
# Bad
|
|
||||||
example:
|
|
||||||
# Comment at wrong indent
|
|
||||||
#Missing space
|
|
||||||
#lowercase start
|
|
||||||
one: true
|
|
||||||
```
|
|
||||||
|
|
||||||
### Multiline Strings
|
|
||||||
|
|
||||||
Use literal `|` (preserves newlines) or folded `>` (joins lines) block scalars.
|
|
||||||
Avoid `\n` in strings. Prefer no-chomp (`|`, `>`) unless you need strip (`|-`, `>-`).
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
# Good
|
|
||||||
message: |
|
|
||||||
Hello!
|
|
||||||
This is a multiline
|
|
||||||
notification message.
|
|
||||||
|
|
||||||
# Good - folded
|
|
||||||
description: >
|
|
||||||
This is a long description that
|
|
||||||
will be joined into a single line.
|
|
||||||
|
|
||||||
# Bad
|
|
||||||
message: "Hello!\nThis is a multiline\nnotification message.\n"
|
|
||||||
```
|
|
||||||
|
|
||||||
### Templates
|
|
||||||
|
|
||||||
Double quotes outside, single quotes inside. Use `states()` and `state_attr()`
|
|
||||||
helpers, not direct state object access. Split long templates across multiple lines.
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
# Good
|
|
||||||
value_template: "{{ states('sensor.temperature') }}"
|
|
||||||
attribute_template: "{{ state_attr('climate.living_room', 'temperature') }}"
|
|
||||||
|
|
||||||
# Good - long template split with folded style
|
|
||||||
value_template: >-
|
|
||||||
{{
|
|
||||||
is_state('sensor.bedroom_co_status', 'Ok')
|
|
||||||
and is_state('sensor.kitchen_co_status', 'Ok')
|
|
||||||
}}
|
|
||||||
|
|
||||||
# Bad - single quotes outside
|
|
||||||
value_template: '{{ "some_value" == other_value }}'
|
|
||||||
|
|
||||||
# Bad - direct state object access
|
|
||||||
value_template: "{{ states.sensor.temperature.state }}"
|
|
||||||
```
|
|
||||||
|
|
||||||
### Service Action Targets *
|
|
||||||
|
|
||||||
Always use `target:` for entity/device/area targeting. Do not put `entity_id` at
|
|
||||||
the action level or inside `data:`.
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
# Good
|
|
||||||
actions:
|
|
||||||
- action: light.turn_on
|
|
||||||
target:
|
|
||||||
entity_id: light.living_room
|
|
||||||
|
|
||||||
# Bad
|
|
||||||
actions:
|
|
||||||
- action: light.turn_on
|
|
||||||
entity_id: light.living_room
|
|
||||||
|
|
||||||
# Bad
|
|
||||||
actions:
|
|
||||||
- action: light.turn_on
|
|
||||||
data:
|
|
||||||
entity_id: light.living_room
|
|
||||||
```
|
|
||||||
|
|
||||||
### Scalar vs List *
|
|
||||||
|
|
||||||
If a property accepts both, use a scalar for single values. Do not wrap a single
|
|
||||||
value in a list. Do not use comma-separated strings.
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
# Good
|
|
||||||
entity_id: light.living_room
|
|
||||||
entity_id:
|
|
||||||
- light.living_room
|
|
||||||
- light.office
|
|
||||||
|
|
||||||
# Bad - single value in a list
|
|
||||||
entity_id:
|
|
||||||
- light.living_room
|
|
||||||
|
|
||||||
# Bad - comma separated
|
|
||||||
entity_id: "light.living_room, light.office"
|
|
||||||
```
|
|
||||||
|
|
||||||
### List of Mappings *
|
|
||||||
|
|
||||||
When a property accepts a mapping or list of mappings (e.g., `actions`, `conditions`),
|
|
||||||
always use a list even for a single item.
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
# Good
|
|
||||||
actions:
|
|
||||||
- action: light.turn_on
|
|
||||||
target:
|
|
||||||
entity_id: light.living_room
|
|
||||||
|
|
||||||
# Bad
|
|
||||||
actions:
|
|
||||||
action: light.turn_on
|
|
||||||
target:
|
|
||||||
entity_id: light.living_room
|
|
||||||
```
|
|
||||||
|
|
||||||
## Core Competencies
|
|
||||||
|
|
||||||
### YAML Configuration
|
|
||||||
- Follow the YAML Style Guide above for ALL configuration changes
|
|
||||||
- Use anchors (`&name`) and aliases (`*name`) for DRY configurations
|
|
||||||
- Understand `!include`, `!include_dir_named`, `!include_dir_list`, `!include_dir_merge_named`, `!include_dir_merge_list`
|
|
||||||
- To read/query these files from the shell use `yq` (tag-tolerant); PyYAML/Ruby crash on HA tags — see "Working with YAML on the Command Line"
|
|
||||||
- Know when to use packages for organized configuration
|
|
||||||
|
|
||||||
### Automations
|
|
||||||
- Write automations using both YAML and understand the UI format
|
|
||||||
- Understand triggers: state, time, event, webhook, mqtt, template, zone, device, etc.
|
|
||||||
- Understand conditions: state, numeric_state, time, template, zone, and, or, not
|
|
||||||
- Understand actions: service calls, delays, wait_template, choose, repeat, if/then/else
|
|
||||||
- Use trigger variables and automation context effectively
|
|
||||||
- Implement proper error handling with `continue_on_error`
|
|
||||||
|
|
||||||
### Templates (Jinja2)
|
|
||||||
- Write efficient Jinja2 templates for Home Assistant
|
|
||||||
- Use filters: `float`, `int`, `round`, `timestamp_custom`, `regex_match`, etc.
|
|
||||||
- Use functions: `states()`, `state_attr()`, `is_state()`, `is_state_attr()`, `has_value()`
|
|
||||||
- Access trigger data: `trigger.to_state`, `trigger.from_state`, `trigger.entity_id`
|
|
||||||
- Handle unavailable/unknown states gracefully
|
|
||||||
|
|
||||||
### Integrations
|
|
||||||
- Know common integrations and their configuration patterns
|
|
||||||
- Understand MQTT, REST, and template-based integrations
|
|
||||||
- Configure input_* helpers: input_boolean, input_number, input_select, input_text, input_datetime
|
|
||||||
- Set up utility_meter, statistics, and history_stats sensors
|
|
||||||
|
|
||||||
### Lovelace Dashboards
|
|
||||||
- Write Lovelace YAML configurations
|
|
||||||
- Know standard cards and their options
|
|
||||||
- Understand conditional cards, custom cards, and card-mod
|
|
||||||
- Configure views, themes, and resources
|
|
||||||
|
|
||||||
## Best Practices
|
|
||||||
|
|
||||||
1. **Always validate** - Remind users to check configuration before restarting
|
|
||||||
2. **Use secrets** - Never hardcode sensitive data; use `!secret` references
|
|
||||||
3. **Backup first** - Suggest backups before major changes
|
|
||||||
4. **Incremental changes** - Make small, testable changes
|
|
||||||
5. **Comments** - Add YAML comments explaining complex logic
|
|
||||||
6. **Naming conventions** - Use consistent entity_id naming (e.g., `sensor.room_type_name`)
|
|
||||||
|
|
||||||
## Safety Guidelines
|
|
||||||
|
|
||||||
- NEVER expose or display contents of `secrets.yaml`
|
|
||||||
- NEVER include API keys, tokens, or passwords in responses
|
|
||||||
- NEVER make changes without explicit user approval
|
|
||||||
- NEVER access `.storage/`, `.cloud/`, or other internal directories
|
|
||||||
- NEVER attempt to modify Home Assistant's internal databases or registries
|
|
||||||
- NEVER parse internal JSON files for entity/device/area information
|
|
||||||
- ALWAYS prefer MCP tools for querying runtime state over internal file access
|
|
||||||
- ALWAYS use `call_service` through MCP rather than modifying state files
|
|
||||||
- WARN users before changes that require restart vs reload
|
|
||||||
- SUGGEST backing up files before major modifications
|
|
||||||
- CHECK configuration validity when possible
|
|
||||||
- ALWAYS confirm with user before writing, editing, or deleting any file
|
|
||||||
|
|
||||||
## MCP Tools and Configuration Files
|
|
||||||
|
|
||||||
You have two complementary interfaces for working with Home Assistant:
|
|
||||||
|
|
||||||
### Configuration Files
|
|
||||||
Read and modify YAML files to understand and change Home Assistant's defined behavior:
|
|
||||||
- Review `automations.yaml` to understand existing automations
|
|
||||||
- Edit `configuration.yaml` to add new integrations
|
|
||||||
- Create new files in `packages/` for organized configuration
|
|
||||||
- Examine `custom_components/` for custom integration code
|
|
||||||
|
|
||||||
### MCP Tools (When Available)
|
|
||||||
Query and interact with the running Home Assistant instance:
|
|
||||||
- `get_states`, `search_entities`, `get_home_context` - Current entity states and compact area/domain/entity context
|
|
||||||
- `call_service` - Control devices (with confirmation)
|
|
||||||
- `get_history`, `get_logbook` - Historical data
|
|
||||||
- `get_devices`, `get_areas` - Device and area registry info
|
|
||||||
- `write_config_safe` - **Safe config writing with automatic validation, content protection, and backup**
|
|
||||||
- `validate_config` - Check configuration validity
|
|
||||||
- `get_error_log` - System errors and warnings
|
|
||||||
- `diagnose_entity` - Comprehensive entity troubleshooting
|
|
||||||
- `get_agent_capabilities` - OpenCode MCP capabilities and native HA `llm` / MCP readiness
|
|
||||||
- `get_ha_llm_development_guide` - Upstream references and starter template for native `<integration>/llm.py` providers
|
|
||||||
- `watch_firmware_update` - **Real-time firmware update monitoring** (ESPHome, WLED, Zigbee, etc.)
|
|
||||||
- `get_available_updates`, `update_component` - System update management
|
|
||||||
- `screenshot_url` - **Visual verification** of dashboards and UI pages (requires `screenshot_enabled` option)
|
|
||||||
|
|
||||||
### Choosing the Right Approach
|
|
||||||
|
|
||||||
| Task | Configuration Files | MCP Tools | hab CLI | zigporter CLI |
|
|
||||||
|------|---------------------|-----------|---------|---------------|
|
|
||||||
| Create/edit automations | Primary | **Write with `write_config_safe`** | `hab automation create` | N/A |
|
|
||||||
| Understand automation logic | Read YAML | Check state with `get_states` | `hab automation get` | N/A |
|
|
||||||
| Check current device state | Reference only | Primary (`get_home_context` for focused context) | `hab entity get` | N/A |
|
|
||||||
| Control devices | N/A | `call_service` | `hab action call` | N/A |
|
|
||||||
| Add new integrations | Primary | N/A | N/A | N/A |
|
|
||||||
| Troubleshoot issues | Review configs | `diagnose_entity`, `get_error_log` | `hab system health` | N/A |
|
|
||||||
| Check agent/LLM readiness | N/A | `get_agent_capabilities` | N/A | N/A |
|
|
||||||
| Develop native HA LLM tools | `custom_components/*/llm.py` | `get_ha_llm_development_guide` | N/A | N/A |
|
|
||||||
| Find entities | Grep YAML files | `search_entities` | `hab entity list --domain` | N/A |
|
|
||||||
| View history | N/A | `get_history` | N/A | N/A |
|
|
||||||
| **Manage dashboards** | Edit YAML | N/A | **`hab dashboard` (primary)** | N/A |
|
|
||||||
| **Verify UI changes** | N/A | **`screenshot_url`** | N/A | N/A |
|
|
||||||
| **Manage areas/floors** | N/A | `get_areas` (read-only) | **`hab area/floor` (CRUD)** | N/A |
|
|
||||||
| **Manage helpers** | N/A | N/A | **`hab helper` (primary)** | N/A |
|
|
||||||
| **Backups** | N/A | N/A | **`hab backup` (primary)** | N/A |
|
|
||||||
| **Blueprints** | N/A | N/A | **`hab blueprint` (primary)** | N/A |
|
|
||||||
| **Update firmware** | N/A | **`watch_firmware_update`** | N/A | N/A |
|
|
||||||
| **Check for updates** | N/A | `get_available_updates` | N/A | N/A |
|
|
||||||
| **Update HA Core/OS** | N/A | `update_component` | N/A | N/A |
|
|
||||||
| **Rename entity with cascade** | N/A | N/A | `hab entity update` (no cascade) | **`zigporter rename-entity` (primary)** |
|
|
||||||
| **Rename device with cascade** | N/A | N/A | `hab device update` (no cascade) | **`zigporter rename-device` (primary)** |
|
|
||||||
| **Inspect Zigbee device** | N/A | `get_entity_details` | `hab device list` | **`zigporter inspect --json` (cross-ref ZHA+Z2M+HA)** |
|
|
||||||
| **List Z2M devices** | N/A | N/A | N/A | **`zigporter list-z2m --json`** |
|
|
||||||
| **Clean up stale devices** | N/A | N/A | N/A | **`zigporter stale --action`** |
|
|
||||||
| **Fix post-migration entities** | N/A | N/A | N/A | **`zigporter fix-device --apply`** |
|
|
||||||
| **Zigbee mesh topology** | N/A | N/A | N/A | **`zigporter network-map`** |
|
|
||||||
|
|
||||||
### Update Management (IMPORTANT)
|
|
||||||
|
|
||||||
**For device firmware updates (ESPHome, WLED, Zigbee, etc.):**
|
|
||||||
Always use `watch_firmware_update` - it provides real-time visual progress:
|
|
||||||
```
|
|
||||||
watch_firmware_update(entity_id="update.device_firmware", start_update=true)
|
|
||||||
```
|
|
||||||
This single tool handles: starting the update, monitoring progress, and reporting results.
|
|
||||||
|
|
||||||
**For system updates (Core, OS, Supervisor, Apps):**
|
|
||||||
```
|
|
||||||
1. get_available_updates() -> Check what needs updating
|
|
||||||
2. update_component(component="core") -> Start update (returns job_id)
|
|
||||||
3. get_update_progress(job_id="...") -> Monitor progress
|
|
||||||
```
|
|
||||||
|
|
||||||
**Both approaches are valid and complementary.** Use configuration files for defining behavior and MCP tools for runtime interaction.
|
|
||||||
|
|
||||||
## Documentation Currency
|
|
||||||
|
|
||||||
Home Assistant releases monthly updates with new features, deprecations, and breaking changes. Your training data may be outdated. **Always verify configuration syntax against current documentation.**
|
|
||||||
|
|
||||||
### Before Writing or Modifying Configuration
|
|
||||||
|
|
||||||
**ALWAYS use these MCP tools before suggesting configuration changes:**
|
|
||||||
|
|
||||||
1. **Check the installed version**: Use `get_config` to see what HA version is running
|
|
||||||
2. **Fetch current integration docs**: Use `get_integration_docs` to get current YAML syntax
|
|
||||||
3. **Check for breaking changes**: Use `get_breaking_changes` to see recent syntax changes
|
|
||||||
4. **Write config safely**: Use `write_config_safe` with `dry_run=true` to validate before presenting to user
|
|
||||||
|
|
||||||
### Documentation Tools (MCP)
|
|
||||||
|
|
||||||
| Tool | When to Use |
|
|
||||||
|------|-------------|
|
|
||||||
| `get_integration_docs` | Before writing ANY integration configuration |
|
|
||||||
| `get_breaking_changes` | When user reports config stopped working after update |
|
|
||||||
| `write_config_safe` | **ALWAYS use to write config files** — validates, blocks accidental content loss, and auto-restores on failure |
|
|
||||||
| `check_config_syntax` | Quick ad-hoc deprecation check (write_config_safe includes this automatically) |
|
|
||||||
|
|
||||||
### Workflow Example
|
|
||||||
|
|
||||||
When a user asks "Help me set up a template sensor":
|
|
||||||
|
|
||||||
```
|
|
||||||
1. get_config() -> Check HA version (e.g., 2024.12.1)
|
|
||||||
2. get_integration_docs("template") -> Get current syntax and examples
|
|
||||||
3. read_file(path) -> Read the EXISTING file content first
|
|
||||||
4. Draft configuration: include ALL existing content + new changes
|
|
||||||
5. write_config_safe(path, yaml, dry_run=true) -> Pre-validate everything
|
|
||||||
6. If errors: fix and repeat step 5
|
|
||||||
7. Present validated config to user and get approval
|
|
||||||
8. write_config_safe(path, yaml) -> Write for real (auto backup + validation)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Common Deprecation Patterns
|
|
||||||
|
|
||||||
Be especially careful with these frequently-changed areas:
|
|
||||||
- **Template sensors/binary_sensors**: `platform: template` under `sensor:` is deprecated; use top-level `template:`
|
|
||||||
- **Entity configurations**: Many moved from YAML to UI-based config
|
|
||||||
- **Trigger-based templates**: Newer syntax preferred over legacy template sensors
|
|
||||||
- **Device triggers**: Syntax evolves with new device types
|
|
||||||
- **MQTT platform syntax**: `platform: mqtt` under domain keys is deprecated; use top-level `mqtt:` key
|
|
||||||
- **Direct state access**: `states.sensor.x.state` is fragile; use `states('sensor.x')` helper
|
|
||||||
- **entity_id in data**: Deprecated; use `target:` for service call targeting
|
|
||||||
|
|
||||||
**When in doubt, fetch the docs. Never rely solely on training data for configuration syntax.**
|
|
||||||
|
|
||||||
## Common Tasks
|
|
||||||
|
|
||||||
### Creating an Automation
|
|
||||||
1. **Read the existing `automations.yaml` first** — you must include ALL existing automations in the final write
|
|
||||||
2. Understand the goal and identify trigger conditions
|
|
||||||
3. Determine required entities (search if MCP available)
|
|
||||||
4. Draft the automation YAML with clear comments
|
|
||||||
5. **Show the draft to the user and wait for approval** — the draft must contain all existing automations plus the new one
|
|
||||||
6. Only write the file after explicit user confirmation
|
|
||||||
7. Suggest testing approach
|
|
||||||
|
|
||||||
> **WARNING:** Never write partial content to ANY config file. Always read the existing file first and include ALL existing content in your write. `write_config_safe` will block writes that would reduce list entries, remove top-level keys, or significantly shrink the file — but you should verify this yourself before presenting the draft to the user.
|
|
||||||
|
|
||||||
### Troubleshooting
|
|
||||||
1. Check entity states and history (via MCP if available)
|
|
||||||
2. Review relevant configuration files
|
|
||||||
3. Check Home Assistant logs for errors
|
|
||||||
4. Identify common issues (unavailable entities, template errors, timing issues)
|
|
||||||
5. **Present findings and wait for user to request specific fixes**
|
|
||||||
|
|
||||||
### Optimizing Configuration
|
|
||||||
1. Identify redundant or inefficient patterns
|
|
||||||
2. **Present recommendations to user**
|
|
||||||
3. Wait for user to approve specific changes
|
|
||||||
4. Implement only the changes the user explicitly approves
|
|
||||||
|
|||||||
+61
-28
@@ -10,22 +10,32 @@ Use this file as the source of truth for projects being worked on with OpenCode.
|
|||||||
- Ask the user before closing a project that appears complete.
|
- Ask the user before closing a project that appears complete.
|
||||||
- Use these statuses: `Active`, `Paused`, and `Complete`.
|
- Use these statuses: `Active`, `Paused`, and `Complete`.
|
||||||
|
|
||||||
|
## Active Projects
|
||||||
|
|
||||||
| ID | Project | Status | Next Step | Last Updated |
|
| ID | Project | Status | Next Step | Last Updated |
|
||||||
| --- | --- | --- | --- | --- |
|
| --- | --- | --- | --- | --- |
|
||||||
| P001 | Bedside panel alarm audio | Active | Deploy and verify alarm audio and volume behavior. | 2026-07-20 |
|
| P001 | Bedside panel alarm audio | Active | Deploy and verify alarm audio and volume behavior. | 2026-07-20 |
|
||||||
| P002 | reTerminal E1001 dashboard | Active | Test display refresh, wake buttons, MQTT control, and deep sleep. | 2026-07-20 |
|
| P002 | reTerminal E1001 dashboard | Active | Test display refresh, wake buttons, MQTT control, and deep sleep. | 2026-07-20 |
|
||||||
| P003 | ESPHome MQTT topic normalization | Active | Deploy paired devices and verify retained synchronization and external clients. | 2026-07-20 |
|
| P003 | ESPHome MQTT topic normalization | Active | Compile and deploy both stair devices, then verify HA-down MQTT synchronization. | 2026-07-21 |
|
||||||
| P004 | Appliance completion announcements | Active | Test an appliance cycle and resolve feed/timestamp inconsistencies. | 2026-07-20 |
|
| P004 | Appliance completion announcements | Active | Test an appliance cycle and resolve feed/timestamp inconsistencies. | 2026-07-20 |
|
||||||
| P005 | Jobs and Chore Tracker V2.3 | Active | Test completion boundaries and resolve stale or duplicated entities. | 2026-07-20 |
|
|
||||||
| P006 | Bedside rain forecast | Active | Verify forecast boundaries and API-failure handling. | 2026-07-20 |
|
| P006 | Bedside rain forecast | Active | Verify forecast boundaries and API-failure handling. | 2026-07-20 |
|
||||||
| P007 | Bedtime stair shutdown | Active | Verify both stair relays during a bedtime run. | 2026-07-20 |
|
| P013 | Full Home Assistant system audit | Active | Complete a comprehensive read-only audit and present prioritized recommendations. | 2026-07-21 |
|
||||||
|
|
||||||
|
## Completed Projects
|
||||||
|
|
||||||
|
| ID | Project | Status | Next Step | Last Updated |
|
||||||
|
| --- | --- | --- | --- | --- |
|
||||||
|
| P005 | Jobs and Chore Tracker V2.3 | Complete | None. | 2026-07-21 |
|
||||||
|
| P007 | Bedtime stair shutdown | Complete | None. | 2026-07-21 |
|
||||||
| P008 | Spook unused entity cleanup | Complete | None. | 2026-07-21 |
|
| P008 | Spook unused entity cleanup | Complete | None. | 2026-07-21 |
|
||||||
| P009 | Lounge front-door camera layout | Complete | None. | 2026-07-21 |
|
| P009 | Lounge front-door camera layout | Complete | None. | 2026-07-21 |
|
||||||
| P010 | Main-page Jobs timing gauges | Complete | None. | 2026-07-21 |
|
| P010 | Main-page Jobs timing gauges | Complete | None. | 2026-07-21 |
|
||||||
| P011 | Downstairs occupancy helper cleanup | Complete | None. | 2026-07-21 |
|
| P011 | Downstairs occupancy helper cleanup | Complete | None. | 2026-07-21 |
|
||||||
| P012 | EdgeRouter X SNMP monitoring | Active | Investigate the current EdgeOS failure and v3.0.x-compatible SNMP approach. | 2026-07-21 |
|
| P012 | EdgeRouter X SNMP monitoring | Complete | None. | 2026-07-21 |
|
||||||
|
|
||||||
## P001: Bedside Panel Alarm Audio
|
## Project Details
|
||||||
|
|
||||||
|
### P001: Bedside Panel Alarm Audio
|
||||||
|
|
||||||
- Objective: Provide reliable local alarm audio and user-adjustable volume on the bedside panel.
|
- Objective: Provide reliable local alarm audio and user-adjustable volume on the bedside panel.
|
||||||
- Current context: ES8311 and I2S output, amplifier control, persistent volume, and Beep/Alarm/Stop controls were added and repeatedly tuned. Compilation, deployment, and hardware behavior have not been confirmed.
|
- Current context: ES8311 and I2S output, amplifier control, persistent volume, and Beep/Alarm/Stop controls were added and repeatedly tuned. Compilation, deployment, and hardware behavior have not been confirmed.
|
||||||
@@ -34,7 +44,7 @@ Use this file as the source of truth for projects being worked on with OpenCode.
|
|||||||
- Next step: Compile and deploy, then test startup noise, Beep/Alarm/Stop, volume endpoints, persistence, and amplifier shutdown.
|
- Next step: Compile and deploy, then test startup noise, Beep/Alarm/Stop, volume endpoints, persistence, and amplifier shutdown.
|
||||||
- Activity: 2026-07-20 - Implemented and tuned the alarm-audio feature through V1.47.
|
- Activity: 2026-07-20 - Implemented and tuned the alarm-audio feature through V1.47.
|
||||||
|
|
||||||
## P002: reTerminal E1001 Dashboard
|
### P002: reTerminal E1001 Dashboard
|
||||||
|
|
||||||
- Objective: Build a low-power e-paper dashboard for job and appliance status.
|
- Objective: Build a low-power e-paper dashboard for job and appliance status.
|
||||||
- Current context: Added ESP32-S3 hardware support, Jobs and Appliances pages, Home Assistant entity imports, icons, buttons, MQTT controls, change-only refresh, retained page hashes, wake-source handling, and deep sleep. Runtime behavior has not been confirmed.
|
- Current context: Added ESP32-S3 hardware support, Jobs and Appliances pages, Home Assistant entity imports, icons, buttons, MQTT controls, change-only refresh, retained page hashes, wake-source handling, and deep sleep. Runtime behavior has not been confirmed.
|
||||||
@@ -42,15 +52,17 @@ Use this file as the source of truth for projects being worked on with OpenCode.
|
|||||||
- Next step: Compile and deploy, then test entity data, rendering, page buttons, timer and button wake, MQTT keep-awake control, fallback sleep, and battery usage.
|
- Next step: Compile and deploy, then test entity data, rendering, page buttons, timer and button wake, MQTT keep-awake control, fallback sleep, and battery usage.
|
||||||
- Activity: 2026-07-20 - Created the device configuration and developed it through v0.6.1.
|
- Activity: 2026-07-20 - Created the device configuration and developed it through v0.6.1.
|
||||||
|
|
||||||
## P003: ESPHome MQTT Topic Normalization
|
### P003: ESPHome MQTT Topic Normalization
|
||||||
|
|
||||||
- Objective: Simplify MQTT command and status topics across related ESPHome devices while preserving synchronization.
|
- Objective: Simplify MQTT command and status topics across related ESPHome devices while preserving synchronization.
|
||||||
- Current context: Flattened component-specific MQTT topics to logical device roots across eleven devices. Updated paired lounge and master-bedroom consumers and restored direct pantry-light control. Deployment and external-client compatibility have not been confirmed.
|
- Current context: Flattened component-specific MQTT topics to logical device roots across eleven devices. Updated paired lounge and master-bedroom consumers and restored direct pantry-light control. The central-stair top and bottom source configurations are now v3.9: physical states publish retained QoS 1 status, remote mirrors consume those status topics, commands remain non-retained, and the superseded lounge-middle member was removed from the HA sync group.
|
||||||
- Key files: Related device configurations under `esphome/`, including lounge, kitchen, office, bedroom, breakfast-bar, and master-bedroom devices.
|
- Key files: Related device configurations under `esphome/`, including `esphome/esp-centralstairs-top.yaml`, `esphome/esp-centralstairs-bottom.yaml`, and `packages/Lighting_2Way_Sync/central_stairway_2way_sync.yaml`.
|
||||||
- Next step: Compile and deploy paired devices together, verify root commands and retained state, audit external MQTT consumers, and remove stale retained messages from superseded topics when safe.
|
- Verification: Both stair ESPHome files parse, Home Assistant configuration validation passed, and both two-member relay pairs synchronized on and off in both directions through Home Assistant. ESPHome compile/upload and HA-down MQTT behavior remain unverified because OpenCode does not have the required Long-Lived Access Token.
|
||||||
|
- Next step: Configure the OpenCode ESPHome token, compile and deploy both stair devices together, test direct MQTT commands and retained status with HA Core unavailable, then continue the broader paired-device and external-client audit.
|
||||||
- Activity: 2026-07-20 - Applied the MQTT topic normalization across the related devices.
|
- Activity: 2026-07-20 - Applied the MQTT topic normalization across the related devices.
|
||||||
|
- Activity: 2026-07-21 - Added retained MQTT status mirroring to both central-stair v3.9 source files, removed the superseded lounge-middle HA member, validated the source, and live-tested both HA relay pairs.
|
||||||
|
|
||||||
## P004: Appliance Completion Announcements
|
### P004: Appliance Completion Announcements
|
||||||
|
|
||||||
- Objective: Route appliance completion events to configured announcement channels and MQTT activity feeds.
|
- Objective: Route appliance completion events to configured announcement channels and MQTT activity feeds.
|
||||||
- Current context: Added per-appliance announcement settings, payloads, channels, LED-matrix indicators, MQTT feed suffixes, suppression logging, retained attributes, and expanded washer, dryer, dishwasher, and EV-charger handling.
|
- Current context: Added per-appliance announcement settings, payloads, channels, LED-matrix indicators, MQTT feed suffixes, suppression logging, retained attributes, and expanded washer, dryer, dishwasher, and EV-charger handling.
|
||||||
@@ -59,16 +71,17 @@ Use this file as the source of truth for projects being worked on with OpenCode.
|
|||||||
- Next step: Resolve the feed and timestamp inconsistencies, then test a complete appliance cycle end to end.
|
- Next step: Resolve the feed and timestamp inconsistencies, then test a complete appliance cycle end to end.
|
||||||
- Activity: 2026-07-20 - Expanded appliance monitoring and completion-feed routing.
|
- Activity: 2026-07-20 - Expanded appliance monitoring and completion-feed routing.
|
||||||
|
|
||||||
## P005: Jobs and Chore Tracker V2.3
|
### P005: Jobs and Chore Tracker V2.3
|
||||||
|
|
||||||
- Objective: Maintain household jobs through a registry-driven tracker with discovery entities and completion windows.
|
- Objective: Maintain household jobs through a registry-driven tracker with discovery entities and completion windows.
|
||||||
- Current context: Centralized five jobs, added generic completion routing, morning and evening dog-feeding windows, MQTT Discovery entities, summary and count entities, manual buttons, missed-job handling, and completion-window expiry.
|
- Current context: Centralized five jobs, added generic completion routing, morning and evening dog-feeding windows, MQTT Discovery entities, summary and count entities, manual buttons, missed-job handling, and completion-window expiry.
|
||||||
- Key files: `packages/Job_Chore_Tracking/jobs_tracker_jobs.yaml` and `packages/Job_Chore_Tracking/jobs_tracker_processor.yaml`.
|
- Key files: `packages/Job_Chore_Tracking/jobs_tracker_jobs.yaml` and `packages/Job_Chore_Tracking/jobs_tracker_processor.yaml`.
|
||||||
- Known issue: Some count entities may be stale or have duplicated `jobs_tracker` naming.
|
- Known issue: Some count entities may be stale or have duplicated `jobs_tracker` naming.
|
||||||
- Next step: Test completion-window boundaries and clean up stale or double-prefixed entities after confirming which entities are current.
|
- Next step: None.
|
||||||
- Activity: 2026-07-20 - Implemented the V2.3 registry and processor changes.
|
- Activity: 2026-07-20 - Implemented the V2.3 registry and processor changes.
|
||||||
|
- Activity: 2026-07-21 - User confirmed completion.
|
||||||
|
|
||||||
## P006: Bedside Rain Forecast
|
### P006: Bedside Rain Forecast
|
||||||
|
|
||||||
- Objective: Supply the bedside panel with useful rain-chance forecasts for today and tomorrow.
|
- Objective: Supply the bedside panel with useful rain-chance forecasts for today and tomorrow.
|
||||||
- Current context: Added Open-Meteo REST sensors for today's remaining forecast period and tomorrow's daylight period using the Home zone coordinates.
|
- Current context: Added Open-Meteo REST sensors for today's remaining forecast period and tomorrow's daylight period using the Home zone coordinates.
|
||||||
@@ -76,15 +89,17 @@ Use this file as the source of truth for projects being worked on with OpenCode.
|
|||||||
- Next step: Verify sunrise and sunset boundary behavior, stale-data handling, and API-failure recovery.
|
- Next step: Verify sunrise and sunset boundary behavior, stale-data handling, and API-failure recovery.
|
||||||
- Activity: 2026-07-20 - Added the Open-Meteo rain forecast package and bedside-panel inputs.
|
- Activity: 2026-07-20 - Added the Open-Meteo rain forecast package and bedside-panel inputs.
|
||||||
|
|
||||||
## P007: Bedtime Stair Shutdown
|
### P007: Bedtime Stair Shutdown
|
||||||
|
|
||||||
- Objective: Ensure the central stair lighting turns off as part of bedtime mode.
|
- Objective: Ensure the central stair lighting turns off as part of bedtime mode.
|
||||||
- Current context: Added the lower main stair lights and stair footer lights to the bedtime shutdown sequence with no delay.
|
- Current context: The lower main stair lights and stair footer lights are included in the bedtime shutdown sequence with no delay. Their HA two-way groups now contain only the active top and bottom relays; the superseded lounge-middle member was removed.
|
||||||
- Key files: `packages/bedtime_mode_actions.yaml`.
|
- Key files: `packages/bedtime_mode_actions.yaml` and `packages/Lighting_2Way_Sync/central_stairway_2way_sync.yaml`.
|
||||||
- Next step: Run bedtime mode and verify both relays switch off as intended.
|
- Verification: Both bottom relays were turned on and the exact zero-delay bedtime shutdown script turned both off. The corresponding top relays followed to off. After the obsolete third member was removed, each two-member pair also synchronized on and off successfully in both HA directions.
|
||||||
|
- Next step: None.
|
||||||
- Activity: 2026-07-20 - Added both central stair relays to bedtime shutdown.
|
- Activity: 2026-07-20 - Added both central stair relays to bedtime shutdown.
|
||||||
|
- Activity: 2026-07-21 - Live-tested both bedtime shutdown targets and both HA two-way relay pairs; all tests passed and the user confirmed completion.
|
||||||
|
|
||||||
## P008: Spook Unused Entity Cleanup
|
### P008: Spook Unused Entity Cleanup
|
||||||
|
|
||||||
- Objective: Resolve Spook warnings caused by stale entity references.
|
- Objective: Resolve Spook warnings caused by stale entity references.
|
||||||
- Current context: The Master Bedroom Echo Show Environment view now uses the live Bedroom 1 Environment (Z19TH) temperature and humidity entities, and the related Spook repair has cleared.
|
- Current context: The Master Bedroom Echo Show Environment view now uses the live Bedroom 1 Environment (Z19TH) temperature and humidity entities, and the related Spook repair has cleared.
|
||||||
@@ -95,7 +110,7 @@ Use this file as the source of truth for projects being worked on with OpenCode.
|
|||||||
- Activity: 2026-07-21 - Replaced both stale dashboard references and confirmed that the Spook repair cleared.
|
- Activity: 2026-07-21 - Replaced both stale dashboard references and confirmed that the Spook repair cleared.
|
||||||
- Activity: 2026-07-21 - User confirmed completion.
|
- Activity: 2026-07-21 - User confirmed completion.
|
||||||
|
|
||||||
## P009: Lounge Front-Door Camera Layout
|
### P009: Lounge Front-Door Camera Layout
|
||||||
|
|
||||||
- Objective: Display the Front Door Camera in landscape orientation on the Lounge dashboard.
|
- Objective: Display the Front Door Camera in landscape orientation on the Lounge dashboard.
|
||||||
- Current context: The Lounge Home view now displays the live `camera.camera2` card at a 4:3 landscape aspect ratio.
|
- Current context: The Lounge Home view now displays the live `camera.camera2` card at a 4:3 landscape aspect ratio.
|
||||||
@@ -105,19 +120,20 @@ Use this file as the source of truth for projects being worked on with OpenCode.
|
|||||||
- Activity: 2026-07-21 - Changed the Front Door Camera card from 3:4 to 4:3 and verified the saved dashboard configuration.
|
- Activity: 2026-07-21 - Changed the Front Door Camera card from 3:4 to 4:3 and verified the saved dashboard configuration.
|
||||||
- Activity: 2026-07-21 - User confirmed completion.
|
- Activity: 2026-07-21 - User confirmed completion.
|
||||||
|
|
||||||
## P010: Main-Page Jobs Timing Gauges
|
### P010: Main-Page Jobs Timing Gauges
|
||||||
|
|
||||||
- Objective: Make the Lounge Home dog-feeding and depooping gauges use timing from the Jobs routine.
|
- Objective: Make the Lounge Home dog-feeding and depooping gauges use timing from the Jobs routine.
|
||||||
- Current context: The gauges now use Jobs-derived template sensors. Dog feeding uses the latest morning/evening completion, and depooping uses the weekly job completion.
|
- Current context: The gauges use numeric-safe Jobs-derived template sensors. Dog feeding uses the latest valid morning/evening completion, and depooping uses the weekly job completion. Jobs startup waits for retained timestamps before refreshing, and stale restored event timestamps cannot mark jobs complete.
|
||||||
- Selected design: Keep two gauges. Dog feeding uses the latest morning/evening completion; depooping uses the weekly job completion.
|
- Selected design: Keep two gauges. Dog feeding uses the latest morning/evening completion; depooping uses the weekly job completion.
|
||||||
- Key file: `packages/pet_feeding_and_jobs.yaml`.
|
- Key files: `packages/pet_feeding_and_jobs.yaml` and `packages/Job_Chore_Tracking/jobs_tracker_jobs.yaml`.
|
||||||
- Verification: Home Assistant accepted the configuration, both template sensors loaded, the feeding gauge reports 17 hours, the depooping gauge reports 156 hours, and all other Home view cards were preserved. Automated visual verification was unavailable because the screenshot tool is disabled.
|
- Verification: Home Assistant accepted the v1.1.0 gauge templates and V2.4 Jobs configuration. Invalid feeding attributes produced unavailable instead of 999999, the retained-state readiness guard passed, the stale restored depooping event failed the freshness guard, a subsequent minute refresh preserved the recovered timestamps, and the gauges reported 23 hours fed and 162 hours depooped. Automated visual verification was unavailable because the screenshot tool is disabled.
|
||||||
- Next step: None.
|
- Next step: None.
|
||||||
- Activity: 2026-07-21 - Created the project and selected the two-gauge Jobs-derived design.
|
- Activity: 2026-07-21 - Created the project and selected the two-gauge Jobs-derived design.
|
||||||
- Activity: 2026-07-21 - Added the Jobs-derived sensors, reloaded templates, and updated both Lounge Home gauges.
|
- Activity: 2026-07-21 - Added the Jobs-derived sensors, reloaded templates, and updated both Lounge Home gauges.
|
||||||
- Activity: 2026-07-21 - User confirmed completion.
|
- Activity: 2026-07-21 - User confirmed completion.
|
||||||
|
- Activity: 2026-07-21 - Fixed a restart race that erased retained feeding timestamps, blocked stale restored event states from creating false completions, removed the 999999 fallback, restored the last valid Jobs timestamps from recorder history, and verified 23-hour/162-hour gauge values.
|
||||||
|
|
||||||
## P011: Downstairs Occupancy Helper Cleanup
|
### P011: Downstairs Occupancy Helper Cleanup
|
||||||
|
|
||||||
- Objective: Eliminate the duplicate downstairs guest occupancy helper.
|
- Objective: Eliminate the duplicate downstairs guest occupancy helper.
|
||||||
- Canonical helper: `input_boolean.downstairs_flat_occupied`, which is YAML-managed and used by the downstairs lockout behavior.
|
- Canonical helper: `input_boolean.downstairs_flat_occupied`, which is YAML-managed and used by the downstairs lockout behavior.
|
||||||
@@ -130,9 +146,26 @@ Use this file as the source of truth for projects being worked on with OpenCode.
|
|||||||
- Activity: 2026-07-21 - Added occupancy-driven greying and tap blocking to the Lounge downstairs-flat light row.
|
- Activity: 2026-07-21 - Added occupancy-driven greying and tap blocking to the Lounge downstairs-flat light row.
|
||||||
- Activity: 2026-07-21 - User verified the Lounge lockout and confirmed completion.
|
- Activity: 2026-07-21 - User verified the Lounge lockout and confirmed completion.
|
||||||
|
|
||||||
## P012: EdgeRouter X SNMP Monitoring
|
### P012: EdgeRouter X SNMP Monitoring
|
||||||
|
|
||||||
- Objective: Restore EdgeRouter X monitoring on firmware v3.0.x using a compatible SNMP-based approach.
|
- Objective: Restore EdgeRouter X monitoring on firmware v3.0.x using a compatible SNMP-based approach.
|
||||||
- Current context: The existing EdgeRouter interface is not working after the router moved to firmware v3.0.x. Prior EdgeRouter/SNMP work was not recorded in project context.
|
- Current context: EdgeOS v2.1.9 is incompatible with EdgeOS v3.0.1. SNMP monitoring now provides major-interface traffic rates and router health data, and the three existing Internet traffic entities use the WAN SNMP derivatives. The old EdgeOS config entry is disabled and reversible; its config entry, entities, devices, and HACS files have not been deleted.
|
||||||
- Next step: Inspect the current configuration, entities, errors, and documentation without changing runtime behavior.
|
- Verification: Backup `1b6dd6d8` contains Home Assistant, the database, folders, and all apps on both backup agents with no failed components. Home Assistant configuration validation and restart succeeded. After disabling EdgeOS, all 31 SNMP entities and the three preserved Internet traffic entities remained available; all 210 old entities remained registered, with 208 unavailable and two having no current state.
|
||||||
|
- Next step: None.
|
||||||
- Activity: 2026-07-21 - Created the project and began the compatibility investigation.
|
- Activity: 2026-07-21 - Created the project and began the compatibility investigation.
|
||||||
|
- Activity: 2026-07-21 - Confirmed the EdgeOS v3.0.1 API incompatibility and validated read-only SNMPv2c access, interface indices, traffic counters, and health OIDs.
|
||||||
|
- Activity: 2026-07-21 - Added `packages/edgerouter_snmp.yaml`, migrated the existing Internet traffic templates to WAN SNMP derivatives, restarted Home Assistant, and verified live values.
|
||||||
|
- Activity: 2026-07-21 - Audited old EdgeOS dependencies, found no remaining YAML or dashboard references, disabled the config entry, and verified SNMP monitoring remained operational.
|
||||||
|
- Activity: 2026-07-21 - Documented `packages/edgerouter_snmp.yaml` as v1.0.0 using zorruno package and section headers; Home Assistant configuration validation passed.
|
||||||
|
- Activity: 2026-07-21 - Updated the SNMP package to v1.0.1, moved all router host values to `!secret edgerouter_host`, passed configuration validation, restarted Home Assistant, and verified all 31 SNMP entities plus the three Internet traffic entities remained available while EdgeOS stayed disabled.
|
||||||
|
- Activity: 2026-07-21 - User confirmed completion.
|
||||||
|
|
||||||
|
### P013: Full Home Assistant System Audit
|
||||||
|
|
||||||
|
- Objective: Perform a comprehensive read-only audit of the Home Assistant installation and present prioritized fixes, changes, and improvements.
|
||||||
|
- Audit report: [`P013_AUDIT_RESULTS.md`](P013_AUDIT_RESULTS.md)
|
||||||
|
- Scope: Configuration validity and deprecations; runtime and integration health; entities and devices; automations, scripts, scenes, and helpers; dashboards; MQTT, Zigbee, and ESPHome; backups and updates; recorder and performance; and security posture without exposing secrets.
|
||||||
|
- Current context: Audit created at the user's request. No Home Assistant state, registry, integration, dashboard, firmware, or configuration changes are authorized as part of the audit.
|
||||||
|
- Next step: Gather evidence through supported configuration files, MCP tools, `hab`, and `zigporter`, then present findings ordered by severity and impact.
|
||||||
|
- Activity: 2026-07-21 - Created the project and began the comprehensive read-only audit.
|
||||||
|
- Activity: 2026-07-22 - Resolved P013-F002 by sanitizing and archiving three retired ESPHome configurations; the user accepted the residual historical Git copies because the keys do not match current secrets or active devices.
|
||||||
|
|||||||
@@ -39,6 +39,3 @@ zha:
|
|||||||
salus_provider: true
|
salus_provider: true
|
||||||
inovelli_provider: true
|
inovelli_provider: true
|
||||||
thirdreality_provider: true
|
thirdreality_provider: true
|
||||||
|
|
||||||
device_tracker:
|
|
||||||
- platform: bluetooth_le_tracker
|
|
||||||
|
|||||||
@@ -0,0 +1,44 @@
|
|||||||
|
# Configure a default setup of Home Assistant (frontend, api, etc)
|
||||||
|
default_config:
|
||||||
|
|
||||||
|
# Helpers (default_config no longer loads input_number)
|
||||||
|
input_number:
|
||||||
|
|
||||||
|
frontend:
|
||||||
|
themes: !include_dir_merge_named themes
|
||||||
|
|
||||||
|
homeassistant:
|
||||||
|
customize: !include customize.yaml
|
||||||
|
packages: !include_dir_named packages
|
||||||
|
|
||||||
|
template: !include_dir_merge_list templates
|
||||||
|
group: !include_dir_merge_named group/
|
||||||
|
automation: !include_dir_merge_list automations/
|
||||||
|
script: !include_dir_merge_named scripts/
|
||||||
|
scene: !include scenes.yaml
|
||||||
|
sensor: !include_dir_merge_list sensor/
|
||||||
|
|
||||||
|
conversation:
|
||||||
|
|
||||||
|
logger:
|
||||||
|
default: info
|
||||||
|
|
||||||
|
zha:
|
||||||
|
enable_quirks: true
|
||||||
|
custom_quirks_path: /config/custom_zha_quirks/
|
||||||
|
device_config:
|
||||||
|
a4:c1:38:02:58:65:86:ed-1: # format: {ieee}-{endpoint_id}
|
||||||
|
type: "switch" # corrected device type
|
||||||
|
a4:c1:38:a1:db:1d:38:b7-1: # format: {ieee}-{endpoint_id}
|
||||||
|
type: "switch" # corrected device type
|
||||||
|
zigpy_config:
|
||||||
|
ota:
|
||||||
|
otau_directory: /config/zigpy_ota
|
||||||
|
ikea_provider: true
|
||||||
|
ledvance_provider: true
|
||||||
|
salus_provider: true
|
||||||
|
inovelli_provider: true
|
||||||
|
thirdreality_provider: true
|
||||||
|
|
||||||
|
device_tracker:
|
||||||
|
- platform: bluetooth_le_tracker
|
||||||
@@ -6,6 +6,7 @@
|
|||||||
# https://home.fox.co.nz/gitea/zorruno/zorruno-homeassistant/src/branch/master/esphome/esp-centralstairs-bottom.yaml
|
# https://home.fox.co.nz/gitea/zorruno/zorruno-homeassistant/src/branch/master/esphome/esp-centralstairs-bottom.yaml
|
||||||
#:########################################################################################:#
|
#:########################################################################################:#
|
||||||
# VERSIONS:
|
# VERSIONS:
|
||||||
|
# V3.9 2026-07-21 Added retained MQTT status mirroring for HA-independent two-way switching
|
||||||
# V3.8 2026-06-09 Added Downstairs Lights Bulk Activation virtual switch toggled by Button 1 double-click
|
# V3.8 2026-06-09 Added Downstairs Lights Bulk Activation virtual switch toggled by Button 1 double-click
|
||||||
# V3.7 2025-09-24 Upload to this device
|
# V3.7 2025-09-24 Upload to this device
|
||||||
# V3.5 2025-07-24 YAML tidyups
|
# V3.5 2025-07-24 YAML tidyups
|
||||||
@@ -83,7 +84,7 @@ substitutions:
|
|||||||
|
|
||||||
# Project Naming
|
# Project Naming
|
||||||
project_name: "Zemismart Technologies.KS-811 Double" # Project Details
|
project_name: "Zemismart Technologies.KS-811 Double" # Project Details
|
||||||
project_version: "v2.1" # Project V denotes release of yaml file, allowing checking of deployed vs latest version
|
project_version: "v3.9" # Project V denotes release of yaml file, allowing checking of deployed vs latest version
|
||||||
|
|
||||||
# MQTT Controls
|
# MQTT Controls
|
||||||
mqtt_command_main_topic: !secret mqtt_command_main_topic
|
mqtt_command_main_topic: !secret mqtt_command_main_topic
|
||||||
@@ -184,19 +185,19 @@ mqtt:
|
|||||||
then:
|
then:
|
||||||
- if:
|
- if:
|
||||||
condition:
|
condition:
|
||||||
lambda: 'return id(Relay_1).state;'
|
lambda: "return id(Relay_1).state;"
|
||||||
then:
|
then:
|
||||||
- mqtt.publish:
|
- mqtt.publish:
|
||||||
topic: "${mqtt_status_topic_1}"
|
topic: "${mqtt_status_topic_1}"
|
||||||
payload: "${mqtt_command_ON}"
|
payload: "${mqtt_command_ON}"
|
||||||
qos: 0
|
qos: 1
|
||||||
retain: false
|
retain: true
|
||||||
else:
|
else:
|
||||||
- mqtt.publish:
|
- mqtt.publish:
|
||||||
topic: "${mqtt_status_topic_1}"
|
topic: "${mqtt_status_topic_1}"
|
||||||
payload: "${mqtt_command_OFF}"
|
payload: "${mqtt_command_OFF}"
|
||||||
qos: 0
|
qos: 1
|
||||||
retain: false
|
retain: true
|
||||||
|
|
||||||
on_message:
|
on_message:
|
||||||
# Relay 1: only turn ON if currently OFF
|
# Relay 1: only turn ON if currently OFF
|
||||||
@@ -241,6 +242,26 @@ mqtt:
|
|||||||
then:
|
then:
|
||||||
- switch.turn_off: Relay_2
|
- switch.turn_off: Relay_2
|
||||||
|
|
||||||
|
# Relay 2 is a mirror of the remote physical Stair Footer Lights relay.
|
||||||
|
- topic: "${mqtt_status_topic_2}"
|
||||||
|
payload: "${mqtt_command_ON}"
|
||||||
|
then:
|
||||||
|
- if:
|
||||||
|
condition:
|
||||||
|
not:
|
||||||
|
- switch.is_on: Relay_2
|
||||||
|
then:
|
||||||
|
- switch.turn_on: Relay_2
|
||||||
|
|
||||||
|
- topic: "${mqtt_status_topic_2}"
|
||||||
|
payload: "${mqtt_command_OFF}"
|
||||||
|
then:
|
||||||
|
- if:
|
||||||
|
condition:
|
||||||
|
- switch.is_on: Relay_2
|
||||||
|
then:
|
||||||
|
- switch.turn_off: Relay_2
|
||||||
|
|
||||||
#:########################################################################################:#
|
#:########################################################################################:#
|
||||||
# STATUS LED:
|
# STATUS LED:
|
||||||
# https://esphome.io/components/status_led.html
|
# https://esphome.io/components/status_led.html
|
||||||
@@ -298,7 +319,7 @@ binary_sensor:
|
|||||||
# Toggle the remote Footer Lights via COMMAND topic, based on our mirrored state
|
# Toggle the remote Footer Lights via COMMAND topic, based on our mirrored state
|
||||||
- if:
|
- if:
|
||||||
condition:
|
condition:
|
||||||
lambda: 'return id(Relay_2).state;'
|
lambda: "return id(Relay_2).state;"
|
||||||
then:
|
then:
|
||||||
- mqtt.publish:
|
- mqtt.publish:
|
||||||
topic: "${mqtt_command_topic_2}"
|
topic: "${mqtt_command_topic_2}"
|
||||||
@@ -332,12 +353,14 @@ switch:
|
|||||||
- mqtt.publish:
|
- mqtt.publish:
|
||||||
topic: "${mqtt_status_topic_1}"
|
topic: "${mqtt_status_topic_1}"
|
||||||
payload: "${mqtt_command_ON}"
|
payload: "${mqtt_command_ON}"
|
||||||
retain: false
|
qos: 1
|
||||||
|
retain: true
|
||||||
on_turn_off:
|
on_turn_off:
|
||||||
- mqtt.publish:
|
- mqtt.publish:
|
||||||
topic: "${mqtt_status_topic_1}"
|
topic: "${mqtt_status_topic_1}"
|
||||||
payload: "${mqtt_command_OFF}"
|
payload: "${mqtt_command_OFF}"
|
||||||
retain: false
|
qos: 1
|
||||||
|
retain: true
|
||||||
|
|
||||||
- platform: gpio
|
- platform: gpio
|
||||||
name: "Relay 2: ${switch_2_name}"
|
name: "Relay 2: ${switch_2_name}"
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
# https://home.fox.co.nz/gitea/zorruno/zorruno-homeassistant/src/branch/master/esphome/esp-centralstairs-top.yaml
|
# https://home.fox.co.nz/gitea/zorruno/zorruno-homeassistant/src/branch/master/esphome/esp-centralstairs-top.yaml
|
||||||
#:########################################################################################:#
|
#:########################################################################################:#
|
||||||
# VERSIONS:
|
# VERSIONS:
|
||||||
|
# V3.9 2026-07-21 Added retained MQTT status mirroring for HA-independent two-way switching
|
||||||
# V3.8 2026-04-26 Added Downstairs Lights Bulk Activation virtual switch toggled by Button 1 double-click
|
# V3.8 2026-04-26 Added Downstairs Lights Bulk Activation virtual switch toggled by Button 1 double-click
|
||||||
# V3.7 2025-09-24 Upload to this device
|
# V3.7 2025-09-24 Upload to this device
|
||||||
# V3.5 2025-07-24 YAML tidyups
|
# V3.5 2025-07-24 YAML tidyups
|
||||||
@@ -83,7 +84,7 @@ substitutions:
|
|||||||
|
|
||||||
# Project Naming
|
# Project Naming
|
||||||
project_name: "Zemismart Technologies.KS-811 Double" # Project Details
|
project_name: "Zemismart Technologies.KS-811 Double" # Project Details
|
||||||
project_version: "v2.1" # Project V denotes release of yaml file, allowing checking of deployed vs latest version
|
project_version: "v3.9" # Project V denotes release of yaml file, allowing checking of deployed vs latest version
|
||||||
|
|
||||||
# MQTT Controls
|
# MQTT Controls
|
||||||
mqtt_command_main_topic: !secret mqtt_command_main_topic
|
mqtt_command_main_topic: !secret mqtt_command_main_topic
|
||||||
@@ -184,19 +185,19 @@ mqtt:
|
|||||||
then:
|
then:
|
||||||
- if:
|
- if:
|
||||||
condition:
|
condition:
|
||||||
lambda: 'return id(Relay_2).state;'
|
lambda: "return id(Relay_2).state;"
|
||||||
then:
|
then:
|
||||||
- mqtt.publish:
|
- mqtt.publish:
|
||||||
topic: "${mqtt_status_topic_2}"
|
topic: "${mqtt_status_topic_2}"
|
||||||
payload: "${mqtt_command_ON}"
|
payload: "${mqtt_command_ON}"
|
||||||
qos: 0
|
qos: 1
|
||||||
retain: false
|
retain: true
|
||||||
else:
|
else:
|
||||||
- mqtt.publish:
|
- mqtt.publish:
|
||||||
topic: "${mqtt_status_topic_2}"
|
topic: "${mqtt_status_topic_2}"
|
||||||
payload: "${mqtt_command_OFF}"
|
payload: "${mqtt_command_OFF}"
|
||||||
qos: 0
|
qos: 1
|
||||||
retain: false
|
retain: true
|
||||||
|
|
||||||
on_message:
|
on_message:
|
||||||
# Relay 1: only turn ON if currently OFF
|
# Relay 1: only turn ON if currently OFF
|
||||||
@@ -241,6 +242,26 @@ mqtt:
|
|||||||
then:
|
then:
|
||||||
- switch.turn_off: Relay_2
|
- switch.turn_off: Relay_2
|
||||||
|
|
||||||
|
# Relay 1 is a mirror of the remote physical Main Stair Lights relay.
|
||||||
|
- topic: "${mqtt_status_topic_1}"
|
||||||
|
payload: "${mqtt_command_ON}"
|
||||||
|
then:
|
||||||
|
- if:
|
||||||
|
condition:
|
||||||
|
not:
|
||||||
|
- switch.is_on: Relay_1
|
||||||
|
then:
|
||||||
|
- switch.turn_on: Relay_1
|
||||||
|
|
||||||
|
- topic: "${mqtt_status_topic_1}"
|
||||||
|
payload: "${mqtt_command_OFF}"
|
||||||
|
then:
|
||||||
|
- if:
|
||||||
|
condition:
|
||||||
|
- switch.is_on: Relay_1
|
||||||
|
then:
|
||||||
|
- switch.turn_off: Relay_1
|
||||||
|
|
||||||
#:########################################################################################:#
|
#:########################################################################################:#
|
||||||
# STATUS LED:
|
# STATUS LED:
|
||||||
# https://esphome.io/components/status_led.html
|
# https://esphome.io/components/status_led.html
|
||||||
@@ -286,7 +307,7 @@ binary_sensor:
|
|||||||
then:
|
then:
|
||||||
- if:
|
- if:
|
||||||
condition:
|
condition:
|
||||||
lambda: 'return id(Relay_1).state;'
|
lambda: "return id(Relay_1).state;"
|
||||||
then:
|
then:
|
||||||
- mqtt.publish:
|
- mqtt.publish:
|
||||||
topic: "${mqtt_command_topic_1}"
|
topic: "${mqtt_command_topic_1}"
|
||||||
@@ -338,9 +359,11 @@ switch:
|
|||||||
- mqtt.publish:
|
- mqtt.publish:
|
||||||
topic: "${mqtt_status_topic_2}"
|
topic: "${mqtt_status_topic_2}"
|
||||||
payload: "${mqtt_command_ON}"
|
payload: "${mqtt_command_ON}"
|
||||||
retain: false
|
qos: 1
|
||||||
|
retain: true
|
||||||
on_turn_off:
|
on_turn_off:
|
||||||
- mqtt.publish:
|
- mqtt.publish:
|
||||||
topic: "${mqtt_status_topic_2}"
|
topic: "${mqtt_status_topic_2}"
|
||||||
payload: "${mqtt_command_OFF}"
|
payload: "${mqtt_command_OFF}"
|
||||||
retain: false
|
qos: 1
|
||||||
|
retain: true
|
||||||
|
|||||||
@@ -1,507 +0,0 @@
|
|||||||
#:########################################################################################:#
|
|
||||||
# TITLE: LOUNGE MIDDLE LIGHTSWITCH
|
|
||||||
# zorruno.com layout v1.1 2026
|
|
||||||
#:########################################################################################:#
|
|
||||||
# REPO:
|
|
||||||
# https://home.fox.co.nz/gitea/zorruno/zorruno-homeassistant/src/branch/master/esphome/esp-loungemiddleswitch.yaml
|
|
||||||
#:########################################################################################:#
|
|
||||||
# VERSIONS:
|
|
||||||
# V3.6 2026-03-11 Updated yaml to zorruno layout V1.1
|
|
||||||
# V3.5 2025-07-28 YAML tidyups
|
|
||||||
#:########################################################################################:#
|
|
||||||
# HARDWARE:
|
|
||||||
# - Zemismart KS-811 Triple push button
|
|
||||||
# - ESP8266 based
|
|
||||||
# - pinout/schematic:
|
|
||||||
# https://community.home-assistant.io/t/zemismart-ks-811-working-with-esphome/
|
|
||||||
# - Relay 1 output on GPIO13
|
|
||||||
# - Relay 2 output on GPIO12
|
|
||||||
# - Relay 3 output on GPIO14
|
|
||||||
# - Button 1 input on GPIO16
|
|
||||||
# - Button 2 input on GPIO5
|
|
||||||
# - Button 3 input on GPIO4
|
|
||||||
# - Status LED on GPIO2
|
|
||||||
#:########################################################################################:#
|
|
||||||
# OPERATION NOTES:
|
|
||||||
# - Button 1:
|
|
||||||
# - Online with HA Loss manual override ON and HA Loss Test OFF:
|
|
||||||
# - Uses Home Assistant last-scene logic
|
|
||||||
# - If last scene is not "All Off", set "All Off"
|
|
||||||
# - If last scene is "All Off", set "Bright"
|
|
||||||
# - Offline/test with HA Loss manual override ON:
|
|
||||||
# - Toggles Relay_1 locally
|
|
||||||
# - Button 2:
|
|
||||||
# - Online with HA Loss manual override ON and HA Loss Test OFF:
|
|
||||||
# - Sets Home Assistant scene "Movie"
|
|
||||||
# - Offline/test with HA Loss manual override ON:
|
|
||||||
# - Toggles Relay_2 locally
|
|
||||||
# - Button 3:
|
|
||||||
# - Online with HA Loss manual override ON and HA Loss Test OFF:
|
|
||||||
# - Sets Home Assistant scene "Reading On/Off"
|
|
||||||
# - Offline/test with HA Loss manual override ON:
|
|
||||||
# - Toggles Relay_3 locally
|
|
||||||
# - On API connect:
|
|
||||||
# - If HA Loss Test is OFF, Relay_1 and Relay_2 follow HA Loss manual override state
|
|
||||||
# - If HA Loss Test is ON, relay override is not applied
|
|
||||||
# - HA Loss manual override defaults ON and persists across restarts
|
|
||||||
# - HA Loss Test defaults OFF and can force offline/test behaviour even when HA is connected
|
|
||||||
#:########################################################################################:#
|
|
||||||
# OFFLINE NOTES:
|
|
||||||
# a) Home Assistant offline (network online):
|
|
||||||
# - Device switches to manual/offline behaviour
|
|
||||||
# - Buttons still operate local relays if HA Loss manual override is ON
|
|
||||||
# - Home Assistant scene calls are unavailable
|
|
||||||
# b) MQTT offline (or HA/MQTT offline):
|
|
||||||
# - This yaml does not depend on MQTT for its button logic
|
|
||||||
# - If Home Assistant/API is offline, device falls back to manual/offline behaviour
|
|
||||||
# - Buttons still operate local relays if HA Loss manual override is ON
|
|
||||||
# c) Entire WiFi/Network offline:
|
|
||||||
# - Device operates in local manual/offline behaviour only
|
|
||||||
# - Buttons still operate local relays if HA Loss manual override is ON
|
|
||||||
# - Accurate time is not needed; SNTP is not required for operation
|
|
||||||
#:########################################################################################:#
|
|
||||||
|
|
||||||
#:########################################################################################:#
|
|
||||||
# SUBSTITUTIONS:
|
|
||||||
# Specific device variable substitutions
|
|
||||||
# If NOT using a secrets file, just replace these with the passwords etc (in quotes)
|
|
||||||
#:########################################################################################:#
|
|
||||||
substitutions:
|
|
||||||
# Device Naming
|
|
||||||
device_name: "esp-loungemiddleswitch"
|
|
||||||
friendly_name: "Lounge Middle Lightswitch (3)"
|
|
||||||
description_comment: "Lounge Main Lightswitch using a Zemismart KS-811 Triple Push Button. Downlights South (1), Downlights Middle (2), Downlights North (3) (Layout V1.1)"
|
|
||||||
device_area: "Lounge" # Allows ESP device to be automatically linked to an 'Area' in Home Assistant.
|
|
||||||
|
|
||||||
# Project Naming
|
|
||||||
project_name: "Zemismart Technologies.KS-811 Triple" # Project Details
|
|
||||||
project_version: "v3.6" # Project V denotes release of yaml file, allowing checking of deployed vs latest version
|
|
||||||
|
|
||||||
# Passwords & Secrets
|
|
||||||
api_key: !secret esp-api_key # unfortunately you can't use substitutions inside secrets names
|
|
||||||
ota_pass: !secret esp-ota_pass # unfortunately you can't use substitutions inside secrets names
|
|
||||||
static_ip_address: !secret esp-loungemiddleswitch_ip
|
|
||||||
|
|
||||||
# If we are changing IP addresses, you must update the current IP address here, otherwise it remains
|
|
||||||
# Don't forget to switch it back when changed.
|
|
||||||
current_ip_address: ${static_ip_address}
|
|
||||||
|
|
||||||
# Device Settings
|
|
||||||
log_level: "INFO" # Define logging level: NONE, ERROR, WARN, INFO, DEBUG (Default), VERBOSE, VERY_VERBOSE
|
|
||||||
update_interval: "60s" # update time for for general sensors etc
|
|
||||||
|
|
||||||
# MQTT LOCAL Controls
|
|
||||||
#mqtt_device_name: "lounge-main-lights"
|
|
||||||
#mqtt_local_command_topic: "${mqtt_command_main_topic}/${mqtt_device_name}" # Topic we will use to command this locally without HA
|
|
||||||
#mqtt_local_status_topic: "${mqtt_status_main_topic}/${mqtt_device_name}" # Topic we will use to view status locally without HA
|
|
||||||
|
|
||||||
# MQTT REMOTE Controls
|
|
||||||
|
|
||||||
# Switch/Relay/Button Naming & Icons
|
|
||||||
#relay_icon: "mdi:lightbulb-group"
|
|
||||||
switch_1_name: "Downlights South" # Lights are connected via DMX dimmer (another esphome controller) (4 Downlights)
|
|
||||||
switch_2_name: "Downlights Middle" # Lights are connected via DMX dimmer (another esphome controller) (6 Downlights)
|
|
||||||
switch_3_name: "Downlights North" # Lights are directly connected (2 downlights)
|
|
||||||
|
|
||||||
#:########################################################################################:#
|
|
||||||
# PACKAGES:
|
|
||||||
# Included Common Packages
|
|
||||||
# https://esphome.io/components/packages.html
|
|
||||||
#:########################################################################################:#
|
|
||||||
packages:
|
|
||||||
#### WIFI, Network (Static/DHCP/IPV6 etc), Fallback AP, Safemode ####
|
|
||||||
common_wifi: !include
|
|
||||||
file: common/network_common.yaml
|
|
||||||
vars:
|
|
||||||
local_device_name: "${device_name}"
|
|
||||||
local_static_ip_address: "${static_ip_address}"
|
|
||||||
local_current_ip_address: "${current_ip_address}"
|
|
||||||
local_ota_pass: "${ota_pass}"
|
|
||||||
|
|
||||||
#### HOME ASSISTANT API (choose encryption or no encryption options) ####
|
|
||||||
common_api: !include
|
|
||||||
file: common/api_common.yaml
|
|
||||||
#file: common/api_common_noencryption.yaml
|
|
||||||
vars:
|
|
||||||
local_api_key: "${api_key}"
|
|
||||||
|
|
||||||
#### MQTT ####
|
|
||||||
common_mqtt: !include
|
|
||||||
file: common/mqtt_common.yaml
|
|
||||||
vars:
|
|
||||||
local_device_name: "${device_name}"
|
|
||||||
|
|
||||||
#### WEB PORTAL ####
|
|
||||||
#common_webportal: !include common/webportal_common.yaml
|
|
||||||
|
|
||||||
#### SNTP (Only use if you want/need accurate timeclocks) ####
|
|
||||||
#common_sntp: !include common/sntp_common.yaml
|
|
||||||
|
|
||||||
#### DIAGNOSTICS Sensors ####
|
|
||||||
diag_basic: !include common/include_basic_diag_sensors.yaml
|
|
||||||
diag_more: !include common/include_more_diag_sensors.yaml
|
|
||||||
#diag_debug: !include common/include_debug_diag_sensors.yaml
|
|
||||||
#diag_resetcount: !include common/include_resetcount_diag_sensors.yaml
|
|
||||||
|
|
||||||
#:########################################################################################:#
|
|
||||||
# ESPHOME:
|
|
||||||
# https://esphome.io/components/esphome.html
|
|
||||||
#:########################################################################################:#
|
|
||||||
esphome:
|
|
||||||
name: "${device_name}"
|
|
||||||
friendly_name: "${friendly_name}"
|
|
||||||
comment: "${description_comment}" # Appears on the esphome page in HA
|
|
||||||
area: "${device_area}"
|
|
||||||
project:
|
|
||||||
name: "${project_name}"
|
|
||||||
version: "${project_version}"
|
|
||||||
|
|
||||||
#:########################################################################################:#
|
|
||||||
# HOME ASSISTANT API:
|
|
||||||
#:########################################################################################:#
|
|
||||||
api:
|
|
||||||
on_client_connected:
|
|
||||||
then:
|
|
||||||
- logger.log:
|
|
||||||
level: INFO
|
|
||||||
format: "API connected: override=%s, ha_loss_test=%s"
|
|
||||||
args:
|
|
||||||
- 'id(ha_loss_override).state ? "true" : "false"'
|
|
||||||
- 'id(ha_loss_test).state ? "true" : "false"'
|
|
||||||
- if:
|
|
||||||
condition:
|
|
||||||
switch.is_on: ha_loss_test
|
|
||||||
then:
|
|
||||||
- logger.log:
|
|
||||||
level: WARN
|
|
||||||
format: "HA Loss Test is ON: not applying relay override on connect"
|
|
||||||
else:
|
|
||||||
- if:
|
|
||||||
condition:
|
|
||||||
switch.is_on: ha_loss_override
|
|
||||||
then:
|
|
||||||
- switch.turn_on: Relay_1
|
|
||||||
- switch.turn_on: Relay_2
|
|
||||||
else:
|
|
||||||
- switch.turn_off: Relay_1
|
|
||||||
- switch.turn_off: Relay_2
|
|
||||||
|
|
||||||
on_client_disconnected:
|
|
||||||
then:
|
|
||||||
- logger.log:
|
|
||||||
level: WARN
|
|
||||||
format: "API disconnected: switching to manual/offline behavior"
|
|
||||||
|
|
||||||
#:########################################################################################:#
|
|
||||||
# ESP PLATFORM AND FRAMEWORK:
|
|
||||||
# https://esphome.io/components/esp8266.html
|
|
||||||
#:########################################################################################:#
|
|
||||||
esp8266:
|
|
||||||
board: esp01_1m
|
|
||||||
early_pin_init: false # Initialise pins early to known values. Recommended false where switches are involved. Defaults to True.
|
|
||||||
board_flash_mode: dout # Default is dout
|
|
||||||
|
|
||||||
#:########################################################################################:#
|
|
||||||
# LOGGER:
|
|
||||||
# https://esphome.io/components/logger.html
|
|
||||||
#:########################################################################################:#
|
|
||||||
logger:
|
|
||||||
level: "${log_level}" # INFO Level suggested, or DEBUG for testing
|
|
||||||
#baud_rate: 0 # set to 0 for no logging via UART, needed if you are using it for other serial things (eg PZEM)
|
|
||||||
#esp8266_store_log_strings_in_flash: false
|
|
||||||
#tx_buffer_size: 64
|
|
||||||
|
|
||||||
#:########################################################################################:#
|
|
||||||
# STATUS LED:
|
|
||||||
# https://esphome.io/components/status_led.html
|
|
||||||
#:########################################################################################:#
|
|
||||||
status_led:
|
|
||||||
pin:
|
|
||||||
number: GPIO2
|
|
||||||
inverted: true
|
|
||||||
|
|
||||||
#:########################################################################################:#
|
|
||||||
# BINARY SENSORS:
|
|
||||||
# https://esphome.io/components/binary_sensor/
|
|
||||||
#:########################################################################################:#
|
|
||||||
binary_sensor:
|
|
||||||
# GPIO16 for KS-811 first button UNLESS
|
|
||||||
# it is a single button KS-811 in which case it is GPIO00
|
|
||||||
- platform: gpio
|
|
||||||
pin:
|
|
||||||
number: GPIO16
|
|
||||||
mode: INPUT
|
|
||||||
inverted: true
|
|
||||||
use_interrupt: false # This pin doesn't support interrupts so use polling. Only suppresses a warning.
|
|
||||||
name: "Button 1: ${switch_1_name}"
|
|
||||||
on_multi_click:
|
|
||||||
# --- Single short click ---
|
|
||||||
# Online (and override ON, and NOT test mode): use HA's last scene to decide.
|
|
||||||
# Offline (override ON): toggle physical Relay_1
|
|
||||||
# Test mode (override ON): behave like offline even if HA is connected
|
|
||||||
- timing:
|
|
||||||
- ON for at most 0.5s
|
|
||||||
- OFF for at least 0.5s
|
|
||||||
then:
|
|
||||||
- if:
|
|
||||||
condition:
|
|
||||||
and:
|
|
||||||
- switch.is_on: ha_loss_override
|
|
||||||
- api.connected:
|
|
||||||
- not:
|
|
||||||
switch.is_on: ha_loss_test
|
|
||||||
then:
|
|
||||||
- if:
|
|
||||||
condition:
|
|
||||||
lambda: |-
|
|
||||||
// Defensive: treat empty/unknown as "not All Off" so we go to a safe "All Off".
|
|
||||||
auto s = id(ha_last_scene).state;
|
|
||||||
if (s.empty() || s == "unknown" || s == "unavailable") return true;
|
|
||||||
return s != "All Off";
|
|
||||||
then:
|
|
||||||
- homeassistant.service:
|
|
||||||
service: script.lounge_set_scene
|
|
||||||
data:
|
|
||||||
scene_name: "All Off"
|
|
||||||
- logger.log:
|
|
||||||
level: INFO
|
|
||||||
format: "BTN1: Single click (online) -> Last scene was not 'All Off' -> Set 'All Off'"
|
|
||||||
else:
|
|
||||||
- homeassistant.service:
|
|
||||||
service: script.lounge_set_scene
|
|
||||||
data:
|
|
||||||
scene_name: "Bright"
|
|
||||||
- logger.log:
|
|
||||||
level: INFO
|
|
||||||
format: "BTN1: Single click (online) -> Last scene was 'All Off' -> Set 'Bright'"
|
|
||||||
else:
|
|
||||||
- if:
|
|
||||||
condition:
|
|
||||||
and:
|
|
||||||
- switch.is_on: ha_loss_override
|
|
||||||
- or:
|
|
||||||
- not:
|
|
||||||
api.connected:
|
|
||||||
- switch.is_on: ha_loss_test
|
|
||||||
then:
|
|
||||||
- if:
|
|
||||||
condition:
|
|
||||||
lambda: |-
|
|
||||||
return id(Relay_1).state;
|
|
||||||
then:
|
|
||||||
- switch.turn_off: Relay_1
|
|
||||||
- logger.log:
|
|
||||||
level: INFO
|
|
||||||
format: "BTN1: Single click (offline/test) -> Relay_1 OFF"
|
|
||||||
else:
|
|
||||||
- switch.turn_on: Relay_1
|
|
||||||
- logger.log:
|
|
||||||
level: INFO
|
|
||||||
format: "BTN1: Single click (offline/test) -> Relay_1 ON"
|
|
||||||
else:
|
|
||||||
- logger.log:
|
|
||||||
level: INFO
|
|
||||||
format: "BTN1: Single click ignored (override OFF)"
|
|
||||||
|
|
||||||
# GPIO05 for second button (only for KS-811-2 Double or -3 Triple)
|
|
||||||
- platform: gpio
|
|
||||||
pin:
|
|
||||||
number: GPIO5
|
|
||||||
mode: INPUT
|
|
||||||
inverted: true
|
|
||||||
name: "Button 2: ${switch_2_name}"
|
|
||||||
on_multi_click:
|
|
||||||
# --- Single short click ---
|
|
||||||
# Online (override ON, NOT test mode): set scene
|
|
||||||
# Offline/test (override ON): toggle Relay_2
|
|
||||||
- timing:
|
|
||||||
- ON for at most 0.5s
|
|
||||||
- OFF for at least 0.5s
|
|
||||||
then:
|
|
||||||
- if:
|
|
||||||
condition:
|
|
||||||
and:
|
|
||||||
- switch.is_on: ha_loss_override
|
|
||||||
- api.connected:
|
|
||||||
- not:
|
|
||||||
switch.is_on: ha_loss_test
|
|
||||||
then:
|
|
||||||
- homeassistant.service:
|
|
||||||
service: script.lounge_set_scene
|
|
||||||
data:
|
|
||||||
scene_name: "Movie"
|
|
||||||
- logger.log:
|
|
||||||
level: INFO
|
|
||||||
format: "BTN2: Single click (online) -> Set 'Movie' Scene"
|
|
||||||
else:
|
|
||||||
- if:
|
|
||||||
condition:
|
|
||||||
and:
|
|
||||||
- switch.is_on: ha_loss_override
|
|
||||||
- or:
|
|
||||||
- not:
|
|
||||||
api.connected:
|
|
||||||
- switch.is_on: ha_loss_test
|
|
||||||
then:
|
|
||||||
- if:
|
|
||||||
condition:
|
|
||||||
lambda: |-
|
|
||||||
return id(Relay_2).state;
|
|
||||||
then:
|
|
||||||
- switch.turn_off: Relay_2
|
|
||||||
- logger.log:
|
|
||||||
level: INFO
|
|
||||||
format: "BTN2: Single click (offline/test) -> Relay_2 OFF"
|
|
||||||
else:
|
|
||||||
- switch.turn_on: Relay_2
|
|
||||||
- logger.log:
|
|
||||||
level: INFO
|
|
||||||
format: "BTN2: Single click (offline/test) -> Relay_2 ON"
|
|
||||||
else:
|
|
||||||
- logger.log:
|
|
||||||
level: INFO
|
|
||||||
format: "BTN2: Single click ignored (override OFF)"
|
|
||||||
|
|
||||||
# GPIO04 for third button (only for KS-811-3 Triple)
|
|
||||||
- platform: gpio
|
|
||||||
pin:
|
|
||||||
number: GPIO4
|
|
||||||
mode: INPUT
|
|
||||||
inverted: true
|
|
||||||
name: "Button 3: ${switch_3_name}"
|
|
||||||
on_multi_click:
|
|
||||||
# --- Single short click ---
|
|
||||||
# Online (override ON, NOT test mode): set scene
|
|
||||||
# Offline/test (override ON): toggle Relay_3
|
|
||||||
- timing:
|
|
||||||
- ON for at most 0.5s
|
|
||||||
- OFF for at least 0.5s
|
|
||||||
then:
|
|
||||||
- if:
|
|
||||||
condition:
|
|
||||||
and:
|
|
||||||
- switch.is_on: ha_loss_override
|
|
||||||
- api.connected:
|
|
||||||
- not:
|
|
||||||
switch.is_on: ha_loss_test
|
|
||||||
then:
|
|
||||||
- homeassistant.service:
|
|
||||||
service: script.lounge_set_scene
|
|
||||||
data:
|
|
||||||
scene_name: "Reading On/Off"
|
|
||||||
- logger.log:
|
|
||||||
level: INFO
|
|
||||||
format: "BTN3: Single click (online) -> Set 'Reading On/Off' Scene"
|
|
||||||
else:
|
|
||||||
- if:
|
|
||||||
condition:
|
|
||||||
and:
|
|
||||||
- switch.is_on: ha_loss_override
|
|
||||||
- or:
|
|
||||||
- not:
|
|
||||||
api.connected:
|
|
||||||
- switch.is_on: ha_loss_test
|
|
||||||
then:
|
|
||||||
- if:
|
|
||||||
condition:
|
|
||||||
lambda: |-
|
|
||||||
return id(Relay_3).state;
|
|
||||||
then:
|
|
||||||
- switch.turn_off: Relay_3
|
|
||||||
- logger.log:
|
|
||||||
level: INFO
|
|
||||||
format: "BTN3: Single click (offline/test) -> Relay_3 OFF"
|
|
||||||
else:
|
|
||||||
- switch.turn_on: Relay_3
|
|
||||||
- logger.log:
|
|
||||||
level: INFO
|
|
||||||
format: "BTN3: Single click (offline/test) -> Relay_3 ON"
|
|
||||||
else:
|
|
||||||
- logger.log:
|
|
||||||
level: INFO
|
|
||||||
format: "BTN3: Single click ignored (override OFF)"
|
|
||||||
|
|
||||||
#:########################################################################################:#
|
|
||||||
# TEXT SENSOR COMPONENT:
|
|
||||||
# https://esphome.io/components/text_sensor/
|
|
||||||
#:########################################################################################:#
|
|
||||||
text_sensor:
|
|
||||||
- platform: homeassistant
|
|
||||||
id: ha_last_scene
|
|
||||||
entity_id: input_text.lounge_last_scene
|
|
||||||
on_value:
|
|
||||||
- logger.log:
|
|
||||||
level: DEBUG
|
|
||||||
format: "HA last scene now: %s"
|
|
||||||
args: ["x.c_str()"]
|
|
||||||
|
|
||||||
#:########################################################################################:#
|
|
||||||
# SWITCH COMPONENT:
|
|
||||||
# https://esphome.io/components/switch/
|
|
||||||
#:########################################################################################:#
|
|
||||||
switch:
|
|
||||||
# GPIO13 for KS-811 first button UNLESS it is KS-811-1 then it is GPIO12
|
|
||||||
- platform: gpio
|
|
||||||
name: "Relay 1: ${switch_1_name}"
|
|
||||||
pin: GPIO13
|
|
||||||
id: Relay_1
|
|
||||||
restore_mode: RESTORE_DEFAULT_ON
|
|
||||||
|
|
||||||
# GPIO12 for second relay (only for KS-811-2 Double or -3 Triple)
|
|
||||||
- platform: gpio
|
|
||||||
name: "Relay 2: ${switch_2_name}"
|
|
||||||
pin: GPIO12
|
|
||||||
id: Relay_2
|
|
||||||
restore_mode: RESTORE_DEFAULT_ON
|
|
||||||
|
|
||||||
# GPIO14 for third relay (only for KS-811-3 Triple)
|
|
||||||
- platform: gpio
|
|
||||||
name: "Relay 3: ${switch_3_name}"
|
|
||||||
pin: GPIO14
|
|
||||||
id: Relay_3
|
|
||||||
|
|
||||||
# HA Loss manual override (default ON; persists across restarts)
|
|
||||||
- platform: template
|
|
||||||
name: "HA Loss manual override"
|
|
||||||
id: ha_loss_override
|
|
||||||
optimistic: true
|
|
||||||
restore_mode: ALWAYS_ON
|
|
||||||
|
|
||||||
# HA Loss Test (default OFF): when ON, force "offline behavior" even if HA is connected
|
|
||||||
- platform: template
|
|
||||||
name: "HA Loss Test"
|
|
||||||
id: ha_loss_test
|
|
||||||
optimistic: true
|
|
||||||
restore_mode: ALWAYS_OFF
|
|
||||||
on_turn_on:
|
|
||||||
- logger.log:
|
|
||||||
level: WARN
|
|
||||||
format: "HA Loss Test turned ON: forcing offline/test behavior for buttons"
|
|
||||||
on_turn_off:
|
|
||||||
- logger.log:
|
|
||||||
level: INFO
|
|
||||||
format: "HA Loss Test turned OFF: forcing Relay_1 and Relay_2 ON, then restoring last scene in 30s"
|
|
||||||
- switch.turn_on: Relay_1
|
|
||||||
- switch.turn_on: Relay_2
|
|
||||||
- delay: 30s
|
|
||||||
- if:
|
|
||||||
condition:
|
|
||||||
and:
|
|
||||||
- api.connected:
|
|
||||||
- lambda: |-
|
|
||||||
auto s = id(ha_last_scene).state;
|
|
||||||
return !(s.empty() || s == "unknown" || s == "unavailable");
|
|
||||||
then:
|
|
||||||
- homeassistant.service:
|
|
||||||
service: script.lounge_set_scene
|
|
||||||
data:
|
|
||||||
scene_name: !lambda |-
|
|
||||||
return id(ha_last_scene).state;
|
|
||||||
- logger.log:
|
|
||||||
level: INFO
|
|
||||||
format: "HA Loss Test OFF: restored last scene: %s"
|
|
||||||
args: ['id(ha_last_scene).state.c_str()']
|
|
||||||
else:
|
|
||||||
- logger.log:
|
|
||||||
level: WARN
|
|
||||||
format: "HA Loss Test OFF: cannot restore scene (API not connected or last scene unknown)"
|
|
||||||
@@ -1,477 +0,0 @@
|
|||||||
#############################################
|
|
||||||
#############################################
|
|
||||||
#
|
|
||||||
# ESP Home
|
|
||||||
# NFC Tag Reader for Home Assistant
|
|
||||||
#
|
|
||||||
#############################################
|
|
||||||
#############################################
|
|
||||||
# Project Link:
|
|
||||||
# https://github.com/adonno/tagreader
|
|
||||||
#############################################
|
|
||||||
# Remember to set the switches on the PN532
|
|
||||||
# for i2C, Switch1: On (up), Switch2: Off (down)
|
|
||||||
#############################################
|
|
||||||
|
|
||||||
|
|
||||||
#############################################
|
|
||||||
# Substitution Text
|
|
||||||
#############################################
|
|
||||||
substitutions:
|
|
||||||
name: esp-nfcaccess1-33ad74
|
|
||||||
friendly_name: TagReader
|
|
||||||
|
|
||||||
#############################################
|
|
||||||
# Inport the project from Github
|
|
||||||
#############################################
|
|
||||||
#dashboard_import:
|
|
||||||
# package_import_url: github://adonno/tagreader/tagreader.yaml
|
|
||||||
|
|
||||||
#############################################
|
|
||||||
# Use Wifi
|
|
||||||
#############################################
|
|
||||||
wifi:
|
|
||||||
ssid: !secret wifi_ssid
|
|
||||||
password: !secret wifi_password
|
|
||||||
# Details for fallback hotspot (captive portal)
|
|
||||||
# in case wifi connection fails
|
|
||||||
ap:
|
|
||||||
ssid: "${name} Hotspot"
|
|
||||||
password: !secret fallback_ap_password
|
|
||||||
|
|
||||||
#############################################
|
|
||||||
# Improv
|
|
||||||
# Use the open Improv standard for configuring Wi-Fi on the
|
|
||||||
# device by using a serial connection to the device, eg. USB.
|
|
||||||
#############################################
|
|
||||||
improv_serial:
|
|
||||||
|
|
||||||
#############################################
|
|
||||||
# Fallback captive portal
|
|
||||||
# Enable the captive portal for inital WiFi setup
|
|
||||||
#############################################
|
|
||||||
captive_portal:
|
|
||||||
|
|
||||||
#############################################
|
|
||||||
# Enable Over the Air Update Capability
|
|
||||||
# Safe mode will detect boot loops
|
|
||||||
#############################################
|
|
||||||
ota:
|
|
||||||
safe_mode: true # Avoid boot loops
|
|
||||||
password: !secret esp-nfcaccess1-33ad74_ota_pass
|
|
||||||
|
|
||||||
#############################################
|
|
||||||
# ESPHome Logging Enable
|
|
||||||
#############################################
|
|
||||||
logger:
|
|
||||||
# baud_rate: 0 # Set baud rate to 0 if using the UART for soething else
|
|
||||||
# level: VERY_VERBOSE
|
|
||||||
# level: VERBOSE
|
|
||||||
|
|
||||||
#############################################
|
|
||||||
# Web Portal for display and monitoring
|
|
||||||
#############################################
|
|
||||||
#web_server:
|
|
||||||
# port: 80
|
|
||||||
# auth:
|
|
||||||
# username: !secret web_server_username
|
|
||||||
# password: !secret web_server_password
|
|
||||||
|
|
||||||
########################################
|
|
||||||
# Specific board for ESPHome device
|
|
||||||
########################################
|
|
||||||
esp8266:
|
|
||||||
board: d1_mini
|
|
||||||
|
|
||||||
#############################################
|
|
||||||
# ESPHome
|
|
||||||
#############################################
|
|
||||||
esphome:
|
|
||||||
name: ${name}
|
|
||||||
# Automatically add the mac address to the name
|
|
||||||
# so you can use a single firmware for all devices
|
|
||||||
name_add_mac_suffix: false
|
|
||||||
# This will allow for (future) project identification,
|
|
||||||
# configuration and updates.
|
|
||||||
project:
|
|
||||||
name: adonno.tag_reader
|
|
||||||
version: "1.4"
|
|
||||||
# If buzzer is enabled, notify on api connection success
|
|
||||||
on_boot:
|
|
||||||
priority: -10
|
|
||||||
then:
|
|
||||||
- wait_until:
|
|
||||||
api.connected:
|
|
||||||
- logger.log: API is connected!
|
|
||||||
- rtttl.play: "success:d=24,o=5,b=100:c,g,b"
|
|
||||||
- light.turn_on:
|
|
||||||
id: activity_led
|
|
||||||
brightness: 100%
|
|
||||||
red: 0%
|
|
||||||
green: 0%
|
|
||||||
blue: 100%
|
|
||||||
flash_length: 500ms
|
|
||||||
- switch.turn_on: buzzer_enabled
|
|
||||||
- switch.turn_on: led_enabled
|
|
||||||
|
|
||||||
|
|
||||||
#############################################
|
|
||||||
# Global variables
|
|
||||||
#############################################
|
|
||||||
globals:
|
|
||||||
- id: source
|
|
||||||
type: std::string
|
|
||||||
- id: url
|
|
||||||
type: std::string
|
|
||||||
- id: info
|
|
||||||
type: std::string
|
|
||||||
|
|
||||||
|
|
||||||
#############################################
|
|
||||||
#############################################
|
|
||||||
# MAIN SENSORS
|
|
||||||
#############################################
|
|
||||||
#############################################
|
|
||||||
|
|
||||||
#############################################
|
|
||||||
# Switches
|
|
||||||
# Define switches to control LED and buzzer from HA
|
|
||||||
#############################################
|
|
||||||
switch:
|
|
||||||
- platform: template
|
|
||||||
name: "${friendly_name} Buzzer Enabled"
|
|
||||||
id: buzzer_enabled
|
|
||||||
icon: mdi:volume-high
|
|
||||||
optimistic: true
|
|
||||||
restore_state: true
|
|
||||||
entity_category: config
|
|
||||||
- platform: template
|
|
||||||
name: "${friendly_name} LED enabled"
|
|
||||||
id: led_enabled
|
|
||||||
icon: mdi:alarm-light-outline
|
|
||||||
optimistic: true
|
|
||||||
restore_state: true
|
|
||||||
entity_category: config
|
|
||||||
|
|
||||||
#############################################
|
|
||||||
# Buttons
|
|
||||||
# Define buttons for writing tags via HA
|
|
||||||
#############################################
|
|
||||||
button:
|
|
||||||
- platform: template
|
|
||||||
name: Write Tag Random
|
|
||||||
id: write_tag_random
|
|
||||||
# Optional variables:
|
|
||||||
icon: "mdi:pencil-box"
|
|
||||||
on_press:
|
|
||||||
then:
|
|
||||||
- light.turn_on:
|
|
||||||
id: activity_led
|
|
||||||
brightness: 100%
|
|
||||||
red: 100%
|
|
||||||
green: 0%
|
|
||||||
blue: 100%
|
|
||||||
- lambda: |-
|
|
||||||
static const char alphanum[] = "0123456789abcdef";
|
|
||||||
std::string uri = "https://www.home-assistant.io/tag/";
|
|
||||||
for (int i = 0; i < 8; i++)
|
|
||||||
uri += alphanum[random_uint32() % (sizeof(alphanum) - 1)];
|
|
||||||
uri += "-";
|
|
||||||
for (int j = 0; j < 3; j++) {
|
|
||||||
for (int i = 0; i < 4; i++)
|
|
||||||
uri += alphanum[random_uint32() % (sizeof(alphanum) - 1)];
|
|
||||||
uri += "-";
|
|
||||||
}
|
|
||||||
for (int i = 0; i < 12; i++)
|
|
||||||
uri += alphanum[random_uint32() % (sizeof(alphanum) - 1)];
|
|
||||||
auto message = new nfc::NdefMessage();
|
|
||||||
message->add_uri_record(uri);
|
|
||||||
ESP_LOGD("tagreader", "Writing payload: %s", uri.c_str());
|
|
||||||
id(pn532_board).write_mode(message);
|
|
||||||
- rtttl.play: "write:d=24,o=5,b=100:b"
|
|
||||||
- wait_until:
|
|
||||||
not:
|
|
||||||
pn532.is_writing:
|
|
||||||
- light.turn_off:
|
|
||||||
id: activity_led
|
|
||||||
- rtttl.play: "write:d=24,o=5,b=100:b,b"
|
|
||||||
|
|
||||||
- platform: template
|
|
||||||
name: Clean Tag
|
|
||||||
id: clean_tag
|
|
||||||
icon: "mdi:nfc-variant-off"
|
|
||||||
on_press:
|
|
||||||
then:
|
|
||||||
- light.turn_on:
|
|
||||||
id: activity_led
|
|
||||||
brightness: 100%
|
|
||||||
red: 100%
|
|
||||||
green: 64.7%
|
|
||||||
blue: 0%
|
|
||||||
- lambda: 'id(pn532_board).clean_mode();'
|
|
||||||
- rtttl.play: "write:d=24,o=5,b=100:b"
|
|
||||||
- wait_until:
|
|
||||||
not:
|
|
||||||
pn532.is_writing:
|
|
||||||
- light.turn_off:
|
|
||||||
id: activity_led
|
|
||||||
- rtttl.play: "write:d=24,o=5,b=100:b,b"
|
|
||||||
|
|
||||||
- platform: template
|
|
||||||
name: Cancel writing
|
|
||||||
id: cancel_writing
|
|
||||||
icon: "mdi:pencil-off"
|
|
||||||
on_press:
|
|
||||||
then:
|
|
||||||
- lambda: 'id(pn532_board).read_mode();'
|
|
||||||
- light.turn_off:
|
|
||||||
id: activity_led
|
|
||||||
- rtttl.play: "write:d=24,o=5,b=100:b,b"
|
|
||||||
|
|
||||||
- platform: restart
|
|
||||||
name: "${friendly_name} Restart"
|
|
||||||
entity_category: config
|
|
||||||
|
|
||||||
#############################################
|
|
||||||
# HA API
|
|
||||||
# Enable Home Assistant API
|
|
||||||
#############################################
|
|
||||||
api:
|
|
||||||
encryption:
|
|
||||||
key: "K2H5vBNv8NLegc3RmwTQVFV7ULtba2DWDOlABmXmGBM="
|
|
||||||
services:
|
|
||||||
- service: rfidreader_tag_ok
|
|
||||||
then:
|
|
||||||
- rtttl.play: "beep:d=16,o=5,b=100:b"
|
|
||||||
- service: rfidreader_tag_ko
|
|
||||||
then:
|
|
||||||
- rtttl.play: "beep:d=8,o=5,b=100:b"
|
|
||||||
- service: play_rtttl
|
|
||||||
variables:
|
|
||||||
song_str: string
|
|
||||||
then:
|
|
||||||
- rtttl.play: !lambda 'return song_str;'
|
|
||||||
- service: write_tag_id
|
|
||||||
variables:
|
|
||||||
tag_id: string
|
|
||||||
then:
|
|
||||||
- light.turn_on:
|
|
||||||
id: activity_led
|
|
||||||
brightness: 100%
|
|
||||||
red: 100%
|
|
||||||
green: 0%
|
|
||||||
blue: 0%
|
|
||||||
- lambda: |-
|
|
||||||
auto message = new nfc::NdefMessage();
|
|
||||||
std::string uri = "https://www.home-assistant.io/tag/";
|
|
||||||
uri += tag_id;
|
|
||||||
message->add_uri_record(uri);
|
|
||||||
id(pn532_board).write_mode(message);
|
|
||||||
- rtttl.play: "write:d=24,o=5,b=100:b"
|
|
||||||
- wait_until:
|
|
||||||
not:
|
|
||||||
pn532.is_writing:
|
|
||||||
- light.turn_off:
|
|
||||||
id: activity_led
|
|
||||||
- rtttl.play: "write:d=24,o=5,b=100:b,b"
|
|
||||||
- service: write_music_tag
|
|
||||||
variables:
|
|
||||||
music_url: string
|
|
||||||
music_info: string
|
|
||||||
then:
|
|
||||||
- light.turn_on:
|
|
||||||
id: activity_led
|
|
||||||
brightness: 100%
|
|
||||||
red: 100%
|
|
||||||
green: 0%
|
|
||||||
blue: 0%
|
|
||||||
- lambda: |-
|
|
||||||
auto message = new nfc::NdefMessage();
|
|
||||||
std::string uri = "";
|
|
||||||
std::string text = "";
|
|
||||||
uri += music_url;
|
|
||||||
text += music_info;
|
|
||||||
if ( music_url != "" ) {
|
|
||||||
message->add_uri_record(uri);
|
|
||||||
}
|
|
||||||
if ( music_info != "" ) {
|
|
||||||
message->add_text_record(text);
|
|
||||||
}
|
|
||||||
id(pn532_board).write_mode(message);
|
|
||||||
- rtttl.play: "write:d=24,o=5,b=100:b"
|
|
||||||
- wait_until:
|
|
||||||
not:
|
|
||||||
pn532.is_writing:
|
|
||||||
- light.turn_off:
|
|
||||||
id: activity_led
|
|
||||||
- rtttl.play: "write:d=24,o=5,b=100:b,b"
|
|
||||||
|
|
||||||
#############################################
|
|
||||||
# i2C bus
|
|
||||||
#############################################
|
|
||||||
i2c:
|
|
||||||
scan: False
|
|
||||||
frequency: 400kHz
|
|
||||||
|
|
||||||
#############################################
|
|
||||||
# NFC Reader
|
|
||||||
#############################################
|
|
||||||
pn532_i2c:
|
|
||||||
id: pn532_board
|
|
||||||
on_tag:
|
|
||||||
then:
|
|
||||||
- if:
|
|
||||||
condition:
|
|
||||||
switch.is_on: led_enabled
|
|
||||||
then:
|
|
||||||
- light.turn_on:
|
|
||||||
id: activity_led
|
|
||||||
brightness: 100%
|
|
||||||
red: 0%
|
|
||||||
green: 100%
|
|
||||||
blue: 0%
|
|
||||||
flash_length: 500ms
|
|
||||||
|
|
||||||
- delay: 0.15s #to fix slow component
|
|
||||||
|
|
||||||
- lambda: |-
|
|
||||||
id(source)="";
|
|
||||||
id(url)="";
|
|
||||||
id(info)="";
|
|
||||||
if (tag.has_ndef_message()) {
|
|
||||||
auto message = tag.get_ndef_message();
|
|
||||||
auto records = message->get_records();
|
|
||||||
for (auto &record : records) {
|
|
||||||
std::string payload = record->get_payload();
|
|
||||||
std::string type = record->get_type();
|
|
||||||
size_t hass = payload.find("https://www.home-assistant.io/tag/");
|
|
||||||
size_t applemusic = payload.find("https://music.apple.com");
|
|
||||||
size_t spotify = payload.find("https://open.spotify.com");
|
|
||||||
size_t sonos = payload.find("sonos-2://");
|
|
||||||
|
|
||||||
if (type == "U" and hass != std::string::npos ) {
|
|
||||||
ESP_LOGD("tagreader", "Found Home Assistant tag NDEF");
|
|
||||||
id(source)="hass";
|
|
||||||
id(url)=payload;
|
|
||||||
id(info)=payload.substr(hass + 34);
|
|
||||||
}
|
|
||||||
else if (type == "U" and applemusic != std::string::npos ) {
|
|
||||||
ESP_LOGD("tagreader", "Found Apple Music tag NDEF");
|
|
||||||
id(source)="amusic";
|
|
||||||
id(url)=payload;
|
|
||||||
}
|
|
||||||
else if (type == "U" and spotify != std::string::npos ) {
|
|
||||||
ESP_LOGD("tagreader", "Found Spotify tag NDEF");
|
|
||||||
id(source)="spotify";
|
|
||||||
id(url)=payload;
|
|
||||||
}
|
|
||||||
else if (type == "U" and sonos != std::string::npos ) {
|
|
||||||
ESP_LOGD("tagreader", "Found Sonos app tag NDEF");
|
|
||||||
id(source)="sonos";
|
|
||||||
id(url)=payload;
|
|
||||||
}
|
|
||||||
else if (type == "T" ) {
|
|
||||||
ESP_LOGD("tagreader", "Found music info tag NDEF");
|
|
||||||
id(info)=payload;
|
|
||||||
}
|
|
||||||
else if ( id(source)=="" ) {
|
|
||||||
id(source)="uid";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
id(source)="uid";
|
|
||||||
}
|
|
||||||
|
|
||||||
- if:
|
|
||||||
condition:
|
|
||||||
lambda: 'return ( id(source)=="uid" );'
|
|
||||||
then:
|
|
||||||
- homeassistant.tag_scanned: !lambda |-
|
|
||||||
ESP_LOGD("tagreader", "No HA NDEF, using UID");
|
|
||||||
return x;
|
|
||||||
else:
|
|
||||||
- if:
|
|
||||||
condition:
|
|
||||||
lambda: 'return ( id(source)=="hass" );'
|
|
||||||
then:
|
|
||||||
- homeassistant.tag_scanned: !lambda 'return id(info);'
|
|
||||||
else:
|
|
||||||
- homeassistant.event:
|
|
||||||
event: esphome.music_tag
|
|
||||||
data:
|
|
||||||
reader: !lambda |-
|
|
||||||
return App.get_name().c_str();
|
|
||||||
source: !lambda |-
|
|
||||||
return id(source);
|
|
||||||
url: !lambda |-
|
|
||||||
return id(url);
|
|
||||||
info: !lambda |-
|
|
||||||
return id(info);
|
|
||||||
|
|
||||||
- if:
|
|
||||||
condition:
|
|
||||||
switch.is_on: buzzer_enabled
|
|
||||||
then:
|
|
||||||
- rtttl.play: "success:d=24,o=5,b=100:c,g,b"
|
|
||||||
on_tag_removed:
|
|
||||||
then:
|
|
||||||
- homeassistant.event:
|
|
||||||
event: esphome.tag_removed
|
|
||||||
|
|
||||||
#############################################
|
|
||||||
# OUTPUTS
|
|
||||||
#############################################
|
|
||||||
# Define the buzzer output
|
|
||||||
output:
|
|
||||||
- platform: esp8266_pwm
|
|
||||||
pin: D7
|
|
||||||
id: buzzer
|
|
||||||
|
|
||||||
#############################################
|
|
||||||
# BINARY SENSORS
|
|
||||||
#############################################
|
|
||||||
binary_sensor:
|
|
||||||
- platform: status
|
|
||||||
name: "${friendly_name} Status"
|
|
||||||
entity_category: diagnostic
|
|
||||||
|
|
||||||
#############################################
|
|
||||||
# TEXT SENSORS
|
|
||||||
#############################################
|
|
||||||
text_sensor:
|
|
||||||
- platform: version
|
|
||||||
hide_timestamp: true
|
|
||||||
name: "${friendly_name} ESPHome Version"
|
|
||||||
entity_category: diagnostic
|
|
||||||
- platform: wifi_info
|
|
||||||
ip_address:
|
|
||||||
name: "${friendly_name} IP Address"
|
|
||||||
icon: mdi:wifi
|
|
||||||
entity_category: diagnostic
|
|
||||||
ssid:
|
|
||||||
name: "${friendly_name} Connected SSID"
|
|
||||||
icon: mdi:wifi-strength-2
|
|
||||||
entity_category: diagnostic
|
|
||||||
|
|
||||||
#############################################
|
|
||||||
# Ring Tone Text Transfer Language for melody
|
|
||||||
#############################################
|
|
||||||
# Define buzzer as output for RTTTL
|
|
||||||
rtttl:
|
|
||||||
output: buzzer
|
|
||||||
|
|
||||||
#############################################
|
|
||||||
# LED
|
|
||||||
#############################################
|
|
||||||
# Configure LED
|
|
||||||
light:
|
|
||||||
- platform: neopixelbus
|
|
||||||
variant: WS2812
|
|
||||||
pin: D8
|
|
||||||
num_leds: 1
|
|
||||||
flash_transition_length: 500ms
|
|
||||||
type: GRB
|
|
||||||
id: activity_led
|
|
||||||
name: "${friendly_name} LED"
|
|
||||||
restore_mode: ALWAYS_OFF
|
|
||||||
@@ -1,136 +0,0 @@
|
|||||||
substitutions:
|
|
||||||
name: m5stack-atom-echo-80be58
|
|
||||||
friendly_name: M5Stack Atom Echo 80be58
|
|
||||||
|
|
||||||
esphome:
|
|
||||||
name: "${name}"
|
|
||||||
friendly_name: "${friendly_name}"
|
|
||||||
name_add_mac_suffix: false
|
|
||||||
project:
|
|
||||||
name: m5stack.atom-echo-voice-assistant
|
|
||||||
version: "1.0"
|
|
||||||
min_version: 2023.5.0
|
|
||||||
|
|
||||||
api:
|
|
||||||
encryption:
|
|
||||||
key: XmXtlYVEuioSNapvz8G/9caKCI0T7aRW/CFy/f83D6g=
|
|
||||||
|
|
||||||
esp32:
|
|
||||||
board: m5stack-atom
|
|
||||||
framework:
|
|
||||||
type: esp-idf
|
|
||||||
|
|
||||||
logger:
|
|
||||||
ota:
|
|
||||||
|
|
||||||
wifi:
|
|
||||||
ssid: !secret wifi_ssid
|
|
||||||
password: !secret wifi_password
|
|
||||||
|
|
||||||
|
|
||||||
dashboard_import:
|
|
||||||
package_import_url: github://esphome/firmware/voice-assistant/m5stack-atom-echo.yaml@main
|
|
||||||
|
|
||||||
improv_serial:
|
|
||||||
|
|
||||||
i2s_audio:
|
|
||||||
i2s_lrclk_pin: GPIO33
|
|
||||||
i2s_bclk_pin: GPIO19
|
|
||||||
|
|
||||||
microphone:
|
|
||||||
- platform: i2s_audio
|
|
||||||
id: echo_microphone
|
|
||||||
i2s_din_pin: GPIO23
|
|
||||||
adc_type: external
|
|
||||||
pdm: true
|
|
||||||
|
|
||||||
speaker:
|
|
||||||
- platform: i2s_audio
|
|
||||||
id: echo_speaker
|
|
||||||
i2s_dout_pin: GPIO22
|
|
||||||
dac_type: external
|
|
||||||
mode: mono
|
|
||||||
|
|
||||||
voice_assistant:
|
|
||||||
microphone: echo_microphone
|
|
||||||
speaker: echo_speaker
|
|
||||||
on_start:
|
|
||||||
- light.turn_on:
|
|
||||||
id: led
|
|
||||||
blue: 100%
|
|
||||||
red: 0%
|
|
||||||
green: 0%
|
|
||||||
effect: none
|
|
||||||
on_tts_start:
|
|
||||||
- light.turn_on:
|
|
||||||
id: led
|
|
||||||
blue: 0%
|
|
||||||
red: 0%
|
|
||||||
green: 100%
|
|
||||||
effect: none
|
|
||||||
on_tts_end:
|
|
||||||
- light.turn_on:
|
|
||||||
id: led
|
|
||||||
blue: 0%
|
|
||||||
red: 0%
|
|
||||||
green: 100%
|
|
||||||
effect: pulse
|
|
||||||
on_end:
|
|
||||||
- delay: 1s
|
|
||||||
- wait_until:
|
|
||||||
not:
|
|
||||||
speaker.is_playing:
|
|
||||||
- light.turn_off: led
|
|
||||||
on_error:
|
|
||||||
- light.turn_on:
|
|
||||||
id: led
|
|
||||||
blue: 0%
|
|
||||||
red: 100%
|
|
||||||
green: 0%
|
|
||||||
effect: none
|
|
||||||
- delay: 1s
|
|
||||||
- light.turn_off: led
|
|
||||||
|
|
||||||
|
|
||||||
binary_sensor:
|
|
||||||
- platform: gpio
|
|
||||||
pin:
|
|
||||||
number: GPIO39
|
|
||||||
inverted: true
|
|
||||||
name: Button
|
|
||||||
disabled_by_default: true
|
|
||||||
entity_category: diagnostic
|
|
||||||
id: echo_button
|
|
||||||
on_multi_click:
|
|
||||||
- timing:
|
|
||||||
- ON FOR AT LEAST 350ms
|
|
||||||
then:
|
|
||||||
- voice_assistant.start:
|
|
||||||
- timing:
|
|
||||||
- ON FOR AT LEAST 350ms
|
|
||||||
- OFF FOR AT LEAST 10ms
|
|
||||||
then:
|
|
||||||
- light.turn_on:
|
|
||||||
id: led
|
|
||||||
blue: 100%
|
|
||||||
red: 0%
|
|
||||||
green: 0%
|
|
||||||
effect: pulse
|
|
||||||
- voice_assistant.stop:
|
|
||||||
|
|
||||||
light:
|
|
||||||
- platform: esp32_rmt_led_strip
|
|
||||||
id: led
|
|
||||||
name: None
|
|
||||||
disabled_by_default: true
|
|
||||||
entity_category: config
|
|
||||||
pin: GPIO27
|
|
||||||
default_transition_length: 0s
|
|
||||||
chipset: SK6812
|
|
||||||
num_leds: 1
|
|
||||||
rgb_order: grb
|
|
||||||
rmt_channel: 0
|
|
||||||
effects:
|
|
||||||
- pulse:
|
|
||||||
transition_length: 250ms
|
|
||||||
update_interval: 250ms
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
substitutions:
|
|
||||||
name: m5stack-atom-echo-82d244
|
|
||||||
friendly_name: M5Stack Atom Echo 82d244
|
|
||||||
packages:
|
|
||||||
m5stack.atom-echo-voice-assistant: github://esphome/firmware/voice-assistant/m5stack-atom-echo.yaml@main
|
|
||||||
esphome:
|
|
||||||
name: ${name}
|
|
||||||
name_add_mac_suffix: false
|
|
||||||
friendly_name: ${friendly_name}
|
|
||||||
api:
|
|
||||||
encryption:
|
|
||||||
key: Oh2XL+cpsK1jjTO8lW31IZ6CQiQNdqCpUgCZpoe4eYI=
|
|
||||||
|
|
||||||
|
|
||||||
wifi:
|
|
||||||
ssid: !secret wifi_ssid
|
|
||||||
password: !secret wifi_password
|
|
||||||
@@ -9,7 +9,7 @@
|
|||||||
# jobs_tracker_jobs.yaml
|
# jobs_tracker_jobs.yaml
|
||||||
#
|
#
|
||||||
# VERSION:
|
# VERSION:
|
||||||
# V2.3 2026-07-20
|
# V2.4 2026-07-21
|
||||||
#
|
#
|
||||||
# PURPOSE:
|
# PURPOSE:
|
||||||
# Defines the job registry, MQTT Discovery entities, completion triggers and
|
# Defines the job registry, MQTT Discovery entities, completion triggers and
|
||||||
@@ -41,6 +41,12 @@
|
|||||||
# Complete
|
# Complete
|
||||||
#
|
#
|
||||||
# VERSION HISTORY:
|
# VERSION HISTORY:
|
||||||
|
# V2.4 2026-07-21
|
||||||
|
# - Startup now waits for every retained job timestamp before refreshing.
|
||||||
|
# - Minute refreshes skip while retained MQTT state is not ready.
|
||||||
|
# - Restored stale event timestamps are no longer accepted as completions.
|
||||||
|
# - Discovery device firmware version updated to 2.4.
|
||||||
|
#
|
||||||
# V2.3 2026-07-20
|
# V2.3 2026-07-20
|
||||||
# - All completion methods now obey the configured completion window.
|
# - All completion methods now obey the configured completion window.
|
||||||
# - Physical completion entities, Home Assistant discovery buttons and direct
|
# - Physical completion entities, Home Assistant discovery buttons and direct
|
||||||
@@ -360,7 +366,7 @@ script:
|
|||||||
device_name: "Jobs Tracker"
|
device_name: "Jobs Tracker"
|
||||||
device_manufacturer: "View Road Home Assistant"
|
device_manufacturer: "View Road Home Assistant"
|
||||||
device_model: "Shared household jobs tracker"
|
device_model: "Shared household jobs tracker"
|
||||||
device_sw_version: "2.3"
|
device_sw_version: "2.4"
|
||||||
|
|
||||||
value_template_state: >-
|
value_template_state: >-
|
||||||
{% raw %}{{ value_json.state | default('unknown') }}{% endraw %}
|
{% raw %}{{ value_json.state | default('unknown') }}{% endraw %}
|
||||||
@@ -697,10 +703,23 @@ automation:
|
|||||||
actions:
|
actions:
|
||||||
- action: script.jobs_tracker_publish_discovery
|
- action: script.jobs_tracker_publish_discovery
|
||||||
|
|
||||||
# Give Home Assistant a moment to process MQTT Discovery.
|
- variables:
|
||||||
- delay: "00:00:10"
|
jobs_tracker_jobs: *jobs_tracker_jobs
|
||||||
|
|
||||||
# Force retained state payloads onto each job topic.
|
# Do not refresh until every retained timestamp has returned from MQTT.
|
||||||
|
# Aborting on timeout protects valid retained data from being replaced by 0.
|
||||||
|
- wait_template: >-
|
||||||
|
{% set ns = namespace(ready=true) %}
|
||||||
|
{% for job in jobs_tracker_jobs %}
|
||||||
|
{% if not is_number(state_attr(job.state_entity, 'last_completed_ts')) %}
|
||||||
|
{% set ns.ready = false %}
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
{{ ns.ready }}
|
||||||
|
timeout: "00:01:30"
|
||||||
|
continue_on_timeout: false
|
||||||
|
|
||||||
|
# Recalculate each job only after retained state is confirmed ready.
|
||||||
- action: script.jobs_tracker_dispatch
|
- action: script.jobs_tracker_dispatch
|
||||||
data:
|
data:
|
||||||
update_all: true
|
update_all: true
|
||||||
@@ -711,25 +730,26 @@ automation:
|
|||||||
mode: single
|
mode: single
|
||||||
|
|
||||||
triggers:
|
triggers:
|
||||||
- trigger: homeassistant
|
|
||||||
event: start
|
|
||||||
id: ha_start
|
|
||||||
|
|
||||||
- trigger: time_pattern
|
- trigger: time_pattern
|
||||||
minutes: "/1"
|
minutes: "/1"
|
||||||
id: tick
|
id: tick
|
||||||
|
|
||||||
|
variables:
|
||||||
|
jobs_tracker_jobs: *jobs_tracker_jobs
|
||||||
|
|
||||||
|
conditions:
|
||||||
|
# Skip the minute tick until every retained timestamp is available.
|
||||||
|
- condition: template
|
||||||
|
value_template: >-
|
||||||
|
{% set ns = namespace(ready=true) %}
|
||||||
|
{% for job in jobs_tracker_jobs %}
|
||||||
|
{% if not is_number(state_attr(job.state_entity, 'last_completed_ts')) %}
|
||||||
|
{% set ns.ready = false %}
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
{{ ns.ready }}
|
||||||
|
|
||||||
actions:
|
actions:
|
||||||
- if:
|
|
||||||
- condition: template
|
|
||||||
value_template: "{{ trigger.id == 'ha_start' }}"
|
|
||||||
then:
|
|
||||||
- action: script.jobs_tracker_publish_discovery
|
|
||||||
|
|
||||||
# Let retained MQTT Discovery and job state messages restore before
|
|
||||||
# the first refresh.
|
|
||||||
- delay: "00:01:30"
|
|
||||||
|
|
||||||
- action: script.jobs_tracker_dispatch
|
- action: script.jobs_tracker_dispatch
|
||||||
data:
|
data:
|
||||||
update_all: true
|
update_all: true
|
||||||
@@ -747,9 +767,18 @@ automation:
|
|||||||
conditions:
|
conditions:
|
||||||
- condition: template
|
- condition: template
|
||||||
value_template: >-
|
value_template: >-
|
||||||
|
{% set valid_state = trigger.to_state is not none
|
||||||
|
and trigger.to_state.state not in ['unknown', 'unavailable'] %}
|
||||||
|
{% set event_entity = trigger.entity_id.startswith('event.') %}
|
||||||
|
{% set event_ts = as_timestamp(trigger.to_state.state, 0)
|
||||||
|
if valid_state and event_entity else 0 %}
|
||||||
|
{% set event_age = as_timestamp(now()) - event_ts %}
|
||||||
{{
|
{{
|
||||||
trigger.to_state is not none
|
valid_state
|
||||||
and trigger.to_state.state not in ['unknown', 'unavailable']
|
and (
|
||||||
|
not event_entity
|
||||||
|
or (event_ts > 0 and event_age >= 0 and event_age <= 30)
|
||||||
|
)
|
||||||
}}
|
}}
|
||||||
|
|
||||||
actions:
|
actions:
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ automation:
|
|||||||
members:
|
members:
|
||||||
- switch.esp_centralstairs_top_relay_1_main_stair_lights
|
- switch.esp_centralstairs_top_relay_1_main_stair_lights
|
||||||
- switch.esp_centralstairs_bottom_relay_1_main_stair_lights_lower
|
- switch.esp_centralstairs_bottom_relay_1_main_stair_lights_lower
|
||||||
- switch.esp_loungemiddleswitch_relay_3_downlights_north
|
|
||||||
sync_on_start: true
|
sync_on_start: true
|
||||||
startup_delay: 5
|
startup_delay: 5
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,476 @@
|
|||||||
|
#:########################################################################################:#
|
||||||
|
# Package: EdgeRouter SNMP Monitoring
|
||||||
|
# File: edgerouter_snmp.yaml
|
||||||
|
# Version: v1.0.1
|
||||||
|
# Date: 2026-07-21
|
||||||
|
#
|
||||||
|
# Purpose:
|
||||||
|
# Monitors the EdgeRouter X through read-only SNMPv2c after the previous EdgeOS
|
||||||
|
# integration became incompatible with EdgeOS v3.0.x. Provides traffic rates
|
||||||
|
# for the major physical interfaces and system health sensors for the router.
|
||||||
|
#
|
||||||
|
# Interfaces:
|
||||||
|
# - eth0 / ifIndex 4: WAN and Internet traffic.
|
||||||
|
# - eth1 / ifIndex 5: Home LAN traffic.
|
||||||
|
# - eth2 / ifIndex 6: Home Wireless traffic.
|
||||||
|
# - eth3 / ifIndex 7: Home Automation traffic.
|
||||||
|
# - eth4 / ifIndex 8: Secondary Home Automation traffic.
|
||||||
|
#
|
||||||
|
# Behaviour:
|
||||||
|
# - Polls 64-bit IF-MIB counters so totals remain reliable on busy interfaces.
|
||||||
|
# - Converts cumulative received/transmitted byte counters into kB/s rates.
|
||||||
|
# - Polls firmware, uptime, CPU idle, load averages, and memory counters.
|
||||||
|
# - Calculates memory usage from total, free, buffer, and cache values.
|
||||||
|
#
|
||||||
|
# Polling:
|
||||||
|
# - Traffic counters: Every 10 seconds.
|
||||||
|
# - Uptime, CPU, load, and memory: Every 60 seconds.
|
||||||
|
# - Firmware description: Every 300 seconds.
|
||||||
|
#
|
||||||
|
# Notes:
|
||||||
|
# - The SNMP community is stored in secrets.yaml and is never hardcoded here.
|
||||||
|
# - WAN traffic intentionally uses physical eth0 because hardware-offloaded
|
||||||
|
# traffic is under-reported by the logical pppoe0 and eth0.10 interfaces.
|
||||||
|
# - Received traffic on LAN interfaces is upload toward the router; transmitted
|
||||||
|
# traffic is download from the router toward clients on that interface.
|
||||||
|
# - SNMP, derivative, and template platforms create standalone entities rather
|
||||||
|
# than a combined Home Assistant device entry.
|
||||||
|
#
|
||||||
|
# Version History:
|
||||||
|
# - v1.0.1 (2026-07-21): Moved the EdgeRouter host address to secrets.yaml.
|
||||||
|
# - v1.0.0 (2026-07-21): Initial documented SNMP traffic and health package.
|
||||||
|
#:########################################################################################:#
|
||||||
|
|
||||||
|
sensor:
|
||||||
|
#:######################################################################################:#
|
||||||
|
# WAN TRAFFIC - eth0 / ifIndex 4
|
||||||
|
#:######################################################################################:#
|
||||||
|
# ifHCInOctets counts bytes received from the Internet. The physical interface
|
||||||
|
# is used so hardware-offloaded traffic is included in the total.
|
||||||
|
- platform: snmp
|
||||||
|
name: "EdgeRouter WAN Received Bytes"
|
||||||
|
unique_id: "edgerouter_wan_received_bytes"
|
||||||
|
host: !secret edgerouter_host
|
||||||
|
community: !secret edgerouter_snmp_community
|
||||||
|
version: 2c
|
||||||
|
baseoid: 1.3.6.1.2.1.31.1.1.1.6.4
|
||||||
|
accept_errors: true
|
||||||
|
device_class: data_size
|
||||||
|
state_class: total_increasing
|
||||||
|
unit_of_measurement: "B"
|
||||||
|
scan_interval: 10
|
||||||
|
|
||||||
|
# ifHCOutOctets counts bytes transmitted from the router to the Internet.
|
||||||
|
- platform: snmp
|
||||||
|
name: "EdgeRouter WAN Transmitted Bytes"
|
||||||
|
unique_id: "edgerouter_wan_transmitted_bytes"
|
||||||
|
host: !secret edgerouter_host
|
||||||
|
community: !secret edgerouter_snmp_community
|
||||||
|
version: 2c
|
||||||
|
baseoid: 1.3.6.1.2.1.31.1.1.1.10.4
|
||||||
|
accept_errors: true
|
||||||
|
device_class: data_size
|
||||||
|
state_class: total_increasing
|
||||||
|
unit_of_measurement: "B"
|
||||||
|
scan_interval: 10
|
||||||
|
|
||||||
|
# Convert the cumulative received-byte counter into the WAN download rate.
|
||||||
|
- platform: derivative
|
||||||
|
name: "EdgeRouter WAN Download Rate"
|
||||||
|
unique_id: "edgerouter_wan_download_rate"
|
||||||
|
source: sensor.edgerouter_wan_received_bytes
|
||||||
|
unit_prefix: k
|
||||||
|
unit_time: s
|
||||||
|
round: 1
|
||||||
|
time_window: "00:00:30"
|
||||||
|
|
||||||
|
# Convert the cumulative transmitted-byte counter into the WAN upload rate.
|
||||||
|
- platform: derivative
|
||||||
|
name: "EdgeRouter WAN Upload Rate"
|
||||||
|
unique_id: "edgerouter_wan_upload_rate"
|
||||||
|
source: sensor.edgerouter_wan_transmitted_bytes
|
||||||
|
unit_prefix: k
|
||||||
|
unit_time: s
|
||||||
|
round: 1
|
||||||
|
time_window: "00:00:30"
|
||||||
|
|
||||||
|
#:######################################################################################:#
|
||||||
|
# HOME LAN TRAFFIC - eth1 / ifIndex 5
|
||||||
|
#:######################################################################################:#
|
||||||
|
# Bytes received by the router from Home LAN clients become the LAN upload rate.
|
||||||
|
- platform: snmp
|
||||||
|
name: "EdgeRouter Home LAN Received Bytes"
|
||||||
|
unique_id: "edgerouter_home_lan_received_bytes"
|
||||||
|
host: !secret edgerouter_host
|
||||||
|
community: !secret edgerouter_snmp_community
|
||||||
|
version: 2c
|
||||||
|
baseoid: 1.3.6.1.2.1.31.1.1.1.6.5
|
||||||
|
accept_errors: true
|
||||||
|
device_class: data_size
|
||||||
|
state_class: total_increasing
|
||||||
|
unit_of_measurement: "B"
|
||||||
|
scan_interval: 10
|
||||||
|
|
||||||
|
# Bytes transmitted by the router to Home LAN clients become the LAN download rate.
|
||||||
|
- platform: snmp
|
||||||
|
name: "EdgeRouter Home LAN Transmitted Bytes"
|
||||||
|
unique_id: "edgerouter_home_lan_transmitted_bytes"
|
||||||
|
host: !secret edgerouter_host
|
||||||
|
community: !secret edgerouter_snmp_community
|
||||||
|
version: 2c
|
||||||
|
baseoid: 1.3.6.1.2.1.31.1.1.1.10.5
|
||||||
|
accept_errors: true
|
||||||
|
device_class: data_size
|
||||||
|
state_class: total_increasing
|
||||||
|
unit_of_measurement: "B"
|
||||||
|
scan_interval: 10
|
||||||
|
|
||||||
|
# Derive the client-to-router upload rate from the received-byte counter.
|
||||||
|
- platform: derivative
|
||||||
|
name: "EdgeRouter Home LAN Upload Rate"
|
||||||
|
unique_id: "edgerouter_home_lan_upload_rate"
|
||||||
|
source: sensor.edgerouter_home_lan_received_bytes
|
||||||
|
unit_prefix: k
|
||||||
|
unit_time: s
|
||||||
|
round: 1
|
||||||
|
time_window: "00:00:30"
|
||||||
|
|
||||||
|
# Derive the router-to-client download rate from the transmitted-byte counter.
|
||||||
|
- platform: derivative
|
||||||
|
name: "EdgeRouter Home LAN Download Rate"
|
||||||
|
unique_id: "edgerouter_home_lan_download_rate"
|
||||||
|
source: sensor.edgerouter_home_lan_transmitted_bytes
|
||||||
|
unit_prefix: k
|
||||||
|
unit_time: s
|
||||||
|
round: 1
|
||||||
|
time_window: "00:00:30"
|
||||||
|
|
||||||
|
#:######################################################################################:#
|
||||||
|
# HOME WIRELESS TRAFFIC - eth2 / ifIndex 6
|
||||||
|
#:######################################################################################:#
|
||||||
|
# Bytes received by the router from the wireless network become its upload rate.
|
||||||
|
- platform: snmp
|
||||||
|
name: "EdgeRouter Home Wireless Received Bytes"
|
||||||
|
unique_id: "edgerouter_home_wireless_received_bytes"
|
||||||
|
host: !secret edgerouter_host
|
||||||
|
community: !secret edgerouter_snmp_community
|
||||||
|
version: 2c
|
||||||
|
baseoid: 1.3.6.1.2.1.31.1.1.1.6.6
|
||||||
|
accept_errors: true
|
||||||
|
device_class: data_size
|
||||||
|
state_class: total_increasing
|
||||||
|
unit_of_measurement: "B"
|
||||||
|
scan_interval: 10
|
||||||
|
|
||||||
|
# Bytes transmitted by the router to the wireless network become its download rate.
|
||||||
|
- platform: snmp
|
||||||
|
name: "EdgeRouter Home Wireless Transmitted Bytes"
|
||||||
|
unique_id: "edgerouter_home_wireless_transmitted_bytes"
|
||||||
|
host: !secret edgerouter_host
|
||||||
|
community: !secret edgerouter_snmp_community
|
||||||
|
version: 2c
|
||||||
|
baseoid: 1.3.6.1.2.1.31.1.1.1.10.6
|
||||||
|
accept_errors: true
|
||||||
|
device_class: data_size
|
||||||
|
state_class: total_increasing
|
||||||
|
unit_of_measurement: "B"
|
||||||
|
scan_interval: 10
|
||||||
|
|
||||||
|
# Derive the wireless-to-router upload rate from the received-byte counter.
|
||||||
|
- platform: derivative
|
||||||
|
name: "EdgeRouter Home Wireless Upload Rate"
|
||||||
|
unique_id: "edgerouter_home_wireless_upload_rate"
|
||||||
|
source: sensor.edgerouter_home_wireless_received_bytes
|
||||||
|
unit_prefix: k
|
||||||
|
unit_time: s
|
||||||
|
round: 1
|
||||||
|
time_window: "00:00:30"
|
||||||
|
|
||||||
|
# Derive the router-to-wireless download rate from the transmitted-byte counter.
|
||||||
|
- platform: derivative
|
||||||
|
name: "EdgeRouter Home Wireless Download Rate"
|
||||||
|
unique_id: "edgerouter_home_wireless_download_rate"
|
||||||
|
source: sensor.edgerouter_home_wireless_transmitted_bytes
|
||||||
|
unit_prefix: k
|
||||||
|
unit_time: s
|
||||||
|
round: 1
|
||||||
|
time_window: "00:00:30"
|
||||||
|
|
||||||
|
#:######################################################################################:#
|
||||||
|
# HOME AUTOMATION TRAFFIC - eth3 / ifIndex 7
|
||||||
|
#:######################################################################################:#
|
||||||
|
# Bytes received by the router from Home Automation become its upload rate.
|
||||||
|
- platform: snmp
|
||||||
|
name: "EdgeRouter Home Automation Received Bytes"
|
||||||
|
unique_id: "edgerouter_home_automation_received_bytes"
|
||||||
|
host: !secret edgerouter_host
|
||||||
|
community: !secret edgerouter_snmp_community
|
||||||
|
version: 2c
|
||||||
|
baseoid: 1.3.6.1.2.1.31.1.1.1.6.7
|
||||||
|
accept_errors: true
|
||||||
|
device_class: data_size
|
||||||
|
state_class: total_increasing
|
||||||
|
unit_of_measurement: "B"
|
||||||
|
scan_interval: 10
|
||||||
|
|
||||||
|
# Bytes transmitted by the router to Home Automation become its download rate.
|
||||||
|
- platform: snmp
|
||||||
|
name: "EdgeRouter Home Automation Transmitted Bytes"
|
||||||
|
unique_id: "edgerouter_home_automation_transmitted_bytes"
|
||||||
|
host: !secret edgerouter_host
|
||||||
|
community: !secret edgerouter_snmp_community
|
||||||
|
version: 2c
|
||||||
|
baseoid: 1.3.6.1.2.1.31.1.1.1.10.7
|
||||||
|
accept_errors: true
|
||||||
|
device_class: data_size
|
||||||
|
state_class: total_increasing
|
||||||
|
unit_of_measurement: "B"
|
||||||
|
scan_interval: 10
|
||||||
|
|
||||||
|
# Derive the Home Automation upload rate from the received-byte counter.
|
||||||
|
- platform: derivative
|
||||||
|
name: "EdgeRouter Home Automation Upload Rate"
|
||||||
|
unique_id: "edgerouter_home_automation_upload_rate"
|
||||||
|
source: sensor.edgerouter_home_automation_received_bytes
|
||||||
|
unit_prefix: k
|
||||||
|
unit_time: s
|
||||||
|
round: 1
|
||||||
|
time_window: "00:00:30"
|
||||||
|
|
||||||
|
# Derive the Home Automation download rate from the transmitted-byte counter.
|
||||||
|
- platform: derivative
|
||||||
|
name: "EdgeRouter Home Automation Download Rate"
|
||||||
|
unique_id: "edgerouter_home_automation_download_rate"
|
||||||
|
source: sensor.edgerouter_home_automation_transmitted_bytes
|
||||||
|
unit_prefix: k
|
||||||
|
unit_time: s
|
||||||
|
round: 1
|
||||||
|
time_window: "00:00:30"
|
||||||
|
|
||||||
|
#:######################################################################################:#
|
||||||
|
# SECONDARY HOME AUTOMATION TRAFFIC - eth4 / ifIndex 8
|
||||||
|
#:######################################################################################:#
|
||||||
|
# Bytes received by the router from this network become its upload rate.
|
||||||
|
- platform: snmp
|
||||||
|
name: "EdgeRouter Home Automation 2 Received Bytes"
|
||||||
|
unique_id: "edgerouter_home_automation_2_received_bytes"
|
||||||
|
host: !secret edgerouter_host
|
||||||
|
community: !secret edgerouter_snmp_community
|
||||||
|
version: 2c
|
||||||
|
baseoid: 1.3.6.1.2.1.31.1.1.1.6.8
|
||||||
|
accept_errors: true
|
||||||
|
device_class: data_size
|
||||||
|
state_class: total_increasing
|
||||||
|
unit_of_measurement: "B"
|
||||||
|
scan_interval: 10
|
||||||
|
|
||||||
|
# Bytes transmitted by the router to this network become its download rate.
|
||||||
|
- platform: snmp
|
||||||
|
name: "EdgeRouter Home Automation 2 Transmitted Bytes"
|
||||||
|
unique_id: "edgerouter_home_automation_2_transmitted_bytes"
|
||||||
|
host: !secret edgerouter_host
|
||||||
|
community: !secret edgerouter_snmp_community
|
||||||
|
version: 2c
|
||||||
|
baseoid: 1.3.6.1.2.1.31.1.1.1.10.8
|
||||||
|
accept_errors: true
|
||||||
|
device_class: data_size
|
||||||
|
state_class: total_increasing
|
||||||
|
unit_of_measurement: "B"
|
||||||
|
scan_interval: 10
|
||||||
|
|
||||||
|
# Derive the secondary Home Automation upload rate from received bytes.
|
||||||
|
- platform: derivative
|
||||||
|
name: "EdgeRouter Home Automation 2 Upload Rate"
|
||||||
|
unique_id: "edgerouter_home_automation_2_upload_rate"
|
||||||
|
source: sensor.edgerouter_home_automation_2_received_bytes
|
||||||
|
unit_prefix: k
|
||||||
|
unit_time: s
|
||||||
|
round: 1
|
||||||
|
time_window: "00:00:30"
|
||||||
|
|
||||||
|
# Derive the secondary Home Automation download rate from transmitted bytes.
|
||||||
|
- platform: derivative
|
||||||
|
name: "EdgeRouter Home Automation 2 Download Rate"
|
||||||
|
unique_id: "edgerouter_home_automation_2_download_rate"
|
||||||
|
source: sensor.edgerouter_home_automation_2_transmitted_bytes
|
||||||
|
unit_prefix: k
|
||||||
|
unit_time: s
|
||||||
|
round: 1
|
||||||
|
time_window: "00:00:30"
|
||||||
|
|
||||||
|
#:######################################################################################:#
|
||||||
|
# ROUTER IDENTITY AND UPTIME
|
||||||
|
#:######################################################################################:#
|
||||||
|
# sysDescr reports the installed EdgeOS firmware and build information.
|
||||||
|
- platform: snmp
|
||||||
|
name: "EdgeRouter Firmware"
|
||||||
|
unique_id: "edgerouter_firmware"
|
||||||
|
host: !secret edgerouter_host
|
||||||
|
community: !secret edgerouter_snmp_community
|
||||||
|
version: 2c
|
||||||
|
baseoid: 1.3.6.1.2.1.1.1.0
|
||||||
|
accept_errors: true
|
||||||
|
scan_interval: 300
|
||||||
|
|
||||||
|
# hrSystemUptime is reported in hundredths of a second and converted to seconds.
|
||||||
|
- platform: snmp
|
||||||
|
name: "EdgeRouter Uptime"
|
||||||
|
unique_id: "edgerouter_uptime"
|
||||||
|
host: !secret edgerouter_host
|
||||||
|
community: !secret edgerouter_snmp_community
|
||||||
|
version: 2c
|
||||||
|
baseoid: 1.3.6.1.2.1.25.1.1.0
|
||||||
|
accept_errors: true
|
||||||
|
device_class: duration
|
||||||
|
state_class: measurement
|
||||||
|
unit_of_measurement: "s"
|
||||||
|
value_template: "{{ ((value | default(0) | float(0)) / 100) | round(0) }}"
|
||||||
|
scan_interval: 60
|
||||||
|
|
||||||
|
#:######################################################################################:#
|
||||||
|
# CPU USAGE AND LOAD AVERAGES
|
||||||
|
#:######################################################################################:#
|
||||||
|
# UCD-SNMP reports CPU idle percentage, so usage is calculated as 100 - idle.
|
||||||
|
- platform: snmp
|
||||||
|
name: "EdgeRouter CPU Usage"
|
||||||
|
unique_id: "edgerouter_cpu_usage"
|
||||||
|
host: !secret edgerouter_host
|
||||||
|
community: !secret edgerouter_snmp_community
|
||||||
|
version: 2c
|
||||||
|
baseoid: 1.3.6.1.4.1.2021.11.11.0
|
||||||
|
accept_errors: true
|
||||||
|
state_class: measurement
|
||||||
|
unit_of_measurement: "%"
|
||||||
|
value_template: "{{ 100 - (value | default(100) | int(100)) }}"
|
||||||
|
scan_interval: 60
|
||||||
|
|
||||||
|
# One-minute load average from the UCD-SNMP load table.
|
||||||
|
- platform: snmp
|
||||||
|
name: "EdgeRouter Load 1 Minute"
|
||||||
|
unique_id: "edgerouter_load_1_minute"
|
||||||
|
host: !secret edgerouter_host
|
||||||
|
community: !secret edgerouter_snmp_community
|
||||||
|
version: 2c
|
||||||
|
baseoid: 1.3.6.1.4.1.2021.10.1.3.1
|
||||||
|
accept_errors: true
|
||||||
|
state_class: measurement
|
||||||
|
value_template: "{{ value | default(0) | float(0) }}"
|
||||||
|
scan_interval: 60
|
||||||
|
|
||||||
|
# Five-minute load average from the UCD-SNMP load table.
|
||||||
|
- platform: snmp
|
||||||
|
name: "EdgeRouter Load 5 Minutes"
|
||||||
|
unique_id: "edgerouter_load_5_minutes"
|
||||||
|
host: !secret edgerouter_host
|
||||||
|
community: !secret edgerouter_snmp_community
|
||||||
|
version: 2c
|
||||||
|
baseoid: 1.3.6.1.4.1.2021.10.1.3.2
|
||||||
|
accept_errors: true
|
||||||
|
state_class: measurement
|
||||||
|
value_template: "{{ value | default(0) | float(0) }}"
|
||||||
|
scan_interval: 60
|
||||||
|
|
||||||
|
# Fifteen-minute load average from the UCD-SNMP load table.
|
||||||
|
- platform: snmp
|
||||||
|
name: "EdgeRouter Load 15 Minutes"
|
||||||
|
unique_id: "edgerouter_load_15_minutes"
|
||||||
|
host: !secret edgerouter_host
|
||||||
|
community: !secret edgerouter_snmp_community
|
||||||
|
version: 2c
|
||||||
|
baseoid: 1.3.6.1.4.1.2021.10.1.3.3
|
||||||
|
accept_errors: true
|
||||||
|
state_class: measurement
|
||||||
|
value_template: "{{ value | default(0) | float(0) }}"
|
||||||
|
scan_interval: 60
|
||||||
|
|
||||||
|
#:######################################################################################:#
|
||||||
|
# MEMORY COUNTERS
|
||||||
|
#:######################################################################################:#
|
||||||
|
# Total physical memory reported by UCD-SNMP in KiB.
|
||||||
|
- platform: snmp
|
||||||
|
name: "EdgeRouter Memory Total"
|
||||||
|
unique_id: "edgerouter_memory_total"
|
||||||
|
host: !secret edgerouter_host
|
||||||
|
community: !secret edgerouter_snmp_community
|
||||||
|
version: 2c
|
||||||
|
baseoid: 1.3.6.1.4.1.2021.4.5.0
|
||||||
|
accept_errors: true
|
||||||
|
device_class: data_size
|
||||||
|
state_class: measurement
|
||||||
|
unit_of_measurement: "KiB"
|
||||||
|
scan_interval: 60
|
||||||
|
|
||||||
|
# Currently free memory reported by UCD-SNMP in KiB.
|
||||||
|
- platform: snmp
|
||||||
|
name: "EdgeRouter Memory Free"
|
||||||
|
unique_id: "edgerouter_memory_free"
|
||||||
|
host: !secret edgerouter_host
|
||||||
|
community: !secret edgerouter_snmp_community
|
||||||
|
version: 2c
|
||||||
|
baseoid: 1.3.6.1.4.1.2021.4.6.0
|
||||||
|
accept_errors: true
|
||||||
|
device_class: data_size
|
||||||
|
state_class: measurement
|
||||||
|
unit_of_measurement: "KiB"
|
||||||
|
scan_interval: 60
|
||||||
|
|
||||||
|
# Memory allocated to buffers, excluded from calculated active usage.
|
||||||
|
- platform: snmp
|
||||||
|
name: "EdgeRouter Memory Buffers"
|
||||||
|
unique_id: "edgerouter_memory_buffers"
|
||||||
|
host: !secret edgerouter_host
|
||||||
|
community: !secret edgerouter_snmp_community
|
||||||
|
version: 2c
|
||||||
|
baseoid: 1.3.6.1.4.1.2021.4.14.0
|
||||||
|
accept_errors: true
|
||||||
|
device_class: data_size
|
||||||
|
state_class: measurement
|
||||||
|
unit_of_measurement: "KiB"
|
||||||
|
scan_interval: 60
|
||||||
|
|
||||||
|
# Memory allocated to cache, excluded from calculated active usage.
|
||||||
|
- platform: snmp
|
||||||
|
name: "EdgeRouter Memory Cached"
|
||||||
|
unique_id: "edgerouter_memory_cached"
|
||||||
|
host: !secret edgerouter_host
|
||||||
|
community: !secret edgerouter_snmp_community
|
||||||
|
version: 2c
|
||||||
|
baseoid: 1.3.6.1.4.1.2021.4.15.0
|
||||||
|
accept_errors: true
|
||||||
|
device_class: data_size
|
||||||
|
state_class: measurement
|
||||||
|
unit_of_measurement: "KiB"
|
||||||
|
scan_interval: 60
|
||||||
|
|
||||||
|
#:######################################################################################:#
|
||||||
|
# DERIVED MEMORY USAGE
|
||||||
|
#:######################################################################################:#
|
||||||
|
# Calculate active memory as total minus free, buffers, and cache. The template
|
||||||
|
# remains unavailable until every source counter has a valid state.
|
||||||
|
template:
|
||||||
|
- sensor:
|
||||||
|
- name: "EdgeRouter Memory Usage"
|
||||||
|
unique_id: "edgerouter_memory_usage"
|
||||||
|
unit_of_measurement: "%"
|
||||||
|
state_class: measurement
|
||||||
|
availability: >-
|
||||||
|
{{
|
||||||
|
has_value('sensor.edgerouter_memory_total')
|
||||||
|
and has_value('sensor.edgerouter_memory_free')
|
||||||
|
and has_value('sensor.edgerouter_memory_buffers')
|
||||||
|
and has_value('sensor.edgerouter_memory_cached')
|
||||||
|
}}
|
||||||
|
state: >-
|
||||||
|
{{
|
||||||
|
(
|
||||||
|
(
|
||||||
|
(states('sensor.edgerouter_memory_total') | float(0))
|
||||||
|
- (states('sensor.edgerouter_memory_free') | float(0))
|
||||||
|
- (states('sensor.edgerouter_memory_buffers') | float(0))
|
||||||
|
- (states('sensor.edgerouter_memory_cached') | float(0))
|
||||||
|
)
|
||||||
|
/ (states('sensor.edgerouter_memory_total') | float(1))
|
||||||
|
* 100
|
||||||
|
)
|
||||||
|
| round(1)
|
||||||
|
}}
|
||||||
@@ -4,22 +4,27 @@ template:
|
|||||||
unique_id: internet_downtraffic
|
unique_id: internet_downtraffic
|
||||||
unit_of_measurement: "kB/s"
|
unit_of_measurement: "kB/s"
|
||||||
state_class: measurement
|
state_class: measurement
|
||||||
state: >
|
availability: "{{ has_value('sensor.edgerouter_wan_download_rate') }}"
|
||||||
{{ state_attr('binary_sensor.edgeos_interface_pppoe0', 'KBytes/ps (Received)') | float(0) }}
|
state: "{{ states('sensor.edgerouter_wan_download_rate') | float(0) }}"
|
||||||
|
|
||||||
- name: "Internet Upload Traffic"
|
- name: "Internet Upload Traffic"
|
||||||
unique_id: internet_uptraffic
|
unique_id: internet_uptraffic
|
||||||
unit_of_measurement: "kB/s"
|
unit_of_measurement: "kB/s"
|
||||||
state_class: measurement
|
state_class: measurement
|
||||||
state: >
|
availability: "{{ has_value('sensor.edgerouter_wan_upload_rate') }}"
|
||||||
{{ state_attr('binary_sensor.edgeos_interface_pppoe0', 'KBytes/ps (Sent)') | float(0) }}
|
state: "{{ states('sensor.edgerouter_wan_upload_rate') | float(0) }}"
|
||||||
|
|
||||||
- name: "Internet Total Traffic"
|
- name: "Internet Total Traffic"
|
||||||
unique_id: internet_totaltraffic
|
unique_id: internet_totaltraffic
|
||||||
unit_of_measurement: "kB/s"
|
unit_of_measurement: "kB/s"
|
||||||
state_class: measurement
|
state_class: measurement
|
||||||
state: >
|
availability: >-
|
||||||
{{
|
{{
|
||||||
(state_attr('binary_sensor.edgeos_interface_pppoe0', 'KBytes/ps (Received)') | float(0))
|
has_value('sensor.edgerouter_wan_download_rate')
|
||||||
+ (state_attr('binary_sensor.edgeos_interface_pppoe0', 'KBytes/ps (Sent)') | float(0))
|
and has_value('sensor.edgerouter_wan_upload_rate')
|
||||||
|
}}
|
||||||
|
state: >-
|
||||||
|
{{
|
||||||
|
(states('sensor.edgerouter_wan_download_rate') | float(0))
|
||||||
|
+ (states('sensor.edgerouter_wan_upload_rate') | float(0))
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
template:
|
||||||
|
- sensor:
|
||||||
|
- name: "Internet Download Traffic"
|
||||||
|
unique_id: internet_downtraffic
|
||||||
|
unit_of_measurement: "kB/s"
|
||||||
|
state_class: measurement
|
||||||
|
state: >
|
||||||
|
{{ state_attr('binary_sensor.edgeos_interface_pppoe0', 'KBytes/ps (Received)') | float(0) }}
|
||||||
|
|
||||||
|
- name: "Internet Upload Traffic"
|
||||||
|
unique_id: internet_uptraffic
|
||||||
|
unit_of_measurement: "kB/s"
|
||||||
|
state_class: measurement
|
||||||
|
state: >
|
||||||
|
{{ state_attr('binary_sensor.edgeos_interface_pppoe0', 'KBytes/ps (Sent)') | float(0) }}
|
||||||
|
|
||||||
|
- name: "Internet Total Traffic"
|
||||||
|
unique_id: internet_totaltraffic
|
||||||
|
unit_of_measurement: "kB/s"
|
||||||
|
state_class: measurement
|
||||||
|
state: >
|
||||||
|
{{
|
||||||
|
(state_attr('binary_sensor.edgeos_interface_pppoe0', 'KBytes/ps (Received)') | float(0))
|
||||||
|
+ (state_attr('binary_sensor.edgeos_interface_pppoe0', 'KBytes/ps (Sent)') | float(0))
|
||||||
|
}}
|
||||||
@@ -1,3 +1,28 @@
|
|||||||
|
#:########################################################################################:#
|
||||||
|
# Package: Pet Feeding and Jobs Timing
|
||||||
|
# File: pet_feeding_and_jobs.yaml
|
||||||
|
# Version: v1.1.0
|
||||||
|
# Date: 2026-07-21
|
||||||
|
#
|
||||||
|
# Purpose:
|
||||||
|
# Provides legacy MQTT pet activity sensors and Jobs-derived timing sensors for
|
||||||
|
# the Lounge dog-feeding and depooping gauges.
|
||||||
|
#
|
||||||
|
# Behaviour:
|
||||||
|
# - Dog feeding reports the newest valid morning or evening completion time.
|
||||||
|
# - Dog depooping reports the weekly Jobs completion time.
|
||||||
|
# - Invalid or unrestored Jobs attributes make a timing sensor unavailable
|
||||||
|
# instead of exposing a numeric sentinel value.
|
||||||
|
#
|
||||||
|
# Version History:
|
||||||
|
# - v1.1.0 (2026-07-21): Reject invalid Jobs timing attributes and remove the
|
||||||
|
# 999999-hour fallback from the dog-feeding gauge source.
|
||||||
|
# - v1.0.0 (2026-07-21): Added Jobs-derived dog timing sensors for P010.
|
||||||
|
#:########################################################################################:#
|
||||||
|
|
||||||
|
#:######################################################################################:#
|
||||||
|
# LEGACY MQTT PET ACTIVITY AND TIMING SENSORS
|
||||||
|
#:######################################################################################:#
|
||||||
mqtt:
|
mqtt:
|
||||||
sensor:
|
sensor:
|
||||||
- unique_id: dog_fed_activity
|
- unique_id: dog_fed_activity
|
||||||
@@ -25,6 +50,9 @@ mqtt:
|
|||||||
state_topic: "viewroad-tele/petfeeding/hoursSinceDePooped"
|
state_topic: "viewroad-tele/petfeeding/hoursSinceDePooped"
|
||||||
unit_of_measurement: "Hours"
|
unit_of_measurement: "Hours"
|
||||||
|
|
||||||
|
#:######################################################################################:#
|
||||||
|
# JOBS-DERIVED LOUNGE GAUGE SENSORS
|
||||||
|
#:######################################################################################:#
|
||||||
template:
|
template:
|
||||||
- sensor:
|
- sensor:
|
||||||
- name: "Jobs Tracker Hours Since Dog Fed"
|
- name: "Jobs Tracker Hours Since Dog Fed"
|
||||||
@@ -34,19 +62,21 @@ template:
|
|||||||
unit_of_measurement: "h"
|
unit_of_measurement: "h"
|
||||||
icon: "mdi:dog-side"
|
icon: "mdi:dog-side"
|
||||||
availability: >-
|
availability: >-
|
||||||
{{
|
{% set morning = state_attr('sensor.jobs_tracker_dog_feeding_morning', 'hours_since_completed') %}
|
||||||
state_attr('sensor.jobs_tracker_dog_feeding_morning', 'hours_since_completed') is not none
|
{% set evening = state_attr('sensor.jobs_tracker_dog_feeding_evening', 'hours_since_completed') %}
|
||||||
or state_attr('sensor.jobs_tracker_dog_feeding_evening', 'hours_since_completed') is not none
|
{{ is_number(morning) or is_number(evening) }}
|
||||||
}}
|
|
||||||
state: >-
|
state: >-
|
||||||
{{
|
{% set morning = state_attr('sensor.jobs_tracker_dog_feeding_morning', 'hours_since_completed') %}
|
||||||
[
|
{% set evening = state_attr('sensor.jobs_tracker_dog_feeding_evening', 'hours_since_completed') %}
|
||||||
state_attr('sensor.jobs_tracker_dog_feeding_morning', 'hours_since_completed') | float(999999),
|
{% if is_number(morning) and is_number(evening) %}
|
||||||
state_attr('sensor.jobs_tracker_dog_feeding_evening', 'hours_since_completed') | float(999999)
|
{{ [morning | float(0), evening | float(0)] | min | round(0) }}
|
||||||
]
|
{% elif is_number(morning) %}
|
||||||
| min
|
{{ morning | float(0) | round(0) }}
|
||||||
| round(0)
|
{% elif is_number(evening) %}
|
||||||
}}
|
{{ evening | float(0) | round(0) }}
|
||||||
|
{% else %}
|
||||||
|
{{ none }}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
- name: "Jobs Tracker Hours Since Dog Depooped"
|
- name: "Jobs Tracker Hours Since Dog Depooped"
|
||||||
unique_id: "jobs_tracker_hours_since_dog_depooped"
|
unique_id: "jobs_tracker_hours_since_dog_depooped"
|
||||||
@@ -55,6 +85,8 @@ template:
|
|||||||
unit_of_measurement: "h"
|
unit_of_measurement: "h"
|
||||||
icon: "mdi:emoticon-poop"
|
icon: "mdi:emoticon-poop"
|
||||||
availability: >-
|
availability: >-
|
||||||
{{ state_attr('sensor.jobs_tracker_dog_depooping', 'hours_since_completed') is not none }}
|
{% set hours = state_attr('sensor.jobs_tracker_dog_depooping', 'hours_since_completed') %}
|
||||||
|
{{ is_number(hours) }}
|
||||||
state: >-
|
state: >-
|
||||||
{{ state_attr('sensor.jobs_tracker_dog_depooping', 'hours_since_completed') | float(none) }}
|
{% set hours = state_attr('sensor.jobs_tracker_dog_depooping', 'hours_since_completed') %}
|
||||||
|
{{ hours | float(0) if is_number(hours) else none }}
|
||||||
|
|||||||
Reference in New Issue
Block a user