iobroker.iot 4.0.1 → 4.0.2

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.
Files changed (199) hide show
  1. package/README.md +1 -1
  2. package/admin/assets/index-5zouxn2w.js +680 -0
  3. package/admin/index_m.html +1 -1
  4. package/admin/rules/assets/{ActionVisu-DE8dgNVF.js → ActionVisu-D9x2f6zu.js} +1 -1
  5. package/admin/rules/assets/{ActionVisu__loadShare__react__loadShare__-BKaC-p5K.js → ActionVisu__loadShare__react__loadShare__-Cg21Ef1U.js} +1 -1
  6. package/admin/rules/assets/{ActionVisu__loadShare__react_mf_2_dom__loadShare__-Bs1o8DtQ.js → ActionVisu__loadShare__react_mf_2_dom__loadShare__-B1zRWn0u.js} +2 -2
  7. package/admin/rules/assets/ActionVisu__mf_v__runtimeInit__mf_v__-Cz053nO7.js +10 -0
  8. package/admin/rules/assets/bootstrap-BCFKIiwZ.js +1 -0
  9. package/admin/rules/assets/hostInit-hos3Txsy.js +1 -1
  10. package/admin/rules/assets/{index-DjPMvPni.js → index-CAAeiKCc.js} +2 -2
  11. package/admin/rules/assets/{index-CbxmbJvB.js → index-Duo7mFlZ.js} +2 -2
  12. package/admin/rules/assets/index-DwYdaT5M.js +1151 -0
  13. package/admin/rules/assets/{virtualExposes-BNZJTatg.js → virtualExposes-BqVak7rX.js} +2 -2
  14. package/admin/rules/customRuleBlocks.js +2 -2
  15. package/build-backend/lib/AlexaSmartHomeV3/Alexa/AlexaResponse.js +268 -0
  16. package/build-backend/lib/AlexaSmartHomeV3/Alexa/AlexaResponse.js.map +1 -0
  17. package/build-backend/lib/AlexaSmartHomeV3/Alexa/Capabilities/Base.js +110 -0
  18. package/build-backend/lib/AlexaSmartHomeV3/Alexa/Capabilities/Base.js.map +1 -0
  19. package/build-backend/lib/AlexaSmartHomeV3/Alexa/Capabilities/BrightnessController.js +19 -0
  20. package/build-backend/lib/AlexaSmartHomeV3/Alexa/Capabilities/BrightnessController.js.map +1 -0
  21. package/build-backend/lib/AlexaSmartHomeV3/Alexa/Capabilities/ColorController.js +14 -0
  22. package/build-backend/lib/AlexaSmartHomeV3/Alexa/Capabilities/ColorController.js.map +1 -0
  23. package/build-backend/lib/AlexaSmartHomeV3/Alexa/Capabilities/ColorTemperatureController.js +19 -0
  24. package/build-backend/lib/AlexaSmartHomeV3/Alexa/Capabilities/ColorTemperatureController.js.map +1 -0
  25. package/build-backend/lib/AlexaSmartHomeV3/Alexa/Capabilities/ContactSensor.js +16 -0
  26. package/build-backend/lib/AlexaSmartHomeV3/Alexa/Capabilities/ContactSensor.js.map +1 -0
  27. package/build-backend/lib/AlexaSmartHomeV3/Alexa/Capabilities/LockController.js +14 -0
  28. package/build-backend/lib/AlexaSmartHomeV3/Alexa/Capabilities/LockController.js.map +1 -0
  29. package/build-backend/lib/AlexaSmartHomeV3/Alexa/Capabilities/ModeController.js +68 -0
  30. package/build-backend/lib/AlexaSmartHomeV3/Alexa/Capabilities/ModeController.js.map +1 -0
  31. package/build-backend/lib/AlexaSmartHomeV3/Alexa/Capabilities/MotionSensor.js +14 -0
  32. package/build-backend/lib/AlexaSmartHomeV3/Alexa/Capabilities/MotionSensor.js.map +1 -0
  33. package/build-backend/lib/AlexaSmartHomeV3/Alexa/Capabilities/PercentageController.js +14 -0
  34. package/build-backend/lib/AlexaSmartHomeV3/Alexa/Capabilities/PercentageController.js.map +1 -0
  35. package/build-backend/lib/AlexaSmartHomeV3/Alexa/Capabilities/PowerController.js +19 -0
  36. package/build-backend/lib/AlexaSmartHomeV3/Alexa/Capabilities/PowerController.js.map +1 -0
  37. package/build-backend/lib/AlexaSmartHomeV3/Alexa/Capabilities/Speaker.js +25 -0
  38. package/build-backend/lib/AlexaSmartHomeV3/Alexa/Capabilities/Speaker.js.map +1 -0
  39. package/build-backend/lib/AlexaSmartHomeV3/Alexa/Capabilities/TemperatureSensor.js +14 -0
  40. package/build-backend/lib/AlexaSmartHomeV3/Alexa/Capabilities/TemperatureSensor.js.map +1 -0
  41. package/build-backend/lib/AlexaSmartHomeV3/Alexa/Capabilities/ThermostatController.js +37 -0
  42. package/build-backend/lib/AlexaSmartHomeV3/Alexa/Capabilities/ThermostatController.js.map +1 -0
  43. package/build-backend/lib/AlexaSmartHomeV3/Alexa/Capabilities/index.js +32 -0
  44. package/build-backend/lib/AlexaSmartHomeV3/Alexa/Capabilities/index.js.map +1 -0
  45. package/build-backend/lib/AlexaSmartHomeV3/Alexa/Directives/Base.js +29 -0
  46. package/build-backend/lib/AlexaSmartHomeV3/Alexa/Directives/Base.js.map +1 -0
  47. package/build-backend/lib/AlexaSmartHomeV3/Alexa/Directives/ChangeReport.js +82 -0
  48. package/build-backend/lib/AlexaSmartHomeV3/Alexa/Directives/ChangeReport.js.map +1 -0
  49. package/build-backend/lib/AlexaSmartHomeV3/Alexa/Directives/Discovery.js +39 -0
  50. package/build-backend/lib/AlexaSmartHomeV3/Alexa/Directives/Discovery.js.map +1 -0
  51. package/build-backend/lib/AlexaSmartHomeV3/Alexa/Directives/ReportState.js +47 -0
  52. package/build-backend/lib/AlexaSmartHomeV3/Alexa/Directives/ReportState.js.map +1 -0
  53. package/build-backend/lib/AlexaSmartHomeV3/Alexa/Directives/index.js +14 -0
  54. package/build-backend/lib/AlexaSmartHomeV3/Alexa/Directives/index.js.map +1 -0
  55. package/build-backend/lib/AlexaSmartHomeV3/Alexa/ModeValues/Base.js +36 -0
  56. package/build-backend/lib/AlexaSmartHomeV3/Alexa/ModeValues/Base.js.map +1 -0
  57. package/build-backend/lib/AlexaSmartHomeV3/Alexa/ModeValues/Closed.js +57 -0
  58. package/build-backend/lib/AlexaSmartHomeV3/Alexa/ModeValues/Closed.js.map +1 -0
  59. package/build-backend/lib/AlexaSmartHomeV3/Alexa/ModeValues/Open.js +57 -0
  60. package/build-backend/lib/AlexaSmartHomeV3/Alexa/ModeValues/Open.js.map +1 -0
  61. package/build-backend/lib/AlexaSmartHomeV3/Alexa/ModeValues/index.js +12 -0
  62. package/build-backend/lib/AlexaSmartHomeV3/Alexa/ModeValues/index.js.map +1 -0
  63. package/build-backend/lib/AlexaSmartHomeV3/Alexa/Properties/AdjustableProperty.js +16 -0
  64. package/build-backend/lib/AlexaSmartHomeV3/Alexa/Properties/AdjustableProperty.js.map +1 -0
  65. package/build-backend/lib/AlexaSmartHomeV3/Alexa/Properties/Base.js +127 -0
  66. package/build-backend/lib/AlexaSmartHomeV3/Alexa/Properties/Base.js.map +1 -0
  67. package/build-backend/lib/AlexaSmartHomeV3/Alexa/Properties/Brightness.js +18 -0
  68. package/build-backend/lib/AlexaSmartHomeV3/Alexa/Properties/Brightness.js.map +1 -0
  69. package/build-backend/lib/AlexaSmartHomeV3/Alexa/Properties/Color.js +38 -0
  70. package/build-backend/lib/AlexaSmartHomeV3/Alexa/Properties/Color.js.map +1 -0
  71. package/build-backend/lib/AlexaSmartHomeV3/Alexa/Properties/ColorTemperatureInKelvin.js +53 -0
  72. package/build-backend/lib/AlexaSmartHomeV3/Alexa/Properties/ColorTemperatureInKelvin.js.map +1 -0
  73. package/build-backend/lib/AlexaSmartHomeV3/Alexa/Properties/DetectionState.js +23 -0
  74. package/build-backend/lib/AlexaSmartHomeV3/Alexa/Properties/DetectionState.js.map +1 -0
  75. package/build-backend/lib/AlexaSmartHomeV3/Alexa/Properties/LockState.js +37 -0
  76. package/build-backend/lib/AlexaSmartHomeV3/Alexa/Properties/LockState.js.map +1 -0
  77. package/build-backend/lib/AlexaSmartHomeV3/Alexa/Properties/Mode.js +34 -0
  78. package/build-backend/lib/AlexaSmartHomeV3/Alexa/Properties/Mode.js.map +1 -0
  79. package/build-backend/lib/AlexaSmartHomeV3/Alexa/Properties/Muted.js +19 -0
  80. package/build-backend/lib/AlexaSmartHomeV3/Alexa/Properties/Muted.js.map +1 -0
  81. package/build-backend/lib/AlexaSmartHomeV3/Alexa/Properties/Percentage.js +18 -0
  82. package/build-backend/lib/AlexaSmartHomeV3/Alexa/Properties/Percentage.js.map +1 -0
  83. package/build-backend/lib/AlexaSmartHomeV3/Alexa/Properties/PowerState.js +25 -0
  84. package/build-backend/lib/AlexaSmartHomeV3/Alexa/Properties/PowerState.js.map +1 -0
  85. package/build-backend/lib/AlexaSmartHomeV3/Alexa/Properties/TargetSetpoint.js +31 -0
  86. package/build-backend/lib/AlexaSmartHomeV3/Alexa/Properties/TargetSetpoint.js.map +1 -0
  87. package/build-backend/lib/AlexaSmartHomeV3/Alexa/Properties/Temperature.js +22 -0
  88. package/build-backend/lib/AlexaSmartHomeV3/Alexa/Properties/Temperature.js.map +1 -0
  89. package/build-backend/lib/AlexaSmartHomeV3/Alexa/Properties/ThermostatMode.js +49 -0
  90. package/build-backend/lib/AlexaSmartHomeV3/Alexa/Properties/ThermostatMode.js.map +1 -0
  91. package/build-backend/lib/AlexaSmartHomeV3/Alexa/Properties/Volume.js +21 -0
  92. package/build-backend/lib/AlexaSmartHomeV3/Alexa/Properties/Volume.js.map +1 -0
  93. package/build-backend/lib/AlexaSmartHomeV3/Alexa/Properties/index.js +38 -0
  94. package/build-backend/lib/AlexaSmartHomeV3/Alexa/Properties/index.js.map +1 -0
  95. package/build-backend/lib/AlexaSmartHomeV3/Controls/AdjustableControl.js +20 -0
  96. package/build-backend/lib/AlexaSmartHomeV3/Controls/AdjustableControl.js.map +1 -0
  97. package/build-backend/lib/AlexaSmartHomeV3/Controls/AdjustablePercentageControl.js +26 -0
  98. package/build-backend/lib/AlexaSmartHomeV3/Controls/AdjustablePercentageControl.js.map +1 -0
  99. package/build-backend/lib/AlexaSmartHomeV3/Controls/AirCondition.js +148 -0
  100. package/build-backend/lib/AlexaSmartHomeV3/Controls/AirCondition.js.map +1 -0
  101. package/build-backend/lib/AlexaSmartHomeV3/Controls/Blind.js +13 -0
  102. package/build-backend/lib/AlexaSmartHomeV3/Controls/Blind.js.map +1 -0
  103. package/build-backend/lib/AlexaSmartHomeV3/Controls/ContactSensor.js +17 -0
  104. package/build-backend/lib/AlexaSmartHomeV3/Controls/ContactSensor.js.map +1 -0
  105. package/build-backend/lib/AlexaSmartHomeV3/Controls/Control.js +321 -0
  106. package/build-backend/lib/AlexaSmartHomeV3/Controls/Control.js.map +1 -0
  107. package/build-backend/lib/AlexaSmartHomeV3/Controls/Ct.js +182 -0
  108. package/build-backend/lib/AlexaSmartHomeV3/Controls/Ct.js.map +1 -0
  109. package/build-backend/lib/AlexaSmartHomeV3/Controls/Dimmer.js +127 -0
  110. package/build-backend/lib/AlexaSmartHomeV3/Controls/Dimmer.js.map +1 -0
  111. package/build-backend/lib/AlexaSmartHomeV3/Controls/Door.js +10 -0
  112. package/build-backend/lib/AlexaSmartHomeV3/Controls/Door.js.map +1 -0
  113. package/build-backend/lib/AlexaSmartHomeV3/Controls/Gate.js +38 -0
  114. package/build-backend/lib/AlexaSmartHomeV3/Controls/Gate.js.map +1 -0
  115. package/build-backend/lib/AlexaSmartHomeV3/Controls/Hue.js +208 -0
  116. package/build-backend/lib/AlexaSmartHomeV3/Controls/Hue.js.map +1 -0
  117. package/build-backend/lib/AlexaSmartHomeV3/Controls/Light.js +47 -0
  118. package/build-backend/lib/AlexaSmartHomeV3/Controls/Light.js.map +1 -0
  119. package/build-backend/lib/AlexaSmartHomeV3/Controls/Lock.js +35 -0
  120. package/build-backend/lib/AlexaSmartHomeV3/Controls/Lock.js.map +1 -0
  121. package/build-backend/lib/AlexaSmartHomeV3/Controls/Motion.js +17 -0
  122. package/build-backend/lib/AlexaSmartHomeV3/Controls/Motion.js.map +1 -0
  123. package/build-backend/lib/AlexaSmartHomeV3/Controls/ReadOnlyDetector.js +26 -0
  124. package/build-backend/lib/AlexaSmartHomeV3/Controls/ReadOnlyDetector.js.map +1 -0
  125. package/build-backend/lib/AlexaSmartHomeV3/Controls/Slider.js +10 -0
  126. package/build-backend/lib/AlexaSmartHomeV3/Controls/Slider.js.map +1 -0
  127. package/build-backend/lib/AlexaSmartHomeV3/Controls/Socket.js +21 -0
  128. package/build-backend/lib/AlexaSmartHomeV3/Controls/Socket.js.map +1 -0
  129. package/build-backend/lib/AlexaSmartHomeV3/Controls/Temperature.js +21 -0
  130. package/build-backend/lib/AlexaSmartHomeV3/Controls/Temperature.js.map +1 -0
  131. package/build-backend/lib/AlexaSmartHomeV3/Controls/Thermostat.js +85 -0
  132. package/build-backend/lib/AlexaSmartHomeV3/Controls/Thermostat.js.map +1 -0
  133. package/build-backend/lib/AlexaSmartHomeV3/Controls/VacuumCleaner.js +27 -0
  134. package/build-backend/lib/AlexaSmartHomeV3/Controls/VacuumCleaner.js.map +1 -0
  135. package/build-backend/lib/AlexaSmartHomeV3/Controls/Volume.js +116 -0
  136. package/build-backend/lib/AlexaSmartHomeV3/Controls/Volume.js.map +1 -0
  137. package/build-backend/lib/AlexaSmartHomeV3/Controls/VolumeGroup.js +10 -0
  138. package/build-backend/lib/AlexaSmartHomeV3/Controls/VolumeGroup.js.map +1 -0
  139. package/build-backend/lib/AlexaSmartHomeV3/Controls/Window.js +10 -0
  140. package/build-backend/lib/AlexaSmartHomeV3/Controls/Window.js.map +1 -0
  141. package/build-backend/lib/AlexaSmartHomeV3/Controls/index.js +121 -0
  142. package/build-backend/lib/AlexaSmartHomeV3/Controls/index.js.map +1 -0
  143. package/build-backend/lib/AlexaSmartHomeV3/Device.js +83 -0
  144. package/build-backend/lib/AlexaSmartHomeV3/Device.js.map +1 -0
  145. package/build-backend/lib/AlexaSmartHomeV3/DeviceManager.js +392 -0
  146. package/build-backend/lib/AlexaSmartHomeV3/DeviceManager.js.map +1 -0
  147. package/build-backend/lib/AlexaSmartHomeV3/Exceptions/AlexaV3Exception.js +10 -0
  148. package/build-backend/lib/AlexaSmartHomeV3/Exceptions/AlexaV3Exception.js.map +1 -0
  149. package/build-backend/lib/AlexaSmartHomeV3/Exceptions/HourlyDeviceRateLimitExceeded.js +10 -0
  150. package/build-backend/lib/AlexaSmartHomeV3/Exceptions/HourlyDeviceRateLimitExceeded.js.map +1 -0
  151. package/build-backend/lib/AlexaSmartHomeV3/Exceptions/OverallDailyRateLimitExceeded.js +10 -0
  152. package/build-backend/lib/AlexaSmartHomeV3/Exceptions/OverallDailyRateLimitExceeded.js.map +1 -0
  153. package/build-backend/lib/AlexaSmartHomeV3/Helpers/AdapterProvider.js +37 -0
  154. package/build-backend/lib/AlexaSmartHomeV3/Helpers/AdapterProvider.js.map +1 -0
  155. package/build-backend/lib/AlexaSmartHomeV3/Helpers/FileHelper.js +53 -0
  156. package/build-backend/lib/AlexaSmartHomeV3/Helpers/FileHelper.js.map +1 -0
  157. package/build-backend/lib/AlexaSmartHomeV3/Helpers/IotProxy.js +27 -0
  158. package/build-backend/lib/AlexaSmartHomeV3/Helpers/IotProxy.js.map +1 -0
  159. package/build-backend/lib/AlexaSmartHomeV3/Helpers/Logger.js +93 -0
  160. package/build-backend/lib/AlexaSmartHomeV3/Helpers/Logger.js.map +1 -0
  161. package/build-backend/lib/AlexaSmartHomeV3/Helpers/RateLimiter.js +112 -0
  162. package/build-backend/lib/AlexaSmartHomeV3/Helpers/RateLimiter.js.map +1 -0
  163. package/build-backend/lib/AlexaSmartHomeV3/Helpers/Utils.js +625 -0
  164. package/build-backend/lib/AlexaSmartHomeV3/Helpers/Utils.js.map +1 -0
  165. package/build-backend/lib/Utils.js +96 -0
  166. package/build-backend/lib/Utils.js.map +1 -0
  167. package/build-backend/lib/adminCommonSocket.js +715 -0
  168. package/build-backend/lib/adminCommonSocket.js.map +1 -0
  169. package/build-backend/lib/alexaCustom.js +542 -0
  170. package/build-backend/lib/alexaCustom.js.map +1 -0
  171. package/build-backend/lib/alexaSmartHomeV2.js +2788 -0
  172. package/build-backend/lib/alexaSmartHomeV3.js +97 -0
  173. package/build-backend/lib/alexaSmartHomeV3.js.map +1 -0
  174. package/build-backend/lib/alisa.js +2409 -0
  175. package/build-backend/lib/devices.js +23 -0
  176. package/build-backend/lib/devices.js.map +1 -0
  177. package/build-backend/lib/functions.js +24 -0
  178. package/build-backend/lib/functions.js.map +1 -0
  179. package/build-backend/lib/googleHome.js +3322 -0
  180. package/build-backend/lib/notifications.js +30 -0
  181. package/build-backend/lib/notifications.js.map +1 -0
  182. package/build-backend/lib/remote.js +1342 -0
  183. package/build-backend/lib/remote.js.map +1 -0
  184. package/build-backend/lib/rooms.js +48 -0
  185. package/build-backend/lib/rooms.js.map +1 -0
  186. package/build-backend/lib/texts.js +81 -0
  187. package/build-backend/lib/texts.js.map +1 -0
  188. package/build-backend/lib/translate.js +21 -0
  189. package/build-backend/lib/translate.js.map +1 -0
  190. package/build-backend/lib/visuApp.js +147 -0
  191. package/build-backend/lib/visuApp.js.map +1 -0
  192. package/build-backend/main.js +1361 -0
  193. package/build-backend/main.js.map +1 -0
  194. package/io-package.json +2 -2
  195. package/package.json +2 -2
  196. package/admin/assets/index-Y479CE6x.js +0 -681
  197. package/admin/rules/assets/ActionVisu__mf_v__runtimeInit__mf_v__-DtUoI3FU.js +0 -5
  198. package/admin/rules/assets/bootstrap-Ba57tu25.js +0 -1
  199. package/admin/rules/assets/index-_Y2FN5EZ.js +0 -1152
