UNPKG

@eva-ics/webengine

Version:
1 lines 154 kB
{"version":3,"file":"webengine.cjs","sources":["../node_modules/submap/dist/submap.es.js","../src/lib.ts"],"sourcesContent":["var m = Object.defineProperty;\nvar M = (t, s, e) => s in t ? m(t, s, { enumerable: !0, configurable: !0, writable: !0, value: e }) : t[s] = e;\nvar g = (t, s, e) => (M(t, typeof s != \"symbol\" ? s + \"\" : s, e), e), W = (t, s, e) => {\n if (!s.has(t))\n throw TypeError(\"Cannot \" + e);\n};\nvar u = (t, s, e) => (W(t, s, \"read from private field\"), e ? e.call(t) : s.get(t)), E = (t, s, e) => {\n if (s.has(t))\n throw TypeError(\"Cannot add the same private member more than once\");\n s instanceof WeakSet ? s.add(t) : s.set(t, e);\n}, h = (t, s, e, i) => (W(t, s, \"write to private field\"), i ? i.call(t, e) : s.set(t, e), e);\nclass C {\n constructor() {\n g(this, \"subscribers\");\n g(this, \"subtopics\");\n g(this, \"subtopics_by_regex\");\n g(this, \"subtopics_any\");\n g(this, \"sub_any\");\n this.subscribers = /* @__PURE__ */ new Set(), this.subtopics = /* @__PURE__ */ new Map(), this.subtopics_by_regex = [], this.sub_any = /* @__PURE__ */ new Set();\n }\n isEmpty() {\n return this.subscribers.size === 0 && this.subtopics.size === 0 && this.subtopics_by_regex.length === 0 && this.subtopics_any === void 0 && this.sub_any.size === 0;\n }\n}\nvar l, _, f, p, y;\nclass I {\n constructor() {\n g(this, \"subscriptions\");\n g(this, \"subscribed_topics\");\n E(this, l, void 0);\n E(this, _, void 0);\n E(this, f, void 0);\n E(this, p, void 0);\n E(this, y, void 0);\n this.subscriptions = new C(), this.subscribed_topics = /* @__PURE__ */ new Map(), h(this, l, 0), h(this, _, \"/\"), h(this, p, /* @__PURE__ */ new Set([\"?\"])), h(this, y, /* @__PURE__ */ new Set([\"*\"]));\n }\n separator(s) {\n return h(this, _, s), this;\n }\n regexPrefix(s) {\n return h(this, f, s), this;\n }\n matchAny(s) {\n return Array.isArray(s) ? h(this, p, new Set(s)) : h(this, p, /* @__PURE__ */ new Set([s])), this;\n }\n wildcard(s) {\n return Array.isArray(s) ? h(this, y, new Set(s)) : h(this, y, /* @__PURE__ */ new Set([s])), this;\n }\n listClients() {\n return Array.from(this.subscriptions.subscribers);\n }\n listTopics(s) {\n return Array.from(this.subscribed_topics.get(s) || []);\n }\n isEmpty() {\n return this.subscribed_topics.size === 0;\n }\n registerClient(s) {\n return this.subscribed_topics.has(s) ? !1 : (this.subscribed_topics.set(s, /* @__PURE__ */ new Set()), !0);\n }\n unregisterClient(s) {\n const e = this.subscribed_topics.get(s);\n if (e === void 0)\n return !1;\n for (const i of e)\n d(\n this.subscriptions,\n i.split(u(this, _)),\n 0,\n s,\n u(this, y),\n u(this, p),\n u(this, f)\n ), h(this, l, u(this, l) - 1);\n return this.subscribed_topics.delete(s), !0;\n }\n subscribe(s, e) {\n const i = this.subscribed_topics.get(e);\n return i === void 0 ? !1 : (i.has(s) || (R(\n this.subscriptions,\n s.split(u(this, _)),\n 0,\n e,\n u(this, y),\n u(this, p),\n u(this, f)\n ), i.add(s), h(this, l, u(this, l) + 1)), !0);\n }\n unsubscribe(s, e) {\n const i = this.subscribed_topics.get(e);\n return i === void 0 ? !1 : (i.has(s) && (d(\n this.subscriptions,\n s.split(u(this, _)),\n 0,\n e,\n u(this, y),\n u(this, p),\n u(this, f)\n ), i.delete(s), h(this, l, u(this, l) - 1)), !0);\n }\n unsubscribeAll(s) {\n const e = this.subscribed_topics.get(s);\n if (e === void 0)\n return !1;\n for (const i of e)\n d(\n this.subscriptions,\n i.split(u(this, _)),\n 0,\n s,\n u(this, y),\n u(this, p),\n u(this, f)\n );\n return h(this, l, u(this, l) - e.size), e.clear(), !0;\n }\n getSubscribers(s) {\n const e = /* @__PURE__ */ new Set();\n return z(\n this.subscriptions,\n s.split(u(this, _)),\n 0,\n u(this, f),\n e\n ), e;\n }\n isSubscribed(s) {\n return v(\n this.subscriptions,\n s.split(u(this, _)),\n 0,\n u(this, f)\n );\n }\n subscriptionCount() {\n return u(this, l);\n }\n clientCount() {\n return this.subscribed_topics.size;\n }\n}\nl = new WeakMap(), _ = new WeakMap(), f = new WeakMap(), p = new WeakMap(), y = new WeakMap();\nfunction R(t, s, e, i, b, o, c) {\n if (e < s.length) {\n const r = s[e];\n if (b.has(r))\n t.sub_any.add(i);\n else if (o.has(r))\n t.subtopics_any || (t.subtopics_any = new C()), R(\n t.subtopics_any,\n s,\n e + 1,\n i,\n b,\n o,\n c\n );\n else if (c && r.startsWith(c)) {\n const n = r.slice(c.length);\n try {\n const S = new RegExp(n);\n let a = t.subtopics_by_regex.find(\n (w) => w.regex.source === S.source\n );\n a || (a = {\n regex: S,\n sub: new C()\n }, t.subtopics_by_regex.push(a)), R(\n a.sub,\n s,\n e + 1,\n i,\n b,\n o,\n c\n );\n } catch {\n }\n } else\n t.subtopics.has(r) || t.subtopics.set(r, new C()), R(\n t.subtopics.get(r),\n s,\n e + 1,\n i,\n b,\n o,\n c\n );\n } else\n t.subscribers.add(i);\n}\nfunction d(t, s, e, i, b, o, c) {\n if (e < s.length) {\n const r = s[e];\n if (b.has(r))\n t.sub_any.delete(i);\n else if (o.has(r))\n t.subtopics_any && (d(\n t.subtopics_any,\n s,\n e + 1,\n i,\n b,\n o,\n c\n ), t.subtopics_any.isEmpty() && (t.subtopics_any = void 0));\n else if (c && r.startsWith(c)) {\n const n = r.slice(c.length);\n try {\n const S = new RegExp(n), a = t.subtopics_by_regex.findIndex(\n (w) => w.regex.source === S.source\n );\n if (a !== -1) {\n const w = t.subtopics_by_regex[a].sub;\n d(\n w,\n s,\n e + 1,\n i,\n b,\n o,\n c\n ), w.isEmpty() && t.subtopics_by_regex.splice(a, 1);\n }\n } catch {\n }\n } else {\n const n = t.subtopics.get(r);\n n && (d(\n n,\n s,\n e + 1,\n i,\n b,\n o,\n c\n ), n.isEmpty() && t.subtopics.delete(r));\n }\n } else\n t.subscribers.delete(i);\n}\nfunction z(t, s, e, i, b) {\n if (e < s.length) {\n const o = s[e];\n for (const c of t.sub_any)\n b.add(c);\n if (i && o.startsWith(i)) {\n const c = o.slice(i.length);\n try {\n const r = new RegExp(c);\n for (const [n, S] of t.subtopics)\n r.test(n) && z(S, s, e + 1, i, b);\n } catch {\n }\n } else {\n const c = t.subtopics.get(o);\n c && z(c, s, e + 1, i, b);\n }\n for (const c of t.subtopics_by_regex)\n c.regex.test(o) && z(c.sub, s, e + 1, i, b);\n t.subtopics_any && z(\n t.subtopics_any,\n s,\n e + 1,\n i,\n b\n );\n } else\n for (const o of t.subscribers)\n b.add(o);\n}\nfunction v(t, s, e, i) {\n if (e < s.length) {\n const b = s[e];\n if (t.sub_any.size > 0)\n return !0;\n if (i && b.startsWith(i)) {\n const o = b.slice(i.length);\n try {\n const c = new RegExp(o);\n for (const [r, n] of t.subtopics)\n if (c.test(r) && v(n, s, e + 1, i))\n return !0;\n } catch {\n }\n } else {\n const o = t.subtopics.get(b);\n if (o && v(o, s, e + 1, i))\n return !0;\n }\n for (const o of t.subtopics_by_regex)\n if (o.regex.test(b) && v(o.sub, s, e + 1, i))\n return !0;\n if (t.subtopics_any && v(\n t.subtopics_any,\n s,\n e + 1,\n i\n ))\n return !0;\n } else if (t.subscribers.size > 0)\n return !0;\n return !1;\n}\nexport {\n I as SubMap\n};\n//# sourceMappingURL=submap.es.js.map\n","const eva_webengine_version = \"0.9.15\";\n\nimport { Logger } from \"bmat/log\";\nimport { cookies } from \"bmat/dom\";\nimport { SubMap } from \"submap\";\n\nconst WILDCARDS = [\"*\", \"#\"];\nconst MATCH_ANY = [\"+\", \"?\"];\n\nconst isMask = (oid: string): boolean => {\n for (let c of oid) {\n if (WILDCARDS.includes(c) || MATCH_ANY.includes(c)) {\n return true;\n }\n }\n return false;\n};\n\nenum SerializationKind {\n JSON = \"json\",\n MsgPack = \"msgpack\"\n}\n\nenum EvaErrorKind {\n NOT_FOUND = -32001,\n ACCESS_DENIED = -32002,\n SYSTEM_ERROR = -32003,\n OTHER = -32004,\n NOT_READY = -32005,\n UNSUPPORTED = -32006,\n CORE_ERROR = -32007,\n TIMEOUT = -32008,\n INVALID_DATA = -32009,\n FUNC_FAILED = -32010,\n ABORTED = -32011,\n ALREADY_EXISTS = -32012,\n BUSY = -32013,\n METHOD_NOT_IMPLEMENTED = -32014,\n TOKEN_RESTRICTED = -32015,\n IO = -32016,\n REGISTRY = -32017,\n EVAHI_AUTH_REQUIRED = -32018,\n\n ACCESS_DENIED_MORE_DATA_REQUIRED = -32022,\n\n PARSE = -32700,\n INVALID_REQUEST = -32600,\n METHOD_NOT_FOUND = -32601,\n INVALID_PARAMS = -32602,\n INTERNAL_RPC = -32603,\n\n BUS_CLIENT_NOT_REGISTERED = -32113,\n BUS_DATA = -32114,\n BUS_IO = -32115,\n BUS_OTHER = -32116,\n BUS_NOT_SUPPORTED = -32117,\n BUS_BUSY = -32118,\n BUS_NOT_DELIVERED = -32119,\n BUS_TIMEOUT = -32120,\n BUS_ACCESS = -32121\n}\n\nenum EventKind {\n HeartbeatSuccess = \"heartbeat.success\",\n HeartbeatError = \"heartbeat.error\",\n LoginSuccess = \"login.success\",\n LoginFailed = \"login.failed\",\n LoginOTPRequired = \"login.otp_required\",\n LoginOTPInvalid = \"login.otp_invalid\",\n LoginOTPSetup = \"login.otp_setup\",\n WsEvent = \"ws.event\",\n ServerReload = \"server.reload\",\n ServerRestart = \"server.restart\",\n LogRecord = \"log.record\",\n LogPostProcess = \"log.postprocess\",\n WASMError = \"wasm.error\"\n}\n\nenum StateProp {\n Status = \"status\",\n Value = \"value\",\n Any = \"any\"\n}\n\nconst GLOBAL_BLOCK_NAME = \".\";\n\nconst ERR_REQUIRE_LOGGED_IN = \"Not logged in\";\n\ninterface EvaConfig {\n engine?: EvaEngineConfig;\n}\n\ninterface EvaEngineConfig {\n api_uri?: string;\n apikey?: string;\n debug?: boolean | number;\n login?: string;\n password?: string;\n set_auth_cookies?: boolean;\n state_updates?: boolean | Array<string>;\n wasm?: boolean | string;\n ws_mode?: boolean;\n log_params?: LogParams;\n interval: { [key in IntervalKind]: number };\n}\n\ninterface OTPParams {\n size?: number;\n issuer?: string;\n user?: string;\n xtr?: string;\n}\n\ninterface HiQRParams {\n size?: number;\n url?: string;\n user?: string;\n password?: string;\n}\n\ninterface LogRecord {\n dt: string;\n h: string;\n l: number;\n lvl: string;\n mod: string;\n msg: string;\n t: number;\n th: string | null;\n}\n\ninterface WsCommand {\n m: string;\n p?: any;\n}\n\ninterface LoginPayload {\n k?: string;\n u?: string;\n p?: string;\n a?: string;\n xopts?: object;\n}\n\ninterface SvcMessage {\n kind: string;\n svc: string;\n message?: string;\n value?: string;\n}\n\ninterface JsonRpcRequest {\n jsonrpc: string;\n method: string;\n params?: object;\n id: number;\n}\n\ninterface JsonRpcResponse {\n jsonrpc: string;\n result?: object;\n error?: EvaError;\n id: number;\n}\n\ninterface External {\n fetch?: any;\n WebSocket?: any;\n QRious?: any;\n msgpack?: any;\n}\n\ninterface ActionResult {\n elapsed: number;\n exitcode: number | null;\n finished: boolean;\n node: string;\n oid: string;\n params: any;\n priority: number;\n status: string;\n svc: string;\n time: any;\n uuid: string;\n out: null | string;\n err: null | string;\n}\n\ninterface StatePayload {\n full?: boolean;\n i?: string | Array<string>;\n}\n\ninterface LvarIncrDecrResult {\n result: number;\n}\n\ninterface LogParams {\n level: number;\n records: number;\n}\n\ninterface ItemState {\n act?: number;\n connected?: boolean;\n enabled?: boolean;\n ieid?: Array<number>;\n meta?: object;\n node?: string;\n oid?: string;\n status?: number | null;\n t?: number;\n value: any;\n}\n\ninterface Watcher {\n func: (state: ItemState) => void;\n prot: boolean;\n}\n\nenum IntervalKind {\n AjaxReload = \"ajax_reload\",\n AjaxLogReload = \"log_reload\",\n ActionWatch = \"action_watch\",\n Heartbeat = \"heartbeat\",\n Reload = \"reload\",\n Restart = \"restart\",\n WSBufTTL = \"ws_buf_ttl\"\n}\n\nclass EvaError {\n code: number;\n message?: string;\n data?: any;\n constructor(code: number, message?: string, data?: any) {\n this.code = code;\n this.message = message;\n this.data = data;\n }\n}\n\nclass EvaBulkRequestPartHandler {\n fn_ok?: (result: any) => void;\n fn_err?: (result: any) => void;\n\n constructor() {}\n then(fn_ok: (result: any) => void) {\n this.fn_ok = fn_ok;\n return this;\n }\n catch(fn_err: (err: any) => void) {\n this.fn_err = fn_err;\n return this;\n }\n}\n\nclass EvaBulkRequest {\n requests: Map<number, EvaBulkRequestPartHandler>;\n payload: Array<any>;\n eva: Eva;\n\n constructor(eva: Eva) {\n this.requests = new Map<number, EvaBulkRequestPartHandler>();\n this.payload = [];\n this.eva = eva;\n }\n /**\n * Prepare API function call for bulk calling\n *\n * Calls any available SFA API function\n *\n * @param p1 item OID (if required) or API call params\n * @param p2 extra call params or empty object\n * @param fn_ok function which is executed on successfull call\n * @parma fn_err function which is executed on error\n *\n * @returns Part handler object\n */\n prepare(\n method: string,\n p1: string | object,\n p2?: object\n ): EvaBulkRequestPartHandler {\n let params: any;\n if (typeof p1 === \"string\" || Array.isArray(p1)) {\n params = p2 || {};\n params.i = p1;\n } else {\n params = p1;\n }\n let p = this.eva._prepare_call_params(params);\n let payload: JsonRpcRequest = this.eva._prepare_api_call(method, p);\n let req = new EvaBulkRequestPartHandler();\n this.requests.set(payload.id, req);\n this.payload.push(payload);\n return req;\n }\n /**\n * Perform bulk API call\n */\n call(): Promise<boolean> {\n let api_uri = `${this.eva.api_uri}/jrpc`;\n this.eva._debug(\"call_bulk\", `${api_uri}`);\n return new Promise((resolve, reject) => {\n if (this.eva.allow_logged_in_calls_only) {\n if (!this.eva.logged_in) {\n throw new EvaError(EvaErrorKind.ACCESS_DENIED, ERR_REQUIRE_LOGGED_IN);\n }\n }\n if (this.payload.length == 0) {\n resolve(true);\n return;\n }\n this.eva.external\n .fetch(api_uri, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\"\n },\n redirect: \"error\",\n body: JSON.stringify(this.payload)\n })\n .then((response: any) => {\n if (response.ok) {\n response\n .json()\n .then((data: JsonRpcResponse) => {\n this.eva._debug(\"call_bulk success\");\n if (Array.isArray(data)) {\n data.forEach((d) => {\n if (\n typeof d.id === \"undefined\" ||\n (typeof d.result === \"undefined\" &&\n typeof d.error === \"undefined\")\n ) {\n reject({\n code: -32009,\n message: \"Invalid server response\",\n data: d\n });\n } else {\n let id = d.id;\n let req = this.requests.get(id);\n let fn_ok;\n let fn_err;\n if (req !== undefined) {\n fn_ok = req.fn_ok;\n fn_err = req.fn_err;\n }\n if (d.error !== undefined) {\n this.eva._debug(\n \"call_bulk req\",\n `${id} failed: ${d.error.code} (${d.error.message})`\n );\n if (fn_err) {\n fn_err({\n code: d.error.code,\n message: d.error.message,\n data: d\n });\n }\n } else {\n if (this.eva.debug == 2) {\n this.eva.log.info(\n `call_bulk API ${id} ${(req as any).func} response`,\n d.result\n );\n }\n if (fn_ok) {\n fn_ok(d.result);\n }\n }\n }\n });\n resolve(true);\n } else {\n let code = EvaErrorKind.INVALID_DATA;\n let message = \"Invalid server response (not an array)\";\n this.eva._debug(\"call_bulk\", `failed: ${code} (${message})`);\n reject(new EvaError(code, message, data));\n }\n })\n .catch((err: any) => {\n let code = EvaErrorKind.INVALID_DATA;\n let message = \"Invalid server response\";\n this.eva._debug(\"call_bulk\", `failed: ${code} (${message})`);\n reject(new EvaError(code, message, err));\n });\n } else {\n let code = EvaErrorKind.CORE_ERROR;\n let message = \"Server error\";\n this.eva._debug(\"call_bulk\", `failed: ${code} (${message})`);\n reject(new EvaError(code, message));\n }\n })\n .catch((err: any) => {\n let code = EvaErrorKind.CORE_ERROR;\n let message = \"Server error\";\n this.eva._debug(\"call_bulk\", `failed: ${code} (${message})`);\n reject(new EvaError(code, message, err));\n });\n });\n }\n}\n\nclass Eva_ACTION {\n eva: Eva;\n\n constructor(eva: Eva) {\n this.eva = eva;\n }\n /**\n * Call unit action with value=1\n *\n * @param oid {string} unit OID\n * @param wait {boolean} wait until the action is completed (default: true)\n */\n async start(oid: string, wait = true): Promise<ActionResult> {\n return this.exec(oid, { v: 1 }, wait);\n }\n /**\n * Call unit action with value=0\n *\n * @param oid {string} unit OID\n * @param wait {boolean} wait until the action is completed (default: true)\n */\n async stop(oid: string, wait = true): Promise<ActionResult> {\n return this.exec(oid, { v: 0 }, wait);\n }\n /**\n * Call unit action to toggle its value\n *\n * @param oid {string} unit OID\n * @param wait {boolean} wait until the action is completed (default: true)\n */\n async toggle(oid: string, wait = true): Promise<ActionResult> {\n return this._act(\"action.toggle\", oid, {}, wait);\n }\n /**\n * Call unit action\n *\n * @param oid {string} unit OID\n * @param params {object} action params\n * @param wait {boolean} wait until the action is completed (default: true)\n */\n exec(oid: string, params: object, wait = true) {\n return this._act(\"action\", oid, params, wait);\n }\n /**\n * Terminate all unit actions\n *\n * @param oid {string} unit OID\n */\n async kill(oid: string) {\n await this.eva.call(\"action.kill\", oid);\n }\n /**\n * Terminate a unit action\n *\n * @param uuid {string} action uuid\n */\n async terminate(uuid: string) {\n let method = \"action.terminate\";\n await this.eva.call(method, { u: uuid });\n }\n /**\n * Run lmacro\n *\n * @param oid {string} lmacro oid\n * @param params {object} call params\n * @param wait {boolean} wait until completed (default: true)\n */\n async run(oid: string, params?: object, wait = true): Promise<ActionResult> {\n return this._act(\"run\", oid, params, wait);\n }\n _act(\n method: string,\n oid: string,\n params?: object,\n wait = false\n ): Promise<ActionResult> {\n return new Promise((resolve, reject) => {\n this.eva\n .call(method, oid, params)\n .then((data: ActionResult) => {\n if (wait === false) {\n resolve(data);\n } else {\n this.eva.watch_action(data.uuid, (res: ActionResult | EvaError) => {\n if ((res as ActionResult).uuid !== undefined) {\n if ((res as ActionResult).finished) {\n resolve(res as ActionResult);\n }\n } else {\n reject(res);\n }\n });\n }\n })\n .catch((err) => {\n reject(err);\n });\n });\n }\n}\n\nclass Eva_LVAR {\n eva: Eva;\n\n constructor(eva: Eva) {\n this.eva = eva;\n }\n /**\n * Reset lvar (set status to 1)\n *\n * @param oid {string} lvar oid\n */\n async reset(oid: string) {\n await this.eva.call(\"lvar.reset\", oid);\n }\n /**\n * Clear lvar (set status to 0)\n *\n * @param oid {string} lvar oid\n */\n async clear(oid: string) {\n await this.eva.call(\"lvar.clear\", oid);\n }\n /**\n * Toggle lvar status\n *\n * @param oid {string} lvar oid\n */\n async toggle(oid: string) {\n await this.eva.call(\"lvar.toggle\", oid);\n }\n /**\n * Increment lvar value\n *\n * @param oid {string} lvar oid\n *\n * @returns the new value\n */\n async incr(oid: string): Promise<number> {\n let data = (await this.eva.call(\"lvar.incr\", oid)) as LvarIncrDecrResult;\n return data.result;\n }\n /**\n * Decrement lvar value\n *\n * @param oid {string} lvar oid\n *\n * @returns the new value\n */\n async decr(oid: string) {\n let data = (await this.eva.call(\"lvar.decr\", oid)) as LvarIncrDecrResult;\n return data.result;\n }\n /**\n * Set lvar state\n *\n * @param oid {string} lvar oid\n * @param status {numberr} lvar status\n * @param value lvar value\n */\n async set(oid: string, status?: number, value?: any) {\n let params: any = {};\n if (status !== undefined) {\n params.status = status;\n }\n if (value !== undefined) {\n params.value = value;\n }\n if (Object.keys(params).length) {\n await this.eva.call(\"lvar.set\", oid, params);\n }\n }\n /**\n * Set lvar status\n *\n * @param oid {string} lvar oid\n * @param status {number} lvar status\n */\n async set_status(oid: string, status: number) {\n await this.set(oid, status);\n }\n /**\n * Set lvar value\n *\n * @param oid {string} lvar oid\n * @param value lvar value\n */\n async set_value(oid: string, value: any) {\n await this.set(oid, (value = value));\n }\n\n /**\n * Get lvar expiration time left\n *\n * @param lvar_oid {string} lvar OID\n *\n * @returns seconds to expiration, -1 if expired, -2 if stopped\n */\n expires(lvar_oid: string): number | null | undefined {\n // get item state\n let state = this.eva.state(lvar_oid) as ItemState;\n // if no such item\n if (state === undefined || state.t === undefined) return undefined;\n // if item has no expiration or expiration is set to zero\n if (\n !state.meta ||\n (state.meta as any).expires === undefined ||\n (state.meta as any).expires == 0\n ) {\n return null;\n }\n // if timer is disabled (stopped), return -2\n if (state.status == 0) return -2;\n // if timer is expired, return -1\n if (state.status == -1) return -1;\n let t =\n (state.meta as any).expires -\n new Date().getTime() / 1000 +\n this.eva.tsdiff +\n state.t;\n if (t < 0) t = 0;\n return t;\n }\n}\n\n/**\n * Binary stream parameters\n */\ninterface EvaStreamParameters {\n /**\n * OID the data is streamed from\n */\n oid: string;\n /**\n * Name of the stream, used for identification, unique\n */\n name: string;\n /**\n * On start callback\n */\n onStart?: () => void;\n /**\n * On data callback\n */\n onData?: (data: ArrayBuffer) => void;\n /**\n * On error callback\n */\n onError?: (err: EvaError) => void;\n /**\n * On end of stream callback, the stream is not closed and may receive more\n * data, but the handler must be prepared certain stream parameters may\n * change\n */\n onEOS?: () => void;\n}\n\nclass _EvaStream {\n oid: string;\n eva: Eva;\n name: string;\n ws: WebSocket | null;\n onStart: () => void;\n onData: (data: ArrayBuffer) => void;\n onError: (err: EvaError) => void;\n onEOS: () => void;\n constructor(oid: string, name: string, eva: Eva) {\n this.oid = oid;\n this.name = name;\n this.eva = eva;\n this.ws = null;\n this.onStart = () => {};\n this.onData = (_data: ArrayBuffer) => {};\n this.onError = (err: EvaError) => {\n this.eva.log.error(`Stream ${this.name} error`, err);\n };\n this.onEOS = () => {};\n }\n _restart() {\n this._stop();\n this._start();\n }\n _start() {\n if (!this.eva.ws_mode) {\n this.onError(\n new EvaError(\n EvaErrorKind.UNSUPPORTED,\n \"WebSocket mode is disabled in EVA ICS WebEngine\"\n )\n );\n return;\n }\n if (!this.eva.api_token) {\n this.onError(new EvaError(EvaErrorKind.ACCESS_DENIED, \"Not logged in\"));\n return;\n }\n let ws_uri = this.eva._get_ws_uri();\n ws_uri += `k=${this.eva.api_token}`;\n const ws = new this.eva.external.WebSocket(ws_uri);\n ws.binaryType = \"arraybuffer\";\n ws.onerror = (evt: Event) => {\n this.onError(\n new EvaError(EvaErrorKind.FUNC_FAILED, `WebSocket error: ${evt.type}`)\n );\n this._stop();\n };\n ws.onmessage = (evt: MessageEvent<any>) => {\n if (typeof evt.data === \"string\") {\n let data = JSON.parse(evt.data);\n if (data.s !== \"stream\") {\n return;\n }\n switch (data.d) {\n case \"start\":\n this.onStart();\n break;\n case \"eos\":\n this.onEOS();\n break;\n case \"forbidden\":\n this.onError(\n new EvaError(\n EvaErrorKind.ACCESS_DENIED,\n `Stream ${this.name} access denied (${this.oid})`\n )\n );\n this._stop();\n break;\n default:\n break;\n }\n } else {\n this.onData(evt.data);\n }\n };\n ws.onclose = () => {\n this._stop();\n };\n ws.addEventListener(\"open\", () => {\n let payload = { m: \"stream.start\", p: { i: this.oid } };\n ws.send(JSON.stringify(payload));\n ws.send(\"\");\n });\n this.ws = ws;\n }\n _stop() {\n const ws = this.ws;\n if (ws) {\n try {\n ws.onclose = null;\n ws.onmessage = () => {};\n ws.onerror = () => {};\n ws.close();\n } catch (err) {\n // web socket may be still open, will close later\n setTimeout(() => {\n try {\n ws?.close();\n } catch (err) {}\n }, 100);\n }\n }\n }\n}\n\nclass _EvaStateBlock {\n state_updates: boolean | Array<string>;\n eva: Eva;\n name: string;\n _ajax_reloader: any;\n constructor(\n name: string,\n state_updates: boolean | Array<string>,\n engine: Eva\n ) {\n this.name = name;\n this.state_updates = state_updates;\n this.eva = engine;\n }\n _start() {\n if (this.eva.ws_mode) {\n this.eva._start_ws(this.state_updates, this.name);\n }\n this.eva._load_states(this.state_updates, this.name).then(() => {\n if (this.eva.ws_mode) {\n const reload = this.eva._intervals.get(IntervalKind.Reload) as number;\n if (reload) {\n this._ajax_reloader = setInterval(() => {\n if (!this.eva.logged_in) {\n this._stop();\n return;\n }\n this.eva._load_states(this.state_updates, this.name);\n }, reload * 1000);\n }\n } else {\n this._ajax_reloader = setInterval(\n () => {\n this.eva._load_states(this.state_updates, this.name);\n },\n this.eva._intervals.get(IntervalKind.AjaxReload) as number\n );\n }\n });\n }\n _restart() {\n this._stop();\n this._start();\n }\n _stop() {\n if (this._ajax_reloader) {\n clearInterval(this._ajax_reloader);\n }\n let ws = this.eva.ws.get(this.name);\n if (ws) {\n this.eva.ws.delete(this.name);\n try {\n ws.onclose = null;\n ws.onerror = function () {};\n ws.close();\n } catch (err) {\n // web socket may be still open, will close later\n setTimeout(() => {\n try {\n ws?.close();\n } catch (err) {}\n }, 100);\n }\n }\n }\n}\n\nenum TokenMode {\n Normal = \"normal\",\n ReadOnly = \"readonly\"\n}\n\nenum SessionAuthKind {\n Token = \"token\",\n Key = \"key\",\n // returned for \"login\" method only, after switched to \"token\"\n Login = \"login\",\n No = \"unauthorized\"\n}\n\ninterface SessionACI {\n acl: string;\n auth: SessionAuthKind;\n auth_svc: string;\n token_mode: TokenMode;\n u: string;\n}\n\ninterface ACLProp {\n items: Array<string>;\n pvt: Array<string>;\n rpvt: Array<string>;\n}\n\nenum ACLOp {\n Log = \"log\",\n Developer = \"developer\",\n Moderator = \"moderator\",\n Supervisor = \"supervisor\"\n}\n\ninterface SessionACL {\n admin?: boolean;\n deny_read: ACLProp;\n deny_write: ACLProp;\n from: Array<string>;\n id: string;\n meta: { [key: string]: Array<any> };\n ops: Array<ACLOp>;\n read: ACLProp;\n write: ACLProp;\n}\n\ninterface ServerInfo {\n aci: SessionACI;\n acl: SessionACL;\n build: number;\n ok: boolean;\n hmi_svc_id: string;\n num_cpus: number;\n product_code: string;\n product_name: string;\n system_name: string;\n time: number;\n uptime: number;\n version: string;\n}\n\nenum LoginState {\n Active = \"active\",\n Starting = \"starting\",\n Stopping = \"stopping\",\n Inactive = \"inactive\",\n Failed = \"failed\",\n OTPRequired = \"otp.required\",\n OTPInvalid = \"otp.invalid\",\n OTPSetup = \"otp.setup\"\n}\n\ninterface SessionState {\n login: LoginState;\n error: EvaError | null;\n otp: string | null;\n}\n\n/**\n * The default session state\n */\nconst defaultSessionState = (): SessionState => {\n return {\n login: LoginState.Inactive,\n error: null,\n otp: null\n };\n};\n\ntype EventHandler = (topic: string, event: any) => void;\n\nenum EventTopic {\n ItemState = \"ST\",\n Server = \"SERVER\",\n Supervisor = \"SUPERVISOR\",\n WeSession = \"WE/SESSION\",\n WeItemState = \"WE/ST\"\n}\n\n// Topics\n//\n// ST/OID (as path)\n// SERVER = server info\n// SERVER/# server events (e.g. SERVER/RELOAD)\n// SUPERVISOR/# supervisor events\n// WE/SESSION = SessionState\n// WE/ST (state updates)\n// WE/ST/BLOCK (state updates on init, null on delete)\n// WE/ST/BLOCK/OID (internal block state)\n\nclass Eva {\n action: Eva_ACTION;\n lvar: Eva_LVAR;\n api_uri: string;\n ws_uri: string;\n #apikey: string;\n api_token: string;\n //api_version: number | null;\n authorized_user: string | null;\n clear_unavailable: boolean;\n debug: boolean | number;\n allow_logged_in_calls_only: boolean;\n external: External;\n evajw: any;\n in_evaHI: boolean;\n log_params: LogParams;\n log: Logger;\n logged_in: boolean;\n login: string;\n login_xopts: object | null;\n log_level_names: Map<number, string>;\n #password: string;\n set_auth_cookies: boolean;\n state_updates: boolean | Array<string>;\n tsdiff: number;\n version: string;\n wasm: boolean | string;\n ws_mode: boolean;\n server_info: ServerInfo | null;\n ignore_password_set_on_next_login: boolean;\n _event_map: SubMap<EventHandler> | null;\n _api_call_id: number;\n _handlers: Map<EventKind, (...args: any[]) => void | boolean>;\n _intervals: Map<IntervalKind, number>;\n _ws_handler_registered: boolean;\n _heartbeat_reloader: any;\n _ajax_reloader: any;\n _log_reloader: any;\n _scheduled_restarter: any;\n _states: Map<string, Map<string, ItemState>>;\n _blocks: Map<string, _EvaStateBlock>;\n _streams: Map<string, _EvaStream>;\n _last_ping: Map<string, number | null>;\n _last_pong: Map<string, number | null>;\n ws: Map<string, WebSocket>;\n _action_states: Map<string, ActionResult>;\n _action_watch_functions: Map<\n String,\n Array<(result: ActionResult | EvaError) => void>\n >;\n _log_subscribed: boolean;\n _log_started: boolean;\n _log_first_load: boolean;\n _log_loaded: boolean;\n _update_state_functions: Map<string, Array<Watcher>>;\n _update_state_mask_functions: Map<string, Array<Watcher>>;\n _lr2p: Array<LogRecord>;\n\n constructor() {\n this.version = eva_webengine_version;\n this.log = new Logger();\n this.login = \"\";\n this.#password = \"\";\n this.login_xopts = null;\n this.#apikey = \"\";\n this.api_uri = \"\";\n this.ws_uri = \"/ws\";\n this.set_auth_cookies = true;\n this.api_token = \"\";\n this.authorized_user = null;\n this.logged_in = false;\n this.ignore_password_set_on_next_login = false;\n this.debug = false;\n this.allow_logged_in_calls_only = false;\n this.state_updates = true;\n this.wasm = false;\n this.clear_unavailable = false;\n this._ws_handler_registered = false;\n this.ws_mode = true;\n this.ws = new Map();\n //this.api_version = null;\n this._api_call_id = 0;\n this.tsdiff = 0;\n this._last_ping = new Map();\n this._last_ping.set(GLOBAL_BLOCK_NAME, null);\n this._last_pong = new Map();\n this._last_pong.set(GLOBAL_BLOCK_NAME, null);\n this._streams = new Map();\n this._log_subscribed = false;\n this._log_started = false;\n this._log_first_load = false;\n this._log_loaded = false;\n this._lr2p = [];\n this._event_map = null;\n this.in_evaHI =\n typeof navigator !== \"undefined\" &&\n typeof navigator.userAgent === \"string\" &&\n navigator.userAgent.startsWith(\"evaHI \");\n this.log_params = {\n level: 20,\n records: 200\n };\n this._update_state_functions = new Map();\n this._update_state_mask_functions = new Map();\n this._handlers = new Map();\n this._handlers.set(EventKind.HeartbeatError, this.restart);\n this._handlers.set(EventKind.WASMError, (e) => {\n this.log.error(e);\n this._critical(\"WASM load error\", true, true);\n });\n this._states = new Map();\n this._states.set(GLOBAL_BLOCK_NAME, new Map());\n this._blocks = new Map();\n this._intervals = new Map([\n [IntervalKind.AjaxReload, 2],\n [IntervalKind.AjaxLogReload, 2],\n [IntervalKind.ActionWatch, 0.5],\n [IntervalKind.Heartbeat, 5],\n [IntervalKind.Reload, 5],\n [IntervalKind.Restart, 1],\n [IntervalKind.WSBufTTL, 0]\n ]);\n this.log_level_names = new Map([\n [10, \"DEBUG\"],\n [20, \"INFO\"],\n [30, \"WARNING\"],\n [40, \"ERROR\"],\n [50, \"CRITICAL\"]\n ]);\n this._heartbeat_reloader = null;\n this._ajax_reloader = null;\n this._log_reloader = null;\n this._scheduled_restarter = null;\n this._action_watch_functions = new Map();\n this._action_states = new Map();\n this._clear();\n this._clear_watchers();\n this.action = new Eva_ACTION(this);\n this.lvar = new Eva_LVAR(this);\n this.evajw = null;\n this.external = {};\n this.server_info = null;\n if (typeof window !== \"undefined\") {\n if (typeof window.fetch !== \"undefined\") {\n this.external.fetch = window.fetch.bind(window);\n }\n } else if (typeof fetch !== \"undefined\") {\n this.external.fetch = fetch;\n } else {\n this.external.fetch = null;\n }\n if (typeof WebSocket !== \"undefined\") {\n this.external.WebSocket = WebSocket;\n } else {\n this.external.WebSocket = null;\n }\n if (\n typeof window !== \"undefined\" &&\n typeof (window as any).QRious !== \"undefined\"\n ) {\n this.external.QRious = (window as any).QRious;\n } else {\n this.external.QRious = null;\n }\n }\n\n // wasm override\n /**\n * Enables pub/sub event map. Usually not required to be called manually, as\n * called automatically as soon as there is a subscription performed.\n */\n enable_event_map() {\n if (this._event_map === null) {\n this._event_map = (new SubMap() as SubMap<EventHandler>)\n .matchAny(MATCH_ANY)\n .wildcard(WILDCARDS)\n .regexPrefix(\"r~\")\n .separator(\"/\");\n }\n }\n\n // wasm override\n /**\n * Subscribe to an event topic\n *\n * @param topic {string} event topic\n * @param fn {EventHandler} event handler\n *\n * @returns true if subscription was successful\n */\n subscribe_event_topic(topic: string, fn: EventHandler): boolean {\n this.enable_event_map();\n this._event_map!.registerClient(fn);\n this._event_map!.subscribe(topic, fn);\n return true;\n }\n\n // wasm override\n /**\n * Subscribe to multiple event topics\n *\n * @param topics {Array<string>} event topics\n * @param fn {EventHandler} event handler\n * @returns true if subscription was successful\n */\n subscribe_event_topics(topics: Array<string>, fn: EventHandler): boolean {\n this.enable_event_map();\n this._event_map!.registerClient(fn);\n for (let topic of topics) {\n this._event_map!.subscribe(topic, fn);\n }\n return true;\n }\n\n // wasm override\n /**\n * Unsubscribe from an event topic\n *\n * @param topic {string} event topic\n * @param fn {EventHandler} event handler\n */\n unsubscribe_event_topic(topic: string, fn: EventHandler) {\n this._event_map?.unsubscribe(topic, fn);\n }\n\n // wasm override\n /**\n * Unsubscribe from multiple event topics\n *\n * @param topics {Array<string>} event topics\n * @param fn {EventHandler} event handler\n */\n unsubscribe_event_topics(topics: Array<string>, fn: EventHandler) {\n for (let topic of topics) {\n this._event_map?.unsubscribe(topic, fn);\n }\n }\n\n // wasm override\n /**\n * Unsubscribe from all event topics\n *\n * @param fn {EventHandler} event handler\n */\n unsubscribe_all_event_topics(fn: EventHandler) {\n this._event_map?.unsubscribeAll(fn);\n this._event_map?.unregisterClient(fn);\n }\n\n // WASM override\n _push_event_topic(topic: string, data: any) {\n if (this._event_map) {\n const clients = this._event_map.getSubscribers(topic);\n for (const client of clients) {\n client(topic, data);\n }\n }\n }\n\n /**\n * Set engine login credentials\n *\n * @param login {string|null} login\n * @param password {string|null} password\n */\n set_login_password(login?: string | null, password?: string | null) {\n this.login = login || \"\";\n this.#password = password || \"\";\n this.#apikey = \"\";\n }\n\n /**\n * Set/clear engine API key\n *\n * @param apikey {string | null} API key\n */\n set_api_key(apikey?: string | null) {\n this.#apikey = apikey || \"\";\n this.login = \"\";\n this.#password = \"\";\n }\n\n /**\n * Is engine password set\n *\n * @returns true if password is set\n */\n is_password_set() {\n return this.#password !== \"\";\n }\n\n /**\n * Is engine authentication set\n *\n * @returns true if auth is set\n */\n is_auth_set(): boolean {\n return this.#apikey !== \"\" || (this.login !== \"\" && this.#password !== \"\");\n }\n\n /**\n * Clear engine authenication credentials\n */\n clear_auth() {\n this.login = \"\";\n this.#password = \"\";\n this.#apikey = \"\";\n }\n\n /**\n * Start a binary stream\n *\n * @param params {EvaStreamParameters} stream parameters\n */\n start_stream(params: EvaStreamParameters) {\n const old_stream = this._streams.get(params.oid);\n if (old_stream) {\n old_stream._stop();\n }\n const stream = new _EvaStream(params.oid, params.name, this);\n if (params.onStart) {\n stream.onStart = params.onStart;\n }\n if (params.onData) {\n stream.onData = params.onData;\n }\n if (params.onError) {\n stream.onError = params.onError;\n }\n if (params.onEOS) {\n stream.onEOS = params.onEOS;\n }\n this._streams.set(params.name, stream);\n stream._start();\n }\n\n /**\n * Stop a binary stream\n *\n * @param name {string} stream name\n */\n stop_stream(name: string) {\n let stream = this._streams.get(name);\n if (stream) {\n stream._stop();\n this._streams.delete(name);\n }\n }\n\n /**\n * Register a state block\n *\n * @param name {string} block name\n * @param state_updates {boolean | Array<string>} state updates\n */\n register_state_block(name: string, state_updates: boolean | Array<string>) {\n if (name == GLOBAL_BLOCK_NAME) {\n throw new EvaError(\n EvaErrorKind.INVALID_PARAMS,\n `WebEngine state block name ${GLOBAL_BLOCK_NAME} is reserved`\n );\n }\n check_state_updates(state_updates);\n let old_block = this._blocks.get(name);\n if (old_block) {\n console.error(\n `WebEngine state block ${name} has been already registered, removing the old instance`\n );\n old_block._stop();\n }\n let block = new _EvaStateBlock(name, state_updates, this);\n if (this.logged_in) {\n block._start();\n }\n this._blocks.set(name, block);\n this._init_block(name);\n this._push_event_topic(`${EventTopic.WeItemState}/${name}`, state_updates);\n }\n\n /**\n * Unregister a state block\n *\n * @param name {string} block name\n */\n unregister_state_block(name: string) {\n let block = this._blocks.get(name);\n if (block) {\n block._stop();\n this._delete_block(name);\n this._blocks.delete(name);\n this._push_event_topic(`${EventTopic.WeItemState}/${name}`, null);\n }\n }\n\n /**\n * Unregister all state blocks\n */\n unregister_all_state_blocks() {\n for (let [name, block] of this._blocks) {\n block._stop();\n this._delete_block(name);\n this._push_event_topic(`${EventTopic.WeItemState}/${name}`, null);\n }\n this._blocks.clear();\n }\n\n bulk_request(): EvaBulkRequest {\n return new EvaBulkRequest(this);\n }\n\n // WASM override\n /**\n * Get engine mode\n \n * @returns \"js\" or \"wasm\"\n */\n get_mode(): string {\n return \"js\";\n }\n\n /**\n * Start the engine\n *\n * After calling the function authenticates user, opens a WebSocket (in\n * case of WS mode) or schedule AJAXs refresh interval.\n */\n start() {\n this._cancel_scheduled_restart();\n this._debug(\"EVA ICS WebEngine\", `version: ${this.version}`);\n if (typeof fetch === \"undefined\") {\n this.log.error(\n '\"fetch\" function is unavailable. Upgrade your web browser or ' +\n \"connect polyfill\"\n );\n return;\n }\n if (this.logged_in) {\n this._debug(\"start\", \"already logged in\");\n return;\n }\n if (this.wasm && !this.evajw) {\n this._start_evajw();\n } else {\n this._start_engine();\n }\n }\n _start_engine() {\n this._push_event_topic(EventTopic.WeSession, {\n login: LoginState.Starting,\n error: null,\n otp: null\n });\n this._clear_last_pings();\n let q: LoginPayload = {};\n if (this.#apikey) {\n q = { k: this.#apikey };\n if (this.login_xopts) {\n q.xopts = this.login_xopts;\n }\n this._debug(\"start\", \"logging in with API key\");\n } else if (this.api_token) {\n q = { a: this.api_token };\n this._debug(\"start\", \"logging in with existing auth token\");\n } else if (this.#password && !this.ignore_password_set_on_next_login) {\n q = { u: this.login, p: this.#password };\n if (this.api_token) {\n q.a = this.api_token;\n }\n if (this.login_xopts) {\n q.xopts = this.login_xopts;\n }\n this._debug(\"start\", \"logging in with password\");\n } else if (this.set_auth_cookies) {\n let token = cookies.read(\"auth\");\n if (token) {\n q = { a: token };\n this._debug(\"start\", \"logging in with cookie-cached auth token\");\n }\n }\n if (Object.keys(q).length === 0) {\n this._debug(\"start\", \"logging in without credentials\");\n }\n let user: string;\n this.ignore_password_set_on_next_login = false;\n this._api_call(\"login\", q)\n .then((data) => {\n this.api_token = data.token;\n user = data.user;\n this._set_token_cookie();\n //if (!this.api_version) {\n //if (data.api_version) {\n //this.api_version = data.api_version;\n //} else {\n //this.api_version = 4;\n //}\n //}\n //if (this.evajw) {\n //this.evajw.set_api_version(data.api_version || 4);\n //}\n return Promise.all([\n this._load_states(this.state_updates, GLOBAL_BLOCK_NAME),\n this._heartbeat(true),\n this._start_ws(this.state_updates, GLOBAL_BLOCK_NAME)\n ]);\n })\n .then(() => {\n if (!this.ws_mode) {\n if (this._ajax_reloader) {\n clearInterval(this._ajax_reloader);\n }\n this._ajax_reloader = setInterval(\n () => {\n this._load_states(this.state_updates, GLOBAL_BLOCK_NAME).catch(\n () => {}\n );\n },\n (this._intervals.get(IntervalKind.AjaxReload) as number) * 1000\n );\n } else {\n if (this._ajax_reloader) {\n clearInterval(this._ajax_reloader);\n }\n let reload = this._intervals.get(IntervalKind.Reload) as number;\n if (reload) {\n this._ajax_reloader = setInterval(() => {\n this._load_states(this.state_updates, GLOBAL_BLOCK_NAME).catch(\n () => {}\n );\n }, reload * 1000);\n }\n }\n if (this._heartbeat_reloader) {\n clearInterval(this._heartbeat_reloader);\n }\n this._heartbeat_reloader = setInterval(\n () => {\n this._heartbeat(false).catch(() => {});\n },\n (this._intervals.get(IntervalKind.Heartbeat) as number) * 1000\n );\n this._debug(\"start\", `login successful, user: ${user}`);\n this.logged_in = true;\n this.authorized_user = user;\n this._invoke_handler(EventKind.LoginSuccess);\n this._push_event_topic(EventTopic.WeSession, {\n login: LoginState.Active,\n error: null,\n otp: null\n });\n for (let [_, block] of this._blocks) {\n block._restart();\n }\n })\n .catch((err) => {\n this._debug(\"start\", err);\n this.logged_in = false;\n if (err?.code === undefined) {\n err = new EvaError(EvaErrorKind.OTHER, \"Unknown error\");\n }\n this._debug(\"start\", `login failed: ${err.code} (${err.message})`);\n this._stop_engine();\n if (\n err.code != EvaErrorKind.CORE_ERROR ||\n err.message != \"Server error\"\n ) {\n this.erase_token_cookie();\n }\n this.error_handler(err, \"login\");\n });\n return true;\n }\n\n /**\n * Get system name\n *\n * @returns the system name or null if the engine is not logged in\n */\n system_name() {\n if (this.server_info) {\n return this.server_info.system_name;\n } else {\n return null;\n }\n }\n /**\n * Sleep the number of seconds\n *\n * @param sec {number} seconds to sleep\n */\n async sleep(sec: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, sec * 1000));\n }\n\n /**\n * Start log processing\n *\n * Starts log processing. The engine must be already logged in.\n *\n * @param log_level {number} log processing level (optional)\n */\n log_start(log_level?: number) {\n this._log_started = true;\n if (log_level !== undefined) {\n this.log_params.level = log_level;\n }\n if (!this.ws_mode || this._log_first_load) {\n this._log_loaded = false;\n this._load_log_entries(true);\n if (!this.ws_mode) {\n this._log_reloader = setInterval(\n () => {\n this._load_log_entries(false);\n },\n (this._intervals.get(IntervalKind.AjaxLogReload) as number) * 1000\n );\n }\n }\n }\n\n /**\n * Set state updates without restart required\n *\n * @param state_updates {boolean} true/false or a string array\n * @param clear_existing {boolean} clear existing states\n *\n */\n async set_state_updates(\n state_updates: Array<string> | boolean,\n clear_existing?: boolean\n ) {\n check_state_updates(state_updates);\n this.state_updates = state_updates;\n this._push_event_topic(EventTopic.WeItemState, state_updates);\n let ws = this.ws.get(GLOBAL_BLOCK_NAME);\n if (ws && ws.readyState === 1) {\n let st: WsCommand = { m: \"unsubscribe.state\" };\n ws.send(JSON.stringify(st));\n ws.send(\"\");\n if (this.state_updates) {\n let st: WsCommand = { m: \"subscribe.state\" };\n