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