Compare commits

...

3 Commits

Author SHA1 Message Date
zorruno 8fdcb128a3 various tidyups 2026-07-22 22:15:25 +12:00
ESPHome Device Builder b9e9c98f7e Edit esp-centralstairs-bottom.yaml 2026-07-21 18:28:33 +12:00
ESPHome Device Builder 6dc9b56ff7 Edit esp-centralstairs-top.yaml 2026-07-21 18:28:33 +12:00
17 changed files with 843 additions and 2049 deletions
+1 -1
View File
@@ -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}
+63 -818
View File
@@ -1,820 +1,65 @@
# 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.
## CRITICAL: User Consent and Scope Rules
You MUST follow these rules strictly:
1. **Never exceed the user's request** - Do exactly what the user asks, nothing more. Do not "improve" or "enhance" beyond the stated scope.
2. **Never make changes without explicit approval** - Before modifying ANY file:
- Show the user exactly what you plan to change
- Wait for their explicit confirmation ("yes", "go ahead", "do it", etc.)
- If they haven't approved, DO NOT proceed
3. **Ask, don't assume** - If the user's request is ambiguous:
- Ask clarifying questions first
- Present options and let them choose
- Never guess at their intent
4. **Read-only by default** - When investigating or troubleshooting:
- Only read files and gather information
- Present findings and recommendations
- Wait for user instruction before making any changes
5. **One change at a time** - When making approved changes:
- Make the minimum change needed
- Show what was changed
- Let the user verify before proceeding to any next step
6. **No unsolicited modifications** - Never:
- "Clean up" code the user didn't ask about
- Add features they didn't request
- Refactor working configurations
- Fix issues they haven't mentioned
7. **Respect "no"** - If a user declines a suggestion, do not:
- Repeat the suggestion
- Make the change anyway
- Try to convince them otherwise
## Project Context Protocol
- Treat every user request as related to a tracked project.
- Identify the project by ID or name before beginning work.
- If the project is unclear, ask the user which project applies.
- Read `PROJECTS.md` and the relevant project section before investigating or making recommendations.
- Keep project IDs stable.
- Record durable decisions, changes, verification results, blockers, and next steps under the relevant project.
- Follow the existing approval rules before writing context updates.
- Never mark a project `Complete` unless the user explicitly confirms completion.
- When a project appears complete, ask: "Should I close Pxxx?"
- Do not store secrets, credentials, or large transient logs in project context.
## Environment Context
- You are running inside the OpenCode app
- The current directory (`/homeassistant`) contains the live Home Assistant configuration
- 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.
- You may have access to MCP tools for interacting with Home Assistant (check with the user)
## Home Assistant Interaction Model
There are three primary, safe ways to interact with Home Assistant:
### 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
You are working directly inside a live Home Assistant installation (`/homeassistant`).
## Critical Mandates & Approval Rules
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").
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.
5. **Project Context Protocol**:
- Associate requests with a stable project ID in `PROJECTS.md` (e.g. `P013`).
- Read `PROJECTS.md` before starting work. Record durable context under the project ID.
- Never mark a project `Complete` unless the user explicitly confirms completion. Ask: "Should I close Pxxx?"
## Off-Limits & Restricted Paths
Never read, write, or access:
- `.storage/`, `.cloud/`, `deps/`, `tts/`
- `home-assistant_v2.db*`, `home-assistant.log*`
- `secrets.yaml` (never expose or log secret contents)
Use MCP tools (`get_devices`, `get_areas`, `get_entity_details`, `get_history`) or CLI tools (`hab`) instead of accessing internal storage or database files.
## Configuration & Architecture Gotchas
- **Directory Includes in `configuration.yaml`**:
- `automation`: Uses `!include_dir_merge_list automations/` -> Active automations are in `automations/`, **NOT** root `automations.yaml` (which is legacy/inactive).
- `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`.
- `packages`: Uses `!include_dir_named packages/`.
- `template`: Uses `!include_dir_merge_list templates/`.
- `sensor`: Uses `!include_dir_merge_list sensor/`.
## CLI & Tooling Reference
### Shell YAML Queries (`yq`)
- **Always use `yq`** (mikefarah/Go version) for CLI YAML processing. PyYAML and Ruby YAML crash on HA tags (`!include`, `!secret`).
- Example: `yq '.homeassistant.latitude' configuration.yaml`
### Home Assistant Admin (`hab` CLI)
- Pre-authenticated CLI tool for managing HA via terminal (`hab <command> --json`).
- Common commands:
- Entities & Devices: `hab entity list --domain light --json`, `hab device list --json`
- Automations/Scripts: `hab automation list --json`, `hab script list --json`
- Dashboards: `hab dashboard list --json`, `hab dashboard get <id> --json`
- Operations: `hab system health --json`, `hab backup create`
### Zigbee Toolkit (`zigporter` CLI)
- Handles cascade renames across automations, scripts, scenes, and Lovelace dashboards.
- Command: `zigporter rename-entity light.old light.new --apply` (Defaults to dry-run if `--apply` omitted).
- Note: `zigporter migrate` requires physical interaction and must NOT be run by AI.
### Safe Configuration Writing (MCP)
- 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.
## Mandatory YAML Style Rules
- **Indentation**: 2 spaces (no tabs).
- **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.
- **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.
- **Templates**: Use `states('sensor.x')` and `is_state(...)` helpers. Never use direct state object access (`states.sensor.x.state`).
+61 -28
View File
@@ -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.
- Use these statuses: `Active`, `Paused`, and `Complete`.
## Active Projects
| ID | Project | Status | Next Step | Last Updated |
| --- | --- | --- | --- | --- |
| 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 |
| 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 |
| 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 |
| 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 |
| P009 | Lounge front-door camera layout | 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 |
| 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.
- 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.
- 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.
- 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.
- 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.
- 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.
- Key files: Related device configurations under `esphome/`, including lounge, kitchen, office, bedroom, breakfast-bar, and master-bedroom devices.
- 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.
- 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 `esphome/esp-centralstairs-top.yaml`, `esphome/esp-centralstairs-bottom.yaml`, and `packages/Lighting_2Way_Sync/central_stairway_2way_sync.yaml`.
- 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-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.
- 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.
- 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.
- 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`.
- 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-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.
- 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.
- 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.
- Current context: Added the lower main stair lights and stair footer lights to the bedtime shutdown sequence with no delay.
- Key files: `packages/bedtime_mode_actions.yaml`.
- Next step: Run bedtime mode and verify both relays switch off as intended.
- 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` and `packages/Lighting_2Way_Sync/central_stairway_2way_sync.yaml`.
- 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-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.
- 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 - 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.
- 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 - 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.
- 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.
- Key file: `packages/pet_feeding_and_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.
- Key files: `packages/pet_feeding_and_jobs.yaml` and `packages/Job_Chore_Tracking/jobs_tracker_jobs.yaml`.
- 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.
- 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 - 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.
- 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 - 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.
- 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.
- Next step: Inspect the current configuration, entities, errors, and documentation without changing runtime behavior.
- 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.
- 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 - 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.
-3
View File
@@ -39,6 +39,3 @@ zha:
salus_provider: true
inovelli_provider: true
thirdreality_provider: true
device_tracker:
- platform: bluetooth_le_tracker
+44
View File
@@ -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
+33 -10
View File
@@ -6,6 +6,7 @@
# https://home.fox.co.nz/gitea/zorruno/zorruno-homeassistant/src/branch/master/esphome/esp-centralstairs-bottom.yaml
#:########################################################################################:#
# 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.7 2025-09-24 Upload to this device
# V3.5 2025-07-24 YAML tidyups
@@ -83,7 +84,7 @@ substitutions:
# Project Naming
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_command_main_topic: !secret mqtt_command_main_topic
@@ -184,19 +185,19 @@ mqtt:
then:
- if:
condition:
lambda: 'return id(Relay_1).state;'
lambda: "return id(Relay_1).state;"
then:
- mqtt.publish:
topic: "${mqtt_status_topic_1}"
payload: "${mqtt_command_ON}"
qos: 0
retain: false
qos: 1
retain: true
else:
- mqtt.publish:
topic: "${mqtt_status_topic_1}"
payload: "${mqtt_command_OFF}"
qos: 0
retain: false
qos: 1
retain: true
on_message:
# Relay 1: only turn ON if currently OFF
@@ -241,6 +242,26 @@ mqtt:
then:
- 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:
# 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
- if:
condition:
lambda: 'return id(Relay_2).state;'
lambda: "return id(Relay_2).state;"
then:
- mqtt.publish:
topic: "${mqtt_command_topic_2}"
@@ -332,15 +353,17 @@ switch:
- mqtt.publish:
topic: "${mqtt_status_topic_1}"
payload: "${mqtt_command_ON}"
retain: false
qos: 1
retain: true
on_turn_off:
- mqtt.publish:
topic: "${mqtt_status_topic_1}"
payload: "${mqtt_command_OFF}"
retain: false
qos: 1
retain: true
- platform: gpio
name: "Relay 2: ${switch_2_name}"
pin: GPIO12
id: Relay_2
# No mqtt.publish here (this is a mirror only)
# No mqtt.publish here (this is a mirror only)
+32 -9
View File
@@ -6,6 +6,7 @@
# https://home.fox.co.nz/gitea/zorruno/zorruno-homeassistant/src/branch/master/esphome/esp-centralstairs-top.yaml
#:########################################################################################:#
# 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.7 2025-09-24 Upload to this device
# V3.5 2025-07-24 YAML tidyups
@@ -83,7 +84,7 @@ substitutions:
# Project Naming
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_command_main_topic: !secret mqtt_command_main_topic
@@ -184,19 +185,19 @@ mqtt:
then:
- if:
condition:
lambda: 'return id(Relay_2).state;'
lambda: "return id(Relay_2).state;"
then:
- mqtt.publish:
topic: "${mqtt_status_topic_2}"
payload: "${mqtt_command_ON}"
qos: 0
retain: false
qos: 1
retain: true
else:
- mqtt.publish:
topic: "${mqtt_status_topic_2}"
payload: "${mqtt_command_OFF}"
qos: 0
retain: false
qos: 1
retain: true
on_message:
# Relay 1: only turn ON if currently OFF
@@ -241,6 +242,26 @@ mqtt:
then:
- 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:
# https://esphome.io/components/status_led.html
@@ -286,7 +307,7 @@ binary_sensor:
then:
- if:
condition:
lambda: 'return id(Relay_1).state;'
lambda: "return id(Relay_1).state;"
then:
- mqtt.publish:
topic: "${mqtt_command_topic_1}"
@@ -338,9 +359,11 @@ switch:
- mqtt.publish:
topic: "${mqtt_status_topic_2}"
payload: "${mqtt_command_ON}"
retain: false
qos: 1
retain: true
on_turn_off:
- mqtt.publish:
topic: "${mqtt_status_topic_2}"
payload: "${mqtt_command_OFF}"
retain: false
qos: 1
retain: true
-507
View File
@@ -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)"
-477
View File
@@ -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
-136
View File
@@ -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
#
# VERSION:
# V2.3 2026-07-20
# V2.4 2026-07-21
#
# PURPOSE:
# Defines the job registry, MQTT Discovery entities, completion triggers and
@@ -41,6 +41,12 @@
# Complete
#
# 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
# - All completion methods now obey the configured completion window.
# - Physical completion entities, Home Assistant discovery buttons and direct
@@ -360,7 +366,7 @@ script:
device_name: "Jobs Tracker"
device_manufacturer: "View Road Home Assistant"
device_model: "Shared household jobs tracker"
device_sw_version: "2.3"
device_sw_version: "2.4"
value_template_state: >-
{% raw %}{{ value_json.state | default('unknown') }}{% endraw %}
@@ -697,10 +703,23 @@ automation:
actions:
- action: script.jobs_tracker_publish_discovery
# Give Home Assistant a moment to process MQTT Discovery.
- delay: "00:00:10"
- variables:
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
data:
update_all: true
@@ -711,25 +730,26 @@ automation:
mode: single
triggers:
- trigger: homeassistant
event: start
id: ha_start
- trigger: time_pattern
minutes: "/1"
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:
- 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
data:
update_all: true
@@ -747,9 +767,18 @@ automation:
conditions:
- condition: 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
and trigger.to_state.state not in ['unknown', 'unavailable']
valid_state
and (
not event_entity
or (event_ts > 0 and event_age >= 0 and event_age <= 30)
)
}}
actions:
@@ -9,7 +9,6 @@ automation:
members:
- switch.esp_centralstairs_top_relay_1_main_stair_lights
- switch.esp_centralstairs_bottom_relay_1_main_stair_lights_lower
- switch.esp_loungemiddleswitch_relay_3_downlights_north
sync_on_start: true
startup_delay: 5
+476
View File
@@ -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)
}}
+12 -7
View File
@@ -4,22 +4,27 @@ template:
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) }}
availability: "{{ has_value('sensor.edgerouter_wan_download_rate') }}"
state: "{{ states('sensor.edgerouter_wan_download_rate') | 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) }}
availability: "{{ has_value('sensor.edgerouter_wan_upload_rate') }}"
state: "{{ states('sensor.edgerouter_wan_upload_rate') | float(0) }}"
- name: "Internet Total Traffic"
unique_id: internet_totaltraffic
unit_of_measurement: "kB/s"
state_class: measurement
state: >
availability: >-
{{
(state_attr('binary_sensor.edgeos_interface_pppoe0', 'KBytes/ps (Received)') | float(0))
+ (state_attr('binary_sensor.edgeos_interface_pppoe0', 'KBytes/ps (Sent)') | float(0))
has_value('sensor.edgerouter_wan_download_rate')
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))
}}
+25
View File
@@ -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))
}}
+46 -14
View File
@@ -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:
sensor:
- unique_id: dog_fed_activity
@@ -25,6 +50,9 @@ mqtt:
state_topic: "viewroad-tele/petfeeding/hoursSinceDePooped"
unit_of_measurement: "Hours"
#:######################################################################################:#
# JOBS-DERIVED LOUNGE GAUGE SENSORS
#:######################################################################################:#
template:
- sensor:
- name: "Jobs Tracker Hours Since Dog Fed"
@@ -34,19 +62,21 @@ template:
unit_of_measurement: "h"
icon: "mdi:dog-side"
availability: >-
{{
state_attr('sensor.jobs_tracker_dog_feeding_morning', 'hours_since_completed') is not none
or state_attr('sensor.jobs_tracker_dog_feeding_evening', 'hours_since_completed') is not none
}}
{% 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') %}
{{ is_number(morning) or is_number(evening) }}
state: >-
{{
[
state_attr('sensor.jobs_tracker_dog_feeding_morning', 'hours_since_completed') | float(999999),
state_attr('sensor.jobs_tracker_dog_feeding_evening', 'hours_since_completed') | float(999999)
]
| min
| round(0)
}}
{% 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') %}
{% if is_number(morning) and is_number(evening) %}
{{ [morning | float(0), evening | float(0)] | min | round(0) }}
{% elif is_number(morning) %}
{{ morning | float(0) | round(0) }}
{% elif is_number(evening) %}
{{ evening | float(0) | round(0) }}
{% else %}
{{ none }}
{% endif %}
- name: "Jobs Tracker Hours Since Dog Depooped"
unique_id: "jobs_tracker_hours_since_dog_depooped"
@@ -55,6 +85,8 @@ template:
unit_of_measurement: "h"
icon: "mdi:emoticon-poop"
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_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 }}