implement sse

This commit is contained in:
Darius
2026-02-05 22:40:50 +01:00
parent 6e1c5e4e67
commit 582c82d66f
6 changed files with 262 additions and 169 deletions

View File

@@ -2,6 +2,7 @@ import {
BaseService,
FullInformation,
HomeAssistantDeskPositionResult,
SseClientChangeEvent,
SseService,
TidalGetCurrent,
type ServiceResult,
@@ -14,33 +15,135 @@ export class HomepageService extends BaseService<null> {
private haService: HomeAssistantService;
private tidalService: TidalService;
private sseService: SseService;
private pollingInterval: ReturnType<typeof setInterval> | null = null;
private songEndTimeout: ReturnType<typeof setTimeout> | null = null;
private lastPoll: FullInformation | null = null;
constructor(haService: HomeAssistantService, tidalService: TidalService, sseService: SseService) {
constructor(
haService: HomeAssistantService,
tidalService: TidalService,
sseService: SseService,
) {
super(null);
this.haService = haService;
this.tidalService = tidalService;
this.sseService = sseService;
this.listenForClientChange();
}
async getFullInformation(): Promise<ServiceResult<FullInformation | string>> {
try {
const [desk, temp, song] = await Promise.all([
this.haService.getDeskPosition(),
this.haService.getTemperatureText(),
this.tidalService.getSong()
this.haService.getDeskPosition(),
this.haService.getTemperatureText(),
this.tidalService.getSong(),
]);
const result = {
ha_desk_position: desk.successful ? this.haService.convertPosResultToApiAnswer(desk.result as HomeAssistantDeskPositionResult) : null,
ha_desk_position: desk.successful
? this.haService.convertPosResultToApiAnswer(
desk.result as HomeAssistantDeskPositionResult,
)
: null,
ha_temp: temp.successful ? temp.result : null,
tidal_current: song ? song.result as TidalGetCurrent : null,
}
tidal_current: song ? (song.result as TidalGetCurrent) : null,
};
return this.getSuccessfulResult(result);
return this.getSuccessfulResult(this.updateLastPoll(result));
} catch {
const error_message = "error getting all information";
logWarning(error_message);
return this.getErrorResult(error_message);
}
}
updateLastPoll(newPoll: FullInformation) {
const updates = [];
if (
this.lastPoll?.ha_desk_position?.is_standing !==
newPoll.ha_desk_position?.is_standing
) {
updates.push({
component: "ha_desk_position",
data: newPoll.ha_desk_position,
});
}
if (this.lastPoll?.ha_temp !== newPoll.ha_temp) {
updates.push({
component: "ha_temp",
data: newPoll.ha_temp,
});
}
if (
this.lastPoll?.tidal_current?.title !== newPoll.tidal_current?.title ||
this.lastPoll?.tidal_current?.status !== newPoll.tidal_current?.status
) {
updates.push({
component: "tidal_current",
data: newPoll.tidal_current,
});
}
this.sseService.notifyClients({
type: "update",
data: updates,
});
this.lastPoll = newPoll;
this.scheduleSongEndPoll(newPoll.tidal_current);
return newPoll;
}
private scheduleSongEndPoll(tidal: TidalGetCurrent | null): void {
this.clearSongEndPoll();
if (!tidal || tidal.status === "paused") {
return;
}
const remainingSeconds = tidal.durationInSeconds - tidal.currentInSeconds;
if (remainingSeconds > 0) {
this.songEndTimeout = setTimeout(
() => {
this.songEndTimeout = null;
this.getFullInformation();
},
(remainingSeconds + 1) * 1000,
);
}
}
private clearSongEndPoll(): void {
if (this.songEndTimeout) {
clearTimeout(this.songEndTimeout);
this.songEndTimeout = null;
}
}
listenForClientChange(): void {
this.sseService.onClientChange((clientChange: SseClientChangeEvent) => {
if (clientChange.clientCount === 0) {
this.stopPolling();
} else {
if (!this.pollingInterval) {
this.startPolling();
}
}
});
}
startPolling(): void {
this.pollingInterval = setInterval(() => {
this.getFullInformation();
}, 30000);
}
stopPolling(): void {
if (this.pollingInterval) {
clearInterval(this.pollingInterval);
}
this.clearSongEndPoll();
}
}