Gui Fixes
This commit is contained in:
@@ -48,3 +48,11 @@ Arbeitsweise:
|
|||||||
- Nach dem Kopieren muss das Home-Assistant-Frontend hart neu geladen werden, damit kein alter Browser-Cache im Weg ist.
|
- Nach dem Kopieren muss das Home-Assistant-Frontend hart neu geladen werden, damit kein alter Browser-Cache im Weg ist.
|
||||||
- Für das Kopieren gibt es im Repo-Root den Helfer `deploy-test.ps1`.
|
- Für das Kopieren gibt es im Repo-Root den Helfer `deploy-test.ps1`.
|
||||||
- Aufruf aus dem Repo-Root: `.\deploy-test.ps1`
|
- Aufruf aus dem Repo-Root: `.\deploy-test.ps1`
|
||||||
|
|
||||||
|
Wichtige Besonderheiten der Sternwarten-Card:
|
||||||
|
|
||||||
|
- Der Dämmerungsbalken arbeitet auf einer lokalen `12:00 → 12:00`-Achse.
|
||||||
|
- Die Dämmerungsphasen werden direkt aus der Sonnenhöhe bestimmt, nicht nur aus einzelnen Dawn-/Dusk-Zeitpunkten.
|
||||||
|
- Das ist wichtig für Sommerfälle ohne astronomische Nacht.
|
||||||
|
- Die Marker `Öffnen geplant` und `Schließen geplant` werden für diese Achse nach lokaler Uhrzeit eingeordnet.
|
||||||
|
- Wenn diese Marker fehlen, ist die erste Prüffrage: Wurde versehentlich wieder gegen ein echtes Datumsfenster statt gegen die `12 → 12`-Achse gerechnet?
|
||||||
|
|||||||
@@ -12,6 +12,28 @@ dist/
|
|||||||
|
|
||||||
`sternwarte-card.js` lädt `astronomy.browser.min.js` automatisch nach, um Dämmerungszeiten und Mondposition präziser zu berechnen.
|
`sternwarte-card.js` lädt `astronomy.browser.min.js` automatisch nach, um Dämmerungszeiten und Mondposition präziser zu berechnen.
|
||||||
|
|
||||||
|
## Dämmerungsbalken
|
||||||
|
|
||||||
|
Der Dämmerungsbalken in `dist/sternwarte-card.js` arbeitet bewusst nicht nur mit einzelnen Dawn-/Dusk-Zeitpunkten, sondern mit einem lokalen `12:00 → 12:00`-Fenster.
|
||||||
|
|
||||||
|
Wichtige Punkte:
|
||||||
|
|
||||||
|
- Die Phasen `Tag`, `bürgerliche Dämmerung`, `nautische Dämmerung`, `astronomische Dämmerung` und `Nacht` werden direkt aus der Sonnenhöhe bestimmt.
|
||||||
|
- Dafür wird das komplette lokale 24-Stunden-Fenster in kleinen Schritten durchgerechnet.
|
||||||
|
- Diese Logik ist robuster für Sommerfälle, in denen es keine astronomische Nacht mehr gibt.
|
||||||
|
|
||||||
|
Marker im Balken:
|
||||||
|
|
||||||
|
- `Öffnen geplant` wird grün dargestellt.
|
||||||
|
- `Schließen geplant` wird rot dargestellt.
|
||||||
|
- Der Marker `Jetzt` ist bewusst hell/weiß, damit er nicht mit `Schließen` verwechselt wird.
|
||||||
|
|
||||||
|
Wichtig für spätere Änderungen:
|
||||||
|
|
||||||
|
- Die Balkenachse ist eine Uhrzeitachse `12 → 12`, keine reine Datumsachse.
|
||||||
|
- Die Marker für `Öffnen geplant` und `Schließen geplant` müssen daher anhand der lokalen Uhrzeit auf diese Achse gelegt werden, nicht anhand eines festen Datumsfensters.
|
||||||
|
- Wenn die Marker verschwinden, zuerst prüfen, ob versehentlich wieder mit einem echten Zeitfenster statt mit der `12 → 12`-Achse gerechnet wird.
|
||||||
|
|
||||||
## Für HACS
|
## Für HACS
|
||||||
|
|
||||||
Dieser Ordner ist als Root eines späteren HACS-Dashboard-Repositorys vorbereitet.
|
Dieser Ordner ist als Root eines späteren HACS-Dashboard-Repositorys vorbereitet.
|
||||||
|
|||||||
+42
-22
@@ -330,12 +330,29 @@ class SternwarteCard extends HTMLElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_parseEntityDate(rawValue) {
|
||||||
|
if (!rawValue || rawValue === "unknown" || rawValue === "unavailable") return null;
|
||||||
|
if (rawValue instanceof Date) {
|
||||||
|
return Number.isNaN(rawValue.getTime()) ? null : rawValue;
|
||||||
|
}
|
||||||
|
if (typeof rawValue !== "string") {
|
||||||
|
const value = new Date(rawValue);
|
||||||
|
return Number.isNaN(value.getTime()) ? null : value;
|
||||||
|
}
|
||||||
|
|
||||||
|
const normalized = rawValue.trim().replace(" ", "T");
|
||||||
|
const hasTimezone = /(?:Z|[+-]\d{2}:\d{2})$/.test(normalized);
|
||||||
|
const localCandidate = hasTimezone ? normalized : normalized;
|
||||||
|
const value = new Date(localCandidate);
|
||||||
|
return Number.isNaN(value.getTime()) ? null : value;
|
||||||
|
}
|
||||||
|
|
||||||
_isoForInput(state) {
|
_isoForInput(state) {
|
||||||
if (!state || !state.state || state.state === "unknown" || state.state === "unavailable") {
|
if (!state || !state.state || state.state === "unknown" || state.state === "unavailable") {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
const value = new Date(state.state);
|
const value = this._parseEntityDate(state.state);
|
||||||
if (Number.isNaN(value.getTime())) return "";
|
if (!value) return "";
|
||||||
const pad = (n) => String(n).padStart(2, "0");
|
const pad = (n) => String(n).padStart(2, "0");
|
||||||
return `${value.getFullYear()}-${pad(value.getMonth() + 1)}-${pad(value.getDate())}T${pad(value.getHours())}:${pad(value.getMinutes())}`;
|
return `${value.getFullYear()}-${pad(value.getMonth() + 1)}-${pad(value.getDate())}T${pad(value.getHours())}:${pad(value.getMinutes())}`;
|
||||||
}
|
}
|
||||||
@@ -344,8 +361,8 @@ class SternwarteCard extends HTMLElement {
|
|||||||
if (!state || !state.state || state.state === "unknown" || state.state === "unavailable") {
|
if (!state || !state.state || state.state === "unknown" || state.state === "unavailable") {
|
||||||
return "\u2014";
|
return "\u2014";
|
||||||
}
|
}
|
||||||
const value = new Date(state.state);
|
const value = this._parseEntityDate(state.state);
|
||||||
if (Number.isNaN(value.getTime())) return "\u2014";
|
if (!value) return "\u2014";
|
||||||
const pad = (n) => String(n).padStart(2, "0");
|
const pad = (n) => String(n).padStart(2, "0");
|
||||||
return `${pad(value.getDate())}.${pad(value.getMonth() + 1)}. ${pad(value.getHours())}:${pad(value.getMinutes())}`;
|
return `${pad(value.getDate())}.${pad(value.getMonth() + 1)}. ${pad(value.getHours())}:${pad(value.getMinutes())}`;
|
||||||
}
|
}
|
||||||
@@ -783,13 +800,12 @@ class SternwarteCard extends HTMLElement {
|
|||||||
|
|
||||||
segments.sort((a, b) => a.start - b.start);
|
segments.sort((a, b) => a.start - b.start);
|
||||||
|
|
||||||
const toWindowHour = (state) => {
|
const toChartHour = (state) => {
|
||||||
if (!state?.state || state.state === "unknown" || state.state === "unavailable") return null;
|
if (!state?.state || state.state === "unknown" || state.state === "unavailable") return null;
|
||||||
const value = new Date(state.state);
|
const value = this._parseEntityDate(state.state);
|
||||||
if (Number.isNaN(value.getTime())) return null;
|
if (!value) return null;
|
||||||
const hour = (value.getTime() - windowStart.getTime()) / 3600000;
|
const localHour = value.getHours() + value.getMinutes() / 60 + value.getSeconds() / 3600;
|
||||||
if (hour < 0 || hour > 24) return null;
|
return (localHour - 12 + 24) % 24;
|
||||||
return hour;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -800,8 +816,8 @@ class SternwarteCard extends HTMLElement {
|
|||||||
color: segment.color,
|
color: segment.color,
|
||||||
})),
|
})),
|
||||||
markerLeft: `${(nowLocal / 24) * 100}%`,
|
markerLeft: `${(nowLocal / 24) * 100}%`,
|
||||||
openMarkerLeft: toWindowHour(this._state(this._scheduleOpenEntityId())),
|
openMarkerLeft: toChartHour(this._state(this._scheduleOpenEntityId())),
|
||||||
closeMarkerLeft: toWindowHour(this._state(this._scheduleCloseEntityId())),
|
closeMarkerLeft: toChartHour(this._state(this._scheduleCloseEntityId())),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1084,24 +1100,28 @@ class SternwarteCard extends HTMLElement {
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
top: -4px;
|
top: -4px;
|
||||||
bottom: -4px;
|
bottom: -4px;
|
||||||
width: 4px;
|
width: 3px;
|
||||||
margin-left: -1px;
|
margin-left: -1px;
|
||||||
background: #d32f2f;
|
background: rgba(255, 255, 255, 0.95);
|
||||||
box-shadow: 0 0 8px rgba(211, 47, 47, 0.55);
|
box-shadow: 0 0 8px rgba(255, 255, 255, 0.45);
|
||||||
|
z-index: 5;
|
||||||
}
|
}
|
||||||
.astro-marker {
|
.astro-marker {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: -3px;
|
top: -4px;
|
||||||
bottom: -3px;
|
bottom: -4px;
|
||||||
width: 4px;
|
width: 6px;
|
||||||
margin-left: -1px;
|
margin-left: -2px;
|
||||||
box-shadow: 0 0 6px rgba(255, 255, 255, 0.25);
|
border-radius: 999px;
|
||||||
|
z-index: 4;
|
||||||
}
|
}
|
||||||
.astro-marker.open {
|
.astro-marker.open {
|
||||||
background: #4caf50;
|
background: #20c45a;
|
||||||
|
box-shadow: 0 0 10px rgba(32, 196, 90, 0.7);
|
||||||
}
|
}
|
||||||
.astro-marker.close {
|
.astro-marker.close {
|
||||||
background: #ffb300;
|
background: #e53935;
|
||||||
|
box-shadow: 0 0 10px rgba(229, 57, 53, 0.72);
|
||||||
}
|
}
|
||||||
.astro-moon-track {
|
.astro-moon-track {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
|||||||
Reference in New Issue
Block a user