88 lines
2.6 KiB
TypeScript
88 lines
2.6 KiB
TypeScript
import type { DeviceConfig, DeviceStatus } from "../types.js";
|
|
import type { HomeAssistantClient } from "./client.js";
|
|
|
|
function normalizeHaValue(kind: DeviceConfig["kind"], state: string): number | null {
|
|
if (kind === "switch") {
|
|
if (state === "on") return 1;
|
|
if (state === "off") return 0;
|
|
return null;
|
|
}
|
|
const n = Number(state);
|
|
return Number.isFinite(n) ? n : null;
|
|
}
|
|
|
|
/** True when both directions are driven by custom endpoints rather than Home Assistant. */
|
|
function isExternallyControlled(device: DeviceConfig): boolean {
|
|
return device.enableUrl !== null && device.disableUrl !== null;
|
|
}
|
|
|
|
async function callEndpoint(url: string): Promise<void> {
|
|
const res = await fetch(url, { method: "POST" });
|
|
if (!res.ok) {
|
|
throw new Error(`custom endpoint request failed: POST ${url} -> ${res.status} ${await res.text()}`);
|
|
}
|
|
}
|
|
|
|
export async function getDeviceStatus(
|
|
ha: HomeAssistantClient,
|
|
device: DeviceConfig,
|
|
shed: boolean,
|
|
lastChangedAt: string | null,
|
|
): Promise<DeviceStatus> {
|
|
// Externally controlled devices have no Home Assistant entity to poll; state is
|
|
// whatever we last commanded via the custom endpoints.
|
|
if (isExternallyControlled(device)) {
|
|
return {
|
|
...device,
|
|
currentValue: shed ? (device.kind === "switch" ? 0 : device.minValue) : device.kind === "switch" ? 1 : device.maxValue,
|
|
shed,
|
|
lastChangedAt,
|
|
available: true,
|
|
};
|
|
}
|
|
try {
|
|
const haState = await ha.getState(device.entityId);
|
|
return {
|
|
...device,
|
|
currentValue: normalizeHaValue(device.kind, haState.state),
|
|
shed,
|
|
lastChangedAt,
|
|
available: haState.state !== "unavailable" && haState.state !== "unknown",
|
|
};
|
|
} catch {
|
|
return {
|
|
...device,
|
|
currentValue: null,
|
|
shed,
|
|
lastChangedAt,
|
|
available: false,
|
|
};
|
|
}
|
|
}
|
|
|
|
/** Sheds (reduces load of) a device: turns a switch off, or lowers a number to its floor. */
|
|
export async function shedDevice(ha: HomeAssistantClient, device: DeviceConfig): Promise<void> {
|
|
if (device.disableUrl) {
|
|
await callEndpoint(device.disableUrl);
|
|
return;
|
|
}
|
|
if (device.kind === "switch") {
|
|
await ha.setSwitch(device.entityId, false);
|
|
} else {
|
|
await ha.setNumber(device.entityId, device.minValue);
|
|
}
|
|
}
|
|
|
|
/** Restores a device to its normal operating value. */
|
|
export async function restoreDevice(ha: HomeAssistantClient, device: DeviceConfig): Promise<void> {
|
|
if (device.enableUrl) {
|
|
await callEndpoint(device.enableUrl);
|
|
return;
|
|
}
|
|
if (device.kind === "switch") {
|
|
await ha.setSwitch(device.entityId, true);
|
|
} else {
|
|
await ha.setNumber(device.entityId, device.maxValue);
|
|
}
|
|
}
|