iobroker.proscenic 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +93 -0
- package/admin/i18n/de.json +9 -0
- package/admin/i18n/en.json +9 -0
- package/admin/i18n/es.json +9 -0
- package/admin/i18n/fr.json +9 -0
- package/admin/i18n/it.json +9 -0
- package/admin/i18n/nl.json +9 -0
- package/admin/i18n/pl.json +9 -0
- package/admin/i18n/pt.json +9 -0
- package/admin/i18n/ru.json +9 -0
- package/admin/i18n/uk.json +9 -0
- package/admin/i18n/zh-cn.json +9 -0
- package/admin/jsonConfig.json +85 -0
- package/admin/proscenic-m7-pro.png +0 -0
- package/admin/proscenic.png +0 -0
- package/build/domain/commands.js +129 -0
- package/build/domain/commands.js.map +7 -0
- package/build/domain/consumables.js +63 -0
- package/build/domain/consumables.js.map +7 -0
- package/build/domain/live-map.js +829 -0
- package/build/domain/live-map.js.map +7 -0
- package/build/domain/maintenance-message.js +128 -0
- package/build/domain/maintenance-message.js.map +7 -0
- package/build/domain/map.js +58 -0
- package/build/domain/map.js.map +7 -0
- package/build/domain/reconnect-policy.js +45 -0
- package/build/domain/reconnect-policy.js.map +7 -0
- package/build/domain/status.js +108 -0
- package/build/domain/status.js.map +7 -0
- package/build/main.js +529 -0
- package/build/main.js.map +7 -0
- package/build/objects/object-definitions.js +214 -0
- package/build/objects/object-definitions.js.map +7 -0
- package/build/objects/projector.js +231 -0
- package/build/objects/projector.js.map +7 -0
- package/build/protocol/crypto.js +81 -0
- package/build/protocol/crypto.js.map +7 -0
- package/build/protocol/gateway-client.js +136 -0
- package/build/protocol/gateway-client.js.map +7 -0
- package/build/protocol/rest-client.js +184 -0
- package/build/protocol/rest-client.js.map +7 -0
- package/build/protocol/types.js +17 -0
- package/build/protocol/types.js.map +7 -0
- package/io-package.json +125 -0
- package/package.json +80 -0
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/protocol/crypto.ts"],
|
|
4
|
+
"sourcesContent": ["import { createDecipheriv, createHash } from \"node:crypto\";\n\nexport const FRAME_DELIMITER = \"#\\t#\";\n\nexport function md5Hex(value: string): string {\n\treturn createHash(\"md5\").update(value, \"utf8\").digest(\"hex\");\n}\n\nexport function gatewayHandshake(token: string, serial: string): string {\n\treturn (\n\t\tJSON.stringify({\n\t\t\tdata: {\n\t\t\t\ttoken,\n\t\t\t\tsn: serial,\n\t\t\t},\n\t\t\tinfoType: 70001,\n\t\t}) + FRAME_DELIMITER\n\t);\n}\n\nexport function splitFrames(buffer: string): { frames: string[]; rest: string } {\n\tconst frames: string[] = [];\n\tlet rest = buffer;\n\n\tfor (;;) {\n\t\tconst index = rest.indexOf(FRAME_DELIMITER);\n\t\tif (index === -1) {\n\t\t\treturn { frames, rest };\n\t\t}\n\n\t\tframes.push(rest.slice(0, index));\n\t\trest = rest.slice(index + FRAME_DELIMITER.length);\n\t}\n}\n\nexport function decryptGatewayPayload(base64Payload: string, token: string): string {\n\tconst key = Buffer.from(token, \"utf8\");\n\tif (![16, 24, 32].includes(key.length)) {\n\t\tthrow new Error(\"Unsupported AES key length\");\n\t}\n\n\tconst decipher = createDecipheriv(`aes-${key.length * 8}-ecb`, key, null);\n\tdecipher.setAutoPadding(true);\n\n\tconst decrypted = Buffer.concat([decipher.update(Buffer.from(base64Payload, \"base64\")), decipher.final()]);\n\n\treturn decrypted.toString(\"utf8\").replace(/\\0+$/u, \"\");\n}\n\nexport function parseJsonObject(text: string): Record<string, unknown> {\n\tconst parsed: unknown = JSON.parse(text);\n\tif (parsed === null || typeof parsed !== \"object\" || Array.isArray(parsed)) {\n\t\tthrow new Error(\"Expected JSON object\");\n\t}\n\n\treturn parsed as Record<string, unknown>;\n}\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,yBAA6C;AAEtC,MAAM,kBAAkB;AAExB,SAAS,OAAO,OAAuB;AAC7C,aAAO,+BAAW,KAAK,EAAE,OAAO,OAAO,MAAM,EAAE,OAAO,KAAK;AAC5D;AAEO,SAAS,iBAAiB,OAAe,QAAwB;AACvE,SACC,KAAK,UAAU;AAAA,IACd,MAAM;AAAA,MACL;AAAA,MACA,IAAI;AAAA,IACL;AAAA,IACA,UAAU;AAAA,EACX,CAAC,IAAI;AAEP;AAEO,SAAS,YAAY,QAAoD;AAC/E,QAAM,SAAmB,CAAC;AAC1B,MAAI,OAAO;AAEX,aAAS;AACR,UAAM,QAAQ,KAAK,QAAQ,eAAe;AAC1C,QAAI,UAAU,IAAI;AACjB,aAAO,EAAE,QAAQ,KAAK;AAAA,IACvB;AAEA,WAAO,KAAK,KAAK,MAAM,GAAG,KAAK,CAAC;AAChC,WAAO,KAAK,MAAM,QAAQ,gBAAgB,MAAM;AAAA,EACjD;AACD;AAEO,SAAS,sBAAsB,eAAuB,OAAuB;AACnF,QAAM,MAAM,OAAO,KAAK,OAAO,MAAM;AACrC,MAAI,CAAC,CAAC,IAAI,IAAI,EAAE,EAAE,SAAS,IAAI,MAAM,GAAG;AACvC,UAAM,IAAI,MAAM,4BAA4B;AAAA,EAC7C;AAEA,QAAM,eAAW,qCAAiB,OAAO,IAAI,SAAS,CAAC,QAAQ,KAAK,IAAI;AACxE,WAAS,eAAe,IAAI;AAE5B,QAAM,YAAY,OAAO,OAAO,CAAC,SAAS,OAAO,OAAO,KAAK,eAAe,QAAQ,CAAC,GAAG,SAAS,MAAM,CAAC,CAAC;AAEzG,SAAO,UAAU,SAAS,MAAM,EAAE,QAAQ,SAAS,EAAE;AACtD;AAEO,SAAS,gBAAgB,MAAuC;AACtE,QAAM,SAAkB,KAAK,MAAM,IAAI;AACvC,MAAI,WAAW,QAAQ,OAAO,WAAW,YAAY,MAAM,QAAQ,MAAM,GAAG;AAC3E,UAAM,IAAI,MAAM,sBAAsB;AAAA,EACvC;AAEA,SAAO;AACR;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
var gateway_client_exports = {};
|
|
20
|
+
__export(gateway_client_exports, {
|
|
21
|
+
ProscenicGatewayClient: () => ProscenicGatewayClient,
|
|
22
|
+
parseGatewayFrame: () => parseGatewayFrame,
|
|
23
|
+
parseGatewayFrameResult: () => parseGatewayFrameResult
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(gateway_client_exports);
|
|
26
|
+
var import_node_net = require("node:net");
|
|
27
|
+
var import_crypto = require("./crypto");
|
|
28
|
+
class ProscenicGatewayClient {
|
|
29
|
+
constructor(options) {
|
|
30
|
+
this.options = options;
|
|
31
|
+
}
|
|
32
|
+
socket = new import_node_net.Socket();
|
|
33
|
+
buffer = "";
|
|
34
|
+
closed = false;
|
|
35
|
+
connect() {
|
|
36
|
+
this.socket.setTimeout(this.options.timeoutMs);
|
|
37
|
+
this.socket.on("connect", () => {
|
|
38
|
+
this.socket.setTimeout(0);
|
|
39
|
+
this.socket.write((0, import_crypto.gatewayHandshake)(this.options.token, this.options.serial), "utf8");
|
|
40
|
+
this.options.onConnect();
|
|
41
|
+
});
|
|
42
|
+
this.socket.on("data", (chunk) => this.handleData(chunk));
|
|
43
|
+
this.socket.on("timeout", () => this.closeWithError(new Error("Gateway socket timeout")));
|
|
44
|
+
this.socket.on("error", (error) => this.closeWithError(error));
|
|
45
|
+
this.socket.on("close", () => this.close());
|
|
46
|
+
this.socket.connect(this.options.endpoint.port, this.options.endpoint.host);
|
|
47
|
+
}
|
|
48
|
+
destroy() {
|
|
49
|
+
this.closed = true;
|
|
50
|
+
this.socket.removeAllListeners();
|
|
51
|
+
this.socket.destroy();
|
|
52
|
+
this.buffer = "";
|
|
53
|
+
}
|
|
54
|
+
handleData(chunk) {
|
|
55
|
+
this.buffer += chunk.toString("utf8");
|
|
56
|
+
if (Buffer.byteLength(this.buffer, "utf8") > this.options.maxBufferBytes) {
|
|
57
|
+
this.closeWithError(new Error("Gateway buffer limit exceeded"));
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
const split = (0, import_crypto.splitFrames)(this.buffer);
|
|
61
|
+
this.buffer = split.rest;
|
|
62
|
+
for (const frame of split.frames) {
|
|
63
|
+
if (frame.trim().length === 0) {
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
const result = parseGatewayFrameResult(frame, this.options.token);
|
|
67
|
+
if (result.error) {
|
|
68
|
+
this.closeWithError(result.error);
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
const event = result.event;
|
|
72
|
+
if (event) {
|
|
73
|
+
this.options.onEvent(event);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
closeWithError(error) {
|
|
78
|
+
if (!this.closed) {
|
|
79
|
+
this.options.onError(error);
|
|
80
|
+
}
|
|
81
|
+
this.close();
|
|
82
|
+
}
|
|
83
|
+
close() {
|
|
84
|
+
if (this.closed) {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
this.closed = true;
|
|
88
|
+
this.socket.removeAllListeners();
|
|
89
|
+
this.socket.destroy();
|
|
90
|
+
this.buffer = "";
|
|
91
|
+
this.options.onClose();
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
function parseGatewayFrameResult(frame, token) {
|
|
95
|
+
try {
|
|
96
|
+
return {
|
|
97
|
+
event: parseGatewayFrame(frame, token)
|
|
98
|
+
};
|
|
99
|
+
} catch (error) {
|
|
100
|
+
return {
|
|
101
|
+
error: error instanceof Error ? error : new Error(String(error))
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
function parseGatewayFrame(frame, token) {
|
|
106
|
+
const envelope = (0, import_crypto.parseJsonObject)(frame);
|
|
107
|
+
if (envelope.encrypt !== true && !("encrypt" in envelope)) {
|
|
108
|
+
return {
|
|
109
|
+
encrypted: false,
|
|
110
|
+
infoType: envelope.infoType,
|
|
111
|
+
data: envelope.data
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
if (typeof envelope.data !== "string") {
|
|
115
|
+
return void 0;
|
|
116
|
+
}
|
|
117
|
+
if (!token) {
|
|
118
|
+
return {
|
|
119
|
+
encrypted: true
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
const decryptedText = (0, import_crypto.decryptGatewayPayload)(envelope.data, token);
|
|
123
|
+
const decrypted = (0, import_crypto.parseJsonObject)(decryptedText);
|
|
124
|
+
return {
|
|
125
|
+
encrypted: true,
|
|
126
|
+
infoType: decrypted.infoType,
|
|
127
|
+
data: decrypted.data
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
131
|
+
0 && (module.exports = {
|
|
132
|
+
ProscenicGatewayClient,
|
|
133
|
+
parseGatewayFrame,
|
|
134
|
+
parseGatewayFrameResult
|
|
135
|
+
});
|
|
136
|
+
//# sourceMappingURL=gateway-client.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/protocol/gateway-client.ts"],
|
|
4
|
+
"sourcesContent": ["import { Socket } from \"node:net\";\nimport { decryptGatewayPayload, gatewayHandshake, parseJsonObject, splitFrames } from \"./crypto\";\nimport type { GatewayEndpoint, GatewayEvent } from \"./types\";\n\nexport interface GatewayClientOptions {\n\tendpoint: GatewayEndpoint;\n\ttoken: string;\n\tserial: string;\n\ttimeoutMs: number;\n\tmaxBufferBytes: number;\n\tonConnect: () => void;\n\tonEvent: (event: GatewayEvent) => void;\n\tonClose: () => void;\n\tonError: (error: Error) => void;\n}\n\nexport class ProscenicGatewayClient {\n\tprivate readonly socket = new Socket();\n\tprivate buffer = \"\";\n\tprivate closed = false;\n\n\tpublic constructor(private readonly options: GatewayClientOptions) {}\n\n\tpublic connect(): void {\n\t\tthis.socket.setTimeout(this.options.timeoutMs);\n\t\tthis.socket.on(\"connect\", () => {\n\t\t\tthis.socket.setTimeout(0);\n\t\t\tthis.socket.write(gatewayHandshake(this.options.token, this.options.serial), \"utf8\");\n\t\t\tthis.options.onConnect();\n\t\t});\n\t\tthis.socket.on(\"data\", (chunk: Buffer) => this.handleData(chunk));\n\t\tthis.socket.on(\"timeout\", () => this.closeWithError(new Error(\"Gateway socket timeout\")));\n\t\tthis.socket.on(\"error\", error => this.closeWithError(error));\n\t\tthis.socket.on(\"close\", () => this.close());\n\t\tthis.socket.connect(this.options.endpoint.port, this.options.endpoint.host);\n\t}\n\n\tpublic destroy(): void {\n\t\tthis.closed = true;\n\t\tthis.socket.removeAllListeners();\n\t\tthis.socket.destroy();\n\t\tthis.buffer = \"\";\n\t}\n\n\tprivate handleData(chunk: Buffer): void {\n\t\tthis.buffer += chunk.toString(\"utf8\");\n\t\tif (Buffer.byteLength(this.buffer, \"utf8\") > this.options.maxBufferBytes) {\n\t\t\tthis.closeWithError(new Error(\"Gateway buffer limit exceeded\"));\n\t\t\treturn;\n\t\t}\n\n\t\tconst split = splitFrames(this.buffer);\n\t\tthis.buffer = split.rest;\n\n\t\tfor (const frame of split.frames) {\n\t\t\tif (frame.trim().length === 0) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst result = parseGatewayFrameResult(frame, this.options.token);\n\t\t\tif (result.error) {\n\t\t\t\tthis.closeWithError(result.error);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst event = result.event;\n\t\t\tif (event) {\n\t\t\t\tthis.options.onEvent(event);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate closeWithError(error: Error): void {\n\t\tif (!this.closed) {\n\t\t\tthis.options.onError(error);\n\t\t}\n\t\tthis.close();\n\t}\n\n\tprivate close(): void {\n\t\tif (this.closed) {\n\t\t\treturn;\n\t\t}\n\n\t\tthis.closed = true;\n\t\tthis.socket.removeAllListeners();\n\t\tthis.socket.destroy();\n\t\tthis.buffer = \"\";\n\t\tthis.options.onClose();\n\t}\n}\n\nexport interface GatewayFrameParseResult {\n\tevent?: GatewayEvent;\n\terror?: Error;\n}\n\nexport function parseGatewayFrameResult(frame: string, token?: string): GatewayFrameParseResult {\n\ttry {\n\t\treturn {\n\t\t\tevent: parseGatewayFrame(frame, token),\n\t\t};\n\t} catch (error) {\n\t\treturn {\n\t\t\terror: error instanceof Error ? error : new Error(String(error)),\n\t\t};\n\t}\n}\n\nexport function parseGatewayFrame(frame: string, token?: string): GatewayEvent | undefined {\n\tconst envelope = parseJsonObject(frame);\n\tif (envelope.encrypt !== true && !(\"encrypt\" in envelope)) {\n\t\treturn {\n\t\t\tencrypted: false,\n\t\t\tinfoType: envelope.infoType,\n\t\t\tdata: envelope.data,\n\t\t};\n\t}\n\n\tif (typeof envelope.data !== \"string\") {\n\t\treturn undefined;\n\t}\n\n\tif (!token) {\n\t\treturn {\n\t\t\tencrypted: true,\n\t\t};\n\t}\n\n\tconst decryptedText = decryptGatewayPayload(envelope.data, token);\n\tconst decrypted = parseJsonObject(decryptedText);\n\n\treturn {\n\t\tencrypted: true,\n\t\tinfoType: decrypted.infoType,\n\t\tdata: decrypted.data,\n\t};\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
var rest_client_exports = {};
|
|
20
|
+
__export(rest_client_exports, {
|
|
21
|
+
ProscenicRestClient: () => ProscenicRestClient
|
|
22
|
+
});
|
|
23
|
+
module.exports = __toCommonJS(rest_client_exports);
|
|
24
|
+
var import_node_https = require("node:https");
|
|
25
|
+
var import_crypto = require("./crypto");
|
|
26
|
+
const REGION_BASE_URLS = {
|
|
27
|
+
eu: "https://mobile.proscenic.com.de"
|
|
28
|
+
};
|
|
29
|
+
class ProscenicRestClient {
|
|
30
|
+
constructor(options) {
|
|
31
|
+
this.options = options;
|
|
32
|
+
this.baseUrl = new URL(REGION_BASE_URLS[options.region]);
|
|
33
|
+
}
|
|
34
|
+
baseUrl;
|
|
35
|
+
async login() {
|
|
36
|
+
var _a;
|
|
37
|
+
const response = await this.postJson(
|
|
38
|
+
"/user/login",
|
|
39
|
+
{
|
|
40
|
+
state: "\u6B27\u6D32",
|
|
41
|
+
countryCode: "49",
|
|
42
|
+
appVer: "1.7.8",
|
|
43
|
+
type: "2",
|
|
44
|
+
os: "IOS",
|
|
45
|
+
password: (0, import_crypto.md5Hex)(this.options.password),
|
|
46
|
+
registrationId: "13165ffa4eb156ac484",
|
|
47
|
+
language: "EN",
|
|
48
|
+
username: this.options.username,
|
|
49
|
+
pwd: this.options.password
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
c: "338",
|
|
53
|
+
lan: "en",
|
|
54
|
+
os: "i",
|
|
55
|
+
"User-Agent": "ProscenicHome/1.7.8",
|
|
56
|
+
v: "1.7.8"
|
|
57
|
+
}
|
|
58
|
+
);
|
|
59
|
+
if (!((_a = response.data) == null ? void 0 : _a.token)) {
|
|
60
|
+
throw new Error("Login succeeded without token");
|
|
61
|
+
}
|
|
62
|
+
return response.data.token;
|
|
63
|
+
}
|
|
64
|
+
async listDevices() {
|
|
65
|
+
var _a, _b;
|
|
66
|
+
const path = `/user/getEquips/${encodeURIComponent(this.options.username)}`;
|
|
67
|
+
const response = await this.postForm(path, {
|
|
68
|
+
username: this.options.username
|
|
69
|
+
});
|
|
70
|
+
return (_b = (_a = response.data) == null ? void 0 : _a.content) != null ? _b : [];
|
|
71
|
+
}
|
|
72
|
+
async getGateway(token, serial) {
|
|
73
|
+
var _a;
|
|
74
|
+
const response = await this.postForm(
|
|
75
|
+
"/appInit/getSockAddr",
|
|
76
|
+
{
|
|
77
|
+
username: this.options.username,
|
|
78
|
+
sn: serial
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
token
|
|
82
|
+
}
|
|
83
|
+
);
|
|
84
|
+
return (_a = response.data) != null ? _a : {};
|
|
85
|
+
}
|
|
86
|
+
async requestConsumables(token, serial) {
|
|
87
|
+
const path = `/instructions/cmd21015/${encodeURIComponent(serial)}`;
|
|
88
|
+
return this.postForm(path, { username: this.options.username }, { token });
|
|
89
|
+
}
|
|
90
|
+
async getMaintenanceHistory(token, serial) {
|
|
91
|
+
var _a;
|
|
92
|
+
const path = `/app/cleanRobot/20003/${encodeURIComponent(serial)}`;
|
|
93
|
+
const response = await this.postForm(
|
|
94
|
+
path,
|
|
95
|
+
{
|
|
96
|
+
username: this.options.username,
|
|
97
|
+
language: "EN",
|
|
98
|
+
model: "M7",
|
|
99
|
+
page: "0",
|
|
100
|
+
size: "10"
|
|
101
|
+
},
|
|
102
|
+
{ token }
|
|
103
|
+
);
|
|
104
|
+
return (_a = response.data) != null ? _a : {};
|
|
105
|
+
}
|
|
106
|
+
async sendCommand(token, command) {
|
|
107
|
+
if (command.contentType) {
|
|
108
|
+
return this.request(command.path, command.body, {
|
|
109
|
+
"Content-Type": command.contentType,
|
|
110
|
+
token
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
return this.postFormPath(command.path, command.body, { token });
|
|
114
|
+
}
|
|
115
|
+
async postJson(path, body, headers = {}) {
|
|
116
|
+
return this.request(path, JSON.stringify(body), {
|
|
117
|
+
"Content-Type": "application/json",
|
|
118
|
+
...headers
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
async postForm(path, body, headers = {}) {
|
|
122
|
+
return this.postFormPath(path, new URLSearchParams(body).toString(), headers);
|
|
123
|
+
}
|
|
124
|
+
async postFormPath(path, body, headers = {}) {
|
|
125
|
+
return this.request(path, body, {
|
|
126
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
127
|
+
...headers
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
async request(path, body, headers) {
|
|
131
|
+
const url = new URL(path, this.baseUrl);
|
|
132
|
+
return new Promise((resolve, reject) => {
|
|
133
|
+
const rejectWithError = (error) => {
|
|
134
|
+
reject(error instanceof Error ? error : new Error(String(error)));
|
|
135
|
+
};
|
|
136
|
+
const req = (0, import_node_https.request)(
|
|
137
|
+
url,
|
|
138
|
+
{
|
|
139
|
+
method: "POST",
|
|
140
|
+
timeout: this.options.timeoutMs,
|
|
141
|
+
headers: {
|
|
142
|
+
"Content-Length": Buffer.byteLength(body).toString(),
|
|
143
|
+
...headers
|
|
144
|
+
}
|
|
145
|
+
},
|
|
146
|
+
(res) => {
|
|
147
|
+
const chunks = [];
|
|
148
|
+
res.on("data", (chunk) => chunks.push(chunk));
|
|
149
|
+
res.on("end", () => {
|
|
150
|
+
var _a;
|
|
151
|
+
const text = Buffer.concat(chunks).toString("utf8");
|
|
152
|
+
try {
|
|
153
|
+
const parsed = JSON.parse(text);
|
|
154
|
+
if (res.statusCode !== 200) {
|
|
155
|
+
rejectWithError(new Error(`HTTP ${(_a = res.statusCode) != null ? _a : "unknown"}`));
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
if (parsed.code !== void 0 && parsed.code !== 0) {
|
|
159
|
+
rejectWithError(new Error(parsed.errorMsg || parsed.msg || `API code ${parsed.code}`));
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
resolve(parsed);
|
|
163
|
+
} catch (error) {
|
|
164
|
+
rejectWithError(error);
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
);
|
|
169
|
+
req.on("timeout", () => {
|
|
170
|
+
req.destroy(new Error(`Request timed out after ${this.options.timeoutMs} ms`));
|
|
171
|
+
});
|
|
172
|
+
req.on("error", (error) => {
|
|
173
|
+
rejectWithError(error);
|
|
174
|
+
});
|
|
175
|
+
req.write(body);
|
|
176
|
+
req.end();
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
181
|
+
0 && (module.exports = {
|
|
182
|
+
ProscenicRestClient
|
|
183
|
+
});
|
|
184
|
+
//# sourceMappingURL=rest-client.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/protocol/rest-client.ts"],
|
|
4
|
+
"sourcesContent": ["import { request as httpsRequest } from \"node:https\";\nimport type { CommandRequest } from \"../domain/commands\";\nimport { md5Hex } from \"./crypto\";\nimport type {\n\tDeviceListData,\n\tDeviceRecord,\n\tGatewayData,\n\tLoginData,\n\tMaintenanceHistoryData,\n\tProscenicEnvelope,\n} from \"./types\";\n\nconst REGION_BASE_URLS = {\n\teu: \"https://mobile.proscenic.com.de\",\n} as const;\n\nexport type ProscenicRegion = keyof typeof REGION_BASE_URLS;\n\nexport interface ProscenicClientOptions {\n\tusername: string;\n\tpassword: string;\n\tregion: ProscenicRegion;\n\ttimeoutMs: number;\n}\n\nexport class ProscenicRestClient {\n\tprivate readonly baseUrl: URL;\n\n\tpublic constructor(private readonly options: ProscenicClientOptions) {\n\t\tthis.baseUrl = new URL(REGION_BASE_URLS[options.region]);\n\t}\n\n\tpublic async login(): Promise<string> {\n\t\tconst response = await this.postJson<LoginData>(\n\t\t\t\"/user/login\",\n\t\t\t{\n\t\t\t\tstate: \"\u6B27\u6D32\",\n\t\t\t\tcountryCode: \"49\",\n\t\t\t\tappVer: \"1.7.8\",\n\t\t\t\ttype: \"2\",\n\t\t\t\tos: \"IOS\",\n\t\t\t\tpassword: md5Hex(this.options.password),\n\t\t\t\tregistrationId: \"13165ffa4eb156ac484\",\n\t\t\t\tlanguage: \"EN\",\n\t\t\t\tusername: this.options.username,\n\t\t\t\tpwd: this.options.password,\n\t\t\t},\n\t\t\t{\n\t\t\t\tc: \"338\",\n\t\t\t\tlan: \"en\",\n\t\t\t\tos: \"i\",\n\t\t\t\t\"User-Agent\": \"ProscenicHome/1.7.8\",\n\t\t\t\tv: \"1.7.8\",\n\t\t\t},\n\t\t);\n\n\t\tif (!response.data?.token) {\n\t\t\tthrow new Error(\"Login succeeded without token\");\n\t\t}\n\n\t\treturn response.data.token;\n\t}\n\n\tpublic async listDevices(): Promise<DeviceRecord[]> {\n\t\tconst path = `/user/getEquips/${encodeURIComponent(this.options.username)}`;\n\t\tconst response = await this.postForm<DeviceListData>(path, {\n\t\t\tusername: this.options.username,\n\t\t});\n\n\t\treturn response.data?.content ?? [];\n\t}\n\n\tpublic async getGateway(token: string, serial: string): Promise<GatewayData> {\n\t\tconst response = await this.postForm<GatewayData>(\n\t\t\t\"/appInit/getSockAddr\",\n\t\t\t{\n\t\t\t\tusername: this.options.username,\n\t\t\t\tsn: serial,\n\t\t\t},\n\t\t\t{\n\t\t\t\ttoken,\n\t\t\t},\n\t\t);\n\n\t\treturn response.data ?? {};\n\t}\n\n\tpublic async requestConsumables(token: string, serial: string): Promise<ProscenicEnvelope> {\n\t\tconst path = `/instructions/cmd21015/${encodeURIComponent(serial)}`;\n\t\treturn this.postForm(path, { username: this.options.username }, { token });\n\t}\n\n\tpublic async getMaintenanceHistory(token: string, serial: string): Promise<MaintenanceHistoryData> {\n\t\tconst path = `/app/cleanRobot/20003/${encodeURIComponent(serial)}`;\n\t\tconst response = await this.postForm<MaintenanceHistoryData>(\n\t\t\tpath,\n\t\t\t{\n\t\t\t\tusername: this.options.username,\n\t\t\t\tlanguage: \"EN\",\n\t\t\t\tmodel: \"M7\",\n\t\t\t\tpage: \"0\",\n\t\t\t\tsize: \"10\",\n\t\t\t},\n\t\t\t{ token },\n\t\t);\n\n\t\treturn response.data ?? {};\n\t}\n\n\tpublic async sendCommand(token: string, command: CommandRequest): Promise<ProscenicEnvelope> {\n\t\tif (command.contentType) {\n\t\t\treturn this.request(command.path, command.body, {\n\t\t\t\t\"Content-Type\": command.contentType,\n\t\t\t\ttoken,\n\t\t\t});\n\t\t}\n\n\t\treturn this.postFormPath(command.path, command.body, { token });\n\t}\n\n\tprivate async postJson<T>(\n\t\tpath: string,\n\t\tbody: Record<string, unknown>,\n\t\theaders: Record<string, string> = {},\n\t): Promise<ProscenicEnvelope<T>> {\n\t\treturn this.request<T>(path, JSON.stringify(body), {\n\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t...headers,\n\t\t});\n\t}\n\n\tprivate async postForm<T>(\n\t\tpath: string,\n\t\tbody: Record<string, string>,\n\t\theaders: Record<string, string> = {},\n\t): Promise<ProscenicEnvelope<T>> {\n\t\treturn this.postFormPath<T>(path, new URLSearchParams(body).toString(), headers);\n\t}\n\n\tprivate async postFormPath<T>(\n\t\tpath: string,\n\t\tbody: string,\n\t\theaders: Record<string, string> = {},\n\t): Promise<ProscenicEnvelope<T>> {\n\t\treturn this.request<T>(path, body, {\n\t\t\t\"Content-Type\": \"application/x-www-form-urlencoded\",\n\t\t\t...headers,\n\t\t});\n\t}\n\n\tprivate async request<T>(\n\t\tpath: string,\n\t\tbody: string,\n\t\theaders: Record<string, string>,\n\t): Promise<ProscenicEnvelope<T>> {\n\t\tconst url = new URL(path, this.baseUrl);\n\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tconst rejectWithError = (error: unknown): void => {\n\t\t\t\treject(error instanceof Error ? error : new Error(String(error)));\n\t\t\t};\n\t\t\tconst req = httpsRequest(\n\t\t\t\turl,\n\t\t\t\t{\n\t\t\t\t\tmethod: \"POST\",\n\t\t\t\t\ttimeout: this.options.timeoutMs,\n\t\t\t\t\theaders: {\n\t\t\t\t\t\t\"Content-Length\": Buffer.byteLength(body).toString(),\n\t\t\t\t\t\t...headers,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tres => {\n\t\t\t\t\tconst chunks: Buffer[] = [];\n\t\t\t\t\tres.on(\"data\", (chunk: Buffer) => chunks.push(chunk));\n\t\t\t\t\tres.on(\"end\", () => {\n\t\t\t\t\t\tconst text = Buffer.concat(chunks).toString(\"utf8\");\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tconst parsed = JSON.parse(text) as ProscenicEnvelope<T>;\n\t\t\t\t\t\t\tif (res.statusCode !== 200) {\n\t\t\t\t\t\t\t\trejectWithError(new Error(`HTTP ${res.statusCode ?? \"unknown\"}`));\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (parsed.code !== undefined && parsed.code !== 0) {\n\t\t\t\t\t\t\t\trejectWithError(new Error(parsed.errorMsg || parsed.msg || `API code ${parsed.code}`));\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tresolve(parsed);\n\t\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\t\trejectWithError(error);\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t\t},\n\t\t\t);\n\n\t\t\treq.on(\"timeout\", () => {\n\t\t\t\treq.destroy(new Error(`Request timed out after ${this.options.timeoutMs} ms`));\n\t\t\t});\n\n\t\t\treq.on(\"error\", error => {\n\t\t\t\trejectWithError(error);\n\t\t\t});\n\t\t\treq.write(body);\n\t\t\treq.end();\n\t\t});\n\t}\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["httpsRequest"]
|
|
7
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __copyProps = (to, from, except, desc) => {
|
|
7
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
8
|
+
for (let key of __getOwnPropNames(from))
|
|
9
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
10
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
11
|
+
}
|
|
12
|
+
return to;
|
|
13
|
+
};
|
|
14
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
15
|
+
var types_exports = {};
|
|
16
|
+
module.exports = __toCommonJS(types_exports);
|
|
17
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/protocol/types.ts"],
|
|
4
|
+
"sourcesContent": ["export interface ProscenicEnvelope<T = unknown> {\n\tcode?: number;\n\tmsg?: string;\n\terrorMsg?: string;\n\tdata?: T;\n}\n\nexport interface LoginData {\n\ttoken: string;\n}\n\nexport interface DeviceRecord {\n\tcode?: string;\n\tmodel?: string;\n\tname?: string;\n\tsn?: string;\n\tstatus?: boolean;\n}\n\nexport interface DeviceListData {\n\tcontent?: DeviceRecord[];\n}\n\nexport interface GatewayAddress {\n\tip?: string;\n\tport?: number | string;\n}\n\nexport interface GatewayData {\n\taddr_list?: GatewayAddress[];\n}\n\nexport interface MaintenanceHistoryData {\n\tcontent?: unknown[];\n\ttotalElements?: number;\n\ttotalPages?: number;\n\tnumber?: number;\n\tsize?: number;\n}\n\nexport interface GatewayEndpoint {\n\thost: string;\n\tport: number;\n}\n\nexport interface GatewayEvent {\n\tencrypted: boolean;\n\tinfoType?: unknown;\n\tdata?: unknown;\n}\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;AAAA;AAAA;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
package/io-package.json
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
{
|
|
2
|
+
"common": {
|
|
3
|
+
"name": "proscenic",
|
|
4
|
+
"version": "0.1.0",
|
|
5
|
+
"news": {
|
|
6
|
+
"0.1.0": {
|
|
7
|
+
"en": "Added M7 Pro commands, status projection, consumables, maintenance history, and experimental live map rendering",
|
|
8
|
+
"de": "M7-Pro-Kommandos, Statusprojektion, Verbrauchswerte, Wartungshistorie und experimentelles Live-Karten-Rendering hinzugefügt",
|
|
9
|
+
"ru": "Добавлены команды M7 Pro, проекция состояния, расходные материалы, история обслуживания и экспериментальная отрисовка карты",
|
|
10
|
+
"pt": "Adicionados comandos do M7 Pro, estados, consumíveis, histórico de manutenção e renderização experimental do mapa",
|
|
11
|
+
"nl": "M7 Pro-opdrachten, statusprojectie, verbruiksgegevens, onderhoudsgeschiedenis en experimentele livekaart toegevoegd",
|
|
12
|
+
"fr": "Ajout des commandes M7 Pro, des états, des consommables, de l'historique de maintenance et du rendu expérimental de carte",
|
|
13
|
+
"it": "Aggiunti comandi M7 Pro, stati, consumabili, cronologia manutenzione e rendering sperimentale della mappa",
|
|
14
|
+
"es": "Añadidos comandos M7 Pro, estados, consumibles, historial de mantenimiento y renderizado experimental del mapa",
|
|
15
|
+
"pl": "Dodano polecenia M7 Pro, stany, materiały eksploatacyjne, historię konserwacji i eksperymentalne renderowanie mapy",
|
|
16
|
+
"uk": "Додано команди M7 Pro, стани, витратні матеріали, історію обслуговування та експериментальне відображення карти",
|
|
17
|
+
"zh-cn": "添加 M7 Pro 命令、状态投影、耗材、维护历史和实验性实时地图渲染"
|
|
18
|
+
},
|
|
19
|
+
"0.0.1": {
|
|
20
|
+
"en": "initial release",
|
|
21
|
+
"de": "Erstveröffentlichung",
|
|
22
|
+
"ru": "Начальная версия",
|
|
23
|
+
"pt": "lançamento inicial",
|
|
24
|
+
"nl": "Eerste uitgave",
|
|
25
|
+
"fr": "Première version",
|
|
26
|
+
"it": "Versione iniziale",
|
|
27
|
+
"es": "Versión inicial",
|
|
28
|
+
"pl": "Pierwsze wydanie",
|
|
29
|
+
"uk": "Початкова версія",
|
|
30
|
+
"zh-cn": "首次出版"
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
"titleLang": {
|
|
34
|
+
"en": "Proscenic",
|
|
35
|
+
"de": "Proscenic",
|
|
36
|
+
"ru": "Proscenic",
|
|
37
|
+
"pt": "Proscenic",
|
|
38
|
+
"nl": "Proscenic",
|
|
39
|
+
"fr": "Proscenic",
|
|
40
|
+
"it": "Proscenic",
|
|
41
|
+
"es": "Proscenic",
|
|
42
|
+
"pl": "Proscenic",
|
|
43
|
+
"uk": "Proscenic",
|
|
44
|
+
"zh-cn": "浦桑尼克"
|
|
45
|
+
},
|
|
46
|
+
"desc": {
|
|
47
|
+
"en": "ioBroker adapter for Proscenic vacuum robots using the legacy Proscenic cloud gateway",
|
|
48
|
+
"de": "ioBroker-Adapter für Proscenic-Saugroboter unter Verwendung des alten Proscenic-Cloud-Gateways",
|
|
49
|
+
"ru": "Адаптер ioBroker для вакуумных роботов Proscenic с использованием устаревшего облачного шлюза Proscenic",
|
|
50
|
+
"pt": "Adaptador ioBroker para robôs a vácuo Proscenic usando o gateway de nuvem Proscenic legado",
|
|
51
|
+
"nl": "ioBroker-adapter voor Proscenic-vacuümrobots die de oudere Proscenic-cloudgateway gebruiken",
|
|
52
|
+
"fr": "Adaptateur ioBroker pour les robots aspirateurs Proscenic utilisant l'ancienne passerelle cloud Proscenic",
|
|
53
|
+
"it": "Adattatore ioBroker per robot aspirapolvere Proscenic che utilizzano il gateway cloud Proscenic legacy",
|
|
54
|
+
"es": "Adaptador ioBroker para robots aspiradores Proscenic que utilizan la puerta de enlace en la nube Proscenic heredada",
|
|
55
|
+
"pl": "Adapter ioBroker dla robotów próżniowych Proscenic korzystających ze starszej bramy chmurowej Proscenic",
|
|
56
|
+
"uk": "Адаптер ioBroker для вакуумних роботів Proscenic, які використовують застарілий хмарний шлюз Proscenic",
|
|
57
|
+
"zh-cn": "使用传统 Proscenic 云网关的 Proscenic 真空机器人的 ioBroker 适配器"
|
|
58
|
+
},
|
|
59
|
+
"authors": ["C@ptain Ch@os <223081183+Musashi1965@users.noreply.github.com>"],
|
|
60
|
+
"keywords": ["proscenic", "vacuum", "robot", "m7 pro"],
|
|
61
|
+
"licenseInformation": {
|
|
62
|
+
"type": "free",
|
|
63
|
+
"license": "MIT"
|
|
64
|
+
},
|
|
65
|
+
"platform": "Javascript/Node.js",
|
|
66
|
+
"icon": "proscenic.png",
|
|
67
|
+
"enabled": true,
|
|
68
|
+
"extIcon": "https://raw.githubusercontent.com/Musashi1965/ioBroker.proscenic/main/admin/proscenic.png",
|
|
69
|
+
"readme": "https://github.com/Musashi1965/ioBroker.proscenic/blob/main/README.md",
|
|
70
|
+
"loglevel": "info",
|
|
71
|
+
"tier": 3,
|
|
72
|
+
"mode": "daemon",
|
|
73
|
+
"type": "household",
|
|
74
|
+
"compact": true,
|
|
75
|
+
"nogit": true,
|
|
76
|
+
"connectionType": "cloud",
|
|
77
|
+
"dataSource": "push",
|
|
78
|
+
"adminUI": {
|
|
79
|
+
"config": "json"
|
|
80
|
+
},
|
|
81
|
+
"dependencies": [
|
|
82
|
+
{
|
|
83
|
+
"js-controller": ">=6.0.11"
|
|
84
|
+
}
|
|
85
|
+
],
|
|
86
|
+
"globalDependencies": [
|
|
87
|
+
{
|
|
88
|
+
"admin": ">=7.6.20"
|
|
89
|
+
}
|
|
90
|
+
]
|
|
91
|
+
},
|
|
92
|
+
"protectedNative": ["username", "password"],
|
|
93
|
+
"encryptedNative": ["username", "password"],
|
|
94
|
+
"native": {
|
|
95
|
+
"region": "eu",
|
|
96
|
+
"vendor": "proscenic",
|
|
97
|
+
"username": "",
|
|
98
|
+
"password": "",
|
|
99
|
+
"deviceCode": "M7_PRO"
|
|
100
|
+
},
|
|
101
|
+
"objects": [],
|
|
102
|
+
"instanceObjects": [
|
|
103
|
+
{
|
|
104
|
+
"_id": "info",
|
|
105
|
+
"type": "channel",
|
|
106
|
+
"common": {
|
|
107
|
+
"name": "Information"
|
|
108
|
+
},
|
|
109
|
+
"native": {}
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
"_id": "info.connection",
|
|
113
|
+
"type": "state",
|
|
114
|
+
"common": {
|
|
115
|
+
"role": "indicator.connected",
|
|
116
|
+
"name": "Device or service connected",
|
|
117
|
+
"type": "boolean",
|
|
118
|
+
"read": true,
|
|
119
|
+
"write": false,
|
|
120
|
+
"def": false
|
|
121
|
+
},
|
|
122
|
+
"native": {}
|
|
123
|
+
}
|
|
124
|
+
]
|
|
125
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "iobroker.proscenic",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "ioBroker adapter for Proscenic vacuum robots using the legacy Proscenic cloud gateway",
|
|
5
|
+
"author": {
|
|
6
|
+
"name": "C@ptain Ch@os",
|
|
7
|
+
"email": "223081183+Musashi1965@users.noreply.github.com"
|
|
8
|
+
},
|
|
9
|
+
"contributors": [],
|
|
10
|
+
"homepage": "https://github.com/Musashi1965/ioBroker.proscenic",
|
|
11
|
+
"license": "MIT",
|
|
12
|
+
"keywords": [
|
|
13
|
+
"ioBroker",
|
|
14
|
+
"proscenic",
|
|
15
|
+
"vacuum",
|
|
16
|
+
"robot",
|
|
17
|
+
"m7 pro",
|
|
18
|
+
"smarthome"
|
|
19
|
+
],
|
|
20
|
+
"repository": {
|
|
21
|
+
"type": "git",
|
|
22
|
+
"url": "https://github.com/Musashi1965/ioBroker.proscenic.git"
|
|
23
|
+
},
|
|
24
|
+
"engines": {
|
|
25
|
+
"node": ">= 22"
|
|
26
|
+
},
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"@iobroker/adapter-core": "^3.4.3"
|
|
29
|
+
},
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"@alcalzone/release-script": "^5.2.1",
|
|
32
|
+
"@alcalzone/release-script-plugin-iobroker": "^5.2.0",
|
|
33
|
+
"@alcalzone/release-script-plugin-license": "^5.2.2",
|
|
34
|
+
"@alcalzone/release-script-plugin-manual-review": "^5.2.0",
|
|
35
|
+
"@iobroker/adapter-dev": "^1.5.0",
|
|
36
|
+
"@iobroker/dev-server": "^0.8.0",
|
|
37
|
+
"@iobroker/eslint-config": "^2.3.4",
|
|
38
|
+
"@iobroker/testing": "^6.2.1",
|
|
39
|
+
"@tsconfig/node22": "^22.0.6",
|
|
40
|
+
"@types/iobroker": "npm:@iobroker/types@^7.2.2",
|
|
41
|
+
"@types/node": "^22.20.2",
|
|
42
|
+
"rimraf": "^6.1.3",
|
|
43
|
+
"source-map-support": "^0.5.21",
|
|
44
|
+
"ts-node": "^10.9.2",
|
|
45
|
+
"typescript": "~5.9.3"
|
|
46
|
+
},
|
|
47
|
+
"main": "build/main.js",
|
|
48
|
+
"files": [
|
|
49
|
+
"README.md",
|
|
50
|
+
"admin{,/!(src)/**}/!(tsconfig|tsconfig.*|.eslintrc).{json,json5}",
|
|
51
|
+
"admin{,/!(src)/**}/*.{html,css,png,svg,jpg,js}",
|
|
52
|
+
"build/",
|
|
53
|
+
"www/",
|
|
54
|
+
"io-package.json",
|
|
55
|
+
"LICENSE"
|
|
56
|
+
],
|
|
57
|
+
"scripts": {
|
|
58
|
+
"prebuild": "rimraf build",
|
|
59
|
+
"build": "build-adapter ts",
|
|
60
|
+
"watch": "build-adapter ts --watch",
|
|
61
|
+
"prebuild:ts": "rimraf build",
|
|
62
|
+
"build:ts": "build-adapter ts",
|
|
63
|
+
"watch:ts": "build-adapter ts --watch",
|
|
64
|
+
"test:ts": "mocha --config test/mocharc.custom.json src/**/*.test.ts",
|
|
65
|
+
"test:package": "mocha test/package --exit",
|
|
66
|
+
"test:integration": "mocha test/integration --exit",
|
|
67
|
+
"test": "npm run test:ts && npm run test:package",
|
|
68
|
+
"check": "tsc --noEmit",
|
|
69
|
+
"check:quick": "npm run lint && npm run check && npm run build && npm test",
|
|
70
|
+
"check:full": "npm run lint && npm run check && npm run build && npm test && npm run test:integration",
|
|
71
|
+
"lint": "eslint -c eslint.config.mjs .",
|
|
72
|
+
"translate": "translate-adapter",
|
|
73
|
+
"release": "release-script",
|
|
74
|
+
"dev-server": "dev-server"
|
|
75
|
+
},
|
|
76
|
+
"bugs": {
|
|
77
|
+
"url": "https://github.com/Musashi1965/ioBroker.proscenic/issues"
|
|
78
|
+
},
|
|
79
|
+
"readmeFilename": "README.md"
|
|
80
|
+
}
|