homebridge-cync-app 0.1.2 → 0.1.5

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/dist/platform.js CHANGED
@@ -18,6 +18,10 @@ const toCyncLogger = (log) => ({
18
18
  */
19
19
  export class CyncAppPlatform {
20
20
  accessories = [];
21
+ configureAccessory(accessory) {
22
+ this.log.info('Restoring cached accessory', accessory.displayName);
23
+ this.accessories.push(accessory);
24
+ }
21
25
  log;
22
26
  api;
23
27
  config;
@@ -91,15 +95,25 @@ export class CyncAppPlatform {
91
95
  this.config = config;
92
96
  this.api = api;
93
97
  // Extract login config from platform config
94
- const cfg = this.config;
95
- const username = (cfg.username ?? cfg.email);
96
- const password = cfg.password;
97
- const twoFactor = cfg.twoFactor;
98
+ const raw = this.config;
99
+ // Canonical config keys: username, password, twoFactor
100
+ // Accept legacy "email" as a fallback source for username, but do not write it back.
101
+ const username = typeof raw.username === 'string'
102
+ ? raw.username
103
+ : typeof raw.email === 'string'
104
+ ? raw.email
105
+ : '';
106
+ const password = typeof raw.password === 'string'
107
+ ? raw.password
108
+ : '';
109
+ const twoFactor = typeof raw.twoFactor === 'string'
110
+ ? raw.twoFactor
111
+ : undefined;
98
112
  const cyncLogger = toCyncLogger(this.log);
99
113
  const tcpClient = new TcpClient(cyncLogger);
100
114
  this.client = new CyncClient(new ConfigClient(cyncLogger), tcpClient, {
101
- email: username ?? '',
102
- password: password ?? '',
115
+ username,
116
+ password,
103
117
  twoFactor,
104
118
  }, this.api.user.storagePath(), cyncLogger);
105
119
  this.tcpClient = tcpClient;
@@ -113,18 +127,17 @@ export class CyncAppPlatform {
113
127
  void this.loadCync();
114
128
  });
115
129
  }
116
- /**
117
- * Called when cached accessories are restored from disk.
118
- */
119
- configureAccessory(accessory) {
120
- this.log.info('Restoring cached accessory', accessory.displayName);
121
- this.accessories.push(accessory);
122
- }
123
130
  async loadCync() {
124
131
  try {
125
- const cfg = this.config;
126
- const username = (cfg.username ?? cfg.email);
127
- const password = cfg.password;
132
+ const raw = this.config;
133
+ const username = typeof raw.username === 'string'
134
+ ? raw.username
135
+ : typeof raw.email === 'string'
136
+ ? raw.email
137
+ : '';
138
+ const password = typeof raw.password === 'string'
139
+ ? raw.password
140
+ : '';
128
141
  if (!username || !password) {
129
142
  this.log.warn('Cync: credentials missing in config.json; skipping cloud login.');
130
143
  return;
@@ -141,7 +154,6 @@ export class CyncAppPlatform {
141
154
  this.cloudConfig = cloudConfig;
142
155
  this.log.info('Cync: cloud configuration loaded; mesh count=%d', cloudConfig.meshes.length);
143
156
  // Ask the CyncClient for the LAN login code derived from stored session.
144
- // If it returns an empty blob, LAN is disabled but cloud still works.
145
157
  let loginCode = new Uint8Array();
146
158
  try {
147
159
  loginCode = this.client.getLanLoginCode();
@@ -151,7 +163,6 @@ export class CyncAppPlatform {
151
163
  }
152
164
  if (loginCode.length > 0) {
153
165
  this.log.info('Cync: LAN login code available (%d bytes); starting TCP transport…', loginCode.length);
154
- // ### 🧩 LAN Transport Bootstrap: wire frame listeners via CyncClient
155
166
  await this.client.startTransport(cloudConfig, loginCode);
156
167
  }
157
168
  else {
@@ -1 +1 @@
1
- {"version":3,"file":"platform.js","sourceRoot":"","sources":["../src/platform.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"platform.js","sourceRoot":"","sources":["../src/platform.ts"],"names":[],"mappings":"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"}
package/eslint.config.js CHANGED
@@ -3,7 +3,11 @@ import tseslint from 'typescript-eslint';
3
3
 
4
4
  export default tseslint.config(
5
5
  {
6
- ignores: ['dist/**'],
6
+ ignores: [
7
+ 'dist/**',
8
+ 'node_modules/**',
9
+ 'homebridge-ui/server.js',
10
+ ],
7
11
  },
8
12
  {
9
13
  rules: {
Binary file
@@ -0,0 +1,182 @@
1
+ <!-- homebridge-ui/public/index.html -->
2
+ <div class="text-center mb-3">
3
+ <img
4
+ src="icon.png"
5
+ alt="Cync App Icon"
6
+ style="width:96px;height:96px;border-radius:5px;"
7
+ />
8
+ </div>
9
+
10
+ <div class="card mb-3">
11
+ <div class="card-body">
12
+ <h3 class="card-title">Homebridge Cync App</h3>
13
+ <p class="card-text">
14
+ Enter your Cync Username, Password, and 6 digit verification code emailed to you by Cync.
15
+ To obtain a fresh code, use the <strong>Request Verification Code</strong> button, then click the Homebridge
16
+ <strong>Save</strong> button at the bottom once you’ve entered the code.
17
+ </p>
18
+ </div>
19
+ </div>
20
+
21
+ <div class="card mb-3">
22
+ <div class="card-body">
23
+ <h5 class="card-title">Cync Cloud Credentials</h5>
24
+
25
+ <div class="mb-3">
26
+ <label for="cyncEmail" class="form-label">Email</label>
27
+ <input type="email" id="cyncEmail" class="form-control" autocomplete="username">
28
+ </div>
29
+
30
+ <div class="mb-3">
31
+ <label for="cyncPassword" class="form-label">Password</label>
32
+ <input type="password" id="cyncPassword" class="form-control" autocomplete="current-password">
33
+ </div>
34
+
35
+ <div class="d-flex flex-wrap gap-2 mb-3">
36
+ <button id="cyncRequestOtpBtn" class="btn btn-secondary">
37
+ Request Verification Code
38
+ </button>
39
+ <button id="cyncSignOutBtn" class="btn btn-outline-danger">
40
+ Sign Out
41
+ </button>
42
+ <span id="cyncStatus" class="align-self-center text-muted ms-2"></span>
43
+ </div>
44
+
45
+ <div class="mb-3">
46
+ <label for="cyncOtp" class="form-label">Verification Code (OTP)</label>
47
+ <input type="text" id="cyncOtp" class="form-control" autocomplete="one-time-code">
48
+ </div>
49
+ </div>
50
+ </div>
51
+ </div>
52
+
53
+ <script>
54
+ homebridge.addEventListener('ready', async () => {
55
+ const emailInput = document.getElementById('cyncEmail');
56
+ const passwordInput = document.getElementById('cyncPassword');
57
+ const otpInput = document.getElementById('cyncOtp');
58
+ const requestOtpBtn = document.getElementById('cyncRequestOtpBtn');
59
+ const signOutBtn = document.getElementById('cyncSignOutBtn');
60
+ const statusEl = document.getElementById('cyncStatus');
61
+
62
+ let pluginConfig = await homebridge.getPluginConfig();
63
+ let cfg = pluginConfig[0] || {};
64
+
65
+ // Helper to update in-memory pluginConfig (persisted when user clicks Save in HB)
66
+ async function updateConfig(partial) {
67
+ cfg = { ...cfg, ...partial };
68
+ pluginConfig = [cfg];
69
+ await homebridge.updatePluginConfig(pluginConfig);
70
+ }
71
+
72
+ function setLockedState(locked) {
73
+ emailInput.disabled = locked;
74
+ passwordInput.disabled = locked;
75
+ otpInput.disabled = locked;
76
+ requestOtpBtn.disabled = locked;
77
+ // Sign Out remains enabled so the user can clear things
78
+ }
79
+
80
+ // Load from config.json into UI
81
+ emailInput.value = cfg.username || '';
82
+ passwordInput.value = cfg.password || '';
83
+ otpInput.value = cfg.twoFactor || '';
84
+
85
+ // Check token status and lock fields if a token exists
86
+ try {
87
+ const status = await homebridge.request('/status');
88
+ if (status && status.ok && status.hasToken) {
89
+ setLockedState(true);
90
+ statusEl.textContent = 'Signed in. Click Sign Out to change credentials.';
91
+ }
92
+ } catch (e) {
93
+ console.error('[cync-ui] /status request failed:', e);
94
+ }
95
+
96
+ // Request OTP email via server.js
97
+ requestOtpBtn.addEventListener('click', async () => {
98
+ const email = emailInput.value.trim();
99
+ const password = passwordInput.value; // do not trim
100
+
101
+ if (!email || !password) {
102
+ homebridge.toast.warning('Enter email and password first.', 'Missing Credentials');
103
+ return;
104
+ }
105
+
106
+ homebridge.showSpinner();
107
+ statusEl.textContent = 'Requesting verification code…';
108
+
109
+ try {
110
+ // Persist latest credentials BEFORE requesting OTP
111
+ await updateConfig({
112
+ username: email,
113
+ password,
114
+ });
115
+
116
+ const res = await homebridge.request('/request-otp', { email });
117
+
118
+ if (res && res.ok) {
119
+ statusEl.textContent = 'Code requested. Check your email.';
120
+ homebridge.toast.success('Verification code sent.', 'OTP Requested');
121
+ } else {
122
+ statusEl.textContent = 'Request may have failed. Check logs.';
123
+ homebridge.toast.warning('Unexpected response requesting code.', 'OTP Request');
124
+ }
125
+ } catch (err) {
126
+ console.error('[cync-ui] Failed to request OTP:', err);
127
+ statusEl.textContent = 'OTP request failed. See logs.';
128
+ homebridge.toast.error('Failed to request 2FA code.', 'Error');
129
+ } finally {
130
+ homebridge.hideSpinner();
131
+ }
132
+ });
133
+
134
+ // OTP field updates config; Homebridge Save persists it
135
+ otpInput.addEventListener('change', async () => {
136
+ try {
137
+ await updateConfig({
138
+ username: emailInput.value.trim(),
139
+ password: passwordInput.value,
140
+ twoFactor: otpInput.value.trim(),
141
+ });
142
+ statusEl.textContent = 'Verification code updated. Click Save.';
143
+ } catch (e) {
144
+ console.error('[cync-ui] Failed to update OTP in config:', e);
145
+ statusEl.textContent = 'Failed to update verification code.';
146
+ }
147
+ });
148
+
149
+ // Sign Out: clear token + unlock fields
150
+ signOutBtn.addEventListener('click', async () => {
151
+ homebridge.showSpinner();
152
+ statusEl.textContent = 'Signing out…';
153
+
154
+ try {
155
+ await homebridge.request('/sign-out');
156
+
157
+ // Clear UI fields
158
+ emailInput.value = '';
159
+ passwordInput.value = '';
160
+ otpInput.value = '';
161
+
162
+ // Clear config (in memory) and unlock inputs
163
+ await updateConfig({
164
+ username: '',
165
+ password: '',
166
+ twoFactor: '',
167
+ });
168
+
169
+ setLockedState(false);
170
+
171
+ statusEl.textContent = 'Signed out. Click Save to apply.';
172
+ homebridge.toast.success('Signed out. Click Save to apply.', 'Signed Out');
173
+ } catch (e) {
174
+ console.error('[cync-ui] Sign-out failed:', e);
175
+ statusEl.textContent = 'Sign-out failed.';
176
+ homebridge.toast.error('Failed to sign out.', 'Error');
177
+ } finally {
178
+ homebridge.hideSpinner();
179
+ }
180
+ });
181
+ });
182
+ </script>
@@ -0,0 +1,62 @@
1
+ // homebridge-ui/server.js
2
+ import { HomebridgePluginUiServer } from '@homebridge/plugin-ui-utils';
3
+ import { ConfigClient } from '../dist/cync/config-client.js';
4
+ import { CyncTokenStore } from '../dist/cync/token-store.js';
5
+
6
+ class CyncUiServer extends HomebridgePluginUiServer {
7
+ constructor() {
8
+ super();
9
+
10
+ this.configClient = new ConfigClient({
11
+ debug: (...a) => console.debug('[cync-ui-config]', ...a),
12
+ info: (...a) => console.info('[cync-ui-config]', ...a),
13
+ warn: (...a) => console.warn('[cync-ui-config]', ...a),
14
+ error: (...a) => console.error('[cync-ui-config]', ...a),
15
+ });
16
+
17
+ this.tokenStore = new CyncTokenStore(this.homebridgeStoragePath);
18
+
19
+ this.onRequest('/request-otp', this.handleRequestOtp.bind(this));
20
+ this.onRequest('/sign-out', this.handleSignOut.bind(this));
21
+ this.onRequest('/status', this.handleStatus.bind(this));
22
+
23
+ this.ready();
24
+ }
25
+
26
+ async handleRequestOtp(payload) {
27
+ const email = typeof payload?.email === 'string' ? payload.email.trim() : '';
28
+ if (!email) {
29
+ return { ok: false, error: 'Missing email' };
30
+ }
31
+
32
+ await this.configClient.sendTwoFactorCode(email);
33
+ return { ok: true };
34
+ }
35
+
36
+ // Delete token file
37
+ async handleSignOut() {
38
+ await this.tokenStore.clear();
39
+ return { ok: true };
40
+ }
41
+
42
+ // Report whether a token exists
43
+ async handleStatus() {
44
+ try {
45
+ const token = await this.tokenStore.load();
46
+ if (!token) {
47
+ return { ok: true, hasToken: false };
48
+ }
49
+ return {
50
+ ok: true,
51
+ hasToken: true,
52
+ userId: token.userId,
53
+ expiresAt: token.expiresAt ?? null,
54
+ };
55
+ } catch {
56
+ // On error, just say "no token"
57
+ return { ok: true, hasToken: false };
58
+ }
59
+ }
60
+ }
61
+
62
+ (() => new CyncUiServer())();
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "homebridge-cync-app",
3
3
  "displayName": "Homebridge Cync App",
4
4
  "type": "module",
5
- "version": "0.1.2",
5
+ "version": "0.1.5",
6
6
  "private": false,
7
7
  "description": "Homebridge plugin that integrates your GE Cync account (via the Cync app/API) and exposes all supported devices: plugs, lights, switches, etc",
8
8
  "author": "Dustin Newell",
@@ -19,17 +19,12 @@
19
19
  "homebridge-plugin",
20
20
  "homebridge",
21
21
  "cync",
22
- "ge",
23
22
  "ge cync",
24
- "ge-lighting",
25
23
  "smart plug",
26
24
  "smart lights",
27
25
  "C by GE"
28
26
  ],
29
27
  "main": "dist/index.js",
30
- "publishConfig": {
31
- "access": "public"
32
- },
33
28
  "homebridge": {
34
29
  "pluginType": "platform",
35
30
  "platform": "CyncAppPlatform"
@@ -45,6 +40,7 @@
45
40
  "watch": "npm run build && npm link && nodemon"
46
41
  },
47
42
  "dependencies": {
43
+ "@homebridge/plugin-ui-utils": "^2.1.2",
48
44
  "homebridge-lib": "^7.1.12"
49
45
  },
50
46
  "devDependencies": {
@@ -42,7 +42,12 @@ export class CyncClient {
42
42
  private switchIdToHomeId: Record<number, string> = {};
43
43
 
44
44
  // Credentials from config.json, used to drive 2FA bootstrap.
45
- private readonly loginConfig: { email: string; password: string; twoFactor?: string };
45
+ //
46
+ // Canonical keys (must match platform + config):
47
+ // - username: login identifier (email address used in Cync app)
48
+ // - password: account password
49
+ // - twoFactor: 6-digit OTP, optional; when present we complete 2FA on restart.
50
+ private readonly loginConfig: { username: string; password: string; twoFactor?: string };
46
51
 
47
52
  // Optional LAN update hook for the platform
48
53
  private lanUpdateHandler: ((update: unknown) => void) | null = null;
@@ -70,7 +75,7 @@ export class CyncClient {
70
75
  constructor(
71
76
  configClient: ConfigClient,
72
77
  tcpClient: TcpClient,
73
- loginConfig: { email: string; password: string; twoFactor?: string },
78
+ loginConfig: { username: string; password: string; twoFactor?: string },
74
79
  storagePath: string,
75
80
  logger?: CyncLogger,
76
81
  ) {
@@ -80,7 +85,18 @@ export class CyncClient {
80
85
 
81
86
  this.loginConfig = loginConfig;
82
87
  this.tokenStore = new CyncTokenStore(storagePath);
88
+
89
+ // One-time sanity log so we can see exactly what was passed in from platform/config.
90
+ this.log.debug(
91
+ 'CyncClient: constructed with loginConfig=%o',
92
+ {
93
+ username: loginConfig.username,
94
+ hasPassword: !!loginConfig.password,
95
+ twoFactor: loginConfig.twoFactor,
96
+ },
97
+ );
83
98
  }
99
+
84
100
  // ### 🧩 LAN Login Code Builder
85
101
  private buildLanLoginCode(authorize: string, userId: number): Uint8Array {
86
102
  const authorizeBytes = Buffer.from(authorize, 'ascii');
@@ -133,17 +149,20 @@ export class CyncClient {
133
149
  }
134
150
 
135
151
  // 2) No stored token – run 2FA bootstrap
136
- const { email, password, twoFactor } = this.loginConfig;
152
+ const { username, password, twoFactor } = this.loginConfig;
137
153
 
138
- if (!email || !password) {
139
- this.log.error('CyncClient: email and password are required to obtain a new token.');
154
+ if (!username || !password) {
155
+ this.log.error('CyncClient: username and password are required to obtain a new token.');
140
156
  return false;
141
157
  }
142
158
 
143
- if (!twoFactor || String(twoFactor).trim() === '') {
159
+ const trimmedCode = typeof twoFactor === 'string' ? twoFactor.trim() : '';
160
+ const hasTwoFactor = trimmedCode.length > 0;
161
+
162
+ if (!hasTwoFactor) {
144
163
  // No 2FA code – request one
145
- this.log.info('Cync: starting 2FA handshake for %s', email);
146
- await this.requestTwoFactorCode(email);
164
+ this.log.info('Cync: starting 2FA handshake for %s', username);
165
+ await this.requestTwoFactorCode(username);
147
166
  this.log.info(
148
167
  'Cync: 2FA code sent to your email. Enter the code as "twoFactor" in the plugin config and restart Homebridge to complete login.',
149
168
  );
@@ -151,11 +170,11 @@ export class CyncClient {
151
170
  }
152
171
 
153
172
  // We have a 2FA code – complete login and persist token
154
- this.log.info('Cync: completing 2FA login for %s', email);
173
+ this.log.info('Cync: completing 2FA login for %s', username);
155
174
  const loginResult = await this.completeTwoFactorLogin(
156
- email,
175
+ username,
157
176
  password,
158
- String(twoFactor).trim(),
177
+ trimmedCode,
159
178
  );
160
179
 
161
180
  // Build LAN login code
@@ -196,16 +215,17 @@ export class CyncClient {
196
215
 
197
216
 
198
217
  /**
199
- * Internal helper: request a 2FA email code using existing authenticate().
200
- */
201
- private async requestTwoFactorCode(email: string): Promise<void> {
202
- await this.authenticate(email);
218
+ * Internal helper: request a 2FA email code using existing authenticate().
219
+ * Accepts the same username value we store in loginConfig (email address for Cync).
220
+ */
221
+ private async requestTwoFactorCode(username: string): Promise<void> {
222
+ await this.authenticate(username);
203
223
  }
204
224
 
205
225
  /**
206
- * Internal helper: complete 2FA login using existing submitTwoFactor().
207
- * This converts CyncLoginSession into the richer shape we want for token storage.
208
- */
226
+ * Internal helper: complete 2FA login using existing submitTwoFactor().
227
+ * This converts CyncLoginSession into the richer shape we want for token storage.
228
+ */
209
229
  private async completeTwoFactorLogin(
210
230
  email: string,
211
231
  password: string,
@@ -218,12 +238,15 @@ export class CyncClient {
218
238
  }
219
239
  > {
220
240
  const session = await this.submitTwoFactor(email, password, code);
241
+
221
242
  // Extract authorize field from session.raw (Cync returns it)
222
243
  const raw = session.raw as Record<string, unknown>;
223
244
  const authorize = typeof raw?.authorize === 'string' ? raw.authorize : undefined;
224
245
 
225
246
  if (!authorize) {
226
- throw new Error('CyncClient: missing "authorize" field from login response; LAN login cannot be generated.');
247
+ throw new Error(
248
+ 'CyncClient: missing "authorize" field from login response; LAN login cannot be generated.',
249
+ );
227
250
  }
228
251
 
229
252
  const s = session as unknown as SessionWithPossibleTokens;
@@ -316,11 +339,11 @@ export class CyncClient {
316
339
  * in the same process, so it works across Homebridge restarts.
317
340
  */
318
341
  public async submitTwoFactor(
319
- email: string,
342
+ username: string,
320
343
  password: string,
321
344
  code: string,
322
345
  ): Promise<CyncLoginSession> {
323
- const trimmedEmail = email.trim();
346
+ const trimmedEmail = username.trim();
324
347
  const trimmedCode = code.trim();
325
348
 
326
349
  this.log.info('CyncClient: completing 2FA login for %s', trimmedEmail);
@@ -347,7 +370,6 @@ export class CyncClient {
347
370
  return session;
348
371
  }
349
372
 
350
-
351
373
  /**
352
374
  * Fetch and cache the cloud configuration (meshes/devices) for the logged-in user.
353
375
  * Also builds HA-style LAN topology mappings: