iobroker.beszel 0.2.4 → 0.2.6

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 CHANGED
@@ -122,6 +122,12 @@ beszel.0.
122
122
 
123
123
  ## Changelog
124
124
 
125
+ ### 0.2.6 (2026-04-08)
126
+ - Use `node:` prefix for built-in modules (http, https, url)
127
+
128
+ ### 0.2.5 (2026-04-08)
129
+ - Restore standard GitHub-based tests, remove CHANGELOG.md, add FORBIDDEN_CHARS reference
130
+
125
131
  ### 0.2.4 (2026-04-05)
126
132
  - Cleaner log messages, remove redundant adapter name prefix
127
133
 
@@ -131,19 +137,7 @@ beszel.0.
131
137
  ### 0.2.2 (2026-04-03)
132
138
  - Modernize dev tooling (esbuild, TypeScript 5.9 pin, testing-action-check v2)
133
139
 
134
- ### 0.2.1 (2026-03-28)
135
- - Error deduplication, auth backoff, protect against empty system list deletion
136
-
137
- ### 0.2.0 (2026-03-28)
138
- - Adapter timer methods, synchronous onUnload, merge About tab, Windows/macOS CI, full MIT license
139
-
140
- ### 0.1.9 (2026-03-19)
141
- - Logging cleanup: stale system removal moved to debug level
142
-
143
- ### 0.1.8 (2026-03-19)
144
- - Add online/offline indicator to system device folders
145
-
146
- Older changelog: [CHANGELOG.md](CHANGELOG.md)
140
+ Older changelog: [CHANGELOG_OLD.md](CHANGELOG_OLD.md)
147
141
 
148
142
  ---
149
143
 
@@ -31,9 +31,9 @@ __export(beszel_client_exports, {
31
31
  BeszelClient: () => BeszelClient
32
32
  });
33
33
  module.exports = __toCommonJS(beszel_client_exports);
34
- var http = __toESM(require("http"));
35
- var https = __toESM(require("https"));
36
- var import_url = require("url");
34
+ var http = __toESM(require("node:http"));
35
+ var https = __toESM(require("node:https"));
36
+ var import_node_url = require("node:url");
37
37
  const TOKEN_REFRESH_MS = 23 * 60 * 60 * 1e3;
