even cooler now

This commit is contained in:
Darius
2026-02-09 20:02:47 +01:00
parent ff66061c32
commit c98304fe8e
6 changed files with 80 additions and 116 deletions

10
package-lock.json generated
View File

@@ -31,8 +31,8 @@
}
},
"node_modules/@dpu/shared": {
"version": "1.9.6",
"resolved": "git+https://git.dariusbag.dev/DarDarBinks/dpu-shared.git#872d3755d06c8e8c6452d8ab7212b6426d84943b",
"version": "1.9.9",
"resolved": "git+https://git.dariusbag.dev/DarDarBinks/dpu-shared.git#d73d98068a779569e3be375f077de1e63f9efa9c",
"dependencies": {
"@types/ws": "^8.18.1",
"axios": "^1.7.9",
@@ -1725,9 +1725,9 @@
}
},
"node_modules/pino": {
"version": "10.3.0",
"resolved": "https://registry.npmjs.org/pino/-/pino-10.3.0.tgz",
"integrity": "sha512-0GNPNzHXBKw6U/InGe79A3Crzyk9bcSyObF9/Gfo9DLEf5qj5RF50RSjsu0W1rZ6ZqRGdzDFCRBQvi9/rSGPtA==",
"version": "10.3.1",
"resolved": "https://registry.npmjs.org/pino/-/pino-10.3.1.tgz",
"integrity": "sha512-r34yH/GlQpKZbU1BvFFqOjhISRo1MNx1tWYsYvmj6KIRHSPMT2+yHOEb1SG6NMvRoHRF0a07kCOox/9yakl1vg==",
"license": "MIT",
"dependencies": {
"@pinojs/redact": "^0.4.0",

View File

@@ -1,9 +1,10 @@
import type {
ComponentUpdate,
GristRecord_PersonalGoals,
HA_Update,
HomeAssistantEntity,
TidalGetCurrent,
import {
StatusComponent,
type ComponentUpdate,
type GristRecord_PersonalGoals,
type HA_Update,
type HomeAssistantEntity,
type TidalGetCurrent,
} from "@dpu/shared";
import type { FastifyInstance, FastifyReply } from "fastify";
import type { FastifyRequest } from "fastify/types/request.js";
@@ -55,7 +56,7 @@ export async function privateHomepageRoutes(
}
const service_result = await hpService.updatePartial([
{
component: "grist",
component: StatusComponent.GRIST_PERSONAL_GOALS,
data: gristService.transformToPersonalGoals(record),
},
]);
@@ -99,7 +100,7 @@ export async function privateHomepageRoutes(
switch (ha_update.entity_id) {
case Config.homeassistant.id_sensor_desk_binary: {
updates.push({
component: "desk",
component: StatusComponent.HA_DESK_POSITION,
data: haService.convertPosResultToApiAnswer(
haService.convertHaEntityToPosResult(ha_entity),
),
@@ -108,7 +109,7 @@ export async function privateHomepageRoutes(
}
case Config.homeassistant.id_sensor_roomtemp:
updates.push({
component: "desk",
component: StatusComponent.HA_TEMP,
data: ha_entity.state,
});
break;
@@ -147,7 +148,7 @@ export async function privateHomepageRoutes(
const update = request.body as TidalGetCurrent;
const service_result = await hpService.updatePartial([
{
component: "tidal",
component: StatusComponent.TIDAL_CURRENT,
data: update,
},
]);

View File

@@ -1,50 +0,0 @@
import type {
API_HA_DeskPosition,
GristRecord_PersonalGoals,
TidalGetCurrent,
} from "@dpu/shared";
import type { FastifyInstance } from "fastify";
import { z } from "zod";
import type { HomepageService } from "./service.js";
export async function publicHomepageRoutes(
fastify: FastifyInstance,
{
hpService,
}: {
hpService: HomepageService;
},
) {
fastify.get(
"/homepage/status",
{
schema: {
description: "Get all current information for dpu status page",
tags: ["homepage"],
response: {
200: z.object({
ha_desk_position: z.custom<API_HA_DeskPosition>().nullable(),
ha_temp: z.string().nullable(),
tidal_current: z.custom<TidalGetCurrent>().nullable(),
grist_personal_goals: z
.custom<GristRecord_PersonalGoals>()
.nullable(),
}),
418: z.object({
error: z.string(),
}),
},
},
},
async (_request, reply) => {
const service_result = await hpService.getFullInformation();
if (!service_result.successful) {
reply.code(418);
return { error: service_result.result };
}
return service_result.result;
},
);
}

View File

@@ -1,18 +1,21 @@
import {
type API_HA_DeskPosition,
BaseService,
type ComponentUpdate,
type FullInformation,
type GristRecord_PersonalGoals,
type HomeAssistantDeskPositionResult,
type ServiceResult,
type TidalGetCurrent,
type WsService,
type API_HA_DeskPosition,
BaseService,
type ComponentUpdate,
type FullInformation,
type GristRecord_PersonalGoals,
type HomeAssistantDeskPositionResult,
type ServiceResult,
type TidalGetCurrent,
type WsService,
createComponentUpdate,
StatusComponent,
} from "@dpu/shared";
import { logInfo } 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 { TidalService } from "../tidal/service";
import type { WebSocket } from "ws";
export class HomepageService extends BaseService<null> {
private gristService: GristService;
@@ -39,21 +42,34 @@ export class HomepageService extends BaseService<null> {
this.wsService = wsService;
}
async sendFullInformationToSocket(socket: WebSocket): Promise<void> {
try {
const [desk, temp, tidal, personal_goals] = await this._getAll();
const updates: ComponentUpdate[] = [
createComponentUpdate(StatusComponent.HA_DESK_POSITION, desk),
createComponentUpdate(StatusComponent.HA_TEMP, temp),
createComponentUpdate(StatusComponent.TIDAL_CURRENT, tidal),
createComponentUpdate(StatusComponent.GRIST_PERSONAL_GOALS, personal_goals)
];
socket.send(JSON.stringify({
type: "update",
data: updates
}))
} catch (error) {
logWarning("error getting all information for socket update.", error);
}
}
async getFullInformation(): Promise<ServiceResult<FullInformation | string>> {
try {
const [desk, temp, tidal, personal_goals] = await Promise.all([
this._getDesk(),
this._getTemp(),
this._getTidal(),
this._getGristPG(),
]);
const [desk, temp, tidal, personal_goals] = await this._getAll();
return this.getSuccessfulResult(
this.updateFull({
ha_desk_position: desk,
ha_temp: temp,
tidal_current: tidal,
grist_personal_goals: personal_goals,
ha_desk_position: desk as API_HA_DeskPosition,
ha_temp: temp as string,
tidal_current: tidal as TidalGetCurrent,
grist_personal_goals: personal_goals as GristRecord_PersonalGoals,
}),
);
} catch (error) {
@@ -61,6 +77,15 @@ export class HomepageService extends BaseService<null> {
}
}
private async _getAll(): Promise<Iterable<unknown>> {
return await Promise.all([
this._getDesk(),
this._getTemp(),
this._getTidal(),
this._getGristPG(),
]);
}
private async _getDesk(): Promise<API_HA_DeskPosition | null> {
const desk = await this.haService.getDeskPosition();
return desk.successful
@@ -112,26 +137,26 @@ export class HomepageService extends BaseService<null> {
const updates: ComponentUpdate[] = [];
for (const component of components) {
switch (component.component) {
case "desk": {
case StatusComponent.HA_DESK_POSITION: {
this.updateHaDesk(
(component.data as API_HA_DeskPosition) ?? (await this._getDesk()),
updates,
);
break;
}
case "temp":
case StatusComponent.HA_TEMP:
this.updateHaTemp(
(component.data as string) ?? (await this._getTemp()),
updates,
);
break;
case "tidal":
case StatusComponent.TIDAL_CURRENT:
this.updateTidal(
(component.data as TidalGetCurrent) ?? (await this._getTidal()),
updates,
);
break;
case "grist":
case StatusComponent.GRIST_PERSONAL_GOALS:
this.updateGristPG(
(component.data as GristRecord_PersonalGoals) ??
(await this._getGristPG()),
@@ -159,20 +184,14 @@ export class HomepageService extends BaseService<null> {
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,
});
updates.push(createComponentUpdate(StatusComponent.HA_DESK_POSITION, new_ha_desk_position));
}
}
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,
});
updates.push(createComponentUpdate(StatusComponent.HA_TEMP, new_ha_temp));
}
}
@@ -187,10 +206,7 @@ export class HomepageService extends BaseService<null> {
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,
});
updates.push(createComponentUpdate(StatusComponent.TIDAL_CURRENT, new_tidal_current));
}
}
@@ -215,10 +231,7 @@ export class HomepageService extends BaseService<null> {
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,
});
updates.push(createComponentUpdate(StatusComponent.GRIST_PERSONAL_GOALS, new_grist_personal_goals));
}
}
}

View File

@@ -3,17 +3,17 @@ import { logError } from "@dpu/shared/dist/logger.js";
import fastifyCors, { type FastifyCorsOptions } from "@fastify/cors";
import fastifySwagger, { type SwaggerOptions } from "@fastify/swagger";
import fastifySwaggerUi, {
type FastifySwaggerUiOptions,
type FastifySwaggerUiOptions,
} from "@fastify/swagger-ui";
import fastifyWebsocket from "@fastify/websocket";
import type { FastifyReply, FastifyRequest } from "fastify";
import Fastify from "fastify";
import fastifyAxios, { type FastifyAxiosOptions } from "fastify-axios";
import {
jsonSchemaTransform,
serializerCompiler,
validatorCompiler,
type ZodTypeProvider,
jsonSchemaTransform,
serializerCompiler,
validatorCompiler,
type ZodTypeProvider,
} from "fastify-type-provider-zod";
import type { OpenAPIV3 } from "openapi-types";
import { SwaggerTheme, SwaggerThemeNameEnum } from "swagger-themes";
@@ -26,7 +26,6 @@ import { HomeAssistantClient } from "./homeassistant/client.js";
import { privateHomeAssistantRoutes } from "./homeassistant/private-routes.js";
import { HomeAssistantService } from "./homeassistant/service.js";
import { privateHomepageRoutes } from "./homepage/private-routes.js";
import { publicHomepageRoutes } from "./homepage/public-routes.js";
import { HomepageService } from "./homepage/service.js";
import { TidalClient } from "./tidal/client.js";
import { privateTidalRoutes } from "./tidal/private-routes.js";
@@ -168,10 +167,7 @@ async function verifyAPIKey(
}
// Register routes
await publicServer.register(publicWsRoutes, { wsService });
await publicServer.register(publicHomepageRoutes, {
hpService,
});
await publicServer.register(publicWsRoutes, { wsService, hpService });
await privateServer.register(privateGristRoutes, { gristService });
await privateServer.register(privateHomeAssistantRoutes, {

View File

@@ -1,16 +1,19 @@
import { logInfo, type WsService } from "@dpu/shared";
import type { FastifyInstance } from "fastify";
import type { HomepageService } from "../homepage/service";
export async function publicWsRoutes(
fastify: FastifyInstance,
{
wsService,
hpService,
}: {
wsService: WsService;
hpService: HomepageService;
},
) {
fastify.get(
"/dpu/events",
"/dpu/status/events",
{
schema: {
description: "Register for WebSocket events",
@@ -22,6 +25,7 @@ export async function publicWsRoutes(
(socket, _request) => {
wsService.addClient(socket);
socket.send(JSON.stringify({ type: "message", data: "Connected" }));
hpService.sendFullInformationToSocket(socket)
logInfo(`Connection for client established`);
socket.on("close", () => {