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.
|
5. Hinterlege im Tab **Konfiguration** Benutzername, Passwort und Site-ID.
|
||||||
6. Speichere die Konfiguration und starte die App.
|
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
|
## SolarEdge Optimizer Data
|
||||||
|
|
||||||
Die App aktualisiert standardmäßig alle zehn Minuten und liefert für jedes
|
Die App aktualisiert standardmäßig alle zehn Minuten und liefert für jedes
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
"workspaces": {
|
"workspaces": {
|
||||||
"": {
|
"": {
|
||||||
"name": "solaredge-optimizers",
|
"name": "solaredge-optimizers",
|
||||||
|
"version": "0.2.0",
|
||||||
},
|
},
|
||||||
"packages/solaredgeapi": {
|
"packages/solaredgeapi": {
|
||||||
"name": "@solar-dash/solaredgeapi",
|
"name": "@solar-dash/solaredgeapi",
|
||||||
@@ -19,7 +20,7 @@
|
|||||||
},
|
},
|
||||||
"solaredge-optimizer-data": {
|
"solaredge-optimizer-data": {
|
||||||
"name": "@solar-dash/solaredge-optimizer-app",
|
"name": "@solar-dash/solaredge-optimizer-app",
|
||||||
"version": "0.1.0",
|
"version": "0.2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@solar-dash/solaredgeapi": "workspace:*",
|
"@solar-dash/solaredgeapi": "workspace:*",
|
||||||
},
|
},
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "solaredge-optimizers",
|
"name": "solaredge-optimizers",
|
||||||
"version": "0.1.0",
|
"version": "0.2.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"packageManager": "bun@1.3.14",
|
"packageManager": "bun@1.3.14",
|
||||||
"workspaces": [
|
"workspaces": [
|
||||||
|
|||||||
@@ -1,5 +1,11 @@
|
|||||||
# Changelog
|
# 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
|
## 0.1.0
|
||||||
|
|
||||||
- Initial Home Assistant App package.
|
- Initial Home Assistant App package.
|
||||||
|
|||||||
@@ -83,9 +83,18 @@ im App-Panel erreichbar.
|
|||||||
## Zugriff aus Home Assistant
|
## Zugriff aus Home Assistant
|
||||||
|
|
||||||
Die App veröffentlicht standardmäßig keinen ungeschützten Port im lokalen
|
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
|
Netz. Ab Version 0.2.0 meldet sie ihren internen Host und Port automatisch über
|
||||||
erreichen. Bei einer lokalen Installation lautet der Hostname typischerweise
|
die Supervisor-Diensterkennung. Dadurch kann die
|
||||||
`local-solaredge-optimizer-data`:
|
[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
|
```text
|
||||||
http://local-solaredge-optimizer-data:8099/api/optimizers
|
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**,
|
Lade anschließend den App Store neu, installiere **SolarEdge Optimizer Data**,
|
||||||
pflege Benutzername, Passwort und Site-ID im Tab **Konfiguration** und starte
|
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
|
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
|
## Sicherheit und Betrieb
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
name: "SolarEdge Optimizer Data"
|
name: "SolarEdge Optimizer Data"
|
||||||
version: "0.1.0"
|
version: "0.2.0"
|
||||||
slug: "solaredge_optimizer_data"
|
slug: "solaredge_optimizer_data"
|
||||||
description: >-
|
description: >-
|
||||||
Cached daily energy and current power data for every SolarEdge optimizer.
|
Cached daily energy and current power data for every SolarEdge optimizer.
|
||||||
@@ -7,6 +7,9 @@ arch:
|
|||||||
- aarch64
|
- aarch64
|
||||||
- amd64
|
- amd64
|
||||||
image: git.jensneuber.de/jens/solaredgeoptimizers
|
image: git.jensneuber.de/jens/solaredgeoptimizers
|
||||||
|
discovery:
|
||||||
|
- solaredge_optimizers
|
||||||
|
hassio_api: true
|
||||||
startup: application
|
startup: application
|
||||||
boot: auto
|
boot: auto
|
||||||
init: false
|
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",
|
"name": "@solar-dash/solaredge-optimizer-app",
|
||||||
"version": "0.1.0",
|
"version": "0.2.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"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,
|
readAppOptions,
|
||||||
resolveHomeAssistantTimeZone,
|
resolveHomeAssistantTimeZone,
|
||||||
} from "./config";
|
} from "./config";
|
||||||
|
import { registerHomeAssistantDiscovery } from "./discovery";
|
||||||
import { createRequestHandler } from "./http";
|
import { createRequestHandler } from "./http";
|
||||||
|
|
||||||
export async function main(): Promise<void> {
|
export async function main(): Promise<void> {
|
||||||
@@ -52,6 +53,15 @@ export async function main(): Promise<void> {
|
|||||||
"info",
|
"info",
|
||||||
`Listening on port ${port}; refresh interval ${options.pollIntervalMinutes} minutes; time zone ${timeZone}`,
|
`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();
|
cache.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user