# Changelog

All notable changes to clucks.net are documented here.  
Follows [Common Changelog](https://common-changelog.org) and [Semantic Versioning](https://semver.org).  
Written for developers. Contains chickens.

---

## [2.6.4] - 2026-05-11

### Fixed

- **CLOSING SOON showing all morning in summer** — door countdown had a 12-hour sanity cap on `diff` intended to guard against `next_setting` rolling to tomorrow after dusk. Correct intent, wrong implementation: in mid-May Yorkshire, sunrise-to-sunset is over 15 hours, so the cap fired in the morning and fell through to CLOSING SOON. Fixed by replacing the time cap with a date check (`nextSet.toDateString() === now.toDateString()`): if `next_setting` has already rolled to tomorrow (sunset passed, door not yet closed), show CLOSING SOON; otherwise show the real countdown. Handles both summer solstice (~17h day) and winter solstice (~7h day) correctly.

## [2.6.3] - 2026-05-08

### Changed
- YouTube stream descriptions updated: "live chicken cam" front-loaded in opening paragraph and ABOUT section header for both eglu and run feeds
- Hashtag order revised: `#LiveChickenCam` and `#ChickenCam` now lead both descriptions (YouTube displays first 3 hashtags above video title in search results)

## [2.6.2] - 2026-05-07

### Fixed

- **Radiation history chart intermittent flat line** — async race condition between history pre-load and live polling could result in `radHistory` entries being out of chronological order. This caused a negative time span on the x-axis, compressing all data points to one edge and appearing as a flat line. History and sensor history now both complete loading before the live polling cycle begins.

---

## [2.6.1] - 2026-05-06

### Fixed

- **Visitor modal now works** — mapmyvisitors.com blocks iframe embedding (X-Frame-Options). Replaced iframe with a NORAD-style stats panel showing ACTIVE NOW / TODAY / ALL TIME from your own counters, with a VIEW WORLD MAP ↗ link that opens mapmyvisitors in a new tab.

---

## [2.6.0] - 2026-05-06

### Changed

- **Visitor counter upgraded to NOW / TODAY / TOTAL** — replaces the previous active/total display. TODAY resets at midnight. Clicking the counter opens the mapmyvisitors world map in a modal. The mapmyvisitors tracking pixel is also embedded invisibly for geographic data collection.

---

## [2.5.1] - 2026-05-06

### Fixed

- **Radiation chart y-axis auto-scales** — was locked to 0–110 CPM making normal background readings (10–25 CPM) appear as a flat line at the bottom. Now auto-scales to the actual data range with padding, same as sensor charts. The 50 CPM and 100 CPM threshold lines still appear when in range, providing context without crushing the visible data.

---

## [2.5.0] - 2026-05-06

### Changed

- **Radiation chart extended to 24 hours** — stores 1440 entries (1 per minute = 24hr). History pre-loaded from server on page open. X-axis now time-proportional with hourly labels (hours only, consistent with sensor charts).
- **Radiation chart hover tooltip** — mouseover snaps to nearest data point, shows CPM value + time in a colour-coded tooltip (green/amber/red matching CPM thresholds). Dot appears on the line at the hovered point.
- **Radiation chart hourly grid lines** — vertical lines at each hour boundary, same style as sensor charts.

---

## [2.4.3] - 2026-05-05

### Fixed

- **CLOSING SOON persisting after door closes on page refresh** — countdown was reading the DOM text of `#door-status` which says `ACQUIRING...` on load, causing it to always take the "door open" branch and show CLOSING SOON after dusk. Fixed by fetching `cover.the_autodoor_door` state directly from HA instead of reading the DOM. Also treats FLOCKDOWN as "closed" for countdown purposes.

---

## [2.4.2] - 2026-05-05

### Changed

- **Lockdown PERIMETER block now clickable** — hovering over LOCKED DOWN turns it amber and stops the blink. Clicking opens a modal showing the full contents of the lockdown file — useful for displaying official APHA zone information, dates, and biosecurity guidance. Modal has red border to match the alert state. Close with ✕ or click outside.

---

## [2.4.1] - 2026-05-05

### Fixed

- **Lockdown race condition** — `fetchDoor()` was racing against `fetchLockdown()` on page load, intermittently overwriting LOCKED DOWN with EGLU BREACHED. Fixed by chaining `fetchDoor()` and its 5s interval inside `fetchLockdown().then(...)` — door polling only starts after the lockdown check has definitively resolved. No blocking flags, no JS errors.

---

## [2.4.0] - 2026-05-05

### Added

- **Bird flu lockdown display** — PERIMETER block shows `LOCKED DOWN` in blinking red when a lockdown is active. Activated by creating a trigger file on the server; removed by deleting it. No automation required — manually triggered when a local avian influenza control zone is declared. Polls every 60 seconds. Lockdown state overrides the normal door open/closed display. The hens are unaware of biosecurity legislation.

---

## [2.3.0] - 2026-05-05

### Changed

- **SEO refocus on "live chicken cam"** — title, meta description, OG/Twitter tags, keywords, and h1 all updated to lead with "Live Chicken Cam" instead of "Live Hen Cam". Keywords expanded to include "live chicken camera", "chicken camera", "backyard chickens live". "Ad-free" removed from description in favour of "no ads, no signup".
- **Sensor chart modal title fixed** — was showing "LAST HOUR" incorrectly; now correctly shows "LAST 24 HOURS".
- **Sensor chart hourly time markers** — vertical grid lines at each hour boundary with HH:MM labels along the x-axis. X positions are now time-proportional (based on actual timestamps) rather than index-based.
- **Sensor chart mouseover tooltip** — hovering over the chart snaps to the nearest data point and shows value + time in a small NORAD-style tooltip. Dot appears on the line at the hovered point. Works on desktop.

---

## [2.2.0] - 2026-05-04

### Changed

- **Sensor history extended to 24 hours** — sensor history charts now show 24 hours of data (48 samples at 30-minute intervals) instead of 1 hour. Server-side cron updated to poll every 30 minutes.
- **Bottom-left links unified and aligned** — ABOUT CLUCKS.NET, WATCH IN HD ON YOUTUBE, and CHANGELOG now render as a single flex column, guaranteeing pixel-perfect left alignment. ABOUT gets ⓘ prefix. WATCH IN HD renamed to WATCH IN HD ON YOUTUBE.
- **Clickable sensor hint removed** — the ▸ triangle from NORAD stat labels removed. Cursor change on hover is sufficient affordance.

---

## [2.1.0] - 2026-05-04

### Added

- **Sensor history charts** — EXT TEMP, INT TEMP, EXT HUMIDITY, INT HUMIDITY, AMBIENT LIGHT, and WIND blocks are now all clickable (shown with ▸). Clicking opens a shared NORAD-aesthetic modal with a canvas line chart of the last 60 readings (1 per minute = 1 hour). Y-axis auto-scales to the data range. Server-side cron polls all six sensors every minute. History available immediately to new visitors. Close with ✕ or click outside.

---

## [2.0.0] - 2026-05-04

### Added

- **Radiation history chart** — clicking the RADIATION block opens a NORAD-aesthetic modal with a canvas line chart of the last 60 readings (1 per minute = 1 hour). Server-side cron polls HA every minute and maintains a rolling JSON history file, so new visitors immediately see a full hour of data rather than an empty chart. Live display now updates every 5 seconds. Grid lines at 50 CPM (amber) and 100 CPM (red). Close with ✕ or click outside. The hens remain unconcerned about background radiation.

---

## [1.9.6] - 2026-05-04

### Changed

- **NET RX / NET TX now display in Mbps** — more standard for network rates. Values multiplied by 8 client-side; netstats endpoint unchanged. Typical display: ~11 Mbps TX for two 6000k streams.

---

## [1.9.5] - 2026-05-04

### Removed

- **MOON PHASE** — removed from NORAD row 3. The hens don't care. Neither do we anymore.

---

## [1.9.4] - 2026-05-04

### Changed

- **Door close offset reduced to 10 minutes** — NORAD countdown now targets dusk + 10 min instead of dusk + 15 min, matching updated HA automation. Remember to update the HA automation to match.

---

## [1.9.3] - 2026-05-04

### Fixed

- **NEXT DOOR ACTION showing ~24h after dusk** — when the door is open and dusk+15min has passed, `sun.sun.next_setting` rolls over to tomorrow's sunset (~23h45m away), causing the countdown to show ~24h15m instead of CLOSING SOON. Fixed with a 12-hour sanity cap: any `closeTime` diff >12h is treated as rollover and shows CLOSING SOON instead.

---

## [1.9.2] - 2026-05-04

### Added

- **Dismiss buttons** — all three floating UI elements now have a ✕ close button. NORAD panel gets a subtle green ✕ top-right; PiP gets a semi-transparent ✕ top-right; quote panel gets an inline ✕ after the quote text. All hide on click until page reload. Reload to restore. The hens cannot be dismissed.

---

## [1.9.1] - 2026-05-04

### Changed

- **Mobile: NET RX, NET TX, MOON PHASE hidden on small screens** — these three row-3 blocks are now `display:none` at ≤768px via `.desktop-only` CSS class. Row 3 on mobile shows only SOLAR S, SOLAR N, and BATTERY. Desktop unchanged.

---

## [1.9.0] - 2026-05-04

### Added

- **NET RX / NET TX stat blocks** — NORAD row 3 now shows live network throughput for the streaming host. RX = camera ingest from UniFi, TX = RTMP upload to YouTube. Updates every 5 seconds. Useful for confirming both streams are pushing and the internet hasn't given up.
- **MOON PHASE** — NORAD row 3 now shows current moon phase as emoji + name (e.g. 🌖 WANING GIBBOUS). Pure JS, no API, no network call, never goes OFFLINE. Calculated from orbital mechanics. The hens are unaware.

---

## [1.8.2] - 2026-05-03

### Changed

- **SEO overhaul** — title tags and meta descriptions rewritten across all pages to lead with "Chicken Cam" keyword. Meta descriptions now lead with the value prop (free, ad-free, instant view, no clicking about) rather than hen names. Added Omlet Eglu Cube as explicit keyword target. OG type changed from `website` to `video.other` on index pages.
- **Structured data** — LD+JSON upgraded on index pages. `VideoObject` now includes `uploadDate`, `embedUrl`, `contentUrl`, `keywords`, and `broadcastDisplayName`. `WebSite` schema with `SearchAction` added to main page.
- **Hidden `h1`** — crawler-visible heading added to both index pages (visually hidden, accessible). Previously no `h1` existed on live cam pages.
- **Sitemap** — `/CHANGELOG.md` added; `lastmod` dates updated. Bogus `/run` entry removed (index-run.html has no separate public URL; both index files serve as `https://clucks.net/`).
- **NORAD row 3** — `SOLAR OUTPUT` split into `SOLAR S` (south, 3.6 kWp) and `SOLAR N` (north, 3.2 kWp). IDs `solar-south`, `solar-north`, `battery`. All remain `OFFLINE` pending Teslemetry integration after May 2026 install.
- **about.html** — solar section updated to reference both arrays (3.6 kWp south + 3.2 kWp north = 6.8 kWp total) and heat pump. Solar grid corrected to match. Title reordered for keyword prominence.
- **"rescue hens" corrected** — Run cam OG/LD+JSON previously described the hens as rescue hens. They are not. Fixed.

---

## [1.8.1] - 2026-05-02

### Changed

- **NEXT DOOR ACTION close countdown** — both index pages now count down to **dusk + 15 minutes** instead of exact sunset. Mirrors HA automation change: door close trigger moved from light-level threshold to sunset + 15 min offset. Countdown display, colour thresholds, and "CLOSING SOON" state unchanged.

---

## [1.8.0] - 2026-04-30

### Added

- **Changelog links** — `📋 CHANGELOG` link added to: bottom of the "Meet the flock" popup, bottom of the "About ClucksNet" popup (footer bar below iframe), bottom of `about.html`, and bottom-left link stack on both index pages. Opens `/CHANGELOG.md` in a NORAD-style iframe modal on index pages; direct link on about.html.
- **Changelog overlay modal** — same iframe-in-overlay pattern as the About overlay. Close button top-right, click-outside-to-dismiss. Loads lazily. Available on both index pages.
- **Bottom-left link stack repositioned** — stack order bottom-to-top: ● LIVE · CHANGELOG · WATCH IN HD · ABOUT CLUCKS.NET.
- **Sprinkler control** — viewers can water the hen run from the website. 10 seconds of water, 60-second cooldown, one session at a time, daytime only, temp must be >10°C, no precipitation. Enforced both client-side and server-side because trust nobody, not even yourself.
- **Pre-water confirmation modal** — explains all the rules before firing. Includes prominent note about YouTube's ~30 second stream delay so viewers don't think it's broken. The hens have not been consulted.
- **Weather-gated watering** — checks Open-Meteo WMO weather codes before allowing. Blocks on rain, drizzle, showers, thunderstorm. Snow excluded because if it's snowing it's definitely below 10°C anyway. Basic thermodynamics.
- **SPRINKLER stat block** in NORAD row 3 — shows `READY · TAP TO WATER`, `WATERING... Xs`, `COOLING DOWN Xs`, `TOO COLD`, `RAINING`, or `OFFLINE (NIGHT)`.
- **Total visitor counter** — increments on new/returning visitors (5-min window). Displayed in NORAD panel as `X Online / ### Total`.
- **YouTube channel description** — rewritten for discoverability. Hook-first, humour throughout, correct tech references. Hen names relegated to after the truncation point where they belong.
- **External YouTube description files** — descriptions moved out of `chickencam_daily.py` into separate text files. Edit copy without touching Python. Revolutionary concept.
- **Old broadcast cleanup** — `delete_old_broadcasts()` added to `chickencam_daily.py`. Runs after each nightly rotation, deletes all `complete` broadcasts except today's. YouTube Studio is now peaceful.
- **Random quote panel** — draggable NORAD-style box, bottom of screen, loads a random quote on page load. 700+ quotes covering: anti-news, anti-corporate, Agile/Waterfall/Kanban satire, AI layoff humour, battery hens vs open plan offices, TPS reports, Terraform/IaC jokes, UAP/alien conspiracy (the hens know about the P47s and P52s and aren't talking), and general hen philosophy.
- **Quote word-wrap on mobile** — previously quotes extended into the void on iOS. The void has been addressed.
- **Close buttons** on NORAD panel and quote box — small ✕, reload to restore. The hens remain on screen regardless.

### Changed

- **Viewer display** — was `X ttl (Y YT · Z WEB)`, now `X Online / ### Total`. Label changed from `VIEWERS · 5 MIN` to `VIEWERS / TOTAL`.
- **`chickencam_daily.py`** — description strings removed, replaced with file loader. Script ~80 lines shorter. Tech references corrected throughout.
- **YouTube stream descriptions** — rewritten. Opens with the hook, not the hen names. mediamtx removed and replaced with accurate FFmpeg pipeline description.
- **Wind display** — changed from km/h to mph. This is Yorkshire. The Met Office uses mph. So do we now.

### Fixed

- **Double cooldown countdown** — polling was starting a second countdown on top of the existing one. Fixed with guard flag. Countdowns now singular.
- **Stale water lock** — empty lock file from previous session caused NORAD to show `WATERING...` on page load despite nobody watering anything.

---

## [1.7.0] - 2026-04-28

### Added

- **About page** (`/about`) — full NORAD-aesthetic page covering the flock, the Eglu, the technology stack, radiation monitor, solar plans, and YouTube links. Linked from NORAD panel (ⓘ button) and bottom of page.
- **About overlay** — clicking ⓘ in NORAD panel or `ABOUT CLUCKS.NET` link opens `/about` in an iframe modal. Closes on ✕ or clicking outside. No page navigation.
- **Draggable NORAD panel and PiP** — mouse and touch drag for both the status panel and the PiP window. PiP click-to-swap preserved via drag detection flag.
- **YouTube dynamic links in about.html** — fetches live stream data on load, patches YouTube links. Links are always current after 3am rotation.
- **`/test` directory** — staging area for HTML files before promotion.

### Changed

- **Info button** — icon changed from ⓘ SVG to 🐔 emoji. The about button (new) gets the ⓘ.
- **`ABOUT CLUCKS.NET` link** — moved to bottom-left stack. Stack order bottom-to-top: ● LIVE · ⎋ WATCH IN HD · ABOUT CLUCKS.NET.
- **about.html content** — corrected: hens are not rescue hens (bought point-of-lay, April 2026, wife's birthday), not a smallholding (detached house with garden), mediamtx removed, YouTube CDN pipeline described accurately, Eglu door now HA-controlled, Tessa is ~~chief supervisor~~ absolute dictator, Octopus Intelligent Go with Export mentioned, solar section updated.
- **Mobile responsiveness** — panel reflows to full width, stat blocks wrap, buttons span full width, PiP scales to 40vw.

---

## [1.6.0] - 2026-04-28

### Added

- **Live viewer count** — queries YouTube Data API v3 for concurrent viewers on both streams. Tracks active web visitors via hash (5-min window). Combined display in NORAD panel.
- **NORAD panel layout finalised** — three rows:
  - Row 1: EXT TEMP · INT TEMP · EXT HUMIDITY · INT HUMIDITY · RADIATION · PERIMETER · buttons
  - Row 2: AMBIENT LIGHT · NEXT DOOR ACTION · LAST OPENED · CONDITIONS · WIND · FLOCK STATUS · VIEWERS/TOTAL
  - Row 3: SOLAR OUTPUT · BATTERY · SPRINKLER
- **INT HUMIDITY** — interior humidity added to panel. Colour bands: green 40–70% (ideal), amber 30–40% or 70–85% (watch it), red <30% or >85% (hen health risk). Based on poultry welfare research.
- **Buttons** — reload (↺), fullscreen (⤢), 🐔 (hen info), ⓘ (about). All 38px wide, consistent.

### Fixed

- **Cloudflare caching PHP responses** — PHP endpoints now bypass cache. Viewer count was returning stale `0` despite counter incrementing correctly.
- **Apache vhost** — fully rewritten cleanly after multiple conflicting rules were resolved.

---

## [1.5.0] - 2026-04-27

### Added

- **`live.json` support** — `chickencam_daily.py` writes current video IDs to web server after each rotation. Index pages fetch on load, populate YouTube player IDs dynamically. Fallback IDs hardcoded for resilience.
- **`⎋ LOCATING CAMERA FEEDS`** — hd-link shows this on load, updates to `⎋ WATCH IN HD` with correct URL after `live.json` fetched.
- **HTML update removed from `chickencam_daily.py`** — `live.json` is the single source of truth.
- **PiP camera swap** — click PiP to swap main/PiP cameras. Drag detection prevents swap firing after drag gesture.
- **Stream monitor** — pushes to live edge every 120 seconds. Threshold: >40s behind live edge.

### Changed

- **Resolution** — FFmpeg scale changed from 1280×720 to 1920×1080. HLS containers removed. YouTube-only pipeline now.
- **`docker-compose.yml`** — now contains only the two camera containers. Clean.

---

## [1.4.0] - 2026-04-26

### Added

- **NORAD-style status panel** — fixed top-right, dark background, green border, Share Tech Mono font. Three rows of stat blocks.
- **HA proxy** — PHP script proxying Home Assistant API to frontend. Referrer-gated. Entity whitelist. Exposes: interior temp/humidity, door state, ambient light, last open/close time, radiation CPM, sun state/elevation.
- **Radiation monitor** — GQ GMC-800 Geiger counter connected to HA via USB. Displayed in NORAD panel. Colour: green ≤50 CPM, amber 51–100, red >100. Normal background in North Yorkshire: 10–50 CPM. If it goes red, something interesting has happened.
- **Weather via Open-Meteo** — exterior temp, conditions (WMO codes mapped to readable strings), wind speed+direction (compass), exterior humidity. No API key required.
- **Flock status** — derived from sun elevation: ROOSTING (<-6°), DUSK/DAWN (transitional), SHELTERING (low light, door open), FORAGING (daytime, light >30%).
- **Door countdown** — shows time until door opens (if EGLU SECURED) or time until door closes (if EGLU BREACHED). Sub-1h shows amber.
- **`EGLU BREACHED`** — door open state. Blinks amber. Margo approves of the drama.

### Changed

- **Apache vhost** — all key files whitelisted via RewriteRules. Catch-all 301 redirects everything else to `https://clucks.net/`.

---

## [1.3.0] - 2026-04-25

### Added

- **Dual-camera layout** — main player fullscreen, PiP bottom-right (28vw, min 200px, max 420px, 16:9). PiP has LIVE badge. Click to swap.
- **YouTube IFrame API** — both players initialised. Quality forced to `hd1080` (main) and `hd720` (PiP).
- **`index-eglu.html`** — Eglu Cube cam primary, Run cam PiP.
- **`index-run.html`** — Run cam primary, Eglu Cube cam PiP.

---

## [1.2.0] - 2026-04-24

### Added

- **YouTube streaming pipeline** — UniFi G5 Turret Ultra cameras → RTSPS → Docker (FFmpeg) → RTMP → YouTube. Two containers.
- **FFmpeg command** — libx264, `veryfast` preset, 6000k bitrate, AAC stereo silent audio track (YouTube ingest requirement), 30fps, 1920×1080.
- **`chickencam_daily.py`** — nightly rotation at 3am via cron. Creates fresh YouTube broadcast events, binds to persistent RTMP stream keys, restarts Docker containers, writes `live.json`. OAuth2 via `google-auth-oauthlib`. Stream keys are persistent; broadcast IDs rotate daily.

### Changed

- **HLS streaming abandoned** — self-hosted mediamtx/nginx-rtmp caused stuttering with multiple concurrent viewers. YouTube CDN solves the egress problem entirely. Latency trade-off (~30s vs ~5s) accepted.

---

## [1.1.0] - 2026-04-24

### Added

- **`clucks.net` domain** — Cloudflare DNS, proxied. Apache 2.4 web server. Accessible from Mac via NFS mount.
- **Self-hosted HLS streaming** — mediamtx → nginx-rtmp → HLS. Abandoned in same release due to multi-viewer stuttering. Documented here for posterity and to explain why there are ghost containers in early docker-compose versions.

---

## [1.0.0] - 2026-04-10

### Added

- Four hens (Crumpet, Triborg, Mishelle, Margo Leadbetter). Point-of-lay. Bought as a birthday surprise for wife. They have settled in with considerable confidence and very little gratitude.
- Omlet Eglu Cube with automatic door. Door controlled by Home Assistant based on sunrise/sunset calculations.
- Two UniFi G5 Turret Ultra cameras.
- Tessa (Bichon Frisé). Self-appointed dictator of the run. Not a hen.

---

*"The hens do not know this changelog exists. The hens are fine."*
