iobroker.javascript 8.9.2 → 9.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 (154) hide show
  1. package/README.md +7 -3
  2. package/admin/assets/{createSvgIcon-BdRvDw53.js → createSvgIcon-BQT-7-GS.js} +1 -1
  3. package/admin/assets/{docs-6na4NFtj.js → docs-BrInh2Pn.js} +2 -2
  4. package/admin/assets/index-BzKS5vx-.js +1329 -0
  5. package/admin/assets/index-CfuHVX2B.js +193 -0
  6. package/admin/assets/index-CuCs5aLs.js +5 -0
  7. package/admin/assets/index-tExaxF69.js +930 -0
  8. package/admin/assets/inheritsLoose--KnFDOw1.js +36 -0
  9. package/admin/assets/{useTheme-Cl1QmYFL.js → useTheme-DF6qfrtI.js} +1 -1
  10. package/admin/custom/assets/{Components-ChpTW7sE.js → Components-D9kRsHNY.js} +1 -1
  11. package/admin/custom/assets/{DefaultPropsProvider-CHpMCMgA.js → DefaultPropsProvider-9qubdy-V.js} +2 -2
  12. package/admin/custom/assets/{FilledInput-CtcQVKyj.js → FilledInput-BpuM_GyB.js} +1 -1
  13. package/admin/custom/assets/Tabs-CvEv0KOT.js +138 -0
  14. package/admin/custom/assets/ThemeProvider-DZTpMR8n.js +29 -0
  15. package/admin/custom/assets/{createSvgIcon-C_vAELFU.js → createSvgIcon-BQThUM-f.js} +1 -1
  16. package/admin/custom/assets/hostInit-CrAs5YZS.js +1 -1
  17. package/admin/custom/assets/index-BXmu6lpn.js +904 -0
  18. package/admin/custom/assets/{index-DwsbskQy.js → index-DIWNdd4a.js} +1 -1
  19. package/admin/custom/assets/index-DZqbj5K4.js +55 -0
  20. package/admin/custom/assets/{index-D5apCzjJ.js → index-Dbe-II7_.js} +6 -6
  21. package/admin/custom/assets/index-sawv1cUe.js +1399 -0
  22. package/admin/custom/assets/{virtualExposes-BPncCdUy.js → virtualExposes-BMKIjWrE.js} +2 -2
  23. package/admin/custom/customComponents.js +2 -2
  24. package/admin/custom/i18n/de.json +29 -29
  25. package/admin/custom/i18n/es.json +29 -29
  26. package/admin/custom/i18n/fr.json +29 -29
  27. package/admin/custom/i18n/it.json +29 -29
  28. package/admin/custom/i18n/nl.json +29 -29
  29. package/admin/custom/i18n/pl.json +29 -29
  30. package/admin/custom/i18n/pt.json +29 -29
  31. package/admin/custom/i18n/ru.json +29 -29
  32. package/admin/custom/i18n/uk.json +29 -29
  33. package/admin/custom/i18n/zh-cn.json +29 -29
  34. package/admin/google-blockly/blockly_compressed.js +875 -887
  35. package/admin/google-blockly/blocks_compressed.js +38 -41
  36. package/admin/google-blockly/javascript_compressed.js +19 -54
  37. package/admin/google-blockly/media/delete-icon.svg +1 -0
  38. package/admin/google-blockly/media/foldout-icon.svg +1 -0
  39. package/admin/google-blockly/media/resize-handle.svg +3 -0
  40. package/admin/i18n/de.json +61 -0
  41. package/admin/i18n/en.json +61 -0
  42. package/admin/i18n/es.json +61 -0
  43. package/admin/i18n/fr.json +61 -0
  44. package/admin/i18n/it.json +61 -0
  45. package/admin/i18n/nl.json +61 -0
  46. package/admin/i18n/pl.json +61 -0
  47. package/admin/i18n/pt.json +61 -0
  48. package/admin/i18n/ru.json +61 -0
  49. package/admin/i18n/uk.json +61 -0
  50. package/admin/i18n/zh-cn.json +61 -0
  51. package/admin/jsonConfig.json +2 -0
  52. package/admin/mf-manifest.json +1 -1
  53. package/admin/remoteEntry.js +2 -2
  54. package/admin/tab.html +4 -4
  55. package/admin/vsFont/README.md +1 -1
  56. package/admin/vsFont/createJson.js +1 -1
  57. package/build-backend/lib/consts.js +346 -0
  58. package/build-backend/lib/consts.js.map +1 -0
  59. package/build-backend/lib/convert.js +120 -0
  60. package/build-backend/lib/convert.js.map +1 -0
  61. package/build-backend/lib/debug.js +163 -0
  62. package/build-backend/lib/debug.js.map +1 -0
  63. package/{lib → build-backend/lib}/debugger.js +261 -232
  64. package/build-backend/lib/debugger.js.map +1 -0
  65. package/build-backend/lib/eventObj.js +177 -0
  66. package/build-backend/lib/eventObj.js.map +1 -0
  67. package/build-backend/lib/inspect.js +728 -0
  68. package/build-backend/lib/inspect.js.map +1 -0
  69. package/build-backend/lib/javascript.d.ts +1972 -0
  70. package/{lib → build-backend/lib}/mirror.js +185 -182
  71. package/build-backend/lib/mirror.js.map +1 -0
  72. package/build-backend/lib/nodeModulesManagement.js +25 -0
  73. package/build-backend/lib/nodeModulesManagement.js.map +1 -0
  74. package/build-backend/lib/patternCompareFunctions.js +321 -0
  75. package/build-backend/lib/patternCompareFunctions.js.map +1 -0
  76. package/build-backend/lib/protectFs.js +477 -0
  77. package/build-backend/lib/protectFs.js.map +1 -0
  78. package/build-backend/lib/request.js +89 -0
  79. package/build-backend/lib/request.js.map +1 -0
  80. package/{lib → build-backend/lib}/sandbox.js +2046 -1852
  81. package/build-backend/lib/sandbox.js.map +1 -0
  82. package/build-backend/lib/scheduler.js +553 -0
  83. package/build-backend/lib/scheduler.js.map +1 -0
  84. package/build-backend/lib/tools.js +183 -0
  85. package/build-backend/lib/tools.js.map +1 -0
  86. package/build-backend/lib/typescriptSettings.js +39 -0
  87. package/build-backend/lib/typescriptSettings.js.map +1 -0
  88. package/build-backend/lib/typescriptTools.js +435 -0
  89. package/build-backend/lib/typescriptTools.js.map +1 -0
  90. package/build-backend/lib/utils.js +66 -0
  91. package/build-backend/lib/utils.js.map +1 -0
  92. package/{lib → build-backend/lib}/words.js +23 -30
  93. package/build-backend/lib/words.js.map +1 -0
  94. package/build-backend/main.js +2671 -0
  95. package/build-backend/main.js.map +1 -0
  96. package/build-backend/types.d.ts +742 -0
  97. package/docs/de/README.md +1 -1
  98. package/docs/en/README.md +1 -1
  99. package/docs/en/javascript.md +10 -6
  100. package/docs/en/upgrade-guide.md +5 -2
  101. package/docs/ru/README.md +1 -1
  102. package/install/installTypings.js +34 -47
  103. package/io-package.json +14 -14
  104. package/package.json +14 -14
  105. package/admin/assets/index-DCkkMRRa.js +0 -1335
  106. package/admin/assets/index-DJfSBoBB.js +0 -192
  107. package/admin/assets/index-DTiA4WdK.js +0 -930
  108. package/admin/assets/index-qABB1JVe.js +0 -5
  109. package/admin/assets/inheritsLoose-BW-3V39H.js +0 -36
  110. package/admin/custom/assets/Tabs-C1KOyWRi.js +0 -137
  111. package/admin/custom/assets/ThemeProvider-Buvx5nSd.js +0 -29
  112. package/admin/custom/assets/index-BGCRznKh.js +0 -904
  113. package/admin/custom/assets/index-DfBPYXqA.js +0 -55
  114. package/admin/custom/assets/index-jIr6kOLK.js +0 -1398
  115. package/admin/i18n/de/flat.txt +0 -427
  116. package/admin/i18n/de/translations.json +0 -429
  117. package/admin/i18n/en/flat.txt +0 -427
  118. package/admin/i18n/en/translations.json +0 -429
  119. package/admin/i18n/es/flat.txt +0 -427
  120. package/admin/i18n/es/translations.json +0 -429
  121. package/admin/i18n/flat.txt +0 -427
  122. package/admin/i18n/fr/flat.txt +0 -427
  123. package/admin/i18n/fr/translations.json +0 -429
  124. package/admin/i18n/it/flat.txt +0 -427
  125. package/admin/i18n/it/translations.json +0 -429
  126. package/admin/i18n/nl/flat.txt +0 -427
  127. package/admin/i18n/nl/translations.json +0 -429
  128. package/admin/i18n/pl/flat.txt +0 -427
  129. package/admin/i18n/pl/translations.json +0 -429
  130. package/admin/i18n/pt/flat.txt +0 -427
  131. package/admin/i18n/pt/translations.json +0 -429
  132. package/admin/i18n/ru/flat.txt +0 -427
  133. package/admin/i18n/ru/translations.json +0 -429
  134. package/admin/i18n/uk/flat.txt +0 -427
  135. package/admin/i18n/uk/translations.json +0 -429
  136. package/admin/i18n/zh-cn/flat.txt +0 -427
  137. package/admin/i18n/zh-cn/translations.json +0 -429
  138. package/lib/adapter-config.d.ts +0 -16
  139. package/lib/consts.js +0 -83
  140. package/lib/convert.js +0 -106
  141. package/lib/debug.js +0 -154
  142. package/lib/eventObj.js +0 -201
  143. package/lib/inspect.js +0 -688
  144. package/lib/javascript.d.ts +0 -1858
  145. package/lib/nodeModulesManagement.js +0 -26
  146. package/lib/patternCompareFunctions.js +0 -391
  147. package/lib/protectFs.js +0 -401
  148. package/lib/request.js +0 -72
  149. package/lib/scheduler.js +0 -447
  150. package/lib/tools.js +0 -192
  151. package/lib/typescriptSettings.js +0 -44
  152. package/lib/typescriptTools.js +0 -529
  153. package/lib/utils.js +0 -87
  154. package/main.js +0 -2653
