@homebridge-plugins/homebridge-tuya 2.0.2-beta.3

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 (191) hide show
  1. package/.idea/copilot.data.migration.agent.xml +6 -0
  2. package/.idea/copilot.data.migration.ask.xml +6 -0
  3. package/.idea/copilot.data.migration.ask2agent.xml +6 -0
  4. package/.idea/copilot.data.migration.edit.xml +6 -0
  5. package/.idea/dictionaries/benpotter.xml +3 -0
  6. package/.idea/homebridge-tuya.iml +8 -0
  7. package/.idea/inspectionProfiles/Project_Default.xml +6 -0
  8. package/.idea/modules.xml +8 -0
  9. package/.idea/vcs.xml +6 -0
  10. package/.idea/workspace.xml +64 -0
  11. package/ADVANCED_OPTIONS.md +267 -0
  12. package/CHANGELOG.md +127 -0
  13. package/LICENSE +20 -0
  14. package/README.md +206 -0
  15. package/SUPPORTED_DEVICES.md +206 -0
  16. package/config.schema.json +550 -0
  17. package/dist/accessory/AccessoryFactory.js +274 -0
  18. package/dist/accessory/AccessoryFactory.js.map +1 -0
  19. package/dist/accessory/AirConditionerAccessory.js +307 -0
  20. package/dist/accessory/AirConditionerAccessory.js.map +1 -0
  21. package/dist/accessory/AirPurifierAccessory.js +90 -0
  22. package/dist/accessory/AirPurifierAccessory.js.map +1 -0
  23. package/dist/accessory/AirQualitySensorAccessory.js +30 -0
  24. package/dist/accessory/AirQualitySensorAccessory.js.map +1 -0
  25. package/dist/accessory/BaseAccessory.js +295 -0
  26. package/dist/accessory/BaseAccessory.js.map +1 -0
  27. package/dist/accessory/CameraAccessory.js +117 -0
  28. package/dist/accessory/CameraAccessory.js.map +1 -0
  29. package/dist/accessory/CarbonDioxideSensorAccessory.js +52 -0
  30. package/dist/accessory/CarbonDioxideSensorAccessory.js.map +1 -0
  31. package/dist/accessory/CarbonMonoxideSensorAccessory.js +52 -0
  32. package/dist/accessory/CarbonMonoxideSensorAccessory.js.map +1 -0
  33. package/dist/accessory/ContactSensorAccessory.js +30 -0
  34. package/dist/accessory/ContactSensorAccessory.js.map +1 -0
  35. package/dist/accessory/DehumidifierAccessory.js +68 -0
  36. package/dist/accessory/DehumidifierAccessory.js.map +1 -0
  37. package/dist/accessory/DiffuserAccessory.js +55 -0
  38. package/dist/accessory/DiffuserAccessory.js.map +1 -0
  39. package/dist/accessory/DimmerAccessory.js +94 -0
  40. package/dist/accessory/DimmerAccessory.js.map +1 -0
  41. package/dist/accessory/DoorbellAccessory.js +89 -0
  42. package/dist/accessory/DoorbellAccessory.js.map +1 -0
  43. package/dist/accessory/ExtractionHoodAccessory.js +118 -0
  44. package/dist/accessory/ExtractionHoodAccessory.js.map +1 -0
  45. package/dist/accessory/FanAccessory.js +123 -0
  46. package/dist/accessory/FanAccessory.js.map +1 -0
  47. package/dist/accessory/GarageDoorAccessory.js +69 -0
  48. package/dist/accessory/GarageDoorAccessory.js.map +1 -0
  49. package/dist/accessory/HeaterAccessory.js +96 -0
  50. package/dist/accessory/HeaterAccessory.js.map +1 -0
  51. package/dist/accessory/HumanPresenceSensorAccessory.js +20 -0
  52. package/dist/accessory/HumanPresenceSensorAccessory.js.map +1 -0
  53. package/dist/accessory/HumidifierAccessory.js +135 -0
  54. package/dist/accessory/HumidifierAccessory.js.map +1 -0
  55. package/dist/accessory/IRAirConditionerAccessory.js +285 -0
  56. package/dist/accessory/IRAirConditionerAccessory.js.map +1 -0
  57. package/dist/accessory/IRControlHubAccessory.js +49 -0
  58. package/dist/accessory/IRControlHubAccessory.js.map +1 -0
  59. package/dist/accessory/IRControlHubSubAccessory.js +53 -0
  60. package/dist/accessory/IRControlHubSubAccessory.js.map +1 -0
  61. package/dist/accessory/IRGenericAccessory.js +50 -0
  62. package/dist/accessory/IRGenericAccessory.js.map +1 -0
  63. package/dist/accessory/LeakSensorAccessory.js +36 -0
  64. package/dist/accessory/LeakSensorAccessory.js.map +1 -0
  65. package/dist/accessory/LightAccessory.js +36 -0
  66. package/dist/accessory/LightAccessory.js.map +1 -0
  67. package/dist/accessory/LightSensorAccessory.js +32 -0
  68. package/dist/accessory/LightSensorAccessory.js.map +1 -0
  69. package/dist/accessory/LocationWeatherAccessory.js +72 -0
  70. package/dist/accessory/LocationWeatherAccessory.js.map +1 -0
  71. package/dist/accessory/LockAccessory.js +56 -0
  72. package/dist/accessory/LockAccessory.js.map +1 -0
  73. package/dist/accessory/MotionSensorAccessory.js +20 -0
  74. package/dist/accessory/MotionSensorAccessory.js.map +1 -0
  75. package/dist/accessory/OutletAccessory.js +23 -0
  76. package/dist/accessory/OutletAccessory.js.map +1 -0
  77. package/dist/accessory/PetFeederAccessory.js +139 -0
  78. package/dist/accessory/PetFeederAccessory.js.map +1 -0
  79. package/dist/accessory/SceneAccessory.js +32 -0
  80. package/dist/accessory/SceneAccessory.js.map +1 -0
  81. package/dist/accessory/SceneSwitchAccessory.js +44 -0
  82. package/dist/accessory/SceneSwitchAccessory.js.map +1 -0
  83. package/dist/accessory/SecuritySystemAccessory.js +30 -0
  84. package/dist/accessory/SecuritySystemAccessory.js.map +1 -0
  85. package/dist/accessory/SmokeSensorAccessory.js +35 -0
  86. package/dist/accessory/SmokeSensorAccessory.js.map +1 -0
  87. package/dist/accessory/SwitchAccessory.js +86 -0
  88. package/dist/accessory/SwitchAccessory.js.map +1 -0
  89. package/dist/accessory/TemperatureHumiditySensorAccessory.js +24 -0
  90. package/dist/accessory/TemperatureHumiditySensorAccessory.js.map +1 -0
  91. package/dist/accessory/ThermostatAccessory.js +190 -0
  92. package/dist/accessory/ThermostatAccessory.js.map +1 -0
  93. package/dist/accessory/ValveAccessory.js +45 -0
  94. package/dist/accessory/ValveAccessory.js.map +1 -0
  95. package/dist/accessory/VibrationSensorAccessory.js +42 -0
  96. package/dist/accessory/VibrationSensorAccessory.js.map +1 -0
  97. package/dist/accessory/WeatherStationAccessory.js +59 -0
  98. package/dist/accessory/WeatherStationAccessory.js.map +1 -0
  99. package/dist/accessory/WetBulbGlobeTemperatureAccessory.js +23 -0
  100. package/dist/accessory/WetBulbGlobeTemperatureAccessory.js.map +1 -0
  101. package/dist/accessory/WhiteNoiseLightAccessory.js +59 -0
  102. package/dist/accessory/WhiteNoiseLightAccessory.js.map +1 -0
  103. package/dist/accessory/WindowAccessory.js +14 -0
  104. package/dist/accessory/WindowAccessory.js.map +1 -0
  105. package/dist/accessory/WindowCoveringAccessory.js +156 -0
  106. package/dist/accessory/WindowCoveringAccessory.js.map +1 -0
  107. package/dist/accessory/WirelessSwitchAccessory.js +42 -0
  108. package/dist/accessory/WirelessSwitchAccessory.js.map +1 -0
  109. package/dist/accessory/characteristic/Active.js +23 -0
  110. package/dist/accessory/characteristic/Active.js.map +1 -0
  111. package/dist/accessory/characteristic/AirQuality.js +75 -0
  112. package/dist/accessory/characteristic/AirQuality.js.map +1 -0
  113. package/dist/accessory/characteristic/CurrentRelativeHumidity.js +24 -0
  114. package/dist/accessory/characteristic/CurrentRelativeHumidity.js.map +1 -0
  115. package/dist/accessory/characteristic/CurrentTemperature.js +24 -0
  116. package/dist/accessory/characteristic/CurrentTemperature.js.map +1 -0
  117. package/dist/accessory/characteristic/CurrentWeather.js +51 -0
  118. package/dist/accessory/characteristic/CurrentWeather.js.map +1 -0
  119. package/dist/accessory/characteristic/CurrentWeatherByOpenMeteo.js +81 -0
  120. package/dist/accessory/characteristic/CurrentWeatherByOpenMeteo.js.map +1 -0
  121. package/dist/accessory/characteristic/CurrentWetBulbGlobeTemperature.js +50 -0
  122. package/dist/accessory/characteristic/CurrentWetBulbGlobeTemperature.js.map +1 -0
  123. package/dist/accessory/characteristic/EnergyUsage.js +103 -0
  124. package/dist/accessory/characteristic/EnergyUsage.js.map +1 -0
  125. package/dist/accessory/characteristic/Light.js +269 -0
  126. package/dist/accessory/characteristic/Light.js.map +1 -0
  127. package/dist/accessory/characteristic/LightSensor.js +24 -0
  128. package/dist/accessory/characteristic/LightSensor.js.map +1 -0
  129. package/dist/accessory/characteristic/LockPhysicalControls.js +22 -0
  130. package/dist/accessory/characteristic/LockPhysicalControls.js.map +1 -0
  131. package/dist/accessory/characteristic/MotionDetected.js +23 -0
  132. package/dist/accessory/characteristic/MotionDetected.js.map +1 -0
  133. package/dist/accessory/characteristic/Name.js +12 -0
  134. package/dist/accessory/characteristic/Name.js.map +1 -0
  135. package/dist/accessory/characteristic/OccupancyDetected.js +20 -0
  136. package/dist/accessory/characteristic/OccupancyDetected.js.map +1 -0
  137. package/dist/accessory/characteristic/On.js +26 -0
  138. package/dist/accessory/characteristic/On.js.map +1 -0
  139. package/dist/accessory/characteristic/OutletInUse.js +15 -0
  140. package/dist/accessory/characteristic/OutletInUse.js.map +1 -0
  141. package/dist/accessory/characteristic/ProgrammableSwitchEvent.js +90 -0
  142. package/dist/accessory/characteristic/ProgrammableSwitchEvent.js.map +1 -0
  143. package/dist/accessory/characteristic/RelativeHumidityDehumidifierThreshold.js +29 -0
  144. package/dist/accessory/characteristic/RelativeHumidityDehumidifierThreshold.js.map +1 -0
  145. package/dist/accessory/characteristic/RotationSpeed.js +79 -0
  146. package/dist/accessory/characteristic/RotationSpeed.js.map +1 -0
  147. package/dist/accessory/characteristic/SecuritySystemState.js +76 -0
  148. package/dist/accessory/characteristic/SecuritySystemState.js.map +1 -0
  149. package/dist/accessory/characteristic/SwingMode.js +22 -0
  150. package/dist/accessory/characteristic/SwingMode.js.map +1 -0
  151. package/dist/accessory/characteristic/TemperatureDisplayUnits.js +26 -0
  152. package/dist/accessory/characteristic/TemperatureDisplayUnits.js.map +1 -0
  153. package/dist/config.js +29 -0
  154. package/dist/config.js.map +1 -0
  155. package/dist/core/TuyaOpenAPI.js +314 -0
  156. package/dist/core/TuyaOpenAPI.js.map +1 -0
  157. package/dist/core/TuyaOpenMQ.js +182 -0
  158. package/dist/core/TuyaOpenMQ.js.map +1 -0
  159. package/dist/device/TuyaCustomDeviceManager.js +65 -0
  160. package/dist/device/TuyaCustomDeviceManager.js.map +1 -0
  161. package/dist/device/TuyaDevice.js +37 -0
  162. package/dist/device/TuyaDevice.js.map +1 -0
  163. package/dist/device/TuyaDeviceManager.js +447 -0
  164. package/dist/device/TuyaDeviceManager.js.map +1 -0
  165. package/dist/device/TuyaHomeDeviceManager.js +64 -0
  166. package/dist/device/TuyaHomeDeviceManager.js.map +1 -0
  167. package/dist/index.js +7 -0
  168. package/dist/index.js.map +1 -0
  169. package/dist/platform.js +466 -0
  170. package/dist/platform.js.map +1 -0
  171. package/dist/settings.js +18 -0
  172. package/dist/settings.js.map +1 -0
  173. package/dist/util/FfmpegStreamingProcess.js +126 -0
  174. package/dist/util/FfmpegStreamingProcess.js.map +1 -0
  175. package/dist/util/InfraredTool.js +396 -0
  176. package/dist/util/InfraredTool.js.map +1 -0
  177. package/dist/util/Logger.js +42 -0
  178. package/dist/util/Logger.js.map +1 -0
  179. package/dist/util/TuyaRecordingDelegate.js +22 -0
  180. package/dist/util/TuyaRecordingDelegate.js.map +1 -0
  181. package/dist/util/TuyaStreamDelegate.js +330 -0
  182. package/dist/util/TuyaStreamDelegate.js.map +1 -0
  183. package/dist/util/color.js +24 -0
  184. package/dist/util/color.js.map +1 -0
  185. package/dist/util/util.js +48 -0
  186. package/dist/util/util.js.map +1 -0
  187. package/jest.config.js +5 -0
  188. package/package.json +74 -0
  189. package/test/custom.test.ts +101 -0
  190. package/test/home.test.ts +81 -0
  191. package/test/util.ts +39 -0
