Zeitplanung und nautische Einzeltermine erweitern
This commit is contained in:
@@ -37,7 +37,6 @@ Für Tests gibt es eine komplette Test-Home-Assistant-Umgebung mit SMB-Zugriff u
|
|||||||
In dieser Freigabe liegen die Card-Dateien, die Home Assistant direkt als Dashboard-Ressourcen lädt:
|
In dieser Freigabe liegen die Card-Dateien, die Home Assistant direkt als Dashboard-Ressourcen lädt:
|
||||||
|
|
||||||
- `\\192.168.1.71\config\www\sternwarte-card.js`
|
- `\\192.168.1.71\config\www\sternwarte-card.js`
|
||||||
- `\\192.168.1.71\config\www\sternwarte-card-retro.js`
|
|
||||||
- `\\192.168.1.71\config\www\astronomy.browser.min.js`
|
- `\\192.168.1.71\config\www\astronomy.browser.min.js`
|
||||||
- `\\192.168.1.71\config\www\moon_small.png`
|
- `\\192.168.1.71\config\www\moon_small.png`
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ $sourceDir = Join-Path $repoRoot "sternwarte-homeassistant-card\dist"
|
|||||||
$targetDir = "\\192.168.1.71\config\www"
|
$targetDir = "\\192.168.1.71\config\www"
|
||||||
$files = @(
|
$files = @(
|
||||||
"sternwarte-card.js",
|
"sternwarte-card.js",
|
||||||
"sternwarte-card-retro.js",
|
|
||||||
"astronomy.browser.min.js",
|
"astronomy.browser.min.js",
|
||||||
"moon_small.png"
|
"moon_small.png"
|
||||||
)
|
)
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
+8
-2
@@ -813,8 +813,10 @@ class SternwarteCard extends HTMLElement {
|
|||||||
if (!state?.state || state.state === "unknown" || state.state === "unavailable") return null;
|
if (!state?.state || state.state === "unknown" || state.state === "unavailable") return null;
|
||||||
const value = this._parseEntityDate(state.state);
|
const value = this._parseEntityDate(state.state);
|
||||||
if (!value) return null;
|
if (!value) return null;
|
||||||
const localHour = value.getHours() + value.getMinutes() / 60 + value.getSeconds() / 3600;
|
const windowEnd = new Date(windowStart);
|
||||||
return (localHour - 12 + 24) % 24;
|
windowEnd.setDate(windowEnd.getDate() + 1);
|
||||||
|
if (value < windowStart || value >= windowEnd) return null;
|
||||||
|
return (value.getTime() - windowStart.getTime()) / 3600000;
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -1550,11 +1552,13 @@ class SternwarteCard extends HTMLElement {
|
|||||||
<input id="open-schedule-input" type="datetime-local" value="${openInputValue}">
|
<input id="open-schedule-input" type="datetime-local" value="${openInputValue}">
|
||||||
<button type="button" class="secondary" id="open-schedule-btn">\u00d6ffnen setzen</button>
|
<button type="button" class="secondary" id="open-schedule-btn">\u00d6ffnen setzen</button>
|
||||||
<button type="button" class="secondary" id="open-schedule-clear-btn">\u00d6ffnen l\u00f6schen</button>
|
<button type="button" class="secondary" id="open-schedule-clear-btn">\u00d6ffnen l\u00f6schen</button>
|
||||||
|
<button type="button" class="secondary" id="open-nautical-btn">Nautisch \u00f6ffnen</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="schedule-grid">
|
<div class="schedule-grid">
|
||||||
<input id="close-schedule-input" type="datetime-local" value="${closeInputValue}">
|
<input id="close-schedule-input" type="datetime-local" value="${closeInputValue}">
|
||||||
<button type="button" class="secondary" id="close-schedule-btn">Schlie\u00dfen setzen</button>
|
<button type="button" class="secondary" id="close-schedule-btn">Schlie\u00dfen setzen</button>
|
||||||
<button type="button" class="secondary" id="close-schedule-clear-btn">Schlie\u00dfen l\u00f6schen</button>
|
<button type="button" class="secondary" id="close-schedule-clear-btn">Schlie\u00dfen l\u00f6schen</button>
|
||||||
|
<button type="button" class="secondary" id="close-nautical-btn">Nautisch schlie\u00dfen</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="inline-actions">
|
<div class="inline-actions">
|
||||||
<button type="button" class="secondary" id="nautical-btn">Nautisch setzen</button>
|
<button type="button" class="secondary" id="nautical-btn">Nautisch setzen</button>
|
||||||
@@ -1586,6 +1590,8 @@ class SternwarteCard extends HTMLElement {
|
|||||||
this._bindClick("power-on-btn", () => this._callEntity("switch", "turn_on", powerEntityId));
|
this._bindClick("power-on-btn", () => this._callEntity("switch", "turn_on", powerEntityId));
|
||||||
this._bindClick("power-off-btn", () => this._callEntity("switch", "turn_off", powerEntityId));
|
this._bindClick("power-off-btn", () => this._callEntity("switch", "turn_off", powerEntityId));
|
||||||
this._bindClick("nautical-btn", () => this._call("sternwarte", "set_nautical_schedule"));
|
this._bindClick("nautical-btn", () => this._call("sternwarte", "set_nautical_schedule"));
|
||||||
|
this._bindClick("open-nautical-btn", () => this._call("sternwarte", "set_nautical_open_schedule"));
|
||||||
|
this._bindClick("close-nautical-btn", () => this._call("sternwarte", "set_nautical_close_schedule"));
|
||||||
this._bindClick("clear-schedule-btn", () => {
|
this._bindClick("clear-schedule-btn", () => {
|
||||||
this._resetScheduleDrafts();
|
this._resetScheduleDrafts();
|
||||||
this._call("sternwarte", "clear_schedule");
|
this._call("sternwarte", "clear_schedule");
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ from .const import (
|
|||||||
SERVICE_POWER_OFF,
|
SERVICE_POWER_OFF,
|
||||||
SERVICE_POWER_ON,
|
SERVICE_POWER_ON,
|
||||||
SERVICE_SET_NAUTICAL_SCHEDULE,
|
SERVICE_SET_NAUTICAL_SCHEDULE,
|
||||||
|
SERVICE_SET_NAUTICAL_OPEN_SCHEDULE,
|
||||||
|
SERVICE_SET_NAUTICAL_CLOSE_SCHEDULE,
|
||||||
SERVICE_STOP_ROOF,
|
SERVICE_STOP_ROOF,
|
||||||
)
|
)
|
||||||
from .coordinator import SternwarteDataUpdateCoordinator
|
from .coordinator import SternwarteDataUpdateCoordinator
|
||||||
@@ -77,6 +79,14 @@ async def _async_handle_set_nautical_schedule(hass: HomeAssistant, call: Service
|
|||||||
await _async_call_on_all(hass, "async_set_next_nautical_schedule")
|
await _async_call_on_all(hass, "async_set_next_nautical_schedule")
|
||||||
|
|
||||||
|
|
||||||
|
async def _async_handle_set_nautical_open_schedule(hass: HomeAssistant, call: ServiceCall) -> None:
|
||||||
|
await _async_call_on_all(hass, "async_set_next_nautical_open_schedule")
|
||||||
|
|
||||||
|
|
||||||
|
async def _async_handle_set_nautical_close_schedule(hass: HomeAssistant, call: ServiceCall) -> None:
|
||||||
|
await _async_call_on_all(hass, "async_set_next_nautical_close_schedule")
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||||
"""Set up Sternwarte from a config entry."""
|
"""Set up Sternwarte from a config entry."""
|
||||||
coordinator = SternwarteDataUpdateCoordinator(hass, entry)
|
coordinator = SternwarteDataUpdateCoordinator(hass, entry)
|
||||||
@@ -106,6 +116,16 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||||||
SERVICE_SET_NAUTICAL_SCHEDULE,
|
SERVICE_SET_NAUTICAL_SCHEDULE,
|
||||||
partial(_async_handle_set_nautical_schedule, hass),
|
partial(_async_handle_set_nautical_schedule, hass),
|
||||||
)
|
)
|
||||||
|
hass.services.async_register(
|
||||||
|
DOMAIN,
|
||||||
|
SERVICE_SET_NAUTICAL_OPEN_SCHEDULE,
|
||||||
|
partial(_async_handle_set_nautical_open_schedule, hass),
|
||||||
|
)
|
||||||
|
hass.services.async_register(
|
||||||
|
DOMAIN,
|
||||||
|
SERVICE_SET_NAUTICAL_CLOSE_SCHEDULE,
|
||||||
|
partial(_async_handle_set_nautical_close_schedule, hass),
|
||||||
|
)
|
||||||
|
|
||||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||||
return True
|
return True
|
||||||
@@ -127,6 +147,8 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||||||
SERVICE_CLEAR_OPEN_SCHEDULE,
|
SERVICE_CLEAR_OPEN_SCHEDULE,
|
||||||
SERVICE_CLEAR_CLOSE_SCHEDULE,
|
SERVICE_CLEAR_CLOSE_SCHEDULE,
|
||||||
SERVICE_SET_NAUTICAL_SCHEDULE,
|
SERVICE_SET_NAUTICAL_SCHEDULE,
|
||||||
|
SERVICE_SET_NAUTICAL_OPEN_SCHEDULE,
|
||||||
|
SERVICE_SET_NAUTICAL_CLOSE_SCHEDULE,
|
||||||
):
|
):
|
||||||
if hass.services.has_service(DOMAIN, service):
|
if hass.services.has_service(DOMAIN, service):
|
||||||
hass.services.async_remove(DOMAIN, service)
|
hass.services.async_remove(DOMAIN, service)
|
||||||
|
|||||||
@@ -49,3 +49,5 @@ ENTITY_ROOF_POSITION: Final = "roof_position"
|
|||||||
ENTITY_SCHEDULE_OPEN: Final = "schedule_open"
|
ENTITY_SCHEDULE_OPEN: Final = "schedule_open"
|
||||||
ENTITY_SCHEDULE_CLOSE: Final = "schedule_close"
|
ENTITY_SCHEDULE_CLOSE: Final = "schedule_close"
|
||||||
ENTITY_SET_NAUTICAL_SCHEDULE: Final = "set_nautical_schedule"
|
ENTITY_SET_NAUTICAL_SCHEDULE: Final = "set_nautical_schedule"
|
||||||
|
SERVICE_SET_NAUTICAL_OPEN_SCHEDULE: Final = "set_nautical_open_schedule"
|
||||||
|
SERVICE_SET_NAUTICAL_CLOSE_SCHEDULE: Final = "set_nautical_close_schedule"
|
||||||
|
|||||||
@@ -249,6 +249,22 @@ class SternwarteDataUpdateCoordinator(DataUpdateCoordinator[SternwarteState]):
|
|||||||
await self._async_save_runtime_state()
|
await self._async_save_runtime_state()
|
||||||
await self.async_request_refresh()
|
await self.async_request_refresh()
|
||||||
|
|
||||||
|
async def async_set_next_nautical_open_schedule(self) -> None:
|
||||||
|
"""Set only the next nautical dusk as the opening schedule."""
|
||||||
|
now = dt_util.now()
|
||||||
|
when = self._get_next_nautical_event(now, "dusk")
|
||||||
|
if when is None:
|
||||||
|
raise HomeAssistantError("Keine kommende nautische Abenddämmerung gefunden")
|
||||||
|
await self.async_set_open_schedule(when)
|
||||||
|
|
||||||
|
async def async_set_next_nautical_close_schedule(self) -> None:
|
||||||
|
"""Set only the next nautical dawn as the closing schedule."""
|
||||||
|
now = dt_util.now()
|
||||||
|
when = self._get_next_nautical_event(now, "dawn")
|
||||||
|
if when is None:
|
||||||
|
raise HomeAssistantError("Keine kommende nautische Morgendämmerung gefunden")
|
||||||
|
await self.async_set_close_schedule(when)
|
||||||
|
|
||||||
async def async_turn_power_on(self) -> None:
|
async def async_turn_power_on(self) -> None:
|
||||||
"""Turn roof power on."""
|
"""Turn roof power on."""
|
||||||
async with self._command_lock:
|
async with self._command_lock:
|
||||||
@@ -831,6 +847,17 @@ class SternwarteDataUpdateCoordinator(DataUpdateCoordinator[SternwarteState]):
|
|||||||
)
|
)
|
||||||
return dt_util.as_local(event_utc).replace(second=0, microsecond=0)
|
return dt_util.as_local(event_utc).replace(second=0, microsecond=0)
|
||||||
|
|
||||||
|
def _get_next_nautical_event(self, now: datetime, event_name: str) -> datetime | None:
|
||||||
|
for day_offset in range(3):
|
||||||
|
base_date = now + timedelta(days=day_offset)
|
||||||
|
twilight = self._calc_twilight(base_date)
|
||||||
|
if twilight["naut"] is None:
|
||||||
|
continue
|
||||||
|
event = self._event_to_local_datetime(base_date, twilight["naut"][event_name])
|
||||||
|
if event and event > now:
|
||||||
|
return event
|
||||||
|
return None
|
||||||
|
|
||||||
def _get_next_nautical_schedule(self, now: datetime) -> dict[str, datetime | None] | None:
|
def _get_next_nautical_schedule(self, now: datetime) -> dict[str, datetime | None] | None:
|
||||||
for day_offset in range(3):
|
for day_offset in range(3):
|
||||||
base_date = now + timedelta(days=day_offset)
|
base_date = now + timedelta(days=day_offset)
|
||||||
|
|||||||
@@ -33,3 +33,11 @@ clear_close_schedule:
|
|||||||
set_nautical_schedule:
|
set_nautical_schedule:
|
||||||
name: Nautische Dämmerung setzen
|
name: Nautische Dämmerung setzen
|
||||||
description: Setzt Öffnen und Schließen auf die nächste nautische Abend- und Morgendämmerung.
|
description: Setzt Öffnen und Schließen auf die nächste nautische Abend- und Morgendämmerung.
|
||||||
|
|
||||||
|
set_nautical_open_schedule:
|
||||||
|
name: Nautische Öffnungszeit setzen
|
||||||
|
description: Setzt nur Öffnen auf die nächste nautische Abenddämmerung.
|
||||||
|
|
||||||
|
set_nautical_close_schedule:
|
||||||
|
name: Nautische Schließzeit setzen
|
||||||
|
description: Setzt nur Schließen auf die nächste nautische Morgendämmerung.
|
||||||
|
|||||||
Reference in New Issue
Block a user