iobroker.iot 3.5.2 → 4.0.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/README.md +24 -9
- package/admin/assets/index-Y479CE6x.js +681 -0
- package/admin/index_m.html +1 -1
- package/admin/rules/assets/{ActionVisu-D9x2f6zu.js → ActionVisu-DE8dgNVF.js} +1 -1
- package/admin/rules/assets/{ActionVisu__loadShare__react__loadShare__-Cg21Ef1U.js → ActionVisu__loadShare__react__loadShare__-BKaC-p5K.js} +1 -1
- package/admin/rules/assets/{ActionVisu__loadShare__react_mf_2_dom__loadShare__-B1zRWn0u.js → ActionVisu__loadShare__react_mf_2_dom__loadShare__-Bs1o8DtQ.js} +2 -2
- package/admin/rules/assets/ActionVisu__mf_v__runtimeInit__mf_v__-DtUoI3FU.js +5 -0
- package/admin/rules/assets/bootstrap-Ba57tu25.js +1 -0
- package/admin/rules/assets/hostInit-hos3Txsy.js +1 -1
- package/admin/rules/assets/{index-Duo7mFlZ.js → index-CbxmbJvB.js} +2 -2
- package/admin/rules/assets/{index-CAAeiKCc.js → index-DjPMvPni.js} +2 -2
- package/admin/rules/assets/index-_Y2FN5EZ.js +1152 -0
- package/admin/rules/assets/{virtualExposes-BqVak7rX.js → virtualExposes-BNZJTatg.js} +2 -2
- package/admin/rules/customRuleBlocks.js +2 -2
- package/io-package.json +27 -27
- package/package.json +17 -14
- package/admin/assets/index-Df2NS929.js +0 -681
- package/admin/rules/assets/ActionVisu__mf_v__runtimeInit__mf_v__-Cz053nO7.js +0 -10
- package/admin/rules/assets/bootstrap-BCFKIiwZ.js +0 -1
- package/admin/rules/assets/index-BwcOGyWI.js +0 -1152
- package/lib/AlexaSmartHomeV3/Alexa/AlexaResponse.js +0 -262
- package/lib/AlexaSmartHomeV3/Alexa/Capabilities/Base.js +0 -124
- package/lib/AlexaSmartHomeV3/Alexa/Capabilities/BrightnessController.js +0 -15
- package/lib/AlexaSmartHomeV3/Alexa/Capabilities/ColorController.js +0 -10
- package/lib/AlexaSmartHomeV3/Alexa/Capabilities/ColorTemperatureController.js +0 -15
- package/lib/AlexaSmartHomeV3/Alexa/Capabilities/ContactSensor.js +0 -10
- package/lib/AlexaSmartHomeV3/Alexa/Capabilities/LockController.js +0 -10
- package/lib/AlexaSmartHomeV3/Alexa/Capabilities/ModeController.js +0 -69
- package/lib/AlexaSmartHomeV3/Alexa/Capabilities/MotionSensor.js +0 -10
- package/lib/AlexaSmartHomeV3/Alexa/Capabilities/PercentageController.js +0 -10
- package/lib/AlexaSmartHomeV3/Alexa/Capabilities/PowerController.js +0 -15
- package/lib/AlexaSmartHomeV3/Alexa/Capabilities/Speaker.js +0 -20
- package/lib/AlexaSmartHomeV3/Alexa/Capabilities/TemperatureSensor.js +0 -10
- package/lib/AlexaSmartHomeV3/Alexa/Capabilities/ThermostatController.js +0 -34
- package/lib/AlexaSmartHomeV3/Alexa/Capabilities/index.js +0 -14
- package/lib/AlexaSmartHomeV3/Alexa/Directives/Base.js +0 -27
- package/lib/AlexaSmartHomeV3/Alexa/Directives/ChangeReport.js +0 -75
- package/lib/AlexaSmartHomeV3/Alexa/Directives/Discovery.js +0 -39
- package/lib/AlexaSmartHomeV3/Alexa/Directives/ReportState.js +0 -50
- package/lib/AlexaSmartHomeV3/Alexa/Directives/index.js +0 -13
- package/lib/AlexaSmartHomeV3/Alexa/ModeValues/Base.js +0 -38
- package/lib/AlexaSmartHomeV3/Alexa/ModeValues/Closed.js +0 -55
- package/lib/AlexaSmartHomeV3/Alexa/ModeValues/Open.js +0 -56
- package/lib/AlexaSmartHomeV3/Alexa/ModeValues/index.js +0 -14
- package/lib/AlexaSmartHomeV3/Alexa/Properties/AdjustableProperty.js +0 -11
- package/lib/AlexaSmartHomeV3/Alexa/Properties/Base.js +0 -108
- package/lib/AlexaSmartHomeV3/Alexa/Properties/Brightness.js +0 -14
- package/lib/AlexaSmartHomeV3/Alexa/Properties/Color.js +0 -40
- package/lib/AlexaSmartHomeV3/Alexa/Properties/ColorTemperatureInKelvin.js +0 -62
- package/lib/AlexaSmartHomeV3/Alexa/Properties/DetectionState.js +0 -22
- package/lib/AlexaSmartHomeV3/Alexa/Properties/LockState.js +0 -40
- package/lib/AlexaSmartHomeV3/Alexa/Properties/Mode.js +0 -32
- package/lib/AlexaSmartHomeV3/Alexa/Properties/Muted.js +0 -17
- package/lib/AlexaSmartHomeV3/Alexa/Properties/Percentage.js +0 -14
- package/lib/AlexaSmartHomeV3/Alexa/Properties/PowerState.js +0 -25
- package/lib/AlexaSmartHomeV3/Alexa/Properties/TargetSetpoint.js +0 -28
- package/lib/AlexaSmartHomeV3/Alexa/Properties/Temperature.js +0 -20
- package/lib/AlexaSmartHomeV3/Alexa/Properties/ThermostatMode.js +0 -52
- package/lib/AlexaSmartHomeV3/Alexa/Properties/Volume.js +0 -19
- package/lib/AlexaSmartHomeV3/Alexa/Properties/index.js +0 -14
- package/lib/AlexaSmartHomeV3/Controls/AdjustableControl.js +0 -22
- package/lib/AlexaSmartHomeV3/Controls/AdjustablePercentageControl.js +0 -28
- package/lib/AlexaSmartHomeV3/Controls/AirCondition.js +0 -144
- package/lib/AlexaSmartHomeV3/Controls/Blind.js +0 -12
- package/lib/AlexaSmartHomeV3/Controls/ContactSensor.js +0 -17
- package/lib/AlexaSmartHomeV3/Controls/Control.js +0 -352
- package/lib/AlexaSmartHomeV3/Controls/Ct.js +0 -172
- package/lib/AlexaSmartHomeV3/Controls/Dimmer.js +0 -124
- package/lib/AlexaSmartHomeV3/Controls/Door.js +0 -9
- package/lib/AlexaSmartHomeV3/Controls/Gate.js +0 -42
- package/lib/AlexaSmartHomeV3/Controls/Hue.js +0 -207
- package/lib/AlexaSmartHomeV3/Controls/Light.js +0 -54
- package/lib/AlexaSmartHomeV3/Controls/Lock.js +0 -36
- package/lib/AlexaSmartHomeV3/Controls/Motion.js +0 -17
- package/lib/AlexaSmartHomeV3/Controls/ReadOnlyDetector.js +0 -25
- package/lib/AlexaSmartHomeV3/Controls/Slider.js +0 -10
- package/lib/AlexaSmartHomeV3/Controls/Socket.js +0 -21
- package/lib/AlexaSmartHomeV3/Controls/Temperature.js +0 -22
- package/lib/AlexaSmartHomeV3/Controls/Thermostat.js +0 -86
- package/lib/AlexaSmartHomeV3/Controls/VacuumCleaner.js +0 -28
- package/lib/AlexaSmartHomeV3/Controls/Volume.js +0 -109
- package/lib/AlexaSmartHomeV3/Controls/VolumeGroup.js +0 -9
- package/lib/AlexaSmartHomeV3/Controls/Window.js +0 -9
- package/lib/AlexaSmartHomeV3/Controls/index.js +0 -14
- package/lib/AlexaSmartHomeV3/Device.js +0 -84
- package/lib/AlexaSmartHomeV3/DeviceManager.js +0 -415
- package/lib/AlexaSmartHomeV3/Exceptions/AlexaV3Exception.js +0 -8
- package/lib/AlexaSmartHomeV3/Exceptions/HourlyDeviceRateLimitExceeded.js +0 -5
- package/lib/AlexaSmartHomeV3/Exceptions/OverallDailyRateLimitExceeded.js +0 -5
- package/lib/AlexaSmartHomeV3/Helpers/AdapterProvider.js +0 -46
- package/lib/AlexaSmartHomeV3/Helpers/FileHelper.js +0 -70
- package/lib/AlexaSmartHomeV3/Helpers/IotProxy.js +0 -44
- package/lib/AlexaSmartHomeV3/Helpers/Logger.js +0 -95
- package/lib/AlexaSmartHomeV3/Helpers/RateLimiter.js +0 -82
- package/lib/AlexaSmartHomeV3/Helpers/Utils.js +0 -583
- package/lib/Utils.js +0 -608
- package/lib/adminCommonSocket.js +0 -781
- package/lib/alexaCustom.js +0 -622
- package/lib/alexaSmartHomeV2.js +0 -2792
- package/lib/alexaSmartHomeV3.js +0 -117
- package/lib/alisa.js +0 -2412
- package/lib/devices.js +0 -20
- package/lib/functions.js +0 -21
- package/lib/googleHome.js +0 -3323
- package/lib/notifications.js +0 -37
- package/lib/remote.js +0 -1363
- package/lib/rooms.js +0 -45
- package/lib/texts.js +0 -79
- package/lib/translate.js +0 -19
- package/lib/visuApp.js +0 -157
- package/main.js +0 -1444
package/lib/alexaSmartHomeV2.js
DELETED
|
@@ -1,2792 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
function AlexaSH2(adapter) {
|
|
4
|
-
let Actions = require('../admin/actions');
|
|
5
|
-
let smartDevices = [];
|
|
6
|
-
let enums = [];
|
|
7
|
-
let valuesON = {};
|
|
8
|
-
let lang = 'de';
|
|
9
|
-
let translate = false;
|
|
10
|
-
let addedId = null;
|
|
11
|
-
|
|
12
|
-
let translateRooms;
|
|
13
|
-
let translateFunctions;
|
|
14
|
-
let translateDevices;
|
|
15
|
-
const words = {
|
|
16
|
-
'No name': { en: 'No name', de: 'Kein Name', ru: 'Нет имени' },
|
|
17
|
-
Group: { en: 'Group', de: 'Gruppe', ru: 'Группа' },
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
this.setLanguage = function (_lang, _translate) {
|
|
21
|
-
lang = _lang;
|
|
22
|
-
translate = _translate;
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
/* function validateName(name) {
|
|
26
|
-
if (!name) return false;
|
|
27
|
-
|
|
28
|
-
for (let n = 0; n < name.length; n++) {
|
|
29
|
-
if (name[n] === ';' || name[n] === '.' || name[n] === '-' || name[n] === ':') return false;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
return true;
|
|
33
|
-
} */
|
|
34
|
-
|
|
35
|
-
function getSmartName(statesOrObj, id) {
|
|
36
|
-
if (!id) {
|
|
37
|
-
if (!adapter.config.noCommon) {
|
|
38
|
-
return statesOrObj.common.smartName;
|
|
39
|
-
}
|
|
40
|
-
return statesOrObj?.common?.custom?.[adapter.namespace]
|
|
41
|
-
? statesOrObj.common.custom[adapter.namespace].smartName
|
|
42
|
-
: undefined;
|
|
43
|
-
}
|
|
44
|
-
if (!adapter.config.noCommon) {
|
|
45
|
-
return statesOrObj[id].common.smartName;
|
|
46
|
-
}
|
|
47
|
-
return statesOrObj[id]?.common?.custom?.[adapter.namespace]
|
|
48
|
-
? statesOrObj[id]?.common?.custom?.[adapter.namespace].smartName || null
|
|
49
|
-
: null;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
function padding(num) {
|
|
53
|
-
num = num.toString(16);
|
|
54
|
-
if (num.length < 2) {
|
|
55
|
-
num = `0${num}`;
|
|
56
|
-
}
|
|
57
|
-
return num;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
async function writeResponse(applianceId, operation, value) {
|
|
61
|
-
for (let d = 0; d < smartDevices.length; d++) {
|
|
62
|
-
if (smartDevices[d].applianceId === applianceId) {
|
|
63
|
-
let text;
|
|
64
|
-
let obj = smartDevices[d];
|
|
65
|
-
switch (operation) {
|
|
66
|
-
case 'ONOFF':
|
|
67
|
-
if (value) {
|
|
68
|
-
if (lang === 'de') {
|
|
69
|
-
text = `${obj.friendlyName} ist <emphasis>eingeschaltet</emphasis>!`;
|
|
70
|
-
} else if (lang === 'ru') {
|
|
71
|
-
text = `${obj.friendlyName} в состоянии включено`;
|
|
72
|
-
} else {
|
|
73
|
-
text = `${obj.friendlyName} turned on`;
|
|
74
|
-
}
|
|
75
|
-
} else {
|
|
76
|
-
if (lang === 'de') {
|
|
77
|
-
text = `${obj.friendlyName} ist <emphasis>ausgeschaltet</emphasis>!`;
|
|
78
|
-
} else if (lang === 'ru') {
|
|
79
|
-
text = `${obj.friendlyName} в состоянии <emphasis>выключено</emphasis>`;
|
|
80
|
-
} else {
|
|
81
|
-
text = `${obj.friendlyName} turned off`;
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
break;
|
|
85
|
-
|
|
86
|
-
case '%':
|
|
87
|
-
if (lang === 'de') {
|
|
88
|
-
text = `${obj.friendlyName} wird auf ${value} Prozent <emphasis>gesetzt</emphasis>`;
|
|
89
|
-
} else if (lang === 'ru') {
|
|
90
|
-
text = `Состояние ${obj.friendlyName} <emphasis>установлено</emphasis> на ${value} процентов`;
|
|
91
|
-
} else {
|
|
92
|
-
text = `${obj.friendlyName} set to ${value} <emphasis>percent</emphasis>`;
|
|
93
|
-
}
|
|
94
|
-
break;
|
|
95
|
-
|
|
96
|
-
case '°':
|
|
97
|
-
if (lang === 'de') {
|
|
98
|
-
text = `${obj.friendlyName} wird auf ${value} Grad <emphasis>gesetzt</emphasis>`;
|
|
99
|
-
} else if (lang === 'ru') {
|
|
100
|
-
text = `Состояние ${obj.friendlyName} <emphasis>установлено</emphasis> на ${value}`;
|
|
101
|
-
} else {
|
|
102
|
-
text = `${obj.friendlyName} set to ${value} <emphasis>grad</emphasis>`;
|
|
103
|
-
}
|
|
104
|
-
break;
|
|
105
|
-
|
|
106
|
-
case '°<>':
|
|
107
|
-
if (value >= 0) {
|
|
108
|
-
if (lang === 'de') {
|
|
109
|
-
text = `${obj.friendlyName} ist <emphasis>erhöht</emphasis> um ${value} Grad`;
|
|
110
|
-
} else if (lang === 'ru') {
|
|
111
|
-
text = `Состояние ${obj.friendlyName} <emphasis>увеличено</emphasis> на ${value}`;
|
|
112
|
-
} else {
|
|
113
|
-
text = `${obj.friendlyName} <emphasis>increased</emphasis> on ${value} percent`;
|
|
114
|
-
}
|
|
115
|
-
value = `+${value}`;
|
|
116
|
-
} else {
|
|
117
|
-
if (lang === 'de') {
|
|
118
|
-
text = `${obj.friendlyName} ist <emphasis>verkleinert</emphasis> um ${value} Grad`;
|
|
119
|
-
} else if (lang === 'ru') {
|
|
120
|
-
text = `Состояние ${obj.friendlyName} <emphasis>уменьшено</emphasis> на ${value}`;
|
|
121
|
-
} else {
|
|
122
|
-
text = `${obj.friendlyName} <emphasis>decreased</emphasis> on ${value} grad`;
|
|
123
|
-
}
|
|
124
|
-
value = `-${value}`;
|
|
125
|
-
}
|
|
126
|
-
break;
|
|
127
|
-
|
|
128
|
-
case '<>':
|
|
129
|
-
if (value >= 0) {
|
|
130
|
-
if (lang === 'de') {
|
|
131
|
-
text = `${obj.friendlyName} ist <emphasis>erhöht</emphasis> um ${value} Prozent`;
|
|
132
|
-
} else if (lang === 'ru') {
|
|
133
|
-
text = `Состояние ${obj.friendlyName} <emphasis>увеличено</emphasis> на ${value}`;
|
|
134
|
-
} else {
|
|
135
|
-
text = `${obj.friendlyName} <emphasis>increased</emphasis> on ${value} percent`;
|
|
136
|
-
}
|
|
137
|
-
value = `+${value}`;
|
|
138
|
-
} else {
|
|
139
|
-
if (lang === 'de') {
|
|
140
|
-
text = `${obj.friendlyName} ist <emphasis>verkleinert</emphasis> um ${value} Prozent`;
|
|
141
|
-
} else if (lang === 'ru') {
|
|
142
|
-
text = `Состояние ${obj.friendlyName} <emphasis>уменьшено</emphasis> на ${value}`;
|
|
143
|
-
} else {
|
|
144
|
-
text = `${obj.friendlyName} <emphasis>decreased</emphasis> on ${value} percent`;
|
|
145
|
-
}
|
|
146
|
-
value = `-${value}`;
|
|
147
|
-
}
|
|
148
|
-
break;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
if (text) {
|
|
152
|
-
await adapter.setStateAsync('smart.lastResponse', text, true);
|
|
153
|
-
if (adapter.config.responseOID) {
|
|
154
|
-
await adapter.setForeignStateAsync(adapter.config.responseOID, text, false);
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
await adapter.setStateAsync('smart.lastFunction', obj.additionalApplianceDetails.func, true);
|
|
158
|
-
await adapter.setStateAsync('smart.lastRoom', obj.additionalApplianceDetails.room, true);
|
|
159
|
-
await adapter.setStateAsync('smart.lastCommand', value, true);
|
|
160
|
-
}
|
|
161
|
-
return;
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
adapter.log.warn(`[ALEXA] Unknown applianceId: ${applianceId}`);
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
function processState(states, id, room, func, alexaIds, groups, names, result) {
|
|
168
|
-
// Make sure to also add the other necessary Endpoints:
|
|
169
|
-
/*adapter.log.debug('[ALEXA] Process: ' + id);
|
|
170
|
-
if(states[id].common.role == 'level.color.hue') {
|
|
171
|
-
let sat = findRole(states,id,'level.color.saturation');
|
|
172
|
-
let bri = findRole(states,id,'level.dimmer');
|
|
173
|
-
let on = findRole(states,id,'switch');
|
|
174
|
-
adapter.log.debug('[ALEXA] Adding needed: ' + sat + ' ' + bri + ' ' + on);
|
|
175
|
-
if(sat) processState(states, sat, room, func, alexaIds, groups, names, result);
|
|
176
|
-
if(bri) processState(states, bri, room, func, alexaIds, groups, names, result);
|
|
177
|
-
if(on) processState(states, on, room, func, alexaIds, groups, names, result);
|
|
178
|
-
}*/
|
|
179
|
-
|
|
180
|
-
try {
|
|
181
|
-
let friendlyName = getSmartName(states, id);
|
|
182
|
-
let nameModified = false;
|
|
183
|
-
let byON;
|
|
184
|
-
let smartType;
|
|
185
|
-
if (!id) {
|
|
186
|
-
return 'id is empty';
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
if (states[id]) {
|
|
190
|
-
// try to convert old notation to new one
|
|
191
|
-
if (states[id].native && !adapter.config.noCommon) {
|
|
192
|
-
if (states[id].native.byON) {
|
|
193
|
-
byON = states[id].native.byON;
|
|
194
|
-
delete states[id].native.byON;
|
|
195
|
-
let smartName = states[id].common.smartName;
|
|
196
|
-
|
|
197
|
-
if (!smartName || typeof smartName !== 'object') {
|
|
198
|
-
smartName = {
|
|
199
|
-
byON,
|
|
200
|
-
en: smartName,
|
|
201
|
-
};
|
|
202
|
-
smartName[lang] = smartName.en;
|
|
203
|
-
} else {
|
|
204
|
-
smartName.byON = byON;
|
|
205
|
-
}
|
|
206
|
-
states[id].common.smartName = smartName || {};
|
|
207
|
-
friendlyName = states[id].common.smartName;
|
|
208
|
-
} else if (typeof states[id].common.smartName === 'string') {
|
|
209
|
-
let nnn = states[id].common.smartName;
|
|
210
|
-
states[id].common.smartName = {};
|
|
211
|
-
states[id].common.smartName[lang] = nnn;
|
|
212
|
-
friendlyName = states[id].common.smartName;
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
} else {
|
|
216
|
-
adapter.log.debug(`[ALEXA] Invalid state "${id}". Not exist or no native part.`);
|
|
217
|
-
return `"${id}" not exists or no native part.`;
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
byON = friendlyName && typeof friendlyName === 'object' ? friendlyName.byON : '';
|
|
221
|
-
smartType = friendlyName && typeof friendlyName === 'object' ? friendlyName.smartType : null;
|
|
222
|
-
|
|
223
|
-
if (typeof friendlyName === 'object' && friendlyName) {
|
|
224
|
-
friendlyName = friendlyName[lang] || friendlyName.en;
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
if (friendlyName === 'ignore' || friendlyName === false) {
|
|
228
|
-
return `"${id}" must be ignored, because of the settings`;
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
if (!friendlyName && !room && !func) {
|
|
232
|
-
return `No friendly name and no room or function found for "${id}"`;
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
if (room && typeof room === 'object') {
|
|
236
|
-
room = room[lang] || room.en;
|
|
237
|
-
}
|
|
238
|
-
if (func && typeof func === 'object') {
|
|
239
|
-
func = func[lang] || func.en;
|
|
240
|
-
}
|
|
241
|
-
let friendlyNames = [];
|
|
242
|
-
if (!friendlyName) {
|
|
243
|
-
if (room) {
|
|
244
|
-
// translate room
|
|
245
|
-
if (translate) {
|
|
246
|
-
translateRooms = translateRooms || require('./rooms.js');
|
|
247
|
-
translateFunctions = translateFunctions || require('./functions.js');
|
|
248
|
-
room = translateRooms(lang, room);
|
|
249
|
-
func = translateFunctions(lang, func);
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
if (adapter.config.functionFirst) {
|
|
253
|
-
if (lang === 'en') {
|
|
254
|
-
friendlyName = `${func}${adapter.config.concatWord ? ` ${adapter.config.concatWord}` : ''} ${room}`;
|
|
255
|
-
} else {
|
|
256
|
-
friendlyName = `${func}${adapter.config.concatWord ? ` ${adapter.config.concatWord}` : ''} ${room}`;
|
|
257
|
-
}
|
|
258
|
-
} else {
|
|
259
|
-
if (lang === 'en') {
|
|
260
|
-
friendlyName = `${room}${adapter.config.concatWord ? ` ${adapter.config.concatWord}` : ''} ${func}`;
|
|
261
|
-
} else {
|
|
262
|
-
friendlyName = `${room}${adapter.config.concatWord ? ` ${adapter.config.concatWord}` : ''} ${func}`;
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
} else {
|
|
266
|
-
friendlyName = states[id].common.name;
|
|
267
|
-
if (adapter.config.replaces) {
|
|
268
|
-
for (let r = 0; r < adapter.config.replaces.length; r++) {
|
|
269
|
-
friendlyName = friendlyName.replace(adapter.config.replaces[r], '');
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
friendlyNames[0] = friendlyName;
|
|
274
|
-
nameModified = false;
|
|
275
|
-
} else if (translate) {
|
|
276
|
-
translateDevices = translateDevices || require('./devices.js');
|
|
277
|
-
friendlyName = translateDevices(lang, friendlyName);
|
|
278
|
-
nameModified = true;
|
|
279
|
-
friendlyNames = friendlyName.split(',');
|
|
280
|
-
} else {
|
|
281
|
-
friendlyNames = friendlyName.split(',');
|
|
282
|
-
nameModified = true;
|
|
283
|
-
}
|
|
284
|
-
for (let i = friendlyNames.length - 1; i >= 0; i--) {
|
|
285
|
-
friendlyNames[i] = (friendlyNames[i] || '').trim();
|
|
286
|
-
if (!friendlyNames[i]) {
|
|
287
|
-
friendlyNames.splice(i, 1);
|
|
288
|
-
} else {
|
|
289
|
-
// friendlyName may not be longer than 128
|
|
290
|
-
friendlyNames[i] = friendlyNames[i]
|
|
291
|
-
.substring(0, 128)
|
|
292
|
-
.replace(/[^.a-zA-Z0-9äÄüÜöÖßÉéÈèÀàÂâÊêÙùÛûÇçÎîËëÏïŸÿÔôŒœãõìòáíóú]+/g, ' ');
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
if (!friendlyNames[0]) {
|
|
297
|
-
adapter.log.warn(`[ALEXA] State ${id} is invalid.`);
|
|
298
|
-
return `State "${id}" has invalid friendly name`;
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
let friendlyDescription = states[id].common.name || id;
|
|
302
|
-
if (typeof friendlyDescription === 'object') {
|
|
303
|
-
friendlyDescription = friendlyDescription[lang] || friendlyDescription.en;
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
let res = Actions.getActions(states[id]);
|
|
307
|
-
if (!res) {
|
|
308
|
-
adapter.log.debug(`[ALEXA] Name "${friendlyDescription}" cannot be written and will be ignored`);
|
|
309
|
-
return `State "${id}" cannot be written and will be ignored`;
|
|
310
|
-
}
|
|
311
|
-
let type = res.type;
|
|
312
|
-
let actions = res.actions;
|
|
313
|
-
|
|
314
|
-
friendlyDescription = friendlyDescription
|
|
315
|
-
.substring(0, 128)
|
|
316
|
-
.replace(/[^.a-zA-Z0-9äÄüÜöÖßÉéÈèÀàÂâÊêÙùÛûÇçÎîËëÏïŸÿÔôŒœãõìòáíóú]+/g, ' ');
|
|
317
|
-
// any letter or number and _ - = # ; : ? @ &
|
|
318
|
-
// let applianceId = id.substring(0, 256).replace(/[^a-zA-Z0-9_=#;:?@&-]+/g, '_');
|
|
319
|
-
|
|
320
|
-
let pos;
|
|
321
|
-
if (alexaIds && (pos = alexaIds.indexOf(id)) !== -1) {
|
|
322
|
-
alexaIds.splice(pos, 1);
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
type = type ? byON || '100' : false;
|
|
326
|
-
let name = states[id].common.name || '';
|
|
327
|
-
if (typeof name === 'object') {
|
|
328
|
-
name = name[lang] || name.en;
|
|
329
|
-
}
|
|
330
|
-
name = name.substring(0, 128);
|
|
331
|
-
|
|
332
|
-
for (let n = 0; n < friendlyNames.length; n++) {
|
|
333
|
-
if (names[friendlyNames[n]]) {
|
|
334
|
-
// Ignore it, because yet in the list
|
|
335
|
-
if (names[friendlyNames[n]].additionalApplianceDetails.id === id) {
|
|
336
|
-
return;
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
// create the virtual group
|
|
340
|
-
if (groups[friendlyNames[n]]) {
|
|
341
|
-
/* // let ids = JSON.parse(groups[friendlyNames[n]].additionalApplianceDetails.ids);
|
|
342
|
-
let _names = JSON.parse(groups[friendlyNames[n]].additionalApplianceDetails.names || '[]');
|
|
343
|
-
let types = JSON.parse(groups[friendlyNames[n]].additionalApplianceDetails.byONs || '[]');
|
|
344
|
-
// ids.push(id);
|
|
345
|
-
_names.push(name);
|
|
346
|
-
types.push(type);
|
|
347
|
-
|
|
348
|
-
// merge actions
|
|
349
|
-
for (let a = 0; a < actions.length; a++) {
|
|
350
|
-
if (!groups[friendlyNames[n]].actions.includes(actions[a])) {
|
|
351
|
-
groups[friendlyNames[n]].actions.push(actions[a]);
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
// groups[friendlyNames[n]].additionalApplianceDetails.ids = JSON.stringify(ids);
|
|
356
|
-
groups[friendlyNames[n]].additionalApplianceDetails.names = JSON.stringify(_names);
|
|
357
|
-
groups[friendlyNames[n]].additionalApplianceDetails.byONs = JSON.stringify(types);*/
|
|
358
|
-
|
|
359
|
-
actions.forEach(
|
|
360
|
-
action =>
|
|
361
|
-
!groups[friendlyNames[n]].actions.includes(action) &&
|
|
362
|
-
groups[friendlyNames[n]].actions.push(action),
|
|
363
|
-
);
|
|
364
|
-
|
|
365
|
-
if (smartType && !groups[friendlyNames[n]].applianceTypes.includes(smartType)) {
|
|
366
|
-
groups[friendlyNames[n]].applianceTypes.push(smartType);
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
groups[friendlyNames[n]].additionalApplianceDetails.byONs[id] = type;
|
|
370
|
-
groups[friendlyNames[n]].additionalApplianceDetails.names[id] = name;
|
|
371
|
-
groups[friendlyNames[n]].additionalApplianceDetails.smartTypes[id] = smartType;
|
|
372
|
-
let addids = [id];
|
|
373
|
-
for (let i = 0; i < addids.length; i++) {
|
|
374
|
-
let newid = addids[i];
|
|
375
|
-
let _parts = newid.split('.');
|
|
376
|
-
_parts.pop();
|
|
377
|
-
let channel = _parts.join('.');
|
|
378
|
-
if (groups[friendlyNames[n]].additionalApplianceDetails.channels.hasOwnProperty(channel)) {
|
|
379
|
-
groups[friendlyNames[n]].additionalApplianceDetails.channels[channel].push({
|
|
380
|
-
id: newid,
|
|
381
|
-
role: states[addids[i]].common.role,
|
|
382
|
-
actions,
|
|
383
|
-
});
|
|
384
|
-
} else {
|
|
385
|
-
groups[friendlyNames[n]].additionalApplianceDetails.channels[channel] = [
|
|
386
|
-
{ id: newid, role: states[addids[i]].common.role, actions },
|
|
387
|
-
];
|
|
388
|
-
}
|
|
389
|
-
}
|
|
390
|
-
} else {
|
|
391
|
-
// convert a single device into a group, because at least two devices found with the same name
|
|
392
|
-
groups[friendlyNames[n]] = {
|
|
393
|
-
applianceId: friendlyNames[n].replace(/[^a-zA-Zа-яА-Я0-9_=#;:?@&-]+/g, '_').substring(0, 128),
|
|
394
|
-
applianceTypes: JSON.parse(JSON.stringify(names[friendlyNames[n]].applianceTypes)),
|
|
395
|
-
manufacturerName: 'ioBroker group',
|
|
396
|
-
modelName: (name || words['No name'][lang]).substring(0, 128),
|
|
397
|
-
version: '1',
|
|
398
|
-
friendlyName: friendlyNames[n].substring(0, 128),
|
|
399
|
-
friendlyDescription: `${words['Group'][lang]} ${friendlyNames[n]}`.substring(0, 128),
|
|
400
|
-
isReachable: true,
|
|
401
|
-
actions: JSON.parse(JSON.stringify(actions)),
|
|
402
|
-
additionalApplianceDetails: {
|
|
403
|
-
group: true,
|
|
404
|
-
channels: {},
|
|
405
|
-
smartTypes: {},
|
|
406
|
-
names: {},
|
|
407
|
-
byONs: {},
|
|
408
|
-
room,
|
|
409
|
-
func,
|
|
410
|
-
},
|
|
411
|
-
};
|
|
412
|
-
|
|
413
|
-
const oldActions = names[friendlyNames[n]].actions;
|
|
414
|
-
// merge actions for the first group, too!
|
|
415
|
-
for (let a = 0; a < oldActions.length; a++) {
|
|
416
|
-
if (!groups[friendlyNames[n]].actions.includes(oldActions[a])) {
|
|
417
|
-
groups[friendlyNames[n]].actions.push(oldActions[a]);
|
|
418
|
-
}
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
let oldObjDetails = names[friendlyNames[n]].additionalApplianceDetails;
|
|
422
|
-
|
|
423
|
-
if (smartType && !groups[friendlyNames[n]].applianceTypes.includes(smartType)) {
|
|
424
|
-
groups[friendlyNames[n]].applianceTypes.push(smartType);
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
groups[friendlyNames[n]].additionalApplianceDetails.byONs[oldObjDetails.id] =
|
|
428
|
-
oldObjDetails.byON;
|
|
429
|
-
groups[friendlyNames[n]].additionalApplianceDetails.names[oldObjDetails.id] =
|
|
430
|
-
oldObjDetails.name;
|
|
431
|
-
groups[friendlyNames[n]].additionalApplianceDetails.smartTypes[oldObjDetails.id] =
|
|
432
|
-
oldObjDetails.smartType;
|
|
433
|
-
groups[friendlyNames[n]].additionalApplianceDetails.byONs[id] = type;
|
|
434
|
-
groups[friendlyNames[n]].additionalApplianceDetails.names[id] = name;
|
|
435
|
-
groups[friendlyNames[n]].additionalApplianceDetails.smartTypes[id] = smartType;
|
|
436
|
-
|
|
437
|
-
let addIDs = [names[friendlyNames[n]].additionalApplianceDetails.id, id];
|
|
438
|
-
for (let i = 0; i < addIDs.length; i++) {
|
|
439
|
-
let newID = addIDs[i];
|
|
440
|
-
let _parts = newID.split('.');
|
|
441
|
-
_parts.pop();
|
|
442
|
-
let channel = _parts.join('.');
|
|
443
|
-
if (groups[friendlyNames[n]].additionalApplianceDetails.channels.hasOwnProperty(channel)) {
|
|
444
|
-
groups[friendlyNames[n]].additionalApplianceDetails.channels[channel].push({
|
|
445
|
-
id: newID,
|
|
446
|
-
role: states[addIDs[i]].common.role,
|
|
447
|
-
actions: newID === id ? actions : oldActions,
|
|
448
|
-
});
|
|
449
|
-
} else {
|
|
450
|
-
groups[friendlyNames[n]].additionalApplianceDetails.channels[channel] = [
|
|
451
|
-
{
|
|
452
|
-
id: newID,
|
|
453
|
-
role: states[addIDs[i]].common.role,
|
|
454
|
-
actions: newID === id ? actions : oldActions,
|
|
455
|
-
},
|
|
456
|
-
];
|
|
457
|
-
}
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
if (JSON.stringify(groups[friendlyNames[n]].additionalApplianceDetails).length > 5000) {
|
|
461
|
-
adapter.log.warn(`[ALEXA] "${groups[friendlyNames[n]].friendlyName}" has too long additionalApplianceDetails`);
|
|
462
|
-
return;
|
|
463
|
-
}
|
|
464
|
-
|
|
465
|
-
result.push(groups[friendlyNames[n]]);
|
|
466
|
-
names[friendlyNames[n]].disabled = true;
|
|
467
|
-
}
|
|
468
|
-
} else {
|
|
469
|
-
const obj = {
|
|
470
|
-
applianceId: friendlyNames[n].replace(/[^a-zA-Zа-яА-Я0-9_=#;:?@&-]+/g, '_').substring(0, 128),
|
|
471
|
-
applianceTypes: [],
|
|
472
|
-
manufacturerName: 'ioBroker',
|
|
473
|
-
modelName: (name || words['No name'][lang]).substring(0, 128),
|
|
474
|
-
version: '1',
|
|
475
|
-
friendlyName: friendlyNames[n].substring(0, 128),
|
|
476
|
-
friendlyDescription: friendlyDescription.substring(0, 128),
|
|
477
|
-
isReachable: true,
|
|
478
|
-
actions: JSON.parse(JSON.stringify(actions)),
|
|
479
|
-
additionalApplianceDetails: {
|
|
480
|
-
id: id.substring(0, 1024),
|
|
481
|
-
role: states[id].common.role,
|
|
482
|
-
name,
|
|
483
|
-
friendlyNames: friendlyNames.join(', '),
|
|
484
|
-
smartType,
|
|
485
|
-
byON: type,
|
|
486
|
-
nameModified,
|
|
487
|
-
room,
|
|
488
|
-
func,
|
|
489
|
-
},
|
|
490
|
-
};
|
|
491
|
-
if (JSON.stringify(obj.additionalApplianceDetails).length > 5000) {
|
|
492
|
-
adapter.log.warn(`[ALEXA] "${id}" has too long additionalApplianceDetails`);
|
|
493
|
-
return;
|
|
494
|
-
}
|
|
495
|
-
|
|
496
|
-
names[friendlyNames[n]] = obj;
|
|
497
|
-
smartType && names[friendlyNames[n]].applianceTypes.push(smartType);
|
|
498
|
-
result.push(obj);
|
|
499
|
-
}
|
|
500
|
-
}
|
|
501
|
-
} catch (e) {
|
|
502
|
-
adapter.log.error(`[ALEXA] Cannot process "${id}": ${e}`);
|
|
503
|
-
return `Error by processing of "${id}": ${e}`;
|
|
504
|
-
}
|
|
505
|
-
}
|
|
506
|
-
|
|
507
|
-
async function controlOnOff(toggle, id, value, writeStates) {
|
|
508
|
-
let obj;
|
|
509
|
-
try {
|
|
510
|
-
obj = await adapter.getForeignObjectAsync(id);
|
|
511
|
-
} catch (err) {
|
|
512
|
-
adapter.log.error(`[ALEXA] Cannot control non state "${id}": ${err}`);
|
|
513
|
-
}
|
|
514
|
-
|
|
515
|
-
if (!obj || obj.type !== 'state') {
|
|
516
|
-
throw new Error(`Cannot control non state: ${id} ${obj ? obj.type : 'no object'}`);
|
|
517
|
-
}
|
|
518
|
-
|
|
519
|
-
let smartName = getSmartName(obj);
|
|
520
|
-
let byON = smartName && typeof smartName === 'object' ? smartName.byON : null;
|
|
521
|
-
|
|
522
|
-
if (toggle && toggle !== id) {
|
|
523
|
-
adapter.log.debug(`Won't control ${id} as we have a switch available`);
|
|
524
|
-
return;
|
|
525
|
-
}
|
|
526
|
-
|
|
527
|
-
if (byON === 'omit') {
|
|
528
|
-
adapter.log.debug(`Won't control value for "${id}", because "omit" configured`);
|
|
529
|
-
return;
|
|
530
|
-
}
|
|
531
|
-
|
|
532
|
-
if (obj.common.type === 'number') {
|
|
533
|
-
// if ON
|
|
534
|
-
if (value) {
|
|
535
|
-
if (byON === 'stored' && valuesON[id]) {
|
|
536
|
-
adapter.log.debug(`[ALEXA] Use stored ON value for "${id}": ${valuesON[id]}`);
|
|
537
|
-
value = valuesON[id];
|
|
538
|
-
} else {
|
|
539
|
-
const min = typeof obj.common.min !== 'undefined' ? obj.common.min : 0;
|
|
540
|
-
const max = typeof obj.common.max !== 'undefined' ? obj.common.max : 100;
|
|
541
|
-
|
|
542
|
-
value = byON ? Math.round(((parseFloat(byON) || 100) * (max - min)) / 100 + min) : max;
|
|
543
|
-
}
|
|
544
|
-
} else {
|
|
545
|
-
// if OFF
|
|
546
|
-
if (byON === 'stored') {
|
|
547
|
-
// remember the last state
|
|
548
|
-
let state;
|
|
549
|
-
try {
|
|
550
|
-
state = await adapter.getForeignStateAsync(id);
|
|
551
|
-
} catch (err) {
|
|
552
|
-
adapter.log.error(`[ALEXA] Cannot get state: ${err}`);
|
|
553
|
-
}
|
|
554
|
-
|
|
555
|
-
if (typeof obj.common.min !== 'undefined') {
|
|
556
|
-
value = obj.common.min;
|
|
557
|
-
} else {
|
|
558
|
-
value = 0;
|
|
559
|
-
}
|
|
560
|
-
// do not remember the minimum/off value
|
|
561
|
-
if (state && state.val !== value) {
|
|
562
|
-
valuesON[id] = state.val;
|
|
563
|
-
}
|
|
564
|
-
|
|
565
|
-
adapter.log.debug(`[ALEXA] OFF Stored: Set "${id}" to ${value} and store old value "${id}"`);
|
|
566
|
-
} else {
|
|
567
|
-
// blinds
|
|
568
|
-
if (typeof obj.common.min !== 'undefined') {
|
|
569
|
-
value = obj.common.min;
|
|
570
|
-
} else {
|
|
571
|
-
value = 0;
|
|
572
|
-
}
|
|
573
|
-
}
|
|
574
|
-
}
|
|
575
|
-
} else if (obj.common.role === 'level.color.rgb') {
|
|
576
|
-
if (value) {
|
|
577
|
-
if (byON === 'stored' && valuesON[id]) {
|
|
578
|
-
adapter.log.debug(`[ALEXA] Use stored ON value for "${id}": ${valuesON[id]}`);
|
|
579
|
-
value = valuesON[id];
|
|
580
|
-
} else {
|
|
581
|
-
value = '#FFFFFF';
|
|
582
|
-
}
|
|
583
|
-
} else {
|
|
584
|
-
if (byON === 'stored') {
|
|
585
|
-
// remember last state
|
|
586
|
-
let state;
|
|
587
|
-
try {
|
|
588
|
-
state = await adapter.getForeignStateAsync(id);
|
|
589
|
-
} catch (err) {
|
|
590
|
-
adapter.log.error(`[ALEXA] Cannot get state: ${err}`);
|
|
591
|
-
}
|
|
592
|
-
|
|
593
|
-
value = '#000000';
|
|
594
|
-
// do not remember the minimum/off value
|
|
595
|
-
if (state && state.val !== value) {
|
|
596
|
-
valuesON[id] = state.val;
|
|
597
|
-
}
|
|
598
|
-
|
|
599
|
-
adapter.log.debug(`[ALEXA] OFF Stored: Set "${id}" to ${value} and store old value "${id}"`);
|
|
600
|
-
} else {
|
|
601
|
-
value = '#000000';
|
|
602
|
-
}
|
|
603
|
-
}
|
|
604
|
-
}
|
|
605
|
-
|
|
606
|
-
adapter.log.debug(`[ALEXA] Set "${id}" to ${value}`);
|
|
607
|
-
|
|
608
|
-
if (writeStates) {
|
|
609
|
-
await adapter.setStateAsync('smart.lastObjectID', id, true);
|
|
610
|
-
}
|
|
611
|
-
|
|
612
|
-
try {
|
|
613
|
-
await adapter.setForeignState(id, value);
|
|
614
|
-
} catch (err) {
|
|
615
|
-
throw new Error(`Cannot switch device: ${err}`);
|
|
616
|
-
}
|
|
617
|
-
}
|
|
618
|
-
|
|
619
|
-
async function controlPercent(dimmer, id, value, writeStates) {
|
|
620
|
-
let obj;
|
|
621
|
-
try {
|
|
622
|
-
obj = await adapter.getForeignObjectAsync(id);
|
|
623
|
-
} catch (err) {
|
|
624
|
-
adapter.log.error(`[ALEXA] Cannot control non state "${id}": ${err}`);
|
|
625
|
-
}
|
|
626
|
-
|
|
627
|
-
if (!obj || obj.type !== 'state') {
|
|
628
|
-
throw new Error(`Cannot control non state: ${id} ${obj ? obj.type : 'no object'}`);
|
|
629
|
-
}
|
|
630
|
-
|
|
631
|
-
if (dimmer && dimmer !== id) {
|
|
632
|
-
adapter.log.debug(`[ALEXA] Won't control ${id} as we have a dimmer available for this channel.`);
|
|
633
|
-
return;
|
|
634
|
-
}
|
|
635
|
-
|
|
636
|
-
let res = Actions.getActions(obj);
|
|
637
|
-
if (!res || !res.actions.includes('setPercentage')) {
|
|
638
|
-
adapter.log.debug(`[ALEXA] ${id} is no valid datapoint to set the percentage.`);
|
|
639
|
-
return;
|
|
640
|
-
}
|
|
641
|
-
|
|
642
|
-
adapter.log.debug(`[ALEXA] Controlling ${id} .`);
|
|
643
|
-
|
|
644
|
-
let max = 100;
|
|
645
|
-
let min = 0;
|
|
646
|
-
|
|
647
|
-
if (typeof obj.common.max !== 'undefined') {
|
|
648
|
-
max = parseFloat(obj.common.max);
|
|
649
|
-
}
|
|
650
|
-
if (typeof obj.common.min !== 'undefined') {
|
|
651
|
-
min = parseFloat(obj.common.min);
|
|
652
|
-
}
|
|
653
|
-
|
|
654
|
-
value = (value / 100) * (max - min) + min;
|
|
655
|
-
|
|
656
|
-
if (obj.common.type === 'boolean') {
|
|
657
|
-
value = value >= adapter.config.deviceOffLevel;
|
|
658
|
-
} else if (
|
|
659
|
-
adapter.config.deviceOffLevel &&
|
|
660
|
-
value >= adapter.config.deviceOffLevel &&
|
|
661
|
-
(!obj.common.role || !obj.common.role.includes('blind'))
|
|
662
|
-
) {
|
|
663
|
-
valuesON[id] = value;
|
|
664
|
-
adapter.log.debug(`[ALEXA] Remember ON value for "${id}": ${value}`);
|
|
665
|
-
}
|
|
666
|
-
|
|
667
|
-
if (writeStates) {
|
|
668
|
-
await adapter.setStateAsync('smart.lastObjectID', id, true);
|
|
669
|
-
}
|
|
670
|
-
|
|
671
|
-
try {
|
|
672
|
-
await adapter.setForeignState(id, value);
|
|
673
|
-
} catch (err) {
|
|
674
|
-
throw new Error(`Cannot switch device: ${err}`);
|
|
675
|
-
}
|
|
676
|
-
}
|
|
677
|
-
|
|
678
|
-
async function controlDelta(dimmer, id, delta, writeStates) {
|
|
679
|
-
let obj;
|
|
680
|
-
try {
|
|
681
|
-
obj = await adapter.getForeignObjectAsync(id);
|
|
682
|
-
} catch (err) {
|
|
683
|
-
adapter.log.error(`[ALEXA] Cannot control non state "${id}": ${err}`);
|
|
684
|
-
}
|
|
685
|
-
|
|
686
|
-
if (!obj || obj.type !== 'state') {
|
|
687
|
-
throw new Error(`[ALEXA] Cannot control non state: ${id} ${obj ? obj.type : 'no object'}`);
|
|
688
|
-
}
|
|
689
|
-
|
|
690
|
-
if (obj.common.type === 'boolean') {
|
|
691
|
-
adapter.log.debug(`[ALEXA] Object ${id} does not support Deltas.`);
|
|
692
|
-
return;
|
|
693
|
-
}
|
|
694
|
-
|
|
695
|
-
if (dimmer && dimmer !== id) {
|
|
696
|
-
adapter.log.debug(`[ALEXA] Won't control ${id} as we have a dimmer available for this channel.`);
|
|
697
|
-
return;
|
|
698
|
-
}
|
|
699
|
-
|
|
700
|
-
let res = Actions.getActions(obj);
|
|
701
|
-
if (!res || (!res.actions.includes('incrementPercentage') && !res.actions.includes('decrementPercentage'))) {
|
|
702
|
-
adapter.log.debug(`[ALEXA] ${id} is no valid datapoint to increment or decrement the percentage.`);
|
|
703
|
-
return;
|
|
704
|
-
}
|
|
705
|
-
|
|
706
|
-
adapter.log.debug(`[ALEXA] Controlling delta ${id}`);
|
|
707
|
-
|
|
708
|
-
let state;
|
|
709
|
-
try {
|
|
710
|
-
state = await adapter.getForeignStateAsync(id);
|
|
711
|
-
} catch (err) {
|
|
712
|
-
adapter.log.error(`[ALEXA] Cannot get state "${id}": ${err}`);
|
|
713
|
-
}
|
|
714
|
-
|
|
715
|
-
let value = state ? state.val || 0 : 0;
|
|
716
|
-
let max = 100;
|
|
717
|
-
let min = 0;
|
|
718
|
-
if (typeof obj.common.max !== 'undefined') {
|
|
719
|
-
max = parseFloat(obj.common.max);
|
|
720
|
-
}
|
|
721
|
-
if (typeof obj.common.min !== 'undefined') {
|
|
722
|
-
min = parseFloat(obj.common.min);
|
|
723
|
-
}
|
|
724
|
-
|
|
725
|
-
// Absolute value => percent => add delta
|
|
726
|
-
value = ((value - min) / (max - min)) * 100 + delta;
|
|
727
|
-
if (value > 100) {
|
|
728
|
-
value = 100;
|
|
729
|
-
}
|
|
730
|
-
if (value < 0) {
|
|
731
|
-
value = 0;
|
|
732
|
-
}
|
|
733
|
-
// percent => absolute value
|
|
734
|
-
value = (value / 100) * (max - min) + min;
|
|
735
|
-
|
|
736
|
-
if (adapter.config.deviceOffLevel && value >= adapter.config.deviceOffLevel) {
|
|
737
|
-
adapter.log.debug(`[ALEXA] Remember ON value for "${id}": ${value}`);
|
|
738
|
-
valuesON[id] = value;
|
|
739
|
-
}
|
|
740
|
-
|
|
741
|
-
if (writeStates) {
|
|
742
|
-
await adapter.setStateAsync('smart.lastObjectID', id, true);
|
|
743
|
-
}
|
|
744
|
-
|
|
745
|
-
try {
|
|
746
|
-
await adapter.setForeignStateAsync(id, value);
|
|
747
|
-
} catch (err) {
|
|
748
|
-
throw new Error(`Cannot set device: ${err}`);
|
|
749
|
-
}
|
|
750
|
-
}
|
|
751
|
-
|
|
752
|
-
async function controlTemperature(id, value, writeStates) {
|
|
753
|
-
//{
|
|
754
|
-
// "header" : {
|
|
755
|
-
// "namespace" : "Alexa.ConnectedHome.Control",
|
|
756
|
-
// "name" : "SetTargetTemperatureConfirmation",
|
|
757
|
-
// "payloadVersion" : "2",
|
|
758
|
-
// "messageId" : "cc36e80c-6357-41e0-9dd4-b76cb3a394e3"
|
|
759
|
-
// },
|
|
760
|
-
// "payload" : {
|
|
761
|
-
// "targetTemperature" : {
|
|
762
|
-
// "value" : 25.0
|
|
763
|
-
// },
|
|
764
|
-
// "temperatureMode" : {
|
|
765
|
-
// "value" : "AUTO"
|
|
766
|
-
// },
|
|
767
|
-
// "previousState" : {
|
|
768
|
-
// "targetTemperature" : {
|
|
769
|
-
// "value" : 21.0
|
|
770
|
-
// },
|
|
771
|
-
// "mode" : {
|
|
772
|
-
// "value" : "AUTO"
|
|
773
|
-
// }
|
|
774
|
-
// }
|
|
775
|
-
// }
|
|
776
|
-
//}
|
|
777
|
-
let obj;
|
|
778
|
-
try {
|
|
779
|
-
obj = await adapter.getForeignObjectAsync(id);
|
|
780
|
-
} catch (err) {
|
|
781
|
-
adapter.log.error(`[ALEXA] Cannot control non state "${id}": ${err}`);
|
|
782
|
-
}
|
|
783
|
-
|
|
784
|
-
if (!obj || obj.type !== 'state') {
|
|
785
|
-
throw new Error(`Cannot control non state: ${id} ${obj ? obj.type : 'no object'}`);
|
|
786
|
-
}
|
|
787
|
-
|
|
788
|
-
let res = Actions.getActions(obj);
|
|
789
|
-
if (!res || !res.actions.includes('setTargetTemperature')) {
|
|
790
|
-
adapter.log.debug(`[ALEXA] ${id} is no valid datapoint to set the target temperature.`);
|
|
791
|
-
return;
|
|
792
|
-
}
|
|
793
|
-
|
|
794
|
-
value = parseFloat(value);
|
|
795
|
-
let max;
|
|
796
|
-
let min;
|
|
797
|
-
if (typeof obj.common.max !== 'undefined') {
|
|
798
|
-
max = parseFloat(obj.common.max);
|
|
799
|
-
}
|
|
800
|
-
if (typeof obj.common.min !== 'undefined') {
|
|
801
|
-
min = parseFloat(obj.common.min);
|
|
802
|
-
}
|
|
803
|
-
|
|
804
|
-
if (min !== undefined && value < min) {
|
|
805
|
-
value = min;
|
|
806
|
-
}
|
|
807
|
-
if (max !== undefined && value > max) {
|
|
808
|
-
value = max;
|
|
809
|
-
}
|
|
810
|
-
|
|
811
|
-
if (obj.common.type === 'boolean') {
|
|
812
|
-
value = !!value;
|
|
813
|
-
}
|
|
814
|
-
|
|
815
|
-
let state;
|
|
816
|
-
try {
|
|
817
|
-
state = await adapter.getForeignStateAsync(id);
|
|
818
|
-
} catch (err) {
|
|
819
|
-
adapter.log.error(`[ALEXA] Cannot get state "${id}": ${err}`);
|
|
820
|
-
}
|
|
821
|
-
|
|
822
|
-
if (writeStates) {
|
|
823
|
-
await adapter.setStateAsync('smart.lastObjectID', id, true);
|
|
824
|
-
}
|
|
825
|
-
|
|
826
|
-
try {
|
|
827
|
-
await adapter.setForeignStateAsync(id, value);
|
|
828
|
-
} catch (err) {
|
|
829
|
-
adapter.log.error(`[ALEXA] Cannot set device: ${err}`);
|
|
830
|
-
}
|
|
831
|
-
|
|
832
|
-
return {
|
|
833
|
-
payload: {
|
|
834
|
-
targetTemperature: {
|
|
835
|
-
value,
|
|
836
|
-
},
|
|
837
|
-
temperatureMode: {
|
|
838
|
-
value: 'AUTO',
|
|
839
|
-
},
|
|
840
|
-
previousState: {
|
|
841
|
-
targetTemperature: {
|
|
842
|
-
value: state ? parseFloat(state.val) || 0 : 0,
|
|
843
|
-
},
|
|
844
|
-
mode: {
|
|
845
|
-
value: 'AUTO',
|
|
846
|
-
},
|
|
847
|
-
},
|
|
848
|
-
},
|
|
849
|
-
};
|
|
850
|
-
}
|
|
851
|
-
|
|
852
|
-
async function getTemperature(id, writeStates) {
|
|
853
|
-
let obj;
|
|
854
|
-
try {
|
|
855
|
-
obj = await adapter.getForeignObjectAsync(id);
|
|
856
|
-
} catch (err) {
|
|
857
|
-
adapter.log.error(`[ALEXA] Cannot ask non state "${id}": ${err}`);
|
|
858
|
-
}
|
|
859
|
-
|
|
860
|
-
if (!obj || obj.type !== 'state') {
|
|
861
|
-
adapter.log.error(`[ALEXA] Cannot ask non state: ${id} ${obj ? obj.type : 'no object'}`);
|
|
862
|
-
return;
|
|
863
|
-
}
|
|
864
|
-
|
|
865
|
-
let res = Actions.getActions(obj);
|
|
866
|
-
if (!res || !res.actions.includes('getTemperatureReading')) {
|
|
867
|
-
adapter.log.debug(`[ALEXA] ${id} is no valid datapoint to get the actual temperature.`);
|
|
868
|
-
return;
|
|
869
|
-
}
|
|
870
|
-
|
|
871
|
-
let state;
|
|
872
|
-
try {
|
|
873
|
-
state = await adapter.getForeignStateAsync(id);
|
|
874
|
-
} catch (err) {
|
|
875
|
-
adapter.log.error(`[ALEXA] Cannot get state "${id}": ${err}`);
|
|
876
|
-
}
|
|
877
|
-
|
|
878
|
-
if (writeStates) {
|
|
879
|
-
await adapter.setStateAsync('smart.lastObjectID', id, true);
|
|
880
|
-
}
|
|
881
|
-
|
|
882
|
-
return { value: state ? state.val : null, ts: state ? state.ts : null };
|
|
883
|
-
}
|
|
884
|
-
|
|
885
|
-
async function getTargetTemperature(id, writeStates) {
|
|
886
|
-
let obj;
|
|
887
|
-
try {
|
|
888
|
-
obj = await adapter.getForeignObjectAsync(id);
|
|
889
|
-
} catch (err) {
|
|
890
|
-
adapter.log.error(`[ALEXA] Cannot ask non state "${id}": ${err}`);
|
|
891
|
-
}
|
|
892
|
-
|
|
893
|
-
if (!obj || obj.type !== 'state') {
|
|
894
|
-
adapter.log.error(`[ALEXA] Cannot ask non state: ${id} ${obj ? obj.type : 'no object'}`);
|
|
895
|
-
return;
|
|
896
|
-
}
|
|
897
|
-
|
|
898
|
-
let res = Actions.getActions(obj);
|
|
899
|
-
if (!res || !res.actions.includes('getTargetTemperature')) {
|
|
900
|
-
adapter.log.debug(`[ALEXA] ${id} is no valid datapoint to get the target temperature.`);
|
|
901
|
-
return;
|
|
902
|
-
}
|
|
903
|
-
|
|
904
|
-
let state;
|
|
905
|
-
try {
|
|
906
|
-
state = await adapter.getForeignStateAsync(id);
|
|
907
|
-
} catch (err) {
|
|
908
|
-
adapter.log.error(`[ALEXA] Cannot get state "${id}": ${err}`);
|
|
909
|
-
}
|
|
910
|
-
|
|
911
|
-
if (writeStates) {
|
|
912
|
-
await adapter.setStateAsync('smart.lastObjectID', id, true);
|
|
913
|
-
}
|
|
914
|
-
|
|
915
|
-
return { value: state ? state.val : null, ts: state ? state.ts : null };
|
|
916
|
-
}
|
|
917
|
-
|
|
918
|
-
async function controlTemperatureDelta(id, delta, writeStates) {
|
|
919
|
-
let obj;
|
|
920
|
-
try {
|
|
921
|
-
obj = await adapter.getForeignObjectAsync(id);
|
|
922
|
-
} catch (err) {
|
|
923
|
-
throw new Error(`Cannot control non state "${id}": ${err}`);
|
|
924
|
-
}
|
|
925
|
-
|
|
926
|
-
if (!obj || obj.type !== 'state') {
|
|
927
|
-
adapter.log.error(`[ALEXA] Cannot control non state: ${id} ${obj ? obj.type : 'no object'}`);
|
|
928
|
-
throw new Error(`Invalid object: ${id}`);
|
|
929
|
-
}
|
|
930
|
-
|
|
931
|
-
let res = Actions.getActions(obj);
|
|
932
|
-
if (!res || !res.actions.includes('setTargetTemperature')) {
|
|
933
|
-
adapter.log.debug(`[ALEXA] ${id} is no valid datapoint to set the target temperature.`);
|
|
934
|
-
return;
|
|
935
|
-
}
|
|
936
|
-
|
|
937
|
-
let state;
|
|
938
|
-
try {
|
|
939
|
-
state = await adapter.getForeignStateAsync(id);
|
|
940
|
-
} catch (err) {
|
|
941
|
-
adapter.log.error(`[ALEXA] Cannot get state "${id}": ${err}`);
|
|
942
|
-
}
|
|
943
|
-
|
|
944
|
-
let value = state ? state.val || 0 : 0;
|
|
945
|
-
let max;
|
|
946
|
-
let min;
|
|
947
|
-
if (typeof obj.common.max !== 'undefined') {
|
|
948
|
-
max = parseFloat(obj.common.max);
|
|
949
|
-
}
|
|
950
|
-
if (typeof obj.common.min !== 'undefined') {
|
|
951
|
-
min = parseFloat(obj.common.min);
|
|
952
|
-
}
|
|
953
|
-
|
|
954
|
-
// Absolute value => percent => add delta
|
|
955
|
-
value = value + delta;
|
|
956
|
-
if (max !== undefined && value > max) {
|
|
957
|
-
value = max;
|
|
958
|
-
}
|
|
959
|
-
if (min !== undefined && value < min) {
|
|
960
|
-
value = min;
|
|
961
|
-
}
|
|
962
|
-
|
|
963
|
-
if (obj.common.type === 'boolean') {
|
|
964
|
-
value = !!value;
|
|
965
|
-
}
|
|
966
|
-
|
|
967
|
-
if (writeStates) {
|
|
968
|
-
await adapter.setStateAsync('smart.lastObjectID', id, true);
|
|
969
|
-
}
|
|
970
|
-
|
|
971
|
-
try {
|
|
972
|
-
await adapter.setForeignStateAsync(id, value);
|
|
973
|
-
} catch (err) {
|
|
974
|
-
throw new Error(`[ALEXA] Cannot switch device: ${err}`);
|
|
975
|
-
}
|
|
976
|
-
|
|
977
|
-
return {
|
|
978
|
-
payload: {
|
|
979
|
-
targetTemperature: {
|
|
980
|
-
value,
|
|
981
|
-
},
|
|
982
|
-
temperatureMode: {
|
|
983
|
-
value: 'AUTO',
|
|
984
|
-
},
|
|
985
|
-
previousState: {
|
|
986
|
-
targetTemperature: {
|
|
987
|
-
value: state ? parseFloat(state.val) || 0 : 0,
|
|
988
|
-
},
|
|
989
|
-
mode: {
|
|
990
|
-
value: 'AUTO',
|
|
991
|
-
},
|
|
992
|
-
},
|
|
993
|
-
},
|
|
994
|
-
};
|
|
995
|
-
}
|
|
996
|
-
|
|
997
|
-
async function controlLock(id, writeStates) {
|
|
998
|
-
let obj;
|
|
999
|
-
try {
|
|
1000
|
-
obj = await adapter.getForeignObjectAsync(id);
|
|
1001
|
-
} catch (err) {
|
|
1002
|
-
adapter.log.error(`[ALEXA] Cannot control non state "${id}": ${err}`);
|
|
1003
|
-
}
|
|
1004
|
-
|
|
1005
|
-
if (!obj || obj.type !== 'state') {
|
|
1006
|
-
throw new Error(`Cannot control non state: ${id} ${obj ? obj.type : 'no object'}`);
|
|
1007
|
-
}
|
|
1008
|
-
|
|
1009
|
-
adapter.log.debug(`[ALEXA] Lock "${id}"`);
|
|
1010
|
-
|
|
1011
|
-
if (writeStates) {
|
|
1012
|
-
await adapter.setStateAsync('smart.lastObjectID', id, true);
|
|
1013
|
-
}
|
|
1014
|
-
if (obj.native.LOCK_VALUE === undefined && obj.native.CONTROL_VALUE === undefined) {
|
|
1015
|
-
adapter.log.warn(
|
|
1016
|
-
`[ALEXA] Cannot choose value for lock: Please define in "${id}" the "native.LOCK_VALUE" or "native.CONTROL_VALUE" with locking value`,
|
|
1017
|
-
);
|
|
1018
|
-
throw new Error('Cannot choose value for lock');
|
|
1019
|
-
} else {
|
|
1020
|
-
try {
|
|
1021
|
-
await adapter.setForeignStateAsync(
|
|
1022
|
-
id,
|
|
1023
|
-
obj.native.CONTROL_VALUE === undefined ? obj.native.LOCK_VALUE : obj.native.CONTROL_VALUE,
|
|
1024
|
-
);
|
|
1025
|
-
} catch (err) {
|
|
1026
|
-
throw new Error(`[ALEXA] Cannot set device: ${err}`);
|
|
1027
|
-
}
|
|
1028
|
-
}
|
|
1029
|
-
}
|
|
1030
|
-
|
|
1031
|
-
async function getLock(id, writeStates) {
|
|
1032
|
-
let obj;
|
|
1033
|
-
try {
|
|
1034
|
-
obj = await adapter.getForeignObjectAsync(id);
|
|
1035
|
-
} catch (err) {
|
|
1036
|
-
adapter.log.error(`[ALEXA] Cannot control non state "${id}": ${err}`);
|
|
1037
|
-
}
|
|
1038
|
-
|
|
1039
|
-
if (!obj || obj.type !== 'state') {
|
|
1040
|
-
throw new Error(`Cannot read non state: ${id} ${obj ? obj.type : 'no object'}`);
|
|
1041
|
-
}
|
|
1042
|
-
|
|
1043
|
-
adapter.log.debug(`[ALEXA] Get lock state "${id}"`);
|
|
1044
|
-
|
|
1045
|
-
if (writeStates) {
|
|
1046
|
-
await adapter.setStateAsync('smart.lastObjectID', id, true);
|
|
1047
|
-
}
|
|
1048
|
-
|
|
1049
|
-
if (obj.native.LOCK_VALUE === undefined) {
|
|
1050
|
-
adapter.log.warn(
|
|
1051
|
-
`[ALEXA] Cannot choose value for lock: Please define in "${id}" the "native.LOCK_VALUE" with locking value`,
|
|
1052
|
-
);
|
|
1053
|
-
throw new Error('Cannot choose value for lock');
|
|
1054
|
-
} else {
|
|
1055
|
-
let state;
|
|
1056
|
-
try {
|
|
1057
|
-
state = await adapter.getForeignStateAsync(id);
|
|
1058
|
-
} catch (err) {
|
|
1059
|
-
adapter.log.error(`[ALEXA] Cannot get state "${id}": ${err}`);
|
|
1060
|
-
}
|
|
1061
|
-
if (
|
|
1062
|
-
obj.native.LOCK_VALUE === 'true' ||
|
|
1063
|
-
obj.native.LOCK_VALUE === '1' ||
|
|
1064
|
-
obj.native.LOCK_VALUE === 1 ||
|
|
1065
|
-
obj.native.LOCK_VALUE === 'locked'
|
|
1066
|
-
) {
|
|
1067
|
-
obj.native.LOCK_VALUE = true;
|
|
1068
|
-
}
|
|
1069
|
-
if (
|
|
1070
|
-
obj.native.LOCK_VALUE === 'false' ||
|
|
1071
|
-
obj.native.LOCK_VALUE === '0' ||
|
|
1072
|
-
obj.native.LOCK_VALUE === 0 ||
|
|
1073
|
-
obj.native.LOCK_VALUE === 'unlocked'
|
|
1074
|
-
) {
|
|
1075
|
-
obj.native.LOCK_VALUE = false;
|
|
1076
|
-
}
|
|
1077
|
-
if (state.val === 'true' || state.val === '1' || state.val === 1 || state.val === 'locked') {
|
|
1078
|
-
state.val = true;
|
|
1079
|
-
}
|
|
1080
|
-
if (state.val === 'false' || state.val === '0' || state.val === 0 || state.val === 'unlocked') {
|
|
1081
|
-
state.val = false;
|
|
1082
|
-
}
|
|
1083
|
-
|
|
1084
|
-
return { value: state.val === obj.native.LOCK_VALUE, ts: state.lc || state.ts };
|
|
1085
|
-
}
|
|
1086
|
-
}
|
|
1087
|
-
|
|
1088
|
-
// expected hue range: [0, 1)
|
|
1089
|
-
// expected saturation range: [0, 1]
|
|
1090
|
-
// expected lightness range: [0, 1]
|
|
1091
|
-
// Based on http://axonflux.com/handy-rgb-to-hsl-and-rgb-to-hsv-color-model-c
|
|
1092
|
-
function hsvToRgb(h, s, v) {
|
|
1093
|
-
let r;
|
|
1094
|
-
let g;
|
|
1095
|
-
let b;
|
|
1096
|
-
let i = Math.floor(h * 6);
|
|
1097
|
-
let f = h * 6 - i;
|
|
1098
|
-
let p = v * (1 - s);
|
|
1099
|
-
let q = v * (1 - f * s);
|
|
1100
|
-
let t = v * (1 - (1 - f) * s);
|
|
1101
|
-
|
|
1102
|
-
switch (i % 6) {
|
|
1103
|
-
case 0:
|
|
1104
|
-
r = v;
|
|
1105
|
-
g = t;
|
|
1106
|
-
b = p;
|
|
1107
|
-
break;
|
|
1108
|
-
|
|
1109
|
-
case 1:
|
|
1110
|
-
r = q;
|
|
1111
|
-
g = v;
|
|
1112
|
-
b = p;
|
|
1113
|
-
break;
|
|
1114
|
-
|
|
1115
|
-
case 2:
|
|
1116
|
-
r = p;
|
|
1117
|
-
g = v;
|
|
1118
|
-
b = t;
|
|
1119
|
-
break;
|
|
1120
|
-
|
|
1121
|
-
case 3:
|
|
1122
|
-
r = p;
|
|
1123
|
-
g = q;
|
|
1124
|
-
b = v;
|
|
1125
|
-
break;
|
|
1126
|
-
|
|
1127
|
-
case 4:
|
|
1128
|
-
r = t;
|
|
1129
|
-
g = p;
|
|
1130
|
-
b = v;
|
|
1131
|
-
break;
|
|
1132
|
-
|
|
1133
|
-
case 5:
|
|
1134
|
-
r = v;
|
|
1135
|
-
g = p;
|
|
1136
|
-
b = q;
|
|
1137
|
-
break;
|
|
1138
|
-
}
|
|
1139
|
-
return '#' + padding(Math.round(r * 255)) + padding(Math.round(g * 255)) + padding(Math.round(b * 255));
|
|
1140
|
-
}
|
|
1141
|
-
|
|
1142
|
-
async function controlColorRgb(id, color, writeStates) {
|
|
1143
|
-
adapter.log.debug(`[ALEXA] ID: ${id} Color: ${JSON.stringify(color)}`);
|
|
1144
|
-
// "color": {
|
|
1145
|
-
// "hue": 0.0,
|
|
1146
|
-
// "saturation": 1.0000,
|
|
1147
|
-
// "brightness": 1.0000
|
|
1148
|
-
// }
|
|
1149
|
-
let obj;
|
|
1150
|
-
try {
|
|
1151
|
-
obj = await adapter.getForeignObjectAsync(id);
|
|
1152
|
-
} catch (err) {
|
|
1153
|
-
adapter.log.error(`[ALEXA] Cannot control non state "${id}": ${err}`);
|
|
1154
|
-
}
|
|
1155
|
-
|
|
1156
|
-
if (!obj || obj.type !== 'state') {
|
|
1157
|
-
throw new Error(`Cannot control non state: ${id} ${obj ? obj.type : 'no object'}`);
|
|
1158
|
-
}
|
|
1159
|
-
|
|
1160
|
-
let rgb = hsvToRgb(color.hue / 360, color.saturation, color.brightness);
|
|
1161
|
-
|
|
1162
|
-
if (writeStates) {
|
|
1163
|
-
await adapter.setStateAsync('smart.lastObjectID', id, true);
|
|
1164
|
-
}
|
|
1165
|
-
try {
|
|
1166
|
-
await adapter.setForeignStateAsync(id, rgb);
|
|
1167
|
-
} catch (err) {
|
|
1168
|
-
throw new Error(`[ALEXA] Cannot set device: ${err}`);
|
|
1169
|
-
}
|
|
1170
|
-
}
|
|
1171
|
-
|
|
1172
|
-
async function controlColorHue(ids, idh, idb, color, writeStates) {
|
|
1173
|
-
// "color": {
|
|
1174
|
-
// "hue": 0.0,
|
|
1175
|
-
// "saturation": 1.0000,
|
|
1176
|
-
// "brightness": 1.0000
|
|
1177
|
-
// }
|
|
1178
|
-
let obj;
|
|
1179
|
-
try {
|
|
1180
|
-
obj = await adapter.getForeignObjectAsync(idh);
|
|
1181
|
-
} catch (err) {
|
|
1182
|
-
adapter.log.error(`[ALEXA] Cannot control non state "${idh}": ${err}`);
|
|
1183
|
-
}
|
|
1184
|
-
|
|
1185
|
-
if (!obj || obj.type !== 'state') {
|
|
1186
|
-
throw new Error(`Cannot control non state: ${idh} ${obj ? obj.type : 'no object'}`);
|
|
1187
|
-
}
|
|
1188
|
-
|
|
1189
|
-
if (obj.common && obj.common.min !== undefined && obj.common.max !== undefined) {
|
|
1190
|
-
color.hue = Math.round((obj.common.max - obj.common.min) * (color.hue / 360) + obj.common.min);
|
|
1191
|
-
}
|
|
1192
|
-
|
|
1193
|
-
if (writeStates) {
|
|
1194
|
-
await adapter.setStateAsync('smart.lastObjectID', idh, true);
|
|
1195
|
-
}
|
|
1196
|
-
|
|
1197
|
-
try {
|
|
1198
|
-
await adapter.setForeignStateAsync(idh, color.hue);
|
|
1199
|
-
} catch (err) {
|
|
1200
|
-
adapter.log.error(`[ALEXA] Cannot set device: ${err}`);
|
|
1201
|
-
}
|
|
1202
|
-
|
|
1203
|
-
if (!ids) {
|
|
1204
|
-
adapter.log.info('Unable to control saturation. No Saturation Endpoint in Smart Device.');
|
|
1205
|
-
return;
|
|
1206
|
-
}
|
|
1207
|
-
|
|
1208
|
-
try {
|
|
1209
|
-
obj = await adapter.getForeignObjectAsync(ids);
|
|
1210
|
-
} catch (err) {
|
|
1211
|
-
adapter.log.error(`[ALEXA] Cannot control non state "${ids}": ${err}`);
|
|
1212
|
-
}
|
|
1213
|
-
|
|
1214
|
-
if (obj && obj.common && obj.common.min !== undefined && obj.common.max !== undefined) {
|
|
1215
|
-
color.saturation = (obj.common.max - obj.common.min) * color.saturation + obj.common.min;
|
|
1216
|
-
} else {
|
|
1217
|
-
color.saturation *= 100;
|
|
1218
|
-
}
|
|
1219
|
-
|
|
1220
|
-
try {
|
|
1221
|
-
await adapter.setForeignStateAsync(ids, color.saturation);
|
|
1222
|
-
} catch (err) {
|
|
1223
|
-
adapter.log.error(`[ALEXA] Cannot set device: ${err}`);
|
|
1224
|
-
}
|
|
1225
|
-
|
|
1226
|
-
if (!idb) {
|
|
1227
|
-
adapter.log.info('Unable to control brightness. No Dimmer Endpoint in Smart Device.');
|
|
1228
|
-
return;
|
|
1229
|
-
}
|
|
1230
|
-
try {
|
|
1231
|
-
obj = await adapter.getForeignObjectAsync(idb);
|
|
1232
|
-
} catch (err) {
|
|
1233
|
-
adapter.log.error(`[ALEXA] Cannot control non state "${idb}": ${err}`);
|
|
1234
|
-
}
|
|
1235
|
-
|
|
1236
|
-
if (obj && obj.common && (obj.common.ignoreAlexaColor === 'true' || obj.common.ignoreAlexaColor === true)) {
|
|
1237
|
-
return;
|
|
1238
|
-
}
|
|
1239
|
-
|
|
1240
|
-
if (obj && obj.common && obj.common.min !== undefined && obj.common.max !== undefined) {
|
|
1241
|
-
color.brightness = (obj.common.max - obj.common.min) * color.brightness + obj.common.min;
|
|
1242
|
-
} else {
|
|
1243
|
-
color.brightness *= 100;
|
|
1244
|
-
}
|
|
1245
|
-
|
|
1246
|
-
try {
|
|
1247
|
-
await adapter.setForeignStateAsync(idb, color.brightness);
|
|
1248
|
-
} catch (err) {
|
|
1249
|
-
throw new Error(`Cannot set device: ${err}`);
|
|
1250
|
-
}
|
|
1251
|
-
}
|
|
1252
|
-
|
|
1253
|
-
async function controlColorTemperature(temperature, id, value, writeStates) {
|
|
1254
|
-
let obj;
|
|
1255
|
-
try {
|
|
1256
|
-
obj = await adapter.getForeignObjectAsync(id);
|
|
1257
|
-
} catch (err) {
|
|
1258
|
-
adapter.log.error(`[ALEXA] Cannot control non state "${id}": ${err}`);
|
|
1259
|
-
}
|
|
1260
|
-
|
|
1261
|
-
if (!obj || obj.type !== 'state') {
|
|
1262
|
-
throw new Error(`Cannot control non state: ${id} ${obj ? obj.type : 'no object'}`);
|
|
1263
|
-
}
|
|
1264
|
-
|
|
1265
|
-
if (!temperature || (temperature && temperature !== id)) {
|
|
1266
|
-
adapter.log.debug(`[ALEXA] Won't control ${id} as not a color temperature.`);
|
|
1267
|
-
return;
|
|
1268
|
-
}
|
|
1269
|
-
|
|
1270
|
-
const unit = obj.common.unit;
|
|
1271
|
-
let friendlyUnit;
|
|
1272
|
-
if (unit == null) {
|
|
1273
|
-
// default to mired
|
|
1274
|
-
friendlyUnit = 'mired';
|
|
1275
|
-
} else if (unit.match('K')) {
|
|
1276
|
-
friendlyUnit = 'kelvin';
|
|
1277
|
-
} else if (unit === '%') {
|
|
1278
|
-
friendlyUnit = 'percent';
|
|
1279
|
-
}
|
|
1280
|
-
|
|
1281
|
-
// Alexa reports a value in Kelvin between 1000 and 10000
|
|
1282
|
-
// By default we limit ourselves to the predefined values:
|
|
1283
|
-
// warm, warm white 2200
|
|
1284
|
-
// incandescent, soft white 2700
|
|
1285
|
-
// white 4000
|
|
1286
|
-
// daylight, daylight white 5500
|
|
1287
|
-
// cool, cool white 7000
|
|
1288
|
-
let minK = 2200;
|
|
1289
|
-
let maxK = 7000;
|
|
1290
|
-
|
|
1291
|
-
if (friendlyUnit === 'kelvin') {
|
|
1292
|
-
if (obj.common.min != null) minK = parseFloat(obj.common.min);
|
|
1293
|
-
if (obj.common.max != null) maxK = parseFloat(obj.common.max);
|
|
1294
|
-
value = Math.max(minK, Math.min(value, maxK));
|
|
1295
|
-
} else {
|
|
1296
|
-
// % or mired
|
|
1297
|
-
// for non-kelvin scales we have to re-calculate the value
|
|
1298
|
-
// The internal representation is in mired
|
|
1299
|
-
value = 1e6 / value;
|
|
1300
|
-
|
|
1301
|
-
let minM = Math.round(1e6 / maxK);
|
|
1302
|
-
let maxM = Math.round(1e6 / minK);
|
|
1303
|
-
if (friendlyUnit === 'mired') {
|
|
1304
|
-
// respect the defined min/max
|
|
1305
|
-
if (obj.common.min != null) minM = parseFloat(obj.common.min);
|
|
1306
|
-
if (obj.common.max != null) maxM = parseFloat(obj.common.max);
|
|
1307
|
-
} else if (friendlyUnit === 'percent') {
|
|
1308
|
-
// scale the value from minM (0%) to maxM (100%)
|
|
1309
|
-
value = ((value - minM) / (maxM - minM)) * 100;
|
|
1310
|
-
// set the limits to 0..100
|
|
1311
|
-
minM = 0;
|
|
1312
|
-
maxM = 100;
|
|
1313
|
-
}
|
|
1314
|
-
value = Math.round(value);
|
|
1315
|
-
value = Math.max(minM, Math.min(value, maxM));
|
|
1316
|
-
}
|
|
1317
|
-
|
|
1318
|
-
adapter.log.debug(`[ALEXA] Controlling: ${id} setting to ${value}`);
|
|
1319
|
-
|
|
1320
|
-
if (writeStates) {
|
|
1321
|
-
await adapter.setStateAsync('smart.lastObjectID', id, true);
|
|
1322
|
-
}
|
|
1323
|
-
|
|
1324
|
-
try {
|
|
1325
|
-
await adapter.setForeignStateAsync(id, value);
|
|
1326
|
-
} catch (err) {
|
|
1327
|
-
adapter.log.error(`[ALEXA] Cannot switch device: ${err}`);
|
|
1328
|
-
}
|
|
1329
|
-
}
|
|
1330
|
-
|
|
1331
|
-
async function controlColorTemperatureDelta(temperature, id, delta, writeStates) {
|
|
1332
|
-
let obj;
|
|
1333
|
-
try {
|
|
1334
|
-
obj = await adapter.getForeignObjectAsync(id);
|
|
1335
|
-
} catch (err) {
|
|
1336
|
-
adapter.log.error(`[ALEXA] Cannot control non state "${id}": ${err}`);
|
|
1337
|
-
}
|
|
1338
|
-
|
|
1339
|
-
if (!obj || obj.type !== 'state') {
|
|
1340
|
-
adapter.log.error(`[ALEXA] Cannot control non state: ${id} ${obj ? obj.type : 'no object'}`);
|
|
1341
|
-
throw new Error('Not controlling');
|
|
1342
|
-
}
|
|
1343
|
-
|
|
1344
|
-
if (!temperature || (temperature && temperature !== id)) {
|
|
1345
|
-
adapter.log.debug(`[ALEXA] Won't control ${id} as not a color temperature.`);
|
|
1346
|
-
throw new Error('Not controlling');
|
|
1347
|
-
}
|
|
1348
|
-
|
|
1349
|
-
const unit = obj.common.unit;
|
|
1350
|
-
let friendlyUnit;
|
|
1351
|
-
if (unit == null) {
|
|
1352
|
-
// default to mired
|
|
1353
|
-
friendlyUnit = 'mired';
|
|
1354
|
-
} else if (unit.match('K')) {
|
|
1355
|
-
friendlyUnit = 'kelvin';
|
|
1356
|
-
} else if (unit === '%') {
|
|
1357
|
-
friendlyUnit = 'percent';
|
|
1358
|
-
}
|
|
1359
|
-
|
|
1360
|
-
// see controlColorTemperature for a description
|
|
1361
|
-
const minK = 2200;
|
|
1362
|
-
const maxK = 7000;
|
|
1363
|
-
let minValue = minK;
|
|
1364
|
-
let maxValue = maxK;
|
|
1365
|
-
|
|
1366
|
-
switch (friendlyUnit) {
|
|
1367
|
-
case 'kelvin': {
|
|
1368
|
-
delta = delta * -1;
|
|
1369
|
-
if (obj.common.min != null) {
|
|
1370
|
-
minValue = parseFloat(obj.common.min);
|
|
1371
|
-
}
|
|
1372
|
-
if (obj.common.max != null) {
|
|
1373
|
-
maxValue = parseFloat(obj.common.max);
|
|
1374
|
-
}
|
|
1375
|
-
break;
|
|
1376
|
-
}
|
|
1377
|
-
case 'mired': {
|
|
1378
|
-
minValue = Math.round(1e6 / maxValue);
|
|
1379
|
-
maxValue = Math.round(1e6 / minValue);
|
|
1380
|
-
if (obj.common.min != null) {
|
|
1381
|
-
minValue = parseFloat(obj.common.min);
|
|
1382
|
-
}
|
|
1383
|
-
if (obj.common.max != null) {
|
|
1384
|
-
maxValue = parseFloat(obj.common.max);
|
|
1385
|
-
}
|
|
1386
|
-
break;
|
|
1387
|
-
}
|
|
1388
|
-
case 'percent': {
|
|
1389
|
-
minValue = 0;
|
|
1390
|
-
maxValue = 100;
|
|
1391
|
-
}
|
|
1392
|
-
}
|
|
1393
|
-
|
|
1394
|
-
let state;
|
|
1395
|
-
try {
|
|
1396
|
-
state = await adapter.getForeignStateAsync(id);
|
|
1397
|
-
} catch (err) {
|
|
1398
|
-
adapter.log.error(`[ALEXA] Cannot get state "${id}": ${err}`);
|
|
1399
|
-
}
|
|
1400
|
-
let value = state ? state.val || 0 : 0;
|
|
1401
|
-
|
|
1402
|
-
// percentage of the state's range that is added/subtracted from the current value
|
|
1403
|
-
const deltaFactor = 0.25;
|
|
1404
|
-
value = Math.round(value + delta * deltaFactor * (maxValue - minValue));
|
|
1405
|
-
value = Math.max(minValue, Math.min(value, maxValue));
|
|
1406
|
-
|
|
1407
|
-
adapter.log.debug(`[ALEXA] Controlling: ${id} setting to ${value}`);
|
|
1408
|
-
|
|
1409
|
-
if (writeStates) {
|
|
1410
|
-
await adapter.setStateAsync('smart.lastObjectID', id, true);
|
|
1411
|
-
}
|
|
1412
|
-
|
|
1413
|
-
try {
|
|
1414
|
-
await adapter.setForeignStateAsync(id, value);
|
|
1415
|
-
} catch (err) {
|
|
1416
|
-
adapter.log.error(`[ALEXA] Cannot switch device: ${err}`);
|
|
1417
|
-
}
|
|
1418
|
-
|
|
1419
|
-
// convert value back to Kelvin for reporting
|
|
1420
|
-
if (friendlyUnit === 'percent') {
|
|
1421
|
-
// back to mired
|
|
1422
|
-
const minM = 1e6 / maxK;
|
|
1423
|
-
const maxM = 1e6 / minK;
|
|
1424
|
-
value = minM + (value / 100) * (maxM - minM);
|
|
1425
|
-
}
|
|
1426
|
-
|
|
1427
|
-
if (friendlyUnit === 'mired' || friendlyUnit === 'percent') {
|
|
1428
|
-
// back to Kelvin
|
|
1429
|
-
value = Math.round(1e6 / value);
|
|
1430
|
-
}
|
|
1431
|
-
|
|
1432
|
-
return value;
|
|
1433
|
-
}
|
|
1434
|
-
|
|
1435
|
-
function findRoleInChannel(channel, role) {
|
|
1436
|
-
for (let i = 0; i < channel.length; i++) {
|
|
1437
|
-
let dev = channel[i];
|
|
1438
|
-
if (dev.role === role) {
|
|
1439
|
-
return dev.id;
|
|
1440
|
-
}
|
|
1441
|
-
}
|
|
1442
|
-
return null;
|
|
1443
|
-
}
|
|
1444
|
-
|
|
1445
|
-
this.getDebug = function (cb) {
|
|
1446
|
-
let discoveredAppliances = JSON.parse(JSON.stringify(smartDevices));
|
|
1447
|
-
for (let j = 0; j < discoveredAppliances.length; j++) {
|
|
1448
|
-
if (!discoveredAppliances[j].additionalApplianceDetails) continue;
|
|
1449
|
-
if (discoveredAppliances[j].additionalApplianceDetails.names !== undefined)
|
|
1450
|
-
delete discoveredAppliances[j].additionalApplianceDetails.names;
|
|
1451
|
-
if (discoveredAppliances[j].additionalApplianceDetails.name !== undefined)
|
|
1452
|
-
delete discoveredAppliances[j].additionalApplianceDetails.name;
|
|
1453
|
-
if (discoveredAppliances[j].additionalApplianceDetails.byON !== undefined)
|
|
1454
|
-
delete discoveredAppliances[j].additionalApplianceDetails.byON;
|
|
1455
|
-
if (discoveredAppliances[j].additionalApplianceDetails.byONs !== undefined)
|
|
1456
|
-
delete discoveredAppliances[j].additionalApplianceDetails.byONs;
|
|
1457
|
-
if (discoveredAppliances[j].additionalApplianceDetails.nameModified !== undefined)
|
|
1458
|
-
delete discoveredAppliances[j].additionalApplianceDetails.nameModified;
|
|
1459
|
-
if (discoveredAppliances[j].additionalApplianceDetails.room !== undefined)
|
|
1460
|
-
delete discoveredAppliances[j].additionalApplianceDetails.room;
|
|
1461
|
-
if (discoveredAppliances[j].additionalApplianceDetails.func !== undefined)
|
|
1462
|
-
delete discoveredAppliances[j].additionalApplianceDetails.func;
|
|
1463
|
-
if (discoveredAppliances[j].additionalApplianceDetails.smartType !== undefined)
|
|
1464
|
-
delete discoveredAppliances[j].additionalApplianceDetails.smartType;
|
|
1465
|
-
if (discoveredAppliances[j].additionalApplianceDetails.smartTypes !== undefined)
|
|
1466
|
-
delete discoveredAppliances[j].additionalApplianceDetails.smartTypes;
|
|
1467
|
-
if (discoveredAppliances[j].additionalApplianceDetails.channels !== undefined)
|
|
1468
|
-
delete discoveredAppliances[j].additionalApplianceDetails.channels;
|
|
1469
|
-
}
|
|
1470
|
-
|
|
1471
|
-
cb({
|
|
1472
|
-
header: {},
|
|
1473
|
-
payload: {
|
|
1474
|
-
discoveredAppliances,
|
|
1475
|
-
},
|
|
1476
|
-
});
|
|
1477
|
-
};
|
|
1478
|
-
|
|
1479
|
-
this.process = async (request, isEnabled) => {
|
|
1480
|
-
let channels = {};
|
|
1481
|
-
let count = 0;
|
|
1482
|
-
let device;
|
|
1483
|
-
|
|
1484
|
-
if (!isEnabled) {
|
|
1485
|
-
request.header.name = 'NotSupportedInCurrentModeError';
|
|
1486
|
-
return {
|
|
1487
|
-
header: request.header,
|
|
1488
|
-
payload: {},
|
|
1489
|
-
};
|
|
1490
|
-
}
|
|
1491
|
-
|
|
1492
|
-
if (
|
|
1493
|
-
request &&
|
|
1494
|
-
request.payload &&
|
|
1495
|
-
request.payload.appliance &&
|
|
1496
|
-
request.payload.appliance.additionalApplianceDetails
|
|
1497
|
-
) {
|
|
1498
|
-
let details = null;
|
|
1499
|
-
for (let i = 0; i < smartDevices.length; i++) {
|
|
1500
|
-
if (smartDevices[i].applianceId === request.payload.appliance.applianceId) {
|
|
1501
|
-
device = smartDevices[i];
|
|
1502
|
-
details = smartDevices[i].additionalApplianceDetails;
|
|
1503
|
-
}
|
|
1504
|
-
}
|
|
1505
|
-
|
|
1506
|
-
if (adapter.common.loglevel === 'debug') {
|
|
1507
|
-
adapter.log.debug(`[ALEXA] Found following devices to control: ${JSON.stringify(details)}`);
|
|
1508
|
-
}
|
|
1509
|
-
|
|
1510
|
-
if (details) {
|
|
1511
|
-
if (details.group) {
|
|
1512
|
-
channels = details.channels;
|
|
1513
|
-
} else {
|
|
1514
|
-
let id = details.id;
|
|
1515
|
-
let role = details.role;
|
|
1516
|
-
let _parts = id.split('.');
|
|
1517
|
-
_parts.pop();
|
|
1518
|
-
let channel = _parts.join('.');
|
|
1519
|
-
channels[channel] = [{ id: id, role: role }];
|
|
1520
|
-
}
|
|
1521
|
-
}
|
|
1522
|
-
|
|
1523
|
-
for (const chan in channels) {
|
|
1524
|
-
if (channels.hasOwnProperty(chan)) {
|
|
1525
|
-
count += channels[chan].length;
|
|
1526
|
-
}
|
|
1527
|
-
}
|
|
1528
|
-
}
|
|
1529
|
-
adapter.log.debug(`[ALEXA] New Request: ${request.header.name}`);
|
|
1530
|
-
|
|
1531
|
-
switch (request.header.name) {
|
|
1532
|
-
case 'DiscoverAppliancesRequest': {
|
|
1533
|
-
//{
|
|
1534
|
-
// "header": {
|
|
1535
|
-
// "messageId": "6d6d6e14-8aee-473e-8c24-0d31ff9c17a2",
|
|
1536
|
-
// "name": "DiscoverAppliancesRequest",
|
|
1537
|
-
// "namespace": "Alexa.ConnectedHome.Discovery",
|
|
1538
|
-
// "payloadVersion": "2"
|
|
1539
|
-
// },
|
|
1540
|
-
// "payload": {
|
|
1541
|
-
// "accessToken": "*OAuth Token here*"
|
|
1542
|
-
// }
|
|
1543
|
-
//}
|
|
1544
|
-
request.header.name = 'DiscoverAppliancesResponse';
|
|
1545
|
-
//if (smartDevices.length > 100) smartDevices.splice(50, smartDevices.length - 50);
|
|
1546
|
-
//console.log(JSON.stringify(smartDevices));
|
|
1547
|
-
const discoveredAppliances = JSON.parse(JSON.stringify(smartDevices));
|
|
1548
|
-
for (let j = 0; j < discoveredAppliances.length; j++) {
|
|
1549
|
-
if (!discoveredAppliances[j].additionalApplianceDetails) continue;
|
|
1550
|
-
if (discoveredAppliances[j].additionalApplianceDetails.names !== undefined)
|
|
1551
|
-
delete discoveredAppliances[j].additionalApplianceDetails.names;
|
|
1552
|
-
if (discoveredAppliances[j].additionalApplianceDetails.name !== undefined)
|
|
1553
|
-
delete discoveredAppliances[j].additionalApplianceDetails.name;
|
|
1554
|
-
if (discoveredAppliances[j].additionalApplianceDetails.byON !== undefined)
|
|
1555
|
-
delete discoveredAppliances[j].additionalApplianceDetails.byON;
|
|
1556
|
-
if (discoveredAppliances[j].additionalApplianceDetails.byONs !== undefined)
|
|
1557
|
-
delete discoveredAppliances[j].additionalApplianceDetails.byONs;
|
|
1558
|
-
if (discoveredAppliances[j].additionalApplianceDetails.nameModified !== undefined)
|
|
1559
|
-
delete discoveredAppliances[j].additionalApplianceDetails.nameModified;
|
|
1560
|
-
if (discoveredAppliances[j].additionalApplianceDetails.room !== undefined)
|
|
1561
|
-
delete discoveredAppliances[j].additionalApplianceDetails.room;
|
|
1562
|
-
if (discoveredAppliances[j].additionalApplianceDetails.func !== undefined)
|
|
1563
|
-
delete discoveredAppliances[j].additionalApplianceDetails.func;
|
|
1564
|
-
if (discoveredAppliances[j].additionalApplianceDetails.smartType !== undefined)
|
|
1565
|
-
delete discoveredAppliances[j].additionalApplianceDetails.smartType;
|
|
1566
|
-
if (discoveredAppliances[j].additionalApplianceDetails.smartTypes !== undefined)
|
|
1567
|
-
delete discoveredAppliances[j].additionalApplianceDetails.smartTypes;
|
|
1568
|
-
if (discoveredAppliances[j].additionalApplianceDetails.channels !== undefined)
|
|
1569
|
-
delete discoveredAppliances[j].additionalApplianceDetails.channels;
|
|
1570
|
-
}
|
|
1571
|
-
|
|
1572
|
-
let response = {
|
|
1573
|
-
header: request.header,
|
|
1574
|
-
payload: {
|
|
1575
|
-
discoveredAppliances /*[
|
|
1576
|
-
{
|
|
1577
|
-
"applianceId": "hm-rpc",
|
|
1578
|
-
"manufacturerName": "ioBroker",
|
|
1579
|
-
"modelName": "Bad.Hauptlicht.Aktor.STATE",
|
|
1580
|
-
"version": "1",
|
|
1581
|
-
"friendlyName": "Licht im Bad",
|
|
1582
|
-
"friendlyDescription": "Bad Hauptlicht Aktor STATE",
|
|
1583
|
-
"isReachable": true,
|
|
1584
|
-
"actions": [
|
|
1585
|
-
"incrementTargetTemperature",
|
|
1586
|
-
"decrementTargetTemperature",
|
|
1587
|
-
"setTargetTemperature"
|
|
1588
|
-
],
|
|
1589
|
-
"additionalApplianceDetails": {
|
|
1590
|
-
id: 'hm-rpc.1.1'
|
|
1591
|
-
}
|
|
1592
|
-
},{
|
|
1593
|
-
"applianceId": "hm-rpc-1",
|
|
1594
|
-
"manufacturerName": "ioBroker",
|
|
1595
|
-
"modelName": "Bad.Hauptlicht.Aktor.STATE",
|
|
1596
|
-
"version": "1",
|
|
1597
|
-
"friendlyName": "Licht im Bad",
|
|
1598
|
-
"friendlyDescription": "Bad Hauptlicht Aktor STATE",
|
|
1599
|
-
"isReachable": true,
|
|
1600
|
-
"actions": [
|
|
1601
|
-
"incrementTargetTemperature",
|
|
1602
|
-
"decrementTargetTemperature",
|
|
1603
|
-
"setTargetTemperature"
|
|
1604
|
-
],
|
|
1605
|
-
"additionalApplianceDetails": {}
|
|
1606
|
-
},
|
|
1607
|
-
{
|
|
1608
|
-
"applianceId": "uniqueThermostatDeviceId",
|
|
1609
|
-
"manufacturerName": "yourManufacturerName",
|
|
1610
|
-
"modelName": "fancyThermostat",
|
|
1611
|
-
"version": "your software version number here.",
|
|
1612
|
-
"friendlyName": "Bedroom Thermostat",
|
|
1613
|
-
"friendlyDescription": "descriptionThatIsShownToCustomer",
|
|
1614
|
-
"isReachable": true,
|
|
1615
|
-
"actions": [
|
|
1616
|
-
"incrementTargetTemperature",
|
|
1617
|
-
"decrementTargetTemperature",
|
|
1618
|
-
"setTargetTemperature"
|
|
1619
|
-
],
|
|
1620
|
-
"additionalApplianceDetails": {
|
|
1621
|
-
}
|
|
1622
|
-
},
|
|
1623
|
-
{
|
|
1624
|
-
"actions": [
|
|
1625
|
-
"incrementPercentage",
|
|
1626
|
-
"decrementPercentage",
|
|
1627
|
-
"setPercentage",
|
|
1628
|
-
"turnOn",
|
|
1629
|
-
"turnOff"
|
|
1630
|
-
],
|
|
1631
|
-
"additionalApplianceDetails": {},
|
|
1632
|
-
"applianceId": "uniqueLightDeviceId",
|
|
1633
|
-
"friendlyDescription": "descriptionThatIsShownToCustomer",
|
|
1634
|
-
"friendlyName": "Living Room",
|
|
1635
|
-
"isReachable": true,
|
|
1636
|
-
"manufacturerName": "yourManufacturerName",
|
|
1637
|
-
"modelName": "fancyLight",
|
|
1638
|
-
"version": "your software version number here."
|
|
1639
|
-
}
|
|
1640
|
-
]*/,
|
|
1641
|
-
},
|
|
1642
|
-
};
|
|
1643
|
-
|
|
1644
|
-
adapter.log.debug(`[ALEXA] Response size: ${Math.round(JSON.stringify(response).length / 1024)}kb`);
|
|
1645
|
-
// adapter.log.debug('[ALEXA] Response: ' + JSON.stringify(response));
|
|
1646
|
-
return response;
|
|
1647
|
-
}
|
|
1648
|
-
|
|
1649
|
-
case 'TurnOnRequest': {
|
|
1650
|
-
// {
|
|
1651
|
-
// "header": {
|
|
1652
|
-
// "messageId": "01ebf625-0b89-4c4d-b3aa-32340e894688",
|
|
1653
|
-
// "name": "TurnOnRequest",
|
|
1654
|
-
// "namespace": "Alexa.ConnectedHome.Control",
|
|
1655
|
-
// "payloadVersion": "2"
|
|
1656
|
-
// },
|
|
1657
|
-
// "payload": {
|
|
1658
|
-
// "accessToken": "[OAuth Token here]",
|
|
1659
|
-
// "appliance": {
|
|
1660
|
-
// "additionalApplianceDetails": {},
|
|
1661
|
-
// "applianceId": "[Device ID for Ceiling Fan]"
|
|
1662
|
-
// }
|
|
1663
|
-
// }
|
|
1664
|
-
// }
|
|
1665
|
-
adapter.log.debug(`[ALEXA] ALEXA ON: ${request.payload.appliance.applianceId}`);
|
|
1666
|
-
|
|
1667
|
-
for (let channel in channels) {
|
|
1668
|
-
if (!channels.hasOwnProperty(channel)) {
|
|
1669
|
-
continue;
|
|
1670
|
-
}
|
|
1671
|
-
// try to find ON value for dimmer
|
|
1672
|
-
let toggle = findRoleInChannel(channels[channel], 'level.dimmer');
|
|
1673
|
-
if (toggle) {
|
|
1674
|
-
const byON = device.additionalApplianceDetails.group
|
|
1675
|
-
? device.additionalApplianceDetails.byONs[toggle]
|
|
1676
|
-
: device.additionalApplianceDetails.byON;
|
|
1677
|
-
if (!byON || byON === '-' || byON === 'omit') {
|
|
1678
|
-
toggle = null;
|
|
1679
|
-
}
|
|
1680
|
-
}
|
|
1681
|
-
|
|
1682
|
-
toggle = toggle || findRoleInChannel(channels[channel], 'switch');
|
|
1683
|
-
|
|
1684
|
-
for (let i = 0; i < channels[channel].length; i++) {
|
|
1685
|
-
let id = channels[channel][i].id;
|
|
1686
|
-
adapter.log.debug(`[ALEXA] Controlling: ${id}`);
|
|
1687
|
-
try {
|
|
1688
|
-
await controlOnOff(toggle, id, true, Object.keys(channels).length === 1);
|
|
1689
|
-
} catch (err) {
|
|
1690
|
-
adapter.log.warn(`[ALEXA] Cannot controlOnOff ${err}`);
|
|
1691
|
-
}
|
|
1692
|
-
}
|
|
1693
|
-
}
|
|
1694
|
-
|
|
1695
|
-
writeResponse(request.payload.appliance.applianceId, 'ONOFF', true).catch(err =>
|
|
1696
|
-
adapter.log.error(`[ALEXA] ${err}`),
|
|
1697
|
-
);
|
|
1698
|
-
|
|
1699
|
-
request.header.name = 'TurnOnConfirmation';
|
|
1700
|
-
|
|
1701
|
-
return {
|
|
1702
|
-
header: request.header,
|
|
1703
|
-
payload: {},
|
|
1704
|
-
};
|
|
1705
|
-
}
|
|
1706
|
-
|
|
1707
|
-
case 'TurnOffRequest': {
|
|
1708
|
-
// {
|
|
1709
|
-
// "header": {
|
|
1710
|
-
// "messageId": "01ebf625-0b89-4c4d-b3aa-32340e894688",
|
|
1711
|
-
// "name": "TurnOffRequest",
|
|
1712
|
-
// "namespace": "Alexa.ConnectedHome.Control",
|
|
1713
|
-
// "payloadVersion": "2"
|
|
1714
|
-
// },
|
|
1715
|
-
// "payload": {
|
|
1716
|
-
// "accessToken": "[OAuth Token here]",
|
|
1717
|
-
// "appliance": {
|
|
1718
|
-
// "additionalApplianceDetails": {},
|
|
1719
|
-
// "applianceId": "[Device ID for Ceiling Fan]"
|
|
1720
|
-
// }
|
|
1721
|
-
// }
|
|
1722
|
-
// }
|
|
1723
|
-
adapter.log.debug(`[ALEXA] ALEXA OFF: ${request.payload.appliance.applianceId}`);
|
|
1724
|
-
|
|
1725
|
-
for (let channel in channels) {
|
|
1726
|
-
if (!channels.hasOwnProperty(channel)) {
|
|
1727
|
-
continue;
|
|
1728
|
-
}
|
|
1729
|
-
let toggle = findRoleInChannel(channels[channel], 'level.dimmer');
|
|
1730
|
-
if (toggle) {
|
|
1731
|
-
const byON = device.additionalApplianceDetails.group
|
|
1732
|
-
? device.additionalApplianceDetails.byONs[toggle]
|
|
1733
|
-
: device.additionalApplianceDetails.byON;
|
|
1734
|
-
if (!byON || byON === '-' || byON === 'omit') {
|
|
1735
|
-
toggle = null;
|
|
1736
|
-
}
|
|
1737
|
-
}
|
|
1738
|
-
|
|
1739
|
-
toggle = toggle || findRoleInChannel(channels[channel], 'switch');
|
|
1740
|
-
|
|
1741
|
-
adapter.log.debug(toggle);
|
|
1742
|
-
for (let i = 0; i < channels[channel].length; i++) {
|
|
1743
|
-
let id = channels[channel][i].id;
|
|
1744
|
-
adapter.log.debug(`[ALEXA] Controlling off: ${id}`);
|
|
1745
|
-
try {
|
|
1746
|
-
await controlOnOff(toggle, id, false, Object.keys(channels).length === 1);
|
|
1747
|
-
} catch (err) {
|
|
1748
|
-
adapter.log.warn(`[ALEXA] Cannot controlOnOff: ${err}`);
|
|
1749
|
-
}
|
|
1750
|
-
}
|
|
1751
|
-
}
|
|
1752
|
-
request.header.name = 'TurnOffConfirmation';
|
|
1753
|
-
|
|
1754
|
-
writeResponse(request.payload.appliance.applianceId, 'ONOFF', false).catch(err =>
|
|
1755
|
-
adapter.log.error(`[ALEXA] ${err}`),
|
|
1756
|
-
);
|
|
1757
|
-
|
|
1758
|
-
return {
|
|
1759
|
-
header: request.header,
|
|
1760
|
-
payload: {},
|
|
1761
|
-
};
|
|
1762
|
-
}
|
|
1763
|
-
|
|
1764
|
-
case 'SetLockStateRequest': {
|
|
1765
|
-
// {
|
|
1766
|
-
// "header": {
|
|
1767
|
-
// "messageId": "01ebf625-0b89-4c4d-b3aa-32340e894688",
|
|
1768
|
-
// "name": "TurnOnRequest",
|
|
1769
|
-
// "namespace": "Alexa.ConnectedHome.Control",
|
|
1770
|
-
// "payloadVersion": "2"
|
|
1771
|
-
// },
|
|
1772
|
-
// "payload": {
|
|
1773
|
-
// "accessToken": "[OAuth Token here]",
|
|
1774
|
-
// "appliance": {
|
|
1775
|
-
// "additionalApplianceDetails": {},
|
|
1776
|
-
// "applianceId": "[Device ID for Ceiling Fan]"
|
|
1777
|
-
// }
|
|
1778
|
-
// }
|
|
1779
|
-
// }
|
|
1780
|
-
adapter.log.debug(`[ALEXA] ALEXA LOCK: ${request.payload.appliance.applianceId}`);
|
|
1781
|
-
let err;
|
|
1782
|
-
for (let channel in channels) {
|
|
1783
|
-
if (!channels.hasOwnProperty(channel)) {
|
|
1784
|
-
continue;
|
|
1785
|
-
}
|
|
1786
|
-
for (let i = 0; i < channels[channel].length; i++) {
|
|
1787
|
-
let id = channels[channel][i].id;
|
|
1788
|
-
adapter.log.debug(`[ALEXA] Controlling lock: ${id}`);
|
|
1789
|
-
try {
|
|
1790
|
-
await controlLock(id, Object.keys(channels).length === 1);
|
|
1791
|
-
} catch (_err) {
|
|
1792
|
-
adapter.log.error(`[ALEXA] Cannot controlLock: ${_err}`);
|
|
1793
|
-
err = _err;
|
|
1794
|
-
}
|
|
1795
|
-
}
|
|
1796
|
-
}
|
|
1797
|
-
|
|
1798
|
-
writeResponse(request.payload.appliance.applianceId, 'ONOFF', true).catch(err =>
|
|
1799
|
-
adapter.log.error(`[ALEXA] ${err}`),
|
|
1800
|
-
);
|
|
1801
|
-
|
|
1802
|
-
request.header.name = 'SetLockStateConfirmation';
|
|
1803
|
-
|
|
1804
|
-
if (err) {
|
|
1805
|
-
return {
|
|
1806
|
-
header: request.header,
|
|
1807
|
-
payload: {
|
|
1808
|
-
lockState: 'UNLOCKED',
|
|
1809
|
-
},
|
|
1810
|
-
};
|
|
1811
|
-
}
|
|
1812
|
-
|
|
1813
|
-
return {
|
|
1814
|
-
header: request.header,
|
|
1815
|
-
payload: {
|
|
1816
|
-
lockState: 'LOCKED',
|
|
1817
|
-
},
|
|
1818
|
-
};
|
|
1819
|
-
}
|
|
1820
|
-
|
|
1821
|
-
case 'GetLockStateRequest': {
|
|
1822
|
-
// {
|
|
1823
|
-
// "header": {
|
|
1824
|
-
// "messageId": "01ebf625-0b89-4c4d-b3aa-32340e894688",
|
|
1825
|
-
// "name": "TurnOnRequest",
|
|
1826
|
-
// "namespace": "Alexa.ConnectedHome.Control",
|
|
1827
|
-
// "payloadVersion": "2"
|
|
1828
|
-
// },
|
|
1829
|
-
// "payload": {
|
|
1830
|
-
// "accessToken": "[OAuth Token here]",
|
|
1831
|
-
// "appliance": {
|
|
1832
|
-
// "additionalApplianceDetails": {},
|
|
1833
|
-
// "applianceId": "[Device ID for Ceiling Fan]"
|
|
1834
|
-
// }
|
|
1835
|
-
// }
|
|
1836
|
-
// }
|
|
1837
|
-
adapter.log.debug(`[ALEXA] ALEXA GetLOCK: ${request.payload.appliance.applianceId}`);
|
|
1838
|
-
let value;
|
|
1839
|
-
let ts;
|
|
1840
|
-
let err;
|
|
1841
|
-
for (let channel in channels) {
|
|
1842
|
-
if (!channels.hasOwnProperty(channel)) {
|
|
1843
|
-
continue;
|
|
1844
|
-
}
|
|
1845
|
-
for (let i = 0; i < channels[channel].length; i++) {
|
|
1846
|
-
let id = channels[channel][i].id;
|
|
1847
|
-
adapter.log.debug(`[ALEXA] Controlling lock: ${id}`);
|
|
1848
|
-
let result;
|
|
1849
|
-
try {
|
|
1850
|
-
result = await getLock(id, Object.keys(channels).length === 1);
|
|
1851
|
-
} catch (_err) {
|
|
1852
|
-
err = _err;
|
|
1853
|
-
adapter.log.error(`[ALEXA] Cannot getLock: ${err}`);
|
|
1854
|
-
}
|
|
1855
|
-
|
|
1856
|
-
if (result) {
|
|
1857
|
-
value = result.value;
|
|
1858
|
-
ts = result.ts;
|
|
1859
|
-
adapter.log.debug(`[ALEXA] Settings result: ${value}`);
|
|
1860
|
-
}
|
|
1861
|
-
}
|
|
1862
|
-
}
|
|
1863
|
-
|
|
1864
|
-
request.header.name = 'GetLockStateResponse';
|
|
1865
|
-
|
|
1866
|
-
if (err) {
|
|
1867
|
-
return {
|
|
1868
|
-
header: request.header,
|
|
1869
|
-
payload: {
|
|
1870
|
-
lockState: 'UNLOCKED',
|
|
1871
|
-
},
|
|
1872
|
-
};
|
|
1873
|
-
}
|
|
1874
|
-
|
|
1875
|
-
return {
|
|
1876
|
-
header: request.header,
|
|
1877
|
-
payload: {
|
|
1878
|
-
lockState: value ? 'LOCKED' : 'UNLOCKED',
|
|
1879
|
-
applianceResponseTimestamp: ts ? new Date(ts).toISOString() : new Date().toISOString(),
|
|
1880
|
-
},
|
|
1881
|
-
};
|
|
1882
|
-
}
|
|
1883
|
-
|
|
1884
|
-
case 'SetPercentageRequest': {
|
|
1885
|
-
adapter.log.debug(
|
|
1886
|
-
`[ALEXA] ALEXA Percent: ${request.payload.appliance.applianceId} ${request.payload.percentageState.value}%`,
|
|
1887
|
-
);
|
|
1888
|
-
for (let channel in channels) {
|
|
1889
|
-
if (!channels.hasOwnProperty(channel)) {
|
|
1890
|
-
continue;
|
|
1891
|
-
}
|
|
1892
|
-
let dimmer = findRoleInChannel(channels[channel], 'level.dimmer');
|
|
1893
|
-
adapter.log.debug(`[ALEXA] DATA: ${JSON.stringify(channels[channel])}`);
|
|
1894
|
-
adapter.log.debug(dimmer);
|
|
1895
|
-
for (let i = 0; i < channels[channel].length; i++) {
|
|
1896
|
-
let id = channels[channel][i].id;
|
|
1897
|
-
adapter.log.debug(`[ALEXA] Controlling percentage: ${id}`);
|
|
1898
|
-
try {
|
|
1899
|
-
await controlPercent(
|
|
1900
|
-
dimmer,
|
|
1901
|
-
id,
|
|
1902
|
-
request.payload.percentageState.value,
|
|
1903
|
-
Object.keys(channels).length === 1,
|
|
1904
|
-
);
|
|
1905
|
-
} catch (err) {
|
|
1906
|
-
adapter.log.error(`[ALEXA] Cannot controlPercent: ${err}`);
|
|
1907
|
-
}
|
|
1908
|
-
}
|
|
1909
|
-
}
|
|
1910
|
-
request.header.name = 'SetPercentageConfirmation';
|
|
1911
|
-
|
|
1912
|
-
writeResponse(request.payload.appliance.applianceId, '%', request.payload.percentageState.value).catch(
|
|
1913
|
-
err => adapter.log.error(`[ALEXA] ${err}`),
|
|
1914
|
-
);
|
|
1915
|
-
|
|
1916
|
-
return {
|
|
1917
|
-
header: request.header,
|
|
1918
|
-
payload: {},
|
|
1919
|
-
};
|
|
1920
|
-
}
|
|
1921
|
-
|
|
1922
|
-
case 'IncrementPercentageRequest': {
|
|
1923
|
-
adapter.log.debug(
|
|
1924
|
-
`[ALEXA] ALEXA Increment: ${request.payload.appliance.applianceId} ${request.payload.deltaPercentage.value}%`,
|
|
1925
|
-
);
|
|
1926
|
-
for (let channel in channels) {
|
|
1927
|
-
if (!channels.hasOwnProperty(channel)) {
|
|
1928
|
-
continue;
|
|
1929
|
-
}
|
|
1930
|
-
let dimmer = findRoleInChannel(channels[channel], 'level.dimmer');
|
|
1931
|
-
|
|
1932
|
-
adapter.log.debug(`[ALEXA] DATA: ${JSON.stringify(channels[channel])}`);
|
|
1933
|
-
adapter.log.debug(dimmer);
|
|
1934
|
-
|
|
1935
|
-
for (let i = 0; i < channels[channel].length; i++) {
|
|
1936
|
-
let id = channels[channel][i].id;
|
|
1937
|
-
adapter.log.debug(`[ALEXA] Controlling increment percentage: ${id}`);
|
|
1938
|
-
try {
|
|
1939
|
-
await controlDelta(
|
|
1940
|
-
dimmer,
|
|
1941
|
-
id,
|
|
1942
|
-
request.payload.percentageState.value,
|
|
1943
|
-
Object.keys(channels).length === 1,
|
|
1944
|
-
);
|
|
1945
|
-
} catch (err) {
|
|
1946
|
-
adapter.log.error(`[ALEXA] Cannot controlDelta: ${err}`);
|
|
1947
|
-
}
|
|
1948
|
-
}
|
|
1949
|
-
}
|
|
1950
|
-
|
|
1951
|
-
request.header.name = 'IncrementPercentageConfirmation';
|
|
1952
|
-
|
|
1953
|
-
writeResponse(request.payload.appliance.applianceId, '<>', request.payload.deltaPercentage.value).catch(
|
|
1954
|
-
err => adapter.log.error(`[ALEXA] ${err}`),
|
|
1955
|
-
);
|
|
1956
|
-
|
|
1957
|
-
return {
|
|
1958
|
-
header: request.header,
|
|
1959
|
-
payload: {},
|
|
1960
|
-
};
|
|
1961
|
-
}
|
|
1962
|
-
|
|
1963
|
-
case 'DecrementPercentageRequest': {
|
|
1964
|
-
adapter.log.debug(
|
|
1965
|
-
`[ALEXA] ALEXA decrement: ${request.payload.appliance.applianceId} ${request.payload.deltaPercentage.value}%`,
|
|
1966
|
-
);
|
|
1967
|
-
for (let channel in channels) {
|
|
1968
|
-
if (!channels.hasOwnProperty(channel)) {
|
|
1969
|
-
continue;
|
|
1970
|
-
}
|
|
1971
|
-
let dimmer = findRoleInChannel(channels[channel], 'level.dimmer');
|
|
1972
|
-
|
|
1973
|
-
adapter.log.debug(`[ALEXA] DATA: ${JSON.stringify(channels[channel])}`);
|
|
1974
|
-
adapter.log.debug(dimmer);
|
|
1975
|
-
|
|
1976
|
-
for (let i = 0; i < channels[channel].length; i++) {
|
|
1977
|
-
let id = channels[channel][i].id;
|
|
1978
|
-
adapter.log.debug(`[ALEXA] Controlling decrement percentage: ${id}`);
|
|
1979
|
-
try {
|
|
1980
|
-
await controlDelta(
|
|
1981
|
-
dimmer,
|
|
1982
|
-
id,
|
|
1983
|
-
request.payload.percentageState.value * -1,
|
|
1984
|
-
Object.keys(channels).length === 1,
|
|
1985
|
-
);
|
|
1986
|
-
} catch (err) {
|
|
1987
|
-
adapter.log.error(`[ALEXA] Cannot controlDelta: ${err}`);
|
|
1988
|
-
}
|
|
1989
|
-
}
|
|
1990
|
-
}
|
|
1991
|
-
|
|
1992
|
-
request.header.name = 'DecrementPercentageConfirmation';
|
|
1993
|
-
|
|
1994
|
-
writeResponse(
|
|
1995
|
-
request.payload.appliance.applianceId,
|
|
1996
|
-
'<>',
|
|
1997
|
-
-1 * request.payload.deltaPercentage.value,
|
|
1998
|
-
).catch(err => adapter.log.error(`[ALEXA] ${err}`));
|
|
1999
|
-
|
|
2000
|
-
return {
|
|
2001
|
-
header: request.header,
|
|
2002
|
-
payload: {},
|
|
2003
|
-
};
|
|
2004
|
-
}
|
|
2005
|
-
|
|
2006
|
-
case 'SetTargetTemperatureRequest': {
|
|
2007
|
-
adapter.log.debug(
|
|
2008
|
-
`[ALEXA] ALEXA temperature Percent: ${request.payload.appliance.applianceId} ${request.payload.targetTemperature.value} grad`,
|
|
2009
|
-
);
|
|
2010
|
-
let err;
|
|
2011
|
-
let response;
|
|
2012
|
-
for (let channel in channels) {
|
|
2013
|
-
if (!channels.hasOwnProperty(channel)) {
|
|
2014
|
-
continue;
|
|
2015
|
-
}
|
|
2016
|
-
|
|
2017
|
-
for (let i = 0; i < channels[channel].length; i++) {
|
|
2018
|
-
let id = channels[channel][i].id;
|
|
2019
|
-
|
|
2020
|
-
adapter.log.debug(`[ALEXA] Controlling temperature: ${id}`);
|
|
2021
|
-
try {
|
|
2022
|
-
const _response = await controlTemperature(
|
|
2023
|
-
id,
|
|
2024
|
-
request.payload.targetTemperature.value,
|
|
2025
|
-
Object.keys(channels).length === 1,
|
|
2026
|
-
);
|
|
2027
|
-
if (_response && !response) {
|
|
2028
|
-
response = _response;
|
|
2029
|
-
}
|
|
2030
|
-
} catch (_err) {
|
|
2031
|
-
err = _err;
|
|
2032
|
-
adapter.log.error(`[ALEXA] Cannot controlTemperature: ${err}`);
|
|
2033
|
-
}
|
|
2034
|
-
}
|
|
2035
|
-
}
|
|
2036
|
-
|
|
2037
|
-
writeResponse(
|
|
2038
|
-
request.payload.appliance.applianceId,
|
|
2039
|
-
'°',
|
|
2040
|
-
request.payload.targetTemperature.value,
|
|
2041
|
-
).catch(err => adapter.log.error(`[ALEXA] ${err}`));
|
|
2042
|
-
|
|
2043
|
-
request.header.name = 'SetTargetTemperatureConfirmation';
|
|
2044
|
-
|
|
2045
|
-
if (err || !response) {
|
|
2046
|
-
return {
|
|
2047
|
-
header: request.header,
|
|
2048
|
-
payload: {
|
|
2049
|
-
targetTemperature: {
|
|
2050
|
-
value: 0,
|
|
2051
|
-
},
|
|
2052
|
-
temperatureMode: {
|
|
2053
|
-
value: 'AUTO',
|
|
2054
|
-
},
|
|
2055
|
-
previousState: {
|
|
2056
|
-
targetTemperature: {
|
|
2057
|
-
value: 0,
|
|
2058
|
-
},
|
|
2059
|
-
mode: {
|
|
2060
|
-
value: 'AUTO',
|
|
2061
|
-
},
|
|
2062
|
-
},
|
|
2063
|
-
},
|
|
2064
|
-
};
|
|
2065
|
-
}
|
|
2066
|
-
response.header = request.header;
|
|
2067
|
-
return response;
|
|
2068
|
-
}
|
|
2069
|
-
|
|
2070
|
-
case 'IncrementTargetTemperatureRequest': {
|
|
2071
|
-
request.payload.deltaTemperature.value = request.payload.deltaTemperature.value || 1;
|
|
2072
|
-
adapter.log.debug(
|
|
2073
|
-
`[ALEXA] ALEXA temperature Increment: ${request.payload.appliance.applianceId} ${request.payload.deltaTemperature.value} grad`,
|
|
2074
|
-
);
|
|
2075
|
-
let response;
|
|
2076
|
-
let err;
|
|
2077
|
-
|
|
2078
|
-
for (let channel in channels) {
|
|
2079
|
-
if (!channels.hasOwnProperty(channel)) {
|
|
2080
|
-
continue;
|
|
2081
|
-
}
|
|
2082
|
-
for (let i = 0; i < channels[channel].length; i++) {
|
|
2083
|
-
let id = channels[channel][i].id;
|
|
2084
|
-
adapter.log.debug(`[ALEXA] Controlling increment temperature: ${id}`);
|
|
2085
|
-
try {
|
|
2086
|
-
const _response = await controlTemperatureDelta(
|
|
2087
|
-
id,
|
|
2088
|
-
request.payload.deltaTemperature.value,
|
|
2089
|
-
Object.keys(channels).length === 1,
|
|
2090
|
-
);
|
|
2091
|
-
if (_response && !response) {
|
|
2092
|
-
response = _response;
|
|
2093
|
-
}
|
|
2094
|
-
} catch (_err) {
|
|
2095
|
-
err = _err;
|
|
2096
|
-
adapter.log.error(`[ALEXA] Cannot controlTemperatureDelta: ${err}`);
|
|
2097
|
-
}
|
|
2098
|
-
}
|
|
2099
|
-
}
|
|
2100
|
-
|
|
2101
|
-
writeResponse(
|
|
2102
|
-
request.payload.appliance.applianceId,
|
|
2103
|
-
'°<>',
|
|
2104
|
-
request.payload.targetTemperature.value,
|
|
2105
|
-
).catch(err => adapter.log.error(`[ALEXA] ${err}`));
|
|
2106
|
-
|
|
2107
|
-
request.header.name = 'IncrementTargetTemperatureConfirmation';
|
|
2108
|
-
|
|
2109
|
-
if (err || !response) {
|
|
2110
|
-
return {
|
|
2111
|
-
header: request.header,
|
|
2112
|
-
payload: {
|
|
2113
|
-
targetTemperature: {
|
|
2114
|
-
value: 0,
|
|
2115
|
-
},
|
|
2116
|
-
temperatureMode: {
|
|
2117
|
-
value: 'AUTO',
|
|
2118
|
-
},
|
|
2119
|
-
previousState: {
|
|
2120
|
-
targetTemperature: {
|
|
2121
|
-
value: 0,
|
|
2122
|
-
},
|
|
2123
|
-
mode: {
|
|
2124
|
-
value: 'AUTO',
|
|
2125
|
-
},
|
|
2126
|
-
},
|
|
2127
|
-
},
|
|
2128
|
-
};
|
|
2129
|
-
}
|
|
2130
|
-
response.header = request.header;
|
|
2131
|
-
return response;
|
|
2132
|
-
}
|
|
2133
|
-
|
|
2134
|
-
case 'DecrementTargetTemperatureRequest': {
|
|
2135
|
-
request.payload.deltaTemperature.value = request.payload.deltaTemperature.value || 1;
|
|
2136
|
-
adapter.log.debug(
|
|
2137
|
-
`[ALEXA] ALEXA temperature decrement: ${request.payload.appliance.applianceId} ${request.payload.deltaTemperature.value} grad`,
|
|
2138
|
-
);
|
|
2139
|
-
let response;
|
|
2140
|
-
let err;
|
|
2141
|
-
|
|
2142
|
-
for (let channel in channels) {
|
|
2143
|
-
if (!channels.hasOwnProperty(channel)) {
|
|
2144
|
-
continue;
|
|
2145
|
-
}
|
|
2146
|
-
|
|
2147
|
-
for (let i = 0; i < channels[channel].length; i++) {
|
|
2148
|
-
let id = channels[channel][i].id;
|
|
2149
|
-
|
|
2150
|
-
adapter.log.debug(`[ALEXA] Controlling decrement temperature: ${id}`);
|
|
2151
|
-
|
|
2152
|
-
try {
|
|
2153
|
-
const _response = await controlTemperatureDelta(
|
|
2154
|
-
id,
|
|
2155
|
-
request.payload.deltaTemperature.value * -1,
|
|
2156
|
-
Object.keys(channels).length === 1,
|
|
2157
|
-
);
|
|
2158
|
-
if (_response && !response) {
|
|
2159
|
-
response = _response;
|
|
2160
|
-
}
|
|
2161
|
-
} catch (_err) {
|
|
2162
|
-
err = _err;
|
|
2163
|
-
adapter.log.error(`[ALEXA] Cannot controlTemperatureDelta: ${err}`);
|
|
2164
|
-
}
|
|
2165
|
-
}
|
|
2166
|
-
}
|
|
2167
|
-
|
|
2168
|
-
writeResponse(
|
|
2169
|
-
request.payload.appliance.applianceId,
|
|
2170
|
-
'°<>',
|
|
2171
|
-
-1 * request.payload.targetTemperature.value,
|
|
2172
|
-
).catch(err => adapter.log.error(`[ALEXA] ${err}`));
|
|
2173
|
-
|
|
2174
|
-
request.header.name = 'DecrementTargetTemperatureConfirmation';
|
|
2175
|
-
|
|
2176
|
-
if (err || !response) {
|
|
2177
|
-
return {
|
|
2178
|
-
header: request.header,
|
|
2179
|
-
payload: {
|
|
2180
|
-
targetTemperature: {
|
|
2181
|
-
value: 0,
|
|
2182
|
-
},
|
|
2183
|
-
temperatureMode: {
|
|
2184
|
-
value: 'AUTO',
|
|
2185
|
-
},
|
|
2186
|
-
previousState: {
|
|
2187
|
-
targetTemperature: {
|
|
2188
|
-
value: 0,
|
|
2189
|
-
},
|
|
2190
|
-
mode: {
|
|
2191
|
-
value: 'AUTO',
|
|
2192
|
-
},
|
|
2193
|
-
},
|
|
2194
|
-
},
|
|
2195
|
-
};
|
|
2196
|
-
}
|
|
2197
|
-
response.header = request.header;
|
|
2198
|
-
return response;
|
|
2199
|
-
}
|
|
2200
|
-
|
|
2201
|
-
case 'GetTemperatureReadingRequest': {
|
|
2202
|
-
adapter.log.debug(`[ALEXA] ALEXA temperature get: ${request.payload.appliance.applianceId}`);
|
|
2203
|
-
let values = 0;
|
|
2204
|
-
let num = 0;
|
|
2205
|
-
let err;
|
|
2206
|
-
let ts;
|
|
2207
|
-
for (let channel in channels) {
|
|
2208
|
-
if (!channels.hasOwnProperty(channel)) {
|
|
2209
|
-
continue;
|
|
2210
|
-
}
|
|
2211
|
-
for (let i = 0; i < channels[channel].length; i++) {
|
|
2212
|
-
let id = channels[channel][i].id;
|
|
2213
|
-
|
|
2214
|
-
adapter.log.debug(`[ALEXA] Get temperature: ${id}`);
|
|
2215
|
-
let result;
|
|
2216
|
-
try {
|
|
2217
|
-
result = await getTemperature(id, Object.keys(channels).length === 1);
|
|
2218
|
-
} catch (_err) {
|
|
2219
|
-
adapter.log.error(`[ALEXA] Cannot getTemperature: ${_err}`);
|
|
2220
|
-
err = _err;
|
|
2221
|
-
}
|
|
2222
|
-
ts = result && result.ts;
|
|
2223
|
-
const value = result && result.value;
|
|
2224
|
-
|
|
2225
|
-
// calculate average temperature
|
|
2226
|
-
if (value !== null && value !== undefined) {
|
|
2227
|
-
num++;
|
|
2228
|
-
values += value;
|
|
2229
|
-
}
|
|
2230
|
-
}
|
|
2231
|
-
}
|
|
2232
|
-
request.header.name = 'GetTemperatureReadingResponse';
|
|
2233
|
-
|
|
2234
|
-
if (err) {
|
|
2235
|
-
return {
|
|
2236
|
-
header: request.header,
|
|
2237
|
-
payload: {
|
|
2238
|
-
temperatureReading: {
|
|
2239
|
-
value: 0,
|
|
2240
|
-
},
|
|
2241
|
-
applianceResponseTimestamp: new Date().toISOString(),
|
|
2242
|
-
},
|
|
2243
|
-
};
|
|
2244
|
-
}
|
|
2245
|
-
|
|
2246
|
-
return {
|
|
2247
|
-
header: request.header,
|
|
2248
|
-
payload: {
|
|
2249
|
-
temperatureReading: {
|
|
2250
|
-
value: Math.round((values * 10) / num) / 10,
|
|
2251
|
-
},
|
|
2252
|
-
applianceResponseTimestamp: ts ? new Date(ts).toISOString() : new Date().toISOString(),
|
|
2253
|
-
},
|
|
2254
|
-
};
|
|
2255
|
-
}
|
|
2256
|
-
|
|
2257
|
-
case 'GetTargetTemperatureRequest': {
|
|
2258
|
-
adapter.log.debug(`[ALEXA] ALEXA TargetTemperature get: ${request.payload.appliance.applianceId}`);
|
|
2259
|
-
let ts;
|
|
2260
|
-
let value;
|
|
2261
|
-
for (let channel in channels) {
|
|
2262
|
-
if (!channels.hasOwnProperty(channel)) {
|
|
2263
|
-
continue;
|
|
2264
|
-
}
|
|
2265
|
-
for (let i = 0; i < channels[channel].length; i++) {
|
|
2266
|
-
let id = channels[channel][i].id;
|
|
2267
|
-
|
|
2268
|
-
adapter.log.debug(`[ALEXA] Getting target temperature: ${id}`);
|
|
2269
|
-
|
|
2270
|
-
let result;
|
|
2271
|
-
try {
|
|
2272
|
-
result = await getTargetTemperature(id, Object.keys(channels).length === 1);
|
|
2273
|
-
} catch (err) {
|
|
2274
|
-
adapter.log.error(`[ALEXA] Cannot getTargetTemperature: ${err}`);
|
|
2275
|
-
}
|
|
2276
|
-
if (result && result.ts) {
|
|
2277
|
-
ts = result.ts;
|
|
2278
|
-
}
|
|
2279
|
-
if (result && result.value) {
|
|
2280
|
-
value = result.value;
|
|
2281
|
-
}
|
|
2282
|
-
}
|
|
2283
|
-
}
|
|
2284
|
-
|
|
2285
|
-
request.header.name = 'GetTargetTemperatureResponse';
|
|
2286
|
-
adapter.log.debug(`[ALEXA] Got: ${value}`);
|
|
2287
|
-
|
|
2288
|
-
return {
|
|
2289
|
-
header: request.header,
|
|
2290
|
-
payload: {
|
|
2291
|
-
targetTemperature: {
|
|
2292
|
-
value: value || 0,
|
|
2293
|
-
},
|
|
2294
|
-
temperatureMode: {
|
|
2295
|
-
value: 'CUSTOM',
|
|
2296
|
-
friendlyName: '',
|
|
2297
|
-
},
|
|
2298
|
-
applianceResponseTimestamp: ts ? new Date(ts).toISOString() : new Date().toISOString(),
|
|
2299
|
-
},
|
|
2300
|
-
};
|
|
2301
|
-
}
|
|
2302
|
-
|
|
2303
|
-
case 'SetColorRequest': {
|
|
2304
|
-
adapter.log.debug(
|
|
2305
|
-
`[ALEXA] ALEXA Color: ${request.payload.appliance.applianceId} ${JSON.stringify(request.payload.color)}`,
|
|
2306
|
-
);
|
|
2307
|
-
|
|
2308
|
-
count = Object.keys(channels).length;
|
|
2309
|
-
|
|
2310
|
-
for (let channel in channels) {
|
|
2311
|
-
if (!channels.hasOwnProperty(channel)) {
|
|
2312
|
-
continue;
|
|
2313
|
-
}
|
|
2314
|
-
let hue = findRoleInChannel(channels[channel], 'level.color.hue');
|
|
2315
|
-
if (hue) {
|
|
2316
|
-
adapter.log.debug(`[ALEXA] Using HUE for: ${channel}`);
|
|
2317
|
-
let sat = findRoleInChannel(channels[channel], 'level.color.saturation');
|
|
2318
|
-
let bri = findRoleInChannel(channels[channel], 'level.dimmer');
|
|
2319
|
-
let color = JSON.parse(JSON.stringify(request.payload.color));
|
|
2320
|
-
try {
|
|
2321
|
-
await controlColorHue(sat, hue, bri, color, Object.keys(channels).length === 1);
|
|
2322
|
-
} catch (err) {
|
|
2323
|
-
adapter.log.error(`[ALEXA] Cannot SetColorRequest: ${err}`);
|
|
2324
|
-
}
|
|
2325
|
-
continue;
|
|
2326
|
-
}
|
|
2327
|
-
|
|
2328
|
-
let rgb = findRoleInChannel(channels[channel], 'level.color.rgb');
|
|
2329
|
-
if (rgb) {
|
|
2330
|
-
adapter.log.debug(`[ALEXA] Using RGB for: ${channel}`);
|
|
2331
|
-
let color = JSON.parse(JSON.stringify(request.payload.color));
|
|
2332
|
-
try {
|
|
2333
|
-
await controlColorRgb(rgb, color, Object.keys(channels).length === 1);
|
|
2334
|
-
} catch (err) {
|
|
2335
|
-
adapter.log.error(`[ALEXA] Cannot SetColorRequest: ${err}`);
|
|
2336
|
-
}
|
|
2337
|
-
continue;
|
|
2338
|
-
}
|
|
2339
|
-
adapter.log.debug(`[ALEXA] Unable to set color for: ${channel}`);
|
|
2340
|
-
adapter.log.debug(`[ALEXA] Count: ${count}`);
|
|
2341
|
-
}
|
|
2342
|
-
|
|
2343
|
-
// writeResponse(request.payload.appliance.applianceId, '%', request.payload.percentageState.value);
|
|
2344
|
-
request.header.name = 'SetColorConfirmation';
|
|
2345
|
-
|
|
2346
|
-
return {
|
|
2347
|
-
header: request.header,
|
|
2348
|
-
payload: {
|
|
2349
|
-
achievedState: {
|
|
2350
|
-
color: JSON.parse(JSON.stringify(request.payload.color)),
|
|
2351
|
-
},
|
|
2352
|
-
},
|
|
2353
|
-
};
|
|
2354
|
-
}
|
|
2355
|
-
|
|
2356
|
-
case 'SetColorTemperatureRequest': {
|
|
2357
|
-
for (let channel in channels) {
|
|
2358
|
-
if (!channels.hasOwnProperty(channel)) {
|
|
2359
|
-
continue;
|
|
2360
|
-
}
|
|
2361
|
-
let temperature = findRoleInChannel(channels[channel], 'level.color.temperature');
|
|
2362
|
-
for (let i = 0; i < channels[channel].length; i++) {
|
|
2363
|
-
let id = channels[channel][i].id;
|
|
2364
|
-
adapter.log.debug(`[ALEXA] Controlling temperature kelvin: ${id}`);
|
|
2365
|
-
try {
|
|
2366
|
-
await controlColorTemperature(
|
|
2367
|
-
temperature,
|
|
2368
|
-
id,
|
|
2369
|
-
request.payload.colorTemperature.value,
|
|
2370
|
-
Object.keys(channels).length === 1,
|
|
2371
|
-
);
|
|
2372
|
-
} catch (err) {
|
|
2373
|
-
adapter.log.error(`[ALEXA] Cannot controlColorTemperature: ${err}`);
|
|
2374
|
-
}
|
|
2375
|
-
}
|
|
2376
|
-
}
|
|
2377
|
-
|
|
2378
|
-
request.header.name = 'SetColorTemperatureConfirmation';
|
|
2379
|
-
|
|
2380
|
-
return {
|
|
2381
|
-
header: request.header,
|
|
2382
|
-
payload: {
|
|
2383
|
-
achievedState: {
|
|
2384
|
-
colorTemperature: {
|
|
2385
|
-
value: request.payload.colorTemperature.value,
|
|
2386
|
-
},
|
|
2387
|
-
},
|
|
2388
|
-
},
|
|
2389
|
-
};
|
|
2390
|
-
}
|
|
2391
|
-
case 'IncrementColorTemperatureRequest': {
|
|
2392
|
-
let res;
|
|
2393
|
-
for (let channel in channels) {
|
|
2394
|
-
if (!channels.hasOwnProperty(channel)) {
|
|
2395
|
-
continue;
|
|
2396
|
-
}
|
|
2397
|
-
let temperature = findRoleInChannel(channels[channel], 'level.color.temperature');
|
|
2398
|
-
for (let i = 0; i < channels[channel].length; i++) {
|
|
2399
|
-
let id = channels[channel][i].id;
|
|
2400
|
-
adapter.log.debug(`[ALEXA] Controlling temperature kelvin: ${id}`);
|
|
2401
|
-
try {
|
|
2402
|
-
const _res = await controlColorTemperatureDelta(
|
|
2403
|
-
temperature,
|
|
2404
|
-
id,
|
|
2405
|
-
-1,
|
|
2406
|
-
Object.keys(channels).length === 1,
|
|
2407
|
-
);
|
|
2408
|
-
if (_res !== undefined && _res !== null) {
|
|
2409
|
-
res = _res;
|
|
2410
|
-
}
|
|
2411
|
-
} catch (err) {
|
|
2412
|
-
adapter.log.warn(`[ALEXA] Cannot controlColorTemperatureDelta: ${err}`);
|
|
2413
|
-
}
|
|
2414
|
-
}
|
|
2415
|
-
}
|
|
2416
|
-
|
|
2417
|
-
request.header.name = 'IncrementColorTemperatureConfirmation';
|
|
2418
|
-
|
|
2419
|
-
return {
|
|
2420
|
-
header: request.header,
|
|
2421
|
-
payload: {
|
|
2422
|
-
achievedState: {
|
|
2423
|
-
colorTemperature: {
|
|
2424
|
-
value: res,
|
|
2425
|
-
},
|
|
2426
|
-
},
|
|
2427
|
-
},
|
|
2428
|
-
};
|
|
2429
|
-
}
|
|
2430
|
-
|
|
2431
|
-
case 'DecrementColorTemperatureRequest': {
|
|
2432
|
-
let res;
|
|
2433
|
-
for (let channel in channels) {
|
|
2434
|
-
if (!channels.hasOwnProperty(channel)) {
|
|
2435
|
-
continue;
|
|
2436
|
-
}
|
|
2437
|
-
let temperature = findRoleInChannel(channels[channel], 'level.color.temperature');
|
|
2438
|
-
for (let i = 0; i < channels[channel].length; i++) {
|
|
2439
|
-
let id = channels[channel][i].id;
|
|
2440
|
-
adapter.log.debug(`[ALEXA] Controlling temperature kelvin: ${id}`);
|
|
2441
|
-
try {
|
|
2442
|
-
const _res = await controlColorTemperatureDelta(
|
|
2443
|
-
temperature,
|
|
2444
|
-
id,
|
|
2445
|
-
1,
|
|
2446
|
-
Object.keys(channels).length === 1,
|
|
2447
|
-
);
|
|
2448
|
-
if (_res !== undefined && _res !== null) {
|
|
2449
|
-
res = _res;
|
|
2450
|
-
}
|
|
2451
|
-
} catch (err) {
|
|
2452
|
-
adapter.log.warn(`[ALEXA] Cannot controlColorTemperatureDelta: ${err}`);
|
|
2453
|
-
}
|
|
2454
|
-
}
|
|
2455
|
-
}
|
|
2456
|
-
|
|
2457
|
-
request.header.name = 'DecrementColorTemperatureConfirmation';
|
|
2458
|
-
|
|
2459
|
-
return {
|
|
2460
|
-
header: request.header,
|
|
2461
|
-
payload: {
|
|
2462
|
-
achievedState: {
|
|
2463
|
-
colorTemperature: {
|
|
2464
|
-
value: res,
|
|
2465
|
-
},
|
|
2466
|
-
},
|
|
2467
|
-
},
|
|
2468
|
-
};
|
|
2469
|
-
}
|
|
2470
|
-
|
|
2471
|
-
case 'HealthCheckRequest': {
|
|
2472
|
-
request.header.name = 'HealthCheckResponse';
|
|
2473
|
-
try {
|
|
2474
|
-
adapter.log.debug(
|
|
2475
|
-
`[ALEXA] HealthCheckRequest duration: ${Date.now() - request.payload.initiationTimestamp} ms`,
|
|
2476
|
-
);
|
|
2477
|
-
} catch (e) {
|
|
2478
|
-
adapter.log.error('[ALEXA] No payload');
|
|
2479
|
-
}
|
|
2480
|
-
let text = 'Das System ist OK';
|
|
2481
|
-
if (lang === 'ru') {
|
|
2482
|
-
text = 'Система в порядке';
|
|
2483
|
-
} else if (lang === 'en') {
|
|
2484
|
-
text = 'The system is OK';
|
|
2485
|
-
}
|
|
2486
|
-
return {
|
|
2487
|
-
header: request.header,
|
|
2488
|
-
payload: {
|
|
2489
|
-
description: text,
|
|
2490
|
-
isHealthy: true,
|
|
2491
|
-
},
|
|
2492
|
-
};
|
|
2493
|
-
}
|
|
2494
|
-
|
|
2495
|
-
default: {
|
|
2496
|
-
request.header.name = 'NotSupportedInCurrentModeError';
|
|
2497
|
-
|
|
2498
|
-
return {
|
|
2499
|
-
header: request.header,
|
|
2500
|
-
payload: {},
|
|
2501
|
-
};
|
|
2502
|
-
}
|
|
2503
|
-
}
|
|
2504
|
-
};
|
|
2505
|
-
|
|
2506
|
-
this.updateDevices = function (_addedId, callback) {
|
|
2507
|
-
if (typeof _addedId === 'function') {
|
|
2508
|
-
callback = _addedId;
|
|
2509
|
-
_addedId = null;
|
|
2510
|
-
}
|
|
2511
|
-
|
|
2512
|
-
addedId = _addedId;
|
|
2513
|
-
|
|
2514
|
-
this.getDevices((err, result, analyseAddedId) => {
|
|
2515
|
-
addedId = null;
|
|
2516
|
-
smartDevices = result;
|
|
2517
|
-
callback && callback(analyseAddedId);
|
|
2518
|
-
});
|
|
2519
|
-
};
|
|
2520
|
-
|
|
2521
|
-
this.getEnums = function () {
|
|
2522
|
-
return enums;
|
|
2523
|
-
};
|
|
2524
|
-
|
|
2525
|
-
this.getDevices = function (callback) {
|
|
2526
|
-
if (!callback) {
|
|
2527
|
-
return smartDevices;
|
|
2528
|
-
}
|
|
2529
|
-
adapter.getObjectView('system', 'state', {}, (err, _states) => {
|
|
2530
|
-
let states = {};
|
|
2531
|
-
let ids = [];
|
|
2532
|
-
let alexaIds = [];
|
|
2533
|
-
let groups = {};
|
|
2534
|
-
let names = {};
|
|
2535
|
-
enums = [];
|
|
2536
|
-
|
|
2537
|
-
if (_states?.rows) {
|
|
2538
|
-
for (let i = 0; i < _states.rows.length; i++) {
|
|
2539
|
-
if (_states.rows[i].value) {
|
|
2540
|
-
states[_states.rows[i].id] = _states.rows[i].value;
|
|
2541
|
-
ids.push(_states.rows[i].id);
|
|
2542
|
-
|
|
2543
|
-
if (adapter.config.noCommon) {
|
|
2544
|
-
if (
|
|
2545
|
-
_states.rows[i].value.common?.custom?.[adapter.namespace]?.smartName &&
|
|
2546
|
-
_states.rows[i].value.common.custom[adapter.namespace].smartName !== 'ignore'
|
|
2547
|
-
) {
|
|
2548
|
-
alexaIds.push(_states.rows[i].id);
|
|
2549
|
-
}
|
|
2550
|
-
} else {
|
|
2551
|
-
if (
|
|
2552
|
-
_states.rows[i].value.common?.smartName &&
|
|
2553
|
-
_states.rows[i].value.common.smartName !== 'ignore'
|
|
2554
|
-
) {
|
|
2555
|
-
alexaIds.push(_states.rows[i].id);
|
|
2556
|
-
}
|
|
2557
|
-
}
|
|
2558
|
-
}
|
|
2559
|
-
}
|
|
2560
|
-
}
|
|
2561
|
-
ids.sort();
|
|
2562
|
-
adapter.getObjectView('system', 'enum', {}, (err, doc) => {
|
|
2563
|
-
// Build overlap from rooms and functions
|
|
2564
|
-
let rooms = [];
|
|
2565
|
-
let funcs = [];
|
|
2566
|
-
let smartName;
|
|
2567
|
-
if (doc?.rows) {
|
|
2568
|
-
for (let i = 0, l = doc.rows.length; i < l; i++) {
|
|
2569
|
-
if (doc.rows[i].value) {
|
|
2570
|
-
let _id = doc.rows[i].id;
|
|
2571
|
-
smartName = getSmartName(doc.rows[i].value);
|
|
2572
|
-
if (_id.match(/^enum\.rooms\./) && smartName !== 'ignore' && smartName !== false) {
|
|
2573
|
-
rooms.push(doc.rows[i].value);
|
|
2574
|
-
}
|
|
2575
|
-
if (_id.match(/^enum\.functions\./) && smartName !== 'ignore' && smartName !== false) {
|
|
2576
|
-
funcs.push(doc.rows[i].value);
|
|
2577
|
-
}
|
|
2578
|
-
if (_id.match(/^enum\.rooms\./) || _id.match(/^enum\.functions\./)) {
|
|
2579
|
-
if (!doc.rows[i].value) {
|
|
2580
|
-
doc.rows[i].value = {};
|
|
2581
|
-
}
|
|
2582
|
-
if (!doc.rows[i].value.common) {
|
|
2583
|
-
doc.rows[i].value.common = {};
|
|
2584
|
-
}
|
|
2585
|
-
|
|
2586
|
-
const en = {
|
|
2587
|
-
id: _id,
|
|
2588
|
-
name: doc.rows[i].value.common.name,
|
|
2589
|
-
color: doc.rows[i].value.common.color,
|
|
2590
|
-
icon: doc.rows[i].value.common.icon,
|
|
2591
|
-
type: 'enum',
|
|
2592
|
-
smartName,
|
|
2593
|
-
};
|
|
2594
|
-
if (typeof en.name === 'object') {
|
|
2595
|
-
en.name = en.name[lang] || en.name.en;
|
|
2596
|
-
}
|
|
2597
|
-
enums.push(en);
|
|
2598
|
-
}
|
|
2599
|
-
}
|
|
2600
|
-
}
|
|
2601
|
-
}
|
|
2602
|
-
let result = [];
|
|
2603
|
-
for (let f = 0; f < funcs.length; f++) {
|
|
2604
|
-
if (
|
|
2605
|
-
!funcs[f].common ||
|
|
2606
|
-
!funcs[f].common.members ||
|
|
2607
|
-
typeof funcs[f].common.members !== 'object' ||
|
|
2608
|
-
!funcs[f].common.members.length
|
|
2609
|
-
) {
|
|
2610
|
-
continue;
|
|
2611
|
-
}
|
|
2612
|
-
|
|
2613
|
-
for (let s = 0; s < funcs[f].common.members.length; s++) {
|
|
2614
|
-
let id = funcs[f].common.members[s];
|
|
2615
|
-
if (typeof id !== 'string') {
|
|
2616
|
-
continue;
|
|
2617
|
-
}
|
|
2618
|
-
smartName = getSmartName(funcs[f]);
|
|
2619
|
-
if (smartName && typeof smartName === 'object') {
|
|
2620
|
-
smartName = smartName[lang] || smartName.en;
|
|
2621
|
-
}
|
|
2622
|
-
let func = smartName || funcs[f].common.name;
|
|
2623
|
-
|
|
2624
|
-
if (!func) {
|
|
2625
|
-
func = funcs[f]._id.substring('enum.functions.'.length);
|
|
2626
|
-
func = func[0].toUpperCase() + func.substring(1);
|
|
2627
|
-
}
|
|
2628
|
-
|
|
2629
|
-
// Find room
|
|
2630
|
-
let room = '';
|
|
2631
|
-
for (let r = 0; r < rooms.length; r++) {
|
|
2632
|
-
if (
|
|
2633
|
-
!rooms[r].common?.members ||
|
|
2634
|
-
typeof rooms[r].common.members !== 'object' ||
|
|
2635
|
-
!rooms[r].common.members.length
|
|
2636
|
-
) {
|
|
2637
|
-
continue;
|
|
2638
|
-
}
|
|
2639
|
-
|
|
2640
|
-
if (rooms[r].common.members.includes(id)) {
|
|
2641
|
-
smartName = getSmartName(rooms[r]);
|
|
2642
|
-
if (smartName && typeof smartName === 'object') {
|
|
2643
|
-
smartName = smartName[lang] || smartName.en;
|
|
2644
|
-
}
|
|
2645
|
-
room = smartName || rooms[r].common.name;
|
|
2646
|
-
if (!room) {
|
|
2647
|
-
room = rooms[r]._id.substring('enum.rooms.'.length);
|
|
2648
|
-
room = room[0].toUpperCase() + room.substring(1);
|
|
2649
|
-
}
|
|
2650
|
-
}
|
|
2651
|
-
|
|
2652
|
-
if (!room) {
|
|
2653
|
-
// may be the channel is in the room
|
|
2654
|
-
let _parts = id.split('.');
|
|
2655
|
-
_parts.pop();
|
|
2656
|
-
let channel = _parts.join('.');
|
|
2657
|
-
if (rooms[r].common.members.includes(channel)) {
|
|
2658
|
-
smartName = getSmartName(funcs[f]);
|
|
2659
|
-
if (smartName && typeof smartName === 'object') {
|
|
2660
|
-
smartName = smartName[lang] || smartName.en;
|
|
2661
|
-
}
|
|
2662
|
-
|
|
2663
|
-
room = smartName || rooms[r].common.name;
|
|
2664
|
-
|
|
2665
|
-
if (!room) {
|
|
2666
|
-
room = rooms[r]._id.substring('enum.rooms.'.length);
|
|
2667
|
-
room = room[0].toUpperCase() + room.substring(1);
|
|
2668
|
-
}
|
|
2669
|
-
}
|
|
2670
|
-
}
|
|
2671
|
-
if (room) {
|
|
2672
|
-
break;
|
|
2673
|
-
}
|
|
2674
|
-
}
|
|
2675
|
-
|
|
2676
|
-
if (!states[id]) {
|
|
2677
|
-
let m = new RegExp(`^${id.replace(/\./g, '\\.')}`);
|
|
2678
|
-
for (let ii = 0; ii < ids.length; ii++) {
|
|
2679
|
-
if (ids[ii] < id) {
|
|
2680
|
-
continue;
|
|
2681
|
-
}
|
|
2682
|
-
if (m.exec(ids[ii])) {
|
|
2683
|
-
if (
|
|
2684
|
-
states[ids[ii]].common.role &&
|
|
2685
|
-
(states[ids[ii]].common.role === 'state' ||
|
|
2686
|
-
states[ids[ii]].common.role.match(/^switch/) ||
|
|
2687
|
-
states[ids[ii]].common.role.match(/^level/))
|
|
2688
|
-
) {
|
|
2689
|
-
processState(states, ids[ii], room, func, alexaIds, groups, names, result);
|
|
2690
|
-
}
|
|
2691
|
-
continue;
|
|
2692
|
-
}
|
|
2693
|
-
break;
|
|
2694
|
-
}
|
|
2695
|
-
} else {
|
|
2696
|
-
processState(states, id, room, func, alexaIds, groups, names, result);
|
|
2697
|
-
}
|
|
2698
|
-
}
|
|
2699
|
-
}
|
|
2700
|
-
|
|
2701
|
-
let analyseAddedId = null;
|
|
2702
|
-
|
|
2703
|
-
// process states with defined smartName
|
|
2704
|
-
for (let j = 0; j < alexaIds.length; j++) {
|
|
2705
|
-
const errorText = processState(states, alexaIds[j], null, null, null, groups, names, result);
|
|
2706
|
-
if (addedId && !analyseAddedId && alexaIds[j] === addedId && errorText) {
|
|
2707
|
-
adapter.log.error(errorText);
|
|
2708
|
-
analyseAddedId = errorText;
|
|
2709
|
-
}
|
|
2710
|
-
}
|
|
2711
|
-
|
|
2712
|
-
result.sort((a, b) => {
|
|
2713
|
-
let fnA = a.friendlyName;
|
|
2714
|
-
if (typeof fnA === 'object') {
|
|
2715
|
-
fnA = fnA[lang] || fnA['en'];
|
|
2716
|
-
}
|
|
2717
|
-
let fnB = b.friendlyName;
|
|
2718
|
-
if (typeof fnB === 'object') {
|
|
2719
|
-
fnB = fnB[lang] || fnB['en'];
|
|
2720
|
-
}
|
|
2721
|
-
if (fnA > fnB) {
|
|
2722
|
-
return 1;
|
|
2723
|
-
}
|
|
2724
|
-
if (fnA < fnB) {
|
|
2725
|
-
return -1;
|
|
2726
|
-
}
|
|
2727
|
-
return 0;
|
|
2728
|
-
});
|
|
2729
|
-
|
|
2730
|
-
for (let k = result.length - 1; k >= 0; k--) {
|
|
2731
|
-
if (result[k].disabled) {
|
|
2732
|
-
result.splice(k, 1);
|
|
2733
|
-
} else {
|
|
2734
|
-
let fn = result[k].friendlyName;
|
|
2735
|
-
if (typeof fn === 'object') {
|
|
2736
|
-
fn = fn[lang] || fn['en'];
|
|
2737
|
-
}
|
|
2738
|
-
adapter.log.debug(`[ALEXA] Created ALEXA device: ${fn} ${JSON.stringify(result[k].actions)}`);
|
|
2739
|
-
}
|
|
2740
|
-
}
|
|
2741
|
-
|
|
2742
|
-
// Check that all applianceId are unique: https://developer.amazon.com/en-US/blogs/alexa/post/Tx480XF5QCA6IN/managing-device-discovery-for-your-alexa-smart-home-skil
|
|
2743
|
-
const uniqueIds = [];
|
|
2744
|
-
for (let i = result.length - 1; i >= 0; i--) {
|
|
2745
|
-
if (uniqueIds.includes(result[i].applianceId)) {
|
|
2746
|
-
adapter.log.error(`[ALEXA] Duplicate applianceId: ${result[i].applianceId}`);
|
|
2747
|
-
result.splice(i, 1);
|
|
2748
|
-
continue;
|
|
2749
|
-
}
|
|
2750
|
-
|
|
2751
|
-
uniqueIds.push(result[i].applianceId);
|
|
2752
|
-
|
|
2753
|
-
// check the maximum length of capabilities
|
|
2754
|
-
if (result[i].actions.length > 100) {
|
|
2755
|
-
adapter.log.error(`[ALEXA] Too many actions by "${result[i].applianceId}": ${result[i].actions.length}`);
|
|
2756
|
-
result.splice(i, 1);
|
|
2757
|
-
}
|
|
2758
|
-
}
|
|
2759
|
-
|
|
2760
|
-
// Check the maximum number of devices
|
|
2761
|
-
if (result.length > 300) {
|
|
2762
|
-
adapter.log.error(`[ALEXA] Too many devices: ${result.length}`);
|
|
2763
|
-
result.splice(300);
|
|
2764
|
-
}
|
|
2765
|
-
|
|
2766
|
-
while (JSON.stringify(result).length > 255 * 1024) {
|
|
2767
|
-
// Find the biggest one
|
|
2768
|
-
let i = -1;
|
|
2769
|
-
let size = 0;
|
|
2770
|
-
for (let j = 0; j < result.length; j++) {
|
|
2771
|
-
const s = JSON.stringify(result[j]).length;
|
|
2772
|
-
if (s > size) {
|
|
2773
|
-
size = s;
|
|
2774
|
-
i = j;
|
|
2775
|
-
}
|
|
2776
|
-
}
|
|
2777
|
-
|
|
2778
|
-
if (i === -1) {
|
|
2779
|
-
break;
|
|
2780
|
-
}
|
|
2781
|
-
adapter.log.warn(`[ALEXA] Too big response. Device "${result[i].applianceId}" will be removed`);
|
|
2782
|
-
// Check the maximum size of the response
|
|
2783
|
-
result.splice(i, 1);
|
|
2784
|
-
}
|
|
2785
|
-
|
|
2786
|
-
callback(err, result, analyseAddedId);
|
|
2787
|
-
});
|
|
2788
|
-
});
|
|
2789
|
-
};
|
|
2790
|
-
}
|
|
2791
|
-
|
|
2792
|
-
module.exports = AlexaSH2;
|