iobroker.sonoff 4.2.0 → 5.0.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.
@@ -1,10 +1,40 @@
1
1
  "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
5
35
  Object.defineProperty(exports, "__esModule", { value: true });
6
36
  exports.FORBIDDEN_CHARS = void 0;
7
- const datapoints_1 = __importDefault(require("./datapoints"));
37
+ const datapoints_1 = __importStar(require("./datapoints"));
8
38
  const hueCalc = true;
9
39
  exports.FORBIDDEN_CHARS = /[\]\\[*,;'"`<>\\?]/g;
10
40
  const NO_PREFIX = '';
@@ -214,9 +244,30 @@ class MQTTBase {
214
244
  this.processTasks().catch(err => this.adapter.log.error(err));
215
245
  }
216
246
  }
247
+ /**
248
+ * Finds the device channel (the `iobId` of its client) a state belongs to. That is not simply the
249
+ * first segment after "<namespace>.": the state can be nested (e.g. "STATE.POWER1" with "Create
250
+ * object tree" on), and a client ID may contain dots itself, as `FORBIDDEN_CHARS` keeps them. So
251
+ * the longest leading path that belongs to a known client wins - the first segment is only the
252
+ * fallback for a client that is not connected.
253
+ *
254
+ * @param id full state ID, e.g. "sonoff.0.DVES_123456.STATE.POWER1"
255
+ */
256
+ getChannelId(id) {
257
+ const parts = id.substring(this.adapter.namespace.length + 1).split('.');
258
+ // The last segment is the data point itself, never part of the channel
259
+ for (let i = parts.length - 1; i > 1; i--) {
260
+ const channelId = parts.slice(0, i).join('.');
261
+ if (this.mappingClients[channelId]) {
262
+ return channelId;
263
+ }
264
+ }
265
+ return parts[0];
266
+ }
217
267
  async onStateChangedColors(id, state, channelId, stateId) {
218
268
  if (!channelId) {
219
269
  const parts = id.split('.');
270
+ channelId = this.getChannelId(id);
220
271
  stateId = parts.pop() || '';
221
272
  if (stateId === 'level' ||
222
273
  stateId === 'state' ||
@@ -225,7 +276,6 @@ class MQTTBase {
225
276
  stateId === 'green') {
226
277
  stateId = `${parts.pop()}.${stateId}`;
227
278
  }
228
- channelId = parts.splice(2, parts.length).join('.');
229
279
  }
230
280
  const ledModeIdExor = `${this.adapter.namespace}.${channelId}.modeLedExor`;
231
281
  if (this.cachedModeExor[ledModeIdExor] === undefined) {
@@ -555,9 +605,8 @@ class MQTTBase {
555
605
  this.adapter.log.debug(`onStateChange ${id}: ${JSON.stringify(state)}`);
556
606
  if (state && !state.ack) {
557
607
  // find client.id
558
- const parts = id.split('.');
559
- const stateId = parts.pop() || '';
560
- const channelId = parts.splice(2, parts.length).join('.');
608
+ const channelId = this.getChannelId(id);
609
+ const stateId = id.split('.').pop() || '';
561
610
  // Check if this is a Zigbee device state change
562
611
  // Pattern: ZbReceived_DEVICEID_ATTRIBUTE (e.g., ZbReceived_0x0856_Power)
563
612
  const zbMatch = stateId.match(/^ZbReceived_([^_]+)_(Power|Dimmer)$/);
@@ -659,15 +708,31 @@ class MQTTBase {
659
708
  this.adapter.log.warn(`New object creation error: ${err.message}`);
660
709
  }
661
710
  }
662
- else if (obj.common.type !== task.data.common.type ||
663
- task.storeMap !== undefined) {
664
- obj.common.type = task.data.common.type;
665
- try {
666
- await this.adapter.setForeignObjectAsync(task.id, obj);
667
- this.adapter.log.info(`Object updated: ${task.id}`);
668
- }
669
- catch (err) {
670
- this.adapter.log.warn(`Object update error: ${err.message}`);
711
+ else {
712
+ const common = task.data.common;
713
+ // Objects created by older versions get the current role, so that the type-detector can
714
+ // recognize the device. A role changed by the user is never in the list and stays as it is.
715
+ const updateRole = !!common.role &&
716
+ obj.common.role !== common.role &&
717
+ !!task.legacyRoles?.includes(obj.common.role || '');
718
+ if (obj.common.type !== common.type || task.storeMap !== undefined || updateRole) {
719
+ obj.common.type = common.type;
720
+ if (updateRole) {
721
+ obj.common.role = common.role;
722
+ obj.common.write = common.write;
723
+ for (const attr of ['unit', 'min', 'max']) {
724
+ if (common[attr] !== undefined) {
725
+ obj.common[attr] = common[attr];
726
+ }
727
+ }
728
+ }
729
+ try {
730
+ await this.adapter.setForeignObjectAsync(task.id, obj);
731
+ this.adapter.log.info(`Object updated: ${task.id}`);
732
+ }
733
+ catch (err) {
734
+ this.adapter.log.warn(`Object update error: ${err.message}`);
735
+ }
671
736
  }
672
737
  }
673
738
  }
@@ -797,6 +862,7 @@ class MQTTBase {
797
862
  native: {},
798
863
  type: 'state',
799
864
  },
865
+ legacyRoles: datapoints_1.legacyRoles[typeKey],
800
866
  };
801
867
  }
802
868
  async syncPowerState(powerId, val) {
@@ -1049,19 +1115,33 @@ class MQTTBase {
1049
1115
  const attributes = {
1050
1116
  role: 'value',
1051
1117
  read: true,
1052
- write: false,
1118
+ // RESULT is the answer to a command, so it can be controlled
1119
+ write: parts[2] === 'RESULT',
1053
1120
  type: typeof val,
1054
1121
  name: `${client.id} ${prefix ? `${prefix} ` : ''}${path.length ? `${path.join(' ')} ` : ''} ${replaceAttr}`,
1055
1122
  };
1056
- if (parts[2] === 'RESULT') {
1057
- // control
1058
- attributes.write = true;
1059
- attributes.role = 'level';
1060
- }
1061
1123
  if (!['number', 'string', 'boolean'].includes(attributes.type)) {
1062
1124
  attributes.type = 'string';
1063
1125
  val = (val || '').toString();
1064
1126
  }
1127
+ const autoRole = datapoints_1.autoRoles[path.length ? `${path.join('_')}_${attr}` : attr];
1128
+ if (autoRole) {
1129
+ attributes.role = autoRole.role;
1130
+ if (autoRole.unit) {
1131
+ attributes.unit = autoRole.unit;
1132
+ }
1133
+ }
1134
+ else if (attributes.type === 'string') {
1135
+ attributes.role = 'text';
1136
+ }
1137
+ else if (attributes.type === 'boolean') {
1138
+ // "switch" and "state" would be taken by the type-detector for a light or a socket
1139
+ attributes.role = attributes.write ? 'switch.setting' : 'indicator';
1140
+ }
1141
+ else if (attributes.write) {
1142
+ // "level" would be taken by the type-detector for a dimmer or a blind
1143
+ attributes.role = 'state';
1144
+ }
1065
1145
  const id = `${this.adapter.namespace}.${client.iobId}.${prefix ? `${prefix}.` : ''}${path.length ? `${path.join('_')}_` : ''}${replaceAttr}`;
1066
1146
  const obj = {
1067
1147
  id,
@@ -1071,6 +1151,7 @@ class MQTTBase {
1071
1151
  native: {},
1072
1152
  type: 'state',
1073
1153
  },
1154
+ legacyRoles: datapoints_1.autoLegacyRoles,
1074
1155
  };
1075
1156
  obj.data.common.name =
1076
1157
  `${client.id} ${prefix ? `${prefix} ` : ''}${path.length ? `${path.join(' ')} ` : ''} ${replaceAttr}`;
@@ -1262,6 +1343,7 @@ class MQTTBase {
1262
1343
  native: {},
1263
1344
  type: 'state',
1264
1345
  },
1346
+ legacyRoles: datapoints_1.legacyRoles[stateId],
1265
1347
  };
1266
1348
  // push only new objects
1267
1349
  this.updateState(obj, async () => {