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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (471) hide show
  1. package/AUTHORS +4 -0
  2. package/CODE_OF_CONDUCT.md +30 -0
  3. package/CONTRIBUTING.md +77 -0
  4. package/README.md +152 -49
  5. package/SECURITY.md +30 -0
  6. package/config.schema.json +92 -3
  7. package/dist/account/ownership.js +290 -0
  8. package/dist/account/ownership.js.map +1 -0
  9. package/dist/account/persistence.js +274 -0
  10. package/dist/account/persistence.js.map +1 -0
  11. package/dist/account/temporary-authentication.js +348 -0
  12. package/dist/account/temporary-authentication.js.map +1 -0
  13. package/dist/configuration.js +167 -0
  14. package/dist/configuration.js.map +1 -0
  15. package/dist/device/member-evidence.js +66 -0
  16. package/dist/device/member-evidence.js.map +1 -0
  17. package/dist/device/member-trust.js +114 -0
  18. package/dist/device/member-trust.js.map +1 -0
  19. package/dist/device/snapshot.js +190 -0
  20. package/dist/device/snapshot.js.map +1 -0
  21. package/dist/diagnostics.js +2218 -0
  22. package/dist/diagnostics.js.map +1 -0
  23. package/dist/homekit/adapter.js +2 -0
  24. package/dist/homekit/adapter.js.map +1 -0
  25. package/dist/homekit/adapters/battery.js +208 -0
  26. package/dist/homekit/adapters/battery.js.map +1 -0
  27. package/dist/homekit/adapters/camera-controls.js +527 -0
  28. package/dist/homekit/adapters/camera-controls.js.map +1 -0
  29. package/dist/homekit/adapters/camera-streaming.js +1515 -0
  30. package/dist/homekit/adapters/camera-streaming.js.map +1 -0
  31. package/dist/homekit/adapters/contact.js +138 -0
  32. package/dist/homekit/adapters/contact.js.map +1 -0
  33. package/dist/homekit/adapters/doorbell.js +57 -0
  34. package/dist/homekit/adapters/doorbell.js.map +1 -0
  35. package/dist/homekit/adapters/information.js +55 -0
  36. package/dist/homekit/adapters/information.js.map +1 -0
  37. package/dist/homekit/adapters/lock.js +489 -0
  38. package/dist/homekit/adapters/lock.js.map +1 -0
  39. package/dist/homekit/adapters/motion.js +100 -0
  40. package/dist/homekit/adapters/motion.js.map +1 -0
  41. package/dist/homekit/adapters/registry.js +26 -0
  42. package/dist/homekit/adapters/registry.js.map +1 -0
  43. package/dist/homekit/adapters/security-system.js +425 -0
  44. package/dist/homekit/adapters/security-system.js.map +1 -0
  45. package/dist/homekit/adapters/siren.js +136 -0
  46. package/dist/homekit/adapters/siren.js.map +1 -0
  47. package/dist/homekit/adapters/smart-light.js +662 -0
  48. package/dist/homekit/adapters/smart-light.js.map +1 -0
  49. package/dist/homekit/coverage-matrix.js +119 -0
  50. package/dist/homekit/coverage-matrix.js.map +1 -0
  51. package/dist/homekit/device-control.js +175 -0
  52. package/dist/homekit/device-control.js.map +1 -0
  53. package/dist/homekit/reconciler.js +319 -0
  54. package/dist/homekit/reconciler.js.map +1 -0
  55. package/dist/homekit/representation.js +23 -0
  56. package/dist/homekit/representation.js.map +1 -0
  57. package/dist/index.js +4 -9
  58. package/dist/index.js.map +1 -1
  59. package/dist/media/contracts.js +2 -0
  60. package/dist/media/contracts.js.map +1 -0
  61. package/dist/media/last-successful-image.js +208 -0
  62. package/dist/media/last-successful-image.js.map +1 -0
  63. package/dist/media/live-stream.js +1003 -0
  64. package/dist/media/live-stream.js.map +1 -0
  65. package/dist/media/recording.js +560 -0
  66. package/dist/media/recording.js.map +1 -0
  67. package/dist/media/session-budget.js +39 -0
  68. package/dist/media/session-budget.js.map +1 -0
  69. package/dist/media/snapshot.js +406 -0
  70. package/dist/media/snapshot.js.map +1 -0
  71. package/dist/media/station-live-sessions.js +101 -0
  72. package/dist/media/station-live-sessions.js.map +1 -0
  73. package/dist/platform.js +97 -809
  74. package/dist/platform.js.map +1 -1
  75. package/dist/runtime/channel.js +375 -0
  76. package/dist/runtime/channel.js.map +1 -0
  77. package/dist/runtime/owner.js +682 -0
  78. package/dist/runtime/owner.js.map +1 -0
  79. package/dist/runtime/sdk-client.js +211 -0
  80. package/dist/runtime/sdk-client.js.map +1 -0
  81. package/dist/runtime/tracker.js +211 -0
  82. package/dist/runtime/tracker.js.map +1 -0
  83. package/dist/settings.js +1 -43
  84. package/dist/settings.js.map +1 -1
  85. package/dist/storage.js +59 -0
  86. package/dist/storage.js.map +1 -0
  87. package/dist/ui/dashboard.js +181 -0
  88. package/dist/ui/dashboard.js.map +1 -0
  89. package/dist/ui/device-image.js +22 -0
  90. package/dist/ui/device-image.js.map +1 -0
  91. package/dist/ui/runtime-channel-client.js +305 -0
  92. package/dist/ui/runtime-channel-client.js.map +1 -0
  93. package/dist/ui/server.js +259 -0
  94. package/dist/ui/server.js.map +1 -0
  95. package/homebridge-ui/public/app.css +1902 -0
  96. package/homebridge-ui/public/assets/devices/clean/clean-T1250.webp +0 -0
  97. package/homebridge-ui/public/assets/devices/clean/clean-T2070.webp +0 -0
  98. package/homebridge-ui/public/assets/devices/clean/clean-T2080.webp +0 -0
  99. package/homebridge-ui/public/assets/devices/clean/clean-T2081.webp +0 -0
  100. package/homebridge-ui/public/assets/devices/clean/clean-T2103.webp +0 -0
  101. package/homebridge-ui/public/assets/devices/clean/clean-T2117.webp +0 -0
  102. package/homebridge-ui/public/assets/devices/clean/clean-T2118.webp +0 -0
  103. package/homebridge-ui/public/assets/devices/clean/clean-T2119.webp +0 -0
  104. package/homebridge-ui/public/assets/devices/clean/clean-T211A.webp +0 -0
  105. package/homebridge-ui/public/assets/devices/clean/clean-T2120.webp +0 -0
  106. package/homebridge-ui/public/assets/devices/clean/clean-T2123.webp +0 -0
  107. package/homebridge-ui/public/assets/devices/clean/clean-T2128.webp +0 -0
  108. package/homebridge-ui/public/assets/devices/clean/clean-T2130.webp +0 -0
  109. package/homebridge-ui/public/assets/devices/clean/clean-T2132.webp +0 -0
  110. package/homebridge-ui/public/assets/devices/clean/clean-T2150.webp +0 -0
  111. package/homebridge-ui/public/assets/devices/clean/clean-T2181.webp +0 -0
  112. package/homebridge-ui/public/assets/devices/clean/clean-T2182.webp +0 -0
  113. package/homebridge-ui/public/assets/devices/clean/clean-T2190.webp +0 -0
  114. package/homebridge-ui/public/assets/devices/clean/clean-T2192.webp +0 -0
  115. package/homebridge-ui/public/assets/devices/clean/clean-T2193.webp +0 -0
  116. package/homebridge-ui/public/assets/devices/clean/clean-T2194.webp +0 -0
  117. package/homebridge-ui/public/assets/devices/clean/clean-T2210.webp +0 -0
  118. package/homebridge-ui/public/assets/devices/clean/clean-T2250.webp +0 -0
  119. package/homebridge-ui/public/assets/devices/clean/clean-T2251.webp +0 -0
  120. package/homebridge-ui/public/assets/devices/clean/clean-T2252.webp +0 -0
  121. package/homebridge-ui/public/assets/devices/clean/clean-T2253.webp +0 -0
  122. package/homebridge-ui/public/assets/devices/clean/clean-T2254.webp +0 -0
  123. package/homebridge-ui/public/assets/devices/clean/clean-T2255.webp +0 -0
  124. package/homebridge-ui/public/assets/devices/clean/clean-T2256.webp +0 -0
  125. package/homebridge-ui/public/assets/devices/clean/clean-T2257.webp +0 -0
  126. package/homebridge-ui/public/assets/devices/clean/clean-T2258.webp +0 -0
  127. package/homebridge-ui/public/assets/devices/clean/clean-T2259.webp +0 -0
  128. package/homebridge-ui/public/assets/devices/clean/clean-T2261.webp +0 -0
  129. package/homebridge-ui/public/assets/devices/clean/clean-T2262.webp +0 -0
  130. package/homebridge-ui/public/assets/devices/clean/clean-T2266.webp +0 -0
  131. package/homebridge-ui/public/assets/devices/clean/clean-T2267.webp +0 -0
  132. package/homebridge-ui/public/assets/devices/clean/clean-T2268.webp +0 -0
  133. package/homebridge-ui/public/assets/devices/clean/clean-T2270.webp +0 -0
  134. package/homebridge-ui/public/assets/devices/clean/clean-T2272.webp +0 -0
  135. package/homebridge-ui/public/assets/devices/clean/clean-T2273.webp +0 -0
  136. package/homebridge-ui/public/assets/devices/clean/clean-T2276.webp +0 -0
  137. package/homebridge-ui/public/assets/devices/clean/clean-T2277.webp +0 -0
  138. package/homebridge-ui/public/assets/devices/clean/clean-T2278.webp +0 -0
  139. package/homebridge-ui/public/assets/devices/clean/clean-T2280.webp +0 -0
  140. package/homebridge-ui/public/assets/devices/clean/clean-T2292.webp +0 -0
  141. package/homebridge-ui/public/assets/devices/clean/clean-T2320.webp +0 -0
  142. package/homebridge-ui/public/assets/devices/clean/clean-T2351.webp +0 -0
  143. package/homebridge-ui/public/assets/devices/clean/clean-T2352.webp +0 -0
  144. package/homebridge-ui/public/assets/devices/clean/clean-T2353.webp +0 -0
  145. package/homebridge-ui/public/assets/devices/life/life-T8L00.webp +0 -0
  146. package/homebridge-ui/public/assets/devices/life/life-T8L01.webp +0 -0
  147. package/homebridge-ui/public/assets/devices/life/life-T8L02.webp +0 -0
  148. package/homebridge-ui/public/assets/devices/life/life-T8L04.webp +0 -0
  149. package/homebridge-ui/public/assets/devices/life/life-T8L10.webp +0 -0
  150. package/homebridge-ui/public/assets/devices/life/life-T8L20.webp +0 -0
  151. package/homebridge-ui/public/assets/devices/life/life-T8L30.webp +0 -0
  152. package/homebridge-ui/public/assets/devices/life/life-T8L40.webp +0 -0
  153. package/homebridge-ui/public/assets/devices/mower/mower-T2801.webp +0 -0
  154. package/homebridge-ui/public/assets/devices/mower/mower-T280B.webp +0 -0
  155. package/homebridge-ui/public/assets/devices/mower/mower-T2880.webp +0 -0
  156. package/homebridge-ui/public/assets/devices/security/security-T7400.webp +0 -0
  157. package/homebridge-ui/public/assets/devices/security/security-T7401.webp +0 -0
  158. package/homebridge-ui/public/assets/devices/security/security-T8001.webp +0 -0
  159. package/homebridge-ui/public/assets/devices/security/security-T8002.webp +0 -0
  160. package/homebridge-ui/public/assets/devices/security/security-T8010.webp +0 -0
  161. package/homebridge-ui/public/assets/devices/security/security-T8021.webp +0 -0
  162. package/homebridge-ui/public/assets/devices/security/security-T8023.webp +0 -0
  163. package/homebridge-ui/public/assets/devices/security/security-T8025.webp +0 -0
  164. package/homebridge-ui/public/assets/devices/security/security-T8030.webp +0 -0
  165. package/homebridge-ui/public/assets/devices/security/security-T8110.webp +0 -0
  166. package/homebridge-ui/public/assets/devices/security/security-T8111.webp +0 -0
  167. package/homebridge-ui/public/assets/devices/security/security-T8112.webp +0 -0
  168. package/homebridge-ui/public/assets/devices/security/security-T8113.webp +0 -0
  169. package/homebridge-ui/public/assets/devices/security/security-T8114.webp +0 -0
  170. package/homebridge-ui/public/assets/devices/security/security-T8122.webp +0 -0
  171. package/homebridge-ui/public/assets/devices/security/security-T8123.webp +0 -0
  172. package/homebridge-ui/public/assets/devices/security/security-T8124.webp +0 -0
  173. package/homebridge-ui/public/assets/devices/security/security-T8130.webp +0 -0
  174. package/homebridge-ui/public/assets/devices/security/security-T8131.webp +0 -0
  175. package/homebridge-ui/public/assets/devices/security/security-T8134.webp +0 -0
  176. package/homebridge-ui/public/assets/devices/security/security-T8140.webp +0 -0
  177. package/homebridge-ui/public/assets/devices/security/security-T8141.webp +0 -0
  178. package/homebridge-ui/public/assets/devices/security/security-T8150.webp +0 -0
  179. package/homebridge-ui/public/assets/devices/security/security-T8151.webp +0 -0
  180. package/homebridge-ui/public/assets/devices/security/security-T8152.webp +0 -0
  181. package/homebridge-ui/public/assets/devices/security/security-T8153.webp +0 -0
  182. package/homebridge-ui/public/assets/devices/security/security-T8160.webp +0 -0
  183. package/homebridge-ui/public/assets/devices/security/security-T8161.webp +0 -0
  184. package/homebridge-ui/public/assets/devices/security/security-T8162.webp +0 -0
  185. package/homebridge-ui/public/assets/devices/security/security-T8170.webp +0 -0
  186. package/homebridge-ui/public/assets/devices/security/security-T8171.webp +0 -0
  187. package/homebridge-ui/public/assets/devices/security/security-T8172.webp +0 -0
  188. package/homebridge-ui/public/assets/devices/security/security-T8173.webp +0 -0
  189. package/homebridge-ui/public/assets/devices/security/security-T8200.webp +0 -0
  190. package/homebridge-ui/public/assets/devices/security/security-T8200X.webp +0 -0
  191. package/homebridge-ui/public/assets/devices/security/security-T8201.webp +0 -0
  192. package/homebridge-ui/public/assets/devices/security/security-T8203.webp +0 -0
  193. package/homebridge-ui/public/assets/devices/security/security-T8210.webp +0 -0
  194. package/homebridge-ui/public/assets/devices/security/security-T8213.webp +0 -0
  195. package/homebridge-ui/public/assets/devices/security/security-T8214.webp +0 -0
  196. package/homebridge-ui/public/assets/devices/security/security-T8222.webp +0 -0
  197. package/homebridge-ui/public/assets/devices/security/security-T8400.webp +0 -0
  198. package/homebridge-ui/public/assets/devices/security/security-T8401.webp +0 -0
  199. package/homebridge-ui/public/assets/devices/security/security-T8410.webp +0 -0
  200. package/homebridge-ui/public/assets/devices/security/security-T8411.webp +0 -0
  201. package/homebridge-ui/public/assets/devices/security/security-T8414.webp +0 -0
  202. package/homebridge-ui/public/assets/devices/security/security-T8416.webp +0 -0
  203. package/homebridge-ui/public/assets/devices/security/security-T8417.webp +0 -0
  204. package/homebridge-ui/public/assets/devices/security/security-T8419.webp +0 -0
  205. package/homebridge-ui/public/assets/devices/security/security-T8419N.webp +0 -0
  206. package/homebridge-ui/public/assets/devices/security/security-T8420.webp +0 -0
  207. package/homebridge-ui/public/assets/devices/security/security-T8420X.webp +0 -0
  208. package/homebridge-ui/public/assets/devices/security/security-T8422.webp +0 -0
  209. package/homebridge-ui/public/assets/devices/security/security-T8423.webp +0 -0
  210. package/homebridge-ui/public/assets/devices/security/security-T8424.webp +0 -0
  211. package/homebridge-ui/public/assets/devices/security/security-T8425.webp +0 -0
  212. package/homebridge-ui/public/assets/devices/security/security-T8441.webp +0 -0
  213. package/homebridge-ui/public/assets/devices/security/security-T8442.webp +0 -0
  214. package/homebridge-ui/public/assets/devices/security/security-T8452.webp +0 -0
  215. package/homebridge-ui/public/assets/devices/security/security-T84A0.webp +0 -0
  216. package/homebridge-ui/public/assets/devices/security/security-T84A1.webp +0 -0
  217. package/homebridge-ui/public/assets/devices/security/security-T8500.webp +0 -0
  218. package/homebridge-ui/public/assets/devices/security/security-T8502.webp +0 -0
  219. package/homebridge-ui/public/assets/devices/security/security-T8503.webp +0 -0
  220. package/homebridge-ui/public/assets/devices/security/security-T8504.webp +0 -0
  221. package/homebridge-ui/public/assets/devices/security/security-T8506.webp +0 -0
  222. package/homebridge-ui/public/assets/devices/security/security-T8510.webp +0 -0
  223. package/homebridge-ui/public/assets/devices/security/security-T8510P.webp +0 -0
  224. package/homebridge-ui/public/assets/devices/security/security-T8520.webp +0 -0
  225. package/homebridge-ui/public/assets/devices/security/security-T8520P.webp +0 -0
  226. package/homebridge-ui/public/assets/devices/security/security-T8530.webp +0 -0
  227. package/homebridge-ui/public/assets/devices/security/security-T8531.webp +0 -0
  228. package/homebridge-ui/public/assets/devices/security/security-T85D0.webp +0 -0
  229. package/homebridge-ui/public/assets/devices/security/security-T85L0.webp +0 -0
  230. package/homebridge-ui/public/assets/devices/security/security-T85P0.webp +0 -0
  231. package/homebridge-ui/public/assets/devices/security/security-T85V0.webp +0 -0
  232. package/homebridge-ui/public/assets/devices/security/security-T8600.webp +0 -0
  233. package/homebridge-ui/public/assets/devices/security/security-T86P2.webp +0 -0
  234. package/homebridge-ui/public/assets/devices/security/security-T8790.webp +0 -0
  235. package/homebridge-ui/public/assets/devices/security/security-T87B0.webp +0 -0
  236. package/homebridge-ui/public/assets/devices/security/security-T87B2.webp +0 -0
  237. package/homebridge-ui/public/assets/devices/security/security-T8900.webp +0 -0
  238. package/homebridge-ui/public/assets/devices/security/security-T8910.webp +0 -0
  239. package/homebridge-ui/public/assets/devices/security/security-T8920.webp +0 -0
  240. package/homebridge-ui/public/assets/devices/security/security-T8960.webp +0 -0
  241. package/homebridge-ui/public/assets/devices/security/security-T8B0.webp +0 -0
  242. package/homebridge-ui/public/assets/devices/security/security-T8E00.webp +0 -0
  243. package/homebridge-ui/public/assets/devices/security/security-T8N00.webp +0 -0
  244. package/homebridge-ui/public/assets/devices/security/security-T8W11C.webp +0 -0
  245. package/homebridge-ui/public/assets/devices/security/security-T90F0.webp +0 -0
  246. package/homebridge-ui/public/assets/devices/security/security-T90M0.webp +0 -0
  247. package/homebridge-ui/public/assets/devices/security/security-T90R0.webp +0 -0
  248. package/homebridge-ui/public/assets/devices/security/security-T90S0.webp +0 -0
  249. package/homebridge-ui/public/assets/icons/inventory.svg +1 -1
  250. package/homebridge-ui/public/assets/icons/troubleshoot.svg +1 -0
  251. package/homebridge-ui/public/assets/icons/warning.svg +1 -1
  252. package/homebridge-ui/public/assets/logo-dark.svg +10 -0
  253. package/homebridge-ui/public/assets/logo.svg +10 -0
  254. package/homebridge-ui/public/i18n/en.json +218 -0
  255. package/homebridge-ui/public/i18n/fr.json +218 -0
  256. package/homebridge-ui/public/index.html +377 -28
  257. package/homebridge-ui/public/js/app.js +1065 -0
  258. package/homebridge-ui/public/js/dashboard.js +385 -0
  259. package/homebridge-ui/public/js/legacy-settings.js +37 -0
  260. package/homebridge-ui/public/js/profile-wizard.js +68 -0
  261. package/homebridge-ui/server.js +2 -1393
  262. package/i18n/runtime/en.json +100 -0
  263. package/media/Snapshot-Unavailable.jpg +0 -0
  264. package/media/Snapshot-black.jpg +0 -0
  265. package/media/camera-disabled.jpg +0 -0
  266. package/media/camera-offline.jpg +0 -0
  267. package/package.json +29 -24
  268. package/.claude/CLAUDE.md +0 -175
  269. package/.claude/PRD.md +0 -241
  270. package/.claude/docs/debug-recording-lessons.md +0 -91
  271. package/.claude/docs/hksv-recording-fix.md +0 -160
  272. package/.claude/skills/architect/SKILL.md +0 -76
  273. package/.claude/skills/developer/SKILL.md +0 -59
  274. package/.claude/skills/new-device-support/SKILL.md +0 -212
  275. package/.claude/skills/new-device-support/check-device.mjs +0 -363
  276. package/.claude/skills/new-device-support/map-properties.mjs +0 -450
  277. package/.claude/skills/new-device-support/verify-device.mjs +0 -272
  278. package/.claude/skills/planner/SKILL.md +0 -100
  279. package/.claude/skills/qa/SKILL.md +0 -79
  280. package/.claude/skills/support/SKILL.md +0 -175
  281. package/.prettierignore +0 -7
  282. package/.prettierrc.json +0 -7
  283. package/FUNDING.yml +0 -1
  284. package/dist/accessories/AutoSyncStationAccessory.js +0 -157
  285. package/dist/accessories/AutoSyncStationAccessory.js.map +0 -1
  286. package/dist/accessories/BaseAccessory.js +0 -261
  287. package/dist/accessories/BaseAccessory.js.map +0 -1
  288. package/dist/accessories/CameraAccessory.js +0 -459
  289. package/dist/accessories/CameraAccessory.js.map +0 -1
  290. package/dist/accessories/Device.js +0 -65
  291. package/dist/accessories/Device.js.map +0 -1
  292. package/dist/accessories/EntrySensorAccessory.js +0 -48
  293. package/dist/accessories/EntrySensorAccessory.js.map +0 -1
  294. package/dist/accessories/GarageDoorAccessory.js +0 -160
  295. package/dist/accessories/GarageDoorAccessory.js.map +0 -1
  296. package/dist/accessories/LockAccessory.js +0 -142
  297. package/dist/accessories/LockAccessory.js.map +0 -1
  298. package/dist/accessories/MotionSensorAccessory.js +0 -48
  299. package/dist/accessories/MotionSensorAccessory.js.map +0 -1
  300. package/dist/accessories/SmartDropAccessory.js +0 -141
  301. package/dist/accessories/SmartDropAccessory.js.map +0 -1
  302. package/dist/accessories/StationAccessory.js +0 -373
  303. package/dist/accessories/StationAccessory.js.map +0 -1
  304. package/dist/config.js +0 -26
  305. package/dist/config.js.map +0 -1
  306. package/dist/controller/DebugRecordingManager.js +0 -402
  307. package/dist/controller/DebugRecordingManager.js.map +0 -1
  308. package/dist/controller/LocalLivestreamManager.js +0 -371
  309. package/dist/controller/LocalLivestreamManager.js.map +0 -1
  310. package/dist/controller/recordingDelegate.js +0 -252
  311. package/dist/controller/recordingDelegate.js.map +0 -1
  312. package/dist/controller/snapshotDelegate.js +0 -348
  313. package/dist/controller/snapshotDelegate.js.map +0 -1
  314. package/dist/controller/streamingDelegate.js +0 -389
  315. package/dist/controller/streamingDelegate.js.map +0 -1
  316. package/dist/interfaces.js +0 -2
  317. package/dist/interfaces.js.map +0 -1
  318. package/dist/media/Snapshot-Unavailable.png +0 -0
  319. package/dist/media/Snapshot-Unavailable.xcf +0 -0
  320. package/dist/media/Snapshot-black.png +0 -0
  321. package/dist/media/camera-disabled.png +0 -0
  322. package/dist/media/camera-offline.png +0 -0
  323. package/dist/media/media/Snapshot-Unavailable.png +0 -0
  324. package/dist/media/media/Snapshot-Unavailable.xcf +0 -0
  325. package/dist/media/media/Snapshot-black.png +0 -0
  326. package/dist/media/media/camera-disabled.png +0 -0
  327. package/dist/media/media/camera-offline.png +0 -0
  328. package/dist/utils/Talkback.js +0 -162
  329. package/dist/utils/Talkback.js.map +0 -1
  330. package/dist/utils/accessoriesStore.js +0 -210
  331. package/dist/utils/accessoriesStore.js.map +0 -1
  332. package/dist/utils/configTypes.js +0 -38
  333. package/dist/utils/configTypes.js.map +0 -1
  334. package/dist/utils/ffmpeg.js +0 -1111
  335. package/dist/utils/ffmpeg.js.map +0 -1
  336. package/dist/utils/interfaces.js +0 -8
  337. package/dist/utils/interfaces.js.map +0 -1
  338. package/dist/utils/utils.js +0 -224
  339. package/dist/utils/utils.js.map +0 -1
  340. package/dist/version.js +0 -2
  341. package/dist/version.js.map +0 -1
  342. package/homebridge-eufy-security.png +0 -0
  343. package/homebridge-ui/diagnosticsCrypto.js +0 -112
  344. package/homebridge-ui/public/app.js +0 -237
  345. package/homebridge-ui/public/assets/devices/4g_lte_cam_s330_large.png +0 -0
  346. package/homebridge-ui/public/assets/devices/4g_lte_starlight_large.jpg +0 -0
  347. package/homebridge-ui/public/assets/devices/BATTERY_DOORBELL_C30.png +0 -0
  348. package/homebridge-ui/public/assets/devices/BATTERY_DOORBELL_C31.png +0 -0
  349. package/homebridge-ui/public/assets/devices/batterydoorbell1080p_large.jpg +0 -0
  350. package/homebridge-ui/public/assets/devices/batterydoorbell2k_large.png +0 -0
  351. package/homebridge-ui/public/assets/devices/batterydoorbell2kdual_large.jpg +0 -0
  352. package/homebridge-ui/public/assets/devices/batterydoorbell_e340_large.png +0 -0
  353. package/homebridge-ui/public/assets/devices/eufy-security-client.png +0 -0
  354. package/homebridge-ui/public/assets/devices/eufyCamS4_large.png +0 -0
  355. package/homebridge-ui/public/assets/devices/eufycam2_large.png +0 -0
  356. package/homebridge-ui/public/assets/devices/eufycam2c_large.jpg +0 -0
  357. package/homebridge-ui/public/assets/devices/eufycam2cpro_large.jpg +0 -0
  358. package/homebridge-ui/public/assets/devices/eufycam2pro_large.jpg +0 -0
  359. package/homebridge-ui/public/assets/devices/eufycam3_large.jpg +0 -0
  360. package/homebridge-ui/public/assets/devices/eufycam3c_large.jpg +0 -0
  361. package/homebridge-ui/public/assets/devices/eufycam3pro_large.png +0 -0
  362. package/homebridge-ui/public/assets/devices/eufycam_large.jpg +0 -0
  363. package/homebridge-ui/public/assets/devices/eufycame330_large.jpg +0 -0
  364. package/homebridge-ui/public/assets/devices/flood_freeze_sensor_e20_t90F0_large.png +0 -0
  365. package/homebridge-ui/public/assets/devices/floodlight2_large.jpg +0 -0
  366. package/homebridge-ui/public/assets/devices/floodlight2pro_large.jpg +0 -0
  367. package/homebridge-ui/public/assets/devices/floodlight_large.jpg +0 -0
  368. package/homebridge-ui/public/assets/devices/floodlightcame340_large.jpg +0 -0
  369. package/homebridge-ui/public/assets/devices/garage_camera_t8452_large.png +0 -0
  370. package/homebridge-ui/public/assets/devices/homebase2_large.png +0 -0
  371. package/homebridge-ui/public/assets/devices/homebase3_large.png +0 -0
  372. package/homebridge-ui/public/assets/devices/homebase_large.jpg +0 -0
  373. package/homebridge-ui/public/assets/devices/homebasemini_large.png +0 -0
  374. package/homebridge-ui/public/assets/devices/indoorcamC210_large.png +0 -0
  375. package/homebridge-ui/public/assets/devices/indoorcamC220_large.png +0 -0
  376. package/homebridge-ui/public/assets/devices/indoorcamC220_t8419N_large.png +0 -0
  377. package/homebridge-ui/public/assets/devices/indoorcamE30_large.png +0 -0
  378. package/homebridge-ui/public/assets/devices/indoorcamc120_large.png +0 -0
  379. package/homebridge-ui/public/assets/devices/indoorcammini_large.jpg +0 -0
  380. package/homebridge-ui/public/assets/devices/indoorcamp24_large.png +0 -0
  381. package/homebridge-ui/public/assets/devices/indoorcams350_large.png +0 -0
  382. package/homebridge-ui/public/assets/devices/keypad_large.png +0 -0
  383. package/homebridge-ui/public/assets/devices/minibase_chime_T8023_large.jpg +0 -0
  384. package/homebridge-ui/public/assets/devices/motionsensor_large.png +0 -0
  385. package/homebridge-ui/public/assets/devices/nvr_s4_max_T8N00_large.png +0 -0
  386. package/homebridge-ui/public/assets/devices/pir_sensor_e20_large.png +0 -0
  387. package/homebridge-ui/public/assets/devices/poe_bullet_ptz_cam_s4_T8E00_large.png +0 -0
  388. package/homebridge-ui/public/assets/devices/sensor_large.png +0 -0
  389. package/homebridge-ui/public/assets/devices/siren_sensor_e20_large.png +0 -0
  390. package/homebridge-ui/public/assets/devices/smartdrop_t8790_large.png +0 -0
  391. package/homebridge-ui/public/assets/devices/smartlock_t8500_large.png +0 -0
  392. package/homebridge-ui/public/assets/devices/smartlock_t8500_wifibridge_large.jpg +0 -0
  393. package/homebridge-ui/public/assets/devices/smartlock_t8503_large.png +0 -0
  394. package/homebridge-ui/public/assets/devices/smartlock_t8504_large.jpg +0 -0
  395. package/homebridge-ui/public/assets/devices/smartlock_t8510P_t8520P_large.png +0 -0
  396. package/homebridge-ui/public/assets/devices/smartlock_t85D0_large.png +0 -0
  397. package/homebridge-ui/public/assets/devices/smartlock_t85L0_large.png +0 -0
  398. package/homebridge-ui/public/assets/devices/smartlock_t85P0_large.png +0 -0
  399. package/homebridge-ui/public/assets/devices/smartlock_t85V0_large.png +0 -0
  400. package/homebridge-ui/public/assets/devices/smartlock_touch_and_wifi_t8502_large.png +0 -0
  401. package/homebridge-ui/public/assets/devices/smartlock_touch_and_wifi_t8506_large.png +0 -0
  402. package/homebridge-ui/public/assets/devices/smartlock_touch_and_wifi_t8520_large.png +0 -0
  403. package/homebridge-ui/public/assets/devices/smartlock_touch_t8510_large.png +0 -0
  404. package/homebridge-ui/public/assets/devices/smartlock_touch_t8510_wifibridge_large.jpg +0 -0
  405. package/homebridge-ui/public/assets/devices/smartlock_video_t8530_large.png +0 -0
  406. package/homebridge-ui/public/assets/devices/smartlock_video_t8531_large.png +0 -0
  407. package/homebridge-ui/public/assets/devices/smartlockwifibridge_t8021_large.jpg +0 -0
  408. package/homebridge-ui/public/assets/devices/smartsafe_s10_t7400_large.png +0 -0
  409. package/homebridge-ui/public/assets/devices/smartsafe_s12_t7401_large.png +0 -0
  410. package/homebridge-ui/public/assets/devices/smarttrack_card_t87B2_large.png +0 -0
  411. package/homebridge-ui/public/assets/devices/smarttrack_link_t87B0_large.png +0 -0
  412. package/homebridge-ui/public/assets/devices/smoke_alarm_e10_t90S0_large.png +0 -0
  413. package/homebridge-ui/public/assets/devices/solocamc210_large.jpg +0 -0
  414. package/homebridge-ui/public/assets/devices/solocamc35_large.png +0 -0
  415. package/homebridge-ui/public/assets/devices/solocame20_large.jpg +0 -0
  416. package/homebridge-ui/public/assets/devices/solocame30_large.png +0 -0
  417. package/homebridge-ui/public/assets/devices/solocame40_large.jpg +0 -0
  418. package/homebridge-ui/public/assets/devices/solocame42_large.png +0 -0
  419. package/homebridge-ui/public/assets/devices/solocaml20_large.jpg +0 -0
  420. package/homebridge-ui/public/assets/devices/solocams220_large.jpg +0 -0
  421. package/homebridge-ui/public/assets/devices/solocams340_large.png +0 -0
  422. package/homebridge-ui/public/assets/devices/solocams40_large.jpg +0 -0
  423. package/homebridge-ui/public/assets/devices/soloindoorcamc24_large.jpg +0 -0
  424. package/homebridge-ui/public/assets/devices/solooutdoorcamc22_large.png +0 -0
  425. package/homebridge-ui/public/assets/devices/solooutdoorcamc24_large.jpg +0 -0
  426. package/homebridge-ui/public/assets/devices/unknown.png +0 -0
  427. package/homebridge-ui/public/assets/devices/walllight_s100_large.png +0 -0
  428. package/homebridge-ui/public/assets/devices/walllight_s120_large.png +0 -0
  429. package/homebridge-ui/public/assets/devices/water_freeze_sensor_t8920_large.png +0 -0
  430. package/homebridge-ui/public/assets/devices/wireddoorbell1080p_large.jpg +0 -0
  431. package/homebridge-ui/public/assets/devices/wireddoorbell2k_large.png +0 -0
  432. package/homebridge-ui/public/assets/devices/wireddoorbelldual_large.jpg +0 -0
  433. package/homebridge-ui/public/assets/icons/attach.svg +0 -1
  434. package/homebridge-ui/public/assets/icons/battery_0.svg +0 -1
  435. package/homebridge-ui/public/assets/icons/battery_1.svg +0 -1
  436. package/homebridge-ui/public/assets/icons/battery_2.svg +0 -1
  437. package/homebridge-ui/public/assets/icons/battery_3.svg +0 -1
  438. package/homebridge-ui/public/assets/icons/battery_4.svg +0 -1
  439. package/homebridge-ui/public/assets/icons/battery_5.svg +0 -1
  440. package/homebridge-ui/public/assets/icons/battery_6.svg +0 -1
  441. package/homebridge-ui/public/assets/icons/copy.svg +0 -1
  442. package/homebridge-ui/public/assets/icons/delete.svg +0 -1
  443. package/homebridge-ui/public/assets/icons/download.svg +0 -1
  444. package/homebridge-ui/public/assets/icons/info.svg +0 -1
  445. package/homebridge-ui/public/assets/icons/refresh.svg +0 -1
  446. package/homebridge-ui/public/assets/icons/satellite_alt.svg +0 -1
  447. package/homebridge-ui/public/assets/icons/solar_power.svg +0 -1
  448. package/homebridge-ui/public/components/device-card.js +0 -174
  449. package/homebridge-ui/public/components/guard-modes.js +0 -88
  450. package/homebridge-ui/public/components/number-input.js +0 -121
  451. package/homebridge-ui/public/components/select.js +0 -73
  452. package/homebridge-ui/public/components/toggle.js +0 -68
  453. package/homebridge-ui/public/services/api.js +0 -250
  454. package/homebridge-ui/public/services/config.js +0 -144
  455. package/homebridge-ui/public/style.css +0 -818
  456. package/homebridge-ui/public/utils/countries.js +0 -73
  457. package/homebridge-ui/public/utils/device-images.js +0 -102
  458. package/homebridge-ui/public/utils/helpers.js +0 -115
  459. package/homebridge-ui/public/views/dashboard.js +0 -226
  460. package/homebridge-ui/public/views/device-detail.js +0 -654
  461. package/homebridge-ui/public/views/diagnostics.js +0 -402
  462. package/homebridge-ui/public/views/login.js +0 -650
  463. package/homebridge-ui/public/views/recordings.js +0 -319
  464. package/homebridge-ui/public/views/settings.js +0 -197
  465. package/homebridge-ui/public/views/unsupported-detail.js +0 -296
  466. package/media/Snapshot-Unavailable.png +0 -0
  467. package/media/Snapshot-Unavailable.xcf +0 -0
  468. package/media/Snapshot-black.png +0 -0
  469. package/media/camera-disabled.png +0 -0
  470. package/media/camera-offline.png +0 -0
  471. package/scripts/decrypt-diagnostics.mjs +0 -257
