@homebridge-plugins/homebridge-eufy-security 4.7.2 → 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.
- 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
|
@@ -1,296 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Unsupported Device Detail View — shows device info dump and guided CTA
|
|
3
|
-
* for requesting support via GitHub.
|
|
4
|
-
*/
|
|
5
|
-
// eslint-disable-next-line no-unused-vars
|
|
6
|
-
const UnsupportedDetailView = {
|
|
7
|
-
|
|
8
|
-
_container: null,
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* @param {HTMLElement} container
|
|
12
|
-
* @param {string} id - uniqueId / serial number
|
|
13
|
-
*/
|
|
14
|
-
async render(container, id) {
|
|
15
|
-
this._container = container;
|
|
16
|
-
container.innerHTML = '';
|
|
17
|
-
|
|
18
|
-
const accessory = this._findAccessory(id);
|
|
19
|
-
|
|
20
|
-
if (!accessory) {
|
|
21
|
-
container.innerHTML = `
|
|
22
|
-
<div class="text-center text-muted py-5">
|
|
23
|
-
<p>Device not found.</p>
|
|
24
|
-
<button class="btn btn-outline-secondary btn-sm" id="btn-back">Back to Dashboard</button>
|
|
25
|
-
</div>`;
|
|
26
|
-
container.querySelector('#btn-back').addEventListener('click', () => App.navigate('dashboard'));
|
|
27
|
-
return;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
// Header (no image for unsupported)
|
|
31
|
-
this._renderHeader(container, accessory);
|
|
32
|
-
|
|
33
|
-
// Main content
|
|
34
|
-
const content = document.createElement('div');
|
|
35
|
-
await this._renderDetail(content, accessory);
|
|
36
|
-
container.appendChild(content);
|
|
37
|
-
},
|
|
38
|
-
|
|
39
|
-
// ===== Header =====
|
|
40
|
-
_renderHeader(container, accessory) {
|
|
41
|
-
const header = document.createElement('div');
|
|
42
|
-
header.className = 'detail-header';
|
|
43
|
-
|
|
44
|
-
const backBtn = document.createElement('button');
|
|
45
|
-
backBtn.className = 'btn btn-link p-0';
|
|
46
|
-
backBtn.innerHTML = '← Back';
|
|
47
|
-
backBtn.style.textDecoration = 'none';
|
|
48
|
-
backBtn.addEventListener('click', () => App.navigate('dashboard'));
|
|
49
|
-
|
|
50
|
-
const info = document.createElement('div');
|
|
51
|
-
info.className = 'detail-header__info';
|
|
52
|
-
info.innerHTML = `
|
|
53
|
-
<h5>${Helpers.escHtml(accessory.displayName)}</h5>
|
|
54
|
-
<small>${accessory.typename || ('Type ' + accessory.type)} · ${accessory.uniqueId}</small>
|
|
55
|
-
`;
|
|
56
|
-
|
|
57
|
-
header.appendChild(backBtn);
|
|
58
|
-
header.appendChild(info);
|
|
59
|
-
container.appendChild(header);
|
|
60
|
-
},
|
|
61
|
-
|
|
62
|
-
// ===== Detail Content =====
|
|
63
|
-
async _renderDetail(content, accessory) {
|
|
64
|
-
const REPO = 'homebridge-plugins/homebridge-eufy-security';
|
|
65
|
-
const LABEL = 'device-support';
|
|
66
|
-
const COMPAT_URL = 'https://bropat.github.io/eufy-security-client/#/supported_devices';
|
|
67
|
-
|
|
68
|
-
const section = document.createElement('div');
|
|
69
|
-
section.className = 'detail-section unsupported-detail';
|
|
70
|
-
|
|
71
|
-
// Description
|
|
72
|
-
const desc = document.createElement('p');
|
|
73
|
-
desc.className = 'text-muted';
|
|
74
|
-
desc.innerHTML =
|
|
75
|
-
'<strong>' + Helpers.iconHtml('info.svg') + ' This device was detected but is not yet supported.</strong><br />' +
|
|
76
|
-
'New device support must first be added to the eufy-security-client library. ' +
|
|
77
|
-
'If your device is not on the compatibility list, please <strong>search for existing issues first</strong> before opening a new one.';
|
|
78
|
-
section.appendChild(desc);
|
|
79
|
-
|
|
80
|
-
// Device info dump — all available data in one box
|
|
81
|
-
const props = accessory.properties || {};
|
|
82
|
-
|
|
83
|
-
// Load raw intel from unsupported.json (served via /unsupportedDevices endpoint)
|
|
84
|
-
let unsupportedIntel = null;
|
|
85
|
-
try {
|
|
86
|
-
const unsupportedData = await Api.loadUnsupportedDevices();
|
|
87
|
-
const entries = unsupportedData.devices || [];
|
|
88
|
-
unsupportedIntel = entries.find(e => e.uniqueId === accessory.uniqueId) || null;
|
|
89
|
-
} catch (e) {
|
|
90
|
-
// ignore — will fall back to basic properties
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
const deviceInfo = {
|
|
94
|
-
uniqueId: accessory.uniqueId,
|
|
95
|
-
displayName: accessory.displayName,
|
|
96
|
-
type: accessory.type,
|
|
97
|
-
typename: accessory.typename || undefined,
|
|
98
|
-
...props,
|
|
99
|
-
...(unsupportedIntel ? {
|
|
100
|
-
rawDevice: unsupportedIntel.rawDevice,
|
|
101
|
-
rawProperties: unsupportedIntel.rawProperties,
|
|
102
|
-
} : {}),
|
|
103
|
-
};
|
|
104
|
-
// Remove potentially large/sensitive fields
|
|
105
|
-
delete deviceInfo.picture;
|
|
106
|
-
|
|
107
|
-
// CTA stepper + buttons
|
|
108
|
-
const stepsWrap = document.createElement('div');
|
|
109
|
-
stepsWrap.className = 'unsupported-detail__steps';
|
|
110
|
-
|
|
111
|
-
// Stepper frieze
|
|
112
|
-
const stepper = document.createElement('div');
|
|
113
|
-
stepper.className = 'unsupported-stepper';
|
|
114
|
-
const steps = [
|
|
115
|
-
{ num: '1', label: 'Check', color: 'success' },
|
|
116
|
-
{ num: '2', label: 'Search', color: 'primary' },
|
|
117
|
-
{ num: '3', label: 'Copy', color: 'warning' },
|
|
118
|
-
{ num: '4', label: 'Create', color: 'danger' },
|
|
119
|
-
];
|
|
120
|
-
steps.forEach((step, i) => {
|
|
121
|
-
const stepEl = document.createElement('div');
|
|
122
|
-
stepEl.className = 'unsupported-stepper__step';
|
|
123
|
-
stepEl.innerHTML = `<span class="unsupported-stepper__circle unsupported-stepper__circle--${step.color}">${step.num}</span><span class="unsupported-stepper__label">${step.label}</span>`;
|
|
124
|
-
stepper.appendChild(stepEl);
|
|
125
|
-
if (i < steps.length - 1) {
|
|
126
|
-
const line = document.createElement('div');
|
|
127
|
-
line.className = 'unsupported-stepper__line';
|
|
128
|
-
stepper.appendChild(line);
|
|
129
|
-
}
|
|
130
|
-
});
|
|
131
|
-
stepsWrap.appendChild(stepper);
|
|
132
|
-
|
|
133
|
-
// Buttons row
|
|
134
|
-
const btnGroup = document.createElement('div');
|
|
135
|
-
btnGroup.className = 'unsupported-detail__actions';
|
|
136
|
-
|
|
137
|
-
// 1) Check compatibility list
|
|
138
|
-
const compatBtn = document.createElement('a');
|
|
139
|
-
compatBtn.href = COMPAT_URL;
|
|
140
|
-
compatBtn.target = '_blank';
|
|
141
|
-
compatBtn.rel = 'noopener noreferrer';
|
|
142
|
-
compatBtn.className = 'btn btn-success';
|
|
143
|
-
compatBtn.textContent = 'Check Compatibility ↗';
|
|
144
|
-
btnGroup.appendChild(compatBtn);
|
|
145
|
-
|
|
146
|
-
// 2) Search existing issues with label
|
|
147
|
-
const searchQuery = encodeURIComponent(`is:issue label:${LABEL} ${accessory.type}`);
|
|
148
|
-
const searchBtn = document.createElement('a');
|
|
149
|
-
searchBtn.href = `https://github.com/${REPO}/issues?q=${searchQuery}`;
|
|
150
|
-
searchBtn.target = '_blank';
|
|
151
|
-
searchBtn.rel = 'noopener noreferrer';
|
|
152
|
-
searchBtn.className = 'btn btn-outline-primary';
|
|
153
|
-
searchBtn.textContent = 'Search Existing Issues ↗';
|
|
154
|
-
btnGroup.appendChild(searchBtn);
|
|
155
|
-
|
|
156
|
-
// 3) Copy device JSON to clipboard
|
|
157
|
-
const deviceDump = JSON.stringify(deviceInfo, null, 2);
|
|
158
|
-
const copyJsonBtn = document.createElement('button');
|
|
159
|
-
copyJsonBtn.className = 'btn btn-outline-warning';
|
|
160
|
-
copyJsonBtn.textContent = 'Copy Device Info';
|
|
161
|
-
copyJsonBtn.addEventListener('click', () => {
|
|
162
|
-
this._copyToClipboard(deviceDump).then(() => {
|
|
163
|
-
copyJsonBtn.textContent = '✓ Copied!';
|
|
164
|
-
setTimeout(() => { copyJsonBtn.textContent = 'Copy Device Info'; }, 2000);
|
|
165
|
-
});
|
|
166
|
-
});
|
|
167
|
-
btnGroup.appendChild(copyJsonBtn);
|
|
168
|
-
|
|
169
|
-
// 4) Create new issue (without embedding JSON — user pastes it)
|
|
170
|
-
const model = props.model || accessory.type;
|
|
171
|
-
const issueTitle = encodeURIComponent(`[Device Support] ${model} (Type ${accessory.type})`);
|
|
172
|
-
const templateParams = [
|
|
173
|
-
`template=device_support.yml`,
|
|
174
|
-
`title=${issueTitle}`,
|
|
175
|
-
`labels=${LABEL}`,
|
|
176
|
-
].join('&');
|
|
177
|
-
const createBtn = document.createElement('a');
|
|
178
|
-
createBtn.href = `https://github.com/${REPO}/issues/new?${templateParams}`;
|
|
179
|
-
createBtn.target = '_blank';
|
|
180
|
-
createBtn.rel = 'noopener noreferrer';
|
|
181
|
-
createBtn.className = 'btn btn-outline-danger';
|
|
182
|
-
createBtn.textContent = 'Create an Issue ↗';
|
|
183
|
-
btnGroup.appendChild(createBtn);
|
|
184
|
-
|
|
185
|
-
stepsWrap.appendChild(btnGroup);
|
|
186
|
-
|
|
187
|
-
// Paste reminder note
|
|
188
|
-
const pasteNote = document.createElement('p');
|
|
189
|
-
pasteNote.className = 'unsupported-detail__paste-note';
|
|
190
|
-
pasteNote.innerHTML = Helpers.iconHtml('info.svg', 14) +
|
|
191
|
-
' Copy the <strong>Device Information</strong> below first, then paste it into the issue form on GitHub.';
|
|
192
|
-
stepsWrap.appendChild(pasteNote);
|
|
193
|
-
|
|
194
|
-
section.appendChild(stepsWrap);
|
|
195
|
-
|
|
196
|
-
// External links note
|
|
197
|
-
const extNote = document.createElement('p');
|
|
198
|
-
extNote.className = 'text-muted';
|
|
199
|
-
extNote.style.cssText = 'font-size: 0.75rem; text-align: right;';
|
|
200
|
-
extNote.textContent = '↗ These links open in a new browser tab on GitHub.';
|
|
201
|
-
section.appendChild(extNote);
|
|
202
|
-
|
|
203
|
-
const infoTitle = document.createElement('div');
|
|
204
|
-
infoTitle.className = 'detail-section__title';
|
|
205
|
-
infoTitle.textContent = 'Device Information';
|
|
206
|
-
section.appendChild(infoTitle);
|
|
207
|
-
|
|
208
|
-
const infoNote = document.createElement('p');
|
|
209
|
-
infoNote.className = 'text-muted';
|
|
210
|
-
infoNote.style.cssText = 'font-size: 0.75rem;';
|
|
211
|
-
infoNote.textContent = 'Diagnostic information below includes raw device properties from the client library.';
|
|
212
|
-
section.appendChild(infoNote);
|
|
213
|
-
|
|
214
|
-
// JSON dump with copy button overlay
|
|
215
|
-
const dumpWrap = document.createElement('div');
|
|
216
|
-
dumpWrap.className = 'unsupported-detail__dump-wrap';
|
|
217
|
-
|
|
218
|
-
const copyBtn = document.createElement('button');
|
|
219
|
-
copyBtn.className = 'unsupported-detail__copy-btn';
|
|
220
|
-
|
|
221
|
-
const _setCopyLabel = (text) => {
|
|
222
|
-
copyBtn.innerHTML = '';
|
|
223
|
-
copyBtn.appendChild(Helpers.icon('copy.svg'));
|
|
224
|
-
copyBtn.append(' ' + text);
|
|
225
|
-
};
|
|
226
|
-
_setCopyLabel('Copy');
|
|
227
|
-
|
|
228
|
-
copyBtn.addEventListener('click', () => {
|
|
229
|
-
this._copyToClipboard(pre.textContent).then(() => {
|
|
230
|
-
copyBtn.textContent = '✓ Copied!';
|
|
231
|
-
setTimeout(() => { _setCopyLabel('Copy'); }, 2000);
|
|
232
|
-
});
|
|
233
|
-
});
|
|
234
|
-
|
|
235
|
-
const pre = document.createElement('pre');
|
|
236
|
-
pre.className = 'unsupported-detail__dump';
|
|
237
|
-
pre.textContent = JSON.stringify(deviceInfo, null, 2);
|
|
238
|
-
|
|
239
|
-
dumpWrap.appendChild(copyBtn);
|
|
240
|
-
dumpWrap.appendChild(pre);
|
|
241
|
-
section.appendChild(dumpWrap);
|
|
242
|
-
|
|
243
|
-
content.appendChild(section);
|
|
244
|
-
},
|
|
245
|
-
|
|
246
|
-
// ===== Helpers =====
|
|
247
|
-
|
|
248
|
-
/**
|
|
249
|
-
* Copy text to clipboard with fallback for non-secure contexts (e.g. Homebridge iframe).
|
|
250
|
-
* @param {string} text
|
|
251
|
-
* @returns {Promise<void>}
|
|
252
|
-
*/
|
|
253
|
-
_copyToClipboard(text) {
|
|
254
|
-
if (navigator.clipboard && navigator.clipboard.writeText) {
|
|
255
|
-
return navigator.clipboard.writeText(text).catch(() => this._copyFallback(text));
|
|
256
|
-
}
|
|
257
|
-
return this._copyFallback(text);
|
|
258
|
-
},
|
|
259
|
-
|
|
260
|
-
_copyFallback(text) {
|
|
261
|
-
return new Promise((resolve, reject) => {
|
|
262
|
-
const textarea = document.createElement('textarea');
|
|
263
|
-
textarea.value = text;
|
|
264
|
-
textarea.style.cssText = 'position:fixed;left:-9999px;top:-9999px;opacity:0';
|
|
265
|
-
document.body.appendChild(textarea);
|
|
266
|
-
textarea.select();
|
|
267
|
-
try {
|
|
268
|
-
document.execCommand('copy') ? resolve() : reject(new Error('execCommand failed'));
|
|
269
|
-
} catch (err) {
|
|
270
|
-
reject(err);
|
|
271
|
-
} finally {
|
|
272
|
-
document.body.removeChild(textarea);
|
|
273
|
-
}
|
|
274
|
-
});
|
|
275
|
-
},
|
|
276
|
-
|
|
277
|
-
/**
|
|
278
|
-
* Find an accessory (device or station) by uniqueId across all stations.
|
|
279
|
-
* For standalone devices the station and device share the same uniqueId —
|
|
280
|
-
* prefer the device because it carries richer data.
|
|
281
|
-
* @param {string} id
|
|
282
|
-
* @returns {object|null}
|
|
283
|
-
*/
|
|
284
|
-
_findAccessory(id) {
|
|
285
|
-
const stations = App.state.stations || [];
|
|
286
|
-
for (const s of stations) {
|
|
287
|
-
// Check devices first — standalone devices have the same uniqueId as their station
|
|
288
|
-
// but carry richer data
|
|
289
|
-
for (const d of s.devices || []) {
|
|
290
|
-
if (d.uniqueId === id) return d;
|
|
291
|
-
}
|
|
292
|
-
if (s.uniqueId === id) return s;
|
|
293
|
-
}
|
|
294
|
-
return null;
|
|
295
|
-
},
|
|
296
|
-
};
|
|
Binary file
|
|
Binary file
|
package/media/Snapshot-black.png
DELETED
|
Binary file
|
|
Binary file
|
package/media/camera-offline.png
DELETED
|
Binary file
|
|
@@ -1,257 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Decrypt and extract an encrypted diagnostics archive produced by the plugin.
|
|
5
|
-
*
|
|
6
|
-
* Usage:
|
|
7
|
-
* node decrypt-diagnostics.mjs <encrypted-file.tar.gz> [private-key.pem]
|
|
8
|
-
*
|
|
9
|
-
* If the private key is omitted, the script looks for it in the keys/
|
|
10
|
-
* directory next to itself (keys/diagnostics_private.pem).
|
|
11
|
-
*
|
|
12
|
-
* The archive is decrypted and extracted into a folder next to the input file,
|
|
13
|
-
* named after the archive (e.g. diagnostics-2026-03-02-11-25-31/).
|
|
14
|
-
*
|
|
15
|
-
* File format (the .tar.gz is actually encrypted, not a plain gzip):
|
|
16
|
-
* [4 bytes] – magic: "DIAG"
|
|
17
|
-
* [1 byte] – format version (currently 0x01)
|
|
18
|
-
* [8 bytes] – creation timestamp (BigUInt64BE, ms since Unix epoch)
|
|
19
|
-
* [2 bytes] – big-endian uint16: length of the encrypted AES key
|
|
20
|
-
* [N bytes] – RSA-OAEP encrypted AES-256 key
|
|
21
|
-
* [12 bytes] – AES-GCM initialisation vector
|
|
22
|
-
* [16 bytes] – AES-GCM authentication tag
|
|
23
|
-
* [rest] – AES-256-GCM ciphertext
|
|
24
|
-
*
|
|
25
|
-
* AAD (Additional Authenticated Data) covers bytes 0 through the end of the
|
|
26
|
-
* encrypted AES key. GCM verifies this automatically during decryption.
|
|
27
|
-
*/
|
|
28
|
-
|
|
29
|
-
import crypto from 'node:crypto';
|
|
30
|
-
import fs from 'node:fs';
|
|
31
|
-
import path from 'node:path';
|
|
32
|
-
import { execFileSync, spawnSync } from 'node:child_process';
|
|
33
|
-
import { fileURLToPath } from 'node:url';
|
|
34
|
-
|
|
35
|
-
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
36
|
-
const DEFAULT_KEY = path.join(__dirname, '..', 'keys', 'diagnostics_private.pem');
|
|
37
|
-
|
|
38
|
-
/** Maximum allowed .enc file size (140 MB). */
|
|
39
|
-
const MAX_ENC_SIZE = 140 * 1024 * 1024;
|
|
40
|
-
|
|
41
|
-
/** Maximum number of entries allowed in the archive. */
|
|
42
|
-
const MAX_ENTRIES = 100;
|
|
43
|
-
|
|
44
|
-
/** Only these file extensions are expected in a legitimate diagnostics archive. */
|
|
45
|
-
const ALLOWED_EXTENSIONS = new Set(['.log', '.gz', '.json']);
|
|
46
|
-
|
|
47
|
-
/** Archives older than this are flagged with a warning (90 days). */
|
|
48
|
-
const EXPIRY_MS = 90 * 24 * 60 * 60 * 1000;
|
|
49
|
-
|
|
50
|
-
function main() {
|
|
51
|
-
const args = process.argv.slice(2);
|
|
52
|
-
|
|
53
|
-
if (args.length < 1) {
|
|
54
|
-
console.error('Usage: node decrypt-diagnostics.mjs <encrypted-file.tar.gz> [private-key.pem]');
|
|
55
|
-
process.exit(1);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
const encFile = path.resolve(args[0]);
|
|
59
|
-
let keyFile = args[1] ? path.resolve(args[1]) : DEFAULT_KEY;
|
|
60
|
-
|
|
61
|
-
if (!fs.existsSync(keyFile)) {
|
|
62
|
-
console.error(`Private key not found: ${keyFile}`);
|
|
63
|
-
process.exit(1);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
// Read inputs
|
|
67
|
-
const encBuffer = fs.readFileSync(encFile);
|
|
68
|
-
|
|
69
|
-
// Reject oversized files
|
|
70
|
-
if (encBuffer.length > MAX_ENC_SIZE) {
|
|
71
|
-
console.error(`File too large (${(encBuffer.length / 1024 / 1024).toFixed(1)} MB) — max allowed is ${MAX_ENC_SIZE / 1024 / 1024} MB.`);
|
|
72
|
-
process.exit(1);
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
const privateKey = fs.readFileSync(keyFile, 'utf-8');
|
|
76
|
-
|
|
77
|
-
// Validate minimum size: 4 (magic) + 1 (version) + 8 (timestamp) + 2 (keyLen) + 512 (RSA-4096 encrypted key) + 12 (IV) + 16 (tag) + 1 (min ciphertext)
|
|
78
|
-
const MIN_SIZE = 4 + 1 + 8 + 2 + 512 + 12 + 16 + 1;
|
|
79
|
-
if (encBuffer.length < MIN_SIZE) {
|
|
80
|
-
console.error(`File too small (${encBuffer.length} bytes) — not a valid encrypted diagnostics archive.`);
|
|
81
|
-
process.exit(1);
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
// Verify magic header and version
|
|
85
|
-
let offset = 0;
|
|
86
|
-
|
|
87
|
-
const magic = encBuffer.subarray(offset, offset + 4).toString();
|
|
88
|
-
offset += 4;
|
|
89
|
-
if (magic !== 'DIAG') {
|
|
90
|
-
console.error(`Invalid file — expected magic "DIAG", got "${magic}".`);
|
|
91
|
-
process.exit(1);
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
const version = encBuffer[offset];
|
|
95
|
-
offset += 1;
|
|
96
|
-
if (version !== 0x01) {
|
|
97
|
-
console.error(`Unsupported format version ${version} — this script supports version 1.`);
|
|
98
|
-
process.exit(1);
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
// Parse the envelope
|
|
102
|
-
const createdAtMs = Number(encBuffer.readBigUInt64BE(offset));
|
|
103
|
-
offset += 8;
|
|
104
|
-
const createdAt = new Date(createdAtMs);
|
|
105
|
-
|
|
106
|
-
if (Number.isNaN(createdAt.getTime())) {
|
|
107
|
-
console.error('Invalid timestamp in archive header — file may be corrupted or tampered.');
|
|
108
|
-
process.exit(1);
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
console.log(`Archive created: ${createdAt.toISOString()}`);
|
|
112
|
-
|
|
113
|
-
const ageMs = Date.now() - createdAtMs;
|
|
114
|
-
if (ageMs > EXPIRY_MS) {
|
|
115
|
-
const ageDays = Math.round(ageMs / (24 * 60 * 60 * 1000));
|
|
116
|
-
console.warn(`⚠️ WARNING: This archive is ${ageDays} days old (created ${createdAt.toISOString()}).`);
|
|
117
|
-
}
|
|
118
|
-
if (createdAtMs > Date.now() + 24 * 60 * 60 * 1000) {
|
|
119
|
-
console.warn('⚠️ WARNING: Archive timestamp is in the future — clock skew or tampering?');
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
const keyLen = encBuffer.readUInt16BE(offset);
|
|
123
|
-
offset += 2;
|
|
124
|
-
|
|
125
|
-
if (offset + keyLen + 12 + 16 > encBuffer.length) {
|
|
126
|
-
console.error('Malformed archive — encrypted key length exceeds file size.');
|
|
127
|
-
process.exit(1);
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
const encryptedKey = encBuffer.subarray(offset, offset + keyLen);
|
|
131
|
-
offset += keyLen;
|
|
132
|
-
|
|
133
|
-
// The AAD covers everything from byte 0 through the end of encryptedKey
|
|
134
|
-
// (magic + version + timestamp + keyLen + encryptedKey)
|
|
135
|
-
const aadEnd = 4 + 1 + 8 + 2 + keyLen;
|
|
136
|
-
const aad = encBuffer.subarray(0, aadEnd);
|
|
137
|
-
|
|
138
|
-
const iv = encBuffer.subarray(offset, offset + 12);
|
|
139
|
-
offset += 12;
|
|
140
|
-
|
|
141
|
-
const authTag = encBuffer.subarray(offset, offset + 16);
|
|
142
|
-
offset += 16;
|
|
143
|
-
|
|
144
|
-
const ciphertext = encBuffer.subarray(offset);
|
|
145
|
-
|
|
146
|
-
// Unwrap the AES key with the private RSA key
|
|
147
|
-
let aesKey;
|
|
148
|
-
try {
|
|
149
|
-
aesKey = crypto.privateDecrypt(
|
|
150
|
-
{
|
|
151
|
-
key: privateKey,
|
|
152
|
-
padding: crypto.constants.RSA_PKCS1_OAEP_PADDING,
|
|
153
|
-
oaepHash: 'sha256',
|
|
154
|
-
},
|
|
155
|
-
encryptedKey,
|
|
156
|
-
);
|
|
157
|
-
} catch (err) {
|
|
158
|
-
console.error('Failed to decrypt AES key — wrong private key?', err.message);
|
|
159
|
-
process.exit(2);
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
// Decrypt the payload
|
|
163
|
-
const decipher = crypto.createDecipheriv('aes-256-gcm', aesKey, iv);
|
|
164
|
-
aesKey.fill(0); // scrub key from memory
|
|
165
|
-
decipher.setAAD(aad);
|
|
166
|
-
decipher.setAuthTag(authTag);
|
|
167
|
-
|
|
168
|
-
let decrypted;
|
|
169
|
-
try {
|
|
170
|
-
decrypted = Buffer.concat([decipher.update(ciphertext), decipher.final()]);
|
|
171
|
-
} catch (err) {
|
|
172
|
-
console.error('Decryption failed — data may be corrupted.', err.message);
|
|
173
|
-
process.exit(3);
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
// Determine output directory next to the .enc file
|
|
177
|
-
const encDir = path.dirname(encFile);
|
|
178
|
-
const baseName = path.basename(encFile)
|
|
179
|
-
.replace(/\.tar\.gz(?:\.enc)?$/, '');
|
|
180
|
-
const outDir = path.join(encDir, baseName);
|
|
181
|
-
|
|
182
|
-
// --- Safety: inspect tar contents before extracting (piped via stdin, no temp file) ---
|
|
183
|
-
const listResult = spawnSync('tar', ['tzf', '-'], { input: decrypted, encoding: 'utf-8', maxBuffer: 10 * 1024 * 1024 });
|
|
184
|
-
if (listResult.status !== 0) {
|
|
185
|
-
console.error('Failed to list archive contents:', listResult.stderr?.trim() || 'unknown error');
|
|
186
|
-
process.exit(4);
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
const listing = listResult.stdout.trim().split('\n');
|
|
190
|
-
|
|
191
|
-
// Reject archives with too many entries (tar bomb protection)
|
|
192
|
-
if (listing.length > MAX_ENTRIES) {
|
|
193
|
-
console.error(`BLOCKED: archive contains ${listing.length} entries (max ${MAX_ENTRIES}).`);
|
|
194
|
-
console.error('This file may be a tar bomb. Aborting.');
|
|
195
|
-
process.exit(5);
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
for (const entry of listing) {
|
|
199
|
-
const normalised = path.normalize(entry);
|
|
200
|
-
|
|
201
|
-
// Path traversal / absolute path check
|
|
202
|
-
if (path.isAbsolute(normalised) || normalised.startsWith('..')) {
|
|
203
|
-
console.error(`BLOCKED: archive contains path-traversal entry: "${entry}"`);
|
|
204
|
-
console.error('This file may be maliciously crafted. Aborting.');
|
|
205
|
-
process.exit(5);
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
// Extension allowlist (skip directory entries ending with /)
|
|
209
|
-
if (!entry.endsWith('/')) {
|
|
210
|
-
const ext = path.extname(entry).toLowerCase();
|
|
211
|
-
// Handle compound extensions like .log.gz
|
|
212
|
-
const doubleExt = path.extname(entry.slice(0, -ext.length)).toLowerCase() + ext;
|
|
213
|
-
if (!ALLOWED_EXTENSIONS.has(ext) && !ALLOWED_EXTENSIONS.has(doubleExt)) {
|
|
214
|
-
console.error(`BLOCKED: archive contains file with disallowed extension: "${entry}"`);
|
|
215
|
-
console.error(`Only ${[...ALLOWED_EXTENSIONS].join(', ')} files are allowed.`);
|
|
216
|
-
process.exit(5);
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
// Check for symlinks inside the archive
|
|
222
|
-
const verboseResult = spawnSync('tar', ['tvzf', '-'], { input: decrypted, encoding: 'utf-8', maxBuffer: 10 * 1024 * 1024 });
|
|
223
|
-
if (verboseResult.status === 0) {
|
|
224
|
-
for (const line of verboseResult.stdout.trim().split('\n')) {
|
|
225
|
-
if (line.startsWith('l') || line.includes(' -> ')) {
|
|
226
|
-
console.error(`BLOCKED: archive contains a symlink: "${line.trim()}"`);
|
|
227
|
-
console.error('This file may be maliciously crafted. Aborting.');
|
|
228
|
-
process.exit(5);
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
// --- Safe to extract (piped via stdin, no temp file on disk) ---
|
|
234
|
-
fs.mkdirSync(outDir, { recursive: true });
|
|
235
|
-
const extractResult = spawnSync('tar', ['xzf', '-', '-C', outDir], { input: decrypted, stdio: ['pipe', 'inherit', 'inherit'] });
|
|
236
|
-
if (extractResult.status !== 0) {
|
|
237
|
-
console.error('Failed to extract archive.');
|
|
238
|
-
process.exit(4);
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
// Strip execute permissions from all extracted files
|
|
242
|
-
const files = fs.readdirSync(outDir);
|
|
243
|
-
for (const f of files) {
|
|
244
|
-
const filePath = path.join(outDir, f);
|
|
245
|
-
try {
|
|
246
|
-
fs.chmodSync(filePath, 0o644);
|
|
247
|
-
} catch {
|
|
248
|
-
// Ignore permission errors on platforms that don't support chmod
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
console.log(`Decrypted and extracted to: ${outDir}/`);
|
|
253
|
-
console.log(`${files.length} file(s):`);
|
|
254
|
-
files.forEach(f => console.log(` ${f}`));
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
main();
|