@homebridge-plugins/homebridge-eufy-security 4.7.2 → 5.0.0-beta.0

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 (471) hide show
  1. package/AUTHORS +4 -0
  2. package/CODE_OF_CONDUCT.md +30 -0
  3. package/CONTRIBUTING.md +77 -0
  4. package/README.md +152 -49
  5. package/SECURITY.md +30 -0
  6. package/config.schema.json +92 -3
  7. package/dist/account/ownership.js +290 -0
  8. package/dist/account/ownership.js.map +1 -0
  9. package/dist/account/persistence.js +274 -0
  10. package/dist/account/persistence.js.map +1 -0
  11. package/dist/account/temporary-authentication.js +348 -0
  12. package/dist/account/temporary-authentication.js.map +1 -0
  13. package/dist/configuration.js +167 -0
  14. package/dist/configuration.js.map +1 -0
  15. package/dist/device/member-evidence.js +66 -0
  16. package/dist/device/member-evidence.js.map +1 -0
  17. package/dist/device/member-trust.js +114 -0
  18. package/dist/device/member-trust.js.map +1 -0
  19. package/dist/device/snapshot.js +190 -0
  20. package/dist/device/snapshot.js.map +1 -0
  21. package/dist/diagnostics.js +2218 -0
  22. package/dist/diagnostics.js.map +1 -0
  23. package/dist/homekit/adapter.js +2 -0
  24. package/dist/homekit/adapter.js.map +1 -0
  25. package/dist/homekit/adapters/battery.js +208 -0
  26. package/dist/homekit/adapters/battery.js.map +1 -0
  27. package/dist/homekit/adapters/camera-controls.js +527 -0
  28. package/dist/homekit/adapters/camera-controls.js.map +1 -0
  29. package/dist/homekit/adapters/camera-streaming.js +1515 -0
  30. package/dist/homekit/adapters/camera-streaming.js.map +1 -0
  31. package/dist/homekit/adapters/contact.js +138 -0
  32. package/dist/homekit/adapters/contact.js.map +1 -0
  33. package/dist/homekit/adapters/doorbell.js +57 -0
  34. package/dist/homekit/adapters/doorbell.js.map +1 -0
  35. package/dist/homekit/adapters/information.js +55 -0
  36. package/dist/homekit/adapters/information.js.map +1 -0
  37. package/dist/homekit/adapters/lock.js +489 -0
  38. package/dist/homekit/adapters/lock.js.map +1 -0
  39. package/dist/homekit/adapters/motion.js +100 -0
  40. package/dist/homekit/adapters/motion.js.map +1 -0
  41. package/dist/homekit/adapters/registry.js +26 -0
  42. package/dist/homekit/adapters/registry.js.map +1 -0
  43. package/dist/homekit/adapters/security-system.js +425 -0
  44. package/dist/homekit/adapters/security-system.js.map +1 -0
  45. package/dist/homekit/adapters/siren.js +136 -0
  46. package/dist/homekit/adapters/siren.js.map +1 -0
  47. package/dist/homekit/adapters/smart-light.js +662 -0
  48. package/dist/homekit/adapters/smart-light.js.map +1 -0
  49. package/dist/homekit/coverage-matrix.js +119 -0
  50. package/dist/homekit/coverage-matrix.js.map +1 -0
  51. package/dist/homekit/device-control.js +175 -0
  52. package/dist/homekit/device-control.js.map +1 -0
  53. package/dist/homekit/reconciler.js +319 -0
  54. package/dist/homekit/reconciler.js.map +1 -0
  55. package/dist/homekit/representation.js +23 -0
  56. package/dist/homekit/representation.js.map +1 -0
  57. package/dist/index.js +4 -9
  58. package/dist/index.js.map +1 -1
  59. package/dist/media/contracts.js +2 -0
  60. package/dist/media/contracts.js.map +1 -0
  61. package/dist/media/last-successful-image.js +208 -0
  62. package/dist/media/last-successful-image.js.map +1 -0
  63. package/dist/media/live-stream.js +1003 -0
  64. package/dist/media/live-stream.js.map +1 -0
  65. package/dist/media/recording.js +560 -0
  66. package/dist/media/recording.js.map +1 -0
  67. package/dist/media/session-budget.js +39 -0
  68. package/dist/media/session-budget.js.map +1 -0
  69. package/dist/media/snapshot.js +406 -0
  70. package/dist/media/snapshot.js.map +1 -0
  71. package/dist/media/station-live-sessions.js +101 -0
  72. package/dist/media/station-live-sessions.js.map +1 -0
  73. package/dist/platform.js +97 -809
  74. package/dist/platform.js.map +1 -1
  75. package/dist/runtime/channel.js +375 -0
  76. package/dist/runtime/channel.js.map +1 -0
  77. package/dist/runtime/owner.js +682 -0
  78. package/dist/runtime/owner.js.map +1 -0
  79. package/dist/runtime/sdk-client.js +211 -0
  80. package/dist/runtime/sdk-client.js.map +1 -0
  81. package/dist/runtime/tracker.js +211 -0
  82. package/dist/runtime/tracker.js.map +1 -0
  83. package/dist/settings.js +1 -43
  84. package/dist/settings.js.map +1 -1
  85. package/dist/storage.js +59 -0
  86. package/dist/storage.js.map +1 -0
  87. package/dist/ui/dashboard.js +181 -0
  88. package/dist/ui/dashboard.js.map +1 -0
  89. package/dist/ui/device-image.js +22 -0
  90. package/dist/ui/device-image.js.map +1 -0
  91. package/dist/ui/runtime-channel-client.js +305 -0
  92. package/dist/ui/runtime-channel-client.js.map +1 -0
  93. package/dist/ui/server.js +259 -0
  94. package/dist/ui/server.js.map +1 -0
  95. package/homebridge-ui/public/app.css +1902 -0
  96. package/homebridge-ui/public/assets/devices/clean/clean-T1250.webp +0 -0
  97. package/homebridge-ui/public/assets/devices/clean/clean-T2070.webp +0 -0
  98. package/homebridge-ui/public/assets/devices/clean/clean-T2080.webp +0 -0
  99. package/homebridge-ui/public/assets/devices/clean/clean-T2081.webp +0 -0
  100. package/homebridge-ui/public/assets/devices/clean/clean-T2103.webp +0 -0
  101. package/homebridge-ui/public/assets/devices/clean/clean-T2117.webp +0 -0
  102. package/homebridge-ui/public/assets/devices/clean/clean-T2118.webp +0 -0
  103. package/homebridge-ui/public/assets/devices/clean/clean-T2119.webp +0 -0
  104. package/homebridge-ui/public/assets/devices/clean/clean-T211A.webp +0 -0
  105. package/homebridge-ui/public/assets/devices/clean/clean-T2120.webp +0 -0
  106. package/homebridge-ui/public/assets/devices/clean/clean-T2123.webp +0 -0
  107. package/homebridge-ui/public/assets/devices/clean/clean-T2128.webp +0 -0
  108. package/homebridge-ui/public/assets/devices/clean/clean-T2130.webp +0 -0
  109. package/homebridge-ui/public/assets/devices/clean/clean-T2132.webp +0 -0
  110. package/homebridge-ui/public/assets/devices/clean/clean-T2150.webp +0 -0
  111. package/homebridge-ui/public/assets/devices/clean/clean-T2181.webp +0 -0
  112. package/homebridge-ui/public/assets/devices/clean/clean-T2182.webp +0 -0
  113. package/homebridge-ui/public/assets/devices/clean/clean-T2190.webp +0 -0
  114. package/homebridge-ui/public/assets/devices/clean/clean-T2192.webp +0 -0
  115. package/homebridge-ui/public/assets/devices/clean/clean-T2193.webp +0 -0
  116. package/homebridge-ui/public/assets/devices/clean/clean-T2194.webp +0 -0
  117. package/homebridge-ui/public/assets/devices/clean/clean-T2210.webp +0 -0
  118. package/homebridge-ui/public/assets/devices/clean/clean-T2250.webp +0 -0
  119. package/homebridge-ui/public/assets/devices/clean/clean-T2251.webp +0 -0
  120. package/homebridge-ui/public/assets/devices/clean/clean-T2252.webp +0 -0
  121. package/homebridge-ui/public/assets/devices/clean/clean-T2253.webp +0 -0
  122. package/homebridge-ui/public/assets/devices/clean/clean-T2254.webp +0 -0
  123. package/homebridge-ui/public/assets/devices/clean/clean-T2255.webp +0 -0
  124. package/homebridge-ui/public/assets/devices/clean/clean-T2256.webp +0 -0
  125. package/homebridge-ui/public/assets/devices/clean/clean-T2257.webp +0 -0
  126. package/homebridge-ui/public/assets/devices/clean/clean-T2258.webp +0 -0
  127. package/homebridge-ui/public/assets/devices/clean/clean-T2259.webp +0 -0
  128. package/homebridge-ui/public/assets/devices/clean/clean-T2261.webp +0 -0
  129. package/homebridge-ui/public/assets/devices/clean/clean-T2262.webp +0 -0
  130. package/homebridge-ui/public/assets/devices/clean/clean-T2266.webp +0 -0
  131. package/homebridge-ui/public/assets/devices/clean/clean-T2267.webp +0 -0
  132. package/homebridge-ui/public/assets/devices/clean/clean-T2268.webp +0 -0
  133. package/homebridge-ui/public/assets/devices/clean/clean-T2270.webp +0 -0
  134. package/homebridge-ui/public/assets/devices/clean/clean-T2272.webp +0 -0
  135. package/homebridge-ui/public/assets/devices/clean/clean-T2273.webp +0 -0
  136. package/homebridge-ui/public/assets/devices/clean/clean-T2276.webp +0 -0
  137. package/homebridge-ui/public/assets/devices/clean/clean-T2277.webp +0 -0
  138. package/homebridge-ui/public/assets/devices/clean/clean-T2278.webp +0 -0
  139. package/homebridge-ui/public/assets/devices/clean/clean-T2280.webp +0 -0
  140. package/homebridge-ui/public/assets/devices/clean/clean-T2292.webp +0 -0
  141. package/homebridge-ui/public/assets/devices/clean/clean-T2320.webp +0 -0
  142. package/homebridge-ui/public/assets/devices/clean/clean-T2351.webp +0 -0
  143. package/homebridge-ui/public/assets/devices/clean/clean-T2352.webp +0 -0
  144. package/homebridge-ui/public/assets/devices/clean/clean-T2353.webp +0 -0
  145. package/homebridge-ui/public/assets/devices/life/life-T8L00.webp +0 -0
  146. package/homebridge-ui/public/assets/devices/life/life-T8L01.webp +0 -0
  147. package/homebridge-ui/public/assets/devices/life/life-T8L02.webp +0 -0
  148. package/homebridge-ui/public/assets/devices/life/life-T8L04.webp +0 -0
  149. package/homebridge-ui/public/assets/devices/life/life-T8L10.webp +0 -0
  150. package/homebridge-ui/public/assets/devices/life/life-T8L20.webp +0 -0
  151. package/homebridge-ui/public/assets/devices/life/life-T8L30.webp +0 -0
  152. package/homebridge-ui/public/assets/devices/life/life-T8L40.webp +0 -0
  153. package/homebridge-ui/public/assets/devices/mower/mower-T2801.webp +0 -0
  154. package/homebridge-ui/public/assets/devices/mower/mower-T280B.webp +0 -0
  155. package/homebridge-ui/public/assets/devices/mower/mower-T2880.webp +0 -0
  156. package/homebridge-ui/public/assets/devices/security/security-T7400.webp +0 -0
  157. package/homebridge-ui/public/assets/devices/security/security-T7401.webp +0 -0
  158. package/homebridge-ui/public/assets/devices/security/security-T8001.webp +0 -0
  159. package/homebridge-ui/public/assets/devices/security/security-T8002.webp +0 -0
  160. package/homebridge-ui/public/assets/devices/security/security-T8010.webp +0 -0
  161. package/homebridge-ui/public/assets/devices/security/security-T8021.webp +0 -0
  162. package/homebridge-ui/public/assets/devices/security/security-T8023.webp +0 -0
  163. package/homebridge-ui/public/assets/devices/security/security-T8025.webp +0 -0
  164. package/homebridge-ui/public/assets/devices/security/security-T8030.webp +0 -0
  165. package/homebridge-ui/public/assets/devices/security/security-T8110.webp +0 -0
  166. package/homebridge-ui/public/assets/devices/security/security-T8111.webp +0 -0
  167. package/homebridge-ui/public/assets/devices/security/security-T8112.webp +0 -0
  168. package/homebridge-ui/public/assets/devices/security/security-T8113.webp +0 -0
  169. package/homebridge-ui/public/assets/devices/security/security-T8114.webp +0 -0
  170. package/homebridge-ui/public/assets/devices/security/security-T8122.webp +0 -0
  171. package/homebridge-ui/public/assets/devices/security/security-T8123.webp +0 -0
  172. package/homebridge-ui/public/assets/devices/security/security-T8124.webp +0 -0
  173. package/homebridge-ui/public/assets/devices/security/security-T8130.webp +0 -0
  174. package/homebridge-ui/public/assets/devices/security/security-T8131.webp +0 -0
  175. package/homebridge-ui/public/assets/devices/security/security-T8134.webp +0 -0
  176. package/homebridge-ui/public/assets/devices/security/security-T8140.webp +0 -0
  177. package/homebridge-ui/public/assets/devices/security/security-T8141.webp +0 -0
  178. package/homebridge-ui/public/assets/devices/security/security-T8150.webp +0 -0
  179. package/homebridge-ui/public/assets/devices/security/security-T8151.webp +0 -0
  180. package/homebridge-ui/public/assets/devices/security/security-T8152.webp +0 -0
  181. package/homebridge-ui/public/assets/devices/security/security-T8153.webp +0 -0
  182. package/homebridge-ui/public/assets/devices/security/security-T8160.webp +0 -0
  183. package/homebridge-ui/public/assets/devices/security/security-T8161.webp +0 -0
  184. package/homebridge-ui/public/assets/devices/security/security-T8162.webp +0 -0
  185. package/homebridge-ui/public/assets/devices/security/security-T8170.webp +0 -0
  186. package/homebridge-ui/public/assets/devices/security/security-T8171.webp +0 -0
  187. package/homebridge-ui/public/assets/devices/security/security-T8172.webp +0 -0
  188. package/homebridge-ui/public/assets/devices/security/security-T8173.webp +0 -0
  189. package/homebridge-ui/public/assets/devices/security/security-T8200.webp +0 -0
  190. package/homebridge-ui/public/assets/devices/security/security-T8200X.webp +0 -0
  191. package/homebridge-ui/public/assets/devices/security/security-T8201.webp +0 -0
  192. package/homebridge-ui/public/assets/devices/security/security-T8203.webp +0 -0
  193. package/homebridge-ui/public/assets/devices/security/security-T8210.webp +0 -0
  194. package/homebridge-ui/public/assets/devices/security/security-T8213.webp +0 -0
  195. package/homebridge-ui/public/assets/devices/security/security-T8214.webp +0 -0
  196. package/homebridge-ui/public/assets/devices/security/security-T8222.webp +0 -0
  197. package/homebridge-ui/public/assets/devices/security/security-T8400.webp +0 -0
  198. package/homebridge-ui/public/assets/devices/security/security-T8401.webp +0 -0
  199. package/homebridge-ui/public/assets/devices/security/security-T8410.webp +0 -0
  200. package/homebridge-ui/public/assets/devices/security/security-T8411.webp +0 -0
  201. package/homebridge-ui/public/assets/devices/security/security-T8414.webp +0 -0
  202. package/homebridge-ui/public/assets/devices/security/security-T8416.webp +0 -0
  203. package/homebridge-ui/public/assets/devices/security/security-T8417.webp +0 -0
  204. package/homebridge-ui/public/assets/devices/security/security-T8419.webp +0 -0
  205. package/homebridge-ui/public/assets/devices/security/security-T8419N.webp +0 -0
  206. package/homebridge-ui/public/assets/devices/security/security-T8420.webp +0 -0
  207. package/homebridge-ui/public/assets/devices/security/security-T8420X.webp +0 -0
  208. package/homebridge-ui/public/assets/devices/security/security-T8422.webp +0 -0
  209. package/homebridge-ui/public/assets/devices/security/security-T8423.webp +0 -0
  210. package/homebridge-ui/public/assets/devices/security/security-T8424.webp +0 -0
  211. package/homebridge-ui/public/assets/devices/security/security-T8425.webp +0 -0
  212. package/homebridge-ui/public/assets/devices/security/security-T8441.webp +0 -0
  213. package/homebridge-ui/public/assets/devices/security/security-T8442.webp +0 -0
  214. package/homebridge-ui/public/assets/devices/security/security-T8452.webp +0 -0
  215. package/homebridge-ui/public/assets/devices/security/security-T84A0.webp +0 -0
  216. package/homebridge-ui/public/assets/devices/security/security-T84A1.webp +0 -0
  217. package/homebridge-ui/public/assets/devices/security/security-T8500.webp +0 -0
  218. package/homebridge-ui/public/assets/devices/security/security-T8502.webp +0 -0
  219. package/homebridge-ui/public/assets/devices/security/security-T8503.webp +0 -0
  220. package/homebridge-ui/public/assets/devices/security/security-T8504.webp +0 -0
  221. package/homebridge-ui/public/assets/devices/security/security-T8506.webp +0 -0
  222. package/homebridge-ui/public/assets/devices/security/security-T8510.webp +0 -0
  223. package/homebridge-ui/public/assets/devices/security/security-T8510P.webp +0 -0
  224. package/homebridge-ui/public/assets/devices/security/security-T8520.webp +0 -0
  225. package/homebridge-ui/public/assets/devices/security/security-T8520P.webp +0 -0
  226. package/homebridge-ui/public/assets/devices/security/security-T8530.webp +0 -0
  227. package/homebridge-ui/public/assets/devices/security/security-T8531.webp +0 -0
  228. package/homebridge-ui/public/assets/devices/security/security-T85D0.webp +0 -0
  229. package/homebridge-ui/public/assets/devices/security/security-T85L0.webp +0 -0
  230. package/homebridge-ui/public/assets/devices/security/security-T85P0.webp +0 -0
  231. package/homebridge-ui/public/assets/devices/security/security-T85V0.webp +0 -0
  232. package/homebridge-ui/public/assets/devices/security/security-T8600.webp +0 -0
  233. package/homebridge-ui/public/assets/devices/security/security-T86P2.webp +0 -0
  234. package/homebridge-ui/public/assets/devices/security/security-T8790.webp +0 -0
  235. package/homebridge-ui/public/assets/devices/security/security-T87B0.webp +0 -0
  236. package/homebridge-ui/public/assets/devices/security/security-T87B2.webp +0 -0
  237. package/homebridge-ui/public/assets/devices/security/security-T8900.webp +0 -0
  238. package/homebridge-ui/public/assets/devices/security/security-T8910.webp +0 -0
  239. package/homebridge-ui/public/assets/devices/security/security-T8920.webp +0 -0
  240. package/homebridge-ui/public/assets/devices/security/security-T8960.webp +0 -0
  241. package/homebridge-ui/public/assets/devices/security/security-T8B0.webp +0 -0
  242. package/homebridge-ui/public/assets/devices/security/security-T8E00.webp +0 -0
  243. package/homebridge-ui/public/assets/devices/security/security-T8N00.webp +0 -0
  244. package/homebridge-ui/public/assets/devices/security/security-T8W11C.webp +0 -0
  245. package/homebridge-ui/public/assets/devices/security/security-T90F0.webp +0 -0
  246. package/homebridge-ui/public/assets/devices/security/security-T90M0.webp +0 -0
  247. package/homebridge-ui/public/assets/devices/security/security-T90R0.webp +0 -0
  248. package/homebridge-ui/public/assets/devices/security/security-T90S0.webp +0 -0
  249. package/homebridge-ui/public/assets/icons/inventory.svg +1 -1
  250. package/homebridge-ui/public/assets/icons/troubleshoot.svg +1 -0
  251. package/homebridge-ui/public/assets/icons/warning.svg +1 -1
  252. package/homebridge-ui/public/assets/logo-dark.svg +10 -0
  253. package/homebridge-ui/public/assets/logo.svg +10 -0
  254. package/homebridge-ui/public/i18n/en.json +218 -0
  255. package/homebridge-ui/public/i18n/fr.json +218 -0
  256. package/homebridge-ui/public/index.html +377 -28
  257. package/homebridge-ui/public/js/app.js +1065 -0
  258. package/homebridge-ui/public/js/dashboard.js +385 -0
  259. package/homebridge-ui/public/js/legacy-settings.js +37 -0
  260. package/homebridge-ui/public/js/profile-wizard.js +68 -0
  261. package/homebridge-ui/server.js +2 -1393
  262. package/i18n/runtime/en.json +100 -0
  263. package/media/Snapshot-Unavailable.jpg +0 -0
  264. package/media/Snapshot-black.jpg +0 -0
  265. package/media/camera-disabled.jpg +0 -0
  266. package/media/camera-offline.jpg +0 -0
  267. package/package.json +29 -24
  268. package/.claude/CLAUDE.md +0 -175
  269. package/.claude/PRD.md +0 -241
  270. package/.claude/docs/debug-recording-lessons.md +0 -91
  271. package/.claude/docs/hksv-recording-fix.md +0 -160
  272. package/.claude/skills/architect/SKILL.md +0 -76
  273. package/.claude/skills/developer/SKILL.md +0 -59
  274. package/.claude/skills/new-device-support/SKILL.md +0 -212
  275. package/.claude/skills/new-device-support/check-device.mjs +0 -363
  276. package/.claude/skills/new-device-support/map-properties.mjs +0 -450
  277. package/.claude/skills/new-device-support/verify-device.mjs +0 -272
  278. package/.claude/skills/planner/SKILL.md +0 -100
  279. package/.claude/skills/qa/SKILL.md +0 -79
  280. package/.claude/skills/support/SKILL.md +0 -175
  281. package/.prettierignore +0 -7
  282. package/.prettierrc.json +0 -7
  283. package/FUNDING.yml +0 -1
  284. package/dist/accessories/AutoSyncStationAccessory.js +0 -157
  285. package/dist/accessories/AutoSyncStationAccessory.js.map +0 -1
  286. package/dist/accessories/BaseAccessory.js +0 -261
  287. package/dist/accessories/BaseAccessory.js.map +0 -1
  288. package/dist/accessories/CameraAccessory.js +0 -459
  289. package/dist/accessories/CameraAccessory.js.map +0 -1
  290. package/dist/accessories/Device.js +0 -65
  291. package/dist/accessories/Device.js.map +0 -1
  292. package/dist/accessories/EntrySensorAccessory.js +0 -48
  293. package/dist/accessories/EntrySensorAccessory.js.map +0 -1
  294. package/dist/accessories/GarageDoorAccessory.js +0 -160
  295. package/dist/accessories/GarageDoorAccessory.js.map +0 -1
  296. package/dist/accessories/LockAccessory.js +0 -142
  297. package/dist/accessories/LockAccessory.js.map +0 -1
  298. package/dist/accessories/MotionSensorAccessory.js +0 -48
  299. package/dist/accessories/MotionSensorAccessory.js.map +0 -1
  300. package/dist/accessories/SmartDropAccessory.js +0 -141
  301. package/dist/accessories/SmartDropAccessory.js.map +0 -1
  302. package/dist/accessories/StationAccessory.js +0 -373
  303. package/dist/accessories/StationAccessory.js.map +0 -1
  304. package/dist/config.js +0 -26
  305. package/dist/config.js.map +0 -1
  306. package/dist/controller/DebugRecordingManager.js +0 -402
  307. package/dist/controller/DebugRecordingManager.js.map +0 -1
  308. package/dist/controller/LocalLivestreamManager.js +0 -371
  309. package/dist/controller/LocalLivestreamManager.js.map +0 -1
  310. package/dist/controller/recordingDelegate.js +0 -252
  311. package/dist/controller/recordingDelegate.js.map +0 -1
  312. package/dist/controller/snapshotDelegate.js +0 -348
  313. package/dist/controller/snapshotDelegate.js.map +0 -1
  314. package/dist/controller/streamingDelegate.js +0 -389
  315. package/dist/controller/streamingDelegate.js.map +0 -1
  316. package/dist/interfaces.js +0 -2
  317. package/dist/interfaces.js.map +0 -1
  318. package/dist/media/Snapshot-Unavailable.png +0 -0
  319. package/dist/media/Snapshot-Unavailable.xcf +0 -0
  320. package/dist/media/Snapshot-black.png +0 -0
  321. package/dist/media/camera-disabled.png +0 -0
  322. package/dist/media/camera-offline.png +0 -0
  323. package/dist/media/media/Snapshot-Unavailable.png +0 -0
  324. package/dist/media/media/Snapshot-Unavailable.xcf +0 -0
  325. package/dist/media/media/Snapshot-black.png +0 -0
  326. package/dist/media/media/camera-disabled.png +0 -0
  327. package/dist/media/media/camera-offline.png +0 -0
  328. package/dist/utils/Talkback.js +0 -162
  329. package/dist/utils/Talkback.js.map +0 -1
  330. package/dist/utils/accessoriesStore.js +0 -210
  331. package/dist/utils/accessoriesStore.js.map +0 -1
  332. package/dist/utils/configTypes.js +0 -38
  333. package/dist/utils/configTypes.js.map +0 -1
  334. package/dist/utils/ffmpeg.js +0 -1111
  335. package/dist/utils/ffmpeg.js.map +0 -1
  336. package/dist/utils/interfaces.js +0 -8
  337. package/dist/utils/interfaces.js.map +0 -1
  338. package/dist/utils/utils.js +0 -224
  339. package/dist/utils/utils.js.map +0 -1
  340. package/dist/version.js +0 -2
  341. package/dist/version.js.map +0 -1
  342. package/homebridge-eufy-security.png +0 -0
  343. package/homebridge-ui/diagnosticsCrypto.js +0 -112
  344. package/homebridge-ui/public/app.js +0 -237
  345. package/homebridge-ui/public/assets/devices/4g_lte_cam_s330_large.png +0 -0
  346. package/homebridge-ui/public/assets/devices/4g_lte_starlight_large.jpg +0 -0
  347. package/homebridge-ui/public/assets/devices/BATTERY_DOORBELL_C30.png +0 -0
  348. package/homebridge-ui/public/assets/devices/BATTERY_DOORBELL_C31.png +0 -0
  349. package/homebridge-ui/public/assets/devices/batterydoorbell1080p_large.jpg +0 -0
  350. package/homebridge-ui/public/assets/devices/batterydoorbell2k_large.png +0 -0
  351. package/homebridge-ui/public/assets/devices/batterydoorbell2kdual_large.jpg +0 -0
  352. package/homebridge-ui/public/assets/devices/batterydoorbell_e340_large.png +0 -0
  353. package/homebridge-ui/public/assets/devices/eufy-security-client.png +0 -0
  354. package/homebridge-ui/public/assets/devices/eufyCamS4_large.png +0 -0
  355. package/homebridge-ui/public/assets/devices/eufycam2_large.png +0 -0
  356. package/homebridge-ui/public/assets/devices/eufycam2c_large.jpg +0 -0
  357. package/homebridge-ui/public/assets/devices/eufycam2cpro_large.jpg +0 -0
  358. package/homebridge-ui/public/assets/devices/eufycam2pro_large.jpg +0 -0
  359. package/homebridge-ui/public/assets/devices/eufycam3_large.jpg +0 -0
  360. package/homebridge-ui/public/assets/devices/eufycam3c_large.jpg +0 -0
  361. package/homebridge-ui/public/assets/devices/eufycam3pro_large.png +0 -0
  362. package/homebridge-ui/public/assets/devices/eufycam_large.jpg +0 -0
  363. package/homebridge-ui/public/assets/devices/eufycame330_large.jpg +0 -0
  364. package/homebridge-ui/public/assets/devices/flood_freeze_sensor_e20_t90F0_large.png +0 -0
  365. package/homebridge-ui/public/assets/devices/floodlight2_large.jpg +0 -0
  366. package/homebridge-ui/public/assets/devices/floodlight2pro_large.jpg +0 -0
  367. package/homebridge-ui/public/assets/devices/floodlight_large.jpg +0 -0
  368. package/homebridge-ui/public/assets/devices/floodlightcame340_large.jpg +0 -0
  369. package/homebridge-ui/public/assets/devices/garage_camera_t8452_large.png +0 -0
  370. package/homebridge-ui/public/assets/devices/homebase2_large.png +0 -0
  371. package/homebridge-ui/public/assets/devices/homebase3_large.png +0 -0
  372. package/homebridge-ui/public/assets/devices/homebase_large.jpg +0 -0
  373. package/homebridge-ui/public/assets/devices/homebasemini_large.png +0 -0
  374. package/homebridge-ui/public/assets/devices/indoorcamC210_large.png +0 -0
  375. package/homebridge-ui/public/assets/devices/indoorcamC220_large.png +0 -0
  376. package/homebridge-ui/public/assets/devices/indoorcamC220_t8419N_large.png +0 -0
  377. package/homebridge-ui/public/assets/devices/indoorcamE30_large.png +0 -0
  378. package/homebridge-ui/public/assets/devices/indoorcamc120_large.png +0 -0
  379. package/homebridge-ui/public/assets/devices/indoorcammini_large.jpg +0 -0
  380. package/homebridge-ui/public/assets/devices/indoorcamp24_large.png +0 -0
  381. package/homebridge-ui/public/assets/devices/indoorcams350_large.png +0 -0
  382. package/homebridge-ui/public/assets/devices/keypad_large.png +0 -0
  383. package/homebridge-ui/public/assets/devices/minibase_chime_T8023_large.jpg +0 -0
  384. package/homebridge-ui/public/assets/devices/motionsensor_large.png +0 -0
  385. package/homebridge-ui/public/assets/devices/nvr_s4_max_T8N00_large.png +0 -0
  386. package/homebridge-ui/public/assets/devices/pir_sensor_e20_large.png +0 -0
  387. package/homebridge-ui/public/assets/devices/poe_bullet_ptz_cam_s4_T8E00_large.png +0 -0
  388. package/homebridge-ui/public/assets/devices/sensor_large.png +0 -0
  389. package/homebridge-ui/public/assets/devices/siren_sensor_e20_large.png +0 -0
  390. package/homebridge-ui/public/assets/devices/smartdrop_t8790_large.png +0 -0
  391. package/homebridge-ui/public/assets/devices/smartlock_t8500_large.png +0 -0
  392. package/homebridge-ui/public/assets/devices/smartlock_t8500_wifibridge_large.jpg +0 -0
  393. package/homebridge-ui/public/assets/devices/smartlock_t8503_large.png +0 -0
  394. package/homebridge-ui/public/assets/devices/smartlock_t8504_large.jpg +0 -0
  395. package/homebridge-ui/public/assets/devices/smartlock_t8510P_t8520P_large.png +0 -0
  396. package/homebridge-ui/public/assets/devices/smartlock_t85D0_large.png +0 -0
  397. package/homebridge-ui/public/assets/devices/smartlock_t85L0_large.png +0 -0
  398. package/homebridge-ui/public/assets/devices/smartlock_t85P0_large.png +0 -0
  399. package/homebridge-ui/public/assets/devices/smartlock_t85V0_large.png +0 -0
  400. package/homebridge-ui/public/assets/devices/smartlock_touch_and_wifi_t8502_large.png +0 -0
  401. package/homebridge-ui/public/assets/devices/smartlock_touch_and_wifi_t8506_large.png +0 -0
  402. package/homebridge-ui/public/assets/devices/smartlock_touch_and_wifi_t8520_large.png +0 -0
  403. package/homebridge-ui/public/assets/devices/smartlock_touch_t8510_large.png +0 -0
  404. package/homebridge-ui/public/assets/devices/smartlock_touch_t8510_wifibridge_large.jpg +0 -0
  405. package/homebridge-ui/public/assets/devices/smartlock_video_t8530_large.png +0 -0
  406. package/homebridge-ui/public/assets/devices/smartlock_video_t8531_large.png +0 -0
  407. package/homebridge-ui/public/assets/devices/smartlockwifibridge_t8021_large.jpg +0 -0
  408. package/homebridge-ui/public/assets/devices/smartsafe_s10_t7400_large.png +0 -0
  409. package/homebridge-ui/public/assets/devices/smartsafe_s12_t7401_large.png +0 -0
  410. package/homebridge-ui/public/assets/devices/smarttrack_card_t87B2_large.png +0 -0
  411. package/homebridge-ui/public/assets/devices/smarttrack_link_t87B0_large.png +0 -0
  412. package/homebridge-ui/public/assets/devices/smoke_alarm_e10_t90S0_large.png +0 -0
  413. package/homebridge-ui/public/assets/devices/solocamc210_large.jpg +0 -0
  414. package/homebridge-ui/public/assets/devices/solocamc35_large.png +0 -0
  415. package/homebridge-ui/public/assets/devices/solocame20_large.jpg +0 -0
  416. package/homebridge-ui/public/assets/devices/solocame30_large.png +0 -0
  417. package/homebridge-ui/public/assets/devices/solocame40_large.jpg +0 -0
  418. package/homebridge-ui/public/assets/devices/solocame42_large.png +0 -0
  419. package/homebridge-ui/public/assets/devices/solocaml20_large.jpg +0 -0
  420. package/homebridge-ui/public/assets/devices/solocams220_large.jpg +0 -0
  421. package/homebridge-ui/public/assets/devices/solocams340_large.png +0 -0
  422. package/homebridge-ui/public/assets/devices/solocams40_large.jpg +0 -0
  423. package/homebridge-ui/public/assets/devices/soloindoorcamc24_large.jpg +0 -0
  424. package/homebridge-ui/public/assets/devices/solooutdoorcamc22_large.png +0 -0
  425. package/homebridge-ui/public/assets/devices/solooutdoorcamc24_large.jpg +0 -0
  426. package/homebridge-ui/public/assets/devices/unknown.png +0 -0
  427. package/homebridge-ui/public/assets/devices/walllight_s100_large.png +0 -0
  428. package/homebridge-ui/public/assets/devices/walllight_s120_large.png +0 -0
  429. package/homebridge-ui/public/assets/devices/water_freeze_sensor_t8920_large.png +0 -0
  430. package/homebridge-ui/public/assets/devices/wireddoorbell1080p_large.jpg +0 -0
  431. package/homebridge-ui/public/assets/devices/wireddoorbell2k_large.png +0 -0
  432. package/homebridge-ui/public/assets/devices/wireddoorbelldual_large.jpg +0 -0
  433. package/homebridge-ui/public/assets/icons/attach.svg +0 -1
  434. package/homebridge-ui/public/assets/icons/battery_0.svg +0 -1
  435. package/homebridge-ui/public/assets/icons/battery_1.svg +0 -1
  436. package/homebridge-ui/public/assets/icons/battery_2.svg +0 -1
  437. package/homebridge-ui/public/assets/icons/battery_3.svg +0 -1
  438. package/homebridge-ui/public/assets/icons/battery_4.svg +0 -1
  439. package/homebridge-ui/public/assets/icons/battery_5.svg +0 -1
  440. package/homebridge-ui/public/assets/icons/battery_6.svg +0 -1
  441. package/homebridge-ui/public/assets/icons/copy.svg +0 -1
  442. package/homebridge-ui/public/assets/icons/delete.svg +0 -1
  443. package/homebridge-ui/public/assets/icons/download.svg +0 -1
  444. package/homebridge-ui/public/assets/icons/info.svg +0 -1
  445. package/homebridge-ui/public/assets/icons/refresh.svg +0 -1
  446. package/homebridge-ui/public/assets/icons/satellite_alt.svg +0 -1
  447. package/homebridge-ui/public/assets/icons/solar_power.svg +0 -1
  448. package/homebridge-ui/public/components/device-card.js +0 -174
  449. package/homebridge-ui/public/components/guard-modes.js +0 -88
  450. package/homebridge-ui/public/components/number-input.js +0 -121
  451. package/homebridge-ui/public/components/select.js +0 -73
  452. package/homebridge-ui/public/components/toggle.js +0 -68
  453. package/homebridge-ui/public/services/api.js +0 -250
  454. package/homebridge-ui/public/services/config.js +0 -144
  455. package/homebridge-ui/public/style.css +0 -818
  456. package/homebridge-ui/public/utils/countries.js +0 -73
  457. package/homebridge-ui/public/utils/device-images.js +0 -102
  458. package/homebridge-ui/public/utils/helpers.js +0 -115
  459. package/homebridge-ui/public/views/dashboard.js +0 -226
  460. package/homebridge-ui/public/views/device-detail.js +0 -654
  461. package/homebridge-ui/public/views/diagnostics.js +0 -402
  462. package/homebridge-ui/public/views/login.js +0 -650
  463. package/homebridge-ui/public/views/recordings.js +0 -319
  464. package/homebridge-ui/public/views/settings.js +0 -197
  465. package/homebridge-ui/public/views/unsupported-detail.js +0 -296
  466. package/media/Snapshot-Unavailable.png +0 -0
  467. package/media/Snapshot-Unavailable.xcf +0 -0
  468. package/media/Snapshot-black.png +0 -0
  469. package/media/camera-disabled.png +0 -0
  470. package/media/camera-offline.png +0 -0
  471. package/scripts/decrypt-diagnostics.mjs +0 -257
