UNPKG

@homebridge-plugins/homebridge-tado

Version:
1,621 lines (1,229 loc) 49.2 kB
/*global $, homebridge, fetchDevicesBar, schema*/ const pageNavigation = { currentContent: false, previousContent: [] }; let customSchemaActive = false; let pluginConfig = false; let currentHome = false; const TIMEOUT = (ms) => new Promise((res) => setTimeout(res, ms)); function toggleContent() { $('#header').hide(); $('#main').show(); return; } async function showOldSchema(oldVersion) { let config = await homebridge.getPluginConfig(); if (oldVersion) { $('#main').removeClass('pb-5'); $('#notSupported').show(); setTimeout(() => { $('#main').fadeOut(500); }, 5000); } else { let activeContent = $('#notConfigured').css('display') !== 'none' ? '#notConfigured' : '#isConfigured'; transPage($('#main, ' + activeContent), $('#headerOld'), false, true); } if (!config.length) homebridge.updatePluginConfig([{}]); homebridge.showSchemaForm(); return; } function transPage(cur, next, removed, showSchema) { if (showSchema) { cur.hide(); next.show(); //pageNavigation.previousContent.push($('#isConfigured')); pageNavigation.previousContent.push(cur); pageNavigation.currentContent = next; return; } else { toggleContent(); } if (cur) { cur.fadeOut(500, () => { next.fadeIn(500); if (!removed) pageNavigation.previousContent.push(cur); pageNavigation.currentContent = next; }); } else { next.fadeIn(500); if (!removed) pageNavigation.previousContent.push(next); pageNavigation.currentContent = next; } if (customSchemaActive) customSchemaActive.end(); homebridge.hideSchemaForm(); return; } function goBack(index) { if (pageNavigation.previousContent.length && pageNavigation.currentContent) { index = index === undefined ? pageNavigation.previousContent.length - 1 : index; transPage(pageNavigation.currentContent, pageNavigation.previousContent[index], true); //pageNavigation.currentContent = pageNavigation.previousContent[index]; pageNavigation.previousContent.splice(index, 1); if (customSchemaActive) customSchemaActive.end(); } return; } async function createCustomSchema(home) { //schema.layout.homes.forEach() customSchemaActive = homebridge.createForm(schema, { name: pluginConfig[0].name, debug: pluginConfig[0].debug, disableHistoryService: pluginConfig[0].disableHistoryService, preferSiriTemperature: pluginConfig[0].preferSiriTemperature, homes: home }); customSchemaActive.onChange(async config => { pluginConfig[0].name = config.name; pluginConfig[0].debug = config.debug; pluginConfig[0].disableHistoryService = config.disableHistoryService; pluginConfig[0].preferSiriTemperature = config.preferSiriTemperature; pluginConfig[0].homes = pluginConfig[0].homes.map(myHome => { if (myHome.name === config.homes.name) { myHome = config.homes; } return myHome; }); try { await homebridge.updatePluginConfig(pluginConfig); } catch (err) { console.log(err); homebridge.toast.error(err.message, 'Error'); } }); return; } async function resetUI() { homebridge.request('/reset'); resetForm(); resetSchema(); currentHome = false; return; } function resetForm() { $('#homeUsername').val(''); $('#homeTadoApiUrl').val(''); $('#homeSkipAuth').prop('checked', false); if (fetchDevicesBar) fetchDevicesBar.set(0); return; } function resetSchema() { if (customSchemaActive) { customSchemaActive.end(); customSchemaActive = false; } return; } function addDeviceToList(home) { let name = home.name; let owner = home.username; $('#deviceSelect').append('<option value="' + name + '">' + name + ' &lt;' + owner + '&gt;' + '</option>'); return; } function removeDeviceFromList(home) { let name = typeof home === 'string' ? home : home.name; $('#deviceSelect option[value=\'' + name + '\']').remove(); return; } async function addNewDeviceToConfig(config, refresh, resync) { try { for (const i in config[0].homes) { let found = false; for (const j in pluginConfig[0].homes) if (config[0].homes[i].name === pluginConfig[0].homes[j].name) found = true; if (!found) { addDeviceToList(config[0].homes[i]); homebridge.toast.success(config[0].homes[i].name + ' added to config!', 'Success'); } else if (refresh) { homebridge.toast.success(config[0].homes[i].name + ' refreshed!', 'Success'); } else if (resync) { homebridge.toast.success(config[0].homes[i].name + ' resynchronized!', 'Success'); } } pluginConfig = JSON.parse(JSON.stringify(config)); await homebridge.updatePluginConfig(pluginConfig); await homebridge.savePluginConfig(); } catch (err) { console.log(err); homebridge.toast.error(err.message, 'Error'); } return; } async function removeDeviceFromConfig(name) { currentHome = name || currentHome; let foundIndex; let pluginConfigBkp = JSON.parse(JSON.stringify(pluginConfig)); pluginConfig[0].homes.forEach((home, index) => { if (home.name === currentHome) { foundIndex = index; } }); if (foundIndex !== undefined) { try { pluginConfig[0].homes.splice(foundIndex, 1); removeDeviceFromList(currentHome); if (!pluginConfig[0].homes.length) { delete pluginConfig[0].debug; delete pluginConfig[0].disableHistoryService; delete pluginConfig[0].preferSiriTemperature; } await homebridge.updatePluginConfig(pluginConfig); await homebridge.savePluginConfig(); homebridge.toast.success(currentHome + ' removed from config!', 'Success'); } catch (err) { pluginConfig = JSON.parse(JSON.stringify(pluginConfigBkp)); throw err; } } else { throw new Error('No home found in config to remove!'); } return; } async function fetchDevices(auth, refresh, resync) { if (!auth && !resync) return homebridge.toast.error('No credentials!', 'Error'); const config = JSON.parse(JSON.stringify(pluginConfig)); try { const fnAuthenticate = async (params) => { homebridge.request('/authenticate', params); const instructionsURL = await homebridge.request('/exec', { dest: 'fullAuthentication' }); if (instructionsURL && instructionsURL !== "") { $("#fetchDevices #authenticationInstructions").html(`Open the following URL in your browser, click "submit" and log in to your tado° account "${params.username}": <a href="${instructionsURL}" target ="_blank">${instructionsURL}</a>`); $("#fetchDevices #authenticationInstructions").css("display", "block"); homebridge.toast.info("Please follow the instructions above and confirm your login."); const authenticationSuccessful = await homebridge.request('/exec', { dest: 'waitForAuthentication' }); $("#fetchDevices #authenticationInstructions").html(""); $("#fetchDevices #authenticationInstructions").css("display", "none"); homebridge.toast.success(authenticationSuccessful); } } if (!resync) { //Init API with credentials await fnAuthenticate(auth); await TIMEOUT(2000); fetchDevicesBar.animate(0.20); } if (refresh) { //refresh selected home //Home Informations let home = config[0].homes.find(home => home && home.name === currentHome); if (!home) return homebridge.toast.error('Cannot refresh ' + currentHome + '. Not found in config!', 'Error'); if (!home.id) { homebridge.toast.info('No Home ID defined in config. Getting Home ID for ' + home.name, auth.username); const me = await homebridge.request('/exec', { dest: 'getMe' }); me.homes.map(foundHome => { if (foundHome.name === home.name) home.id = foundHome.id; }); await TIMEOUT(1000); if (!home.id) return homebridge.toast.error('Cannot get a Home ID for ' + home.name + '. ' + home.name + ' not found for this user!', auth.username); } await TIMEOUT(2000); fetchDevicesBar.animate(0.40); const homeInfo = await homebridge.request('/exec', { dest: 'getHome', data: home.id }); for (let [i, home] of config[0].homes.entries()) { if (config[0].homes[i].name === homeInfo.name) { config[0].homes[i].id = homeInfo.id; config[0].homes[i].username = auth.username; config[0].homes[i].tadoApiUrl = auth.tadoApiUrl; config[0].homes[i].skipAuth = auth.skipAuth; config[0].homes[i].temperatureUnit = homeInfo.temperatureUnit || 'CELSIUS'; config[0].homes[i].zones = config[0].homes[i].zones || []; if (homeInfo.geolocation) config[0].homes[i].geolocation = { longitude: homeInfo.geolocation.longitude.toString(), latitude: homeInfo.geolocation.latitude.toString() }; //init devices for childLock config[0].homes[i].extras = config[0].homes[i].extras || {}; config[0].homes[i].extras.childLockSwitches = config[0].homes[i].extras.childLockSwitches || []; let allFoundDevices = []; await TIMEOUT(2000); fetchDevicesBar.animate(0.60); //Mobile Devices Informations const mobileDevices = await homebridge.request('/exec', { dest: 'getMobileDevices', data: home.id }); if (!config[0].homes[i].presence) config[0].homes[i].presence = { anyone: false, accTypeAnyone: 'OCCUPANCY', user: [] }; //Remove not registred devices config[0].homes[i].presence.user.forEach((user, index) => { let found = false; mobileDevices.forEach(foundUser => { if (foundUser.name === user.name) { found = true; } }); if (!found) { homebridge.toast.info(user.name + ' removed from config!', auth.username); config[0].homes[i].presence.user.splice(index, 1); } }); //Check for new registred devices if (config[0].homes[i].presence.user.length) { for (const foundUser of mobileDevices) { let userIndex; config[0].homes[i].presence.user.forEach((user, index) => { if (user.name === foundUser.name) { userIndex = index; } }); if (userIndex === undefined) { config[0].homes[i].presence.user.push({ active: false, name: foundUser.name, accType: 'OCCUPANCY' }); } } } else { config[0].homes[i].presence.user = mobileDevices.map(user => { return { active: false, name: user.name, accType: 'OCCUPANCY' }; }); } await TIMEOUT(2000); fetchDevicesBar.animate(0.80); //Zone Informations const zones = await homebridge.request('/exec', { dest: 'getZones', data: home.id }); //Remove not available zones config[0].homes[i].zones.forEach((zone, index) => { let found = false; zones.forEach(foundZone => { if (foundZone.name === zone.name) { found = true; } }); if (!found) { homebridge.toast.info(zone.name + ' removed from config!', auth.username); config[0].homes[i].zones.splice(index, 1); } }); //Check for new zones or refresh exist one if (config[0].homes[i].zones.length) { for (const foundZone of zones) { const capabilities = await homebridge.request('/exec', { dest: 'getZoneCapabilities', data: [home.id, foundZone.id] }) || {}; let minTempValue = capabilities.temperatures ? homeInfo.temperatureUnit === 'CELSIUS' ? capabilities.temperatures.celsius.min : capabilities.temperatures.fahrenheit.min : foundZone.type === 'HOT_WATER' ? homeInfo.temperatureUnit === 'CELSIUS' ? 30 : 86 : homeInfo.temperatureUnit === 'CELSIUS' ? 5 : 41; let maxTempValue = capabilities.temperatures ? homeInfo.temperatureUnit === 'CELSIUS' ? capabilities.temperatures.celsius.max : capabilities.temperatures.fahrenheit.max : foundZone.type === 'HOT_WATER' ? homeInfo.temperatureUnit === 'CELSIUS' ? 65 : 149 : homeInfo.temperatureUnit === 'CELSIUS' ? 27 : 77; let minTempStep = capabilities.temperatures ? homeInfo.temperatureUnit === 'CELSIUS' ? capabilities.temperatures.celsius.step : capabilities.temperatures.fahrenheit.step : 1; if (foundZone.devices) foundZone.devices.forEach(dev => { if (dev.deviceType && (dev.deviceType.includes('VA01') || dev.deviceType.includes('VA02'))) allFoundDevices.push({ name: foundZone.name + ' ' + dev.shortSerialNo, serialNumber: dev.shortSerialNo }); }); let zoneIndex; config[0].homes[i].zones.forEach((zone, index) => { if (zone.name === foundZone.name) { zoneIndex = index; } }); if (zoneIndex !== undefined) { config[0].homes[i].zones[zoneIndex].id = foundZone.id; config[0].homes[i].zones[zoneIndex].type = foundZone.type; config[0].homes[i].zones[zoneIndex].minValue = minTempValue; config[0].homes[i].zones[zoneIndex].maxValue = maxTempValue; config[0].homes[i].zones[zoneIndex].minStep = minTempStep; } else { config[0].homes[i].zones.push({ active: true, id: foundZone.id, name: foundZone.name, type: foundZone.type, delaySwitch: false, autoOffDelay: false, noBattery: false, mode: 'MANUAL', modeTimer: 30, minValue: minTempValue, maxValue: maxTempValue, minStep: minTempStep, easyMode: false, openWindowSensor: false, openWindowSwitch: false, separateTemperature: false, separateHumidity: false, accTypeBoiler: 'SWITCH', boilerTempSupport: false }); } } } else { for (const zone of zones) { const capabilities = await homebridge.request('/exec', { dest: 'getZoneCapabilities', data: [home.id, zone.id] }) || {}; let minTempValue = capabilities.temperatures ? homeInfo.temperatureUnit === 'CELSIUS' ? capabilities.temperatures.celsius.min : capabilities.temperatures.fahrenheit.min : zone.type === 'HOT_WATER' ? homeInfo.temperatureUnit === 'CELSIUS' ? 30 : 86 : homeInfo.temperatureUnit === 'CELSIUS' ? 5 : 41; let maxTempValue = capabilities.temperatures ? homeInfo.temperatureUnit === 'CELSIUS' ? capabilities.temperatures.celsius.max : capabilities.temperatures.fahrenheit.max : zone.type === 'HOT_WATER' ? homeInfo.temperatureUnit === 'CELSIUS' ? 65 : 149 : homeInfo.temperatureUnit === 'CELSIUS' ? 27 : 77; let minTempStep = capabilities.temperatures ? homeInfo.temperatureUnit === 'CELSIUS' ? capabilities.temperatures.celsius.step : capabilities.temperatures.fahrenheit.step : 1; if (zone.devices) zone.devices.forEach(dev => { allFoundDevices.push({ name: zone.name + ' ' + dev.shortSerialNo, serialNumber: dev.shortSerialNo }); }); config[0].homes[i].zones.push({ active: true, id: zone.id, name: zone.name, type: zone.type, delaySwitch: false, autoOffDelay: false, noBattery: false, mode: 'MANUAL', modeTimer: 30, minValue: minTempValue, maxValue: maxTempValue, minStep: minTempStep, easyMode: false, openWindowSensor: false, openWindowSwitch: false, separateTemperature: false, separateHumidity: false, accTypeBoiler: 'SWITCH', boilerTempSupport: false }); } } //remove non existing childLockSwitches config[0].homes[i].extras.childLockSwitches.forEach((childLockSwitch, index) => { let found = false; allFoundDevices.forEach(foundDevice => { if (foundDevice.serialNumber === childLockSwitch.serialNumber) { found = true; } }); if (!found) { homebridge.toast.info(childLockSwitch.name + ' removed from config!', auth.username); config[0].homes[i].extras.childLockSwitches.splice(index, 1); } }); //check for new childLockSwitches if (config[0].homes[i].extras.childLockSwitches.length) { for (const foundDevice of allFoundDevices) { let found = false; config[0].homes[i].extras.childLockSwitches.forEach(childLockSwitch => { if (childLockSwitch.serialNumber === foundDevice.serialNumber) { found = true; } }); if (!found) { config[0].homes[i].extras.childLockSwitches.push({ active: false, name: foundDevice.name, serialNumber: foundDevice.serialNumber }); } } } else { config[0].homes[i].extras.childLockSwitches = allFoundDevices.map(device => { return { active: false, name: device.name, serialNumber: device.serialNumber }; }); } } } } else if (resync) { homebridge.toast.info('Checking for available homes for given user...', 'Info'); const availableHomesInApis = []; for (let home of config[0].homes) { if (home.name && home.username) { //Init API with credentials await fnAuthenticate({ username: home.username, tadoApiUrl: home.tadoApiUrl, skipAuth: home.skipAuth }); //resync home (refresh/remove) const me = await homebridge.request('/exec', { dest: 'getMe' }); me.homes.forEach(foundHome => { availableHomesInApis.push({ id: foundHome.id, name: foundHome.name, username: home.username, tadoApiUrl: home.tadoApiUrl, skipAuth: home.skipAuth }); }); } } await TIMEOUT(2000); homebridge.toast.info('Found ' + availableHomesInApis.length + ' in total!', 'Info'); await TIMEOUT(2000); homebridge.toast.info('Search for homes in the config to remove that were not found in the API...', 'Info'); let removedHomes = 0; //remove non exist homes from config that doesnt exist in api for (let [i, home] of config[0].homes.entries()) { if (home.name && home.username) { //Init API with credentials await fnAuthenticate({ username: home.username, tadoApiUrl: home.tadoApiUrl, skipAuth: home.skipAuth }); let foundHome; for (const apiHome of availableHomesInApis) { if (home.name === apiHome.name || home.id === apiHome.id) { foundHome = apiHome; } } if (!foundHome) { homebridge.toast.info(home.name + ' removed from config!', home.username); await removeDeviceFromConfig(home.name); config[0].homes.splice(i, 1); removedHomes += 1; await TIMEOUT(2000); } } } if (!removedHomes) await TIMEOUT(2000); homebridge.toast.info(removedHomes + ' removed from config in total!', 'Info'); await TIMEOUT(2000); //refresh existing homes for (let [i, home] of config[0].homes.entries()) { if (home.name && home.username) { //Init API with credentials await fnAuthenticate({ username: home.username, tadoApiUrl: home.tadoApiUrl, skipAuth: home.skipAuth }); let foundHome; for (const apiHome of availableHomesInApis) { if (home.name === apiHome.name || home.id === apiHome.id) { foundHome = apiHome; home.id = apiHome.id; } } if (foundHome) { homebridge.toast.info(home.name + ' resynchronizing...', home.username); const homeInfo = await homebridge.request('/exec', { dest: 'getHome', data: home.id }); config[0].homes[i].id = homeInfo.id; config[0].homes[i].username = foundHome.username; config[0].homes[i].tadoApiUrl = foundHome.tadoApiUrl; config[0].homes[i].skipAuth = foundHome.skipAuth; config[0].homes[i].temperatureUnit = homeInfo.temperatureUnit || 'CELSIUS'; config[0].homes[i].zones = config[0].homes[i].zones || []; if (homeInfo.geolocation) config[0].homes[i].geolocation = { longitude: homeInfo.geolocation.longitude.toString(), latitude: homeInfo.geolocation.latitude.toString() }; //init devices for childLock config[0].homes[i].extras = config[0].homes[i].extras || {}; config[0].homes[i].extras.childLockSwitches = config[0].homes[i].extras.childLockSwitches || []; let allFoundDevices = []; //Mobile Devices Informations const mobileDevices = await homebridge.request('/exec', { dest: 'getMobileDevices', data: home.id }); if (!config[0].homes[i].presence) config[0].homes[i].presence = { anyone: false, accTypeAnyone: 'OCCUPANCY', user: [] }; //Remove not registred devices config[0].homes[i].presence.user.forEach((user, index) => { let found = false; mobileDevices.forEach(foundUser => { if (foundUser.name === user.name) { found = true; } }); if (!found) { homebridge.toast.info(user.name + ' removed from config!', home.username); config[0].homes[i].presence.user.splice(index, 1); } }); //Check for new registred devices if (config[0].homes[i].presence.user.length) { for (const foundUser of mobileDevices) { let userIndex; config[0].homes[i].presence.user.forEach((user, index) => { if (user.name === foundUser.name) { userIndex = index; } }); if (userIndex === undefined) { config[0].homes[i].presence.user.push({ active: false, name: foundUser.name, accType: 'OCCUPANCY' }); } } } else { config[0].homes[i].presence.user = mobileDevices.map(user => { return { active: false, name: user.name, accType: 'OCCUPANCY' }; }); } //Zone Informations const zones = await homebridge.request('/exec', { dest: 'getZones', data: home.id }); //Remove not available zones config[0].homes[i].zones.forEach((zone, index) => { let found = false; zones.forEach(foundZone => { if (foundZone.name === zone.name) { found = true; } }); if (!found) { homebridge.toast.info(zone.name + ' removed from config!', home.username); config[0].homes[i].zones.splice(index, 1); } }); //Check for new zones or refresh exist one if (config[0].homes[i].zones.length) { for (const foundZone of zones) { const capabilities = await homebridge.request('/exec', { dest: 'getZoneCapabilities', data: [home.id, foundZone.id] }) || {}; let minTempValue = capabilities.temperatures ? homeInfo.temperatureUnit === 'CELSIUS' ? capabilities.temperatures.celsius.min : capabilities.temperatures.fahrenheit.min : foundZone.type === 'HOT_WATER' ? homeInfo.temperatureUnit === 'CELSIUS' ? 30 : 86 : homeInfo.temperatureUnit === 'CELSIUS' ? 5 : 41; let maxTempValue = capabilities.temperatures ? homeInfo.temperatureUnit === 'CELSIUS' ? capabilities.temperatures.celsius.max : capabilities.temperatures.fahrenheit.max : foundZone.type === 'HOT_WATER' ? homeInfo.temperatureUnit === 'CELSIUS' ? 65 : 149 : homeInfo.temperatureUnit === 'CELSIUS' ? 27 : 77; let minTempStep = capabilities.temperatures ? homeInfo.temperatureUnit === 'CELSIUS' ? capabilities.temperatures.celsius.step : capabilities.temperatures.fahrenheit.step : 1; if (foundZone.devices) foundZone.devices.forEach(dev => { if (dev.deviceType && (dev.deviceType.includes('VA01') || dev.deviceType.includes('VA02'))) allFoundDevices.push({ name: foundZone.name + ' ' + dev.shortSerialNo, serialNumber: dev.shortSerialNo }); }); let zoneIndex; config[0].homes[i].zones.forEach((zone, index) => { if (zone.name === foundZone.name) { zoneIndex = index; } }); if (zoneIndex !== undefined) { config[0].homes[i].zones[zoneIndex].id = foundZone.id; config[0].homes[i].zones[zoneIndex].type = foundZone.type; config[0].homes[i].zones[zoneIndex].minValue = minTempValue; config[0].homes[i].zones[zoneIndex].maxValue = maxTempValue; config[0].homes[i].zones[zoneIndex].minStep = minTempStep; } else { config[0].homes[i].zones.push({ active: true, id: foundZone.id, name: foundZone.name, type: foundZone.type, delaySwitch: false, autoOffDelay: false, noBattery: false, mode: 'MANUAL', modeTimer: 30, minValue: minTempValue, maxValue: maxTempValue, minStep: minTempStep, easyMode: false, openWindowSensor: false, openWindowSwitch: false, separateTemperature: false, separateHumidity: false, accTypeBoiler: 'SWITCH', boilerTempSupport: false }); } } } else { for (const zone of zones) { const capabilities = await homebridge.request('/exec', { dest: 'getZoneCapabilities', data: [home.id, zone.id] }) || {}; let minTempValue = capabilities.temperatures ? homeInfo.temperatureUnit === 'CELSIUS' ? capabilities.temperatures.celsius.min : capabilities.temperatures.fahrenheit.min : zone.type === 'HOT_WATER' ? homeInfo.temperatureUnit === 'CELSIUS' ? 30 : 86 : homeInfo.temperatureUnit === 'CELSIUS' ? 5 : 41; let maxTempValue = capabilities.temperatures ? homeInfo.temperatureUnit === 'CELSIUS' ? capabilities.temperatures.celsius.max : capabilities.temperatures.fahrenheit.max : zone.type === 'HOT_WATER' ? homeInfo.temperatureUnit === 'CELSIUS' ? 65 : 149 : homeInfo.temperatureUnit === 'CELSIUS' ? 27 : 77; let minTempStep = capabilities.temperatures ? homeInfo.temperatureUnit === 'CELSIUS' ? capabilities.temperatures.celsius.step : capabilities.temperatures.fahrenheit.step : 1; if (zone.devices) zone.devices.forEach(dev => { if (dev.deviceType && (dev.deviceType.includes('VA01') || dev.deviceType.includes('VA02'))) allFoundDevices.push({ name: zone.name + ' ' + dev.shortSerialNo, serialNumber: dev.shortSerialNo }); }); config[0].homes[i].zones.push({ active: true, id: zone.id, name: zone.name, type: zone.type, delaySwitch: false, autoOffDelay: false, noBattery: false, mode: 'MANUAL', modeTimer: 30, minValue: minTempValue, maxValue: maxTempValue, minStep: minTempStep, easyMode: false, openWindowSensor: false, openWindowSwitch: false, separateTemperature: false, separateHumidity: false, accTypeBoiler: 'SWITCH', boilerTempSupport: false }); } } //remove non existing childLockSwitches config[0].homes[i].extras.childLockSwitches.forEach((childLockSwitch, index) => { let found = false; allFoundDevices.forEach(foundDevice => { if (foundDevice.serialNumber === childLockSwitch.serialNumber) { found = true; } }); if (!found) { homebridge.toast.info(childLockSwitch.serialNumber + ' removed from config!', home.username); config[0].homes[i].extras.childLockSwitches.splice(index, 1); } }); //check for new childLockSwitches if (config[0].homes[i].extras.childLockSwitches.length) { for (const foundDevice of allFoundDevices) { let found = false; config[0].homes[i].extras.childLockSwitches.forEach(childLockSwitch => { if (childLockSwitch.serialNumber === foundDevice.serialNumber) { found = true; } }); if (!found) { config[0].homes[i].extras.childLockSwitches.push({ active: false, name: foundDevice.name, serialNumber: foundDevice.serialNumber }); } } } else { config[0].homes[i].extras.childLockSwitches = allFoundDevices.map(device => { return { active: false, name: device.name, serialNumber: device.serialNumber }; }); } await TIMEOUT(2000); homebridge.toast.info(home.name + ' resynchronized!', home.username); await TIMEOUT(2000); } } } homebridge.toast.info('Looking for homes which are found in the API but are not configured...', 'Info'); await TIMEOUT(2000); let addedHomes = 0; //add new homes from API that doesnt exist in config for (const foundHome of availableHomesInApis) { let found = false; config[0].homes.forEach(home => { if (home.name === foundHome.name || home.id === foundHome.id) found = true; }); if (!found) { homebridge.toast.info('Found ' + foundHome.name, foundHome.username); addedHomes += 1; //Init API with credentials await fnAuthenticate({ username: foundHome.username, tadoApiUrl: foundHome.tadoApiUrl, skipAuth: foundHome.skipAuth }); const homeConfig = { id: foundHome.id, name: foundHome.name, username: foundHome.username, tadoApiUrl: foundHome.tadoApiUrl, skipAuth: foundHome.skipAuth, polling: 300, zones: [], presence: { anyone: false, accTypeAnyone: 'OCCUPANCY', user: [] }, weather: { temperatureSensor: false, solarIntensity: false, accTypeSolarIntensity: 'LIGHTBULB' }, extras: { centralSwitch: false, runningInformation: false, boostSwitch: false, sheduleSwitch: false, turnoffSwitch: false, presenceLock: false, accTypePresenceLock: 'ALARM', childLockSwitches: [] }, telegram: { active: false } }; //Home Informations const homeInfo = await homebridge.request('/exec', { dest: 'getHome', data: foundHome.id }); homeConfig.temperatureUnit = homeInfo.temperatureUnit; homeConfig.geolocation = { longitude: homeInfo.geolocation.longitude.toString(), latitude: homeInfo.geolocation.latitude.toString() }; //Mobile Devices Informations const mobileDevices = await homebridge.request('/exec', { dest: 'getMobileDevices', data: foundHome.id }); homeConfig.presence.user = mobileDevices.map(user => { return { active: false, name: user.name, accType: 'OCCUPANCY' }; }); //Zone Informations const zones = await homebridge.request('/exec', { dest: 'getZones', data: foundHome.id }); for (const zone of zones) { const capabilities = await homebridge.request('/exec', { dest: 'getZoneCapabilities', data: [homeInfo.id, zone.id] }) || {}; let minTempValue = capabilities.temperatures ? homeInfo.temperatureUnit === 'CELSIUS' ? capabilities.temperatures.celsius.min : capabilities.temperatures.fahrenheit.min : zone.type === 'HOT_WATER' ? homeInfo.temperatureUnit === 'CELSIUS' ? 30 : 86 : homeInfo.temperatureUnit === 'CELSIUS' ? 5 : 41; let maxTempValue = capabilities.temperatures ? homeInfo.temperatureUnit === 'CELSIUS' ? capabilities.temperatures.celsius.max : capabilities.temperatures.fahrenheit.max : zone.type === 'HOT_WATER' ? homeInfo.temperatureUnit === 'CELSIUS' ? 65 : 149 : homeInfo.temperatureUnit === 'CELSIUS' ? 27 : 77; let minTempStep = capabilities.temperatures ? homeInfo.temperatureUnit === 'CELSIUS' ? capabilities.temperatures.celsius.step : capabilities.temperatures.fahrenheit.step : 1; if (zone.devices) zone.devices.forEach(device => { if (device.deviceType && (device.deviceType.includes('VA01') || device.deviceType.includes('VA02'))) homeConfig.extras.childLockSwitches.push({ active: false, name: zone.name + ' ' + device.shortSerialNo, serialNumber: device.shortSerialNo }); }); homeConfig.zones.push({ active: true, id: zone.id, name: zone.name, type: zone.type, delaySwitch: false, autoOffDelay: false, noBattery: false, mode: 'MANUAL', modeTimer: 30, minValue: minTempValue, maxValue: maxTempValue, minStep: minTempStep, easyMode: false, openWindowSensor: false, openWindowSwitch: false, separateTemperature: false, separateHumidity: false, accTypeBoiler: 'SWITCH', boilerTempSupport: false }); } config[0].homes.push(homeConfig); await TIMEOUT(2000); homebridge.toast.info(foundHome.name + ' added to config.json!', foundHome.username); await TIMEOUT(2000); } } if (!addedHomes) await TIMEOUT(2000); homebridge.toast.info(removedHomes + ' new homes configured!', 'Info'); } else { //add new account/home const me = await homebridge.request('/exec', { dest: 'getMe' }); for (const foundHome of me.homes) { let homeIndex; config[0].homes.forEach((home, index) => { if (home.name === foundHome.name || home.id === foundHome.id) { homeIndex = index; } }); if (homeIndex === undefined) { const homeConfig = { id: foundHome.id, name: foundHome.name, username: auth.username, tadoApiUrl: auth.tadoApiUrl, skipAuth: auth.skipAuth, polling: 300, zones: [], presence: { anyone: false, accTypeAnyone: 'OCCUPANCY', user: [] }, weather: { temperatureSensor: false, solarIntensity: false, accTypeSolarIntensity: 'LIGHTBULB' }, extras: { centralSwitch: false, runningInformation: false, boostSwitch: false, sheduleSwitch: false, turnoffSwitch: false, presenceLock: false, accTypePresenceLock: 'ALARM', childLockSwitches: [] }, telegram: { active: false } }; await TIMEOUT(2000); fetchDevicesBar.animate(0.40); //Home Informations const homeInfo = await homebridge.request('/exec', { dest: 'getHome', data: foundHome.id }); homeConfig.temperatureUnit = homeInfo.temperatureUnit; homeConfig.geolocation = { longitude: homeInfo.geolocation.longitude.toString(), latitude: homeInfo.geolocation.latitude.toString() }; await TIMEOUT(2000); fetchDevicesBar.animate(0.60); //Mobile Devices Informations const mobileDevices = await homebridge.request('/exec', { dest: 'getMobileDevices', data: foundHome.id }); homeConfig.presence.user = mobileDevices.map(user => { return { active: false, name: user.name, accType: 'OCCUPANCY' }; }); await TIMEOUT(2000); fetchDevicesBar.animate(0.80); //Zone Informations const zones = await homebridge.request('/exec', { dest: 'getZones', data: foundHome.id }); for (const zone of zones) { const capabilities = await homebridge.request('/exec', { dest: 'getZoneCapabilities', data: [homeInfo.id, zone.id] }) || {}; let minTempValue = capabilities.temperatures ? homeInfo.temperatureUnit === 'CELSIUS' ? capabilities.temperatures.celsius.min : capabilities.temperatures.fahrenheit.min : zone.type === 'HOT_WATER' ? homeInfo.temperatureUnit === 'CELSIUS' ? 30 : 86 : homeInfo.temperatureUnit === 'CELSIUS' ? 5 : 41; let maxTempValue = capabilities.temperatures ? homeInfo.temperatureUnit === 'CELSIUS' ? capabilities.temperatures.celsius.max : capabilities.temperatures.fahrenheit.max : zone.type === 'HOT_WATER' ? homeInfo.temperatureUnit === 'CELSIUS' ? 65 : 149 : homeInfo.temperatureUnit === 'CELSIUS' ? 27 : 77; let minTempStep = capabilities.temperatures ? homeInfo.temperatureUnit === 'CELSIUS' ? capabilities.temperatures.celsius.step : capabilities.temperatures.fahrenheit.step : 1; if (zone.devices) zone.devices.forEach(device => { if (device.deviceType && (device.deviceType.includes('VA01') || device.deviceType.includes('VA02'))) homeConfig.extras.childLockSwitches.push({ active: false, name: zone.name + ' ' + device.shortSerialNo, serialNumber: device.shortSerialNo }); }); homeConfig.zones.push({ active: true, id: zone.id, name: zone.name, type: zone.type, delaySwitch: false, autoOffDelay: false, noBattery: false, mode: 'MANUAL', modeTimer: 30, minValue: minTempValue, maxValue: maxTempValue, minStep: minTempStep, easyMode: false, openWindowSensor: false, openWindowSwitch: false, separateTemperature: false, separateHumidity: false, accTypeBoiler: 'SWITCH', boilerTempSupport: false }); } config[0].homes.push(homeConfig); } } } await TIMEOUT(2000); fetchDevicesBar.animate(1.00); if (resync) homebridge.toast.info('Resynchronized!', auth.username); await TIMEOUT(2000); return config; } catch (err) { fetchDevicesBar.set(0); fetchDevicesBar.setText('Error!'); console.log(err); homebridge.toast.error(err.message, 'Error'); await TIMEOUT(2000); return false; } } (async () => { try { //check version before load ui if (window.homebridge.serverEnv.env && window.compareVersions(window.homebridge.serverEnv.env.packageVersion, '4.34.0') < 0) { await showOldSchema(true); return; } pluginConfig = await homebridge.getPluginConfig(); if (!pluginConfig.length) { pluginConfig = [{ platform: 'TadoPlatform', name: 'TadoPlatform', homes: [] }]; transPage(false, $('#notConfigured')); } else { if (!pluginConfig[0].homes || (pluginConfig[0].homes && !pluginConfig[0].homes.length)) { pluginConfig[0].homes = []; return transPage(false, $('#notConfigured')); } pluginConfig[0].homes.forEach(home => { $('#deviceSelect').append('<option value="' + home.name + '">' + home.name + ' &lt;' + home.username + '&gt;</option>'); }); transPage(false, $('#isConfigured')); } } catch (err) { console.log(err); homebridge.toast.error(err.message, 'Error'); } })(); //jquery listener $('.back').on('click', () => { goBack(); }); $('.oldConfig').on('click', async () => { await showOldSchema(false); }); $('#start, #addDevice').on('click', () => { resetUI(); let activeContent = $('#notConfigured').css('display') !== 'none' ? $('#notConfigured') : $('#isConfigured'); transPage(activeContent, $('#configureDevice')); }); $('#reSync').on('click', async () => { try { homebridge.showSpinner(); const config = await fetchDevices(false, false, true); if (config) { await addNewDeviceToConfig(config, false, true); resetUI(); } homebridge.hideSpinner(); } catch (err) { homebridge.hideSpinner(); console.log(err); homebridge.toast.error(err.message, 'Error'); } }); $('#auth').on('click', async () => { try { let auth = { username: $('#homeUsername').val(), tadoApiUrl: $('#homeTadoApiUrl').val(), skipAuth: $('#homeSkipAuth').prop('checked') }; transPage($('#configureDevice'), $('#fetchDevices')); const config = await fetchDevices(auth, false, false); if (config) { await addNewDeviceToConfig(config, false, false); transPage($('#fetchDevices'), $('#isConfigured')); resetUI(); } } catch (err) { console.log(err); homebridge.toast.error(err.message, 'Error'); } }); $('#editDevice').on('click', () => { resetUI(); currentHome = $('#deviceSelect option:selected').val(); let home = pluginConfig[0].homes.find(home => home.name === currentHome); if (!home) return homebridge.toast.error('Can not find selected home!', 'Error'); createCustomSchema(home); transPage($('#main, #isConfigured'), $('#header'), false, true); }); $('#refreshDevice').on('click', async () => { if (customSchemaActive && currentHome) { resetSchema(); let home = pluginConfig[0].homes.find(home => home.name === currentHome); if (!home) return homebridge.toast.error('Can not find home in config!', 'Error'); transPage($('#isConfigured'), $('#fetchDevices')); const config = await fetchDevices({ username: home.username, tadoApiUrl: home.tadoApiUrl, skipAuth: home.skipAuth }, true, false); if (config) { await addNewDeviceToConfig(config, true, false); transPage($('#fetchDevices'), $('#isConfigured')); resetUI(); } } else { homebridge.toast.error('No home selected to refresh!', 'Error'); } }); $('#removeDevice').on('click', async () => { try { await removeDeviceFromConfig(); resetUI(); transPage(false, pluginConfig[0].homes.length ? $('#isConfigured') : $('#notConfigured')); } catch (err) { console.log(err); homebridge.toast.error(err.message, 'Error'); } });