Compare commits
7 Commits
4e2107f7f7
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| fe6feee250 | |||
| fce8c7b0bc | |||
| f46b29e6ab | |||
| 32df6ce407 | |||
| 8fdcb128a3 | |||
| b9e9c98f7e | |||
| 6dc9b56ff7 |
+1
-1
@@ -1 +1 @@
|
|||||||
{"pid": 71, "version": 1, "ha_version": "2026.7.2", "start_ts": 1784593872.104367}
|
{"pid": 74, "version": 1, "ha_version": "2026.7.3", "start_ts": 1784842567.418518}
|
||||||
@@ -1,820 +0,0 @@
|
|||||||
# 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
|
|
||||||
-138
@@ -1,138 +0,0 @@
|
|||||||
# Project Tracker
|
|
||||||
|
|
||||||
Use this file as the source of truth for projects being worked on with OpenCode.
|
|
||||||
|
|
||||||
- Associate each user request with a project ID before beginning work.
|
|
||||||
- Ask the user which project applies when the request is ambiguous.
|
|
||||||
- Keep project IDs stable and use the relevant section to store durable context.
|
|
||||||
- Update context as work progresses, subject to the approval rules in `AGENTS.md`.
|
|
||||||
- Never mark a project complete unless the user explicitly confirms it is complete.
|
|
||||||
- Ask the user before closing a project that appears complete.
|
|
||||||
- Use these statuses: `Active`, `Paused`, and `Complete`.
|
|
||||||
|
|
||||||
| 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 |
|
|
||||||
| 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 |
|
|
||||||
| 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 |
|
|
||||||
|
|
||||||
## 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.
|
|
||||||
- Key files: `esphome/esp-bedside-panel.yaml`.
|
|
||||||
- Known issue: The file changelog records V1.47 while `project_version` still reports v1.46.
|
|
||||||
- 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
|
|
||||||
|
|
||||||
- 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.
|
|
||||||
- Key files: `esphome/esp-reterminal-e1001.yaml`.
|
|
||||||
- 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
|
|
||||||
|
|
||||||
- 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.
|
|
||||||
- Activity: 2026-07-20 - Applied the MQTT topic normalization across the related devices.
|
|
||||||
|
|
||||||
## 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.
|
|
||||||
- Key files: `packages/Appliance_Monitoring/appliance_monitor_processor.yaml`, `packages/Appliance_Monitoring/appliances_status_monitoring.yaml`, `packages/view_road_announcement_router.yaml`, and `packages/appliance_status_mqttfeed.yaml`.
|
|
||||||
- Known issues: MQTT feed channels may not yet be enabled for each appliance, and timestamp sensors may receive time-only payloads.
|
|
||||||
- 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
|
|
||||||
|
|
||||||
- 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.
|
|
||||||
- Activity: 2026-07-20 - Implemented the V2.3 registry and processor changes.
|
|
||||||
|
|
||||||
## 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.
|
|
||||||
- Key files: `packages/open_meteo_rain_chance.yaml` and `esphome/esp-bedside-panel.yaml`.
|
|
||||||
- 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
|
|
||||||
|
|
||||||
- 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.
|
|
||||||
- Activity: 2026-07-20 - Added both central stair relays to bedtime shutdown.
|
|
||||||
|
|
||||||
## 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.
|
|
||||||
- Replacement entities: `sensor.bedroom_1_environment_z19th_temperature` and `sensor.bedroom_1_environment_z19th_humidity`.
|
|
||||||
- Verification: The stored card definitions reference the replacement entities, and Spook no longer reports the Master Bedroom Echo Show issue. Automated visual verification was unavailable because the screenshot tool is disabled.
|
|
||||||
- Next step: None.
|
|
||||||
- Activity: 2026-07-21 - Created the project and selected the live Bedroom 1 Environment (Z19TH) sensor pair.
|
|
||||||
- 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
|
|
||||||
|
|
||||||
- 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.
|
|
||||||
- Verification: The saved view retains all seven cards and the Front Door Camera live-view settings, and `camera.camera2` is streaming. Automated visual verification was unavailable because the screenshot tool is disabled.
|
|
||||||
- Next step: None.
|
|
||||||
- Activity: 2026-07-21 - Created the project and identified the Front Door Camera card on the Lounge Home view.
|
|
||||||
- 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
|
|
||||||
|
|
||||||
- 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.
|
|
||||||
- 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.
|
|
||||||
- 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.
|
|
||||||
|
|
||||||
## 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.
|
|
||||||
- Removed duplicate: `input_boolean.guest_area_occupied`, which was UI-managed and referenced only by the Test dashboard.
|
|
||||||
- Current context: All nine stored Test dashboard references now use the canonical helper, the duplicate helper has been deleted, and the Lounge row of eight downstairs-flat light icons now greys out and blocks taps while occupied.
|
|
||||||
- Verification: The duplicate returns entity not found, the canonical helper remains available with its automation and script relationships, no Test dashboard broken-reference repair was created, and the saved Lounge templates render both the enabled and occupied branches correctly. Automated visual verification was unavailable because the screenshot tool is disabled.
|
|
||||||
- Next step: None.
|
|
||||||
- Activity: 2026-07-21 - Created the project and completed the dependency audit.
|
|
||||||
- Activity: 2026-07-21 - Migrated the Test dashboard references and deleted the duplicate helper.
|
|
||||||
- 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
|
|
||||||
|
|
||||||
- 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.
|
|
||||||
- Activity: 2026-07-21 - Created the project and began the compatibility investigation.
|
|
||||||
@@ -1,114 +0,0 @@
|
|||||||
- id: '1778927514490'
|
|
||||||
alias: NSPanel Easy Configuration (v2026.5.5)
|
|
||||||
description: ''
|
|
||||||
use_blueprint:
|
|
||||||
path: edwardtfn/nspanel_easy_blueprint.yaml
|
|
||||||
input:
|
|
||||||
nspanel_name: 369e58c3779ff3dd9a0b4c59fadeac4f
|
|
||||||
weather_entity: weather.astroweather_backyard
|
|
||||||
indoortemp: sensor.lounge_inside_temperature
|
|
||||||
- id: '1778927924212'
|
|
||||||
alias: NSPanel Easy Configuration (v2026.5.5)
|
|
||||||
description: ''
|
|
||||||
use_blueprint:
|
|
||||||
path: edwardtfn/nspanel_easy_blueprint.yaml
|
|
||||||
input:
|
|
||||||
nspanel_name: 369e58c3779ff3dd9a0b4c59fadeac4f
|
|
||||||
button_page01_label: Lounge Scenes
|
|
||||||
button_pages_icon_size: '8'
|
|
||||||
entity01: script.nspanel_lounge_scene_bright
|
|
||||||
entity01_name: Bright
|
|
||||||
entity01_icon: mdi:brightness-7
|
|
||||||
entity01_confirm: false
|
|
||||||
entity02: script.nspanel_lounge_scene_tv_general
|
|
||||||
entity02_name: TV General
|
|
||||||
entity02_icon: mdi:television
|
|
||||||
entity02_confirm: false
|
|
||||||
entity03: script.nspanel_lounge_scene_movie
|
|
||||||
entity03_name: Movie
|
|
||||||
entity03_icon: mdi:movie-open
|
|
||||||
entity03_confirm: false
|
|
||||||
entity04: script.nspanel_lounge_scene_balanced
|
|
||||||
entity04_name: Balanced
|
|
||||||
entity04_icon: mdi:scale-balance
|
|
||||||
entity04_confirm: false
|
|
||||||
entity05: script.nspanel_lounge_scene_warm_cozy
|
|
||||||
entity05_name: Warm Cozy
|
|
||||||
entity05_icon: mdi:fireplace
|
|
||||||
entity05_confirm: false
|
|
||||||
entity06: script.nspanel_lounge_scene_reading
|
|
||||||
entity06_name: Reading
|
|
||||||
entity06_icon: mdi:book-open-page-variant
|
|
||||||
entity06_confirm: false
|
|
||||||
entity07: script.nspanel_lounge_scene_dining
|
|
||||||
entity07_name: Dining
|
|
||||||
entity07_icon: mdi:silverware-fork-knife
|
|
||||||
entity07_confirm: false
|
|
||||||
entity08: script.nspanel_lounge_scene_all_off
|
|
||||||
entity08_name: All Off
|
|
||||||
entity08_icon: mdi:power
|
|
||||||
entity08_confirm: false
|
|
||||||
mode: single
|
|
||||||
- id: '1778929633433'
|
|
||||||
alias: NSPanel Easy Configuration (v2026.5.5)
|
|
||||||
description: ''
|
|
||||||
use_blueprint:
|
|
||||||
path: edwardtfn/nspanel_easy_blueprint.yaml
|
|
||||||
input:
|
|
||||||
nspanel_name: 369e58c3779ff3dd9a0b4c59fadeac4f
|
|
||||||
button_page01_label: Lounge Scenes
|
|
||||||
button_pages_icon_size: '8'
|
|
||||||
entity01: script.nspanel_lounge_scene_bright
|
|
||||||
entity01_name: Bright
|
|
||||||
entity01_icon: mdi:brightness-7
|
|
||||||
entity01_confirm: false
|
|
||||||
entity02: script.nspanel_lounge_scene_tv_general
|
|
||||||
entity02_name: TV General
|
|
||||||
entity02_icon: mdi:television
|
|
||||||
entity02_confirm: false
|
|
||||||
entity03: script.nspanel_lounge_scene_movie
|
|
||||||
entity03_name: Movie
|
|
||||||
entity03_icon: mdi:movie-open
|
|
||||||
entity03_confirm: false
|
|
||||||
entity04: script.nspanel_lounge_scene_balanced
|
|
||||||
entity04_name: Balanced
|
|
||||||
entity04_icon: mdi:scale-balance
|
|
||||||
entity04_confirm: false
|
|
||||||
entity05: script.nspanel_lounge_scene_warm_cozy
|
|
||||||
entity05_name: Warm Cozy
|
|
||||||
entity05_icon: mdi:fireplace
|
|
||||||
entity05_confirm: false
|
|
||||||
entity06: script.nspanel_lounge_scene_reading
|
|
||||||
entity06_name: Reading
|
|
||||||
entity06_icon: mdi:book-open-page-variant
|
|
||||||
entity06_confirm: false
|
|
||||||
entity07: script.nspanel_lounge_scene_dining
|
|
||||||
entity07_name: Dining
|
|
||||||
entity07_icon: mdi:silverware-fork-knife
|
|
||||||
entity07_confirm: false
|
|
||||||
entity08: script.nspanel_lounge_scene_all_off
|
|
||||||
entity08_name: All Off
|
|
||||||
entity08_icon: mdi:power
|
|
||||||
entity08_confirm: false
|
|
||||||
mode: single
|
|
||||||
- id: '1778930023427'
|
|
||||||
alias: NSPanel Lounge Controller
|
|
||||||
description: ''
|
|
||||||
use_blueprint:
|
|
||||||
path: edwardtfn/nspanel_easy_blueprint.yaml
|
|
||||||
input:
|
|
||||||
nspanel_name: 369e58c3779ff3dd9a0b4c59fadeac4f
|
|
||||||
- id: '1778930202677'
|
|
||||||
alias: NSPanel Lounge Controller
|
|
||||||
description: ''
|
|
||||||
use_blueprint:
|
|
||||||
path: edwardtfn/nspanel_easy_blueprint.yaml
|
|
||||||
input:
|
|
||||||
nspanel_name: 369e58c3779ff3dd9a0b4c59fadeac4f
|
|
||||||
- id: '1778930948144'
|
|
||||||
alias: Lounge NSPanel Controls
|
|
||||||
description: ''
|
|
||||||
use_blueprint:
|
|
||||||
path: edwardtfn/nspanel_easy_blueprint.yaml
|
|
||||||
input:
|
|
||||||
nspanel_name: 369e58c3779ff3dd9a0b4c59fadeac4f
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
- id: do_random_actions_when_away
|
|
||||||
alias: Activate Random Actions for Security
|
|
||||||
mode: parallel # Enable parallel executions
|
|
||||||
trigger:
|
|
||||||
- platform: time_pattern
|
|
||||||
minutes: /5
|
|
||||||
condition:
|
|
||||||
alias: "Away routine active, nearly sunset, and all family not home"
|
|
||||||
condition: and
|
|
||||||
conditions:
|
|
||||||
- condition: sun
|
|
||||||
after: sunset
|
|
||||||
after_offset: -00:30:00 #30 mins before
|
|
||||||
- condition: state
|
|
||||||
entity_id: input_boolean.away_occupied_routine
|
|
||||||
state: "on"
|
|
||||||
- condition: state
|
|
||||||
entity_id: group.family_away
|
|
||||||
state: "not_home"
|
|
||||||
action:
|
|
||||||
service: script.light_duration
|
|
||||||
data:
|
|
||||||
# Pass randomly selected light from "group.simulation_lights" as script variable "light"
|
|
||||||
light: "{{states.group.simulation_lights.attributes.entity_id | random}}"
|
|
||||||
# random value between 5 & 29
|
|
||||||
duration: "00:{{ '{:02}'.format(range(5,29) | random | int) }}:00"
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
- id: "1635929281159"
|
|
||||||
alias: Weekly Low battery level detection
|
|
||||||
description: ""
|
|
||||||
use_blueprint:
|
|
||||||
path: sbyx/low-battery-level-detection-notification-for-all-battery-sensors.yaml
|
|
||||||
input:
|
|
||||||
day: 1
|
|
||||||
time: "21:00:00"
|
|
||||||
actions:
|
|
||||||
- service: notify.pushover_zorruno
|
|
||||||
data:
|
|
||||||
message: >
|
|
||||||
Weekly Battery Check. Low Devices:
|
|
||||||
{{sensors}}
|
|
||||||
title: View Road Sensors
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
- id: notify_if_batteries_are_low
|
|
||||||
alias: Daily alert that zigbee batteries Are Low
|
|
||||||
initial_state: true
|
|
||||||
trigger:
|
|
||||||
- platform: time
|
|
||||||
at: "19:00:00"
|
|
||||||
condition:
|
|
||||||
- condition: or
|
|
||||||
conditions:
|
|
||||||
- condition: template
|
|
||||||
value_template: >
|
|
||||||
{% set min_battery_level = 20 -%}
|
|
||||||
{% set ns = namespace(found=false) -%}
|
|
||||||
{% for entity_id in states.group.battery_levels.attributes.entity_id -%}
|
|
||||||
{% set parts = entity_id.split('.') -%}
|
|
||||||
{% if (states(entity_id) | replace("%","") | int) < min_battery_level -%}
|
|
||||||
{% set ns.found = true -%}
|
|
||||||
{% endif -%}
|
|
||||||
{% endfor -%}
|
|
||||||
{{ ns.found }}
|
|
||||||
action:
|
|
||||||
- service: notify.pushover_zorruno
|
|
||||||
data:
|
|
||||||
title: View Road Sensors
|
|
||||||
message: >
|
|
||||||
{%- set min_battery_level = 20 -%}
|
|
||||||
{%- for entity_id in states.group.battery_levels.attributes.entity_id -%}
|
|
||||||
{%- set parts = entity_id.split('.') -%}
|
|
||||||
{%- if (states(entity_id) | replace("%","") | int) < min_battery_level -%}
|
|
||||||
{{ states[parts[0]][parts[1]].name }} level is {{ states(entity_id) }}%.{{ '\n' }}
|
|
||||||
{%- endif -%}
|
|
||||||
{%- endfor -%}
|
|
||||||
-1386
File diff suppressed because it is too large
Load Diff
@@ -39,6 +39,3 @@ zha:
|
|||||||
salus_provider: true
|
salus_provider: true
|
||||||
inovelli_provider: true
|
inovelli_provider: true
|
||||||
thirdreality_provider: true
|
thirdreality_provider: true
|
||||||
|
|
||||||
device_tracker:
|
|
||||||
- platform: bluetooth_le_tracker
|
|
||||||
|
|||||||
@@ -1,137 +0,0 @@
|
|||||||
"""Quirk for TS0207 rain sensors."""
|
|
||||||
|
|
||||||
import zigpy.types as t
|
|
||||||
from typing import Any, Type
|
|
||||||
from zigpy.profiles import zha
|
|
||||||
from zigpy.quirks import CustomDevice, CustomCluster
|
|
||||||
from zigpy.zcl.clusters.general import (
|
|
||||||
Basic,
|
|
||||||
Groups,
|
|
||||||
Identify,
|
|
||||||
OnOff,
|
|
||||||
Ota,
|
|
||||||
PowerConfiguration,
|
|
||||||
Scenes,
|
|
||||||
Time,
|
|
||||||
)
|
|
||||||
from zigpy.zcl.clusters.lightlink import LightLink
|
|
||||||
from zigpy.zcl.clusters.security import IasZone
|
|
||||||
from zhaquirks.const import (
|
|
||||||
DEVICE_TYPE,
|
|
||||||
ENDPOINTS,
|
|
||||||
INPUT_CLUSTERS,
|
|
||||||
MODELS_INFO,
|
|
||||||
OUTPUT_CLUSTERS,
|
|
||||||
PROFILE_ID,
|
|
||||||
)
|
|
||||||
from zhaquirks.tuya.mcu import TuyaMCUCluster
|
|
||||||
from zhaquirks.tuya import (
|
|
||||||
TuyaManufCluster,
|
|
||||||
DPToAttributeMapping,
|
|
||||||
EnchantedDevice,
|
|
||||||
TuyaNoBindPowerConfigurationCluster,
|
|
||||||
)
|
|
||||||
|
|
||||||
ZONE_TYPE = 0x0001
|
|
||||||
|
|
||||||
class TuyaSolarRainSensorCluster(TuyaMCUCluster):
|
|
||||||
"""Tuya manufacturer cluster."""
|
|
||||||
|
|
||||||
attributes = TuyaMCUCluster.attributes.copy()
|
|
||||||
attributes.update(
|
|
||||||
{
|
|
||||||
0xEF65: ("light_intensity", t.uint32_t, True),
|
|
||||||
0xEF66: ("average_light_intensity_20mins", t.uint32_t, True),
|
|
||||||
0xEF67: ("todays_max_light_intensity", t.uint32_t, True),
|
|
||||||
0xEF68: ("cleaning_reminder", t.Bool, True),
|
|
||||||
0xEF69: ("rain_sensor_voltage", t.uint32_t, True),
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
dp_to_attribute: dict[int, DPToAttributeMapping] = {
|
|
||||||
101: DPToAttributeMapping(
|
|
||||||
TuyaMCUCluster.ep_attribute,
|
|
||||||
"light_intensity",
|
|
||||||
),
|
|
||||||
102: DPToAttributeMapping(
|
|
||||||
TuyaMCUCluster.ep_attribute,
|
|
||||||
"average_light_intensity_20mins",
|
|
||||||
),
|
|
||||||
103: DPToAttributeMapping(
|
|
||||||
TuyaMCUCluster.ep_attribute,
|
|
||||||
"todays_max_light_intensity",
|
|
||||||
),
|
|
||||||
104: DPToAttributeMapping(
|
|
||||||
TuyaMCUCluster.ep_attribute,
|
|
||||||
"cleaning_reminder",
|
|
||||||
),
|
|
||||||
105: DPToAttributeMapping(
|
|
||||||
TuyaMCUCluster.ep_attribute,
|
|
||||||
"rain_sensor_voltage",
|
|
||||||
),
|
|
||||||
}
|
|
||||||
|
|
||||||
data_point_handlers = {
|
|
||||||
101: "_dp_2_attr_update",
|
|
||||||
102: "_dp_2_attr_update",
|
|
||||||
103: "_dp_2_attr_update",
|
|
||||||
104: "_dp_2_attr_update",
|
|
||||||
105: "_dp_2_attr_update",
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class TuyaIasZone(CustomCluster, IasZone):
|
|
||||||
"""IAS Zone for rain sensors."""
|
|
||||||
|
|
||||||
_CONSTANT_ATTRIBUTES = {ZONE_TYPE: IasZone.ZoneType.Water_Sensor}
|
|
||||||
|
|
||||||
|
|
||||||
class TuyaSolarRainSensor(EnchantedDevice):
|
|
||||||
"""TS0207 Rain sensor quirk."""
|
|
||||||
|
|
||||||
signature = {
|
|
||||||
MODELS_INFO: [("_TZ3210_tgvtvdoc", "TS0207")],
|
|
||||||
ENDPOINTS: {
|
|
||||||
1: {
|
|
||||||
PROFILE_ID: zha.PROFILE_ID,
|
|
||||||
DEVICE_TYPE: zha.DeviceType.IAS_ZONE,
|
|
||||||
INPUT_CLUSTERS: [
|
|
||||||
Basic.cluster_id,
|
|
||||||
PowerConfiguration.cluster_id,
|
|
||||||
Groups.cluster_id,
|
|
||||||
Scenes.cluster_id,
|
|
||||||
IasZone.cluster_id,
|
|
||||||
TuyaMCUCluster.cluster_id,
|
|
||||||
],
|
|
||||||
OUTPUT_CLUSTERS: [
|
|
||||||
Identify.cluster_id,
|
|
||||||
Groups.cluster_id,
|
|
||||||
OnOff.cluster_id,
|
|
||||||
Time.cluster_id,
|
|
||||||
Ota.cluster_id,
|
|
||||||
LightLink.cluster_id,
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
replacement = {
|
|
||||||
ENDPOINTS: {
|
|
||||||
1: {
|
|
||||||
PROFILE_ID: zha.PROFILE_ID,
|
|
||||||
DEVICE_TYPE: zha.DeviceType.IAS_ZONE,
|
|
||||||
INPUT_CLUSTERS: [
|
|
||||||
Basic.cluster_id,
|
|
||||||
Groups.cluster_id,
|
|
||||||
Scenes.cluster_id,
|
|
||||||
TuyaNoBindPowerConfigurationCluster,
|
|
||||||
TuyaIasZone,
|
|
||||||
TuyaSolarRainSensorCluster,
|
|
||||||
],
|
|
||||||
OUTPUT_CLUSTERS: [
|
|
||||||
Time.cluster_id,
|
|
||||||
Ota.cluster_id,
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
@@ -1,358 +0,0 @@
|
|||||||
# rivsc https://blog.rivsc.ovh
|
|
||||||
import logging
|
|
||||||
from typing import Final
|
|
||||||
|
|
||||||
from zigpy.quirks.v2 import QuirkBuilder, BinarySensorDeviceClass
|
|
||||||
|
|
||||||
import zigpy.types as t
|
|
||||||
from zigpy.zcl.foundation import ZCLAttributeDef
|
|
||||||
from zigpy.zcl.clusters.measurement import (
|
|
||||||
IlluminanceMeasurement,
|
|
||||||
OccupancySensing,
|
|
||||||
)
|
|
||||||
from zigpy.zcl.clusters.security import IasZone
|
|
||||||
from zigpy.quirks.v2.homeassistant import EntityPlatform, EntityType
|
|
||||||
|
|
||||||
from zhaquirks.tuya import (
|
|
||||||
TuyaLocalCluster,
|
|
||||||
TuyaPowerConfigurationCluster2AAA,
|
|
||||||
)
|
|
||||||
from zhaquirks.tuya.mcu import TuyaMCUCluster, DPToAttributeMapping
|
|
||||||
|
|
||||||
class PresenceState(t.enum8):
|
|
||||||
"""Presence State values"""
|
|
||||||
none = 0x00
|
|
||||||
presence = 0x01
|
|
||||||
peaceful = 0x02
|
|
||||||
small_movement = 0x03
|
|
||||||
large_movement = 0x04
|
|
||||||
|
|
||||||
class TuyaOccupancySensing(OccupancySensing, TuyaLocalCluster):
|
|
||||||
"""Tuya local OccupancySensing cluster."""
|
|
||||||
|
|
||||||
class TuyaIlluminanceMeasurement(IlluminanceMeasurement, TuyaLocalCluster):
|
|
||||||
"""Tuya local IlluminanceMeasurement cluster."""
|
|
||||||
|
|
||||||
class HumanPresenceSensorManufCluster(TuyaMCUCluster):
|
|
||||||
"""Human Presence Sensor ZG-205Z (5.8GHz)"""
|
|
||||||
|
|
||||||
class AttributeDefs(TuyaMCUCluster.AttributeDefs):
|
|
||||||
"""Tuya DataPoints attributes"""
|
|
||||||
|
|
||||||
# Presence state
|
|
||||||
presence_state: Final = ZCLAttributeDef(
|
|
||||||
id=0x0001, # DP 1
|
|
||||||
type=t.uint16_t,
|
|
||||||
access="rp",
|
|
||||||
is_manufacturer_specific=True,
|
|
||||||
)
|
|
||||||
# Target distance
|
|
||||||
target_distance: Final = ZCLAttributeDef(
|
|
||||||
id=0x0101, # DP 101
|
|
||||||
type=t.uint16_t,
|
|
||||||
access="rp",
|
|
||||||
is_manufacturer_specific=True,
|
|
||||||
)
|
|
||||||
# Illuminance value
|
|
||||||
illuminance_lux: Final = ZCLAttributeDef(
|
|
||||||
id=0x0102, # DP 102
|
|
||||||
type=t.uint16_t,
|
|
||||||
access="rp",
|
|
||||||
is_manufacturer_specific=True,
|
|
||||||
)
|
|
||||||
# None delay time (presence keep time)
|
|
||||||
none_delay_time: Final = ZCLAttributeDef(
|
|
||||||
id=0x0103, # DP 103
|
|
||||||
type=t.uint16_t,
|
|
||||||
is_manufacturer_specific=True,
|
|
||||||
)
|
|
||||||
# Indicator
|
|
||||||
indicator: Final = ZCLAttributeDef(
|
|
||||||
id=0x0104, # DP 104
|
|
||||||
type=t.Bool,
|
|
||||||
is_manufacturer_specific=True,
|
|
||||||
)
|
|
||||||
# Move detection max distance
|
|
||||||
move_detection_max: Final = ZCLAttributeDef(
|
|
||||||
id=0x0107, # DP 107
|
|
||||||
type=t.uint16_t,
|
|
||||||
is_manufacturer_specific=True,
|
|
||||||
)
|
|
||||||
# Move detection min distance
|
|
||||||
move_detection_min: Final = ZCLAttributeDef(
|
|
||||||
id=0x0108, # DP 108
|
|
||||||
type=t.uint16_t,
|
|
||||||
is_manufacturer_specific=True,
|
|
||||||
)
|
|
||||||
# Breath detection max distance
|
|
||||||
breath_detection_max: Final = ZCLAttributeDef(
|
|
||||||
id=0x0109, # DP 109
|
|
||||||
type=t.uint16_t,
|
|
||||||
is_manufacturer_specific=True,
|
|
||||||
)
|
|
||||||
# Breath detection min distance
|
|
||||||
breath_detection_min: Final = ZCLAttributeDef(
|
|
||||||
id=0x0110, # DP 110
|
|
||||||
type=t.uint16_t,
|
|
||||||
is_manufacturer_specific=True,
|
|
||||||
)
|
|
||||||
# Small move detection max distance
|
|
||||||
small_move_detection_max: Final = ZCLAttributeDef(
|
|
||||||
id=0x0114, # DP 114
|
|
||||||
type=t.uint16_t,
|
|
||||||
is_manufacturer_specific=True,
|
|
||||||
)
|
|
||||||
# Small move detection min distance
|
|
||||||
small_move_detection_min: Final = ZCLAttributeDef(
|
|
||||||
id=0x0115, # DP 115
|
|
||||||
type=t.uint16_t,
|
|
||||||
is_manufacturer_specific=True,
|
|
||||||
)
|
|
||||||
# Move sensitivity
|
|
||||||
move_sensitivity: Final = ZCLAttributeDef(
|
|
||||||
id=0x0116, # DP 116
|
|
||||||
type=t.uint8_t,
|
|
||||||
is_manufacturer_specific=True,
|
|
||||||
)
|
|
||||||
# Small move sensitivity
|
|
||||||
small_move_sensitivity: Final = ZCLAttributeDef(
|
|
||||||
id=0x0117, # DP 117
|
|
||||||
type=t.uint8_t,
|
|
||||||
is_manufacturer_specific=True,
|
|
||||||
)
|
|
||||||
# Breath sensitivity
|
|
||||||
breath_sensitivity: Final = ZCLAttributeDef(
|
|
||||||
id=0x0118, # DP 118
|
|
||||||
type=t.uint8_t,
|
|
||||||
is_manufacturer_specific=True,
|
|
||||||
)
|
|
||||||
|
|
||||||
dp_to_attribute: dict[int, DPToAttributeMapping] = {
|
|
||||||
1: DPToAttributeMapping(
|
|
||||||
TuyaMCUCluster.ep_attribute,
|
|
||||||
"presence_state",
|
|
||||||
converter=PresenceState
|
|
||||||
),
|
|
||||||
101: DPToAttributeMapping(
|
|
||||||
TuyaMCUCluster.ep_attribute,
|
|
||||||
"target_distance",
|
|
||||||
converter=lambda x: x / 100 if x is not None else 0
|
|
||||||
),
|
|
||||||
102: DPToAttributeMapping(
|
|
||||||
TuyaMCUCluster.ep_attribute,
|
|
||||||
"illuminance_lux",
|
|
||||||
),
|
|
||||||
103: DPToAttributeMapping(
|
|
||||||
TuyaMCUCluster.ep_attribute,
|
|
||||||
"none_delay_time",
|
|
||||||
converter=lambda x: x if x is not None else 30
|
|
||||||
),
|
|
||||||
104: DPToAttributeMapping(
|
|
||||||
TuyaMCUCluster.ep_attribute,
|
|
||||||
"indicator",
|
|
||||||
),
|
|
||||||
107: DPToAttributeMapping(
|
|
||||||
TuyaMCUCluster.ep_attribute,
|
|
||||||
"move_detection_max",
|
|
||||||
converter=lambda x: x / 100 if x is not None else 10
|
|
||||||
),
|
|
||||||
108: DPToAttributeMapping(
|
|
||||||
TuyaMCUCluster.ep_attribute,
|
|
||||||
"move_detection_min",
|
|
||||||
converter=lambda x: x / 100 if x is not None else 0
|
|
||||||
),
|
|
||||||
109: DPToAttributeMapping(
|
|
||||||
TuyaMCUCluster.ep_attribute,
|
|
||||||
"breath_detection_max",
|
|
||||||
converter=lambda x: x / 100 if x is not None else 6
|
|
||||||
),
|
|
||||||
110: DPToAttributeMapping(
|
|
||||||
TuyaMCUCluster.ep_attribute,
|
|
||||||
"breath_detection_min",
|
|
||||||
converter=lambda x: x / 100 if x is not None else 0
|
|
||||||
),
|
|
||||||
114: DPToAttributeMapping(
|
|
||||||
TuyaMCUCluster.ep_attribute,
|
|
||||||
"small_move_detection_max",
|
|
||||||
converter=lambda x: x / 100 if x is not None else 6
|
|
||||||
),
|
|
||||||
115: DPToAttributeMapping(
|
|
||||||
TuyaMCUCluster.ep_attribute,
|
|
||||||
"small_move_detection_min",
|
|
||||||
converter=lambda x: x / 100 if x is not None else 0
|
|
||||||
),
|
|
||||||
116: DPToAttributeMapping(
|
|
||||||
TuyaMCUCluster.ep_attribute,
|
|
||||||
"move_sensitivity",
|
|
||||||
converter=lambda x: x if x is not None else 5
|
|
||||||
),
|
|
||||||
117: DPToAttributeMapping(
|
|
||||||
TuyaMCUCluster.ep_attribute,
|
|
||||||
"small_move_sensitivity",
|
|
||||||
converter=lambda x: x if x is not None else 5
|
|
||||||
),
|
|
||||||
118: DPToAttributeMapping(
|
|
||||||
TuyaMCUCluster.ep_attribute,
|
|
||||||
"breath_sensitivity",
|
|
||||||
converter=lambda x: x if x is not None else 5
|
|
||||||
),
|
|
||||||
}
|
|
||||||
|
|
||||||
data_point_handlers = {
|
|
||||||
1: "_dp_2_attr_update",
|
|
||||||
101: "_dp_2_attr_update",
|
|
||||||
102: "_dp_2_attr_update",
|
|
||||||
103: "_dp_2_attr_update",
|
|
||||||
104: "_dp_2_attr_update",
|
|
||||||
107: "_dp_2_attr_update",
|
|
||||||
108: "_dp_2_attr_update",
|
|
||||||
109: "_dp_2_attr_update",
|
|
||||||
110: "_dp_2_attr_update",
|
|
||||||
114: "_dp_2_attr_update",
|
|
||||||
115: "_dp_2_attr_update",
|
|
||||||
116: "_dp_2_attr_update",
|
|
||||||
117: "_dp_2_attr_update",
|
|
||||||
118: "_dp_2_attr_update",
|
|
||||||
}
|
|
||||||
|
|
||||||
(
|
|
||||||
QuirkBuilder("_TZE204_dapwryy7", "TS0601")
|
|
||||||
.skip_configuration()
|
|
||||||
.removes(IasZone.cluster_id)
|
|
||||||
.adds(HumanPresenceSensorManufCluster)
|
|
||||||
#.adds(TuyaOccupancySensing)
|
|
||||||
.replaces(TuyaPowerConfigurationCluster2AAA)
|
|
||||||
.replaces(TuyaIlluminanceMeasurement)
|
|
||||||
.binary_sensor(
|
|
||||||
"presence_state",
|
|
||||||
HumanPresenceSensorManufCluster.cluster_id,
|
|
||||||
endpoint_id=1,
|
|
||||||
#entity_type=EntityType.STANDARD, # very soon (zigpy channel #dev on github
|
|
||||||
device_class=BinarySensorDeviceClass.OCCUPANCY,
|
|
||||||
fallback_name="Presence"
|
|
||||||
)
|
|
||||||
.enum(
|
|
||||||
HumanPresenceSensorManufCluster.AttributeDefs.presence_state.name,
|
|
||||||
PresenceState,
|
|
||||||
HumanPresenceSensorManufCluster.cluster_id,
|
|
||||||
entity_platform=EntityPlatform.SENSOR,
|
|
||||||
entity_type=EntityType.STANDARD,
|
|
||||||
fallback_name="Presence State",
|
|
||||||
translation_key="presence_state"
|
|
||||||
)
|
|
||||||
.number(
|
|
||||||
HumanPresenceSensorManufCluster.AttributeDefs.target_distance.name,
|
|
||||||
HumanPresenceSensorManufCluster.cluster_id,
|
|
||||||
step=0.01,
|
|
||||||
min_value=0,
|
|
||||||
max_value=10,
|
|
||||||
#unit="m", # fail :/
|
|
||||||
fallback_name="Target Distance (m)",
|
|
||||||
translation_key="target_distance",
|
|
||||||
#entity_type=EntityType.STANDARD # not yet :/
|
|
||||||
)
|
|
||||||
.number(
|
|
||||||
HumanPresenceSensorManufCluster.AttributeDefs.none_delay_time.name,
|
|
||||||
HumanPresenceSensorManufCluster.cluster_id,
|
|
||||||
step=1,
|
|
||||||
min_value=0,
|
|
||||||
max_value=28800,
|
|
||||||
unit="s",
|
|
||||||
fallback_name="Hold Delay Time",
|
|
||||||
translation_key="none_delay_time"
|
|
||||||
)
|
|
||||||
.switch(
|
|
||||||
HumanPresenceSensorManufCluster.AttributeDefs.indicator.name,
|
|
||||||
HumanPresenceSensorManufCluster.cluster_id,
|
|
||||||
fallback_name="LED Indicator",
|
|
||||||
translation_key="indicator"
|
|
||||||
)
|
|
||||||
.number(
|
|
||||||
HumanPresenceSensorManufCluster.AttributeDefs.move_detection_max.name,
|
|
||||||
HumanPresenceSensorManufCluster.cluster_id,
|
|
||||||
step=0.01,
|
|
||||||
min_value=0,
|
|
||||||
max_value=10,
|
|
||||||
#unit="m",
|
|
||||||
fallback_name="Move Detection Max Distance (m)",
|
|
||||||
translation_key="move_detection_max"
|
|
||||||
)
|
|
||||||
.number(
|
|
||||||
HumanPresenceSensorManufCluster.AttributeDefs.move_detection_min.name,
|
|
||||||
HumanPresenceSensorManufCluster.cluster_id,
|
|
||||||
step=0.01,
|
|
||||||
min_value=0,
|
|
||||||
max_value=10,
|
|
||||||
#unit="m",
|
|
||||||
fallback_name="Move Detection Min Distance (m)",
|
|
||||||
translation_key="move_detection_min"
|
|
||||||
)
|
|
||||||
.number(
|
|
||||||
HumanPresenceSensorManufCluster.AttributeDefs.small_move_detection_max.name,
|
|
||||||
HumanPresenceSensorManufCluster.cluster_id,
|
|
||||||
step=0.01,
|
|
||||||
min_value=0,
|
|
||||||
max_value=6,
|
|
||||||
#unit="m",
|
|
||||||
fallback_name="Small Move Detection Max Distance (m)",
|
|
||||||
translation_key="small_move_detection_max"
|
|
||||||
)
|
|
||||||
.number(
|
|
||||||
HumanPresenceSensorManufCluster.AttributeDefs.small_move_detection_min.name,
|
|
||||||
HumanPresenceSensorManufCluster.cluster_id,
|
|
||||||
step=0.01,
|
|
||||||
min_value=0,
|
|
||||||
max_value=6,
|
|
||||||
#unit="m",
|
|
||||||
fallback_name="Small Move Detection Min Distance (m)",
|
|
||||||
translation_key="small_move_detection_min"
|
|
||||||
)
|
|
||||||
.number(
|
|
||||||
HumanPresenceSensorManufCluster.AttributeDefs.breath_detection_max.name,
|
|
||||||
HumanPresenceSensorManufCluster.cluster_id,
|
|
||||||
step=0.01,
|
|
||||||
min_value=0,
|
|
||||||
max_value=6,
|
|
||||||
#unit="m",
|
|
||||||
fallback_name="Breath Detection Max Distance (m)",
|
|
||||||
translation_key="breath_detection_max"
|
|
||||||
)
|
|
||||||
.number(
|
|
||||||
HumanPresenceSensorManufCluster.AttributeDefs.breath_detection_min.name,
|
|
||||||
HumanPresenceSensorManufCluster.cluster_id,
|
|
||||||
step=0.01,
|
|
||||||
min_value=0,
|
|
||||||
max_value=6,
|
|
||||||
#unit="m",
|
|
||||||
fallback_name="Breath Detection Min Distance (m)",
|
|
||||||
translation_key="breath_detection_min"
|
|
||||||
)
|
|
||||||
.number(
|
|
||||||
HumanPresenceSensorManufCluster.AttributeDefs.move_sensitivity.name,
|
|
||||||
HumanPresenceSensorManufCluster.cluster_id,
|
|
||||||
step=1,
|
|
||||||
min_value=0,
|
|
||||||
max_value=10,
|
|
||||||
fallback_name="Move Sensitivity",
|
|
||||||
translation_key="move_sensitivity"
|
|
||||||
)
|
|
||||||
.number(
|
|
||||||
HumanPresenceSensorManufCluster.AttributeDefs.small_move_sensitivity.name,
|
|
||||||
HumanPresenceSensorManufCluster.cluster_id,
|
|
||||||
step=1,
|
|
||||||
min_value=0,
|
|
||||||
max_value=10,
|
|
||||||
fallback_name="Small Move Sensitivity",
|
|
||||||
translation_key="small_move_sensitivity"
|
|
||||||
)
|
|
||||||
.number(
|
|
||||||
HumanPresenceSensorManufCluster.AttributeDefs.breath_sensitivity.name,
|
|
||||||
HumanPresenceSensorManufCluster.cluster_id,
|
|
||||||
step=1,
|
|
||||||
min_value=0,
|
|
||||||
max_value=10,
|
|
||||||
fallback_name="Breath Sensitivity",
|
|
||||||
translation_key="breath_sensitivity"
|
|
||||||
)
|
|
||||||
.add_to_registry()
|
|
||||||
)
|
|
||||||
@@ -1,293 +0,0 @@
|
|||||||
"""Tuya Din Power Meter."""
|
|
||||||
|
|
||||||
from zigpy.profiles import zha
|
|
||||||
import zigpy.types as t
|
|
||||||
from zigpy.zcl.clusters.general import Basic, Groups, Ota, Scenes, Time
|
|
||||||
from zigpy.zcl.clusters.homeautomation import ElectricalMeasurement
|
|
||||||
from zigpy.zcl.clusters.smartenergy import Metering
|
|
||||||
|
|
||||||
from zhaquirks import Bus, LocalDataCluster
|
|
||||||
from zhaquirks.const import (
|
|
||||||
DEVICE_TYPE,
|
|
||||||
ENDPOINTS,
|
|
||||||
INPUT_CLUSTERS,
|
|
||||||
MODELS_INFO,
|
|
||||||
OUTPUT_CLUSTERS,
|
|
||||||
PROFILE_ID,
|
|
||||||
)
|
|
||||||
from zhaquirks.tuya import TuyaManufClusterAttributes, TuyaOnOff, TuyaSwitch
|
|
||||||
|
|
||||||
TUYA_TOTAL_ENERGY_ATTR = 0x0211
|
|
||||||
TUYA_CURRENT_ATTR = 0x0212
|
|
||||||
TUYA_POWER_ATTR = 0x0213
|
|
||||||
TUYA_VOLTAGE_ATTR = 0x0214
|
|
||||||
TUYA_DIN_SWITCH_ATTR = 0x0101
|
|
||||||
|
|
||||||
SWITCH_EVENT = "switch_event"
|
|
||||||
|
|
||||||
"""Hiking Power Meter Attributes"""
|
|
||||||
HIKING_DIN_SWITCH_ATTR = 0x0110
|
|
||||||
HIKING_TOTAL_ENERGY_DELIVERED_ATTR = 0x0201
|
|
||||||
HIKING_TOTAL_ENERGY_RECEIVED_ATTR = 0x0266
|
|
||||||
HIKING_VOLTAGE_CURRENT_ATTR = 0x0006
|
|
||||||
HIKING_POWER_ATTR = 0x0267
|
|
||||||
HIKING_FREQUENCY_ATTR = 0x0269
|
|
||||||
HIKING_POWER_FACTOR_ATTR = 0x026F
|
|
||||||
HIKING_TOTAL_REACTIVE_ATTR = 0x026D
|
|
||||||
HIKING_REACTIVE_POWER_ATTR = 0x026E
|
|
||||||
|
|
||||||
|
|
||||||
class TuyaManufClusterDinPower(TuyaManufClusterAttributes):
|
|
||||||
"""Manufacturer Specific Cluster of the Tuya Power Meter device."""
|
|
||||||
|
|
||||||
attributes = {
|
|
||||||
TUYA_TOTAL_ENERGY_ATTR: ("energy", t.uint32_t, True),
|
|
||||||
TUYA_CURRENT_ATTR: ("current", t.int16s, True),
|
|
||||||
TUYA_POWER_ATTR: ("power", t.uint16_t, True),
|
|
||||||
TUYA_VOLTAGE_ATTR: ("voltage", t.uint16_t, True),
|
|
||||||
TUYA_DIN_SWITCH_ATTR: ("switch", t.uint8_t, True),
|
|
||||||
}
|
|
||||||
|
|
||||||
def _update_attribute(self, attrid, value):
|
|
||||||
super()._update_attribute(attrid, value)
|
|
||||||
if attrid == TUYA_TOTAL_ENERGY_ATTR:
|
|
||||||
self.endpoint.smartenergy_metering.energy_deliver_reported(value / 100)
|
|
||||||
elif attrid == TUYA_CURRENT_ATTR:
|
|
||||||
self.endpoint.electrical_measurement.current_reported(value)
|
|
||||||
elif attrid == TUYA_POWER_ATTR:
|
|
||||||
self.endpoint.electrical_measurement.power_reported(value / 10)
|
|
||||||
elif attrid == TUYA_VOLTAGE_ATTR:
|
|
||||||
self.endpoint.electrical_measurement.voltage_reported(value / 10)
|
|
||||||
elif attrid == TUYA_DIN_SWITCH_ATTR:
|
|
||||||
self.endpoint.device.switch_bus.listener_event(
|
|
||||||
SWITCH_EVENT, self.endpoint.endpoint_id, value
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class TuyaPowerMeasurement(LocalDataCluster, ElectricalMeasurement):
|
|
||||||
"""Custom class for power, voltage and current measurement."""
|
|
||||||
|
|
||||||
POWER_ID = 0x050B
|
|
||||||
VOLTAGE_ID = 0x0505
|
|
||||||
CURRENT_ID = 0x0508
|
|
||||||
REACTIVE_POWER_ID = 0x050E
|
|
||||||
AC_FREQUENCY_ID = 0x0300
|
|
||||||
TOTAL_REACTIVE_POWER_ID = 0x0305
|
|
||||||
POWER_FACTOR_ID = 0x0510
|
|
||||||
|
|
||||||
AC_CURRENT_MULTIPLIER = 0x0602
|
|
||||||
AC_CURRENT_DIVISOR = 0x0603
|
|
||||||
AC_FREQUENCY_MULTIPLIER = 0x0400
|
|
||||||
AC_FREQUENCY_DIVISOR = 0x0401
|
|
||||||
|
|
||||||
_CONSTANT_ATTRIBUTES = {
|
|
||||||
AC_CURRENT_MULTIPLIER: 1,
|
|
||||||
AC_CURRENT_DIVISOR: 1000,
|
|
||||||
AC_FREQUENCY_MULTIPLIER: 1,
|
|
||||||
AC_FREQUENCY_DIVISOR: 100,
|
|
||||||
}
|
|
||||||
|
|
||||||
def voltage_reported(self, value):
|
|
||||||
"""Voltage reported."""
|
|
||||||
self._update_attribute(self.VOLTAGE_ID, value)
|
|
||||||
|
|
||||||
def power_reported(self, value):
|
|
||||||
"""Power reported."""
|
|
||||||
self._update_attribute(self.POWER_ID, value)
|
|
||||||
|
|
||||||
def power_factor_reported(self, value):
|
|
||||||
"""Power Factor reported."""
|
|
||||||
self._update_attribute(self.POWER_FACTOR_ID, value)
|
|
||||||
|
|
||||||
def reactive_power_reported(self, value):
|
|
||||||
"""Reactive Power reported."""
|
|
||||||
self._update_attribute(self.REACTIVE_POWER_ID, value)
|
|
||||||
|
|
||||||
def current_reported(self, value):
|
|
||||||
"""Ampers reported."""
|
|
||||||
self._update_attribute(self.CURRENT_ID, value)
|
|
||||||
|
|
||||||
def frequency_reported(self, value):
|
|
||||||
"""AC Frequency reported."""
|
|
||||||
self._update_attribute(self.AC_FREQUENCY_ID, value)
|
|
||||||
|
|
||||||
def reactive_energy_reported(self, value):
|
|
||||||
"""Summation Reactive Energy reported."""
|
|
||||||
self._update_attribute(self.TOTAL_REACTIVE_POWER_ID, value)
|
|
||||||
|
|
||||||
|
|
||||||
class TuyaElectricalMeasurement(LocalDataCluster, Metering):
|
|
||||||
"""Custom class for total energy measurement."""
|
|
||||||
|
|
||||||
CURRENT_DELIVERED_ID = 0x0000
|
|
||||||
CURRENT_RECEIVED_ID = 0x0001
|
|
||||||
POWER_WATT = 0x0000
|
|
||||||
|
|
||||||
"""Setting unit of measurement."""
|
|
||||||
_CONSTANT_ATTRIBUTES = {0x0300: POWER_WATT}
|
|
||||||
|
|
||||||
def energy_deliver_reported(self, value):
|
|
||||||
"""Summation Energy Deliver reported."""
|
|
||||||
self._update_attribute(self.CURRENT_DELIVERED_ID, value)
|
|
||||||
|
|
||||||
def energy_receive_reported(self, value):
|
|
||||||
"""Summation Energy Receive reported."""
|
|
||||||
self._update_attribute(self.CURRENT_RECEIVED_ID, value)
|
|
||||||
|
|
||||||
|
|
||||||
class HikingManufClusterDinPower(TuyaManufClusterAttributes):
|
|
||||||
"""Manufacturer Specific Cluster of the Hiking Power Meter device."""
|
|
||||||
|
|
||||||
attributes = {
|
|
||||||
HIKING_DIN_SWITCH_ATTR: ("switch", t.uint8_t, True),
|
|
||||||
HIKING_TOTAL_ENERGY_DELIVERED_ATTR: ("energy_delivered", t.uint32_t, True),
|
|
||||||
HIKING_TOTAL_ENERGY_RECEIVED_ATTR: ("energy_received", t.uint16_t, True),
|
|
||||||
HIKING_VOLTAGE_CURRENT_ATTR: ("voltage_current", t.uint32_t, True),
|
|
||||||
HIKING_POWER_ATTR: ("power", t.uint16_t, True),
|
|
||||||
HIKING_FREQUENCY_ATTR: ("frequency", t.uint16_t, True),
|
|
||||||
HIKING_TOTAL_REACTIVE_ATTR: ("total_reactive_energy", t.int32s, True),
|
|
||||||
HIKING_REACTIVE_POWER_ATTR: ("reactive_power", t.int16s, True),
|
|
||||||
HIKING_POWER_FACTOR_ATTR: ("power_factor", t.uint16_t, True),
|
|
||||||
}
|
|
||||||
|
|
||||||
def _update_attribute(self, attrid, value):
|
|
||||||
super()._update_attribute(attrid, value)
|
|
||||||
if attrid == HIKING_DIN_SWITCH_ATTR:
|
|
||||||
self.endpoint.device.switch_bus.listener_event(SWITCH_EVENT, 16, value)
|
|
||||||
elif attrid == HIKING_TOTAL_ENERGY_DELIVERED_ATTR:
|
|
||||||
self.endpoint.smartenergy_metering.energy_deliver_reported(value / 100)
|
|
||||||
elif attrid == HIKING_TOTAL_ENERGY_RECEIVED_ATTR:
|
|
||||||
self.endpoint.smartenergy_metering.energy_receive_reported(value / 100)
|
|
||||||
elif attrid == HIKING_VOLTAGE_CURRENT_ATTR:
|
|
||||||
self.endpoint.electrical_measurement.current_reported(value >> 16)
|
|
||||||
self.endpoint.electrical_measurement.voltage_reported(
|
|
||||||
(value & 0x0000FFFF) / 10
|
|
||||||
)
|
|
||||||
elif attrid == HIKING_POWER_ATTR:
|
|
||||||
self.endpoint.electrical_measurement.power_reported(value)
|
|
||||||
elif attrid == HIKING_FREQUENCY_ATTR:
|
|
||||||
self.endpoint.electrical_measurement.frequency_reported(value)
|
|
||||||
elif attrid == HIKING_TOTAL_REACTIVE_ATTR:
|
|
||||||
self.endpoint.electrical_measurement.reactive_energy_reported(value)
|
|
||||||
elif attrid == HIKING_REACTIVE_POWER_ATTR:
|
|
||||||
self.endpoint.electrical_measurement.reactive_power_reported(value)
|
|
||||||
elif attrid == HIKING_POWER_FACTOR_ATTR:
|
|
||||||
self.endpoint.electrical_measurement.power_factor_reported(value / 10)
|
|
||||||
|
|
||||||
|
|
||||||
class TuyaPowerMeter(TuyaSwitch):
|
|
||||||
"""Tuya power meter device."""
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
"""Init device."""
|
|
||||||
self.switch_bus = Bus()
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
|
|
||||||
signature = {
|
|
||||||
# "node_descriptor": "<NodeDescriptor byte1=1 byte2=64 mac_capability_flags=142 manufacturer_code=4098
|
|
||||||
# maximum_buffer_size=82 maximum_incoming_transfer_size=82 server_mask=11264
|
|
||||||
# maximum_outgoing_transfer_size=82 descriptor_capability_field=0>",
|
|
||||||
# device_version=1
|
|
||||||
# input_clusters=[0x0000, 0x0004, 0x0005, 0xef00]
|
|
||||||
# output_clusters=[0x000a, 0x0019]
|
|
||||||
MODELS_INFO: [
|
|
||||||
("_TZE200_byzdayie", "TS0601"),
|
|
||||||
("_TZE200_ewxhg6o9", "TS0601"),
|
|
||||||
],
|
|
||||||
ENDPOINTS: {
|
|
||||||
# <SimpleDescriptor endpoint=1 profile=260 device_type=51
|
|
||||||
# device_version=1
|
|
||||||
# input_clusters=[0, 4, 5, 61184]
|
|
||||||
# output_clusters=[10, 25]>
|
|
||||||
1: {
|
|
||||||
PROFILE_ID: zha.PROFILE_ID,
|
|
||||||
DEVICE_TYPE: zha.DeviceType.SMART_PLUG,
|
|
||||||
INPUT_CLUSTERS: [
|
|
||||||
Basic.cluster_id,
|
|
||||||
Groups.cluster_id,
|
|
||||||
Scenes.cluster_id,
|
|
||||||
TuyaManufClusterAttributes.cluster_id,
|
|
||||||
],
|
|
||||||
OUTPUT_CLUSTERS: [Time.cluster_id, Ota.cluster_id],
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
replacement = {
|
|
||||||
ENDPOINTS: {
|
|
||||||
1: {
|
|
||||||
PROFILE_ID: zha.PROFILE_ID,
|
|
||||||
DEVICE_TYPE: zha.DeviceType.SMART_PLUG,
|
|
||||||
INPUT_CLUSTERS: [
|
|
||||||
Basic.cluster_id,
|
|
||||||
Groups.cluster_id,
|
|
||||||
Scenes.cluster_id,
|
|
||||||
TuyaManufClusterDinPower,
|
|
||||||
TuyaPowerMeasurement,
|
|
||||||
TuyaElectricalMeasurement,
|
|
||||||
TuyaOnOff,
|
|
||||||
],
|
|
||||||
OUTPUT_CLUSTERS: [Time.cluster_id, Ota.cluster_id],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class HikingPowerMeter(TuyaSwitch):
|
|
||||||
"""Hiking Power Meter Device - DDS238-2."""
|
|
||||||
|
|
||||||
signature = {
|
|
||||||
# "node_descriptor": "<NodeDescriptor byte1=1 byte2=64 mac_capability_flags=142 manufacturer_code=4098
|
|
||||||
# maximum_buffer_size=82 maximum_incoming_transfer_size=82 server_mask=11264
|
|
||||||
# maximum_outgoing_transfer_size=82 descriptor_capability_field=0>",
|
|
||||||
# device_version=1
|
|
||||||
# input_clusters=[0x0000, 0x0004, 0x0005, 0xef00]
|
|
||||||
# output_clusters=[0x000a, 0x0019]
|
|
||||||
MODELS_INFO: [
|
|
||||||
("_TZE200_bkkmqmyo", "TS0601"),
|
|
||||||
("_TZE204_81yrt3lo", "TS0601"),
|
|
||||||
],
|
|
||||||
ENDPOINTS: {
|
|
||||||
# <SimpleDescriptor endpoint=1 profile=260 device_type=51
|
|
||||||
# device_version=1
|
|
||||||
# input_clusters=[0, 4, 5, 61184]
|
|
||||||
# output_clusters=[10, 25]>
|
|
||||||
1: {
|
|
||||||
PROFILE_ID: zha.PROFILE_ID,
|
|
||||||
DEVICE_TYPE: zha.DeviceType.SMART_PLUG,
|
|
||||||
INPUT_CLUSTERS: [
|
|
||||||
Basic.cluster_id,
|
|
||||||
Groups.cluster_id,
|
|
||||||
Scenes.cluster_id,
|
|
||||||
TuyaManufClusterAttributes.cluster_id,
|
|
||||||
],
|
|
||||||
OUTPUT_CLUSTERS: [Time.cluster_id, Ota.cluster_id],
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
replacement = {
|
|
||||||
ENDPOINTS: {
|
|
||||||
1: {
|
|
||||||
PROFILE_ID: zha.PROFILE_ID,
|
|
||||||
DEVICE_TYPE: zha.DeviceType.SMART_PLUG,
|
|
||||||
INPUT_CLUSTERS: [
|
|
||||||
Basic.cluster_id,
|
|
||||||
Groups.cluster_id,
|
|
||||||
Scenes.cluster_id,
|
|
||||||
HikingManufClusterDinPower,
|
|
||||||
TuyaElectricalMeasurement,
|
|
||||||
TuyaPowerMeasurement,
|
|
||||||
],
|
|
||||||
OUTPUT_CLUSTERS: [Time.cluster_id, Ota.cluster_id],
|
|
||||||
},
|
|
||||||
16: {
|
|
||||||
PROFILE_ID: zha.PROFILE_ID,
|
|
||||||
DEVICE_TYPE: zha.DeviceType.SMART_PLUG,
|
|
||||||
INPUT_CLUSTERS: [
|
|
||||||
TuyaOnOff,
|
|
||||||
],
|
|
||||||
OUTPUT_CLUSTERS: [],
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,175 +0,0 @@
|
|||||||
"""
|
|
||||||
DY-YG400A Smoke Sensor, TS0601 from _TZE204_ntcy3xu1
|
|
||||||
https://www.aliexpress.com/item/1005005863519099.html
|
|
||||||
https://www.aliexpress.com/item/1005005854203557.html
|
|
||||||
"""
|
|
||||||
|
|
||||||
import logging
|
|
||||||
|
|
||||||
import zigpy.profiles.zha
|
|
||||||
from zigpy.quirks import CustomDevice
|
|
||||||
import zigpy.types as t
|
|
||||||
from zigpy.zcl.clusters.general import (
|
|
||||||
Basic,
|
|
||||||
Groups,
|
|
||||||
Ota,
|
|
||||||
Scenes,
|
|
||||||
Time,
|
|
||||||
BinaryInput,
|
|
||||||
BatterySize,
|
|
||||||
)
|
|
||||||
from zigpy.zcl.clusters.security import IasZone
|
|
||||||
from zhaquirks import Bus
|
|
||||||
from zhaquirks.const import (
|
|
||||||
DEVICE_TYPE,
|
|
||||||
ENDPOINTS,
|
|
||||||
INPUT_CLUSTERS,
|
|
||||||
MODELS_INFO,
|
|
||||||
OUTPUT_CLUSTERS,
|
|
||||||
PROFILE_ID,
|
|
||||||
ZONE_TYPE,
|
|
||||||
)
|
|
||||||
from zhaquirks.tuya import (
|
|
||||||
TuyaLocalCluster,
|
|
||||||
TuyaManufCluster,
|
|
||||||
TuyaManufClusterAttributes,
|
|
||||||
TuyaPowerConfigurationCluster,
|
|
||||||
)
|
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
TUYA_SMOKE_DETECTED_ATTR = 0x0401 # [0]/[1] [Detected]/[Clear]!
|
|
||||||
TUYA_SMOKE_TAMPERED_ATTR = 0x0104 # [0]/[1] [Clear]/[Tampered]!
|
|
||||||
TUYA_SMOKE_BATTERY_ATTR = 0x040e # [0]/[1]/[2] [Low]/[Med]]/[Full]!
|
|
||||||
|
|
||||||
|
|
||||||
class TuyaSmokeDetectorCluster(TuyaManufClusterAttributes):
|
|
||||||
"""Manufacturer Specific Cluster of the TS0601 smoke detector."""
|
|
||||||
|
|
||||||
attributes = {
|
|
||||||
TUYA_SMOKE_DETECTED_ATTR: ("smoke_detected", t.uint8_t, True),
|
|
||||||
TUYA_SMOKE_TAMPERED_ATTR: ("tampered_device", t.uint8_t, True),
|
|
||||||
TUYA_SMOKE_BATTERY_ATTR: ("battery_status", t.uint8_t, True),
|
|
||||||
}
|
|
||||||
|
|
||||||
def _update_attribute(self, attrid, value):
|
|
||||||
super()._update_attribute(attrid, value)
|
|
||||||
if attrid == TUYA_SMOKE_DETECTED_ATTR:
|
|
||||||
status = IasZone.ZoneStatus.Alarm_1 if value == 0 else 0
|
|
||||||
self.endpoint.device.ias_bus.listener_event("update_attribute", "zone_status", status)
|
|
||||||
elif attrid == TUYA_SMOKE_TAMPERED_ATTR:
|
|
||||||
self.endpoint.device.tamper_detection_bus.listener_event(
|
|
||||||
"update_attribute", "present_value", bool(value)
|
|
||||||
)
|
|
||||||
elif attrid == TUYA_SMOKE_BATTERY_ATTR:
|
|
||||||
batt = 5 if value == 0 else 40 if value == 1 else 100
|
|
||||||
self.endpoint.device.battery_bus.listener_event("battery_change", batt)
|
|
||||||
else:
|
|
||||||
_LOGGER.warning(
|
|
||||||
"[0x%04x:%s:0x%04x] unhandled attribute: 0x%04x",
|
|
||||||
self.endpoint.device.nwk,
|
|
||||||
self.endpoint.endpoint_id,
|
|
||||||
self.cluster_id,
|
|
||||||
attrid,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class TuyaIasZone(TuyaLocalCluster, IasZone):
|
|
||||||
"""
|
|
||||||
IAS Zone: this generates the "Smoke" entity for HA.
|
|
||||||
Receives updates from TuyaSmokeDetectorCluster.
|
|
||||||
"""
|
|
||||||
|
|
||||||
_CONSTANT_ATTRIBUTES = {ZONE_TYPE: IasZone.ZoneType.Fire_Sensor}
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
"""Init."""
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
self.endpoint.device.ias_bus.add_listener(self)
|
|
||||||
|
|
||||||
|
|
||||||
class TuyaPowerConfigCluster(TuyaPowerConfigurationCluster):
|
|
||||||
"""
|
|
||||||
Power Config Cluster: this generates the "Battery" entity for HA.
|
|
||||||
Receives updates from TuyaSmokeDetectorCluster.
|
|
||||||
"""
|
|
||||||
|
|
||||||
_CONSTANT_ATTRIBUTES = {
|
|
||||||
TuyaPowerConfigurationCluster.attributes_by_name["battery_size"].id: BatterySize.AAA,
|
|
||||||
TuyaPowerConfigurationCluster.attributes_by_name["battery_quantity"].id: 2,
|
|
||||||
TuyaPowerConfigurationCluster.attributes_by_name["battery_rated_voltage"].id: 15,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class TuyaTamperDetection(TuyaLocalCluster, BinaryInput):
|
|
||||||
"""
|
|
||||||
Tamper Detection Cluster: this generates the "Binary input" entity for HA, which is updated
|
|
||||||
with the Tampered state. Receives updates from TuyaSmokeDetectorCluster.
|
|
||||||
"""
|
|
||||||
|
|
||||||
_CONSTANT_ATTRIBUTES = {
|
|
||||||
BinaryInput.attributes_by_name["description"].id: "Tamper Detected",
|
|
||||||
BinaryInput.attributes_by_name["active_text"].id: "Tampered",
|
|
||||||
BinaryInput.attributes_by_name["inactive_text"].id: "Clear",
|
|
||||||
}
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
"""Init."""
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
self.endpoint.device.tamper_detection_bus.add_listener(self)
|
|
||||||
|
|
||||||
|
|
||||||
class TuyaSmokeDetector0601(CustomDevice):
|
|
||||||
"""TS0601 Smoke detector quirk."""
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
"""Init."""
|
|
||||||
self.ias_bus = Bus()
|
|
||||||
self.battery_bus = Bus()
|
|
||||||
self.tamper_detection_bus = Bus()
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
|
|
||||||
signature = {
|
|
||||||
MODELS_INFO: [
|
|
||||||
"""("_TZE200_uebojraa", "TS0601"),"""
|
|
||||||
("_TZE200_uebojraa"),
|
|
||||||
],
|
|
||||||
ENDPOINTS: {
|
|
||||||
1: {
|
|
||||||
PROFILE_ID: zigpy.profiles.zha.PROFILE_ID,
|
|
||||||
DEVICE_TYPE: zigpy.profiles.zha.DeviceType.SMART_PLUG,
|
|
||||||
INPUT_CLUSTERS: [
|
|
||||||
Basic.cluster_id,
|
|
||||||
Groups.cluster_id,
|
|
||||||
Scenes.cluster_id,
|
|
||||||
TuyaManufCluster.cluster_id,
|
|
||||||
],
|
|
||||||
OUTPUT_CLUSTERS: [
|
|
||||||
Time.cluster_id,
|
|
||||||
Ota.cluster_id,
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
replacement = {
|
|
||||||
ENDPOINTS: {
|
|
||||||
1: {
|
|
||||||
PROFILE_ID: zigpy.profiles.zha.PROFILE_ID,
|
|
||||||
DEVICE_TYPE: zigpy.profiles.zha.DeviceType.IAS_ZONE,
|
|
||||||
INPUT_CLUSTERS: [
|
|
||||||
Basic.cluster_id,
|
|
||||||
Groups.cluster_id,
|
|
||||||
Scenes.cluster_id,
|
|
||||||
TuyaIasZone,
|
|
||||||
TuyaSmokeDetectorCluster,
|
|
||||||
TuyaPowerConfigCluster,
|
|
||||||
TuyaTamperDetection,
|
|
||||||
],
|
|
||||||
OUTPUT_CLUSTERS: [
|
|
||||||
Time.cluster_id,
|
|
||||||
Ota.cluster_id,
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
@@ -1,151 +0,0 @@
|
|||||||
"""Tuya valve devices."""
|
|
||||||
|
|
||||||
import logging
|
|
||||||
from typing import Dict
|
|
||||||
|
|
||||||
from zigpy.profiles import zha
|
|
||||||
from zigpy.quirks import CustomDevice
|
|
||||||
import zigpy.types as t
|
|
||||||
from zigpy.zcl import foundation
|
|
||||||
from zigpy.zcl.clusters.general import (
|
|
||||||
Basic,
|
|
||||||
Groups,
|
|
||||||
Ota,
|
|
||||||
PowerConfiguration,
|
|
||||||
Scenes,
|
|
||||||
Time,
|
|
||||||
)
|
|
||||||
|
|
||||||
from zhaquirks.const import (
|
|
||||||
DEVICE_TYPE,
|
|
||||||
ENDPOINTS,
|
|
||||||
INPUT_CLUSTERS,
|
|
||||||
MODELS_INFO,
|
|
||||||
OUTPUT_CLUSTERS,
|
|
||||||
PROFILE_ID,
|
|
||||||
)
|
|
||||||
from zhaquirks.tuya import NoManufacturerCluster
|
|
||||||
from zhaquirks.tuya.mcu import (
|
|
||||||
DPToAttributeMapping,
|
|
||||||
TuyaAttributesCluster,
|
|
||||||
TuyaDPType,
|
|
||||||
TuyaLevelControl,
|
|
||||||
TuyaMCUCluster,
|
|
||||||
TuyaOnOff,
|
|
||||||
TuyaOnOffNM,
|
|
||||||
TuyaPowerConfigurationCluster,
|
|
||||||
)
|
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
class TuyaLevelControlNM(NoManufacturerCluster, TuyaLevelControl):
|
|
||||||
"""Tuya LevelControl cluster with NoManufacturerID."""
|
|
||||||
|
|
||||||
|
|
||||||
class TuyaValveManufCluster(TuyaMCUCluster):
|
|
||||||
"""Tuya valve manufacturer cluster."""
|
|
||||||
|
|
||||||
attributes = TuyaMCUCluster.attributes.copy()
|
|
||||||
attributes.update(
|
|
||||||
{
|
|
||||||
0xEF03: ("dp_3", t.uint32_t, True),
|
|
||||||
0xEF65: ("dp_101", t.uint32_t, True),
|
|
||||||
0xEF66: ("dp_102", t.uint32_t, True), # <-- new cluster attribute with fake IDs (102=0x66).Format= ID: ("name", type, True)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
dp_to_attribute: Dict[int, DPToAttributeMapping] = {
|
|
||||||
2: DPToAttributeMapping(
|
|
||||||
TuyaOnOffNM.ep_attribute,
|
|
||||||
"on_off",
|
|
||||||
dp_type=TuyaDPType.BOOL,
|
|
||||||
),
|
|
||||||
1: DPToAttributeMapping(
|
|
||||||
TuyaLevelControlNM.ep_attribute,
|
|
||||||
"current_level",
|
|
||||||
dp_type=TuyaDPType.VALUE,
|
|
||||||
converter=lambda x: (x * 255) // 100,
|
|
||||||
dp_converter=lambda x: (x * 100) // 255,
|
|
||||||
),
|
|
||||||
3: DPToAttributeMapping(
|
|
||||||
TuyaMCUCluster.ep_attribute,
|
|
||||||
"dp_3",
|
|
||||||
dp_type=TuyaDPType.VALUE,
|
|
||||||
),
|
|
||||||
101: DPToAttributeMapping(
|
|
||||||
TuyaMCUCluster.ep_attribute,
|
|
||||||
"dp_101",
|
|
||||||
dp_type=TuyaDPType.VALUE,
|
|
||||||
),
|
|
||||||
102: DPToAttributeMapping( # <-- device DP102
|
|
||||||
TuyaMCUCluster.ep_attribute, # <-- reference to the cluster which has the attribute
|
|
||||||
"dp_102", # <-- attribute "name"
|
|
||||||
dp_type=TuyaDPType.VALUE, # <-- DP Type it is related to the attribute type
|
|
||||||
),
|
|
||||||
108: DPToAttributeMapping(
|
|
||||||
TuyaPowerConfigurationCluster.ep_attribute,
|
|
||||||
"battery_percentage_remaining",
|
|
||||||
dp_type=TuyaDPType.VALUE,
|
|
||||||
),
|
|
||||||
}
|
|
||||||
|
|
||||||
data_point_handlers = {
|
|
||||||
2: "_dp_2_attr_update",
|
|
||||||
1: "_dp_2_attr_update",
|
|
||||||
3: "_dp_2_attr_update",
|
|
||||||
101: "_dp_2_attr_update",
|
|
||||||
102: "_dp_2_attr_update",
|
|
||||||
108: "_dp_2_attr_update",
|
|
||||||
}
|
|
||||||
|
|
||||||
async def write_attributes(self, attributes, manufacturer=None):
|
|
||||||
"""Overwrite to force manufacturer code."""
|
|
||||||
|
|
||||||
return await super().write_attributes(
|
|
||||||
attributes, manufacturer=foundation.ZCLHeader.NO_MANUFACTURER_ID
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class TuyaValve(CustomDevice):
|
|
||||||
"""Tuya valve device."""
|
|
||||||
|
|
||||||
signature = {
|
|
||||||
"""MODELS_INFO: [("_TZE200_sh1btabb", "TS0601")],"""
|
|
||||||
MODELS_INFO: [("_TZE200_sh1btabb")],
|
|
||||||
|
|
||||||
ENDPOINTS: {
|
|
||||||
# <SimpleDescriptor endpoint=1 profile=260 device_type=0x0051
|
|
||||||
# input_clusters=[0x0000, 0x0004, 0x0005, 0xef00]
|
|
||||||
# output_clusters=[0x000a, 0x0019]>
|
|
||||||
1: {
|
|
||||||
PROFILE_ID: zha.PROFILE_ID,
|
|
||||||
DEVICE_TYPE: zha.DeviceType.SMART_PLUG,
|
|
||||||
INPUT_CLUSTERS: [
|
|
||||||
Basic.cluster_id,
|
|
||||||
Groups.cluster_id,
|
|
||||||
Scenes.cluster_id,
|
|
||||||
TuyaValveManufCluster.cluster_id,
|
|
||||||
],
|
|
||||||
OUTPUT_CLUSTERS: [Time.cluster_id, Ota.cluster_id],
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
replacement = {
|
|
||||||
ENDPOINTS: {
|
|
||||||
1: {
|
|
||||||
DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT,
|
|
||||||
INPUT_CLUSTERS: [
|
|
||||||
Basic.cluster_id,
|
|
||||||
Groups.cluster_id,
|
|
||||||
Scenes.cluster_id,
|
|
||||||
TuyaValveManufCluster,
|
|
||||||
TuyaOnOffNM,
|
|
||||||
TuyaLevelControlNM,
|
|
||||||
TuyaPowerConfigurationCluster,
|
|
||||||
],
|
|
||||||
OUTPUT_CLUSTERS: [Time.cluster_id, Ota.cluster_id],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -244,7 +244,7 @@ globals:
|
|||||||
- id: max_power_7d
|
- id: max_power_7d
|
||||||
type: float
|
type: float
|
||||||
restore_value: true
|
restore_value: true
|
||||||
initial_value: '0.0'
|
initial_value: "0.0"
|
||||||
update_interval: 5min
|
update_interval: 5min
|
||||||
|
|
||||||
# Powered hours elapsed during the current seven-day period.
|
# Powered hours elapsed during the current seven-day period.
|
||||||
@@ -252,7 +252,7 @@ globals:
|
|||||||
- id: max_power_hours_elapsed
|
- id: max_power_hours_elapsed
|
||||||
type: uint16_t
|
type: uint16_t
|
||||||
restore_value: true
|
restore_value: true
|
||||||
initial_value: '0'
|
initial_value: "0"
|
||||||
update_interval: 6h
|
update_interval: 6h
|
||||||
|
|
||||||
#:########################################################################################:#
|
#:########################################################################################:#
|
||||||
@@ -411,7 +411,7 @@ switch:
|
|||||||
id: relay
|
id: relay
|
||||||
pin: GPIO12
|
pin: GPIO12
|
||||||
|
|
||||||
restore_mode: RESTORE_DEFAULT_ON
|
restore_mode: RESTORE_DEFAULT_OFF
|
||||||
|
|
||||||
on_turn_on:
|
on_turn_on:
|
||||||
then:
|
then:
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
# https://home.fox.co.nz/gitea/zorruno/zorruno-homeassistant/src/branch/master/esphome/esp-centralstairs-bottom.yaml
|
# https://home.fox.co.nz/gitea/zorruno/zorruno-homeassistant/src/branch/master/esphome/esp-centralstairs-bottom.yaml
|
||||||
#:########################################################################################:#
|
#:########################################################################################:#
|
||||||
# VERSIONS:
|
# VERSIONS:
|
||||||
|
# V3.9 2026-07-21 Added retained MQTT status mirroring for HA-independent two-way switching
|
||||||
# V3.8 2026-06-09 Added Downstairs Lights Bulk Activation virtual switch toggled by Button 1 double-click
|
# V3.8 2026-06-09 Added Downstairs Lights Bulk Activation virtual switch toggled by Button 1 double-click
|
||||||
# V3.7 2025-09-24 Upload to this device
|
# V3.7 2025-09-24 Upload to this device
|
||||||
# V3.5 2025-07-24 YAML tidyups
|
# V3.5 2025-07-24 YAML tidyups
|
||||||
@@ -83,7 +84,7 @@ substitutions:
|
|||||||
|
|
||||||
# Project Naming
|
# Project Naming
|
||||||
project_name: "Zemismart Technologies.KS-811 Double" # Project Details
|
project_name: "Zemismart Technologies.KS-811 Double" # Project Details
|
||||||
project_version: "v2.1" # Project V denotes release of yaml file, allowing checking of deployed vs latest version
|
project_version: "v3.9" # Project V denotes release of yaml file, allowing checking of deployed vs latest version
|
||||||
|
|
||||||
# MQTT Controls
|
# MQTT Controls
|
||||||
mqtt_command_main_topic: !secret mqtt_command_main_topic
|
mqtt_command_main_topic: !secret mqtt_command_main_topic
|
||||||
@@ -184,19 +185,19 @@ mqtt:
|
|||||||
then:
|
then:
|
||||||
- if:
|
- if:
|
||||||
condition:
|
condition:
|
||||||
lambda: 'return id(Relay_1).state;'
|
lambda: "return id(Relay_1).state;"
|
||||||
then:
|
then:
|
||||||
- mqtt.publish:
|
- mqtt.publish:
|
||||||
topic: "${mqtt_status_topic_1}"
|
topic: "${mqtt_status_topic_1}"
|
||||||
payload: "${mqtt_command_ON}"
|
payload: "${mqtt_command_ON}"
|
||||||
qos: 0
|
qos: 1
|
||||||
retain: false
|
retain: true
|
||||||
else:
|
else:
|
||||||
- mqtt.publish:
|
- mqtt.publish:
|
||||||
topic: "${mqtt_status_topic_1}"
|
topic: "${mqtt_status_topic_1}"
|
||||||
payload: "${mqtt_command_OFF}"
|
payload: "${mqtt_command_OFF}"
|
||||||
qos: 0
|
qos: 1
|
||||||
retain: false
|
retain: true
|
||||||
|
|
||||||
on_message:
|
on_message:
|
||||||
# Relay 1: only turn ON if currently OFF
|
# Relay 1: only turn ON if currently OFF
|
||||||
@@ -241,6 +242,26 @@ mqtt:
|
|||||||
then:
|
then:
|
||||||
- switch.turn_off: Relay_2
|
- switch.turn_off: Relay_2
|
||||||
|
|
||||||
|
# Relay 2 is a mirror of the remote physical Stair Footer Lights relay.
|
||||||
|
- topic: "${mqtt_status_topic_2}"
|
||||||
|
payload: "${mqtt_command_ON}"
|
||||||
|
then:
|
||||||
|
- if:
|
||||||
|
condition:
|
||||||
|
not:
|
||||||
|
- switch.is_on: Relay_2
|
||||||
|
then:
|
||||||
|
- switch.turn_on: Relay_2
|
||||||
|
|
||||||
|
- topic: "${mqtt_status_topic_2}"
|
||||||
|
payload: "${mqtt_command_OFF}"
|
||||||
|
then:
|
||||||
|
- if:
|
||||||
|
condition:
|
||||||
|
- switch.is_on: Relay_2
|
||||||
|
then:
|
||||||
|
- switch.turn_off: Relay_2
|
||||||
|
|
||||||
#:########################################################################################:#
|
#:########################################################################################:#
|
||||||
# STATUS LED:
|
# STATUS LED:
|
||||||
# https://esphome.io/components/status_led.html
|
# https://esphome.io/components/status_led.html
|
||||||
@@ -298,7 +319,7 @@ binary_sensor:
|
|||||||
# Toggle the remote Footer Lights via COMMAND topic, based on our mirrored state
|
# Toggle the remote Footer Lights via COMMAND topic, based on our mirrored state
|
||||||
- if:
|
- if:
|
||||||
condition:
|
condition:
|
||||||
lambda: 'return id(Relay_2).state;'
|
lambda: "return id(Relay_2).state;"
|
||||||
then:
|
then:
|
||||||
- mqtt.publish:
|
- mqtt.publish:
|
||||||
topic: "${mqtt_command_topic_2}"
|
topic: "${mqtt_command_topic_2}"
|
||||||
@@ -332,12 +353,14 @@ switch:
|
|||||||
- mqtt.publish:
|
- mqtt.publish:
|
||||||
topic: "${mqtt_status_topic_1}"
|
topic: "${mqtt_status_topic_1}"
|
||||||
payload: "${mqtt_command_ON}"
|
payload: "${mqtt_command_ON}"
|
||||||
retain: false
|
qos: 1
|
||||||
|
retain: true
|
||||||
on_turn_off:
|
on_turn_off:
|
||||||
- mqtt.publish:
|
- mqtt.publish:
|
||||||
topic: "${mqtt_status_topic_1}"
|
topic: "${mqtt_status_topic_1}"
|
||||||
payload: "${mqtt_command_OFF}"
|
payload: "${mqtt_command_OFF}"
|
||||||
retain: false
|
qos: 1
|
||||||
|
retain: true
|
||||||
|
|
||||||
- platform: gpio
|
- platform: gpio
|
||||||
name: "Relay 2: ${switch_2_name}"
|
name: "Relay 2: ${switch_2_name}"
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
# https://home.fox.co.nz/gitea/zorruno/zorruno-homeassistant/src/branch/master/esphome/esp-centralstairs-top.yaml
|
# https://home.fox.co.nz/gitea/zorruno/zorruno-homeassistant/src/branch/master/esphome/esp-centralstairs-top.yaml
|
||||||
#:########################################################################################:#
|
#:########################################################################################:#
|
||||||
# VERSIONS:
|
# VERSIONS:
|
||||||
|
# V3.9 2026-07-21 Added retained MQTT status mirroring for HA-independent two-way switching
|
||||||
# V3.8 2026-04-26 Added Downstairs Lights Bulk Activation virtual switch toggled by Button 1 double-click
|
# V3.8 2026-04-26 Added Downstairs Lights Bulk Activation virtual switch toggled by Button 1 double-click
|
||||||
# V3.7 2025-09-24 Upload to this device
|
# V3.7 2025-09-24 Upload to this device
|
||||||
# V3.5 2025-07-24 YAML tidyups
|
# V3.5 2025-07-24 YAML tidyups
|
||||||
@@ -83,7 +84,7 @@ substitutions:
|
|||||||
|
|
||||||
# Project Naming
|
# Project Naming
|
||||||
project_name: "Zemismart Technologies.KS-811 Double" # Project Details
|
project_name: "Zemismart Technologies.KS-811 Double" # Project Details
|
||||||
project_version: "v2.1" # Project V denotes release of yaml file, allowing checking of deployed vs latest version
|
project_version: "v3.9" # Project V denotes release of yaml file, allowing checking of deployed vs latest version
|
||||||
|
|
||||||
# MQTT Controls
|
# MQTT Controls
|
||||||
mqtt_command_main_topic: !secret mqtt_command_main_topic
|
mqtt_command_main_topic: !secret mqtt_command_main_topic
|
||||||
@@ -184,19 +185,19 @@ mqtt:
|
|||||||
then:
|
then:
|
||||||
- if:
|
- if:
|
||||||
condition:
|
condition:
|
||||||
lambda: 'return id(Relay_2).state;'
|
lambda: "return id(Relay_2).state;"
|
||||||
then:
|
then:
|
||||||
- mqtt.publish:
|
- mqtt.publish:
|
||||||
topic: "${mqtt_status_topic_2}"
|
topic: "${mqtt_status_topic_2}"
|
||||||
payload: "${mqtt_command_ON}"
|
payload: "${mqtt_command_ON}"
|
||||||
qos: 0
|
qos: 1
|
||||||
retain: false
|
retain: true
|
||||||
else:
|
else:
|
||||||
- mqtt.publish:
|
- mqtt.publish:
|
||||||
topic: "${mqtt_status_topic_2}"
|
topic: "${mqtt_status_topic_2}"
|
||||||
payload: "${mqtt_command_OFF}"
|
payload: "${mqtt_command_OFF}"
|
||||||
qos: 0
|
qos: 1
|
||||||
retain: false
|
retain: true
|
||||||
|
|
||||||
on_message:
|
on_message:
|
||||||
# Relay 1: only turn ON if currently OFF
|
# Relay 1: only turn ON if currently OFF
|
||||||
@@ -241,6 +242,26 @@ mqtt:
|
|||||||
then:
|
then:
|
||||||
- switch.turn_off: Relay_2
|
- switch.turn_off: Relay_2
|
||||||
|
|
||||||
|
# Relay 1 is a mirror of the remote physical Main Stair Lights relay.
|
||||||
|
- topic: "${mqtt_status_topic_1}"
|
||||||
|
payload: "${mqtt_command_ON}"
|
||||||
|
then:
|
||||||
|
- if:
|
||||||
|
condition:
|
||||||
|
not:
|
||||||
|
- switch.is_on: Relay_1
|
||||||
|
then:
|
||||||
|
- switch.turn_on: Relay_1
|
||||||
|
|
||||||
|
- topic: "${mqtt_status_topic_1}"
|
||||||
|
payload: "${mqtt_command_OFF}"
|
||||||
|
then:
|
||||||
|
- if:
|
||||||
|
condition:
|
||||||
|
- switch.is_on: Relay_1
|
||||||
|
then:
|
||||||
|
- switch.turn_off: Relay_1
|
||||||
|
|
||||||
#:########################################################################################:#
|
#:########################################################################################:#
|
||||||
# STATUS LED:
|
# STATUS LED:
|
||||||
# https://esphome.io/components/status_led.html
|
# https://esphome.io/components/status_led.html
|
||||||
@@ -286,7 +307,7 @@ binary_sensor:
|
|||||||
then:
|
then:
|
||||||
- if:
|
- if:
|
||||||
condition:
|
condition:
|
||||||
lambda: 'return id(Relay_1).state;'
|
lambda: "return id(Relay_1).state;"
|
||||||
then:
|
then:
|
||||||
- mqtt.publish:
|
- mqtt.publish:
|
||||||
topic: "${mqtt_command_topic_1}"
|
topic: "${mqtt_command_topic_1}"
|
||||||
@@ -338,9 +359,11 @@ switch:
|
|||||||
- mqtt.publish:
|
- mqtt.publish:
|
||||||
topic: "${mqtt_status_topic_2}"
|
topic: "${mqtt_status_topic_2}"
|
||||||
payload: "${mqtt_command_ON}"
|
payload: "${mqtt_command_ON}"
|
||||||
retain: false
|
qos: 1
|
||||||
|
retain: true
|
||||||
on_turn_off:
|
on_turn_off:
|
||||||
- mqtt.publish:
|
- mqtt.publish:
|
||||||
topic: "${mqtt_status_topic_2}"
|
topic: "${mqtt_status_topic_2}"
|
||||||
payload: "${mqtt_command_OFF}"
|
payload: "${mqtt_command_OFF}"
|
||||||
retain: false
|
qos: 1
|
||||||
|
retain: true
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,507 +0,0 @@
|
|||||||
#:########################################################################################:#
|
|
||||||
# TITLE: LOUNGE MIDDLE LIGHTSWITCH
|
|
||||||
# zorruno.com layout v1.1 2026
|
|
||||||
#:########################################################################################:#
|
|
||||||
# REPO:
|
|
||||||
# https://home.fox.co.nz/gitea/zorruno/zorruno-homeassistant/src/branch/master/esphome/esp-loungemiddleswitch.yaml
|
|
||||||
#:########################################################################################:#
|
|
||||||
# VERSIONS:
|
|
||||||
# V3.6 2026-03-11 Updated yaml to zorruno layout V1.1
|
|
||||||
# V3.5 2025-07-28 YAML tidyups
|
|
||||||
#:########################################################################################:#
|
|
||||||
# HARDWARE:
|
|
||||||
# - Zemismart KS-811 Triple push button
|
|
||||||
# - ESP8266 based
|
|
||||||
# - pinout/schematic:
|
|
||||||
# https://community.home-assistant.io/t/zemismart-ks-811-working-with-esphome/
|
|
||||||
# - Relay 1 output on GPIO13
|
|
||||||
# - Relay 2 output on GPIO12
|
|
||||||
# - Relay 3 output on GPIO14
|
|
||||||
# - Button 1 input on GPIO16
|
|
||||||
# - Button 2 input on GPIO5
|
|
||||||
# - Button 3 input on GPIO4
|
|
||||||
# - Status LED on GPIO2
|
|
||||||
#:########################################################################################:#
|
|
||||||
# OPERATION NOTES:
|
|
||||||
# - Button 1:
|
|
||||||
# - Online with HA Loss manual override ON and HA Loss Test OFF:
|
|
||||||
# - Uses Home Assistant last-scene logic
|
|
||||||
# - If last scene is not "All Off", set "All Off"
|
|
||||||
# - If last scene is "All Off", set "Bright"
|
|
||||||
# - Offline/test with HA Loss manual override ON:
|
|
||||||
# - Toggles Relay_1 locally
|
|
||||||
# - Button 2:
|
|
||||||
# - Online with HA Loss manual override ON and HA Loss Test OFF:
|
|
||||||
# - Sets Home Assistant scene "Movie"
|
|
||||||
# - Offline/test with HA Loss manual override ON:
|
|
||||||
# - Toggles Relay_2 locally
|
|
||||||
# - Button 3:
|
|
||||||
# - Online with HA Loss manual override ON and HA Loss Test OFF:
|
|
||||||
# - Sets Home Assistant scene "Reading On/Off"
|
|
||||||
# - Offline/test with HA Loss manual override ON:
|
|
||||||
# - Toggles Relay_3 locally
|
|
||||||
# - On API connect:
|
|
||||||
# - If HA Loss Test is OFF, Relay_1 and Relay_2 follow HA Loss manual override state
|
|
||||||
# - If HA Loss Test is ON, relay override is not applied
|
|
||||||
# - HA Loss manual override defaults ON and persists across restarts
|
|
||||||
# - HA Loss Test defaults OFF and can force offline/test behaviour even when HA is connected
|
|
||||||
#:########################################################################################:#
|
|
||||||
# OFFLINE NOTES:
|
|
||||||
# a) Home Assistant offline (network online):
|
|
||||||
# - Device switches to manual/offline behaviour
|
|
||||||
# - Buttons still operate local relays if HA Loss manual override is ON
|
|
||||||
# - Home Assistant scene calls are unavailable
|
|
||||||
# b) MQTT offline (or HA/MQTT offline):
|
|
||||||
# - This yaml does not depend on MQTT for its button logic
|
|
||||||
# - If Home Assistant/API is offline, device falls back to manual/offline behaviour
|
|
||||||
# - Buttons still operate local relays if HA Loss manual override is ON
|
|
||||||
# c) Entire WiFi/Network offline:
|
|
||||||
# - Device operates in local manual/offline behaviour only
|
|
||||||
# - Buttons still operate local relays if HA Loss manual override is ON
|
|
||||||
# - Accurate time is not needed; SNTP is not required for operation
|
|
||||||
#:########################################################################################:#
|
|
||||||
|
|
||||||
#:########################################################################################:#
|
|
||||||
# SUBSTITUTIONS:
|
|
||||||
# Specific device variable substitutions
|
|
||||||
# If NOT using a secrets file, just replace these with the passwords etc (in quotes)
|
|
||||||
#:########################################################################################:#
|
|
||||||
substitutions:
|
|
||||||
# Device Naming
|
|
||||||
device_name: "esp-loungemiddleswitch"
|
|
||||||
friendly_name: "Lounge Middle Lightswitch (3)"
|
|
||||||
description_comment: "Lounge Main Lightswitch using a Zemismart KS-811 Triple Push Button. Downlights South (1), Downlights Middle (2), Downlights North (3) (Layout V1.1)"
|
|
||||||
device_area: "Lounge" # Allows ESP device to be automatically linked to an 'Area' in Home Assistant.
|
|
||||||
|
|
||||||
# Project Naming
|
|
||||||
project_name: "Zemismart Technologies.KS-811 Triple" # Project Details
|
|
||||||
project_version: "v3.6" # Project V denotes release of yaml file, allowing checking of deployed vs latest version
|
|
||||||
|
|
||||||
# Passwords & Secrets
|
|
||||||
api_key: !secret esp-api_key # unfortunately you can't use substitutions inside secrets names
|
|
||||||
ota_pass: !secret esp-ota_pass # unfortunately you can't use substitutions inside secrets names
|
|
||||||
static_ip_address: !secret esp-loungemiddleswitch_ip
|
|
||||||
|
|
||||||
# If we are changing IP addresses, you must update the current IP address here, otherwise it remains
|
|
||||||
# Don't forget to switch it back when changed.
|
|
||||||
current_ip_address: ${static_ip_address}
|
|
||||||
|
|
||||||
# Device Settings
|
|
||||||
log_level: "INFO" # Define logging level: NONE, ERROR, WARN, INFO, DEBUG (Default), VERBOSE, VERY_VERBOSE
|
|
||||||
update_interval: "60s" # update time for for general sensors etc
|
|
||||||
|
|
||||||
# MQTT LOCAL Controls
|
|
||||||
#mqtt_device_name: "lounge-main-lights"
|
|
||||||
#mqtt_local_command_topic: "${mqtt_command_main_topic}/${mqtt_device_name}" # Topic we will use to command this locally without HA
|
|
||||||
#mqtt_local_status_topic: "${mqtt_status_main_topic}/${mqtt_device_name}" # Topic we will use to view status locally without HA
|
|
||||||
|
|
||||||
# MQTT REMOTE Controls
|
|
||||||
|
|
||||||
# Switch/Relay/Button Naming & Icons
|
|
||||||
#relay_icon: "mdi:lightbulb-group"
|
|
||||||
switch_1_name: "Downlights South" # Lights are connected via DMX dimmer (another esphome controller) (4 Downlights)
|
|
||||||
switch_2_name: "Downlights Middle" # Lights are connected via DMX dimmer (another esphome controller) (6 Downlights)
|
|
||||||
switch_3_name: "Downlights North" # Lights are directly connected (2 downlights)
|
|
||||||
|
|
||||||
#:########################################################################################:#
|
|
||||||
# PACKAGES:
|
|
||||||
# Included Common Packages
|
|
||||||
# https://esphome.io/components/packages.html
|
|
||||||
#:########################################################################################:#
|
|
||||||
packages:
|
|
||||||
#### WIFI, Network (Static/DHCP/IPV6 etc), Fallback AP, Safemode ####
|
|
||||||
common_wifi: !include
|
|
||||||
file: common/network_common.yaml
|
|
||||||
vars:
|
|
||||||
local_device_name: "${device_name}"
|
|
||||||
local_static_ip_address: "${static_ip_address}"
|
|
||||||
local_current_ip_address: "${current_ip_address}"
|
|
||||||
local_ota_pass: "${ota_pass}"
|
|
||||||
|
|
||||||
#### HOME ASSISTANT API (choose encryption or no encryption options) ####
|
|
||||||
common_api: !include
|
|
||||||
file: common/api_common.yaml
|
|
||||||
#file: common/api_common_noencryption.yaml
|
|
||||||
vars:
|
|
||||||
local_api_key: "${api_key}"
|
|
||||||
|
|
||||||
#### MQTT ####
|
|
||||||
common_mqtt: !include
|
|
||||||
file: common/mqtt_common.yaml
|
|
||||||
vars:
|
|
||||||
local_device_name: "${device_name}"
|
|
||||||
|
|
||||||
#### WEB PORTAL ####
|
|
||||||
#common_webportal: !include common/webportal_common.yaml
|
|
||||||
|
|
||||||
#### SNTP (Only use if you want/need accurate timeclocks) ####
|
|
||||||
#common_sntp: !include common/sntp_common.yaml
|
|
||||||
|
|
||||||
#### DIAGNOSTICS Sensors ####
|
|
||||||
diag_basic: !include common/include_basic_diag_sensors.yaml
|
|
||||||
diag_more: !include common/include_more_diag_sensors.yaml
|
|
||||||
#diag_debug: !include common/include_debug_diag_sensors.yaml
|
|
||||||
#diag_resetcount: !include common/include_resetcount_diag_sensors.yaml
|
|
||||||
|
|
||||||
#:########################################################################################:#
|
|
||||||
# ESPHOME:
|
|
||||||
# https://esphome.io/components/esphome.html
|
|
||||||
#:########################################################################################:#
|
|
||||||
esphome:
|
|
||||||
name: "${device_name}"
|
|
||||||
friendly_name: "${friendly_name}"
|
|
||||||
comment: "${description_comment}" # Appears on the esphome page in HA
|
|
||||||
area: "${device_area}"
|
|
||||||
project:
|
|
||||||
name: "${project_name}"
|
|
||||||
version: "${project_version}"
|
|
||||||
|
|
||||||
#:########################################################################################:#
|
|
||||||
# HOME ASSISTANT API:
|
|
||||||
#:########################################################################################:#
|
|
||||||
api:
|
|
||||||
on_client_connected:
|
|
||||||
then:
|
|
||||||
- logger.log:
|
|
||||||
level: INFO
|
|
||||||
format: "API connected: override=%s, ha_loss_test=%s"
|
|
||||||
args:
|
|
||||||
- 'id(ha_loss_override).state ? "true" : "false"'
|
|
||||||
- 'id(ha_loss_test).state ? "true" : "false"'
|
|
||||||
- if:
|
|
||||||
condition:
|
|
||||||
switch.is_on: ha_loss_test
|
|
||||||
then:
|
|
||||||
- logger.log:
|
|
||||||
level: WARN
|
|
||||||
format: "HA Loss Test is ON: not applying relay override on connect"
|
|
||||||
else:
|
|
||||||
- if:
|
|
||||||
condition:
|
|
||||||
switch.is_on: ha_loss_override
|
|
||||||
then:
|
|
||||||
- switch.turn_on: Relay_1
|
|
||||||
- switch.turn_on: Relay_2
|
|
||||||
else:
|
|
||||||
- switch.turn_off: Relay_1
|
|
||||||
- switch.turn_off: Relay_2
|
|
||||||
|
|
||||||
on_client_disconnected:
|
|
||||||
then:
|
|
||||||
- logger.log:
|
|
||||||
level: WARN
|
|
||||||
format: "API disconnected: switching to manual/offline behavior"
|
|
||||||
|
|
||||||
#:########################################################################################:#
|
|
||||||
# ESP PLATFORM AND FRAMEWORK:
|
|
||||||
# https://esphome.io/components/esp8266.html
|
|
||||||
#:########################################################################################:#
|
|
||||||
esp8266:
|
|
||||||
board: esp01_1m
|
|
||||||
early_pin_init: false # Initialise pins early to known values. Recommended false where switches are involved. Defaults to True.
|
|
||||||
board_flash_mode: dout # Default is dout
|
|
||||||
|
|
||||||
#:########################################################################################:#
|
|
||||||
# LOGGER:
|
|
||||||
# https://esphome.io/components/logger.html
|
|
||||||
#:########################################################################################:#
|
|
||||||
logger:
|
|
||||||
level: "${log_level}" # INFO Level suggested, or DEBUG for testing
|
|
||||||
#baud_rate: 0 # set to 0 for no logging via UART, needed if you are using it for other serial things (eg PZEM)
|
|
||||||
#esp8266_store_log_strings_in_flash: false
|
|
||||||
#tx_buffer_size: 64
|
|
||||||
|
|
||||||
#:########################################################################################:#
|
|
||||||
# STATUS LED:
|
|
||||||
# https://esphome.io/components/status_led.html
|
|
||||||
#:########################################################################################:#
|
|
||||||
status_led:
|
|
||||||
pin:
|
|
||||||
number: GPIO2
|
|
||||||
inverted: true
|
|
||||||
|
|
||||||
#:########################################################################################:#
|
|
||||||
# BINARY SENSORS:
|
|
||||||
# https://esphome.io/components/binary_sensor/
|
|
||||||
#:########################################################################################:#
|
|
||||||
binary_sensor:
|
|
||||||
# GPIO16 for KS-811 first button UNLESS
|
|
||||||
# it is a single button KS-811 in which case it is GPIO00
|
|
||||||
- platform: gpio
|
|
||||||
pin:
|
|
||||||
number: GPIO16
|
|
||||||
mode: INPUT
|
|
||||||
inverted: true
|
|
||||||
use_interrupt: false # This pin doesn't support interrupts so use polling. Only suppresses a warning.
|
|
||||||
name: "Button 1: ${switch_1_name}"
|
|
||||||
on_multi_click:
|
|
||||||
# --- Single short click ---
|
|
||||||
# Online (and override ON, and NOT test mode): use HA's last scene to decide.
|
|
||||||
# Offline (override ON): toggle physical Relay_1
|
|
||||||
# Test mode (override ON): behave like offline even if HA is connected
|
|
||||||
- timing:
|
|
||||||
- ON for at most 0.5s
|
|
||||||
- OFF for at least 0.5s
|
|
||||||
then:
|
|
||||||
- if:
|
|
||||||
condition:
|
|
||||||
and:
|
|
||||||
- switch.is_on: ha_loss_override
|
|
||||||
- api.connected:
|
|
||||||
- not:
|
|
||||||
switch.is_on: ha_loss_test
|
|
||||||
then:
|
|
||||||
- if:
|
|
||||||
condition:
|
|
||||||
lambda: |-
|
|
||||||
// Defensive: treat empty/unknown as "not All Off" so we go to a safe "All Off".
|
|
||||||
auto s = id(ha_last_scene).state;
|
|
||||||
if (s.empty() || s == "unknown" || s == "unavailable") return true;
|
|
||||||
return s != "All Off";
|
|
||||||
then:
|
|
||||||
- homeassistant.service:
|
|
||||||
service: script.lounge_set_scene
|
|
||||||
data:
|
|
||||||
scene_name: "All Off"
|
|
||||||
- logger.log:
|
|
||||||
level: INFO
|
|
||||||
format: "BTN1: Single click (online) -> Last scene was not 'All Off' -> Set 'All Off'"
|
|
||||||
else:
|
|
||||||
- homeassistant.service:
|
|
||||||
service: script.lounge_set_scene
|
|
||||||
data:
|
|
||||||
scene_name: "Bright"
|
|
||||||
- logger.log:
|
|
||||||
level: INFO
|
|
||||||
format: "BTN1: Single click (online) -> Last scene was 'All Off' -> Set 'Bright'"
|
|
||||||
else:
|
|
||||||
- if:
|
|
||||||
condition:
|
|
||||||
and:
|
|
||||||
- switch.is_on: ha_loss_override
|
|
||||||
- or:
|
|
||||||
- not:
|
|
||||||
api.connected:
|
|
||||||
- switch.is_on: ha_loss_test
|
|
||||||
then:
|
|
||||||
- if:
|
|
||||||
condition:
|
|
||||||
lambda: |-
|
|
||||||
return id(Relay_1).state;
|
|
||||||
then:
|
|
||||||
- switch.turn_off: Relay_1
|
|
||||||
- logger.log:
|
|
||||||
level: INFO
|
|
||||||
format: "BTN1: Single click (offline/test) -> Relay_1 OFF"
|
|
||||||
else:
|
|
||||||
- switch.turn_on: Relay_1
|
|
||||||
- logger.log:
|
|
||||||
level: INFO
|
|
||||||
format: "BTN1: Single click (offline/test) -> Relay_1 ON"
|
|
||||||
else:
|
|
||||||
- logger.log:
|
|
||||||
level: INFO
|
|
||||||
format: "BTN1: Single click ignored (override OFF)"
|
|
||||||
|
|
||||||
# GPIO05 for second button (only for KS-811-2 Double or -3 Triple)
|
|
||||||
- platform: gpio
|
|
||||||
pin:
|
|
||||||
number: GPIO5
|
|
||||||
mode: INPUT
|
|
||||||
inverted: true
|
|
||||||
name: "Button 2: ${switch_2_name}"
|
|
||||||
on_multi_click:
|
|
||||||
# --- Single short click ---
|
|
||||||
# Online (override ON, NOT test mode): set scene
|
|
||||||
# Offline/test (override ON): toggle Relay_2
|
|
||||||
- timing:
|
|
||||||
- ON for at most 0.5s
|
|
||||||
- OFF for at least 0.5s
|
|
||||||
then:
|
|
||||||
- if:
|
|
||||||
condition:
|
|
||||||
and:
|
|
||||||
- switch.is_on: ha_loss_override
|
|
||||||
- api.connected:
|
|
||||||
- not:
|
|
||||||
switch.is_on: ha_loss_test
|
|
||||||
then:
|
|
||||||
- homeassistant.service:
|
|
||||||
service: script.lounge_set_scene
|
|
||||||
data:
|
|
||||||
scene_name: "Movie"
|
|
||||||
- logger.log:
|
|
||||||
level: INFO
|
|
||||||
format: "BTN2: Single click (online) -> Set 'Movie' Scene"
|
|
||||||
else:
|
|
||||||
- if:
|
|
||||||
condition:
|
|
||||||
and:
|
|
||||||
- switch.is_on: ha_loss_override
|
|
||||||
- or:
|
|
||||||
- not:
|
|
||||||
api.connected:
|
|
||||||
- switch.is_on: ha_loss_test
|
|
||||||
then:
|
|
||||||
- if:
|
|
||||||
condition:
|
|
||||||
lambda: |-
|
|
||||||
return id(Relay_2).state;
|
|
||||||
then:
|
|
||||||
- switch.turn_off: Relay_2
|
|
||||||
- logger.log:
|
|
||||||
level: INFO
|
|
||||||
format: "BTN2: Single click (offline/test) -> Relay_2 OFF"
|
|
||||||
else:
|
|
||||||
- switch.turn_on: Relay_2
|
|
||||||
- logger.log:
|
|
||||||
level: INFO
|
|
||||||
format: "BTN2: Single click (offline/test) -> Relay_2 ON"
|
|
||||||
else:
|
|
||||||
- logger.log:
|
|
||||||
level: INFO
|
|
||||||
format: "BTN2: Single click ignored (override OFF)"
|
|
||||||
|
|
||||||
# GPIO04 for third button (only for KS-811-3 Triple)
|
|
||||||
- platform: gpio
|
|
||||||
pin:
|
|
||||||
number: GPIO4
|
|
||||||
mode: INPUT
|
|
||||||
inverted: true
|
|
||||||
name: "Button 3: ${switch_3_name}"
|
|
||||||
on_multi_click:
|
|
||||||
# --- Single short click ---
|
|
||||||
# Online (override ON, NOT test mode): set scene
|
|
||||||
# Offline/test (override ON): toggle Relay_3
|
|
||||||
- timing:
|
|
||||||
- ON for at most 0.5s
|
|
||||||
- OFF for at least 0.5s
|
|
||||||
then:
|
|
||||||
- if:
|
|
||||||
condition:
|
|
||||||
and:
|
|
||||||
- switch.is_on: ha_loss_override
|
|
||||||
- api.connected:
|
|
||||||
- not:
|
|
||||||
switch.is_on: ha_loss_test
|
|
||||||
then:
|
|
||||||
- homeassistant.service:
|
|
||||||
service: script.lounge_set_scene
|
|
||||||
data:
|
|
||||||
scene_name: "Reading On/Off"
|
|
||||||
- logger.log:
|
|
||||||
level: INFO
|
|
||||||
format: "BTN3: Single click (online) -> Set 'Reading On/Off' Scene"
|
|
||||||
else:
|
|
||||||
- if:
|
|
||||||
condition:
|
|
||||||
and:
|
|
||||||
- switch.is_on: ha_loss_override
|
|
||||||
- or:
|
|
||||||
- not:
|
|
||||||
api.connected:
|
|
||||||
- switch.is_on: ha_loss_test
|
|
||||||
then:
|
|
||||||
- if:
|
|
||||||
condition:
|
|
||||||
lambda: |-
|
|
||||||
return id(Relay_3).state;
|
|
||||||
then:
|
|
||||||
- switch.turn_off: Relay_3
|
|
||||||
- logger.log:
|
|
||||||
level: INFO
|
|
||||||
format: "BTN3: Single click (offline/test) -> Relay_3 OFF"
|
|
||||||
else:
|
|
||||||
- switch.turn_on: Relay_3
|
|
||||||
- logger.log:
|
|
||||||
level: INFO
|
|
||||||
format: "BTN3: Single click (offline/test) -> Relay_3 ON"
|
|
||||||
else:
|
|
||||||
- logger.log:
|
|
||||||
level: INFO
|
|
||||||
format: "BTN3: Single click ignored (override OFF)"
|
|
||||||
|
|
||||||
#:########################################################################################:#
|
|
||||||
# TEXT SENSOR COMPONENT:
|
|
||||||
# https://esphome.io/components/text_sensor/
|
|
||||||
#:########################################################################################:#
|
|
||||||
text_sensor:
|
|
||||||
- platform: homeassistant
|
|
||||||
id: ha_last_scene
|
|
||||||
entity_id: input_text.lounge_last_scene
|
|
||||||
on_value:
|
|
||||||
- logger.log:
|
|
||||||
level: DEBUG
|
|
||||||
format: "HA last scene now: %s"
|
|
||||||
args: ["x.c_str()"]
|
|
||||||
|
|
||||||
#:########################################################################################:#
|
|
||||||
# SWITCH COMPONENT:
|
|
||||||
# https://esphome.io/components/switch/
|
|
||||||
#:########################################################################################:#
|
|
||||||
switch:
|
|
||||||
# GPIO13 for KS-811 first button UNLESS it is KS-811-1 then it is GPIO12
|
|
||||||
- platform: gpio
|
|
||||||
name: "Relay 1: ${switch_1_name}"
|
|
||||||
pin: GPIO13
|
|
||||||
id: Relay_1
|
|
||||||
restore_mode: RESTORE_DEFAULT_ON
|
|
||||||
|
|
||||||
# GPIO12 for second relay (only for KS-811-2 Double or -3 Triple)
|
|
||||||
- platform: gpio
|
|
||||||
name: "Relay 2: ${switch_2_name}"
|
|
||||||
pin: GPIO12
|
|
||||||
id: Relay_2
|
|
||||||
restore_mode: RESTORE_DEFAULT_ON
|
|
||||||
|
|
||||||
# GPIO14 for third relay (only for KS-811-3 Triple)
|
|
||||||
- platform: gpio
|
|
||||||
name: "Relay 3: ${switch_3_name}"
|
|
||||||
pin: GPIO14
|
|
||||||
id: Relay_3
|
|
||||||
|
|
||||||
# HA Loss manual override (default ON; persists across restarts)
|
|
||||||
- platform: template
|
|
||||||
name: "HA Loss manual override"
|
|
||||||
id: ha_loss_override
|
|
||||||
optimistic: true
|
|
||||||
restore_mode: ALWAYS_ON
|
|
||||||
|
|
||||||
# HA Loss Test (default OFF): when ON, force "offline behavior" even if HA is connected
|
|
||||||
- platform: template
|
|
||||||
name: "HA Loss Test"
|
|
||||||
id: ha_loss_test
|
|
||||||
optimistic: true
|
|
||||||
restore_mode: ALWAYS_OFF
|
|
||||||
on_turn_on:
|
|
||||||
- logger.log:
|
|
||||||
level: WARN
|
|
||||||
format: "HA Loss Test turned ON: forcing offline/test behavior for buttons"
|
|
||||||
on_turn_off:
|
|
||||||
- logger.log:
|
|
||||||
level: INFO
|
|
||||||
format: "HA Loss Test turned OFF: forcing Relay_1 and Relay_2 ON, then restoring last scene in 30s"
|
|
||||||
- switch.turn_on: Relay_1
|
|
||||||
- switch.turn_on: Relay_2
|
|
||||||
- delay: 30s
|
|
||||||
- if:
|
|
||||||
condition:
|
|
||||||
and:
|
|
||||||
- api.connected:
|
|
||||||
- lambda: |-
|
|
||||||
auto s = id(ha_last_scene).state;
|
|
||||||
return !(s.empty() || s == "unknown" || s == "unavailable");
|
|
||||||
then:
|
|
||||||
- homeassistant.service:
|
|
||||||
service: script.lounge_set_scene
|
|
||||||
data:
|
|
||||||
scene_name: !lambda |-
|
|
||||||
return id(ha_last_scene).state;
|
|
||||||
- logger.log:
|
|
||||||
level: INFO
|
|
||||||
format: "HA Loss Test OFF: restored last scene: %s"
|
|
||||||
args: ['id(ha_last_scene).state.c_str()']
|
|
||||||
else:
|
|
||||||
- logger.log:
|
|
||||||
level: WARN
|
|
||||||
format: "HA Loss Test OFF: cannot restore scene (API not connected or last scene unknown)"
|
|
||||||
@@ -1,477 +0,0 @@
|
|||||||
#############################################
|
|
||||||
#############################################
|
|
||||||
#
|
|
||||||
# ESP Home
|
|
||||||
# NFC Tag Reader for Home Assistant
|
|
||||||
#
|
|
||||||
#############################################
|
|
||||||
#############################################
|
|
||||||
# Project Link:
|
|
||||||
# https://github.com/adonno/tagreader
|
|
||||||
#############################################
|
|
||||||
# Remember to set the switches on the PN532
|
|
||||||
# for i2C, Switch1: On (up), Switch2: Off (down)
|
|
||||||
#############################################
|
|
||||||
|
|
||||||
|
|
||||||
#############################################
|
|
||||||
# Substitution Text
|
|
||||||
#############################################
|
|
||||||
substitutions:
|
|
||||||
name: esp-nfcaccess1-33ad74
|
|
||||||
friendly_name: TagReader
|
|
||||||
|
|
||||||
#############################################
|
|
||||||
# Inport the project from Github
|
|
||||||
#############################################
|
|
||||||
#dashboard_import:
|
|
||||||
# package_import_url: github://adonno/tagreader/tagreader.yaml
|
|
||||||
|
|
||||||
#############################################
|
|
||||||
# Use Wifi
|
|
||||||
#############################################
|
|
||||||
wifi:
|
|
||||||
ssid: !secret wifi_ssid
|
|
||||||
password: !secret wifi_password
|
|
||||||
# Details for fallback hotspot (captive portal)
|
|
||||||
# in case wifi connection fails
|
|
||||||
ap:
|
|
||||||
ssid: "${name} Hotspot"
|
|
||||||
password: !secret fallback_ap_password
|
|
||||||
|
|
||||||
#############################################
|
|
||||||
# Improv
|
|
||||||
# Use the open Improv standard for configuring Wi-Fi on the
|
|
||||||
# device by using a serial connection to the device, eg. USB.
|
|
||||||
#############################################
|
|
||||||
improv_serial:
|
|
||||||
|
|
||||||
#############################################
|
|
||||||
# Fallback captive portal
|
|
||||||
# Enable the captive portal for inital WiFi setup
|
|
||||||
#############################################
|
|
||||||
captive_portal:
|
|
||||||
|
|
||||||
#############################################
|
|
||||||
# Enable Over the Air Update Capability
|
|
||||||
# Safe mode will detect boot loops
|
|
||||||
#############################################
|
|
||||||
ota:
|
|
||||||
safe_mode: true # Avoid boot loops
|
|
||||||
password: !secret esp-nfcaccess1-33ad74_ota_pass
|
|
||||||
|
|
||||||
#############################################
|
|
||||||
# ESPHome Logging Enable
|
|
||||||
#############################################
|
|
||||||
logger:
|
|
||||||
# baud_rate: 0 # Set baud rate to 0 if using the UART for soething else
|
|
||||||
# level: VERY_VERBOSE
|
|
||||||
# level: VERBOSE
|
|
||||||
|
|
||||||
#############################################
|
|
||||||
# Web Portal for display and monitoring
|
|
||||||
#############################################
|
|
||||||
#web_server:
|
|
||||||
# port: 80
|
|
||||||
# auth:
|
|
||||||
# username: !secret web_server_username
|
|
||||||
# password: !secret web_server_password
|
|
||||||
|
|
||||||
########################################
|
|
||||||
# Specific board for ESPHome device
|
|
||||||
########################################
|
|
||||||
esp8266:
|
|
||||||
board: d1_mini
|
|
||||||
|
|
||||||
#############################################
|
|
||||||
# ESPHome
|
|
||||||
#############################################
|
|
||||||
esphome:
|
|
||||||
name: ${name}
|
|
||||||
# Automatically add the mac address to the name
|
|
||||||
# so you can use a single firmware for all devices
|
|
||||||
name_add_mac_suffix: false
|
|
||||||
# This will allow for (future) project identification,
|
|
||||||
# configuration and updates.
|
|
||||||
project:
|
|
||||||
name: adonno.tag_reader
|
|
||||||
version: "1.4"
|
|
||||||
# If buzzer is enabled, notify on api connection success
|
|
||||||
on_boot:
|
|
||||||
priority: -10
|
|
||||||
then:
|
|
||||||
- wait_until:
|
|
||||||
api.connected:
|
|
||||||
- logger.log: API is connected!
|
|
||||||
- rtttl.play: "success:d=24,o=5,b=100:c,g,b"
|
|
||||||
- light.turn_on:
|
|
||||||
id: activity_led
|
|
||||||
brightness: 100%
|
|
||||||
red: 0%
|
|
||||||
green: 0%
|
|
||||||
blue: 100%
|
|
||||||
flash_length: 500ms
|
|
||||||
- switch.turn_on: buzzer_enabled
|
|
||||||
- switch.turn_on: led_enabled
|
|
||||||
|
|
||||||
|
|
||||||
#############################################
|
|
||||||
# Global variables
|
|
||||||
#############################################
|
|
||||||
globals:
|
|
||||||
- id: source
|
|
||||||
type: std::string
|
|
||||||
- id: url
|
|
||||||
type: std::string
|
|
||||||
- id: info
|
|
||||||
type: std::string
|
|
||||||
|
|
||||||
|
|
||||||
#############################################
|
|
||||||
#############################################
|
|
||||||
# MAIN SENSORS
|
|
||||||
#############################################
|
|
||||||
#############################################
|
|
||||||
|
|
||||||
#############################################
|
|
||||||
# Switches
|
|
||||||
# Define switches to control LED and buzzer from HA
|
|
||||||
#############################################
|
|
||||||
switch:
|
|
||||||
- platform: template
|
|
||||||
name: "${friendly_name} Buzzer Enabled"
|
|
||||||
id: buzzer_enabled
|
|
||||||
icon: mdi:volume-high
|
|
||||||
optimistic: true
|
|
||||||
restore_state: true
|
|
||||||
entity_category: config
|
|
||||||
- platform: template
|
|
||||||
name: "${friendly_name} LED enabled"
|
|
||||||
id: led_enabled
|
|
||||||
icon: mdi:alarm-light-outline
|
|
||||||
optimistic: true
|
|
||||||
restore_state: true
|
|
||||||
entity_category: config
|
|
||||||
|
|
||||||
#############################################
|
|
||||||
# Buttons
|
|
||||||
# Define buttons for writing tags via HA
|
|
||||||
#############################################
|
|
||||||
button:
|
|
||||||
- platform: template
|
|
||||||
name: Write Tag Random
|
|
||||||
id: write_tag_random
|
|
||||||
# Optional variables:
|
|
||||||
icon: "mdi:pencil-box"
|
|
||||||
on_press:
|
|
||||||
then:
|
|
||||||
- light.turn_on:
|
|
||||||
id: activity_led
|
|
||||||
brightness: 100%
|
|
||||||
red: 100%
|
|
||||||
green: 0%
|
|
||||||
blue: 100%
|
|
||||||
- lambda: |-
|
|
||||||
static const char alphanum[] = "0123456789abcdef";
|
|
||||||
std::string uri = "https://www.home-assistant.io/tag/";
|
|
||||||
for (int i = 0; i < 8; i++)
|
|
||||||
uri += alphanum[random_uint32() % (sizeof(alphanum) - 1)];
|
|
||||||
uri += "-";
|
|
||||||
for (int j = 0; j < 3; j++) {
|
|
||||||
for (int i = 0; i < 4; i++)
|
|
||||||
uri += alphanum[random_uint32() % (sizeof(alphanum) - 1)];
|
|
||||||
uri += "-";
|
|
||||||
}
|
|
||||||
for (int i = 0; i < 12; i++)
|
|
||||||
uri += alphanum[random_uint32() % (sizeof(alphanum) - 1)];
|
|
||||||
auto message = new nfc::NdefMessage();
|
|
||||||
message->add_uri_record(uri);
|
|
||||||
ESP_LOGD("tagreader", "Writing payload: %s", uri.c_str());
|
|
||||||
id(pn532_board).write_mode(message);
|
|
||||||
- rtttl.play: "write:d=24,o=5,b=100:b"
|
|
||||||
- wait_until:
|
|
||||||
not:
|
|
||||||
pn532.is_writing:
|
|
||||||
- light.turn_off:
|
|
||||||
id: activity_led
|
|
||||||
- rtttl.play: "write:d=24,o=5,b=100:b,b"
|
|
||||||
|
|
||||||
- platform: template
|
|
||||||
name: Clean Tag
|
|
||||||
id: clean_tag
|
|
||||||
icon: "mdi:nfc-variant-off"
|
|
||||||
on_press:
|
|
||||||
then:
|
|
||||||
- light.turn_on:
|
|
||||||
id: activity_led
|
|
||||||
brightness: 100%
|
|
||||||
red: 100%
|
|
||||||
green: 64.7%
|
|
||||||
blue: 0%
|
|
||||||
- lambda: 'id(pn532_board).clean_mode();'
|
|
||||||
- rtttl.play: "write:d=24,o=5,b=100:b"
|
|
||||||
- wait_until:
|
|
||||||
not:
|
|
||||||
pn532.is_writing:
|
|
||||||
- light.turn_off:
|
|
||||||
id: activity_led
|
|
||||||
- rtttl.play: "write:d=24,o=5,b=100:b,b"
|
|
||||||
|
|
||||||
- platform: template
|
|
||||||
name: Cancel writing
|
|
||||||
id: cancel_writing
|
|
||||||
icon: "mdi:pencil-off"
|
|
||||||
on_press:
|
|
||||||
then:
|
|
||||||
- lambda: 'id(pn532_board).read_mode();'
|
|
||||||
- light.turn_off:
|
|
||||||
id: activity_led
|
|
||||||
- rtttl.play: "write:d=24,o=5,b=100:b,b"
|
|
||||||
|
|
||||||
- platform: restart
|
|
||||||
name: "${friendly_name} Restart"
|
|
||||||
entity_category: config
|
|
||||||
|
|
||||||
#############################################
|
|
||||||
# HA API
|
|
||||||
# Enable Home Assistant API
|
|
||||||
#############################################
|
|
||||||
api:
|
|
||||||
encryption:
|
|
||||||
key: "K2H5vBNv8NLegc3RmwTQVFV7ULtba2DWDOlABmXmGBM="
|
|
||||||
services:
|
|
||||||
- service: rfidreader_tag_ok
|
|
||||||
then:
|
|
||||||
- rtttl.play: "beep:d=16,o=5,b=100:b"
|
|
||||||
- service: rfidreader_tag_ko
|
|
||||||
then:
|
|
||||||
- rtttl.play: "beep:d=8,o=5,b=100:b"
|
|
||||||
- service: play_rtttl
|
|
||||||
variables:
|
|
||||||
song_str: string
|
|
||||||
then:
|
|
||||||
- rtttl.play: !lambda 'return song_str;'
|
|
||||||
- service: write_tag_id
|
|
||||||
variables:
|
|
||||||
tag_id: string
|
|
||||||
then:
|
|
||||||
- light.turn_on:
|
|
||||||
id: activity_led
|
|
||||||
brightness: 100%
|
|
||||||
red: 100%
|
|
||||||
green: 0%
|
|
||||||
blue: 0%
|
|
||||||
- lambda: |-
|
|
||||||
auto message = new nfc::NdefMessage();
|
|
||||||
std::string uri = "https://www.home-assistant.io/tag/";
|
|
||||||
uri += tag_id;
|
|
||||||
message->add_uri_record(uri);
|
|
||||||
id(pn532_board).write_mode(message);
|
|
||||||
- rtttl.play: "write:d=24,o=5,b=100:b"
|
|
||||||
- wait_until:
|
|
||||||
not:
|
|
||||||
pn532.is_writing:
|
|
||||||
- light.turn_off:
|
|
||||||
id: activity_led
|
|
||||||
- rtttl.play: "write:d=24,o=5,b=100:b,b"
|
|
||||||
- service: write_music_tag
|
|
||||||
variables:
|
|
||||||
music_url: string
|
|
||||||
music_info: string
|
|
||||||
then:
|
|
||||||
- light.turn_on:
|
|
||||||
id: activity_led
|
|
||||||
brightness: 100%
|
|
||||||
red: 100%
|
|
||||||
green: 0%
|
|
||||||
blue: 0%
|
|
||||||
- lambda: |-
|
|
||||||
auto message = new nfc::NdefMessage();
|
|
||||||
std::string uri = "";
|
|
||||||
std::string text = "";
|
|
||||||
uri += music_url;
|
|
||||||
text += music_info;
|
|
||||||
if ( music_url != "" ) {
|
|
||||||
message->add_uri_record(uri);
|
|
||||||
}
|
|
||||||
if ( music_info != "" ) {
|
|
||||||
message->add_text_record(text);
|
|
||||||
}
|
|
||||||
id(pn532_board).write_mode(message);
|
|
||||||
- rtttl.play: "write:d=24,o=5,b=100:b"
|
|
||||||
- wait_until:
|
|
||||||
not:
|
|
||||||
pn532.is_writing:
|
|
||||||
- light.turn_off:
|
|
||||||
id: activity_led
|
|
||||||
- rtttl.play: "write:d=24,o=5,b=100:b,b"
|
|
||||||
|
|
||||||
#############################################
|
|
||||||
# i2C bus
|
|
||||||
#############################################
|
|
||||||
i2c:
|
|
||||||
scan: False
|
|
||||||
frequency: 400kHz
|
|
||||||
|
|
||||||
#############################################
|
|
||||||
# NFC Reader
|
|
||||||
#############################################
|
|
||||||
pn532_i2c:
|
|
||||||
id: pn532_board
|
|
||||||
on_tag:
|
|
||||||
then:
|
|
||||||
- if:
|
|
||||||
condition:
|
|
||||||
switch.is_on: led_enabled
|
|
||||||
then:
|
|
||||||
- light.turn_on:
|
|
||||||
id: activity_led
|
|
||||||
brightness: 100%
|
|
||||||
red: 0%
|
|
||||||
green: 100%
|
|
||||||
blue: 0%
|
|
||||||
flash_length: 500ms
|
|
||||||
|
|
||||||
- delay: 0.15s #to fix slow component
|
|
||||||
|
|
||||||
- lambda: |-
|
|
||||||
id(source)="";
|
|
||||||
id(url)="";
|
|
||||||
id(info)="";
|
|
||||||
if (tag.has_ndef_message()) {
|
|
||||||
auto message = tag.get_ndef_message();
|
|
||||||
auto records = message->get_records();
|
|
||||||
for (auto &record : records) {
|
|
||||||
std::string payload = record->get_payload();
|
|
||||||
std::string type = record->get_type();
|
|
||||||
size_t hass = payload.find("https://www.home-assistant.io/tag/");
|
|
||||||
size_t applemusic = payload.find("https://music.apple.com");
|
|
||||||
size_t spotify = payload.find("https://open.spotify.com");
|
|
||||||
size_t sonos = payload.find("sonos-2://");
|
|
||||||
|
|
||||||
if (type == "U" and hass != std::string::npos ) {
|
|
||||||
ESP_LOGD("tagreader", "Found Home Assistant tag NDEF");
|
|
||||||
id(source)="hass";
|
|
||||||
id(url)=payload;
|
|
||||||
id(info)=payload.substr(hass + 34);
|
|
||||||
}
|
|
||||||
else if (type == "U" and applemusic != std::string::npos ) {
|
|
||||||
ESP_LOGD("tagreader", "Found Apple Music tag NDEF");
|
|
||||||
id(source)="amusic";
|
|
||||||
id(url)=payload;
|
|
||||||
}
|
|
||||||
else if (type == "U" and spotify != std::string::npos ) {
|
|
||||||
ESP_LOGD("tagreader", "Found Spotify tag NDEF");
|
|
||||||
id(source)="spotify";
|
|
||||||
id(url)=payload;
|
|
||||||
}
|
|
||||||
else if (type == "U" and sonos != std::string::npos ) {
|
|
||||||
ESP_LOGD("tagreader", "Found Sonos app tag NDEF");
|
|
||||||
id(source)="sonos";
|
|
||||||
id(url)=payload;
|
|
||||||
}
|
|
||||||
else if (type == "T" ) {
|
|
||||||
ESP_LOGD("tagreader", "Found music info tag NDEF");
|
|
||||||
id(info)=payload;
|
|
||||||
}
|
|
||||||
else if ( id(source)=="" ) {
|
|
||||||
id(source)="uid";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
id(source)="uid";
|
|
||||||
}
|
|
||||||
|
|
||||||
- if:
|
|
||||||
condition:
|
|
||||||
lambda: 'return ( id(source)=="uid" );'
|
|
||||||
then:
|
|
||||||
- homeassistant.tag_scanned: !lambda |-
|
|
||||||
ESP_LOGD("tagreader", "No HA NDEF, using UID");
|
|
||||||
return x;
|
|
||||||
else:
|
|
||||||
- if:
|
|
||||||
condition:
|
|
||||||
lambda: 'return ( id(source)=="hass" );'
|
|
||||||
then:
|
|
||||||
- homeassistant.tag_scanned: !lambda 'return id(info);'
|
|
||||||
else:
|
|
||||||
- homeassistant.event:
|
|
||||||
event: esphome.music_tag
|
|
||||||
data:
|
|
||||||
reader: !lambda |-
|
|
||||||
return App.get_name().c_str();
|
|
||||||
source: !lambda |-
|
|
||||||
return id(source);
|
|
||||||
url: !lambda |-
|
|
||||||
return id(url);
|
|
||||||
info: !lambda |-
|
|
||||||
return id(info);
|
|
||||||
|
|
||||||
- if:
|
|
||||||
condition:
|
|
||||||
switch.is_on: buzzer_enabled
|
|
||||||
then:
|
|
||||||
- rtttl.play: "success:d=24,o=5,b=100:c,g,b"
|
|
||||||
on_tag_removed:
|
|
||||||
then:
|
|
||||||
- homeassistant.event:
|
|
||||||
event: esphome.tag_removed
|
|
||||||
|
|
||||||
#############################################
|
|
||||||
# OUTPUTS
|
|
||||||
#############################################
|
|
||||||
# Define the buzzer output
|
|
||||||
output:
|
|
||||||
- platform: esp8266_pwm
|
|
||||||
pin: D7
|
|
||||||
id: buzzer
|
|
||||||
|
|
||||||
#############################################
|
|
||||||
# BINARY SENSORS
|
|
||||||
#############################################
|
|
||||||
binary_sensor:
|
|
||||||
- platform: status
|
|
||||||
name: "${friendly_name} Status"
|
|
||||||
entity_category: diagnostic
|
|
||||||
|
|
||||||
#############################################
|
|
||||||
# TEXT SENSORS
|
|
||||||
#############################################
|
|
||||||
text_sensor:
|
|
||||||
- platform: version
|
|
||||||
hide_timestamp: true
|
|
||||||
name: "${friendly_name} ESPHome Version"
|
|
||||||
entity_category: diagnostic
|
|
||||||
- platform: wifi_info
|
|
||||||
ip_address:
|
|
||||||
name: "${friendly_name} IP Address"
|
|
||||||
icon: mdi:wifi
|
|
||||||
entity_category: diagnostic
|
|
||||||
ssid:
|
|
||||||
name: "${friendly_name} Connected SSID"
|
|
||||||
icon: mdi:wifi-strength-2
|
|
||||||
entity_category: diagnostic
|
|
||||||
|
|
||||||
#############################################
|
|
||||||
# Ring Tone Text Transfer Language for melody
|
|
||||||
#############################################
|
|
||||||
# Define buzzer as output for RTTTL
|
|
||||||
rtttl:
|
|
||||||
output: buzzer
|
|
||||||
|
|
||||||
#############################################
|
|
||||||
# LED
|
|
||||||
#############################################
|
|
||||||
# Configure LED
|
|
||||||
light:
|
|
||||||
- platform: neopixelbus
|
|
||||||
variant: WS2812
|
|
||||||
pin: D8
|
|
||||||
num_leds: 1
|
|
||||||
flash_transition_length: 500ms
|
|
||||||
type: GRB
|
|
||||||
id: activity_led
|
|
||||||
name: "${friendly_name} LED"
|
|
||||||
restore_mode: ALWAYS_OFF
|
|
||||||
@@ -191,7 +191,7 @@ globals:
|
|||||||
- id: restore_mode
|
- id: restore_mode
|
||||||
type: int
|
type: int
|
||||||
restore_value: yes
|
restore_value: yes
|
||||||
initial_value: "2"
|
initial_value: "0"
|
||||||
|
|
||||||
#:########################################################################################:#
|
#:########################################################################################:#
|
||||||
# SELECT COMPONENT:
|
# SELECT COMPONENT:
|
||||||
|
|||||||
@@ -21,11 +21,11 @@ substitutions:
|
|||||||
#friendly_name: "Main Dishwashers Power"
|
#friendly_name: "Main Dishwashers Power"
|
||||||
friendly_name: "Spare1 Athomv1"
|
friendly_name: "Spare1 Athomv1"
|
||||||
description_comment: "Main Dishwashers Power Monitor :: Athom Smart Plug Power Monitor V1"
|
description_comment: "Main Dishwashers Power Monitor :: Athom Smart Plug Power Monitor V1"
|
||||||
device_area: "Kitchen" # Allows ESP device to be automatically linked to an 'Area' in Home Assistant.
|
device_area: "Kitchen" # Allows ESP device to be automatically linked to an 'Area' in Home Assistant.
|
||||||
|
|
||||||
# Project Naming
|
# Project Naming
|
||||||
project_name: "Athom Technology.Smart Plug V1" # Project Details
|
project_name: "Athom Technology.Smart Plug V1" # Project Details
|
||||||
project_version: "v1.1" # Project V denotes release of yaml file, allowing checking of deployed vs latest version
|
project_version: "v1.1" # Project V denotes release of yaml file, allowing checking of deployed vs latest version
|
||||||
|
|
||||||
# Passwords
|
# Passwords
|
||||||
api_key: !secret esp-api_key # unfortunately you can't use substitutions inside secrets names
|
api_key: !secret esp-api_key # unfortunately you can't use substitutions inside secrets names
|
||||||
@@ -38,7 +38,7 @@ substitutions:
|
|||||||
|
|
||||||
# Device Settings
|
# Device Settings
|
||||||
relay_icon: "mdi:dishwasher"
|
relay_icon: "mdi:dishwasher"
|
||||||
current_limit : "10" # Current Limit in Amps. AU Plug = 10. IL, BR, EU, UK, US Plug = 16.
|
current_limit: "10" # Current Limit in Amps. AU Plug = 10. IL, BR, EU, UK, US Plug = 16.
|
||||||
|
|
||||||
##########################################################################################
|
##########################################################################################
|
||||||
# PACKAGES: Included Common Packages
|
# PACKAGES: Included Common Packages
|
||||||
@@ -90,18 +90,13 @@ esphome:
|
|||||||
project:
|
project:
|
||||||
name: "${project_name}"
|
name: "${project_name}"
|
||||||
version: "${project_version}"
|
version: "${project_version}"
|
||||||
on_boot:
|
|
||||||
priority: 200
|
|
||||||
then:
|
|
||||||
- switch.turn_on: "relay"
|
|
||||||
|
|
||||||
##########################################################################################
|
##########################################################################################
|
||||||
# ESP Platform and Framework
|
# ESP Platform and Framework
|
||||||
# https://esphome.io/components/esp32.html
|
# https://esphome.io/components/esp32.html
|
||||||
##########################################################################################
|
##########################################################################################
|
||||||
esp8266:
|
esp8266:
|
||||||
board: esp8285
|
board: esp8285
|
||||||
restore_from_flash: True # mainly for calculating cumulative energy, but not that important here
|
restore_from_flash: True # mainly for calculating cumulative energy, but not that important here
|
||||||
|
|
||||||
preferences:
|
preferences:
|
||||||
flash_write_interval: 5min
|
flash_write_interval: 5min
|
||||||
@@ -117,7 +112,7 @@ mdns:
|
|||||||
# https://esphome.io/components/logger.html
|
# https://esphome.io/components/logger.html
|
||||||
##########################################################################################
|
##########################################################################################
|
||||||
logger:
|
logger:
|
||||||
level: "${log_level}" #INFO Level suggested, or DEBUG for testing
|
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)
|
#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
|
#esp8266_store_log_strings_in_flash: false
|
||||||
#tx_buffer_size: 64
|
#tx_buffer_size: 64
|
||||||
@@ -149,8 +144,8 @@ binary_sensor:
|
|||||||
- min_length: 20ms
|
- min_length: 20ms
|
||||||
max_length: 500ms
|
max_length: 500ms
|
||||||
then:
|
then:
|
||||||
- switch.toggle:
|
- switch.toggle:
|
||||||
id: relay
|
id: relay
|
||||||
|
|
||||||
- platform: template
|
- platform: template
|
||||||
name: "Relay Status"
|
name: "Relay Status"
|
||||||
@@ -166,7 +161,6 @@ switch:
|
|||||||
name: "Power Output"
|
name: "Power Output"
|
||||||
pin: GPIO14
|
pin: GPIO14
|
||||||
id: relay
|
id: relay
|
||||||
restore_mode: RESTORE_DEFAULT_ON # Ensures the relay is restored (or off) at boot
|
restore_mode: RESTORE_DEFAULT_OFF # Restore previous state, default off
|
||||||
#internal: true # Hides the switch from Home Assistant
|
#internal: true # Hides the switch from Home Assistant
|
||||||
icon: "${relay_icon}"
|
icon: "${relay_icon}"
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
basement_lights:
|
|
||||||
name: Basement Lights
|
|
||||||
entities:
|
|
||||||
- switch.esp_officelights_relay_1_nighttime_lights
|
|
||||||
- switch.esp_officelights_relay_2_daytime_lights
|
|
||||||
- switch.esp_underhouselights_underhouse_entrance_lights
|
|
||||||
- switch.esp_underhouselights_underhouse_storage_lights
|
|
||||||
- switch.esp_laundrylights_relay_1_laundry_lights
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
family_away:
|
|
||||||
name: All Family Members Away
|
|
||||||
entities:
|
|
||||||
- !secret person_1
|
|
||||||
- !secret person_2
|
|
||||||
- !secret person_3
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
|
|
||||||
@@ -9,7 +9,7 @@
|
|||||||
# jobs_tracker_jobs.yaml
|
# jobs_tracker_jobs.yaml
|
||||||
#
|
#
|
||||||
# VERSION:
|
# VERSION:
|
||||||
# V2.3 2026-07-20
|
# V2.4 2026-07-21
|
||||||
#
|
#
|
||||||
# PURPOSE:
|
# PURPOSE:
|
||||||
# Defines the job registry, MQTT Discovery entities, completion triggers and
|
# Defines the job registry, MQTT Discovery entities, completion triggers and
|
||||||
@@ -41,6 +41,12 @@
|
|||||||
# Complete
|
# Complete
|
||||||
#
|
#
|
||||||
# VERSION HISTORY:
|
# VERSION HISTORY:
|
||||||
|
# V2.4 2026-07-21
|
||||||
|
# - Startup now waits for every retained job timestamp before refreshing.
|
||||||
|
# - Minute refreshes skip while retained MQTT state is not ready.
|
||||||
|
# - Restored stale event timestamps are no longer accepted as completions.
|
||||||
|
# - Discovery device firmware version updated to 2.4.
|
||||||
|
#
|
||||||
# V2.3 2026-07-20
|
# V2.3 2026-07-20
|
||||||
# - All completion methods now obey the configured completion window.
|
# - All completion methods now obey the configured completion window.
|
||||||
# - Physical completion entities, Home Assistant discovery buttons and direct
|
# - Physical completion entities, Home Assistant discovery buttons and direct
|
||||||
@@ -360,7 +366,7 @@ script:
|
|||||||
device_name: "Jobs Tracker"
|
device_name: "Jobs Tracker"
|
||||||
device_manufacturer: "View Road Home Assistant"
|
device_manufacturer: "View Road Home Assistant"
|
||||||
device_model: "Shared household jobs tracker"
|
device_model: "Shared household jobs tracker"
|
||||||
device_sw_version: "2.3"
|
device_sw_version: "2.4"
|
||||||
|
|
||||||
value_template_state: >-
|
value_template_state: >-
|
||||||
{% raw %}{{ value_json.state | default('unknown') }}{% endraw %}
|
{% raw %}{{ value_json.state | default('unknown') }}{% endraw %}
|
||||||
@@ -697,10 +703,23 @@ automation:
|
|||||||
actions:
|
actions:
|
||||||
- action: script.jobs_tracker_publish_discovery
|
- action: script.jobs_tracker_publish_discovery
|
||||||
|
|
||||||
# Give Home Assistant a moment to process MQTT Discovery.
|
- variables:
|
||||||
- delay: "00:00:10"
|
jobs_tracker_jobs: *jobs_tracker_jobs
|
||||||
|
|
||||||
# Force retained state payloads onto each job topic.
|
# Do not refresh until every retained timestamp has returned from MQTT.
|
||||||
|
# Aborting on timeout protects valid retained data from being replaced by 0.
|
||||||
|
- wait_template: >-
|
||||||
|
{% set ns = namespace(ready=true) %}
|
||||||
|
{% for job in jobs_tracker_jobs %}
|
||||||
|
{% if not is_number(state_attr(job.state_entity, 'last_completed_ts')) %}
|
||||||
|
{% set ns.ready = false %}
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
{{ ns.ready }}
|
||||||
|
timeout: "00:01:30"
|
||||||
|
continue_on_timeout: false
|
||||||
|
|
||||||
|
# Recalculate each job only after retained state is confirmed ready.
|
||||||
- action: script.jobs_tracker_dispatch
|
- action: script.jobs_tracker_dispatch
|
||||||
data:
|
data:
|
||||||
update_all: true
|
update_all: true
|
||||||
@@ -711,25 +730,26 @@ automation:
|
|||||||
mode: single
|
mode: single
|
||||||
|
|
||||||
triggers:
|
triggers:
|
||||||
- trigger: homeassistant
|
|
||||||
event: start
|
|
||||||
id: ha_start
|
|
||||||
|
|
||||||
- trigger: time_pattern
|
- trigger: time_pattern
|
||||||
minutes: "/1"
|
minutes: "/1"
|
||||||
id: tick
|
id: tick
|
||||||
|
|
||||||
|
variables:
|
||||||
|
jobs_tracker_jobs: *jobs_tracker_jobs
|
||||||
|
|
||||||
|
conditions:
|
||||||
|
# Skip the minute tick until every retained timestamp is available.
|
||||||
|
- condition: template
|
||||||
|
value_template: >-
|
||||||
|
{% set ns = namespace(ready=true) %}
|
||||||
|
{% for job in jobs_tracker_jobs %}
|
||||||
|
{% if not is_number(state_attr(job.state_entity, 'last_completed_ts')) %}
|
||||||
|
{% set ns.ready = false %}
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
{{ ns.ready }}
|
||||||
|
|
||||||
actions:
|
actions:
|
||||||
- if:
|
|
||||||
- condition: template
|
|
||||||
value_template: "{{ trigger.id == 'ha_start' }}"
|
|
||||||
then:
|
|
||||||
- action: script.jobs_tracker_publish_discovery
|
|
||||||
|
|
||||||
# Let retained MQTT Discovery and job state messages restore before
|
|
||||||
# the first refresh.
|
|
||||||
- delay: "00:01:30"
|
|
||||||
|
|
||||||
- action: script.jobs_tracker_dispatch
|
- action: script.jobs_tracker_dispatch
|
||||||
data:
|
data:
|
||||||
update_all: true
|
update_all: true
|
||||||
@@ -747,9 +767,18 @@ automation:
|
|||||||
conditions:
|
conditions:
|
||||||
- condition: template
|
- condition: template
|
||||||
value_template: >-
|
value_template: >-
|
||||||
|
{% set valid_state = trigger.to_state is not none
|
||||||
|
and trigger.to_state.state not in ['unknown', 'unavailable'] %}
|
||||||
|
{% set event_entity = trigger.entity_id.startswith('event.') %}
|
||||||
|
{% set event_ts = as_timestamp(trigger.to_state.state, 0)
|
||||||
|
if valid_state and event_entity else 0 %}
|
||||||
|
{% set event_age = as_timestamp(now()) - event_ts %}
|
||||||
{{
|
{{
|
||||||
trigger.to_state is not none
|
valid_state
|
||||||
and trigger.to_state.state not in ['unknown', 'unavailable']
|
and (
|
||||||
|
not event_entity
|
||||||
|
or (event_ts > 0 and event_age >= 0 and event_age <= 30)
|
||||||
|
)
|
||||||
}}
|
}}
|
||||||
|
|
||||||
actions:
|
actions:
|
||||||
|
|||||||
@@ -1,3 +1,27 @@
|
|||||||
|
#:########################################################################################:#
|
||||||
|
# Package: Central Stairway Two-Way Synchronization
|
||||||
|
# File: Lighting_2Way_Sync/central_stairway_2way_sync.yaml
|
||||||
|
# Version: v1.1.0
|
||||||
|
# Date: 2026-07-22
|
||||||
|
#
|
||||||
|
# Purpose:
|
||||||
|
# Keeps the upper and lower controls for the main stair lights and stair footer
|
||||||
|
# lights synchronized through Home Assistant.
|
||||||
|
#
|
||||||
|
# Behaviour:
|
||||||
|
# - Synchronizes each two-member relay pair in both directions.
|
||||||
|
# - Synchronizes state after Home Assistant starts, following a five-second delay.
|
||||||
|
# - Leaves direct device-level MQTT synchronization to the ESPHome devices.
|
||||||
|
#
|
||||||
|
# Dependency:
|
||||||
|
# - Blueprint: zorruno/two_three_way_sync.yaml
|
||||||
|
#
|
||||||
|
# Version History:
|
||||||
|
# - v1.1.0 (2026-07-22): Removed the superseded lounge-middle relay and retained
|
||||||
|
# two active relays in each synchronization pair.
|
||||||
|
# - v1.0.0 (2026-06-22): Initial central stairway synchronization package.
|
||||||
|
#:########################################################################################:#
|
||||||
|
|
||||||
automation:
|
automation:
|
||||||
# Stair Lights Top
|
# Stair Lights Top
|
||||||
- id: stair_lights_top_2way_sync
|
- id: stair_lights_top_2way_sync
|
||||||
@@ -9,7 +33,6 @@ automation:
|
|||||||
members:
|
members:
|
||||||
- switch.esp_centralstairs_top_relay_1_main_stair_lights
|
- switch.esp_centralstairs_top_relay_1_main_stair_lights
|
||||||
- switch.esp_centralstairs_bottom_relay_1_main_stair_lights_lower
|
- switch.esp_centralstairs_bottom_relay_1_main_stair_lights_lower
|
||||||
- switch.esp_loungemiddleswitch_relay_3_downlights_north
|
|
||||||
sync_on_start: true
|
sync_on_start: true
|
||||||
startup_delay: 5
|
startup_delay: 5
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,24 @@
|
|||||||
|
#:########################################################################################:#
|
||||||
|
# Package: Garage Lighting Two-Way Synchronization
|
||||||
|
# File: Lighting_2Way_Sync/garage_lights_2way_sync.yaml
|
||||||
|
# Version: v1.0.0
|
||||||
|
# Date: 2026-06-22
|
||||||
|
#
|
||||||
|
# Purpose:
|
||||||
|
# Keeps the paired controls for the garage-entry corridor/outside lighting and
|
||||||
|
# the internal garage lighting synchronized through Home Assistant.
|
||||||
|
#
|
||||||
|
# Behaviour:
|
||||||
|
# - Synchronizes each two-member relay pair in both directions.
|
||||||
|
# - Synchronizes state after Home Assistant starts, following a five-second delay.
|
||||||
|
#
|
||||||
|
# Dependency:
|
||||||
|
# - Blueprint: zorruno/two_three_way_sync.yaml
|
||||||
|
#
|
||||||
|
# Version History:
|
||||||
|
# - v1.0.0 (2026-06-22): Initial garage lighting synchronization package.
|
||||||
|
#:########################################################################################:#
|
||||||
|
|
||||||
automation:
|
automation:
|
||||||
# Two lights and two switches for these corridor lights
|
# Two lights and two switches for these corridor lights
|
||||||
- id: garage_entry_outside_2way_sync
|
- id: garage_entry_outside_2way_sync
|
||||||
|
|||||||
@@ -183,7 +183,3 @@ automation:
|
|||||||
- service: input_boolean.turn_off
|
- service: input_boolean.turn_off
|
||||||
target:
|
target:
|
||||||
entity_id: input_boolean.poollights_mode_switch
|
entity_id: input_boolean.poollights_mode_switch
|
||||||
|
|
||||||
- service: input_boolean.turn_on
|
|
||||||
target:
|
|
||||||
entity_id: input_boolean.timer_light_midnight_pending
|
|
||||||
|
|||||||
@@ -152,6 +152,3 @@ automation:
|
|||||||
- service: input_boolean.turn_off
|
- service: input_boolean.turn_off
|
||||||
data:
|
data:
|
||||||
entity_id: input_boolean.poolpump_mode_switch
|
entity_id: input_boolean.poolpump_mode_switch
|
||||||
- service: input_boolean.turn_on
|
|
||||||
data:
|
|
||||||
entity_id: input_boolean.timer_midnight_pending
|
|
||||||
|
|||||||
@@ -1,3 +1,23 @@
|
|||||||
|
#:########################################################################################:#
|
||||||
|
# Package: Anniversary Events
|
||||||
|
# File: anniversaries.yaml
|
||||||
|
# Version: v1.0.0
|
||||||
|
# Date: 2024-09-04
|
||||||
|
#
|
||||||
|
# Purpose:
|
||||||
|
# Provides an anniversary sensor that reports the number of days until the
|
||||||
|
# next configured event and exposes event details as attributes.
|
||||||
|
#
|
||||||
|
# Dependency:
|
||||||
|
# - Anniversary custom integration (platform: anniversary).
|
||||||
|
#
|
||||||
|
# Output:
|
||||||
|
# - sensor.events: Days until the configured holiday event.
|
||||||
|
#
|
||||||
|
# Version History:
|
||||||
|
# - v1.0.0 (2024-09-04): Initial anniversary event sensor package.
|
||||||
|
#:########################################################################################:#
|
||||||
|
|
||||||
sensor:
|
sensor:
|
||||||
- platform: anniversary
|
- platform: anniversary
|
||||||
name: events
|
name: events
|
||||||
|
|||||||
@@ -1,3 +1,29 @@
|
|||||||
|
#:########################################################################################:#
|
||||||
|
# Package: Appliance Activity Feed Sensors
|
||||||
|
# File: appliance_status_mqttfeed.yaml
|
||||||
|
# Version: v1.2.0
|
||||||
|
# Date: 2026-07-20
|
||||||
|
#
|
||||||
|
# Purpose:
|
||||||
|
# Provides MQTT sensors for appliance, vehicle-charger, and tool-charger
|
||||||
|
# completion messages published to the household activity feed.
|
||||||
|
#
|
||||||
|
# Dependencies:
|
||||||
|
# - Home Assistant MQTT integration and broker connectivity.
|
||||||
|
# - External publishers or the announcement router must publish completion
|
||||||
|
# payloads to the configured activity-feed topics.
|
||||||
|
#
|
||||||
|
# Known Limitation:
|
||||||
|
# - sensor.washer_finished has device_class timestamp and therefore requires
|
||||||
|
# an ISO 8601 timestamp. P013-F018 records that the current router may publish
|
||||||
|
# a time-only payload, which is not valid for this device class.
|
||||||
|
#
|
||||||
|
# Version History:
|
||||||
|
# - v1.2.0 (2026-07-20): Added the downstairs dishwasher completion sensor.
|
||||||
|
# - v1.1.0 (2025-05-18): Set the washer completion sensor as a timestamp.
|
||||||
|
# - v1.0.0 (2023-10-20): Added the initial MQTT activity-feed sensors.
|
||||||
|
#:########################################################################################:#
|
||||||
|
|
||||||
mqtt:
|
mqtt:
|
||||||
sensor:
|
sensor:
|
||||||
- unique_id: washing_machine_finished
|
- unique_id: washing_machine_finished
|
||||||
|
|||||||
@@ -1,23 +1,24 @@
|
|||||||
################################################################################
|
#:########################################################################################:#
|
||||||
# PACKAGE: Underhouse Entrance Auto Lights
|
# Package: Underhouse Entrance Auto Lights
|
||||||
################################################################################
|
# File: auto_lights_underhouse_entrance.yaml
|
||||||
|
# Version: v1.2.0
|
||||||
|
# Date: 2026-05-12
|
||||||
#
|
#
|
||||||
# Version:
|
# Purpose:
|
||||||
# 1.2 - 2026-05-12
|
# Replaces the Node-RED automation for the underhouse entrance lights. Uses
|
||||||
# - Added fast Zigbee2MQTT FP1-style event triggers:
|
# Zigbee2MQTT mmWave event triggers (enter/approach/presence) to turn lights
|
||||||
# sensor.underhouse_mmwave_sensor_x23ms_presence_event = enter
|
# on instantly, then off after 400 seconds of sustained clear presence.
|
||||||
# sensor.underhouse_mmwave_sensor_x23ms_presence_event = approach
|
|
||||||
# - Keeps binary presence as a backup trigger and as the clear/off condition.
|
|
||||||
# - This should behave closer to the previous Node-RED automation.
|
|
||||||
#
|
#
|
||||||
# Notes:
|
# Behaviour:
|
||||||
# - The UI may show the binary sensor as "Detected", but the YAML state is
|
# - Fast Zigbee2MQTT FP1-style event triggers (enter, approach) turn lights on.
|
||||||
# normally "on".
|
# - Binary presence sensor serves as a backup trigger and clearance condition.
|
||||||
# - The presence_event sensor can react earlier than the binary presence sensor.
|
# - Lights turn off 400 seconds after presence clears.
|
||||||
# - The event sensor is used to turn lights on quickly, but not to decide when
|
|
||||||
# the area is clear, because it may remain on its last event value.
|
|
||||||
#
|
#
|
||||||
################################################################################
|
# Version History:
|
||||||
|
# - v1.2.0 (2026-05-12): Added fast Zigbee2MQTT FP1-style event triggers and
|
||||||
|
# converted header to standard package format.
|
||||||
|
#
|
||||||
|
#:########################################################################################:#
|
||||||
|
|
||||||
automation:
|
automation:
|
||||||
- id: underhouse_entrance_auto_lights
|
- id: underhouse_entrance_auto_lights
|
||||||
|
|||||||
@@ -1,28 +1,24 @@
|
|||||||
#:########################################################################################:#
|
#:########################################################################################:#
|
||||||
# TITLE: UNDERHOUSE STORAGE LIGHTS AUTO LIGHTS PACKAGE
|
# Package: Underhouse Storage Lights
|
||||||
#:########################################################################################:#
|
# File: auto_lights_underhouse_storage.yaml
|
||||||
# DESCRIPTION:
|
# Version: v1.1.0
|
||||||
|
# Date: 2026-06-14
|
||||||
|
#
|
||||||
|
# Purpose:
|
||||||
# Replaces the Node-RED automation for the underhouse storage lights.
|
# Replaces the Node-RED automation for the underhouse storage lights.
|
||||||
# Doors, PIR sensors, a remote switch, and local relay changes can start or
|
# Doors, PIR sensors, a remote switch, and local relay changes can start or
|
||||||
# extend the storage lighting timer.
|
# extend the storage lighting timer.
|
||||||
#
|
#
|
||||||
# VERSION:
|
# Behaviour:
|
||||||
# V1.1 2026-06-14
|
# - Individual door-open triggers turn lights on immediately.
|
||||||
# - Door-open trigger now watches each real door contact individually.
|
# - All doors closed turns lights off immediately.
|
||||||
# - This means any door changing from closed to open turns the lights on
|
# - PIR occupancy can start/restart the motion timer.
|
||||||
# immediately, even if another door is already open.
|
# - Remote switch press starts or extends the light timer.
|
||||||
# - All doors closed now turns the lights off immediately.
|
|
||||||
# - PIR occupancy can still start/restart the motion timer after that.
|
|
||||||
#
|
#
|
||||||
# V1.0 2026-05-12
|
# Version History:
|
||||||
# - Initial Home Assistant package version.
|
# - v1.1.0 (2026-06-14): Door-open trigger now watches each real door contact
|
||||||
#
|
# individually; all doors closed turns lights off immediately.
|
||||||
# EDIT NOTES:
|
# - v1.0.0 (2026-05-12): Initial Home Assistant package version.
|
||||||
# - Add future door contacts in all places marked FUTURE DOOR.
|
|
||||||
# - Add future PIR sensors in the PIR group marked FUTURE PIR.
|
|
||||||
# - The generated group entities used below should be:
|
|
||||||
# binary_sensor.underhouse_storage_door_contacts
|
|
||||||
# binary_sensor.underhouse_storage_pir_presence
|
|
||||||
#:########################################################################################:#
|
#:########################################################################################:#
|
||||||
|
|
||||||
binary_sensor:
|
binary_sensor:
|
||||||
@@ -112,8 +108,7 @@ automation:
|
|||||||
entity_id:
|
entity_id:
|
||||||
- binary_sensor.underhouse_stealth_door_x07rs_contact
|
- binary_sensor.underhouse_stealth_door_x07rs_contact
|
||||||
- binary_sensor.underhouse_north_door_door
|
- binary_sensor.underhouse_north_door_door
|
||||||
# FUTURE DOOR: replace this with the actual third underhouse storage door contact.
|
- binary_sensor.under_house_underhouse_east_stealth_door_door
|
||||||
#- binary_sensor.underhouse_future_door_contact
|
|
||||||
from: "off"
|
from: "off"
|
||||||
to: "on"
|
to: "on"
|
||||||
|
|
||||||
@@ -213,8 +208,7 @@ automation:
|
|||||||
entity_id:
|
entity_id:
|
||||||
- binary_sensor.underhouse_stealth_door_x07rs_contact
|
- binary_sensor.underhouse_stealth_door_x07rs_contact
|
||||||
- binary_sensor.underhouse_north_door_door
|
- binary_sensor.underhouse_north_door_door
|
||||||
# FUTURE DOOR: replace this with the actual third underhouse storage door contact.
|
- binary_sensor.under_house_underhouse_east_stealth_door_door
|
||||||
#- binary_sensor.underhouse_future_door_contact
|
|
||||||
from: "on"
|
from: "on"
|
||||||
to: "off"
|
to: "off"
|
||||||
action:
|
action:
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
#:########################################################################################:#
|
||||||
|
# Package: Basement Lights
|
||||||
|
# File: basement_lights.yaml
|
||||||
|
# Version: v1.0.1
|
||||||
|
# Date: 2026-07-24
|
||||||
|
#
|
||||||
|
# Purpose:
|
||||||
|
# Consolidated basement lighting configuration for two-way switching synchronization
|
||||||
|
# between the basement hallway and front door area lights.
|
||||||
|
#
|
||||||
|
# Version History:
|
||||||
|
# - v1.0.0 (2024-12-20): Created basement lighting package with two-way sync.
|
||||||
|
# - v1.0.1 (2026-07-24): Standardized header format with additional metadata.
|
||||||
|
#:########################################################################################:#
|
||||||
|
|
||||||
|
group:
|
||||||
|
basement_lights:
|
||||||
|
name: "Basement Lights"
|
||||||
|
entities:
|
||||||
|
- switch.esp_officelights_relay_1_nighttime_lights
|
||||||
|
- switch.esp_officelights_relay_2_daytime_lights
|
||||||
|
- switch.esp_underhouselights_underhouse_entrance_lights
|
||||||
|
- switch.esp_underhouselights_underhouse_storage_lights
|
||||||
|
- switch.esp_laundrylights_relay_1_laundry_lights
|
||||||
@@ -1,3 +1,26 @@
|
|||||||
|
#:########################################################################################:#
|
||||||
|
# Package: Bedroom 3 Window LED Actions
|
||||||
|
# File: bedroom_3_led_actions.yaml
|
||||||
|
# Version: v1.0.0
|
||||||
|
# Date: 2023-10-20
|
||||||
|
#
|
||||||
|
# Purpose:
|
||||||
|
# Provides a selectable control for publishing Bedroom 3 window LED commands
|
||||||
|
# to the household MQTT command topic.
|
||||||
|
#
|
||||||
|
# Behaviour:
|
||||||
|
# - The Off option publishes the off command.
|
||||||
|
# - The 1 Hour Fade option publishes the fade_1 command.
|
||||||
|
# - The placeholder option preserves the neutral selector state.
|
||||||
|
#
|
||||||
|
# Dependencies:
|
||||||
|
# - Home Assistant MQTT integration and broker connectivity.
|
||||||
|
# - An external MQTT consumer for the Bedroom 3 window LED action topic.
|
||||||
|
#
|
||||||
|
# Version History:
|
||||||
|
# - v1.0.0 (2023-10-20): Initial Bedroom 3 window LED action package.
|
||||||
|
#:########################################################################################:#
|
||||||
|
|
||||||
input_select:
|
input_select:
|
||||||
bedroom_3_led_actions:
|
bedroom_3_led_actions:
|
||||||
icon: mdi:led-strip
|
icon: mdi:led-strip
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
#:########################################################################################:#
|
#:########################################################################################:#
|
||||||
# Package: BYD Charging Schedule At Home
|
# Package: BYD Charging Schedule At Home
|
||||||
# File: byd_charging_schedule_athome.yaml
|
# File: byd_charging_schedule_athome.yaml
|
||||||
# Version: v1.0.1
|
# Version: v1.1.0
|
||||||
# Date: 2026-06-19
|
# Date: 2026-07-23
|
||||||
#
|
#
|
||||||
# Purpose:
|
# Purpose:
|
||||||
# Enables the BYD Atto 3 charging schedule when the car arrives home.
|
# Enables the BYD Atto 3 charging schedule when the car arrives home.
|
||||||
@@ -11,11 +11,12 @@
|
|||||||
#
|
#
|
||||||
# Behaviour:
|
# Behaviour:
|
||||||
# - Confirmed arrival home:
|
# - Confirmed arrival home:
|
||||||
# Turns ON switch.atto_3_schedule_enabled,
|
# Sets the schedule to 9:00 PM through midnight with charge-to-full disabled,
|
||||||
# but only if the car had previously been confirmed away.
|
# enables it, and retries cloud reconciliation once per minute.
|
||||||
#
|
#
|
||||||
# - Confirmed away:
|
# - Confirmed away:
|
||||||
# After away_confirm_minutes, turns OFF switch.atto_3_schedule_enabled.
|
# After away_confirm_minutes, disables the schedule and retries cloud
|
||||||
|
# reconciliation once per minute.
|
||||||
#
|
#
|
||||||
# - Manual control:
|
# - Manual control:
|
||||||
# If the car is already home and the user or another HA automation turns
|
# If the car is already home and the user or another HA automation turns
|
||||||
@@ -35,24 +36,35 @@ input_boolean:
|
|||||||
|
|
||||||
automation:
|
automation:
|
||||||
#:######################################################################################:#
|
#:######################################################################################:#
|
||||||
# ARRIVAL - Turn schedule ON only after a confirmed away period
|
# ARRIVAL - Reconcile and enable schedule after a confirmed away period
|
||||||
#:######################################################################################:#
|
#:######################################################################################:#
|
||||||
- id: byd_atto3_charging_schedule_arrival_enable
|
- id: byd_atto3_charging_schedule_arrival_enable
|
||||||
alias: "BYD Atto 3 Charging Schedule - Arrival Enable"
|
alias: "BYD Atto 3 Charging Schedule - Arrival Enable"
|
||||||
description: >
|
description: >
|
||||||
Turns the BYD charging schedule ON when the car arrives home, but only if
|
Reconciles the BYD charging schedule to 9:00 PM through midnight and turns
|
||||||
it was previously confirmed away. This prevents brief away/home glitches
|
it ON when the car arrives home after a confirmed away period. Rechecks the
|
||||||
from undoing a manual OFF while the car is actually still at home.
|
cloud-confirmed state once per minute and retries failed updates.
|
||||||
mode: single
|
mode: single
|
||||||
variables: &byd_charging_schedule_vars
|
variables: &byd_charging_schedule_vars
|
||||||
tracker_entity: device_tracker.byd_atto3_ibeacon_bur4c2a_bermuda_tracker
|
tracker_entity: device_tracker.byd_atto3_ibeacon_bur4c2a_bermuda_tracker
|
||||||
schedule_switch_entity: switch.atto_3_schedule_enabled
|
schedule_switch_entity: switch.atto_3_schedule_enabled
|
||||||
|
schedule_start_time_entity: time.atto_3_start_time
|
||||||
|
schedule_end_time_entity: time.atto_3_end_time
|
||||||
|
schedule_charge_to_full_entity: switch.atto_3_charge_to_full
|
||||||
|
|
||||||
# HA normally stores "At Home" as "home".
|
# HA normally stores "At Home" as "home".
|
||||||
home_state: "home"
|
home_state: "home"
|
||||||
|
|
||||||
|
# Required charging schedule settings.
|
||||||
|
expected_start_time: "21:00:00"
|
||||||
|
expected_end_time: "00:00:00"
|
||||||
|
|
||||||
# Adjustable YAML-only delay before the car is treated as truly away.
|
# Adjustable YAML-only delay before the car is treated as truly away.
|
||||||
away_confirm_minutes: 15
|
away_confirm_minutes: 10
|
||||||
|
|
||||||
|
# Verify cloud-confirmed schedule state at one-minute intervals.
|
||||||
|
schedule_verify_attempts: 6
|
||||||
|
schedule_verify_interval: "00:01:00"
|
||||||
|
|
||||||
# These states will not be treated as confirmed away.
|
# These states will not be treated as confirmed away.
|
||||||
ignored_tracker_states:
|
ignored_tracker_states:
|
||||||
@@ -69,7 +81,7 @@ automation:
|
|||||||
{{ trigger.to_state is not none
|
{{ trigger.to_state is not none
|
||||||
and trigger.from_state is not none
|
and trigger.from_state is not none
|
||||||
and trigger.to_state.state == home_state
|
and trigger.to_state.state == home_state
|
||||||
and trigger.from_state.state != home_state }}
|
and trigger.from_state.state == 'not_home' }}
|
||||||
|
|
||||||
actions:
|
actions:
|
||||||
- choose:
|
- choose:
|
||||||
@@ -78,23 +90,72 @@ automation:
|
|||||||
entity_id: input_boolean.byd_atto3_charging_schedule_confirmed_away
|
entity_id: input_boolean.byd_atto3_charging_schedule_confirmed_away
|
||||||
state: "on"
|
state: "on"
|
||||||
sequence:
|
sequence:
|
||||||
- action: switch.turn_on
|
- action: input_boolean.turn_off
|
||||||
target:
|
target:
|
||||||
entity_id: "{{ schedule_switch_entity }}"
|
entity_id: input_boolean.byd_atto3_charging_schedule_confirmed_away
|
||||||
|
|
||||||
- action: input_boolean.turn_off
|
- repeat:
|
||||||
target:
|
count: "{{ schedule_verify_attempts | int(6) }}"
|
||||||
entity_id: input_boolean.byd_atto3_charging_schedule_confirmed_away
|
sequence:
|
||||||
|
- if:
|
||||||
|
- condition: template
|
||||||
|
value_template: "{{ is_state(tracker_entity, home_state) }}"
|
||||||
|
then:
|
||||||
|
- if:
|
||||||
|
- condition: template
|
||||||
|
value_template: >-
|
||||||
|
{{ states(schedule_start_time_entity) != expected_start_time }}
|
||||||
|
then:
|
||||||
|
- action: time.set_value
|
||||||
|
continue_on_error: true
|
||||||
|
target:
|
||||||
|
entity_id: "{{ schedule_start_time_entity }}"
|
||||||
|
data:
|
||||||
|
time: "{{ expected_start_time }}"
|
||||||
|
|
||||||
|
- if:
|
||||||
|
- condition: template
|
||||||
|
value_template: >-
|
||||||
|
{{ states(schedule_end_time_entity) != expected_end_time }}
|
||||||
|
then:
|
||||||
|
- action: time.set_value
|
||||||
|
continue_on_error: true
|
||||||
|
target:
|
||||||
|
entity_id: "{{ schedule_end_time_entity }}"
|
||||||
|
data:
|
||||||
|
time: "{{ expected_end_time }}"
|
||||||
|
|
||||||
|
- if:
|
||||||
|
- condition: template
|
||||||
|
value_template: >-
|
||||||
|
{{ not is_state(schedule_charge_to_full_entity, 'off') }}
|
||||||
|
then:
|
||||||
|
- action: switch.turn_off
|
||||||
|
continue_on_error: true
|
||||||
|
target:
|
||||||
|
entity_id: "{{ schedule_charge_to_full_entity }}"
|
||||||
|
|
||||||
|
- if:
|
||||||
|
- condition: template
|
||||||
|
value_template: >-
|
||||||
|
{{ not is_state(schedule_switch_entity, 'on') }}
|
||||||
|
then:
|
||||||
|
- action: switch.turn_on
|
||||||
|
continue_on_error: true
|
||||||
|
target:
|
||||||
|
entity_id: "{{ schedule_switch_entity }}"
|
||||||
|
|
||||||
|
- delay: "{{ schedule_verify_interval }}"
|
||||||
|
|
||||||
#:######################################################################################:#
|
#:######################################################################################:#
|
||||||
# DEPARTURE - Turn schedule OFF only after confirmed away timeout
|
# DEPARTURE - Disable schedule after confirmed away timeout
|
||||||
#:######################################################################################:#
|
#:######################################################################################:#
|
||||||
- id: byd_atto3_charging_schedule_departure_disable_after_timeout
|
- id: byd_atto3_charging_schedule_departure_disable_after_timeout
|
||||||
alias: "BYD Atto 3 Charging Schedule - Departure Disable After Timeout"
|
alias: "BYD Atto 3 Charging Schedule - Departure Disable After Timeout"
|
||||||
description: >
|
description: >
|
||||||
When the car leaves home, waits for the configured away timeout. If the car
|
When the car leaves home, waits for the configured away timeout. If the car
|
||||||
is still away after that delay, turns the BYD charging schedule OFF and
|
is still away, marks it confirmed away, turns the BYD charging schedule OFF,
|
||||||
marks the car as confirmed away.
|
and retries failed cloud updates once per minute.
|
||||||
mode: restart
|
mode: restart
|
||||||
variables: *byd_charging_schedule_vars
|
variables: *byd_charging_schedule_vars
|
||||||
|
|
||||||
@@ -111,7 +172,7 @@ automation:
|
|||||||
|
|
||||||
actions:
|
actions:
|
||||||
- delay:
|
- delay:
|
||||||
minutes: "{{ away_confirm_minutes | int(15) }}"
|
minutes: "{{ away_confirm_minutes | int(10) }}"
|
||||||
|
|
||||||
- condition: template
|
- condition: template
|
||||||
value_template: >-
|
value_template: >-
|
||||||
@@ -119,14 +180,28 @@ automation:
|
|||||||
{{ current_state != home_state
|
{{ current_state != home_state
|
||||||
and current_state not in ignored_tracker_states }}
|
and current_state not in ignored_tracker_states }}
|
||||||
|
|
||||||
- action: switch.turn_off
|
|
||||||
target:
|
|
||||||
entity_id: "{{ schedule_switch_entity }}"
|
|
||||||
|
|
||||||
- action: input_boolean.turn_on
|
- action: input_boolean.turn_on
|
||||||
target:
|
target:
|
||||||
entity_id: input_boolean.byd_atto3_charging_schedule_confirmed_away
|
entity_id: input_boolean.byd_atto3_charging_schedule_confirmed_away
|
||||||
|
|
||||||
|
- repeat:
|
||||||
|
count: "{{ schedule_verify_attempts | int(6) }}"
|
||||||
|
sequence:
|
||||||
|
- if:
|
||||||
|
- condition: template
|
||||||
|
value_template: >-
|
||||||
|
{% set current_state = states(tracker_entity) %}
|
||||||
|
{{ current_state != home_state
|
||||||
|
and current_state not in ignored_tracker_states
|
||||||
|
and not is_state(schedule_switch_entity, 'off') }}
|
||||||
|
then:
|
||||||
|
- action: switch.turn_off
|
||||||
|
continue_on_error: true
|
||||||
|
target:
|
||||||
|
entity_id: "{{ schedule_switch_entity }}"
|
||||||
|
|
||||||
|
- delay: "{{ schedule_verify_interval }}"
|
||||||
|
|
||||||
#:######################################################################################:#
|
#:######################################################################################:#
|
||||||
# STARTUP SAFETY - If HA restarts while the car is away, re-arm arrival logic
|
# STARTUP SAFETY - If HA restarts while the car is away, re-arm arrival logic
|
||||||
#:######################################################################################:#
|
#:######################################################################################:#
|
||||||
@@ -134,8 +209,8 @@ automation:
|
|||||||
alias: "BYD Atto 3 Charging Schedule - Startup Away Check"
|
alias: "BYD Atto 3 Charging Schedule - Startup Away Check"
|
||||||
description: >
|
description: >
|
||||||
On Home Assistant startup, if the car is already away and remains away
|
On Home Assistant startup, if the car is already away and remains away
|
||||||
beyond the configured timeout, turn the schedule OFF and mark the car as
|
beyond the configured timeout, mark it confirmed away and reconcile the
|
||||||
confirmed away so that the next confirmed arrival can enable the schedule.
|
charging schedule OFF so the next confirmed arrival can enable it.
|
||||||
mode: single
|
mode: single
|
||||||
variables: *byd_charging_schedule_vars
|
variables: *byd_charging_schedule_vars
|
||||||
|
|
||||||
@@ -153,7 +228,7 @@ automation:
|
|||||||
and current_state not in ignored_tracker_states }}
|
and current_state not in ignored_tracker_states }}
|
||||||
|
|
||||||
- delay:
|
- delay:
|
||||||
minutes: "{{ away_confirm_minutes | int(15) }}"
|
minutes: "{{ away_confirm_minutes | int(10) }}"
|
||||||
|
|
||||||
- condition: template
|
- condition: template
|
||||||
value_template: >-
|
value_template: >-
|
||||||
@@ -161,10 +236,24 @@ automation:
|
|||||||
{{ current_state != home_state
|
{{ current_state != home_state
|
||||||
and current_state not in ignored_tracker_states }}
|
and current_state not in ignored_tracker_states }}
|
||||||
|
|
||||||
- action: switch.turn_off
|
|
||||||
target:
|
|
||||||
entity_id: "{{ schedule_switch_entity }}"
|
|
||||||
|
|
||||||
- action: input_boolean.turn_on
|
- action: input_boolean.turn_on
|
||||||
target:
|
target:
|
||||||
entity_id: input_boolean.byd_atto3_charging_schedule_confirmed_away
|
entity_id: input_boolean.byd_atto3_charging_schedule_confirmed_away
|
||||||
|
|
||||||
|
- repeat:
|
||||||
|
count: "{{ schedule_verify_attempts | int(6) }}"
|
||||||
|
sequence:
|
||||||
|
- if:
|
||||||
|
- condition: template
|
||||||
|
value_template: >-
|
||||||
|
{% set current_state = states(tracker_entity) %}
|
||||||
|
{{ current_state != home_state
|
||||||
|
and current_state not in ignored_tracker_states
|
||||||
|
and not is_state(schedule_switch_entity, 'off') }}
|
||||||
|
then:
|
||||||
|
- action: switch.turn_off
|
||||||
|
continue_on_error: true
|
||||||
|
target:
|
||||||
|
entity_id: "{{ schedule_switch_entity }}"
|
||||||
|
|
||||||
|
- delay: "{{ schedule_verify_interval }}"
|
||||||
|
|||||||
@@ -1,6 +1,28 @@
|
|||||||
|
#:########################################################################################:#
|
||||||
|
# Package: CCTV Motion Feed
|
||||||
|
# File: cctv_controls.yaml
|
||||||
|
# Version: v1.1.0
|
||||||
|
# Date: 2026-07-24
|
||||||
|
#
|
||||||
|
# Purpose:
|
||||||
|
# Provides a Home Assistant binary sensor for front-door motion messages
|
||||||
|
# published by the external CCTV system over MQTT.
|
||||||
|
#
|
||||||
|
# Dependencies:
|
||||||
|
# - Home Assistant MQTT integration and broker connectivity.
|
||||||
|
# - An external CCTV publisher for the configured motion topic.
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# Version History:
|
||||||
|
# - v1.1.0 (2026-07-24): Added explicit numeric ON/OFF payloads (P013-F039).
|
||||||
|
# - v1.0.0 (2022-10-11): Initial CCTV front-door motion sensor package.
|
||||||
|
#:########################################################################################:#
|
||||||
|
|
||||||
mqtt:
|
mqtt:
|
||||||
binary_sensor:
|
binary_sensor:
|
||||||
- unique_id: front_door_movement_camera_corner
|
- unique_id: front_door_movement_camera_corner
|
||||||
name: "Front Door Movement"
|
name: "Front Door Movement"
|
||||||
state_topic: "viewroad-status/cctv/front_door_motion"
|
state_topic: "viewroad-status/cctv/front_door_motion"
|
||||||
|
payload_on: "1"
|
||||||
|
payload_off: "0"
|
||||||
icon: mdi:exit-run
|
icon: mdi:exit-run
|
||||||
|
|||||||
@@ -1,3 +1,23 @@
|
|||||||
|
#:########################################################################################:#
|
||||||
|
# Package: Dashboard Helpers
|
||||||
|
# File: dashboard_helpers.yaml
|
||||||
|
# Version: v1.0.1
|
||||||
|
# Date: 2026-07-24
|
||||||
|
#
|
||||||
|
# Purpose:
|
||||||
|
# Provides helper entities used by stored Home Assistant dashboards for
|
||||||
|
# temporary display behavior that does not belong to a device integration.
|
||||||
|
#
|
||||||
|
# Behaviour:
|
||||||
|
# - The moon-phase overlay helper controls whether the percentage is displayed.
|
||||||
|
# - The companion script enables the overlay for ten seconds and then disables it.
|
||||||
|
# - Restart mode restarts the ten-second display period when called repeatedly.
|
||||||
|
#
|
||||||
|
# Version History:
|
||||||
|
# - v1.0.0 (2026-02-16): Added the temporary moon-phase percentage overlay helper.
|
||||||
|
# - v1.0.1 (2026-07-24): Standardized header format with additional metadata.
|
||||||
|
#:########################################################################################:#
|
||||||
|
|
||||||
input_boolean:
|
input_boolean:
|
||||||
moon_phase_percent_overlay:
|
moon_phase_percent_overlay:
|
||||||
name: Moon phase percent overlay
|
name: Moon phase percent overlay
|
||||||
|
|||||||
@@ -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 60 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: 60
|
||||||
|
|
||||||
|
# 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: 60
|
||||||
|
|
||||||
|
# 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: 60
|
||||||
|
|
||||||
|
# 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: 60
|
||||||
|
|
||||||
|
# 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: 60
|
||||||
|
|
||||||
|
# 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: 60
|
||||||
|
|
||||||
|
# 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: 60
|
||||||
|
|
||||||
|
# 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: 60
|
||||||
|
|
||||||
|
# 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: 60
|
||||||
|
|
||||||
|
# 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: 60
|
||||||
|
|
||||||
|
# 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)
|
||||||
|
}}
|
||||||
@@ -1,3 +1,26 @@
|
|||||||
|
#:########################################################################################:#
|
||||||
|
# Package: Refrigerator Temperature MQTT Publishing
|
||||||
|
# File: fridge_temperatures.yaml
|
||||||
|
# Version: v1.0.0
|
||||||
|
# Date: 2024-09-04
|
||||||
|
#
|
||||||
|
# Purpose:
|
||||||
|
# Publishes refrigerator and freezer temperature changes to household MQTT
|
||||||
|
# status topics for use by external displays and consumers.
|
||||||
|
#
|
||||||
|
# Behaviour:
|
||||||
|
# - Publishes the source sensor state whenever a valid temperature changes.
|
||||||
|
# - Suppresses unknown and unavailable source states.
|
||||||
|
# - Covers the drinks fridge, drinks freezer, and main kitchen fridge.
|
||||||
|
#
|
||||||
|
# Dependencies:
|
||||||
|
# - The three configured Home Assistant temperature sensors.
|
||||||
|
# - Home Assistant MQTT integration and broker connectivity.
|
||||||
|
#
|
||||||
|
# Version History:
|
||||||
|
# - v1.0.0 (2024-09-04): Initial refrigerator temperature publishing package.
|
||||||
|
#:########################################################################################:#
|
||||||
|
|
||||||
automation:
|
automation:
|
||||||
- id: publish_fridge_temps_drinks_fridge
|
- id: publish_fridge_temps_drinks_fridge
|
||||||
alias: publish_fridge_temps_drinks_fridge
|
alias: publish_fridge_temps_drinks_fridge
|
||||||
|
|||||||
@@ -1,42 +0,0 @@
|
|||||||
rest_command:
|
|
||||||
chores1_request:
|
|
||||||
url: !secret grocy_api_uri_chore6
|
|
||||||
method: PUT
|
|
||||||
headers:
|
|
||||||
accept: "*/*"
|
|
||||||
GROCY-API-KEY: !secret grocy_api_key
|
|
||||||
content_type: "application/json"
|
|
||||||
payload: '{
|
|
||||||
"name" : "{{ name }}" ,
|
|
||||||
"next_execution_assigned_to_user_id" : "{{ assigned_to }}"
|
|
||||||
}'
|
|
||||||
# payload: '{"name" : "{{ name }}" , "next_estimated_execution_time" : "{{ next_time }}", "next_execution_assigned_to_user_id" : "{{ assigned_to }}" }'
|
|
||||||
verify_ssl: false
|
|
||||||
|
|
||||||
sensor:
|
|
||||||
- platform: rest
|
|
||||||
name: Dog Motel Cleaning
|
|
||||||
resource: !secret grocy_api_uri_chore3
|
|
||||||
method: GET
|
|
||||||
value_template: "{{ (((as_timestamp(value_json.next_estimated_execution_time))-as_timestamp(now())) | int /60/1440) | round(0) }}"
|
|
||||||
json_attributes:
|
|
||||||
- last_tracked
|
|
||||||
- estimated_execution_time
|
|
||||||
headers:
|
|
||||||
Accept: application/json
|
|
||||||
Content-Type: application/json
|
|
||||||
GROCY-API-KEY: !secret grocy_api_key
|
|
||||||
unit_of_measurement: "Days"
|
|
||||||
- platform: rest
|
|
||||||
name: Red Bin Out
|
|
||||||
resource: !secret grocy_api_uri_chore2
|
|
||||||
method: GET
|
|
||||||
value_template: "{{ (((as_timestamp(value_json.next_estimated_execution_time))-as_timestamp(now())) | int /60/1440) | round(0) }}"
|
|
||||||
json_attributes:
|
|
||||||
- last_tracked
|
|
||||||
- estimated_execution_time
|
|
||||||
headers:
|
|
||||||
Accept: application/json
|
|
||||||
Content-Type: application/json
|
|
||||||
GROCY-API-KEY: !secret grocy_api_key
|
|
||||||
unit_of_measurement: "Days"
|
|
||||||
@@ -1,3 +1,30 @@
|
|||||||
|
#:########################################################################################:#
|
||||||
|
# Package: Node-RED Health Checks
|
||||||
|
# File: health_checks.yaml
|
||||||
|
# Version: v1.2.0
|
||||||
|
# Date: 2025-08-25
|
||||||
|
#
|
||||||
|
# Purpose:
|
||||||
|
# Monitors the Node-RED Panda instance through independent HTTP and MQTT health
|
||||||
|
# signals and combines them into one connectivity status.
|
||||||
|
#
|
||||||
|
# Behaviour:
|
||||||
|
# - Polls the HTTP health endpoint every 30 seconds with a three-second timeout.
|
||||||
|
# - Tracks the MQTT birth/will topic with QoS 1 and a 180-second expiry.
|
||||||
|
# - Reports the combined check as available when either source is available.
|
||||||
|
# - Reports the combined check as connected when either source is connected.
|
||||||
|
#
|
||||||
|
# Dependencies:
|
||||||
|
# - Reachability of the configured Node-RED HTTP health endpoint.
|
||||||
|
# - Home Assistant MQTT integration and the Node-RED birth/will publisher.
|
||||||
|
#
|
||||||
|
# Version History:
|
||||||
|
# - v1.2.0 (2025-08-25): Reduced MQTT expiry to 180 seconds and clarified the
|
||||||
|
# combined HTTP/MQTT sensor name.
|
||||||
|
# - v1.1.0 (2025-08-22): Split HTTP and MQTT probes and added a combined sensor.
|
||||||
|
# - v1.0.0 (2025-08-21): Initial Node-RED health-check package.
|
||||||
|
#:########################################################################################:#
|
||||||
|
|
||||||
# ---- HTTP health probe ----
|
# ---- HTTP health probe ----
|
||||||
binary_sensor:
|
binary_sensor:
|
||||||
- platform: rest
|
- platform: rest
|
||||||
|
|||||||
@@ -1,25 +1,55 @@
|
|||||||
|
#:########################################################################################:#
|
||||||
|
# Package: Internet Traffic Summary
|
||||||
|
# File: internet_traffic.yaml
|
||||||
|
# Version: v1.2.0
|
||||||
|
# Date: 2026-07-22
|
||||||
|
#
|
||||||
|
# Purpose:
|
||||||
|
# Preserves the household Internet download, upload, and total traffic sensors
|
||||||
|
# while sourcing current WAN rates from the EdgeRouter SNMP package.
|
||||||
|
#
|
||||||
|
# Behaviour:
|
||||||
|
# - Mirrors the EdgeRouter WAN download and upload rates in kB/s.
|
||||||
|
# - Calculates total traffic as the sum of download and upload rates.
|
||||||
|
# - Marks each sensor unavailable when its required source is unavailable.
|
||||||
|
#
|
||||||
|
# Dependency:
|
||||||
|
# - packages/edgerouter_snmp.yaml
|
||||||
|
#
|
||||||
|
# Version History:
|
||||||
|
# - v1.2.0 (2026-07-22): Migrated source data from EdgeOS attributes to the
|
||||||
|
# EdgeRouter SNMP WAN rate sensors and added availability handling.
|
||||||
|
# - v1.1.0 (2025-12-21): Migrated the three sensors to modern template syntax.
|
||||||
|
# - v1.0.0 (2022-10-12): Added the initial EdgeOS-based traffic sensors.
|
||||||
|
#:########################################################################################:#
|
||||||
|
|
||||||
template:
|
template:
|
||||||
- sensor:
|
- sensor:
|
||||||
- name: "Internet Download Traffic"
|
- name: "Internet Download Traffic"
|
||||||
unique_id: internet_downtraffic
|
unique_id: internet_downtraffic
|
||||||
unit_of_measurement: "kB/s"
|
unit_of_measurement: "kB/s"
|
||||||
state_class: measurement
|
state_class: measurement
|
||||||
state: >
|
availability: "{{ has_value('sensor.edgerouter_wan_download_rate') }}"
|
||||||
{{ state_attr('binary_sensor.edgeos_interface_pppoe0', 'KBytes/ps (Received)') | float(0) }}
|
state: "{{ states('sensor.edgerouter_wan_download_rate') | float(0) }}"
|
||||||
|
|
||||||
- name: "Internet Upload Traffic"
|
- name: "Internet Upload Traffic"
|
||||||
unique_id: internet_uptraffic
|
unique_id: internet_uptraffic
|
||||||
unit_of_measurement: "kB/s"
|
unit_of_measurement: "kB/s"
|
||||||
state_class: measurement
|
state_class: measurement
|
||||||
state: >
|
availability: "{{ has_value('sensor.edgerouter_wan_upload_rate') }}"
|
||||||
{{ state_attr('binary_sensor.edgeos_interface_pppoe0', 'KBytes/ps (Sent)') | float(0) }}
|
state: "{{ states('sensor.edgerouter_wan_upload_rate') | float(0) }}"
|
||||||
|
|
||||||
- name: "Internet Total Traffic"
|
- name: "Internet Total Traffic"
|
||||||
unique_id: internet_totaltraffic
|
unique_id: internet_totaltraffic
|
||||||
unit_of_measurement: "kB/s"
|
unit_of_measurement: "kB/s"
|
||||||
state_class: measurement
|
state_class: measurement
|
||||||
state: >
|
availability: >-
|
||||||
{{
|
{{
|
||||||
(state_attr('binary_sensor.edgeos_interface_pppoe0', 'KBytes/ps (Received)') | float(0))
|
has_value('sensor.edgerouter_wan_download_rate')
|
||||||
+ (state_attr('binary_sensor.edgeos_interface_pppoe0', 'KBytes/ps (Sent)') | float(0))
|
and has_value('sensor.edgerouter_wan_upload_rate')
|
||||||
|
}}
|
||||||
|
state: >-
|
||||||
|
{{
|
||||||
|
(states('sensor.edgerouter_wan_download_rate') | float(0))
|
||||||
|
+ (states('sensor.edgerouter_wan_upload_rate') | float(0))
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -1,3 +1,30 @@
|
|||||||
|
#:########################################################################################:#
|
||||||
|
# Package: Kogan Kettle MQTT Control
|
||||||
|
# File: kogan_kettle.yaml
|
||||||
|
# Version: v1.0.0
|
||||||
|
# Date: 2022-10-11
|
||||||
|
#
|
||||||
|
# Purpose:
|
||||||
|
# Provides kettle temperature monitoring, power control, and selectable
|
||||||
|
# keep-warm temperatures through a Tasmota/TuyaMCU MQTT interface.
|
||||||
|
#
|
||||||
|
# Behaviour:
|
||||||
|
# - Reads temperature data from the kettle's TuyaReceived MQTT payload.
|
||||||
|
# - Controls kettle power through its MQTT power command topic.
|
||||||
|
# - Maps the keep-warm selector to the kettle's TuyaSend4 commands.
|
||||||
|
#
|
||||||
|
# Dependencies:
|
||||||
|
# - Home Assistant MQTT integration and broker connectivity.
|
||||||
|
# - The configured Tasmota/TuyaMCU kettle MQTT topics and payload structure.
|
||||||
|
#
|
||||||
|
# Known Limitation:
|
||||||
|
# - P013-F026 records that temperature parsing assumes a fixed nested JSON
|
||||||
|
# payload. Malformed or unrelated messages may cause template errors.
|
||||||
|
#
|
||||||
|
# Version History:
|
||||||
|
# - v1.0.0 (2022-10-11): Initial kettle monitoring and control package.
|
||||||
|
#:########################################################################################:#
|
||||||
|
|
||||||
mqtt:
|
mqtt:
|
||||||
sensor:
|
sensor:
|
||||||
- unique_id: kettle.temp
|
- unique_id: kettle.temp
|
||||||
|
|||||||
@@ -1,17 +1,24 @@
|
|||||||
#:########################################################################################:#
|
#:########################################################################################:#
|
||||||
# Low Battery Device Summary Package #
|
# Low Battery Monitoring Package #
|
||||||
#:########################################################################################:#
|
#:########################################################################################:#
|
||||||
#
|
#
|
||||||
# TITLE:
|
# TITLE:
|
||||||
# Low Battery Device Summary
|
# Low Battery Monitoring
|
||||||
#
|
#
|
||||||
# FILE:
|
# FILE:
|
||||||
# packages/low_battery_device_quantity.yaml
|
# packages/low_battery_device_quantity.yaml
|
||||||
#
|
#
|
||||||
# VERSION:
|
# VERSION:
|
||||||
# V1.1 2026-07-20
|
# V1.2 2026-07-23
|
||||||
#
|
#
|
||||||
# VERSION HISTORY:
|
# VERSION HISTORY:
|
||||||
|
# V1.3 2026-07-24
|
||||||
|
# - Truncated list sensor state to count-only summary; full list stays in attributes.
|
||||||
|
#
|
||||||
|
# V1.2 2026-07-23
|
||||||
|
# - Moved the weekly low-battery blueprint automation into this package.
|
||||||
|
# - Preserved the Monday 21:00 schedule and Zorruno Pushover notification.
|
||||||
|
#
|
||||||
# V1.1 2026-07-20
|
# V1.1 2026-07-20
|
||||||
# - Changed the low-battery threshold from 97% to 20%.
|
# - Changed the low-battery threshold from 97% to 20%.
|
||||||
# - Added structured documentation for scope, exclusions, and outputs.
|
# - Added structured documentation for scope, exclusions, and outputs.
|
||||||
@@ -21,17 +28,19 @@
|
|||||||
#
|
#
|
||||||
# PURPOSE:
|
# PURPOSE:
|
||||||
# Finds numeric sensor entities with device_class battery whose value is 20%
|
# Finds numeric sensor entities with device_class battery whose value is 20%
|
||||||
# or lower, then exposes a count and a human-readable device list.
|
# or lower, exposes count and list sensors, and runs a weekly notification.
|
||||||
#
|
#
|
||||||
# SCOPE AND EXCLUSIONS:
|
# SCOPE AND EXCLUSIONS:
|
||||||
# - Only entities in the sensor domain with device_class battery are checked.
|
# - Only entities in the sensor domain with device_class battery are checked.
|
||||||
# - Unknown, unavailable, non-numeric, and "Ok" states are excluded.
|
# - Unknown, unavailable, non-numeric, and "Ok" states are excluded.
|
||||||
# - Battery entities in other domains are not included.
|
# - Battery entities in other domains are not included in the summaries.
|
||||||
|
# - The notification blueprint also checks battery binary sensors.
|
||||||
#
|
#
|
||||||
# OUTPUTS:
|
# OUTPUTS:
|
||||||
# - sensor.devices_with_low_battery reports the number of matching sensors.
|
# - sensor.devices_with_low_battery reports the number of matching sensors.
|
||||||
# - sensor.devices_with_low_battery_list reports names and percentages.
|
# - sensor.devices_with_low_battery_list reports names and percentages.
|
||||||
# - The list sensor also exposes count, entities, and names attributes.
|
# - The list sensor also exposes count, entities, and names attributes.
|
||||||
|
# - A Pushover notification runs each Monday at 21:00 when batteries are low.
|
||||||
#
|
#
|
||||||
#:########################################################################################:#
|
#:########################################################################################:#
|
||||||
|
|
||||||
@@ -69,21 +78,11 @@ template:
|
|||||||
mdi:battery-alert
|
mdi:battery-alert
|
||||||
{% endif %}
|
{% endif %}
|
||||||
state: >
|
state: >
|
||||||
{% set ns = namespace(names=[]) %}
|
{% set count = states('sensor.devices_with_low_battery') | int(0) %}
|
||||||
{% for s in states.sensor %}
|
{% if count == 0 %}
|
||||||
{% if s.attributes.device_class == 'battery' %}
|
|
||||||
{% set st = s.state %}
|
|
||||||
{% if st not in ['unknown', 'unavailable', 'Ok'] %}
|
|
||||||
{% if st | int(-1) != -1 and (st | int(0)) <= 20 %}
|
|
||||||
{% set ns.names = ns.names + [s.name ~ ' (' ~ (st | int(0)) ~ '%)'] %}
|
|
||||||
{% endif %}
|
|
||||||
{% endif %}
|
|
||||||
{% endif %}
|
|
||||||
{% endfor %}
|
|
||||||
{% if ns.names | length == 0 %}
|
|
||||||
None
|
None
|
||||||
{% else %}
|
{% else %}
|
||||||
{{ ns.names | join(', ') }}
|
{{ count }} devices
|
||||||
{% endif %}
|
{% endif %}
|
||||||
attributes:
|
attributes:
|
||||||
count: "{{ states('sensor.devices_with_low_battery') | int(0) }}"
|
count: "{{ states('sensor.devices_with_low_battery') | int(0) }}"
|
||||||
@@ -113,3 +112,22 @@ template:
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{{ ns.n }}
|
{{ ns.n }}
|
||||||
|
|
||||||
|
automation:
|
||||||
|
- id: "1635929281159"
|
||||||
|
alias: "Weekly Low battery level detection"
|
||||||
|
description: >
|
||||||
|
Checks battery-class entities each Monday and notifies Zorruno when any
|
||||||
|
are below the blueprint threshold.
|
||||||
|
use_blueprint:
|
||||||
|
path: sbyx/low-battery-level-detection-notification-for-all-battery-sensors.yaml
|
||||||
|
input:
|
||||||
|
day: 1
|
||||||
|
time: "21:00:00"
|
||||||
|
actions:
|
||||||
|
- action: notify.pushover_zorruno
|
||||||
|
data:
|
||||||
|
title: "View Road Sensors"
|
||||||
|
message: >
|
||||||
|
Weekly Battery Check. Low Devices:
|
||||||
|
{{ sensors }}
|
||||||
|
|||||||
@@ -1,78 +0,0 @@
|
|||||||
template:
|
|
||||||
- sensor:
|
|
||||||
- name: "Devices with low battery"
|
|
||||||
unique_id: devices_with_low_battery_count
|
|
||||||
unit_of_measurement: "devices"
|
|
||||||
state: >
|
|
||||||
{% set ns = namespace(c=0) %}
|
|
||||||
{% for s in states.sensor %}
|
|
||||||
{% if s.attributes.device_class == 'battery' %}
|
|
||||||
{% set st = s.state %}
|
|
||||||
{% if st not in ['unknown', 'unavailable', 'Ok'] %}
|
|
||||||
{% if st | int(-1) != -1 and (st | int(0)) <= 97 %}
|
|
||||||
{% set ns.c = ns.c + 1 %}
|
|
||||||
{% endif %}
|
|
||||||
{% endif %}
|
|
||||||
{% endif %}
|
|
||||||
{% endfor %}
|
|
||||||
{{ ns.c }}
|
|
||||||
icon: >
|
|
||||||
{% if this.state | int(0) == 0 %}
|
|
||||||
mdi:check-circle
|
|
||||||
{% else %}
|
|
||||||
mdi:battery-alert
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
- name: "Devices with low battery list"
|
|
||||||
unique_id: devices_with_low_battery_list
|
|
||||||
icon: >
|
|
||||||
{% if states('sensor.devices_with_low_battery') | int(0) == 0 %}
|
|
||||||
mdi:check-circle
|
|
||||||
{% else %}
|
|
||||||
mdi:battery-alert
|
|
||||||
{% endif %}
|
|
||||||
state: >
|
|
||||||
{% set ns = namespace(names=[]) %}
|
|
||||||
{% for s in states.sensor %}
|
|
||||||
{% if s.attributes.device_class == 'battery' %}
|
|
||||||
{% set st = s.state %}
|
|
||||||
{% if st not in ['unknown', 'unavailable', 'Ok'] %}
|
|
||||||
{% if st | int(-1) != -1 and (st | int(0)) <= 97 %}
|
|
||||||
{% set ns.names = ns.names + [s.name ~ ' (' ~ (st | int(0)) ~ '%)'] %}
|
|
||||||
{% endif %}
|
|
||||||
{% endif %}
|
|
||||||
{% endif %}
|
|
||||||
{% endfor %}
|
|
||||||
{% if ns.names | length == 0 %}
|
|
||||||
None
|
|
||||||
{% else %}
|
|
||||||
{{ ns.names | join(', ') }}
|
|
||||||
{% endif %}
|
|
||||||
attributes:
|
|
||||||
count: "{{ states('sensor.devices_with_low_battery') | int(0) }}"
|
|
||||||
entities: >
|
|
||||||
{% set ns = namespace(e=[]) %}
|
|
||||||
{% for s in states.sensor %}
|
|
||||||
{% if s.attributes.device_class == 'battery' %}
|
|
||||||
{% set st = s.state %}
|
|
||||||
{% if st not in ['unknown', 'unavailable', 'Ok'] %}
|
|
||||||
{% if st | int(-1) != -1 and (st | int(0)) <= 97 %}
|
|
||||||
{% set ns.e = ns.e + [s.entity_id] %}
|
|
||||||
{% endif %}
|
|
||||||
{% endif %}
|
|
||||||
{% endif %}
|
|
||||||
{% endfor %}
|
|
||||||
{{ ns.e }}
|
|
||||||
names: >
|
|
||||||
{% set ns = namespace(n=[]) %}
|
|
||||||
{% for s in states.sensor %}
|
|
||||||
{% if s.attributes.device_class == 'battery' %}
|
|
||||||
{% set st = s.state %}
|
|
||||||
{% if st not in ['unknown', 'unavailable', 'Ok'] %}
|
|
||||||
{% if st | int(-1) != -1 and (st | int(0)) <= 97 %}
|
|
||||||
{% set ns.n = ns.n + [s.name] %}
|
|
||||||
{% endif %}
|
|
||||||
{% endif %}
|
|
||||||
{% endif %}
|
|
||||||
{% endfor %}
|
|
||||||
{{ ns.n }}
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
#media_player:
|
|
||||||
# MPD Media Player on Lounge Touchscreen
|
|
||||||
# - platform: mpd
|
|
||||||
# host: !secret lounge_mpd_player
|
|
||||||
# scan_interval: 1
|
|
||||||
@@ -1,265 +0,0 @@
|
|||||||
#:########################################################################################:#
|
|
||||||
# Moonraker 3D Printer Package #
|
|
||||||
#:########################################################################################:#
|
|
||||||
#
|
|
||||||
# TITLE:
|
|
||||||
# Moonraker 3D Printer
|
|
||||||
#
|
|
||||||
# FILE:
|
|
||||||
# packages/moonraker_3dprinter.yaml
|
|
||||||
#
|
|
||||||
# VERSION:
|
|
||||||
# V1.1 2026-07-20
|
|
||||||
#
|
|
||||||
# VERSION HISTORY:
|
|
||||||
# V1.1 2026-07-20
|
|
||||||
# - Added structured documentation for setup, polling, outputs, and safety.
|
|
||||||
# - Recorded that the Creality printer is not currently connected.
|
|
||||||
#
|
|
||||||
# V1.0
|
|
||||||
# - Initial Moonraker REST sensors, commands, and customizations.
|
|
||||||
#
|
|
||||||
# PURPOSE:
|
|
||||||
# Reads Klipper and Moonraker printer data and provides Home Assistant REST
|
|
||||||
# commands for controlling a Creality K1C 3D printer.
|
|
||||||
#
|
|
||||||
# CURRENT STATUS:
|
|
||||||
# - As of 2026-07-20, this configuration no longer connects to the printer.
|
|
||||||
# - It is retained as a reference for future reconnection work.
|
|
||||||
# - Before reconnecting, verify the printer address, Moonraker availability,
|
|
||||||
# queried Klipper object names, and companion template entities.
|
|
||||||
#
|
|
||||||
# DEPENDENCIES:
|
|
||||||
# - A reachable Moonraker server for the Klipper printer.
|
|
||||||
# - Current configured endpoint: http://192.168.2.31:7125
|
|
||||||
# - Companion templates: templates/moonraker_3dprinter.yaml
|
|
||||||
# - The queried sensors and object names must match the Klipper configuration.
|
|
||||||
#
|
|
||||||
# POLLING:
|
|
||||||
# - Full printer status, printer info, and server data poll every second.
|
|
||||||
# - Current print file metadata polls every five seconds.
|
|
||||||
#
|
|
||||||
# RAW REST OUTPUTS:
|
|
||||||
# - sensor.printer_3d_sensor provides printer object status attributes.
|
|
||||||
# - sensor.printer_3d_info provides printer state and state message.
|
|
||||||
# - sensor.printer_3d_server provides Mainsail server configuration data.
|
|
||||||
# - sensor.printer_3d_file_metadata provides current-file metadata.
|
|
||||||
# - Companion templates derive user-facing sensors from these raw entities.
|
|
||||||
#
|
|
||||||
# CONTROLS:
|
|
||||||
# - Emergency stop immediately halts the printer.
|
|
||||||
# - Firmware restart restarts the Klipper firmware process.
|
|
||||||
# - Cancel stops the current print; pause and resume control print progress.
|
|
||||||
#
|
|
||||||
# SAFETY AND NETWORK:
|
|
||||||
# - Emergency stop, firmware restart, and cancel are disruptive actions and
|
|
||||||
# should only be exposed through dashboard controls with confirmation.
|
|
||||||
# - The configured endpoint uses unencrypted HTTP without authentication and
|
|
||||||
# assumes Moonraker is available only on a trusted local network.
|
|
||||||
#
|
|
||||||
# USER CONFIGURATION:
|
|
||||||
# - If the printer address changes, update every Moonraker URL in this file.
|
|
||||||
# - Confirm the filament and chamber sensor object names before reconnecting.
|
|
||||||
#
|
|
||||||
#:########################################################################################:#
|
|
||||||
|
|
||||||
#:########################################################################################:#
|
|
||||||
# Home Assistant Customizations #
|
|
||||||
#:########################################################################################:#
|
|
||||||
|
|
||||||
homeassistant:
|
|
||||||
customize:
|
|
||||||
rest_command.3d_printer_emergency_stop:
|
|
||||||
friendly_name: "Emergency Stop (K1C)"
|
|
||||||
area_id: Laundry
|
|
||||||
icon: mdi:alert-octagon
|
|
||||||
|
|
||||||
rest_command.3d_printer_firmware_restart:
|
|
||||||
friendly_name: "Firmware Restart (K1C)"
|
|
||||||
area_id: Laundry
|
|
||||||
icon: mdi:restart
|
|
||||||
|
|
||||||
rest_command.3d_printer_cancel:
|
|
||||||
friendly_name: "Cancel Print (K1C)"
|
|
||||||
area_id: Laundry
|
|
||||||
icon: mdi:cancel
|
|
||||||
|
|
||||||
rest_command.3d_printer_pause:
|
|
||||||
friendly_name: "Pause Print (K1C)"
|
|
||||||
area_id: Laundry
|
|
||||||
icon: mdi:pause-circle
|
|
||||||
|
|
||||||
rest_command.3d_printer_resume:
|
|
||||||
friendly_name: "Resume Print (K1C)"
|
|
||||||
area_id: Laundry
|
|
||||||
icon: mdi:play-circle
|
|
||||||
|
|
||||||
sensor.printer_3d_file_metadata:
|
|
||||||
area_id: Laundry
|
|
||||||
friendly_name: "3D Printer (K1C) Print file data"
|
|
||||||
|
|
||||||
sensor.printer_3d_sensor:
|
|
||||||
area_id: Laundry
|
|
||||||
friendly_name: "3D Printer (K1C) Sensors"
|
|
||||||
|
|
||||||
sensor.printer_3d_info:
|
|
||||||
area_id: Laundry
|
|
||||||
friendly_name: "3D Printer (K1C) State"
|
|
||||||
|
|
||||||
sensor.printer_3d_server:
|
|
||||||
area_id: Laundry
|
|
||||||
friendly_name: "3D Printer (K1C) Print Server"
|
|
||||||
|
|
||||||
sensor.3d_printer_chamber_temp:
|
|
||||||
area_id: Laundry
|
|
||||||
|
|
||||||
sensor.3d_printer_hotend_target:
|
|
||||||
area_id: Laundry
|
|
||||||
|
|
||||||
sensor.3d_printer_hotend_actual:
|
|
||||||
area_id: Laundry
|
|
||||||
|
|
||||||
sensor.3d_printer_hotend_power:
|
|
||||||
area_id: Laundry
|
|
||||||
|
|
||||||
sensor.3d_printer_bed_target:
|
|
||||||
area_id: Laundry
|
|
||||||
|
|
||||||
sensor.3d_printer_bed_actual:
|
|
||||||
area_id: Laundry
|
|
||||||
|
|
||||||
sensor.3d_printer_bed_power:
|
|
||||||
area_id: Laundry
|
|
||||||
|
|
||||||
sensor.3d_printer_state:
|
|
||||||
area_id: Laundry
|
|
||||||
|
|
||||||
sensor.3d_printer_current_print:
|
|
||||||
area_id: Laundry
|
|
||||||
|
|
||||||
sensor.3d_printer_current_progress:
|
|
||||||
area_id: Laundry
|
|
||||||
|
|
||||||
sensor.3d_printer_print_time:
|
|
||||||
area_id: Laundry
|
|
||||||
|
|
||||||
sensor.3d_printer_time_remaining:
|
|
||||||
area_id: Laundry
|
|
||||||
|
|
||||||
sensor.3d_printer_eta:
|
|
||||||
area_id: Laundry
|
|
||||||
|
|
||||||
sensor.3d_printer_message:
|
|
||||||
area_id: Laundry
|
|
||||||
|
|
||||||
sensor.3d_printer_layer_height:
|
|
||||||
area_id: Laundry
|
|
||||||
|
|
||||||
sensor.3d_printer_object_height:
|
|
||||||
area_id: Laundry
|
|
||||||
|
|
||||||
sensor.3d_printer_current_height:
|
|
||||||
area_id: Laundry
|
|
||||||
|
|
||||||
sensor.3d_printer_current_layer:
|
|
||||||
area_id: Laundry
|
|
||||||
|
|
||||||
sensor.3d_printer_total_layers:
|
|
||||||
area_id: Laundry
|
|
||||||
|
|
||||||
sensor.3d_printer_actual_layer:
|
|
||||||
area_id: Laundry
|
|
||||||
|
|
||||||
sensor.3d_printer_object_thumbnails:
|
|
||||||
area_id: Laundry
|
|
||||||
|
|
||||||
sensor.3d_printer_state_message:
|
|
||||||
area_id: Laundry
|
|
||||||
|
|
||||||
sensor.3d_printer_heaters_power:
|
|
||||||
area_id: Laundry
|
|
||||||
|
|
||||||
sensor.3d_printer_printername:
|
|
||||||
area_id: Laundry
|
|
||||||
|
|
||||||
binary_sensor.3d_printer_filament:
|
|
||||||
area_id: Laundry
|
|
||||||
|
|
||||||
# === REST sensor: File metadata for current 3D print ===
|
|
||||||
rest:
|
|
||||||
scan_interval: 5
|
|
||||||
resource_template: "http://192.168.2.31:7125/server/files/metadata?filename={{ states('sensor.3d_printer_current_print') | urlencode }}"
|
|
||||||
sensor:
|
|
||||||
- name: printer_3d_file_metadata
|
|
||||||
unique_id: "192.168.2.315ec44d90-419c-419c-802d-d34071639c08"
|
|
||||||
icon: mdi:file-document-outline
|
|
||||||
json_attributes_path: "$.result"
|
|
||||||
json_attributes:
|
|
||||||
- layer_height
|
|
||||||
- object_height
|
|
||||||
- thumbnails
|
|
||||||
value_template: "OK"
|
|
||||||
|
|
||||||
rest_command:
|
|
||||||
3d_printer_emergency_stop:
|
|
||||||
url: "http://192.168.2.31:7125/printer/emergency_stop"
|
|
||||||
method: post
|
|
||||||
3d_printer_firmware_restart:
|
|
||||||
url: "http://192.168.2.31:7125/printer/firmware_restart"
|
|
||||||
method: post
|
|
||||||
3d_printer_cancel:
|
|
||||||
url: "http://192.168.2.31:7125/printer/print/cancel"
|
|
||||||
method: post
|
|
||||||
3d_printer_pause:
|
|
||||||
url: "http://192.168.2.31:7125/printer/print/pause"
|
|
||||||
method: post
|
|
||||||
3d_printer_resume:
|
|
||||||
url: "http://192.168.2.31:7125/printer/print/resume"
|
|
||||||
method: post
|
|
||||||
|
|
||||||
sensor:
|
|
||||||
# === REST sensor: Full 3D printer status (Klipper printer/objects/query) ===
|
|
||||||
- platform: rest
|
|
||||||
name: printer_3d_sensor
|
|
||||||
unique_id: "192.168.2.313a0c25fa-297d-4c19-b03c-ddf04840682b"
|
|
||||||
icon: mdi:printer-3d-nozzle
|
|
||||||
resource: "http://192.168.2.31:7125/printer/objects/query?heater_bed&extruder&print_stats&toolhead&display_status&virtual_sdcard&gcode_move&filament_motion_sensor%20btt_smart_filament&temperature_sensor%20Chamber_Temp"
|
|
||||||
json_attributes_path: "$.result.status"
|
|
||||||
json_attributes:
|
|
||||||
- heater_bed
|
|
||||||
- extruder
|
|
||||||
- print_stats
|
|
||||||
- toolhead
|
|
||||||
- display_status
|
|
||||||
- virtual_sdcard
|
|
||||||
- gcode_move
|
|
||||||
- "filament_motion_sensor btt_smart_filament_sensor"
|
|
||||||
- "temperature_sensor Chamber_Temp"
|
|
||||||
value_template: "OK"
|
|
||||||
force_update: true
|
|
||||||
scan_interval: 1
|
|
||||||
|
|
||||||
# === REST sensor: Basic printer state and message ===
|
|
||||||
- platform: rest
|
|
||||||
name: printer_3d_info
|
|
||||||
unique_id: "192.168.2.311cba6677-02bd-4273-9083-b8301bf6943b"
|
|
||||||
icon: mdi:information-outline
|
|
||||||
scan_interval: 1
|
|
||||||
resource_template: "http://192.168.2.31:7125/printer/info"
|
|
||||||
json_attributes_path: "$.result"
|
|
||||||
json_attributes:
|
|
||||||
- state_message
|
|
||||||
- state
|
|
||||||
value_template: "OK"
|
|
||||||
|
|
||||||
# === REST sensor: Server configuration details (e.g., printer name) ===
|
|
||||||
- platform: rest
|
|
||||||
name: printer_3d_server
|
|
||||||
unique_id: "192.168.2.311cba6677-02bd-4273-9083-b8301bf6949"
|
|
||||||
icon: mdi:server
|
|
||||||
scan_interval: 1
|
|
||||||
resource_template: "http://192.168.2.31:7125/server/database/item?namespace=mainsail&key=general"
|
|
||||||
json_attributes_path: "$.result.value"
|
|
||||||
json_attributes:
|
|
||||||
- printername
|
|
||||||
value_template: "OK"
|
|
||||||
@@ -1,3 +1,25 @@
|
|||||||
|
#:########################################################################################:#
|
||||||
|
# Package: NIWA Local Tides
|
||||||
|
# File: niwa_tides.yaml
|
||||||
|
# Version: v1.0.0
|
||||||
|
# Date: 2022-10-12
|
||||||
|
#
|
||||||
|
# Purpose:
|
||||||
|
# Provides local tide level and forecast information from NIWA for use in Home
|
||||||
|
# Assistant dashboards, automations, and templates.
|
||||||
|
#
|
||||||
|
# Output:
|
||||||
|
# - sensor.local_tides reports the current tide level and exposes upcoming high
|
||||||
|
# and low tide times, levels, phase, and percentage as attributes.
|
||||||
|
#
|
||||||
|
# Dependencies:
|
||||||
|
# - NIWA Tides custom integration (platform: niwa_tides).
|
||||||
|
# - NIWA API key and location coordinates stored in secrets.yaml.
|
||||||
|
#
|
||||||
|
# Version History:
|
||||||
|
# - v1.0.0 (2022-10-12): Initial local NIWA tide sensor package.
|
||||||
|
#:########################################################################################:#
|
||||||
|
|
||||||
sensor:
|
sensor:
|
||||||
- platform: niwa_tides
|
- platform: niwa_tides
|
||||||
api_key: !secret niwa_tides_api_key
|
api_key: !secret niwa_tides_api_key
|
||||||
|
|||||||
@@ -1,52 +0,0 @@
|
|||||||
automation:
|
|
||||||
- id: nspanel_lounge_controller
|
|
||||||
alias: "NSPanel Master Bedroom Controller"
|
|
||||||
description: ""
|
|
||||||
use_blueprint:
|
|
||||||
path: edwardtfn/nspanel_easy_blueprint.yaml
|
|
||||||
input:
|
|
||||||
nspanel_name: 084fb5b4a94e636d01244b1838156446
|
|
||||||
|
|
||||||
button_page01_label: Lounge Lighting Scenes
|
|
||||||
button_pages_icon_size: "10"
|
|
||||||
|
|
||||||
entity01: script.nspanel_lounge_scene_bright
|
|
||||||
entity01_name: "Bright"
|
|
||||||
entity01_icon: mdi:brightness-7
|
|
||||||
entity01_confirm: false
|
|
||||||
|
|
||||||
entity02: script.nspanel_lounge_scene_tv_general
|
|
||||||
entity02_name: "TV General"
|
|
||||||
entity02_icon: mdi:television
|
|
||||||
entity02_confirm: false
|
|
||||||
|
|
||||||
entity03: script.nspanel_lounge_scene_movie
|
|
||||||
entity03_name: "Movie"
|
|
||||||
entity03_icon: mdi:movie-open
|
|
||||||
entity03_confirm: false
|
|
||||||
|
|
||||||
entity04: script.nspanel_lounge_scene_balanced
|
|
||||||
entity04_name: "Balance"
|
|
||||||
entity04_icon: mdi:scale-balance
|
|
||||||
entity04_confirm: false
|
|
||||||
|
|
||||||
entity05: script.nspanel_lounge_scene_warm_cozy
|
|
||||||
entity05_name: "Warm Cozy"
|
|
||||||
entity05_icon: mdi:fireplace
|
|
||||||
entity05_confirm: false
|
|
||||||
|
|
||||||
entity06: script.nspanel_lounge_scene_reading
|
|
||||||
entity06_name: "Reading On/Off"
|
|
||||||
entity06_icon: mdi:book-open-page-variant
|
|
||||||
entity06_confirm: false
|
|
||||||
|
|
||||||
entity07: script.nspanel_lounge_scene_dining
|
|
||||||
entity07_name: "Dining On/Off"
|
|
||||||
entity07_icon: mdi:silverware-fork-knife
|
|
||||||
entity07_confirm: false
|
|
||||||
|
|
||||||
entity08: script.nspanel_lounge_scene_all_off
|
|
||||||
entity08_name: "All Off"
|
|
||||||
entity08_icon: mdi:power
|
|
||||||
entity08_confirm: false
|
|
||||||
mode: single
|
|
||||||
@@ -1,3 +1,28 @@
|
|||||||
|
#:########################################################################################:#
|
||||||
|
# Package: Pet Feeding and Jobs Timing
|
||||||
|
# File: pet_feeding_and_jobs.yaml
|
||||||
|
# Version: v1.1.0
|
||||||
|
# Date: 2026-07-21
|
||||||
|
#
|
||||||
|
# Purpose:
|
||||||
|
# Provides legacy MQTT pet activity sensors and Jobs-derived timing sensors for
|
||||||
|
# the Lounge dog-feeding and depooping gauges.
|
||||||
|
#
|
||||||
|
# Behaviour:
|
||||||
|
# - Dog feeding reports the newest valid morning or evening completion time.
|
||||||
|
# - Dog depooping reports the weekly Jobs completion time.
|
||||||
|
# - Invalid or unrestored Jobs attributes make a timing sensor unavailable
|
||||||
|
# instead of exposing a numeric sentinel value.
|
||||||
|
#
|
||||||
|
# Version History:
|
||||||
|
# - v1.1.0 (2026-07-21): Reject invalid Jobs timing attributes and remove the
|
||||||
|
# 999999-hour fallback from the dog-feeding gauge source.
|
||||||
|
# - v1.0.0 (2026-07-21): Added Jobs-derived dog timing sensors for P010.
|
||||||
|
#:########################################################################################:#
|
||||||
|
|
||||||
|
#:######################################################################################:#
|
||||||
|
# LEGACY MQTT PET ACTIVITY AND TIMING SENSORS
|
||||||
|
#:######################################################################################:#
|
||||||
mqtt:
|
mqtt:
|
||||||
sensor:
|
sensor:
|
||||||
- unique_id: dog_fed_activity
|
- unique_id: dog_fed_activity
|
||||||
@@ -25,6 +50,9 @@ mqtt:
|
|||||||
state_topic: "viewroad-tele/petfeeding/hoursSinceDePooped"
|
state_topic: "viewroad-tele/petfeeding/hoursSinceDePooped"
|
||||||
unit_of_measurement: "Hours"
|
unit_of_measurement: "Hours"
|
||||||
|
|
||||||
|
#:######################################################################################:#
|
||||||
|
# JOBS-DERIVED LOUNGE GAUGE SENSORS
|
||||||
|
#:######################################################################################:#
|
||||||
template:
|
template:
|
||||||
- sensor:
|
- sensor:
|
||||||
- name: "Jobs Tracker Hours Since Dog Fed"
|
- name: "Jobs Tracker Hours Since Dog Fed"
|
||||||
@@ -34,19 +62,21 @@ template:
|
|||||||
unit_of_measurement: "h"
|
unit_of_measurement: "h"
|
||||||
icon: "mdi:dog-side"
|
icon: "mdi:dog-side"
|
||||||
availability: >-
|
availability: >-
|
||||||
{{
|
{% set morning = state_attr('sensor.jobs_tracker_dog_feeding_morning', 'hours_since_completed') %}
|
||||||
state_attr('sensor.jobs_tracker_dog_feeding_morning', 'hours_since_completed') is not none
|
{% set evening = state_attr('sensor.jobs_tracker_dog_feeding_evening', 'hours_since_completed') %}
|
||||||
or state_attr('sensor.jobs_tracker_dog_feeding_evening', 'hours_since_completed') is not none
|
{{ is_number(morning) or is_number(evening) }}
|
||||||
}}
|
|
||||||
state: >-
|
state: >-
|
||||||
{{
|
{% set morning = state_attr('sensor.jobs_tracker_dog_feeding_morning', 'hours_since_completed') %}
|
||||||
[
|
{% set evening = state_attr('sensor.jobs_tracker_dog_feeding_evening', 'hours_since_completed') %}
|
||||||
state_attr('sensor.jobs_tracker_dog_feeding_morning', 'hours_since_completed') | float(999999),
|
{% if is_number(morning) and is_number(evening) %}
|
||||||
state_attr('sensor.jobs_tracker_dog_feeding_evening', 'hours_since_completed') | float(999999)
|
{{ [morning | float(0), evening | float(0)] | min | round(0) }}
|
||||||
]
|
{% elif is_number(morning) %}
|
||||||
| min
|
{{ morning | float(0) | round(0) }}
|
||||||
| round(0)
|
{% elif is_number(evening) %}
|
||||||
}}
|
{{ evening | float(0) | round(0) }}
|
||||||
|
{% else %}
|
||||||
|
{{ none }}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
- name: "Jobs Tracker Hours Since Dog Depooped"
|
- name: "Jobs Tracker Hours Since Dog Depooped"
|
||||||
unique_id: "jobs_tracker_hours_since_dog_depooped"
|
unique_id: "jobs_tracker_hours_since_dog_depooped"
|
||||||
@@ -55,6 +85,8 @@ template:
|
|||||||
unit_of_measurement: "h"
|
unit_of_measurement: "h"
|
||||||
icon: "mdi:emoticon-poop"
|
icon: "mdi:emoticon-poop"
|
||||||
availability: >-
|
availability: >-
|
||||||
{{ state_attr('sensor.jobs_tracker_dog_depooping', 'hours_since_completed') is not none }}
|
{% set hours = state_attr('sensor.jobs_tracker_dog_depooping', 'hours_since_completed') %}
|
||||||
|
{{ is_number(hours) }}
|
||||||
state: >-
|
state: >-
|
||||||
{{ state_attr('sensor.jobs_tracker_dog_depooping', 'hours_since_completed') | float(none) }}
|
{% set hours = state_attr('sensor.jobs_tracker_dog_depooping', 'hours_since_completed') %}
|
||||||
|
{{ hours | float(0) if is_number(hours) else none }}
|
||||||
|
|||||||
@@ -1,26 +0,0 @@
|
|||||||
mqtt:
|
|
||||||
sensor:
|
|
||||||
- unique_id: dog_fed_activity
|
|
||||||
name: "Dog Fed"
|
|
||||||
state_topic: "viewroad-status/activityfeed/dogfed"
|
|
||||||
icon: mdi:dog
|
|
||||||
- unique_id: depooped_activity
|
|
||||||
name: "DePooped"
|
|
||||||
state_topic: "viewroad-status/activityfeed/depooped"
|
|
||||||
icon: mdi:emoticon-poop
|
|
||||||
- unique_id: jobs_done_activity
|
|
||||||
name: "Jobs Done"
|
|
||||||
state_topic: "viewroad-status/activityfeed/jobsdone"
|
|
||||||
icon: mdi:notebook
|
|
||||||
- unique_id: hours_since_jobs_done
|
|
||||||
name: "Hours Since Jobs Done"
|
|
||||||
state_topic: "viewroad-tele/petfeeding/hoursSinceJobsDone"
|
|
||||||
unit_of_measurement: "Hours"
|
|
||||||
- unique_id: hours_since_zorro_fed
|
|
||||||
name: "Hours Since Zorro Fed"
|
|
||||||
state_topic: "viewroad-tele/petfeeding/hoursSinceDogFed"
|
|
||||||
unit_of_measurement: "Hours"
|
|
||||||
- unique_id: hours_since_depooped
|
|
||||||
name: "Hours Since DePooped"
|
|
||||||
state_topic: "viewroad-tele/petfeeding/hoursSinceDePooped"
|
|
||||||
unit_of_measurement: "Hours"
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
mqtt:
|
|
||||||
sensor:
|
|
||||||
- unique_id: piano.lastfinish
|
|
||||||
name: "Last Piano Practice Complete"
|
|
||||||
state_topic: "viewroad-status/activityfeed/pianopractice"
|
|
||||||
- unique_id: piano.lastpracticeduration
|
|
||||||
state_topic: "viewroad-status/piano/lastPracticeDuration"
|
|
||||||
name: "Last Piano Practice Time"
|
|
||||||
unit_of_measurement: "mins"
|
|
||||||
- unique_id: piano.todayspracticeduration
|
|
||||||
state_topic: "viewroad-status/piano/todayPracticeDuration"
|
|
||||||
name: "Today's Piano Practice Time"
|
|
||||||
unit_of_measurement: "mins"
|
|
||||||
- unique_id: piano.thisweekspracticeduration
|
|
||||||
state_topic: "viewroad-status/piano/thisWeekPracticeDuration"
|
|
||||||
name: "This Week's Piano Practice Time"
|
|
||||||
unit_of_measurement: "mins"
|
|
||||||
|
|
||||||
automation:
|
|
||||||
- id: "1648705777863"
|
|
||||||
alias: Tag Started Piano Practice is Scanned
|
|
||||||
description: ""
|
|
||||||
trigger:
|
|
||||||
- platform: tag
|
|
||||||
tag_id: af3f595c-5d41-4bb6-8985-27c70b0e6217
|
|
||||||
condition: []
|
|
||||||
action:
|
|
||||||
- service: mqtt.publish
|
|
||||||
data:
|
|
||||||
topic: viewroad-commands/chores/piano_practice
|
|
||||||
payload: start
|
|
||||||
mode: single
|
|
||||||
- id: "Tag Finished Piano Practice is Scanned"
|
|
||||||
alias: Tag Finished Piano Practice is Scanned
|
|
||||||
description: ""
|
|
||||||
trigger:
|
|
||||||
- platform: tag
|
|
||||||
tag_id: 6e7a6ab5-c7e4-47f3-be8f-830241175d69
|
|
||||||
condition: []
|
|
||||||
action:
|
|
||||||
- service: mqtt.publish
|
|
||||||
data:
|
|
||||||
topic: viewroad-commands/chores/piano_practice
|
|
||||||
payload: end
|
|
||||||
mode: single
|
|
||||||
+31
-1
@@ -1,2 +1,32 @@
|
|||||||
# enable the recorder integration (optional)
|
#:########################################################################################:#
|
||||||
|
# Package: Recorder Configuration
|
||||||
|
# File: recorder.yaml
|
||||||
|
# Version: v1.1.0
|
||||||
|
# Date: 2026-07-23
|
||||||
|
#
|
||||||
|
# Purpose:
|
||||||
|
# Enables Home Assistant Recorder with targeted exclusions for high-frequency
|
||||||
|
# diagnostic and summary entities that do not require historical storage.
|
||||||
|
#
|
||||||
|
# Exclusions:
|
||||||
|
# - ESPHome uptime sensors.
|
||||||
|
# - Home Assistant summary v2 sensors.
|
||||||
|
# - Jobs Tracker summary sensor.
|
||||||
|
#
|
||||||
|
# Notes:
|
||||||
|
# - Excluded entities remain available for current-state use.
|
||||||
|
# - Recorder retention and database settings continue to use Home Assistant
|
||||||
|
# defaults unless configured elsewhere.
|
||||||
|
#
|
||||||
|
# Version History:
|
||||||
|
# - v1.1.0 (2026-07-23): Added targeted exclusions during P013-F010 cleanup.
|
||||||
|
# - v1.0.0 (2022-10-11): Initial Recorder package.
|
||||||
|
#:########################################################################################:#
|
||||||
|
|
||||||
recorder:
|
recorder:
|
||||||
|
exclude:
|
||||||
|
entity_globs:
|
||||||
|
- sensor.esp_*_uptime*
|
||||||
|
- sensor.ha_summary_v2_*
|
||||||
|
entities:
|
||||||
|
- sensor.jobs_tracker_summary
|
||||||
|
|||||||
@@ -1,3 +1,33 @@
|
|||||||
|
#:########################################################################################:#
|
||||||
|
# Package: RobotDyn MQTT Dimmer
|
||||||
|
# File: robotdyn_dimmers.yaml
|
||||||
|
# Version: v1.1.0
|
||||||
|
# Date: 2026-02-05
|
||||||
|
#
|
||||||
|
# Purpose:
|
||||||
|
# Provides a brightness-only Home Assistant light entity for the lounge
|
||||||
|
# RobotDyn dimmer through its JSON MQTT interface.
|
||||||
|
#
|
||||||
|
# Behaviour:
|
||||||
|
# - Reads JSON state updates from the dimmer state topic.
|
||||||
|
# - Publishes power and brightness commands to the dimmer command topic.
|
||||||
|
# - Uses a separate MQTT availability topic for online/offline status.
|
||||||
|
#
|
||||||
|
# Dependency:
|
||||||
|
# - Home Assistant MQTT integration and the external RobotDyn MQTT publisher.
|
||||||
|
#
|
||||||
|
# Known Limitations:
|
||||||
|
# - P013-F027 records that command messages are retained and may replay after
|
||||||
|
# reconnecting.
|
||||||
|
# - The configured availability topic contains "availablity"; confirm whether
|
||||||
|
# that spelling matches the publisher before correcting it.
|
||||||
|
#
|
||||||
|
# Version History:
|
||||||
|
# - v1.1.0 (2026-02-05): Added the current name, default entity ID, explicit
|
||||||
|
# availability payloads, and block-style color modes.
|
||||||
|
# - v1.0.0 (2025-09-10): Initial lounge MQTT dimmer package.
|
||||||
|
#:########################################################################################:#
|
||||||
|
|
||||||
mqtt:
|
mqtt:
|
||||||
- light:
|
- light:
|
||||||
schema: json
|
schema: json
|
||||||
|
|||||||
@@ -1,3 +1,27 @@
|
|||||||
|
#:########################################################################################:#
|
||||||
|
# Package: Security and Notification Helpers
|
||||||
|
# File: security_and_notifications.yaml
|
||||||
|
# Version: v1.1.0
|
||||||
|
# Date: 2026-07-21
|
||||||
|
#
|
||||||
|
# Purpose:
|
||||||
|
# Provides the user-controlled helper that enables away-occupancy security
|
||||||
|
# behavior such as the household lighting simulation.
|
||||||
|
#
|
||||||
|
# Behaviour:
|
||||||
|
# - input_boolean.away_occupied_routine enables eligible away-mode automations.
|
||||||
|
# - The current consumer is the away-light simulation package.
|
||||||
|
#
|
||||||
|
# Dependencies:
|
||||||
|
# - packages/simulation_lights.yaml
|
||||||
|
# - packages/quiet_time.yaml owns the canonical quiet-time helper.
|
||||||
|
#
|
||||||
|
# Version History:
|
||||||
|
# - v1.1.0 (2026-07-21): Disabled the duplicate quiet-time helper after moving
|
||||||
|
# canonical ownership to the dedicated quiet-time package.
|
||||||
|
# - v1.0.0 (2024-05-27): Added quiet-time and away-security helpers.
|
||||||
|
#:########################################################################################:#
|
||||||
|
|
||||||
input_boolean:
|
input_boolean:
|
||||||
# Legacy duplicate retained temporarily for verification.
|
# Legacy duplicate retained temporarily for verification.
|
||||||
# quiet_time:
|
# quiet_time:
|
||||||
|
|||||||
@@ -1,8 +1,39 @@
|
|||||||
|
#:########################################################################################:#
|
||||||
|
# Package: Away-Light Security Simulation
|
||||||
|
# File: simulation_lights.yaml
|
||||||
|
# Version: v1.0.0
|
||||||
|
# Date: 2026-07-24
|
||||||
|
#
|
||||||
|
# Purpose:
|
||||||
|
# Simulates household activity while the away routine is active and all
|
||||||
|
# configured family members are away. Every five minutes after sunset, the
|
||||||
|
# automation selects a configured entity and operates it for a random period.
|
||||||
|
#
|
||||||
|
# Version History:
|
||||||
|
# - v1.0.0 (2026-07-24): Consolidated the simulation groups, away automation, and timed helper
|
||||||
|
# script into one feature package.
|
||||||
|
# - Preserved the existing automation ID, group keys, and script key.
|
||||||
|
# - Aligned the automation with the existing simulation_lights_all group.
|
||||||
|
#:########################################################################################:#
|
||||||
|
|
||||||
group:
|
group:
|
||||||
simulation_lights_all:
|
#:######################################################################################:#
|
||||||
name: Simulation Lights (All)
|
# FAMILY PRESENCE
|
||||||
|
#:######################################################################################:#
|
||||||
|
family_away:
|
||||||
|
name: "All Family Members Away"
|
||||||
entities:
|
entities:
|
||||||
- group.downstairs_flat_lights # ← now valid here
|
- !secret person_1
|
||||||
|
- !secret person_2
|
||||||
|
- !secret person_3
|
||||||
|
|
||||||
|
#:######################################################################################:#
|
||||||
|
# SIMULATION ENTITIES
|
||||||
|
#:######################################################################################:#
|
||||||
|
simulation_lights_all:
|
||||||
|
name: "Simulation Lights (All)"
|
||||||
|
entities:
|
||||||
|
- group.downstairs_flat_lights
|
||||||
- switch.esp_centralstairs_top_relay_2_stair_footer_lights
|
- switch.esp_centralstairs_top_relay_2_stair_footer_lights
|
||||||
- switch.esp_centralstairs_bottom_relay_1_main_stair_lights_lower
|
- switch.esp_centralstairs_bottom_relay_1_main_stair_lights_lower
|
||||||
- switch.esp_entrancebathrmlights_relay_1_main_lights
|
- switch.esp_entrancebathrmlights_relay_1_main_lights
|
||||||
@@ -16,3 +47,62 @@ group:
|
|||||||
- light.esp_loungepelmetleds2_lounge_pelmet_leds_a
|
- light.esp_loungepelmetleds2_lounge_pelmet_leds_a
|
||||||
- light.esp_lounge6chdimmer_couch_spots_left
|
- light.esp_lounge6chdimmer_couch_spots_left
|
||||||
- light.esp_lounge6chdimmer_couch_spots_right
|
- light.esp_lounge6chdimmer_couch_spots_right
|
||||||
|
|
||||||
|
script:
|
||||||
|
#:######################################################################################:#
|
||||||
|
# RANDOM-DURATION ENTITY CONTROL
|
||||||
|
#:######################################################################################:#
|
||||||
|
light_duration:
|
||||||
|
description: "Turns an entity on for a supplied duration and then turns it off."
|
||||||
|
mode: parallel
|
||||||
|
fields:
|
||||||
|
light:
|
||||||
|
description: "Entity to operate."
|
||||||
|
example: light.bedroom
|
||||||
|
duration:
|
||||||
|
description: "How long the entity should remain on."
|
||||||
|
example: "00:25:00"
|
||||||
|
sequence:
|
||||||
|
- action: logbook.log
|
||||||
|
data:
|
||||||
|
name: "Script Notice"
|
||||||
|
message: "Light {{ light }} will be turned on for {{ duration }}"
|
||||||
|
|
||||||
|
- action: homeassistant.turn_on
|
||||||
|
target:
|
||||||
|
entity_id: "{{ light }}"
|
||||||
|
|
||||||
|
- delay: "{{ duration }}"
|
||||||
|
|
||||||
|
- action: homeassistant.turn_off
|
||||||
|
target:
|
||||||
|
entity_id: "{{ light }}"
|
||||||
|
|
||||||
|
automation:
|
||||||
|
#:######################################################################################:#
|
||||||
|
# AWAY-LIGHT SIMULATION
|
||||||
|
#:######################################################################################:#
|
||||||
|
- id: do_random_actions_when_away
|
||||||
|
alias: "Activate Random Actions for Security"
|
||||||
|
description: >
|
||||||
|
Periodically runs the timed simulation script after sunset when the away
|
||||||
|
routine is active and all configured family members are away.
|
||||||
|
mode: parallel
|
||||||
|
triggers:
|
||||||
|
- trigger: time_pattern
|
||||||
|
minutes: "/5"
|
||||||
|
conditions:
|
||||||
|
- condition: sun
|
||||||
|
after: sunset
|
||||||
|
after_offset: "-00:30:00"
|
||||||
|
- condition: state
|
||||||
|
entity_id: input_boolean.away_occupied_routine
|
||||||
|
state: "on"
|
||||||
|
- condition: state
|
||||||
|
entity_id: group.family_away
|
||||||
|
state: "not_home"
|
||||||
|
actions:
|
||||||
|
- action: script.light_duration
|
||||||
|
data:
|
||||||
|
light: "{{ state_attr('group.simulation_lights_all', 'entity_id') | random }}"
|
||||||
|
duration: "00:{{ '{:02}'.format(range(5, 29) | random | int) }}:00"
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
#:########################################################################################:#
|
||||||
|
# Package: Vehicle Announcements
|
||||||
|
# File: vehicle_announcements.yaml
|
||||||
|
# Version: v1.0.0
|
||||||
|
# Date: 2026-07-23
|
||||||
|
#
|
||||||
|
# Purpose:
|
||||||
|
# Sends vehicle arrival announcements through the View Road announcement router.
|
||||||
|
#:########################################################################################:#
|
||||||
|
|
||||||
|
automation:
|
||||||
|
- id: byd_atto3_arrival_announcement
|
||||||
|
alias: "BYD Atto 3 - Arrival Announcement"
|
||||||
|
description: >
|
||||||
|
Sends a Pushover notification to the Zorruno phone when the BYD Atto 3
|
||||||
|
Bermuda tracker arrives home.
|
||||||
|
mode: single
|
||||||
|
triggers:
|
||||||
|
- trigger: state
|
||||||
|
entity_id: device_tracker.byd_atto3_ibeacon_bur4c2a_bermuda_tracker
|
||||||
|
from: "not_home"
|
||||||
|
to: "home"
|
||||||
|
actions:
|
||||||
|
- action: script.view_road_announcement
|
||||||
|
data:
|
||||||
|
channels: "pushover_zorruno"
|
||||||
|
title: "Vehicle Notification"
|
||||||
|
long_announcement: "The BYD Atto 3 has arrived home."
|
||||||
@@ -1,40 +1,52 @@
|
|||||||
#:########################################################################################:#
|
#:########################################################################################:#
|
||||||
# Auckland Council Waste Collection Package #
|
# Council Waste Collection Package #
|
||||||
#:########################################################################################:#
|
#:########################################################################################:#
|
||||||
#
|
#
|
||||||
# TITLE:
|
# TITLE:
|
||||||
# Auckland Council Waste Collection
|
# Council Waste Collection
|
||||||
#
|
#
|
||||||
# FILE:
|
# FILE:
|
||||||
# packages/waste_collection.yaml
|
# packages/waste_collection.yaml
|
||||||
#
|
#
|
||||||
# VERSION:
|
# VERSION:
|
||||||
# V1.1 2026-07-20
|
# V1.4 2026-07-22
|
||||||
#
|
#
|
||||||
# VERSION HISTORY:
|
# VERSION HISTORY:
|
||||||
|
# V1.4 2026-07-22
|
||||||
|
# - Fixed waste type names to match Auckland Council source output (recycling, food scraps)
|
||||||
|
# - Resolved P013-F019: Recycling and food-waste sensors were unknown due to type mismatch
|
||||||
|
#
|
||||||
|
# V1.3 2026-07-22
|
||||||
|
# - Moved the source identifier to !secret council_name.
|
||||||
|
# - Changed source documentation and MQTT unique IDs to generic wording.
|
||||||
|
#
|
||||||
|
# V1.2 2026-07-22
|
||||||
|
# - Moved the source area number to !secret council_area.
|
||||||
|
# - Changed package and schedule display names to generic council wording.
|
||||||
|
#
|
||||||
# V1.1 2026-07-20
|
# V1.1 2026-07-20
|
||||||
# - Added source setup, schedule, MQTT, and maintenance documentation.
|
# - Added source setup, schedule, MQTT, and maintenance documentation.
|
||||||
#
|
#
|
||||||
# V1.0
|
# V1.0
|
||||||
# - Initial Auckland Council schedules and MQTT bin-status sensors.
|
# - Initial council schedules and MQTT bin-status sensors.
|
||||||
#
|
#
|
||||||
# PURPOSE:
|
# PURPOSE:
|
||||||
# Provides Auckland Council rubbish, recycling, and food-waste collection
|
# Provides council rubbish, recycling, and food-waste collection schedules,
|
||||||
# schedules, plus independent MQTT sensors indicating whether bins are out.
|
# plus independent MQTT sensors indicating whether bins are out.
|
||||||
#
|
#
|
||||||
# DEPENDENCY:
|
# DEPENDENCY:
|
||||||
# Requires the Waste Collection Schedule custom integration and its
|
# Requires the Waste Collection Schedule custom integration and its configured
|
||||||
# aucklandcouncil_govt_nz source.
|
# council source.
|
||||||
#
|
#
|
||||||
# AREA NUMBER SETUP:
|
# AREA NUMBER SETUP:
|
||||||
# - Open the Auckland Council rubbish and recycling collection-day finder.
|
# - Open the council rubbish and recycling collection-day finder.
|
||||||
# - Select the property address.
|
# - Select the property address.
|
||||||
# - Use the displayed assessment number as area_number below.
|
# - Store the displayed assessment number as council_area in secrets.yaml.
|
||||||
# - Source documentation:
|
# - Source documentation:
|
||||||
# https://github.com/mampfes/hacs_waste_collection_schedule/blob/master/doc/source/aucklandcouncil_govt_nz.md
|
# https://github.com/mampfes/hacs_waste_collection_schedule
|
||||||
#
|
#
|
||||||
# SCHEDULE SENSORS:
|
# SCHEDULE SENSORS:
|
||||||
# - Types are rubbish, recycle, and food-waste.
|
# - Types are rubbish, recycling, and food scraps (matching Auckland source output).
|
||||||
# - Each sensor returns one upcoming collection with a one-day lead time.
|
# - Each sensor returns one upcoming collection with a one-day lead time.
|
||||||
# - details_format is upcoming and add_days_to adds relative-day text.
|
# - details_format is upcoming and add_days_to adds relative-day text.
|
||||||
#
|
#
|
||||||
@@ -48,19 +60,20 @@
|
|||||||
# - As of 2026-07-20, the rubbish schedule resolves correctly.
|
# - As of 2026-07-20, the rubbish schedule resolves correctly.
|
||||||
# - Recycling and food-waste report unknown; review the source type names when
|
# - Recycling and food-waste report unknown; review the source type names when
|
||||||
# investigating those schedules.
|
# investigating those schedules.
|
||||||
|
# - As of 2026-07-22, fixed type names to match Auckland Council source output.
|
||||||
#
|
#
|
||||||
#:########################################################################################:#
|
#:########################################################################################:#
|
||||||
|
|
||||||
waste_collection_schedule:
|
waste_collection_schedule:
|
||||||
sources:
|
sources:
|
||||||
- name: aucklandcouncil_govt_nz
|
- name: !secret council_name
|
||||||
args:
|
args:
|
||||||
area_number: 12344403722 # Auckland Council assessment number.
|
area_number: !secret council_area
|
||||||
|
|
||||||
sensor:
|
sensor:
|
||||||
- platform: waste_collection_schedule
|
- platform: waste_collection_schedule
|
||||||
#source_index: 0
|
#source_index: 0
|
||||||
name: Auckland Council Waste
|
name: "Council Waste"
|
||||||
details_format: upcoming
|
details_format: upcoming
|
||||||
count: 1
|
count: 1
|
||||||
leadtime: 1
|
leadtime: 1
|
||||||
@@ -72,7 +85,7 @@ sensor:
|
|||||||
- rubbish
|
- rubbish
|
||||||
- platform: waste_collection_schedule
|
- platform: waste_collection_schedule
|
||||||
#source_index: 0
|
#source_index: 0
|
||||||
name: Auckland Council Recycling
|
name: "Council Recycling"
|
||||||
details_format: upcoming
|
details_format: upcoming
|
||||||
count: 1
|
count: 1
|
||||||
leadtime: 1
|
leadtime: 1
|
||||||
@@ -81,10 +94,10 @@ sensor:
|
|||||||
add_days_to: true
|
add_days_to: true
|
||||||
#event_index: EVENT_INDEX
|
#event_index: EVENT_INDEX
|
||||||
types:
|
types:
|
||||||
- recycle
|
- recycling
|
||||||
- platform: waste_collection_schedule
|
- platform: waste_collection_schedule
|
||||||
#source_index: 0
|
#source_index: 0
|
||||||
name: Auckland Council Food-Waste
|
name: "Council Food-Waste"
|
||||||
details_format: upcoming
|
details_format: upcoming
|
||||||
count: 1
|
count: 1
|
||||||
leadtime: 1
|
leadtime: 1
|
||||||
@@ -93,16 +106,16 @@ sensor:
|
|||||||
add_days_to: true
|
add_days_to: true
|
||||||
#event_index: EVENT_INDEX
|
#event_index: EVENT_INDEX
|
||||||
types:
|
types:
|
||||||
- food-waste
|
- food scraps
|
||||||
|
|
||||||
mqtt:
|
mqtt:
|
||||||
sensor:
|
sensor:
|
||||||
- unique_id: auckland_council_red_waste_bin_out
|
- unique_id: council_red_waste_bin_out
|
||||||
name: "Red Waste Bin Out"
|
name: "Red Waste Bin Out"
|
||||||
state_topic: "viewroad-status/rubbishbinstatus/redbin-out"
|
state_topic: "viewroad-status/rubbishbinstatus/redbin-out"
|
||||||
- unique_id: auckland_council_green_recycling_bin_out
|
- unique_id: council_green_recycling_bin_out
|
||||||
name: "Green Recycling Bin Out"
|
name: "Green Recycling Bin Out"
|
||||||
state_topic: "viewroad-status/rubbishbinstatus/greenbin-out"
|
state_topic: "viewroad-status/rubbishbinstatus/greenbin-out"
|
||||||
- unique_id: auckland_council_food_waste_bin_out
|
- unique_id: council_food_waste_bin_out
|
||||||
name: "Food Waste Bin Out"
|
name: "Food Waste Bin Out"
|
||||||
state_topic: "viewroad-status/rubbishbinstatus/foodbin-out"
|
state_topic: "viewroad-status/rubbishbinstatus/foodbin-out"
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
skip:
|
|
||||||
alias: skip
|
|
||||||
sequence:
|
|
||||||
- service: media_player.media_seek
|
|
||||||
data_template:
|
|
||||||
seek_position: "{{ state_attr(media_player.office_tv, 'media_position') | float + 10 }}"
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
action: browser_mod.javascript
|
|
||||||
data:
|
|
||||||
code: lovelace_reload()
|
|
||||||
browser_id:
|
|
||||||
- lounge
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
light_duration:
|
|
||||||
mode: parallel # Enable parallel executions
|
|
||||||
description: "Turns on a light for a while, and then turns it off"
|
|
||||||
fields:
|
|
||||||
light:
|
|
||||||
description: "A specific light"
|
|
||||||
example: "light.bedroom"
|
|
||||||
duration:
|
|
||||||
description: "How long the light should be on in minutes"
|
|
||||||
example: "25"
|
|
||||||
sequence:
|
|
||||||
- service: logbook.log
|
|
||||||
data:
|
|
||||||
name: "Script Notice.."
|
|
||||||
message: >
|
|
||||||
"Light {{ light }} will be turned on for {{ duration }}"
|
|
||||||
- service: homeassistant.turn_on
|
|
||||||
data:
|
|
||||||
entity_id: "{{ light }}"
|
|
||||||
- delay: "{{ duration }}"
|
|
||||||
- service: homeassistant.turn_off
|
|
||||||
data:
|
|
||||||
entity_id: "{{ light }}"
|
|
||||||
@@ -1,308 +0,0 @@
|
|||||||
- binary_sensor:
|
|
||||||
- name: 3D Printer (K1C) Filament
|
|
||||||
unique_id: "192.168.2.315ce32fc9-5e95-49e7-80e7-bfdd2cf1d1fd"
|
|
||||||
device_class: motion
|
|
||||||
state: >
|
|
||||||
{% set attr = states.sensor.printer_3d_sensor.attributes.get("filament_switch_sensor Filament") %}
|
|
||||||
{{ attr.filament_detected if attr is defined else false }}
|
|
||||||
icon: mdi:printer-3d-nozzle-alert
|
|
||||||
availability: >
|
|
||||||
{% set items = ['sensor.printer_3d_sensor'] %}
|
|
||||||
{{ expand(items) | rejectattr('state','in',['unknown','unavailable']) | list | count == items | count }}
|
|
||||||
attributes:
|
|
||||||
friendly_name: "Filament Detected"
|
|
||||||
|
|
||||||
- sensor:
|
|
||||||
- name: 3D Printer (K1C) Printer Name
|
|
||||||
unique_id: "192.168.2.313a0f3144-a801-422f-adb6-e2ed35796074"
|
|
||||||
state: '{{ states.sensor.printer_3d_server.attributes["printername"] }}'
|
|
||||||
availability: >
|
|
||||||
{% set items = ['sensor.printer_3d_server'] %}
|
|
||||||
{{ expand(items) | rejectattr('state','in',['unknown','unavailable']) | list | count == items | count }}
|
|
||||||
icon: mdi:printer-3d
|
|
||||||
attributes:
|
|
||||||
friendly_name: "Printer Name"
|
|
||||||
|
|
||||||
# === Sensor: Chamber temperature (actual) ===
|
|
||||||
- name: 3D Printer (K1C) Chamber Temp
|
|
||||||
unique_id: "192.168.2.31167e5dec-719c-42d3-9560-4f177573f741"
|
|
||||||
state: '{{ states.sensor.printer_3d_sensor.attributes["temperature_sensor Chamber_Temp"]["temperature"] | float(0) | round(1) }}'
|
|
||||||
unit_of_measurement: "°C"
|
|
||||||
icon: mdi:thermometer
|
|
||||||
availability: >
|
|
||||||
{% set items = ['sensor.printer_3d_sensor'] %}
|
|
||||||
{{ expand(items)|rejectattr('state','in',['unknown','unavailable'])|list|count == items|count }}
|
|
||||||
attributes:
|
|
||||||
friendly_name: "Chamber Actual"
|
|
||||||
|
|
||||||
# === Sensor: Hotend target temperature ===
|
|
||||||
- name: 3D Printer (K1C) Hotend Target
|
|
||||||
unique_id: "192.168.2.31167e5dec-719c-42d3-9560-4f177573f740"
|
|
||||||
state: '{{ states.sensor.printer_3d_sensor.attributes["extruder"]["target"] | float(0) | round(1) }}'
|
|
||||||
unit_of_measurement: "°C"
|
|
||||||
icon: mdi:thermometer
|
|
||||||
availability: >
|
|
||||||
{% set items = ['sensor.printer_3d_sensor'] %}
|
|
||||||
{{ expand(items)|rejectattr('state','in',['unknown','unavailable'])|list|count == items|count }}
|
|
||||||
attributes:
|
|
||||||
friendly_name: "Hotend Target"
|
|
||||||
|
|
||||||
# === Sensor: Hotend actual temperature ===
|
|
||||||
- name: 3D Printer (K1C) Hotend Actual
|
|
||||||
unique_id: "192.168.2.317c2593fb-a7dd-4ed7-a865-2e4dddddbb40"
|
|
||||||
state: '{{ states.sensor.printer_3d_sensor.attributes["extruder"]["temperature"] | float(0) | round(1) }}'
|
|
||||||
unit_of_measurement: "°C"
|
|
||||||
icon: mdi:thermometer
|
|
||||||
availability: >
|
|
||||||
{% set items = ['sensor.printer_3d_sensor'] %}
|
|
||||||
{{ expand(items)|rejectattr('state','in',['unknown','unavailable'])|list|count == items|count }}
|
|
||||||
attributes:
|
|
||||||
friendly_name: "Hotend Actual"
|
|
||||||
|
|
||||||
- name: 3D Printer (K1C) Hotend Power
|
|
||||||
unique_id: "192.168.2.31c57a1e35-92fa-403e-b3cf-ec1e97ef94cf"
|
|
||||||
state: '{{ states.sensor.printer_3d_sensor.attributes["extruder"]["power"] | float(0) | round(3) * 100}}'
|
|
||||||
availability: >
|
|
||||||
{% set items = ['sensor.printer_3d_sensor'] %}
|
|
||||||
{{ expand(items)|rejectattr('state','in',['unknown','unavailable'])
|
|
||||||
|list|count == items|count }}
|
|
||||||
unit_of_measurement: "%"
|
|
||||||
icon: mdi:flash
|
|
||||||
attributes:
|
|
||||||
friendly_name: "Hotend Power"
|
|
||||||
|
|
||||||
- name: 3D Printer (K1C) Bed Target
|
|
||||||
unique_id: "192.168.2.31eadec415-c281-4814-8fba-17c6c42670ec"
|
|
||||||
state: '{{ states.sensor.printer_3d_sensor.attributes["heater_bed"]["target"] | float(0) | round(1) }}'
|
|
||||||
unit_of_measurement: "°C"
|
|
||||||
icon: mdi:thermometer
|
|
||||||
availability: >
|
|
||||||
{% set items = ['sensor.printer_3d_sensor'] %}
|
|
||||||
{{ expand(items)|rejectattr('state','in',['unknown','unavailable']) | list | count == items | count }}
|
|
||||||
attributes:
|
|
||||||
friendly_name: "Bed Target"
|
|
||||||
|
|
||||||
- name: 3D Printer (K1C) Bed Actual
|
|
||||||
unique_id: "192.168.2.31cb33503c-c5d4-4e06-b79b-c1cd64ca2d7a"
|
|
||||||
state: '{{ states.sensor.printer_3d_sensor.attributes["heater_bed"]["temperature"] | float(0) | round(1) }}'
|
|
||||||
unit_of_measurement: "°C"
|
|
||||||
icon: mdi:thermometer
|
|
||||||
availability: >
|
|
||||||
{% set items = ['sensor.printer_3d_sensor'] %}
|
|
||||||
{{ expand(items)|rejectattr('state','in',['unknown','unavailable']) | list | count == items | count }}
|
|
||||||
attributes:
|
|
||||||
friendly_name: "Bed Actual"
|
|
||||||
|
|
||||||
- name: 3D Printer (K1C) Bed Power
|
|
||||||
unique_id: "192.168.2.31abb2fc5b-8173-4131-a942-fb11e30f4efa"
|
|
||||||
state: '{{ states.sensor.printer_3d_sensor.attributes["heater_bed"]["power"] | float(0) | round(3) * 100 }}'
|
|
||||||
unit_of_measurement: "%"
|
|
||||||
icon: mdi:flash
|
|
||||||
availability: >
|
|
||||||
{% set items = ['sensor.printer_3d_sensor'] %}
|
|
||||||
{{ expand(items)|rejectattr('state','in',['unknown','unavailable']) | list | count == items | count }}
|
|
||||||
attributes:
|
|
||||||
friendly_name: "Bed Power"
|
|
||||||
|
|
||||||
- name: 3D Printer (K1C) Printer State
|
|
||||||
unique_id: "192.168.2.31758ce84c-3210-4f29-b6b3-38139180de96"
|
|
||||||
state: '{{ states.sensor.printer_3d_sensor.attributes["print_stats"]["state"] }}'
|
|
||||||
icon: >
|
|
||||||
{% set val = states.sensor.printer_3d_sensor.attributes["print_stats"]["state"] %}
|
|
||||||
{% if val == 'standby' %}
|
|
||||||
mdi:sleep
|
|
||||||
{% elif val == 'error' %}
|
|
||||||
mdi:alert-circle
|
|
||||||
{% elif val == 'printing' %}
|
|
||||||
mdi:printer-3d-nozzle
|
|
||||||
{% elif val == 'paused' %}
|
|
||||||
mdi:pause-circle
|
|
||||||
{% elif val == 'complete' %}
|
|
||||||
mdi:printer-3d
|
|
||||||
{% else %}
|
|
||||||
mdi:help-circle
|
|
||||||
{% endif %}
|
|
||||||
availability: >
|
|
||||||
{% set items = ['sensor.printer_3d_sensor'] %}
|
|
||||||
{{ expand(items) | rejectattr('state', 'in', ['unknown','unavailable']) | list | count == items | count }}
|
|
||||||
attributes:
|
|
||||||
friendly_name: "Printer State"
|
|
||||||
|
|
||||||
- name: 3D Printer (K1C) Current Print
|
|
||||||
unique_id: "192.168.2.313a0f3144-a801-422f-adb6-e2ed35796072"
|
|
||||||
state: '{{ states.sensor.printer_3d_sensor.attributes["print_stats"]["filename"] }}'
|
|
||||||
icon: mdi:file
|
|
||||||
availability: >
|
|
||||||
{% set items = ['sensor.printer_3d_sensor'] %}
|
|
||||||
{{ expand(items) | rejectattr('state', 'in', ['unknown','unavailable']) | list | count == items | count }}
|
|
||||||
attributes:
|
|
||||||
friendly_name: "Current Print"
|
|
||||||
|
|
||||||
- name: 3D Printer (K1C) Current Progress
|
|
||||||
unique_id: "192.168.2.318ec7163b-be00-4a10-8051-48cf9a260a29"
|
|
||||||
state: '{{ ((states.sensor.printer_3d_sensor.attributes["display_status"]["progress"]) * 100) | round(0, "floor") }}'
|
|
||||||
unit_of_measurement: "%"
|
|
||||||
icon: mdi:progress-clock
|
|
||||||
availability: >
|
|
||||||
{% set items = ['sensor.printer_3d_sensor'] %}
|
|
||||||
{{ expand(items) | rejectattr('state', 'in', ['unknown','unavailable']) | list | count == items | count }}
|
|
||||||
attributes:
|
|
||||||
friendly_name: "Progress"
|
|
||||||
|
|
||||||
- name: 3D Printer (K1C) Print time
|
|
||||||
unique_id: "192.168.2.31e1ac0fac-e8ba-4e05-8fa0-c8d8076f9f63"
|
|
||||||
state: '{{ states.sensor.printer_3d_sensor.attributes["print_stats"]["print_duration"] | timestamp_custom("%H:%M:%S", 0) }}'
|
|
||||||
icon: mdi:camera-timer
|
|
||||||
availability: >
|
|
||||||
{% set items = ['sensor.printer_3d_sensor'] %}
|
|
||||||
{{ expand(items) | rejectattr('state', 'in', ['unknown','unavailable']) | list | count == items | count }}
|
|
||||||
attributes:
|
|
||||||
friendly_name: "Time Elapsed"
|
|
||||||
|
|
||||||
- name: 3D Printer (K1C) Time Remaining
|
|
||||||
unique_id: "192.168.2.31e12c8def-5ebe-43a4-9f75-605a0c46fc0f"
|
|
||||||
state: >
|
|
||||||
{{
|
|
||||||
(
|
|
||||||
(
|
|
||||||
states.sensor.printer_3d_sensor.attributes["print_stats"]["print_duration"]
|
|
||||||
/ states.sensor.printer_3d_sensor.attributes["display_status"]["progress"]
|
|
||||||
- states.sensor.printer_3d_sensor.attributes["print_stats"]["print_duration"]
|
|
||||||
) if states.sensor.printer_3d_sensor.attributes["display_status"]["progress"] > 0 else 0
|
|
||||||
) | timestamp_custom("%H:%M:%S", 0)
|
|
||||||
}}
|
|
||||||
icon: mdi:timer-sand
|
|
||||||
availability: >
|
|
||||||
{% set items = ['sensor.printer_3d_sensor'] %}
|
|
||||||
{{ expand(items) | rejectattr('state','in',['unknown','unavailable']) | list | count == items | count }}
|
|
||||||
attributes:
|
|
||||||
friendly_name: "Time Remaining"
|
|
||||||
|
|
||||||
- name: 3d_printer_eta
|
|
||||||
unique_id: "192.168.2.31a2b57068-9a32-4d2c-8cc9-57d2389a9082"
|
|
||||||
state: >
|
|
||||||
{{
|
|
||||||
(
|
|
||||||
as_timestamp(now()) +
|
|
||||||
(
|
|
||||||
(
|
|
||||||
states.sensor.printer_3d_sensor.attributes["print_stats"]["print_duration"]
|
|
||||||
/ states.sensor.printer_3d_sensor.attributes["display_status"]["progress"]
|
|
||||||
- states.sensor.printer_3d_sensor.attributes["print_stats"]["print_duration"]
|
|
||||||
) if states.sensor.printer_3d_sensor.attributes["display_status"]["progress"] > 0 else 0
|
|
||||||
)
|
|
||||||
) | timestamp_custom("%H:%M:%S", 1)
|
|
||||||
}}
|
|
||||||
icon: mdi:av-timer
|
|
||||||
availability: >
|
|
||||||
{% set items = ['sensor.printer_3d_sensor'] %}
|
|
||||||
{{ expand(items) | rejectattr('state','in',['unknown','unavailable']) | list | count == items | count }}
|
|
||||||
attributes:
|
|
||||||
friendly_name: "ETA"
|
|
||||||
|
|
||||||
- name: 3D Printer (K1C) Message
|
|
||||||
unique_id: "192.168.2.310c418c0d-e59e-4d4e-aa11-8fae53df58f8"
|
|
||||||
state: '{{ states.sensor.printer_3d_sensor.attributes["display_status"]["message"] }}'
|
|
||||||
icon: mdi:message-cog
|
|
||||||
availability: >
|
|
||||||
{% set items = ['sensor.printer_3d_sensor'] %}
|
|
||||||
{{ expand(items) | rejectattr('state','in',['unknown','unavailable']) | list | count == items | count }}
|
|
||||||
attributes:
|
|
||||||
friendly_name: "Message"
|
|
||||||
|
|
||||||
- name: 3D Printer (K1C) Layer Height
|
|
||||||
unique_id: "192.168.2.316cca455d-c327-4567-b886-4d7f99714265"
|
|
||||||
state: '{{ states.sensor.printer_3d_file_metadata.attributes["layer_height"] | float(0) }}'
|
|
||||||
unit_of_measurement: "mm"
|
|
||||||
icon: mdi:arrow-collapse-down
|
|
||||||
availability: >
|
|
||||||
{% set items = ['sensor.printer_3d_file_metadata'] %}
|
|
||||||
{{ expand(items) | rejectattr('state','in',['unknown','unavailable']) | list | count == items | count }}
|
|
||||||
attributes:
|
|
||||||
friendly_name: "Layer Height"
|
|
||||||
|
|
||||||
- name: 3D Printer (K1C) Object Height
|
|
||||||
unique_id: "192.168.2.316d6d9dc0-9a02-4ce4-a797-c84b42e011a6"
|
|
||||||
state: '{{ (states.sensor.printer_3d_file_metadata.attributes["object_height"] | float(0)) - (states.sensor.printer_3d_file_metadata.attributes["layer_height"] | float(0)) }}'
|
|
||||||
unit_of_measurement: "mm"
|
|
||||||
icon: mdi:arrow-expand-vertical
|
|
||||||
availability: >
|
|
||||||
{% set items = ['sensor.printer_3d_file_metadata'] %}
|
|
||||||
{{ expand(items) | rejectattr('state','in',['unknown','unavailable']) | list | count == items | count }}
|
|
||||||
attributes:
|
|
||||||
friendly_name: "Object Height"
|
|
||||||
|
|
||||||
- name: 3D Printer (K1C) Current Height
|
|
||||||
unique_id: "192.168.2.31d440e568-d4d1-4b3f-85c4-fdacd68c0e1a"
|
|
||||||
state: '{{ states.sensor.printer_3d_sensor.attributes["gcode_move"]["gcode_position"][2] | float(0) | round(2) }}'
|
|
||||||
unit_of_measurement: "mm"
|
|
||||||
icon: mdi:arrow-collapse-down
|
|
||||||
availability: >
|
|
||||||
{% set items = ['sensor.printer_3d_sensor'] %}
|
|
||||||
{{ expand(items) | rejectattr('state','in',['unknown','unavailable']) | list | count == items | count }}
|
|
||||||
attributes:
|
|
||||||
friendly_name: "Current Height"
|
|
||||||
|
|
||||||
- name: 3D Printer (K1C) Current Layer
|
|
||||||
unique_id: "192.168.2.316a77acc1-8134-4354-b2f6-390adab81993"
|
|
||||||
state: '{{ (states("sensor.3d_printer_current_height") | float(0) / states("sensor.3d_printer_layer_height") | float(0)) | round(0) }}'
|
|
||||||
icon: mdi:counter
|
|
||||||
availability: >
|
|
||||||
{% set items = ['sensor.3d_printer_current_height','sensor.3d_printer_layer_height'] %}
|
|
||||||
{{ expand(items) | rejectattr('state','in',['unknown','unavailable']) | list | count == items | count }}
|
|
||||||
attributes:
|
|
||||||
friendly_name: "Current Layer"
|
|
||||||
|
|
||||||
- name: 3D Printer (K1C) Total Layers
|
|
||||||
unique_id: "192.168.2.3153cfe906-28ec-44cd-926a-b08ffb8766e5"
|
|
||||||
state: '{{ (states("sensor.3d_printer_object_height") | float(0) / states("sensor.3d_printer_layer_height") | float(0)) | round(0) }}'
|
|
||||||
icon: mdi:counter
|
|
||||||
availability: >
|
|
||||||
{% set items = ['sensor.3d_printer_object_height','sensor.3d_printer_layer_height'] %}
|
|
||||||
{{ expand(items) | rejectattr('state','in',['unknown','unavailable']) | list | count == items | count }}
|
|
||||||
attributes:
|
|
||||||
friendly_name: "Total Layers"
|
|
||||||
|
|
||||||
- name: 3D Printer (K1C) Actual Layer
|
|
||||||
unique_id: "192.168.2.31167e5dec-719c-42d3-9560-4f177573f742"
|
|
||||||
state: '{{ states.sensor.printer_3d_sensor.attributes.print_stats["info"]["current_layer"] }} / {{ states.sensor.printer_3d_sensor.attributes.print_stats["info"]["total_layer"] }}'
|
|
||||||
icon: mdi:layers-triple
|
|
||||||
availability: >
|
|
||||||
{% set items = ['sensor.printer_3d_sensor'] %}
|
|
||||||
{{ expand(items) | rejectattr('state','in',['unknown','unavailable']) | list | count == items | count }}
|
|
||||||
attributes:
|
|
||||||
friendly_name: "Actual Layer"
|
|
||||||
|
|
||||||
- name: 3D Printer (K1C) Object Thumbnails
|
|
||||||
unique_id: "192.168.2.3159b37837-b751-4d31-98c2-516a52edf833"
|
|
||||||
state: >
|
|
||||||
{% set dir = states('sensor.3d_printer_current_print') %}
|
|
||||||
{% set thumb = state_attr('sensor.printer_3d_file_metadata','thumbnails') %}
|
|
||||||
{% set img = (thumb | last).relative_path if thumb else 'not available' %}
|
|
||||||
{{ (dir.split('/')[:-1] + [img]) | join('/') }}
|
|
||||||
icon: mdi:image
|
|
||||||
availability: >
|
|
||||||
{% set items = ['sensor.printer_3d_file_metadata'] %}
|
|
||||||
{{ expand(items) | rejectattr('state','in',['unknown','unavailable']) | list | count == items | count }}
|
|
||||||
attributes:
|
|
||||||
friendly_name: "Object Thumbnails"
|
|
||||||
|
|
||||||
- name: 3D Printer (K1C) State Message
|
|
||||||
unique_id: "192.168.2.319a5184c9-ac5b-44a9-a691-2b67c243d197"
|
|
||||||
state: '{{ states.sensor.printer_3d_info.attributes["state_message"] }}'
|
|
||||||
icon: mdi:message-cog
|
|
||||||
availability: >
|
|
||||||
{% set items = ['sensor.printer_3d_info'] %}
|
|
||||||
{{ expand(items) | rejectattr('state','in',['unknown','unavailable']) | list | count == items | count }}
|
|
||||||
attributes:
|
|
||||||
friendly_name: "State Message"
|
|
||||||
|
|
||||||
- name: 3D Printer (K1C) Bed and Nozzle Power
|
|
||||||
unique_id: "192.168.2.313988e9be-c910-4634-8205-38d53170d5a5"
|
|
||||||
state: '{{ states("sensor.3d_printer_bed_power") | float(0) | round(1) }}% / {{ states("sensor.3d_printer_hotend_power") | float(0) | round(1) }}%'
|
|
||||||
icon: mdi:meter-electric-outline
|
|
||||||
availability: >
|
|
||||||
{% set items = ['sensor.3d_printer_bed_power','sensor.3d_printer_hotend_power'] %}
|
|
||||||
{{ expand(items) | rejectattr('state','in',['unknown','unavailable']) | list | count == items | count }}
|
|
||||||
attributes:
|
|
||||||
friendly_name: "Bed and Nozzle Power"
|
|
||||||
Reference in New Issue
Block a user