@@ -0,0 +1,682 @@
1
+ import { join } from 'node:path';
2
+ import { AccountOwnership } from '../account/ownership.js';
3
+ import { AccountSessionPersistence } from '../account/persistence.js';
4
+ import { armDiagnosticsAuthorization, PLUGIN_VERSION, reportRuntimeNotice, } from '../diagnostics.js';
5
+ import { indexDeviceEvidence } from '../device/member-evidence.js';
6
+ import { batteryLevel, cameraEnablement } from '../device/member-trust.js';
7
+ import { parseCompleteDeviceSnapshot } from '../device/snapshot.js';
8
+ import { RuntimeChannelServer, runtimeChannelEndpointForHost, } from './channel.js';
9
+ import { RuntimeTracker, runtimeStatusFor } from './tracker.js';
10
+ /**
11
+ * How long a runtime that stood down on request keeps trying to take the account session back.
12
+ *
13
+ * It outlasts the interactive authentication a stand-down is requested for, whose own deadline is five minutes.
14
+ */
15
+ const REARM_WINDOW_MS = 15 * 60_000;
16
+ /** How long between attempts to take the account session back after standing down on request. */
17
+ const REARM_INTERVAL_MS = 15_000;
18
+ /** Owns the long-lived SDK session, canonical registry, and runtime state transitions. */
19
+ /**
20
+ * Whether a process with this title is a Homebridge child bridge, and so may end itself to be replaced.
21
+ *
22
+ * Homebridge titles a child bridge `homebridge: <plugin name>` and leaves the main process plain `homebridge`.
23
+ * Only a child bridge is respawned when it ends; ending the main process would stop every other plugin with it.
24
+ * A title this does not recognise answers false, so an unfamiliar host loses the offer rather than the bridge.
25
+ */
26
+ export function isChildBridgeTitle(title) {
27
+ return title.startsWith('homebridge: ');
28
+ }
29
+ export class RuntimeOwner {
30
+ log;
31
+ configuredConfig;
32
+ clientFactory;
33
+ client;
34
+ startPromise;
35
+ stopPromise;
36
+ statusPublisher;
37
+ statusPublisherActive = false;
38
+ channel;
39
+ ownership;
40
+ accountScope;
41
+ runtimeLease;
42
+ pendingOwnership;
43
+ runtimeState = 'stopped';
44
+ registryView;
45
+ registryVersion = 0;
46
+ registryListeners = new Set();
47
+ eventListeners = new Set();
48
+ unconfirmedListeners = new Set();
49
+ stateListeners = new Set();
50
+ stopping = false;
51
+ cleanupTerminalState;
52
+ storageRoot;
53
+ persistence;
54
+ shutdownTimeoutMs;
55
+ rearmWindowMs;
56
+ rearmIntervalMs;
57
+ shuttingDown = false;
58
+ standingDown = false;
59
+ rearming = false;
60
+ rearmTimer;
61
+ rearmGeneration;
62
+ rearmBlocked = false;
63
+ activeGeneration;
64
+ constructor(log, configuredConfig, clientFactory, options = {}) {
65
+ this.log = log;
66
+ this.configuredConfig = configuredConfig;
67
+ this.clientFactory = clientFactory;
68
+ this.storageRoot = options.storageRoot;
69
+ this.shutdownTimeoutMs = options.shutdownTimeoutMs ?? 10_000;
70
+ this.rearmWindowMs = options.rearmWindowMs ?? REARM_WINDOW_MS;
71
+ this.rearmIntervalMs = options.rearmIntervalMs ?? REARM_INTERVAL_MS;
72
+ this.ownership = options.ownership;
73
+ this.statusPublisher = options.statusPublisher;
74
+ this.channel = options.channel;
75
+ if (this.storageRoot) {
76
+ this.persistence = options.persistence ?? new AccountSessionPersistence(join(this.storageRoot, 'accounts'));
77
+ }
78
+ else {
79
+ this.persistence = options.persistence;
80
+ this.client = clientFactory(configuredConfig);
81
+ }
82
+ }
83
+ start() {
84
+ if (this.stopPromise) {
85
+ return Promise.resolve();
86
+ }
87
+ this.startPromise ??= this.startClient();
88
+ return this.startPromise;
89
+ }
90
+ currentRegistry() {
91
+ return this.registryView;
92
+ }
93
+ currentState() {
94
+ return this.runtimeState;
95
+ }
96
+ currentAvailability(serial) {
97
+ return this.client?.deviceAvailability?.(serial);
98
+ }
99
+ subscribeRegistry(listener) {
100
+ this.registryListeners.add(listener);
101
+ return () => this.registryListeners.delete(listener);
102
+ }
103
+ subscribeEvents(listener) {
104
+ this.eventListeners.add(listener);
105
+ return () => this.eventListeners.delete(listener);
106
+ }
107
+ /**
108
+ * Follows a write the device acknowledged and never applied. Separate from the event subscription because
109
+ * it is an operation outcome and not device state: nothing about the device changed, which is the news.
110
+ */
111
+ subscribeUnconfirmedWrites(listener) {
112
+ this.unconfirmedListeners.add(listener);
113
+ return () => this.unconfirmedListeners.delete(listener);
114
+ }
115
+ subscribeState(listener) {
116
+ this.stateListeners.add(listener);
117
+ return () => this.stateListeners.delete(listener);
118
+ }
119
+ stop() {
120
+ this.shuttingDown = true;
121
+ this.clearRearm();
122
+ return this.stopOwnership();
123
+ }
124
+ /**
125
+ * Releases the account session so another process may own it, and takes it back when it can.
126
+ *
127
+ * Reports whether a stand-down was started, which is acceptance and not completion: the release runs the same
128
+ * bounded cleanup a shutdown runs, and the endpoint closes inside the release guard. Only the lease is
129
+ * evidence that the session is free. A runtime holding no lease, one already stopping, and one already
130
+ * standing down each refuse, so the caller behaves as it does without this channel.
131
+ */
132
+ standDown() {
133
+ if (!this.storageRoot || !this.runtimeLease || this.stopping || this.standingDown || this.shuttingDown) {
134
+ return false;
135
+ }
136
+ this.standingDown = true;
137
+ this.rearmGeneration = this.activeGeneration;
138
+ void this.stopOwnership().finally(() => {
139
+ this.standingDown = false;
140
+ if (!this.shuttingDown) {
141
+ this.scheduleRearm(Date.now() + this.rearmWindowMs);
142
+ }
143
+ });
144
+ return true;
145
+ }
146
+ stopOwnership() {
147
+ this.cleanupTerminalState = 'stopped';
148
+ if (!this.stopPromise) {
149
+ this.stopping = true;
150
+ if (this.runtimeState !== 'owner-conflict') {
151
+ this.transitionTo('stopping');
152
+ if (this.statusPublisherActive) {
153
+ this.statusPublisher?.update('stopping');
154
+ }
155
+ }
156
+ this.stopPromise = this.cleanupWithinDeadline('stopped');
157
+ }
158
+ else if (this.runtimeState !== 'owner-conflict' &&
159
+ this.runtimeState !== 'failed' &&
160
+ this.runtimeState !== 'stopping') {
161
+ this.transitionTo('stopping');
162
+ if (this.statusPublisherActive) {
163
+ this.statusPublisher?.update('stopping');
164
+ }
165
+ }
166
+ return this.stopPromise.then(() => {
167
+ if (this.runtimeState !== 'owner-conflict' && this.runtimeState !== 'failed' && this.runtimeState !== 'stopped') {
168
+ if (this.statusPublisherActive) {
169
+ this.statusPublisher?.stop();
170
+ }
171
+ this.transitionTo('stopped');
172
+ }
173
+ });
174
+ }
175
+ /**
176
+ * Schedules the next attempt to take the account session back, or gives up once the window has passed.
177
+ *
178
+ * Nothing can tell this runtime when the session is free: the endpoint's lifetime is the lease, so the channel
179
+ * is gone for exactly as long as the reason to re-arm lasts, and the process that asked may itself be gone.
180
+ */
181
+ scheduleRearm(deadline) {
182
+ this.rearming = true;
183
+ if (Date.now() >= deadline) {
184
+ this.clearRearm();
185
+ reportRuntimeNotice(this.log, 'stand-down-not-rearmed');
186
+ return;
187
+ }
188
+ this.rearmTimer = setTimeout(() => void this.attemptRearm(deadline), this.rearmIntervalMs);
189
+ this.rearmTimer.unref();
190
+ }
191
+ /**
192
+ * Takes the account session back if it is free, and schedules another attempt only if the lease is still held.
193
+ *
194
+ * A held lease is the ordinary case for as long as the authentication this runtime stood down for is running,
195
+ * so it reports nothing and leaves this runtime stopped, which is what it is. Any other outcome is a state this
196
+ * runtime reached and reported, and retrying it would report it again on every interval of the window.
197
+ */
198
+ async attemptRearm(deadline) {
199
+ this.rearmTimer = undefined;
200
+ if (this.shuttingDown || !this.rearming) {
201
+ return;
202
+ }
203
+ this.rearmBlocked = false;
204
+ this.resetForRearm();
205
+ await this.start();
206
+ if (!this.rearming) {
207
+ return;
208
+ }
209
+ if (!this.rearmBlocked) {
210
+ this.clearRearm();
211
+ return;
212
+ }
213
+ this.scheduleRearm(deadline);
214
+ }
215
+ /**
216
+ * Returns this owner to the state a start begins from, keeping what a stopped runtime is defined to retain.
217
+ *
218
+ * The latest complete registry view and its version survive, because HomeKit's topology is built from them
219
+ * and only a later complete inventory may replace them. Everything a cleanup consumed is cleared so that one
220
+ * cycle's cleanup cannot be a second cycle's, which is what keeps a lease from being released twice.
221
+ */
222
+ resetForRearm() {
223
+ this.startPromise = undefined;
224
+ this.stopPromise = undefined;
225
+ this.stopping = false;
226
+ this.cleanupTerminalState = undefined;
227
+ this.statusPublisherActive = false;
228
+ this.pendingOwnership = undefined;
229
+ this.client = undefined;
230
+ }
231
+ clearRearm() {
232
+ clearTimeout(this.rearmTimer);
233
+ this.rearmTimer = undefined;
234
+ this.rearming = false;
235
+ this.rearmBlocked = false;
236
+ this.rearmGeneration = undefined;
237
+ }
238
+ async startClient() {
239
+ try {
240
+ const { active, config } = await this.activeAccount();
241
+ this.activeGeneration = active?.generation;
242
+ if (this.stopping) {
243
+ return;
244
+ }
245
+ if (this.rearming && this.activeGeneration !== this.rearmGeneration) {
246
+ reportRuntimeNotice(this.log, 'stand-down-account-replaced');
247
+ this.clearRearm();
248
+ return;
249
+ }
250
+ if (this.storageRoot && !active) {
251
+ this.statusPublisher ??= this.createStatusPublisher();
252
+ this.statusPublisherActive = this.statusPublisher.update('authentication-required');
253
+ this.transitionTo('authentication-required');
254
+ return;
255
+ }
256
+ if (this.storageRoot && config.username?.trim()) {
257
+ this.accountScope = config.username.trim().toLowerCase();
258
+ this.ownership ??= new AccountOwnership(join(this.storageRoot, 'ownership'));
259
+ this.statusPublisher ??= this.createStatusPublisher();
260
+ }
261
+ if (this.ownership && this.accountScope && active) {
262
+ if (!this.rearming) {
263
+ this.transitionTo('acquiring-ownership');
264
+ }
265
+ const pendingOwnership = this.ownership.acquire(this.accountScope, 'runtime');
266
+ this.pendingOwnership = pendingOwnership;
267
+ const ownership = await pendingOwnership;
268
+ if (this.stopping) {
269
+ return;
270
+ }
271
+ this.pendingOwnership = undefined;
272
+ if (ownership.state === 'owner-conflict') {
273
+ if (this.rearming) {
274
+ this.rearmBlocked = true;
275
+ }
276
+ else {
277
+ this.transitionTo('owner-conflict');
278
+ }
279
+ return;
280
+ }
281
+ this.runtimeLease = ownership.lease;
282
+ this.clearRearm();
283
+ await this.openChannel();
284
+ const previousSnapshot = active.snapshot.load() ?? undefined;
285
+ if (!this.statusPublisher?.start('starting', {
286
+ generation: active.generation,
287
+ complete: false,
288
+ snapshot: previousSnapshot,
289
+ })) {
290
+ throw new Error('runtime status tracker could not be started');
291
+ }
292
+ this.statusPublisherActive = true;
293
+ this.transitionTo('starting');
294
+ if (!active.session.load()) {
295
+ this.transitionTo('authentication-required');
296
+ await this.beginCleanup('authentication-required', {
297
+ generation: active.generation,
298
+ complete: false,
299
+ snapshot: previousSnapshot,
300
+ });
301
+ return;
302
+ }
303
+ this.client = this.clientFactory(config, active, this.log);
304
+ this.client.onEvent?.((event) => this.publishEvent(event));
305
+ this.client.onUnconfirmedWrite?.((write) => this.publishUnconfirmedWrite(write));
306
+ this.client.onInventory?.((result) => {
307
+ void this.applyRuntimeResult(active, result).catch(() => this.beginCleanup('failed'));
308
+ });
309
+ const result = await this.client.start();
310
+ if (result) {
311
+ await this.applyRuntimeResult(active, result);
312
+ }
313
+ return;
314
+ }
315
+ this.client ??= this.clientFactory(config, active ?? undefined, this.log);
316
+ await this.client.start();
317
+ }
318
+ catch {
319
+ await this.beginCleanup('failed');
320
+ }
321
+ }
322
+ async activeAccount() {
323
+ const active = await this.persistence?.active();
324
+ const configuration = active?.configuration.load();
325
+ if (!active) {
326
+ return { active: null, config: this.configuredConfig };
327
+ }
328
+ if (!configuration) {
329
+ if (this.configuredConfig.username?.trim().toLowerCase() !== active.account) {
330
+ throw new Error('legacy active account generation does not match Homebridge configuration');
331
+ }
332
+ return { active, config: this.configuredConfig };
333
+ }
334
+ if (configuration.username?.trim().toLowerCase() !== active.account) {
335
+ throw new Error('active account generation has mismatched configuration');
336
+ }
337
+ return { active, config: configuration };
338
+ }
339
+ createStatusPublisher() {
340
+ return new RuntimeTracker(join(this.storageRoot, 'tracker.json'), 90_000, Date.now, () => {
341
+ reportRuntimeNotice(this.log, 'status-publication-failed');
342
+ });
343
+ }
344
+ async applyRuntimeResult(active, result) {
345
+ if (this.stopping ||
346
+ (this.runtimeState === 'authentication-required' && result.state !== 'authentication-required')) {
347
+ return;
348
+ }
349
+ const previousSnapshot = active.snapshot.load() ?? undefined;
350
+ if (result.state === 'authentication-required') {
351
+ if (this.runtimeState === 'authentication-required') {
352
+ return;
353
+ }
354
+ this.transitionTo('authentication-required');
355
+ await this.beginCleanup('authentication-required', {
356
+ generation: active.generation,
357
+ complete: false,
358
+ snapshot: previousSnapshot,
359
+ });
360
+ return;
361
+ }
362
+ if (result.state === 'degraded') {
363
+ let latestSnapshot = previousSnapshot;
364
+ const completeTopology = result.complete === true;
365
+ if (completeTopology) {
366
+ this.acceptCompleteRegistry(active, result.registry, result.snapshot);
367
+ latestSnapshot = result.snapshot;
368
+ }
369
+ this.statusPublisher?.update('degraded', {
370
+ generation: active.generation,
371
+ complete: completeTopology,
372
+ snapshot: latestSnapshot,
373
+ status: completeTopology ? 'transport-degraded' : 'incomplete-inventory',
374
+ });
375
+ this.transitionTo('degraded');
376
+ return;
377
+ }
378
+ this.acceptCompleteRegistry(active, result.registry, result.snapshot);
379
+ if (!this.statusPublisher?.update('ready', {
380
+ generation: active.generation,
381
+ complete: true,
382
+ snapshot: result.snapshot,
383
+ })) {
384
+ throw new Error('complete runtime snapshot could not be published');
385
+ }
386
+ this.transitionTo('ready');
387
+ }
388
+ acceptCompleteRegistry(active, registry, snapshot) {
389
+ const registrySerials = [...registry.keys()].sort();
390
+ const snapshotSerials = snapshot.devices.map((device) => device.sn).sort();
391
+ if (JSON.stringify(registrySerials) !== JSON.stringify(snapshotSerials)) {
392
+ throw new Error('canonical registry does not match its complete device snapshot');
393
+ }
394
+ active.snapshot.save(snapshot);
395
+ this.publishRegistry(active.generation, registry, snapshot);
396
+ }
397
+ async releaseRuntimeLease(onReleased = () => undefined) {
398
+ const lease = this.runtimeLease;
399
+ this.runtimeLease = undefined;
400
+ if (!lease) {
401
+ onReleased();
402
+ return true;
403
+ }
404
+ try {
405
+ let finalized = false;
406
+ const release = await lease.release(() => {
407
+ finalized = true;
408
+ onReleased();
409
+ });
410
+ if (release.state !== 'stopped') {
411
+ return false;
412
+ }
413
+ if (!finalized) {
414
+ reportRuntimeNotice(this.log, 'ownership-release-not-finalized');
415
+ return false;
416
+ }
417
+ }
418
+ catch {
419
+ reportRuntimeNotice(this.log, 'ownership-release-failed');
420
+ return false;
421
+ }
422
+ return true;
423
+ }
424
+ async stopRuntimeClient() {
425
+ const client = this.client;
426
+ this.client = undefined;
427
+ try {
428
+ await client?.stop();
429
+ }
430
+ catch {
431
+ reportRuntimeNotice(this.log, 'shutdown-failed');
432
+ return false;
433
+ }
434
+ return true;
435
+ }
436
+ beginCleanup(completedState, update) {
437
+ this.stopping = true;
438
+ if (!this.stopPromise) {
439
+ this.cleanupTerminalState = completedState;
440
+ this.stopPromise = this.cleanupWithinDeadline(completedState, update);
441
+ }
442
+ return this.stopPromise;
443
+ }
444
+ async cleanupWithinDeadline(completedState, update) {
445
+ if (this.runtimeState === 'owner-conflict') {
446
+ return;
447
+ }
448
+ const deadline = Date.now() + this.shutdownTimeoutMs;
449
+ const clientStopped = await this.boundedCleanup(this.stopRuntimeClient(), deadline);
450
+ const ownershipSettled = await this.settlePendingOwnership(deadline);
451
+ const cleanupFailedBeforeRelease = clientStopped !== true || ownershipSettled !== true;
452
+ let publishedTerminalState = cleanupFailedBeforeRelease ? 'failed' : (this.cleanupTerminalState ?? completedState);
453
+ const leaseReleased = await this.boundedCleanup(this.releaseRuntimeLease(() => {
454
+ this.channel?.close();
455
+ this.publishTerminalState(publishedTerminalState, update);
456
+ }), deadline);
457
+ const timedOut = clientStopped === 'timeout' || ownershipSettled === 'timeout' || leaseReleased === 'timeout';
458
+ if (timedOut) {
459
+ reportRuntimeNotice(this.log, 'shutdown-timeout', { durationMs: this.shutdownTimeoutMs });
460
+ }
461
+ if (leaseReleased === 'timeout') {
462
+ publishedTerminalState = 'failed';
463
+ this.publishTerminalState('failed');
464
+ }
465
+ const terminalState = leaseReleased === true ? publishedTerminalState : 'failed';
466
+ this.transitionTo(terminalState);
467
+ }
468
+ /**
469
+ * Binds the live channel once this process is the account's owner.
470
+ *
471
+ * The endpoint's lifetime is the lease's: opened here, and closed inside the release guard. An unbound
472
+ * endpoint is reported and the runtime starts without one.
473
+ */
474
+ async openChannel() {
475
+ if (this.storageRoot) {
476
+ const storageRoot = this.storageRoot;
477
+ this.channel ??= new RuntimeChannelServer(runtimeChannelEndpointForHost(storageRoot), () => this.channelStatus(), {
478
+ devices: () => this.channelDevices(),
479
+ authorization: (notice) => armDiagnosticsAuthorization(this.log, storageRoot, notice.supportCaseId),
480
+ standDown: () => this.standDown(),
481
+ version: PLUGIN_VERSION,
482
+ restartable: isChildBridgeTitle(process.title),
483
+ restart: () => this.endForReplacement(),
484
+ });
485
+ }
486
+ if (this.channel && !(await this.channel.open())) {
487
+ reportRuntimeNotice(this.log, 'channel-serve-failed');
488
+ }
489
+ }
490
+ /**
491
+ * Ends this process so Homebridge replaces it with the installed build, reporting whether that was begun.
492
+ *
493
+ * Refused unless this process is a child bridge, because the main process is not respawned and every other
494
+ * plugin would go down with it. The signal is deferred so the request that asked for it is answered first,
495
+ * and it is the same signal Homebridge itself uses to cycle a child bridge, rather than an abrupt exit.
496
+ */
497
+ endForReplacement() {
498
+ if (!isChildBridgeTitle(process.title)) {
499
+ return false;
500
+ }
501
+ setTimeout(() => process.kill(process.pid, 'SIGTERM'), 250).unref();
502
+ return true;
503
+ }
504
+ /**
505
+ * What the live channel answers: the state this runtime holds now, and the inventory it last published.
506
+ *
507
+ * `status` accompanies the state it was published with, and is otherwise derived from the live state
508
+ * through the one mapping the tracker uses, so the pair cannot disagree. `generation` and `complete`
509
+ * describe the published inventory and outlive a state change. `updatedAt` is when this answer was given.
510
+ */
511
+ channelStatus() {
512
+ const published = this.statusPublisher?.latest?.();
513
+ const current = published?.state === this.runtimeState ? published : undefined;
514
+ return {
515
+ state: this.runtimeState,
516
+ status: current?.status ?? runtimeStatusFor(this.runtimeState),
517
+ ...(published?.generation === undefined ? {} : { generation: published.generation }),
518
+ complete: published?.complete ?? false,
519
+ updatedAt: new Date().toISOString(),
520
+ };
521
+ }
522
+ /**
523
+ * What the live channel observes about each device its published inventory names.
524
+ *
525
+ * Taken from the registry this runtime already holds and the evidence the same published manifest carries,
526
+ * so no second capability model is built from SDK internals. Nothing is observed once the SDK client is
527
+ * gone, because a surface belonging to a stopped client states nothing about a device now. A field left
528
+ * `undefined` is dropped on the way to the wire: unobserved is not unreachable, and it is not switched off.
529
+ */
530
+ channelDevices() {
531
+ const view = this.registryView;
532
+ if (!view || !this.client) {
533
+ return [];
534
+ }
535
+ return view.snapshot.devices.map((manifest) => ({
536
+ serial: manifest.sn,
537
+ availability: this.currentAvailability(manifest.sn)?.availability,
538
+ enabled: this.observedEnablement(view.registry.get(manifest.sn), manifest),
539
+ battery: this.observedBattery(view.registry.get(manifest.sn), manifest),
540
+ }));
541
+ }
542
+ /**
543
+ * A camera's enablement where its own surface states one.
544
+ *
545
+ * The capability accessor is a call into the SDK's binding and can fault, and a device that is not a camera
546
+ * has no accessor at all. Both are unobserved.
547
+ */
548
+ observedEnablement(device, manifest) {
549
+ try {
550
+ const camera = device?.camera?.();
551
+ return camera ? cameraEnablement(camera, indexDeviceEvidence(manifest).members) : undefined;
552
+ }
553
+ catch {
554
+ return undefined;
555
+ }
556
+ }
557
+ /**
558
+ * A device's battery level where its own surface states one.
559
+ *
560
+ * The capability accessor is a call into the SDK's binding and can fault, and a device that runs on mains
561
+ * power has no accessor at all. Both are unobserved.
562
+ */
563
+ observedBattery(device, manifest) {
564
+ try {
565
+ const battery = device?.battery?.();
566
+ return battery ? batteryLevel(battery, indexDeviceEvidence(manifest).members) : undefined;
567
+ }
568
+ catch {
569
+ return undefined;
570
+ }
571
+ }
572
+ publishTerminalState(terminalState, update) {
573
+ if (terminalState === 'stopped') {
574
+ if (this.statusPublisherActive) {
575
+ this.statusPublisher?.stop();
576
+ }
577
+ }
578
+ else if (this.statusPublisherActive) {
579
+ if (terminalState === 'authentication-required') {
580
+ this.statusPublisher?.update(terminalState, update);
581
+ }
582
+ else {
583
+ this.statusPublisher?.update(terminalState);
584
+ }
585
+ }
586
+ }
587
+ async boundedCleanup(operation, deadline) {
588
+ let timer;
589
+ const timeout = new Promise((resolve) => {
590
+ timer = setTimeout(() => resolve('timeout'), Math.max(0, deadline - Date.now()));
591
+ timer.unref();
592
+ });
593
+ const result = await Promise.race([operation, timeout]);
594
+ clearTimeout(timer);
595
+ return result;
596
+ }
597
+ async settlePendingOwnership(deadline) {
598
+ const pendingOwnership = this.pendingOwnership;
599
+ if (!pendingOwnership) {
600
+ return true;
601
+ }
602
+ const acquisition = pendingOwnership
603
+ .then((ownership) => {
604
+ if (this.pendingOwnership === pendingOwnership) {
605
+ this.pendingOwnership = undefined;
606
+ }
607
+ if (ownership.state === 'owner') {
608
+ this.runtimeLease = ownership.lease;
609
+ }
610
+ return true;
611
+ })
612
+ .catch(() => {
613
+ reportRuntimeNotice(this.log, 'ownership-acquisition-failed');
614
+ return false;
615
+ });
616
+ const settled = await this.boundedCleanup(acquisition, deadline);
617
+ if (settled === 'timeout') {
618
+ void acquisition.then(async (acquired) => {
619
+ if (!acquired) {
620
+ return;
621
+ }
622
+ const released = await this.boundedCleanup(this.releaseRuntimeLease(), Date.now() + this.shutdownTimeoutMs);
623
+ if (released !== true) {
624
+ reportRuntimeNotice(this.log, 'ownership-release-incomplete');
625
+ }
626
+ });
627
+ }
628
+ return settled;
629
+ }
630
+ publishRegistry(generation, registry, snapshot) {
631
+ const view = Object.freeze({
632
+ version: ++this.registryVersion,
633
+ generation,
634
+ registry: new Map(registry),
635
+ snapshot: parseCompleteDeviceSnapshot(snapshot),
636
+ });
637
+ this.registryView = view;
638
+ for (const listener of this.registryListeners) {
639
+ try {
640
+ listener(view);
641
+ }
642
+ catch {
643
+ reportRuntimeNotice(this.log, 'registry-subscriber-failed');
644
+ }
645
+ }
646
+ }
647
+ publishUnconfirmedWrite(write) {
648
+ for (const listener of this.unconfirmedListeners) {
649
+ try {
650
+ listener(write);
651
+ }
652
+ catch {
653
+ reportRuntimeNotice(this.log, 'event-subscriber-failed');
654
+ }
655
+ }
656
+ }
657
+ publishEvent(event) {
658
+ for (const listener of this.eventListeners) {
659
+ try {
660
+ listener(event);
661
+ }
662
+ catch {
663
+ reportRuntimeNotice(this.log, 'event-subscriber-failed');
664
+ }
665
+ }
666
+ }
667
+ transitionTo(state) {
668
+ if (this.runtimeState === state) {
669
+ return;
670
+ }
671
+ this.runtimeState = state;
672
+ for (const listener of this.stateListeners) {
673
+ try {
674
+ listener(state);
675
+ }
676
+ catch {
677
+ reportRuntimeNotice(this.log, 'state-subscriber-failed');
678
+ }
679
+ }
680
+ }
681
+ }
682
+ //# sourceMappingURL=owner.js.map