UNPKG

homebridge-deconz

Version:
296 lines (270 loc) 9.67 kB
<!-- homebridge-deconz/homebridge-ui/public/index.html Homebridge plug-in for deCONZ. Copyright © 2022-2025 Erik Baauw. All rights reserved. --> <link rel="stylesheet" href="style.css"> <script src="https://unpkg.com/vue@3"></script> <p align="center"> <a href="https://github.com/ebaauw/homebridge-deconz/wiki/Configuration" target="_blank"> <img src="homebridge-deconz.png" height="200px"> </a> </p> <div id="app"> <!-- v-if can be used on any element, it will only render the element if the condition is true --> <div v-if="view === 'gateways'"> <div class="w-100 d-flex justify-content-between align-items-center mb-1"> <h4 class="mb-0">Gateways</h4> <!-- @click will call the addGateway method --> <button class="btn btn-primary mr-0" @click="addGateway">Add <i class="fas fa-plus" ></i></button> </div> <ul class="list-group"> <!-- here we are looping over data.pluginConfig.gateways using v-for --> <li class="list-group-item d-flex justify-content-between align-items-center" v-for="(gateway, $index) in pluginConfig.gateways" :key="$index"> <div> {{ gateway.name }} <div class="grey-text"> {{ gateway.host }} </div> </div> <div> <div class="btn-group" role="group" aria-label="Basic example"> <!-- @click will call the editGateway(gateway) method with the selected gateway as the first argument --> <button class="btn btn-primary" @click="editGateway(gateway)"> <i class="fas fa-cog"></i> </button> <!-- @click will call the deleteGateway($index) method with its position in the array / index as the first argument --> <button class="btn btn-danger" @click="deleteGateway($index)"> <i class="fas fa-trash"></i> </button> </div> </div> </li> </ul> </div> <div v-if="view === 'edit-gateway'"> <h4 class="mb-2"> Configure Gateway <span v-if="selectedGateway"> - {{ selectedGateway.name }} </span> </h4> <!-- @click will call the connect method --> <button class="btn btn-primary ml-0" @click="connect">Connect</button> <!-- @click will call the getApiKey method --> <button class="btn btn-primary" @click="getApiKey">Get API Key</button> </div> </div> <script> const { createApp } = Vue; const gatewaySchema = { schema: { type: 'object', properties: { host: { description: 'Gateway hostname and port.', default: 'localhost:80', type: 'string', required: true }, name: { description: 'Homebridge log plugin name.', default: 'deCONZ', type: 'string' }, forceHttp: { description: "Use plain http instead of https.", type: "boolean" }, noResponse: { description: "Report unreachable lights as <i>No Response</i> in HomeKit.", type: "boolean" }, parallelRequests: { description:" The number of ansynchronous requests Homebridge deCONZ sends in parallel to a deCONZ gateway. Default: 10.", type: "integer", minimum: 1, maximum: 30 }, stealth: { description: "Stealth mode: don't make any calls to the Internet. Default: false.", type: "boolean" }, timeout: { description: "The timeout in seconds to wait for a response from a deCONZ gateway. Default: 5.", type: "integer", minimum: 1, maximum: 30 }, waitTimePut: { description: "The time, in milliseconds, to wait after sending a PUT request, before sending the next PUT request. Default: 50.", type: "integer", minimum: 0, maximum: 50 }, waitTimePutGroup: { description: "The time, in milliseconds, to wait after sending a PUT request to a group, before sending the next PUT request. Default: 1000.", type: "integer", minimum: 0, maximum: 1000 }, waitTimeResend: { description: "The time, in milliseconds, to wait before resending a request after an ECONNRESET or http status 503 error. Default: 300.", type: "integer", minimum: 100, maximum: 1000 }, waitTimeReset: { description: "The timeout in milliseconds, to wait before resetting a characteristic value. Default: 500.", type: "integer", minimum: 10, maximum: 2000 }, waitTimeUpdate: { description: "The time, in milliseconds, to wait for a change from HomeKit to another characteristic for the same light or group, before updating the deCONZ gateway. Default: 100.", type: "integer", minimum: 0, maximum: 500 } } }, layout: [ "host", "name", { type: "fieldset", expandable: true, title: "Advanced Settings", description: "Don't change these, unless you understand what you're doing.", items: [ "forceHttp", "parallelRequests", "stealth", "timeout", "waitTimePut", "waitTimePutGroup", "waitTimeResend", "waitTimeReset", "waitTimeUpdate" ] } ] }; const myApp = createApp({ /** * This is called when the app is loaded. It's the entry point. */ async created() { const config = await homebridge.getPluginConfig(); if (!config.length) { // if no config yet, create the basic config required this.pluginConfig = { gateways: [], }; } else { // if config does exist, we pretty safely assume only one config block and take this out this.pluginConfig = config[0]; if (!Array.isArray(this.pluginConfig.gateways)) { this.pluginConfig.gateways = []; } } }, data() { /** * This is reactive data - it can be accessed in any of the methods via this.key * It can be used in the HTML directly via {{ key }} */ return { view: 'gateways', selectedGateway: null, pluginConfig: { gateways: [], }, } }, watch: { /** * This handler will be called whenever the object of data.pluginConfig changes * Doing it likes this means we don't need to worry about keeping the UI in sync with plugin changes * This will take care of everything as long as we keep data.pluginConfig correct */ pluginConfig: { deep: true, handler(newValue, oldValue) { // need to do a deep copy to clean the object before sending it to the Homebridge UI const config = JSON.parse(JSON.stringify(newValue)) homebridge.updatePluginConfig([config]); } }, }, methods: { /** * These are methods that can be called from the HTML. * eg. <button @click="methodName"> or <button @click="methodName(someArg)"> */ addGateway() { // create an empty selected gateway this.selectedGateway = {}; // set the view to edit-gateway this.view = "edit-gateway"; // start the form const gatewayForm = homebridge.createForm(gatewaySchema, {}, 'OK', 'Cancel'); gatewayForm.onChange((form) => { // push changes as they happen into the selectedGateway object this.selectedGateway = form; }); gatewayForm.onSubmit((form) => { // on save, push the new gateway object into the pluginConfig.gateways array this.pluginConfig.gateways.push(form); this.view = "gateways"; gatewayForm.end(); }); gatewayForm.onCancel((form) => { this.view = "gateways"; gatewayForm.end(); }); }, editGateway(gateway) { this.view = "edit-gateway"; // create a copy of the current gateway object const source = JSON.parse(JSON.stringify(gateway)) // set the selectedGateway so we can access it in the template easily this.selectedGateway = source; // load the form const gatewayForm = homebridge.createForm(gatewaySchema, source, 'OK', 'Cancel'); // on changes, update the selectedGateway gatewayForm.onChange((form) => { this.selectedGateway = form; }); // on save, update the gateway object in the pluginConfig.gateways array gatewayForm.onSubmit((form) => { Object.assign(gateway, form); this.view = "gateways"; gatewayForm.end(); }); // on cancel, just go back to the gateways view, not updating the pluginConfig.gateways array with any changes gatewayForm.onCancel((form) => { this.view = "gateways"; gatewayForm.end(); }); }, deleteGateway(index) { // on delete, just remove it from the gateways array this.pluginConfig.gateways.splice(index, 1); }, connect() { console.log('connect clicked for ', this.selectedGateway); }, getApiKey() { console.log('get api key clicked for ', this.selectedGateway); } } }); /** * Watch for the ready event, then start the vue app */ homebridge.addEventListener('ready', async () => { console.log('ready') myApp.mount('#app') }); </script>