mirror of
https://github.com/Matysh/houseplan-card
synced 2026-10-08 07:29:13 +00:00
feat(view): the moon in its phase over the "Follow the Sun" background (#661)
At dawn, dusk and night the environment shows the moon in the top-left corner of the scene, behind the plan: computed in the card from the home coordinates and the browser clock (short Meeus series + topocentric parallax, within 1.4° / 1.8 pp of JPL Horizons), one designer image under a continuous phase mask with the lit side always on the left (owner 2026-09-29), a feathered terminator, 3°/3 % thresholds and the 2 s fade of the window rays. Everything but a small gate lives in the lazy moon-runtime chunk, with its own 30 s ticker. General settings: "Sun" becomes "Sun and Moon" with one switch, on for new installations (DEFAULT_CONFIG), off for existing ones. The initial View graph sat 728 B under its budget: the gate is paid for by moving fifteen dialog-only strings of General settings into the lazy settings dictionary (#459) and by one build fingerprint literal instead of three, so the graph ends 4 B above dev. Golden: two new moon scenes, and the two General settings help frames show «Sun and Moon»; the WSL artifact test fixture now models scenes whose first capture awaits acceptance. Issue: #661 User-Visible: yes Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018qZfe7YS4rqEMKoVeS3GKd
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
e515bdaab8
commit
c8c470ed57
@@ -2,6 +2,13 @@
|
||||
|
||||
## Unreleased
|
||||
|
||||
- On the "Follow the Sun" background, the moon in its current phase now appears
|
||||
in the top-left corner of the scene at dawn, dusk and night, behind the plan.
|
||||
It follows the real moon over your home (Home Assistant home location), fades
|
||||
in and out as it crosses 3° above the horizon, and is hidden by day and around
|
||||
new moon. General settings: the "Sun" section is now "Sun and Moon", with a
|
||||
new switch that is on for new installations
|
||||
([#661](https://github.com/Matysh/houseplan-card/issues/661)).
|
||||
- In 2.5D View, device icons no longer shift when a device changes state (for
|
||||
example when a light turns on and shows its brightness); a Home Assistant
|
||||
update in a dense plan also no longer re-lays out every icon
|
||||
|
||||
@@ -8,6 +8,13 @@
|
||||
|
||||
## Не выпущено
|
||||
|
||||
- На фоне «Следует за Солнцем» в сумерках и ночью в левом верхнем углу сцены,
|
||||
за планом, теперь видна луна в текущей фазе. Она следует настоящей луне над
|
||||
вашим домом (по координатам дома из Home Assistant), плавно появляется и
|
||||
исчезает при пересечении 3° над горизонтом, днём и в новолуние её нет. В общих
|
||||
настройках раздел «Солнце» стал «Солнце и Луна», в нём новый переключатель,
|
||||
включённый у новых установок
|
||||
([#661](https://github.com/Matysh/houseplan-card/issues/661)).
|
||||
- В объёмном 2.5D-виде значки устройств больше не сдвигаются, когда меняется
|
||||
состояние устройства (например, лампа включилась и показала яркость);
|
||||
обновление Home Assistant в плотном плане больше не переразмещает все значки
|
||||
|
||||
@@ -134,6 +134,24 @@ An older frontend ignores the field and shows Flat. An older backend preserves
|
||||
it through the unknown-settings policy. Full backup/import carries `settings`
|
||||
whole, so the value survives.
|
||||
|
||||
## Moon (#661)
|
||||
|
||||
`settings.moon` is an optional global boolean: the moon on the "Follow the Sun"
|
||||
background for every space, user, device and kiosk. Only exact `true` switches
|
||||
it on; absence, `false` or anything else read as off. Saving `false` removes
|
||||
the key. There is no per-space moon: a space shows it only when its effective
|
||||
`bg_mode` is `daynight`. The backend accepts only a boolean
|
||||
(`vol.Optional("moon"): bool`), and the privacy-safe support projection copies
|
||||
only a validated boolean. New installations get `moon: true` in
|
||||
`DEFAULT_CONFIG`; an existing config has no key and keeps its look after the
|
||||
update (docs/SUN.md). "Reset to defaults" in General settings sets `true`. The
|
||||
store and model versions do not change; there is no migration.
|
||||
|
||||
An older frontend ignores the field and shows no moon. An older backend
|
||||
preserves it through the unknown-settings policy. Full backup/import carries
|
||||
`settings` whole, so the value survives; a one-space export does not carry
|
||||
global settings.
|
||||
|
||||
## Stairs (#663)
|
||||
|
||||
`spaces[].stairs[]` is an optional bounded (250 records per space)
|
||||
|
||||
+92
@@ -376,6 +376,92 @@ The wash lives where the Flat `.sunlayer` lives (floor group, above room fills
|
||||
and Glow) as `.sunlayer.iso-sunwash`, one `.iso-sunbeam[data-opening]` per lit
|
||||
window. Witness: `demo/smoke_iso_sun.mjs`.
|
||||
|
||||
## Moon — `settings.moon` (#661)
|
||||
|
||||
At dawn, dusk and night on the "Follow the Sun" background, the moon in its
|
||||
current phase stands in the top-left corner of the scene: a thin crescent, a
|
||||
half, a full disc. General settings › Sun and Moon › «Moon over the plan at
|
||||
dusk and night» switches it for the whole installation.
|
||||
|
||||
**When it is shown** — all at once, otherwise there is no moon:
|
||||
|
||||
- `settings.moon === true` (global; absent, `false` or anything else is off);
|
||||
- the effective `bg_mode` of the space is `daynight` — the moon lives in the
|
||||
four-phase environment, so a space with its own `static` has no moon, and
|
||||
there is no per-space moon switch;
|
||||
- the environment phase is `dawn`, `dusk` or `night` (the same
|
||||
`resolveDayCycle`, browser-clock fallback included);
|
||||
- the topocentric altitude is ≥ 3° (`MOON_ELEVATION_MIN = RAY_ELEVATION_MIN`);
|
||||
- the illuminated fraction is ≥ 3 % (`MOON_MIN_ILLUMINATION`, about ±1.5 days
|
||||
around new moon);
|
||||
- a View surface: View, kiosk or `houseplan-space-card` (editors have no
|
||||
environment);
|
||||
- `hass.config.latitude/longitude` are finite numbers.
|
||||
|
||||
**Where the numbers come from.** Home Assistant publishes no moon altitude
|
||||
(`sun.sun` is the sun; the optional Moon integration gives eight phase names
|
||||
without altitude, illumination or hemisphere). The card computes both from the
|
||||
home coordinates and the browser clock (`src/moon.ts`): the short Meeus series
|
||||
as in SunCalc, the topocentric parallax `h − π·cos h` with
|
||||
`π = asin(6378.14 / distance)`, no refraction; the illuminated fraction
|
||||
`k = (1 + cos i) / 2` from the Sun–Moon elongation. Against JPL Horizons
|
||||
(airless) on twelve points in Moscow and Sydney, October 2026: altitude within
|
||||
1.39°, illumination within 1.77 percentage points (`test/moon.test.mjs`, the
|
||||
table and the query parameters are in the test).
|
||||
|
||||
**Phase.** One designer image of the full moon (`assets/moon/houseplan-1.0.0`,
|
||||
art by JB) under an SVG mask. The lit side is **always the left one**, in both
|
||||
hemispheres; waning runs the waxing states backwards (owner 2026-09-29). The
|
||||
lit region is the left half of the box plus or minus the terminator
|
||||
half-ellipse `R × R·|2k − 1|` (bulging into the dark side when `k > 0.5`); the
|
||||
half's outer arc runs along the box, never along the limb, so the disc keeps
|
||||
the art's own anti-aliased edge. The mask is a `<mask>`, not a `clipPath`
|
||||
(Chrome with GPU rasterisation draws clip edges jagged). The terminator is
|
||||
feathered by a Gaussian blur inside the mask (5 units of 512, about 2 px at
|
||||
200 px), except for a full disc (`k ≥ 0.995`). The phase is continuous in `k`,
|
||||
quantised to 0.01 only so the element changes when the fingerprint does. The
|
||||
dark side is the same art at 8 % opacity.
|
||||
|
||||
**Place, size, layer.** Fixed top-left corner, box `min(200px, 25cqmin)` of the
|
||||
scene (the environment is the size container), inset 5 % of the box; it does
|
||||
not move with pan or zoom and never takes the pointer. It is the last child of
|
||||
`.hp-day-cycle-env`: above the phase gradients and the sun glow, below the plan
|
||||
paper, rooms, devices, labels and UI. A plan that fills the scene covers the
|
||||
moon partly or entirely — that is the environment's norm, like the sun glow
|
||||
(owner decision 7).
|
||||
|
||||
**Movement.** Opacity only, 2 s on the background curve (`RAY_FADE_MS`),
|
||||
none under `prefers-reduced-motion`: rising through 3°, setting through it,
|
||||
the new-moon threshold and the day phase all fade; the first appearance after
|
||||
a page or chunk load does not. The element is recomputed on every render of the
|
||||
environment and by its own 30 s ticker (the moon rises at most 0.25°/min);
|
||||
the ticker keeps the last rendered inputs, and an equal fingerprint
|
||||
(`visible | k`) costs no render. A card that renders without a moon, leaves the
|
||||
page or is hidden drops or pauses its ticker.
|
||||
|
||||
**Weight.** Astronomy, art (≈ 9 KB gzip) and template are one lazy chunk,
|
||||
`moon-runtime-*`, loaded when the moon is switched on, the environment exists
|
||||
and it is not daytime; until it arrives there is no moon, a failed load is
|
||||
retried at most every 30 s through the exact-build loader of the isometric
|
||||
runtime. The initial View graph carries only the gate (`src/moon-gate.ts`).
|
||||
One element, no CSS filter and no `will-change`: the composited layer count
|
||||
does not change (`demo/smoke_daycycle_layer_budget.mjs`).
|
||||
|
||||
**Limits (documented, not bugs).**
|
||||
|
||||
- Position accuracy ≈ 1°: the moment of crossing 3° may differ from ephemerides
|
||||
by a few minutes.
|
||||
- The terminator is not tilted by the parallactic angle, and the lit side does
|
||||
not follow the hemisphere (owner decision): the crescent is "vertical".
|
||||
- Earthshine is not modelled; the dark side is an 8 % silhouette for legibility.
|
||||
- The moon does not follow its azimuth — fixed corner of the scene.
|
||||
- A dense layout (plan filling the screen) shows only the part of the disc in
|
||||
the margins.
|
||||
- Coordinates come from Home Assistant: an installation that kept the default
|
||||
home location gets someone else's moon, as it gets someone else's `sun.sun`.
|
||||
- A wrong tablet clock gives a wrong phase and moment — the same class as the
|
||||
background's clock fallback.
|
||||
|
||||
## Weather independence and legacy `weather_entity`
|
||||
|
||||
Weather never changes the window rays. Once the feature, compass,
|
||||
@@ -417,6 +503,12 @@ saving General settings removes it.
|
||||
inheritance); unit-tested in `test/sun.test.mjs`.
|
||||
- `src/day-cycle-render.ts` — the shared constant environment layers and
|
||||
plan-outline variables for full and static cards.
|
||||
- `src/moon.ts`, `src/moon-runtime.ts`, `src/moon-gate.ts`,
|
||||
`src/moon-art.generated.ts` — the moon (#661): pure astronomy and mask, the
|
||||
lazy chunk with the element and its ticker, the initial-graph gate, the
|
||||
generated art (`node scripts/generate-moon-assets.mjs` from
|
||||
`assets/moon/houseplan-1.0.0`); unit-tested in `test/moon.test.mjs`,
|
||||
end-to-end in `demo/smoke_moon.mjs`.
|
||||
- `src/houseplan-card.ts` — the memoised wedge layer, four-phase background
|
||||
lifecycle, and both settings dialogs (compass dial included).
|
||||
- `src/space-render.ts` / `src/space-card.ts` — static-card environment and
|
||||
|
||||
+29
-2
@@ -37,7 +37,7 @@ relay, and exact plan geometry is attached only after you opt in and preview it.
|
||||
12. [Device visual states](#12-device-visual-states)
|
||||
13. [Room fills and light](#13-room-fills-and-light)
|
||||
14. [Background editor](#14-background-editor)
|
||||
15. [Sun background and window rays](#15-sun-background-and-window-rays)
|
||||
15. [Sun and Moon: background, window rays and the moon](#15-sun-and-moon-background-window-rays-and-the-moon)
|
||||
16. [Robot vacuums](#16-robot-vacuums)
|
||||
17. [Kiosk](#17-kiosk)
|
||||
18. [Static space card](#18-static-space-card)
|
||||
@@ -1603,7 +1603,7 @@ keeps the same repairable placeholder instead of rejecting the whole plan.
|
||||
|
||||

|
||||
|
||||
## 15. Sun background and window rays
|
||||
## 15. Sun and Moon: background, window rays and the moon
|
||||
|
||||
These are independent features. **Follow the sun** needs no compass: it uses
|
||||
valid `sun.sun` data and otherwise falls back to the browser clock. North and
|
||||
@@ -1651,6 +1651,33 @@ static card share the phase; editors keep their normal background. New installs
|
||||
and spaces default to Follow the sun; upgrades/imports preserve their existing
|
||||
choice. Shadows from trees, awnings or other building wings are not modelled.
|
||||
|
||||
### Moon
|
||||
|
||||
On the **Follow the sun** background, at dawn, dusk and night, the moon in its
|
||||
current phase stands in the top-left corner of the scene — a thin crescent, a
|
||||
half, a full disc. One switch in General settings turns it on: **Sun and Moon**
|
||||
→ **Moon over the plan at dusk and night**. It is on for new installations and
|
||||
off for upgraded ones until switched on: an update never changes how a plan
|
||||
looks.
|
||||
|
||||
| Condition | Result |
|
||||
|---|---|
|
||||
| Daytime, moon below 3° above the horizon, or new moon (under 3 % lit) | No moon |
|
||||
| The moon rises above 3° or sets below it | Fades in/out over 2 seconds; immediately with reduced motion |
|
||||
| Phase | Changes continuously, every day; the lit side is always on the left, waning runs the same states backwards |
|
||||
| Space with its own static background | No moon — it belongs to the Follow the sun background |
|
||||
| The plan covers the corner of the scene | The moon is behind the plan: the part of the disc in the margins shows; the plan, devices and labels are always on top |
|
||||
| Editors | No moon |
|
||||
| Full card, kiosk, static space card | The same |
|
||||
|
||||
Position and phase are computed in the card from the Home Assistant home
|
||||
location (Settings → System → General) and the device clock, with no
|
||||
integration or external data. A Home Assistant that kept the default location
|
||||
shows someone else's moon — as with `sun.sun`; a wrong tablet clock gives a
|
||||
wrong phase and moment. Position accuracy is about 1°, so crossing 3° may
|
||||
differ from ephemerides by a few minutes. The terminator tilt and earthshine
|
||||
are not modelled; the dark side is a faint silhouette so a crescent reads.
|
||||
|
||||
## 16. Robot vacuums
|
||||
|
||||
Live position needs finite `vacuum_position` or `robot_position` coordinates.
|
||||
|
||||
+30
-3
@@ -38,7 +38,7 @@ Assistant. Эта полноэкранная панель — основной
|
||||
12. [Визуальные состояния устройств](#12-визуальные-состояния-устройств)
|
||||
13. [Заливки комнат и свет](#13-заливки-комнат-и-свет)
|
||||
14. [Редактор подложки](#14-редактор-подложки)
|
||||
15. [Солнце: фон и оконные лучи](#15-солнце-фон-и-оконные-лучи)
|
||||
15. [Солнце и Луна: фон, оконные лучи и луна](#15-солнце-и-луна-фон-оконные-лучи-и-луна)
|
||||
16. [Роботы-пылесосы](#16-роботы-пылесосы)
|
||||
17. [Киоск-режим](#17-киоск-режим)
|
||||
18. [Статическая карточка пространства](#18-статическая-карточка-пространства)
|
||||
@@ -1790,9 +1790,9 @@ WebP и безопасный SVG; лимит 2 МиБ относится к со
|
||||
недостающего содержимого и после согласия оставляет ту же восстанавливаемую
|
||||
рамку, а не отклоняет весь план.
|
||||
|
||||
## 15. Солнце: фон и оконные лучи
|
||||
## 15. Солнце и Луна: фон, оконные лучи и луна
|
||||
|
||||
Это две независимые функции. Фон **Следует за Солнцем** работает без компаса:
|
||||
Фон и оконные лучи — две независимые функции; луна живёт на фоне. Фон **Следует за Солнцем** работает без компаса:
|
||||
он использует корректные данные `sun.sun`, а при их отсутствии автоматически
|
||||
переходит на локальные часы браузера. Направление севера и `sun.sun` обязательны
|
||||
только для оконных лучей. Общие значения можно переопределить в конкретном
|
||||
@@ -1850,6 +1850,33 @@ WebP и безопасный SVG; лимит 2 МиБ относится к со
|
||||
|
||||
Тени от других крыльев здания и внешних объектов не моделируются.
|
||||
|
||||
### Луна
|
||||
|
||||
При фоне **Следует за Солнцем** в сумерках (утренних и вечерних) и ночью в левом
|
||||
верхнем углу сцены видна луна в текущей фазе — тонкий серп, половина, полная.
|
||||
Включается одним переключателем в общих настройках, раздел **Солнце и Луна** →
|
||||
**Луна на плане в сумерках и ночью**. У новых установок переключатель включён,
|
||||
у обновлённых старых — выключен, пока его не включат: обновление не меняет вид
|
||||
плана.
|
||||
|
||||
| Условие | Результат |
|
||||
|---|---|
|
||||
| День, луна ниже 3° над горизонтом или новолуние (освещено меньше 3 %) | Луны нет |
|
||||
| Луна поднимается выше 3° или уходит ниже | Плавно появляется/исчезает за 2 секунды; при системном уменьшении движения — сразу |
|
||||
| Фаза | Меняется непрерывно, каждый день; освещённая сторона всегда слева, убывание идёт теми же состояниями в обратном порядке |
|
||||
| Пространство с собственным статическим фоном | Луны нет — она часть фона «Следует за Солнцем» |
|
||||
| План закрывает угол сцены | Луна за планом: видна та часть диска, что в полях; план, устройства и подписи всегда поверх неё |
|
||||
| Редакторы | Луны нет |
|
||||
| Полная карточка, киоск, статическая карточка пространства | Одинаково |
|
||||
|
||||
Положение и фаза считаются в карточке по координатам дома из Home Assistant
|
||||
(Настройки → Система → Общие) и часам устройства, без интеграций и внешних
|
||||
данных. Если координаты дома в Home Assistant не заданы, луна будет «чужой» —
|
||||
как и данные `sun.sun`; с неверными часами планшета неверны фаза и момент
|
||||
появления. Точность положения около 1°: момент пересечения 3° может разойтись с
|
||||
эфемеридами на несколько минут. Наклон терминатора и пепельный свет не
|
||||
воспроизводятся, тёмная сторона — едва заметный силуэт для читаемости серпа.
|
||||
|
||||
## 16. Роботы-пылесосы
|
||||
|
||||
Живая позиция доступна для маркера пылесоса, если источник отдаёт конечные
|
||||
|
||||
Reference in New Issue
Block a user