@switchbot/homebridge-switchbot 5.0.0-beta.11 → 5.0.0-beta.110
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/.github/ISSUE_TEMPLATE/e2e-verification.md +36 -0
- package/.github/workflows/ci.yml +32 -0
- package/.github/workflows/manual-e2e.yml +115 -0
- package/.github/workflows/release.yml +0 -4
- package/CHANGELOG.md +35 -0
- package/E2E-VERIFICATION.md +121 -0
- package/MIGRATION.md +44 -0
- package/README.md +56 -3
- package/config.schema.json +170 -14787
- package/dist/device-types.d.ts +31 -0
- package/dist/device-types.d.ts.map +1 -0
- package/dist/device-types.js +216 -0
- package/dist/device-types.js.map +1 -0
- package/dist/deviceFactory.d.ts +13 -0
- package/dist/deviceFactory.d.ts.map +1 -0
- package/dist/deviceFactory.js +120 -0
- package/dist/deviceFactory.js.map +1 -0
- package/dist/devices/deviceBase.d.ts +50 -0
- package/dist/devices/deviceBase.d.ts.map +1 -0
- package/dist/devices/deviceBase.js +119 -0
- package/dist/devices/deviceBase.js.map +1 -0
- package/dist/devices/genericDevice.d.ts +298 -0
- package/dist/devices/genericDevice.d.ts.map +1 -0
- package/dist/devices/genericDevice.js +1067 -0
- package/dist/devices/genericDevice.js.map +1 -0
- package/dist/homebridge-ui/endpoints/config.d.ts +3 -0
- package/dist/homebridge-ui/endpoints/config.d.ts.map +1 -0
- package/dist/homebridge-ui/endpoints/config.js +104 -0
- package/dist/homebridge-ui/endpoints/config.js.map +1 -0
- package/dist/homebridge-ui/endpoints/credentials.d.ts +6 -0
- package/dist/homebridge-ui/endpoints/credentials.d.ts.map +1 -0
- package/dist/homebridge-ui/endpoints/credentials.js +65 -0
- package/dist/homebridge-ui/endpoints/credentials.js.map +1 -0
- package/dist/homebridge-ui/endpoints/devices.d.ts +6 -0
- package/dist/homebridge-ui/endpoints/devices.d.ts.map +1 -0
- package/dist/homebridge-ui/endpoints/devices.js +342 -0
- package/dist/homebridge-ui/endpoints/devices.js.map +1 -0
- package/dist/homebridge-ui/endpoints/discovery.d.ts +7 -0
- package/dist/homebridge-ui/endpoints/discovery.d.ts.map +1 -0
- package/dist/homebridge-ui/endpoints/discovery.js +171 -0
- package/dist/homebridge-ui/endpoints/discovery.js.map +1 -0
- package/dist/homebridge-ui/public/css/styles.css +254 -0
- package/dist/homebridge-ui/public/index.html +112 -247
- package/dist/homebridge-ui/public/js/api.d.ts +19 -0
- package/dist/homebridge-ui/public/js/api.d.ts.map +1 -0
- package/dist/homebridge-ui/public/js/api.js +178 -0
- package/dist/homebridge-ui/public/js/api.js.map +1 -0
- package/dist/homebridge-ui/public/js/api.ts +205 -0
- package/dist/homebridge-ui/public/js/app.d.ts +2 -0
- package/dist/homebridge-ui/public/js/app.d.ts.map +1 -0
- package/dist/homebridge-ui/public/js/app.js +1581 -0
- package/dist/homebridge-ui/public/js/app.js.map +7 -0
- package/dist/homebridge-ui/public/js/app.ts +22 -0
- package/dist/homebridge-ui/public/js/constants.d.ts +2 -0
- package/dist/homebridge-ui/public/js/constants.d.ts.map +1 -0
- package/dist/homebridge-ui/public/js/constants.js +2 -0
- package/dist/homebridge-ui/public/js/constants.js.map +1 -0
- package/dist/homebridge-ui/public/js/constants.ts +1 -0
- package/dist/homebridge-ui/public/js/credentials.d.ts +3 -0
- package/dist/homebridge-ui/public/js/credentials.d.ts.map +1 -0
- package/dist/homebridge-ui/public/js/credentials.js +96 -0
- package/dist/homebridge-ui/public/js/credentials.js.map +1 -0
- package/dist/homebridge-ui/public/js/credentials.ts +100 -0
- package/dist/homebridge-ui/public/js/devices-delete.d.ts +3 -0
- package/dist/homebridge-ui/public/js/devices-delete.d.ts.map +1 -0
- package/dist/homebridge-ui/public/js/devices-delete.js +206 -0
- package/dist/homebridge-ui/public/js/devices-delete.js.map +1 -0
- package/dist/homebridge-ui/public/js/devices-delete.ts +236 -0
- package/dist/homebridge-ui/public/js/devices.d.ts +9 -0
- package/dist/homebridge-ui/public/js/devices.d.ts.map +1 -0
- package/dist/homebridge-ui/public/js/devices.js +81 -0
- package/dist/homebridge-ui/public/js/devices.js.map +1 -0
- package/dist/homebridge-ui/public/js/devices.ts +88 -0
- package/dist/homebridge-ui/public/js/discovery.d.ts +3 -0
- package/dist/homebridge-ui/public/js/discovery.d.ts.map +1 -0
- package/dist/homebridge-ui/public/js/discovery.js +233 -0
- package/dist/homebridge-ui/public/js/discovery.js.map +1 -0
- package/dist/homebridge-ui/public/js/discovery.ts +277 -0
- package/dist/homebridge-ui/public/js/logger.d.ts +6 -0
- package/dist/homebridge-ui/public/js/logger.d.ts.map +1 -0
- package/dist/homebridge-ui/public/js/logger.js +12 -0
- package/dist/homebridge-ui/public/js/logger.js.map +1 -0
- package/dist/homebridge-ui/public/js/logger.ts +11 -0
- package/dist/homebridge-ui/public/js/modal.d.ts +5 -0
- package/dist/homebridge-ui/public/js/modal.d.ts.map +1 -0
- package/dist/homebridge-ui/public/js/modal.js +26 -0
- package/dist/homebridge-ui/public/js/modal.js.map +1 -0
- package/dist/homebridge-ui/public/js/modal.ts +28 -0
- package/dist/homebridge-ui/public/js/modals.d.ts +2 -0
- package/dist/homebridge-ui/public/js/modals.d.ts.map +1 -0
- package/dist/homebridge-ui/public/js/modals.js +177 -0
- package/dist/homebridge-ui/public/js/modals.js.map +1 -0
- package/dist/homebridge-ui/public/js/modals.ts +200 -0
- package/dist/homebridge-ui/public/js/render.d.ts +63 -0
- package/dist/homebridge-ui/public/js/render.d.ts.map +1 -0
- package/dist/homebridge-ui/public/js/render.js +439 -0
- package/dist/homebridge-ui/public/js/render.js.map +1 -0
- package/dist/homebridge-ui/public/js/render.ts +510 -0
- package/dist/homebridge-ui/public/js/toast.d.ts +6 -0
- package/dist/homebridge-ui/public/js/toast.d.ts.map +1 -0
- package/dist/homebridge-ui/public/js/toast.js +29 -0
- package/dist/homebridge-ui/public/js/toast.js.map +1 -0
- package/dist/homebridge-ui/public/js/toast.ts +37 -0
- package/dist/homebridge-ui/public/js/types.d.ts +23 -0
- package/dist/homebridge-ui/public/js/types.d.ts.map +1 -0
- package/dist/homebridge-ui/public/js/types.js +2 -0
- package/dist/homebridge-ui/public/js/types.js.map +1 -0
- package/dist/homebridge-ui/public/js/types.ts +26 -0
- package/dist/homebridge-ui/server.js +11 -41
- package/dist/homebridge-ui/server.js.map +1 -1
- package/dist/homebridge-ui/utils/config-parser.d.ts +35 -0
- package/dist/homebridge-ui/utils/config-parser.d.ts.map +1 -0
- package/dist/homebridge-ui/utils/config-parser.js +87 -0
- package/dist/homebridge-ui/utils/config-parser.js.map +1 -0
- package/dist/homebridge-ui/utils/device-migration.d.ts +35 -0
- package/dist/homebridge-ui/utils/device-migration.d.ts.map +1 -0
- package/dist/homebridge-ui/utils/device-migration.js +111 -0
- package/dist/homebridge-ui/utils/device-migration.js.map +1 -0
- package/dist/homebridge-ui/utils/logger.d.ts +7 -0
- package/dist/homebridge-ui/utils/logger.d.ts.map +1 -0
- package/dist/homebridge-ui/utils/logger.js +17 -0
- package/dist/homebridge-ui/utils/logger.js.map +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -65
- package/dist/index.js.map +1 -1
- package/dist/platform.d.ts +35 -0
- package/dist/platform.d.ts.map +1 -0
- package/dist/platform.js +1567 -0
- package/dist/platform.js.map +1 -0
- package/dist/settings.d.ts +10 -249
- package/dist/settings.d.ts.map +1 -1
- package/dist/settings.js +5 -30
- package/dist/settings.js.map +1 -1
- package/dist/switchbotClient.d.ts +32 -0
- package/dist/switchbotClient.d.ts.map +1 -0
- package/dist/switchbotClient.js +207 -0
- package/dist/switchbotClient.js.map +1 -0
- package/dist/utils.d.ts +39 -50
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +39 -688
- package/dist/utils.js.map +1 -1
- package/docs/assets/highlight.css +14 -0
- package/docs/assets/icons.js +1 -1
- package/docs/assets/icons.svg +1 -1
- package/docs/assets/main.js +2 -2
- package/docs/assets/style.css +3 -3
- package/docs/index.html +77 -13
- package/docs/variables/default.html +1 -1
- package/eslint.config.js +7 -8
- package/nodemon.json +2 -2
- package/package.json +35 -32
- package/scripts/build-ui.js +36 -0
- package/scripts/e2e/README.md +25 -0
- package/scripts/e2e/curtain-e2e.sh +70 -0
- package/scripts/e2e/fan-e2e.sh +75 -0
- package/scripts/e2e/light-advanced-e2e.sh +97 -0
- package/scripts/e2e/light-e2e.sh +75 -0
- package/scripts/e2e/list-accessories.sh +19 -0
- package/scripts/e2e/lock-e2e.sh +65 -0
- package/scripts/free-dev-ports.mjs +105 -0
- package/scripts/generate-matter-maps.js +60 -0
- package/scripts/run-e2e-local.sh +14 -0
- package/scripts/sync-device-types.mjs +28 -0
- package/src/device-types.ts +230 -0
- package/src/deviceFactory.ts +163 -0
- package/src/devices/deviceBase.ts +141 -0
- package/src/devices/genericDevice.ts +998 -0
- package/src/homebridge-ui/endpoints/config.ts +123 -0
- package/src/homebridge-ui/endpoints/credentials.ts +74 -0
- package/src/homebridge-ui/endpoints/devices.ts +380 -0
- package/src/homebridge-ui/endpoints/discovery.ts +191 -0
- package/src/homebridge-ui/public/css/styles.css +254 -0
- package/src/homebridge-ui/public/index.html +112 -247
- package/src/homebridge-ui/public/js/api.ts +205 -0
- package/src/homebridge-ui/public/js/app.ts +22 -0
- package/src/homebridge-ui/public/js/constants.ts +1 -0
- package/src/homebridge-ui/public/js/credentials.ts +100 -0
- package/src/homebridge-ui/public/js/devices-delete.ts +236 -0
- package/src/homebridge-ui/public/js/devices.ts +88 -0
- package/src/homebridge-ui/public/js/discovery.ts +277 -0
- package/src/homebridge-ui/public/js/logger.ts +11 -0
- package/src/homebridge-ui/public/js/modal.ts +28 -0
- package/src/homebridge-ui/public/js/modals.ts +200 -0
- package/src/homebridge-ui/public/js/render.ts +510 -0
- package/src/homebridge-ui/public/js/toast.ts +37 -0
- package/src/homebridge-ui/public/js/types.ts +26 -0
- package/src/homebridge-ui/server.ts +11 -43
- package/src/homebridge-ui/utils/config-parser.ts +108 -0
- package/src/homebridge-ui/utils/device-migration.ts +144 -0
- package/src/homebridge-ui/utils/logger.ts +17 -0
- package/src/index.ts +4 -66
- package/src/platform.ts +1566 -0
- package/src/settings.ts +12 -277
- package/src/switchbotClient.ts +216 -0
- package/src/utils.ts +45 -713
- package/switchbot-homebridge-switchbot-5.0.0.tgz +0 -0
- package/test/accessory-restore.spec.ts +73 -0
- package/test/device-mapping.spec.ts +37 -0
- package/test/deviceFactory.spec.ts +18 -0
- package/test/e2e/run-e2e.spec.ts +50 -0
- package/test/fan-swing.spec.ts +29 -0
- package/test/helpers/matter-harness.ts +53 -0
- package/test/lock-users.spec.ts +44 -0
- package/test/matter-childbridge.spec.ts +55 -0
- package/test/matter-descriptors.spec.ts +97 -0
- package/test/matter-device-state.spec.ts +101 -0
- package/test/matter-integration.spec.ts +70 -0
- package/test/platform.integration.spec.ts +55 -0
- package/test/switchbot-client-debounce.spec.ts +131 -0
- package/test/switchbot-client-openapi.spec.ts +56 -0
- package/test/switchbotClient.spec.ts +10 -0
- package/test/utils.spec.ts +20 -0
- package/todo/COMPLETED.md +189 -0
- package/todo/README.md +59 -0
- package/todo/TODO.md +205 -0
- package/todo/mockups/filtering-sorting.svg +112 -0
- package/todo/mockups/modal-switchbot-theme.svg +66 -0
- package/todo/mockups/signal-strength-visualization.svg +94 -0
- package/vitest.config.ts +7 -0
- package/coverage/base.css +0 -224
- package/coverage/block-navigation.js +0 -87
- package/coverage/clover.xml +0 -15847
- package/coverage/coverage-final.json +0 -42
- package/coverage/docs/assets/dmt/dmt-component-data.js.html +0 -85
- package/coverage/docs/assets/dmt/dmt-components.js.html +0 -286
- package/coverage/docs/assets/dmt/index.html +0 -131
- package/coverage/docs/assets/hierarchy.js.html +0 -85
- package/coverage/docs/assets/icons.js.html +0 -136
- package/coverage/docs/assets/index.html +0 -146
- package/coverage/docs/assets/main.js.html +0 -265
- package/coverage/favicon.png +0 -0
- package/coverage/index.html +0 -191
- package/coverage/prettify.css +0 -1
- package/coverage/prettify.js +0 -2
- package/coverage/sort-arrow-sprite.png +0 -0
- package/coverage/sorter.js +0 -196
- package/coverage/src/device/blindtilt.ts.html +0 -3238
- package/coverage/src/device/bot.ts.html +0 -2803
- package/coverage/src/device/ceilinglight.ts.html +0 -2338
- package/coverage/src/device/colorbulb.ts.html +0 -2824
- package/coverage/src/device/contact.ts.html +0 -1465
- package/coverage/src/device/curtain.ts.html +0 -2869
- package/coverage/src/device/device.ts.html +0 -2500
- package/coverage/src/device/fan.ts.html +0 -2242
- package/coverage/src/device/hub.ts.html +0 -1408
- package/coverage/src/device/humidifier.ts.html +0 -2116
- package/coverage/src/device/index.html +0 -416
- package/coverage/src/device/iosensor.ts.html +0 -1375
- package/coverage/src/device/lightstrip.ts.html +0 -2617
- package/coverage/src/device/lock.ts.html +0 -1963
- package/coverage/src/device/meter.ts.html +0 -1372
- package/coverage/src/device/meterplus.ts.html +0 -1384
- package/coverage/src/device/meterpro.ts.html +0 -1618
- package/coverage/src/device/motion.ts.html +0 -1264
- package/coverage/src/device/plug.ts.html +0 -1372
- package/coverage/src/device/relayswitch.ts.html +0 -2284
- package/coverage/src/device/robotvacuumcleaner.ts.html +0 -1810
- package/coverage/src/device/waterdetector.ts.html +0 -1294
- package/coverage/src/homebridge-ui/index.html +0 -116
- package/coverage/src/homebridge-ui/server.ts.html +0 -229
- package/coverage/src/index.html +0 -161
- package/coverage/src/index.ts.html +0 -124
- package/coverage/src/irdevice/airconditioner.ts.html +0 -1687
- package/coverage/src/irdevice/airpurifier.ts.html +0 -844
- package/coverage/src/irdevice/camera.ts.html +0 -475
- package/coverage/src/irdevice/fan.ts.html +0 -766
- package/coverage/src/irdevice/index.html +0 -251
- package/coverage/src/irdevice/irdevice.ts.html +0 -1117
- package/coverage/src/irdevice/light.ts.html +0 -826
- package/coverage/src/irdevice/other.ts.html +0 -2458
- package/coverage/src/irdevice/tv.ts.html +0 -1222
- package/coverage/src/irdevice/vacuumcleaner.ts.html +0 -466
- package/coverage/src/irdevice/waterheater.ts.html +0 -469
- package/coverage/src/platform.ts.html +0 -8776
- package/coverage/src/settings.ts.html +0 -934
- package/coverage/src/utils.ts.html +0 -2092
- package/dist/devices-hap/airpurifier.d.ts +0 -54
- package/dist/devices-hap/airpurifier.d.ts.map +0 -1
- package/dist/devices-hap/airpurifier.js +0 -527
- package/dist/devices-hap/airpurifier.js.map +0 -1
- package/dist/devices-hap/blindtilt.d.ts +0 -90
- package/dist/devices-hap/blindtilt.d.ts.map +0 -1
- package/dist/devices-hap/blindtilt.js +0 -974
- package/dist/devices-hap/blindtilt.js.map +0 -1
- package/dist/devices-hap/bot.d.ts +0 -102
- package/dist/devices-hap/bot.d.ts.map +0 -1
- package/dist/devices-hap/bot.js +0 -811
- package/dist/devices-hap/bot.js.map +0 -1
- package/dist/devices-hap/ceilinglight.d.ts +0 -85
- package/dist/devices-hap/ceilinglight.d.ts.map +0 -1
- package/dist/devices-hap/ceilinglight.js +0 -701
- package/dist/devices-hap/ceilinglight.js.map +0 -1
- package/dist/devices-hap/colorbulb.d.ts +0 -88
- package/dist/devices-hap/colorbulb.d.ts.map +0 -1
- package/dist/devices-hap/colorbulb.js +0 -881
- package/dist/devices-hap/colorbulb.js.map +0 -1
- package/dist/devices-hap/contact.d.ts +0 -44
- package/dist/devices-hap/contact.d.ts.map +0 -1
- package/dist/devices-hap/contact.js +0 -409
- package/dist/devices-hap/contact.js.map +0 -1
- package/dist/devices-hap/curtain.d.ts +0 -73
- package/dist/devices-hap/curtain.d.ts.map +0 -1
- package/dist/devices-hap/curtain.js +0 -869
- package/dist/devices-hap/curtain.js.map +0 -1
- package/dist/devices-hap/device.d.ts +0 -98
- package/dist/devices-hap/device.d.ts.map +0 -1
- package/dist/devices-hap/device.js +0 -749
- package/dist/devices-hap/device.js.map +0 -1
- package/dist/devices-hap/fan.d.ts +0 -69
- package/dist/devices-hap/fan.d.ts.map +0 -1
- package/dist/devices-hap/fan.js +0 -649
- package/dist/devices-hap/fan.js.map +0 -1
- package/dist/devices-hap/hub.d.ts +0 -37
- package/dist/devices-hap/hub.d.ts.map +0 -1
- package/dist/devices-hap/hub.js +0 -392
- package/dist/devices-hap/hub.js.map +0 -1
- package/dist/devices-hap/humidifier.d.ts +0 -68
- package/dist/devices-hap/humidifier.d.ts.map +0 -1
- package/dist/devices-hap/humidifier.js +0 -628
- package/dist/devices-hap/humidifier.js.map +0 -1
- package/dist/devices-hap/iosensor.d.ts +0 -42
- package/dist/devices-hap/iosensor.d.ts.map +0 -1
- package/dist/devices-hap/iosensor.js +0 -382
- package/dist/devices-hap/iosensor.js.map +0 -1
- package/dist/devices-hap/lightstrip.d.ts +0 -79
- package/dist/devices-hap/lightstrip.d.ts.map +0 -1
- package/dist/devices-hap/lightstrip.js +0 -797
- package/dist/devices-hap/lightstrip.js.map +0 -1
- package/dist/devices-hap/lock.d.ts +0 -53
- package/dist/devices-hap/lock.d.ts.map +0 -1
- package/dist/devices-hap/lock.js +0 -561
- package/dist/devices-hap/lock.js.map +0 -1
- package/dist/devices-hap/meter.d.ts +0 -37
- package/dist/devices-hap/meter.d.ts.map +0 -1
- package/dist/devices-hap/meter.js +0 -379
- package/dist/devices-hap/meter.js.map +0 -1
- package/dist/devices-hap/meterplus.d.ts +0 -42
- package/dist/devices-hap/meterplus.d.ts.map +0 -1
- package/dist/devices-hap/meterplus.js +0 -384
- package/dist/devices-hap/meterplus.js.map +0 -1
- package/dist/devices-hap/meterpro.d.ts +0 -43
- package/dist/devices-hap/meterpro.d.ts.map +0 -1
- package/dist/devices-hap/meterpro.js +0 -468
- package/dist/devices-hap/meterpro.js.map +0 -1
- package/dist/devices-hap/motion.d.ts +0 -42
- package/dist/devices-hap/motion.d.ts.map +0 -1
- package/dist/devices-hap/motion.js +0 -345
- package/dist/devices-hap/motion.js.map +0 -1
- package/dist/devices-hap/plug.d.ts +0 -49
- package/dist/devices-hap/plug.d.ts.map +0 -1
- package/dist/devices-hap/plug.js +0 -395
- package/dist/devices-hap/plug.js.map +0 -1
- package/dist/devices-hap/relayswitch.d.ts +0 -96
- package/dist/devices-hap/relayswitch.d.ts.map +0 -1
- package/dist/devices-hap/relayswitch.js +0 -642
- package/dist/devices-hap/relayswitch.js.map +0 -1
- package/dist/devices-hap/robotvacuumcleaner.d.ts +0 -54
- package/dist/devices-hap/robotvacuumcleaner.d.ts.map +0 -1
- package/dist/devices-hap/robotvacuumcleaner.js +0 -523
- package/dist/devices-hap/robotvacuumcleaner.js.map +0 -1
- package/dist/devices-hap/waterdetector.d.ts +0 -41
- package/dist/devices-hap/waterdetector.d.ts.map +0 -1
- package/dist/devices-hap/waterdetector.js +0 -356
- package/dist/devices-hap/waterdetector.js.map +0 -1
- package/dist/devices-matter/BaseMatterAccessory.d.ts +0 -63
- package/dist/devices-matter/BaseMatterAccessory.d.ts.map +0 -1
- package/dist/devices-matter/BaseMatterAccessory.js +0 -100
- package/dist/devices-matter/BaseMatterAccessory.js.map +0 -1
- package/dist/devices-matter/ColorLightAccessory.d.ts +0 -20
- package/dist/devices-matter/ColorLightAccessory.d.ts.map +0 -1
- package/dist/devices-matter/ColorLightAccessory.js +0 -95
- package/dist/devices-matter/ColorLightAccessory.js.map +0 -1
- package/dist/devices-matter/ColorTemperatureLightAccessory.d.ts +0 -18
- package/dist/devices-matter/ColorTemperatureLightAccessory.d.ts.map +0 -1
- package/dist/devices-matter/ColorTemperatureLightAccessory.js +0 -78
- package/dist/devices-matter/ColorTemperatureLightAccessory.js.map +0 -1
- package/dist/devices-matter/ContactSensorAccessory.d.ts +0 -12
- package/dist/devices-matter/ContactSensorAccessory.d.ts.map +0 -1
- package/dist/devices-matter/ContactSensorAccessory.js +0 -34
- package/dist/devices-matter/ContactSensorAccessory.js.map +0 -1
- package/dist/devices-matter/DimmableLightAccessory.d.ts +0 -58
- package/dist/devices-matter/DimmableLightAccessory.d.ts.map +0 -1
- package/dist/devices-matter/DimmableLightAccessory.js +0 -167
- package/dist/devices-matter/DimmableLightAccessory.js.map +0 -1
- package/dist/devices-matter/DoorLockAccessory.d.ts +0 -14
- package/dist/devices-matter/DoorLockAccessory.d.ts.map +0 -1
- package/dist/devices-matter/DoorLockAccessory.js +0 -50
- package/dist/devices-matter/DoorLockAccessory.js.map +0 -1
- package/dist/devices-matter/ExtendedColorLightAccessory.d.ts +0 -21
- package/dist/devices-matter/ExtendedColorLightAccessory.d.ts.map +0 -1
- package/dist/devices-matter/ExtendedColorLightAccessory.js +0 -107
- package/dist/devices-matter/ExtendedColorLightAccessory.js.map +0 -1
- package/dist/devices-matter/FanAccessory.d.ts +0 -16
- package/dist/devices-matter/FanAccessory.d.ts.map +0 -1
- package/dist/devices-matter/FanAccessory.js +0 -81
- package/dist/devices-matter/FanAccessory.js.map +0 -1
- package/dist/devices-matter/HumiditySensorAccessory.d.ts +0 -12
- package/dist/devices-matter/HumiditySensorAccessory.d.ts.map +0 -1
- package/dist/devices-matter/HumiditySensorAccessory.js +0 -34
- package/dist/devices-matter/HumiditySensorAccessory.js.map +0 -1
- package/dist/devices-matter/LeakSensorAccessory.d.ts +0 -12
- package/dist/devices-matter/LeakSensorAccessory.d.ts.map +0 -1
- package/dist/devices-matter/LeakSensorAccessory.js +0 -33
- package/dist/devices-matter/LeakSensorAccessory.js.map +0 -1
- package/dist/devices-matter/LightSensorAccessory.d.ts +0 -12
- package/dist/devices-matter/LightSensorAccessory.d.ts.map +0 -1
- package/dist/devices-matter/LightSensorAccessory.js +0 -34
- package/dist/devices-matter/LightSensorAccessory.js.map +0 -1
- package/dist/devices-matter/OccupancySensorAccessory.d.ts +0 -12
- package/dist/devices-matter/OccupancySensorAccessory.d.ts.map +0 -1
- package/dist/devices-matter/OccupancySensorAccessory.js +0 -39
- package/dist/devices-matter/OccupancySensorAccessory.js.map +0 -1
- package/dist/devices-matter/OnOffLightAccessory.d.ts +0 -38
- package/dist/devices-matter/OnOffLightAccessory.d.ts.map +0 -1
- package/dist/devices-matter/OnOffLightAccessory.js +0 -118
- package/dist/devices-matter/OnOffLightAccessory.js.map +0 -1
- package/dist/devices-matter/OnOffOutletAccessory.d.ts +0 -12
- package/dist/devices-matter/OnOffOutletAccessory.d.ts.map +0 -1
- package/dist/devices-matter/OnOffOutletAccessory.js +0 -40
- package/dist/devices-matter/OnOffOutletAccessory.js.map +0 -1
- package/dist/devices-matter/OnOffSwitchAccessory.d.ts +0 -14
- package/dist/devices-matter/OnOffSwitchAccessory.d.ts.map +0 -1
- package/dist/devices-matter/OnOffSwitchAccessory.js +0 -42
- package/dist/devices-matter/OnOffSwitchAccessory.js.map +0 -1
- package/dist/devices-matter/RoboticVacuumAccessory.d.ts +0 -68
- package/dist/devices-matter/RoboticVacuumAccessory.d.ts.map +0 -1
- package/dist/devices-matter/RoboticVacuumAccessory.js +0 -334
- package/dist/devices-matter/RoboticVacuumAccessory.js.map +0 -1
- package/dist/devices-matter/SmokeCOAlarmAccessory.d.ts +0 -11
- package/dist/devices-matter/SmokeCOAlarmAccessory.d.ts.map +0 -1
- package/dist/devices-matter/SmokeCOAlarmAccessory.js +0 -49
- package/dist/devices-matter/SmokeCOAlarmAccessory.js.map +0 -1
- package/dist/devices-matter/TemperatureSensorAccessory.d.ts +0 -12
- package/dist/devices-matter/TemperatureSensorAccessory.d.ts.map +0 -1
- package/dist/devices-matter/TemperatureSensorAccessory.js +0 -36
- package/dist/devices-matter/TemperatureSensorAccessory.js.map +0 -1
- package/dist/devices-matter/ThermostatAccessory.d.ts +0 -19
- package/dist/devices-matter/ThermostatAccessory.d.ts.map +0 -1
- package/dist/devices-matter/ThermostatAccessory.js +0 -95
- package/dist/devices-matter/ThermostatAccessory.js.map +0 -1
- package/dist/devices-matter/VenetianBlindAccessory.d.ts +0 -19
- package/dist/devices-matter/VenetianBlindAccessory.d.ts.map +0 -1
- package/dist/devices-matter/VenetianBlindAccessory.js +0 -99
- package/dist/devices-matter/VenetianBlindAccessory.js.map +0 -1
- package/dist/devices-matter/WindowBlindAccessory.d.ts +0 -17
- package/dist/devices-matter/WindowBlindAccessory.d.ts.map +0 -1
- package/dist/devices-matter/WindowBlindAccessory.js +0 -80
- package/dist/devices-matter/WindowBlindAccessory.js.map +0 -1
- package/dist/devices-matter/custom/PowerStripAccessory.d.ts +0 -97
- package/dist/devices-matter/custom/PowerStripAccessory.d.ts.map +0 -1
- package/dist/devices-matter/custom/PowerStripAccessory.js +0 -265
- package/dist/devices-matter/custom/PowerStripAccessory.js.map +0 -1
- package/dist/devices-matter/custom/index.d.ts +0 -8
- package/dist/devices-matter/custom/index.d.ts.map +0 -1
- package/dist/devices-matter/custom/index.js +0 -8
- package/dist/devices-matter/custom/index.js.map +0 -1
- package/dist/devices-matter/index.d.ts +0 -29
- package/dist/devices-matter/index.d.ts.map +0 -1
- package/dist/devices-matter/index.js +0 -28
- package/dist/devices-matter/index.js.map +0 -1
- package/dist/index.test.d.ts +0 -2
- package/dist/index.test.d.ts.map +0 -1
- package/dist/index.test.js +0 -19
- package/dist/index.test.js.map +0 -1
- package/dist/irdevice/airconditioner.d.ts +0 -61
- package/dist/irdevice/airconditioner.d.ts.map +0 -1
- package/dist/irdevice/airconditioner.js +0 -472
- package/dist/irdevice/airconditioner.js.map +0 -1
- package/dist/irdevice/airpurifier.d.ts +0 -50
- package/dist/irdevice/airpurifier.d.ts.map +0 -1
- package/dist/irdevice/airpurifier.js +0 -213
- package/dist/irdevice/airpurifier.js.map +0 -1
- package/dist/irdevice/camera.d.ts +0 -32
- package/dist/irdevice/camera.d.ts.map +0 -1
- package/dist/irdevice/camera.js +0 -107
- package/dist/irdevice/camera.js.map +0 -1
- package/dist/irdevice/fan.d.ts +0 -36
- package/dist/irdevice/fan.d.ts.map +0 -1
- package/dist/irdevice/fan.js +0 -200
- package/dist/irdevice/fan.js.map +0 -1
- package/dist/irdevice/irdevice.d.ts +0 -68
- package/dist/irdevice/irdevice.d.ts.map +0 -1
- package/dist/irdevice/irdevice.js +0 -298
- package/dist/irdevice/irdevice.js.map +0 -1
- package/dist/irdevice/light.d.ts +0 -36
- package/dist/irdevice/light.d.ts.map +0 -1
- package/dist/irdevice/light.js +0 -206
- package/dist/irdevice/light.js.map +0 -1
- package/dist/irdevice/other.d.ts +0 -57
- package/dist/irdevice/other.d.ts.map +0 -1
- package/dist/irdevice/other.js +0 -778
- package/dist/irdevice/other.js.map +0 -1
- package/dist/irdevice/tv.d.ts +0 -45
- package/dist/irdevice/tv.d.ts.map +0 -1
- package/dist/irdevice/tv.js +0 -327
- package/dist/irdevice/tv.js.map +0 -1
- package/dist/irdevice/vacuumcleaner.d.ts +0 -28
- package/dist/irdevice/vacuumcleaner.d.ts.map +0 -1
- package/dist/irdevice/vacuumcleaner.js +0 -104
- package/dist/irdevice/vacuumcleaner.js.map +0 -1
- package/dist/irdevice/waterheater.d.ts +0 -30
- package/dist/irdevice/waterheater.d.ts.map +0 -1
- package/dist/irdevice/waterheater.js +0 -105
- package/dist/irdevice/waterheater.js.map +0 -1
- package/dist/platform-hap.d.ts +0 -149
- package/dist/platform-hap.d.ts.map +0 -1
- package/dist/platform-hap.js +0 -2861
- package/dist/platform-hap.js.map +0 -1
- package/dist/platform-matter.d.ts +0 -120
- package/dist/platform-matter.d.ts.map +0 -1
- package/dist/platform-matter.js +0 -984
- package/dist/platform-matter.js.map +0 -1
- package/dist/verifyconfig.test.d.ts +0 -2
- package/dist/verifyconfig.test.d.ts.map +0 -1
- package/dist/verifyconfig.test.js +0 -167
- package/dist/verifyconfig.test.js.map +0 -1
- package/src/custom.d.ts +0 -7
- package/src/devices-hap/airpurifier.ts +0 -563
- package/src/devices-hap/blindtilt.ts +0 -1049
- package/src/devices-hap/bot.ts +0 -900
- package/src/devices-hap/ceilinglight.ts +0 -742
- package/src/devices-hap/colorbulb.ts +0 -904
- package/src/devices-hap/contact.ts +0 -457
- package/src/devices-hap/curtain.ts +0 -944
- package/src/devices-hap/device.ts +0 -811
- package/src/devices-hap/fan.ts +0 -711
- package/src/devices-hap/hub.ts +0 -439
- package/src/devices-hap/humidifier.ts +0 -669
- package/src/devices-hap/iosensor.ts +0 -427
- package/src/devices-hap/lightstrip.ts +0 -836
- package/src/devices-hap/lock.ts +0 -620
- package/src/devices-hap/meter.ts +0 -426
- package/src/devices-hap/meterplus.ts +0 -430
- package/src/devices-hap/meterpro.ts +0 -522
- package/src/devices-hap/motion.ts +0 -390
- package/src/devices-hap/plug.ts +0 -423
- package/src/devices-hap/relayswitch.ts +0 -727
- package/src/devices-hap/robotvacuumcleaner.ts +0 -568
- package/src/devices-hap/waterdetector.ts +0 -400
- package/src/devices-matter/BaseMatterAccessory.ts +0 -131
- package/src/devices-matter/ColorLightAccessory.ts +0 -110
- package/src/devices-matter/ColorTemperatureLightAccessory.ts +0 -92
- package/src/devices-matter/ContactSensorAccessory.ts +0 -41
- package/src/devices-matter/DimmableLightAccessory.ts +0 -192
- package/src/devices-matter/DoorLockAccessory.ts +0 -60
- package/src/devices-matter/ExtendedColorLightAccessory.ts +0 -123
- package/src/devices-matter/FanAccessory.ts +0 -95
- package/src/devices-matter/HumiditySensorAccessory.ts +0 -41
- package/src/devices-matter/LeakSensorAccessory.ts +0 -40
- package/src/devices-matter/LightSensorAccessory.ts +0 -41
- package/src/devices-matter/OccupancySensorAccessory.ts +0 -48
- package/src/devices-matter/OnOffLightAccessory.ts +0 -133
- package/src/devices-matter/OnOffOutletAccessory.ts +0 -46
- package/src/devices-matter/OnOffSwitchAccessory.ts +0 -51
- package/src/devices-matter/RoboticVacuumAccessory.ts +0 -407
- package/src/devices-matter/SmokeCOAlarmAccessory.ts +0 -59
- package/src/devices-matter/TemperatureSensorAccessory.ts +0 -43
- package/src/devices-matter/ThermostatAccessory.ts +0 -110
- package/src/devices-matter/VenetianBlindAccessory.ts +0 -115
- package/src/devices-matter/WindowBlindAccessory.ts +0 -92
- package/src/devices-matter/custom/PowerStripAccessory.ts +0 -309
- package/src/devices-matter/custom/index.ts +0 -8
- package/src/devices-matter/index.ts +0 -29
- package/src/index.test.ts +0 -24
- package/src/irdevice/airconditioner.ts +0 -533
- package/src/irdevice/airpurifier.ts +0 -252
- package/src/irdevice/camera.ts +0 -129
- package/src/irdevice/fan.ts +0 -226
- package/src/irdevice/irdevice.ts +0 -344
- package/src/irdevice/light.ts +0 -246
- package/src/irdevice/other.ts +0 -790
- package/src/irdevice/tv.ts +0 -378
- package/src/irdevice/vacuumcleaner.ts +0 -126
- package/src/irdevice/waterheater.ts +0 -127
- package/src/platform-hap.ts +0 -2997
- package/src/platform-matter.ts +0 -1110
- package/src/verifyconfig.test.ts +0 -197
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { fetchCredentialStatus as apiFetchCredentialStatus, saveCredentials as apiSaveCredentials, syncParentPluginConfigFromDisk } from './api.js'
|
|
2
|
+
import { uiLog } from './logger.js'
|
|
3
|
+
import { hideBusyUi, showBusyUi } from './modal.js'
|
|
4
|
+
import { toastError, toastSuccess, toastWarning } from './toast.js'
|
|
5
|
+
|
|
6
|
+
export async function loadCredentialStatus(): Promise<void> {
|
|
7
|
+
try {
|
|
8
|
+
const creds = await apiFetchCredentialStatus()
|
|
9
|
+
|
|
10
|
+
if (!creds) {
|
|
11
|
+
uiLog.error('Failed to load credentials')
|
|
12
|
+
return
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const tokenStatus = document.getElementById('tokenStatus')
|
|
16
|
+
const secretStatus = document.getElementById('secretStatus')
|
|
17
|
+
|
|
18
|
+
if (!tokenStatus || !secretStatus) { return }
|
|
19
|
+
|
|
20
|
+
if (creds.hasToken) {
|
|
21
|
+
tokenStatus.textContent = `✓ Configured (${creds.tokenLength} characters)`
|
|
22
|
+
tokenStatus.classList.add('ok')
|
|
23
|
+
} else {
|
|
24
|
+
tokenStatus.textContent = 'Not configured'
|
|
25
|
+
tokenStatus.classList.remove('ok')
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (creds.hasSecret) {
|
|
29
|
+
secretStatus.textContent = `✓ Configured (${creds.secretLength} characters)`
|
|
30
|
+
secretStatus.classList.add('ok')
|
|
31
|
+
} else {
|
|
32
|
+
secretStatus.textContent = 'Not configured'
|
|
33
|
+
secretStatus.classList.remove('ok')
|
|
34
|
+
}
|
|
35
|
+
} catch (e) {
|
|
36
|
+
uiLog.error('Error loading credentials:', e)
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export async function saveCredentials(): Promise<void> {
|
|
41
|
+
const token = (document.getElementById('token') as HTMLInputElement)?.value
|
|
42
|
+
const secret = (document.getElementById('secret') as HTMLInputElement)?.value
|
|
43
|
+
const saveStatus = document.getElementById('saveStatus')
|
|
44
|
+
const saveBtn = document.getElementById('saveBtn') as HTMLButtonElement
|
|
45
|
+
|
|
46
|
+
if (!saveStatus || !saveBtn) { return }
|
|
47
|
+
|
|
48
|
+
if (!token || !secret) {
|
|
49
|
+
saveStatus.textContent = 'Please enter both token and secret'
|
|
50
|
+
saveStatus.classList.add('error')
|
|
51
|
+
toastWarning('Please enter both token and secret')
|
|
52
|
+
return
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
try {
|
|
56
|
+
showBusyUi()
|
|
57
|
+
saveBtn.disabled = true
|
|
58
|
+
saveBtn.textContent = 'Saving...'
|
|
59
|
+
uiLog.info('Saving credentials...')
|
|
60
|
+
|
|
61
|
+
const result = await apiSaveCredentials(token, secret)
|
|
62
|
+
uiLog.info('Save response:', result)
|
|
63
|
+
|
|
64
|
+
const message = result?.message || 'Credentials saved successfully'
|
|
65
|
+
saveStatus.textContent = `✓ ${message}`
|
|
66
|
+
saveStatus.classList.remove('error')
|
|
67
|
+
saveStatus.classList.add('success-msg')
|
|
68
|
+
toastSuccess(message)
|
|
69
|
+
|
|
70
|
+
const synced = await syncParentPluginConfigFromDisk(true)
|
|
71
|
+
if (synced) {
|
|
72
|
+
saveStatus.textContent += ' - Config saved automatically.'
|
|
73
|
+
toastSuccess('Configuration synced and saved automatically')
|
|
74
|
+
} else {
|
|
75
|
+
toastWarning('Credentials saved, but configuration sync failed')
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Clear inputs after successful save
|
|
79
|
+
;(document.getElementById('token') as HTMLInputElement).value = ''
|
|
80
|
+
;(document.getElementById('secret') as HTMLInputElement).value = ''
|
|
81
|
+
|
|
82
|
+
// Reload status to verify save
|
|
83
|
+
setTimeout(() => loadCredentialStatus(), 500)
|
|
84
|
+
|
|
85
|
+
// Clear status message
|
|
86
|
+
setTimeout(() => {
|
|
87
|
+
saveStatus.textContent = ''
|
|
88
|
+
saveStatus.classList.remove('success-msg')
|
|
89
|
+
}, 3000)
|
|
90
|
+
} catch (e) {
|
|
91
|
+
uiLog.error('Save error:', e)
|
|
92
|
+
saveStatus.textContent = `Error: ${e instanceof Error ? e.message : 'Failed to save'}`
|
|
93
|
+
saveStatus.classList.add('error')
|
|
94
|
+
toastError(e instanceof Error ? e.message : 'Failed to save credentials')
|
|
95
|
+
} finally {
|
|
96
|
+
hideBusyUi()
|
|
97
|
+
saveBtn.disabled = false
|
|
98
|
+
saveBtn.textContent = 'Save Credentials'
|
|
99
|
+
}
|
|
100
|
+
}
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
import { deleteDevice as apiDeleteDevice, deleteAllDevices as apiDeleteAllDevices, syncParentPluginConfigFromDisk, fetchDevices } from './api.js'
|
|
2
|
+
import { uiLog } from './logger.js'
|
|
3
|
+
import { hideBusyUi, showBusyUi } from './modal.js'
|
|
4
|
+
import { renderDeviceList } from './render.js'
|
|
5
|
+
import { toastError, toastSuccess, toastWarning } from './toast.js'
|
|
6
|
+
|
|
7
|
+
async function confirmDeleteDialog(deviceNameOrId: string): Promise<boolean> {
|
|
8
|
+
return new Promise((resolve) => {
|
|
9
|
+
const overlay = document.createElement('div')
|
|
10
|
+
overlay.style.position = 'fixed'
|
|
11
|
+
overlay.style.top = '0'
|
|
12
|
+
overlay.style.left = '0'
|
|
13
|
+
overlay.style.width = '100%'
|
|
14
|
+
overlay.style.height = '100%'
|
|
15
|
+
overlay.style.background = 'rgba(0,0,0,0.6)'
|
|
16
|
+
overlay.style.display = 'flex'
|
|
17
|
+
overlay.style.alignItems = 'center'
|
|
18
|
+
overlay.style.justifyContent = 'center'
|
|
19
|
+
overlay.style.zIndex = '10000'
|
|
20
|
+
|
|
21
|
+
const modal = document.createElement('div')
|
|
22
|
+
modal.style.background = getComputedStyle(document.body).backgroundColor
|
|
23
|
+
modal.style.color = getComputedStyle(document.body).color
|
|
24
|
+
modal.style.padding = '20px'
|
|
25
|
+
modal.style.borderRadius = '10px'
|
|
26
|
+
modal.style.minWidth = '340px'
|
|
27
|
+
modal.style.maxWidth = '90vw'
|
|
28
|
+
modal.style.boxShadow = '0 12px 40px rgba(0,0,0,0.35)'
|
|
29
|
+
|
|
30
|
+
const title = document.createElement('h3')
|
|
31
|
+
title.textContent = 'Delete Device'
|
|
32
|
+
title.style.margin = '0 0 10px 0'
|
|
33
|
+
|
|
34
|
+
const message = document.createElement('p')
|
|
35
|
+
message.textContent = `Remove "${deviceNameOrId}" from configuration?`
|
|
36
|
+
message.style.margin = '0 0 16px 0'
|
|
37
|
+
|
|
38
|
+
const actions = document.createElement('div')
|
|
39
|
+
actions.style.display = 'flex'
|
|
40
|
+
actions.style.justifyContent = 'flex-end'
|
|
41
|
+
actions.style.gap = '8px'
|
|
42
|
+
|
|
43
|
+
const cancelBtn = document.createElement('button')
|
|
44
|
+
cancelBtn.textContent = 'Cancel'
|
|
45
|
+
cancelBtn.style.background = '#6b7280'
|
|
46
|
+
|
|
47
|
+
const deleteBtn = document.createElement('button')
|
|
48
|
+
deleteBtn.textContent = 'Delete'
|
|
49
|
+
deleteBtn.style.background = '#ef4444'
|
|
50
|
+
|
|
51
|
+
const cleanup = (result: boolean) => {
|
|
52
|
+
overlay.remove()
|
|
53
|
+
resolve(result)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
cancelBtn.onclick = () => cleanup(false)
|
|
57
|
+
deleteBtn.onclick = () => cleanup(true)
|
|
58
|
+
overlay.addEventListener('click', (event) => {
|
|
59
|
+
if (event.target === overlay) {
|
|
60
|
+
cleanup(false)
|
|
61
|
+
}
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
actions.appendChild(cancelBtn)
|
|
65
|
+
actions.appendChild(deleteBtn)
|
|
66
|
+
modal.appendChild(title)
|
|
67
|
+
modal.appendChild(message)
|
|
68
|
+
modal.appendChild(actions)
|
|
69
|
+
overlay.appendChild(modal)
|
|
70
|
+
document.body.appendChild(overlay)
|
|
71
|
+
})
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export async function deleteDeviceFromConfig(deviceId: string, deviceName: string): Promise<void> {
|
|
75
|
+
uiLog.info('Delete button clicked for device:', deviceId, deviceName)
|
|
76
|
+
|
|
77
|
+
const confirmed = await confirmDeleteDialog(deviceName || deviceId)
|
|
78
|
+
if (!confirmed) {
|
|
79
|
+
uiLog.info('Delete cancelled by user')
|
|
80
|
+
return
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
try {
|
|
84
|
+
showBusyUi()
|
|
85
|
+
uiLog.info('Sending delete request for deviceId:', deviceId)
|
|
86
|
+
const resp = await apiDeleteDevice(deviceId)
|
|
87
|
+
uiLog.info('Delete response:', resp)
|
|
88
|
+
|
|
89
|
+
if (!resp || resp.success === false) {
|
|
90
|
+
throw new Error(resp?.data?.message || 'Failed to delete device')
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
uiLog.info('Syncing parent config from disk...')
|
|
94
|
+
const synced = await syncParentPluginConfigFromDisk(true)
|
|
95
|
+
if (!synced) {
|
|
96
|
+
toastWarning('Device deleted, but configuration sync failed')
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
uiLog.info('Refreshing device list...')
|
|
100
|
+
// Refresh device list
|
|
101
|
+
const list = await fetchDevices()
|
|
102
|
+
uiLog.info('Rendering devices:', list.length)
|
|
103
|
+
renderDeviceList(list)
|
|
104
|
+
|
|
105
|
+
uiLog.info('✓ Device deleted successfully')
|
|
106
|
+
toastSuccess(`Device "${deviceName || deviceId}" deleted successfully`)
|
|
107
|
+
} catch (e) {
|
|
108
|
+
uiLog.error('Delete error:', e)
|
|
109
|
+
toastError(e instanceof Error ? e.message : 'Failed to delete device')
|
|
110
|
+
} finally {
|
|
111
|
+
hideBusyUi()
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
async function confirmDeleteAllDialog(deviceCount: number): Promise<boolean> {
|
|
116
|
+
return new Promise((resolve) => {
|
|
117
|
+
const overlay = document.createElement('div')
|
|
118
|
+
overlay.style.position = 'fixed'
|
|
119
|
+
overlay.style.top = '0'
|
|
120
|
+
overlay.style.left = '0'
|
|
121
|
+
overlay.style.width = '100%'
|
|
122
|
+
overlay.style.height = '100%'
|
|
123
|
+
overlay.style.background = 'rgba(0,0,0,0.7)'
|
|
124
|
+
overlay.style.display = 'flex'
|
|
125
|
+
overlay.style.alignItems = 'center'
|
|
126
|
+
overlay.style.justifyContent = 'center'
|
|
127
|
+
overlay.style.zIndex = '10000'
|
|
128
|
+
|
|
129
|
+
const modal = document.createElement('div')
|
|
130
|
+
modal.style.background = getComputedStyle(document.body).backgroundColor
|
|
131
|
+
modal.style.color = getComputedStyle(document.body).color
|
|
132
|
+
modal.style.padding = '20px'
|
|
133
|
+
modal.style.borderRadius = '10px'
|
|
134
|
+
modal.style.minWidth = '380px'
|
|
135
|
+
modal.style.maxWidth = '90vw'
|
|
136
|
+
modal.style.boxShadow = '0 12px 40px rgba(0,0,0,0.35)'
|
|
137
|
+
modal.style.borderTop = '3px solid #ef4444'
|
|
138
|
+
|
|
139
|
+
const title = document.createElement('h3')
|
|
140
|
+
title.textContent = '⚠️ Remove All Devices'
|
|
141
|
+
title.style.margin = '0 0 12px 0'
|
|
142
|
+
title.style.color = '#ef4444'
|
|
143
|
+
|
|
144
|
+
const message = document.createElement('p')
|
|
145
|
+
message.innerHTML = `Are you sure you want to remove <strong>all ${deviceCount} device(s)</strong> from your configuration?<br><br>This action cannot be undone.`
|
|
146
|
+
message.style.margin = '0 0 18px 0'
|
|
147
|
+
message.style.lineHeight = '1.5'
|
|
148
|
+
|
|
149
|
+
const actions = document.createElement('div')
|
|
150
|
+
actions.style.display = 'flex'
|
|
151
|
+
actions.style.justifyContent = 'flex-end'
|
|
152
|
+
actions.style.gap = '10px'
|
|
153
|
+
|
|
154
|
+
const cancelBtn = document.createElement('button')
|
|
155
|
+
cancelBtn.textContent = 'Cancel'
|
|
156
|
+
cancelBtn.style.background = '#6b7280'
|
|
157
|
+
cancelBtn.style.padding = '8px 16px'
|
|
158
|
+
cancelBtn.style.fontSize = '13px'
|
|
159
|
+
cancelBtn.className = 'secondary'
|
|
160
|
+
|
|
161
|
+
const deleteBtn = document.createElement('button')
|
|
162
|
+
deleteBtn.textContent = 'Remove All'
|
|
163
|
+
deleteBtn.style.background = '#ef4444'
|
|
164
|
+
deleteBtn.style.padding = '8px 20px'
|
|
165
|
+
deleteBtn.style.fontSize = '13px'
|
|
166
|
+
|
|
167
|
+
const cleanup = (result: boolean) => {
|
|
168
|
+
overlay.remove()
|
|
169
|
+
resolve(result)
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
cancelBtn.onclick = () => cleanup(false)
|
|
173
|
+
deleteBtn.onclick = () => cleanup(true)
|
|
174
|
+
overlay.addEventListener('click', (event) => {
|
|
175
|
+
if (event.target === overlay) {
|
|
176
|
+
cleanup(false)
|
|
177
|
+
}
|
|
178
|
+
})
|
|
179
|
+
|
|
180
|
+
actions.appendChild(cancelBtn)
|
|
181
|
+
actions.appendChild(deleteBtn)
|
|
182
|
+
modal.appendChild(title)
|
|
183
|
+
modal.appendChild(message)
|
|
184
|
+
modal.appendChild(actions)
|
|
185
|
+
overlay.appendChild(modal)
|
|
186
|
+
document.body.appendChild(overlay)
|
|
187
|
+
})
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
export async function deleteAllDevicesFromConfig(): Promise<void> {
|
|
191
|
+
uiLog.info('Remove All Devices button clicked')
|
|
192
|
+
|
|
193
|
+
try {
|
|
194
|
+
// Get current device count
|
|
195
|
+
const list = await fetchDevices()
|
|
196
|
+
if (!list || list.length === 0) {
|
|
197
|
+
toastWarning('No devices to remove')
|
|
198
|
+
return
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const confirmed = await confirmDeleteAllDialog(list.length)
|
|
202
|
+
if (!confirmed) {
|
|
203
|
+
uiLog.info('Remove all cancelled by user')
|
|
204
|
+
return
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
showBusyUi()
|
|
208
|
+
uiLog.info('Sending delete all devices request')
|
|
209
|
+
const resp = await apiDeleteAllDevices()
|
|
210
|
+
uiLog.info('Delete all response:', resp)
|
|
211
|
+
|
|
212
|
+
if (!resp || resp.success === false) {
|
|
213
|
+
throw new Error(resp?.data?.message || 'Failed to delete all devices')
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
uiLog.info('Syncing parent config from disk...')
|
|
217
|
+
const synced = await syncParentPluginConfigFromDisk(true)
|
|
218
|
+
if (!synced) {
|
|
219
|
+
toastWarning('Devices deleted, but configuration sync failed')
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
uiLog.info('Refreshing device list...')
|
|
223
|
+
const updatedList = await fetchDevices()
|
|
224
|
+
uiLog.info('Rendering devices:', updatedList.length)
|
|
225
|
+
renderDeviceList(updatedList)
|
|
226
|
+
|
|
227
|
+
const deletedCount = resp?.deletedCount || list.length
|
|
228
|
+
uiLog.info(`✓ Removed ${deletedCount} device(s) successfully`)
|
|
229
|
+
toastSuccess(`Removed ${deletedCount} device(s) successfully`)
|
|
230
|
+
} catch (e) {
|
|
231
|
+
uiLog.error('Delete all error:', e)
|
|
232
|
+
toastError(e instanceof Error ? e.message : 'Failed to delete all devices')
|
|
233
|
+
} finally {
|
|
234
|
+
hideBusyUi()
|
|
235
|
+
}
|
|
236
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { addDevice, fetchDevices, syncParentPluginConfigFromDisk } from './api.js'
|
|
2
|
+
import { uiLog } from './logger.js'
|
|
3
|
+
import { hideBusyUi, showBusyUi } from './modal.js'
|
|
4
|
+
import { renderDeviceList } from './render.js'
|
|
5
|
+
import { toastError, toastInfo, toastSuccess, toastWarning } from './toast.js'
|
|
6
|
+
|
|
7
|
+
export async function addDeviceToConfig(device: any, options: { refresh?: boolean, showStatus?: boolean } = {}): Promise<{ added: boolean }> {
|
|
8
|
+
const { refresh = true, showStatus = true } = options
|
|
9
|
+
|
|
10
|
+
try {
|
|
11
|
+
showBusyUi()
|
|
12
|
+
uiLog.info('Adding device to config:', device)
|
|
13
|
+
const resp = await addDevice(device.id, device.name, device.type)
|
|
14
|
+
uiLog.info('Add device response:', resp)
|
|
15
|
+
|
|
16
|
+
if (!resp || resp.success === false) {
|
|
17
|
+
throw new Error(resp?.data?.message || 'Failed to add device')
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const alreadyExists = !!resp?.alreadyExists
|
|
21
|
+
const message
|
|
22
|
+
= resp?.message
|
|
23
|
+
|| (alreadyExists
|
|
24
|
+
? `Device "${device.name}" already in config`
|
|
25
|
+
: `Device "${device.name}" added successfully!`)
|
|
26
|
+
|
|
27
|
+
if (alreadyExists) {
|
|
28
|
+
toastInfo(message)
|
|
29
|
+
} else {
|
|
30
|
+
toastSuccess(message)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (showStatus) {
|
|
34
|
+
const status = document.getElementById('discoverStatus')
|
|
35
|
+
if (status) {
|
|
36
|
+
status.textContent = (alreadyExists ? '• ' : '✓ ') + message
|
|
37
|
+
status.classList.remove('error')
|
|
38
|
+
status.classList.add('success-msg')
|
|
39
|
+
|
|
40
|
+
// Sync parent Homebridge form cache and auto-save to prevent cache overwrite
|
|
41
|
+
if (!alreadyExists) {
|
|
42
|
+
const synced = await syncParentPluginConfigFromDisk(true)
|
|
43
|
+
status.textContent += synced
|
|
44
|
+
? ' - Config saved automatically.'
|
|
45
|
+
: ' - Warning: config may not persist until you close/reopen settings.'
|
|
46
|
+
|
|
47
|
+
if (synced) {
|
|
48
|
+
toastSuccess('Configuration synced and saved automatically')
|
|
49
|
+
} else {
|
|
50
|
+
toastWarning('Configuration sync failed; close and reopen settings before Save')
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (refresh) {
|
|
57
|
+
await loadConfiguredDevices()
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return { added: !alreadyExists }
|
|
61
|
+
} catch (e) {
|
|
62
|
+
uiLog.error('Add device error:', e)
|
|
63
|
+
const status = document.getElementById('discoverStatus')
|
|
64
|
+
if (status) {
|
|
65
|
+
status.textContent = `✗ Error: ${e instanceof Error ? e.message : 'Failed to add device'}`
|
|
66
|
+
status.classList.add('error')
|
|
67
|
+
}
|
|
68
|
+
toastError(e instanceof Error ? e.message : 'Failed to add device')
|
|
69
|
+
return { added: false }
|
|
70
|
+
} finally {
|
|
71
|
+
hideBusyUi()
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export async function initRemoveAllButton(): Promise<void> {
|
|
76
|
+
const removeAllBtn = document.getElementById('removeAllBtn')
|
|
77
|
+
if (!removeAllBtn) { return }
|
|
78
|
+
|
|
79
|
+
removeAllBtn.addEventListener('click', async () => {
|
|
80
|
+
const { deleteAllDevicesFromConfig } = await import('./devices-delete.js')
|
|
81
|
+
await deleteAllDevicesFromConfig()
|
|
82
|
+
})
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export async function loadConfiguredDevices(): Promise<void> {
|
|
86
|
+
const list = await fetchDevices()
|
|
87
|
+
renderDeviceList(list)
|
|
88
|
+
}
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
import {
|
|
2
|
+
addDevicesInBulk,
|
|
3
|
+
discoverDevices as apiDiscoverDevices,
|
|
4
|
+
fetchDevices,
|
|
5
|
+
syncParentPluginConfigFromDisk,
|
|
6
|
+
} from './api.js'
|
|
7
|
+
import { loadConfiguredDevices } from './devices.js'
|
|
8
|
+
import { uiLog } from './logger.js'
|
|
9
|
+
import { hideBusyUi, showBusyUi } from './modal.js'
|
|
10
|
+
import { renderDiscoveredDevices, filterDevices, sortDevices, getDiscoveryPreferences, setDiscoveryPreferences } from './render.js'
|
|
11
|
+
import { toastError, toastInfo, toastSuccess, toastWarning } from './toast.js'
|
|
12
|
+
|
|
13
|
+
export async function discoverDevices(): Promise<void> {
|
|
14
|
+
const btn = document.getElementById('discoverBtn') as HTMLButtonElement
|
|
15
|
+
const status = document.getElementById('discoverStatus')
|
|
16
|
+
const list = document.getElementById('discoveredList')
|
|
17
|
+
const autoAddAll = (document.getElementById('autoAddAllCheckbox') as HTMLInputElement)?.checked
|
|
18
|
+
|
|
19
|
+
if (!btn || !status || !list) { return }
|
|
20
|
+
|
|
21
|
+
try {
|
|
22
|
+
showBusyUi()
|
|
23
|
+
btn.disabled = true
|
|
24
|
+
btn.textContent = '🔍 Discovering...'
|
|
25
|
+
status.textContent = 'Searching SwitchBot account...'
|
|
26
|
+
status.classList.remove('error')
|
|
27
|
+
|
|
28
|
+
const devicesRaw = await apiDiscoverDevices()
|
|
29
|
+
uiLog.info('Discover response:', devicesRaw)
|
|
30
|
+
|
|
31
|
+
// Deduplicate by device ID
|
|
32
|
+
const devices = devicesRaw.filter((d, index, arr) => !!d?.id && arr.findIndex(x => x?.id === d.id) === index)
|
|
33
|
+
|
|
34
|
+
if (!devices.length) {
|
|
35
|
+
status.textContent = 'No devices found in your SwitchBot account'
|
|
36
|
+
toastInfo('No devices found in your SwitchBot account')
|
|
37
|
+
list.style.display = 'none'
|
|
38
|
+
return
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// If auto-add is enabled, add all devices immediately using bulk endpoint
|
|
42
|
+
if (autoAddAll) {
|
|
43
|
+
status.textContent = `Auto-adding ${devices.length} device(s)...`
|
|
44
|
+
|
|
45
|
+
try {
|
|
46
|
+
const bulkResult = await addDevicesInBulk(
|
|
47
|
+
devices.map(d => ({
|
|
48
|
+
deviceId: d.id,
|
|
49
|
+
name: d.name,
|
|
50
|
+
type: d.type,
|
|
51
|
+
rssi: d.rssi,
|
|
52
|
+
address: d.address,
|
|
53
|
+
model: d.model,
|
|
54
|
+
})),
|
|
55
|
+
)
|
|
56
|
+
uiLog.info('Bulk add response:', bulkResult)
|
|
57
|
+
|
|
58
|
+
if (!bulkResult || bulkResult.success === false) {
|
|
59
|
+
throw new Error(bulkResult?.data?.message || 'Bulk add failed')
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const addedCount
|
|
63
|
+
= bulkResult?.addedCount
|
|
64
|
+
?? bulkResult?.data?.addedCount
|
|
65
|
+
?? 0
|
|
66
|
+
const skippedCount
|
|
67
|
+
= bulkResult?.skippedCount
|
|
68
|
+
?? bulkResult?.data?.skippedCount
|
|
69
|
+
?? 0
|
|
70
|
+
status.textContent = `✓ Added ${addedCount} device(s)${skippedCount > 0 ? ` (${skippedCount} skipped)` : ''}`
|
|
71
|
+
if (addedCount > 0) {
|
|
72
|
+
toastSuccess(`Added ${addedCount} device(s)${skippedCount > 0 ? ` (${skippedCount} skipped)` : ''}`)
|
|
73
|
+
} else if (skippedCount > 0) {
|
|
74
|
+
toastWarning(`No new devices were added (${skippedCount} skipped)`)
|
|
75
|
+
}
|
|
76
|
+
status.classList.remove('error')
|
|
77
|
+
list.style.display = 'none'
|
|
78
|
+
|
|
79
|
+
// Sync parent Homebridge form cache and auto-save to prevent cache overwrite
|
|
80
|
+
// Run sync when discovery returned devices (even if backend response shape changes)
|
|
81
|
+
if (devices.length > 0) {
|
|
82
|
+
const synced = await syncParentPluginConfigFromDisk(true)
|
|
83
|
+
status.textContent += synced
|
|
84
|
+
? ' - Config saved automatically.'
|
|
85
|
+
: ' - Warning: config may not persist until you close/reopen settings.'
|
|
86
|
+
|
|
87
|
+
if (synced) {
|
|
88
|
+
toastSuccess('Configuration synced and saved automatically')
|
|
89
|
+
} else {
|
|
90
|
+
toastWarning('Configuration sync failed; close and reopen settings before Save')
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
} catch (e) {
|
|
94
|
+
uiLog.error('Bulk add error:', e)
|
|
95
|
+
status.textContent = `✗ Error: ${e instanceof Error ? e.message : 'Failed to add devices'}`
|
|
96
|
+
status.classList.add('error')
|
|
97
|
+
toastError(e instanceof Error ? e.message : 'Failed to add devices')
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Refresh the configured devices list
|
|
101
|
+
await loadConfiguredDevices()
|
|
102
|
+
return
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
status.textContent = `Found ${devices.length} device(s)`
|
|
106
|
+
|
|
107
|
+
// Create filter/sort controls
|
|
108
|
+
const controlsDiv = document.createElement('div')
|
|
109
|
+
controlsDiv.style.cssText = 'margin-bottom: 12px; display: flex; gap: 12px; flex-wrap: wrap; align-items: center;'
|
|
110
|
+
|
|
111
|
+
// Load saved preferences
|
|
112
|
+
const preferences = getDiscoveryPreferences()
|
|
113
|
+
|
|
114
|
+
// Filter buttons
|
|
115
|
+
const filterLabel = document.createElement('label')
|
|
116
|
+
filterLabel.style.fontSize = '12px'
|
|
117
|
+
filterLabel.style.fontWeight = '500'
|
|
118
|
+
filterLabel.textContent = 'Filter:'
|
|
119
|
+
|
|
120
|
+
const filterGroup = document.createElement('div')
|
|
121
|
+
filterGroup.style.display = 'flex'
|
|
122
|
+
filterGroup.style.gap = '4px'
|
|
123
|
+
|
|
124
|
+
const filterOptions: Array<{ label: string, value: 'all' | 'ble' | 'api' | 'both' | 'ir' }> = [
|
|
125
|
+
{ label: 'All', value: 'all' },
|
|
126
|
+
{ label: 'BLE', value: 'ble' },
|
|
127
|
+
{ label: 'API', value: 'api' },
|
|
128
|
+
{ label: 'Both', value: 'both' },
|
|
129
|
+
{ label: 'IR', value: 'ir' },
|
|
130
|
+
]
|
|
131
|
+
|
|
132
|
+
for (const option of filterOptions) {
|
|
133
|
+
const btn = document.createElement('button')
|
|
134
|
+
btn.textContent = option.label
|
|
135
|
+
btn.style.padding = '4px 8px'
|
|
136
|
+
btn.style.fontSize = '11px'
|
|
137
|
+
btn.style.borderRadius = '3px'
|
|
138
|
+
btn.style.cursor = 'pointer'
|
|
139
|
+
btn.style.border = preferences.connectionType === option.value ? '2px solid #007AFF' : '1px solid #ccc'
|
|
140
|
+
btn.style.backgroundColor = preferences.connectionType === option.value ? '#f0f7ff' : '#fff'
|
|
141
|
+
|
|
142
|
+
btn.onclick = () => {
|
|
143
|
+
preferences.connectionType = option.value
|
|
144
|
+
setDiscoveryPreferences(preferences)
|
|
145
|
+
updateDiscoveryView(devices, preferences)
|
|
146
|
+
// Update button styles
|
|
147
|
+
for (const b of Array.from(filterGroup.querySelectorAll('button'))) {
|
|
148
|
+
;(b as HTMLButtonElement).style.border = '1px solid #ccc'
|
|
149
|
+
;(b as HTMLButtonElement).style.backgroundColor = '#fff'
|
|
150
|
+
}
|
|
151
|
+
btn.style.border = '2px solid #007AFF'
|
|
152
|
+
btn.style.backgroundColor = '#f0f7ff'
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
filterGroup.appendChild(btn)
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Sort dropdown
|
|
159
|
+
const sortLabel = document.createElement('label')
|
|
160
|
+
sortLabel.style.fontSize = '12px'
|
|
161
|
+
sortLabel.style.fontWeight = '500'
|
|
162
|
+
sortLabel.style.marginLeft = '8px'
|
|
163
|
+
sortLabel.textContent = 'Sort:'
|
|
164
|
+
|
|
165
|
+
const sortSelect = document.createElement('select')
|
|
166
|
+
sortSelect.style.fontSize = '11px'
|
|
167
|
+
sortSelect.style.padding = '4px 8px'
|
|
168
|
+
sortSelect.style.borderRadius = '3px'
|
|
169
|
+
sortSelect.value = preferences.sortBy
|
|
170
|
+
|
|
171
|
+
const sortOptions = [
|
|
172
|
+
{ label: 'Name', value: 'name' },
|
|
173
|
+
{ label: 'Signal Strength', value: 'signal' },
|
|
174
|
+
{ label: 'Type', value: 'type' },
|
|
175
|
+
{ label: 'Connection', value: 'connection' },
|
|
176
|
+
]
|
|
177
|
+
|
|
178
|
+
for (const opt of sortOptions) {
|
|
179
|
+
const option = document.createElement('option')
|
|
180
|
+
option.value = opt.value
|
|
181
|
+
option.textContent = opt.label
|
|
182
|
+
sortSelect.appendChild(option)
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
sortSelect.onchange = () => {
|
|
186
|
+
preferences.sortBy = sortSelect.value as any
|
|
187
|
+
setDiscoveryPreferences(preferences)
|
|
188
|
+
updateDiscoveryView(devices, preferences)
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Search box
|
|
192
|
+
const searchInput = document.createElement('input')
|
|
193
|
+
searchInput.type = 'text'
|
|
194
|
+
searchInput.placeholder = 'Search by name, ID, or type...'
|
|
195
|
+
searchInput.style.fontSize = '11px'
|
|
196
|
+
searchInput.style.padding = '4px 8px'
|
|
197
|
+
searchInput.style.borderRadius = '3px'
|
|
198
|
+
searchInput.style.border = '1px solid #ccc'
|
|
199
|
+
searchInput.style.flex = '1'
|
|
200
|
+
searchInput.style.minWidth = '200px'
|
|
201
|
+
searchInput.style.maxWidth = '300px'
|
|
202
|
+
searchInput.value = preferences.searchQuery
|
|
203
|
+
|
|
204
|
+
let searchTimeout: NodeJS.Timeout
|
|
205
|
+
searchInput.oninput = () => {
|
|
206
|
+
clearTimeout(searchTimeout)
|
|
207
|
+
searchTimeout = setTimeout(() => {
|
|
208
|
+
preferences.searchQuery = searchInput.value
|
|
209
|
+
setDiscoveryPreferences(preferences)
|
|
210
|
+
updateDiscoveryView(devices, preferences)
|
|
211
|
+
}, 300) // Debounce search
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// Assemble controls
|
|
215
|
+
controlsDiv.appendChild(filterLabel)
|
|
216
|
+
controlsDiv.appendChild(filterGroup)
|
|
217
|
+
controlsDiv.appendChild(sortLabel)
|
|
218
|
+
controlsDiv.appendChild(sortSelect)
|
|
219
|
+
controlsDiv.appendChild(searchInput)
|
|
220
|
+
|
|
221
|
+
// Clear list and add controls
|
|
222
|
+
list.innerHTML = ''
|
|
223
|
+
list.appendChild(controlsDiv)
|
|
224
|
+
|
|
225
|
+
// Display discovery list
|
|
226
|
+
list.style.display = 'block'
|
|
227
|
+
|
|
228
|
+
// Initial render with saved preferences
|
|
229
|
+
await updateDiscoveryView(devices, preferences)
|
|
230
|
+
} catch (e) {
|
|
231
|
+
uiLog.error('Discovery error:', e)
|
|
232
|
+
status.textContent = `Error: ${e instanceof Error ? e.message : 'Discovery failed'}`
|
|
233
|
+
status.classList.add('error')
|
|
234
|
+
toastError(e instanceof Error ? e.message : 'Discovery failed')
|
|
235
|
+
list.style.display = 'none'
|
|
236
|
+
} finally {
|
|
237
|
+
hideBusyUi()
|
|
238
|
+
btn.disabled = false
|
|
239
|
+
btn.textContent = '🔍 Discover Devices'
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Update discovery list with filtered and sorted devices
|
|
245
|
+
*/
|
|
246
|
+
async function updateDiscoveryView(
|
|
247
|
+
allDevices: any[],
|
|
248
|
+
preferences: {
|
|
249
|
+
connectionType: 'all' | 'ble' | 'api' | 'both' | 'ir'
|
|
250
|
+
sortBy: 'name' | 'signal' | 'type' | 'connection'
|
|
251
|
+
searchQuery: string
|
|
252
|
+
},
|
|
253
|
+
): Promise<void> {
|
|
254
|
+
// Apply filters and sort
|
|
255
|
+
const filtered = filterDevices(allDevices, preferences.connectionType, preferences.searchQuery)
|
|
256
|
+
const sorted = sortDevices(filtered, preferences.sortBy)
|
|
257
|
+
|
|
258
|
+
// Render updated list
|
|
259
|
+
const ul = await renderDiscoveredDevices(sorted)
|
|
260
|
+
document.getElementById('discoveredDevices')?.replaceWith(ul)
|
|
261
|
+
ul.id = 'discoveredDevices'
|
|
262
|
+
|
|
263
|
+
// Update status with count
|
|
264
|
+
const status = document.getElementById('discoverStatus')
|
|
265
|
+
if (status) {
|
|
266
|
+
const totalCount = allDevices.length
|
|
267
|
+
const filteredCount = sorted.length
|
|
268
|
+
status.textContent = filteredCount === totalCount
|
|
269
|
+
? `Found ${totalCount} device(s)`
|
|
270
|
+
: `Showing ${filteredCount} of ${totalCount} device(s)`
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
export async function addDeviceToConfig(device: any): Promise<void> {
|
|
275
|
+
const { addDeviceToConfig: addDevice } = await import('./devices.js')
|
|
276
|
+
await addDevice(device)
|
|
277
|
+
}
|