UNPKG

hap-homematic

Version:

provides a homekit bridge to the ccu

335 lines (303 loc) 12.9 kB
/* * File: welcomewizzard.js * Project: hap-homematic * File Created: Friday, 27th March 2020 5:44:00 pm * Author: Thomas Kluge (th.kluge@me.com) * ----- * The MIT License (MIT) * * Copyright (c) Thomas Kluge <th.kluge@me.com> (https://github.com/thkl) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * ========================================================================== */ import { Dialog, Button, DatabaseGrid, Input, CheckBox, Label } from './ui.js' import { Wizzard } from './wizzards.js' export class WelcomeWizzard extends Wizzard { constructor(application) { super(application) let self = this this.status = new Label() this.finishButton = new Button('success', self.__('Finish'), async (e, btn) => { self.application.refreshAll() if (self.onClose) { self.onClose() } self.dialog.close() }, false) this.nextButton = new Button('info', self.__('Next'), async (e, btn) => { self.finishStep(self.step) }, true) self.dialog = new Dialog({ dialogId: 'newhapinstance', buttons: [ self.status, self.activitySpinner, self.dissmissButton, self.nextButton, self.finishButton ], title: self.__('Welcome'), dialogClass: 'modal-info', size: 'modal-xl' }) } showQR(item) { let dialog = new Dialog({ dialogId: 'selectorDialog', buttons: [ self.dissmissButton ], title: 'QR Setup Code', dialogClass: 'modal-success', scrollable: true, size: 'modal-md' }) var qr = qrcode(4, 'L'); qr.addData(item.setupURI) qr.make() let content = $('<div>') let img = $('<div>').addClass('homeKitCode') let pin = item.pincode.replace(/-/g, '') let lbl = $('<span>').append(pin.substring(0, 4) + '<br />' + pin.substring(4, 8)) img.append(lbl) img.append(qr.createImgTag(6, 0)) content.append(img) dialog.setBody(content) dialog.open() } createPage1() { let self = this this.step = 1 let content = $('<div>').append(self.__('<h2>Welcome to HAP-HomeMatic.</h2><br />With HAP-HomeMatic you are able to use your HomeMatic devices (or most of it) in HomeKit.')) content.append('<br /><br />') content.append(self.__('Its time for the first setup.<br />In HomeKit your are able to group your devices by rooms. And to make that as easy as possible for you, its recomented to create a HAP Instance for every room first.')) content.append('<br /><br />') content.append(self.__('So here are your rooms from your CCU. Please mark every room from which you want to use devices in HomeKit.')) content.append('<br /><br />') this.instancesToCreate = {} let grid = new DatabaseGrid('rooms', this.roomDeviceList, { rowStyle: 'margin-bottom:5px' }) grid.setTitleLabels([self.__('CCU Room') + '<br />' + self.__('(Num o supported devices)'), self.__('Create HomeKit instance'), self.__('Instance name')]) grid.setColumns([ { sz: { sm: 6, md: 3, lg: 3, xl: 3 } }, { sz: { sm: 6, md: 3, lg: 3, xl: 3 } }, { sz: { sm: 6, md: 5, lg: 5, xl: 5 } } ]) grid.setRenderer((row, room) => { if (self.instancesToCreate[room.id] === undefined) { self.instancesToCreate[room.id] = { roomID: room.id, name: room.name, create: false } // build an empty record if we do not have one } let checkBox = new CheckBox('create_' + room.id, self.instancesToCreate[room.id].create, (e, input) => { // set the create button to the last known state issue #203 self.instancesToCreate[room.id].create = input.checked }) let inputName = new Input('instancename_' + room.id, self.instancesToCreate[room.id].name, (e, input) => { // also set the name to the last known name self.instancesToCreate[room.id].name = input.value }) inputName.setGroupLabel('HomeMatic ') return ([room.name + ' (' + room.devices.length + ')', checkBox.render(), inputName.render()]) }) content.append(grid.render()) content.append(self.__('You may add more rooms/instances later on.')) this.dialog.setBody(content) } createPage2() { let self = this this.step = 3 let content = $('<div>').append(self.__('Great! Step 2: Here are the new bridges just created. Now its time to add them to HomeKit. Use the HomeKit App of your choice and add them one by one.')) content.append('<br /><br />') content.append(self.__('During the setup in HomeKit you can assign a specific room to every bridge. All new devices added to a bridge will then automatically be assigned to this room.')) content.append('<br />') content.append(self.__('Please make sure, the given port for the bridge is not blocked by the firewall of your ccu.')) content.append('<br />') content.append(self.__('If you are using Apples Home App please tap after adding the bridge on the house icon top left, choose Home Settings, tap on your home and there you are able to setup the rooms for the bridges. This is tricky, so its recomented to use eg Elgato Eve.')) content.append('<br /><br />') let grid = new DatabaseGrid('instances', this.application.getBridges(), { rowStyle: 'margin-bottom:5px' }) grid.setTitleLabels([self.__('Instance name'), self.__('Pin Code'), 'Port', self.__('CCU Room')]) grid.setColumns([ { sz: { sm: 6, md: 3, lg: 3, xl: 3 } }, { sz: { sm: 6, md: 3, lg: 3, xl: 3 } }, { sz: { sm: 6, md: 1, lg: 1, xl: 1 } }, { sz: { sm: 6, md: 4, lg: 4, xl: 4 } } ]) grid.setRenderer((row, bridge) => { let room = self.application.getRoombyId(bridge.roomId) let ccuRoomName = (room) ? room.name : '-' let pinLabel = new Label('pinLabel' + bridge.id) pinLabel.setLabel(bridge.pincode) if (bridge.user === bridge.user.toUpperCase()) { pinLabel.setStyle('cursor:pointer;text-decoration:underline;') } pinLabel.label.bind('click', (e) => { self.showQR(bridge) }) return ([bridge.displayName, pinLabel.render(), bridge.port, ccuRoomName]) }) content.append(grid.render()) content.append(self.__('When you done with this, click on Next')) this.dialog.setBody(content) } buildSuggestedName(device, channel) { if ((device) && (channel)) { let cDefaultName = device.type + ' ' + channel.address let idx = cDefaultName.indexOf(channel.name) if (idx === -1) { return channel.name.replace(/[.:#_()-]/g, ' ') } else { return device.name.replace(/[.:#_()-]/g, ' ') } } } loopInstances() { let self = this this.step = 4 if (this.step4Bridges === undefined) { this.step4Bridges = this.application.getBridges().filter(bridge => bridge.roomId !== 0) } if (this.wizzardStepBridgeNum >= this.step4Bridges.length) { this.finishStep(5) return } var content let bridge = this.step4Bridges[this.wizzardStepBridgeNum] if (bridge) { let roomId = bridge.roomId // show all devices in this room let room = this.roomDeviceList.filter(room => room.id === roomId)[0] || undefined if (room) { this.wizzardSettings = { method: 'createapplicanceswizzard', instanceId: bridge.id, channels: [] } // build a channel List let channelzToAdd = [] room.devices.map(device => { device.channels.map(channel => { let name = self.buildSuggestedName(device, channel) channelzToAdd.push({ deviceName: device.name, channel: channel, transfer: false, name: name }) }) }) this.wizzardSettings.channels = channelzToAdd content = $('<div>').append(self.__('Room %s', room.name)) content.append('<br />') content.append(self.__('Please mark all channels from this room, you want to use in HomeKit.')) let grid = new DatabaseGrid('devroom', channelzToAdd, { rowStyle: 'margin-bottom:5px' }) grid.maxRecords = 15 grid.setTitleLabels([this.__('Add'), this.__('Device'), this.__('Channel/Type'), this.__('HomeKit name')]) grid.setColumns([ { sz: { sm: 6, md: 1, lg: 1, xl: 1 } }, { sz: { sm: 6, md: 3, lg: 3, xl: 3 } }, { sz: { sm: 6, md: 3, lg: 3, xl: 3 } }, { sz: { sm: 6, md: 4, lg: 4, xl: 4 } } ]) grid.setRenderer((row, chObj) => { // create a subGrid let checkBox = new CheckBox('create_' + chObj.channel.id, true, (e, input) => { chObj.transfer = input.checked self.wizzardSettings.channels = channelzToAdd }) checkBox.setValue(chObj.transfer) let inputName = new Input('applicance_' + chObj.channel.id, chObj.name, (e, input) => { chObj.name = input.value // if the user edits the name he wants to add this .. i think chObj.transfer = true checkBox.setValue(true) }) return ([checkBox.render(), chObj.deviceName, chObj.channel.name + '<br />' + chObj.channel.type, inputName.render()]) }) content.append(grid.render()) } else { content = $('<div>').append('wizzard error, roomt not found') } content.append(self.__('You may add other channels later via this WebUI')) this.dialog.setBody(content) } } showFinalPage() { this.step = 4 let content = $('<div>').append(this.__('OK, you are done. You should now be able to control the selected devices via HomeKit.')) content.append('<br /><br />') content.append(this.__('Some devices have additional settings or alternative services. Settings can be made here.')) content.append(' ') content.append(this.__('You can add programs and variables to homekit via the WebUI.')) content.append('<br /><br />') content.append(this.__('Have fun ! :o)')) content.append('<br />') this.dialog.setBody(content) } async finishStep(step) { let self = this switch (step) { case 0: this.createPage1() break case 1: self.activitySpinner.setActive(true) await this.application.makeApiRequest({ method: 'createinstancewizzard', playload: JSON.stringify(this.instancesToCreate) }) setTimeout(async () => { await this.application.refreshBridges() self.activitySpinner.setActive(false) this.step = 2 this.createPage2() }, 4000) break case 2: this.createPage2() break case 3: this.wizzardStepBridgeNum = 0 await this.application.refreshBridges() this.loopInstances() break case 4: // filter all channels with transfer = true this.activitySpinner.setActive(true) var chList = this.wizzardSettings.channels chList = chList.filter(cObj => cObj.transfer === true) // remove device stuff let payload = [] chList.map(chObj => { chObj.channel.name = chObj.name // copy the users name payload.push(chObj.channel) }) this.wizzardSettings.channels = undefined // remove this this.wizzardSettings.payload = JSON.stringify(payload) await this.application.makeApiRequest(this.wizzardSettings) this.wizzardStepBridgeNum = this.wizzardStepBridgeNum + 1 this.activitySpinner.setActive(false) this.loopInstances() break case 5: // show final page this.finishButton.setActive(true) this.nextButton.setActive(false) this.showFinalPage() break } } run(roomDeviceList, step) { this.roomDeviceList = roomDeviceList this.finishStep(step) super.run() } }