Initial SolarEdge Optimizer Home Assistant App
This commit is contained in:
@@ -0,0 +1,84 @@
|
||||
import { SolarEdgePortalClient } from "@solar-dash/solaredgeapi";
|
||||
|
||||
import { OptimizerDataCache } from "./cache";
|
||||
import { collectOptimizerData } from "./collector";
|
||||
import {
|
||||
DEFAULT_PORT,
|
||||
readAppOptions,
|
||||
resolveHomeAssistantTimeZone,
|
||||
} from "./config";
|
||||
import { createRequestHandler } from "./http";
|
||||
|
||||
export async function main(): Promise<void> {
|
||||
const options = await readAppOptions();
|
||||
const timeZone = await resolveHomeAssistantTimeZone();
|
||||
const portalClient = new SolarEdgePortalClient({
|
||||
username: options.username,
|
||||
password: options.password,
|
||||
siteId: options.siteId,
|
||||
});
|
||||
const cache = new OptimizerDataCache({
|
||||
pollIntervalMs: options.pollIntervalMinutes * 60_000,
|
||||
loadSnapshot: async () => {
|
||||
log("info", `Refreshing optimizer data for site ${options.siteId}`);
|
||||
const snapshot = await collectOptimizerData({
|
||||
reader: portalClient,
|
||||
siteId: options.siteId,
|
||||
timeZone,
|
||||
});
|
||||
log(
|
||||
snapshot.failedOptimizerCount === 0 ? "info" : "warning",
|
||||
`Refresh finished: ${snapshot.successfulOptimizerCount}/${snapshot.optimizerCount} optimizer energy values`,
|
||||
);
|
||||
return snapshot;
|
||||
},
|
||||
});
|
||||
const port = readPort(process.env.PORT);
|
||||
const server = Bun.serve({
|
||||
hostname: "0.0.0.0",
|
||||
port,
|
||||
fetch: createRequestHandler(cache),
|
||||
});
|
||||
|
||||
const shutDown = (): void => {
|
||||
log("info", "Stopping SolarEdge Optimizer Data App");
|
||||
cache.stop();
|
||||
void server.stop(true);
|
||||
};
|
||||
process.once("SIGTERM", shutDown);
|
||||
process.once("SIGINT", shutDown);
|
||||
|
||||
log(
|
||||
"info",
|
||||
`Listening on port ${port}; refresh interval ${options.pollIntervalMinutes} minutes; time zone ${timeZone}`,
|
||||
);
|
||||
cache.start();
|
||||
}
|
||||
|
||||
function readPort(value: string | undefined): number {
|
||||
const port = value === undefined ? DEFAULT_PORT : Number(value);
|
||||
if (!Number.isInteger(port) || port < 1 || port > 65_535) {
|
||||
throw new Error("PORT must be an integer from 1 to 65535");
|
||||
}
|
||||
return port;
|
||||
}
|
||||
|
||||
function log(level: "info" | "warning" | "error", message: string): void {
|
||||
console.log(`${new Date().toISOString()} [${level}] ${message}`);
|
||||
}
|
||||
|
||||
if (import.meta.main) {
|
||||
try {
|
||||
await main();
|
||||
} catch (error) {
|
||||
log("error", formatStartupError(error));
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
function formatStartupError(error: unknown): string {
|
||||
if (error instanceof Error) {
|
||||
return `${error.name}: ${error.message}`;
|
||||
}
|
||||
return "Unknown startup error";
|
||||
}
|
||||
Reference in New Issue
Block a user