From 4fd0744c53dfe5e2be069a36183558153f4bdaa3 Mon Sep 17 00:00:00 2001 From: Vincent Filby Date: Mon, 17 Aug 2026 20:02:54 -0700 Subject: [PATCH 1/2] Add brand icon and a single-page day simulator MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - brands/shade_engine: icon.svg source plus 256/512 PNGs laid out for a home-assistant/brands custom_integrations submission (the integrations page loads images from brands.home-assistant.io, so the icon shows up once that PR merges); README logo. - docs/simulator.html: self-contained simulator — paste zone YAML, pick date/location/sky, and it plays a full day through JS ports of calculator.py (eye-zone + reflected-glare, verified identical to the Python on a 3,536-case grid) and core.py (deadband, defer-not-drop rate limiting, settle window, manual-move holds), driven by a NOAA solar model, a synthetic outdoor-lux curve, and an editable lux/elevation policy ladder. Per-zone timelines with mode strips, a scrubbable room cross-section showing eye zone and reflector bounce, and a manual-move injector to demo the hold lifecycle. Co-Authored-By: Claude Fable 5 --- README.md | 15 + brands/shade_engine/README.md | 17 + brands/shade_engine/icon.png | Bin 0 -> 16717 bytes brands/shade_engine/icon.svg | 50 ++ brands/shade_engine/icon@2x.png | Bin 0 -> 19705 bytes docs/simulator.html | 1333 +++++++++++++++++++++++++++++++ 6 files changed, 1415 insertions(+) create mode 100644 brands/shade_engine/README.md create mode 100644 brands/shade_engine/icon.png create mode 100644 brands/shade_engine/icon.svg create mode 100644 brands/shade_engine/icon@2x.png create mode 100644 docs/simulator.html diff --git a/README.md b/README.md index 3eded5c..abfc917 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +Shade Engine — a half-drawn shade over a sunset window + # Shade Engine [![Validate](https://github.com/vfilby/shade-engine/actions/workflows/validate.yml/badge.svg)](https://github.com/vfilby/shade-engine/actions/workflows/validate.yml) @@ -223,6 +225,19 @@ disabled.) data: { option: night } ``` +## Simulator + +[`docs/simulator.html`](docs/simulator.html) is a self-contained, single-page +simulator (no build, no network — open it in any browser). Paste your zone +YAML, pick a date, location, and sky, and it plays the whole stack through a +day: the calculator's glare position (a direct port of `calculator.py`, +eye-zone and reflected-glare constraints included), a lux/elevation policy +ladder with editable thresholds, and the actuator's real semantics — deadband, +rate limiting that defers, and manual-move holds (there's a control to inject +a manual move and watch the hold start and expire). A room cross-section +diagram shows the sun ray, eye zone, and reflector bounce at any scrubbed +minute — useful for sanity-checking a zone's geometry before deploying it. + ## Verifying your geometry Run in shadow mode first: configure zones, restart, and graph diff --git a/brands/shade_engine/README.md b/brands/shade_engine/README.md new file mode 100644 index 0000000..7f760df --- /dev/null +++ b/brands/shade_engine/README.md @@ -0,0 +1,17 @@ +# Brand assets + +Source (`icon.svg`) and rasterized icons for the integration. `icon.png` +(256×256) and `icon@2x.png` (512×512) match the layout Home Assistant's +[brands repository](https://github.com/home-assistant/brands) expects under +`custom_integrations/shade_engine/` — the integrations page loads its images +from `brands.home-assistant.io`, so the icon only appears in the UI once a +copy of these files is merged there. + +Regenerate the PNGs from the SVG with headless Chrome: + +```sh +chrome --headless --default-background-color=00000000 \ + --window-size=256,256 --screenshot=icon.png icon.svg +chrome --headless --default-background-color=00000000 \ + --window-size=512,512 --screenshot=icon@2x.png icon.svg +``` diff --git a/brands/shade_engine/icon.png b/brands/shade_engine/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..58163ef7fe7ad649a774e88108be8004a7244a1c GIT binary patch literal 16717 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/brands/shade_engine/icon@2x.png b/brands/shade_engine/icon@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..2ac0d87b89d5ac94bc6c57d33e7e7fc69128f649 GIT binary patch literal 19705 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 + + + + +Shade Engine Simulator + + + + +
+
+

Shade Engine Simulator

+ geometry · policy · actuator, one day at a time +
+

+ Paste a shade_engine zone configuration, set a day and a sky, and watch what the + engine would do: the calculator's glare position, the policy ladder switching modes, and the + actuator's actual cover commands — deadband, rate limiting, and manual-move holds included. + Times are shown in this browser's timezone; illuminance is a synthetic outdoor-sensor model. +

