hoffmation-base 0.0.1

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.
Files changed (108) hide show
  1. package/.eslintrc.js +27 -0
  2. package/.prettierrc.js +9 -0
  3. package/LICENSE +21 -0
  4. package/README.md +1 -0
  5. package/index.js +1 -0
  6. package/models/connectionCallbacks.ts +13 -0
  7. package/models/daytime.ts +3 -0
  8. package/models/deviceConfig.ts +8 -0
  9. package/models/dimmerSettings.ts +5 -0
  10. package/models/lampSettings.ts +5 -0
  11. package/models/ledSettings.ts +19 -0
  12. package/models/logLevel.ts +9 -0
  13. package/models/persistence/BasicRoomInfo.ts +3 -0
  14. package/models/persistence/DailyMovementCount.ts +3 -0
  15. package/models/persistence/RoomDetailInfo.ts +4 -0
  16. package/models/persistence/temperaturDataPoint.ts +12 -0
  17. package/models/persistence/todaysCount.ts +3 -0
  18. package/models/rooms/RoomBase.ts +357 -0
  19. package/models/rooms/RoomSettings/RoomSettings.ts +159 -0
  20. package/models/rooms/RoomSettings/hmIPRoomSettings.ts +53 -0
  21. package/models/rooms/RoomSettings/iRoomDefaultSettings.ts +17 -0
  22. package/models/rooms/RoomSettings/readme.md +18 -0
  23. package/models/rooms/RoomSettings/zigbeeRoomSettings.ts +51 -0
  24. package/models/rooms/iRoomImportEnforcer.ts +3 -0
  25. package/models/rooms/readme.md +11 -0
  26. package/models/temperaturSettings.ts +22 -0
  27. package/models/timeCallback.ts +90 -0
  28. package/package.json +57 -0
  29. package/server/config/config-readme.md +19 -0
  30. package/server/config/iConfig.ts +53 -0
  31. package/server/devices/DeviceInfo.ts +66 -0
  32. package/server/devices/Griffe.ts +31 -0
  33. package/server/devices/Heizgruppen.ts +91 -0
  34. package/server/devices/Rollos.ts +48 -0
  35. package/server/devices/deviceUpdater.ts +72 -0
  36. package/server/devices/devices.ts +189 -0
  37. package/server/devices/groups/fensterGroup.ts +175 -0
  38. package/server/devices/groups/heatGroup.ts +32 -0
  39. package/server/devices/groups/lampenGroup.ts +88 -0
  40. package/server/devices/groups/praesenzGroup.ts +182 -0
  41. package/server/devices/groups/smokeGroup.ts +16 -0
  42. package/server/devices/groups/sonosGroup.ts +33 -0
  43. package/server/devices/groups/tasterGroup.ts +48 -0
  44. package/server/devices/groups/waterGroup.ts +16 -0
  45. package/server/devices/hmIPDevices/Fenster.ts +114 -0
  46. package/server/devices/hmIPDevices/FensterPosition.ts +5 -0
  47. package/server/devices/hmIPDevices/TuerPosition.ts +4 -0
  48. package/server/devices/hmIPDevices/hmIpBewegung.ts +126 -0
  49. package/server/devices/hmIPDevices/hmIpDevice.ts +90 -0
  50. package/server/devices/hmIPDevices/hmIpDeviceType.ts +14 -0
  51. package/server/devices/hmIPDevices/hmIpGriff.ts +143 -0
  52. package/server/devices/hmIPDevices/hmIpHeizgruppe.ts +172 -0
  53. package/server/devices/hmIPDevices/hmIpHeizung.ts +69 -0
  54. package/server/devices/hmIPDevices/hmIpLampe.ts +119 -0
  55. package/server/devices/hmIPDevices/hmIpPraezenz.ts +99 -0
  56. package/server/devices/hmIPDevices/hmIpRoll.ts +133 -0
  57. package/server/devices/hmIPDevices/hmIpTaste.ts +72 -0
  58. package/server/devices/hmIPDevices/hmIpTaster.ts +73 -0
  59. package/server/devices/hmIPDevices/hmIpTherm.ts +19 -0
  60. package/server/devices/hmIPDevices/hmIpTuer.ts +115 -0
  61. package/server/devices/hmIPDevices/hmIpWippe.ts +55 -0
  62. package/server/devices/iDeviceUpdater.ts +4 -0
  63. package/server/devices/iIoBrokerDevice.ts +44 -0
  64. package/server/devices/wledDevice.ts +124 -0
  65. package/server/devices/zigbee/ZigbeeActuator.ts +113 -0
  66. package/server/devices/zigbee/zigbeeAquaraVibra.ts +171 -0
  67. package/server/devices/zigbee/zigbeeAquaraWater.ts +94 -0
  68. package/server/devices/zigbee/zigbeeBlitzShp.ts +77 -0
  69. package/server/devices/zigbee/zigbeeDevice.ts +115 -0
  70. package/server/devices/zigbee/zigbeeDeviceType.ts +13 -0
  71. package/server/devices/zigbee/zigbeeHeimanSmoke.ts +99 -0
  72. package/server/devices/zigbee/zigbeeIkeaSteckdose.ts +31 -0
  73. package/server/devices/zigbee/zigbeeIlluActuator.ts +37 -0
  74. package/server/devices/zigbee/zigbeeIlluDimmer.ts +165 -0
  75. package/server/devices/zigbee/zigbeeIlluLampe.ts +33 -0
  76. package/server/devices/zigbee/zigbeeIlluLedRGBCCT.ts +137 -0
  77. package/server/ioBroker/connection.ts +1655 -0
  78. package/server/ioBroker/ioBroker.main.ts +99 -0
  79. package/server/ioBroker/socketIOAuthInfo.ts +5 -0
  80. package/server/ioBroker/socketIOConnectOptions.ts +6 -0
  81. package/server/ioBroker/socketIOLogging.ts +29 -0
  82. package/server/ioBroker/socketIOVisCommand.ts +11 -0
  83. package/server/services/HTTPSOptions.ts +14 -0
  84. package/server/services/Sonos/mp3-server.ts +75 -0
  85. package/server/services/Sonos/polly-service.ts +100 -0
  86. package/server/services/Sonos/sonos-service.ts +199 -0
  87. package/server/services/Telegram/telegram-Commands.ts +215 -0
  88. package/server/services/Telegram/telegram-service.ts +171 -0
  89. package/server/services/Telegram/telegramMessageCalback.ts +11 -0
  90. package/server/services/calendar/m/303/274ll-service.ts +224 -0
  91. package/server/services/dbo/persist.ts +125 -0
  92. package/server/services/https-service.ts +71 -0
  93. package/server/services/log-service.ts +69 -0
  94. package/server/services/news-service.ts +81 -0
  95. package/server/services/settings-service.ts +23 -0
  96. package/server/services/time-callback-service.ts +223 -0
  97. package/server/services/utils/ringstorage.ts +24 -0
  98. package/server/services/utils/utils.ts +52 -0
  99. package/server/services/weather/weather-alert.ts +7 -0
  100. package/server/services/weather/weather-current.ts +26 -0
  101. package/server/services/weather/weather-daily.ts +22 -0
  102. package/server/services/weather/weather-feelsLike.ts +6 -0
  103. package/server/services/weather/weather-hourly.ts +17 -0
  104. package/server/services/weather/weather-item.ts +6 -0
  105. package/server/services/weather/weather-minutes.ts +4 -0
  106. package/server/services/weather/weather-service.ts +277 -0
  107. package/server/services/weather/weather-temp.ts +8 -0
  108. package/tsconfig.json +59 -0
