Compare commits

..

8 Commits

Author SHA1 Message Date
Joshua King f8dacd3122 Add CORS allowed origins for HTTP configuration 2026-08-23 11:40:45 -04:00
Joshua King 6e1b7eab74 Use Daylight Calendar Card on the Family Hub dashboard.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-09 15:19:15 -04:00
Joshua King f0af1eacfa Show Mon–Fri school lunch menus in a full-width panel layout.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-09 14:47:55 -04:00
Joshua King 1ee7f0c739 Add lunch menus dashboard with week-view school calendars.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-09 14:13:55 -04:00
Joshua King 8f7bae56db Pin trusted_networks auto-login to the kiosk user. 2026-08-08 22:03:31 -04:00
Joshua King 962628912d Put trusted_networks before homeassistant so login bypass can run. 2026-08-08 21:54:31 -04:00
Joshua King 0bff60aa45 Add trusted_networks auth provider for local LAN access. 2026-08-08 21:37:12 -04:00
root 1d6dce7b42 Correct the Gitea host in docs: cubecraftcreations -> cubecraftlabs
Both CLAUDE.md and print_log_context.md named the mirror as
code.cubecraftcreations.com. The actual `origin` is
https://code.cubecraftlabs.com/overseer/Home-Assistant.git, and the
documented host returns 404 for that repo path, so anyone following the
docs to find or clone the mirror was sent to the wrong server.

Record the full origin URL in CLAUDE.md rather than just the hostname, so
the next reader does not have to infer the path.