@@ -0,0 +1,330 @@
1
+ "use strict";
2
+ /* eslint-disable @typescript-eslint/no-unused-vars */
3
+ /* eslint-disable max-len */
4
+ Object.defineProperty(exports, "__esModule", { value: true });
5
+ exports.TuyaStreamingDelegate = void 0;
6
+ const camera_utils_1 = require("@homebridge/camera-utils");
7
+ const child_process_1 = require("child_process");
8
+ const dgram_1 = require("dgram");
9
+ const FfmpegStreamingProcess_1 = require("./FfmpegStreamingProcess");
10
+ /*
11
+ interface SampleRateEntry {
12
+ type: AudioRecordingCodecType;
13
+ bitrateMode: number;
14
+ samplerate: AudioRecordingSamplerate[];
15
+ audioChannels: number;
16
+ }
17
+ */
18
+ class TuyaStreamingDelegate {
19
+ constructor(camera) {
20
+ this.pendingSessions = {};
21
+ this.ongoingSessions = {};
22
+ this.camera = camera;
23
+ this.hap = camera.platform.api.hap;
24
+ // this.recordingDelegate = new TuyaRecordingDelegate();
25
+ const resolutions = [
26
+ [320, 180, 30],
27
+ [320, 240, 15],
28
+ [320, 240, 30],
29
+ [480, 270, 30],
30
+ [480, 360, 30],
31
+ [640, 360, 30],
32
+ [640, 480, 30],
33
+ [1280, 720, 30],
34
+ [1280, 960, 30],
35
+ [1920, 1080, 30],
36
+ [1600, 1200, 30],
37
+ ];
38
+ const streamingOptions = {
39
+ supportedCryptoSuites: [0 /* SRTPCryptoSuites.AES_CM_128_HMAC_SHA1_80 */],
40
+ video: {
41
+ codec: {
42
+ profiles: [0 /* H264Profile.BASELINE */, 1 /* H264Profile.MAIN */, 2 /* H264Profile.HIGH */],
43
+ levels: [0 /* H264Level.LEVEL3_1 */, 1 /* H264Level.LEVEL3_2 */, 2 /* H264Level.LEVEL4_0 */],
44
+ },
45
+ resolutions: resolutions,
46
+ },
47
+ audio: {
48
+ twoWayAudio: false,
49
+ codecs: [
50
+ {
51
+ type: "AAC-eld" /* AudioStreamingCodecType.AAC_ELD */,
52
+ samplerate: 16 /* AudioStreamingSamplerate.KHZ_16 */,
53
+ },
54
+ ],
55
+ },
56
+ };
57
+ const recordingOptions = {
58
+ overrideEventTriggerOptions: [
59
+ 1 /* EventTriggerOption.MOTION */,
60
+ 2 /* EventTriggerOption.DOORBELL */,
61
+ ],
62
+ prebufferLength: 4 * 1000,
63
+ mediaContainerConfiguration: [
64
+ {
65
+ type: 0 /* MediaContainerType.FRAGMENTED_MP4 */,
66
+ fragmentLength: 4000,
67
+ },
68
+ ],
69
+ video: {
70
+ parameters: {
71
+ profiles: [
72
+ 0 /* H264Profile.BASELINE */,
73
+ 1 /* H264Profile.MAIN */,
74
+ 2 /* H264Profile.HIGH */,
75
+ ],
76
+ levels: [
77
+ 0 /* H264Level.LEVEL3_1 */,
78
+ 1 /* H264Level.LEVEL3_2 */,
79
+ 2 /* H264Level.LEVEL4_0 */,
80
+ ],
81
+ },
82
+ resolutions: resolutions,
83
+ type: 0 /* this.hap.VideoCodecType.H264 */,
84
+ },
85
+ audio: {
86
+ codecs: [
87
+ {
88
+ samplerate: 3 /* this.hap.AudioRecordingSamplerate.KHZ_32 */,
89
+ type: 0 /* this.hap.AudioRecordingCodecType.AAC_LC */,
90
+ },
91
+ ],
92
+ },
93
+ };
94
+ const options = {
95
+ delegate: this,
96
+ streamingOptions: streamingOptions,
97
+ // recording: {
98
+ // options: recordingOptions,
99
+ // delegate: this.recordingDelegate
100
+ // }
101
+ };
102
+ this.controller = new this.hap.CameraController(options);
103
+ }
104
+ stopStream(sessionId) {
105
+ var _a, _b, _c;
106
+ const session = this.ongoingSessions[sessionId];
107
+ if (session) {
108
+ if (session.timeout) {
109
+ clearTimeout(session.timeout);
110
+ }
111
+ try {
112
+ (_a = session.socket) === null || _a === void 0 ? void 0 : _a.close();
113
+ }
114
+ catch (error) {
115
+ this.camera.log.error(`Error occurred closing socket: ${error}`);
116
+ }
117
+ try {
118
+ (_b = session.mainProcess) === null || _b === void 0 ? void 0 : _b.stop();
119
+ }
120
+ catch (error) {
121
+ this.camera.log.error(`Error occurred terminating main FFmpeg process: ${error}`);
122
+ }
123
+ try {
124
+ (_c = session.returnProcess) === null || _c === void 0 ? void 0 : _c.stop();
125
+ }
126
+ catch (error) {
127
+ this.camera.log.error(`Error occurred terminating two-way FFmpeg process: ${error}`);
128
+ }
129
+ delete this.ongoingSessions[sessionId];
130
+ this.camera.log.info('Stopped video stream.');
131
+ }
132
+ }
133
+ forceStopStream(sessionId) {
134
+ this.controller.forceStopStreamingSession(sessionId);
135
+ }
136
+ async handleSnapshotRequest(request, callback) {
137
+ try {
138
+ this.camera.log.debug(`Snapshot requested: ${request.width} x ${request.height}`);
139
+ const snapshot = await this.fetchSnapshot();
140
+ this.camera.log.debug('Sending snapshot');
141
+ callback(undefined, snapshot);
142
+ }
143
+ catch (error) {
144
+ callback(error);
145
+ }
146
+ }
147
+ async prepareStream(request, callback) {
148
+ const videoIncomingPort = await (0, camera_utils_1.reservePorts)({
149
+ count: 1,
150
+ });
151
+ const videoSSRC = this.hap.CameraController.generateSynchronisationSource();
152
+ const audioIncomingPort = await (0, camera_utils_1.reservePorts)({
153
+ count: 1,
154
+ });
155
+ const audioSSRC = this.hap.CameraController.generateSynchronisationSource();
156
+ const sessionInfo = {
157
+ address: request.targetAddress,
158
+ addressVersion: request.addressVersion,
159
+ audioCryptoSuite: request.audio.srtpCryptoSuite,
160
+ audioPort: request.audio.port,
161
+ audioSRTP: Buffer.concat([request.audio.srtp_key, request.audio.srtp_salt]),
162
+ audioSSRC: audioSSRC,
163
+ audioIncomingPort: audioIncomingPort[0],
164
+ videoCryptoSuite: request.video.srtpCryptoSuite,
165
+ videoPort: request.video.port,
166
+ videoSRTP: Buffer.concat([request.video.srtp_key, request.video.srtp_salt]),
167
+ videoSSRC: videoSSRC,
168
+ videoIncomingPort: videoIncomingPort[0],
169
+ };
170
+ const response = {
171
+ video: {
172
+ port: sessionInfo.videoIncomingPort,
173
+ ssrc: videoSSRC,
174
+ srtp_key: request.video.srtp_key,
175
+ srtp_salt: request.video.srtp_salt,
176
+ },
177
+ audio: {
178
+ port: sessionInfo.audioIncomingPort,
179
+ ssrc: audioSSRC,
180
+ srtp_key: request.audio.srtp_key,
181
+ srtp_salt: request.audio.srtp_salt,
182
+ },
183
+ };
184
+ this.pendingSessions[request.sessionID] = sessionInfo;
185
+ callback(undefined, response);
186
+ }
187
+ async handleStreamRequest(request, callback) {
188
+ switch (request.type) {
189
+ case "start" /* this.hap.StreamRequestTypes.START */: {
190
+ this.camera.log.debug(`Start stream requested: ${request.video.width}x${request.video.height}, ${request.video.fps} fps, ${request.video.max_bit_rate} kbps`);
191
+ await this.startStream(request, callback);
192
+ break;
193
+ }
194
+ case "reconfigure" /* this.hap.StreamRequestTypes.RECONFIGURE */: {
195
+ this.camera.log.debug(`Reconfigure stream requested: ${request.video.width}x${request.video.height}, ${request.video.fps} fps, ${request.video.max_bit_rate} kbps (Ignored)`);
196
+ callback();
197
+ break;
198
+ }
199
+ case "stop" /* this.hap.StreamRequestTypes.STOP */: {
200
+ this.camera.log.debug('Stop stream requested');
201
+ this.stopStream(request.sessionID);
202
+ callback();
203
+ break;
204
+ }
205
+ }
206
+ }
207
+ async retrieveDeviceRTSP() {
208
+ const data = await this.camera.deviceManager.api.post(`/v1.0/devices/${this.camera.device.id}/stream/actions/allocate`, {
209
+ type: 'rtsp',
210
+ });
211
+ return data.result.url;
212
+ }
213
+ async startStream(request, callback) {
214
+ const sessionInfo = this.pendingSessions[request.sessionID];
215
+ if (!sessionInfo) {
216
+ this.camera.log.error('Error finding session information.');
217
+ callback(new Error('Error finding session information'));
218
+ }
219
+ const vcodec = 'libx264';
220
+ const mtu = 1316; // request.video.mtu is not used
221
+ const fps = request.video.fps;
222
+ const videoBitrate = request.video.max_bit_rate;
223
+ const rtspUrl = await this.retrieveDeviceRTSP();
224
+ const ffmpegArgs = [
225
+ '-hide_banner',
226
+ '-loglevel', 'verbose',
227
+ '-i', rtspUrl,
228
+ '-an', '-sn', '-dn',
229
+ '-r', fps.toString(),
230
+ '-codec:v', vcodec,
231
+ '-pix_fmt', 'yuv420p',
232
+ '-color_range', 'mpeg',
233
+ '-f', 'rawvideo',
234
+ ];
235
+ const encoderOptions = '-preset ultrafast -tune zerolatency';
236
+ if (encoderOptions) {
237
+ ffmpegArgs.push(...encoderOptions.split(/\s+/));
238
+ }
239
+ if (videoBitrate > 0) {
240
+ ffmpegArgs.push('-b:v', `${videoBitrate}k`);
241
+ }
242
+ // Video Stream
243
+ ffmpegArgs.push('-payload_type', `${request.video.pt}`, '-ssrc', `${sessionInfo.videoSSRC}`, '-f', 'rtp', '-srtp_out_suite', 'AES_CM_128_HMAC_SHA1_80', '-srtp_out_params', sessionInfo.videoSRTP.toString('base64'), `srtp://${sessionInfo.address}:${sessionInfo.videoPort}?rtcpport=${sessionInfo.videoPort}&pkt_size=${mtu}`);
244
+ // Setting up audio
245
+ if (request.audio.codec === "OPUS" /* AudioStreamingCodecType.OPUS */ ||
246
+ request.audio.codec === "AAC-eld" /* AudioStreamingCodecType.AAC_ELD */) {
247
+ ffmpegArgs.push('-vn', '-sn', '-dn');
248
+ if (request.audio.codec === "OPUS" /* AudioStreamingCodecType.OPUS */) {
249
+ ffmpegArgs.push('-acodec', 'libopus', '-application', 'lowdelay');
250
+ }
251
+ else {
252
+ ffmpegArgs.push('-acodec', 'libfdk_aac', '-profile:a', 'aac_eld');
253
+ }
254
+ ffmpegArgs.push('-flags', '+global_header', '-f', 'null', '-ar', `${request.audio.sample_rate}k`, '-b:a', `${request.audio.max_bit_rate}k`, '-ac', `${request.audio.channel}`, '-payload_type', `${request.audio.pt}`, '-ssrc', `${sessionInfo.audioSSRC}`, '-f', 'rtp', '-srtp_out_suite', 'AES_CM_128_HMAC_SHA1_80', '-srtp_out_params', sessionInfo.audioSRTP.toString('base64'), `srtp://${sessionInfo.address}:${sessionInfo.audioPort}?rtcpport=${sessionInfo.audioPort}&pkt_size=188`);
255
+ }
256
+ else {
257
+ this.camera.log.error(`Unsupported audio codec requested: ${request.audio.codec}`);
258
+ }
259
+ ffmpegArgs.push('-progress', 'pipe:1');
260
+ const activeSession = {};
261
+ activeSession.socket = (0, dgram_1.createSocket)(sessionInfo.addressVersion === 'ipv6' ? 'udp6' : 'udp4');
262
+ activeSession.socket.on('error', (err) => {
263
+ this.camera.log.error('Socket error: ' + err.message);
264
+ this.stopStream(request.sessionID);
265
+ });
266
+ activeSession.socket.on('message', () => {
267
+ if (activeSession.timeout) {
268
+ clearTimeout(activeSession.timeout);
269
+ }
270
+ activeSession.timeout = setTimeout(() => {
271
+ this.camera.log.info('Device appears to be inactive. Stopping stream.');
272
+ this.controller.forceStopStreamingSession(request.sessionID);
273
+ this.stopStream(request.sessionID);
274
+ }, request.video.rtcp_interval * 5 * 1000);
275
+ });
276
+ activeSession.socket.bind(sessionInfo.videoIncomingPort);
277
+ activeSession.mainProcess = new FfmpegStreamingProcess_1.FfmpegStreamingProcess(request.sessionID, camera_utils_1.defaultFfmpegPath, ffmpegArgs, this.camera.log, this, callback);
278
+ this.ongoingSessions[request.sessionID] = activeSession;
279
+ delete this.pendingSessions[request.sessionID];
280
+ }
281
+ async fetchSnapshot() {
282
+ if (!this.camera.device.online) {
283
+ this.camera.log.debug('Device is currently offline.');
284
+ throw new Error('Device is currently offline.');
285
+ }
286
+ // TODO: Check if there is a stream already running to fetch snapshot.
287
+ const rtspUrl = await this.retrieveDeviceRTSP();
288
+ const ffmpegArgs = [
289
+ '-i', rtspUrl,
290
+ '-frames:v', '1',
291
+ '-hide_banner',
292
+ '-loglevel',
293
+ 'error',
294
+ '-f',
295
+ 'image2',
296
+ '-',
297
+ ];
298
+ return new Promise((resolve, reject) => {
299
+ this.camera.log.debug(`Running Snapshot command: ${camera_utils_1.defaultFfmpegPath} ${ffmpegArgs.map(value => JSON.stringify(value)).join(' ')}`);
300
+ const ffmpeg = (0, child_process_1.spawn)(camera_utils_1.defaultFfmpegPath, ffmpegArgs.map(x => x.toString()), { env: process.env });
301
+ let errors = [];
302
+ let snapshotBuffer = Buffer.alloc(0);
303
+ ffmpeg.stdout.on('data', (data) => {
304
+ snapshotBuffer = Buffer.concat([snapshotBuffer, data]);
305
+ });
306
+ ffmpeg.on('error', (error) => {
307
+ this.camera.log.error(`FFmpeg process creation failed: ${error.message} - Showing "offline" image instead.`);
308
+ reject('Failed to fetch snapshot.');
309
+ });
310
+ ffmpeg.stderr.on('data', (data) => {
311
+ errors = errors.slice(-5);
312
+ errors.push(data.toString().replace(/(\r\n|\n|\r)/gm, ' '));
313
+ });
314
+ ffmpeg.on('close', () => {
315
+ if (snapshotBuffer.length > 0) {
316
+ resolve(snapshotBuffer);
317
+ }
318
+ else {
319
+ this.camera.log.error('Failed to fetch snapshot. Showing "offline" image instead.');
320
+ if (errors.length > 0) {
321
+ this.camera.log.error(errors.join(' - '));
322
+ }
323
+ reject('Unable to fetch snapshot.');
324
+ }
325
+ });
326
+ });
327
+ }
328
+ }
329
+ exports.TuyaStreamingDelegate = TuyaStreamingDelegate;
330
+ //# sourceMappingURL=TuyaStreamDelegate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TuyaStreamDelegate.js","sourceRoot":"","sources":["../../src/util/TuyaStreamDelegate.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.miredToKelvin = exports.kelvinToMired = exports.kelvinToHSV = void 0;
7
+ const color_convert_1 = __importDefault(require("color-convert"));
8
+ const kelvin_to_rgb_1 = __importDefault(require("kelvin-to-rgb"));
9
+ function kelvinToHSV(kevin) {
10
+ const [r, g, b] = (0, kelvin_to_rgb_1.default)(kevin);
11
+ const [h, s, v] = color_convert_1.default.rgb.hsv(r, g, b);
12
+ return { h, s, v };
13
+ }
14
+ exports.kelvinToHSV = kelvinToHSV;
15
+ // https://en.wikipedia.org/wiki/Mired
16
+ function kelvinToMired(kelvin) {
17
+ return 1e6 / kelvin;
18
+ }
19
+ exports.kelvinToMired = kelvinToMired;
20
+ function miredToKelvin(mired) {
21
+ return 1e6 / mired;
22
+ }
23
+ exports.miredToKelvin = miredToKelvin;
24
+ //# sourceMappingURL=color.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"color.js","sourceRoot":"","sources":["../../src/util/color.ts"],"names":[],"mappings":";;;;;;AAAA,kEAAoC;AACpC,kEAAwC;AAExC,SAAgB,WAAW,CAAC,KAAa;IACvC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,GAAG,IAAA,uBAAW,EAAC,KAAK,CAAC,CAAC;IACrC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,GAAG,uBAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;IAC3C,OAAO,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC;AACrB,CAAC;AAJD,kCAIC;AAED,sCAAsC;AACtC,SAAgB,aAAa,CAAC,MAAc;IAC1C,OAAO,GAAG,GAAG,MAAM,CAAC;AACtB,CAAC;AAFD,sCAEC;AAED,SAAgB,aAAa,CAAC,KAAa;IACzC,OAAO,GAAG,GAAG,KAAK,CAAC;AACrB,CAAC;AAFD,sCAEC"}
@@ -0,0 +1,48 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.toHapProperty = exports.limit = exports.remap = void 0;
4
+ function remap(value, srcStart, srcEnd, dstStart, dstEnd) {
5
+ const percent = (value - srcStart) / (srcEnd - srcStart);
6
+ const result = percent * (dstEnd - dstStart) + dstStart;
7
+ return result;
8
+ }
9
+ exports.remap = remap;
10
+ function limit(value, start, end) {
11
+ let result = value;
12
+ result = Math.min(end, result);
13
+ result = Math.max(start, result);
14
+ return result;
15
+ }
16
+ exports.limit = limit;
17
+ function toHapProperty(property) {
18
+ return Object.entries(property).reduce((hap, [key, value]) => {
19
+ switch (key) {
20
+ case 'min': {
21
+ const multiple = Math.pow(10, property ? property['scale'] : 0);
22
+ hap['minValue'] = Math.max(-273.15, value / multiple);
23
+ break;
24
+ }
25
+ case 'max': {
26
+ const multiple = Math.pow(10, property ? property['scale'] : 0);
27
+ hap['maxValue'] = Math.min(400, value / multiple);
28
+ break;
29
+ }
30
+ case 'step': {
31
+ const multiple = Math.pow(10, property ? property['scale'] : 0);
32
+ hap['minStep'] = Math.max(0.01, value / multiple);
33
+ break;
34
+ }
35
+ case 'range': {
36
+ hap['validValues'] = value;
37
+ break;
38
+ }
39
+ default: {
40
+ hap[key] = value;
41
+ break;
42
+ }
43
+ }
44
+ return hap;
45
+ }, {});
46
+ }
47
+ exports.toHapProperty = toHapProperty;
48
+ //# sourceMappingURL=util.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"util.js","sourceRoot":"","sources":["../../src/util/util.ts"],"names":[],"mappings":";;;AAEA,SAAgB,KAAK,CACnB,KAAa,EACb,QAAgB,EAChB,MAAc,EACd,QAAgB,EAChB,MAAc;IAEd,MAAM,OAAO,GAAG,CAAC,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,MAAM,GAAG,QAAQ,CAAC,CAAC;IACzD,MAAM,MAAM,GAAG,OAAO,GAAG,CAAC,MAAM,GAAG,QAAQ,CAAC,GAAG,QAAQ,CAAC;IACxD,OAAO,MAAM,CAAC;AAChB,CAAC;AAVD,sBAUC;AAED,SAAgB,KAAK,CACnB,KAAa,EACb,KAAa,EACb,GAAW;IAEX,IAAI,MAAM,GAAG,KAAK,CAAC;IACnB,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;IAC/B,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;IACjC,OAAO,MAAM,CAAC;AAChB,CAAC;AATD,sBASC;AAED,SAAgB,aAAa,CAC3B,QAAkC;IAElC,OAAO,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE;QAC3D,QAAQ,GAAG,EAAE;YACX,KAAK,KAAK,CAAC,CAAC;gBACV,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;gBAChE,GAAG,CAAC,UAAU,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,KAAK,GAAG,QAAQ,CAAC,CAAC;gBACtD,MAAM;aACP;YACD,KAAK,KAAK,CAAC,CAAC;gBACV,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;gBAChE,GAAG,CAAC,UAAU,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,GAAG,QAAQ,CAAC,CAAC;gBAClD,MAAM;aACP;YACD,KAAK,MAAM,CAAC,CAAC;gBACX,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;gBAChE,GAAG,CAAC,SAAS,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,GAAG,QAAQ,CAAC,CAAC;gBAClD,MAAM;aACP;YACD,KAAK,OAAO,CAAC,CAAC;gBACZ,GAAG,CAAC,aAAa,CAAC,GAAG,KAAK,CAAC;gBAC3B,MAAM;aACP;YACD,OAAO,CAAC,CAAC;gBACP,GAAG,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;gBACjB,MAAM;aACP;SACF;QACD,OAAO,GAAG,CAAC;IACb,CAAC,EAAE,EAAE,CAAC,CAAC;AACT,CAAC;AA/BD,sCA+BC"}
package/jest.config.js ADDED
@@ -0,0 +1,5 @@
1
+ /** @type {import('ts-jest').JestConfigWithTsJest} */
2
+ module.exports = {
3
+ preset: 'ts-jest',
4
+ testEnvironment: 'node',
5
+ };
package/package.json ADDED
@@ -0,0 +1,74 @@
1
+ {
2
+ "name": "@homebridge-plugins/homebridge-tuya",
3
+ "displayName": "Homebridge Tuya",
4
+ "version": "2.0.2-beta.3",
5
+ "description": "Forked from 0x5e/homebridge-tuya-platform, with a focus on fixing bugs and adding new device support.",
6
+ "license": "MIT",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/homebridge-plugins/homebridge-tuya"
10
+ },
11
+ "homepage": "https://github.com/homebridge-plugins/homebridge-tuya#readme",
12
+ "bugs": {
13
+ "url": "https://github.com/homebridge-plugins/homebridge-tuya/issues"
14
+ },
15
+ "funding": [
16
+ {
17
+ "type": "github",
18
+ "url": "https://github.com/sponsors/tassy-h"
19
+ }
20
+ ],
21
+ "engines": {
22
+ "node": ">=14.18.1",
23
+ "homebridge": ">=1.3.5"
24
+ },
25
+ "main": "dist/index.js",
26
+ "scripts": {
27
+ "lint": "eslint src/**/*.ts --max-warnings=0",
28
+ "test": "jest",
29
+ "watch": "npm run build && npm link && nodemon",
30
+ "launch": "tsc && homebridge -I -D",
31
+ "build": "rimraf ./dist && tsc",
32
+ "prepublishOnly": "npm run lint && npm run build"
33
+ },
34
+ "keywords": [
35
+ "homebridge-plugin",
36
+ "homekit",
37
+ "tuya"
38
+ ],
39
+ "dependencies": {
40
+ "@homebridge/camera-utils": "^2.2.0",
41
+ "async-await-retry": "^2.0.1",
42
+ "color-convert": "^2.0.1",
43
+ "crypto-js": "^4.1.1",
44
+ "debounce": "^1.2.1",
45
+ "jsonschema": "^1.4.1",
46
+ "kelvin-to-rgb": "^1.0.2",
47
+ "lodash.isequal": "^4.5.0",
48
+ "mqtt": "^4.2.6",
49
+ "uuid": "^9.0.0"
50
+ },
51
+ "devDependencies": {
52
+ "@types/color-convert": "^2.0.0",
53
+ "@types/crypto-js": "^4.1.1",
54
+ "@types/debounce": "^1.2.1",
55
+ "@types/jest": "^29.1.2",
56
+ "@types/lodash.isequal": "^4.5.6",
57
+ "@types/node": "^18.11.9",
58
+ "@types/uuid": "^8.3.4",
59
+ "@typescript-eslint/eslint-plugin": "^5.0.0",
60
+ "@typescript-eslint/parser": "^5.0.0",
61
+ "eslint": "8.57.1",
62
+ "homebridge": "^1.3.5",
63
+ "jest": "^29.1.2",
64
+ "nodemon": "^3.1.10",
65
+ "rimraf": "^3.0.2",
66
+ "ts-jest": "^29.0.3",
67
+ "ts-node": "^10.3.0",
68
+ "typescript": "^4.8.4"
69
+ },
70
+ "homebridge": {
71
+ "plugin_type": "platform",
72
+ "platform": "TuyaPlatform"
73
+ }
74
+ }
@@ -0,0 +1,101 @@
1
+ /* eslint-disable no-console */
2
+ import { describe, expect, test } from '@jest/globals';
3
+
4
+ import TuyaOpenAPI from '../src/core/TuyaOpenAPI';
5
+ import TuyaDevice from '../src/device/TuyaDevice';
6
+
7
+ import TuyaCustomDeviceManager from '../src/device/TuyaCustomDeviceManager';
8
+
9
+ import { config, expectDevice, expectSuccessResponse } from './util';
10
+
11
+ const { options } = config;
12
+ if (options.projectType === '1') {
13
+ const api = new TuyaOpenAPI(options.endpoint, options.accessId, options.accessKey);
14
+ const deviceManager = new TuyaCustomDeviceManager(api);
15
+
16
+ describe('TuyaOpenAPI', () => {
17
+ test('getToken()', async () => {
18
+ const res = await api.getToken();
19
+ expectSuccessResponse(res);
20
+ });
21
+
22
+ test('customGetUserInfo()', async () => {
23
+ const res = await api.customGetUserInfo('homebridge');
24
+ expectSuccessResponse(res);
25
+ });
26
+
27
+ test('customCreateUser()', async () => {
28
+ const res = await api.customCreateUser('homebridge', 'homebridge');
29
+ if (res.success === false && res.code === 14520015) {
30
+ // already exist
31
+ } else {
32
+ expectSuccessResponse(res);
33
+ }
34
+ });
35
+
36
+ test('customLogin()', async () => {
37
+ const res = await api.customLogin('homebridge', 'homebridge');
38
+ expectSuccessResponse(res);
39
+ expect(api.isLogin()).toBeTruthy();
40
+ });
41
+
42
+ test('_refreshAccessTokenIfNeed()', async () => {
43
+ api.tokenInfo.expire = 0;
44
+ await api._refreshAccessTokenIfNeed('');
45
+ });
46
+ });
47
+
48
+ describe('TuyaCustomDeviceManager', () => {
49
+
50
+ const assetIDList: string[] = [];
51
+ test('getAssetList()', async () => {
52
+ const res = await deviceManager.getAssetList();
53
+ expectSuccessResponse(res);
54
+ const assets = res.result.list || res.result.assets;
55
+ for (const { asset_id } of assets) {
56
+ assetIDList.push(asset_id);
57
+ }
58
+ });
59
+
60
+ test('updateDevices()', async () => {
61
+ const devices = await deviceManager.updateDevices(assetIDList);
62
+ expect(devices).not.toBeNull();
63
+ for (const device of devices) {
64
+ expectDevice(device);
65
+ }
66
+ }, 30 * 1000);
67
+
68
+ test('updateDevice()', async () => {
69
+ let device: TuyaDevice | null = Array.from(deviceManager.devices)[0];
70
+ expectDevice(device);
71
+ device = await deviceManager.updateDevice(device.id);
72
+ expectDevice(device!);
73
+ });
74
+ });
75
+
76
+ describe('TuyaOpenMQ', () => {
77
+
78
+ test('start()', async () => {
79
+ await new Promise((resolve, reject) => {
80
+ deviceManager.mq._onConnect = () => {
81
+ console.log('TuyaOpenMQ connected');
82
+ resolve(null);
83
+ };
84
+ deviceManager.mq._onError = (err) => {
85
+ console.log('TuyaOpenMQ error:', err);
86
+ reject(err);
87
+ };
88
+ deviceManager.mq.start();
89
+ });
90
+ });
91
+
92
+ test('stop()', async () => {
93
+ deviceManager.mq.stop();
94
+ });
95
+
96
+ });
97
+ } else {
98
+ test('', async () => {
99
+ //
100
+ });
101
+ }