8 Commits

Author SHA1 Message Date
Yurii
43c065b97a Merge branch 'master' into 1.5.0-dev 2024-12-04 07:56:19 +03:00
Yurii
5c1e967fdc refactor: improved display of sensors on Dashboard 2024-12-04 06:09:20 +03:00
Yurii
105a79f72c refactor: optimization of OT requests for CH2 2024-12-04 05:55:01 +03:00
Yurii
50280f6db3 fix: negative temperature values from BLE devices 2024-12-04 05:36:04 +03:00
Yurii
c97e50669c fix: typo in purpose of sensors for emergency mode 2024-12-04 05:35:19 +03:00
Yurii
43fd095714 fix: digest auth changed to basic
Digest auth not working on ios #99
2024-12-02 06:26:03 +03:00
Yurii
1eb10563ed chore: update readme 2024-11-30 23:58:54 +03:00
Yurii
f4af237472 chore: bump version to 1.4.6 2024-11-16 14:19:40 +03:00
13 changed files with 138 additions and 112 deletions

View File

@@ -82,3 +82,6 @@ All available information and instructions can be found in the wiki:
## Debug
To display DEBUG messages you must enable debug in settings (switch is disabled by default).
You can connect via Telnet to read messages. IP: ESP8266 ip, port: 23
___
This project is tested with BrowserStack.

View File

@@ -14,7 +14,7 @@ extra_configs = secrets.default.ini
core_dir = .pio
[env]
version = 1.5.0-alpha
version = 1.5.0
framework = arduino
lib_deps =
bblanchon/ArduinoJson@^7.1.0

View File

