formatting and grist

This commit is contained in:
Darius
2026-02-06 18:59:34 +01:00
parent a8280ce27f
commit e1ef661a7a
9 changed files with 141 additions and 16 deletions

View File

@@ -2,8 +2,7 @@ API_KEY=apiKey
PORT= PORT=
ENV_DEV=true ENV_DEV=true
TIDAL_HOST=http://localhost TIDAL_HOST=http://localhost:47836
TIDAL_PORT=47836
HA_API_URL=http://homeassistant.com/api/states/ HA_API_URL=http://homeassistant.com/api/states/
HA_API_TOKEN=Nina hätte hier jetzt ihr Token ausversehen stehen hihi HA_API_TOKEN=Nina hätte hier jetzt ihr Token ausversehen stehen hihi

10
package-lock.json generated
View File

@@ -28,8 +28,8 @@
} }
}, },
"node_modules/@dpu/shared": { "node_modules/@dpu/shared": {
"version": "1.7.1", "version": "1.8.1",
"resolved": "git+https://git.dariusbag.dev/DarDarBinks/dpu-shared.git#cd95b631746e3f5d50120c68f69b934cf7f8e87b", "resolved": "git+https://git.dariusbag.dev/DarDarBinks/dpu-shared.git#27dc6f2b1214b8e2aff65de510caea402c2f88db",
"dependencies": { "dependencies": {
"@types/ws": "^8.18.1", "@types/ws": "^8.18.1",
"axios": "^1.7.9", "axios": "^1.7.9",
@@ -1383,9 +1383,9 @@
} }
}, },
"node_modules/get-tsconfig": { "node_modules/get-tsconfig": {
"version": "4.13.5", "version": "4.13.6",
"resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.5.tgz", "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.6.tgz",
"integrity": "sha512-v4/4xAEpBRp6SvCkWhnGCaLkJf9IwWzrsygJPxD/+p2/xPE3C5m2fA9FD0Ry9tG+Rqqq3gBzHSl6y1/T9V/tMQ==", "integrity": "sha512-shZT/QMiSHc/YBLxxOkMtgSid5HFoauqCE3/exfsEcwg1WkeqjG+V40yBbBrsD+jW2HDXcs28xOfcbm2jI8Ddw==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {

View File

@@ -6,7 +6,7 @@
"clean": "rimraf dist", "clean": "rimraf dist",
"build": "npm run clean && tsc", "build": "npm run clean && tsc",
"start": "node dist/index.js", "start": "node dist/index.js",
"dev": "tsx watch src/index.ts" "dev": "tsx watch --inspect-brk src/index.ts"
}, },
"keywords": [], "keywords": [],
"author": "Darius", "author": "Darius",

View File

@@ -7,18 +7,24 @@ export const Config = {
port: process.env.PORT || "8080", port: process.env.PORT || "8080",
env_dev: process.env.ENV_DEV || false, env_dev: process.env.ENV_DEV || false,
tidal: { grist: {
host: process.env.TIDAL_HOST || "", api_url: process.env.GRIST_API_URL || "",
port: process.env.TIDAL_PORT || "", api_token: process.env.GRIST_API_TOKEN || "",
table_personal_goals_path: process.env.GRIST_TPG_PATH || "",
}, },
homeassistant: { homeassistant: {
api_url: process.env.HA_API_URL || "", api_url: process.env.HA_API_URL || "",
api_token: process.env.HA_API_TOKEN || "", api_token: process.env.HA_API_TOKEN || "",
id_desk_sensor_binary: process.env.HA_DESK_SENSOR_BINARY || "", id_sensor_desk_binary: process.env.HA_SENSOR_DESK_BINARY || "",
id_room_sensors: process.env.HA_ROOMTEMP_SENSOR_IDS?.split(",") || [], id_sensors_roomtemp: process.env.HA_SENSORS_ROOMTEMP?.split(",") || [],
id_webhook_stand: process.env.HA_STANDING_WEBHOOK || "", id_webhook_stand: process.env.HA_STANDING_WEBHOOK || "",
}, },
tidal: {
address: process.env.TIDAL_ADDRESS || "",
},
} as const; } as const;

15
src/grist/client.ts Normal file
View File

@@ -0,0 +1,15 @@
import { BaseClient } from "@dpu/shared";
import { printNetworkError } from "@dpu/shared/dist/logger.js";
export class GristClient extends BaseClient {
async get<T>(endpoint: string): Promise<T> {
try {
const response = await this.getAxios().get<T>(`${endpoint}`);
return response.data;
} catch (error) {
printNetworkError(error);
throw error;
}
}
}

39
src/grist/routes.ts Normal file
View File

@@ -0,0 +1,39 @@
import type { GristRecord_PersonalGoals } from "@dpu/shared";
import type { FastifyInstance } from "fastify";
import { z } from "zod";
import type { GristService } from "./service";
export async function gristRoutes(
fastify: FastifyInstance,
{
gristService,
}: {
gristService: GristService;
},
) {
fastify.get(
"/grist/today",
{
schema: {
description: "Get goals for today",
tags: ["grist"],
response: {
200: z.custom<GristRecord_PersonalGoals>(),
418: z.object({
error: z.string(),
}),
},
},
},
async (_request, reply) => {
const service_result = await gristService.getToday();
if (!service_result.successful) {
reply.code(418);
return { error: service_result.result };
}
return service_result.result;
},
);
}

53
src/grist/service.ts Normal file
View File

