homey-api 1.5.30 → 1.7.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.
- package/assets/types/homey-api.d.ts +1 -1
- package/assets/types/homey-api.private.d.ts +10 -1
- package/lib/AthomCloudAPI/StorageAdapter.js +1 -1
- package/lib/AthomCloudAPI/{StorageAdapterNodeJS.js → StorageAdapterMemory.js} +3 -3
- package/lib/AthomCloudAPI.js +109 -20
- package/lib/HomeyAPI/HomeyAPIV2/Device.js +8 -0
- package/lib/HomeyCloudAPI.js +3 -4
- package/lib/Util.js +71 -10
- package/package.json +2 -2
|
@@ -5637,6 +5637,15 @@
|
|
|
5637
5637
|
|
|
5638
5638
|
|
|
5639
5639
|
|
|
5640
|
+
):
|
|
5641
|
+
boolean;
|
|
5642
|
+
|
|
5643
|
+
isReactNative(
|
|
5644
|
+
|
|
5645
|
+
|
|
5646
|
+
|
|
5647
|
+
|
|
5648
|
+
|
|
5640
5649
|
):
|
|
5641
5650
|
boolean;
|
|
5642
5651
|
|
|
@@ -5844,7 +5853,7 @@
|
|
|
5844
5853
|
|
|
5845
5854
|
}
|
|
5846
5855
|
|
|
5847
|
-
export class
|
|
5856
|
+
export class StorageAdapterMemory extends StorageAdapter {
|
|
5848
5857
|
|
|
5849
5858
|
|
|
5850
5859
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* Abstract storage adapter. To be extended by your own, or {@link AthomCloudAPI.StorageAdapterBrowser} or {@link AthomCloudAPI.
|
|
4
|
+
* Abstract storage adapter. To be extended by your own, or {@link AthomCloudAPI.StorageAdapterBrowser} or {@link AthomCloudAPI.StorageAdapterMemory}.
|
|
5
5
|
* @class
|
|
6
6
|
* @memberof AthomCloudAPI
|
|
7
7
|
*/
|
|
@@ -3,12 +3,12 @@
|
|
|
3
3
|
const StorageAdapter = require('./StorageAdapter');
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
* In-memory storage adapter for Node.js.
|
|
6
|
+
* In-memory storage adapter for Node.js or React Native.
|
|
7
7
|
* @class
|
|
8
8
|
* @extends StorageAdapter
|
|
9
9
|
* @memberof AthomCloudAPI
|
|
10
10
|
*/
|
|
11
|
-
class
|
|
11
|
+
class StorageAdapterMemory extends StorageAdapter {
|
|
12
12
|
|
|
13
13
|
constructor() {
|
|
14
14
|
super();
|
|
@@ -34,4 +34,4 @@ class StorageAdapterNodeJS extends StorageAdapter {
|
|
|
34
34
|
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
module.exports =
|
|
37
|
+
module.exports = StorageAdapterMemory;
|
package/lib/AthomCloudAPI.js
CHANGED
|
@@ -9,7 +9,7 @@ const Homey = require('./AthomCloudAPI/Homey');
|
|
|
9
9
|
const Token = require('./AthomCloudAPI/Token');
|
|
10
10
|
const StorageAdapter = require('./AthomCloudAPI/StorageAdapter');
|
|
11
11
|
const StorageAdapterBrowser = require('./AthomCloudAPI/StorageAdapterBrowser');
|
|
12
|
-
const
|
|
12
|
+
const StorageAdapterMemory = require('./AthomCloudAPI/StorageAdapterMemory');
|
|
13
13
|
|
|
14
14
|
class AthomCloudAPI extends API {
|
|
15
15
|
|
|
@@ -18,7 +18,7 @@ class AthomCloudAPI extends API {
|
|
|
18
18
|
static Token = Token;
|
|
19
19
|
static StorageAdapter = StorageAdapter;
|
|
20
20
|
static StorageAdapterBrowser = StorageAdapterBrowser;
|
|
21
|
-
static
|
|
21
|
+
static StorageAdapterMemory = StorageAdapterMemory;
|
|
22
22
|
|
|
23
23
|
static SPECIFICATION = require('../assets/specifications/AthomCloudAPI.json');
|
|
24
24
|
static SPECIFICATION_URL = 'https://api.athom.com/specification.json';
|
|
@@ -35,7 +35,7 @@ and login on that user's Homey.`;
|
|
|
35
35
|
const AthomCloudAPI = require('homey-api/lib/AthomCloudAPI');
|
|
36
36
|
|
|
37
37
|
// Create an AthomCloudAPI instance
|
|
38
|
-
const
|
|
38
|
+
const cloudApi = new {@link AthomCloudAPI AthomCloudAPI}({
|
|
39
39
|
clientId: '5a8d4ca6eb9f7a2c9d6ccf6d',
|
|
40
40
|
clientSecret: 'e3ace394af9f615857ceaa61b053f966ddcfb12a',
|
|
41
41
|
redirectUrl: 'http://localhost',
|
|
@@ -43,30 +43,38 @@ const api = new AthomCloudAPI({
|
|
|
43
43
|
|
|
44
44
|
// Check if we're logged in
|
|
45
45
|
// If not, redirect the user to the OAuth2 dialog
|
|
46
|
-
const loggedIn = await
|
|
46
|
+
const loggedIn = await {@link AthomCloudAPI cloudApi}.{@link AthomCloudAPI#isLoggedIn isLoggedIn}();
|
|
47
47
|
if (!loggedIn) {
|
|
48
|
-
if (
|
|
49
|
-
const token = await
|
|
48
|
+
if ({@link AthomCloudAPI cloudApi}.{@link AthomCloudAPI#hasAuthorizationCode hasAuthorizationCode}()) {
|
|
49
|
+
const token = await {@link AthomCloudAPI cloudApi}.{@link AthomCloudAPI#authenticateWithAuthorizationCode authenticateWithAuthorizationCode}();
|
|
50
50
|
} else {
|
|
51
|
-
window.location.href =
|
|
51
|
+
window.location.href = {@link AthomCloudAPI cloudApi}.{@link AthomCloudAPI#getLoginUrl getLoginUrl}();
|
|
52
52
|
return;
|
|
53
53
|
}
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
// Get the logged in user
|
|
57
|
-
const user = await cloudApi.getAuthenticatedUser();
|
|
57
|
+
const user = await {@link AthomCloudAPI cloudApi}.{@link AthomCloudAPI#getAuthenticatedUser getAuthenticatedUser}();
|
|
58
58
|
|
|
59
59
|
// Get the first Homey of the logged in user
|
|
60
|
-
const homey = await user.getFirstHomey();
|
|
60
|
+
const homey = await {@link AthomCloudAPI.User user}.{@link AthomCloudAPI.User#getFirstHomey getFirstHomey}();
|
|
61
61
|
|
|
62
62
|
// Create a session on this Homey
|
|
63
|
-
const homeyApi = await homey.authenticate();
|
|
63
|
+
const homeyApi = await {@link AthomCloudAPI.Homey homey}.{@link AthomCloudAPI.Homey#authenticate authenticate}();
|
|
64
64
|
|
|
65
|
-
//
|
|
66
|
-
const
|
|
67
|
-
|
|
65
|
+
// Get all Zones from ManagerZones
|
|
66
|
+
const zones = await {@link HomeyAPIV2 homeyApi}.{@link HomeyAPIV2.ManagerZones zones}.{@link HomeyAPIV2.ManagerZones#getZones getZones}();
|
|
67
|
+
|
|
68
|
+
// Get all Devices from ManagerDevices
|
|
69
|
+
const devices = await {@link HomeyAPIV2 homeyApi}.{@link HomeyAPIV2.ManagerDevices devices}.{@link HomeyAPIV2.ManagerDevices#getDevices getDevices}();
|
|
70
|
+
|
|
71
|
+
// Turn all devices on
|
|
72
|
+
for(const {@link HomeyAPIV2.ManagerDevices.Device device} of Object.values(devices)) {
|
|
68
73
|
// Turn device on
|
|
69
|
-
await device.
|
|
74
|
+
await {@link HomeyAPIV2.ManagerDevices.Device device}.{@link HomeyAPIV2.ManagerDevices.Device#setCapabilityValue setCapabilityValue}({
|
|
75
|
+
capabilityId: 'onoff',
|
|
76
|
+
value: true,
|
|
77
|
+
});
|
|
70
78
|
}`;
|
|
71
79
|
|
|
72
80
|
static JSDOC_PARAMS = `
|
|
@@ -75,7 +83,7 @@ for(const device of Object.values(devices)) {
|
|
|
75
83
|
@param {string} opts.redirectUrl
|
|
76
84
|
@param {boolean} [opts.autoRefreshTokens=true]
|
|
77
85
|
@param {AthomCloudAPI.Token} [opts.token=null]
|
|
78
|
-
@param {AthomCloudAPI.StorageAdapter} [opts.store={@link AthomCloudAPI.StorageAdapterBrowser} or {@link AthomCloudAPI.
|
|
86
|
+
@param {AthomCloudAPI.StorageAdapter} [opts.store={@link AthomCloudAPI.StorageAdapterBrowser} or {@link AthomCloudAPI.StorageAdapterMemory}]`;
|
|
79
87
|
|
|
80
88
|
constructor({
|
|
81
89
|
clientId,
|
|
@@ -85,7 +93,7 @@ for(const device of Object.values(devices)) {
|
|
|
85
93
|
token = null,
|
|
86
94
|
store = Util.isBrowser()
|
|
87
95
|
? new StorageAdapterBrowser()
|
|
88
|
-
: new
|
|
96
|
+
: new StorageAdapterMemory(),
|
|
89
97
|
...args
|
|
90
98
|
} = {}) {
|
|
91
99
|
super({ ...args });
|
|
@@ -304,10 +312,11 @@ for(const device of Object.values(devices)) {
|
|
|
304
312
|
body.append('grant_type', 'client_credentials');
|
|
305
313
|
|
|
306
314
|
const response = await Util.fetch(`${this.baseUrl}/oauth2/token`, {
|
|
307
|
-
body,
|
|
315
|
+
body: body.toString(),
|
|
308
316
|
method: 'post',
|
|
309
317
|
headers: {
|
|
310
318
|
Authorization: `Basic ${Util.base64(`${this.__clientId}:${this.__clientSecret}`)}`,
|
|
319
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
311
320
|
},
|
|
312
321
|
});
|
|
313
322
|
|
|
@@ -332,6 +341,13 @@ for(const device of Object.values(devices)) {
|
|
|
332
341
|
return this.__token;
|
|
333
342
|
}
|
|
334
343
|
|
|
344
|
+
/**
|
|
345
|
+
* Authenticate with an authorization code.
|
|
346
|
+
* @param {Object} [opts]
|
|
347
|
+
* @param {String} opts.code - Default to `?code=...` when in a browser.
|
|
348
|
+
* @param {Boolean} [opts.removeCodeFromHistory=true] - Remove `?code=...` from the URL in the address bar.
|
|
349
|
+
* @returns {Promise<AthomCloudAPI.Token>}
|
|
350
|
+
*/
|
|
335
351
|
async authenticateWithAuthorizationCode({
|
|
336
352
|
code,
|
|
337
353
|
removeCodeFromHistory = true,
|
|
@@ -358,10 +374,11 @@ for(const device of Object.values(devices)) {
|
|
|
358
374
|
body.append('code', code);
|
|
359
375
|
|
|
360
376
|
const response = await Util.fetch(`${this.baseUrl}/oauth2/token`, {
|
|
361
|
-
body,
|
|
377
|
+
body: body.toString(),
|
|
362
378
|
method: 'post',
|
|
363
379
|
headers: {
|
|
364
380
|
Authorization: `Basic ${Util.base64(`${this.__clientId}:${this.__clientSecret}`)}`,
|
|
381
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
365
382
|
},
|
|
366
383
|
});
|
|
367
384
|
|
|
@@ -418,10 +435,11 @@ for(const device of Object.values(devices)) {
|
|
|
418
435
|
body.append('password', password);
|
|
419
436
|
|
|
420
437
|
const response = await Util.fetch(`${this.baseUrl}/oauth2/token`, {
|
|
421
|
-
body,
|
|
438
|
+
body: body.toString(),
|
|
422
439
|
method: 'post',
|
|
423
440
|
headers: {
|
|
424
441
|
Authorization: `Basic ${Util.base64(`${this.__clientId}:${this.__clientSecret}`)}`,
|
|
442
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
425
443
|
},
|
|
426
444
|
});
|
|
427
445
|
|
|
@@ -462,10 +480,11 @@ for(const device of Object.values(devices)) {
|
|
|
462
480
|
body.append('refresh_token', this.__token.refresh_token);
|
|
463
481
|
|
|
464
482
|
const response = await Util.fetch(`${this.baseUrl}/oauth2/token`, {
|
|
465
|
-
body,
|
|
483
|
+
body: body.toString(),
|
|
466
484
|
method: 'post',
|
|
467
485
|
headers: {
|
|
468
486
|
Authorization: `Basic ${Util.base64(`${this.__clientId}:${this.__clientSecret}`)}`,
|
|
487
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
469
488
|
},
|
|
470
489
|
});
|
|
471
490
|
|
|
@@ -500,6 +519,76 @@ for(const device of Object.values(devices)) {
|
|
|
500
519
|
return this.__refreshTokenPromise;
|
|
501
520
|
}
|
|
502
521
|
|
|
522
|
+
/**
|
|
523
|
+
* Update the currently authenticated user.
|
|
524
|
+
*
|
|
525
|
+
* @private
|
|
526
|
+
* @param {Object} [opts]
|
|
527
|
+
* @param {String} [opts.firstname]
|
|
528
|
+
* @param {String} [opts.lastname]
|
|
529
|
+
* @param {String} [opts.email]
|
|
530
|
+
* @returns {Promise<AthomCloudAPI.User>}
|
|
531
|
+
*/
|
|
532
|
+
async updateUserMe({
|
|
533
|
+
firstname,
|
|
534
|
+
lastname,
|
|
535
|
+
email,
|
|
536
|
+
}) {
|
|
537
|
+
const me = await this.getAuthenticatedUser();
|
|
538
|
+
return this.updateUser({
|
|
539
|
+
id: me._id,
|
|
540
|
+
user: {
|
|
541
|
+
firstname,
|
|
542
|
+
lastname,
|
|
543
|
+
email,
|
|
544
|
+
},
|
|
545
|
+
});
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
/**
|
|
549
|
+
* Update the currently authenticated user's avatar.
|
|
550
|
+
*
|
|
551
|
+
* @private
|
|
552
|
+
* @param {Buffer} imageBuffer Buffer of the new avatat
|
|
553
|
+
* @param {"jpg"|"jpeg"|"png"|"gif"} imageType Type of the new avatar
|
|
554
|
+
* @returns {Promise<Object>}
|
|
555
|
+
*/
|
|
556
|
+
async updateUserMeAvatar(imageBuffer, imageType) {
|
|
557
|
+
if (!Buffer.isBuffer(imageBuffer)) {
|
|
558
|
+
throw new Error('Invalid Image. Expected Buffer.');
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
if (!imageType) {
|
|
562
|
+
throw new Error('Missing Image Type');
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
if (!['jpg', 'png', 'gif'].includes(imageType)) {
|
|
566
|
+
throw new Error(`Invalid Image Type: ${imageType}`);
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
if (imageType === 'jpg') {
|
|
570
|
+
imageType = 'jpeg';
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
const me = await this.getAuthenticatedUser();
|
|
574
|
+
const body = Buffer.concat([
|
|
575
|
+
Buffer.from(`--__X_HOMEY_BOUNDARY__\r\nContent-Disposition: form-data; name="avatar"; filename="avatar"\r\nContent-Type: image/${imageType}\r\n\r\n`),
|
|
576
|
+
Buffer.from(imageBuffer),
|
|
577
|
+
Buffer.from('\r\n--__X_HOMEY_BOUNDARY__--\r\n'),
|
|
578
|
+
]);
|
|
579
|
+
|
|
580
|
+
return this.call({
|
|
581
|
+
method: 'POST',
|
|
582
|
+
path: `/user/${me._id}/avatar`,
|
|
583
|
+
headers: {
|
|
584
|
+
'Content-Type': 'multipart/form-data; boundary="__X_HOMEY_BOUNDARY__"',
|
|
585
|
+
'Content-Length': body.length,
|
|
586
|
+
},
|
|
587
|
+
body,
|
|
588
|
+
bodyJSON: false,
|
|
589
|
+
});
|
|
590
|
+
}
|
|
591
|
+
|
|
503
592
|
}
|
|
504
593
|
|
|
505
594
|
module.exports = AthomCloudAPI;
|
|
@@ -24,6 +24,14 @@ class Device extends Item {
|
|
|
24
24
|
* @param {number|boolean|string} listener.value
|
|
25
25
|
* @returns {HomeyAPIV2.ManagerDevices.Device.DeviceCapability}
|
|
26
26
|
* @function HomeyAPIV2.ManagerDevices.Device#makeCapabilityInstance
|
|
27
|
+
* @example
|
|
28
|
+
*
|
|
29
|
+
* const onOffInstance = device.makeCapabilityInstance('onoff', value => {
|
|
30
|
+
* console.log('Device onoff changed to:', value);
|
|
31
|
+
* });
|
|
32
|
+
*
|
|
33
|
+
* // Turn on
|
|
34
|
+
* onOffInstance.setValue(true).catch(console.error);
|
|
27
35
|
*/
|
|
28
36
|
makeCapabilityInstance(capabilityId, listener) {
|
|
29
37
|
this.connect().catch(err => {
|
package/lib/HomeyCloudAPI.js
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const fetch = require('node-fetch');
|
|
4
|
-
|
|
5
3
|
const API = require('./API');
|
|
6
4
|
const APIError = require('./APIError');
|
|
5
|
+
const Util = require('./Util');
|
|
7
6
|
|
|
8
7
|
class HomeyCloudAPI extends API {
|
|
9
8
|
|
|
@@ -26,7 +25,7 @@ class HomeyCloudAPI extends API {
|
|
|
26
25
|
* @returns {string} result.region - e.g. `eu-central-1`
|
|
27
26
|
*/
|
|
28
27
|
static async getClosestRegion() {
|
|
29
|
-
const res = await fetch(`https://${HomeyCloudAPI.SPECIFICATION.host}`);
|
|
28
|
+
const res = await Util.fetch(`https://${HomeyCloudAPI.SPECIFICATION.host}`);
|
|
30
29
|
if (!res.ok) {
|
|
31
30
|
throw new Error(res.statusText || 'Unknown Error');
|
|
32
31
|
}
|
|
@@ -56,7 +55,7 @@ class HomeyCloudAPI extends API {
|
|
|
56
55
|
* @returns {Promise<object>} result
|
|
57
56
|
*/
|
|
58
57
|
async getSystemStatus({ secret }) {
|
|
59
|
-
const res = await fetch(`${this.baseUrl}/api/system/status`, {
|
|
58
|
+
const res = await Util.fetch(`${this.baseUrl}/api/system/status`, {
|
|
60
59
|
headers: {
|
|
61
60
|
'X-Homey-Secret': secret,
|
|
62
61
|
},
|
package/lib/Util.js
CHANGED
|
@@ -18,13 +18,22 @@ class Util {
|
|
|
18
18
|
* @returns {Promise}
|
|
19
19
|
*/
|
|
20
20
|
static async fetch(...args) {
|
|
21
|
-
|
|
21
|
+
if (this.isReactNative()) {
|
|
22
|
+
return fetch(...args);
|
|
23
|
+
}
|
|
24
|
+
|
|
22
25
|
if (this.isBrowser()) {
|
|
23
26
|
return window.fetch(...args);
|
|
24
27
|
}
|
|
25
28
|
|
|
26
|
-
|
|
27
|
-
|
|
29
|
+
if (this.isNodeJS()) {
|
|
30
|
+
const fetch = require('node-fetch');
|
|
31
|
+
return fetch(...args);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (typeof fetch !== 'undefined') {
|
|
35
|
+
return fetch(...args);
|
|
36
|
+
}
|
|
28
37
|
}
|
|
29
38
|
|
|
30
39
|
/**
|
|
@@ -81,17 +90,26 @@ class Util {
|
|
|
81
90
|
return window.location.protocol === 'http:';
|
|
82
91
|
}
|
|
83
92
|
|
|
93
|
+
/**
|
|
94
|
+
* @returns {boolean}
|
|
95
|
+
*/
|
|
96
|
+
static isReactNative() {
|
|
97
|
+
return (typeof navigator !== 'undefined' && navigator.product === 'ReactNative');
|
|
98
|
+
}
|
|
99
|
+
|
|
84
100
|
/**
|
|
85
101
|
* @returns {boolean}
|
|
86
102
|
*/
|
|
87
103
|
static isBrowser() {
|
|
88
|
-
|
|
104
|
+
if (this.isReactNative()) return false;
|
|
105
|
+
return (typeof document !== 'undefined' && typeof document.window !== 'undefined');
|
|
89
106
|
}
|
|
90
107
|
|
|
91
108
|
/**
|
|
92
109
|
* @returns {boolean}
|
|
93
110
|
*/
|
|
94
111
|
static isNodeJS() {
|
|
112
|
+
if (this.isReactNative()) return false;
|
|
95
113
|
return (typeof process !== 'undefined');
|
|
96
114
|
}
|
|
97
115
|
|
|
@@ -119,19 +137,58 @@ class Util {
|
|
|
119
137
|
}
|
|
120
138
|
|
|
121
139
|
/**
|
|
140
|
+
* This method encodes a string into a base64 string.
|
|
141
|
+
* It's provided as Util because Node.js uses `Buffer`,
|
|
142
|
+
* browsers use `btoa` and React Native doesn't provide anything.
|
|
122
143
|
* @param {string} input - Input
|
|
123
144
|
* @returns {string} - Base64 encoded output
|
|
124
145
|
*/
|
|
125
|
-
static base64(
|
|
126
|
-
|
|
127
|
-
|
|
146
|
+
static base64(s) {
|
|
147
|
+
function btoaLookup(index) {
|
|
148
|
+
if (index >= 0 && index < 64) {
|
|
149
|
+
const keystr = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
|
|
150
|
+
return keystr[index];
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Throw INVALID_CHARACTER_ERR exception here -- won't be hit in the tests.
|
|
154
|
+
return undefined;
|
|
128
155
|
}
|
|
129
156
|
|
|
130
|
-
if (typeof
|
|
131
|
-
|
|
157
|
+
if (typeof s !== 'string') {
|
|
158
|
+
throw new Error('Invalid Input');
|
|
132
159
|
}
|
|
133
160
|
|
|
134
|
-
|
|
161
|
+
let i;
|
|
162
|
+
|
|
163
|
+
// "The btoa() method must throw an "InvalidCharacterError" DOMException if
|
|
164
|
+
// data contains any character whose code point is greater than U+00FF."
|
|
165
|
+
for (i = 0; i < s.length; i++) {
|
|
166
|
+
if (s.charCodeAt(i) > 255) {
|
|
167
|
+
return null;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
let out = '';
|
|
171
|
+
for (i = 0; i < s.length; i += 3) {
|
|
172
|
+
const groupsOfSix = [undefined, undefined, undefined, undefined];
|
|
173
|
+
groupsOfSix[0] = s.charCodeAt(i) >> 2;
|
|
174
|
+
groupsOfSix[1] = (s.charCodeAt(i) & 0x03) << 4;
|
|
175
|
+
if (s.length > i + 1) {
|
|
176
|
+
groupsOfSix[1] |= s.charCodeAt(i + 1) >> 4;
|
|
177
|
+
groupsOfSix[2] = (s.charCodeAt(i + 1) & 0x0f) << 2;
|
|
178
|
+
}
|
|
179
|
+
if (s.length > i + 2) {
|
|
180
|
+
groupsOfSix[2] |= s.charCodeAt(i + 2) >> 6;
|
|
181
|
+
groupsOfSix[3] = s.charCodeAt(i + 2) & 0x3f;
|
|
182
|
+
}
|
|
183
|
+
for (let j = 0; j < groupsOfSix.length; j++) {
|
|
184
|
+
if (typeof groupsOfSix[j] === 'undefined') {
|
|
185
|
+
out += '=';
|
|
186
|
+
} else {
|
|
187
|
+
out += btoaLookup(groupsOfSix[j]);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
return out;
|
|
135
192
|
}
|
|
136
193
|
|
|
137
194
|
/**
|
|
@@ -161,6 +218,10 @@ class Util {
|
|
|
161
218
|
return process.env[key] || null;
|
|
162
219
|
}
|
|
163
220
|
|
|
221
|
+
if (this.isReactNative()) {
|
|
222
|
+
return null;
|
|
223
|
+
}
|
|
224
|
+
|
|
164
225
|
return null;
|
|
165
226
|
}
|
|
166
227
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "homey-api",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.7.0",
|
|
4
4
|
"description": "Homey API",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"types": "assets/types/homey-api.d.ts",
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
"eslint": "^7.32.0",
|
|
54
54
|
"eslint-config-athom": "^2.1.1",
|
|
55
55
|
"fs-extra": "^10.0.0",
|
|
56
|
-
"homey-jsdoc-template": "github:athombv/homey-jsdoc-template#1.
|
|
56
|
+
"homey-jsdoc-template": "github:athombv/homey-jsdoc-template#1.5.1",
|
|
57
57
|
"http-server": "^0.12.3",
|
|
58
58
|
"jsdoc": "^3.6.7",
|
|
59
59
|
"jsdoc-to-markdown": "^7.1.0",
|