diff --git a/.ha_run.lock b/.ha_run.lock index b22181a..498d958 100644 --- a/.ha_run.lock +++ b/.ha_run.lock @@ -1 +1 @@ -{"pid": 74, "version": 1, "ha_version": "2026.7.3", "start_ts": 1784711954.8835843} \ No newline at end of file +{"pid": 74, "version": 1, "ha_version": "2026.7.3", "start_ts": 1784842567.418518} \ No newline at end of file diff --git a/AGENTS.md b/AGENTS.md deleted file mode 100644 index b234c29..0000000 --- a/AGENTS.md +++ /dev/null @@ -1,65 +0,0 @@ -# Home Assistant OpenCode Rules - -You are working directly inside a live Home Assistant installation (`/homeassistant`). - -## Critical Mandates & Approval Rules - -1. **Read-Only by Default**: Only read files and query APIs when investigating. Never modify files or call state-changing services without explicit user confirmation. -2. **Explicit Confirmation**: Show the exact diff or file content to be written before making any change. Wait for user approval ("yes", "go ahead"). -3. **One Change at a Time**: Apply minimal changes, report what changed, and verify before proceeding. -4. **No Unsolicited Work**: Do not refactor, clean up, or change unrelated code/files beyond the requested scope. -5. **Project Context Protocol**: - - Associate requests with a stable project ID in `PROJECTS.md` (e.g. `P013`). - - Read `PROJECTS.md` before starting work. Record durable context under the project ID. - - Never mark a project `Complete` unless the user explicitly confirms completion. Ask: "Should I close Pxxx?" - -## Off-Limits & Restricted Paths - -Never read, write, or access: -- `.storage/`, `.cloud/`, `deps/`, `tts/` -- `home-assistant_v2.db*`, `home-assistant.log*` -- `secrets.yaml` (never expose or log secret contents) - -Use MCP tools (`get_devices`, `get_areas`, `get_entity_details`, `get_history`) or CLI tools (`hab`) instead of accessing internal storage or database files. - -## Configuration & Architecture Gotchas - -- **Directory Includes in `configuration.yaml`**: - - `automation`: Uses `!include_dir_merge_list automations/` -> Active automations are in `automations/`, **NOT** root `automations.yaml` (which is legacy/inactive). - - `script`: Uses `!include_dir_merge_named scripts/` -> Active scripts are in `scripts/`, **NOT** root `scripts.yaml`. - - `group`: Uses `!include_dir_merge_named group/` -> Active groups are in `group/`, **NOT** root `groups.yaml`. - - `packages`: Uses `!include_dir_named packages/`. - - `template`: Uses `!include_dir_merge_list templates/`. - - `sensor`: Uses `!include_dir_merge_list sensor/`. - -## CLI & Tooling Reference - -### Shell YAML Queries (`yq`) -- **Always use `yq`** (mikefarah/Go version) for CLI YAML processing. PyYAML and Ruby YAML crash on HA tags (`!include`, `!secret`). -- Example: `yq '.homeassistant.latitude' configuration.yaml` - -### Home Assistant Admin (`hab` CLI) -- Pre-authenticated CLI tool for managing HA via terminal (`hab --json`). -- Common commands: - - Entities & Devices: `hab entity list --domain light --json`, `hab device list --json` - - Automations/Scripts: `hab automation list --json`, `hab script list --json` - - Dashboards: `hab dashboard list --json`, `hab dashboard get --json` - - Operations: `hab system health --json`, `hab backup create` - -### Zigbee Toolkit (`zigporter` CLI) -- Handles cascade renames across automations, scripts, scenes, and Lovelace dashboards. -- Command: `zigporter rename-entity light.old light.new --apply` (Defaults to dry-run if `--apply` omitted). -- Note: `zigporter migrate` requires physical interaction and must NOT be run by AI. - -### Safe Configuration Writing (MCP) -- Prefer `write_config_safe(file_path, content, dry_run=true)` to pre-validate before writing. -- Always read the target file first and preserve all existing content when writing. - -## Mandatory YAML Style Rules - -- **Indentation**: 2 spaces (no tabs). -- **Strings**: Double quotes (`"..."`). Exceptions: entity IDs, action names, area IDs, device classes, modes. -- **Booleans**: Lowercase `true`/`false` only. Never `on`/`off`/`yes`/`no` as YAML booleans. -- **Lists & Maps**: Block style only. Never flow style `[1, 2]` or `{key: val}`. -- **Action Targets**: Always use `target:` block (`target: entity_id: light.room`). Do not put `entity_id` inside `data:` or at root action level. -- **Templates**: Use `states('sensor.x')` and `is_state(...)` helpers. Never use direct state object access (`states.sensor.x.state`). diff --git a/PROJECTS.md b/PROJECTS.md deleted file mode 100644 index be32b20..0000000 --- a/PROJECTS.md +++ /dev/null @@ -1,171 +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`. - -## Active Projects - -| ID | Project | Status | Next Step | Last Updated | -| --- | --- | --- | --- | --- | -| P001 | Bedside panel alarm audio | Active | Deploy and verify alarm audio and volume behavior. | 2026-07-20 | -| P002 | reTerminal E1001 dashboard | Active | Test display refresh, wake buttons, MQTT control, and deep sleep. | 2026-07-20 | -| P003 | ESPHome MQTT topic normalization | Active | Compile and deploy both stair devices, then verify HA-down MQTT synchronization. | 2026-07-21 | -| P004 | Appliance completion announcements | Active | Test an appliance cycle and resolve feed/timestamp inconsistencies. | 2026-07-20 | -| P006 | Bedside rain forecast | Active | Verify forecast boundaries and API-failure handling. | 2026-07-20 | -| P013 | Full Home Assistant system audit | Active | Complete a comprehensive read-only audit and present prioritized recommendations. | 2026-07-21 | - -## Completed Projects - -| ID | Project | Status | Next Step | Last Updated | -| --- | --- | --- | --- | --- | -| P005 | Jobs and Chore Tracker V2.3 | Complete | None. | 2026-07-21 | -| P007 | Bedtime stair shutdown | Complete | None. | 2026-07-21 | -| P008 | Spook unused entity cleanup | Complete | None. | 2026-07-21 | -| P009 | Lounge front-door camera layout | Complete | None. | 2026-07-21 | -| P010 | Main-page Jobs timing gauges | Complete | None. | 2026-07-21 | -| P011 | Downstairs occupancy helper cleanup | Complete | None. | 2026-07-21 | -| P012 | EdgeRouter X SNMP monitoring | Complete | None. | 2026-07-21 | - -## Project Details - -### P001: Bedside Panel Alarm Audio - -- Objective: Provide reliable local alarm audio and user-adjustable volume on the bedside panel. -- Current context: ES8311 and I2S output, amplifier control, persistent volume, and Beep/Alarm/Stop controls were added and repeatedly tuned. Compilation, deployment, and hardware behavior have not been confirmed. -- 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. The central-stair top and bottom source configurations are now v3.9: physical states publish retained QoS 1 status, remote mirrors consume those status topics, commands remain non-retained, and the superseded lounge-middle member was removed from the HA sync group. -- Key files: Related device configurations under `esphome/`, including `esphome/esp-centralstairs-top.yaml`, `esphome/esp-centralstairs-bottom.yaml`, and `packages/Lighting_2Way_Sync/central_stairway_2way_sync.yaml`. -- Verification: Both stair ESPHome files parse, Home Assistant configuration validation passed, and both two-member relay pairs synchronized on and off in both directions through Home Assistant. ESPHome compile/upload and HA-down MQTT behavior remain unverified because OpenCode does not have the required Long-Lived Access Token. -- Next step: Configure the OpenCode ESPHome token, compile and deploy both stair devices together, test direct MQTT commands and retained status with HA Core unavailable, then continue the broader paired-device and external-client audit. -- Activity: 2026-07-20 - Applied the MQTT topic normalization across the related devices. -- Activity: 2026-07-21 - Added retained MQTT status mirroring to both central-stair v3.9 source files, removed the superseded lounge-middle HA member, validated the source, and live-tested both HA relay pairs. - -### P004: Appliance Completion Announcements - -- 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: None. -- Activity: 2026-07-20 - Implemented the V2.3 registry and processor changes. -- Activity: 2026-07-21 - User confirmed completion. - -### 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: The lower main stair lights and stair footer lights are included in the bedtime shutdown sequence with no delay. Their HA two-way groups now contain only the active top and bottom relays; the superseded lounge-middle member was removed. -- Key files: `packages/bedtime_mode_actions.yaml` and `packages/Lighting_2Way_Sync/central_stairway_2way_sync.yaml`. -- Verification: Both bottom relays were turned on and the exact zero-delay bedtime shutdown script turned both off. The corresponding top relays followed to off. After the obsolete third member was removed, each two-member pair also synchronized on and off successfully in both HA directions. -- Next step: None. -- Activity: 2026-07-20 - Added both central stair relays to bedtime shutdown. -- Activity: 2026-07-21 - Live-tested both bedtime shutdown targets and both HA two-way relay pairs; all tests passed and the user confirmed completion. - -### P008: Spook Unused Entity Cleanup - -- 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 use numeric-safe Jobs-derived template sensors. Dog feeding uses the latest valid morning/evening completion, and depooping uses the weekly job completion. Jobs startup waits for retained timestamps before refreshing, and stale restored event timestamps cannot mark jobs complete. -- Selected design: Keep two gauges. Dog feeding uses the latest morning/evening completion; depooping uses the weekly job completion. -- Key files: `packages/pet_feeding_and_jobs.yaml` and `packages/Job_Chore_Tracking/jobs_tracker_jobs.yaml`. -- Verification: Home Assistant accepted the v1.1.0 gauge templates and V2.4 Jobs configuration. Invalid feeding attributes produced unavailable instead of 999999, the retained-state readiness guard passed, the stale restored depooping event failed the freshness guard, a subsequent minute refresh preserved the recovered timestamps, and the gauges reported 23 hours fed and 162 hours depooped. Automated visual verification was unavailable because the screenshot tool is disabled. -- Next step: None. -- Activity: 2026-07-21 - Created the project and selected the two-gauge Jobs-derived design. -- Activity: 2026-07-21 - Added the Jobs-derived sensors, reloaded templates, and updated both Lounge Home gauges. -- Activity: 2026-07-21 - User confirmed completion. -- Activity: 2026-07-21 - Fixed a restart race that erased retained feeding timestamps, blocked stale restored event states from creating false completions, removed the 999999 fallback, restored the last valid Jobs timestamps from recorder history, and verified 23-hour/162-hour gauge values. - -### P011: Downstairs Occupancy Helper Cleanup - -- 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: EdgeOS v2.1.9 is incompatible with EdgeOS v3.0.1. SNMP monitoring now provides major-interface traffic rates and router health data, and the three existing Internet traffic entities use the WAN SNMP derivatives. The old EdgeOS config entry is disabled and reversible; its config entry, entities, devices, and HACS files have not been deleted. -- Verification: Backup `1b6dd6d8` contains Home Assistant, the database, folders, and all apps on both backup agents with no failed components. Home Assistant configuration validation and restart succeeded. After disabling EdgeOS, all 31 SNMP entities and the three preserved Internet traffic entities remained available; all 210 old entities remained registered, with 208 unavailable and two having no current state. -- Next step: None. -- Activity: 2026-07-21 - Created the project and began the compatibility investigation. -- Activity: 2026-07-21 - Confirmed the EdgeOS v3.0.1 API incompatibility and validated read-only SNMPv2c access, interface indices, traffic counters, and health OIDs. -- Activity: 2026-07-21 - Added `packages/edgerouter_snmp.yaml`, migrated the existing Internet traffic templates to WAN SNMP derivatives, restarted Home Assistant, and verified live values. -- Activity: 2026-07-21 - Audited old EdgeOS dependencies, found no remaining YAML or dashboard references, disabled the config entry, and verified SNMP monitoring remained operational. -- Activity: 2026-07-21 - Documented `packages/edgerouter_snmp.yaml` as v1.0.0 using zorruno package and section headers; Home Assistant configuration validation passed. -- Activity: 2026-07-21 - Updated the SNMP package to v1.0.1, moved all router host values to `!secret edgerouter_host`, passed configuration validation, restarted Home Assistant, and verified all 31 SNMP entities plus the three Internet traffic entities remained available while EdgeOS stayed disabled. -- Activity: 2026-07-21 - User confirmed completion. - -### P013: Full Home Assistant System Audit - -- Objective: Perform a comprehensive read-only audit of the Home Assistant installation and present prioritized fixes, changes, and improvements. -- Audit report: [`P013_AUDIT_RESULTS.md`](P013_AUDIT_RESULTS.md) -- Scope: Configuration validity and deprecations; runtime and integration health; entities and devices; automations, scripts, scenes, and helpers; dashboards; MQTT, Zigbee, and ESPHome; backups and updates; recorder and performance; and security posture without exposing secrets. -- Current context: Audit created at the user's request. No Home Assistant state, registry, integration, dashboard, firmware, or configuration changes are authorized as part of the audit. -- Next step: Gather evidence through supported configuration files, MCP tools, `hab`, and `zigporter`, then present findings ordered by severity and impact. -- Activity: 2026-07-21 - Created the project and began the comprehensive read-only audit. -- Activity: 2026-07-22 - Resolved P013-F002 by sanitizing and archiving three retired ESPHome configurations; the user accepted the residual historical Git copies because the keys do not match current secrets or active devices. diff --git a/automations.yaml b/automations.yaml deleted file mode 100644 index 8f87b12..0000000 --- a/automations.yaml +++ /dev/null @@ -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 diff --git a/automations/random_actions_security.yaml b/automations/random_actions_security.yaml deleted file mode 100644 index e7bbbf6..0000000 --- a/automations/random_actions_security.yaml +++ /dev/null @@ -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" diff --git a/automations/weekly_battery_notifications_check.yaml b/automations/weekly_battery_notifications_check.yaml deleted file mode 100644 index ad3549b..0000000 --- a/automations/weekly_battery_notifications_check.yaml +++ /dev/null @@ -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 diff --git a/automations/zigbee_low_battery_notifications_2.yaml b/automations/zigbee_low_battery_notifications_2.yaml deleted file mode 100644 index d23b0aa..0000000 --- a/automations/zigbee_low_battery_notifications_2.yaml +++ /dev/null @@ -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 -%} diff --git a/config.boot b/config.boot deleted file mode 100644 index 39a09ec..0000000 --- a/config.boot +++ /dev/null @@ -1,1386 +0,0 @@ -firewall { - all-ping enable - broadcast-ping disable - group { - address-group High_Address_Range_Group_DHCP { - address 192.168.2.230-192.168.2.254 - description "25 DHCP addresses on 2.230-2.254" - } - address-group LAN_Interfaces_for_NAT { - address 192.168.1.0/24 - address 192.168.2.0/24 - address 192.168.3.0/24 - address 172.31.0.0/16 - description "all 4 LAN interfaces" - } - address-group Low_Address_Range_DHCP { - address 192.168.2.1-192.168.2.9 - description "9 DHCP addresses on 2.1-2.9" - } - } - ipv6-name LANv6 { - default-action accept - } - ipv6-receive-redirects disable - ipv6-src-route disable - ip-src-route disable - log-martians enable - name WAN_IN { - default-action drop - description "WAN to internal" - rule 10 { - action accept - description "Allow established/related" - state { - established enable - related enable - } - } - rule 20 { - action drop - description "Drop invalid state" - state { - invalid enable - } - } - } - name WAN_LOCAL { - default-action drop - description "WAN to router" - rule 10 { - action accept - description "Allow established/related" - state { - established enable - related enable - } - } - rule 20 { - action drop - description "Drop invalid state" - state { - invalid enable - } - } - } - name WAN_OUT { - default-action accept - rule 1 { - action accept - description "Drop access to Note 8 Phone (Testing)" - log disable - protocol all - source { - mac-address 8c:45:00:04:03:54 - } - } - rule 3 { - action drop - description "Drop Access to Mollies Macbook Pro" - log disable - protocol all - source { - mac-address 00:23:12:55:00:99 - } - } - rule 4 { - action accept - description "Drop Access to Mollies iPad Mini" - log disable - protocol all - source { - mac-address 54:99:63:24:10:10 - } - } - rule 6 { - action drop - description "Drop Access to Mollies iPhone 12 2023" - log disable - protocol all - source { - mac-address f6:b5:d8:df:b7:20 - } - } - rule 7 { - action drop - description "Drop Access to Mollies Chromebook Intel" - log disable - protocol all - source { - mac-address 00:bb:60:d0:29:b5 - } - } - rule 8 { - action drop - description "Drop DHCP non-assigned 1-9" - disable - log disable - protocol all - source { - group { - address-group Low_Address_Range_DHCP - } - } - } - rule 10 { - action drop - description "Drop DHCP non-assigned 230-254" - disable - log disable - protocol all - source { - group { - address-group High_Address_Range_Group_DHCP - } - } - } - } - options { - mss-clamp { - mss 1412 - } - } - receive-redirects disable - send-redirects enable - source-validation disable - syn-cookies enable -} -interfaces { - ethernet eth0 { - duplex auto - speed auto - vif 10 { - description "Internet (PPPoE)" - pppoe 0 { - default-route auto - firewall { - in { - name WAN_IN - } - local { - name WAN_LOCAL - } - out { - name WAN_OUT - } - } - mtu 1492 - name-server auto - password - user-id user@spark.co.nz - } - } - } - ethernet eth1 { - address 192.168.1.25/24 - address fd00:1234:5678:1::1/64 - description "eth1 - Home LAN" - duplex auto - ipv6 { - dup-addr-detect-transmits 1 - router-advert { - cur-hop-limit 64 - link-mtu 0 - managed-flag false - max-interval 600 - other-config-flag false - prefix fd00:1234:5678:1::/64 { - autonomous-flag true - on-link-flag true - valid-lifetime 2592000 - } - reachable-time 0 - retrans-timer 0 - send-advert true - } - } - speed auto - } - ethernet eth2 { - description "eth2 - Home Wireless" - duplex auto - speed auto - } - ethernet eth3 { - address 192.168.3.25/24 - address fd00:1234:5678:3::1/64 - description "eth3 - Home Automation" - duplex auto - ipv6 { - dup-addr-detect-transmits 1 - router-advert { - cur-hop-limit 64 - link-mtu 0 - managed-flag false - max-interval 600 - other-config-flag false - prefix fd00:1234:5678:3::/64 { - autonomous-flag true - on-link-flag true - valid-lifetime 2592000 - } - reachable-time 0 - retrans-timer 0 - send-advert true - } - } - speed auto - } - ethernet eth4 { - address 172.31.4.25/16 - address fd00:1234:5678:4::1/64 - description "eth4 - (Home Automation)" - duplex auto - ipv6 { - dup-addr-detect-transmits 1 - router-advert { - cur-hop-limit 64 - link-mtu 0 - managed-flag false - max-interval 600 - other-config-flag false - prefix fd00:1234:5678:4::/64 { - autonomous-flag true - on-link-flag true - valid-lifetime 2592000 - } - reachable-time 0 - retrans-timer 0 - send-advert true - } - } - poe { - output off - } - speed auto - } - loopback lo { - } - switch switch0 { - address 192.168.2.25/24 - description Local - mtu 1500 - switch-port { - interface eth2 { - } - vlan-aware disable - } - } -} -port-forward { - auto-firewall enable - hairpin-nat enable - lan-interface eth1 - lan-interface switch0 - lan-interface eth2 - lan-interface eth3 - lan-interface eth4 - rule 1 { - description "plex - ostrich" - forward-to { - address 192.168.1.210 - port 32400 - } - original-port 32406 - protocol tcp_udp - } - rule 2 { - description "transmission - giraffe" - forward-to { - address 192.168.1.210 - port 51413 - } - original-port 51413 - protocol tcp_udp - } - rule 3 { - description ipsec-vpn - forward-to { - address 192.168.3.200 - } - original-port 500 - protocol udp - } - rule 4 { - description ipsec-vpn - forward-to { - address 192.168.3.200 - } - original-port 4500 - protocol udp - } - rule 5 { - description "x2go - ferret" - forward-to { - address 192.168.1.84 - port 22 - } - original-port 26 - protocol tcp_udp - } - rule 6 { - description "tor - ferret" - forward-to { - address 192.168.1.84 - port 1298 - } - original-port 1298 - protocol tcp_udp - } - rule 7 { - description "deluge - ferret" - forward-to { - address 192.168.1.84 - port 6882 - } - original-port 6882 - protocol tcp_udp - } - rule 8 { - description openvpn - forward-to { - address 192.168.3.200 - } - original-port 1194 - protocol udp - } - rule 9 { - description openvpn-2 - forward-to { - address 192.168.3.200 - } - original-port 9443 - protocol tcp_udp - } - rule 10 { - description openvpn-www - forward-to { - address 192.168.3.200 - port 943 - } - original-port 943 - protocol tcp - } - rule 11 { - description "transmission - nas1" - forward-to { - address 192.168.1.237 - port 51500 - } - original-port 51500 - protocol tcp_udp - } - rule 12 { - description "traefik - panda" - forward-to { - address 192.168.3.200 - port 443 - } - original-port 443 - protocol tcp - } - rule 13 { - description "traefik - panda" - forward-to { - address 192.168.3.200 - port 80 - } - original-port 80 - protocol tcp - } - rule 14 { - description "transmission - goat" - forward-to { - address 192.168.2.44 - port 51418 - } - original-port 51418 - protocol tcp_udp - } - rule 15 { - description "transmission - donkey" - forward-to { - address 192.168.2.24 - port 41419 - } - original-port 41419 - protocol tcp_udp - } - rule 16 { - description wireguard - forward-to { - address 192.168.3.200 - port 51820 - } - original-port 51820 - protocol udp - } - rule 17 { - description rustdesk-5 - forward-to { - address 192.168.3.200 - port 21115 - } - original-port 21115 - protocol tcp_udp - } - rule 18 { - description rustdesk-6 - forward-to { - address 192.168.3.200 - port 21116 - } - original-port 21116 - protocol tcp_udp - } - rule 19 { - description rustdesk-7 - forward-to { - address 192.168.3.200 - port 21117 - } - original-port 21117 - protocol tcp_udp - } - rule 20 { - description rustdesk-8 - forward-to { - address 192.168.3.200 - port 21118 - } - original-port 21118 - protocol tcp_udp - } - rule 21 { - description rustdesk-9 - forward-to { - address 192.168.3.200 - port 21119 - } - original-port 21119 - protocol tcp_udp - } - wan-interface pppoe0 -} -service { - dhcp-server { - disabled false - hostfile-update enable - shared-network-name Home_Automation_eth3 { - authoritative disable - subnet 192.168.3.0/24 { - default-router 192.168.3.25 - dns-server 192.168.3.25 - lease 86400 - start 192.168.3.51 { - stop 192.168.3.99 - } - static-mapping panda { - ip-address 192.168.3.200 - mac-address c0:3f:d5:6d:90:80 - } - } - } - shared-network-name LAN1 { - authoritative enable - subnet 192.168.1.0/24 { - default-router 192.168.1.25 - dns-server 192.168.3.200 - dns-server 192.168.1.25 - lease 86400 - start 192.168.1.38 { - stop 192.168.1.99 - } - static-mapping AVTech-Camera { - ip-address 192.168.1.131 - mac-address 00:0e:53:26:8f:51 - } - static-mapping Alarm_Mega { - ip-address 192.168.1.230 - mac-address de:ad:be:ef:fe:ed - } - static-mapping CCTV-DVR1 { - ip-address 192.168.1.225 - mac-address 00:12:41:08:cd:e8 - } - static-mapping Cam1-Garage { - ip-address 192.168.1.112 - mac-address 00:12:41:24:8d:8d - } - static-mapping Cam2-Frontdoor { - ip-address 192.168.1.113 - mac-address 00:12:31:5e:75:53 - } - static-mapping Cam3-GarageSide { - ip-address 192.168.1.115 - mac-address 00:12:41:24:8d:99 - } - static-mapping Cam4-Driveway { - ip-address 192.168.1.116 - mac-address 00:12:31:42:15:90 - } - static-mapping Cam5-Trailer { - ip-address 192.168.1.117 - mac-address 00:12:41:24:8d:bb - } - static-mapping Cam6-Drive_Lower { - ip-address 192.168.1.119 - mac-address 00:12:41:24:8D:8F - } - static-mapping Cam7-Drive_Upper { - ip-address 192.168.1.118 - mac-address 00:12:16:bc:a0:48 - } - static-mapping Cam8-Patio { - ip-address 192.168.1.114 - mac-address 00:12:41:24:99:AB - } - static-mapping HDHomerun-12304BC9 { - ip-address 192.168.1.221 - mac-address 00:18:dd:23:04:bc - } - static-mapping MainTV-Chromecast-Ultra { - ip-address 192.168.1.223 - mac-address 44:09:b8:61:bf:ca - } - static-mapping Panasonic_Viera_COM-MID1 { - ip-address 192.168.1.224 - mac-address 04:20:9a:4d:4a:6b - } - static-mapping Pi-Photobooth { - ip-address 192.168.1.205 - mac-address b8:27:eb:3f:10:f5 - } - static-mapping RaspiPlex2 { - ip-address 192.168.1.204 - mac-address b8:27:eb:12:f4:fe - } - static-mapping Sony_TV_Lounge { - ip-address 192.168.1.226 - mac-address 88:c9:e8:03:53:85 - } - static-mapping Tuya_bulb_1--ESP_B9DA3A { - ip-address 192.168.1.51 - mac-address 50:02:91:b9:da:3a - } - static-mapping Tuya_kogan_smartplug1 { - ip-address 192.168.1.52 - mac-address d4:a6:51:2a:a8:f5 - } - static-mapping ferret { - ip-address 192.168.1.222 - mac-address b8:ca:3a:7e:3f:5f - } - static-mapping guineapig { - ip-address 192.168.1.220 - mac-address 24:be:05:17:a4:d7 - } - static-mapping homeassistant { - ip-address 192.168.1.42 - mac-address 44:8a:5b:b6:41:58 - } - static-mapping odroidxu4 { - ip-address 192.168.1.237 - mac-address 00:1e:06:32:b7:81 - } - static-mapping ostrich { - ip-address 192.168.1.210 - mac-address 70:85:c2:72:b8:56 - } - static-mapping viewrd-opizerologger { - ip-address 192.168.1.208 - mac-address 3e:bd:5c:0e:1c:d8 - } - } - } - shared-network-name LAN2 { - authoritative enable - subnet 192.168.2.0/24 { - default-router 192.168.2.25 - dns-server 192.168.3.200 - dns-server 192.168.2.25 - lease 86400 - start 192.168.2.231 { - stop 192.168.2.249 - } - static-mapping ChromeCast-FoxholeBedroom { - ip-address 192.168.2.216 - mac-address 6c:ad:f8:85:83:1b - } - static-mapping ChromeCast-OfficeTV { - ip-address 192.168.2.213 - mac-address f4:f5:e8:5e:ac:78 - } - static-mapping ChromeClock-LenovoBedroom { - ip-address 192.168.2.214 - mac-address 84:b8:b8:20:7f:fb - } - static-mapping ChromeTic-FoxholeSpeaker { - ip-address 192.168.2.207 - mac-address 6c:5a:b5:54:f0:9c - } - static-mapping ChromeTic-LoungeSpeaker { - ip-address 192.168.2.210 - mac-address 6c:5a:b5:54:e4:3c - } - static-mapping ChromeTic-Mollie { - ip-address 192.168.2.215 - mac-address 6c:5a:b5:54:f7:05 - } - static-mapping ChromeTic-Mollie2-White { - ip-address 192.168.2.211 - mac-address 6c:5a:b5:55:43:3d - } - static-mapping Chromecast-Ultra { - ip-address 192.168.2.212 - mac-address 00:f6:20:70:0b:d6 - } - static-mapping ESP_3b1a59 { - ip-address 192.168.2.200 - mac-address c8:c9:a3:3b:1a:59 - } - static-mapping Foxhole-Bedroom-Androidbox { - ip-address 192.168.2.51 - mac-address e0:76:d0:56:7c:aa - } - static-mapping Foxhole-TV-JVC { - ip-address 192.168.2.52 - mac-address d4:3a:2e:9c:c3:aa - } - static-mapping Galaxy-A72 { - ip-address 192.168.2.32 - mac-address 52:c6:ae:32:75:a4 - } - static-mapping Howard-MiPhone { - ip-address 192.168.2.45 - mac-address fa:80:35:67:60:1a - } - static-mapping Howard-iPad { - ip-address 192.168.2.41 - mac-address a8:5b:78:b1:f1:d9 - } - static-mapping Howards-iPad-Air2023 { - ip-address 192.168.2.43 - mac-address b0:e5:ef:a2:f0:58 - } - static-mapping Howard-Dell17inch-Chromebook { - ip-address 192.168.2.46 - mac-address 2C:6E:85:C5:34:C9 - } - static-mapping Kims-AppleWatch4 { - ip-address 192.168.2.37 - mac-address 62:d0:8b:94:d5:21 - } - static-mapping Kims-Applewatch3 { - ip-address 192.168.2.40 - mac-address be:bb:ba:6e:c3:21 - } - static-mapping Kims-BuzzybeiPhone15 { - ip-address 192.168.2.38 - mac-address 78:a7:c7:98:3b:23 - } - static-mapping Kims-iPad { - ip-address 192.168.2.36 - mac-address fa:71:97:8a:ee:48 - } - static-mapping Kims-iPad-2 { - ip-address 192.168.2.39 - mac-address 88:cb:87:ba:cd:52 - } - static-mapping Mollies-Air { - ip-address 192.168.2.33 - mac-address 60:3e:5f:0c:f8:f8 - } - static-mapping MolliesiPadMini { - ip-address 192.168.2.30 - mac-address 54:99:63:24:10:10 - } - static-mapping Mollies-iphone12-2023 { - ip-address 192.168.2.34 - mac-address 9a:d8:d8:28:e8:2f - } - static-mapping Natalies-iPhone { - ip-address 192.168.2.47 - mac-address 90:a2:5b:eb:64:9c - } - static-mapping Natalies_Macbook_2025 { - ip-address 192.168.2.27 - mac-address 8e:41:e8:ae:94:98 - } - static-mapping Natalies_iPhone_2025 { - ip-address 192.168.2.23 - mac-address 76:c5:f8:36:ca:b0 - } - static-mapping Nintendo-Wii { - ip-address 192.168.2.48 - mac-address 58:bd:a3:83:61:3b - } - static-mapping Oven-GEMODULE5413 { - ip-address 192.168.2.49 - mac-address d8:28:c9:c8:6e:4c - } - static-mapping Ring-Front_Door-80c0c1 { - ip-address 192.168.2.50 - mac-address 34:3e:a4:80:c0:c1 - } - static-mapping amazon-echodot-masterbed-33QG { - ip-address 192.168.2.218 - mac-address 1c:4d:66:f4:67:3e - } - static-mapping amazon-echodot-office-341E { - ip-address 192.168.2.217 - mac-address 40:a2:db:b3:31:8a - } - static-mapping amazon-echoshow-mollie-68af115a435f4c7c { - ip-address 192.168.2.223 - mac-address 44:3d:54:ba:2b:c2 - } - static-mapping amazon-echoshow_kitchen-dcc473ae2 { - ip-address 192.168.2.219 - mac-address 48:b4:23:b0:02:22 - } - static-mapping android-460d2c8f8783d9d3-HP-Tablet { - ip-address 192.168.2.18 - mac-address 88:33:14:bd:15:9f - } - static-mapping beaver { - ip-address 192.168.2.26 - mac-address bc:85:56:36:88:19 - } - static-mapping brother-mfcj5720dw { - ip-address 192.168.2.20 - mac-address 2c:33:7a:31:e8:76 - } - static-mapping capybara { - ip-address 192.168.2.22 - mac-address 50:81:40:2D:84:02 - } - static-mapping cctv-quad1 { - ip-address 192.168.2.53 - mac-address b8:27:eb:26:de:ad - } - static-mapping cctv-quad2 { - ip-address 192.168.2.54 - mac-address b8:27:eb:f4:48:92 - } - static-mapping deco-E4R-8 { - ip-address 192.168.2.196 - mac-address e8:48:b8:33:b2:14 - } - static-mapping deco-E4R-9 { - ip-address 192.168.2.197 - mac-address e8:48:b8:33:b2:7c - } - static-mapping deco-M4R-11 { - ip-address 192.168.2.195 - mac-address e4:c3:2a:2f:29:e4 - } - static-mapping deco-M5-1 { - ip-address 192.168.2.201 - mac-address b0:4e:26:7c:71:39 - } - static-mapping deco-M5-2 { - ip-address 192.168.2.202 - mac-address b0:4e:26:7c:6c:84 - } - static-mapping deco-M5-3 { - ip-address 192.168.2.203 - mac-address b0:4e:26:49:b3:4e - } - static-mapping deco-M5-4 { - ip-address 192.168.2.204 - mac-address b0:4e:26:7c:7e:ef - } - static-mapping deco-M5-5 { - ip-address 192.168.2.206 - mac-address b0:4e:26:7c:74:5e - } - static-mapping deco-M5-6 { - ip-address 192.168.2.205 - mac-address b0:4e:26:7c:78:8c - } - static-mapping deco-X20-7 { - ip-address 192.168.2.199 - mac-address 10:27:f5:80:15:d0 - } - static-mapping deco-X20-10 { - ip-address 192.168.2.198 - mac-address 10:27:f5:80:13:18 - } - static-mapping deco-X20-13 { - ip-address 192.168.2.194 - mac-address e4:c3:2a:c7:fe:08 - } - static-mapping deco-X20-x { - ip-address 192.168.2.208 - mac-address e8:48:b8:22:1a:9c - } - static-mapping deco-X20-y { - ip-address 192.168.2.209 - mac-address e8:48:b8:22:29:b0 - } - static-mapping donkey { - ip-address 192.168.2.24 - mac-address 50:3E:AA:16:7C:F0 - } - static-mapping esp-attobat { - ip-address 192.168.2.176 - mac-address a4:e5:7c:bc:6b:0c - } - static-mapping esp-entbtproxy { - ip-address 192.168.2.165 - mac-address 0c:b8:15:f5:2a:cc - } - static-mapping esp-entmulti { - ip-address 192.168.2.169 - mac-address e4:65:b8:77:76:ec - } - static-mapping esp-leafbat { - ip-address 192.168.2.168 - mac-address a4:e5:7c:bc:93:0b - } - static-mapping esp-mainovenmonitor { - ip-address 192.168.2.177 - mac-address 5c:cf:7f:bd:7b:36 - } - static-mapping esp-midesklamp1s { - ip-address 192.168.2.166 - mac-address d4:f0:ea:7a:ec:93 - } - static-mapping esp-netcupfan2 { - ip-address 192.168.2.173 - mac-address 58:bf:25:da:e9:d9 - } - static-mapping esp-occupancyoffice { - ip-address 192.168.2.179 - mac-address 0c:b8:15:c0:e7:00 - } - static-mapping esp-occupancystair { - ip-address 192.168.2.178 - mac-address 0c:b8:15:c1:f1:0c - } - static-mapping esp-weatherstation { - ip-address 192.168.2.171 - mac-address 40:f5:20:28:2b:3e - } - static-mapping espresense-garage { - ip-address 192.168.2.161 - mac-address 0c:b8:15:f7:44:10 - } - static-mapping esp-bt-fridge-temps { - ip-address 192.168.2.174 - mac-address 24:6f:28:b9:a6:10 - } - static-mapping esp-nfcaccess1-33ad74 { - ip-address 192.168.2.175 - mac-address 5c:cf:7f:33:ad:74 - } - static-mapping howard-2025lap-wifi-IT459996 { - ip-address 192.168.2.57 - mac-address 4c:0f:3e:8f:36:35 - } - static-mapping howard-2025lap-wired-IT459996 { - ip-address 192.168.2.58 - mac-address b4:e9:b8:9f:ca:30 - } - static-mapping howard-OnePlus-12 { - ip-address 192.168.2.42 - mac-address ac:c0:48:66:21:a7 - } - static-mapping howard-gwatch5pro-SM-R925F { - ip-address 192.168.2.44 - mac-address d2:3e:3c:19:34:28 - } - static-mapping k1c-ingenic-EFF9 { - ip-address 192.168.2.31 - mac-address fc:ee:28:05:ef:f9 - } - static-mapping kim-dhl-laptop2 { - ip-address 192.168.2.159 - mac-address e8:bf:b8:2c:61:36 - } - static-mapping kim-iPhone-13dhl { - ip-address 192.168.2.56 - mac-address 3c:3b:77:cb:98:27 - } - static-mapping loungedisplay-ethernet { - ip-address 192.168.2.29 - mac-address 00:01:80:78:b2:73 - } - static-mapping loungedisplay-wireless { - ip-address 192.168.2.28 - mac-address 00:25:d3:76:58:d3 - } - static-mapping obk-ks811t-006D-garageentry { - ip-address 192.168.2.193 - mac-address 50:8a:06:81:de:6d - } - static-mapping obk-ks811t-B1C4-mainbath1 { - ip-address 192.168.2.192 - mac-address cc:8c:bf:2d:b1:c4 - } - static-mapping openmqtt-1-esp32-AAC2CC { - ip-address 192.168.2.185 - mac-address 0c:8b:95:aa:c2:cc - } - static-mapping panasonic-heat-pump-bedroom3 { - ip-address 192.168.2.226 - mac-address e8:c7:cf:59:bc:cc - } - static-mapping panasonic-heat-pump-lounge { - ip-address 192.168.2.225 - mac-address e8:c7:cf:63:f4:ce - } - static-mapping panasonic-heat-pump-masterbedroom { - ip-address 192.168.2.227 - mac-address e8:c7:cf:54:df:70 - } - static-mapping sonoff-ifan02-bed2-7042 { - ip-address 192.168.2.114 - mac-address 60:01:94:ac:db:82 - } - static-mapping sony-playstation4 { - ip-address 192.168.2.19 - mac-address 0c:fe:45:4e:3c:28 - } - static-mapping squeezelite-8a390 { - ip-address 192.168.2.221 - mac-address 24:0a:c4:8a:03:90 - } - static-mapping tasmota-athbulb-2152-1 { - ip-address 192.168.2.121 - mac-address 80:64:6f:9e:28:68 - } - static-mapping tasmota-lc4chan-7725-loungedisplay { - ip-address 192.168.2.148 - mac-address d8:bf:c0:fc:3e:2d - } - static-mapping tasmo-H801-4469-outsideent_lights { - ip-address 192.168.2.125 - mac-address 60:01:94:6f:91:75 - } - static-mapping tasmo-H801-loungeled1-6180-6180 { - ip-address 192.168.2.70 - mac-address 60:01:94:6f:98:24 - } - static-mapping tasmo-arlecrgb-3522-bulb-3-2522 { - ip-address 192.168.2.60 - mac-address ec:fa:bc:72:c9:da - } - static-mapping tasmo-arlec-1923-bulb-2-1923 { - ip-address 192.168.2.117 - mac-address f4:cf:a2:55:c7:83 - } - static-mapping tasmo-arlec-6600-bulb-1-6600 { - ip-address 192.168.2.97 - mac-address f4:cf:a2:5d:19:c8 - } - static-mapping tasmo-hw622-5799-netcupboardfan { - ip-address 192.168.2.64 - mac-address 50:02:91:5f:d6:a7 - } - static-mapping tasmo-ifan02-3793-bedrm1-1 { - ip-address 192.168.2.101 - mac-address 84:0D:8E:47:AE:D1 - } - static-mapping tasmo-ifan03-2541-foxholekitchen { - ip-address 192.168.2.105 - mac-address dc:4f:22:b0:49:ed - } - static-mapping tasmo-kkettle-3773-portable-377 { - ip-address 192.168.2.112 - mac-address e8:68:e7:60:6e:bd - } - static-mapping tasmo-ks811d-1043-hallway-2 { - ip-address 192.168.2.81 - mac-address 2c:f4:32:06:e4:13 - } - static-mapping tasmo-ks811d-1242-entrance-1242 { - ip-address 192.168.2.118 - mac-address 40:f5:20:e0:44:da - } - static-mapping tasmo-ks811d-1701-stairs-2 { - ip-address 192.168.2.86 - mac-address 48:3f:da:16:86:a8 - } - static-mapping tasmo-ks811d-2552-lounge-2-2552 { - ip-address 192.168.2.138 - mac-address 8c:aa:b5:e0:29:f8 - } - static-mapping tasmo-ks811d-3777-bedrm2-1-3777 { - ip-address 192.168.2.77 - mac-address dc:4f:22:72:0e:c1 - } - static-mapping tasmo-ks811d-4381-stairs-1 { - ip-address 192.168.2.82 - mac-address 48:3f:da:16:51:20 - } - static-mapping tasmo-ks811d-5613-lounge-4 { - ip-address 192.168.2.67 - mac-address 8c:aa:b5:df:f5:ed - } - static-mapping tasmo-ks811d-7244-bdrm3-1-7244 { - ip-address 192.168.2.84 - mac-address cc:50:e3:ee:5c:4c - } - static-mapping tasmo-ks811d-7449-mainbath-2 { - ip-address 192.168.2.151 - mac-address 8c:aa:b5:e0:7d:19 - } - static-mapping tasmo-ks811d-7749-hallway-3-774 { - ip-address 192.168.2.85 - mac-address cc:50:e3:ee:5e:45 - } - static-mapping tasmo-ks811s-2940-hallway-1-294 { - ip-address 192.168.2.80 - mac-address 50:02:91:43:0b:7c - } - static-mapping tasmo-ks811s-3075-lounge-1-3075 { - ip-address 192.168.2.98 - mac-address ec:fa:bc:5c:4c:03 - } - static-mapping tasmo-ks811s-3136-downstbed2-1- { - ip-address 192.168.2.75 - mac-address 50:02:91:43:0c:40 - } - static-mapping tasmo-ks811s-3211-lounge-2 { - ip-address 192.168.2.107 - mac-address ec:fa:bc:5c:4c:8b - } - static-mapping tasmo-ks811s-3491-downstloun-1 { - ip-address 192.168.2.73 - mac-address 50:02:91:43:0d:a3 - } - static-mapping tasmo-ks811t-0702-lounge-3-0702 { - ip-address 192.168.2.116 - mac-address 24:62:ab:05:a2:be - } - static-mapping tasmo-ks811t-0707-lounge-2 { - ip-address 192.168.2.83 - mac-address 24:62:ab:05:a2:c3 - } - static-mapping tasmo-ks811t-0804-bedrm2-2 { - ip-address 192.168.2.141 - mac-address 24:62:ab:05:a3:24 - } - static-mapping tasmo-ks811t-3647-bedrm1-1 { - ip-address 192.168.2.78 - mac-address 84:F3:EB:EC:6E:3F - } - static-mapping tasmo-ks811t-3680-bedrm1-2-3680 { - ip-address 192.168.2.95 - mac-address 84:f3:eb:ec:6e:60 - } - static-mapping tasmo-ks811t-4658-lounge-5 { - ip-address 192.168.2.66 - mac-address 84:cc:a8:8c:52:32 - } - static-mapping tasmo-mhled-4905-loungecupboard { - ip-address 192.168.2.109 - mac-address c4:4f:33:d2:53:29 - } - static-mapping tasmo-mhled-5012-breakfastbar { - ip-address 192.168.2.89 - mac-address 24:62:ab:6e:d3:94 - } - static-mapping tasmo-mhled-5436-bookshelves { - ip-address 192.168.2.102 - mac-address 24:62:ab:6e:d5:3c - } - static-mapping tasmo-nspnl-2044-lounge-1-2044 { - ip-address 192.168.2.149 - mac-address 24:d7:eb:0d:e7:fc - } - static-mapping tasmo-ptled-1540-bed3leds { - ip-address 192.168.2.126 - mac-address ac:0b:fb:d3:86:04 - } - static-mapping tasmo-ptled-7673-leds { - ip-address 192.168.2.127 - mac-address ac:0b:fb:d3:1d:f9 - } - static-mapping tasmo-s4chan-7594-garage-1-7594 { - ip-address 192.168.2.92 - mac-address 60:01:94:88:dd:aa - } - static-mapping tasmo-sbasic-5264-garageextlight-1 { - ip-address 192.168.2.87 - mac-address 2c:3a:e8:0f:54:90 - } - static-mapping tasmo-sbas-0407-garagelight-040 { - ip-address 192.168.2.111 - mac-address 5c:cf:7f:3b:c1:97 - } - static-mapping tasmo-sbas-3598-officegpo-2-359 { - ip-address 192.168.2.63 - mac-address 2c:3a:e8:0f:4e:0e - } - static-mapping tasmo-sbas-5072-cupboard1-5072 { - ip-address 192.168.2.94 - mac-address 5c:cf:7f:bb:13:d0 - } - static-mapping tasmo-sbas-7363-officemultiusb- { - ip-address 192.168.2.99 - mac-address 5c:cf:7f:3b:bc:c3 - } - static-mapping tasmo-sdev-0501-laundryenv { - ip-address 192.168.2.124 - mac-address 5c:cf:7f:92:41:f5 - } - static-mapping tasmo-sdev-0778-switch-2-0778 { - ip-address 192.168.2.91 - mac-address 5c:cf:7f:92:43:0a - } - static-mapping tasmo-sdev-8126-gasheater { - ip-address 192.168.2.103 - mac-address 5c:cf:7f:92:3f:be - } - static-mapping tasmo-sdual-4231-underhouselights { - ip-address 192.168.2.122 - mac-address 60:01:94:84:70:87 - } - static-mapping tasmo-slusb-0768-1 { - ip-address 192.168.2.72 - mac-address 2c:f4:32:e5:23:00 - } - static-mapping tasmo-sonrf-0022 { - ip-address 192.168.2.115 - mac-address 60:01:94:8b:60:16 - } - static-mapping tasmo-spow-1957-laundrywash-195 { - ip-address 192.168.2.69 - mac-address 5c:cf:7f:b4:a7:a5 - } - static-mapping tasmo-spow-5526-laundrydry { - ip-address 192.168.2.88 - mac-address 5c:cf:7f:92:35:96 - } - static-mapping tasmo-spow-5747-poolpump-5747 { - ip-address 192.168.2.93 - mac-address 5c:cf:7f:a0:36:73 - } - static-mapping tasmo-wemosd1-7280-powermon-1-7 { - ip-address 192.168.2.96 - mac-address 2c:f4:32:4b:bc:70 - } - static-mapping tasmo-wemoskogan-5020-powermon- { - ip-address 192.168.2.119 - mac-address 2c:f4:32:4b:33:9c - } - static-mapping tasmo-ytfir-a25e21-office-1-771 { - ip-address 192.168.2.113 - mac-address e0:98:06:a2:5e:21 - } - static-mapping tasmo-zigb-6440-1 { - ip-address 192.168.2.104 - mac-address 84:cc:a8:9c:99:28 - } - static-mapping tuya-kogan-smartplug-1-A8F5 { - ip-address 192.168.2.157 - mac-address d4:a6:51:2a:a8:f5 - } - static-mapping tuya-kogan-smartplug-2-C39A { - ip-address 192.168.2.158 - mac-address d4:a6:51:2a:c3:9a - } - static-mapping wled-1-loungedisplay { - ip-address 192.168.2.180 - mac-address 5c:cf:7f:06:53:6d - } - } - } - shared-network-name temp_LAN4 { - authoritative disable - subnet 172.31.0.0/16 { - default-router 172.31.4.25 - dns-server 172.31.4.25 - lease 86400 - start 172.31.25.1 { - stop 172.31.25.254 - } - static-mapping homeassistant { - ip-address 172.31.6.42 - mac-address 44:8a:5b:b6:41:58 - } - static-mapping openhasp_lounge1 { - ip-address 172.31.8.2 - mac-address 98:a3:16:e7:ea:fc - } - } - } - static-arp disable - use-dnsmasq enable - } - dns { - forwarding { - cache-size 150 - listen-on eth1 - listen-on pppoe0 - listen-on switch0 - listen-on eth3 - } - } - gui { - http-port 80 - https-port 443 - older-ciphers enable - } - mdns { - repeater { - interface eth1 - interface switch0 - interface eth3 - interface eth4 - interface eth2 - } - } - nat { - rule 5010 { - description "masquerade for WAN" - log disable - outbound-interface pppoe0 - protocol all - source { - group { - address-group LAN_Interfaces_for_NAT - } - } - type masquerade - } - } - snmp { - community { - authorization ro - client 172.31.6.42 - } - listen-address 192.168.1.25 { - port 161 - } - } - ssh { - port 22 - protocol-version v2 - } - unms { - disable - } -} -system { - analytics-handler { - send-analytics-report false - } - crash-handler { - send-crash-report false - } - domain-name foxhome - host-name ubnt - ipv6 { - } - login { - user automated_rules { - authentication { - encrypted-password - plaintext-password "" - public-keys edgerouterfirewall { - key AAAAB3NzaC1yc2EAAAADAQABAAABgQDCtS6cCLDEXkVTYkh0ihU3YZ5pGh5ehgr2OJjC7Vwf3cA+ppWqVYVvVnV0UguLEqOLFGBKbgQ9izrz29aDVhFfGfoL3n/6zf5YbMOwbh2K8xgK3lZYhTOcW0wO/nBsI2bU2UNgNbl2wVQ90M0bc629mRgp3FDX0sekDYmOBsXw84uEkk38+eyGuI38oBMEZiVOgF4OAnd1KG5eWgH20ygEIqSYHAeCR24xLKUWJj+wds2JOFasAc1bAGEN3rG2dau94z8g0X1a728pVyrPcfl6hcSuAUfgbX2vfa4tAoe4KBEIRroW8SIFhPqtFO6XG2bX/vjHOQfzTfpSz9P1OjMvEBlt2OEYHJiKBldGgIipJO7tX+LolmYbdOOHWY9kxQv6ecTvZ8Jli7hxzxqwCdwn37XHP+ND09HGMP9GuxRCwpIXCMgOGi+9R/bjB7HQ340QNq4gawxjsPt/8ho0MIUbmv5m2nMZE0EQSpkefjtAscs8Od6pnl5g3xqplAsdSuM= - type ssh-rsa - } - public-keys linux { - key AAAAB3NzaC1yc2EAAAADAQABAAABgQDCtS6cCLDEXkVTYkh0ihU3YZ5pGh5ehgr2OJjC7Vwf3cA+ppWqVYVvVnV0UguLEqOLFGBKbgQ9izrz29aDVhFfGfoL3n/6zf5YbMOwbh2K8xgK3lZYhTOcW0wO/nBsI2bU2UNgNbl2wVQ90M0bc629mRgp3FDX0sekDYmOBsXw84uEkk38+eyGuI38oBMEZiVOgF4OAnd1KG5eWgH20ygEIqSYHAeCR24xLKUWJj+wds2JOFasAc1bAGEN3rG2dau94z8g0X1a728pVyrPcfl6hcSuAUfgbX2vfa4tAoe4KBEIRroW8SIFhPqtFO6XG2bX/vjHOQfzTfpSz9P1OjMvEBlt2OEYHJiKBldGgIipJO7tX+LolmYbdOOHWY9kxQv6ecTvZ8Jli7hxzxqwCdwn37XHP+ND09HGMP9GuxRCwpIXCMgOGi+9R/bjB7HQ340QNq4gawxjsPt/8ho0MIUbmv5m2nMZE0EQSpkefjtAscs8Od6pnl5g3xqplAsdSuM==edgerouterfirewall - type ssh-rsa - } - } - full-name automated_rules - level admin - } - user homeassistant { - authentication { - encrypted-password - plaintext-password "" - } - full-name zorruno - level operator - } - user zorruno { - authentication { - encrypted-password - plaintext-password "" - public-keys donkey { - key AAAAB3NzaC1yc2EAAAADAQABAAABgQDusoL6L/UtdQLswOlF/RQMjx1FGnM6lbjYVfnM9vWGHPg5m0Adx0SQQOIL4oAIj1UwJOvkzXl9UxvyW4rGjJGpkWeypGRcwJeoO3RcnjSHBNKz2IwMaMyRzqQL9GqMa9FvAzQVzk6ndDkBgQoNfn3oeid1PxSCbt6qDfVGqOWlTHUbRrgMlulwq3nkjHWFtu45i0VcySZYSl3byWuc/xpx6SWz0otVuXEzD+pc2PSO67kLNGPxE/Jq4FS8YWQszyw1yWg/rw9QNS+ft8cc0KkxjXjUTjuYMpdcMyLLrq+dgzcB8vUTcYMZx1L5gtBLDqEOK6wPLQgjauPdUPxw7xgQwrB2BXY3U+z+AzQHMu/EFoLtZiYOhyMaWS4xwiERzGbw0M6fb9YXjLo2nK3HlXNuwqka0fptHv3HFNoTMqzRJ5arukaEdqDZIsTI/myan7p8Sq9sqnu3MqFmoCkOjTaNBofDXwRsR1FAJwAzCmVq4ucSUQqYt87Ybjn/LHLvDU0== - type ssh-rsa - } - public-keys howard@donkey { - key AAAAB3NzaC1yc2EAAAADAQABAAABgQCiGMm9Tsm108FmXFM86NQwVKJgEg3U+QPTzde0UXj11rCnDkVHM4JmfgZ6qGbQbeYF0IZ7/Rx3lncS7v6VQ1Mg75uEmSy9U8TfYDKnYb97RhK8rD8oFCJjekF1q9zLP2oWI9zlVNkwO8YTZy/5KvJxgzI9THx60RlNieMgUzAma7FtUsuYniPlUfle92I9s4BFHIp/9kDhTBxVUUofU01+JPWceAdVyzaEp651z5Vxzpce8iLkoXyw86tWb6jCDHo4K9nDsIqYzMTqSJXcM00Ak5ZU7ZTpI/6xs1P5K0nvz8Oi1QRHYEhZivEdpAgPHR2ldPi2Nkh7f2+1mi5iT8ytunrpk24Qj4ePQqzNkl8NNue5dlsLLyBpiIpa4DNyT3x2XV5mQtruSixZn/bTX0LidviYBJulKfZm2/Wd0Z1xfpmB+Iid3vXuv4j8YdhCLiDj7jmU0O3qD+1SiAOHeL/7KvxLCn00JhsSOYbOBR33sVWlRIPUji3a1qUOEr7VxdM= - type ssh-rsa - } - } - full-name zorruno - level admin - } - } - name-server 127.0.0.1 - name-server 8.8.8.8 - ntp { - server 0.ubnt.pool.ntp.org { - } - server 1.ubnt.pool.ntp.org { - } - server 2.ubnt.pool.ntp.org { - } - server 3.ubnt.pool.ntp.org { - } - } - offload { - hwnat enable - ipsec enable - } - package { - repository blacklist { - components main - description "Britannic blacklist debian stretch repository" - distribution stretch - password "" - url https://raw.githubusercontent.com/britannic/debian-repo/master/blacklist/public/ - username "" - } - } - static-host-mapping { - host-name homeassistant.local { - alias homeassistant - inet 192.168.1.42 - } - host-name panda.foxhome3 { - alias panda - inet 192.168.3.200 - } - } - syslog { - global { - facility all { - level notice - } - facility protocols { - level debug - } - } - } - task-scheduler { - task update_blacklists { - executable { - arguments 10800 - path /config/scripts/update-dnsmasq-cronjob.sh - } - interval 1d - } - } - time-zone Pacific/Auckland - traffic-analysis { - dpi enable - export enable - } -} - - -/* Warning: Do not remove the following line. */ -/* === vyatta-config-version: "config-management@1:conntrack@1:cron@1:dhcp-relay@1:dhcp-server@4:firewall@5:ipsec@5:nat@3:qos@1:quagga@2:suspend@1:system@5:ubnt-l2tp@1:ubnt-pptp@1:ubnt-udapi-server@1:ubnt-unms@2:ubnt-util@1:vrrp@1:vyatta-netflow@1:webgui@1:webproxy@1:zone-policy@1" === */ -/* Release version: v3.0.1.5862409.250924.1407 */ diff --git a/configuration.yaml.bak b/configuration.yaml.bak deleted file mode 100644 index b475938..0000000 --- a/configuration.yaml.bak +++ /dev/null @@ -1,44 +0,0 @@ -# Configure a default setup of Home Assistant (frontend, api, etc) -default_config: - -# Helpers (default_config no longer loads input_number) -input_number: - -frontend: - themes: !include_dir_merge_named themes - -homeassistant: - customize: !include customize.yaml - packages: !include_dir_named packages - -template: !include_dir_merge_list templates -group: !include_dir_merge_named group/ -automation: !include_dir_merge_list automations/ -script: !include_dir_merge_named scripts/ -scene: !include scenes.yaml -sensor: !include_dir_merge_list sensor/ - -conversation: - -logger: - default: info - -zha: - enable_quirks: true - custom_quirks_path: /config/custom_zha_quirks/ - device_config: - a4:c1:38:02:58:65:86:ed-1: # format: {ieee}-{endpoint_id} - type: "switch" # corrected device type - a4:c1:38:a1:db:1d:38:b7-1: # format: {ieee}-{endpoint_id} - type: "switch" # corrected device type - zigpy_config: - ota: - otau_directory: /config/zigpy_ota - ikea_provider: true - ledvance_provider: true - salus_provider: true - inovelli_provider: true - thirdreality_provider: true - -device_tracker: - - platform: bluetooth_le_tracker diff --git a/custom_zha_quirks/ts0207_rain.py.old b/custom_zha_quirks/ts0207_rain.py.old deleted file mode 100644 index 63dee78..0000000 --- a/custom_zha_quirks/ts0207_rain.py.old +++ /dev/null @@ -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, - ], - }, - }, - } \ No newline at end of file diff --git a/custom_zha_quirks/ts0601_motion copy.py.old b/custom_zha_quirks/ts0601_motion copy.py.old deleted file mode 100644 index 2fb2d54..0000000 --- a/custom_zha_quirks/ts0601_motion copy.py.old +++ /dev/null @@ -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() -) \ No newline at end of file diff --git a/custom_zha_quirks/ts0601_power_din_tuya.py.old b/custom_zha_quirks/ts0601_power_din_tuya.py.old deleted file mode 100644 index ac5fed2..0000000 --- a/custom_zha_quirks/ts0601_power_din_tuya.py.old +++ /dev/null @@ -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": "", - # device_version=1 - # input_clusters=[0x0000, 0x0004, 0x0005, 0xef00] - # output_clusters=[0x000a, 0x0019] - MODELS_INFO: [ - ("_TZE200_byzdayie", "TS0601"), - ("_TZE200_ewxhg6o9", "TS0601"), - ], - ENDPOINTS: { - # - 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": "", - # device_version=1 - # input_clusters=[0x0000, 0x0004, 0x0005, 0xef00] - # output_clusters=[0x000a, 0x0019] - MODELS_INFO: [ - ("_TZE200_bkkmqmyo", "TS0601"), - ("_TZE204_81yrt3lo", "TS0601"), - ], - ENDPOINTS: { - # - 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: [], - }, - } - } \ No newline at end of file diff --git a/custom_zha_quirks/ts0601_smoke.py.old b/custom_zha_quirks/ts0601_smoke.py.old deleted file mode 100644 index 5650bdc..0000000 --- a/custom_zha_quirks/ts0601_smoke.py.old +++ /dev/null @@ -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, - ], - }, - }, - } \ No newline at end of file diff --git a/custom_zha_quirks/ts0601_valve_garden.py.old b/custom_zha_quirks/ts0601_valve_garden.py.old deleted file mode 100644 index 40c7430..0000000 --- a/custom_zha_quirks/ts0601_valve_garden.py.old +++ /dev/null @@ -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: { - # - 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], - } - } - } diff --git a/esphome/esp-gasheater copy.yaml.old b/esphome/esp-gasheater copy.yaml.old deleted file mode 100644 index 2d045e7..0000000 --- a/esphome/esp-gasheater copy.yaml.old +++ /dev/null @@ -1,1342 +0,0 @@ -#:########################################################################################:# -# TITLE: -# RINNAI NEO GAS HEATER CONTROLLER -#:########################################################################################:# -# REPO: -# https://home.fox.co.nz/gitea/zorruno/zorruno-homeassistant/src/branch/master/esphome/esp-gasheater.yaml -#:########################################################################################:# -# VERSIONS: -# V1.11 2026-04-26 Restored Heater Operation after regression; restored fast optimistic -# master setpoint number with debounced MQTT publish; restored proportional -# flame tuning; kept current GPIO12/13/14 relay mapping -# V1.10 2026-04-25 Reworked OFF-state enforcement so non-BYPASS modes actively force the -# heater fully OFF when power is intended OFF; reboot now forces heater -# OFF before returning to BYPASS (Remote Only) -# V1.8 2026-04-21 Physical testing, swapped GPIO12 and 14 (had the buttons/relays inverted) -# V1.7 2026-03-26 Added Heater Operation switch; MQTT status now mirrors the Status Text -# entity; preserved Test Mode AUTO test temperature and visible 6-step calibration -# V1.6 2026-03-22 Added richer status text: At Temperature, Operating (Level X), -# Calibrating, and Fault (Room Temperature Lost) -# V1.5 2026-03-22 Added Test Temperature Value slider for Test Mode AUTO; status text -# shows Test Mode state; temperature units set to °C -# V1.4 2026-03-22 Updated yaml to zorruno layout V1.1; boot into BYPASS (Remote Only); -# renamed remote-only mode; refined Test Mode to click real power relay; -# added setpoint text and setpoint up/down buttons -# V1.3 2026-03-22 Refined Test Mode so power relay still clicks; added setpoint text plus -# setpoint up/down buttons; Test Mode can now be used for MANUAL or AUTO testing -# V1.2 2026-03-22 Added Test Mode bench testing override -# V1.1 2026-03-22 Updated yaml to zorruno layout V1.1; forced AUTO on reboot; initialized -# stale-temp timer at boot; cleared Remote Control Only self-power flag -# V1.0 2025-07-21 First setup (replacement of Tasmota) -#:########################################################################################:# -# HARDWARE: -# - Sonoff DEV (ESP8266) -# - 3-relay daughterboard -# - Rinnai Neo gas heater -# - Relay outputs: -# - GPIO14 = ON/OFF button press -# - GPIO13 = Flame UP button press -# - GPIO12 = Flame DOWN button press -# - LED inputs: -# - GPIO4 = Red standby LED -# - GPIO5 = Blue operating LED -#:########################################################################################:# -# OPERATION NOTES: -# - Replaces the RF remote by pressing the heater panel buttons via relays. -# - Heater manages its own ignition, flame safety, and fan control. -# - AUTO mode uses: -# - Local HA setpoint number entity -# - External room temperature via MQTT -# - In Test Mode, AUTO uses the local Test Temperature Value slider instead of MQTT room -# temperature. -# - Setpoint is locally adjustable and also supports two-way MQTT sync. -# - Manual flame control is available via: -# - Flame Level select (1..7) -# - Flame Up step button -# - Flame Down step button -# - Setpoint helper entities are available via: -# - Setpoint Text -# - Setpoint Up (+${setpoint_step_c} °C) -# - Setpoint Down (-${setpoint_step_c} °C) -# - Test Mode is for bench testing before connection to the heater: -# - Forces Mode to MANUAL when enabled -# - Uses an internal powered flag instead of LED monitoring -# - Still clicks the real power relay when Power is turned ON or OFF -# - AUTO may still be selected manually later for testing -# - Heater Operation is a high-level thermostat enable helper: -# - ON = turn Test Mode OFF (if needed) and set Mode to AUTO -# - OFF = force heater Power OFF and set Mode to BYPASS (Remote Only) -# - In any mode except BYPASS (Remote Only), if the controller intends heater power OFF -# but either heater LED still shows the heater ON, ESPHome will pulse the heater ON/OFF -# button to force it fully OFF. -# - On every ESPHome restart (including yaml upload or power loss), the controller first -# forces the heater OFF, then returns to BYPASS (Remote Only). -# - On power ON in AUTO, the controller: -# - Applies a warmup lockout -# - Calibrates to level 1 using 6x DOWN -# - Begins auto level adjustment -# - If the heater is turned on externally while in AUTO and the controller intended it -# to be ON, the controller will re-baseline after warmup and resume automatic control. -# - Fast-ramp mode allows faster flame stepping for a short period after turn-on. -#:########################################################################################:# -# MQTT COMMANDS: -# - Room temperature input: -# viewroad-status/lounge-environment/temperature -# - Setpoint input/output: -# viewroad-status/lounge-environment/temp-setpoint -# - Heater operating status output: -# viewroad-status/lounge-gasheater -#:########################################################################################:# -# OFFLINE NOTES: -# a) HA offline, WiFi/MQTT online: -# - Device continues to operate normally. -# - AUTO control still works if room temperature MQTT is still being published. -# - Setpoint can still be changed via MQTT. -# -# b) MQTT offline (or HA and MQTT offline): -# - No fresh room temperature or setpoint updates are received. -# - Device retains the last local setpoint. -# - If AUTO is active and temperature data becomes stale, the heater will turn OFF after -# temp_stale_timeout_min as a fail-safe. -# - Manual mode and flame tracking still work locally inside the ESP device logic. -# -# c) Entire WiFi/Network offline: -# - No MQTT temperature or setpoint updates are available. -# - Accurate time is not needed. SNTP is not required for this device. -# - If AUTO is active and the heater is ON, stale temperature fail-safe will turn it OFF -# after the configured timeout. -# - Existing heater safety systems still operate normally. -#:########################################################################################:# - -########################################################################################## -# SPECIFIC DEVICE VARIABLE SUBSTITUTIONS -########################################################################################## -substitutions: - # Device Naming - device_name: "esp-gasheater" - friendly_name: "Rinnai Neo Gas Heater" - description_comment: "7 Setting controller for Rinnai Neo Gas heater :: Sonoff DEV + Relay Board" - device_area: "Lounge" - - # Project Naming - project_name: "Sonoff Technologies.Sonoff DEV" - project_version: "v1.11" - - # Passwords & Secrets - api_key: !secret esp-api_key - ota_pass: !secret esp-ota_pass - static_ip_address: !secret esp-gasheater_ip - #mqtt_local_command_topic: !secret mqtt_command_main_topic - #mqtt_local_status_topic: !secret mqtt_status_main_topic - - # 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" - update_interval: "60s" - - ######################################################################################## - # TIMING TUNABLES - ######################################################################################## - relay_pulse_ms: "250" - min_click_gap_ms: "1200" - warmup_seconds: "30" - control_interval_s: "10" - adjust_step_interval_s: "10" - recalibrate_interval_min: "180" - temp_stale_timeout_min: "20" - fast_ramp_seconds: "240" - fast_ramp_step_s: "4" - setpoint_mqtt_debounce_ms: "500" - power_off_verify_delay_s: "2" - power_off_check_interval_s: "30" - boot_force_off_attempts: "3" - - # Optional anti short-cycle (set to "0" to disable either one) - min_on_minutes: "1" - min_off_minutes: "0.5" - - ######################################################################################## - # ELECTRICAL POLARITY - ######################################################################################## - relay_inverted: "true" - red_led_inverted: "true" - blue_led_inverted: "true" - - ######################################################################################## - # THERMOSTAT HYSTERESIS (°C) - ######################################################################################## - on_below_c: "0.2" - off_above_c: "0.3" - - ######################################################################################## - # SETPOINT LIMITS FOR HA NUMBER (°C) - ######################################################################################## - setpoint_min_c: "6.0" - setpoint_max_c: "32.0" - setpoint_step_c: "0.2" - - - ######################################################################################## - # PROPORTIONAL FLAME CONTROL - # - proportional_aggression = 1.0 keeps the base thresholds unchanged - # - >1.0 makes AUTO more aggressive (higher flame for the same temperature error) - # - <1.0 makes AUTO softer - # - individual flame_level_X_diff_c values can still be fine tuned - ######################################################################################## - proportional_aggression: "3.0" - flame_level_7_diff_c: "3.0" - flame_level_6_diff_c: "2.5" - flame_level_5_diff_c: "2.0" - flame_level_4_diff_c: "1.5" - flame_level_3_diff_c: "1.0" - flame_level_2_diff_c: "0.5" - -########################################################################################## -# PACKAGES: Included Common Packages -########################################################################################## -packages: - 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}" - - common_api: !include - file: common/api_common.yaml - vars: - local_api_key: "${api_key}" - - common_mqtt: !include - file: common/mqtt_common.yaml - vars: - local_device_name: "${device_name}" - - #### WEB PORTAL #### - #common_webportal: !include common/webportal_common.yaml - - common_sntp: !include - file: common/sntp_common.yaml - - common_general_sensors: !include - file: common/sensors_common.yaml - vars: - local_friendly_name: "${friendly_name}" - local_update_interval: "${update_interval}" - -########################################################################################## -# ESPHOME -########################################################################################## -esphome: - name: "${device_name}" - friendly_name: "${friendly_name}" - comment: "${description_comment}" - area: "${device_area}" - - on_boot: - priority: -100 - then: - - lambda: |- - // After any ESPHome restart, the controller must return to a safe OFF state. - id(power_intended_on) = false; - - // Keep the internal float mirror aligned with the user-facing number entity. - id(setpoint_c) = id(setpoint_number).state; - - id(last_temp_ms) = millis(); - id(warmup_until_ms) = millis() + (${warmup_seconds} * 1000UL); - - script.execute: recompute_powered_state - - delay: 1s - - script.execute: boot_force_heater_off - -########################################################################################## -# ESP PLATFORM -########################################################################################## -esp8266: - board: esp01_1m - restore_from_flash: true - -preferences: - flash_write_interval: 5min - -mdns: - disabled: true - -########################################################################################## -# GLOBAL VARIABLES -########################################################################################## -globals: - - id: current_level - type: int - restore_value: yes - initial_value: "1" - - - id: desired_level - type: int - restore_value: no - initial_value: "1" - - - id: last_adjust_ms - type: uint32_t - restore_value: no - initial_value: "0" - - - id: last_recal_ms - type: uint32_t - restore_value: no - initial_value: "0" - - - id: last_on_ms - type: uint32_t - restore_value: no - initial_value: "0" - - - id: last_off_ms - type: uint32_t - restore_value: no - initial_value: "0" - - - id: setpoint_c - type: float - restore_value: yes - initial_value: "21.0" - - - id: sp_suppress_mqtt - type: bool - restore_value: no - initial_value: "false" - - - id: last_temp_ms - type: uint32_t - restore_value: no - initial_value: "0" - - - id: warmup_until_ms - type: uint32_t - restore_value: no - initial_value: "0" - - - id: last_level_target - type: int - restore_value: no - initial_value: "1" - - - id: target_confirmed - type: bool - restore_value: no - initial_value: "true" - - - id: ramp_until_ms - type: uint32_t - restore_value: no - initial_value: "0" - - - id: self_power_on - type: bool - restore_value: no - initial_value: "false" - - # Controller intent for heater power in non-BYPASS modes. - # false = heater should be OFF and ESPHome will enforce OFF if LEDs still show ON. - - id: power_intended_on - type: bool - restore_value: no - initial_value: "false" - - # Bench-test logical power state used only when Test Mode is enabled - - id: test_powered - type: bool - restore_value: no - initial_value: "false" - - # Bench-test room temperature used only when Test Mode is enabled - - id: test_temp_c - type: float - restore_value: yes - initial_value: "20.0" - - # True while a calibration sequence is actively stepping down to level 1 - - id: calibrating - type: bool - restore_value: no - initial_value: "false" - - # True when remote MQTT room temperature has gone stale in normal (non-test) mode - - id: room_temp_lost_fault - type: bool - restore_value: no - initial_value: "false" - -########################################################################################## -# ESPHOME LOGGING ENABLE -########################################################################################## -logger: - level: "${log_level}" - baud_rate: 0 - -########################################################################################## -# BINARY SENSORS -########################################################################################## -binary_sensor: - - platform: gpio - id: led_standby_red - name: "${friendly_name} Standby LED" - pin: - number: GPIO4 - mode: - input: true - pullup: true - inverted: ${red_led_inverted} - filters: - - delayed_on_off: 50ms - #entity_category: diagnostic - on_state: - - script.execute: recompute_powered_state - - - platform: gpio - id: led_operating_blue - name: "${friendly_name} Operating LED" - pin: - number: GPIO5 - mode: - input: true - pullup: true - inverted: ${blue_led_inverted} - filters: - - delayed_on_off: 50ms - #entity_category: diagnostic - on_state: - - script.execute: recompute_powered_state - - - platform: template - id: powered_state - internal: true - on_press: - - lambda: |- - id(last_on_ms) = millis(); - if (!id(test_mode_enabled).state && id(mode_select).state == "AUTO") { - id(warmup_until_ms) = millis() + (${warmup_seconds} * 1000UL); - } - - if: - condition: - lambda: 'return !id(test_mode_enabled).state && id(mode_select).state != "BYPASS (Remote Only)" && !id(power_intended_on) && !id(self_power_on);' - then: - - script.execute: enforce_heater_off - - if: - condition: - lambda: 'return !id(test_mode_enabled).state && (id(mode_select).state == "AUTO") && id(power_intended_on) && (!id(self_power_on));' - then: - - lambda: |- - id(ramp_until_ms) = millis() + (${fast_ramp_seconds} * 1000UL); - - delay: ${warmup_seconds}s - - script.execute: calibrate_to_min - - script.execute: auto_control_tick - - script.execute: adjust_to_desired - - on_release: - - lambda: |- - id(last_off_ms) = millis(); - id(self_power_on) = false; - -########################################################################################## -# SWITCH COMPONENT -########################################################################################## -switch: - - platform: gpio - id: sw_power_raw - internal: true - restore_mode: ALWAYS_OFF - pin: - number: GPIO14 - inverted: ${relay_inverted} - - - platform: gpio - id: sw_up_raw - internal: true - restore_mode: ALWAYS_OFF - pin: - number: GPIO13 - inverted: ${relay_inverted} - - - platform: gpio - id: sw_down_raw - internal: true - restore_mode: ALWAYS_OFF - pin: - number: GPIO12 - inverted: ${relay_inverted} - - - platform: template - id: test_mode_enabled - name: "${friendly_name} Test Mode" - icon: mdi:test-tube - optimistic: true - restore_mode: ALWAYS_OFF - internal: true - turn_on_action: - - lambda: |- - // Preserve current logical power state when entering Test Mode - id(test_powered) = id(powered_state).state; - id(room_temp_lost_fault) = false; - - select.set: - id: mode_select - option: "MANUAL" - - script.execute: recompute_powered_state - turn_off_action: - - lambda: |- - id(test_powered) = false; - - script.execute: recompute_powered_state - - # High-level thermostat enable helper. - # ON = thermostat logic active in AUTO - # OFF = force BYPASS and turn heater power OFF - - platform: template - id: heater_operation_switch - name: "Heater Operation" - icon: mdi:radiator - lambda: |- - return id(mode_select).state == "AUTO"; - - turn_on_action: - - if: - condition: - switch.is_on: test_mode_enabled - then: - - switch.turn_off: test_mode_enabled - - select.set: - id: mode_select - option: "AUTO" - - turn_off_action: - - lambda: |- - id(power_intended_on) = false; - - switch.turn_off: power_switch - - select.set: - id: mode_select - option: "BYPASS (Remote Only)" - - - platform: template - id: power_switch - name: "${friendly_name} Activate Flame" - icon: mdi:power - lambda: |- - return id(powered_state).state; - turn_on_action: - - lambda: |- - if (id(mode_select).state != "BYPASS (Remote Only)" || id(test_mode_enabled).state) { - id(power_intended_on) = true; - } - - if: - condition: - lambda: 'return id(mode_select).state == "BYPASS (Remote Only)" && !id(test_mode_enabled).state;' - then: - - logger.log: "BYPASS (Remote Only): ignoring Power ON." - else: - - if: - condition: - lambda: |- - return !id(powered_state).state; - then: - - script.execute: press_power - - if: - condition: - lambda: 'return id(test_mode_enabled).state;' - then: - - lambda: |- - id(test_powered) = true; - - script.execute: recompute_powered_state - - script.execute: after_power_on_reset_to_auto - - turn_off_action: - - lambda: |- - if (id(mode_select).state != "BYPASS (Remote Only)" || id(test_mode_enabled).state) { - id(power_intended_on) = false; - } - - if: - condition: - lambda: 'return id(mode_select).state == "BYPASS (Remote Only)" && !id(test_mode_enabled).state;' - then: - - logger.log: "BYPASS (Remote Only): ignoring Power OFF." - else: - - if: - condition: - lambda: |- - return id(powered_state).state; - then: - - script.execute: press_power - - if: - condition: - lambda: 'return id(test_mode_enabled).state;' - then: - - lambda: |- - id(test_powered) = false; - - script.execute: recompute_powered_state - -########################################################################################## -# SENSORS -########################################################################################## -sensor: - - platform: mqtt_subscribe - id: room_temp - name: "${friendly_name} Room Temperature (MQTT)" - topic: "viewroad-status/lounge-environment/temperature" - unit_of_measurement: "°C" - accuracy_decimals: 1 - on_value: - - lambda: |- - id(last_temp_ms) = millis(); - id(room_temp_lost_fault) = false; - float v = x; - if (v < -10.0f) v = -10.0f; - if (v > 50.0f) v = 50.0f; - if (v != id(room_temp).state) id(room_temp).publish_state(v); - - - platform: mqtt_subscribe - id: setpoint_from_mqtt - internal: true - topic: "viewroad-command/lounge-gasheater/setpoint" - unit_of_measurement: "°C" - accuracy_decimals: 1 - on_value: - - lambda: |- - float v = x; - const float vmin = ${setpoint_min_c}; - const float vmax = ${setpoint_max_c}; - const float step = ${setpoint_step_c}; - if (v < vmin) v = vmin; - if (v > vmax) v = vmax; - v = roundf(v / step) * step; - - id(sp_suppress_mqtt) = true; - - number.set: - id: setpoint_number - value: !lambda |- - float v = x; - const float vmin = ${setpoint_min_c}; - const float vmax = ${setpoint_max_c}; - const float step = ${setpoint_step_c}; - if (v < vmin) v = vmin; - if (v > vmax) v = vmax; - v = roundf(v / step) * step; - return v; - - lambda: |- - id(sp_suppress_mqtt) = false; - -########################################################################################## -# TEXT SENSOR COMPONENT -########################################################################################## -text_sensor: - - platform: template - id: status_text - name: "${friendly_name} Status Text" - update_interval: 1s - lambda: |- - const bool use_test_temp = id(test_mode_enabled).state; - const float effective_t = use_test_temp ? id(test_temp_c) : id(room_temp).state; - const float at_temp_threshold = id(setpoint_c) + ${off_above_c}; - const std::string mode = id(mode_select).state; - - if (!use_test_temp && id(room_temp_lost_fault)) { - return std::string("Fault (Room Temperature Lost)"); - } - - if (id(calibrating)) { - if (use_test_temp) return std::string("Calibrating (Test)"); - return std::string("Calibrating"); - } - - if (use_test_temp) { - if (mode == "MANUAL") { - if (id(powered_state).state) { - char buf[48]; - snprintf(buf, sizeof(buf), "Manual (Level %d) (Test)", id(current_level)); - return std::string(buf); - } - if (id(led_standby_red).state) return std::string("Manual (Standby) (Test)"); - return std::string("Manual (Off) (Test)"); - } - - if (id(powered_state).state) { - char buf[40]; - snprintf(buf, sizeof(buf), "Operating (Level %d) (Test)", id(current_level)); - return std::string(buf); - } - if (mode == "AUTO" && !isnan(effective_t) && effective_t >= at_temp_threshold) { - return std::string("At Temperature (Test)"); - } - return std::string("Off (Test)"); - } - - if (mode == "BYPASS (Remote Only)") { - if (id(led_operating_blue).state) return std::string("Remote Only (Operating)"); - if (id(led_standby_red).state) return std::string("Remote Only (Standby)"); - return std::string("Remote Only (Off)"); - } - - if (mode == "MANUAL") { - if (id(led_operating_blue).state) { - char buf[32]; - snprintf(buf, sizeof(buf), "Manual (Level %d)", id(current_level)); - return std::string(buf); - } - if (id(led_standby_red).state) return std::string("Manual (Standby)"); - return std::string("Manual (Off)"); - } - - if (id(led_operating_blue).state) { - char buf[32]; - snprintf(buf, sizeof(buf), "Operating (Level %d)", id(current_level)); - return std::string(buf); - } - if (!isnan(effective_t) && effective_t >= at_temp_threshold) { - return std::string("At Temperature"); - } - if (id(led_standby_red).state) return std::string("Standby"); - return std::string("Standby"); - on_value: - - script.execute: publish_status - - - platform: template - id: setpoint_text - name: "Setpoint Text" - icon: mdi:thermometer - update_interval: 500ms - lambda: |- - char buf[16]; - snprintf(buf, sizeof(buf), "%.1f °C", id(setpoint_number).state); - return std::string(buf); - - - platform: template - id: test_temperature_text - name: "${friendly_name} Test Mode Temp" - icon: mdi:thermometer-chevron-up - update_interval: 1s - internal: true - lambda: |- - char buf[20]; - snprintf(buf, sizeof(buf), "%.1f °C", id(test_temp_c)); - return std::string(buf); - -########################################################################################## -# SELECT COMPONENT -########################################################################################## -select: - - platform: template - id: mode_select - name: "Mode" - options: ["AUTO","MANUAL","BYPASS (Remote Only)"] - optimistic: true - initial_option: "BYPASS (Remote Only)" - restore_value: false - set_action: - - lambda: |- - if (x == "BYPASS (Remote Only)") { - id(power_intended_on) = false; - } - if (x != "AUTO") { - id(room_temp_lost_fault) = false; - } - - - platform: template - id: flame_level_select - name: "${friendly_name} Flame Level" - options: ["1","2","3","4","5","6","7"] - optimistic: true - restore_value: true - set_action: - - if: - condition: - lambda: |- - int sel = atoi(x.c_str()); - return sel == id(current_level); - then: - - logger.log: "Flame Level: no change; ignoring selection." - else: - - if: - condition: - lambda: 'return id(mode_select).state != "BYPASS (Remote Only)";' - then: - - select.set: - id: mode_select - option: "MANUAL" - - lambda: |- - id(power_intended_on) = true; - int target = atoi(x.c_str()); - if (target < 1) target = 1; - if (target > 7) target = 7; - id(desired_level) = target; - - if: - condition: - lambda: 'return !id(powered_state).state;' - then: - - script.execute: press_power - - if: - condition: - lambda: 'return id(test_mode_enabled).state;' - then: - - lambda: |- - id(test_powered) = true; - - script.execute: recompute_powered_state - - if: - condition: - lambda: 'return !id(test_mode_enabled).state;' - then: - - lambda: |- - id(warmup_until_ms) = millis() + (${warmup_seconds} * 1000UL); - id(ramp_until_ms) = millis() + (${fast_ramp_seconds} * 1000UL); - - delay: ${warmup_seconds}s - - script.execute: calibrate_to_min - - script.execute: adjust_to_desired - else: - - logger.log: "Test Mode active: logical power now ON." - - script.execute: adjust_to_desired - else: - - script.execute: adjust_to_desired - else: - - logger.log: "BYPASS (Remote Only): ignoring Flame Level selection." - -########################################################################################## -# BUTTON COMPONENT -########################################################################################## -button: - - platform: template - id: btn_flame_up - name: "${friendly_name} Flame Up (step)" - on_press: - - if: - condition: - lambda: 'return id(mode_select).state != "BYPASS (Remote Only)";' - then: - - lambda: |- - id(power_intended_on) = true; - - select.set: - id: mode_select - option: "MANUAL" - - script.execute: step_up_once - else: - - logger.log: "BYPASS (Remote Only): ignoring Flame Up (step)." - - - platform: template - id: btn_flame_down - name: "${friendly_name} Flame Down (step)" - on_press: - - if: - condition: - lambda: 'return id(mode_select).state != "BYPASS (Remote Only)";' - then: - - lambda: |- - id(power_intended_on) = true; - - select.set: - id: mode_select - option: "MANUAL" - - script.execute: step_down_once - else: - - logger.log: "BYPASS (Remote Only): ignoring Flame Down (step)." - - - platform: template - id: btn_calibrate_min - name: "${friendly_name} Calibrate: Force Level 1 (6x down)" - entity_category: config - on_press: - - script.execute: calibrate_to_min - - - platform: template - id: btn_setpoint_up - name: "${friendly_name} Setpoint Up" - icon: mdi:plus - on_press: - - number.set: - id: setpoint_number - value: !lambda |- - const float step = ${setpoint_step_c}; - float v = id(setpoint_number).state + step; - const float vmax = ${setpoint_max_c}; - if (v > vmax) v = vmax; - v = roundf(v / step) * step; - return v; - - - platform: template - id: btn_setpoint_down - name: "${friendly_name} Setpoint Down" - icon: mdi:minus - on_press: - - number.set: - id: setpoint_number - value: !lambda |- - const float step = ${setpoint_step_c}; - float v = id(setpoint_number).state - step; - const float vmin = ${setpoint_min_c}; - if (v < vmin) v = vmin; - v = roundf(v / step) * step; - return v; - -########################################################################################## -# NUMBER COMPONENT -########################################################################################## -number: - # User-facing setpoint control. - # This number is the master source of truth for the setpoint so HA/OpenHASP reacts immediately. - - platform: template - id: setpoint_number - name: "${friendly_name} Setpoint" - icon: mdi:thermometer - unit_of_measurement: "°C" - min_value: ${setpoint_min_c} - max_value: ${setpoint_max_c} - step: ${setpoint_step_c} - mode: slider - optimistic: true - restore_value: true - initial_value: 21.0 - set_action: - - lambda: |- - const float step = ${setpoint_step_c}; - id(setpoint_c) = roundf(x / step) * step; - - if: - condition: - lambda: |- - return !id(sp_suppress_mqtt); - then: - - script.execute: publish_setpoint_mqtt - - # Bench-test room temperature slider used by AUTO whenever Test Mode is ON. - - platform: template - id: test_temperature_value - name: "${friendly_name} Test Temperature Value" - icon: mdi:thermometer-chevron-up - unit_of_measurement: "°C" - min_value: -10.0 - max_value: 40.0 - step: 0.1 - mode: slider - internal: true - lambda: |- - return id(test_temp_c); - set_action: - - lambda: |- - id(test_temp_c) = x; - -########################################################################################## -# INTERVAL COMPONENT -########################################################################################## -interval: - - interval: ${control_interval_s}s - then: - - if: - condition: - lambda: 'return id(mode_select).state == "AUTO";' - then: - - script.execute: auto_control_tick - - # Outside BYPASS, if the controller intends power OFF but LEDs still show ON, - # keep checking and force the heater OFF. - - interval: ${power_off_check_interval_s}s - then: - - if: - condition: - lambda: 'return !id(test_mode_enabled).state && id(mode_select).state != "BYPASS (Remote Only)" && !id(power_intended_on) && id(powered_state).state && !id(self_power_on);' - then: - - script.execute: enforce_heater_off - -########################################################################################## -# SCRIPT COMPONENT -########################################################################################## -script: - # Debounced setpoint MQTT mirror. - # Keeps HA/OpenHASP setpoint changes responsive while avoiding retained-MQTT bursts. - - id: publish_setpoint_mqtt - mode: restart - then: - - delay: ${setpoint_mqtt_debounce_ms}ms - - mqtt.publish: - topic: "viewroad-status/lounge-gasheater/setpoint" - retain: true - payload: !lambda |- - char buf[16]; - snprintf(buf, sizeof(buf), "%.1f", id(setpoint_c)); - return std::string(buf); - - # Mirror the already-computed Status Text entity out to MQTT. - # This keeps the HA text entity and MQTT status using the same source of truth. - - id: publish_status - mode: restart - then: - - mqtt.publish: - topic: "viewroad-status/lounge-gasheater" - payload: !lambda |- - if (id(status_text).state.size() == 0) return std::string("Unknown"); - return id(status_text).state; - - # Recompute the helper powered state from LEDs or the Test Mode override. - # Status MQTT publishing is handled by status_text on_value so we avoid duplicate publishes here. - - id: recompute_powered_state - then: - - lambda: |- - const bool led_powered = - id(led_operating_blue).state || id(led_standby_red).state; - - const bool new_state = - id(test_mode_enabled).state ? id(test_powered) : led_powered; - - id(powered_state).publish_state(new_state); - - # Force heater OFF after an ESPHome reboot before returning to BYPASS. - - id: boot_force_heater_off - mode: restart - then: - - repeat: - count: ${boot_force_off_attempts} - then: - - script.execute: recompute_powered_state - - if: - condition: - lambda: 'return !id(test_mode_enabled).state && id(powered_state).state;' - then: - - logger.log: "Boot: heater appears ON, pulsing power to force OFF." - - script.execute: press_power - - script.wait: press_with_gap - - delay: ${power_off_verify_delay_s}s - - script.execute: recompute_powered_state - - select.set: - id: mode_select - option: "BYPASS (Remote Only)" - - lambda: |- - id(power_intended_on) = false; - - # In non-BYPASS modes, if the controller intends power OFF but LEDs still show ON, - # pulse the heater ON/OFF button to force the heater fully OFF. - - id: enforce_heater_off - mode: restart - then: - - delay: ${power_off_verify_delay_s}s - - script.execute: recompute_powered_state - - if: - condition: - lambda: 'return !id(test_mode_enabled).state && id(mode_select).state != "BYPASS (Remote Only)" && !id(power_intended_on) && id(powered_state).state && !id(self_power_on);' - then: - - logger.log: "Heater should be OFF but LEDs still show ON. Pulsing power to force OFF." - - script.execute: press_power - - # Low-level relay click helper. - # Handles the button press pulse and enforces a minimum gap between clicks. - - id: press_with_gap - mode: queued - parameters: - which: string - then: - - if: - condition: - lambda: 'return which == "power";' - then: - - switch.turn_on: sw_power_raw - - delay: ${relay_pulse_ms}ms - - switch.turn_off: sw_power_raw - else: - - if: - condition: - lambda: 'return which == "up";' - then: - - switch.turn_on: sw_up_raw - - delay: ${relay_pulse_ms}ms - - switch.turn_off: sw_up_raw - else: - - switch.turn_on: sw_down_raw - - delay: ${relay_pulse_ms}ms - - switch.turn_off: sw_down_raw - - delay: ${min_click_gap_ms}ms - - - id: press_power - then: - - lambda: |- - id(self_power_on) = true; - - script.execute: - id: press_with_gap - which: "power" - - - id: press_up - then: - - if: - condition: - lambda: 'return id(mode_select).state != "BYPASS (Remote Only)";' - then: - - script.execute: - id: press_with_gap - which: "up" - else: - - logger.log: "BYPASS (Remote Only): ignoring UP press." - - - id: press_down - then: - - if: - condition: - lambda: 'return id(mode_select).state != "BYPASS (Remote Only)";' - then: - - script.execute: - id: press_with_gap - which: "down" - else: - - logger.log: "BYPASS (Remote Only): ignoring DOWN press." - - # Step one flame level UP and only update the visible level after the relay click finishes. - - id: step_up_once - then: - - if: - condition: - lambda: 'return id(powered_state).state;' - then: - - script.execute: press_up - - script.wait: press_with_gap - - lambda: |- - if (id(current_level) < 7) id(current_level)++; - - select.set: - id: flame_level_select - option: !lambda 'return std::to_string(id(current_level));' - - lambda: |- - id(last_adjust_ms) = millis(); - else: - - logger.log: "Heater is OFF; ignoring manual step up." - - # Step one flame level DOWN and only update the visible level after the relay click finishes. - - id: step_down_once - then: - - if: - condition: - lambda: 'return id(powered_state).state;' - then: - - script.execute: press_down - - script.wait: press_with_gap - - lambda: |- - if (id(current_level) > 1) id(current_level)--; - - select.set: - id: flame_level_select - option: !lambda 'return std::to_string(id(current_level));' - - lambda: |- - id(last_adjust_ms) = millis(); - else: - - logger.log: "Heater is OFF; ignoring manual step down." - - # Force a visible re-baseline to flame level 1. - # Six DOWN clicks guarantee that even from level 7 we end at level 1. - - id: calibrate_to_min - then: - - if: - condition: - lambda: 'return id(mode_select).state != "BYPASS (Remote Only)" && id(powered_state).state;' - then: - - lambda: |- - id(calibrating) = true; - - script.execute: publish_status - - repeat: - count: 6 - then: - - script.execute: step_down_once - - script.wait: step_down_once - - lambda: |- - id(last_recal_ms) = millis(); - id(calibrating) = false; - - select.set: - id: flame_level_select - option: "1" - - script.execute: publish_status - else: - - logger.log: "Skipping calibration (BYPASS or heater OFF)." - - # Moves one step at a time toward desired_level. - # In Test Mode, target confirmation is bypassed so bench testing feels responsive. - - id: adjust_to_desired - mode: queued - then: - - if: - condition: - lambda: 'return millis() < id(warmup_until_ms) && !id(test_mode_enabled).state;' - then: - - logger.log: - format: "Warmup: suppressing level change." - level: DEBUG - else: - - if: - condition: - lambda: |- - const uint32_t step_ms = (millis() < id(ramp_until_ms)) - ? (${fast_ramp_step_s} * 1000UL) - : (${adjust_step_interval_s} * 1000UL); - return (millis() - id(last_adjust_ms)) >= step_ms; - then: - - if: - condition: - lambda: 'return !id(powered_state).state;' - then: - - logger.log: - format: "Heater is OFF; not adjusting level." - level: DEBUG - else: - - if: - condition: - lambda: 'return id(test_mode_enabled).state || id(target_confirmed) || (millis() < id(ramp_until_ms));' - then: - - if: - condition: - lambda: 'return id(current_level) < id(desired_level);' - then: - - script.execute: step_up_once - else: - - if: - condition: - lambda: 'return id(current_level) > id(desired_level);' - then: - - script.execute: step_down_once - - # Called after a successful power ON request. - # Handles warmup, optional calibration, and initial AUTO control startup. - - id: after_power_on_reset_to_auto - then: - - if: - condition: - lambda: 'return id(mode_select).state != "BYPASS (Remote Only)";' - then: - - lambda: |- - id(power_intended_on) = true; - - if: - condition: - lambda: 'return id(test_mode_enabled).state;' - then: - - if: - condition: - lambda: 'return id(mode_select).state == "AUTO";' - then: - - lambda: |- - id(warmup_until_ms) = millis() + (${warmup_seconds} * 1000UL); - id(ramp_until_ms) = millis() + (${fast_ramp_seconds} * 1000UL); - - delay: ${warmup_seconds}s - - script.execute: calibrate_to_min - - script.execute: auto_control_tick - - script.execute: adjust_to_desired - - lambda: |- - id(self_power_on) = false; - else: - - lambda: |- - id(self_power_on) = false; - - logger.log: "Test Mode active: staying in MANUAL; no AUTO startup actions." - else: - - lambda: |- - id(warmup_until_ms) = millis() + (${warmup_seconds} * 1000UL); - id(ramp_until_ms) = millis() + (${fast_ramp_seconds} * 1000UL); - - select.set: - id: mode_select - option: "AUTO" - - delay: ${warmup_seconds}s - - script.execute: calibrate_to_min - - script.execute: auto_control_tick - - script.execute: adjust_to_desired - - lambda: |- - id(self_power_on) = false; - else: - - lambda: |- - id(self_power_on) = false; - - logger.log: "BYPASS (Remote Only): keeping mode; no auto-calibrate." - - # Main thermostat logic. - # Uses test_temp_c whenever Test Mode is enabled, otherwise uses MQTT room_temp. - - id: auto_control_tick - mode: queued - then: - - lambda: |- - const bool use_test_temp = id(test_mode_enabled).state; - const float t = use_test_temp ? id(test_temp_c) : id(room_temp).state; - const float sp = id(setpoint_c); - const uint32_t now_ms = millis(); - - if (!use_test_temp) { - const bool temp_lost = (id(last_temp_ms) > 0) && - ((now_ms - id(last_temp_ms)) >= (${temp_stale_timeout_min} * 60000UL)); - id(room_temp_lost_fault) = temp_lost; - if (temp_lost) { - ESP_LOGW("gas", "AUTO: room temperature lost; skipping control tick"); - return; - } - } else { - id(room_temp_lost_fault) = false; - } - - if (isnan(t)) { - ESP_LOGW("gas", "AUTO: temperature missing; skipping control tick"); - return; - } - - if (isnan(sp)) { - ESP_LOGW("gas", "AUTO: setpoint missing; skipping control tick"); - return; - } - - const bool powered = id(powered_state).state; - const float low = sp - ${on_below_c}; - const float high = sp + ${off_above_c}; - - const bool can_turn_on = (!powered) && ((${min_off_minutes} == 0) || ((now_ms - id(last_off_ms)) >= (${min_off_minutes} * 60000UL))); - const bool can_turn_off = ( powered) && ((${min_on_minutes} == 0) || ((now_ms - id(last_on_ms)) >= (${min_on_minutes} * 60000UL))); - - if (t <= low && can_turn_on) { - ESP_LOGI("gas", "AUTO: below setpoint -> ON"); - id(power_intended_on) = true; - id(press_power).execute(); - if (id(test_mode_enabled).state) { - id(test_powered) = true; - id(powered_state).publish_state(true); - id(publish_status).execute(); - } - id(after_power_on_reset_to_auto).execute(); - return; - } - - if (t >= high && can_turn_off) { - ESP_LOGI("gas", "AUTO: above setpoint -> OFF"); - id(power_intended_on) = false; - id(press_power).execute(); - if (id(test_mode_enabled).state) { - id(test_powered) = false; - id(powered_state).publish_state(false); - id(publish_status).execute(); - } - return; - } - - if (powered) { - float diff = sp - t; - int target = 1; - - // Proportional flame mapping: - // - A higher proportional_aggression lowers the effective thresholds and makes - // the controller choose higher flame levels sooner. - // - The individual flame_level_X_diff_c substitutions still allow fine tuning. - const float k = ${proportional_aggression}; - - if (diff >= (${flame_level_7_diff_c} / k)) target = 7; - else if (diff >= (${flame_level_6_diff_c} / k)) target = 6; - else if (diff >= (${flame_level_5_diff_c} / k)) target = 5; - else if (diff >= (${flame_level_4_diff_c} / k)) target = 4; - else if (diff >= (${flame_level_3_diff_c} / k)) target = 3; - else if (diff >= (${flame_level_2_diff_c} / k)) target = 2; - else target = 1; - - id(desired_level) = target; - id(target_confirmed) = (target == id(last_level_target)); - id(last_level_target) = target; - } - - - if: - condition: - lambda: 'return id(powered_state).state;' - then: - - if: - condition: - lambda: |- - return (id(desired_level) == 1) && - ((millis() - id(last_recal_ms)) >= (${recalibrate_interval_min} * 60000UL)); - then: - - logger.log: "Re-baseline at level 1 (6x DOWN)." - - script.execute: calibrate_to_min - - script.execute: adjust_to_desired \ No newline at end of file diff --git a/group/basement_lights.yaml b/group/basement_lights.yaml deleted file mode 100644 index 664d009..0000000 --- a/group/basement_lights.yaml +++ /dev/null @@ -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 diff --git a/group/family_presence.yaml b/group/family_presence.yaml deleted file mode 100644 index 3b98ea1..0000000 --- a/group/family_presence.yaml +++ /dev/null @@ -1,6 +0,0 @@ -family_away: - name: All Family Members Away - entities: - - !secret person_1 - - !secret person_2 - - !secret person_3 diff --git a/groups.yaml b/groups.yaml deleted file mode 100644 index 8b13789..0000000 --- a/groups.yaml +++ /dev/null @@ -1 +0,0 @@ - diff --git a/packages/Lighting_2Way_Sync/central_stairway_2way_sync.yaml b/packages/Lighting_2Way_Sync/central_stairway_2way_sync.yaml index 1d9c9c7..0f60119 100644 --- a/packages/Lighting_2Way_Sync/central_stairway_2way_sync.yaml +++ b/packages/Lighting_2Way_Sync/central_stairway_2way_sync.yaml @@ -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: # Stair Lights Top - id: stair_lights_top_2way_sync diff --git a/packages/Lighting_2Way_Sync/garage_lights_2way_sync.yaml b/packages/Lighting_2Way_Sync/garage_lights_2way_sync.yaml index d925175..f5a7232 100644 --- a/packages/Lighting_2Way_Sync/garage_lights_2way_sync.yaml +++ b/packages/Lighting_2Way_Sync/garage_lights_2way_sync.yaml @@ -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: # Two lights and two switches for these corridor lights - id: garage_entry_outside_2way_sync diff --git a/packages/Pool_Controls/pool_light_operation.yaml b/packages/Pool_Controls/pool_light_operation.yaml index e304a6a..e984708 100644 --- a/packages/Pool_Controls/pool_light_operation.yaml +++ b/packages/Pool_Controls/pool_light_operation.yaml @@ -183,7 +183,3 @@ automation: - service: input_boolean.turn_off target: entity_id: input_boolean.poollights_mode_switch - - - service: input_boolean.turn_on - target: - entity_id: input_boolean.timer_light_midnight_pending diff --git a/packages/Pool_Controls/pool_pump_operation.yaml b/packages/Pool_Controls/pool_pump_operation.yaml index 01da89d..071571f 100644 --- a/packages/Pool_Controls/pool_pump_operation.yaml +++ b/packages/Pool_Controls/pool_pump_operation.yaml @@ -152,6 +152,3 @@ automation: - service: input_boolean.turn_off data: entity_id: input_boolean.poolpump_mode_switch - - service: input_boolean.turn_on - data: - entity_id: input_boolean.timer_midnight_pending diff --git a/packages/anniversaries.yaml b/packages/anniversaries.yaml index 85cdf9b..d0eae85 100644 --- a/packages/anniversaries.yaml +++ b/packages/anniversaries.yaml @@ -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: - platform: anniversary name: events diff --git a/packages/appliance_status_mqttfeed.yaml b/packages/appliance_status_mqttfeed.yaml index 00a0eb4..bd8cc1a 100644 --- a/packages/appliance_status_mqttfeed.yaml +++ b/packages/appliance_status_mqttfeed.yaml @@ -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: sensor: - unique_id: washing_machine_finished diff --git a/packages/auto_lights_underhouse_entrance.yaml b/packages/auto_lights_underhouse_entrance.yaml index d18638e..957bed1 100644 --- a/packages/auto_lights_underhouse_entrance.yaml +++ b/packages/auto_lights_underhouse_entrance.yaml @@ -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: -# 1.2 - 2026-05-12 -# - Added fast Zigbee2MQTT FP1-style event triggers: -# sensor.underhouse_mmwave_sensor_x23ms_presence_event = enter -# 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. +# Purpose: +# Replaces the Node-RED automation for the underhouse entrance lights. Uses +# Zigbee2MQTT mmWave event triggers (enter/approach/presence) to turn lights +# on instantly, then off after 400 seconds of sustained clear presence. # -# Notes: -# - The UI may show the binary sensor as "Detected", but the YAML state is -# normally "on". -# - The presence_event sensor can react earlier than the binary presence sensor. -# - 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. +# Behaviour: +# - Fast Zigbee2MQTT FP1-style event triggers (enter, approach) turn lights on. +# - Binary presence sensor serves as a backup trigger and clearance condition. +# - Lights turn off 400 seconds after presence clears. # -################################################################################ +# Version History: +# - v1.2.0 (2026-05-12): Added fast Zigbee2MQTT FP1-style event triggers and +# converted header to standard package format. +# +#:########################################################################################:# automation: - id: underhouse_entrance_auto_lights diff --git a/packages/auto_lights_underhouse_storage.yaml b/packages/auto_lights_underhouse_storage.yaml index 8a6c44c..ee44618 100644 --- a/packages/auto_lights_underhouse_storage.yaml +++ b/packages/auto_lights_underhouse_storage.yaml @@ -1,28 +1,24 @@ #:########################################################################################:# -# TITLE: UNDERHOUSE STORAGE LIGHTS AUTO LIGHTS PACKAGE -#:########################################################################################:# -# DESCRIPTION: +# Package: Underhouse Storage Lights +# File: auto_lights_underhouse_storage.yaml +# Version: v1.1.0 +# Date: 2026-06-14 +# +# Purpose: # Replaces the Node-RED automation for the underhouse storage lights. # Doors, PIR sensors, a remote switch, and local relay changes can start or # extend the storage lighting timer. # -# VERSION: -# V1.1 2026-06-14 -# - Door-open trigger now watches each real door contact individually. -# - This means any door changing from closed to open turns the lights on -# immediately, even if another door is already open. -# - All doors closed now turns the lights off immediately. -# - PIR occupancy can still start/restart the motion timer after that. +# Behaviour: +# - Individual door-open triggers turn lights on immediately. +# - All doors closed turns lights off immediately. +# - PIR occupancy can start/restart the motion timer. +# - Remote switch press starts or extends the light timer. # -# V1.0 2026-05-12 -# - Initial Home Assistant package version. -# -# EDIT NOTES: -# - 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 +# Version History: +# - v1.1.0 (2026-06-14): Door-open trigger now watches each real door contact +# individually; all doors closed turns lights off immediately. +# - v1.0.0 (2026-05-12): Initial Home Assistant package version. #:########################################################################################:# binary_sensor: @@ -112,8 +108,7 @@ automation: entity_id: - binary_sensor.underhouse_stealth_door_x07rs_contact - binary_sensor.underhouse_north_door_door - # FUTURE DOOR: replace this with the actual third underhouse storage door contact. - #- binary_sensor.underhouse_future_door_contact + - binary_sensor.under_house_underhouse_east_stealth_door_door from: "off" to: "on" @@ -213,8 +208,7 @@ automation: entity_id: - binary_sensor.underhouse_stealth_door_x07rs_contact - binary_sensor.underhouse_north_door_door - # FUTURE DOOR: replace this with the actual third underhouse storage door contact. - #- binary_sensor.underhouse_future_door_contact + - binary_sensor.under_house_underhouse_east_stealth_door_door from: "on" to: "off" action: diff --git a/packages/basement_lights.yaml b/packages/basement_lights.yaml new file mode 100644 index 0000000..0069822 --- /dev/null +++ b/packages/basement_lights.yaml @@ -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 diff --git a/packages/bedroom_3_led_actions.yaml b/packages/bedroom_3_led_actions.yaml index 3335c54..253812e 100644 --- a/packages/bedroom_3_led_actions.yaml +++ b/packages/bedroom_3_led_actions.yaml @@ -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: bedroom_3_led_actions: icon: mdi:led-strip diff --git a/packages/byd_charging_schedule_athome.yaml b/packages/byd_charging_schedule_athome.yaml index 97d651d..591665b 100644 --- a/packages/byd_charging_schedule_athome.yaml +++ b/packages/byd_charging_schedule_athome.yaml @@ -1,8 +1,8 @@ #:########################################################################################:# # Package: BYD Charging Schedule At Home # File: byd_charging_schedule_athome.yaml -# Version: v1.0.1 -# Date: 2026-06-19 +# Version: v1.1.0 +# Date: 2026-07-23 # # Purpose: # Enables the BYD Atto 3 charging schedule when the car arrives home. @@ -11,11 +11,12 @@ # # Behaviour: # - Confirmed arrival home: -# Turns ON switch.atto_3_schedule_enabled, -# but only if the car had previously been confirmed away. +# Sets the schedule to 9:00 PM through midnight with charge-to-full disabled, +# enables it, and retries cloud reconciliation once per minute. # # - 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: # If the car is already home and the user or another HA automation turns @@ -35,24 +36,35 @@ input_boolean: 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 alias: "BYD Atto 3 Charging Schedule - Arrival Enable" description: > - Turns the BYD charging schedule ON when the car arrives home, but only if - it was previously confirmed away. This prevents brief away/home glitches - from undoing a manual OFF while the car is actually still at home. + Reconciles the BYD charging schedule to 9:00 PM through midnight and turns + it ON when the car arrives home after a confirmed away period. Rechecks the + cloud-confirmed state once per minute and retries failed updates. mode: single variables: &byd_charging_schedule_vars tracker_entity: device_tracker.byd_atto3_ibeacon_bur4c2a_bermuda_tracker 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". 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. - 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. ignored_tracker_states: @@ -69,7 +81,7 @@ automation: {{ trigger.to_state is not none and trigger.from_state is not none and trigger.to_state.state == home_state - and trigger.from_state.state != home_state }} + and trigger.from_state.state == 'not_home' }} actions: - choose: @@ -78,23 +90,72 @@ automation: entity_id: input_boolean.byd_atto3_charging_schedule_confirmed_away state: "on" sequence: - - action: switch.turn_on + - action: input_boolean.turn_off target: - entity_id: "{{ schedule_switch_entity }}" + entity_id: input_boolean.byd_atto3_charging_schedule_confirmed_away - - action: input_boolean.turn_off - target: - entity_id: input_boolean.byd_atto3_charging_schedule_confirmed_away + - repeat: + count: "{{ schedule_verify_attempts | int(6) }}" + 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 alias: "BYD Atto 3 Charging Schedule - Departure Disable After Timeout" description: > 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 - marks the car as confirmed away. + is still away, marks it confirmed away, turns the BYD charging schedule OFF, + and retries failed cloud updates once per minute. mode: restart variables: *byd_charging_schedule_vars @@ -111,7 +172,7 @@ automation: actions: - delay: - minutes: "{{ away_confirm_minutes | int(15) }}" + minutes: "{{ away_confirm_minutes | int(10) }}" - condition: template value_template: >- @@ -119,14 +180,28 @@ automation: {{ current_state != home_state and current_state not in ignored_tracker_states }} - - action: switch.turn_off - target: - entity_id: "{{ schedule_switch_entity }}" - - action: input_boolean.turn_on target: 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 #:######################################################################################:# @@ -134,8 +209,8 @@ automation: alias: "BYD Atto 3 Charging Schedule - Startup Away Check" description: > 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 - confirmed away so that the next confirmed arrival can enable the schedule. + beyond the configured timeout, mark it confirmed away and reconcile the + charging schedule OFF so the next confirmed arrival can enable it. mode: single variables: *byd_charging_schedule_vars @@ -153,7 +228,7 @@ automation: and current_state not in ignored_tracker_states }} - delay: - minutes: "{{ away_confirm_minutes | int(15) }}" + minutes: "{{ away_confirm_minutes | int(10) }}" - condition: template value_template: >- @@ -161,10 +236,24 @@ automation: {{ current_state != home_state and current_state not in ignored_tracker_states }} - - action: switch.turn_off - target: - entity_id: "{{ schedule_switch_entity }}" - - action: input_boolean.turn_on target: 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 }}" diff --git a/packages/cctv_controls.yaml b/packages/cctv_controls.yaml index ef664ee..84c386e 100644 --- a/packages/cctv_controls.yaml +++ b/packages/cctv_controls.yaml @@ -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: binary_sensor: - unique_id: front_door_movement_camera_corner name: "Front Door Movement" state_topic: "viewroad-status/cctv/front_door_motion" + payload_on: "1" + payload_off: "0" icon: mdi:exit-run diff --git a/packages/dashboard_helpers.yaml b/packages/dashboard_helpers.yaml index 93a482e..966ec56 100644 --- a/packages/dashboard_helpers.yaml +++ b/packages/dashboard_helpers.yaml @@ -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: moon_phase_percent_overlay: name: Moon phase percent overlay diff --git a/packages/edgerouter_snmp.yaml b/packages/edgerouter_snmp.yaml index b7eadb8..9c4aae7 100644 --- a/packages/edgerouter_snmp.yaml +++ b/packages/edgerouter_snmp.yaml @@ -23,7 +23,7 @@ # - Calculates memory usage from total, free, buffer, and cache values. # # Polling: -# - Traffic counters: Every 10 seconds. +# - Traffic counters: Every 60 seconds. # - Uptime, CPU, load, and memory: Every 60 seconds. # - Firmware description: Every 300 seconds. # @@ -58,7 +58,7 @@ sensor: device_class: data_size state_class: total_increasing unit_of_measurement: "B" - scan_interval: 10 + scan_interval: 60 # ifHCOutOctets counts bytes transmitted from the router to the Internet. - platform: snmp @@ -72,7 +72,7 @@ sensor: device_class: data_size state_class: total_increasing unit_of_measurement: "B" - scan_interval: 10 + scan_interval: 60 # Convert the cumulative received-byte counter into the WAN download rate. - platform: derivative @@ -109,7 +109,7 @@ sensor: device_class: data_size state_class: total_increasing unit_of_measurement: "B" - scan_interval: 10 + scan_interval: 60 # Bytes transmitted by the router to Home LAN clients become the LAN download rate. - platform: snmp @@ -123,7 +123,7 @@ sensor: device_class: data_size state_class: total_increasing unit_of_measurement: "B" - scan_interval: 10 + scan_interval: 60 # Derive the client-to-router upload rate from the received-byte counter. - platform: derivative @@ -160,7 +160,7 @@ sensor: device_class: data_size state_class: total_increasing unit_of_measurement: "B" - scan_interval: 10 + scan_interval: 60 # Bytes transmitted by the router to the wireless network become its download rate. - platform: snmp @@ -174,7 +174,7 @@ sensor: device_class: data_size state_class: total_increasing unit_of_measurement: "B" - scan_interval: 10 + scan_interval: 60 # Derive the wireless-to-router upload rate from the received-byte counter. - platform: derivative @@ -211,7 +211,7 @@ sensor: device_class: data_size state_class: total_increasing unit_of_measurement: "B" - scan_interval: 10 + scan_interval: 60 # Bytes transmitted by the router to Home Automation become its download rate. - platform: snmp @@ -225,7 +225,7 @@ sensor: device_class: data_size state_class: total_increasing unit_of_measurement: "B" - scan_interval: 10 + scan_interval: 60 # Derive the Home Automation upload rate from the received-byte counter. - platform: derivative @@ -262,7 +262,7 @@ sensor: device_class: data_size state_class: total_increasing unit_of_measurement: "B" - scan_interval: 10 + scan_interval: 60 # Bytes transmitted by the router to this network become its download rate. - platform: snmp @@ -276,7 +276,7 @@ sensor: device_class: data_size state_class: total_increasing unit_of_measurement: "B" - scan_interval: 10 + scan_interval: 60 # Derive the secondary Home Automation upload rate from received bytes. - platform: derivative diff --git a/packages/fridge_temperatures.yaml b/packages/fridge_temperatures.yaml index 83c13a4..249f105 100644 --- a/packages/fridge_temperatures.yaml +++ b/packages/fridge_temperatures.yaml @@ -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: - id: publish_fridge_temps_drinks_fridge alias: publish_fridge_temps_drinks_fridge diff --git a/packages/grocy_api.yaml.old b/packages/grocy_api.yaml.old deleted file mode 100644 index af6f445..0000000 --- a/packages/grocy_api.yaml.old +++ /dev/null @@ -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" diff --git a/packages/health_checks.yaml b/packages/health_checks.yaml index 7196393..041fa19 100644 --- a/packages/health_checks.yaml +++ b/packages/health_checks.yaml @@ -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 ---- binary_sensor: - platform: rest diff --git a/packages/internet_traffic.yaml b/packages/internet_traffic.yaml index c3348b6..7b84f62 100644 --- a/packages/internet_traffic.yaml +++ b/packages/internet_traffic.yaml @@ -1,3 +1,28 @@ +#:########################################################################################:# +# 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: - sensor: - name: "Internet Download Traffic" diff --git a/packages/internet_traffic.yaml.bak b/packages/internet_traffic.yaml.bak deleted file mode 100644 index 85927f1..0000000 --- a/packages/internet_traffic.yaml.bak +++ /dev/null @@ -1,25 +0,0 @@ -template: - - sensor: - - name: "Internet Download Traffic" - unique_id: internet_downtraffic - unit_of_measurement: "kB/s" - state_class: measurement - state: > - {{ state_attr('binary_sensor.edgeos_interface_pppoe0', 'KBytes/ps (Received)') | float(0) }} - - - name: "Internet Upload Traffic" - unique_id: internet_uptraffic - unit_of_measurement: "kB/s" - state_class: measurement - state: > - {{ state_attr('binary_sensor.edgeos_interface_pppoe0', 'KBytes/ps (Sent)') | float(0) }} - - - name: "Internet Total Traffic" - unique_id: internet_totaltraffic - unit_of_measurement: "kB/s" - state_class: measurement - state: > - {{ - (state_attr('binary_sensor.edgeos_interface_pppoe0', 'KBytes/ps (Received)') | float(0)) - + (state_attr('binary_sensor.edgeos_interface_pppoe0', 'KBytes/ps (Sent)') | float(0)) - }} diff --git a/packages/kogan_kettle.yaml b/packages/kogan_kettle.yaml index 382c081..5d1755d 100644 --- a/packages/kogan_kettle.yaml +++ b/packages/kogan_kettle.yaml @@ -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: sensor: - unique_id: kettle.temp diff --git a/packages/low_battery_device_quantity.yaml b/packages/low_battery_device_quantity.yaml index a05d570..d842b94 100644 --- a/packages/low_battery_device_quantity.yaml +++ b/packages/low_battery_device_quantity.yaml @@ -1,17 +1,24 @@ #:########################################################################################:# -# Low Battery Device Summary Package # +# Low Battery Monitoring Package # #:########################################################################################:# # # TITLE: -# Low Battery Device Summary +# Low Battery Monitoring # # FILE: # packages/low_battery_device_quantity.yaml # # VERSION: -# V1.1 2026-07-20 +# V1.2 2026-07-23 # # 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 # - Changed the low-battery threshold from 97% to 20%. # - Added structured documentation for scope, exclusions, and outputs. @@ -21,17 +28,19 @@ # # PURPOSE: # 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: # - Only entities in the sensor domain with device_class battery are checked. # - 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: # - sensor.devices_with_low_battery reports the number of matching sensors. # - sensor.devices_with_low_battery_list reports names and percentages. # - 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 {% 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)) <= 20 %} - {% set ns.names = ns.names + [s.name ~ ' (' ~ (st | int(0)) ~ '%)'] %} - {% endif %} - {% endif %} - {% endif %} - {% endfor %} - {% if ns.names | length == 0 %} + {% set count = states('sensor.devices_with_low_battery') | int(0) %} + {% if count == 0 %} None {% else %} - {{ ns.names | join(', ') }} + {{ count }} devices {% endif %} attributes: count: "{{ states('sensor.devices_with_low_battery') | int(0) }}" @@ -113,3 +112,22 @@ template: {% endif %} {% endfor %} {{ 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 }} diff --git a/packages/low_battery_device_quantity.yaml.bak b/packages/low_battery_device_quantity.yaml.bak deleted file mode 100644 index aad13a6..0000000 --- a/packages/low_battery_device_quantity.yaml.bak +++ /dev/null @@ -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 }} diff --git a/packages/media_players.yaml.old b/packages/media_players.yaml.old deleted file mode 100644 index 49e0244..0000000 --- a/packages/media_players.yaml.old +++ /dev/null @@ -1,5 +0,0 @@ -#media_player: -# MPD Media Player on Lounge Touchscreen -# - platform: mpd -# host: !secret lounge_mpd_player -# scan_interval: 1 diff --git a/packages/moonraker_3dprinter.yaml b/packages/moonraker_3dprinter.yaml deleted file mode 100644 index 2737535..0000000 --- a/packages/moonraker_3dprinter.yaml +++ /dev/null @@ -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" diff --git a/packages/niwa_tides.yaml b/packages/niwa_tides.yaml index 4771347..2ee109d 100644 --- a/packages/niwa_tides.yaml +++ b/packages/niwa_tides.yaml @@ -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: - platform: niwa_tides api_key: !secret niwa_tides_api_key diff --git a/packages/nspanel_masterbdrm_controller.yaml.old b/packages/nspanel_masterbdrm_controller.yaml.old deleted file mode 100644 index 4e61afa..0000000 --- a/packages/nspanel_masterbdrm_controller.yaml.old +++ /dev/null @@ -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 diff --git a/packages/pet_feeding_and_jobs.yaml.bak b/packages/pet_feeding_and_jobs.yaml.bak deleted file mode 100644 index a69ca4d..0000000 --- a/packages/pet_feeding_and_jobs.yaml.bak +++ /dev/null @@ -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" diff --git a/packages/piano_practice.yaml.old b/packages/piano_practice.yaml.old deleted file mode 100644 index 6074d36..0000000 --- a/packages/piano_practice.yaml.old +++ /dev/null @@ -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 diff --git a/packages/recorder.yaml b/packages/recorder.yaml index 8ddc46d..9917ab1 100644 --- a/packages/recorder.yaml +++ b/packages/recorder.yaml @@ -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: + exclude: + entity_globs: + - sensor.esp_*_uptime* + - sensor.ha_summary_v2_* + entities: + - sensor.jobs_tracker_summary diff --git a/packages/robotdyn_dimmers.yaml b/packages/robotdyn_dimmers.yaml index 3f2b088..c193827 100644 --- a/packages/robotdyn_dimmers.yaml +++ b/packages/robotdyn_dimmers.yaml @@ -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: - light: schema: json diff --git a/packages/security_and_notifications.yaml b/packages/security_and_notifications.yaml index 31d499e..1db62a4 100644 --- a/packages/security_and_notifications.yaml +++ b/packages/security_and_notifications.yaml @@ -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: # Legacy duplicate retained temporarily for verification. # quiet_time: diff --git a/packages/simulation_lights.yaml b/packages/simulation_lights.yaml index 86eef4f..aff2986 100644 --- a/packages/simulation_lights.yaml +++ b/packages/simulation_lights.yaml @@ -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: - simulation_lights_all: - name: Simulation Lights (All) + #:######################################################################################:# + # FAMILY PRESENCE + #:######################################################################################:# + family_away: + name: "All Family Members Away" 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_bottom_relay_1_main_stair_lights_lower - switch.esp_entrancebathrmlights_relay_1_main_lights @@ -16,3 +47,62 @@ group: - light.esp_loungepelmetleds2_lounge_pelmet_leds_a - light.esp_lounge6chdimmer_couch_spots_left - 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" diff --git a/packages/vehicle_announcements.yaml b/packages/vehicle_announcements.yaml new file mode 100644 index 0000000..4d4b374 --- /dev/null +++ b/packages/vehicle_announcements.yaml @@ -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." diff --git a/packages/waste_collection.yaml b/packages/waste_collection.yaml index 1e33855..3dcf050 100644 --- a/packages/waste_collection.yaml +++ b/packages/waste_collection.yaml @@ -1,40 +1,52 @@ #:########################################################################################:# -# Auckland Council Waste Collection Package # +# Council Waste Collection Package # #:########################################################################################:# # # TITLE: -# Auckland Council Waste Collection +# Council Waste Collection # # FILE: # packages/waste_collection.yaml # # VERSION: -# V1.1 2026-07-20 +# V1.4 2026-07-22 # # 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 # - Added source setup, schedule, MQTT, and maintenance documentation. # # V1.0 -# - Initial Auckland Council schedules and MQTT bin-status sensors. +# - Initial council schedules and MQTT bin-status sensors. # # PURPOSE: -# Provides Auckland Council rubbish, recycling, and food-waste collection -# schedules, plus independent MQTT sensors indicating whether bins are out. +# Provides council rubbish, recycling, and food-waste collection schedules, +# plus independent MQTT sensors indicating whether bins are out. # # DEPENDENCY: -# Requires the Waste Collection Schedule custom integration and its -# aucklandcouncil_govt_nz source. +# Requires the Waste Collection Schedule custom integration and its configured +# council source. # # 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. -# - Use the displayed assessment number as area_number below. +# - Store the displayed assessment number as council_area in secrets.yaml. # - 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: -# - 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. # - 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. # - Recycling and food-waste report unknown; review the source type names when # investigating those schedules. +# - As of 2026-07-22, fixed type names to match Auckland Council source output. # #:########################################################################################:# waste_collection_schedule: sources: - - name: aucklandcouncil_govt_nz + - name: !secret council_name args: - area_number: 12344403722 # Auckland Council assessment number. + area_number: !secret council_area sensor: - platform: waste_collection_schedule #source_index: 0 - name: Auckland Council Waste + name: "Council Waste" details_format: upcoming count: 1 leadtime: 1 @@ -72,7 +85,7 @@ sensor: - rubbish - platform: waste_collection_schedule #source_index: 0 - name: Auckland Council Recycling + name: "Council Recycling" details_format: upcoming count: 1 leadtime: 1 @@ -81,10 +94,10 @@ sensor: add_days_to: true #event_index: EVENT_INDEX types: - - recycle + - recycling - platform: waste_collection_schedule #source_index: 0 - name: Auckland Council Food-Waste + name: "Council Food-Waste" details_format: upcoming count: 1 leadtime: 1 @@ -93,16 +106,16 @@ sensor: add_days_to: true #event_index: EVENT_INDEX types: - - food-waste + - food scraps mqtt: sensor: - - unique_id: auckland_council_red_waste_bin_out + - unique_id: council_red_waste_bin_out name: "Red Waste Bin 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" 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" - state_topic: "viewroad-status/rubbishbinstatus/foodbin-out" + state_topic: "viewroad-status/rubbishbinstatus/foodbin-out" \ No newline at end of file diff --git a/scripts.yaml b/scripts.yaml deleted file mode 100644 index e69de29..0000000 diff --git a/scripts/office_mediaplayer.yaml b/scripts/office_mediaplayer.yaml deleted file mode 100644 index e2f7b1f..0000000 --- a/scripts/office_mediaplayer.yaml +++ /dev/null @@ -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 }}" diff --git a/scripts/reload_lounge_browser.yaml.old b/scripts/reload_lounge_browser.yaml.old deleted file mode 100644 index a6d3387..0000000 --- a/scripts/reload_lounge_browser.yaml.old +++ /dev/null @@ -1,5 +0,0 @@ -action: browser_mod.javascript -data: - code: lovelace_reload() - browser_id: - - lounge diff --git a/scripts/turn_light_on_for_random_time.yaml b/scripts/turn_light_on_for_random_time.yaml deleted file mode 100644 index e8f404c..0000000 --- a/scripts/turn_light_on_for_random_time.yaml +++ /dev/null @@ -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 }}" diff --git a/templates/moonraker_3dprinter.yaml b/templates/moonraker_3dprinter.yaml deleted file mode 100644 index cfb60db..0000000 --- a/templates/moonraker_3dprinter.yaml +++ /dev/null @@ -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" diff --git a/themes.yaml b/themes.yaml deleted file mode 100644 index e69de29..0000000