@homebridge-plugins/homebridge-tuya 3.0.0-beta.2 → 3.0.0-beta.21

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 (298) hide show
  1. package/README.md +354 -142
  2. package/config.schema.json +1221 -893
  3. package/dist/cloud/api/TuyaOpenAPI.js +108 -60
  4. package/dist/cloud/api/TuyaOpenAPI.js.map +1 -1
  5. package/dist/cloud/api/TuyaOpenMQ.js +45 -22
  6. package/dist/cloud/api/TuyaOpenMQ.js.map +1 -1
  7. package/dist/cloud/device/{TuyaDeviceManager.js → TuyaCloudDeviceManager.js} +146 -96
  8. package/dist/cloud/device/TuyaCloudDeviceManager.js.map +1 -0
  9. package/dist/cloud/device/TuyaCustomDeviceManager.js +75 -5
  10. package/dist/cloud/device/TuyaCustomDeviceManager.js.map +1 -1
  11. package/dist/cloud/device/TuyaDevice.js.map +1 -1
  12. package/dist/cloud/device/TuyaHomeDeviceManager.js +81 -13
  13. package/dist/cloud/device/TuyaHomeDeviceManager.js.map +1 -1
  14. package/dist/config.js +26 -5
  15. package/dist/config.js.map +1 -1
  16. package/dist/local/DynamicChildDiscovery.js +2 -1
  17. package/dist/local/DynamicChildDiscovery.js.map +1 -1
  18. package/dist/local/LocalDevice.js +16 -3
  19. package/dist/local/LocalDevice.js.map +1 -1
  20. package/dist/local/LocalDeviceManager.js +361 -164
  21. package/dist/local/LocalDeviceManager.js.map +1 -1
  22. package/dist/local/TuyaDiscovery.js +16 -7
  23. package/dist/local/TuyaDiscovery.js.map +1 -1
  24. package/dist/local/ZigbeeGatewayDetection.js +7 -4
  25. package/dist/local/ZigbeeGatewayDetection.js.map +1 -1
  26. package/dist/local/protocol/ProtocolUtilities.js +34 -17
  27. package/dist/local/protocol/ProtocolUtilities.js.map +1 -1
  28. package/dist/local/protocol/ProtocolV31V32.js +4 -2
  29. package/dist/local/protocol/ProtocolV31V32.js.map +1 -1
  30. package/dist/local/protocol/ProtocolV33.js +4 -2
  31. package/dist/local/protocol/ProtocolV33.js.map +1 -1
  32. package/dist/local/protocol/ProtocolV34.js +10 -5
  33. package/dist/local/protocol/ProtocolV34.js.map +1 -1
  34. package/dist/local/protocol/ProtocolV35.js +8 -4
  35. package/dist/local/protocol/ProtocolV35.js.map +1 -1
  36. package/dist/platform.js +253 -493
  37. package/dist/platform.js.map +1 -1
  38. package/dist/settings.js +0 -2
  39. package/dist/settings.js.map +1 -1
  40. package/dist/shared/TuyaDeviceManager.js +147 -0
  41. package/dist/shared/TuyaDeviceManager.js.map +1 -0
  42. package/dist/shared/TuyaHybridDeviceManager.js +215 -0
  43. package/dist/shared/TuyaHybridDeviceManager.js.map +1 -0
  44. package/dist/shared/accessory/AccessoryFactory.js +350 -0
  45. package/dist/shared/accessory/AccessoryFactory.js.map +1 -0
  46. package/dist/shared/{accessories → accessory}/AirConditionerAccessory.js +21 -15
  47. package/dist/shared/accessory/AirConditionerAccessory.js.map +1 -0
  48. package/dist/shared/{accessories → accessory}/AirPurifierAccessory.js.map +1 -1
  49. package/dist/shared/accessory/AirQualitySensorAccessory.js.map +1 -0
  50. package/dist/shared/{accessories → accessory}/BaseAccessory.js +16 -95
  51. package/dist/shared/accessory/BaseAccessory.js.map +1 -0
  52. package/dist/shared/{accessories → accessory}/BlindsAccessory.js.map +1 -1
  53. package/dist/shared/{accessories → accessory}/CameraAccessory.js +2 -2
  54. package/dist/shared/accessory/CameraAccessory.js.map +1 -0
  55. package/dist/shared/{accessories → accessory}/CarbonDioxideSensorAccessory.js.map +1 -1
  56. package/dist/shared/{accessories → accessory}/CarbonMonoxideSensorAccessory.js.map +1 -1
  57. package/dist/shared/{accessories → accessory}/ContactSensorAccessory.js.map +1 -1
  58. package/dist/shared/{accessories → accessory}/DehumidifierAccessory.js.map +1 -1
  59. package/dist/shared/{accessories → accessory}/DiffuserAccessory.js.map +1 -1
  60. package/dist/shared/{accessories → accessory}/DimmerAccessory.js.map +1 -1
  61. package/dist/shared/{accessories → accessory}/DoorbellAccessory.js.map +1 -1
  62. package/dist/shared/{accessories → accessory}/ExtractionHoodAccessory.js.map +1 -1
  63. package/dist/shared/{accessories → accessory}/FanAccessory.js.map +1 -1
  64. package/dist/shared/{accessories → accessory}/GarageDoorAccessory.js +19 -2
  65. package/dist/shared/accessory/GarageDoorAccessory.js.map +1 -0
  66. package/dist/shared/{accessories → accessory}/HeaterAccessory.js.map +1 -1
  67. package/dist/shared/{accessories → accessory}/HeaterAccessory_old.js.map +1 -1
  68. package/dist/shared/accessory/HumanPresenceSensorAccessory.js.map +1 -0
  69. package/dist/shared/{accessories → accessory}/HumidifierAccessory.js.map +1 -1
  70. package/dist/shared/accessory/IRAdapter.js +167 -0
  71. package/dist/shared/accessory/IRAdapter.js.map +1 -0
  72. package/dist/shared/accessory/IRAirConditionerAccessory.js.map +1 -0
  73. package/dist/shared/accessory/IRControlHubAccessory.js.map +1 -0
  74. package/dist/shared/accessory/IRControlHubSubAccessory.js.map +1 -0
  75. package/dist/shared/accessory/IRFanAccessory.js +90 -0
  76. package/dist/shared/accessory/IRFanAccessory.js.map +1 -0
  77. package/dist/shared/accessory/IRGenericAccessory.js.map +1 -0
  78. package/dist/shared/{accessories → accessory}/LeakSensorAccessory.js.map +1 -1
  79. package/dist/shared/{accessories → accessory}/LightAccessory.js.map +1 -1
  80. package/dist/shared/{accessories → accessory}/LightSensorAccessory.js.map +1 -1
  81. package/dist/shared/{accessories → accessory}/LocationWeatherAccessory.js +1 -1
  82. package/dist/shared/accessory/LocationWeatherAccessory.js.map +1 -0
  83. package/dist/shared/{accessories → accessory}/LockAccessory.js +3 -3
  84. package/dist/shared/{accessories → accessory}/LockAccessory.js.map +1 -1
  85. package/dist/shared/accessory/MotionSensorAccessory.js.map +1 -0
  86. package/dist/shared/accessory/OutletAccessory.js.map +1 -0
  87. package/dist/shared/{accessories → accessory}/PetFeederAccessory.js.map +1 -1
  88. package/dist/shared/{accessories → accessory}/SceneAccessory.js +1 -1
  89. package/dist/shared/accessory/SceneAccessory.js.map +1 -0
  90. package/dist/shared/accessory/SceneSwitchAccessory.js.map +1 -0
  91. package/dist/shared/accessory/SecuritySystemAccessory.js.map +1 -0
  92. package/dist/shared/{accessories → accessory}/SmokeSensorAccessory.js.map +1 -1
  93. package/dist/shared/{accessories → accessory}/SwitchAccessory.js +1 -45
  94. package/dist/shared/accessory/SwitchAccessory.js.map +1 -0
  95. package/dist/shared/accessory/TemperatureHumiditySensorAccessory.js.map +1 -0
  96. package/dist/shared/{accessories → accessory}/ThermostatAccessory.js.map +1 -1
  97. package/dist/shared/{accessories → accessory}/TowerRackAccessory.js.map +1 -1
  98. package/dist/shared/{accessories → accessory}/ValveAccessory.js.map +1 -1
  99. package/dist/shared/{accessories → accessory}/VibrationSensorAccessory.js.map +1 -1
  100. package/dist/shared/accessory/WeatherStationAccessory.js.map +1 -0
  101. package/dist/shared/accessory/WetBulbGlobeTemperatureAccessory.js.map +1 -0
  102. package/dist/shared/{accessories → accessory}/WhiteNoiseLightAccessory.js +1 -0
  103. package/dist/shared/accessory/WhiteNoiseLightAccessory.js.map +1 -0
  104. package/dist/shared/accessory/WindowAccessory.js.map +1 -0
  105. package/dist/shared/{accessories → accessory}/WindowCoveringAccessory.js.map +1 -1
  106. package/dist/shared/{accessories → accessory}/WirelessSwitchAccessory.js.map +1 -1
  107. package/dist/shared/accessory/characteristic/Active.js.map +1 -0
  108. package/dist/shared/accessory/characteristic/AirQuality.js.map +1 -0
  109. package/dist/shared/{accessories → accessory}/characteristic/CurrentRelativeHumidity.js +3 -0
  110. package/dist/shared/accessory/characteristic/CurrentRelativeHumidity.js.map +1 -0
  111. package/dist/shared/accessory/characteristic/CurrentTemperature.js.map +1 -0
  112. package/dist/shared/accessory/characteristic/CurrentWeather.js.map +1 -0
  113. package/dist/shared/accessory/characteristic/CurrentWeatherByOpenMeteo.js.map +1 -0
  114. package/dist/shared/{accessories → accessory}/characteristic/CurrentWetBulbGlobeTemperature.js +3 -2
  115. package/dist/shared/accessory/characteristic/CurrentWetBulbGlobeTemperature.js.map +1 -0
  116. package/dist/shared/{accessories → accessory}/characteristic/EnergyUsage.js +0 -5
  117. package/dist/shared/accessory/characteristic/EnergyUsage.js.map +1 -0
  118. package/dist/shared/{accessories → accessory}/characteristic/Light.js +28 -5
  119. package/dist/shared/accessory/characteristic/Light.js.map +1 -0
  120. package/dist/shared/accessory/characteristic/LightSensor.js.map +1 -0
  121. package/dist/shared/{accessories → accessory}/characteristic/LockPhysicalControls.js +3 -0
  122. package/dist/shared/accessory/characteristic/LockPhysicalControls.js.map +1 -0
  123. package/dist/shared/accessory/characteristic/MotionDetected.js.map +1 -0
  124. package/dist/shared/accessory/characteristic/Name.js.map +1 -0
  125. package/dist/shared/accessory/characteristic/OccupancyDetected.js.map +1 -0
  126. package/dist/shared/{accessories → accessory}/characteristic/On.js +1 -0
  127. package/dist/shared/accessory/characteristic/On.js.map +1 -0
  128. package/dist/shared/accessory/characteristic/OutletInUse.js.map +1 -0
  129. package/dist/shared/accessory/characteristic/ProgrammableSwitchEvent.js.map +1 -0
  130. package/dist/shared/{accessories → accessory}/characteristic/RelativeHumidityDehumidifierThreshold.js +3 -0
  131. package/dist/shared/accessory/characteristic/RelativeHumidityDehumidifierThreshold.js.map +1 -0
  132. package/dist/shared/accessory/characteristic/RotationSpeed.js.map +1 -0
  133. package/dist/shared/accessory/characteristic/SecuritySystemState.js.map +1 -0
  134. package/dist/shared/{accessories → accessory}/characteristic/SwingMode.js +3 -0
  135. package/dist/shared/accessory/characteristic/SwingMode.js.map +1 -0
  136. package/dist/shared/accessory/characteristic/TargetTemperature.js.map +1 -0
  137. package/dist/shared/accessory/characteristic/TemperatureDisplayUnits.js.map +1 -0
  138. package/dist/shared/util/ConfigHash.js +2 -2
  139. package/dist/shared/util/ConfigHash.js.map +1 -1
  140. package/dist/shared/util/ConfigMigrator.js +222 -0
  141. package/dist/shared/util/ConfigMigrator.js.map +1 -0
  142. package/dist/shared/util/FfmpegStreamingProcess.js +62 -0
  143. package/dist/shared/util/FfmpegStreamingProcess.js.map +1 -1
  144. package/dist/shared/util/InfraredTool.js +0 -2
  145. package/dist/shared/util/InfraredTool.js.map +1 -1
  146. package/dist/shared/util/Logger.js +85 -10
  147. package/dist/shared/util/Logger.js.map +1 -1
  148. package/dist/shared/util/TuyaStreamDelegate.js +194 -97
  149. package/dist/shared/util/TuyaStreamDelegate.js.map +1 -1
  150. package/dist/shared/util/util.js +59 -4
  151. package/dist/shared/util/util.js.map +1 -1
  152. package/docs/ADVANCED_OPTIONS.md +603 -0
  153. package/docs/CONFIG_GUIDE.md +616 -0
  154. package/docs/Get-Local-Keys-for-Your-Devices.md +246 -0
  155. package/{SUPPORTED_DEVICES.md → docs/SUPPORTED_DEVICES.md} +24 -3
  156. package/eslint.config.mjs +0 -1
  157. package/{jest.config.js → jest.config.ts} +5 -2
  158. package/package.json +12 -9
  159. package/test/ConfigMigrator.test.ts +1060 -0
  160. package/test/Logger.test.ts +482 -0
  161. package/test/accessoryFactory.data.test.ts +172 -0
  162. package/test/accessoryFactory.test.ts +64 -35
  163. package/test/airConditioner.test.ts +742 -0
  164. package/test/config.test.ts +17 -13
  165. package/test/custom.test.ts +2 -2
  166. package/test/errorHandling.test.ts +12 -12
  167. package/test/home.test.ts +1 -1
  168. package/test/hybridDeviceManager.test.ts +808 -0
  169. package/test/light.test.ts +250 -0
  170. package/test/localDevice.test.ts +21 -50
  171. package/test/localDeviceManager.behavior.test.ts +196 -31
  172. package/test/localDeviceManager.test.ts +124 -41
  173. package/test/mqttMessages.test.ts +33 -14
  174. package/test/platform.test.ts +337 -814
  175. package/test/platform.validation.local.test.ts +58 -119
  176. package/test/tuyaCustomDeviceManager.test.ts +106 -16
  177. package/test/tuyaDeviceManager.test.ts +636 -639
  178. package/test/tuyaDiscovery.test.ts +21 -33
  179. package/test/tuyaHomeDeviceManager.test.ts +109 -18
  180. package/test/tuyaOpenAPI.test.ts +25 -44
  181. package/test/tuyaOpenMQ.test.ts +15 -23
  182. package/test/util.test.ts +107 -7
  183. package/test/zigbee/LocalDevice.zigbee.test.ts +14 -18
  184. package/test/zigbee/LocalDeviceManager.cloud-discovery.test.ts +19 -20
  185. package/test/zigbee/LocalDeviceManager.zigbee.reconnect.test.ts +19 -20
  186. package/test/zigbee/LocalDeviceManager.zigbee.test.ts +73 -35
  187. package/test/zigbee/ZigbeeGatewayDetection.test.ts +2 -2
  188. package/ADVANCED_OPTIONS.md +0 -267
  189. package/dist/cloud/device/TuyaDeviceManager.js.map +0 -1
  190. package/dist/local/CloudLocalReconciliation.js +0 -180
  191. package/dist/local/CloudLocalReconciliation.js.map +0 -1
  192. package/dist/shared/AccessoryFactory.js +0 -276
  193. package/dist/shared/AccessoryFactory.js.map +0 -1
  194. package/dist/shared/accessories/AccessoryFactory.js +0 -305
  195. package/dist/shared/accessories/AccessoryFactory.js.map +0 -1
  196. package/dist/shared/accessories/AirConditionerAccessory.js.map +0 -1
  197. package/dist/shared/accessories/AirQualitySensorAccessory.js.map +0 -1
  198. package/dist/shared/accessories/BaseAccessory.js.map +0 -1
  199. package/dist/shared/accessories/CameraAccessory.js.map +0 -1
  200. package/dist/shared/accessories/GarageDoorAccessory.js.map +0 -1
  201. package/dist/shared/accessories/HumanPresenceSensorAccessory.js.map +0 -1
  202. package/dist/shared/accessories/IRAirConditionerAccessory.js.map +0 -1
  203. package/dist/shared/accessories/IRControlHubAccessory.js.map +0 -1
  204. package/dist/shared/accessories/IRControlHubSubAccessory.js.map +0 -1
  205. package/dist/shared/accessories/IRGenericAccessory.js.map +0 -1
  206. package/dist/shared/accessories/LocationWeatherAccessory.js.map +0 -1
  207. package/dist/shared/accessories/MotionSensorAccessory.js.map +0 -1
  208. package/dist/shared/accessories/OutletAccessory.js.map +0 -1
  209. package/dist/shared/accessories/SceneAccessory.js.map +0 -1
  210. package/dist/shared/accessories/SceneSwitchAccessory.js.map +0 -1
  211. package/dist/shared/accessories/SecuritySystemAccessory.js.map +0 -1
  212. package/dist/shared/accessories/SwitchAccessory.js.map +0 -1
  213. package/dist/shared/accessories/TemperatureHumiditySensorAccessory.js.map +0 -1
  214. package/dist/shared/accessories/WeatherStationAccessory.js.map +0 -1
  215. package/dist/shared/accessories/WetBulbGlobeTemperatureAccessory.js.map +0 -1
  216. package/dist/shared/accessories/WhiteNoiseLightAccessory.js.map +0 -1
  217. package/dist/shared/accessories/WindowAccessory.js.map +0 -1
  218. package/dist/shared/accessories/characteristic/Active.js.map +0 -1
  219. package/dist/shared/accessories/characteristic/AirQuality.js.map +0 -1
  220. package/dist/shared/accessories/characteristic/CurrentRelativeHumidity.js.map +0 -1
  221. package/dist/shared/accessories/characteristic/CurrentTemperature.js.map +0 -1
  222. package/dist/shared/accessories/characteristic/CurrentWeather.js.map +0 -1
  223. package/dist/shared/accessories/characteristic/CurrentWeatherByOpenMeteo.js.map +0 -1
  224. package/dist/shared/accessories/characteristic/CurrentWetBulbGlobeTemperature.js.map +0 -1
  225. package/dist/shared/accessories/characteristic/EnergyUsage.js.map +0 -1
  226. package/dist/shared/accessories/characteristic/Light.js.map +0 -1
  227. package/dist/shared/accessories/characteristic/LightSensor.js.map +0 -1
  228. package/dist/shared/accessories/characteristic/LockPhysicalControls.js.map +0 -1
  229. package/dist/shared/accessories/characteristic/MotionDetected.js.map +0 -1
  230. package/dist/shared/accessories/characteristic/Name.js.map +0 -1
  231. package/dist/shared/accessories/characteristic/OccupancyDetected.js.map +0 -1
  232. package/dist/shared/accessories/characteristic/On.js.map +0 -1
  233. package/dist/shared/accessories/characteristic/OutletInUse.js.map +0 -1
  234. package/dist/shared/accessories/characteristic/ProgrammableSwitchEvent.js.map +0 -1
  235. package/dist/shared/accessories/characteristic/RelativeHumidityDehumidifierThreshold.js.map +0 -1
  236. package/dist/shared/accessories/characteristic/RotationSpeed.js.map +0 -1
  237. package/dist/shared/accessories/characteristic/SecuritySystemState.js.map +0 -1
  238. package/dist/shared/accessories/characteristic/SwingMode.js.map +0 -1
  239. package/dist/shared/accessories/characteristic/TargetTemperature.js.map +0 -1
  240. package/dist/shared/accessories/characteristic/TemperatureDisplayUnits.js.map +0 -1
  241. package/test/zigbee/CloudLocalReconciliation.test.ts +0 -284
  242. package/tuya-local-configs/dev-1.hash +0 -1
  243. package/tuya-local-configs/device1.hash +0 -1
  244. package/wiki/Get-Local-Keys-for-Your-Devices.md +0 -85
  245. /package/dist/shared/{accessories → accessory}/AirPurifierAccessory.js +0 -0
  246. /package/dist/shared/{accessories → accessory}/AirQualitySensorAccessory.js +0 -0
  247. /package/dist/shared/{accessories → accessory}/BlindsAccessory.js +0 -0
  248. /package/dist/shared/{accessories → accessory}/CarbonDioxideSensorAccessory.js +0 -0
  249. /package/dist/shared/{accessories → accessory}/CarbonMonoxideSensorAccessory.js +0 -0
  250. /package/dist/shared/{accessories → accessory}/ContactSensorAccessory.js +0 -0
  251. /package/dist/shared/{accessories → accessory}/DehumidifierAccessory.js +0 -0
  252. /package/dist/shared/{accessories → accessory}/DiffuserAccessory.js +0 -0
  253. /package/dist/shared/{accessories → accessory}/DimmerAccessory.js +0 -0
  254. /package/dist/shared/{accessories → accessory}/DoorbellAccessory.js +0 -0
  255. /package/dist/shared/{accessories → accessory}/ExtractionHoodAccessory.js +0 -0
  256. /package/dist/shared/{accessories → accessory}/FanAccessory.js +0 -0
  257. /package/dist/shared/{accessories → accessory}/HeaterAccessory.js +0 -0
  258. /package/dist/shared/{accessories → accessory}/HeaterAccessory_old.js +0 -0
  259. /package/dist/shared/{accessories → accessory}/HumanPresenceSensorAccessory.js +0 -0
  260. /package/dist/shared/{accessories → accessory}/HumidifierAccessory.js +0 -0
  261. /package/dist/shared/{accessories → accessory}/IRAirConditionerAccessory.js +0 -0
  262. /package/dist/shared/{accessories → accessory}/IRControlHubAccessory.js +0 -0
  263. /package/dist/shared/{accessories → accessory}/IRControlHubSubAccessory.js +0 -0
  264. /package/dist/shared/{accessories → accessory}/IRGenericAccessory.js +0 -0
  265. /package/dist/shared/{accessories → accessory}/LeakSensorAccessory.js +0 -0
  266. /package/dist/shared/{accessories → accessory}/LightAccessory.js +0 -0
  267. /package/dist/shared/{accessories → accessory}/LightSensorAccessory.js +0 -0
  268. /package/dist/shared/{accessories → accessory}/MotionSensorAccessory.js +0 -0
  269. /package/dist/shared/{accessories → accessory}/OutletAccessory.js +0 -0
  270. /package/dist/shared/{accessories → accessory}/PetFeederAccessory.js +0 -0
  271. /package/dist/shared/{accessories → accessory}/SceneSwitchAccessory.js +0 -0
  272. /package/dist/shared/{accessories → accessory}/SecuritySystemAccessory.js +0 -0
  273. /package/dist/shared/{accessories → accessory}/SmokeSensorAccessory.js +0 -0
  274. /package/dist/shared/{accessories → accessory}/TemperatureHumiditySensorAccessory.js +0 -0
  275. /package/dist/shared/{accessories → accessory}/ThermostatAccessory.js +0 -0
  276. /package/dist/shared/{accessories → accessory}/TowerRackAccessory.js +0 -0
  277. /package/dist/shared/{accessories → accessory}/ValveAccessory.js +0 -0
  278. /package/dist/shared/{accessories → accessory}/VibrationSensorAccessory.js +0 -0
  279. /package/dist/shared/{accessories → accessory}/WeatherStationAccessory.js +0 -0
  280. /package/dist/shared/{accessories → accessory}/WetBulbGlobeTemperatureAccessory.js +0 -0
  281. /package/dist/shared/{accessories → accessory}/WindowAccessory.js +0 -0
  282. /package/dist/shared/{accessories → accessory}/WindowCoveringAccessory.js +0 -0
  283. /package/dist/shared/{accessories → accessory}/WirelessSwitchAccessory.js +0 -0
  284. /package/dist/shared/{accessories → accessory}/characteristic/Active.js +0 -0
  285. /package/dist/shared/{accessories → accessory}/characteristic/AirQuality.js +0 -0
  286. /package/dist/shared/{accessories → accessory}/characteristic/CurrentTemperature.js +0 -0
  287. /package/dist/shared/{accessories → accessory}/characteristic/CurrentWeather.js +0 -0
  288. /package/dist/shared/{accessories → accessory}/characteristic/CurrentWeatherByOpenMeteo.js +0 -0
  289. /package/dist/shared/{accessories → accessory}/characteristic/LightSensor.js +0 -0
  290. /package/dist/shared/{accessories → accessory}/characteristic/MotionDetected.js +0 -0
  291. /package/dist/shared/{accessories → accessory}/characteristic/Name.js +0 -0
  292. /package/dist/shared/{accessories → accessory}/characteristic/OccupancyDetected.js +0 -0
  293. /package/dist/shared/{accessories → accessory}/characteristic/OutletInUse.js +0 -0
  294. /package/dist/shared/{accessories → accessory}/characteristic/ProgrammableSwitchEvent.js +0 -0
  295. /package/dist/shared/{accessories → accessory}/characteristic/RotationSpeed.js +0 -0
  296. /package/dist/shared/{accessories → accessory}/characteristic/SecuritySystemState.js +0 -0
  297. /package/dist/shared/{accessories → accessory}/characteristic/TargetTemperature.js +0 -0
  298. /package/dist/shared/{accessories → accessory}/characteristic/TemperatureDisplayUnits.js +0 -0
