@homebridge-plugins/homebridge-eufy-security 4.7.1 → 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 (469) 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 -769
  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 -431
  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/LockAccessory.js +0 -142
  295. package/dist/accessories/LockAccessory.js.map +0 -1
  296. package/dist/accessories/MotionSensorAccessory.js +0 -48
  297. package/dist/accessories/MotionSensorAccessory.js.map +0 -1
  298. package/dist/accessories/SmartDropAccessory.js +0 -141
  299. package/dist/accessories/SmartDropAccessory.js.map +0 -1
  300. package/dist/accessories/StationAccessory.js +0 -373
  301. package/dist/accessories/StationAccessory.js.map +0 -1
  302. package/dist/config.js +0 -26
  303. package/dist/config.js.map +0 -1
  304. package/dist/controller/DebugRecordingManager.js +0 -402
  305. package/dist/controller/DebugRecordingManager.js.map +0 -1
  306. package/dist/controller/LocalLivestreamManager.js +0 -371
  307. package/dist/controller/LocalLivestreamManager.js.map +0 -1
  308. package/dist/controller/recordingDelegate.js +0 -252
  309. package/dist/controller/recordingDelegate.js.map +0 -1
  310. package/dist/controller/snapshotDelegate.js +0 -348
  311. package/dist/controller/snapshotDelegate.js.map +0 -1
  312. package/dist/controller/streamingDelegate.js +0 -389
  313. package/dist/controller/streamingDelegate.js.map +0 -1
  314. package/dist/interfaces.js +0 -2
  315. package/dist/interfaces.js.map +0 -1
  316. package/dist/media/Snapshot-Unavailable.png +0 -0
  317. package/dist/media/Snapshot-Unavailable.xcf +0 -0
  318. package/dist/media/Snapshot-black.png +0 -0
  319. package/dist/media/camera-disabled.png +0 -0
  320. package/dist/media/camera-offline.png +0 -0
  321. package/dist/media/media/Snapshot-Unavailable.png +0 -0
  322. package/dist/media/media/Snapshot-Unavailable.xcf +0 -0
  323. package/dist/media/media/Snapshot-black.png +0 -0
  324. package/dist/media/media/camera-disabled.png +0 -0
  325. package/dist/media/media/camera-offline.png +0 -0
  326. package/dist/utils/Talkback.js +0 -162
  327. package/dist/utils/Talkback.js.map +0 -1
  328. package/dist/utils/accessoriesStore.js +0 -207
  329. package/dist/utils/accessoriesStore.js.map +0 -1
  330. package/dist/utils/configTypes.js +0 -36
  331. package/dist/utils/configTypes.js.map +0 -1
  332. package/dist/utils/ffmpeg.js +0 -1111
  333. package/dist/utils/ffmpeg.js.map +0 -1
  334. package/dist/utils/interfaces.js +0 -8
  335. package/dist/utils/interfaces.js.map +0 -1
  336. package/dist/utils/utils.js +0 -224
  337. package/dist/utils/utils.js.map +0 -1
  338. package/dist/version.js +0 -2
  339. package/dist/version.js.map +0 -1
  340. package/homebridge-eufy-security.png +0 -0
  341. package/homebridge-ui/diagnosticsCrypto.js +0 -112
  342. package/homebridge-ui/public/app.js +0 -237
  343. package/homebridge-ui/public/assets/devices/4g_lte_cam_s330_large.png +0 -0
  344. package/homebridge-ui/public/assets/devices/4g_lte_starlight_large.jpg +0 -0
  345. package/homebridge-ui/public/assets/devices/BATTERY_DOORBELL_C30.png +0 -0
  346. package/homebridge-ui/public/assets/devices/BATTERY_DOORBELL_C31.png +0 -0
  347. package/homebridge-ui/public/assets/devices/batterydoorbell1080p_large.jpg +0 -0
  348. package/homebridge-ui/public/assets/devices/batterydoorbell2k_large.png +0 -0
  349. package/homebridge-ui/public/assets/devices/batterydoorbell2kdual_large.jpg +0 -0
  350. package/homebridge-ui/public/assets/devices/batterydoorbell_e340_large.png +0 -0
  351. package/homebridge-ui/public/assets/devices/eufy-security-client.png +0 -0
  352. package/homebridge-ui/public/assets/devices/eufyCamS4_large.png +0 -0
  353. package/homebridge-ui/public/assets/devices/eufycam2_large.png +0 -0
  354. package/homebridge-ui/public/assets/devices/eufycam2c_large.jpg +0 -0
  355. package/homebridge-ui/public/assets/devices/eufycam2cpro_large.jpg +0 -0
  356. package/homebridge-ui/public/assets/devices/eufycam2pro_large.jpg +0 -0
  357. package/homebridge-ui/public/assets/devices/eufycam3_large.jpg +0 -0
  358. package/homebridge-ui/public/assets/devices/eufycam3c_large.jpg +0 -0
  359. package/homebridge-ui/public/assets/devices/eufycam3pro_large.png +0 -0
  360. package/homebridge-ui/public/assets/devices/eufycam_large.jpg +0 -0
  361. package/homebridge-ui/public/assets/devices/eufycame330_large.jpg +0 -0
  362. package/homebridge-ui/public/assets/devices/flood_freeze_sensor_e20_t90F0_large.png +0 -0
  363. package/homebridge-ui/public/assets/devices/floodlight2_large.jpg +0 -0
  364. package/homebridge-ui/public/assets/devices/floodlight2pro_large.jpg +0 -0
  365. package/homebridge-ui/public/assets/devices/floodlight_large.jpg +0 -0
  366. package/homebridge-ui/public/assets/devices/floodlightcame340_large.jpg +0 -0
  367. package/homebridge-ui/public/assets/devices/garage_camera_t8452_large.png +0 -0
  368. package/homebridge-ui/public/assets/devices/homebase2_large.png +0 -0
  369. package/homebridge-ui/public/assets/devices/homebase3_large.png +0 -0
  370. package/homebridge-ui/public/assets/devices/homebase_large.jpg +0 -0
  371. package/homebridge-ui/public/assets/devices/homebasemini_large.png +0 -0
  372. package/homebridge-ui/public/assets/devices/indoorcamC210_large.png +0 -0
  373. package/homebridge-ui/public/assets/devices/indoorcamC220_large.png +0 -0
  374. package/homebridge-ui/public/assets/devices/indoorcamC220_t8419N_large.png +0 -0
  375. package/homebridge-ui/public/assets/devices/indoorcamE30_large.png +0 -0
  376. package/homebridge-ui/public/assets/devices/indoorcamc120_large.png +0 -0
  377. package/homebridge-ui/public/assets/devices/indoorcammini_large.jpg +0 -0
  378. package/homebridge-ui/public/assets/devices/indoorcamp24_large.png +0 -0
  379. package/homebridge-ui/public/assets/devices/indoorcams350_large.png +0 -0
  380. package/homebridge-ui/public/assets/devices/keypad_large.png +0 -0
  381. package/homebridge-ui/public/assets/devices/minibase_chime_T8023_large.jpg +0 -0
  382. package/homebridge-ui/public/assets/devices/motionsensor_large.png +0 -0
  383. package/homebridge-ui/public/assets/devices/nvr_s4_max_T8N00_large.png +0 -0
  384. package/homebridge-ui/public/assets/devices/pir_sensor_e20_large.png +0 -0
  385. package/homebridge-ui/public/assets/devices/poe_bullet_ptz_cam_s4_T8E00_large.png +0 -0
  386. package/homebridge-ui/public/assets/devices/sensor_large.png +0 -0
  387. package/homebridge-ui/public/assets/devices/siren_sensor_e20_large.png +0 -0
  388. package/homebridge-ui/public/assets/devices/smartdrop_t8790_large.png +0 -0
  389. package/homebridge-ui/public/assets/devices/smartlock_t8500_large.png +0 -0
  390. package/homebridge-ui/public/assets/devices/smartlock_t8500_wifibridge_large.jpg +0 -0
  391. package/homebridge-ui/public/assets/devices/smartlock_t8503_large.png +0 -0
  392. package/homebridge-ui/public/assets/devices/smartlock_t8504_large.jpg +0 -0
  393. package/homebridge-ui/public/assets/devices/smartlock_t8510P_t8520P_large.png +0 -0
  394. package/homebridge-ui/public/assets/devices/smartlock_t85D0_large.png +0 -0
  395. package/homebridge-ui/public/assets/devices/smartlock_t85L0_large.png +0 -0
  396. package/homebridge-ui/public/assets/devices/smartlock_t85P0_large.png +0 -0
  397. package/homebridge-ui/public/assets/devices/smartlock_t85V0_large.png +0 -0
  398. package/homebridge-ui/public/assets/devices/smartlock_touch_and_wifi_t8502_large.png +0 -0
  399. package/homebridge-ui/public/assets/devices/smartlock_touch_and_wifi_t8506_large.png +0 -0
  400. package/homebridge-ui/public/assets/devices/smartlock_touch_and_wifi_t8520_large.png +0 -0
  401. package/homebridge-ui/public/assets/devices/smartlock_touch_t8510_large.png +0 -0
  402. package/homebridge-ui/public/assets/devices/smartlock_touch_t8510_wifibridge_large.jpg +0 -0
  403. package/homebridge-ui/public/assets/devices/smartlock_video_t8530_large.png +0 -0
  404. package/homebridge-ui/public/assets/devices/smartlock_video_t8531_large.png +0 -0
  405. package/homebridge-ui/public/assets/devices/smartlockwifibridge_t8021_large.jpg +0 -0
  406. package/homebridge-ui/public/assets/devices/smartsafe_s10_t7400_large.png +0 -0
  407. package/homebridge-ui/public/assets/devices/smartsafe_s12_t7401_large.png +0 -0
  408. package/homebridge-ui/public/assets/devices/smarttrack_card_t87B2_large.png +0 -0
  409. package/homebridge-ui/public/assets/devices/smarttrack_link_t87B0_large.png +0 -0
  410. package/homebridge-ui/public/assets/devices/smoke_alarm_e10_t90S0_large.png +0 -0
  411. package/homebridge-ui/public/assets/devices/solocamc210_large.jpg +0 -0
  412. package/homebridge-ui/public/assets/devices/solocamc35_large.png +0 -0
  413. package/homebridge-ui/public/assets/devices/solocame20_large.jpg +0 -0
  414. package/homebridge-ui/public/assets/devices/solocame30_large.png +0 -0
  415. package/homebridge-ui/public/assets/devices/solocame40_large.jpg +0 -0
  416. package/homebridge-ui/public/assets/devices/solocame42_large.png +0 -0
  417. package/homebridge-ui/public/assets/devices/solocaml20_large.jpg +0 -0
  418. package/homebridge-ui/public/assets/devices/solocams220_large.jpg +0 -0
  419. package/homebridge-ui/public/assets/devices/solocams340_large.png +0 -0
  420. package/homebridge-ui/public/assets/devices/solocams40_large.jpg +0 -0
  421. package/homebridge-ui/public/assets/devices/soloindoorcamc24_large.jpg +0 -0
  422. package/homebridge-ui/public/assets/devices/solooutdoorcamc22_large.png +0 -0
  423. package/homebridge-ui/public/assets/devices/solooutdoorcamc24_large.jpg +0 -0
  424. package/homebridge-ui/public/assets/devices/unknown.png +0 -0
  425. package/homebridge-ui/public/assets/devices/walllight_s100_large.png +0 -0
  426. package/homebridge-ui/public/assets/devices/walllight_s120_large.png +0 -0
  427. package/homebridge-ui/public/assets/devices/water_freeze_sensor_t8920_large.png +0 -0
  428. package/homebridge-ui/public/assets/devices/wireddoorbell1080p_large.jpg +0 -0
  429. package/homebridge-ui/public/assets/devices/wireddoorbell2k_large.png +0 -0
  430. package/homebridge-ui/public/assets/devices/wireddoorbelldual_large.jpg +0 -0
  431. package/homebridge-ui/public/assets/icons/attach.svg +0 -1
  432. package/homebridge-ui/public/assets/icons/battery_0.svg +0 -1
  433. package/homebridge-ui/public/assets/icons/battery_1.svg +0 -1
  434. package/homebridge-ui/public/assets/icons/battery_2.svg +0 -1
  435. package/homebridge-ui/public/assets/icons/battery_3.svg +0 -1
  436. package/homebridge-ui/public/assets/icons/battery_4.svg +0 -1
  437. package/homebridge-ui/public/assets/icons/battery_5.svg +0 -1
  438. package/homebridge-ui/public/assets/icons/battery_6.svg +0 -1
  439. package/homebridge-ui/public/assets/icons/copy.svg +0 -1
  440. package/homebridge-ui/public/assets/icons/delete.svg +0 -1
  441. package/homebridge-ui/public/assets/icons/download.svg +0 -1
  442. package/homebridge-ui/public/assets/icons/info.svg +0 -1
  443. package/homebridge-ui/public/assets/icons/refresh.svg +0 -1
  444. package/homebridge-ui/public/assets/icons/satellite_alt.svg +0 -1
  445. package/homebridge-ui/public/assets/icons/solar_power.svg +0 -1
  446. package/homebridge-ui/public/components/device-card.js +0 -174
  447. package/homebridge-ui/public/components/guard-modes.js +0 -88
  448. package/homebridge-ui/public/components/number-input.js +0 -121
  449. package/homebridge-ui/public/components/select.js +0 -73
  450. package/homebridge-ui/public/components/toggle.js +0 -68
  451. package/homebridge-ui/public/services/api.js +0 -250
  452. package/homebridge-ui/public/services/config.js +0 -144
  453. package/homebridge-ui/public/style.css +0 -818
  454. package/homebridge-ui/public/utils/countries.js +0 -73
  455. package/homebridge-ui/public/utils/device-images.js +0 -102
  456. package/homebridge-ui/public/utils/helpers.js +0 -115
  457. package/homebridge-ui/public/views/dashboard.js +0 -226
  458. package/homebridge-ui/public/views/device-detail.js +0 -626
  459. package/homebridge-ui/public/views/diagnostics.js +0 -402
  460. package/homebridge-ui/public/views/login.js +0 -650
  461. package/homebridge-ui/public/views/recordings.js +0 -319
  462. package/homebridge-ui/public/views/settings.js +0 -197
  463. package/homebridge-ui/public/views/unsupported-detail.js +0 -296
  464. package/media/Snapshot-Unavailable.png +0 -0
  465. package/media/Snapshot-Unavailable.xcf +0 -0
  466. package/media/Snapshot-black.png +0 -0
  467. package/media/camera-disabled.png +0 -0
  468. package/media/camera-offline.png +0 -0
  469. 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);