mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2026-10-28 11:14:58 +00:00
formatting
This commit is contained in:
@@ -30,6 +30,7 @@ BreakConstructorInitializersBeforeComma: true # Always break constructor initial
|
||||
ExperimentalAutoDetectBinPacking: false
|
||||
KeepEmptyLinesAtTheStartOfBlocks: false
|
||||
MaxEmptyLinesToKeep: 1
|
||||
InsertNewlineAtEOF: true # clang-format 16+: one newline at end of file if missing
|
||||
PenaltyBreakBeforeFirstCallParameter: 200
|
||||
PenaltyExcessCharacter: 10
|
||||
PointerAlignment: Middle
|
||||
|
||||
@@ -64,6 +64,7 @@ StateUpdateResult APSettings::update(JsonObject root, APSettings & settings) {
|
||||
if (appliedOnce && modeChanged) {
|
||||
emsesp::EMSESP::network_.schedule_reconnect();
|
||||
}
|
||||
|
||||
appliedOnce = true;
|
||||
|
||||
return StateUpdateResult::CHANGED;
|
||||
|
||||
@@ -102,6 +102,7 @@ String ArduinoJsonJWT::encode(const char * cstr, int inputLen) {
|
||||
while (result.length() > 0 && result.charAt(result.length() - 1) == '=') {
|
||||
result.remove(result.length() - 1);
|
||||
}
|
||||
|
||||
result.replace('+', '-');
|
||||
result.replace('/', '_');
|
||||
|
||||
@@ -129,4 +130,4 @@ String ArduinoJsonJWT::decode(String value) {
|
||||
|
||||
// return as string
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,6 +52,7 @@ class StaticContentHandler : public AsyncWebHandler {
|
||||
return &WWW_ASSETS[i];
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -62,6 +63,7 @@ class StaticContentHandler : public AsyncWebHandler {
|
||||
if (cached == nullptr) {
|
||||
cached = lookup("/index.html");
|
||||
}
|
||||
|
||||
return cached;
|
||||
}
|
||||
};
|
||||
@@ -91,6 +93,7 @@ void ESP32React::begin() {
|
||||
defaultHeaders.addHeader(asyncsrv::T_CORS_ACAH, "Accept, Content-Type, Authorization");
|
||||
defaultHeaders.addHeader(asyncsrv::T_CORS_ACAC, "true");
|
||||
}
|
||||
|
||||
defaultHeaders.addHeader(asyncsrv::T_Server, networkSettings.hostname);
|
||||
});
|
||||
_apSettingsService.begin();
|
||||
|
||||
@@ -35,6 +35,7 @@ class FSPersistence {
|
||||
settingsFile.close();
|
||||
return;
|
||||
}
|
||||
|
||||
settingsFile.close();
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ class IPUtils {
|
||||
static bool isSet(const IPAddress & ip) {
|
||||
return ip != getNotSetIP();
|
||||
}
|
||||
|
||||
static bool isNotSet(const IPAddress & ip) {
|
||||
return ip == getNotSetIP();
|
||||
}
|
||||
@@ -19,4 +20,4 @@ class IPUtils {
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -14,14 +14,17 @@ class JsonUtils {
|
||||
if (!defaultIp.fromString(def)) {
|
||||
defaultIp = INADDR_NONE;
|
||||
}
|
||||
|
||||
readIP(root, key, ip, defaultIp);
|
||||
}
|
||||
|
||||
static void readIP(JsonObject root, const String & key, IPAddress & ip, const IPAddress & defaultIp = INADDR_NONE) {
|
||||
const JsonVariant value = root[key];
|
||||
if (!value.is<String>() || !ip.fromString(value.as<const char *>())) {
|
||||
ip = defaultIp;
|
||||
}
|
||||
}
|
||||
|
||||
static void writeIP(JsonObject root, const String & key, const IPAddress & ip) {
|
||||
if (IPUtils::isSet(ip)) {
|
||||
char ipStr[16];
|
||||
|
||||
@@ -38,6 +38,7 @@ void MqttSettingsService::startClient() {
|
||||
if ((isSecure && _state.enableTLS) || (!isSecure && !_state.enableTLS)) {
|
||||
return;
|
||||
}
|
||||
|
||||
delete _mqttClient;
|
||||
_mqttClient = nullptr;
|
||||
}
|
||||
@@ -49,10 +50,12 @@ void MqttSettingsService::startClient() {
|
||||
} else {
|
||||
_mqttClient = new espMqttClientSecure(EMSESP_MQTT_PRIORITY, EMSESP_MQTT_RUNNING_CORE);
|
||||
}
|
||||
|
||||
if (!_mqttClient) {
|
||||
emsesp::EMSESP::logger().warning("MQTT Client alloc failed");
|
||||
return;
|
||||
}
|
||||
|
||||
static_cast<espMqttClientSecure *>(_mqttClient)->onConnect([this](bool sessionPresent) { onMqttConnect(sessionPresent); });
|
||||
static_cast<espMqttClientSecure *>(_mqttClient)->onDisconnect([this](espMqttClientTypes::DisconnectReason reason) { onMqttDisconnect(reason); });
|
||||
static_cast<espMqttClientSecure *>(_mqttClient)
|
||||
@@ -62,12 +65,14 @@ void MqttSettingsService::startClient() {
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
isSecure = false;
|
||||
if (emsesp::EMSESP::system_.PSram() == 0) {
|
||||
_mqttClient = new espMqttClient(espMqttClientTypes::UseInternalTask::NO);
|
||||
} else {
|
||||
_mqttClient = new espMqttClient(EMSESP_MQTT_PRIORITY, EMSESP_MQTT_RUNNING_CORE);
|
||||
}
|
||||
|
||||
static_cast<espMqttClient *>(_mqttClient)->onConnect([this](bool sessionPresent) { onMqttConnect(sessionPresent); });
|
||||
static_cast<espMqttClient *>(_mqttClient)->onDisconnect([this](espMqttClientTypes::DisconnectReason reason) { onMqttDisconnect(reason); });
|
||||
static_cast<espMqttClient *>(_mqttClient)
|
||||
@@ -82,14 +87,17 @@ void MqttSettingsService::loop() {
|
||||
// emsesp::EMSESP::logger().info("Network connection dropped, stopping MQTT client");
|
||||
_mqttClient->disconnect(true);
|
||||
}
|
||||
|
||||
if (_reconfigureMqtt || (_disconnectedAt && static_cast<uint32_t>(uuid::get_uptime() - _disconnectedAt) >= MQTT_RECONNECTION_DELAY)) {
|
||||
// configure MQTT client
|
||||
if (_reconfigureMqtt && _state.enabled) {
|
||||
emsesp::EMSESP::logger().info("Starting MQTT service");
|
||||
}
|
||||
|
||||
_disconnectedAt = configureMqtt() ? 0 : uuid::get_uptime();
|
||||
_reconfigureMqtt = false;
|
||||
}
|
||||
|
||||
if (emsesp::EMSESP::system_.PSram() == 0) {
|
||||
_mqttClient->loop();
|
||||
}
|
||||
@@ -188,21 +196,25 @@ bool MqttSettingsService::configureMqtt() {
|
||||
String certificate = "-----BEGIN CERTIFICATE-----\n" + _state.rootCA + "\n-----END CERTIFICATE-----\n";
|
||||
static_cast<espMqttClientSecure *>(_mqttClient)->setCACert(certificate.c_str());
|
||||
}
|
||||
|
||||
static_cast<espMqttClientSecure *>(_mqttClient)->setServer(_state.host.c_str(), _state.port);
|
||||
if (_state.username.length() > 0) {
|
||||
static_cast<espMqttClientSecure *>(_mqttClient)
|
||||
->setCredentials(_state.username.c_str(), _state.password.length() > 0 ? _state.password.c_str() : nullptr);
|
||||
}
|
||||
|
||||
static_cast<espMqttClientSecure *>(_mqttClient)->setClientId(_state.clientId.c_str());
|
||||
static_cast<espMqttClientSecure *>(_mqttClient)->setKeepAlive(_state.keepAlive);
|
||||
static_cast<espMqttClientSecure *>(_mqttClient)->setCleanSession(_state.cleanSession);
|
||||
static_cast<espMqttClientSecure *>(_mqttClient)->setWill(will_topic, 1, true, "offline"); // QOS 1, retain
|
||||
return (_mqttClient->connect());
|
||||
}
|
||||
|
||||
static_cast<espMqttClient *>(_mqttClient)->setServer(_state.host.c_str(), _state.port);
|
||||
if (_state.username.length() > 0) {
|
||||
static_cast<espMqttClient *>(_mqttClient)->setCredentials(_state.username.c_str(), _state.password.length() > 0 ? _state.password.c_str() : nullptr);
|
||||
}
|
||||
|
||||
static_cast<espMqttClient *>(_mqttClient)->setClientId(_state.clientId.c_str());
|
||||
static_cast<espMqttClient *>(_mqttClient)->setKeepAlive(_state.keepAlive);
|
||||
static_cast<espMqttClient *>(_mqttClient)->setCleanSession(_state.cleanSession);
|
||||
@@ -322,6 +334,7 @@ StateUpdateResult MqttSettings::update(JsonObject root, MqttSettings & settings)
|
||||
if (newSettings.publish_single) {
|
||||
newSettings.ha_enabled = false;
|
||||
}
|
||||
|
||||
changed = true;
|
||||
}
|
||||
|
||||
@@ -338,6 +351,7 @@ StateUpdateResult MqttSettings::update(JsonObject root, MqttSettings & settings)
|
||||
if (newSettings.ha_enabled) {
|
||||
newSettings.publish_single = false;
|
||||
}
|
||||
|
||||
changed = true;
|
||||
}
|
||||
|
||||
@@ -387,9 +401,11 @@ StateUpdateResult MqttSettings::update(JsonObject root, MqttSettings & settings)
|
||||
if (newSettings.rootCA.length() == 0 && newSettings.enableTLS) {
|
||||
newSettings.rootCA = "insecure";
|
||||
}
|
||||
|
||||
if (newSettings.enableTLS != settings.enableTLS || newSettings.rootCA != settings.rootCA) {
|
||||
changed = true;
|
||||
}
|
||||
|
||||
// save the new settings
|
||||
settings = newSettings;
|
||||
|
||||
|
||||
@@ -97,8 +97,10 @@ StateUpdateResult NTPSettings::update(JsonObject root, NTPSettings & settings) {
|
||||
while (settings.tzFormat.length() < 40) {
|
||||
settings.tzFormat += " ";
|
||||
}
|
||||
|
||||
while (settings.tzFormatT.length() < 40) {
|
||||
settings.tzFormatT += " ";
|
||||
}
|
||||
|
||||
return StateUpdateResult::CHANGED;
|
||||
}
|
||||
|
||||
@@ -45,6 +45,7 @@ void NTPStatus::ntpStatus(AsyncWebServerRequest * request) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}();
|
||||
|
||||
@@ -59,4 +60,4 @@ void NTPStatus::ntpStatus(AsyncWebServerRequest * request) {
|
||||
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ void add_ipv6(JsonObject root, NetworkInterface & netif) {
|
||||
if (found.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
JsonArray addresses = root["ipv6"].to<JsonArray>();
|
||||
for (const auto & ipv6 : found) {
|
||||
JsonObject entry = addresses.add<JsonObject>();
|
||||
@@ -28,10 +29,12 @@ void add_gateways(JsonObject root, NetworkInterface & netif) {
|
||||
if (IPUtils::isSet(netif.gatewayIP())) {
|
||||
root["gateway_ip"] = netif.gatewayIP().toString();
|
||||
}
|
||||
|
||||
auto found = emsesp::Network::ipv6_gateways(netif);
|
||||
if (found.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
JsonArray gateways = root["gateway_ipv6"].to<JsonArray>();
|
||||
for (const auto & ip : found) {
|
||||
gateways.add(ip.toString());
|
||||
|
||||
@@ -36,6 +36,7 @@ void SecurityManager::ensureRestDispatcher(AsyncWebServer * server) {
|
||||
if (_restDispatcherInstalled || server == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
_restDispatcherInstalled = true;
|
||||
server->addHandler(new RestCatchAllHandler(this));
|
||||
}
|
||||
@@ -47,6 +48,7 @@ void SecurityManager::dispatchRest(AsyncWebServerRequest * request, JsonVariant
|
||||
if (!route.method.matches(method)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!route.uri.matches(request)) {
|
||||
continue;
|
||||
}
|
||||
@@ -64,6 +66,7 @@ void SecurityManager::dispatchRest(AsyncWebServerRequest * request, JsonVariant
|
||||
} else {
|
||||
route.plainHandler(request);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -78,11 +81,13 @@ bool SecurityManager::RestCatchAllHandler::canHandle(AsyncWebServerRequest * req
|
||||
if (!request->isHTTP()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const auto & route : _owner->_restRoutes) {
|
||||
if (route.method.matches(request->method()) && route.uri.matches(request)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -96,6 +101,7 @@ void SecurityManager::RestCatchAllHandler::handleBody(AsyncWebServerRequest * re
|
||||
if (total == 0 || total > kMaxBodySize || !isJsonContent(request)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (index == 0 && request->_tempObject == nullptr) {
|
||||
request->_tempObject = calloc(total + 1, sizeof(uint8_t)); // freed by request destructor
|
||||
if (request->_tempObject == nullptr) {
|
||||
@@ -103,6 +109,7 @@ void SecurityManager::RestCatchAllHandler::handleBody(AsyncWebServerRequest * re
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (request->_tempObject != nullptr) {
|
||||
memcpy(static_cast<uint8_t *>(request->_tempObject) + index, data, len);
|
||||
}
|
||||
@@ -116,8 +123,10 @@ void SecurityManager::RestCatchAllHandler::handleRequest(AsyncWebServerRequest *
|
||||
request->send(400);
|
||||
return;
|
||||
}
|
||||
|
||||
json = doc.as<JsonVariant>();
|
||||
}
|
||||
|
||||
_owner->dispatchRest(request, json);
|
||||
}
|
||||
|
||||
|
||||
@@ -50,9 +50,11 @@ class AuthenticationPredicates {
|
||||
(void)authentication;
|
||||
return true;
|
||||
};
|
||||
|
||||
static bool IS_AUTHENTICATED(const Authentication & authentication) {
|
||||
return authentication.authenticated;
|
||||
};
|
||||
|
||||
static bool IS_ADMIN(const Authentication & authentication) {
|
||||
return authentication.authenticated && authentication.user->admin;
|
||||
};
|
||||
|
||||
@@ -29,6 +29,7 @@ Authentication SecuritySettingsService::authenticateRequest(AsyncWebServerReques
|
||||
String value = tokenParamater->value();
|
||||
return authenticateJWT(value);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -48,6 +49,7 @@ Authentication SecuritySettingsService::authenticateJWT(String & jwt) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -57,6 +59,7 @@ Authentication SecuritySettingsService::authenticate(const String & username, co
|
||||
return Authentication(_user);
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -91,5 +94,6 @@ void SecuritySettingsService::generateToken(AsyncWebServerRequest * request) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
request->send(401);
|
||||
}
|
||||
|
||||
@@ -62,6 +62,7 @@ class SecuritySettings {
|
||||
settings.users.emplace_back(FACTORY_ADMIN_USERNAME, FACTORY_ADMIN_PASSWORD, true);
|
||||
settings.users.emplace_back(FACTORY_GUEST_USERNAME, FACTORY_GUEST_PASSWORD, false);
|
||||
}
|
||||
|
||||
return StateUpdateResult::CHANGED;
|
||||
}
|
||||
};
|
||||
@@ -90,4 +91,4 @@ class SecuritySettingsService final : public StatefulService<SecuritySettings>,
|
||||
boolean validatePayload(JsonObject parsedPayload, const User * user); // Verify the payload is correct
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -50,6 +50,7 @@ class StatefulService {
|
||||
if (!cb) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
StateUpdateHandlerInfo_t updateHandler(std::move(cb), allowRemove);
|
||||
_updateHandlers.push_back(std::move(updateHandler));
|
||||
return updateHandler._id;
|
||||
@@ -73,6 +74,7 @@ class StatefulService {
|
||||
if (result == StateUpdateResult::CHANGED) {
|
||||
callUpdateHandlers();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -90,6 +92,7 @@ class StatefulService {
|
||||
if (result == StateUpdateResult::CHANGED) {
|
||||
callUpdateHandlers();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -55,6 +55,7 @@ void UploadFileService::handleUpload(AsyncWebServerRequest * request, const Stri
|
||||
} else {
|
||||
emsesp::EMSESP::logger().info("MD5 digest received (%s). Now upload the firmware BIN", _ota.md5());
|
||||
}
|
||||
|
||||
return;
|
||||
default:
|
||||
_ota.clearMd5();
|
||||
@@ -78,6 +79,7 @@ void UploadFileService::handleUpload(AsyncWebServerRequest * request, const Stri
|
||||
handleError(request, 406);
|
||||
return;
|
||||
}
|
||||
|
||||
_is_recovery = true;
|
||||
emsesp::EMSESP::logger().info("Recovery firmware uploading to the %s partition (size: %dKB). Please wait...", target->label, filesize / 1024);
|
||||
} else
|
||||
@@ -96,6 +98,7 @@ void UploadFileService::handleUpload(AsyncWebServerRequest * request, const Stri
|
||||
if (_ota.md5Applied()) {
|
||||
emsesp::EMSESP::logger().info("Firmware MD5 check enabled (%s)", _ota.md5());
|
||||
}
|
||||
|
||||
request->onDisconnect([this] { handleDisconnect(); }); // success, let's make sure we end the update if the client hangs up
|
||||
} else {
|
||||
emsesp::EMSESP::logger().err("OTA begin failed: %s", _ota.errorString());
|
||||
@@ -133,6 +136,7 @@ void UploadFileService::handleUpload(AsyncWebServerRequest * request, const Stri
|
||||
handleError(request, 500); // internal error, failed
|
||||
return;
|
||||
}
|
||||
|
||||
if (final) {
|
||||
if (!_ota.end()) {
|
||||
emsesp::EMSESP::logger().err("OTA update failed: %s", _ota.errorString());
|
||||
@@ -156,6 +160,7 @@ void UploadFileService::uploadComplete(AsyncWebServerRequest * request) {
|
||||
request->send(request->beginResponse(code));
|
||||
_response_sent = true;
|
||||
}
|
||||
|
||||
_is_firmware = false;
|
||||
_is_filesystem = false;
|
||||
_is_recovery = false;
|
||||
@@ -187,6 +192,7 @@ void UploadFileService::uploadComplete(AsyncWebServerRequest * request) {
|
||||
emsesp::EMSESP::logger().info("Firmware MD5 matches");
|
||||
root["md5_ok"] = true;
|
||||
}
|
||||
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
_ota.clearMd5();
|
||||
@@ -214,6 +220,7 @@ void UploadFileService::uploadComplete(AsyncWebServerRequest * request) {
|
||||
AsyncWebServerResponse * response = request->beginResponse(200);
|
||||
request->send(response);
|
||||
}
|
||||
|
||||
emsesp::EMSESP::system_.systemStatus(
|
||||
emsesp::SYSTEM_STATUS::SYSTEM_STATUS_PENDING_RESTART); // will be handled by the main loop. We use pending for the Web's SystemMonitor
|
||||
return;
|
||||
@@ -237,6 +244,7 @@ void UploadFileService::handleError(AsyncWebServerRequest * request, int code) {
|
||||
if (request->_tempObject) {
|
||||
return;
|
||||
}
|
||||
|
||||
request->_tempObject = reinterpret_cast<void *>(static_cast<intptr_t>(code)); // stop later chunks from retrying the write
|
||||
|
||||
emsesp::EMSESP::logger().info("Upload error: %d", code);
|
||||
|
||||
@@ -34,9 +34,10 @@ void WiFiScanner::listNetworks(AsyncWebServerRequest * request) {
|
||||
network["channel"] = WiFi.channel(i);
|
||||
network["encryption_type"] = static_cast<uint8_t>(WiFi.encryptionType(i));
|
||||
}
|
||||
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
} else {
|
||||
request->send(202); // special code to indicate scan in progress
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ class ModuleLibrary {
|
||||
: key(key)
|
||||
, module(std::move(module)) {
|
||||
}
|
||||
|
||||
const char * key;
|
||||
std::unique_ptr<Module> module;
|
||||
};
|
||||
|
||||
@@ -35,22 +35,27 @@ void IRAM_ATTR AnalogSensor::freqIrq0() {
|
||||
edgecnt[0] = edgecnt[0] + 1;
|
||||
edge[0] = micros();
|
||||
}
|
||||
|
||||
portEXIT_CRITICAL_ISR(&mux);
|
||||
}
|
||||
|
||||
void IRAM_ATTR AnalogSensor::freqIrq1() {
|
||||
portENTER_CRITICAL_ISR(&mux);
|
||||
if (micros() - edge[1] > 10) { // limit to 100kHz
|
||||
edgecnt[1] = edgecnt[1] + 1;
|
||||
edge[1] = micros();
|
||||
}
|
||||
|
||||
portEXIT_CRITICAL_ISR(&mux);
|
||||
}
|
||||
|
||||
void IRAM_ATTR AnalogSensor::freqIrq2() {
|
||||
portENTER_CRITICAL_ISR(&mux);
|
||||
if (micros() - edge[2] > 10) { // limit to 100kHz
|
||||
edgecnt[2] = edgecnt[2] + 1;
|
||||
edge[2] = micros();
|
||||
}
|
||||
|
||||
portEXIT_CRITICAL_ISR(&mux);
|
||||
}
|
||||
#endif
|
||||
@@ -89,6 +94,7 @@ void AnalogSensor::start(const bool factory_settings) {
|
||||
});
|
||||
LOG_DEBUG("Adding system sensors for core_voltage, supply_voltage and led");
|
||||
}
|
||||
|
||||
reload(true); // fetch the list of sensors from our customization service
|
||||
|
||||
if (!analog_enabled_) {
|
||||
@@ -140,6 +146,7 @@ void AnalogSensor::reload(bool get_nvs) {
|
||||
sensors_.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
changed_ = true;
|
||||
// load the list of analog sensors from the customization service
|
||||
// and store them locally and then activate them
|
||||
@@ -155,11 +162,13 @@ void AnalogSensor::reload(bool get_nvs) {
|
||||
&& (sensor_.type() != sensor.type || sensor_.offset() != sensor.offset || sensor_.factor() != sensor.factor)) {
|
||||
sensor_.set_value(sensor.offset);
|
||||
}
|
||||
|
||||
if ((sensor.type == AnalogType::COUNTER || (sensor.type >= AnalogType::CNT_0 && sensor.type <= AnalogType::CNT_2))
|
||||
&& sensor_.offset() != sensor.offset && sensor.offset != EMSESP::nvs_.getDouble(sensor.name, 0)) {
|
||||
EMSESP::nvs_.putDouble(sensor.name, sensor.offset);
|
||||
sensor_.set_value(sensor.offset);
|
||||
}
|
||||
|
||||
sensor_.set_name(sensor.name);
|
||||
sensor_.set_type(sensor.type);
|
||||
sensor_.set_offset(sensor.offset);
|
||||
@@ -169,6 +178,7 @@ void AnalogSensor::reload(bool get_nvs) {
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
it = sensors_.erase(it);
|
||||
} else {
|
||||
@@ -184,6 +194,7 @@ void AnalogSensor::reload(bool get_nvs) {
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
// it's new, we assume it's valid
|
||||
sensors_.emplace_back(sensor.gpio, sensor.name, sensor.offset, sensor.factor, sensor.uom, sensor.type, sensor.is_system);
|
||||
@@ -212,6 +223,7 @@ void AnalogSensor::reload(bool get_nvs) {
|
||||
CommandFlag::ADMIN_ONLY);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
@@ -225,10 +237,12 @@ void AnalogSensor::reload(bool get_nvs) {
|
||||
exclude_types_.push_back(sensor.type());
|
||||
exclude_types_.push_back(sensor.type() + 3);
|
||||
}
|
||||
|
||||
if ((sensor.type() >= AnalogType::CNT_0 && sensor.type() <= AnalogType::CNT_2)) {
|
||||
exclude_types_.push_back(sensor.type());
|
||||
exclude_types_.push_back(sensor.type() - 3);
|
||||
}
|
||||
|
||||
if ((sensor.type() >= AnalogType::PWM_0 && sensor.type() <= AnalogType::PWM_2)) {
|
||||
exclude_types_.push_back(sensor.type());
|
||||
}
|
||||
@@ -247,6 +261,7 @@ void AnalogSensor::reload(bool get_nvs) {
|
||||
#endif
|
||||
pinMode(sensor.gpio(), INPUT_PULLUP);
|
||||
}
|
||||
|
||||
if (sensor.type() == AnalogType::ADC) {
|
||||
LOG_DEBUG("ADC Sensor on GPIO %02d", sensor.gpio());
|
||||
// analogSetPinAttenuation does not work with analogReadMilliVolts
|
||||
@@ -263,6 +278,7 @@ void AnalogSensor::reload(bool get_nvs) {
|
||||
if (double_t val = EMSESP::nvs_.getDouble(sensor.name(), 0)) {
|
||||
sensor.set_value(val);
|
||||
}
|
||||
|
||||
publish_sensor(sensor);
|
||||
} else if (sensor.type() == AnalogType::TIMER || sensor.type() == AnalogType::RATE) {
|
||||
LOG_DEBUG("Timer/Rate on GPIO %02d", sensor.gpio());
|
||||
@@ -290,6 +306,7 @@ void AnalogSensor::reload(bool get_nvs) {
|
||||
if (double_t val = EMSESP::nvs_.getDouble(sensor.name(), 0)) {
|
||||
sensor.set_value(val);
|
||||
}
|
||||
|
||||
publish_sensor(sensor);
|
||||
attachInterrupt(sensor.gpio(), index == 0 ? freqIrq0 : index == 1 ? freqIrq1 : freqIrq2, FALLING);
|
||||
edgecnt[index] = 0;
|
||||
@@ -320,6 +337,7 @@ void AnalogSensor::reload(bool get_nvs) {
|
||||
} else if (sensor.offset() < 0) {
|
||||
sensor.set_offset(0);
|
||||
}
|
||||
|
||||
dacWrite(sensor.gpio(), sensor.offset());
|
||||
sensor.set_value(sensor.offset());
|
||||
} else
|
||||
@@ -330,6 +348,7 @@ void AnalogSensor::reload(bool get_nvs) {
|
||||
} else if (sensor.offset() < 0) {
|
||||
sensor.set_offset(0);
|
||||
}
|
||||
|
||||
dacWrite(sensor.gpio(), sensor.offset());
|
||||
sensor.set_value(sensor.offset());
|
||||
} else
|
||||
@@ -344,10 +363,12 @@ void AnalogSensor::reload(bool get_nvs) {
|
||||
} else if (sensor.uom() > 1) {
|
||||
sensor.set_uom(2);
|
||||
}
|
||||
|
||||
sensor.set_offset(sensor.offset() > 0 ? 1 : 0);
|
||||
digitalWrite(sensor.gpio(), (sensor.offset() == 0) ^ (sensor.factor() > 0));
|
||||
sensor.set_value(sensor.offset());
|
||||
}
|
||||
|
||||
publish_sensor(sensor);
|
||||
} else if (sensor.type() == AnalogType::PULSE) {
|
||||
LOG_DEBUG("Pulse on GPIO %02d", sensor.gpio());
|
||||
@@ -362,6 +383,7 @@ void AnalogSensor::reload(bool get_nvs) {
|
||||
} else if (sensor.offset() < 0) {
|
||||
sensor.set_offset(0);
|
||||
}
|
||||
|
||||
ledcWrite(sensor.gpio(), (uint32_t)(sensor.offset() * 8191 / 100));
|
||||
sensor.set_value(sensor.offset());
|
||||
sensor.set_uom(DeviceValueUOM::PERCENT);
|
||||
@@ -406,6 +428,7 @@ void AnalogSensor::measure() {
|
||||
sensor.sum_ = (sensor.sum_ * 15 + a * 16) / 16;
|
||||
sensor.analog_ = sensor.sum_ / 16;
|
||||
}
|
||||
|
||||
if (sensor.analog_ > 0 && sensor.analog_ < NTC_VREF_MV
|
||||
&& (sensor.last_reading_ + 1 < sensor.analog_ || sensor.last_reading_ > sensor.analog_ + 1)) {
|
||||
sensor.set_value(sensor.offset() + 1 / (1 / T25 + log((double)sensor.analog_ / (NTC_VREF_MV - sensor.analog_) * (Rt / R0)) / Beta)
|
||||
@@ -429,6 +452,7 @@ void AnalogSensor::measure() {
|
||||
} else if (micros() - edge[index] > FREQ_ZERO_TIMEOUT_US && sensor.value() > 0) {
|
||||
sensor.set_value(0);
|
||||
}
|
||||
|
||||
if (sensor.value() != oldval) {
|
||||
changed_ = true;
|
||||
publish_sensor(sensor);
|
||||
@@ -461,6 +485,7 @@ void AnalogSensor::measure() {
|
||||
sensor.polltime_ = uuid::get_uptime(); // remember time of pinchange
|
||||
sensor.poll_ = current_reading;
|
||||
}
|
||||
|
||||
// debounce and check for real pinchange
|
||||
if (uuid::get_uptime() - sensor.polltime_ >= 15 && sensor.poll_ != sensor.last_reading_) {
|
||||
sensor.last_reading_ = sensor.poll_;
|
||||
@@ -475,15 +500,18 @@ void AnalogSensor::measure() {
|
||||
} else if (sensor.type() == AnalogType::TIMER) { // default seconds with factor 1
|
||||
sensor.set_value(sensor.factor() * (sensor.polltime_ - sensor.last_polltime_) / 1000);
|
||||
}
|
||||
|
||||
sensor.last_polltime_ = sensor.polltime_;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (sensor.type() == AnalogType::PULSE && sensor.value() && sensor.polltime_ && sensor.polltime_ < uuid::get_uptime()) {
|
||||
sensor.set_value(0);
|
||||
digitalWrite(sensor.gpio(), sensor.offset());
|
||||
sensor.polltime_ = 0;
|
||||
}
|
||||
|
||||
// see if there is a change and increment # reads
|
||||
if (old_value != sensor.value()) {
|
||||
sensorreads_++;
|
||||
@@ -531,6 +559,7 @@ bool AnalogSensor::update(uint8_t gpio, const char * org_name, double offset, do
|
||||
} else {
|
||||
strlcpy(name, org_name, sizeof(name));
|
||||
}
|
||||
|
||||
// first see if we can find the sensor in our customization list
|
||||
bool found_sensor = false;
|
||||
EMSESP::webCustomizationService.update([&](WebCustomization & settings) {
|
||||
@@ -545,12 +574,14 @@ bool AnalogSensor::update(uint8_t gpio, const char * org_name, double offset, do
|
||||
if (EMSESP::nvs_.isKey(AnalogCustomization.name)) {
|
||||
EMSESP::nvs_.remove(AnalogCustomization.name);
|
||||
}
|
||||
|
||||
settings.analogCustomizations.remove(AnalogCustomization);
|
||||
} else {
|
||||
// update existing record
|
||||
if (!strcmp(name, AnalogCustomization.name) && EMSESP::nvs_.isKey(AnalogCustomization.name)) {
|
||||
EMSESP::nvs_.remove(AnalogCustomization.name);
|
||||
}
|
||||
|
||||
strlcpy(AnalogCustomization.name, name, sizeof(AnalogCustomization.name));
|
||||
AnalogCustomization.offset = offset;
|
||||
AnalogCustomization.factor = factor;
|
||||
@@ -559,9 +590,11 @@ bool AnalogSensor::update(uint8_t gpio, const char * org_name, double offset, do
|
||||
AnalogCustomization.is_system = is_system;
|
||||
LOG_DEBUG("Customizing existing analog GPIO %02d", gpio);
|
||||
}
|
||||
|
||||
return StateUpdateResult::CHANGED; // persist the change
|
||||
}
|
||||
}
|
||||
|
||||
return StateUpdateResult::UNCHANGED;
|
||||
});
|
||||
|
||||
@@ -608,6 +641,7 @@ bool AnalogSensor::updated_values() {
|
||||
changed_ = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -620,14 +654,17 @@ void AnalogSensor::publish_sensor(const Sensor & sensor) const {
|
||||
} else {
|
||||
snprintf(topic, sizeof(topic), "%s%s/%s", F_(analogsensor), "_data", sensor.name());
|
||||
}
|
||||
|
||||
char result[12];
|
||||
if (sensor.type() == AnalogType::DIGITAL_IN || sensor.type() == AnalogType::DIGITAL_OUT) {
|
||||
Helpers::render_boolean(result, sensor.value() != 0);
|
||||
} else {
|
||||
Helpers::render_value(result, sensor.value(), 2); // double
|
||||
}
|
||||
|
||||
Mqtt::queue_publish(topic, result); // always publish as doubles
|
||||
}
|
||||
|
||||
char cmd[COMMAND_MAX_LENGTH];
|
||||
snprintf(cmd, sizeof(cmd), "%s/%s", F_(analogsensor), sensor.name());
|
||||
EMSESP::webSchedulerService.onChange(cmd);
|
||||
@@ -661,6 +698,7 @@ void AnalogSensor::remove_ha_topic(const int8_t type, const uint8_t gpio) const
|
||||
} else {
|
||||
snprintf(topic, sizeof(topic), "sensor/%s/%s_%02d/config", Mqtt::basename().c_str(), F_(analogsensor), gpio);
|
||||
}
|
||||
|
||||
Mqtt::queue_remove_topic(topic);
|
||||
}
|
||||
|
||||
@@ -677,6 +715,7 @@ void AnalogSensor::publish_values(const bool force) {
|
||||
for (const auto & sensor : sensors_) {
|
||||
publish_sensor(sensor);
|
||||
}
|
||||
|
||||
return;
|
||||
} else if (!EMSESP::mqtt_.get_publish_onchange(EMSdevice::DeviceType::SYSTEM)) {
|
||||
return; // wait for first time period
|
||||
@@ -728,10 +767,12 @@ void AnalogSensor::publish_values(const bool force) {
|
||||
snprintf(val_obj, sizeof(val_obj), "value_json['%s']", sensor.name());
|
||||
snprintf(val_cond, sizeof(val_cond), "%s is defined", val_obj);
|
||||
}
|
||||
|
||||
char sample_val[12] = "0";
|
||||
if (sensor.type() == AnalogType::DIGITAL_IN || sensor.type() == AnalogType::DIGITAL_OUT || sensor.type() == AnalogType::PULSE) {
|
||||
Helpers::render_boolean(sample_val, false);
|
||||
}
|
||||
|
||||
// don't bother with value template conditions if using Domoticz which doesn't fully support MQTT Discovery
|
||||
if (Mqtt::discovery_type() == Mqtt::discoveryType::HOMEASSISTANT) {
|
||||
config["val_tpl"] = (std::string) "{{" + val_obj + " if " + val_cond + " else 'None'}}";
|
||||
@@ -840,6 +881,7 @@ bool AnalogSensor::get_value_info(JsonObject output, const char * cmd, const int
|
||||
for (const auto & sensor : sensors_) {
|
||||
output[sensor.name()] = sensor.value();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -848,6 +890,7 @@ bool AnalogSensor::get_value_info(JsonObject output, const char * cmd, const int
|
||||
for (const auto & sensor : sensors_) {
|
||||
get_value_json(output[sensor.name()].to<JsonObject>(), sensor);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -857,6 +900,7 @@ bool AnalogSensor::get_value_info(JsonObject output, const char * cmd, const int
|
||||
output["api_data"] = metrics;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -886,12 +930,14 @@ std::string AnalogSensor::get_metrics_prometheus() {
|
||||
} else {
|
||||
result += (std::string) ", boolean";
|
||||
}
|
||||
|
||||
result += (std::string) ", readable, visible";
|
||||
if (sensor.type() == AnalogType::COUNTER || sensor.type() == AnalogType::RGB || sensor.type() == AnalogType::PULSE
|
||||
|| (sensor.type() >= AnalogType::DIGITAL_OUT && sensor.type() <= AnalogType::PWM_2)
|
||||
|| (sensor.type() >= AnalogType::CNT_0 && sensor.type() <= AnalogType::CNT_2)) {
|
||||
result += (std::string) ", writable";
|
||||
}
|
||||
|
||||
result += (std::string) "\n# TYPE emsesp_" + sensor.name() + " gauge\n";
|
||||
result += (std::string) "emsesp_" + sensor.name() + " ";
|
||||
if (sensor.type() != AnalogType::DIGITAL_OUT && sensor.type() != AnalogType::DIGITAL_IN) {
|
||||
@@ -900,6 +946,7 @@ std::string AnalogSensor::get_metrics_prometheus() {
|
||||
result += (std::string)(sensor.value() == 0 ? "0\n" : "1\n");
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -970,6 +1017,7 @@ bool AnalogSensor::command_setvalue(const char * value, const int8_t gpio) {
|
||||
if (!Helpers::value2bool(value, b)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
val = b ? 1 : 0;
|
||||
}
|
||||
|
||||
@@ -984,6 +1032,7 @@ bool AnalogSensor::command_setvalue(const char * value, const int8_t gpio) {
|
||||
// sensor.set_offset(val);
|
||||
sensor.set_value(val);
|
||||
}
|
||||
|
||||
sensor.set_offset(sensor.value());
|
||||
if (sensor.value() != EMSESP::nvs_.getDouble(sensor.name(), 0)) {
|
||||
EMSESP::nvs_.putDouble(sensor.name(), sensor.value());
|
||||
@@ -1037,16 +1086,19 @@ bool AnalogSensor::command_setvalue(const char * value, const int8_t gpio) {
|
||||
} else if (val < 0) {
|
||||
val = 0;
|
||||
}
|
||||
|
||||
sensor.set_offset(val);
|
||||
sensor.set_value(val);
|
||||
ledcWrite(sensor.gpio(), (uint32_t)(sensor.offset() * 8191 / 100));
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (oldoffset != sensor.offset()) {
|
||||
publish_sensor(sensor);
|
||||
changed_ = true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1054,4 +1106,4 @@ bool AnalogSensor::command_setvalue(const char * value, const int8_t gpio) {
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace emsesp
|
||||
} // namespace emsesp
|
||||
|
||||
@@ -63,6 +63,7 @@ class AnalogSensor {
|
||||
bool is_system() const {
|
||||
return is_system_;
|
||||
}
|
||||
|
||||
void set_is_system(const bool is_system) {
|
||||
is_system_ = is_system;
|
||||
}
|
||||
@@ -70,6 +71,7 @@ class AnalogSensor {
|
||||
double factor() const {
|
||||
return factor_;
|
||||
}
|
||||
|
||||
void set_factor(const double factor) {
|
||||
factor_ = factor;
|
||||
}
|
||||
@@ -81,6 +83,7 @@ class AnalogSensor {
|
||||
void set_uom(const uint8_t uom) {
|
||||
uom_ = uom;
|
||||
}
|
||||
|
||||
uint8_t uom() const {
|
||||
return uom_;
|
||||
}
|
||||
@@ -172,6 +175,7 @@ class AnalogSensor {
|
||||
if (exclude_disabled_system) {
|
||||
return std::count_if(sensors_.begin(), sensors_.end(), [](const Sensor & sensor) { return !sensor.is_system(); });
|
||||
}
|
||||
|
||||
return sensors_.size();
|
||||
}
|
||||
|
||||
@@ -221,4 +225,4 @@ class AnalogSensor {
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -35,6 +35,7 @@ uint8_t Command::process(const char * path, const bool is_admin, const JsonObjec
|
||||
if (!strncmp(path, Mqtt::base().c_str(), Mqtt::base().length())) {
|
||||
path += Mqtt::base().length();
|
||||
}
|
||||
|
||||
SUrlParser p; // parse URL for the path names
|
||||
p.parse(path);
|
||||
|
||||
@@ -102,6 +103,7 @@ uint8_t Command::process(const char * path, const bool is_admin, const JsonObjec
|
||||
if (device_type >= EMSdevice::DeviceType::BOILER) {
|
||||
command_p = parse_command_string(command_p, id_n);
|
||||
}
|
||||
|
||||
if (command_p == nullptr) {
|
||||
// handle dead endpoints like api/system or api/boiler
|
||||
// default to 'value' for all devices
|
||||
@@ -159,6 +161,7 @@ uint8_t Command::process(const char * path, const bool is_admin, const JsonObjec
|
||||
if (device_type1 >= EMSdevice::DeviceType::BOILER) {
|
||||
data_p = parse_command_string(data_p, id_d);
|
||||
}
|
||||
|
||||
if (data_p == nullptr) {
|
||||
return CommandRet::INVALID;
|
||||
}
|
||||
@@ -202,6 +205,7 @@ uint8_t Command::process(const char * path, const bool is_admin, const JsonObjec
|
||||
} else {
|
||||
return json_message(CommandRet::ERROR, "cannot parse command", output); // can't process
|
||||
}
|
||||
|
||||
return return_code;
|
||||
}
|
||||
|
||||
@@ -222,6 +226,7 @@ const char * Command::return_code_string(const uint8_t return_code) {
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
static char s[4]; // static to avoid allocation on each call and loosing scope
|
||||
return Helpers::smallitoa(s, return_code);
|
||||
}
|
||||
@@ -249,6 +254,7 @@ const char * Command::parse_command_string(const char * command, int8_t & id) {
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
lowerCmd[i] = tolower(command[i]);
|
||||
}
|
||||
|
||||
lowerCmd[len] = '\0';
|
||||
|
||||
// check prefix and valid number range, also check 'id'
|
||||
@@ -294,6 +300,7 @@ const char * Command::parse_command_string(const char * command, int8_t & id) {
|
||||
id = id_org;
|
||||
return cmd_org;
|
||||
}
|
||||
|
||||
command++;
|
||||
|
||||
// return null for empty command
|
||||
@@ -315,6 +322,7 @@ const char * Command::get_attribute(const char * cmd) {
|
||||
*breakp = '\0';
|
||||
return breakp + 1;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -365,6 +373,7 @@ uint8_t Command::call(const uint8_t device_type, const char * command, const cha
|
||||
if (command == nullptr) {
|
||||
return CommandRet::NOT_FOUND;
|
||||
}
|
||||
|
||||
char cmd[COMMAND_MAX_LENGTH];
|
||||
strlcpy(cmd, Helpers::toLower(command).c_str(), sizeof(cmd));
|
||||
|
||||
@@ -378,6 +387,7 @@ uint8_t Command::call(const uint8_t device_type, const char * command, const cha
|
||||
if (!strcmp(cmd, F_(commands))) {
|
||||
return Command::list(device_type, output);
|
||||
}
|
||||
|
||||
// for the Commands device, calling a named command executes it (using its stored value)
|
||||
// rather than returning its definition, so skip the value-info lookup and fall through
|
||||
// to the registered command function. The list keywords above are still handled.
|
||||
@@ -430,6 +440,7 @@ uint8_t Command::call(const uint8_t device_type, const char * command, const cha
|
||||
output["message"] = err;
|
||||
LOG_WARNING("Command failed: %s", err.c_str());
|
||||
}
|
||||
|
||||
return CommandRet::NOT_FOUND;
|
||||
}
|
||||
|
||||
@@ -461,6 +472,7 @@ uint8_t Command::call(const uint8_t device_type, const char * command, const cha
|
||||
} else {
|
||||
snprintf(error, sizeof(error), "Command %s: %s failed (%s)", cmd, value, return_code_string(return_code));
|
||||
}
|
||||
|
||||
output.clear();
|
||||
output["message"] = error;
|
||||
LOG_WARNING(error);
|
||||
@@ -475,6 +487,7 @@ uint8_t Command::call(const uint8_t device_type, const char * command, const cha
|
||||
} else {
|
||||
snprintf(info_s, sizeof(info_s), "%s/%s", dname, cmd);
|
||||
}
|
||||
|
||||
if (single_command) {
|
||||
// log as DEBUG (TRACE) regardless if compiled with EMSESP_DEBUG
|
||||
logger_.debug("%sCalled command %s", ro.c_str(), info_s);
|
||||
@@ -552,6 +565,7 @@ void Command::erase_command(const uint8_t device_type, const char * cmd, uint8_t
|
||||
if ((cmd == nullptr) || (strlen(cmd) == 0) || (cmdfunctions_.empty())) {
|
||||
return;
|
||||
}
|
||||
|
||||
const std::string cmd_lower = Helpers::toLower(cmd);
|
||||
auto it = cmdfunctions_.begin();
|
||||
for (auto const & cf : cmdfunctions_) {
|
||||
@@ -559,6 +573,7 @@ void Command::erase_command(const uint8_t device_type, const char * cmd, uint8_t
|
||||
cmdfunctions_.erase(it);
|
||||
return;
|
||||
}
|
||||
|
||||
it++;
|
||||
}
|
||||
}
|
||||
@@ -607,6 +622,7 @@ bool Command::list(const uint8_t device_type, JsonObject output) {
|
||||
sorted_cmds.push_back(tagged_cmd(cf.cmd_, cf.flags_));
|
||||
}
|
||||
}
|
||||
|
||||
sorted_cmds.sort();
|
||||
|
||||
for (const auto & cl : sorted_cmds) {
|
||||
@@ -616,6 +632,7 @@ bool Command::list(const uint8_t device_type, JsonObject output) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -628,6 +645,7 @@ void Command::show(uuid::console::Shell & shell, uint8_t device_type, bool verbo
|
||||
sorted_cmds.push_back(tagged_cmd(cf.cmd_, cf.flags_));
|
||||
}
|
||||
}
|
||||
|
||||
if (device_type == EMSdevice::DeviceType::SYSTEM) {
|
||||
sorted_cmds.emplace_back("settings/showertimer");
|
||||
sorted_cmds.emplace_back("settings/showeralert");
|
||||
@@ -638,6 +656,7 @@ void Command::show(uuid::console::Shell & shell, uint8_t device_type, bool verbo
|
||||
sorted_cmds.emplace_back("ap/enabled");
|
||||
sorted_cmds.emplace_back("syslog/enabled");
|
||||
}
|
||||
|
||||
sorted_cmds.sort(); // sort them
|
||||
|
||||
// if not in verbose mode, just print them on a single line and exit
|
||||
@@ -650,6 +669,7 @@ void Command::show(uuid::console::Shell & shell, uint8_t device_type, bool verbo
|
||||
shell.print(cl);
|
||||
shell.print(" ");
|
||||
}
|
||||
|
||||
shell.println();
|
||||
return;
|
||||
}
|
||||
@@ -668,6 +688,7 @@ void Command::show(uuid::console::Shell & shell, uint8_t device_type, bool verbo
|
||||
while (i++ < 30) {
|
||||
shell.print(' ');
|
||||
}
|
||||
|
||||
shell.print(COLOR_BRIGHT_CYAN);
|
||||
if (cf.has_flags(CommandFlag::CMD_FLAG_HC)) {
|
||||
shell.print(Helpers::translated_word(FL_(tag_hcx)));
|
||||
@@ -685,12 +706,14 @@ void Command::show(uuid::console::Shell & shell, uint8_t device_type, bool verbo
|
||||
shell.print(Helpers::translated_word(FL_(tag_srcx)));
|
||||
shell.print(' ');
|
||||
}
|
||||
|
||||
shell.print(Helpers::translated_word(cf.description_));
|
||||
if (!cf.has_flags(CommandFlag::ADMIN_ONLY)) {
|
||||
shell.print(' ');
|
||||
shell.print(COLOR_BRIGHT_GREEN);
|
||||
shell.print('*');
|
||||
}
|
||||
|
||||
shell.print(COLOR_RESET);
|
||||
shell.println();
|
||||
}
|
||||
@@ -747,6 +770,7 @@ void Command::show_devices(uuid::console::Shell & shell) {
|
||||
if (EMSESP::sensor_enabled()) {
|
||||
shell.printf("%s ", EMSdevice::device_type_2_device_name(EMSdevice::DeviceType::TEMPERATURESENSOR));
|
||||
}
|
||||
|
||||
if (EMSESP::analog_enabled()) {
|
||||
shell.printf("%s ", EMSdevice::device_type_2_device_name(EMSdevice::DeviceType::ANALOGSENSOR));
|
||||
}
|
||||
@@ -759,6 +783,7 @@ void Command::show_devices(uuid::console::Shell & shell) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
shell.println();
|
||||
}
|
||||
|
||||
@@ -788,6 +813,7 @@ void Command::show_all(uuid::console::Shell & shell) {
|
||||
if (EMSESP::sensor_enabled()) {
|
||||
show(shell, EMSdevice::DeviceType::TEMPERATURESENSOR, true);
|
||||
}
|
||||
|
||||
if (EMSESP::analog_enabled()) {
|
||||
show(shell, EMSdevice::DeviceType::ANALOGSENSOR, true);
|
||||
}
|
||||
@@ -811,6 +837,7 @@ uint8_t Command::json_message(uint8_t error_code, const char * message, const Js
|
||||
} else {
|
||||
output["message"] = message;
|
||||
}
|
||||
|
||||
return error_code;
|
||||
}
|
||||
|
||||
@@ -826,6 +853,7 @@ std::string SUrlParser::path() {
|
||||
s += f;
|
||||
s += "/";
|
||||
}
|
||||
|
||||
s.pop_back(); // deleting last letter, that is slash '/'
|
||||
return std::string(s);
|
||||
}
|
||||
@@ -858,12 +886,14 @@ bool SUrlParser::parse(const char * uri) {
|
||||
m_folders.push_back(s);
|
||||
s.clear();
|
||||
}
|
||||
|
||||
t = Type::folder;
|
||||
} else if (*c == '?' && (t == Type::folder || t == Type::begin)) {
|
||||
if (s.length() > 0) {
|
||||
m_folders.push_back(s);
|
||||
s.clear();
|
||||
}
|
||||
|
||||
t = Type::param;
|
||||
} else if (*c == '=' && (t == Type::param || t == Type::begin)) {
|
||||
m_keysvalues[s] = "";
|
||||
@@ -877,6 +907,7 @@ bool SUrlParser::parse(const char * uri) {
|
||||
m_keysvalues[s] = "";
|
||||
last_param = s;
|
||||
}
|
||||
|
||||
t = Type::param;
|
||||
s.clear();
|
||||
} else if (*c == '\0' && s.length() > 0) {
|
||||
@@ -888,11 +919,13 @@ bool SUrlParser::parse(const char * uri) {
|
||||
m_keysvalues[s] = "";
|
||||
last_param = s;
|
||||
}
|
||||
|
||||
s.clear();
|
||||
} else if (*c == '\0' && s.length() == 0) {
|
||||
if (t == Type::param && last_param.length() > 0) {
|
||||
m_keysvalues[last_param] = "";
|
||||
}
|
||||
|
||||
s.clear();
|
||||
} else {
|
||||
s += *c;
|
||||
|
||||
+4
-1
@@ -86,12 +86,15 @@ class Command {
|
||||
inline void add_flags(uint8_t flags) {
|
||||
flags_ |= flags;
|
||||
}
|
||||
|
||||
inline bool has_flags(uint8_t flags) const {
|
||||
return (flags_ & flags) == flags;
|
||||
}
|
||||
|
||||
inline void remove_flags(uint8_t flags) {
|
||||
flags_ &= ~flags;
|
||||
}
|
||||
|
||||
inline uint8_t flags() const {
|
||||
return flags_;
|
||||
}
|
||||
@@ -183,4 +186,4 @@ class SUrlParser {
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -55,6 +55,7 @@ static void console_log_level(Shell & shell, const std::vector<std::string> & ar
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
shell.printfln(F_(log_level_fmt), uuid::log::format_level_uppercase(shell.log_level()));
|
||||
}
|
||||
|
||||
@@ -224,6 +225,7 @@ static void setup_commands(std::shared_ptr<Commands> const & commands) {
|
||||
return StateUpdateResult::CHANGED;
|
||||
}
|
||||
}
|
||||
|
||||
// otherwise add a new admin user
|
||||
securitySettings.users.emplace_back(F_(admin), password2.c_str(), true);
|
||||
return StateUpdateResult::CHANGED;
|
||||
@@ -395,6 +397,7 @@ static void setup_commands(std::shared_ptr<Commands> const & commands) {
|
||||
if (!data.empty()) {
|
||||
data += " ";
|
||||
}
|
||||
|
||||
data += arg;
|
||||
}
|
||||
|
||||
@@ -509,6 +512,7 @@ static void setup_commands(std::shared_ptr<Commands> const & commands) {
|
||||
if (device_type >= EMSdevice::DeviceType::BOILER) {
|
||||
cmd = Command::parse_command_string(cmd, id); // extract hc or dhw
|
||||
}
|
||||
|
||||
if (cmd == nullptr) {
|
||||
cmd = F_(values);
|
||||
}
|
||||
@@ -544,6 +548,7 @@ static void setup_commands(std::shared_ptr<Commands> const & commands) {
|
||||
shell.println(data.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
serializeJsonPretty(doc, shell);
|
||||
shell.println();
|
||||
return;
|
||||
@@ -575,6 +580,7 @@ static void setup_commands(std::shared_ptr<Commands> const & commands) {
|
||||
devices_list.emplace_back(EMSdevice::device_type_2_device_name(device_class.first));
|
||||
}
|
||||
}
|
||||
|
||||
return devices_list;
|
||||
} else if (current_arguments.size() == 1) {
|
||||
uint8_t device_type = EMSdevice::device_name_2_device_type(current_arguments[0].c_str());
|
||||
@@ -585,6 +591,7 @@ static void setup_commands(std::shared_ptr<Commands> const & commands) {
|
||||
command_list.emplace_back(cf.cmd_);
|
||||
}
|
||||
}
|
||||
|
||||
return command_list;
|
||||
}
|
||||
}
|
||||
@@ -612,6 +619,7 @@ void EMSESPShell::stopped() {
|
||||
if (has_flags(CommandFlags::ADMIN)) {
|
||||
logger().log(LogLevel::INFO, LogFacility::AUTH, F("Admin session closed on console %s"), console_name().c_str());
|
||||
}
|
||||
|
||||
logger().log(LogLevel::INFO, LogFacility::CONSOLE, F("User session closed on console %s"), console_name().c_str());
|
||||
}
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@ uint8_t EMSdevice::count_entities() {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
@@ -40,6 +41,7 @@ uint8_t EMSdevice::count_entities_fav() {
|
||||
for (const auto & dv : devicevalues_) {
|
||||
count += dv.has_state(DeviceValueState::DV_FAVORITE) && dv.hasValue();
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
@@ -50,6 +52,7 @@ bool EMSdevice::has_entities() const {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool found = false;
|
||||
EMSESP::webCustomizationService.read([&](WebCustomization & settings) {
|
||||
for (const auto & entityCustomization : settings.entityCustomizations) {
|
||||
@@ -84,6 +87,7 @@ uint8_t EMSdevice::tag_to_flag(const uint8_t tag) {
|
||||
} else if (tag >= DeviceValueTAG::TAG_SRC1 && tag <= DeviceValueTAG::TAG_SRC16) {
|
||||
return CommandFlag::CMD_FLAG_SRC;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -112,6 +116,7 @@ const char * EMSdevice::brand_to_cstr() const {
|
||||
if (!custom_brand().empty()) {
|
||||
return custom_brand().c_str();
|
||||
}
|
||||
|
||||
switch (brand_) {
|
||||
case EMSdevice::Brand::BOSCH:
|
||||
return F_(bosch);
|
||||
@@ -192,6 +197,7 @@ const char * EMSdevice::device_type_2_device_name_translated() {
|
||||
if (flags_ == EMS_DEVICE_FLAG_HEATPUMP) {
|
||||
return Helpers::translated_word(FL_(boiler_hp_device));
|
||||
}
|
||||
|
||||
return Helpers::translated_word(FL_(boiler_device));
|
||||
case DeviceType::THERMOSTAT:
|
||||
return Helpers::translated_word(FL_(thermostat_device));
|
||||
@@ -224,6 +230,7 @@ const char * EMSdevice::device_type_2_device_name_translated() {
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return Helpers::translated_word(FL_(unknown_device));
|
||||
}
|
||||
|
||||
@@ -243,45 +250,59 @@ uint8_t EMSdevice::device_name_2_device_type(const char * topic) {
|
||||
if (!strcmp(lowtopic, F_(boiler))) {
|
||||
return DeviceType::BOILER;
|
||||
}
|
||||
|
||||
if (!strcmp(lowtopic, F_(thermostat))) {
|
||||
return DeviceType::THERMOSTAT;
|
||||
}
|
||||
|
||||
if (!strcmp(lowtopic, F_(scheduler))) {
|
||||
return DeviceType::SCHEDULER;
|
||||
}
|
||||
|
||||
if (!strcmp(lowtopic, F_(heatpump))) {
|
||||
return DeviceType::HEATPUMP;
|
||||
}
|
||||
|
||||
if (!strcmp(lowtopic, F_(solar))) {
|
||||
return DeviceType::SOLAR;
|
||||
}
|
||||
|
||||
if (!strcmp(lowtopic, F_(mixer))) {
|
||||
return DeviceType::MIXER;
|
||||
}
|
||||
|
||||
if (!strcmp(lowtopic, F_(switcher))) {
|
||||
return DeviceType::SWITCH;
|
||||
}
|
||||
|
||||
if (!strcmp(lowtopic, F_(gateway))) {
|
||||
return DeviceType::GATEWAY;
|
||||
}
|
||||
|
||||
if (!strcmp(lowtopic, F_(alert))) {
|
||||
return DeviceType::ALERT;
|
||||
}
|
||||
|
||||
if (!strcmp(lowtopic, F_(extension))) {
|
||||
return DeviceType::EXTENSION;
|
||||
}
|
||||
|
||||
if (!strcmp(lowtopic, F_(heatsource))) {
|
||||
return DeviceType::HEATSOURCE;
|
||||
}
|
||||
|
||||
if (!strcmp(lowtopic, F_(ventilation))) {
|
||||
return DeviceType::VENTILATION;
|
||||
}
|
||||
|
||||
if (!strcmp(lowtopic, F_(water))) {
|
||||
return DeviceType::WATER;
|
||||
}
|
||||
|
||||
if (!strcmp(lowtopic, F_(pool))) {
|
||||
return DeviceType::POOL;
|
||||
}
|
||||
|
||||
if (!strcmp(lowtopic, F_(connect))) {
|
||||
return DeviceType::CONNECT;
|
||||
}
|
||||
@@ -290,18 +311,23 @@ uint8_t EMSdevice::device_name_2_device_type(const char * topic) {
|
||||
if (!strcmp(lowtopic, F_(custom))) {
|
||||
return DeviceType::CUSTOM;
|
||||
}
|
||||
|
||||
if (!strcmp(lowtopic, F_(temperaturesensor))) {
|
||||
return DeviceType::TEMPERATURESENSOR;
|
||||
}
|
||||
|
||||
if (!strcmp(lowtopic, F_(analogsensor))) {
|
||||
return DeviceType::ANALOGSENSOR;
|
||||
}
|
||||
|
||||
if (!strcmp(lowtopic, F_(scheduler))) {
|
||||
return DeviceType::SCHEDULER;
|
||||
}
|
||||
|
||||
if (!strcmp(lowtopic, F_(commands))) {
|
||||
return DeviceType::COMMAND;
|
||||
}
|
||||
|
||||
if (!strcmp(lowtopic, F_(system))) {
|
||||
return DeviceType::SYSTEM;
|
||||
}
|
||||
@@ -403,6 +429,7 @@ bool EMSdevice::is_fetch(uint16_t telegram_id, uint8_t len) const {
|
||||
return tf.fetch_ && tf.length_ >= len;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -413,6 +440,7 @@ bool EMSdevice::is_received(uint16_t telegram_id) const {
|
||||
return tf.received_;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -423,6 +451,7 @@ bool EMSdevice::has_tags(const int8_t tag) const {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -433,6 +462,7 @@ bool EMSdevice::has_cmd(const char * cmd, const int8_t id) const {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -471,6 +501,7 @@ void EMSdevice::show_telegram_handlers(uuid::console::Shell & shell) const {
|
||||
if (telegram_functions_.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
// colored list of type-ids
|
||||
shell.printf(" This %s will listen to telegram type IDs: ", device_type_name().c_str());
|
||||
@@ -492,6 +523,7 @@ void EMSdevice::show_telegram_handlers(uuid::console::Shell & shell) const {
|
||||
shell.printf("0x%02X ", tf.telegram_type_id_);
|
||||
}
|
||||
}
|
||||
|
||||
shell.println();
|
||||
shell.printf(" Fetched telegram type IDs: ");
|
||||
for (const auto & tf : telegram_functions_) {
|
||||
@@ -499,6 +531,7 @@ void EMSdevice::show_telegram_handlers(uuid::console::Shell & shell) const {
|
||||
shell.printf("0x%02X ", tf.telegram_type_id_);
|
||||
}
|
||||
}
|
||||
|
||||
shell.println();
|
||||
shell.printf(" Pending telegram type IDs: ");
|
||||
for (const auto & tf : telegram_functions_) {
|
||||
@@ -506,11 +539,13 @@ void EMSdevice::show_telegram_handlers(uuid::console::Shell & shell) const {
|
||||
shell.printf("0x%02X ", tf.telegram_type_id_);
|
||||
}
|
||||
}
|
||||
|
||||
shell.println();
|
||||
shell.printf(" Ignored telegram type IDs: ");
|
||||
for (auto handlers : handlers_ignored_) {
|
||||
shell.printf("0x%02X ", handlers);
|
||||
}
|
||||
|
||||
shell.println();
|
||||
}
|
||||
|
||||
@@ -525,6 +560,7 @@ char * EMSdevice::show_telegram_handlers(char * result, const size_t len, const
|
||||
if (i++ > 0) {
|
||||
strlcat(result, " ", len);
|
||||
}
|
||||
|
||||
strlcat(result, Helpers::hextoa(tf.telegram_type_id_, true).c_str(), len);
|
||||
}
|
||||
}
|
||||
@@ -535,6 +571,7 @@ char * EMSdevice::show_telegram_handlers(char * result, const size_t len, const
|
||||
if (i++ > 0) {
|
||||
strlcat(result, " ", len);
|
||||
}
|
||||
|
||||
strlcat(result, Helpers::hextoa(h).c_str(), len);
|
||||
}
|
||||
}
|
||||
@@ -548,6 +585,7 @@ void EMSdevice::add_handlers_ignored(const uint16_t handler) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
handlers_ignored_.push_back(handler);
|
||||
}
|
||||
|
||||
@@ -623,6 +661,7 @@ void EMSdevice::add_device_value(int8_t tag, // to
|
||||
} else if (uom == DeviceValueUOM::PERCENT) {
|
||||
*(uint8_t *)(value_p) = use_max ? static_cast<uint8_t>(max) : (test_active ? EMS_VALUE_DEFAULT_UINT8_DUMMY : EMS_VALUE_DEFAULT_UINT8);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -662,6 +701,7 @@ void EMSdevice::add_device_value(int8_t tag, // to
|
||||
if (has_custom_name) {
|
||||
custom_fullname = entity_id.substr(custom_name_pos + 1);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -692,6 +732,7 @@ void EMSdevice::erase_device_values() {
|
||||
Command::erase_command(device_type_, dv.short_name, tag_to_flag(dv.tag));
|
||||
}
|
||||
}
|
||||
|
||||
devicevalues_.clear();
|
||||
telegram_functions_.clear();
|
||||
handlers_ignored_.clear();
|
||||
@@ -802,6 +843,7 @@ bool EMSdevice::is_readable(const void * value_p) const {
|
||||
return !dv.has_state(DeviceValueState::DV_API_MQTT_EXCLUDE);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -814,6 +856,7 @@ bool EMSdevice::is_readonly(const std::string & cmd, const int8_t id) const {
|
||||
return dv.has_state(DeviceValueState::DV_READONLY);
|
||||
}
|
||||
}
|
||||
|
||||
return true; // not found, no write
|
||||
}
|
||||
|
||||
@@ -824,6 +867,7 @@ bool EMSdevice::has_command(const void * value_p) const {
|
||||
return dv.has_cmd && !dv.has_state(DeviceValueState::DV_READONLY);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -879,8 +923,10 @@ void EMSdevice::publish_value(void * value_p) const {
|
||||
strlcpy(payload, enum_str, sizeof(payload));
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case DeviceValueType::UINT16:
|
||||
Helpers::render_value(payload, *(uint16_t *)(value_p), num_op, fahrenheit);
|
||||
break;
|
||||
@@ -905,6 +951,7 @@ void EMSdevice::publish_value(void * value_p) const {
|
||||
if (Helpers::hasValue((char *)(value_p))) {
|
||||
Helpers::render_string(payload, (char *)(value_p), sizeof(payload));
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@@ -913,6 +960,7 @@ void EMSdevice::publish_value(void * value_p) const {
|
||||
if (Mqtt::publish_single() && payload[0] != '\0') {
|
||||
Mqtt::queue_publish(topic, payload);
|
||||
}
|
||||
|
||||
// check scheduler for on change
|
||||
char cmd[COMMAND_MAX_LENGTH];
|
||||
if (dv.tag >= DeviceValueTAG::TAG_HC1) {
|
||||
@@ -920,6 +968,7 @@ void EMSdevice::publish_value(void * value_p) const {
|
||||
} else {
|
||||
snprintf(cmd, sizeof(cmd), "%s/%s", device_type_2_device_name(device_type_), (dv.short_name));
|
||||
}
|
||||
|
||||
EMSESP::webSchedulerService.onChange(cmd);
|
||||
}
|
||||
}
|
||||
@@ -933,6 +982,7 @@ std::string EMSdevice::get_value_uom(const std::string & shortname) const {
|
||||
if ((dv.uom == DeviceValueUOM::NONE) || (dv.uom == DeviceValueUOM::MINUTES)) {
|
||||
break;
|
||||
}
|
||||
|
||||
return EMSdevice::uom_to_string(dv.uom);
|
||||
}
|
||||
}
|
||||
@@ -949,6 +999,7 @@ bool EMSdevice::export_values(uint8_t device_type, JsonObject output, const int8
|
||||
has_value |= emsdevice->generate_values(output, id, (id < 1), output_target); // use nested for id -1 and 0
|
||||
}
|
||||
}
|
||||
|
||||
return has_value;
|
||||
}
|
||||
|
||||
@@ -962,10 +1013,12 @@ bool EMSdevice::export_values(uint8_t device_type, JsonObject output, const int8
|
||||
output_hc = output[EMSdevice::tag_to_mqtt(tag)].to<JsonObject>();
|
||||
nest_created = true;
|
||||
}
|
||||
|
||||
has_value |= emsdevice->generate_values(output_hc, tag, true, output_target); // use nested for id -1 and 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return has_value;
|
||||
}
|
||||
|
||||
@@ -1083,12 +1136,14 @@ void EMSdevice::generate_values_web(JsonObject output, const bool is_dashboard)
|
||||
l.add(Helpers::render_boolean(result, false, true));
|
||||
l.add(Helpers::render_boolean(result, true, true));
|
||||
}
|
||||
|
||||
// add command help template
|
||||
else if (dv.type == DeviceValueType::STRING || (dv.type == DeviceValueType::CMD && dv.uom == DeviceValueUOM::NONE)) {
|
||||
if (dv.options_size == 1) {
|
||||
obj["h"] = dv.options_single[0]; // NOT translated
|
||||
}
|
||||
}
|
||||
|
||||
// handle INTs
|
||||
else {
|
||||
// add min and max values, if available
|
||||
@@ -1182,6 +1237,7 @@ void EMSdevice::generate_values_web_customization(JsonArray output) {
|
||||
} else {
|
||||
obj["n"] = "!" + std::string(fullname); // prefix commands with a !
|
||||
}
|
||||
|
||||
if (dv.has_tag()) {
|
||||
obj["t"] = tag_to_string(dv.tag);
|
||||
}
|
||||
@@ -1229,6 +1285,7 @@ void EMSdevice::generate_values_web_customization(JsonArray output) {
|
||||
obj["w"] = false; // not writeable as it won't be shown
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1243,6 +1300,7 @@ void EMSdevice::set_climate_minmax(int8_t tag, int16_t min, uint32_t max) {
|
||||
dv.max = max;
|
||||
dv.remove_state(DeviceValueState::DV_HA_CONFIG_CREATED);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -1254,6 +1312,7 @@ void EMSdevice::setValueEnum(const void * value_p, const char * const ** options
|
||||
if (dv.options != options && Mqtt::ha_enabled()) {
|
||||
dv.remove_state(DeviceValueState::DV_HA_CONFIG_CREATED);
|
||||
}
|
||||
|
||||
dv.options = options;
|
||||
dv.options_size = Helpers::count_items(options);
|
||||
break;
|
||||
@@ -1324,6 +1383,7 @@ void EMSdevice::getCustomizationEntities(std::vector<std::string> & entity_ids)
|
||||
strcpy(name, tag_to_mqtt(dv.tag));
|
||||
strcat(name, "/");
|
||||
}
|
||||
|
||||
strcat(name, dv.short_name);
|
||||
std::string entity_name = name;
|
||||
|
||||
@@ -1335,6 +1395,7 @@ void EMSdevice::getCustomizationEntities(std::vector<std::string> & entity_ids)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_set && (mask || dv.has_custom_fullname())) {
|
||||
if (!dv.has_custom_fullname()) {
|
||||
entity_ids.push_back(Helpers::hextoa(mask, false) + entity_name);
|
||||
@@ -1403,6 +1464,7 @@ void EMSdevice::dump_devicevalue_info() {
|
||||
Serial.print("\\|");
|
||||
}
|
||||
}
|
||||
|
||||
Serial.print(']');
|
||||
break;
|
||||
|
||||
@@ -1454,6 +1516,7 @@ void EMSdevice::dump_devicevalue_info() {
|
||||
Serial.print(dv.max);
|
||||
Serial.print(")");
|
||||
}
|
||||
|
||||
Serial.print(",");
|
||||
|
||||
// uom
|
||||
@@ -1462,6 +1525,7 @@ void EMSdevice::dump_devicevalue_info() {
|
||||
} else {
|
||||
Serial.print(DeviceValue::DeviceValueUOM_s[dv.uom]);
|
||||
}
|
||||
|
||||
Serial.print(",");
|
||||
|
||||
// writeable flag
|
||||
@@ -1576,9 +1640,11 @@ bool EMSdevice::get_value_info(JsonObject output, const char * cmd, const int8_t
|
||||
if (!strcmp(cmd, F_(info))) {
|
||||
return export_values(device_type(), output, tag, EMSdevice::OUTPUT_TARGET::API_VERBOSE);
|
||||
}
|
||||
|
||||
if (!strcmp(cmd, F_(values))) {
|
||||
return export_values(device_type(), output, tag, EMSdevice::OUTPUT_TARGET::API_SHORTNAMES);
|
||||
}
|
||||
|
||||
if (!strcmp(cmd, F_(entities))) {
|
||||
char name[30];
|
||||
for (auto & dv : devicevalues_) {
|
||||
@@ -1589,14 +1655,17 @@ bool EMSdevice::get_value_info(JsonObject output, const char * cmd, const int8_t
|
||||
get_value_json(output[name].to<JsonObject>(), dv);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!strcmp(cmd, F_(metrics))) {
|
||||
std::string metrics = get_metrics_prometheus(tag);
|
||||
if (!metrics.empty()) {
|
||||
output["api_data"] = metrics;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1613,6 +1682,7 @@ bool EMSdevice::get_value_info(JsonObject output, const char * cmd, const int8_t
|
||||
return Command::get_attribute(output, cmd_s, attribute_s);
|
||||
}
|
||||
}
|
||||
|
||||
return false; // not found, but don't return a message error yet
|
||||
}
|
||||
|
||||
@@ -1649,11 +1719,13 @@ void EMSdevice::get_value_json(JsonObject json, DeviceValue & dv) {
|
||||
json[value] = Helpers::translated_word(dv.options[*(uint8_t *)(dv.value_p)]); // text
|
||||
}
|
||||
}
|
||||
|
||||
json[type] = F_(enum);
|
||||
JsonArray enum_ = json[F_(enum)].to<JsonArray>();
|
||||
for (uint8_t i = 0; i < dv.options_size; i++) {
|
||||
enum_.add(Helpers::translated_word(dv.options[i]));
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1661,6 +1733,7 @@ void EMSdevice::get_value_json(JsonObject json, DeviceValue & dv) {
|
||||
if (Helpers::hasValue(*(uint16_t *)(dv.value_p))) {
|
||||
json[value] = serialized(Helpers::render_value(val, *(uint16_t *)(dv.value_p), dv.numeric_operator, fahrenheit));
|
||||
}
|
||||
|
||||
json[type] = F_(number);
|
||||
break;
|
||||
|
||||
@@ -1668,6 +1741,7 @@ void EMSdevice::get_value_json(JsonObject json, DeviceValue & dv) {
|
||||
if (Helpers::hasValue(*(uint8_t *)(dv.value_p))) {
|
||||
json[value] = serialized(Helpers::render_value(val, *(uint8_t *)(dv.value_p), dv.numeric_operator, fahrenheit));
|
||||
}
|
||||
|
||||
json[type] = F_(number);
|
||||
break;
|
||||
|
||||
@@ -1675,6 +1749,7 @@ void EMSdevice::get_value_json(JsonObject json, DeviceValue & dv) {
|
||||
if (Helpers::hasValue(*(int16_t *)(dv.value_p))) {
|
||||
json[value] = serialized(Helpers::render_value(val, *(int16_t *)(dv.value_p), dv.numeric_operator, fahrenheit));
|
||||
}
|
||||
|
||||
json[type] = F_(number);
|
||||
break;
|
||||
|
||||
@@ -1682,6 +1757,7 @@ void EMSdevice::get_value_json(JsonObject json, DeviceValue & dv) {
|
||||
if (Helpers::hasValue(*(int8_t *)(dv.value_p))) {
|
||||
json[value] = serialized(Helpers::render_value(val, *(int8_t *)(dv.value_p), dv.numeric_operator, fahrenheit));
|
||||
}
|
||||
|
||||
json[type] = F_(number);
|
||||
break;
|
||||
|
||||
@@ -1690,6 +1766,7 @@ void EMSdevice::get_value_json(JsonObject json, DeviceValue & dv) {
|
||||
if (Helpers::hasValue(*(uint32_t *)(dv.value_p))) {
|
||||
json[value] = serialized(Helpers::render_value(val, *(uint32_t *)(dv.value_p), dv.numeric_operator));
|
||||
}
|
||||
|
||||
json[type] = F_(number);
|
||||
break;
|
||||
|
||||
@@ -1700,6 +1777,7 @@ void EMSdevice::get_value_json(JsonObject json, DeviceValue & dv) {
|
||||
json["index"] = value_b ? 1 : 0;
|
||||
Mqtt::add_value_bool(json, value, value_b);
|
||||
}
|
||||
|
||||
json[type] = ("boolean");
|
||||
break;
|
||||
|
||||
@@ -1707,6 +1785,7 @@ void EMSdevice::get_value_json(JsonObject json, DeviceValue & dv) {
|
||||
if (Helpers::hasValue(*(uint32_t *)(dv.value_p))) {
|
||||
json[value] = serialized(Helpers::render_value(val, *(uint32_t *)(dv.value_p), dv.numeric_operator));
|
||||
}
|
||||
|
||||
json[type] = F_(number);
|
||||
break;
|
||||
|
||||
@@ -1715,6 +1794,7 @@ void EMSdevice::get_value_json(JsonObject json, DeviceValue & dv) {
|
||||
char s[55];
|
||||
json[value] = Helpers::render_string(s, (char *)(dv.value_p), sizeof(s));
|
||||
}
|
||||
|
||||
json[type] = ("string");
|
||||
break;
|
||||
|
||||
@@ -1730,6 +1810,7 @@ void EMSdevice::get_value_json(JsonObject json, DeviceValue & dv) {
|
||||
} else {
|
||||
json[type] = F_(command);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -1773,11 +1854,13 @@ std::string EMSdevice::get_metrics_prometheus(const int8_t tag) {
|
||||
if (tag >= 0 && tag != dv.tag) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// only count supported types
|
||||
if (dv.hasValue() && is_supported_type(dv.type)) {
|
||||
entity_count++;
|
||||
}
|
||||
}
|
||||
|
||||
result.reserve(160 * entity_count);
|
||||
|
||||
for (auto & dv : devicevalues_) {
|
||||
@@ -1799,30 +1882,35 @@ std::string EMSdevice::get_metrics_prometheus(const int8_t tag) {
|
||||
has_value = true;
|
||||
metric_value = (bool)*(uint8_t *)(dv.value_p) ? 1.0 : 0.0;
|
||||
}
|
||||
|
||||
break;
|
||||
case DeviceValueType::UINT8:
|
||||
if (Helpers::hasValue(*(uint8_t *)(dv.value_p))) {
|
||||
has_value = true;
|
||||
metric_value = *(uint8_t *)(dv.value_p);
|
||||
}
|
||||
|
||||
break;
|
||||
case DeviceValueType::INT8:
|
||||
if (Helpers::hasValue(*(int8_t *)(dv.value_p))) {
|
||||
has_value = true;
|
||||
metric_value = *(int8_t *)(dv.value_p);
|
||||
}
|
||||
|
||||
break;
|
||||
case DeviceValueType::UINT16:
|
||||
if (Helpers::hasValue(*(uint16_t *)(dv.value_p))) {
|
||||
has_value = true;
|
||||
metric_value = *(uint16_t *)(dv.value_p);
|
||||
}
|
||||
|
||||
break;
|
||||
case DeviceValueType::INT16:
|
||||
if (Helpers::hasValue(*(int16_t *)(dv.value_p))) {
|
||||
has_value = true;
|
||||
metric_value = *(int16_t *)(dv.value_p);
|
||||
}
|
||||
|
||||
break;
|
||||
case DeviceValueType::UINT24:
|
||||
case DeviceValueType::UINT32:
|
||||
@@ -1831,12 +1919,14 @@ std::string EMSdevice::get_metrics_prometheus(const int8_t tag) {
|
||||
has_value = true;
|
||||
metric_value = *(uint32_t *)(dv.value_p);
|
||||
}
|
||||
|
||||
break;
|
||||
case DeviceValueType::ENUM:
|
||||
if (*(uint8_t *)(dv.value_p) < dv.options_size) {
|
||||
has_value = true;
|
||||
metric_value = *(uint8_t *)(dv.value_p);
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@@ -1890,8 +1980,10 @@ std::string EMSdevice::get_metrics_prometheus(const int8_t tag) {
|
||||
if (i > 0) {
|
||||
enum_mapping += "; ";
|
||||
}
|
||||
|
||||
enum_mapping += std::to_string(i) + ": " + std::string(Helpers::translated_word(dv.options[i]));
|
||||
}
|
||||
|
||||
enum_mapping += ")";
|
||||
} else if (dv.uom != DeviceValueUOM::NONE) {
|
||||
uom_str = uom_to_string(dv.uom);
|
||||
@@ -1901,6 +1993,7 @@ std::string EMSdevice::get_metrics_prometheus(const int8_t tag) {
|
||||
if (!uom_str.empty()) {
|
||||
help_line += ", " + uom_str;
|
||||
}
|
||||
|
||||
if (!enum_mapping.empty()) {
|
||||
help_line += ", enum, " + enum_mapping;
|
||||
}
|
||||
@@ -1912,9 +2005,11 @@ std::string EMSdevice::get_metrics_prometheus(const int8_t tag) {
|
||||
if (readable) {
|
||||
help_line += ", readable";
|
||||
}
|
||||
|
||||
if (writeable) {
|
||||
help_line += ", writeable";
|
||||
}
|
||||
|
||||
if (visible) {
|
||||
help_line += ", visible";
|
||||
}
|
||||
@@ -1940,6 +2035,7 @@ std::string EMSdevice::get_metrics_prometheus(const int8_t tag) {
|
||||
if (!circuit_label.empty()) {
|
||||
result += "{circuit=\"" + circuit_label + "\"}";
|
||||
}
|
||||
|
||||
result += " ";
|
||||
|
||||
char val_str[30];
|
||||
@@ -1959,6 +2055,7 @@ std::string EMSdevice::get_metrics_prometheus(const int8_t tag) {
|
||||
} else {
|
||||
snprintf(val_str, sizeof(val_str), "%.2f", final_value);
|
||||
}
|
||||
|
||||
result += val_str;
|
||||
result += "\n";
|
||||
}
|
||||
@@ -2092,6 +2189,7 @@ bool EMSdevice::generate_values(JsonObject output, const int8_t tag_filter, cons
|
||||
if (dv.numeric_operator == DeviceValueNumOp::DV_NUMOP_DIV60) {
|
||||
time_value /= 60; // sometimes we need to divide by 60
|
||||
}
|
||||
|
||||
if (output_target == OUTPUT_TARGET::API_VERBOSE || output_target == OUTPUT_TARGET::CONSOLE) {
|
||||
char time_s[60];
|
||||
snprintf(time_s,
|
||||
@@ -2125,6 +2223,7 @@ bool EMSdevice::generate_values(JsonObject output, const int8_t tag_filter, cons
|
||||
if (fahrenheit) {
|
||||
v = (v - (32 * (fahrenheit - 1))) / 1.8; // reset to °C
|
||||
}
|
||||
|
||||
if (v < dv.min) {
|
||||
dv.min = v;
|
||||
dv.remove_state(DeviceValueState::DV_HA_CONFIG_CREATED);
|
||||
@@ -2174,6 +2273,7 @@ void EMSdevice::mqtt_ha_entity_config_create() {
|
||||
} else {
|
||||
dv.add_state(DeviceValueState::DV_HA_CLIMATE_NO_RT);
|
||||
}
|
||||
|
||||
dv.add_state(DeviceValueState::DV_HA_CONFIG_CREATED);
|
||||
count++;
|
||||
}
|
||||
@@ -2205,9 +2305,11 @@ void EMSdevice::mqtt_ha_entity_config_create() {
|
||||
if ((d.tag != dv.tag) || (d.type != DeviceValueType::ENUM)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!strcmp(d.short_name, FL_(mode)[0]) && (d.options_size > 0)) {
|
||||
mode_options = d.options;
|
||||
}
|
||||
|
||||
if (!strcmp(d.short_name, FL_(icon)[0])) {
|
||||
uint8_t val = *(uint8_t *)(d.value_p);
|
||||
if (val != 0 && val < d.options_size) {
|
||||
@@ -2215,6 +2317,7 @@ void EMSdevice::mqtt_ha_entity_config_create() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Mqtt::publish_ha_climate_config(dv, true, mode_options, false, icon);
|
||||
count++;
|
||||
}
|
||||
@@ -2250,6 +2353,7 @@ bool EMSdevice::has_telegram_id(uint16_t id) const {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -2281,6 +2385,7 @@ bool EMSdevice::handle_telegram(const std::shared_ptr<const Telegram> & telegram
|
||||
tf.process_function_(this, telegram);
|
||||
return true;
|
||||
}
|
||||
|
||||
// if the data block is empty and we have not received data before, assume that this telegram
|
||||
// is not recognized by the bus master. So remove it from the automatic fetch list
|
||||
if (telegram->message_length == 0 && telegram->offset == 0 && !tf.received_) {
|
||||
@@ -2292,8 +2397,10 @@ bool EMSdevice::handle_telegram(const std::shared_ptr<const Telegram> & telegram
|
||||
if (uuid::get_uptime_sec() > FETCH_RETRY_DURATION_SEC) {
|
||||
tf.fetch_ = false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (telegram->message_length > 0) {
|
||||
tf.received_ = true;
|
||||
tf.process_function_(this, telegram);
|
||||
@@ -2302,6 +2409,7 @@ bool EMSdevice::handle_telegram(const std::shared_ptr<const Telegram> & telegram
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false; // type not found
|
||||
}
|
||||
|
||||
@@ -2332,6 +2440,7 @@ std::string EMSdevice::name() {
|
||||
if (model().empty()) {
|
||||
return default_name();
|
||||
}
|
||||
|
||||
return model() + "/" + std::string(default_name());
|
||||
}
|
||||
|
||||
@@ -2442,6 +2551,7 @@ int EMSdevice::modbus_value_to_json(uint8_t tag, const std::string & shortname,
|
||||
if (modbus_data.size() != 2) {
|
||||
return -2;
|
||||
}
|
||||
|
||||
jsonValue["value"] = modbus_data[0] || modbus_data[1];
|
||||
}
|
||||
|
||||
|
||||
@@ -120,12 +120,15 @@ class EMSdevice {
|
||||
inline void add_flags(uint8_t flags) {
|
||||
flags_ |= flags;
|
||||
}
|
||||
|
||||
inline bool has_flags(uint8_t flags) const {
|
||||
return (flags_ & flags) == flags;
|
||||
}
|
||||
|
||||
inline void remove_flags(uint8_t flags) {
|
||||
flags_ &= ~flags;
|
||||
}
|
||||
|
||||
inline uint8_t flags() const {
|
||||
return flags_;
|
||||
}
|
||||
@@ -147,6 +150,7 @@ class EMSdevice {
|
||||
const std::string & custom_brand() const {
|
||||
return custom_brand_;
|
||||
}
|
||||
|
||||
// set device model
|
||||
void model(std::string const & model) {
|
||||
model_ = model;
|
||||
@@ -240,6 +244,7 @@ class EMSdevice {
|
||||
publish_value((void *)&value);
|
||||
return;
|
||||
}
|
||||
|
||||
++i;
|
||||
}
|
||||
}
|
||||
@@ -388,6 +393,7 @@ class EMSdevice {
|
||||
bool ha_config_done() const {
|
||||
return ha_config_done_;
|
||||
}
|
||||
|
||||
void ha_config_done(const bool v) {
|
||||
ha_config_done_ = v;
|
||||
}
|
||||
@@ -571,6 +577,7 @@ class EMSdevice {
|
||||
, fetch_(fetch) {
|
||||
}
|
||||
};
|
||||
|
||||
void dump_telegram_info(std::vector<TelegramFunctionDump> & telegram_functions_dump);
|
||||
void dump_devicevalue_info();
|
||||
#endif
|
||||
|
||||
@@ -326,6 +326,7 @@ bool DeviceValue::get_min_max(int16_t & dv_set_min, uint32_t & dv_set_max) {
|
||||
} else {
|
||||
dv_set_max = Helpers::transformNumFloat(EMS_VALUE_UINT8_NOTSET - 1, numeric_operator, fahrenheit);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -337,6 +338,7 @@ bool DeviceValue::get_min_max(int16_t & dv_set_min, uint32_t & dv_set_max) {
|
||||
dv_set_min = Helpers::transformNumFloat(-EMS_VALUE_INT8_NOTSET + 1, numeric_operator, fahrenheit);
|
||||
dv_set_max = Helpers::transformNumFloat(EMS_VALUE_INT8_NOTSET - 1, numeric_operator, fahrenheit);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -359,11 +361,14 @@ bool DeviceValue::get_custom_min(int16_t & val) {
|
||||
if (fahrenheit) {
|
||||
v = (v - (32 * (fahrenheit - 1))) / 1.8; // reset to °C
|
||||
}
|
||||
|
||||
if (max > 0 && v > 0 && (uint32_t)v > max) {
|
||||
return false;
|
||||
}
|
||||
|
||||
val = v;
|
||||
}
|
||||
|
||||
return has_min;
|
||||
}
|
||||
|
||||
@@ -378,11 +383,14 @@ bool DeviceValue::get_custom_max(uint32_t & val) {
|
||||
if (fahrenheit) {
|
||||
v = (v - (32 * (fahrenheit - 1))) / 1.8; // reset to °C
|
||||
}
|
||||
|
||||
if (v < 0 || v < (int32_t)min) {
|
||||
return false;
|
||||
}
|
||||
|
||||
val = v;
|
||||
}
|
||||
|
||||
return has_max;
|
||||
}
|
||||
|
||||
@@ -417,6 +425,7 @@ std::string DeviceValue::get_custom_fullname() const {
|
||||
if (minmax_pos != std::string::npos) {
|
||||
return cf.substr(0, minmax_pos);
|
||||
}
|
||||
|
||||
return cf;
|
||||
}
|
||||
|
||||
@@ -427,6 +436,7 @@ std::string DeviceValue::get_fullname() const {
|
||||
if (customname.empty()) {
|
||||
return Helpers::translated_word(fullname);
|
||||
}
|
||||
|
||||
return customname;
|
||||
}
|
||||
|
||||
@@ -436,6 +446,7 @@ std::string DeviceValue::get_name(const std::string & entity) {
|
||||
if (pos != std::string::npos) {
|
||||
return entity.substr(2, pos - 2);
|
||||
}
|
||||
|
||||
return entity.substr(2);
|
||||
}
|
||||
|
||||
|
||||
@@ -236,12 +236,15 @@ class DeviceValue {
|
||||
void add_state(uint8_t s) {
|
||||
state |= s;
|
||||
}
|
||||
|
||||
bool has_state(uint8_t s) const {
|
||||
return (state & s) == s;
|
||||
}
|
||||
|
||||
void remove_state(uint8_t s) {
|
||||
state &= ~s;
|
||||
}
|
||||
|
||||
uint8_t get_state() const {
|
||||
return state;
|
||||
}
|
||||
|
||||
@@ -120,6 +120,7 @@ void EMSESP::fetch_device_values(const uint8_t device_id) {
|
||||
for (const auto & emsdevice : emsdevices) {
|
||||
emsdevice->fetch_values();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -144,6 +145,7 @@ bool EMSESP::valid_device(const uint8_t device_id) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false; // can't find it
|
||||
}
|
||||
|
||||
@@ -170,6 +172,7 @@ bool EMSESP::cmd_is_readonly(const uint8_t device_type, const uint8_t device_id,
|
||||
return emsdevice->is_readonly(cmd, id);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -183,6 +186,7 @@ uint8_t EMSESP::device_id_from_cmd(const uint8_t device_type, const char * cmd,
|
||||
return emsdevice->device_id();
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -202,6 +206,7 @@ void EMSESP::clear_all_devices() {
|
||||
for (const auto & emsdevice : emsdevices) {
|
||||
emsdevice->erase_device_values();
|
||||
}
|
||||
|
||||
emsdevices.clear();
|
||||
}
|
||||
|
||||
@@ -221,6 +226,7 @@ void EMSESP::compact_entities_if_stable() {
|
||||
if (!entity_compaction_pending_) {
|
||||
return; // nothing to do (cheap early-out on the hot path)
|
||||
}
|
||||
|
||||
if ((uuid::get_uptime() - last_entity_change_) < ENTITY_COMPACT_DELAY) {
|
||||
return; // still settling
|
||||
}
|
||||
@@ -230,6 +236,7 @@ void EMSESP::compact_entities_if_stable() {
|
||||
emsdevice->compact();
|
||||
}
|
||||
}
|
||||
|
||||
Command::compact();
|
||||
|
||||
entity_compaction_pending_ = false;
|
||||
@@ -248,6 +255,7 @@ uint8_t EMSESP::count_devices() {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
@@ -348,6 +356,7 @@ void EMSESP::show_ems(uuid::console::Shell & shell) {
|
||||
} else if ((it.telegram_->operation) == Telegram::Operation::TX_WRITE) {
|
||||
op = "WRITE";
|
||||
}
|
||||
|
||||
shell.printfln(" [%02d%c] %s %s", it.id_, ((it.retry_) ? '*' : ' '), op.c_str(), pretty_telegram(it.telegram_).c_str());
|
||||
}
|
||||
}
|
||||
@@ -385,10 +394,12 @@ void EMSESP::dump_all_entities(uuid::console::Shell & shell) {
|
||||
if (device.device_type == DeviceType::MIXER) {
|
||||
device_id = 0x20; // hc
|
||||
}
|
||||
|
||||
// For Heatsource set AM200
|
||||
if (device.device_type == DeviceType::HEATSOURCE) {
|
||||
device_id = EMSdevice::EMS_DEVICE_ID_AHS1;
|
||||
}
|
||||
|
||||
// For MX400 SRC plus base
|
||||
if (device.device_type == DeviceType::CONNECT && device.product_id == 17) {
|
||||
device_id = EMSdevice::EMS_DEVICE_ID_RFBASE;
|
||||
@@ -510,6 +521,7 @@ void EMSESP::show_device_values(uuid::console::Shell & shell) {
|
||||
|
||||
shell.printfln(" %s: %s%s %s%s", key.c_str(), COLOR_BRIGHT_GREEN, p.value().as<std::string>().c_str(), uom.c_str(), COLOR_RESET);
|
||||
}
|
||||
|
||||
shell.println();
|
||||
}
|
||||
}
|
||||
@@ -524,6 +536,7 @@ void EMSESP::show_device_values(uuid::console::Shell & shell) {
|
||||
for (JsonPair p : custom_output) {
|
||||
shell.printfln(" %s: %s%s%s", p.key().c_str(), COLOR_BRIGHT_GREEN, p.value().as<std::string>().c_str(), COLOR_RESET);
|
||||
}
|
||||
|
||||
shell.println();
|
||||
}
|
||||
}
|
||||
@@ -555,6 +568,7 @@ void EMSESP::show_sensor_values(uuid::console::Shell & shell) {
|
||||
sensor.is_system() ? "Yes" : "No");
|
||||
}
|
||||
}
|
||||
|
||||
shell.println();
|
||||
}
|
||||
|
||||
@@ -588,6 +602,7 @@ void EMSESP::show_sensor_values(uuid::console::Shell & shell) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
shell.println();
|
||||
}
|
||||
}
|
||||
@@ -650,6 +665,7 @@ void EMSESP::publish_all_loop() {
|
||||
if (Mqtt::ha_enabled()) {
|
||||
Mqtt::ha_status();
|
||||
}
|
||||
|
||||
system_.send_heartbeat(); // send MQTT heartbeat topic
|
||||
break;
|
||||
default:
|
||||
@@ -698,9 +714,11 @@ void EMSESP::publish_device_values(uint8_t device_type) {
|
||||
json_tag = doc[EMSdevice::tag_to_mqtt(tag)].to<JsonObject>();
|
||||
nest_created = true;
|
||||
}
|
||||
|
||||
need_publish |= emsdevice->generate_values(json_tag, tag, false, EMSdevice::OUTPUT_TARGET::MQTT);
|
||||
}
|
||||
}
|
||||
|
||||
if (need_publish && !nested) {
|
||||
Mqtt::queue_publish(Mqtt::tag_to_topic(device_type, tag), json);
|
||||
json = doc.to<JsonObject>();
|
||||
@@ -712,6 +730,7 @@ void EMSESP::publish_device_values(uint8_t device_type) {
|
||||
if (doc.overflowed()) {
|
||||
LOG_WARNING("MQTT buffer overflow, please use individual topics");
|
||||
}
|
||||
|
||||
Mqtt::queue_publish(Mqtt::tag_to_topic(device_type, DeviceValueTAG::TAG_NONE), json);
|
||||
}
|
||||
|
||||
@@ -769,6 +788,7 @@ void EMSESP::publish_response(const std::shared_ptr<const Telegram> & telegram)
|
||||
delete[] buffer;
|
||||
buffer = nullptr;
|
||||
}
|
||||
|
||||
if (buffer == nullptr) {
|
||||
offset = telegram->offset; // store offset from first part
|
||||
type = telegram->type_id;
|
||||
@@ -778,17 +798,21 @@ void EMSESP::publish_response(const std::shared_ptr<const Telegram> & telegram)
|
||||
buffer[i * 3 + 1] = '0';
|
||||
buffer[i * 3 + 2] = ' ';
|
||||
}
|
||||
|
||||
buffer[267] = '\0';
|
||||
}
|
||||
|
||||
if (telegram->message_length) {
|
||||
strlcpy(&buffer[(telegram->offset - offset) * 3], Helpers::data_to_hex(telegram->message_data, telegram->message_length).c_str(), 768);
|
||||
} else {
|
||||
strlcpy(&buffer[(telegram->offset - offset) * 3], "", 768);
|
||||
}
|
||||
|
||||
if (response_id_ != 0) {
|
||||
buffer[strlen(buffer)] = ' '; // overwrite termination \0
|
||||
return; // do not delete buffer
|
||||
}
|
||||
|
||||
JsonDocument doc;
|
||||
char s[10];
|
||||
doc["src"] = Helpers::hextoa(s, telegram->src);
|
||||
@@ -801,6 +825,7 @@ void EMSESP::publish_response(const std::shared_ptr<const Telegram> & telegram)
|
||||
uint32_t value = Helpers::hextoint(buffer);
|
||||
doc["value"] = value;
|
||||
}
|
||||
|
||||
Mqtt::queue_publish("response", doc.as<JsonObject>());
|
||||
delete[] buffer;
|
||||
buffer = nullptr;
|
||||
@@ -1061,6 +1086,7 @@ void EMSESP::process_UBADevices(const std::shared_ptr<const Telegram> & telegram
|
||||
LOG_DEBUG("Remove EMS device with ID 0x%02X", device_id);
|
||||
erase_device(device_id); //
|
||||
}
|
||||
|
||||
next_byte = next_byte >> 1; // advance 1 bit
|
||||
}
|
||||
}
|
||||
@@ -1072,6 +1098,7 @@ void EMSESP::process_deviceName(const std::shared_ptr<const Telegram> & telegram
|
||||
if (telegram->offset > 27 || (telegram->offset + telegram->message_length) < 29) {
|
||||
return;
|
||||
}
|
||||
|
||||
char name[16];
|
||||
// len including zero terminator, if there is one, otherwise copy to end of telegram
|
||||
// https://github.com/emsesp/EMS-ESP32/discussions/2482#discussioncomment-12649817
|
||||
@@ -1081,6 +1108,7 @@ void EMSESP::process_deviceName(const std::shared_ptr<const Telegram> & telegram
|
||||
while (isprint(*c)) {
|
||||
c++;
|
||||
};
|
||||
|
||||
*c = '\0';
|
||||
if (strlen(name) > 2) { // https://github.com/emsesp/EMS-ESP32/issues/2166#issuecomment-2454488657
|
||||
LOG_DEBUG("Model name received for device 0x%02X: %s", telegram->src, name);
|
||||
@@ -1177,17 +1205,21 @@ bool EMSESP::process_telegram(const std::shared_ptr<const Telegram> & telegram)
|
||||
if (!trace_raw_) {
|
||||
LOG_TRACE("%s", pretty_telegram(telegram).c_str());
|
||||
}
|
||||
|
||||
if (!read_next_) {
|
||||
response_id_ = 0;
|
||||
}
|
||||
|
||||
publish_response(telegram);
|
||||
} else {
|
||||
LOG_NOTICE("%s", pretty_telegram(telegram).c_str());
|
||||
}
|
||||
|
||||
// check if read is finished or gives more parts
|
||||
if (!read_next_) {
|
||||
read_id_ = WATCH_ID_NONE;
|
||||
}
|
||||
|
||||
read_next_ = false;
|
||||
} else if (watch() == WATCH_ON) {
|
||||
if ((watch_id_ == WATCH_ID_NONE) || (telegram->type_id == watch_id_)
|
||||
@@ -1226,6 +1258,7 @@ bool EMSESP::process_telegram(const std::shared_ptr<const Telegram> & telegram)
|
||||
if (!wait_km_) {
|
||||
process_UBADevices(telegram);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1252,6 +1285,7 @@ bool EMSESP::process_telegram(const std::shared_ptr<const Telegram> & telegram)
|
||||
found_device->has_update(false); // reset flag
|
||||
publish_device_values(found_device->device_type()); // publish to MQTT if we explicitly have too
|
||||
}
|
||||
|
||||
// auto publish: timeinterval 0 and publish single not set, only if queue is empty
|
||||
else if (mqtt_.get_publish_onchange(found_device->device_type()) && found_device->has_update() && mqtt_.publish_queued() == 0) {
|
||||
found_device->has_update(false); // reset flag
|
||||
@@ -1260,6 +1294,7 @@ bool EMSESP::process_telegram(const std::shared_ptr<const Telegram> & telegram)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break; // remove this to handle same telegrams on multiple devices
|
||||
}
|
||||
}
|
||||
@@ -1271,12 +1306,14 @@ bool EMSESP::process_telegram(const std::shared_ptr<const Telegram> & telegram)
|
||||
if (found_device && telegram->message_length > 0) {
|
||||
found_device->add_handlers_ignored(telegram->type_id);
|
||||
}
|
||||
|
||||
// handle unknown broadcasted telegrams (or send to us)
|
||||
if (telegram->dest == 0 || telegram->dest == EMSbus::ems_bus_id()) {
|
||||
LOG_DEBUG("No telegram type handler found for ID 0x%02X (src 0x%02X)", telegram->type_id, telegram->src);
|
||||
if (watch() == WATCH_UNKNOWN) {
|
||||
LOG_NOTICE("%s", pretty_telegram(telegram).c_str());
|
||||
}
|
||||
|
||||
if (!wait_km_ && !found_device && (telegram->src != EMSbus::ems_bus_id()) && (telegram->message_length > 0)) {
|
||||
#ifdef EMSESP_GATEWAY_TX_HOLD
|
||||
// a new device often sends several broadcasts before its version reply is processed, only ask once
|
||||
@@ -1291,6 +1328,7 @@ bool EMSESP::process_telegram(const std::shared_ptr<const Telegram> & telegram)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return telegram_found;
|
||||
}
|
||||
|
||||
@@ -1300,6 +1338,7 @@ bool EMSESP::device_hasEntities(const uint8_t device_id) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false; // not found
|
||||
}
|
||||
|
||||
@@ -1381,12 +1420,15 @@ bool EMSESP::add_device(const uint8_t device_id, const uint8_t product_id, const
|
||||
if (product_id == 0) { // no product-id, ignore
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((*it)->product_id() == product_id) { // update version if we have valid product_id
|
||||
if (!strcmp((*it)->version(), "00.00")) {
|
||||
(*it)->version(version);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// product-id has changed for this device-id, delete and re-add
|
||||
(*it)->erase_device_values();
|
||||
emsdevices.erase(it); // erase the old device without product_id and re detect
|
||||
@@ -1406,6 +1448,7 @@ bool EMSESP::add_device(const uint8_t device_id, const uint8_t product_id, const
|
||||
device_p = &device;
|
||||
break;
|
||||
}
|
||||
|
||||
if (device_id >= EMSdevice::EMS_DEVICE_ID_HS1 && device_id <= EMSdevice::EMS_DEVICE_ID_HS16) {
|
||||
device_p = &device;
|
||||
device_p->device_type = DeviceType::HEATSOURCE;
|
||||
@@ -1442,11 +1485,13 @@ bool EMSESP::add_device(const uint8_t device_id, const uint8_t product_id, const
|
||||
default_name = "Wireless sensor base";
|
||||
}
|
||||
}
|
||||
|
||||
// map MX400 also to RF Base
|
||||
if (device_id == EMSdevice::EMS_DEVICE_ID_RFBASE) {
|
||||
device_type = DeviceType::CONNECT;
|
||||
default_name = "Wireless base";
|
||||
}
|
||||
|
||||
if ((device_id >= EMSdevice::EMS_DEVICE_ID_DHW1 && device_id <= EMSdevice::EMS_DEVICE_ID_DHW8) || device_id == EMSdevice::EMS_DEVICE_ID_IPM_DHW) {
|
||||
device_type = DeviceType::WATER;
|
||||
}
|
||||
@@ -1463,6 +1508,7 @@ bool EMSESP::add_device(const uint8_t device_id, const uint8_t product_id, const
|
||||
flags = DeviceFlags::EMS_DEVICE_FLAG_CR120;
|
||||
default_name = "CR120";
|
||||
}
|
||||
|
||||
if (product_id == 158 && strncmp(version, "73", 2) == 0) {
|
||||
flags = DeviceFlags::EMS_DEVICE_FLAG_HMC310;
|
||||
default_name = "HMC310";
|
||||
@@ -1628,6 +1674,7 @@ void EMSESP::reply_own_request(const uint8_t * data, const uint8_t length, const
|
||||
EMSuart::transmit(reply, 7);
|
||||
LOG_DEBUG("Empty reply to 0x%02X read 0x%02X%02X", reply[1], data[5], data[6]);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1730,6 +1777,7 @@ void EMSESP::incoming_telegram(uint8_t * data, const uint8_t length) {
|
||||
if (poll_id != 0x48 && (uuid::get_uptime_sec() - connect_time) < EMS_WAIT_KM_TIMEOUT) {
|
||||
return;
|
||||
}
|
||||
|
||||
wait_km_ = false; // KM200 is polled, from now on it is safe to send
|
||||
}
|
||||
|
||||
@@ -1797,6 +1845,7 @@ void EMSESP::scheduled_fetch_values() {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
webCustomEntityService.fetch();
|
||||
no = 0;
|
||||
}
|
||||
@@ -1809,6 +1858,7 @@ void EMSESP::gateway_rx_check(const uint8_t * data, const uint8_t length) {
|
||||
if (length < 4 || (data[0] & 0x7F) != EMSdevice::EMS_DEVICE_ID_GATEWAY1 || !(data[1] & 0x80)) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t dest = data[1] & 0x7F;
|
||||
if (dest == 0 || dest == EMSbus::ems_bus_id()) {
|
||||
return;
|
||||
@@ -1819,10 +1869,12 @@ void EMSESP::gateway_rx_check(const uint8_t * data, const uint8_t length) {
|
||||
gateway_window_start_ = now;
|
||||
gateway_window_count_ = 0;
|
||||
}
|
||||
|
||||
if (now - gateway_fast_start_ > GATEWAY_FAST_WINDOW) {
|
||||
gateway_fast_start_ = now;
|
||||
gateway_fast_count_ = 0;
|
||||
}
|
||||
|
||||
++gateway_window_count_;
|
||||
++gateway_fast_count_;
|
||||
if (gateway_window_count_ >= GATEWAY_BURST_THRESHOLD || gateway_fast_count_ >= GATEWAY_FAST_THRESHOLD) {
|
||||
@@ -1840,12 +1892,15 @@ void EMSESP::gateway_tx_hold_start() {
|
||||
if ((int32_t)(now - gateway_cooldown_until_) < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
gateway_cooldown_until_ = 0;
|
||||
}
|
||||
|
||||
if (!gateway_hold_until_) {
|
||||
gateway_hold_start_ = now;
|
||||
LOG_INFO("Gateway 0x%02X is busy, holding Tx", EMSdevice::EMS_DEVICE_ID_GATEWAY1);
|
||||
}
|
||||
|
||||
gateway_hold_until_ = (now + GATEWAY_HOLD_TIME) | 1; // 0 means no hold
|
||||
}
|
||||
|
||||
@@ -1853,6 +1908,7 @@ bool EMSESP::gateway_tx_hold() {
|
||||
if (!gateway_hold_until_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t now = uuid::get_uptime();
|
||||
if (now - gateway_hold_start_ > GATEWAY_HOLD_MAX) {
|
||||
gateway_hold_until_ = 0;
|
||||
@@ -1860,11 +1916,13 @@ bool EMSESP::gateway_tx_hold() {
|
||||
LOG_WARNING("Gateway 0x%02X still busy after %lu s, resuming Tx", EMSdevice::EMS_DEVICE_ID_GATEWAY1, (unsigned long)(GATEWAY_HOLD_MAX / 1000));
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((int32_t)(now - gateway_hold_until_) >= 0) {
|
||||
gateway_hold_until_ = 0;
|
||||
LOG_INFO("Gateway 0x%02X is quiet, resuming Tx after %lu s", EMSdevice::EMS_DEVICE_ID_GATEWAY1, (unsigned long)((now - gateway_hold_start_) / 1000));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
@@ -2044,6 +2102,7 @@ void EMSESP::start() {
|
||||
force_analog_sensors = settings.analogCustomizations.empty();
|
||||
force_temperature_sensors = settings.sensorCustomizations.empty();
|
||||
};
|
||||
|
||||
webCustomizationService.read(check_empty_customizations);
|
||||
}
|
||||
}
|
||||
@@ -2052,6 +2111,7 @@ void EMSESP::start() {
|
||||
device_library_ = {
|
||||
#include "device_library.h"
|
||||
};
|
||||
|
||||
LOG_INFO("Library loaded: %d EMS devices, %d device entities, %s", device_library_.size(), EMSESP_TRANSLATION_COUNT, system_.languages_string().c_str());
|
||||
|
||||
// start telnet service if it's enabled
|
||||
@@ -2192,10 +2252,12 @@ void EMSESP::loop() {
|
||||
shell_prompt();
|
||||
show_prompt = false; // only show it once
|
||||
}
|
||||
|
||||
int c = serial_console_.read();
|
||||
if (c != -1) {
|
||||
show_prompt = true;
|
||||
}
|
||||
|
||||
// https://daleswanson.org/ascii.htm#:~:text=0
|
||||
// CTRL-D to open
|
||||
if (c == '\x04') {
|
||||
|
||||
@@ -196,6 +196,7 @@ class EMSESP {
|
||||
static uint8_t watch() {
|
||||
return watch_;
|
||||
}
|
||||
|
||||
static void set_read_id(uint16_t id) {
|
||||
read_id_ = id;
|
||||
}
|
||||
@@ -211,6 +212,7 @@ class EMSESP {
|
||||
static bool wait_validate() {
|
||||
return (wait_validate_ != 0);
|
||||
}
|
||||
|
||||
static void wait_validate(uint16_t wait) {
|
||||
wait_validate_ = wait;
|
||||
}
|
||||
@@ -328,6 +330,7 @@ class EMSESP {
|
||||
const char * default_name;
|
||||
uint8_t flags;
|
||||
};
|
||||
|
||||
static std::vector<Device_record, AllocatorPSRAM<Device_record>> device_library_;
|
||||
|
||||
static uint16_t watch_id_;
|
||||
|
||||
@@ -165,16 +165,20 @@ class Network {
|
||||
if (!desc) {
|
||||
return NetIface::NONE;
|
||||
}
|
||||
|
||||
if (strcmp(desc, "sta") == 0) {
|
||||
return NetIface::WIFI;
|
||||
}
|
||||
|
||||
if (strcmp(desc, "ap") == 0) {
|
||||
return NetIface::AP;
|
||||
}
|
||||
|
||||
// any "eth*" (eth, eth0, eth1, ...) is treated as Ethernet
|
||||
if (strncmp(desc, "eth", 3) == 0) {
|
||||
return NetIface::ETHERNET;
|
||||
}
|
||||
|
||||
return NetIface::NONE;
|
||||
}
|
||||
|
||||
@@ -190,6 +194,7 @@ class Network {
|
||||
default:
|
||||
return "None";
|
||||
}
|
||||
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
|
||||
@@ -58,6 +58,7 @@ class EMSFactory {
|
||||
if (auto * ptr = makeRaw(device_type, device_id, product_id, version, default_name, flags, brand)) {
|
||||
return std::unique_ptr<EMSdevice>(ptr);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
@@ -55,9 +55,11 @@ static int compare_prerelease(const std::string & a, const std::string & b) {
|
||||
if (a == b) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (a.empty()) {
|
||||
return 1; // release > prerelease
|
||||
}
|
||||
|
||||
if (b.empty()) {
|
||||
return -1;
|
||||
}
|
||||
@@ -70,9 +72,11 @@ static int compare_prerelease(const std::string & a, const std::string & b) {
|
||||
if (ea == std::string::npos) {
|
||||
ea = a.size();
|
||||
}
|
||||
|
||||
if (eb == std::string::npos) {
|
||||
eb = b.size();
|
||||
}
|
||||
|
||||
std::string id_a = a.substr(ia, ea - ia);
|
||||
std::string id_b = b.substr(ib, eb - ib);
|
||||
|
||||
@@ -102,9 +106,11 @@ static int compare_prerelease(const std::string & a, const std::string & b) {
|
||||
if (ia < a.size()) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (ib < b.size()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -144,6 +150,7 @@ void FirmwareVersion::parse(const char * s) {
|
||||
if (s == nullptr || *s == '\0') {
|
||||
return;
|
||||
}
|
||||
|
||||
// parse numeric major.minor.patch; accept partial ("3", "3.9", "3.9.0")
|
||||
sscanf(s, "%d.%d.%d", &major_, &minor_, &patch_);
|
||||
// capture prerelease tag after '-' if present (stop at '+' which is build metadata)
|
||||
|
||||
@@ -53,6 +53,7 @@ char * Helpers::hextoa(char * result, const uint16_t value) {
|
||||
if (value <= 0xFF) {
|
||||
return hextoa(result, (uint8_t)value);
|
||||
}
|
||||
|
||||
hextoa(result, (uint8_t)(value >> 8));
|
||||
hextoa(&result[2], (uint8_t)(value & 0xFF));
|
||||
return result;
|
||||
@@ -201,6 +202,7 @@ char * Helpers::render_boolean(char * result, const bool value, const bool dashb
|
||||
} else {
|
||||
strlcpy(result, value ? "true" : "false", 7); // default
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -209,10 +211,12 @@ char * Helpers::remove_trailing_zero(char * result) {
|
||||
while (result[strlen(result) - 1] == '0') {
|
||||
result[strlen(result) - 1] = '\0';
|
||||
}
|
||||
|
||||
if (result[strlen(result) - 1] == '.') {
|
||||
result[strlen(result) - 1] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -228,6 +232,7 @@ char * Helpers::render_value(char * result, uint8_t value, int8_t format, const
|
||||
} else {
|
||||
render_boolean(result, true); // assume on. could have value 0x01 or 0xFF
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -301,6 +306,7 @@ char * Helpers::render_value(char * result, const double value, const int8_t for
|
||||
*result++ = '0'; // add leading zeros
|
||||
}
|
||||
}
|
||||
|
||||
itoa(decimal, result, 10);
|
||||
|
||||
return remove_trailing_zero(ret);
|
||||
@@ -376,6 +382,7 @@ char * Helpers::render_value(char * result, const uint32_t value, const int8_t f
|
||||
if (!hasValue(value)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
result[0] = '\0';
|
||||
uint32_t new_value = fahrenheit ? format ? value * 1.8 + 32 * format * (fahrenheit - 1) : value * 1.8 + 32 * (fahrenheit - 1) : value;
|
||||
char s[14] = {0};
|
||||
@@ -447,9 +454,11 @@ char * Helpers::render_string(char * result, const char * c, const uint8_t len)
|
||||
*p = (*c & 0x80) ? '?' : *c;
|
||||
break;
|
||||
}
|
||||
|
||||
c++;
|
||||
p++;
|
||||
}
|
||||
|
||||
*p = '\0'; // terminate result
|
||||
return result;
|
||||
}
|
||||
@@ -489,12 +498,15 @@ char * Helpers::utf8tolatin1(char * result, const char * c, const uint8_t len) {
|
||||
} else {
|
||||
*p = *c;
|
||||
}
|
||||
|
||||
c++;
|
||||
p++;
|
||||
}
|
||||
|
||||
*p = '\0'; // terminate result
|
||||
return result;
|
||||
}
|
||||
|
||||
// creates string of hex values from an array of bytes
|
||||
std::string Helpers::data_to_hex(const uint8_t * data, const uint8_t length) {
|
||||
if (length == 0) {
|
||||
@@ -512,6 +524,7 @@ std::string Helpers::data_to_hex(const uint8_t * data, const uint8_t length) {
|
||||
*p++ = buffer[1];
|
||||
*p++ = ' '; // space
|
||||
}
|
||||
|
||||
*--p = '\0'; // null terminate just in case, loosing the trailing space
|
||||
|
||||
return std::string(str.data());
|
||||
@@ -559,13 +572,16 @@ int Helpers::atoint(const char * value) {
|
||||
if (s == '-') {
|
||||
++value;
|
||||
}
|
||||
|
||||
while (*value >= '0' && *value <= '9') {
|
||||
x = (x * 10) + (*value - '0');
|
||||
++value;
|
||||
}
|
||||
|
||||
if (s == '-') {
|
||||
return (-x);
|
||||
}
|
||||
|
||||
return x;
|
||||
}
|
||||
|
||||
@@ -603,6 +619,7 @@ bool Helpers::hasValue(const uint8_t & value, const uint8_t isBool) {
|
||||
if (isBool == EMS_VALUE_BOOL) {
|
||||
return (value != EMS_VALUE_BOOL_NOTSET);
|
||||
}
|
||||
|
||||
return (value != EMS_VALUE_UINT8_NOTSET);
|
||||
}
|
||||
|
||||
@@ -647,6 +664,7 @@ bool Helpers::value2number(const char * value, int & value_i, const int min, con
|
||||
if (value_i >= min && value_i <= max) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -666,6 +684,7 @@ bool Helpers::value2float(const char * value, float & value_f) {
|
||||
value_f = atof(value + 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -674,8 +693,10 @@ bool Helpers::value2temperature(const char * value, float & value_f, bool relati
|
||||
if (EMSESP::system_.fahrenheit()) {
|
||||
value_f = relative ? (value_f / 1.8) : (value_f - 32) / 1.8;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -684,8 +705,10 @@ bool Helpers::value2temperature(const char * value, int & value_i, const bool re
|
||||
if (EMSESP::system_.fahrenheit()) {
|
||||
value_i = relative ? (value_i / 1.8) : (value_i - 32) / 1.8;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -735,6 +758,7 @@ bool Helpers::value2enum(const char * value, uint8_t & value_ui, const char * co
|
||||
if ((value == nullptr) || (strlen(value) == 0)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string str = toLower(value);
|
||||
|
||||
for (value_ui = 0; strs[value_ui]; value_ui++) {
|
||||
@@ -746,6 +770,7 @@ bool Helpers::value2enum(const char * value, uint8_t & value_ui, const char * co
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
value_ui = 0;
|
||||
return false;
|
||||
}
|
||||
@@ -755,6 +780,7 @@ bool Helpers::value2enum(const char * value, uint8_t & value_ui, const char * co
|
||||
if (!value2enum(value, v, strs) || v >= mask.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
value_ui = mask[v];
|
||||
return true;
|
||||
}
|
||||
@@ -766,6 +792,7 @@ bool Helpers::value2enum(const char * value, uint8_t & value_ui, const char * co
|
||||
if ((value == nullptr) || (strlen(value) == 0)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string str = toLower(value);
|
||||
|
||||
std::string s_on = Helpers::translated_word(FL_(on));
|
||||
@@ -791,6 +818,7 @@ bool Helpers::value2enum(const char * value, uint8_t & value_ui, const char * co
|
||||
if (!value2enum(value, v, strs) || v >= mask.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
value_ui = mask[v];
|
||||
return true;
|
||||
}
|
||||
@@ -829,6 +857,7 @@ void Helpers::CharToUpperUTF8(char * c) {
|
||||
if ((p_v >= (char)0xA0) && (p_v <= (char)0xBE)) {
|
||||
*p -= 0x20;
|
||||
}
|
||||
|
||||
// Additional special characters for supported languages
|
||||
switch (p_v) {
|
||||
case (char)0xA0: // à -> À
|
||||
@@ -865,6 +894,7 @@ void Helpers::CharToUpperUTF8(char * c) {
|
||||
*p -= 0x20;
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
case (char)0xC4:
|
||||
switch (p_v) {
|
||||
@@ -881,6 +911,7 @@ void Helpers::CharToUpperUTF8(char * c) {
|
||||
*p -= 1;
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
case (char)0xC5:
|
||||
switch (p_v) {
|
||||
@@ -903,6 +934,7 @@ void Helpers::CharToUpperUTF8(char * c) {
|
||||
*p -= 1;
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
*c = toupper(*c); // works on Latin1 letters
|
||||
@@ -939,6 +971,7 @@ uint8_t Helpers::count_items(const char * const * list) {
|
||||
list_size++;
|
||||
}
|
||||
}
|
||||
|
||||
return list_size;
|
||||
}
|
||||
|
||||
@@ -951,6 +984,7 @@ uint8_t Helpers::count_items(const char * const ** list) {
|
||||
list_size++;
|
||||
}
|
||||
}
|
||||
|
||||
return list_size;
|
||||
}
|
||||
|
||||
@@ -983,19 +1017,23 @@ uint16_t Helpers::string2minutes(const std::string & str) {
|
||||
if (str[i] >= '0' && str[i] <= '9') {
|
||||
tmp = tmp * 10 + str[i] - '0';
|
||||
}
|
||||
|
||||
// Or if we got a colon
|
||||
else if (str[i] == ':') {
|
||||
// If we were reading the hours
|
||||
if (state == 0) {
|
||||
res = 60 * tmp;
|
||||
}
|
||||
|
||||
// Or if we were reading the minutes
|
||||
else if (state == 1) {
|
||||
if (tmp > 60) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
res += tmp;
|
||||
}
|
||||
|
||||
// Or we got an extra colon
|
||||
else {
|
||||
return 0;
|
||||
@@ -1009,6 +1047,7 @@ uint16_t Helpers::string2minutes(const std::string & str) {
|
||||
else {
|
||||
return 0;
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
@@ -1034,6 +1073,7 @@ int16_t Helpers::calc_dew(int16_t temp, uint8_t humi) {
|
||||
if (humi == EMS_VALUE_UINT8_NOTSET || temp == EMS_VALUE_INT16_NOTSET) {
|
||||
return EMS_VALUE_INT16_NOTSET;
|
||||
}
|
||||
|
||||
const float k2 = 17.62;
|
||||
const float k3 = 243.12;
|
||||
const float t = (float)temp / 10;
|
||||
|
||||
@@ -69,6 +69,7 @@ int HttpClient::request(std::string url, const std::string & method, const std::
|
||||
ssl_client->setBufferSizes(TLS_RX_BUFFER_SIZE, TLS_TX_BUFFER_SIZE);
|
||||
ssl_client->setSessionTimeout(TLS_SESSION_TIMEOUT_S);
|
||||
}
|
||||
|
||||
// WiFiClient is NetworkClient, which declares no setTimeout() of its own - calling it binds to
|
||||
// Stream::setTimeout() and only affects readBytes(), leaving the socket on the core's 3s default
|
||||
basic_client->setConnectionTimeout(CONNECT_TIMEOUT_MS);
|
||||
@@ -89,17 +90,21 @@ int HttpClient::request(std::string url, const std::string & method, const std::
|
||||
agent_set |= (key == "user-agent");
|
||||
req += std::string(p.key().c_str()) + ": " + p.value().as<std::string>() + "\r\n";
|
||||
}
|
||||
|
||||
if (!agent_set) {
|
||||
req += "User-Agent: EMS-ESP\r\n"; // CDNs are more likely to stall on a request without one
|
||||
}
|
||||
|
||||
if (is_post) {
|
||||
if (!content_set) {
|
||||
req += "Content-Type: ";
|
||||
req += value.starts_with('{') ? asyncsrv::T_application_json : asyncsrv::T_text_plain;
|
||||
req += "\r\n";
|
||||
}
|
||||
|
||||
req += "Content-Length: " + std::to_string(value.length()) + "\r\n";
|
||||
}
|
||||
|
||||
req += "Connection: close\r\n\r\n"; // the blank line terminates the headers - without it the server never responds
|
||||
|
||||
ssl_client->print(req.c_str());
|
||||
@@ -120,6 +125,7 @@ int HttpClient::request(std::string url, const std::string & method, const std::
|
||||
truncated = true;
|
||||
break;
|
||||
}
|
||||
|
||||
uint8_t buf[128];
|
||||
const size_t want = (avail < (int)sizeof(buf)) ? (size_t)avail : sizeof(buf);
|
||||
const int len = ssl_client->read(buf, want);
|
||||
@@ -128,20 +134,25 @@ int HttpClient::request(std::string url, const std::string & method, const std::
|
||||
last_data = millis();
|
||||
continue;
|
||||
}
|
||||
|
||||
// available() reported bytes the client won't hand over. Yielding here keeps a
|
||||
// stale count from busy-spinning the whole read budget on this task's core
|
||||
delay(1);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!ssl_client->connected()) {
|
||||
break; // closed, with nothing left buffered
|
||||
}
|
||||
|
||||
// the server may take a while to start replying
|
||||
if (millis() - last_data > (result.empty() ? FIRST_BYTE_TIMEOUT_MS : IDLE_TIMEOUT_MS)) {
|
||||
break;
|
||||
}
|
||||
|
||||
delay(1);
|
||||
}
|
||||
|
||||
ssl_client->stop();
|
||||
|
||||
const auto received = result.length();
|
||||
@@ -170,6 +181,7 @@ int HttpClient::request(std::string url, const std::string & method, const std::
|
||||
} else {
|
||||
EMSESP::logger().warning("%s connection to %s failed", is_https ? "HTTPS" : "HTTP", host.c_str());
|
||||
}
|
||||
|
||||
delete ssl_client;
|
||||
delete basic_client;
|
||||
|
||||
|
||||
+9
-1
@@ -59,6 +59,7 @@ Channel * init(uint8_t pin) {
|
||||
if (ch.pin == pin) {
|
||||
return &ch;
|
||||
}
|
||||
|
||||
if (!free_slot && ch.pin == NO_PIN) {
|
||||
free_slot = &ch;
|
||||
}
|
||||
@@ -73,12 +74,14 @@ Channel * init(uint8_t pin) {
|
||||
free_slot = &ch;
|
||||
}
|
||||
}
|
||||
|
||||
end(free_slot->pin);
|
||||
}
|
||||
|
||||
if (!rmtInit(pin, RMT_TX_MODE, RMT_MEM_NUM_BLOCKS_1, RMT_FREQ_HZ)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
free_slot->pin = pin;
|
||||
free_slot->last_tx_us = micros() - RESET_US; // no wait on first write
|
||||
return free_slot;
|
||||
@@ -188,6 +191,7 @@ bool LED::loop(uint8_t healthcheck, bool button_busy) {
|
||||
} else {
|
||||
reset_led();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -267,6 +271,7 @@ void LED::set_led(Color color) {
|
||||
if (!led_gpio_ || color == last_color_) {
|
||||
return;
|
||||
}
|
||||
|
||||
last_color_ = color;
|
||||
|
||||
// RGB lookup table indexed by Color enum (must match enum order in led.h)
|
||||
@@ -283,6 +288,7 @@ void LED::set_led(Color color) {
|
||||
{0, B, B}, // CYAN
|
||||
{H, 0, H} // PINK
|
||||
};
|
||||
|
||||
static_assert(sizeof(rgb_table) / sizeof(rgb_table[0]) == Color::PINK + 1, "rgb_table must match the Color enum");
|
||||
|
||||
const uint8_t * rgb = rgb_table[color];
|
||||
@@ -324,6 +330,7 @@ bool LED::set_custom_led_routine(const std::string & color, const std::string &
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!color_matched) {
|
||||
return false;
|
||||
}
|
||||
@@ -393,6 +400,7 @@ void LED::sequence_led() {
|
||||
is_user_led_blink_ = false;
|
||||
previous_healthcheck_ = System::HEALTHCHECK_RESET; // this will force the healthcheck to be checked again
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -414,4 +422,4 @@ void LED::start_led_fast_flash(uint8_t duration) {
|
||||
led_fast_flash_timer_ = true; // it's active
|
||||
}
|
||||
|
||||
} // namespace emsesp
|
||||
} // namespace emsesp
|
||||
|
||||
@@ -72,21 +72,27 @@ constexpr uint32_t TYPE_CCRC = 0x500;
|
||||
constexpr bool tag_isvalid(uint32_t t) {
|
||||
return (t & 0x80000000) == 0;
|
||||
}
|
||||
|
||||
constexpr uint32_t tag_type2(uint32_t t) {
|
||||
return (t & 0x78000000) >> 20;
|
||||
}
|
||||
|
||||
constexpr uint32_t tag_type3(uint32_t t) {
|
||||
return (t & 0x7ff00000) >> 20;
|
||||
}
|
||||
|
||||
constexpr uint32_t tag_chunk(uint32_t t) {
|
||||
return (t & 0x0ff00000) >> 20;
|
||||
}
|
||||
|
||||
constexpr uint32_t tag_id(uint32_t t) {
|
||||
return (t & 0x000ffc00) >> 10;
|
||||
}
|
||||
|
||||
constexpr uint32_t tag_size(uint32_t t) {
|
||||
return t & 0x3ff;
|
||||
}
|
||||
|
||||
// a size of 0x3ff marks a delete tag, which carries no payload
|
||||
constexpr uint32_t tag_dsize(uint32_t t) {
|
||||
return 4 + (tag_size(t) == 0x3ff ? 0 : tag_size(t));
|
||||
@@ -95,9 +101,11 @@ constexpr uint32_t tag_dsize(uint32_t t) {
|
||||
uint32_t rd_be32(const uint8_t * p) {
|
||||
return (static_cast<uint32_t>(p[0]) << 24) | (static_cast<uint32_t>(p[1]) << 16) | (static_cast<uint32_t>(p[2]) << 8) | p[3];
|
||||
}
|
||||
|
||||
uint32_t rd_le32(const uint8_t * p) {
|
||||
return (static_cast<uint32_t>(p[3]) << 24) | (static_cast<uint32_t>(p[2]) << 16) | (static_cast<uint32_t>(p[1]) << 8) | p[0];
|
||||
}
|
||||
|
||||
void wr_le32(uint8_t * p, uint32_t v) {
|
||||
p[0] = static_cast<uint8_t>(v);
|
||||
p[1] = static_cast<uint8_t>(v >> 8);
|
||||
@@ -125,11 +133,13 @@ uint32_t lfs_crc(uint32_t crc, const void * buffer, size_t size) {
|
||||
0xa00ae278,
|
||||
0xbdbdf21c,
|
||||
};
|
||||
|
||||
const uint8_t * data = static_cast<const uint8_t *>(buffer);
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
crc = (crc >> 4) ^ rtable[(crc ^ data[i]) & 0xf];
|
||||
crc = (crc >> 4) ^ rtable[(crc ^ (data[i] >> 4)) & 0xf];
|
||||
}
|
||||
|
||||
return crc;
|
||||
}
|
||||
|
||||
@@ -166,15 +176,18 @@ Located locate_superblock(const uint8_t * buf) {
|
||||
if (!tag_isvalid(tag)) {
|
||||
break; // nothing committed beyond this point
|
||||
}
|
||||
|
||||
if (off + tag_dsize(tag) > BLOCK_SIZE) {
|
||||
break;
|
||||
}
|
||||
|
||||
ptag = tag;
|
||||
|
||||
if (tag_type2(tag) == TYPE_CCRC) {
|
||||
if (crc != rd_le32(&buf[off + 4])) {
|
||||
break; // commit never completed, so the chain ends here
|
||||
}
|
||||
|
||||
if (pending) {
|
||||
found.found = true;
|
||||
found.struct_off = pending_off;
|
||||
@@ -182,6 +195,7 @@ Located locate_superblock(const uint8_t * buf) {
|
||||
found.crc_off = off + 4;
|
||||
pending = false;
|
||||
}
|
||||
|
||||
ptag ^= (tag_chunk(tag) & 1u) << 31;
|
||||
crc = 0xffffffff;
|
||||
commit_start = off + tag_dsize(tag); // the padding after the CRC is not covered
|
||||
@@ -204,11 +218,13 @@ const esp_partition_t * find_partition(const char * partition_label) {
|
||||
if (partition_label != nullptr) {
|
||||
return esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY, partition_label);
|
||||
}
|
||||
|
||||
// 0x83 is the littlefs subtype, 0x82 the spiffs one that older partition tables use
|
||||
const esp_partition_t * part = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, static_cast<esp_partition_subtype_t>(0x83), nullptr);
|
||||
if (part != nullptr) {
|
||||
return part;
|
||||
}
|
||||
|
||||
return esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_SPIFFS, nullptr);
|
||||
}
|
||||
|
||||
|
||||
@@ -37,12 +37,14 @@ void Modbus::start(uint8_t systemServerId, uint16_t port, uint8_t max_clients, u
|
||||
modbusServer_->registerWorker(i, WRITE_MULT_REGISTERS, [this](auto && request) { return handleWrite(request); });
|
||||
}
|
||||
}
|
||||
|
||||
modbusServer_->start(port, max_clients, timeout);
|
||||
LOG_INFO("Starting Modbus service (ID %d, port %d)", systemServerId, port);
|
||||
#else
|
||||
if (!check_parameter_order()) {
|
||||
LOG_ERROR("Unable to enable Modbus - the parameter list order is corrupt. This is a firmware bug.");
|
||||
}
|
||||
|
||||
LOG_INFO("Modbus deactivated in standalone build.");
|
||||
#endif
|
||||
}
|
||||
@@ -71,8 +73,10 @@ bool Modbus::check_parameter_order() {
|
||||
LOG_ERROR("Error in modbus parameters: %s must be listed before %s.", mi.short_name, prev->short_name);
|
||||
return false;
|
||||
}
|
||||
|
||||
prev = &mi;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -263,10 +267,12 @@ ModbusMessage Modbus::handleRead(const ModbusMessage & request) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (error_code) {
|
||||
if (uuid::get_uptime_sec() > 60 || error_code < -2) { // suppress not found messages for the first minute
|
||||
LOG_ERROR("Unable to read raw device value %s for tag=%d - error_code = %d", modbusInfo->short_name, (int)tag, error_code);
|
||||
}
|
||||
|
||||
response.setError(request.getServerID(), request.getFunctionCode(), SERVER_DEVICE_FAILURE);
|
||||
return response;
|
||||
}
|
||||
@@ -361,6 +367,7 @@ ModbusMessage Modbus::handleWrite(const ModbusMessage & request) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (error_code) {
|
||||
// error getting modbus value as json
|
||||
LOG_ERROR("error getting modbus value as json, error code = %d", error_code);
|
||||
@@ -397,6 +404,7 @@ ModbusMessage Modbus::handleWrite(const ModbusMessage & request) {
|
||||
} else {
|
||||
snprintf(error, sizeof(error), "Modbus write command failed with error code (%s)", Command::return_code_string(return_code));
|
||||
}
|
||||
|
||||
LOG_ERROR(error);
|
||||
response.setError(request.getServerID(), request.getFunctionCode(), ILLEGAL_DATA_VALUE);
|
||||
} else {
|
||||
|
||||
@@ -83,6 +83,7 @@ void Mqtt::subscribe(const uint8_t device_type, const std::string & topic, mqtt_
|
||||
if (cb) {
|
||||
mqtt_subfunction.mqtt_subfunction_ = cb;
|
||||
}
|
||||
|
||||
return; // exit - don't add
|
||||
}
|
||||
}
|
||||
@@ -237,6 +238,7 @@ void Mqtt::on_message(const char * topic, const uint8_t * payload, size_t len) {
|
||||
queue_publish_message(topic, "", true);
|
||||
LOG_DEBUG("Remove topic %s", topic);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -251,6 +253,7 @@ void Mqtt::on_message(const char * topic, const uint8_t * payload, size_t len) {
|
||||
LOG_ERROR("error: invalid payload %s for this topic %s", message, topic);
|
||||
Mqtt::queue_publish("response", "error: invalid data");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -282,6 +285,7 @@ void Mqtt::on_message(const char * topic, const uint8_t * payload, size_t len) {
|
||||
} else {
|
||||
snprintf(error, sizeof(error), "MQTT command failed with error %s", Command::return_code_string(return_code));
|
||||
}
|
||||
|
||||
LOG_ERROR(error);
|
||||
Mqtt::queue_publish("response", error);
|
||||
} else {
|
||||
@@ -351,6 +355,7 @@ void Mqtt::reset_mqtt() {
|
||||
if (mqttClient_->connected()) {
|
||||
mqttClient_->disconnect(true); // force a disconnect
|
||||
}
|
||||
|
||||
load_settings(); // reload MQTT settings
|
||||
}
|
||||
|
||||
@@ -401,6 +406,7 @@ void Mqtt::start() {
|
||||
if (initialized_) {
|
||||
return;
|
||||
}
|
||||
|
||||
initialized_ = true;
|
||||
|
||||
// add the 'publish' command ('call system publish' in console or via API)
|
||||
@@ -447,6 +453,7 @@ bool Mqtt::get_publish_onchange(uint8_t device_type) {
|
||||
if (publish_single_ && !ha_enabled_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (device_type == EMSdevice::DeviceType::BOILER) {
|
||||
if (!publish_time_boiler_) {
|
||||
return true;
|
||||
@@ -470,6 +477,7 @@ bool Mqtt::get_publish_onchange(uint8_t device_type) {
|
||||
} else if (!publish_time_other_) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -626,6 +634,7 @@ void Mqtt::ha_status() {
|
||||
"bootTime",
|
||||
DeviceValueUOM::UPTIME); // called Uptime in HA and derives value from bootTime element in info topic
|
||||
}
|
||||
|
||||
// add sub or pub task to the queue.
|
||||
// the base is not included in the topic
|
||||
bool Mqtt::queue_message(const uint8_t operation, const std::string & topic, const std::string & payload, const bool retain) {
|
||||
@@ -648,14 +657,17 @@ bool Mqtt::queue_message(const uint8_t operation, const std::string & topic, con
|
||||
mqtt_message_id_++;
|
||||
mqtt_publish_fails_++;
|
||||
}
|
||||
|
||||
LOG_WARNING("%s failed: low memory", operation == Operation::PUBLISH ? "Publish" : operation == Operation::SUBSCRIBE ? "Subscribe" : "Unsubscribe");
|
||||
return false; // quit
|
||||
}
|
||||
|
||||
if (queuecount_ >= MQTT_QUEUE_MAX_SIZE) {
|
||||
if (operation == Operation::PUBLISH) {
|
||||
mqtt_message_id_++;
|
||||
mqtt_publish_fails_++;
|
||||
}
|
||||
|
||||
LOG_WARNING("%s failed: queue full", operation == Operation::PUBLISH ? "Publish" : operation == Operation::SUBSCRIBE ? "Subscribe" : "Unsubscribe");
|
||||
return false; // quit
|
||||
}
|
||||
@@ -758,6 +770,7 @@ bool Mqtt::queue_publish(const char * topic, const JsonObjectConst payload, cons
|
||||
serializeJson(payload, payload_text); // convert json to string
|
||||
return queue_publish_message(topic, payload_text, retain);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -893,6 +906,7 @@ bool Mqtt::publish_ha_sensor_config(uint8_t type, // EMSdev
|
||||
snprintf(entity_with_tag, sizeof(entity_with_tag), "%sww", dhw_old[i]);
|
||||
}
|
||||
}
|
||||
|
||||
snprintf(uniq_id, sizeof(uniq_id), "%s_%s", device_name, entity_with_tag);
|
||||
} else if (has_tag && device_type == EMSdevice::DeviceType::WATER && tag >= DeviceValue::DeviceValueTAG::TAG_DHW3) {
|
||||
snprintf(entity_with_tag, sizeof(entity_with_tag), "wwc%d_%s", tag - DeviceValue::DeviceValueTAG::TAG_DHW1 + 1, entity);
|
||||
@@ -913,6 +927,7 @@ bool Mqtt::publish_ha_sensor_config(uint8_t type, // EMSdev
|
||||
snprintf(entity_with_tag, sizeof(entity_with_tag), "%sww", dhw_old[i]);
|
||||
}
|
||||
}
|
||||
|
||||
snprintf(uniq_id, sizeof(uniq_id), "%s_%s_%s", Mqtt::basename().c_str(), device_name, entity_with_tag);
|
||||
} else if (has_tag && device_type == EMSdevice::DeviceType::WATER && tag >= DeviceValue::DeviceValueTAG::TAG_DHW3) {
|
||||
snprintf(entity_with_tag, sizeof(entity_with_tag), "wwc%d_%s", tag - DeviceValue::DeviceValueTAG::TAG_DHW1 + 1, entity);
|
||||
@@ -975,6 +990,7 @@ bool Mqtt::publish_ha_sensor_config(uint8_t type, // EMSdev
|
||||
if (discovery_type() == discoveryType::HOMEASSISTANT || discovery_type() == discoveryType::DOMOTICZ_LATEST) {
|
||||
snprintf(topic, sizeof(topic), "number/%s", config_topic);
|
||||
}
|
||||
|
||||
break;
|
||||
case DeviceValueType::BOOL:
|
||||
// switch - https://www.home-assistant.io/integrations/switch.mqtt
|
||||
@@ -990,6 +1006,7 @@ bool Mqtt::publish_ha_sensor_config(uint8_t type, // EMSdev
|
||||
} else if (discovery_type() == discoveryType::HOMEASSISTANT || discovery_type() == discoveryType::DOMOTICZ_LATEST) {
|
||||
snprintf(topic, sizeof(topic), "number/%s", config_topic);
|
||||
}
|
||||
|
||||
break;
|
||||
case DeviceValueType::STRING:
|
||||
// text - https://www.home-assistant.io/integrations/text.mqtt
|
||||
@@ -997,6 +1014,7 @@ bool Mqtt::publish_ha_sensor_config(uint8_t type, // EMSdev
|
||||
if (discovery_type() == discoveryType::HOMEASSISTANT) {
|
||||
snprintf(topic, sizeof(topic), "text/%s", config_topic); // e.g. set_datetime, set_holiday, set_wwswitchtime
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@@ -1040,6 +1058,7 @@ bool Mqtt::publish_ha_sensor_config(uint8_t type, // EMSdev
|
||||
} else {
|
||||
snprintf(command_topic, sizeof(command_topic), "~/%s/%s", device_name, entity);
|
||||
}
|
||||
|
||||
doc["cmd_t"] = command_topic;
|
||||
|
||||
// extend for enums and also commands, add options
|
||||
@@ -1063,6 +1082,7 @@ bool Mqtt::publish_ha_sensor_config(uint8_t type, // EMSdev
|
||||
} else {
|
||||
doc["mode"] = "box"; // auto, slider or box
|
||||
}
|
||||
|
||||
if (num_op > 0) {
|
||||
doc["step"] = 1.0 / num_op;
|
||||
} else if (num_op < 0) {
|
||||
@@ -1089,6 +1109,7 @@ bool Mqtt::publish_ha_sensor_config(uint8_t type, // EMSdev
|
||||
} else {
|
||||
snprintf(ha_name, sizeof(ha_name), "%s", F_name); // no tag
|
||||
}
|
||||
|
||||
free(F_name); // very important!
|
||||
doc["name"] = ha_name;
|
||||
|
||||
@@ -1108,6 +1129,7 @@ bool Mqtt::publish_ha_sensor_config(uint8_t type, // EMSdev
|
||||
snprintf(stat_t, sizeof(stat_t), "~/%s", F_(info));
|
||||
}
|
||||
}
|
||||
|
||||
doc["stat_t"] = stat_t;
|
||||
|
||||
// value template
|
||||
@@ -1141,6 +1163,7 @@ bool Mqtt::publish_ha_sensor_config(uint8_t type, // EMSdev
|
||||
// default
|
||||
doc["val_tpl"] = (std::string) "{{" + val_obj + " if " + val_cond + " else 'None'}}";
|
||||
}
|
||||
|
||||
add_ha_avty_section(doc.as<JsonObject>(), stat_t, val_cond); // adds availability section
|
||||
} else {
|
||||
// Domoticz doesn't support value templates, so we just use the value directly
|
||||
@@ -1180,6 +1203,7 @@ void Mqtt::add_ha_classes(JsonObject doc, const uint8_t device_type, const uint8
|
||||
if (device_type == EMSdevice::DeviceType::SYSTEM) {
|
||||
doc["ent_cat"] = "diagnostic"; // instead of 'config'
|
||||
}
|
||||
|
||||
if (type == DeviceValueType::BOOL) {
|
||||
return;
|
||||
}
|
||||
@@ -1238,12 +1262,14 @@ void Mqtt::add_ha_classes(JsonObject doc, const uint8_t device_type, const uint8
|
||||
if (display_only) {
|
||||
doc[ic_ha] = F_(iconpercent); // set icon
|
||||
}
|
||||
|
||||
doc[sc_ha] = sc_ha_measurement;
|
||||
if (entity == FL_(airHumidity)[0] || entity == FL_(remotehum)[0]) {
|
||||
doc[dc_ha] = "humidity";
|
||||
} else if (entity == FL_(battery)[0]) {
|
||||
doc[dc_ha] = "battery";
|
||||
}
|
||||
|
||||
break;
|
||||
case DeviceValueUOM::SECONDS:
|
||||
case DeviceValueUOM::MINUTES:
|
||||
@@ -1253,12 +1279,14 @@ void Mqtt::add_ha_classes(JsonObject doc, const uint8_t device_type, const uint8
|
||||
} else {
|
||||
doc[sc_ha] = sc_ha_measurement;
|
||||
}
|
||||
|
||||
doc[dc_ha] = "duration"; // https://github.com/emsesp/EMS-ESP32/issues/822
|
||||
break;
|
||||
case DeviceValueUOM::KB:
|
||||
if (display_only) {
|
||||
doc[ic_ha] = F_(iconkb);
|
||||
}
|
||||
|
||||
doc[dc_ha] = "data_size";
|
||||
break;
|
||||
case DeviceValueUOM::LMIN:
|
||||
@@ -1266,6 +1294,7 @@ void Mqtt::add_ha_classes(JsonObject doc, const uint8_t device_type, const uint8
|
||||
if (display_only) {
|
||||
doc[ic_ha] = F_(iconlmin);
|
||||
}
|
||||
|
||||
doc[sc_ha] = sc_ha_measurement;
|
||||
doc[dc_ha] = "volume_flow_rate";
|
||||
break;
|
||||
@@ -1280,6 +1309,7 @@ void Mqtt::add_ha_classes(JsonObject doc, const uint8_t device_type, const uint8
|
||||
} else {
|
||||
doc[sc_ha] = sc_ha_measurement; // no device class for this
|
||||
}
|
||||
|
||||
break;
|
||||
case DeviceValueUOM::KWH:
|
||||
doc[sc_ha] = sc_ha_total_increasing;
|
||||
@@ -1289,6 +1319,7 @@ void Mqtt::add_ha_classes(JsonObject doc, const uint8_t device_type, const uint8
|
||||
if (display_only) {
|
||||
doc[ic_ha] = F_(iconua);
|
||||
}
|
||||
|
||||
doc[sc_ha] = sc_ha_measurement;
|
||||
// doc[dc_ha] = "current"; // there is no uA in HA, only mA
|
||||
break;
|
||||
@@ -1354,6 +1385,7 @@ void Mqtt::add_ha_classes(JsonObject doc, const uint8_t device_type, const uint8
|
||||
if (display_only) {
|
||||
doc[ic_ha] = F_(iconnum); // set icon
|
||||
}
|
||||
|
||||
// determine if its a measurement or total increasing
|
||||
// most of the values are measurement. for example Tx Reads will increment but can be reset to 0 after a restart
|
||||
// all the starts are increasing, and they are ULONGs
|
||||
@@ -1363,6 +1395,7 @@ void Mqtt::add_ha_classes(JsonObject doc, const uint8_t device_type, const uint8
|
||||
doc[sc_ha] = sc_ha_measurement; // default to measurement
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1412,10 +1445,12 @@ bool Mqtt::publish_ha_climate_config(const DeviceValue & dv, const bool has_room
|
||||
snprintf(currtemp_s, sizeof(currtemp_s), "value_json.%s%d.currtemp", tagname, hc_num);
|
||||
snprintf(currtemp_cond, sizeof(currtemp_cond), "value_json.%s%d is defined and %s is defined", tagname, hc_num, currtemp_s);
|
||||
}
|
||||
|
||||
if (tag >= DeviceValueTAG::TAG_SRC1) {
|
||||
snprintf(currhum_s, sizeof(currhum_s), "value_json.%s%d.airhumidity", tagname, hc_num);
|
||||
snprintf(currhum_cond, sizeof(currhum_cond), "value_json.%s%d is defined and %s is defined", tagname, hc_num, currhum_s);
|
||||
}
|
||||
|
||||
snprintf(topic_t, sizeof(topic_t), "~/%s", Mqtt::tag_to_topic(device_type, DeviceValueTAG::TAG_NONE).c_str());
|
||||
} else {
|
||||
// single format
|
||||
@@ -1428,10 +1463,12 @@ bool Mqtt::publish_ha_climate_config(const DeviceValue & dv, const bool has_room
|
||||
snprintf(currtemp_s, sizeof(currtemp_s), "value_json.currtemp");
|
||||
snprintf(currtemp_cond, sizeof(currtemp_cond), "%s is defined", currtemp_s);
|
||||
}
|
||||
|
||||
if (tag >= DeviceValueTAG::TAG_SRC1) {
|
||||
snprintf(currhum_s, sizeof(currhum_s), "value_json.airhumidity");
|
||||
snprintf(currhum_cond, sizeof(currhum_cond), "%s is defined", currhum_s);
|
||||
}
|
||||
|
||||
snprintf(topic_t, sizeof(topic_t), "~/%s", Mqtt::tag_to_topic(device_type, tag).c_str());
|
||||
}
|
||||
|
||||
@@ -1480,6 +1517,7 @@ bool Mqtt::publish_ha_climate_config(const DeviceValue & dv, const bool has_room
|
||||
doc["curr_temp_t"] = topic_t;
|
||||
doc["curr_temp_tpl"] = (std::string) "{{" + currtemp_s + " if " + currtemp_cond + " else 'None'}}";
|
||||
}
|
||||
|
||||
if (tag >= DeviceValueTAG::TAG_SRC1) {
|
||||
doc["curr_hum_t"] = topic_t;
|
||||
doc["curr_hum_tpl"] = (std::string) "{{" + currhum_s + " if " + currhum_cond + " else 'None'}}";
|
||||
@@ -1531,12 +1569,15 @@ bool Mqtt::publish_ha_climate_config(const DeviceValue & dv, const bool has_room
|
||||
if (found_auto) {
|
||||
modes.add("auto");
|
||||
}
|
||||
|
||||
if (found_heat) {
|
||||
modes.add("heat");
|
||||
}
|
||||
|
||||
if (found_off) {
|
||||
modes.add("off");
|
||||
}
|
||||
|
||||
if (found_cool) {
|
||||
modes.add("cool");
|
||||
}
|
||||
|
||||
+2
-1
@@ -164,6 +164,7 @@ class Mqtt {
|
||||
if (discovery_prefix_.empty()) {
|
||||
return std::string{};
|
||||
}
|
||||
|
||||
return discovery_prefix_ + "/";
|
||||
}
|
||||
|
||||
@@ -334,4 +335,4 @@ class Mqtt {
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -151,6 +151,7 @@ bool Network::formatBSSID([[maybe_unused]] const String & bssid, [[maybe_unused]
|
||||
if (bssid.isEmpty() || sscanf(bssid.c_str(), "%X:%X:%X:%X:%X:%X", &tmp[0], &tmp[1], &tmp[2], &tmp[3], &tmp[4], &tmp[5]) != 6) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (uint8_t i = 0; i < 6; i++) {
|
||||
mac[i] = static_cast<uint8_t>(tmp[i]);
|
||||
}
|
||||
@@ -187,6 +188,7 @@ std::vector<Network::IPv6Address> Network::ipv6_addresses([[maybe_unused]] Netwo
|
||||
esp_ip6_addr_type_t type;
|
||||
const char * name;
|
||||
};
|
||||
|
||||
static const Scope scopes[] = {{ESP_IP6_ADDR_IS_GLOBAL, "global"},
|
||||
{ESP_IP6_ADDR_IS_UNIQUE_LOCAL, "unique local"},
|
||||
{ESP_IP6_ADDR_IS_SITE_LOCAL, "site local"},
|
||||
@@ -216,6 +218,7 @@ std::vector<IPAddress> Network::dns_servers(const NetworkInterface & netif) {
|
||||
servers.push_back(ip);
|
||||
}
|
||||
}
|
||||
|
||||
return servers;
|
||||
}
|
||||
|
||||
@@ -236,9 +239,11 @@ esp_err_t collect_ipv6_gateways(void * ctx) {
|
||||
if (router == nullptr || netif_get_index(router->netif) != args->netif_index) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const ip6_addr_t * addr = &router->next_hop_address;
|
||||
args->gateways->push_back(IPAddress(IPv6, reinterpret_cast<const uint8_t *>(addr->addr), ip6_addr_zone(addr)));
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
@@ -343,6 +348,7 @@ NetPhase Network::initialPhase() const {
|
||||
if (!ssid_.isEmpty() || ap_provisionMode_ == AP_MODE_NEVER) {
|
||||
return NetPhase::WIFI;
|
||||
}
|
||||
|
||||
return NetPhase::AP;
|
||||
}
|
||||
|
||||
@@ -354,6 +360,7 @@ void Network::loop() {
|
||||
reconnect_pending_ = false;
|
||||
reconnect();
|
||||
}
|
||||
|
||||
// already have a connection: verify it's still alive
|
||||
if (network_ip_ != 0 || last_disconnect_reason_ != 0) {
|
||||
checkConnection();
|
||||
@@ -394,10 +401,12 @@ void Network::checkConnection() {
|
||||
if (iface_from_desc(esp_netif_get_desc(netif)) != network_iface_) {
|
||||
continue;
|
||||
}
|
||||
|
||||
esp_netif_ip_info_t ip_info = {};
|
||||
if (esp_netif_is_netif_up(netif) && esp_netif_get_ip_info(netif, &ip_info) == ESP_OK && ip_info.ip.addr == network_ip_) {
|
||||
still_up = true;
|
||||
}
|
||||
|
||||
break; // only one active netif per kind in our world (sta / eth / ap)
|
||||
}
|
||||
|
||||
@@ -416,8 +425,10 @@ void Network::checkConnection() {
|
||||
if (wifi_radio_off_) {
|
||||
startWiFiRadio();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
begin();
|
||||
}
|
||||
#endif
|
||||
@@ -439,6 +450,7 @@ void Network::setWiFiPower([[maybe_unused]] uint8_t tx_power) {
|
||||
if (!WiFi.setTxPower(static_cast<wifi_power_t>(tx_power))) {
|
||||
emsesp::EMSESP::logger().warning("Failed to set WiFi Tx Power");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -455,6 +467,7 @@ void Network::setWiFiPower([[maybe_unused]] uint8_t tx_power) {
|
||||
if (newrssi < threshold) {
|
||||
min_tx_pwr = threshold - newrssi;
|
||||
}
|
||||
|
||||
if (min_tx_pwr > max_tx_pwr) {
|
||||
min_tx_pwr = max_tx_pwr;
|
||||
}
|
||||
@@ -621,6 +634,7 @@ void Network::startWiFiRadio() {
|
||||
} else {
|
||||
esp_wifi_set_bandwidth(static_cast<wifi_interface_t>(ESP_IF_WIFI_STA), WIFI_BW_HT40);
|
||||
}
|
||||
|
||||
if (nosleep_) {
|
||||
WiFi.setSleep(false); // turn off sleep - WIFI_PS_NONE
|
||||
} else {
|
||||
@@ -638,6 +652,7 @@ void Network::stopWiFiRadio() {
|
||||
if (wifi_radio_off_) {
|
||||
return;
|
||||
}
|
||||
|
||||
WiFi.mode(WIFI_OFF);
|
||||
wifi_radio_off_ = true;
|
||||
wifi_connect_pending_ = false;
|
||||
@@ -801,18 +816,23 @@ const char * Network::ethernet_status() const {
|
||||
if (phy_type_ == PHY_type::PHY_TYPE_NONE) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (ethClockConflictsPsram()) {
|
||||
return "disabled - clock mode GPIO16/17 conflicts with PSRAM";
|
||||
}
|
||||
|
||||
if (ethernet_connected()) {
|
||||
return "connected";
|
||||
}
|
||||
|
||||
if (ethernet_init_failed_) {
|
||||
return "failed to initialize - no PHY found, check the board profile";
|
||||
}
|
||||
|
||||
if (!ethernet_started_) {
|
||||
return "not started";
|
||||
}
|
||||
|
||||
return ethernet_connect_pending_ ? "connecting" : "no connection";
|
||||
#else
|
||||
return nullptr;
|
||||
@@ -871,6 +891,7 @@ void Network::findNetworks() {
|
||||
if (desc) {
|
||||
strlcpy(info.desc, desc, sizeof(info.desc));
|
||||
}
|
||||
|
||||
info.has_ipv6 = (esp_netif_get_ip6_linklocal(netif, &info.ip6) == ESP_OK);
|
||||
|
||||
best_iface = candidate;
|
||||
@@ -896,9 +917,11 @@ void Network::findNetworks() {
|
||||
} else if (network_iface_ == NetIface::AP) {
|
||||
phase_ = NetPhase::AP;
|
||||
}
|
||||
|
||||
if (!ethernet_ever_connected_ && ethernet_connected()) {
|
||||
ethernet_ever_connected_ = true;
|
||||
}
|
||||
|
||||
LOG_INFO("Network connected via %s (IP: " IPSTR ")",
|
||||
network_iface_ == NetIface::ETHERNET ? "Ethernet"
|
||||
: network_iface_ == NetIface::WIFI ? "WiFi"
|
||||
@@ -962,6 +985,7 @@ void Network::findNetworks() {
|
||||
LOG_WARNING("Ethernet failed to connect after %u attempts, switching to WiFi", connect_retry_);
|
||||
phase_ = NetPhase::WIFI;
|
||||
}
|
||||
|
||||
ethernet_connect_pending_ = false;
|
||||
} else if (phase_ == NetPhase::WIFI) {
|
||||
if (!wifi_ever_connected_) {
|
||||
@@ -972,8 +996,10 @@ void Network::findNetworks() {
|
||||
phase_ = NetPhase::AP;
|
||||
}
|
||||
}
|
||||
|
||||
wifi_connect_pending_ = false;
|
||||
}
|
||||
|
||||
connect_retry_ = 0;
|
||||
}
|
||||
}
|
||||
@@ -1009,6 +1035,7 @@ void Network::startAP() {
|
||||
LOG_DEBUG("softAPConfig failed");
|
||||
return;
|
||||
}
|
||||
|
||||
esp_wifi_set_bandwidth(static_cast<wifi_interface_t>(ESP_IF_WIFI_AP), WIFI_BW_HT20);
|
||||
|
||||
// WiFi.softAPenableIPv6();
|
||||
@@ -1027,6 +1054,7 @@ void Network::startAP() {
|
||||
WiFi.softAPdisconnect(true);
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_INFO("Starting Access Point with captive portal on %u.%u.%u.%u", apIp[0], apIp[1], apIp[2], apIp[3]);
|
||||
|
||||
ap_dnsServer_ = new DNSServer;
|
||||
@@ -1035,6 +1063,7 @@ void Network::startAP() {
|
||||
WiFi.softAPdisconnect(true);
|
||||
return;
|
||||
}
|
||||
|
||||
ap_dnsServer_->start(DNS_PORT, "*", apIp);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -81,6 +81,7 @@ class PsramAsyncJsonResponse : public ::AsyncJsonResponse {
|
||||
if (_contentLength) {
|
||||
_isValid = true;
|
||||
}
|
||||
|
||||
return _contentLength;
|
||||
}
|
||||
|
||||
@@ -119,6 +120,7 @@ class PsramAsyncMessagePackResponse : public ::AsyncMessagePackResponse {
|
||||
if (_contentLength) {
|
||||
_isValid = true;
|
||||
}
|
||||
|
||||
return _contentLength;
|
||||
}
|
||||
|
||||
|
||||
@@ -57,6 +57,7 @@ class PsramJsonAllocator : public ArduinoJson::Allocator {
|
||||
if (p == nullptr) {
|
||||
p = malloc(size);
|
||||
}
|
||||
|
||||
return p;
|
||||
#endif
|
||||
}
|
||||
@@ -80,6 +81,7 @@ class PsramJsonAllocator : public ArduinoJson::Allocator {
|
||||
if (p == nullptr) {
|
||||
p = realloc(ptr, new_size);
|
||||
}
|
||||
|
||||
return p;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -36,10 +36,12 @@ uint32_t Roomctrl::timeout_ = 0;
|
||||
void Roomctrl::set_timeout(uint8_t t) {
|
||||
timeout_ = t * 3600000; // ms
|
||||
}
|
||||
|
||||
void Roomctrl::set_remotetemp(const uint8_t type, const uint8_t hc, const int16_t temp) {
|
||||
if (!type_[hc] && !type) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (remotetemp_[hc] != EMS_VALUE_INT16_NOTSET && temp == EMS_VALUE_INT16_NOTSET) { // switch remote off
|
||||
remotetemp_[hc] = EMS_VALUE_INT16_NOTSET;
|
||||
switch_off_[hc] = true;
|
||||
@@ -47,13 +49,16 @@ void Roomctrl::set_remotetemp(const uint8_t type, const uint8_t hc, const int16_
|
||||
sendtype_[hc] = SendType::TEMP;
|
||||
return;
|
||||
}
|
||||
|
||||
if (hc >= HCS || !type) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (remotetemp_[hc] != temp) {
|
||||
send_time_[hc] = uuid::get_uptime() - SEND_INTERVAL; // send now
|
||||
sendtype_[hc] = SendType::TEMP;
|
||||
}
|
||||
|
||||
type_[hc] = type;
|
||||
remotetemp_[hc] = temp;
|
||||
receive_time_[hc] = uuid::get_uptime();
|
||||
@@ -64,10 +69,12 @@ void Roomctrl::set_remotehum(const uint8_t type, const uint8_t hc, const int8_t
|
||||
if (hc >= HCS || type != type_[hc]) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (remotehum_[hc] != hum) {
|
||||
send_time_[hc] = uuid::get_uptime() - SEND_INTERVAL; // send now
|
||||
sendtype_[hc] = SendType::HUMI;
|
||||
}
|
||||
|
||||
remotehum_[hc] = hum;
|
||||
}
|
||||
|
||||
@@ -82,6 +89,7 @@ uint8_t Roomctrl::get_hc(uint8_t addr) {
|
||||
} else if (addr >= 0x18 && addr <= 0x1B && (type_[addr - 0x18] == RC20 || type_[addr - 0x18] == FB10)) {
|
||||
return addr - 0x18; // RC20, FB10
|
||||
}
|
||||
|
||||
return 0xFF; // invalid
|
||||
}
|
||||
|
||||
@@ -92,11 +100,13 @@ void Roomctrl::send(uint8_t addr) {
|
||||
if ((addr & 0x80) || EMSESP::system_.readonly_mode()) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t hc = get_hc(addr);
|
||||
// check address, reply only on addresses 0x18..0x1B or 0x40..0x43
|
||||
if (hc >= HCS) {
|
||||
return;
|
||||
}
|
||||
|
||||
// no reply if the temperature is not set
|
||||
if (!switch_off_[hc] && remotetemp_[hc] == EMS_VALUE_INT16_NOTSET && remotehum_[hc] == EMS_VALUE_UINT8_NOTSET) {
|
||||
return;
|
||||
@@ -108,12 +118,14 @@ void Roomctrl::send(uint8_t addr) {
|
||||
sendtype_[hc] = SendType::TEMP;
|
||||
EMSESP::logger().warning("remotetemp timeout hc%d, stop sending roomtemperature to thermostat", hc);
|
||||
}
|
||||
|
||||
if (switch_off_[hc] || (uuid::get_uptime() - send_time_[hc]) > SEND_INTERVAL) { // check interval
|
||||
if (type_[hc] == RC100H || type_[hc] == RT800 || type_[hc] == RC120RF) {
|
||||
if (sendtype_[hc] == SendType::HUMI) { // send humidity
|
||||
if (switch_off_[hc]) {
|
||||
remotehum_[hc] = EMS_VALUE_UINT8_NOTSET;
|
||||
}
|
||||
|
||||
send_time_[hc] = uuid::get_uptime();
|
||||
humidity(addr, 0x10, hc);
|
||||
sendtype_[hc] = SendType::TEMP;
|
||||
@@ -123,6 +135,7 @@ void Roomctrl::send(uint8_t addr) {
|
||||
} else {
|
||||
send_time_[hc] = uuid::get_uptime();
|
||||
}
|
||||
|
||||
temperature(addr, 0x10, hc); // send to master-thermostat
|
||||
}
|
||||
} else if (type_[hc] == RC200 || type_[hc] == RC100 || type_[hc] == FB10) {
|
||||
@@ -132,6 +145,7 @@ void Roomctrl::send(uint8_t addr) {
|
||||
send_time_[hc] = uuid::get_uptime();
|
||||
temperature(addr, 0x00, hc); // send to all
|
||||
}
|
||||
|
||||
if (remotehum_[hc] == EMS_VALUE_UINT8_NOTSET && switch_off_[hc]) {
|
||||
switch_off_[hc] = false;
|
||||
type_[hc] = RemoteType::NONE;
|
||||
@@ -149,22 +163,27 @@ void Roomctrl::check(uint8_t addr, const uint8_t * data, const uint8_t length) {
|
||||
if (EMSESP::system_.readonly_mode()) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t hc = get_hc(addr);
|
||||
if (hc >= HCS || length < 5) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (type_[hc] == SENSOR) {
|
||||
return;
|
||||
}
|
||||
|
||||
// no reply if the temperature is not set
|
||||
if (remotetemp_[hc] == EMS_VALUE_INT16_NOTSET) {
|
||||
return;
|
||||
}
|
||||
|
||||
// reply to writes with write nack byte
|
||||
if ((addr & 0x80) == 0) { // it's a write to us
|
||||
ack_write(); // accept writes, don't care.
|
||||
return;
|
||||
}
|
||||
|
||||
addr &= 0x7F;
|
||||
// reads: for now we only reply to version and remote temperature
|
||||
// empty message back if temperature not set or unknown message type
|
||||
@@ -382,6 +401,7 @@ void Roomctrl::humidity(uint8_t addr, uint8_t dst, uint8_t hc) {
|
||||
if (dew != EMS_VALUE_INT16_NOTSET) {
|
||||
dew8 = static_cast<int8_t>((dew >= 0 ? dew + 5 : dew - 5) / 10);
|
||||
}
|
||||
|
||||
uint8_t data[11];
|
||||
data[0] = addr | EMSbus::ems_mask();
|
||||
data[1] = dst & 0x7F;
|
||||
@@ -436,6 +456,7 @@ void Roomctrl::replyF7(uint8_t addr, uint8_t dst, uint8_t offset, uint8_t typehh
|
||||
data[7] = 0;
|
||||
data[8] = 0;
|
||||
}
|
||||
|
||||
data[9] = EMSbus::calculate_crc(data, 9); // append CRC
|
||||
EMSuart::transmit(data, 10);
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ class Roomctrl {
|
||||
static bool is_remote(const uint8_t hc) {
|
||||
return (hc < 4 && remotetemp_[hc] != EMS_VALUE_INT16_NOTSET);
|
||||
}
|
||||
|
||||
static void set_timeout(uint8_t t);
|
||||
|
||||
private:
|
||||
@@ -63,4 +64,4 @@ class Roomctrl {
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -46,6 +46,7 @@ void Shower::start() {
|
||||
LOG_WARNING("Coldshot failed. Shower not active");
|
||||
force_coldshot = false;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
FL_(coldshot_cmd),
|
||||
@@ -90,6 +91,7 @@ void Shower::loop() {
|
||||
set_shower_state(true);
|
||||
LOG_DEBUG("hot water still running, starting shower timer");
|
||||
}
|
||||
|
||||
// check if the shower has been on too long
|
||||
else if ((shower_alert_ && ((time_now - timer_start_) > next_alert_)) || force_coldshot) {
|
||||
shower_alert_start();
|
||||
@@ -138,6 +140,7 @@ void Shower::loop() {
|
||||
set_shower_state(false);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -178,6 +181,7 @@ void Shower::set_shower_state(bool state, bool force) {
|
||||
if ((shower_state_ == old_shower_state_) && !force) {
|
||||
return;
|
||||
}
|
||||
|
||||
old_shower_state_ = shower_state_;
|
||||
|
||||
// always publish as a string - see https://github.com/emsesp/EMS-ESP/issues/369
|
||||
@@ -205,6 +209,7 @@ void Shower::create_ha_discovery() {
|
||||
} else {
|
||||
snprintf(str, sizeof(str), "shower_active"); // v3.4 compatible
|
||||
}
|
||||
|
||||
doc["uniq_id"] = str;
|
||||
doc["def_ent_id"] = (std::string) "binary_sensor." + str;
|
||||
doc["stat_t"] = "~/shower_active";
|
||||
|
||||
@@ -41,6 +41,7 @@ class Shower {
|
||||
void shower_alert(bool enable) {
|
||||
shower_alert_ = enable;
|
||||
}
|
||||
|
||||
void ha_reset() {
|
||||
ha_configdone_ = false;
|
||||
}
|
||||
|
||||
@@ -41,6 +41,7 @@ std::deque<Token> exprToTokens(const std::string & expr) {
|
||||
i += (*p == '{') ? 1 : (*p == '}') ? -1 : 0;
|
||||
++p;
|
||||
}
|
||||
|
||||
const auto s = std::string(b, p);
|
||||
tokens.emplace_back(Token::Type::String, s, -3);
|
||||
if (*p == '\0') {
|
||||
@@ -84,6 +85,7 @@ std::deque<Token> exprToTokens(const std::string & expr) {
|
||||
while ((*p >= 'a' && *p <= 'z') || (*p >= 'A' && *p <= 'Z') || (*p == '_') || (*p & 0x80)) {
|
||||
++p;
|
||||
}
|
||||
|
||||
const auto s = std::string(b, p);
|
||||
tokens.emplace_back(Token::Type::String, s, -3);
|
||||
--p;
|
||||
@@ -93,6 +95,7 @@ std::deque<Token> exprToTokens(const std::string & expr) {
|
||||
while (*p && *p != '"') {
|
||||
++p;
|
||||
}
|
||||
|
||||
const auto s = std::string(b, p);
|
||||
tokens.emplace_back(Token::Type::String, s, -3);
|
||||
if (*p == '\0') {
|
||||
@@ -104,6 +107,7 @@ std::deque<Token> exprToTokens(const std::string & expr) {
|
||||
while (*p && *p != '\'') {
|
||||
++p;
|
||||
}
|
||||
|
||||
const auto s = std::string(b, p);
|
||||
tokens.emplace_back(Token::Type::String, s, -3);
|
||||
if (*p == '\0') {
|
||||
@@ -114,6 +118,7 @@ std::deque<Token> exprToTokens(const std::string & expr) {
|
||||
while (isdigit(*p) || *p == '.') {
|
||||
++p;
|
||||
}
|
||||
|
||||
const auto s = std::string(b, p);
|
||||
tokens.emplace_back(Token::Type::Number, s, -2);
|
||||
--p;
|
||||
@@ -160,6 +165,7 @@ std::deque<Token> exprToTokens(const std::string & expr) {
|
||||
tokens.pop_back();
|
||||
continue;
|
||||
}
|
||||
|
||||
token = Token::Type::Unary;
|
||||
c = 'm';
|
||||
precedence = 5;
|
||||
@@ -167,6 +173,7 @@ std::deque<Token> exprToTokens(const std::string & expr) {
|
||||
// otherwise, it's binary '-'
|
||||
precedence = 2;
|
||||
}
|
||||
|
||||
break;
|
||||
case '&':
|
||||
if (p[1] == '&')
|
||||
@@ -189,12 +196,14 @@ std::deque<Token> exprToTokens(const std::string & expr) {
|
||||
precedence = 2;
|
||||
token = Token::Type::Unary;
|
||||
}
|
||||
|
||||
break;
|
||||
case '<':
|
||||
if (p[1] == '=') {
|
||||
++p;
|
||||
c = '{';
|
||||
}
|
||||
|
||||
precedence = 1;
|
||||
token = Token::Type::Compare;
|
||||
break;
|
||||
@@ -203,6 +212,7 @@ std::deque<Token> exprToTokens(const std::string & expr) {
|
||||
++p;
|
||||
c = '}';
|
||||
}
|
||||
|
||||
precedence = 1;
|
||||
token = Token::Type::Compare;
|
||||
break;
|
||||
@@ -213,6 +223,7 @@ std::deque<Token> exprToTokens(const std::string & expr) {
|
||||
token = Token::Type::Compare;
|
||||
break;
|
||||
}
|
||||
|
||||
const auto s = std::string(1, c);
|
||||
tokens.emplace_back(token, s, precedence, rightAssociative);
|
||||
}
|
||||
@@ -321,6 +332,7 @@ std::deque<Token> shuntingYard(const std::deque<Token> & tokens) {
|
||||
queue.push_back(std::move(stack.back()));
|
||||
stack.pop_back();
|
||||
}
|
||||
|
||||
return queue;
|
||||
}
|
||||
|
||||
@@ -329,9 +341,11 @@ bool isnum(const std::string & s) {
|
||||
if (s.empty() || s.find_first_of("0123456789") == std::string::npos) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (s.find_first_not_of("0123456789.") == std::string::npos || (s[0] == '-' && s.find_first_not_of("0123456789.", 1) == std::string::npos)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -350,17 +364,20 @@ std::string commands(std::string & expr, bool quotes) {
|
||||
if (e == std::string::npos) {
|
||||
e = expr.length();
|
||||
}
|
||||
|
||||
char cmd[COMMAND_MAX_LENGTH];
|
||||
size_t l = e - f;
|
||||
if (l >= sizeof(cmd) - 1) {
|
||||
break;
|
||||
}
|
||||
|
||||
expr_new.copy(cmd, l, f);
|
||||
cmd[l] = '\0';
|
||||
|
||||
if (strstr(cmd, "/value") == nullptr) {
|
||||
strlcat(cmd, "/value", sizeof(cmd) - 6);
|
||||
}
|
||||
|
||||
JsonDocument doc_out;
|
||||
JsonDocument doc_in;
|
||||
JsonObject output = doc_out.to<JsonObject>();
|
||||
@@ -373,6 +390,7 @@ std::string commands(std::string & expr, bool quotes) {
|
||||
if (return_code != CommandRet::OK && return_code != CommandRet::NO_VALUE) {
|
||||
return expr = "";
|
||||
}
|
||||
|
||||
std::string data;
|
||||
if (output["api_data"].is<std::string>()) {
|
||||
data = output["api_data"].as<std::string>();
|
||||
@@ -383,12 +401,14 @@ std::string commands(std::string & expr, bool quotes) {
|
||||
} else {
|
||||
serializeJson(output, data);
|
||||
}
|
||||
|
||||
expr.replace(f, l, data);
|
||||
e = f + data.length();
|
||||
expr_new = Helpers::toLower(expr);
|
||||
f = expr_new.find(d, e);
|
||||
}
|
||||
}
|
||||
|
||||
if (quotes) {
|
||||
// remove double quotes
|
||||
auto f = expr.find("\"\"");
|
||||
@@ -397,6 +417,7 @@ std::string commands(std::string & expr, bool quotes) {
|
||||
f = expr.find("\"\"");
|
||||
}
|
||||
}
|
||||
|
||||
return expr;
|
||||
}
|
||||
|
||||
@@ -406,6 +427,7 @@ int to_logic(const std::string & s) {
|
||||
if (!Helpers::value2bool(s.c_str(), value_b)) {
|
||||
return -1; // invalid
|
||||
}
|
||||
|
||||
return value_b;
|
||||
|
||||
// if (s.empty()) {
|
||||
@@ -427,9 +449,11 @@ std::string to_string(double d) {
|
||||
while (!s.empty() && s.back() == '0') {
|
||||
s.pop_back();
|
||||
}
|
||||
|
||||
if (!s.empty() && s.back() == '.') {
|
||||
s.pop_back();
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
@@ -476,6 +500,7 @@ std::string calculate(const std::string & expr) {
|
||||
if (stack.empty()) {
|
||||
return "";
|
||||
}
|
||||
|
||||
const auto rhs = stack.back();
|
||||
stack.pop_back();
|
||||
if (token.str[0] == '!') {
|
||||
@@ -489,20 +514,25 @@ std::string calculate(const std::string & expr) {
|
||||
EMSESP::logger().warning("'!' needs a boolean or numeric operand, got '%s'. Literal text must be quoted", rhs.c_str());
|
||||
return "";
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (token.str[0] == 'h') {
|
||||
// hex string to number
|
||||
if (rhs.empty() || rhs.find_first_not_of("0123456789abcdefABCDEF") != std::string::npos) {
|
||||
return "";
|
||||
}
|
||||
|
||||
stack.push_back(to_string(std::stoi(rhs, 0, 16)));
|
||||
break;
|
||||
}
|
||||
|
||||
// bail out on non-numeric operand
|
||||
if (!isnum(rhs)) {
|
||||
return "";
|
||||
}
|
||||
|
||||
auto rhd = std::stod(rhs);
|
||||
switch (token.str[0]) {
|
||||
default:
|
||||
@@ -551,6 +581,7 @@ std::string calculate(const std::string & expr) {
|
||||
if (stack.size() < 2) {
|
||||
return "";
|
||||
}
|
||||
|
||||
const auto rhs = stack.back();
|
||||
stack.pop_back();
|
||||
const auto lhs = stack.back();
|
||||
@@ -564,6 +595,7 @@ std::string calculate(const std::string & expr) {
|
||||
stack.push_back((std::stod(lhs) < std::stod(rhs)) ? "1" : "0");
|
||||
break;
|
||||
}
|
||||
|
||||
stack.push_back((lhs < rhs) ? "1" : "0");
|
||||
break;
|
||||
case '{':
|
||||
@@ -571,6 +603,7 @@ std::string calculate(const std::string & expr) {
|
||||
stack.push_back((std::stod(lhs) <= std::stod(rhs)) ? "1" : "0");
|
||||
break;
|
||||
}
|
||||
|
||||
stack.push_back((lhs <= rhs) ? "1" : "0");
|
||||
break;
|
||||
case '>':
|
||||
@@ -578,6 +611,7 @@ std::string calculate(const std::string & expr) {
|
||||
stack.push_back((std::stod(lhs) > std::stod(rhs)) ? "1" : "0");
|
||||
break;
|
||||
}
|
||||
|
||||
stack.push_back((lhs > rhs) ? "1" : "0");
|
||||
break;
|
||||
case '}':
|
||||
@@ -585,6 +619,7 @@ std::string calculate(const std::string & expr) {
|
||||
stack.push_back((std::stod(lhs) >= std::stod(rhs)) ? "1" : "0");
|
||||
break;
|
||||
}
|
||||
|
||||
stack.push_back((lhs >= rhs) ? "1" : "0");
|
||||
break;
|
||||
case '=':
|
||||
@@ -592,6 +627,7 @@ std::string calculate(const std::string & expr) {
|
||||
stack.push_back((std::stod(lhs) == std::stod(rhs)) ? "1" : "0");
|
||||
break;
|
||||
}
|
||||
|
||||
// compare strings lower case
|
||||
stack.push_back((Helpers::toLower(lhs) == Helpers::toLower(rhs)) ? "1" : "0");
|
||||
break;
|
||||
@@ -600,6 +636,7 @@ std::string calculate(const std::string & expr) {
|
||||
stack.push_back((std::stod(lhs) != std::stod(rhs)) ? "1" : "0");
|
||||
break;
|
||||
}
|
||||
|
||||
// compare strings lower case
|
||||
stack.push_back((Helpers::toLower(lhs) != Helpers::toLower(rhs)) ? "1" : "0");
|
||||
break;
|
||||
@@ -610,6 +647,7 @@ std::string calculate(const std::string & expr) {
|
||||
if (stack.size() < 2) {
|
||||
return "";
|
||||
}
|
||||
|
||||
const auto rhs = to_logic(stack.back());
|
||||
stack.pop_back();
|
||||
const auto lhs = to_logic(stack.back());
|
||||
@@ -617,6 +655,7 @@ std::string calculate(const std::string & expr) {
|
||||
if (rhs < 0 || lhs < 0) {
|
||||
return "";
|
||||
}
|
||||
|
||||
switch (token.str[0]) {
|
||||
default:
|
||||
return "";
|
||||
@@ -634,6 +673,7 @@ std::string calculate(const std::string & expr) {
|
||||
if (stack.size() < 2) {
|
||||
return "";
|
||||
}
|
||||
|
||||
const auto rhs = stack.back();
|
||||
stack.pop_back();
|
||||
const auto lhs = stack.back();
|
||||
@@ -642,10 +682,12 @@ std::string calculate(const std::string & expr) {
|
||||
stack.push_back(lhs + rhs);
|
||||
break;
|
||||
}
|
||||
|
||||
// bail out on non-numeric operands
|
||||
if (!isnum(lhs) || !isnum(rhs)) {
|
||||
return "";
|
||||
}
|
||||
|
||||
auto lhd = std::stod(lhs);
|
||||
auto rhd = std::stod(rhs);
|
||||
switch (token.str[0]) {
|
||||
@@ -686,6 +728,7 @@ std::string calculate(const std::string & expr) {
|
||||
for (const auto & s : stack) {
|
||||
result += s;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -704,6 +747,7 @@ static size_t find_unquoted(const std::string & s, char c, size_t from = 0) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return std::string::npos;
|
||||
}
|
||||
|
||||
@@ -725,6 +769,7 @@ std::string compute(const std::string & expr) {
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
std::string cmd = expr_new.substr(f, e - f).c_str();
|
||||
JsonDocument doc;
|
||||
if (DeserializationError::Ok == deserializeJson(doc, cmd)) {
|
||||
@@ -747,6 +792,7 @@ std::string compute(const std::string & expr) {
|
||||
keys_s = p.key().c_str();
|
||||
}
|
||||
}
|
||||
|
||||
std::string value = doc[value_s] | "";
|
||||
std::string method = doc[method_s] | "GET";
|
||||
std::string result;
|
||||
@@ -758,6 +804,7 @@ std::string compute(const std::string & expr) {
|
||||
if (doc[keys_s].is<JsonArray>()) {
|
||||
keys_doc.set(doc[keys_s].as<JsonArray>());
|
||||
}
|
||||
|
||||
JsonArray keys = keys_doc.as<JsonArray>();
|
||||
if (key.length() || !keys.isNull()) {
|
||||
doc.clear();
|
||||
@@ -775,17 +822,21 @@ std::string compute(const std::string & expr) {
|
||||
break; // type mismatch
|
||||
}
|
||||
}
|
||||
|
||||
result = json.as<std::string>();
|
||||
}
|
||||
|
||||
expr_new.replace(f, e - f, result.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
expr_new.replace(f, e - f, result);
|
||||
} else if (httpResult != 0) {
|
||||
// httpResult of 0 means no url
|
||||
EMSESP::logger().warning("URL command failed with https code: %d, response: %s", httpResult, result.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
f = expr_new.find_first_of('{', e);
|
||||
}
|
||||
|
||||
@@ -799,15 +850,18 @@ std::string compute(const std::string & expr) {
|
||||
q1 = find_unquoted(expr_new, '?', q1 + 1);
|
||||
c1 = find_unquoted(expr_new, ':', c1 + 1);
|
||||
}
|
||||
|
||||
if (c1 == std::string::npos) {
|
||||
return ""; // error: missing colon
|
||||
}
|
||||
|
||||
auto s = q; // start search brackets
|
||||
int br = 0; // count brackets
|
||||
while (s > 0 && (br || expr_new[s - 1] != '(')) {
|
||||
s--;
|
||||
br += (expr_new[s] == '(' ? -1 : expr_new[s] == ')' ? 1 : 0);
|
||||
}
|
||||
|
||||
std::string cond = calculate(expr_new.substr(s, q - s));
|
||||
if (cond.length() == 0) {
|
||||
return "";
|
||||
@@ -821,6 +875,7 @@ std::string compute(const std::string & expr) {
|
||||
br += (expr_new[e] == ')' ? -1 : expr_new[e] == '(' ? 1 : 0);
|
||||
}
|
||||
}
|
||||
|
||||
expr_new.erase(c1, e + 1 - c1); // remove second expression after colon
|
||||
expr_new.erase(s, q + 1 - s); // remove condition before questionmark
|
||||
} else if (cond[0] == '0') {
|
||||
@@ -828,6 +883,7 @@ std::string compute(const std::string & expr) {
|
||||
} else {
|
||||
return ""; // error
|
||||
}
|
||||
|
||||
q = find_unquoted(expr_new, '?'); // search next instance
|
||||
}
|
||||
|
||||
|
||||
@@ -122,9 +122,11 @@ static void show_addresses(uuid::console::Shell & shell, NetworkInterface & neti
|
||||
for (const auto & dns : Network::dns_servers(netif)) {
|
||||
shell.printfln(" %s DNS Server: %s", dns.type() == IPv4 ? "IPv4" : "IPv6", uuid::printable_to_string(dns).c_str());
|
||||
}
|
||||
|
||||
for (const auto & ipv6 : Network::ipv6_addresses(netif)) {
|
||||
shell.printfln(" IPv6 address: %s (%s)", uuid::printable_to_string(ipv6.ip).c_str(), ipv6.scope);
|
||||
}
|
||||
|
||||
for (const auto & gateway : Network::ipv6_gateways(netif)) {
|
||||
shell.printfln(" IPv6 gateway: %s", uuid::printable_to_string(gateway).c_str());
|
||||
}
|
||||
@@ -170,6 +172,7 @@ uint8_t System::language_index() {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return 0; // EN only
|
||||
}
|
||||
|
||||
@@ -198,6 +201,7 @@ bool System::command_sendmail(const char * value, const int8_t) {
|
||||
if (!enabled) {
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG_DEBUG("Command sendmail port %d%s called with '%s'",
|
||||
port,
|
||||
security == EMAIL_SECURITY::SSL ? " (SSL)"
|
||||
@@ -219,6 +223,7 @@ bool System::command_sendmail(const char * value, const int8_t) {
|
||||
body = doc["body"] | body;
|
||||
}
|
||||
}
|
||||
|
||||
// keep the original body if the calculator returns nothing, so unquoted literal text still gets sent
|
||||
std::string computed_body = compute(body.c_str());
|
||||
if (!computed_body.empty()) {
|
||||
@@ -269,6 +274,7 @@ bool System::command_sendmail(const char * value, const int8_t) {
|
||||
cleanup();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!smtp->isConnected()) {
|
||||
LOG_ERROR("send email connection error: %s", smtp->status().text.c_str());
|
||||
cleanup();
|
||||
@@ -284,6 +290,7 @@ bool System::command_sendmail(const char * value, const int8_t) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
SMTPMessage & msg = smtp->getMessage();
|
||||
msg.headers.add(rfc822_subject, subject);
|
||||
msg.headers.add(rfc822_from, sender);
|
||||
@@ -324,6 +331,7 @@ std::string System::languages_string() {
|
||||
languages_string += ",";
|
||||
}
|
||||
}
|
||||
|
||||
languages_string += ")";
|
||||
return languages_string;
|
||||
}
|
||||
@@ -338,6 +346,7 @@ bool System::command_response(const char * value, const int8_t id, JsonObject ou
|
||||
} else {
|
||||
output["response"] = Mqtt::get_response();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -454,6 +463,7 @@ bool System::command_watch(const char * value, const int8_t id) {
|
||||
if (w == 0 || EMSESP::watch() == EMSESP::Watch::WATCH_OFF) {
|
||||
EMSESP::watch_id(0);
|
||||
}
|
||||
|
||||
if (Mqtt::publish_single() && w != EMSESP::watch()) {
|
||||
if (Mqtt::publish_single2cmd()) {
|
||||
Mqtt::queue_publish("system/watch", EMSESP::system_.enum_format() == ENUM_FORMAT_INDEX ? Helpers::itoa(w) : (FL_(list_watch)[w]));
|
||||
@@ -461,6 +471,7 @@ bool System::command_watch(const char * value, const int8_t id) {
|
||||
Mqtt::queue_publish("system_data/watch", EMSESP::system_.enum_format() == ENUM_FORMAT_INDEX ? Helpers::itoa(w) : (FL_(list_watch)[w]));
|
||||
}
|
||||
}
|
||||
|
||||
EMSESP::watch(w);
|
||||
return true;
|
||||
} else if (i) {
|
||||
@@ -471,12 +482,15 @@ bool System::command_watch(const char * value, const int8_t id) {
|
||||
Mqtt::queue_publish("system_data/watch", Helpers::hextoa(i));
|
||||
}
|
||||
}
|
||||
|
||||
EMSESP::watch_id(i);
|
||||
if (EMSESP::watch() == EMSESP::Watch::WATCH_OFF) {
|
||||
EMSESP::watch(EMSESP::Watch::WATCH_ON);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -484,6 +498,7 @@ void System::store_nvs_values() {
|
||||
if (Command::find_command(EMSdevice::DeviceType::BOILER, 0, "nompower", 0) != nullptr) {
|
||||
Command::call(EMSdevice::DeviceType::BOILER, "nompower", "-1"); // trigger a write
|
||||
}
|
||||
|
||||
EMSESP::analogsensor_.store_counters();
|
||||
EMSESP::nvs_.end();
|
||||
}
|
||||
@@ -543,6 +558,7 @@ void System::get_partition_info() {
|
||||
if (EMSESP::nvs_.isKey(part->label)) {
|
||||
p_info.version = EMSESP::nvs_.getString(part->label).c_str();
|
||||
}
|
||||
|
||||
char c[20];
|
||||
snprintf(c, sizeof(c), "d_%s", (const char *)part->label);
|
||||
time_t d = EMSESP::nvs_.getULong(c, 0);
|
||||
@@ -556,12 +572,14 @@ void System::get_partition_info() {
|
||||
} else {
|
||||
p_info.size = 0;
|
||||
}
|
||||
|
||||
partition_info_[part->label] = p_info;
|
||||
}
|
||||
}
|
||||
|
||||
it = esp_partition_next(it); // loop to next partition
|
||||
}
|
||||
|
||||
esp_partition_iterator_release(it);
|
||||
#endif
|
||||
}
|
||||
@@ -577,14 +595,17 @@ void System::recovery_installed() {
|
||||
if (EMSESP::nvs_.isKey(factory->label)) {
|
||||
EMSESP::nvs_.remove(factory->label);
|
||||
}
|
||||
|
||||
const time_t now = time(nullptr);
|
||||
if (now > 1500000000L) {
|
||||
EMSESP::nvs_.putULong(c, now); // UTC epoch, read by the recovery app. Only if the clock is set (NTP)
|
||||
} else if (EMSESP::nvs_.isKey(c)) {
|
||||
EMSESP::nvs_.remove(c); // clock not set, the date of the previous firmware is wrong
|
||||
}
|
||||
|
||||
partition_info_.erase(factory->label); // on the AsyncTCP task, same as the web handlers reading it
|
||||
}
|
||||
|
||||
LOG_INFO("Recovery firmware v%s installed", recovery_version().c_str());
|
||||
#endif
|
||||
}
|
||||
@@ -697,6 +718,7 @@ void System::system_restart(const char * partitionname) {
|
||||
LOG_ERROR("Partition '%s' not found", partitionname);
|
||||
return;
|
||||
}
|
||||
|
||||
if (strcmp(partition->label, partitionname) && strcmp(partitionname, "boot") != 0) {
|
||||
partition = esp_ota_get_next_update_partition(partition);
|
||||
if (!partition || strcmp(partition->label, partitionname)) {
|
||||
@@ -704,6 +726,7 @@ void System::system_restart(const char * partitionname) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// error if partition is empty
|
||||
uint64_t buffer;
|
||||
esp_partition_read(partition, 0, &buffer, 8);
|
||||
@@ -711,6 +734,7 @@ void System::system_restart(const char * partitionname) {
|
||||
LOG_ERROR("Partition '%s' is empty, not bootable", partition->label);
|
||||
return;
|
||||
}
|
||||
|
||||
// set the boot partition
|
||||
esp_ota_set_boot_partition(partition);
|
||||
}
|
||||
@@ -728,6 +752,7 @@ void System::system_restart(const char * partitionname) {
|
||||
Shell::loop_all();
|
||||
delay(10); // give telnet TCP stack time to transmit
|
||||
}
|
||||
|
||||
Serial.flush(); // wait for hardware TX buffer to drain
|
||||
|
||||
Mqtt::disconnect(); // gracefully disconnect MQTT (flushes the DISCONNECT before reboot, needed for QOS1)
|
||||
@@ -767,6 +792,7 @@ void System::syslog_init() {
|
||||
syslog_.mark_interval(0);
|
||||
// syslog_.destination("");
|
||||
}
|
||||
|
||||
if (Mqtt::publish_single()) {
|
||||
if (Mqtt::publish_single2cmd()) {
|
||||
Mqtt::queue_publish("system/syslog", syslog_enabled_ ? (FL_(list_syslog_level)[syslog_level_ + 1]) : "off");
|
||||
@@ -806,6 +832,7 @@ void System::modbus_init() {
|
||||
delete EMSESP::modbus_;
|
||||
EMSESP::modbus_ = nullptr;
|
||||
}
|
||||
|
||||
modbus_enabled_ = settings.modbus_enabled;
|
||||
modbus_port_ = settings.modbus_port;
|
||||
modbus_max_clients_ = settings.modbus_max_clients;
|
||||
@@ -871,6 +898,7 @@ void System::check_crash_loop() {
|
||||
if (factory == nullptr || factory == esp_ota_get_running_partition()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// fails if the factory partition doesn't hold a valid image, then keep trying the current one
|
||||
if (esp_ota_set_boot_partition(factory) == ESP_OK) {
|
||||
crash_count_ = 0;
|
||||
@@ -889,6 +917,7 @@ void System::check_firmware_health() {
|
||||
if (firmware_healthy_ || uuid::get_uptime_sec() < FIRMWARE_HEALTHY_UPTIME) {
|
||||
return;
|
||||
}
|
||||
|
||||
firmware_healthy_ = true;
|
||||
crash_count_ = 0;
|
||||
if (OtaUpdater::confirmRunningApp()) {
|
||||
@@ -905,6 +934,7 @@ void System::start() {
|
||||
if (crash_count_) {
|
||||
LOG_WARNING("Restarted after a crash (%u in a row)", crash_count_);
|
||||
}
|
||||
|
||||
const esp_partition_t * invalid = esp_ota_get_last_invalid_partition();
|
||||
if (invalid != nullptr) {
|
||||
LOG_WARNING("Firmware in partition %s failed to start and was rolled back", invalid->label);
|
||||
@@ -986,6 +1016,7 @@ void System::button_OnLongPress(PButton & b) {
|
||||
LOG_NOTICE("reset disabled, can't boot");
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_NOTICE("Button pressed - long press - restart EMS-ESP");
|
||||
EMSESP::system_.system_restart();
|
||||
}
|
||||
@@ -997,6 +1028,7 @@ void System::button_OnVLongPress(PButton & b) {
|
||||
LOG_NOTICE("reset disabled, can't boot");
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_NOTICE("Button pressed - very long press - perform factory reset in Recovery Mode");
|
||||
EMSESP::system_.system_restart("boot");
|
||||
// EMSESP::led_.start_led_fast_flash(5); // Start LED flash timer for 5 seconds
|
||||
@@ -1009,6 +1041,7 @@ void System::button_init() {
|
||||
LOG_WARNING("Multi-functional button not detected");
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_DEBUG("Multi-functional button enabled");
|
||||
|
||||
myPButton_.onClick(BUTTON_Debounce, button_OnClick);
|
||||
@@ -1077,6 +1110,7 @@ void System::send_info_mqtt() {
|
||||
if (!Mqtt::connected() || connection == _connection) {
|
||||
return;
|
||||
}
|
||||
|
||||
_connection = connection;
|
||||
JsonDocument doc;
|
||||
|
||||
@@ -1270,6 +1304,7 @@ int8_t System::wifi_quality(int8_t dBm) {
|
||||
if (dBm >= -50) {
|
||||
return 100;
|
||||
}
|
||||
|
||||
return 2 * (dBm + 100);
|
||||
}
|
||||
|
||||
@@ -1358,12 +1393,14 @@ void System::show_system(uuid::console::Shell & shell) {
|
||||
} else {
|
||||
shell.printfln(" PSRAM: not available");
|
||||
}
|
||||
|
||||
// GPIOs
|
||||
shell.println(" GPIOs:");
|
||||
shell.printf(" allowed:");
|
||||
for (const auto & gpio : valid_system_gpios_) {
|
||||
shell.printf(" %d", gpio);
|
||||
}
|
||||
|
||||
shell.printfln(" [total %d]", valid_system_gpios_.size());
|
||||
shell.printf(" in use:");
|
||||
auto sorted_gpios = used_gpios_;
|
||||
@@ -1371,12 +1408,14 @@ void System::show_system(uuid::console::Shell & shell) {
|
||||
for (const auto & gpio : sorted_gpios) {
|
||||
shell.printf(" %d(%s)", gpio.pin, gpio.source.c_str());
|
||||
}
|
||||
|
||||
shell.printfln(" [total %d]", used_gpios_.size());
|
||||
auto available = available_gpios();
|
||||
shell.printf(" available:");
|
||||
for (const auto & gpio : available) {
|
||||
shell.printf(" %d", gpio);
|
||||
}
|
||||
|
||||
shell.printfln(" [total %d]", available.size());
|
||||
// List all partitions and their version info
|
||||
shell.println(" Partitions:");
|
||||
@@ -1394,6 +1433,7 @@ void System::show_system(uuid::console::Shell & shell) {
|
||||
if (partition.second.version.empty()) {
|
||||
continue; // no version, empty string
|
||||
}
|
||||
|
||||
std::string installed;
|
||||
if (partition.second.install_date > 0) {
|
||||
char time_string[25];
|
||||
@@ -1401,11 +1441,13 @@ void System::show_system(uuid::console::Shell & shell) {
|
||||
strftime(time_string, sizeof(time_string), "%FT%T", localtime(&d));
|
||||
installed = std::string(", installed on ") + time_string;
|
||||
}
|
||||
|
||||
const bool is_running = strcmp(running_label, partition.first.c_str()) == 0;
|
||||
const bool is_boot = strcmp(boot_label, partition.first.c_str()) == 0;
|
||||
if (is_boot) {
|
||||
boot_listed = true;
|
||||
}
|
||||
|
||||
const char * mark = is_running && is_boot ? "** active, boot **" : is_running ? "** active **" : is_boot ? "** boot **" : "";
|
||||
shell.printfln(" %s: v%s (%d KB%s) %s", partition.first.c_str(), partition.second.version.c_str(), partition.second.size, installed.c_str(), mark);
|
||||
}
|
||||
@@ -1421,12 +1463,14 @@ void System::show_system(uuid::console::Shell & shell) {
|
||||
if (is_boot) {
|
||||
boot_listed = true;
|
||||
}
|
||||
|
||||
int size_kb = 0;
|
||||
esp_image_metadata_t meta = {};
|
||||
esp_partition_pos_t part_pos = {.offset = factory->address, .size = factory->size};
|
||||
if (esp_image_verify(ESP_IMAGE_VERIFY_SILENT, &part_pos, &meta) == ESP_OK) {
|
||||
size_kb = static_cast<int>(meta.image_len / 1024);
|
||||
}
|
||||
|
||||
const char * mark = is_running && is_boot ? "** active, boot **" : is_running ? "** active **" : is_boot ? "** boot **" : "";
|
||||
shell.printfln(" %s: Recovery v%s (%d KB) %s", factory->label, desc.version, size_kb, mark);
|
||||
}
|
||||
@@ -1477,11 +1521,14 @@ void System::show_system(uuid::console::Shell & shell) {
|
||||
shell.printfln(" = unknown");
|
||||
break;
|
||||
}
|
||||
|
||||
err = nvs_entry_next(&it);
|
||||
}
|
||||
|
||||
if (it != nullptr) {
|
||||
nvs_release_iterator(it); // just in case
|
||||
}
|
||||
|
||||
if (err == ESP_OK) {
|
||||
shell.println();
|
||||
}
|
||||
@@ -1538,6 +1585,7 @@ void System::show_system(uuid::console::Shell & shell) {
|
||||
if (ethernet_status != nullptr) {
|
||||
shell.printfln(" Ethernet Status: %s", ethernet_status);
|
||||
}
|
||||
|
||||
if (EMSESP::network_.ethernet_connected()) {
|
||||
shell.printfln(" Ethernet MAC address: %s", ETH.macAddress().c_str());
|
||||
shell.printfln(" Hostname: %s", ETH.getHostname());
|
||||
@@ -1616,24 +1664,31 @@ bool System::check_restore() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// continue processing the rest of the sections
|
||||
saveSettings(EMSESP_SETTINGS_FILE, section);
|
||||
}
|
||||
|
||||
if (section_type == "commands") {
|
||||
saveSettings(EMSESP_COMMANDS_FILE, section);
|
||||
}
|
||||
|
||||
if (section_type == "schedule") {
|
||||
saveSettings(EMSESP_SCHEDULER_FILE, section);
|
||||
}
|
||||
|
||||
if (section_type == "customizations") {
|
||||
saveSettings(EMSESP_CUSTOMIZATION_FILE, section);
|
||||
}
|
||||
|
||||
if (section_type == "entities") {
|
||||
saveSettings(EMSESP_CUSTOMENTITY_FILE, section);
|
||||
}
|
||||
|
||||
if (section_type == "modules") {
|
||||
saveSettings(EMSESP_MODULES_FILE, section);
|
||||
}
|
||||
|
||||
if (section_type == "customSupport") {
|
||||
// it's a custom support, extract json and write to /config/customSupport.json file
|
||||
File customSupportFile = LittleFS.open(EMSESP_CUSTOMSUPPORT_FILE, "w");
|
||||
@@ -1660,6 +1715,7 @@ bool System::check_restore() {
|
||||
EMSESP::nvs_.putChar(key.c_str(), v);
|
||||
LOG_DEBUG("Restored NVS value: %s = %d", key.c_str(), v);
|
||||
}
|
||||
|
||||
break;
|
||||
case NVS_TYPE_U8:
|
||||
if (entry["value"].is<JsonVariantConst>()) {
|
||||
@@ -1667,6 +1723,7 @@ bool System::check_restore() {
|
||||
EMSESP::nvs_.putUChar(key.c_str(), v);
|
||||
LOG_DEBUG("Restored NVS value: %s = %d", key.c_str(), v);
|
||||
}
|
||||
|
||||
break;
|
||||
case NVS_TYPE_I32:
|
||||
if (entry["value"].is<JsonVariantConst>()) {
|
||||
@@ -1674,6 +1731,7 @@ bool System::check_restore() {
|
||||
EMSESP::nvs_.putInt(key.c_str(), v);
|
||||
LOG_DEBUG("Restored NVS value: %s = %d", key.c_str(), v);
|
||||
}
|
||||
|
||||
break;
|
||||
case NVS_TYPE_U32:
|
||||
if (entry["value"].is<JsonVariantConst>()) {
|
||||
@@ -1681,6 +1739,7 @@ bool System::check_restore() {
|
||||
EMSESP::nvs_.putUInt(key.c_str(), v);
|
||||
LOG_DEBUG("Restored NVS value: %s = %d", key.c_str(), v);
|
||||
}
|
||||
|
||||
break;
|
||||
case NVS_TYPE_I64:
|
||||
if (entry["value"].is<JsonVariantConst>()) {
|
||||
@@ -1688,6 +1747,7 @@ bool System::check_restore() {
|
||||
EMSESP::nvs_.putLong64(key.c_str(), v);
|
||||
LOG_DEBUG("Restored NVS value: %s = %d", key.c_str(), v);
|
||||
}
|
||||
|
||||
break;
|
||||
case NVS_TYPE_U64:
|
||||
if (entry["value"].is<JsonVariantConst>()) {
|
||||
@@ -1695,6 +1755,7 @@ bool System::check_restore() {
|
||||
EMSESP::nvs_.putULong64(key.c_str(), v);
|
||||
LOG_DEBUG("Restored NVS value: %s = %d", key.c_str(), v);
|
||||
}
|
||||
|
||||
break;
|
||||
case NVS_TYPE_BLOB:
|
||||
// used for double values
|
||||
@@ -1703,6 +1764,7 @@ bool System::check_restore() {
|
||||
EMSESP::nvs_.putDouble(key.c_str(), v);
|
||||
LOG_DEBUG("Restored NVS value: %s = %d", key.c_str(), v);
|
||||
}
|
||||
|
||||
break;
|
||||
case NVS_TYPE_STR:
|
||||
case NVS_TYPE_ANY:
|
||||
@@ -1712,6 +1774,7 @@ bool System::check_restore() {
|
||||
EMSESP::nvs_.putString(key.c_str(), v.c_str());
|
||||
LOG_DEBUG("Restored NVS value: %s = %s", key.c_str(), v.c_str());
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1824,6 +1887,7 @@ bool System::check_upgrade() {
|
||||
LOG_INFO("Upgrade: setting MQTT Entity ID format to v3.6 format (4)");
|
||||
return StateUpdateResult::CHANGED;
|
||||
}
|
||||
|
||||
return StateUpdateResult::UNCHANGED;
|
||||
});
|
||||
}
|
||||
@@ -1840,11 +1904,13 @@ bool System::check_upgrade() {
|
||||
LOG_INFO("Upgrade: setting WiFi TX Power to Auto");
|
||||
changed = StateUpdateResult::CHANGED;
|
||||
}
|
||||
|
||||
if (networkSettings.nosleep != true) {
|
||||
networkSettings.nosleep = true;
|
||||
LOG_INFO("Upgrade: disabling WiFi nosleep");
|
||||
changed = StateUpdateResult::CHANGED;
|
||||
}
|
||||
|
||||
return changed;
|
||||
});
|
||||
EMSESP::network_.reconnect();
|
||||
@@ -1873,6 +1939,7 @@ bool System::check_upgrade() {
|
||||
LOG_INFO("Upgrade: setting AP provision mode to on disconnect");
|
||||
return StateUpdateResult::CHANGED;
|
||||
}
|
||||
|
||||
return StateUpdateResult::UNCHANGED;
|
||||
});
|
||||
// Scheduler name is now mandatory, update FS if name is empty
|
||||
@@ -1885,6 +1952,7 @@ bool System::check_upgrade() {
|
||||
schedule_changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
return schedule_changed ? StateUpdateResult::CHANGED : StateUpdateResult::UNCHANGED;
|
||||
});
|
||||
#endif
|
||||
@@ -1923,9 +1991,11 @@ bool System::check_upgrade() {
|
||||
} else {
|
||||
snprintf(ci.name, sizeof(ci.name), "schedule_%d", idx);
|
||||
}
|
||||
|
||||
commands.commandItems.push_back(ci);
|
||||
idx++;
|
||||
}
|
||||
|
||||
return StateUpdateResult::CHANGED;
|
||||
});
|
||||
|
||||
@@ -1937,12 +2007,14 @@ bool System::check_upgrade() {
|
||||
if (it == scheduler.scheduleItems.end()) {
|
||||
break;
|
||||
}
|
||||
|
||||
// flag 132 (0x84) is the old IMMEDIATE format which has no command - erase the entry
|
||||
if (item["flags"].as<uint8_t>() == 0x84) {
|
||||
it = scheduler.scheduleItems.erase(it);
|
||||
idx++;
|
||||
continue;
|
||||
}
|
||||
|
||||
const char * nm = item["name"];
|
||||
char cmd_name[sizeof(it->name)];
|
||||
if (nm != nullptr && nm[0] != '\0') {
|
||||
@@ -1951,10 +2023,12 @@ bool System::check_upgrade() {
|
||||
snprintf(cmd_name, sizeof(cmd_name), "schedule_%d", idx);
|
||||
strlcpy(it->name, cmd_name, sizeof(it->name)); // keep entry name consistent with its command
|
||||
}
|
||||
|
||||
it->cmd_name = cmd_name;
|
||||
++it;
|
||||
idx++;
|
||||
}
|
||||
|
||||
return StateUpdateResult::CHANGED;
|
||||
});
|
||||
|
||||
@@ -1975,11 +2049,13 @@ bool System::check_upgrade() {
|
||||
return StateUpdateResult::CHANGED;
|
||||
}
|
||||
}
|
||||
|
||||
// Migrate language from cz to cs
|
||||
if (settings.locale == "cz") {
|
||||
settings.locale = "cs";
|
||||
return StateUpdateResult::CHANGED;
|
||||
}
|
||||
|
||||
return StateUpdateResult::UNCHANGED;
|
||||
});
|
||||
} else if (this_version < settings_version) {
|
||||
@@ -2060,8 +2136,10 @@ void System::exportSettings(const std::string & type, [[maybe_unused]] const cha
|
||||
LOG_ERROR("Failed to deserialize settings file %s", filename);
|
||||
return;
|
||||
}
|
||||
|
||||
output[section].set(jsonDocument.as<JsonObject>());
|
||||
}
|
||||
|
||||
LOG_DEBUG("Exported %s settings from file %s", section, filename);
|
||||
} else {
|
||||
LOG_ERROR("No settings file for %s found", filename);
|
||||
@@ -2177,6 +2255,7 @@ void System::exportSystemBackup(JsonObject output) {
|
||||
entry["value"] = EMSESP::nvs_.getString(info.key);
|
||||
break;
|
||||
}
|
||||
|
||||
err = nvs_entry_next(&it);
|
||||
}
|
||||
|
||||
@@ -2279,6 +2358,7 @@ bool System::command_service(const char * cmd, const char * value) {
|
||||
if (ok) {
|
||||
LOG_INFO("System command '%s' with value '%s'", cmd, value);
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
@@ -2288,9 +2368,11 @@ bool System::get_value_info(JsonObject output, const char * cmd) {
|
||||
LOG_ERROR("empty system command");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!strcmp(cmd, "restart")) { // restart is a command, not an entity
|
||||
return false;
|
||||
}
|
||||
|
||||
// check for hardcoded "info"/"value"
|
||||
if (!strcmp(cmd, F_(info)) || !strcmp(cmd, F_(values))) {
|
||||
return command_info("", 0, output);
|
||||
@@ -2303,6 +2385,7 @@ bool System::get_value_info(JsonObject output, const char * cmd) {
|
||||
output["api_data"] = metrics;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -2323,6 +2406,7 @@ bool System::get_value_info(JsonObject output, const char * cmd) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -2344,9 +2428,11 @@ bool System::get_value_info(JsonObject output, const char * cmd) {
|
||||
for (JsonPair p1 : p.value().as<JsonObject>()) {
|
||||
output[p1.key().c_str()] = p1.value().as<std::string>();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -2363,6 +2449,7 @@ bool System::get_value_info(JsonObject output, const char * cmd) {
|
||||
output["api_data"] = p1.value().as<std::string>();
|
||||
return true;
|
||||
}
|
||||
|
||||
get_value_json(output, p_key, p1_key, p1.value());
|
||||
return true;
|
||||
}
|
||||
@@ -2370,6 +2457,7 @@ bool System::get_value_info(JsonObject output, const char * cmd) {
|
||||
} // else skip, but we don't have value pairs in system root
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -2378,6 +2466,7 @@ void System::get_value_json(JsonObject output, const std::string & circuit, cons
|
||||
if (circuit.length()) {
|
||||
output["circuit"] = circuit;
|
||||
}
|
||||
|
||||
output["readable"] = true;
|
||||
output["writeable"] =
|
||||
(name == "txpause" || name == "showerTimer" || name == "showerAlert" || name == "enabled" || name == "hideLed" || name == "analogEnabled");
|
||||
@@ -2420,6 +2509,7 @@ std::string System::get_metrics_prometheus() {
|
||||
escaped += c;
|
||||
}
|
||||
}
|
||||
|
||||
return escaped;
|
||||
};
|
||||
|
||||
@@ -2435,6 +2525,7 @@ std::string System::get_metrics_prometheus() {
|
||||
c = '_';
|
||||
}
|
||||
}
|
||||
|
||||
return sanitized;
|
||||
};
|
||||
|
||||
@@ -2446,6 +2537,7 @@ std::string System::get_metrics_prometheus() {
|
||||
c = tolower(c);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
@@ -2454,14 +2546,17 @@ std::string System::get_metrics_prometheus() {
|
||||
if (path == "system" && key == "uptime") {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (path == "ntp" && key == "timestamp") {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (path.find("devices[") != std::string::npos) {
|
||||
if (key == "handlersReceived" || key == "handlersFetched" || key == "handlersPending" || key == "handlersIgnored") {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
@@ -2519,11 +2614,14 @@ std::string System::get_metrics_prometheus() {
|
||||
if (!first) {
|
||||
result += ", ";
|
||||
}
|
||||
|
||||
result += label.first + "=\"" + escape_label(label.second) + "\"";
|
||||
first = false;
|
||||
}
|
||||
|
||||
result += "}";
|
||||
}
|
||||
|
||||
result += " " + std::to_string(device["productID"].as<int>()) + "\n";
|
||||
}
|
||||
|
||||
@@ -2544,11 +2642,14 @@ std::string System::get_metrics_prometheus() {
|
||||
if (!first) {
|
||||
result += ", ";
|
||||
}
|
||||
|
||||
result += label.first + "=\"" + escape_label(label.second) + "\"";
|
||||
first = false;
|
||||
}
|
||||
|
||||
result += "}";
|
||||
}
|
||||
|
||||
result += " " + std::to_string(device["entities"].as<int>()) + "\n";
|
||||
}
|
||||
}
|
||||
@@ -2578,6 +2679,7 @@ std::string System::get_metrics_prometheus() {
|
||||
snprintf(val_str, sizeof(val_str), "%.2f", p.value().as<float>());
|
||||
result += val_str;
|
||||
}
|
||||
|
||||
result += "\n";
|
||||
} else if (is_string) {
|
||||
// collect string for info metric (skip dynamic strings like uptime and timestamp)
|
||||
@@ -2592,6 +2694,7 @@ std::string System::get_metrics_prometheus() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!key_exists) {
|
||||
local_info_labels.push_back({lower_key, val});
|
||||
}
|
||||
@@ -2617,11 +2720,14 @@ std::string System::get_metrics_prometheus() {
|
||||
if (!first) {
|
||||
result += ", ";
|
||||
}
|
||||
|
||||
result += label.first + "=\"" + escape_label(label.second) + "\"";
|
||||
first = false;
|
||||
}
|
||||
|
||||
result += "}";
|
||||
}
|
||||
|
||||
result += " 1\n";
|
||||
}
|
||||
};
|
||||
@@ -2692,6 +2798,7 @@ bool System::command_info(const char * value, const int8_t id, JsonObject output
|
||||
node["psramSize"] = EMSESP::system_.PSram();
|
||||
node["freePsram"] = ESP.getFreePsram() / 1024;
|
||||
}
|
||||
|
||||
node["model"] = EMSESP::system_.getBBQKeesGatewayDetails();
|
||||
#if CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S2
|
||||
node["temperature"] = EMSESP::system_.temperature();
|
||||
@@ -2705,8 +2812,10 @@ bool System::command_info(const char * value, const int8_t id, JsonObject output
|
||||
if (!gpios_allowed_str.empty()) {
|
||||
gpios_allowed_str += ", ";
|
||||
}
|
||||
|
||||
gpios_allowed_str += Helpers::itoa(gpio);
|
||||
}
|
||||
|
||||
node["gpios_allowed"] = gpios_allowed_str;
|
||||
|
||||
std::string gpios_in_use_str;
|
||||
@@ -2716,8 +2825,10 @@ bool System::command_info(const char * value, const int8_t id, JsonObject output
|
||||
if (!gpios_in_use_str.empty()) {
|
||||
gpios_in_use_str += ", ";
|
||||
}
|
||||
|
||||
gpios_in_use_str += Helpers::itoa(gpio.pin);
|
||||
}
|
||||
|
||||
node["gpios_in_use"] = gpios_in_use_str;
|
||||
|
||||
std::string gpios_available_str;
|
||||
@@ -2725,8 +2836,10 @@ bool System::command_info(const char * value, const int8_t id, JsonObject output
|
||||
if (!gpios_available_str.empty()) {
|
||||
gpios_available_str += ", ";
|
||||
}
|
||||
|
||||
gpios_available_str += Helpers::itoa(gpio);
|
||||
}
|
||||
|
||||
node["gpios_available"] = gpios_available_str;
|
||||
|
||||
// Network Status
|
||||
@@ -2765,6 +2878,7 @@ bool System::command_info(const char * value, const int8_t id, JsonObject output
|
||||
if (WiFi.status() == WL_CONNECTED && !settings.bssid.isEmpty()) {
|
||||
node["BSSID"] = "set"; // we don't disclose the name
|
||||
}
|
||||
|
||||
node["TxPowerSetting"] = settings.tx_power;
|
||||
node["staticIP"] = settings.staticIPConfig;
|
||||
node["lowBandwidth"] = settings.bandwidth20;
|
||||
@@ -2819,6 +2933,7 @@ bool System::command_info(const char * value, const int8_t id, JsonObject output
|
||||
node["MQTTPublishFails"] = Mqtt::publish_fails();
|
||||
node["MQTTReconnects"] = Mqtt::connect_count();
|
||||
}
|
||||
|
||||
EMSESP::esp32React.getMqttSettingsService()->read([&](const MqttSettings & settings) {
|
||||
node["enabled"] = settings.enabled;
|
||||
node["clientID"] = settings.clientId;
|
||||
@@ -2911,6 +3026,7 @@ bool System::command_info(const char * value, const int8_t id, JsonObject output
|
||||
node["busStatus"] = "unknown";
|
||||
break;
|
||||
}
|
||||
|
||||
node["busProtocol"] = EMSbus::is_ht3() ? "HT3" : "Buderus";
|
||||
node["busTelegramsReceived"] = EMSESP::rxservice_.telegram_count();
|
||||
node["busReads"] = EMSESP::txservice_.telegram_read_count();
|
||||
@@ -2935,6 +3051,7 @@ bool System::command_info(const char * value, const int8_t id, JsonObject output
|
||||
node["showerAlertColdshot"] = settings.shower_alert_coldshot; // seconds
|
||||
node["showerAlertTrigger"] = settings.shower_alert_trigger; // minutes
|
||||
}
|
||||
|
||||
if (settings.board_profile == "CUSTOM") {
|
||||
node["phyType"] = settings.phy_type;
|
||||
if (settings.phy_type != PHY_type::PHY_TYPE_NONE) {
|
||||
@@ -2942,6 +3059,7 @@ bool System::command_info(const char * value, const int8_t id, JsonObject output
|
||||
node["ethPhyAddr"] = settings.eth_phy_addr;
|
||||
node["ethClockMmode"] = settings.eth_clock_mode;
|
||||
}
|
||||
|
||||
node["rxGPIO"] = settings.rx_gpio;
|
||||
node["txGPIO"] = settings.tx_gpio;
|
||||
node["dallasGPIO"] = settings.dallas_gpio;
|
||||
@@ -2949,6 +3067,7 @@ bool System::command_info(const char * value, const int8_t id, JsonObject output
|
||||
node["ledGPIO"] = settings.led_gpio;
|
||||
node["ledType"] = settings.led_type;
|
||||
}
|
||||
|
||||
node["hideLed"] = settings.hide_led;
|
||||
node["noTokenApi"] = settings.notoken_api;
|
||||
node["readonlyMode"] = settings.readonly_mode;
|
||||
@@ -2994,14 +3113,17 @@ bool System::command_info(const char * value, const int8_t id, JsonObject output
|
||||
if (result[0] != '\0') {
|
||||
obj["handlersReceived"] = result; // don't show handlers if there aren't any
|
||||
}
|
||||
|
||||
(void)emsdevice->show_telegram_handlers(result, sizeof(result), EMSdevice::Handlers::FETCHED);
|
||||
if (result[0] != '\0') {
|
||||
obj["handlersFetched"] = result;
|
||||
}
|
||||
|
||||
(void)emsdevice->show_telegram_handlers(result, sizeof(result), EMSdevice::Handlers::PENDING);
|
||||
if (result[0] != '\0') {
|
||||
obj["handlersPending"] = result;
|
||||
}
|
||||
|
||||
(void)emsdevice->show_telegram_handlers(result, sizeof(result), EMSdevice::Handlers::IGNORED);
|
||||
if (result[0] != '\0') {
|
||||
obj["handlersIgnored"] = result;
|
||||
@@ -3018,24 +3140,28 @@ bool System::command_info(const char * value, const int8_t id, JsonObject output
|
||||
obj["name"] = F_(temperaturesensor);
|
||||
obj["entities"] = EMSESP::temperaturesensor_.count_entities();
|
||||
}
|
||||
|
||||
if (EMSESP::analogsensor_.count_entities()) {
|
||||
JsonObject obj = devices.add<JsonObject>();
|
||||
obj["type"] = F_(analogsensor);
|
||||
obj["name"] = F_(analogsensor);
|
||||
obj["entities"] = EMSESP::analogsensor_.count_entities();
|
||||
}
|
||||
|
||||
if (EMSESP::webSchedulerService.count_entities()) {
|
||||
JsonObject obj = devices.add<JsonObject>();
|
||||
obj["type"] = F_(scheduler);
|
||||
obj["name"] = F_(scheduler);
|
||||
obj["entities"] = EMSESP::webSchedulerService.count_entities();
|
||||
}
|
||||
|
||||
if (EMSESP::webCommandService.count_entities()) {
|
||||
JsonObject obj = devices.add<JsonObject>();
|
||||
obj["type"] = F_(commands);
|
||||
obj["name"] = F_(commands);
|
||||
obj["entities"] = EMSESP::webCommandService.count_entities();
|
||||
}
|
||||
|
||||
if (EMSESP::webCustomEntityService.count_entities()) {
|
||||
JsonObject obj = devices.add<JsonObject>();
|
||||
obj["type"] = F_(custom);
|
||||
@@ -3200,6 +3326,7 @@ bool System::command_txpause(const char * value, const int8_t) {
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -3331,6 +3458,7 @@ String System::getBBQKeesGatewayDetails([[maybe_unused]] uint8_t detail) {
|
||||
uint32_t model : 4;
|
||||
uint32_t mfg : 4;
|
||||
};
|
||||
|
||||
uint32_t reg;
|
||||
} gw;
|
||||
|
||||
@@ -3422,6 +3550,7 @@ bool System::uploadFirmwareURL(const char * url) {
|
||||
ssl_client.setBufferSizes(HttpClient::TLS_RX_BUFFER_SIZE, HttpClient::TLS_TX_BUFFER_SIZE);
|
||||
ssl_client.setSessionTimeout(HttpClient::TLS_SESSION_TIMEOUT_S);
|
||||
}
|
||||
|
||||
basic_client.setTimeout(FIRMWARE_UPLOAD_READ_TIMEOUT_S * 1000); // socket-level read timeout, in ms
|
||||
ssl_client.setTimeout(FIRMWARE_UPLOAD_READ_TIMEOUT_S); // Stream::readBytes timeout used by Update
|
||||
ssl_client.setClient(&basic_client, is_https); // enableSSL = false for plain HTTP
|
||||
@@ -3479,10 +3608,12 @@ bool System::uploadFirmwareURL(const char * url) {
|
||||
if (line.isEmpty()) {
|
||||
break; // end of headers
|
||||
}
|
||||
|
||||
int colon = line.indexOf(':');
|
||||
if (colon < 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String name = line.substring(0, colon);
|
||||
name.toLowerCase();
|
||||
String val = line.substring(colon + 1);
|
||||
@@ -3501,6 +3632,7 @@ bool System::uploadFirmwareURL(const char * url) {
|
||||
LOG_ERROR("Firmware upload failed - too many redirects");
|
||||
return false;
|
||||
}
|
||||
|
||||
String lower_loc = location;
|
||||
lower_loc.toLowerCase();
|
||||
if (lower_loc.startsWith("https://") || lower_loc.startsWith("http://")) {
|
||||
@@ -3511,6 +3643,7 @@ bool System::uploadFirmwareURL(const char * url) {
|
||||
LOG_ERROR("Firmware upload failed - cross-scheme redirect to %s", location.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
url_remain = location.substring(new_is_https ? 8 : 7);
|
||||
} else if (location.startsWith("/")) {
|
||||
url_remain = host + location; // relative redirect, same host
|
||||
@@ -3518,6 +3651,7 @@ bool System::uploadFirmwareURL(const char * url) {
|
||||
LOG_ERROR("Firmware upload failed - unsupported redirect to %s", location.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG_DEBUG("Following redirect to %s", url_remain.c_str());
|
||||
continue;
|
||||
}
|
||||
@@ -3540,6 +3674,7 @@ bool System::uploadFirmwareURL(const char * url) {
|
||||
while (ssl_client.connected() && !ssl_client.available() && millis() - body_wait < FIRMWARE_UPLOAD_RESPONSE_TIMEOUT) {
|
||||
delay(1);
|
||||
}
|
||||
|
||||
if (!ssl_client.available()) {
|
||||
ssl_client.stop();
|
||||
LOG_ERROR("Firmware upload failed - no body received");
|
||||
@@ -3577,6 +3712,7 @@ bool System::uploadFirmwareURL(const char * url) {
|
||||
LOG_ERROR("The recovery firmware can't be installed on this board");
|
||||
return false; // error
|
||||
}
|
||||
|
||||
if (!ota.beginFirmware(firmware_size, factory, false)) {
|
||||
LOG_ERROR("Recovery firmware upload failed - %s", ota.errorString());
|
||||
return false; // error
|
||||
@@ -3611,6 +3747,7 @@ bool System::uploadFirmwareURL(const char * url) {
|
||||
} else {
|
||||
Update.abort(); // release the OTA partition handle so a later attempt can start cleanly
|
||||
}
|
||||
|
||||
ssl_client.stop(); // drop the connection
|
||||
saved_url.clear(); // prevent it from downloading again
|
||||
EMSESP::system_.systemStatus(SYSTEM_STATUS::SYSTEM_STATUS_NORMAL);
|
||||
@@ -3624,13 +3761,16 @@ bool System::uploadFirmwareURL(const char * url) {
|
||||
if (!ssl_client.connected()) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (millis() - wait_start > FIRMWARE_UPLOAD_STALL_TIMEOUT) {
|
||||
break;
|
||||
}
|
||||
|
||||
// also bail out promptly if a cancel arrives mid-stall
|
||||
if (EMSESP::system_.systemStatus() < SYSTEM_STATUS::SYSTEM_STATUS_UPLOADING) {
|
||||
break;
|
||||
}
|
||||
|
||||
delay(1);
|
||||
}
|
||||
|
||||
@@ -3639,10 +3779,12 @@ bool System::uploadFirmwareURL(const char * url) {
|
||||
if (EMSESP::system_.systemStatus() < SYSTEM_STATUS::SYSTEM_STATUS_UPLOADING) {
|
||||
continue;
|
||||
}
|
||||
|
||||
LOG_ERROR("Firmware upload failed - read stalled at %u of %d bytes", (unsigned)total_read, firmware_size);
|
||||
if (is_recovery) {
|
||||
ota.abort();
|
||||
}
|
||||
|
||||
EMSESP::system_.systemStatus(SYSTEM_STATUS::SYSTEM_STATUS_ERROR_UPLOAD);
|
||||
return false;
|
||||
}
|
||||
@@ -3658,6 +3800,7 @@ bool System::uploadFirmwareURL(const char * url) {
|
||||
if (is_recovery) {
|
||||
ota.abort();
|
||||
}
|
||||
|
||||
EMSESP::system_.systemStatus(SYSTEM_STATUS::SYSTEM_STATUS_ERROR_UPLOAD);
|
||||
return false;
|
||||
}
|
||||
@@ -3670,18 +3813,22 @@ bool System::uploadFirmwareURL(const char * url) {
|
||||
if (is_recovery) {
|
||||
ota.abort();
|
||||
}
|
||||
|
||||
EMSESP::system_.systemStatus(SYSTEM_STATUS::SYSTEM_STATUS_ERROR_UPLOAD);
|
||||
return false;
|
||||
}
|
||||
|
||||
// the recovery firmware must be for this chip, and really be the recovery
|
||||
if (is_recovery && !(OtaUpdater::isCompatibleFirmware(buf, n) && OtaUpdater::isRecoveryFirmware(buf, n))) {
|
||||
LOG_ERROR("Recovery firmware upload failed - not a recovery firmware image for this chip");
|
||||
if (is_recovery) {
|
||||
ota.abort();
|
||||
}
|
||||
|
||||
EMSESP::system_.systemStatus(SYSTEM_STATUS::SYSTEM_STATUS_ERROR_UPLOAD);
|
||||
return false;
|
||||
}
|
||||
|
||||
magic_ok = true;
|
||||
}
|
||||
|
||||
@@ -3694,6 +3841,7 @@ bool System::uploadFirmwareURL(const char * url) {
|
||||
if (is_recovery) {
|
||||
ota.abort();
|
||||
}
|
||||
|
||||
EMSESP::system_.systemStatus(SYSTEM_STATUS::SYSTEM_STATUS_ERROR_UPLOAD);
|
||||
return false;
|
||||
}
|
||||
@@ -3775,6 +3923,7 @@ bool System::readCommand(const char * data) {
|
||||
} else if (num_args == 2) {
|
||||
length = Helpers::atoint(value); // decimal
|
||||
}
|
||||
|
||||
num_args++;
|
||||
}
|
||||
}
|
||||
@@ -4063,6 +4212,7 @@ std::vector<uint8_t> System::available_gpios() {
|
||||
gpios.push_back(gpio); // didn't find it in used_gpios_, so it's available
|
||||
}
|
||||
}
|
||||
|
||||
return gpios;
|
||||
}
|
||||
|
||||
@@ -4071,6 +4221,7 @@ void System::make_snapshot_gpios(std::vector<int8_t> & u_gpios, std::vector<int8
|
||||
for (const auto & usage : used_gpios_) {
|
||||
u_gpios.push_back(usage.pin);
|
||||
}
|
||||
|
||||
for (const auto & gpio : valid_system_gpios_) {
|
||||
s_gpios.push_back(gpio);
|
||||
}
|
||||
@@ -4099,6 +4250,7 @@ void System::listDir([[maybe_unused]] const char * dirname, [[maybe_unused]] uin
|
||||
LOG_DEBUG("Failed to open directory %s", dirname);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!root.isDirectory()) {
|
||||
LOG_DEBUG("%s is not a directory", dirname);
|
||||
return;
|
||||
@@ -4118,6 +4270,7 @@ void System::listDir([[maybe_unused]] const char * dirname, [[maybe_unused]] uin
|
||||
std::string line = " (file) " + std::string(file.name()) + " (" + std::to_string(file.size()) + " bytes)";
|
||||
LOG_DEBUG("%s", line.c_str());
|
||||
}
|
||||
|
||||
file = root.openNextFile();
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -308,6 +308,7 @@ class System {
|
||||
void PSram(uint32_t psram) {
|
||||
psram_ = psram / 1024;
|
||||
}
|
||||
|
||||
uint32_t PSram() {
|
||||
return psram_;
|
||||
}
|
||||
@@ -315,6 +316,7 @@ class System {
|
||||
uint32_t appFree() {
|
||||
return appfree_;
|
||||
}
|
||||
|
||||
uint32_t appUsed() {
|
||||
return appused_;
|
||||
}
|
||||
@@ -323,9 +325,11 @@ class System {
|
||||
static uint32_t getMaxAllocMem() {
|
||||
return max_alloc_mem_;
|
||||
}
|
||||
|
||||
static uint32_t getHeapMem() {
|
||||
return heap_mem_;
|
||||
}
|
||||
|
||||
// All-time low watermark of free internal heap (KB).
|
||||
// Unlike getHeapMem() (sampled now), this captures the *lowest* free heap
|
||||
// has ever been since boot — i.e. the worst transient dip during MQTT
|
||||
@@ -335,6 +339,7 @@ class System {
|
||||
static uint32_t getMinFreeMem() {
|
||||
return min_free_mem_;
|
||||
}
|
||||
|
||||
static void refreshHeapMem() {
|
||||
#ifndef EMSESP_STANDALONE
|
||||
max_alloc_mem_ = ESP.getMaxAllocHeap() / 1024;
|
||||
@@ -346,6 +351,7 @@ class System {
|
||||
static bool test_set_all_active() {
|
||||
return test_set_all_active_;
|
||||
}
|
||||
|
||||
static void test_set_all_active(bool n) {
|
||||
#if defined(EMSESP_TEST)
|
||||
if (n) {
|
||||
@@ -453,6 +459,7 @@ class System {
|
||||
uint8_t pin;
|
||||
std::string source;
|
||||
};
|
||||
|
||||
static std::vector<uint8_t, AllocatorPSRAM<uint8_t>> valid_system_gpios_; // list of valid GPIOs for the ESP32 board that can be used
|
||||
static std::vector<GpioUsage, AllocatorPSRAM<GpioUsage>> used_gpios_; // list of GPIOs used by the application
|
||||
|
||||
|
||||
+29
-1
@@ -56,6 +56,7 @@ uint8_t EMSbus::calculate_crc(const uint8_t * data, const uint8_t length) {
|
||||
crc = ems_crc_table[crc];
|
||||
crc ^= data[i++];
|
||||
}
|
||||
|
||||
return crc;
|
||||
}
|
||||
|
||||
@@ -110,6 +111,7 @@ std::string Telegram::to_string() const {
|
||||
data[2] = this->type_id;
|
||||
length = 4;
|
||||
}
|
||||
|
||||
for (uint8_t i = 0; i < this->message_length; i++) {
|
||||
data[length++] = this->message_data[i];
|
||||
}
|
||||
@@ -159,12 +161,14 @@ void RxService::add(uint8_t * data, uint8_t length) {
|
||||
if (!bus_connected()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (data[0] != EMSuart::last_tx_src()) { // do not count echos as errors
|
||||
telegram_error_count_++;
|
||||
LOG_WARNING("Incomplete Rx: %s (crc: %02X)", Helpers::data_to_hex(data, length).c_str(), crc); // include CRC
|
||||
} else {
|
||||
LOG_TRACE("Incomplete Rx: %s (crc: %02X)", Helpers::data_to_hex(data, length).c_str(), crc); // include CRC
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -207,11 +211,13 @@ void RxService::add(uint8_t * data, uint8_t length) {
|
||||
message_data = data + 6;
|
||||
message_length = length - 7;
|
||||
}
|
||||
|
||||
// EMS+ boilers broadcast UBAMonitorFastPlus (0xE4); EMS/HT3 use 0x18.
|
||||
// Extended telegrams alone are not enough: Junkers HT3 also uses them (e.g. 0x16F).
|
||||
if (type_id == 0xE4) {
|
||||
set_ems2();
|
||||
}
|
||||
|
||||
// if we're watching and "raw" print out actual telegram as bytes to the console
|
||||
// including the CRC at the end
|
||||
if (EMSESP::watch() == EMSESP::Watch::WATCH_RAW) {
|
||||
@@ -293,8 +299,10 @@ uint8_t TxService::get_send_id() {
|
||||
count = 0;
|
||||
return tx_telegrams_.empty() ? ems_bus_id() : tx_telegrams_.front().telegram_->src;
|
||||
}
|
||||
|
||||
return tx_telegrams_.front().telegram_->src;
|
||||
}
|
||||
|
||||
count = 0;
|
||||
return ems_bus_id();
|
||||
}
|
||||
@@ -312,6 +320,7 @@ void TxService::send() {
|
||||
send_poll();
|
||||
return;
|
||||
}
|
||||
|
||||
delayed_send_ = 0;
|
||||
|
||||
// if we're in read-only mode (tx_mode 0) forget the Tx call
|
||||
@@ -334,6 +343,7 @@ void TxService::send_telegram(const QueuedTxTelegram & tx_telegram) {
|
||||
if (ems_mask() != EMS_MASK_UNSET) {
|
||||
src ^= ems_mask();
|
||||
}
|
||||
|
||||
telegram_raw[0] = src;
|
||||
|
||||
// dest - for READ the MSB must be set
|
||||
@@ -343,6 +353,7 @@ void TxService::send_telegram(const QueuedTxTelegram & tx_telegram) {
|
||||
if (telegram->operation == Telegram::Operation::TX_READ) {
|
||||
dest |= 0x80; // read has 8th bit set for the destination
|
||||
}
|
||||
|
||||
telegram_raw[1] = dest;
|
||||
|
||||
uint8_t message_p = 0; // this is the position in the telegram where we want to put our message data
|
||||
@@ -390,6 +401,7 @@ void TxService::send_telegram(const QueuedTxTelegram & tx_telegram) {
|
||||
telegram_raw[message_p++] = telegram->message_data[i];
|
||||
}
|
||||
}
|
||||
|
||||
// make a copy of the telegram with new dest (without read-flag)
|
||||
telegram_last_ = std::make_shared<Telegram>(
|
||||
telegram->operation, telegram->src, dest & 0x7F, telegram->type_id, telegram->offset, telegram->message_data, telegram->message_length);
|
||||
@@ -424,6 +436,7 @@ void TxService::send_telegram(const QueuedTxTelegram & tx_telegram) {
|
||||
} else {
|
||||
increment_telegram_write_fail_count(); // another Tx fail
|
||||
}
|
||||
|
||||
tx_state(Telegram::Operation::NONE); // nothing send, tx not in wait state
|
||||
return;
|
||||
}
|
||||
@@ -437,8 +450,10 @@ void TxService::send_telegram(const QueuedTxTelegram & tx_telegram) {
|
||||
EMSESP::set_read_id(telegram->type_id);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
tx_state(telegram->operation); // tx now in a wait state
|
||||
}
|
||||
|
||||
@@ -463,6 +478,7 @@ void TxService::add(const uint8_t operation,
|
||||
} else {
|
||||
telegram_read_fail_count_++;
|
||||
}
|
||||
|
||||
tx_telegrams_.pop_front();
|
||||
}
|
||||
#endif
|
||||
@@ -472,6 +488,7 @@ void TxService::add(const uint8_t operation,
|
||||
} else {
|
||||
tx_telegrams_.emplace_back(tx_telegram_id_++, std::move(telegram), false, validateid); // add to back of queue
|
||||
}
|
||||
|
||||
if (validateid != 0) {
|
||||
EMSESP::wait_validate(validateid);
|
||||
}
|
||||
@@ -543,6 +560,7 @@ void TxService::add(uint8_t operation, const uint8_t * data, const uint8_t lengt
|
||||
} else {
|
||||
telegram_read_fail_count_++;
|
||||
}
|
||||
|
||||
tx_telegrams_.pop_front();
|
||||
}
|
||||
|
||||
@@ -553,6 +571,7 @@ void TxService::add(uint8_t operation, const uint8_t * data, const uint8_t lengt
|
||||
} else {
|
||||
tx_telegrams_.emplace_back(tx_telegram_id_++, std::move(telegram), false, validate_id); // add to back of queue
|
||||
}
|
||||
|
||||
if (validate_id != 0) {
|
||||
EMSESP::wait_validate(validate_id);
|
||||
}
|
||||
@@ -576,6 +595,7 @@ void TxService::read_request(const uint16_t type_id, const uint8_t dest, const u
|
||||
if (length > 0 && length < message_data) {
|
||||
message_data = length;
|
||||
}
|
||||
|
||||
add(Telegram::Operation::TX_READ, dest, type_id, offset, &message_data, 1, 0, front);
|
||||
}
|
||||
|
||||
@@ -597,6 +617,7 @@ bool TxService::send_raw(const char * telegram_data) {
|
||||
data[count++] = (uint8_t)strtol(p, 0, 16);
|
||||
p = strtok(nullptr, " ,");
|
||||
}
|
||||
|
||||
free(telegram);
|
||||
// check valid length
|
||||
if (count < 4) {
|
||||
@@ -621,8 +642,10 @@ void TxService::retry_tx(const uint8_t operation, const uint8_t * data, const ui
|
||||
EMSESP::set_response_id(0);
|
||||
EMSESP::set_read_id(0);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
increment_telegram_read_fail_count(); // another Tx fail
|
||||
} else {
|
||||
increment_telegram_write_fail_count(); // another Tx fail
|
||||
@@ -636,6 +659,7 @@ void TxService::retry_tx(const uint8_t operation, const uint8_t * data, const ui
|
||||
if (operation == Telegram::Operation::TX_READ) {
|
||||
EMSESP::rxservice_.add_empty(telegram_last_->dest, telegram_last_->src, telegram_last_->type_id, telegram_last_->offset);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -652,6 +676,7 @@ void TxService::retry_tx(const uint8_t operation, const uint8_t * data, const ui
|
||||
EMSESP::wait_validate(0); // do not wait for validation
|
||||
return;
|
||||
}
|
||||
|
||||
// for the last try wait 2 sec before sending.
|
||||
delayed_send_ = (retry_count_ < MAXIMUM_TX_RETRIES) ? 0 : (uuid::get_uptime() + POST_SEND_DELAY);
|
||||
tx_telegrams_.emplace_front(tx_telegram_id_++, std::move(telegram_last_), true, get_post_send_query());
|
||||
@@ -672,14 +697,17 @@ uint16_t TxService::read_next_tx(const uint8_t offset, const uint8_t length) {
|
||||
if (offset + old_length >= telegram_last_->offset + telegram_last_->message_data[0]) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// we request all and get a short telegram with requested offset
|
||||
if ((next_length + next_offset) == 0xFF && old_length < max_length - 4 && offset <= telegram_last_->offset) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (offset >= telegram_last_->offset && old_length > 0 && next_length > 0) {
|
||||
add(Telegram::Operation::TX_READ, telegram_last_->dest, telegram_last_->type_id, next_offset, &next_length, 1, 0, true);
|
||||
return telegram_last_->type_id;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -713,4 +741,4 @@ uint16_t TxService::post_send_query() {
|
||||
return post_typeid;
|
||||
}
|
||||
|
||||
} // namespace emsesp
|
||||
} // namespace emsesp
|
||||
|
||||
+9
-1
@@ -210,6 +210,7 @@ class EMSbus {
|
||||
if ((uuid::get_uptime() - last_bus_activity_) > EMS_BUS_TIMEOUT) {
|
||||
bus_connected_ = false;
|
||||
}
|
||||
|
||||
return bus_connected_;
|
||||
#endif
|
||||
}
|
||||
@@ -220,6 +221,7 @@ class EMSbus {
|
||||
if (!last_bus_activity_) {
|
||||
bus_uptime_start_ = timestamp;
|
||||
}
|
||||
|
||||
last_bus_activity_ = timestamp;
|
||||
bus_connected_ = true;
|
||||
}
|
||||
@@ -229,12 +231,14 @@ class EMSbus {
|
||||
if (!bus_uptime_start_) {
|
||||
return 0; // not yet initialized
|
||||
}
|
||||
|
||||
return (uint32_t)((uuid::get_uptime() - bus_uptime_start_) / 1000ULL);
|
||||
}
|
||||
|
||||
static uint8_t tx_state() {
|
||||
return tx_state_;
|
||||
}
|
||||
|
||||
static void tx_state(uint8_t tx_state) {
|
||||
tx_state_ = tx_state;
|
||||
}
|
||||
@@ -280,6 +284,7 @@ class RxService : public EMSbus {
|
||||
if (telegram_error_count_ == 0) {
|
||||
return 100; // all good, 100%
|
||||
}
|
||||
|
||||
uint8_t q = (telegram_error_count_ * 100 / (telegram_count_ + telegram_error_count_));
|
||||
return (q <= EMS_BUS_QUALITY_RX_THRESHOLD ? 100 : 100 - q);
|
||||
}
|
||||
@@ -359,6 +364,7 @@ class TxService : public EMSbus {
|
||||
uint32_t telegram_read_count() const {
|
||||
return telegram_read_count_;
|
||||
}
|
||||
|
||||
uint32_t telegram_write_count() const {
|
||||
return telegram_write_count_;
|
||||
}
|
||||
@@ -396,6 +402,7 @@ class TxService : public EMSbus {
|
||||
if (telegram_read_fail_count_ == 0) {
|
||||
return 100; // all good, 100%
|
||||
}
|
||||
|
||||
return (100 - (uint8_t)(telegram_read_fail_count_ * 100 / (telegram_read_fail_count_ + telegram_read_count_)));
|
||||
}
|
||||
|
||||
@@ -403,6 +410,7 @@ class TxService : public EMSbus {
|
||||
if (telegram_write_fail_count_ == 0) {
|
||||
return 100; // all good, 100%
|
||||
}
|
||||
|
||||
return (100 - (uint8_t)(telegram_write_fail_count_ * 100 / (telegram_write_fail_count_ + telegram_write_count_)));
|
||||
}
|
||||
|
||||
@@ -464,4 +472,4 @@ class TxService : public EMSbus {
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -97,6 +97,7 @@ void TemperatureSensor::loop() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
last_activity_ = time_now;
|
||||
}
|
||||
} else if (state_ == State::READING) {
|
||||
@@ -127,6 +128,7 @@ void TemperatureSensor::loop() {
|
||||
if (!parasite_) {
|
||||
bus_.depower();
|
||||
}
|
||||
|
||||
if (bus_.crc8(addr, ADDR_LEN - 1) == addr[ADDR_LEN - 1]) {
|
||||
switch (addr[0]) {
|
||||
case TYPE_DS18B20:
|
||||
@@ -147,11 +149,13 @@ void TemperatureSensor::loop() {
|
||||
publish_sensor(sensor);
|
||||
changed_ |= true;
|
||||
}
|
||||
|
||||
sensor.read = true;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// add new sensor. this will create the id string, empty name and offset
|
||||
if (!found && (sensors_.size() < (MAX_SENSORS - 1))) {
|
||||
// LOG_NOTICE("Adding new sensor for %s", Sensor(addr).id().c_str());
|
||||
@@ -169,6 +173,7 @@ void TemperatureSensor::loop() {
|
||||
} else {
|
||||
sensorfails_++;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -184,6 +189,7 @@ void TemperatureSensor::loop() {
|
||||
if (!parasite_) {
|
||||
bus_.depower();
|
||||
}
|
||||
|
||||
// check for missing sensors after some samples
|
||||
if (++scancnt_ > SCAN_MAX) {
|
||||
for (auto & sensor : sensors_) {
|
||||
@@ -191,8 +197,10 @@ void TemperatureSensor::loop() {
|
||||
sensor.temperature_c = EMS_VALUE_INT16_NOTSET;
|
||||
changed_ = true;
|
||||
}
|
||||
|
||||
sensor.read = false;
|
||||
}
|
||||
|
||||
scancnt_ = 0;
|
||||
} else if (scancnt_ == SCAN_START + 1) { // startup
|
||||
firstscan_ = sensors_.size();
|
||||
@@ -208,11 +216,13 @@ void TemperatureSensor::loop() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
s->set_name("gateway_temperature");
|
||||
s->set_is_system(true); // mark as internal system temperature sensor
|
||||
if (!EMSESP::nvs_.isKey("intTemp")) {
|
||||
EMSESP::nvs_.putString("intTemp", s->id());
|
||||
}
|
||||
|
||||
LOG_DEBUG("Adding system sensor for gateway_temperature");
|
||||
EMSESP::webCustomizationService.update([&](WebCustomization & settings) {
|
||||
auto newSensor = SensorCustomization();
|
||||
@@ -224,11 +234,13 @@ void TemperatureSensor::loop() {
|
||||
return StateUpdateResult::CHANGED;
|
||||
});
|
||||
}
|
||||
|
||||
// LOG_NOTICE("Adding %d sensor(s) from first scan", firstscan_);
|
||||
} else if ((scancnt_ <= 0) && (firstscan_ != sensors_.size())) { // check 2 times for no change of sensor #
|
||||
scancnt_ = SCAN_START;
|
||||
sensors_.clear(); // restart scanning and clear to get correct numbering
|
||||
}
|
||||
|
||||
state_ = State::IDLE;
|
||||
}
|
||||
}
|
||||
@@ -241,6 +253,7 @@ bool TemperatureSensor::temperature_convert_complete(const uint32_t time) {
|
||||
if (parasite_) {
|
||||
return time > CONVERSION_MS; // don't care, use the datasheet time
|
||||
}
|
||||
|
||||
return bus_.read_bit() == 1;
|
||||
#else
|
||||
return time > CONVERSION_MS;
|
||||
@@ -300,6 +313,7 @@ int16_t TemperatureSensor::get_temperature_c(const uint8_t addr[]) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
raw_value = ((int32_t)raw_value * 625 + 500) / 1000; // round to 0.1
|
||||
return raw_value;
|
||||
#else
|
||||
@@ -338,6 +352,7 @@ bool TemperatureSensor::update(const char * id, const char * name, int16_t offse
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
auto newSensor = SensorCustomization();
|
||||
strlcpy(newSensor.id, id, sizeof(newSensor.id));
|
||||
@@ -347,6 +362,7 @@ bool TemperatureSensor::update(const char * id, const char * name, int16_t offse
|
||||
settings.sensorCustomizations.push_back(newSensor);
|
||||
LOG_DEBUG("Adding new customization for sensor ID %s", id);
|
||||
}
|
||||
|
||||
sensor.ha_registered = false; // it's changed so we may need to recreate the HA config
|
||||
return StateUpdateResult::CHANGED;
|
||||
});
|
||||
@@ -363,6 +379,7 @@ bool TemperatureSensor::updated_values() {
|
||||
changed_ = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -380,6 +397,7 @@ bool TemperatureSensor::get_value_info(JsonObject output, const char * cmd, cons
|
||||
output[sensor.name()] = serialized(Helpers::render_value(val, sensor.temperature_c, 10, EMSESP::system_.fahrenheit() ? 2 : 0));
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -387,6 +405,7 @@ bool TemperatureSensor::get_value_info(JsonObject output, const char * cmd, cons
|
||||
for (const auto & sensor : sensors_) {
|
||||
get_value_json(output[sensor.name()].to<JsonObject>(), sensor);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -396,6 +415,7 @@ bool TemperatureSensor::get_value_info(JsonObject output, const char * cmd, cons
|
||||
output["api_data"] = metrics;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -425,6 +445,7 @@ std::string TemperatureSensor::get_metrics_prometheus() {
|
||||
result +=
|
||||
(std::string) "emsesp_" + sensor.name() + " " + Helpers::render_value(val, sensor.temperature_c, 10, EMSESP::system_.fahrenheit() ? 2 : 0) + "\n";
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -455,9 +476,11 @@ void TemperatureSensor::publish_sensor(const Sensor & sensor) {
|
||||
} else {
|
||||
snprintf(topic, sizeof(topic), "%s%s/%s", F_(temperaturesensor), "_data", sensor.name());
|
||||
}
|
||||
|
||||
char payload[10];
|
||||
Mqtt::queue_publish(topic, Helpers::render_value(payload, sensor.temperature_c, 10, EMSESP::system_.fahrenheit() ? 2 : 0));
|
||||
}
|
||||
|
||||
char cmd[COMMAND_MAX_LENGTH];
|
||||
snprintf(cmd, sizeof(cmd), "%s/%s", F_(temperaturesensor), sensor.name());
|
||||
EMSESP::webSchedulerService.onChange(cmd);
|
||||
@@ -491,6 +514,7 @@ void TemperatureSensor::publish_values(const bool force) {
|
||||
for (const auto & sensor : sensors_) {
|
||||
publish_sensor(sensor);
|
||||
}
|
||||
|
||||
return;
|
||||
} else if (!EMSESP::mqtt_.get_publish_onchange(EMSdevice::DeviceType::SYSTEM)) {
|
||||
return; // wait for first time period
|
||||
@@ -609,6 +633,7 @@ const char * TemperatureSensor::Sensor::name() const {
|
||||
if (name_[0] == '\0') {
|
||||
return id_;
|
||||
}
|
||||
|
||||
return name_;
|
||||
}
|
||||
|
||||
@@ -628,6 +653,7 @@ bool TemperatureSensor::Sensor::apply_customization() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
@@ -669,4 +695,4 @@ void TemperatureSensor::load_test_data() {
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace emsesp
|
||||
} // namespace emsesp
|
||||
|
||||
@@ -48,6 +48,7 @@ class TemperatureSensor {
|
||||
bool is_system() const {
|
||||
return is_system_;
|
||||
}
|
||||
|
||||
void set_is_system(const bool is_system) {
|
||||
is_system_ = is_system;
|
||||
}
|
||||
@@ -59,6 +60,7 @@ class TemperatureSensor {
|
||||
int16_t offset() const {
|
||||
return offset_;
|
||||
}
|
||||
|
||||
void set_offset(const int16_t offset) {
|
||||
offset_ = offset;
|
||||
}
|
||||
@@ -124,6 +126,7 @@ class TemperatureSensor {
|
||||
if (exclude_disabled_system) {
|
||||
return std::count_if(sensors_.begin(), sensors_.end(), [](const Sensor & sensor) { return !sensor.is_system(); });
|
||||
}
|
||||
|
||||
return sensors_.size();
|
||||
}
|
||||
|
||||
@@ -192,4 +195,4 @@ class TemperatureSensor {
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -32,9 +32,10 @@ Alert::Alert(uint8_t device_type, uint8_t device_id, uint8_t product_id, const c
|
||||
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &setFlowTemp_, DeviceValueType::UINT8, FL_(setFlowTemp), DeviceValueUOM::DEGREES);
|
||||
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &setBurnPow_, DeviceValueType::UINT8, FL_(setBurnPow), DeviceValueUOM::PERCENT);
|
||||
}
|
||||
|
||||
// UBASetPoint 0x1A
|
||||
void Alert::process_UBASetPoints(const std::shared_ptr<const Telegram> & telegram) {
|
||||
has_update(telegram, setFlowTemp_, 0); // boiler set temp from thermostat
|
||||
has_update(telegram, setBurnPow_, 1); // max burner power in %
|
||||
}
|
||||
} // namespace emsesp
|
||||
} // namespace emsesp
|
||||
|
||||
+114
-2
@@ -282,6 +282,7 @@ Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const
|
||||
0,
|
||||
10);
|
||||
}
|
||||
|
||||
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA,
|
||||
&heatingActivated_,
|
||||
DeviceValueType::BOOL,
|
||||
@@ -753,6 +754,7 @@ Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const
|
||||
DeviceValueUOM::NONE,
|
||||
MAKE_CF_CB(set_maxHeatDhw));
|
||||
}
|
||||
|
||||
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA,
|
||||
&auxHeaterSource_,
|
||||
DeviceValueType::ENUM,
|
||||
@@ -1102,6 +1104,7 @@ Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const
|
||||
120,
|
||||
240);
|
||||
}
|
||||
|
||||
register_device_value(DeviceValueTAG::TAG_DHW1,
|
||||
&wwCircMode_,
|
||||
DeviceValueType::ENUM,
|
||||
@@ -1192,12 +1195,15 @@ Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const
|
||||
if (nrgHeatF_ < 0 || nrgHeatF_ >= EMS_VALUE_UINT32_NOTSET) {
|
||||
nrgHeatF_ = 0;
|
||||
}
|
||||
|
||||
if (nrgWwF_ < 0 || nrgWwF_ >= EMS_VALUE_UINT32_NOTSET) {
|
||||
nrgWwF_ = 0;
|
||||
}
|
||||
|
||||
if (nomPower_ == EMS_VALUE_UINT8_NOTSET) {
|
||||
nomPower_ = 0;
|
||||
}
|
||||
|
||||
store_energy();
|
||||
// update/publish the values
|
||||
has_update(nrgHeat_, (uint32_t)(nrgHeatF_ + 0.5));
|
||||
@@ -1211,12 +1217,15 @@ void Boiler::store_energy() {
|
||||
if (nrgHeatF_ != EMSESP::nvs_.getDouble("nrgheat")) {
|
||||
EMSESP::nvs_.putDouble("nrgheat", nrgHeatF_);
|
||||
}
|
||||
|
||||
if (nrgWwF_ != EMSESP::nvs_.getDouble("nrgww")) {
|
||||
EMSESP::nvs_.putDouble("nrgww", nrgWwF_);
|
||||
}
|
||||
|
||||
if (nomPower_ != EMSESP::nvs_.getUChar("nompower")) {
|
||||
EMSESP::nvs_.putUChar("nompower", nomPower_);
|
||||
}
|
||||
|
||||
// LOG_DEBUG("energy values stored");
|
||||
}
|
||||
|
||||
@@ -1250,6 +1259,7 @@ void Boiler::check_active() {
|
||||
if (Helpers::hasValue(wwCurFlow_) && (wwCurFlow_ > 0) && (wwType_ == 1)) {
|
||||
flowsensor = true;
|
||||
}
|
||||
|
||||
if (flowsensor) {
|
||||
b = ((wwCurFlow_ > 0) && ((boilerState_ & 0x0A) == 0x0A));
|
||||
} else {
|
||||
@@ -1271,6 +1281,7 @@ void Boiler::check_active() {
|
||||
EMSESP::webSettingsService.read([&](WebSettings & settings) { forceHeatingOff_ = settings.boiler_heatingoff ? EMS_VALUE_BOOL_ON : 0; });
|
||||
has_update(&forceHeatingOff_);
|
||||
}
|
||||
|
||||
static uint32_t lastSendHeatingOff = 0;
|
||||
if (forceHeatingOff_ == EMS_VALUE_BOOL_ON && (uuid::get_uptime_sec() - lastSendHeatingOff) >= 60) {
|
||||
lastSendHeatingOff = uuid::get_uptime_sec();
|
||||
@@ -1278,6 +1289,7 @@ void Boiler::check_active() {
|
||||
uint8_t data[] = {1, 0, 0, 1, 1};
|
||||
write_command(EMS_TYPE_UBASetPoints2, 0, data, sizeof(data), 0);
|
||||
}
|
||||
|
||||
uint8_t data[] = {0, 0, 0, 0};
|
||||
write_command(EMS_TYPE_UBASetPoints, 0, data, sizeof(data), 0);
|
||||
}
|
||||
@@ -1304,6 +1316,7 @@ void Boiler::check_active() {
|
||||
} else if (curBurnPow_ == 0 && (heatBurnPow + wwBurnPow) > 0) { // on burner switch off
|
||||
store_energy();
|
||||
}
|
||||
|
||||
// store new modulation and time
|
||||
heatBurnPow = heatingActive_ && !tapwaterActive_ ? curBurnPow_ : 0;
|
||||
wwBurnPow = tapwaterActive_ ? curBurnPow_ : 0;
|
||||
@@ -1319,6 +1332,7 @@ void Boiler::process_UBAFactory(const std::shared_ptr<const Telegram> & telegram
|
||||
if (telegram->offset > 4 || telegram->offset + telegram->message_length < 7) {
|
||||
return;
|
||||
}
|
||||
|
||||
toggle_fetch(telegram->type_id, false); // only read once
|
||||
uint8_t min = 0;
|
||||
uint8_t max = 0;
|
||||
@@ -1330,6 +1344,7 @@ void Boiler::process_UBAFactory(const std::shared_ptr<const Telegram> & telegram
|
||||
if (nomPower > 0 && nomPower_ == 0) {
|
||||
has_update(nomPower_, nomPower);
|
||||
}
|
||||
|
||||
if (min <= max) {
|
||||
// set_minmax(&burnMinPower_, 0, max); // removed #2918
|
||||
set_minmax(&burnMaxPower_, min, max);
|
||||
@@ -1389,6 +1404,7 @@ void Boiler::process_UBAMonitorFast(const std::shared_ptr<const Telegram> & tele
|
||||
curBurnPow = 100;
|
||||
selBurnPow = 100;
|
||||
}
|
||||
|
||||
has_update(selBurnPow_, selBurnPow);
|
||||
has_update(curBurnPow_, curBurnPow);
|
||||
|
||||
@@ -1464,6 +1480,7 @@ void Boiler::process_UBAParameterWW(const std::shared_ptr<const Telegram> & tele
|
||||
} else {
|
||||
wwComfort = EMS_VALUE_UINT8_NOTSET;
|
||||
}
|
||||
|
||||
has_update(wwComfort_, wwComfort);
|
||||
}
|
||||
}
|
||||
@@ -1520,16 +1537,18 @@ void Boiler::process_UBAMonitorFastPlus(const std::shared_ptr<const Telegram> &
|
||||
if (syspress == 0) {
|
||||
syspress = EMS_VALUE_UINT8_NOTSET;
|
||||
}
|
||||
has_update(sysPress_, syspress);
|
||||
|
||||
has_update(sysPress_, syspress);
|
||||
//has_update(telegram, temperatur_, 13); // unknown temperature
|
||||
has_update(telegram, heatblock_, 23); // see #1317
|
||||
has_update(telegram, headertemp_, 25); // see #1317
|
||||
//has_update(telegram, temperatur_, 27); // unknown temperature
|
||||
|
||||
telegram->read_value(exhaustTemp1_, 31);
|
||||
if (Helpers::hasValue(exhaustTemp1_)) {
|
||||
has_update(exhaustTemp_, exhaustTemp1_);
|
||||
}
|
||||
|
||||
// heating sends a second 0xE4 with 0xFFFF here, which would clear a valid flow, #3224
|
||||
uint16_t pc0flow = pc0Flow_;
|
||||
if (telegram->read_value(pc0flow, 36) && Helpers::hasValue(pc0flow)) { // see https://github.com/emsesp/EMS-ESP32/issues/2001
|
||||
@@ -1597,10 +1616,12 @@ void Boiler::process_UBAMonitorSlowPlus2(const std::shared_ptr<const Telegram> &
|
||||
if (telegram->read_value(state, 2)) {
|
||||
boilerState_ |= state == 1 ? 0x09 : 0; // heating 0/1
|
||||
}
|
||||
|
||||
state = EMS_VALUE_UINT8_NOTSET;
|
||||
if (telegram->read_value(state, 5)) {
|
||||
boilerState_ |= state == 1 ? 0x0A : 0; // dhw 0/1
|
||||
}
|
||||
|
||||
check_active(); // do a quick check to see if the hot water or heating is active
|
||||
}
|
||||
}
|
||||
@@ -1620,6 +1641,7 @@ void Boiler::process_UBAMonitorSlowPlus(const std::shared_ptr<const Telegram> &
|
||||
if (!Helpers::hasValue(exhaustTemp1_)) {
|
||||
has_update(telegram, exhaustTemp_, 6);
|
||||
}
|
||||
|
||||
has_update(telegram, burnStarts_, 10, 3); // force to 3 bytes
|
||||
has_update(telegram, burnWorkMin_, 13, 3); // force to 3 bytes
|
||||
has_update(telegram, burn2WorkMin_, 16, 3); // force to 3 bytes
|
||||
@@ -1686,6 +1708,7 @@ void Boiler::process_UBAParameterWWPlus(const std::shared_ptr<const Telegram> &
|
||||
} else {
|
||||
wwComfort1 = EMS_VALUE_UINT8_NOTSET;
|
||||
}
|
||||
|
||||
has_update(wwComfort1_, wwComfort1);
|
||||
}
|
||||
}
|
||||
@@ -1779,6 +1802,7 @@ void Boiler::process_HpPower(const std::shared_ptr<const Telegram> & telegram) {
|
||||
if (coolingType_ == 3) {
|
||||
has_bitupdate(telegram, hp4wayValve_, 0, 3); // https://github.com/emsesp/EMS-ESP32/issues/2844#issuecomment-3869770845
|
||||
}
|
||||
|
||||
has_bitupdate(telegram, elHeatStep1_, 3, 0);
|
||||
has_bitupdate(telegram, elHeatStep2_, 3, 1);
|
||||
has_bitupdate(telegram, elHeatStep3_, 3, 2);
|
||||
@@ -1797,7 +1821,7 @@ void Boiler::process_HpPower(const std::shared_ptr<const Telegram> & telegram) {
|
||||
has_bitupdate(telegram, hpInput[2].state, 1, 6);
|
||||
has_bitupdate(telegram, hpInput[3].state, 1, 7);
|
||||
|
||||
// from https://github.com/emsesp/EMS-ESP32/issues/2669
|
||||
// https://github.com/emsesp/EMS-ESP32/issues/2669
|
||||
has_update(telegram, hpTargetSpd_, 22);
|
||||
has_update(telegram, receiverValveVr0_, 15);
|
||||
has_update(telegram, expansionValveVr1_, 16);
|
||||
@@ -1860,11 +1884,13 @@ void Boiler::process_HpInConfig(const std::shared_ptr<const Telegram> & telegram
|
||||
telegram->read_value(option[j], index[j] + i);
|
||||
option[j] = option[j] == 1 ? '1' : '0';
|
||||
}
|
||||
|
||||
option[12] = atoi(&hpInput[i].option[12]);
|
||||
telegram->read_value(option[12], 27 + i); // modulation
|
||||
Helpers::smallitoa(&option[12], (uint16_t)option[12]);
|
||||
has_update(hpInput[i].option, option, 16);
|
||||
}
|
||||
|
||||
// input 4 <inv>[<comp><aux><cool><heat><dhw><pv><prot><pres><mod>]
|
||||
uint8_t index4[] = {42, 43, 44, 45, 46, 47, 52, 50, 49, 48};
|
||||
for (uint8_t j = 0; j < 9; j++) {
|
||||
@@ -1872,6 +1898,7 @@ void Boiler::process_HpInConfig(const std::shared_ptr<const Telegram> & telegram
|
||||
telegram->read_value(option[j], index4[j]);
|
||||
option[j] = option[j] == 1 ? '1' : '0';
|
||||
}
|
||||
|
||||
option[9] = atoi(&hpInput[3].option[9]);
|
||||
telegram->read_value(option[9], 48); // modulation
|
||||
Helpers::smallitoa(&option[9], (uint16_t)option[9]);
|
||||
@@ -1893,6 +1920,7 @@ void Boiler::process_HpHeaterConfig(const std::shared_ptr<const Telegram> & tele
|
||||
has_enumupdate(telegram, maxHeatDhw_, 4, {0, 2, 4, 5});
|
||||
return;
|
||||
}
|
||||
|
||||
has_update(telegram, maxHeatComp_, 2);
|
||||
has_update(telegram, maxHeatHeat_, 3);
|
||||
has_update(telegram, maxHeatDhw_, 4);
|
||||
@@ -1973,6 +2001,7 @@ void Boiler::process_UBAErrorMessage(const std::shared_ptr<const Telegram> & tel
|
||||
if (telegram->offset > 0 || telegram->message_length < 11) {
|
||||
return;
|
||||
}
|
||||
|
||||
// data: displaycode(2), errornumber(2), year, month, hour, day, minute, duration(2), src-addr
|
||||
static uint32_t lastCodeDate_ = 0; // last code date
|
||||
uint8_t code[3] = {telegram->message_data[0], telegram->message_data[1], 0};
|
||||
@@ -1992,8 +2021,10 @@ void Boiler::process_UBAErrorMessage(const std::shared_ptr<const Telegram> & tel
|
||||
has_update(lastCode_, newCode, sizeof(lastCode_));
|
||||
lastCodeDate_ = 1;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// store only the newest code from telegrams 10 and 11
|
||||
if (date > lastCodeDate_) {
|
||||
lastCodeDate_ = date;
|
||||
@@ -2063,6 +2094,7 @@ void Boiler::process_UBAErrorMessage2(const std::shared_ptr<const Telegram> & te
|
||||
snprintf(&code[3], sizeof(code) - 3, "(%d) @uptime %lu - %lu min", codeNo, starttime, endtime);
|
||||
date = starttime;
|
||||
}
|
||||
|
||||
if (date > lastCodeDate_) {
|
||||
lastCodeDate_ = date;
|
||||
has_update(lastCode_, code, sizeof(lastCode_));
|
||||
@@ -2129,6 +2161,7 @@ void Boiler::process_UBAErrorMessage3(const std::shared_ptr<const Telegram> & te
|
||||
snprintf(&code[3], sizeof(code) - 3, "(%d) @uptime %lu - %lu min", codeNo, starttime, endtime);
|
||||
date = starttime;
|
||||
}
|
||||
|
||||
if (date > lastCodeDate_) {
|
||||
lastCodeDate_ = date;
|
||||
has_update(lastCode_, code, sizeof(lastCode_));
|
||||
@@ -2357,6 +2390,7 @@ bool Boiler::set_keepWarmTemp(const char * value, const int8_t id) {
|
||||
if (!Helpers::value2temperature(value, v)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
write_command(0x772, 1, v, 0x772);
|
||||
return true;
|
||||
}
|
||||
@@ -2366,6 +2400,7 @@ bool Boiler::set_returnTemp(const char * value, const int8_t id) {
|
||||
if (!Helpers::value2temperature(value, v)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
write_command(0x772, 2, v, 0x772);
|
||||
return true;
|
||||
}
|
||||
@@ -2529,6 +2564,7 @@ bool Boiler::set_ww_disinfect_duration(const char * value, const int8_t id) {
|
||||
if (!Helpers::value2number(value, v, 120, 240)) { // HPC410 menu: 120-240 min in steps of 10
|
||||
return false;
|
||||
}
|
||||
|
||||
write_command(EMS_TYPE_UBAParameterWWPlus, 15, (v + 5) / 10, EMS_TYPE_UBAParameterWWPlus);
|
||||
return true;
|
||||
}
|
||||
@@ -2539,6 +2575,7 @@ bool Boiler::set_ww_prio(const char * value, const int8_t) {
|
||||
if (!Helpers::value2bool(value, b)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
write_command(0x2CC, 3, b ? 0xFF : 0, 0x2CC);
|
||||
return true;
|
||||
}
|
||||
@@ -2557,6 +2594,7 @@ bool Boiler::set_flow_temp(const char * value, const int8_t id) {
|
||||
if (has_telegram_id(0xE4)) {
|
||||
EMSESP::txservice_.add(Telegram::Operation::TX_WRITE, device_id(), EMS_TYPE_UBASetPoints2, 1, &v1, 1, 0, false);
|
||||
}
|
||||
|
||||
// always write to 0x1A
|
||||
EMSESP::txservice_.add(Telegram::Operation::TX_WRITE, device_id(), EMS_TYPE_UBASetPoints, 0, &v1, 1, 0, false);
|
||||
return true;
|
||||
@@ -2569,6 +2607,7 @@ bool Boiler::set_flow_temp(const char * value, const int8_t id) {
|
||||
} else {
|
||||
write_command(EMS_TYPE_UBASetPoints, 0, v, 0x18);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -2585,6 +2624,7 @@ bool Boiler::set_burn_power(const char * value, const int8_t id) {
|
||||
} else {
|
||||
write_command(EMS_TYPE_UBASetPoints, 1, v);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -2778,6 +2818,7 @@ bool Boiler::set_pumpMode(const char * value, const int8_t id) {
|
||||
write_command(EMS_TYPE_UBAParameters, 11, v, EMS_TYPE_UBAParameters);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -2787,6 +2828,7 @@ bool Boiler::set_pumpCharacter(const char * value, const int8_t id) {
|
||||
write_command(EMS_TYPE_UBAParametersPlus, 15, v, EMS_TYPE_UBAParametersPlus);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -2889,6 +2931,7 @@ bool Boiler::set_ww_mode(const char * value, const int8_t id) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -3074,6 +3117,7 @@ bool Boiler::set_reset(const char * value, const int8_t id) {
|
||||
write_command(0x05, 6, 154);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -3082,6 +3126,7 @@ bool Boiler::set_manDefrost(const char * value, const int8_t) {
|
||||
if (!Helpers::value2bool(value, b)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
write_command(0x05, 52, b ? 1 : 0);
|
||||
return true;
|
||||
}
|
||||
@@ -3091,6 +3136,7 @@ bool Boiler::set_chimneysweeper(const char * value, const int8_t) {
|
||||
if (!Helpers::value2bool(value, b)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
write_command(0x05, 0x04, b ? 100 : 0);
|
||||
return true;
|
||||
}
|
||||
@@ -3122,6 +3168,7 @@ bool Boiler::set_maintenance(const char * value, const int8_t id) {
|
||||
LOG_WARNING("Setting maintenance: wrong format %d.%d.%d", day, month, year + 2000);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -3145,6 +3192,7 @@ bool Boiler::set_maintenance(const char * value, const int8_t id) {
|
||||
LOG_WARNING("Setting maintenance: wrong format");
|
||||
return false;
|
||||
}
|
||||
|
||||
//maintenance
|
||||
bool Boiler::set_maintenancetime(const char * value, const int8_t id) {
|
||||
int hrs;
|
||||
@@ -3156,6 +3204,7 @@ bool Boiler::set_maintenancetime(const char * value, const int8_t id) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
LOG_WARNING("Setting maintenance: wrong format");
|
||||
return false;
|
||||
}
|
||||
@@ -3174,6 +3223,7 @@ bool Boiler::set_maintenancedate(const char * value, const int8_t id) {
|
||||
LOG_WARNING("Setting maintenance: wrong format %d.%d.%d", day, month, year + 2000);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -3187,6 +3237,7 @@ bool Boiler::set_pool_temp(const char * value, const int8_t id) {
|
||||
if (!Helpers::value2temperature(value, v)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t v2 = ((v * 2) + 0.5);
|
||||
// LOG_INFO("Setting pool temperature to %d.%d C", v2 >> 1, (v2 & 0x01) * 5);
|
||||
write_command(0x48A, 1, v2, 0x48A);
|
||||
@@ -3200,6 +3251,7 @@ bool Boiler::set_pool_on(const char * value, const int8_t id) {
|
||||
if (!Helpers::value2bool(value, v)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
write_command(0x48A, 0, v ? 0x01 : 0x00, 0x48A);
|
||||
return true;
|
||||
}
|
||||
@@ -3220,6 +3272,7 @@ bool Boiler::set_emergency_ops(const char * value, const int8_t id) {
|
||||
if (!Helpers::value2bool(value, v)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
write_command(EMS_TYPE_UBAParametersPlus, 18, v ? 0x01 : 0x00, EMS_TYPE_UBAParametersPlus);
|
||||
return true;
|
||||
}
|
||||
@@ -3228,11 +3281,13 @@ bool Boiler::set_HpInLogic(const char * value, const int8_t id) {
|
||||
if (id == 0 || id > 4 || strlen(value) > 15) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool v;
|
||||
if (strlen(value) == 1 && Helpers::value2bool(value, v)) {
|
||||
write_command(0x486, id == 4 ? 42 : id - 1, v ? 1 : 0, 0x486);
|
||||
return true;
|
||||
}
|
||||
|
||||
char option[] = {"xxxxxxxxxxxxxxx"};
|
||||
strncpy(option, value, strlen(option)); // copy without termination
|
||||
// inputs 1,2,3 <inv>[<evu1><evu2><evu3><comp><aux><cool><heat><dhw><pv><prot><pres><mod>]
|
||||
@@ -3243,9 +3298,11 @@ bool Boiler::set_HpInLogic(const char * value, const int8_t id) {
|
||||
write_command(0x486, index[i] + id - 1, option[i] - '0');
|
||||
}
|
||||
}
|
||||
|
||||
if (option[12] >= '0' && option[12] <= '9') {
|
||||
write_command(0x486, 27, (uint8_t)atoi(&option[12]));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -3256,9 +3313,11 @@ bool Boiler::set_HpInLogic(const char * value, const int8_t id) {
|
||||
write_command(0x486, index4[i], option[i] - '0');
|
||||
}
|
||||
}
|
||||
|
||||
if (option[9] >= '0' && option[9] <= '9') {
|
||||
write_command(0x486, 48, (uint8_t)atoi(&option[9]));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -3279,6 +3338,7 @@ bool Boiler::set_maxHeat(const char * value, const int8_t id) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
write_command(0x492, id, v, 0x492);
|
||||
return true;
|
||||
}
|
||||
@@ -3288,6 +3348,7 @@ bool Boiler::set_silentMode(const char * value, const int8_t id) {
|
||||
if (!Helpers::value2enum(value, v, FL_(enum_silentMode))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
write_command(0x484, 10, v, 0x484);
|
||||
return true;
|
||||
}
|
||||
@@ -3296,6 +3357,7 @@ bool Boiler::set_silentFrom(const char * value, const int8_t id) {
|
||||
if (value == nullptr || value[0] < '0' || value[0] > '9') {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto v = Helpers::string2minutes(value);
|
||||
write_command(0x484, 52, v / 15, 0x484);
|
||||
return true;
|
||||
@@ -3305,6 +3367,7 @@ bool Boiler::set_silentTo(const char * value, const int8_t id) {
|
||||
if (value == nullptr || value[0] < '0' || value[0] > '9') {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto v = Helpers::string2minutes(value);
|
||||
write_command(0x484, 53, v / 15, 0x484);
|
||||
return true;
|
||||
@@ -3316,6 +3379,7 @@ bool Boiler::set_minTempSilent(const char * value, const int8_t id) {
|
||||
write_command(0x484, 11, v, 0x484);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -3325,6 +3389,7 @@ bool Boiler::set_additionalHeaterOnly(const char * value, const int8_t id) {
|
||||
write_command(0x491, model() == EMSdevice::EMS_DEVICE_FLAG_CS6800 ? 3 : 1, v ? 1 : 0, 0x491);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -3334,6 +3399,7 @@ bool Boiler::set_additionalHeater(const char * value, const int8_t id) {
|
||||
write_command(0x491, 2, v ? 1 : 0, 0x491);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -3343,6 +3409,7 @@ bool Boiler::set_tempParMode(const char * value, const int8_t id) {
|
||||
write_command(0x491, 5, v, 0x491);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -3354,6 +3421,7 @@ bool Boiler::set_additionalHeaterDelay(const char * value, const int8_t id) {
|
||||
write_command(0x491, 16, data, 2, 0x491);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -3363,6 +3431,7 @@ bool Boiler::set_auxHeatMode(const char * value, const int8_t id) {
|
||||
write_command(0x491, 4, v, 0x491);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -3372,6 +3441,7 @@ bool Boiler::set_auxLimit(const char * value, const int8_t id) {
|
||||
write_command(0x491, id, (uint8_t)(v * 10), 0x491);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -3381,6 +3451,7 @@ bool Boiler::set_auxHeaterSource(const char * value, const int8_t) {
|
||||
write_command(0x491, 0, v, 0x491);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -3390,6 +3461,7 @@ bool Boiler::set_pvCooling(const char * value, const int8_t id) {
|
||||
write_command(0x485, 21, v ? 1 : 0, 0x485);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -3401,6 +3473,7 @@ bool Boiler::set_hpHyst(const char * value, const int8_t id) {
|
||||
write_command(0x484, id, data, 2, 0x484);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -3410,6 +3483,7 @@ bool Boiler::set_tempDiff(const char * value, const int8_t id) {
|
||||
write_command(0x48B, id, (uint8_t)(v * 10), 0x48B);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -3420,6 +3494,7 @@ bool Boiler::set_wwOffTemp(const char * value, const int8_t id) {
|
||||
write_command(0x499, id, v, 0x499);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -3429,6 +3504,7 @@ bool Boiler::set_wwDiffTemp(const char * value, const int8_t id) {
|
||||
write_command(0x499, id, v, 0x499);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -3438,6 +3514,7 @@ bool Boiler::set_hpCircPumpWw(const char * value, const int8_t id) {
|
||||
write_command(0x484, 46, v ? 1 : 0, 0x484);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -3447,6 +3524,7 @@ bool Boiler::set_hpPumpMode(const char * value, const int8_t id) {
|
||||
write_command(0x48B, 18, v, 0x48B);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -3456,6 +3534,7 @@ bool Boiler::set_hpMaxPower(const char * value, const int8_t id) {
|
||||
write_command(0x484, 31, v, 0x484);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -3465,6 +3544,7 @@ bool Boiler::set_pvMaxComp(const char * value, const int8_t) {
|
||||
write_command(0x484, 54, (uint8_t)(v * 10), 0x484);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -3474,6 +3554,7 @@ bool Boiler::set_hpDiffPress(const char * value, const int8_t id) {
|
||||
write_command(0x2CC, 9, (uint8_t)(v / 50), 0x2CC);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -3484,6 +3565,7 @@ bool Boiler::set_hpPowerLimit(const char * value, const int8_t id) {
|
||||
write_command(0x4A7, 0, data, 2, 0x4A7);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -3493,6 +3575,7 @@ bool Boiler::set_powerReduction(const char * value, const int8_t) {
|
||||
write_command(0x484, 64, v / 10, 0x484);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -3502,6 +3585,7 @@ bool Boiler::set_vp_cooling(const char * value, const int8_t id) {
|
||||
write_command(0x49C, 3, v ? 1 : 0, 0x49C);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -3511,6 +3595,7 @@ bool Boiler::set_heatCable(const char * value, const int8_t id) {
|
||||
write_command(0x49D, 2, v ? 1 : 0, 0x49D);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -3520,6 +3605,7 @@ bool Boiler::set_VC0valve(const char * value, const int8_t id) {
|
||||
write_command(0x49D, 3, v ? 1 : 0, 0x49D);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -3529,6 +3615,7 @@ bool Boiler::set_primePump(const char * value, const int8_t id) {
|
||||
write_command(0x49D, 4, v ? 1 : 0, 0x49D);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -3538,6 +3625,7 @@ bool Boiler::set_primePumpMod(const char * value, const int8_t id) {
|
||||
write_command(0x49D, 5, v, 0x49D);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -3547,6 +3635,7 @@ bool Boiler::set_hp3wayValve(const char * value, const int8_t id) {
|
||||
write_command(0x49D, 6, v ? 1 : 0, 0x49D);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -3556,6 +3645,7 @@ bool Boiler::set_elHeatStep(const char * value, const int8_t id) {
|
||||
write_command(0x49D, 6 + id, v ? 1 : 0, 0x49D);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -3565,6 +3655,7 @@ bool Boiler::set_wwAlternatingOper(const char * value, const int8_t id) {
|
||||
if (!Helpers::value2bool(value, v)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
write_command(EMS_TYPE_UBAParameterWWPlus, 14, v ? 1 : 0, EMS_TYPE_UBAParameterWWPlus);
|
||||
return true;
|
||||
}
|
||||
@@ -3576,6 +3667,7 @@ bool Boiler::set_wwAltOpPrio(const char * value, const int8_t id) {
|
||||
write_command(0x484, id, v, 0x484);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -3588,15 +3680,18 @@ bool Boiler::set_forceHeatingOff(const char * value, const int8_t id) {
|
||||
uint8_t data[] = {1, heatingTemp_, (Helpers::hasValue(burnMaxPower_) ? burnMaxPower_ : (uint8_t)100), 1, 1};
|
||||
write_command(EMS_TYPE_UBASetPoints2, 0, data, sizeof(data), 0);
|
||||
}
|
||||
|
||||
uint8_t data[] = {heatingTemp_,
|
||||
(Helpers::hasValue(burnMaxPower_) ? burnMaxPower_ : (uint8_t)100),
|
||||
(Helpers::hasValue(pumpModMax_) ? pumpModMax_ : (uint8_t)100),
|
||||
0};
|
||||
write_command(EMS_TYPE_UBASetPoints, 0, data, sizeof(data), 0);
|
||||
}
|
||||
|
||||
has_update(forceHeatingOff_, v);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -3606,10 +3701,12 @@ bool Boiler::set_nrgHeat(const char * value, const int8_t id) {
|
||||
if (!Helpers::value2float(value, v)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (v >= 0) {
|
||||
nrgHeatF_ = v * 100;
|
||||
has_update(nrgHeat_, (uint32_t)(nrgHeatF_ + 0.5));
|
||||
}
|
||||
|
||||
store_energy();
|
||||
return true;
|
||||
}
|
||||
@@ -3619,10 +3716,12 @@ bool Boiler::set_nrgWw(const char * value, const int8_t id) {
|
||||
if (!Helpers::value2float(value, v)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (v >= 0) {
|
||||
nrgWwF_ = v * 100;
|
||||
has_update(nrgWw_, (uint32_t)(nrgWwF_ + 0.5));
|
||||
}
|
||||
|
||||
store_energy();
|
||||
return true;
|
||||
}
|
||||
@@ -3632,9 +3731,11 @@ bool Boiler::set_nomPower(const char * value, const int8_t id) {
|
||||
if (!Helpers::value2number(value, v)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (v > 0 && v < EMS_VALUE_UINT8_NOTSET) {
|
||||
has_update(nomPower_, (uint8_t)v);
|
||||
}
|
||||
|
||||
store_energy();
|
||||
return true;
|
||||
}
|
||||
@@ -3645,6 +3746,7 @@ bool Boiler::set_curveOn(const char * value, const int8_t id) {
|
||||
write_command(0x28, 0, v ? 0xFF : 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -3653,6 +3755,7 @@ bool Boiler::set_curveBase(const char * value, const int8_t id) {
|
||||
if (!Helpers::value2temperature(value, v)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
write_command(0x28, 2, v);
|
||||
return true;
|
||||
}
|
||||
@@ -3662,6 +3765,7 @@ bool Boiler::set_curveEnd(const char * value, const int8_t id) {
|
||||
if (!Helpers::value2temperature(value, v)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
write_command(0x28, 1, v);
|
||||
return true;
|
||||
}
|
||||
@@ -3671,6 +3775,7 @@ bool Boiler::set_summerTemp(const char * value, const int8_t id) {
|
||||
if (!Helpers::value2temperature(value, v)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
write_command(0x28, 3, v);
|
||||
return true;
|
||||
}
|
||||
@@ -3681,6 +3786,7 @@ bool Boiler::set_nofrost(const char * value, const int8_t id) {
|
||||
write_command(0x28, 4, v ? 0xFF : 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -3689,6 +3795,7 @@ bool Boiler::set_nofrostTemp(const char * value, const int8_t id) {
|
||||
if (!Helpers::value2temperature(value, v)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
write_command(0x28, 5, v);
|
||||
return true;
|
||||
}
|
||||
@@ -3699,6 +3806,7 @@ bool Boiler::set_fan(const char * value, const int8_t id) {
|
||||
write_command(0x4A5, 9, (uint8_t)v, 0x4A5);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -3708,6 +3816,7 @@ bool Boiler::set_shutdown(const char * value, const int8_t id) {
|
||||
write_command(0x484, 58, b ? 1 : 0, 0x484);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -3717,6 +3826,7 @@ bool Boiler::set_pumpKickHour(const char * value, const int8_t) {
|
||||
write_command(0xEB, 0, v, 0xEB);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -3726,6 +3836,7 @@ bool Boiler::set_pumpKickDay(const char * value, const int8_t) {
|
||||
write_command(0xEB, 1, v + 1, 0xEB);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -3736,6 +3847,7 @@ bool Boiler::set_pumpKickDelay(const char * value, const int8_t) {
|
||||
write_command(0xEB, 2, data, 2, 0xEB);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
+32
-1
@@ -34,6 +34,7 @@ class Boiler : public EMSdevice {
|
||||
inline uint8_t model() const {
|
||||
return (flags() & 0x0F);
|
||||
}
|
||||
|
||||
inline bool isHeatPump() const {
|
||||
return flags() & 0x08;
|
||||
}
|
||||
@@ -454,9 +455,11 @@ class Boiler : public EMSdevice {
|
||||
inline bool set_hyst2_on(const char * value, const int8_t id) {
|
||||
return set_hyst_on(value, 2);
|
||||
}
|
||||
|
||||
inline bool set_hyst2_off(const char * value, const int8_t id) {
|
||||
return set_hyst_off(value, 2);
|
||||
}
|
||||
|
||||
bool set_burn_period(const char * value, const int8_t id);
|
||||
bool set_pump_delay(const char * value, const int8_t id);
|
||||
bool set_reset(const char * value, const int8_t id);
|
||||
@@ -476,25 +479,32 @@ class Boiler : public EMSdevice {
|
||||
inline bool set_HpIn1Logic(const char * value, const int8_t id) {
|
||||
return set_HpInLogic(value, 1);
|
||||
}
|
||||
|
||||
inline bool set_HpIn2Logic(const char * value, const int8_t id) {
|
||||
return set_HpInLogic(value, 2);
|
||||
}
|
||||
|
||||
inline bool set_HpIn3Logic(const char * value, const int8_t id) {
|
||||
return set_HpInLogic(value, 3);
|
||||
}
|
||||
|
||||
inline bool set_HpIn4Logic(const char * value, const int8_t id) {
|
||||
return set_HpInLogic(value, 4);
|
||||
}
|
||||
|
||||
bool set_maxHeat(const char * value, const int8_t id);
|
||||
inline bool set_maxHeatComp(const char * value, const int8_t id) {
|
||||
return set_maxHeat(value, 2);
|
||||
}
|
||||
|
||||
inline bool set_maxHeatHeat(const char * value, const int8_t id) {
|
||||
return set_maxHeat(value, 3);
|
||||
}
|
||||
|
||||
inline bool set_maxHeatDhw(const char * value, const int8_t id) {
|
||||
return set_maxHeat(value, 4);
|
||||
}
|
||||
|
||||
bool set_silentMode(const char * value, const int8_t id);
|
||||
bool set_minTempSilent(const char * value, const int8_t id);
|
||||
bool set_silentFrom(const char * value, const int8_t id);
|
||||
@@ -518,55 +528,71 @@ class Boiler : public EMSdevice {
|
||||
inline bool set_auxMaxLimit(const char * value, const int8_t id) {
|
||||
return set_auxLimit(value, 14);
|
||||
}
|
||||
|
||||
inline bool set_auxLimitStart(const char * value, const int8_t id) {
|
||||
return set_auxLimit(value, 15);
|
||||
}
|
||||
|
||||
bool set_hpHyst(const char * value, const int8_t id);
|
||||
inline bool set_hpHystHeat(const char * value, const int8_t id) {
|
||||
return set_hpHyst(value, 37);
|
||||
}
|
||||
|
||||
inline bool set_hpHystCool(const char * value, const int8_t id) {
|
||||
return set_hpHyst(value, 35);
|
||||
}
|
||||
|
||||
inline bool set_hpHystPool(const char * value, const int8_t id) {
|
||||
return set_hpHyst(value, 33);
|
||||
}
|
||||
|
||||
bool set_tempDiff(const char * value, const int8_t id);
|
||||
inline bool set_tempDiffHeat(const char * value, const int8_t id) {
|
||||
return set_tempDiff(value, 4);
|
||||
}
|
||||
|
||||
inline bool set_tempDiffCool(const char * value, const int8_t id) {
|
||||
return set_tempDiff(value, 3);
|
||||
}
|
||||
|
||||
bool set_wwOffTemp(const char * value, const int8_t id);
|
||||
inline bool set_wwComfOffTemp(const char * value, const int8_t id) {
|
||||
return set_wwOffTemp(value, 1);
|
||||
}
|
||||
|
||||
inline bool set_wwEcoOffTemp(const char * value, const int8_t id) {
|
||||
return set_wwOffTemp(value, 0);
|
||||
}
|
||||
|
||||
inline bool set_wwEcoPlusOffTemp(const char * value, const int8_t id) {
|
||||
return set_wwOffTemp(value, 5);
|
||||
}
|
||||
|
||||
bool set_wwDiffTemp(const char * value, const int8_t id);
|
||||
inline bool set_wwComfDiffTemp(const char * value, const int8_t id) {
|
||||
return set_wwDiffTemp(value, 12);
|
||||
}
|
||||
|
||||
inline bool set_wwEcoDiffTemp(const char * value, const int8_t id) {
|
||||
return set_wwDiffTemp(value, 13);
|
||||
}
|
||||
|
||||
inline bool set_wwEcoPlusDiffTemp(const char * value, const int8_t id) {
|
||||
return set_wwDiffTemp(value, 14);
|
||||
}
|
||||
|
||||
inline bool set_wwComfStopTemp(const char * value, const int8_t id) {
|
||||
return set_wwOffTemp(value, 8);
|
||||
}
|
||||
|
||||
inline bool set_wwEcoStopTemp(const char * value, const int8_t id) {
|
||||
return set_wwOffTemp(value, 9);
|
||||
}
|
||||
|
||||
inline bool set_wwEcoPlusStopTemp(const char * value, const int8_t id) {
|
||||
return set_wwOffTemp(value, 10);
|
||||
}
|
||||
|
||||
bool set_vp_cooling(const char * value, const int8_t id);
|
||||
bool set_heatCable(const char * value, const int8_t id);
|
||||
bool set_VC0valve(const char * value, const int8_t id);
|
||||
@@ -577,20 +603,25 @@ class Boiler : public EMSdevice {
|
||||
inline bool set_elHeatStep1(const char * value, const int8_t id) {
|
||||
return set_elHeatStep(value, 1);
|
||||
}
|
||||
|
||||
inline bool set_elHeatStep2(const char * value, const int8_t id) {
|
||||
return set_elHeatStep(value, 2);
|
||||
}
|
||||
|
||||
inline bool set_elHeatStep3(const char * value, const int8_t id) {
|
||||
return set_elHeatStep(value, 3);
|
||||
}
|
||||
|
||||
bool set_wwAlternatingOper(const char * value, const int8_t id);
|
||||
bool set_wwAltOpPrio(const char * value, const int8_t id);
|
||||
inline bool set_wwAltOpPrioHeat(const char * value, const int8_t id) {
|
||||
return set_wwAltOpPrio(value, 2);
|
||||
}
|
||||
|
||||
inline bool set_wwAltOpPrioWw(const char * value, const int8_t id) {
|
||||
return set_wwAltOpPrio(value, 3);
|
||||
}
|
||||
|
||||
bool set_forceHeatingOff(const char * value, const int8_t id);
|
||||
|
||||
/*
|
||||
@@ -622,4 +653,4 @@ class Boiler : public EMSdevice {
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -27,6 +27,7 @@ Connect::Connect(uint8_t device_type, uint8_t device_id, uint8_t product_id, con
|
||||
if (device_id != EMSdevice::EMS_DEVICE_ID_RFBASE) { // Modems have no entities
|
||||
return;
|
||||
}
|
||||
|
||||
register_telegram_type(0xD1, "RFOutdoorTemp", false, MAKE_PF_CB(process_OutdoorTemp));
|
||||
register_telegram_type(0x06, "RCTime", false, MAKE_PF_CB(process_RCTime));
|
||||
|
||||
@@ -45,6 +46,7 @@ Connect::Connect(uint8_t device_type, uint8_t device_id, uint8_t product_id, con
|
||||
register_telegram_type(0x1230 + i, "Roomparams", false, MAKE_PF_CB(process_roomThermostatParam)); // fetch for active circuits
|
||||
register_telegram_type(0x1244 + i, "Roomdata", false, MAKE_PF_CB(process_roomThermostatData)); // broadcasted
|
||||
}
|
||||
|
||||
register_telegram_type(0x0B65, "Roomschedule", true, MAKE_PF_CB(process_roomSchedule));
|
||||
register_telegram_type(0xF7, "MenuConfig", false, MAKE_PF_CB(process_roomConfig));
|
||||
// 0x2040, broadcast 36 bytes:
|
||||
@@ -57,6 +59,7 @@ Connect::Connect(uint8_t device_type, uint8_t device_id, uint8_t product_id, con
|
||||
register_device_values_room(new_room);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* OutdoorTemp - type 0xD1 - external temperature
|
||||
*/
|
||||
@@ -70,6 +73,7 @@ void Connect::process_RCTime(const std::shared_ptr<const Telegram> & telegram) {
|
||||
if (telegram->offset || telegram->message_length < 10) {
|
||||
return;
|
||||
}
|
||||
|
||||
char time[sizeof(dateTime_)];
|
||||
snprintf(time,
|
||||
sizeof(time),
|
||||
@@ -106,9 +110,11 @@ std::shared_ptr<Connect::RoomCircuit> Connect::room_circuit(const uint8_t num, c
|
||||
return room_circuit;
|
||||
}
|
||||
}
|
||||
|
||||
if (!create) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// create a new circuit object and add to the list
|
||||
auto new_room = std::make_shared<Connect::RoomCircuit>(num);
|
||||
room_circuits_.push_back(new_room);
|
||||
@@ -128,12 +134,14 @@ void Connect::process_roomThermostat(const std::shared_ptr<const Telegram> & tel
|
||||
if (rc == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
has_update(telegram, rc->temp_, 0);
|
||||
has_update(telegram, rc->humidity_, 2); // could show -3 if not set
|
||||
// make sure we have read mode and icon, needed for ha climate
|
||||
if (!Mqtt::ha_enabled() || (Helpers::hasValue(rc->mode_) && Helpers::hasValue(rc->icon_))) {
|
||||
has_update(telegram, rc->seltemp_, 3);
|
||||
}
|
||||
|
||||
// calculate dew temperature
|
||||
has_update(rc->dewtemp_, Helpers::calc_dew(rc->temp_, rc->humidity_));
|
||||
}
|
||||
@@ -145,12 +153,14 @@ void Connect::process_roomThermostatName(const std::shared_ptr<const Telegram> &
|
||||
if (rc == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
has_update(telegram, rc->icon_, 0);
|
||||
for (uint8_t i = telegram->offset; i < telegram->message_length + telegram->offset && i < 100; i++) {
|
||||
if ((i > 1) && (i % 2) == 0) {
|
||||
rc->name_[(i - 2) / 2] = telegram->message_data[i - telegram->offset];
|
||||
}
|
||||
}
|
||||
|
||||
rc->name_[50] = '\0'; // make sure name is terminated
|
||||
}
|
||||
|
||||
@@ -161,6 +171,7 @@ void Connect::process_roomThermostatSettings(const std::shared_ptr<const Telegra
|
||||
if (rc == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
// has_enumupdate(telegram, rc->mode_, 0, {3, 1, 0}); // modes off, manual auto
|
||||
// has_update(telegram, rc->mode_, 0); // modes: auto, heat, cool, off
|
||||
// has_update(telegram, rc->tempautotemp_, 1); // FF means off
|
||||
@@ -215,6 +226,7 @@ void Connect::process_roomSchedule(const std::shared_ptr<const Telegram> & teleg
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
toggle_fetch(telegram->type_id, false); // fetch only once if all is initialized
|
||||
}
|
||||
|
||||
@@ -227,6 +239,7 @@ void Connect::process_roomConfig(const std::shared_ptr<const Telegram> & telegra
|
||||
if (rc == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t bits = telegram->message_data[3];
|
||||
if (rc->mode_ == 2) {
|
||||
if (bits & 0x02) {
|
||||
@@ -235,6 +248,7 @@ void Connect::process_roomConfig(const std::shared_ptr<const Telegram> & telegra
|
||||
rc->coolmode_ = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
switch (bits & 0x6A) {
|
||||
case 0: // cooling off
|
||||
@@ -272,12 +286,14 @@ bool Connect::set_mode(const char * value, const int8_t id) {
|
||||
if (rc == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t v;
|
||||
if (!Helpers::value2enum(value, v, FL_(enum_mode2), {3, 1, 0})) {
|
||||
if (!Helpers::value2enum(value, v, FL_(enum_mode_ha), {3, 1, 0})) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
write_command(0xBB5 + rc->room(), rc->coolmode_ == 1 ? 4 : 0, v); // no validate, mode change is broadcasted
|
||||
return true;
|
||||
}
|
||||
@@ -287,6 +303,7 @@ bool Connect::set_seltemp(const char * value, const int8_t id) {
|
||||
if (rc == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
float v;
|
||||
if (Helpers::value2temperature(value, v)) {
|
||||
// depends on mode, auto (2 for enum_mode2, 0 for enum_mode8) set in offset 1
|
||||
@@ -295,8 +312,10 @@ bool Connect::set_seltemp(const char * value, const int8_t id) {
|
||||
} else {
|
||||
write_command(0xBB5 + rc->room(), rc->mode_ == 2 ? 1 : 3, v == -1 ? 0xFF : uint8_t(v * 2));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -305,6 +324,7 @@ bool Connect::set_name(const char * value, const int8_t id) {
|
||||
if (rc == nullptr || value == nullptr || strlen(value) > 50) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Helpers::utf8tolatin1(rc->name_, value, sizeof(rc->name_));
|
||||
uint8_t len = strlen(rc->name_) * 2 + 2;
|
||||
uint8_t data[len];
|
||||
@@ -312,6 +332,7 @@ bool Connect::set_name(const char * value, const int8_t id) {
|
||||
data[2 * i] = 0;
|
||||
data[2 * i + 1] = rc->name_[i];
|
||||
}
|
||||
|
||||
uint8_t ofs = 0;
|
||||
while (len > 0) {
|
||||
uint8_t part = len > 25 ? 25 : len;
|
||||
@@ -319,6 +340,7 @@ bool Connect::set_name(const char * value, const int8_t id) {
|
||||
ofs += part;
|
||||
len -= part;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -327,11 +349,13 @@ bool Connect::set_childlock(const char * value, const int8_t id) {
|
||||
if (rc == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool b;
|
||||
if (Helpers::value2bool(value, b)) {
|
||||
write_command(0xBB5 + rc->room(), 7, b ? 1 : 0, 0xBB5 + rc->room());
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -340,11 +364,13 @@ bool Connect::set_icon(const char * value, const int8_t id) {
|
||||
if (rc == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t v;
|
||||
if (Helpers::value2enum(value, v, FL_(enum_icons))) {
|
||||
write_command(0x0B3D + rc->room(), 0, v, 0x0B3D + rc->room());
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@ class Connect : public EMSdevice {
|
||||
RoomCircuit(const uint8_t num)
|
||||
: room_(num) {
|
||||
}
|
||||
|
||||
~RoomCircuit() = default;
|
||||
int16_t temp_;
|
||||
uint8_t humidity_;
|
||||
|
||||
@@ -36,6 +36,7 @@ void Controller::process_dateTime(const std::shared_ptr<const Telegram> & telegr
|
||||
if (telegram->offset > 0 || telegram->message_length < 5) {
|
||||
return;
|
||||
}
|
||||
|
||||
char newdatetime[sizeof(dateTime_)];
|
||||
// publish as dd.mm.yyyy hh:mm
|
||||
snprintf(newdatetime,
|
||||
@@ -49,4 +50,4 @@ void Controller::process_dateTime(const std::shared_ptr<const Telegram> & telegr
|
||||
has_update(dateTime_, newdatetime, sizeof(dateTime_));
|
||||
}
|
||||
|
||||
} // namespace emsesp
|
||||
} // namespace emsesp
|
||||
|
||||
@@ -28,6 +28,7 @@ Extension::Extension(uint8_t device_type, uint8_t device_id, uint8_t product_id,
|
||||
// no entities for T1RF outdoor sensor, values are coming from RFbase 0x50 (connect)
|
||||
return;
|
||||
}
|
||||
|
||||
// Extension module EM100 device_id 0x12
|
||||
register_telegram_type(0x935, "EM100SetMessage", true, MAKE_PF_CB(process_EM100SetMessage), 5);
|
||||
register_telegram_type(0x936, "EM100OutMessage", false, MAKE_PF_CB(process_EM100OutMessage));
|
||||
@@ -108,6 +109,7 @@ bool Extension::set_minV(const char * value, const int8_t id) {
|
||||
if (!Helpers::value2float(value, v)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
write_command(0x935, 1, (uint8_t)(v * 10));
|
||||
return true;
|
||||
}
|
||||
@@ -117,6 +119,7 @@ bool Extension::set_maxV(const char * value, const int8_t id) {
|
||||
if (!Helpers::value2float(value, v)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
write_command(0x935, 2, (uint8_t)(v * 10));
|
||||
return true;
|
||||
}
|
||||
@@ -126,6 +129,7 @@ bool Extension::set_minT(const char * value, const int8_t id) {
|
||||
if (!Helpers::value2temperature(value, v)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
write_command(0x935, 3, v);
|
||||
return true;
|
||||
}
|
||||
@@ -135,6 +139,7 @@ bool Extension::set_maxT(const char * value, const int8_t id) {
|
||||
if (!Helpers::value2temperature(value, v)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
write_command(0x935, 4, v);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -381,119 +381,148 @@ bool Heatpump::set_controlStrategy(const char * value, const int8_t id) {
|
||||
if (!Helpers::value2enum(value, v, FL_(enum_hybridStrategy1))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
write_command(0x998, 0, v, 0x998);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Heatpump::set_lowNoiseMode(const char * value, const int8_t id) {
|
||||
uint8_t v;
|
||||
if (!Helpers::value2enum(value, v, FL_(enum_lowNoiseMode))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
write_command(0x998, 9, v, 0x998);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Heatpump::set_lowNoiseStart(const char * value, const int8_t id) {
|
||||
int v;
|
||||
if (!Helpers::value2number(value, v, 0, 23)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
write_command(0x998, 10, v, 0x998);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Heatpump::set_lowNoiseStop(const char * value, const int8_t id) {
|
||||
int v;
|
||||
if (!Helpers::value2number(value, v, 0, 23)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
write_command(0x998, 11, v, 0x998);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Heatpump::set_hybridDHW(const char * value, const int8_t id) {
|
||||
uint8_t v;
|
||||
if (!Helpers::value2enum(value, v, FL_(enum_comfort2))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
write_command(0x998, 1, v, 0x998);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Heatpump::set_energyPriceGas(const char * value, const int8_t id) {
|
||||
int v;
|
||||
if (!Helpers::value2number(value, v)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
write_command(0x998, 2, v, 0x998);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Heatpump::set_energyPriceEl(const char * value, const int8_t id) {
|
||||
int v;
|
||||
if (!Helpers::value2number(value, v)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
write_command(0x998, 5, v, 0x998);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Heatpump::set_energyPricePV(const char * value, const int8_t id) {
|
||||
int v;
|
||||
if (!Helpers::value2number(value, v)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
write_command(0x998, 6, v, 0x998);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Heatpump::set_switchOverTemp(const char * value, const int8_t id) {
|
||||
int v;
|
||||
if (!Helpers::value2number(value, v)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
write_command(0x998, 14, v, 0x998);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Function tests:
|
||||
bool Heatpump::set_airPurgeMode(const char * value, const int8_t id) {
|
||||
bool v;
|
||||
if (!Helpers::value2bool(value, v)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
write_command(0x999, 0, v, 0x999);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Heatpump::set_heatPumpOutput(const char * value, const int8_t id) {
|
||||
int v;
|
||||
if (!Helpers::value2number(value, v, 0, 100)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
write_command(0x999, 2, v, 0x999);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Heatpump::set_coolingCircuit(const char * value, const int8_t id) {
|
||||
bool v;
|
||||
if (!Helpers::value2bool(value, v)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
write_command(0x999, 6, v, 0x999);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Heatpump::set_compStartMod(const char * value, const int8_t id) {
|
||||
int v;
|
||||
if (!Helpers::value2number(value, v, 0, 100)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
write_command(0x999, 7, v, 0x999);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Heatpump::set_heatDrainPan(const char * value, const int8_t id) {
|
||||
bool v;
|
||||
if (!Helpers::value2bool(value, v)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
write_command(0x999, 9, v, 0x999);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Heatpump::set_heatCable(const char * value, const int8_t id) {
|
||||
bool v;
|
||||
if (!Helpers::value2bool(value, v)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
write_command(0x999, 10, v, 0x999);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -187,6 +187,7 @@ bool Heatsource::set_vr2Config(const char * value, const int8_t id) {
|
||||
if (!Helpers::value2enum(value, v, FL_(enum_vr2Config))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
write_command(0x54C, 12, v, 0x54C);
|
||||
return true;
|
||||
}
|
||||
@@ -197,6 +198,7 @@ bool Heatsource::set_ahsActivated(const char * value, const int8_t id) {
|
||||
if (!Helpers::value2bool(value, v)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
write_command(0x54C, 0, v, 0x54C);
|
||||
return true;
|
||||
}
|
||||
@@ -207,6 +209,7 @@ bool Heatsource::set_aPumpConfig(const char * value, const int8_t id) {
|
||||
if (!Helpers::value2bool(value, v)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
write_command(0x54C, 4, v, 0x54C);
|
||||
return true;
|
||||
}
|
||||
@@ -217,6 +220,7 @@ bool Heatsource::set_aPumpSignal(const char * value, const int8_t id) {
|
||||
if (!Helpers::value2enum(value, v, FL_(enum_aPumpSignal))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
write_command(0x54C, 3, v, 0x54C);
|
||||
return true;
|
||||
}
|
||||
@@ -227,6 +231,7 @@ bool Heatsource::set_aPumpMin(const char * value, const int8_t id) {
|
||||
if (!Helpers::value2number(value, v)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
write_command(0x54C, 21, v, 0x54C);
|
||||
return true;
|
||||
}
|
||||
@@ -237,6 +242,7 @@ bool Heatsource::set_tempRise(const char * value, const int8_t id) {
|
||||
if (!Helpers::value2bool(value, v)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
write_command(0x54C, 1, v, 0x54C);
|
||||
return true;
|
||||
}
|
||||
@@ -247,6 +253,7 @@ bool Heatsource::set_setReturnTemp(const char * value, const int8_t id) {
|
||||
if (!Helpers::value2temperature(value, v)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
write_command(0x54C, 6, v, 0x54C);
|
||||
return true;
|
||||
}
|
||||
@@ -257,6 +264,7 @@ bool Heatsource::set_mixRuntime(const char * value, const int8_t id) {
|
||||
if (!Helpers::value2number(value, v)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t data[2] = {(uint8_t)(v >> 8), (uint8_t)v};
|
||||
write_command(0x54C, 8, data, 2, 0x54C);
|
||||
return true;
|
||||
@@ -268,6 +276,7 @@ bool Heatsource::set_setFlowTemp(const char * value, const int8_t id) {
|
||||
if (!Helpers::value2number(value, v)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
write_command(0x54C, 7, v, 0x54C);
|
||||
return true;
|
||||
}
|
||||
@@ -278,6 +287,7 @@ bool Heatsource::set_bufBypass(const char * value, const int8_t id) {
|
||||
if (!Helpers::value2enum(value, v, FL_(enum_bufBypass))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
write_command(0x54C, 2, v, 0x54C);
|
||||
return true;
|
||||
}
|
||||
@@ -288,6 +298,7 @@ bool Heatsource::set_bufMixRuntime(const char * value, const int8_t id) {
|
||||
if (!Helpers::value2number(value, v)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t data[2] = {(uint8_t)(v >> 8), (uint8_t)v};
|
||||
write_command(0x54C, 10, data, 2, 0x54C);
|
||||
return true;
|
||||
@@ -299,6 +310,7 @@ bool Heatsource::set_bufConfig(const char * value, const int8_t id) {
|
||||
if (!Helpers::value2enum(value, v, FL_(enum_bufConfig))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
write_command(0x54C, 20, v, 0x54C);
|
||||
return true;
|
||||
}
|
||||
@@ -309,6 +321,7 @@ bool Heatsource::set_blockMode(const char * value, const int8_t id) {
|
||||
if (!Helpers::value2enum(value, v, FL_(enum_blockMode))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
write_command(0x54C, 16, v, 0x54C);
|
||||
return true;
|
||||
}
|
||||
@@ -319,6 +332,7 @@ bool Heatsource::set_blockTerm(const char * value, const int8_t id) {
|
||||
if (!Helpers::value2enum(value, v, FL_(enum_blockTerm))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
write_command(0x54C, 17, v, 0x54C);
|
||||
return true;
|
||||
}
|
||||
@@ -329,6 +343,7 @@ bool Heatsource::set_blockHyst(const char * value, const int8_t id) {
|
||||
if (!Helpers::value2temperature(value, v, true)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
write_command(0x54C, 14, v, 0x54C);
|
||||
return true;
|
||||
}
|
||||
@@ -339,6 +354,7 @@ bool Heatsource::set_releaseWait(const char * value, const int8_t id) {
|
||||
if (!Helpers::value2number(value, v)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
write_command(0x54C, 15, v, 0x54C);
|
||||
return true;
|
||||
}
|
||||
@@ -363,4 +379,4 @@ bool Heatsource::set_valveReturn(const char * value, const int8_t id) {
|
||||
}
|
||||
*/
|
||||
|
||||
} // namespace emsesp
|
||||
} // namespace emsesp
|
||||
|
||||
@@ -190,19 +190,23 @@ bool Mixer::set_flowSetTemp(const char * value, const int8_t id) {
|
||||
if (!Helpers::value2number(value, v)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (flags() == EMSdevice::EMS_DEVICE_FLAG_MM10) {
|
||||
write_command(0xAC, 0, v, 0xAB);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (flags() == EMSdevice::EMS_DEVICE_FLAG_IPM) {
|
||||
write_command(0x123, 0, v, 0x10C);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (flags() == EMSdevice::EMS_DEVICE_FLAG_MMPLUS) {
|
||||
uint8_t hc = device_id() - 0x20;
|
||||
write_command(0x2E1 + hc, 1, v, 0x2D7 + hc);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -211,21 +215,25 @@ bool Mixer::set_pump(const char * value, const int8_t id) {
|
||||
if (!Helpers::value2bool(value, b)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (flags() == EMSdevice::EMS_DEVICE_FLAG_MM10) {
|
||||
// AC telegram can only be written with offset 0
|
||||
uint8_t dat[2] = {flowSetTemp_, b ? (uint8_t)0x64 : (uint8_t)0};
|
||||
write_command(0xAC, 0, dat, sizeof(dat), 0xAB);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (flags() == EMSdevice::EMS_DEVICE_FLAG_IPM) {
|
||||
write_command(0x123, 1, b ? 0x64 : 0, 0x10C);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (flags() == EMSdevice::EMS_DEVICE_FLAG_MMPLUS) {
|
||||
uint8_t hc = device_id() - 0x20;
|
||||
write_command(0x2E1 + hc, 2, b ? 0x64 : 0, 0x2D7 + hc);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -234,15 +242,18 @@ bool Mixer::set_activated(const char * value, const int8_t id) {
|
||||
if (!Helpers::value2bool(value, b)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (flags() == EMSdevice::EMS_DEVICE_FLAG_MM10) {
|
||||
write_command(0xAA, 0, b ? 0xFF : 0, 0xAA);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (flags() == EMSdevice::EMS_DEVICE_FLAG_MMPLUS) {
|
||||
uint8_t hc = device_id() - 0x20;
|
||||
write_command(0x2CD + hc, 0, b ? 0xFF : 0, 0x2CD + hc);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -251,17 +262,20 @@ bool Mixer::set_setValveTime(const char * value, const int8_t id) {
|
||||
if (!Helpers::value2number(value, v)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (flags() == EMSdevice::EMS_DEVICE_FLAG_MM10) {
|
||||
v = (v + 5) / 10;
|
||||
write_command(0xAA, 1, v, 0xAA);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (flags() == EMSdevice::EMS_DEVICE_FLAG_MMPLUS) {
|
||||
v = (v + 5) / 10;
|
||||
uint8_t hc = device_id() - 0x20;
|
||||
write_command(0x2CD + hc, 1, v, 0x2CD + hc);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -270,11 +284,13 @@ bool Mixer::set_flowTempOffset(const char * value, const int8_t id) {
|
||||
if (!Helpers::value2number(value, v)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (flags() == EMSdevice::EMS_DEVICE_FLAG_MMPLUS) {
|
||||
uint8_t hc = device_id() - 0x20;
|
||||
write_command(0x2CD + hc, 2, v, 0x2CD + hc);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -283,6 +299,7 @@ bool Mixer::set_pressure(const char * value, const int8_t) {
|
||||
if (!Helpers::value2number(value, v)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t hc = device_id() - 0x20;
|
||||
write_command(0x2CD + hc, 9, v / 50, 0x2CD + hc);
|
||||
return true;
|
||||
@@ -293,6 +310,7 @@ bool Mixer::set_wwprio(const char * value, const int8_t) {
|
||||
if (!Helpers::value2bool(value, b)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t hc = device_id() - 0x20;
|
||||
write_command(0x2CD + hc, 3, b ? 0xFF : 0, 0x2CD + hc);
|
||||
return true;
|
||||
|
||||
@@ -50,6 +50,7 @@ void Pool::process_HpPoolStatus(const std::shared_ptr<const Telegram> & telegram
|
||||
if (telegram->read_value(request, 4)) {
|
||||
has_update(poolRequest_, request == 0 ? EMS_VALUE_BOOL_ON : EMS_VALUE_BOOL_OFF);
|
||||
}
|
||||
|
||||
has_update(telegram, poolTemp_, 0);
|
||||
has_update(telegram, poolShunt_, 3); // 0-100%, 0% = pool / 100% = heat
|
||||
telegram->read_value(poolShuntStatus__, 2);
|
||||
|
||||
@@ -137,6 +137,7 @@ Solar::Solar(uint8_t device_type, uint8_t device_id, uint8_t product_id, const c
|
||||
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &data0_, DeviceValueType::UINT8, FL_(data0), DeviceValueUOM::NONE);
|
||||
*/
|
||||
}
|
||||
|
||||
if (flags == EMSdevice::EMS_DEVICE_FLAG_ISM) {
|
||||
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA,
|
||||
&cylMiddleTemp_,
|
||||
@@ -482,6 +483,7 @@ void Solar::process_SM10Monitor(const std::shared_ptr<const Telegram> & telegram
|
||||
if (solarpumpmod == 100 && solarPumpMod_ == 0 && solarPumpMinMod_ > 0) {
|
||||
solarpumpmod = solarPumpMinMod_; // set to minimum
|
||||
}
|
||||
|
||||
has_update(solarPumpMod_, solarpumpmod);
|
||||
|
||||
if (!Helpers::hasValue(maxFlow_)) {
|
||||
@@ -497,11 +499,13 @@ void Solar::process_SM10Monitor(const std::shared_ptr<const Telegram> & telegram
|
||||
if (energy.size() >= 60) {
|
||||
energy.pop_front();
|
||||
}
|
||||
|
||||
energy.push_back(solarPower_);
|
||||
int32_t sum = 0;
|
||||
for (auto e : energy) {
|
||||
sum += e;
|
||||
}
|
||||
|
||||
energyLastHour_ = sum > 0 ? sum / 6 : 0; // counts in 0.1 Wh
|
||||
has_update(&solarPower_);
|
||||
has_update(&energyLastHour_);
|
||||
@@ -667,10 +671,12 @@ void Solar::process_SM100Status(const std::shared_ptr<const Telegram> & telegram
|
||||
if (solarpumpmod == 100 && solarPumpMod_ == 0 && solarPumpMinMod_ > 0) {
|
||||
solarpumpmod = solarPumpMinMod_ * 5;
|
||||
}
|
||||
|
||||
has_update(solarPumpMod_, solarpumpmod);
|
||||
if (cylinderpumpmod == 100 && cylPumpMod_ == 0 && solarPumpMinMod_ > 0) {
|
||||
cylinderpumpmod = solarPumpMinMod_ * 5;
|
||||
}
|
||||
|
||||
has_update(cylPumpMod_, cylinderpumpmod);
|
||||
|
||||
has_bitupdate(telegram, cylHeated_, 3, 1); // issue #422
|
||||
@@ -683,6 +689,7 @@ void Solar::process_SM100Status(const std::shared_ptr<const Telegram> & telegram
|
||||
if (solarpumpmod == 100 && solarPump2Mod_ == 0 && solarPumpMinMod_ > 0) {
|
||||
solarpumpmod = solarPumpMinMod_ * 5; // set to minimum
|
||||
}
|
||||
|
||||
has_update(solarPump2Mod_, solarpumpmod);
|
||||
|
||||
has_update(telegram, transferPumpMod_, 14);
|
||||
@@ -720,6 +727,7 @@ void Solar::process_SM100CollectorConfig(const std::shared_ptr<const Telegram> &
|
||||
collector2Area_ = EMS_VALUE_UINT16_NOTSET;
|
||||
collector2Type_ = EMS_VALUE_UINT8_NOTSET;
|
||||
}
|
||||
|
||||
// has_enumupdate(telegram, collector2Type_, 8, 1);
|
||||
}
|
||||
|
||||
@@ -795,11 +803,13 @@ bool Solar::set_CollectorMaxTemp(const char * value, const int8_t id) {
|
||||
if (!Helpers::value2temperature(value, temperature)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (flags() == EMSdevice::EMS_DEVICE_FLAG_SM10) {
|
||||
write_command(0x96, 3, (uint8_t)temperature, 0x96);
|
||||
} else {
|
||||
write_command(0x35A, 0, (uint8_t)temperature, 0x35A);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -809,11 +819,13 @@ bool Solar::set_CollectorMinTemp(const char * value, const int8_t id) {
|
||||
if (!Helpers::value2temperature(value, temperature)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (flags() == EMSdevice::EMS_DEVICE_FLAG_SM10) {
|
||||
write_command(0x96, 4, (uint8_t)temperature, 0x96);
|
||||
} else {
|
||||
write_command(0x35A, 4, (uint8_t)temperature, 0x35A);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -823,6 +835,7 @@ bool Solar::set_cylMaxTemp(const char * value, const int8_t id) {
|
||||
if (!Helpers::value2temperature(value, temperature)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (flags() == EMSdevice::EMS_DEVICE_FLAG_SM10) {
|
||||
write_command(0x96, 5, (uint8_t)temperature, 0x96);
|
||||
} else if (flags() == EMSdevice::EMS_DEVICE_FLAG_ISM) {
|
||||
@@ -830,6 +843,7 @@ bool Solar::set_cylMaxTemp(const char * value, const int8_t id) {
|
||||
} else {
|
||||
write_command(0x35A, 3, (uint8_t)temperature, 0x35A);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -839,11 +853,13 @@ bool Solar::set_PumpMinMod(const char * value, const int8_t id) {
|
||||
if (!Helpers::value2number(value, modulation)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (flags() == EMSdevice::EMS_DEVICE_FLAG_SM10) {
|
||||
write_command(0x96, 2, (uint8_t)modulation, 0x96);
|
||||
} else {
|
||||
write_command(0x35A, 6, (uint8_t)(modulation + 2) / 5, 0x35A);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -853,6 +869,7 @@ bool Solar::set_Pump2MinMod(const char * value, const int8_t id) {
|
||||
if (!Helpers::value2number(value, modulation)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// write_command(0x35D, x, (uint8_t)(modulation), 0x35D);
|
||||
return true;
|
||||
}
|
||||
@@ -863,6 +880,7 @@ bool Solar::set_wwMinTemp(const char * value, const int8_t id) {
|
||||
if (!Helpers::value2temperature(value, temperature)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
write_command(0x96, 6, (uint8_t)temperature, 0x96);
|
||||
return true;
|
||||
}
|
||||
@@ -873,11 +891,13 @@ bool Solar::set_TurnoffDiff(const char * value, const int8_t id) {
|
||||
if (!Helpers::value2temperature(value, temperature, true)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (flags() == EMSdevice::EMS_DEVICE_FLAG_SM10) {
|
||||
write_command(0x96, 8, (uint8_t)temperature, 0x96);
|
||||
} else {
|
||||
write_command(0x35A, 7, (uint8_t)(temperature * 10), 0x35A);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -887,11 +907,13 @@ bool Solar::set_TurnonDiff(const char * value, const int8_t id) {
|
||||
if (!Helpers::value2temperature(value, temperature, true)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (flags() == EMSdevice::EMS_DEVICE_FLAG_SM10) {
|
||||
write_command(0x96, 7, (uint8_t)temperature, 0x96);
|
||||
} else {
|
||||
write_command(0x35A, 8, (uint8_t)(temperature * 10), 0x35A);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -901,6 +923,7 @@ bool Solar::set_TurnoffDiff2(const char * value, const int8_t id) {
|
||||
if (!Helpers::value2temperature(value, temperature, true)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
write_command(0x361, 3, (uint8_t)(temperature * 10), 0x361);
|
||||
return true;
|
||||
}
|
||||
@@ -911,6 +934,7 @@ bool Solar::set_TurnonDiff2(const char * value, const int8_t id) {
|
||||
if (!Helpers::value2temperature(value, temperature, true)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
write_command(0x361, 4, (uint8_t)(temperature * 10), 0x361);
|
||||
return true;
|
||||
}
|
||||
@@ -921,6 +945,7 @@ bool Solar::set_SM10MaxFlow(const char * value, const int8_t id) {
|
||||
if (!Helpers::value2float(value, flow)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
maxFlow_ = (flow * 10);
|
||||
EMSESP::webSettingsService.update([&](WebSettings & settings) {
|
||||
settings.solar_maxflow = maxFlow_;
|
||||
@@ -935,6 +960,7 @@ bool Solar::set_heatTransferSystem(const char * value, const int8_t id) {
|
||||
if (!Helpers::value2bool(value, b)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
write_command(0x358, 5, b ? 0xFF : 0x00); // change is broadcasted
|
||||
return true;
|
||||
}
|
||||
@@ -945,6 +971,7 @@ bool Solar::set_externalCyl(const char * value, const int8_t id) {
|
||||
if (!Helpers::value2bool(value, b)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
write_command(0x358, 9, b ? 0xFF : 0x00); // change is broadcasted
|
||||
return true;
|
||||
}
|
||||
@@ -955,6 +982,7 @@ bool Solar::set_thermalDisinfect(const char * value, const int8_t id) {
|
||||
if (!Helpers::value2bool(value, b)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
write_command(0x358, 10, b ? 0xFF : 0x00); // change is broadcasted
|
||||
return true;
|
||||
}
|
||||
@@ -965,6 +993,7 @@ bool Solar::set_heatMetering(const char * value, const int8_t id) {
|
||||
if (!Helpers::value2bool(value, b)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
write_command(0x358, 14, b ? 0xFF : 0x00); // change is broadcasted
|
||||
return true;
|
||||
}
|
||||
@@ -975,11 +1004,13 @@ bool Solar::set_solarEnabled(const char * value, const int8_t id) {
|
||||
if (!Helpers::value2bool(value, b)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (flags() == EMSdevice::EMS_DEVICE_FLAG_SM10) {
|
||||
write_command(0x96, 0, b ? 0xFF : 0x00, 0x96);
|
||||
} else {
|
||||
write_command(0x358, 19, b ? 0xFF : 0x00); // change is broadcasted
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -989,6 +1020,7 @@ bool Solar::set_solarMode(const char * value, const int8_t id) {
|
||||
if (!Helpers::value2enum(value, num, FL_(enum_solarmode))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
write_command(0x35A, 5, num, 0x35A);
|
||||
return true;
|
||||
}
|
||||
@@ -1010,6 +1042,7 @@ bool Solar::set_solarPumpKick(const char * value, const int8_t id) {
|
||||
if (!Helpers::value2bool(value, b)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
write_command(0x35A, 9, b ? 0x01 : 0x00, 0x35A);
|
||||
return true;
|
||||
}
|
||||
@@ -1020,6 +1053,7 @@ bool Solar::set_solarPump2Kick(const char * value, const int8_t id) {
|
||||
if (!Helpers::value2bool(value, b)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
write_command(0x35D, 0, b ? 0x01 : 0x00, 0x35D);
|
||||
return true;
|
||||
}
|
||||
@@ -1030,6 +1064,7 @@ bool Solar::set_plainWaterMode(const char * value, const int8_t id) {
|
||||
if (!Helpers::value2bool(value, b)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
write_command(0x35A, 10, b ? 0x01 : 0x00, 0x35A);
|
||||
return true;
|
||||
}
|
||||
@@ -1040,6 +1075,7 @@ bool Solar::set_doubleMatchFlow(const char * value, const int8_t id) {
|
||||
if (!Helpers::value2bool(value, b)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
write_command(0x35A, 11, b ? 0x01 : 0x00, 0x35A);
|
||||
return true;
|
||||
}
|
||||
@@ -1050,6 +1086,7 @@ bool Solar::set_climateZone(const char * value, const int8_t id) {
|
||||
if (!Helpers::value2number(value, zone)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
write_command(0x380, 0, zone, 0x380);
|
||||
return true;
|
||||
}
|
||||
@@ -1060,6 +1097,7 @@ bool Solar::set_collector1Area(const char * value, const int8_t id) {
|
||||
if (!Helpers::value2float(value, area)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
write_command(0x380, 3, (uint16_t)(area * 10), 0x380);
|
||||
return true;
|
||||
}
|
||||
@@ -1070,6 +1108,7 @@ bool Solar::set_collector2Area(const char * value, const int8_t id) {
|
||||
if (!Helpers::value2float(value, area)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
write_command(0x380, 6, (uint16_t)(area * 10), 0x380);
|
||||
return true;
|
||||
}
|
||||
@@ -1080,6 +1119,7 @@ bool Solar::set_collector1Type(const char * value, const int8_t id) {
|
||||
if (!Helpers::value2enum(value, num, FL_(enum_collectortype))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
write_command(0x380, 5, num + 1, 0x380);
|
||||
return true;
|
||||
}
|
||||
@@ -1090,6 +1130,7 @@ bool Solar::set_collector2Type(const char * value, const int8_t id) {
|
||||
if (!Helpers::value2enum(value, num, FL_(enum_collectortype))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
write_command(0x380, 8, num + 1, 0x380);
|
||||
return true;
|
||||
}
|
||||
@@ -1100,6 +1141,7 @@ bool Solar::set_cylPriority(const char * value, const int8_t id) {
|
||||
if (!Helpers::value2enum(value, num, FL_(enum_cylprio))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
write_command(0x35F, 3, num, 0x35F);
|
||||
return true;
|
||||
}
|
||||
@@ -1109,6 +1151,7 @@ bool Solar::set_heatAssist(const char * value, const int8_t id) {
|
||||
if (!Helpers::value2temperature(value, temperature)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
write_command(0x35C, 0, (uint8_t)(temperature * 10), 0x35C);
|
||||
return true;
|
||||
}
|
||||
@@ -1118,6 +1161,7 @@ bool Solar::set_diffControl(const char * value, const int8_t id) {
|
||||
if (!Helpers::value2temperature(value, temperature)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
write_command(0x361, 4, (uint8_t)(temperature * 10), 0x361);
|
||||
return true;
|
||||
}
|
||||
@@ -1127,6 +1171,7 @@ bool Solar::set_solarHeatAssistOn(const char * value, const int8_t) {
|
||||
if (!Helpers::value2float(value, t)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
write_command(0x35C, 0, (uint8_t)(t * 10), 0x35C);
|
||||
return true;
|
||||
}
|
||||
@@ -1136,6 +1181,7 @@ bool Solar::set_solarHeatAssistOff(const char * value, const int8_t) {
|
||||
if (!Helpers::value2float(value, t)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
write_command(0x35C, 1, (uint8_t)(t * 10), 0x35C);
|
||||
return true;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -32,6 +32,7 @@ class Thermostat : public EMSdevice {
|
||||
: hc_num_(hc_num)
|
||||
, model_(model) {
|
||||
}
|
||||
|
||||
~HeatingCircuit() = default;
|
||||
|
||||
int16_t selTemp;
|
||||
@@ -190,6 +191,7 @@ class Thermostat : public EMSdevice {
|
||||
DhwCircuit(const uint8_t offset)
|
||||
: dhw_num_(offset) {
|
||||
}
|
||||
|
||||
~DhwCircuit() = default;
|
||||
uint8_t wwExtra_;
|
||||
uint8_t wwMode_;
|
||||
@@ -507,6 +509,7 @@ class Thermostat : public EMSdevice {
|
||||
inline bool set_vacation(const char * value, const int8_t id) {
|
||||
return set_holiday(value, id, true);
|
||||
}
|
||||
|
||||
bool set_pause(const char * value, const int8_t id);
|
||||
bool set_party(const char * value, const int8_t id);
|
||||
bool set_holidaymode(const char * value, const int8_t id);
|
||||
@@ -537,78 +540,103 @@ class Thermostat : public EMSdevice {
|
||||
inline bool set_temp(const char * value, const int8_t id) {
|
||||
return set_temperature_value(value, id, HeatingCircuit::Mode::AUTO);
|
||||
}
|
||||
|
||||
inline bool set_nighttemp(const char * value, const int8_t id) {
|
||||
return set_temperature_value(value, id, HeatingCircuit::Mode::NIGHT);
|
||||
}
|
||||
|
||||
inline bool set_daytemp(const char * value, const int8_t id) {
|
||||
return set_temperature_value(value, id, HeatingCircuit::Mode::DAY);
|
||||
}
|
||||
|
||||
inline bool set_daylowtemp(const char * value, const int8_t id) {
|
||||
return set_temperature_value(value, id, HeatingCircuit::Mode::DAYLOW);
|
||||
}
|
||||
|
||||
inline bool set_daymidtemp(const char * value, const int8_t id) {
|
||||
return set_temperature_value(value, id, HeatingCircuit::Mode::DAYMID);
|
||||
}
|
||||
|
||||
inline bool set_comforttemp(const char * value, const int8_t id) {
|
||||
return set_temperature_value(value, id, HeatingCircuit::Mode::COMFORT);
|
||||
}
|
||||
|
||||
inline bool set_nofrosttemp(const char * value, const int8_t id) {
|
||||
return set_temperature_value(value, id, HeatingCircuit::Mode::NOFROST);
|
||||
}
|
||||
|
||||
inline bool set_ecotemp(const char * value, const int8_t id) {
|
||||
return set_temperature_value(value, id, HeatingCircuit::Mode::ECO);
|
||||
}
|
||||
|
||||
inline bool set_heattemp(const char * value, const int8_t id) {
|
||||
return set_temperature_value(value, id, HeatingCircuit::Mode::HEAT);
|
||||
}
|
||||
|
||||
inline bool set_summertemp(const char * value, const int8_t id) {
|
||||
return set_temperature_value(value, id, HeatingCircuit::Mode::SUMMER);
|
||||
}
|
||||
|
||||
inline bool set_designtemp(const char * value, const int8_t id) {
|
||||
return set_temperature_value(value, id, HeatingCircuit::Mode::DESIGN);
|
||||
}
|
||||
|
||||
inline bool set_offsettemp(const char * value, const int8_t id) {
|
||||
return set_temperature_value(value, id, HeatingCircuit::Mode::OFFSET);
|
||||
}
|
||||
|
||||
inline bool set_holidaytemp(const char * value, const int8_t id) {
|
||||
return set_temperature_value(value, id, HeatingCircuit::Mode::HOLIDAY);
|
||||
}
|
||||
|
||||
inline bool set_offtemp(const char * value, const int8_t id) {
|
||||
return set_temperature_value(value, id, HeatingCircuit::Mode::OFF);
|
||||
}
|
||||
|
||||
inline bool set_manualtemp(const char * value, const int8_t id) {
|
||||
return set_temperature_value(value, id, HeatingCircuit::Mode::MANUAL);
|
||||
}
|
||||
|
||||
inline bool set_tempautotemp(const char * value, const int8_t id) {
|
||||
return set_temperature_value(value, id, HeatingCircuit::Mode::TEMPAUTO);
|
||||
}
|
||||
|
||||
inline bool set_noreducetemp(const char * value, const int8_t id) {
|
||||
return set_temperature_value(value, id, HeatingCircuit::Mode::NOREDUCE);
|
||||
}
|
||||
|
||||
inline bool set_reducetemp(const char * value, const int8_t id) {
|
||||
return set_temperature_value(value, id, HeatingCircuit::Mode::REDUCE);
|
||||
}
|
||||
|
||||
inline bool set_vacreducetemp(const char * value, const int8_t id) {
|
||||
return set_temperature_value(value, id, HeatingCircuit::Mode::VACREDUCE);
|
||||
}
|
||||
|
||||
inline bool set_flowtempoffset(const char * value, const int8_t id) {
|
||||
return set_temperature_value(value, id, HeatingCircuit::Mode::FLOWOFFSET, true);
|
||||
}
|
||||
|
||||
inline bool set_maxflowtemp(const char * value, const int8_t id) {
|
||||
return set_temperature_value(value, id, HeatingCircuit::Mode::MAXFLOW);
|
||||
}
|
||||
|
||||
inline bool set_minflowtemp(const char * value, const int8_t id) {
|
||||
return set_temperature_value(value, id, HeatingCircuit::Mode::MINFLOW);
|
||||
}
|
||||
|
||||
inline bool set_baseflowtemp(const char * value, const int8_t id) {
|
||||
return set_temperature_value(value, id, HeatingCircuit::Mode::BASEFLOW);
|
||||
}
|
||||
|
||||
inline bool set_roominfluence(const char * value, const int8_t id) {
|
||||
return set_temperature_value(value, id, HeatingCircuit::Mode::ROOMINFLUENCE, true);
|
||||
}
|
||||
|
||||
inline bool set_cooltemp(const char * value, const int8_t id) {
|
||||
return set_temperature_value(value, id, HeatingCircuit::Mode::COOLTEMP);
|
||||
}
|
||||
|
||||
inline bool set_coolstart(const char * value, const int8_t id) {
|
||||
return set_temperature_value(value, id, HeatingCircuit::Mode::COOLSTART);
|
||||
}
|
||||
@@ -636,31 +664,40 @@ class Thermostat : public EMSdevice {
|
||||
inline bool set_wwVacation(const char * value, const int8_t id) {
|
||||
return set_holiday(value, DeviceValueTAG::TAG_DHW1, true);
|
||||
}
|
||||
|
||||
inline bool set_wwHoliday(const char * value, const int8_t id) {
|
||||
return set_holiday(value, DeviceValueTAG::TAG_DHW1);
|
||||
}
|
||||
|
||||
bool set_RC30Vacation(const char * value, const int8_t id);
|
||||
inline bool set_RC30Vacation1(const char * value, const int8_t id) {
|
||||
return set_RC30Vacation(value, 1);
|
||||
}
|
||||
|
||||
inline bool set_RC30Vacation2(const char * value, const int8_t id) {
|
||||
return set_RC30Vacation(value, 2);
|
||||
}
|
||||
|
||||
inline bool set_RC30Vacation3(const char * value, const int8_t id) {
|
||||
return set_RC30Vacation(value, 3);
|
||||
}
|
||||
|
||||
inline bool set_RC30Vacation4(const char * value, const int8_t id) {
|
||||
return set_RC30Vacation(value, 4);
|
||||
}
|
||||
|
||||
inline bool set_RC30Vacation5(const char * value, const int8_t id) {
|
||||
return set_RC30Vacation(value, 5);
|
||||
}
|
||||
|
||||
inline bool set_RC30Vacation6(const char * value, const int8_t id) {
|
||||
return set_RC30Vacation(value, 6);
|
||||
}
|
||||
|
||||
inline bool set_RC30Vacation7(const char * value, const int8_t id) {
|
||||
return set_RC30Vacation(value, 7);
|
||||
}
|
||||
|
||||
bool set_datetime(const char * value, const int8_t id);
|
||||
bool set_minexttemp(const char * value, const int8_t id);
|
||||
bool set_clockoffset(const char * value, const int8_t id);
|
||||
@@ -710,4 +747,4 @@ class Thermostat : public EMSdevice {
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -97,6 +97,7 @@ bool Ventilation::set_ventMode(const char * value, const int8_t id) {
|
||||
if (!Helpers::value2enum(value, v, FL_(enum_ventMode))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
write_command(0x56B, 0, v - 1, 0x56B);
|
||||
return true;
|
||||
}
|
||||
@@ -106,6 +107,7 @@ bool Ventilation::set_filter(const char * value, const int8_t id) {
|
||||
if (!Helpers::value2number(value, v)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// write_command(0x5xx, 0, v, 0x5xx);
|
||||
return true;
|
||||
}
|
||||
@@ -115,6 +117,7 @@ bool Ventilation::set_bypass(const char * value, const int8_t id) {
|
||||
if (!Helpers::value2bool(value, b)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
write_command(0x55C, 1, b ? 1 : 0, 0x587);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -76,6 +76,7 @@ Water::Water(uint8_t device_type, uint8_t device_id, uint8_t product_id, const c
|
||||
} else if (tag == DeviceValueTAG::TAG_DHW4) {
|
||||
register_telegram_type(0x07C3, "SM100wwCommand", false, MAKE_PF_CB(process_SM100wwCommand)); // command from dhw3->dhw4
|
||||
}
|
||||
|
||||
// device values...
|
||||
register_device_value(tag, &wwTemp_, DeviceValueType::UINT16, DeviceValueNumOp::DV_NUMOP_DIV10, FL_(wwTemp), DeviceValueUOM::DEGREES);
|
||||
register_device_value(tag, &wwTemp2_, DeviceValueType::UINT16, DeviceValueNumOp::DV_NUMOP_DIV10, FL_(wwStorageTemp1), DeviceValueUOM::DEGREES);
|
||||
@@ -133,6 +134,7 @@ void Water::process_SM100wwParam(const std::shared_ptr<const Telegram> & telegra
|
||||
// (daily heating time thermostat 2F5, offset 9, offset 8 on/off)
|
||||
has_update(telegram, errorDisp_, 19);
|
||||
}
|
||||
|
||||
// SM100wwParam2 - 0x07AC, Solar Module(0x2A) -> (0x00)
|
||||
void Water::process_SM100wwParam2(const std::shared_ptr<const Telegram> & telegram) {
|
||||
has_update(telegram, wwDeltaTRet_, 1);
|
||||
@@ -248,6 +250,7 @@ bool Water::set_wwSelTemp(const char * value, const int8_t id) {
|
||||
if (!Helpers::value2temperature(value, temperature)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (flags() == EMSdevice::EMS_DEVICE_FLAG_IPM) {
|
||||
write_command(0x35, 3, (uint8_t)temperature, 0x34);
|
||||
} else if (flags() == EMSdevice::EMS_DEVICE_FLAG_MMPLUS) {
|
||||
@@ -255,6 +258,7 @@ bool Water::set_wwSelTemp(const char * value, const int8_t id) {
|
||||
} else { // SM100
|
||||
write_command(0x7A6, 9, (uint8_t)temperature, 0x7A6);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -263,6 +267,7 @@ bool Water::set_wwMaxTemp(const char * value, const int8_t id) {
|
||||
if (!Helpers::value2temperature(value, temperature)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (flags() == EMSdevice::EMS_DEVICE_FLAG_IPM) {
|
||||
return false;
|
||||
} else if (flags() == EMSdevice::EMS_DEVICE_FLAG_MMPLUS) {
|
||||
@@ -270,6 +275,7 @@ bool Water::set_wwMaxTemp(const char * value, const int8_t id) {
|
||||
} else { // SM100
|
||||
write_command(0x7A6, 8, (uint8_t)temperature, 0x7A6);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -278,6 +284,7 @@ bool Water::set_wwRedTemp(const char * value, const int8_t id) {
|
||||
if (!Helpers::value2temperature(value, temperature)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (flags() == EMSdevice::EMS_DEVICE_FLAG_IPM) {
|
||||
return false;
|
||||
} else if (flags() == EMSdevice::EMS_DEVICE_FLAG_MMPLUS) {
|
||||
@@ -285,6 +292,7 @@ bool Water::set_wwRedTemp(const char * value, const int8_t id) {
|
||||
} else { // SM100
|
||||
write_command(0x7A6, 10, (uint8_t)temperature, 0x7A6);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -293,6 +301,7 @@ bool Water::set_wwHotTemp(const char * value, const int8_t id) {
|
||||
if (!Helpers::value2temperature(value, temperature)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
write_command(0x7A6, 7, (uint8_t)temperature, 0x7A6);
|
||||
return true;
|
||||
}
|
||||
@@ -302,6 +311,7 @@ bool Water::set_wwDailyTemp(const char * value, const int8_t id) {
|
||||
if (!Helpers::value2temperature(value, temperature)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
write_command(0x7A6, 6, (uint8_t)temperature, 0x7A6);
|
||||
return true;
|
||||
}
|
||||
@@ -311,6 +321,7 @@ bool Water::set_wwDisinfectionTemp(const char * value, const int8_t id) {
|
||||
if (!Helpers::value2temperature(value, temperature)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (flags() == EMSdevice::EMS_DEVICE_FLAG_IPM) {
|
||||
write_command(0x33, 8, (uint8_t)temperature, 0x33);
|
||||
} else if (flags() == EMSdevice::EMS_DEVICE_FLAG_MMPLUS) {
|
||||
@@ -318,6 +329,7 @@ bool Water::set_wwDisinfectionTemp(const char * value, const int8_t id) {
|
||||
} else { // SM100
|
||||
write_command(0x7A6, 12, (uint8_t)temperature, 0x7A6);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -326,6 +338,7 @@ bool Water::set_wwCirc(const char * value, const int8_t id) {
|
||||
if (!Helpers::value2bool(value, b)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (flags() == EMSdevice::EMS_DEVICE_FLAG_IPM) {
|
||||
write_command(0x33, 6, b ? 0xFF : 0x00, 0x33);
|
||||
} else if (flags() == EMSdevice::EMS_DEVICE_FLAG_MMPLUS) {
|
||||
@@ -333,6 +346,7 @@ bool Water::set_wwCirc(const char * value, const int8_t id) {
|
||||
} else { // SM100
|
||||
write_command(0x7A5, 0, b ? 0xFF : 0x00, 0x7A5);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -341,6 +355,7 @@ bool Water::set_wwCircMode(const char * value, const int8_t id) {
|
||||
if (!Helpers::value2enum(value, num, FL_(enum_wwCircMode))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (flags() == EMSdevice::EMS_DEVICE_FLAG_IPM) {
|
||||
write_command(0x33, 7, num, 0x33);
|
||||
} else if (flags() == EMSdevice::EMS_DEVICE_FLAG_MMPLUS) {
|
||||
@@ -348,6 +363,7 @@ bool Water::set_wwCircMode(const char * value, const int8_t id) {
|
||||
} else { // SM100
|
||||
write_command(0x7A5, 3, num, 0x7A5);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -357,11 +373,13 @@ bool Water::set_wwCircTc(const char * value, const int8_t id) {
|
||||
if (!Helpers::value2bool(value, b)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (flags() == EMSdevice::EMS_DEVICE_FLAG_MMPLUS) {
|
||||
write_command(0x33B + dhw_, 4, b ? 0x01 : 0x00, 0x33B + dhw_);
|
||||
} else { // SM100
|
||||
write_command(0x7A5, 4, b ? 0xFF : 0x00, 0x7A5);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -370,6 +388,7 @@ bool Water::set_wwKeepWarm(const char * value, const int8_t id) {
|
||||
if (!Helpers::value2bool(value, b)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
write_command(0x7AE, 0, b ? 0xFF : 0x00, 0x7AE);
|
||||
return true;
|
||||
}
|
||||
@@ -379,10 +398,12 @@ bool Water::set_wwDiffTemp(const char * value, const int8_t id) {
|
||||
if (!Helpers::value2temperature(value, v)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (flags() == EMSdevice::EMS_DEVICE_FLAG_MMPLUS) {
|
||||
write_command(0x313 + dhw_, 7, (int8_t)(v * 10), 0x313 + dhw_);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -391,10 +412,12 @@ bool Water::set_wwRequiredTemp(const char * value, const int8_t id) {
|
||||
if (!Helpers::value2temperature(value, v)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (flags() == EMSdevice::EMS_DEVICE_FLAG_MMPLUS) {
|
||||
write_command(0x313 + dhw_, 4, (uint8_t)v, 0x313 + dhw_);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -403,6 +426,7 @@ bool Water::set_wwFlowTempOffset(const char * value, const int8_t id) {
|
||||
if (!Helpers::value2number(value, n)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
write_command(0x33, 5, n, 0x33);
|
||||
return true;
|
||||
}
|
||||
@@ -412,6 +436,7 @@ bool Water::set_wwHystOn(const char * value, const int8_t id) {
|
||||
if (!Helpers::value2number(value, n)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
write_command(0x33, 3, n, 0x33);
|
||||
return true;
|
||||
}
|
||||
@@ -421,6 +446,7 @@ bool Water::set_wwHystOff(const char * value, const int8_t id) {
|
||||
if (!Helpers::value2number(value, n)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
write_command(0x33, 4, n, 0x33);
|
||||
return true;
|
||||
}
|
||||
@@ -430,6 +456,7 @@ bool Water::set_wwRetValve(const char * value, const int8_t id) {
|
||||
if (!Helpers::value2number(value, n)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
write_command(0x7AD, 1, n, 0x7AD);
|
||||
return true;
|
||||
}
|
||||
@@ -439,6 +466,7 @@ bool Water::set_errorDisp(const char * value, const int8_t id) {
|
||||
if (!Helpers::value2enum(value, n, FL_(enum_errorDisp))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
write_command(0x7A6, 19, n, 0x7A6);
|
||||
return true;
|
||||
}
|
||||
@@ -448,6 +476,7 @@ bool Water::set_wwDeltaTRet(const char * value, const int8_t id) {
|
||||
if (!Helpers::value2number(value, n)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
write_command(0x7AC, 1, n, 0x7AC);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -316,6 +316,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
id1 = Helpers::atoint(id1_s.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
if (!id2_s.empty()) {
|
||||
id2 = Helpers::atoint(id2_s.c_str());
|
||||
}
|
||||
@@ -326,6 +327,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
shell.printfln("Usage: test add <device_id> <product_id>");
|
||||
return;
|
||||
}
|
||||
|
||||
shell.printfln("Testing adding a device (deviceID 0x%02X, product_id %d), with all values...", id1, id2);
|
||||
test("add", id1, id2);
|
||||
shell.invoke_command("show devices");
|
||||
@@ -694,6 +696,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
if (json.size() != 0) {
|
||||
serializeJson(doc, Serial);
|
||||
}
|
||||
|
||||
shell.println();
|
||||
Serial.print(COLOR_RESET);
|
||||
|
||||
@@ -715,6 +718,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
Serial.println(" **");
|
||||
}
|
||||
}
|
||||
|
||||
ok = true;
|
||||
}
|
||||
|
||||
@@ -983,6 +987,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
if (id1 == -1) {
|
||||
id1 = 0;
|
||||
}
|
||||
|
||||
shell.printfln("Testing healthcheck with %d", id1);
|
||||
EMSESP::system_.healthcheck(id1);
|
||||
ok = true;
|
||||
@@ -2212,6 +2217,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
if (!pass) {
|
||||
failed++;
|
||||
}
|
||||
|
||||
shell.printfln("%s %-14s -> %-14s expected %-9s got %-9s", pass ? "PASS" : "FAIL", test.settings, test.current, test.expected, actual);
|
||||
}
|
||||
|
||||
@@ -2238,6 +2244,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
snprintf(value, 7, "val%03d", i);
|
||||
doc[key] = value;
|
||||
}
|
||||
|
||||
doc.shrinkToFit();
|
||||
JsonObject jo = doc.as<JsonObject>();
|
||||
shell.printfln("Length of JSON payload = %d", measureJson(jo));
|
||||
@@ -2528,11 +2535,13 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
for (const auto & d : response._data) {
|
||||
shell.printf(" %d", d);
|
||||
}
|
||||
|
||||
if (response._data.size() == 5 && response._data[3] == 0 && response._data[4] == (uint8_t)EMS_VALUE_DEFAULT_INT8_DUMMY) {
|
||||
shell.printf(" [OK]");
|
||||
} else {
|
||||
shell.printf(" [ERROR - invalid response]");
|
||||
}
|
||||
|
||||
shell.println();
|
||||
} else {
|
||||
shell.printf("mintempsilent [MODBUS ERROR %d]\n", response.getError());
|
||||
@@ -2553,6 +2562,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
for (const auto & d : response._data) {
|
||||
shell.printf(" %d", d);
|
||||
}
|
||||
|
||||
shell.println(" [OK]");
|
||||
} else {
|
||||
shell.printf("selflowtemp [MODBUS ERROR %d]\n", response.getError());
|
||||
@@ -2574,6 +2584,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
for (const auto & d : response._data) {
|
||||
shell.printf(" %d", d);
|
||||
}
|
||||
|
||||
shell.println(" [OK]");
|
||||
} else {
|
||||
shell.printf("remotetemp [MODBUS ERROR %d]\n", response.getError());
|
||||
@@ -2691,6 +2702,7 @@ void Test::rx_telegram(const std::vector<uint8_t> & rx_data) {
|
||||
data[i] = rx_data[i];
|
||||
i++;
|
||||
}
|
||||
|
||||
data[i] = EMSESP::rxservice_.calculate_crc(data, i);
|
||||
EMSESP::rxservice_.add(data, len + 1);
|
||||
|
||||
@@ -2706,6 +2718,7 @@ void Test::uart_telegram(const std::vector<uint8_t> & rx_data) {
|
||||
data[i] = rx_data[i];
|
||||
i++;
|
||||
}
|
||||
|
||||
data[i] = EMSESP::rxservice_.calculate_crc(data, i);
|
||||
EMSESP::incoming_telegram(data, i + 1);
|
||||
|
||||
@@ -2719,6 +2732,7 @@ void Test::uart_telegram_withCRC(const char * rx_data) {
|
||||
for (uint8_t i = 0; i < strlen(rx_data); i++) {
|
||||
telegram[i] = rx_data[i];
|
||||
}
|
||||
|
||||
telegram[strlen(rx_data)] = '\0'; // make sure its terminated
|
||||
|
||||
uint8_t count = 0;
|
||||
@@ -2758,6 +2772,7 @@ void Test::uart_telegram(const char * rx_data) {
|
||||
for (uint8_t i = 0; i < strlen(rx_data); i++) {
|
||||
telegram[i] = rx_data[i];
|
||||
}
|
||||
|
||||
telegram[strlen(rx_data)] = '\0'; // make sure its terminated
|
||||
|
||||
uint8_t count = 0;
|
||||
|
||||
@@ -54,6 +54,7 @@ void EMSuart::uart_event_task(void * pvParameters) {
|
||||
if (length == 2 || (length >= 6 && length <= EMS_MAXBUFFERSIZE)) {
|
||||
EMSESP::incoming_telegram(telegram, (uint8_t)(length - 1));
|
||||
}
|
||||
|
||||
length = 0;
|
||||
} else if (event.type == UART_BUFFER_FULL) {
|
||||
uart_flush_input(EMSUART_NUM);
|
||||
@@ -61,6 +62,7 @@ void EMSuart::uart_event_task(void * pvParameters) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
@@ -118,6 +120,7 @@ void EMSuart::start(const uint8_t tx_mode, const uint8_t rx_gpio, const uint8_t
|
||||
gpio_pullup_en((gpio_num_t)rx_gpio);
|
||||
vTaskResume(xHandle);
|
||||
}
|
||||
|
||||
tx_mode_ = tx_mode;
|
||||
uart_enable_intr_mask(EMSUART_NUM, UART_BRK_DET_INT_ENA | UART_RXFIFO_FULL_INT_ENA);
|
||||
}
|
||||
@@ -181,6 +184,7 @@ uint8_t EMSuart::transmit(const uint8_t * buf, const uint8_t len) {
|
||||
uart_write_bytes(EMSUART_NUM, &buf[i], 1);
|
||||
delayMicroseconds(EMSUART_TX_WAIT_PLUS);
|
||||
}
|
||||
|
||||
uart_gen_break(EMSUART_TX_BRK_PLUS);
|
||||
return EMS_TX_STATUS_OK;
|
||||
}
|
||||
@@ -191,6 +195,7 @@ uint8_t EMSuart::transmit(const uint8_t * buf, const uint8_t len) {
|
||||
uart_write_bytes(EMSUART_NUM, &buf[i], 1);
|
||||
delayMicroseconds(EMSUART_TX_WAIT_HT3);
|
||||
}
|
||||
|
||||
uart_gen_break(EMSUART_TX_BRK_HT3);
|
||||
return EMS_TX_STATUS_OK;
|
||||
}
|
||||
@@ -206,6 +211,7 @@ uint8_t EMSuart::transmit(const uint8_t * buf, const uint8_t len) {
|
||||
uart_get_buffered_data_len(EMSUART_NUM, &rx1);
|
||||
} while ((rx1 == rx0) && (--timeoutcnt));
|
||||
}
|
||||
|
||||
uart_gen_break(EMSUART_TX_BRK_EMS);
|
||||
return EMS_TX_STATUS_OK;
|
||||
}
|
||||
|
||||
@@ -44,6 +44,7 @@ void WebAPIService::webAPIService(AsyncWebServerRequest * request, JsonVariant j
|
||||
request->send(429); // too many requests
|
||||
return;
|
||||
}
|
||||
|
||||
busy = true;
|
||||
JsonDocument input_doc; // has no body JSON so create dummy as empty input object
|
||||
JsonObject input;
|
||||
@@ -58,6 +59,7 @@ void WebAPIService::webAPIService(AsyncWebServerRequest * request, JsonVariant j
|
||||
input = input_doc.to<JsonObject>();
|
||||
input["data"] = json.as<std::string>();
|
||||
}
|
||||
|
||||
parse(request, input);
|
||||
busy = false;
|
||||
}
|
||||
@@ -95,21 +97,27 @@ void WebAPIService::parse(AsyncWebServerRequest * request, JsonObject input) {
|
||||
if (request->hasParam(F_(device))) {
|
||||
input["device"] = request->getParam(F_(device))->value().c_str();
|
||||
}
|
||||
|
||||
if (request->hasParam(F_(cmd))) {
|
||||
input["cmd"] = request->getParam(F_(cmd))->value().c_str();
|
||||
}
|
||||
|
||||
if (request->hasParam(F_(data))) {
|
||||
input["data"] = request->getParam(F_(data))->value().c_str();
|
||||
}
|
||||
|
||||
if (request->hasParam(F_(value))) {
|
||||
input["value"] = request->getParam(F_(value))->value().c_str();
|
||||
}
|
||||
|
||||
if (request->hasParam(F_(id))) {
|
||||
input["id"] = Helpers::atoint(request->getParam(F_(id))->value().c_str());
|
||||
}
|
||||
|
||||
if (request->hasParam(F_(hc))) {
|
||||
input["hc"] = Helpers::atoint(request->getParam(F_(hc))->value().c_str());
|
||||
}
|
||||
|
||||
if (request->hasParam(F_(dhw))) {
|
||||
input["dhw"] = Helpers::atoint(request->getParam(F_(dhw))->value().c_str());
|
||||
}
|
||||
@@ -205,6 +213,7 @@ void WebAPIService::storeResponse(JsonObject response) {
|
||||
storeResponseDoc_.clear(); // clear it, so can only recall once
|
||||
storeResponseDoc_.add(response); // add the object to our doc
|
||||
}
|
||||
|
||||
const char * WebAPIService::getResponse() {
|
||||
static std::string buffer;
|
||||
serializeJson(storeResponseDoc_, buffer);
|
||||
|
||||
@@ -59,6 +59,7 @@ void WebActivityService::webActivityService(AsyncWebServerRequest * request) {
|
||||
statJson["q"] =
|
||||
EMSESP::temperaturesensor_.reads() == 0 ? 100 : 100 - (uint8_t)((100 * EMSESP::temperaturesensor_.fails()) / EMSESP::temperaturesensor_.reads());
|
||||
}
|
||||
|
||||
if (EMSESP::analog_enabled()) {
|
||||
statJson = statsJson.add<JsonObject>();
|
||||
statJson["id"] = 4;
|
||||
@@ -66,6 +67,7 @@ void WebActivityService::webActivityService(AsyncWebServerRequest * request) {
|
||||
statJson["f"] = EMSESP::analogsensor_.fails();
|
||||
statJson["q"] = EMSESP::analogsensor_.reads() == 0 ? 100 : 100 - (uint8_t)((100 * EMSESP::analogsensor_.fails()) / EMSESP::analogsensor_.reads());
|
||||
}
|
||||
|
||||
if (Mqtt::enabled()) {
|
||||
statJson = statsJson.add<JsonObject>();
|
||||
statJson["id"] = 5;
|
||||
@@ -100,4 +102,4 @@ void WebActivityService::webActivityService(AsyncWebServerRequest * request) {
|
||||
request->send(response);
|
||||
}
|
||||
|
||||
} // namespace emsesp
|
||||
} // namespace emsesp
|
||||
|
||||
@@ -73,6 +73,7 @@ bool WebCommandService::queueCommand(const char * name, const char * value) {
|
||||
if (job == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
job->name = name ? name : "";
|
||||
job->has_value = (value != nullptr);
|
||||
job->value = value ? value : "";
|
||||
@@ -81,6 +82,7 @@ bool WebCommandService::queueCommand(const char * name, const char * value) {
|
||||
delete job;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
@@ -95,6 +97,7 @@ bool WebCommandService::isUrlCommand(const std::string & command) {
|
||||
if (deserializeJson(doc, command) != DeserializationError::Ok) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string url = doc["url"] | "";
|
||||
auto lower_url = Helpers::toLower(url.c_str());
|
||||
return lower_url.starts_with("http://") || lower_url.starts_with("https://");
|
||||
@@ -123,6 +126,7 @@ bool WebCommandService::valueContainsUrl(const std::string & value) {
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
JsonDocument doc;
|
||||
if (deserializeJson(doc, value.substr(f, e - f)) == DeserializationError::Ok) {
|
||||
for (JsonPairConst p : doc.as<JsonObjectConst>()) {
|
||||
@@ -131,8 +135,10 @@ bool WebCommandService::valueContainsUrl(const std::string & value) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
f = value.find_first_of('{', e);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -149,6 +155,7 @@ bool WebCommandService::dispatchCommand(const char * name, const char * value) {
|
||||
if (isUrlCommand(ci->cmd.c_str())) {
|
||||
return queueCommand(name, value);
|
||||
}
|
||||
|
||||
// internal command whose value embeds a {url} fetch (e.g. system/message) - the value is
|
||||
// resolved by compute() at execution time and would block, so offload it to the worker task
|
||||
// the effective value is the override if given, else the command's stored default
|
||||
@@ -209,6 +216,7 @@ StateUpdateResult WebCommands::update(JsonObject root, WebCommands & webCommands
|
||||
FL_(command_cmd),
|
||||
CommandFlag::ADMIN_ONLY);
|
||||
}
|
||||
|
||||
return StateUpdateResult::CHANGED;
|
||||
}
|
||||
|
||||
@@ -217,12 +225,14 @@ const CommandItem * WebCommandService::find(const char * name) {
|
||||
if (name == nullptr || name[0] == '\0') {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto lower_name = Helpers::toLower(name);
|
||||
for (const CommandItem & ci : *commandItems_) {
|
||||
if (ci.name[0] != '\0' && Helpers::toLower(ci.name) == lower_name) {
|
||||
return &ci;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -233,6 +243,7 @@ bool WebCommandService::executeCommand(const char * name, const char * value) {
|
||||
EMSESP::logger().warning("Command '%s' not found", name ? name : "");
|
||||
return false;
|
||||
}
|
||||
|
||||
// if there is a value use it, otherwise use the command's default value
|
||||
std::string cmd_value = value ? value : ci->value.c_str();
|
||||
return executeCommand(ci->name, std::string(ci->cmd.c_str()), cmd_value);
|
||||
@@ -268,6 +279,7 @@ bool WebCommandService::executeCommand(const char * name, const std::string & co
|
||||
commands(s, false);
|
||||
url.replace(q + 1, l, s);
|
||||
}
|
||||
|
||||
// the cmd's embedded value only gets entity substitution (commands), the passed value is fully computed
|
||||
std::string value = doc["value"] | computed_data;
|
||||
std::string method = doc["method"] | "GET";
|
||||
@@ -308,6 +320,7 @@ bool WebCommandService::executeCommand(const char * name, const std::string & co
|
||||
if (data.empty() && output.size()) {
|
||||
Mqtt::queue_publish("response", output);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -317,6 +330,7 @@ bool WebCommandService::executeCommand(const char * name, const std::string & co
|
||||
} else {
|
||||
snprintf(error, sizeof(error), "Command '%s': %s failed with error %s", name, cmd.c_str(), Command::return_code_string(return_code));
|
||||
}
|
||||
|
||||
EMSESP::logger().warning(error);
|
||||
return false;
|
||||
}
|
||||
@@ -332,6 +346,7 @@ bool WebCommandService::get_value_info(JsonObject output, const char * cmd) {
|
||||
output[(const char *)ci.name] = ci.value.c_str();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -341,6 +356,7 @@ bool WebCommandService::get_value_info(JsonObject output, const char * cmd) {
|
||||
output[(const char *)ci.name] = std::string(ci.cmd + ": " + ci.value);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -350,6 +366,7 @@ bool WebCommandService::get_value_info(JsonObject output, const char * cmd) {
|
||||
get_value_json(output[ci.name].to<JsonObject>(), ci);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -359,6 +376,7 @@ bool WebCommandService::get_value_info(JsonObject output, const char * cmd) {
|
||||
output["api_data"] = metrics;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -381,10 +399,12 @@ std::string WebCommandService::get_metrics_prometheus() {
|
||||
if (ci.name[0] == '\0') {
|
||||
continue;
|
||||
}
|
||||
|
||||
result += (std::string) "# HELP emsesp_cmd_" + ci.name + " " + ci.name + ", readable, writable, visible\n";
|
||||
result += (std::string) "# TYPE emsesp_cmd_" + ci.name + " gauge\n";
|
||||
result += (std::string) "emsesp_cmd_" + ci.name + " 1\n";
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -403,6 +423,7 @@ void WebCommandService::publish(const bool force) {
|
||||
if (!Mqtt::enabled() || commandItems_->empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (force && !EMSESP::mqtt_.get_publish_onchange(EMSdevice::DeviceType::SYSTEM)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -74,6 +74,7 @@ StateUpdateResult WebCustomEntity::update(JsonObject root, WebCustomEntity & web
|
||||
if (entityItem.raw) {
|
||||
delete[] entityItem.raw;
|
||||
}
|
||||
|
||||
if (entityItem.ram == 2) { // NVS
|
||||
char key[sizeof(entityItem.name) + 2];
|
||||
snprintf(key, sizeof(key), "c:%s", entityItem.name);
|
||||
@@ -81,10 +82,12 @@ StateUpdateResult WebCustomEntity::update(JsonObject root, WebCustomEntity & web
|
||||
EMSESP::nvs_.remove(key);
|
||||
}
|
||||
}
|
||||
|
||||
if (entityItem.ram) { // save name/value pairs for change checking
|
||||
doc[entityItem.name] = entityItem.value;
|
||||
}
|
||||
}
|
||||
|
||||
webCustomEntity.customEntityItems.clear();
|
||||
EMSESP::webCustomEntityService.ha_reset();
|
||||
|
||||
@@ -110,6 +113,7 @@ StateUpdateResult WebCustomEntity::update(JsonObject root, WebCustomEntity & web
|
||||
entityItem.value_type = DeviceValueType::STRING;
|
||||
entityItem.writeable = true;
|
||||
}
|
||||
|
||||
entityItem.raw = nullptr;
|
||||
if (entityItem.ram == 0 && entityItem.value_type == DeviceValueType::STRING) {
|
||||
entityItem.raw = new uint8_t[(size_t)entityItem.factor + 1];
|
||||
@@ -134,6 +138,7 @@ StateUpdateResult WebCustomEntity::update(JsonObject root, WebCustomEntity & web
|
||||
if (entityItem.factor == 0) {
|
||||
entityItem.factor = 1;
|
||||
}
|
||||
|
||||
if (entityItem.ram == 2) { // NVS
|
||||
char key[sizeof(entityItem.name) + 2];
|
||||
snprintf(key, sizeof(key), "c:%s", entityItem.name);
|
||||
@@ -141,6 +146,7 @@ StateUpdateResult WebCustomEntity::update(JsonObject root, WebCustomEntity & web
|
||||
entityItem.data = EMSESP::nvs_.getString(key).c_str();
|
||||
}
|
||||
}
|
||||
|
||||
webCustomEntity.customEntityItems.push_back(entityItem); // add to list
|
||||
|
||||
if (entityItem.writeable && entityItem.name[0] != '\0') {
|
||||
@@ -161,6 +167,7 @@ StateUpdateResult WebCustomEntity::update(JsonObject root, WebCustomEntity & web
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return StateUpdateResult::CHANGED;
|
||||
}
|
||||
|
||||
@@ -179,6 +186,7 @@ bool WebCustomEntityService::command_setvalue(const char * value, const int8_t i
|
||||
if (entityItem.data == value) {
|
||||
return true; // no change
|
||||
}
|
||||
|
||||
if (entityItem.ram == 1) {
|
||||
entityItem.data = value;
|
||||
} else if (entityItem.ram == 2) { // NVS
|
||||
@@ -198,6 +206,7 @@ bool WebCustomEntityService::command_setvalue(const char * value, const int8_t i
|
||||
data[count++] = (uint8_t)strtol(p, 0, 16);
|
||||
p = strtok(nullptr, " ,");
|
||||
}
|
||||
|
||||
free(telegram);
|
||||
uint8_t offset = entityItem.offset;
|
||||
uint8_t * dat = data;
|
||||
@@ -208,6 +217,7 @@ bool WebCustomEntityService::command_setvalue(const char * value, const int8_t i
|
||||
count -= len;
|
||||
dat += len;
|
||||
}
|
||||
|
||||
delete[] data;
|
||||
// validate telegram
|
||||
EMSESP::send_read_request(entityItem.type_id, entityItem.device_id, entityItem.offset, length);
|
||||
@@ -217,12 +227,14 @@ bool WebCustomEntityService::command_setvalue(const char * value, const int8_t i
|
||||
if (!Helpers::value2bool(value, v)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
EMSESP::send_write_request(entityItem.type_id, entityItem.device_id, entityItem.offset, v ? (uint8_t)entityItem.factor : 0, entityItem.type_id);
|
||||
} else {
|
||||
float f;
|
||||
if (!Helpers::value2float(value, f)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int v = (f / entityItem.factor + 0.5);
|
||||
if (entityItem.value_type == DeviceValueType::UINT8 || entityItem.value_type == DeviceValueType::INT8) {
|
||||
EMSESP::send_write_request(entityItem.type_id, entityItem.device_id, entityItem.offset, v, entityItem.type_id);
|
||||
@@ -239,12 +251,14 @@ bool WebCustomEntityService::command_setvalue(const char * value, const int8_t i
|
||||
if (EMSESP::mqtt_.get_publish_onchange(EMSdevice::DeviceType::SYSTEM)) {
|
||||
publish();
|
||||
}
|
||||
|
||||
char cmd[COMMAND_MAX_LENGTH];
|
||||
snprintf(cmd, sizeof(cmd), "%s/%s", F_(custom), entityItem.name);
|
||||
EMSESP::webSchedulerService.onChange(cmd);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -263,30 +277,35 @@ void WebCustomEntityService::render_value(JsonObject output, CustomEntityItem co
|
||||
Mqtt::add_value_bool(output, name, (uint8_t)entity.value != 0);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
case DeviceValueType::INT8:
|
||||
if ((int8_t)entity.value != EMS_VALUE_INT8_NOTSET) {
|
||||
std::string v = Helpers::render_value(payload, entity.factor * (int8_t)entity.value, 2);
|
||||
output[name] = add_uom ? serialized(v + ' ' + EMSdevice::uom_to_string(entity.uom)) : serialized(v);
|
||||
}
|
||||
|
||||
break;
|
||||
case DeviceValueType::UINT8:
|
||||
if ((uint8_t)entity.value != EMS_VALUE_UINT8_NOTSET) {
|
||||
std::string v = Helpers::render_value(payload, entity.factor * (int8_t)entity.value, 2);
|
||||
output[name] = add_uom ? serialized(v + ' ' + EMSdevice::uom_to_string(entity.uom)) : serialized(v);
|
||||
}
|
||||
|
||||
break;
|
||||
case DeviceValueType::INT16:
|
||||
if ((int16_t)entity.value != EMS_VALUE_INT16_NOTSET) {
|
||||
std::string v = Helpers::render_value(payload, entity.factor * (int16_t)entity.value, 2);
|
||||
output[name] = add_uom ? serialized(v + ' ' + EMSdevice::uom_to_string(entity.uom)) : serialized(v);
|
||||
}
|
||||
|
||||
break;
|
||||
case DeviceValueType::UINT16:
|
||||
if ((uint16_t)entity.value != EMS_VALUE_UINT16_NOTSET) {
|
||||
std::string v = Helpers::render_value(payload, entity.factor * (uint16_t)entity.value, 2);
|
||||
output[name] = add_uom ? serialized(v + ' ' + EMSdevice::uom_to_string(entity.uom)) : serialized(v);
|
||||
}
|
||||
|
||||
break;
|
||||
case DeviceValueType::UINT24:
|
||||
case DeviceValueType::TIME:
|
||||
@@ -295,6 +314,7 @@ void WebCustomEntityService::render_value(JsonObject output, CustomEntityItem co
|
||||
std::string v = Helpers::render_value(payload, entity.factor * entity.value, 2);
|
||||
output[name] = add_uom ? serialized(v + ' ' + EMSdevice::uom_to_string(entity.uom)) : serialized(v);
|
||||
}
|
||||
|
||||
break;
|
||||
// case DeviceValueType::STRING:
|
||||
default:
|
||||
@@ -302,6 +322,7 @@ void WebCustomEntityService::render_value(JsonObject output, CustomEntityItem co
|
||||
if (entity.data.length() > 0) {
|
||||
output[name] = add_uom ? entity.data + ' ' + EMSdevice::uom_to_string(entity.uom) : entity.data;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -335,6 +356,7 @@ bool WebCustomEntityService::get_value_info(JsonObject output, const char * cmd)
|
||||
render_value(output, entity);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -344,6 +366,7 @@ bool WebCustomEntityService::get_value_info(JsonObject output, const char * cmd)
|
||||
auto nest = output[entity.name].to<JsonObject>();
|
||||
get_value_json(nest, entity);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -353,6 +376,7 @@ bool WebCustomEntityService::get_value_info(JsonObject output, const char * cmd)
|
||||
output["api_data"] = metrics;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -364,6 +388,7 @@ bool WebCustomEntityService::get_value_info(JsonObject output, const char * cmd)
|
||||
return Command::get_attribute(output, cmd, attribute_s);
|
||||
}
|
||||
}
|
||||
|
||||
return false; // not found
|
||||
}
|
||||
|
||||
@@ -376,10 +401,12 @@ std::string WebCustomEntityService::get_metrics_prometheus() {
|
||||
if (entity.hide || entity.name[0] == '\0') {
|
||||
continue;
|
||||
}
|
||||
|
||||
result += (std::string) "# HELP emsesp_" + entity.name + " " + entity.name;
|
||||
if (entity.uom != 0) {
|
||||
result += (std::string) ", " + EMSdevice::uom_to_string(entity.uom);
|
||||
}
|
||||
|
||||
result += (std::string) ", readable, visible" + (entity.writeable ? ", writable\n" : "\n");
|
||||
result += (std::string) "# TYPE emsesp_" + entity.name + " gauge\n";
|
||||
result += (std::string) "emsesp_" + entity.name + " ";
|
||||
@@ -408,10 +435,13 @@ std::string WebCustomEntityService::get_metrics_prometheus() {
|
||||
if (entity.data.length() > 0) {
|
||||
result += entity.data;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
result += (std::string) "\n";
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -466,11 +496,13 @@ void WebCustomEntityService::publish(const bool force) {
|
||||
if (!Mqtt::enabled() || customEntityItems_->empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (force) {
|
||||
if (Mqtt::publish_single()) {
|
||||
for (CustomEntityItem & entityItem : *customEntityItems_) {
|
||||
publish_single(entityItem);
|
||||
}
|
||||
|
||||
return;
|
||||
} else if (!EMSESP::mqtt_.get_publish_onchange(EMSdevice::DeviceType::SYSTEM)) {
|
||||
return; // wait for first time period
|
||||
@@ -485,6 +517,7 @@ void WebCustomEntityService::publish(const bool force) {
|
||||
if (entityItem.hide) {
|
||||
continue;
|
||||
}
|
||||
|
||||
render_value(output, entityItem);
|
||||
// create HA config
|
||||
if (Mqtt::ha_enabled() && !ha_configdone_) {
|
||||
@@ -524,6 +557,7 @@ void WebCustomEntityService::publish(const bool force) {
|
||||
} else {
|
||||
snprintf(topic, sizeof(topic), "sensor/%s/%s_%s/config", Mqtt::basename().c_str(), F_(custom), entityItem.name);
|
||||
}
|
||||
|
||||
char command_topic[Mqtt::MQTT_TOPIC_MAX_SIZE];
|
||||
snprintf(command_topic, sizeof(command_topic), "~/%s/%s", F_(custom), entityItem.name);
|
||||
config["cmd_t"] = command_topic;
|
||||
@@ -559,6 +593,7 @@ void WebCustomEntityService::publish(const bool force) {
|
||||
snprintf(topic, sizeof(topic), "%s_data", F_(custom));
|
||||
Mqtt::queue_publish(topic, output);
|
||||
}
|
||||
|
||||
// EMSESP::logger().debug("publish %d custom entities", output.size());
|
||||
}
|
||||
|
||||
@@ -591,6 +626,7 @@ void WebCustomEntityService::generate_value_web(JsonObject output, const bool is
|
||||
if (entity.hide) {
|
||||
continue;
|
||||
}
|
||||
|
||||
bool include = false;
|
||||
JsonObject root_obj = nodes.add<JsonObject>(); // create the object, we know there is a value
|
||||
|
||||
@@ -627,29 +663,34 @@ void WebCustomEntityService::generate_value_web(JsonObject output, const bool is
|
||||
l.add(Helpers::render_boolean(s, true, true));
|
||||
break;
|
||||
}
|
||||
|
||||
case DeviceValueType::INT8:
|
||||
if ((int8_t)entity.value != EMS_VALUE_INT8_NOTSET) {
|
||||
obj["v"] = Helpers::transformNumFloat(entity.factor * (int8_t)entity.value);
|
||||
include = true;
|
||||
}
|
||||
|
||||
break;
|
||||
case DeviceValueType::UINT8:
|
||||
if ((uint8_t)entity.value != EMS_VALUE_UINT8_NOTSET) {
|
||||
obj["v"] = Helpers::transformNumFloat(entity.factor * (uint8_t)entity.value);
|
||||
include = true;
|
||||
}
|
||||
|
||||
break;
|
||||
case DeviceValueType::INT16:
|
||||
if ((int16_t)entity.value != EMS_VALUE_INT16_NOTSET) {
|
||||
obj["v"] = Helpers::transformNumFloat(entity.factor * (int16_t)entity.value);
|
||||
include = true;
|
||||
}
|
||||
|
||||
break;
|
||||
case DeviceValueType::UINT16:
|
||||
if ((uint16_t)entity.value != EMS_VALUE_UINT16_NOTSET) {
|
||||
obj["v"] = Helpers::transformNumFloat(entity.factor * (uint16_t)entity.value);
|
||||
include = true;
|
||||
}
|
||||
|
||||
break;
|
||||
case DeviceValueType::UINT24:
|
||||
case DeviceValueType::TIME:
|
||||
@@ -658,12 +699,14 @@ void WebCustomEntityService::generate_value_web(JsonObject output, const bool is
|
||||
obj["v"] = Helpers::transformNumFloat(entity.factor * entity.value);
|
||||
include = true;
|
||||
}
|
||||
|
||||
break;
|
||||
case DeviceValueType::STRING:
|
||||
if (entity.data.length() > 0) {
|
||||
obj["v"] = entity.data;
|
||||
include = true;
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@@ -696,6 +739,7 @@ void WebCustomEntityService::fetch() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (needFetch) {
|
||||
EMSESP::send_read_request(entity.type_id,
|
||||
entity.device_id,
|
||||
@@ -722,6 +766,7 @@ bool WebCustomEntityService::get_value(const std::shared_ptr<const Telegram> & t
|
||||
message_length -= entity.offset - telegram->offset;
|
||||
offset = 0;
|
||||
}
|
||||
|
||||
auto length = std::min(offset + message_length, (int)entity.factor);
|
||||
auto rest = std::min((int)entity.factor - offset, (int)message_length);
|
||||
if (rest > 0) {
|
||||
@@ -734,6 +779,7 @@ bool WebCustomEntityService::get_value(const std::shared_ptr<const Telegram> & t
|
||||
} else if (EMSESP::mqtt_.get_publish_onchange(EMSdevice::DeviceType::SYSTEM)) {
|
||||
has_change = true;
|
||||
}
|
||||
|
||||
char cmd[COMMAND_MAX_LENGTH];
|
||||
snprintf(cmd, sizeof(cmd), "%s/%s", F_(custom), entity.name);
|
||||
EMSESP::webSchedulerService.onChange(cmd);
|
||||
@@ -745,10 +791,12 @@ bool WebCustomEntityService::get_value(const std::shared_ptr<const Telegram> & t
|
||||
for (uint8_t i = 0; i < len[entity.value_type]; i++) {
|
||||
value = (value << 8) + telegram->message_data[i + entity.offset - telegram->offset];
|
||||
}
|
||||
|
||||
// mask bits for bool values
|
||||
if (entity.value_type == DeviceValueType::BOOL && entity.factor > 0) {
|
||||
value = (value & (uint8_t)entity.factor) ? 1 : 0;
|
||||
}
|
||||
|
||||
if (value != entity.value) {
|
||||
entity.value = value;
|
||||
if (Mqtt::publish_single()) {
|
||||
@@ -756,10 +804,12 @@ bool WebCustomEntityService::get_value(const std::shared_ptr<const Telegram> & t
|
||||
} else if (EMSESP::mqtt_.get_publish_onchange(EMSdevice::DeviceType::SYSTEM)) {
|
||||
has_change = true;
|
||||
}
|
||||
|
||||
char cmd[COMMAND_MAX_LENGTH];
|
||||
snprintf(cmd, sizeof(cmd), "%s/%s", F_(custom), entity.name);
|
||||
EMSESP::webSchedulerService.onChange(cmd);
|
||||
}
|
||||
|
||||
// EMSESP::logger().debug("custom entity %s received with value %d", entity.name, (int)entity.val);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -110,6 +110,7 @@ StateUpdateResult WebCustomization::update(JsonObject root, WebCustomization & c
|
||||
*p = '_';
|
||||
}
|
||||
}
|
||||
|
||||
customizations.sensorCustomizations.push_back(sensor); // add to list
|
||||
}
|
||||
}
|
||||
@@ -126,6 +127,7 @@ StateUpdateResult WebCustomization::update(JsonObject root, WebCustomization & c
|
||||
EMSESP::logger().warning("Analog sensor: Invalid GPIO %d for %s. Skipping.", analogJson["gpio"].as<uint8_t>(), analog_sensor_name);
|
||||
continue;
|
||||
}
|
||||
|
||||
auto analog = AnalogCustomization();
|
||||
strlcpy(analog.name, analogJson["name"].as<const char *>(), sizeof(analog.name));
|
||||
analog.gpio = analogJson["gpio"];
|
||||
@@ -137,6 +139,7 @@ StateUpdateResult WebCustomization::update(JsonObject root, WebCustomization & c
|
||||
if (_start && analog.type == EMSESP::analogsensor_.AnalogType::DIGITAL_OUT && analog.uom > DeviceValue::DeviceValueUOM::NONE) {
|
||||
analog.offset = analog.uom > 1 ? 1 : 0;
|
||||
}
|
||||
|
||||
customizations.analogCustomizations.push_back(analog); // add to list
|
||||
}
|
||||
}
|
||||
@@ -332,11 +335,13 @@ void WebCustomizationService::customization_entities(AsyncWebServerRequest * req
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_set) {
|
||||
entity_ids.push_back(entity_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -499,6 +504,7 @@ void WebCustomizationService::load_test_data() {
|
||||
dv.state = DeviceValueState::DV_FAVORITE; // set as favorite
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,6 +55,7 @@ class AnalogCustomization {
|
||||
bool operator==(const AnalogCustomization & a) const {
|
||||
return gpio == a.gpio;
|
||||
}
|
||||
|
||||
bool operator!=(const AnalogCustomization & a) const {
|
||||
return !operator==(a);
|
||||
}
|
||||
@@ -111,4 +112,4 @@ class WebCustomizationService : public StatefulService<WebCustomization> {
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -115,15 +115,18 @@ void WebDataService::sensor_data(AsyncWebServerRequest * request) {
|
||||
if (Helpers::hasValue(sensor.temperature_c)) {
|
||||
obj["t"] = (float)sensor.temperature_c * 0.18 + 32;
|
||||
}
|
||||
|
||||
obj["u"] = DeviceValueUOM::FAHRENHEIT;
|
||||
obj["o"] = (float)sensor.offset() * 0.18;
|
||||
} else {
|
||||
if (Helpers::hasValue(sensor.temperature_c)) {
|
||||
obj["t"] = (float)sensor.temperature_c / 10;
|
||||
}
|
||||
|
||||
obj["u"] = DeviceValueUOM::DEGREES;
|
||||
obj["o"] = (float)(sensor.offset()) / 10;
|
||||
}
|
||||
|
||||
obj["s"] = sensor.is_system();
|
||||
}
|
||||
}
|
||||
@@ -188,6 +191,7 @@ void WebDataService::device_data(AsyncWebServerRequest * request) {
|
||||
for (uint16_t i = 0; i < (TxService::POST_SEND_DELAY + 500) && EMSESP::wait_validate(); i++) {
|
||||
delay(1);
|
||||
}
|
||||
|
||||
EMSESP::wait_validate(0); // reset in case of timeout
|
||||
#ifndef EMSESP_STANDALONE
|
||||
JsonObject output = response->getRoot();
|
||||
@@ -263,14 +267,17 @@ void WebDataService::write_device_value(AsyncWebServerRequest * request, JsonVar
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (device_type == EMSdevice::DeviceType::UNKNOWN) {
|
||||
EMSESP::logger().warning("Write command failed, bad device id: %d", unique_id);
|
||||
AsyncWebServerResponse * response = request->beginResponse(400); // bad request
|
||||
request->send(response);
|
||||
return;
|
||||
}
|
||||
|
||||
// create JSON for output
|
||||
auto * response = new PsramAsyncJsonResponse(false);
|
||||
JsonObject output = response->getRoot();
|
||||
@@ -282,6 +289,7 @@ void WebDataService::write_device_value(AsyncWebServerRequest * request, JsonVar
|
||||
if (device_type >= EMSdevice::DeviceType::BOILER) {
|
||||
cmd = Command::parse_command_string(cmd, id); // extract hc or dhw
|
||||
}
|
||||
|
||||
if (data.is<const char *>()) {
|
||||
return_code = Command::call(device_type, cmd, data.as<const char *>(), true, id, output);
|
||||
} else if (data.is<int>()) {
|
||||
@@ -413,6 +421,7 @@ void WebDataService::dashboard_data(AsyncWebServerRequest * request) {
|
||||
if (sensor.is_system()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
JsonObject node = nodes.add<JsonObject>();
|
||||
node["id"] = (EMSdevice::DeviceTypeUniqueID::TEMPERATURESENSOR_UID * 100) + count++;
|
||||
|
||||
@@ -422,11 +431,13 @@ void WebDataService::dashboard_data(AsyncWebServerRequest * request) {
|
||||
if (Helpers::hasValue(sensor.temperature_c)) {
|
||||
dv["v"] = (float)sensor.temperature_c * 0.18 + 32;
|
||||
}
|
||||
|
||||
dv["u"] = DeviceValueUOM::FAHRENHEIT;
|
||||
} else {
|
||||
if (Helpers::hasValue(sensor.temperature_c)) {
|
||||
dv["v"] = (float)sensor.temperature_c / 10;
|
||||
}
|
||||
|
||||
dv["u"] = DeviceValueUOM::DEGREES;
|
||||
}
|
||||
}
|
||||
@@ -444,6 +455,7 @@ void WebDataService::dashboard_data(AsyncWebServerRequest * request) {
|
||||
if (sensor.is_system()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
JsonObject node = nodes.add<JsonObject>();
|
||||
node["id"] = (EMSdevice::DeviceTypeUniqueID::ANALOGSENSOR_UID * 100) + count++;
|
||||
|
||||
@@ -469,6 +481,7 @@ void WebDataService::dashboard_data(AsyncWebServerRequest * request) {
|
||||
dv["v"] = Helpers::transformNumFloat(sensor.value());
|
||||
dv["u"] = sensor.uom();
|
||||
}
|
||||
|
||||
if (sensor.type() == AnalogSensor::AnalogType::COUNTER
|
||||
|| (sensor.type() >= AnalogSensor::AnalogType::DIGITAL_OUT && sensor.type() <= AnalogSensor::AnalogType::PWM_2)
|
||||
|| sensor.type() == AnalogSensor::AnalogType::RGB || sensor.type() == AnalogSensor::AnalogType::PULSE
|
||||
@@ -535,4 +548,4 @@ void WebDataService::dashboard_data(AsyncWebServerRequest * request) {
|
||||
request->send(response);
|
||||
}
|
||||
|
||||
} // namespace emsesp
|
||||
} // namespace emsesp
|
||||
|
||||
@@ -48,6 +48,7 @@ void WebLogService::start() {
|
||||
if (settings.weblog_buffer) {
|
||||
maximum_log_messages_ = settings.weblog_buffer;
|
||||
}
|
||||
|
||||
limit_log_messages_ = maximum_log_messages_;
|
||||
compact_ = settings.weblog_compact;
|
||||
level_ = (uuid::log::Level)settings.weblog_level;
|
||||
@@ -166,6 +167,7 @@ char * WebLogService::messagetime(char * out, const uint64_t t, const size_t buf
|
||||
strftime(timestr, 25, "%FT%T", localtime(&t1));
|
||||
snprintf(out, bufsize, "%s.%03d", timestr, (int)(wall_ms % 1000));
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
@@ -188,6 +190,7 @@ void WebLogService::transmit(const QueuedLogMessage & message) {
|
||||
if (scratch_buf_.capacity() < len + 1) {
|
||||
scratch_buf_.reserve(len + 1);
|
||||
}
|
||||
|
||||
scratch_buf_.resize(len);
|
||||
serializeJson(jsonDocument, scratch_buf_.data(), len + 1);
|
||||
events_.send(scratch_buf_.c_str(), "message", message.id_);
|
||||
@@ -223,6 +226,7 @@ void WebLogService::getSetValues(AsyncWebServerRequest * request, JsonVariant js
|
||||
if (max_messages) {
|
||||
maximum_log_messages_ = max_messages;
|
||||
}
|
||||
|
||||
compact_ = json["compact"];
|
||||
|
||||
uuid::log::Logger::register_handler(this, level_);
|
||||
@@ -247,4 +251,4 @@ void WebLogService::getSetValues(AsyncWebServerRequest * request, JsonVariant js
|
||||
request->send(200); // OK
|
||||
}
|
||||
|
||||
} // namespace emsesp
|
||||
} // namespace emsesp
|
||||
|
||||
@@ -71,6 +71,7 @@ StateUpdateResult WebScheduler::update(JsonObject root, WebScheduler & webSchedu
|
||||
EMSESP::nvs_.remove(key);
|
||||
}
|
||||
}
|
||||
|
||||
webScheduler.scheduleItems.clear();
|
||||
EMSESP::webSchedulerService.ha_reset();
|
||||
|
||||
@@ -95,6 +96,7 @@ StateUpdateResult WebScheduler::update(JsonObject root, WebScheduler & webSchedu
|
||||
if (EMSESP::nvs_.isKey(key)) {
|
||||
webScheduler.scheduleItems.back().active = EMSESP::nvs_.getBool(key);
|
||||
}
|
||||
|
||||
Command::add(
|
||||
EMSdevice::DeviceType::SCHEDULER,
|
||||
webScheduler.scheduleItems.back().name,
|
||||
@@ -104,6 +106,7 @@ StateUpdateResult WebScheduler::update(JsonObject root, WebScheduler & webSchedu
|
||||
FL_(schedule_cmd),
|
||||
CommandFlag::ADMIN_ONLY);
|
||||
}
|
||||
|
||||
return StateUpdateResult::CHANGED;
|
||||
}
|
||||
|
||||
@@ -134,6 +137,7 @@ bool WebSchedulerService::command_setvalue(const char * value, const int8_t id,
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -150,6 +154,7 @@ bool WebSchedulerService::get_value_info(JsonObject output, const char * cmd) {
|
||||
Mqtt::add_value_bool(output, scheduleItem.name, scheduleItem.active);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -160,6 +165,7 @@ bool WebSchedulerService::get_value_info(JsonObject output, const char * cmd) {
|
||||
strlcpy(name, scheduleItem.name[0] == '\0' ? Helpers::smallitoa(name, i++) : scheduleItem.name, sizeof(name));
|
||||
get_value_json(output[name].to<JsonObject>(), scheduleItem);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -169,6 +175,7 @@ bool WebSchedulerService::get_value_info(JsonObject output, const char * cmd) {
|
||||
output["api_data"] = metrics;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -191,10 +198,12 @@ std::string WebSchedulerService::get_metrics_prometheus() {
|
||||
if (scheduleItem.name[0] == '\0') {
|
||||
continue;
|
||||
}
|
||||
|
||||
result += (std::string) "# HELP emsesp_" + scheduleItem.name + " " + scheduleItem.name + ", boolean, readable, visible, writable\n";
|
||||
result += (std::string) "# TYPE emsesp_" + scheduleItem.name + " gauge\n";
|
||||
result += (std::string) "emsesp_" + scheduleItem.name + " " + (scheduleItem.active ? "1\n" : "0\n");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -213,6 +222,7 @@ void WebSchedulerService::get_value_json(JsonObject output, const ScheduleItem &
|
||||
} else {
|
||||
output["time"] = scheduleItem.time;
|
||||
}
|
||||
|
||||
output["cmd_name"] = scheduleItem.cmd_name;
|
||||
output["readable"] = true;
|
||||
output["writeable"] = true;
|
||||
@@ -241,11 +251,13 @@ void WebSchedulerService::publish(const bool force) {
|
||||
if (!Mqtt::enabled() || scheduleItems_->empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (force) {
|
||||
if (Mqtt::publish_single()) {
|
||||
for (const ScheduleItem & scheduleItem : *scheduleItems_) {
|
||||
publish_single(scheduleItem.name, scheduleItem.active);
|
||||
}
|
||||
|
||||
return;
|
||||
} else if (!EMSESP::mqtt_.get_publish_onchange(EMSdevice::DeviceType::SYSTEM)) {
|
||||
return; // wait for first time period
|
||||
@@ -279,6 +291,7 @@ void WebSchedulerService::publish(const bool force) {
|
||||
} else {
|
||||
snprintf(val_tpl, sizeof(val_tpl), "{{%s}}", val_obj); // omit value conditional Jinja2 template code
|
||||
}
|
||||
|
||||
config["val_tpl"] = val_tpl;
|
||||
|
||||
char uniq_s[70];
|
||||
@@ -328,6 +341,7 @@ bool WebSchedulerService::runScheduleCommand(const ScheduleItem & si) {
|
||||
EMSESP::logger().warning("Schedule '%s': no command assigned", si.name);
|
||||
return false;
|
||||
}
|
||||
|
||||
return EMSESP::webCommandService.dispatchCommand(si.cmd_name.c_str());
|
||||
}
|
||||
|
||||
@@ -339,6 +353,7 @@ bool WebSchedulerService::onChange(const char * cmd) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -369,6 +384,7 @@ void WebSchedulerService::loop() {
|
||||
if (scheduleItems_->empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// do not execute any command in the first 60 secondes
|
||||
if (uuid::get_uptime_sec() < 60) {
|
||||
return;
|
||||
@@ -395,6 +411,7 @@ void WebSchedulerService::loop() {
|
||||
scheduleItem.retry_cnt = runScheduleCommand(scheduleItem) ? 0xFF : 0;
|
||||
}
|
||||
}
|
||||
|
||||
last_tm_min = -1;
|
||||
}
|
||||
|
||||
@@ -406,11 +423,13 @@ void WebSchedulerService::loop() {
|
||||
&& scheduleItem.retry_cnt < MAX_STARTUP_RETRIES) {
|
||||
scheduleItem.retry_cnt = runScheduleCommand(scheduleItem) ? 0xFF : scheduleItem.retry_cnt + 1;
|
||||
}
|
||||
|
||||
if (scheduleItem.active && scheduleItem.flags == SCHEDULEFLAG_SCHEDULE_TIMER && scheduleItem.elapsed_min > 0
|
||||
&& (uptime_min % scheduleItem.elapsed_min == 0)) {
|
||||
runScheduleCommand(scheduleItem);
|
||||
}
|
||||
}
|
||||
|
||||
last_uptime_min = uptime_min;
|
||||
}
|
||||
|
||||
@@ -427,6 +446,7 @@ void WebSchedulerService::loop() {
|
||||
runScheduleCommand(scheduleItem);
|
||||
}
|
||||
}
|
||||
|
||||
last_tm_min = tm->tm_min;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,6 +70,7 @@ class WebSchedulerService : public StatefulService<WebScheduler> {
|
||||
void ha_reset() {
|
||||
ha_configdone_ = false;
|
||||
}
|
||||
|
||||
uint8_t count_entities();
|
||||
bool onChange(const char * cmd);
|
||||
|
||||
@@ -96,4 +97,4 @@ class WebSchedulerService : public StatefulService<WebScheduler> {
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -149,6 +149,7 @@ StateUpdateResult WebSettings::update(JsonObject root, WebSettings & settings) {
|
||||
if (settings.eth_power != -1) { // Ethernet Power -1 means disabled
|
||||
EMSESP::system_.remove_gpio(settings.eth_power, true);
|
||||
}
|
||||
|
||||
// remove the ethernet pins from valid list, regardless of whether the GPIOs are valid or not
|
||||
EMSESP::system_.remove_gpio(23, true); // MDC
|
||||
EMSESP::system_.remove_gpio(18, true); // MDIO
|
||||
@@ -410,6 +411,7 @@ void WebSettingsService::onUpdate() {
|
||||
if (WebSettings::has_flags(WebSettings::ChangeFlags::MODBUS)) {
|
||||
EMSESP::system_.modbus_init();
|
||||
}
|
||||
|
||||
WebSettings::reset_flags();
|
||||
EMSESP::system_.reset_unused_gpios();
|
||||
}
|
||||
@@ -514,6 +516,7 @@ void WebSettings::set_board_profile(WebSettings & settings) {
|
||||
settings.board_profile = "S32"; // ESP32 standard WiFi with PSRAM
|
||||
}
|
||||
}
|
||||
|
||||
// override if we know the target from the build config like C3, S2, S3 etc..
|
||||
#elif CONFIG_IDF_TARGET_ESP32C3
|
||||
settings.board_profile = "C3MINI";
|
||||
@@ -561,6 +564,7 @@ bool WebSettings::check_flag(int prev_v, int new_v, uint16_t flag) {
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -69,6 +69,7 @@ void WebStatusService::systemStatus(AsyncWebServerRequest * request) {
|
||||
if (esp_sntp_enabled()) {
|
||||
ntp_status = (EMSESP::system_.ntp_connected()) ? 2 : 1;
|
||||
}
|
||||
|
||||
root["ntp_status"] = ntp_status;
|
||||
if (ntp_status == 2) {
|
||||
// send back actual time if NTP enabled and active
|
||||
@@ -129,6 +130,7 @@ void WebStatusService::systemStatus(AsyncWebServerRequest * request) {
|
||||
root["psram_size"] = EMSESP::system_.PSram();
|
||||
root["free_psram"] = ESP.getFreePsram() / 1024;
|
||||
}
|
||||
|
||||
root["model"] = EMSESP::system_.getBBQKeesGatewayDetails();
|
||||
root["board"] = EMSESP::system_.getBBQKeesGatewayDetails(FUSE_VALUE::BOARD);
|
||||
#if CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S2
|
||||
@@ -140,6 +142,7 @@ void WebStatusService::systemStatus(AsyncWebServerRequest * request) {
|
||||
if (partition != NULL) {
|
||||
root["recovery_version"] = EMSESP::system_.recovery_version();
|
||||
}
|
||||
|
||||
partition = esp_ota_get_next_update_partition(nullptr);
|
||||
if (partition) {
|
||||
uint64_t buffer;
|
||||
@@ -159,6 +162,7 @@ void WebStatusService::systemStatus(AsyncWebServerRequest * request) {
|
||||
if (partition.second.version.empty() || partition.second.size == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
JsonObject part = partitions.add<JsonObject>();
|
||||
part["partition"] = partition.first;
|
||||
part["version"] = partition.second.version;
|
||||
@@ -182,6 +186,7 @@ void WebStatusService::systemStatus(AsyncWebServerRequest * request) {
|
||||
// we're ready to do the actual restart ASAP
|
||||
EMSESP::system_.systemStatus(SYSTEM_STATUS::SYSTEM_STATUS_RESTART_REQUESTED);
|
||||
}
|
||||
|
||||
if (EMSESP::system_.systemStatus() == SYSTEM_STATUS::SYSTEM_STATUS_ERROR_UPLOAD) {
|
||||
// error is reported, back to normal state
|
||||
EMSESP::system_.systemStatus(SYSTEM_STATUS::SYSTEM_STATUS_NORMAL);
|
||||
@@ -375,6 +380,7 @@ void WebStatusService::getVersions(JsonObject root) {
|
||||
if (info.version.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
JsonObject out = root[key].to<JsonObject>();
|
||||
out["version"] = info.version;
|
||||
out["date"] = info.date;
|
||||
@@ -405,6 +411,7 @@ void WebStatusService::schedule_versions_refresh() {
|
||||
if (next == 0) {
|
||||
next = 1; // 0 is the "idle" sentinel — never let the wrap land there
|
||||
}
|
||||
|
||||
versions_next_fetch_ms_ = next;
|
||||
}
|
||||
|
||||
@@ -444,6 +451,7 @@ void WebStatusService::loop() {
|
||||
if (versions_failures_ < VERSIONS_MAX_BACKOFF_STEPS) {
|
||||
versions_failures_++;
|
||||
}
|
||||
|
||||
interval = VERSIONS_RETRY_INTERVAL_MS << (versions_failures_ - 1);
|
||||
}
|
||||
|
||||
@@ -451,6 +459,7 @@ void WebStatusService::loop() {
|
||||
if (next == 0) {
|
||||
next = 1;
|
||||
}
|
||||
|
||||
versions_next_fetch_ms_ = next;
|
||||
#endif
|
||||
}
|
||||
@@ -467,6 +476,7 @@ bool WebStatusService::refresh_versions_cache() {
|
||||
EMSESP::logger().warning("Unable to retrieve online version information (HTTP error code %d)", http_code);
|
||||
return false;
|
||||
}
|
||||
|
||||
DeserializationError err = deserializeJson(doc, result);
|
||||
if (err) {
|
||||
#if defined(EMSESP_DEBUG)
|
||||
@@ -483,6 +493,7 @@ bool WebStatusService::refresh_versions_cache() {
|
||||
out = {};
|
||||
return;
|
||||
}
|
||||
|
||||
out.version = section["version"] | "";
|
||||
out.date = section["date"] | "";
|
||||
out.upgradeable = !out.version.empty() && FirmwareVersion(out.version) > current_version;
|
||||
@@ -516,6 +527,7 @@ bool WebStatusService::current_upgradeable() const {
|
||||
if (!versions_cache_valid_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return is_dev ? versions_dev_.upgradeable : versions_stable_.upgradeable;
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ class WebStatusService {
|
||||
void set_current_version(const std::string & version) {
|
||||
current_version_s = version;
|
||||
}
|
||||
|
||||
std::string get_current_version() {
|
||||
return current_version_s;
|
||||
}
|
||||
@@ -67,6 +68,7 @@ class WebStatusService {
|
||||
std::string date;
|
||||
bool upgradeable = false;
|
||||
};
|
||||
|
||||
VersionInfo versions_stable_;
|
||||
VersionInfo versions_dev_;
|
||||
bool versions_cache_valid_ = false; // true once we've had at least one successful fetch
|
||||
|
||||
Reference in New Issue
Block a user