UNPKG

homebridge-melcloud-control

Version:

Homebridge plugin to control Mitsubishi Air Conditioner, Heat Pump and Energy Recovery Ventilation.

377 lines (324 loc) 16.4 kB
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>MELCloud Account Configuration</title> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.0/css/bootstrap.min.css"> <script src="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.2/js/all.min.js"></script> </head> <body> <div class="container mt-4"> <div class="text-center"> <img src="homebridge-melcloud-control.png" alt="Image" height="120" /> </div> <div id="melCloudAccount" class="card card-body mt-3"> <form id="configForm"> <div class="text-center"> <label id="accountName" class="fw-bold" style="font-size: 23px;">Account</label><br> <label id="info" class="d-block" style="font-size: 17px;"></label> <label id="info1" class="d-block" style="font-size: 15px;"></label> <label id="info2" class="d-block" style="font-size: 15px;"></label> </div> <div class="mb-3"> <label for="name" class="form-label">Name</label> <input id="name" type="text" class="form-control" required> </div> <div class="mb-3"> <label for="user" class="form-label">User Name</label> <input id="user" type="text" class="form-control" required> </div> <div class="mb-3"> <label for="passwd" class="form-label">Password</label> <input id="passwd" type="password" class="form-control" autocomplete="off" required> </div> <div class="mb-3"> <label for="language" class="form-label">Language</label> <select id="language" name="language" class="form-control"> <option value="0">English</option> <option value="1">Български</option> <option value="2">Čeština</option> <option value="3">Dansk</option> <option value="4">Deutsch</option> <option value="5">Eesti</option> <option value="6">Español</option> <option value="7">Français</option> <option value="8">Հայերեն</option> <option value="9">Latviešu</option> <option value="10">Lietuvių</option> <option value="11">Magyar</option> <option value="12">Nederlands</option> <option value="13">Norwegian</option> <option value="14">Polski</option> <option value="15">Português</option> <option value="16">Русский</option> <option value="17">Suomi</option> <option value="18">Svenska</option> <option value="19">Українська</option> <option value="20">Türkçe</option> <option value="21">Ελληνικά</option> <option value="22">Hrvatski</option> <option value="23">Română</option> <option value="24">Slovenščina</option> </select> </div> <div class="text-center"> <button id="logIn" type="button" class="btn btn-secondary">Connect to MELCloud</button> <button id="configButton" type="button" class="btn btn-secondary"><i class="fas fa-gear"></i></button> </div> </form> </div> <div id="accountButton" class="mt-3"></div> </div> <script> (async () => { //get the plugin config array const pluginConfig = await homebridge.getPluginConfig(); if (!pluginConfig.length) { pluginConfig.push({}); await homebridge.updatePluginConfig(pluginConfig); homebridge.showSchemaForm(); return; } this.configButtonState = false; //get accounts count const accountsCount = pluginConfig[0].accounts.length; this.deviceIndex = 0; for (let i = 0; i < accountsCount; i++) { //create buttons const button = document.createElement("button"); button.setAttribute("type", "button"); button.setAttribute("id", `button${i}`); button.setAttribute("class", "btn btn-primary"); button.style.textTransform = 'none'; button.innerText = pluginConfig[0].accounts[i].name; document.getElementById("accountButton").appendChild(button); //get actuall value on account select document.getElementById(`button${i}`).addEventListener('click', async () => { for (let j = 0; j < accountsCount; j++) { const setRemoveAtribute = j === i ? document.getElementById(`button${j}`).setAttribute("class", "btn btn-secondary") : document.getElementById(`button${j}`).setAttribute("class", "btn btn-primary"); } document.getElementById('accountName').innerHTML = pluginConfig[0].accounts[i].name || ''; document.getElementById('name').value = pluginConfig[0].accounts[i].name || ''; document.getElementById('user').value = pluginConfig[0].accounts[i].user || ''; document.getElementById('passwd').value = pluginConfig[0].accounts[i].passwd || ''; document.getElementById('language').value = pluginConfig[0].accounts[i].language || ''; const accountConfigured = pluginConfig[0].accounts[i].name && pluginConfig[0].accounts[i].user && pluginConfig[0].accounts[i].passwd && pluginConfig[0].accounts[i].language; const button = document.getElementById('logIn'); const setButtonState = accountConfigured ? button.disabled = false : button.disabled = true; await homebridge.updatePluginConfig(pluginConfig); this.deviceIndex = i; }); const click = i === accountsCount - 1 ? document.getElementById(`button0`).click() : false; const update = i === accountsCount - 1 ? await homebridge.updatePluginConfig(pluginConfig) : false; }; //load melCloudAccount form document.getElementById('melCloudAccount').style.display = 'block'; //watch for changes to the form document.getElementById('configForm').addEventListener('input', async () => { pluginConfig[0].accounts[this.deviceIndex].name = document.querySelector('#name').value; pluginConfig[0].accounts[this.deviceIndex].user = document.querySelector('#user').value; pluginConfig[0].accounts[this.deviceIndex].passwd = document.querySelector('#passwd').value; pluginConfig[0].accounts[this.deviceIndex].language = document.querySelector('#language').value; const accountConfigured = pluginConfig[0].accounts[this.deviceIndex].name && pluginConfig[0].accounts[this.deviceIndex].user && pluginConfig[0].accounts[this.deviceIndex].passwd && pluginConfig[0].accounts[this.deviceIndex].language; const button = document.getElementById('logIn'); const setButtonState = accountConfigured ? button.disabled = false : button.disabled = true; await homebridge.updatePluginConfig(pluginConfig); await homebridge.savePluginConfig(pluginConfig); }); //watch for changes to the config button document.getElementById('configButton').addEventListener('click', async () => { const showHideSettings = this.configButtonState ? homebridge.hideSchemaForm() : homebridge.showSchemaForm(); const setRemoveAtribute = this.configButtonState ? document.getElementById(`configButton`).setAttribute("class", "btn btn-secondary") : document.getElementById(`configButton`).setAttribute("class", "btn btn-primary"); this.configButtonState = !this.configButtonState; }); //watch for click on the login button document.getElementById('logIn').addEventListener('click', async () => { homebridge.showSpinner(); document.getElementById(`logIn`).setAttribute("class", "btn btn-primary"); document.getElementById('info').innerHTML = 'Connecting...'; document.getElementById('info').style.color = 'yellow'; try { const account = pluginConfig[0].accounts[this.deviceIndex]; const { name: accountName, user, passwd, language } = account; const payload = { accountName, user, passwd, language }; const devicesInMelCloud = await homebridge.request('/connect', payload); const newDevices = { ata: [], ataPresets: [], atw: [], atwPresets: [], erv: [], ervPresets: [], }; const devicesByTypeInMelCloud = { ata: [], atw: [], erv: [] }; // Categorize devices by type for (const deviceInMelcloud of devicesInMelCloud) { switch (deviceInMelcloud.Type) { case 0: devicesByTypeInMelCloud.ata.push(deviceInMelcloud); break; case 1: devicesByTypeInMelCloud.atw.push(deviceInMelcloud); break; case 3: devicesByTypeInMelCloud.erv.push(deviceInMelcloud); break; } } // Ensure config arrays exist const ataDevicesInConfig = account.ataDevices ?? (account.ataDevices = []); const atwDevicesInConfig = account.atwDevices ?? (account.atwDevices = []); const ervDevicesInConfig = account.ervDevices ?? (account.ervDevices = []); // Helper for updating UI const updateInfo = (id, text, color) => { const el = document.getElementById(id); if (el) { el.innerHTML = text; el.style.color = color; } }; // Handle ATA devices devicesByTypeInMelCloud.ata.forEach((device) => { const { DeviceID: deviceId, Type: deviceType, DeviceName: deviceName, Presets: devicePresets = [] } = device; const deviceAta = { id: deviceId, type: deviceType, typeString: "Air Conditioner", name: deviceName, displayMode: 1, heatDryFanMode: 1, coolDryFanMode: 1, autoDryFanMode: 1, temperatureSensor: false, temperatureSensorOutdoor: false, presets: [], buttonsSensors: [] }; if (!ataDevicesInConfig.some(device => device.id === deviceId)) { ataDevicesInConfig.push(deviceAta); newDevices.ata.push(deviceAta); } devicePresets.forEach((preset) => { const { ID: presetId, NumberDescription: presetName } = preset; preset.id = presetId; preset.name = presetName; preset.displayType = 0; preset.namePrefix = false; const ataDevice = ataDevicesInConfig.find((device) => device.id === deviceId); if (ataDevice && !ataDevice.presets.some((p) => p.id === presetId)) { ataDevice.presets.push(preset); newDevices.ataPresets.push(preset); } }); }); // Handle ATW devices devicesByTypeInMelCloud.atw.forEach((device) => { const { DeviceID: deviceId, Type: deviceType, DeviceName: deviceName, Presets: devicePresets = [] } = device; const deviceAtw = { id: deviceId, type: deviceType, typeString: "Heat Pump", name: deviceName, displayMode: 1, hideZone: 0, temperatureSensor: false, temperatureSensorFlow: false, temperatureSensorReturn: false, temperatureSensorFlowZone1: false, temperatureSensorReturnZone1: false, temperatureSensorFlowWaterTank: false, temperatureSensorReturnWaterTank: false, temperatureSensorFlowZone2: false, temperatureSensorReturnZone2: false, temperatureSensorOutdoor: false, presets: [], buttonsSensors: [] }; if (!atwDevicesInConfig.some(device => device.id === deviceId)) { atwDevicesInConfig.push(deviceAtw); newDevices.atw.push(deviceAtw); } devicePresets.forEach((preset) => { const { ID: presetId, NumberDescription: presetName } = preset; preset.id = presetId; preset.name = presetName; preset.displayType = 0; preset.namePrefix = false; const atwDevice = atwDevicesInConfig.find((device) => device.id === deviceId); if (atwDevice && !atwDevice.presets.some((p) => p.id === presetId)) { atwDevice.presets.push(preset); newDevices.atwPresets.push(preset); } }); }); // Handle ERV devices devicesByTypeInMelCloud.erv.forEach((device) => { const { DeviceID: deviceId, Type: deviceType, DeviceName: deviceName, Presets: devicePresets = [] } = device; const deviceErv = { id: deviceId, type: deviceType, typeString: "Energy Recovery Ventilation", name: deviceName, displayMode: 1, temperatureSensor: false, temperatureSensorOutdoor: false, temperatureSensorSupply: false, presets: [], buttonsSensors: [] }; if (!ervDevicesInConfig.some(device => device.id === deviceId)) { ervDevicesInConfig.push(deviceErv); newDevices.erv.push(deviceErv); } devicePresets.forEach((preset) => { const { ID: presetId, NumberDescription: presetName } = preset; preset.id = presetId; preset.name = presetName; preset.displayType = 0; preset.namePrefix = false; const ervDevice = ervDevicesInConfig.find((device) => device.id === deviceId); if (ervDevice && !ervDevice.presets.some((p) => p.id === presetId)) { ervDevice.presets.push(preset); newDevices.ervPresets.push(preset); } }); }); // Display info const textAta = `ATA: ${newDevices.ata.length}`; const textAtw = `ATW: ${newDevices.atw.length}`; const textErv = `ERV: ${newDevices.erv.length}`; const textAtaPresets = `ATA Presets: ${newDevices.ataPresets.length}`; const textAtwPresets = `ATW Presets: ${newDevices.atwPresets.length}`; const textErvPresets = `ERV Presets: ${newDevices.ervPresets.length}`; const newDevicesCount = newDevices.ata.length + newDevices.atw.length + newDevices.erv.length; const newPresetsCount = newDevices.ataPresets.length + newDevices.atwPresets.length + newDevices.ervPresets.length; if (newDevicesCount === 0 && newPresetsCount === 0) { updateInfo('info', 'No new devices found.', 'white'); } else { if (newDevicesCount > 0) { updateInfo('info', `Found, ${textAta}, ${textAtw}, ${textErv}.`, 'green'); updateInfo('info2', 'Now You can configure it.', 'green'); } if (newPresetsCount > 0) { updateInfo('info1', `${textAtaPresets}, ${textAtwPresets}, ${textErvPresets}.`, 'green'); if (newDevicesCount === 0) { updateInfo('info', `Found, ${textAtaPresets}, ${textAtwPresets}, ${textErvPresets}.`, 'green'); updateInfo('info1', 'Now You can configure it.', 'green'); } } } // Save updated config await homebridge.updatePluginConfig(pluginConfig); await homebridge.savePluginConfig(pluginConfig); // Update button document.getElementById('logIn').setAttribute('class', 'btn btn-secondary'); homebridge.hideSpinner(); } catch (error) { updateInfo('info', 'Check Your credentials data and try again.', 'yellow'); updateInfo('info1', `Error: ${error}`, 'red'); document.getElementById('logIn').setAttribute('class', 'btn btn-secondary'); } finally { homebridge.hideSpinner(); } }); })(); </script>