17
17
//
18
18
// Supports both Nest REST and Protobuf APIs for communication
19
19
//
20
- // Code version 3 /10/2024
20
+ // Code version 5 /10/2024
21
21
// Mark Hulskamp
22
22
'use strict' ;
23
23
24
- // Define Homebridge module requirements
24
+ // Define HAP-NodeJS module requirements
25
25
import HAP from 'hap-nodejs' ;
26
26
27
27
// Define nodejs module requirements
@@ -30,6 +30,7 @@ import fs from 'node:fs';
30
30
import path from 'node:path' ;
31
31
import { fileURLToPath } from 'node:url' ;
32
32
import { setInterval } from 'node:timers' ;
33
+ import { createRequire } from 'node:module' ;
33
34
34
35
// Import our modules
35
36
import NestAccfactory from './system.js' ;
@@ -44,7 +45,9 @@ HomeKitDevice.HISTORY = HomeKitHistory;
44
45
import Logger from './logger.js' ;
45
46
const log = Logger . withPrefix ( HomeKitDevice . PLATFORM_NAME ) ;
46
47
47
- const __filename = fileURLToPath ( import . meta. url ) ; // Make a defined for JS __dirname
48
+ // Import the package.json file to get the version number
49
+ const { version } = createRequire ( import . meta. url ) ( '../package.json' ) ;
50
+
48
51
const __dirname = path . dirname ( fileURLToPath ( import . meta. url ) ) ; // Make a defined for JS __dirname
49
52
const ACCESSORYPINCODE = '031-45-154' ; // Default HomeKit pairing code
50
53
const CONFIGURATIONFILE = 'Nest_config.json' ; // Default configuration file name
@@ -56,38 +59,35 @@ function loadConfiguration(filename) {
56
59
}
57
60
58
61
let config = undefined ;
62
+ let legacyFormat = false ;
59
63
60
64
try {
61
65
let loadedConfig = JSON . parse ( fs . readFileSync ( filename ) ) ;
62
66
63
67
config = {
64
- nest : { } ,
65
- google : { } ,
66
68
options : { } ,
67
69
devices : { } ,
68
70
} ;
69
71
70
72
// Load in 'current' configuration structure if present
71
73
// Most of the code below is to handle the 'legacy' structure
72
- if ( typeof loadedConfig ?. nest === 'object' ) {
73
- config . nest = loadedConfig . nest ;
74
- }
75
74
if ( typeof loadedConfig ?. Connections ?. Nest === 'object' ) {
75
+ legacyFormat = true ;
76
76
config . nest = loadedConfig . Connections . Nest ;
77
77
}
78
78
if ( typeof loadedConfig ?. SessionToken === 'string' && loadedConfig . SessionToken !== '' ) {
79
+ legacyFormat = true ;
79
80
config . nest = {
80
81
access_token : loadedConfig . SessionToken ,
81
82
fieldTest : false ,
82
83
} ;
83
84
}
84
- if ( typeof loadedConfig ?. google === 'object' ) {
85
- config . google = loadedConfig . google ;
86
- }
87
85
if ( typeof loadedConfig ?. Connections ?. Google === 'object' ) {
86
+ legacyFormat = true ;
88
87
config . google = loadedConfig . Connections . Google ;
89
88
}
90
89
if ( typeof loadedConfig ?. Connections ?. GoogleToken === 'object' ) {
90
+ legacyFormat = true ;
91
91
config . google = loadedConfig . Connections . GoogleToken ;
92
92
}
93
93
if ( typeof loadedConfig ?. options === 'object' ) {
@@ -103,23 +103,50 @@ function loadConfiguration(filename) {
103
103
}
104
104
if ( key === 'EveApp' && typeof value === 'boolean' ) {
105
105
// Evehome app integration
106
+ legacyFormat = true ;
106
107
config . options . eveHistory = value ;
107
108
}
108
109
if ( key === 'Weather' && typeof value === 'boolean' ) {
109
110
// weather device(s)
111
+ legacyFormat = true ;
110
112
config . options . weather = value ;
111
113
}
112
114
if ( key === 'HKSV' && typeof value === 'boolean' ) {
113
115
// HomeKit Secure Video
116
+ legacyFormat = true ;
114
117
config . options . hksv = value ;
115
118
}
116
119
if ( key === 'HomeKitCode' && typeof value === 'string' && value !== '' ) {
117
120
// HomeKit paring code
121
+ legacyFormat = true ;
118
122
config . options . hkPairingCode = value ;
119
123
}
120
124
if ( key === 'Elevation' && isNaN ( value ) === false ) {
125
+ // Weather location elevation
126
+ legacyFormat = true ;
121
127
config . options . elevation = Number ( value ) ;
122
128
}
129
+ if ( typeof value === 'object' && value ?. access_token !== undefined && value ?. access_token !== '' ) {
130
+ // Nest access_token
131
+ config [ key . toLocaleLowerCase ( ) ] = {
132
+ access_token : value ?. access_token ,
133
+ fieldTest : value ?. fieldTest === true ,
134
+ } ;
135
+ }
136
+ if (
137
+ typeof value === 'object' &&
138
+ value ?. issuetoken !== undefined &&
139
+ value ?. issuetoken !== '' &&
140
+ value ?. cookie !== undefined &&
141
+ value ?. cookie !== ''
142
+ ) {
143
+ // Google issue token/cookie
144
+ config [ key . toLocaleLowerCase ( ) ] = {
145
+ issuetoken : value ?. issuetoken ,
146
+ cookie : value ?. cookie ,
147
+ fieldTest : value ?. fieldTest === true ,
148
+ } ;
149
+ }
123
150
124
151
if (
125
152
key !== 'Connections' &&
@@ -134,34 +161,61 @@ function loadConfiguration(filename) {
134
161
// Since key value is an object, and not an object for a value we expect
135
162
// Ssumme its a device configuration for matching serial number
136
163
key = key . toUpperCase ( ) ;
137
- config . devices [ key ] = { } ;
138
164
Object . entries ( value ) . forEach ( ( [ subKey , value ] ) => {
139
165
if ( subKey === 'Exclude' && typeof value === 'boolean' ) {
140
166
// Per device excluding
167
+ legacyFormat = true ;
168
+ if ( config . devices ?. [ key ] === undefined ) {
169
+ config . devices [ key ] = { } ;
170
+ }
141
171
config . devices [ key ] [ 'exclude' ] = value ;
142
172
}
143
173
if ( subKey === 'HumiditySensor' && typeof value === 'boolean' ) {
144
174
// Seperate humidity sensor for this device (Only valid for thermostats)
175
+ legacyFormat = true ;
176
+ if ( config . devices ?. [ key ] === undefined ) {
177
+ config . devices [ key ] = { } ;
178
+ }
145
179
config . devices [ key ] [ 'humiditySensor' ] = value ;
146
180
}
147
181
if ( subKey === 'EveApp' && typeof value === 'boolean' ) {
148
182
// Per device Evehome app integration
183
+ legacyFormat = true ;
184
+ if ( config . devices ?. [ key ] === undefined ) {
185
+ config . devices [ key ] = { } ;
186
+ }
149
187
config . devices [ key ] [ 'eveHistory' ] = value ;
150
188
}
151
189
if ( subKey === 'HKSV' && typeof value === 'boolean' ) {
152
190
// Per device HomeKit Secure Video
191
+ legacyFormat = true ;
192
+ if ( config . devices ?. [ key ] === undefined ) {
193
+ config . devices [ key ] = { } ;
194
+ }
153
195
config . devices [ key ] [ 'hksv' ] = value ;
154
196
}
155
197
if ( subKey === 'Option.indoor_chime_switch' && typeof value === 'boolean' ) {
156
198
// Per device silence indoor chime
199
+ legacyFormat = true ;
200
+ if ( config . devices ?. [ key ] === undefined ) {
201
+ config . devices [ key ] = { } ;
202
+ }
157
203
config . devices [ key ] [ 'chimeSwitch' ] = value ;
158
204
}
159
205
if ( subKey === 'Option.elevation' && isNaN ( value ) === false ) {
160
206
// Per device elevation setting (for weather)
207
+ legacyFormat = true ;
208
+ if ( config . devices ?. [ key ] === undefined ) {
209
+ config . devices [ key ] = { } ;
210
+ }
161
211
config . devices [ key ] [ 'elevation' ] = Number ( value ) ;
162
212
}
163
213
if ( ( subKey === 'HomeKitCode' || subKey === 'hkPairingCode' ) && typeof value === 'string' && value !== '' ) {
164
214
// Per device HomeKit paring code
215
+ legacyFormat = true ;
216
+ if ( config . devices ?. [ key ] === undefined ) {
217
+ config . devices [ key ] = { } ;
218
+ }
165
219
config . devices [ key ] [ 'hkPairingCode' ] = value ;
166
220
}
167
221
if ( subKey === 'DoorbellCooldown' && isNaN ( value ) === false ) {
@@ -170,6 +224,10 @@ function loadConfiguration(filename) {
170
224
// If greather than 1000, assume milliseconds value passed in, so convert to seconds
171
225
value = Math . floor ( value / 1000 ) ;
172
226
}
227
+ legacyFormat = true ;
228
+ if ( config . devices ?. [ key ] === undefined ) {
229
+ config . devices [ key ] = { } ;
230
+ }
173
231
config . devices [ key ] [ 'doorbellCooldown' ] = value ;
174
232
}
175
233
if ( subKey === 'MotionCooldown' && isNaN ( value ) === false ) {
@@ -178,6 +236,10 @@ function loadConfiguration(filename) {
178
236
// If greather than 1000, assume milliseconds value passed in, so convert to seconds
179
237
value = Math . floor ( value / 1000 ) ;
180
238
}
239
+ legacyFormat = true ;
240
+ if ( config . devices ?. [ key ] === undefined ) {
241
+ config . devices [ key ] = { } ;
242
+ }
181
243
config . devices [ key ] [ 'motionCooldown' ] = value ;
182
244
}
183
245
if ( subKey === 'PersonCooldown' && isNaN ( value ) === false ) {
@@ -186,9 +248,17 @@ function loadConfiguration(filename) {
186
248
// If greather than 1000, assume milliseconds value passed in, so convert to seconds
187
249
value = Math . floor ( value / 1000 ) ;
188
250
}
251
+ legacyFormat = true ;
252
+ if ( config . devices ?. [ key ] === undefined ) {
253
+ config . devices [ key ] = { } ;
254
+ }
189
255
config . devices [ key ] [ 'personCooldown' ] = value ;
190
256
}
257
+ legacyFormat = true ;
191
258
if ( subKey . startsWith ( 'External' ) === true && typeof value === 'string' && value !== '' ) {
259
+ if ( config . devices ?. [ key ] === undefined ) {
260
+ config . devices [ key ] = { } ;
261
+ }
192
262
config . devices [ key ] [ 'external' + subKey . substring ( 8 ) ] = value ;
193
263
}
194
264
} ) ;
@@ -200,6 +270,18 @@ function loadConfiguration(filename) {
200
270
config . options . hkPairingCode = ACCESSORYPINCODE ;
201
271
}
202
272
273
+ // See if we flagged any legacy format options, if so, put warning to user to check and upgrade format/options
274
+ if ( legacyFormat === true ) {
275
+ log . prefix = '' ;
276
+ log . warn ( '' ) ;
277
+ log . warn ( 'NOTICE' ) ;
278
+ log . warn ( '> The loaded configuration file contains legacy options. Please review the readme at the link below' ) ;
279
+ log . warn ( '> Consider updating these in your configuration file as the mapping from legacy to current options maybe removed' ) ;
280
+ log . warn ( '> https://github.com/n0rt0nthec4t/homebridge-nest-accfactory/blob/main/src/docker-standalone/README.md' ) ;
281
+ log . warn ( '' ) ;
282
+ log . prefix = HomeKitDevice . PLATFORM_NAME ;
283
+ }
284
+
203
285
// eslint-disable-next-line no-unused-vars
204
286
} catch ( error ) {
205
287
// Empty
@@ -209,7 +291,7 @@ function loadConfiguration(filename) {
209
291
}
210
292
211
293
// Startup code
212
- log . info ( 'Starting ' + __filename + ' using HAP-NodeJS library v' + HAP . HAPLibraryVersion ( ) ) ;
294
+ log . success ( HomeKitDevice . PLUGIN_NAME + ' v ' + version + ' ( HAP v' + HAP . HAPLibraryVersion ( ) + ') (Node v' + process . versions . node + ')' ) ;
213
295
214
296
// Check to see if a configuration file was passed into use and validate if present
215
297
let configurationFile = path . resolve ( __dirname + '/' + CONFIGURATIONFILE ) ;
@@ -228,18 +310,14 @@ if (fs.existsSync(configurationFile) === false) {
228
310
}
229
311
230
312
// Have a configuration file, now load the configuration options
231
- log . info ( 'Configuration will be read from "%s"' , configurationFile ) ;
232
313
let config = loadConfiguration ( configurationFile ) ;
233
314
if ( config === undefined ) {
234
315
log . info ( 'Configuration file contains invalid JSON options' ) ;
235
316
log . info ( 'Exiting.' ) ;
236
317
process . exit ( 1 ) ;
237
318
}
238
- if ( config ?. nest === undefined || config ?. google === undefined ) {
239
- log . info ( 'Either a Nest and/or Google connection details were not specified in the configuration file' ) ;
240
- log . info ( 'Exiting.' ) ;
241
- process . exit ( 1 ) ;
242
- }
319
+
320
+ log . info ( 'Loaded configuration from "%s"' , configurationFile ) ;
243
321
244
322
log . info (
245
323
'Devices will be advertised to HomeKit using "%s" mDNS provider' ,
0 commit comments