Auf ae.js umgestellt
This commit is contained in:
@@ -6,9 +6,12 @@ Lovelace-Card für die Sternwarte.
|
||||
|
||||
```text
|
||||
dist/
|
||||
astronomy.browser.min.js
|
||||
sternwarte-card.js
|
||||
```
|
||||
|
||||
`sternwarte-card.js` lädt `astronomy.browser.min.js` automatisch nach, um Dämmerungszeiten und Mondposition präziser zu berechnen.
|
||||
|
||||
## Für HACS
|
||||
|
||||
Dieser Ordner ist als Root eines späteren HACS-Dashboard-Repositorys vorbereitet.
|
||||
|
||||
+116
-90
@@ -14,6 +14,14 @@ const DEFAULT_ENTITIES = {
|
||||
astroLon: 11.0481766,
|
||||
};
|
||||
|
||||
const ASTRONOMY_LIB_URL = (() => {
|
||||
try {
|
||||
return new URL("./astronomy.browser.min.js", import.meta.url).toString();
|
||||
} catch (error) {
|
||||
return null;
|
||||
}
|
||||
})();
|
||||
|
||||
class SternwarteCard extends HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
@@ -22,9 +30,11 @@ class SternwarteCard extends HTMLElement {
|
||||
close: { draft: "", editing: false, pending: false },
|
||||
};
|
||||
this._pendingRender = false;
|
||||
this._astronomyLoadPromise = null;
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
this._ensureAstronomy();
|
||||
this._ensureClockTimer();
|
||||
}
|
||||
|
||||
@@ -71,6 +81,46 @@ class SternwarteCard extends HTMLElement {
|
||||
}, 30000);
|
||||
}
|
||||
|
||||
_astronomy() {
|
||||
return window.Astronomy || null;
|
||||
}
|
||||
|
||||
_ensureAstronomy() {
|
||||
const astronomy = this._astronomy();
|
||||
if (astronomy) return Promise.resolve(astronomy);
|
||||
if (this._astronomyLoadPromise) return this._astronomyLoadPromise;
|
||||
if (!ASTRONOMY_LIB_URL) return Promise.resolve(null);
|
||||
|
||||
this._astronomyLoadPromise = new Promise((resolve) => {
|
||||
const finish = () => {
|
||||
const loaded = this._astronomy();
|
||||
if (loaded && this._hass) {
|
||||
this._requestRender();
|
||||
}
|
||||
this._astronomyLoadPromise = null;
|
||||
resolve(loaded || null);
|
||||
};
|
||||
|
||||
const existing = Array.from(document.querySelectorAll("script"))
|
||||
.find((script) => script.src === ASTRONOMY_LIB_URL);
|
||||
|
||||
if (existing) {
|
||||
existing.addEventListener("load", finish, { once: true });
|
||||
existing.addEventListener("error", finish, { once: true });
|
||||
return;
|
||||
}
|
||||
|
||||
const script = document.createElement("script");
|
||||
script.src = ASTRONOMY_LIB_URL;
|
||||
script.async = true;
|
||||
script.addEventListener("load", finish, { once: true });
|
||||
script.addEventListener("error", finish, { once: true });
|
||||
document.head.appendChild(script);
|
||||
});
|
||||
|
||||
return this._astronomyLoadPromise;
|
||||
}
|
||||
|
||||
_requestRender() {
|
||||
if (this._isScheduleEditing()) {
|
||||
this._pendingRender = true;
|
||||
@@ -389,107 +439,83 @@ class SternwarteCard extends HTMLElement {
|
||||
return date >= lastSunday(2, 1) && date < lastSunday(9, 1);
|
||||
}
|
||||
|
||||
_calcTwilight(date) {
|
||||
_astroObserver() {
|
||||
const astronomy = this._astronomy();
|
||||
if (!astronomy) return null;
|
||||
const lat = Number(this.config.astroLat);
|
||||
const lon = Number(this.config.astroLon);
|
||||
const jd = date.getTime() / 86400000 + 2440587.5;
|
||||
const n = jd - 2451545.0;
|
||||
const L = ((280.460 + 0.9856474 * n) % 360 + 360) % 360;
|
||||
const g = ((357.528 + 0.9856003 * n) % 360 + 360) % 360;
|
||||
const gR = g * Math.PI / 180;
|
||||
const lam = L + 1.915 * Math.sin(gR) + 0.020 * Math.sin(2 * gR);
|
||||
const lamR = lam * Math.PI / 180;
|
||||
const eps = 23.439 - 0.0000004 * n;
|
||||
const epsR = eps * Math.PI / 180;
|
||||
const dec = Math.asin(Math.sin(epsR) * Math.sin(lamR));
|
||||
const RA = Math.atan2(Math.cos(epsR) * Math.sin(lamR), Math.cos(lamR)) * 180 / Math.PI / 15;
|
||||
const GMST0h = ((L + 180) % 360 + 360) % 360 / 15;
|
||||
const solarNoon = (12 - lon / 15 - (RA - GMST0h + 12 + 720) % 24 + 24) % 24;
|
||||
const latR = lat * Math.PI / 180;
|
||||
|
||||
const eventUTC = (depDeg) => {
|
||||
const cosH = (Math.sin(-depDeg * Math.PI / 180) - Math.sin(latR) * Math.sin(dec))
|
||||
/ (Math.cos(latR) * Math.cos(dec));
|
||||
if (cosH < -1 || cosH > 1) return null;
|
||||
const H = Math.acos(cosH) * 180 / Math.PI / 15;
|
||||
return { dawn: (solarNoon - H + 24) % 24, dusk: (solarNoon + H) % 24 };
|
||||
};
|
||||
|
||||
return {
|
||||
sun: eventUTC(0.833),
|
||||
civil: eventUTC(6),
|
||||
naut: eventUTC(12),
|
||||
astro: eventUTC(18),
|
||||
};
|
||||
if (!Number.isFinite(lat) || !Number.isFinite(lon)) return null;
|
||||
return new astronomy.Observer(lat, lon, 0);
|
||||
}
|
||||
|
||||
_julianDate(date) {
|
||||
return date.getTime() / 86400000 + 2440587.5;
|
||||
_astroTimeToUtcHour(eventTime) {
|
||||
if (!eventTime) return null;
|
||||
const date = eventTime.date instanceof Date ? eventTime.date : null;
|
||||
if (!date || Number.isNaN(date.getTime())) return null;
|
||||
return date.getUTCHours() + date.getUTCMinutes() / 60 + date.getUTCSeconds() / 3600;
|
||||
}
|
||||
|
||||
_gmstHours(jd) {
|
||||
const T = (jd - 2451545) / 36525;
|
||||
const gmst = 280.46061837 + 360.98564736629 * (jd - 2451545) + 0.000387933 * T * T
|
||||
- (T * T * T) / 38710000;
|
||||
return (((gmst % 360) + 360) % 360) / 15;
|
||||
_calcTwilight(date) {
|
||||
const astronomy = this._astronomy();
|
||||
const observer = this._astroObserver();
|
||||
if (!astronomy || !observer) {
|
||||
return {
|
||||
sun: { dawn: null, dusk: null },
|
||||
civil: { dawn: null, dusk: null },
|
||||
naut: { dawn: null, dusk: null },
|
||||
astro: { dawn: null, dusk: null },
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
const bodySun = astronomy.Body?.Sun ?? "Sun";
|
||||
const start = new Date(date);
|
||||
start.setHours(0, 0, 0, 0);
|
||||
const limitDays = 2;
|
||||
const altitudeEvent = (altitude) => ({
|
||||
dawn: this._astroTimeToUtcHour(astronomy.SearchAltitude(bodySun, observer, +1, start, limitDays, altitude)),
|
||||
dusk: this._astroTimeToUtcHour(astronomy.SearchAltitude(bodySun, observer, -1, start, limitDays, altitude)),
|
||||
});
|
||||
|
||||
return {
|
||||
sun: {
|
||||
dawn: this._astroTimeToUtcHour(astronomy.SearchRiseSet(bodySun, observer, +1, start, limitDays, 0)),
|
||||
dusk: this._astroTimeToUtcHour(astronomy.SearchRiseSet(bodySun, observer, -1, start, limitDays, 0)),
|
||||
},
|
||||
civil: altitudeEvent(-6),
|
||||
naut: altitudeEvent(-12),
|
||||
astro: altitudeEvent(-18),
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
sun: { dawn: null, dusk: null },
|
||||
civil: { dawn: null, dusk: null },
|
||||
naut: { dawn: null, dusk: null },
|
||||
astro: { dawn: null, dusk: null },
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
_moonStateAt(date) {
|
||||
const jd = this._julianDate(date);
|
||||
const d = jd - 2451543.5;
|
||||
const lat = Number(this.config.astroLat) * Math.PI / 180;
|
||||
const lonHours = Number(this.config.astroLon) / 15;
|
||||
const toRad = (deg) => deg * Math.PI / 180;
|
||||
const normalizeDeg = (deg) => ((deg % 360) + 360) % 360;
|
||||
const astronomy = this._astronomy();
|
||||
const observer = this._astroObserver();
|
||||
if (!astronomy || !observer) {
|
||||
return { altitude: Number.NEGATIVE_INFINITY, illumination: 0 };
|
||||
}
|
||||
|
||||
const sunMeanLongitude = normalizeDeg(280.460 + 0.9856474 * (jd - 2451545.0));
|
||||
const sunMeanAnomaly = normalizeDeg(357.528 + 0.9856003 * (jd - 2451545.0));
|
||||
const sunLongitude = normalizeDeg(
|
||||
sunMeanLongitude + 1.915 * Math.sin(toRad(sunMeanAnomaly)) + 0.020 * Math.sin(2 * toRad(sunMeanAnomaly)),
|
||||
);
|
||||
try {
|
||||
const bodyMoon = astronomy.Body?.Moon ?? "Moon";
|
||||
const equator = astronomy.Equator(bodyMoon, date, observer, true, true);
|
||||
const horizon = astronomy.Horizon(date, observer, equator.ra, equator.dec, "normal");
|
||||
const illumination = astronomy.Illumination(bodyMoon, date);
|
||||
|
||||
const N = normalizeDeg(125.1228 - 0.0529538083 * d);
|
||||
const i = toRad(5.1454);
|
||||
const w = normalizeDeg(318.0634 + 0.1643573223 * d);
|
||||
const a = 60.2666;
|
||||
const e = 0.0549;
|
||||
const M = normalizeDeg(115.3654 + 13.0649929509 * d);
|
||||
const E = M + (180 / Math.PI) * e * Math.sin(toRad(M)) * (1 + e * Math.cos(toRad(M)));
|
||||
const xv = a * (Math.cos(toRad(E)) - e);
|
||||
const yv = a * (Math.sqrt(1 - e * e) * Math.sin(toRad(E)));
|
||||
const v = Math.atan2(yv, xv);
|
||||
const r = Math.sqrt(xv * xv + yv * yv);
|
||||
|
||||
const xh = r * (Math.cos(toRad(N)) * Math.cos(v + toRad(w)) - Math.sin(toRad(N)) * Math.sin(v + toRad(w)) * Math.cos(i));
|
||||
const yh = r * (Math.sin(toRad(N)) * Math.cos(v + toRad(w)) + Math.cos(toRad(N)) * Math.sin(v + toRad(w)) * Math.cos(i));
|
||||
const zh = r * Math.sin(v + toRad(w)) * Math.sin(i);
|
||||
|
||||
const moonLon = Math.atan2(yh, xh);
|
||||
const moonLat = Math.atan2(zh, Math.sqrt(xh * xh + yh * yh));
|
||||
const obliquity = toRad(23.4393 - 0.0000004 * d);
|
||||
|
||||
const xe = r * Math.cos(moonLon) * Math.cos(moonLat);
|
||||
const ye = r * Math.sin(moonLon) * Math.cos(moonLat);
|
||||
const ze = r * Math.sin(moonLat);
|
||||
|
||||
const xeq = xe;
|
||||
const yeq = ye * Math.cos(obliquity) - ze * Math.sin(obliquity);
|
||||
const zeq = ye * Math.sin(obliquity) + ze * Math.cos(obliquity);
|
||||
|
||||
const raHours = (((Math.atan2(yeq, xeq) * 180 / Math.PI) / 15) + 24) % 24;
|
||||
const dec = Math.atan2(zeq, Math.sqrt(xeq * xeq + yeq * yeq));
|
||||
const lstHours = (this._gmstHours(jd) + lonHours + 24) % 24;
|
||||
const hourAngle = toRad((lstHours - raHours) * 15);
|
||||
const altitude = Math.asin(
|
||||
Math.sin(dec) * Math.sin(lat) + Math.cos(dec) * Math.cos(lat) * Math.cos(hourAngle),
|
||||
);
|
||||
|
||||
const elongation = Math.acos(
|
||||
Math.cos(moonLat) * Math.cos(moonLon - toRad(sunLongitude)),
|
||||
);
|
||||
const illumination = Math.max(0, Math.min(1, (1 - Math.cos(elongation)) / 2));
|
||||
|
||||
return { altitude, illumination };
|
||||
return {
|
||||
altitude: (Number(horizon?.altitude) || 0) * Math.PI / 180,
|
||||
illumination: Math.max(0, Math.min(1, Number(illumination?.phase_fraction) || 0)),
|
||||
};
|
||||
} catch (error) {
|
||||
return { altitude: Number.NEGATIVE_INFINITY, illumination: 0 };
|
||||
}
|
||||
}
|
||||
|
||||
_moonTrackState() {
|
||||
|
||||
Reference in New Issue
Block a user