@@ -218,13 +218,13 @@ protected:
// set outdoor sensor flag
if (settings.equitherm.enabled) {
if (!Sensors::existsConnectedSensorsByPurpose(Sensors::Purpose::INDOOR_TEMP)) {
if (!Sensors::existsConnectedSensorsByPurpose(Sensors::Purpose::OUTDOOR_TEMP)) {
emergencyFlags |= 0b00000001;
}
}
// set indoor sensor flags
if (!Sensors::existsConnectedSensorsByPurpose(Sensors::Purpose::OUTDOOR_TEMP)) {
if (!Sensors::existsConnectedSensorsByPurpose(Sensors::Purpose::INDOOR_TEMP)) {
if (!settings.equitherm.enabled && settings.pid.enabled) {
emergencyFlags |= 0b00000010;
}

View File

@@ -1038,25 +1038,27 @@ protected:
}
// Set CH2 temp
if (vars.master.ch2.enabled && !settings.opentherm.nativeHeatingControl) {
// Converted target CH2 temp
float convertedTemp = convertTemp(
vars.master.ch2.targetTemp,
settings.system.unitSystem,
settings.opentherm.unitSystem
);
if (!settings.opentherm.nativeHeatingControl && vars.master.ch2.enabled) {
if (settings.opentherm.heatingCh1ToCh2 || settings.opentherm.dhwToCh2) {
// Converted target CH2 temp
float convertedTemp = convertTemp(
vars.master.ch2.targetTemp,
settings.system.unitSystem,
settings.opentherm.unitSystem
);
if (this->needSetCh2Temp(convertedTemp)) {
if (this->setCh2Temp(convertedTemp)) {
this->ch2SetTempTime = millis();
if (this->needSetCh2Temp(convertedTemp)) {
if (this->setCh2Temp(convertedTemp)) {
this->ch2SetTempTime = millis();
Log.sinfoln(
FPSTR(L_OT_CH2), F("Set temp: %.2f (converted: %.2f, response: %.2f)"),
vars.master.ch2.targetTemp, convertedTemp, vars.slave.ch2.targetTemp
);
Log.sinfoln(
FPSTR(L_OT_CH2), F("Set temp: %.2f (converted: %.2f, response: %.2f)"),
vars.master.ch2.targetTemp, convertedTemp, vars.slave.ch2.targetTemp
);
} else {
Log.swarningln(FPSTR(L_OT_CH2), F("Failed set temp"));
} else {
Log.swarningln(FPSTR(L_OT_CH2), F("Failed set temp"));
}
}
}
}

View File

@@ -108,8 +108,8 @@ protected:
// dashboard page
auto dashboardPage = (new StaticPage("/dashboard.html", &LittleFS, F("/pages/dashboard.html"), PORTAL_CACHE))
->setBeforeSendCallback([this]() {
if (this->isAuthRequired() && !this->webServer->authenticate(settings.portal.login, settings.portal.password)) {
this->webServer->requestAuthentication(DIGEST_AUTH);
if (this->isAuthRequired() && !this->isValidCredentials()) {
this->webServer->requestAuthentication(BASIC_AUTH);
return false;
}
@@ -119,11 +119,9 @@ protected:
// restart
this->webServer->on(F("/restart.html"), HTTP_GET, [this]() {
if (this->isAuthRequired()) {
if (!this->webServer->authenticate(settings.portal.login, settings.portal.password)) {
this->webServer->send(401);
return;
}
if (this->isAuthRequired() && !this->isValidCredentials()) {
this->webServer->requestAuthentication(BASIC_AUTH);
return;
}
vars.actions.restart = true;
@@ -134,8 +132,8 @@ protected:
// network settings page
auto networkPage = (new StaticPage("/network.html", &LittleFS, F("/pages/network.html"), PORTAL_CACHE))
->setBeforeSendCallback([this]() {
if (this->isAuthRequired() && !this->webServer->authenticate(settings.portal.login, settings.portal.password)) {
this->webServer->requestAuthentication(DIGEST_AUTH);
if (this->isAuthRequired() && !this->isValidCredentials()) {
this->webServer->requestAuthentication(BASIC_AUTH);
return false;
}
@@ -146,8 +144,8 @@ protected:
// settings page
auto settingsPage = (new StaticPage("/settings.html", &LittleFS, F("/pages/settings.html"), PORTAL_CACHE))
->setBeforeSendCallback([this]() {
if (this->isAuthRequired() && !this->webServer->authenticate(settings.portal.login, settings.portal.password)) {
this->webServer->requestAuthentication(DIGEST_AUTH);
if (this->isAuthRequired() && !this->isValidCredentials()) {
this->webServer->requestAuthentication(BASIC_AUTH);
return false;
}
@@ -158,8 +156,8 @@ protected:
// sensors page
auto sensorsPage = (new StaticPage("/sensors.html", &LittleFS, F("/pages/sensors.html"), PORTAL_CACHE))
->setBeforeSendCallback([this]() {
if (this->isAuthRequired() && !this->webServer->authenticate(settings.portal.login, settings.portal.password)) {
this->webServer->requestAuthentication(DIGEST_AUTH);
if (this->isAuthRequired() && !this->isValidCredentials()) {
this->webServer->requestAuthentication(BASIC_AUTH);
return false;
}
@@ -170,8 +168,8 @@ protected:
// upgrade page
auto upgradePage = (new StaticPage("/upgrade.html", &LittleFS, F("/pages/upgrade.html"), PORTAL_CACHE))
->setBeforeSendCallback([this]() {
if (this->isAuthRequired() && !this->webServer->authenticate(settings.portal.login, settings.portal.password)) {
this->webServer->requestAuthentication(DIGEST_AUTH);
if (this->isAuthRequired() && !this->isValidCredentials()) {
this->webServer->requestAuthentication(BASIC_AUTH);
return false;
}
@@ -181,7 +179,7 @@ protected:
// OTA
auto upgradeHandler = (new UpgradeHandler("/api/upgrade"))->setCanUploadCallback([this](const String& uri) {
if (this->isAuthRequired() && !this->webServer->authenticate(settings.portal.login, settings.portal.password)) {
if (this->isAuthRequired() && !this->isValidCredentials()) {
this->webServer->sendHeader(F("Connection"), F("close"));
this->webServer->send(401);
return false;
@@ -222,10 +220,8 @@ protected:
// backup
this->webServer->on(F("/api/backup/save"), HTTP_GET, [this]() {
if (this->isAuthRequired()) {
if (!this->webServer->authenticate(settings.portal.login, settings.portal.password)) {
return this->webServer->send(401);
}
if (this->isAuthRequired() && !this->isValidCredentials()) {
return this->webServer->send(401);
}
JsonDocument doc;
@@ -248,10 +244,8 @@ protected:
});
this->webServer->on(F("/api/backup/restore"), HTTP_POST, [this]() {
if (this->isAuthRequired()) {
if (!this->webServer->authenticate(settings.portal.login, settings.portal.password)) {
return this->webServer->send(401);
}
if (this->isAuthRequired() && !this->isValidCredentials()) {
return this->webServer->send(401);
}
if (vars.states.restarting) {
@@ -329,10 +323,8 @@ protected:
// network
this->webServer->on(F("/api/network/settings"), HTTP_GET, [this]() {
if (this->isAuthRequired()) {
if (!this->webServer->authenticate(settings.portal.login, settings.portal.password)) {
return this->webServer->send(401);
}
if (this->isAuthRequired() && !this->isValidCredentials()) {
return this->webServer->send(401);
}
JsonDocument doc;
@@ -343,10 +335,8 @@ protected:
});
this->webServer->on(F("/api/network/settings"), HTTP_POST, [this]() {
if (this->isAuthRequired()) {
if (!this->webServer->authenticate(settings.portal.login, settings.portal.password)) {
return this->webServer->send(401);
}
if (this->isAuthRequired() && !this->isValidCredentials()) {
return this->webServer->send(401);
}
if (vars.states.restarting) {
@@ -402,11 +392,8 @@ protected:
});
this->webServer->on(F("/api/network/scan"), HTTP_GET, [this]() {
if (this->isAuthRequired()) {
if (!this->webServer->authenticate(settings.portal.login, settings.portal.password)) {
this->webServer->send(401);
return;
}
if (this->isAuthRequired() && !this->isValidCredentials()) {
return this->webServer->send(401);
}
auto apCount = WiFi.scanComplete();
@@ -448,10 +435,8 @@ protected:
// settings
this->webServer->on(F("/api/settings"), HTTP_GET, [this]() {
if (this->isAuthRequired()) {
if (!this->webServer->authenticate(settings.portal.login, settings.portal.password)) {
return this->webServer->send(401);
}
if (this->isAuthRequired() && !this->isValidCredentials()) {
return this->webServer->send(401);
}
JsonDocument doc;
@@ -462,10 +447,8 @@ protected:
});
this->webServer->on(F("/api/settings"), HTTP_POST, [this]() {
if (this->isAuthRequired()) {
if (!this->webServer->authenticate(settings.portal.login, settings.portal.password)) {
return this->webServer->send(401);
}
if (this->isAuthRequired() && !this->isValidCredentials()) {
return this->webServer->send(401);
}
if (vars.states.restarting) {
@@ -513,10 +496,8 @@ protected:
// sensors list
this->webServer->on(F("/api/sensors"), HTTP_GET, [this]() {
if (this->isAuthRequired()) {
if (!this->webServer->authenticate(settings.portal.login, settings.portal.password)) {
return this->webServer->send(401);
}
if (this->isAuthRequired() && !this->isValidCredentials()) {
return this->webServer->send(401);
}
bool detailed = false;
@@ -528,6 +509,7 @@ protected:
for (uint8_t sensorId = 0; sensorId <= Sensors::getMaxSensorId(); sensorId++) {
if (detailed) {
auto& sSensor = Sensors::settings[sensorId];
doc[sensorId][FPSTR(S_ENABLED)] = sSensor.enabled;
doc[sensorId][FPSTR(S_NAME)] = sSensor.name;
doc[sensorId][FPSTR(S_PURPOSE)] = static_cast<uint8_t>(sSensor.purpose);
sensorResultToJson(sensorId, doc[sensorId]);
@@ -543,10 +525,8 @@ protected:
// sensor settings
this->webServer->on(F("/api/sensor"), HTTP_GET, [this]() {
if (this->isAuthRequired()) {
if (!this->webServer->authenticate(settings.portal.login, settings.portal.password)) {
return this->webServer->send(401);
}
if (this->isAuthRequired() && !this->isValidCredentials()) {
return this->webServer->send(401);
}
if (!this->webServer->hasArg(F("id"))) {
@@ -571,10 +551,8 @@ protected:
});
this->webServer->on(F("/api/sensor"), HTTP_POST, [this]() {
if (this->isAuthRequired()) {
if (!this->webServer->authenticate(settings.portal.login, settings.portal.password)) {
return this->webServer->send(401);
}
if (this->isAuthRequired() && !this->isValidCredentials()) {
return this->webServer->send(401);
}
if (vars.states.restarting) {
@@ -654,10 +632,8 @@ protected:
});
this->webServer->on(F("/api/vars"), HTTP_POST, [this]() {
if (this->isAuthRequired()) {
if (!this->webServer->authenticate(settings.portal.login, settings.portal.password)) {
return this->webServer->send(401);
}
if (this->isAuthRequired() && !this->isValidCredentials()) {
return this->webServer->send(401);
}
const String& plain = this->webServer->arg(0);
@@ -956,6 +932,10 @@ protected:
return !network->isApEnabled() && settings.portal.auth && strlen(settings.portal.password);
}
bool isValidCredentials() {
return this->webServer->authenticate(settings.portal.login, settings.portal.password);
}
void onCaptivePortal() {
const String& uri = this->webServer->uri();

View File

@@ -561,7 +561,7 @@ protected:
return;
}
float rawTemp = ((pData[0] | (pData[1] << 8)) * 0.01f);
float rawTemp = (pChar->getValue<int16_t>() * 0.01f);
Log.straceln(
FPSTR(L_SENSORS_BLE),
F("Sensor #%hhu '%s': received temp: %.2f"),
@@ -634,7 +634,7 @@ protected:
return;
}
float rawTemp = ((pData[0] | (pData[1] << 8)) * 0.1f);
float rawTemp = (pChar->getValue<int16_t>() * 0.1f);
Log.straceln(
FPSTR(L_SENSORS_BLE),
F("Sensor #%hhu '%s': received temp: %.2f"),
@@ -719,7 +719,7 @@ protected:
return;
}
float rawHumidity = ((pData[0] | (pData[1] << 8)) * 0.01f);
float rawHumidity = (pChar->getValue<uint16_t>() * 0.01f);
Log.straceln(
FPSTR(L_SENSORS_BLE),
F("Sensor #%hhu '%s': received humidity: %.2f"),
@@ -818,7 +818,7 @@ protected:
return;
}
uint8_t rawBattery = pData[0];
auto rawBattery = pChar->getValue<uint8_t>();
Log.straceln(
FPSTR(L_SENSORS_BLE),
F("Sensor #%hhu '%s': received battery: %.2f"),

View File

@@ -380,13 +380,18 @@
console.log(newSettings);
}
let parameters = { cache: 'no-cache' };
let parameters = {
method: "GET",
cache: "no-cache",
credentials: "include"
};
if (modified) {
parameters.method = "POST";
parameters.body = JSON.stringify(newSettings);
}
const response = await fetch('/api/settings', parameters);
const response = await fetch("/api/settings", parameters);
if (!response.ok) {
throw new Error('Response not valid');
}
@@ -424,7 +429,11 @@
// vars
try {
const response = await fetch('/api/vars', { cache: 'no-cache' });
const response = await fetch("/api/vars", {
cache: "no-cache",
credentials: "include"
});
if (!response.ok) {
throw new Error('Response not valid');
}
@@ -540,7 +549,11 @@
// sensors
try {
const response = await fetch("/api/sensors?detailed=1", { cache: "no-cache" });
const response = await fetch("/api/sensors?detailed=1", {
cache: "no-cache",
credentials: "include"
});
if (!response.ok) {
throw new Error("Response not valid");
}
@@ -568,12 +581,12 @@
}
const sData = result[sensorId];
if (sData.purpose == 255) {
sensorNode.classList.add("hidden");
if (!sData.enabled || sData.purpose == 255) {
sensorNode.classList.toggle("hidden", true);
continue;
}
sensorNode.classList.remove("hidden");
sensorNode.classList.toggle("hidden", false);
setStatus(
".sStatus",

View File

@@ -170,7 +170,11 @@
setTimeout(async function onLoadPage() {
try {
const response = await fetch('/api/info', { cache: 'no-cache' });
const response = await fetch("/api/info", {
cache: "no-cache",
credentials: "include"
});
if (!response.ok) {
throw new Error('Response not valid');
}

View File

@@ -199,7 +199,11 @@
};
try {
const response = await fetch('/api/network/settings', { cache: 'no-cache' });
const response = await fetch("/api/network/settings", {
cache: "no-cache",
credentials: "include"
});
if (!response.ok) {
throw new Error('Response not valid');
}

View File

@@ -194,7 +194,11 @@
const templateNode = container.querySelector("#template");
try {
const response = await fetch("/api/sensors", { cache: "no-cache" });
const response = await fetch("/api/sensors", {
cache: "no-cache",
credentials: "include"
});
if (!response.ok) {
throw new Error("Response not valid");
}
@@ -287,7 +291,11 @@
}
try {
const response = await fetch(sensorForm.action, { cache: "no-cache" });
const response = await fetch(sensorForm.action, {
cache: "no-cache",
credentials: "include"
});
if (response.status != 200) {
return;
}

View File

@@ -808,7 +808,11 @@
};
try {
const response = await fetch('/api/settings', { cache: 'no-cache' });
const response = await fetch("/api/settings", {
cache: "no-cache",
credentials: "include"
});
if (!response.ok) {
throw new Error('Response not valid');
}

View File

@@ -32,7 +32,7 @@ class Lang {
}
if (!this.localeIsSupported(this.defaultLocale)) {
const selected = this.switcher.selectedIndex ?? 0;
const selected = this.switcher.selectedIndex ? this.switcher.selectedIndex : 0;
this.defaultLocale = this.switcher.options[selected].value;
}

View File

@@ -60,10 +60,11 @@ const setupForm = (formSelector, onResultCallback = null, noCastItems = []) => {
}
let response = await fetch(url, {
method: 'POST',
cache: 'no-cache',
method: "POST",
cache: "no-cache",
credentials: "include",
headers: {
'Content-Type': 'application/json'
"Content-Type": "application/json"
},
body: form2json(fd, noCastItems)
});
@@ -218,7 +219,10 @@ const setupNetworkScanForm = (formSelector, tableSelector) => {
attempts--;
try {
let response = await fetch(url, { cache: 'no-cache' });
let response = await fetch(url, {
cache: "no-cache",
credentials: "include"
});
if (response.status == 200) {
await onSuccess(response);
@@ -309,10 +313,11 @@ const setupRestoreBackupForm = (formSelector) => {
if (data.settings != undefined) {
let response = await fetch(url, {
method: 'POST',
cache: 'no-cache',
method: "POST",
cache: "no-cache",
credentials: "include",
headers: {
'Content-Type': 'application/json'
"Content-Type": "application/json"
},
body: JSON.stringify({"settings": data.settings})
});
@@ -331,10 +336,11 @@ const setupRestoreBackupForm = (formSelector) => {
payload["sensors"][sensorId] = data.sensors[sensorId];
const response = await fetch(url, {
method: 'POST',
cache: 'no-cache',
method: "POST",
cache: "no-cache",
credentials: "include",
headers: {
'Content-Type': 'application/json'
"Content-Type": "application/json"
},
body: JSON.stringify(payload)
});
@@ -348,10 +354,11 @@ const setupRestoreBackupForm = (formSelector) => {
if (data.network != undefined) {
let response = await fetch(url, {
method: 'POST',
cache: 'no-cache',
method: "POST",
cache: "no-cache",
credentials: "include",
headers: {
'Content-Type': 'application/json'
"Content-Type": "application/json"
},
body: JSON.stringify({"network": data.network})
});
@@ -496,8 +503,9 @@ const setupUpgradeForm = (formSelector) => {
try {
let fd = new FormData(form);
let response = await fetch(url, {
method: 'POST',
cache: 'no-cache',
method: "POST",
cache: "no-cache",
credentials: "include",
body: fd
});