homebridge-yoto 0.0.7 → 0.0.9
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/lib/platform.js +70 -50
- package/lib/playerAccessory.js +38 -29
- package/lib/yotoMqtt.js +20 -2
- package/package.json +1 -1
package/lib/platform.js
CHANGED
|
@@ -5,9 +5,10 @@
|
|
|
5
5
|
/** @import { API, DynamicPlatformPlugin, Logger, PlatformAccessory, PlatformConfig } from 'homebridge' */
|
|
6
6
|
/** @import { YotoDevice, YotoPlatformConfig, YotoAccessoryContext } from './types.js' */
|
|
7
7
|
|
|
8
|
+
import { readFile, writeFile, mkdir } from 'fs/promises'
|
|
9
|
+
import { join } from 'path'
|
|
8
10
|
import { YotoAuth } from './auth.js'
|
|
9
11
|
import { YotoApi } from './yotoApi.js'
|
|
10
|
-
import { YotoMqtt } from './yotoMqtt.js'
|
|
11
12
|
import { YotoPlayerAccessory } from './playerAccessory.js'
|
|
12
13
|
import {
|
|
13
14
|
PLATFORM_NAME,
|
|
@@ -44,10 +45,13 @@ export class YotoPlatform {
|
|
|
44
45
|
/** @type {string[]} */
|
|
45
46
|
this.discoveredUUIDs = []
|
|
46
47
|
|
|
48
|
+
// Persistent storage path for tokens
|
|
49
|
+
this.storagePath = api.user.storagePath()
|
|
50
|
+
this.tokenFilePath = join(this.storagePath, 'homebridge-yoto-tokens.json')
|
|
51
|
+
|
|
47
52
|
// Initialize API clients
|
|
48
53
|
this.auth = new YotoAuth(log, this.config.clientId)
|
|
49
54
|
this.yotoApi = new YotoApi(log, this.auth)
|
|
50
|
-
this.yotoMqtt = new YotoMqtt(log)
|
|
51
55
|
|
|
52
56
|
// Set up token refresh callback
|
|
53
57
|
this.yotoApi.setTokenRefreshCallback(this.handleTokenRefresh.bind(this))
|
|
@@ -69,6 +73,9 @@ export class YotoPlatform {
|
|
|
69
73
|
*/
|
|
70
74
|
async initialize () {
|
|
71
75
|
try {
|
|
76
|
+
// Load tokens from persistent storage
|
|
77
|
+
await this.loadTokens()
|
|
78
|
+
|
|
72
79
|
// Check if we have stored credentials
|
|
73
80
|
if (!this.config.accessToken || !this.config.refreshToken) {
|
|
74
81
|
await this.performDeviceFlow()
|
|
@@ -93,15 +100,6 @@ export class YotoPlatform {
|
|
|
93
100
|
}
|
|
94
101
|
throw error
|
|
95
102
|
}
|
|
96
|
-
|
|
97
|
-
// Connect to MQTT with first device ID
|
|
98
|
-
if (this.accessories.size > 0) {
|
|
99
|
-
const firstAccessory = Array.from(this.accessories.values())[0]
|
|
100
|
-
if (firstAccessory) {
|
|
101
|
-
const firstDeviceId = firstAccessory.context.device.deviceId
|
|
102
|
-
await this.yotoMqtt.connect(this.config.accessToken || '', firstDeviceId)
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
103
|
} catch (error) {
|
|
106
104
|
this.log.error(LOG_PREFIX.PLATFORM, 'Initialization failed:', error)
|
|
107
105
|
}
|
|
@@ -122,10 +120,10 @@ export class YotoPlatform {
|
|
|
122
120
|
this.config.refreshToken = tokenResponse.refresh_token || ''
|
|
123
121
|
this.config.tokenExpiresAt = this.auth.calculateExpiresAt(tokenResponse.expires_in)
|
|
124
122
|
|
|
125
|
-
// Save tokens to
|
|
126
|
-
await this.
|
|
123
|
+
// Save tokens to persistent storage
|
|
124
|
+
await this.saveTokens()
|
|
127
125
|
|
|
128
|
-
this.log.info(LOG_PREFIX.PLATFORM, '✓ Authentication successful and saved
|
|
126
|
+
this.log.info(LOG_PREFIX.PLATFORM, '✓ Authentication successful and saved!')
|
|
129
127
|
}
|
|
130
128
|
|
|
131
129
|
/**
|
|
@@ -138,8 +136,8 @@ export class YotoPlatform {
|
|
|
138
136
|
this.config.refreshToken = ''
|
|
139
137
|
this.config.tokenExpiresAt = 0
|
|
140
138
|
|
|
141
|
-
// Save cleared
|
|
142
|
-
await this.
|
|
139
|
+
// Save cleared tokens
|
|
140
|
+
await this.saveTokens()
|
|
143
141
|
|
|
144
142
|
// Restart initialization
|
|
145
143
|
await this.initialize()
|
|
@@ -157,43 +155,62 @@ export class YotoPlatform {
|
|
|
157
155
|
this.config.refreshToken = refreshToken
|
|
158
156
|
this.config.tokenExpiresAt = expiresAt
|
|
159
157
|
|
|
160
|
-
// Save updated tokens to
|
|
161
|
-
this.
|
|
158
|
+
// Save updated tokens to persistent storage
|
|
159
|
+
this.saveTokens().catch(error => {
|
|
162
160
|
this.log.error(LOG_PREFIX.PLATFORM, 'Failed to save refreshed tokens:', error)
|
|
163
161
|
})
|
|
164
162
|
|
|
165
|
-
//
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
163
|
+
// Note: MQTT reconnection is handled by each accessory's own MQTT client
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Load tokens from config or persistent storage
|
|
168
|
+
* Priority: config.json > persistent storage file
|
|
169
|
+
* @returns {Promise<void>}
|
|
170
|
+
*/
|
|
171
|
+
async loadTokens () {
|
|
172
|
+
// First check if tokens are in config.json
|
|
173
|
+
if (this.config.accessToken && this.config.refreshToken) {
|
|
174
|
+
this.log.debug(LOG_PREFIX.PLATFORM, 'Using tokens from config.json')
|
|
175
|
+
return
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Fall back to persistent storage file
|
|
179
|
+
try {
|
|
180
|
+
const data = await readFile(this.tokenFilePath, 'utf-8')
|
|
181
|
+
const tokens = JSON.parse(data)
|
|
182
|
+
|
|
183
|
+
if (tokens.accessToken && tokens.refreshToken) {
|
|
184
|
+
this.config.accessToken = tokens.accessToken
|
|
185
|
+
this.config.refreshToken = tokens.refreshToken
|
|
186
|
+
this.config.tokenExpiresAt = tokens.tokenExpiresAt
|
|
187
|
+
this.log.debug(LOG_PREFIX.PLATFORM, 'Loaded tokens from persistent storage')
|
|
175
188
|
}
|
|
189
|
+
} catch (error) {
|
|
190
|
+
// File doesn't exist or is invalid - not an error on first run
|
|
191
|
+
this.log.debug(LOG_PREFIX.PLATFORM, 'No saved tokens found in storage')
|
|
176
192
|
}
|
|
177
193
|
}
|
|
178
194
|
|
|
179
195
|
/**
|
|
180
|
-
* Save
|
|
196
|
+
* Save tokens to persistent storage
|
|
181
197
|
* @returns {Promise<void>}
|
|
182
198
|
*/
|
|
183
|
-
async
|
|
199
|
+
async saveTokens () {
|
|
184
200
|
try {
|
|
185
|
-
//
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
this.
|
|
191
|
-
|
|
192
|
-
// Fallback for older Homebridge versions
|
|
193
|
-
this.log.debug(LOG_PREFIX.PLATFORM, 'Config updated in memory (restart Homebridge to persist)')
|
|
201
|
+
// Ensure storage directory exists
|
|
202
|
+
await mkdir(this.storagePath, { recursive: true })
|
|
203
|
+
|
|
204
|
+
const tokens = {
|
|
205
|
+
accessToken: this.config.accessToken || '',
|
|
206
|
+
refreshToken: this.config.refreshToken || '',
|
|
207
|
+
tokenExpiresAt: this.config.tokenExpiresAt || 0
|
|
194
208
|
}
|
|
209
|
+
|
|
210
|
+
await writeFile(this.tokenFilePath, JSON.stringify(tokens, null, 2), 'utf-8')
|
|
211
|
+
this.log.debug(LOG_PREFIX.PLATFORM, 'Saved tokens to persistent storage')
|
|
195
212
|
} catch (error) {
|
|
196
|
-
this.log.error(LOG_PREFIX.PLATFORM, 'Failed to save
|
|
213
|
+
this.log.error(LOG_PREFIX.PLATFORM, 'Failed to save tokens:', error)
|
|
197
214
|
throw error
|
|
198
215
|
}
|
|
199
216
|
}
|
|
@@ -267,8 +284,12 @@ export class YotoPlatform {
|
|
|
267
284
|
this.api.updatePlatformAccessories([existingAccessory])
|
|
268
285
|
|
|
269
286
|
// Create handler for this accessory
|
|
270
|
-
|
|
271
|
-
|
|
287
|
+
const handler = new YotoPlayerAccessory(this, typedAccessory)
|
|
288
|
+
|
|
289
|
+
// Initialize accessory (connect MQTT, etc.)
|
|
290
|
+
handler.initialize().catch(error => {
|
|
291
|
+
this.log.error(LOG_PREFIX.PLATFORM, `Failed to initialize ${device.name}:`, error)
|
|
292
|
+
})
|
|
272
293
|
} else {
|
|
273
294
|
// Create new accessory
|
|
274
295
|
this.log.info(LOG_PREFIX.PLATFORM, 'Adding new accessory:', device.name)
|
|
@@ -289,8 +310,12 @@ export class YotoPlatform {
|
|
|
289
310
|
}
|
|
290
311
|
|
|
291
312
|
// Create handler for this accessory
|
|
292
|
-
|
|
293
|
-
|
|
313
|
+
const handler = new YotoPlayerAccessory(this, typedAccessory)
|
|
314
|
+
|
|
315
|
+
// Initialize accessory (connect MQTT, etc.)
|
|
316
|
+
handler.initialize().catch(error => {
|
|
317
|
+
this.log.error(LOG_PREFIX.PLATFORM, `Failed to initialize ${device.name}:`, error)
|
|
318
|
+
})
|
|
294
319
|
|
|
295
320
|
// Register with Homebridge
|
|
296
321
|
this.api.registerPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [accessory])
|
|
@@ -328,11 +353,6 @@ export class YotoPlatform {
|
|
|
328
353
|
*/
|
|
329
354
|
async shutdown () {
|
|
330
355
|
this.log.info(LOG_PREFIX.PLATFORM, 'Shutting down...')
|
|
331
|
-
|
|
332
|
-
try {
|
|
333
|
-
await this.yotoMqtt.disconnect()
|
|
334
|
-
} catch (error) {
|
|
335
|
-
this.log.error(LOG_PREFIX.PLATFORM, 'Error during shutdown:', error)
|
|
336
|
-
}
|
|
356
|
+
// MQTT cleanup is handled by individual accessories
|
|
337
357
|
}
|
|
338
358
|
}
|
package/lib/playerAccessory.js
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
/** @import { YotoPlatform } from './platform.js' */
|
|
7
7
|
/** @import { YotoAccessoryContext, YotoDeviceStatus, YotoPlaybackEvents } from './types.js' */
|
|
8
8
|
|
|
9
|
+
import { YotoMqtt } from './yotoMqtt.js'
|
|
9
10
|
import {
|
|
10
11
|
DEFAULT_MANUFACTURER,
|
|
11
12
|
DEFAULT_MODEL,
|
|
@@ -37,6 +38,9 @@ export class YotoPlayerAccessory {
|
|
|
37
38
|
this.currentEvents = accessory.context.lastEvents || null
|
|
38
39
|
this.lastUpdateTime = Date.now()
|
|
39
40
|
|
|
41
|
+
// Create dedicated MQTT client for this device
|
|
42
|
+
this.mqtt = new YotoMqtt(this.log)
|
|
43
|
+
|
|
40
44
|
this.log.debug(LOG_PREFIX.ACCESSORY, `[${this.device.name}] Initializing accessory`)
|
|
41
45
|
|
|
42
46
|
// Set up services
|
|
@@ -93,8 +97,16 @@ export class YotoPlayerAccessory {
|
|
|
93
97
|
this.activeContentInfo = null
|
|
94
98
|
}
|
|
95
99
|
|
|
96
|
-
//
|
|
97
|
-
|
|
100
|
+
// MQTT connection will be initiated by platform after construction
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Initialize the accessory - connect MQTT and subscribe to updates
|
|
105
|
+
* Called by platform after construction
|
|
106
|
+
* @returns {Promise<void>}
|
|
107
|
+
*/
|
|
108
|
+
async initialize () {
|
|
109
|
+
await this.connectMqtt()
|
|
98
110
|
}
|
|
99
111
|
|
|
100
112
|
/**
|
|
@@ -396,31 +408,28 @@ export class YotoPlayerAccessory {
|
|
|
396
408
|
return
|
|
397
409
|
}
|
|
398
410
|
|
|
411
|
+
// TEMPORARY: Debug logging for MQTT troubleshooting
|
|
412
|
+
this.log.warn(LOG_PREFIX.ACCESSORY, `[${this.device.name}] MQTT Connection Details:`)
|
|
413
|
+
this.log.warn(LOG_PREFIX.ACCESSORY, ` Device ID: ${this.device.deviceId}`)
|
|
414
|
+
this.log.warn(LOG_PREFIX.ACCESSORY, ` Access Token: ${this.platform.config.accessToken}`)
|
|
415
|
+
this.log.warn(LOG_PREFIX.ACCESSORY, ` Token Length: ${this.platform.config.accessToken.length}`)
|
|
416
|
+
|
|
399
417
|
// Connect MQTT with device ID and access token
|
|
400
|
-
await this.
|
|
418
|
+
await this.mqtt.connect(
|
|
401
419
|
this.platform.config.accessToken,
|
|
402
420
|
this.device.deviceId
|
|
403
421
|
)
|
|
404
422
|
|
|
405
423
|
// Subscribe to device topics
|
|
406
|
-
await this.
|
|
407
|
-
} catch (error) {
|
|
408
|
-
this.log.error(LOG_PREFIX.ACCESSORY, `[${this.device.name}] Failed to connect MQTT:`, error)
|
|
409
|
-
}
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
/**
|
|
413
|
-
* Subscribe to MQTT updates for this device
|
|
414
|
-
*/
|
|
415
|
-
async subscribeMqtt () {
|
|
416
|
-
try {
|
|
417
|
-
await this.platform.yotoMqtt.subscribeToDevice(this.device.deviceId, {
|
|
424
|
+
await this.mqtt.subscribeToDevice(this.device.deviceId, {
|
|
418
425
|
onStatus: this.handleStatusUpdate.bind(this),
|
|
419
426
|
onEvents: this.handleEventsUpdate.bind(this),
|
|
420
427
|
onResponse: this.handleCommandResponse.bind(this)
|
|
421
428
|
})
|
|
429
|
+
|
|
430
|
+
this.log.debug(LOG_PREFIX.ACCESSORY, `[${this.device.name}] MQTT connected and subscribed`)
|
|
422
431
|
} catch (error) {
|
|
423
|
-
this.log.error(LOG_PREFIX.ACCESSORY, `[${this.device.name}] Failed to
|
|
432
|
+
this.log.error(LOG_PREFIX.ACCESSORY, `[${this.device.name}] Failed to connect MQTT:`, error)
|
|
424
433
|
}
|
|
425
434
|
}
|
|
426
435
|
|
|
@@ -681,11 +690,11 @@ export class YotoPlayerAccessory {
|
|
|
681
690
|
|
|
682
691
|
try {
|
|
683
692
|
if (value === this.platform.Characteristic.TargetMediaState.PLAY) {
|
|
684
|
-
await this.
|
|
693
|
+
await this.mqtt.resumeCard(this.device.deviceId)
|
|
685
694
|
} else if (value === this.platform.Characteristic.TargetMediaState.PAUSE) {
|
|
686
|
-
await this.
|
|
695
|
+
await this.mqtt.pauseCard(this.device.deviceId)
|
|
687
696
|
} else if (value === this.platform.Characteristic.TargetMediaState.STOP) {
|
|
688
|
-
await this.
|
|
697
|
+
await this.mqtt.stopCard(this.device.deviceId)
|
|
689
698
|
}
|
|
690
699
|
} catch (error) {
|
|
691
700
|
this.log.error(LOG_PREFIX.ACCESSORY, `[${this.device.name}] Failed to set media state:`, error)
|
|
@@ -714,7 +723,7 @@ export class YotoPlayerAccessory {
|
|
|
714
723
|
this.log.debug(LOG_PREFIX.ACCESSORY, `[${this.device.name}] Set volume:`, value)
|
|
715
724
|
|
|
716
725
|
try {
|
|
717
|
-
await this.
|
|
726
|
+
await this.mqtt.setVolume(this.device.deviceId, Number(value))
|
|
718
727
|
} catch (error) {
|
|
719
728
|
this.log.error(LOG_PREFIX.ACCESSORY, `[${this.device.name}] Failed to set volume:`, error)
|
|
720
729
|
throw new this.platform.api.hap.HapStatusError(
|
|
@@ -744,12 +753,12 @@ export class YotoPlayerAccessory {
|
|
|
744
753
|
try {
|
|
745
754
|
if (value) {
|
|
746
755
|
// Mute - set volume to 0
|
|
747
|
-
await this.
|
|
756
|
+
await this.mqtt.setVolume(this.device.deviceId, 0)
|
|
748
757
|
} else {
|
|
749
758
|
// Unmute - restore to a reasonable volume if currently 0
|
|
750
759
|
const currentVolume = this.currentStatus?.userVolume || 0
|
|
751
760
|
const targetVolume = currentVolume === 0 ? 50 : currentVolume
|
|
752
|
-
await this.
|
|
761
|
+
await this.mqtt.setVolume(this.device.deviceId, targetVolume)
|
|
753
762
|
}
|
|
754
763
|
} catch (error) {
|
|
755
764
|
this.log.error(LOG_PREFIX.ACCESSORY, `[${this.device.name}] Failed to set mute:`, error)
|
|
@@ -1038,10 +1047,10 @@ export class YotoPlayerAccessory {
|
|
|
1038
1047
|
try {
|
|
1039
1048
|
if (value === this.platform.Characteristic.Active.INACTIVE) {
|
|
1040
1049
|
// Turn off timer
|
|
1041
|
-
await this.
|
|
1050
|
+
await this.mqtt.setSleepTimer(this.device.deviceId, 0)
|
|
1042
1051
|
} else {
|
|
1043
1052
|
// Turn on with default duration (30 minutes)
|
|
1044
|
-
await this.
|
|
1053
|
+
await this.mqtt.setSleepTimer(this.device.deviceId, 30 * 60)
|
|
1045
1054
|
}
|
|
1046
1055
|
} catch (error) {
|
|
1047
1056
|
this.log.error(LOG_PREFIX.ACCESSORY, `[${this.device.name}] Failed to set sleep timer active:`, error)
|
|
@@ -1080,10 +1089,10 @@ export class YotoPlayerAccessory {
|
|
|
1080
1089
|
|
|
1081
1090
|
if (seconds === 0) {
|
|
1082
1091
|
// Turn off timer
|
|
1083
|
-
await this.
|
|
1092
|
+
await this.mqtt.setSleepTimer(this.device.deviceId, 0)
|
|
1084
1093
|
} else {
|
|
1085
1094
|
// Set timer with specified duration
|
|
1086
|
-
await this.
|
|
1095
|
+
await this.mqtt.setSleepTimer(this.device.deviceId, seconds)
|
|
1087
1096
|
}
|
|
1088
1097
|
} catch (error) {
|
|
1089
1098
|
this.log.error(LOG_PREFIX.ACCESSORY, `[${this.device.name}] Failed to set sleep timer:`, error)
|
|
@@ -1229,10 +1238,10 @@ export class YotoPlayerAccessory {
|
|
|
1229
1238
|
try {
|
|
1230
1239
|
if (value) {
|
|
1231
1240
|
// Turn on - set to white or previous color
|
|
1232
|
-
await this.
|
|
1241
|
+
await this.mqtt.setAmbientLight(this.device.deviceId, 255, 255, 255)
|
|
1233
1242
|
} else {
|
|
1234
1243
|
// Turn off - set to black
|
|
1235
|
-
await this.
|
|
1244
|
+
await this.mqtt.setAmbientLight(this.device.deviceId, 0, 0, 0)
|
|
1236
1245
|
}
|
|
1237
1246
|
} catch (error) {
|
|
1238
1247
|
this.log.error(LOG_PREFIX.ACCESSORY, `[${this.device.name}] Failed to set ambient light on:`, error)
|
|
@@ -1342,7 +1351,7 @@ export class YotoPlayerAccessory {
|
|
|
1342
1351
|
const { r, g, b } = this.hsvToRgb(h, s, v)
|
|
1343
1352
|
|
|
1344
1353
|
// Send MQTT command
|
|
1345
|
-
await this.
|
|
1354
|
+
await this.mqtt.setAmbientLight(this.device.deviceId, r, g, b)
|
|
1346
1355
|
|
|
1347
1356
|
// Clear pending values
|
|
1348
1357
|
this.pendingAmbientHue = undefined
|
package/lib/yotoMqtt.js
CHANGED
|
@@ -65,14 +65,31 @@ export class YotoMqtt {
|
|
|
65
65
|
this.log.info(LOG_PREFIX.MQTT, `Connecting to ${this.brokerUrl}...`)
|
|
66
66
|
|
|
67
67
|
const clientId = `homebridge-yoto-${deviceId}-${Date.now()}`
|
|
68
|
+
const username = `${deviceId}?x-amz-customauthorizer-name=${YOTO_MQTT_AUTH_NAME}`
|
|
69
|
+
|
|
70
|
+
// TEMPORARY: Detailed debug logging for MQTT troubleshooting
|
|
71
|
+
this.log.warn(LOG_PREFIX.MQTT, '='.repeat(60))
|
|
72
|
+
this.log.warn(LOG_PREFIX.MQTT, 'MQTT CONNECTION DETAILS')
|
|
73
|
+
this.log.warn(LOG_PREFIX.MQTT, '='.repeat(60))
|
|
74
|
+
this.log.warn(LOG_PREFIX.MQTT, `Broker URL: ${this.brokerUrl}`)
|
|
75
|
+
this.log.warn(LOG_PREFIX.MQTT, `Client ID: ${clientId}`)
|
|
76
|
+
this.log.warn(LOG_PREFIX.MQTT, `Username: ${username}`)
|
|
77
|
+
this.log.warn(LOG_PREFIX.MQTT, `Device ID: ${deviceId}`)
|
|
78
|
+
this.log.warn(LOG_PREFIX.MQTT, `Token: ${accessToken}`)
|
|
79
|
+
this.log.warn(LOG_PREFIX.MQTT, `Token length: ${accessToken.length}`)
|
|
80
|
+
this.log.warn(LOG_PREFIX.MQTT, 'Port: 443')
|
|
81
|
+
this.log.warn(LOG_PREFIX.MQTT, 'Protocol: wss')
|
|
82
|
+
this.log.warn(LOG_PREFIX.MQTT, 'Keepalive: 300')
|
|
83
|
+
this.log.warn(LOG_PREFIX.MQTT, `Connect Timeout: ${MQTT_CONNECT_TIMEOUT}ms`)
|
|
84
|
+
this.log.warn(LOG_PREFIX.MQTT, '='.repeat(60))
|
|
68
85
|
|
|
69
86
|
this.client = mqtt.connect(this.brokerUrl, {
|
|
70
87
|
keepalive: 300,
|
|
71
88
|
port: 443,
|
|
72
89
|
protocol: 'wss',
|
|
73
|
-
username
|
|
90
|
+
username,
|
|
74
91
|
password: accessToken,
|
|
75
|
-
reconnectPeriod:
|
|
92
|
+
reconnectPeriod: 0, // Disable auto-reconnect - we'll handle reconnection manually
|
|
76
93
|
connectTimeout: MQTT_CONNECT_TIMEOUT,
|
|
77
94
|
clientId,
|
|
78
95
|
clean: true,
|
|
@@ -93,6 +110,7 @@ export class YotoMqtt {
|
|
|
93
110
|
|
|
94
111
|
this.client.on('error', (error) => {
|
|
95
112
|
this.log.error(LOG_PREFIX.MQTT, 'Connection error:', error)
|
|
113
|
+
this.log.error(LOG_PREFIX.MQTT, 'Error details:', JSON.stringify(error, null, 2))
|
|
96
114
|
if (!this.connected) {
|
|
97
115
|
reject(error)
|
|
98
116
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "homebridge-yoto",
|
|
3
3
|
"description": "Control your Yoto players through Apple HomeKit with real-time MQTT updates",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.9",
|
|
5
5
|
"author": "Bret Comnes <bcomnes@gmail.com> (https://bret.io)",
|
|
6
6
|
"bugs": {
|
|
7
7
|
"url": "https://github.com/bcomnes/homebridge-yoto/issues"
|