@@ -0,0 +1,2409 @@
1
+ const ChannelDetector = require('@iobroker/type-detector');
2
+ const axios = require('axios');
3
+ const uuid = require('uuid').v1;
4
+
5
+ const textsT = require('./texts').default;
6
+ const roomsT = require('./rooms').default;
7
+ const funcsT = require('./functions').default;
8
+
9
+ // Description
10
+ // ??
11
+
12
+ const ignoreIds = [/^system\./, /^script\./];
13
+
14
+ function replaceInvalidChars(name) {
15
+ name = name.replace(/[^a-zA-Z0-9А-Яа-я_]/g, '_');
16
+ name = name.replace(/Ü/g, 'UE');
17
+ name = name.replace(/Ä/g, 'AE');
18
+ name = name.replace(/Ö/g, 'OE');
19
+ name = name.replace(/ü/g, 'ue');
20
+ name = name.replace(/ä/g, 'ae');
21
+ name = name.replace(/ö/g, 'oe');
22
+ name = name.replace(/ß/g, 'ss');
23
+ return name;
24
+ }
25
+
26
+ const typesMapping = {
27
+ on_off: 'OnOff',
28
+ color_setting: 'RGB',
29
+ 'range_unit.percent': 'Brightness',
30
+ 'range_unit.temperature.celsius': 'setTargetTemperature',
31
+ 'float_temperature_unit.temperature.celsius': 'getActualTemperature',
32
+ 'float_temperature_unit.temperature.kelvin': 'getActualTemperature',
33
+ 'float_humidity_unit.percent': 'getActualHumidity',
34
+ bool_motion: 'getMotion',
35
+ bool_censor: 'getContact',
36
+ };
37
+
38
+ // const URL_STATUS = 'https://alisastatus.iobroker.in/v1/alisaStatus';
39
+ const URL_STATUS = 'https://20k0wcmzs4.execute-api.eu-west-1.amazonaws.com/default/alisaStatus';
40
+ const PROTOCOL_VERSION = 1;
41
+ const RETRY_UNKNOWN_DEVICES_INTERVAL = 10 * 60000; // 10 minutes
42
+
43
+ class YandexAliceConverter {
44
+ constructor(adapter) {
45
+ this.adapter = adapter;
46
+ this.lang = 'ru';
47
+
48
+ this.types = {
49
+ [ChannelDetector.Types.socket]: this._processSocket.bind(this),
50
+ [ChannelDetector.Types.light]: this._processLight.bind(this),
51
+ [ChannelDetector.Types.dimmer]: this._processDimmer.bind(this),
52
+ [ChannelDetector.Types.ct]: this._processCT.bind(this),
53
+ [ChannelDetector.Types.rgbSingle]: this._processRGB.bind(this),
54
+ [ChannelDetector.Types.airCondition]: this._processAC.bind(this),
55
+ [ChannelDetector.Types.thermostat]: this._processAC.bind(this),
56
+ [ChannelDetector.Types.blind]: this._processBlinds.bind(this),
57
+ [ChannelDetector.Types.lock]: this._processLock.bind(this),
58
+ [ChannelDetector.Types.vacuumCleaner]: this._processVacuumCleaner.bind(this),
59
+ [ChannelDetector.Types.gate]: this._processLock.bind(this),
60
+ // sensors
61
+ [ChannelDetector.Types.motion]: this._processMotion.bind(this),
62
+ [ChannelDetector.Types.door]: this._processContact.bind(this),
63
+ [ChannelDetector.Types.window]: this._processContact.bind(this),
64
+ [ChannelDetector.Types.temperature]: this._processTemperature.bind(this),
65
+ [ChannelDetector.Types.humidity]: this._processHumidity.bind(this),
66
+ [ChannelDetector.Types.buttonSensor]: this._processButtonSensor.bind(this),
67
+ };
68
+ this._entities = [];
69
+ this._entity2ID = {};
70
+ this._ID2entity = {};
71
+ }
72
+
73
+ setLanguage(lang) {
74
+ this.lang = lang || 'ru';
75
+ }
76
+
77
+ _getSmartName(obj) {
78
+ if (!this.adapter.config.noCommon) {
79
+ return obj?.common ? obj.common.smartName || '' : '';
80
+ }
81
+ return obj?.common?.custom?.[this.adapter.namespace] || ''
82
+ ? obj.common.custom[this.adapter.namespace].smartName
83
+ : '';
84
+ }
85
+
86
+ _getObjectName(obj, _lang) {
87
+ _lang = _lang || this.lang;
88
+
89
+ let result = this._getSmartName(obj);
90
+
91
+ if (!result || (typeof result !== 'object' && typeof result !== 'string')) {
92
+ result = obj?.common ? obj.common.name : null;
93
+ result ||= obj._id;
94
+ }
95
+
96
+ if (typeof result === 'object') {
97
+ if (result[_lang] || result.en) {
98
+ return result[_lang] || result.en;
99
+ }
100
+ // take first not empty value
101
+ const lang = Object.keys(result).find(lang => result[lang]);
102
+ if (result[lang]) {
103
+ return result[lang];
104
+ }
105
+
106
+ return obj._id;
107
+ }
108
+
109
+ return result || '';
110
+ }
111
+
112
+ _generateName(obj, lang) {
113
+ return this._getObjectName(obj, lang).replace(/[^-._\w0-9А-Яа-яÄÜÖßäöü]/g, '_');
114
+ }
115
+
116
+ _addID2entity(id, entity) {
117
+ this._ID2entity[id] = this._ID2entity[id] || [];
118
+ const found = this._ID2entity[id].find(e => e.entity_id === entity.entity_id);
119
+ if (!found) {
120
+ this._ID2entity[id].push(entity);
121
+ }
122
+ }
123
+
124
+ // ------------------------------- START OF CONVERTERS ---------------------------------------- //
125
+ _getStateBooleanProperty(entity, name, mapping) {
126
+ return new Promise(resolve => {
127
+ const attribute = entity.ATTRIBUTES.find(attr => attr.attribute === name);
128
+ const stateId = attribute ? attribute.getId || attribute.setId : undefined;
129
+ entity.context.capabilities = [];
130
+ const capability = entity.context.properties.find(
131
+ cap => cap.type === 'devices.properties.event' && cap.parameters.instance === name,
132
+ );
133
+ if (capability && stateId) {
134
+ this.adapter.getForeignState(stateId, (err, state) => {
135
+ if (!err && state) {
136
+ capability.state = {
137
+ instance: name,
138
+ value: mapping[
139
+ state.val === 'false' || state.val === '0'
140
+ ? false
141
+ : state.val === 'true' ||
142
+ state.val === true ||
143
+ state.val === 1 ||
144
+ state.val === '1' ||
145
+ !!state.val
146
+ ],
147
+ };
148
+ }
149
+ resolve();
150
+ });
151
+ } else {
152
+ resolve();
153
+ }
154
+ });
155
+ }
156
+
157
+ _getStateFloatProperty(entity, name) {
158
+ return new Promise(resolve => {
159
+ const attribute = entity.ATTRIBUTES.find(attr => attr.attribute === name);
160
+ const stateId = attribute ? attribute.getId || attribute.setId : undefined;
161
+ const capability = entity.context.properties.find(
162
+ cap => cap.type === 'devices.properties.float' && cap.parameters.instance === name,
163
+ );
164
+ if (capability && stateId) {
165
+ this.adapter.getForeignState(stateId, (err, state) => {
166
+ if (!err && state) {
167
+ capability.state = {
168
+ instance: name,
169
+ value: parseFloat(state.val) || 0,
170
+ };
171
+ }
172
+ resolve();
173
+ });
174
+ } else {
175
+ resolve();
176
+ }
177
+ });
178
+ }
179
+
180
+ _getEventStringProperty(entity, name, mapping) {
181
+ return new Promise(resolve => {
182
+ const attribute = entity.ATTRIBUTES.find(attr => attr.attribute === name);
183
+ const stateId = attribute ? attribute.getId || attribute.setId : undefined;
184
+ entity.context.capabilities = [];
185
+ const capability = entity.context.properties.find(
186
+ cap => cap.type === 'devices.properties.event' && cap.parameters.instance === name,
187
+ );
188
+ if (capability && stateId) {
189
+ this.adapter.getForeignState(stateId, (err, state) => {
190
+ if (!err && state) {
191
+ capability.state = {
192
+ instance: name,
193
+ value: mapping ? (mapping[state.val] || '').toLowerCase() : state.val,
194
+ };
195
+ }
196
+ resolve();
197
+ });
198
+ } else {
199
+ resolve();
200
+ }
201
+ });
202
+ }
203
+
204
+ _getStateNumber(entity, type, name) {
205
+ return new Promise(resolve => {
206
+ const attribute = entity.ATTRIBUTES.find(attr => attr.attribute === name);
207
+ const stateId = attribute ? attribute.getId || attribute.setId : undefined;
208
+ const capability = entity.context.capabilities.find(
209
+ cap => cap.type === type && cap.parameters.instance === name,
210
+ );
211
+ if (capability && stateId) {
212
+ this.adapter.getForeignState(stateId, (err, state) => {
213
+ if (!err && state) {
214
+ capability.state = {
215
+ instance: name,
216
+ value: parseFloat(state.val) || 0,
217
+ };
218
+ }
219
+ resolve();
220
+ });
221
+ } else {
222
+ resolve();
223
+ }
224
+ });
225
+ }
226
+
227
+ _setStateNumber(entity, data, type, name) {
228
+ return new Promise(resolve => {
229
+ const attribute = entity.ATTRIBUTES.find(attr => attr.attribute === name);
230
+ const stateId = attribute ? attribute.setId || attribute.getId : undefined;
231
+ const capability = data.capabilities.find(cap => cap.type === type && cap.state.instance === name);
232
+ if (capability && capability.state && stateId) {
233
+ if (capability.state.relative) {
234
+ return this.adapter.getForeignState(stateId, (err, state) => {
235
+ if (state) {
236
+ const val = (parseFloat(state.val) || 0) + parseFloat(capability.state.value);
237
+ this.adapter.setForeignState(stateId, val);
238
+ capability.state.action_result = { status: 'DONE' };
239
+ resolve(data);
240
+ } else {
241
+ // https://yandex.ru/dev/dialogs/alice/doc/smart-home/concepts/response-codes-docpage/#codes__codes-api
242
+ capability.state.action_result = {
243
+ status: 'ERROR',
244
+ error_code: 'INVALID_VALUE',
245
+ error_message: 'State has invalid value',
246
+ };
247
+ resolve(data);
248
+ }
249
+ });
250
+ } else {
251
+ this.adapter.setForeignState(stateId, parseFloat(capability.state.value));
252
+ capability.state.action_result = { status: 'DONE' };
253
+ }
254
+ }
255
+ resolve(data);
256
+ });
257
+ }
258
+
259
+ _getStateMode(entity, name) {
260
+ return new Promise(resolve => {
261
+ const attribute = entity.ATTRIBUTES.find(attr => attr.attribute === name);
262
+ const stateId = attribute ? attribute.getId || attribute.setId : undefined;
263
+ const capability = entity.context.capabilities.find(
264
+ cap => cap.type === 'devices.capabilities.mode' && cap.parameters.instance === name,
265
+ );
266
+ if (capability && stateId) {
267
+ this.adapter.getForeignState(stateId, (err, state) => {
268
+ if (!err && state) {
269
+ capability.state = {
270
+ instance: name,
271
+ value: attribute.states ? (attribute.states[state.val] || '').toLowerCase() : state.val,
272
+ };
273
+ }
274
+ resolve();
275
+ });
276
+ } else {
277
+ resolve();
278
+ }
279
+ });
280
+ }
281
+
282
+ _setStateMode(entity, data, name) {
283
+ return new Promise(resolve => {
284
+ const attribute = entity.ATTRIBUTES.find(attr => attr.attribute === name);
285
+ const stateId = attribute ? attribute.setId || attribute.getId : undefined;
286
+ const capability = data.capabilities.find(
287
+ cap => cap.type === 'devices.capabilities.mode' && cap.state.instance === name,
288
+ );
289
+ if (capability && capability.state && stateId) {
290
+ let val = attribute.states
291
+ ? Object.keys(attribute.states).find(
292
+ val => attribute.states[val].toLowerCase() === capability.state.value.toLowerCase(),
293
+ )
294
+ : capability.state.value;
295
+ if (attribute.states && attribute.type === 'number') {
296
+ val = parseInt(val, 10);
297
+ }
298
+ this.adapter.setForeignState(stateId, val);
299
+ capability.state.action_result = { status: 'DONE' };
300
+ }
301
+ resolve(data);
302
+ });
303
+ }
304
+
305
+ _getStateRange(entity, name) {
306
+ return this._getStateNumber(entity, 'devices.capabilities.range', name);
307
+ }
308
+
309
+ _setStateRange(entity, data, name) {
310
+ return this._setStateNumber(entity, data, 'devices.capabilities.range', name);
311
+ }
312
+
313
+ _getStateOnOff(entity) {
314
+ return new Promise(resolve => {
315
+ const stateId = entity.STATE.getId;
316
+ const capability = entity.context.capabilities.find(cap => cap.type === 'devices.capabilities.on_off');
317
+ if (capability && stateId) {
318
+ this.adapter.getForeignState(stateId, (err, state) => {
319
+ if (!err && state) {
320
+ capability.state = {
321
+ instance: 'on',
322
+ value:
323
+ entity.STATE.min !== undefined && entity.STATE.min === state.val
324
+ ? false
325
+ : entity.STATE.max !== undefined && entity.STATE.max === state.val
326
+ ? true
327
+ : !!state.val,
328
+ };
329
+ }
330
+ resolve();
331
+ });
332
+ } else {
333
+ resolve();
334
+ }
335
+ });
336
+ }
337
+
338
+ _setStateOnOff(entity, data) {
339
+ return new Promise(resolve => {
340
+ const stateId = entity.STATE.setId;
341
+ const capability = data.capabilities.find(cap => cap.type === 'devices.capabilities.on_off');
342
+ if (capability && capability.state && stateId) {
343
+ if (entity.STATE.type === 'number') {
344
+ const value = capability.state.value
345
+ ? entity.STATE.max !== undefined
346
+ ? entity.STATE.max
347
+ : 100
348
+ : entity.STATE.min !== undefined
349
+ ? entity.STATE.min
350
+ : 0;
351
+ this.adapter.setForeignState(stateId, value);
352
+ // If number has the same ID as ON/OFF => set in the same answer the new state for dimmer/blind/...
353
+ /*const numberState = entity.ATTRIBUTES.find(item => item.getId === stateId)
354
+ if (numberState) {
355
+ let capability = data.capabilities.find(cap => cap.type === numberState.type);
356
+ if (!capability) {
357
+ data.capabilities.push({
358
+ type: 'devices.capabilities.range',
359
+ state: {
360
+ instance: numberState.attribute,
361
+ value: value
362
+ }
363
+ });
364
+ }
365
+ }*/
366
+ } else {
367
+ this.adapter.setForeignState(stateId, capability.state.value);
368
+ }
369
+ capability.state.action_result = { status: 'DONE' };
370
+ }
371
+ resolve(data);
372
+ });
373
+ }
374
+
375
+ _setStateLock(entity, data) {
376
+ return new Promise(resolve => {
377
+ const stateId = entity.STATE.setId;
378
+ const capability = data.capabilities.find(cap => cap.type === 'devices.capabilities.on_off');
379
+
380
+ if (capability && capability.state && stateId) {
381
+ if (capability.state.value && entity.STATE.openId) {
382
+ this.adapter.setForeignState(entity.STATE.openId, capability.state.value);
383
+ } else {
384
+ this.adapter.setForeignState(stateId, capability.state.value);
385
+ }
386
+ capability.state.action_result = { status: 'DONE' };
387
+ }
388
+ resolve(data);
389
+ });
390
+ }
391
+
392
+ _getStateBrightness(entity) {
393
+ return this._getStateRange(entity, 'brightness');
394
+ }
395
+
396
+ _setStateBrightness(entity, data) {
397
+ return this._setStateRange(entity, data, 'brightness');
398
+ }
399
+
400
+ _getStateCT(entity) {
401
+ return new Promise(resolve => {
402
+ const attribute = entity.ATTRIBUTES.find(attr => attr.attribute === 'ct');
403
+ const stateId = attribute ? attribute.getId : undefined;
404
+ const capability = entity.context.capabilities.find(
405
+ cap => cap.type === 'devices.capabilities.color_setting',
406
+ );
407
+ if (capability && stateId) {
408
+ this.adapter.getForeignState(stateId, (err, state) => {
409
+ if (!err && state) {
410
+ let val = parseInt(state.val);
411
+ // if (val) {
412
+ // val = 1000000/val;
413
+ // }
414
+ capability.state = {
415
+ instance: 'temperature_k',
416
+ value: val,
417
+ };
418
+ }
419
+ resolve();
420
+ });
421
+ } else {
422
+ resolve();
423
+ }
424
+ });
425
+ }
426
+
427
+ _setStateCT(entity, data) {
428
+ return new Promise(resolve => {
429
+ const attribute = entity.ATTRIBUTES.find(attr => attr.attribute === 'ct');
430
+ const stateId = attribute ? attribute.getId : undefined;
431
+ const capability = data.capabilities.find(cap => cap.type === 'devices.capabilities.color_setting');
432
+ if (capability && capability.state && stateId && capability.state.instance === 'temperature_k') {
433
+ let val = parseInt(capability.state.value);
434
+ // if (val) {
435
+ // val = Math.round(val/1000000);
436
+ // }
437
+ this.adapter.setForeignState(stateId, val);
438
+ capability.state.action_result = { status: 'DONE' };
439
+ }
440
+ resolve(data);
441
+ });
442
+ }
443
+
444
+ _getStateRGB(entity) {
445
+ return new Promise(resolve => {
446
+ const attribute = entity.ATTRIBUTES.find(attr => attr.attribute === 'rgb');
447
+ const stateId = attribute ? attribute.getId : undefined;
448
+ const capability = entity.context.capabilities.find(
449
+ cap => cap.type === 'devices.capabilities.color_setting',
450
+ );
451
+ if (capability && stateId) {
452
+ this.adapter.getForeignState(stateId, (err, state) => {
453
+ if (!err && state) {
454
+ const val = state.val.replace('#', '');
455
+ capability.state = {
456
+ instance: 'rgb',
457
+ value: parseInt(val, 16),
458
+ };
459
+ }
460
+ resolve();
461
+ });
462
+ } else {
463
+ resolve();
464
+ }
465
+ });
466
+ }
467
+
468
+ _setStateRGB(entity, data) {
469
+ return new Promise(resolve => {
470
+ const attribute = entity.ATTRIBUTES.find(attr => attr.attribute === 'rgb');
471
+ const stateId = attribute ? attribute.getId : undefined;
472
+ const capability = data.capabilities.find(cap => cap.type === 'devices.capabilities.color_setting');
473
+ if (capability && capability.state && stateId && capability.state.instance === 'rgb') {
474
+ const val = capability.state.value.toString(16);
475
+ this.adapter.setForeignState(stateId, `#${val}`);
476
+ capability.state.action_result = { status: 'DONE' };
477
+ }
478
+ resolve(data);
479
+ });
480
+ }
481
+
482
+ _getStateTemperature(entity) {
483
+ return this._getStateRange(entity, 'temperature');
484
+ }
485
+
486
+ _setStateTemperature(entity, data) {
487
+ return this._setStateRange(entity, data, 'temperature');
488
+ }
489
+
490
+ _getStateThermostat(entity) {
491
+ return this._getStateMode(entity, 'thermostat');
492
+ }
493
+
494
+ _setStateThermostat(entity, data) {
495
+ return this._setStateMode(entity, data, 'thermostat');
496
+ }
497
+
498
+ _getStateSwing(entity) {
499
+ return this._getStateMode(entity, 'swing');
500
+ }
501
+
502
+ _setStateSwing(entity, data) {
503
+ return this._setStateMode(entity, data, 'swing');
504
+ }
505
+
506
+ _getStateSpeed(entity) {
507
+ return this._getStateMode(entity, 'fan_speed');
508
+ }
509
+
510
+ _setStateSpeed(entity, data) {
511
+ return this._setStateMode(entity, data, 'fan_speed');
512
+ }
513
+
514
+ _getStateBlinds(entity) {
515
+ return new Promise(resolve => {
516
+ const attribute = entity.ATTRIBUTES.find(attr => attr.attribute === 'open');
517
+ const stateId = attribute ? attribute.getId : undefined;
518
+ const capability = entity.context.capabilities.find(cap => cap.type === 'devices.capabilities.range');
519
+ if (capability && stateId) {
520
+ this.adapter.getForeignState(stateId, (err, state) => {
521
+ if (!err && state) {
522
+ capability.state = {
523
+ instance: 'open',
524
+ value: state.val === true ? 100 : state.val === false ? 0 : state.val,
525
+ };
526
+ }
527
+ resolve();
528
+ });
529
+ } else {
530
+ resolve();
531
+ }
532
+ });
533
+ }
534
+
535
+ _setStateBlinds(entity, data) {
536
+ return new Promise(resolve => {
537
+ const attribute = entity.ATTRIBUTES.find(attr => attr.attribute === 'open');
538
+ const stateId = attribute ? attribute.setId || attribute.getId : undefined;
539
+ const capability = data.capabilities.find(cap => cap.type === 'devices.capabilities.range');
540
+ if (capability && capability.state && stateId) {
541
+ this.adapter.setForeignState(stateId, capability.state.value);
542
+ capability.state.action_result = { status: 'DONE' };
543
+ }
544
+ resolve(data);
545
+ });
546
+ }
547
+
548
+ _getStatePause(entity) {
549
+ return new Promise(resolve => {
550
+ const attribute = entity.ATTRIBUTES.find(attr => attr.attribute === 'pause');
551
+ const stateId = attribute ? attribute.getId : undefined;
552
+ const capability = entity.context.capabilities.find(cap => cap.type === 'devices.capabilities.toggle');
553
+ if (capability && stateId) {
554
+ this.adapter.getForeignState(stateId, (err, state) => {
555
+ if (!err && state) {
556
+ capability.state = {
557
+ instance: 'pause',
558
+ value:
559
+ state.val === true ||
560
+ state.val === 'true' ||
561
+ state.val === '1' ||
562
+ state.val === 1 ||
563
+ state.val === 'AN' ||
564
+ state.val === 'ON' ||
565
+ state.val === 'an' ||
566
+ state.val === 'on',
567
+ };
568
+ }
569
+ resolve();
570
+ });
571
+ } else {
572
+ resolve();
573
+ }
574
+ });
575
+ }
576
+
577
+ _setStatePause(entity, data) {
578
+ return new Promise(resolve => {
579
+ const stateId = entity.STATE.setId;
580
+ const capability = data.capabilities.find(
581
+ cap => cap.type === 'devices.capabilities.toggle' && cap.state.instance === 'pause',
582
+ );
583
+ if (capability && capability.state && stateId) {
584
+ this.adapter.setForeignState(
585
+ stateId,
586
+ capability.state.value === true || capability.state.value === 'true',
587
+ );
588
+ capability.state.action_result = { status: 'DONE' };
589
+ }
590
+ resolve(data);
591
+ });
592
+ }
593
+
594
+ _getStateVacuumMode(entity) {
595
+ return this._getStateMode(entity, 'cleanup_mode');
596
+ }
597
+
598
+ _setStateVacuumMode(entity, data) {
599
+ return this._setStateMode(entity, data, 'cleanup_mode');
600
+ }
601
+
602
+ _getStateVacuumWorkMode(entity) {
603
+ return this._getStateMode(entity, 'work_speed');
604
+ }
605
+
606
+ _setStateVacuumWorkMode(entity, data) {
607
+ return this._setStateMode(entity, data, 'work_speed');
608
+ }
609
+
610
+ _getStateFloat(entity, name) {
611
+ return this._getStateNumber(entity, 'devices.properties.float', name);
612
+ }
613
+
614
+ _processCommon(id, name, room, func, obj, entityType, entity_id) {
615
+ if (!name) {
616
+ if (func && room) {
617
+ name = `${room} ${func}`;
618
+ } else {
619
+ name = obj.common.custom[this.adapter.namespace].name || this._generateName(obj);
620
+ }
621
+ }
622
+ const _name = replaceInvalidChars(this._generateName(obj, 'en'));
623
+
624
+ const entity = {
625
+ entity_id: entity_id || `${entityType}.${_name}`,
626
+ //state: this._iobState2EntityState(obj._id, state.val);
627
+ attributes: {
628
+ friendly_name: name,
629
+ },
630
+
631
+ // объект описания smart-устройства
632
+ context: {
633
+ id: obj._id,
634
+ type: entityType,
635
+ name: name,
636
+ description: name,
637
+ room: room,
638
+ custom_data: {
639
+ entity_id: entity_id || `${entityType}.${_name}`,
640
+ },
641
+ capabilities: [],
642
+ properties: [],
643
+ device_info: {
644
+ manufacturer: 'IOBroker',
645
+ model: entity_id || `${entityType}.${_name}`,
646
+ hw_version: '',
647
+ sw_version: this.adapter.version,
648
+ },
649
+ },
650
+
651
+ // доступные команды для управления
652
+ COMMANDS: {},
653
+ ATTRIBUTES: [],
654
+ };
655
+
656
+ if (obj.common.unit) {
657
+ entity.attributes.unit_of_measurement = obj.common.unit;
658
+ //entity.attributes.unit_of_measurement_dict = obj.common.unit;
659
+ }
660
+
661
+ this._ID2entity[obj._id] = this._ID2entity[obj._id] || [];
662
+ this._ID2entity[obj._id].push(entity);
663
+ this._entity2ID[entity.entity_id] = entity;
664
+ this._entities.push(entity);
665
+ return entity;
666
+ }
667
+
668
+ _processSocket(id, control, name, room, func, _obj, objects) {
669
+ const entity = this._processCommon(id, name, room, func, _obj, 'devices.types.switch');
670
+
671
+ let state = control.states.find(s => s.id && s.name === 'SET');
672
+ entity.STATE = { setId: null, getId: null };
673
+ if (state && state.id) {
674
+ entity.STATE.setId = state.id;
675
+ entity.STATE.getId = state.id;
676
+ entity.STATE.type = objects[state.id] && objects[state.id].common && objects[state.id].common.type;
677
+ entity.attributes.icon = 'mdi:power-socket-eu';
678
+ this._addID2entity(state.id, entity);
679
+ }
680
+
681
+ state = control.states.find(s => s.id && s.name === 'ACTUAL');
682
+ if (state && state.id) {
683
+ entity.STATE.getId = state.id;
684
+ this._addID2entity(state.id, entity);
685
+ }
686
+
687
+ // capabilities
688
+ entity.context.capabilities.push({
689
+ type: 'devices.capabilities.on_off',
690
+ retrievable: true,
691
+ });
692
+ entity.COMMANDS.get_state = this._getStateOnOff.bind(this);
693
+ entity.COMMANDS.set_state = this._setStateOnOff.bind(this);
694
+ return entity;
695
+ }
696
+
697
+ _processLight(id, control, name, room, func, _obj, objects) {
698
+ const entity = this._processCommon(id, name, room, func, _obj, 'devices.types.light');
699
+
700
+ let state = control.states.find(s => s.id && ['ON_SET', 'ON', 'SET'].includes(s.name));
701
+ entity.STATE = { setId: null, getId: null };
702
+ if (state && state.id) {
703
+ entity.STATE.type = objects[state.id] && objects[state.id].common && objects[state.id].common.type;
704
+ entity.STATE.min = objects[state.id] && objects[state.id].common && objects[state.id].common.min;
705
+ entity.STATE.max = objects[state.id] && objects[state.id].common && objects[state.id].common.max;
706
+ entity.STATE.setId = state.id;
707
+ entity.STATE.getId = state.id;
708
+ this._addID2entity(state.id, entity);
709
+ }
710
+
711
+ state = control.states.find(s => s.id && s.name === 'ACTUAL');
712
+ if (state && state.id) {
713
+ entity.STATE.getId = state.id;
714
+ this._addID2entity(state.id, entity);
715
+ }
716
+
717
+ // capabilities
718
+ entity.context.capabilities.push({
719
+ type: 'devices.capabilities.on_off',
720
+ retrievable: true,
721
+ });
722
+ entity.COMMANDS.get_state = this._getStateOnOff.bind(this);
723
+ entity.COMMANDS.set_state = this._setStateOnOff.bind(this);
724
+ return entity;
725
+ }
726
+
727
+ _processDimmer(id, control, name, room, func, _obj, objects) {
728
+ const entity = this._processCommon(id, name, room, func, _obj, 'devices.types.light');
729
+
730
+ let state = control.states.find(s => s.id && ['ON_SET', 'ON'].includes(s.name));
731
+ entity.STATE = { setId: null, getId: null };
732
+ if (state && state.id) {
733
+ entity.STATE.type = objects[state.id] && objects[state.id].common && objects[state.id].common.type;
734
+ entity.STATE.min = objects[state.id] && objects[state.id].common && objects[state.id].common.min;
735
+ entity.STATE.max = objects[state.id] && objects[state.id].common && objects[state.id].common.max;
736
+ entity.STATE.setId = state.id;
737
+ entity.STATE.getId = state.id;
738
+ this._addID2entity(state.id, entity);
739
+ }
740
+
741
+ state = control.states.find(s => s.id && s.name === 'ON_ACTUAL');
742
+ if (state && state.id) {
743
+ entity.STATE.getId = state.id;
744
+ this._addID2entity(state.id, entity);
745
+ }
746
+
747
+ let getDimmer;
748
+ state = control.states.find(s => s.id && s.name === 'ACTUAL');
749
+ if (state && state.id) {
750
+ getDimmer = state.id;
751
+ }
752
+
753
+ state = control.states.find(s => s.id && ['DIMMER', 'SET', 'BRIGHTNESS'].includes(s.name));
754
+ let setDimmer = '';
755
+ if (state && state.id) {
756
+ setDimmer = state.id;
757
+ entity.ATTRIBUTES.push({
758
+ attribute: 'brightness',
759
+ getId: getDimmer || setDimmer,
760
+ setId: setDimmer,
761
+ type: objects[setDimmer] && objects[setDimmer].common && objects[setDimmer].common.type,
762
+ });
763
+ this._addID2entity(state.id, entity);
764
+ } else if (getDimmer) {
765
+ entity.ATTRIBUTES.push({
766
+ attribute: 'brightness',
767
+ getId: getDimmer,
768
+ setId: null,
769
+ type: objects[getDimmer] && objects[getDimmer].common && objects[getDimmer].common.type,
770
+ });
771
+ this._addID2entity(state.id, entity);
772
+ }
773
+
774
+ // capabilities
775
+ if (entity.STATE.getId) {
776
+ entity.context.capabilities.push({
777
+ type: 'devices.capabilities.on_off',
778
+ retrievable: true,
779
+ });
780
+ }
781
+
782
+ if (getDimmer || setDimmer) {
783
+ entity.context.capabilities.push({
784
+ type: 'devices.capabilities.range',
785
+ retrievable: !!getDimmer,
786
+ parameters: {
787
+ instance: 'brightness',
788
+ random_access: true,
789
+ unit: 'unit.percent',
790
+ range: {
791
+ min:
792
+ objects[getDimmer] &&
793
+ objects[getDimmer].common &&
794
+ objects[getDimmer].common.min !== undefined
795
+ ? objects[getDimmer].common.min
796
+ : 0,
797
+ max:
798
+ objects[getDimmer] &&
799
+ objects[getDimmer].common &&
800
+ objects[getDimmer].common.max !== undefined
801
+ ? objects[getDimmer].common.max
802
+ : 100,
803
+ precision: 1,
804
+ },
805
+ },
806
+ });
807
+ }
808
+
809
+ entity.COMMANDS.get_state = entity => this._getStateOnOff(entity).then(() => this._getStateBrightness(entity));
810
+
811
+ entity.COMMANDS.set_state = (entity, data) =>
812
+ this._setStateOnOff(entity, data).then(res => this._setStateBrightness(entity, res));
813
+
814
+ return entity;
815
+ }
816
+
817
+ _processCT(id, control, name, room, func, _obj, objects) {
818
+ const entity = this._processDimmer(id, control, name, room, func, _obj, objects);
819
+ const ctState = control.states.find(s => s.id && ['TEMPERATURE'].includes(s.name));
820
+ if (ctState && ctState.id) {
821
+ entity.ATTRIBUTES.push({
822
+ attribute: 'ct',
823
+ getId: ctState.id,
824
+ setId: ctState.id,
825
+ type: objects[ctState.id] && objects[ctState.id].common && objects[ctState.id].common.type,
826
+ min: objects[ctState.id] && objects[ctState.id].common && objects[ctState.id].common.min,
827
+ max: objects[ctState.id] && objects[ctState.id].common && objects[ctState.id].common.max,
828
+ });
829
+ this._addID2entity(ctState.id, entity);
830
+
831
+ // capabilities
832
+ entity.context.capabilities.push({
833
+ type: 'devices.capabilities.color_setting',
834
+ retrievable: true,
835
+ parameters: {
836
+ temperature_k: {
837
+ min:
838
+ (objects[ctState.id] && objects[ctState.id].common && objects[ctState.id].common.min) ||
839
+ 2700,
840
+ max:
841
+ (objects[ctState.id] && objects[ctState.id].common && objects[ctState.id].common.max) ||
842
+ 9000,
843
+ precision: 100,
844
+ },
845
+ },
846
+ });
847
+
848
+ const get_state = entity.COMMANDS.get_state;
849
+ const set_state = entity.COMMANDS.set_state;
850
+
851
+ entity.COMMANDS.get_state = entity => get_state(entity).then(() => this._getStateCT(entity));
852
+
853
+ entity.COMMANDS.set_state = (entity, data) =>
854
+ set_state(entity, data).then(data => this._setStateCT(entity, data));
855
+ }
856
+ return entity;
857
+ }
858
+
859
+ _processAC(id, control, name, room, func, _obj, objects) {
860
+ // create main device
861
+ const entity = this._processCommon(
862
+ id,
863
+ name,
864
+ room,
865
+ func,
866
+ _obj,
867
+ control.type === Types.thermostat ? 'devices.types.thermostat' : 'devices.types.thermostat.ac',
868
+ );
869
+
870
+ // default action: ON/OFF
871
+ let state = control.states.find(s => s.id && ['POWER', 'ON'].includes(s.name));
872
+ entity.STATE = { setId: null, getId: null };
873
+ if (state && state.id) {
874
+ entity.STATE.setId = state.id;
875
+ entity.STATE.getId = state.id;
876
+ entity.STATE.type = objects[state.id] && objects[state.id].common && objects[state.id].common.type;
877
+ entity.STATE.min = objects[state.id] && objects[state.id].common && objects[state.id].common.min;
878
+ entity.STATE.max = objects[state.id] && objects[state.id].common && objects[state.id].common.max;
879
+ this._addID2entity(state.id, entity);
880
+ }
881
+
882
+ // capabilities
883
+ if (entity.STATE.getId) {
884
+ entity.context.capabilities.push({
885
+ type: 'devices.capabilities.on_off',
886
+ retrievable: true,
887
+ });
888
+ }
889
+
890
+ // devices.capabilities.range - temperature
891
+ let getTemperature;
892
+ state = control.states.find(s => s.id && s.name === 'ACTUAL');
893
+ if (state && state.id) {
894
+ getTemperature = state.id;
895
+ }
896
+
897
+ state = control.states.find(s => s.id && ['SET'].includes(s.name));
898
+ let setTemperature = '';
899
+ if (state && state.id) {
900
+ setTemperature = state.id;
901
+ entity.ATTRIBUTES.push({
902
+ attribute: 'temperature',
903
+ getId: getTemperature || setTemperature,
904
+ setId: setTemperature,
905
+ type: objects[setTemperature] && objects[setTemperature].common && objects[setTemperature].common.type,
906
+ });
907
+ this._addID2entity(state.id, entity);
908
+ } else if (getTemperature) {
909
+ entity.ATTRIBUTES.push({
910
+ attribute: 'temperature',
911
+ getId: getTemperature,
912
+ setId: null,
913
+ type: objects[getTemperature] && objects[getTemperature].common && objects[getTemperature].common.type,
914
+ });
915
+ this._addID2entity(state.id, entity);
916
+ }
917
+
918
+ if (getTemperature || setTemperature) {
919
+ entity.context.capabilities.push({
920
+ type: 'devices.capabilities.range',
921
+ retrievable: true,
922
+ parameters: {
923
+ instance: 'temperature',
924
+ random_access: true,
925
+ unit: 'unit.temperature.celsius',
926
+ range: {
927
+ min:
928
+ objects[setTemperature] &&
929
+ objects[setTemperature].common &&
930
+ objects[setTemperature].common.min !== undefined
931
+ ? objects[setTemperature].common.min
932
+ : 12,
933
+ max:
934
+ objects[setTemperature] &&
935
+ objects[setTemperature].common &&
936
+ objects[setTemperature].common.max !== undefined
937
+ ? objects[setTemperature].common.max
938
+ : 30,
939
+ precision: 1,
940
+ },
941
+ },
942
+ });
943
+ }
944
+
945
+ // devices.capabilities.mode - thermostat
946
+ state = control.states.find(s => s.id && ['MODE'].includes(s.name));
947
+ if (state && state.id) {
948
+ let thermostatId = state.id;
949
+ const states = objects[thermostatId].common.states || {};
950
+ entity.ATTRIBUTES.push({
951
+ attribute: 'thermostat',
952
+ getId: thermostatId,
953
+ setId: thermostatId,
954
+ states,
955
+ type: objects[thermostatId] && objects[thermostatId].common && objects[thermostatId].common.type,
956
+ });
957
+ this._addID2entity(state.id, entity);
958
+ const modes = [];
959
+ const values = Object.values(states).map(name => name.toLowerCase());
960
+
961
+ values.includes('auto') && modes.push({ value: 'auto' });
962
+ values.includes('cool') && modes.push({ value: 'cool' });
963
+ values.includes('dry') && modes.push({ value: 'dry' });
964
+ values.includes('eco') && modes.push({ value: 'eco' });
965
+ values.includes('fan_only') && modes.push({ value: 'fan_only' });
966
+ values.includes('heat') && modes.push({ value: 'heat' });
967
+
968
+ entity.context.capabilities.push({
969
+ type: 'devices.capabilities.mode',
970
+ retrievable: true,
971
+ parameters: {
972
+ instance: 'thermostat',
973
+ modes,
974
+ },
975
+ });
976
+ }
977
+
978
+ // devices.capabilities.mode - swing
979
+ state = control.states.find(s => s.id && ['SWING'].includes(s.name));
980
+ if (state && state.id) {
981
+ let swingId = state.id;
982
+ const states = objects[swingId].common.states || {};
983
+ entity.ATTRIBUTES.push({
984
+ attribute: 'swing',
985
+ getId: swingId,
986
+ setId: swingId,
987
+ states,
988
+ type: objects[swingId] && objects[swingId].common && objects[swingId].common.type,
989
+ });
990
+ this._addID2entity(state.id, entity);
991
+ const modes = [];
992
+ const values = Object.values(states).map(name => name.toLowerCase());
993
+
994
+ values.includes('auto') && modes.push({ value: 'auto' });
995
+ values.includes('horizontal') && modes.push({ value: 'horizontal' });
996
+ values.includes('stationary') && modes.push({ value: 'stationary' });
997
+ values.includes('vertical') && modes.push({ value: 'vertical' });
998
+
999
+ entity.context.capabilities.push({
1000
+ type: 'devices.capabilities.mode',
1001
+ retrievable: true,
1002
+ parameters: {
1003
+ instance: 'swing',
1004
+ modes,
1005
+ },
1006
+ });
1007
+ }
1008
+
1009
+ // devices.capabilities.mode - fan_speed
1010
+ state = control.states.find(s => s.id && ['SPEED'].includes(s.name));
1011
+ if (state && state.id) {
1012
+ let fanSpeedId = state.id;
1013
+ const states = objects[fanSpeedId].common.states || {};
1014
+ entity.ATTRIBUTES.push({
1015
+ attribute: 'fan_speed',
1016
+ getId: fanSpeedId,
1017
+ setId: fanSpeedId,
1018
+ states,
1019
+ type: objects[fanSpeedId] && objects[fanSpeedId].common && objects[fanSpeedId].common.type,
1020
+ });
1021
+ this._addID2entity(state.id, entity);
1022
+ const modes = [];
1023
+ const values = Object.values(states).map(name => name.toLowerCase());
1024
+
1025
+ values.includes('auto') && modes.push({ value: 'auto' });
1026
+ values.includes('high') && modes.push({ value: 'high' });
1027
+ values.includes('low') && modes.push({ value: 'low' });
1028
+ values.includes('medium') && modes.push({ value: 'medium' });
1029
+ values.includes('quiet') && modes.push({ value: 'quiet' });
1030
+ values.includes('turbo') && modes.push({ value: 'turbo' });
1031
+
1032
+ entity.context.capabilities.push({
1033
+ type: 'devices.capabilities.mode',
1034
+ retrievable: true,
1035
+ parameters: {
1036
+ instance: 'fan_speed',
1037
+ modes,
1038
+ },
1039
+ });
1040
+ }
1041
+
1042
+ entity.COMMANDS.get_state = entity =>
1043
+ this._getStateOnOff(entity)
1044
+ .then(() => this._getStateTemperature(entity))
1045
+ .then(() => this._getStateThermostat(entity))
1046
+ .then(() => this._getStateSwing(entity))
1047
+ .then(() => this._getStateSpeed(entity));
1048
+
1049
+ entity.COMMANDS.set_state = (entity, data) =>
1050
+ this._setStateOnOff(entity, data)
1051
+ .then(res => this._setStateTemperature(entity, res))
1052
+ .then(res => this._setStateThermostat(entity, res))
1053
+ .then(res => this._setStateSwing(entity, res))
1054
+ .then(res => this._setStateSpeed(entity, res));
1055
+
1056
+ return entity;
1057
+ }
1058
+
1059
+ _processRGB(id, control, name, room, func, _obj, objects) {
1060
+ const entity = this._processDimmer(id, control, name, room, func, _obj, objects);
1061
+
1062
+ const rgbState = control.states.find(s => s.id && ['RGB'].includes(s.name));
1063
+ if (rgbState && rgbState.id) {
1064
+ entity.ATTRIBUTES.push({
1065
+ attribute: 'rgb',
1066
+ getId: rgbState.id,
1067
+ setId: rgbState.id,
1068
+ type: objects[rgbState.id] && objects[rgbState.id].common && objects[rgbState.id].common.type,
1069
+ min: objects[rgbState.id] && objects[rgbState.id].common && objects[rgbState.id].common.min,
1070
+ max: objects[rgbState.id] && objects[rgbState.id].common && objects[rgbState.id].common.max,
1071
+ });
1072
+ this._addID2entity(rgbState.id, entity);
1073
+
1074
+ // capabilities
1075
+ const capability = entity.context.capabilities.find(
1076
+ cap => cap.type === 'devices.capabilities.color_setting',
1077
+ );
1078
+ if (!capability) {
1079
+ entity.context.capabilities.push({
1080
+ type: 'devices.capabilities.color_setting',
1081
+ retrievable: true,
1082
+ parameters: {
1083
+ color_model: 'rgb',
1084
+ },
1085
+ });
1086
+ } else {
1087
+ capability.parameters.color_model = 'rgb';
1088
+ }
1089
+
1090
+ const get_state = entity.COMMANDS.get_state;
1091
+ const set_state = entity.COMMANDS.set_state;
1092
+
1093
+ entity.COMMANDS.get_state = entity => get_state(entity).then(() => this._getStateRGB(entity));
1094
+
1095
+ entity.COMMANDS.set_state = (entity, data) =>
1096
+ set_state(entity, data).then(data => this._setStateRGB(entity, data));
1097
+ }
1098
+
1099
+ return entity;
1100
+ }
1101
+
1102
+ _processBlinds(id, control, name, room, func, _obj, objects) {
1103
+ const entity = this._processCommon(id, name, room, func, _obj, 'devices.types.openable.curtain');
1104
+
1105
+ let state = control.states.find(s => s.id && ['SET'].includes(s.name));
1106
+ entity.STATE = { setId: null, getId: null };
1107
+ if (
1108
+ state &&
1109
+ state.id &&
1110
+ objects[state.id] &&
1111
+ objects[state.id].common &&
1112
+ objects[state.id].common.type === 'boolean'
1113
+ ) {
1114
+ entity.STATE.setId = state.id;
1115
+ entity.STATE.getId = state.id;
1116
+ entity.STATE.type = 'boolean';
1117
+ this._addID2entity(state.id, entity);
1118
+ }
1119
+
1120
+ state = control.states.find(s => s.id && s.name === 'ACTUAL');
1121
+ if (state && state.id && objects[state.id].common.type === 'boolean') {
1122
+ entity.STATE.getId = state.id;
1123
+ this._addID2entity(state.id, entity);
1124
+ }
1125
+
1126
+ // capabilities
1127
+ if (entity.STATE.getId) {
1128
+ entity.context.capabilities.push({
1129
+ type: 'devices.capabilities.on_off',
1130
+ retrievable: true,
1131
+ });
1132
+ }
1133
+
1134
+ state = control.states.find(s => s.id && ['SET'].includes(s.name));
1135
+ if (
1136
+ state &&
1137
+ state.id &&
1138
+ objects[state.id] &&
1139
+ objects[state.id].common &&
1140
+ objects[state.id].common.type === 'number'
1141
+ ) {
1142
+ const blindId = state.id;
1143
+ const attribute = {
1144
+ attribute: 'open',
1145
+ getId: blindId,
1146
+ setId: blindId,
1147
+ type: 'number',
1148
+ min: objects[state.id].common.min !== undefined ? objects[state.id].common.min : 0,
1149
+ max: objects[state.id].common.max !== undefined ? objects[state.id].common.max : 100,
1150
+ };
1151
+ entity.ATTRIBUTES.push(attribute);
1152
+ this._addID2entity(blindId, entity);
1153
+
1154
+ const getState = control.states.find(s => s.id && s.name === 'ACTUAL');
1155
+ if (
1156
+ getState &&
1157
+ getState.id &&
1158
+ objects[getState.id] &&
1159
+ objects[getState.id].common &&
1160
+ objects[getState.id].common.type === 'number'
1161
+ ) {
1162
+ attribute.getId = getState.id;
1163
+ this._addID2entity(getState.id, entity);
1164
+ }
1165
+
1166
+ entity.context.capabilities.push({
1167
+ type: 'devices.capabilities.range',
1168
+ retrievable: true,
1169
+ parameters: {
1170
+ instance: 'open',
1171
+ random_access: true,
1172
+ unit: 'unit.percent',
1173
+ range: {
1174
+ min: objects[blindId].common.min !== undefined ? objects[blindId].common.min : 0,
1175
+ max: objects[blindId].common.max !== undefined ? objects[blindId].common.max : 100,
1176
+ precision: 1,
1177
+ },
1178
+ },
1179
+ });
1180
+
1181
+ // Simulate On/Off
1182
+ if (!entity.STATE.getId) {
1183
+ entity.STATE.setId = state.id;
1184
+ entity.STATE.getId = getState ? getState.id : state.id;
1185
+ entity.STATE.type = 'number';
1186
+ entity.context.capabilities.push({
1187
+ type: 'devices.capabilities.on_off',
1188
+ retrievable: true,
1189
+ });
1190
+ }
1191
+ }
1192
+
1193
+ entity.COMMANDS.get_state = entity => this._getStateOnOff(entity).then(() => this._getStateBlinds(entity));
1194
+
1195
+ entity.COMMANDS.set_state = (entity, data) =>
1196
+ this._setStateOnOff(entity, data).then(res => this._setStateBlinds(entity, res));
1197
+
1198
+ return entity;
1199
+ }
1200
+
1201
+ _processLock(id, control, name, room, func, _obj, objects) {
1202
+ const entity = this._processCommon(id, name, room, func, _obj, 'devices.types.openable');
1203
+
1204
+ let state = control.states.find(s => s.id && ['SET'].includes(s.name));
1205
+ entity.STATE = { setId: null, getId: null };
1206
+ if (state && state.id) {
1207
+ entity.STATE.type = objects[state.id].common.type;
1208
+ entity.STATE.min = objects[state.id].common.min;
1209
+ entity.STATE.max = objects[state.id].common.max;
1210
+ entity.STATE.setId = state.id;
1211
+ entity.STATE.getId = state.id;
1212
+ this._addID2entity(state.id, entity);
1213
+ }
1214
+
1215
+ state = control.states.find(s => s.id && s.name === 'ACTUAL');
1216
+ if (state && state.id) {
1217
+ entity.STATE.getId = state.id;
1218
+ this._addID2entity(state.id, entity);
1219
+ }
1220
+
1221
+ state = control.states.find(s => s.id && s.name === 'OPEN');
1222
+ if (state && state.id) {
1223
+ entity.STATE.openId = state.id;
1224
+ this._addID2entity(state.id, entity);
1225
+ }
1226
+
1227
+ // capabilities
1228
+ if (entity.STATE.getId) {
1229
+ entity.context.capabilities.push({
1230
+ type: 'devices.capabilities.on_off',
1231
+ retrievable: true,
1232
+ });
1233
+ }
1234
+
1235
+ entity.COMMANDS.get_state = entity => this._getStateOnOff(entity);
1236
+
1237
+ entity.COMMANDS.set_state = (entity, data) => this._setStateLock(entity, data);
1238
+
1239
+ return entity;
1240
+ }
1241
+
1242
+ _processVacuumCleaner(id, control, name, room, func, _obj, objects) {
1243
+ // create main device
1244
+ const entity = this._processCommon(id, name, room, func, _obj, 'devices.types.vacuum_cleaner');
1245
+
1246
+ // default action: ON/OFF
1247
+ let state = control.states.find(s => s.id && ['POWER'].includes(s.name));
1248
+ entity.STATE = { setId: null, getId: null };
1249
+ if (state && state.id) {
1250
+ entity.STATE.setId = state.id;
1251
+ entity.STATE.getId = state.id;
1252
+ entity.STATE.type = objects[state.id] && objects[state.id].common && objects[state.id].common.type;
1253
+ entity.STATE.min = objects[state.id] && objects[state.id].common && objects[state.id].common.min;
1254
+ entity.STATE.max = objects[state.id] && objects[state.id].common && objects[state.id].common.max;
1255
+ this._addID2entity(state.id, entity);
1256
+ }
1257
+
1258
+ // capabilities
1259
+ if (entity.STATE.getId) {
1260
+ entity.context.capabilities.push({
1261
+ type: 'devices.capabilities.on_off',
1262
+ retrievable: true,
1263
+ });
1264
+ }
1265
+
1266
+ // devices.capabilities.mode - cleanup_mode
1267
+ state = control.states.find(s => s.id && ['MODE'].includes(s.name));
1268
+ if (state && state.id) {
1269
+ const states = objects[state.id].common.states || {};
1270
+ entity.ATTRIBUTES.push({
1271
+ attribute: 'cleanup_mode',
1272
+ getId: state.id,
1273
+ setId: state.id,
1274
+ states,
1275
+ type: objects[state.id] && objects[state.id].common && objects[state.id].common.type,
1276
+ });
1277
+ this._addID2entity(state.id, entity);
1278
+ const modes = [];
1279
+ const values = Object.values(states).map(name => name.toLowerCase());
1280
+
1281
+ values.includes('auto') && modes.push({ value: 'auto' });
1282
+ values.includes('eco') && modes.push({ value: 'eco' });
1283
+ values.includes('express') && modes.push({ value: 'express' });
1284
+ values.includes('normal') && modes.push({ value: 'normal' });
1285
+ values.includes('quiet') && modes.push({ value: 'quiet' });
1286
+
1287
+ entity.context.capabilities.push({
1288
+ type: 'devices.capabilities.mode',
1289
+ retrievable: true,
1290
+ parameters: {
1291
+ instance: 'cleanup_mode',
1292
+ modes,
1293
+ },
1294
+ });
1295
+ }
1296
+
1297
+ // devices.capabilities.mode - work_speed
1298
+ state = control.states.find(s => s.id && ['WORK_MODE'].includes(s.name));
1299
+ if (state && state.id) {
1300
+ const states = objects[state.id].common.states || {};
1301
+ entity.ATTRIBUTES.push({
1302
+ attribute: 'work_speed',
1303
+ getId: state.id,
1304
+ setId: state.id,
1305
+ states,
1306
+ type: objects[state.id] && objects[state.id].common && objects[state.id].common.type,
1307
+ });
1308
+ this._addID2entity(state.id, entity);
1309
+ const modes = [];
1310
+ const values = Object.values(states).map(name => name.toLowerCase());
1311
+
1312
+ // 0: 'AUTO', 1: 'FAST', 2: 'MEDIUM', 3: 'SLOW', 4: 'TURBO'
1313
+ values.includes('auto') && modes.push({ value: 'auto' });
1314
+ values.includes('fast') && modes.push({ value: 'fast' });
1315
+ values.includes('medium') && modes.push({ value: 'medium' });
1316
+ values.includes('slow') && modes.push({ value: 'slow' });
1317
+ values.includes('turbo') && modes.push({ value: 'turbo' });
1318
+
1319
+ entity.context.capabilities.push({
1320
+ type: 'devices.capabilities.mode',
1321
+ retrievable: true,
1322
+ parameters: {
1323
+ instance: 'work_speed',
1324
+ modes,
1325
+ },
1326
+ });
1327
+ }
1328
+
1329
+ // devices.capabilities.mode - pause
1330
+ state = control.states.find(s => s.id && ['PAUSE'].includes(s.name));
1331
+ if (state && state.id) {
1332
+ const states = objects[state.id].common.states || {};
1333
+ entity.ATTRIBUTES.push({
1334
+ attribute: 'pause',
1335
+ getId: state.id,
1336
+ setId: state.id,
1337
+ states,
1338
+ type: objects[state.id] && objects[state.id].common && objects[state.id].common.type,
1339
+ });
1340
+ this._addID2entity(state.id, entity);
1341
+
1342
+ entity.context.capabilities.push({
1343
+ type: 'devices.capabilities.toggle',
1344
+ retrievable: true,
1345
+ parameters: {
1346
+ instance: 'pause',
1347
+ },
1348
+ });
1349
+ }
1350
+
1351
+ // devices.capabilities.float - water_level
1352
+ state = control.states.find(s => s.id && ['WATER'].includes(s.name));
1353
+ if (state && state.id) {
1354
+ const states = objects[state.id].common.states || {};
1355
+ entity.ATTRIBUTES.push({
1356
+ attribute: 'water_level',
1357
+ getId: state.id,
1358
+ states,
1359
+ type: objects[state.id] && objects[state.id].common && objects[state.id].common.type,
1360
+ });
1361
+ this._addID2entity(state.id, entity);
1362
+
1363
+ entity.context.capabilities.push({
1364
+ type: 'devices.capabilities.float',
1365
+ retrievable: true,
1366
+ parameters: {
1367
+ instance: 'water_level',
1368
+ unit: 'unit.percent',
1369
+ },
1370
+ });
1371
+ }
1372
+
1373
+ // devices.capabilities.float - battery_level
1374
+ state = control.states.find(s => s.id && ['BATTERY'].includes(s.name));
1375
+ if (state && state.id) {
1376
+ const states = objects[state.id].common.states || {};
1377
+ entity.ATTRIBUTES.push({
1378
+ attribute: 'battery_level',
1379
+ getId: state.id,
1380
+ states,
1381
+ type: objects[state.id] && objects[state.id].common && objects[state.id].common.type,
1382
+ });
1383
+ this._addID2entity(state.id, entity);
1384
+
1385
+ entity.context.capabilities.push({
1386
+ type: 'devices.capabilities.float',
1387
+ retrievable: true,
1388
+ parameters: {
1389
+ instance: 'battery_level',
1390
+ unit: 'unit.percent',
1391
+ },
1392
+ });
1393
+ }
1394
+
1395
+ entity.COMMANDS.get_state = entity =>
1396
+ this._getStateOnOff(entity)
1397
+ .then(() => this._getStateVacuumMode(entity))
1398
+ .then(() => this._getStateVacuumWorkMode(entity))
1399
+ .then(() => this._getStatePause(entity))
1400
+ .then(() => this._getStateFloat(entity, 'water_level'))
1401
+ .then(() => this._getStateFloat(entity, 'battery_level'));
1402
+
1403
+ entity.COMMANDS.set_state = (entity, data) =>
1404
+ this._setStateOnOff(entity, data)
1405
+ .then(res => this._setStatePause(entity, res))
1406
+ .then(res => this._setStateVacuumMode(entity, res))
1407
+ .then(res => this._setStateVacuumWorkMode(entity, res));
1408
+
1409
+ return entity;
1410
+ }
1411
+
1412
+ _processTemperature(id, control, name, room, func, _obj, objects) {
1413
+ const entity = this._processCommon(id, name, room, func, _obj, 'devices.types.sensor');
1414
+
1415
+ let state = control.states.find(s => s.id && ['ACTUAL'].includes(s.name));
1416
+ entity.STATE = { getId: null };
1417
+ const temperatureId = state && state.id;
1418
+ if (
1419
+ temperatureId &&
1420
+ objects[temperatureId] &&
1421
+ objects[temperatureId].common &&
1422
+ objects[temperatureId].common.type === 'number'
1423
+ ) {
1424
+ entity.STATE.getId = temperatureId;
1425
+ entity.STATE.type = 'number';
1426
+ this._addID2entity(temperatureId, entity);
1427
+
1428
+ entity.ATTRIBUTES.push({
1429
+ attribute: 'temperature',
1430
+ getId: temperatureId,
1431
+ type: 'number',
1432
+ });
1433
+
1434
+ entity.context.properties.push({
1435
+ type: 'devices.properties.float',
1436
+ retrievable: true,
1437
+ parameters: {
1438
+ instance: 'temperature',
1439
+ unit:
1440
+ objects[temperatureId].common.unit === '°K' ||
1441
+ objects[temperatureId].common.unit === 'K' ||
1442
+ objects[temperatureId].common.unit === 'K°'
1443
+ ? 'unit.temperature.kelvin'
1444
+ : 'unit.temperature.celsius',
1445
+ },
1446
+ });
1447
+ }
1448
+
1449
+ // humidity
1450
+ state = control.states.find(s => s.id && ['SECOND'].includes(s.name));
1451
+ const humidityId = state && state.id;
1452
+ if (
1453
+ humidityId &&
1454
+ objects[humidityId] &&
1455
+ objects[humidityId].common &&
1456
+ objects[humidityId].common.type === 'number'
1457
+ ) {
1458
+ entity.ATTRIBUTES.push({
1459
+ attribute: 'humidity',
1460
+ getId: humidityId,
1461
+ type: 'number',
1462
+ });
1463
+ this._addID2entity(humidityId, entity);
1464
+
1465
+ entity.context.properties.push({
1466
+ type: 'devices.properties.float',
1467
+ retrievable: true,
1468
+ parameters: {
1469
+ instance: 'humidity',
1470
+ unit: 'unit.percent',
1471
+ },
1472
+ });
1473
+ }
1474
+
1475
+ entity.COMMANDS.get_state = entity =>
1476
+ this._getStateFloatProperty(entity, 'temperature').then(() =>
1477
+ this._getStateFloatProperty(entity, 'humidity'),
1478
+ );
1479
+
1480
+ return entity;
1481
+ }
1482
+
1483
+ _processHumidity(id, control, name, room, func, _obj, objects) {
1484
+ const entity = this._processCommon(id, name, room, func, _obj, 'devices.types.sensor');
1485
+
1486
+ let state = control.states.find(s => s.id && ['ACTUAL'].includes(s.name));
1487
+ entity.STATE = { getId: null };
1488
+ const humidityId = state && state.id;
1489
+ if (
1490
+ humidityId &&
1491
+ objects[humidityId] &&
1492
+ objects[humidityId].common &&
1493
+ objects[humidityId].common.type === 'number'
1494
+ ) {
1495
+ entity.STATE.getId = humidityId;
1496
+ entity.STATE.type = 'number';
1497
+ this._addID2entity(humidityId, entity);
1498
+
1499
+ entity.ATTRIBUTES.push({
1500
+ attribute: 'humidity',
1501
+ getId: humidityId,
1502
+ type: 'number',
1503
+ });
1504
+
1505
+ entity.context.properties.push({
1506
+ type: 'devices.properties.float',
1507
+ retrievable: true,
1508
+ parameters: {
1509
+ instance: 'humidity',
1510
+ unit: 'unit.percent',
1511
+ },
1512
+ });
1513
+ }
1514
+
1515
+ entity.COMMANDS.get_state = entity => this._getStateFloatProperty(entity, 'humidity');
1516
+
1517
+ return entity;
1518
+ }
1519
+
1520
+ _processMotion(id, control, name, room, func, _obj, objects) {
1521
+ const entity = this._processCommon(id, name, room, func, _obj, 'devices.types.sensor');
1522
+
1523
+ let state = control.states.find(s => s.id && ['ACTUAL'].includes(s.name));
1524
+ entity.STATE = { getId: null };
1525
+ const motionId = state && state.id;
1526
+ if (motionId && objects[motionId] && objects[motionId].common && objects[motionId].common.type === 'boolean') {
1527
+ entity.STATE.getId = motionId;
1528
+ entity.STATE.type = 'boolean';
1529
+ this._addID2entity(motionId, entity);
1530
+
1531
+ entity.ATTRIBUTES.push({
1532
+ attribute: 'motion',
1533
+ getId: motionId,
1534
+ type: 'number',
1535
+ });
1536
+
1537
+ entity.context.properties.push({
1538
+ type: 'devices.properties.event',
1539
+ retrievable: true,
1540
+ parameters: {
1541
+ instance: 'motion',
1542
+ events: [
1543
+ {
1544
+ value: 'detected',
1545
+ },
1546
+ {
1547
+ value: 'not_detected',
1548
+ },
1549
+ ],
1550
+ },
1551
+ });
1552
+ }
1553
+
1554
+ entity.COMMANDS.get_state = entity =>
1555
+ this._getStateBooleanProperty(entity, 'motion', { true: 'detected', false: 'not_detected' });
1556
+
1557
+ return entity;
1558
+ }
1559
+
1560
+ _processContact(id, control, name, room, func, _obj, objects) {
1561
+ const entity = this._processCommon(id, name, room, func, _obj, 'devices.types.sensor');
1562
+
1563
+ let state = control.states.find(s => s.id && ['ACTUAL'].includes(s.name));
1564
+ entity.STATE = { getId: null };
1565
+ const contactId = state && state.id;
1566
+ if (
1567
+ contactId &&
1568
+ objects[contactId] &&
1569
+ objects[contactId].common &&
1570
+ objects[contactId].common.type === 'boolean'
1571
+ ) {
1572
+ entity.STATE.getId = contactId;
1573
+ entity.STATE.type = 'boolean';
1574
+ this._addID2entity(contactId, entity);
1575
+
1576
+ entity.ATTRIBUTES.push({
1577
+ attribute: 'open',
1578
+ getId: contactId,
1579
+ type: 'number',
1580
+ });
1581
+
1582
+ entity.context.properties.push({
1583
+ type: 'devices.properties.event',
1584
+ retrievable: true,
1585
+ parameters: {
1586
+ instance: 'open',
1587
+ events: [
1588
+ {
1589
+ value: 'opened',
1590
+ },
1591
+ {
1592
+ value: 'closed',
1593
+ },
1594
+ ],
1595
+ },
1596
+ });
1597
+ }
1598
+
1599
+ entity.COMMANDS.get_state = entity =>
1600
+ this._getStateBooleanProperty(entity, 'open', { true: 'closed', false: 'opened' });
1601
+
1602
+ return entity;
1603
+ }
1604
+
1605
+ _processButtonSensor(id, control, name, room, func, _obj, objects) {
1606
+ const entity = this._processCommon(id, name, room, func, _obj, 'devices.types.sensor');
1607
+
1608
+ let state = control.states.find(s => s.id && ['PRESS'].includes(s.name));
1609
+ entity.STATE = { getId: null };
1610
+ const buttonId = state && state.id;
1611
+ if (buttonId && objects[buttonId] && objects[buttonId].common && objects[buttonId].common.type === 'boolean') {
1612
+ entity.STATE.getId = buttonId;
1613
+ entity.STATE.type = 'string';
1614
+ this._addID2entity(buttonId, entity);
1615
+
1616
+ entity.ATTRIBUTES.push({
1617
+ attribute: 'button',
1618
+ getId: buttonId,
1619
+ type: 'boolean',
1620
+ });
1621
+
1622
+ entity.context.properties.push({
1623
+ type: 'devices.properties.event',
1624
+ retrievable: true,
1625
+ parameters: {
1626
+ instance: 'button',
1627
+ events: [{ value: 'click' }, { value: 'double_click' }, { value: 'long_press' }],
1628
+ },
1629
+ });
1630
+ }
1631
+
1632
+ entity.COMMANDS.get_state = entity => this._getStateBooleanProperty(entity, 'button', { true: 'click' });
1633
+
1634
+ return entity;
1635
+ }
1636
+ }
1637
+
1638
+ class YandexAlisa {
1639
+ constructor(adapter, urlKey) {
1640
+ this.adapter = adapter;
1641
+ this.lang = 'ru';
1642
+ this.urlKey = urlKey;
1643
+ this.user_id = adapter.config.login.replace(/[^-_:a-zA-Z1-9]/g, '_');
1644
+
1645
+ this.smartDevices = [];
1646
+ this.enums = [];
1647
+ this.usedIds = [];
1648
+ this.detector = new ChannelDetector.default();
1649
+ this.unknownDevices = {};
1650
+ this.rateCalculation = [];
1651
+
1652
+ this.converter = new YandexAliceConverter(adapter);
1653
+ }
1654
+
1655
+ _subscribeAllIds(ids, cb) {
1656
+ if (!ids || !ids.length) {
1657
+ cb && cb();
1658
+ } else {
1659
+ const id = ids.shift();
1660
+ console.log(`Subscribe ${id}`);
1661
+ this.adapter.subscribeForeignStates(id, () => setImmediate(() => this._subscribeAllIds(ids, cb)));
1662
+ }
1663
+ }
1664
+
1665
+ _unsubscribeAllIds(ids, cb) {
1666
+ if (!ids || !ids.length) {
1667
+ cb && cb();
1668
+ } else {
1669
+ const id = ids.shift();
1670
+ console.log('Unsubscribe ' + id);
1671
+ this.adapter.unsubscribeForeignStates(id, () => setImmediate(() => this._unsubscribeAllIds(ids, cb)));
1672
+ }
1673
+ }
1674
+
1675
+ unsubscribeAllIds(cb) {
1676
+ const ids = [];
1677
+ this.smartDevices &&
1678
+ this.smartDevices.forEach(device =>
1679
+ device.ATTRIBUTES.forEach(
1680
+ item => item && item.getId && !ids.includes(item.getId) && ids.push(item.getId),
1681
+ ),
1682
+ );
1683
+
1684
+ this.adapter.log.debug(`[ALISA] Unsubscribe ${ids.length} states for Alisa`);
1685
+ this._unsubscribeAllIds(ids, () => {
1686
+ this.adapter.log.debug(`[ALISA] Unsubscribe done`);
1687
+ cb && cb();
1688
+ });
1689
+ }
1690
+
1691
+ subscribeAllIds(cb) {
1692
+ const ids = [];
1693
+ this.smartDevices &&
1694
+ this.smartDevices.forEach(device =>
1695
+ device.ATTRIBUTES.forEach(
1696
+ item => item && item.getId && !ids.includes(item.getId) && ids.push(item.getId),
1697
+ ),
1698
+ );
1699
+
1700
+ this.adapter.log.debug(`[ALISA] Subscribe ${ids.length} states for Alisa`);
1701
+
1702
+ this._subscribeAllIds(ids, () => {
1703
+ this.adapter.log.debug(`[ALISA] Subscribe done`);
1704
+ cb && cb();
1705
+ });
1706
+ }
1707
+
1708
+ getObjectName(obj) {
1709
+ let name = '';
1710
+ // extract from smartName the name
1711
+ if (this.adapter.config.noCommon) {
1712
+ if (
1713
+ obj.common &&
1714
+ obj.common.custom &&
1715
+ obj.common.custom[this.adapter.namespace] &&
1716
+ obj.common.custom[this.adapter.namespace].smartName &&
1717
+ obj.common.custom[this.adapter.namespace].smartName !== 'ignore'
1718
+ ) {
1719
+ name = obj.common.custom[this.adapter.namespace].smartName;
1720
+ }
1721
+ } else {
1722
+ if (obj.common && obj.common.smartName && obj.common.smartName !== 'ignore') {
1723
+ name = obj.common.smartName;
1724
+ }
1725
+ }
1726
+
1727
+ // if no smart name found, get the normal key
1728
+ if (!name && obj && obj.common && obj.common.name) {
1729
+ name = obj.common.name;
1730
+ }
1731
+
1732
+ if (name && typeof name === 'object') {
1733
+ name = name[this.lang] || name['en'];
1734
+ }
1735
+
1736
+ if (!name && obj) {
1737
+ name = obj._id.split('.').pop();
1738
+ }
1739
+
1740
+ return name;
1741
+ }
1742
+
1743
+ checkName(name, obj, room, func) {
1744
+ if (!name) {
1745
+ name = name || this.getObjectName(obj);
1746
+ name = name.replace(/[^a-zA-ZöäüßÖÄÜа-яА-Я0-9]/g, ' ');
1747
+ const _name = name.toLowerCase();
1748
+ let pos;
1749
+ if (room) {
1750
+ pos = _name.indexOf(room.toLowerCase());
1751
+ if (pos !== -1) {
1752
+ name = name.substring(0, pos) + name.substring(pos + room.length + 1);
1753
+ }
1754
+ }
1755
+ if (func) {
1756
+ pos = _name.indexOf(func.toLowerCase());
1757
+ if (pos !== -1) {
1758
+ name = name.substring(0, pos) + name.substring(pos + room.length + 1);
1759
+ }
1760
+ }
1761
+ name = name.replace(/\s\s/g).replace(/\s\s/g).trim();
1762
+ }
1763
+ return name;
1764
+ }
1765
+
1766
+ setLanguage(_lang) {
1767
+ this.lang = _lang || 'ru';
1768
+ this.converter.setLanguage(this.lang);
1769
+ }
1770
+
1771
+ getSmartName(states, id) {
1772
+ if (!id) {
1773
+ if (!this.adapter.config.noCommon) {
1774
+ return states.common.smartName;
1775
+ } else {
1776
+ return states && states.common && states.common.custom && states.common.custom[this.adapter.namespace]
1777
+ ? states.common.custom[this.adapter.namespace].smartName
1778
+ : undefined;
1779
+ }
1780
+ } else if (!this.adapter.config.noCommon) {
1781
+ return states[id] && states[id].common ? states[id].common.smartName : null;
1782
+ } else {
1783
+ return states[id] &&
1784
+ states[id].common &&
1785
+ states[id].common.custom &&
1786
+ states[id].common.custom[this.adapter.namespace]
1787
+ ? states[id].common.custom[this.adapter.namespace].smartName
1788
+ : null;
1789
+ }
1790
+ }
1791
+
1792
+ processState(ids, objects, id, roomName, funcName, result) {
1793
+ if (!id) {
1794
+ return;
1795
+ }
1796
+
1797
+ let friendlyName = this.getSmartName(objects, id);
1798
+ if (typeof friendlyName === 'object' && friendlyName) {
1799
+ friendlyName = friendlyName[this.lang] || friendlyName.en;
1800
+ }
1801
+
1802
+ if (friendlyName === 'ignore' || friendlyName === false) {
1803
+ return;
1804
+ }
1805
+
1806
+ if (!friendlyName && !roomName && !funcName) {
1807
+ return;
1808
+ }
1809
+
1810
+ try {
1811
+ // try to detect device
1812
+ const options = {
1813
+ objects,
1814
+ id,
1815
+ _keysOptional: ids,
1816
+ _usedIdsOptional: this.usedIds,
1817
+ };
1818
+ const controls = this.detector.detect(options);
1819
+ if (controls) {
1820
+ controls.forEach(control => {
1821
+ if (this.converter.types[control.type]) {
1822
+ const entity = this.converter.types[control.type](
1823
+ id,
1824
+ control,
1825
+ friendlyName,
1826
+ roomName,
1827
+ funcName,
1828
+ objects[id],
1829
+ objects,
1830
+ );
1831
+ if (!entity) {
1832
+ return;
1833
+ }
1834
+
1835
+ const _entity = result.find(e => e.entity_id === entity.entity_id);
1836
+ if (_entity) {
1837
+ console.log('Duplicates found for ' + entity.entity_id);
1838
+ return;
1839
+ }
1840
+
1841
+ result.push(entity);
1842
+ this.adapter.log.debug(
1843
+ `[ALISA] Created Yandex Alice device: ${entity.entity_id} - ${control.type} - ${id}`,
1844
+ );
1845
+ }
1846
+ });
1847
+ } else {
1848
+ console.log(`[ALISA] Nothing found for ${options.id}`);
1849
+ }
1850
+ } catch (e) {
1851
+ this.adapter.log.error(`[ALISA] Cannot process "${id}": ${e}`);
1852
+ }
1853
+ }
1854
+
1855
+ _readObjects() {
1856
+ return new Promise(resolve => {
1857
+ this.adapter.getObjectView('system', 'state', {}, (err, _states) => {
1858
+ this.adapter.getObjectView('system', 'channel', {}, (err, _channels) => {
1859
+ this.adapter.getObjectView('system', 'device', {}, (err, _devices) => {
1860
+ this.adapter.getObjectView('system', 'enum', {}, (err, _enums) => {
1861
+ const objects = {};
1862
+ const enums = {};
1863
+ if (_devices && _devices.rows) {
1864
+ for (let i = 0; i < _devices.rows.length; i++) {
1865
+ if (
1866
+ _devices.rows[i].value &&
1867
+ _devices.rows[i].value._id &&
1868
+ !ignoreIds.find(reg => reg.test(_devices.rows[i].value._id))
1869
+ ) {
1870
+ objects[_devices.rows[i].value._id] = _devices.rows[i].value;
1871
+ }
1872
+ }
1873
+ }
1874
+ if (_channels && _channels.rows) {
1875
+ for (let i = 0; i < _channels.rows.length; i++) {
1876
+ if (
1877
+ _channels.rows[i].value &&
1878
+ _channels.rows[i].value._id &&
1879
+ !ignoreIds.find(reg => reg.test(_channels.rows[i].value._id))
1880
+ ) {
1881
+ objects[_channels.rows[i].value._id] = _channels.rows[i].value;
1882
+ }
1883
+ }
1884
+ }
1885
+ if (_states && _states.rows) {
1886
+ for (let i = 0; i < _states.rows.length; i++) {
1887
+ if (
1888
+ _states.rows[i].value &&
1889
+ _states.rows[i].value._id &&
1890
+ !ignoreIds.find(reg => reg.test(_states.rows[i].value._id))
1891
+ ) {
1892
+ objects[_states.rows[i].value._id] = _states.rows[i].value;
1893
+ }
1894
+ }
1895
+ }
1896
+ if (_enums && _enums.rows) {
1897
+ for (let i = 0; i < _enums.rows.length; i++) {
1898
+ if (_enums.rows[i].value && _enums.rows[i].value._id) {
1899
+ enums[_enums.rows[i].value._id] = _enums.rows[i].value;
1900
+ objects[_enums.rows[i].value._id] = _enums.rows[i].value;
1901
+ }
1902
+ }
1903
+ }
1904
+ resolve({ objects, enums });
1905
+ });
1906
+ });
1907
+ });
1908
+ });
1909
+ });
1910
+ }
1911
+
1912
+ updateDevices(cb) {
1913
+ this.unsubscribeAllIds(() => {
1914
+ this._updateDevices().then(smartDevices => {
1915
+ this.smartDevices = smartDevices;
1916
+ this.adapter.log.debug(`[ALISA] SmartDevices: ${JSON.stringify(smartDevices)}`);
1917
+ // Check KEY
1918
+ return this.checkUrlKey()
1919
+ .then(() => this.subscribeAllIds(cb))
1920
+ .catch(err => {
1921
+ this.adapter.config.yandexAlisa &&
1922
+ this.adapter.log.warn(
1923
+ '[ALISA] Invalid URL Pro key. Status auto-update is disabled you can set states but receive states only manually: ' +
1924
+ err,
1925
+ );
1926
+ // call cb otherwise frontend get no results
1927
+ cb && cb();
1928
+ });
1929
+ });
1930
+ });
1931
+ }
1932
+
1933
+ getDevices() {
1934
+ const result = this.smartDevices.map(device => {
1935
+ return {
1936
+ name: device.attributes.friendly_name,
1937
+ main: { getId: device.STATE.getId, setId: device.STATE.setId },
1938
+ attributes: device.ATTRIBUTES
1939
+ ? device.ATTRIBUTES.map(a => {
1940
+ return { name: a.attribute, getId: a.getId, setId: a.setId };
1941
+ })
1942
+ : [],
1943
+ actions: device.context.capabilities.length
1944
+ ? device.context.capabilities.map(cap => {
1945
+ const capText = cap.type.replace('devices.capabilities.', '');
1946
+ const capTextUnit =
1947
+ capText + (cap.parameters && cap.parameters.unit ? '_' + cap.parameters.unit : '');
1948
+ if (!typesMapping[capTextUnit]) {
1949
+ this.adapter.log.debug(`[ALISA] No mapping found for ${capTextUnit}`);
1950
+ }
1951
+
1952
+ return typesMapping[capTextUnit] || capText;
1953
+ })
1954
+ : device.context.properties.map(cap => {
1955
+ const capText = cap.type.replace('devices.properties.', '');
1956
+ const capTextUnit = `${capText}_${cap.parameters.instance}${cap.parameters && cap.parameters.unit ? '_' + cap.parameters.unit : ''}`;
1957
+ if (!typesMapping[capTextUnit]) {
1958
+ this.adapter.log.debug(`[ALISA] No mapping found for ${capTextUnit}`);
1959
+ }
1960
+
1961
+ return typesMapping[capTextUnit] || capText;
1962
+ }),
1963
+ iobID: device.context.id,
1964
+ description: device.context.description,
1965
+ room: device.context.room,
1966
+ func: device.context.type.replace('devices.types.', '').toUpperCase(),
1967
+ };
1968
+ });
1969
+ this.adapter.log.debug(`[ALISA] Devices: ${JSON.stringify(result)}`);
1970
+ return result;
1971
+ }
1972
+
1973
+ getAll() {
1974
+ return this._updateDevices().then(smartDevices => {
1975
+ this.smartDevices = smartDevices;
1976
+ this.adapter.log.debug(`[ALISA] SmartDevices: ${JSON.stringify(smartDevices)}`);
1977
+ });
1978
+ }
1979
+
1980
+ _getSmartDeviceData(entity) {
1981
+ return new Promise(resolve => {
1982
+ if (entity.context) {
1983
+ if (entity.COMMANDS && entity.COMMANDS.get_state) {
1984
+ entity.COMMANDS.get_state(entity).then(() => resolve(entity.context));
1985
+ } else {
1986
+ resolve(entity.context);
1987
+ }
1988
+ } else {
1989
+ resolve();
1990
+ }
1991
+ });
1992
+ }
1993
+
1994
+ _getSmartDeviceState(context) {
1995
+ const result = {
1996
+ id: context.id,
1997
+ };
1998
+
1999
+ if (context.capabilities && context.capabilities.length) {
2000
+ result.capabilities = context.capabilities
2001
+ .map(c => {
2002
+ if (c.state) {
2003
+ return { type: c.type, state: c.state };
2004
+ }
2005
+ })
2006
+ .filter(c => c);
2007
+ }
2008
+
2009
+ if (context.properties && context.properties.length) {
2010
+ result.properties = context.properties
2011
+ .map(c => {
2012
+ if (c.state) {
2013
+ return { type: c.type, state: c.state };
2014
+ }
2015
+ })
2016
+ .filter(c => c);
2017
+ }
2018
+
2019
+ return result;
2020
+ }
2021
+
2022
+ getSmartDevices() {
2023
+ return this.getAll().then(() => {
2024
+ const result = [];
2025
+ this.smartDevices.forEach(entity => {
2026
+ result.push(this._getSmartDeviceData(entity));
2027
+ });
2028
+ return Promise.all(result);
2029
+ });
2030
+ }
2031
+
2032
+ querySmartDevicesByIds(ids) {
2033
+ return new Promise(resolve => {
2034
+ const result = [];
2035
+ const exists = [];
2036
+ this.smartDevices
2037
+ .filter(entity => ids.includes(entity.context.id))
2038
+ .forEach(entity => {
2039
+ exists.push(entity.context.id);
2040
+ result.push(this._getSmartDeviceData(entity));
2041
+ });
2042
+ ids.forEach(id => {
2043
+ if (!exists.includes(id)) {
2044
+ result.push({
2045
+ id: id,
2046
+ error_code: 'DEVICE_NOT_FOUND',
2047
+ error_message: 'Device not found',
2048
+ });
2049
+ }
2050
+ });
2051
+ resolve(Promise.all(result));
2052
+ });
2053
+ }
2054
+
2055
+ _updateDevices() {
2056
+ return this._readObjects().then(data => {
2057
+ const { objects, enums } = data;
2058
+ let ids = Object.keys(objects);
2059
+
2060
+ this.enums = [];
2061
+ this.smartDevices = {};
2062
+ this.enums = [];
2063
+ this.usedIds = [];
2064
+ this.keys = [];
2065
+
2066
+ ids.sort();
2067
+
2068
+ // Build overlap from rooms and functions
2069
+ let rooms = [];
2070
+ let funcs = [];
2071
+ let smartName;
2072
+ Object.keys(enums).forEach(id => {
2073
+ smartName = this.getSmartName(enums[id]);
2074
+ if (id.match(/^enum\.rooms\./) && smartName !== 'ignore' && smartName !== false) {
2075
+ rooms.push(id);
2076
+ } else if (id.match(/^enum\.functions\./) && smartName !== 'ignore' && smartName !== false) {
2077
+ funcs.push(id);
2078
+ }
2079
+ });
2080
+
2081
+ let result = [];
2082
+
2083
+ let roomNames = {};
2084
+ funcs.forEach(funcId => {
2085
+ const func = enums[funcId];
2086
+ if (
2087
+ !func.common ||
2088
+ !func.common.members ||
2089
+ typeof func.common.members !== 'object' ||
2090
+ !func.common.members.length
2091
+ )
2092
+ return;
2093
+
2094
+ // Get the name of function (with language and if name is empty)
2095
+ let funcName = this.getSmartName(func);
2096
+ funcName = funcName || func.common.name;
2097
+ if (funcName && typeof funcName === 'object') {
2098
+ funcName = funcName[this.lang] || funcName.en;
2099
+ }
2100
+ if (!funcName) {
2101
+ funcName = funcId.substring('enum.functions.'.length);
2102
+ funcName = funcName[0].toUpperCase() + funcName.substring(1);
2103
+ }
2104
+
2105
+ func.common.members.forEach(id => {
2106
+ rooms.forEach(roomId => {
2107
+ const room = enums[roomId];
2108
+ if (
2109
+ !room.common ||
2110
+ !room.common.members ||
2111
+ typeof func.common.members !== 'object' ||
2112
+ !room.common.members.length
2113
+ )
2114
+ return;
2115
+
2116
+ // If state or channel is in some room and in some function
2117
+ const pos = room.common.members.indexOf(id);
2118
+ if (pos !== -1) {
2119
+ // find name for room if not found earlier
2120
+ if (!roomNames[roomId]) {
2121
+ // Get the name of function (with language and if name is empty)
2122
+ let roomName = this.getSmartName(room);
2123
+ roomName = roomName || room.common.name;
2124
+ if (roomName && typeof roomName === 'object') {
2125
+ roomName = roomName[this.lang] || roomName.en;
2126
+ }
2127
+ if (!roomName) {
2128
+ roomName = roomId.substring('enum.rooms.'.length);
2129
+ roomName = roomName[0].toUpperCase() + roomName.substring(1);
2130
+ }
2131
+ roomNames[roomId] = roomName;
2132
+ }
2133
+
2134
+ this.processState(ids, objects, id, roomNames[roomId], funcName, result);
2135
+ }
2136
+ });
2137
+ });
2138
+ });
2139
+
2140
+ // scan alias.* and linkeddevices.*
2141
+ for (let i = 0; i < ids.length; i++) {
2142
+ if (ids[i] < 'alias.') {
2143
+ continue;
2144
+ }
2145
+ if (ids[i] > 'linkeddevices.\u9999') {
2146
+ break;
2147
+ }
2148
+
2149
+ if (
2150
+ (ids[i].startsWith('alias.') || ids[i].startsWith('linkeddevices.')) &&
2151
+ objects[ids[i]] &&
2152
+ (objects[ids[i]].type === 'device' || objects[ids[i]].type === 'channel')
2153
+ ) {
2154
+ this.processState(
2155
+ ids,
2156
+ objects,
2157
+ ids[i],
2158
+ roomsT(this.lang, 'undefined'),
2159
+ funcsT(this.lang, 'undefined'),
2160
+ result,
2161
+ );
2162
+ }
2163
+ }
2164
+
2165
+ this.usedIds = null;
2166
+ this.keys = null;
2167
+
2168
+ result.forEach(entity =>
2169
+ this.adapter.log.debug(`[ALISA] ${entity.context.id} => ${entity.context.type} ${entity.context.name}`),
2170
+ );
2171
+
2172
+ return result;
2173
+ });
2174
+ }
2175
+
2176
+ _doSmartDeviceAction(entity, data) {
2177
+ return new Promise(resolve => {
2178
+ if (entity.COMMANDS && entity.COMMANDS.set_state) {
2179
+ entity.COMMANDS.set_state(entity, data).then(res => resolve(res));
2180
+ } else {
2181
+ resolve({
2182
+ id: data.id,
2183
+ action_result: {
2184
+ status: 'ERROR',
2185
+ error_code: 'INVALID_ACTION',
2186
+ error_message: 'Device has not this action',
2187
+ },
2188
+ });
2189
+ }
2190
+ });
2191
+ }
2192
+
2193
+ checkUrlKey(forceCheck) {
2194
+ const now = Date.now();
2195
+ if (this.urlKey && (!this.keyPromise || now - this.keyPromiseTime > 900000)) {
2196
+ /* 15 Minutes */
2197
+ this.keyPromiseTime = now;
2198
+ const url = `${URL_STATUS}?user=${encodeURIComponent(this.adapter.config.login)}&urlKey=${encodeURIComponent(this.urlKey.key)}&p=${PROTOCOL_VERSION}&v=${this.adapter.version}`;
2199
+ this.keyPromise = axios
2200
+ .get(url, { validateStatus: status => status === 200 })
2201
+ .then(response => {
2202
+ this.adapter.log.debug(`[ALISA] CHECK URL reported: ${JSON.stringify(response.data)}`);
2203
+ this.urlKeyOk = true;
2204
+ })
2205
+ .catch(error => {
2206
+ let errorMessage;
2207
+ if (error.response) {
2208
+ errorMessage = error.response.data || error.response.status;
2209
+ } else if (error.request) {
2210
+ errorMessage = 'No answer';
2211
+ } else {
2212
+ errorMessage = error.message;
2213
+ }
2214
+ this.adapter.config.yandexAlisa &&
2215
+ this.adapter.log.error(
2216
+ '[ALISA] Url Key error. Alisa Request and Response are working. But device states are not reported automatically. If you have pro license please try to delete iot.0.certs: ' +
2217
+ errorMessage,
2218
+ );
2219
+ });
2220
+ } else {
2221
+ this.keyPromise = this.keyPromise || Promise.resolve();
2222
+ }
2223
+
2224
+ return this.keyPromise;
2225
+ }
2226
+
2227
+ async updateState(id, state) {
2228
+ const now = Date.now();
2229
+ // Only pro with valid license can update states
2230
+ if (
2231
+ !this.urlKeyOk ||
2232
+ (this.unknownDevices[id] && now - this.unknownDevices[id] < RETRY_UNKNOWN_DEVICES_INTERVAL)
2233
+ ) {
2234
+ return;
2235
+ }
2236
+
2237
+ if (this.urlKey) {
2238
+ let i = 0;
2239
+ while (i < this.rateCalculation.length) {
2240
+ if (now - this.rateCalculation[i] < 60000) {
2241
+ break;
2242
+ }
2243
+ i++;
2244
+ }
2245
+ if (i) {
2246
+ if (i < this.rateCalculation.length) {
2247
+ this.rateCalculation.splice(0, i);
2248
+ } else {
2249
+ this.rateCalculation = [];
2250
+ }
2251
+ }
2252
+
2253
+ if (this.rateCalculation.length > 60) {
2254
+ return this.adapter.log.warn(
2255
+ `[ALISA] Sending too fast: ${this.rateCalculation.length} in last minute!`,
2256
+ );
2257
+ }
2258
+
2259
+ this.rateCalculation.push(now);
2260
+
2261
+ this.querySmartDevicesByIds([id])
2262
+ .then(devices => {
2263
+ const json = {
2264
+ ts: Date.now() / 100,
2265
+ payload: {
2266
+ user_id: this.user_id,
2267
+ devices: devices.map(d => this._getSmartDeviceState(d)),
2268
+ },
2269
+ };
2270
+ this.adapter.log.debug(`[ALISA] Response: ${JSON.stringify(json)}`);
2271
+ const url = `${URL_STATUS}?user=${encodeURIComponent(this.adapter.config.login)}&urlKey=${encodeURIComponent(this.urlKey.key)}&p=${PROTOCOL_VERSION}&v=${this.adapter.version}`;
2272
+ return axios.post(url, json, { validateStatus: status => status === 200 }).then(response => {
2273
+ if (this.unknownDevices[id]) {
2274
+ delete this.unknownDevices[id];
2275
+ }
2276
+ this.adapter.log.debug(
2277
+ `[ALISA] Status reported: ${JSON.stringify(json)} ${JSON.stringify(response.data)}`,
2278
+ );
2279
+ });
2280
+ })
2281
+ .catch(error => {
2282
+ if (error.response && error.response.status === 404) {
2283
+ this.adapter.log.error(`[ALISA] device ${id} is unknown to alisa`);
2284
+ this.unknownDevices[id] = Date.now();
2285
+ } else if (error.response && error.response.status === 401) {
2286
+ this.adapter.log.error(`[ALISA] auth error: ${JSON.stringify(error.response.body)}`);
2287
+ this.urlKeyOk = false; // invalidate urlKey
2288
+ } else if (error.response && error.response.status === 410) {
2289
+ this.adapter.log.error(
2290
+ `[ALISA] invalid protocol version: ${JSON.stringify(error.response.body)}`,
2291
+ );
2292
+ this.urlKeyOk = false; // invalidate urlKey
2293
+ } else {
2294
+ let errorMessage;
2295
+ if (error.response) {
2296
+ errorMessage = error.response.data || error.response.status;
2297
+ } else if (error.request) {
2298
+ errorMessage = 'No answer';
2299
+ } else {
2300
+ errorMessage = error.message;
2301
+ }
2302
+
2303
+ this.adapter.log.error(`[ALISA] Cannot updateState: ${errorMessage}`);
2304
+ this.adapter.log.debug(`[ALISA] ${JSON.stringify(error.response && error.response.body)}`);
2305
+ }
2306
+ });
2307
+ }
2308
+ }
2309
+
2310
+ doAction(deviceData) {
2311
+ return new Promise(resolve => {
2312
+ const entity = this.smartDevices.find(entity => deviceData.id === entity.context.id);
2313
+ entity && resolve(this._doSmartDeviceAction(entity, deviceData));
2314
+ resolve();
2315
+ });
2316
+ }
2317
+
2318
+ async process(request, isEnabled) {
2319
+ if (!request) {
2320
+ this.adapter.log.error('[ALISA] Invalid request: no request!');
2321
+ return { error: 'Invalid request: no request!' };
2322
+ }
2323
+
2324
+ if (!isEnabled) {
2325
+ return { error: textsT(this.lang, 'The service deactivated'), errorCode: 500 };
2326
+ }
2327
+
2328
+ if (!request.alisa) {
2329
+ return { error: textsT(this.lang, 'missing inputs'), errorCode: 400 };
2330
+ }
2331
+
2332
+ this.adapter.log.debug(`[ALISA] Received ${JSON.stringify(request.alisa)}`);
2333
+ // remove first word. It can be changed in the future.
2334
+ let url = request.alisa.replace(/^\/[-_\w\d]+\//, '/');
2335
+ switch (url) {
2336
+ // https://yandex.ru/dev/dialogs/alice/doc/smart-home/reference/check-docpage/
2337
+ case '/v1.0':
2338
+ return {};
2339
+
2340
+ // https://yandex.ru/dev/dialogs/alice/doc/smart-home/reference/get-devices-docpage/
2341
+ case '/v1.0/user/devices': {
2342
+ const result = {
2343
+ request_id: uuid(),
2344
+ payload: {
2345
+ user_id: this.user_id,
2346
+ devices: await this.getSmartDevices(),
2347
+ },
2348
+ };
2349
+ this.adapter.log.debug(`[ALISA] Response: ${JSON.stringify(result)}`);
2350
+ return result;
2351
+ }
2352
+
2353
+ // https://yandex.ru/dev/dialogs/alice/doc/smart-home/reference/post-devices-query-docpage/
2354
+ case '/v1.0/user/devices/query':
2355
+ const queryDevices = request.devices || [];
2356
+ const ids = [];
2357
+
2358
+ queryDevices.forEach(element => ids.push(element.id));
2359
+
2360
+ if (ids) {
2361
+ const devices = await this.querySmartDevicesByIds(ids);
2362
+ const queryResult = {
2363
+ request_id: uuid(),
2364
+ payload: {
2365
+ user_id: this.user_id,
2366
+ devices: devices.map(d => this._getSmartDeviceState(d)),
2367
+ },
2368
+ };
2369
+ if (!devices.length) {
2370
+ queryResult.payload.devices = queryDevices;
2371
+ }
2372
+ const result = queryResult;
2373
+ this.adapter.log.debug(`[ALISA] Response: ${JSON.stringify(result)}`);
2374
+ return result;
2375
+ }
2376
+ break;
2377
+
2378
+ // https://yandex.ru/dev/dialogs/alice/doc/smart-home/reference/post-action-docpage/
2379
+ case '/v1.0/user/devices/action': {
2380
+ const actionDevices = request.payload ? request.payload.devices || [] : [];
2381
+ const devices = [];
2382
+
2383
+ for (let i = 0; i < actionDevices.length; i++) {
2384
+ devices.push(await this.doAction(actionDevices[i]));
2385
+ }
2386
+
2387
+ const result = {
2388
+ request_id: uuid(),
2389
+ payload: {
2390
+ devices,
2391
+ },
2392
+ };
2393
+ this.adapter.log.debug(`[ALISA] Response: ${JSON.stringify(result)}`);
2394
+ return result;
2395
+ }
2396
+
2397
+ // https://yandex.ru/dev/dialogs/alice/doc/smart-home/reference/unlink-docpage/
2398
+ case '/alisaIot/v1.0/user/unlink':
2399
+ return {};
2400
+
2401
+ default:
2402
+ return { error: textsT(this.lang, 'missing data'), errorCode: 400 };
2403
+ }
2404
+
2405
+ return { error: textsT(this.lang, 'missing inputs'), errorCode: 400 };
2406
+ }
2407
+ }
2408
+
2409
+ module.exports = YandexAlisa;