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,3322 @@
1
+ const axios = require('axios');
2
+ const ChannelDetector = require('@iobroker/type-detector');
3
+
4
+ const textsT = require('./texts').default;
5
+ const roomsT = require('./rooms').default;
6
+ const funcsT = require('./functions').default;
7
+
8
+ const PROTOCOL_VERSION = 1;
9
+ const RETRY_UNKNOWN_DEVICES_INTERVAL = 10 * 60000; // 10 minutes
10
+
11
+ const traitEnum = {
12
+ 'action.devices.traits.ArmDisarm': {
13
+ command: 'arm',
14
+ defaultAttributes: `{
15
+ "availableArmLevels": {
16
+ "levels": [
17
+ {
18
+ "level_name": "L1",
19
+ "level_values": [
20
+ {
21
+ "level_synonym": [
22
+ "home and guarding",
23
+ "SL1"
24
+ ],
25
+ "lang": "en"
26
+ },
27
+ {
28
+ "level_synonym": [
29
+ "zuhause und bewachen",
30
+ "SL1"
31
+ ],
32
+ "lang": "de"
33
+ },
34
+ {
35
+ "level_synonym": [
36
+ "дома и охрана",
37
+ "SL1"
38
+ ],
39
+ "lang": "ru"
40
+ }
41
+ ]
42
+ },
43
+ {
44
+ "level_name": "L2",
45
+ "level_values": [
46
+ {
47
+ "level_synonym": [
48
+ "away and guarding",
49
+ "SL2"
50
+ ],
51
+ "lang": "en"
52
+ },
53
+ {
54
+ "level_synonym": [
55
+ "weg und bewachen",
56
+ "SL2"
57
+ ],
58
+ "lang": "de"
59
+ },
60
+ {
61
+ "level_synonym": [
62
+ "все ушли и охрана",
63
+ "SL2"
64
+ ],
65
+ "lang": "ru"
66
+ }
67
+ ]
68
+ }
69
+ ],
70
+ "ordered": true
71
+ }
72
+ }`,
73
+ },
74
+ 'action.devices.traits.Brightness': {
75
+ command: 'brightness',
76
+ defaultAttributes: '{}',
77
+ },
78
+ 'action.devices.traits.CameraStream': {
79
+ command: 'cameraStream',
80
+ defaultAttributes: `{
81
+ "cameraStreamSupportedProtocols": [
82
+ "hls",
83
+ "dash"
84
+ ],
85
+ "cameraStreamNeedAuthToken": true,
86
+ "cameraStreamNeedDrmEncryption": false
87
+ }`,
88
+ },
89
+ 'action.devices.traits.ColorSetting_temperature': {
90
+ command: 'color_temperature',
91
+ defaultAttributes: `{
92
+ "colorModel": "rgb",
93
+ "colorTemperatureRange": {
94
+ "temperatureMinK": 2000,
95
+ "temperatureMaxK": 9000
96
+ },
97
+ "commandOnlyColorSetting": false
98
+ }`,
99
+ },
100
+ 'action.devices.traits.ColorSetting_spectrumRGB': {
101
+ command: 'color_spectrumRGB',
102
+ defaultAttributes: `{
103
+ "colorModel": "rgb",
104
+ "colorTemperatureRange": {
105
+ "temperatureMinK": 2000,
106
+ "temperatureMaxK": 9000
107
+ },
108
+ "commandOnlyColorSetting": false
109
+ }`,
110
+ },
111
+ 'action.devices.traits.Dock': {
112
+ command: 'command',
113
+ defaultAttributes: '{}',
114
+ },
115
+ 'action.devices.traits.FanSpeed': {
116
+ command: 'fanSpeed',
117
+ getter: 'currentFanSpeedSetting',
118
+ defaultAttributes: `{
119
+ "availableFanSpeeds": {
120
+ "speeds": [
121
+ {
122
+ "speed_name": "Low",
123
+ "speed_values": [
124
+ {
125
+ "speed_synonym": [
126
+ "low",
127
+ "slow"
128
+ ],
129
+ "lang": "en"
130
+ },
131
+ {
132
+ "speed_synonym": [
133
+ "niedrig",
134
+ "schleppend"
135
+ ],
136
+ "lang": "de"
137
+ },
138
+ {
139
+ "speed_synonym": [
140
+ "вниз",
141
+ "медленно"
142
+ ],
143
+ "lang": "ru"
144
+ }
145
+ ]
146
+ },
147
+ {
148
+ "speed_name": "High",
149
+ "speed_values": [
150
+ {
151
+ "speed_synonym": [
152
+ "high"
153
+ ],
154
+ "lang": "en"
155
+ },
156
+ {
157
+ "speed_synonym": [
158
+ "hoch"
159
+ ],
160
+ "lang": "de"
161
+ },
162
+ {
163
+ "speed_synonym": [
164
+ "вверх"
165
+ ],
166
+ "lang": "ru"
167
+ }
168
+ ]
169
+ }
170
+ ],
171
+ "ordered": true
172
+ },
173
+ "reversible": true
174
+ }`,
175
+ },
176
+ 'action.devices.traits.LightEffects': {
177
+ command: 'command',
178
+ defaultAttributes: `{
179
+ "supportedEffects": [
180
+ "colorLoop"
181
+ ]
182
+ }`,
183
+ },
184
+ 'action.devices.traits.Locator': {
185
+ command: 'command',
186
+ defaultAttributes: '{}',
187
+ },
188
+ 'action.devices.traits.LockUnlock': {
189
+ command: 'lock',
190
+ getter: 'isLocked',
191
+ defaultAttributes: '{}',
192
+ },
193
+ 'action.devices.traits.Modes': {
194
+ command: 'updateModeSettings',
195
+ defaultAttributes: `{
196
+ "availableModes": [
197
+ {
198
+ "name": "load",
199
+ "name_values": [
200
+ {
201
+ "name_synonym": [
202
+ "load",
203
+ "size",
204
+ "load size"
205
+ ],
206
+ "lang": "en"
207
+ },
208
+ {
209
+ "name_synonym": [
210
+ "beladung",
211
+ "gewicht",
212
+ "beladungsgewicht"
213
+ ],
214
+ "lang": "de"
215
+ },
216
+ {
217
+ "name_synonym": [
218
+ "загрузка",
219
+ "размер",
220
+ "размер загрузки"
221
+ ],
222
+ "lang": "ru"
223
+ }
224
+ ],
225
+ "settings": [
226
+ {
227
+ "setting_name": "small",
228
+ "setting_values": [
229
+ {
230
+ "setting_synonym": [
231
+ "small",
232
+ "half"
233
+ ],
234
+ "lang": "en"
235
+ },
236
+ {
237
+ "setting_synonym": [
238
+ "klein",
239
+ "hälfte"
240
+ ],
241
+ "lang": "de"
242
+ },
243
+ {
244
+ "setting_synonym": [
245
+ "малый",
246
+ "половниа"
247
+ ],
248
+ "lang": "ru"
249
+ }
250
+ ]
251
+ },
252
+ {
253
+ "setting_name": "large",
254
+ "setting_values": [
255
+ {
256
+ "setting_synonym": [
257
+ "large",
258
+ "full"
259
+ ],
260
+ "lang": "en"
261
+ },
262
+ {
263
+ "setting_synonym": [
264
+ "große",
265
+ "voll"
266
+ ],
267
+ "lang": "de"
268
+ },
269
+ {
270
+ "setting_synonym": [
271
+ "большая",
272
+ "полная"
273
+ ],
274
+ "lang": "ru"
275
+ }
276
+ ]
277
+ }
278
+ ],
279
+ "ordered": true
280
+ }
281
+ ]
282
+ }`,
283
+ },
284
+ 'action.devices.traits.OnOff': {
285
+ command: 'on',
286
+ defaultAttributes: `{
287
+ "commandOnlyOnOff": false
288
+ }`,
289
+ },
290
+ 'action.devices.traits.OpenClose': {
291
+ command: 'openPercent',
292
+ defaultAttributes: `{
293
+ "openDirection": [
294
+ "UP",
295
+ "DOWN"
296
+ ]
297
+ }`,
298
+ },
299
+ 'action.devices.traits.RunCycle': {
300
+ command: 'currentTotalRemainingTime',
301
+ defaultAttributes: '{}',
302
+ },
303
+ 'action.devices.traits.Scene': {
304
+ command: 'ActivateScene',
305
+ defaultAttributes: `{
306
+ "sceneReversible": true
307
+ }`,
308
+ },
309
+ 'action.devices.traits.StartStop': {
310
+ command: 'start',
311
+ getter: 'isRunning',
312
+ defaultAttributes: ` {
313
+ "pausable": false,
314
+ "availableZones": [
315
+ "kitchen",
316
+ "living room",
317
+ "master bedroom"
318
+ ]
319
+ }`,
320
+ },
321
+ 'action.devices.traits.TemperatureControl_temperatureSetpointCelsius': {
322
+ command: 'temperature',
323
+ getter: 'temperatureSetpointCelsius',
324
+ defaultAttributes: `{
325
+ "temperatureRange": {
326
+ "minThresholdCelsius": 30,
327
+ "maxThresholdCelsius": 100
328
+ },
329
+ "temperatureStepCelsius": 1,
330
+ "temperatureUnitForUX": "C"
331
+ }`,
332
+ },
333
+ 'action.devices.traits.TemperatureControl_temperatureAmbientCelsius': {
334
+ command: '',
335
+ getter: 'temperatureAmbientCelsius',
336
+ defaultAttributes: '{}',
337
+ },
338
+ 'action.devices.traits.TemperatureSetting_thermostatTemperatureSetpoint': {
339
+ command: 'thermostatTemperatureSetpoint',
340
+ defaultAttributes: `{
341
+ "availableThermostatModes": "off,heat,cool,on",
342
+ "thermostatTemperatureUnit": "C"
343
+ }`,
344
+ },
345
+ 'action.devices.traits.TemperatureSetting_thermostatMode': {
346
+ command: 'thermostatMode',
347
+ defaultAttributes: `{}`,
348
+ },
349
+ 'action.devices.traits.TemperatureSetting_thermostatTemperatureAmbient': {
350
+ command: '',
351
+ getter: 'thermostatTemperatureAmbient',
352
+ defaultAttributes: `{}`,
353
+ },
354
+ 'action.devices.traits.TemperatureSetting_thermostatHumidityAmbient': {
355
+ command: '',
356
+ getter: 'thermostatHumidityAmbient',
357
+ defaultAttributes: `{}`,
358
+ },
359
+ 'action.devices.traits.Timer': {
360
+ command: 'timerRemainingSec',
361
+ defaultAttributes: `{
362
+ "temperatureRange": {
363
+ "minThresholdCelsius": 65.5,
364
+ "maxThresholdCelcius": 288
365
+ },
366
+ "temperatureUnitForUX": "C",
367
+ "pausable": false,
368
+ "maxTimerLimitSec": 7200,
369
+ "commandOnlyTimer": false
370
+ }`,
371
+ },
372
+ 'action.devices.traits.Toggles': {
373
+ command: 'updateToggleSettings',
374
+ defaultAttributes: `{
375
+ "availableToggles": [
376
+ {
377
+ "name": "sterilization",
378
+ "name_values": [
379
+ {
380
+ "name_synonym": [
381
+ "bio-clean",
382
+ "ultrasound"
383
+ ],
384
+ "lang": "en"
385
+ }
386
+ ]
387
+ }
388
+ ]
389
+ }`,
390
+ },
391
+ 'action.devices.traits.Volume': {
392
+ command: 'volumeLevel',
393
+ defaultAttributes: '{}',
394
+ },
395
+ };
396
+
397
+ const customToTraitEnum = {
398
+ on: 'action.devices.traits.OnOff',
399
+ openClose: 'action.devices.traits.OpenClose',
400
+ openDirection: 'action.devices.traits.OpenClose',
401
+ openPercent: 'action.devices.traits.OpenClose',
402
+ isLocked: 'action.devices.traits.LockUnlock',
403
+ brightness: 'action.devices.traits.Brightness',
404
+ thermostatTemperatureSetpoint: 'action.devices.traits.TemperatureSetting_thermostatTemperatureSetpoint',
405
+ thermostatTemperatureAmbient: 'action.devices.traits.TemperatureSetting_thermostatTemperatureAmbient',
406
+ thermostatHumidityAmbient: 'action.devices.traits.TemperatureSetting_thermostatHumidityAmbient',
407
+ color_spectrumRGB: 'action.devices.traits.ColorSetting_spectrumRGB',
408
+ color_hue: 'action.devices.traits.ColorSetting_spectrumRGB',
409
+ color_saturation: 'action.devices.traits.ColorSetting_spectrumRGB',
410
+ color_temperature: 'action.devices.traits.ColorSetting_temperature',
411
+ };
412
+
413
+ const URL_STATUS = 'https://gstatus.iobroker.in/v1/googleHomeStatus';
414
+
415
+ const ignoreIds = [/^system\./, /^script\./];
416
+
417
+ class GoogleHome {
418
+ constructor(adapter, urlKey) {
419
+ this.adapter = adapter;
420
+ this.urlKey = urlKey;
421
+ this.lang = 'de';
422
+ this.agentUserId = adapter.config.login.replace(/[^-_:a-zA-Z1-9]/g, '_');
423
+
424
+ this.smartDevices = {};
425
+ this.smartDevicesSentToGoogle = {};
426
+ this.enums = [];
427
+ this.usedIds = [];
428
+ this.detector = new ChannelDetector.default();
429
+ this.urlKeyOk = false;
430
+ this.keyPromise = null;
431
+ this.keyPromiseTime = null;
432
+ this.unknownDevices = {};
433
+ this.reportedStates = {};
434
+ this.rateCalculation = [];
435
+
436
+ this.tasksTimer = null;
437
+
438
+ this.converter = {
439
+ [ChannelDetector.Types.socket]: this.processSocket.bind(this),
440
+ [ChannelDetector.Types.light]: this.processLight.bind(this),
441
+ [ChannelDetector.Types.info]: this.processInfo.bind(this),
442
+ [ChannelDetector.Types.dimmer]: this.processDimmer.bind(this),
443
+ [ChannelDetector.Types.rgbSingle]: this.processRgbSingle.bind(this),
444
+ [ChannelDetector.Types.rgb]: this.processRgb.bind(this),
445
+ [ChannelDetector.Types.hue]: this.processHue.bind(this),
446
+ [ChannelDetector.Types.ct]: this.processCT.bind(this),
447
+ [ChannelDetector.Types.temperature]: this.processTemperature.bind(this),
448
+ [ChannelDetector.Types.thermostat]: this.processThermostat.bind(this),
449
+ [ChannelDetector.Types.button]: this.processButton.bind(this),
450
+ [ChannelDetector.Types.windowTilt]: this.processWindowTilt.bind(this),
451
+ [ChannelDetector.Types.door]: this.processWindowTilt.bind(this),
452
+ [ChannelDetector.Types.window]: this.processWindowTilt.bind(this),
453
+ [ChannelDetector.Types.blind]: this.processBlind.bind(this),
454
+ [ChannelDetector.Types.slider]: this.processBlind.bind(this),
455
+ [ChannelDetector.Types.media]: this.processMedia.bind(this),
456
+ };
457
+ }
458
+
459
+ checkUrlKey() {
460
+ const now = Date.now();
461
+ if (!this.keyPromise || now - this.keyPromiseTime > 900000) {
462
+ /* 15 Minutes */
463
+ this.keyPromiseTime = now;
464
+
465
+ const url = `${URL_STATUS}?user=${encodeURIComponent(this.adapter.config.login)}&urlKey=${encodeURIComponent(this.urlKey ? this.urlKey.key : '')}&p=${PROTOCOL_VERSION}&v=${this.adapter.version}`;
466
+
467
+ this.keyPromise = axios
468
+ .get(url, { validateStatus: status => status === 200 })
469
+ .then(response => {
470
+ this.adapter.log.debug(`[GHOME] CHECK URL reported: ${JSON.stringify(response.data)}`);
471
+ this.urlKeyOk = true;
472
+ })
473
+ .catch(error => {
474
+ let errorMessage;
475
+ if (error.response) {
476
+ errorMessage = error.response.data || error.response.status;
477
+ } else if (error.request) {
478
+ errorMessage = 'No answer';
479
+ } else {
480
+ errorMessage = error.message;
481
+ }
482
+ this.adapter.config.googleHome &&
483
+ this.adapter.log.error(
484
+ `[GHOME] Url Key error. Google Request and Response are working. But device states are not reported automatically. If you have pro license please try to delete iot.0.certs: ${JSON.stringify(errorMessage)}`,
485
+ );
486
+ });
487
+ }
488
+
489
+ return this.keyPromise;
490
+ }
491
+
492
+ _subscribeAllIds(ids, cb) {
493
+ if (!ids || !ids.length) {
494
+ cb && cb();
495
+ } else {
496
+ const id = ids.shift();
497
+ if (!id) {
498
+ setImmediate(() => this._subscribeAllIds(ids, cb));
499
+ } else {
500
+ console.log(`[GHOME] Subscribe ${id}`);
501
+ this.adapter.subscribeForeignStates(id, () => setImmediate(() => this._subscribeAllIds(ids, cb)));
502
+ }
503
+ }
504
+ }
505
+
506
+ _unsubscribeAllIds(ids, cb) {
507
+ if (!ids || !ids.length) {
508
+ cb && cb();
509
+ } else {
510
+ const id = ids.shift();
511
+ if (!id) {
512
+ setImmediate(() => this._unsubscribeAllIds(ids, cb));
513
+ } else {
514
+ console.log(`[GHOME] Unsubscribe ${id}`);
515
+ this.adapter.unsubscribeForeignStates(id, () => setImmediate(() => this._unsubscribeAllIds(ids, cb)));
516
+ }
517
+ }
518
+ }
519
+
520
+ unsubscribeAllIds(cb) {
521
+ const ids = [];
522
+ this.smartDevices &&
523
+ Object.keys(this.smartDevices).forEach(devId => {
524
+ const custom = this.smartDevices[devId].customData;
525
+ custom &&
526
+ Object.keys(custom).forEach(attr => {
527
+ attr && attr.startsWith('get_') && !ids.includes(custom[attr]) && ids.push(custom[attr]);
528
+ });
529
+ });
530
+
531
+ this.adapter.log.debug(`[GHOME] Unsubscribe ${ids.length} states for google home`);
532
+ this._unsubscribeAllIds(ids, () => {
533
+ this.adapter.log.debug(`[GHOME] Unsubscribe done`);
534
+ cb && cb();
535
+ });
536
+ }
537
+
538
+ subscribeAllIds(cb) {
539
+ const ids = [];
540
+ this.smartDevices &&
541
+ Object.keys(this.smartDevices).forEach(devId => {
542
+ const custom = this.smartDevices[devId].customData;
543
+ custom &&
544
+ Object.keys(custom).forEach(
545
+ attr =>
546
+ attr && attr.startsWith('get_') && !ids.includes(custom[attr]) && ids.push(custom[attr]),
547
+ );
548
+ });
549
+
550
+ this.adapter.log.debug(`[GHOME] Subscribe ${ids.length} states for google home`);
551
+
552
+ this._subscribeAllIds(ids, () => {
553
+ this.adapter.log.debug(`[GHOME] Subscribe done`);
554
+ cb && cb();
555
+ });
556
+ }
557
+
558
+ getObjectName(obj, onlySimpleName) {
559
+ let name = '';
560
+ // extract from smartName the name
561
+ if (!onlySimpleName) {
562
+ if (this.adapter.config.noCommon) {
563
+ if (
564
+ obj.common &&
565
+ obj.common.custom &&
566
+ obj.common.custom[this.adapter.namespace] &&
567
+ obj.common.custom[this.adapter.namespace].smartName &&
568
+ obj.common.custom[this.adapter.namespace].smartName !== 'ignore'
569
+ ) {
570
+ name = obj.common.custom[this.adapter.namespace].smartName;
571
+ }
572
+ } else {
573
+ if (
574
+ obj.common &&
575
+ obj.common.smartName &&
576
+ obj.common.smartName !== true &&
577
+ obj.common.smartName !== 'ignore'
578
+ ) {
579
+ name = obj.common.smartName;
580
+ }
581
+ }
582
+ }
583
+ // if no smart name found, get the normal key
584
+ if (!name && obj && obj.common && obj.common.name) {
585
+ name = obj.common.name;
586
+ }
587
+
588
+ if (name && typeof name === 'object') {
589
+ name = name[this.lang] || name['en'];
590
+ }
591
+
592
+ if (!name && obj) {
593
+ name = obj._id.split('.').pop();
594
+ }
595
+
596
+ return name;
597
+ }
598
+
599
+ checkName(name, obj, room, func) {
600
+ if (!name) {
601
+ name = name || this.getObjectName(obj) || '';
602
+ name = name.replace(/[^a-zA-ZöäüßÖÄÜа-яА-Я0-9ÉéÈèÀàÂâÊêÙùÛûÇçÎîËëÏïŸÿÔôŒœãõìòáíóú]/g, ' ');
603
+ let _name = name.toLowerCase();
604
+ let pos;
605
+ if (room) {
606
+ pos = _name.indexOf(room.toLowerCase());
607
+ if (pos !== -1) {
608
+ name = name.substring(0, pos) + name.substring(pos + room.length);
609
+ _name = _name.substring(0, pos) + _name.substring(pos + room.length);
610
+ }
611
+ }
612
+ if (func) {
613
+ pos = _name.indexOf(func.toLowerCase());
614
+ if (pos !== -1) {
615
+ name = name.substring(0, pos) + name.substring(pos + func.length);
616
+ }
617
+ }
618
+ name = name.replace(/\s\s+/g, ' ').trim();
619
+ }
620
+ return name || func;
621
+ }
622
+
623
+ processSocket(id, control, name, room, func, obj) {
624
+ const setOnOffState = control.states.find(state => state.name === 'SET' && state.id);
625
+ const set_on = setOnOffState && setOnOffState.id;
626
+
627
+ const getOnOffState = control.states.find(state => state.name === 'ACTUAL' && state.id);
628
+ const get_on = (getOnOffState && getOnOffState.id) || set_on;
629
+
630
+ name = this.checkName(name, obj, room, func);
631
+
632
+ return {
633
+ type: 'action.devices.types.OUTLET',
634
+ traits: ['action.devices.traits.OnOff'],
635
+ name: {
636
+ defaultNames: [room + 'steckdose'],
637
+ name: this.getObjectName(obj) || name,
638
+ nicknames: [name],
639
+ },
640
+ willReportState: true,
641
+ roomHint: room || '',
642
+ deviceInfo: {
643
+ manufacturer: 'ioBroker',
644
+ model: id.split('.')[0],
645
+ },
646
+ customData: {
647
+ set_on,
648
+ get_on,
649
+ },
650
+ id,
651
+ };
652
+ }
653
+
654
+ processLight(id, control, name, room, func, obj) {
655
+ const setOnOffState = control.states.find(state => state.name === 'SET' && state.id);
656
+ const set_on = setOnOffState && setOnOffState.id;
657
+
658
+ const getOnOffState = control.states.find(state => state.name === 'ACTUAL' && state.id);
659
+ const get_on = (getOnOffState && getOnOffState.id) || set_on;
660
+
661
+ const traits = ['action.devices.traits.OnOff'];
662
+ name = this.checkName(name, obj, room, func);
663
+
664
+ return {
665
+ type: 'action.devices.types.LIGHT',
666
+ traits,
667
+ name: {
668
+ defaultNames: [textsT(this.lang, 'light', room)],
669
+ name: this.getObjectName(obj) || name,
670
+ nicknames: [name],
671
+ },
672
+ willReportState: true,
673
+ roomHint: room || '',
674
+ deviceInfo: {
675
+ manufacturer: 'ioBroker',
676
+ model: id.split('.')[0],
677
+ },
678
+ customData: {
679
+ set_on,
680
+ get_on,
681
+ },
682
+ id,
683
+ };
684
+ }
685
+
686
+ processInfo(id, control, name, room, func, obj) {
687
+ const getOnOffState = control.states.find(state => state.name === 'ACTUAL' && state.id);
688
+ const get_on = getOnOffState && getOnOffState.id;
689
+
690
+ name = this.checkName(name, obj, room, func);
691
+
692
+ const get_isLocked = get_on;
693
+ const set_lock = get_on;
694
+ const set_openPercent = set_lock;
695
+ const get_openPercent = get_isLocked;
696
+
697
+ const traits = [];
698
+
699
+ traits.push('action.devices.traits.LockUnlock');
700
+ traits.push('action.devices.traits.OpenClose');
701
+
702
+ return {
703
+ type: 'action.devices.types.SENSOR',
704
+ traits: traits,
705
+ displayTraits: ['action.devices.traits.OpenClose'],
706
+ name: {
707
+ defaultNames: [room + func],
708
+ name: this.getObjectName(obj) || name,
709
+ nicknames: [name],
710
+ },
711
+ attributes: {
712
+ queryOnlyOpenClose: true,
713
+ },
714
+ displayAttributes: JSON.stringify({
715
+ queryOnlyOpenClose: true,
716
+ }),
717
+ willReportState: true,
718
+ roomHint: room || '',
719
+ deviceInfo: {
720
+ manufacturer: 'ioBroker',
721
+ model: id.split('.')[0],
722
+ },
723
+ customData: {
724
+ get_isLocked,
725
+ set_lock,
726
+ set_openPercent,
727
+ get_openPercent,
728
+ },
729
+ id,
730
+ };
731
+ }
732
+
733
+ processButton(id, control, name, room, func, obj) {
734
+ const setOnOffState = control.states.find(state => state.name === 'SET' && state.id);
735
+ const set_on = setOnOffState && setOnOffState.id;
736
+
737
+ const getOnOffState = control.states.find(state => state.name === 'ACTUAL' && state.id);
738
+ const get_on = (getOnOffState && getOnOffState.id) || set_on;
739
+
740
+ const traits = ['action.devices.traits.OnOff'];
741
+ name = this.checkName(name, obj, room, func);
742
+
743
+ return {
744
+ type: 'action.devices.types.SWITCH',
745
+ traits,
746
+ name: {
747
+ defaultNames: [room + func],
748
+ name: this.getObjectName(obj) || name,
749
+ nicknames: [name],
750
+ },
751
+ willReportState: true,
752
+ roomHint: room || '',
753
+ deviceInfo: {
754
+ manufacturer: 'ioBroker',
755
+ model: id.split('.')[0],
756
+ },
757
+ customData: {
758
+ set_on,
759
+ get_on,
760
+ },
761
+ id,
762
+ };
763
+ }
764
+
765
+ processDimmer(id, control, name, room, func, obj) {
766
+ const setBrightnessState = control.states.find(state => state.name === 'SET' && state.id);
767
+ let set_brightness = setBrightnessState && setBrightnessState.id;
768
+ if (set_brightness.indexOf('.dimspeed') !== -1) {
769
+ let idArray = set_brightness.split('.');
770
+ idArray.pop();
771
+ idArray.push('bri');
772
+ set_brightness = idArray.join('.');
773
+ }
774
+ const getBrightnessState = control.states.find(state => state.name === 'ACTUAL' && state.id);
775
+ const get_brightness = (getBrightnessState && getBrightnessState.id) || set_brightness;
776
+
777
+ const setOnOffState = control.states.find(state => state.name === 'ON_SET' && state.id);
778
+ const set_on = (setOnOffState && setOnOffState.id) || set_brightness;
779
+
780
+ const getOnOffState = control.states.find(state => state.name === 'ON_ACTUAL' && state.id);
781
+ const get_on = (getOnOffState && getOnOffState.id) || set_on;
782
+
783
+ const traits = [];
784
+
785
+ if (set_on) {
786
+ traits.push('action.devices.traits.OnOff');
787
+ }
788
+
789
+ if (set_brightness) {
790
+ traits.push('action.devices.traits.Brightness');
791
+ }
792
+
793
+ name = this.checkName(name, obj, room, func);
794
+
795
+ return {
796
+ type: 'action.devices.types.LIGHT',
797
+ traits,
798
+ displayTraits: ['action.devices.traits.Brightness'],
799
+ name: {
800
+ defaultNames: [textsT(this.lang, 'light', room)],
801
+ name: this.getObjectName(obj) || name,
802
+ nicknames: [name],
803
+ },
804
+ willReportState: true,
805
+ roomHint: room || '',
806
+ deviceInfo: {
807
+ manufacturer: 'ioBroker',
808
+ model: id.split('.')[0],
809
+ },
810
+ customData: {
811
+ set_on,
812
+ get_on,
813
+ set_brightness,
814
+ get_brightness,
815
+ },
816
+ id,
817
+ };
818
+ }
819
+
820
+ processHue(id, control, name, room, func, obj) {
821
+ let setBrightnessState = control.states.find(state => state.name === 'BRIGHTNESS' && state.id);
822
+ if (!setBrightnessState) {
823
+ setBrightnessState = control.states.find(state => state.name === 'DIMMER' && state.id);
824
+ }
825
+ const set_brightness = setBrightnessState && setBrightnessState.id;
826
+ const get_brightness = set_brightness;
827
+
828
+ let setOnOffState = control.states.find(state => state.name === 'ON' && state.id);
829
+
830
+ const set_on = (setOnOffState && setOnOffState.id) || set_brightness;
831
+ const get_on = set_on;
832
+
833
+ let setHueState = control.states.find(state => state.name === 'HUE' && state.id);
834
+
835
+ const set_color_hue = setHueState && setHueState.id;
836
+ const get_color_hue = set_color_hue;
837
+
838
+ let setSaturationState = control.states.find(state => state.name === 'SATURATION' && state.id);
839
+
840
+ const set_color_saturation = setSaturationState && setSaturationState.id;
841
+ const get_color_saturation = set_color_saturation;
842
+
843
+ let setTempState = control.states.find(state => state.name === 'TEMPERATURE' && state.id);
844
+
845
+ const set_color_temperature = setTempState && setTempState.id;
846
+ const get_color_temperature = set_color_temperature;
847
+
848
+ const traits = [];
849
+
850
+ if (set_on) {
851
+ traits.push('action.devices.traits.OnOff');
852
+ }
853
+
854
+ if (set_brightness) {
855
+ traits.push('action.devices.traits.Brightness');
856
+ }
857
+ if (set_color_hue) {
858
+ traits.push('action.devices.traits.ColorSetting');
859
+ }
860
+ name = this.checkName(name, obj, room, func);
861
+
862
+ return {
863
+ type: 'action.devices.types.LIGHT',
864
+ traits,
865
+ displayTraits: ['action.devices.traits.ColorSetting_spectrumRGB'],
866
+
867
+ attributes: {
868
+ colorModel: 'rgb',
869
+ colorTemperatureRange: {
870
+ temperatureMinK: 2000,
871
+ temperatureMaxK: 9000,
872
+ },
873
+ },
874
+ displayAttributes: JSON.stringify({
875
+ colorModel: 'rgb',
876
+ colorTemperatureRange: {
877
+ temperatureMinK: 2000,
878
+ temperatureMaxK: 9000,
879
+ },
880
+ }),
881
+ name: {
882
+ defaultNames: [textsT(this.lang, 'light', room)],
883
+ name: this.getObjectName(obj) || name,
884
+ nicknames: [name],
885
+ },
886
+ willReportState: true,
887
+ roomHint: room || '',
888
+ deviceInfo: {
889
+ manufacturer: 'ioBroker',
890
+ model: id.split('.')[0],
891
+ },
892
+ customData: {
893
+ set_on,
894
+ get_on,
895
+ set_brightness,
896
+ get_brightness,
897
+ set_color_hue,
898
+ get_color_hue,
899
+ set_color_saturation,
900
+ get_color_saturation,
901
+ set_color_temperature,
902
+ get_color_temperature,
903
+ },
904
+ id,
905
+ };
906
+ }
907
+
908
+ processRgbSingle(id, control, name, room, func, obj) {
909
+ let setBrightnessState = control.states.find(state => state.name === 'BRIGHTNESS' && state.id);
910
+ if (!setBrightnessState) {
911
+ setBrightnessState = control.states.find(state => state.name === 'DIMMER' && state.id);
912
+ }
913
+ const set_brightness = setBrightnessState && setBrightnessState.id;
914
+ const get_brightness = set_brightness;
915
+
916
+ let setOnOffState = control.states.find(state => state.name === 'ON_LIGHT' && state.id);
917
+ if (!setOnOffState) {
918
+ setOnOffState = control.states.find(state => state.name === 'ON' && state.id);
919
+ }
920
+ const set_on = (setOnOffState && setOnOffState.id) || set_brightness;
921
+ const get_on = set_on;
922
+
923
+ let setRGBState = control.states.find(state => state.name === 'RGB' && state.id);
924
+
925
+ const set_color_spectrumRGB = setRGBState && setRGBState.id;
926
+ const get_color_spectrumRGB = set_color_spectrumRGB;
927
+
928
+ let setTempState = control.states.find(state => state.name === 'TEMPERATURE' && state.id);
929
+
930
+ const set_color_temperature = setTempState && setTempState.id;
931
+ const get_color_temperature = set_color_temperature;
932
+
933
+ const traits = [];
934
+
935
+ if (set_on) {
936
+ traits.push('action.devices.traits.OnOff');
937
+ }
938
+
939
+ if (set_brightness) {
940
+ traits.push('action.devices.traits.Brightness');
941
+ }
942
+
943
+ if (set_color_spectrumRGB) {
944
+ traits.push('action.devices.traits.ColorSetting');
945
+ }
946
+
947
+ name = this.checkName(name, obj, room, func);
948
+
949
+ return {
950
+ type: 'action.devices.types.LIGHT',
951
+ traits,
952
+ displayTraits: ['action.devices.traits.ColorSetting_spectrumRGB'],
953
+
954
+ attributes: {
955
+ colorModel: 'rgb',
956
+ colorTemperatureRange: {
957
+ temperatureMinK: 2000,
958
+ temperatureMaxK: 9000,
959
+ },
960
+ },
961
+ displayAttributes: JSON.stringify({
962
+ colorModel: 'rgb',
963
+ colorTemperatureRange: {
964
+ temperatureMinK: 2000,
965
+ temperatureMaxK: 9000,
966
+ },
967
+ }),
968
+ name: {
969
+ defaultNames: [textsT(this.lang, 'light', room)],
970
+ name: this.getObjectName(obj) || name,
971
+ nicknames: [name],
972
+ },
973
+ willReportState: true,
974
+ roomHint: room || '',
975
+ deviceInfo: {
976
+ manufacturer: 'ioBroker',
977
+ model: id.split('.')[0],
978
+ },
979
+ customData: {
980
+ set_on,
981
+ get_on,
982
+ set_brightness,
983
+ get_brightness,
984
+ set_color_spectrumRGB,
985
+ get_color_spectrumRGB,
986
+ set_color_temperature,
987
+ get_color_temperature,
988
+ },
989
+ id,
990
+ };
991
+ }
992
+
993
+ processRgb(id, control, name, room, func, obj) {
994
+ let setBrightnessState = control.states.find(state => state.name === 'BRIGHTNESS' && state.id);
995
+ if (!setBrightnessState) {
996
+ setBrightnessState = control.states.find(state => state.name === 'DIMMER' && state.id);
997
+ }
998
+ const set_brightness = setBrightnessState && setBrightnessState.id;
999
+ const get_brightness = set_brightness;
1000
+
1001
+ let setOnOffState = control.states.find(state => state.name === 'ON_LIGHT' && state.id);
1002
+ if (!setOnOffState) {
1003
+ setOnOffState = control.states.find(state => state.name === 'ON' && state.id);
1004
+ }
1005
+ const set_on = (setOnOffState && setOnOffState.id) || set_brightness;
1006
+ const get_on = set_on;
1007
+
1008
+ let setTempState = control.states.find(state => state.name === 'TEMPERATURE' && state.id);
1009
+
1010
+ const set_color_temperature = setTempState && setTempState.id;
1011
+ const get_color_temperature = set_color_temperature;
1012
+
1013
+ let setRState = control.states.find(state => state.name === 'RED' && state.id);
1014
+
1015
+ const set_color_R = setRState && setRState.id;
1016
+ const get_color_R = set_color_R;
1017
+
1018
+ let setGState = control.states.find(state => state.name === 'GREEN' && state.id);
1019
+
1020
+ const set_color_G = setGState && setGState.id;
1021
+ const get_color_G = set_color_G;
1022
+
1023
+ let setBState = control.states.find(state => state.name === 'BLUE' && state.id);
1024
+
1025
+ const set_color_B = setBState && setBState.id;
1026
+ const get_color_B = set_color_B;
1027
+
1028
+ const traits = [];
1029
+
1030
+ if (set_on) {
1031
+ traits.push('action.devices.traits.OnOff');
1032
+ }
1033
+
1034
+ if (set_brightness) {
1035
+ traits.push('action.devices.traits.Brightness');
1036
+ }
1037
+
1038
+ traits.push('action.devices.traits.ColorSetting');
1039
+
1040
+ name = this.checkName(name, obj, room, func);
1041
+
1042
+ return {
1043
+ type: 'action.devices.types.LIGHT',
1044
+ traits,
1045
+ displayTraits: ['action.devices.traits.ColorSetting_spectrumRGB'],
1046
+
1047
+ attributes: {
1048
+ colorModel: 'rgb',
1049
+ colorTemperatureRange: {
1050
+ temperatureMinK: 2000,
1051
+ temperatureMaxK: 9000,
1052
+ },
1053
+ },
1054
+ displayAttributes: JSON.stringify({
1055
+ colorModel: 'rgb',
1056
+ colorTemperatureRange: {
1057
+ temperatureMinK: 2000,
1058
+ temperatureMaxK: 9000,
1059
+ },
1060
+ }),
1061
+ name: {
1062
+ defaultNames: [textsT(this.lang, 'light', room)],
1063
+ name: this.getObjectName(obj) || name,
1064
+ nicknames: [name],
1065
+ },
1066
+ willReportState: true,
1067
+ roomHint: room || '',
1068
+ deviceInfo: {
1069
+ manufacturer: 'ioBroker',
1070
+ model: id.split('.')[0],
1071
+ },
1072
+ customData: {
1073
+ set_on,
1074
+ get_on,
1075
+ set_brightness,
1076
+ get_brightness,
1077
+ set_color_temperature,
1078
+ get_color_temperature,
1079
+ set_color_R,
1080
+ get_color_R,
1081
+ set_color_G,
1082
+ get_color_G,
1083
+ set_color_B,
1084
+ get_color_B,
1085
+ },
1086
+ id,
1087
+ };
1088
+ }
1089
+
1090
+ processCT(id, control, name, room, func, obj) {
1091
+ let setBrightnessState = control.states.find(state => state.name === 'BRIGHTNESS' && state.id);
1092
+ if (!setBrightnessState) {
1093
+ setBrightnessState = control.states.find(state => state.name === 'DIMMER' && state.id);
1094
+ }
1095
+ const set_brightness = setBrightnessState && setBrightnessState.id;
1096
+ const get_brightness = set_brightness;
1097
+
1098
+ let setOnOffState = control.states.find(state => state.name === 'ON' && state.id);
1099
+ if (!setOnOffState) {
1100
+ setOnOffState = control.states.find(state => state.name === 'ON_LIGHT' && state.id);
1101
+ }
1102
+ const set_on = (setOnOffState && setOnOffState.id) || set_brightness;
1103
+ const get_on = set_on;
1104
+
1105
+ let setTempState = control.states.find(state => state.name === 'TEMPERATURE' && state.id);
1106
+
1107
+ const set_color_temperature = setTempState && setTempState.id;
1108
+ const get_color_temperature = set_color_temperature;
1109
+
1110
+ const traits = [];
1111
+
1112
+ if (set_on) {
1113
+ traits.push('action.devices.traits.OnOff');
1114
+ }
1115
+
1116
+ if (set_brightness) {
1117
+ traits.push('action.devices.traits.Brightness');
1118
+ }
1119
+ if (set_color_temperature) {
1120
+ traits.push('action.devices.traits.ColorSetting');
1121
+ }
1122
+ name = this.checkName(name, obj, room, func);
1123
+
1124
+ return {
1125
+ type: 'action.devices.types.LIGHT',
1126
+ traits,
1127
+ displayTraits: ['action.devices.traits.ColorSetting_temperature'],
1128
+ attributes: {
1129
+ colorModel: 'rgb',
1130
+ colorTemperatureRange: {
1131
+ temperatureMinK: 2000,
1132
+ temperatureMaxK: 9000,
1133
+ },
1134
+ },
1135
+ displayAttributes: JSON.stringify({
1136
+ colorModel: 'rgb',
1137
+ colorTemperatureRange: {
1138
+ temperatureMinK: 2000,
1139
+ temperatureMaxK: 9000,
1140
+ },
1141
+ }),
1142
+ name: {
1143
+ defaultNames: [textsT(this.lang, 'light', room)],
1144
+ name: this.getObjectName(obj) || name,
1145
+ nicknames: [name],
1146
+ },
1147
+ willReportState: true,
1148
+ roomHint: room || '',
1149
+ deviceInfo: {
1150
+ manufacturer: 'ioBroker',
1151
+ model: id.split('.')[0],
1152
+ },
1153
+ customData: {
1154
+ set_on,
1155
+ get_on,
1156
+ set_brightness,
1157
+ get_brightness,
1158
+ set_color_temperature,
1159
+ get_color_temperature,
1160
+ },
1161
+ id,
1162
+ };
1163
+ }
1164
+
1165
+ processWindowTilt(id, control, name, room, func, obj) {
1166
+ const getWindowTile = control.states.find(state => state.name === 'ACTUAL' && state.id);
1167
+ const get_isLocked = getWindowTile && getWindowTile.id;
1168
+ const set_lock = getWindowTile && getWindowTile.id;
1169
+ const set_openPercent = set_lock;
1170
+ const get_openPercent = get_isLocked;
1171
+
1172
+ const traits = [];
1173
+
1174
+ traits.push('action.devices.traits.LockUnlock');
1175
+ traits.push('action.devices.traits.OpenClose');
1176
+
1177
+ name = this.checkName(name, obj, room, func);
1178
+
1179
+ return {
1180
+ type: 'action.devices.types.WINDOW',
1181
+ traits,
1182
+ attributes: {
1183
+ queryOnlyOpenClose: true,
1184
+ },
1185
+ displayAttributes: JSON.stringify({
1186
+ queryOnlyOpenClose: true,
1187
+ }),
1188
+ name: {
1189
+ defaultNames: [textsT(this.lang, 'window', room)],
1190
+ name: this.getObjectName(obj) || name,
1191
+ nicknames: [name],
1192
+ },
1193
+ willReportState: true,
1194
+ roomHint: room || '',
1195
+ deviceInfo: {
1196
+ manufacturer: 'ioBroker',
1197
+ model: id.split('.')[0],
1198
+ },
1199
+ customData: {
1200
+ set_lock,
1201
+ get_isLocked,
1202
+ set_openPercent,
1203
+ get_openPercent,
1204
+ },
1205
+ id,
1206
+ };
1207
+ }
1208
+
1209
+ processBlind(id, control, name, room, func, obj) {
1210
+ const getOpenPercent = control.states.find(state => state.name === 'SET' && state.id);
1211
+ const set_openPercent = getOpenPercent && getOpenPercent.id;
1212
+ const get_openPercent = set_openPercent;
1213
+
1214
+ const getOpenDirection = control.states.find(state => state.name === 'DIRECTION' && state.id);
1215
+ const set_openDirection = getOpenDirection && getOpenDirection.id;
1216
+ const get_openDirection = set_openDirection;
1217
+
1218
+ const traits = [];
1219
+
1220
+ traits.push('action.devices.traits.OpenClose');
1221
+
1222
+ name = this.checkName(name, obj, room, func);
1223
+
1224
+ return {
1225
+ type: 'action.devices.types.BLINDS',
1226
+ traits,
1227
+ name: {
1228
+ defaultNames: [textsT(this.lang, 'blinds', room)],
1229
+ name: this.getObjectName(obj) || name,
1230
+ nicknames: [name],
1231
+ },
1232
+ willReportState: true,
1233
+ roomHint: room || '',
1234
+ deviceInfo: {
1235
+ manufacturer: 'ioBroker',
1236
+ model: id.split('.')[0],
1237
+ },
1238
+ customData: {
1239
+ set_openPercent,
1240
+ get_openPercent,
1241
+ set_openDirection,
1242
+ get_openDirection,
1243
+ },
1244
+ id,
1245
+ };
1246
+ }
1247
+
1248
+ processTemperature(id, control, name, room, func, obj) {
1249
+ const setThermostatTemperatureSetpoint = control.states.find(state => state.name === 'SET' && state.id);
1250
+ const set_thermostatTemperatureSetpoint =
1251
+ setThermostatTemperatureSetpoint && setThermostatTemperatureSetpoint.id;
1252
+
1253
+ const getThermostatTemperatureAmbient = control.states.find(state => state.name === 'ACTUAL' && state.id);
1254
+ const get_thermostatTemperatureAmbient =
1255
+ (getThermostatTemperatureAmbient && getThermostatTemperatureAmbient.id) ||
1256
+ set_thermostatTemperatureSetpoint;
1257
+
1258
+ const getThermostatHumidityAmbient = control.states.find(state => state.name === 'SECOND' && state.id);
1259
+ const get_thermostatHumidityAmbient = getThermostatHumidityAmbient && getThermostatHumidityAmbient.id;
1260
+
1261
+ const traits = [];
1262
+ traits.push('action.devices.traits.TemperatureSetting');
1263
+
1264
+ name = this.checkName(name, obj, room, func);
1265
+
1266
+ return {
1267
+ type: 'action.devices.types.THERMOSTAT',
1268
+ traits,
1269
+ displayTraits: ['action.devices.traits.TemperatureSetting_thermostatTemperatureSetpoint'],
1270
+ attributes: {
1271
+ queryOnlyTemperatureSetting: true,
1272
+ thermostatTemperatureUnit: 'C',
1273
+ },
1274
+ displayAttributes: JSON.stringify({
1275
+ queryOnlyTemperatureSetting: true,
1276
+ thermostatTemperatureUnit: 'C',
1277
+ }),
1278
+ name: {
1279
+ defaultNames: [textsT(this.lang, 'thermostat', room)],
1280
+ name: this.getObjectName(obj) || name,
1281
+ nicknames: [name],
1282
+ },
1283
+ willReportState: true,
1284
+ roomHint: room || '',
1285
+ deviceInfo: {
1286
+ manufacturer: 'ioBroker',
1287
+ model: id.split('.')[0],
1288
+ },
1289
+ customData: {
1290
+ set_thermostatTemperatureSetpoint,
1291
+ get_thermostatTemperatureAmbient,
1292
+ get_thermostatHumidityAmbient,
1293
+ },
1294
+ id,
1295
+ };
1296
+ }
1297
+
1298
+ processThermostat(id, control, name, room, func, obj) {
1299
+ const setThermostatTemperatureSetpoint = control.states.find(state => state.name === 'SET' && state.id);
1300
+ const set_thermostatTemperatureSetpoint =
1301
+ setThermostatTemperatureSetpoint && setThermostatTemperatureSetpoint.id;
1302
+ const get_thermostatTemperatureSetpoint = set_thermostatTemperatureSetpoint;
1303
+ const getThermostatTemperatureAmbient = control.states.find(state => state.name === 'ACTUAL' && state.id);
1304
+ const get_thermostatTemperatureAmbient =
1305
+ (getThermostatTemperatureAmbient && getThermostatTemperatureAmbient.id) ||
1306
+ set_thermostatTemperatureSetpoint;
1307
+
1308
+ const getThermostatHumidityAmbient = control.states.find(state => state.name === 'HUMIDITY' && state.id);
1309
+ const get_thermostatHumidityAmbient = getThermostatHumidityAmbient && getThermostatHumidityAmbient.id;
1310
+
1311
+ const traits = [];
1312
+ traits.push('action.devices.traits.TemperatureSetting');
1313
+
1314
+ name = this.checkName(name, obj, room, func);
1315
+
1316
+ return {
1317
+ type: 'action.devices.types.THERMOSTAT',
1318
+ traits,
1319
+ displayTraits: ['action.devices.traits.TemperatureSetting_thermostatTemperatureSetpoint'],
1320
+ attributes: {
1321
+ availableThermostatModes: 'off,heat,cool,on',
1322
+ thermostatTemperatureUnit: 'C',
1323
+ },
1324
+ displayAttributes: JSON.stringify({
1325
+ availableThermostatModes: 'off,heat,cool,on',
1326
+ thermostatTemperatureUnit: 'C',
1327
+ }),
1328
+
1329
+ name: {
1330
+ defaultNames: [textsT(this.lang, 'thermostat', room)],
1331
+ name: this.getObjectName(obj) || name,
1332
+ nicknames: [name],
1333
+ },
1334
+ willReportState: false,
1335
+ roomHint: room || '',
1336
+ deviceInfo: {
1337
+ manufacturer: 'ioBroker',
1338
+ model: id.split('.')[0],
1339
+ },
1340
+ customData: {
1341
+ set_thermostatTemperatureSetpoint,
1342
+ get_thermostatTemperatureSetpoint,
1343
+ get_thermostatTemperatureAmbient,
1344
+ get_thermostatHumidityAmbient,
1345
+ },
1346
+ id,
1347
+ };
1348
+ }
1349
+
1350
+ processMedia(id, control, name, room, func, obj) {
1351
+ const setOnOffState = control.states.find(state => state.name === 'STATE' && state.id);
1352
+ const set_on = setOnOffState && setOnOffState.id;
1353
+ const get_on = set_on;
1354
+
1355
+ const setPlayState = control.states.find(state => state.name === 'PLAY' && state.id);
1356
+ const set_mediaPlay = (setPlayState && setPlayState.id) || set_on;
1357
+
1358
+ const setPauseState = control.states.find(state => state.name === 'PAUSE' && state.id);
1359
+ const set_mediaPause = setPauseState && setPauseState.id;
1360
+
1361
+ const setStopState = control.states.find(state => state.name === 'STOP' && state.id);
1362
+ const set_mediaStop = setStopState && setStopState.id;
1363
+
1364
+ const setNextState = control.states.find(state => state.name === 'NEXT' && state.id);
1365
+ const set_mediaNext = setNextState && setNextState.id;
1366
+
1367
+ const setPrevState = control.states.find(state => state.name === 'PREV' && state.id);
1368
+ const set_mediaPrev = setPrevState && setPrevState.id;
1369
+
1370
+ const setVolumeState = control.states.find(state => state.name === 'VOLUME' && state.id);
1371
+ const set_brightness = setVolumeState && setVolumeState.id;
1372
+
1373
+ const traits = [
1374
+ 'action.devices.traits.OnOff',
1375
+ 'action.devices.traits.MediaState',
1376
+ 'action.devices.traits.Brightness',
1377
+ ];
1378
+ name = this.checkName(name, obj, room, func);
1379
+
1380
+ return {
1381
+ type: 'action.devices.types.SPEAKER',
1382
+ traits,
1383
+ displayTraits: ['action.devices.traits.Brightness'],
1384
+ name: {
1385
+ defaultNames: [textsT(this.lang, 'media', room)],
1386
+ name: this.getObjectName(obj) || name,
1387
+ nicknames: [name],
1388
+ },
1389
+ willReportState: true,
1390
+ roomHint: room || '',
1391
+ deviceInfo: {
1392
+ manufacturer: 'ioBroker',
1393
+ model: id.split('.')[0],
1394
+ },
1395
+ customData: {
1396
+ set_on,
1397
+ get_on,
1398
+ set_mediaPlay,
1399
+ set_mediaPause,
1400
+ set_mediaStop,
1401
+ set_mediaNext,
1402
+ set_mediaPrev,
1403
+ set_brightness,
1404
+ },
1405
+ id,
1406
+ };
1407
+ }
1408
+
1409
+ setLanguage(_lang) {
1410
+ this.lang = _lang || 'de';
1411
+ }
1412
+
1413
+ getSmartName(states, id) {
1414
+ if (!id) {
1415
+ if (!this.adapter.config.noCommon) {
1416
+ if (states.common) {
1417
+ return states.common.smartName;
1418
+ } else {
1419
+ this.adapter.log.warn(
1420
+ `[GHOME] No common property for ${JSON.stringify(states)} IoT options available please check the state.`,
1421
+ );
1422
+ return null;
1423
+ }
1424
+ } else {
1425
+ return states && states.common && states.common.custom && states.common.custom[this.adapter.namespace]
1426
+ ? states.common.custom[this.adapter.namespace].smartName
1427
+ : undefined;
1428
+ }
1429
+ } else if (!this.adapter.config.noCommon) {
1430
+ return states[id] && states[id].common ? states[id].common.smartName : null;
1431
+ } else {
1432
+ return states[id] &&
1433
+ states[id].common &&
1434
+ states[id].common.custom &&
1435
+ states[id].common.custom[this.adapter.namespace]
1436
+ ? states[id].common.custom[this.adapter.namespace].smartName || null
1437
+ : null;
1438
+ }
1439
+ }
1440
+
1441
+ async processState(ids, objects, id, roomName, funcName, result, smartEnum, parentId) {
1442
+ try {
1443
+ if (!id || !objects[id] || result[id]) {
1444
+ return;
1445
+ }
1446
+ this.adapter.log.debug(`[GHOME] Process: ${id} ${roomName} ${funcName}`);
1447
+ let smartName = this.getSmartName(objects, id);
1448
+
1449
+ if (smartName !== undefined && (smartName === 'ignore' || smartName === false)) {
1450
+ this.adapter.log.warn(
1451
+ `[GHOME] ${id} is ignored because the property smartName is false or ignore. To use this state again, remove the property smartName in Object explorer or add it manually under Google Devices`,
1452
+ );
1453
+ return;
1454
+ }
1455
+
1456
+ let friendlyName = '';
1457
+ if (typeof smartName === 'object' && smartName) {
1458
+ friendlyName = smartName[this.lang] || smartName.en;
1459
+ }
1460
+ if (!friendlyName) {
1461
+ friendlyName = this.checkName('', objects[id], roomName, funcName);
1462
+ }
1463
+
1464
+ //check if comes from smart enum and is not writable
1465
+ // if (smartEnum && objects[id].common.write === false) {
1466
+ // return;
1467
+ // }
1468
+
1469
+ result[id] = {};
1470
+ result[id].traits = [];
1471
+ result[id].customData = {};
1472
+ if (parentId) {
1473
+ result[id].parentId = parentId;
1474
+ }
1475
+
1476
+ let childStates = [];
1477
+ result[id].ioType = objects[id].type;
1478
+ if (objects[id].type === 'channel') {
1479
+ childStates = this.getAllStatesInChannel(Object.keys(objects), id);
1480
+ } else if (objects[id].type === 'device') {
1481
+ childStates = this.getAllStatesInDevice(Object.keys(objects), id);
1482
+ // if no states, it may be device without channels
1483
+ if (!childStates.length) {
1484
+ childStates = this.getAllStatesInChannel(Object.keys(objects), id);
1485
+ }
1486
+ }
1487
+
1488
+ if (typeof smartName === 'object' && smartName) {
1489
+ result[id]['conv2GH'] = {};
1490
+ result[id]['displayConv2GH'] = '';
1491
+ result[id]['conv2iob'] = {};
1492
+ result[id]['displayConv2iob'] = '';
1493
+ if (smartName.ghConv2GH) {
1494
+ result[id]['conv2GH'][id] = smartName.ghConv2GH;
1495
+ result[id]['displayConv2GH'] = smartName.ghConv2GH;
1496
+ }
1497
+ if (smartName.ghConv2iob) {
1498
+ result[id]['conv2iob'][id] = smartName.ghConv2iob;
1499
+ result[id]['displayConv2iob'] = smartName.ghConv2iob;
1500
+ }
1501
+ }
1502
+ if ((typeof smartName === 'object' && smartName && smartName.ghType && smartName.ghTraits) || parentId) {
1503
+ if (smartName && smartName.ghType) {
1504
+ result[id].type = smartName.ghType;
1505
+ }
1506
+ if (smartName && smartName.ghTraits && smartName.ghTraits.length > 0 && smartName.ghTraits[0]) {
1507
+ result[id].traits = [smartName.ghTraits[0].split('_')[0]];
1508
+ result[id].displayTraits = [smartName.ghTraits[0]];
1509
+ }
1510
+ try {
1511
+ if (smartName && smartName.ghAttributes) {
1512
+ result[id].attributes = JSON.parse(smartName.ghAttributes);
1513
+ result[id].displayAttributes = JSON.stringify(result[id].attributes);
1514
+ }
1515
+ } catch (error) {
1516
+ this.adapter.log.error(`[GHOME] Cannot parse attributes ${error} ${smartName.ghAttributes}`);
1517
+ this.adapter.log.error(`[GHOME] Cannot parse attributes ${error} ${smartName.ghAttributes}`);
1518
+ result[id].attributes = {};
1519
+ result[id].displayAttributes = smartName.ghAttributes;
1520
+ }
1521
+ for (let c = 0; c < childStates.length; c++) {
1522
+ await this.processState(ids, objects, childStates[c], roomName, funcName, result, false, id);
1523
+ }
1524
+ } else if (smartEnum) {
1525
+ try {
1526
+ // try to detect device
1527
+ if (id.match(/^hm-rpc\..*?\.CUX/)) {
1528
+ this.adapter.log.debug(`[GHOME] Ignore "${id}": Because it is a virtual device`);
1529
+ return;
1530
+ }
1531
+ const options = {
1532
+ objects: objects,
1533
+ id: id,
1534
+ _keysOptional: ids,
1535
+ _usedIdsOptional: this.usedIds,
1536
+ };
1537
+
1538
+ const controls = this.detector.detect(options);
1539
+ // if (id.indexOf('deconz.0.Lights.27') !== -1)
1540
+ // debugger
1541
+ if (controls) {
1542
+ let control = controls[0];
1543
+ if (controls[0].type === 'socket' && controls[1] && controls[1].type !== 'info') {
1544
+ control = controls[1];
1545
+ }
1546
+ this.adapter.log.debug(`[GHOME] Type: ${control.type}`);
1547
+
1548
+ if (this.converter.hasOwnProperty(control.type)) {
1549
+ this.adapter.log.info(`[GHOME] ${id} is auto added with type ${control.type}.`);
1550
+ result[id] = this.converter[control.type](
1551
+ id,
1552
+ control,
1553
+ friendlyName,
1554
+ roomName,
1555
+ funcName,
1556
+ objects[id],
1557
+ );
1558
+ result[id].displayTraits = result[id].displayTraits || result[id].traits;
1559
+
1560
+ if (typeof smartName === 'object' && smartName && smartName.ghAttributes) {
1561
+ result[id].attributes = JSON.parse(smartName.ghAttributes);
1562
+ result[id].displayAttributes = JSON.stringify(result[id].attributes);
1563
+ }
1564
+
1565
+ result[id].smartEnum = 'X';
1566
+ result[id]['conv2GH'] = {};
1567
+ result[id]['conv2iob'] = {};
1568
+
1569
+ if (result[id].customData.set_brightness) {
1570
+ const briState = await this.adapter.getForeignObjectAsync(
1571
+ result[id].customData.set_brightness,
1572
+ );
1573
+ if (briState && briState.common && briState.common.max && briState.common.max >= 101) {
1574
+ result[id]['conv2GH'][result[id].customData.set_brightness] =
1575
+ 'return value/' + briState.common.max / 100;
1576
+ result[id]['conv2iob'][result[id].customData.set_brightness] =
1577
+ 'return value*' + briState.common.max / 100;
1578
+ }
1579
+ }
1580
+ if (result[id].customData.set_color_temperature) {
1581
+ const set_color_temperature = await this.adapter.getForeignObjectAsync(
1582
+ result[id].customData.set_color_temperature,
1583
+ );
1584
+ if (
1585
+ set_color_temperature &&
1586
+ set_color_temperature.common &&
1587
+ set_color_temperature.common.max <= 500
1588
+ ) {
1589
+ result[id]['conv2GH'][result[id].customData.set_color_temperature] =
1590
+ 'return 1000000/value';
1591
+ result[id]['conv2iob'][result[id].customData.set_color_temperature] =
1592
+ 'return 1000000/value';
1593
+ }
1594
+ }
1595
+
1596
+ result[id].ioType = objects[id].type;
1597
+
1598
+ this.adapter.log.debug(
1599
+ `[GHOME] Created Google HOME device: ${result[id].name.defaultNames[0]} - ${control.type} - ${id}`,
1600
+ );
1601
+
1602
+ // childStates.forEach((child) => {
1603
+ Object.keys(result[id].customData).forEach(element => {
1604
+ // if (element.indexOf('get_') !== -1) {
1605
+
1606
+ const childID = result[id].customData[element];
1607
+
1608
+ if (!childID) {
1609
+ return;
1610
+ }
1611
+ if (childID === id) {
1612
+ this.adapter.log.debug(`[GHOME] SmartEnum is equal child skip child detection`);
1613
+ return;
1614
+ }
1615
+
1616
+ if (result[childID] && !result[childID].smartEnum) {
1617
+ result[childID].parentId = id;
1618
+ this.adapter.log.warn(
1619
+ `[GHOME] ${childID} has custom settings this is overriding auto-detected settings. To use auto-detected settings please delete this state in the instance settings under Google Devices.`,
1620
+ );
1621
+ return;
1622
+ }
1623
+ const trait = customToTraitEnum[element.substring(4)];
1624
+ result[childID] = {};
1625
+ result[childID].traits = [trait];
1626
+ result[childID].roomHint = roomName;
1627
+ result[childID].smartEnum = 'X';
1628
+ result[childID].type = result[id].type;
1629
+
1630
+ result[childID].ioType = objects[childID] ? objects[childID].type : '';
1631
+ if (element.includes('set_')) {
1632
+ result[childID].ioType =
1633
+ ` SETTER:\n\u00a0\u00a0\u00a0\u00a0${result[childID].ioType}`;
1634
+ }
1635
+ result[childID].displayTraits = [trait];
1636
+ if (childID && result[id]['conv2GH'][childID]) {
1637
+ result[childID]['displayConv2GH'] = result[id]['conv2GH'][childID];
1638
+ }
1639
+ if (childID && result[id]['conv2iob'][childID]) {
1640
+ result[childID]['displayConv2iob'] = result[id]['conv2iob'][childID];
1641
+ }
1642
+ result[childID].parentId = id;
1643
+ result[childID].name = {
1644
+ nicknames: [childID],
1645
+ };
1646
+ result[childID].id = childID;
1647
+ // }
1648
+ });
1649
+ // })
1650
+ } else {
1651
+ this.adapter.log.info(
1652
+ `[GHOME] Cannot auto convert ${id}. Type ${control.type} is not available, yet. If you need the state please add him manually`,
1653
+ );
1654
+ this.adapter.log.debug(`[GHOME] ${JSON.stringify(control)}`);
1655
+ }
1656
+ // });
1657
+ } else {
1658
+ console.log(`[GHOME] Nothing found for ${options.id}`);
1659
+ }
1660
+ } catch (e) {
1661
+ this.adapter.log.error(`[GHOME] Cannot process "${id}": ${e}`);
1662
+ this.adapter.log.error(`[GHOME] ${e.stack}`);
1663
+ }
1664
+ return;
1665
+ } else if (typeof smartName === 'object' && smartName && smartName.smartType) {
1666
+ //SMARTTYPES = ['LIGHT', 'SWITCH', 'THERMOSTAT', 'ACTIVITY_TRIGGER', 'SCENE_TRIGGER', 'SMARTPLUG', 'SMARTLOCK', 'CAMERA'];
1667
+ switch (smartName.smartType) {
1668
+ case 'LIGHT':
1669
+ result[id].traits = ['action.devices.traits.OnOff'];
1670
+ result[id].type = 'action.devices.types.LIGHT';
1671
+ break;
1672
+ case 'SWITCH':
1673
+ result[id].traits = ['action.devices.traits.OnOff'];
1674
+ result[id].type = 'action.devices.types.SWITCH';
1675
+ break;
1676
+ case 'SMARTPLUG':
1677
+ result[id].traits = ['action.devices.traits.OnOff'];
1678
+ result[id].type = 'action.devices.types.OUTLET';
1679
+ break;
1680
+ case 'SMARTLOCK':
1681
+ result[id].traits = ['action.devices.traits.LockUnlock'];
1682
+ result[id].type = 'action.devices.types.DOOR';
1683
+ break;
1684
+ }
1685
+
1686
+ result[id].displayTraits = result[id].displayTraits || result[id].traits;
1687
+ result[id].ioType = objects[id].type;
1688
+ result[id].smartEnum = 'X';
1689
+
1690
+ let friendlyNamesArray = [];
1691
+ if (friendlyName) {
1692
+ friendlyNamesArray = friendlyName.replace(/, /g, ',').split(',');
1693
+ }
1694
+
1695
+ result[id].name = {
1696
+ defaultNames: [this.getObjectName(objects[id], true)],
1697
+ name: friendlyNamesArray[0],
1698
+ nicknames: friendlyNamesArray,
1699
+ };
1700
+ result[id].willReportState = true;
1701
+ result[id].roomHint = roomName || '';
1702
+ result[id].deviceInfo = {
1703
+ manufacturer: 'ioBroker',
1704
+ model: id.split('.')[0],
1705
+ };
1706
+ result[id].id = id;
1707
+ result[id].otherDeviceIds = [
1708
+ {
1709
+ deviceId: id,
1710
+ },
1711
+ ];
1712
+
1713
+ this.adapter.log.debug(
1714
+ `[GHOME] Created Google HOME device from Alexa SmartType: ${result[id].name.defaultNames[0]} - ${result[id].type} - ${id}`,
1715
+ );
1716
+
1717
+ return;
1718
+ } else {
1719
+ //friendlyName = this.checkName('', objects[id], roomName, funcName);
1720
+ delete result[id];
1721
+ return;
1722
+ }
1723
+
1724
+ let friendlyNamesArray = [];
1725
+ if (friendlyName) {
1726
+ friendlyNamesArray = friendlyName.replace(/, /g, ',').split(',');
1727
+ }
1728
+
1729
+ result[id].name = {
1730
+ defaultNames: [this.getObjectName(objects[id], true)],
1731
+ name: friendlyNamesArray[0],
1732
+ nicknames: friendlyNamesArray,
1733
+ };
1734
+ result[id].willReportState = true;
1735
+ result[id].roomHint = roomName || '';
1736
+ result[id].deviceInfo = {
1737
+ manufacturer: 'ioBroker',
1738
+ model: id.split('.')[0],
1739
+ };
1740
+ result[id].id = id;
1741
+ result[id].otherDeviceIds = [
1742
+ {
1743
+ deviceId: id,
1744
+ },
1745
+ ];
1746
+ if (
1747
+ smartName &&
1748
+ smartName.ghTraits &&
1749
+ smartName.ghTraits.length > 0 &&
1750
+ smartName.ghTraits[0] &&
1751
+ traitEnum[smartName.ghTraits[0]]
1752
+ ) {
1753
+ const command = traitEnum[smartName.ghTraits[0]].command;
1754
+ const getter = traitEnum[smartName.ghTraits[0]].getter;
1755
+ if (command) {
1756
+ result[id].customData[`set_${command}`] = id;
1757
+ }
1758
+ if (getter) {
1759
+ result[id].customData[`get_${getter}`] = id;
1760
+ } else {
1761
+ result[id].customData[`get_${command}`] = id;
1762
+ }
1763
+
1764
+ //check if is default attributes
1765
+ Object.keys(traitEnum).forEach(trait => {
1766
+ try {
1767
+ if (
1768
+ JSON.stringify(JSON.parse(traitEnum[trait].defaultAttributes)) ===
1769
+ JSON.stringify(JSON.parse(smartName.ghAttributes))
1770
+ ) {
1771
+ smartName.ghAttributes = '';
1772
+ }
1773
+ } catch (error) {}
1774
+ });
1775
+
1776
+ if (!smartName.ghAttributes && traitEnum[smartName.ghTraits[0]].defaultAttributes) {
1777
+ const defaultAttributes = traitEnum[smartName.ghTraits[0]].defaultAttributes;
1778
+ result[id].attributes = JSON.parse(defaultAttributes);
1779
+ const obj = await this.adapter.getForeignObjectAsync(id);
1780
+ if (
1781
+ !obj.common ||
1782
+ !obj.common.smartName ||
1783
+ !obj.common.smartName.ghAttributes ||
1784
+ obj.common.smartName.ghAttributes !== defaultAttributes
1785
+ ) {
1786
+ await this.adapter.extendForeignObjectAsync(id, {
1787
+ common: {
1788
+ smartName: {
1789
+ ghAttributes: defaultAttributes,
1790
+ },
1791
+ },
1792
+ });
1793
+ }
1794
+ } else {
1795
+ result[id].attributes = {};
1796
+ }
1797
+ }
1798
+
1799
+ //merge states with same name and room
1800
+ if (this.smartNames[friendlyName] && this.smartNames[friendlyName].roomHint === roomName) {
1801
+ if (
1802
+ smartName &&
1803
+ smartName.ghTraits &&
1804
+ Array.isArray(smartName.ghTraits) &&
1805
+ smartName.ghTraits.length > 0 &&
1806
+ smartName.ghTraits[0]
1807
+ ) {
1808
+ const orgId = this.smartNames[friendlyName].id;
1809
+ result[orgId].traits.push(smartName.ghTraits[0].split('_')[0]);
1810
+
1811
+ if (result[id].attributes) {
1812
+ if (result[orgId].attributes) {
1813
+ result[orgId].attributes = Object.assign(result[orgId].attributes, result[id].attributes);
1814
+ } else {
1815
+ result[orgId].attributes = result[id].attributes;
1816
+ }
1817
+ }
1818
+ result[orgId]['conv2GH'] = { ...result[orgId]['conv2GH'], ...result[id]['conv2GH'] };
1819
+ result[orgId]['conv2iob'] = { ...result[orgId]['conv2iob'], ...result[id]['conv2iob'] };
1820
+
1821
+ const command = traitEnum[smartName.ghTraits[0]].command;
1822
+ const getter = traitEnum[smartName.ghTraits[0]].getter;
1823
+ if (command) {
1824
+ result[orgId].customData[`set_${command}`] = id;
1825
+ }
1826
+ if (getter) {
1827
+ result[orgId].customData[`get_${getter}`] = id;
1828
+ } else {
1829
+ result[orgId].customData[`get_${command}`] = id;
1830
+ }
1831
+ this.adapter.log.debug(`[GHOME] ${JSON.stringify(result[orgId])}`);
1832
+ result[id].merged = true;
1833
+ }
1834
+ } else {
1835
+ this.smartNames[friendlyName] = result[id];
1836
+ }
1837
+ this.adapter.log.debug(`[GHOME] ${JSON.stringify(result[id])}`);
1838
+ } catch (error) {
1839
+ if (id && result[id]) {
1840
+ delete result[id];
1841
+ }
1842
+ this.adapter.log.error(`[GHOME] Cannot process: ${id} ${error}`);
1843
+ this.adapter.log.error(`[GHOME] ${objects[id]}`);
1844
+ this.adapter.log.debug(`[GHOME] ${error.stack}`);
1845
+ }
1846
+ }
1847
+
1848
+ getAllStatesInChannel(keys, channelId) {
1849
+ const reg = new RegExp(`^${channelId.replace(/\./g, '\\.')}\\.[^.]+$`);
1850
+ return keys.filter(_id => reg.test(_id));
1851
+ }
1852
+
1853
+ getAllStatesInDevice(keys, channelId) {
1854
+ const reg = new RegExp(`^${channelId.replace(/\./g, '\\.')}\\.[^.]+\\.[^.]+$`);
1855
+ return keys.filter(_id => reg.test(_id));
1856
+ }
1857
+
1858
+ _readObjects() {
1859
+ return new Promise(resolve => {
1860
+ this.adapter.getObjectView('system', 'state', {}, (err, _states) => {
1861
+ this.adapter.getObjectView('system', 'channel', {}, (err, _channels) => {
1862
+ this.adapter.getObjectView('system', 'device', {}, (err, _devices) => {
1863
+ this.adapter.getObjectView('system', 'enum', {}, (err, _enums) => {
1864
+ const objects = {};
1865
+ const enums = {};
1866
+ if (_devices && _devices.rows) {
1867
+ for (let i = 0; i < _devices.rows.length; i++) {
1868
+ if (
1869
+ _devices.rows[i].value &&
1870
+ _devices.rows[i].value._id &&
1871
+ !ignoreIds.find(reg => reg.test(_devices.rows[i].value._id))
1872
+ ) {
1873
+ objects[_devices.rows[i].value._id] = _devices.rows[i].value;
1874
+ }
1875
+ }
1876
+ }
1877
+ if (_channels && _channels.rows) {
1878
+ for (let i = 0; i < _channels.rows.length; i++) {
1879
+ if (
1880
+ _channels.rows[i].value &&
1881
+ _channels.rows[i].value._id &&
1882
+ !ignoreIds.find(reg => reg.test(_channels.rows[i].value._id))
1883
+ ) {
1884
+ objects[_channels.rows[i].value._id] = _channels.rows[i].value;
1885
+ }
1886
+ }
1887
+ }
1888
+ if (_states && _states.rows) {
1889
+ for (let i = 0; i < _states.rows.length; i++) {
1890
+ if (
1891
+ _states.rows[i].value &&
1892
+ _states.rows[i].value._id &&
1893
+ !ignoreIds.find(reg => reg.test(_states.rows[i].value._id))
1894
+ ) {
1895
+ objects[_states.rows[i].value._id] = _states.rows[i].value;
1896
+ }
1897
+ }
1898
+ }
1899
+ if (_enums && _enums.rows) {
1900
+ for (let i = 0; i < _enums.rows.length; i++) {
1901
+ if (_enums.rows[i].value && _enums.rows[i].value._id) {
1902
+ enums[_enums.rows[i].value._id] = _enums.rows[i].value;
1903
+ objects[_enums.rows[i].value._id] = _enums.rows[i].value;
1904
+ }
1905
+ }
1906
+ }
1907
+ resolve({
1908
+ objects,
1909
+ enums,
1910
+ });
1911
+ });
1912
+ });
1913
+ });
1914
+ });
1915
+ });
1916
+ }
1917
+
1918
+ filterValidGoogleDevices() {
1919
+ let returnObject = {};
1920
+ Object.keys(this.smartDevices).map(id => {
1921
+ if (!this.smartDevices[id].merged) {
1922
+ if (
1923
+ (this.smartDevices[id].name &&
1924
+ this.smartDevices[id].name.name &&
1925
+ this.smartDevices[id].type &&
1926
+ this.smartDevices[id].traits &&
1927
+ this.smartDevices[id].traits.length > 0) ||
1928
+ this.smartDevices[id].parentId
1929
+ ) {
1930
+ returnObject[id] = this.smartDevices[id];
1931
+ }
1932
+ }
1933
+ });
1934
+ return returnObject;
1935
+ }
1936
+
1937
+ updateDevices(cb) {
1938
+ this.unsubscribeAllIds(() => {
1939
+ this._updateDevices().then(smartDevices => {
1940
+ this.smartDevices = smartDevices;
1941
+ this.smartDevicesSentToGoogle = this.filterValidGoogleDevices();
1942
+ // Check KEY
1943
+ return this.checkUrlKey()
1944
+ .then(() => this.subscribeAllIds(cb))
1945
+ .catch(err => {
1946
+ this.adapter.config.googleHome &&
1947
+ this.adapter.log.warn(
1948
+ `[GHOME] Invalid URL Pro key. Status auto-update is disabled you can set states but receive states only manually: ${err}`,
1949
+ );
1950
+ // call cb otherwise frontend get no results
1951
+ cb && cb();
1952
+ });
1953
+ });
1954
+ });
1955
+ }
1956
+
1957
+ getDevices() {
1958
+ const devices = Object.keys(this.smartDevices)
1959
+ .filter(device => !!this.smartDevices[device].name)
1960
+ .map(device => this.smartDevices[device]);
1961
+ return devices;
1962
+ }
1963
+
1964
+ _findRoomOfId(allRooms, enums, objects, id) {
1965
+ // find room
1966
+ let roomHint = '';
1967
+ allRooms.forEach(roomId => {
1968
+ const room = enums[roomId];
1969
+ if (!room.common || !room.common.members || !room.common.members.length) {
1970
+ return;
1971
+ }
1972
+
1973
+ // If state or channel is in some room and in some function
1974
+ let pos = room.common.members.indexOf(id);
1975
+ if (pos === -1) {
1976
+ const idArray = id.split('.');
1977
+ idArray.pop();
1978
+ const parent = idArray.join('.');
1979
+ if (objects[parent] && objects[parent].type === 'channel') {
1980
+ pos = room.common.members.indexOf(parent);
1981
+ }
1982
+ }
1983
+ if (pos !== -1) {
1984
+ // Get the name of function (with language and if name is empty)
1985
+ let roomName = this.getSmartName(room);
1986
+ roomName = roomName || room.common.name;
1987
+ if (roomName && typeof roomName === 'object') {
1988
+ roomName = roomName[this.lang] || roomName.en;
1989
+ }
1990
+ if (!roomName) {
1991
+ roomName = roomId.substring('enum.rooms.'.length);
1992
+ roomName = roomName[0].toUpperCase() + roomName.substring(1);
1993
+ }
1994
+ roomHint = roomName;
1995
+ }
1996
+ });
1997
+
1998
+ return roomHint;
1999
+ }
2000
+
2001
+ async _updateDevices() {
2002
+ const { objects, enums } = await this._readObjects();
2003
+
2004
+ let ids = Object.keys(objects);
2005
+
2006
+ this.enums = [];
2007
+ this.smartDevices = {};
2008
+ this.smartNames = {};
2009
+ this.enums = [];
2010
+ this.usedIds = [];
2011
+ this.keys = [];
2012
+
2013
+ ids.sort();
2014
+
2015
+ // Build overlap from rooms and functions
2016
+ let rooms = [];
2017
+ let allRooms = [];
2018
+ let funcs = [];
2019
+ let smartName;
2020
+ Object.keys(enums).forEach(id => {
2021
+ smartName = this.getSmartName(enums[id]);
2022
+ if (id.match(/^enum\.rooms\./)) {
2023
+ allRooms.push(id);
2024
+ if (smartName !== 'ignore' && smartName !== false) {
2025
+ rooms.push(id);
2026
+ }
2027
+ } else if (id.match(/^enum\.functions\./) && smartName !== 'ignore' && smartName !== false) {
2028
+ funcs.push(id);
2029
+ }
2030
+ });
2031
+
2032
+ let result = {};
2033
+ let roomNames = {};
2034
+
2035
+ // find smart names without room or func
2036
+ this.adapter.log.debug('[GHOME] Start non smart enum processing');
2037
+ const keys = Object.keys(objects);
2038
+ for (let k = 0; k < keys.length; k++) {
2039
+ const id = keys[k];
2040
+ smartName = this.getSmartName(objects[id]);
2041
+ if (smartName && smartName !== 'ignore' && smartName !== false) {
2042
+ const roomHint = this._findRoomOfId(allRooms, enums, objects, id);
2043
+ await this.processState(ids, objects, id, roomHint, '', result, false);
2044
+ }
2045
+ }
2046
+
2047
+ this.adapter.log.debug('[GHOME] Start smartenum processing');
2048
+
2049
+ for (let f = 0; f < funcs.length; f++) {
2050
+ const funcId = funcs[f];
2051
+ const func = enums[funcId];
2052
+
2053
+ if (
2054
+ !func.common ||
2055
+ !func.common.members ||
2056
+ typeof func.common.members !== 'object' ||
2057
+ !func.common.members.length
2058
+ ) {
2059
+ continue;
2060
+ }
2061
+ this.adapter.log.debug(`[GHOME] Process ${funcId}`);
2062
+ // Get the name of function (with language and if name is empty)
2063
+ let funcName = this.getSmartName(func);
2064
+ funcName = funcName || func.common.name;
2065
+
2066
+ if (funcName && typeof funcName === 'object') {
2067
+ funcName = funcName[this.lang] || funcName.en;
2068
+ }
2069
+
2070
+ if (!funcName) {
2071
+ funcName = funcId.substring('enum.functions.'.length);
2072
+ funcName = funcName[0].toUpperCase() + funcName.substring(1);
2073
+ }
2074
+
2075
+ for (let m = 0; m < func.common.members.length; m++) {
2076
+ const id = func.common.members[m];
2077
+ if (!result[id]) {
2078
+ for (let r = 0; r < rooms.length; r++) {
2079
+ const roomId = rooms[r];
2080
+ const room = enums[roomId];
2081
+
2082
+ if (
2083
+ !room.common ||
2084
+ !room.common.members ||
2085
+ typeof func.common.members !== 'object' ||
2086
+ !room.common.members.length
2087
+ ) {
2088
+ continue;
2089
+ }
2090
+
2091
+ // If state or channel is in some room and in some function
2092
+ const pos = room.common.members.indexOf(id);
2093
+
2094
+ if (pos !== -1) {
2095
+ // find name for room if not found earlier
2096
+ if (!roomNames[roomId]) {
2097
+ // Get the name of function (with language and if name is empty)
2098
+ let roomName = this.getSmartName(room);
2099
+
2100
+ roomName = roomName || room.common.name;
2101
+
2102
+ if (roomName && typeof roomName === 'object') {
2103
+ roomName = roomName[this.lang] || roomName.en;
2104
+ }
2105
+
2106
+ if (!roomName) {
2107
+ roomName = roomId.substring('enum.rooms.'.length);
2108
+ roomName = roomName[0].toUpperCase() + roomName.substring(1);
2109
+ }
2110
+ roomNames[roomId] = roomName;
2111
+ }
2112
+ if (objects[id] && objects[id].type === 'state') {
2113
+ this.adapter.log.warn(
2114
+ `[GHOME] ${id} is a state. It's recommended to add rooms and functionality to channels or devices and not to a state to get auto detected for Google Home. This works only for simple switches.`,
2115
+ );
2116
+ }
2117
+ await this.processState(ids, objects, id, roomNames[roomId], funcName, result, true);
2118
+ }
2119
+ }
2120
+ }
2121
+ }
2122
+ }
2123
+
2124
+ // scan alias.* and linkeddevices.*
2125
+ for (let i = 0; i < ids.length; i++) {
2126
+ if (ids[i] < 'alias.') {
2127
+ continue;
2128
+ }
2129
+ if (ids[i] > 'linkeddevices.\u9999') {
2130
+ break;
2131
+ }
2132
+
2133
+ if (
2134
+ (ids[i].startsWith('alias.') || ids[i].startsWith('linkeddevices.')) &&
2135
+ objects[ids[i]] &&
2136
+ (objects[ids[i]].type === 'device' || objects[ids[i]].type === 'channel')
2137
+ ) {
2138
+ const roomHint = this._findRoomOfId(allRooms, enums, objects, ids[i]);
2139
+ if (roomHint) {
2140
+ await this.processState(
2141
+ ids,
2142
+ objects,
2143
+ ids[i],
2144
+ roomsT(this.lang, roomHint),
2145
+ funcsT(this.lang, 'undefined'),
2146
+ result,
2147
+ true,
2148
+ );
2149
+ }
2150
+ }
2151
+ }
2152
+
2153
+ this.usedIds = null;
2154
+ this.keys = null;
2155
+
2156
+ Object.keys(result).forEach(id => this.adapter.log.debug(`[GHOME] ${id} => ${result[id].type}`));
2157
+
2158
+ return result;
2159
+ }
2160
+
2161
+ async updateState(id, state) {
2162
+ const now = Date.now();
2163
+ // Only pro with a valid license can update states
2164
+ if (
2165
+ !this.urlKeyOk ||
2166
+ (this.unknownDevices[id] && now - this.unknownDevices[id] < RETRY_UNKNOWN_DEVICES_INTERVAL)
2167
+ ) {
2168
+ // 10 minutes
2169
+ return;
2170
+ }
2171
+
2172
+ let json = {};
2173
+ let found = false;
2174
+ for (const devId in this.smartDevicesSentToGoogle) {
2175
+ if (this.smartDevicesSentToGoogle.hasOwnProperty(devId)) {
2176
+ const custom = this.smartDevicesSentToGoogle[devId].customData;
2177
+ let idIsAttr = false;
2178
+ for (const attr in custom) {
2179
+ if (custom.hasOwnProperty(attr) && attr?.startsWith('get_')) {
2180
+ if (custom[attr] === id) {
2181
+ idIsAttr = true;
2182
+ } else {
2183
+ continue;
2184
+ }
2185
+ const _attr = attr.substring(4);
2186
+ json[devId] = json[devId] || {};
2187
+ json[devId][_attr] = {
2188
+ id: custom[attr],
2189
+ };
2190
+ }
2191
+ }
2192
+
2193
+ if (!idIsAttr) {
2194
+ continue;
2195
+ }
2196
+
2197
+ for (const attr in custom) {
2198
+ if (custom.hasOwnProperty(attr) && attr && custom[attr] === id && attr.startsWith('get_')) {
2199
+ const _attr = attr.substring(4);
2200
+ let val;
2201
+ json[devId] = json[devId] || {};
2202
+
2203
+ try {
2204
+ if (_attr === 'on') {
2205
+ val =
2206
+ state.val === 1 ||
2207
+ state.val === '1' ||
2208
+ state.val === 'true' ||
2209
+ state.val === 'ON' ||
2210
+ state.val === 'on' ||
2211
+ state.val === true ||
2212
+ (typeof state.val === 'number' && state.val > 0);
2213
+ } else if (_attr === 'color_R' || _attr === 'color_G' || _attr === 'color_B') {
2214
+ if (!json[devId]['color_R'] && !!json[devId]['color_G'] && !json[devId]['color_B']) {
2215
+ this.adapter.log.warn(
2216
+ `[GHOME] Invalid structure for "${devId}": ${JSON.stringify(json[devId])}`,
2217
+ );
2218
+ val = this.reportedStates[devId][_attr].val;
2219
+ } else {
2220
+ // It could be only with one or two colors, so check if any absent
2221
+ const r = json[devId].color_R
2222
+ ? await this.adapter.getForeignStateAsync(json[devId].color_R.id)
2223
+ : { val: 0 };
2224
+ const g = json[devId].color_G
2225
+ ? await this.adapter.getForeignStateAsync(json[devId].color_G.id)
2226
+ : { val: 0 };
2227
+ const b = json[devId].color_B
2228
+ ? await this.adapter.getForeignStateAsync(json[devId].color_B.id)
2229
+ : { val: 0 };
2230
+ const spectrumRgb = (r.val << 16) + (g.val << 8) + b.val;
2231
+
2232
+ val = Math.floor(spectrumRgb);
2233
+ }
2234
+ } else if (_attr === 'color_hue') {
2235
+ json[devId].color = json[devId].color ? json[devId].color : {};
2236
+ const hue = json[devId].color_hue
2237
+ ? await this.adapter.getForeignStateAsync(json[devId].color_hue.id)
2238
+ : { val: 0 };
2239
+ const sat = json[devId].color_saturation
2240
+ ? await this.adapter.getForeignStateAsync(json[devId].color_saturation.id)
2241
+ : { val: 0 };
2242
+ const value = json[devId].brightness
2243
+ ? await this.adapter.getForeignStateAsync(json[devId].brightness.id)
2244
+ : { val: 0 };
2245
+ const h = hue.val / 360;
2246
+ const s = sat.val / 255;
2247
+ const v = value.val / 100;
2248
+ const i = Math.floor(h * 6);
2249
+ const f = h * 6 - i;
2250
+ const p = v * (1 - s);
2251
+ const q = v * (1 - f * s);
2252
+ const t = v * (1 - (1 - f) * s);
2253
+
2254
+ let r;
2255
+ let g;
2256
+ let b;
2257
+
2258
+ switch (i % 6) {
2259
+ case 0:
2260
+ r = v;
2261
+ g = t;
2262
+ b = p;
2263
+ break;
2264
+ case 1:
2265
+ r = q;
2266
+ g = v;
2267
+ b = p;
2268
+ break;
2269
+ case 2:
2270
+ r = p;
2271
+ g = v;
2272
+ b = t;
2273
+ break;
2274
+ case 3:
2275
+ r = p;
2276
+ g = q;
2277
+ b = v;
2278
+ break;
2279
+ case 4:
2280
+ r = t;
2281
+ g = p;
2282
+ b = v;
2283
+ break;
2284
+ case 5:
2285
+ r = v;
2286
+ g = p;
2287
+ b = q;
2288
+ break;
2289
+ }
2290
+ val = Math.floor((r << (16 + g)) << (8 + b));
2291
+ } else if (_attr === 'color_spectrumRGB') {
2292
+ const rgb = state.val;
2293
+ json[devId].color = json[devId].color ? json[devId].color : {};
2294
+ if (rgb && rgb.substring) {
2295
+ val = parseInt(rgb.substring(1), 16) || 0;
2296
+ } else {
2297
+ val = rgb;
2298
+ }
2299
+ } else if (_attr === 'color_temperature') {
2300
+ const temp = state.val;
2301
+ val = Math.floor(temp) || 0;
2302
+ } else if (_attr === 'isLocked') {
2303
+ val = !!state.val;
2304
+ } else if (_attr === 'openDirection') {
2305
+ val = state.val === 2 ? 'DOWN' : 'UP';
2306
+ } else if (_attr === 'openPercent') {
2307
+ if (state.val === true || state.val === false) {
2308
+ val = state.val === false ? 100 : 0;
2309
+ } else {
2310
+ val = parseInt(state.val) || 0;
2311
+ }
2312
+ } else {
2313
+ val = state.val;
2314
+ }
2315
+
2316
+ if (this.smartDevices[devId].conv2GH[custom[attr]]) {
2317
+ try {
2318
+ const conv = new Function('value', this.smartDevices[devId].conv2GH[custom[attr]]);
2319
+ val = conv(val);
2320
+ } catch (e) {
2321
+ this.adapter.log.error(
2322
+ `Invalid convert function in ${devId}/${custom[attr]}: ${this.smartDevices[devId].conv2GH[custom[attr]]}`,
2323
+ );
2324
+ }
2325
+ }
2326
+
2327
+ // do not send the same state to google
2328
+ if (
2329
+ (this.reportedStates[devId] &&
2330
+ this.reportedStates[devId][_attr] &&
2331
+ this.reportedStates[devId][_attr].val !== val) ||
2332
+ !this.reportedStates[devId] ||
2333
+ !this.reportedStates[devId][_attr]
2334
+ ) {
2335
+ this.reportedStates[devId] = this.reportedStates[devId] || {};
2336
+ this.reportedStates[devId][_attr] = this.reportedStates[devId][_attr] || {};
2337
+ this.reportedStates[devId][_attr].val = val;
2338
+ this.reportedStates[devId][_attr].ts = now;
2339
+ json[devId] = json[devId] || {};
2340
+ if (_attr.includes('color_')) {
2341
+ json[devId].color = json[devId].color ? json[devId].color : {};
2342
+ if (_attr === 'color_temperature') {
2343
+ json[devId].color.temperatureK = Math.floor(val);
2344
+ } else {
2345
+ json[devId].color.spectrumRGB = val;
2346
+ }
2347
+ } else if (_attr.substring(4).split('_').length > 1) {
2348
+ const attrArray = _attr.substring(4).split('_');
2349
+ if (json[devId][attrArray[0]]) {
2350
+ json[devId][attrArray[0]][attrArray[1]] = val;
2351
+ } else {
2352
+ json[devId][attrArray[0]] = {};
2353
+ json[devId][attrArray[0]][attrArray[1]] = val;
2354
+ }
2355
+ } else {
2356
+ json[devId][_attr] = val;
2357
+ }
2358
+ found = true;
2359
+ }
2360
+ } catch (error) {
2361
+ this.adapter.log.error(`[GHOME] ${error.message} ${error.stack}`);
2362
+ }
2363
+ }
2364
+ }
2365
+ }
2366
+ }
2367
+
2368
+ if (found && this.urlKey) {
2369
+ const now = Date.now();
2370
+
2371
+ let i = 0;
2372
+ while (i < this.rateCalculation.length) {
2373
+ if (now - this.rateCalculation[i] < 60000) {
2374
+ break;
2375
+ }
2376
+ i++;
2377
+ }
2378
+ if (i) {
2379
+ if (i < this.rateCalculation.length) {
2380
+ this.rateCalculation.splice(0, i);
2381
+ } else {
2382
+ this.rateCalculation = [];
2383
+ }
2384
+ }
2385
+
2386
+ if (this.rateCalculation.length > 60) {
2387
+ return this.adapter.log.warn(
2388
+ `[GHOME] Sending too fast: ${this.rateCalculation.length} in last minute!`,
2389
+ );
2390
+ }
2391
+
2392
+ this.rateCalculation.push(now);
2393
+
2394
+ const url = `${URL_STATUS}?user=${encodeURIComponent(this.adapter.config.login)}&urlKey=${encodeURIComponent(this.urlKey.key)}&p=${PROTOCOL_VERSION}&v=${this.adapter.version}`;
2395
+ axios
2396
+ .post(url, json, { validateStatus: status => status === 200 })
2397
+ .then(response => {
2398
+ if (this.unknownDevices[id]) {
2399
+ delete this.unknownDevices[id];
2400
+ }
2401
+ this.adapter.log.debug(
2402
+ `[GHOME] Status reported: ${JSON.stringify(json)} ${JSON.stringify(response.data)}`,
2403
+ );
2404
+ })
2405
+ .catch(error => {
2406
+ if (error.response && error.response.status === 404) {
2407
+ this.adapter.log.error(`[GHOME] device ${id} is unknown to google home`);
2408
+ this.unknownDevices[id] = Date.now();
2409
+ } else if (error.response && error.response.status === 401) {
2410
+ this.adapter.log.error(`[GHOME] auth error: ${JSON.stringify(error.response.body)}`);
2411
+ this.urlKeyOk = false; // invalidate urlKey
2412
+ } else if (error.response && error.response.status === 410) {
2413
+ this.adapter.log.error(
2414
+ `[GHOME] invalid protocol version: ${JSON.stringify(error.response.body)}`,
2415
+ );
2416
+ this.urlKeyOk = false; // invalidate urlKey
2417
+ } else {
2418
+ let errorMessage;
2419
+ if (error.response) {
2420
+ errorMessage = error.response.data || error.response.status;
2421
+ } else if (error.request) {
2422
+ errorMessage = 'No answer';
2423
+ } else {
2424
+ errorMessage = error.message;
2425
+ }
2426
+
2427
+ this.adapter.log.error(`[GHOME] Cannot updateState: ${JSON.stringify(errorMessage)}`);
2428
+ this.adapter.log.debug(`[GHOME] ${JSON.stringify(json)}`);
2429
+ }
2430
+ });
2431
+ }
2432
+ }
2433
+
2434
+ async updateStates(json) {
2435
+ if (!this.urlKeyOk || !this.urlKey) {
2436
+ return;
2437
+ }
2438
+ if (!json) {
2439
+ json = {};
2440
+ for (const devId in this.smartDevicesSentToGoogle) {
2441
+ if (this.smartDevicesSentToGoogle.hasOwnProperty(devId)) {
2442
+ const custom = this.smartDevicesSentToGoogle[devId].customData;
2443
+ for (const attr in custom) {
2444
+ if (custom.hasOwnProperty(attr) && attr && attr.startsWith('get_')) {
2445
+ const _attr = attr.substring(4);
2446
+ json[devId] = json[devId] || {};
2447
+ json[devId][_attr] = {
2448
+ id: custom[attr],
2449
+ };
2450
+ }
2451
+ }
2452
+ }
2453
+ }
2454
+ }
2455
+
2456
+ for (let devId in json) {
2457
+ try {
2458
+ if (!json.hasOwnProperty(devId)) {
2459
+ continue;
2460
+ }
2461
+
2462
+ for (let attr in json[devId]) {
2463
+ if (!json[devId].hasOwnProperty(attr)) {
2464
+ continue;
2465
+ }
2466
+
2467
+ if (typeof json[devId][attr] === 'object' && json[devId][attr].id) {
2468
+ let state = await this.adapter.getForeignStateAsync(json[devId][attr].id);
2469
+ if (state === null || state.val === null || state.val === '') {
2470
+ delete json[devId][attr];
2471
+ if (Object.keys(json[devId]).length === 0) {
2472
+ delete json[devId];
2473
+ }
2474
+ continue;
2475
+ }
2476
+
2477
+ state = state || { val: false };
2478
+
2479
+ if (
2480
+ this.smartDevices[devId].conv2GH &&
2481
+ this.smartDevices[devId].conv2GH[json[devId][attr].id]
2482
+ ) {
2483
+ try {
2484
+ const conv = new Function(
2485
+ 'value',
2486
+ this.smartDevices[devId].conv2GH[json[devId][attr].id],
2487
+ );
2488
+ state.val = conv(state.val);
2489
+ } catch (e) {
2490
+ this.adapter.log.error(
2491
+ `Invalid convert function in ${devId}/${attr}: ${this.smartDevices[devId].conv2GH[json[devId][attr].id]}`,
2492
+ );
2493
+ }
2494
+ }
2495
+ if (attr === 'on') {
2496
+ json[devId][attr] =
2497
+ state.val === 1 ||
2498
+ state.val === '1' ||
2499
+ state.val === 'true' ||
2500
+ state.val === 'ON' ||
2501
+ state.val === 'on' ||
2502
+ state.val === true ||
2503
+ (typeof state.val === 'number' && state.val > 0);
2504
+ } else if (attr === 'color_R' || attr === 'color_G' || attr === 'color_B') {
2505
+ const r = await this.adapter.getForeignStateAsync(json[devId].color_R.id);
2506
+ const g = await this.adapter.getForeignStateAsync(json[devId].color_G.id);
2507
+ const b = await this.adapter.getForeignStateAsync(json[devId].color_B.id);
2508
+ const spectrumRgb = (r.val << 16) + (g.val << 8) + b.val;
2509
+ json[devId].color = json[devId].color ? json[devId].color : {};
2510
+ json[devId].color.spectrumRGB = Math.floor(spectrumRgb);
2511
+ delete json[devId]['color_R'];
2512
+ delete json[devId]['color_G'];
2513
+ delete json[devId]['color_B'];
2514
+ } else if (attr === 'color_hue') {
2515
+ json[devId].color = json[devId].color ? json[devId].color : {};
2516
+ if (json[devId].color_hue && json[devId].color_saturation && json[devId].brightness) {
2517
+ try {
2518
+ const hue = await this.adapter.getForeignStateAsync(json[devId].color_hue.id);
2519
+ const sat = await this.adapter.getForeignStateAsync(
2520
+ json[devId].color_saturation.id,
2521
+ );
2522
+ const val = await this.adapter.getForeignStateAsync(json[devId].brightness.id);
2523
+ const h = hue.val / 360;
2524
+ const s = sat.val / 255;
2525
+ const v = val.val / 100;
2526
+ const i = Math.floor(h * 6);
2527
+ const f = h * 6 - i;
2528
+ const p = v * (1 - s);
2529
+ const q = v * (1 - f * s);
2530
+ const t = v * (1 - (1 - f) * s);
2531
+ let r, g, b;
2532
+ switch (i % 6) {
2533
+ case 0:
2534
+ r = v;
2535
+ g = t;
2536
+ b = p;
2537
+ break;
2538
+ case 1:
2539
+ r = q;
2540
+ g = v;
2541
+ b = p;
2542
+ break;
2543
+ case 2:
2544
+ r = p;
2545
+ g = v;
2546
+ b = t;
2547
+ break;
2548
+ case 3:
2549
+ r = p;
2550
+ g = q;
2551
+ b = v;
2552
+ break;
2553
+ case 4:
2554
+ r = t;
2555
+ g = p;
2556
+ b = v;
2557
+ break;
2558
+ case 5:
2559
+ r = v;
2560
+ g = p;
2561
+ b = q;
2562
+ break;
2563
+ }
2564
+ json[devId].color.spectrumRGB = Math.floor((r << (16 + g)) << (8 + b));
2565
+ } catch (error) {
2566
+ this.adapter.log.error(`[GHOME] ${error}`);
2567
+ }
2568
+ }
2569
+ delete json[devId]['color_hue'];
2570
+ delete json[devId]['color_saturation'];
2571
+ delete json[devId]['brightness'];
2572
+ } else if (attr === 'color_spectrumRGB') {
2573
+ const rgb = state.val;
2574
+ json[devId].color = json[devId].color ? json[devId].color : {};
2575
+ if (rgb && rgb.substring) {
2576
+ json[devId].color.spectrumRGB = parseInt(rgb.substring(1), 16) || 0;
2577
+ } else {
2578
+ json[devId].color.spectrumRGB = rgb;
2579
+ }
2580
+
2581
+ delete json[devId][attr];
2582
+ } else if (attr === 'color_temperature') {
2583
+ const temp = state.val;
2584
+
2585
+ json[devId].color = json[devId].color ? json[devId].color : {};
2586
+ json[devId].color.temperatureK = Math.floor(temp);
2587
+
2588
+ delete json[devId][attr];
2589
+ } else if (attr === 'isLocked') {
2590
+ json[devId][attr] = !!state.val;
2591
+ } else if (attr === 'openDirection') {
2592
+ json[devId][attr] = state.val === 2 ? 'DOWN' : 'UP';
2593
+ } else if (attr === 'openPercent') {
2594
+ if (state.val === true || state.val === false || isNaN(state.val)) {
2595
+ json[devId][attr] = state.val === false ? 100 : 0;
2596
+ } else {
2597
+ json[devId][attr] = parseInt(state.val) || 0;
2598
+ }
2599
+ } else if (attr === 'thermostatTemperatureSetpoint') {
2600
+ json[devId][attr] = state.val;
2601
+ if (json[devId].thermostatTemperatureAmbient > json[devId].thermostatTemperatureSetpoint) {
2602
+ json[devId].thermostatMode = 'cool';
2603
+ } else {
2604
+ json[devId].thermostatMode = 'heat';
2605
+ }
2606
+ } else {
2607
+ if (attr.split('_').length > 1) {
2608
+ const attrArray = attr.split('_');
2609
+ delete json[devId][attr];
2610
+ if (json[devId][attrArray[0]]) {
2611
+ json[devId][attrArray[0]][attrArray[1]] = state.val;
2612
+ } else {
2613
+ json[devId][attrArray[0]] = {};
2614
+ json[devId][attrArray[0]][attrArray[1]] = state.val;
2615
+ }
2616
+ } else {
2617
+ if (!isNaN(state.val)) {
2618
+ state.val = Math.floor(state.val);
2619
+ }
2620
+ json[devId][attr] = state.val;
2621
+ }
2622
+ }
2623
+ // return;
2624
+ }
2625
+ }
2626
+ } catch (error) {
2627
+ this.adapter.log.error(`[GHOME] ${error}`);
2628
+ this.adapter.log.error(`[GHOME] ${error.stack}`);
2629
+ }
2630
+ }
2631
+
2632
+ const url = `${URL_STATUS}?user=${encodeURIComponent(this.adapter.config.login)}&urlKey=${encodeURIComponent(this.urlKey.key)}&p=${PROTOCOL_VERSION}&v=${this.adapter.version}`;
2633
+ const sendRequest = () => {
2634
+ axios
2635
+ .post(url, json, { validateStatus: status => status === 200 })
2636
+ .then(response => {
2637
+ Object.keys(this.unknownDevices).forEach(id => {
2638
+ if (this.unknownDevices[id]) {
2639
+ delete this.unknownDevices[id];
2640
+ }
2641
+ });
2642
+
2643
+ this.adapter.log.debug(`[GHOME] Status reported: ${JSON.stringify(response.data)}`);
2644
+ })
2645
+ .catch(error => {
2646
+ if (error.response && error.response.status === 404) {
2647
+ this.adapter.log.error(`[GHOME] devices are unknown to google home`);
2648
+ Object.keys(this.unknownDevices).forEach(id => (this.unknownDevices[id] = Date.now()));
2649
+ } else if (error.response && error.response.status === 401) {
2650
+ this.adapter.log.error(`[GHOME] auth error: ${JSON.stringify(error.response.data)}`);
2651
+ this.urlKeyOk = false; // invalidate urlKey
2652
+ } else if (error.response && error.response.status === 410) {
2653
+ this.adapter.log.error(
2654
+ `[GHOME] invalid protocol version: ${JSON.stringify(error.response.data)}`,
2655
+ );
2656
+ this.urlKeyOk = false; // invalidate urlKey
2657
+ } else {
2658
+ let errorMessage;
2659
+ if (error.response) {
2660
+ errorMessage = error.response.data || error.response.status;
2661
+ } else if (error.request) {
2662
+ errorMessage = 'No answer';
2663
+ } else {
2664
+ errorMessage = error.message;
2665
+ }
2666
+ this.adapter.log.error(`[GHOME] Cannot updateStates: ${errorMessage}`);
2667
+ this.adapter.log.debug(`[GHOME] ${JSON.stringify(json)}`);
2668
+ }
2669
+ });
2670
+ };
2671
+ setTimeout(() => sendRequest(), 100);
2672
+ }
2673
+
2674
+ // https://developers.google.com/actions/smarthome/create#request
2675
+ sync(requestId) {
2676
+ this.smartDevicesSentToGoogle = {};
2677
+ let devicesArray = [];
2678
+ Object.keys(this.smartDevices).map(id => {
2679
+ if (!this.smartDevices[id].merged) {
2680
+ if (
2681
+ this.smartDevices[id].name &&
2682
+ this.smartDevices[id].name.name &&
2683
+ this.smartDevices[id].type &&
2684
+ this.smartDevices[id].traits &&
2685
+ this.smartDevices[id].traits.length > 0
2686
+ ) {
2687
+ const dev = JSON.parse(JSON.stringify(this.smartDevices[id]));
2688
+ delete dev.displayTraits;
2689
+ delete dev.displayAttributes;
2690
+ delete dev.smartEnum;
2691
+ delete dev.ioType;
2692
+ delete dev.parentId;
2693
+ delete dev.conv2GH;
2694
+ delete dev.displayConv2GH;
2695
+ delete dev.conv2iob;
2696
+ delete dev.displayConv2iob;
2697
+ // not yet active
2698
+ // dev.willReportState = this.urlKeyOk;
2699
+ devicesArray.push(dev);
2700
+ return dev;
2701
+ } else if (!this.smartDevices[id].parentId) {
2702
+ this.adapter.log.warn(`[GHOME] Missing name, type or treat for: ${id}. Not added to GoogleHome`);
2703
+ this.adapter.log.debug(`[GHOME] ${JSON.stringify(this.smartDevices[id])}`);
2704
+ }
2705
+ }
2706
+ return null;
2707
+ });
2708
+
2709
+ if (devicesArray.length === 0) {
2710
+ this.adapter.log.warn(
2711
+ '[GHOME] No devices defined. Did you add no sensors or indicate states to rooms and enums?',
2712
+ );
2713
+ }
2714
+
2715
+ devicesArray.forEach(element => (this.smartDevicesSentToGoogle[element.id] = element));
2716
+
2717
+ this.tasksTimer && clearTimeout(this.tasksTimer);
2718
+ this.tasksTimer = setTimeout(() => this.updateStates(), 6000);
2719
+
2720
+ return {
2721
+ requestId,
2722
+ payload: {
2723
+ agentUserId: this.agentUserId,
2724
+ devices: devicesArray,
2725
+ },
2726
+ };
2727
+ }
2728
+
2729
+ getStates(ids, callback, states) {
2730
+ states = states || {};
2731
+ if (!ids || !ids.length) {
2732
+ callback(states);
2733
+ } else {
2734
+ const id = ids.shift();
2735
+ if (id === undefined) {
2736
+ setImmediate(() => this.getStates(ids, callback, states));
2737
+ } else {
2738
+ this.adapter.getForeignState(id, (err, state) => {
2739
+ states[id] = state && state.val;
2740
+ setImmediate(() => this.getStates(ids, callback, states));
2741
+ });
2742
+ }
2743
+ }
2744
+ }
2745
+
2746
+ // possible responses
2747
+ query(requestId, devices) {
2748
+ return new Promise(resolve => {
2749
+ const responseDev = {};
2750
+ const ids = [];
2751
+ try {
2752
+ devices.forEach(dev => {
2753
+ if (this.smartDevices[dev.id]) {
2754
+ const attrs = this.smartDevices[dev.id].customData;
2755
+ if (attrs) {
2756
+ Object.keys(attrs).forEach(
2757
+ attr =>
2758
+ attr &&
2759
+ attr.startsWith('get_') &&
2760
+ attrs[attr] !== undefined &&
2761
+ !ids.includes(attrs[attr]) &&
2762
+ ids.push(attrs[attr]),
2763
+ );
2764
+ } else {
2765
+ this.adapter.log.error(
2766
+ `[GHOME] No Google Home customData for ${dev.id} cannot handle Google Request.`,
2767
+ );
2768
+ }
2769
+ }
2770
+ });
2771
+
2772
+ this.getStates(ids, states => {
2773
+ devices.forEach(dev => {
2774
+ if (this.smartDevices[dev.id]) {
2775
+ responseDev[dev.id] = {
2776
+ online: true,
2777
+ };
2778
+ const attrs = this.smartDevices[dev.id].customData;
2779
+ if (attrs) {
2780
+ Object.keys(attrs).forEach(attr => {
2781
+ if (attr && attr.startsWith('get_')) {
2782
+ if (
2783
+ this.smartDevices[dev.id].conv2GH &&
2784
+ this.smartDevices[dev.id].conv2GH[attrs[attr]]
2785
+ ) {
2786
+ try {
2787
+ const conv = new Function(
2788
+ 'value',
2789
+ this.smartDevices[dev.id].conv2GH[attrs[attr]],
2790
+ );
2791
+ states[attrs[attr]] = conv(states[attrs[attr]]);
2792
+ } catch (e) {
2793
+ this.adapter.log.error(
2794
+ `Invalid convert function in ${dev.id}/${attrs[attr]}: ${this.smartDevices[dev.id].conv2GH[attrs[attr]]}`,
2795
+ );
2796
+ }
2797
+ }
2798
+ if (attr.substring(4).split('_').length > 1) {
2799
+ const attrArray = attr.substring(4).split('_');
2800
+ if (
2801
+ attr === 'get_color_R' ||
2802
+ attr === 'get_color_G' ||
2803
+ attr === 'get_color_B'
2804
+ ) {
2805
+ const spectrumRgb =
2806
+ (states[attrs.get_color_R] << 16) +
2807
+ (states[attrs.get_color_G] << 8) +
2808
+ states[attrs.get_color_B];
2809
+ responseDev[dev.id].color = responseDev[dev.id].color
2810
+ ? responseDev[dev.id].color
2811
+ : {};
2812
+ responseDev[dev.id].color.spectrumRgb = spectrumRgb;
2813
+ } else if (attr === 'get_color_hue') {
2814
+ responseDev[dev.id].color = responseDev[dev.id].color
2815
+ ? responseDev[dev.id].color
2816
+ : {};
2817
+ const h = states[attrs.get_color_hue] / 360;
2818
+ const s = states[attrs.get_color_saturation] / 255;
2819
+ const v = states[attrs.get_brightness] / 100;
2820
+ const i = Math.floor(h * 6);
2821
+ const f = h * 6 - i;
2822
+ const p = v * (1 - s);
2823
+ const q = v * (1 - f * s);
2824
+ const t = v * (1 - (1 - f) * s);
2825
+ let r, g, b;
2826
+ switch (i % 6) {
2827
+ case 0:
2828
+ r = v;
2829
+ g = t;
2830
+ b = p;
2831
+ break;
2832
+ case 1:
2833
+ r = q;
2834
+ g = v;
2835
+ b = p;
2836
+ break;
2837
+ case 2:
2838
+ r = p;
2839
+ g = v;
2840
+ b = t;
2841
+ break;
2842
+ case 3:
2843
+ r = p;
2844
+ g = q;
2845
+ b = v;
2846
+ break;
2847
+ case 4:
2848
+ r = t;
2849
+ g = p;
2850
+ b = v;
2851
+ break;
2852
+ case 5:
2853
+ r = v;
2854
+ g = p;
2855
+ b = q;
2856
+ break;
2857
+ }
2858
+ responseDev[dev.id].color.spectrumRgb = (r << (16 + g)) << (8 + b);
2859
+ } else if (attr === 'get_color_spectrumRGB') {
2860
+ const rgb = states[attrs.get_color_spectrumRGB];
2861
+ responseDev[dev.id].color = responseDev[dev.id].color
2862
+ ? responseDev[dev.id].color
2863
+ : {};
2864
+ if (rgb && rgb.substring) {
2865
+ responseDev[dev.id].color.spectrumRgb =
2866
+ parseInt(rgb.substring(1), 16) || 0;
2867
+ } else {
2868
+ responseDev[dev.id].color.spectrumRgb = rgb;
2869
+ }
2870
+ } else {
2871
+ if (responseDev[dev.id][attrArray[0]]) {
2872
+ responseDev[dev.id][attrArray[0]][attrArray[1]] =
2873
+ states[attrs[attr]];
2874
+ } else {
2875
+ responseDev[dev.id][attrArray[0]] = {};
2876
+ responseDev[dev.id][attrArray[0]][attrArray[1]] =
2877
+ states[attrs[attr]];
2878
+ }
2879
+ }
2880
+ } else {
2881
+ if (
2882
+ attr === 'get_isLocked' &&
2883
+ states[attrs[attr]] !== true &&
2884
+ states[attrs[attr]] !== false
2885
+ ) {
2886
+ responseDev[dev.id][attr.substring(4)] = states[attrs[attr]] === 0;
2887
+ }
2888
+ if (
2889
+ attr === 'get_openPercent' &&
2890
+ (states[attrs[attr]] === true || states[attrs[attr]] === false)
2891
+ ) {
2892
+ responseDev[dev.id][attr.substring(4)] =
2893
+ states[attrs[attr]] === true ? 0 : 100;
2894
+ } else {
2895
+ responseDev[dev.id][attr.substring(4)] = states[attrs[attr]];
2896
+ }
2897
+ }
2898
+ }
2899
+ });
2900
+ } else {
2901
+ this.adapter.log.error(
2902
+ `[GHOME] No Google Home customData for ${dev.id} cannot handle Google Request.`,
2903
+ );
2904
+ }
2905
+ //Workaround for no thermostat mode available
2906
+ if (responseDev[dev.id].thermostatTemperatureSetpoint) {
2907
+ if (
2908
+ responseDev[dev.id].thermostatTemperatureAmbient >
2909
+ responseDev[dev.id].thermostatTemperatureSetpoint
2910
+ ) {
2911
+ responseDev[dev.id].thermostatMode = 'cool';
2912
+ } else {
2913
+ responseDev[dev.id].thermostatMode = 'heat';
2914
+ }
2915
+ }
2916
+ } else {
2917
+ responseDev[dev.id] = {
2918
+ online: false,
2919
+ };
2920
+ }
2921
+
2922
+ /*responseDev[dev.id] = {
2923
+ on: true,
2924
+ online: true,
2925
+
2926
+ brightness: 44,
2927
+ color: {
2928
+ name: 'soft white',
2929
+ temperature: 2700
2930
+ }
2931
+ }*/
2932
+ });
2933
+ resolve({
2934
+ requestId,
2935
+ payload: {
2936
+ devices: responseDev,
2937
+ },
2938
+ });
2939
+ });
2940
+ } catch (error) {
2941
+ this.adapter.log.error(`[GHOME] ${error.stack}`);
2942
+ this.adapter.log.error(`[GHOME] ${JSON.stringify(devices)}`);
2943
+ this.adapter.log.error(`[GHOME] ${(ids || []).toString()}`);
2944
+ }
2945
+ });
2946
+ }
2947
+
2948
+ setStates(tasks, callback, results) {
2949
+ results = results || {};
2950
+ if (!tasks || !tasks.length) {
2951
+ callback(results);
2952
+ } else {
2953
+ const task = tasks.shift();
2954
+ this.adapter.log.debug(`[GHOME] setState: ${JSON.stringify(task)}`);
2955
+
2956
+ if (task.cmd === 'action.devices.commands.SetToggles') {
2957
+ this.adapter.getForeignState(task.id, task.val, (error, state) => {
2958
+ if (!error) {
2959
+ const val = !(state && state.val);
2960
+ this.adapter.setForeignState(task.id, val, error => {
2961
+ results[task.devId] = results[task.devId] || {};
2962
+ results[task.devId][task.param] = val;
2963
+ if (error) {
2964
+ results[task.devId].error = error;
2965
+ }
2966
+ setImmediate(() => this.setStates(tasks, callback, results));
2967
+ });
2968
+ } else {
2969
+ results[task.devId] = {
2970
+ error,
2971
+ };
2972
+ setImmediate(() => this.setStates(tasks, callback, results));
2973
+ }
2974
+ });
2975
+ } else {
2976
+ this.adapter.setForeignState(task.id, task.val, error => {
2977
+ results[task.devId] = results[task.devId] || {};
2978
+ results[task.devId][task.param] = task.val;
2979
+
2980
+ if (error) {
2981
+ results[task.devId].error = error;
2982
+ }
2983
+ setImmediate(() => this.setStates(tasks, callback, results));
2984
+ });
2985
+ }
2986
+ }
2987
+ }
2988
+
2989
+ execute(requestId, commands) {
2990
+ return new Promise(resolve => {
2991
+ if (!commands) {
2992
+ this.adapter.log.error('[GHOME] Invalid parameter commands - NULL');
2993
+ resolve({
2994
+ error: 'Invalid parameter',
2995
+ });
2996
+ }
2997
+
2998
+ const tasks = [];
2999
+ commands.forEach(command => {
3000
+ command.execution.forEach(execute => {
3001
+ this.adapter.log.debug(`[GHOME] execute ${execute.command} => ${JSON.stringify(execute.params)}`);
3002
+
3003
+ command.devices.forEach(dev => {
3004
+ if (this.smartDevices[dev.id]) {
3005
+ const attrs = this.smartDevices[dev.id].customData;
3006
+
3007
+ if (!attrs) {
3008
+ return this.adapter.log.error(
3009
+ `[GHOME] Try to control ${dev.id}, but the device is not configured for Google Home`,
3010
+ );
3011
+ }
3012
+
3013
+ if (execute.command === 'action.devices.commands.SetToggles') {
3014
+ if (attrs.set_on) {
3015
+ tasks.push({
3016
+ id: attrs.set_on,
3017
+ cmd: execute.command,
3018
+ param: 'on',
3019
+ devId: dev.id,
3020
+ });
3021
+ }
3022
+ } else {
3023
+ if (
3024
+ (typeof execute.params === 'undefined' ||
3025
+ execute.command === 'action.devices.commands.ActivateScene') &&
3026
+ execute.command
3027
+ ) {
3028
+ const command = execute.command.split('.').pop();
3029
+ execute.params = {};
3030
+ execute.params[command] = !execute.params.deactivate;
3031
+ }
3032
+
3033
+ Object.keys(execute.params).forEach(param => {
3034
+ let paths = [];
3035
+ if (typeof execute.params[param] === 'object') {
3036
+ Object.keys(execute.params[param]).forEach(subElement => {
3037
+ if (subElement === 'spectrumRGB' && attrs.set_color_R) {
3038
+ const rgb = parseInt(execute.params.color.spectrumRGB) || 0;
3039
+ paths.push({
3040
+ path: 'color_R',
3041
+ val: (rgb & 0xff0000) >> 16,
3042
+ });
3043
+ paths.push({
3044
+ path: 'color_G',
3045
+ val: (rgb & 0x00ff00) >> 8,
3046
+ });
3047
+ paths.push({
3048
+ path: 'color_B',
3049
+ val: rgb & 0x0000ff,
3050
+ });
3051
+ } else if (subElement === 'spectrumRGB' && attrs.set_color_hue) {
3052
+ const rgb = parseInt(execute.params.color.spectrumRGB) || 0;
3053
+ const r = ((rgb & 0xff0000) >> 16) / 255;
3054
+ const g = ((rgb & 0x00ff00) >> 8) / 255;
3055
+ const b = (rgb & 0x0000ff) / 255;
3056
+
3057
+ const max = Math.max(r, g, b),
3058
+ min = Math.min(r, g, b);
3059
+ let h,
3060
+ s,
3061
+ v = max;
3062
+
3063
+ let d = max - min;
3064
+ s = max ? d / max : 0;
3065
+
3066
+ if (max === min) {
3067
+ h = 0; // achromatic
3068
+ } else {
3069
+ switch (max) {
3070
+ case r:
3071
+ h = (g - b) / d + (g < b ? 6 : 0);
3072
+ break;
3073
+ case g:
3074
+ h = (b - r) / d + 2;
3075
+ break;
3076
+ case b:
3077
+ h = (r - g) / d + 4;
3078
+ break;
3079
+ }
3080
+
3081
+ h /= 6;
3082
+ }
3083
+ paths.push({
3084
+ path: 'color_hue',
3085
+ val: h,
3086
+ });
3087
+ paths.push({
3088
+ path: 'color_saturation',
3089
+ val: s,
3090
+ });
3091
+ paths.push({
3092
+ path: 'brightness',
3093
+ val: v,
3094
+ });
3095
+ } else if (subElement === 'spectrumRGB') {
3096
+ const rgb = parseInt(execute.params.color.spectrumRGB) || 0;
3097
+ paths.push({
3098
+ path: 'color_spectrumRGB',
3099
+ val: `#${('000000' + rgb.toString(16)).substr(-6)}`,
3100
+ });
3101
+ } else {
3102
+ paths.push({
3103
+ path: `${param}_${subElement}`,
3104
+ val: execute.params[param][subElement],
3105
+ });
3106
+ }
3107
+ });
3108
+ } else {
3109
+ // if there no dedicated onoff use onoff trait and translate it to 0 : 100
3110
+ if (param === 'on' && attrs[`set_${param}`] === attrs.set_brightness) {
3111
+ paths.push({
3112
+ path: param,
3113
+ val: execute.params[param] ? 100 : 0,
3114
+ });
3115
+ } else {
3116
+ paths.push({
3117
+ path: param,
3118
+ val: execute.params[param],
3119
+ });
3120
+ }
3121
+ }
3122
+
3123
+ paths.forEach(element => {
3124
+ if (attrs[`set_${element.path}`]) {
3125
+ if (
3126
+ this.smartDevices[dev.id].conv2iob &&
3127
+ this.smartDevices[dev.id].conv2iob[attrs[`set_${element.path}`]]
3128
+ ) {
3129
+ try {
3130
+ const conv = new Function(
3131
+ 'value',
3132
+ this.smartDevices[dev.id].conv2iob[
3133
+ attrs[`set_${element.path}`]
3134
+ ],
3135
+ );
3136
+ element.val = conv(element.val);
3137
+ } catch (e) {
3138
+ this.adapter.log.error(
3139
+ `Invalid convert function in ${dev.id}/${attrs[`set_${element.path}`]}: ${this.smartDevices[dev.id].conv2iob[attrs[`set_${element.path}`]]}`,
3140
+ );
3141
+ }
3142
+ }
3143
+ tasks.push({
3144
+ id: attrs[`set_${element.path}`],
3145
+ val: element.val,
3146
+ param,
3147
+ devId: dev.id,
3148
+ });
3149
+ }
3150
+ });
3151
+ });
3152
+ }
3153
+ }
3154
+ });
3155
+ });
3156
+ });
3157
+
3158
+ this.setStates(tasks, results => {
3159
+ const responseCommands = Object.keys(results).map(devId => {
3160
+ if (results[devId].error) {
3161
+ return {
3162
+ ids: [devId],
3163
+ status: 'ERROR',
3164
+ errorCode: results[devId].error,
3165
+ };
3166
+ } else {
3167
+ if (results[devId].online === undefined) {
3168
+ results[devId].online = true;
3169
+ }
3170
+ return {
3171
+ ids: [devId],
3172
+ status: 'SUCCESS',
3173
+ states: results[devId],
3174
+ };
3175
+ }
3176
+ });
3177
+
3178
+ resolve({
3179
+ requestId,
3180
+ payload: {
3181
+ commands: responseCommands,
3182
+ },
3183
+ });
3184
+ });
3185
+ });
3186
+ }
3187
+
3188
+ async process(request, isEnabled) {
3189
+ if (!request) {
3190
+ this.adapter.log.error('[GHOME] Invalid request: no request!');
3191
+ return;
3192
+ }
3193
+
3194
+ if (!isEnabled) {
3195
+ return {
3196
+ error: textsT(this.lang, 'The service deactivated'),
3197
+ errorCode: 501,
3198
+ };
3199
+ }
3200
+
3201
+ if (!request.inputs || !request.inputs.length) {
3202
+ return {
3203
+ error: textsT(this.lang, 'missing inputs'),
3204
+ errorCode: 401,
3205
+ };
3206
+ }
3207
+
3208
+ // actually we take only first input from request
3209
+ for (let i = 0; i < request.inputs.length; i++) {
3210
+ const input = request.inputs[i];
3211
+ const intent = input.intent;
3212
+ if (!intent) {
3213
+ continue;
3214
+ }
3215
+
3216
+ this.adapter.log.debug(`[GHOME] Received ${intent}`);
3217
+
3218
+ switch (intent) {
3219
+ case 'action.devices.SYNC':
3220
+ /**
3221
+ * request:
3222
+ * {
3223
+ * "requestId": "ff36a3cc-ec34-11e6-b1a0-64510650abcf",
3224
+ * "inputs": [{
3225
+ * "intent": "action.devices.SYNC",
3226
+ * }]
3227
+ * }
3228
+ */
3229
+ // sync
3230
+ return this.sync(request.requestId);
3231
+
3232
+ case 'action.devices.QUERY':
3233
+ /**
3234
+ * request:
3235
+ * {
3236
+ * "requestId": "ff36a3cc-ec34-11e6-b1a0-64510650abcf",
3237
+ * "inputs": [{
3238
+ * "intent": "action.devices.QUERY",
3239
+ * "payload": {
3240
+ * "devices": [{
3241
+ * "id": "123",
3242
+ * "customData": {
3243
+ * "fooValue": 12,
3244
+ * "barValue": true,
3245
+ * "bazValue": "alpaca sauce"
3246
+ * }
3247
+ * }, {
3248
+ * "id": "234",
3249
+ * "customData": {
3250
+ * "fooValue": 74,
3251
+ * "barValue": false,
3252
+ * "bazValue": "sheep dip"
3253
+ * }
3254
+ * }]
3255
+ * }
3256
+ * }]
3257
+ * }
3258
+ */
3259
+
3260
+ // async
3261
+ return this.query(request.requestId, input.payload.devices);
3262
+
3263
+ case 'action.devices.EXECUTE':
3264
+ /**
3265
+ * request:
3266
+ * {
3267
+ * "requestId": "ff36a3cc-ec34-11e6-b1a0-64510650abcf",
3268
+ * "inputs": [{
3269
+ * "intent": "action.devices.EXECUTE",
3270
+ * "payload": {
3271
+ * "commands": [{
3272
+ * "devices": [{
3273
+ * "id": "123",
3274
+ * "customData": {
3275
+ * "fooValue": 12,
3276
+ * "barValue": true,
3277
+ * "bazValue": "alpaca sauce"
3278
+ * }
3279
+ * }, {
3280
+ * "id": "234",
3281
+ * "customData": {
3282
+ * "fooValue": 74,
3283
+ * "barValue": false,
3284
+ * "bazValue": "sheep dip"
3285
+ * }
3286
+ * }],
3287
+ * "execution": [{
3288
+ * "command": "action.devices.commands.OnOff",
3289
+ * "params": {
3290
+ * "on": true
3291
+ * }
3292
+ * }]
3293
+ * }]
3294
+ * }
3295
+ * }]
3296
+ * }
3297
+ */
3298
+
3299
+ // async
3300
+ return this.execute(request.requestId, input.payload.commands);
3301
+
3302
+ case 'action.devices.DISCONNECT':
3303
+ this.adapter.log.info('[GHOME] Google home unlinked!');
3304
+ // sync
3305
+ return {};
3306
+
3307
+ default:
3308
+ return {
3309
+ error: textsT(this.lang, 'missing intent'),
3310
+ errorCode: 401,
3311
+ };
3312
+ }
3313
+ }
3314
+
3315
+ return {
3316
+ error: textsT(this.lang, 'missing inputs'),
3317
+ errorCode: 401,
3318
+ };
3319
+ }
3320
+ }
3321
+
3322
+ module.exports = GoogleHome;