@@ -1,27 +1,21 @@
1
1
  /* eslint-disable no-console */
2
2
  import { describe, expect, test, beforeEach, jest } from '@jest/globals';
3
3
  import EventEmitter from 'events';
4
- import TuyaDeviceManager from '../src/cloud/device/TuyaDeviceManager';
4
+ import TuyaDeviceManager from '../src/cloud/device/TuyaCloudDeviceManager';
5
5
  import TuyaDevice from '../src/cloud/device/TuyaDevice';
6
+ import TuyaHomeDeviceManager from '../src/cloud/device/TuyaHomeDeviceManager';
7
+ import { TuyaPlatformHomeConfig } from '../src/config';
8
+ import { ExLogger, initLogger } from '../src/shared/util/Logger';
6
9
 
7
10
  // Mock Logger
8
- jest.mock('../src/shared/util/Logger', () => ({
9
- __esModule: true,
10
- default: class Logger {
11
- log() {}
12
- info() {}
13
- warn() {}
14
- error() {}
15
- debug() {}
16
- },
17
- PrefixLogger: class PrefixLogger {
18
- constructor(public log: any, public name: string, public debugEnabled: boolean) {}
19
- info() {}
20
- warn() {}
21
- error() {}
22
- debug() {}
23
- },
24
- }));
11
+ const mockLog: ExLogger = {
12
+ debug: jest.fn(),
13
+ info: jest.fn(),
14
+ warn: jest.fn(),
15
+ error: jest.fn(),
16
+ log: jest.fn(),
17
+ success: jest.fn(),
18
+ } as unknown as ExLogger;
25
19
 
