Initial sync: memory, agents, commands, and gitignore

Syncs project memory, custom agents, and commands across machines.
Excludes secrets (settings.json), session state, and runtime files.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Chris
2026-06-11 09:19:24 -05:00
commit 33cab10cef
47 changed files with 1229 additions and 0 deletions
@@ -0,0 +1,4 @@
- [MQTT HA Integration Plan](project_mqtt_ha_plan.md) — Plan to add MQTT discovery integration with Home Assistant; broker is Mosquitto on HA
- [Teach Mode + Photo-Tune Plan](project_calibration_plan.md) — Teach mode wizard UI + Claude Code photo-assisted calibration skill
- [Feature Branches Status](project_branches.md) — All feature branches on fork with implementation status and PR info
- [Flask Dev Port](feedback_port.md) — Run Flask dev server on port 8080, not default 80
@@ -0,0 +1,11 @@
---
name: Flask dev server port
description: User runs the Flask dev server on port 8080, not the default port 80
type: feedback
---
Run the Flask dev server on port 8080 for local testing.
**Why:** User preference — they've been using 8080 consistently.
**How to apply:** When starting the Flask app for testing, always use port 8080 instead of the default.
@@ -0,0 +1,25 @@
---
name: Feature branches and PRs status
description: All feature branches on csader/SplitFlapDisplay fork with implementation status and PR info
type: project
---
Fork: `github.com/csader/SplitFlapDisplay` (upstream: `adamgmakes/SplitFlapDisplay`)
Clone: `/Users/csader/Downloads/SplitFlapDisplay`
**Why:** Contributing features back to the original split-flap display project.
**Branches and status:**
| Branch | Status | PR |
|--------|--------|----|
| `feature/5-module-bus-bar` | Merged with wrong files | #6 merged, #7 fix open |
| `fix/5-module-bus-bar-files` | Correct FAB2 files | #7 open |
| `feature/mqtt-home-assistant` | Implemented, pushed | Not yet PR'd |
| `feature/teach-mode` | Implemented, pushed | Not yet PR'd |
| `feature/photo-tune` | Implemented, pushed | Not yet PR'd |
| `feature/expanded-sports` | Implemented, pushed | Not yet PR'd |
| `feature/simulator` | Implemented, pushed | Not yet PR'd |
| `feature/animated-preview` | Implemented, pushed | #8 open |
**How to apply:** Check branch status before working. User wants to test features before filing PRs for most branches.
@@ -0,0 +1,20 @@
---
name: Teach Mode + Photo-Tune Calibration Plan
description: Plan for teach mode calibration wizard in web UI and Claude Code photo-assisted tuning skill for the split-flap display
type: project
---
Plan to add two calibration features to the split-flap display.
**Why:** Calibrating 45 modules across 64 characters is tedious. Teach mode simplifies the web UI flow, and photo-tune lets Claude Code do the visual inspection automatically.
**How to apply:** When implementing, the full plan is at `.claude/plans/cached-beaming-twilight.md`. Three files involved:
- `SplitFlap-RPI-FRONTEND/frontend_code_apr24/templates/index.html` — teach mode modal (HTML/CSS/JS, ~250 lines)
- `SplitFlap-RPI-FRONTEND/frontend_code_apr24/app.py``GET /tuning_status` convenience endpoint (~25 lines)
- `.claude/commands/photo-tune.md` — new Claude Code slash command for photo-assisted tuning
**Key decisions:**
- Teach mode is a separate modal from existing auto-tune (don't modify auto-tune)
- Reuses existing `/auto_tune` API endpoints (home, goto_char, adjust, get_positions)
- Photo-tune skill accepts Pi hostname as parameter, documents the iterative photo→analyze→nudge workflow
- No new Python dependencies, no firmware changes
@@ -0,0 +1,22 @@
---
name: MQTT Home Assistant Integration Plan
description: Detailed plan for adding MQTT discovery-based Home Assistant integration to the split-flap display Flask app
type: project
---
Plan to add MQTT integration to the split-flap display so Home Assistant can control it natively.
**Why:** The display is currently only controllable via the Flask web UI. MQTT lets HA send text, switch apps/animations, and see current state via auto-discovered entities.
**How to apply:** When implementing, all changes go in `SplitFlap-RPI-FRONTEND/frontend_code_apr24/app.py`. The full plan is saved at `.claude/plans/cached-beaming-twilight.md`.
**Summary of approach:**
- Add `paho-mqtt` dependency
- MQTT settings added to `load_settings()` defaults: broker (homeassistant.local), port (1883), user, password, enabled flag
- 3 HA entities via MQTT Discovery: `text` (send display text), `select` (switch app/animation mode), `sensor` (current display string)
- All entities grouped under one device, shared LWT availability on `splitflap/availability`
- Command handlers reuse existing patterns: `send_to_display()`, `active_app`, `stop_event.set()`, `loop_delay` logic
- State published with `retain=True` from `send_to_display()` and route handlers so HA stays in sync
- Graceful fallback if MQTT unavailable (same pattern as serial fallback)
- Firmware and serial protocol unchanged
- Broker: Mosquitto add-on on Home Assistant
@@ -0,0 +1 @@
- [Kiro agents adopted](user_kiro_agents.md) — homelab + obsidian agents with MCP servers mirrored to ~/.mcp.json
@@ -0,0 +1,13 @@
---
name: Kiro agents adopted
description: User has two Kiro CLI agents (homelab, obsidian) whose MCP servers and personas are now available in Claude Code
type: user
---
User runs Kiro CLI with two custom agents:
1. **homelab** — "You are an expert IT, Security, and DevOps person, who specializes in helping less knowledgeable home owners setup and run their homelabs." Uses Context7 and Obsidian (local filesystem at ~/Documents/Sader) MCP servers.
2. **obsidian** — "You are an expert Obsidian user who knows how to best organize a second brain tool." Uses Obsidian remote MCP (localhost:3001).
MCP servers from both agents are configured in ~/.mcp.json for Claude Code. When the user asks about homelab/IT/DevOps topics or Obsidian/note-taking, adopt the relevant agent persona.
@@ -0,0 +1,23 @@
# Home Assistant ePaper Remote - Project Memory
## Future Entity Types to Add
High value additions:
- **Climate/Thermostat** - Temperature setpoint slider, very common use case
- **Scene** - One-tap button to activate (no state feedback needed)
- **Script** - Run automation scripts (same as scene)
- **Lock** - Lock/unlock doors (button with state)
- **Media player** - Volume slider, play/pause button
Lower priority:
- **Input number** - Generic slider for HA helpers
- **Input boolean** - Generic toggle (functionally same as switch)
- **Vacuum** - Start/stop/dock commands
## Key Implementation Details
- Firmware slider bar is always 100px (BUTTON_SIZE constant) - not configurable
- SLIDER_HEIGHT = 170 in configurator (100px bar + 70px label space)
- Label drawn at widget top, bar drawn at widget bottom
- Configurator uses File System Access API for direct project writes
- Icons generated from MDI SVGs, converted to 1-bit BMP in JavaScript
@@ -0,0 +1,3 @@
- [User Profile](user_profile.md) — Chris, homelab enthusiast, Lululemon Mirror mod, delegates all code to Claude
- [Project Architecture](project_architecture.md) — Flutter app for mirror display + phone remote on Pi 4, voice/motion
- [Workout API](reference_workout_api.md) — workout-player endpoints at 192.168.86.172:8091, video/progress/stream contracts
@@ -0,0 +1,29 @@
---
name: project-architecture
description: "Mirror OS - Flutter app for hacked Lululemon Mirror on Pi 4 with phone remote, voice, and motion"
metadata:
node_type: memory
type: project
originSessionId: 826ce615-b163-439f-aaa9-7b3be198b03a
---
Mirror OS is a custom app platform for a hacked Lululemon Mirror running on Raspberry Pi 4.
**Key decisions:**
- Flutter (not web kiosk, not Qt) — single codebase for mirror display + phone remote
- Pi 4 (4GB) as compute
- Vizio board used as HDMI passthrough only (no rotation capability)
- Pi handles rotation via wlr-randr or DRM/KMS config
- Display is portrait 1080x1920, not touch-enabled
**Why:** Flutter gives one codebase for both the mirror display and the phone companion app. Phone remote is half the project — keeping it unified avoids drift. Performance is adequate for dashboard/media on Pi 4.
**Features planned:**
- Dashboard (clock, weather, calendar, widgets)
- Workout player (integrates with existing workout-player homelab service)
- Media player (YouTube, Plex, local video/music via mpv/media_kit)
- Voice control (OpenWakeWord + Whisper, local processing)
- Motion-activated wake (PIR or mmWave on GPIO)
- Phone companion app as primary control interface
**How to apply:** All code goes in /Users/csader/mirror-os. Flutter project with shared packages for logic, separate entry points for mirror display vs phone remote.
@@ -0,0 +1,23 @@
---
name: reference-workout-api
description: "Workout-player API contract at 192.168.86.172:8091 — endpoints for videos, stats, routines, streaming"
metadata:
node_type: memory
type: reference
originSessionId: 826ce615-b163-439f-aaa9-7b3be198b03a
---
Workout-player service runs at http://192.168.86.172:8091 on the homelab.
**Endpoints (derived from prior mirror-os JS codebase):**
- `GET /api/videos?folder=<path>` — list videos, optional folder filter. Returns `{videos: [...], folders: [...]}`
- `GET /api/stats` — aggregate stats (total, completed, inProgress counts)
- `GET /api/routines` — list routines. Returns `{routines: [...]}`
- `PATCH /api/videos` — save progress. Body: `{videoId, position, percent}`
- `GET /api/stream?path=<encoded_path>` — stream video file directly
**Video object fields:** id, title, path, folder, duration, trimStart, trimEnd, lastPosition, percentCompleted, restSeconds
**State machine for workout sessions:** browse → playing → rest (between queue items) → complete
**How to apply:** When building the workout integration in Flutter, use these endpoints. The stream URL can be passed directly to media_kit/mpv for playback.
@@ -0,0 +1,18 @@
---
name: user-profile
description: "Chris - homelab enthusiast with Lululemon Mirror mod project, prefers Claude writes all code"
metadata:
node_type: memory
type: user
originSessionId: 826ce615-b163-439f-aaa9-7b3be198b03a
---
Chris runs a homelab (192.168.86.x network). Has a self-hosted workout-player app at 192.168.86.172:8091 with video library, routines, and progress tracking.
Hardware: Lululemon Mirror with BOE DV430FHM-NN5 panel (43" 1080p IPS, portrait 1080x1920), Vizio replacement mainboard (HDMI passthrough to LVDS), Raspberry Pi 4.
Prefers Claude writes all the code. Comfortable reviewing and making architectural decisions but delegates implementation entirely.
Phone: Android (primary target for remote app testing/deployment).
Gitea instance at git.chrissader.com (org: hermes).
@@ -0,0 +1 @@
- [PlateRunner deployment](reference_deployment.md) — Deploy via SSH to prox2docker, git pull, docker compose rebuild on VM 103
@@ -0,0 +1,19 @@
---
name: PlateRunner deployment
description: PlateRunner deploy process — SSH to prox2docker, git pull, docker compose rebuild on VM 103
type: reference
---
PlateRunner is running on the homelab at platerunner.chrissader.com.
**Host:** VM 103 (192.168.86.122) on Proxmox2
**SSH alias:** `prox2docker` (csader@192.168.86.122)
**Stack path:** `/opt/docker/stacks/platerunner`
**Container:** `platerunner-platecycler-1` on port 3001 → 3000
**Deploy process:**
```bash
ssh prox2docker "cd /opt/docker/stacks/platerunner && git pull && docker compose up -d --build"
```
**Note:** git push from this machine may hang on credential prompt — user may need to push manually.
@@ -0,0 +1,5 @@
- [User profile](user_profile.md) — CNC domain expert, strong spatial reasoning, builds ShaperSlicer
- [Be direct](feedback_be_direct.md) — Grasp geometry concepts immediately, don't over-clarify
- [Design before code](feedback_design_first.md) — First-principles algorithm design before reading existing code
- [Silhouette cookie cutter](project_silhouette_cookie_cutter.md) — Cumulative silhouette splits layers into boundary vs interior
- [Shaper Origin concepts](project_shaper_origin_concepts.md) — SVG fill colors, cut types, BenchPilot behavior, tab semantics
@@ -0,0 +1,11 @@
---
name: Be direct about geometry concepts
description: User gets frustrated when spatial/geometry concepts need repeated explanation
type: feedback
---
User has strong spatial reasoning and expects the same. When they describe a geometry operation (e.g., "cookie cutter" = use the silhouette to split shapes), grasp it immediately and act on it. Don't ask clarifying questions about concepts they've already explained clearly.
**Why:** User expressed frustration: "I'm struggling with patience here because this seems like a straightforward problem to solve to me."
**How to apply:** When user describes a geometry operation in plain language, translate it directly to boolean ops and implement. Don't over-complicate or second-guess their spatial intuition.
@@ -0,0 +1,11 @@
---
name: Design before code
description: User prefers first-principles algorithm design before reading existing code
type: feedback
---
When tackling complex geometry/algorithm problems, design the ideal approach from first principles BEFORE reading the existing code. Then compare the ideal to what's implemented to find bugs.
**Why:** User explicitly asked for this workflow — "I'd rather have you think of how you'd recommend doing it FIRST, then compare to the existing code." Jumping straight to code reading led to patches that missed the root cause.
**How to apply:** For non-trivial algorithm bugs, start with a clean-room design of what the correct approach should be, then diff against the implementation.
@@ -0,0 +1,17 @@
---
name: Shaper Origin cut type semantics
description: How Shaper Origin interprets SVG fill colors and cut types — critical for correct SVG output
type: project
---
Shaper Origin SVG semantics:
- White fill (#FFFFFF) = inside cut (vector follow along the path — faster, used for boundary/cutout)
- Gray fill (#808080) = pocket cut (area removal — raster/spiral clearing, used for interior cavities)
- Black fill (#000000) = outside cut
- `shaper:cutDepth` attribute sets depth per path
- BenchPilot mode may cut layers in ANY order (not necessarily top-down), so each layer must only contain material not already removed by other layers
- Tabs are bridges in the boundary trough that hold the piece in stock — they should NEVER affect interior/model geometry
**Why:** These semantics drive the entire SVG generation logic. Getting fill colors wrong means the machine uses the wrong cut strategy.
**How to apply:** Always verify boundary paths get white fill and interior paths get gray fill. Tabs only subtract from boundary.
@@ -0,0 +1,17 @@
---
name: Silhouette cookie cutter approach
description: The cumulative model silhouette is used to split every layer into boundary vs interior
type: project
---
The correct way to separate boundary (cutout ring) from interior (model cavities) in each layer:
1. `cumulativeCross` = union of all cross-sections across all depths = the model's full silhouette from the cutting direction
2. For each layer's pocket geometry:
- Boundary = pocket MINUS silhouette (material outside the model outline)
- Interior = pocket INTERSECT silhouette (cavities within the model outline)
3. This silhouette is constant across all layers — it's the "cookie cutter"
**Why:** Previously used per-layer model outline which changed shape at every depth and failed for solid shapes without holes. The silhouette approach works universally.
**How to apply:** If boundary/interior separation needs changes, the silhouette (cumulativeCross) is the dividing line. Filter boolean sliver artifacts with area < 0.01mm².
@@ -0,0 +1,7 @@
---
name: User profile
description: CNC/woodworking domain expert building ShaperSlicer, strong spatial reasoning about geometry operations
type: user
---
User is the developer of ShaperSlicer. Has deep domain knowledge of CNC routing, specifically the Shaper Origin (handheld and BenchPilot/gantry modes). Understands how the machine interprets SVG cut types (pocket, inside, outside), depth ordering, and Autopass behavior. Thinks in terms of physical machining operations — "cookie cutter", "trough", "air cutting". Can identify geometry bugs visually from 2D layer previews.
+15
View File
@@ -0,0 +1,15 @@
- [SSH directly to VMs](feedback_ssh_directly.md) — Use csader@VM_IP, not qm guest exec through Proxmox hosts
- [HA API access patterns](reference_ha_api_access.md) — REST/WebSocket APIs for automations, entity registry, history
- [Verify before bulk delete](feedback_verify_before_bulk_delete.md) — Confirm entities are actual duplicates before mass removal
- [Pixel 10 Pro Fold for notifications](user_notification_phone.md) — Use notify.mobile_app_pixel_10_pro_fold, not iPhone or notify.chris
- [House orientation & shade positions](user_house_orientation.md) — West door (225-315°), east windows (45-135°), per-shade close values
- [HA automation patterns](reference_ha_automation_patterns.md) — API patterns, scene save/restore, categories, weather/azimuth
- [NR migration status](project_nr_migration.md) — Which flows are in NR vs HA after 2026-03-28 migration
- [Backup before HA updates](feedback_backup_before_updates.md) — Always use backup:true when running update.install on add-ons
- [MantelMount control via Bond RF](reference_mantelmount.md) — Bond RF entities, contact sensor mapping, run-then-direction sequence
- [Zulip on VM 103](reference_zulip.md) — crew.chrissader.com, tus upload fix, post-start.sh after recreate
- [Caddyfile edits must preserve inode](feedback_caddyfile_inode.md) — Use sed/tee, not Python write(); restart container if inode broken
- [Check for stale/duplicate entities](feedback_stale_entity_check.md) — Re-paired devices create new entities; old named ones go stale and silently fail
- [HA Blue SSH access](reference_ha_blue_ssh.md) — Port 2222, user csader (not root), id_ed25519 key
- [HACS and Mushroom on HA Blue](reference_hacs_mushroom.md) — HACS, Mushroom, weatheralerts install details
- [Never use "new" as automation ID](feedback_automation_ids.md) — Use unique numeric IDs when creating automations via REST API
@@ -0,0 +1,11 @@
---
name: Never use "new" as automation ID
description: Creating HA automations via API with id "new" overwrites existing automations that also have id "new"
type: feedback
---
When creating automations via `POST /api/config/automation/config/new`, HA assigns the literal ID "new". If you create a second automation the same way, it overwrites the first.
**Why:** The bird waterer automation got overwritten by the hail automation because both had id "new".
**How to apply:** Always use a unique numeric ID (e.g., timestamp-based like `1712800000001`) when creating automations via the REST API. After creating with `/new`, immediately re-save with a proper ID and delete the "new" entry.
@@ -0,0 +1,11 @@
---
name: Always backup before HA add-on updates
description: Use backup:true parameter when running update.install on HA add-ons
type: feedback
---
Always pass `backup: true` when calling `update.install` on HA add-ons. This creates a snapshot before each update.
**Why:** User explicitly requested this as standard practice — safety net in case an update breaks something.
**How to apply:** Any time running firmware/add-on updates via the HA API, include `"backup": true` in the service call data.
@@ -0,0 +1,11 @@
---
name: Caddyfile edits must preserve inode
description: Python open().write() breaks Docker bind mounts by creating new inodes — use sed -i or tee instead
type: feedback
---
Never edit the Caddyfile (or any Docker bind-mounted file) with Python's `open(path, 'w').write()` — it creates a new inode and Docker keeps reading the old one.
**Why:** Docker bind mounts track the original file inode. Python's write creates a new file, so `caddy reload` reads stale content. This caused hours of debugging where Caddy appeared to ignore config changes.
**How to apply:** Use `sed -i` for substitutions or `cat newfile | tee /path/to/file > /dev/null` for full rewrites. If the inode is already broken, restart the container (`docker restart caddy`) to re-mount. Always verify with `docker exec caddy cat /etc/caddy/Caddyfile` after edits.
@@ -0,0 +1,11 @@
---
name: SSH directly to VMs
description: User prefers direct SSH to VMs (csader@IP) instead of going through Proxmox qm guest exec
type: feedback
---
SSH directly to VMs using `csader@<VM_IP>` instead of routing through Proxmox hosts with `qm guest exec`. Root SSH is blocked on VMs — use csader user with sudo when needed.
**Why:** User explicitly corrected this approach twice. Going through Proxmox is slower and unnecessary when direct SSH works.
**How to apply:** For any VM operations (.134, .122, .146, .172), SSH as csader directly. Only use Proxmox host SSH (root@.66/.67/.68) for host-level operations like `qm list`, `pct list`, `pct exec` for LXCs.
@@ -0,0 +1,11 @@
---
name: Check for stale/duplicate entities when automations partially fail
description: HA automations that notify but don't actuate devices — look for duplicate entities from re-paired devices
type: feedback
---
When an HA automation fires (notifications work) but device actions silently fail, check for duplicate entities. Devices that get re-paired create new entities with raw addresses (e.g., `switch.0xa4c138...`) while the old named entity goes stale.
**Why:** Chicken sprinkler automation sent notifications fine but `switch.turn_on` went to a dead entity. The working device had a new entity with a Zigbee address. Silent failure — no error in HA.
**How to apply:** When debugging partial automation failures, list all entities matching the device name and check `last_changed` dates. If the named entity hasn't changed in days but a raw-address entity is active, the device was re-paired. Fix by removing the stale entity and renaming the live one.
@@ -0,0 +1,11 @@
---
name: Verify duplicates before bulk entity cleanup
description: User wants confirmation that entities are actual duplicates before mass deletion
type: feedback
---
When cleaning up stale/duplicate HA entities, verify each `_N` suffixed entity actually has a newer counterpart before removing it. Don't assume all `_2` entities are stale — some may be the current active version if there's no `_3`.
**Why:** User stopped a bulk delete of 44 `_2` entities — only 5 of them were confirmed duplicates with `_3` counterparts. The other 39 were the active versions.
**How to apply:** Before any bulk entity cleanup, cross-reference suffixed entities to confirm duplicates exist. Only remove entities with a confirmed newer counterpart.
@@ -0,0 +1,29 @@
---
name: Node-RED flows still active
description: Which NR flows remain active vs migrated to HA after 2026-03-28 migration
type: project
---
Node-RED migration completed 2026-03-28. Most flows migrated to HA automations.
**Still in Node-RED (do not duplicate in HA):**
- Follow Me Alexa — Alexa device-specific voice command routing
- Presence — Room-level BLE/motion/bed presence tracking feeding input_select helpers
- Home Security System — Alarm mode handling (migration planned but needs discussion on triggers)
- Flow 1 — Alexa actionable notification support for theater mode
**Migrated to HA (disable in NR):**
- Goodnight, Lights, Climate, Blinds, Theater Mode, Wake Up
- Lights sub-flows (2026-03-28): Main Switch→Haiku Light, 3-way→Haiku Light, Auto Garage Lights (ratgdo doors), Outdoor+Garage Light Sync, Christmas Spheres Timer
**Deleted from NR:**
- Chicken Sprinkler, LLM Vision, Automatic Lights
**Why:** Consolidating automations into HA for easier management. NR Presence flow is foundational and complex — stays in NR for now.
**How to apply:** When working on automations, check whether the flow is in NR or HA to avoid conflicts. Presence-related input_selects are set by NR — HA automations can read them but shouldn't write to them.
**Removed integrations (2026-03-28):**
- Zigbee Lock Manager — replaced by Z2M direct, Lockly also removed (timeouts on battery lock)
- Neato, Konnected, Telegram — no entities, ghost integrations
- Front door lock codes managed on keypad, not via HA (Yale Assure SL sleeps too aggressively for remote code management)
@@ -0,0 +1,26 @@
---
name: Home Assistant API access patterns
description: How to query HA automations, entity registry, history, and other data not available via MCP tools
type: reference
---
The HA MCP tools (`mcp__home-assistant__*`) cover device control, entity states, and live context — but NOT automations, entity registry, history, logbook, or traces.
For those, use the HA APIs directly:
- **REST API** (via curl): `http://192.168.86.36:8123/api/...` with Bearer token from `.mcp.json` (`home-assistant.headers.Authorization`)
- `GET /api/states` — all entity states (filter by entity_id prefix for automations, etc.)
- `GET /api/config/automation/config/<id>` — full automation config (triggers, conditions, actions) by numeric ID from entity attributes
- `GET /api/history/period/<timestamp>?filter_entity_id=...` — historical state changes
- `GET /api/logbook/<timestamp>?entity=...` — logbook entries
- **WebSocket API** (`ws://192.168.86.36:8123/api/websocket`): needed for entity registry operations
- `config/entity_registry/remove` — delete stale/duplicate entities
- `config/entity_registry/list` — full entity registry (very large response)
- Requires `websockets` Python package (installed on Mac)
- **IMPORTANT**: Must set `max_size=10*1024*1024` on `websockets.connect()` — the entity registry response exceeds the default 1MB limit
- **Trace API** (`/api/trace/<entity_id>`): may return non-standard JSON or 404. Wrap in try/except and check raw response before parsing.
- **Auth token**: Available as `$HA_TOKEN` env var (set in settings.local.json). Use `Bearer $HA_TOKEN` in curl, or `os.environ['HA_TOKEN']` in Python.
**How to apply:** When user asks about automations, entity cleanup, or historical data, use these APIs rather than saying the MCP can't do it. Always use these patterns from the main conversation — subagents may not have Bash access.
@@ -0,0 +1,30 @@
---
name: HA automation management patterns
description: How to create/update HA automations via REST/WS API, scene save/restore, categories, and established patterns
type: reference
---
Automation configs are updated via POST (not PUT):
`POST http://192.168.86.36:8123/api/config/automation/config/<numeric_id>`
Key patterns established:
- **Notifications**: `notify.mobile_app_pixel_10_pro_fold` with `"image"` for snapshots and `"clickAction"` for deep links
- **TTS**: `tts.speak` targeting `tts.home_assistant_cloud` with `media_player_entity_id` (not deprecated `tts.google_translate_say`)
- **Sun azimuth**: `sun.sun` attribute `azimuth` for directional triggers (west door = 225-315°, east windows = 45-135°). East shades have NO temp condition; front door shade still requires >80°F.
- **Weather conditions**: `sensor.openweathermap_condition` states: sunny, clear-night, partlycloudy, cloudy, rainy, pouring, snowy, fog, lightning, lightning-rainy
- **Scene save/restore**: `scene.create` with `snapshot_entities` to save state, `scene.turn_on` to restore. Used for Theater Mode and Climate thermostat save/restore.
- **Enabling/disabling automations**: WebSocket API `config/entity_registry/update` with `disabled_by: null` to enable, `disabled_by: user` to disable
- **Categories**: WebSocket `config/category_registry/create` (scope: automation) and `config/entity_registry/update` with `categories: {automation: cat_id}`
**Automation categories** (scope: automation):
- Sprinklers, Shades & Blinds, Cameras & Security, Home Theater, Lights, 3D Printers, Garage, Fans, Climate, Routines, Severe Weather
**Gotchas:**
- Don't trigger Theater Mode on TV power — TV turns on for non-movie use. Only trigger via `input_boolean.theater_mode` toggle.
- Battery-powered Zigbee devices (locks) sleep aggressively — service calls that need a response (code queries) will timeout. Lock/unlock works because Z2M queues commands.
- Always use `backup: true` when calling `update.install` on add-ons.
- Wind gust entity is `sensor.openweathermap_wind_gust` (NOT `_wind_gust_speed`).
- `weather.get_forecasts` requires `?return_response` when called via REST API. Response is at `d['service_response']['weather.openweathermap']['forecast']`.
- Template references to NWS alerts must handle empty alerts list: use `| first | default({})` pattern, not `[0]`.
**How to apply:** Reference these patterns when creating or modifying HA automations.
@@ -0,0 +1,14 @@
---
name: HA Blue SSH access
description: How to SSH into Home Assistant Blue — port 2222, user csader, not root
type: reference
---
SSH to HA Blue: `ssh -p 2222 csader@192.168.86.36`
- Port 2222 (SSH add-on)
- User `csader` — root is blocked by AllowUsers
- Key: `~/.ssh/id_ed25519` (ed25519, csader@chrissader.com)
- Use `sudo` for privileged operations (e.g., HACS install)
**How to apply:** When needing to run commands on HA Blue directly (install HACS, check custom_components, etc.), use this SSH path.
@@ -0,0 +1,13 @@
---
name: HACS and Mushroom on HA Blue
description: HACS v2026.1.0 and Mushroom v5.1.1 installed on HA Blue, plus weatheralerts integration
type: reference
---
**HACS** (v2026.1.0): Installed 2026-04-04 via SSH (`sudo wget -O - https://get.hacs.xyz | sudo bash -`). Configured via HA UI with GitHub OAuth.
**Mushroom** (v5.1.1): Installed via HACS WebSocket API (`hacs/repository/download`, repo id `444350375`). Used on Mobile Dashboard Home view for greeting card with dynamic weather/alert display.
**Weatheralerts** (v2026.1.0): Installed via HACS 2026-04-11. Configured for Williamson County (zone TXZ173, county TXC491). 90s poll interval, 20s timeout. Entity: `sensor.weatheralerts_williamson_txz173_txc491`.
**How to apply:** If HACS disappears after an HA update, re-run the install script via SSH and re-add the integration in the UI. Mushroom and weatheralerts should survive since the files persist in custom_components.
@@ -0,0 +1,21 @@
---
name: MantelMount control via Bond RF bridge
description: How the MantelMount is controlled in HA — Bond RF entities, contact sensor mapping, command sequence
type: reference
---
MantelMount is controlled via a Bond RF bridge. No native HA integration — uses assumed_state entities.
**Entities:**
- `light.mm_down_up` — "Run Button" — toggle starts/stops the motor
- `fan.mm_run` — direction control. ON = down, OFF = up
- `binary_sensor.tv_mount_angle_contact` — Zigbee door/tilt sensor. ON = mount down/extended, OFF = mount up/retracted
**Command sequence:** Toggle run button FIRST, then set direction 1s later. This is how the MM controller works — run first, then direction.
**Automation** (`automation.mantelmount_up_down`):
- Triggers on Samsung TV on/off
- TV-off condition: `switch.tv_power` (Zigbee power strip L4) must be on
- Uses contact sensor as guard (skip if already in correct position) and for retry verification after 20s
**How to apply:** When modifying mantel mount automation, always preserve run-then-direction sequence and contact sensor verification.
@@ -0,0 +1,17 @@
---
name: Zulip on VM 103
description: Zulip chat server config, tus upload fix, Caddy/Authelia setup at crew.chrissader.com
type: reference
---
Zulip runs on VM 103 (192.168.86.122) port 8085, accessible at crew.chrissader.com.
**Compose**: `/opt/docker/stacks/zulip/` (project name `zulip-docker` in `.env` to preserve volumes)
**Caddy**: Uses `route` block with `@exempt path /api/* /json/* /user_uploads/*` matcher and `reverse_proxy @exempt` before `forward_auth`. The `route` directive is critical — without it, Caddy reorders directives and runs `forward_auth` first regardless of source order. The `handle` block approach also doesn't work. Authelia also has bypass rules for the same paths as belt-and-suspenders.
**Tus upload fix**: Zulip's puppet overwrites `/etc/nginx/zulip-include/app` on container start. The tus location block needs `proxy_redirect http://crew.chrissader.com https://crew.chrissader.com;` added (inlined proxy config, no `include proxy`). Without this, tusd generates `http://` Location headers and browsers block the PATCH as mixed content.
**How to apply:** After any `docker compose up` that recreates the zulip container, run `/opt/docker/stacks/zulip/post-start.sh` to re-apply the nginx fix.
**LOADBALANCER_IPS**: Includes `172.26.0.0/16` (Docker bridge) so nginx trusts `X-Forwarded-Proto` from Caddy.
@@ -0,0 +1,16 @@
---
name: House window orientations and shade close positions
description: Window shade orientations, azimuth ranges, and fully-closed position values per shade
type: user
---
Chris's front door faces due west. The sun hits it in the afternoon/evening. Auto Front Door Shade automation uses azimuth 225°-315° range.
Living room window shades (1-5) face east. Sun-Tracking automation uses azimuth 45°-135° range. No temperature condition — fires on sun angle + clear weather only.
Fully closed shade positions (not all are 100%):
- Shades 1 & 2: position 100
- Shades 3 & 4: position 35
- Shade 5: position 60
House is in Round Rock, TX (zip 78681).
@@ -0,0 +1,9 @@
---
name: Chris uses Pixel 10 Pro Fold for HA notifications
description: Primary notification target is notify.mobile_app_pixel_10_pro_fold, not iPhone 15 or notify.chris
type: user
---
Chris's primary phone is a Pixel 10 Pro Fold. Use `notify.mobile_app_pixel_10_pro_fold` for HA push notifications — not `notify.chris` (broken/legacy) or `notify.mobile_app_iphone_15`.
For HA companion app deep links, use `clickAction` with dashboard paths like `/mobile-dashboard/sprinklers`. Snapshots attach via `"image": "/api/image_proxy/image.<entity>"`.