homebridge-pvs6 1.0.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.
@@ -0,0 +1,168 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PVS6Platform = void 0;
4
+ const settings_1 = require("./settings");
5
+ const pvs6Client_1 = require("./pvs6Client");
6
+ const solarAccessory_1 = require("./solarAccessory");
7
+ const gridImportAccessory_1 = require("./gridImportAccessory");
8
+ const gridExportAccessory_1 = require("./gridExportAccessory");
9
+ const eveCharacteristics_1 = require("./eveCharacteristics");
10
+ const MIN_POLL_INTERVAL = 5;
11
+ const AUTH_RETRY_MS = 30000;
12
+ const AUTH_BACKOFF_MS = 60000;
13
+ const OVERLOAD_BACKOFF_MS = 15000;
14
+ class PVS6Platform {
15
+ constructor(log, config, api) {
16
+ this.accessories = [];
17
+ this.pollInFlight = false;
18
+ this.backedOff = false;
19
+ this.log = log;
20
+ this.api = api;
21
+ this.Service = api.hap.Service;
22
+ this.Characteristic = api.hap.Characteristic;
23
+ this.eveChars = (0, eveCharacteristics_1.createEveCharacteristics)(api);
24
+ const pvs6Config = config;
25
+ this.config = pvs6Config;
26
+ if (!pvs6Config.host || !pvs6Config.serialNumber) {
27
+ this.log.error('Missing required config: host and serialNumber are required.');
28
+ this.client = new pvs6Client_1.PVS6Client('', '', log);
29
+ this.pollIntervalMs = MIN_POLL_INTERVAL * 1000;
30
+ return;
31
+ }
32
+ let pollInterval = pvs6Config.pollInterval ?? 10;
33
+ if (pollInterval < MIN_POLL_INTERVAL) {
34
+ this.log.warn(`pollInterval ${pollInterval}s is below minimum; clamping to ${MIN_POLL_INTERVAL}s`);
35
+ pollInterval = MIN_POLL_INTERVAL;
36
+ }
37
+ this.pollIntervalMs = pollInterval * 1000;
38
+ this.client = new pvs6Client_1.PVS6Client(pvs6Config.host, pvs6Config.serialNumber, log);
39
+ this.log.info('Finished initializing platform:', config.name ?? settings_1.PLATFORM_NAME);
40
+ this.api.on('didFinishLaunching', () => {
41
+ this.log.debug('didFinishLaunching — setting up accessories and authenticating');
42
+ // fakegato-history must be loaded after the api is fully initialised
43
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
44
+ const FakeGatoHistoryService = require('fakegato-history')(this.api);
45
+ this.setupAccessories(FakeGatoHistoryService);
46
+ this.startAuthAndPolling();
47
+ });
48
+ this.api.on('shutdown', () => {
49
+ if (this.pollTimer) {
50
+ clearInterval(this.pollTimer);
51
+ }
52
+ });
53
+ }
54
+ // Called by Homebridge for each accessory it has cached from a previous run.
55
+ configureAccessory(accessory) {
56
+ this.log.info('Loading accessory from cache:', accessory.displayName);
57
+ this.accessories.push(accessory);
58
+ }
59
+ setupAccessories(
60
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
61
+ FakeGatoHistoryService) {
62
+ const { serialNumber } = this.config;
63
+ // Solar Production is always registered.
64
+ const solarName = this.config.solarName ?? 'Solar Production';
65
+ const solarUuid = this.api.hap.uuid.generate(`${serialNumber}-solar`);
66
+ this.solarAccessory = new solarAccessory_1.SolarAccessory(this, this.getOrCreateAccessory(solarUuid, solarName), FakeGatoHistoryService, solarName, serialNumber);
67
+ // Grid Import + Grid Export are registered together as an optional pair (default: enabled).
68
+ if (this.config.accessories?.grid !== false) {
69
+ const importName = this.config.gridName ?? 'Grid Meter - Import';
70
+ const importUuid = this.api.hap.uuid.generate(`${serialNumber}-grid`);
71
+ this.gridImportAccessory = new gridImportAccessory_1.GridImportAccessory(this, this.getOrCreateAccessory(importUuid, importName), FakeGatoHistoryService, importName, serialNumber);
72
+ const exportName = this.config.gridExportName ?? 'Grid Meter - Export';
73
+ const exportUuid = this.api.hap.uuid.generate(`${serialNumber}-grid-export`);
74
+ this.gridExportAccessory = new gridExportAccessory_1.GridExportAccessory(this, this.getOrCreateAccessory(exportUuid, exportName), FakeGatoHistoryService, exportName, serialNumber);
75
+ }
76
+ }
77
+ getOrCreateAccessory(uuid, displayName) {
78
+ const existing = this.accessories.find(a => a.UUID === uuid);
79
+ if (existing) {
80
+ this.log.info('Restoring accessory from cache:', existing.displayName);
81
+ existing.displayName = displayName;
82
+ this.api.updatePlatformAccessories([existing]);
83
+ return existing;
84
+ }
85
+ this.log.info('Registering new accessory:', displayName);
86
+ const accessory = new this.api.platformAccessory(displayName, uuid);
87
+ this.api.registerPlatformAccessories(settings_1.PLUGIN_NAME, settings_1.PLATFORM_NAME, [accessory]);
88
+ this.accessories.push(accessory);
89
+ return accessory;
90
+ }
91
+ startAuthAndPolling() {
92
+ this.client
93
+ .authenticate()
94
+ .then(() => {
95
+ this.log.info('Authenticated with PVS6 — starting polling');
96
+ this.startPolling();
97
+ })
98
+ .catch((err) => {
99
+ this.log.error(`Authentication failed: ${err.message}. Retrying in ${AUTH_RETRY_MS / 1000}s`);
100
+ setTimeout(() => this.startAuthAndPolling(), AUTH_RETRY_MS);
101
+ });
102
+ }
103
+ startPolling() {
104
+ this.pollTimer = setInterval(async () => {
105
+ if (this.pollInFlight) {
106
+ this.log.debug('Poll skipped — previous request still in flight');
107
+ return;
108
+ }
109
+ if (this.backedOff) {
110
+ this.log.debug('Poll skipped — in backoff period');
111
+ return;
112
+ }
113
+ this.pollInFlight = true;
114
+ try {
115
+ const reading = await this.client.poll();
116
+ this.solarAccessory?.updateValues(reading);
117
+ this.gridImportAccessory?.updateValues(reading);
118
+ this.gridExportAccessory?.updateValues(reading);
119
+ }
120
+ catch (err) {
121
+ if (err instanceof pvs6Client_1.HttpError) {
122
+ if (err.statusCode === 401) {
123
+ this.log.warn('HTTP 401 — session expired, re-authenticating...');
124
+ this.enterBackoff(AUTH_BACKOFF_MS);
125
+ this.client.authenticate()
126
+ .then(() => {
127
+ this.log.info('Re-authenticated — resuming polls');
128
+ this.backedOff = false;
129
+ })
130
+ .catch((authErr) => {
131
+ this.log.error(`Re-auth failed: ${authErr.message}. Backing off ${AUTH_BACKOFF_MS / 1000}s`);
132
+ });
133
+ }
134
+ else if (err.statusCode >= 500) {
135
+ this.log.warn(`HTTP ${err.statusCode} from PVS6 — device may be overloaded. Backing off ${OVERLOAD_BACKOFF_MS / 1000}s`);
136
+ this.enterBackoff(OVERLOAD_BACKOFF_MS);
137
+ }
138
+ else {
139
+ this.log.warn(`Poll HTTP error: ${err.message}`);
140
+ }
141
+ }
142
+ else if (err instanceof Error) {
143
+ if (err.message.includes('timeout')) {
144
+ this.log.warn('Poll timed out — skipping cycle');
145
+ }
146
+ else if (err.message.includes('JSON parse')) {
147
+ this.log.warn(`${err.message}`);
148
+ }
149
+ else {
150
+ this.log.warn(`Poll error: ${err.message}`);
151
+ }
152
+ }
153
+ }
154
+ finally {
155
+ this.pollInFlight = false;
156
+ }
157
+ }, this.pollIntervalMs);
158
+ }
159
+ enterBackoff(durationMs) {
160
+ this.backedOff = true;
161
+ setTimeout(() => {
162
+ this.backedOff = false;
163
+ this.log.info('Backoff period ended — resuming polls');
164
+ }, durationMs);
165
+ }
166
+ }
167
+ exports.PVS6Platform = PVS6Platform;
168
+ //# sourceMappingURL=platform.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"platform.js","sourceRoot":"","sources":["../src/platform.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,35 @@
1
+ import { Logger } from 'homebridge';
2
+ export interface PVS6Reading {
3
+ pvPowerW: number;
4
+ pvEnergyKWh: number;
5
+ netPowerW: number;
6
+ gridImportKWh: number;
7
+ gridExportKWh: number;
8
+ }
9
+ export declare class HttpError extends Error {
10
+ readonly statusCode: number;
11
+ constructor(statusCode: number, message: string);
12
+ }
13
+ export declare class PVS6Client {
14
+ private readonly host;
15
+ private readonly serialNumber;
16
+ private readonly log;
17
+ private sessionCookie;
18
+ private livedataCacheId;
19
+ private mdataCacheId;
20
+ private consumptionMeterIdx;
21
+ private lastRequestTime;
22
+ private lastReading;
23
+ private static readonly MIN_INTERVAL_MS;
24
+ private static readonly TIMEOUT_MS;
25
+ constructor(host: string, serialNumber: string, log: Logger);
26
+ private get password();
27
+ authenticate(): Promise<void>;
28
+ poll(): Promise<PVS6Reading>;
29
+ private fetchCache;
30
+ private parseReading;
31
+ private discoverMeters;
32
+ private num;
33
+ private rawRequest;
34
+ }
35
+ //# sourceMappingURL=pvs6Client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pvs6Client.d.ts","sourceRoot":"","sources":["../src/pvs6Client.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAEpC,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,qBAAa,SAAU,SAAQ,KAAK;aAEhB,UAAU,EAAE,MAAM;gBAAlB,UAAU,EAAE,MAAM,EAClC,OAAO,EAAE,MAAM;CAKlB;AAKD,qBAAa,UAAU;IAoBnB,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,YAAY;IAC7B,OAAO,CAAC,QAAQ,CAAC,GAAG;IArBtB,OAAO,CAAC,aAAa,CAAM;IAC3B,OAAO,CAAC,eAAe,CAAuB;IAC9C,OAAO,CAAC,YAAY,CAAuB;IAC3C,OAAO,CAAC,mBAAmB,CAAuB;IAClD,OAAO,CAAC,eAAe,CAAK;IAG5B,OAAO,CAAC,WAAW,CAMjB;IAEF,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,eAAe,CAAQ;IAC/C,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAS;gBAGxB,IAAI,EAAE,MAAM,EACZ,YAAY,EAAE,MAAM,EACpB,GAAG,EAAE,MAAM;IAG9B,OAAO,KAAK,QAAQ,GAEnB;IAIK,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;IAyB7B,IAAI,IAAI,OAAO,CAAC,WAAW,CAAC;YAMpB,UAAU;IAgExB,OAAO,CAAC,YAAY;IA8BpB,OAAO,CAAC,cAAc;IAsBtB,OAAO,CAAC,GAAG;IAeX,OAAO,CAAC,UAAU;CAsDnB"}
@@ -0,0 +1,262 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.PVS6Client = exports.HttpError = void 0;
37
+ const https = __importStar(require("https"));
38
+ class HttpError extends Error {
39
+ constructor(statusCode, message) {
40
+ super(message);
41
+ this.statusCode = statusCode;
42
+ this.name = 'HttpError';
43
+ }
44
+ }
45
+ exports.HttpError = HttpError;
46
+ class PVS6Client {
47
+ constructor(host, serialNumber, log) {
48
+ this.host = host;
49
+ this.serialNumber = serialNumber;
50
+ this.log = log;
51
+ this.sessionCookie = '';
52
+ this.livedataCacheId = null;
53
+ this.mdataCacheId = null;
54
+ this.consumptionMeterIdx = null;
55
+ this.lastRequestTime = 0;
56
+ // Retained across polls — partial responses use last known good values (spec §ErrorHandling)
57
+ this.lastReading = {
58
+ pvPowerW: 0,
59
+ pvEnergyKWh: 0,
60
+ netPowerW: 0,
61
+ gridImportKWh: 0,
62
+ gridExportKWh: 0,
63
+ };
64
+ }
65
+ get password() {
66
+ return this.serialNumber.slice(-5);
67
+ }
68
+ // Authenticate with the PVS6 and store the session cookie.
69
+ // The PVS6 uses GET /auth?login with Basic auth; the session cookie arrives in Set-Cookie.
70
+ async authenticate() {
71
+ const credentials = Buffer.from(`ssm_owner:${this.password}`).toString('base64');
72
+ const { status, headers } = await this.rawRequest('GET', '/auth?login', {
73
+ Authorization: `Basic ${credentials}`,
74
+ });
75
+ if (status === 401) {
76
+ throw new HttpError(401, 'PVS6 authentication rejected — check serialNumber');
77
+ }
78
+ if (status !== 200) {
79
+ throw new HttpError(status, `Auth returned HTTP ${status}`);
80
+ }
81
+ const setCookieHeader = headers['set-cookie'];
82
+ if (setCookieHeader && setCookieHeader.length > 0) {
83
+ // Store the cookie name=value pair (strip attributes like Path, Expires, etc.)
84
+ this.sessionCookie = setCookieHeader[0].split(';')[0].trim();
85
+ this.log.debug(`Session cookie: ${this.sessionCookie}`);
86
+ }
87
+ this.log.debug('Authenticated with PVS6');
88
+ }
89
+ // Fetch livedata and meter data, parse into a PVS6Reading.
90
+ // Throws HttpError on HTTP errors; throws Error on timeout or parse failure.
91
+ async poll() {
92
+ const livedataVars = await this.fetchCache('livedata', '/sys/livedata/');
93
+ const mdataVars = await this.fetchCache('mdata', '/sys/devices/meter/');
94
+ return this.parseReading(livedataVars, mdataVars);
95
+ }
96
+ async fetchCache(name, matchPattern) {
97
+ const cacheId = name === 'livedata' ? this.livedataCacheId : this.mdataCacheId;
98
+ if (cacheId !== null) {
99
+ try {
100
+ const { status, body } = await this.rawRequest('GET', `/vars?cache=${encodeURIComponent(cacheId)}&fmt=obj`);
101
+ if (status === 200) {
102
+ const data = JSON.parse(body);
103
+ this.log.debug(`${name} cache hit (id=${cacheId})`);
104
+ return data;
105
+ }
106
+ this.log.debug(`${name} cache returned HTTP ${status} — falling back to match query`);
107
+ }
108
+ catch (err) {
109
+ this.log.debug(`${name} cache request failed (${err}) — falling back to match query`);
110
+ }
111
+ // Cache stale or server reset — clear and re-fetch via match
112
+ if (name === 'livedata') {
113
+ this.livedataCacheId = null;
114
+ }
115
+ else {
116
+ this.mdataCacheId = null;
117
+ }
118
+ }
119
+ // First-time or cache-miss: fetch with match= to create the cache
120
+ const { status, body } = await this.rawRequest('GET', `/vars?match=${matchPattern}&fmt=obj`);
121
+ if (status === 401) {
122
+ throw new HttpError(401, 'Unauthorized — session expired');
123
+ }
124
+ if (status >= 500) {
125
+ throw new HttpError(status, `PVS6 HTTP ${status} — device may be overloaded`);
126
+ }
127
+ if (status !== 200) {
128
+ throw new HttpError(status, `Unexpected HTTP ${status}`);
129
+ }
130
+ if (!body.trim()) {
131
+ throw new Error('Empty response body from PVS6');
132
+ }
133
+ let data;
134
+ try {
135
+ data = JSON.parse(body);
136
+ }
137
+ catch {
138
+ throw new Error(`JSON parse failure — raw response: ${body.slice(0, 200)}`);
139
+ }
140
+ // Try to find a cache key in the response: it's a non-path key (doesn't start with '/')
141
+ for (const key of Object.keys(data)) {
142
+ if (!key.startsWith('/')) {
143
+ const id = String(data[key]);
144
+ if (name === 'livedata') {
145
+ this.livedataCacheId = id;
146
+ }
147
+ else {
148
+ this.mdataCacheId = id;
149
+ }
150
+ this.log.debug(`${name} cache key stored: ${id}`);
151
+ break;
152
+ }
153
+ }
154
+ return data;
155
+ }
156
+ parseReading(livedata, mdata) {
157
+ if (this.consumptionMeterIdx === null) {
158
+ this.discoverMeters(mdata);
159
+ }
160
+ const last = this.lastReading;
161
+ const consIdx = this.consumptionMeterIdx;
162
+ const pvPowerKW = this.num(livedata['/sys/livedata/pv_p'], 'pv_p');
163
+ const pvEnergyKWh = this.num(livedata['/sys/livedata/pv_en'], 'pv_en');
164
+ const netPowerKW = this.num(livedata['/sys/livedata/net_p'], 'net_p');
165
+ const gridImportKWhRaw = consIdx !== null
166
+ ? this.num(mdata[`/sys/devices/meter/${consIdx}/posLtea3phsumKwh`], 'cons.posLtea3phsumKwh')
167
+ : null;
168
+ const gridExportKWhRaw = consIdx !== null
169
+ ? this.num(mdata[`/sys/devices/meter/${consIdx}/negLtea3phsumKwh`], 'cons.negLtea3phsumKwh')
170
+ : null;
171
+ const reading = {
172
+ pvPowerW: pvPowerKW !== null ? Math.round(pvPowerKW * 1000 * 10) / 10 : last.pvPowerW,
173
+ pvEnergyKWh: pvEnergyKWh ?? last.pvEnergyKWh,
174
+ netPowerW: netPowerKW !== null ? Math.round(netPowerKW * 1000 * 10) / 10 : last.netPowerW,
175
+ gridImportKWh: gridImportKWhRaw ?? last.gridImportKWh,
176
+ gridExportKWh: gridExportKWhRaw ?? last.gridExportKWh,
177
+ };
178
+ this.lastReading = reading;
179
+ return reading;
180
+ }
181
+ discoverMeters(mdata) {
182
+ const indices = new Set();
183
+ for (const key of Object.keys(mdata)) {
184
+ const m = key.match(/^\/sys\/devices\/meter\/(\d+)\//);
185
+ if (m) {
186
+ indices.add(m[1]);
187
+ }
188
+ }
189
+ for (const idx of indices) {
190
+ const model = String(mdata[`/sys/devices/meter/${idx}/prodMdlNm`] ?? '');
191
+ if (model.toLowerCase().endsWith('c')) {
192
+ this.consumptionMeterIdx = idx;
193
+ this.log.debug(`Consumption meter at index ${idx}: ${model}`);
194
+ }
195
+ }
196
+ if (this.consumptionMeterIdx === null) {
197
+ this.log.warn('No consumption meter found in PVS6 response (model ending in "c")');
198
+ }
199
+ }
200
+ num(raw, field) {
201
+ if (raw === undefined || raw === '') {
202
+ this.log.debug(`Field ${field} missing from PVS6 response — using last known value`);
203
+ return null;
204
+ }
205
+ const val = Number(raw);
206
+ if (isNaN(val)) {
207
+ this.log.debug(`Field ${field} is not a number: ${raw} — using last known value`);
208
+ return null;
209
+ }
210
+ return val;
211
+ }
212
+ // Core HTTP request with rate limiting and timeout.
213
+ // All requests share a global 5-second minimum interval (PVS6 embedded server protection).
214
+ rawRequest(method, path, extraHeaders = {}) {
215
+ return new Promise((resolve, reject) => {
216
+ const now = Date.now();
217
+ const elapsed = now - this.lastRequestTime;
218
+ const delay = this.lastRequestTime > 0 && elapsed < PVS6Client.MIN_INTERVAL_MS
219
+ ? PVS6Client.MIN_INTERVAL_MS - elapsed
220
+ : 0;
221
+ const doRequest = () => {
222
+ this.lastRequestTime = Date.now();
223
+ const headers = { ...extraHeaders };
224
+ if (!headers['Authorization'] && this.sessionCookie) {
225
+ headers['Cookie'] = this.sessionCookie;
226
+ }
227
+ const options = {
228
+ hostname: this.host,
229
+ path,
230
+ method,
231
+ headers,
232
+ rejectUnauthorized: false,
233
+ };
234
+ const req = https.request(options, (res) => {
235
+ const chunks = [];
236
+ res.on('data', (chunk) => chunks.push(chunk));
237
+ res.on('end', () => {
238
+ const body = Buffer.concat(chunks).toString('utf8');
239
+ this.log.debug(`${method} ${path} → HTTP ${res.statusCode}`);
240
+ resolve({ status: res.statusCode ?? 0, headers: res.headers, body });
241
+ });
242
+ });
243
+ req.setTimeout(PVS6Client.TIMEOUT_MS, () => {
244
+ req.destroy(new Error(`Request timeout after ${PVS6Client.TIMEOUT_MS / 1000}s`));
245
+ });
246
+ req.on('error', (err) => reject(err));
247
+ req.end();
248
+ };
249
+ if (delay > 0) {
250
+ this.log.debug(`Rate limiting: waiting ${delay}ms before next PVS6 request`);
251
+ setTimeout(doRequest, delay);
252
+ }
253
+ else {
254
+ doRequest();
255
+ }
256
+ });
257
+ }
258
+ }
259
+ exports.PVS6Client = PVS6Client;
260
+ PVS6Client.MIN_INTERVAL_MS = 5000;
261
+ PVS6Client.TIMEOUT_MS = 10000;
262
+ //# sourceMappingURL=pvs6Client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pvs6Client.js","sourceRoot":"","sources":["../src/pvs6Client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ import { PlatformConfig } from 'homebridge';
2
+ export declare const PLATFORM_NAME = "PVS6";
3
+ export declare const PLUGIN_NAME = "homebridge-pvs6";
4
+ export interface PVS6Config extends PlatformConfig {
5
+ host: string;
6
+ serialNumber: string;
7
+ pollInterval?: number;
8
+ accessories?: {
9
+ grid?: boolean;
10
+ };
11
+ solarName?: string;
12
+ gridName?: string;
13
+ gridExportName?: string;
14
+ }
15
+ //# sourceMappingURL=settings.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"settings.d.ts","sourceRoot":"","sources":["../src/settings.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAE5C,eAAO,MAAM,aAAa,SAAS,CAAC;AACpC,eAAO,MAAM,WAAW,oBAAoB,CAAC;AAE7C,MAAM,WAAW,UAAW,SAAQ,cAAc;IAChD,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE;QACZ,IAAI,CAAC,EAAE,OAAO,CAAC;KAChB,CAAC;IACF,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB"}
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PLUGIN_NAME = exports.PLATFORM_NAME = void 0;
4
+ exports.PLATFORM_NAME = 'PVS6';
5
+ exports.PLUGIN_NAME = 'homebridge-pvs6';
6
+ //# sourceMappingURL=settings.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"settings.js","sourceRoot":"","sources":["../src/settings.ts"],"names":[],"mappings":";;;AAEa,QAAA,aAAa,GAAG,MAAM,CAAC;AACvB,QAAA,WAAW,GAAG,iBAAiB,CAAC"}
@@ -0,0 +1,13 @@
1
+ import { PlatformAccessory } from 'homebridge';
2
+ import { PVS6Platform } from './platform';
3
+ import { PVS6Reading } from './pvs6Client';
4
+ export declare class SolarAccessory {
5
+ private readonly platform;
6
+ private readonly service;
7
+ private readonly historyService;
8
+ private lastPowerW;
9
+ private lastEnergyKWh;
10
+ constructor(platform: PVS6Platform, accessory: PlatformAccessory, FakeGatoHistoryService: any, displayName: string, serialNumber: string);
11
+ updateValues(reading: PVS6Reading): void;
12
+ }
13
+ //# sourceMappingURL=solarAccessory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"solarAccessory.d.ts","sourceRoot":"","sources":["../src/solarAccessory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAW,MAAM,YAAY,CAAC;AACxD,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAG3C,qBAAa,cAAc;IASvB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAR3B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAU;IAElC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAM;IAErC,OAAO,CAAC,UAAU,CAAK;IACvB,OAAO,CAAC,aAAa,CAAK;gBAGP,QAAQ,EAAE,YAAY,EACvC,SAAS,EAAE,iBAAiB,EAE5B,sBAAsB,EAAE,GAAG,EAC3B,WAAW,EAAE,MAAM,EACnB,YAAY,EAAE,MAAM;IA+CtB,YAAY,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI;CAmBzC"}
@@ -0,0 +1,63 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SolarAccessory = void 0;
4
+ const eveCharacteristics_1 = require("./eveCharacteristics");
5
+ class SolarAccessory {
6
+ constructor(platform, accessory,
7
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
8
+ FakeGatoHistoryService, displayName, serialNumber) {
9
+ this.platform = platform;
10
+ this.lastPowerW = 0;
11
+ this.lastEnergyKWh = 0;
12
+ const { Characteristic } = platform;
13
+ const { EveWatts, EveKWh } = platform.eveChars;
14
+ const infoService = accessory.getService(platform.Service.AccessoryInformation) ??
15
+ accessory.addService(platform.Service.AccessoryInformation);
16
+ infoService
17
+ .setCharacteristic(Characteristic.Manufacturer, 'SunStrong')
18
+ .setCharacteristic(Characteristic.Model, 'PVS6')
19
+ .setCharacteristic(Characteristic.SerialNumber, `${serialNumber}-solar`);
20
+ const existingService = accessory.services.find(s => s.UUID === eveCharacteristics_1.EVE_ENERGY_SERVICE_UUID);
21
+ this.service = existingService ??
22
+ accessory.addService(new platform.api.hap.Service(displayName, eveCharacteristics_1.EVE_ENERGY_SERVICE_UUID));
23
+ this.service.setCharacteristic(Characteristic.Name, displayName);
24
+ // On = true when solar is producing.
25
+ // HAP's On is read/write; the setter reverts to the polled state immediately.
26
+ this.service
27
+ .getCharacteristic(Characteristic.On)
28
+ .onGet(() => this.lastPowerW > 0)
29
+ .onSet(async () => {
30
+ this.service.updateCharacteristic(Characteristic.On, this.lastPowerW > 0);
31
+ });
32
+ // OutletInUse is required for Eve Energy to render correctly
33
+ this.service
34
+ .getCharacteristic(Characteristic.OutletInUse)
35
+ .onGet(() => true);
36
+ // Eve custom characteristics — getCharacteristic() is idempotent across restarts
37
+ this.service
38
+ .getCharacteristic(EveWatts)
39
+ .onGet(() => this.lastPowerW);
40
+ this.service
41
+ .getCharacteristic(EveKWh)
42
+ .onGet(() => this.lastEnergyKWh);
43
+ // fakegato history — 'energy' type records { time, power } in Watts
44
+ this.historyService = new FakeGatoHistoryService('energy', accessory, { storage: 'fs' });
45
+ }
46
+ updateValues(reading) {
47
+ const { Characteristic } = this.platform;
48
+ const { EveWatts, EveKWh } = this.platform.eveChars;
49
+ this.lastPowerW = reading.pvPowerW;
50
+ this.lastEnergyKWh = reading.pvEnergyKWh;
51
+ this.service.updateCharacteristic(Characteristic.On, this.lastPowerW > 0);
52
+ this.service.updateCharacteristic(Characteristic.OutletInUse, true);
53
+ this.service.updateCharacteristic(EveWatts, this.lastPowerW);
54
+ this.service.updateCharacteristic(EveKWh, this.lastEnergyKWh);
55
+ this.historyService.addEntry({
56
+ time: Math.round(Date.now() / 1000),
57
+ power: this.lastPowerW,
58
+ });
59
+ this.platform.log.debug(`Solar: ${this.lastPowerW}W ${this.lastEnergyKWh}kWh`);
60
+ }
61
+ }
62
+ exports.SolarAccessory = SolarAccessory;
63
+ //# sourceMappingURL=solarAccessory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"solarAccessory.js","sourceRoot":"","sources":["../src/solarAccessory.ts"],"names":[],"mappings":";;;AAGA,6DAA+D;AAE/D,MAAa,cAAc;IAQzB,YACmB,QAAsB,EACvC,SAA4B;IAC5B,8DAA8D;IAC9D,sBAA2B,EAC3B,WAAmB,EACnB,YAAoB;QALH,aAAQ,GAAR,QAAQ,CAAc;QAJjC,eAAU,GAAG,CAAC,CAAC;QACf,kBAAa,GAAG,CAAC,CAAC;QAUxB,MAAM,EAAE,cAAc,EAAE,GAAG,QAAQ,CAAC;QACpC,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,QAAQ,CAAC,QAAQ,CAAC;QAE/C,MAAM,WAAW,GACf,SAAS,CAAC,UAAU,CAAC,QAAQ,CAAC,OAAO,CAAC,oBAAoB,CAAC;YAC3D,SAAS,CAAC,UAAU,CAAC,QAAQ,CAAC,OAAO,CAAC,oBAAoB,CAAC,CAAC;QAE9D,WAAW;aACR,iBAAiB,CAAC,cAAc,CAAC,YAAY,EAAE,WAAW,CAAC;aAC3D,iBAAiB,CAAC,cAAc,CAAC,KAAK,EAAE,MAAM,CAAC;aAC/C,iBAAiB,CAAC,cAAc,CAAC,YAAY,EAAE,GAAG,YAAY,QAAQ,CAAC,CAAC;QAE3E,MAAM,eAAe,GAAG,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,4CAAuB,CAAC,CAAC;QACzF,IAAI,CAAC,OAAO,GAAG,eAAe;YAC5B,SAAS,CAAC,UAAU,CAAC,IAAI,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,WAAW,EAAE,4CAAuB,CAAC,CAAC,CAAC;QAE3F,IAAI,CAAC,OAAO,CAAC,iBAAiB,CAAC,cAAc,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC;QAEjE,qCAAqC;QACrC,8EAA8E;QAC9E,IAAI,CAAC,OAAO;aACT,iBAAiB,CAAC,cAAc,CAAC,EAAE,CAAC;aACpC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,UAAU,GAAG,CAAC,CAAC;aAChC,KAAK,CAAC,KAAK,IAAI,EAAE;YAChB,IAAI,CAAC,OAAO,CAAC,oBAAoB,CAAC,cAAc,CAAC,EAAE,EAAE,IAAI,CAAC,UAAU,GAAG,CAAC,CAAC,CAAC;QAC5E,CAAC,CAAC,CAAC;QAEL,6DAA6D;QAC7D,IAAI,CAAC,OAAO;aACT,iBAAiB,CAAC,cAAc,CAAC,WAAW,CAAC;aAC7C,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;QAErB,iFAAiF;QACjF,IAAI,CAAC,OAAO;aACT,iBAAiB,CAAC,QAAQ,CAAC;aAC3B,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAEhC,IAAI,CAAC,OAAO;aACT,iBAAiB,CAAC,MAAM,CAAC;aACzB,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QAEnC,oEAAoE;QACpE,IAAI,CAAC,cAAc,GAAG,IAAI,sBAAsB,CAAC,QAAQ,EAAE,SAAS,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;IAC3F,CAAC;IAED,YAAY,CAAC,OAAoB;QAC/B,MAAM,EAAE,cAAc,EAAE,GAAG,IAAI,CAAC,QAAQ,CAAC;QACzC,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC;QAEpD,IAAI,CAAC,UAAU,GAAG,OAAO,CAAC,QAAQ,CAAC;QACnC,IAAI,CAAC,aAAa,GAAG,OAAO,CAAC,WAAW,CAAC;QAEzC,IAAI,CAAC,OAAO,CAAC,oBAAoB,CAAC,cAAc,CAAC,EAAE,EAAE,IAAI,CAAC,UAAU,GAAG,CAAC,CAAC,CAAC;QAC1E,IAAI,CAAC,OAAO,CAAC,oBAAoB,CAAC,cAAc,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC;QACpE,IAAI,CAAC,OAAO,CAAC,oBAAoB,CAAC,QAAQ,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;QAC7D,IAAI,CAAC,OAAO,CAAC,oBAAoB,CAAC,MAAM,EAAE,IAAI,CAAC,aAAa,CAAC,CAAC;QAE9D,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC;YAC3B,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC;YACnC,KAAK,EAAE,IAAI,CAAC,UAAU;SACvB,CAAC,CAAC;QAEH,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,UAAU,IAAI,CAAC,UAAU,MAAM,IAAI,CAAC,aAAa,KAAK,CAAC,CAAC;IAClF,CAAC;CACF;AAhFD,wCAgFC"}