26
20
  // Mock TuyaOpenMQ
27
21
  jest.mock('../src/cloud/api/TuyaOpenMQ', () => {
@@ -41,773 +35,776 @@ jest.mock('../src/cloud/api/TuyaOpenAPI', () => {
41
35
  };
42
36
  });
43
37
 
44
- describe('TuyaDeviceManager', () => {
45
- let manager: TuyaDeviceManager;
46
- let mockAPI: any;
47
-
48
- beforeEach(() => {
49
- const getMock = jest.fn() as any;
50
- getMock.mockImplementation((url: string) => {
51
- if (url.includes('specifications')) {
38
+ describe('TuyaDeviceManager', () =>
39
+ {
40
+ let manager: TuyaDeviceManager;
41
+ let mockAPI: any;
42
+ let mockConfig: TuyaPlatformHomeConfig;
43
+
44
+ beforeEach(() => {
45
+ initLogger(mockLog);
46
+ const getMock = jest.fn() as any;
47
+ getMock.mockImplementation((url: string) => {
48
+ if (url.includes('specifications')) {
49
+ return Promise.resolve({
50
+ success: true,
51
+ result: {
52
+ status: [],
53
+ functions: [],
54
+ },
55
+ });
56
+ }
52
57
  return Promise.resolve({
53
58
  success: true,
54
59
  result: {
60
+ id: 'device_001',
61
+ name: 'Test Device',
62
+ product_id: 'prod_001',
63
+ category: 'dj',
55
64
  status: [],
56
- functions: [],
65
+ schema: [],
57
66
  },
58
67
  });
59
- }
60
- return Promise.resolve({
61
- success: true,
62
- result: {
63
- id: 'device_001',
64
- name: 'Test Device',
65
- product_id: 'prod_001',
66
- category: 'dj',
67
- status: [],
68
- schema: [],
69
- },
70
68
  });
71
- });
72
-
73
- mockAPI = {
74
- log: { log: console.log } as any,
75
- accessId: 'test_access_id',
76
- accessKey: 'test_access_key',
77
- get: getMock,
78
- };
79
-
80
- manager = new TuyaDeviceManager(mockAPI, false);
81
- });
82
-
83
- describe('initialization', () => {
84
- test('creates device manager instance', () => {
85
- expect(manager).toBeInstanceOf(EventEmitter);
86
- expect(manager).toBeDefined();
87
- });
69
+
70
+ mockAPI = {
71
+ log: { log: console.log } as any,
72
+ accessId: 'test_access_id',
73
+ accessKey: 'test_access_key',
74
+ get: getMock,
75
+ };
88
76
 
89
- test('stores API reference', () => {
90
- expect(manager.api).toBe(mockAPI);
91
- });
92
-
93
- test('initializes with debug disabled', () => {
94
- expect(manager.debug).toBe(false);
95
- });
77
+ mockConfig = {
78
+ projectType: '2',
79
+ accessId: 'test_id',
80
+ accessKey: 'test_key',
81
+ countryCode: 1,
82
+ username: 'user@example.com',
83
+ password: 'password',
84
+ appSchema: 'tuyaSmart',
85
+ generateWeatherAccessory: false,
86
+ weatherAPI: '',
87
+ forceIPv4: false,
88
+ } as TuyaPlatformHomeConfig;
96
89
 
97
- test('initializes with debug enabled', () => {
98
- const debugManager = new TuyaDeviceManager(mockAPI, true);
99
- expect(debugManager.debug).toBe(true);
90
+ manager = new TuyaHomeDeviceManager(mockAPI, mockConfig, false);
100
91
  });
101
92
 
102
- test('initializes MQTT connection', () => {
103
- expect(manager.mq).toBeDefined();
104
- });
93
+ describe('initialization', () => {
94
+ test('creates device manager instance', () => {
95
+ expect(manager).toBeInstanceOf(EventEmitter);
96
+ expect(manager).toBeDefined();
97
+ });
105
98
 
106
- test('creates logger', () => {
107
- expect(manager.log).toBeDefined();
108
- });
99
+ test('stores API reference', () => {
100
+ expect(manager.api).toBe(mockAPI);
101
+ });
109
102
 
110
- test('initializes empty owner IDs array', () => {
111
- expect(manager.ownerIDs).toEqual([]);
112
- expect(Array.isArray(manager.ownerIDs)).toBe(true);
113
- });
103
+ test('initializes with debug disabled', () => {
104
+ expect(manager.debug).toBe(false);
105
+ });
114
106
 
115
- test('initializes empty devices array', () => {
116
- expect(manager.devices).toEqual([]);
117
- expect(Array.isArray(manager.devices)).toBe(true);
118
- });
119
- });
107
+ test('initializes with debug enabled', () => {
108
+ const debugManager = new TuyaHomeDeviceManager(mockAPI, mockConfig, true);
109
+ expect(debugManager.debug).toBe(true);
110
+ });
120
111
 
121
- describe('static events', () => {
122
- test('has DEVICE_ADD event', () => {
123
- expect(TuyaDeviceManager.Events.DEVICE_ADD).toBe('DEVICE_ADD');
124
- });
112
+ test('initializes MQTT connection', () => {
113
+ expect(manager.mq).toBeDefined();
114
+ });
125
115
 
126
- test('has DEVICE_INFO_UPDATE event', () => {
127
- expect(TuyaDeviceManager.Events.DEVICE_INFO_UPDATE).toBe('DEVICE_INFO_UPDATE');
128
- });
116
+ test('creates logger', () => {
117
+ expect(manager['log']).toBeDefined();
118
+ expect((manager['log'] as any).prefix).toEqual('TuyaHomeDeviceManager');
119
+ });
129
120
 
130
- test('has DEVICE_STATUS_UPDATE event', () => {
131
- expect(TuyaDeviceManager.Events.DEVICE_STATUS_UPDATE).toBe('DEVICE_STATUS_UPDATE');
132
- });
121
+ test('initializes empty owner IDs array', () => {
122
+ expect(manager.ownerIDs).toEqual([]);
123
+ expect(Array.isArray(manager.ownerIDs)).toBe(true);
124
+ });
133
125
 
134
- test('has DEVICE_DELETE event', () => {
135
- expect(TuyaDeviceManager.Events.DEVICE_DELETE).toBe('DEVICE_DELETE');
126
+ test('initializes empty devices array', () => {
127
+ expect(manager.devices).toEqual([]);
128
+ expect(Array.isArray(manager.devices)).toBe(true);
129
+ });
136
130
  });
137
- });
138
131
 
139
- describe('virtual device creation', () => {
140
- test('creates virtual device from base device', () => {
141
- const baseDevice = new TuyaDevice({
142
- id: 'base_device_001',
143
- product_id: 'product_001',
144
- category: 'dj',
145
- name: 'Base Light',
146
- schema: [],
147
- status: [],
132
+ describe('static events', () => {
133
+ test('has DEVICE_ADD event', () => {
134
+ expect(TuyaDeviceManager.Events.DEVICE_ADD).toBe('DEVICE_ADD');
148
135
  });
149
136
 
150
- const virtualDevice = manager.createVirtualDevice(baseDevice, 'virtual_uuid');
137
+ test('has DEVICE_INFO_UPDATE event', () => {
138
+ expect(TuyaDeviceManager.Events.DEVICE_INFO_UPDATE).toBe('DEVICE_INFO_UPDATE');
139
+ });
151
140
 
152
- expect(virtualDevice).toBeDefined();
153
- expect(virtualDevice).toBeInstanceOf(TuyaDevice);
154
- expect(virtualDevice.sub).toBe(true);
155
- expect(virtualDevice.parent_id).toBe('base_device_001');
156
- });
141
+ test('has DEVICE_STATUS_UPDATE event', () => {
142
+ expect(TuyaDeviceManager.Events.DEVICE_STATUS_UPDATE).toBe('DEVICE_STATUS_UPDATE');
143
+ });
157
144
 
158
- test('sets virtual device properties', () => {
159
- const baseDevice = new TuyaDevice({
160
- id: 'base_001',
161
- product_id: 'prod_001',
162
- category: 'dj',
163
- name: 'Base',
164
- schema: [],
165
- status: [],
145
+ test('has DEVICE_DELETE event', () => {
146
+ expect(TuyaDeviceManager.Events.DEVICE_DELETE).toBe('DEVICE_DELETE');
166
147
  });
148
+ });
167
149
 
168
- const virtualDevice = manager.createVirtualDevice(baseDevice, 'virt_uuid_123');
150
+ describe('virtual device creation', () => {
151
+ test('creates virtual device from base device', () => {
152
+ const baseDevice = new TuyaDevice({
153
+ id: 'base_device_001',
154
+ product_id: 'product_001',
155
+ category: 'dj',
156
+ name: 'Base Light',
157
+ schema: [],
158
+ status: [],
159
+ });
169
160
 
170
- expect(virtualDevice.name).toBe('Virtual Device');
171
- expect(virtualDevice.product_name).toBe('virtual product');
172
- expect(virtualDevice.ip).toBe('');
173
- expect(virtualDevice.remote_keys).toBeUndefined();
174
- });
161
+ const virtualDevice = manager.createVirtualDevice(baseDevice, 'virtual_uuid');
175
162
 
176
- test('generates UUID if not provided', () => {
177
- const baseDevice = new TuyaDevice({
178
- id: 'base_001',
179
- product_id: 'prod_001',
180
- category: 'kg',
181
- name: 'Base',
182
- schema: [],
183
- status: [],
163
+ expect(virtualDevice).toBeDefined();
164
+ expect(virtualDevice).toBeInstanceOf(TuyaDevice);
165
+ expect(virtualDevice.sub).toBe(true);
166
+ expect(virtualDevice.parent_id).toBe('base_device_001');
184
167
  });
185
168
 
186
- const virtualDevice = manager.createVirtualDevice(baseDevice, '');
169
+ test('sets virtual device properties', () => {
170
+ const baseDevice = new TuyaDevice({
171
+ id: 'base_001',
172
+ product_id: 'prod_001',
173
+ category: 'dj',
174
+ name: 'Base',
175
+ schema: [],
176
+ status: [],
177
+ });
187
178
 
188
- expect(virtualDevice.id).toBeDefined();
189
- expect(virtualDevice.id.length).toBeGreaterThan(0);
190
- });
179
+ const virtualDevice = manager.createVirtualDevice(baseDevice, 'virt_uuid_123');
191
180
 
192
- test('clones base device schema', () => {
193
- const baseDevice = new TuyaDevice({
194
- id: 'base_001',
195
- product_id: 'prod_001',
196
- category: 'dj',
197
- name: 'Base',
198
- schema: [
199
- {
200
- code: 'switch',
201
- mode: 'rw' as any,
202
- type: 'Boolean' as any,
203
- property: {} as any,
204
- },
205
- ],
206
- status: [],
181
+ expect(virtualDevice.name).toBe('Virtual Device');
182
+ expect(virtualDevice.product_name).toBe('virtual product');
183
+ expect(virtualDevice.ip).toBe('');
184
+ expect(virtualDevice.remote_keys).toBeUndefined();
207
185
  });
208
186
 
209
- const virtualDevice = manager.createVirtualDevice(baseDevice, 'virt_uuid');
187
+ test('generates UUID if not provided', () => {
188
+ const baseDevice = new TuyaDevice({
189
+ id: 'base_001',
190
+ product_id: 'prod_001',
191
+ category: 'kg',
192
+ name: 'Base',
193
+ schema: [],
194
+ status: [],
195
+ });
210
196
 
211
- expect(virtualDevice.schema).toBeDefined();
212
- expect(virtualDevice.schema.length).toBeGreaterThan(0);
213
- });
197
+ const virtualDevice = manager.createVirtualDevice(baseDevice, '');
214
198
 
215
- test('clones base device status', () => {
216
- const baseDevice = new TuyaDevice({
217
- id: 'base_001',
218
- product_id: 'prod_001',
219
- category: 'dj',
220
- name: 'Base',
221
- schema: [],
222
- status: [{ code: 'switch_1', value: 1 }],
199
+ expect(virtualDevice.id).toBeDefined();
200
+ expect(virtualDevice.id.length).toBeGreaterThan(0);
223
201
  });
224
202
 
225
- const virtualDevice = manager.createVirtualDevice(baseDevice, 'virt_uuid');
203
+ test('clones base device schema', () => {
204
+ const baseDevice = new TuyaDevice({
205
+ id: 'base_001',
206
+ product_id: 'prod_001',
207
+ category: 'dj',
208
+ name: 'Base',
209
+ schema: [
210
+ {
211
+ code: 'switch',
212
+ mode: 'rw' as any,
213
+ type: 'Boolean' as any,
214
+ property: {} as any,
215
+ },
216
+ ],
217
+ status: [],
218
+ });
226
219
 
227
- expect(virtualDevice.status).toBeDefined();
228
- });
229
- });
220
+ const virtualDevice = manager.createVirtualDevice(baseDevice, 'virt_uuid');
230
221
 
231
- describe('device retrieval', () => {
232
- test('finds device by ID', () => {
233
- const device1 = new TuyaDevice({
234
- id: 'device_001',
235
- product_id: 'prod_001',
236
- category: 'dj',
237
- name: 'Device 1',
238
- schema: [],
239
- status: [],
222
+ expect(virtualDevice.schema).toBeDefined();
223
+ expect(virtualDevice.schema.length).toBeGreaterThan(0);
240
224
  });
241
225
 
242
- manager.devices.push(device1);
243
-
244
- const found = manager.getDevice('device_001');
245
- expect(found).toBe(device1);
246
- });
226
+ test('clones base device status', () => {
227
+ const baseDevice = new TuyaDevice({
228
+ id: 'base_001',
229
+ product_id: 'prod_001',
230
+ category: 'dj',
231
+ name: 'Base',
232
+ schema: [],
233
+ status: [{ code: 'switch_1', value: 1 }],
234
+ });
247
235
 
248
- test('returns undefined for non-existent device', () => {
249
- manager.devices = [];
250
- const found = manager.getDevice('non_existent');
236
+ const virtualDevice = manager.createVirtualDevice(baseDevice, 'virt_uuid');
251
237
 
252
- expect(found).toBeUndefined();
238
+ expect(virtualDevice.status).toBeDefined();
239
+ });
253
240
  });
254
241
 
255
- test('finds correct device among multiple', () => {
256
- const device1 = new TuyaDevice({
257
- id: 'device_001',
258
- product_id: 'prod_001',
259
- category: 'dj',
260
- name: 'Device 1',
261
- schema: [],
262
- status: [],
263
- });
242
+ describe('device retrieval', () => {
243
+ test('finds device by ID', () => {
244
+ const device1 = new TuyaDevice({
245
+ id: 'device_001',
246
+ product_id: 'prod_001',
247
+ category: 'dj',
248
+ name: 'Device 1',
249
+ schema: [],
250
+ status: [],
251
+ });
264
252
 
265
- const device2 = new TuyaDevice({
266
- id: 'device_002',
267
- product_id: 'prod_002',
268
- category: 'kg',
269
- name: 'Device 2',
270
- schema: [],
271
- status: [],
272
- });
253
+ manager.devices.push(device1);
273
254
 
274
- manager.devices = [device1, device2];
255
+ const found = manager.getDevice('device_001');
256
+ expect(found).toBe(device1);
257
+ });
275
258
 
276
- const found = manager.getDevice('device_002');
277
- expect(found?.id).toBe('device_002');
278
- expect(found?.category).toBe('kg');
279
- });
280
- });
259
+ test('returns undefined for non-existent device', () => {
260
+ manager.devices = [];
261
+ const found = manager.getDevice('non_existent');
281
262
 
282
- describe('device array management', () => {
283
- test('can add devices to array', () => {
284
- const device = new TuyaDevice({
285
- id: 'dev_001',
286
- product_id: 'prod_001',
287
- category: 'dj',
288
- name: 'Device',
289
- schema: [],
290
- status: [],
263
+ expect(found).toBeUndefined();
291
264
  });
292
265
 
293
- manager.devices.push(device);
266
+ test('finds correct device among multiple', () => {
267
+ const device1 = new TuyaDevice({
268
+ id: 'device_001',
269
+ product_id: 'prod_001',
270
+ category: 'dj',
271
+ name: 'Device 1',
272
+ schema: [],
273
+ status: [],
274
+ });
294
275
 
295
- expect(manager.devices.length).toBe(1);
296
- expect(manager.devices[0]).toBe(device);
297
- });
276
+ const device2 = new TuyaDevice({
277
+ id: 'device_002',
278
+ product_id: 'prod_002',
279
+ category: 'kg',
280
+ name: 'Device 2',
281
+ schema: [],
282
+ status: [],
283
+ });
298
284
 
299
- test('can remove devices from array', () => {
300
- const device1 = new TuyaDevice({
301
- id: 'dev_001',
302
- product_id: 'prod_001',
303
- category: 'dj',
304
- name: 'Device 1',
305
- schema: [],
306
- status: [],
307
- });
285
+ manager.devices = [device1, device2];
308
286
 
309
- const device2 = new TuyaDevice({
310
- id: 'dev_002',
311
- product_id: 'prod_002',
312
- category: 'kg',
313
- name: 'Device 2',
314
- schema: [],
315
- status: [],
287
+ const found = manager.getDevice('device_002');
288
+ expect(found?.id).toBe('device_002');
289
+ expect(found?.category).toBe('kg');
316
290
  });
291
+ });
292
+
293
+ describe('device array management', () => {
294
+ test('can add devices to array', () => {
295
+ const device = new TuyaDevice({
296
+ id: 'dev_001',
297
+ product_id: 'prod_001',
298
+ category: 'dj',
299
+ name: 'Device',
300
+ schema: [],
301
+ status: [],
302
+ });
317
303
 
318
- manager.devices = [device1, device2];
319
- manager.devices = manager.devices.filter(d => d.id !== 'dev_001');
304
+ manager.devices.push(device);
320
305
 
321
- expect(manager.devices.length).toBe(1);
322
- expect(manager.devices[0].id).toBe('dev_002');
323
- });
306
+ expect(manager.devices.length).toBe(1);
307
+ expect(manager.devices[0]).toBe(device);
308
+ });
324
309
 
325
- test('can clear all devices', () => {
326
- manager.devices = [
327
- new TuyaDevice({
328
- id: 'dev_1',
329
- product_id: 'prod_1',
310
+ test('can remove devices from array', () => {
311
+ const device1 = new TuyaDevice({
312
+ id: 'dev_001',
313
+ product_id: 'prod_001',
330
314
  category: 'dj',
331
315
  name: 'Device 1',
332
316
  schema: [],
333
317
  status: [],
334
- }),
335
- new TuyaDevice({
336
- id: 'dev_2',
337
- product_id: 'prod_2',
318
+ });
319
+
320
+ const device2 = new TuyaDevice({
321
+ id: 'dev_002',
322
+ product_id: 'prod_002',
338
323
  category: 'kg',
339
324
  name: 'Device 2',
340
325
  schema: [],
341
326
  status: [],
342
- }),
343
- ];
327
+ });
344
328
 
345
- manager.devices = [];
329
+ manager.devices = [device1, device2];
330
+ manager.devices = manager.devices.filter(d => d.id !== 'dev_001');
346
331
 
347
- expect(manager.devices.length).toBe(0);
348
- });
349
- });
332
+ expect(manager.devices.length).toBe(1);
333
+ expect(manager.devices[0].id).toBe('dev_002');
334
+ });
350
335
 
351
- describe('owner ID management', () => {
352
- test('can add owner IDs', () => {
353
- manager.ownerIDs.push('owner_123');
354
- manager.ownerIDs.push('owner_456');
336
+ test('can clear all devices', () => {
337
+ manager.devices = [
338
+ new TuyaDevice({
339
+ id: 'dev_1',
340
+ product_id: 'prod_1',
341
+ category: 'dj',
342
+ name: 'Device 1',
343
+ schema: [],
344
+ status: [],
345
+ }),
346
+ new TuyaDevice({
347
+ id: 'dev_2',
348
+ product_id: 'prod_2',
349
+ category: 'kg',
350
+ name: 'Device 2',
351
+ schema: [],
352
+ status: [],
353
+ }),
354
+ ];
355
+
356
+ manager.devices = [];
355
357
 
356
- expect(manager.ownerIDs.length).toBe(2);
357
- expect(manager.ownerIDs).toContain('owner_123');
358
+ expect(manager.devices.length).toBe(0);
359
+ });
358
360
  });
359
361
 
360
- test('can clear owner IDs', () => {
361
- manager.ownerIDs = ['owner_1', 'owner_2'];
362
- manager.ownerIDs = [];
362
+ describe('owner ID management', () => {
363
+ test('can add owner IDs', () => {
364
+ manager.ownerIDs.push('owner_123');
365
+ manager.ownerIDs.push('owner_456');
363
366
 
364
- expect(manager.ownerIDs.length).toBe(0);
365
- });
367
+ expect(manager.ownerIDs.length).toBe(2);
368
+ expect(manager.ownerIDs).toContain('owner_123');
369
+ });
366
370
 
367
- test('prevents duplicate owner IDs', () => {
368
- manager.ownerIDs = ['owner_123', 'owner_456'].filter(
369
- (id, index, self) => self.indexOf(id) === index
370
- );
371
+ test('can clear owner IDs', () => {
372
+ manager.ownerIDs = ['owner_1', 'owner_2'];
373
+ manager.ownerIDs = [];
371
374
 
372
- expect(new Set(manager.ownerIDs).size).toBe(manager.ownerIDs.length);
373
- });
374
- });
375
+ expect(manager.ownerIDs.length).toBe(0);
376
+ });
375
377
 
376
- describe('event emitter behavior', () => {
377
- test('is instance of EventEmitter', () => {
378
- expect(manager instanceof EventEmitter).toBe(true);
378
+ test('prevents duplicate owner IDs', () => {
379
+ manager.ownerIDs = ['owner_123', 'owner_456'].filter(
380
+ (id, index, self) => self.indexOf(id) === index
381
+ );
382
+
383
+ expect(new Set(manager.ownerIDs).size).toBe(manager.ownerIDs.length);
384
+ });
379
385
  });
380
386
 
381
- test('can listen for events', (done) => {
382
- manager.on('test_event', (data) => {
383
- expect(data).toBe('test_data');
384
- done();
387
+ describe('event emitter behavior', () => {
388
+ test('is instance of EventEmitter', () => {
389
+ expect(manager instanceof EventEmitter).toBe(true);
385
390
  });
386
391
 
387
- manager.emit('test_event', 'test_data');
388
- });
392
+ test('can listen for events', (done) => {
393
+ manager.on('test_event', (data) => {
394
+ expect(data).toBe('test_data');
395
+ done();
396
+ });
389
397
 
390
- test('can emit DEVICE_ADD events', (done) => {
391
- manager.on(TuyaDeviceManager.Events.DEVICE_ADD, (device) => {
392
- expect(device.id).toBe('device_001');
393
- done();
398
+ manager.emit('test_event', 'test_data');
394
399
  });
395
400
 
396
- const device = new TuyaDevice({
397
- id: 'device_001',
398
- product_id: 'prod_001',
399
- category: 'dj',
400
- name: 'Device',
401
- schema: [],
402
- status: [],
401
+ test('can emit DEVICE_ADD events', (done) => {
402
+ manager.on(TuyaDeviceManager.Events.DEVICE_ADD, (device) => {
403
+ expect(device.id).toBe('device_001');
404
+ done();
405
+ });
406
+
407
+ const device = new TuyaDevice({
408
+ id: 'device_001',
409
+ product_id: 'prod_001',
410
+ category: 'dj',
411
+ name: 'Device',
412
+ schema: [],
413
+ status: [],
414
+ });
415
+
416
+ manager.emit(TuyaDeviceManager.Events.DEVICE_ADD, device);
403
417
  });
404
418
 
405
- manager.emit(TuyaDeviceManager.Events.DEVICE_ADD, device);
419
+ test('can emit DEVICE_STATUS_UPDATE events', (done) => {
420
+ manager.on(TuyaDeviceManager.Events.DEVICE_STATUS_UPDATE, (deviceId, status) => {
421
+ expect(deviceId).toBe('device_001');
422
+ expect(status).toBeDefined();
423
+ done();
424
+ });
425
+
426
+ manager.emit(TuyaDeviceManager.Events.DEVICE_STATUS_UPDATE, 'device_001', {});
427
+ });
406
428
  });
407
429
 
408
- test('can emit DEVICE_STATUS_UPDATE events', (done) => {
409
- manager.on(TuyaDeviceManager.Events.DEVICE_STATUS_UPDATE, (deviceId, status) => {
410
- expect(deviceId).toBe('device_001');
411
- expect(status).toBeDefined();
412
- done();
430
+ describe('MQTT integration', () => {
431
+ test('adds message listener on init', () => {
432
+ const newManager = new TuyaHomeDeviceManager(mockAPI, mockConfig, false);
433
+ expect(newManager.mq.addMessageListener).toHaveBeenCalled();
413
434
  });
414
435
 
415
- manager.emit(TuyaDeviceManager.Events.DEVICE_STATUS_UPDATE, 'device_001', {});
436
+ test('MQTT is available through manager', () => {
437
+ expect(manager.mq).toBeDefined();
438
+ expect(manager.mq.addMessageListener).toBeDefined();
439
+ });
416
440
  });
417
- });
418
441
 
419
- describe('MQTT integration', () => {
420
- test('adds message listener on init', () => {
421
- const newManager = new TuyaDeviceManager(mockAPI, false);
422
- expect(newManager.mq.addMessageListener).toHaveBeenCalled();
423
- });
442
+ describe('device filtering', () => {
443
+ test('can filter devices by category', () => {
444
+ manager.devices = [
445
+ new TuyaDevice({
446
+ id: 'light_1',
447
+ product_id: 'prod_1',
448
+ category: 'dj',
449
+ name: 'Light',
450
+ schema: [],
451
+ status: [],
452
+ }),
453
+ new TuyaDevice({
454
+ id: 'switch_1',
455
+ product_id: 'prod_2',
456
+ category: 'kg',
457
+ name: 'Switch',
458
+ schema: [],
459
+ status: [],
460
+ }),
461
+ ];
462
+
463
+ const lights = manager.devices.filter(d => d.category === 'dj');
464
+ expect(lights.length).toBe(1);
465
+ expect(lights[0].category).toBe('dj');
466
+ });
424
467
 
425
- test('MQTT is available through manager', () => {
426
- expect(manager.mq).toBeDefined();
427
- expect(manager.mq.addMessageListener).toBeDefined();
468
+ test('can filter virtual devices', () => {
469
+ const baseDevice = new TuyaDevice({
470
+ id: 'base_001',
471
+ product_id: 'prod_001',
472
+ category: 'dj',
473
+ name: 'Base',
474
+ schema: [],
475
+ status: [],
476
+ });
477
+
478
+ const virtualDevice = manager.createVirtualDevice(baseDevice, 'virt_123');
479
+ manager.devices = [baseDevice, virtualDevice];
480
+
481
+ const virtuals = manager.devices.filter(d => d.sub === true);
482
+ expect(virtuals.length).toBe(1);
483
+ expect(virtuals[0].parent_id).toBe('base_001');
484
+ });
428
485
  });
429
- });
430
486
 
431
- describe('device filtering', () => {
432
- test('can filter devices by category', () => {
433
- manager.devices = [
434
- new TuyaDevice({
435
- id: 'light_1',
487
+ describe('multiple manager instances', () => {
488
+ test('maintain separate device lists', () => {
489
+ const manager1 = new TuyaHomeDeviceManager(mockAPI, mockConfig, false);
490
+ const manager2 = new TuyaHomeDeviceManager(mockAPI, mockConfig, false);
491
+
492
+ const device1 = new TuyaDevice({
493
+ id: 'dev_manager1',
436
494
  product_id: 'prod_1',
437
495
  category: 'dj',
438
- name: 'Light',
496
+ name: 'Device 1',
439
497
  schema: [],
440
498
  status: [],
441
- }),
442
- new TuyaDevice({
443
- id: 'switch_1',
499
+ });
500
+
501
+ const device2 = new TuyaDevice({
502
+ id: 'dev_manager2',
444
503
  product_id: 'prod_2',
445
504
  category: 'kg',
446
- name: 'Switch',
505
+ name: 'Device 2',
447
506
  schema: [],
448
507
  status: [],
449
- }),
450
- ];
508
+ });
451
509
 
452
- const lights = manager.devices.filter(d => d.category === 'dj');
453
- expect(lights.length).toBe(1);
454
- expect(lights[0].category).toBe('dj');
455
- });
510
+ manager1.devices.push(device1);
511
+ manager2.devices.push(device2);
456
512
 
457
- test('can filter virtual devices', () => {
458
- const baseDevice = new TuyaDevice({
459
- id: 'base_001',
460
- product_id: 'prod_001',
461
- category: 'dj',
462
- name: 'Base',
463
- schema: [],
464
- status: [],
513
+ expect(manager1.devices.length).toBe(1);
514
+ expect(manager2.devices.length).toBe(1);
515
+ expect(manager1.devices[0].id).not.toBe(manager2.devices[0].id);
465
516
  });
466
-
467
- const virtualDevice = manager.createVirtualDevice(baseDevice, 'virt_123');
468
- manager.devices = [baseDevice, virtualDevice];
469
-
470
- const virtuals = manager.devices.filter(d => d.sub === true);
471
- expect(virtuals.length).toBe(1);
472
- expect(virtuals[0].parent_id).toBe('base_001');
473
517
  });
474
- });
475
518
 
476
- describe('multiple manager instances', () => {
477
- test('maintain separate device lists', () => {
478
- const manager1 = new TuyaDeviceManager(mockAPI, false);
479
- const manager2 = new TuyaDeviceManager(mockAPI, false);
519
+ describe('device type handling', () => {
520
+ test('handles light devices', () => {
521
+ const light = new TuyaDevice({
522
+ id: 'light_001',
523
+ product_id: 'dj_prod',
524
+ category: 'dj',
525
+ name: 'Smart Light',
526
+ schema: [],
527
+ status: [],
528
+ });
480
529
 
481
- const device1 = new TuyaDevice({
482
- id: 'dev_manager1',
483
- product_id: 'prod_1',
484
- category: 'dj',
485
- name: 'Device 1',
486
- schema: [],
487
- status: [],
488
- });
530
+ manager.devices.push(light);
489
531
 
490
- const device2 = new TuyaDevice({
491
- id: 'dev_manager2',
492
- product_id: 'prod_2',
493
- category: 'kg',
494
- name: 'Device 2',
495
- schema: [],
496
- status: [],
532
+ const found = manager.getDevice('light_001');
533
+ expect(found?.category).toBe('dj');
497
534
  });
498
535
 
499
- manager1.devices.push(device1);
500
- manager2.devices.push(device2);
536
+ test('handles switch devices', () => {
537
+ const sw = new TuyaDevice({
538
+ id: 'switch_001',
539
+ product_id: 'kg_prod',
540
+ category: 'kg',
541
+ name: 'Smart Switch',
542
+ schema: [],
543
+ status: [],
544
+ });
501
545
 
502
- expect(manager1.devices.length).toBe(1);
503
- expect(manager2.devices.length).toBe(1);
504
- expect(manager1.devices[0].id).not.toBe(manager2.devices[0].id);
505
- });
506
- });
546
+ manager.devices.push(sw);
507
547
 
508
- describe('device type handling', () => {
509
- test('handles light devices', () => {
510
- const light = new TuyaDevice({
511
- id: 'light_001',
512
- product_id: 'dj_prod',
513
- category: 'dj',
514
- name: 'Smart Light',
515
- schema: [],
516
- status: [],
548
+ const found = manager.getDevice('switch_001');
549
+ expect(found?.category).toBe('kg');
517
550
  });
518
551
 
519
- manager.devices.push(light);
552
+ test('handles thermostat devices', () => {
553
+ const thermostat = new TuyaDevice({
554
+ id: 'thermo_001',
555
+ product_id: 'wk_prod',
556
+ category: 'wk',
557
+ name: 'Thermostat',
558
+ schema: [],
559
+ status: [],
560
+ });
520
561
 
521
- const found = manager.getDevice('light_001');
522
- expect(found?.category).toBe('dj');
523
- });
562
+ manager.devices.push(thermostat);
524
563
 
525
- test('handles switch devices', () => {
526
- const sw = new TuyaDevice({
527
- id: 'switch_001',
528
- product_id: 'kg_prod',
529
- category: 'kg',
530
- name: 'Smart Switch',
531
- schema: [],
532
- status: [],
564
+ const found = manager.getDevice('thermo_001');
565
+ expect(found?.category).toBe('wk');
533
566
  });
567
+ });
534
568
 
535
- manager.devices.push(sw);
569
+ describe('async operations', () => {
570
+ test('updateDevices returns array', async () => {
571
+ const result = await manager.updateDevices([]);
572
+ expect(Array.isArray(result)).toBe(true);
573
+ });
536
574
 
537
- const found = manager.getDevice('switch_001');
538
- expect(found?.category).toBe('kg');
575
+ test('updateDevice can be called', async () => {
576
+ // Mock implementation since actual HTTP calls are not set up
577
+ const result = await manager.updateDevice('device_001');
578
+ expect(typeof result === 'object' || result === null).toBe(true);
579
+ });
539
580
  });
540
581
 
541
- test('handles thermostat devices', () => {
542
- const thermostat = new TuyaDevice({
543
- id: 'thermo_001',
544
- product_id: 'wk_prod',
545
- category: 'wk',
546
- name: 'Thermostat',
547
- schema: [],
548
- status: [],
582
+ describe('infrared device methods', () => {
583
+ test('getInfraredRemotes calls API with correct endpoint', async () => {
584
+ const result = await manager.getInfraredRemotes('ir_001');
585
+ expect(result).toBeDefined();
549
586
  });
550
587
 
551
- manager.devices.push(thermostat);
588
+ test('getInfraredKeys calls API with correct endpoint', async () => {
589
+ const result = await manager.getInfraredKeys('ir_001', 'remote_001');
590
+ expect(result).toBeDefined();
591
+ });
552
592
 
553
- const found = manager.getDevice('thermo_001');
554
- expect(found?.category).toBe('wk');
555
- });
556
- });
593
+ test('getInfraredACStatus calls API with correct endpoint', async () => {
594
+ const result = await manager.getInfraredACStatus('ir_001', 'remote_001');
595
+ expect(result).toBeDefined();
596
+ });
557
597
 
558
- describe('async operations', () => {
559
- test('updateDevices returns array', async () => {
560
- const result = await manager.updateDevices([]);
561
- expect(Array.isArray(result)).toBe(true);
598
+ test('getInfraredDIYKeys calls API with correct endpoint', async () => {
599
+ const result = await manager.getInfraredDIYKeys('ir_001', 'remote_001');
600
+ expect(result).toBeDefined();
601
+ });
562
602
  });
563
603
 
564
- test('updateDevice can be called', async () => {
565
- // Mock implementation since actual HTTP calls are not set up
566
- const result = await manager.updateDevice('device_001');
567
- expect(typeof result === 'object' || result === null).toBe(true);
568
- });
569
- });
604
+ describe('infrared remote resolution', () => {
605
+ test('resolveInfraredRemotes returns empty array when no IR devices', async () => {
606
+ const parentDevice = new TuyaDevice({
607
+ id: 'parent_001',
608
+ name: 'Hub',
609
+ category: 'wifizigbee',
610
+ product_id: 'hub_prod',
611
+ schema: [],
612
+ status: [],
613
+ });
570
614
 
571
- describe('infrared device methods', () => {
572
- test('getInfraredRemotes calls API with correct endpoint', async () => {
573
- const result = await manager.getInfraredRemotes('ir_001');
574
- expect(result).toBeDefined();
575
- });
615
+ const result = await manager.resolveInfraredRemotes(parentDevice, [parentDevice]);
616
+ expect(Array.isArray(result)).toBe(true);
617
+ expect(result.length).toBe(0);
618
+ });
576
619
 
577
- test('getInfraredKeys calls API with correct endpoint', async () => {
578
- const result = await manager.getInfraredKeys('ir_001', 'remote_001');
579
- expect(result).toBeDefined();
580
- });
620
+ test('resolveInfraredRemotes finds IR devices with matching location', async () => {
621
+ const parentDevice = new TuyaDevice({
622
+ id: 'parent_001',
623
+ name: 'Hub',
624
+ category: 'wnykq',
625
+ product_id: 'hub_prod',
626
+ schema: [],
627
+ status: [],
628
+ lat: '40.7128',
629
+ lon: '-74.0060',
630
+ });
581
631
 
582
- test('getInfraredACStatus calls API with correct endpoint', async () => {
583
- const result = await manager.getInfraredACStatus('ir_001', 'remote_001');
584
- expect(result).toBeDefined();
585
- });
632
+ const irDevice = new TuyaDevice({
633
+ id: 'ir_001',
634
+ name: 'IR Remote',
635
+ category: 'infrared_ac',
636
+ product_id: 'ir_prod',
637
+ schema: [],
638
+ status: [],
639
+ sub: true,
640
+ lat: '40.7128',
641
+ lon: '-74.0060',
642
+ });
586
643
 
587
- test('getInfraredDIYKeys calls API with correct endpoint', async () => {
588
- const result = await manager.getInfraredDIYKeys('ir_001', 'remote_001');
589
- expect(result).toBeDefined();
590
- });
591
- });
592
-
593
- describe('infrared remote resolution', () => {
594
- test('resolveInfraredRemotes returns empty array when no IR devices', () => {
595
- const parentDevice = new TuyaDevice({
596
- id: 'parent_001',
597
- name: 'Hub',
598
- category: 'wifizigbee',
599
- product_id: 'hub_prod',
600
- schema: [],
601
- status: [],
602
- });
603
-
604
- const result = manager.resolveInfraredRemotes(parentDevice, [parentDevice]);
605
- expect(Array.isArray(result)).toBe(true);
606
- expect(result.length).toBe(0);
607
- });
644
+ const result = await manager.resolveInfraredRemotes(parentDevice, [parentDevice, irDevice]);
645
+ expect(result.length).toBeGreaterThan(0);
646
+ });
608
647
 
609
- test('resolveInfraredRemotes finds IR devices with matching location', () => {
610
- const parentDevice = new TuyaDevice({
611
- id: 'parent_001',
612
- name: 'Hub',
613
- category: 'wifizigbee',
614
- product_id: 'hub_prod',
615
- schema: [],
616
- status: [],
617
- lat: '40.7128',
618
- lon: '-74.0060',
619
- });
620
-
621
- const irDevice = new TuyaDevice({
622
- id: 'ir_001',
623
- name: 'IR Remote',
624
- category: 'infrared_ac',
625
- product_id: 'ir_prod',
626
- schema: [],
627
- status: [],
628
- sub: true,
629
- lat: '40.7128',
630
- lon: '-74.0060',
631
- });
632
-
633
- const result = manager.resolveInfraredRemotes(parentDevice, [parentDevice, irDevice]);
634
- expect(result.length).toBeGreaterThan(0);
635
- });
648
+ test('resolveInfraredRemotes finds IR devices with matching update_time', async () => {
649
+ const timestamp = 1234567890;
650
+ const parentDevice = new TuyaDevice({
651
+ id: 'parent_001',
652
+ name: 'Hub',
653
+ category: 'wnykq',
654
+ product_id: 'hub_prod',
655
+ schema: [],
656
+ status: [],
657
+ update_time: timestamp,
658
+ });
636
659
 
637
- test('resolveInfraredRemotes finds IR devices with matching update_time', () => {
638
- const timestamp = 1234567890;
639
- const parentDevice = new TuyaDevice({
640
- id: 'parent_001',
641
- name: 'Hub',
642
- category: 'wifizigbee',
643
- product_id: 'hub_prod',
644
- schema: [],
645
- status: [],
646
- update_time: timestamp,
647
- });
648
-
649
- const irDevice = new TuyaDevice({
650
- id: 'ir_001',
651
- name: 'IR Remote',
652
- category: 'infrared_light',
653
- product_id: 'ir_prod',
654
- schema: [],
655
- status: [],
656
- sub: true,
657
- update_time: timestamp,
658
- });
659
-
660
- const result = manager.resolveInfraredRemotes(parentDevice, [parentDevice, irDevice]);
661
- expect(result.length).toBeGreaterThan(0);
662
- });
663
- });
664
-
665
- describe('infrared device fixing', () => {
666
- test('fixInfraredDevice sets org_category_id', () => {
667
- const device = new TuyaDevice({
668
- id: 'ir_ac_001',
669
- name: 'IR AC',
670
- category: 'infrared_ac',
671
- product_id: 'prsgoryjfdtb42r4',
672
- schema: [],
673
- status: [],
674
- remote_keys: {
675
- category_id: 5,
676
- org_category_id: 5,
677
- brand_id: 0,
678
- remote_index: 0,
679
- single_air: false,
680
- duplicate_power: false,
681
- key_list: [],
682
- key_range: [],
683
- },
684
- });
685
-
686
- manager.fixInfraredDevice(device);
687
- expect(device.remote_keys?.org_category_id).toBe(5);
688
- });
689
- });
690
-
691
- describe('HAP category resolution', () => {
692
- test('resolveHAPCategoryID returns correct category for fan product', () => {
693
- const device = new TuyaDevice({
694
- id: 'ir_fan_001',
695
- name: 'IR Fan',
696
- category: 'infrared_fan',
697
- product_id: 'prsgoryjfdtb42r4',
698
- schema: [],
699
- status: [],
700
- remote_keys: {
701
- category_id: 5,
702
- org_category_id: 5,
703
- brand_id: 0,
704
- remote_index: 0,
705
- single_air: false,
706
- duplicate_power: false,
707
- key_list: [],
708
- key_range: [],
709
- },
710
- });
711
-
712
- const result = manager.resolveHAPCategoryID(device);
713
- expect(result).toBe(8); // Fan category
714
- });
660
+ const irDevice = new TuyaDevice({
661
+ id: 'ir_001',
662
+ name: 'IR Remote',
663
+ category: 'infrared_light',
664
+ product_id: 'ir_prod',
665
+ schema: [],
666
+ status: [],
667
+ sub: true,
668
+ update_time: timestamp,
669
+ });
715
670
 
716
- test('resolveHAPCategoryID returns DIY for DIY product', () => {
717
- const device = new TuyaDevice({
718
- id: 'ir_diy_001',
719
- name: 'IR DIY',
720
- category: 'infrared_diy',
721
- product_id: 'k6ozylayfgnskuq6',
722
- schema: [],
723
- status: [],
724
- remote_keys: {
725
- category_id: 5,
726
- org_category_id: 5,
727
- brand_id: 0,
728
- remote_index: 0,
729
- single_air: false,
730
- duplicate_power: false,
731
- key_list: [],
732
- key_range: [],
733
- },
734
- });
735
-
736
- const result = manager.resolveHAPCategoryID(device);
737
- expect(result).toBe(999); // DIY category
671
+ const result = await manager.resolveInfraredRemotes(parentDevice, [parentDevice, irDevice]);
672
+ expect(result.length).toBeGreaterThan(0);
673
+ });
738
674
  });
739
675
 
740
- test('resolveHAPCategoryID falls back to device category', () => {
741
- const device = new TuyaDevice({
742
- id: 'ir_other_001',
743
- name: 'IR Other',
744
- category: 'infrared_other',
745
- product_id: 'other_prod',
746
- schema: [],
747
- status: [],
748
- remote_keys: {
749
- category_id: 7,
750
- org_category_id: 7,
751
- brand_id: 0,
752
- remote_index: 0,
753
- single_air: false,
754
- duplicate_power: false,
755
- key_list: [],
756
- key_range: [],
757
- },
758
- });
759
-
760
- const result = manager.resolveHAPCategoryID(device);
761
- expect(result).toBe(7);
676
+ describe('infrared device fixing', () => {
677
+ test('fixInfraredDevice sets org_category_id', () => {
678
+ const device = new TuyaDevice({
679
+ id: 'ir_ac_001',
680
+ name: 'IR AC',
681
+ category: 'infrared_ac',
682
+ product_id: 'prsgoryjfdtb42r4',
683
+ schema: [],
684
+ status: [],
685
+ remote_keys: {
686
+ category_id: 5,
687
+ org_category_id: 5,
688
+ brand_id: 0,
689
+ remote_index: 0,
690
+ single_air: false,
691
+ duplicate_power: false,
692
+ key_list: [],
693
+ key_range: [],
694
+ },
695
+ });
696
+
697
+ manager.fixInfraredDevice(device);
698
+ expect(device.remote_keys?.org_category_id).toBe(5);
699
+ });
762
700
  });
763
701
 
764
- test('resolveHAPCategoryID defaults to DIY for device without remote_keys', () => {
765
- const device = new TuyaDevice({
766
- id: 'ir_001',
767
- name: 'IR Device',
768
- category: 'infrared_ac',
769
- product_id: 'ir_prod',
770
- schema: [],
771
- status: [],
702
+ describe('HAP category resolution', () => {
703
+ test('resolveHAPCategoryID returns correct category for fan product', () => {
704
+ const device = new TuyaDevice({
705
+ id: 'ir_fan_001',
706
+ name: 'IR Fan',
707
+ category: 'infrared_fan',
708
+ product_id: 'prsgoryjfdtb42r4',
709
+ schema: [],
710
+ status: [],
711
+ remote_keys: {
712
+ category_id: 5,
713
+ org_category_id: 5,
714
+ brand_id: 0,
715
+ remote_index: 0,
716
+ single_air: false,
717
+ duplicate_power: false,
718
+ key_list: [],
719
+ key_range: [],
720
+ },
721
+ });
722
+
723
+ const result = manager.resolveHAPCategoryID(device);
724
+ expect(result).toBe(8); // Fan category
772
725
  });
773
726
 
774
- const result = manager.resolveHAPCategoryID(device);
775
- expect(result).toBe(999);
776
- });
777
- });
727
+ test('resolveHAPCategoryID returns DIY for DIY product', () => {
728
+ const device = new TuyaDevice({
729
+ id: 'ir_diy_001',
730
+ name: 'IR DIY',
731
+ category: 'infrared_diy',
732
+ product_id: 'k6ozylayfgnskuq6',
733
+ schema: [],
734
+ status: [],
735
+ remote_keys: {
736
+ category_id: 5,
737
+ org_category_id: 5,
738
+ brand_id: 0,
739
+ remote_index: 0,
740
+ single_air: false,
741
+ duplicate_power: false,
742
+ key_list: [],
743
+ key_range: [],
744
+ },
745
+ });
778
746
 
779
- describe('updateInfraredRemotes', () => {
780
- test('updateInfraredRemotes skips non-IR devices', async () => {
781
- const devices = [
782
- new TuyaDevice({
783
- id: 'light_001',
784
- name: 'Light',
785
- category: 'dj',
786
- product_id: 'light_prod',
747
+ const result = manager.resolveHAPCategoryID(device);
748
+ expect(result).toBe(999); // DIY category
749
+ });
750
+
751
+ test('resolveHAPCategoryID falls back to device category', () => {
752
+ const device = new TuyaDevice({
753
+ id: 'ir_other_001',
754
+ name: 'IR Other',
755
+ category: 'infrared_other',
756
+ product_id: 'other_prod',
787
757
  schema: [],
788
758
  status: [],
789
- }),
790
- ];
759
+ remote_keys: {
760
+ category_id: 7,
761
+ org_category_id: 7,
762
+ brand_id: 0,
763
+ remote_index: 0,
764
+ single_air: false,
765
+ duplicate_power: false,
766
+ key_list: [],
767
+ key_range: [],
768
+ },
769
+ });
791
770
 
792
- await manager.updateInfraredRemotes(devices);
793
- // Should not throw
794
- expect(true).toBe(true);
795
- });
796
- });
797
-
798
- describe('dump method', () => {
799
- test('dump logs object properties without error', () => {
800
- const testObj = { key1: 'value1', key2: 123 };
801
- expect(() => {
802
- manager.dump(testObj);
803
- }).not.toThrow();
771
+ const result = manager.resolveHAPCategoryID(device);
772
+ expect(result).toBe(7);
773
+ });
774
+
775
+ test('resolveHAPCategoryID defaults to DIY for device without remote_keys', () => {
776
+ const device = new TuyaDevice({
777
+ id: 'ir_001',
778
+ name: 'IR Device',
779
+ category: 'infrared_ac',
780
+ product_id: 'ir_prod',
781
+ schema: [],
782
+ status: [],
783
+ });
784
+
785
+ const result = manager.resolveHAPCategoryID(device);
786
+ expect(result).toBe(999);
787
+ });
804
788
  });
805
789
 
806
- test('dump handles nested objects', () => {
807
- const testObj = { outer: { inner: 'value' } };
808
- expect(() => {
809
- manager.dump(testObj);
810
- }).not.toThrow();
790
+ describe('updateInfraredRemotes', () => {
791
+ test('updateInfraredRemotes skips non-IR devices', async () => {
792
+ const devices = [
793
+ new TuyaDevice({
794
+ id: 'light_001',
795
+ name: 'Light',
796
+ category: 'dj',
797
+ product_id: 'light_prod',
798
+ schema: [],
799
+ status: [],
800
+ }),
801
+ ];
802
+
803
+ await manager.updateInfraredRemotes(devices);
804
+ // Should not throw
805
+ expect(true).toBe(true);
806
+ });
811
807
  });
812
- });
813
- });
808
+
809
+ }
810
+ );