Add Home Assistant integration discovery
This commit is contained in:
@@ -19,6 +19,11 @@ Voraussetzung ist Home Assistant OS.
|
||||
5. Hinterlege im Tab **Konfiguration** Benutzername, Passwort und Site-ID.
|
||||
6. Speichere die Konfiguration und starte die App.
|
||||
|
||||
Ab App-Version 0.2.0 meldet sich die App automatisch bei Home Assistant. Mit
|
||||
der Custom Integration aus
|
||||
`https://git.jensneuber.de/jens/ha-solaredge-optimizers` werden daraus Geräte
|
||||
und Sensoren für Tagesenergie und aktuelle Leistung jedes Optimierers.
|
||||
|
||||
## SolarEdge Optimizer Data
|
||||
|
||||
Die App aktualisiert standardmäßig alle zehn Minuten und liefert für jedes
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
"workspaces": {
|
||||
"": {
|
||||
"name": "solaredge-optimizers",
|
||||
"version": "0.2.0",
|
||||
},
|
||||
"packages/solaredgeapi": {
|
||||
"name": "@solar-dash/solaredgeapi",
|
||||
@@ -19,7 +20,7 @@
|
||||
},
|
||||
"solaredge-optimizer-data": {
|
||||
"name": "@solar-dash/solaredge-optimizer-app",
|
||||
"version": "0.1.0",
|
||||
"version": "0.2.0",
|
||||
"dependencies": {
|
||||
"@solar-dash/solaredgeapi": "workspace:*",
|
||||
},
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "solaredge-optimizers",
|
||||
"version": "0.1.0",
|
||||
"version": "0.2.0",
|
||||
"private": true,
|
||||
"packageManager": "bun@1.3.14",
|
||||
"workspaces": [
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# Changelog
|
||||
|
||||
## 0.2.0
|
||||
|
||||
- Register the internal optimizer API through Home Assistant Supervisor
|
||||
discovery.
|
||||
- Enable automatic setup of the SolarEdge Optimizers custom integration.
|
||||
|
||||
## 0.1.0
|
||||
|
||||
- Initial Home Assistant App package.
|
||||
|
||||
@@ -83,9 +83,18 @@ im App-Panel erreichbar.
|
||||
## Zugriff aus Home Assistant
|
||||
|
||||
Die App veröffentlicht standardmäßig keinen ungeschützten Port im lokalen
|
||||
Netz. Home Assistant und andere Apps können sie über das interne App-Netzwerk
|
||||
erreichen. Bei einer lokalen Installation lautet der Hostname typischerweise
|
||||
`local-solaredge-optimizer-data`:
|
||||
Netz. Ab Version 0.2.0 meldet sie ihren internen Host und Port automatisch über
|
||||
die Supervisor-Diensterkennung. Dadurch kann die
|
||||
[SolarEdge Optimizers Integration](https://git.jensneuber.de/jens/ha-solaredge-optimizers)
|
||||
ohne manuelle interne URL eingerichtet werden.
|
||||
|
||||
Nach Installation und Start der App erscheint unter **Einstellungen → Geräte &
|
||||
Dienste** die gefundene Integration **SolarEdge Optimizers**. Installiere die
|
||||
Custom Integration vorher über HACS und bestätige anschließend den Fund.
|
||||
|
||||
Home Assistant und andere Apps können die API außerdem direkt über das interne
|
||||
App-Netzwerk erreichen. Bei einer lokalen Installation lautet der Hostname
|
||||
typischerweise `local-solaredge-optimizer-data`:
|
||||
|
||||
```text
|
||||
http://local-solaredge-optimizer-data:8099/api/optimizers
|
||||
@@ -119,7 +128,7 @@ für eine lokale Installation nach:
|
||||
Lade anschließend den App Store neu, installiere **SolarEdge Optimizer Data**,
|
||||
pflege Benutzername, Passwort und Site-ID im Tab **Konfiguration** und starte
|
||||
die App. Der Supervisor lädt dabei das zur App-Version passende Multi-Arch-Image
|
||||
`git.jensneuber.de/jens/solaredgeoptimizers:0.1.0`.
|
||||
`git.jensneuber.de/jens/solaredgeoptimizers:0.2.0`.
|
||||
|
||||
## Sicherheit und Betrieb
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: "SolarEdge Optimizer Data"
|
||||
version: "0.1.0"
|
||||
version: "0.2.0"
|
||||
slug: "solaredge_optimizer_data"
|
||||
description: >-
|
||||
Cached daily energy and current power data for every SolarEdge optimizer.
|
||||
@@ -7,6 +7,9 @@ arch:
|
||||
- aarch64
|
||||
- amd64
|
||||
image: git.jensneuber.de/jens/solaredgeoptimizers
|
||||
discovery:
|
||||
- solaredge_optimizers
|
||||
hassio_api: true
|
||||
startup: application
|
||||
boot: auto
|
||||
init: false
|
||||
|
||||
+14
-14
File diff suppressed because one or more lines are too long
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@solar-dash/solaredge-optimizer-app",
|
||||
"version": "0.1.0",
|
||||
"version": "0.2.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
import { describe, expect, test } from "bun:test";
|
||||
|
||||
import { registerHomeAssistantDiscovery } from "./discovery";
|
||||
|
||||
describe("registerHomeAssistantDiscovery", () => {
|
||||
test("registers the internal App endpoint with Supervisor", async () => {
|
||||
let requestUrl: string | undefined;
|
||||
let requestInit: RequestInit | undefined;
|
||||
const fetchImpl = (async (
|
||||
input: string | URL | Request,
|
||||
init?: RequestInit,
|
||||
) => {
|
||||
requestUrl = String(input);
|
||||
requestInit = init;
|
||||
return Response.json({ result: "ok", data: { uuid: "test-uuid" } });
|
||||
}) as typeof fetch;
|
||||
|
||||
await registerHomeAssistantDiscovery({
|
||||
port: 8099,
|
||||
host: "abc-solaredge-optimizer-data",
|
||||
supervisorToken: "secret-token",
|
||||
fetchImpl,
|
||||
});
|
||||
|
||||
expect(requestUrl).toBe("http://supervisor/discovery");
|
||||
expect(requestInit?.method).toBe("POST");
|
||||
expect(requestInit?.headers).toEqual({
|
||||
Accept: "application/json",
|
||||
Authorization: "Bearer secret-token",
|
||||
"Content-Type": "application/json",
|
||||
});
|
||||
expect(JSON.parse(String(requestInit?.body))).toEqual({
|
||||
service: "solaredge_optimizers",
|
||||
config: { host: "abc-solaredge-optimizer-data", port: 8099 },
|
||||
});
|
||||
});
|
||||
|
||||
test("does not start without a Supervisor token", async () => {
|
||||
await expect(
|
||||
registerHomeAssistantDiscovery({
|
||||
port: 8099,
|
||||
host: "abc-solaredge-optimizer-data",
|
||||
supervisorToken: "",
|
||||
}),
|
||||
).rejects.toThrow("Supervisor token is unavailable");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
import { hostname as systemHostname } from "node:os";
|
||||
|
||||
const DISCOVERY_SERVICE = "solaredge_optimizers";
|
||||
const SUPERVISOR_DISCOVERY_URL = "http://supervisor/discovery";
|
||||
|
||||
export async function registerHomeAssistantDiscovery({
|
||||
port,
|
||||
host = process.env.HOSTNAME || systemHostname(),
|
||||
supervisorToken = process.env.SUPERVISOR_TOKEN,
|
||||
fetchImpl = fetch,
|
||||
}: {
|
||||
port: number;
|
||||
host?: string;
|
||||
supervisorToken?: string;
|
||||
fetchImpl?: typeof fetch;
|
||||
}): Promise<void> {
|
||||
if (!host) {
|
||||
throw new Error("App hostname is unavailable");
|
||||
}
|
||||
if (!supervisorToken) {
|
||||
throw new Error("Supervisor token is unavailable");
|
||||
}
|
||||
|
||||
const response = await fetchImpl(SUPERVISOR_DISCOVERY_URL, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
Accept: "application/json",
|
||||
Authorization: `Bearer ${supervisorToken}`,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
service: DISCOVERY_SERVICE,
|
||||
config: { host, port },
|
||||
}),
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw new Error(`Supervisor discovery failed with HTTP ${response.status}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
readAppOptions,
|
||||
resolveHomeAssistantTimeZone,
|
||||
} from "./config";
|
||||
import { registerHomeAssistantDiscovery } from "./discovery";
|
||||
import { createRequestHandler } from "./http";
|
||||
|
||||
export async function main(): Promise<void> {
|
||||
@@ -52,6 +53,15 @@ export async function main(): Promise<void> {
|
||||
"info",
|
||||
`Listening on port ${port}; refresh interval ${options.pollIntervalMinutes} minutes; time zone ${timeZone}`,
|
||||
);
|
||||
try {
|
||||
await registerHomeAssistantDiscovery({ port });
|
||||
log("info", "Registered optimizer API discovery with Home Assistant");
|
||||
} catch (error) {
|
||||
log(
|
||||
"warning",
|
||||
`Home Assistant discovery registration failed: ${formatStartupError(error)}`,
|
||||
);
|
||||
}
|
||||
cache.start();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user