iobroker.smartfriends 1.0.1 → 1.1.0-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -0
- package/io-package.json +116 -113
- package/lib/SchellenbergBridge.js +122 -46
- package/lib/comunication/comModel/responseBody/NewCompatibilityConfiguration.js +7 -2
- package/lib/devices/DeviceManager.js +125 -0
- package/lib/{SchellenbergDevice.js → devices/SchellenbergDevice.js} +62 -91
- package/lib/devices/SchellenbergMasterDevice.js +26 -0
- package/lib/helpers/CommonDefines.js +1 -19
- package/main.js +1 -1
- package/package.json +1 -1
- package/lib/DeviceManager.js +0 -121
package/README.md
CHANGED
|
@@ -33,6 +33,10 @@ The adapter establishes a direct connection to the gateway to control and query
|
|
|
33
33
|
Placeholder for the next version (at the beginning of the line):
|
|
34
34
|
### __WORK IN PROGRESS__
|
|
35
35
|
-->
|
|
36
|
+
### 1.1.0-alpha.0 (2025-12-23)
|
|
37
|
+
|
|
38
|
+
- (Black-Thunder) Refactored device handling: dynamic states, removed type whitelist, grouped devices under master ID
|
|
39
|
+
|
|
36
40
|
### 1.0.1 (2025-12-20)
|
|
37
41
|
|
|
38
42
|
- (Black-Thunder) Increased robustness when communicating with the gateway
|
package/io-package.json
CHANGED
|
@@ -1,115 +1,118 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
2
|
+
"common": {
|
|
3
|
+
"name": "smartfriends",
|
|
4
|
+
"version": "1.1.0-alpha.0",
|
|
5
|
+
"news": {
|
|
6
|
+
"1.1.0-alpha.0": {
|
|
7
|
+
"en": "Refactored device handling: dynamic states, removed type whitelist, grouped devices under master ID",
|
|
8
|
+
"de": "Überarbeitete Geräteverwaltung: dynamische Zustände, entfernte Typ-Whitelist, gruppierte Geräte unter Master-ID",
|
|
9
|
+
"ru": "Рефакторированная обработка устройств: динамические состояния, удаленный белый список типов, сгруппированные устройства под идентификатором Master ID",
|
|
10
|
+
"pt": "Manipulação do dispositivo refatorado: estados dinâmicos, lista branca do tipo removido, dispositivos agrupados sob ID mestre",
|
|
11
|
+
"nl": "Refactored device handling: dynamische toestanden, verwijderd type whitelist, gegroepeerde apparaten onder master ID",
|
|
12
|
+
"fr": "Manipulation de l'appareil refacturé : états dynamiques, liste blanche de type enlevé, dispositifs groupés sous Master ID",
|
|
13
|
+
"it": "Movimentazione del dispositivo refattore: stati dinamici, tipo rimosso whitelist, dispositivi raggruppati sotto master ID",
|
|
14
|
+
"es": "Manejo de dispositivo refactorizado: estados dinámicos, lista blanca de tipo eliminado, dispositivos agrupados bajo ID maestro",
|
|
15
|
+
"pl": "Przekształcona obsługa urządzenia: stany dynamiczne, usunięty biały typ, zgrupowane urządzenia pod master ID",
|
|
16
|
+
"uk": "Рефакторний пристрій обробки: динамічні стани, видалений тип білий список, вбудовані пристрої під магістр ID",
|
|
17
|
+
"zh-cn": "重构设备处理: 动态状态, 删除类型白名单, 主 ID 下分组设备"
|
|
18
|
+
},
|
|
19
|
+
"1.0.1": {
|
|
20
|
+
"en": "Increased robustness when communicating with the gateway\nAdded new option to ignore certificate errors",
|
|
21
|
+
"de": "Erhöhte Robustheit bei der Kommunikation mit dem Gateway\nNeue Option hinzugefügt, um Zertifikatsfehler zu ignorieren",
|
|
22
|
+
"ru": "Повышенная надежность при общении с шлюзом\nДобавлена новая возможность игнорировать ошибки сертификата",
|
|
23
|
+
"pt": "Maior robustez ao se comunicar com o gateway\nAdicionada nova opção para ignorar erros de certificado",
|
|
24
|
+
"nl": "Verhoogde robuustheid bij het communiceren met de gateway\nNieuwe optie toegevoegd om certificaatfouten te negeren",
|
|
25
|
+
"fr": "Une robustesse accrue lors de la communication avec la passerelle\nAjout d'une nouvelle option pour ignorer les erreurs de certificat",
|
|
26
|
+
"it": "Maggiore robustezza quando si comunica con il gateway\nAggiunta nuova opzione per ignorare gli errori del certificato",
|
|
27
|
+
"es": "Mayor robustez al comunicarse con la puerta de entrada\nNueva opción para ignorar errores de certificado",
|
|
28
|
+
"pl": "Większa odporność podczas komunikacji z bramą\nDodano nową opcję do ignorowania błędów certyfikatu",
|
|
29
|
+
"uk": "Підвищена надійність при спілкуванні з шлюзом\nДодано новий варіант ігнорувати помилки сертифіката",
|
|
30
|
+
"zh-cn": "与网关沟通时的强度提高\n添加新选项以忽略证书错误"
|
|
31
|
+
},
|
|
32
|
+
"1.0.0": {
|
|
33
|
+
"en": "initial release",
|
|
34
|
+
"de": "Erstveröffentlichung",
|
|
35
|
+
"ru": "Начальная версия",
|
|
36
|
+
"pt": "lançamento inicial",
|
|
37
|
+
"nl": "Eerste uitgave",
|
|
38
|
+
"fr": "Première version",
|
|
39
|
+
"it": "Versione iniziale",
|
|
40
|
+
"es": "Versión inicial",
|
|
41
|
+
"pl": "Pierwsze wydanie",
|
|
42
|
+
"zh-cn": "首次出版",
|
|
43
|
+
"uk": "початковий випуск"
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
"titleLang": {
|
|
47
|
+
"en": "SmartFriends",
|
|
48
|
+
"de": "SmartFriends",
|
|
49
|
+
"ru": "SmartFriends",
|
|
50
|
+
"pt": "SmartFriends",
|
|
51
|
+
"nl": "SmartFriends",
|
|
52
|
+
"fr": "SmartFriends",
|
|
53
|
+
"it": "SmartFriends",
|
|
54
|
+
"es": "SmartFriends",
|
|
55
|
+
"pl": "SmartFriends",
|
|
56
|
+
"zh-cn": "SmartFriends",
|
|
57
|
+
"uk": "SmartFriends"
|
|
58
|
+
},
|
|
59
|
+
"desc": {
|
|
60
|
+
"en": "Local control of SmartFriends gateways and connected devices without cloud access.",
|
|
61
|
+
"de": "Lokale Steuerung von SmartFriends-Gateways und angeschlossenen Geräten ohne Cloud-Anbindung.",
|
|
62
|
+
"ru": "Локальное управление шлюзами SmartFriends и подключёнными устройствами без облака.",
|
|
63
|
+
"pt": "Controle local de gateways SmartFriends e dispositivos conectados sem uso de nuvem.",
|
|
64
|
+
"nl": "Lokale aansturing van SmartFriends-gateways en aangesloten apparaten zonder cloud.",
|
|
65
|
+
"fr": "Contrôle local des passerelles SmartFriends et des appareils connectés sans cloud.",
|
|
66
|
+
"it": "Controllo locale dei gateway SmartFriends e dei dispositivi collegati senza cloud.",
|
|
67
|
+
"es": "Control local de gateways SmartFriends y dispositivos conectados sin nube.",
|
|
68
|
+
"pl": "Lokalne sterowanie bramkami SmartFriends i podłączonymi urządzeniami bez chmury.",
|
|
69
|
+
"zh-cn": "无需云服务即可本地控制 SmartFriends 网关及其连接的设备。",
|
|
70
|
+
"uk": "Локальне керування шлюзами SmartFriends і підключеними пристроями без хмари."
|
|
71
|
+
},
|
|
72
|
+
"authors": ["Black-Thunder <glwars@aol.de>"],
|
|
73
|
+
"keywords": ["home automation", "smartfriends", "schellenberg"],
|
|
74
|
+
"licenseInformation": {
|
|
75
|
+
"license": "MIT",
|
|
76
|
+
"type": "free"
|
|
77
|
+
},
|
|
78
|
+
"platform": "Javascript/Node.js",
|
|
79
|
+
"icon": "smartfriends.png",
|
|
80
|
+
"enabled": false,
|
|
81
|
+
"extIcon": "https://raw.githubusercontent.com/Black-Thunder/ioBroker.smartfriends/master/admin/smartfriends.png",
|
|
82
|
+
"readme": "https://github.com/Black-Thunder/ioBroker.smartfriends/blob/master/README.md",
|
|
83
|
+
"loglevel": "info",
|
|
84
|
+
"mode": "daemon",
|
|
85
|
+
"type": "iot-systems",
|
|
86
|
+
"compact": true,
|
|
87
|
+
"connectionType": "local",
|
|
88
|
+
"dataSource": "poll",
|
|
89
|
+
"adminUI": {
|
|
90
|
+
"config": "json"
|
|
91
|
+
},
|
|
92
|
+
"dependencies": [
|
|
93
|
+
{
|
|
94
|
+
"js-controller": ">=6.0.11"
|
|
95
|
+
}
|
|
96
|
+
],
|
|
97
|
+
"globalDependencies": [
|
|
98
|
+
{
|
|
99
|
+
"admin": ">=7.6.17"
|
|
100
|
+
}
|
|
101
|
+
],
|
|
102
|
+
"tier": 2
|
|
103
|
+
},
|
|
104
|
+
"encryptedNative": ["smartFriendsPassword"],
|
|
105
|
+
"protectedNative": ["smartFriendsPassword"],
|
|
106
|
+
"native": {
|
|
107
|
+
"smartFriendsPort": 4300,
|
|
108
|
+
"smartFriendsIP": "",
|
|
109
|
+
"smartFriendsUsername": "",
|
|
110
|
+
"smartFriendsPassword": "",
|
|
111
|
+
"smartFriendsCSymbol": "D19033i",
|
|
112
|
+
"smartFriendsShcVersion": "3.7.4",
|
|
113
|
+
"smartFriendsShApiVersion": "3.4",
|
|
114
|
+
"ignoreSslErrors": false
|
|
115
|
+
},
|
|
116
|
+
"objects": [],
|
|
117
|
+
"instanceObjects": []
|
|
115
118
|
}
|
|
@@ -1,25 +1,17 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
-
//--------------------------------------------------
|
|
4
|
-
//Copyright 2020 Pascâl Hartmann
|
|
5
|
-
//See LICENSE File
|
|
6
|
-
//--------------------------------------------------
|
|
7
|
-
//Handles incoming messages from a SmartSocket and
|
|
8
|
-
//emits Events defined in helpers/Events
|
|
9
|
-
//This is an exmaple Class that implements the DataDelegateInterface which is required by the Socket
|
|
10
|
-
//Implement an App in this Fashion
|
|
11
|
-
//--------------------------------------------------
|
|
12
|
-
|
|
13
3
|
const maxRetries = 3; // number of connection retries when connection was lost
|
|
14
4
|
const reconnectInterval = 10 * 1000; // time (in ms) after which a single new reconnection try should be made
|
|
15
5
|
const retryInterval = 30 * 60 * 1000; // time (in ms) after which new reconnection tries should be made
|
|
16
6
|
|
|
7
|
+
const commonDefines = require("./helpers/CommonDefines");
|
|
17
8
|
const CommandFactory = require("./comunication/CommandFactory");
|
|
18
9
|
const AllNewDeviceInfos = require("./comunication/comModel/responseBody/AllNewDeviceInfos");
|
|
19
10
|
const JSONResponse = require("./comunication/comModel/JSONResponse");
|
|
20
11
|
const SmartSocketFactory = require("./comunication/SmartSocketFactory");
|
|
21
12
|
const CommonDefines = require("./helpers/CommonDefines");
|
|
22
|
-
const DeviceManager = require("./DeviceManager");
|
|
13
|
+
const DeviceManager = require("./devices/DeviceManager");
|
|
14
|
+
const { SchellenbergMasterDevice } = require("./devices/SchellenbergMasterDevice");
|
|
23
15
|
|
|
24
16
|
class SchellenbergBridge {
|
|
25
17
|
constructor(adapter) {
|
|
@@ -39,6 +31,7 @@ class SchellenbergBridge {
|
|
|
39
31
|
this.retryCounter = 0;
|
|
40
32
|
this.stopRenewal = false;
|
|
41
33
|
this.deviceManager = new DeviceManager(this.adapter);
|
|
34
|
+
this.deviceDefinitions = [];
|
|
42
35
|
}
|
|
43
36
|
|
|
44
37
|
async Connect() {
|
|
@@ -72,48 +65,128 @@ class SchellenbergBridge {
|
|
|
72
65
|
);
|
|
73
66
|
const response = await this.socket.sendAndRecieveCommand(command, this.loginResponse.sessionID);
|
|
74
67
|
|
|
75
|
-
if (response?.response) {
|
|
76
|
-
|
|
77
|
-
await this.processAllNewDeviceInfos(parsedResponse);
|
|
68
|
+
if (!response?.response) {
|
|
69
|
+
return;
|
|
78
70
|
}
|
|
71
|
+
|
|
72
|
+
const allNewDeviceInfosParsed = AllNewDeviceInfos.default.fromObject(response.response);
|
|
73
|
+
this.processCompatibilityConfiguration(allNewDeviceInfosParsed);
|
|
74
|
+
await this.processAllNewDeviceInfos(allNewDeviceInfosParsed);
|
|
75
|
+
this.processInitialDeviceValues(allNewDeviceInfosParsed);
|
|
79
76
|
} catch (err) {
|
|
80
77
|
this.adapter.log.error(`Connection failed: ${err.message}`);
|
|
81
78
|
this.handleDisconnect();
|
|
82
79
|
}
|
|
83
80
|
}
|
|
84
81
|
|
|
82
|
+
updateTimestamp(parsed) {
|
|
83
|
+
if (parsed.currentTimestamp) {
|
|
84
|
+
this.lastTimestamp = parsed.currentTimestamp;
|
|
85
|
+
this.adapter.log.debug(`Updated timestamp to ${this.lastTimestamp}`);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
processCompatibilityConfiguration(parsed) {
|
|
90
|
+
if (this.deviceDefinitions?.length) {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const standards = parsed.newCompatibilityConfiguration?.compatibleRadioStandards ?? [];
|
|
95
|
+
this.deviceDefinitions = [];
|
|
96
|
+
|
|
97
|
+
for (const standard of standards) {
|
|
98
|
+
for (const def of standard.compatibleDevices ?? []) {
|
|
99
|
+
if (def.deviceDesignation) {
|
|
100
|
+
this.deviceDefinitions.push(def);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
this.adapter.log.debug(`Loaded ${this.deviceDefinitions.length} device definitions`);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
processInitialDeviceValues(parsed) {
|
|
109
|
+
const values = parsed.newDeviceValues?.values ?? [];
|
|
110
|
+
|
|
111
|
+
for (const value of values) {
|
|
112
|
+
this.handleDeviceValue(value);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
handleDeviceValue(deviceValue) {
|
|
117
|
+
if (!deviceValue || deviceValue.deviceID == null) {
|
|
118
|
+
this.adapter.log.debug("Invalid device value received, ignoring.");
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
this.adapter.log.debug(`Device value update: deviceID=${deviceValue.deviceID}, value=${deviceValue.value}`);
|
|
123
|
+
|
|
124
|
+
this.deviceManager.updateDeviceValue(deviceValue);
|
|
125
|
+
}
|
|
126
|
+
|
|
85
127
|
async processAllNewDeviceInfos(response) {
|
|
86
128
|
try {
|
|
87
|
-
const
|
|
88
|
-
|
|
129
|
+
const deviceInfos = response.newDeviceInfos?.values ?? [];
|
|
130
|
+
const createdMasters = [];
|
|
131
|
+
|
|
132
|
+
// 1️⃣ Child-Devices nach MasterID gruppieren
|
|
133
|
+
const devicesByMaster = {};
|
|
134
|
+
for (const device of deviceInfos) {
|
|
135
|
+
// Definition dynamisch zuweisen
|
|
136
|
+
device.definition = this.deviceDefinitions.find(d => d.deviceDesignation === device.deviceDesignation);
|
|
137
|
+
if (!device.definition) {
|
|
138
|
+
this.adapter.log.debug(
|
|
139
|
+
`Skipping device ${device.deviceName} (${device.deviceDesignation}) – no definition found`,
|
|
140
|
+
);
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
89
143
|
|
|
90
|
-
|
|
144
|
+
const masterId = device.masterDeviceID || device.deviceID;
|
|
145
|
+
if (!devicesByMaster[masterId]) {
|
|
146
|
+
devicesByMaster[masterId] = [];
|
|
147
|
+
}
|
|
148
|
+
devicesByMaster[masterId].push(device);
|
|
149
|
+
}
|
|
91
150
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
151
|
+
// 2️⃣ MasterDevices anlegen
|
|
152
|
+
for (const [masterId, childDevices] of Object.entries(devicesByMaster)) {
|
|
153
|
+
const masterName = childDevices[0].masterDeviceName || childDevices[0].deviceName;
|
|
154
|
+
const masterDevice = new SchellenbergMasterDevice(this.adapter, masterId, masterName, []);
|
|
155
|
+
|
|
156
|
+
// 3️⃣ Child-Devices unter Master anlegen
|
|
157
|
+
for (const child of childDevices) {
|
|
158
|
+
// Nur Devices mit definierbaren Control-States
|
|
159
|
+
const hasSwitching = child.definition.deviceType?.switchingValues?.length;
|
|
160
|
+
const isPosition = child.definition.deviceType?.kind === commonDefines.AdapterStateIDs.Position;
|
|
161
|
+
|
|
162
|
+
if (!hasSwitching && !isPosition) {
|
|
163
|
+
this.adapter.log.debug(
|
|
164
|
+
`Skipping device ${child.deviceName} (${child.deviceDesignation}) – no definable control states found`,
|
|
165
|
+
);
|
|
166
|
+
continue;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const schellenbergDevice = await this.deviceManager.createDevice({
|
|
170
|
+
id: child.deviceID,
|
|
171
|
+
name: child.deviceName,
|
|
172
|
+
deviceType: child.deviceTypClient ?? child.definition.deviceType?.kind ?? "unknown",
|
|
173
|
+
designation: child.deviceDesignation,
|
|
174
|
+
definition: child.definition,
|
|
175
|
+
masterPrefix: `${commonDefines.AdapterDatapointIDs.Devices}.${masterId}`, // States unter Master-Ordner
|
|
104
176
|
});
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
);
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
);
|
|
177
|
+
|
|
178
|
+
masterDevice.childDevices.push(schellenbergDevice);
|
|
179
|
+
this.adapter.log.info(`Device created: ${child.deviceName} (ID: ${child.deviceID})`);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
if (masterDevice.childDevices?.length !== 0) {
|
|
183
|
+
await masterDevice.createMasterFolder();
|
|
184
|
+
createdMasters.push(masterDevice);
|
|
112
185
|
}
|
|
113
186
|
}
|
|
114
187
|
|
|
115
188
|
this.adapter.log.info(
|
|
116
|
-
`Processed ${
|
|
189
|
+
`Processed ${deviceInfos.length} device(s), created ${createdMasters.length} master device(s).`,
|
|
117
190
|
);
|
|
118
191
|
} catch (error) {
|
|
119
192
|
this.adapter.log.error(`Error processing devices: ${error.message}`);
|
|
@@ -272,7 +345,7 @@ class SchellenbergBridge {
|
|
|
272
345
|
|
|
273
346
|
async handleLoginMessage(response) {
|
|
274
347
|
if (response.sessionID && response.hardware && response.macAddress && response.shsVersion) {
|
|
275
|
-
this.adapter.log.debug(
|
|
348
|
+
this.adapter.log.debug(`Connection and login to gateway ${response.hardware} successful.`);
|
|
276
349
|
this.loginResponse = response;
|
|
277
350
|
this.retryCounter = 0;
|
|
278
351
|
|
|
@@ -309,14 +382,17 @@ class SchellenbergBridge {
|
|
|
309
382
|
}
|
|
310
383
|
|
|
311
384
|
handleUpdate(response) {
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
385
|
+
// new device values
|
|
386
|
+
if (response.newDeviceValues?.values?.length) {
|
|
387
|
+
for (const value of response.newDeviceValues.values) {
|
|
388
|
+
this.handleDeviceValue(value);
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
// new device infos (optional, für später)
|
|
393
|
+
if (response.newDeviceInfos?.values?.length) {
|
|
394
|
+
for (const device of response.newDeviceInfos.values) {
|
|
395
|
+
this.adapter.log.debug(`New device info received for deviceID=${device.deviceID}`);
|
|
320
396
|
}
|
|
321
397
|
}
|
|
322
398
|
}
|
|
@@ -6,11 +6,16 @@
|
|
|
6
6
|
//--------------------------------------------------
|
|
7
7
|
|
|
8
8
|
class NewCompatibilityConfiguration {
|
|
9
|
-
constructor(compatibilityConfigurationVersion) {
|
|
9
|
+
constructor(compatibilityConfigurationVersion, compatibleRadioStandards = []) {
|
|
10
10
|
this.compatibilityConfigurationVersion = compatibilityConfigurationVersion;
|
|
11
|
+
this.compatibleRadioStandards = compatibleRadioStandards;
|
|
11
12
|
}
|
|
13
|
+
|
|
12
14
|
static fromObject(object) {
|
|
13
|
-
return new NewCompatibilityConfiguration(
|
|
15
|
+
return new NewCompatibilityConfiguration(
|
|
16
|
+
object.compatibilityConfigurationVersion,
|
|
17
|
+
object.compatibleRadioStandards ?? [],
|
|
18
|
+
);
|
|
14
19
|
}
|
|
15
20
|
}
|
|
16
21
|
exports.default = NewCompatibilityConfiguration;
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
const { SchellenbergDevice } = require("./SchellenbergDevice");
|
|
2
|
+
const commonDefines = require("../helpers/CommonDefines");
|
|
3
|
+
const commandFactory = require("../comunication/CommandFactory");
|
|
4
|
+
|
|
5
|
+
class DeviceManager {
|
|
6
|
+
constructor(adapter) {
|
|
7
|
+
this.adapter = adapter;
|
|
8
|
+
this.devices = new Map();
|
|
9
|
+
this.log = adapter.log;
|
|
10
|
+
this.bridge = null;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
setBridge(bridge) {
|
|
14
|
+
this.bridge = bridge;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
async createDevice(deviceInfo) {
|
|
18
|
+
try {
|
|
19
|
+
const device = new SchellenbergDevice(this.adapter);
|
|
20
|
+
Object.assign(device, deviceInfo);
|
|
21
|
+
await device.CreateAndSave(deviceInfo.masterPrefix);
|
|
22
|
+
this.devices.set(deviceInfo.id, device);
|
|
23
|
+
return device;
|
|
24
|
+
} catch (error) {
|
|
25
|
+
this.log.error(`Failed to create device ${deviceInfo.id}: ${error.message}`);
|
|
26
|
+
throw error;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
getDevice(id) {
|
|
31
|
+
return this.devices.get(id);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
getAllDevices() {
|
|
35
|
+
return Array.from(this.devices.values());
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async updateDevice(id, data) {
|
|
39
|
+
const device = this.getDevice(id);
|
|
40
|
+
if (device) {
|
|
41
|
+
Object.assign(device, data);
|
|
42
|
+
await device.Update();
|
|
43
|
+
this.log.debug(`Device updated: ${id}`);
|
|
44
|
+
} else {
|
|
45
|
+
this.log.warn(`Device not found for update: ${id}`);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async deleteDevice(id) {
|
|
50
|
+
const device = this.getDevice(id);
|
|
51
|
+
if (device) {
|
|
52
|
+
await device.Delete();
|
|
53
|
+
this.devices.delete(id);
|
|
54
|
+
this.log.debug(`Device deleted: ${id}`);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
async handleStateChange(id, state) {
|
|
59
|
+
try {
|
|
60
|
+
if (!state || state.ack) {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const devicePrefix = `${this.adapter.namespace}.${commonDefines.AdapterDatapointIDs.Devices}.`;
|
|
65
|
+
const path = id.replace(devicePrefix, "");
|
|
66
|
+
const parts = path.split(".");
|
|
67
|
+
|
|
68
|
+
let childId;
|
|
69
|
+
if (parts.length === 1) {
|
|
70
|
+
// nur ein Device, kein Master
|
|
71
|
+
childId = parts[0];
|
|
72
|
+
} else if (parts.length >= 2) {
|
|
73
|
+
// Master + Child
|
|
74
|
+
childId = parts[1];
|
|
75
|
+
} else {
|
|
76
|
+
this.adapter.log.warn(`Invalid state path: ${id}`);
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const obj = await this.adapter.getObjectAsync(id);
|
|
81
|
+
|
|
82
|
+
const commandValue = typeof state.val === "number" ? state.val : obj?.native?.commandValue;
|
|
83
|
+
|
|
84
|
+
if (commandValue == null) {
|
|
85
|
+
this.adapter.log.warn(`No commandValue defined for ${id}`);
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
this.adapter.log.debug(`Control triggered: device=${childId}, command=${commandValue} (state=${id})`);
|
|
90
|
+
|
|
91
|
+
await this.sendDeviceCommand(childId, commandValue);
|
|
92
|
+
|
|
93
|
+
await this.adapter.setStateAsync(id, false, true);
|
|
94
|
+
} catch (error) {
|
|
95
|
+
this.log.error(`Error in handleStateChange: ${error.responseMessage}`);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
async sendDeviceCommand(deviceId, value) {
|
|
100
|
+
try {
|
|
101
|
+
if (!this.bridge) {
|
|
102
|
+
throw new Error("Bridge not initialized");
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
this.log.debug(`Sending value '${value}' to device ${deviceId}...`);
|
|
106
|
+
|
|
107
|
+
await this.bridge.sendAndReceiveCommand(commandFactory.default.createSetDeviceValueCmd(deviceId, value));
|
|
108
|
+
} catch (error) {
|
|
109
|
+
this.log.error(`Error in sendDeviceCommand: ${error.responseMessage}`);
|
|
110
|
+
throw error;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
updateDeviceValue(deviceValue) {
|
|
115
|
+
const device = this.devices?.get(deviceValue.deviceID);
|
|
116
|
+
if (!device) {
|
|
117
|
+
this.adapter.log.debug(`Received value for unknown device ${deviceValue.deviceID}, ignoring.`);
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
device.updateValue(deviceValue);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
module.exports = DeviceManager;
|
|
@@ -1,29 +1,29 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
-
const commonDefines = require("
|
|
3
|
+
const commonDefines = require("../helpers/CommonDefines");
|
|
4
4
|
|
|
5
5
|
class SchellenbergDevice {
|
|
6
6
|
constructor(adapter) {
|
|
7
7
|
this.adapter = adapter;
|
|
8
|
-
this.
|
|
8
|
+
this.definition = null;
|
|
9
9
|
|
|
10
10
|
// Info
|
|
11
11
|
this.id = -1;
|
|
12
12
|
this.name = "";
|
|
13
13
|
this.deviceType = "";
|
|
14
14
|
this.designation = "";
|
|
15
|
-
|
|
16
|
-
// Control
|
|
17
|
-
this.power = false;
|
|
18
15
|
}
|
|
19
16
|
|
|
20
17
|
// Creates all necessery states and channels and writes the values into the DB
|
|
21
|
-
async CreateAndSave() {
|
|
22
|
-
const devicePrefix =
|
|
18
|
+
async CreateAndSave(masterPrefix) {
|
|
19
|
+
const devicePrefix = masterPrefix
|
|
20
|
+
? `${masterPrefix}.${this.id}`
|
|
21
|
+
: `${commonDefines.AdapterDatapointIDs.Devices}.${this.id}`;
|
|
22
|
+
|
|
23
23
|
await this.adapter.setObjectNotExistsAsync(devicePrefix, {
|
|
24
24
|
type: "channel",
|
|
25
25
|
common: {
|
|
26
|
-
name:
|
|
26
|
+
name: this.name,
|
|
27
27
|
},
|
|
28
28
|
native: {},
|
|
29
29
|
});
|
|
@@ -85,6 +85,7 @@ class SchellenbergDevice {
|
|
|
85
85
|
|
|
86
86
|
//#region CONTROL
|
|
87
87
|
let controlPrefix = `${devicePrefix}.${commonDefines.AdapterDatapointIDs.Control}`;
|
|
88
|
+
|
|
88
89
|
await this.adapter.setObjectNotExistsAsync(controlPrefix, {
|
|
89
90
|
type: "channel",
|
|
90
91
|
common: {
|
|
@@ -95,98 +96,42 @@ class SchellenbergDevice {
|
|
|
95
96
|
|
|
96
97
|
controlPrefix += ".";
|
|
97
98
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
99
|
+
// Dynamisch aus device.definition
|
|
100
|
+
if (this.definition?.deviceType?.switchingValues) {
|
|
101
|
+
for (const stateDef of this.definition.deviceType.switchingValues) {
|
|
102
|
+
const stateId = stateDef.name.replace(/\${|}/g, "").toLowerCase(); // ${On} → On
|
|
103
|
+
await this.adapter.setObjectNotExistsAsync(`${controlPrefix}${stateId}`, {
|
|
101
104
|
type: "state",
|
|
102
105
|
common: {
|
|
103
|
-
name:
|
|
106
|
+
name: stateId,
|
|
104
107
|
type: "boolean",
|
|
105
|
-
role: "button
|
|
108
|
+
role: "button",
|
|
106
109
|
read: false,
|
|
107
110
|
write: true,
|
|
108
111
|
def: false,
|
|
109
|
-
desc: "Move downwards, lower sunblind",
|
|
110
112
|
},
|
|
111
|
-
native: {
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
await this.adapter.setObjectNotExistsAsync(controlPrefix + commonDefines.AdapterStateIDs.MoveUp, {
|
|
115
|
-
type: "state",
|
|
116
|
-
common: {
|
|
117
|
-
name: "Move upwards",
|
|
118
|
-
type: "boolean",
|
|
119
|
-
role: "button.start",
|
|
120
|
-
read: false,
|
|
121
|
-
write: true,
|
|
122
|
-
def: false,
|
|
123
|
-
desc: "Move upwards, retract sunblind",
|
|
113
|
+
native: {
|
|
114
|
+
commandValue: stateDef.value,
|
|
124
115
|
},
|
|
125
|
-
native: {},
|
|
126
116
|
});
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
common: {
|
|
146
|
-
name: "Close shutter",
|
|
147
|
-
type: "boolean",
|
|
148
|
-
role: "button.start",
|
|
149
|
-
read: false,
|
|
150
|
-
write: true,
|
|
151
|
-
def: false,
|
|
152
|
-
desc: "Close shutter",
|
|
153
|
-
},
|
|
154
|
-
native: {},
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
await this.adapter.setObjectNotExistsAsync(controlPrefix + commonDefines.AdapterStateIDs.Open, {
|
|
158
|
-
type: "state",
|
|
159
|
-
common: {
|
|
160
|
-
name: "Open shutter",
|
|
161
|
-
type: "boolean",
|
|
162
|
-
role: "button.start",
|
|
163
|
-
read: false,
|
|
164
|
-
write: true,
|
|
165
|
-
def: false,
|
|
166
|
-
desc: "Open shutter",
|
|
167
|
-
},
|
|
168
|
-
native: {},
|
|
169
|
-
});
|
|
170
|
-
|
|
171
|
-
await this.adapter.setObjectNotExistsAsync(controlPrefix + commonDefines.AdapterStateIDs.MoveStop, {
|
|
172
|
-
type: "state",
|
|
173
|
-
common: {
|
|
174
|
-
name: "Stop movement",
|
|
175
|
-
type: "boolean",
|
|
176
|
-
role: "button.stop",
|
|
177
|
-
read: false,
|
|
178
|
-
write: true,
|
|
179
|
-
def: false,
|
|
180
|
-
desc: "Stop shutter movement",
|
|
181
|
-
},
|
|
182
|
-
native: {},
|
|
183
|
-
});
|
|
184
|
-
break;
|
|
185
|
-
default:
|
|
186
|
-
this.adapter.log.error(
|
|
187
|
-
`No controls known for device type '${this.deviceType}' - Report this to the developer!`,
|
|
188
|
-
);
|
|
189
|
-
break;
|
|
117
|
+
}
|
|
118
|
+
} else if (this.definition.deviceType.kind === commonDefines.AdapterStateIDs.Position) {
|
|
119
|
+
await this.adapter.setObjectNotExistsAsync(`${controlPrefix}${commonDefines.AdapterStateIDs.Position}`, {
|
|
120
|
+
type: "state",
|
|
121
|
+
common: {
|
|
122
|
+
name: commonDefines.AdapterStateIDs.Position,
|
|
123
|
+
type: "number",
|
|
124
|
+
role: "level.blind",
|
|
125
|
+
read: true,
|
|
126
|
+
write: true,
|
|
127
|
+
min: this.definition.deviceType.min ?? 0,
|
|
128
|
+
max: this.definition.deviceType.max ?? 100,
|
|
129
|
+
step: this.definition.deviceType.step ?? 1,
|
|
130
|
+
unit: "%",
|
|
131
|
+
def: 0,
|
|
132
|
+
},
|
|
133
|
+
native: {},
|
|
134
|
+
});
|
|
190
135
|
}
|
|
191
136
|
//#endregion
|
|
192
137
|
|
|
@@ -213,6 +158,32 @@ class SchellenbergDevice {
|
|
|
213
158
|
|
|
214
159
|
this.adapter.log.debug(`Updated device data for device ${this.id} (${this.name})`);
|
|
215
160
|
}
|
|
161
|
+
|
|
162
|
+
async updateValue(value) {
|
|
163
|
+
// value: Instanz von DeviceValue
|
|
164
|
+
if (value.deviceID !== this.id) {
|
|
165
|
+
// Diese Instanz betrifft nicht dieses Device
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
let controlPrefix = "";
|
|
170
|
+
if (value.masterDeviceID != this.id) {
|
|
171
|
+
// Child-Device unter einem Master-Device
|
|
172
|
+
controlPrefix = `${commonDefines.AdapterDatapointIDs.Devices}.${value.masterDeviceID}.${this.id}.${commonDefines.AdapterDatapointIDs.Control}.`;
|
|
173
|
+
} else {
|
|
174
|
+
// Einzelnes Device
|
|
175
|
+
controlPrefix = `${commonDefines.AdapterDatapointIDs.Devices}.${this.id}.${commonDefines.AdapterDatapointIDs.Control}.`;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (this.definition?.deviceType?.switchingValues) {
|
|
179
|
+
// SwitchingValues ignorieren, liefern keine Updates zurück
|
|
180
|
+
} else if (this.definition?.deviceType?.kind === commonDefines.AdapterStateIDs.Position) {
|
|
181
|
+
// Für Position → direkt setzen
|
|
182
|
+
const stateId = `${controlPrefix}${commonDefines.AdapterStateIDs.Position}`;
|
|
183
|
+
await this.adapter.setStateAsync(stateId, value.value, true);
|
|
184
|
+
this.adapter.log.debug(`Device ${this.id}: Updated position = ${value.value}`);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
216
187
|
}
|
|
217
188
|
|
|
218
189
|
exports.SchellenbergDevice = SchellenbergDevice;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
const commonDefines = require("../helpers/CommonDefines");
|
|
2
|
+
|
|
3
|
+
class SchellenbergMasterDevice {
|
|
4
|
+
constructor(adapter, masterId, name, devices) {
|
|
5
|
+
this.adapter = adapter;
|
|
6
|
+
this.id = masterId;
|
|
7
|
+
this.name = name;
|
|
8
|
+
this.childDevices = devices; // Array von SchellenbergDevice
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
async createMasterFolder() {
|
|
12
|
+
const masterPrefix = `${commonDefines.AdapterDatapointIDs.Devices}.${this.id}`;
|
|
13
|
+
await this.adapter.setObjectNotExistsAsync(masterPrefix, {
|
|
14
|
+
type: "channel",
|
|
15
|
+
common: { name: this.name },
|
|
16
|
+
native: {},
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
// Child Devices anlegen
|
|
20
|
+
for (const child of this.childDevices) {
|
|
21
|
+
await child.CreateAndSave(masterPrefix); // MasterPrefix übergeben
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
exports.SchellenbergMasterDevice = SchellenbergMasterDevice;
|
|
@@ -18,23 +18,5 @@ exports.AdapterStateIDs = Object.freeze({
|
|
|
18
18
|
TypeClient: "typeClient",
|
|
19
19
|
Designation: "designation",
|
|
20
20
|
// devices.XXX.control
|
|
21
|
-
|
|
22
|
-
Open: "open",
|
|
23
|
-
MoveDown: "moveDown",
|
|
24
|
-
Close: "close",
|
|
25
|
-
MoveStop: "moveStop",
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
exports.KnownDeviceTypes = Object.freeze({
|
|
29
|
-
AwningEngine: { name: "Markisenantrieb Plus", type: "${Awning}" },
|
|
30
|
-
RollingShutter: { name: "Gurtantrieb", type: "${RollingShutter}" },
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
exports.DeviceCommands = Object.freeze({
|
|
34
|
-
MoveDown: { name: "MoveDown", value: 1 },
|
|
35
|
-
Open: { name: "Open", value: 1 },
|
|
36
|
-
MoveUp: { name: "MoveUp", value: 2 },
|
|
37
|
-
Close: { name: "Close", value: 2 },
|
|
38
|
-
MoveStop: { name: "MoveStop", value: 0 },
|
|
39
|
-
UNDEF: { name: "UndefinedCommand", value: -1 },
|
|
21
|
+
Position: "position",
|
|
40
22
|
});
|
package/main.js
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
const utils = require("@iobroker/adapter-core");
|
|
9
9
|
const schellenbergBridge = require("./lib/SchellenbergBridge");
|
|
10
10
|
const commonDefines = require("./lib/helpers/CommonDefines");
|
|
11
|
-
const DeviceManager = require("./lib/DeviceManager");
|
|
11
|
+
const DeviceManager = require("./lib/devices/DeviceManager");
|
|
12
12
|
|
|
13
13
|
let SchellenbergBridge = null;
|
|
14
14
|
|
package/package.json
CHANGED
package/lib/DeviceManager.js
DELETED
|
@@ -1,121 +0,0 @@
|
|
|
1
|
-
const { SchellenbergDevice } = require("./SchellenbergDevice");
|
|
2
|
-
const commonDefines = require("./helpers/CommonDefines");
|
|
3
|
-
const commandFactory = require("./comunication/CommandFactory");
|
|
4
|
-
|
|
5
|
-
class DeviceManager {
|
|
6
|
-
constructor(adapter) {
|
|
7
|
-
this.adapter = adapter;
|
|
8
|
-
this.devices = new Map();
|
|
9
|
-
this.log = adapter.log;
|
|
10
|
-
this.bridge = null;
|
|
11
|
-
|
|
12
|
-
// Bind all methods that use 'this' context
|
|
13
|
-
this.handleStateChange = this.handleStateChange.bind(this);
|
|
14
|
-
this.sendDeviceCommand = this.sendDeviceCommand.bind(this);
|
|
15
|
-
this.mapControlCommand = this.mapControlCommand.bind(this);
|
|
16
|
-
this.createDevice = this.createDevice.bind(this);
|
|
17
|
-
this.updateDevice = this.updateDevice.bind(this);
|
|
18
|
-
this.deleteDevice = this.deleteDevice.bind(this);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
setBridge(bridge) {
|
|
22
|
-
this.bridge = bridge;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
async createDevice(deviceInfo) {
|
|
26
|
-
try {
|
|
27
|
-
const device = new SchellenbergDevice(this.adapter);
|
|
28
|
-
Object.assign(device, deviceInfo);
|
|
29
|
-
await device.CreateAndSave();
|
|
30
|
-
this.devices.set(deviceInfo.id, device);
|
|
31
|
-
return device;
|
|
32
|
-
} catch (error) {
|
|
33
|
-
this.log.error(`Failed to create device ${deviceInfo.id}: ${error.message}`);
|
|
34
|
-
throw error;
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
getDevice(id) {
|
|
39
|
-
return this.devices.get(id);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
getAllDevices() {
|
|
43
|
-
return Array.from(this.devices.values());
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
async updateDevice(id, data) {
|
|
47
|
-
const device = this.getDevice(id);
|
|
48
|
-
if (device) {
|
|
49
|
-
Object.assign(device, data);
|
|
50
|
-
await device.Update();
|
|
51
|
-
this.log.debug(`Device updated: ${id}`);
|
|
52
|
-
} else {
|
|
53
|
-
this.log.warn(`Device not found for update: ${id}`);
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
async deleteDevice(id) {
|
|
58
|
-
const device = this.getDevice(id);
|
|
59
|
-
if (device) {
|
|
60
|
-
await device.Delete();
|
|
61
|
-
this.devices.delete(id);
|
|
62
|
-
this.log.debug(`Device deleted: ${id}`);
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
async handleStateChange(id, state) {
|
|
67
|
-
try {
|
|
68
|
-
if (!state || state.ack || state.val === false) {
|
|
69
|
-
return;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
let deviceId = id.replace(`${this.adapter.namespace}.${commonDefines.AdapterDatapointIDs.Devices}.`, "");
|
|
73
|
-
deviceId = deviceId.substring(0, deviceId.indexOf("."));
|
|
74
|
-
|
|
75
|
-
const controlOption = id.substring(id.lastIndexOf(".") + 1, id.length);
|
|
76
|
-
const controlCommand = this.mapControlCommand(controlOption);
|
|
77
|
-
|
|
78
|
-
if (deviceId && controlCommand !== commonDefines.DeviceCommands.UNDEF) {
|
|
79
|
-
await this.sendDeviceCommand(deviceId, controlCommand, id);
|
|
80
|
-
}
|
|
81
|
-
} catch (error) {
|
|
82
|
-
this.log.error(`Error in handleStateChange: ${error.message}`);
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
mapControlCommand(controlOption) {
|
|
87
|
-
const commandMap = {
|
|
88
|
-
[commonDefines.AdapterStateIDs.MoveDown]: commonDefines.DeviceCommands.MoveDown,
|
|
89
|
-
[commonDefines.AdapterStateIDs.Close]: commonDefines.DeviceCommands.Close,
|
|
90
|
-
[commonDefines.AdapterStateIDs.MoveUp]: commonDefines.DeviceCommands.MoveUp,
|
|
91
|
-
[commonDefines.AdapterStateIDs.Open]: commonDefines.DeviceCommands.Open,
|
|
92
|
-
[commonDefines.AdapterStateIDs.MoveStop]: commonDefines.DeviceCommands.MoveStop,
|
|
93
|
-
};
|
|
94
|
-
|
|
95
|
-
const command = commandMap[controlOption];
|
|
96
|
-
if (!command) {
|
|
97
|
-
this.log.error(`Unsupported control option: ${controlOption} - Please report this to the developer!`);
|
|
98
|
-
return commonDefines.DeviceCommands.UNDEF;
|
|
99
|
-
}
|
|
100
|
-
return command;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
async sendDeviceCommand(deviceId, controlCommand, stateId) {
|
|
104
|
-
try {
|
|
105
|
-
if (!this.bridge) {
|
|
106
|
-
throw new Error("Bridge not initialized");
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
this.log.debug(`Sending command '${controlCommand.name}' to device ${deviceId}...`);
|
|
110
|
-
await this.bridge.sendAndReceiveCommand(
|
|
111
|
-
commandFactory.default.createSetDeviceValueCmd(deviceId, controlCommand.value),
|
|
112
|
-
);
|
|
113
|
-
await this.adapter.setState(stateId, false, true);
|
|
114
|
-
} catch (error) {
|
|
115
|
-
this.log.error(`Error in sendDeviceCommand: ${error.message}`);
|
|
116
|
-
throw error;
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
module.exports = DeviceManager;
|