38
38
  class BeszelClient {
39
39
  baseUrl;
@@ -142,7 +142,7 @@ class BeszelClient {
142
142
  return new Promise((resolve, reject) => {
143
143
  let parsedUrl;
144
144
  try {
145
- parsedUrl = new import_url.URL(this.baseUrl + path);
145
+ parsedUrl = new import_node_url.URL(this.baseUrl + path);
146
146
  } catch {
147
147
  reject(new Error(`Invalid URL: ${this.baseUrl + path}`));
148
148
  return;
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/lib/beszel-client.ts"],
4
- "sourcesContent": ["import * as http from \"http\";\nimport * as https from \"https\";\nimport { URL } from \"url\";\nimport type {\n AuthResponse,\n BeszelContainer,\n BeszelSystem,\n BeszelSystemStats,\n PocketBaseList,\n SystemStats,\n} from \"./types.js\";\n\nconst TOKEN_REFRESH_MS = 23 * 60 * 60 * 1000; // 23 hours\n\n/**\n * HTTP client for the Beszel PocketBase REST API.\n * Uses only Node.js built-in http/https \u2014 no extra dependencies.\n */\nexport class BeszelClient {\n private readonly baseUrl: string;\n private readonly username: string;\n private readonly password: string;\n\n private token: string | null = null;\n private tokenTime = 0;\n\n /**\n * @param url Beszel Hub base URL, e.g. http://192.168.1.100:8090\n * @param username Login username\n * @param password Login password\n */\n constructor(url: string, username: string, password: string) {\n // Strip trailing slash\n this.baseUrl = url.replace(/\\/+$/, \"\");\n this.username = username;\n this.password = password;\n }\n\n /** Force token re-authentication on the next request */\n public invalidateToken(): void {\n this.token = null;\n this.tokenTime = 0;\n }\n\n /**\n * Test the connection to Beszel.\n * Returns { success: true } or { success: false, message: reason }.\n */\n public async checkConnection(): Promise<{\n success: boolean;\n message: string;\n }> {\n try {\n this.invalidateToken();\n await this.authenticate();\n return { success: true, message: \"Connected successfully\" };\n } catch (err) {\n return {\n success: false,\n message: err instanceof Error ? err.message : String(err),\n };\n }\n }\n\n /** Fetch all systems */\n public async getSystems(): Promise<BeszelSystem[]> {\n await this.ensureToken();\n const data = await this.fetchJson<PocketBaseList<BeszelSystem>>(\n \"/api/collections/systems/records?perPage=200&sort=name\",\n );\n return data.items;\n }\n\n /**\n * Fetch the latest 1m stats per system.\n * Returns a Map<systemId, SystemStats>.\n *\n * @param systemIds List of system IDs to fetch stats for\n */\n public async getLatestStats(\n systemIds: string[],\n ): Promise<Map<string, SystemStats>> {\n if (systemIds.length === 0) {\n return new Map();\n }\n await this.ensureToken();\n const data = await this.fetchJson<PocketBaseList<BeszelSystemStats>>(\n \"/api/collections/system_stats/records?sort=-updated&perPage=200&filter=type%3D'1m'\",\n );\n\n // Deduplicate: keep the newest record per system\n const result = new Map<string, SystemStats>();\n for (const record of data.items) {\n if (!result.has(record.system)) {\n result.set(record.system, record.stats);\n }\n }\n return result;\n }\n\n /** Fetch all containers */\n public async getContainers(): Promise<BeszelContainer[]> {\n await this.ensureToken();\n const data = await this.fetchJson<PocketBaseList<BeszelContainer>>(\n \"/api/collections/containers/records?perPage=500&sort=system%2Cname\",\n );\n return data.items;\n }\n\n // -------------------------------------------------------------------------\n // Private helpers\n // -------------------------------------------------------------------------\n\n private async ensureToken(): Promise<void> {\n const now = Date.now();\n if (this.token && now - this.tokenTime < TOKEN_REFRESH_MS) {\n return;\n }\n await this.authenticate();\n }\n\n private async authenticate(): Promise<void> {\n const body = JSON.stringify({\n identity: this.username,\n password: this.password,\n });\n\n const data = await this.request<AuthResponse>(\n \"POST\",\n \"/api/collections/users/auth-with-password\",\n body,\n null, // no auth token yet\n );\n\n this.token = data.token;\n this.tokenTime = Date.now();\n }\n\n private async fetchJson<T>(path: string): Promise<T> {\n return this.request<T>(\"GET\", path, null, this.token);\n }\n\n private request<T>(\n method: string,\n path: string,\n body: string | null,\n token: string | null,\n ): Promise<T> {\n return new Promise((resolve, reject) => {\n let parsedUrl: URL;\n try {\n parsedUrl = new URL(this.baseUrl + path);\n } catch {\n reject(new Error(`Invalid URL: ${this.baseUrl + path}`));\n return;\n }\n\n const isHttps = parsedUrl.protocol === \"https:\";\n const transport = isHttps ? https : http;\n\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n Accept: \"application/json\",\n };\n if (token) {\n headers.Authorization = token;\n }\n if (body !== null) {\n headers[\"Content-Length\"] = Buffer.byteLength(body).toString();\n }\n\n const options: http.RequestOptions = {\n hostname: parsedUrl.hostname,\n port: parsedUrl.port || (isHttps ? 443 : 80),\n path: parsedUrl.pathname + parsedUrl.search,\n method,\n headers,\n timeout: 15000,\n };\n\n const req = transport.request(options, (res) => {\n const chunks: Buffer[] = [];\n res.on(\"data\", (chunk: Buffer) => chunks.push(chunk));\n res.on(\"end\", () => {\n const raw = Buffer.concat(chunks).toString(\"utf8\");\n if (\n !res.statusCode ||\n res.statusCode < 200 ||\n res.statusCode >= 300\n ) {\n // Propagate 401 specifically so caller can re-auth\n const err = new Error(\n `HTTP ${res.statusCode ?? \"?\"}: ${raw.slice(0, 200)}`,\n );\n (err as NodeJS.ErrnoException).code =\n res.statusCode === 401 ? \"UNAUTHORIZED\" : \"HTTP_ERROR\";\n reject(err);\n return;\n }\n try {\n resolve(JSON.parse(raw) as T);\n } catch {\n reject(new Error(`Invalid JSON response from ${path}`));\n }\n });\n });\n\n req.on(\"timeout\", () => {\n req.destroy();\n reject(new Error(`Request to ${path} timed out`));\n });\n\n req.on(\"error\", (err) => reject(err));\n\n if (body !== null) {\n req.write(body);\n }\n req.end();\n });\n }\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import * as http from \"node:http\";\nimport * as https from \"node:https\";\nimport { URL } from \"node:url\";\nimport type {\n AuthResponse,\n BeszelContainer,\n BeszelSystem,\n BeszelSystemStats,\n PocketBaseList,\n SystemStats,\n} from \"./types.js\";\n\nconst TOKEN_REFRESH_MS = 23 * 60 * 60 * 1000; // 23 hours\n\n/**\n * HTTP client for the Beszel PocketBase REST API.\n * Uses only Node.js built-in http/https \u2014 no extra dependencies.\n */\nexport class BeszelClient {\n private readonly baseUrl: string;\n private readonly username: string;\n private readonly password: string;\n\n private token: string | null = null;\n private tokenTime = 0;\n\n /**\n * @param url Beszel Hub base URL, e.g. http://192.168.1.100:8090\n * @param username Login username\n * @param password Login password\n */\n constructor(url: string, username: string, password: string) {\n // Strip trailing slash\n this.baseUrl = url.replace(/\\/+$/, \"\");\n this.username = username;\n this.password = password;\n }\n\n /** Force token re-authentication on the next request */\n public invalidateToken(): void {\n this.token = null;\n this.tokenTime = 0;\n }\n\n /**\n * Test the connection to Beszel.\n * Returns { success: true } or { success: false, message: reason }.\n */\n public async checkConnection(): Promise<{\n success: boolean;\n message: string;\n }> {\n try {\n this.invalidateToken();\n await this.authenticate();\n return { success: true, message: \"Connected successfully\" };\n } catch (err) {\n return {\n success: false,\n message: err instanceof Error ? err.message : String(err),\n };\n }\n }\n\n /** Fetch all systems */\n public async getSystems(): Promise<BeszelSystem[]> {\n await this.ensureToken();\n const data = await this.fetchJson<PocketBaseList<BeszelSystem>>(\n \"/api/collections/systems/records?perPage=200&sort=name\",\n );\n return data.items;\n }\n\n /**\n * Fetch the latest 1m stats per system.\n * Returns a Map<systemId, SystemStats>.\n *\n * @param systemIds List of system IDs to fetch stats for\n */\n public async getLatestStats(\n systemIds: string[],\n ): Promise<Map<string, SystemStats>> {\n if (systemIds.length === 0) {\n return new Map();\n }\n await this.ensureToken();\n const data = await this.fetchJson<PocketBaseList<BeszelSystemStats>>(\n \"/api/collections/system_stats/records?sort=-updated&perPage=200&filter=type%3D'1m'\",\n );\n\n // Deduplicate: keep the newest record per system\n const result = new Map<string, SystemStats>();\n for (const record of data.items) {\n if (!result.has(record.system)) {\n result.set(record.system, record.stats);\n }\n }\n return result;\n }\n\n /** Fetch all containers */\n public async getContainers(): Promise<BeszelContainer[]> {\n await this.ensureToken();\n const data = await this.fetchJson<PocketBaseList<BeszelContainer>>(\n \"/api/collections/containers/records?perPage=500&sort=system%2Cname\",\n );\n return data.items;\n }\n\n // -------------------------------------------------------------------------\n // Private helpers\n // -------------------------------------------------------------------------\n\n private async ensureToken(): Promise<void> {\n const now = Date.now();\n if (this.token && now - this.tokenTime < TOKEN_REFRESH_MS) {\n return;\n }\n await this.authenticate();\n }\n\n private async authenticate(): Promise<void> {\n const body = JSON.stringify({\n identity: this.username,\n password: this.password,\n });\n\n const data = await this.request<AuthResponse>(\n \"POST\",\n \"/api/collections/users/auth-with-password\",\n body,\n null, // no auth token yet\n );\n\n this.token = data.token;\n this.tokenTime = Date.now();\n }\n\n private async fetchJson<T>(path: string): Promise<T> {\n return this.request<T>(\"GET\", path, null, this.token);\n }\n\n private request<T>(\n method: string,\n path: string,\n body: string | null,\n token: string | null,\n ): Promise<T> {\n return new Promise((resolve, reject) => {\n let parsedUrl: URL;\n try {\n parsedUrl = new URL(this.baseUrl + path);\n } catch {\n reject(new Error(`Invalid URL: ${this.baseUrl + path}`));\n return;\n }\n\n const isHttps = parsedUrl.protocol === \"https:\";\n const transport = isHttps ? https : http;\n\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n Accept: \"application/json\",\n };\n if (token) {\n headers.Authorization = token;\n }\n if (body !== null) {\n headers[\"Content-Length\"] = Buffer.byteLength(body).toString();\n }\n\n const options: http.RequestOptions = {\n hostname: parsedUrl.hostname,\n port: parsedUrl.port || (isHttps ? 443 : 80),\n path: parsedUrl.pathname + parsedUrl.search,\n method,\n headers,\n timeout: 15000,\n };\n\n const req = transport.request(options, (res) => {\n const chunks: Buffer[] = [];\n res.on(\"data\", (chunk: Buffer) => chunks.push(chunk));\n res.on(\"end\", () => {\n const raw = Buffer.concat(chunks).toString(\"utf8\");\n if (\n !res.statusCode ||\n res.statusCode < 200 ||\n res.statusCode >= 300\n ) {\n // Propagate 401 specifically so caller can re-auth\n const err = new Error(\n `HTTP ${res.statusCode ?? \"?\"}: ${raw.slice(0, 200)}`,\n );\n (err as NodeJS.ErrnoException).code =\n res.statusCode === 401 ? \"UNAUTHORIZED\" : \"HTTP_ERROR\";\n reject(err);\n return;\n }\n try {\n resolve(JSON.parse(raw) as T);\n } catch {\n reject(new Error(`Invalid JSON response from ${path}`));\n }\n });\n });\n\n req.on(\"timeout\", () => {\n req.destroy();\n reject(new Error(`Request to ${path} timed out`));\n });\n\n req.on(\"error\", (err) => reject(err));\n\n if (body !== null) {\n req.write(body);\n }\n req.end();\n });\n }\n}\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,WAAsB;AACtB,YAAuB;AACvB,sBAAoB;AAUpB,MAAM,mBAAmB,KAAK,KAAK,KAAK;AAMjC,MAAM,aAAa;AAAA,EACP;AAAA,EACA;AAAA,EACA;AAAA,EAET,QAAuB;AAAA,EACvB,YAAY;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOpB,YAAY,KAAa,UAAkB,UAAkB;AAE3D,SAAK,UAAU,IAAI,QAAQ,QAAQ,EAAE;AACrC,SAAK,WAAW;AAChB,SAAK,WAAW;AAAA,EAClB;AAAA;AAAA,EAGO,kBAAwB;AAC7B,SAAK,QAAQ;AACb,SAAK,YAAY;AAAA,EACnB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAa,kBAGV;AACD,QAAI;AACF,WAAK,gBAAgB;AACrB,YAAM,KAAK,aAAa;AACxB,aAAO,EAAE,SAAS,MAAM,SAAS,yBAAyB;AAAA,IAC5D,SAAS,KAAK;AACZ,aAAO;AAAA,QACL,SAAS;AAAA,QACT,SAAS,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAAA,MAC1D;AAAA,IACF;AAAA,EACF;AAAA;AAAA,EAGA,MAAa,aAAsC;AACjD,UAAM,KAAK,YAAY;AACvB,UAAM,OAAO,MAAM,KAAK;AAAA,MACtB;AAAA,IACF;AACA,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAa,eACX,WACmC;AACnC,QAAI,UAAU,WAAW,GAAG;AAC1B,aAAO,oBAAI,IAAI;AAAA,IACjB;AACA,UAAM,KAAK,YAAY;AACvB,UAAM,OAAO,MAAM,KAAK;AAAA,MACtB;AAAA,IACF;AAGA,UAAM,SAAS,oBAAI,IAAyB;AAC5C,eAAW,UAAU,KAAK,OAAO;AAC/B,UAAI,CAAC,OAAO,IAAI,OAAO,MAAM,GAAG;AAC9B,eAAO,IAAI,OAAO,QAAQ,OAAO,KAAK;AAAA,MACxC;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,MAAa,gBAA4C;AACvD,UAAM,KAAK,YAAY;AACvB,UAAM,OAAO,MAAM,KAAK;AAAA,MACtB;AAAA,IACF;AACA,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAMA,MAAc,cAA6B;AACzC,UAAM,MAAM,KAAK,IAAI;AACrB,QAAI,KAAK,SAAS,MAAM,KAAK,YAAY,kBAAkB;AACzD;AAAA,IACF;AACA,UAAM,KAAK,aAAa;AAAA,EAC1B;AAAA,EAEA,MAAc,eAA8B;AAC1C,UAAM,OAAO,KAAK,UAAU;AAAA,MAC1B,UAAU,KAAK;AAAA,MACf,UAAU,KAAK;AAAA,IACjB,CAAC;AAED,UAAM,OAAO,MAAM,KAAK;AAAA,MACtB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,IACF;AAEA,SAAK,QAAQ,KAAK;AAClB,SAAK,YAAY,KAAK,IAAI;AAAA,EAC5B;AAAA,EAEA,MAAc,UAAa,MAA0B;AACnD,WAAO,KAAK,QAAW,OAAO,MAAM,MAAM,KAAK,KAAK;AAAA,EACtD;AAAA,EAEQ,QACN,QACA,MACA,MACA,OACY;AACZ,WAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,UAAI;AACJ,UAAI;AACF,oBAAY,IAAI,oBAAI,KAAK,UAAU,IAAI;AAAA,MACzC,QAAQ;AACN,eAAO,IAAI,MAAM,gBAAgB,KAAK,UAAU,IAAI,EAAE,CAAC;AACvD;AAAA,MACF;AAEA,YAAM,UAAU,UAAU,aAAa;AACvC,YAAM,YAAY,UAAU,QAAQ;AAEpC,YAAM,UAAkC;AAAA,QACtC,gBAAgB;AAAA,QAChB,QAAQ;AAAA,MACV;AACA,UAAI,OAAO;AACT,gBAAQ,gBAAgB;AAAA,MAC1B;AACA,UAAI,SAAS,MAAM;AACjB,gBAAQ,gBAAgB,IAAI,OAAO,WAAW,IAAI,EAAE,SAAS;AAAA,MAC/D;AAEA,YAAM,UAA+B;AAAA,QACnC,UAAU,UAAU;AAAA,QACpB,MAAM,UAAU,SAAS,UAAU,MAAM;AAAA,QACzC,MAAM,UAAU,WAAW,UAAU;AAAA,QACrC;AAAA,QACA;AAAA,QACA,SAAS;AAAA,MACX;AAEA,YAAM,MAAM,UAAU,QAAQ,SAAS,CAAC,QAAQ;AAC9C,cAAM,SAAmB,CAAC;AAC1B,YAAI,GAAG,QAAQ,CAAC,UAAkB,OAAO,KAAK,KAAK,CAAC;AACpD,YAAI,GAAG,OAAO,MAAM;AAvL5B;AAwLU,gBAAM,MAAM,OAAO,OAAO,MAAM,EAAE,SAAS,MAAM;AACjD,cACE,CAAC,IAAI,cACL,IAAI,aAAa,OACjB,IAAI,cAAc,KAClB;AAEA,kBAAM,MAAM,IAAI;AAAA,cACd,SAAQ,SAAI,eAAJ,YAAkB,GAAG,KAAK,IAAI,MAAM,GAAG,GAAG,CAAC;AAAA,YACrD;AACA,YAAC,IAA8B,OAC7B,IAAI,eAAe,MAAM,iBAAiB;AAC5C,mBAAO,GAAG;AACV;AAAA,UACF;AACA,cAAI;AACF,oBAAQ,KAAK,MAAM,GAAG,CAAM;AAAA,UAC9B,QAAQ;AACN,mBAAO,IAAI,MAAM,8BAA8B,IAAI,EAAE,CAAC;AAAA,UACxD;AAAA,QACF,CAAC;AAAA,MACH,CAAC;AAED,UAAI,GAAG,WAAW,MAAM;AACtB,YAAI,QAAQ;AACZ,eAAO,IAAI,MAAM,cAAc,IAAI,YAAY,CAAC;AAAA,MAClD,CAAC;AAED,UAAI,GAAG,SAAS,CAAC,QAAQ,OAAO,GAAG,CAAC;AAEpC,UAAI,SAAS,MAAM;AACjB,YAAI,MAAM,IAAI;AAAA,MAChB;AACA,UAAI,IAAI;AAAA,IACV,CAAC;AAAA,EACH;AACF;",
6
6
  "names": []
7
7
  }
@@ -30,7 +30,7 @@ class StateManager {
30
30
  this.adapter = adapter;
31
31
  }
32
32
  /**
33
- * Sanitize a name to a valid ioBroker state ID segment.
33
+ * Sanitize a name to a valid ioBroker state ID segment (see adapter.FORBIDDEN_CHARS).
34
34
  * Lowercase, replace non-alphanumeric with _, max 50 chars, trim underscores.
35
35
  *
36
36
  * @param name Raw name to sanitize
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/lib/state-manager.ts"],
4
- "sourcesContent": ["import type * as utils from \"@iobroker/adapter-core\";\nimport type {\n AdapterConfig,\n BeszelContainer,\n BeszelSystem,\n SystemStats,\n} from \"./types.js\";\n\n/**\n * Manages creation, update and cleanup of ioBroker objects and states for Beszel systems.\n */\nexport class StateManager {\n private readonly adapter: utils.AdapterInstance;\n\n /**\n * @param adapter The ioBroker adapter instance\n */\n constructor(adapter: utils.AdapterInstance) {\n this.adapter = adapter;\n }\n\n /**\n * Sanitize a name to a valid ioBroker state ID segment.\n * Lowercase, replace non-alphanumeric with _, max 50 chars, trim underscores.\n *\n * @param name Raw name to sanitize\n */\n public sanitize(name: string): string {\n return name\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"_\")\n .replace(/^_+|_+$/g, \"\")\n .slice(0, 50);\n }\n\n /**\n * Update all states for a single system.\n *\n * @param system Beszel system record\n * @param stats Latest stats for this system, or undefined if unavailable\n * @param containers Container records belonging to this system\n * @param config Current adapter configuration\n */\n public async updateSystem(\n system: BeszelSystem,\n stats: SystemStats | undefined,\n containers: BeszelContainer[],\n config: AdapterConfig,\n ): Promise<void> {\n const sysId = `systems.${this.sanitize(system.name)}`;\n\n // Create/update device object with online indicator\n await this.adapter.extendObjectAsync(sysId, {\n type: \"device\",\n common: {\n name: system.name,\n statusStates: {\n onlineId: `${this.adapter.namespace}.${sysId}.online`,\n },\n } as ioBroker.DeviceCommon,\n native: { id: system.id, host: system.host },\n });\n\n // Always: online + status\n const isUp = system.status === \"up\";\n await this.createAndSetState(\n `${sysId}.online`,\n {\n name: \"Online\",\n type: \"boolean\",\n role: \"indicator.reachable\",\n read: true,\n write: false,\n } as ioBroker.StateCommon,\n isUp,\n );\n\n await this.createAndSetState(\n `${sysId}.status`,\n {\n name: \"Status\",\n type: \"string\",\n role: \"text\",\n read: true,\n write: false,\n } as ioBroker.StateCommon,\n system.status,\n );\n\n // Uptime\n if (config.metrics_uptime) {\n const uptime = system.info.u ?? null;\n\n await this.createAndSetState(\n `${sysId}.uptime`,\n {\n name: \"Uptime\",\n type: \"number\",\n role: \"value\",\n unit: \"s\",\n read: true,\n write: false,\n } as ioBroker.StateCommon,\n uptime,\n );\n\n await this.createAndSetState(\n `${sysId}.uptime_text`,\n {\n name: \"Uptime (formatted)\",\n type: \"string\",\n role: \"text\",\n read: true,\n write: false,\n } as ioBroker.StateCommon,\n uptime !== null ? this.formatUptime(uptime) : null,\n );\n }\n\n // Agent version\n if (config.metrics_agentVersion) {\n await this.createAndSetState(\n `${sysId}.agent_version`,\n {\n name: \"Agent Version\",\n type: \"string\",\n role: \"text\",\n read: true,\n write: false,\n } as ioBroker.StateCommon,\n system.info.v ?? null,\n );\n }\n\n // Systemd services\n if (config.metrics_services) {\n const sv = system.info.sv;\n await this.createAndSetState(\n `${sysId}.services_total`,\n {\n name: \"Services Total\",\n type: \"number\",\n role: \"value\",\n read: true,\n write: false,\n } as ioBroker.StateCommon,\n sv?.[0] ?? null,\n );\n\n await this.createAndSetState(\n `${sysId}.services_failed`,\n {\n name: \"Services Failed\",\n type: \"number\",\n role: \"value.warning\",\n read: true,\n write: false,\n } as ioBroker.StateCommon,\n sv?.[1] ?? null,\n );\n }\n\n // Stats-based metrics (only if stats available)\n if (stats) {\n await this.updateStatsStates(sysId, system, stats, config);\n }\n\n // Load avg fallback to system.info.la if no stats\n if (config.metrics_loadAvg && !stats) {\n await this.createLoadAvgStates(sysId, system.info.la);\n }\n\n // Containers\n if (config.metrics_containers) {\n await this.updateContainers(sysId, system.id, containers);\n } else {\n await this.deleteChannelIfExists(`${sysId}.containers`);\n }\n }\n\n /**\n * Remove device objects for systems that are no longer in Beszel.\n *\n * @param activeSystemNames Sanitized names of currently active systems\n */\n public async cleanupSystems(activeSystemNames: string[]): Promise<void> {\n const activeIds = new Set(\n activeSystemNames.map((n) => `systems.${this.sanitize(n)}`),\n );\n\n const objects = await this.adapter.getObjectViewAsync(\"system\", \"device\", {\n startkey: `${this.adapter.namespace}.systems.`,\n endkey: `${this.adapter.namespace}.systems.\\u9999`,\n });\n\n if (!objects?.rows) {\n return;\n }\n\n for (const row of objects.rows) {\n const id = row.id;\n // Extract the relative id part\n const relativeId = id.startsWith(`${this.adapter.namespace}.`)\n ? id.slice(this.adapter.namespace.length + 1)\n : id;\n\n // Only delete direct children of \"systems.\" (one level deep)\n const parts = relativeId.split(\".\");\n if (\n parts.length === 2 &&\n parts[0] === \"systems\" &&\n !activeIds.has(relativeId)\n ) {\n this.adapter.log.debug(`Removing stale system: ${relativeId}`);\n await this.adapter.delObjectAsync(relativeId, { recursive: true });\n }\n }\n }\n\n /**\n * Delete states for metrics that have been disabled in the config.\n * Called on startup to clean up previously-enabled states.\n *\n * @param systemId Sanitized system name (the part after \"systems.\")\n * @param config Current adapter configuration\n */\n public async cleanupMetrics(\n systemId: string,\n config: AdapterConfig,\n ): Promise<void> {\n const sysId = `systems.${systemId}`;\n const toDelete: string[] = [];\n\n if (!config.metrics_uptime) {\n toDelete.push(`${sysId}.uptime`, `${sysId}.uptime_text`);\n }\n if (!config.metrics_agentVersion) {\n toDelete.push(`${sysId}.agent_version`);\n }\n if (!config.metrics_services) {\n toDelete.push(`${sysId}.services_total`, `${sysId}.services_failed`);\n }\n if (!config.metrics_cpu) {\n toDelete.push(`${sysId}.cpu_usage`);\n }\n if (!config.metrics_loadAvg) {\n toDelete.push(\n `${sysId}.load_avg_1m`,\n `${sysId}.load_avg_5m`,\n `${sysId}.load_avg_15m`,\n );\n }\n if (!config.metrics_cpuBreakdown) {\n toDelete.push(\n `${sysId}.cpu_user`,\n `${sysId}.cpu_system`,\n `${sysId}.cpu_iowait`,\n `${sysId}.cpu_steal`,\n `${sysId}.cpu_idle`,\n );\n }\n if (!config.metrics_memory) {\n toDelete.push(\n `${sysId}.memory_percent`,\n `${sysId}.memory_used`,\n `${sysId}.memory_total`,\n );\n }\n if (!config.metrics_memoryDetails) {\n toDelete.push(`${sysId}.memory_buffers`, `${sysId}.memory_zfs_arc`);\n }\n if (!config.metrics_swap) {\n toDelete.push(`${sysId}.swap_used`, `${sysId}.swap_total`);\n }\n if (!config.metrics_disk) {\n toDelete.push(\n `${sysId}.disk_percent`,\n `${sysId}.disk_used`,\n `${sysId}.disk_total`,\n );\n }\n if (!config.metrics_diskSpeed) {\n toDelete.push(`${sysId}.disk_read`, `${sysId}.disk_write`);\n }\n if (!config.metrics_network) {\n toDelete.push(`${sysId}.network_sent`, `${sysId}.network_recv`);\n }\n if (!config.metrics_temperature) {\n toDelete.push(`${sysId}.temperature`);\n }\n if (!config.metrics_battery) {\n toDelete.push(`${sysId}.battery_percent`, `${sysId}.battery_charging`);\n }\n\n for (const id of toDelete) {\n const obj = await this.adapter.getObjectAsync(id);\n if (obj) {\n await this.adapter.delObjectAsync(id);\n }\n }\n\n // Channels\n if (!config.metrics_temperatureDetails) {\n await this.deleteChannelIfExists(`${sysId}.temperatures`);\n }\n if (!config.metrics_gpu) {\n await this.deleteChannelIfExists(`${sysId}.gpu`);\n }\n if (!config.metrics_extraFs) {\n await this.deleteChannelIfExists(`${sysId}.filesystems`);\n }\n if (!config.metrics_containers) {\n await this.deleteChannelIfExists(`${sysId}.containers`);\n }\n }\n\n // -------------------------------------------------------------------------\n // Private helpers\n // -------------------------------------------------------------------------\n\n private async updateStatsStates(\n sysId: string,\n system: BeszelSystem,\n stats: SystemStats,\n config: AdapterConfig,\n ): Promise<void> {\n // CPU\n if (config.metrics_cpu) {\n await this.createAndSetState(\n `${sysId}.cpu_usage`,\n {\n name: \"CPU Usage\",\n type: \"number\",\n role: \"level\",\n unit: \"%\",\n min: 0,\n max: 100,\n read: true,\n write: false,\n } as ioBroker.StateCommon,\n stats.cpu ?? null,\n );\n }\n\n // Load avg \u2014 prefer stats.la, fallback to system.info.la\n if (config.metrics_loadAvg) {\n await this.createLoadAvgStates(sysId, stats.la ?? system.info.la);\n }\n\n // CPU breakdown\n if (config.metrics_cpuBreakdown && stats.cpub && stats.cpub.length >= 5) {\n const [user, sys, iowait, steal, idle] = stats.cpub;\n await this.createAndSetState(\n `${sysId}.cpu_user`,\n {\n name: \"CPU User %\",\n type: \"number\",\n role: \"level\",\n unit: \"%\",\n min: 0,\n max: 100,\n read: true,\n write: false,\n } as ioBroker.StateCommon,\n user,\n );\n await this.createAndSetState(\n `${sysId}.cpu_system`,\n {\n name: \"CPU System %\",\n type: \"number\",\n role: \"level\",\n unit: \"%\",\n min: 0,\n max: 100,\n read: true,\n write: false,\n } as ioBroker.StateCommon,\n sys,\n );\n await this.createAndSetState(\n `${sysId}.cpu_iowait`,\n {\n name: \"CPU IOWait %\",\n type: \"number\",\n role: \"level\",\n unit: \"%\",\n min: 0,\n max: 100,\n read: true,\n write: false,\n } as ioBroker.StateCommon,\n iowait,\n );\n await this.createAndSetState(\n `${sysId}.cpu_steal`,\n {\n name: \"CPU Steal %\",\n type: \"number\",\n role: \"level\",\n unit: \"%\",\n min: 0,\n max: 100,\n read: true,\n write: false,\n } as ioBroker.StateCommon,\n steal,\n );\n await this.createAndSetState(\n `${sysId}.cpu_idle`,\n {\n name: \"CPU Idle %\",\n type: \"number\",\n role: \"level\",\n unit: \"%\",\n min: 0,\n max: 100,\n read: true,\n write: false,\n } as ioBroker.StateCommon,\n idle,\n );\n }\n\n // Memory\n if (config.metrics_memory) {\n await this.createAndSetState(\n `${sysId}.memory_percent`,\n {\n name: \"Memory %\",\n type: \"number\",\n role: \"level\",\n unit: \"%\",\n min: 0,\n max: 100,\n read: true,\n write: false,\n } as ioBroker.StateCommon,\n stats.mp ?? null,\n );\n await this.createAndSetState(\n `${sysId}.memory_used`,\n {\n name: \"Memory Used\",\n type: \"number\",\n role: \"value\",\n unit: \"GB\",\n read: true,\n write: false,\n } as ioBroker.StateCommon,\n stats.mu ?? null,\n );\n await this.createAndSetState(\n `${sysId}.memory_total`,\n {\n name: \"Memory Total\",\n type: \"number\",\n role: \"value\",\n unit: \"GB\",\n read: true,\n write: false,\n } as ioBroker.StateCommon,\n stats.m ?? null,\n );\n }\n\n // Memory details\n if (config.metrics_memoryDetails) {\n await this.createAndSetState(\n `${sysId}.memory_buffers`,\n {\n name: \"Memory Buffers+Cache\",\n type: \"number\",\n role: \"value\",\n unit: \"GB\",\n read: true,\n write: false,\n } as ioBroker.StateCommon,\n stats.mb ?? null,\n );\n await this.createAndSetState(\n `${sysId}.memory_zfs_arc`,\n {\n name: \"Memory ZFS ARC\",\n type: \"number\",\n role: \"value\",\n unit: \"GB\",\n read: true,\n write: false,\n } as ioBroker.StateCommon,\n stats.mz ?? null,\n );\n }\n\n // Swap\n if (config.metrics_swap) {\n await this.createAndSetState(\n `${sysId}.swap_used`,\n {\n name: \"Swap Used\",\n type: \"number\",\n role: \"value\",\n unit: \"GB\",\n read: true,\n write: false,\n } as ioBroker.StateCommon,\n stats.su ?? null,\n );\n await this.createAndSetState(\n `${sysId}.swap_total`,\n {\n name: \"Swap Total\",\n type: \"number\",\n role: \"value\",\n unit: \"GB\",\n read: true,\n write: false,\n } as ioBroker.StateCommon,\n stats.s ?? null,\n );\n }\n\n // Disk\n if (config.metrics_disk) {\n await this.createAndSetState(\n `${sysId}.disk_percent`,\n {\n name: \"Disk %\",\n type: \"number\",\n role: \"level\",\n unit: \"%\",\n min: 0,\n max: 100,\n read: true,\n write: false,\n } as ioBroker.StateCommon,\n stats.dp ?? null,\n );\n await this.createAndSetState(\n `${sysId}.disk_used`,\n {\n name: \"Disk Used\",\n type: \"number\",\n role: \"value\",\n unit: \"GB\",\n read: true,\n write: false,\n } as ioBroker.StateCommon,\n stats.du ?? null,\n );\n await this.createAndSetState(\n `${sysId}.disk_total`,\n {\n name: \"Disk Total\",\n type: \"number\",\n role: \"value\",\n unit: \"GB\",\n read: true,\n write: false,\n } as ioBroker.StateCommon,\n stats.d ?? null,\n );\n }\n\n // Disk speed\n if (config.metrics_diskSpeed) {\n await this.createAndSetState(\n `${sysId}.disk_read`,\n {\n name: \"Disk Read\",\n type: \"number\",\n role: \"value\",\n unit: \"MB/s\",\n read: true,\n write: false,\n } as ioBroker.StateCommon,\n stats.dr ?? null,\n );\n await this.createAndSetState(\n `${sysId}.disk_write`,\n {\n name: \"Disk Write\",\n type: \"number\",\n role: \"value\",\n unit: \"MB/s\",\n read: true,\n write: false,\n } as ioBroker.StateCommon,\n stats.dw ?? null,\n );\n }\n\n // Network\n if (config.metrics_network) {\n await this.createAndSetState(\n `${sysId}.network_sent`,\n {\n name: \"Network Sent\",\n type: \"number\",\n role: \"value\",\n unit: \"MB/s\",\n read: true,\n write: false,\n } as ioBroker.StateCommon,\n stats.ns ?? null,\n );\n await this.createAndSetState(\n `${sysId}.network_recv`,\n {\n name: \"Network Received\",\n type: \"number\",\n role: \"value\",\n unit: \"MB/s\",\n read: true,\n write: false,\n } as ioBroker.StateCommon,\n stats.nr ?? null,\n );\n }\n\n // Temperature (average of top 3)\n if (config.metrics_temperature) {\n const avgTemp = this.computeTopAvgTemp(stats.t);\n await this.createAndSetState(\n `${sysId}.temperature`,\n {\n name: \"Temperature (avg top 3)\",\n type: \"number\",\n role: \"value.temperature\",\n unit: \"\u00B0C\",\n read: true,\n write: false,\n } as ioBroker.StateCommon,\n avgTemp,\n );\n }\n\n // Temperature details\n if (config.metrics_temperatureDetails && stats.t) {\n await this.ensureChannel(`${sysId}.temperatures`, \"Temperatures\");\n for (const [sensor, temp] of Object.entries(stats.t)) {\n const sensorId = this.sanitize(sensor);\n await this.createAndSetState(\n `${sysId}.temperatures.${sensorId}`,\n {\n name: sensor,\n type: \"number\",\n role: \"value.temperature\",\n unit: \"\u00B0C\",\n read: true,\n write: false,\n } as ioBroker.StateCommon,\n temp,\n );\n }\n } else if (!config.metrics_temperatureDetails) {\n await this.deleteChannelIfExists(`${sysId}.temperatures`);\n }\n\n // Battery\n if (config.metrics_battery) {\n const bat = stats.bat ?? system.info.bat;\n await this.createAndSetState(\n `${sysId}.battery_percent`,\n {\n name: \"Battery %\",\n type: \"number\",\n role: \"value\",\n unit: \"%\",\n min: 0,\n max: 100,\n read: true,\n write: false,\n } as ioBroker.StateCommon,\n bat?.[0] ?? null,\n );\n await this.createAndSetState(\n `${sysId}.battery_charging`,\n {\n name: \"Battery Charging\",\n type: \"boolean\",\n role: \"indicator\",\n read: true,\n write: false,\n } as ioBroker.StateCommon,\n bat ? bat[1] > 0 : null,\n );\n }\n\n // GPU\n if (config.metrics_gpu && stats.g && Object.keys(stats.g).length > 0) {\n await this.ensureChannel(`${sysId}.gpu`, \"GPU\");\n for (const [gpuId, gpuData] of Object.entries(stats.g)) {\n const safeId = this.sanitize(gpuId);\n const gpuLabel = gpuData.n ?? gpuId;\n await this.ensureChannel(`${sysId}.gpu.${safeId}`, gpuLabel);\n await this.createAndSetState(\n `${sysId}.gpu.${safeId}.usage`,\n {\n name: \"GPU Usage\",\n type: \"number\",\n role: \"level\",\n unit: \"%\",\n min: 0,\n max: 100,\n read: true,\n write: false,\n } as ioBroker.StateCommon,\n gpuData.u ?? null,\n );\n await this.createAndSetState(\n `${sysId}.gpu.${safeId}.memory_used`,\n {\n name: \"GPU Memory Used\",\n type: \"number\",\n role: \"value\",\n unit: \"GB\",\n read: true,\n write: false,\n } as ioBroker.StateCommon,\n gpuData.mu ?? null,\n );\n await this.createAndSetState(\n `${sysId}.gpu.${safeId}.memory_total`,\n {\n name: \"GPU Memory Total\",\n type: \"number\",\n role: \"value\",\n unit: \"GB\",\n read: true,\n write: false,\n } as ioBroker.StateCommon,\n gpuData.mt ?? null,\n );\n await this.createAndSetState(\n `${sysId}.gpu.${safeId}.power`,\n {\n name: \"GPU Power\",\n type: \"number\",\n role: \"value\",\n unit: \"W\",\n read: true,\n write: false,\n } as ioBroker.StateCommon,\n gpuData.p ?? null,\n );\n }\n } else if (!config.metrics_gpu) {\n await this.deleteChannelIfExists(`${sysId}.gpu`);\n }\n\n // Extra filesystems\n if (\n config.metrics_extraFs &&\n stats.efs &&\n Object.keys(stats.efs).length > 0\n ) {\n await this.ensureChannel(`${sysId}.filesystems`, \"Filesystems\");\n for (const [fsName, fsData] of Object.entries(stats.efs)) {\n const safeId = this.sanitize(fsName);\n await this.ensureChannel(`${sysId}.filesystems.${safeId}`, fsName);\n\n const total = fsData.d ?? null;\n const used = fsData.du ?? null;\n const percent =\n total !== null && used !== null && total > 0\n ? Math.round((used / total) * 100)\n : null;\n\n await this.createAndSetState(\n `${sysId}.filesystems.${safeId}.disk_percent`,\n {\n name: \"Disk %\",\n type: \"number\",\n role: \"level\",\n unit: \"%\",\n min: 0,\n max: 100,\n read: true,\n write: false,\n } as ioBroker.StateCommon,\n percent,\n );\n await this.createAndSetState(\n `${sysId}.filesystems.${safeId}.disk_used`,\n {\n name: \"Disk Used\",\n type: \"number\",\n role: \"value\",\n unit: \"GB\",\n read: true,\n write: false,\n } as ioBroker.StateCommon,\n used,\n );\n await this.createAndSetState(\n `${sysId}.filesystems.${safeId}.disk_total`,\n {\n name: \"Disk Total\",\n type: \"number\",\n role: \"value\",\n unit: \"GB\",\n read: true,\n write: false,\n } as ioBroker.StateCommon,\n total,\n );\n await this.createAndSetState(\n `${sysId}.filesystems.${safeId}.read_speed`,\n {\n name: \"Read Speed\",\n type: \"number\",\n role: \"value\",\n unit: \"MB/s\",\n read: true,\n write: false,\n } as ioBroker.StateCommon,\n fsData.r ?? null,\n );\n await this.createAndSetState(\n `${sysId}.filesystems.${safeId}.write_speed`,\n {\n name: \"Write Speed\",\n type: \"number\",\n role: \"value\",\n unit: \"MB/s\",\n read: true,\n write: false,\n } as ioBroker.StateCommon,\n fsData.w ?? null,\n );\n }\n } else if (!config.metrics_extraFs) {\n await this.deleteChannelIfExists(`${sysId}.filesystems`);\n }\n }\n\n private async updateContainers(\n sysId: string,\n systemId: string,\n allContainers: BeszelContainer[],\n ): Promise<void> {\n const sysContainers = allContainers.filter((c) => c.system === systemId);\n if (sysContainers.length === 0) {\n return;\n }\n\n await this.ensureChannel(`${sysId}.containers`, \"Containers\");\n\n const healthLabels = [\"none\", \"starting\", \"healthy\", \"unhealthy\"];\n\n for (const container of sysContainers) {\n const cId = this.sanitize(container.name);\n await this.ensureChannel(`${sysId}.containers.${cId}`, container.name);\n\n await this.createAndSetState(\n `${sysId}.containers.${cId}.status`,\n {\n name: \"Status\",\n type: \"string\",\n role: \"text\",\n read: true,\n write: false,\n } as ioBroker.StateCommon,\n container.status,\n );\n\n await this.createAndSetState(\n `${sysId}.containers.${cId}.health`,\n {\n name: \"Health\",\n type: \"string\",\n role: \"text\",\n read: true,\n write: false,\n } as ioBroker.StateCommon,\n healthLabels[container.health] ?? \"unknown\",\n );\n\n await this.createAndSetState(\n `${sysId}.containers.${cId}.cpu`,\n {\n name: \"CPU Usage\",\n type: \"number\",\n role: \"level\",\n unit: \"%\",\n min: 0,\n max: 100,\n read: true,\n write: false,\n } as ioBroker.StateCommon,\n container.cpu,\n );\n\n await this.createAndSetState(\n `${sysId}.containers.${cId}.memory`,\n {\n name: \"Memory\",\n type: \"number\",\n role: \"value\",\n unit: \"MB\",\n read: true,\n write: false,\n } as ioBroker.StateCommon,\n container.memory,\n );\n\n await this.createAndSetState(\n `${sysId}.containers.${cId}.image`,\n {\n name: \"Image\",\n type: \"string\",\n role: \"text\",\n read: true,\n write: false,\n } as ioBroker.StateCommon,\n container.image,\n );\n }\n }\n\n private async ensureChannel(id: string, name: string): Promise<void> {\n await this.adapter.setObjectNotExistsAsync(id, {\n type: \"channel\",\n common: { name } as ioBroker.ObjectCommon,\n native: {},\n });\n }\n\n private async deleteChannelIfExists(id: string): Promise<void> {\n try {\n const obj = await this.adapter.getObjectAsync(id);\n if (obj) {\n await this.adapter.delObjectAsync(id, { recursive: true });\n }\n } catch {\n // ignore\n }\n }\n\n /**\n * Create or update the three load average states.\n *\n * @param sysId - State ID prefix (e.g. \"systems.my_server\")\n * @param la - Load average tuple [1m, 5m, 15m], or undefined\n */\n private async createLoadAvgStates(\n sysId: string,\n la: [number, number, number] | undefined,\n ): Promise<void> {\n await this.createAndSetState(\n `${sysId}.load_avg_1m`,\n {\n name: \"Load Average 1m\",\n type: \"number\",\n role: \"value\",\n read: true,\n write: false,\n } as ioBroker.StateCommon,\n la?.[0] ?? null,\n );\n await this.createAndSetState(\n `${sysId}.load_avg_5m`,\n {\n name: \"Load Average 5m\",\n type: \"number\",\n role: \"value\",\n read: true,\n write: false,\n } as ioBroker.StateCommon,\n la?.[1] ?? null,\n );\n await this.createAndSetState(\n `${sysId}.load_avg_15m`,\n {\n name: \"Load Average 15m\",\n type: \"number\",\n role: \"value\",\n read: true,\n write: false,\n } as ioBroker.StateCommon,\n la?.[2] ?? null,\n );\n }\n\n private async createAndSetState(\n id: string,\n common: ioBroker.StateCommon,\n value: ioBroker.StateValue,\n ): Promise<void> {\n await this.adapter.setObjectNotExistsAsync(id, {\n type: \"state\",\n common,\n native: {},\n });\n await this.adapter.setStateAsync(id, { val: value, ack: true });\n }\n\n private computeTopAvgTemp(\n temps: Record<string, number> | undefined,\n ): number | null {\n if (!temps) {\n return null;\n }\n const values = Object.values(temps).filter(\n (v) => typeof v === \"number\" && isFinite(v),\n );\n if (values.length === 0) {\n return null;\n }\n values.sort((a, b) => b - a);\n const top3 = values.slice(0, 3);\n const avg = top3.reduce((sum, v) => sum + v, 0) / top3.length;\n return Math.round(avg * 10) / 10;\n }\n\n private formatUptime(seconds: number): string {\n const d = Math.floor(seconds / 86400);\n const h = Math.floor((seconds % 86400) / 3600);\n const m = Math.floor((seconds % 3600) / 60);\n const parts: string[] = [];\n if (d > 0) {\n parts.push(`${d}d`);\n }\n if (h > 0) {\n parts.push(`${h}h`);\n }\n if (m > 0 || parts.length === 0) {\n parts.push(`${m}m`);\n }\n return parts.join(\" \");\n }\n}\n"],
4
+ "sourcesContent": ["import type * as utils from \"@iobroker/adapter-core\";\nimport type {\n AdapterConfig,\n BeszelContainer,\n BeszelSystem,\n SystemStats,\n} from \"./types.js\";\n\n/**\n * Manages creation, update and cleanup of ioBroker objects and states for Beszel systems.\n */\nexport class StateManager {\n private readonly adapter: utils.AdapterInstance;\n\n /**\n * @param adapter The ioBroker adapter instance\n */\n constructor(adapter: utils.AdapterInstance) {\n this.adapter = adapter;\n }\n\n /**\n * Sanitize a name to a valid ioBroker state ID segment (see adapter.FORBIDDEN_CHARS).\n * Lowercase, replace non-alphanumeric with _, max 50 chars, trim underscores.\n *\n * @param name Raw name to sanitize\n */\n public sanitize(name: string): string {\n return name\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"_\")\n .replace(/^_+|_+$/g, \"\")\n .slice(0, 50);\n }\n\n /**\n * Update all states for a single system.\n *\n * @param system Beszel system record\n * @param stats Latest stats for this system, or undefined if unavailable\n * @param containers Container records belonging to this system\n * @param config Current adapter configuration\n */\n public async updateSystem(\n system: BeszelSystem,\n stats: SystemStats | undefined,\n containers: BeszelContainer[],\n config: AdapterConfig,\n ): Promise<void> {\n const sysId = `systems.${this.sanitize(system.name)}`;\n\n // Create/update device object with online indicator\n await this.adapter.extendObjectAsync(sysId, {\n type: \"device\",\n common: {\n name: system.name,\n statusStates: {\n onlineId: `${this.adapter.namespace}.${sysId}.online`,\n },\n } as ioBroker.DeviceCommon,\n native: { id: system.id, host: system.host },\n });\n\n // Always: online + status\n const isUp = system.status === \"up\";\n await this.createAndSetState(\n `${sysId}.online`,\n {\n name: \"Online\",\n type: \"boolean\",\n role: \"indicator.reachable\",\n read: true,\n write: false,\n } as ioBroker.StateCommon,\n isUp,\n );\n\n await this.createAndSetState(\n `${sysId}.status`,\n {\n name: \"Status\",\n type: \"string\",\n role: \"text\",\n read: true,\n write: false,\n } as ioBroker.StateCommon,\n system.status,\n );\n\n // Uptime\n if (config.metrics_uptime) {\n const uptime = system.info.u ?? null;\n\n await this.createAndSetState(\n `${sysId}.uptime`,\n {\n name: \"Uptime\",\n type: \"number\",\n role: \"value\",\n unit: \"s\",\n read: true,\n write: false,\n } as ioBroker.StateCommon,\n uptime,\n );\n\n await this.createAndSetState(\n `${sysId}.uptime_text`,\n {\n name: \"Uptime (formatted)\",\n type: \"string\",\n role: \"text\",\n read: true,\n write: false,\n } as ioBroker.StateCommon,\n uptime !== null ? this.formatUptime(uptime) : null,\n );\n }\n\n // Agent version\n if (config.metrics_agentVersion) {\n await this.createAndSetState(\n `${sysId}.agent_version`,\n {\n name: \"Agent Version\",\n type: \"string\",\n role: \"text\",\n read: true,\n write: false,\n } as ioBroker.StateCommon,\n system.info.v ?? null,\n );\n }\n\n // Systemd services\n if (config.metrics_services) {\n const sv = system.info.sv;\n await this.createAndSetState(\n `${sysId}.services_total`,\n {\n name: \"Services Total\",\n type: \"number\",\n role: \"value\",\n read: true,\n write: false,\n } as ioBroker.StateCommon,\n sv?.[0] ?? null,\n );\n\n await this.createAndSetState(\n `${sysId}.services_failed`,\n {\n name: \"Services Failed\",\n type: \"number\",\n role: \"value.warning\",\n read: true,\n write: false,\n } as ioBroker.StateCommon,\n sv?.[1] ?? null,\n );\n }\n\n // Stats-based metrics (only if stats available)\n if (stats) {\n await this.updateStatsStates(sysId, system, stats, config);\n }\n\n // Load avg fallback to system.info.la if no stats\n if (config.metrics_loadAvg && !stats) {\n await this.createLoadAvgStates(sysId, system.info.la);\n }\n\n // Containers\n if (config.metrics_containers) {\n await this.updateContainers(sysId, system.id, containers);\n } else {\n await this.deleteChannelIfExists(`${sysId}.containers`);\n }\n }\n\n /**\n * Remove device objects for systems that are no longer in Beszel.\n *\n * @param activeSystemNames Sanitized names of currently active systems\n */\n public async cleanupSystems(activeSystemNames: string[]): Promise<void> {\n const activeIds = new Set(\n activeSystemNames.map((n) => `systems.${this.sanitize(n)}`),\n );\n\n const objects = await this.adapter.getObjectViewAsync(\"system\", \"device\", {\n startkey: `${this.adapter.namespace}.systems.`,\n endkey: `${this.adapter.namespace}.systems.\\u9999`,\n });\n\n if (!objects?.rows) {\n return;\n }\n\n for (const row of objects.rows) {\n const id = row.id;\n // Extract the relative id part\n const relativeId = id.startsWith(`${this.adapter.namespace}.`)\n ? id.slice(this.adapter.namespace.length + 1)\n : id;\n\n // Only delete direct children of \"systems.\" (one level deep)\n const parts = relativeId.split(\".\");\n if (\n parts.length === 2 &&\n parts[0] === \"systems\" &&\n !activeIds.has(relativeId)\n ) {\n this.adapter.log.debug(`Removing stale system: ${relativeId}`);\n await this.adapter.delObjectAsync(relativeId, { recursive: true });\n }\n }\n }\n\n /**\n * Delete states for metrics that have been disabled in the config.\n * Called on startup to clean up previously-enabled states.\n *\n * @param systemId Sanitized system name (the part after \"systems.\")\n * @param config Current adapter configuration\n */\n public async cleanupMetrics(\n systemId: string,\n config: AdapterConfig,\n ): Promise<void> {\n const sysId = `systems.${systemId}`;\n const toDelete: string[] = [];\n\n if (!config.metrics_uptime) {\n toDelete.push(`${sysId}.uptime`, `${sysId}.uptime_text`);\n }\n if (!config.metrics_agentVersion) {\n toDelete.push(`${sysId}.agent_version`);\n }\n if (!config.metrics_services) {\n toDelete.push(`${sysId}.services_total`, `${sysId}.services_failed`);\n }\n if (!config.metrics_cpu) {\n toDelete.push(`${sysId}.cpu_usage`);\n }\n if (!config.metrics_loadAvg) {\n toDelete.push(\n `${sysId}.load_avg_1m`,\n `${sysId}.load_avg_5m`,\n `${sysId}.load_avg_15m`,\n );\n }\n if (!config.metrics_cpuBreakdown) {\n toDelete.push(\n `${sysId}.cpu_user`,\n `${sysId}.cpu_system`,\n `${sysId}.cpu_iowait`,\n `${sysId}.cpu_steal`,\n `${sysId}.cpu_idle`,\n );\n }\n if (!config.metrics_memory) {\n toDelete.push(\n `${sysId}.memory_percent`,\n `${sysId}.memory_used`,\n `${sysId}.memory_total`,\n );\n }\n if (!config.metrics_memoryDetails) {\n toDelete.push(`${sysId}.memory_buffers`, `${sysId}.memory_zfs_arc`);\n }\n if (!config.metrics_swap) {\n toDelete.push(`${sysId}.swap_used`, `${sysId}.swap_total`);\n }\n if (!config.metrics_disk) {\n toDelete.push(\n `${sysId}.disk_percent`,\n `${sysId}.disk_used`,\n `${sysId}.disk_total`,\n );\n }\n if (!config.metrics_diskSpeed) {\n toDelete.push(`${sysId}.disk_read`, `${sysId}.disk_write`);\n }\n if (!config.metrics_network) {\n toDelete.push(`${sysId}.network_sent`, `${sysId}.network_recv`);\n }\n if (!config.metrics_temperature) {\n toDelete.push(`${sysId}.temperature`);\n }\n if (!config.metrics_battery) {\n toDelete.push(`${sysId}.battery_percent`, `${sysId}.battery_charging`);\n }\n\n for (const id of toDelete) {\n const obj = await this.adapter.getObjectAsync(id);\n if (obj) {\n await this.adapter.delObjectAsync(id);\n }\n }\n\n // Channels\n if (!config.metrics_temperatureDetails) {\n await this.deleteChannelIfExists(`${sysId}.temperatures`);\n }\n if (!config.metrics_gpu) {\n await this.deleteChannelIfExists(`${sysId}.gpu`);\n }\n if (!config.metrics_extraFs) {\n await this.deleteChannelIfExists(`${sysId}.filesystems`);\n }\n if (!config.metrics_containers) {\n await this.deleteChannelIfExists(`${sysId}.containers`);\n }\n }\n\n // -------------------------------------------------------------------------\n // Private helpers\n // -------------------------------------------------------------------------\n\n private async updateStatsStates(\n sysId: string,\n system: BeszelSystem,\n stats: SystemStats,\n config: AdapterConfig,\n ): Promise<void> {\n // CPU\n if (config.metrics_cpu) {\n await this.createAndSetState(\n `${sysId}.cpu_usage`,\n {\n name: \"CPU Usage\",\n type: \"number\",\n role: \"level\",\n unit: \"%\",\n min: 0,\n max: 100,\n read: true,\n write: false,\n } as ioBroker.StateCommon,\n stats.cpu ?? null,\n );\n }\n\n // Load avg \u2014 prefer stats.la, fallback to system.info.la\n if (config.metrics_loadAvg) {\n await this.createLoadAvgStates(sysId, stats.la ?? system.info.la);\n }\n\n // CPU breakdown\n if (config.metrics_cpuBreakdown && stats.cpub && stats.cpub.length >= 5) {\n const [user, sys, iowait, steal, idle] = stats.cpub;\n await this.createAndSetState(\n `${sysId}.cpu_user`,\n {\n name: \"CPU User %\",\n type: \"number\",\n role: \"level\",\n unit: \"%\",\n min: 0,\n max: 100,\n read: true,\n write: false,\n } as ioBroker.StateCommon,\n user,\n );\n await this.createAndSetState(\n `${sysId}.cpu_system`,\n {\n name: \"CPU System %\",\n type: \"number\",\n role: \"level\",\n unit: \"%\",\n min: 0,\n max: 100,\n read: true,\n write: false,\n } as ioBroker.StateCommon,\n sys,\n );\n await this.createAndSetState(\n `${sysId}.cpu_iowait`,\n {\n name: \"CPU IOWait %\",\n type: \"number\",\n role: \"level\",\n unit: \"%\",\n min: 0,\n max: 100,\n read: true,\n write: false,\n } as ioBroker.StateCommon,\n iowait,\n );\n await this.createAndSetState(\n `${sysId}.cpu_steal`,\n {\n name: \"CPU Steal %\",\n type: \"number\",\n role: \"level\",\n unit: \"%\",\n min: 0,\n max: 100,\n read: true,\n write: false,\n } as ioBroker.StateCommon,\n steal,\n );\n await this.createAndSetState(\n `${sysId}.cpu_idle`,\n {\n name: \"CPU Idle %\",\n type: \"number\",\n role: \"level\",\n unit: \"%\",\n min: 0,\n max: 100,\n read: true,\n write: false,\n } as ioBroker.StateCommon,\n idle,\n );\n }\n\n // Memory\n if (config.metrics_memory) {\n await this.createAndSetState(\n `${sysId}.memory_percent`,\n {\n name: \"Memory %\",\n type: \"number\",\n role: \"level\",\n unit: \"%\",\n min: 0,\n max: 100,\n read: true,\n write: false,\n } as ioBroker.StateCommon,\n stats.mp ?? null,\n );\n await this.createAndSetState(\n `${sysId}.memory_used`,\n {\n name: \"Memory Used\",\n type: \"number\",\n role: \"value\",\n unit: \"GB\",\n read: true,\n write: false,\n } as ioBroker.StateCommon,\n stats.mu ?? null,\n );\n await this.createAndSetState(\n `${sysId}.memory_total`,\n {\n name: \"Memory Total\",\n type: \"number\",\n role: \"value\",\n unit: \"GB\",\n read: true,\n write: false,\n } as ioBroker.StateCommon,\n stats.m ?? null,\n );\n }\n\n // Memory details\n if (config.metrics_memoryDetails) {\n await this.createAndSetState(\n `${sysId}.memory_buffers`,\n {\n name: \"Memory Buffers+Cache\",\n type: \"number\",\n role: \"value\",\n unit: \"GB\",\n read: true,\n write: false,\n } as ioBroker.StateCommon,\n stats.mb ?? null,\n );\n await this.createAndSetState(\n `${sysId}.memory_zfs_arc`,\n {\n name: \"Memory ZFS ARC\",\n type: \"number\",\n role: \"value\",\n unit: \"GB\",\n read: true,\n write: false,\n } as ioBroker.StateCommon,\n stats.mz ?? null,\n );\n }\n\n // Swap\n if (config.metrics_swap) {\n await this.createAndSetState(\n `${sysId}.swap_used`,\n {\n name: \"Swap Used\",\n type: \"number\",\n role: \"value\",\n unit: \"GB\",\n read: true,\n write: false,\n } as ioBroker.StateCommon,\n stats.su ?? null,\n );\n await this.createAndSetState(\n `${sysId}.swap_total`,\n {\n name: \"Swap Total\",\n type: \"number\",\n role: \"value\",\n unit: \"GB\",\n read: true,\n write: false,\n } as ioBroker.StateCommon,\n stats.s ?? null,\n );\n }\n\n // Disk\n if (config.metrics_disk) {\n await this.createAndSetState(\n `${sysId}.disk_percent`,\n {\n name: \"Disk %\",\n type: \"number\",\n role: \"level\",\n unit: \"%\",\n min: 0,\n max: 100,\n read: true,\n write: false,\n } as ioBroker.StateCommon,\n stats.dp ?? null,\n );\n await this.createAndSetState(\n `${sysId}.disk_used`,\n {\n name: \"Disk Used\",\n type: \"number\",\n role: \"value\",\n unit: \"GB\",\n read: true,\n write: false,\n } as ioBroker.StateCommon,\n stats.du ?? null,\n );\n await this.createAndSetState(\n `${sysId}.disk_total`,\n {\n name: \"Disk Total\",\n type: \"number\",\n role: \"value\",\n unit: \"GB\",\n read: true,\n write: false,\n } as ioBroker.StateCommon,\n stats.d ?? null,\n );\n }\n\n // Disk speed\n if (config.metrics_diskSpeed) {\n await this.createAndSetState(\n `${sysId}.disk_read`,\n {\n name: \"Disk Read\",\n type: \"number\",\n role: \"value\",\n unit: \"MB/s\",\n read: true,\n write: false,\n } as ioBroker.StateCommon,\n stats.dr ?? null,\n );\n await this.createAndSetState(\n `${sysId}.disk_write`,\n {\n name: \"Disk Write\",\n type: \"number\",\n role: \"value\",\n unit: \"MB/s\",\n read: true,\n write: false,\n } as ioBroker.StateCommon,\n stats.dw ?? null,\n );\n }\n\n // Network\n if (config.metrics_network) {\n await this.createAndSetState(\n `${sysId}.network_sent`,\n {\n name: \"Network Sent\",\n type: \"number\",\n role: \"value\",\n unit: \"MB/s\",\n read: true,\n write: false,\n } as ioBroker.StateCommon,\n stats.ns ?? null,\n );\n await this.createAndSetState(\n `${sysId}.network_recv`,\n {\n name: \"Network Received\",\n type: \"number\",\n role: \"value\",\n unit: \"MB/s\",\n read: true,\n write: false,\n } as ioBroker.StateCommon,\n stats.nr ?? null,\n );\n }\n\n // Temperature (average of top 3)\n if (config.metrics_temperature) {\n const avgTemp = this.computeTopAvgTemp(stats.t);\n await this.createAndSetState(\n `${sysId}.temperature`,\n {\n name: \"Temperature (avg top 3)\",\n type: \"number\",\n role: \"value.temperature\",\n unit: \"\u00B0C\",\n read: true,\n write: false,\n } as ioBroker.StateCommon,\n avgTemp,\n );\n }\n\n // Temperature details\n if (config.metrics_temperatureDetails && stats.t) {\n await this.ensureChannel(`${sysId}.temperatures`, \"Temperatures\");\n for (const [sensor, temp] of Object.entries(stats.t)) {\n const sensorId = this.sanitize(sensor);\n await this.createAndSetState(\n `${sysId}.temperatures.${sensorId}`,\n {\n name: sensor,\n type: \"number\",\n role: \"value.temperature\",\n unit: \"\u00B0C\",\n read: true,\n write: false,\n } as ioBroker.StateCommon,\n temp,\n );\n }\n } else if (!config.metrics_temperatureDetails) {\n await this.deleteChannelIfExists(`${sysId}.temperatures`);\n }\n\n // Battery\n if (config.metrics_battery) {\n const bat = stats.bat ?? system.info.bat;\n await this.createAndSetState(\n `${sysId}.battery_percent`,\n {\n name: \"Battery %\",\n type: \"number\",\n role: \"value\",\n unit: \"%\",\n min: 0,\n max: 100,\n read: true,\n write: false,\n } as ioBroker.StateCommon,\n bat?.[0] ?? null,\n );\n await this.createAndSetState(\n `${sysId}.battery_charging`,\n {\n name: \"Battery Charging\",\n type: \"boolean\",\n role: \"indicator\",\n read: true,\n write: false,\n } as ioBroker.StateCommon,\n bat ? bat[1] > 0 : null,\n );\n }\n\n // GPU\n if (config.metrics_gpu && stats.g && Object.keys(stats.g).length > 0) {\n await this.ensureChannel(`${sysId}.gpu`, \"GPU\");\n for (const [gpuId, gpuData] of Object.entries(stats.g)) {\n const safeId = this.sanitize(gpuId);\n const gpuLabel = gpuData.n ?? gpuId;\n await this.ensureChannel(`${sysId}.gpu.${safeId}`, gpuLabel);\n await this.createAndSetState(\n `${sysId}.gpu.${safeId}.usage`,\n {\n name: \"GPU Usage\",\n type: \"number\",\n role: \"level\",\n unit: \"%\",\n min: 0,\n max: 100,\n read: true,\n write: false,\n } as ioBroker.StateCommon,\n gpuData.u ?? null,\n );\n await this.createAndSetState(\n `${sysId}.gpu.${safeId}.memory_used`,\n {\n name: \"GPU Memory Used\",\n type: \"number\",\n role: \"value\",\n unit: \"GB\",\n read: true,\n write: false,\n } as ioBroker.StateCommon,\n gpuData.mu ?? null,\n );\n await this.createAndSetState(\n `${sysId}.gpu.${safeId}.memory_total`,\n {\n name: \"GPU Memory Total\",\n type: \"number\",\n role: \"value\",\n unit: \"GB\",\n read: true,\n write: false,\n } as ioBroker.StateCommon,\n gpuData.mt ?? null,\n );\n await this.createAndSetState(\n `${sysId}.gpu.${safeId}.power`,\n {\n name: \"GPU Power\",\n type: \"number\",\n role: \"value\",\n unit: \"W\",\n read: true,\n write: false,\n } as ioBroker.StateCommon,\n gpuData.p ?? null,\n );\n }\n } else if (!config.metrics_gpu) {\n await this.deleteChannelIfExists(`${sysId}.gpu`);\n }\n\n // Extra filesystems\n if (\n config.metrics_extraFs &&\n stats.efs &&\n Object.keys(stats.efs).length > 0\n ) {\n await this.ensureChannel(`${sysId}.filesystems`, \"Filesystems\");\n for (const [fsName, fsData] of Object.entries(stats.efs)) {\n const safeId = this.sanitize(fsName);\n await this.ensureChannel(`${sysId}.filesystems.${safeId}`, fsName);\n\n const total = fsData.d ?? null;\n const used = fsData.du ?? null;\n const percent =\n total !== null && used !== null && total > 0\n ? Math.round((used / total) * 100)\n : null;\n\n await this.createAndSetState(\n `${sysId}.filesystems.${safeId}.disk_percent`,\n {\n name: \"Disk %\",\n type: \"number\",\n role: \"level\",\n unit: \"%\",\n min: 0,\n max: 100,\n read: true,\n write: false,\n } as ioBroker.StateCommon,\n percent,\n );\n await this.createAndSetState(\n `${sysId}.filesystems.${safeId}.disk_used`,\n {\n name: \"Disk Used\",\n type: \"number\",\n role: \"value\",\n unit: \"GB\",\n read: true,\n write: false,\n } as ioBroker.StateCommon,\n used,\n );\n await this.createAndSetState(\n `${sysId}.filesystems.${safeId}.disk_total`,\n {\n name: \"Disk Total\",\n type: \"number\",\n role: \"value\",\n unit: \"GB\",\n read: true,\n write: false,\n } as ioBroker.StateCommon,\n total,\n );\n await this.createAndSetState(\n `${sysId}.filesystems.${safeId}.read_speed`,\n {\n name: \"Read Speed\",\n type: \"number\",\n role: \"value\",\n unit: \"MB/s\",\n read: true,\n write: false,\n } as ioBroker.StateCommon,\n fsData.r ?? null,\n );\n await this.createAndSetState(\n `${sysId}.filesystems.${safeId}.write_speed`,\n {\n name: \"Write Speed\",\n type: \"number\",\n role: \"value\",\n unit: \"MB/s\",\n read: true,\n write: false,\n } as ioBroker.StateCommon,\n fsData.w ?? null,\n );\n }\n } else if (!config.metrics_extraFs) {\n await this.deleteChannelIfExists(`${sysId}.filesystems`);\n }\n }\n\n private async updateContainers(\n sysId: string,\n systemId: string,\n allContainers: BeszelContainer[],\n ): Promise<void> {\n const sysContainers = allContainers.filter((c) => c.system === systemId);\n if (sysContainers.length === 0) {\n return;\n }\n\n await this.ensureChannel(`${sysId}.containers`, \"Containers\");\n\n const healthLabels = [\"none\", \"starting\", \"healthy\", \"unhealthy\"];\n\n for (const container of sysContainers) {\n const cId = this.sanitize(container.name);\n await this.ensureChannel(`${sysId}.containers.${cId}`, container.name);\n\n await this.createAndSetState(\n `${sysId}.containers.${cId}.status`,\n {\n name: \"Status\",\n type: \"string\",\n role: \"text\",\n read: true,\n write: false,\n } as ioBroker.StateCommon,\n container.status,\n );\n\n await this.createAndSetState(\n `${sysId}.containers.${cId}.health`,\n {\n name: \"Health\",\n type: \"string\",\n role: \"text\",\n read: true,\n write: false,\n } as ioBroker.StateCommon,\n healthLabels[container.health] ?? \"unknown\",\n );\n\n await this.createAndSetState(\n `${sysId}.containers.${cId}.cpu`,\n {\n name: \"CPU Usage\",\n type: \"number\",\n role: \"level\",\n unit: \"%\",\n min: 0,\n max: 100,\n read: true,\n write: false,\n } as ioBroker.StateCommon,\n container.cpu,\n );\n\n await this.createAndSetState(\n `${sysId}.containers.${cId}.memory`,\n {\n name: \"Memory\",\n type: \"number\",\n role: \"value\",\n unit: \"MB\",\n read: true,\n write: false,\n } as ioBroker.StateCommon,\n container.memory,\n );\n\n await this.createAndSetState(\n `${sysId}.containers.${cId}.image`,\n {\n name: \"Image\",\n type: \"string\",\n role: \"text\",\n read: true,\n write: false,\n } as ioBroker.StateCommon,\n container.image,\n );\n }\n }\n\n private async ensureChannel(id: string, name: string): Promise<void> {\n await this.adapter.setObjectNotExistsAsync(id, {\n type: \"channel\",\n common: { name } as ioBroker.ObjectCommon,\n native: {},\n });\n }\n\n private async deleteChannelIfExists(id: string): Promise<void> {\n try {\n const obj = await this.adapter.getObjectAsync(id);\n if (obj) {\n await this.adapter.delObjectAsync(id, { recursive: true });\n }\n } catch {\n // ignore\n }\n }\n\n /**\n * Create or update the three load average states.\n *\n * @param sysId - State ID prefix (e.g. \"systems.my_server\")\n * @param la - Load average tuple [1m, 5m, 15m], or undefined\n */\n private async createLoadAvgStates(\n sysId: string,\n la: [number, number, number] | undefined,\n ): Promise<void> {\n await this.createAndSetState(\n `${sysId}.load_avg_1m`,\n {\n name: \"Load Average 1m\",\n type: \"number\",\n role: \"value\",\n read: true,\n write: false,\n } as ioBroker.StateCommon,\n la?.[0] ?? null,\n );\n await this.createAndSetState(\n `${sysId}.load_avg_5m`,\n {\n name: \"Load Average 5m\",\n type: \"number\",\n role: \"value\",\n read: true,\n write: false,\n } as ioBroker.StateCommon,\n la?.[1] ?? null,\n );\n await this.createAndSetState(\n `${sysId}.load_avg_15m`,\n {\n name: \"Load Average 15m\",\n type: \"number\",\n role: \"value\",\n read: true,\n write: false,\n } as ioBroker.StateCommon,\n la?.[2] ?? null,\n );\n }\n\n private async createAndSetState(\n id: string,\n common: ioBroker.StateCommon,\n value: ioBroker.StateValue,\n ): Promise<void> {\n await this.adapter.setObjectNotExistsAsync(id, {\n type: \"state\",\n common,\n native: {},\n });\n await this.adapter.setStateAsync(id, { val: value, ack: true });\n }\n\n private computeTopAvgTemp(\n temps: Record<string, number> | undefined,\n ): number | null {\n if (!temps) {\n return null;\n }\n const values = Object.values(temps).filter(\n (v) => typeof v === \"number\" && isFinite(v),\n );\n if (values.length === 0) {\n return null;\n }\n values.sort((a, b) => b - a);\n const top3 = values.slice(0, 3);\n const avg = top3.reduce((sum, v) => sum + v, 0) / top3.length;\n return Math.round(avg * 10) / 10;\n }\n\n private formatUptime(seconds: number): string {\n const d = Math.floor(seconds / 86400);\n const h = Math.floor((seconds % 86400) / 3600);\n const m = Math.floor((seconds % 3600) / 60);\n const parts: string[] = [];\n if (d > 0) {\n parts.push(`${d}d`);\n }\n if (h > 0) {\n parts.push(`${h}h`);\n }\n if (m > 0 || parts.length === 0) {\n parts.push(`${m}m`);\n }\n return parts.join(\" \");\n }\n}\n"],
5
5
  "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAWO,MAAM,aAAa;AAAA,EACP;AAAA;AAAA;AAAA;AAAA,EAKjB,YAAY,SAAgC;AAC1C,SAAK,UAAU;AAAA,EACjB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQO,SAAS,MAAsB;AACpC,WAAO,KACJ,YAAY,EACZ,QAAQ,eAAe,GAAG,EAC1B,QAAQ,YAAY,EAAE,EACtB,MAAM,GAAG,EAAE;AAAA,EAChB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,MAAa,aACX,QACA,OACA,YACA,QACe;AAhDnB;AAiDI,UAAM,QAAQ,WAAW,KAAK,SAAS,OAAO,IAAI,CAAC;AAGnD,UAAM,KAAK,QAAQ,kBAAkB,OAAO;AAAA,MAC1C,MAAM;AAAA,MACN,QAAQ;AAAA,QACN,MAAM,OAAO;AAAA,QACb,cAAc;AAAA,UACZ,UAAU,GAAG,KAAK,QAAQ,SAAS,IAAI,KAAK;AAAA,QAC9C;AAAA,MACF;AAAA,MACA,QAAQ,EAAE,IAAI,OAAO,IAAI,MAAM,OAAO,KAAK;AAAA,IAC7C,CAAC;AAGD,UAAM,OAAO,OAAO,WAAW;AAC/B,UAAM,KAAK;AAAA,MACT,GAAG,KAAK;AAAA,MACR;AAAA,QACE,MAAM;AAAA,QACN,MAAM;AAAA,QACN,MAAM;AAAA,QACN,MAAM;AAAA,QACN,OAAO;AAAA,MACT;AAAA,MACA;AAAA,IACF;AAEA,UAAM,KAAK;AAAA,MACT,GAAG,KAAK;AAAA,MACR;AAAA,QACE,MAAM;AAAA,QACN,MAAM;AAAA,QACN,MAAM;AAAA,QACN,MAAM;AAAA,QACN,OAAO;AAAA,MACT;AAAA,MACA,OAAO;AAAA,IACT;AAGA,QAAI,OAAO,gBAAgB;AACzB,YAAM,UAAS,YAAO,KAAK,MAAZ,YAAiB;AAEhC,YAAM,KAAK;AAAA,QACT,GAAG,KAAK;AAAA,QACR;AAAA,UACE,MAAM;AAAA,UACN,MAAM;AAAA,UACN,MAAM;AAAA,UACN,MAAM;AAAA,UACN,MAAM;AAAA,UACN,OAAO;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAEA,YAAM,KAAK;AAAA,QACT,GAAG,KAAK;AAAA,QACR;AAAA,UACE,MAAM;AAAA,UACN,MAAM;AAAA,UACN,MAAM;AAAA,UACN,MAAM;AAAA,UACN,OAAO;AAAA,QACT;AAAA,QACA,WAAW,OAAO,KAAK,aAAa,MAAM,IAAI;AAAA,MAChD;AAAA,IACF;AAGA,QAAI,OAAO,sBAAsB;AAC/B,YAAM,KAAK;AAAA,QACT,GAAG,KAAK;AAAA,QACR;AAAA,UACE,MAAM;AAAA,UACN,MAAM;AAAA,UACN,MAAM;AAAA,UACN,MAAM;AAAA,UACN,OAAO;AAAA,QACT;AAAA,SACA,YAAO,KAAK,MAAZ,YAAiB;AAAA,MACnB;AAAA,IACF;AAGA,QAAI,OAAO,kBAAkB;AAC3B,YAAM,KAAK,OAAO,KAAK;AACvB,YAAM,KAAK;AAAA,QACT,GAAG,KAAK;AAAA,QACR;AAAA,UACE,MAAM;AAAA,UACN,MAAM;AAAA,UACN,MAAM;AAAA,UACN,MAAM;AAAA,UACN,OAAO;AAAA,QACT;AAAA,SACA,8BAAK,OAAL,YAAW;AAAA,MACb;AAEA,YAAM,KAAK;AAAA,QACT,GAAG,KAAK;AAAA,QACR;AAAA,UACE,MAAM;AAAA,UACN,MAAM;AAAA,UACN,MAAM;AAAA,UACN,MAAM;AAAA,UACN,OAAO;AAAA,QACT;AAAA,SACA,8BAAK,OAAL,YAAW;AAAA,MACb;AAAA,IACF;AAGA,QAAI,OAAO;AACT,YAAM,KAAK,kBAAkB,OAAO,QAAQ,OAAO,MAAM;AAAA,IAC3D;AAGA,QAAI,OAAO,mBAAmB,CAAC,OAAO;AACpC,YAAM,KAAK,oBAAoB,OAAO,OAAO,KAAK,EAAE;AAAA,IACtD;AAGA,QAAI,OAAO,oBAAoB;AAC7B,YAAM,KAAK,iBAAiB,OAAO,OAAO,IAAI,UAAU;AAAA,IAC1D,OAAO;AACL,YAAM,KAAK,sBAAsB,GAAG,KAAK,aAAa;AAAA,IACxD;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAa,eAAe,mBAA4C;AACtE,UAAM,YAAY,IAAI;AAAA,MACpB,kBAAkB,IAAI,CAAC,MAAM,WAAW,KAAK,SAAS,CAAC,CAAC,EAAE;AAAA,IAC5D;AAEA,UAAM,UAAU,MAAM,KAAK,QAAQ,mBAAmB,UAAU,UAAU;AAAA,MACxE,UAAU,GAAG,KAAK,QAAQ,SAAS;AAAA,MACnC,QAAQ,GAAG,KAAK,QAAQ,SAAS;AAAA,IACnC,CAAC;AAED,QAAI,EAAC,mCAAS,OAAM;AAClB;AAAA,IACF;AAEA,eAAW,OAAO,QAAQ,MAAM;AAC9B,YAAM,KAAK,IAAI;AAEf,YAAM,aAAa,GAAG,WAAW,GAAG,KAAK,QAAQ,SAAS,GAAG,IACzD,GAAG,MAAM,KAAK,QAAQ,UAAU,SAAS,CAAC,IAC1C;AAGJ,YAAM,QAAQ,WAAW,MAAM,GAAG;AAClC,UACE,MAAM,WAAW,KACjB,MAAM,CAAC,MAAM,aACb,CAAC,UAAU,IAAI,UAAU,GACzB;AACA,aAAK,QAAQ,IAAI,MAAM,0BAA0B,UAAU,EAAE;AAC7D,cAAM,KAAK,QAAQ,eAAe,YAAY,EAAE,WAAW,KAAK,CAAC;AAAA,MACnE;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAa,eACX,UACA,QACe;AACf,UAAM,QAAQ,WAAW,QAAQ;AACjC,UAAM,WAAqB,CAAC;AAE5B,QAAI,CAAC,OAAO,gBAAgB;AAC1B,eAAS,KAAK,GAAG,KAAK,WAAW,GAAG,KAAK,cAAc;AAAA,IACzD;AACA,QAAI,CAAC,OAAO,sBAAsB;AAChC,eAAS,KAAK,GAAG,KAAK,gBAAgB;AAAA,IACxC;AACA,QAAI,CAAC,OAAO,kBAAkB;AAC5B,eAAS,KAAK,GAAG,KAAK,mBAAmB,GAAG,KAAK,kBAAkB;AAAA,IACrE;AACA,QAAI,CAAC,OAAO,aAAa;AACvB,eAAS,KAAK,GAAG,KAAK,YAAY;AAAA,IACpC;AACA,QAAI,CAAC,OAAO,iBAAiB;AAC3B,eAAS;AAAA,QACP,GAAG,KAAK;AAAA,QACR,GAAG,KAAK;AAAA,QACR,GAAG,KAAK;AAAA,MACV;AAAA,IACF;AACA,QAAI,CAAC,OAAO,sBAAsB;AAChC,eAAS;AAAA,QACP,GAAG,KAAK;AAAA,QACR,GAAG,KAAK;AAAA,QACR,GAAG,KAAK;AAAA,QACR,GAAG,KAAK;AAAA,QACR,GAAG,KAAK;AAAA,MACV;AAAA,IACF;AACA,QAAI,CAAC,OAAO,gBAAgB;AAC1B,eAAS;AAAA,QACP,GAAG,KAAK;AAAA,QACR,GAAG,KAAK;AAAA,QACR,GAAG,KAAK;AAAA,MACV;AAAA,IACF;AACA,QAAI,CAAC,OAAO,uBAAuB;AACjC,eAAS,KAAK,GAAG,KAAK,mBAAmB,GAAG,KAAK,iBAAiB;AAAA,IACpE;AACA,QAAI,CAAC,OAAO,cAAc;AACxB,eAAS,KAAK,GAAG,KAAK,cAAc,GAAG,KAAK,aAAa;AAAA,IAC3D;AACA,QAAI,CAAC,OAAO,cAAc;AACxB,eAAS;AAAA,QACP,GAAG,KAAK;AAAA,QACR,GAAG,KAAK;AAAA,QACR,GAAG,KAAK;AAAA,MACV;AAAA,IACF;AACA,QAAI,CAAC,OAAO,mBAAmB;AAC7B,eAAS,KAAK,GAAG,KAAK,cAAc,GAAG,KAAK,aAAa;AAAA,IAC3D;AACA,QAAI,CAAC,OAAO,iBAAiB;AAC3B,eAAS,KAAK,GAAG,KAAK,iBAAiB,GAAG,KAAK,eAAe;AAAA,IAChE;AACA,QAAI,CAAC,OAAO,qBAAqB;AAC/B,eAAS,KAAK,GAAG,KAAK,cAAc;AAAA,IACtC;AACA,QAAI,CAAC,OAAO,iBAAiB;AAC3B,eAAS,KAAK,GAAG,KAAK,oBAAoB,GAAG,KAAK,mBAAmB;AAAA,IACvE;AAEA,eAAW,MAAM,UAAU;AACzB,YAAM,MAAM,MAAM,KAAK,QAAQ,eAAe,EAAE;AAChD,UAAI,KAAK;AACP,cAAM,KAAK,QAAQ,eAAe,EAAE;AAAA,MACtC;AAAA,IACF;AAGA,QAAI,CAAC,OAAO,4BAA4B;AACtC,YAAM,KAAK,sBAAsB,GAAG,KAAK,eAAe;AAAA,IAC1D;AACA,QAAI,CAAC,OAAO,aAAa;AACvB,YAAM,KAAK,sBAAsB,GAAG,KAAK,MAAM;AAAA,IACjD;AACA,QAAI,CAAC,OAAO,iBAAiB;AAC3B,YAAM,KAAK,sBAAsB,GAAG,KAAK,cAAc;AAAA,IACzD;AACA,QAAI,CAAC,OAAO,oBAAoB;AAC9B,YAAM,KAAK,sBAAsB,GAAG,KAAK,aAAa;AAAA,IACxD;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAMA,MAAc,kBACZ,OACA,QACA,OACA,QACe;AArUnB;AAuUI,QAAI,OAAO,aAAa;AACtB,YAAM,KAAK;AAAA,QACT,GAAG,KAAK;AAAA,QACR;AAAA,UACE,MAAM;AAAA,UACN,MAAM;AAAA,UACN,MAAM;AAAA,UACN,MAAM;AAAA,UACN,KAAK;AAAA,UACL,KAAK;AAAA,UACL,MAAM;AAAA,UACN,OAAO;AAAA,QACT;AAAA,SACA,WAAM,QAAN,YAAa;AAAA,MACf;AAAA,IACF;AAGA,QAAI,OAAO,iBAAiB;AAC1B,YAAM,KAAK,oBAAoB,QAAO,WAAM,OAAN,YAAY,OAAO,KAAK,EAAE;AAAA,IAClE;AAGA,QAAI,OAAO,wBAAwB,MAAM,QAAQ,MAAM,KAAK,UAAU,GAAG;AACvE,YAAM,CAAC,MAAM,KAAK,QAAQ,OAAO,IAAI,IAAI,MAAM;AAC/C,YAAM,KAAK;AAAA,QACT,GAAG,KAAK;AAAA,QACR;AAAA,UACE,MAAM;AAAA,UACN,MAAM;AAAA,UACN,MAAM;AAAA,UACN,MAAM;AAAA,UACN,KAAK;AAAA,UACL,KAAK;AAAA,UACL,MAAM;AAAA,UACN,OAAO;AAAA,QACT;AAAA,QACA;AAAA,MACF;AACA,YAAM,KAAK;AAAA,QACT,GAAG,KAAK;AAAA,QACR;AAAA,UACE,MAAM;AAAA,UACN,MAAM;AAAA,UACN,MAAM;AAAA,UACN,MAAM;AAAA,UACN,KAAK;AAAA,UACL,KAAK;AAAA,UACL,MAAM;AAAA,UACN,OAAO;AAAA,QACT;AAAA,QACA;AAAA,MACF;AACA,YAAM,KAAK;AAAA,QACT,GAAG,KAAK;AAAA,QACR;AAAA,UACE,MAAM;AAAA,UACN,MAAM;AAAA,UACN,MAAM;AAAA,UACN,MAAM;AAAA,UACN,KAAK;AAAA,UACL,KAAK;AAAA,UACL,MAAM;AAAA,UACN,OAAO;AAAA,QACT;AAAA,QACA;AAAA,MACF;AACA,YAAM,KAAK;AAAA,QACT,GAAG,KAAK;AAAA,QACR;AAAA,UACE,MAAM;AAAA,UACN,MAAM;AAAA,UACN,MAAM;AAAA,UACN,MAAM;AAAA,UACN,KAAK;AAAA,UACL,KAAK;AAAA,UACL,MAAM;AAAA,UACN,OAAO;AAAA,QACT;AAAA,QACA;AAAA,MACF;AACA,YAAM,KAAK;AAAA,QACT,GAAG,KAAK;AAAA,QACR;AAAA,UACE,MAAM;AAAA,UACN,MAAM;AAAA,UACN,MAAM;AAAA,UACN,MAAM;AAAA,UACN,KAAK;AAAA,UACL,KAAK;AAAA,UACL,MAAM;AAAA,UACN,OAAO;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAGA,QAAI,OAAO,gBAAgB;AACzB,YAAM,KAAK;AAAA,QACT,GAAG,KAAK;AAAA,QACR;AAAA,UACE,MAAM;AAAA,UACN,MAAM;AAAA,UACN,MAAM;AAAA,UACN,MAAM;AAAA,UACN,KAAK;AAAA,UACL,KAAK;AAAA,UACL,MAAM;AAAA,UACN,OAAO;AAAA,QACT;AAAA,SACA,WAAM,OAAN,YAAY;AAAA,MACd;AACA,YAAM,KAAK;AAAA,QACT,GAAG,KAAK;AAAA,QACR;AAAA,UACE,MAAM;AAAA,UACN,MAAM;AAAA,UACN,MAAM;AAAA,UACN,MAAM;AAAA,UACN,MAAM;AAAA,UACN,OAAO;AAAA,QACT;AAAA,SACA,WAAM,OAAN,YAAY;AAAA,MACd;AACA,YAAM,KAAK;AAAA,QACT,GAAG,KAAK;AAAA,QACR;AAAA,UACE,MAAM;AAAA,UACN,MAAM;AAAA,UACN,MAAM;AAAA,UACN,MAAM;AAAA,UACN,MAAM;AAAA,UACN,OAAO;AAAA,QACT;AAAA,SACA,WAAM,MAAN,YAAW;AAAA,MACb;AAAA,IACF;AAGA,QAAI,OAAO,uBAAuB;AAChC,YAAM,KAAK;AAAA,QACT,GAAG,KAAK;AAAA,QACR;AAAA,UACE,MAAM;AAAA,UACN,MAAM;AAAA,UACN,MAAM;AAAA,UACN,MAAM;AAAA,UACN,MAAM;AAAA,UACN,OAAO;AAAA,QACT;AAAA,SACA,WAAM,OAAN,YAAY;AAAA,MACd;AACA,YAAM,KAAK;AAAA,QACT,GAAG,KAAK;AAAA,QACR;AAAA,UACE,MAAM;AAAA,UACN,MAAM;AAAA,UACN,MAAM;AAAA,UACN,MAAM;AAAA,UACN,MAAM;AAAA,UACN,OAAO;AAAA,QACT;AAAA,SACA,WAAM,OAAN,YAAY;AAAA,MACd;AAAA,IACF;AAGA,QAAI,OAAO,cAAc;AACvB,YAAM,KAAK;AAAA,QACT,GAAG,KAAK;AAAA,QACR;AAAA,UACE,MAAM;AAAA,UACN,MAAM;AAAA,UACN,MAAM;AAAA,UACN,MAAM;AAAA,UACN,MAAM;AAAA,UACN,OAAO;AAAA,QACT;AAAA,SACA,WAAM,OAAN,YAAY;AAAA,MACd;AACA,YAAM,KAAK;AAAA,QACT,GAAG,KAAK;AAAA,QACR;AAAA,UACE,MAAM;AAAA,UACN,MAAM;AAAA,UACN,MAAM;AAAA,UACN,MAAM;AAAA,UACN,MAAM;AAAA,UACN,OAAO;AAAA,QACT;AAAA,SACA,WAAM,MAAN,YAAW;AAAA,MACb;AAAA,IACF;AAGA,QAAI,OAAO,cAAc;AACvB,YAAM,KAAK;AAAA,QACT,GAAG,KAAK;AAAA,QACR;AAAA,UACE,MAAM;AAAA,UACN,MAAM;AAAA,UACN,MAAM;AAAA,UACN,MAAM;AAAA,UACN,KAAK;AAAA,UACL,KAAK;AAAA,UACL,MAAM;AAAA,UACN,OAAO;AAAA,QACT;AAAA,SACA,WAAM,OAAN,YAAY;AAAA,MACd;AACA,YAAM,KAAK;AAAA,QACT,GAAG,KAAK;AAAA,QACR;AAAA,UACE,MAAM;AAAA,UACN,MAAM;AAAA,UACN,MAAM;AAAA,UACN,MAAM;AAAA,UACN,MAAM;AAAA,UACN,OAAO;AAAA,QACT;AAAA,SACA,WAAM,OAAN,YAAY;AAAA,MACd;AACA,YAAM,KAAK;AAAA,QACT,GAAG,KAAK;AAAA,QACR;AAAA,UACE,MAAM;AAAA,UACN,MAAM;AAAA,UACN,MAAM;AAAA,UACN,MAAM;AAAA,UACN,MAAM;AAAA,UACN,OAAO;AAAA,QACT;AAAA,SACA,WAAM,MAAN,YAAW;AAAA,MACb;AAAA,IACF;AAGA,QAAI,OAAO,mBAAmB;AAC5B,YAAM,KAAK;AAAA,QACT,GAAG,KAAK;AAAA,QACR;AAAA,UACE,MAAM;AAAA,UACN,MAAM;AAAA,UACN,MAAM;AAAA,UACN,MAAM;AAAA,UACN,MAAM;AAAA,UACN,OAAO;AAAA,QACT;AAAA,SACA,WAAM,OAAN,YAAY;AAAA,MACd;AACA,YAAM,KAAK;AAAA,QACT,GAAG,KAAK;AAAA,QACR;AAAA,UACE,MAAM;AAAA,UACN,MAAM;AAAA,UACN,MAAM;AAAA,UACN,MAAM;AAAA,UACN,MAAM;AAAA,UACN,OAAO;AAAA,QACT;AAAA,SACA,WAAM,OAAN,YAAY;AAAA,MACd;AAAA,IACF;AAGA,QAAI,OAAO,iBAAiB;AAC1B,YAAM,KAAK;AAAA,QACT,GAAG,KAAK;AAAA,QACR;AAAA,UACE,MAAM;AAAA,UACN,MAAM;AAAA,UACN,MAAM;AAAA,UACN,MAAM;AAAA,UACN,MAAM;AAAA,UACN,OAAO;AAAA,QACT;AAAA,SACA,WAAM,OAAN,YAAY;AAAA,MACd;AACA,YAAM,KAAK;AAAA,QACT,GAAG,KAAK;AAAA,QACR;AAAA,UACE,MAAM;AAAA,UACN,MAAM;AAAA,UACN,MAAM;AAAA,UACN,MAAM;AAAA,UACN,MAAM;AAAA,UACN,OAAO;AAAA,QACT;AAAA,SACA,WAAM,OAAN,YAAY;AAAA,MACd;AAAA,IACF;AAGA,QAAI,OAAO,qBAAqB;AAC9B,YAAM,UAAU,KAAK,kBAAkB,MAAM,CAAC;AAC9C,YAAM,KAAK;AAAA,QACT,GAAG,KAAK;AAAA,QACR;AAAA,UACE,MAAM;AAAA,UACN,MAAM;AAAA,UACN,MAAM;AAAA,UACN,MAAM;AAAA,UACN,MAAM;AAAA,UACN,OAAO;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAGA,QAAI,OAAO,8BAA8B,MAAM,GAAG;AAChD,YAAM,KAAK,cAAc,GAAG,KAAK,iBAAiB,cAAc;AAChE,iBAAW,CAAC,QAAQ,IAAI,KAAK,OAAO,QAAQ,MAAM,CAAC,GAAG;AACpD,cAAM,WAAW,KAAK,SAAS,MAAM;AACrC,cAAM,KAAK;AAAA,UACT,GAAG,KAAK,iBAAiB,QAAQ;AAAA,UACjC;AAAA,YACE,MAAM;AAAA,YACN,MAAM;AAAA,YACN,MAAM;AAAA,YACN,MAAM;AAAA,YACN,MAAM;AAAA,YACN,OAAO;AAAA,UACT;AAAA,UACA;AAAA,QACF;AAAA,MACF;AAAA,IACF,WAAW,CAAC,OAAO,4BAA4B;AAC7C,YAAM,KAAK,sBAAsB,GAAG,KAAK,eAAe;AAAA,IAC1D;AAGA,QAAI,OAAO,iBAAiB;AAC1B,YAAM,OAAM,WAAM,QAAN,YAAa,OAAO,KAAK;AACrC,YAAM,KAAK;AAAA,QACT,GAAG,KAAK;AAAA,QACR;AAAA,UACE,MAAM;AAAA,UACN,MAAM;AAAA,UACN,MAAM;AAAA,UACN,MAAM;AAAA,UACN,KAAK;AAAA,UACL,KAAK;AAAA,UACL,MAAM;AAAA,UACN,OAAO;AAAA,QACT;AAAA,SACA,gCAAM,OAAN,YAAY;AAAA,MACd;AACA,YAAM,KAAK;AAAA,QACT,GAAG,KAAK;AAAA,QACR;AAAA,UACE,MAAM;AAAA,UACN,MAAM;AAAA,UACN,MAAM;AAAA,UACN,MAAM;AAAA,UACN,OAAO;AAAA,QACT;AAAA,QACA,MAAM,IAAI,CAAC,IAAI,IAAI;AAAA,MACrB;AAAA,IACF;AAGA,QAAI,OAAO,eAAe,MAAM,KAAK,OAAO,KAAK,MAAM,CAAC,EAAE,SAAS,GAAG;AACpE,YAAM,KAAK,cAAc,GAAG,KAAK,QAAQ,KAAK;AAC9C,iBAAW,CAAC,OAAO,OAAO,KAAK,OAAO,QAAQ,MAAM,CAAC,GAAG;AACtD,cAAM,SAAS,KAAK,SAAS,KAAK;AAClC,cAAM,YAAW,aAAQ,MAAR,YAAa;AAC9B,cAAM,KAAK,cAAc,GAAG,KAAK,QAAQ,MAAM,IAAI,QAAQ;AAC3D,cAAM,KAAK;AAAA,UACT,GAAG,KAAK,QAAQ,MAAM;AAAA,UACtB;AAAA,YACE,MAAM;AAAA,YACN,MAAM;AAAA,YACN,MAAM;AAAA,YACN,MAAM;AAAA,YACN,KAAK;AAAA,YACL,KAAK;AAAA,YACL,MAAM;AAAA,YACN,OAAO;AAAA,UACT;AAAA,WACA,aAAQ,MAAR,YAAa;AAAA,QACf;AACA,cAAM,KAAK;AAAA,UACT,GAAG,KAAK,QAAQ,MAAM;AAAA,UACtB;AAAA,YACE,MAAM;AAAA,YACN,MAAM;AAAA,YACN,MAAM;AAAA,YACN,MAAM;AAAA,YACN,MAAM;AAAA,YACN,OAAO;AAAA,UACT;AAAA,WACA,aAAQ,OAAR,YAAc;AAAA,QAChB;AACA,cAAM,KAAK;AAAA,UACT,GAAG,KAAK,QAAQ,MAAM;AAAA,UACtB;AAAA,YACE,MAAM;AAAA,YACN,MAAM;AAAA,YACN,MAAM;AAAA,YACN,MAAM;AAAA,YACN,MAAM;AAAA,YACN,OAAO;AAAA,UACT;AAAA,WACA,aAAQ,OAAR,YAAc;AAAA,QAChB;AACA,cAAM,KAAK;AAAA,UACT,GAAG,KAAK,QAAQ,MAAM;AAAA,UACtB;AAAA,YACE,MAAM;AAAA,YACN,MAAM;AAAA,YACN,MAAM;AAAA,YACN,MAAM;AAAA,YACN,MAAM;AAAA,YACN,OAAO;AAAA,UACT;AAAA,WACA,aAAQ,MAAR,YAAa;AAAA,QACf;AAAA,MACF;AAAA,IACF,WAAW,CAAC,OAAO,aAAa;AAC9B,YAAM,KAAK,sBAAsB,GAAG,KAAK,MAAM;AAAA,IACjD;AAGA,QACE,OAAO,mBACP,MAAM,OACN,OAAO,KAAK,MAAM,GAAG,EAAE,SAAS,GAChC;AACA,YAAM,KAAK,cAAc,GAAG,KAAK,gBAAgB,aAAa;AAC9D,iBAAW,CAAC,QAAQ,MAAM,KAAK,OAAO,QAAQ,MAAM,GAAG,GAAG;AACxD,cAAM,SAAS,KAAK,SAAS,MAAM;AACnC,cAAM,KAAK,cAAc,GAAG,KAAK,gBAAgB,MAAM,IAAI,MAAM;AAEjE,cAAM,SAAQ,YAAO,MAAP,YAAY;AAC1B,cAAM,QAAO,YAAO,OAAP,YAAa;AAC1B,cAAM,UACJ,UAAU,QAAQ,SAAS,QAAQ,QAAQ,IACvC,KAAK,MAAO,OAAO,QAAS,GAAG,IAC/B;AAEN,cAAM,KAAK;AAAA,UACT,GAAG,KAAK,gBAAgB,MAAM;AAAA,UAC9B;AAAA,YACE,MAAM;AAAA,YACN,MAAM;AAAA,YACN,MAAM;AAAA,YACN,MAAM;AAAA,YACN,KAAK;AAAA,YACL,KAAK;AAAA,YACL,MAAM;AAAA,YACN,OAAO;AAAA,UACT;AAAA,UACA;AAAA,QACF;AACA,cAAM,KAAK;AAAA,UACT,GAAG,KAAK,gBAAgB,MAAM;AAAA,UAC9B;AAAA,YACE,MAAM;AAAA,YACN,MAAM;AAAA,YACN,MAAM;AAAA,YACN,MAAM;AAAA,YACN,MAAM;AAAA,YACN,OAAO;AAAA,UACT;AAAA,UACA;AAAA,QACF;AACA,cAAM,KAAK;AAAA,UACT,GAAG,KAAK,gBAAgB,MAAM;AAAA,UAC9B;AAAA,YACE,MAAM;AAAA,YACN,MAAM;AAAA,YACN,MAAM;AAAA,YACN,MAAM;AAAA,YACN,MAAM;AAAA,YACN,OAAO;AAAA,UACT;AAAA,UACA;AAAA,QACF;AACA,cAAM,KAAK;AAAA,UACT,GAAG,KAAK,gBAAgB,MAAM;AAAA,UAC9B;AAAA,YACE,MAAM;AAAA,YACN,MAAM;AAAA,YACN,MAAM;AAAA,YACN,MAAM;AAAA,YACN,MAAM;AAAA,YACN,OAAO;AAAA,UACT;AAAA,WACA,YAAO,MAAP,YAAY;AAAA,QACd;AACA,cAAM,KAAK;AAAA,UACT,GAAG,KAAK,gBAAgB,MAAM;AAAA,UAC9B;AAAA,YACE,MAAM;AAAA,YACN,MAAM;AAAA,YACN,MAAM;AAAA,YACN,MAAM;AAAA,YACN,MAAM;AAAA,YACN,OAAO;AAAA,UACT;AAAA,WACA,YAAO,MAAP,YAAY;AAAA,QACd;AAAA,MACF;AAAA,IACF,WAAW,CAAC,OAAO,iBAAiB;AAClC,YAAM,KAAK,sBAAsB,GAAG,KAAK,cAAc;AAAA,IACzD;AAAA,EACF;AAAA,EAEA,MAAc,iBACZ,OACA,UACA,eACe;AAz0BnB;AA00BI,UAAM,gBAAgB,cAAc,OAAO,CAAC,MAAM,EAAE,WAAW,QAAQ;AACvE,QAAI,cAAc,WAAW,GAAG;AAC9B;AAAA,IACF;AAEA,UAAM,KAAK,cAAc,GAAG,KAAK,eAAe,YAAY;AAE5D,UAAM,eAAe,CAAC,QAAQ,YAAY,WAAW,WAAW;AAEhE,eAAW,aAAa,eAAe;AACrC,YAAM,MAAM,KAAK,SAAS,UAAU,IAAI;AACxC,YAAM,KAAK,cAAc,GAAG,KAAK,eAAe,GAAG,IAAI,UAAU,IAAI;AAErE,YAAM,KAAK;AAAA,QACT,GAAG,KAAK,eAAe,GAAG;AAAA,QAC1B;AAAA,UACE,MAAM;AAAA,UACN,MAAM;AAAA,UACN,MAAM;AAAA,UACN,MAAM;AAAA,UACN,OAAO;AAAA,QACT;AAAA,QACA,UAAU;AAAA,MACZ;AAEA,YAAM,KAAK;AAAA,QACT,GAAG,KAAK,eAAe,GAAG;AAAA,QAC1B;AAAA,UACE,MAAM;AAAA,UACN,MAAM;AAAA,UACN,MAAM;AAAA,UACN,MAAM;AAAA,UACN,OAAO;AAAA,QACT;AAAA,SACA,kBAAa,UAAU,MAAM,MAA7B,YAAkC;AAAA,MACpC;AAEA,YAAM,KAAK;AAAA,QACT,GAAG,KAAK,eAAe,GAAG;AAAA,QAC1B;AAAA,UACE,MAAM;AAAA,UACN,MAAM;AAAA,UACN,MAAM;AAAA,UACN,MAAM;AAAA,UACN,KAAK;AAAA,UACL,KAAK;AAAA,UACL,MAAM;AAAA,UACN,OAAO;AAAA,QACT;AAAA,QACA,UAAU;AAAA,MACZ;AAEA,YAAM,KAAK;AAAA,QACT,GAAG,KAAK,eAAe,GAAG;AAAA,QAC1B;AAAA,UACE,MAAM;AAAA,UACN,MAAM;AAAA,UACN,MAAM;AAAA,UACN,MAAM;AAAA,UACN,MAAM;AAAA,UACN,OAAO;AAAA,QACT;AAAA,QACA,UAAU;AAAA,MACZ;AAEA,YAAM,KAAK;AAAA,QACT,GAAG,KAAK,eAAe,GAAG;AAAA,QAC1B;AAAA,UACE,MAAM;AAAA,UACN,MAAM;AAAA,UACN,MAAM;AAAA,UACN,MAAM;AAAA,UACN,OAAO;AAAA,QACT;AAAA,QACA,UAAU;AAAA,MACZ;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAc,cAAc,IAAY,MAA6B;AACnE,UAAM,KAAK,QAAQ,wBAAwB,IAAI;AAAA,MAC7C,MAAM;AAAA,MACN,QAAQ,EAAE,KAAK;AAAA,MACf,QAAQ,CAAC;AAAA,IACX,CAAC;AAAA,EACH;AAAA,EAEA,MAAc,sBAAsB,IAA2B;AAC7D,QAAI;AACF,YAAM,MAAM,MAAM,KAAK,QAAQ,eAAe,EAAE;AAChD,UAAI,KAAK;AACP,cAAM,KAAK,QAAQ,eAAe,IAAI,EAAE,WAAW,KAAK,CAAC;AAAA,MAC3D;AAAA,IACF,QAAQ;AAAA,IAER;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAc,oBACZ,OACA,IACe;AAr7BnB;AAs7BI,UAAM,KAAK;AAAA,MACT,GAAG,KAAK;AAAA,MACR;AAAA,QACE,MAAM;AAAA,QACN,MAAM;AAAA,QACN,MAAM;AAAA,QACN,MAAM;AAAA,QACN,OAAO;AAAA,MACT;AAAA,OACA,8BAAK,OAAL,YAAW;AAAA,IACb;AACA,UAAM,KAAK;AAAA,MACT,GAAG,KAAK;AAAA,MACR;AAAA,QACE,MAAM;AAAA,QACN,MAAM;AAAA,QACN,MAAM;AAAA,QACN,MAAM;AAAA,QACN,OAAO;AAAA,MACT;AAAA,OACA,8BAAK,OAAL,YAAW;AAAA,IACb;AACA,UAAM,KAAK;AAAA,MACT,GAAG,KAAK;AAAA,MACR;AAAA,QACE,MAAM;AAAA,QACN,MAAM;AAAA,QACN,MAAM;AAAA,QACN,MAAM;AAAA,QACN,OAAO;AAAA,MACT;AAAA,OACA,8BAAK,OAAL,YAAW;AAAA,IACb;AAAA,EACF;AAAA,EAEA,MAAc,kBACZ,IACA,QACA,OACe;AACf,UAAM,KAAK,QAAQ,wBAAwB,IAAI;AAAA,MAC7C,MAAM;AAAA,MACN;AAAA,MACA,QAAQ,CAAC;AAAA,IACX,CAAC;AACD,UAAM,KAAK,QAAQ,cAAc,IAAI,EAAE,KAAK,OAAO,KAAK,KAAK,CAAC;AAAA,EAChE;AAAA,EAEQ,kBACN,OACe;AACf,QAAI,CAAC,OAAO;AACV,aAAO;AAAA,IACT;AACA,UAAM,SAAS,OAAO,OAAO,KAAK,EAAE;AAAA,MAClC,CAAC,MAAM,OAAO,MAAM,YAAY,SAAS,CAAC;AAAA,IAC5C;AACA,QAAI,OAAO,WAAW,GAAG;AACvB,aAAO;AAAA,IACT;AACA,WAAO,KAAK,CAAC,GAAG,MAAM,IAAI,CAAC;AAC3B,UAAM,OAAO,OAAO,MAAM,GAAG,CAAC;AAC9B,UAAM,MAAM,KAAK,OAAO,CAAC,KAAK,MAAM,MAAM,GAAG,CAAC,IAAI,KAAK;AACvD,WAAO,KAAK,MAAM,MAAM,EAAE,IAAI;AAAA,EAChC;AAAA,EAEQ,aAAa,SAAyB;AAC5C,UAAM,IAAI,KAAK,MAAM,UAAU,KAAK;AACpC,UAAM,IAAI,KAAK,MAAO,UAAU,QAAS,IAAI;AAC7C,UAAM,IAAI,KAAK,MAAO,UAAU,OAAQ,EAAE;AAC1C,UAAM,QAAkB,CAAC;AACzB,QAAI,IAAI,GAAG;AACT,YAAM,KAAK,GAAG,CAAC,GAAG;AAAA,IACpB;AACA,QAAI,IAAI,GAAG;AACT,YAAM,KAAK,GAAG,CAAC,GAAG;AAAA,IACpB;AACA,QAAI,IAAI,KAAK,MAAM,WAAW,GAAG;AAC/B,YAAM,KAAK,GAAG,CAAC,GAAG;AAAA,IACpB;AACA,WAAO,MAAM,KAAK,GAAG;AAAA,EACvB;AACF;",
6
6
  "names": []
7
7
  }
package/io-package.json CHANGED
@@ -1,8 +1,34 @@
1
1
  {
2
2
  "common": {
3
3
  "name": "beszel",
4
- "version": "0.2.4",
4
+ "version": "0.2.6",
5
5
  "news": {
6
+ "0.2.6": {
7
+ "en": "Use node: prefix for built-in modules (S5043)",
8
+ "de": "node:-Präfix für eingebaute Module (S5043)",
9
+ "ru": "Префикс node: для встроенных модулей (S5043)",
10
+ "pt": "Prefixo node: para módulos nativos (S5043)",
11
+ "nl": "node: prefix voor ingebouwde modules (S5043)",
12
+ "fr": "Préfixe node: pour les modules natifs (S5043)",
13
+ "it": "Prefisso node: per moduli nativi (S5043)",
14
+ "es": "Prefijo node: para módulos nativos (S5043)",
15
+ "pl": "Prefiks node: dla wbudowanych modułów (S5043)",
16
+ "uk": "Префікс node: для вбудованих модулів (S5043)",
17
+ "zh-cn": "为内置模块使用node:前缀 (S5043)"
18
+ },
19
+ "0.2.5": {
20
+ "en": "Restore standard GitHub-based tests, remove CHANGELOG.md, add FORBIDDEN_CHARS reference, fix Dependabot cooldown",
21
+ "de": "Standard GitHub-Tests wiederhergestellt, CHANGELOG.md entfernt, FORBIDDEN_CHARS-Referenz, Dependabot-Cooldown",
22
+ "ru": "Восстановлены стандартные тесты GitHub, удалён CHANGELOG.md, добавлена ссылка FORBIDDEN_CHARS, cooldown Dependabot",
23
+ "pt": "Restaurados testes padrão GitHub, removido CHANGELOG.md, referência FORBIDDEN_CHARS, cooldown Dependabot",
24
+ "nl": "Standaard GitHub-tests hersteld, CHANGELOG.md verwijderd, FORBIDDEN_CHARS-referentie, Dependabot-cooldown",
25
+ "fr": "Tests GitHub standard restaurés, CHANGELOG.md supprimé, référence FORBIDDEN_CHARS, cooldown Dependabot",
26
+ "it": "Ripristinati test GitHub standard, rimosso CHANGELOG.md, riferimento FORBIDDEN_CHARS, cooldown Dependabot",
27
+ "es": "Restauradas pruebas estándar GitHub, eliminado CHANGELOG.md, referencia FORBIDDEN_CHARS, cooldown Dependabot",
28
+ "pl": "Przywrócono standardowe testy GitHub, usunięto CHANGELOG.md, referencja FORBIDDEN_CHARS, cooldown Dependabot",
29
+ "uk": "Відновлено стандартні тести GitHub, видалено CHANGELOG.md, посилання FORBIDDEN_CHARS, cooldown Dependabot",
30
+ "zh-cn": "恢复标准GitHub测试,删除CHANGELOG.md,添加FORBIDDEN_CHARS引用,修复Dependabot冷却"
31
+ },
6
32
  "0.2.4": {
7
33
  "en": "Cleaner log messages, remove redundant adapter name prefix",
8
34
  "de": "Sauberere Log-Meldungen, redundanten Adapter-Namen-Präfix entfernt",
@@ -67,32 +93,6 @@
67
93
  "pl": "Metody timera adaptera, synchroniczne onUnload, zakładka O nas zintegrowana z Połączenie, CI Windows/macOS, pełny tekst licencji MIT w README",
68
94
  "uk": "Методи таймера адаптера, синхронний onUnload, вкладка Про програму об'єднана з Підключення, CI для Windows/macOS, повний текст ліцензії MIT в README",
69
95
  "zh-cn": "使用适配器定时器方法,同步onUnload,将关于标签合并到连接标签,添加Windows/macOS CI,README中完整MIT许可证文本"
70
- },
71
- "0.1.9": {
72
- "en": "Logging cleanup: stale system removal moved to debug level",
73
- "de": "Logging-Bereinigung: Entfernung veralteter Systeme auf Debug-Level verschoben",
74
- "ru": "Очистка логирования: удаление устаревших систем перенесено на уровень debug",
75
- "pt": "Limpeza de logging: remoção de sistemas obsoletos movida para nível debug",
76
- "nl": "Logging opruiming: verwijdering van verouderde systemen verplaatst naar debug-niveau",
77
- "fr": "Nettoyage du logging: suppression des systèmes obsolètes déplacée au niveau debug",
78
- "it": "Pulizia logging: rimozione di sistemi obsoleti spostata al livello debug",
79
- "es": "Limpieza de logging: eliminación de sistemas obsoletos movida al nivel debug",
80
- "pl": "Czyszczenie logowania: usuwanie przestarzałych systemów przeniesione na poziom debug",
81
- "uk": "Очищення логування: видалення застарілих систем переміщено на рівень debug",
82
- "zh-cn": "日志清理:将过期系统的删除移至debug级别"
83
- },
84
- "0.1.8": {
85
- "en": "Add online/offline indicator to system device folders",
86
- "de": "Online/Offline-Anzeige zu System-Geräteordnern hinzugefügt",
87
- "ru": "Добавлен индикатор онлайн/офлайн в папки системных устройств",
88
- "pt": "Adicionar indicador online/offline às pastas de dispositivos do sistema",
89
- "nl": "Online/offline-indicator toegevoegd aan systeemapparaapmappen",
90
- "fr": "Ajout d'un indicateur en ligne/hors ligne aux dossiers d'appareils système",
91
- "it": "Aggiunto indicatore online/offline alle cartelle dei dispositivi di sistema",
92
- "es": "Agregar indicador en línea/fuera de línea a las carpetas de dispositivos del sistema",
93
- "pl": "Dodano wskaźnik online/offline do folderów urządzeń systemowych",
94
- "uk": "Додано індикатор онлайн/офлайн до папок системних пристроїв",
95
- "zh-cn": "为系统设备文件夹添加在线/离线指示器"
96
96
  }
97
97
  },
98
98
  "titleLang": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "iobroker.beszel",
3
- "version": "0.2.4",
3
+ "version": "0.2.6",
4
4
  "description": "ioBroker adapter for Beszel server monitoring",
5
5
  "author": {
6
6
  "name": "krobi",
@@ -51,12 +51,13 @@
51
51
  "scripts": {
52
52
  "prebuild": "rimraf build",
53
53
  "build": "build-adapter ts",
54
- "build:test": "tsc -p tsconfig.test.json",
54
+ "build:test": "rm -rf ./build && tsc -p tsconfig.test.json",
55
55
  "watch": "build-adapter ts --watch",
56
56
  "check": "tsc --noEmit",
57
- "test:package": "npm run build:test && mocha --exit \"build/test/testPackageFiles.js\"",
58
- "test:integration": "npm run build:test && mocha --exit \"build/test/**/*.js\"",
59
- "test": "npm run build:test && mocha --exit \"build/test/**/*.js\"",
57
+ "test:ts": "npm run build:test && mocha --exit \"build/test/testBeszelClient.js\" \"build/test/testStateManager.js\"",
58
+ "test:package": "mocha test/package --exit",
59
+ "test:integration": "mocha test/integration --exit",
60
+ "test": "npm run test:ts && npm run test:package",
60
61
  "lint": "eslint",
61
62
  "lint:fix": "eslint --fix",
62
63
  "release": "release-script"