@@ -0,0 +1,53 @@
import {
BaseService,
type GristRecord_PersonalGoals,
logWarning,
type ServiceResult,
} from "@dpu/shared";
import { Config } from "../config.js";
import type { GristClient } from "./client.js";
export class GristService extends BaseService<GristClient> {
async getToday(): Promise<ServiceResult<GristRecord_PersonalGoals | string>> {
try {
const filter_string = encodeURIComponent(
`{ "id": [${this.getTodayAsId()}]}`,
);
const query = `${Config.grist.table_personal_goals_path}?filter=${filter_string}`;
const response =
await this.getClient().get<Record<string, unknown>>(query);
return this.getSuccessfulResult(this.transformToPersonalGoals(response));
} catch {
const error_message = "error getting record from grist";
logWarning(error_message);
return this.getErrorResult(error_message);
}
}
getTodayAsId(date = new Date()) {
const start = new Date(date.getFullYear(), 0, 0);
const diff = date.getTime() - start.getTime();
return Math.floor(diff / 86400000);
}
transformToPersonalGoals(
obj: Record<string, unknown>,
): GristRecord_PersonalGoals {
return {
went_outside: (obj.went_outside as boolean) ?? false,
standing: (obj.standing as boolean) ?? false,
standing_goal: (obj.standing_goal as number) ?? 0,
steps: (obj.steps as boolean) ?? false,
steps_goal: (obj.steps_goal as number) ?? 0,
pushups: (obj.pushups as boolean) ?? false,
squats: (obj.squats as boolean) ?? false,
leg_raises: (obj.leg_raises as boolean) ?? false,
reps_goal: (obj.reps_goal as number) ?? 0,
stairs: (obj.stairs as boolean) ?? false,
stairs_goal: (obj.stairs_goal as number) ?? 0,
is_workday: (obj.WeekdayHelper as number) === 1,
};
}
}

View File

@@ -48,7 +48,7 @@ export class HomeAssistantService extends BaseService<HomeAssistantClient> {
> { > {
try { try {
const raw = await this.getClient().getEntityState( const raw = await this.getClient().getEntityState(
Config.homeassistant.id_desk_sensor_binary, Config.homeassistant.id_sensor_desk_binary,
); );
const position = Number(raw.state); const position = Number(raw.state);
@@ -108,7 +108,7 @@ export class HomeAssistantService extends BaseService<HomeAssistantClient> {
private async getTemperatures(): Promise<HomeAssistantEntity[]> { private async getTemperatures(): Promise<HomeAssistantEntity[]> {
try { try {
return await this.getClient().getEntityStates( return await this.getClient().getEntityStates(
Config.homeassistant.id_room_sensors, Config.homeassistant.id_sensors_roomtemp,
); );
} catch (error) { } catch (error) {
logWarning("error getting temperatures:", error); logWarning("error getting temperatures:", error);

View File

@@ -16,6 +16,9 @@ import {
import { SwaggerTheme, SwaggerThemeNameEnum } from "swagger-themes"; import { SwaggerTheme, SwaggerThemeNameEnum } from "swagger-themes";
import { z } from "zod"; import { z } from "zod";
import { Config } from "./config.js"; import { Config } from "./config.js";
import { GristClient } from "./grist/client.js";
import { gristRoutes } from "./grist/routes.js";
import { GristService } from "./grist/service.js";
import { HomeAssistantClient } from "./homeassistant/client.js"; import { HomeAssistantClient } from "./homeassistant/client.js";
import { homeAssistantRoutes } from "./homeassistant/routes.js"; import { homeAssistantRoutes } from "./homeassistant/routes.js";
import { HomeAssistantService } from "./homeassistant/service.js"; import { HomeAssistantService } from "./homeassistant/service.js";
@@ -69,6 +72,12 @@ await fastify.register(fastifySwaggerUi, {
await fastify.register(fastifyAxios, { await fastify.register(fastifyAxios, {
clients: { clients: {
grist: {
baseURL: Config.grist.api_url,
headers: {
Authorization: `Bearer ${Config.grist.api_token}`,
},
},
homeassistant: { homeassistant: {
baseURL: Config.homeassistant.api_url, baseURL: Config.homeassistant.api_url,
headers: { headers: {
@@ -76,13 +85,16 @@ await fastify.register(fastifyAxios, {
}, },
}, },
tidal: { tidal: {
baseURL: `${Config.tidal.host}:${Config.tidal.port}`, baseURL: `${Config.tidal.address}`,
}, },
}, },
}); });
await fastify.register(fastifyWebsocket); await fastify.register(fastifyWebsocket);
const gristClient = new GristClient(fastify.axios.grist);
const gristService = new GristService(gristClient);
const haClient = new HomeAssistantClient(fastify.axios.homeassistant); const haClient = new HomeAssistantClient(fastify.axios.homeassistant);
const haService = new HomeAssistantService(haClient); const haService = new HomeAssistantService(haClient);
@@ -108,6 +120,7 @@ async function verifyAPIKey(
const port = parseInt(Config.port, 10); const port = parseInt(Config.port, 10);
// Register routes // Register routes
await fastify.register(gristRoutes, { gristService });
await fastify.register(homeAssistantRoutes, { haService, verifyAPIKey }); await fastify.register(homeAssistantRoutes, { haService, verifyAPIKey });
await fastify.register(tidalRoutes, { tidalService, verifyAPIKey }); await fastify.register(tidalRoutes, { tidalService, verifyAPIKey });
await fastify.register(wsRoutes, { wsService }); await fastify.register(wsRoutes, { wsService });