homebridge-moonside 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,343 @@
1
+ const FIREBASE_API_KEY = 'AIzaSyCC-qQZqcZhxqsbO7GB0nXZShab9gV06Bk';
2
+ const FIREBASE_IDENTITY_URL = 'https://identitytoolkit.googleapis.com/v1/accounts:signInWithPassword';
3
+ const FIREBASE_TOKEN_REFRESH_URL = 'https://securetoken.googleapis.com/v1/token';
4
+ const REALTIME_DATABASE_URL = 'https://moonside-501a1.firebaseio.com';
5
+ const FIRESTORE_RUNQUERY_URL = 'https://firestore.googleapis.com/v1/projects/moonside-501a1/databases/(default)/documents:runQuery';
6
+ export class MoonsideApiClient {
7
+ logger;
8
+ email;
9
+ password;
10
+ apiKey;
11
+ idToken;
12
+ refreshToken;
13
+ tokenExpiry = 0;
14
+ localId;
15
+ eventSourceRestartTimer;
16
+ streamAbortController;
17
+ textDecoder = new TextDecoder();
18
+ constructor(logger, email, password, apiKey = FIREBASE_API_KEY) {
19
+ this.logger = logger;
20
+ this.email = email;
21
+ this.password = password;
22
+ this.apiKey = apiKey;
23
+ }
24
+ async getDeviceState(deviceId) {
25
+ await this.ensureAuthenticated();
26
+ const url = this.buildDeviceUrl(deviceId);
27
+ const path = this.describeDevicePath(deviceId);
28
+ this.logger.debug('GET %s', path);
29
+ const response = await fetch(url);
30
+ if (!response.ok) {
31
+ const details = await response.text();
32
+ throw new Error(`Failed to query Moonside cloud: ${response.status} ${response.statusText} - ${details}`);
33
+ }
34
+ const payload = await response.json();
35
+ this.logger.debug('GET %s -> %s', path, JSON.stringify(payload));
36
+ return payload;
37
+ }
38
+ async sendControl(deviceId, controlData) {
39
+ return this.patchDevice(deviceId, { controlData });
40
+ }
41
+ async patchDevice(deviceId, payload) {
42
+ await this.ensureAuthenticated();
43
+ const url = this.buildDeviceUrl(deviceId);
44
+ const path = this.describeDevicePath(deviceId);
45
+ this.logger.debug('PATCH %s <= %s', path, JSON.stringify(payload));
46
+ const response = await fetch(url, {
47
+ method: 'PATCH',
48
+ headers: { 'Content-Type': 'application/json' },
49
+ body: JSON.stringify(payload),
50
+ });
51
+ if (!response.ok) {
52
+ const details = await response.text();
53
+ throw new Error(`Failed to patch device ${deviceId}: ${response.status} ${response.statusText} - ${details}`);
54
+ }
55
+ const body = await response.json();
56
+ this.logger.debug('PATCH %s -> %s', path, JSON.stringify(body));
57
+ return body;
58
+ }
59
+ async fetchDevices() {
60
+ await this.ensureAuthenticated();
61
+ const url = this.buildDevicesUrl();
62
+ const response = await fetch(url);
63
+ if (!response.ok) {
64
+ const details = await response.text();
65
+ throw new Error(`Failed to fetch devices: ${response.status} ${response.statusText} - ${details}`);
66
+ }
67
+ const data = await response.json();
68
+ const entries = Object.entries(data ?? {});
69
+ return new Map(entries);
70
+ }
71
+ async fetchThemeLibrary() {
72
+ await this.ensureAuthenticated();
73
+ const body = {
74
+ structuredQuery: {
75
+ from: [
76
+ {
77
+ collectionId: 'app-lighting-effects',
78
+ allDescendants: true,
79
+ },
80
+ ],
81
+ },
82
+ };
83
+ const response = await fetch(`${FIRESTORE_RUNQUERY_URL}?key=${this.apiKey}`, {
84
+ method: 'POST',
85
+ headers: {
86
+ 'Content-Type': 'application/json',
87
+ Authorization: `Bearer ${this.idToken}`,
88
+ },
89
+ body: JSON.stringify(body),
90
+ });
91
+ if (!response.ok) {
92
+ const details = await response.text();
93
+ throw new Error(`Failed to load theme catalog: ${response.status} ${response.statusText} - ${details}`);
94
+ }
95
+ const payload = await response.json();
96
+ const themes = new Map();
97
+ for (const entry of payload) {
98
+ const doc = entry.document;
99
+ if (!doc?.fields) {
100
+ continue;
101
+ }
102
+ const nameField = doc.fields.name;
103
+ const commandField = doc.fields.themeControlCode;
104
+ if (!nameField || nameField.stringValue === undefined || !commandField || commandField.stringValue === undefined) {
105
+ continue;
106
+ }
107
+ const params = this.parseFirestoreArray(doc.fields.themeParams);
108
+ const controlData = this.buildThemeCommand(commandField.stringValue, params);
109
+ const id = doc.name?.split('/').pop() ?? nameField.stringValue;
110
+ const def = {
111
+ id,
112
+ name: nameField.stringValue,
113
+ controlData,
114
+ };
115
+ themes.set(nameField.stringValue.toLowerCase(), def);
116
+ }
117
+ return themes;
118
+ }
119
+ async subscribeToDeviceUpdates(onUpdate, onError, onBeforeConnect) {
120
+ const connect = async () => {
121
+ try {
122
+ await this.ensureAuthenticated();
123
+ await onBeforeConnect?.();
124
+ const url = this.buildDevicesUrl();
125
+ this.streamAbortController?.abort();
126
+ this.streamAbortController = new AbortController();
127
+ this.logger.info('Opening Moonside realtime stream');
128
+ await this.consumeEventStream(url, this.streamAbortController.signal, onUpdate);
129
+ }
130
+ catch (error) {
131
+ if (error instanceof DOMException && error.name === 'AbortError') {
132
+ return;
133
+ }
134
+ onError?.(error instanceof Error ? error : new Error(String(error)));
135
+ }
136
+ finally {
137
+ if (!this.streamAbortController?.signal.aborted) {
138
+ this.eventSourceRestartTimer = setTimeout(() => {
139
+ void connect();
140
+ }, 5000);
141
+ }
142
+ }
143
+ };
144
+ void connect();
145
+ return () => {
146
+ this.streamAbortController?.abort();
147
+ if (this.eventSourceRestartTimer) {
148
+ clearTimeout(this.eventSourceRestartTimer);
149
+ }
150
+ };
151
+ }
152
+ parseFirestoreArray(field) {
153
+ if (!field || field.arrayValue?.values === undefined) {
154
+ return [];
155
+ }
156
+ const values = field.arrayValue.values.map(value => {
157
+ if (value.integerValue !== undefined) {
158
+ return Number(value.integerValue);
159
+ }
160
+ if (value.doubleValue !== undefined) {
161
+ return Number(value.doubleValue);
162
+ }
163
+ return 0;
164
+ });
165
+ return values;
166
+ }
167
+ buildThemeCommand(code, params) {
168
+ const suffix = params.map(value => `${value},`).join('');
169
+ return `THEME.${code}.${suffix}`;
170
+ }
171
+ handleStreamPayload(payload, onUpdate) {
172
+ const path = payload.path ?? '/';
173
+ const data = payload.data;
174
+ if (path === '/' && data && typeof data === 'object') {
175
+ const entries = Object.entries(data);
176
+ for (const [deviceId, state] of entries) {
177
+ const decodedId = decodeURIComponent(deviceId);
178
+ onUpdate(decodedId, state);
179
+ }
180
+ return;
181
+ }
182
+ const trimmed = path.startsWith('/') ? path.slice(1) : path;
183
+ if (!trimmed) {
184
+ return;
185
+ }
186
+ const segments = trimmed.split('/');
187
+ const encodedDeviceId = segments.shift();
188
+ if (!encodedDeviceId) {
189
+ return;
190
+ }
191
+ const deviceId = decodeURIComponent(encodedDeviceId);
192
+ if (data === null) {
193
+ onUpdate(deviceId, null);
194
+ return;
195
+ }
196
+ const inflated = this.inflateNestedData(segments, data);
197
+ onUpdate(deviceId, inflated);
198
+ }
199
+ inflateNestedData(keys, value) {
200
+ if (!keys.length) {
201
+ return value;
202
+ }
203
+ return keys.reduceRight((acc, key) => ({ [key]: acc }), value);
204
+ }
205
+ async consumeEventStream(url, signal, onUpdate) {
206
+ const response = await fetch(url, {
207
+ headers: { Accept: 'text/event-stream' },
208
+ signal,
209
+ });
210
+ if (!response.ok || !response.body) {
211
+ const details = await response.text();
212
+ throw new Error(`Failed to open realtime stream: ${response.status} ${response.statusText} - ${details}`);
213
+ }
214
+ const reader = response.body.getReader();
215
+ let buffer = '';
216
+ while (true) {
217
+ const { value, done } = await reader.read();
218
+ if (done) {
219
+ break;
220
+ }
221
+ buffer += this.textDecoder.decode(value, { stream: true });
222
+ buffer = buffer.replace(/\r/g, '');
223
+ let index;
224
+ while ((index = buffer.indexOf('\n\n')) >= 0) {
225
+ const rawEvent = buffer.slice(0, index).trim();
226
+ buffer = buffer.slice(index + 2);
227
+ this.processSseEvent(rawEvent, onUpdate);
228
+ }
229
+ }
230
+ }
231
+ processSseEvent(rawEvent, onUpdate) {
232
+ if (!rawEvent) {
233
+ return;
234
+ }
235
+ const lines = rawEvent.split('\n');
236
+ let eventType = 'message';
237
+ let dataPayload = '';
238
+ for (const line of lines) {
239
+ if (line.startsWith('event:')) {
240
+ eventType = line.slice(6).trim();
241
+ }
242
+ else if (line.startsWith('data:')) {
243
+ dataPayload += line.slice(5).trim();
244
+ }
245
+ }
246
+ if (!dataPayload || (eventType !== 'put' && eventType !== 'patch')) {
247
+ return;
248
+ }
249
+ if (this.logger.isDebugEnabled()) {
250
+ this.logger.debug('Realtime event (%s): %s', eventType, dataPayload);
251
+ }
252
+ try {
253
+ const payload = JSON.parse(dataPayload);
254
+ this.handleStreamPayload(payload, onUpdate);
255
+ }
256
+ catch (error) {
257
+ this.logger.warn('Failed to parse realtime payload: %s', error instanceof Error ? error.message : String(error));
258
+ }
259
+ }
260
+ buildDeviceUrl(deviceId) {
261
+ if (!this.idToken || !this.localId) {
262
+ throw new Error('Moonside client has not authenticated');
263
+ }
264
+ const encodedDevice = encodeURIComponent(deviceId);
265
+ return `${REALTIME_DATABASE_URL}/userDevices/${this.localId}/${encodedDevice}.json?auth=${this.idToken}`;
266
+ }
267
+ buildDevicesUrl() {
268
+ if (!this.idToken || !this.localId) {
269
+ throw new Error('Moonside client has not authenticated');
270
+ }
271
+ return `${REALTIME_DATABASE_URL}/userDevices/${this.localId}.json?auth=${this.idToken}`;
272
+ }
273
+ describeDevicePath(deviceId) {
274
+ if (!this.localId) {
275
+ return `userDevices/<unknown>/${deviceId}`;
276
+ }
277
+ return `userDevices/${this.localId}/${deviceId}`;
278
+ }
279
+ async ensureAuthenticated() {
280
+ const needsAuth = !this.idToken || Date.now() >= this.tokenExpiry;
281
+ if (!needsAuth) {
282
+ return;
283
+ }
284
+ if (!this.refreshToken) {
285
+ await this.login();
286
+ return;
287
+ }
288
+ try {
289
+ await this.refresh();
290
+ }
291
+ catch (error) {
292
+ this.logger.warn('Token refresh failed, logging in again.');
293
+ await this.login();
294
+ }
295
+ }
296
+ async login() {
297
+ this.logger.info('Authenticating with Moonside cloud as %s', this.email);
298
+ const url = `${FIREBASE_IDENTITY_URL}?key=${this.apiKey}`;
299
+ const response = await fetch(url, {
300
+ method: 'POST',
301
+ headers: { 'Content-Type': 'application/json' },
302
+ body: JSON.stringify({
303
+ email: this.email,
304
+ password: this.password,
305
+ returnSecureToken: true,
306
+ }),
307
+ });
308
+ if (!response.ok) {
309
+ const details = await response.text();
310
+ throw new Error(`Moonside login failed: ${response.status} ${response.statusText} - ${details}`);
311
+ }
312
+ const payload = await response.json();
313
+ this.idToken = payload.idToken;
314
+ this.refreshToken = payload.refreshToken;
315
+ this.localId = payload.localId;
316
+ this.tokenExpiry = Date.now() + (parseInt(payload.expiresIn, 10) - 120) * 1000;
317
+ }
318
+ async refresh() {
319
+ if (!this.refreshToken) {
320
+ await this.login();
321
+ return;
322
+ }
323
+ const url = `${FIREBASE_TOKEN_REFRESH_URL}?key=${this.apiKey}`;
324
+ const response = await fetch(url, {
325
+ method: 'POST',
326
+ headers: { 'Content-Type': 'application/json' },
327
+ body: JSON.stringify({
328
+ grant_type: 'refresh_token',
329
+ refresh_token: this.refreshToken,
330
+ }),
331
+ });
332
+ if (!response.ok) {
333
+ const details = await response.text();
334
+ throw new Error(`Token refresh failed: ${response.status} ${response.statusText} - ${details}`);
335
+ }
336
+ const payload = await response.json();
337
+ this.idToken = payload.id_token;
338
+ this.refreshToken = payload.refresh_token;
339
+ this.localId = payload.user_id;
340
+ this.tokenExpiry = Date.now() + (parseInt(payload.expires_in, 10) - 120) * 1000;
341
+ }
342
+ }
343
+ //# sourceMappingURL=moonsideApi.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"moonsideApi.js","sourceRoot":"","sources":["../src/moonsideApi.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,60 @@
1
+ import type { API, Characteristic, DynamicPlatformPlugin, Logging, PlatformAccessory, PlatformConfig, Service } from 'homebridge';
2
+ import { MoonsideApiClient } from './moonsideApi.js';
3
+ import { PluginLogger, type PluginLogLevel } from './logger.js';
4
+ export interface MoonsideDeviceConfig {
5
+ deviceId: string;
6
+ name: string;
7
+ }
8
+ export interface MoonsideDeviceConfig {
9
+ name: string;
10
+ deviceId: string;
11
+ }
12
+ export interface MoonsidePlatformConfig extends PlatformConfig {
13
+ email: string;
14
+ password: string;
15
+ pollingInterval?: number;
16
+ enablePolling?: boolean;
17
+ firebaseApiKey?: string;
18
+ logLevel?: PluginLogLevel;
19
+ themeSwitches?: string[];
20
+ }
21
+ /**
22
+ * HomebridgePlatform
23
+ * This class is the main constructor for your plugin, this is where you should
24
+ * parse the user config and discover/register accessories with Homebridge.
25
+ */
26
+ export declare class MoonsideCloudPlatform implements DynamicPlatformPlugin {
27
+ readonly log: Logging;
28
+ readonly config: MoonsidePlatformConfig;
29
+ readonly api: API;
30
+ readonly Service: typeof Service;
31
+ readonly Characteristic: typeof Characteristic;
32
+ readonly accessories: Map<string, PlatformAccessory>;
33
+ readonly apiClient?: MoonsideApiClient;
34
+ readonly pollingInterval: number;
35
+ readonly enablePolling: boolean;
36
+ readonly logger: PluginLogger;
37
+ private readonly configured;
38
+ private readonly accessoriesByDeviceId;
39
+ private readonly themeAccessories;
40
+ private readonly themeSwitchNames;
41
+ private streamUnsubscribe?;
42
+ private themeDefinitionsCache?;
43
+ constructor(log: Logging, config: MoonsidePlatformConfig, api: API);
44
+ /**
45
+ * This function is invoked when homebridge restores cached accessories from disk at startup.
46
+ * It should be used to set up event handlers for characteristics and update respective values.
47
+ */
48
+ configureAccessory(accessory: PlatformAccessory): void;
49
+ discoverDevices(): Promise<void>;
50
+ private registerOrUpdateAccessory;
51
+ private removeAccessory;
52
+ private startRealtimeStream;
53
+ private syncDeviceSnapshot;
54
+ private refreshDevicesFromCloud;
55
+ private registerOrUpdateThemeAccessory;
56
+ private removeThemeAccessory;
57
+ private refreshThemeAccessories;
58
+ private resolveThemeDefinitions;
59
+ private resolveLogLevel;
60
+ }