update for partial polling, add grist

This commit is contained in:
Darius
2026-02-06 21:28:48 +01:00
parent d1d0ff55a5
commit 8948470baa
3 changed files with 180 additions and 57 deletions

4
package-lock.json generated
View File

@@ -28,8 +28,8 @@
} }
}, },
"node_modules/@dpu/shared": { "node_modules/@dpu/shared": {
"version": "1.8.1", "version": "1.8.4",
"resolved": "git+https://git.dariusbag.dev/DarDarBinks/dpu-shared.git#27dc6f2b1214b8e2aff65de510caea402c2f88db", "resolved": "git+https://git.dariusbag.dev/DarDarBinks/dpu-shared.git#ff4248961b97a84bfdbeed85cd83b007332378ac",
"dependencies": { "dependencies": {
"@types/ws": "^8.18.1", "@types/ws": "^8.18.1",
"axios": "^1.7.9", "axios": "^1.7.9",

View File

@@ -35,13 +35,13 @@ export class GristService extends BaseService<GristClient> {
} }
} }
getTodayAsId(date = new Date()) { private getTodayAsId(date = new Date()) {
const start = new Date(date.getFullYear(), 0, 0); const start = new Date(date.getFullYear(), 0, 0);
const diff = date.getTime() - start.getTime(); const diff = date.getTime() - start.getTime();
return Math.floor(diff / 86400000); return Math.floor(diff / 86400000);
} }
transformToPersonalGoals( private transformToPersonalGoals(
obj: Record<string, unknown>, obj: Record<string, unknown>,
): GristRecord_PersonalGoals { ): GristRecord_PersonalGoals {
return { return {

View File

@@ -1,28 +1,40 @@
import { import {
type API_HA_DeskPosition,
BaseService, BaseService,
type ComponentUpdate,
type FullInformation, type FullInformation,
type GristRecord_PersonalGoals,
type HomeAssistantDeskPositionResult, type HomeAssistantDeskPositionResult,
type ServiceResult, type ServiceResult,
type TidalGetCurrent, type TidalGetCurrent,
type WsService, type WsService,
} from "@dpu/shared"; } from "@dpu/shared";
import { logInfo, logWarning } from "@dpu/shared/dist/logger.js"; import { logInfo, logWarning } from "@dpu/shared/dist/logger.js";
import type { GristService } from "../grist/service";
import type { HomeAssistantService } from "../homeassistant/service"; import type { HomeAssistantService } from "../homeassistant/service";
import type { TidalService } from "../tidal/service"; import type { TidalService } from "../tidal/service";
export class HomepageService extends BaseService<null> { export class HomepageService extends BaseService<null> {
private gristService: GristService;
private haService: HomeAssistantService; private haService: HomeAssistantService;
private tidalService: TidalService; private tidalService: TidalService;
private wsService: WsService; private wsService: WsService;
private pollingInterval: ReturnType<typeof setInterval> | null = null; private pollingIntervals: ReturnType<typeof setInterval>[] = [];
private lastPoll: FullInformation | null = null; private lastPoll: FullInformation = {
ha_desk_position: null,
ha_temp: null,
tidal_current: null,
grist_personal_goals: null,
};
constructor( constructor(
gristService: GristService,
haService: HomeAssistantService, haService: HomeAssistantService,
tidalService: TidalService, tidalService: TidalService,
wsService: WsService, wsService: WsService,
) { ) {
super(null); super(null);
this.gristService = gristService;
this.haService = haService; this.haService = haService;
this.tidalService = tidalService; this.tidalService = tidalService;
this.wsService = wsService; this.wsService = wsService;
@@ -31,23 +43,21 @@ export class HomepageService extends BaseService<null> {
async getFullInformation(): Promise<ServiceResult<FullInformation | string>> { async getFullInformation(): Promise<ServiceResult<FullInformation | string>> {
try { try {
const [desk, temp, song] = await Promise.all([ const [desk, temp, tidal, personal_goals] = await Promise.all([
this.haService.getDeskPosition(), this._getDesk(),
this.haService.getTemperatureText(), this._getTemp(),
this.tidalService.getSong(), this._getTidal(),
this._getGristPG(),
]); ]);
const result = { return this.getSuccessfulResult(
ha_desk_position: desk.successful this.updateFull({
? this.haService.convertPosResultToApiAnswer( ha_desk_position: desk,
desk.result as HomeAssistantDeskPositionResult, ha_temp: temp,
) tidal_current: tidal,
: null, grist_personal_goals: personal_goals,
ha_temp: temp.successful ? temp.result : null, }),
tidal_current: song ? (song.result as TidalGetCurrent) : null, );
};
return this.getSuccessfulResult(this.updateLastPoll(result));
} catch { } catch {
const error_message = "error getting all information"; const error_message = "error getting all information";
logWarning(error_message); logWarning(error_message);
@@ -55,34 +65,39 @@ export class HomepageService extends BaseService<null> {
} }
} }
updateLastPoll(newPoll: FullInformation) { private async _getDesk(): Promise<API_HA_DeskPosition | null> {
const updates = []; const desk = await this.haService.getDeskPosition();
if ( return desk.successful
this.lastPoll?.ha_desk_position?.is_standing !== ? this.haService.convertPosResultToApiAnswer(
newPoll.ha_desk_position?.is_standing desk.result as HomeAssistantDeskPositionResult,
) { )
updates.push({ : null;
component: "ha_desk_position", }
data: newPoll.ha_desk_position,
});
}
if (this.lastPoll?.ha_temp !== newPoll.ha_temp) { private async _getTemp(): Promise<string | null> {
updates.push({ const temp = await this.haService.getTemperatureText();
component: "ha_temp", return temp.successful ? temp.result : null;
data: newPoll.ha_temp, }
});
}
if ( private async _getTidal(): Promise<TidalGetCurrent | null> {
this.lastPoll?.tidal_current?.title !== newPoll.tidal_current?.title || const tidal = await this.tidalService.getSong();
this.lastPoll?.tidal_current?.status !== newPoll.tidal_current?.status return tidal ? (tidal.result as TidalGetCurrent) : null;
) { }
updates.push({
component: "tidal_current", private async _getGristPG(): Promise<GristRecord_PersonalGoals | null> {
data: newPoll.tidal_current, const personal_goals = await this.gristService.getToday();
}); return personal_goals.successful
} ? (personal_goals.result as GristRecord_PersonalGoals)
: null;
}
updateFull(newPoll: FullInformation): FullInformation {
const updates: ComponentUpdate[] = [];
this.updateHaDesk(newPoll.ha_desk_position, updates);
this.updateHaTemp(newPoll.ha_temp, updates);
this.updateTidal(newPoll.tidal_current, updates);
this.updateGristPG(newPoll.grist_personal_goals, updates);
if (updates.length > 0) { if (updates.length > 0) {
logInfo("Updating clients"); logInfo("Updating clients");
@@ -92,16 +107,117 @@ export class HomepageService extends BaseService<null> {
}); });
} }
this.lastPoll = newPoll;
return newPoll; return newPoll;
} }
async updatePartial(components: string[]): Promise<void> {
const updates: ComponentUpdate[] = [];
for (const component of components) {
switch (component) {
case "desk": {
this.updateHaDesk(await this._getDesk(), updates);
break;
}
case "temp":
this.updateHaTemp(await this._getTemp(), updates);
break;
case "tidal":
this.updateTidal(await this._getTidal(), updates);
break;
case "grist":
this.updateGristPG(await this._getGristPG(), updates);
break;
default:
}
}
this.wsService.broadcast({
type: "update",
data: updates,
});
}
private updateHaDesk(
new_ha_desk_position: API_HA_DeskPosition | null,
updates: ComponentUpdate[],
): void {
if (
this.lastPoll.ha_desk_position?.is_standing !==
new_ha_desk_position?.is_standing
) {
this.lastPoll.ha_desk_position = new_ha_desk_position;
updates.push({
component: "ha_desk_position",
data: new_ha_desk_position,
});
}
}
private updateHaTemp(
new_ha_temp: string | null,
updates: ComponentUpdate[],
): void {
if (this.lastPoll.ha_temp !== new_ha_temp) {
this.lastPoll.ha_temp = new_ha_temp;
updates.push({
component: "ha_temp",
data: new_ha_temp,
});
}
}
private updateTidal(
new_tidal_current: TidalGetCurrent | null,
updates: ComponentUpdate[],
): void {
if (
this.lastPoll.tidal_current?.title !== new_tidal_current?.title ||
this.lastPoll.tidal_current?.artists !== new_tidal_current?.artists ||
this.lastPoll.tidal_current?.status !== new_tidal_current?.status ||
this.lastPoll.tidal_current?.volume !== new_tidal_current?.volume
) {
this.lastPoll.tidal_current = new_tidal_current;
updates.push({
component: "tidal_current",
data: new_tidal_current,
});
}
}
private updateGristPG(
new_grist_personal_goals: GristRecord_PersonalGoals | null,
updates: ComponentUpdate[],
): void {
if (
this.lastPoll.grist_personal_goals?.went_outside !==
new_grist_personal_goals?.went_outside ||
this.lastPoll.grist_personal_goals?.standing !==
new_grist_personal_goals?.standing ||
this.lastPoll.grist_personal_goals?.steps !==
new_grist_personal_goals?.steps ||
this.lastPoll.grist_personal_goals?.pushups !==
new_grist_personal_goals?.pushups ||
this.lastPoll.grist_personal_goals?.squats !==
new_grist_personal_goals?.squats ||
this.lastPoll.grist_personal_goals?.leg_raises !==
new_grist_personal_goals?.leg_raises ||
this.lastPoll.grist_personal_goals?.stairs !==
new_grist_personal_goals?.stairs
) {
this.lastPoll.grist_personal_goals = new_grist_personal_goals;
updates.push({
component: "grist_personal_goals",
data: new_grist_personal_goals,
});
}
}
listenForClientChange(): void { listenForClientChange(): void {
this.wsService.onClientChange((clients: number) => { this.wsService.onClientChange((clients: number) => {
if (clients === 0) { if (clients === 0) {
this.stopPolling(); this.stopPolling();
} else { } else {
if (!this.pollingInterval) { if (this.pollingIntervals.length === 0) {
this.startPolling(); this.startPolling();
} }
} }
@@ -110,17 +226,24 @@ export class HomepageService extends BaseService<null> {
startPolling(): void { startPolling(): void {
logInfo("Polling started"); logInfo("Polling started");
this.pollingInterval = setInterval(() => {
logInfo("Polling now"); const config: [string[], number][] = [
this.getFullInformation(); [["tidal"], 20_000],
}, 30000); [["desk"], 60_000],
[["temp", "grist"], 600_000],
];
this.pollingIntervals = config.map(([components, interval]) =>
setInterval(() => {
logInfo(`Polling ${components.join(", ")}`);
this.updatePartial(components);
}, interval),
);
} }
stopPolling(): void { stopPolling(): void {
logInfo("Polling ended"); logInfo("Polling ended");
if (this.pollingInterval) { this.pollingIntervals.forEach(clearInterval);
clearInterval(this.pollingInterval); this.pollingIntervals = [];
this.pollingInterval = null;
}
} }
} }