iobroker.smartfriends 1.1.0 → 1.2.0-alpha.1

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2025 Black-Thunder <glwars@aol.de>
3
+ Copyright (c) 2025-2026 Black-Thunder <glwars@aol.de>
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
@@ -20,8 +20,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
20
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
21
  SOFTWARE.
22
22
 
23
-
24
- -------------------------------------------------------------------------------
23
+ ---
25
24
 
26
25
  LoPablo/SchellenbergApi (https://github.com/LoPablo/SchellenbergApi)
27
26
 
package/README.md CHANGED
@@ -33,6 +33,15 @@ 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.2.0-alpha.1 (2026-01-08)
37
+
38
+ - (Black-Thunder) Timeout for initial device request was increased
39
+ - (Black-Thunder) Devices without defined device type are ignored
40
+
41
+ ### 1.2.0-alpha.0 (2026-01-02)
42
+
43
+ - (Black-Thunder) Refactored device handling and added support for further device types
44
+
36
45
  ### 1.1.0 (2025-12-28)
37
46
 
38
47
  - (Black-Thunder) Refactored device handling: dynamic states, removed type whitelist, grouped devices under master ID
@@ -55,7 +64,7 @@ Special thanks und credits to [LoPablo](https://github.com/LoPablo/SchellenbergA
55
64
 
56
65
  MIT License
57
66
 
58
- Copyright (c) 2025 Black-Thunder <glwars@aol.de>
67
+ Copyright (c) 2025-2026 Black-Thunder <glwars@aol.de>
59
68
 
60
69
  Permission is hereby granted, free of charge, to any person obtaining a copy
61
70
  of this software and associated documentation files (the "Software"), to deal
package/io-package.json CHANGED
@@ -1,8 +1,34 @@
1
1
  {
2
2
  "common": {
3
3
  "name": "smartfriends",
4
- "version": "1.1.0",
4
+ "version": "1.2.0-alpha.1",
5
5
  "news": {
6
+ "1.2.0-alpha.1": {
7
+ "en": "Timeout for initial device request was increased\nDevices without defined device type are ignored",
8
+ "de": "Timeout für erste Geräteanfrage wurde erhöht\nGeräte ohne definierten Gerätetyp werden ignoriert",
9
+ "ru": "Время ожидания первоначального запроса устройства было увеличено\nУстройства без определенного типа устройства игнорируются",
10
+ "pt": "O tempo limite para a solicitação inicial do dispositivo foi aumentado\nOs dispositivos sem tipo de dispositivo definido são ignorados",
11
+ "nl": "Tijdslimiet voor het eerste apparaatverzoek werd verhoogd\nApparaten zonder gedefinieerd apparaattype worden genegeerd",
12
+ "fr": "Le délai pour la demande initiale d'appareil a été augmenté\nLes dispositifs sans type défini sont ignorés",
13
+ "it": "Timeout per la richiesta iniziale del dispositivo è stato aumentato\nI dispositivi senza tipo di dispositivo definito vengono ignorati",
14
+ "es": "Se aumentó el tiempo para la solicitud inicial del dispositivo\nLos dispositivos sin tipo de dispositivo definido son ignorados",
15
+ "pl": "Zwiększono czas na początkowe żądanie urządzenia\nUrządzenia bez określonego typu urządzenia są ignorowane",
16
+ "uk": "Підвищено запит на початковий пристрій\nПристрої без визначеного типу пристрою ігноруються",
17
+ "zh-cn": "初始设备请求的超时增加\n没有定义设备类型的设备会被忽略"
18
+ },
19
+ "1.2.0-alpha.0": {
20
+ "en": "Refactored device handling and added support for further device types",
21
+ "de": "Refactored Device Handling und zusätzliche Unterstützung für weitere Gerätetypen",
22
+ "ru": "Рефакторированная обработка устройств и дополнительная поддержка для других типов устройств",
23
+ "pt": "Manipulação do dispositivo refatorizado e suporte adicional para outros tipos de dispositivo",
24
+ "nl": "Refactored device handling en toegevoegde ondersteuning voor andere apparaattypes",
25
+ "fr": "Manipulation de l'appareil refactoré et support supplémentaire pour d'autres types d'appareil",
26
+ "it": "Gestione del dispositivo e supporto aggiunto per ulteriori tipi di dispositivo",
27
+ "es": "Manejo de dispositivo refactorizado y soporte añadido para nuevos tipos de dispositivos",
28
+ "pl": "Zmieniona obsługa urządzenia i dodana obsługa dla innych typów urządzeń",
29
+ "uk": "Рефакторний пристрій обробки та додано підтримку для подальших типів пристроїв",
30
+ "zh-cn": "对设备类型进行重构处理并添加支持"
31
+ },
6
32
  "1.1.0": {
7
33
  "en": "Refactored device handling: dynamic states, removed type whitelist, grouped devices under master ID\nHandle device value updates now correctly",
8
34
  "de": "Refactored Device Handling: dynamische Zustände, entfernte Typ Whitelist, gruppierte Geräte unter Master ID\nGerätewert-Updates jetzt korrekt ausschalten",
@@ -12,6 +12,7 @@ const SmartSocketFactory = require("./comunication/SmartSocketFactory");
12
12
  const CommonDefines = require("./helpers/CommonDefines");
13
13
  const DeviceManager = require("./devices/DeviceManager");
14
14
  const { SchellenbergMasterDevice } = require("./devices/SchellenbergMasterDevice");
15
+ const { getDeviceCapabilities } = require("./devices/DeviceKindCapabilities");
15
16
 
16
17
  class SchellenbergBridge {
17
18
  constructor(adapter) {
@@ -63,7 +64,7 @@ class SchellenbergBridge {
63
64
  this.compatibilityConfigurationVersion,
64
65
  this.languageTranslationVersion,
65
66
  );
66
- const response = await this.socket.sendAndRecieveCommand(command, this.loginResponse.sessionID);
67
+ const response = await this.socket.sendAndReceiveCommand(command, this.loginResponse.sessionID);
67
68
 
68
69
  if (!response?.response) {
69
70
  return;
@@ -170,25 +171,34 @@ class SchellenbergBridge {
170
171
 
171
172
  // 2️⃣ MasterDevices anlegen
172
173
  for (const [masterId, childDevices] of Object.entries(devicesByMaster)) {
173
- const masterName = childDevices[0].masterDeviceName || childDevices[0].deviceName;
174
+ const masterName = (childDevices[0].masterDeviceName || childDevices[0].deviceName).replace(
175
+ /\${|}/g,
176
+ "",
177
+ );
174
178
  const masterDevice = new SchellenbergMasterDevice(this.adapter, masterId, masterName, []);
175
179
 
176
180
  // 3️⃣ Child-Devices unter Master anlegen
177
181
  for (const child of childDevices) {
178
- // Nur Devices mit definierbaren Control-States
179
- const hasSwitching = child.definition.deviceType?.switchingValues?.length;
180
- const isPosition = child.definition.deviceType?.kind === commonDefines.AdapterStateIDs.Position;
182
+ // Nur Devices mit definierbaren States
183
+ this.adapter.log.debug(
184
+ `Getting device capabilites from definition: ${JSON.stringify(child.definition)}`,
185
+ );
186
+ const capabilities = getDeviceCapabilities(child.definition);
181
187
 
182
- if (!hasSwitching && !isPosition) {
188
+ if (!capabilities.shouldBeCreated) {
183
189
  this.adapter.log.debug(
184
- `Skipping device ${child.deviceName} (${child.deviceDesignation}) – no definable control states found`,
190
+ `Skipping device ${child.deviceName} (${child.deviceDesignation}) – no definable states found`,
185
191
  );
186
192
  continue;
193
+ } else {
194
+ this.adapter.log.debug(
195
+ `Device ${child.deviceID}: kind=${child.definition.deviceType.kind}, capabilities=${JSON.stringify(capabilities)}`,
196
+ );
187
197
  }
188
198
 
189
199
  const schellenbergDevice = await this.deviceManager.createDevice({
190
200
  id: child.deviceID,
191
- name: child.deviceName,
201
+ name: child.deviceName.replace(/\${|}/g, ""),
192
202
  deviceType: child.deviceTypClient ?? child.definition.deviceType?.kind ?? "unknown",
193
203
  designation: child.deviceDesignation,
194
204
  definition: child.definition,
@@ -391,7 +401,7 @@ class SchellenbergBridge {
391
401
 
392
402
  sendAndReceiveCommand(command) {
393
403
  if (this.socket && this.loginResponse && this.loginResponse.sessionID) {
394
- return this.socket.sendAndRecieveCommand(command, this.loginResponse.sessionID);
404
+ return this.socket.sendAndReceiveCommand(command, this.loginResponse.sessionID);
395
405
  }
396
406
 
397
407
  this.adapter.log.error("Login to the gateway was not successful yet. Ignoring command.");
@@ -70,7 +70,7 @@ exports.LoginCommand = LoginCommand;
70
70
 
71
71
  class allNewInfoCommand extends JSONCommand.default {
72
72
  constructor(timestamp, compatibilityConfigurationVersion, languageTranslationVersion) {
73
- super("getAllNewInfos");
73
+ super("getAllNewInfos", 15000); // higher timeout as this command might take longer to receive a response
74
74
  this.timestamp = timestamp;
75
75
  this.compatibilityConfigurationVersion = compatibilityConfigurationVersion;
76
76
  this.languageTranslationVersion = languageTranslationVersion;
@@ -169,16 +169,24 @@ class SmartSocket {
169
169
  //sends command and waits for receive in the promise queue of the message handler
170
170
  //don't know if the output socket of the gateway is in-order, so maybe there needs
171
171
  //to be some checking
172
- sendAndRecieveCommand(command, sessionkey) {
173
- const localPromise = new Deferred.default(this.adapter, (resolve, reject) => {
174
- if (this.internalSocket && command) {
175
- this.adapter.log.debug(`Send and receive JSON: ${command.toString(sessionkey)}`);
176
- this.internalSocket.write(command.toString(sessionkey));
177
- this.internalSocket.write("\n");
178
- } else {
179
- reject();
180
- }
181
- });
172
+ sendAndReceiveCommand(command, sessionkey) {
173
+ const timeout = command.timeout ?? 5000; // default timeout is 5 sec
174
+
175
+ const localPromise = new Deferred.default(
176
+ this.adapter,
177
+ (resolve, reject) => {
178
+ if (this.internalSocket && command) {
179
+ this.adapter.log.debug(
180
+ `Send and receive JSON (timeout: ${timeout}ms): ${command.toString(sessionkey)}`,
181
+ );
182
+ this.internalSocket.write(command.toString(sessionkey));
183
+ this.internalSocket.write("\n");
184
+ } else {
185
+ reject();
186
+ }
187
+ },
188
+ timeout,
189
+ );
182
190
  if (this.dataDelegate) {
183
191
  this.dataDelegate.queueUpPromise(localPromise);
184
192
  }
@@ -35,7 +35,7 @@ class SmartSocketFactory {
35
35
  .then(socket => {
36
36
  if (socket) {
37
37
  socket
38
- .sendAndRecieveCommand(CommandFactory.default.createHeloCmd(username))
38
+ .sendAndReceiveCommand(CommandFactory.default.createHeloCmd(username))
39
39
  .then(responseHelo => {
40
40
  if (responseHelo.response) {
41
41
  const parsedResponseHelo = HeloResponse.default.fromObject(
@@ -52,7 +52,7 @@ class SmartSocketFactory {
52
52
  parsedResponseHelo.sessionSalt,
53
53
  );
54
54
  socket
55
- .sendAndRecieveCommand(
55
+ .sendAndReceiveCommand(
56
56
  CommandFactory.default.createLoginCommand(
57
57
  username,
58
58
  digest,
@@ -8,13 +8,16 @@
8
8
  //--------------------------------------------------
9
9
 
10
10
  class JSONCommand {
11
- constructor(method) {
11
+ constructor(method, timeoutMs = 5000) {
12
12
  this.command = method;
13
+ this.timeout = timeoutMs;
13
14
  }
15
+
14
16
  toString(sessionID) {
15
17
  if (sessionID) {
16
18
  this.sessionID = sessionID;
17
19
  }
20
+
18
21
  return JSON.stringify(this);
19
22
  }
20
23
  }
@@ -0,0 +1,70 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * Declarative registry for device kinds that provide
5
+ * a level-like control (position, brightness, etc.)
6
+ */
7
+ const LevelKinds = {
8
+ position: {
9
+ role: "level.blind",
10
+ desc: "Position of the device",
11
+ unit: "%",
12
+ defaultMin: 0,
13
+ defaultMax: 100,
14
+ defaultStep: 1,
15
+ },
16
+ brightness: {
17
+ role: "level.dimmer",
18
+ desc: "Brightness of the device",
19
+ unit: "%",
20
+ defaultMin: 0,
21
+ defaultMax: 100,
22
+ defaultStep: 1,
23
+ },
24
+ };
25
+
26
+ /**
27
+ * Declarative registry for device kinds that provide
28
+ * a sensor state information (temperature, rain, etc.)
29
+ */
30
+ const SensorKinds = {
31
+ thermometer: {
32
+ role: "level.temperature",
33
+ desc: "Measured temperature",
34
+ unit: "°C",
35
+ defaultMin: -90,
36
+ defaultMax: 60,
37
+ defaultStep: 0.5,
38
+ },
39
+ volume: {
40
+ role: "value.rain",
41
+ desc: "Measured rain volume",
42
+ unit: "mm",
43
+ defaultMin: 0,
44
+ defaultMax: 350,
45
+ defaultStep: 1,
46
+ },
47
+ // generic sensor kind for different types of weather sensors (e.g. atmospheric pressure, wind speed, etc.)
48
+ weather: {
49
+ role: "value",
50
+ desc: "Measured value",
51
+ unit: "",
52
+ },
53
+ };
54
+
55
+ /**
56
+ * Declarative registry for device kinds that provide
57
+ * an alarm information
58
+ */
59
+ const AlarmKinds = {
60
+ failureStatus: {
61
+ role: "value",
62
+ desc: "Error code of the device",
63
+ },
64
+ };
65
+
66
+ module.exports = {
67
+ AlarmKinds,
68
+ LevelKinds,
69
+ SensorKinds,
70
+ };
@@ -0,0 +1,90 @@
1
+ "use strict";
2
+
3
+ const { LevelKinds, SensorKinds, AlarmKinds } = require("./DeviceCapabilityRegistry");
4
+
5
+ /**
6
+ * Resolves capabilities from a SmartFriends device definition
7
+ */
8
+ function getDeviceCapabilities(definition) {
9
+ if (!definition?.deviceType) {
10
+ return {
11
+ shouldBeCreated: false,
12
+ hasWritableStates: false,
13
+ hasSwitchingValues: false,
14
+ hasLevel: false,
15
+ hasSensor: false,
16
+ hasAlarm: false,
17
+ configObj: null,
18
+ };
19
+ }
20
+
21
+ const deviceType = definition?.deviceType ?? {};
22
+ const kind = deviceType.kind ?? "unknown";
23
+ const deviceDesignation = definition?.deviceDesignation?.replace(/\${|}/g, "") ?? kind;
24
+ const model = deviceType.model ?? "unknown";
25
+
26
+ const switchingValues = Array.isArray(deviceType.switchingValues) ? deviceType.switchingValues : [];
27
+
28
+ const hasSwitchingValues = switchingValues.length > 0;
29
+ let configObj = {
30
+ kind,
31
+ name: deviceDesignation,
32
+ };
33
+
34
+ const levelConfig = LevelKinds[kind] ?? null;
35
+ const sensorConfig = (SensorKinds[kind] && model == "analog") ?? null; // only sensor type "analog" contains measurable values
36
+ const alarmConfig = AlarmKinds[kind] ?? null;
37
+
38
+ if (levelConfig) {
39
+ configObj = {
40
+ ...configObj,
41
+ role: levelConfig.role,
42
+ desc: levelConfig.desc,
43
+ unit: levelConfig.unit,
44
+ min: deviceType.min ?? levelConfig.defaultMin,
45
+ max: deviceType.max ?? levelConfig.defaultMax,
46
+ step: deviceType.step ?? levelConfig.defaultStep,
47
+ };
48
+ } else if (sensorConfig) {
49
+ configObj = {
50
+ ...configObj,
51
+ role: sensorConfig.role,
52
+ desc: sensorConfig.desc,
53
+ unit: sensorConfig.unit,
54
+ min: deviceType.min ?? sensorConfig.defaultMin,
55
+ max: deviceType.max ?? sensorConfig.defaultMax,
56
+ step: deviceType.step ?? sensorConfig.defaultStep,
57
+ };
58
+ } else if (alarmConfig) {
59
+ configObj = {
60
+ ...configObj,
61
+ role: alarmConfig.role,
62
+ desc: alarmConfig.desc,
63
+ };
64
+
65
+ // map "textOptions" (if present) into configObj as enumerated states
66
+ if (deviceType.textOptions && Array.isArray(deviceType.textOptions) && deviceType.textOptions.length > 0) {
67
+ configObj.states = deviceType.textOptions.reduce((acc, opt) => {
68
+ acc[opt.value] = `${opt.state} (${opt.name.replace(/\${|}/g, "")})`;
69
+ return acc;
70
+ }, {});
71
+ }
72
+ }
73
+
74
+ return {
75
+ // state creation decision
76
+ shouldBeCreated: hasSwitchingValues || !!levelConfig || !!sensorConfig || !!alarmConfig,
77
+ hasWritableStates: hasSwitchingValues || !!levelConfig,
78
+ // metadata for builders
79
+ hasSwitchingValues,
80
+ hasLevel: !!levelConfig,
81
+ hasSensor: !!sensorConfig,
82
+ hasAlarm: !!alarmConfig,
83
+ // config object for state creation
84
+ configObj: configObj,
85
+ };
86
+ }
87
+
88
+ module.exports = {
89
+ getDeviceCapabilities,
90
+ };
@@ -1,4 +1,4 @@
1
- const { SchellenbergDevice } = require("./SchellenbergDevice");
1
+ const SchellenbergDevice = require("./SchellenbergDevice");
2
2
  const commonDefines = require("../helpers/CommonDefines");
3
3
  const commandFactory = require("../comunication/CommandFactory");
4
4
 
@@ -1,5 +1,6 @@
1
1
  "use strict";
2
2
 
3
+ const { getDeviceCapabilities } = require("./DeviceKindCapabilities");
3
4
  const commonDefines = require("../helpers/CommonDefines");
4
5
 
5
6
  class SchellenbergDevice {
@@ -16,6 +17,9 @@ class SchellenbergDevice {
16
17
 
17
18
  // Creates all necessery states and channels and writes the values into the DB
18
19
  async CreateAndSave(masterPrefix) {
20
+ // Dynamisch aus device.definition
21
+ const capabilities = getDeviceCapabilities(this.definition);
22
+
19
23
  let devicePrefix = masterPrefix
20
24
  ? `${masterPrefix}.${this.id}`
21
25
  : `${commonDefines.AdapterDatapointIDs.Devices}.${this.id}`;
@@ -83,65 +87,121 @@ class SchellenbergDevice {
83
87
  },
84
88
  native: {},
85
89
  });
90
+
91
+ if (capabilities.hasSensor) {
92
+ await this.createSensorState(infoPrefix, capabilities.configObj);
93
+ }
94
+
95
+ if (capabilities.hasAlarm) {
96
+ await this.createAlarmState(infoPrefix, capabilities.configObj);
97
+ }
86
98
  //#endregion
87
99
 
88
100
  //#region CONTROL
89
- let controlPrefix = `${devicePrefix}.${commonDefines.AdapterDatapointIDs.Control}`;
101
+ if (capabilities.hasWritableStates) {
102
+ let controlPrefix = `${devicePrefix}.${commonDefines.AdapterDatapointIDs.Control}`;
90
103
 
91
- await this.adapter.setObjectNotExistsAsync(controlPrefix, {
92
- type: "channel",
93
- common: {
94
- name: "Device control",
95
- },
96
- native: {},
97
- });
104
+ await this.adapter.setObjectNotExistsAsync(controlPrefix, {
105
+ type: "channel",
106
+ common: {
107
+ name: "Device control",
108
+ },
109
+ native: {},
110
+ });
98
111
 
99
- controlPrefix += ".";
112
+ controlPrefix += ".";
100
113
 
101
- // Dynamisch aus device.definition
102
- if (this.definition?.deviceType?.switchingValues) {
103
- for (const stateDef of this.definition.deviceType.switchingValues) {
104
- const stateId = stateDef.name
105
- .replace(/\${|}/g, "") // ${On} → On
106
- .replace(this.adapter.FORBIDDEN_CHARS, "")
107
- .toLowerCase();
108
-
109
- await this.adapter.setObjectNotExistsAsync(`${controlPrefix}${stateId}`, {
110
- type: "state",
111
- common: {
112
- name: stateId,
113
- type: "boolean",
114
- role: "button",
115
- read: false,
116
- write: true,
117
- def: false,
118
- },
119
- native: {
120
- commandValue: stateDef.value,
121
- },
122
- });
114
+ if (capabilities.hasSwitchingValues) {
115
+ await this.createSwitchStates(controlPrefix);
123
116
  }
124
- } else if (this.definition.deviceType.kind === commonDefines.AdapterStateIDs.Position) {
125
- await this.adapter.setObjectNotExistsAsync(`${controlPrefix}${commonDefines.AdapterStateIDs.Position}`, {
117
+
118
+ if (capabilities.hasLevel) {
119
+ await this.createLevelState(controlPrefix, capabilities.configObj);
120
+ }
121
+ }
122
+ //#endregion
123
+
124
+ this.adapter.log.debug(`Created and saved device ${this.id} (${this.name})`);
125
+ }
126
+
127
+ async createSwitchStates(statePrefix) {
128
+ for (const stateDef of this.definition.deviceType.switchingValues) {
129
+ const stateId = stateDef.name
130
+ .replace(/\${|}/g, "") // ${On} → On
131
+ .replace(this.adapter.FORBIDDEN_CHARS, "")
132
+ .toLowerCase();
133
+
134
+ await this.adapter.setObjectNotExistsAsync(`${statePrefix}${stateId}`, {
126
135
  type: "state",
127
136
  common: {
128
- name: commonDefines.AdapterStateIDs.Position,
129
- type: "number",
130
- role: "level.blind",
131
- read: true,
137
+ name: stateId,
138
+ type: "boolean",
139
+ role: "button",
140
+ read: false,
132
141
  write: true,
133
- min: this.definition.deviceType.min ?? 0,
134
- max: this.definition.deviceType.max ?? 100,
135
- step: this.definition.deviceType.step ?? 1,
136
- unit: "%",
137
- def: 0,
142
+ def: false,
143
+ },
144
+ native: {
145
+ commandValue: stateDef.value,
138
146
  },
139
- native: {},
140
147
  });
141
148
  }
142
- //#endregion
149
+ }
143
150
 
144
- this.adapter.log.debug(`Created and saved device ${this.id} (${this.name})`);
151
+ async createLevelState(statePrefix, levelConfigObj) {
152
+ await this.adapter.setObjectNotExistsAsync(`${statePrefix}${levelConfigObj.kind}`, {
153
+ type: "state",
154
+ common: {
155
+ name: levelConfigObj.kind,
156
+ desc: levelConfigObj.desc,
157
+ type: "number",
158
+ role: levelConfigObj.role,
159
+ read: true,
160
+ write: true,
161
+ min: levelConfigObj.min,
162
+ max: levelConfigObj.max,
163
+ step: levelConfigObj.step,
164
+ unit: levelConfigObj.unit,
165
+ def: 0,
166
+ },
167
+ native: {},
168
+ });
169
+ }
170
+
171
+ async createSensorState(statePrefix, sensorConfigObj) {
172
+ await this.adapter.setObjectNotExistsAsync(`${statePrefix}${sensorConfigObj.kind}`, {
173
+ type: "state",
174
+ common: {
175
+ name: sensorConfigObj.kind,
176
+ desc: sensorConfigObj.desc,
177
+ type: "number",
178
+ role: sensorConfigObj.role,
179
+ read: true,
180
+ write: false,
181
+ min: sensorConfigObj.min,
182
+ max: sensorConfigObj.max,
183
+ step: sensorConfigObj.step,
184
+ unit: sensorConfigObj.unit,
185
+ def: 0,
186
+ },
187
+ native: {},
188
+ });
189
+ }
190
+
191
+ async createAlarmState(statePrefix, alarmConfigObj) {
192
+ await this.adapter.setObjectNotExistsAsync(`${statePrefix}${alarmConfigObj.kind}`, {
193
+ type: "state",
194
+ common: {
195
+ name: alarmConfigObj.kind,
196
+ desc: alarmConfigObj.desc,
197
+ type: "number",
198
+ role: alarmConfigObj.role,
199
+ read: true,
200
+ write: false,
201
+ states: alarmConfigObj.states,
202
+ },
203
+ native: {},
204
+ });
145
205
  }
146
206
 
147
207
  // Only writes changed data into the DB
@@ -173,23 +233,44 @@ class SchellenbergDevice {
173
233
  }
174
234
 
175
235
  let controlPrefix = "";
236
+ let infoPrefix = "";
176
237
  if (value.masterDeviceID != this.id) {
177
238
  // Child-Device unter einem Master-Device
178
239
  controlPrefix = `${commonDefines.AdapterDatapointIDs.Devices}.${value.masterDeviceID}.${this.id}.${commonDefines.AdapterDatapointIDs.Control}.`;
240
+ infoPrefix = `${commonDefines.AdapterDatapointIDs.Devices}.${value.masterDeviceID}.${this.id}.${commonDefines.AdapterDatapointIDs.Info}.`;
179
241
  } else {
180
242
  // Einzelnes Device
181
243
  controlPrefix = `${commonDefines.AdapterDatapointIDs.Devices}.${this.id}.${commonDefines.AdapterDatapointIDs.Control}.`;
244
+ infoPrefix = `${commonDefines.AdapterDatapointIDs.Devices}.${this.id}.${commonDefines.AdapterDatapointIDs.Info}.`;
182
245
  }
183
246
 
184
- if (this.definition?.deviceType?.switchingValues) {
247
+ // Dynamisch aus device.definition
248
+ const capabilities = getDeviceCapabilities(this.definition);
249
+
250
+ if (capabilities.hasSwitchingValues) {
185
251
  // SwitchingValues ignorieren, liefern keine Updates zurück
186
- } else if (this.definition?.deviceType?.kind === commonDefines.AdapterStateIDs.Position) {
187
- // Für Position → direkt setzen
188
- const stateId = `${controlPrefix}${commonDefines.AdapterStateIDs.Position}`;
189
- await this.adapter.setStateAsync(stateId, value.value, true);
190
- this.adapter.log.debug(`Device ${this.id}: Updated position = ${value.value}`);
252
+ this.adapter.log.debug(`Device ${this.id}: switchingValue update ignored (value=${value.value})`);
253
+ return;
191
254
  }
255
+
256
+ if (capabilities.hasLevel) {
257
+ await this.setDeviceStateValue(controlPrefix, capabilities.configObj, value);
258
+ }
259
+
260
+ if (capabilities.hasSensor) {
261
+ await this.setDeviceStateValue(infoPrefix, capabilities.configObj, value);
262
+ }
263
+
264
+ if (capabilities.hasAlarm) {
265
+ await this.setDeviceStateValue(infoPrefix, capabilities.configObj, value);
266
+ }
267
+ }
268
+
269
+ async setDeviceStateValue(statePrefix, configObj, value) {
270
+ const stateId = `${statePrefix}${configObj.kind}`;
271
+ await this.adapter.setStateAsync(stateId, value.value, true);
272
+ this.adapter.log.debug(`Device ${this.id}: Updated ${configObj.kind} = ${value.value}`);
192
273
  }
193
274
  }
194
275
 
195
- exports.SchellenbergDevice = SchellenbergDevice;
276
+ module.exports = SchellenbergDevice;
@@ -10,30 +10,38 @@
10
10
  //--------------------------------------------------
11
11
 
12
12
  class Deferred {
13
- constructor(adapter, executor) {
13
+ constructor(adapter, executor, timeoutMs = 5000) {
14
14
  this.adapter = adapter;
15
+
15
16
  this.timeout = this.adapter.setTimeout(() => {
16
17
  this.reject("timeout");
17
- }, 5000);
18
+ }, timeoutMs);
19
+
18
20
  this.promise = new Promise((resolve, reject) => {
19
21
  this._resolveSelf = resolve;
20
22
  this._rejectSelf = reject;
21
23
  });
24
+
22
25
  executor.call(this, this._resolveSelf, this._rejectSelf);
23
26
  }
27
+
24
28
  then(onfulfilled, onrejected) {
25
29
  return this.promise.then(onfulfilled, onrejected);
26
30
  }
31
+
27
32
  catch(onrejected) {
28
33
  return this.promise.then(onrejected);
29
34
  }
35
+
30
36
  finally(onfinally) {
31
37
  return this.promise.finally(onfinally);
32
38
  }
39
+
33
40
  resolve(val) {
34
41
  this.adapter.clearTimeout(this.timeout);
35
42
  this._resolveSelf(val);
36
43
  }
44
+
37
45
  reject(reason) {
38
46
  this.adapter.clearTimeout(this.timeout);
39
47
  this._rejectSelf(reason);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "iobroker.smartfriends",
3
- "version": "1.1.0",
3
+ "version": "1.2.0-alpha.1",
4
4
  "description": "smartfriends",
5
5
  "author": {
6
6
  "name": "Black-Thunder",