Note this is only about the git host. cubecraftcreations.com is used
elsewhere in this setup and is left alone.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-27 13:19:58 -04:00
8 changed files with 265 additions and 13 deletions
+1 -1
View File
@@ -4,7 +4,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
## What this repo is
The Home Assistant configuration directory (`/config` on the live HAOS host, mirrored to this git repo). It is **configuration, not application code** — there is no build or test suite. "Running" a change means HA loads it; validation is by config-check and reload, not unit tests. The repo is also mirrored to a self-hosted Gitea at `code.cubecraftcreations.com`.
The Home Assistant configuration directory (`/config` on the live HAOS host, mirrored to this git repo). It is **configuration, not application code** — there is no build or test suite. "Running" a change means HA loads it; validation is by config-check and reload, not unit tests. The repo is also mirrored to a self-hosted Gitea at `code.cubecraftlabs.com` (`origin` is `https://code.cubecraftlabs.com/overseer/Home-Assistant.git`).
Edits here only take effect after they reach the live host and HA reloads the affected domain. There is no compile step; a malformed YAML file breaks the domain it belongs to (or all of HA if it's `configuration.yaml`).
+12
View File
@@ -1,5 +1,15 @@
homeassistant:
packages: !include_dir_named packages/
auth_providers:
- type: trusted_networks
trusted_networks:
- 192.168.69.0/24
- 10.60.1.0/24
trusted_users:
192.168.69.0/24: 66e8deaeb6944b65a6ed652d671599e2
10.60.1.0/24: 66e8deaeb6944b65a6ed652d671599e2
allow_bypass_login: true
- type: homeassistant
# Loads default set of integrations. Do not remove.
default_config:
@@ -34,6 +44,8 @@ http:
use_x_forwarded_for: true
trusted_proxies:
- 10.60.1.1
cors_allowed_origins:
- https://dakboard.com
### notify_events ###
notify_events:
+1
View File
@@ -1,6 +1,7 @@
voice-alarms: !include voice-alarms.yaml
family-hub: !include family-hub.yaml
at-a-glance: !include at-a-glance.yaml
lunch-menus: !include lunch-menus.yaml
# Each key is the dashboard's url_path, and HA requires it to contain a hyphen
# (see homeassistant/components/lovelace: url_path validation). A hyphen-less
# key fails validation for the whole `lovelace:` block, not just its own entry,
+5
View File
@@ -0,0 +1,5 @@
mode: yaml
title: Lunch Menus
icon: mdi:food-apple
show_in_sidebar: true
filename: dashboards/views/lunch-menus-dashboard.yaml
+6 -11
View File
@@ -80,23 +80,18 @@ views:
round_temperature: false
# ── Family Calendar ──
- type: calendar
# Daylight Calendar Card (HACS: daylight-calendar-card). Agenda mirrors
# the old stock listWeek “upcoming” feel on a wall panel.
- type: custom:daylight-calendar-card
title: Upcoming Events
initial_view: listWeek
default_view: agenda
color_scheme: auto
preference_storage_key: family_hub_calendar
entities:
- calendar.family_events
- calendar.birthdays
- calendar.holidays_in_united_states
- calendar.gmail_calendar
card_mod:
style: |
ha-card {
--calendar-day-color: var(--cc-text, var(--primary-text-color));
--calendar-day-weekday-color: var(--cc-text-muted, var(--secondary-text-color));
--fc-today-bg-color: var(--cc-accent-soft);
--fc-event-bg-color: var(--cc-accent);
--fc-event-border-color: var(--cc-accent);
}
# ── Quick Glance ──
- type: heading
+177
View File
@@ -0,0 +1,177 @@
# Lunch Menus — school cafeteria calendars (MonFri each)
#
# Panel layout so the title spans the full width and the three school cards
# sit in one row. Menus come from packages/lunch_menus.yaml (stock calendar
# cards cannot limit to a school-week window).
title: Lunch Menus
theme: cubecraft
views:
- title: Lunch Menus
path: lunch-menus
icon: mdi:food-apple
theme: cubecraft
type: panel
cards:
- type: vertical-stack
cards:
- type: markdown
content: |
{% set wd = now().weekday() %}
{% if wd >= 5 %}
{% set monday = today_at() + timedelta(days=(7 - wd)) %}
{% else %}
{% set monday = today_at() - timedelta(days=wd) %}
{% endif %}
{% set friday = monday + timedelta(days=4) %}
# School Lunch Menus
## {{ monday.strftime("%b %-d") }} {{ friday.strftime("%b %-d, %Y") }}
card_mod:
style: |
ha-card {
background: var(--cc-gradient);
color: #fff;
border: none;
border-radius: var(--cc-radius-brand);
box-shadow: var(--cc-shadow-brand);
padding: 6px 12px;
text-align: center;
}
ha-card h1 {
margin: 0;
font-size: 1rem;
font-weight: 600;
letter-spacing: 0.02em;
opacity: 0.85;
color: #fff;
}
ha-card h2 {
margin: 2px 0 0 0;
font-size: 1.5rem;
font-weight: 800;
letter-spacing: -0.01em;
color: #fff;
}
- type: grid
columns: 3
square: false
cards:
- type: markdown
title: BHS Lunch Menu
entity_id: sensor.bhs_lunch_menu_week
content: |
{% set events = state_attr('sensor.bhs_lunch_menu_week', 'events') or [] %}
{% if events is string %}{% set events = [] %}{% endif %}
{% set wd = now().weekday() %}
{% if wd >= 5 %}
{% set monday = today_at() + timedelta(days=(7 - wd)) %}
{% else %}
{% set monday = today_at() - timedelta(days=wd) %}
{% endif %}
{% for offset in range(5) %}
{% set d = (monday + timedelta(days=offset)).date() %}
{% set d_str = d.isoformat() %}
{% set ns = namespace(found=false) %}
### {{ d.strftime("%A") }}
{{ d.strftime("%b %-d") }}
{% for e in events if e.start is defined and e.start[:10] == d_str %}
{% set ns.found = true %}
- {{ e.summary }}
{% endfor %}
{% if not ns.found %}
_No menu listed_
{% endif %}
{% endfor %}
card_mod: &cc_menu
style: |
ha-card .card-header {
color: var(--cc-text, var(--primary-text-color));
font-weight: 700;
}
ha-markdown {
padding-top: 0 !important;
}
ha-markdown h3 {
margin: 0.85rem 0 0.15rem 0;
font-size: 1rem;
font-weight: 700;
color: var(--cc-accent, var(--primary-color));
}
ha-markdown h3:first-child {
margin-top: 0;
}
ha-markdown p {
margin: 0 0 0.35rem 0;
color: var(--cc-text-muted, var(--secondary-text-color));
font-size: 0.85rem;
}
ha-markdown ul {
margin: 0 0 0.5rem 0;
padding-left: 1.1rem;
}
ha-markdown li {
margin: 0.15rem 0;
color: var(--cc-text, var(--primary-text-color));
}
ha-markdown em {
color: var(--cc-text-muted, var(--secondary-text-color));
}
- type: markdown
title: BIS School Lunches
entity_id: sensor.bis_lunch_menu_week
content: |
{% set events = state_attr('sensor.bis_lunch_menu_week', 'events') or [] %}
{% if events is string %}{% set events = [] %}{% endif %}
{% set wd = now().weekday() %}
{% if wd >= 5 %}
{% set monday = today_at() + timedelta(days=(7 - wd)) %}
{% else %}
{% set monday = today_at() - timedelta(days=wd) %}
{% endif %}
{% for offset in range(5) %}
{% set d = (monday + timedelta(days=offset)).date() %}
{% set d_str = d.isoformat() %}
{% set ns = namespace(found=false) %}
### {{ d.strftime("%A") }}
{{ d.strftime("%b %-d") }}
{% for e in events if e.start is defined and e.start[:10] == d_str %}
{% set ns.found = true %}
- {{ e.summary }}
{% endfor %}
{% if not ns.found %}
_No menu listed_
{% endif %}
{% endfor %}
card_mod: *cc_menu
- type: markdown
title: HMS Lunch Menu
entity_id: sensor.hms_lunch_menu_week
content: |
{% set events = state_attr('sensor.hms_lunch_menu_week', 'events') or [] %}
{% if events is string %}{% set events = [] %}{% endif %}
{% set wd = now().weekday() %}
{% if wd >= 5 %}
{% set monday = today_at() + timedelta(days=(7 - wd)) %}
{% else %}
{% set monday = today_at() - timedelta(days=wd) %}
{% endif %}
{% for offset in range(5) %}
{% set d = (monday + timedelta(days=offset)).date() %}
{% set d_str = d.isoformat() %}
{% set ns = namespace(found=false) %}
### {{ d.strftime("%A") }}
{{ d.strftime("%b %-d") }}
{% for e in events if e.start is defined and e.start[:10] == d_str %}
{% set ns.found = true %}
- {{ e.summary }}
{% endfor %}
{% if not ns.found %}
_No menu listed_
{% endif %}
{% endfor %}
card_mod: *cc_menu
+62
View File
@@ -0,0 +1,62 @@
################################################################################
# lunch_menus.yaml
#
# Pulls MonFri of the current school week from each lunch Google Calendar into
# template sensors. On Sat/Sun, advances to next week's Monday. The Lunch Menus
# dashboard renders those attributes — the stock calendar card has no school-
# week view.
################################################################################
template:
- trigger:
- platform: time_pattern
minutes: "/15"
- platform: homeassistant
event: start
- platform: event
event_type: event_template_reloaded
action:
- variables:
# weekday(): Mon=0 … Sun=6. Weekend → next Monday.
school_monday: >-
{% set wd = now().weekday() %}
{% if wd >= 5 %}
{{ (today_at() + timedelta(days=(7 - wd))).isoformat() }}
{% else %}
{{ (today_at() - timedelta(days=wd)).isoformat() }}
{% endif %}
- action: calendar.get_events
target:
entity_id:
- calendar.bhs_lunch_menu
- calendar.bis_school_lunches
- calendar.hms_lunch_menu
data:
start_date_time: "{{ school_monday }}"
duration:
days: 5
response_variable: agenda
sensor:
- name: "BHS Lunch Menu Week"
unique_id: bhs_lunch_menu_week
icon: mdi:food-apple
state: "{{ agenda['calendar.bhs_lunch_menu'].events | count }}"
attributes:
events: "{{ agenda['calendar.bhs_lunch_menu'].events }}"
week_start: "{{ school_monday }}"
- name: "BIS Lunch Menu Week"
unique_id: bis_lunch_menu_week
icon: mdi:food-apple
state: "{{ agenda['calendar.bis_school_lunches'].events | count }}"
attributes:
events: "{{ agenda['calendar.bis_school_lunches'].events }}"
week_start: "{{ school_monday }}"
- name: "HMS Lunch Menu Week"
unique_id: hms_lunch_menu_week
icon: mdi:food-apple
state: "{{ agenda['calendar.hms_lunch_menu'].events | count }}"
attributes:
events: "{{ agenda['calendar.hms_lunch_menu'].events }}"
week_start: "{{ school_monday }}"
+1 -1
View File
@@ -55,6 +55,6 @@ The finish automation condition checks `| length == 36` on the stored ID.
## HA Setup
- HAOS on x86 server
- Gitea repo at code.cubecraftcreations.com (username: overseer)
- Gitea repo at code.cubecraftlabs.com (username: overseer)
- IoT VLAN: 192.168.69.x, Main: 192.168.1.x
- ~150 devices, UniFi networking