@@ -0,0 +1,1655 @@
1
+ /* eslint-disable prefer-rest-params */
2
+ import { IncomingMessage } from 'http';
3
+ import { SocketIOAuthInfo } from './socketIOAuthInfo';
4
+ import { SocketIOConnectOpts } from './socketIOConnectOptions';
5
+ import { SocketIoLogging, SocketIoLogLevel } from './socketIOLogging';
6
+ import { SocketIOVisCommand } from './socketIOVisCommand';
7
+ import { ConnectionCallbacks } from '../../models/connectionCallbacks';
8
+ import io from 'socket.io-client';
9
+ import { Utils } from '../services/utils/utils';
10
+
11
+ let session: unknown;
12
+ let app: unknown;
13
+ let socketSession: unknown;
14
+ let storage: unknown;
15
+ const socketNamespace: string = '';
16
+ const socketUrl: string = '';
17
+ const socketForceWebSockets: boolean = true;
18
+
19
+ export class IOBrokerConnection {
20
+ private _isExecutedInBrowser: boolean = false;
21
+ private _authInfo: SocketIOAuthInfo = new SocketIOAuthInfo();
22
+ private _authRunning: boolean = false;
23
+ private _cmdData: unknown;
24
+ private _cmdQueue?: Array<{ func: string; args: IArguments }> = new Array<{ func: string; args: IArguments }>();
25
+ private _connCallbacks: ConnectionCallbacks = new ConnectionCallbacks();
26
+ private _cmdInstance: string = '';
27
+ private _countDown: number = 0;
28
+ private _defaultMode: number = 0x644;
29
+ private _disconnectedSince?: Date;
30
+ private _enums?: {
31
+ [groupName: string]: Record<string, ioBroker.Enum>;
32
+ }; // used if _useStorage === true
33
+ private _isAuthDone: boolean = false;
34
+ private _isAuthRequired: boolean = false;
35
+ private _isConnected: boolean = false;
36
+ private _isSecure: boolean = false;
37
+ private _lastTimer?: Date;
38
+ private _namespace: string = 'vis.0';
39
+ private _objects?: Record<string, ioBroker.Object>; // used if _useStorage === true
40
+ private _onConnChange: unknown;
41
+ private _onUpdate: unknown;
42
+ private _reconnectInterval: number = 10000; // reconnect interval
43
+ private _reloadInterval: number = 30; // if connection was absent longer than 30 seconds
44
+ private _socket: SocketIOClient.Socket;
45
+ private _type: string = 'socket.io'; // [SignalR | socket.io | local]
46
+ private _timer?: NodeJS.Timeout;
47
+ private _timeout: number = 0; // 0 - use transport default timeout to detect disconnect
48
+ private _user: string = '';
49
+ private _useStorage: boolean = false;
50
+ private _connectInterval?: NodeJS.Timeout;
51
+ private _countInterval?: NodeJS.Timeout;
52
+ private _gettingStates?: number;
53
+
54
+ //#region GetterSetter
55
+
56
+ /**
57
+ * Getter isExecutedInBrowser
58
+ * @return {boolean }
59
+ */
60
+ public get isExecutedInBrowser(): boolean {
61
+ return this._isExecutedInBrowser;
62
+ }
63
+
64
+ /**
65
+ * Getter enums
66
+ * @return {unknown}
67
+ */
68
+ public get enums():
69
+ | {
70
+ [groupName: string]: Record<string, ioBroker.Enum>;
71
+ }
72
+ | undefined {
73
+ return this._enums;
74
+ }
75
+
76
+ /**
77
+ * Getter isAuthDone
78
+ * @return {boolean }
79
+ */
80
+ public get isAuthDone(): boolean {
81
+ return this._isAuthDone;
82
+ }
83
+
84
+ /**
85
+ * Getter namespace
86
+ * @return {string }
87
+ */
88
+ public get namespace(): string {
89
+ return this._namespace;
90
+ }
91
+
92
+ /**
93
+ * Getter objects
94
+ * @return {unknown}
95
+ */
96
+ public get objects(): Record<string, ioBroker.Object> | undefined {
97
+ return this._objects;
98
+ }
99
+
100
+ /**
101
+ * Getter type
102
+ * @return {string }
103
+ */
104
+ public get type(): string {
105
+ return this._type;
106
+ }
107
+
108
+ /**
109
+ * Getter timeout
110
+ * @return {number }
111
+ */
112
+ public get timeout(): number {
113
+ return this._timeout;
114
+ }
115
+
116
+ /**
117
+ * Getter user
118
+ * @return {unknown}
119
+ */
120
+ public get user(): string {
121
+ return this._user;
122
+ }
123
+
124
+ /**
125
+ * Setter enums
126
+ * @param {unknown} value
127
+ */
128
+ public set enums(
129
+ value:
130
+ | {
131
+ [groupName: string]: Record<string, ioBroker.Enum>;
132
+ }
133
+ | undefined,
134
+ ) {
135
+ this._enums = value;
136
+ }
137
+
138
+ /**
139
+ * Setter namespace
140
+ * @param {string } value
141
+ */
142
+ public set namespace(value: string) {
143
+ this._namespace = value;
144
+ }
145
+
146
+ /**
147
+ * Setter objects
148
+ * @param {unknown} value
149
+ */
150
+ public set objects(value: Record<string, ioBroker.Object> | undefined) {
151
+ this._objects = value;
152
+ }
153
+
154
+ /**
155
+ * Setter type
156
+ * @param {string } value
157
+ */
158
+ public set type(value: string) {
159
+ this._type = value;
160
+ }
161
+
162
+ /**
163
+ * Setter timeout
164
+ * @param {number } value
165
+ */
166
+ public set timeout(value: number) {
167
+ this._timeout = value;
168
+ }
169
+
170
+ /**
171
+ * Setter user
172
+ * @param {unknown} value
173
+ */
174
+ public set user(value: string) {
175
+ this._user = value;
176
+ }
177
+
178
+ //#endregion
179
+
180
+ public constructor(
181
+ connOptions: SocketIOConnectOpts = {},
182
+ connCallbacks: ConnectionCallbacks = new ConnectionCallbacks(),
183
+ objectsRequired: boolean = false,
184
+ ) {
185
+ // init namespace
186
+ if (typeof socketNamespace !== 'undefined') this.namespace = socketNamespace;
187
+
188
+ if (!connOptions.name) connOptions.name = this.namespace;
189
+
190
+ // To start vis as local use one of:
191
+ // - start vis from directory with name local, e.g. c:/blbla/local/ioBroker.vis/www/index.html
192
+ // - do not create "_socket/info.js" file in "www" directory
193
+ // - create "_socket/info.js" file with
194
+ // var socketUrl = "local"; var socketSession = ""; sysLang="en";
195
+ // in this case you can overwrite browser language settings
196
+ if (
197
+ this._isExecutedInBrowser &&
198
+ (document.URL.split('/local/')[1] ||
199
+ (typeof socketUrl === 'undefined' && !connOptions.connLink) ||
200
+ (typeof socketUrl !== 'undefined' && socketUrl === 'local'))
201
+ ) {
202
+ this._type = 'local';
203
+ }
204
+
205
+ if (typeof session !== 'undefined') {
206
+ const user = session.get('user');
207
+ if (user) {
208
+ this._authInfo.user = user;
209
+ this._authInfo.hash = session.get('hash');
210
+ this._authInfo.salt = session.get('salt');
211
+ }
212
+ }
213
+
214
+ this._connCallbacks = connCallbacks;
215
+
216
+ let connLink = connOptions.connLink;
217
+ if (!connOptions.connLink && this._isExecutedInBrowser) {
218
+ connLink = window.localStorage.getItem('connLink');
219
+ }
220
+
221
+ // Connection data from "/_socket/info.js"
222
+ if (!connLink && typeof socketUrl !== 'undefined') connLink = socketUrl;
223
+ if (!connOptions.socketSession && typeof socketSession !== 'undefined') connOptions.socketSession = socketSession;
224
+ if (connOptions.socketForceWebSockets === undefined && typeof socketForceWebSockets !== 'undefined') {
225
+ connOptions.socketForceWebSockets = socketForceWebSockets;
226
+ }
227
+
228
+ connOptions.socketSession = connOptions.socketSession || 'nokey';
229
+
230
+ let url;
231
+ if (connLink) {
232
+ url = connLink;
233
+ if (typeof connLink !== 'undefined') {
234
+ if (connLink[0] === ':') connLink = location.protocol + '://' + location.hostname + connLink;
235
+ }
236
+ } else {
237
+ url = location.protocol + '//' + location.host;
238
+ }
239
+
240
+ const opts: SocketIOClient.ConnectOpts = {
241
+ query: 'key=' + connOptions.socketSession,
242
+ reconnectionDelayMax: 10000,
243
+ reconnectionAttempts: Infinity,
244
+ reconnection: false,
245
+ upgrade: !connOptions.socketForceWebSockets,
246
+ rememberUpgrade: connOptions.socketForceWebSockets,
247
+ transports: connOptions.socketForceWebSockets ? ['websocket'] : undefined,
248
+ };
249
+
250
+ SocketIoLogging.writeLog(SocketIoLogLevel.Trace, `Going to create socket for url ${url}`);
251
+ console.log(opts);
252
+ this._socket = io(url, opts);
253
+ SocketIoLogging.writeLog(SocketIoLogLevel.Trace, 'Created socket');
254
+
255
+ this._socket.on('connect', () => {
256
+ SocketIoLogging.writeLog(SocketIoLogLevel.Trace, 'In Connect callback');
257
+ if (this._disconnectedSince) {
258
+ const offlineTime = new Date().getTime() - this._disconnectedSince.getTime();
259
+ SocketIoLogging.writeLog(SocketIoLogLevel.Debug, 'was offline for ' + offlineTime / 1000 + 's');
260
+
261
+ // reload whole page if no connection longer than some period
262
+ if (this._isExecutedInBrowser && this._reloadInterval && offlineTime > this._reloadInterval * 1000) {
263
+ window.location.reload();
264
+ }
265
+
266
+ this._disconnectedSince = undefined;
267
+ }
268
+
269
+ if (this._connectInterval) {
270
+ clearInterval(this._connectInterval);
271
+ this._connectInterval = undefined;
272
+ }
273
+ if (this._countInterval) {
274
+ clearInterval(this._countInterval);
275
+ this._countInterval = undefined;
276
+ }
277
+
278
+ if (this.isExecutedInBrowser) {
279
+ const elem = document.getElementById('server-disconnect');
280
+ if (elem) elem.style.display = 'none';
281
+ }
282
+
283
+ this._socket.emit('name', connOptions.name);
284
+ SocketIoLogging.writeLog(SocketIoLogLevel.Debug, new Date().toISOString() + ' Connected => authenticate');
285
+ Utils.guardedTimeout(
286
+ () => {
287
+ const wait = Utils.guardedTimeout(
288
+ () => {
289
+ SocketIoLogging.writeLog(SocketIoLogLevel.Error, 'No answer from server');
290
+ if (this._isExecutedInBrowser) window.location.reload();
291
+ },
292
+ 3000,
293
+ this,
294
+ );
295
+
296
+ this._socket.emit('authenticate', (isOk: boolean, isSecure: boolean) => {
297
+ clearTimeout(wait);
298
+ SocketIoLogging.writeLog(SocketIoLogLevel.Debug, new Date().toISOString() + ' Authenticated: ' + isOk);
299
+ if (isOk) {
300
+ this._onAuth(objectsRequired, isSecure);
301
+ } else {
302
+ SocketIoLogging.writeLog(SocketIoLogLevel.Debug, 'permissionError');
303
+ }
304
+ });
305
+ },
306
+ 50,
307
+ this,
308
+ );
309
+ });
310
+
311
+ this._socket.on('reauthenticate', () => {
312
+ if (this._connCallbacks.onConnChange) {
313
+ this._connCallbacks.onConnChange(false);
314
+ if (typeof app !== 'undefined') app.onConnChange(false);
315
+ }
316
+ SocketIoLogging.writeLog(SocketIoLogLevel.Warn, 'reauthenticate');
317
+ if (this._isExecutedInBrowser) window.location.reload();
318
+ });
319
+
320
+ this._socket.on('connect_error', (err: unknown) => {
321
+ SocketIoLogging.writeLog(SocketIoLogLevel.Error, `Couldn't Connect --> Reconecting (Error: ${err})`);
322
+
323
+ this.reconnect(connOptions);
324
+ });
325
+
326
+ this._socket.on('disconnect', () => {
327
+ this._disconnectedSince = new Date();
328
+
329
+ // called only once when connection lost (and it was here before)
330
+ this._isConnected = false;
331
+ if (this._connCallbacks.onConnChange !== undefined) {
332
+ Utils.guardedTimeout(
333
+ () => {
334
+ if (typeof this._connCallbacks.onConnChange !== 'undefined') {
335
+ this._connCallbacks.onConnChange(this._isConnected);
336
+ }
337
+ if (typeof app !== 'undefined') app.onConnChange(this._isConnected);
338
+ },
339
+ 5000,
340
+ this,
341
+ );
342
+ }
343
+
344
+ // reconnect
345
+ this.reconnect(connOptions);
346
+ });
347
+
348
+ // after reconnect the "connect" event will be called
349
+ this._socket.on('reconnect', () => {
350
+ const discoSinceTime: number = this._disconnectedSince === undefined ? 0 : this._disconnectedSince?.getTime();
351
+ const offlineTime = new Date().getTime() - discoSinceTime;
352
+ SocketIoLogging.writeLog(SocketIoLogLevel.Info, `was offline for ${offlineTime / 1000}s`);
353
+
354
+ // reload whole page if no connection longer than one minute
355
+ if (this._reloadInterval && offlineTime > this._reloadInterval * 1000) {
356
+ window.location.reload();
357
+ }
358
+ // anyway "on connect" is called
359
+ });
360
+
361
+ this._socket.on('objectChange', (id: string, obj: ioBroker.Object) => {
362
+ // If cache used
363
+ if (this._useStorage && typeof storage !== 'undefined') {
364
+ const objects = this._objects || storage.get('objects');
365
+ if (objects) {
366
+ if (obj) {
367
+ objects[id] = obj;
368
+ } else {
369
+ if (objects[id]) delete objects[id];
370
+ }
371
+ storage.set('objects', objects);
372
+ }
373
+ }
374
+
375
+ if (this._connCallbacks.onObjectChange) {
376
+ this._connCallbacks.onObjectChange(id, obj);
377
+ }
378
+ });
379
+
380
+ this._socket.on('stateChange', (id: string, state: ioBroker.State) => {
381
+ if (!id || state === null || typeof state !== 'object') return;
382
+
383
+ if (this._connCallbacks.onCommand && id === this.namespace + '.control.command') {
384
+ if (state.ack) return;
385
+
386
+ if (
387
+ state.val &&
388
+ typeof state.val === 'string' &&
389
+ state.val[0] === '{' &&
390
+ state.val[state.val.length - 1] === '}'
391
+ ) {
392
+ try {
393
+ state.val = JSON.parse(state.val);
394
+ } catch (e) {
395
+ SocketIoLogging.writeLog(
396
+ SocketIoLogLevel.Debug,
397
+ 'Command seems to be an object, but cannot parse it: ' + state.val,
398
+ );
399
+ }
400
+ }
401
+ // if command is an object {instance: 'iii', command: 'cmd', data: 'ddd'}
402
+ if (state.val && typeof state.val === 'object' && (state.val as unknown).instance) {
403
+ // vis only:
404
+ const visCommand: SocketIOVisCommand = new SocketIOVisCommand(state.val as unknown);
405
+ if (this._connCallbacks.onCommand(visCommand.instance, visCommand.command, visCommand.data)) {
406
+ // clear state
407
+ this.setState(id, { val: '', ack: true });
408
+ }
409
+ } else {
410
+ if (this._connCallbacks.onCommand(this._cmdInstance, state.val, this._cmdData)) {
411
+ // clear state
412
+ this.setState(id, { val: '', ack: true });
413
+ }
414
+ }
415
+ } else if (id === this.namespace + '.control.data') {
416
+ this._cmdData = state.val;
417
+ } else if (id === this.namespace + '.control.instance') {
418
+ this._cmdInstance = state.val as string;
419
+ } else if (this._connCallbacks.onUpdate) {
420
+ this._connCallbacks.onUpdate(id, state);
421
+ }
422
+ });
423
+
424
+ this._socket.on('permissionError', (err: Error) => {
425
+ if (this._connCallbacks.onError) {
426
+ /* {
427
+ command:
428
+ type:
429
+ operation:
430
+ arg:
431
+ }*/
432
+ this._connCallbacks.onError(err);
433
+ } else {
434
+ SocketIoLogging.writeLog(SocketIoLogLevel.Debug, 'permissionError');
435
+ }
436
+ });
437
+ }
438
+
439
+ private _checkConnection(pFunc: unknown, pArguments: IArguments): boolean {
440
+ if (!this._isConnected) {
441
+ SocketIoLogging.writeLog(SocketIoLogLevel.Warn, 'No connection!');
442
+ return false;
443
+ }
444
+
445
+ if (this._queueCmdIfRequired(pFunc, pArguments)) {
446
+ SocketIoLogging.writeLog(SocketIoLogLevel.Warn, 'Command queued');
447
+ return false;
448
+ }
449
+
450
+ //socket.io
451
+ if (this._socket === null) {
452
+ SocketIoLogging.writeLog(SocketIoLogLevel.Warn, 'socket.io not initialized');
453
+ return false;
454
+ }
455
+ return true;
456
+ }
457
+
458
+ private _monitor(): void {
459
+ if (this._timer !== undefined) return;
460
+ const ts: number = new Date().getTime();
461
+ const lastTimerTime: number = this._lastTimer === undefined ? 0 : this._lastTimer.getTime();
462
+ if (this._reloadInterval && ts - lastTimerTime > this._reloadInterval * 1000) {
463
+ // It seems, that PC was in a sleep => Reload page to request authentication anew
464
+ if (this._isExecutedInBrowser) window.location.reload();
465
+ } else {
466
+ this._lastTimer = new Date(ts);
467
+ }
468
+ this._timer = Utils.guardedTimeout(
469
+ () => {
470
+ this._timer = undefined;
471
+ this._monitor();
472
+ },
473
+ 10000,
474
+ this,
475
+ );
476
+ }
477
+
478
+ private _onAuth(pObjectsRequired: boolean, pIsSecure: boolean): void {
479
+ this._isSecure = pIsSecure;
480
+
481
+ if (this._isSecure) {
482
+ this._lastTimer = new Date();
483
+ this._monitor();
484
+ }
485
+
486
+ this._socket.emit('subscribe', '*');
487
+ if (pObjectsRequired) this._socket.emit('subscribeObjects', '*');
488
+
489
+ if (this._isConnected === true) {
490
+ // This seems to be a reconnect because we're already connected!
491
+ // -> prevent firing onConnChange twice
492
+ return;
493
+ }
494
+
495
+ this._isConnected = true;
496
+ if (this._connCallbacks.onConnChange) {
497
+ Utils.guardedNewThread(() => {
498
+ this._socket.emit('authEnabled', (auth: unknown, user: string) => {
499
+ this._user = user;
500
+ if (typeof this._connCallbacks.onConnChange !== 'undefined') {
501
+ this._connCallbacks.onConnChange(this._isConnected);
502
+ }
503
+ if (typeof app !== 'undefined') app.onConnChange(this._isConnected);
504
+ });
505
+ }, this);
506
+ }
507
+ }
508
+
509
+ public reconnect(pConnOptions: unknown): void {
510
+ // reconnect
511
+ if (this._connectInterval !== undefined || (pConnOptions.mayReconnect && !pConnOptions.mayReconnect())) {
512
+ return;
513
+ }
514
+
515
+ this._connectInterval = Utils.guardedInterval(
516
+ () => {
517
+ SocketIoLogging.writeLog(SocketIoLogLevel.Debug, 'Trying connect...');
518
+ this._socket.connect();
519
+ this._countDown = Math.floor(this._reconnectInterval / 1000);
520
+ SocketIoLogging.writeLog(
521
+ SocketIoLogLevel.Trace,
522
+ `Connection.ts: Connection Retry Countdown ${this._countDown}`,
523
+ );
524
+ },
525
+ this._reconnectInterval,
526
+ this,
527
+ );
528
+
529
+ this._countDown = Math.floor(this._reconnectInterval / 1000);
530
+ SocketIoLogging.writeLog(SocketIoLogLevel.Trace, `Connection.ts: Connection Retry Countdown ${this._countDown}`);
531
+
532
+ this._countInterval = Utils.guardedInterval(
533
+ () => {
534
+ this._countDown--;
535
+ SocketIoLogging.writeLog(
536
+ SocketIoLogLevel.Trace,
537
+ `Connection.ts: Connection Retry Countdown ${this._countDown}`,
538
+ );
539
+ },
540
+ 1000,
541
+ this,
542
+ );
543
+ }
544
+
545
+ // FIXME: CallBack Type
546
+ public logout(callback: unknown): void {
547
+ if (!this._isConnected) {
548
+ SocketIoLogging.writeLog(SocketIoLogLevel.Debug, 'No connection!');
549
+ return;
550
+ }
551
+
552
+ this._socket.emit('logout', callback);
553
+ }
554
+
555
+ // FIXME: CallBack Type
556
+ public getVersion(callback: unknown): void {
557
+ if (!this._checkConnection('getVersion', arguments)) {
558
+ return;
559
+ }
560
+
561
+ this._socket.emit(
562
+ 'getVersion',
563
+ // FIXME: version Type
564
+ (error: unknown, version: string) => {
565
+ if (callback) {
566
+ callback(error, version);
567
+ }
568
+ },
569
+ );
570
+ }
571
+
572
+ // FIXME: CallBack Type
573
+ private _checkAuth(callback: unknown) {
574
+ if (!this._isConnected) {
575
+ SocketIoLogging.writeLog(SocketIoLogLevel.Debug, '_checkAuth: No connection!');
576
+ return;
577
+ }
578
+ //socket.io
579
+ if (this._socket === null) {
580
+ SocketIoLogging.writeLog(SocketIoLogLevel.DeepTrace, '_checkAuth: socket.io not initialized');
581
+ return;
582
+ }
583
+ this._socket.emit(
584
+ 'getVersion',
585
+ // FIXME: CallBack Type
586
+ (error: unknown, version: unknown) => {
587
+ if (callback) {
588
+ SocketIoLogging.writeLog(SocketIoLogLevel.DeepTrace, '_checkAuth: socket.io getVersion Callback');
589
+ callback(error, version);
590
+ }
591
+ },
592
+ );
593
+ }
594
+
595
+ public readFile(filename: string, callback: ioBroker.ReadFileCallback, isRemote: boolean): void {
596
+ if (!callback) throw 'No callback set';
597
+
598
+ if (this._type === 'local') {
599
+ try {
600
+ const data = storage.get(filename);
601
+ callback(null, data ? JSON.parse(storage.get(filename)) : null);
602
+ } catch (err) {
603
+ callback(err, undefined);
604
+ }
605
+ } else {
606
+ if (!this._checkConnection('readFile', arguments)) return;
607
+
608
+ if (!isRemote && typeof app !== 'undefined') {
609
+ app.readLocalFile(filename.replace(/^\/vis\.0\//, ''), callback);
610
+ } else {
611
+ let adapter = this.namespace;
612
+ if (filename[0] === '/') {
613
+ const p = filename.split('/');
614
+ adapter = p[1];
615
+ p.splice(0, 2);
616
+ filename = p.join('/');
617
+ }
618
+
619
+ this._socket.emit(
620
+ 'readFile',
621
+ adapter,
622
+ filename,
623
+ (err: Error, data: string | Buffer | undefined, mimeType: string) => {
624
+ Utils.guardedNewThread(() => {
625
+ callback(err, data, mimeType);
626
+ }, this);
627
+ },
628
+ );
629
+ }
630
+ }
631
+ }
632
+
633
+ public getMimeType(ext: string): string {
634
+ if (ext.indexOf('.') !== -1) {
635
+ const regMatch = ext.toLowerCase().match(/\.[^.]+$/);
636
+ ext = regMatch !== null && regMatch.length > 0 ? regMatch[0] : '';
637
+ }
638
+ let _mimeType = '';
639
+ if (ext === '.css') {
640
+ _mimeType = 'text/css';
641
+ } else if (ext === '.bmp') {
642
+ _mimeType = 'image/bmp';
643
+ } else if (ext === '.png') {
644
+ _mimeType = 'image/png';
645
+ } else if (ext === '.jpg') {
646
+ _mimeType = 'image/jpeg';
647
+ } else if (ext === '.jpeg') {
648
+ _mimeType = 'image/jpeg';
649
+ } else if (ext === '.gif') {
650
+ _mimeType = 'image/gif';
651
+ } else if (ext === '.tif') {
652
+ _mimeType = 'image/tiff';
653
+ } else if (ext === '.js') {
654
+ _mimeType = 'application/javascript';
655
+ } else if (ext === '.html') {
656
+ _mimeType = 'text/html';
657
+ } else if (ext === '.htm') {
658
+ _mimeType = 'text/html';
659
+ } else if (ext === '.json') {
660
+ _mimeType = 'application/json';
661
+ } else if (ext === '.xml') {
662
+ _mimeType = 'text/xml';
663
+ } else if (ext === '.svg') {
664
+ _mimeType = 'image/svg+xml';
665
+ } else if (ext === '.eot') {
666
+ _mimeType = 'application/vnd.ms-fontobject';
667
+ } else if (ext === '.ttf') {
668
+ _mimeType = 'application/font-sfnt';
669
+ } else if (ext === '.woff') {
670
+ _mimeType = 'application/font-woff';
671
+ } else if (ext === '.wav') {
672
+ _mimeType = 'audio/wav';
673
+ } else if (ext === '.mp3') {
674
+ _mimeType = 'audio/mpeg3';
675
+ } else {
676
+ _mimeType = 'text/javascript';
677
+ }
678
+ return _mimeType;
679
+ }
680
+
681
+ // FIXME: Callback Type
682
+ public readFile64(filename: string, callback: unknown, isRemote: boolean): void {
683
+ if (!callback) {
684
+ throw 'No callback set';
685
+ }
686
+
687
+ if (!this._checkConnection('readFile', arguments)) return;
688
+
689
+ if (!isRemote && typeof app !== 'undefined') {
690
+ // FIXME: data Type
691
+ app.readLocalFile(filename.replace(/^\/vis\.0\//, ''), (err: Error, data: string, mimeType: string) => {
692
+ Utils.guardedNewThread(() => {
693
+ if (data) {
694
+ callback(err, { mime: mimeType || this.getMimeType(filename), data: btoa(data) }, filename);
695
+ } else {
696
+ callback(err, filename);
697
+ }
698
+ }, this);
699
+ });
700
+ } else {
701
+ let adapter = this.namespace;
702
+ if (filename[0] === '/') {
703
+ const p = filename.split('/');
704
+ adapter = p[1];
705
+ p.splice(0, 2);
706
+ filename = p.join('/');
707
+ }
708
+
709
+ this._socket.emit(
710
+ 'readFile64',
711
+ adapter,
712
+ filename,
713
+ // FIXME: data Type
714
+ (err: Error, data: unknown, mimeType: string) => {
715
+ Utils.guardedNewThread(() => {
716
+ if (data) {
717
+ callback(err, { mime: mimeType || this.getMimeType(filename), data: data }, filename);
718
+ } else {
719
+ callback(err, { mime: mimeType || this.getMimeType(filename) }, filename);
720
+ }
721
+ }, this);
722
+ },
723
+ );
724
+ }
725
+ }
726
+
727
+ public writeFile(
728
+ filename: string,
729
+ data: unknown | string,
730
+ mode: number,
731
+ callback: ioBroker.ErrnoCallback,
732
+ ...args: unknown[]
733
+ ): void {
734
+ if (this._type === 'local') {
735
+ storage.set(filename, JSON.stringify(data));
736
+ if (callback) {
737
+ callback();
738
+ }
739
+ return;
740
+ }
741
+
742
+ if (!this._checkConnection('writeFile', arguments)) {
743
+ return;
744
+ }
745
+ const sData: string = typeof data === 'object' ? JSON.stringify(data, null, 2) : data;
746
+
747
+ const parts = filename.split('/');
748
+ const adapter = parts[1];
749
+ parts.splice(0, 2);
750
+ if (adapter === 'vis') {
751
+ this._socket.emit(
752
+ 'writeFile',
753
+ adapter,
754
+ parts.join('/'),
755
+ sData,
756
+ mode ? { mode: this._defaultMode } : {},
757
+ callback,
758
+ );
759
+ return;
760
+ }
761
+
762
+ this._socket.emit('writeFile', this.namespace, filename, sData, mode ? { mode: this._defaultMode } : {}, callback);
763
+ }
764
+
765
+ // Write file base 64
766
+ public writeFile64(filename: string, data: string, callback: ioBroker.ErrnoCallback): void {
767
+ if (!this._checkConnection('writeFile', arguments)) return;
768
+
769
+ const parts = filename.split('/');
770
+ const adapter = parts[1];
771
+ parts.splice(0, 2);
772
+
773
+ this._socket.emit('writeFile', adapter, parts.join('/'), atob(data), { mode: this._defaultMode }, callback);
774
+ }
775
+
776
+ public readDir(dirname: string, callback: ioBroker.ReadDirCallback): void {
777
+ //socket.io
778
+ if (this._socket === null) {
779
+ SocketIoLogging.writeLog(SocketIoLogLevel.Debug, 'socket.io not initialized');
780
+ return;
781
+ }
782
+ if (!dirname) dirname = '/';
783
+ const parts = dirname.split('/');
784
+ const adapter = parts[1];
785
+ parts.splice(0, 2);
786
+
787
+ this._socket.emit(
788
+ 'readDir',
789
+ adapter,
790
+ parts.join('/'),
791
+ { filter: true },
792
+ (err: Error, data: ioBroker.ReadDirResult[]) => {
793
+ if (callback) callback(err, data);
794
+ },
795
+ );
796
+ }
797
+
798
+ public mkdir(dirname: string, callback: ioBroker.ErrnoCallback): void {
799
+ const parts = dirname.split('/');
800
+ const adapter = parts[1];
801
+ parts.splice(0, 2);
802
+
803
+ this._socket.emit('mkdir', adapter, parts.join('/'), (err: Error) => {
804
+ callback && callback(err);
805
+ });
806
+ }
807
+
808
+ public unlink(name: string, callback: ioBroker.ErrorCallback): void {
809
+ const parts = name.split('/');
810
+ const adapter = parts[1];
811
+ parts.splice(0, 2);
812
+
813
+ this._socket.emit('unlink', adapter, parts.join('/'), (err: Error) => {
814
+ callback && callback(err);
815
+ });
816
+ }
817
+
818
+ public renameFile(oldname: string, newname: string, callback: ioBroker.ErrnoCallback): void {
819
+ const parts1 = oldname.split('/');
820
+ const adapter = parts1[1];
821
+ parts1.splice(0, 2);
822
+ const parts2 = newname.split('/');
823
+ parts2.splice(0, 2);
824
+ this._socket.emit('rename', adapter, parts1.join('/'), parts2.join('/'), (err: Error) => {
825
+ callback && callback(err);
826
+ });
827
+ }
828
+
829
+ public setState(
830
+ pointId: string,
831
+ state: string | number | boolean | ioBroker.State | ioBroker.SettableState | null,
832
+ callback?: ioBroker.SetStateCallback,
833
+ ): void {
834
+ //socket.io
835
+ if (this._socket === null) {
836
+ SocketIoLogging.writeLog(SocketIoLogLevel.Debug, 'socket.io not initialized');
837
+ return;
838
+ }
839
+ if (!callback) {
840
+ callback = (err?, id?) => {
841
+ if (err) {
842
+ SocketIoLogging.writeLog(
843
+ SocketIoLogLevel.Error,
844
+ `socket.io setState Error: ${err}\nwith updating ${pointId} to value ${state}\nid:${id}`,
845
+ );
846
+ }
847
+ };
848
+ }
849
+ this._socket.emit('setState', pointId, state, callback);
850
+ }
851
+
852
+ public sendTo(
853
+ instance: string,
854
+ command: string,
855
+ payload: ioBroker.MessagePayload,
856
+ callback: ioBroker.MessageCallback | ioBroker.MessageCallbackInfo,
857
+ ): void {
858
+ //socket.io
859
+ if (this._socket === null) {
860
+ //SocketIoLogging.writeLog(SocketIoLogLevel.Debug,'socket.io not initialized');
861
+ return;
862
+ }
863
+ this._socket.emit('sendTo', instance, command, payload, callback);
864
+ }
865
+
866
+ // callback(err: Error, data)
867
+ public getStates(IDs: string[] | null, callback: ioBroker.GetStatesCallback): void {
868
+ SocketIoLogging.writeLog(SocketIoLogLevel.DeepTrace, 'getStates');
869
+ if (this._type === 'local') {
870
+ return callback(null, {});
871
+ }
872
+
873
+ if (!this._checkConnection('getStates', arguments)) {
874
+ SocketIoLogging.writeLog(SocketIoLogLevel.Debug, 'getStates: No Connection');
875
+ return;
876
+ }
877
+ this._gettingStates = this._gettingStates || 0;
878
+ this._gettingStates++;
879
+ if (this._gettingStates > 1) {
880
+ // fix for slow devices
881
+ SocketIoLogging.writeLog(
882
+ SocketIoLogLevel.Trace,
883
+ 'Trying to get empty list, because the whole list could not be loaded',
884
+ );
885
+ // FIXME: Check if this is correct to get all
886
+ IDs = null;
887
+ }
888
+ this._socket.emit('getStates', IDs, (err: Error, data: Record<string, ioBroker.State>) => {
889
+ SocketIoLogging.writeLog(SocketIoLogLevel.Trace, `getStates Callback; Error: "${err}"`);
890
+ this._gettingStates !== undefined && this._gettingStates--;
891
+ if (err || !data) {
892
+ if (callback) {
893
+ callback(err || new Error('Authentication required'));
894
+ }
895
+ } else if (callback) {
896
+ callback(null, data);
897
+ }
898
+ });
899
+ }
900
+
901
+ // TODO: check if ioBroker.Object is correct
902
+ private _fillChildren(objects: { [id: string]: ioBroker.Object & { children?: string[] } }): void {
903
+ const items: Array<string> = [];
904
+
905
+ for (const id in objects) {
906
+ items.push(id);
907
+ }
908
+ items.sort();
909
+
910
+ for (let i = 0; i < items.length; i++) {
911
+ if (objects[items[i]].common) {
912
+ let j = i + 1;
913
+ const children = [];
914
+ const len = items[i].length + 1;
915
+ const name = items[i] + '.';
916
+ while (j < items.length && items[j].substring(0, len) === name) {
917
+ children.push(items[j++]);
918
+ }
919
+
920
+ objects[items[i]].children = children;
921
+ }
922
+ }
923
+ }
924
+
925
+ // callback(err: Error, data)
926
+ public getObjects(useCache: boolean, callback: ioBroker.GetObjectsCallback): void {
927
+ if (typeof useCache === 'function') {
928
+ callback = useCache;
929
+ useCache = false;
930
+ }
931
+ // If cache used
932
+ if (this._useStorage && useCache) {
933
+ if (typeof storage !== 'undefined') {
934
+ const objects = this._objects || storage.get('objects');
935
+ if (objects) return callback(null, objects);
936
+ } else if (this._objects) {
937
+ return callback(null, this._objects);
938
+ }
939
+ }
940
+
941
+ if (!this._checkConnection('getObjects', arguments)) return;
942
+ this._socket.emit('getObjects', (err: Error, data: Record<string, ioBroker.Object>) => {
943
+ if (err) {
944
+ callback(err);
945
+ return;
946
+ }
947
+
948
+ // Read all enums
949
+ this._socket.emit(
950
+ 'getObjectView',
951
+ 'system',
952
+ 'enum',
953
+ { startkey: 'enum.', endkey: 'enum.\u9999' },
954
+ (err: Error, res: { rows: ioBroker.GetObjectViewItem[] }) => {
955
+ if (err) {
956
+ callback(err);
957
+ return;
958
+ }
959
+ const enums: Record<string, ioBroker.Object> = {};
960
+ for (const row of res.rows) {
961
+ const currentId = row.id;
962
+ if (row.value !== null) {
963
+ data[currentId] = row.value;
964
+ enums[currentId] = row.value;
965
+ }
966
+ }
967
+
968
+ // Read all adapters for images
969
+ this._socket.emit(
970
+ 'getObjectView',
971
+ 'system',
972
+ 'instance',
973
+ { startkey: 'system.adapter.', endkey: 'system.adapter.\u9999' },
974
+ (err: Error, res: { rows: ioBroker.GetObjectViewItem[] }) => {
975
+ if (err) {
976
+ callback(err);
977
+ return;
978
+ }
979
+ for (const row of res.rows) {
980
+ if (row.value !== null) {
981
+ data[row.id] = row.value;
982
+ }
983
+ }
984
+ // find out default file mode
985
+ if (
986
+ data['system.adapter.' + this.namespace] &&
987
+ data['system.adapter.' + this.namespace].native &&
988
+ data['system.adapter.' + this.namespace].native.defaultFileMode
989
+ ) {
990
+ this._defaultMode = data['system.adapter.' + this.namespace].native.defaultFileMode;
991
+ }
992
+
993
+ // Read all channels for images
994
+ this._socket.emit(
995
+ 'getObjectView',
996
+ 'system',
997
+ 'channel',
998
+ { startkey: '', endkey: '\u9999' },
999
+ (err: Error, res: { rows: ioBroker.GetObjectViewItem[] }) => {
1000
+ if (err) {
1001
+ callback(err);
1002
+ return;
1003
+ }
1004
+
1005
+ for (const row of res.rows) {
1006
+ if (row.value !== null) {
1007
+ data[row.id] = row.value;
1008
+ }
1009
+ }
1010
+
1011
+ // Read all devices for images
1012
+ this._socket.emit(
1013
+ 'getObjectView',
1014
+ 'system',
1015
+ 'device',
1016
+ { startkey: '', endkey: '\u9999' },
1017
+ (err: Error, res: { rows: ioBroker.GetObjectViewItem[] }) => {
1018
+ if (err) {
1019
+ callback(err);
1020
+ return;
1021
+ }
1022
+
1023
+ for (const row of res.rows) {
1024
+ if (row.value !== null) {
1025
+ data[row.id] = row.value;
1026
+ }
1027
+ }
1028
+
1029
+ if (this._useStorage) {
1030
+ this._fillChildren(data);
1031
+ this._objects = data;
1032
+ this._enums = enums;
1033
+
1034
+ if (typeof storage !== 'undefined') {
1035
+ storage.set('objects', data);
1036
+ storage.set('enums', enums);
1037
+ storage.set('timeSync', new Date().getTime());
1038
+ }
1039
+ }
1040
+
1041
+ if (callback) callback(err, data);
1042
+ },
1043
+ );
1044
+ },
1045
+ );
1046
+ },
1047
+ );
1048
+ },
1049
+ );
1050
+ });
1051
+ }
1052
+
1053
+ public getChildren(
1054
+ id: string,
1055
+ useCache: boolean,
1056
+ callback: (err?: Error | null, children?: string[]) => void,
1057
+ ...args: unknown[]
1058
+ ): void {
1059
+ if (!this._checkConnection('getChildren', arguments)) return;
1060
+
1061
+ if (!id) return callback(new Error('getChildren: no id given'));
1062
+
1063
+ const data: Record<string, ioBroker.Object> = {};
1064
+
1065
+ if (this._useStorage && useCache) {
1066
+ if (typeof storage !== 'undefined') {
1067
+ const objects = storage.get('objects');
1068
+ if (objects && objects[id] && objects[id].children) {
1069
+ return callback(null, objects[id].children);
1070
+ }
1071
+ } else if (this._objects && this._objects[id] && this._objects[id].children) {
1072
+ return callback(null, this._objects[id].children);
1073
+ }
1074
+ }
1075
+
1076
+ // Read all devices
1077
+ this._socket.emit(
1078
+ 'getObjectView',
1079
+ 'system',
1080
+ 'device',
1081
+ { startkey: id + '.', endkey: id + '.\u9999' },
1082
+ (err: Error, res: { rows: ioBroker.GetObjectViewItem[] }) => {
1083
+ if (err) {
1084
+ callback(err);
1085
+ return;
1086
+ }
1087
+
1088
+ for (const row of res.rows) {
1089
+ if (row.value !== null) {
1090
+ data[row.id] = row.value;
1091
+ }
1092
+ }
1093
+
1094
+ this._socket.emit(
1095
+ 'getObjectView',
1096
+ 'system',
1097
+ 'channel',
1098
+ { startkey: id + '.', endkey: id + '.\u9999' },
1099
+ (err: Error, res: { rows: ioBroker.GetObjectViewItem[] }) => {
1100
+ if (err) {
1101
+ callback(err);
1102
+ return;
1103
+ }
1104
+
1105
+ for (const row of res.rows) {
1106
+ if (row.value !== null) {
1107
+ data[row.id] = row.value;
1108
+ }
1109
+ }
1110
+
1111
+ // Read all adapters for images
1112
+ this._socket.emit(
1113
+ 'getObjectView',
1114
+ 'system',
1115
+ 'state',
1116
+ { startkey: id + '.', endkey: id + '.\u9999' },
1117
+ (err: Error, res: { rows: ioBroker.GetObjectViewItem[] }) => {
1118
+ if (err) {
1119
+ callback(err);
1120
+ return;
1121
+ }
1122
+
1123
+ for (const row of res.rows) {
1124
+ if (row.value !== null) {
1125
+ data[row.id] = row.value;
1126
+ }
1127
+ }
1128
+
1129
+ const list = [];
1130
+
1131
+ const count = id.split('.').length;
1132
+
1133
+ // find direct children
1134
+ for (const _id in data) {
1135
+ const parts = _id.split('.');
1136
+ if (count + 1 === parts.length) {
1137
+ list.push(_id);
1138
+ }
1139
+ }
1140
+ list.sort();
1141
+
1142
+ if (this._useStorage && typeof storage !== 'undefined') {
1143
+ const objects = storage.get('objects') || {};
1144
+
1145
+ for (const id_ in data) {
1146
+ objects[id_] = data[id_];
1147
+ }
1148
+ if (objects[id] && objects[id].common) {
1149
+ objects[id].children = list;
1150
+ }
1151
+ // Store for every element theirs children
1152
+ const items = [];
1153
+ for (const __id in data) {
1154
+ items.push(__id);
1155
+ }
1156
+ items.sort();
1157
+
1158
+ for (let k = 0; k < items.length; k++) {
1159
+ if (objects[items[k]].common) {
1160
+ let j = k + 1;
1161
+ const children = [];
1162
+ const len = items[k].length + 1;
1163
+ const name = items[k] + '.';
1164
+ while (j < items.length && items[j].substring(0, len) === name) {
1165
+ children.push(items[j++]);
1166
+ }
1167
+
1168
+ objects[items[k]].children = children;
1169
+ }
1170
+ }
1171
+
1172
+ storage.set('objects', objects);
1173
+ }
1174
+
1175
+ if (callback) callback(err, list);
1176
+ },
1177
+ );
1178
+ },
1179
+ );
1180
+ },
1181
+ );
1182
+ }
1183
+
1184
+ public getObject(id: string, useCache: boolean, callback: ioBroker.GetObjectCallback): void {
1185
+ if (!id) return callback(new Error('no id given'));
1186
+
1187
+ // If cache used
1188
+ if (this._useStorage && useCache && typeof storage !== 'undefined') {
1189
+ if (typeof storage !== 'undefined') {
1190
+ const objects = this._objects || storage.get('objects');
1191
+ if (objects && objects[id]) return callback(null, objects[id]);
1192
+ } else if (this._enums) {
1193
+ return callback(null, this._enums as unknown as ioBroker.OtherObject);
1194
+ }
1195
+ }
1196
+
1197
+ this._socket.emit('getObject', id, (err: Error, obj: ioBroker.Object) => {
1198
+ if (err) {
1199
+ callback(err);
1200
+ return;
1201
+ }
1202
+ if (this._useStorage && typeof storage !== 'undefined') {
1203
+ const objects = storage.get('objects') || {};
1204
+ objects[id] = obj;
1205
+ storage.set('objects', objects);
1206
+ }
1207
+ return callback(null, obj);
1208
+ });
1209
+ }
1210
+
1211
+ public getEnums(enumName: string, useCache: boolean, callback: ioBroker.GetEnumsCallback): void {
1212
+ // If cache used
1213
+ if (this._useStorage && useCache) {
1214
+ if (typeof storage !== 'undefined') {
1215
+ const enums: { [id: string]: ioBroker.Enum } = this._enums || storage.get('enums');
1216
+ if (enums) return callback(null, enums);
1217
+ } else if (this._enums) {
1218
+ return callback(null, this._enums);
1219
+ }
1220
+ }
1221
+
1222
+ if (this._type === 'local') {
1223
+ return callback(null, {});
1224
+ }
1225
+
1226
+ enumName = enumName ? enumName + '.' : '';
1227
+
1228
+ // Read all enums
1229
+ this._socket.emit(
1230
+ 'getObjectView',
1231
+ 'system',
1232
+ 'enum',
1233
+ { startkey: 'enum.' + enumName, endkey: 'enum.' + enumName + '\u9999' },
1234
+ (err: Error, res: { rows: ioBroker.GetObjectViewItem[] }) => {
1235
+ if (err) {
1236
+ callback(err);
1237
+ return;
1238
+ }
1239
+ const enums: Record<string, ioBroker.Object> = {};
1240
+ for (const row of res.rows) {
1241
+ if (row.value !== null) {
1242
+ enums[row.id] = row.value;
1243
+ }
1244
+ }
1245
+
1246
+ if (this._useStorage && typeof storage !== 'undefined') {
1247
+ storage.set('enums', enums);
1248
+ }
1249
+ callback(null, enums);
1250
+ },
1251
+ );
1252
+ }
1253
+
1254
+ // return time when the objects were synchronized
1255
+ public getSyncTime(): Date {
1256
+ if (this._useStorage && typeof storage !== 'undefined') {
1257
+ const timeSync = storage.get('timeSync');
1258
+ if (timeSync) return new Date(timeSync);
1259
+ }
1260
+ return new Date(0);
1261
+ }
1262
+
1263
+ // FIXME finish implementation of this file
1264
+ public addObject(objId: unknown, obj: unknown, callback: unknown): void {
1265
+ if (!this._isConnected) {
1266
+ SocketIoLogging.writeLog(SocketIoLogLevel.Debug, 'No connection!');
1267
+ return;
1268
+ }
1269
+ //socket.io
1270
+ if (this._socket === null) {
1271
+ SocketIoLogging.writeLog(SocketIoLogLevel.Debug, 'socket.io not initialized');
1272
+ return;
1273
+ }
1274
+ }
1275
+
1276
+ public delObject(objId: string): void {
1277
+ if (!this._checkConnection('delObject', arguments)) return;
1278
+
1279
+ this._socket.emit('delObject', objId);
1280
+ }
1281
+
1282
+ public httpGet(url: string, callback: (res: IncomingMessage | Error) => void): void {
1283
+ if (!this._isConnected) {
1284
+ SocketIoLogging.writeLog(SocketIoLogLevel.Debug, 'No connection!');
1285
+ return;
1286
+ }
1287
+ //socket.io
1288
+ if (this._socket === null) {
1289
+ SocketIoLogging.writeLog(SocketIoLogLevel.Debug, 'socket.io not initialized');
1290
+ return;
1291
+ }
1292
+ this._socket.emit('httpGet', url, (data: IncomingMessage | Error) => {
1293
+ if (callback) callback(data);
1294
+ });
1295
+ }
1296
+
1297
+ public logError(errorText: string): void {
1298
+ SocketIoLogging.writeLog(SocketIoLogLevel.Debug, 'Error: ' + errorText);
1299
+ if (!this._isConnected) {
1300
+ //SocketIoLogging.writeLog(SocketIoLogLevel.Debug,'No connection!');
1301
+ return;
1302
+ }
1303
+ //socket.io
1304
+ if (this._socket === null) {
1305
+ SocketIoLogging.writeLog(SocketIoLogLevel.Debug, 'socket.io not initialized');
1306
+ return;
1307
+ }
1308
+ this._socket.emit('log', 'error', 'Addon DashUI ' + errorText);
1309
+ }
1310
+
1311
+ private _queueCmdIfRequired(func: string, args: IArguments) {
1312
+ if (this.isAuthDone) {
1313
+ SocketIoLogging.writeLog(SocketIoLogLevel.DeepTrace, `_queueCmdIfRequired: Auth is already done`);
1314
+ return false;
1315
+ }
1316
+ SocketIoLogging.writeLog(SocketIoLogLevel.DeepTrace, `_queueCmdIfRequired: Auth is not yet done`);
1317
+ // Queue command
1318
+ this._cmdQueue.push({ func: func, args: args });
1319
+ if (this._authRunning) {
1320
+ SocketIoLogging.writeLog(
1321
+ SocketIoLogLevel.DeepTrace,
1322
+ `_queueCmdIfRequired: Authentication Process is already Running`,
1323
+ );
1324
+ return true;
1325
+ }
1326
+
1327
+ SocketIoLogging.writeLog(SocketIoLogLevel.DeepTrace, `_queueCmdIfRequired: Starting Authentication Process`);
1328
+ this._authRunning = true;
1329
+ // Try to read version
1330
+ this._checkAuth((error: unknown, version: string) => {
1331
+ SocketIoLogging.writeLog(SocketIoLogLevel.DeepTrace, `_queueCmdIfRequired: _checkAuth CB data: "${version}"`);
1332
+ // If we have got version string, so there is no authentication, or we are authenticated
1333
+ this._authRunning = false;
1334
+ if (!version) {
1335
+ // Auth required
1336
+ this._isAuthRequired = true;
1337
+ // What for AuthRequest from server
1338
+ return;
1339
+ }
1340
+ this._isAuthDone = true;
1341
+ // Repeat all stored requests
1342
+ const __cmdQueue = this._cmdQueue;
1343
+ // Trigger GC
1344
+ this._cmdQueue = undefined;
1345
+ this._cmdQueue = [];
1346
+ for (let t = 0, len = __cmdQueue.length; t < len; t++) {
1347
+ (this as unknown)[__cmdQueue[t].func].apply(this, __cmdQueue[t].args);
1348
+ }
1349
+ });
1350
+
1351
+ return true;
1352
+ }
1353
+
1354
+ public authenticate(user: string, password: string, salt: string): void {
1355
+ this._authRunning = true;
1356
+
1357
+ if (user !== undefined) {
1358
+ this._authInfo = {
1359
+ user: user,
1360
+ hash: password + salt,
1361
+ salt: salt,
1362
+ };
1363
+ }
1364
+
1365
+ if (!this._isConnected) {
1366
+ SocketIoLogging.writeLog(SocketIoLogLevel.Debug, 'No connection!');
1367
+ return;
1368
+ }
1369
+
1370
+ if (!this._authInfo) {
1371
+ SocketIoLogging.writeLog(SocketIoLogLevel.Debug, 'No credentials!');
1372
+ }
1373
+ }
1374
+
1375
+ public getConfig(
1376
+ useCache: boolean,
1377
+ callback: (error: Error | null, conf?: Record<string, any>) => void,
1378
+ ...args: unknown[]
1379
+ ): void {
1380
+ if (!this._checkConnection('getConfig', arguments)) return;
1381
+
1382
+ if (typeof useCache === 'function') {
1383
+ callback = useCache;
1384
+ useCache = false;
1385
+ }
1386
+ if (this._useStorage && useCache) {
1387
+ if (typeof storage !== 'undefined') {
1388
+ const objects = storage.get('objects');
1389
+ if (objects && objects['system.config']) {
1390
+ return callback(null, objects['system.config'].common);
1391
+ }
1392
+ } else if (this._objects && this._objects['system.config']) {
1393
+ return callback(null, this._objects['system.config'].common);
1394
+ }
1395
+ }
1396
+ this._socket.emit('getObject', 'system.config', (err: Error, obj: ioBroker.Object) => {
1397
+ if (err || !obj || !obj.common) {
1398
+ callback(new Error('Cannot read language'));
1399
+ return;
1400
+ }
1401
+
1402
+ if (this._useStorage && typeof storage !== 'undefined') {
1403
+ const objects = storage.get('objects') || {};
1404
+ objects['system.config'] = obj;
1405
+ storage.set('objects', objects);
1406
+ }
1407
+
1408
+ callback(null, obj.common);
1409
+ });
1410
+ }
1411
+
1412
+ public sendCommand(
1413
+ instance: string,
1414
+ command: string,
1415
+ data: string | number | boolean | unknown[] | Record<string, any> | null,
1416
+ ack: boolean = true,
1417
+ ): void {
1418
+ this.setState(this.namespace + '.control.instance', { val: instance || 'notdefined', ack: true });
1419
+ this.setState(this.namespace + '.control.data', { val: data, ack: true });
1420
+ this.setState(this.namespace + '.control.command', { val: command, ack: ack });
1421
+ }
1422
+
1423
+ private _detectViews(
1424
+ projectDir: string,
1425
+ callback: (
1426
+ err?: NodeJS.ErrnoException | null,
1427
+ obj?: { name: string; readOnly: undefined | boolean; mode: number },
1428
+ ) => void,
1429
+ ) {
1430
+ this.readDir(
1431
+ '/' + this.namespace + '/' + projectDir,
1432
+ (err?: NodeJS.ErrnoException | null, dirs?: ioBroker.ReadDirResult[]) => {
1433
+ if (err) {
1434
+ callback(err);
1435
+ return;
1436
+ }
1437
+
1438
+ // find vis-views.json
1439
+ if (dirs === undefined) {
1440
+ callback(new Error('No directories found'));
1441
+ return;
1442
+ }
1443
+
1444
+ for (const dir of dirs) {
1445
+ if (dir.file === 'vis-views.json' && (!dir.acl || dir.acl.read)) {
1446
+ return callback(err, {
1447
+ name: projectDir,
1448
+ readOnly: dir.acl && !dir.acl.write,
1449
+ mode: dir.acl ? dir.acl.permissions : 0,
1450
+ });
1451
+ }
1452
+ }
1453
+ callback(err);
1454
+ },
1455
+ );
1456
+ }
1457
+
1458
+ public readProjects(
1459
+ callback: (
1460
+ err?: NodeJS.ErrnoException | null,
1461
+ objects?: Array<{ name: string; readOnly: undefined | boolean; mode: number }>,
1462
+ ) => void,
1463
+ ): void {
1464
+ this.readDir('/' + this.namespace, (err?: NodeJS.ErrnoException | null, dirs?: ioBroker.ReadDirResult[]) => {
1465
+ const result: Array<{ name: string; readOnly: undefined | boolean; mode: number }> = [];
1466
+ let count = 0;
1467
+ if (err) {
1468
+ callback(err);
1469
+ return;
1470
+ }
1471
+
1472
+ if (dirs === undefined) {
1473
+ callback(new Error('No Dirs Found'));
1474
+ return;
1475
+ }
1476
+
1477
+ for (const dir of dirs) {
1478
+ if (!dir.isDir) {
1479
+ continue;
1480
+ }
1481
+
1482
+ count++;
1483
+ this._detectViews(dir.file, (subErr, project) => {
1484
+ if (project) result.push(project);
1485
+
1486
+ err = err || subErr;
1487
+ if (!--count) callback(err, result);
1488
+ });
1489
+ }
1490
+ });
1491
+ }
1492
+
1493
+ public chmodProject(projectDir: string, mode: number | string, callback: ioBroker.ChownFileCallback): void {
1494
+ //socket.io
1495
+ if (this._socket === null) {
1496
+ SocketIoLogging.writeLog(SocketIoLogLevel.Debug, 'socket.io not initialized');
1497
+ return;
1498
+ }
1499
+ this._socket.emit(
1500
+ 'chmodFile',
1501
+ this.namespace,
1502
+ projectDir + '*',
1503
+ { mode: mode },
1504
+ (err: Error, data?: ioBroker.ChownFileResult[], id?: string) => {
1505
+ if (callback) callback(err, data, id);
1506
+ },
1507
+ );
1508
+ }
1509
+
1510
+ public clearCache(): void {
1511
+ if (typeof storage !== 'undefined') {
1512
+ storage.empty();
1513
+ }
1514
+ }
1515
+
1516
+ public getHistory(
1517
+ id: string,
1518
+ options: ioBroker.GetHistoryOptions & { timeout?: number },
1519
+ callback: ioBroker.GetHistoryCallback,
1520
+ ...args: unknown[]
1521
+ ): void {
1522
+ if (!this._checkConnection('getHistory', arguments)) return;
1523
+
1524
+ if (!options) options = {};
1525
+
1526
+ if (!options.timeout) {
1527
+ options.timeout = 2000;
1528
+ }
1529
+
1530
+ let timeout: NodeJS.Timeout | undefined = Utils.guardedTimeout(
1531
+ () => {
1532
+ timeout = undefined;
1533
+ callback(new Error('timeout'));
1534
+ },
1535
+ (options as any).timeout,
1536
+ this,
1537
+ );
1538
+ this._socket.emit('getHistory', id, options, (err: Error, result: ioBroker.GetHistoryResult) => {
1539
+ if (timeout) {
1540
+ clearTimeout(timeout);
1541
+ timeout = undefined;
1542
+ }
1543
+ callback(err, result);
1544
+ });
1545
+ }
1546
+
1547
+ private getLiveHost(callback: (host: string) => void) {
1548
+ this._socket.emit(
1549
+ 'getObjectView',
1550
+ 'system',
1551
+ 'host',
1552
+ { startkey: 'system.host.', endkey: 'system.host.\u9999' },
1553
+ (err?: Error, res?: { rows: ioBroker.GetObjectViewItem[] }) => {
1554
+ if (err || res === undefined || res.rows.length === 0) {
1555
+ callback('');
1556
+ return;
1557
+ }
1558
+
1559
+ const _hosts = [];
1560
+
1561
+ for (const row of res.rows) {
1562
+ _hosts.push(row.id + '.alive');
1563
+ }
1564
+
1565
+ this.getStates(_hosts, (err, states) => {
1566
+ if (err) {
1567
+ callback('');
1568
+ }
1569
+ for (const h in states) {
1570
+ if (states[h].val) {
1571
+ callback(h.substring(0, h.length - '.alive'.length));
1572
+ return;
1573
+ }
1574
+ }
1575
+ callback('');
1576
+ });
1577
+ },
1578
+ );
1579
+ }
1580
+
1581
+ private readDirAsZip(project: string, useConvert: boolean = false, callback: ioBroker.ReadDirCallback) {
1582
+ if (!this._isConnected) {
1583
+ SocketIoLogging.writeLog(SocketIoLogLevel.Debug, 'No connection!');
1584
+ return;
1585
+ }
1586
+ //socket.io
1587
+ if (this._socket === null) {
1588
+ SocketIoLogging.writeLog(SocketIoLogLevel.Debug, 'socket.io not initialized');
1589
+ return;
1590
+ }
1591
+ if (project.match(/\/$/)) project = project.substring(0, project.length - 1);
1592
+
1593
+ this.getLiveHost((host) => {
1594
+ if (!host) {
1595
+ SocketIoLogging.writeLog(SocketIoLogLevel.Error, 'No active host found');
1596
+ return;
1597
+ }
1598
+ // to do find active host
1599
+ this._socket.emit(
1600
+ 'sendToHost',
1601
+ host,
1602
+ 'readDirAsZip',
1603
+ {
1604
+ id: this.namespace,
1605
+ name: project || 'main',
1606
+ options: {
1607
+ settings: useConvert,
1608
+ },
1609
+ },
1610
+ // (data: ioBroker.Message) => {
1611
+ // FIXME: ioBroker.Message has no attribute error
1612
+ (data: unknown) => {
1613
+ if (data.error) SocketIoLogging.writeLog(SocketIoLogLevel.Error, data.error);
1614
+ if (callback) callback(data.error, data.data);
1615
+ },
1616
+ );
1617
+ });
1618
+ }
1619
+
1620
+ private writeDirAsZip(project: string, base64: string, callback: ioBroker.ReadDirCallback): void {
1621
+ if (!this._isConnected) {
1622
+ SocketIoLogging.writeLog(SocketIoLogLevel.Debug, 'No connection!');
1623
+ return;
1624
+ }
1625
+ //socket.io
1626
+ if (this._socket === null) {
1627
+ SocketIoLogging.writeLog(SocketIoLogLevel.Debug, 'socket.io not initialized');
1628
+ return;
1629
+ }
1630
+ if (project.match(/\/$/)) project = project.substring(0, project.length - 1);
1631
+
1632
+ this.getLiveHost((host) => {
1633
+ if (!host) {
1634
+ SocketIoLogging.writeLog(SocketIoLogLevel.Error, 'No active host found');
1635
+ return;
1636
+ }
1637
+ this._socket.emit(
1638
+ 'sendToHost',
1639
+ host,
1640
+ 'writeDirAsZip',
1641
+ {
1642
+ id: this.namespace,
1643
+ name: project || 'main',
1644
+ data: base64,
1645
+ },
1646
+ // (data: ioBroker.Message) => {
1647
+ // FIXME: ioBroker.Message has no attribute error
1648
+ (data: unknown) => {
1649
+ if (data.error) SocketIoLogging.writeLog(SocketIoLogLevel.Error, data.error);
1650
+ if (callback) callback(data.error, data.message);
1651
+ },
1652
+ );
1653
+ });
1654
+ }
1655
+ }