homebridge-hilo-challenge 0.1.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,28 @@
1
+ {
2
+ "pluginAlias": "HiloChallenge",
3
+ "pluginType": "platform",
4
+ "singular": true,
5
+ "schema": {
6
+ "type": "object",
7
+ "properties": {
8
+ "refreshToken": {
9
+ "title": "Hilo Refresh Token",
10
+ "type": "string",
11
+ "required": true,
12
+ "description": "Your Hilo refresh token — copy this from your homebridge-hilo plugin config."
13
+ },
14
+ "locationId": {
15
+ "title": "Location ID (optional)",
16
+ "type": "string",
17
+ "description": "Your Hilo location URN. Found in homebridge-hilo logs: 'Subscribed to updates for location urn:hilo:crm:...' Leave blank to auto-discover."
18
+ },
19
+ "pollInterval": {
20
+ "title": "Poll Interval (seconds)",
21
+ "type": "integer",
22
+ "default": 60,
23
+ "minimum": 30,
24
+ "maximum": 300
25
+ }
26
+ }
27
+ }
28
+ }
@@ -0,0 +1,15 @@
1
+ import { PlatformAccessory, Logger, HAP } from 'homebridge';
2
+ import { ChallengePhaseName } from './hiloApi';
3
+ export type SensorPhase = 'preheat' | 'reduction';
4
+ export declare class HiloChallengeSensorAccessory {
5
+ private readonly hap;
6
+ private readonly log;
7
+ private readonly accessory;
8
+ private readonly phase;
9
+ private readonly service;
10
+ private active;
11
+ constructor(hap: HAP, log: Logger, accessory: PlatformAccessory, phase: SensorPhase);
12
+ private getContactState;
13
+ updatePhase(currentPhase: ChallengePhaseName): void;
14
+ }
15
+ //# sourceMappingURL=challengeAccessory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"challengeAccessory.d.ts","sourceRoot":"","sources":["../src/challengeAccessory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAW,iBAAiB,EAAuB,MAAM,EAAE,GAAG,EAAE,MAAM,YAAY,CAAC;AAC1F,OAAO,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAG/C,MAAM,MAAM,WAAW,GAAG,SAAS,GAAG,WAAW,CAAC;AAElD,qBAAa,4BAA4B;IAKrC,OAAO,CAAC,QAAQ,CAAC,GAAG;IACpB,OAAO,CAAC,QAAQ,CAAC,GAAG;IACpB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,KAAK;IAPxB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAU;IAClC,OAAO,CAAC,MAAM,CAAS;gBAGJ,GAAG,EAAE,GAAG,EACR,GAAG,EAAE,MAAM,EACX,SAAS,EAAE,iBAAiB,EAC5B,KAAK,EAAE,WAAW;IAmBrC,OAAO,CAAC,eAAe;IAQvB,WAAW,CAAC,YAAY,EAAE,kBAAkB,GAAG,IAAI;CAoBpD"}
@@ -0,0 +1,47 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.HiloChallengeSensorAccessory = void 0;
4
+ class HiloChallengeSensorAccessory {
5
+ constructor(hap, log, accessory, phase) {
6
+ this.hap = hap;
7
+ this.log = log;
8
+ this.accessory = accessory;
9
+ this.phase = phase;
10
+ this.active = false;
11
+ const info = this.accessory.getService(this.hap.Service.AccessoryInformation);
12
+ info
13
+ .setCharacteristic(this.hap.Characteristic.Manufacturer, 'Hilo / Hydro-Québec')
14
+ .setCharacteristic(this.hap.Characteristic.Model, 'Challenge Sensor')
15
+ .setCharacteristic(this.hap.Characteristic.SerialNumber, `hilo-challenge-${phase}`);
16
+ this.service =
17
+ this.accessory.getService(this.hap.Service.ContactSensor) ||
18
+ this.accessory.addService(this.hap.Service.ContactSensor);
19
+ this.service.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName);
20
+ this.service
21
+ .getCharacteristic(this.hap.Characteristic.ContactSensorState)
22
+ .onGet(this.getContactState.bind(this));
23
+ }
24
+ getContactState() {
25
+ // NO_CONTACT (1) = phase is active → triggers HomeKit automations
26
+ // CONTACT_DETECTED (0) = phase is not active → normal/idle
27
+ return this.active
28
+ ? this.hap.Characteristic.ContactSensorState.CONTACT_NOT_DETECTED
29
+ : this.hap.Characteristic.ContactSensorState.CONTACT_DETECTED;
30
+ }
31
+ updatePhase(currentPhase) {
32
+ const wasActive = this.active;
33
+ if (this.phase === 'reduction') {
34
+ this.active = currentPhase === 'reduction';
35
+ }
36
+ else {
37
+ // preheat sensor is active during preheat only
38
+ this.active = currentPhase === 'preheat';
39
+ }
40
+ if (this.active !== wasActive) {
41
+ this.log.info(`[${this.accessory.displayName}] ${this.active ? 'ACTIVE (challenge phase started)' : 'INACTIVE (challenge phase ended)'}`);
42
+ this.service.updateCharacteristic(this.hap.Characteristic.ContactSensorState, this.getContactState());
43
+ }
44
+ }
45
+ }
46
+ exports.HiloChallengeSensorAccessory = HiloChallengeSensorAccessory;
47
+ //# sourceMappingURL=challengeAccessory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"challengeAccessory.js","sourceRoot":"","sources":["../src/challengeAccessory.ts"],"names":[],"mappings":";;;AAMA,MAAa,4BAA4B;IAIvC,YACmB,GAAQ,EACR,GAAW,EACX,SAA4B,EAC5B,KAAkB;QAHlB,QAAG,GAAH,GAAG,CAAK;QACR,QAAG,GAAH,GAAG,CAAQ;QACX,cAAS,GAAT,SAAS,CAAmB;QAC5B,UAAK,GAAL,KAAK,CAAa;QAN7B,WAAM,GAAG,KAAK,CAAC;QAQrB,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,oBAAoB,CAAE,CAAC;QAC/E,IAAI;aACD,iBAAiB,CAAC,IAAI,CAAC,GAAG,CAAC,cAAc,CAAC,YAAY,EAAE,qBAAqB,CAAC;aAC9E,iBAAiB,CAAC,IAAI,CAAC,GAAG,CAAC,cAAc,CAAC,KAAK,EAAE,kBAAkB,CAAC;aACpE,iBAAiB,CAAC,IAAI,CAAC,GAAG,CAAC,cAAc,CAAC,YAAY,EAAE,kBAAkB,KAAK,EAAE,CAAC,CAAC;QAEtF,IAAI,CAAC,OAAO;YACV,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,aAAa,CAAC;gBACzD,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;QAE5D,IAAI,CAAC,OAAO,CAAC,iBAAiB,CAAC,IAAI,CAAC,GAAG,CAAC,cAAc,CAAC,IAAI,EAAE,SAAS,CAAC,WAAW,CAAC,CAAC;QAEpF,IAAI,CAAC,OAAO;aACT,iBAAiB,CAAC,IAAI,CAAC,GAAG,CAAC,cAAc,CAAC,kBAAkB,CAAC;aAC7D,KAAK,CAAC,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IAC5C,CAAC;IAEO,eAAe;QACrB,kEAAkE;QAClE,2DAA2D;QAC3D,OAAO,IAAI,CAAC,MAAM;YAChB,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,cAAc,CAAC,kBAAkB,CAAC,oBAAoB;YACjE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,cAAc,CAAC,kBAAkB,CAAC,gBAAgB,CAAC;IAClE,CAAC;IAED,WAAW,CAAC,YAAgC;QAC1C,MAAM,SAAS,GAAG,IAAI,CAAC,MAAM,CAAC;QAE9B,IAAI,IAAI,CAAC,KAAK,KAAK,WAAW,EAAE,CAAC;YAC/B,IAAI,CAAC,MAAM,GAAG,YAAY,KAAK,WAAW,CAAC;QAC7C,CAAC;aAAM,CAAC;YACN,+CAA+C;YAC/C,IAAI,CAAC,MAAM,GAAG,YAAY,KAAK,SAAS,CAAC;QAC3C,CAAC;QAED,IAAI,IAAI,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;YAC9B,IAAI,CAAC,GAAG,CAAC,IAAI,CACX,IAAI,IAAI,CAAC,SAAS,CAAC,WAAW,KAAK,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,kCAAkC,CAAC,CAAC,CAAC,kCAAkC,EAAE,CAC3H,CAAC;YACF,IAAI,CAAC,OAAO,CAAC,oBAAoB,CAC/B,IAAI,CAAC,GAAG,CAAC,cAAc,CAAC,kBAAkB,EAC1C,IAAI,CAAC,eAAe,EAAE,CACvB,CAAC;QACJ,CAAC;IACH,CAAC;CACF;AAvDD,oEAuDC"}
@@ -0,0 +1,30 @@
1
+ export type ChallengePhaseName = 'none' | 'preheat' | 'reduction' | 'recovery';
2
+ export declare class HiloApi {
3
+ private refreshToken;
4
+ private readonly log;
5
+ private accessToken;
6
+ private tokenExpiry;
7
+ private currentRefreshToken;
8
+ private loginInFlight;
9
+ constructor(refreshToken: string, log: {
10
+ info: (m: string) => void;
11
+ debug: (m: string) => void;
12
+ error: (m: string) => void;
13
+ });
14
+ private ensureToken;
15
+ private refreshAccessToken;
16
+ private apiHeaders;
17
+ getLocationId(): Promise<string>;
18
+ /**
19
+ * Query the Hilo GraphQL API for the current challenge phase.
20
+ * Inspects the gDState field on each device in the location:
21
+ * ACTIVE → reduction phase
22
+ * PRE_HEAT → preheat phase
23
+ * RECOVERY → recovery phase
24
+ * anything else (OFF, absent) → none
25
+ *
26
+ * Returns the first non-none phase found, or 'none' if no challenge is active.
27
+ */
28
+ getChallengePhase(locationId: string): Promise<ChallengePhaseName>;
29
+ }
30
+ //# sourceMappingURL=hiloApi.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hiloApi.d.ts","sourceRoot":"","sources":["../src/hiloApi.ts"],"names":[],"mappings":"AAkBA,MAAM,MAAM,kBAAkB,GAAG,MAAM,GAAG,SAAS,GAAG,WAAW,GAAG,UAAU,CAAC;AAyD/E,qBAAa,OAAO;IAOhB,OAAO,CAAC,YAAY;IACpB,OAAO,CAAC,QAAQ,CAAC,GAAG;IAPtB,OAAO,CAAC,WAAW,CAAuB;IAC1C,OAAO,CAAC,WAAW,CAAK;IACxB,OAAO,CAAC,mBAAmB,CAAS;IACpC,OAAO,CAAC,aAAa,CAA8B;gBAGzC,YAAY,EAAE,MAAM,EACX,GAAG,EAAE;QAAE,IAAI,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;QAAC,KAAK,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;QAAC,KAAK,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAA;KAAE;YAK/F,WAAW;YASX,kBAAkB;IAuBhC,OAAO,CAAC,UAAU;IAQZ,aAAa,IAAI,OAAO,CAAC,MAAM,CAAC;IA4BtC;;;;;;;;;OASG;IACG,iBAAiB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,CAAC;CAmCzE"}
@@ -0,0 +1,200 @@
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.HiloApi = void 0;
37
+ const https = __importStar(require("https"));
38
+ const querystring = __importStar(require("querystring"));
39
+ // Azure B2C auth config — same values as homebridge-hilo
40
+ const B2C_HOST = 'connexion.hiloenergie.com';
41
+ const B2C_TENANT = 'HiloDirectoryB2C.onmicrosoft.com';
42
+ const B2C_POLICY = 'B2C_1A_SIGN_IN';
43
+ const CLIENT_ID = '1ca9f585-4a55-4085-8e30-9746a65fa561';
44
+ const SCOPE = [
45
+ 'openid',
46
+ 'https://HiloDirectoryB2C.onmicrosoft.com/hiloapis/user_impersonation',
47
+ 'offline_access',
48
+ ].join(' ');
49
+ const GRAPHQL_HOST = 'platform.hiloenergie.com';
50
+ const GRAPHQL_PATH = '/api/digital-twin/v3/graphql';
51
+ const APIM_KEY = '20eeaedcb86945afa3fe792cea89b8bf';
52
+ // GraphQL query — inline fragments for every device type that exposes gDState.
53
+ // gDState values observed: 'ACTIVE' (reduction), 'PRE_HEAT' (preheat),
54
+ // 'RECOVERY' (recovery), 'OFF' / absent (no challenge).
55
+ const GD_STATE_QUERY = `
56
+ query GetLocationGDState($locationHiloId: String!) {
57
+ getLocation(id: $locationHiloId) {
58
+ devices {
59
+ __typename
60
+ ... on BasicThermostat { gDState }
61
+ ... on HeatingFloorThermostat { gDState }
62
+ ... on LowVoltageThermostat { gDState }
63
+ ... on WaterHeater { gDState }
64
+ }
65
+ }
66
+ }
67
+ `;
68
+ function httpsRequest(method, hostname, path, headers, body) {
69
+ return new Promise((resolve, reject) => {
70
+ const reqHeaders = { ...headers };
71
+ if (body) {
72
+ reqHeaders['Content-Length'] = Buffer.byteLength(body).toString();
73
+ }
74
+ const req = https.request({ hostname, path, method, headers: reqHeaders }, (res) => {
75
+ const chunks = [];
76
+ res.on('data', (c) => chunks.push(c));
77
+ res.on('end', () => resolve({ status: res.statusCode ?? 0, data: Buffer.concat(chunks).toString('utf8') }));
78
+ });
79
+ req.on('error', reject);
80
+ if (body)
81
+ req.write(body);
82
+ req.end();
83
+ });
84
+ }
85
+ class HiloApi {
86
+ constructor(refreshToken, log) {
87
+ this.refreshToken = refreshToken;
88
+ this.log = log;
89
+ this.accessToken = null;
90
+ this.tokenExpiry = 0;
91
+ this.loginInFlight = null;
92
+ this.currentRefreshToken = refreshToken;
93
+ }
94
+ async ensureToken() {
95
+ if (this.accessToken && Date.now() < this.tokenExpiry - 60000) {
96
+ return;
97
+ }
98
+ if (this.loginInFlight)
99
+ return this.loginInFlight;
100
+ this.loginInFlight = this.refreshAccessToken().finally(() => { this.loginInFlight = null; });
101
+ return this.loginInFlight;
102
+ }
103
+ async refreshAccessToken() {
104
+ this.log.debug('Refreshing Hilo access token');
105
+ const body = querystring.stringify({
106
+ grant_type: 'refresh_token',
107
+ refresh_token: this.currentRefreshToken,
108
+ client_id: CLIENT_ID,
109
+ scope: SCOPE,
110
+ });
111
+ const path = `/${B2C_TENANT}/oauth2/v2.0/token?p=${B2C_POLICY}`;
112
+ const res = await httpsRequest('POST', B2C_HOST, path, {
113
+ 'Content-Type': 'application/x-www-form-urlencoded',
114
+ }, body);
115
+ if (res.status >= 400) {
116
+ throw new Error(`Token refresh failed: HTTP ${res.status}: ${res.data}`);
117
+ }
118
+ const json = JSON.parse(res.data);
119
+ this.accessToken = json.access_token;
120
+ this.currentRefreshToken = json.refresh_token ?? this.currentRefreshToken;
121
+ this.tokenExpiry = Date.now() + json.expires_in * 1000;
122
+ this.log.debug('Hilo access token refreshed');
123
+ }
124
+ apiHeaders() {
125
+ return {
126
+ Authorization: `Bearer ${this.accessToken}`,
127
+ 'Ocp-Apim-Subscription-Key': APIM_KEY,
128
+ 'Content-Type': 'application/json',
129
+ };
130
+ }
131
+ async getLocationId() {
132
+ await this.ensureToken();
133
+ // Try known REST endpoint patterns. These may return 404 on newer API versions —
134
+ // if so, set "locationId" manually in your config (see README).
135
+ const candidates = [
136
+ '/v2/api/Locations',
137
+ '/GDService/v1/api/Locations',
138
+ '/v1/api/Locations',
139
+ ];
140
+ for (const path of candidates) {
141
+ this.log.debug(`Trying location endpoint: ${path}`);
142
+ const res = await httpsRequest('GET', 'api.hiloenergie.com', path, this.apiHeaders());
143
+ if (res.status === 200) {
144
+ this.log.debug(`Locations response from ${path}: ${res.data}`);
145
+ const data = JSON.parse(res.data);
146
+ const locations = Array.isArray(data)
147
+ ? data
148
+ : (data.items ?? data.locations ?? data.value ?? []);
149
+ if (locations.length > 0)
150
+ return String(locations[0].id);
151
+ }
152
+ this.log.debug(`${path} returned ${res.status}`);
153
+ }
154
+ throw new Error('Could not auto-discover location ID. Add "locationId" to your config. ' +
155
+ 'You can find it in the homebridge-hilo logs — look for "Subscribed to updates for location".');
156
+ }
157
+ /**
158
+ * Query the Hilo GraphQL API for the current challenge phase.
159
+ * Inspects the gDState field on each device in the location:
160
+ * ACTIVE → reduction phase
161
+ * PRE_HEAT → preheat phase
162
+ * RECOVERY → recovery phase
163
+ * anything else (OFF, absent) → none
164
+ *
165
+ * Returns the first non-none phase found, or 'none' if no challenge is active.
166
+ */
167
+ async getChallengePhase(locationId) {
168
+ await this.ensureToken();
169
+ const requestBody = JSON.stringify({
170
+ query: GD_STATE_QUERY,
171
+ variables: { locationHiloId: locationId },
172
+ });
173
+ this.log.debug(`Querying Hilo GraphQL for gDState (location: ${locationId})`);
174
+ const res = await httpsRequest('POST', GRAPHQL_HOST, GRAPHQL_PATH, this.apiHeaders(), requestBody);
175
+ if (res.status >= 400) {
176
+ throw new Error(`GraphQL request failed: HTTP ${res.status}: ${res.data}`);
177
+ }
178
+ const json = JSON.parse(res.data);
179
+ if (json.errors?.length) {
180
+ throw new Error(`GraphQL errors: ${json.errors.map((e) => e.message).join(', ')}`);
181
+ }
182
+ const devices = json.data?.getLocation?.devices ?? [];
183
+ this.log.debug(`GraphQL returned ${devices.length} device(s)`);
184
+ for (const device of devices) {
185
+ const state = device.gDState;
186
+ if (!state || state === 'OFF')
187
+ continue;
188
+ this.log.debug(`Device ${device.__typename ?? 'unknown'} gDState: ${state}`);
189
+ if (state === 'ACTIVE')
190
+ return 'reduction';
191
+ if (state === 'PRE_HEAT')
192
+ return 'preheat';
193
+ if (state === 'RECOVERY')
194
+ return 'recovery';
195
+ }
196
+ return 'none';
197
+ }
198
+ }
199
+ exports.HiloApi = HiloApi;
200
+ //# sourceMappingURL=hiloApi.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hiloApi.js","sourceRoot":"","sources":["../src/hiloApi.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import { API } from 'homebridge';
2
+ declare const _default: (api: API) => void;
3
+ export = _default;
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,YAAY,CAAC;yBAGvB,KAAK,GAAG,KAAG,IAAI;AAAzB,kBAEE"}
package/dist/index.js ADDED
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ const platform_1 = require("./platform");
3
+ module.exports = (api) => {
4
+ api.registerPlatform(platform_1.PLATFORM_NAME, platform_1.HiloChallengePlatform);
5
+ };
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,yCAAkE;AAElE,iBAAS,CAAC,GAAQ,EAAQ,EAAE;IAC1B,GAAG,CAAC,gBAAgB,CAAC,wBAAa,EAAE,gCAAqB,CAAC,CAAC;AAC7D,CAAC,CAAC"}
@@ -0,0 +1,18 @@
1
+ import { API, DynamicPlatformPlugin, Logger, PlatformAccessory, PlatformConfig } from 'homebridge';
2
+ export declare const PLATFORM_NAME = "HiloChallenge";
3
+ export declare const PLUGIN_NAME = "homebridge-hilo-challenge";
4
+ export declare class HiloChallengePlatform implements DynamicPlatformPlugin {
5
+ readonly log: Logger;
6
+ readonly config: PlatformConfig;
7
+ readonly homebridgeApi: API;
8
+ private readonly hiloApi;
9
+ private readonly cachedAccessories;
10
+ private readonly sensorAccessories;
11
+ private readonly pollInterval;
12
+ private locationId;
13
+ constructor(log: Logger, config: PlatformConfig, homebridgeApi: API);
14
+ configureAccessory(accessory: PlatformAccessory): void;
15
+ private init;
16
+ private poll;
17
+ }
18
+ //# sourceMappingURL=platform.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"platform.d.ts","sourceRoot":"","sources":["../src/platform.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,qBAAqB,EAAE,MAAM,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAInG,eAAO,MAAM,aAAa,kBAAkB,CAAC;AAC7C,eAAO,MAAM,WAAW,8BAA8B,CAAC;AAOvD,qBAAa,qBAAsB,YAAW,qBAAqB;aAQ/C,GAAG,EAAE,MAAM;aACX,MAAM,EAAE,cAAc;aACtB,aAAa,EAAE,GAAG;IATpC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAU;IAClC,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAA2B;IAC7D,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAwD;IAC1F,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IACtC,OAAO,CAAC,UAAU,CAAuB;gBAGvB,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,cAAc,EACtB,aAAa,EAAE,GAAG;IAUpC,kBAAkB,CAAC,SAAS,EAAE,iBAAiB,GAAG,IAAI;YAKxC,IAAI;YAqCJ,IAAI;CAYnB"}
@@ -0,0 +1,77 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.HiloChallengePlatform = exports.PLUGIN_NAME = exports.PLATFORM_NAME = void 0;
4
+ const hiloApi_1 = require("./hiloApi");
5
+ const challengeAccessory_1 = require("./challengeAccessory");
6
+ exports.PLATFORM_NAME = 'HiloChallenge';
7
+ exports.PLUGIN_NAME = 'homebridge-hilo-challenge';
8
+ const SENSORS = [
9
+ { phase: 'preheat', name: 'Hilo Preheat' },
10
+ { phase: 'reduction', name: 'Hilo Reduction' },
11
+ ];
12
+ class HiloChallengePlatform {
13
+ constructor(log, config, homebridgeApi) {
14
+ this.log = log;
15
+ this.config = config;
16
+ this.homebridgeApi = homebridgeApi;
17
+ this.cachedAccessories = [];
18
+ this.sensorAccessories = new Map();
19
+ this.locationId = null;
20
+ this.pollInterval = config['pollInterval'] ?? 60;
21
+ this.hiloApi = new hiloApi_1.HiloApi(config['refreshToken'], log);
22
+ this.homebridgeApi.on('didFinishLaunching', () => {
23
+ this.init();
24
+ });
25
+ }
26
+ configureAccessory(accessory) {
27
+ this.log.debug(`Restoring cached accessory: ${accessory.displayName}`);
28
+ this.cachedAccessories.push(accessory);
29
+ }
30
+ async init() {
31
+ // Discover location ID
32
+ try {
33
+ const configured = this.config['locationId'];
34
+ this.locationId = configured ?? await this.hiloApi.getLocationId();
35
+ this.log.info(`Using Hilo location ID: ${this.locationId}`);
36
+ }
37
+ catch (err) {
38
+ this.log.error(`Failed to get Hilo location ID: ${err}`);
39
+ this.log.error('Set "locationId" manually in config if auto-discovery fails.');
40
+ return;
41
+ }
42
+ // Register or restore accessories
43
+ for (const { phase, name } of SENSORS) {
44
+ const uuid = this.homebridgeApi.hap.uuid.generate(`hilo-challenge-${phase}`);
45
+ const existing = this.cachedAccessories.find((a) => a.UUID === uuid);
46
+ if (existing) {
47
+ this.log.info(`Restoring sensor: ${existing.displayName}`);
48
+ this.sensorAccessories.set(phase, new challengeAccessory_1.HiloChallengeSensorAccessory(this.homebridgeApi.hap, this.log, existing, phase));
49
+ }
50
+ else {
51
+ this.log.info(`Adding sensor: ${name}`);
52
+ const accessory = new this.homebridgeApi.platformAccessory(name, uuid);
53
+ this.sensorAccessories.set(phase, new challengeAccessory_1.HiloChallengeSensorAccessory(this.homebridgeApi.hap, this.log, accessory, phase));
54
+ this.homebridgeApi.registerPlatformAccessories(exports.PLUGIN_NAME, exports.PLATFORM_NAME, [accessory]);
55
+ }
56
+ }
57
+ // Initial poll then recurring
58
+ await this.poll();
59
+ setInterval(() => this.poll(), this.pollInterval * 1000);
60
+ }
61
+ async poll() {
62
+ if (!this.locationId)
63
+ return;
64
+ try {
65
+ const phase = await this.hiloApi.getChallengePhase(this.locationId);
66
+ this.log.debug(`Current Hilo challenge phase: ${phase}`);
67
+ for (const sensor of this.sensorAccessories.values()) {
68
+ sensor.updatePhase(phase);
69
+ }
70
+ }
71
+ catch (err) {
72
+ this.log.error(`Failed to poll Hilo challenge: ${err}`);
73
+ }
74
+ }
75
+ }
76
+ exports.HiloChallengePlatform = HiloChallengePlatform;
77
+ //# sourceMappingURL=platform.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"platform.js","sourceRoot":"","sources":["../src/platform.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "homebridge-hilo-challenge",
3
+ "version": "0.1.0",
4
+ "description": "Hilo Homebridge plugin — exposes Hilo challenge phases as HomeKit contact sensors",
5
+ "license": "MIT",
6
+ "author": "Justin Prest",
7
+ "main": "dist/index.js",
8
+ "files": [
9
+ "dist",
10
+ "config.schema.json"
11
+ ],
12
+ "scripts": {
13
+ "build": "tsc",
14
+ "watch": "tsc -w",
15
+ "prepublishOnly": "npm run build"
16
+ },
17
+ "homebridge": {
18
+ "displayName": "Hilo Challenge",
19
+ "pluginType": "platform",
20
+ "singular": true
21
+ },
22
+ "keywords": [
23
+ "homebridge-plugin",
24
+ "hilo",
25
+ "hilo-challenge",
26
+ "hydro-quebec",
27
+ "homekit",
28
+ "energy",
29
+ "demand-response"
30
+ ],
31
+ "engines": {
32
+ "node": ">=18.0.0",
33
+ "homebridge": ">=1.6.0"
34
+ },
35
+ "peerDependencies": {
36
+ "homebridge": ">=1.6.0"
37
+ },
38
+ "devDependencies": {
39
+ "@types/node": "^20.0.0",
40
+ "homebridge": "^1.8.0",
41
+ "typescript": "^5.0.0"
42
+ }
43
+ }