@@ -0,0 +1,385 @@
1
+ (function dashboardModule(global) {
2
+ function escapeHtml(value) {
3
+ return String(value)
4
+ .replaceAll('&', '&')
5
+ .replaceAll('<', '&lt;')
6
+ .replaceAll('>', '&gt;')
7
+ .replaceAll('"', '&quot;')
8
+ .replaceAll("'", '&#039;');
9
+ }
10
+
11
+ function preferenceControl(device, key, preference, messages) {
12
+ const labels = {
13
+ represented: messages.preferenceRepresented,
14
+ audio: messages.preferenceAudio,
15
+ snapshotMode: messages.preferenceSnapshotMode,
16
+ };
17
+ const represented = preference.represented;
18
+ const dependent = key === 'represented' ? '' : ` data-requires-representation${represented ? '' : ' hidden'}`;
19
+ if (key === 'snapshotMode') {
20
+ const descriptions = {
21
+ Cloud: messages.snapshotModeCloudDescription,
22
+ Live: messages.snapshotModeLiveDescription,
23
+ Refresh: messages.snapshotModeRefreshDescription,
24
+ };
25
+ return `<div class="snapshot-setting" data-setting${dependent}><span class="setting-label">${escapeHtml(labels[key])}</span><div class="snapshot-segments" role="radiogroup" aria-label="${escapeHtml(labels[key])}">${['Cloud', 'Live', 'Refresh'].map((mode) => `<label class="snapshot-segment"><input type="radio" name="snapshot-${escapeHtml(device.serial)}" value="${mode}" data-preference="${key}" data-serial="${escapeHtml(device.serial)}" data-original="${escapeHtml(preference.snapshotMode)}" data-description="${escapeHtml(descriptions[mode])}"${preference.snapshotMode === mode ? ' checked' : ''}><span>${mode}</span></label>`).join('')}</div><p class="snapshot-description" data-snapshot-description>${escapeHtml(descriptions[preference.snapshotMode])}</p><p class="snapshot-help">${escapeHtml(messages.snapshotModeHelp)}</p></div>`;
26
+ }
27
+ return `<label class="toggle" data-setting${dependent}><input type="checkbox" data-preference="${key}" data-serial="${escapeHtml(device.serial)}" data-original="${String(preference[key])}"${preference[key] ? ' checked' : ''}><span>${escapeHtml(labels[key])}</span></label>`;
28
+ }
29
+
30
+ function markPendingPreference(control, key, value) {
31
+ if (!control?.classList || control.dataset.original === undefined) return;
32
+ const current = key === 'snapshotMode' ? String(value) : String(Boolean(value));
33
+ const setting = control.closest('[data-setting]');
34
+ const changed = current !== control.dataset.original;
35
+ if (key === 'snapshotMode') {
36
+ setting.querySelectorAll('[data-preference="snapshotMode"]').forEach((entry) => {
37
+ entry.classList.remove('preference-control-changed');
38
+ });
39
+ }
40
+ control.classList.toggle('preference-control-changed', changed);
41
+ setting?.classList.toggle('preference-changed', changed);
42
+ const tile = control.closest('.device-tile');
43
+ tile?.classList.toggle('device-tile-changed', Boolean(tile.querySelector('.preference-control-changed')));
44
+ }
45
+
46
+ /**
47
+ * The eight battery icons the shipped set draws, from empty to full.
48
+ *
49
+ * Ordered, because the level picks one by its position: eight steps of twelve and a half percent each.
50
+ */
51
+ const BATTERY_STEPS = [
52
+ 'battery_0',
53
+ 'battery_1',
54
+ 'battery_2',
55
+ 'battery_3',
56
+ 'battery_4',
57
+ 'battery_5',
58
+ 'battery_6',
59
+ 'battery_full',
60
+ ];
61
+
62
+ /**
63
+ * The battery badge a device gets, where it reports a level, and nothing where it does not.
64
+ *
65
+ * The icon carries the level and the exact percentage goes to the label: a tile is read at a glance, and a
66
+ * number on every tile would be read deliberately or not at all. A device on mains power reports no level, and
67
+ * neither does one nothing is observing, so both are left without a badge rather than shown an empty one.
68
+ */
69
+ function batteryBadge(device, messages) {
70
+ if (typeof device.battery !== 'number') {
71
+ return undefined;
72
+ }
73
+ const step = Math.floor(device.battery / (100 / BATTERY_STEPS.length));
74
+ const icon = BATTERY_STEPS[Math.min(BATTERY_STEPS.length - 1, Math.max(0, step))];
75
+ const label = (messages.deviceBattery ?? '').replace('{level}', String(Math.round(device.battery)));
76
+ return label ? { icon, label, variant: 'battery' } : undefined;
77
+ }
78
+
79
+ /**
80
+ * The one thing worth saying about a device in place of its model, or nothing.
81
+ *
82
+ * Ordered by what displaces what: a device nothing can reach says nothing else useful about itself, a camera
83
+ * reports itself switched off only while it is reachable, a device withheld from HomeKit is the user's own
84
+ * decision rather than a fault, and a camera that reports itself on says so last, because that is the state a
85
+ * user assumes until one of the others contradicts it. A device that reports none of them keeps its model,
86
+ * which is what identifies it when there is nothing to report, and so does one whose catalog carries no
87
+ * wording for the status it has.
88
+ */
89
+ function deviceStatus(device, rank, messages) {
90
+ const [key, tone] =
91
+ device.availability === 'unavailable'
92
+ ? ['deviceStatusUnreachable', 'fault']
93
+ : device.enabled === false
94
+ ? ['deviceStatusSwitchedOff', 'alert']
95
+ : rank === 1
96
+ ? ['deviceStatusHidden', 'quiet']
97
+ : device.enabled === true
98
+ ? ['deviceStatusOnline', 'live']
99
+ : [];
100
+ const label = key ? messages[key] : undefined;
101
+ return label ? { label, tone } : undefined;
102
+ }
103
+
104
+ function renderDevices(devices, config, messages, deviceGroups) {
105
+ const preferences = config.entityPreferences ?? {};
106
+ deviceGroups.innerHTML = ['security', 'life', 'clean']
107
+ .map((category) => {
108
+ const categoryDevices = devices
109
+ .filter((device) => device.category === category)
110
+ .map((device) => ({
111
+ device,
112
+ rank: device.diagnosticOnly
113
+ ? 2
114
+ : preferences[device.serial]?.represented === false
115
+ ? 1
116
+ : 0,
117
+ }))
118
+ .sort(
119
+ (left, right) =>
120
+ left.rank - right.rank ||
121
+ left.device.deviceClass.localeCompare(right.device.deviceClass) ||
122
+ left.device.modelName.localeCompare(right.device.modelName) ||
123
+ left.device.name.localeCompare(right.device.name),
124
+ );
125
+ if (categoryDevices.length === 0) return '';
126
+ const tiles = categoryDevices
127
+ .map(({ device, rank }) => {
128
+ const badges = [
129
+ device.diagnosticOnly ? { icon: 'troubleshoot', label: messages.diagnosticOnly } : undefined,
130
+ batteryBadge(device, messages),
131
+ ].filter(Boolean);
132
+ const artwork = device.artwork
133
+ ? `<img src="${escapeHtml(device.artwork)}" alt="" loading="lazy" data-device-artwork>`
134
+ : '';
135
+ const status = deviceStatus(device, rank, messages);
136
+ const secondLine = status
137
+ ? `<p class="device-status device-status-${status.tone}"><span class="device-status-dot" aria-hidden="true"></span><span class="device-status-label">${escapeHtml(status.label)}</span></p>`
138
+ : `<p>${escapeHtml(device.modelName)}</p>`;
139
+ const tile = `
140
+ <div class="device-art" aria-hidden="true"><img class="device-class-icon" src="assets/icons/inventory.svg" alt=""><span>${escapeHtml(device.deviceClass)}</span>${artwork}</div>
141
+ <div class="device-copy"><h3>${escapeHtml(device.name)}</h3>${secondLine}</div>
142
+ <div class="device-badges">${badges.map(({ icon, label, variant }) => `<span class="device-badge device-badge-${variant ?? icon}" role="img" tabindex="0" aria-label="${escapeHtml(label)}" data-tooltip="${escapeHtml(label)}"><img src="assets/icons/${icon}.svg" alt=""></span>`).join('')}</div>`;
143
+ const disabledClass = rank === 1 ? ' device-tile-disabled' : '';
144
+ return `<article class="device-tile${disabledClass}" data-category="${category}" data-rank="${rank}" data-serial="${escapeHtml(device.serial)}" data-device-class="${escapeHtml(device.deviceClass)}"><button class="device-summary device-open-control" type="button" aria-haspopup="dialog">${tile}</button></article>`;
145
+ })
146
+ .join('');
147
+ const categoryKey = `category${category[0].toUpperCase()}${category.slice(1)}`;
148
+ return `<section class="device-group"><h2>${escapeHtml(messages[categoryKey])}</h2><div class="device-grid">${tiles}</div></section>`;
149
+ })
150
+ .join('');
151
+ }
152
+
153
+ /**
154
+ * Shows the notice that this page is answering from a superseded build, and only where one is proven.
155
+ *
156
+ * The version is named because it is the one fact a reader cannot get anywhere else on the page. The action is
157
+ * offered only where ending the older build would replace it; elsewhere the copy asks for the restart instead,
158
+ * so nobody is given a button that cannot work.
159
+ */
160
+ /*
161
+ * What each adapter becomes in HomeKit, in words. Keyed by the adapter the projection names, because the name a
162
+ * user reads belongs here rather than in the code that decides which adapters apply.
163
+ */
164
+ const HOMEKIT_SERVICE_LABELS = {
165
+ 'arming.security-system': 'serviceSecuritySystem',
166
+ 'camera.streaming': 'serviceCamera',
167
+ 'camera.controls': 'serviceCameraSwitches',
168
+ 'contact.sensor': 'serviceContact',
169
+ 'doorbell.press': 'serviceDoorbell',
170
+ 'lock.mechanism': 'serviceLock',
171
+ 'motion.sensor': 'serviceMotion',
172
+ 'siren.test': 'serviceSiren',
173
+ 'smart-light.lightbulb': 'serviceLight',
174
+ };
175
+
176
+ /**
177
+ * Names what the device becomes in HomeKit, above the switch that decides whether it becomes it at all.
178
+ *
179
+ * "Represent in HomeKit" says that something appears and never what, which is least obvious exactly where it
180
+ * matters most: a station appears as an alarm, and a camera as several things at once. An adapter this build has
181
+ * no words for is left out rather than shown as its key, and a device that names none — an answer from a build
182
+ * that predates this line — costs its tile the line rather than the whole dashboard.
183
+ */
184
+ function representationLine(device, messages) {
185
+ const named = (device.representation ?? []).map((key) => messages[HOMEKIT_SERVICE_LABELS[key]]).filter(Boolean);
186
+ if (named.length === 0) return '';
187
+ return `<p class="preference-representation">${escapeHtml(`${messages.representationLabel} ${named.join(', ')}`)}</p>`;
188
+ }
189
+
190
+ function renderUpdatePending(result, messages, elements) {
191
+ if (!elements.updatePending) {
192
+ return;
193
+ }
194
+ const pending = result.runningVersion !== undefined;
195
+ elements.updatePending.hidden = !pending;
196
+ if (!pending) {
197
+ return;
198
+ }
199
+ elements.updatePendingSummary.textContent = result.restartable
200
+ ? messages.updatePendingSummary
201
+ : messages.updatePendingManual;
202
+ elements.updatePendingVersion.textContent = `${messages.updatePendingVersionLabel} ${result.runningVersion}`;
203
+ elements.updateRestart.hidden = !result.restartable;
204
+ }
205
+
206
+ /*
207
+ * The states a diagnostic run can shed light on. Sign-in, another process holding the session, and a pending
208
+ * restart each have their own remedy stated on the page, so sending those to the wizard would be a detour.
209
+ */
210
+ const DIAGNOSABLE = new Set(['degraded', 'incomplete', 'missing', 'stale']);
211
+
212
+ function render(result, config, messages, elements) {
213
+ const suffix = result.state
214
+ .split('-')
215
+ .map((part) => part[0].toUpperCase() + part.slice(1))
216
+ .join('');
217
+ elements.title.textContent = messages[`dashboard${suffix}Title`] ?? messages.dashboardIncompleteTitle;
218
+ elements.summary.textContent = messages[`dashboard${suffix}Summary`] ?? messages.dashboardIncompleteSummary;
219
+ elements.dashboard.dataset.state = result.state;
220
+ // Offered rather than announced: a run is worth suggesting only where the state is one a run explains.
221
+ if (elements.diagnose) elements.diagnose.hidden = !DIAGNOSABLE.has(result.state);
222
+ elements.dashboard.hidden = false;
223
+ elements.masthead.hidden = true;
224
+ renderUpdatePending(result, messages, elements);
225
+ renderDevices(result.devices, config, messages, elements.groups);
226
+ elements.setup.hidden = true;
227
+ elements.authenticate.hidden = false;
228
+ if (result.devices.length > 0) {
229
+ elements.pageTitle.textContent = messages.dashboardPageTitle;
230
+ } else if (result.state === 'authentication-required') {
231
+ elements.setup.hidden = false;
232
+ }
233
+ }
234
+
235
+ function bindPreferences(elements, getConfig, saveConfig, getMessages, openDevice) {
236
+ elements.groups.addEventListener('click', (event) => {
237
+ const opener = event.target.closest?.('.device-open-control');
238
+ if (!opener) return;
239
+ openDevice?.(opener.closest('.device-tile').dataset.serial, opener);
240
+ });
241
+ elements.groups.addEventListener(
242
+ 'load',
243
+ (event) => {
244
+ if (event.target?.dataset?.deviceArtwork !== undefined) {
245
+ event.target.parentElement.dataset.artworkLoaded = '';
246
+ }
247
+ },
248
+ true,
249
+ );
250
+ elements.groups.addEventListener(
251
+ 'error',
252
+ (event) => {
253
+ if (event.target?.dataset?.deviceArtwork !== undefined) event.target.hidden = true;
254
+ },
255
+ true,
256
+ );
257
+ (elements.deviceSettingsControls ?? elements.groups).addEventListener('change', async (event) => {
258
+ const control = event.target;
259
+ const serial = control?.dataset?.serial;
260
+ const key = control?.dataset?.preference;
261
+ const existing = getConfig();
262
+ if (!existing || !serial || !key) return;
263
+ const defaults = { represented: true, audio: true, snapshotMode: 'Refresh' };
264
+ const value = key === 'snapshotMode' ? control.value : control.checked;
265
+ markPendingPreference(control, key, value);
266
+ if (key === 'snapshotMode') {
267
+ control.closest('.snapshot-setting')?.querySelector('[data-snapshot-description]').replaceChildren(
268
+ control.dataset.description,
269
+ );
270
+ }
271
+ const entityPreferences = { ...(existing.entityPreferences ?? {}) };
272
+ const preference = { ...(entityPreferences[serial] ?? {}) };
273
+ if (value === defaults[key]) delete preference[key];
274
+ else preference[key] = value;
275
+ if (Object.keys(preference).length === 0) delete entityPreferences[serial];
276
+ else entityPreferences[serial] = preference;
277
+ try {
278
+ await saveConfig({ ...existing, entityPreferences });
279
+ if (key === 'represented') {
280
+ // The control and the tile it speaks for are in two different places now, so the tile is found by serial.
281
+ const tile = [...elements.groups.querySelectorAll('.device-tile')].find(
282
+ (candidate) => candidate.dataset.serial === serial,
283
+ );
284
+ tile?.classList.toggle('device-tile-disabled', !value);
285
+ control
286
+ .closest('.preference-grid')
287
+ ?.querySelectorAll('[data-requires-representation]')
288
+ .forEach((setting) => {
289
+ setting.hidden = !value;
290
+ });
291
+ }
292
+ } catch {
293
+ elements.summary.textContent = getMessages().preferenceSaveFailed;
294
+ }
295
+ });
296
+ }
297
+
298
+ /**
299
+ * Fills the settings page for one device, which is where its controls live rather than on the tile itself.
300
+ *
301
+ * The tile is a summary and a fixed square; a device's settings are neither, and grew past it. A device that
302
+ * nothing represents gets the explanation instead of controls, because there is nothing for it to obey.
303
+ */
304
+ function renderDeviceSettings(device, config, messages, elements) {
305
+ const preferences = config.entityPreferences ?? {};
306
+ elements.deviceSettingsTitle.textContent = device.name;
307
+ elements.deviceSettingsEyebrow.textContent = device.modelName;
308
+ if (device.diagnosticOnly) {
309
+ elements.deviceSettingsControls.innerHTML = `<div class="diagnostic-panel"><img src="assets/icons/troubleshoot.svg" alt=""><strong>${escapeHtml(messages.diagnosticOnly)}</strong><p>${escapeHtml(messages.diagnosticDescription)}</p><p>${escapeHtml(messages.diagnosticNoAction)}</p></div>`;
310
+ return;
311
+ }
312
+ const preference = {
313
+ represented: preferences[device.serial]?.represented ?? true,
314
+ audio: preferences[device.serial]?.audio ?? true,
315
+ snapshotMode: preferences[device.serial]?.snapshotMode ?? 'Refresh',
316
+ };
317
+ const controls = device.preferences
318
+ .map((key) => preferenceControl(device, key, preference, messages))
319
+ .join('');
320
+ /*
321
+ * A camera that stands alone is its own station, so it carries an arming state and appears as a security system
322
+ * of its own. Stated here because a household of standalone cameras shows several, which reads as a fault.
323
+ */
324
+ const ownArming =
325
+ device.deviceClass === 'camera' && (device.representation ?? []).includes('arming.security-system')
326
+ ? `<p class="preference-note">${escapeHtml(messages.cameraOwnArming)}</p>`
327
+ : '';
328
+ elements.deviceSettingsControls.innerHTML = `${representationLine(device, messages)}${ownArming}${controls}`;
329
+ }
330
+
331
+ /**
332
+ * Puts each camera's own last image on its tile, keeping the packaged product photograph where there is none.
333
+ *
334
+ * One request per tile and one at a time: an image is up to ten megabytes and the answers are base64, so
335
+ * asking for every camera at once would hold the whole grid's worth in the page before any of it is shown.
336
+ * The product photograph is only replaced once the browser has decoded the answer, so a truncated or
337
+ * undecodable image leaves the tile exactly as it was rather than empty.
338
+ */
339
+ async function applyDeviceImages(elements, requestImage) {
340
+ for (const tile of elements.groups.querySelectorAll('.device-tile[data-device-class="camera"][data-serial]')) {
341
+ const art = tile.querySelector('.device-art');
342
+ if (!art) continue;
343
+ let answer;
344
+ try {
345
+ answer = await requestImage(tile.dataset.serial);
346
+ } catch {
347
+ continue;
348
+ }
349
+ if (!answer?.image) continue;
350
+ let image = art.querySelector('img[data-device-artwork]');
351
+ if (!image) {
352
+ image = document.createElement('img');
353
+ image.alt = '';
354
+ image.setAttribute('data-device-artwork', '');
355
+ art.appendChild(image);
356
+ }
357
+ const packaged = image.getAttribute('src');
358
+ image.addEventListener(
359
+ 'load',
360
+ () => {
361
+ tile.setAttribute('data-artwork-scene', '');
362
+ },
363
+ { once: true },
364
+ );
365
+ image.addEventListener(
366
+ 'error',
367
+ () => {
368
+ tile.removeAttribute('data-artwork-scene');
369
+ image.hidden = !packaged;
370
+ if (packaged) image.src = packaged;
371
+ },
372
+ { once: true },
373
+ );
374
+ image.src = `data:image/jpeg;base64,${answer.image}`;
375
+ }
376
+ }
377
+
378
+ global.HomebridgeEufyDashboard = {
379
+ applyDeviceImages,
380
+ bindPreferences,
381
+ render,
382
+ renderDeviceSettings,
383
+ renderUpdatePending,
384
+ };
385
+ })(window);
@@ -0,0 +1,37 @@
1
+ (function legacySettingsModule(global) {
2
+ const keys = [
3
+ 'CameraMaxLivestreamDuration',
4
+ 'autoSyncStation',
5
+ 'cameras',
6
+ 'cleanCache',
7
+ 'debugLivestream',
8
+ 'enableDetailedLogging',
9
+ 'enableEmbeddedPKCS1Support',
10
+ 'experimentalMode',
11
+ 'hkAway',
12
+ 'hkHome',
13
+ 'hkNight',
14
+ 'hkOff',
15
+ 'ignoreDevices',
16
+ 'ignoreMultipleDevicesWarning',
17
+ 'ignoreStations',
18
+ 'omitLogFiles',
19
+ 'stations',
20
+ 'syncStationModes',
21
+ 'useEmbeddedPKCS1Support',
22
+ ];
23
+
24
+ function effectful(key, value) {
25
+ const defaults = { CameraMaxLivestreamDuration: 30, hkHome: 1, hkAway: 0, hkNight: 3, hkOff: 63 };
26
+ if (Object.hasOwn(defaults, key)) return value !== defaults[key];
27
+ if (Array.isArray(value)) return value.length > 0;
28
+ if (value && typeof value === 'object') return Object.keys(value).length > 0;
29
+ return Boolean(value);
30
+ }
31
+
32
+ function names(block) {
33
+ return keys.filter((key) => Object.hasOwn(block, key) && effectful(key, block[key])).sort();
34
+ }
35
+
36
+ global.HomebridgeEufyLegacySettings = { names };
37
+ })(window);
@@ -0,0 +1,68 @@
1
+ (function attachDiagnosticsWizard(global) {
2
+ const questions = [
3
+ { profile: 'dashboard-ui', message: 'diagnosticsQuestionDashboard' },
4
+ { profile: 'startup-authentication', message: 'diagnosticsQuestionStartup' },
5
+ { profile: 'device-representation', message: 'diagnosticsQuestionDevices' },
6
+ { profile: 'control-state', message: 'diagnosticsQuestionControl' },
7
+ { profile: 'live-media', message: 'diagnosticsQuestionLiveMedia' },
8
+ { profile: 'hksv-recording', message: 'diagnosticsQuestionRecording' },
9
+ ];
10
+
11
+ function start() {
12
+ return { mode: 'questions', questionIndex: 0, profile: undefined, source: 'questions' };
13
+ }
14
+
15
+ function answer(state, matches) {
16
+ if (matches) {
17
+ return { ...state, mode: 'frequency', profile: questions[state.questionIndex].profile };
18
+ }
19
+ if (state.questionIndex === questions.length - 1) {
20
+ return { ...state, mode: 'frequency', profile: 'other' };
21
+ }
22
+ return { ...state, questionIndex: state.questionIndex + 1 };
23
+ }
24
+
25
+ function direct() {
26
+ return { mode: 'direct', questionIndex: 0, profile: undefined, source: 'direct' };
27
+ }
28
+
29
+ function selectDirect(state, profile) {
30
+ return { ...state, mode: 'frequency', profile, source: 'direct' };
31
+ }
32
+
33
+ function reject(state) {
34
+ return state.source === 'direct' ? direct() : start();
35
+ }
36
+
37
+ function chooseReproductionMode(state, reproductionMode) {
38
+ return { ...state, mode: 'match', reproductionMode };
39
+ }
40
+
41
+ function backFromFrequency(state) {
42
+ return state.source === 'direct' ? direct() : start();
43
+ }
44
+
45
+ function screen(session, startingAnother) {
46
+ if (session.partialExportAvailable && !startingAnother) return 'review';
47
+ if (startingAnother || session.status === 'inactive' || session.status === 'expired') return 'choose';
48
+ if (session.status === 'authorized' || session.status === 'reproducing') return 'reproduce';
49
+ return 'status';
50
+ }
51
+
52
+ function backgroundActive(session) {
53
+ return session.status === 'reproducing' && session.profile === 'dashboard-ui';
54
+ }
55
+
56
+ global.HomebridgeEufyDiagnosticsWizard = {
57
+ answer,
58
+ backFromFrequency,
59
+ backgroundActive,
60
+ chooseReproductionMode,
61
+ direct,
62
+ questions,
63
+ reject,
64
+ screen,
65
+ selectDirect,
66
+ start,
67
+ };
68
+ })(window);