diff --git a/automations.yaml b/automations.yaml index e69de29..989e941 100644 --- a/automations.yaml +++ b/automations.yaml @@ -0,0 +1,72 @@ +- id: '1792441676327' + alias: NestQuest UniFi quest access + description: '' + use_blueprint: + path: Maniacs-Mansion/unifi_quest_access.yaml + input: + remaining_sensor: sensor.nestquest_chloe_quests_remaining_today + client_switches: + - switch.default_blocked + - switch.iphone_blocked_2 +- id: '1792441738406' + alias: NestQuest UniFi quest access + description: '' + use_blueprint: + path: Maniacs-Mansion/unifi_quest_access.yaml + input: + remaining_sensor: sensor.nestquest_chloe_quests_remaining_today + client_switches: + - switch.iphone_blocked +- id: '1792453243454' + alias: NestQuest UniFi quest access + description: '' + use_blueprint: + path: Maniacs-Mansion/unifi_quest_access.yaml + input: + remaining_sensor: sensor.nestquest_declan_quests_remaining_today + client_switches: + - switch.default_blocked_2 +- id: '1792453338330' + alias: Declan NestQuest Access Control + description: Manages internet access for Declan by unblocking devices based on the + number of remaining daily quests. + use_blueprint: + path: Maniacs-Mansion/unifi_quest_access.yaml + input: + remaining_sensor: sensor.nestquest_declan_quests_remaining_today + client_switches: + - switch.declan_iphone_blocked + - switch.default_blocked_2 +- id: '1792453462434' + alias: Jordyn NestQuest Access Control + description: Manages network access for Jordyn's devices based on the number of + remaining daily quests. + use_blueprint: + path: Maniacs-Mansion/unifi_quest_access.yaml + input: + remaining_sensor: sensor.nestquest_jordyn_quests_remaining_today + client_switches: + - switch.jordyn_iphone_blocked + - switch.default_blocked_3 +- id: '1792453576643' + alias: Jordyn Quest Access Control + description: Manages network access for Jordyn's devices based on the number of + remaining daily quests. + use_blueprint: + path: Maniacs-Mansion/unifi_quest_access.yaml + input: + remaining_sensor: sensor.nestquest_jordyn_quests_remaining_today + client_switches: + - switch.jordyn_iphone_blocked + - switch.default_blocked_3 +- id: '1792453896291' + alias: Declan NestQuest Access Control + description: Monitors Declan's remaining daily quests and automatically blocks his + iPhone and the default device once the limit is reached. + use_blueprint: + path: Maniacs-Mansion/unifi_quest_access.yaml + input: + remaining_sensor: sensor.nestquest_declan_quests_remaining_today + client_switches: + - switch.declan_iphone_blocked + - switch.default_blocked_2 diff --git a/blueprints/automation/Maniacs-Mansion/afternoon_reminder.yaml b/blueprints/automation/Maniacs-Mansion/afternoon_reminder.yaml new file mode 100644 index 0000000..aa89b8a --- /dev/null +++ b/blueprints/automation/Maniacs-Mansion/afternoon_reminder.yaml @@ -0,0 +1,67 @@ +blueprint: + name: NestQuest afternoon reminder + description: 'Sends an afternoon nudge listing ONLY the children who still have + open quests today. Children who are present and all clear are omitted (the nudge + is for stragglers, not a roll call), children who are not present today are omitted + entirely, and the household rollup sensors are excluded by the same present guard. + When every present child is already done the automation sends NOTHING at all: + a template condition stops the run before the notification. The notify service + is chosen by the household when importing this blueprint — NestQuest never hard-codes + a personal notify target.' + domain: automation + input: + afternoon_time: + name: Afternoon time + description: Local time of day the reminder is sent. + default: '15:00' + selector: + time: {} + notify_target: + name: Notify service + description: The Home Assistant notify service to call for the reminder, for + example notify.mobile_app_your_phone — a placeholder; pick the notify service + for your own device. + selector: + text: + multiline: false + multiple: false + send_reminder: + name: Send the reminder + description: Turn off to keep the automation installed but skip the notification + entirely. + default: true + selector: + boolean: {} + source_url: https://github.com/Maniacs-Mansion/NestQuest/blob/main/custom_components/nestquest/blueprints/automation/afternoon_reminder.yaml +mode: single +variables: + send_reminder: !input send_reminder +triggers: +- trigger: time + at: !input afternoon_time +conditions: +- condition: template + value_template: '{{ send_reminder | bool }}' +- condition: template + value_template: "{%- set ns = namespace(any_open=false) -%}\n{%- for due in states.sensor\n + \ | selectattr('entity_id', 'search', '^sensor\\\\.nestquest_.*_quests_due_today(_\\\\d+)?$') + -%}\n {%- if due.attributes.get('present') -%}\n {%- set remaining_values + = states.sensor\n | selectattr('entity_id', 'search', '^sensor\\\\.nestquest_.*_quests_remaining_today(_\\\\d+)?$')\n + \ | selectattr('attributes.child_id', 'eq', due.attributes.child_id)\n + \ | map(attribute='state')\n | list -%}\n {%- if remaining_values + and (remaining_values | first | int) > 0 -%}\n {%- set ns.any_open = true + -%}\n {%- endif -%}\n {%- endif -%}\n{%- endfor -%}\n{{- ns.any_open -}}\n" +actions: +- action: !input notify_target + data: + title: NestQuest afternoon reminder + message: "{%- set ns = namespace(lines=[]) -%}\n{%- for due in states.sensor\n + \ | selectattr('entity_id', 'search', '^sensor\\\\.nestquest_.*_quests_due_today(_\\\\d+)?$') + -%}\n {%- if due.attributes.get('present') -%}\n {%- set remaining_values + = states.sensor\n | selectattr('entity_id', 'search', '^sensor\\\\.nestquest_.*_quests_remaining_today(_\\\\d+)?$')\n + \ | selectattr('attributes.child_id', 'eq', due.attributes.child_id)\n + \ | map(attribute='state')\n | list -%}\n {%- if remaining_values + and (remaining_values | first | int) > 0 -%}\n {%- set line = due.attributes.child_name + ~ ' still has ' ~ (remaining_values | first | int) ~ ' quest(s) open.' -%}\n + \ {%- set ns.lines = ns.lines + [line] -%}\n {%- endif -%}\n {%- endif + -%}\n{%- endfor -%}\n{{- ns.lines | join('\\n') -}}" diff --git a/blueprints/automation/Maniacs-Mansion/day_complete_celebration.yaml b/blueprints/automation/Maniacs-Mansion/day_complete_celebration.yaml new file mode 100644 index 0000000..847cb19 --- /dev/null +++ b/blueprints/automation/Maniacs-Mansion/day_complete_celebration.yaml @@ -0,0 +1,87 @@ +blueprint: + name: NestQuest day complete celebration + description: 'Celebrates the moment a child clears every quest for the day: the + automation is driven by the nestquest_child_day_complete event, which NestQuest + only fires when quests were owed and all of them are complete — a zero-quest day + can never trigger this celebration. Once-per-child-per-day dedupe: point last_celebrated_helper + at ONE input_text helper (create it under Settings, Devices and Services, Helpers + — plain text). The automation records each celebrated child as ":" + inside that helper, and a child who un-completes a quest and completes it again + the same day is celebrated once, not twice — while a SECOND child clearing their + day the same date is still celebrated in full. The helper is pruned to today''s + entries on every celebration, so it never grows past one entry per child per day. + A helper that does not exist yet, or has never been set, lets the first celebration + through. The notification message may use the event payload''s child_name, quests_due + and quests_completed. The notify service is chosen by the household when importing + this blueprint — NestQuest never hard-codes a personal notify target.' + domain: automation + input: + notify_target: + name: Notify service + description: The Home Assistant notify service to call for the celebration, + for example notify.mobile_app_your_phone — a placeholder; pick the notify + service for your own device. + selector: + text: + multiline: false + multiple: false + celebration_message: + name: Celebration message + description: 'The message sent when a child clears every quest today. May use + the event payload: trigger.event.data.child_name, trigger.event.data.quests_due + and trigger.event.data.quests_completed.' + default: Legendary! {{ trigger.event.data.child_name }} cleared every quest + today! + selector: + text: + multiline: false + multiple: false + celebrate: + name: Send the celebration + description: Turn off to keep the automation installed but skip the notification + entirely. + default: true + selector: + boolean: {} + last_celebrated_helper: + name: Last-celebrated helper + description: A single input_text helper remembering which children were already + celebrated today, as ":" entries separated by commas. The + automation skips a child whose entry for today is already stored, and rewrites + the helper with only today's entries plus the new one after each celebration. + selector: + entity: + filter: + - domain: + - input_text + multiple: false + reorder: false + source_url: https://github.com/Maniacs-Mansion/NestQuest/blob/main/custom_components/nestquest/blueprints/automation/day_complete_celebration.yaml +mode: queued +variables: + celebrate: !input celebrate + last_celebrated_helper: !input last_celebrated_helper +triggers: +- trigger: event + event_type: nestquest_child_day_complete +conditions: +- condition: template + value_template: '{{ celebrate | bool }}' +- condition: template + value_template: "{{ (trigger.event.data.child_id ~ ':' ~ now().strftime('%Y-%m-%d'))\n + \ not in ((states(last_celebrated_helper) or '')\n .split(',')\n | + select('ne', '')\n | list) }}" +actions: +- action: !input notify_target + data: + title: NestQuest day complete + message: !input celebration_message +- action: input_text.set_value + target: + entity_id: !input last_celebrated_helper + data: + value: "{%- set today = now().strftime('%Y-%m-%d') -%} {%- set ns = namespace(entries=[]) + -%} {%- for entry in (states(last_celebrated_helper) or '').split(',') -%}\n + \ {%- if entry and entry.endswith(':' ~ today) -%}\n {%- set ns.entries = + ns.entries + [entry] -%}\n {%- endif -%}\n{%- endfor -%} {{ (ns.entries + [trigger.event.data.child_id + ~ ':' ~ today]) | join(',') }}" diff --git a/blueprints/automation/Maniacs-Mansion/end_of_day_report.yaml b/blueprints/automation/Maniacs-Mansion/end_of_day_report.yaml new file mode 100644 index 0000000..86a0187 --- /dev/null +++ b/blueprints/automation/Maniacs-Mansion/end_of_day_report.yaml @@ -0,0 +1,89 @@ +blueprint: + name: NestQuest end-of-day report + description: Sends the parent an end-of-day report on how the day went. It runs + deliberately BEFORE NestQuest's midnight missed-quest sweep, so it reads the same + day's data (what is still open at report time, and how much of the day was completed) + rather than tomorrow-morning's hindsight. Every PRESENT child gets a completion + line, so the parent always receives the day's summary; children who still owe + quests get their open count called out on that line, and children who are not + present today are omitted entirely. A child whose remaining-count sensor is unavailable + is reported with the completion line only. By default the report is skipped when + every present child is already done — turn on send-even-when-clear to always get + the day's completion summary. The notify service is chosen by the household when + importing this blueprint — NestQuest never hard-codes a personal notify target. + domain: automation + input: + report_time: + name: Report time + description: Local time of day the report is sent — deliberately before the + midnight missed-quest sweep, so the report reads the same day's data. + default: '20:00' + selector: + time: {} + notify_target: + name: Notify service + description: The Home Assistant notify service to call for the report, for example + notify.mobile_app_your_phone — a placeholder; pick the notify service for + your own device. + selector: + text: + multiline: false + multiple: false + send_report: + name: Send the report + description: Turn off to keep the automation installed but skip the notification + entirely. + default: true + selector: + boolean: {} + send_even_when_clear: + name: Send even when clear + description: Send the report even when every present child has already finished + their quests today. When off (the default), an all-clear day sends nothing. + default: false + selector: + boolean: {} + source_url: https://github.com/Maniacs-Mansion/NestQuest/blob/main/custom_components/nestquest/blueprints/automation/end_of_day_report.yaml +mode: single +variables: + send_report: !input send_report + send_even_when_clear: !input send_even_when_clear +triggers: +- trigger: time + at: !input report_time +conditions: +- condition: template + value_template: '{{ send_report | bool }}' +- condition: template + value_template: "{%- set ns = namespace(any_open=false) -%}\n{%- for due in states.sensor\n + \ | selectattr('entity_id', 'search', '^sensor\\\\.nestquest_.*_quests_due_today(_\\\\d+)?$') + -%}\n {%- if due.attributes.get('present') -%}\n {%- set remaining_values + = states.sensor\n | selectattr('entity_id', 'search', '^sensor\\\\.nestquest_.*_quests_remaining_today(_\\\\d+)?$')\n + \ | selectattr('attributes.child_id', 'eq', due.attributes.child_id)\n + \ | map(attribute='state')\n | list -%}\n {%- if not remaining_values + or (remaining_values | first | int(-1)) != 0 -%}\n {%- set ns.any_open = + true -%}\n {%- endif -%}\n {%- endif -%}\n{%- endfor -%}\n{{- send_even_when_clear + | bool or ns.any_open -}}\n" +actions: +- action: !input notify_target + data: + title: NestQuest end-of-day report + message: "{%- set ns = namespace(lines=[]) -%}\n{%- for due in states.sensor\n + \ | selectattr('entity_id', 'search', '^sensor\\\\.nestquest_.*_quests_due_today(_\\\\d+)?$') + -%}\n {%- if due.attributes.get('present') -%}\n {%- set due_count = due.state + | int -%}\n {%- set completed_values = states.sensor\n | selectattr('entity_id', + 'search', '^sensor\\\\.nestquest_.*_quests_completed_today(_\\\\d+)?$')\n | + selectattr('attributes.child_id', 'eq', due.attributes.child_id)\n | + map(attribute='state')\n | list -%}\n {%- set remaining_values = + states.sensor\n | selectattr('entity_id', 'search', '^sensor\\\\.nestquest_.*_quests_remaining_today(_\\\\d+)?$')\n + \ | selectattr('attributes.child_id', 'eq', due.attributes.child_id)\n + \ | map(attribute='state')\n | list -%}\n {%- set completed_count + = (completed_values | first | int) if completed_values else 0 -%}\n {%- set + remaining_count = (remaining_values | first | int) if remaining_values else + none -%}\n {%- if remaining_count is not none and remaining_count > 0 -%}\n + \ {%- set line = due.attributes.child_name ~ ': ' ~ remaining_count ~ ' + still open (' ~ completed_count ~ '/' ~ due_count ~ ' done).' -%}\n {%- else + -%}\n {%- set line = due.attributes.child_name ~ ': ' ~ completed_count + ~ '/' ~ due_count ~ ' done.' -%}\n {%- endif -%}\n {%- set ns.lines = + ns.lines + [line] -%}\n {%- endif -%}\n{%- endfor -%}\n{{- ns.lines | join('\\n') + -}}" diff --git a/blueprints/automation/Maniacs-Mansion/unifi_quest_access.yaml b/blueprints/automation/Maniacs-Mansion/unifi_quest_access.yaml new file mode 100644 index 0000000..ca19a89 --- /dev/null +++ b/blueprints/automation/Maniacs-Mansion/unifi_quest_access.yaml @@ -0,0 +1,92 @@ +blueprint: + name: NestQuest UniFi quest access + description: One automation per child. Completely block the selected UniFi clients + after the local weekday/weekend cutoff while that child has quests remaining TODAY. + Restore network access immediately when the count reaches zero, and at midnight + even if yesterday was incomplete. A missing or unavailable quest sensor is treated + as still open after the cutoff; verify the sensor before enabling this automation. + The selected UniFi client switches are ON when access is allowed and OFF when + blocked. Do not select infrastructure or adult devices. + domain: automation + input: + remaining_sensor: + name: Child's quests remaining today + description: Select this child's NestQuest quests remaining today sensor. + selector: + entity: + filter: + - integration: nestquest + domain: + - sensor + multiple: false + reorder: false + client_switches: + name: Child's UniFi client access switches + description: Select every UniFi client access switch for this child. These switches + are ON when network access is allowed and OFF when blocked. + selector: + entity: + multiple: true + filter: + - integration: unifi + domain: + - switch + reorder: false + weekday_cutoff: + name: Monday-Friday cutoff + default: '18:25:00' + selector: + time: {} + weekend_cutoff: + name: Saturday-Sunday cutoff + default: '14:00:00' + selector: + time: {} + source_url: https://github.com/Maniacs-Mansion/NestQuest/blob/main/custom_components/nestquest/blueprints/automation/unifi_quest_access.yaml +mode: restart +variables: + remaining_sensor: !input remaining_sensor + client_switches: !input client_switches + weekday_cutoff: !input weekday_cutoff + weekend_cutoff: !input weekend_cutoff +triggers: +- trigger: state + entity_id: !input remaining_sensor +- trigger: time + at: !input weekday_cutoff +- trigger: time + at: !input weekend_cutoff +- trigger: time + at: 00:00:00 +- trigger: homeassistant + event: start +- trigger: time_pattern + minutes: /5 +conditions: +- condition: template + value_template: '{{ client_switches | count > 0 }}' +actions: +- variables: + should_block: "{{ now().strftime('%H:%M') >=\n (weekday_cutoff[:5] if now().weekday() + < 5 else weekend_cutoff[:5])\n and (states(remaining_sensor) | int(-1)) != + 0 }}" +- repeat: + for_each: '{{ client_switches }}' + sequence: + - choose: + - conditions: + - condition: template + value_template: '{{ should_block | bool and is_state(repeat.item, ''on'') + }}' + sequence: + - action: switch.turn_off + target: + entity_id: '{{ repeat.item }}' + - conditions: + - condition: template + value_template: '{{ not (should_block | bool) and is_state(repeat.item, + ''off'') }}' + sequence: + - action: switch.turn_on + target: + entity_id: '{{ repeat.item }}' diff --git a/esphome/.device-builder.json b/esphome/.device-builder.json index eec3674..ea8a92e 100644 --- a/esphome/.device-builder.json +++ b/esphome/.device-builder.json @@ -14,15 +14,15 @@ }, "_firmware_jobs": [ { - "job_id": "cd93b1b34cb5", - "configuration": "resin-curing-timer.yaml", + "job_id": "bb19d223ab5c", + "configuration": "bambu-printer-display.yaml", "job_type": "compile", "status": "completed", - "created_at": "2026-10-17T19:07:37.762042+00:00", - "started_at": "2026-10-17T19:07:37.763132+00:00", - "completed_at": "2026-10-17T19:09:40.012778+00:00", - "compile_started_at": "2026-10-17T19:07:57.149555+00:00", - "compile_ended_at": "2026-10-17T19:09:39.494762+00:00", + "created_at": "2026-10-19T19:05:44.828542+00:00", + "started_at": "2026-10-19T19:05:44.829561+00:00", + "completed_at": "2026-10-19T19:06:58.521787+00:00", + "compile_started_at": "2026-10-19T19:05:54.474763+00:00", + "compile_ended_at": "2026-10-19T19:06:57.987575+00:00", "exit_code": 0, "error": null, "port": "", @@ -31,7 +31,69 @@ "new_name": "", "depends_on": "", "dependency_released": false, - "progress": 99, + "progress": 83, + "remote_peer": "", + "remote_job_id": "", + "remote_peer_label": "", + "device_name": "", + "device_friendly_name": "", + "source": "local", + "source_pin_sha256": "", + "source_label": "", + "source_esphome_version": "", + "target_esphome_version": "", + "failure_reason": "" + }, + { + "job_id": "b4aaf902c652", + "configuration": "bambu-printer-display.yaml", + "job_type": "upload", + "status": "cancelled", + "created_at": "2026-10-18T13:33:52.743290+00:00", + "started_at": "2026-10-18T13:35:06.474973+00:00", + "completed_at": "2026-10-18T13:44:03.699755+00:00", + "compile_started_at": null, + "compile_ended_at": null, + "exit_code": -15, + "error": null, + "port": "OTA", + "flash_bootloader": false, + "is_deferred_install": false, + "new_name": "", + "depends_on": "0bac95d6302f", + "dependency_released": true, + "progress": 50, + "remote_peer": "", + "remote_job_id": "", + "remote_peer_label": "", + "device_name": "", + "device_friendly_name": "", + "source": "local", + "source_pin_sha256": "", + "source_label": "", + "source_esphome_version": "", + "target_esphome_version": "", + "failure_reason": "" + }, + { + "job_id": "197c6aafb405", + "configuration": "resin-curing-timer.yaml", + "job_type": "compile", + "status": "completed", + "created_at": "2026-10-17T19:20:52.620376+00:00", + "started_at": "2026-10-17T19:20:52.620726+00:00", + "completed_at": "2026-10-17T19:21:20.348169+00:00", + "compile_started_at": "2026-10-17T19:20:55.169397+00:00", + "compile_ended_at": "2026-10-17T19:21:19.787726+00:00", + "exit_code": 0, + "error": null, + "port": "", + "flash_bootloader": false, + "is_deferred_install": false, + "new_name": "", + "depends_on": "", + "dependency_released": false, + "progress": 36, "remote_peer": "", "remote_job_id": "", "remote_peer_label": "", @@ -736,9 +798,6 @@ "declan-a1-camera.yaml": { "mac_address": "D4:E9:F4:A3:59:E8" }, - "esp32-h2c-camera.yaml": { - "mac_address": "E8:F6:0A:87:12:B0" - }, "laundry-litter-box-camera.yaml": { "mac_address": "D4:E9:F4:A3:59:E8" }, @@ -760,16 +819,16 @@ "panel-hardware-test.yaml": { "mac_address": "68:B6:B3:33:69:50" }, - "living-room-remote.yaml": { - "mac_address": "68:B6:B3:33:69:50" - }, - "family-room-remote.yaml": { - "mac_address": "34:CD:B0:0C:3D:CC" - }, "living-room.yaml": { "mac_address": "B0:CB:D8:03:3B:6C" }, "resin-curing-timer.yaml": { "mac_address": "B0:CB:D8:D7:B9:18" + }, + "bambu-printer-display.yaml": { + "mac_address": "34:CD:B0:0E:42:A0" + }, + "family-room-control.yaml": { + "mac_address": "A4:F0:0F:68:A1:8C" } } \ No newline at end of file diff --git a/esphome/family-room-control.local.yaml b/esphome/family-room-control.local.yaml new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/esphome/family-room-control.local.yaml @@ -0,0 +1 @@ +{} diff --git a/esphome/family-room-control.yaml b/esphome/family-room-control.yaml new file mode 100644 index 0000000..c56e171 --- /dev/null +++ b/esphome/family-room-control.yaml @@ -0,0 +1,32 @@ +# Keep this file safe: it contains the unique keys for this screen. +# Wi-Fi comes from the secrets.yaml of ESPHome Device Builder. +substitutions: + DEVICE_NAME: "family-room-control" + DEVICE_FRIENDLY_NAME: "Family Room Control" + LANGUAGE: "en" + DISPLAY_MODEL: "ST7789V" + GITHUB_REF: "screens-v0.4.89" + +esphome: + name: "family-room-control" + friendly_name: "Family Room Control" + +packages: + display: + url: https://github.com/MaxGramser/homeassistant_espscreen + ref: screens-v0.4.89 + files: [packages/cyd.yaml] + refresh: 0s + local_overrides: !include family-room-control.local.yaml + +api: + encryption: + key: "T0Qgy1qdHCXD0IiO72V0AqUb1WL3F9pOQ/zg2RlG9sg=" +ota: + - platform: esphome + password: "zPMI1ZoPpQIu8yulgUiUTi8pPT8UClg9" + allow_partition_access: true +wifi: + ssid: !secret wifi_ssid + password: !secret wifi_password + power_save_mode: none