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