homebridge-melcloud-control
Version:
Homebridge plugin to control Mitsubishi Air Conditioner, Heat Pump and Energy Recovery Ventilation.
423 lines (364 loc) • 19.1 kB
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 accountName = pluginConfig[0].accounts[this.deviceIndex].name;
const user = pluginConfig[0].accounts[this.deviceIndex].user;
const passwd = pluginConfig[0].accounts[this.deviceIndex].passwd;
const language = pluginConfig[0].accounts[this.deviceIndex].language;
const payload = {
accountName: accountName,
user: user,
passwd: passwd,
language: language
};
const devicesInMelCloud = await homebridge.request('/connect', payload);
//new found devices
const newDevices = {
ata: [],
ataPresets: [],
atw: [],
atwPresets: [],
erv: [],
ervPresets: [],
};
//get devices from melcloud by type
const devicesByTypeInMelCloud = {
ata: [],
atw: [],
erv: []
};
for (const deviceInMelcloud of devicesInMelCloud) {
const deviceType = deviceInMelcloud.Type;
const pusDeviceTypeAta = deviceType === 0 ? devicesByTypeInMelCloud.ata.push(deviceInMelcloud) : false;
const pusDeviceTypeAtw = deviceType === 1 ? devicesByTypeInMelCloud.atw.push(deviceInMelcloud) : false;
const pusDeviceTypeErv = deviceType === 3 ? devicesByTypeInMelCloud.erv.push(deviceInMelcloud) : false;
};
//devices in config
const ataDevicesInConfigExist = pluginConfig[0].accounts[this.deviceIndex].ataDevices ?? false;
const atwDevicesInConfigExist = pluginConfig[0].accounts[this.deviceIndex].atwDevices ?? false;
const ervDevicesInConfigExist = pluginConfig[0].accounts[this.deviceIndex].ervDevices ?? false;
//check key of devices in config exist
const ataDevicesInConfig = !ataDevicesInConfigExist ? pluginConfig[0].accounts[this.deviceIndex].ataDevices = [] : ataDevicesInConfigExist;
const atwDevicesInConfig = !atwDevicesInConfigExist ? pluginConfig[0].accounts[this.deviceIndex].atwDevices = [] : atwDevicesInConfigExist;
const ervDevicesInConfig = !ervDevicesInConfigExist ? pluginConfig[0].accounts[this.deviceIndex].ervDevices = [] : ervDevicesInConfigExist;
//device ata
devicesByTypeInMelCloud.ata.forEach((device, index) => {
const deviceId = device.DeviceID;
const deviceType = device.Type;
const deviceName = device.DeviceName;
const devicePresets = device.Presets ?? [];
const deviceAta = {
id: deviceId,
type: deviceType,
typeString: "Air Conditioner",
disableAccessory: false,
name: deviceName,
displayMode: 1,
heatDryFanMode: 1,
coolDryFanMode: 1,
autoDryFanMode: 1,
temperatureSensor: false,
temperatureSensorOutdoor: false,
presets: [],
buttonsSensors: []
};
const deviceExistsInConfig = ataDevicesInConfig.some(device => device.id === deviceId);
if (!deviceExistsInConfig) {
ataDevicesInConfig.push(deviceAta);
newDevices.ata.push(deviceAta);
};
//presets of device in melcloud
devicePresets.forEach((preset) => {
const presetId = preset.ID;
const presetName = preset.NumberDescription;
preset.id = presetId;
preset.name = presetName;
preset.displayType == 0;
preset.namePrefix = false;
//find the specific ataDevice to which the preset belongs
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);
}
});
});
const textAta = `ATA: ${newDevices.ata.length}`;
const textAtaPresets = `ATA Presets: ${newDevices.ataPresets.length}`;
//device atw
devicesByTypeInMelCloud.atw.forEach((device, index) => {
const deviceId = device.DeviceID;
const deviceType = device.Type;
const deviceName = device.DeviceName;
const devicePresets = device.Presets ?? [];
const deviceAtw = {
id: deviceId,
type: deviceType,
typeString: "Heat Pump",
disableAccessory: false,
name: deviceName,
displayMode: 1,
temperatureSensor: false,
temperatureSensorFlow: false,
temperatureSensorReturn: false,
temperatureSensorFlowZone1: false,
temperatureSensorReturnZone1: false,
temperatureSensorFlowWaterTank: false,
temperatureSensorReturnWaterTank: false,
temperatureSensorFlowZone2: false,
temperatureSensorReturnZone2: false,
temperatureSensor: false,
temperatureSensorOutdoor: false,
presets: [],
buttonsSensors: []
};
const deviceExistsInConfig = atwDevicesInConfig.some(device => device.id === deviceId);
if (!deviceExistsInConfig) {
atwDevicesInConfig.push(deviceAtw);
newDevices.atw.push(deviceAtw);
};
//presets of device in melcloud
devicePresets.forEach((preset) => {
const presetId = preset.ID;
const presetName = preset.NumberDescription;
preset.id = presetId;
preset.name = presetName;
preset.displayType == 0;
preset.namePrefix = false;
//find the specific ataDevice to which the preset belongs
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);
}
});
});
const textAtw = `ATW: ${newDevices.atw.length}`;
const textAtwPresets = `ATW Presets: ${newDevices.atwPresets.length}`;
//device erv
devicesByTypeInMelCloud.erv.forEach((device, index) => {
const deviceId = device.DeviceID;
const deviceType = device.Type;
const deviceName = device.DeviceName;
const devicePresets = device.Presets ?? [];
const deviceErv = {
id: deviceId,
type: deviceType,
typeString: "Energy Recovery Ventilation",
disableAccessory: false,
name: deviceName,
displayMode: 1,
temperatureSensor: false,
temperatureSensorOutdoor: false,
temperatureSensorSupply: false,
temperatureSensor: false,
temperatureSensorOutdoor: false,
presets: [],
buttonsSensors: []
};
const deviceExistsInConfig = ervDevicesInConfig.some(device => device.id === deviceId);
if (!deviceExistsInConfig) {
ervDevicesInConfig.push(deviceErv);
newDevices.erv.push(deviceErv);
};
//presets of device in melcloud
devicePresets.forEach((preset) => {
const presetId = preset.ID;
const presetName = preset.NumberDescription;
preset.id = presetId;
preset.name = presetName;
preset.displayType == 0;
preset.namePrefix = false;
//find the specific ataDevice to which the preset belongs
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);
}
});
});
const textErv = `ERV: ${newDevices.erv.length}`;
const textErvPresets = `ERV Presets: ${newDevices.ervPresets.length}`;
//display info
const newDevicesCount = newDevices.ata.length + newDevices.atw.length + newDevices.erv.length ?? 0;
const newPresetsCount = newDevices.ataPresets.length + newDevices.atwPresets.length + newDevices.ervPresets.length ?? 0;
if (newDevicesCount === 0 && newPresetsCount === 0) {
document.getElementById('info').innerHTML = 'No new devices found.';
document.getElementById('info').style.color = 'white';
};
if (newDevicesCount > 0 && newPresetsCount > 0) {
document.getElementById('info').innerHTML = `Found, ${textAta}, ${textAtw}, ${textErv}.`;
document.getElementById('info').style.color = 'green';
document.getElementById('info1').innerHTML = `${textAtaPresets}, ${textAtwPresets}, ${textErvPresets}.`;
document.getElementById('info1').style.color = 'green';
document.getElementById('info2').innerHTML = 'Now You can configure it.';
document.getElementById('info2').style.color = 'green';
};
if (newDevicesCount > 0 && newPresetsCount === 0) {
document.getElementById('info').innerHTML = `Found, ${textAta}, ${textAtw}, ${textErv}.`;
document.getElementById('info').style.color = 'green';
document.getElementById('info1').innerHTML = 'Now You can configure it.';
document.getElementById('info1').style.color = 'green';
};
if (newDevicesCount === 0 && newPresetsCount > 0) {
document.getElementById('info').innerHTML = `Found, ${textAtaPresets}, ${textAtwPresets}, ${textErvPresets}.`;
document.getElementById('info').style.color = 'green';
document.getElementById('info1').innerHTML = 'Now You can configure it.';
document.getElementById('info1').style.color = 'green';
}
//update ans save plugin config
await homebridge.updatePluginConfig(pluginConfig);
await homebridge.savePluginConfig(pluginConfig);
//update button state
document.getElementById(`logIn`).setAttribute("class", "btn btn-secondary");
homebridge.hideSpinner();
} catch (error) {
document.getElementById('info').innerHTML = 'Check Your credentials data and try again.';
document.getElementById('info').style.color = 'yellow';
document.getElementById('info1').innerHTML = `Error: ${error}`;
document.getElementById('info1').style.color = 'red';
document.getElementById(`logIn`).setAttribute("class", "btn btn-secondary");
} finally {
homebridge.hideSpinner();
};
});
})();
</script>