import { API_HA_DeskPosition, BaseService, type HomeAssistantDeskPositionResult, type HomeAssistantEntity, type ServiceResult, } from "@dpu/shared"; import { logWarning } from "@dpu/shared/dist/logger.js"; import { calculateSecondsBetween } from "@dpu/shared/dist/timehelper.js"; import { Config } from "../config.js"; import type { HomeAssistantClient } from "./client.js"; export class HomeAssistantService extends BaseService { async startStandingAutomation(): Promise> { try { const positionResult = await this.getDeskPosition(); if (!positionResult.successful) { throw Error(positionResult.result as string); } const position = positionResult.result as HomeAssistantDeskPositionResult; if (position.as_boolean) { throw Error( `desk is already in standing position and has been for ${position.last_changed.toReadable(true)}`, ); } if (position.last_changed.seconds < 300) { throw Error("desk has moved too recently"); } const result = await this.getClient().triggerWebhook( Config.homeassistant.id_webhook_stand, ); return this.getSuccessfulResult(result); } catch (error) { const error_message = `error starting stand automation. ${error instanceof Error ? error.message : error}`; logWarning(error_message); return this.getErrorResult(error_message); } } async getDeskPosition(): Promise< ServiceResult > { try { const raw = await this.getClient().getEntityState( Config.homeassistant.id_desk_sensor_binary, ); const position = Number(raw.state); const result = { raw, as_boolean: position === 1, as_text: () => { if (position === 1) return "standing"; if (position === 0) return "sitting"; return "unknown"; }, last_changed: calculateSecondsBetween( new Date(raw.last_changed).getTime(), Date.now(), ), }; return this.getSuccessfulResult(result); } catch (error) { const error_message = "error getting desk position"; logWarning(error_message, error); return this.getErrorResult(error_message); } } convertPosResultToApiAnswer(position: HomeAssistantDeskPositionResult): API_HA_DeskPosition { return { position: position.as_text(), is_standing: position.as_boolean, last_changed: position.last_changed.toReadable(true), } } async getTemperatureText(): Promise> { try { const entities = await this.getTemperatures(); const values = entities .map((entity) => parseFloat(entity.state)) .filter((value) => !Number.isNaN(value)); const average = values.length > 0 ? values.reduce((sum, value) => sum + value, 0) / values.length : 0; const result = average.toFixed(2); return this.getSuccessfulResult(result); } catch (error) { const error_message = "error getting temperature as text"; logWarning(error_message, error); return this.getErrorResult(error_message); } } private async getTemperatures(): Promise { try { return await this.getClient().getEntityStates( Config.homeassistant.id_room_sensors, ); } catch (error) { logWarning("error getting temperatures:", error); return []; } } }