homebridge-foxess 1.2.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/build.yml +9 -7
- package/.github/workflows/npm-publish.yml +3 -37
- package/dist/accessories/inverterAccessory.js.map +1 -1
- package/dist/foxess-api.js +57 -0
- package/dist/foxess-api.js.map +1 -0
- package/dist/platform.js +13 -13
- package/dist/platform.js.map +1 -1
- package/package.json +8 -11
- package/renovate.json +20 -0
- package/src/accessories/inverterAccessory.ts +4 -4
- package/src/foxess-api.ts +121 -0
- package/src/platform.ts +16 -16
- package/.github/dependabot.yml +0 -22
- package/.github/workflows/automerge.yml +0 -15
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
# This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node
|
|
2
|
-
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
|
|
3
|
-
|
|
4
1
|
name: Build
|
|
5
2
|
permissions:
|
|
6
3
|
contents: read
|
|
7
4
|
|
|
8
|
-
on:
|
|
5
|
+
on:
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
push:
|
|
9
|
+
branches: [main]
|
|
9
10
|
|
|
10
11
|
jobs:
|
|
11
12
|
|
|
@@ -15,16 +16,17 @@ jobs:
|
|
|
15
16
|
|
|
16
17
|
strategy:
|
|
17
18
|
matrix:
|
|
18
|
-
node-version: [
|
|
19
|
+
node-version: [20.x, 22.x]
|
|
19
20
|
|
|
20
21
|
steps:
|
|
21
|
-
- uses: actions/checkout@
|
|
22
|
+
- uses: actions/checkout@v6
|
|
22
23
|
- uses: pnpm/action-setup@v4
|
|
23
24
|
- name: Use Node.js ${{ matrix.node-version }}
|
|
24
|
-
uses: actions/setup-node@
|
|
25
|
+
uses: actions/setup-node@v6.2.0
|
|
25
26
|
with:
|
|
26
27
|
node-version: ${{ matrix.node-version }}
|
|
27
28
|
cache: pnpm
|
|
28
29
|
- run: pnpm install --frozen-lockfile
|
|
30
|
+
- run: pnpm audit --audit-level=moderate
|
|
29
31
|
- run: pnpm lint
|
|
30
32
|
- run: pnpm build
|
|
@@ -13,9 +13,9 @@ jobs:
|
|
|
13
13
|
permissions:
|
|
14
14
|
contents: read
|
|
15
15
|
steps:
|
|
16
|
-
- uses: actions/checkout@
|
|
17
|
-
- uses: pnpm/action-setup@
|
|
18
|
-
- uses: actions/setup-node@
|
|
16
|
+
- uses: actions/checkout@v6
|
|
17
|
+
- uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda
|
|
18
|
+
- uses: actions/setup-node@v6.2.0
|
|
19
19
|
with:
|
|
20
20
|
node-version: latest
|
|
21
21
|
cache: pnpm
|
|
@@ -25,37 +25,3 @@ jobs:
|
|
|
25
25
|
- run: pnpm publish --no-git-checks
|
|
26
26
|
env:
|
|
27
27
|
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
28
|
-
|
|
29
|
-
update-build-number:
|
|
30
|
-
needs: publish
|
|
31
|
-
runs-on: ubuntu-latest
|
|
32
|
-
permissions:
|
|
33
|
-
pull-requests: write
|
|
34
|
-
contents: write
|
|
35
|
-
steps:
|
|
36
|
-
- uses: actions/checkout@v5
|
|
37
|
-
with:
|
|
38
|
-
ref: main
|
|
39
|
-
fetch-tags: true
|
|
40
|
-
- uses: pnpm/action-setup@v4
|
|
41
|
-
- uses: actions/setup-node@v5.0.0
|
|
42
|
-
with:
|
|
43
|
-
node-version: latest
|
|
44
|
-
cache: pnpm
|
|
45
|
-
registry-url: https://registry.npmjs.org/
|
|
46
|
-
- run: pnpm version patch --no-git-tag-version
|
|
47
|
-
- name: get-npm-version
|
|
48
|
-
id: version-after
|
|
49
|
-
uses: martinbeentjes/npm-get-version-action@v1.3.1
|
|
50
|
-
- name: Create Pull Request
|
|
51
|
-
uses: peter-evans/create-pull-request@v7
|
|
52
|
-
with:
|
|
53
|
-
add-paths: |
|
|
54
|
-
package.json
|
|
55
|
-
delete-branch: true
|
|
56
|
-
title: Bump to version ${{ steps.version-after.outputs.current-version }}
|
|
57
|
-
commit-message: |
|
|
58
|
-
Bump to version ${{ steps.version-after.outputs.current-version }}
|
|
59
|
-
token: ${{ secrets.PAT }}
|
|
60
|
-
committer: AutoHippo <auto@hippo.org>
|
|
61
|
-
branch: bump-to-version-${{ steps.version-after.outputs.current-version }}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"inverterAccessory.js","sourceRoot":"","sources":["../../src/accessories/inverterAccessory.ts"],"names":[],"mappings":";;;AAIA,MAAM,aAAa,GAAG,MAAM,CAAA;AAEf,QAAA,YAAY,GAAwB,IAAI,GAAG,CAAiB;IACvE,CAAC,YAAY,EAAE,YAAY,CAAC;IAC5B,CAAC,iBAAiB,EAAE,cAAc,CAAC;IACnC,CAAC,aAAa,EAAE,eAAe,CAAC;IAChC,CAAC,sBAAsB,EAAE,wBAAwB,CAAC;CACnD,CAAC,CAAA;AAEF,MAAa,iBAAiB;IAMC;IAA2C;
|
|
1
|
+
{"version":3,"file":"inverterAccessory.js","sourceRoot":"","sources":["../../src/accessories/inverterAccessory.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.calculateSignature = calculateSignature;
|
|
4
|
+
exports.getDevices = getDevices;
|
|
5
|
+
exports.getRealTimeData = getRealTimeData;
|
|
6
|
+
const node_crypto_1 = require("node:crypto");
|
|
7
|
+
const BaseUrl = 'https://www.foxesscloud.com';
|
|
8
|
+
function calculateSignature(path, apiKey, timestamp) {
|
|
9
|
+
return (0, node_crypto_1.createHash)('md5').update(`${path}\\r\\n${apiKey}\\r\\n${timestamp.toString()}`).digest('hex');
|
|
10
|
+
}
|
|
11
|
+
function headers(path, apiKey) {
|
|
12
|
+
const timestamp = Date.now();
|
|
13
|
+
return {
|
|
14
|
+
'Content-Type': 'application/json',
|
|
15
|
+
signature: calculateSignature(path, apiKey, timestamp),
|
|
16
|
+
token: apiKey,
|
|
17
|
+
timestamp: timestamp.toString(),
|
|
18
|
+
lang: 'en'
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
async function post(path, apiKey, body) {
|
|
22
|
+
const response = await fetch(`${BaseUrl}${path}`, {
|
|
23
|
+
method: 'POST',
|
|
24
|
+
headers: headers(path, apiKey),
|
|
25
|
+
body: JSON.stringify(body)
|
|
26
|
+
});
|
|
27
|
+
if (!response.ok) {
|
|
28
|
+
throw new Error(`FoxESS API ${path}: HTTP ${response.status.toString()} ${response.statusText}`);
|
|
29
|
+
}
|
|
30
|
+
const data = await response.json();
|
|
31
|
+
if (data.errno !== 0) {
|
|
32
|
+
throw new Error(`FoxESS API ${path}: errno ${data.errno.toString()}`);
|
|
33
|
+
}
|
|
34
|
+
return data.result;
|
|
35
|
+
}
|
|
36
|
+
const deviceListPath = '/op/v0/device/list';
|
|
37
|
+
async function getDevices(apiKey) {
|
|
38
|
+
const results = [];
|
|
39
|
+
let page = 0;
|
|
40
|
+
let total = 0;
|
|
41
|
+
do {
|
|
42
|
+
const result = await post(deviceListPath, apiKey, {
|
|
43
|
+
currentPage: ++page,
|
|
44
|
+
pageSize: 100
|
|
45
|
+
});
|
|
46
|
+
total = result.total;
|
|
47
|
+
if (result.data !== undefined) {
|
|
48
|
+
results.push(...result.data);
|
|
49
|
+
}
|
|
50
|
+
} while (results.length < total);
|
|
51
|
+
return results;
|
|
52
|
+
}
|
|
53
|
+
const realTimePath = '/op/v0/device/real/query';
|
|
54
|
+
async function getRealTimeData(apiKey, options) {
|
|
55
|
+
return post(realTimePath, apiKey, options ?? {});
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=foxess-api.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"foxess-api.js","sourceRoot":"","sources":["../src/foxess-api.ts"],"names":[],"mappings":";;AAkDA,gDAEC;AAuCD,gCAkBC;AASD,0CAEC;AAlHD,6CAAwC;AAExC,MAAM,OAAO,GAAG,6BAA6B,CAAA;AA0C7C,SAAgB,kBAAkB,CAAC,IAAY,EAAE,MAAc,EAAE,SAAiB;IAChF,OAAO,IAAA,wBAAU,EAAC,KAAK,CAAC,CAAC,MAAM,CAAC,GAAG,IAAI,SAAS,MAAM,SAAS,SAAS,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;AACtG,CAAC;AAED,SAAS,OAAO,CAAC,IAAY,EAAE,MAAc;IAC3C,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAA;IAC5B,OAAO;QACL,cAAc,EAAE,kBAAkB;QAClC,SAAS,EAAE,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,CAAC;QACtD,KAAK,EAAE,MAAM;QACb,SAAS,EAAE,SAAS,CAAC,QAAQ,EAAE;QAC/B,IAAI,EAAE,IAAI;KACX,CAAA;AACH,CAAC;AAED,KAAK,UAAU,IAAI,CAAI,IAAY,EAAE,MAAc,EAAE,IAAa;IAChE,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,OAAO,GAAG,IAAI,EAAE,EAAE;QAChD,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;QAC9B,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;KAC3B,CAAC,CAAA;IAEF,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,MAAM,IAAI,KAAK,CAAC,cAAc,IAAI,UAAU,QAAQ,CAAC,MAAM,CAAC,QAAQ,EAAE,IAAI,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAA;IAClG,CAAC;IAED,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAoB,CAAA;IACpD,IAAI,IAAI,CAAC,KAAK,KAAK,CAAC,EAAE,CAAC;QACrB,MAAM,IAAI,KAAK,CAAC,cAAc,IAAI,WAAW,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAA;IACvE,CAAC;IAED,OAAO,IAAI,CAAC,MAAM,CAAA;AACpB,CAAC;AAID,MAAM,cAAc,GAAG,oBAAoB,CAAA;AAKpC,KAAK,UAAU,UAAU,CAAC,MAAc;IAC7C,MAAM,OAAO,GAAe,EAAE,CAAA;IAC9B,IAAI,IAAI,GAAG,CAAC,CAAA;IACZ,IAAI,KAAK,GAAG,CAAC,CAAA;IAEb,GAAG,CAAC;QACF,MAAM,MAAM,GAAG,MAAM,IAAI,CAA4B,cAAc,EAAE,MAAM,EAAE;YAC3E,WAAW,EAAE,EAAE,IAAI;YACnB,QAAQ,EAAE,GAAG;SACd,CAAC,CAAA;QAEF,KAAK,GAAG,MAAM,CAAC,KAAK,CAAA;QACpB,IAAI,MAAM,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;YAC9B,OAAO,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,CAAA;QAC9B,CAAC;IACH,CAAC,QAAQ,OAAO,CAAC,MAAM,GAAG,KAAK,EAAC;IAEhC,OAAO,OAAO,CAAA;AAChB,CAAC;AAID,MAAM,YAAY,GAAG,0BAA0B,CAAA;AAKxC,KAAK,UAAU,eAAe,CAAC,MAAc,EAAE,OAA6B;IACjF,OAAO,IAAI,CAAiB,YAAY,EAAE,MAAM,EAAE,OAAO,IAAI,EAAE,CAAC,CAAA;AAClE,CAAC"}
|
package/dist/platform.js
CHANGED
|
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.FoxESSPlatform = void 0;
|
|
4
4
|
const settings_1 = require("./settings");
|
|
5
5
|
const inverterAccessory_1 = require("./accessories/inverterAccessory");
|
|
6
|
-
const
|
|
6
|
+
const foxess_api_1 = require("./foxess-api");
|
|
7
7
|
const minInterval = 60 * 1000;
|
|
8
8
|
class FoxESSPlatform {
|
|
9
9
|
log;
|
|
@@ -55,9 +55,9 @@ class FoxESSPlatform {
|
|
|
55
55
|
this.api.unregisterPlatformAccessories(settings_1.PLUGIN_NAME, settings_1.PLATFORM_NAME, [accessory]);
|
|
56
56
|
}
|
|
57
57
|
async discoverDevices() {
|
|
58
|
-
const inverters = await
|
|
59
|
-
for (const
|
|
60
|
-
this.createInverter(
|
|
58
|
+
const inverters = await (0, foxess_api_1.getDevices)(this.apiKey);
|
|
59
|
+
for (const inv of inverters) {
|
|
60
|
+
this.createInverter(inv);
|
|
61
61
|
}
|
|
62
62
|
for (const accessory of this.accessories) {
|
|
63
63
|
const context = accessory.context;
|
|
@@ -72,23 +72,23 @@ class FoxESSPlatform {
|
|
|
72
72
|
}
|
|
73
73
|
}
|
|
74
74
|
}
|
|
75
|
-
createInverter(
|
|
76
|
-
const uuid = this.api.hap.uuid.generate(
|
|
77
|
-
this.log.debug('Looking up:',
|
|
75
|
+
createInverter(inv) {
|
|
76
|
+
const uuid = this.api.hap.uuid.generate(inv.deviceSN);
|
|
77
|
+
this.log.debug('Looking up:', inv.deviceSN);
|
|
78
78
|
const existingAccessory = this.accessories.find(accessory => accessory.UUID === uuid);
|
|
79
|
-
const displayName = `Inverter ${
|
|
79
|
+
const displayName = `Inverter ${inv.deviceSN}`;
|
|
80
80
|
if (existingAccessory !== undefined && 'displayName' in existingAccessory) {
|
|
81
81
|
this.log.info('Restoring existing accessory from cache:', existingAccessory.displayName);
|
|
82
|
-
existingAccessory.context =
|
|
82
|
+
existingAccessory.context = inv;
|
|
83
83
|
existingAccessory.displayName = displayName;
|
|
84
84
|
this.api.updatePlatformAccessories([existingAccessory]);
|
|
85
|
-
this.inverters.set(
|
|
85
|
+
this.inverters.set(inv.deviceSN, new inverterAccessory_1.InverterAccessory(this, existingAccessory, this.indicators));
|
|
86
86
|
}
|
|
87
87
|
else {
|
|
88
88
|
this.log.info('Adding new accessory:', displayName);
|
|
89
89
|
const accessory = new this.api.platformAccessory(displayName, uuid);
|
|
90
|
-
accessory.context =
|
|
91
|
-
this.inverters.set(
|
|
90
|
+
accessory.context = inv;
|
|
91
|
+
this.inverters.set(inv.deviceSN, new inverterAccessory_1.InverterAccessory(this, accessory, this.indicators));
|
|
92
92
|
this.api.registerPlatformAccessories(settings_1.PLUGIN_NAME, settings_1.PLATFORM_NAME, [accessory]);
|
|
93
93
|
}
|
|
94
94
|
}
|
|
@@ -107,7 +107,7 @@ class FoxESSPlatform {
|
|
|
107
107
|
}
|
|
108
108
|
async updateCurrentLevel() {
|
|
109
109
|
this.log.debug('Fetching real time data');
|
|
110
|
-
const results = await
|
|
110
|
+
const results = await (0, foxess_api_1.getRealTimeData)(this.apiKey, { variables: Array.from(inverterAccessory_1.DisplayNames.keys()) });
|
|
111
111
|
if (results === undefined) {
|
|
112
112
|
this.log.warn('No results came through.');
|
|
113
113
|
return;
|
package/dist/platform.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"platform.js","sourceRoot":"","sources":["../src/platform.ts"],"names":[],"mappings":";;;AACA,yCAAuD;AACvD,uEAAiF;AAEjF,
|
|
1
|
+
{"version":3,"file":"platform.js","sourceRoot":"","sources":["../src/platform.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "homebridge-foxess",
|
|
3
3
|
"displayName": "Homebridge FoxESS",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.3.0",
|
|
5
5
|
"description": "Basic utility accessories for displaying solar FoxESS solar information in Homebridge: https://github.com/nfarina/homebridge",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"keywords": [
|
|
@@ -30,18 +30,15 @@
|
|
|
30
30
|
"homebridge": ">=1.7.0 || ^2.0.0-beta.0"
|
|
31
31
|
},
|
|
32
32
|
"devDependencies": {
|
|
33
|
-
"@eslint/js": "^9.
|
|
34
|
-
"@stylistic/eslint-plugin": "^5.
|
|
35
|
-
"@types/node": "^24.
|
|
36
|
-
"eslint": "^9.
|
|
37
|
-
"homebridge": "^1.11.
|
|
38
|
-
"rimraf": "^6.
|
|
33
|
+
"@eslint/js": "^9.39.4",
|
|
34
|
+
"@stylistic/eslint-plugin": "^5.10.0",
|
|
35
|
+
"@types/node": "^24.12.0",
|
|
36
|
+
"eslint": "^9.39.4",
|
|
37
|
+
"homebridge": "^1.11.3",
|
|
38
|
+
"rimraf": "^6.1.3",
|
|
39
39
|
"ts-node": "^10.9.2",
|
|
40
40
|
"typescript": "^5.9.3",
|
|
41
|
-
"typescript-eslint": "^8.
|
|
42
|
-
},
|
|
43
|
-
"dependencies": {
|
|
44
|
-
"foxess-lib": "^0.7.1"
|
|
41
|
+
"typescript-eslint": "^8.57.1"
|
|
45
42
|
},
|
|
46
43
|
"scripts": {
|
|
47
44
|
"forceClean": "rimraf ./dist",
|
package/renovate.json
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
|
3
|
+
"extends": ["config:recommended"],
|
|
4
|
+
"labels": ["dependencies"],
|
|
5
|
+
"lockFileMaintenance": {
|
|
6
|
+
"enabled": true,
|
|
7
|
+
"schedule": ["before 7am on Monday"],
|
|
8
|
+
"commitMessageAction": "upgrade",
|
|
9
|
+
"automerge": true
|
|
10
|
+
},
|
|
11
|
+
"packageRules": [
|
|
12
|
+
{
|
|
13
|
+
"matchUpdateTypes": ["minor", "patch"],
|
|
14
|
+
"groupName": "non-major dependencies",
|
|
15
|
+
"automerge": true
|
|
16
|
+
}
|
|
17
|
+
],
|
|
18
|
+
"commitMessagePrefix": "build(deps):",
|
|
19
|
+
"dependencyDashboard": true
|
|
20
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { CharacteristicGetHandler, Characteristic, PlatformAccessory, Service } from 'homebridge'
|
|
2
2
|
import type { FoxESSPlatform } from '../platform'
|
|
3
3
|
import type { Indicators } from '../indicators'
|
|
4
|
-
import {
|
|
4
|
+
import type { Inverter, RealTimeData } from '../foxess-api'
|
|
5
5
|
const minLightLevel = 0.0001
|
|
6
6
|
|
|
7
7
|
export const DisplayNames: Map<string, string> = new Map<string, string>([
|
|
@@ -12,12 +12,12 @@ export const DisplayNames: Map<string, string> = new Map<string, string>([
|
|
|
12
12
|
])
|
|
13
13
|
|
|
14
14
|
export class InverterAccessory {
|
|
15
|
-
private readonly inverter:
|
|
15
|
+
private readonly inverter: Inverter
|
|
16
16
|
private readonly values: Map<string, number> = new Map<string, number>()
|
|
17
17
|
private readonly gridUsageSwitchOn: Characteristic | undefined
|
|
18
18
|
private readonly generationSwitchOn: Characteristic | undefined
|
|
19
19
|
|
|
20
|
-
constructor(private readonly platform: FoxESSPlatform, private readonly accessory: PlatformAccessory<
|
|
20
|
+
constructor(private readonly platform: FoxESSPlatform, private readonly accessory: PlatformAccessory<Inverter>, private readonly indicators: Indicators | undefined) {
|
|
21
21
|
this.platform.log.info('Initialising inverter:', this.accessory.displayName)
|
|
22
22
|
this.inverter = this.accessory.context
|
|
23
23
|
const informationService = this.accessory.getService(this.platform.Service.AccessoryInformation)
|
|
@@ -85,7 +85,7 @@ export class InverterAccessory {
|
|
|
85
85
|
this.gridUsageSwitchOn?.updateValue(this.gridUsageSwitchState())
|
|
86
86
|
}
|
|
87
87
|
|
|
88
|
-
public update(value:
|
|
88
|
+
public update(value: RealTimeData): void {
|
|
89
89
|
this.platform.log.debug('Updating', this.accessory.displayName, 'with', value.datas.length, 'value(s)')
|
|
90
90
|
value.datas.forEach((data) => {
|
|
91
91
|
const service = this.accessory.getService(data.variable)
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FoxESS Cloud API client.
|
|
3
|
+
* Replaces the archived foxess-lib package with direct API calls
|
|
4
|
+
* using native Node.js crypto and fetch (Node 18+).
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { createHash } from 'node:crypto'
|
|
8
|
+
|
|
9
|
+
const BaseUrl = 'https://www.foxesscloud.com'
|
|
10
|
+
|
|
11
|
+
/* ---- Types ---- */
|
|
12
|
+
|
|
13
|
+
export interface Inverter {
|
|
14
|
+
deviceSN: string
|
|
15
|
+
deviceType: string
|
|
16
|
+
productType: string
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface RealTimeData {
|
|
20
|
+
deviceSN: string
|
|
21
|
+
datas: { variable: string, value: number }[]
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface RealTimeDataRequest {
|
|
25
|
+
variables?: string[]
|
|
26
|
+
deviceSN?: string[]
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
interface ApiResponse<T> {
|
|
30
|
+
errno: number
|
|
31
|
+
result: T
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
interface PaginatedResult<T> {
|
|
35
|
+
total: number
|
|
36
|
+
data: T[] | undefined
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/* ---- Auth ---- */
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Calculate the FoxESS API signature.
|
|
43
|
+
*
|
|
44
|
+
* IMPORTANT: The separator is the literal 4-character string `\r\n`,
|
|
45
|
+
* NOT actual CR+LF control characters.
|
|
46
|
+
*
|
|
47
|
+
* Test vector:
|
|
48
|
+
* calculateSignature("/op/v0/device/list", "abcdefghij012345689", 1705809089)
|
|
49
|
+
* → "68a007c2450d6697fbe2990f92000269"
|
|
50
|
+
*/
|
|
51
|
+
export function calculateSignature(path: string, apiKey: string, timestamp: number): string {
|
|
52
|
+
return createHash('md5').update(`${path}\\r\\n${apiKey}\\r\\n${timestamp.toString()}`).digest('hex')
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function headers(path: string, apiKey: string): Record<string, string> {
|
|
56
|
+
const timestamp = Date.now()
|
|
57
|
+
return {
|
|
58
|
+
'Content-Type': 'application/json',
|
|
59
|
+
signature: calculateSignature(path, apiKey, timestamp),
|
|
60
|
+
token: apiKey,
|
|
61
|
+
timestamp: timestamp.toString(),
|
|
62
|
+
lang: 'en'
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async function post<T>(path: string, apiKey: string, body: unknown): Promise<T> {
|
|
67
|
+
const response = await fetch(`${BaseUrl}${path}`, {
|
|
68
|
+
method: 'POST',
|
|
69
|
+
headers: headers(path, apiKey),
|
|
70
|
+
body: JSON.stringify(body)
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
if (!response.ok) {
|
|
74
|
+
throw new Error(`FoxESS API ${path}: HTTP ${response.status.toString()} ${response.statusText}`)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const data = await response.json() as ApiResponse<T>
|
|
78
|
+
if (data.errno !== 0) {
|
|
79
|
+
throw new Error(`FoxESS API ${path}: errno ${data.errno.toString()}`)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return data.result
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/* ---- Device List ---- */
|
|
86
|
+
|
|
87
|
+
const deviceListPath = '/op/v0/device/list'
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* List all inverters for the account (paginated).
|
|
91
|
+
*/
|
|
92
|
+
export async function getDevices(apiKey: string): Promise<Inverter[]> {
|
|
93
|
+
const results: Inverter[] = []
|
|
94
|
+
let page = 0
|
|
95
|
+
let total = 0
|
|
96
|
+
|
|
97
|
+
do {
|
|
98
|
+
const result = await post<PaginatedResult<Inverter>>(deviceListPath, apiKey, {
|
|
99
|
+
currentPage: ++page,
|
|
100
|
+
pageSize: 100
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
total = result.total
|
|
104
|
+
if (result.data !== undefined) {
|
|
105
|
+
results.push(...result.data)
|
|
106
|
+
}
|
|
107
|
+
} while (results.length < total)
|
|
108
|
+
|
|
109
|
+
return results
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/* ---- Real-time Data ---- */
|
|
113
|
+
|
|
114
|
+
const realTimePath = '/op/v0/device/real/query'
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Get real-time data for inverters.
|
|
118
|
+
*/
|
|
119
|
+
export async function getRealTimeData(apiKey: string, options?: RealTimeDataRequest): Promise<RealTimeData[] | undefined> {
|
|
120
|
+
return post<RealTimeData[]>(realTimePath, apiKey, options ?? {})
|
|
121
|
+
}
|
package/src/platform.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { type API, type DynamicPlatformPlugin, type Logger, type PlatformAccesso
|
|
|
2
2
|
import { PLATFORM_NAME, PLUGIN_NAME } from './settings'
|
|
3
3
|
import { InverterAccessory, DisplayNames } from './accessories/inverterAccessory'
|
|
4
4
|
import { type Indicators } from './indicators'
|
|
5
|
-
import {
|
|
5
|
+
import { type Inverter, getDevices, getRealTimeData } from './foxess-api'
|
|
6
6
|
|
|
7
7
|
const minInterval: number = 60 * 1000
|
|
8
8
|
|
|
@@ -60,13 +60,13 @@ export class FoxESSPlatform implements DynamicPlatformPlugin {
|
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
async discoverDevices(): Promise<void> {
|
|
63
|
-
const inverters = await
|
|
64
|
-
for (const
|
|
65
|
-
this.createInverter(
|
|
63
|
+
const inverters = await getDevices(this.apiKey)
|
|
64
|
+
for (const inv of inverters) {
|
|
65
|
+
this.createInverter(inv)
|
|
66
66
|
}
|
|
67
67
|
|
|
68
68
|
for (const accessory of this.accessories) {
|
|
69
|
-
const context = accessory.context as
|
|
69
|
+
const context = accessory.context as Inverter
|
|
70
70
|
if (!('deviceSN' in context)) {
|
|
71
71
|
this.log.error('Unknown context for accessory:', accessory.displayName, 'data: ', JSON.stringify(accessory.context))
|
|
72
72
|
} else {
|
|
@@ -78,22 +78,22 @@ export class FoxESSPlatform implements DynamicPlatformPlugin {
|
|
|
78
78
|
}
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
-
private createInverter(
|
|
82
|
-
const uuid = this.api.hap.uuid.generate(
|
|
83
|
-
this.log.debug('Looking up:',
|
|
84
|
-
const existingAccessory = this.accessories.find(accessory => accessory.UUID === uuid) as PlatformAccessory<
|
|
85
|
-
const displayName = `Inverter ${
|
|
81
|
+
private createInverter(inv: Inverter): void {
|
|
82
|
+
const uuid = this.api.hap.uuid.generate(inv.deviceSN)
|
|
83
|
+
this.log.debug('Looking up:', inv.deviceSN)
|
|
84
|
+
const existingAccessory = this.accessories.find(accessory => accessory.UUID === uuid) as PlatformAccessory<Inverter> | undefined
|
|
85
|
+
const displayName = `Inverter ${inv.deviceSN}`
|
|
86
86
|
if (existingAccessory !== undefined && 'displayName' in existingAccessory) {
|
|
87
87
|
this.log.info('Restoring existing accessory from cache:', existingAccessory.displayName)
|
|
88
|
-
existingAccessory.context =
|
|
88
|
+
existingAccessory.context = inv
|
|
89
89
|
existingAccessory.displayName = displayName
|
|
90
90
|
this.api.updatePlatformAccessories([existingAccessory])
|
|
91
|
-
this.inverters.set(
|
|
91
|
+
this.inverters.set(inv.deviceSN, new InverterAccessory(this, existingAccessory, this.indicators))
|
|
92
92
|
} else {
|
|
93
93
|
this.log.info('Adding new accessory:', displayName)
|
|
94
|
-
const accessory = new this.api.platformAccessory<
|
|
95
|
-
accessory.context =
|
|
96
|
-
this.inverters.set(
|
|
94
|
+
const accessory = new this.api.platformAccessory<Inverter>(displayName, uuid)
|
|
95
|
+
accessory.context = inv
|
|
96
|
+
this.inverters.set(inv.deviceSN, new InverterAccessory(this, accessory, this.indicators))
|
|
97
97
|
this.api.registerPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [accessory])
|
|
98
98
|
}
|
|
99
99
|
}
|
|
@@ -113,7 +113,7 @@ export class FoxESSPlatform implements DynamicPlatformPlugin {
|
|
|
113
113
|
|
|
114
114
|
async updateCurrentLevel(): Promise<void> {
|
|
115
115
|
this.log.debug('Fetching real time data')
|
|
116
|
-
const results = await
|
|
116
|
+
const results = await getRealTimeData(this.apiKey, { variables: Array.from(DisplayNames.keys()) })
|
|
117
117
|
if (results === undefined) {
|
|
118
118
|
this.log.warn('No results came through.')
|
|
119
119
|
return
|
package/.github/dependabot.yml
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
# To get started with Dependabot version updates, you'll need to specify which
|
|
2
|
-
# package ecosystems to update and where the package manifests are located.
|
|
3
|
-
# Please see the documentation for all configuration options:
|
|
4
|
-
# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
|
|
5
|
-
|
|
6
|
-
version: 2
|
|
7
|
-
updates:
|
|
8
|
-
|
|
9
|
-
# Maintain dependencies for GitHub Actions
|
|
10
|
-
- package-ecosystem: "github-actions"
|
|
11
|
-
directory: "/"
|
|
12
|
-
schedule:
|
|
13
|
-
interval: "monthly"
|
|
14
|
-
|
|
15
|
-
# Maintain dependencies for npm
|
|
16
|
-
- package-ecosystem: "npm"
|
|
17
|
-
directory: "/"
|
|
18
|
-
schedule:
|
|
19
|
-
interval: "monthly"
|
|
20
|
-
allow:
|
|
21
|
-
- dependency-type: "production"
|
|
22
|
-
rebase-strategy: "disabled"
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
name: Auto-merge
|
|
2
|
-
on: pull_request
|
|
3
|
-
|
|
4
|
-
jobs:
|
|
5
|
-
automerge:
|
|
6
|
-
permissions:
|
|
7
|
-
pull-requests: write
|
|
8
|
-
contents: write
|
|
9
|
-
runs-on: ubuntu-latest
|
|
10
|
-
if: github.actor == 'dependabot[bot]' || ((startsWith(github.event.pull_request.title, 'Bump to version ') || startsWith(github.event.pull_request.title, 'Update PNPM')) && (github.actor == 'github-actions' || github.actor == 'teh-hippo'))
|
|
11
|
-
steps:
|
|
12
|
-
- uses: actions/checkout@v5
|
|
13
|
-
- run: gh pr merge --rebase --delete-branch --auto "${{ github.event.pull_request.number }}"
|
|
14
|
-
env:
|
|
15
|
-
GH_TOKEN: ${{ github.token }}
|