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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (469) hide show
  1. package/AUTHORS +4 -0
  2. package/CODE_OF_CONDUCT.md +30 -0
  3. package/CONTRIBUTING.md +77 -0
  4. package/README.md +152 -49
  5. package/SECURITY.md +30 -0
  6. package/config.schema.json +92 -3
  7. package/dist/account/ownership.js +290 -0
  8. package/dist/account/ownership.js.map +1 -0
  9. package/dist/account/persistence.js +274 -0
  10. package/dist/account/persistence.js.map +1 -0
  11. package/dist/account/temporary-authentication.js +348 -0
  12. package/dist/account/temporary-authentication.js.map +1 -0
  13. package/dist/configuration.js +167 -0
  14. package/dist/configuration.js.map +1 -0
  15. package/dist/device/member-evidence.js +66 -0
  16. package/dist/device/member-evidence.js.map +1 -0
  17. package/dist/device/member-trust.js +114 -0
  18. package/dist/device/member-trust.js.map +1 -0
  19. package/dist/device/snapshot.js +190 -0
  20. package/dist/device/snapshot.js.map +1 -0
  21. package/dist/diagnostics.js +2218 -0
  22. package/dist/diagnostics.js.map +1 -0
  23. package/dist/homekit/adapter.js +2 -0
  24. package/dist/homekit/adapter.js.map +1 -0
  25. package/dist/homekit/adapters/battery.js +208 -0
  26. package/dist/homekit/adapters/battery.js.map +1 -0
  27. package/dist/homekit/adapters/camera-controls.js +527 -0
  28. package/dist/homekit/adapters/camera-controls.js.map +1 -0
  29. package/dist/homekit/adapters/camera-streaming.js +1515 -0
  30. package/dist/homekit/adapters/camera-streaming.js.map +1 -0
  31. package/dist/homekit/adapters/contact.js +138 -0
  32. package/dist/homekit/adapters/contact.js.map +1 -0
  33. package/dist/homekit/adapters/doorbell.js +57 -0
  34. package/dist/homekit/adapters/doorbell.js.map +1 -0
  35. package/dist/homekit/adapters/information.js +55 -0
  36. package/dist/homekit/adapters/information.js.map +1 -0
  37. package/dist/homekit/adapters/lock.js +489 -0
  38. package/dist/homekit/adapters/lock.js.map +1 -0
  39. package/dist/homekit/adapters/motion.js +100 -0
  40. package/dist/homekit/adapters/motion.js.map +1 -0
  41. package/dist/homekit/adapters/registry.js +26 -0
  42. package/dist/homekit/adapters/registry.js.map +1 -0
  43. package/dist/homekit/adapters/security-system.js +425 -0
  44. package/dist/homekit/adapters/security-system.js.map +1 -0
  45. package/dist/homekit/adapters/siren.js +136 -0
  46. package/dist/homekit/adapters/siren.js.map +1 -0
  47. package/dist/homekit/adapters/smart-light.js +662 -0
  48. package/dist/homekit/adapters/smart-light.js.map +1 -0
  49. package/dist/homekit/coverage-matrix.js +119 -0
  50. package/dist/homekit/coverage-matrix.js.map +1 -0
  51. package/dist/homekit/device-control.js +175 -0
  52. package/dist/homekit/device-control.js.map +1 -0
  53. package/dist/homekit/reconciler.js +319 -0
  54. package/dist/homekit/reconciler.js.map +1 -0
  55. package/dist/homekit/representation.js +23 -0
  56. package/dist/homekit/representation.js.map +1 -0
  57. package/dist/index.js +4 -9
  58. package/dist/index.js.map +1 -1
  59. package/dist/media/contracts.js +2 -0
  60. package/dist/media/contracts.js.map +1 -0
  61. package/dist/media/last-successful-image.js +208 -0
  62. package/dist/media/last-successful-image.js.map +1 -0
  63. package/dist/media/live-stream.js +1003 -0
  64. package/dist/media/live-stream.js.map +1 -0
  65. package/dist/media/recording.js +560 -0
  66. package/dist/media/recording.js.map +1 -0
  67. package/dist/media/session-budget.js +39 -0
  68. package/dist/media/session-budget.js.map +1 -0
  69. package/dist/media/snapshot.js +406 -0
  70. package/dist/media/snapshot.js.map +1 -0
  71. package/dist/media/station-live-sessions.js +101 -0
  72. package/dist/media/station-live-sessions.js.map +1 -0
  73. package/dist/platform.js +97 -769
  74. package/dist/platform.js.map +1 -1
  75. package/dist/runtime/channel.js +375 -0
  76. package/dist/runtime/channel.js.map +1 -0
  77. package/dist/runtime/owner.js +682 -0
  78. package/dist/runtime/owner.js.map +1 -0
  79. package/dist/runtime/sdk-client.js +211 -0
  80. package/dist/runtime/sdk-client.js.map +1 -0
  81. package/dist/runtime/tracker.js +211 -0
  82. package/dist/runtime/tracker.js.map +1 -0
  83. package/dist/settings.js +1 -43
  84. package/dist/settings.js.map +1 -1
  85. package/dist/storage.js +59 -0
  86. package/dist/storage.js.map +1 -0
  87. package/dist/ui/dashboard.js +181 -0
  88. package/dist/ui/dashboard.js.map +1 -0
  89. package/dist/ui/device-image.js +22 -0
  90. package/dist/ui/device-image.js.map +1 -0
  91. package/dist/ui/runtime-channel-client.js +305 -0
  92. package/dist/ui/runtime-channel-client.js.map +1 -0
  93. package/dist/ui/server.js +259 -0
  94. package/dist/ui/server.js.map +1 -0
  95. package/homebridge-ui/public/app.css +1902 -0
  96. package/homebridge-ui/public/assets/devices/clean/clean-T1250.webp +0 -0
  97. package/homebridge-ui/public/assets/devices/clean/clean-T2070.webp +0 -0
  98. package/homebridge-ui/public/assets/devices/clean/clean-T2080.webp +0 -0
  99. package/homebridge-ui/public/assets/devices/clean/clean-T2081.webp +0 -0
  100. package/homebridge-ui/public/assets/devices/clean/clean-T2103.webp +0 -0
  101. package/homebridge-ui/public/assets/devices/clean/clean-T2117.webp +0 -0
  102. package/homebridge-ui/public/assets/devices/clean/clean-T2118.webp +0 -0
  103. package/homebridge-ui/public/assets/devices/clean/clean-T2119.webp +0 -0
  104. package/homebridge-ui/public/assets/devices/clean/clean-T211A.webp +0 -0
  105. package/homebridge-ui/public/assets/devices/clean/clean-T2120.webp +0 -0
  106. package/homebridge-ui/public/assets/devices/clean/clean-T2123.webp +0 -0
  107. package/homebridge-ui/public/assets/devices/clean/clean-T2128.webp +0 -0
  108. package/homebridge-ui/public/assets/devices/clean/clean-T2130.webp +0 -0
  109. package/homebridge-ui/public/assets/devices/clean/clean-T2132.webp +0 -0
  110. package/homebridge-ui/public/assets/devices/clean/clean-T2150.webp +0 -0
  111. package/homebridge-ui/public/assets/devices/clean/clean-T2181.webp +0 -0
  112. package/homebridge-ui/public/assets/devices/clean/clean-T2182.webp +0 -0
  113. package/homebridge-ui/public/assets/devices/clean/clean-T2190.webp +0 -0
  114. package/homebridge-ui/public/assets/devices/clean/clean-T2192.webp +0 -0
  115. package/homebridge-ui/public/assets/devices/clean/clean-T2193.webp +0 -0
  116. package/homebridge-ui/public/assets/devices/clean/clean-T2194.webp +0 -0
  117. package/homebridge-ui/public/assets/devices/clean/clean-T2210.webp +0 -0
  118. package/homebridge-ui/public/assets/devices/clean/clean-T2250.webp +0 -0
  119. package/homebridge-ui/public/assets/devices/clean/clean-T2251.webp +0 -0
  120. package/homebridge-ui/public/assets/devices/clean/clean-T2252.webp +0 -0
  121. package/homebridge-ui/public/assets/devices/clean/clean-T2253.webp +0 -0
  122. package/homebridge-ui/public/assets/devices/clean/clean-T2254.webp +0 -0
  123. package/homebridge-ui/public/assets/devices/clean/clean-T2255.webp +0 -0
  124. package/homebridge-ui/public/assets/devices/clean/clean-T2256.webp +0 -0
  125. package/homebridge-ui/public/assets/devices/clean/clean-T2257.webp +0 -0
  126. package/homebridge-ui/public/assets/devices/clean/clean-T2258.webp +0 -0
  127. package/homebridge-ui/public/assets/devices/clean/clean-T2259.webp +0 -0
  128. package/homebridge-ui/public/assets/devices/clean/clean-T2261.webp +0 -0
  129. package/homebridge-ui/public/assets/devices/clean/clean-T2262.webp +0 -0
  130. package/homebridge-ui/public/assets/devices/clean/clean-T2266.webp +0 -0
  131. package/homebridge-ui/public/assets/devices/clean/clean-T2267.webp +0 -0
  132. package/homebridge-ui/public/assets/devices/clean/clean-T2268.webp +0 -0
  133. package/homebridge-ui/public/assets/devices/clean/clean-T2270.webp +0 -0
  134. package/homebridge-ui/public/assets/devices/clean/clean-T2272.webp +0 -0
  135. package/homebridge-ui/public/assets/devices/clean/clean-T2273.webp +0 -0
  136. package/homebridge-ui/public/assets/devices/clean/clean-T2276.webp +0 -0
  137. package/homebridge-ui/public/assets/devices/clean/clean-T2277.webp +0 -0
  138. package/homebridge-ui/public/assets/devices/clean/clean-T2278.webp +0 -0
  139. package/homebridge-ui/public/assets/devices/clean/clean-T2280.webp +0 -0
  140. package/homebridge-ui/public/assets/devices/clean/clean-T2292.webp +0 -0
  141. package/homebridge-ui/public/assets/devices/clean/clean-T2320.webp +0 -0
  142. package/homebridge-ui/public/assets/devices/clean/clean-T2351.webp +0 -0
  143. package/homebridge-ui/public/assets/devices/clean/clean-T2352.webp +0 -0
  144. package/homebridge-ui/public/assets/devices/clean/clean-T2353.webp +0 -0
  145. package/homebridge-ui/public/assets/devices/life/life-T8L00.webp +0 -0
  146. package/homebridge-ui/public/assets/devices/life/life-T8L01.webp +0 -0
  147. package/homebridge-ui/public/assets/devices/life/life-T8L02.webp +0 -0
  148. package/homebridge-ui/public/assets/devices/life/life-T8L04.webp +0 -0
  149. package/homebridge-ui/public/assets/devices/life/life-T8L10.webp +0 -0
  150. package/homebridge-ui/public/assets/devices/life/life-T8L20.webp +0 -0
  151. package/homebridge-ui/public/assets/devices/life/life-T8L30.webp +0 -0
  152. package/homebridge-ui/public/assets/devices/life/life-T8L40.webp +0 -0
  153. package/homebridge-ui/public/assets/devices/mower/mower-T2801.webp +0 -0
  154. package/homebridge-ui/public/assets/devices/mower/mower-T280B.webp +0 -0
  155. package/homebridge-ui/public/assets/devices/mower/mower-T2880.webp +0 -0
  156. package/homebridge-ui/public/assets/devices/security/security-T7400.webp +0 -0
  157. package/homebridge-ui/public/assets/devices/security/security-T7401.webp +0 -0
  158. package/homebridge-ui/public/assets/devices/security/security-T8001.webp +0 -0
  159. package/homebridge-ui/public/assets/devices/security/security-T8002.webp +0 -0
  160. package/homebridge-ui/public/assets/devices/security/security-T8010.webp +0 -0
  161. package/homebridge-ui/public/assets/devices/security/security-T8021.webp +0 -0
  162. package/homebridge-ui/public/assets/devices/security/security-T8023.webp +0 -0
  163. package/homebridge-ui/public/assets/devices/security/security-T8025.webp +0 -0
  164. package/homebridge-ui/public/assets/devices/security/security-T8030.webp +0 -0
  165. package/homebridge-ui/public/assets/devices/security/security-T8110.webp +0 -0
  166. package/homebridge-ui/public/assets/devices/security/security-T8111.webp +0 -0
  167. package/homebridge-ui/public/assets/devices/security/security-T8112.webp +0 -0
  168. package/homebridge-ui/public/assets/devices/security/security-T8113.webp +0 -0
  169. package/homebridge-ui/public/assets/devices/security/security-T8114.webp +0 -0
  170. package/homebridge-ui/public/assets/devices/security/security-T8122.webp +0 -0
  171. package/homebridge-ui/public/assets/devices/security/security-T8123.webp +0 -0
  172. package/homebridge-ui/public/assets/devices/security/security-T8124.webp +0 -0
  173. package/homebridge-ui/public/assets/devices/security/security-T8130.webp +0 -0
  174. package/homebridge-ui/public/assets/devices/security/security-T8131.webp +0 -0
  175. package/homebridge-ui/public/assets/devices/security/security-T8134.webp +0 -0
  176. package/homebridge-ui/public/assets/devices/security/security-T8140.webp +0 -0
  177. package/homebridge-ui/public/assets/devices/security/security-T8141.webp +0 -0
  178. package/homebridge-ui/public/assets/devices/security/security-T8150.webp +0 -0
  179. package/homebridge-ui/public/assets/devices/security/security-T8151.webp +0 -0
  180. package/homebridge-ui/public/assets/devices/security/security-T8152.webp +0 -0
  181. package/homebridge-ui/public/assets/devices/security/security-T8153.webp +0 -0
  182. package/homebridge-ui/public/assets/devices/security/security-T8160.webp +0 -0
  183. package/homebridge-ui/public/assets/devices/security/security-T8161.webp +0 -0
  184. package/homebridge-ui/public/assets/devices/security/security-T8162.webp +0 -0
  185. package/homebridge-ui/public/assets/devices/security/security-T8170.webp +0 -0
  186. package/homebridge-ui/public/assets/devices/security/security-T8171.webp +0 -0
  187. package/homebridge-ui/public/assets/devices/security/security-T8172.webp +0 -0
  188. package/homebridge-ui/public/assets/devices/security/security-T8173.webp +0 -0
  189. package/homebridge-ui/public/assets/devices/security/security-T8200.webp +0 -0
  190. package/homebridge-ui/public/assets/devices/security/security-T8200X.webp +0 -0
  191. package/homebridge-ui/public/assets/devices/security/security-T8201.webp +0 -0
  192. package/homebridge-ui/public/assets/devices/security/security-T8203.webp +0 -0
  193. package/homebridge-ui/public/assets/devices/security/security-T8210.webp +0 -0
  194. package/homebridge-ui/public/assets/devices/security/security-T8213.webp +0 -0
  195. package/homebridge-ui/public/assets/devices/security/security-T8214.webp +0 -0
  196. package/homebridge-ui/public/assets/devices/security/security-T8222.webp +0 -0
  197. package/homebridge-ui/public/assets/devices/security/security-T8400.webp +0 -0
  198. package/homebridge-ui/public/assets/devices/security/security-T8401.webp +0 -0
  199. package/homebridge-ui/public/assets/devices/security/security-T8410.webp +0 -0
  200. package/homebridge-ui/public/assets/devices/security/security-T8411.webp +0 -0
  201. package/homebridge-ui/public/assets/devices/security/security-T8414.webp +0 -0
  202. package/homebridge-ui/public/assets/devices/security/security-T8416.webp +0 -0
  203. package/homebridge-ui/public/assets/devices/security/security-T8417.webp +0 -0
  204. package/homebridge-ui/public/assets/devices/security/security-T8419.webp +0 -0
  205. package/homebridge-ui/public/assets/devices/security/security-T8419N.webp +0 -0
  206. package/homebridge-ui/public/assets/devices/security/security-T8420.webp +0 -0
  207. package/homebridge-ui/public/assets/devices/security/security-T8420X.webp +0 -0
  208. package/homebridge-ui/public/assets/devices/security/security-T8422.webp +0 -0
  209. package/homebridge-ui/public/assets/devices/security/security-T8423.webp +0 -0
  210. package/homebridge-ui/public/assets/devices/security/security-T8424.webp +0 -0
  211. package/homebridge-ui/public/assets/devices/security/security-T8425.webp +0 -0
  212. package/homebridge-ui/public/assets/devices/security/security-T8441.webp +0 -0
  213. package/homebridge-ui/public/assets/devices/security/security-T8442.webp +0 -0
  214. package/homebridge-ui/public/assets/devices/security/security-T8452.webp +0 -0
  215. package/homebridge-ui/public/assets/devices/security/security-T84A0.webp +0 -0
  216. package/homebridge-ui/public/assets/devices/security/security-T84A1.webp +0 -0
  217. package/homebridge-ui/public/assets/devices/security/security-T8500.webp +0 -0
  218. package/homebridge-ui/public/assets/devices/security/security-T8502.webp +0 -0
  219. package/homebridge-ui/public/assets/devices/security/security-T8503.webp +0 -0
  220. package/homebridge-ui/public/assets/devices/security/security-T8504.webp +0 -0
  221. package/homebridge-ui/public/assets/devices/security/security-T8506.webp +0 -0
  222. package/homebridge-ui/public/assets/devices/security/security-T8510.webp +0 -0
  223. package/homebridge-ui/public/assets/devices/security/security-T8510P.webp +0 -0
  224. package/homebridge-ui/public/assets/devices/security/security-T8520.webp +0 -0
  225. package/homebridge-ui/public/assets/devices/security/security-T8520P.webp +0 -0
  226. package/homebridge-ui/public/assets/devices/security/security-T8530.webp +0 -0
  227. package/homebridge-ui/public/assets/devices/security/security-T8531.webp +0 -0
  228. package/homebridge-ui/public/assets/devices/security/security-T85D0.webp +0 -0
  229. package/homebridge-ui/public/assets/devices/security/security-T85L0.webp +0 -0
  230. package/homebridge-ui/public/assets/devices/security/security-T85P0.webp +0 -0
  231. package/homebridge-ui/public/assets/devices/security/security-T85V0.webp +0 -0
  232. package/homebridge-ui/public/assets/devices/security/security-T8600.webp +0 -0
  233. package/homebridge-ui/public/assets/devices/security/security-T86P2.webp +0 -0
  234. package/homebridge-ui/public/assets/devices/security/security-T8790.webp +0 -0
  235. package/homebridge-ui/public/assets/devices/security/security-T87B0.webp +0 -0
  236. package/homebridge-ui/public/assets/devices/security/security-T87B2.webp +0 -0
  237. package/homebridge-ui/public/assets/devices/security/security-T8900.webp +0 -0
  238. package/homebridge-ui/public/assets/devices/security/security-T8910.webp +0 -0
  239. package/homebridge-ui/public/assets/devices/security/security-T8920.webp +0 -0
  240. package/homebridge-ui/public/assets/devices/security/security-T8960.webp +0 -0
  241. package/homebridge-ui/public/assets/devices/security/security-T8B0.webp +0 -0
  242. package/homebridge-ui/public/assets/devices/security/security-T8E00.webp +0 -0
  243. package/homebridge-ui/public/assets/devices/security/security-T8N00.webp +0 -0
  244. package/homebridge-ui/public/assets/devices/security/security-T8W11C.webp +0 -0
  245. package/homebridge-ui/public/assets/devices/security/security-T90F0.webp +0 -0
  246. package/homebridge-ui/public/assets/devices/security/security-T90M0.webp +0 -0
  247. package/homebridge-ui/public/assets/devices/security/security-T90R0.webp +0 -0
  248. package/homebridge-ui/public/assets/devices/security/security-T90S0.webp +0 -0
  249. package/homebridge-ui/public/assets/icons/inventory.svg +1 -1
  250. package/homebridge-ui/public/assets/icons/troubleshoot.svg +1 -0
  251. package/homebridge-ui/public/assets/icons/warning.svg +1 -1
  252. package/homebridge-ui/public/assets/logo-dark.svg +10 -0
  253. package/homebridge-ui/public/assets/logo.svg +10 -0
  254. package/homebridge-ui/public/i18n/en.json +218 -0
  255. package/homebridge-ui/public/i18n/fr.json +218 -0
  256. package/homebridge-ui/public/index.html +377 -28
  257. package/homebridge-ui/public/js/app.js +1065 -0
  258. package/homebridge-ui/public/js/dashboard.js +385 -0
  259. package/homebridge-ui/public/js/legacy-settings.js +37 -0
  260. package/homebridge-ui/public/js/profile-wizard.js +68 -0
  261. package/homebridge-ui/server.js +2 -1393
  262. package/i18n/runtime/en.json +100 -0
  263. package/media/Snapshot-Unavailable.jpg +0 -0
  264. package/media/Snapshot-black.jpg +0 -0
  265. package/media/camera-disabled.jpg +0 -0
  266. package/media/camera-offline.jpg +0 -0
  267. package/package.json +29 -24
  268. package/.claude/CLAUDE.md +0 -175
  269. package/.claude/PRD.md +0 -241
  270. package/.claude/docs/debug-recording-lessons.md +0 -91
  271. package/.claude/docs/hksv-recording-fix.md +0 -160
  272. package/.claude/skills/architect/SKILL.md +0 -76
  273. package/.claude/skills/developer/SKILL.md +0 -59
  274. package/.claude/skills/new-device-support/SKILL.md +0 -212
  275. package/.claude/skills/new-device-support/check-device.mjs +0 -363
  276. package/.claude/skills/new-device-support/map-properties.mjs +0 -450
  277. package/.claude/skills/new-device-support/verify-device.mjs +0 -272
  278. package/.claude/skills/planner/SKILL.md +0 -100
  279. package/.claude/skills/qa/SKILL.md +0 -79
  280. package/.claude/skills/support/SKILL.md +0 -175
  281. package/.prettierignore +0 -7
  282. package/.prettierrc.json +0 -7
  283. package/FUNDING.yml +0 -1
  284. package/dist/accessories/AutoSyncStationAccessory.js +0 -157
  285. package/dist/accessories/AutoSyncStationAccessory.js.map +0 -1
  286. package/dist/accessories/BaseAccessory.js +0 -261
  287. package/dist/accessories/BaseAccessory.js.map +0 -1
  288. package/dist/accessories/CameraAccessory.js +0 -431
  289. package/dist/accessories/CameraAccessory.js.map +0 -1
  290. package/dist/accessories/Device.js +0 -65
  291. package/dist/accessories/Device.js.map +0 -1
  292. package/dist/accessories/EntrySensorAccessory.js +0 -48
  293. package/dist/accessories/EntrySensorAccessory.js.map +0 -1
  294. package/dist/accessories/LockAccessory.js +0 -142
  295. package/dist/accessories/LockAccessory.js.map +0 -1
  296. package/dist/accessories/MotionSensorAccessory.js +0 -48
  297. package/dist/accessories/MotionSensorAccessory.js.map +0 -1
  298. package/dist/accessories/SmartDropAccessory.js +0 -141
  299. package/dist/accessories/SmartDropAccessory.js.map +0 -1
  300. package/dist/accessories/StationAccessory.js +0 -373
  301. package/dist/accessories/StationAccessory.js.map +0 -1
  302. package/dist/config.js +0 -26
  303. package/dist/config.js.map +0 -1
  304. package/dist/controller/DebugRecordingManager.js +0 -402
  305. package/dist/controller/DebugRecordingManager.js.map +0 -1
  306. package/dist/controller/LocalLivestreamManager.js +0 -371
  307. package/dist/controller/LocalLivestreamManager.js.map +0 -1
  308. package/dist/controller/recordingDelegate.js +0 -252
  309. package/dist/controller/recordingDelegate.js.map +0 -1
  310. package/dist/controller/snapshotDelegate.js +0 -348
  311. package/dist/controller/snapshotDelegate.js.map +0 -1
  312. package/dist/controller/streamingDelegate.js +0 -389
  313. package/dist/controller/streamingDelegate.js.map +0 -1
  314. package/dist/interfaces.js +0 -2
  315. package/dist/interfaces.js.map +0 -1
  316. package/dist/media/Snapshot-Unavailable.png +0 -0
  317. package/dist/media/Snapshot-Unavailable.xcf +0 -0
  318. package/dist/media/Snapshot-black.png +0 -0
  319. package/dist/media/camera-disabled.png +0 -0
  320. package/dist/media/camera-offline.png +0 -0
  321. package/dist/media/media/Snapshot-Unavailable.png +0 -0
  322. package/dist/media/media/Snapshot-Unavailable.xcf +0 -0
  323. package/dist/media/media/Snapshot-black.png +0 -0
  324. package/dist/media/media/camera-disabled.png +0 -0
  325. package/dist/media/media/camera-offline.png +0 -0
  326. package/dist/utils/Talkback.js +0 -162
  327. package/dist/utils/Talkback.js.map +0 -1
  328. package/dist/utils/accessoriesStore.js +0 -207
  329. package/dist/utils/accessoriesStore.js.map +0 -1
  330. package/dist/utils/configTypes.js +0 -36
  331. package/dist/utils/configTypes.js.map +0 -1
  332. package/dist/utils/ffmpeg.js +0 -1111
  333. package/dist/utils/ffmpeg.js.map +0 -1
  334. package/dist/utils/interfaces.js +0 -8
  335. package/dist/utils/interfaces.js.map +0 -1
  336. package/dist/utils/utils.js +0 -224
  337. package/dist/utils/utils.js.map +0 -1
  338. package/dist/version.js +0 -2
  339. package/dist/version.js.map +0 -1
  340. package/homebridge-eufy-security.png +0 -0
  341. package/homebridge-ui/diagnosticsCrypto.js +0 -112
  342. package/homebridge-ui/public/app.js +0 -237
  343. package/homebridge-ui/public/assets/devices/4g_lte_cam_s330_large.png +0 -0
  344. package/homebridge-ui/public/assets/devices/4g_lte_starlight_large.jpg +0 -0
  345. package/homebridge-ui/public/assets/devices/BATTERY_DOORBELL_C30.png +0 -0
  346. package/homebridge-ui/public/assets/devices/BATTERY_DOORBELL_C31.png +0 -0
  347. package/homebridge-ui/public/assets/devices/batterydoorbell1080p_large.jpg +0 -0
  348. package/homebridge-ui/public/assets/devices/batterydoorbell2k_large.png +0 -0
  349. package/homebridge-ui/public/assets/devices/batterydoorbell2kdual_large.jpg +0 -0
  350. package/homebridge-ui/public/assets/devices/batterydoorbell_e340_large.png +0 -0
  351. package/homebridge-ui/public/assets/devices/eufy-security-client.png +0 -0
  352. package/homebridge-ui/public/assets/devices/eufyCamS4_large.png +0 -0
  353. package/homebridge-ui/public/assets/devices/eufycam2_large.png +0 -0
  354. package/homebridge-ui/public/assets/devices/eufycam2c_large.jpg +0 -0
  355. package/homebridge-ui/public/assets/devices/eufycam2cpro_large.jpg +0 -0
  356. package/homebridge-ui/public/assets/devices/eufycam2pro_large.jpg +0 -0
  357. package/homebridge-ui/public/assets/devices/eufycam3_large.jpg +0 -0
  358. package/homebridge-ui/public/assets/devices/eufycam3c_large.jpg +0 -0
  359. package/homebridge-ui/public/assets/devices/eufycam3pro_large.png +0 -0
  360. package/homebridge-ui/public/assets/devices/eufycam_large.jpg +0 -0
  361. package/homebridge-ui/public/assets/devices/eufycame330_large.jpg +0 -0
  362. package/homebridge-ui/public/assets/devices/flood_freeze_sensor_e20_t90F0_large.png +0 -0
  363. package/homebridge-ui/public/assets/devices/floodlight2_large.jpg +0 -0
  364. package/homebridge-ui/public/assets/devices/floodlight2pro_large.jpg +0 -0
  365. package/homebridge-ui/public/assets/devices/floodlight_large.jpg +0 -0
  366. package/homebridge-ui/public/assets/devices/floodlightcame340_large.jpg +0 -0
  367. package/homebridge-ui/public/assets/devices/garage_camera_t8452_large.png +0 -0
  368. package/homebridge-ui/public/assets/devices/homebase2_large.png +0 -0
  369. package/homebridge-ui/public/assets/devices/homebase3_large.png +0 -0
  370. package/homebridge-ui/public/assets/devices/homebase_large.jpg +0 -0
  371. package/homebridge-ui/public/assets/devices/homebasemini_large.png +0 -0
  372. package/homebridge-ui/public/assets/devices/indoorcamC210_large.png +0 -0
  373. package/homebridge-ui/public/assets/devices/indoorcamC220_large.png +0 -0
  374. package/homebridge-ui/public/assets/devices/indoorcamC220_t8419N_large.png +0 -0
  375. package/homebridge-ui/public/assets/devices/indoorcamE30_large.png +0 -0
  376. package/homebridge-ui/public/assets/devices/indoorcamc120_large.png +0 -0
  377. package/homebridge-ui/public/assets/devices/indoorcammini_large.jpg +0 -0
  378. package/homebridge-ui/public/assets/devices/indoorcamp24_large.png +0 -0
  379. package/homebridge-ui/public/assets/devices/indoorcams350_large.png +0 -0
  380. package/homebridge-ui/public/assets/devices/keypad_large.png +0 -0
  381. package/homebridge-ui/public/assets/devices/minibase_chime_T8023_large.jpg +0 -0
  382. package/homebridge-ui/public/assets/devices/motionsensor_large.png +0 -0
  383. package/homebridge-ui/public/assets/devices/nvr_s4_max_T8N00_large.png +0 -0
  384. package/homebridge-ui/public/assets/devices/pir_sensor_e20_large.png +0 -0
  385. package/homebridge-ui/public/assets/devices/poe_bullet_ptz_cam_s4_T8E00_large.png +0 -0
  386. package/homebridge-ui/public/assets/devices/sensor_large.png +0 -0
  387. package/homebridge-ui/public/assets/devices/siren_sensor_e20_large.png +0 -0
  388. package/homebridge-ui/public/assets/devices/smartdrop_t8790_large.png +0 -0
  389. package/homebridge-ui/public/assets/devices/smartlock_t8500_large.png +0 -0
  390. package/homebridge-ui/public/assets/devices/smartlock_t8500_wifibridge_large.jpg +0 -0
  391. package/homebridge-ui/public/assets/devices/smartlock_t8503_large.png +0 -0
  392. package/homebridge-ui/public/assets/devices/smartlock_t8504_large.jpg +0 -0
  393. package/homebridge-ui/public/assets/devices/smartlock_t8510P_t8520P_large.png +0 -0
  394. package/homebridge-ui/public/assets/devices/smartlock_t85D0_large.png +0 -0
  395. package/homebridge-ui/public/assets/devices/smartlock_t85L0_large.png +0 -0
  396. package/homebridge-ui/public/assets/devices/smartlock_t85P0_large.png +0 -0
  397. package/homebridge-ui/public/assets/devices/smartlock_t85V0_large.png +0 -0
  398. package/homebridge-ui/public/assets/devices/smartlock_touch_and_wifi_t8502_large.png +0 -0
  399. package/homebridge-ui/public/assets/devices/smartlock_touch_and_wifi_t8506_large.png +0 -0
  400. package/homebridge-ui/public/assets/devices/smartlock_touch_and_wifi_t8520_large.png +0 -0
  401. package/homebridge-ui/public/assets/devices/smartlock_touch_t8510_large.png +0 -0
  402. package/homebridge-ui/public/assets/devices/smartlock_touch_t8510_wifibridge_large.jpg +0 -0
  403. package/homebridge-ui/public/assets/devices/smartlock_video_t8530_large.png +0 -0
  404. package/homebridge-ui/public/assets/devices/smartlock_video_t8531_large.png +0 -0
  405. package/homebridge-ui/public/assets/devices/smartlockwifibridge_t8021_large.jpg +0 -0
  406. package/homebridge-ui/public/assets/devices/smartsafe_s10_t7400_large.png +0 -0
  407. package/homebridge-ui/public/assets/devices/smartsafe_s12_t7401_large.png +0 -0
  408. package/homebridge-ui/public/assets/devices/smarttrack_card_t87B2_large.png +0 -0
  409. package/homebridge-ui/public/assets/devices/smarttrack_link_t87B0_large.png +0 -0
  410. package/homebridge-ui/public/assets/devices/smoke_alarm_e10_t90S0_large.png +0 -0
  411. package/homebridge-ui/public/assets/devices/solocamc210_large.jpg +0 -0
  412. package/homebridge-ui/public/assets/devices/solocamc35_large.png +0 -0
  413. package/homebridge-ui/public/assets/devices/solocame20_large.jpg +0 -0
  414. package/homebridge-ui/public/assets/devices/solocame30_large.png +0 -0
  415. package/homebridge-ui/public/assets/devices/solocame40_large.jpg +0 -0
  416. package/homebridge-ui/public/assets/devices/solocame42_large.png +0 -0
  417. package/homebridge-ui/public/assets/devices/solocaml20_large.jpg +0 -0
  418. package/homebridge-ui/public/assets/devices/solocams220_large.jpg +0 -0
  419. package/homebridge-ui/public/assets/devices/solocams340_large.png +0 -0
  420. package/homebridge-ui/public/assets/devices/solocams40_large.jpg +0 -0
  421. package/homebridge-ui/public/assets/devices/soloindoorcamc24_large.jpg +0 -0
  422. package/homebridge-ui/public/assets/devices/solooutdoorcamc22_large.png +0 -0
  423. package/homebridge-ui/public/assets/devices/solooutdoorcamc24_large.jpg +0 -0
  424. package/homebridge-ui/public/assets/devices/unknown.png +0 -0
  425. package/homebridge-ui/public/assets/devices/walllight_s100_large.png +0 -0
  426. package/homebridge-ui/public/assets/devices/walllight_s120_large.png +0 -0
  427. package/homebridge-ui/public/assets/devices/water_freeze_sensor_t8920_large.png +0 -0
  428. package/homebridge-ui/public/assets/devices/wireddoorbell1080p_large.jpg +0 -0
  429. package/homebridge-ui/public/assets/devices/wireddoorbell2k_large.png +0 -0
  430. package/homebridge-ui/public/assets/devices/wireddoorbelldual_large.jpg +0 -0
  431. package/homebridge-ui/public/assets/icons/attach.svg +0 -1
  432. package/homebridge-ui/public/assets/icons/battery_0.svg +0 -1
  433. package/homebridge-ui/public/assets/icons/battery_1.svg +0 -1
  434. package/homebridge-ui/public/assets/icons/battery_2.svg +0 -1
  435. package/homebridge-ui/public/assets/icons/battery_3.svg +0 -1
  436. package/homebridge-ui/public/assets/icons/battery_4.svg +0 -1
  437. package/homebridge-ui/public/assets/icons/battery_5.svg +0 -1
  438. package/homebridge-ui/public/assets/icons/battery_6.svg +0 -1
  439. package/homebridge-ui/public/assets/icons/copy.svg +0 -1
  440. package/homebridge-ui/public/assets/icons/delete.svg +0 -1
  441. package/homebridge-ui/public/assets/icons/download.svg +0 -1
  442. package/homebridge-ui/public/assets/icons/info.svg +0 -1
  443. package/homebridge-ui/public/assets/icons/refresh.svg +0 -1
  444. package/homebridge-ui/public/assets/icons/satellite_alt.svg +0 -1
  445. package/homebridge-ui/public/assets/icons/solar_power.svg +0 -1
  446. package/homebridge-ui/public/components/device-card.js +0 -174
  447. package/homebridge-ui/public/components/guard-modes.js +0 -88
  448. package/homebridge-ui/public/components/number-input.js +0 -121
  449. package/homebridge-ui/public/components/select.js +0 -73
  450. package/homebridge-ui/public/components/toggle.js +0 -68
  451. package/homebridge-ui/public/services/api.js +0 -250
  452. package/homebridge-ui/public/services/config.js +0 -144
  453. package/homebridge-ui/public/style.css +0 -818
  454. package/homebridge-ui/public/utils/countries.js +0 -73
  455. package/homebridge-ui/public/utils/device-images.js +0 -102
  456. package/homebridge-ui/public/utils/helpers.js +0 -115
  457. package/homebridge-ui/public/views/dashboard.js +0 -226
  458. package/homebridge-ui/public/views/device-detail.js +0 -626
  459. package/homebridge-ui/public/views/diagnostics.js +0 -402
  460. package/homebridge-ui/public/views/login.js +0 -650
  461. package/homebridge-ui/public/views/recordings.js +0 -319
  462. package/homebridge-ui/public/views/settings.js +0 -197
  463. package/homebridge-ui/public/views/unsupported-detail.js +0 -296
  464. package/media/Snapshot-Unavailable.png +0 -0
  465. package/media/Snapshot-Unavailable.xcf +0 -0
  466. package/media/Snapshot-black.png +0 -0
  467. package/media/camera-disabled.png +0 -0
  468. package/media/camera-offline.png +0 -0
  469. package/scripts/decrypt-diagnostics.mjs +0 -257
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Counts concurrent media work against the ceiling an operator declared, and refuses what would exceed it.
3
+ *
4
+ * The ceiling is a count rather than a measurement because neither input a plugin could measure it from is
5
+ * sound. A core count is not a capacity — on a containerised eight-core host the readable quota was 2.5 cores
6
+ * — and a container quota is not a substitute, because a host without one exposes no such file. Throughput
7
+ * the running adaptations report is measurable but not attributable: a session coding below its negotiated
8
+ * rate reads identically whether the host is saturated, the SDK source stalled, backpressure is holding it,
9
+ * or the camera simply delivers below that rate, and refusing a session for a cause the signal cannot
10
+ * establish presents as a broken camera.
11
+ *
12
+ * A ceiling of zero declares no ceiling, so an installation that never sets one is never counted and behaves
13
+ * exactly as it did before this existed.
14
+ */
15
+ export class DeclaredMediaSessionBudget {
16
+ ceiling;
17
+ held = 0;
18
+ constructor(ceiling) {
19
+ this.ceiling = ceiling;
20
+ }
21
+ /** One share of the ceiling, or nothing when granting it would exceed what the operator declared. */
22
+ claim() {
23
+ if (this.ceiling > 0 && this.held >= this.ceiling) {
24
+ return undefined;
25
+ }
26
+ this.held += 1;
27
+ let released = false;
28
+ return {
29
+ release: () => {
30
+ if (released) {
31
+ return;
32
+ }
33
+ released = true;
34
+ this.held -= 1;
35
+ },
36
+ };
37
+ }
38
+ }
39
+ //# sourceMappingURL=session-budget.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-budget.js","sourceRoot":"","sources":["../../src/media/session-budget.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;GAaG;AACH,MAAM,OAAO,0BAA0B;IAGR,OAAO;IAF5B,IAAI,GAAG,CAAC,CAAC;IAEjB,YAA6B,OAAe;uBAAf,OAAO;IAAW,CAAC;IAEhD,qGAAqG;IACrG,KAAK;QACH,IAAI,IAAI,CAAC,OAAO,GAAG,CAAC,IAAI,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YAClD,OAAO,SAAS,CAAC;QACnB,CAAC;QACD,IAAI,CAAC,IAAI,IAAI,CAAC,CAAC;QACf,IAAI,QAAQ,GAAG,KAAK,CAAC;QACrB,OAAO;YACL,OAAO,EAAE,GAAG,EAAE;gBACZ,IAAI,QAAQ,EAAE,CAAC;oBACb,OAAO;gBACT,CAAC;gBACD,QAAQ,GAAG,IAAI,CAAC;gBAChB,IAAI,CAAC,IAAI,IAAI,CAAC,CAAC;YACjB,CAAC;SACF,CAAC;IACJ,CAAC;CACF"}
@@ -0,0 +1,406 @@
1
+ import { readFile } from 'node:fs/promises';
2
+ import { LiveSnapshotUnavailableError, StoredSnapshotUnavailableError } from '@mega-yfue/eufy-sdk';
3
+ const LIVE_REFRESH_INTERVAL_MS = 120_000;
4
+ /**
5
+ * How much later than the interval a refresh may fall due, drawn independently per camera per refresh.
6
+ *
7
+ * The spread only ever delays a refresh, because the interval is a floor on what this policy is allowed to
8
+ * cost rather than a target. Half the interval separates the cameras of one household within a few rounds, and
9
+ * makes a retained image only that much staler than the interval already allows.
10
+ */
11
+ const LIVE_REFRESH_SPREAD_MS = LIVE_REFRESH_INTERVAL_MS / 2;
12
+ /** The largest image this plugin will accept or retain, which keeps one inside the Homebridge backup limit. */
13
+ export const MAXIMUM_IMAGE_BYTES = 10 * 1024 * 1024;
14
+ export function isBoundedJpeg(jpeg) {
15
+ return (jpeg.length > 5 &&
16
+ jpeg.length <= MAXIMUM_IMAGE_BYTES &&
17
+ jpeg[0] === 0xff &&
18
+ jpeg[1] === 0xd8 &&
19
+ jpeg[2] === 0xff &&
20
+ jpeg[jpeg.length - 2] === 0xff &&
21
+ jpeg[jpeg.length - 1] === 0xd9);
22
+ }
23
+ /**
24
+ * The images served when a camera cannot supply one. They ship beside the package as baseline JPEGs at the
25
+ * largest resolution HomeKit asks for, because a controller scales what it is given and these paths must not
26
+ * depend on an encoder being available.
27
+ *
28
+ * Resolving them relative to this module reaches the same packaged files whether the module was loaded from
29
+ * `src/` or from `dist/`, both of which sit one directory below the package root.
30
+ */
31
+ const PACKAGED_IMAGES = {
32
+ disabled: new URL('../../media/camera-disabled.jpg', import.meta.url),
33
+ offline: new URL('../../media/camera-offline.jpg', import.meta.url),
34
+ unavailable: new URL('../../media/Snapshot-Unavailable.jpg', import.meta.url),
35
+ };
36
+ const packagedImages = new Map();
37
+ /**
38
+ * Reads one packaged image at most once per process, and answers nothing at all when this package does not
39
+ * carry it.
40
+ *
41
+ * The read in progress is what is retained rather than its result, so concurrent requests for the same
42
+ * placeholder share one read instead of each starting another.
43
+ */
44
+ function packagedImage(name) {
45
+ let pending = packagedImages.get(name);
46
+ if (!pending) {
47
+ pending = readFile(PACKAGED_IMAGES[name]).catch(() => undefined);
48
+ packagedImages.set(name, pending);
49
+ }
50
+ return pending;
51
+ }
52
+ /**
53
+ * One acquisition outcome that left a request unanswered, carrying which acquisition it was rather than a
54
+ * message a consumer would have to parse. The cause stays as the message, because a bounded vocabulary is
55
+ * the only thing that may leave this domain.
56
+ */
57
+ class UnansweredSnapshot extends Error {
58
+ failure;
59
+ constructor(failure, message) {
60
+ super(message);
61
+ this.failure = failure;
62
+ }
63
+ }
64
+ /**
65
+ * The plugin reason for each reason the SDK's stored acquisition reports.
66
+ *
67
+ * The SDK owns why its own acquisition could not answer, so these are its words under a prefix naming the
68
+ * acquisition rather than a plugin re-interpretation of them. The tables are exhaustive by type, so an SDK
69
+ * that declares a new reason fails this build; one that reports a reason outside its declared union at
70
+ * runtime is treated as the acquisition having failed without saying more.
71
+ */
72
+ const STORED_FAILURES = {
73
+ 'not-observed': 'stored-not-observed',
74
+ pending: 'stored-pending',
75
+ 'download-failed': 'stored-download-failed',
76
+ 'invalid-image': 'stored-invalid-image',
77
+ };
78
+ /** The plugin reason for each reason the SDK's live still acquisition reports. */
79
+ const LIVE_FAILURES = {
80
+ 'no-keyframe': 'live-no-keyframe',
81
+ 'source-failed': 'live-source-failed',
82
+ 'undecodable-burst': 'live-undecodable-burst',
83
+ 'decoder-unavailable': 'live-decoder-unavailable',
84
+ };
85
+ /**
86
+ * Why the stored acquisition produced no usable image. An SDK refusal carries its own bounded reason; any
87
+ * other outcome, including bytes this plugin refused to accept as an image, is the acquisition having
88
+ * failed without saying more than that.
89
+ */
90
+ function storedFailure(error) {
91
+ return error instanceof StoredSnapshotUnavailableError && Object.hasOwn(STORED_FAILURES, error.reason)
92
+ ? STORED_FAILURES[error.reason]
93
+ : 'stored-failed';
94
+ }
95
+ /** Why the live still acquisition produced no usable image, under the same rule as the stored one. */
96
+ function liveFailure(error) {
97
+ return error instanceof LiveSnapshotUnavailableError && Object.hasOwn(LIVE_FAILURES, error.reason)
98
+ ? LIVE_FAILURES[error.reason]
99
+ : 'live-failed';
100
+ }
101
+ /**
102
+ * Which acquisition left a request unanswered. An outcome this domain did not classify is reported as no
103
+ * acquisition having answered, because that is the only claim it can still make truthfully.
104
+ */
105
+ function unansweredBy(error) {
106
+ return error instanceof UnansweredSnapshot ? error.failure : 'no-acquisition';
107
+ }
108
+ /**
109
+ * The share held by work whose cost the declared ceiling has already admitted somewhere else.
110
+ *
111
+ * Exactly two cases qualify, and both would be charged twice by a claim of their own. A request that joins an
112
+ * acquisition already in flight is answered by that acquisition's decoder rather than one of its own; a still
113
+ * taken from a source a live session is holding open adds a decode to a pull that session already paid for. A
114
+ * ceiling of one would otherwise refuse a camera a still while it was streaming, which is the case sharing a
115
+ * warm source exists to serve.
116
+ */
117
+ const UNBILLED = { release: () => undefined };
118
+ /**
119
+ * What an acquisition counts against when no ceiling was declared: nothing.
120
+ *
121
+ * Normalising the absent budget to one that admits everything keeps a single encoding of "no ceiling". Reading
122
+ * the absence at each decision instead would make one `undefined` mean both "nothing was declared" and "the
123
+ * host has no room", which are opposite answers.
124
+ */
125
+ const UNBOUNDED_MEDIA = { claim: () => UNBILLED };
126
+ /** Applies externally distinct stored-only, fresh-live, and retained-image acquisition policies. */
127
+ export class SnapshotAcquisition {
128
+ images;
129
+ budget;
130
+ packaged;
131
+ random;
132
+ stations;
133
+ pendingLive = new WeakMap();
134
+ liveRefreshDueAtMs = new Map();
135
+ retentionGenerations = new Map();
136
+ failedLive = new Map();
137
+ constructor(images, budget, packaged = packagedImage, random = Math.random, stations) {
138
+ this.images = images;
139
+ this.budget = budget;
140
+ this.packaged = packaged;
141
+ this.random = random;
142
+ this.stations = stations;
143
+ }
144
+ /**
145
+ * Answers one snapshot request under the selected policy.
146
+ *
147
+ * A camera an admitted observation reports as disabled is presented with the packaged disabled image and
148
+ * nothing else: no acquisition is attempted, and its retained image is kept but never served, because a
149
+ * real frame from before the camera was switched off would misrepresent what it is doing now. An absent or
150
+ * malformed observation is not a disabled one and falls through to the normal policy.
151
+ *
152
+ * After a permitted acquisition fails, a last successful real image wins over an explicit offline
153
+ * presentation. Offline is shown only from a typed unavailable observation with no retained image; all
154
+ * other empty states use the packaged unavailable image. A missing or malformed packaged image leaves the
155
+ * request failing rather than serving bytes HomeKit cannot decode.
156
+ *
157
+ * Every request no camera image could answer names the acquisition that left it unanswered through
158
+ * `onUnavailable`, whether a placeholder was substituted or nothing could be served at all. An intended
159
+ * disabled or offline presentation names nothing, because that image is the answer rather than a
160
+ * substitution for a missing one; a disabled camera whose packaged image this package does not carry
161
+ * therefore fails silently here, because the fault is the installation rather than the camera.
162
+ */
163
+ acquire(scope, source, mode, presentation = {}) {
164
+ if (presentation.enabled === false) {
165
+ return this.presentable('disabled').then((disabled) => {
166
+ if (disabled) {
167
+ return disabled;
168
+ }
169
+ throw new Error('disabled camera presentation is unavailable');
170
+ });
171
+ }
172
+ return this.acquired(scope, source, mode, presentation).catch(async (error) => {
173
+ const retained = await this.images?.read(scope.serial);
174
+ if (retained) {
175
+ return retained;
176
+ }
177
+ if (presentation.availability === 'unavailable') {
178
+ const offline = await this.presentable('offline');
179
+ if (offline) {
180
+ return offline;
181
+ }
182
+ }
183
+ presentation.onUnavailable?.(unansweredBy(error));
184
+ const unavailable = await this.presentable('unavailable');
185
+ if (!unavailable) {
186
+ throw error;
187
+ }
188
+ return unavailable;
189
+ });
190
+ }
191
+ /** Retains one real image from a source another successful HomeKit live session already holds open. */
192
+ async captureFromWarmLive(scope, source) {
193
+ const capture = this.live(scope, source, UNBILLED);
194
+ if (capture) {
195
+ await capture;
196
+ }
197
+ }
198
+ discard(serial) {
199
+ this.retentionGenerations.set(serial, this.retentionGeneration(serial) + 1);
200
+ this.failedLive.delete(serial);
201
+ void this.images?.discard?.(serial);
202
+ }
203
+ reconcile(serials) {
204
+ const current = new Set(serials);
205
+ for (const serial of this.retentionGenerations.keys()) {
206
+ if (!current.has(serial)) {
207
+ this.retentionGenerations.set(serial, this.retentionGeneration(serial) + 1);
208
+ }
209
+ }
210
+ for (const serial of this.failedLive.keys()) {
211
+ if (!current.has(serial)) {
212
+ this.failedLive.delete(serial);
213
+ }
214
+ }
215
+ void this.images?.reconcile?.(current);
216
+ }
217
+ /** One packaged image, or nothing when this package does not carry a decodable one under that name. */
218
+ async presentable(name) {
219
+ const image = await this.packaged(name);
220
+ return image && isBoundedJpeg(image) ? image : undefined;
221
+ }
222
+ /**
223
+ * The acquisition each mode is entitled to, rejecting with the one that left the request unanswered.
224
+ *
225
+ * A `Refresh` camera with nothing retained and no stored acquisition is answered by the live refresh it
226
+ * just started, so it has no retained image yet; once that refresh has failed, the failure it reported is
227
+ * the honest reason for every later request until one succeeds.
228
+ */
229
+ acquired(scope, source, mode, presentation) {
230
+ if (mode === 'Cloud') {
231
+ return (this.stored(scope, source) ??
232
+ Promise.reject(new UnansweredSnapshot('stored-unavailable', 'stored camera snapshot is unavailable')));
233
+ }
234
+ if (mode === 'Live') {
235
+ if (!source.snapshotLive) {
236
+ return Promise.reject(new UnansweredSnapshot('live-unavailable', 'live camera snapshot is unavailable'));
237
+ }
238
+ const claim = this.claimForLive(scope);
239
+ if (!claim) {
240
+ return Promise.reject(new UnansweredSnapshot('live-at-capacity', 'the declared concurrent media limit is reached'));
241
+ }
242
+ return this.live(scope, source, claim);
243
+ }
244
+ if (mode === 'Refresh') {
245
+ if (presentation.availability !== 'unavailable') {
246
+ this.refreshLiveWhenDue(scope, source, presentation);
247
+ }
248
+ return this.refreshed(scope, source);
249
+ }
250
+ throw new TypeError(`unsupported snapshot acquisition mode: ${mode}`);
251
+ }
252
+ /** What a `Refresh` camera can answer with now: its retained image, or the stored acquisition instead. */
253
+ async refreshed(scope, source) {
254
+ const retained = await this.images?.read(scope.serial);
255
+ if (retained) {
256
+ return retained;
257
+ }
258
+ const stored = this.stored(scope, source);
259
+ if (stored) {
260
+ return stored;
261
+ }
262
+ throw new UnansweredSnapshot(this.pendingRefreshFailure(scope, source), 'no camera snapshot image is available');
263
+ }
264
+ /**
265
+ * Whether a `Refresh` camera has nothing yet, nothing since its live still acquisition failed, or
266
+ * nothing ever. The remembered failure is the acquisition's own, whichever call made it: a still a warm
267
+ * live session was asked for is the same acquisition a refresh performs, so its failure explains a later
268
+ * unanswered request just as well.
269
+ */
270
+ pendingRefreshFailure(scope, source) {
271
+ if (!source.snapshotLive) {
272
+ return 'no-acquisition';
273
+ }
274
+ return this.failedLive.get(scope.serial) ?? 'no-retained-image';
275
+ }
276
+ stored(scope, source) {
277
+ const snapshotStored = source.snapshotStored;
278
+ if (!snapshotStored) {
279
+ return undefined;
280
+ }
281
+ const generation = this.retentionGeneration(scope.serial);
282
+ return Promise.resolve()
283
+ .then(snapshotStored)
284
+ .then((jpeg) => {
285
+ return this.retain(scope, jpeg, 'stored-only', generation);
286
+ })
287
+ .catch((error) => {
288
+ throw new UnansweredSnapshot(storedFailure(error), errorMessage(error));
289
+ });
290
+ }
291
+ /**
292
+ * One share of the declared ceiling for a still, or nothing when the host has no room for another.
293
+ *
294
+ * A request joining an acquisition already in flight rides that acquisition's share, because it is answered
295
+ * by the same decoder.
296
+ */
297
+ claimForLive(scope) {
298
+ if (this.pendingLive.has(scope.identity)) {
299
+ return UNBILLED;
300
+ }
301
+ return (this.budget ?? UNBOUNDED_MEDIA).claim();
302
+ }
303
+ live(scope, source, claim) {
304
+ const snapshotLive = source.snapshotLive;
305
+ if (!snapshotLive) {
306
+ claim.release();
307
+ return undefined;
308
+ }
309
+ const current = this.pendingLive.get(scope.identity);
310
+ if (current) {
311
+ claim.release();
312
+ return current;
313
+ }
314
+ const generation = this.retentionGeneration(scope.serial);
315
+ /**
316
+ * A still holds the station while it captures, so a live view opened on a sibling can ask it to yield.
317
+ * Yielding aborts the acquisition rather than waiting it out: an abandoned burst that keeps asking for the
318
+ * channel is exactly the contention the hold exists to remove, and the last good image answers the request
319
+ * in its place.
320
+ */
321
+ const abandonment = new AbortController();
322
+ const releaseStation = scope.stationSn !== undefined
323
+ ? this.stations?.hold(scope.stationSn, scope.serial, 'snapshot', () => abandonment.abort(new Error('a live view took the station')))
324
+ : undefined;
325
+ const pending = Promise.resolve()
326
+ .then(() => snapshotLive({ signal: abandonment.signal }))
327
+ .then(({ jpeg }) => {
328
+ const retained = this.retain(scope, jpeg, 'live', generation);
329
+ this.failedLive.delete(scope.serial);
330
+ return retained;
331
+ })
332
+ .catch((error) => {
333
+ const failure = liveFailure(error);
334
+ this.failedLive.set(scope.serial, failure);
335
+ throw new UnansweredSnapshot(failure, errorMessage(error));
336
+ })
337
+ .finally(() => {
338
+ releaseStation?.();
339
+ claim.release();
340
+ if (this.pendingLive.get(scope.identity) === pending) {
341
+ this.pendingLive.delete(scope.identity);
342
+ }
343
+ });
344
+ this.pendingLive.set(scope.identity, pending);
345
+ return pending;
346
+ }
347
+ /** Only validated source images may be presented as camera imagery or retained for later requests. */
348
+ retain(scope, jpeg, provenance, generation) {
349
+ if (!isBoundedJpeg(jpeg)) {
350
+ throw new Error('camera snapshot is not a bounded JPEG');
351
+ }
352
+ if (generation === this.retentionGeneration(scope.serial)) {
353
+ void this.images?.write(scope.serial, jpeg, provenance);
354
+ }
355
+ return jpeg;
356
+ }
357
+ retentionGeneration(serial) {
358
+ return this.retentionGenerations.get(serial) ?? 0;
359
+ }
360
+ /**
361
+ * Refresh acquires live imagery only on request, no more than once per spread interval, and never polls.
362
+ *
363
+ * The next due time is committed when a refresh starts rather than derived when a request is decided, so
364
+ * the spread each camera drew holds however often that camera is asked about. Deriving it per request would
365
+ * take the shortest of many draws and return the busiest cameras to a shared clock.
366
+ *
367
+ * A refresh the declared ceiling had no room for never happened, so it commits no window: the camera keeps
368
+ * answering from its retained image and the next request tries again. Spending the window on a refusal would
369
+ * let a busy host push every camera's refresh further away precisely while they are all being asked for.
370
+ *
371
+ * A refresh that fails while the camera still has nothing retained explains the placeholder that camera
372
+ * is showing, so it is reported through the request that started it. A camera whose retained image
373
+ * already answers its requests reports nothing, because a stale real image is still a camera image.
374
+ */
375
+ refreshLiveWhenDue(scope, source, presentation) {
376
+ const dueAt = this.liveRefreshDueAtMs.get(scope.serial);
377
+ if (dueAt !== undefined && Date.now() < dueAt) {
378
+ return;
379
+ }
380
+ if (scope.stationSn !== undefined && this.stations?.admits(scope.stationSn, scope.serial, 'snapshot') === false) {
381
+ return;
382
+ }
383
+ if (this.pendingLive.has(scope.identity) || !source.snapshotLive) {
384
+ return;
385
+ }
386
+ const claim = this.claimForLive(scope);
387
+ if (!claim) {
388
+ return;
389
+ }
390
+ const refresh = this.live(scope, source, claim);
391
+ if (!refresh) {
392
+ return;
393
+ }
394
+ this.liveRefreshDueAtMs.set(scope.serial, Date.now() + LIVE_REFRESH_INTERVAL_MS + this.random() * LIVE_REFRESH_SPREAD_MS);
395
+ refresh.catch(async (error) => {
396
+ if (!(await this.images?.read(scope.serial))) {
397
+ presentation.onUnavailable?.(unansweredBy(error));
398
+ }
399
+ });
400
+ }
401
+ }
402
+ /** The cause of one failed acquisition, kept inside this domain as a message rather than a claim. */
403
+ function errorMessage(error) {
404
+ return error instanceof Error ? error.message : String(error);
405
+ }
406
+ //# sourceMappingURL=snapshot.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"snapshot.js","sourceRoot":"","sources":["../../src/media/snapshot.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,101 @@
1
+ /**
2
+ * Which claim holds each station's one live channel, and who yields to whom.
3
+ *
4
+ * A HomeBase fans several cameras over one session and serves them ONE at a time, and the SDK refuses a second
5
+ * camera rather than admitting it and degrading both. Deciding which camera deserves the station is this
6
+ * plugin's, because it depends on what HomeKit shows at once and on what the operator is looking at, neither of
7
+ * which the SDK can know. A standalone camera is its own station and contends with nobody.
8
+ *
9
+ * The order is `live`, then `recording`, then `snapshot`:
10
+ *
11
+ * - A live view is on a screen now. Jitter there is the one degradation nobody can absorb, and an operator
12
+ * who opened a camera is telling us which one matters.
13
+ * - A recording writes to a file, so it survives being interrupted less well than being jittery, but it is
14
+ * not free to abandon the way a still is: a missed event cannot be re-taken.
15
+ * - A still fills a tile that is off screen exactly while a live view is on it, and re-running it costs
16
+ * nobody anything. Where it cannot run, the last good image stands in.
17
+ *
18
+ * A stronger claim ASKS the weaker holders to yield rather than seizing the station: each holder registered how
19
+ * to abandon its own work, and abandoning is what frees the channel the SDK will otherwise refuse. A holder
20
+ * that registered no way to yield is left alone, so nothing is dropped that cannot be stopped cleanly.
21
+ *
22
+ * None of it applies WITHIN one camera. Every egress on a camera shares one pull, so a recording and a live
23
+ * view of the same camera are served together and neither yields to the other. That is what a motion
24
+ * notification produces once the operator taps the tile, and it is the common shape rather than the exception.
25
+ */
26
+ const CLAIM_RANK = { live: 3, recording: 2, snapshot: 1 };
27
+ /**
28
+ * The one registry of live sessions a station is serving, keyed by station serial.
29
+ *
30
+ * Both sides of the question read it: HomeKit records a live session on its camera's station, and snapshot
31
+ * acquisition asks whether a station is already serving one before opening a burst on it.
32
+ */
33
+ export class StationLiveSessions {
34
+ sessions = new Map();
35
+ /** How many stations are currently holding at least one session. */
36
+ get held() {
37
+ return this.sessions.size;
38
+ }
39
+ /** The strongest claim currently held on `stationSn`, or `undefined` where nothing holds it. */
40
+ heldFor(stationSn) {
41
+ let strongest;
42
+ for (const session of this.sessions.get(stationSn) ?? []) {
43
+ if (strongest === undefined || CLAIM_RANK[session.claim] > CLAIM_RANK[strongest]) {
44
+ strongest = session.claim;
45
+ }
46
+ }
47
+ return strongest;
48
+ }
49
+ /**
50
+ * Whether `claim` may take this station now.
51
+ *
52
+ * Equal claims do not displace each other: a second live view does not evict the first, and the SDK refuses
53
+ * it, which is the honest answer for a station that cannot serve both.
54
+ */
55
+ admits(stationSn, camera, claim) {
56
+ let strongestElsewhere;
57
+ for (const session of this.sessions.get(stationSn) ?? []) {
58
+ if (session.camera === camera) {
59
+ return true;
60
+ }
61
+ if (strongestElsewhere === undefined || CLAIM_RANK[session.claim] > CLAIM_RANK[strongestElsewhere]) {
62
+ strongestElsewhere = session.claim;
63
+ }
64
+ }
65
+ return strongestElsewhere === undefined || CLAIM_RANK[claim] > CLAIM_RANK[strongestElsewhere];
66
+ }
67
+ /**
68
+ * Record one session on `stationSn`, asking anything weaker to yield first, and answer the release that ends
69
+ * it.
70
+ *
71
+ * `abandon` is how this session gives the station back before it would have finished. It is called at most
72
+ * once, and never on the session that is taking the station.
73
+ */
74
+ hold(stationSn, camera, claim, abandon) {
75
+ const yielding = [...(this.sessions.get(stationSn) ?? [])].filter((session) => session.camera !== camera && CLAIM_RANK[claim] > CLAIM_RANK[session.claim]);
76
+ const session = abandon ? { camera, claim, abandon } : { camera, claim };
77
+ const held = this.sessions.get(stationSn) ?? new Set();
78
+ held.add(session);
79
+ this.sessions.set(stationSn, held);
80
+ for (const weaker of yielding) {
81
+ if (weaker.asked) {
82
+ continue;
83
+ }
84
+ weaker.asked = true;
85
+ weaker.abandon?.();
86
+ }
87
+ let released = false;
88
+ return () => {
89
+ if (released) {
90
+ return;
91
+ }
92
+ released = true;
93
+ const remaining = this.sessions.get(stationSn);
94
+ remaining?.delete(session);
95
+ if (remaining && remaining.size === 0) {
96
+ this.sessions.delete(stationSn);
97
+ }
98
+ };
99
+ }
100
+ }
101
+ //# sourceMappingURL=station-live-sessions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"station-live-sessions.js","sourceRoot":"","sources":["../../src/media/station-live-sessions.ts"],"names":[],"mappings":"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"}