homebridge-melcloud-control
Version:
Homebridge plugin to control Mitsubishi Air Conditioner, Heat Pump and Energy Recovery Ventilation.
377 lines (324 loc) • 16.4 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 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>