Compare commits

...

9 Commits

20 changed files with 426 additions and 271 deletions

View File

@ -3,7 +3,29 @@ All notable changes to this project will be documented in this file.
## [Unreleased] - Development
## [14.5.0.3]
## [14.6.0.1]
### Added
- Command `JsonPP 0..7` to enable (>0) JSON Pretty Print on user interfaces and set number of indents
- Command `JsonPP <command>|backlog <command>;...` to enable JSON PP only once
### Breaking Changed
### Changed
### Fixed
- Berry `bytes().asstring()` now truncates a string if buffer contains NULL (#23311)
- Berry string literals containing NULL are truncated (#23312)
### Removed
## [Released]
## [14.6.0] 20250416
- Release Ryan
## [14.5.0.3] 20250416
### Added
- Extend command `GPIO` with different display options and allowing updating of module GPIO's in one go
- Berry `bytes.add()` now accepts 3-bytes values (#23200)
@ -29,9 +51,6 @@ All notable changes to this project will be documented in this file.
- TLS increase timeout and fix crash (#23249)
- Berry `readline` when a line is exactly 98 characters (#23276)
### Removed
## [14.5.0.2] 20250325
### Added
- Berry load `.tapp` files in `/.extensions/` then in `/` (#23113)
@ -77,8 +96,6 @@ All notable changes to this project will be documented in this file.
### Fixed
- Too many zeros in RCSwitch received data regression from v14.4.1.4 (#23050)
## [Released]
## [14.5.0] 20250219
- Release Ruth

View File

@ -18,7 +18,7 @@ See [CHANGELOG.md](https://github.com/arendst/Tasmota/blob/development/CHANGELOG
## Development
[![Dev Version](https://img.shields.io/badge/development%20version-v14.5.x.x-blue.svg)](https://github.com/arendst/Tasmota)
[![Dev Version](https://img.shields.io/badge/development%20version-v14.6.x.x-blue.svg)](https://github.com/arendst/Tasmota)
[![Download Dev](https://img.shields.io/badge/download-development-yellow.svg)](http://ota.tasmota.com/tasmota/)
[![Tasmota CI](https://github.com/arendst/Tasmota/workflows/Tasmota%20CI/badge.svg)](https://github.com/arendst/Tasmota/actions?query=workflow%3A%22Tasmota+CI%22)
[![Tasmota ESP32 CI](https://github.com/arendst/Tasmota/workflows/Tasmota%20ESP32%20CI/badge.svg)](https://github.com/arendst/Tasmota/actions?query=workflow%3A%22Tasmota+ESP32+CI%22)

View File

@ -31,7 +31,7 @@ Firmware binaries can be downloaded from http://ota.tasmota.com/tasmota/release/
## Development
[![Dev Version](https://img.shields.io/badge/development%20version-v14.5.x.x-blue.svg)](https://github.com/arendst/Tasmota)
[![Dev Version](https://img.shields.io/badge/development%20version-v14.6.x.x-blue.svg)](https://github.com/arendst/Tasmota)
[![Download Dev](https://img.shields.io/badge/download-development-yellow.svg)](http://ota.tasmota.com/tasmota/)
[![Tasmota CI](https://github.com/arendst/Tasmota/actions/workflows/build_all_the_things.yml/badge.svg)](https://github.com/arendst/Tasmota/actions/workflows/build_all_the_things.yml)
[![Build_development](https://github.com/arendst/Tasmota/actions/workflows/Tasmota_build_devel.yml/badge.svg)](https://github.com/arendst/Tasmota/actions/workflows/Tasmota_build_devel.yml)

View File

@ -36,9 +36,9 @@ While fallback or downgrading is common practice it was never supported due to S
This release will be supported from ESP8266/Arduino library Core version **2.7.8** due to reported security and stability issues on previous Core version. This will also support gzipped binaries.
This release will be supported from ESP32/Arduino library Core version **v3.1.1.250203**.
This release will be supported from ESP32/Arduino library Core version **v3.1.3.250411**.
Support of ESP8266 Core versions before 2.7.8 and ESP32 Core versions before v3.1.1.250203 have been removed.
Support of ESP8266 Core versions before 2.7.8 and ESP32 Core versions before v3.1.3.250411 have been removed.
## Support of TLS
@ -75,12 +75,12 @@ Latest released binaries can be downloaded from
- http://ota.tasmota.com/tasmota/release
Historical binaries can be downloaded from
- http://ota.tasmota.com/tasmota/release-14.5.0
- http://ota.tasmota.com/tasmota/release-14.6.0
The latter links can be used for OTA upgrades too like ``OtaUrl http://ota.tasmota.com/tasmota/release/tasmota.bin.gz``
### ESP32, ESP32-C2, ESP32-C3, ESP32-C6, ESP32-S2 and ESP32-S3 based
The following binary downloads have been compiled with ESP32/Arduino library core version **v3.1.1.250203**.
The following binary downloads have been compiled with ESP32/Arduino library core version **v3.1.3.250411**.
- **tasmota32.bin** = The Tasmota version with most drivers including additional sensors and KNX for 4M+ flash. **RECOMMENDED RELEASE BINARY**
- **tasmota32solo1.bin** = The Tasmota version with most drivers including additional sensors and KNX for single core ESP32 and 4M+ flash.
@ -104,7 +104,7 @@ Latest released binaries can be downloaded from
- https://ota.tasmota.com/tasmota32/release
Historical binaries can be downloaded from
- https://ota.tasmota.com/tasmota32/release-14.5.0
- https://ota.tasmota.com/tasmota32/release-14.6.0
The latter links can be used for OTA upgrades too like ``OtaUrl https://ota.tasmota.com/tasmota32/release/tasmota32.bin``
@ -114,58 +114,17 @@ The latter links can be used for OTA upgrades too like ``OtaUrl https://ota.tasm
[Complete list](BUILDS.md) of available feature and sensors.
## Changelog v14.5.0.3
## Changelog v14.6.0.1
### Added
- Filesystem command ``UfsList[2]``
- Extend command `GPIO` with different display options and allowing updating of module GPIO's in one go
- Support Vango Technologies V924x ultralow power, single-phase, power measurement [#23127](https://github.com/arendst/Tasmota/issues/23127)
- Support for HLK-LD2402 24GHz smart wave motion sensor [#23133](https://github.com/arendst/Tasmota/issues/23133)
- Support for Telnet server using command `Telnet <0|1|port>[,<IP filter>]` if enabled with `#define USE_TELNET`
- Support for XMODEM over serial and telnet if enabled with `#define USE_XYZMODEM`
- PZEM_AC device address in JSON and GUI [#23268](https://github.com/arendst/Tasmota/issues/23268)
- Allow acl in mqtt when client certificate is in use with `#define USE_MQTT_CLIENT_CERT` [#22998](https://github.com/arendst/Tasmota/issues/22998)
- AlpineJS 2.8.2 - optional for now [#23259](https://github.com/arendst/Tasmota/issues/23259)
- ESP32 show network interface priority in `Status 5` debug logging [#23302](https://github.com/arendst/Tasmota/issues/23302)
- Berry experimental driver for AXP2101 for M5Core2v1.1 [#23039](https://github.com/arendst/Tasmota/issues/23039)
- Berry `tasmota.when_network_up()` and simplified Matter using it [#23057](https://github.com/arendst/Tasmota/issues/23057)
- Berry `introspect.solidified()` to know if a Berry object is solidified or in RAM [#23063](https://github.com/arendst/Tasmota/issues/23063)
- Berry `global.undef()` to undefine a global variable [#23073](https://github.com/arendst/Tasmota/issues/23073)
- Berry load `.tapp` files in `/.extensions/` then in `/` [#23113](https://github.com/arendst/Tasmota/issues/23113)
- Berry `re.dump()` [#23162](https://github.com/arendst/Tasmota/issues/23162)
- Berry `bytes.add()` now accepts 3-bytes values [#23200](https://github.com/arendst/Tasmota/issues/23200)
- Berry expose `esp_http_server` for websockets [#23206](https://github.com/arendst/Tasmota/issues/23206)
- Matter prepare for ICD cluster [#23158](https://github.com/arendst/Tasmota/issues/23158)
- LVGL experimental mirroring of display on Web UI [#23041](https://github.com/arendst/Tasmota/issues/23041)
- HASPmota autostart when `pages.jsonl` exists [#23181](https://github.com/arendst/Tasmota/issues/23181)
- Command `JsonPP 0..7` to enable (>0) JSON Pretty Print on user interfaces and set number of indents
- Command `JsonPP <command>|backlog <command>;...` to enable JSON PP only once
### Breaking Changed
- Berry remove `Leds.create_matrix` from the standard library waiting for reimplementation [#23114](https://github.com/arendst/Tasmota/issues/23114)
- HASPmota added `y2_min` and `y2_max` to control the second series of `chart` [#23287](https://github.com/arendst/Tasmota/issues/23287)
- HASPmota default theme is now Tasmota-style [#23288](https://github.com/arendst/Tasmota/issues/23288)
### Changed
- ESP32 Platform from 2025.02.30 to 2025.04.30, Framework (Arduino Core) from v3.1.1.250203 to v3.1.3.250411 and IDF from v5.3.2.250120 to 5.3.2.250403 [#23280](https://github.com/arendst/Tasmota/issues/23280)
- Output of commands `GPIO` and `GPIOs` swapped
- Smoothen light gamma curve when using `Fade` [#23230](https://github.com/arendst/Tasmota/issues/23230)
- RCSwitch `RCSWITCH_SEPARATION_LIMIT` from 4100 to 3600
- GPIOViewer from v1.6.1 to v1.6.2 (No functional change)
- HLK-LD2402 updates for firmware 3.3.5+ [#23281](https://github.com/arendst/Tasmota/issues/23281)
- ESP8266 enable FTP for >= 4MB variants [#23120](https://github.com/arendst/Tasmota/issues/23120)
- ESP32 enable webcam version 2 [#18732](https://github.com/arendst/Tasmota/issues/18732)
- Berry update flasher for Sonoff ZBBridge Pro [#23136](https://github.com/arendst/Tasmota/issues/23136)
- Berry `re` now accepts `bytes()` as precompiled patterns, added `re.compilebytes()` [#23149](https://github.com/arendst/Tasmota/issues/23149)
- LVGL, prepare for HASPmota theme, change: no-grow when clicked, DPI set to 160 [#23040](https://github.com/arendst/Tasmota/issues/23040)
- LVGL Mirroring add checkbox to enable/disable the feature (in the iterim for a better solution) [#23047](https://github.com/arendst/Tasmota/issues/23047)
- Leds Panel add checkbox to enable/disable the feature (in the iterim for a better solution) [#23048](https://github.com/arendst/Tasmota/issues/23048)
### Fixed
- Too many zeros in RCSwitch received data regression from v14.4.1.4 [#23050](https://github.com/arendst/Tasmota/issues/23050)
- INA226 driver fixes [#23197](https://github.com/arendst/Tasmota/issues/23197)
- TLS increase timeout and fix crash [#23249](https://github.com/arendst/Tasmota/issues/23249)
- ESP32 receive incomplete serial data over 128 bytes [#23156](https://github.com/arendst/Tasmota/issues/23156)
- ESP32 intermittent exception on WiFi AP cannot be reached [#23115](https://github.com/arendst/Tasmota/issues/23115)
- ESP32-C3 WiFi sleep [#23096](https://github.com/arendst/Tasmota/issues/23096)
- Berry prevent `import` from hiding a solidified class [#23112](https://github.com/arendst/Tasmota/issues/23112)
- Berry `readline` when a line is exactly 98 characters [#23276](https://github.com/arendst/Tasmota/issues/23276)
- Berry `bytes().asstring()` now truncates a string if buffer contains NULL [#23311](https://github.com/arendst/Tasmota/issues/23311)
- Berry string literals containing NULL are truncated [#23312](https://github.com/arendst/Tasmota/issues/23312)
### Removed

View File

@ -0,0 +1,128 @@
/**
* Base64 encoding and decoding of strings. Uses '+' for 62, '/' for 63, '=' for padding
*/
#include "base64.hpp"
unsigned char binary_to_base64(unsigned char v) {
// Capital letters - 'A' is ascii 65 and base64 0
if(v < 26) return v + 'A';
// Lowercase letters - 'a' is ascii 97 and base64 26
if(v < 52) return v + 71;
// Digits - '0' is ascii 48 and base64 52
if(v < 62) return v - 4;
// '+' is ascii 43 and base64 62
if(v == 62) return '+';
// '/' is ascii 47 and base64 63
if(v == 63) return '/';
return 64;
}
unsigned char base64_to_binary(unsigned char c) {
// Capital letters - 'A' is ascii 65 and base64 0
if('A' <= c && c <= 'Z') return c - 'A';
// Lowercase letters - 'a' is ascii 97 and base64 26
if('a' <= c && c <= 'z') return c - 71;
// Digits - '0' is ascii 48 and base64 52
if('0' <= c && c <= '9') return c + 4;
// '+' is ascii 43 and base64 62
if(c == '+') return 62;
// '/' is ascii 47 and base64 63
if(c == '/') return 63;
return 255;
}
unsigned int encode_base64_length(unsigned int input_length) {
return (input_length + 2)/3*4;
}
unsigned int decode_base64_length(unsigned char input[]) {
unsigned char *start = input;
while(base64_to_binary(input[0]) < 64) {
++input;
}
unsigned int input_length = input - start;
unsigned int output_length = input_length/4*3;
switch(input_length % 4) {
default: return output_length;
case 2: return output_length + 1;
case 3: return output_length + 2;
}
}
unsigned int encode_base64(unsigned char input[], unsigned int input_length, unsigned char output[]) {
unsigned int full_sets = input_length/3;
// While there are still full sets of 24 bits...
for(unsigned int i = 0; i < full_sets; ++i) {
output[0] = binary_to_base64( input[0] >> 2);
output[1] = binary_to_base64((input[0] & 0x03) << 4 | input[1] >> 4);
output[2] = binary_to_base64((input[1] & 0x0F) << 2 | input[2] >> 6);
output[3] = binary_to_base64( input[2] & 0x3F);
input += 3;
output += 4;
}
switch(input_length % 3) {
case 0:
output[0] = '\0';
break;
case 1:
output[0] = binary_to_base64( input[0] >> 2);
output[1] = binary_to_base64((input[0] & 0x03) << 4);
output[2] = '=';
output[3] = '=';
output[4] = '\0';
break;
case 2:
output[0] = binary_to_base64( input[0] >> 2);
output[1] = binary_to_base64((input[0] & 0x03) << 4 | input[1] >> 4);
output[2] = binary_to_base64((input[1] & 0x0F) << 2);
output[3] = '=';
output[4] = '\0';
break;
}
return encode_base64_length(input_length);
}
unsigned int decode_base64(unsigned char input[], unsigned char output[]) {
unsigned int output_length = decode_base64_length(input);
// While there are still full sets of 24 bits...
for(unsigned int i = 2; i < output_length; i += 3) {
output[0] = base64_to_binary(input[0]) << 2 | base64_to_binary(input[1]) >> 4;
output[1] = base64_to_binary(input[1]) << 4 | base64_to_binary(input[2]) >> 2;
output[2] = base64_to_binary(input[2]) << 6 | base64_to_binary(input[3]);
input += 4;
output += 3;
}
switch(output_length % 3) {
case 1:
output[0] = base64_to_binary(input[0]) << 2 | base64_to_binary(input[1]) >> 4;
break;
case 2:
output[0] = base64_to_binary(input[0]) << 2 | base64_to_binary(input[1]) >> 4;
output[1] = base64_to_binary(input[1]) << 4 | base64_to_binary(input[2]) >> 2;
break;
}
return output_length;
}

View File

@ -69,127 +69,4 @@ unsigned int encode_base64(unsigned char input[], unsigned int input_length, uns
*/
unsigned int decode_base64(unsigned char input[], unsigned char output[]);
unsigned char binary_to_base64(unsigned char v) {
// Capital letters - 'A' is ascii 65 and base64 0
if(v < 26) return v + 'A';
// Lowercase letters - 'a' is ascii 97 and base64 26
if(v < 52) return v + 71;
// Digits - '0' is ascii 48 and base64 52
if(v < 62) return v - 4;
// '+' is ascii 43 and base64 62
if(v == 62) return '+';
// '/' is ascii 47 and base64 63
if(v == 63) return '/';
return 64;
}
unsigned char base64_to_binary(unsigned char c) {
// Capital letters - 'A' is ascii 65 and base64 0
if('A' <= c && c <= 'Z') return c - 'A';
// Lowercase letters - 'a' is ascii 97 and base64 26
if('a' <= c && c <= 'z') return c - 71;
// Digits - '0' is ascii 48 and base64 52
if('0' <= c && c <= '9') return c + 4;
// '+' is ascii 43 and base64 62
if(c == '+') return 62;
// '/' is ascii 47 and base64 63
if(c == '/') return 63;
return 255;
}
unsigned int encode_base64_length(unsigned int input_length) {
return (input_length + 2)/3*4;
}
unsigned int decode_base64_length(unsigned char input[]) {
unsigned char *start = input;
while(base64_to_binary(input[0]) < 64) {
++input;
}
unsigned int input_length = input - start;
unsigned int output_length = input_length/4*3;
switch(input_length % 4) {
default: return output_length;
case 2: return output_length + 1;
case 3: return output_length + 2;
}
}
unsigned int encode_base64(unsigned char input[], unsigned int input_length, unsigned char output[]) {
unsigned int full_sets = input_length/3;
// While there are still full sets of 24 bits...
for(unsigned int i = 0; i < full_sets; ++i) {
output[0] = binary_to_base64( input[0] >> 2);
output[1] = binary_to_base64((input[0] & 0x03) << 4 | input[1] >> 4);
output[2] = binary_to_base64((input[1] & 0x0F) << 2 | input[2] >> 6);
output[3] = binary_to_base64( input[2] & 0x3F);
input += 3;
output += 4;
}
switch(input_length % 3) {
case 0:
output[0] = '\0';
break;
case 1:
output[0] = binary_to_base64( input[0] >> 2);
output[1] = binary_to_base64((input[0] & 0x03) << 4);
output[2] = '=';
output[3] = '=';
output[4] = '\0';
break;
case 2:
output[0] = binary_to_base64( input[0] >> 2);
output[1] = binary_to_base64((input[0] & 0x03) << 4 | input[1] >> 4);
output[2] = binary_to_base64((input[1] & 0x0F) << 2);
output[3] = '=';
output[4] = '\0';
break;
}
return encode_base64_length(input_length);
}
unsigned int decode_base64(unsigned char input[], unsigned char output[]) {
unsigned int output_length = decode_base64_length(input);
// While there are still full sets of 24 bits...
for(unsigned int i = 2; i < output_length; i += 3) {
output[0] = base64_to_binary(input[0]) << 2 | base64_to_binary(input[1]) >> 4;
output[1] = base64_to_binary(input[1]) << 4 | base64_to_binary(input[2]) >> 2;
output[2] = base64_to_binary(input[2]) << 6 | base64_to_binary(input[3]);
input += 4;
output += 3;
}
switch(output_length % 3) {
case 1:
output[0] = base64_to_binary(input[0]) << 2 | base64_to_binary(input[1]) >> 4;
break;
case 2:
output[0] = base64_to_binary(input[0]) << 2 | base64_to_binary(input[1]) >> 4;
output[1] = base64_to_binary(input[1]) << 4 | base64_to_binary(input[2]) >> 2;
break;
}
return output_length;
}
#endif // ifndef

View File

@ -806,7 +806,8 @@ static int m_asstring(bvm *vm)
{
buf_impl attr = bytes_check_data(vm, 0);
check_ptr(vm, &attr);
be_pushnstring(vm, (const char*) attr.bufptr, attr.len);
size_t safe_len = strnlen((const char*) attr.bufptr, attr.len);
be_pushnstring(vm, (const char*) attr.bufptr, safe_len);
be_return(vm);
}

View File

@ -284,7 +284,8 @@ static void tr_string(blexer *lexer)
break;
}
}
lexer->buf.len = dst - lexbuf(lexer);
size_t len = dst - lexbuf(lexer);
lexer->buf.len = strnlen(lexbuf(lexer), len);
}
static int skip_newline(blexer *lexer)

View File

@ -354,3 +354,9 @@ assert(b.appendb64(c, 2) == bytes("AABBCC49673D3D"))
b = bytes("AABBCC")
assert(bytes().fromstring(bytes("11").tob64()) == bytes('45513D3D'))
assert(b.appendb64(c, 1, 1) == bytes("AABBCC45513D3D"))
#- asstring truncates if NULL is present -#
s=bytes("414243").asstring()
assert(size(s) == 3)
s=bytes("410000").asstring()
assert(size(s) == 1)

View File

@ -84,3 +84,6 @@ var malformed_numbers = [
for i : malformed_numbers
test_source(i, 'malformed number')
end
#- ensure that string literal with NULL character is truncated -#
assert(size('aa\000bb\000cc') == 2)

View File

@ -272,6 +272,7 @@
// Commands tasmota.ino
#define D_CMND_BACKLOG "Backlog"
#define D_CMND_JSON "Json"
#define D_CMND_JSON_PP "JsonPP"
#define D_CMND_DELAY "Delay"
#define D_CMND_NODELAY "NoDelay"
#define D_CMND_STATUS "Status"

View File

@ -16,9 +16,10 @@ class TASCONSOLE {
virtual void begin(uint32_t) = 0;
virtual void flush() = 0;
virtual size_t println() = 0;
virtual size_t print(char *) = 0;
virtual size_t printf(const char*, char *, const char*&, const char*&, const char*&) = 0;
virtual size_t printf(char *) = 0;
virtual size_t println(const char*) = 0;
virtual size_t print(char*) = 0;
virtual size_t printf(const char*, char*, const char*&, const char*&, const char*&) = 0;
virtual size_t printf(char*) = 0;
virtual size_t read() = 0;
virtual size_t write(uint8_t) = 0;
virtual size_t write(const uint8_t *buf, size_t size) = 0;
@ -49,7 +50,11 @@ public:
return object->println();
}
size_t print(char * string) {
size_t println(const char *string) {
return object->println(string);
}
size_t print(char *string) {
return object->print(string);
}
@ -96,10 +101,15 @@ public:
object.flush();
}
size_t println() override {
size_t println() override {
return object.println();
}
size_t print(char * string) override {
size_t println(const char *string) override {
return object.println(string);
}
size_t print(char *string) override {
return object.print(string);
}

View File

@ -252,9 +252,7 @@ typedef union {
uint32_t spare13 : 1; // bit 13
uint32_t spare14 : 1; // bit 14
uint32_t spare15 : 1; // bit 15
uint32_t spare16 : 1; // bit 16
uint32_t spare17 : 1; // bit 17
uint32_t spare18 : 1; // bit 18
uint32_t json_pretty_print : 3; // bit 16.18 (v14.6.0.1) - JSON pretty print log data no or indent
uint32_t dali_group_sliders : 5; // bit 19.23 (v14.3.0.3) - (DALI) Number of group sliders 0 to 16
uint32_t FTP_Mode : 2; // bit 24/25
uint32_t tariff_forced : 2; // bit 26/27 (v12.4.0.2) - Energy forced tariff : 0=tariff change on time, 1|2=tariff forced

View File

@ -22,6 +22,6 @@
#define TASMOTA_SHA_SHORT // Filled by Github sed
const uint32_t TASMOTA_VERSION = 0x0E050003; // 14.5.0.3
const uint32_t TASMOTA_VERSION = 0x0E060001; // 14.6.0.1
#endif // _TASMOTA_VERSION_H_

View File

@ -28,7 +28,7 @@
* Use online command StateText to translate ON, OFF, HOLD and TOGGLE.
* Use online command Prefix to translate cmnd, stat and tele.
*
* Updated until v9.4.0.1 - Last update 12.03.2025
* Updated until v9.4.0.1 - Last update 17.04.2025
\*********************************************************************/
#define LANGUAGE_MODULE_NAME // Enable to display "Module Generic" (ie Spanish), Disable to display "Generic Module" (ie English)
@ -52,7 +52,7 @@
// Common
#define D_ABSOLUTE_HUMIDITY "Umidità ass"
#define D_ADDRESS "Address"
#define D_ADDRESS "Indirizzo"
#define D_ADMIN "Admin"
#define D_AIR_QUALITY "Qualità dell'aria"
#define D_AP "AP" // Access Point

View File

@ -2611,6 +2611,95 @@ bool GetLog(uint32_t req_loglevel, uint32_t* index_p, char** entry_pp, size_t* l
return false;
}
bool LogDataJsonPrettyPrint(const char *log_line, uint32_t log_data_len, std::function<void(const char*, uint32_t)> println) {
// log_line:
// 14:49:36.123 MQTT: stat/wemos5/RESULT = {"POWER":"OFF"}
// 14:30:16.749-172/38 MQT: tele/atomlite3/INFO3 = {"Info3":{"RestartReason":"Vbat power on reset","BootCount":74}}
if (!Settings->mbflag2.json_pretty_print) { return false; } // [JsonPP] Number of indents
char *bch = (char*)memchr(log_line, '{', log_data_len);
if (!bch) { return false; } // No JSON data
uint32_t pos_brace = bch - log_line;
uint32_t cnt_brace = 0; // {}
uint32_t len_mxtime = strchr(log_line, ' ') - log_line +2;
uint32_t pos_value_pair = pos_brace;
uint32_t cnt_bracket = 0; // []
uint32_t cnt_Indent = 0; // indent
bool quotes = false; // ""
bool bracket_comma = false;
bool pls_print = false;
for (uint32_t i = pos_brace; i < log_data_len; i++) {
char curchar = log_line[i];
char nxtchar = log_line[i +1];
cnt_Indent = cnt_brace + cnt_bracket;
if (curchar == '{') {
cnt_brace++;
pls_print = true;
}
else if (cnt_brace) {
if (nxtchar == '}') {
pls_print = true;
}
if (curchar == '}') {
cnt_brace--;
if (cnt_brace) {
if (nxtchar != ',') {
pls_print = true;
cnt_Indent = cnt_brace + cnt_bracket;
}
} else {
pls_print = true;
cnt_Indent = 0;
}
}
else if (curchar == '[') {
cnt_bracket++;
if (nxtchar == '[') {
pls_print = true;
}
}
else if (curchar == ']') {
cnt_bracket--;
if (nxtchar == ',') {
bracket_comma = true;
}
else {
pls_print = true;
if ((nxtchar == ']') || (nxtchar == '}')) {
cnt_Indent = cnt_brace + cnt_bracket;
}
}
}
else if (curchar == '"') {
quotes ^= 1;
}
else if (curchar == ',') {
if (!quotes && (!cnt_bracket || bracket_comma)) {
bracket_comma = false;
pls_print = true;
}
}
}
if (pls_print) {
pls_print = false;
uint32_t len_id = (pos_brace == i) ? pos_brace +1 : len_mxtime;
uint32_t len_indent = cnt_Indent * Settings->mbflag2.json_pretty_print;
uint32_t len_value_pair = (i - pos_value_pair) +1;
uint32_t len_full = len_id + len_indent + len_value_pair +1;
char line[len_full]; // Known max value pair size is 152
strlcpy(line, log_line, len_id); // Repeat mxtime
sprintf(line, "%s%*s", line, len_indent, ""); // Add space indent
strncat(line, log_line + pos_value_pair, len_value_pair);
println(line, strlen(line)); // Callback for output
pos_value_pair = i +1;
}
}
return true;
}
uint32_t HighestLogLevel(void) {
uint32_t highest_loglevel = TasmotaGlobal.seriallog_level;
if (Settings->seriallog_level > highest_loglevel) { highest_loglevel = Settings->seriallog_level; }
@ -2660,7 +2749,9 @@ void AddLogData(uint32_t loglevel, const char* log_data, const char* log_data_pa
if ((loglevel <= TasmotaGlobal.seriallog_level) &&
(TasmotaGlobal.masterlog_level <= TasmotaGlobal.seriallog_level)) {
TasConsole.printf("%s%s%s%s\r\n", mxtime, log_data, log_data_payload, log_data_retained);
if (!Settings->mbflag2.json_pretty_print || !strchr(log_data_payload, '{')) {
TasConsole.printf("%s%s%s%s\r\n", mxtime, log_data, log_data_payload, log_data_retained);
}
}
if (!TasmotaGlobal.log_buffer) { return; } // Leave now if there is no buffer available
@ -2707,6 +2798,14 @@ void AddLogData(uint32_t loglevel, const char* log_data, const char* log_data_pa
// These calls fail to show initial logging
log_line += 2; // Skip log_buffer_pointer and loglevel
// 14:49:36.123 MQTT: stat/wemos5/RESULT = {"POWER":"OFF"}
// 14:30:16.749-172/38 MQT: tele/atomlite3/INFO3 = {"Info3":{"RestartReason":"Vbat power on reset","BootCount":74}}
if ((loglevel <= TasmotaGlobal.seriallog_level) &&
(TasmotaGlobal.masterlog_level <= TasmotaGlobal.seriallog_level)) {
LogDataJsonPrettyPrint(log_line, log_data_len, TasConsoleLDJsonPPCb);
}
#ifdef USE_SERIAL_BRIDGE
if (loglevel <= TasmotaGlobal.seriallog_level) {
SerialBridgeWrite(log_line, log_data_len);
@ -2723,6 +2822,10 @@ void AddLogData(uint32_t loglevel, const char* log_data, const char* log_data_pa
}
}
void TasConsoleLDJsonPPCb(const char* line, uint32_t len) {
TasConsole.println(line);
}
void AddLog(uint32_t loglevel, PGM_P formatP, ...) {
#ifdef ESP32
if (xPortInIsrContext()) {

View File

@ -29,18 +29,18 @@ const char kTasmotaCommands[] PROGMEM = "|" // No prefix
D_CMND_VOLTAGE_RESOLUTION "|" D_CMND_FREQUENCY_RESOLUTION "|" D_CMND_CURRENT_RESOLUTION "|" D_CMND_ENERGY_RESOLUTION "|" D_CMND_WEIGHT_RESOLUTION "|"
D_CMND_MODULE "|" D_CMND_MODULES "|" D_CMND_GPIO "|" D_CMND_GPIOREAD "|" D_CMND_GPIOS "|" D_CMND_TEMPLATE "|" D_CMND_PWM "|" D_CMND_PWMFREQUENCY "|" D_CMND_PWMRANGE "|"
D_CMND_BUTTONDEBOUNCE "|" D_CMND_SWITCHDEBOUNCE "|" D_CMND_SYSLOG "|" D_CMND_LOGHOST "|" D_CMND_LOGPORT "|"
#ifdef USE_UFILESYS
D_CMND_FILELOG "|"
#endif // USE_UFILESYS
D_CMND_SERIALBUFFER "|" D_CMND_SERIALSEND "|" D_CMND_BAUDRATE "|" D_CMND_SERIALCONFIG "|" D_CMND_SERIALDELIMITER "|"
D_CMND_IPADDRESS "|" D_CMND_NTPSERVER "|" D_CMND_AP "|" D_CMND_SSID "|" D_CMND_PASSWORD "|" D_CMND_HOSTNAME "|" D_CMND_WIFICONFIG "|" D_CMND_WIFI "|" D_CMND_DNSTIMEOUT "|"
D_CMND_DEVICENAME "|" D_CMND_FN "|" D_CMND_FRIENDLYNAME "|" D_CMND_SWITCHMODE "|" D_CMND_INTERLOCK "|" D_CMND_TELEPERIOD "|" D_CMND_RESET "|" D_CMND_TIME "|" D_CMND_TIMEZONE "|" D_CMND_TIMESTD "|"
D_CMND_TIMEDST "|" D_CMND_ALTITUDE "|" D_CMND_LEDPOWER "|" D_CMND_LEDSTATE "|" D_CMND_LEDMASK "|" D_CMND_LEDPWM_ON "|" D_CMND_LEDPWM_OFF "|" D_CMND_LEDPWM_MODE "|"
D_CMND_WIFIPOWER "|" D_CMND_TEMPOFFSET "|" D_CMND_HUMOFFSET "|" D_CMND_SPEEDUNIT "|" D_CMND_GLOBAL_TEMP "|" D_CMND_GLOBAL_HUM"|" D_CMND_GLOBAL_PRESS "|" D_CMND_SWITCHTEXT "|" D_CMND_WIFISCAN "|" D_CMND_WIFITEST "|"
D_CMND_ZIGBEE_BATTPERCENT "|"
D_CMND_DEVICENAME "|" D_CMND_FN "|" D_CMND_FRIENDLYNAME "|" D_CMND_SWITCHMODE "|" D_CMND_INTERLOCK "|" D_CMND_TELEPERIOD "|" D_CMND_RESET "|"
D_CMND_TIME "|" D_CMND_TIMEZONE "|" D_CMND_TIMESTD "|" D_CMND_TIMEDST "|" D_CMND_ALTITUDE "|"
D_CMND_LEDPOWER "|" D_CMND_LEDSTATE "|" D_CMND_LEDMASK "|" D_CMND_LEDPWM_ON "|" D_CMND_LEDPWM_OFF "|" D_CMND_LEDPWM_MODE "|"
D_CMND_WIFIPOWER "|" D_CMND_TEMPOFFSET "|" D_CMND_HUMOFFSET "|" D_CMND_SPEEDUNIT "|" D_CMND_GLOBAL_TEMP "|" D_CMND_GLOBAL_HUM"|" D_CMND_GLOBAL_PRESS "|" D_CMND_SWITCHTEXT "|"
D_CMND_WIFISCAN "|" D_CMND_WIFITEST "|" D_CMND_ZIGBEE_BATTPERCENT "|"
#ifdef USE_I2C
D_CMND_I2CSCAN "|" D_CMND_I2CDRIVER "|"
#endif
#ifdef USE_DEVICE_GROUPS
D_CMND_DEVGROUP_NAME "|"
#ifdef USE_DEVICE_GROUPS_SEND
@ -48,14 +48,20 @@ const char kTasmotaCommands[] PROGMEM = "|" // No prefix
#endif // USE_DEVICE_GROUPS_SEND
D_CMND_DEVGROUP_SHARE "|" D_CMND_DEVGROUPSTATUS "|" D_CMND_DEVGROUP_TIE "|"
#endif // USE_DEVICE_GROUPS
D_CMND_SETSENSOR "|" D_CMND_SENSOR "|" D_CMND_DRIVER "|" D_CMND_JSON
#ifdef USE_UFILESYS
D_CMND_FILELOG "|"
#endif // USE_UFILESYS
#ifdef ESP32
"|Info|"
"|Info|"
#if defined(SOC_TOUCH_VERSION_1) || defined(SOC_TOUCH_VERSION_2)
D_CMND_TOUCH_CAL "|" D_CMND_TOUCH_THRES "|"
#endif // ESP32 SOC_TOUCH_VERSION_1 or SOC_TOUCH_VERSION_2
D_CMND_CPU_FREQUENCY
#endif // ESP32
D_CMND_SETSENSOR "|" D_CMND_SENSOR "|" D_CMND_DRIVER "|" D_CMND_JSON "|" D_CMND_JSON_PP
#endif //FIRMWARE_MINIMAL
;
@ -72,18 +78,18 @@ void (* const TasmotaCommand[])(void) PROGMEM = {
&CmndVoltageResolution, &CmndFrequencyResolution, &CmndCurrentResolution, &CmndEnergyResolution, &CmndWeightResolution,
&CmndModule, &CmndModules, &CmndGpio, &CmndGpioRead, &CmndGpios, &CmndTemplate, &CmndPwm, &CmndPwmfrequency, &CmndPwmrange,
&CmndButtonDebounce, &CmndSwitchDebounce, &CmndSyslog, &CmndLoghost, &CmndLogport,
#ifdef USE_UFILESYS
&CmndFilelog,
#endif // USE_UFILESYS
&CmndSerialBuffer, &CmndSerialSend, &CmndBaudrate, &CmndSerialConfig, &CmndSerialDelimiter,
&CmndIpAddress, &CmndNtpServer, &CmndAp, &CmndSsid, &CmndPassword, &CmndHostname, &CmndWifiConfig, &CmndWifi, &CmndDnsTimeout,
&CmndDevicename, &CmndFriendlyname, &CmndFriendlyname, &CmndSwitchMode, &CmndInterlock, &CmndTeleperiod, &CmndReset, &CmndTime, &CmndTimezone, &CmndTimeStd,
&CmndTimeDst, &CmndAltitude, &CmndLedPower, &CmndLedState, &CmndLedMask, &CmndLedPwmOn, &CmndLedPwmOff, &CmndLedPwmMode,
&CmndWifiPower, &CmndTempOffset, &CmndHumOffset, &CmndSpeedUnit, &CmndGlobalTemp, &CmndGlobalHum, &CmndGlobalPress, &CmndSwitchText, &CmndWifiScan, &CmndWifiTest,
&CmndBatteryPercent,
&CmndDevicename, &CmndFriendlyname, &CmndFriendlyname, &CmndSwitchMode, &CmndInterlock, &CmndTeleperiod, &CmndReset,
&CmndTime, &CmndTimezone, &CmndTimeStd, &CmndTimeDst, &CmndAltitude,
&CmndLedPower, &CmndLedState, &CmndLedMask, &CmndLedPwmOn, &CmndLedPwmOff, &CmndLedPwmMode,
&CmndWifiPower, &CmndTempOffset, &CmndHumOffset, &CmndSpeedUnit, &CmndGlobalTemp, &CmndGlobalHum, &CmndGlobalPress, &CmndSwitchText,
&CmndWifiScan, &CmndWifiTest, &CmndBatteryPercent,
#ifdef USE_I2C
&CmndI2cScan, &CmndI2cDriver,
#endif
#ifdef USE_DEVICE_GROUPS
&CmndDevGroupName,
#ifdef USE_DEVICE_GROUPS_SEND
@ -91,14 +97,20 @@ void (* const TasmotaCommand[])(void) PROGMEM = {
#endif // USE_DEVICE_GROUPS_SEND
&CmndDevGroupShare, &CmndDevGroupStatus, &CmndDevGroupTie,
#endif // USE_DEVICE_GROUPS
&CmndSetSensor, &CmndSensor, &CmndDriver, &CmndJson
#ifdef USE_UFILESYS
&CmndFilelog,
#endif // USE_UFILESYS
#ifdef ESP32
, &CmndInfo,
&CmndInfo,
#if defined(SOC_TOUCH_VERSION_1) || defined(SOC_TOUCH_VERSION_2)
&CmndTouchCal, &CmndTouchThres,
#endif // ESP32 SOC_TOUCH_VERSION_1 or SOC_TOUCH_VERSION_2
&CmndCpuFrequency
&CmndCpuFrequency,
#endif // ESP32
&CmndSetSensor, &CmndSensor, &CmndDriver, &CmndJson, &CmndJsonPP
#endif //FIRMWARE_MINIMAL
};
@ -652,6 +664,35 @@ void CmndDelay(void) {
ResponseCmndNumber(bl_delay);
}
void CmndJsonPP(void) {
// JsonPP 0 - Disable JSON Pretty Print
// JsonPP 1..7 - Enable JSON Pretty Print with 1..7 indent spaces
// JsonPP <command> - If not enabled, enable JSON PP with 1 indent, execute command and restore JsonPP
// JsonPP Backlog <command>;... - If not enabled, enable JSON PP with 1 indent, execute command and restore JsonPP
if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 7)) {
Settings->mbflag2.json_pretty_print = XdrvMailbox.payload;
}
else if (XdrvMailbox.data_len) {
uint32_t last_json_pretty_print = Settings->mbflag2.json_pretty_print;
if (0 == Settings->mbflag2.json_pretty_print) {
Settings->mbflag2.json_pretty_print = 1; // Default 1 indent if not set
}
bool backlog = (strchr(XdrvMailbox.data, ';') != nullptr);
String cmnds = XdrvMailbox.data;
if (!last_json_pretty_print && backlog) {
cmnds += ";_JsonPP ";
cmnds += last_json_pretty_print; // Restore JsonPP after execution of backlog commands
}
ExecuteCommand((char*)cmnds.c_str(), SRC_IGNORE);
if (!last_json_pretty_print && !backlog) { // Restore JsonPP after execution of single command
Settings->mbflag2.json_pretty_print = last_json_pretty_print;
}
ResponseClear();
return;
}
ResponseCmndNumber(Settings->mbflag2.json_pretty_print);
}
void CmndPower(void)
{
if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= TasmotaGlobal.devices_present)) {

View File

@ -500,6 +500,7 @@ struct WEB {
bool upload_services_stopped = false;
bool reset_web_log_flag = false; // Reset web console log
bool initial_config = false;
bool cflg;
} Web;
/*********************************************************************************************/
@ -3737,18 +3738,24 @@ void HandleConsoleRefresh(void) {
index = 0;
Web.reset_web_log_flag = true;
}
bool cflg = (index);
Web.cflg = (index);
char* line;
size_t len;
while (GetLog(Settings->weblog_level, &index, &line, &len)) {
if (cflg) { WSContentSend_P(PSTR("\n")); }
WSContentSend(line, len -1);
cflg = true;
if (!LogDataJsonPrettyPrint(line, len -1, WSContentSendLDJsonPPCb)) {
WSContentSendLDJsonPPCb(line, len -1);
}
}
WSContentSend_P(PSTR("}1"));
WSContentEnd();
}
void WSContentSendLDJsonPPCb(const char* line, uint32_t len) {
if (Web.cflg) { WSContentSend_P(PSTR("\n")); }
WSContentSend(line, len);
Web.cflg = true;
}
/********************************************************************************************/
void HandleNotFound(void) {

View File

@ -65,8 +65,9 @@ enum XTrim1aModes { XYZT_NONE, XYZT_TRIM, XYZT_AUTO };
enum XReceiveModes { XYZD_NONE, XYZD_SOH, XYZD_BLK1, XYZD_BLK2, XYZD_DATA };
enum XYZFileSteps { XYZM_IDLE,
XYZM_SEND, XYZM_SEOT, XYZM_ACKT, XYZM_COMPLETE, XYZM_ERROR, XYZM_DONE,
XYZM_RECEIVE, XYZM_RCV_START, XYZM_RCV_EOT };
XYZM_SEND, XYZM_SND_ACK,
XYZM_RECEIVE, XYZM_RCV_START, XYZM_RCV_EOT,
XYZM_ERROR, XYZM_DONE };
enum XReceiveStates { XYZS_OK, XYZS_TIMEOUT, XYZS_EOT, XYZS_CAN, XYZS_OTHER, XYZS_CHECKSUM, XYZS_PACKET, XYZS_FILE };
@ -712,8 +713,7 @@ void XModemSendStart(void) {
XYZFile.step = XYZM_SEND;
AddLog(LOG_LEVEL_DEBUG, PSTR("XMD: Send started"));
} else {
XYZFile.step = XYZM_SEOT;
AddLog(LOG_LEVEL_DEBUG, PSTR("XMD: Send aborted"));
XYZModemInit();
}
}
@ -738,8 +738,8 @@ bool XYZModemLoop(void) {
// *** Send
case XYZM_SEND: { // *** Handle file send using XModem - upload
if (XYZModemFileAvailable()) {
if (XYZFile.byte_counter && !(XYZFile.byte_counter % 10240)) { // Show progress every 10kB
AddLog(LOG_LEVEL_DEBUG, PSTR("XMD: Progress %d kB"), XYZFile.byte_counter / 1024);
if (XYZFile.byte_counter && !(XYZFile.byte_counter % 10240)) { // Show progress every 10KB
AddLog(LOG_LEVEL_DEBUG, PSTR("XMD: Progress %d KB"), XYZFile.byte_counter / 1024);
}
if (!XYZModemSend(XYZModem.packet_no)) {
AddLog(LOG_LEVEL_DEBUG, PSTR("XMD: Packet %d send failed"), XYZModem.packet_no);
@ -748,21 +748,16 @@ bool XYZModemLoop(void) {
}
XYZModem.packet_no++;
} else {
XYZFile.step = XYZM_SEOT;
AddLog(LOG_LEVEL_DEBUG, PSTR("XMD: Send %d bytes"), XYZFile.size);
// Once the last block is ACKed by the target, the transfer should be finalized by an
// EOT (ASCII 0x04) packet from the source. This packet is confirmed via XModem ACK
// from the target.
XYZModemWrite(XYZM_EOT); // *** Send EOT
XYZModem.timeout = millis() + (30 * 1000); // Allow 30 seconds to receive EOT ACK
XYZFile.step = XYZM_SND_ACK;
}
break;
}
case XYZM_SEOT: { // *** Send EOT and wait for ACK
// Once the last block is ACKed by the target, the transfer should be finalized by an
// EOT (ASCII 0x04) packet from the source. This packet is confirmed via XModem ACK
// from the target.
XYZModemWrite(XYZM_EOT);
XYZModem.timeout = millis() + (30 * 1000); // Allow 30 seconds to receive EOT ACK
XYZFile.step = XYZM_ACKT;
break;
}
case XYZM_ACKT: { // *** Send EOT and wait for ACK
case XYZM_SND_ACK: { // *** Wait for ACK
// The ACK for the last XModem data packet may take much longer (1-3 seconds) than prior
// data packets to be received.
if (millis() > XYZModem.timeout) {
@ -778,9 +773,8 @@ bool XYZModemLoop(void) {
return true;
}
else if (XYZM_ACK == xmodem_ack) {
AddLog(LOG_LEVEL_DEBUG, PSTR("XMD: Successful"));
XYZModem.timeout = millis() + (30 * 1000); // Allow 30 seconds
XYZFile.byte_counter = 0;
// !!! Send an AddLog here as it previously would interfere with XModem protocol !!!
AddLog(LOG_LEVEL_INFO, PSTR("XMD: Send %d bytes succesful"), XYZFile.size);
XYZFile.step = XYZM_DONE;
}
}
@ -807,16 +801,15 @@ bool XYZModemLoop(void) {
break;
}
case XYZM_RCV_START: {
if (XYZFile.byte_counter && !(XYZFile.byte_counter % 10240)) { // Show progress every 10kB
AddLog(LOG_LEVEL_DEBUG, PSTR("XMD: Progress %d kB"), XYZFile.byte_counter / 1024);
if (XYZFile.byte_counter && !(XYZFile.byte_counter % 10240)) { // Show progress every 10KB
AddLog(LOG_LEVEL_DEBUG, PSTR("XMD: Progress %d KB"), XYZFile.byte_counter / 1024);
}
int result = XYZModemReceive(XYZModem.packet_no);
if (result) {
switch (result) {
case XYZS_EOT: {
AddLog(LOG_LEVEL_DEBUG, PSTR("XMD: Received %d bytes"), XYZFile.byte_counter);
AddLog(LOG_LEVEL_DEBUG, PSTR("XMD: Successful"));
XYZModemFileWriteEot(1);
AddLog(LOG_LEVEL_INFO, PSTR("XMD: Received %d bytes succesful"), XYZFile.byte_counter);
XYZFile.step = XYZM_DONE;
break;
}
@ -875,17 +868,6 @@ bool XYZModemLoop(void) {
break;
}
// *** Finish
case XYZM_COMPLETE: { // *** Wait for send complete
if (millis() > XYZModem.timeout) {
AddLog(LOG_LEVEL_DEBUG, PSTR("XMD: Timeout"));
XYZFile.step = XYZM_ERROR;
return true;
} else {
XYZFile.state = XYZM_COMPLETE;
XYZFile.step = XYZM_DONE;
}
break;
}
case XYZM_ERROR:
XYZFile.state = XYZM_ERROR;
AddLog(LOG_LEVEL_DEBUG, PSTR("XMD: Failed"));

View File

@ -97,6 +97,25 @@ void TelnetWriteColor(uint32_t color) {
}
}
void TelnetLDJsonPPCb(const char* line, uint32_t len) {
uint32_t textcolor = Telnet.color[Telnet.prompt];
uint32_t diffcolor = textcolor;
if ((textcolor >= 30) && (textcolor <= 37)) {
diffcolor += 60; // Highlight color
}
else if ((textcolor >= 90) && (textcolor <= 97)) {
diffcolor -= 60; // Lowlight color
}
char* time_end = (char*)memchr(line, ' ', len); // Find first word (usually 14:49:36.123-017)
uint32_t time_len = time_end - line;
TelnetWriteColor(diffcolor);
Telnet.client.write(line, time_len);
TelnetWriteColor(textcolor);
Telnet.client.write(time_end, len - time_len);
TelnetWriteColor(0);
Telnet.client.println();
}
void TelnetWrite(char *line, uint32_t len) {
if (Telnet.client) {
if (3 == Telnet.prompt) { // Print linefeed for non-requested data
@ -104,22 +123,10 @@ void TelnetWrite(char *line, uint32_t len) {
Telnet.client.println();
}
// line = 14:49:36.123-017 MQTT: stat/wemos5/RESULT = {"POWER":"OFF"}
uint32_t textcolor = Telnet.color[Telnet.prompt];
uint32_t diffcolor = textcolor;
if ((textcolor >= 30) && (textcolor <= 37)) {
diffcolor += 60; // Highlight color
if (!LogDataJsonPrettyPrint(line, len, TelnetLDJsonPPCb)) {
TelnetLDJsonPPCb(line, len);
}
else if ((textcolor >= 90) && (textcolor <= 97)) {
diffcolor -= 60; // Lowlight color
}
char* time_end = (char*)memchr(line, ' ', len); // Find first word (usually 14:49:36.123-017)
uint32_t time_len = time_end - line;
TelnetWriteColor(diffcolor);
Telnet.client.write(line, time_len);
TelnetWriteColor(textcolor);
Telnet.client.write(time_end, len - time_len);
TelnetWriteColor(0);
Telnet.client.println();
}
}
@ -182,10 +189,24 @@ void TelnetLoop(void) {
}
// Input keyboard data
bool telnet_iac = false;
while (Telnet.client.available()) {
yield();
uint8_t in_byte = Telnet.client.read();
if (telnet_iac) {
telnet_iac = false;
if (in_byte != 0xFF) {
// Process telnet Interpret as Command (IAC) codes
AddLog(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_TELNET "IAC %d"), in_byte);
continue;
}
}
else if (0xFF == in_byte) { // Telnet Interpret as Command (IAC)
telnet_iac = true;
continue;
}
#ifdef USE_XYZMODEM
if (XYZModemWifiClientStart(&Telnet.client, in_byte)) { return; }
#endif // USE_XYZMODEM