@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
@@ -1,650 +0,0 @@
1
- /**
2
- * Login View — multi-step wizard for Eufy Security authentication.
3
- * Steps: Welcome → Credentials → TFA (if needed) → Captcha (if needed) → Discovery
4
- */
5
- // eslint-disable-next-line no-unused-vars
6
- const LoginView = {
7
- STEP: { WELCOME: 0, CREDENTIALS: 1, TFA: 2, CAPTCHA: 3, DISCOVERY: 4 },
8
-
9
- _currentStep: 0,
10
- _captchaData: null,
11
- _credentials: null,
12
- _container: null,
13
- /** @type {object|null} Login options to be consumed by _renderDiscovery for inline auth */
14
- _loginOptions: null,
15
-
16
- render(container) {
17
- this._container = container;
18
- this._currentStep = this.STEP.WELCOME;
19
- this._renderStep();
20
- },
21
-
22
- _renderStep() {
23
- const c = this._container;
24
- c.innerHTML = '';
25
-
26
- const wrap = document.createElement('div');
27
- wrap.className = 'login-card';
28
-
29
- switch (this._currentStep) {
30
- case this.STEP.WELCOME:
31
- this._renderWelcome(wrap);
32
- break;
33
- case this.STEP.CREDENTIALS:
34
- this._renderCredentials(wrap);
35
- break;
36
- case this.STEP.TFA:
37
- this._renderTFA(wrap);
38
- break;
39
- case this.STEP.CAPTCHA:
40
- this._renderCaptcha(wrap);
41
- break;
42
- case this.STEP.DISCOVERY:
43
- this._renderDiscovery(wrap);
44
- break;
45
- }
46
-
47
- c.appendChild(wrap);
48
- },
49
-
50
- // ===== Step 0: Welcome =====
51
- _renderWelcome(wrap) {
52
- this._sectionTitle(wrap, 'Welcome');
53
- const body = wrap;
54
- body.insertAdjacentHTML('beforeend', `
55
- <div class="welcome-banner">
56
- <div class="welcome-banner__title">Eufy Security for HomeKit</div>
57
- <div class="welcome-banner__text">
58
- Connect your Eufy Security devices to Apple HomeKit through Homebridge.
59
- You'll need your Eufy account credentials to get started.
60
- </div>
61
- </div>
62
- <div class="alert alert-warning mt-3" role="alert" style="font-size: 0.85rem;">
63
- <div class="form-check">
64
- <input class="form-check-input" type="checkbox" id="ack-guest-admin">
65
- <label class="form-check-label" for="ack-guest-admin">
66
- <strong>Important:</strong> Use a <strong>dedicated guest admin account</strong> — not your primary Eufy account.
67
- <a href="https://github.com/homebridge-plugins/homebridge-eufy-security/wiki/Create-a-dedicated-admin-account-for-Homebridge-Eufy-Security-Plugin" target="_blank">Learn more</a>
68
- </label>
69
- </div>
70
- </div>
71
- <div class="alert alert-info mt-2" role="alert" style="font-size: 0.85rem;">
72
- <div class="form-check">
73
- <input class="form-check-input" type="checkbox" id="ack-stop-plugin">
74
- <label class="form-check-label" for="ack-stop-plugin">
75
- If the plugin is currently running, please <strong>stop it first</strong> before logging in here. Both cannot use the same credentials simultaneously.
76
- </label>
77
- </div>
78
- </div>
79
- <div id="node-version-warning" class="d-none"></div>
80
- <div class="d-flex flex-wrap gap-2 mt-2 welcome-actions">
81
- <button class="btn btn-primary flex-fill" id="btn-start" disabled>Continue to Login</button>
82
- <button class="btn btn-outline-success flex-fill d-none" id="btn-reconnect" disabled>
83
- <span class="spinner-border spinner-border-sm d-none me-1" id="reconnect-spinner"></span>
84
- Refresh Devices
85
- </button>
86
- </div>
87
- `);
88
-
89
- const ack1 = body.querySelector('#ack-guest-admin');
90
- const ack2 = body.querySelector('#ack-stop-plugin');
91
- const btnStart = body.querySelector('#btn-start');
92
- const nodeWarningContainer = body.querySelector('#node-version-warning');
93
- let ack3 = null; // Node version checkbox, only if affected
94
-
95
- const updateBtn = () => {
96
- const allChecked = ack1.checked && ack2.checked && (!ack3 || ack3.checked);
97
- btnStart.disabled = !allChecked;
98
- };
99
- ack1.addEventListener('change', updateBtn);
100
- ack2.addEventListener('change', updateBtn);
101
-
102
- // Check Node.js version and conditionally show warning
103
- App.checkNodeVersion().then(() => {
104
- const warning = App.state.nodeVersionWarning;
105
- if (warning && warning.affected) {
106
- nodeWarningContainer.className = 'alert alert-danger mt-2';
107
- nodeWarningContainer.setAttribute('role', 'alert');
108
- nodeWarningContainer.style.fontSize = '0.85rem';
109
-
110
- const formCheck = document.createElement('div');
111
- formCheck.className = 'form-check';
112
-
113
- const input = document.createElement('input');
114
- input.className = 'form-check-input';
115
- input.type = 'checkbox';
116
- input.id = 'ack-node-version';
117
- formCheck.appendChild(input);
118
-
119
- const label = document.createElement('label');
120
- label.className = 'form-check-label';
121
- label.htmlFor = 'ack-node-version';
122
-
123
- const strongPrefix = document.createElement('strong');
124
- strongPrefix.textContent = 'Streaming unavailable:';
125
- label.appendChild(strongPrefix);
126
- label.appendChild(document.createTextNode(' Node.js '));
127
- const strongVer = document.createElement('strong');
128
- strongVer.textContent = warning.nodeVersion;
129
- label.appendChild(strongVer);
130
- label.appendChild(document.createTextNode(' — '));
131
- Helpers.appendNodeVersionWarning(label);
132
-
133
- formCheck.appendChild(label);
134
- nodeWarningContainer.appendChild(formCheck);
135
- ack3 = nodeWarningContainer.querySelector('#ack-node-version');
136
- ack3.addEventListener('change', updateBtn);
137
- // Also update refresh button state when node version checkbox changes
138
- ack3.addEventListener('change', () => {
139
- const reconnectBtn = body.querySelector('#btn-reconnect');
140
- if (reconnectBtn && !reconnectBtn.classList.contains('d-none')) {
141
- const allChecked = ack1.checked && ack2.checked && (!ack3 || ack3.checked);
142
- reconnectBtn.disabled = !allChecked;
143
- }
144
- });
145
- updateBtn();
146
- }
147
- });
148
-
149
- btnStart.addEventListener('click', () => {
150
- this._currentStep = this.STEP.CREDENTIALS;
151
- this._renderStep();
152
- });
153
-
154
- // Check for persistent cache and conditionally show "Refresh Devices" button
155
- const btnReconnect = body.querySelector('#btn-reconnect');
156
- Api.checkCache().then((result) => {
157
- if (result && result.valid) {
158
- btnReconnect.classList.remove('d-none');
159
- // Highlight Refresh as the primary action, demote Continue to Login
160
- btnReconnect.classList.remove('btn-outline-success');
161
- btnReconnect.classList.add('btn-success');
162
- btnStart.classList.remove('btn-primary');
163
- btnStart.classList.add('btn-outline-primary');
164
- const updateReconnectBtn = () => {
165
- const allChecked = ack1.checked && ack2.checked && (!ack3 || ack3.checked);
166
- btnReconnect.disabled = !allChecked;
167
- };
168
- ack1.addEventListener('change', updateReconnectBtn);
169
- ack2.addEventListener('change', updateReconnectBtn);
170
- updateReconnectBtn();
171
- }
172
- }).catch(() => { /* cache check failed — hide refresh button */ });
173
-
174
- btnReconnect.addEventListener('click', async () => {
175
- btnReconnect.disabled = true;
176
- btnStart.disabled = true;
177
-
178
- try {
179
- // Get saved credentials from plugin config
180
- const config = await Config.get();
181
- if (!config.username || !config.password) {
182
- throw new Error('No saved credentials found. Please log in manually.');
183
- }
184
-
185
- // Go straight to discovery — auth will happen there
186
- this._credentials = null; // reconnect uses existing config
187
- this._loginOptions = {
188
- username: config.username,
189
- password: config.password,
190
- country: config.country || 'US',
191
- deviceName: config.deviceName || '',
192
- reconnect: true,
193
- };
194
- this._currentStep = this.STEP.DISCOVERY;
195
- this._renderStep();
196
- } catch (e) {
197
- btnReconnect.disabled = false;
198
- btnStart.disabled = false;
199
- const errMsg = e.message || 'Failed to load credentials.';
200
- btnReconnect.classList.add('eufy-tooltip');
201
- btnReconnect.setAttribute('data-tooltip', errMsg);
202
- btnReconnect.classList.add('btn-outline-danger');
203
- setTimeout(() => {
204
- btnReconnect.classList.remove('eufy-tooltip');
205
- btnReconnect.removeAttribute('data-tooltip');
206
- btnReconnect.classList.remove('btn-outline-danger');
207
- }, 6000);
208
- }
209
- });
210
- },
211
-
212
- // ===== Step 1: Credentials =====
213
- _renderCredentials(wrap) {
214
- this._sectionTitle(wrap, 'Sign In');
215
- const body = wrap;
216
-
217
- body.insertAdjacentHTML('beforeend', `
218
- <div class="mb-3">
219
- <label for="login-email" class="form-label">Email Address</label>
220
- <input type="email" class="form-control" id="login-email" placeholder="your-eufy-email@example.com" required>
221
- </div>
222
- <div class="mb-3">
223
- <label for="login-password" class="form-label">Password</label>
224
- <input type="password" class="form-control" id="login-password" placeholder="Password" required>
225
- </div>
226
- <div class="mb-3">
227
- <label for="login-country" class="form-label">Country</label>
228
- <select class="form-select" id="login-country"></select>
229
- </div>
230
- <div class="mb-3">
231
- <label for="login-device" class="form-label">Device Name</label>
232
- <div class="input-group">
233
- <input type="text" class="form-control" id="login-device" value="" placeholder="e.g. My Homebridge" required>
234
- <button class="btn btn-outline-secondary eufy-tooltip" type="button" id="btn-generate-name" data-tooltip="Generate random name">
235
- ${Helpers.iconHtml('refresh.svg', 16, 'Generate name')}
236
- </button>
237
- </div>
238
- <div class="form-text">A name to identify this Homebridge instance to Eufy.</div>
239
- </div>
240
- <div id="login-error" class="alert alert-danger d-none" role="alert"></div>
241
- <button class="btn btn-primary w-100" id="btn-login" type="button">
242
- <span class="spinner-border spinner-border-sm d-none me-1" id="login-spinner"></span>
243
- Sign In
244
- </button>
245
- `);
246
-
247
- // Populate country dropdown
248
- const countrySelect = body.querySelector('#login-country');
249
- Object.entries(COUNTRIES).forEach(([code, name]) => {
250
- const opt = document.createElement('option');
251
- opt.value = code;
252
- opt.textContent = name;
253
- if (code === 'US') opt.selected = true;
254
- countrySelect.appendChild(opt);
255
- });
256
-
257
- // Pre-fill from existing config if available
258
- this._prefillCredentials(body);
259
-
260
- // Generate random device name (default if not already set by config)
261
- const deviceInput = body.querySelector('#login-device');
262
- if (!deviceInput.value) {
263
- deviceInput.value = Helpers.generateDeviceName();
264
- }
265
- body.querySelector('#btn-generate-name').addEventListener('click', () => {
266
- deviceInput.value = Helpers.generateDeviceName();
267
- });
268
-
269
- // Submit
270
- body.querySelector('#btn-login').addEventListener('click', () => this._doLogin(body));
271
-
272
- // Enter key support
273
- body.querySelectorAll('input').forEach((input) => {
274
- input.addEventListener('keydown', (e) => {
275
- if (e.key === 'Enter') this._doLogin(body);
276
- });
277
- });
278
- },
279
-
280
- async _prefillCredentials(body) {
281
- try {
282
- const config = await Config.get();
283
- if (config.username) body.querySelector('#login-email').value = config.username;
284
- if (config.password) body.querySelector('#login-password').value = config.password;
285
- if (config.country) body.querySelector('#login-country').value = config.country;
286
- if (config.deviceName) body.querySelector('#login-device').value = config.deviceName;
287
- } catch (e) {
288
- // Ignore — no config yet
289
- }
290
- },
291
-
292
- async _doLogin(body) {
293
- const email = body.querySelector('#login-email').value.trim();
294
- const password = body.querySelector('#login-password').value;
295
- const country = body.querySelector('#login-country').value;
296
- const deviceName = body.querySelector('#login-device').value.trim();
297
-
298
- if (!email || !password || !deviceName) {
299
- this._showError(body, 'Please enter your email, password, and device name.');
300
- return;
301
- }
302
-
303
- // Stash credentials in memory — save only after full auth succeeds
304
- this._credentials = { username: email, password: password, country: country, deviceName: deviceName };
305
-
306
- // Go straight to discovery — auth will happen there
307
- this._loginOptions = {
308
- username: email,
309
- password: password,
310
- country: country,
311
- deviceName: deviceName,
312
- };
313
- this._currentStep = this.STEP.DISCOVERY;
314
- this._renderStep();
315
- },
316
-
317
- // ===== Step 2: TFA =====
318
- _renderTFA(wrap) {
319
- this._sectionTitle(wrap, 'Two-Factor Authentication');
320
- const body = wrap;
321
-
322
- body.insertAdjacentHTML('beforeend', `
323
- <p class="text-muted" style="font-size: 0.85rem;">
324
- A verification code has been sent to your registered device or email. Enter it below.
325
- </p>
326
- <div class="mb-3">
327
- <label for="tfa-code" class="form-label">Verification Code</label>
328
- <input type="text" class="form-control text-center" id="tfa-code" placeholder="000000"
329
- maxlength="6" autocomplete="one-time-code" inputmode="numeric" style="font-size: 1.5rem; letter-spacing: 0.3em;">
330
- </div>
331
- <div id="login-error" class="alert alert-danger d-none" role="alert"></div>
332
- <button class="btn btn-primary w-100" id="btn-verify">
333
- <span class="spinner-border spinner-border-sm d-none me-1" id="login-spinner"></span>
334
- Verify
335
- </button>
336
- `);
337
-
338
- body.querySelector('#tfa-code').focus();
339
-
340
- body.querySelector('#btn-verify').addEventListener('click', () => this._doTFA(body));
341
- body.querySelector('#tfa-code').addEventListener('keydown', (e) => {
342
- if (e.key === 'Enter') this._doTFA(body);
343
- });
344
- },
345
-
346
- async _doTFA(body) {
347
- const code = body.querySelector('#tfa-code').value.trim();
348
- if (!code) {
349
- this._showError(body, 'Please enter the verification code.');
350
- return;
351
- }
352
-
353
- // Go straight to discovery — auth outcome arrives via push events
354
- this._loginOptions = { verifyCode: code };
355
- this._currentStep = this.STEP.DISCOVERY;
356
- this._renderStep();
357
- },
358
-
359
- // ===== Step 3: Captcha =====
360
- _renderCaptcha(wrap) {
361
- this._sectionTitle(wrap, 'Captcha Verification');
362
- const body = wrap;
363
-
364
- body.insertAdjacentHTML('beforeend', `
365
- <p class="text-muted" style="font-size: 0.85rem;">
366
- Please solve the captcha below to continue.
367
- </p>
368
- <div class="text-center mb-3">
369
- <img id="captcha-image" class="img-fluid border rounded" alt="Captcha" style="max-height: 100px;">
370
- </div>
371
- <div class="mb-3">
372
- <label for="captcha-code" class="form-label">Captcha Code</label>
373
- <input type="text" class="form-control text-center" id="captcha-code" placeholder="Enter captcha"
374
- style="font-size: 1.2rem; letter-spacing: 0.2em;">
375
- </div>
376
- <div id="login-error" class="alert alert-danger d-none" role="alert"></div>
377
- <button class="btn btn-primary w-100" id="btn-captcha">
378
- <span class="spinner-border spinner-border-sm d-none me-1" id="login-spinner"></span>
379
- Submit
380
- </button>
381
- `);
382
-
383
- if (this._captchaData && this._captchaData.captcha) {
384
- body.querySelector('#captcha-image').src = this._captchaData.captcha;
385
- }
386
-
387
- body.querySelector('#captcha-code').focus();
388
-
389
- body.querySelector('#btn-captcha').addEventListener('click', () => this._doCaptcha(body));
390
- body.querySelector('#captcha-code').addEventListener('keydown', (e) => {
391
- if (e.key === 'Enter') this._doCaptcha(body);
392
- });
393
- },
394
-
395
- async _doCaptcha(body) {
396
- const code = body.querySelector('#captcha-code').value.trim();
397
- if (!code) {
398
- this._showError(body, 'Please enter the captcha code.');
399
- return;
400
- }
401
-
402
- // Go straight to discovery — auth outcome arrives via push events
403
- this._loginOptions = {
404
- captcha: {
405
- captchaCode: code,
406
- captchaId: this._captchaData.id,
407
- },
408
- };
409
- this._currentStep = this.STEP.DISCOVERY;
410
- this._renderStep();
411
- },
412
-
413
- // ===== Step 4: Discovery =====
414
- _renderDiscovery(wrap) {
415
- // loginOptions may be set by the reconnect button or _doLogin before navigating here
416
- const loginOptions = this._loginOptions || null;
417
- this._loginOptions = null;
418
-
419
- const isAuthNeeded = !!loginOptions;
420
-
421
- wrap.innerHTML = `
422
- <div class="discovery-screen">
423
- <div class="discovery-screen__icon">${Helpers.iconHtml('satellite_alt.svg', 32)}</div>
424
- <div class="discovery-screen__title">${isAuthNeeded ? 'Refreshing your devices...' : 'Discovering your devices...'}</div>
425
- <div class="discovery-screen__subtitle">
426
- ${isAuthNeeded ? 'Authenticating and re-discovering all your stations and devices.' : 'Connecting to Eufy servers and detecting all your stations and devices. Hang tight!'}
427
- </div>
428
- <div class="progress mt-4" style="margin: 0 auto; height: 6px;">
429
- <div class="progress-bar progress-bar-striped progress-bar-animated" role="progressbar"
430
- style="width: 5%" id="discovery-progress"></div>
431
- </div>
432
- <div class="text-muted mt-2" style="font-size: 0.8rem;" id="discovery-status">${isAuthNeeded ? 'Authenticating...' : 'Connecting to Eufy Cloud...'}</div>
433
- <div id="discovery-warning" class="d-none mt-3" style="margin: 0 auto;"></div>
434
- <div id="discovery-error" class="d-none mt-3" style="margin: 0 auto;"></div>
435
- </div>
436
- `;
437
-
438
- const progressBar = wrap.querySelector('#discovery-progress');
439
- const statusEl = wrap.querySelector('#discovery-status');
440
- const warningEl = wrap.querySelector('#discovery-warning');
441
- const errorEl = wrap.querySelector('#discovery-error');
442
- let warningActive = false;
443
-
444
- // --- Show auth error with a back-to-login button ---
445
- const showAuthError = (message) => {
446
- progressBar.classList.remove('progress-bar-animated');
447
- progressBar.classList.add('bg-danger');
448
- statusEl.textContent = '';
449
- errorEl.className = 'mt-3';
450
- errorEl.style.margin = '0 auto';
451
- errorEl.innerHTML = `
452
- <div class="alert alert-danger mb-2" role="alert" style="font-size: 0.82rem;">
453
- ${Helpers.escHtml(message)}
454
- </div>
455
- <button class="btn btn-sm btn-primary" id="btn-back-login">Back to Login</button>
456
- `;
457
- errorEl.querySelector('#btn-back-login').addEventListener('click', () => {
458
- this._credentials = null;
459
- this._currentStep = this.STEP.WELCOME;
460
- this._renderStep();
461
- });
462
- };
463
-
464
- // --- Handler functions (shared by catch-up replay and live events) ---
465
- const handleProgress = (data) => {
466
- if (!data) return;
467
-
468
- if (typeof data.progress === 'number' && data.progress > 0) {
469
- progressBar.style.width = data.progress + '%';
470
- }
471
-
472
- // Update status text — allow unsupportedWait messages through even when warning banner is shown
473
- if (data.message && (!warningActive || data.phase === 'unsupportedWait')) {
474
- statusEl.textContent = data.message;
475
- }
476
- };
477
-
478
- const handleWarning = (data) => {
479
- if (!data) return;
480
- warningActive = true;
481
-
482
- // Update status to indicate we're waiting
483
- statusEl.textContent = 'Waiting for extra device details...';
484
-
485
- warningEl.className = 'mt-3';
486
- warningEl.style.margin = '0 auto';
487
- warningEl.innerHTML = `
488
- <div class="alert alert-warning mb-2" role="alert" style="font-size: 0.82rem; text-align: left;">
489
- <strong>${data.unsupportedCount || ''} unsupported device(s)</strong> detected.<br>
490
- <span class="text-muted" style="font-size: 0.78rem;">${Helpers.escHtml(data.unsupportedNames || '')}</span>
491
- <hr class="my-2">
492
- Collecting raw device data for diagnostics.
493
- </div>
494
- <div class="d-flex justify-content-center">
495
- <button class="btn btn-sm btn-outline-secondary" id="btn-skip-intel">Skip &amp; Continue</button>
496
- </div>
497
- `;
498
-
499
- // Skip button — tell server to abort the wait
500
- warningEl.querySelector('#btn-skip-intel').addEventListener('click', () => {
501
- warningActive = false;
502
- warningEl.innerHTML = '<div class="text-muted" style="font-size: 0.78rem;">Skipped — finalizing devices...</div>';
503
- statusEl.textContent = 'Finalizing...';
504
- Api.skipIntelWait().catch(() => { /* ignore */ });
505
- });
506
-
507
- // When the server finishes (addAccessory event), it will proceed automatically
508
- };
509
-
510
- // Register live event listeners
511
- Api.onDiscoveryProgress(handleProgress);
512
- Api.onDiscoveryWarning(handleWarning);
513
-
514
- // Auth outcome event listeners — all driven by server push events
515
- Api.onAuthSuccess(() => {
516
- // Server already sends discoveryProgress with "Authenticated" message
517
- catchUpOnState();
518
- });
519
- Api.onAuthError((data) => {
520
- showAuthError(data && data.message ? data.message : 'Authentication failed.');
521
- });
522
- Api.onTfaRequest(() => {
523
- this._currentStep = this.STEP.TFA;
524
- this._renderStep();
525
- });
526
- Api.onCaptchaRequest((data) => {
527
- this._captchaData = data;
528
- this._currentStep = this.STEP.CAPTCHA;
529
- this._renderStep();
530
- });
531
-
532
- // Catch up on discovery events that fired during the login request.
533
- const catchUpOnState = () => {
534
- Api.getDiscoveryState().then((state) => {
535
- if (state && state.progress > 0) {
536
- handleProgress(state);
537
- }
538
- }).catch(() => { /* ignore — live events will still work */ });
539
- };
540
-
541
- // Fire login request — resolves immediately, outcomes arrive as push events
542
- if (isAuthNeeded) {
543
- Api.login(loginOptions).catch((e) => {
544
- showAuthError('Connection error: ' + (e.message || e));
545
- });
546
- } else {
547
- // Auth already done (came from _doTFA / _doCaptcha) — just catch up
548
- catchUpOnState();
549
- }
550
-
551
- // Listen for the batch-processed accessories
552
- Api.onAccessoriesReady((payload) => {
553
- warningActive = false;
554
-
555
- const stations = Array.isArray(payload) ? payload : (payload && payload.stations) || [];
556
- const extended = payload && payload.extendedDiscovery;
557
- const noDevices = payload && payload.noDevices;
558
-
559
- // Save credentials even when no devices found — the account is valid
560
- if (this._credentials) {
561
- Config.updateGlobal(this._credentials).then(() => Config.save());
562
- this._credentials = null;
563
- }
564
-
565
- // If no stations or no devices were discovered, go to dashboard with empty state
566
- const totalDevices = (stations || []).reduce((sum, s) => sum + (s.devices ? s.devices.length : 0), 0);
567
- if (noDevices || !stations || stations.length === 0 || totalDevices === 0) {
568
- progressBar.style.width = '100%';
569
- progressBar.classList.remove('progress-bar-animated');
570
- progressBar.classList.add('bg-warning');
571
- statusEl.textContent = 'No devices found — redirecting to dashboard...';
572
-
573
- setTimeout(() => {
574
- App.state.stations = stations || [];
575
- App.state.cacheDate = new Date().toISOString();
576
- App.navigate('dashboard');
577
- }, 1500);
578
- return;
579
- }
580
-
581
- progressBar.style.width = '100%';
582
- progressBar.classList.remove('progress-bar-animated');
583
- statusEl.textContent = extended
584
- ? `Done — ${totalDevices} device(s) discovered (collected extra details for unsupported).`
585
- : `Done — ${totalDevices} device(s) discovered!`;
586
-
587
- // Hide the warning area
588
- warningEl.className = 'd-none';
589
-
590
- // Go to dashboard
591
- setTimeout(() => {
592
- App.state.stations = stations;
593
- App.state.cacheDate = new Date().toISOString();
594
- App.navigate('dashboard');
595
- }, 500);
596
- });
597
- },
598
-
599
- /**
600
- * Replaces the discovery screen content with an error message and a retry button.
601
- */
602
- _renderDiscoveryError(wrap, message) {
603
- wrap.innerHTML = `
604
- <div class="discovery-screen">
605
- <div class="discovery-screen__icon">${Helpers.iconHtml('warning.svg', 32)}</div>
606
- <div class="discovery-screen__title">Discovery Failed</div>
607
- <div class="discovery-screen__subtitle" style="color: var(--bs-danger, #dc3545);">
608
- ${message}
609
- </div>
610
- <button class="btn btn-primary mt-4" id="btn-retry-login">Retry Login</button>
611
- </div>
612
- `;
613
- wrap.querySelector('#btn-retry-login').addEventListener('click', () => {
614
- this._credentials = null;
615
- this._currentStep = this.STEP.CREDENTIALS;
616
- this._renderStep();
617
- });
618
- },
619
-
620
- // ===== Helpers =====
621
-
622
- _sectionTitle(container, title) {
623
- const header = document.createElement('div');
624
- header.className = 'login-section-title';
625
- header.textContent = title;
626
- container.appendChild(header);
627
- },
628
-
629
- _showError(body, msg) {
630
- const el = body.querySelector('#login-error');
631
- if (el) {
632
- el.textContent = msg;
633
- el.classList.remove('d-none');
634
- }
635
- },
636
-
637
- _hideError(body) {
638
- const el = body.querySelector('#login-error');
639
- if (el) el.classList.add('d-none');
640
- },
641
-
642
- _setLoading(body, loading) {
643
- const btn = body.querySelector('.btn-primary');
644
- const spinner = body.querySelector('#login-spinner');
645
- if (btn) btn.disabled = loading;
646
- if (spinner) {
647
- spinner.classList.toggle('d-none', !loading);
648
- }
649
- },
650
- };