mirror of
https://github.com/Laxilef/OTGateway.git
synced 2025-12-11 18:54:28 +05:00
feat: added feat use of BLE external sensor; added events onIndoorSensorDisconnect and onOutdoorSensorDisconnect for emergency mode; added polling of rssi, humidity, battery for BLE sensors
This commit is contained in:
101
src/MainTask.h
101
src/MainTask.h
@@ -29,7 +29,6 @@ protected:
|
|||||||
enum class PumpStartReason {NONE, HEATING, ANTISTUCK};
|
enum class PumpStartReason {NONE, HEATING, ANTISTUCK};
|
||||||
|
|
||||||
Blinker* blinker = nullptr;
|
Blinker* blinker = nullptr;
|
||||||
unsigned long firstFailConnect = 0;
|
|
||||||
unsigned long lastHeapInfo = 0;
|
unsigned long lastHeapInfo = 0;
|
||||||
unsigned int minFreeHeap = 0;
|
unsigned int minFreeHeap = 0;
|
||||||
unsigned int minMaxFreeBlockHeap = 0;
|
unsigned int minMaxFreeBlockHeap = 0;
|
||||||
@@ -39,6 +38,8 @@ protected:
|
|||||||
PumpStartReason extPumpStartReason = PumpStartReason::NONE;
|
PumpStartReason extPumpStartReason = PumpStartReason::NONE;
|
||||||
unsigned long externalPumpStartTime = 0;
|
unsigned long externalPumpStartTime = 0;
|
||||||
bool telnetStarted = false;
|
bool telnetStarted = false;
|
||||||
|
bool emergencyDetected = false;
|
||||||
|
unsigned long emergencyFlipTime = 0;
|
||||||
|
|
||||||
#if defined(ARDUINO_ARCH_ESP32)
|
#if defined(ARDUINO_ARCH_ESP32)
|
||||||
const char* getTaskName() override {
|
const char* getTaskName() override {
|
||||||
@@ -85,10 +86,6 @@ protected:
|
|||||||
vars.states.mqtt = tMqtt->isConnected();
|
vars.states.mqtt = tMqtt->isConnected();
|
||||||
vars.sensors.rssi = network->isConnected() ? WiFi.RSSI() : 0;
|
vars.sensors.rssi = network->isConnected() ? WiFi.RSSI() : 0;
|
||||||
|
|
||||||
if (vars.states.emergency && !settings.emergency.enable) {
|
|
||||||
vars.states.emergency = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (network->isConnected()) {
|
if (network->isConnected()) {
|
||||||
if (!this->telnetStarted && telnetStream != nullptr) {
|
if (!this->telnetStarted && telnetStream != nullptr) {
|
||||||
telnetStream->begin(23, false);
|
telnetStream->begin(23, false);
|
||||||
@@ -102,17 +99,6 @@ protected:
|
|||||||
tMqtt->disable();
|
tMqtt->disable();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!vars.states.emergency && settings.emergency.enable && settings.emergency.onMqttFault && !tMqtt->isEnabled()) {
|
|
||||||
vars.states.emergency = true;
|
|
||||||
|
|
||||||
} else if (vars.states.emergency && !settings.emergency.onMqttFault) {
|
|
||||||
vars.states.emergency = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this->firstFailConnect != 0) {
|
|
||||||
this->firstFailConnect = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( Log.getLevel() != TinyLogger::Level::INFO && !settings.system.debug ) {
|
if ( Log.getLevel() != TinyLogger::Level::INFO && !settings.system.debug ) {
|
||||||
Log.setLevel(TinyLogger::Level::INFO);
|
Log.setLevel(TinyLogger::Level::INFO);
|
||||||
|
|
||||||
@@ -129,21 +115,10 @@ protected:
|
|||||||
if (tMqtt->isEnabled()) {
|
if (tMqtt->isEnabled()) {
|
||||||
tMqtt->disable();
|
tMqtt->disable();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!vars.states.emergency && settings.emergency.enable && settings.emergency.onNetworkFault) {
|
|
||||||
if (this->firstFailConnect == 0) {
|
|
||||||
this->firstFailConnect = millis();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (millis() - this->firstFailConnect > (settings.emergency.tresholdTime * 1000)) {
|
|
||||||
vars.states.emergency = true;
|
|
||||||
Log.sinfoln(FPSTR(L_MAIN), F("Emergency mode enabled"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
this->yield();
|
this->yield();
|
||||||
|
|
||||||
|
this->emergency();
|
||||||
this->ledStatus();
|
this->ledStatus();
|
||||||
this->externalPump();
|
this->externalPump();
|
||||||
this->yield();
|
this->yield();
|
||||||
@@ -213,6 +188,76 @@ protected:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void emergency() {
|
||||||
|
if (!settings.emergency.enable && vars.states.emergency) {
|
||||||
|
this->emergencyDetected = false;
|
||||||
|
vars.states.emergency = false;
|
||||||
|
|
||||||
|
Log.sinfoln(FPSTR(L_MAIN), F("Emergency mode disabled"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!settings.emergency.enable) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// flags
|
||||||
|
uint8_t emergencyFlags = 0b00000000;
|
||||||
|
|
||||||
|
// set network flag
|
||||||
|
if (settings.emergency.onNetworkFault && !network->isConnected()) {
|
||||||
|
emergencyFlags |= 0b00000001;
|
||||||
|
}
|
||||||
|
|
||||||
|
// set mqtt flag
|
||||||
|
if (settings.emergency.onMqttFault && (!tMqtt->isEnabled() || !tMqtt->isConnected())) {
|
||||||
|
emergencyFlags |= 0b00000010;
|
||||||
|
}
|
||||||
|
|
||||||
|
// set outdoor sensor flag
|
||||||
|
if (settings.sensors.outdoor.type == SensorType::DS18B20 || settings.sensors.outdoor.type == SensorType::BLUETOOTH) {
|
||||||
|
if (settings.emergency.onOutdoorSensorDisconnect && !vars.sensors.outdoor.connected) {
|
||||||
|
emergencyFlags |= 0b00000100;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// set indoor sensor flag
|
||||||
|
if (settings.sensors.indoor.type == SensorType::DS18B20 || settings.sensors.indoor.type == SensorType::BLUETOOTH) {
|
||||||
|
if (settings.emergency.onIndoorSensorDisconnect && !vars.sensors.indoor.connected) {
|
||||||
|
emergencyFlags |= 0b00001000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if any flags is true
|
||||||
|
if ((emergencyFlags & 0b00001111) != 0) {
|
||||||
|
if (!this->emergencyDetected) {
|
||||||
|
// flip flag
|
||||||
|
this->emergencyDetected = true;
|
||||||
|
this->emergencyFlipTime = millis();
|
||||||
|
|
||||||
|
} else if (this->emergencyDetected && !vars.states.emergency) {
|
||||||
|
// enable emergency
|
||||||
|
if (millis() - this->emergencyFlipTime > (settings.emergency.tresholdTime * 1000)) {
|
||||||
|
vars.states.emergency = true;
|
||||||
|
Log.sinfoln(FPSTR(L_MAIN), F("Emergency mode enabled (%hhu)"), emergencyFlags);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
if (this->emergencyDetected) {
|
||||||
|
// flip flag
|
||||||
|
this->emergencyDetected = false;
|
||||||
|
this->emergencyFlipTime = millis();
|
||||||
|
|
||||||
|
} else if (!this->emergencyDetected && vars.states.emergency) {
|
||||||
|
// disable emergency
|
||||||
|
if (millis() - this->emergencyFlipTime > 30000) {
|
||||||
|
vars.states.emergency = false;
|
||||||
|
Log.sinfoln(FPSTR(L_MAIN), F("Emergency mode disabled"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void ledStatus() {
|
void ledStatus() {
|
||||||
uint8_t errors[4];
|
uint8_t errors[4];
|
||||||
uint8_t errCount = 0;
|
uint8_t errCount = 0;
|
||||||
|
|||||||
@@ -198,17 +198,6 @@ protected:
|
|||||||
this->onConnect();
|
this->onConnect();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (settings.emergency.enable && settings.emergency.onMqttFault) {
|
|
||||||
if (!this->connected && !vars.states.emergency && millis() - this->disconnectedTime > (settings.emergency.tresholdTime * 1000)) {
|
|
||||||
vars.states.emergency = true;
|
|
||||||
Log.sinfoln(FPSTR(L_MQTT), F("Emergency mode enabled"));
|
|
||||||
|
|
||||||
} else if (this->connected && vars.states.emergency && millis() - this->connectedTime > 10000) {
|
|
||||||
vars.states.emergency = false;
|
|
||||||
Log.sinfoln(FPSTR(L_MQTT), F("Emergency mode disabled"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this->connected) {
|
if (!this->connected) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -198,6 +198,10 @@ protected:
|
|||||||
etRegulator.Kt = 0;
|
etRegulator.Kt = 0;
|
||||||
etRegulator.indoorTemp = 0;
|
etRegulator.indoorTemp = 0;
|
||||||
|
|
||||||
|
} else if ((settings.sensors.indoor.type == SensorType::DS18B20 || settings.sensors.indoor.type == SensorType::BLUETOOTH) && !vars.sensors.indoor.connected) {
|
||||||
|
etRegulator.Kt = 0;
|
||||||
|
etRegulator.indoorTemp = 0;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
etRegulator.Kt = settings.equitherm.t_factor;
|
etRegulator.Kt = settings.equitherm.t_factor;
|
||||||
etRegulator.indoorTemp = indoorTemp;
|
etRegulator.indoorTemp = indoorTemp;
|
||||||
|
|||||||
@@ -35,19 +35,18 @@ protected:
|
|||||||
unsigned long initOutdoorSensorTime = 0;
|
unsigned long initOutdoorSensorTime = 0;
|
||||||
unsigned long startOutdoorConversionTime = 0;
|
unsigned long startOutdoorConversionTime = 0;
|
||||||
float filteredOutdoorTemp = 0;
|
float filteredOutdoorTemp = 0;
|
||||||
bool emptyOutdoorTemp = true;
|
float prevFilteredOutdoorTemp = 0;
|
||||||
|
|
||||||
bool initIndoorSensor = false;
|
bool initIndoorSensor = false;
|
||||||
unsigned long initIndoorSensorTime = 0;
|
unsigned long initIndoorSensorTime = 0;
|
||||||
unsigned long startIndoorConversionTime = 0;
|
unsigned long startIndoorConversionTime = 0;
|
||||||
float filteredIndoorTemp = 0;
|
float filteredIndoorTemp = 0;
|
||||||
bool emptyIndoorTemp = true;
|
float prevFilteredIndoorTemp = 0;
|
||||||
|
|
||||||
#if defined(ARDUINO_ARCH_ESP32)
|
#if defined(ARDUINO_ARCH_ESP32)
|
||||||
#if USE_BLE
|
#if USE_BLE
|
||||||
BLEClient* pBleClient = nullptr;
|
unsigned long outdoorConnectedTime = 0;
|
||||||
bool initBleSensor = false;
|
unsigned long indoorConnectedTime = 0;
|
||||||
bool initBleNotify = false;
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
const char* getTaskName() override {
|
const char* getTaskName() override {
|
||||||
@@ -69,26 +68,62 @@ protected:
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
void loop() {
|
void loop() {
|
||||||
bool indoorTempUpdated = false;
|
|
||||||
bool outdoorTempUpdated = false;
|
|
||||||
|
|
||||||
if (settings.sensors.outdoor.type == SensorType::DS18B20 && GPIO_IS_VALID(settings.sensors.outdoor.gpio)) {
|
|
||||||
outdoorTemperatureSensor();
|
|
||||||
outdoorTempUpdated = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (settings.sensors.indoor.type == SensorType::DS18B20 && GPIO_IS_VALID(settings.sensors.indoor.gpio)) {
|
|
||||||
indoorTemperatureSensor();
|
|
||||||
indoorTempUpdated = true;
|
|
||||||
}
|
|
||||||
#if USE_BLE
|
#if USE_BLE
|
||||||
else if (settings.sensors.indoor.type == SensorType::BLUETOOTH) {
|
if (!NimBLEDevice::getInitialized() && millis() > 5000) {
|
||||||
indoorTemperatureBluetoothSensor();
|
Log.sinfoln(FPSTR(L_SENSORS_BLE), F("Init BLE"));
|
||||||
indoorTempUpdated = true;
|
BLEDevice::init("");
|
||||||
|
NimBLEDevice::setPower(ESP_PWR_LVL_P9);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (outdoorTempUpdated) {
|
if (settings.sensors.outdoor.type == SensorType::DS18B20 && GPIO_IS_VALID(settings.sensors.outdoor.gpio)) {
|
||||||
|
outdoorDallasSensor();
|
||||||
|
}
|
||||||
|
#if USE_BLE
|
||||||
|
else if (settings.sensors.outdoor.type == SensorType::BLUETOOTH) {
|
||||||
|
bool connected = this->bluetoothSensor(
|
||||||
|
BLEAddress(settings.sensors.outdoor.bleAddress),
|
||||||
|
&vars.sensors.outdoor.rssi,
|
||||||
|
&this->filteredOutdoorTemp,
|
||||||
|
&vars.sensors.outdoor.humidity,
|
||||||
|
&vars.sensors.outdoor.battery
|
||||||
|
);
|
||||||
|
|
||||||
|
if (connected) {
|
||||||
|
this->outdoorConnectedTime = millis();
|
||||||
|
vars.sensors.outdoor.connected = true;
|
||||||
|
|
||||||
|
} else if (millis() - this->outdoorConnectedTime > 60000) {
|
||||||
|
vars.sensors.outdoor.connected = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (settings.sensors.indoor.type == SensorType::DS18B20 && GPIO_IS_VALID(settings.sensors.indoor.gpio)) {
|
||||||
|
indoorDallasSensor();
|
||||||
|
}
|
||||||
|
#if USE_BLE
|
||||||
|
else if (settings.sensors.indoor.type == SensorType::BLUETOOTH) {
|
||||||
|
bool connected = this->bluetoothSensor(
|
||||||
|
BLEAddress(settings.sensors.indoor.bleAddress),
|
||||||
|
&vars.sensors.indoor.rssi,
|
||||||
|
&this->filteredIndoorTemp,
|
||||||
|
&vars.sensors.indoor.humidity,
|
||||||
|
&vars.sensors.indoor.battery
|
||||||
|
);
|
||||||
|
|
||||||
|
if (connected) {
|
||||||
|
this->indoorConnectedTime = millis();
|
||||||
|
vars.sensors.indoor.connected = true;
|
||||||
|
|
||||||
|
} else if (millis() - this->indoorConnectedTime > 60000) {
|
||||||
|
vars.sensors.indoor.connected = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// convert
|
||||||
|
if (fabs(this->prevFilteredOutdoorTemp - this->filteredOutdoorTemp) >= 0.1f) {
|
||||||
float newTemp = settings.sensors.outdoor.offset;
|
float newTemp = settings.sensors.outdoor.offset;
|
||||||
if (settings.system.unitSystem == UnitSystem::METRIC) {
|
if (settings.system.unitSystem == UnitSystem::METRIC) {
|
||||||
newTemp += this->filteredOutdoorTemp;
|
newTemp += this->filteredOutdoorTemp;
|
||||||
@@ -101,9 +136,11 @@ protected:
|
|||||||
vars.temperatures.outdoor = newTemp;
|
vars.temperatures.outdoor = newTemp;
|
||||||
Log.sinfoln(FPSTR(L_SENSORS_OUTDOOR), F("New temp: %f"), vars.temperatures.outdoor);
|
Log.sinfoln(FPSTR(L_SENSORS_OUTDOOR), F("New temp: %f"), vars.temperatures.outdoor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this->prevFilteredOutdoorTemp = this->filteredOutdoorTemp;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (indoorTempUpdated) {
|
if (fabs(this->prevFilteredIndoorTemp - this->filteredIndoorTemp) > 0.1f) {
|
||||||
float newTemp = settings.sensors.indoor.offset;
|
float newTemp = settings.sensors.indoor.offset;
|
||||||
if (settings.system.unitSystem == UnitSystem::METRIC) {
|
if (settings.system.unitSystem == UnitSystem::METRIC) {
|
||||||
newTemp += this->filteredIndoorTemp;
|
newTemp += this->filteredIndoorTemp;
|
||||||
@@ -116,127 +153,378 @@ protected:
|
|||||||
vars.temperatures.indoor = newTemp;
|
vars.temperatures.indoor = newTemp;
|
||||||
Log.sinfoln(FPSTR(L_SENSORS_INDOOR), F("New temp: %f"), vars.temperatures.indoor);
|
Log.sinfoln(FPSTR(L_SENSORS_INDOOR), F("New temp: %f"), vars.temperatures.indoor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this->prevFilteredIndoorTemp = this->filteredIndoorTemp;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#if USE_BLE
|
#if USE_BLE
|
||||||
void indoorTemperatureBluetoothSensor() {
|
bool bluetoothSensor(const BLEAddress& address, int8_t* const pRssi, float* const pTemperature, float* const pHumidity = nullptr, float* const pBattery = nullptr) {
|
||||||
static bool initBleNotify = false;
|
if (!NimBLEDevice::getInitialized()) {
|
||||||
if (!initBleSensor && millis() > 5000) {
|
return false;
|
||||||
Log.sinfoln(FPSTR(L_SENSORS_BLE), F("Init BLE"));
|
|
||||||
BLEDevice::init("");
|
|
||||||
|
|
||||||
pBleClient = BLEDevice::createClient();
|
|
||||||
pBleClient->setConnectTimeout(5);
|
|
||||||
|
|
||||||
initBleSensor = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!initBleSensor || pBleClient->isConnected()) {
|
NimBLEClient* pClient = nullptr;
|
||||||
return;
|
pClient = NimBLEDevice::getClientByPeerAddress(address);
|
||||||
}
|
|
||||||
|
|
||||||
// Reset init notify flag
|
|
||||||
this->initBleNotify = false;
|
|
||||||
|
|
||||||
// Connect to the remote BLE Server.
|
if (pClient == nullptr) {
|
||||||
BLEAddress bleServerAddress(settings.sensors.indoor.bleAddress);
|
pClient = NimBLEDevice::getDisconnectedClient();
|
||||||
if (!pBleClient->connect(bleServerAddress)) {
|
|
||||||
Log.swarningln(FPSTR(L_SENSORS_BLE), "Failed connecting to device at %s", bleServerAddress.toString().c_str());
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.sinfoln(FPSTR(L_SENSORS_BLE), "Connected to device at %s", bleServerAddress.toString().c_str());
|
if (pClient == nullptr) {
|
||||||
|
if (NimBLEDevice::getClientListSize() >= NIMBLE_MAX_CONNECTIONS) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
NimBLEUUID serviceUUID((uint16_t) 0x181AU);
|
pClient = NimBLEDevice::createClient();
|
||||||
BLERemoteService* pRemoteService = pBleClient->getService(serviceUUID);
|
pClient->setConnectTimeout(5);
|
||||||
if (!pRemoteService) {
|
|
||||||
Log.straceln(FPSTR(L_SENSORS_BLE), F("Failed to find service UUID: %s"), serviceUUID.toString().c_str());
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.straceln(FPSTR(L_SENSORS_BLE), F("Found service UUID: %s"), serviceUUID.toString().c_str());
|
if(pClient->isConnected()) {
|
||||||
|
*pRssi = pClient->getRssi();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// 0x2A6E - Notify temperature x0.01C (pvvx)
|
if (!pClient->connect(address)) {
|
||||||
if (!this->initBleNotify) {
|
Log.swarningln(FPSTR(L_SENSORS_BLE), "Device %s: failed connecting", address.toString().c_str());
|
||||||
NimBLEUUID charUUID((uint16_t) 0x2A6E);
|
|
||||||
BLERemoteCharacteristic* pRemoteCharacteristic = pRemoteService->getCharacteristic(charUUID);
|
|
||||||
if (pRemoteCharacteristic && pRemoteCharacteristic->canNotify()) {
|
|
||||||
Log.straceln(FPSTR(L_SENSORS_BLE), F("Found characteristic UUID: %s"), charUUID.toString().c_str());
|
|
||||||
|
|
||||||
this->initBleNotify = pRemoteCharacteristic->subscribe(true, [this](NimBLERemoteCharacteristic*, uint8_t* pData, size_t length, bool isNotify) {
|
NimBLEDevice::deleteClient(pClient);
|
||||||
if (length != 2) {
|
return false;
|
||||||
Log.swarningln(FPSTR(L_SENSORS_BLE), F("Invalid notification data"));
|
}
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
float rawTemp = ((pData[0] | (pData[1] << 8)) * 0.01f);
|
Log.sinfoln(FPSTR(L_SENSORS_BLE), "Device %s: connected", address.toString().c_str());
|
||||||
Log.straceln(FPSTR(L_SENSORS_INDOOR), F("Raw temp: %f"), rawTemp);
|
NimBLERemoteService* pService = nullptr;
|
||||||
|
NimBLERemoteCharacteristic* pChar = nullptr;
|
||||||
|
|
||||||
if (this->emptyIndoorTemp) {
|
// ENV Service (0x181A)
|
||||||
this->filteredIndoorTemp = rawTemp;
|
pService = pClient->getService(NimBLEUUID((uint16_t) 0x181AU));
|
||||||
this->emptyIndoorTemp = false;
|
if (!pService) {
|
||||||
|
Log.straceln(
|
||||||
|
FPSTR(L_SENSORS_BLE),
|
||||||
|
F("Device %s: failed to find env service (%s)"),
|
||||||
|
address.toString().c_str(),
|
||||||
|
pService->getUUID().toString().c_str()
|
||||||
|
);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
Log.straceln(
|
||||||
|
FPSTR(L_SENSORS_BLE),
|
||||||
|
F("Device %s: found env service (%s)"),
|
||||||
|
address.toString().c_str(),
|
||||||
|
pService->getUUID().toString().c_str()
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
// 0x2A6E - Notify temperature x0.01C (pvvx)
|
||||||
|
bool tempNotifyCreated = false;
|
||||||
|
if (!tempNotifyCreated) {
|
||||||
|
pChar = pService->getCharacteristic(NimBLEUUID((uint16_t) 0x2A6E));
|
||||||
|
|
||||||
|
if (pChar && pChar->canNotify()) {
|
||||||
|
Log.straceln(
|
||||||
|
FPSTR(L_SENSORS_BLE),
|
||||||
|
F("Device %s: found temperature char (%s) in env service"),
|
||||||
|
address.toString().c_str(),
|
||||||
|
pChar->getUUID().toString().c_str()
|
||||||
|
);
|
||||||
|
|
||||||
|
tempNotifyCreated = pChar->subscribe(true, [pTemperature](NimBLERemoteCharacteristic* pChar, uint8_t* pData, size_t length, bool isNotify) {
|
||||||
|
NimBLEClient* pClient = pChar->getRemoteService()->getClient();
|
||||||
|
|
||||||
|
if (length != 2) {
|
||||||
|
Log.swarningln(
|
||||||
|
FPSTR(L_SENSORS_BLE),
|
||||||
|
F("Device %s: invalid notification data at temperature char (%s)"),
|
||||||
|
pClient->getPeerAddress().toString().c_str(),
|
||||||
|
pChar->getUUID().toString().c_str()
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
float rawTemp = ((pData[0] | (pData[1] << 8)) * 0.01f);
|
||||||
|
Log.straceln(
|
||||||
|
FPSTR(L_SENSORS_INDOOR),
|
||||||
|
F("Device %s: raw temp %f"),
|
||||||
|
pClient->getPeerAddress().toString().c_str(),
|
||||||
|
rawTemp
|
||||||
|
);
|
||||||
|
|
||||||
|
if (fabs(*pTemperature) < 0.1f) {
|
||||||
|
*pTemperature = rawTemp;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
*pTemperature += (rawTemp - (*pTemperature)) * EXT_SENSORS_FILTER_K;
|
||||||
|
}
|
||||||
|
|
||||||
|
*pTemperature = floor((*pTemperature) * 100) / 100;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (tempNotifyCreated) {
|
||||||
|
Log.straceln(
|
||||||
|
FPSTR(L_SENSORS_BLE),
|
||||||
|
F("Device %s: subscribed to temperature char (%s) in env service"),
|
||||||
|
address.toString().c_str(),
|
||||||
|
pChar->getUUID().toString().c_str()
|
||||||
|
);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
this->filteredIndoorTemp += (rawTemp - this->filteredIndoorTemp) * EXT_SENSORS_FILTER_K;
|
Log.swarningln(
|
||||||
|
FPSTR(L_SENSORS_BLE),
|
||||||
|
F("Device %s: failed to subscribe to temperature char (%s) in env service"),
|
||||||
|
address.toString().c_str(),
|
||||||
|
pChar->getUUID().toString().c_str()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this->filteredIndoorTemp = floor(this->filteredIndoorTemp * 100) / 100;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (this->initBleNotify) {
|
// 0x2A1F - Notify temperature x0.1C (atc1441/pvvx)
|
||||||
Log.straceln(FPSTR(L_SENSORS_BLE), F("Subscribed to characteristic UUID: %s"), charUUID.toString().c_str());
|
if (!tempNotifyCreated) {
|
||||||
|
pChar = pService->getCharacteristic(NimBLEUUID((uint16_t) 0x2A1F));
|
||||||
|
|
||||||
} else {
|
if (pChar && pChar->canNotify()) {
|
||||||
Log.swarningln(FPSTR(L_SENSORS_BLE), F("Failed to subscribe to characteristic UUID: %s"), charUUID.toString().c_str());
|
Log.straceln(
|
||||||
|
FPSTR(L_SENSORS_BLE),
|
||||||
|
F("Device %s: found temperature char (%s) in env service"),
|
||||||
|
address.toString().c_str(),
|
||||||
|
pChar->getUUID().toString().c_str()
|
||||||
|
);
|
||||||
|
|
||||||
|
tempNotifyCreated = pChar->subscribe(true, [pTemperature](NimBLERemoteCharacteristic* pChar, uint8_t* pData, size_t length, bool isNotify) {
|
||||||
|
NimBLEClient* pClient = pChar->getRemoteService()->getClient();
|
||||||
|
|
||||||
|
if (length != 2) {
|
||||||
|
Log.swarningln(
|
||||||
|
FPSTR(L_SENSORS_BLE),
|
||||||
|
F("Device %s: invalid notification data at temperature char (%s)"),
|
||||||
|
pClient->getPeerAddress().toString().c_str(),
|
||||||
|
pChar->getUUID().toString().c_str()
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
float rawTemp = ((pData[0] | (pData[1] << 8)) * 0.1f);
|
||||||
|
Log.straceln(
|
||||||
|
FPSTR(L_SENSORS_INDOOR),
|
||||||
|
F("Device %s: raw temp %f"),
|
||||||
|
pClient->getPeerAddress().toString().c_str(),
|
||||||
|
rawTemp
|
||||||
|
);
|
||||||
|
|
||||||
|
if (fabs(*pTemperature) < 0.1f) {
|
||||||
|
*pTemperature = rawTemp;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
*pTemperature += (rawTemp - (*pTemperature)) * EXT_SENSORS_FILTER_K;
|
||||||
|
}
|
||||||
|
|
||||||
|
*pTemperature = floor((*pTemperature) * 100) / 100;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (tempNotifyCreated) {
|
||||||
|
Log.straceln(
|
||||||
|
FPSTR(L_SENSORS_BLE),
|
||||||
|
F("Device %s: subscribed to temperature char (%s) in env service"),
|
||||||
|
address.toString().c_str(),
|
||||||
|
pChar->getUUID().toString().c_str()
|
||||||
|
);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
Log.swarningln(
|
||||||
|
FPSTR(L_SENSORS_BLE),
|
||||||
|
F("Device %s: failed to subscribe to temperature char (%s) in env service"),
|
||||||
|
address.toString().c_str(),
|
||||||
|
pChar->getUUID().toString().c_str()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!tempNotifyCreated) {
|
||||||
|
Log.swarningln(
|
||||||
|
FPSTR(L_SENSORS_BLE),
|
||||||
|
F("Device %s: not found supported temperature chars in env service"),
|
||||||
|
address.toString().c_str()
|
||||||
|
);
|
||||||
|
|
||||||
|
pClient->disconnect();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 0x2A6F - Notify about humidity x0.01% (pvvx)
|
||||||
|
if (pHumidity != nullptr) {
|
||||||
|
bool humidityNotifyCreated = false;
|
||||||
|
if (!humidityNotifyCreated) {
|
||||||
|
pChar = pService->getCharacteristic(NimBLEUUID((uint16_t) 0x2A6F));
|
||||||
|
|
||||||
|
if (pChar && pChar->canNotify()) {
|
||||||
|
Log.straceln(
|
||||||
|
FPSTR(L_SENSORS_BLE),
|
||||||
|
F("Device %s: found humidity char (%s) in env service"),
|
||||||
|
address.toString().c_str(),
|
||||||
|
pChar->getUUID().toString().c_str()
|
||||||
|
);
|
||||||
|
|
||||||
|
humidityNotifyCreated = pChar->subscribe(true, [pHumidity](NimBLERemoteCharacteristic* pChar, uint8_t* pData, size_t length, bool isNotify) {
|
||||||
|
NimBLEClient* pClient = pChar->getRemoteService()->getClient();
|
||||||
|
|
||||||
|
if (length != 2) {
|
||||||
|
Log.swarningln(
|
||||||
|
FPSTR(L_SENSORS_BLE),
|
||||||
|
F("Device %s: invalid notification data at humidity char (%s)"),
|
||||||
|
pClient->getPeerAddress().toString().c_str(),
|
||||||
|
pChar->getUUID().toString().c_str()
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
float rawHumidity = ((pData[0] | (pData[1] << 8)) * 0.01f);
|
||||||
|
Log.straceln(
|
||||||
|
FPSTR(L_SENSORS_INDOOR),
|
||||||
|
F("Device %s: raw humidity %f"),
|
||||||
|
pClient->getPeerAddress().toString().c_str(),
|
||||||
|
rawHumidity
|
||||||
|
);
|
||||||
|
|
||||||
|
if (fabs(*pHumidity) < 0.1f) {
|
||||||
|
*pHumidity = rawHumidity;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
*pHumidity += (rawHumidity - (*pHumidity)) * EXT_SENSORS_FILTER_K;
|
||||||
|
}
|
||||||
|
|
||||||
|
*pHumidity = floor((*pHumidity) * 100) / 100;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (humidityNotifyCreated) {
|
||||||
|
Log.straceln(
|
||||||
|
FPSTR(L_SENSORS_BLE),
|
||||||
|
F("Device %s: subscribed to humidity char (%s) in env service"),
|
||||||
|
address.toString().c_str(),
|
||||||
|
pChar->getUUID().toString().c_str()
|
||||||
|
);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
Log.swarningln(
|
||||||
|
FPSTR(L_SENSORS_BLE),
|
||||||
|
F("Device %s: failed to subscribe to humidity char (%s) in env service"),
|
||||||
|
address.toString().c_str(),
|
||||||
|
pChar->getUUID().toString().c_str()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!humidityNotifyCreated) {
|
||||||
|
Log.swarningln(
|
||||||
|
FPSTR(L_SENSORS_BLE),
|
||||||
|
F("Device %s: not found supported humidity chars in env service"),
|
||||||
|
address.toString().c_str()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 0x2A1F - Notify temperature x0.1C (atc1441/pvvx)
|
|
||||||
if (!this->initBleNotify) {
|
|
||||||
NimBLEUUID charUUID((uint16_t) 0x2A1F);
|
|
||||||
BLERemoteCharacteristic* pRemoteCharacteristic = pRemoteService->getCharacteristic(charUUID);
|
|
||||||
if (pRemoteCharacteristic && pRemoteCharacteristic->canNotify()) {
|
|
||||||
Log.straceln(FPSTR(L_SENSORS_BLE), F("Found characteristic UUID: %s"), charUUID.toString().c_str());
|
|
||||||
|
|
||||||
this->initBleNotify = pRemoteCharacteristic->subscribe(true, [this](NimBLERemoteCharacteristic*, uint8_t* pData, size_t length, bool isNotify) {
|
// Battery Service (0x180F)
|
||||||
if (length != 2) {
|
if (pBattery != nullptr) {
|
||||||
Log.swarningln(FPSTR(L_SENSORS_BLE), F("Invalid notification data"));
|
pService = pClient->getService(NimBLEUUID((uint16_t) 0x180F));
|
||||||
return;
|
if (!pService) {
|
||||||
|
Log.straceln(
|
||||||
|
FPSTR(L_SENSORS_BLE),
|
||||||
|
F("Device %s: failed to find battery service (%s)"),
|
||||||
|
address.toString().c_str(),
|
||||||
|
pService->getUUID().toString().c_str()
|
||||||
|
);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
Log.straceln(
|
||||||
|
FPSTR(L_SENSORS_BLE),
|
||||||
|
F("Device %s: found battery service (%s)"),
|
||||||
|
address.toString().c_str(),
|
||||||
|
pService->getUUID().toString().c_str()
|
||||||
|
);
|
||||||
|
|
||||||
|
// 0x2A19 - Notify the battery charge level 0..99% (pvvx)
|
||||||
|
bool batteryNotifyCreated = false;
|
||||||
|
if (!batteryNotifyCreated) {
|
||||||
|
pChar = pService->getCharacteristic(NimBLEUUID((uint16_t) 0x2A19));
|
||||||
|
|
||||||
|
if (pChar && pChar->canNotify()) {
|
||||||
|
Log.straceln(
|
||||||
|
FPSTR(L_SENSORS_BLE),
|
||||||
|
F("Device %s: found battery char (%s) in battery service"),
|
||||||
|
address.toString().c_str(),
|
||||||
|
pChar->getUUID().toString().c_str()
|
||||||
|
);
|
||||||
|
|
||||||
|
batteryNotifyCreated = pChar->subscribe(true, [pBattery](NimBLERemoteCharacteristic* pChar, uint8_t* pData, size_t length, bool isNotify) {
|
||||||
|
NimBLEClient* pClient = pChar->getRemoteService()->getClient();
|
||||||
|
|
||||||
|
if (length != 1) {
|
||||||
|
Log.swarningln(
|
||||||
|
FPSTR(L_SENSORS_BLE),
|
||||||
|
F("Device %s: invalid notification data at battery char (%s)"),
|
||||||
|
pClient->getPeerAddress().toString().c_str(),
|
||||||
|
pChar->getUUID().toString().c_str()
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t rawBattery = pData[0];
|
||||||
|
Log.straceln(
|
||||||
|
FPSTR(L_SENSORS_INDOOR),
|
||||||
|
F("Device %s: raw battery %hhu"),
|
||||||
|
pClient->getPeerAddress().toString().c_str(),
|
||||||
|
rawBattery
|
||||||
|
);
|
||||||
|
|
||||||
|
if (fabs(*pBattery) < 0.1f) {
|
||||||
|
*pBattery = rawBattery;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
*pBattery += (rawBattery - (*pBattery)) * EXT_SENSORS_FILTER_K;
|
||||||
|
}
|
||||||
|
|
||||||
|
*pBattery = floor((*pBattery) * 100) / 100;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (batteryNotifyCreated) {
|
||||||
|
Log.straceln(
|
||||||
|
FPSTR(L_SENSORS_BLE),
|
||||||
|
F("Device %s: subscribed to battery char (%s) in battery service"),
|
||||||
|
address.toString().c_str(),
|
||||||
|
pChar->getUUID().toString().c_str()
|
||||||
|
);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
Log.swarningln(
|
||||||
|
FPSTR(L_SENSORS_BLE),
|
||||||
|
F("Device %s: failed to subscribe to battery char (%s) in battery service"),
|
||||||
|
address.toString().c_str(),
|
||||||
|
pChar->getUUID().toString().c_str()
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
float rawTemp = ((pData[0] | (pData[1] << 8)) * 0.1);
|
if (!batteryNotifyCreated) {
|
||||||
Log.straceln(FPSTR(L_SENSORS_INDOOR), F("Raw temp: %f"), rawTemp);
|
Log.swarningln(
|
||||||
|
FPSTR(L_SENSORS_BLE),
|
||||||
if (this->emptyIndoorTemp) {
|
F("Device %s: not found supported battery chars in battery service"),
|
||||||
this->filteredIndoorTemp = rawTemp;
|
address.toString().c_str()
|
||||||
this->emptyIndoorTemp = false;
|
);
|
||||||
|
|
||||||
} else {
|
|
||||||
this->filteredIndoorTemp += (rawTemp - this->filteredIndoorTemp) * EXT_SENSORS_FILTER_K;
|
|
||||||
}
|
|
||||||
|
|
||||||
this->filteredIndoorTemp = floor(this->filteredIndoorTemp * 100) / 100;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (this->initBleNotify) {
|
|
||||||
Log.straceln(FPSTR(L_SENSORS_BLE), F("Subscribed to characteristic UUID: %s"), charUUID.toString().c_str());
|
|
||||||
|
|
||||||
} else {
|
|
||||||
Log.swarningln(FPSTR(L_SENSORS_BLE), F("Failed to subscribe to characteristic UUID: %s"), charUUID.toString().c_str());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this->initBleNotify) {
|
return true;
|
||||||
Log.swarningln(FPSTR(L_SENSORS_BLE), F("Not found supported characteristics"));
|
|
||||||
pBleClient->disconnect();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void outdoorTemperatureSensor() {
|
void outdoorDallasSensor() {
|
||||||
if (!this->initOutdoorSensor) {
|
if (!this->initOutdoorSensor) {
|
||||||
if (this->initOutdoorSensorTime && millis() - this->initOutdoorSensorTime < EXT_SENSORS_INTERVAL * 10) {
|
if (this->initOutdoorSensorTime && millis() - this->initOutdoorSensorTime < EXT_SENSORS_INTERVAL * 10) {
|
||||||
return;
|
return;
|
||||||
@@ -265,6 +553,10 @@ protected:
|
|||||||
Log.sinfoln(FPSTR(L_SENSORS_OUTDOOR), F("Started"));
|
Log.sinfoln(FPSTR(L_SENSORS_OUTDOOR), F("Started"));
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
if (vars.sensors.outdoor.connected) {
|
||||||
|
vars.sensors.outdoor.connected = false;
|
||||||
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -294,9 +586,12 @@ protected:
|
|||||||
} else {
|
} else {
|
||||||
Log.straceln(FPSTR(L_SENSORS_OUTDOOR), F("Raw temp: %f"), rawTemp);
|
Log.straceln(FPSTR(L_SENSORS_OUTDOOR), F("Raw temp: %f"), rawTemp);
|
||||||
|
|
||||||
if (this->emptyOutdoorTemp) {
|
if (!vars.sensors.outdoor.connected) {
|
||||||
|
vars.sensors.outdoor.connected = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fabs(this->filteredOutdoorTemp) < 0.1f) {
|
||||||
this->filteredOutdoorTemp = rawTemp;
|
this->filteredOutdoorTemp = rawTemp;
|
||||||
this->emptyOutdoorTemp = false;
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
this->filteredOutdoorTemp += (rawTemp - this->filteredOutdoorTemp) * EXT_SENSORS_FILTER_K;
|
this->filteredOutdoorTemp += (rawTemp - this->filteredOutdoorTemp) * EXT_SENSORS_FILTER_K;
|
||||||
@@ -308,7 +603,7 @@ protected:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void indoorTemperatureSensor() {
|
void indoorDallasSensor() {
|
||||||
if (!this->initIndoorSensor) {
|
if (!this->initIndoorSensor) {
|
||||||
if (this->initIndoorSensorTime && millis() - this->initIndoorSensorTime < EXT_SENSORS_INTERVAL * 10) {
|
if (this->initIndoorSensorTime && millis() - this->initIndoorSensorTime < EXT_SENSORS_INTERVAL * 10) {
|
||||||
return;
|
return;
|
||||||
@@ -337,6 +632,10 @@ protected:
|
|||||||
Log.sinfoln(FPSTR(L_SENSORS_INDOOR), F("Started"));
|
Log.sinfoln(FPSTR(L_SENSORS_INDOOR), F("Started"));
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
if (vars.sensors.indoor.connected) {
|
||||||
|
vars.sensors.indoor.connected = false;
|
||||||
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -366,9 +665,12 @@ protected:
|
|||||||
} else {
|
} else {
|
||||||
Log.straceln(FPSTR(L_SENSORS_INDOOR), F("Raw temp: %f"), rawTemp);
|
Log.straceln(FPSTR(L_SENSORS_INDOOR), F("Raw temp: %f"), rawTemp);
|
||||||
|
|
||||||
if (this->emptyIndoorTemp) {
|
if (!vars.sensors.indoor.connected) {
|
||||||
|
vars.sensors.indoor.connected = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fabs(this->filteredIndoorTemp) < 0.1f) {
|
||||||
this->filteredIndoorTemp = rawTemp;
|
this->filteredIndoorTemp = rawTemp;
|
||||||
this->emptyIndoorTemp = false;
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
this->filteredIndoorTemp += (rawTemp - this->filteredIndoorTemp) * EXT_SENSORS_FILTER_K;
|
this->filteredIndoorTemp += (rawTemp - this->filteredIndoorTemp) * EXT_SENSORS_FILTER_K;
|
||||||
|
|||||||
@@ -77,13 +77,15 @@ struct Settings {
|
|||||||
} mqtt;
|
} mqtt;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
bool enable = true;
|
bool enable = false;
|
||||||
float target = DEFAULT_HEATING_TARGET_TEMP;
|
float target = DEFAULT_HEATING_TARGET_TEMP;
|
||||||
unsigned short tresholdTime = 120;
|
unsigned short tresholdTime = 120;
|
||||||
bool useEquitherm = false;
|
bool useEquitherm = false;
|
||||||
bool usePid = false;
|
bool usePid = false;
|
||||||
bool onNetworkFault = true;
|
bool onNetworkFault = true;
|
||||||
bool onMqttFault = true;
|
bool onMqttFault = true;
|
||||||
|
bool onIndoorSensorDisconnect = false;
|
||||||
|
bool onOutdoorSensorDisconnect = false;
|
||||||
} emergency;
|
} emergency;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
@@ -124,6 +126,7 @@ struct Settings {
|
|||||||
struct {
|
struct {
|
||||||
SensorType type = SensorType::BOILER;
|
SensorType type = SensorType::BOILER;
|
||||||
byte gpio = DEFAULT_SENSOR_OUTDOOR_GPIO;
|
byte gpio = DEFAULT_SENSOR_OUTDOOR_GPIO;
|
||||||
|
uint8_t bleAddress[6] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
||||||
float offset = 0.0f;
|
float offset = 0.0f;
|
||||||
} outdoor;
|
} outdoor;
|
||||||
|
|
||||||
@@ -165,6 +168,20 @@ struct Variables {
|
|||||||
float dhwFlowRate = 0.0f;
|
float dhwFlowRate = 0.0f;
|
||||||
byte faultCode = 0;
|
byte faultCode = 0;
|
||||||
int8_t rssi = 0;
|
int8_t rssi = 0;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
bool connected = false;
|
||||||
|
int8_t rssi = 0;
|
||||||
|
float battery = 0.0f;
|
||||||
|
float humidity = 0.0f;
|
||||||
|
} outdoor;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
bool connected = false;
|
||||||
|
int8_t rssi = 0;
|
||||||
|
float battery = 0.0f;
|
||||||
|
float humidity = 0.0f;
|
||||||
|
} indoor;
|
||||||
} sensors;
|
} sensors;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
|
|||||||
74
src/utils.h
74
src/utils.h
@@ -371,6 +371,8 @@ void settingsToJson(const Settings& src, JsonVariant dst, bool safe = false) {
|
|||||||
dst["emergency"]["usePid"] = src.emergency.usePid;
|
dst["emergency"]["usePid"] = src.emergency.usePid;
|
||||||
dst["emergency"]["onNetworkFault"] = src.emergency.onNetworkFault;
|
dst["emergency"]["onNetworkFault"] = src.emergency.onNetworkFault;
|
||||||
dst["emergency"]["onMqttFault"] = src.emergency.onMqttFault;
|
dst["emergency"]["onMqttFault"] = src.emergency.onMqttFault;
|
||||||
|
dst["emergency"]["onIndoorSensorDisconnect"] = src.emergency.onIndoorSensorDisconnect;
|
||||||
|
dst["emergency"]["onOutdoorSensorDisconnect"] = src.emergency.onOutdoorSensorDisconnect;
|
||||||
}
|
}
|
||||||
|
|
||||||
dst["heating"]["enable"] = src.heating.enable;
|
dst["heating"]["enable"] = src.heating.enable;
|
||||||
@@ -401,12 +403,24 @@ void settingsToJson(const Settings& src, JsonVariant dst, bool safe = false) {
|
|||||||
|
|
||||||
dst["sensors"]["outdoor"]["type"] = static_cast<byte>(src.sensors.outdoor.type);
|
dst["sensors"]["outdoor"]["type"] = static_cast<byte>(src.sensors.outdoor.type);
|
||||||
dst["sensors"]["outdoor"]["gpio"] = src.sensors.outdoor.gpio;
|
dst["sensors"]["outdoor"]["gpio"] = src.sensors.outdoor.gpio;
|
||||||
|
|
||||||
|
char bleAddress[18];
|
||||||
|
sprintf(
|
||||||
|
bleAddress,
|
||||||
|
"%02x:%02x:%02x:%02x:%02x:%02x",
|
||||||
|
src.sensors.outdoor.bleAddress[0],
|
||||||
|
src.sensors.outdoor.bleAddress[1],
|
||||||
|
src.sensors.outdoor.bleAddress[2],
|
||||||
|
src.sensors.outdoor.bleAddress[3],
|
||||||
|
src.sensors.outdoor.bleAddress[4],
|
||||||
|
src.sensors.outdoor.bleAddress[5]
|
||||||
|
);
|
||||||
|
dst["sensors"]["outdoor"]["bleAddress"] = String(bleAddress);
|
||||||
dst["sensors"]["outdoor"]["offset"] = roundd(src.sensors.outdoor.offset, 2);
|
dst["sensors"]["outdoor"]["offset"] = roundd(src.sensors.outdoor.offset, 2);
|
||||||
|
|
||||||
dst["sensors"]["indoor"]["type"] = static_cast<byte>(src.sensors.indoor.type);
|
dst["sensors"]["indoor"]["type"] = static_cast<byte>(src.sensors.indoor.type);
|
||||||
dst["sensors"]["indoor"]["gpio"] = src.sensors.indoor.gpio;
|
dst["sensors"]["indoor"]["gpio"] = src.sensors.indoor.gpio;
|
||||||
|
|
||||||
char bleAddress[18];
|
|
||||||
sprintf(
|
sprintf(
|
||||||
bleAddress,
|
bleAddress,
|
||||||
"%02x:%02x:%02x:%02x:%02x:%02x",
|
"%02x:%02x:%02x:%02x:%02x:%02x",
|
||||||
@@ -883,7 +897,7 @@ bool jsonToSettings(const JsonVariantConst src, Settings& dst, bool safe = false
|
|||||||
if (src["emergency"]["useEquitherm"].is<bool>()) {
|
if (src["emergency"]["useEquitherm"].is<bool>()) {
|
||||||
bool value = src["emergency"]["useEquitherm"].as<bool>();
|
bool value = src["emergency"]["useEquitherm"].as<bool>();
|
||||||
|
|
||||||
if (!dst.opentherm.nativeHeatingControl && dst.sensors.outdoor.type != SensorType::MANUAL) {
|
if (!dst.opentherm.nativeHeatingControl && dst.sensors.outdoor.type != SensorType::MANUAL && dst.sensors.outdoor.type != SensorType::BLUETOOTH) {
|
||||||
if (value != dst.emergency.useEquitherm) {
|
if (value != dst.emergency.useEquitherm) {
|
||||||
dst.emergency.useEquitherm = value;
|
dst.emergency.useEquitherm = value;
|
||||||
changed = true;
|
changed = true;
|
||||||
@@ -903,7 +917,7 @@ bool jsonToSettings(const JsonVariantConst src, Settings& dst, bool safe = false
|
|||||||
if (src["emergency"]["usePid"].is<bool>()) {
|
if (src["emergency"]["usePid"].is<bool>()) {
|
||||||
bool value = src["emergency"]["usePid"].as<bool>();
|
bool value = src["emergency"]["usePid"].as<bool>();
|
||||||
|
|
||||||
if (!dst.opentherm.nativeHeatingControl && dst.sensors.indoor.type != SensorType::MANUAL) {
|
if (!dst.opentherm.nativeHeatingControl && dst.sensors.indoor.type != SensorType::MANUAL && dst.sensors.indoor.type != SensorType::BLUETOOTH) {
|
||||||
if (value != dst.emergency.usePid) {
|
if (value != dst.emergency.usePid) {
|
||||||
dst.emergency.usePid = value;
|
dst.emergency.usePid = value;
|
||||||
changed = true;
|
changed = true;
|
||||||
@@ -937,6 +951,26 @@ bool jsonToSettings(const JsonVariantConst src, Settings& dst, bool safe = false
|
|||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (src["emergency"]["onIndoorSensorDisconnect"].is<bool>()) {
|
||||||
|
bool value = src["emergency"]["onIndoorSensorDisconnect"].as<bool>();
|
||||||
|
|
||||||
|
if (value != dst.emergency.onIndoorSensorDisconnect) {
|
||||||
|
dst.emergency.onIndoorSensorDisconnect = value;
|
||||||
|
dst.emergency.usePid = false;
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (src["emergency"]["onOutdoorSensorDisconnect"].is<bool>()) {
|
||||||
|
bool value = src["emergency"]["onOutdoorSensorDisconnect"].as<bool>();
|
||||||
|
|
||||||
|
if (value != dst.emergency.onOutdoorSensorDisconnect) {
|
||||||
|
dst.emergency.onOutdoorSensorDisconnect = value;
|
||||||
|
dst.emergency.useEquitherm = false;
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -1167,6 +1201,16 @@ bool jsonToSettings(const JsonVariantConst src, Settings& dst, bool safe = false
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
#if USE_BLE
|
||||||
|
case static_cast<byte>(SensorType::BLUETOOTH):
|
||||||
|
if (dst.sensors.outdoor.type != SensorType::BLUETOOTH) {
|
||||||
|
dst.sensors.outdoor.type = SensorType::BLUETOOTH;
|
||||||
|
dst.emergency.useEquitherm = false;
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -1189,6 +1233,21 @@ bool jsonToSettings(const JsonVariantConst src, Settings& dst, bool safe = false
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if USE_BLE
|
||||||
|
if (!src["sensors"]["outdoor"]["bleAddress"].isNull()) {
|
||||||
|
String value = src["sensors"]["outdoor"]["bleAddress"].as<String>();
|
||||||
|
int tmp[6];
|
||||||
|
if(sscanf(value.c_str(), "%02x:%02x:%02x:%02x:%02x:%02x", &tmp[0], &tmp[1], &tmp[2], &tmp[3], &tmp[4], &tmp[5]) == 6) {
|
||||||
|
for(uint8_t i = 0; i < 6; i++) {
|
||||||
|
if (dst.sensors.outdoor.bleAddress[i] != (uint8_t) tmp[i]) {
|
||||||
|
dst.sensors.outdoor.bleAddress[i] = (uint8_t) tmp[i];
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
if (!src["sensors"]["outdoor"]["offset"].isNull()) {
|
if (!src["sensors"]["outdoor"]["offset"].isNull()) {
|
||||||
float value = src["sensors"]["outdoor"]["offset"].as<float>();
|
float value = src["sensors"]["outdoor"]["offset"].as<float>();
|
||||||
|
|
||||||
@@ -1222,6 +1281,7 @@ bool jsonToSettings(const JsonVariantConst src, Settings& dst, bool safe = false
|
|||||||
case static_cast<byte>(SensorType::BLUETOOTH):
|
case static_cast<byte>(SensorType::BLUETOOTH):
|
||||||
if (dst.sensors.indoor.type != SensorType::BLUETOOTH) {
|
if (dst.sensors.indoor.type != SensorType::BLUETOOTH) {
|
||||||
dst.sensors.indoor.type = SensorType::BLUETOOTH;
|
dst.sensors.indoor.type = SensorType::BLUETOOTH;
|
||||||
|
dst.emergency.usePid = false;
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -1439,6 +1499,14 @@ void varsToJson(const Variables& src, JsonVariant dst) {
|
|||||||
dst["sensors"]["faultCode"] = src.sensors.faultCode;
|
dst["sensors"]["faultCode"] = src.sensors.faultCode;
|
||||||
dst["sensors"]["rssi"] = src.sensors.rssi;
|
dst["sensors"]["rssi"] = src.sensors.rssi;
|
||||||
dst["sensors"]["uptime"] = millis() / 1000ul;
|
dst["sensors"]["uptime"] = millis() / 1000ul;
|
||||||
|
dst["sensors"]["outdoor"]["connected"] = src.sensors.outdoor.connected;
|
||||||
|
dst["sensors"]["outdoor"]["rssi"] = src.sensors.outdoor.rssi;
|
||||||
|
dst["sensors"]["outdoor"]["battery"] = roundd(src.sensors.outdoor.battery, 2);
|
||||||
|
dst["sensors"]["outdoor"]["humidity"] = roundd(src.sensors.outdoor.humidity, 2);
|
||||||
|
dst["sensors"]["indoor"]["connected"] = src.sensors.indoor.connected;
|
||||||
|
dst["sensors"]["indoor"]["rssi"] = src.sensors.indoor.rssi;
|
||||||
|
dst["sensors"]["indoor"]["battery"] = roundd(src.sensors.indoor.battery, 2);
|
||||||
|
dst["sensors"]["indoor"]["humidity"] = roundd(src.sensors.indoor.humidity, 2);
|
||||||
|
|
||||||
dst["temperatures"]["indoor"] = roundd(src.temperatures.indoor, 2);
|
dst["temperatures"]["indoor"] = roundd(src.temperatures.indoor, 2);
|
||||||
dst["temperatures"]["outdoor"] = roundd(src.temperatures.outdoor, 2);
|
dst["temperatures"]["outdoor"] = roundd(src.temperatures.outdoor, 2);
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
"issues": "Issues & questions",
|
"issues": "Issues & questions",
|
||||||
"releases": "Releases"
|
"releases": "Releases"
|
||||||
},
|
},
|
||||||
|
"dbm": "dBm",
|
||||||
|
|
||||||
"button": {
|
"button": {
|
||||||
"upgrade": "Upgrade",
|
"upgrade": "Upgrade",
|
||||||
@@ -87,6 +88,14 @@
|
|||||||
"fault": "Fault",
|
"fault": "Fault",
|
||||||
"diag": "Diagnostic",
|
"diag": "Diagnostic",
|
||||||
"extpump": "External pump",
|
"extpump": "External pump",
|
||||||
|
"outdoorSensorConnected": "Outdoor sensor connected",
|
||||||
|
"outdoorSensorRssi": "Outdoor sensor RSSI",
|
||||||
|
"outdoorSensorHumidity": "Outdoor sensor humidity",
|
||||||
|
"outdoorSensorBattery": "Outdoor sensor battery",
|
||||||
|
"indoorSensorConnected": "Indoor sensor connected",
|
||||||
|
"indoorSensorRssi": "Indoor sensor RSSI",
|
||||||
|
"indoorSensorHumidity": "Indoor sensor humidity",
|
||||||
|
"indoorSensorBattery": "Indoor sensor battery",
|
||||||
"modulation": "Modulation",
|
"modulation": "Modulation",
|
||||||
"pressure": "Pressure",
|
"pressure": "Pressure",
|
||||||
"dhwFlowRate": "DHW flow rate",
|
"dhwFlowRate": "DHW flow rate",
|
||||||
@@ -208,7 +217,7 @@
|
|||||||
},
|
},
|
||||||
|
|
||||||
"emergency": {
|
"emergency": {
|
||||||
"desc": "<b>!</b> Emergency mode can be useful <u>only</u> when using Equitherm and/or PID (when normal work) and when reporting indoor/outdoor temperature via MQTT or API. In this mode, sensor values that are reported via MQTT/API are not used.",
|
"desc": "<b>!</b> Emergency mode can be useful <u>only</u> when using Equitherm and/or PID (when normal work) and when reporting indoor/outdoor temperature via MQTT/API/BLE. In this mode, sensor values that are reported via MQTT/API/BLE are not used.",
|
||||||
|
|
||||||
"target": {
|
"target": {
|
||||||
"title": "Target temperature",
|
"title": "Target temperature",
|
||||||
@@ -218,12 +227,14 @@
|
|||||||
|
|
||||||
"events": {
|
"events": {
|
||||||
"network": "On network fault",
|
"network": "On network fault",
|
||||||
"mqtt": "On MQTT fault"
|
"mqtt": "On MQTT fault",
|
||||||
|
"indoorSensorDisconnect": "On loss connection with indoor sensor",
|
||||||
|
"outdoorSensorDisconnect": "On loss connection with outdoor sensor"
|
||||||
},
|
},
|
||||||
|
|
||||||
"regulators": {
|
"regulators": {
|
||||||
"equitherm": "Equitherm <small>(requires at least an external/boiler <u>outdoor</u> sensor)</small>",
|
"equitherm": "Equitherm <small>(requires at least an external (DS18B20) or boiler <u>outdoor</u> sensor)</small>",
|
||||||
"pid": "PID <small>(requires at least an external/BLE <u>indoor</u> sensor)</small>"
|
"pid": "PID <small>(requires at least an external (DS18B20) <u>indoor</u> sensor)</small>"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
"issues": "Проблемы и вопросы",
|
"issues": "Проблемы и вопросы",
|
||||||
"releases": "Релизы"
|
"releases": "Релизы"
|
||||||
},
|
},
|
||||||
|
"dbm": "дБм",
|
||||||
|
|
||||||
"button": {
|
"button": {
|
||||||
"upgrade": "Обновить",
|
"upgrade": "Обновить",
|
||||||
@@ -87,6 +88,14 @@
|
|||||||
"fault": "Ошибка",
|
"fault": "Ошибка",
|
||||||
"diag": "Диагностика",
|
"diag": "Диагностика",
|
||||||
"extpump": "Внешний насос",
|
"extpump": "Внешний насос",
|
||||||
|
"outdoorSensorConnected": "Датчик наруж. темп.",
|
||||||
|
"outdoorSensorRssi": "RSSI датчика наруж. темп.",
|
||||||
|
"outdoorSensorHumidity": "Влажность с наруж. датчика темп.",
|
||||||
|
"outdoorSensorBattery": "Заряд наруж. датчика темп.",
|
||||||
|
"indoorSensorConnected": "Датчик внутр. темп.",
|
||||||
|
"indoorSensorRssi": "RSSI датчика внутр. темп.",
|
||||||
|
"indoorSensorHumidity": "Влажность с внутр. датчика темп.",
|
||||||
|
"indoorSensorBattery": "Заряд внутр. датчика темп.",
|
||||||
"modulation": "Уровень модуляции",
|
"modulation": "Уровень модуляции",
|
||||||
"pressure": "Давление",
|
"pressure": "Давление",
|
||||||
"dhwFlowRate": "Расход ГВС",
|
"dhwFlowRate": "Расход ГВС",
|
||||||
@@ -208,7 +217,7 @@
|
|||||||
},
|
},
|
||||||
|
|
||||||
"emergency": {
|
"emergency": {
|
||||||
"desc": "<b>!</b> Аварийный режим может быть полезен <u>только</u> при использовании ПЗА и/или ПИД и при передачи наружной/внутренней температуры через MQTT или API. В этом режиме значения датчиков, передаваемые через MQTT/API, не используются.",
|
"desc": "<b>!</b> Аварийный режим может быть полезен <u>только</u> при использовании ПЗА и/или ПИД и при передачи наружной/внутренней температуры через MQTT/API/BLE. В этом режиме значения датчиков, передаваемые через MQTT/API/BLE, не используются.",
|
||||||
|
|
||||||
"target": {
|
"target": {
|
||||||
"title": "Целевая температура",
|
"title": "Целевая температура",
|
||||||
@@ -218,12 +227,14 @@
|
|||||||
|
|
||||||
"events": {
|
"events": {
|
||||||
"network": "При отключении сети",
|
"network": "При отключении сети",
|
||||||
"mqtt": "При отключении MQTT"
|
"mqtt": "При отключении MQTT",
|
||||||
|
"indoorSensorDisconnect": "При потере связи с датчиком внутренней темп.",
|
||||||
|
"outdoorSensorDisconnect": "При потере связи с датчиком наружной темп."
|
||||||
},
|
},
|
||||||
|
|
||||||
"regulators": {
|
"regulators": {
|
||||||
"equitherm": "ПЗА <small>(требуется внешний или подключенный к котлу датчик <u>наружной</u> температуры)</small>",
|
"equitherm": "ПЗА <small>(требуется внешний (DS18B20) или подключенный к котлу датчик <u>наружной</u> температуры)</small>",
|
||||||
"pid": "ПИД <small>(требуется внешний/BLE датчик <u>внутренней</u> температуры)</small>"
|
"pid": "ПИД <small>(требуется внешний (DS18B20) датчик <u>внутренней</u> температуры)</small>"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -114,6 +114,38 @@
|
|||||||
<th scope="row" data-i18n>dashboard.state.extpump</th>
|
<th scope="row" data-i18n>dashboard.state.extpump</th>
|
||||||
<td><input type="radio" id="ot-external-pump" aria-invalid="false" checked disabled /></td>
|
<td><input type="radio" id="ot-external-pump" aria-invalid="false" checked disabled /></td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th scope="row" data-i18n>dashboard.state.outdoorSensorConnected</th>
|
||||||
|
<td><input type="radio" id="outdoor-sensor-connected" aria-invalid="false" checked disabled /></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th scope="row" data-i18n>dashboard.state.outdoorSensorRssi</th>
|
||||||
|
<td><b id="outdoor-sensor-rssi"></b> <span data-i18n>dbm</span></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th scope="row" data-i18n>dashboard.state.outdoorSensorHumidity</th>
|
||||||
|
<td><b id="outdoor-sensor-humidity"></b> %</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th scope="row" data-i18n>dashboard.state.outdoorSensorBattery</th>
|
||||||
|
<td><b id="outdoor-sensor-battery"></b> %</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th scope="row" data-i18n>dashboard.state.indoorSensorConnected</th>
|
||||||
|
<td><input type="radio" id="indoor-sensor-connected" aria-invalid="false" checked disabled /></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th scope="row" data-i18n>dashboard.state.indoorSensorRssi</th>
|
||||||
|
<td><b id="indoor-sensor-rssi"></b> <span data-i18n>dbm</span></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th scope="row" data-i18n>dashboard.state.indoorSensorHumidity</th>
|
||||||
|
<td><b id="indoor-sensor-humidity"></b> %</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th scope="row" data-i18n>dashboard.state.indoorSensorBattery</th>
|
||||||
|
<td><b id="indoor-sensor-battery"></b> %</td>
|
||||||
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row" data-i18n>dashboard.state.modulation</th>
|
<th scope="row" data-i18n>dashboard.state.modulation</th>
|
||||||
<td><b id="ot-modulation"></b> %</td>
|
<td><b id="ot-modulation"></b> %</td>
|
||||||
@@ -377,6 +409,7 @@
|
|||||||
setValue('#thermostat-dhw-current', result.temperatures.dhw);
|
setValue('#thermostat-dhw-current', result.temperatures.dhw);
|
||||||
|
|
||||||
setState('#ot-connected', result.states.otStatus);
|
setState('#ot-connected', result.states.otStatus);
|
||||||
|
setState('#mqtt-connected', result.states.mqtt);
|
||||||
setState('#ot-emergency', result.states.emergency);
|
setState('#ot-emergency', result.states.emergency);
|
||||||
setState('#ot-heating', result.states.heating);
|
setState('#ot-heating', result.states.heating);
|
||||||
setState('#ot-dhw', result.states.dhw);
|
setState('#ot-dhw', result.states.dhw);
|
||||||
@@ -384,7 +417,15 @@
|
|||||||
setState('#ot-fault', result.states.fault);
|
setState('#ot-fault', result.states.fault);
|
||||||
setState('#ot-diagnostic', result.states.diagnostic);
|
setState('#ot-diagnostic', result.states.diagnostic);
|
||||||
setState('#ot-external-pump', result.states.externalPump);
|
setState('#ot-external-pump', result.states.externalPump);
|
||||||
setState('#mqtt-connected', result.states.mqtt);
|
setState('#outdoor-sensor-connected', result.sensors.outdoor.connected);
|
||||||
|
setState('#indoor-sensor-connected', result.sensors.indoor.connected);
|
||||||
|
|
||||||
|
setValue('#outdoor-sensor-rssi', result.sensors.outdoor.rssi);
|
||||||
|
setValue('#outdoor-sensor-humidity', result.sensors.outdoor.humidity);
|
||||||
|
setValue('#outdoor-sensor-battery', result.sensors.outdoor.battery);
|
||||||
|
setValue('#indoor-sensor-rssi', result.sensors.indoor.rssi);
|
||||||
|
setValue('#indoor-sensor-humidity', result.sensors.indoor.humidity);
|
||||||
|
setValue('#indoor-sensor-battery', result.sensors.indoor.battery);
|
||||||
|
|
||||||
setValue('#ot-modulation', result.sensors.modulation);
|
setValue('#ot-modulation', result.sensors.modulation);
|
||||||
setValue('#ot-pressure', result.sensors.pressure);
|
setValue('#ot-pressure', result.sensors.pressure);
|
||||||
|
|||||||
@@ -233,6 +233,16 @@
|
|||||||
<input type="checkbox" id="emergency-on-mqtt-fault" name="emergency[onMqttFault]" value="true">
|
<input type="checkbox" id="emergency-on-mqtt-fault" name="emergency[onMqttFault]" value="true">
|
||||||
<span data-i18n>settings.emergency.events.mqtt</span>
|
<span data-i18n>settings.emergency.events.mqtt</span>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
|
<label for="emergency-on-indoor-sensor-disconnect">
|
||||||
|
<input type="checkbox" id="emergency-on-indoor-sensor-disconnect" name="emergency[onIndoorSensorDisconnect]" value="true">
|
||||||
|
<span data-i18n>settings.emergency.events.indoorSensorDisconnect</span>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label for="emergency-on-outdoor-sensor-disconnect">
|
||||||
|
<input type="checkbox" id="emergency-on-outdoor-sensor-disconnect" name="emergency[onOutdoorSensorDisconnect]" value="true">
|
||||||
|
<span data-i18n>settings.emergency.events.outdoorSensorDisconnect</span>
|
||||||
|
</label>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
<fieldset>
|
<fieldset>
|
||||||
@@ -544,6 +554,11 @@
|
|||||||
<input type="radio" class="outdoor-sensor-type" name="sensors[outdoor][type]" value="2" />
|
<input type="radio" class="outdoor-sensor-type" name="sensors[outdoor][type]" value="2" />
|
||||||
<span data-i18n>settings.tempSensor.source.ext</span>
|
<span data-i18n>settings.tempSensor.source.ext</span>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
|
<label>
|
||||||
|
<input type="radio" class="outdoor-sensor-type" name="sensors[outdoor][type]" value="3" />
|
||||||
|
<span data-i18n>settings.tempSensor.source.ble</span>
|
||||||
|
</label>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
<label for="outdoor-sensor-gpio">
|
<label for="outdoor-sensor-gpio">
|
||||||
@@ -551,10 +566,18 @@
|
|||||||
<input type="number" inputmode="numeric" id="outdoor-sensor-gpio" name="sensors[outdoor][gpio]" min="0" max="254" step="1">
|
<input type="number" inputmode="numeric" id="outdoor-sensor-gpio" name="sensors[outdoor][gpio]" min="0" max="254" step="1">
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<label for="outdoor-sensor-offset">
|
<div class="grid">
|
||||||
<span data-i18n>settings.tempSensor.offset</span>
|
<label for="outdoor-sensor-offset">
|
||||||
<input type="number" inputmode="numeric" id="outdoor-sensor-offset" name="sensors[outdoor][offset]" min="-10" max="10" step="0.01" required>
|
<span data-i18n>settings.tempSensor.offset</span>
|
||||||
</label>
|
<input type="number" inputmode="numeric" id="outdoor-sensor-offset" name="sensors[outdoor][offset]" min="-10" max="10" step="0.01" required>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label for="outdoor-sensor-ble-addresss">
|
||||||
|
<span data-i18n>settings.tempSensor.bleAddress.title</span>
|
||||||
|
<input type="text" id="outdoor-sensor-ble-addresss" name="sensors[outdoor][bleAddress]" pattern="([A-Fa-f0-9]{2}:){5}[A-Fa-f0-9]{2}">
|
||||||
|
<small data-i18n>settings.tempSensor.bleAddress.note</small>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
<button type="submit" data-i18n>button.save</button>
|
<button type="submit" data-i18n>button.save</button>
|
||||||
</form>
|
</form>
|
||||||
@@ -724,6 +747,7 @@
|
|||||||
setRadioValue('.outdoor-sensor-type', data.sensors.outdoor.type);
|
setRadioValue('.outdoor-sensor-type', data.sensors.outdoor.type);
|
||||||
setInputValue('#outdoor-sensor-gpio', data.sensors.outdoor.gpio < 255 ? data.sensors.outdoor.gpio : '');
|
setInputValue('#outdoor-sensor-gpio', data.sensors.outdoor.gpio < 255 ? data.sensors.outdoor.gpio : '');
|
||||||
setInputValue('#outdoor-sensor-offset', data.sensors.outdoor.offset);
|
setInputValue('#outdoor-sensor-offset', data.sensors.outdoor.offset);
|
||||||
|
setInputValue('#outdoor-sensor-ble-addresss', data.sensors.outdoor.bleAddress);
|
||||||
setBusy('#outdoor-sensor-settings-busy', '#outdoor-sensor-settings', false);
|
setBusy('#outdoor-sensor-settings-busy', '#outdoor-sensor-settings', false);
|
||||||
|
|
||||||
// Indoor sensor
|
// Indoor sensor
|
||||||
@@ -772,6 +796,8 @@
|
|||||||
setCheckboxValue('#emergency-use-pid', data.emergency.usePid);
|
setCheckboxValue('#emergency-use-pid', data.emergency.usePid);
|
||||||
setCheckboxValue('#emergency-on-network-fault', data.emergency.onNetworkFault);
|
setCheckboxValue('#emergency-on-network-fault', data.emergency.onNetworkFault);
|
||||||
setCheckboxValue('#emergency-on-mqtt-fault', data.emergency.onMqttFault);
|
setCheckboxValue('#emergency-on-mqtt-fault', data.emergency.onMqttFault);
|
||||||
|
setCheckboxValue('#emergency-on-indoor-sensor-disconnect', data.emergency.onIndoorSensorDisconnect);
|
||||||
|
setCheckboxValue('#emergency-on-outdoor-sensor-disconnect', data.emergency.onOutdoorSensorDisconnect);
|
||||||
setInputValue('#emergency-target', data.emergency.target, {
|
setInputValue('#emergency-target', data.emergency.target, {
|
||||||
"min": (!data.emergency.useEquitherm && !data.emergency.usePid) ? data.heating.minTemp : 10,
|
"min": (!data.emergency.useEquitherm && !data.emergency.usePid) ? data.heating.minTemp : 10,
|
||||||
"max": (!data.emergency.useEquitherm && !data.emergency.usePid) ? data.heating.maxTemp : 30,
|
"max": (!data.emergency.useEquitherm && !data.emergency.usePid) ? data.heating.maxTemp : 30,
|
||||||
|
|||||||
Reference in New Issue
Block a user