+ +
+ + +
+
+

Sky

+
+
+
Sun elevation (°)
+
+
+
+
Outdoor illuminance (lx, log)
+
+
+
+
+ +
+

Time

+
+ 12:00 + +
+
+ +
+ +
+
+
+

Room cross-section

+ +
+
+
+
+
+
+
+
+
+ + + + From 7a301a17d6ce76dcbe7ba2c683309124684d90ab Mon Sep 17 00:00:00 2001 From: Vincent Filby Date: Mon, 17 Aug 2026 20:05:09 -0700 Subject: [PATCH 2/2] Ship brand icon inside the integration (HA 2026.3 local brands) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit home-assistant/brands stopped accepting custom-integration PRs in Feb 2026; since HA 2026.3 an integration's own brand/ directory is served locally and takes priority over the brands CDN. Move the PNGs to custom_components/shade_engine/brand/ and keep the SVG source in assets/. The icon appears on the integrations page as soon as this version is deployed — no external submission involved. Co-Authored-By: Claude Fable 5 --- README.md | 2 +- assets/README.md | 18 ++++++++++++++++++ {brands/shade_engine => assets}/icon.svg | 0 brands/shade_engine/README.md | 17 ----------------- .../shade_engine/brand}/icon.png | Bin .../shade_engine/brand}/icon@2x.png | Bin 6 files changed, 19 insertions(+), 18 deletions(-) create mode 100644 assets/README.md rename {brands/shade_engine => assets}/icon.svg (100%) delete mode 100644 brands/shade_engine/README.md rename {brands/shade_engine => custom_components/shade_engine/brand}/icon.png (100%) rename {brands/shade_engine => custom_components/shade_engine/brand}/icon@2x.png (100%) diff --git a/README.md b/README.md index abfc917..72b3db3 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -Shade Engine — a half-drawn shade over a sunset window +Shade Engine — a half-drawn shade over a sunset window # Shade Engine diff --git a/assets/README.md b/assets/README.md new file mode 100644 index 0000000..24fb12f --- /dev/null +++ b/assets/README.md @@ -0,0 +1,18 @@ +# Brand assets + +`icon.svg` is the source for the integration's brand images. The rasterized +copies live at `custom_components/shade_engine/brand/icon.png` (256×256) and +`icon@2x.png` (512×512): since Home Assistant 2026.3, brand images shipped in +a `brand/` directory inside the integration are served locally and take +priority over the brands CDN, so no submission to +[home-assistant/brands](https://github.com/home-assistant/brands) is needed +(that repo stopped accepting custom-integration PRs in early 2026). + +Regenerate the PNGs from the SVG with headless Chrome: + +```sh +chrome --headless --default-background-color=00000000 \ + --window-size=256,256 --screenshot=icon.png assets/icon.svg +chrome --headless --default-background-color=00000000 \ + --window-size=512,512 --screenshot=icon@2x.png assets/icon.svg +``` diff --git a/brands/shade_engine/icon.svg b/assets/icon.svg similarity index 100% rename from brands/shade_engine/icon.svg rename to assets/icon.svg diff --git a/brands/shade_engine/README.md b/brands/shade_engine/README.md deleted file mode 100644 index 7f760df..0000000 --- a/brands/shade_engine/README.md +++ /dev/null @@ -1,17 +0,0 @@ -# Brand assets - -Source (`icon.svg`) and rasterized icons for the integration. `icon.png` -(256×256) and `icon@2x.png` (512×512) match the layout Home Assistant's -[brands repository](https://github.com/home-assistant/brands) expects under -`custom_integrations/shade_engine/` — the integrations page loads its images -from `brands.home-assistant.io`, so the icon only appears in the UI once a -copy of these files is merged there. - -Regenerate the PNGs from the SVG with headless Chrome: - -```sh -chrome --headless --default-background-color=00000000 \ - --window-size=256,256 --screenshot=icon.png icon.svg -chrome --headless --default-background-color=00000000 \ - --window-size=512,512 --screenshot=icon@2x.png icon.svg -``` diff --git a/brands/shade_engine/icon.png b/custom_components/shade_engine/brand/icon.png similarity index 100% rename from brands/shade_engine/icon.png rename to custom_components/shade_engine/brand/icon.png diff --git a/brands/shade_engine/icon@2x.png b/custom_components/shade_engine/brand/icon@2x.png similarity index 100% rename from brands/shade_engine/icon@2x.png rename to custom_components/shade_engine/brand/icon@2x.png