package/main.js DELETED
@@ -1,2653 +0,0 @@
1
- /*
2
- * Javascript adapter
3
- *
4
- * The MIT License (MIT)
5
- *
6
- * Copyright (c) 2014-2024 bluefox <dogafox@gmail.com>,
7
- *
8
- * Copyright (c) 2014 hobbyquaker
9
- */
10
-
11
- /* jshint -W097 */
12
- /* jshint -W083 */
13
- /* jshint strict: false */
14
- /* jslint node: true */
15
- /* jshint shadow: true */
16
- 'use strict';
17
-
18
- const vm = require('node:vm');
19
- const nodeFS = require('node:fs');
20
- const nodePath = require('node:path');
21
- const tsc = require('virtual-tsc');
22
- const Mirror = require('./lib/mirror');
23
- const fork = require('child_process').fork;
24
-
25
- const mods = {
26
- fs: {},
27
- dgram: require('node:dgram'),
28
- crypto: require('node:crypto'),
29
- dns: require('node:dns'),
30
- events: require('node:events'),
31
- http: require('node:http'),
32
- https: require('node:https'),
33
- http2: require('node:http2'),
34
- net: require('node:net'),
35
- os: require('node:os'),
36
- path: require('node:path'),
37
- util: require('node:util'),
38
- child_process: require('node:child_process'),
39
- stream: require('node:stream'),
40
- zlib: require('node:zlib'),
41
- suncalc: require('suncalc2'),
42
- axios: require('axios'),
43
- wake_on_lan: require('wake_on_lan'),
44
- nodeSchedule: require('node-schedule')
45
- };
46
-
47
- /**
48
- * List of forbidden Locations for a mirror directory
49
- * relative to the default data directory
50
- * ATTENTION: the same list is also located in index_m.html!!
51
- * @type {*[]}
52
- */
53
- const forbiddenMirrorLocations = [
54
- 'backup-objects',
55
- 'files',
56
- 'backitup',
57
- '../backups',
58
- '../node_modules',
59
- '../log',
60
- ];
61
-
62
- const utils = require('@iobroker/adapter-core');
63
- const words = require('./lib/words');
64
- const sandBox = require('./lib/sandbox');
65
- const { requestModuleNameByUrl } = require('./lib/nodeModulesManagement.js');
66
- const eventObj = require('./lib/eventObj');
67
- const Scheduler = require('./lib/scheduler');
68
- const { targetTsLib, tsCompilerOptions, jsDeclarationCompilerOptions } = require('./lib/typescriptSettings');
69
- const { hashSource, isObject } = require('./lib/tools');
70
- const { isDeepStrictEqual } = require('node:util');
71
- const {
72
- resolveTypescriptLibs,
73
- resolveTypings,
74
- scriptIdToTSFilename,
75
- transformScriptBeforeCompilation,
76
- transformGlobalDeclarations
77
- } = require('./lib/typescriptTools');
78
-
79
- const packageJson = require('./package.json');
80
- const SCRIPT_CODE_MARKER = 'script.js.';
81
-
82
- const stopCounters = {};
83
- let setStateCountCheckInterval = null;
84
-
85
- let webstormDebug;
86
- let debugMode;
87
- if (process.argv) {
88
- for (let a = 1; a < process.argv.length; a++) {
89
- if (process.argv[a].startsWith('--webstorm')) {
90
- webstormDebug = process.argv[a].replace(/^(.*?=\s*)/, '');
91
- }
92
- if (process.argv[a] === '--debugScript') {
93
- if (!process.argv[a + 1]) {
94
- console.log('No script name provided');
95
- process.exit(300);
96
- } else {
97
- debugMode = process.argv[a + 1];
98
- }
99
- }
100
- }
101
- }
102
-
103
- const isCI = !!process.env.CI;
104
-
105
- // ambient declarations for typescript
106
- /** @type {Record<string, string>} */
107
- let tsAmbient;
108
-
109
- // TypeScript's scripts are only recompiled if their source hash changes.
110
- // If an adapter update fixes the compilation bugs, a user won't notice until the changes and re-saves the script.
111
- // To avoid that, we also include the
112
- // adapter version and TypeScript version in the hash
113
- const tsSourceHashBase = `versions:adapter=${packageJson.version},typescript=${packageJson.dependencies.typescript}`;
114
-
115
- let mirror;
116
-
117
- /** @type {boolean} if logs are subscribed or not */
118
- let logSubscribed;
119
-
120
- /**
121
- * @param {string} scriptID - The current script the declarations were generated from
122
- * @param {string} declarations
123
- */
124
- function provideDeclarationsForGlobalScript(scriptID, declarations) {
125
- // Remember which declarations this global script had access to,
126
- // we need this so the editor doesn't show a duplicate identifier error
127
- if (globalDeclarations != null && globalDeclarations !== '') {
128
- knownGlobalDeclarationsByScript[scriptID] = globalDeclarations;
129
- }
130
- // and concatenate the global declarations for the next scripts
131
- globalDeclarations += declarations + '\n';
132
- // remember all previously generated global declarations,
133
- // so global scripts can reference each other
134
- const globalDeclarationPath = 'global.d.ts';
135
- tsAmbient[globalDeclarationPath] = globalDeclarations;
136
- // make sure the next script compilation has access to the updated declarations
137
- tsServer.provideAmbientDeclarations({
138
- [globalDeclarationPath]: globalDeclarations
139
- });
140
- jsDeclarationServer.provideAmbientDeclarations({
141
- [globalDeclarationPath]: globalDeclarations
142
- });
143
- }
144
-
145
- // taken from here: https://stackoverflow.com/questions/11887934/how-to-check-if-dst-daylight-saving-time-is-in-effect-and-if-so-the-offset
146
- function dstOffsetAtDate(dateInput) {
147
- const fullYear = dateInput.getFullYear() | 0;
148
- // "Leap Years are any year that can be exactly divided by 4 (2012, 2016, etc)
149
- // except if it can be exactly divided by 100, then it isn't (2100, 2200, etc)
150
- // except if it can be exactly divided by 400, then it is (2000, 2400)"
151
- // (https://www.mathsisfun.com/leap-years.html).
152
- const isLeapYear = ((fullYear & 3) | (fullYear/100 & 3)) === 0 ? 1 : 0;
153
- // (fullYear & 3) = (fullYear % 4), but faster
154
- //Alternative:var isLeapYear=(new Date(currentYear,1,29,12)).getDate()===29?1:0
155
- const fullMonth = dateInput.getMonth() | 0;
156
- return (
157
- // 1. We know what the time since the Epoch really is
158
- (+dateInput) // same as the dateInput.getTime() method
159
- // 2. We know what the time since the Epoch at the start of the year is
160
- - (+new Date(fullYear, 0)) // day defaults to 1 if not explicitly zeroed
161
- // 3. Now, subtract what we would expect the time to be if daylight savings
162
- // did not exist. This yields the time-offset due to daylight savings.
163
- - ((
164
- ((
165
- // Calculate the day of the year in the Gregorian calendar
166
- // The code below works based upon the facts of signed right shifts
167
- // • (x) >> n: shifts n and fills in the n highest bits with 0s
168
- // • (-x) >> n: shifts n and fills in the n highest bits with 1s
169
- // (This assumes that x is a positive integer)
170
- -1 + // first day in the year is day 1
171
- (31 & ((-fullMonth) >> 4)) + // January // (-11)>>4 = -1
172
- ((28 + isLeapYear) & ((1 - fullMonth) >> 4)) + // February
173
- (31 & ((2 - fullMonth) >> 4)) + // March
174
- (30 & ((3 - fullMonth) >> 4)) + // April
175
- (31 & ((4 - fullMonth) >> 4)) + // May
176
- (30 & ((5 - fullMonth) >> 4)) + // June
177
- (31 & ((6 - fullMonth) >> 4)) + // July
178
- (31 & ((7 - fullMonth) >> 4)) + // August
179
- (30 & ((8 - fullMonth) >> 4)) + // September
180
- (31 & ((9 - fullMonth) >> 4)) + // October
181
- (30 & ((10 - fullMonth) >> 4)) + // November
182
- // There are no months past December: the year rolls into the next.
183
- // Thus, fullMonth is 0-based, so it will never be 12 in Javascript
184
-
185
- (dateInput.getDate() | 0) // get day of the month
186
-
187
- ) & 0xffff) * 24 * 60 // 24 hours in a day, 60 minutes in an hour
188
- + (dateInput.getHours() & 0xff) * 60 // 60 minutes in an hour
189
- + (dateInput.getMinutes() & 0xff)
190
- )|0) * 60 * 1000 // 60 seconds in a minute * 1000 milliseconds in a second
191
- - (dateInput.getSeconds() & 0xff) * 1000 // 1000 milliseconds in a second
192
- - dateInput.getMilliseconds()
193
- );
194
- }
195
-
196
- function loadTypeScriptDeclarations() {
197
- // try to load the typings on disk for all 3rd party modules
198
- const packages = [
199
- 'node', // this provides auto-completion for most builtins
200
- 'request', // preloaded by the adapter
201
- ];
202
- // Also include user-selected libraries (but only those that are also installed)
203
- if (
204
- adapter.config
205
- && typeof adapter.config.libraries === 'string'
206
- && typeof adapter.config.libraryTypings === 'string'
207
- ) {
208
- const installedLibs = adapter.config.libraries
209
- .split(/[,;\s]+/)
210
- .map((s) => s.trim().split('@')[0])
211
- .filter((s) => !!s);
212
-
213
- const wantsTypings = adapter.config.libraryTypings.split(/[,;\s]+/).map(s => s.trim()).filter(s => !!s);
214
- // Add all installed libraries the user has requested typings for to the list of packages
215
- for (const lib of installedLibs) {
216
- if (wantsTypings.includes(lib) && !packages.includes(lib)) {
217
- packages.push(lib);
218
- }
219
- }
220
- // Some packages have sub-modules (e.g., rxjs/operators) that are not exposed through the main entry point
221
- // If typings are requested for them, also add them if the base module is installed
222
- for (const lib of wantsTypings) {
223
- // Extract the package name and check if we need to add it
224
- if (!lib.includes('/')) {
225
- continue;
226
- }
227
- const pkgName = lib.substr(0, lib.indexOf('/'));
228
-
229
- if (installedLibs.includes(pkgName) && !packages.includes(lib)) {
230
- packages.push(lib);
231
- }
232
- }
233
- }
234
- for (const pkg of packages) {
235
- let pkgTypings = resolveTypings(pkg,
236
- adapter.getAdapterScopedPackageIdentifier ? adapter.getAdapterScopedPackageIdentifier(pkg) : pkg,
237
- // node needs ambient typings, so we don't wrap it in declare module
238
- pkg !== 'node'
239
- );
240
- if (!pkgTypings) {
241
- // Create the empty dummy declarations so users don't get the "not found" error
242
- // for installed packages
243
- pkgTypings = {
244
- [`node_modules/@types/${pkg}/index.d.ts`]: `declare module "${pkg}";`,
245
- };
246
- }
247
- adapter.log.debug(`Loaded TypeScript definitions for ${pkg}: ${JSON.stringify(Object.keys(pkgTypings))}`);
248
- // remember the declarations for the editor
249
- Object.assign(tsAmbient, pkgTypings);
250
- // and give the language servers access to them
251
- tsServer.provideAmbientDeclarations(pkgTypings);
252
- jsDeclarationServer.provideAmbientDeclarations(pkgTypings);
253
- }
254
- }
255
-
256
- const context = {
257
- mods,
258
- objects: {},
259
- states: {},
260
- interimStateValues: {},
261
- stateIds: [],
262
- errorLogFunction: null,
263
- subscriptions: [],
264
- subscriptionsFile: [],
265
- subscriptionsObject: [],
266
- subscribedPatterns: {},
267
- subscribedPatternsFile: {},
268
- adapterSubs: {},
269
- cacheObjectEnums: {},
270
- isEnums: false, // If some subscription wants enum
271
- channels: null,
272
- devices: null,
273
- logWithLineInfo: null,
274
- scheduler: null,
275
- timers: {},
276
- enums: [],
277
- timerId: 0,
278
- names: {},
279
- scripts: {},
280
- messageBusHandlers: {},
281
- logSubscriptions: {},
282
- tempDirectories: {},
283
- folderCreationVerifiedObjects: {},
284
- updateLogSubscriptions,
285
- convertBackStringifiedValues,
286
- updateObjectContext,
287
- prepareStateObject,
288
- debugMode,
289
- timeSettings: {
290
- format12: false,
291
- leadingZeros: true
292
- },
293
- rulesOpened: null, //opened rules
294
- getAbsoluteDefaultDataDir: utils.getAbsoluteDefaultDataDir,
295
- };
296
-
297
- const regExGlobalOld = /_global$/;
298
- const regExGlobalNew = /script\.js\.global\./;
299
-
300
- function checkIsGlobal(obj) {
301
- return obj && obj.common && (regExGlobalOld.test(obj.common.name) || regExGlobalNew.test(obj._id));
302
- }
303
-
304
- function convertBackStringifiedValues(id, state) {
305
- if (state && typeof state.val === 'string' &&
306
- context.objects[id] && context.objects[id].common &&
307
- (context.objects[id].common.type === 'array' || context.objects[id].common.type === 'object')) {
308
- try {
309
- state.val = JSON.parse(state.val);
310
- } catch (err) {
311
- if (id.startsWith('javascript.') || id.startsWith('0_userdata.0')) {
312
- adapter.log.info(`Could not parse value for id ${id} into ${context.objects[id].common.type}: ${err.message}`);
313
- } else {
314
- adapter.log.debug(`Could not parse value for id ${id} into ${context.objects[id].common.type}: ${err.message}`);
315
- }
316
- }
317
- }
318
- return state;
319
- }
320
-
321
- function prepareStateObject(id, state, isAck) {
322
- if (state === null) {
323
- state = { val: null };
324
- }
325
-
326
- if (isAck === true || isAck === false || isAck === 'true' || isAck === 'false') {
327
- if (isObject(state) && state.val !== undefined) {
328
- // we assume that we were given a state object if
329
- // state is an object that contains a `val` property
330
- if (!Object.prototype.hasOwnProperty.call(state, 'ack')) {
331
- state.ack = isAck;
332
- }
333
- } else {
334
- // otherwise assume that the given state is the value to be set
335
- state = {val: state, ack: isAck};
336
- }
337
- }
338
-
339
- if (adapter.config.subscribe) {
340
- return state;
341
- }
342
- // set other values to have a full state object
343
- // mirrors logic from statesInRedis
344
- if (state.ts === undefined) {
345
- state.ts = Date.now();
346
- }
347
-
348
- if (state.q === undefined) {
349
- state.q = 0;
350
- }
351
-
352
- state.from =
353
- typeof state.from === 'string' && state.from !== '' ? state.from : `system.adapter.${adapter.namespace}`;
354
-
355
- if (state.lc === undefined) {
356
- const formerStateValue = context.interimStateValues[id] || context.states[id];
357
- if (!formerStateValue) {
358
- state.lc = state.ts;
359
- } else {
360
- // isDeepStrictEqual works on objects and primitive values
361
- const hasChanged = !isDeepStrictEqual(formerStateValue.val, state.val);
362
- if (!formerStateValue.lc || hasChanged) {
363
- state.lc = state.ts;
364
- } else {
365
- state.lc = formerStateValue.lc;
366
- }
367
- }
368
- }
369
-
370
- return state;
371
- }
372
-
373
- function fileMatching(sub, id, fileName) {
374
- if (sub.idRegEx) {
375
- if (!sub.idRegEx.test(id)) {
376
- return false;
377
- }
378
- } else {
379
- if (sub.id !== id) {
380
- return false;
381
- }
382
- }
383
- if (sub.fileRegEx) {
384
- if (!sub.fileRegEx.test(fileName)) {
385
- return false;
386
- }
387
- } else {
388
- if (sub.fileNamePattern !== fileName) {
389
- return false;
390
- }
391
- }
392
-
393
- return true;
394
- }
395
-
396
- /**
397
- * @type {Set<string>}
398
- * Stores the IDs of script objects whose change should be ignored because
399
- * the compiled source was just updated
400
- */
401
- const ignoreObjectChange = new Set();
402
-
403
- let objectsInitDone = false;
404
- let statesInitDone = false;
405
-
406
- /** @type {ioBroker.Adapter} */
407
- let adapter;
408
-
409
- function startAdapter(options) {
410
- options = options || {};
411
- Object.assign(options, {
412
- name: 'javascript',
413
- useFormatDate: true, // load float formatting
414
-
415
- /**
416
- * @param id { string }
417
- * @param obj { ioBroker.Object }
418
- */
419
- objectChange: (id, obj) => {
420
- // Check if we should ignore this change (once!) because we just updated the compiled sources
421
- if (ignoreObjectChange.has(id)) {
422
- // Update the cached script object and do nothing more
423
- context.objects[id] = obj;
424
- ignoreObjectChange.delete(id);
425
- return;
426
- }
427
-
428
- // When still in initializing: already remember current values,
429
- // but data structures are initialized elsewhere
430
- if (!objectsInitDone) {
431
- if (obj) {
432
- context.objects[id] = obj;
433
- }
434
- return;
435
- }
436
-
437
-
438
- if (id.startsWith('enum.')) {
439
- // clear cache
440
- context.cacheObjectEnums = {};
441
-
442
- // update context.enums array
443
- if (obj) {
444
- // If new
445
- if (!context.enums.includes(id)) {
446
- context.enums.push(id);
447
- context.enums.sort();
448
- }
449
- } else {
450
- const pos = context.enums.indexOf(id);
451
- // if deleted
452
- if (pos !== -1) {
453
- context.enums.splice(pos, 1);
454
- }
455
- }
456
- }
457
-
458
- if (obj && id === 'system.config') {
459
- // set language for debug messages
460
- if (obj.common && obj.common.language) {
461
- words.setLanguage(obj.common.language);
462
- }
463
- }
464
-
465
- // update stored time format for variables.dayTime
466
- if (id === adapter.namespace + '.variables.dayTime' && obj && obj.native) {
467
- context.timeSettings.format12 = obj.native.format12 || false;
468
- context.timeSettings.leadingZeros = obj.native.leadingZeros === undefined ? true : obj.native.leadingZeros;
469
- }
470
-
471
- // send changes to disk mirror
472
- mirror && mirror.onObjectChange(id, obj);
473
-
474
- const formerObj = context.objects[id];
475
-
476
- updateObjectContext(id, obj); // Update all Meta object data
477
-
478
- // for alias object changes on state objects we need to manually update the
479
- // state cache value because new value is only published on next change
480
- if (obj && obj.type === 'state' && id.startsWith('alias.0.')) {
481
- adapter.getForeignState(id, (err, state) => {
482
- if (err) {
483
- return;
484
- }
485
- if (state) {
486
- context.states[id] = state;
487
- } else if (context.states[id] !== undefined) {
488
- delete context.states[id];
489
- }
490
- });
491
- }
492
-
493
- context.subscriptionsObject.forEach(sub => {
494
- // ToDo: implement comparing with id.0.* too
495
- if (sub.pattern === id) {
496
- try {
497
- sub.callback(id, obj);
498
- } catch (err) {
499
- adapter.log.error(`Error in callback: ${err}`);
500
- }
501
- }
502
- });
503
-
504
- // handle Script object updates
505
- if (!obj && formerObj && formerObj.type === 'script') {
506
- // Object Deleted just now
507
- if (checkIsGlobal(formerObj)) {
508
- // it was a global Script, and it was enabled and is now deleted => restart adapter
509
- if (formerObj.enabled) {
510
- adapter.log.info(`Active global Script ${id} deleted. Restart instance.`);
511
- adapter.restart();
512
- }
513
- } else if (formerObj.common && formerObj.common.engine === `system.adapter.${adapter.namespace}`) {
514
- // It was a non-global Script and deleted => stop and remove it
515
- stop(id);
516
-
517
- // delete scriptEnabled.blabla variable
518
- const idActive = 'scriptEnabled.' + id.substring(SCRIPT_CODE_MARKER.length);
519
- adapter.delObject(idActive);
520
- adapter.delState(idActive);
521
-
522
- // delete scriptProblem.blabla variable
523
- const idProblem = 'scriptProblem.' + id.substring(SCRIPT_CODE_MARKER.length);
524
- adapter.delObject(idProblem);
525
- adapter.delState(idProblem);
526
- }
527
- } else if (!formerObj && obj && obj.type === 'script') {
528
- // New script that does not exist before
529
- if (checkIsGlobal(obj)) {
530
- // new global script added => restart adapter
531
- if (obj.common.enabled) {
532
- adapter.log.info(`Active global Script ${id} created. Restart instance.`);
533
- adapter.restart();
534
- }
535
- } else if (obj.common && obj.common.engine === `system.adapter.${adapter.namespace}`) {
536
- // new non-global script - create states for scripts
537
- createActiveObject(id, obj.common.enabled, () => createProblemObject(id));
538
- if (obj.common.enabled) {
539
- // if enabled => Start script
540
- load(id);
541
- }
542
- }
543
- } else if (obj && obj.type === 'script' && formerObj && formerObj.common) {
544
- // Script changed ...
545
- if (checkIsGlobal(obj)) {
546
- if (obj.common.enabled || formerObj.common.enabled) {
547
- adapter.log.info(`Global Script ${id} updated. Restart instance.`);
548
- adapter.restart();
549
- }
550
- } else { // No global script
551
- if (obj.common && obj.common.engine === 'system.adapter.' + adapter.namespace) {
552
- // create states for scripts
553
- createActiveObject(id, obj.common.enabled, () => createProblemObject(id));
554
- }
555
-
556
- if ((formerObj.common.enabled && !obj.common.enabled) ||
557
- (formerObj.common.engine === 'system.adapter.' + adapter.namespace && obj.common.engine !== 'system.adapter.' + adapter.namespace)) {
558
-
559
- // Script disabled
560
- if (formerObj.common.enabled && formerObj.common.engine === 'system.adapter.' + adapter.namespace) {
561
- // Remove it from executing
562
- stop(id);
563
- }
564
- } else if ((!formerObj.common.enabled && obj.common.enabled) ||
565
- (formerObj.common.engine !== 'system.adapter.' + adapter.namespace && obj.common.engine === 'system.adapter.' + adapter.namespace)) {
566
- // Script enabled
567
-
568
- if (obj.common.enabled && obj.common.engine === 'system.adapter.' + adapter.namespace) {
569
- // Start script
570
- load(id);
571
- }
572
- } else { //if (obj.common.source !== formerObj.common.source) {
573
- // Source changed => restart it
574
- stopCounters[id] = stopCounters[id] ? stopCounters[id] + 1 : 1;
575
- stop(id, (res, _id) =>
576
- // only start again after stop when "last" object change to prevent problems on
577
- // multiple changes in fast frequency
578
- !--stopCounters[id] && load(_id));
579
- }
580
- }
581
- }
582
- },
583
-
584
- stateChange: (id, state) => {
585
- if (context.interimStateValues[id] !== undefined) {
586
- // any update invalidates the remembered interim value
587
- delete context.interimStateValues[id];
588
- }
589
- if (!id || id.startsWith('messagebox.') || id.startsWith('log.')) {
590
- return;
591
- }
592
-
593
- if (id === `${adapter.namespace}.debug.to` && state && !state.ack) {
594
- return !debugMode && debugSendToInspector(state.val);
595
- }
596
-
597
- // When still in initializing: already remember current values,
598
- // but data structures are initialized elsewhere
599
- if (!statesInitDone) {
600
- if (state) {
601
- context.states[id] = state;
602
- }
603
- return;
604
- }
605
-
606
- const oldState = context.states[id];
607
- if (state) {
608
- if (oldState) {
609
- // enable or disable script
610
- if (!state.ack && id.startsWith(activeStr) && context.objects[id] && context.objects[id].native && context.objects[id].native.script) {
611
- adapter.extendForeignObject(context.objects[id].native.script, { common: { enabled: state.val } });
612
- }
613
-
614
- // monitor if adapter is alive and send all subscriptions once more, after adapter goes online
615
- if (/*oldState && */oldState.val === false && state.val && id.endsWith('.alive')) {
616
- if (context.adapterSubs[id]) {
617
- const parts = id.split('.');
618
- const a = `${parts[2]}.${parts[3]}`;
619
- for (let t = 0; t < context.adapterSubs[id].length; t++) {
620
- adapter.log.info(`Detected coming adapter "${a}". Send subscribe: ${context.adapterSubs[id][t]}`);
621
- adapter.sendTo(a, 'subscribe', context.adapterSubs[id][t]);
622
- }
623
- }
624
- }
625
- } else if (/*!oldState && */!context.stateIds.includes(id)) {
626
- context.stateIds.push(id);
627
- context.stateIds.sort();
628
- }
629
- context.states[id] = state;
630
- } else {
631
- if (oldState) delete context.states[id];
632
- state = {};
633
- const pos = context.stateIds.indexOf(id);
634
- if (pos !== -1) {
635
- context.stateIds.splice(pos, 1);
636
- }
637
- }
638
- const _eventObj = eventObj.createEventObject(context, id, context.convertBackStringifiedValues(id, state), context.convertBackStringifiedValues(id, oldState));
639
-
640
- // if this state matches any subscriptions
641
- for (let i = 0, l = context.subscriptions.length; i < l; i++) {
642
- const sub = context.subscriptions[i];
643
- if (sub && patternMatching(_eventObj, sub.patternCompareFunctions)) {
644
- try {
645
- sub.callback(_eventObj);
646
- } catch (err) {
647
- adapter.log.error(`Error in callback: ${err}`);
648
- }
649
- }
650
- }
651
- },
652
-
653
- fileChange: (id, fileName, size) => {
654
- // if this file matches any subscriptions
655
- for (let i = 0, l = context.subscriptionsFile.length; i < l; i++) {
656
- const sub = context.subscriptionsFile[i];
657
- if (sub && fileMatching(sub, id, fileName)) {
658
- try {
659
- sub.callback(id, fileName, size, sub.withFile);
660
- } catch (err) {
661
- adapter.log.error(`Error in callback: ${err}`);
662
- }
663
- }
664
- }
665
- },
666
-
667
- unload: callback => {
668
- debugStop()
669
- .then(() => {
670
- stopTimeSchedules();
671
- setStateCountCheckInterval && clearInterval(setStateCountCheckInterval);
672
- stopAllScripts(callback);
673
- });
674
- },
675
-
676
- ready: () => {
677
- adapter.config.maxSetStatePerMinute = parseInt(adapter.config.maxSetStatePerMinute, 10) || 1000;
678
- adapter.config.maxTriggersPerScript = parseInt(adapter.config.maxTriggersPerScript, 10) || 100;
679
-
680
- if (adapter.supportsFeature && adapter.supportsFeature('PLUGINS')) {
681
- const sentryInstance = adapter.getPluginInstance('sentry');
682
- if (sentryInstance) {
683
- const Sentry = sentryInstance.getSentryObject();
684
- if (Sentry) {
685
- Sentry.configureScope(scope => {
686
- scope.addEventProcessor((event, _hint) => {
687
- if (event.exception && event.exception.values && event.exception.values[0]) {
688
- const eventData = event.exception.values[0];
689
- if (eventData.stacktrace && eventData.stacktrace.frames && Array.isArray(eventData.stacktrace.frames) && eventData.stacktrace.frames.length) {
690
- // Exclude event if script Marker is included
691
- if (eventData.stacktrace.frames.find(frame => frame.filename && frame.filename.includes(SCRIPT_CODE_MARKER))) {
692
- return null;
693
- }
694
- //Exclude event if own directory is included but not inside own node_modules
695
- const ownNodeModulesDir = nodePath.join(__dirname, 'node_modules');
696
- if (!eventData.stacktrace.frames.find(frame => frame.filename && frame.filename.includes(__dirname) && !frame.filename.includes(ownNodeModulesDir))) {
697
- return null;
698
- }
699
- // We have exception data and do not sorted it out, so report it
700
- return event;
701
- }
702
- }
703
-
704
- // No exception in it ... do not report
705
- return null;
706
- });
707
- main();
708
- });
709
- } else {
710
- main();
711
- }
712
- } else {
713
- main();
714
- }
715
- } else {
716
- main();
717
- }
718
- },
719
-
720
- message: obj => {
721
- if (obj) {
722
- switch (obj.command) {
723
- // process messageTo commands
724
- case 'toScript':
725
- case 'jsMessageBus':
726
- if (obj.message && (
727
- obj.message.instance === null ||
728
- obj.message.instance === undefined ||
729
- ('javascript.' + obj.message.instance === adapter.namespace) ||
730
- (obj.message.instance === adapter.namespace)
731
- )) {
732
- Object.keys(context.messageBusHandlers).forEach(name => {
733
- // script name could be script.js.xxx or only xxx
734
- if ((!obj.message.script || obj.message.script === name) && context.messageBusHandlers[name][obj.message.message]) {
735
- context.messageBusHandlers[name][obj.message.message].forEach(handler => {
736
- const sandbox = handler.sandbox;
737
-
738
- sandbox.verbose && sandbox.log(`onMessage: ${JSON.stringify(obj.message)}`, 'info');
739
-
740
- try {
741
- if (obj.callback) {
742
- handler.cb.call(sandbox, obj.message.data, result => {
743
- sandbox.verbose && sandbox.log(`onMessage result: ${JSON.stringify(result)}`, 'info');
744
-
745
- adapter.sendTo(obj.from, obj.command, result, obj.callback);
746
- });
747
- } else {
748
- handler.cb.call(sandbox, obj.message.data, result => {
749
- sandbox.verbose && sandbox.log(`onMessage result: ${JSON.stringify(result)}`, 'info');
750
- });
751
- }
752
- } catch (e) {
753
- adapter.setState('scriptProblem.' + name.substring(SCRIPT_CODE_MARKER.length), true, true);
754
- context.logError('Error in callback', e);
755
- }
756
- });
757
- }
758
- });
759
- }
760
- break;
761
-
762
- case 'loadTypings': { // Load typings for the editor
763
- const typings = {};
764
-
765
- // try to load TypeScript lib files from disk
766
- try {
767
- const typescriptLibs = resolveTypescriptLibs(targetTsLib);
768
- Object.assign(typings, typescriptLibs);
769
- } catch (e) { /* ok, no lib then */
770
- }
771
-
772
- // provide the already-loaded ioBroker typings and global script declarations
773
- Object.assign(typings, tsAmbient);
774
-
775
- // also provide the known global declarations for each global script
776
- for (const globalScriptPaths of Object.keys(knownGlobalDeclarationsByScript)) {
777
- typings[`${globalScriptPaths}.d.ts`] = knownGlobalDeclarationsByScript[globalScriptPaths];
778
- }
779
-
780
- if (obj.callback) {
781
- adapter.sendTo(obj.from, obj.command, {typings}, obj.callback);
782
- }
783
- break;
784
- }
785
-
786
- case 'calcAstroAll': {
787
- if (obj.message) {
788
- const sunriseOffset = parseInt(obj.message.sunriseOffset === undefined ? adapter.config.sunriseOffset : obj.message.sunriseOffset, 10) || 0;
789
- const sunsetOffset = parseInt(obj.message.sunsetOffset === undefined ? adapter.config.sunsetOffset : obj.message.sunsetOffset, 10) || 0;
790
- const longitude = parseFloat(obj.message.longitude === undefined ? adapter.config.longitude : obj.message.longitude) || 0;
791
- const latitude = parseFloat(obj.message.latitude === undefined ? adapter.config.latitude : obj.message.latitude) || 0;
792
- const today = getAstroStartOfDay();
793
- let astroEvents = {};
794
- try {
795
- astroEvents = mods.suncalc.getTimes(today, latitude, longitude);
796
- } catch (e) {
797
- adapter.log.error(`Cannot calculate astro data: ${e}`);
798
- }
799
- if (astroEvents) {
800
- try {
801
- astroEvents.nextSunrise = getAstroEvent(
802
- today,
803
- obj.message.sunriseEvent || adapter.config.sunriseEvent,
804
- obj.message.sunriseLimitStart || adapter.config.sunriseLimitStart,
805
- obj.message.sunriseLimitEnd || adapter.config.sunriseLimitEnd,
806
- sunriseOffset,
807
- false,
808
- latitude,
809
- longitude,
810
- true
811
- );
812
- astroEvents.nextSunset = getAstroEvent(
813
- today,
814
- obj.message.sunsetEvent || adapter.config.sunsetEvent,
815
- obj.message.sunsetLimitStart || adapter.config.sunsetLimitStart,
816
- obj.message.sunsetLimitEnd || adapter.config.sunsetLimitEnd,
817
- sunsetOffset,
818
- true,
819
- latitude,
820
- longitude,
821
- true
822
- );
823
- } catch (e) {
824
- adapter.log.error(`Cannot calculate astro data: ${e}`);
825
- }
826
- }
827
-
828
- const result = {};
829
- const keys = Object.keys(astroEvents).sort((a, b) => astroEvents[a] - astroEvents[b]);
830
- keys.forEach(key => {
831
- const validDate = astroEvents[key] !== null && !isNaN(astroEvents[key].getTime());
832
-
833
- result[key] = {
834
- isValidDate: validDate,
835
- serverTime: validDate ? formatHoursMinutesSeconds(astroEvents[key]) : 'n/a',
836
- date: validDate ? astroEvents[key].toISOString() : 'n/a',
837
- };
838
- });
839
-
840
- obj.callback && adapter.sendTo(obj.from, obj.command, result, obj.callback);
841
- }
842
- break;
843
- }
844
-
845
- case 'calcAstro': {
846
- if (obj.message) {
847
- const longitude = parseFloat(obj.message.longitude === undefined ? adapter.config.longitude : obj.message.longitude) || 0;
848
- const latitude = parseFloat(obj.message.latitude === undefined ? adapter.config.latitude : obj.message.latitude) || 0;
849
- const today = getAstroStartOfDay();
850
-
851
- const sunriseEvent = obj.message?.sunriseEvent || adapter.config.sunriseEvent;
852
- const sunriseLimitStart = obj.message?.sunriseLimitStart || adapter.config.sunriseLimitStart;
853
- const sunriseLimitEnd = obj.message?.sunriseLimitEnd || adapter.config.sunriseLimitEnd;
854
- const sunriseOffset = parseInt(obj.message.sunriseOffset === undefined ? adapter.config.sunriseOffset : obj.message.sunriseOffset, 10) || 0;
855
- const nextSunrise = getAstroEvent(
856
- today,
857
- sunriseEvent,
858
- sunriseLimitStart,
859
- sunriseLimitEnd,
860
- sunriseOffset,
861
- false,
862
- latitude,
863
- longitude,
864
- true
865
- );
866
-
867
- const sunsetEvent = obj.message?.sunsetEvent || adapter.config.sunsetEvent;
868
- const sunsetLimitStart = obj.message?.sunsetLimitStart || adapter.config.sunsetLimitStart;
869
- const sunsetLimitEnd = obj.message?.sunsetLimitEnd || adapter.config.sunsetLimitEnd;
870
- const sunsetOffset = parseInt(obj.message.sunsetOffset === undefined ? adapter.config.sunsetOffset : obj.message.sunsetOffset, 10) || 0;
871
- const nextSunset = getAstroEvent(
872
- today,
873
- sunsetEvent,
874
- sunsetLimitStart,
875
- sunsetLimitEnd,
876
- sunsetOffset,
877
- true,
878
- latitude,
879
- longitude,
880
- true
881
- );
882
-
883
- const validDateSunrise = nextSunrise !== null && !isNaN(nextSunrise.getTime());
884
- const validDateSunset = nextSunset !== null && !isNaN(nextSunset.getTime());
885
-
886
- adapter.log.debug(`calcAstro sunrise: ${sunriseEvent} -> start ${sunriseLimitStart}, end: ${sunriseLimitEnd}, offset: ${sunriseOffset} - ${validDateSunrise ? nextSunrise.toISOString() : 'n/a'}`);
887
- adapter.log.debug(`calcAstro sunset: ${sunsetEvent} -> start ${sunsetLimitStart}, end: ${sunsetLimitEnd}, offset: ${sunsetOffset} - ${validDateSunset ? nextSunset.toISOString() : 'n/a'}`);
888
-
889
- obj.callback && adapter.sendTo(obj.from, obj.command, {
890
- nextSunrise: {
891
- isValidDate: validDateSunrise,
892
- serverTime: validDateSunrise ? formatHoursMinutesSeconds(nextSunrise) : 'n/a',
893
- date: nextSunrise.toISOString(),
894
- },
895
- nextSunset: {
896
- isValidDate: validDateSunset,
897
- serverTime: validDateSunset ? formatHoursMinutesSeconds(nextSunset) : 'n/a',
898
- date: nextSunset.toISOString(),
899
- }
900
- }, obj.callback);
901
- }
902
- break;
903
- }
904
-
905
- case 'debug': {
906
- !debugMode && debugStart(obj.message);
907
- break;
908
- }
909
-
910
- case 'debugStop': {
911
- !debugMode && debugStop()
912
- .then(() => console.log('stopped'));
913
- break;
914
- }
915
-
916
- case 'rulesOn': {
917
- context.rulesOpened = obj.message;
918
- console.log('Enable messaging for ' + context.rulesOpened);
919
- break;
920
- }
921
-
922
- case 'rulesOff': {
923
- // may be if (context.rulesOpened === obj.message)
924
- console.log('Disable messaging for ' + context.rulesOpened);
925
- context.rulesOpened = null;
926
- break;
927
- }
928
-
929
- case 'getIoBrokerDataDir': {
930
- obj.callback && adapter.sendTo(obj.from, obj.command, {
931
- dataDir: utils.getAbsoluteDefaultDataDir(),
932
- sep: nodePath.sep
933
- }, obj.callback);
934
- break;
935
- }
936
- }
937
- }
938
- },
939
-
940
- /**
941
- * If the JS-Controller catches an unhandled error, this will be called
942
- * so we have a chance to handle it ourself.
943
- * @param {Error} err
944
- */
945
- error: err => {
946
- // Identify unhandled errors originating from callbacks in scripts
947
- // These are not caught by wrapping the execution code in try-catch
948
- if (err && typeof err.stack === 'string') {
949
- const scriptCodeMarkerIndex = err.stack.indexOf(SCRIPT_CODE_MARKER);
950
- if (scriptCodeMarkerIndex > -1) {
951
- // This is a script error
952
- let scriptName = err.stack.substr(scriptCodeMarkerIndex);
953
- scriptName = scriptName.substr(0, scriptName.indexOf(':'));
954
- context.logError(scriptName, err);
955
-
956
- // Leave the script running for now
957
- // signal to the JS-Controller that we handled the error ourselves
958
- return true;
959
- }
960
- // check if a path contains adaptername but not own node_module
961
- // this regex matched "iobroker.javascript/" if NOT followed by "node_modules"
962
- if (!err.stack.match(/iobroker\.javascript[/\\](?!.*node_modules).*/g)) {
963
- // This is an error without any info on origin (mostly async errors like connection errors)
964
- // also consider it as being from a script
965
- adapter.log.error('An error happened which is most likely from one of your scripts, but the originating script could not be detected.');
966
- adapter.log.error('Error: ' + err.message);
967
- adapter.log.error(err.stack);
968
-
969
- // signal to the JS-Controller that we handled the error ourselves
970
- return true;
971
- }
972
- }
973
- }
974
- });
975
-
976
- adapter = new utils.Adapter(options);
977
-
978
- // handler for logs
979
- adapter.on('log', msg =>
980
- Object.keys(context.logSubscriptions).forEach(name =>
981
- context.logSubscriptions[name].forEach(handler => {
982
- if (typeof handler.cb === 'function' && (handler.severity === '*' || handler.severity === msg.severity)) {
983
- handler.sandbox.logHandler = handler.severity || '*';
984
- handler.cb.call(handler.sandbox, msg);
985
- handler.sandbox.logHandler = null;
986
- }
987
- })));
988
-
989
- context.adapter = adapter;
990
-
991
- return adapter;
992
- }
993
-
994
- function updateObjectContext(id, obj) {
995
- if (obj) {
996
- // add state to state ID's list
997
- if (obj.type === 'state') {
998
- if (!context.stateIds.includes(id)) {
999
- context.stateIds.push(id);
1000
- context.stateIds.sort();
1001
- }
1002
- if (context.devices && context.channels) {
1003
- const parts = id.split('.');
1004
- parts.pop();
1005
- const chn = parts.join('.');
1006
- context.channels[chn] = context.channels[chn] || [];
1007
- context.channels[chn].push(id);
1008
-
1009
- parts.pop();
1010
- const dev = parts.join('.');
1011
- context.devices[dev] = context.devices[dev] || [];
1012
- context.devices[dev].push(id);
1013
- }
1014
- }
1015
- } else {
1016
- // delete object from state ID's list
1017
- const pos = context.stateIds.indexOf(id);
1018
- pos !== -1 && context.stateIds.splice(pos, 1);
1019
- if (context.devices && context.channels) {
1020
- const parts = id.split('.');
1021
- parts.pop();
1022
- const chn = parts.join('.');
1023
- if (context.channels[chn]) {
1024
- const posChn = context.channels[chn].indexOf(id);
1025
- posChn !== -1 && context.channels[chn].splice(posChn, 1);
1026
- }
1027
-
1028
- parts.pop();
1029
- const dev = parts.join('.');
1030
- if (context.devices[dev]) {
1031
- const posDev = context.devices[dev].indexOf(id);
1032
- posDev !== -1 && context.devices[dev].splice(posDev, 1);
1033
- }
1034
- }
1035
-
1036
- delete context.folderCreationVerifiedObjects[id];
1037
- }
1038
-
1039
- if (!obj && context.objects[id]) {
1040
- // objects was deleted
1041
- removeFromNames(id);
1042
- delete context.objects[id];
1043
- } else if (obj && !context.objects[id]) {
1044
- // object was added
1045
- context.objects[id] = obj;
1046
- addToNames(obj);
1047
- } else if (obj && context.objects[id].common) {
1048
- // Object just changed
1049
- context.objects[id] = obj;
1050
-
1051
- const n = getName(id);
1052
- let nn = context.objects[id].common ? context.objects[id].common.name : '';
1053
-
1054
- if (nn && typeof nn === 'object') {
1055
- nn = nn[words.getLanguage()] || nn.en;
1056
- }
1057
-
1058
- if (n !== nn) {
1059
- if (n) {
1060
- removeFromNames(id);
1061
- }
1062
- if (nn) {
1063
- addToNames(obj);
1064
- }
1065
- }
1066
- }
1067
- }
1068
-
1069
- function main() {
1070
- !debugMode && patchFont()
1071
- .then(patched => patched && adapter.log.debug('Font patched'));
1072
-
1073
- adapter.log.debug(`config.subscribe (Do not subscribe all states on start): ${adapter.config.subscribe}`);
1074
-
1075
- // correct jsonConfig for admin
1076
- adapter.getForeignObject(`system.adapter.${adapter.namespace}`, (err, obj) => {
1077
- if (obj && obj.common) {
1078
- if (obj.common.adminUI?.config !== 'json') {
1079
- obj.common.adminUI = obj.common.adminUI || {};
1080
- obj.common.adminUI.config = 'json';
1081
- adapter.setForeignObject(obj._id, obj);
1082
- }
1083
- }
1084
- });
1085
-
1086
- // todo
1087
- context.errorLogFunction = webstormDebug ? console : adapter.log;
1088
- activeStr = `${adapter.namespace}.scriptEnabled.`;
1089
-
1090
- mods.fs = new require('./lib/protectFs')(adapter.log, utils.getAbsoluteDefaultDataDir());
1091
- mods['fs/promises'] = mods.fs.promises; // to avoid require('fs/promises');
1092
-
1093
- // try to read TS declarations
1094
- try {
1095
- tsAmbient = {
1096
- 'javascript.d.ts': nodeFS.readFileSync(mods.path.join(__dirname, 'lib/javascript.d.ts'), 'utf8')
1097
- };
1098
- tsServer.provideAmbientDeclarations(tsAmbient);
1099
- jsDeclarationServer.provideAmbientDeclarations(tsAmbient);
1100
- } catch (e) {
1101
- adapter.log.warn(`Could not read TypeScript ambient declarations: ${e.message}`);
1102
- // This should not happen, so send a error report to Sentry
1103
- if (adapter.supportsFeature && adapter.supportsFeature('PLUGINS')) {
1104
- const sentryInstance = adapter.getPluginInstance('sentry');
1105
- if (sentryInstance) {
1106
- const sentryObject = sentryInstance.getSentryObject();
1107
- if (sentryObject) sentryObject.captureException(e);
1108
- }
1109
- }
1110
- // Keep the adapter from crashing when the included typings cannot be read
1111
- tsAmbient = {};
1112
- }
1113
-
1114
- context.logWithLineInfo = function (level, msg) {
1115
- if (msg === undefined) {
1116
- return context.logWithLineInfo('info', msg);
1117
- }
1118
-
1119
- context.errorLogFunction && context.errorLogFunction[level](msg);
1120
-
1121
- const stack = (new Error().stack).split('\n');
1122
-
1123
- for (let i = 3; i < stack.length; i++) {
1124
- if (!stack[i]) {
1125
- continue;
1126
- }
1127
- if (stack[i].match(/runInContext|runInNewContext|javascript\.js:/)) {
1128
- break;
1129
- }
1130
- context.errorLogFunction && context.errorLogFunction[level](fixLineNo(stack[i]));
1131
- }
1132
- };
1133
-
1134
- context.logWithLineInfo.warn = context.logWithLineInfo.bind(1, 'warn');
1135
- context.logWithLineInfo.error = context.logWithLineInfo.bind(1, 'error');
1136
- context.logWithLineInfo.info = context.logWithLineInfo.bind(1, 'info');
1137
-
1138
- installLibraries().then(() => {
1139
-
1140
- // Load the TS declarations for Node.js and all 3rd party modules
1141
- loadTypeScriptDeclarations();
1142
-
1143
- getData(() => {
1144
- context.scheduler = new Scheduler(adapter.log, Date, mods.suncalc, adapter.config.latitude, adapter.config.longitude);
1145
- dayTimeSchedules(adapter, context);
1146
- sunTimeSchedules(adapter, context);
1147
- timeSchedule(adapter, context);
1148
-
1149
- // Warning. It could have a side-effect in compact mode, so all adapters will accept self signed certificates
1150
- if (adapter.config.allowSelfSignedCerts) {
1151
- process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
1152
- }
1153
-
1154
- adapter.getObjectView('script', 'javascript', {}, async (err, doc) => {
1155
- globalScript = '';
1156
- globalDeclarations = '';
1157
- knownGlobalDeclarationsByScript = {};
1158
- if (doc && doc.rows && doc.rows.length) {
1159
- // assemble global script
1160
- for (let g = 0; g < doc.rows.length; g++) {
1161
- const obj = doc.rows[g].value;
1162
- if (checkIsGlobal(obj)) {
1163
- if (obj && obj.common) {
1164
- const engineType = (obj.common.engineType || '').toLowerCase();
1165
-
1166
- if (obj.common.enabled) {
1167
- if (engineType.startsWith('typescript')) {
1168
- // TypeScript
1169
- adapter.log.info(`${obj._id}: compiling TypeScript source...`);
1170
- // In order to compile global TypeScript, we need to do some transformations
1171
- // 1. For top-level-await, some statements must be wrapped in an immediately-invoked async function
1172
- // 2. If any global script uses `import`, the declarations are no longer visible if they are not exported with `declare global`
1173
- const transformedSource = transformScriptBeforeCompilation(obj.common.source, true);
1174
- // The source code must be transformed in order to support top level await
1175
- // Global scripts must not be treated as a module, otherwise their methods
1176
- // cannot be found by the normal scripts
1177
- // We need to hash both global declarations that are known until now
1178
- // AND the script source, because changing either can change the compilation output
1179
- const sourceHash = hashSource(tsSourceHashBase + globalDeclarations + transformedSource);
1180
-
1181
- /** @type {string | undefined} */
1182
- let compiled;
1183
- /** @type {string | undefined} */
1184
- let declarations;
1185
- // If we already stored the compiled source code and the original source hash,
1186
- // use the hash to check whether we can rely on the compiled source code or
1187
- // if we need to compile it again
1188
- if (
1189
- typeof obj.common.compiled === 'string'
1190
- && typeof obj.common.sourceHash === 'string'
1191
- && sourceHash === obj.common.sourceHash
1192
- ) {
1193
- // We can reuse the stored source
1194
- compiled = obj.common.compiled;
1195
- declarations = obj.common.declarations;
1196
- adapter.log.info(`${obj._id}: source code did not change, using cached compilation result...`);
1197
- } else {
1198
- // We don't have a hashed source code or the original source changed, compile it
1199
- const filename = scriptIdToTSFilename(obj._id);
1200
- let tsCompiled;
1201
- try {
1202
- tsCompiled = tsServer.compile(filename, transformedSource);
1203
- } catch (e) {
1204
- adapter.log.error(`${obj._id}: TypeScript compilation failed:\n${e}`);
1205
- continue;
1206
- }
1207
-
1208
- const errors = tsCompiled.diagnostics.map(diag => diag.annotatedSource + '\n').join('\n');
1209
-
1210
- if (tsCompiled.success) {
1211
- if (errors.length > 0) {
1212
- adapter.log.warn(`${obj._id}: TypeScript compilation completed with errors:\n${errors}`);
1213
- } else {
1214
- adapter.log.info(`${obj._id}: TypeScript compilation successful`);
1215
- }
1216
- compiled = tsCompiled.result;
1217
- // Global scripts that have been transformed to support `import` need to have their declarations transformed aswell
1218
- declarations = transformGlobalDeclarations(tsCompiled.declarations || '');
1219
-
1220
- const newCommon = {
1221
- sourceHash,
1222
- compiled,
1223
- };
1224
- if (declarations) newCommon.declarations = declarations;
1225
-
1226
- // Store the compiled source and the original source hash, so we don't need to do the work again next time
1227
- ignoreObjectChange.add(obj._id); // ignore the next change and don't restart scripts
1228
- adapter.extendForeignObject(obj._id, {
1229
- common: newCommon
1230
- });
1231
- } else {
1232
- adapter.log.error(`${obj._id}: TypeScript compilation failed:\n${errors}`);
1233
- continue;
1234
- }
1235
- }
1236
- globalScript += compiled + '\n';
1237
- // if declarations were generated, remember them
1238
- if (declarations != null) {
1239
- provideDeclarationsForGlobalScript(obj._id, declarations);
1240
- }
1241
- } else { // javascript
1242
- const sourceCode = obj.common.source;
1243
- globalScript += sourceCode + '\n';
1244
-
1245
- // try to compile the declarations so TypeScripts can use
1246
- // functions defined in global JavaScripts
1247
- const filename = scriptIdToTSFilename(obj._id);
1248
- let tsCompiled;
1249
- try {
1250
- tsCompiled = jsDeclarationServer.compile(filename, sourceCode);
1251
- } catch (e) {
1252
- adapter.log.warn(`${obj._id}: Error while generating type declarations, skipping:\n${e}`);
1253
- continue;
1254
- }
1255
- // if declarations were generated, remember them
1256
- if (tsCompiled.success && tsCompiled.declarations != null) {
1257
- provideDeclarationsForGlobalScript(obj._id, tsCompiled.declarations);
1258
- }
1259
- }
1260
-
1261
- }
1262
- }
1263
- }
1264
- }
1265
- }
1266
-
1267
- globalScript = globalScript.replace(/\r\n/g, '\n');
1268
- globalScriptLines = globalScript.split(/\n/g).length - 1;
1269
-
1270
- if (doc && doc.rows && doc.rows.length) {
1271
- // load all scripts
1272
- for (let i = 0; i < doc.rows.length; i++) {
1273
- if (!checkIsGlobal(doc.rows[i].value)) {
1274
- load(doc.rows[i].value);
1275
- }
1276
- }
1277
- }
1278
-
1279
- if (adapter.config.mirrorPath) {
1280
- adapter.config.mirrorInstance = parseInt(adapter.config.mirrorInstance, 10) || 0;
1281
- if (adapter.instance === adapter.config.mirrorInstance) {
1282
- const ioBDataDir = utils.getAbsoluteDefaultDataDir() + nodePath.sep;
1283
- adapter.config.mirrorPath = nodePath.normalize(adapter.config.mirrorPath);
1284
- let mirrorForbidden = false;
1285
- for (let dir of forbiddenMirrorLocations) {
1286
- dir = nodePath.join(ioBDataDir, dir) + nodePath.sep;
1287
- if (dir.includes(adapter.config.mirrorPath) || adapter.config.mirrorPath.startsWith(dir)) {
1288
- adapter.log.error(`The Mirror directory is not allowed to be a central ioBroker directory!`);
1289
- adapter.log.error(`Directory ${adapter.config.mirrorPath} is not allowed to mirror files!`);
1290
- mirrorForbidden = true;
1291
- break;
1292
- }
1293
- }
1294
- if (!mirrorForbidden) {
1295
- mirror = new Mirror({
1296
- adapter,
1297
- log: adapter.log,
1298
- diskRoot: adapter.config.mirrorPath
1299
- });
1300
- }
1301
- }
1302
- }
1303
-
1304
- // CHeck setState counter per minute and stop script if too high
1305
- setStateCountCheckInterval = setInterval(() => {
1306
- Object.keys(context.scripts).forEach(id => {
1307
- if (!context.scripts[id]) return;
1308
- const currentSetStatePerMinuteCounter = context.scripts[id].setStatePerMinuteCounter;
1309
- context.scripts[id].setStatePerMinuteCounter = 0;
1310
- if (currentSetStatePerMinuteCounter > adapter.config.maxSetStatePerMinute) {
1311
- context.scripts[id].setStatePerMinuteProblemCounter++;
1312
- adapter.log.debug(`Script ${id} has reached the maximum of ${adapter.config.maxSetStatePerMinute} setState calls per minute in ${context.scripts[id].setStatePerMinuteProblemCounter} consecutive minutes`);
1313
- // Allow "too high counters" for 1 minute for script starts or such and only
1314
- // stop the script when lasts longer
1315
- if (context.scripts[id].setStatePerMinuteProblemCounter > 1) {
1316
- adapter.log.error(`Script ${id} is calling setState more than ${adapter.config.maxSetStatePerMinute} times per minute! Stopping Script now! Please check your script!`);
1317
- stop(id);
1318
- }
1319
- } else if (context.scripts[id].setStatePerMinuteProblemCounter > 0) {
1320
- context.scripts[id].setStatePerMinuteProblemCounter--;
1321
- adapter.log.debug(`Script ${id} has NOT reached the maximum of ${adapter.config.maxSetStatePerMinute} setState calls per minute. Decrease problem counter to ${context.scripts[id].setStatePerMinuteProblemCounter}`);
1322
- }
1323
- });
1324
- }, 60000);
1325
-
1326
- });
1327
- });
1328
- });
1329
- }
1330
-
1331
- function stopAllScripts(cb) {
1332
- Object.keys(context.scripts).forEach(id => stop(id));
1333
- setTimeout(() => cb(), 0);
1334
- }
1335
-
1336
- let globalScript = '';
1337
- /** Generated declarations for global TypeScripts */
1338
- let globalDeclarations = '';
1339
- // Remember which definitions the global scripts
1340
- // have access to, because it depends on the compile order
1341
- let knownGlobalDeclarationsByScript = {};
1342
- let globalScriptLines = 0;
1343
- // let activeRegEx = null;
1344
- let activeStr = ''; // enabled state prefix
1345
- let dayScheduleTimer = null; // schedule for astrological day
1346
- let sunScheduleTimer = null; // schedule for sun moment times
1347
- let timeScheduleTimer = null; // schedule for astrological day
1348
-
1349
- function getNextTimeEvent(time, useNextDay) {
1350
- const now = getAstroStartOfDay();
1351
- let [timeHours, timeMinutes] = time.split(':');
1352
- timeHours = parseInt(timeHours, 10);
1353
- timeMinutes = parseInt(timeMinutes, 10);
1354
- if (useNextDay && (now.getHours() > timeHours || (now.getHours() === timeHours && now.getMinutes() > timeMinutes))) {
1355
- now.setDate(now.getDate() + 1);
1356
- }
1357
-
1358
- now.setHours(timeHours);
1359
- now.setMinutes(timeMinutes);
1360
-
1361
- return now;
1362
- }
1363
-
1364
- function getAstroEvent(date, astroEvent, start, end, offsetMinutes, isDayEnd, latitude, longitude, useNextDay) {
1365
- let ts = mods.suncalc.getTimes(date, latitude, longitude)[astroEvent];
1366
- if (!ts || ts.getTime().toString() === 'NaN') {
1367
- ts = isDayEnd ? getNextTimeEvent(end, useNextDay) : getNextTimeEvent(start, useNextDay);
1368
- }
1369
- ts.setMilliseconds(0);
1370
- ts.setMinutes(ts.getMinutes() + (parseInt(offsetMinutes, 10) || 0));
1371
-
1372
- let [timeHoursStart, timeMinutesStart] = start.split(':');
1373
- timeHoursStart = parseInt(timeHoursStart, 10);
1374
- timeMinutesStart = parseInt(timeMinutesStart, 10) || 0;
1375
-
1376
- if (ts.getHours() < timeHoursStart || (ts.getHours() === timeHoursStart && ts.getMinutes() < timeMinutesStart)) {
1377
- ts = getNextTimeEvent(start, useNextDay);
1378
- ts.setSeconds(0);
1379
- }
1380
-
1381
- let [timeHoursEnd, timeMinutesEnd] = end.split(':');
1382
- timeHoursEnd = parseInt(timeHoursEnd, 10);
1383
- timeMinutesEnd = parseInt(timeMinutesEnd, 10) || 0;
1384
-
1385
- if (ts.getHours() > timeHoursEnd || (ts.getHours() === timeHoursEnd && ts.getMinutes() > timeMinutesEnd)) {
1386
- ts = getNextTimeEvent(end, useNextDay);
1387
- ts.setSeconds(0);
1388
- }
1389
-
1390
- // if event in the past
1391
- if (date > ts && useNextDay) {
1392
- // take the next day
1393
- ts.setDate(ts.getDate() + 1);
1394
- }
1395
- return ts;
1396
- }
1397
-
1398
- function timeSchedule(adapter, context) {
1399
- const now = new Date();
1400
- let hours = now.getHours();
1401
- const minutes = now.getMinutes();
1402
- if (context.timeSettings.format12) {
1403
- if (hours > 12) {
1404
- hours -= 12;
1405
- }
1406
- }
1407
- if (context.timeSettings.leadingZeros) {
1408
- hours = hours.toString().padStart(2, '0');
1409
- }
1410
-
1411
- adapter.setState('variables.dayTime', {val: `${hours}:${minutes.toString().padStart(2, '0')}`, ack: true});
1412
-
1413
- now.setMinutes(now.getMinutes() + 1);
1414
- now.setSeconds(0);
1415
- now.setMilliseconds(0);
1416
- const interval = now.getTime() - Date.now();
1417
- timeScheduleTimer = setTimeout(timeSchedule, interval, adapter, context);
1418
- }
1419
-
1420
- function dayTimeSchedules(adapter, context) {
1421
- // get astrological event
1422
- if (adapter.config.latitude === undefined || adapter.config.longitude === undefined ||
1423
- adapter.config.latitude === '' || adapter.config.longitude === '' ||
1424
- adapter.config.latitude === null || adapter.config.longitude === null) {
1425
- adapter.log.error('Longitude or latitude does not set. Cannot use astro.');
1426
- return;
1427
- }
1428
-
1429
- // Calculate next event today
1430
- const todayDate = getAstroStartOfDay();
1431
- const nowDate = new Date();
1432
-
1433
- const todaySunrise = getAstroEvent(todayDate, adapter.config.sunriseEvent, adapter.config.sunriseLimitStart, adapter.config.sunriseLimitEnd, adapter.config.sunriseOffset, false, adapter.config.latitude, adapter.config.longitude);
1434
- const todaySunset = getAstroEvent(todayDate, adapter.config.sunsetEvent, adapter.config.sunsetLimitStart, adapter.config.sunsetLimitEnd, adapter.config.sunsetOffset, true, adapter.config.latitude, adapter.config.longitude);
1435
-
1436
- // Sunrise
1437
- let sunriseTimeout = todaySunrise.getTime() - nowDate.getTime();
1438
- if (sunriseTimeout < 0 || sunriseTimeout > 3600000) {
1439
- sunriseTimeout = 3600000;
1440
- }
1441
-
1442
- // Sunset
1443
- let sunsetTimeout = todaySunset.getTime() - nowDate.getTime();
1444
- if (sunsetTimeout < 0 || sunsetTimeout > 3600000) {
1445
- sunsetTimeout = 3600000;
1446
- }
1447
-
1448
- adapter.getState('variables.isDayTime', (err, state) => {
1449
- let isDay;
1450
- if (sunriseTimeout < 5000) {
1451
- isDay = true;
1452
- } else if (sunsetTimeout < 5000) {
1453
- isDay = false;
1454
- } else {
1455
- // check if in between
1456
- isDay = nowDate.getTime() > (todaySunrise.getTime() - 60000) && nowDate <= todaySunset;
1457
- }
1458
-
1459
- const val = state ? !!state.val : false;
1460
- if (val !== isDay || state === null) {
1461
- adapter.setState('variables.isDayTime', {val: isDay, ack: true});
1462
- }
1463
- });
1464
-
1465
- adapter.getState('variables.isDaylightSaving', (err, state) => {
1466
- const isDayLightSaving = dstOffsetAtDate(nowDate) !== 0;
1467
- const val = state ? !!state.val : false;
1468
-
1469
- if (val !== isDayLightSaving || state === null) {
1470
- adapter.setState('variables.isDaylightSaving', {val: isDayLightSaving, ack: true});
1471
- }
1472
- });
1473
-
1474
- let nextTimeout = sunriseTimeout;
1475
- if (sunriseTimeout > sunsetTimeout) {
1476
- nextTimeout = sunsetTimeout;
1477
- }
1478
- nextTimeout = nextTimeout - 3000;
1479
- if (nextTimeout < 3000) {
1480
- nextTimeout = 3000;
1481
- }
1482
-
1483
- dayScheduleTimer = setTimeout(dayTimeSchedules, nextTimeout, adapter, context);
1484
- }
1485
-
1486
- function getAstroStartOfDay() {
1487
- const d = new Date();
1488
- d.setMinutes(0);
1489
- d.setSeconds(0);
1490
- d.setMilliseconds(0);
1491
- d.setTime(d.getTime() - (d.getTimezoneOffset() * 60 * 1000));
1492
- d.setUTCHours(0);
1493
-
1494
- return d;
1495
- }
1496
-
1497
- function formatHoursMinutesSeconds(date) {
1498
- const h = String(date.getHours());
1499
- const m = String(date.getMinutes());
1500
- const s = String(date.getSeconds());
1501
-
1502
- return `${h.padStart(2, '0')}:${m.padStart(2, '0')}:${s.padStart(2, '0')}`;
1503
- }
1504
-
1505
- async function sunTimeSchedules(adapter, context) {
1506
- if (adapter.config.createAstroStates) {
1507
- if (!isNaN(adapter.config.longitude) && !isNaN(adapter.config.longitude)) {
1508
- const calcDate = getAstroStartOfDay();
1509
-
1510
- const times = mods.suncalc.getTimes(calcDate, adapter.config.latitude, adapter.config.longitude);
1511
-
1512
- adapter.log.debug(`[sunTimeSchedules] Times: ${JSON.stringify(times)}`);
1513
-
1514
- for (const t in times) {
1515
- try {
1516
- const objId = `variables.astro.${t}`;
1517
-
1518
- await adapter.setObjectNotExistsAsync(objId, {
1519
- type: 'state',
1520
- common: {
1521
- name: `Astro ${t}`,
1522
- type: 'string',
1523
- role: 'value',
1524
- read: true,
1525
- write: false,
1526
- },
1527
- native: {},
1528
- });
1529
-
1530
- if (times[t] !== null && !isNaN(times[t].getTime())) {
1531
- const timeFormatted = formatHoursMinutesSeconds(times[t]);
1532
- await adapter.setStateAsync(objId, { val: timeFormatted, c: times[t].toISOString(), ack: true });
1533
- } else {
1534
- await adapter.setStateAsync(objId, { val: null, c: 'n/a', ack: true, q: 0x01 });
1535
- }
1536
- } catch (err) {
1537
- adapter.log.error(`[sunTimeSchedules] Unable to set state for astro time "${t}" (${times[t].getTime()}): ${err}`);
1538
- }
1539
- }
1540
-
1541
- const todayDate = new Date();
1542
- todayDate.setHours(0);
1543
- todayDate.setMinutes(0);
1544
- todayDate.setSeconds(1);
1545
- todayDate.setMilliseconds(0);
1546
- todayDate.setDate(todayDate.getDate() + 1);
1547
-
1548
- adapter.log.debug(`[sunTimeSchedules] Next: ${todayDate.toISOString()}`);
1549
- sunScheduleTimer = setTimeout(sunTimeSchedules, todayDate.getTime() - Date.now(), adapter, context);
1550
- }
1551
- } else {
1552
- // remove astro states if disabled
1553
- adapter.delObject('variables.astro', { recursive: true });
1554
- }
1555
- }
1556
-
1557
- function stopTimeSchedules() {
1558
- dayScheduleTimer && clearTimeout(dayScheduleTimer);
1559
- sunScheduleTimer && clearTimeout(sunScheduleTimer);
1560
- timeScheduleTimer && clearTimeout(timeScheduleTimer);
1561
- }
1562
-
1563
- /**
1564
- * Redirects the virtual-tsc log output to the ioBroker log
1565
- * @param {string} msg message
1566
- * @param {string} sev severity (info, silly, debug, warn, error)
1567
- */
1568
- function tsLog(msg, sev) {
1569
- // shift the severities around, we don't care about the small details
1570
- if (sev == null || sev === 'info') {
1571
- sev = 'debug';
1572
- } else if (sev === 'debug') {
1573
- // Don't spam build logs on Travis
1574
- if (isCI) return;
1575
- sev = 'silly';
1576
- }
1577
-
1578
- if (adapter && adapter.log) {
1579
- adapter.log[sev](msg);
1580
- } else {
1581
- console.log(`[${sev.toUpperCase()}] ${msg}`);
1582
- }
1583
- }
1584
- // Due to a npm bug, virtual-tsc may be hoisted to the top level node_modules but
1585
- // typescript may still be in the adapter level (https://npm.community/t/packages-with-peerdependencies-are-incorrectly-hoisted/4794),
1586
- // so we need to tell virtual-tsc where typescript is
1587
- tsc.setTypeScriptResolveOptions({
1588
- paths: [require.resolve('typescript')],
1589
- });
1590
- // compiler instance for typescript
1591
- /** @type {tsc.Server} */
1592
- const tsServer = new tsc.Server(tsCompilerOptions, tsLog);
1593
-
1594
- // compiler instance for global JS declarations
1595
- /** @type {tsc.Server} */
1596
- const jsDeclarationServer = new tsc.Server(
1597
- jsDeclarationCompilerOptions,
1598
- isCI ? false : undefined
1599
- );
1600
-
1601
- function addGetProperty(object) {
1602
- try {
1603
- Object.defineProperty(object, 'get', {
1604
- value: function (id) {
1605
- return this[id] || this[adapter.namespace + '.' + id];
1606
- },
1607
- enumerable: false
1608
- });
1609
- } catch (e) {
1610
- console.error('Cannot install get property');
1611
- }
1612
- }
1613
-
1614
- function fixLineNo(line) {
1615
- if (line.includes('javascript.js:')) {
1616
- return line;
1617
- }
1618
- if (!/script[s]?\.js[.\\/]/.test(line)) {
1619
- return line;
1620
- }
1621
- if (/:([\d]+):/.test(line)) {
1622
- line = line.replace(/:([\d]+):/, ($0, $1) =>
1623
- ':' + ($1 > globalScriptLines + 1 ? $1 - globalScriptLines - 1 : $1) + ':'); // one line for 'async function ()'
1624
- } else {
1625
- line = line.replace(/:([\d]+)$/, ($0, $1) =>
1626
- ':' + ($1 > globalScriptLines + 1 ? $1 - globalScriptLines - 1 : $1)); // one line for 'async function ()'
1627
- }
1628
- return line;
1629
- }
1630
-
1631
- context.logError = function (msg, e, offs) {
1632
- const stack = e.stack ? e.stack.toString().split('\n') : (e ? e.toString() : '');
1633
- if (!msg.includes('\n')) {
1634
- msg = msg.replace(/[: ]*$/, ': ');
1635
- }
1636
-
1637
- //errorLogFunction.error(msg + stack[0]);
1638
- context.errorLogFunction.error(msg + fixLineNo(stack[0]));
1639
- for (let i = offs || 1; i < stack.length; i++) {
1640
- if (!stack[i]) {
1641
- continue;
1642
- }
1643
- if (stack[i].match(/runInNewContext|javascript\.js:/)) {
1644
- break;
1645
- }
1646
- //adapter.log.error(fixLineNo(stack[i]));
1647
- context.errorLogFunction.error(fixLineNo(stack[i]));
1648
- }
1649
- };
1650
-
1651
- function createActiveObject(id, enabled, cb) {
1652
- const idActive = `${adapter.namespace}.scriptEnabled.${id.substring(SCRIPT_CODE_MARKER.length)}`;
1653
-
1654
- if (!context.objects[idActive]) {
1655
- context.objects[idActive] = {
1656
- _id: idActive,
1657
- common: {
1658
- name: `scriptEnabled.${id.substring(SCRIPT_CODE_MARKER.length)}`,
1659
- desc: 'controls script activity',
1660
- type: 'boolean',
1661
- write: true,
1662
- read: true,
1663
- role: 'switch.active'
1664
- },
1665
- native: {
1666
- script: id
1667
- },
1668
- type: 'state'
1669
- };
1670
- adapter.setForeignObject(idActive, context.objects[idActive], err => {
1671
- if (!err) {
1672
- const intermediateStateValue = prepareStateObject(idActive, !!enabled, true);
1673
- adapter.setForeignState(idActive, !!enabled, true, () => {
1674
- if (enabled && !adapter.config.subscribe) {
1675
- context.interimStateValues[idActive] = intermediateStateValue;
1676
- }
1677
- cb && cb();
1678
- });
1679
- } else if (cb) {
1680
- cb();
1681
- }
1682
- });
1683
- } else {
1684
- adapter.getForeignState(idActive, (err, state) => {
1685
- if (state && state.val !== enabled) {
1686
- const intermediateStateValue = prepareStateObject(idActive, !!enabled, true);
1687
- adapter.setForeignState(idActive, !!enabled, true, () => {
1688
- if (enabled && !adapter.config.subscribe) {
1689
- context.interimStateValues[id] = intermediateStateValue;
1690
- }
1691
- cb && cb();
1692
- });
1693
- } else if (cb) {
1694
- cb();
1695
- }
1696
- });
1697
- }
1698
- }
1699
-
1700
- function createProblemObject(id, cb) {
1701
- const idProblem = adapter.namespace + '.scriptProblem.' + id.substring(SCRIPT_CODE_MARKER.length);
1702
-
1703
- if (!context.objects[idProblem]) {
1704
- context.objects[idProblem] = {
1705
- _id: idProblem,
1706
- common: {
1707
- name: 'scriptProblem.' + id.substring(SCRIPT_CODE_MARKER.length),
1708
- desc: 'Script has a problem',
1709
- type: 'boolean',
1710
- expert: true,
1711
- write: false,
1712
- read: true,
1713
- role: 'indicator.error'
1714
- },
1715
- native: {
1716
- script: id
1717
- },
1718
- type: 'state'
1719
- };
1720
- adapter.setForeignObject(idProblem, context.objects[idProblem], err => {
1721
- if (!err) {
1722
- adapter.setForeignState(idProblem, false, true, cb);
1723
- } else if (cb) {
1724
- cb();
1725
- }
1726
- });
1727
- } else {
1728
- adapter.getForeignState(idProblem, (err, state) => {
1729
- if (state && state.val !== false) {
1730
- adapter.setForeignState(idProblem, false, true, cb);
1731
- } else if (cb) {
1732
- cb();
1733
- }
1734
- });
1735
- }
1736
- }
1737
-
1738
- function addToNames(obj) {
1739
- const id = obj._id;
1740
-
1741
- if (obj.common && obj.common.name) {
1742
- let name = obj.common.name;
1743
- if (name && typeof name === 'object') {
1744
- name = name[words.getLanguage()] || name.en;
1745
- }
1746
- if (!name || typeof name !== 'string') { // TODO, take name in current language
1747
- return;
1748
- }
1749
-
1750
- if (!context.names[name]) {
1751
- context.names[name] = id;
1752
- } else {
1753
- // convert to array
1754
- if (!Array.isArray(context.names[name])) {
1755
- context.names[name] = [context.names[name]];
1756
- }
1757
-
1758
- context.names[name].push(id);
1759
- }
1760
- }
1761
- }
1762
-
1763
- function removeFromNames(id) {
1764
- const n = getName(id);
1765
-
1766
- if (n) {
1767
- if (Array.isArray(context.names[n])) {
1768
- const pos = context.names[n].indexOf(id);
1769
- if (pos > -1) {
1770
- context.names[n].splice(pos, 1);
1771
-
1772
- if (context.names[n].length === 1) {
1773
- context.names[n] = context.names[n][0];
1774
- }
1775
- }
1776
- } else {
1777
- delete context.names[n];
1778
- }
1779
- }
1780
- }
1781
-
1782
- function getName(id) {
1783
- for (const n in context.names) {
1784
- if (context.names[n] && Array.isArray(context.names[n])) {
1785
- if (context.names[n].includes(id)) {
1786
- return n;
1787
- }
1788
- } else if (context.names[n] === id) {
1789
- return n;
1790
- }
1791
- }
1792
-
1793
- return null;
1794
- }
1795
-
1796
- async function installNpm(npmLib) {
1797
- return new Promise((resolve, reject) => {
1798
- const path = __dirname;
1799
-
1800
- // Also, set the working directory (cwd) of the process instead of using --prefix
1801
- // because that has ugly bugs on Windows
1802
- const cmd = `npm install ${npmLib} --omit=dev`;
1803
- adapter.log.info(`Installing ${npmLib} into ${__dirname} - cmd: ${cmd}`);
1804
-
1805
- // System call used for update of js-controller itself,
1806
- // because during installation npm packet will be deleted too, but some files must be loaded even during the installation process.
1807
- const child = mods['child_process'].exec(cmd, {
1808
- windowsHide: true,
1809
- cwd: path,
1810
- });
1811
-
1812
- child.stdout && child.stdout.on('data', buf =>
1813
- adapter.log.info(buf.toString('utf8')));
1814
-
1815
- child.stderr && child.stderr.on('data', buf =>
1816
- adapter.log.error(buf.toString('utf8')));
1817
-
1818
- child.on('err', err => {
1819
- adapter.log.error(`Cannot install ${npmLib}: ${err}`);
1820
- reject(npmLib);
1821
- });
1822
- child.on('error', err => {
1823
- adapter.log.error(`Cannot install ${npmLib}: ${err}`);
1824
- reject(npmLib);
1825
- });
1826
-
1827
- child.on('exit', (code /* , signal */) => {
1828
- if (code) {
1829
- adapter.log.error(`Cannot install ${npmLib}: ${code}`);
1830
- reject(code);
1831
- }
1832
- // command succeeded
1833
- resolve(code);
1834
- });
1835
- });
1836
- }
1837
-
1838
- async function installLibraries() {
1839
- if (typeof adapter.config?.libraries !== 'string') {
1840
- adapter.config.libraries = '';
1841
- }
1842
-
1843
- const libraries = adapter.config.libraries.split(/[,;\s]+/).filter(d => d.length > 0);
1844
-
1845
- adapter.log.debug(`Custom libraries in config: "${adapter.config.libraries}": ${JSON.stringify(libraries)}`);
1846
-
1847
- let installedNodeModules = [];
1848
- const keepModules = [];
1849
-
1850
- // js-controller >= 6.x
1851
- if (typeof adapter.listInstalledNodeModules === 'function') {
1852
- installedNodeModules = await adapter.listInstalledNodeModules();
1853
-
1854
- adapter.log.debug(`Found installed libraries: ${JSON.stringify(installedNodeModules)}`);
1855
- }
1856
-
1857
- for (const lib of libraries) {
1858
- let depName = lib;
1859
- let version = 'latest';
1860
-
1861
- if (depName.includes('@') && depName.lastIndexOf('@') > 0) {
1862
- const parts = depName.split('@');
1863
- version = parts.pop() ?? 'latest';
1864
- depName = parts.join('@');
1865
- }
1866
-
1867
- /** The real module name, because the dependency can be an url too */
1868
- let moduleName = depName;
1869
-
1870
- if (URL.canParse(depName)) {
1871
- moduleName = await requestModuleNameByUrl(depName);
1872
-
1873
- adapter.log.debug(`Found custom library in config: "${moduleName}@${version}" (from ${depName})`);
1874
- } else {
1875
- adapter.log.debug(`Found custom library in config: "${moduleName}@${version}"`);
1876
- }
1877
-
1878
- keepModules.push(moduleName);
1879
-
1880
- // js-controller >= 6.x
1881
- if (typeof adapter.installNodeModule === 'function') {
1882
- try {
1883
- const result = await adapter.installNodeModule(depName, { version });
1884
- if (result.success) {
1885
- adapter.log.debug(`Installed custom library: "${moduleName}@${version}"`);
1886
-
1887
- const importedModule = await adapter.importNodeModule(moduleName);
1888
- context.mods[moduleName] = importedModule.default ?? importedModule;
1889
- } else {
1890
- adapter.log.warn(`Cannot install custom npm package "${moduleName}@${version}"`);
1891
- }
1892
- } catch (e) {
1893
- adapter.log.warn(`Cannot install custom npm package "${moduleName}@${version}": ${e.message}`);
1894
- }
1895
- } else if (!nodeFS.existsSync(`${__dirname}/node_modules/${depName}/package.json`)) {
1896
- // js-controller < 6.x
1897
- adapter.log.info(`Installing custom library (legacy mode): "${lib}"`);
1898
-
1899
- try {
1900
- await installNpm(lib);
1901
- adapter.log.info(`Installed custom npm package (legacy mode): "${libraries[lib]}"`);
1902
- } catch (err) {
1903
- adapter.log.warn(`Cannot install custom npm package "${libraries[lib]}" (legacy mode): ${err}`);
1904
- }
1905
- }
1906
- }
1907
-
1908
- // js-controller >= 6.x
1909
- if (typeof adapter.uninstallNodeModule === 'function') {
1910
- for (const installedNodeModule of installedNodeModules) {
1911
- if (!keepModules.includes(installedNodeModule)) {
1912
- try {
1913
- await adapter.uninstallNodeModule(installedNodeModule);
1914
-
1915
- adapter.log.info(`Removed custom npm package: "${installedNodeModule}"`);
1916
- } catch (err) {
1917
- adapter.log.warn(`Cannot remove custom npm package ${installedNodeModule}: ${err}`);
1918
- }
1919
- }
1920
- }
1921
- }
1922
- }
1923
-
1924
- function createVM(source, name, wrapAsync) {
1925
- if (debugMode && name !== debugMode) {
1926
- return false;
1927
- }
1928
-
1929
- if (!debugMode) {
1930
- const logSubscriptionsText = "\n;\nlog(`registered ${__engine.__subscriptions} subscription${__engine.__subscriptions === 1 ? '' : 's'}," +
1931
- " ${__engine.__schedules} schedule${__engine.__schedules === 1 ? '' : 's'}," +
1932
- " ${__engine.__subscriptionsMessage} message${__engine.__subscriptionsMessage === 1 ? '' : 's'}," +
1933
- " ${__engine.__subscriptionsLog} log${__engine.__subscriptionsLog === 1 ? '' : 's'}" +
1934
- " and ${__engine.__subscriptionsFile} file subscription${__engine.__subscriptionsFile === 1 ? '' : 's'}`);\n";
1935
-
1936
- if (wrapAsync) {
1937
- source = `(async () => {\n${source}\n${logSubscriptionsText}\n})();`;
1938
- } else {
1939
- source = `${source}\n${logSubscriptionsText}`;
1940
- }
1941
- } else {
1942
- if (wrapAsync) {
1943
- source = `(async () => {debugger;\n${source}\n})();`;
1944
- } else {
1945
- source = `debugger;${source}`;
1946
- }
1947
- }
1948
-
1949
- try {
1950
- const options = {
1951
- filename: name,
1952
- displayErrors: true
1953
- // lineOffset: globalScriptLines
1954
- };
1955
- return {
1956
- script: new vm.Script(source, options),
1957
- };
1958
- } catch (e) {
1959
- context.logError(`${name} compile failed:\r\nat `, e);
1960
- return false;
1961
- }
1962
- }
1963
-
1964
- function execute(script, name, engineType, verbose, debug) {
1965
- script.intervals = [];
1966
- script.timeouts = [];
1967
- script.schedules = [];
1968
- script.wizards = [];
1969
- script.name = name;
1970
- script.engineType = engineType;
1971
- script._id = Math.floor(Math.random() * 0xFFFFFFFF);
1972
- script.subscribes = {};
1973
- script.subscribesFile = {};
1974
- script.setStatePerMinuteCounter = 0;
1975
- script.setStatePerMinuteProblemCounter = 0;
1976
- adapter.setState(`scriptProblem.${name.substring(SCRIPT_CODE_MARKER.length)}`, { val: false, ack: true, expire: 1000 });
1977
-
1978
- const sandbox = sandBox(script, name, verbose, debug, context);
1979
-
1980
- try {
1981
- script.script.runInNewContext(sandbox, {
1982
- filename: name,
1983
- displayErrors: true
1984
- // lineOffset: globalScriptLines
1985
- });
1986
- } catch (e) {
1987
- adapter.setState(`scriptProblem.${name.substring(SCRIPT_CODE_MARKER.length)}`, { val: true, ack: true, c: 'execute' });
1988
- context.logError(name, e);
1989
- }
1990
- }
1991
-
1992
- function unsubscribe(id) {
1993
- if (!id) {
1994
- adapter.log.warn('unsubscribe: empty name');
1995
- return;
1996
- }
1997
-
1998
- if (id.constructor && id.constructor.name === 'RegExp') {
1999
- //adapter.log.warn('unsubscribe: todo - process regexp');
2000
- return;
2001
- }
2002
-
2003
- if (typeof id !== 'string') {
2004
- adapter.log.error(`unsubscribe: invalid type of id - ${typeof id}`);
2005
- return;
2006
- }
2007
- const parts = id.split('.');
2008
- const _adapter = `system.adapter.${parts[0]}.${parts[1]}`;
2009
- if (context.objects[_adapter] && context.objects[_adapter].common && context.objects[_adapter].common.subscribable) {
2010
- const a = parts[0] + '.' + parts[1];
2011
- const alive = `system.adapter.${a}.alive`;
2012
- if (context.adapterSubs[alive]) {
2013
- const pos = context.adapterSubs[alive].indexOf(id);
2014
- pos !== -1 && context.adapterSubs[alive].splice(pos, 1);
2015
- if (!context.adapterSubs[alive].length) {
2016
- delete context.adapterSubs[alive];
2017
- }
2018
- }
2019
- adapter.sendTo(a, 'unsubscribe', id);
2020
- }
2021
- }
2022
-
2023
- // Analyse if logs are still required or not
2024
- function updateLogSubscriptions() {
2025
- let found = false;
2026
- // go through all scripts and check if some one script still require logs
2027
- Object.keys(context.logSubscriptions).forEach(scriptName => {
2028
- if (!context.logSubscriptions?.[scriptName] || !context.logSubscriptions[scriptName].length) {
2029
- delete context.logSubscriptions[scriptName];
2030
- } else {
2031
- found = true;
2032
- }
2033
- });
2034
-
2035
- if (found && !logSubscribed) {
2036
- logSubscribed = true;
2037
- adapter.requireLog(logSubscribed);
2038
- adapter.log.info(`Subscribed to log messages (found logSubscriptions)`);
2039
- } else if (!found && logSubscribed) {
2040
- logSubscribed = false;
2041
- adapter.requireLog(logSubscribed);
2042
- adapter.log.info(`Unsubscribed from log messages (not found logSubscriptions)`);
2043
- }
2044
- }
2045
-
2046
- function stop(name, callback) {
2047
- adapter.log.info(`Stopping script ${name}`);
2048
-
2049
- adapter.setState('scriptEnabled.' + name.substring(SCRIPT_CODE_MARKER.length), false, true);
2050
-
2051
- if (context.messageBusHandlers[name]) {
2052
- delete context.messageBusHandlers[name];
2053
- }
2054
-
2055
- if (context.tempDirectories[name]) {
2056
- try {
2057
- mods.fs.rmSync(context.tempDirectories[name], { recursive: true });
2058
-
2059
- adapter.log.debug(`Removed temp directory of ${name}: ${context.tempDirectories[name]}`);
2060
- } catch (err) {
2061
- adapter.log.warn(`Unable to remove temp directory of ${name}: ${context.tempDirectories[name]}`);
2062
- }
2063
-
2064
- delete context.tempDirectories[name];
2065
- }
2066
-
2067
- if (context.logSubscriptions[name]) {
2068
- delete context.logSubscriptions[name];
2069
- updateLogSubscriptions();
2070
- }
2071
-
2072
- if (context.scripts[name]) {
2073
- // Remove from subscriptions
2074
- context.isEnums = false;
2075
- if (adapter.config.subscribe) {
2076
- // check all subscribed IDs
2077
- Object.keys(context.scripts[name].subscribes).forEach(id => {
2078
- if (context.subscribedPatterns[id]) {
2079
- context.subscribedPatterns[id] -= context.scripts[name].subscribes[id];
2080
- if (context.subscribedPatterns[id] <= 0) {
2081
- adapter.unsubscribeForeignStates(id);
2082
- delete context.subscribedPatterns[id];
2083
- if (context.states[id]) {
2084
- delete context.states[id];
2085
- }
2086
- }
2087
- }
2088
- });
2089
- }
2090
-
2091
- for (let i = context.subscriptions.length - 1; i >= 0; i--) {
2092
- if (context.subscriptions[i].name === name) {
2093
- const sub = context.subscriptions.splice(i, 1)[0];
2094
- sub && unsubscribe(sub.pattern.id);
2095
- } else {
2096
- if (!context.isEnums && context.subscriptions[i].pattern.enumName || context.subscriptions[i].pattern.enumId) {
2097
- context.isEnums = true;
2098
- }
2099
- }
2100
- }
2101
-
2102
- // check all subscribed files
2103
- Object.keys(context.scripts[name].subscribesFile).forEach(key => {
2104
- if (context.subscribedPatternsFile[key]) {
2105
- context.subscribedPatternsFile[key] -= context.scripts[name].subscribesFile[key];
2106
- if (context.subscribedPatternsFile[key] <= 0) {
2107
- const [id, file] = key.split('$%$');
2108
- adapter.unsubscribeForeignFiles(id, file);
2109
- delete context.subscribedPatternsFile[key];
2110
- }
2111
- }
2112
- });
2113
- for (let i = context.subscriptionsFile.length - 1; i >= 0; i--) {
2114
- if (context.subscriptionsFile[i].name === name) {
2115
- context.subscriptionsFile.splice(i, 1)[0];
2116
- }
2117
- }
2118
-
2119
- for (let i = context.subscriptionsObject.length - 1; i >= 0; i--) {
2120
- if (context.subscriptionsObject[i].name === name) {
2121
- const sub = context.subscriptionsObject.splice(i, 1)[0];
2122
- sub && adapter.unsubscribeForeignObjects(sub.pattern);
2123
- }
2124
- }
2125
-
2126
- // Stop all timeouts
2127
- for (let i = 0; i < context.scripts[name].timeouts.length; i++) {
2128
- clearTimeout(context.scripts[name].timeouts[i]);
2129
- }
2130
- // Stop all intervals
2131
- for (let i = 0; i < context.scripts[name].intervals.length; i++) {
2132
- clearInterval(context.scripts[name].intervals[i]);
2133
- }
2134
- // Stop all scheduled jobs
2135
- for (let i = 0; i < context.scripts[name].schedules.length; i++) {
2136
- if (context.scripts[name].schedules[i]) {
2137
- const _name = context.scripts[name].schedules[i].name;
2138
- if (!mods.nodeSchedule.cancelJob(context.scripts[name].schedules[i])) {
2139
- adapter.log.error(`Error by canceling scheduled job "${_name}"`);
2140
- }
2141
- }
2142
- }
2143
-
2144
- // Stop all time wizards jobs
2145
- for (let i = 0; i < context.scripts[name].wizards.length; i++) {
2146
- if (context.scripts[name].wizards[i]) {
2147
- context.scheduler && context.scheduler.remove(context.scripts[name].wizards[i]);
2148
- }
2149
- }
2150
-
2151
- // if callback for on stop
2152
- if (typeof context.scripts[name].onStopCb === 'function') {
2153
- context.scripts[name].onStopTimeout = parseInt(context.scripts[name].onStopTimeout, 10) || 1000;
2154
-
2155
- let timeout = setTimeout(() => {
2156
- if (timeout) {
2157
- timeout = null;
2158
- delete context.scripts[name];
2159
- typeof callback === 'function' && callback(true, name);
2160
- }
2161
- }, context.scripts[name].onStopTimeout);
2162
-
2163
- try {
2164
- context.scripts[name].onStopCb(() => {
2165
- if (timeout) {
2166
- clearTimeout(timeout);
2167
- timeout = null;
2168
- delete context.scripts[name];
2169
- typeof callback === 'function' && callback(true, name);
2170
- }
2171
- });
2172
- } catch (e) {
2173
- adapter.log.error('error in onStop callback: ' + e);
2174
- }
2175
-
2176
- } else {
2177
- delete context.scripts[name];
2178
- typeof callback === 'function' && callback(true, name);
2179
- }
2180
- } else {
2181
- typeof callback === 'function' && callback(false, name);
2182
- }
2183
- }
2184
-
2185
- function prepareScript(obj, callback) {
2186
- if (obj && obj.common && obj.common.enabled && debugState.scriptName === obj._id) {
2187
- const id = obj._id;
2188
- return debugStop()
2189
- .then(() => {
2190
- adapter.log.info(`Debugging of ${id} was stopped, because started in normal mode`);
2191
- prepareScript(obj, callback);
2192
- });
2193
- }
2194
-
2195
- if (obj && obj.common &&
2196
- (obj.common.enabled || debugMode === obj._id) &&
2197
- obj.common.engine === `system.adapter.${adapter.namespace}` &&
2198
- obj.common.source
2199
- ) {
2200
- const name = obj._id;
2201
-
2202
- const nameId = name.substring(SCRIPT_CODE_MARKER.length);
2203
- if (!nameId.length || nameId.endsWith('.')) {
2204
- adapter.log.error(`Script name ${name} is invalid!`);
2205
- typeof callback === 'function' && callback(false, name);
2206
- return;
2207
- }
2208
- const idActive = `scriptEnabled.${nameId}`;
2209
- if (!adapter.config.subscribe) {
2210
- context.interimStateValues[idActive] = prepareStateObject(`${adapter.namespace}.${idActive}`, true, true);
2211
- }
2212
- adapter.setState(idActive, true, true);
2213
- obj.common.engineType = obj.common.engineType || '';
2214
-
2215
- if ((obj.common.engineType.toLowerCase().startsWith('javascript') || obj.common.engineType === 'Blockly' || obj.common.engineType === 'Rules')) {
2216
- // Javascript
2217
- adapter.log.info(`Start JavaScript ${name} (${obj.common.engineType})`);
2218
-
2219
- let sourceFn = name;
2220
- if (webstormDebug) {
2221
- const fn = name.replace(/^script.js./, '').replace(/\./g, '/');
2222
- sourceFn = mods.path.join(webstormDebug, `${fn}.js`);
2223
- }
2224
- context.scripts[name] = createVM(globalScript + '\n' + obj.common.source, sourceFn, true);
2225
- if (!context.scripts[name]) {
2226
- delete context.scripts[name];
2227
- typeof callback === 'function' && callback(false, name);
2228
- return;
2229
- }
2230
- execute(context.scripts[name], sourceFn, obj.common.engineType, obj.common.verbose, obj.common.debug);
2231
- typeof callback === 'function' && callback(true, name);
2232
- } else if (obj.common.engineType.toLowerCase().startsWith('typescript')) {
2233
- // TypeScript
2234
- adapter.log.info(`Compiling TypeScript source ${name}`);
2235
- // The source code must be transformed in order to support top level await
2236
- // and to force TypeScript to compile the code as a module
2237
- const transformedSource = transformScriptBeforeCompilation(obj.common.source, false);
2238
- // We need to hash both global declarations that are known until now
2239
- // AND the script source, because changing either can change the compilation output
2240
- const sourceHash = hashSource(tsSourceHashBase + globalDeclarations + transformedSource);
2241
-
2242
- let compiled;
2243
- // If we already stored the compiled source code and the original source hash,
2244
- // use the hash to check whether we can rely on the compiled source code or
2245
- // if we need to compile it again
2246
- if (
2247
- typeof obj.common.compiled === 'string'
2248
- && typeof obj.common.sourceHash === 'string'
2249
- && sourceHash === obj.common.sourceHash
2250
- ) {
2251
- // We can reuse the stored source
2252
- compiled = obj.common.compiled;
2253
- adapter.log.info(`${name}: source code did not change, using cached compilation result...`);
2254
- } else {
2255
- // We don't have a hashed source code, or the original source changed, compile it
2256
- const filename = scriptIdToTSFilename(name);
2257
- let tsCompiled;
2258
- try {
2259
- tsCompiled = tsServer.compile(filename, transformedSource);
2260
- } catch (e) {
2261
- adapter.log.error(`${obj._id}: TypeScript compilation failed:\n${e}`);
2262
- typeof callback === 'function' && callback(false, name);
2263
- return;
2264
- }
2265
-
2266
- const errors = tsCompiled.diagnostics.map(diag => diag.annotatedSource + '\n').join('\n');
2267
-
2268
- if (tsCompiled.success) {
2269
- if (errors.length > 0) {
2270
- adapter.log.warn(`${name}: TypeScript compilation had errors:\n${errors}`);
2271
- } else {
2272
- adapter.log.info(`${name}: TypeScript compilation successful`);
2273
- }
2274
- compiled = tsCompiled.result;
2275
-
2276
- // Store the compiled source and the original source hash, so we don't need to do the work again next time
2277
- ignoreObjectChange.add(name); // ignore the next change and don't restart scripts
2278
- adapter.extendForeignObject(name, {
2279
- common: {
2280
- sourceHash,
2281
- compiled,
2282
- }
2283
- });
2284
- } else {
2285
- adapter.log.error(`${name}: TypeScript compilation failed:\n${errors}`);
2286
- typeof callback === 'function' && callback(false, name);
2287
- return;
2288
- }
2289
- }
2290
- context.scripts[name] = createVM(globalScript + '\n' + compiled, name, false);
2291
- if (!context.scripts[name]) {
2292
- delete context.scripts[name];
2293
- typeof callback === 'function' && callback(false, name);
2294
- return;
2295
- }
2296
- execute(context.scripts[name], name, obj.common.engineType, obj.common.verbose, obj.common.debug);
2297
- typeof callback === 'function' && callback(true, name);
2298
- }
2299
- } else {
2300
- let _name;
2301
- if (obj && obj._id) {
2302
- _name = obj._id;
2303
- const scriptIdName = _name.substring(SCRIPT_CODE_MARKER.length);
2304
-
2305
- if (!scriptIdName.length || scriptIdName.endsWith('.')) {
2306
- adapter.log.error(`Script name ${_name} is invalid!`);
2307
- typeof callback === 'function' && callback(false, _name);
2308
- return;
2309
- }
2310
- adapter.setState(`scriptEnabled.${scriptIdName}`, false, true);
2311
- }
2312
- !obj && adapter.log.error('Invalid script');
2313
- typeof callback === 'function' && callback(false, _name);
2314
- }
2315
- }
2316
-
2317
- function load(nameOrObject, callback) {
2318
- if (typeof nameOrObject === 'object') {
2319
- // create states for scripts
2320
- createActiveObject(nameOrObject._id, nameOrObject && nameOrObject.common && nameOrObject.common.enabled, () =>
2321
- createProblemObject(nameOrObject._id, () =>
2322
- prepareScript(nameOrObject, callback)));
2323
-
2324
- } else {
2325
- adapter.getForeignObject(nameOrObject, (err, obj) => {
2326
- if (!obj || err) {
2327
- err && adapter.log.error(`Invalid script "${nameOrObject}": ${err}`);
2328
- typeof callback === 'function' && callback(false, nameOrObject);
2329
- } else {
2330
- return load(obj, callback);
2331
- }
2332
- });
2333
- }
2334
- }
2335
-
2336
- function patternMatching(event, patternFunctions) {
2337
- let matched = false;
2338
- for (let i = 0, len = patternFunctions.length; i < len; i++) {
2339
- if (patternFunctions[i](event)) {
2340
- if (patternFunctions.logic === 'or') {
2341
- return true;
2342
- }
2343
- matched = true;
2344
- } else if (patternFunctions.logic === 'and') {
2345
- return false;
2346
- }
2347
- }
2348
- return matched;
2349
- }
2350
-
2351
- async function getData(callback) {
2352
- await adapter.subscribeForeignObjectsAsync('*');
2353
-
2354
- if (!adapter.config.subscribe) {
2355
- await adapter.subscribeForeignStatesAsync('*');
2356
- } else {
2357
- await adapter.subscribeStatesAsync('debug.to');
2358
- await adapter.subscribeStatesAsync('scriptEnabled.*');
2359
- }
2360
-
2361
- adapter.log.info('requesting all states');
2362
- adapter.getForeignStates('*', (err, res) => {
2363
- if (!adapter.config.subscribe) {
2364
- if (err || !res) {
2365
- adapter.log.error(`Could not initialize states: ${err ? err.message : 'no result'}`);
2366
- adapter.terminate(utils.EXIT_CODES.START_IMMEDIATELY_AFTER_STOP);
2367
- return;
2368
- }
2369
- context.states = Object.assign(res, context.states);
2370
-
2371
- addGetProperty(context.states);
2372
- }
2373
-
2374
- // remember all IDs
2375
- for (const id in res) {
2376
- if (Object.prototype.hasOwnProperty.call(res, id)) {
2377
- context.stateIds.push(id);
2378
- }
2379
- }
2380
- statesInitDone = true;
2381
- adapter.log.info('received all states');
2382
- objectsInitDone && typeof callback === 'function' && callback();
2383
- });
2384
-
2385
- adapter.log.info('requesting all objects');
2386
-
2387
- adapter.getObjectList({ include_docs: true }, (err, res) => {
2388
- if (err || !res || !res.rows) {
2389
- adapter.log.error(`Could not initialize objects: ${err ? err.message : 'no result'}`);
2390
- adapter.terminate(utils.EXIT_CODES.START_IMMEDIATELY_AFTER_STOP);
2391
- return;
2392
- }
2393
- context.objects = {};
2394
- for (let i = 0; i < res.rows.length; i++) {
2395
- if (!res.rows[i].doc) {
2396
- adapter.log.debug(`Got empty object for index ${i} (${res.rows[i].id})`);
2397
- continue;
2398
- }
2399
- if (context.objects[res.rows[i].doc._id] === undefined) { // If was already there ignore
2400
- context.objects[res.rows[i].doc._id] = res.rows[i].doc;
2401
- }
2402
- context.objects[res.rows[i].doc._id].type === 'enum' && context.enums.push(res.rows[i].doc._id);
2403
-
2404
- // Collect all names
2405
- addToNames(context.objects[res.rows[i].doc._id]);
2406
- }
2407
- addGetProperty(context.objects);
2408
-
2409
- const systemConfig = context.objects['system.config'];
2410
-
2411
- // set language for debug messages
2412
- if (systemConfig && systemConfig.common && systemConfig.common.language) {
2413
- words.setLanguage(systemConfig.common.language);
2414
- } else if (adapter.language) {
2415
- words.setLanguage(adapter.language);
2416
- }
2417
-
2418
- // try to use system coordinates
2419
- if (adapter.config.useSystemGPS) {
2420
- if (systemConfig && systemConfig.common && systemConfig.common.latitude) {
2421
- adapter.config.latitude = systemConfig.common.latitude;
2422
- adapter.config.longitude = systemConfig.common.longitude;
2423
- } else if (adapter.latitude) {
2424
- adapter.config.latitude = adapter.latitude;
2425
- adapter.config.longitude = adapter.longitude;
2426
- }
2427
- }
2428
- adapter.config.latitude = parseFloat(adapter.config.latitude);
2429
- adapter.config.longitude = parseFloat(adapter.config.longitude);
2430
-
2431
- if (isNaN(adapter.config.latitude)) {
2432
- adapter.log.warn(`Configured latitude is not a number - check (instance/system) configuration`);
2433
- } else if (adapter.config.latitude < -90 || adapter.config.latitude > 90) {
2434
- adapter.log.warn(`Configured latitude "${adapter.config.latitude}" is invalid - check (instance/system) configuration`);
2435
- }
2436
-
2437
- if (isNaN(adapter.config.longitude)) {
2438
- adapter.log.warn(`Configured longitude is not a number - check (instance/system) configuration`);
2439
- } else if (adapter.config.longitude < -180 || adapter.config.longitude > 180) {
2440
- adapter.log.warn(`Configured longitude "${adapter.config.longitude}" is invalid - check (instance/system) configuration`);
2441
- }
2442
-
2443
- adapter.config.sunriseEvent = adapter.config.sunriseEvent || 'nightEnd';
2444
- adapter.config.sunriseOffset = adapter.config.sunriseOffset || 0;
2445
- adapter.config.sunriseLimitStart = adapter.config.sunriseLimitStart || '06:00';
2446
- adapter.config.sunriseLimitEnd = adapter.config.sunriseLimitEnd || '12:00';
2447
-
2448
- adapter.config.sunsetEvent = adapter.config.sunsetEvent || 'dusk';
2449
- adapter.config.sunsetOffset = adapter.config.sunsetOffset || 0;
2450
- adapter.config.sunsetLimitStart = adapter.config.sunsetLimitStart || '18:00';
2451
- adapter.config.sunsetLimitEnd = adapter.config.sunsetLimitEnd || '23:00';
2452
-
2453
- objectsInitDone = true;
2454
- adapter.log.info('received all objects');
2455
- statesInitDone && typeof callback === 'function' && callback();
2456
- });
2457
- }
2458
-
2459
- const debugState = {
2460
- scriptName: '',
2461
- child: null,
2462
- promiseOnEnd: null,
2463
- paused: false,
2464
- started: 0,
2465
- };
2466
-
2467
- function debugStop() {
2468
- if (debugState.child) {
2469
- debugSendToInspector({cmd: 'end'});
2470
- debugState.endTimeout = setTimeout(() => {
2471
- debugState.endTimeout = null;
2472
- debugState.child && debugState.child.kill('SIGTERM');
2473
- }, 500);
2474
- } else {
2475
- debugState.promiseOnEnd = Promise.resolve();
2476
- }
2477
-
2478
- return debugState.promiseOnEnd
2479
- .then(() => {
2480
- debugState.child = null;
2481
- debugState.running = false;
2482
- debugState.scriptName = '';
2483
- debugState.endTimeout && clearTimeout(debugState.endTimeout);
2484
- debugState.endTimeout = null;
2485
- });
2486
- }
2487
-
2488
- function debugDisableScript(id) {
2489
- const obj = context.objects[id];
2490
- if (obj && obj.common && obj.common.enabled) {
2491
- return adapter.extendForeignObjectAsync(obj._id, {common: {enabled: false}});
2492
- } else {
2493
- return Promise.resolve();
2494
- }
2495
- }
2496
-
2497
- function debugSendToInspector(message) {
2498
- if (debugState.child) {
2499
- try {
2500
- adapter.log.info(`send to debugger: ${message}`);
2501
- debugState.child.send(message);
2502
- } catch (e) {
2503
- debugStop()
2504
- .then(() => adapter.log.info(`Debugging of "${debugState.scriptName}" was stopped, because started in normal mode`));
2505
- }
2506
- } else {
2507
- adapter.log.error(`Cannot send command to terminated inspector`);
2508
- return adapter.setState('debug.from', JSON.stringify({cmd: 'error', error: `Cannot send command to terminated inspector`, id: 1}), true);
2509
- }
2510
- }
2511
-
2512
- function debugStart(data) {
2513
- if (Date.now() - debugState.started < 1000) {
2514
- console.log('Start ignored');
2515
- return;
2516
- }
2517
-
2518
- debugState.started = Date.now();
2519
- // stop the script if it's running
2520
- debugDisableScript(data.scriptName)
2521
- .then(() => debugStop())
2522
- .then(() => {
2523
- if (data.adapter) {
2524
- debugState.adapterInstance = data.adapter;
2525
- debugState.scriptName = '';
2526
- } else {
2527
- debugState.adapterInstance = '';
2528
- debugState.scriptName = data.scriptName;
2529
- }
2530
-
2531
- debugState.breakOnStart = data.breakOnStart;
2532
-
2533
- debugState.promiseOnEnd = new Promise(resolve => {
2534
- const options = {
2535
- stdio: ['ignore', 'inherit', 'inherit', 'ipc'],
2536
- //stdio: ['pipe', 'pipe', 'pipe', 'ipc']
2537
- };
2538
- const args = [];
2539
- if (debugState.adapterInstance) {
2540
- args.push('--breakOnStart');
2541
- }
2542
-
2543
- debugState.child = fork(`${__dirname}/lib/inspect.js`, args, options);
2544
-
2545
- /*debugState.child.stdout.setEncoding('utf8');
2546
- debugState.child.stderr.setEncoding('utf8');
2547
- debugState.child.stdout.on('data', childPrint);
2548
- debugState.child.stderr.on('data', childPrint);*/
2549
-
2550
- debugState.child && debugState.child.on('message', message => {
2551
- if (typeof message === 'string') {
2552
- try {
2553
- message = JSON.parse(message);
2554
- } catch (e) {
2555
- return adapter.log.error(`Cannot parse message from inspector: ${message}`);
2556
- }
2557
- }
2558
-
2559
- message.cmd !== 'ready' && adapter.setState('debug.from', JSON.stringify(message), true);
2560
-
2561
- switch (message.cmd) {
2562
- case 'ready': {
2563
- debugSendToInspector({cmd: 'start', scriptName: debugState.scriptName, adapterInstance: debugState.adapterInstance, instance: adapter.instance});
2564
- break;
2565
- }
2566
-
2567
- case 'watched': {
2568
- //console.log(`WATCHED: ${JSON.stringify(message)}`);
2569
- break;
2570
- }
2571
-
2572
- case 'paused': {
2573
- debugState.paused = true;
2574
- console.log(`host: PAUSED`);
2575
- break;
2576
- }
2577
-
2578
- case 'resumed' : {
2579
- debugState.paused = false;
2580
- //console.log(`STARTED`);
2581
- break;
2582
- }
2583
-
2584
- case 'log' : {
2585
- console.log(`[${message.severity}] ${message.text}`);
2586
- break;
2587
- }
2588
-
2589
- case 'readyToDebug': {
2590
- console.log(`host: readyToDebug (set breakpoints): [${message.scriptId}] ${message.script}`);
2591
- break;
2592
- }
2593
- }
2594
- });
2595
- debugState.child && debugState.child.on('error', error => {
2596
- adapter.log.error(`Cannot start inspector: ${error}`);
2597
- adapter.setState('debug.from', JSON.stringify({cmd: 'error', error}), true);
2598
- });
2599
-
2600
- debugState.child && debugState.child.on('exit', code => {
2601
- if (code) {
2602
- adapter.setState('debug.from', JSON.stringify({cmd: 'error', error: `invalid response code: ${code}`}), true);
2603
- }
2604
- adapter.setState('debug.from', JSON.stringify({cmd: 'debugStopped', code}), true);
2605
- debugState.child = null;
2606
- resolve(code);
2607
- });
2608
- });
2609
- });
2610
- }
2611
-
2612
- async function patchFont() {
2613
- let stat;
2614
- let dbFile;
2615
- try {
2616
- stat = nodeFS.statSync(`${__dirname}/admin/vs/base/browser/ui/codicons/codicon/codicon.ttf`);
2617
- dbFile = await adapter.readFileAsync('javascript.admin', `vs/base/browser/ui/codicons/codicon/codicon.ttf`);
2618
- } catch (error) {
2619
- // ignore
2620
- }
2621
- if (dbFile && dbFile.file) {
2622
- dbFile = dbFile.file;
2623
- }
2624
-
2625
- if (!stat || stat.size !== 73452 || !dbFile || dbFile.byteLength !== 73452) {
2626
- try {
2627
- const buffer = Buffer.from(JSON.parse(nodeFS.readFileSync(`${__dirname}/admin/vsFont/codicon.json`)), 'base64');
2628
-
2629
- const zip = await require('jszip').loadAsync(buffer);
2630
- const data = await zip.file('codicon.ttf').async('arraybuffer');
2631
- if (data.byteLength !== 73452) {
2632
- throw new Error('invalid font file!');
2633
- }
2634
- nodeFS.writeFileSync(`${__dirname}/admin/vs/base/browser/ui/codicons/codicon/codicon.ttf`, Buffer.from(data));
2635
- // upload this file
2636
- await adapter.writeFileAsync('javascript.admin', 'vs/base/browser/ui/codicons/codicon/codicon.ttf', Buffer.from(data));
2637
- return true;
2638
- } catch (error) {
2639
- adapter.log.error(`Cannot patch font: ${error}`);
2640
- return false;
2641
- }
2642
- } else {
2643
- return false;
2644
- }
2645
- }
2646
-
2647
- // If started as allInOne mode => return function to create instance
2648
- if (module.parent) {
2649
- module.exports = startAdapter;
2650
- } else {
2651
- // or start the instance directly
2652
- startAdapter();
2653
- }