Merge branch 'development' into prerelease-15.0.0

This commit is contained in:
Theo Arends 2025-06-11 09:20:16 +02:00
commit 59911c3467
6 changed files with 2062 additions and 1988 deletions

View File

@ -18,6 +18,7 @@ All notable changes to this project will be documented in this file.
- Berry `compile` and `tasmota.compile` option to compile in local context (#23457) - Berry `compile` and `tasmota.compile` option to compile in local context (#23457)
- Support for AP33772S USB PD Sink Controller as used in CentyLab RotoPD - Support for AP33772S USB PD Sink Controller as used in CentyLab RotoPD
- Berry mqtt publish rule processing - Berry mqtt publish rule processing
- Berry `tasmota.is_network_up()`
### Changed ### Changed
- ESP32 Platform from 2025.04.30 to 2025.05.40, Framework (Arduino Core) from v3.1.3.250411 to v3.2.0.250504 and IDF from v5.3.2.250403 to v5.4.1.250501 (#23397) - ESP32 Platform from 2025.04.30 to 2025.05.40, Framework (Arduino Core) from v3.1.3.250411 to v3.2.0.250504 and IDF from v5.3.2.250403 to v5.4.1.250501 (#23397)

View File

@ -184,6 +184,7 @@ class be_class_tasmota (scope: global, name: Tasmota) {
exec_cmd, closure(class_Tasmota_exec_cmd_closure) exec_cmd, closure(class_Tasmota_exec_cmd_closure)
gc, closure(class_Tasmota_gc_closure) gc, closure(class_Tasmota_gc_closure)
event, closure(class_Tasmota_event_closure) event, closure(class_Tasmota_event_closure)
is_network_up, closure(class_Tasmota_is_network_up_closure)
when_network_up, closure(class_Tasmota_when_network_up_closure) when_network_up, closure(class_Tasmota_when_network_up_closure)
run_network_up, closure(class_Tasmota_run_network_up_closure) run_network_up, closure(class_Tasmota_run_network_up_closure)
add_driver, closure(class_Tasmota_add_driver_closure) add_driver, closure(class_Tasmota_add_driver_closure)

View File

@ -724,12 +724,16 @@ class Tasmota
end end
end end
# returns `true` if the network stack is connected
def is_network_up()
return tasmota.wifi()['up'] || tasmota.eth()['up']
end
# add a closure to the list to be called when network is connected # add a closure to the list to be called when network is connected
# or call immediately if network is already up # or call immediately if network is already up
def when_network_up(cl) def when_network_up(cl)
self.check_not_method(cl) self.check_not_method(cl)
var is_connected = tasmota.wifi()['up'] || tasmota.eth()['up'] if self.is_network_up()
if is_connected
cl() # call closure cl() # call closure
else else
if (self._wnu == nil) if (self._wnu == nil)
@ -743,8 +747,7 @@ class Tasmota
# run all pending closures when network is up # run all pending closures when network is up
def run_network_up() def run_network_up()
if (self._wnu == nil) return end if (self._wnu == nil) return end
var is_connected = tasmota.wifi()['up'] || tasmota.eth()['up'] if self.is_network_up()
if is_connected
# run all closures in a safe loop # run all closures in a safe loop
while (size(self._wnu) > 0) while (size(self._wnu) > 0)
var cl = self._wnu[0] var cl = self._wnu[0]
@ -760,13 +763,12 @@ class Tasmota
end end
def event(event_type, cmd, idx, payload, raw) def event(event_type, cmd, idx, payload, raw)
import introspect if (event_type == 'every_50ms')
if event_type=='every_50ms'
if (self._wnu) self.run_network_up() end # capture when network becomes connected if (self._wnu) self.run_network_up() end # capture when network becomes connected
self.run_timers() self.run_timers()
end #- first run deferred events -# end #- first run deferred events -#
if event_type=='every_250ms' if (event_type == 'every_250ms')
self.run_cron() self.run_cron()
end end
@ -777,11 +779,12 @@ class Tasmota
keep_going = true keep_going = true
end end
if event_type=='cmd' return self.exec_cmd(cmd, idx, payload) if (event_type == 'cmd') return self.exec_cmd(cmd, idx, payload)
elif event_type=='tele' return self.exec_tele(payload) elif (event_type == 'tele') return self.exec_tele(payload)
elif event_type=='rule' return self.exec_rules(payload, bool(idx)) elif (event_type == 'rule') return self.exec_rules(payload, bool(idx))
elif event_type=='gc' return self.gc() elif (event_type == 'gc') return self.gc()
elif self._drivers elif self._drivers
import introspect
var i = 0 var i = 0
while i < size(self._drivers) while i < size(self._drivers)
var d = self._drivers[i] var d = self._drivers[i]
@ -811,6 +814,16 @@ class Tasmota
return done return done
end end
######################################################################
# add_driver
#
# Add an instance to the dispatchin of Berry events
#
# Args:
# - `d`: instance (or driver)
# The events will be dispatched to this instance whenever
# it has a method with the same name of the instance
######################################################################
def add_driver(d) def add_driver(d)
if type(d) != 'instance' if type(d) != 'instance'
raise "value_error", "instance required" raise "value_error", "instance required"

View File

@ -2469,6 +2469,11 @@ void SyslogAsync(bool refresh) {
and below message in syslog if hostname starts with a "z" and below message in syslog if hostname starts with a "z"
2023-12-17T00:09:52.797782+01:00 domus8 rsyslogd: Uncompression of a message failed with return code -3 - enable debug logging if you need further information. Message ignored. [v8.2302.0] 2023-12-17T00:09:52.797782+01:00 domus8 rsyslogd: Uncompression of a message failed with return code -3 - enable debug logging if you need further information. Message ignored. [v8.2302.0]
Notice in both cases the date and time is taken from the syslog server Notice in both cases the date and time is taken from the syslog server
Example of rsyslog filter using rsyslog properties:
:programname, startswith, "ESP-" /var/log/udp-logs/esp.log # Log in esp.log
:programname, startswith, "ESP-" stop # Do not log in syslog
*/ */
// snprintf_P(header, sizeof(header), PSTR("%s ESP-"), NetworkHostname()); // snprintf_P(header, sizeof(header), PSTR("%s ESP-"), NetworkHostname());
@ -2483,6 +2488,10 @@ void SyslogAsync(bool refresh) {
LOG_LEVEL_INFO 2 -> severity level 6 - Informational LOG_LEVEL_INFO 2 -> severity level 6 - Informational
LOG_LEVEL_DEBUG 3 -> severity level 7 - Debug LOG_LEVEL_DEBUG 3 -> severity level 7 - Debug
LOG_LEVEL_DEBUG_MORE 4 -> severity level 7 - Debug LOG_LEVEL_DEBUG_MORE 4 -> severity level 7 - Debug
Example of rsyslog filter using rsyslog properties:
:programname, startswith, "ESP-" /var/log/udp-logs/esp.log # Log in esp.log
:programname, startswith, "ESP-" stop # Do not log in syslog
*/ */
// snprintf_P(header, sizeof(header), PSTR("<%d>%s ESP-"), 128 + min(loglevel * 3, 7), NetworkHostname()); // snprintf_P(header, sizeof(header), PSTR("<%d>%s ESP-"), 128 + min(loglevel * 3, 7), NetworkHostname());
@ -2493,6 +2502,10 @@ void SyslogAsync(bool refresh) {
SYSLOG-MSG = <134>Jan 1 00:00:02 wemos5 ESP-HTP: Web server active on wemos5 with IP address 192.168.2.172 SYSLOG-MSG = <134>Jan 1 00:00:02 wemos5 ESP-HTP: Web server active on wemos5 with IP address 192.168.2.172
Result = 2023-01-01T00:00:02+01:00 wemos5 ESP-HTP: Web server active on wemos5 with IP address 192.168.2.172 Result = 2023-01-01T00:00:02+01:00 wemos5 ESP-HTP: Web server active on wemos5 with IP address 192.168.2.172
Notice Year is taken from syslog server. Month, day and time is provided by Tasmota device. No milliseconds Notice Year is taken from syslog server. Month, day and time is provided by Tasmota device. No milliseconds
Example of rsyslog filter using rsyslog properties:
:programname, startswith, "ESP-" /var/log/udp-logs/esp.log # Log in esp.log
:programname, startswith, "ESP-" stop # Do not log in syslog
*/ */
// snprintf_P(header, sizeof(header), PSTR("<134>%s %s ESP-"), GetSyslogDate(line).c_str(), NetworkHostname()); // snprintf_P(header, sizeof(header), PSTR("<134>%s %s ESP-"), GetSyslogDate(line).c_str(), NetworkHostname());
@ -2500,16 +2513,21 @@ void SyslogAsync(bool refresh) {
uint32_t msg_len = len -mxtime -1; uint32_t msg_len = len -mxtime -1;
/* RFC5424 - Syslog protocol - <PRI>VERSION TIMESTAMP HOSTNAME APP_NAME PROCID STRUCTURED-DATA MSGID MSG /* RFC5424 - Syslog protocol - <PRI>VERSION TIMESTAMP HOSTNAME APP_NAME PROCID STRUCTURED-DATA MSGID MSG
<PRI> = Facility 16 (= local use 0), Severity 6 (= informational) => 16 * 8 + 6 = <134> <PRI>[5] = Facility 16 (= local use 0), Severity 6 (= informational) => 16 * 8 + 6 = <134>
VERSION = 1 VERSION[2] = 1
TIMESTAMP = yyyy-mm-ddThh:mm:ss.nnnnnn-hh:mm (= local with timezone) TIMESTAMP = yyyy-mm-ddThh:mm:ss.nnnnnn-hh:mm (= local with timezone)
APP_NAME = tasmota HOSTNAME[255] = wemos5
PROCID = - APP_NAME[48] = tasmota
PROCID[128] = -
STRUCTURED-DATA = - STRUCTURED-DATA = -
MSGID = HTP: MSGID[32] = HTP:
SYSLOG-MSG = <134>1 1970-01-01T00:00:02.096000+01:00 wemos5 Tasmota - - ESP-HTP: Web server active on wemos5 with IP address 192.168.2.172 SYSLOG-MSG = <134>1 1970-01-01T00:00:02.096000+01:00 wemos5 tasmota - - HTP: Web server active on wemos5 with IP address 192.168.2.172
Result = 1970-01-01T00:00:02.096000+00:00 wemos5 Tasmota ESP-HTP: Web server active on wemos5 with IP address 192.168.2.172 Result = 1970-01-01T00:00:02.096000+00:00 wemos5 tasmota HTP: Web server active on wemos5 with IP address 192.168.2.172
Notice date and time is provided by Tasmota device. Notice date and time is provided by Tasmota device.
Example of rsyslog filter using rsyslog properties:
:programname, isequal, "tasmota" /var/log/udp-logs/esp.log # Log in esp.log
:programname, isequal, "tasmota" stop # Do not log in syslog
*/ */
char timestamp[mxtime]; char timestamp[mxtime];
subStr(timestamp, line, " ", 1); // 00:00:02.096-026 subStr(timestamp, line, " ", 1); // 00:00:02.096-026
@ -2520,19 +2538,29 @@ void SyslogAsync(bool refresh) {
GetDate().c_str(), timestamp, GetTimeZone().c_str(), // 1970-01-01T00:00:02.096000+01:00 GetDate().c_str(), timestamp, GetTimeZone().c_str(), // 1970-01-01T00:00:02.096000+01:00
NetworkHostname()); NetworkHostname());
*/ */
/*
// msgid is currently not well supported in rsyslog (https://github.com/rsyslog/rsyslog/issues/3592#issuecomment-480186237)
char msgid[5]; char msgid[5];
char* line_msgid = strchr(msg_start, ' '); char* line_msgid = strchr(msg_start, ' ');
if (line_msgid - msg_start < sizeof(msgid)) { // Only 3 character message ids supported if (line_msgid && (line_msgid - msg_start < sizeof(msgid))) { // Only 3 character message ids supported
subStr(msgid, msg_start, " ", 1); // HTP: subStr(msgid, msg_start, " ", 1); // HTP:
msg_start += strlen(msgid); msg_start += strlen(msgid);
msg_len -= strlen(msgid); msg_len -= strlen(msgid);
} else { } else {
strcpy(msgid, "-"); // - strcpy(msgid, "-"); // -
} }
*/
char msgid[2] = { 0 };
char* line_msgid = strchr(msg_start, ':');
if ((line_msgid == nullptr) || (line_msgid - msg_start != 3)) { // Only 3 character message id supported
strcpy(msgid, "-"); // -
}
snprintf_P(header, sizeof(header), PSTR("<%d>1 %s%s000%s %s tasmota - - %s"), snprintf_P(header, sizeof(header), PSTR("<%d>1 %s%s000%s %s tasmota - - %s"),
128 + min(loglevel * 3, 7), // Error (1) = 131, Info (2) = 134, Debug (3) = 135, DebugMore = (4) 135 128 + min(loglevel * 3, 7), // Error (1) = 131, Info (2) = 134, Debug (3) = 135, DebugMore = (4) 135
GetDate().c_str(), timestamp, GetTimeZone().c_str(), // 1970-01-01T00:00:02.096000+01:00 GetDate().c_str(), timestamp, GetTimeZone().c_str(), // 1970-01-01T00:00:02.096000+01:00
NetworkHostname(), msgid); NetworkHostname(), msgid);
/* /*
TasConsole.printf("Loglevel "); TasConsole.printf("Loglevel ");
TasConsole.print(loglevel); TasConsole.print(loglevel);

View File

@ -292,6 +292,7 @@ void Script_ticker4_end(void) {
#define SCRIPT_UDP_BUFFER_SIZE 128 #define SCRIPT_UDP_BUFFER_SIZE 128
#endif #endif
#define SCRIPT_UDP_PORT 1999 #define SCRIPT_UDP_PORT 1999
#endif
// EEPROM MACROS // EEPROM MACROS
// i2c eeprom // i2c eeprom
@ -1529,6 +1530,7 @@ char *script;
} }
#ifdef USE_SCRIPT_GLOBVARS
int32_t udp_call(char *url, uint32_t port, char *sbuf) { int32_t udp_call(char *url, uint32_t port, char *sbuf) {
WiFiUDP udp; WiFiUDP udp;
IPAddress adr; IPAddress adr;
@ -1597,20 +1599,22 @@ void Script_PollUdp(void) {
if (glob_script_mem.udp_flags.udp_connected ) { if (glob_script_mem.udp_flags.udp_connected ) {
uint32_t timeout = millis(); uint32_t timeout = millis();
while (1) { while (1) {
char *packet_buffer = glob_script_mem.packet_buffer;
uint16_t plen = glob_script_mem.Script_PortUdp.parsePacket(); uint16_t plen = glob_script_mem.Script_PortUdp.parsePacket();
if (!plen || plen > glob_script_mem.pb_size) { if (!plen || plen > glob_script_mem.pb_size) {
glob_script_mem.Script_PortUdp.flush(); if (plen > 0) {
glob_script_mem.Script_PortUdp.read(packet_buffer, glob_script_mem.pb_size - 1);
glob_script_mem.Script_PortUdp.flush();
}
break; break;
} }
// not more then 500 ms // not more then 500 ms
if (millis() - timeout > 500) { break;} if (millis() - timeout > 500) { break;}
char *packet_buffer = glob_script_mem.packet_buffer; int32_t len = glob_script_mem.Script_PortUdp.read(packet_buffer, glob_script_mem.pb_size - 1);
int32_t len = glob_script_mem.Script_PortUdp.read(packet_buffer, glob_script_mem.pb_size);
packet_buffer[len] = 0; packet_buffer[len] = 0;
glob_script_mem.script_udp_remote_ip = glob_script_mem.Script_PortUdp.remoteIP(); glob_script_mem.script_udp_remote_ip = glob_script_mem.Script_PortUdp.remoteIP();
#ifdef SCRIPT_DEBUG_UDP #ifdef SCRIPT_DEBUG_UDP
//AddLog(LOG_LEVEL_DEBUG, PSTR("UDP: Packet %s - %d - %s"), packet_buffer, len, script_udp_remote_ip.toString().c_str());
AddLog(LOG_LEVEL_DEBUG, PSTR("UDP: received Packet %s - %d - %_I"), packet_buffer, len, (uint32_t)glob_script_mem.script_udp_remote_ip); AddLog(LOG_LEVEL_DEBUG, PSTR("UDP: received Packet %s - %d - %_I"), packet_buffer, len, (uint32_t)glob_script_mem.script_udp_remote_ip);
#endif #endif
char *lp = packet_buffer; char *lp = packet_buffer;
@ -1706,7 +1710,12 @@ void script_udp_sendvar(char *vname, TS_FLOAT *fp, char *sp, uint16_t alen) {
if (!glob_script_mem.udp_flags.udp_used) return; if (!glob_script_mem.udp_flags.udp_used) return;
if (!glob_script_mem.udp_flags.udp_connected) return; if (!glob_script_mem.udp_flags.udp_connected) return;
char sbuf[SCRIPT_MAX_SBSIZE + 4]; uint16_t ubsiz = SCRIPT_MAX_SBSIZE + 16;
if (ubsiz < 32) {
ubsiz = 32;
}
char sbuf[ubsiz];
strcpy(sbuf, "=>"); strcpy(sbuf, "=>");
strcat(sbuf, vname); strcat(sbuf, vname);
if (glob_script_mem.udp_flags.udp_binary_payload == 0 || !fp) { if (glob_script_mem.udp_flags.udp_binary_payload == 0 || !fp) {
@ -8218,10 +8227,14 @@ startline:
and_or = 0; and_or = 0;
if (if_exe[ifstck - 1] == 0) { if (if_exe[ifstck - 1] == 0) {
// not enabled // not enabled
#if 0
glob_script_mem.FLAGS.ignore_line = 1; glob_script_mem.FLAGS.ignore_line = 1;
/* // AddLog(LOG_LEVEL_INFO, PSTR(">>> %d"),ifstck);
#else
// AddLog(LOG_LEVEL_INFO, PSTR(">>> %d"),ifstck);
while (*lp) { while (*lp) {
if (*lp == SCRIPT_EOL) { if (*lp == SCRIPT_EOL) {
lp--;
break; break;
} }
if (*lp == '{') { if (*lp == '{') {
@ -8232,7 +8245,7 @@ startline:
lp++; lp++;
} }
goto next_line; goto next_line;
*/ #endif
} }
} else if (!strncmp(lp, "then", 4) && if_state[ifstck] == 1) { } else if (!strncmp(lp, "then", 4) && if_state[ifstck] == 1) {
lp += 4; lp += 4;