Merge remote-tracking branch 'origin/dev2' into dev

This commit is contained in:
proddy
2023-07-30 22:26:15 +02:00
117 changed files with 3054 additions and 1823 deletions

17
.vscode/settings.json vendored
View File

@@ -9,7 +9,7 @@
}, },
"eslint.nodePath": "interface/.yarn/sdks", "eslint.nodePath": "interface/.yarn/sdks",
"eslint.workingDirectories": ["interface"], "eslint.workingDirectories": ["interface"],
"prettier.prettierPath": "interface/.yarn/sdks/prettier/index.js", "prettier.prettierPath": "",
"typescript.tsdk": "interface/.yarn/sdks/typescript/lib", "typescript.tsdk": "interface/.yarn/sdks/typescript/lib",
"typescript.enablePromptUseWorkspaceTsdk": true, "typescript.enablePromptUseWorkspaceTsdk": true,
"files.associations": { "files.associations": {
@@ -28,5 +28,18 @@
"utility": "cpp", "utility": "cpp",
"string": "cpp", "string": "cpp",
"string_view": "cpp" "string_view": "cpp"
} },
"todo-tree.filtering.excludeGlobs": [
"**/vendor/**",
"**/node_modules/**",
"**/dist/**",
"**/bower_components/**",
"**/build/**",
"**/.vscode/**",
"**/.github/**",
"**/_output/**",
"**/*.min.*",
"**/*.map",
"**/ArduinoJson/**"
]
} }

21
.vscode/tasks.json vendored
View File

@@ -4,20 +4,15 @@
"version": "2.0.0", "version": "2.0.0",
"tasks": [ "tasks": [
{ {
"label": "PlatformIO: Execute EMS-ESP (standalone)",
"type": "shell", "type": "shell",
"command": "./.pio/build/standalone/program", "label": "build standalone emsesp",
"linux": { "command": "make",
"options": { "args": [],
"env": { "problemMatcher": ["$gcc"],
// Workaround for sdl2 `-m32` crash "group": {
// https://bugs.launchpad.net/ubuntu/+source/libsdl2/+bug/1775067/comments/7 "kind": "build",
"DBUS_FATAL_WARNINGS": "0" "isDefault": true
} }
}
},
"dependsOn": ["PlatformIO: Build EMS-ESP (standalone)"],
"problemMatcher": []
} }
] ]
} }

View File

@@ -4,32 +4,52 @@
## **IMPORTANT! BREAKING CHANGES** ## **IMPORTANT! BREAKING CHANGES**
There are breaking changes in 3.6.0. Please read carefully before applying the update. There are breaking changes between 3.5.x and earlier versions of 3.6.0. Please read carefully before applying the update.
- The sensors have been renamed. `dallassensor` is now `temperaturesensor` in MQTT and `ts` in the Customizations file. Also `analogs` is now `analogsensor` in MQTT and `as` in the Customizations file. If you have customizations, make backup first using the Download option and rename the JSON arrays to `as` and `ts` respectively. Also removed any MQTT topics that start with `dallassensor` using something like MQTTExplorer. - The sensors have been renamed. `dallassensor` is now `temperaturesensor` in the MQTT topic and named `ts` in the Customizations file. Likewise `analogs` is now `analogsensor` in MQTT and called `as` in the Customizations file. If you have previous customizations you will need to manually update by downloading, changing the JSON file and uploading. It's also recommended cleaning up any old MQTT topics from your broker using an application like MQTTExplorer.
- The format of the Custom Entities has changed, so you will need to manually re-create them.
## Added ## Added
- Workaround for better Domoticz MQTT intergration? [#904](https://github.com/emsesp/EMS-ESP32/issues/904) - Workaround for better Domoticz MQTT intergration? [#904](https://github.com/emsesp/EMS-ESP32/issues/904)
- Show MAC address without connecting to network enhancement [#933](https://github.com/emsesp/EMS-ESP32/issues/933)
- Warn user in WebUI of unsaved changes [#911](https://github.com/emsesp/EMS-ESP32/issues/911) - Warn user in WebUI of unsaved changes [#911](https://github.com/emsesp/EMS-ESP32/issues/911)
- Detect old Tado thermostat, device-id 0x19, no entities - Detect old Tado thermostat, device-id 0x19, no entities
- Some more HM200 entities [#500](https://github.com/emsesp/EMS-ESP32/issues/500) - Some more HM200 entities [#500](https://github.com/emsesp/EMS-ESP32/issues/500)
- Custom Scheduler [#701](https://github.com/emsesp/EMS-ESP32/issues/701) - Added Scheduler [#701](https://github.com/emsesp/EMS-ESP32/issues/701)
- Custom Entities read from EMS bus - Added Custom Entities read/write from EMS bus
- Build S3 binary with github actions - Build S3 binary with github actions
- Greenstar HIU [#1158](https://github.com/emsesp/EMS-ESP32/issues/1158) - Greenstar HIU [#1158](https://github.com/emsesp/EMS-ESP32/issues/1158)
- AM200 code 10 [#1161](https://github.com/emsesp/EMS-ESP32/issues/1161) - AM200 code 10 [#1161](https://github.com/emsesp/EMS-ESP32/issues/1161)
- Ventilation device [#1172](https://github.com/emsesp/EMS-ESP32/issues/1172) - Ventilation device (Logavent HRV176) [#1172](https://github.com/emsesp/EMS-ESP32/issues/1172)
- Turn ETH off on wifi connect [#1167](https://github.com/emsesp/EMS-ESP32/issues/1167) - Turn ETH off on wifi connect [#1167](https://github.com/emsesp/EMS-ESP32/issues/1167)
- Support for multiple EMS-ESPs with HA [#1196](https://github.com/emsesp/EMS-ESP32/issues/1196) - Support for multiple EMS-ESPs with HA [#1196](https://github.com/emsesp/EMS-ESP32/issues/1196)
- Italian translation [#1199](https://github.com/emsesp/EMS-ESP32/issues/1199) - Italian translation [#1199](https://github.com/emsesp/EMS-ESP32/issues/1199)
- Turkish language support [#1076](https://github.com/emsesp/EMS-ESP32/issues/1076)
- Buderus GB182 - HC1 mode change not work bug [#1193](https://github.com/emsesp/EMS-ESP32/issues/1193)
- Minimal flow temperature enhancement [#1192](https://github.com/emsesp/EMS-ESP32/issues/1192)
- Roomtemperature Switching Difference enhancement [#1191](https://github.com/emsesp/EMS-ESP32/issues/1191)
- Dew Point Temperature Difference enhancement [#1190](https://github.com/emsesp/EMS-ESP32/issues/1190)
- Control of heating circuit mode enhancement [#1187](https://github.com/emsesp/EMS-ESP32/issues/1187)
- Warn user in WebUI of unsaved changes enhancement [#911](https://github.com/emsesp/EMS-ESP32/issues/911)
- Create safebuild app to fit into factory partition to give ESP32 more flash memory enhancement [#608](https://github.com/emsesp/EMS-ESP32/issues/608)
- Support ESP32 S2, C3 mini and S3 [#605](https://github.com/emsesp/EMS-ESP32/issues/605)
- Support Buderus AM200 [#1161](https://github.com/emsesp/EMS-ESP32/issues/1161)
- Custom telegram handler [#1155](https://github.com/emsesp/EMS-ESP32/issues/1155)
- Added support for TLS in MQTT (ESP32-S3 only) [#1178](https://github.com/emsesp/EMS-ESP32/issues/1178)
- Boardprofile BBQKees Gateway S3
- Custom entity type RAW [#1212](https://github.com/emsesp/EMS-ESP32/discussions/1212)
- API command response [#1212](https://github.com/emsesp/EMS-ESP32/discussions/1212)
## Fixed ## Fixed
- HA-discovery for analog sensor commands [#1035](https://github.com/emsesp/EMS-ESP32/issues/1035) - HA-discovery for analog sensor commands [#1035](https://github.com/emsesp/EMS-ESP32/issues/1035)
- Enum order of RC3x nofrost mode - Enum order of RC3x nofrost mode
- Heartbeat interval - Heartbeat interval
- Exhaust temperature always zero on GB125/MC110/RC310 bug [#1147](https://github.com/emsesp/EMS-ESP32/issues/1147)
- thermostat modetype is not changing when mode changes (e.g. to night) bugSomething isn't working [#1098](https://github.com/emsesp/EMS-ESP32/issues/1098)
- NTP: cant apply changed timezone [#1182](https://github.com/emsesp/EMS-ESP32/issues/1182)
- Missing Status of VS1 for Buderus SM200 enhancement [#1034](https://github.com/emsesp/EMS-ESP32/issues/1034)
- Allowed gpios for S3
## Changed ## Changed
@@ -38,9 +58,13 @@ There are breaking changes in 3.6.0. Please read carefully before applying the u
- Write repeated selflowtemp if tx-queue is empty without verify [#954](https://github.com/emsesp/EMS-ESP32/issues/954) - Write repeated selflowtemp if tx-queue is empty without verify [#954](https://github.com/emsesp/EMS-ESP32/issues/954)
- HA discovery recreate after disconnect by device [#1067](https://github.com/emsesp/EMS-ESP32/issues/1067) - HA discovery recreate after disconnect by device [#1067](https://github.com/emsesp/EMS-ESP32/issues/1067)
- File upload: check flash size (overflow) instead of filesize - File upload: check flash size (overflow) instead of filesize
- Improved HA Discovery so previous configs no longer need to be removed when starting [#1077](https://github.com/emsesp/EMS-ESP32/pull/1077) (thanks @pswid!) - Improved HA Discovery so previous configs no longer need to be removed when starting [#1077](https://github.com/emsesp/EMS-ESP32/pull/1077) (thanks @pswid)
- Enlarge UART-Stack to 2,5k - Enlarge UART-Stack to 2,5k
- Retry timeout for Mqtt-QOS1/2 10seconds - Retry timeout for Mqtt-QOS1/2 10seconds
- Optimize WebUI rendering when using Dialog Boxes [#1116](https://github.com/emsesp/EMS-ESP32/issues/1116) - Optimize WebUI rendering when using Dialog Boxes [#1116](https://github.com/emsesp/EMS-ESP32/issues/1116)
- Optimize Web libraries to reduce bundle size (3.6.x) [#1112](https://github.com/emsesp/EMS-ESP32/issues/1112) - Optimize Web libraries to reduce bundle size (3.6.x) [#1112](https://github.com/emsesp/EMS-ESP32/issues/1112)
- Use [espMqttClient](https://github.com/bertmelis/espMqttClient) with integrated queue [#1178](https://github.com/emsesp/EMS-ESP32/issues/1178) - Use [espMqttClient](https://github.com/bertmelis/espMqttClient) with integrated queue [#1178](https://github.com/emsesp/EMS-ESP32/issues/1178)
- Move Sensors from Web dashboard to it's own tab enhancement [#1170](https://github.com/emsesp/EMS-ESP32/issues/1170)
- Optimize WebUI dashboard data [#1169](https://github.com/emsesp/EMS-ESP32/issues/1169)
- Replace React core library with Preact to save on memory footprint
- Response to `system/send` raw reads gives combined data for telegrams with more parts

View File

@@ -17,9 +17,9 @@ MAKEFLAGS+="j "
#TARGET := $(notdir $(CURDIR)) #TARGET := $(notdir $(CURDIR))
TARGET := emsesp TARGET := emsesp
BUILD := build BUILD := build
SOURCES := src src/* lib_standalone lib/uuid-common/src lib/uuid-console/src lib/uuid-log/src src/devices lib/ArduinoJson/src lib/PButton lib/semver SOURCES := src src/* lib_standalone lib/uuid-common/src lib/uuid-console/src lib/uuid-log/src src/devices lib/ArduinoJson/src lib/PButton lib/semver lib/espMqttClient/src lib/espMqttClient/src/*
INCLUDES := src lib_standalone lib/ArduinoJson/src lib/uuid-common/src lib/uuid-console/src lib/uuid-log/src lib/uuid-telnet/src lib/uuid-syslog/src lib/semver lib/* src/devices INCLUDES := src lib_standalone lib/espMqttClient/src lib/espMqttClient/src/Transport lib/ArduinoJson/src lib/uuid-common/src lib/uuid-console/src lib/uuid-log/src lib/uuid-telnet/src lib/uuid-syslog/src lib/semver lib/* src/devices
LIBRARIES := LIBRARIES :=
CPPCHECK = cppcheck CPPCHECK = cppcheck
# CHECKFLAGS = -q --force --std=c++17 # CHECKFLAGS = -q --force --std=c++17
@@ -28,16 +28,18 @@ CHECKFLAGS = -q --force --std=c++11
#---------------------------------------------------------------------- #----------------------------------------------------------------------
# Languages Standard # Languages Standard
#---------------------------------------------------------------------- #----------------------------------------------------------------------
# C_STANDARD := -std=c17 C_STANDARD := -std=c17
# CXX_STANDARD := -std=c++17 # CXX_STANDARD := -std=c++17
C_STANDARD := -std=c11 CXX_STANDARD := -std=gnu++11
CXX_STANDARD := -std=c++11
# C_STANDARD := -std=c11
# CXX_STANDARD := -std=c++11
#---------------------------------------------------------------------- #----------------------------------------------------------------------
# Defined Symbols # Defined Symbols
#---------------------------------------------------------------------- #----------------------------------------------------------------------
DEFINES += -DARDUINOJSON_ENABLE_STD_STRING=1 -DARDUINOJSON_ENABLE_PROGMEM=1 -DARDUINOJSON_ENABLE_ARDUINO_STRING -DARDUINOJSON_USE_DOUBLE=0 -DARDUINO DEFINES += -DARDUINOJSON_ENABLE_STD_STRING=1 -DARDUINOJSON_ENABLE_PROGMEM=1 -DARDUINOJSON_ENABLE_ARDUINO_STRING -DARDUINOJSON_USE_DOUBLE=0
DEFINES += -DEMSESP_DEBUG -DEMSESP_STANDALONE -DEMSESP_TEST DEFINES += -DEMSESP_DEBUG -DEMSESP_STANDALONE -DEMSESP_TEST -D__linux__ -DEMC_RX_BUFFER_SIZE=1500
DEFINES += $(ARGS) DEFINES += $(ARGS)
DEFAULTS = -DEMSESP_DEFAULT_LOCALE=\"en\" -DEMSESP_DEFAULT_TX_MODE=8 -DEMSESP_DEFAULT_VERSION=\"3.6.0-dev\" -DEMSESP_DEFAULT_BOARD_PROFILE=\"S32\" DEFAULTS = -DEMSESP_DEFAULT_LOCALE=\"en\" -DEMSESP_DEFAULT_TX_MODE=8 -DEMSESP_DEFAULT_VERSION=\"3.6.0-dev\" -DEMSESP_DEFAULT_BOARD_PROFILE=\"S32\"
@@ -52,7 +54,7 @@ CSOURCES := $(foreach dir,$(SOURCES),$(wildcard $(dir)/*.c))
CXXSOURCES := $(foreach dir,$(SOURCES),$(wildcard $(dir)/*.cpp)) CXXSOURCES := $(foreach dir,$(SOURCES),$(wildcard $(dir)/*.cpp))
OBJS := $(patsubst %,$(BUILD)/%.o,$(basename $(CSOURCES)) $(basename $(CXXSOURCES)) ) OBJS := $(patsubst %,$(BUILD)/%.o,$(basename $(CSOURCES)) $(basename $(CXXSOURCES)) )
DEPS := $(patsubst %,$(BUILD)/%.d,$(basename $(CSOURCES)) $(basename $(CXXSOURCES)) ) DEPS := $(patsubst %,$(BUILD)/%.d,$(basename $(CSOURCES)) $(basename $(CXXSOURCES)) )
INCLUDE += $(addprefix -I,$(foreach dir,$(INCLUDES), $(wildcard $(dir)))) INCLUDE += $(addprefix -I,$(foreach dir,$(INCLUDES), $(wildcard $(dir))))
INCLUDE += $(addprefix -I,$(foreach dir,$(LIBRARIES),$(wildcard $(dir)/include))) INCLUDE += $(addprefix -I,$(foreach dir,$(LIBRARIES),$(wildcard $(dir)/include)))
@@ -79,7 +81,7 @@ CPPFLAGS += -g3
CPPFLAGS += -Os CPPFLAGS += -Os
CFLAGS += $(CPPFLAGS) CFLAGS += $(CPPFLAGS)
CFLAGS += -Wall -Wextra -Werror -Wswitch-enum -Wno-unused-parameter -Wno-inconsistent-missing-override -Wno-unused-lambda-capture CFLAGS += -Wall -Wextra -Werror -Wswitch-enum -Wno-unused-parameter -Wno-inconsistent-missing-override -Wno-missing-braces -Wno-unused-lambda-capture
CXXFLAGS += $(CFLAGS) -MMD CXXFLAGS += $(CFLAGS) -MMD

View File

@@ -1,6 +1,6 @@
# Name, Type, SubType, Offset, Size, Flags # Name, Type, SubType, Offset, Size, Flags
nvs, data, nvs, 0x9000, 0x5000, nvs, data, nvs, 0x9000, 0x5000,
otadata, data, ota, , 0x2000, otadata, data, ota, , 0x2000,
app1, app, ota_1, , 0x140000,
app0, app, ota_0, , 0x2A0000, app0, app, ota_0, , 0x2A0000,
app1, app, ota_1, , 0x140000,
spiffs, data, spiffs, , 64K, spiffs, data, spiffs, , 64K,
1 # Name Type SubType Offset Size Flags
2 nvs data nvs 0x9000 0x5000
3 otadata data ota 0x2000
app1 app ota_1 0x140000
4 app0 app ota_0 0x2A0000
5 app1 app ota_1 0x140000
6 spiffs data spiffs 64K

View File

@@ -41,6 +41,7 @@
"@typescript-eslint/consistent-type-definitions": ["off", "type"], "@typescript-eslint/consistent-type-definitions": ["off", "type"],
"@typescript-eslint/explicit-function-return-type": "off", "@typescript-eslint/explicit-function-return-type": "off",
"@typescript-eslint/no-unsafe-call": "off", "@typescript-eslint/no-unsafe-call": "off",
"@typescript-eslint/no-unsafe-enum-comparison": "off",
"@typescript-eslint/no-unsafe-assignment": "off", "@typescript-eslint/no-unsafe-assignment": "off",
"@typescript-eslint/no-unsafe-return": "off", "@typescript-eslint/no-unsafe-return": "off",
"@typescript-eslint/no-unsafe-member-access": "off", "@typescript-eslint/no-unsafe-member-access": "off",

View File

@@ -1,5 +1,5 @@
{ {
"adapter": "react", "adapter": "react",
"baseLocale": "pl", "baseLocale": "pl",
"$schema": "https://unpkg.com/typesafe-i18n@5.24.3/schema/typesafe-i18n.json" "$schema": "https://unpkg.com/typesafe-i18n@5.25.1/schema/typesafe-i18n.json"
} }

View File

@@ -1,4 +1,4 @@
<!DOCTYPE html> <!doctype html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />

View File

@@ -20,55 +20,56 @@
}, },
"dependencies": { "dependencies": {
"@alova/adapter-xhr": "^1.0.1", "@alova/adapter-xhr": "^1.0.1",
"@alova/scene-react": "^1.1.3",
"@emotion/react": "^11.11.1", "@emotion/react": "^11.11.1",
"@emotion/styled": "^11.11.0", "@emotion/styled": "^11.11.0",
"@mui/icons-material": "^5.11.16", "@mui/icons-material": "^5.14.1",
"@mui/material": "^5.13.6", "@mui/material": "^5.14.2",
"@preact/compat": "^17.1.2",
"@table-library/react-table-library": "4.1.4", "@table-library/react-table-library": "4.1.4",
"@types/lodash-es": "^4.17.7", "@types/lodash-es": "^4.17.8",
"@types/node": "^20.3.3", "@types/node": "^20.4.5",
"@types/react": "^18.2.14", "@types/react": "^18.2.17",
"@types/react-dom": "^18.2.6", "@types/react-dom": "^18.2.7",
"@types/react-router-dom": "^5.3.3", "@types/react-router-dom": "^5.3.3",
"alova": "^2.9.1", "alova": "^2.9.3",
"async-validator": "^4.2.5", "async-validator": "^4.2.5",
"history": "^5.3.0", "history": "^5.3.0",
"jwt-decode": "^3.1.2", "jwt-decode": "^3.1.2",
"lodash-es": "^4.17.21", "lodash-es": "^4.17.21",
"mime-types": "^2.1.35", "mime-types": "^2.1.35",
"preact": "^10.16.0",
"react": "latest", "react": "latest",
"react-dom": "latest", "react-dom": "latest",
"react-dropzone": "^14.2.3", "react-dropzone": "^14.2.3",
"react-icons": "^4.10.1", "react-icons": "^4.10.1",
"react-router-dom": "^6.14.1", "react-router-dom": "^6.14.2",
"react-toastify": "^9.1.3", "react-toastify": "^9.1.3",
"sockette": "^2.0.6", "sockette": "^2.0.6",
"typesafe-i18n": "^5.24.3", "typesafe-i18n": "^5.25.1",
"typescript": "^5.1.6" "typescript": "^5.1.6"
}, },
"devDependencies": { "devDependencies": {
"@types/mime-types": "^2", "@preact/preset-vite": "^2.5.0",
"@typescript-eslint/eslint-plugin": "^5.60.1", "@typescript-eslint/eslint-plugin": "^6.2.0",
"@typescript-eslint/parser": "^5.60.1", "@typescript-eslint/parser": "^6.2.0",
"@vitejs/plugin-react-swc": "^3.3.2", "cspell": "^6.31.2",
"eslint": "^8.44.0", "eslint": "^8.46.0",
"eslint-config-airbnb": "^19.0.4", "eslint-config-airbnb": "^19.0.4",
"eslint-config-airbnb-typescript": "^17.0.0", "eslint-config-airbnb-typescript": "^17.1.0",
"eslint-config-prettier": "^8.8.0", "eslint-config-prettier": "^8.9.0",
"eslint-import-resolver-typescript": "^3.5.5", "eslint-import-resolver-typescript": "^3.5.5",
"eslint-plugin-autofix": "^1.1.0", "eslint-plugin-autofix": "^1.1.0",
"eslint-plugin-import": "^2.27.5", "eslint-plugin-import": "^2.28.0",
"eslint-plugin-jsx-a11y": "^6.7.1", "eslint-plugin-jsx-a11y": "^6.7.1",
"eslint-plugin-prettier": "^4.2.1", "eslint-plugin-prettier": "alpha",
"eslint-plugin-react": "^7.32.2", "eslint-plugin-react": "^7.33.0",
"eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-react-hooks": "^4.6.0",
"nodemon": "^2.0.22", "nodemon": "^3.0.1",
"npm-run-all": "^4.1.5", "npm-run-all": "^4.1.5",
"prettier": "^2.8.8", "prettier": "^3.0.0",
"rollup-plugin-visualizer": "^5.9.2", "rollup-plugin-visualizer": "^5.9.2",
"terser": "^5.18.2", "terser": "^5.19.2",
"vite": "^4.3.9", "vite": "^4.4.7",
"vite-plugin-svgr": "^3.2.0", "vite-plugin-svgr": "^3.2.0",
"vite-tsconfig-paths": "^4.2.0" "vite-tsconfig-paths": "^4.2.0"
}, },

View File

@@ -1,5 +1,5 @@
/* /*
* Uses font-size 400 (normal) only and Latin (plus extra unicode chars) to keep flash memory to a minimun * Uses font-size 400 (normal) only and Latin (plus extra unicode chars) to keep flash memory to a minimum
* View fonts on https://fonts.google.com/ * View fonts on https://fonts.google.com/
* Download woff2 using e.g. https://fonts.googleapis.com/css2?family=Lato or https://fonts.googleapis.com/css2?family=Roboto * Download woff2 using e.g. https://fonts.googleapis.com/css2?family=Lato or https://fonts.googleapis.com/css2?family=Roboto
*/ */
@@ -8,7 +8,10 @@
font-style: normal; font-style: normal;
font-weight: 400; font-weight: 400;
/* src: url(https://fonts.gstatic.com/s/roboto/v30/KFOmCnqEu92Fr1Mu4mxK.woff2) format('woff2'); */ /* src: url(https://fonts.gstatic.com/s/roboto/v30/KFOmCnqEu92Fr1Mu4mxK.woff2) format('woff2'); */
src: local('Roboto'), local('Roboto-Regular'), url(../fonts/re.woff2) format('woff2'); src:
local('Roboto'),
local('Roboto-Regular'),
url(../fonts/re.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0104-0107, U+0118-0119, U+011E-011F, U+0130-0131, U+0141-0144, U+0152-0153, U+015A-015B, unicode-range: U+0000-00FF, U+0104-0107, U+0118-0119, U+011E-011F, U+0130-0131, U+0141-0144, U+0152-0153, U+015A-015B,
U+015E-015F, U+0179-017C, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+015E-015F, U+0179-017C, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193,
U+2212, U+2215, U+FEFF, U+FFFD; U+2212, U+2215, U+FEFF, U+FFFD;

View File

@@ -4,6 +4,7 @@ import { ToastContainer, Slide } from 'react-toastify';
import 'react-toastify/dist/ReactToastify.min.css'; import 'react-toastify/dist/ReactToastify.min.css';
import { localStorageDetector } from 'typesafe-i18n/detectors'; import { localStorageDetector } from 'typesafe-i18n/detectors';
import { FeaturesLoader } from './contexts/features';
import type { FC } from 'react'; import type { FC } from 'react';
import AppRouting from 'AppRouting'; import AppRouting from 'AppRouting';
import CustomTheme from 'CustomTheme'; import CustomTheme from 'CustomTheme';
@@ -26,7 +27,9 @@ const App: FC = () => {
return ( return (
<TypesafeI18n locale={detectedLocale}> <TypesafeI18n locale={detectedLocale}>
<CustomTheme> <CustomTheme>
<AppRouting /> <FeaturesLoader>
<AppRouting />
</FeaturesLoader>
<ToastContainer <ToastContainer
position="bottom-left" position="bottom-left"
autoClose={3000} autoClose={3000}

View File

@@ -13,7 +13,6 @@ import Security from 'framework/security/Security';
import System from 'framework/system/System'; import System from 'framework/system/System';
const AuthenticatedRouting: FC = () => ( const AuthenticatedRouting: FC = () => (
// TODO not sure if this is needed, to redirect on 401. If so add incerceptor to Alova
// const location = useLocation(); // const location = useLocation();
// const navigate = useNavigate(); // const navigate = useNavigate();
// const handleApiResponseError = useCallback( // const handleApiResponseError = useCallback(

View File

@@ -3,6 +3,7 @@ import { Box, Fab, Paper, Typography, Button } from '@mui/material';
import { useRequest } from 'alova'; import { useRequest } from 'alova';
import { useContext, useState } from 'react'; import { useContext, useState } from 'react';
import { toast } from 'react-toastify'; import { toast } from 'react-toastify';
import { FeaturesContext } from './contexts/features';
import type { ValidateFieldsError } from 'async-validator'; import type { ValidateFieldsError } from 'async-validator';
import type { Locales } from 'i18n/i18n-types'; import type { Locales } from 'i18n/i18n-types';
@@ -33,6 +34,8 @@ const SignIn: FC = () => {
const { LL, setLocale, locale } = useContext(I18nContext); const { LL, setLocale, locale } = useContext(I18nContext);
const { features } = useContext(FeaturesContext);
const [signInRequest, setSignInRequest] = useState<SignInRequest>({ const [signInRequest, setSignInRequest] = useState<SignInRequest>({
username: '', username: '',
password: '' password: ''
@@ -55,7 +58,7 @@ const SignIn: FC = () => {
const signIn = async () => { const signIn = async () => {
await callSignIn(signInRequest).catch((event) => { await callSignIn(signInRequest).catch((event) => {
if (event.message === 'Unauthorized') { if (event.message === 'Unauthorized') {
toast.warn(LL.INVALID_LOGIN()); toast.warning(LL.INVALID_LOGIN());
} else { } else {
toast.error(LL.ERROR() + ' ' + event.message); toast.error(LL.ERROR() + ' ' + event.message);
} }
@@ -107,6 +110,7 @@ const SignIn: FC = () => {
})} })}
> >
<Typography variant="h4">{PROJECT_NAME}</Typography> <Typography variant="h4">{PROJECT_NAME}</Typography>
<Typography variant="subtitle2">{features.version}</Typography>
<Box <Box
mt={2} mt={2}
mb={2} mb={2}
@@ -117,14 +121,14 @@ const SignIn: FC = () => {
} }
}} }}
> >
<Button size="small" variant={locale === 'en' ? 'contained' : 'outlined'} onClick={() => selectLocale('en')}>
<GBflag style={{ width: 24 }} />
&nbsp;EN
</Button>
<Button size="small" variant={locale === 'de' ? 'contained' : 'outlined'} onClick={() => selectLocale('de')}> <Button size="small" variant={locale === 'de' ? 'contained' : 'outlined'} onClick={() => selectLocale('de')}>
<DEflag style={{ width: 24 }} /> <DEflag style={{ width: 24 }} />
&nbsp;DE &nbsp;DE
</Button> </Button>
<Button size="small" variant={locale === 'en' ? 'contained' : 'outlined'} onClick={() => selectLocale('en')}>
<GBflag style={{ width: 24 }} />
&nbsp;EN
</Button>
<Button size="small" variant={locale === 'fr' ? 'contained' : 'outlined'} onClick={() => selectLocale('fr')}> <Button size="small" variant={locale === 'fr' ? 'contained' : 'outlined'} onClick={() => selectLocale('fr')}>
<FRflag style={{ width: 24 }} /> <FRflag style={{ width: 24 }} />
&nbsp;FR &nbsp;FR

View File

@@ -46,8 +46,7 @@ export const alovaInstance = createAlova({
} }
// Interceptor for request failure. This interceptor will be entered when the request is wrong. // Interceptor for request failure. This interceptor will be entered when the request is wrong.
// TODO how best to handle http errors like 401 (unauthorized) // http errors like 401 (unauthorized) are handled either in the methods or AuthenticatedRouting()
// but I think this is handled correctly in AppRouting? See AuthenticatedRouting()
// onError: (error, method) => { // onError: (error, method) => {
// alert(error.message); // alert(error.message);
// } // }

View File

@@ -6,7 +6,8 @@ export const readNetworkStatus = () => alovaInstance.Get<NetworkStatus>('/rest/n
export const scanNetworks = () => alovaInstance.Get('/rest/scanNetworks'); export const scanNetworks = () => alovaInstance.Get('/rest/scanNetworks');
export const listNetworks = () => export const listNetworks = () =>
alovaInstance.Get<WiFiNetworkList>('/rest/listNetworks', { alovaInstance.Get<WiFiNetworkList>('/rest/listNetworks', {
name: 'listNetworks' name: 'listNetworks',
timeout: 20000 // timeout 20 seconds
}); });
export const readNetworkSettings = () => export const readNetworkSettings = () =>
alovaInstance.Get<NetworkSettings>('/rest/networkSettings', { name: 'networkSettings' }); alovaInstance.Get<NetworkSettings>('/rest/networkSettings', { name: 'networkSettings' });

View File

@@ -21,22 +21,22 @@ export const fetchLog = () => alovaInstance.Post('/rest/fetchLog');
// Get versions from github // Get versions from github
export const getStableVersion = () => export const getStableVersion = () =>
alovaInstanceGH.Get<Version>('releases/latest', { alovaInstanceGH.Get<Version>('releases/latest', {
transformData(reponse: any) { transformData(response: any) {
return { return {
version: reponse.data.name, version: response.data.name,
url: reponse.data.assets[1].browser_download_url, url: response.data.assets[1].browser_download_url,
changelog: reponse.data.assets[0].browser_download_url changelog: response.data.assets[0].browser_download_url
}; };
} }
}); });
export const getDevVersion = () => export const getDevVersion = () =>
alovaInstanceGH.Get<Version>('releases/tags/latest', { alovaInstanceGH.Get<Version>('releases/tags/latest', {
transformData(reponse: any) { transformData(response: any) {
return { return {
version: reponse.data.name.split(/\s+/).splice(-1), version: response.data.name.split(/\s+/).splice(-1),
url: reponse.data.assets[1].browser_download_url, url: response.data.assets[1].browser_download_url,
changelog: reponse.data.assets[0].browser_download_url changelog: response.data.assets[0].browser_download_url
}; };
} }
}); });

View File

@@ -74,14 +74,14 @@ const LayoutAuthMenu: FC = () => {
size="small" size="small"
select select
> >
<MenuItem key="en" value="en">
<GBflag style={{ width: 16, verticalAlign: 'middle' }} />
&nbsp;EN
</MenuItem>
<MenuItem key="de" value="de"> <MenuItem key="de" value="de">
<DEflag style={{ width: 16, verticalAlign: 'middle' }} /> <DEflag style={{ width: 16, verticalAlign: 'middle' }} />
&nbsp;DE &nbsp;DE
</MenuItem> </MenuItem>
<MenuItem key="en" value="en">
<GBflag style={{ width: 16, verticalAlign: 'middle' }} />
&nbsp;EN
</MenuItem>
<MenuItem key="fr" value="fr"> <MenuItem key="fr" value="fr">
<FRflag style={{ width: 16, verticalAlign: 'middle' }} /> <FRflag style={{ width: 16, verticalAlign: 'middle' }} />
&nbsp;FR &nbsp;FR

View File

@@ -5,10 +5,9 @@ import type { FC } from 'react';
import type { RequiredChildrenProps } from 'utils'; import type { RequiredChildrenProps } from 'utils';
import * as FeaturesApi from 'api/features'; import * as FeaturesApi from 'api/features';
import { ApplicationError, LoadingSpinner } from 'components';
const FeaturesLoader: FC<RequiredChildrenProps> = (props) => { const FeaturesLoader: FC<RequiredChildrenProps> = (props) => {
const { data: features, error } = useRequest(FeaturesApi.readFeatures); const { data: features } = useRequest(FeaturesApi.readFeatures);
if (features) { if (features) {
return ( return (
@@ -21,12 +20,6 @@ const FeaturesLoader: FC<RequiredChildrenProps> = (props) => {
</FeaturesContext.Provider> </FeaturesContext.Provider>
); );
} }
if (error) {
return <ApplicationError message={error?.message} />;
}
return <LoadingSpinner height="100vh" />;
}; };
export default FeaturesLoader; export default FeaturesLoader;

View File

@@ -168,7 +168,21 @@ const MqttSettingsForm: FC = () => {
<MenuItem value={2}>2</MenuItem> <MenuItem value={2}>2</MenuItem>
</TextField> </TextField>
</Grid> </Grid>
{data.rootCA !== undefined && (
<Grid item xs={12} sm={6}>
<ValidatedPasswordField
name="rootCA"
label={LL.CERT()}
fullWidth
variant="outlined"
value={data.rootCA}
onChange={updateFormValue}
margin="normal"
/>
</Grid>
)}
</Grid> </Grid>
<BlockFormControlLabel <BlockFormControlLabel
control={<Checkbox name="clean_session" checked={data.clean_session} onChange={updateFormValue} />} control={<Checkbox name="clean_session" checked={data.clean_session} onChange={updateFormValue} />}
label={LL.MQTT_CLEAN_SESSION()} label={LL.MQTT_CLEAN_SESSION()}

View File

@@ -68,10 +68,8 @@ const MqttStatusForm: FC = () => {
return 'Malformed credentials'; return 'Malformed credentials';
case MqttDisconnectReason.MQTT_NOT_AUTHORIZED: case MqttDisconnectReason.MQTT_NOT_AUTHORIZED:
return 'Not authorized'; return 'Not authorized';
case MqttDisconnectReason.ESP8266_NOT_ENOUGH_SPACE:
return 'Device out of memory';
case MqttDisconnectReason.TLS_BAD_FINGERPRINT: case MqttDisconnectReason.TLS_BAD_FINGERPRINT:
return 'Server fingerprint invalid'; return 'TSL fingerprint invalid';
default: default:
return 'Unknown'; return 'Unknown';
} }

View File

@@ -33,8 +33,12 @@ export const networkSecurityMode = ({ encryption_type }: WiFiNetwork) => {
return 'WPA2 Enterprise'; return 'WPA2 Enterprise';
case WiFiEncryptionType.WIFI_AUTH_OPEN: case WiFiEncryptionType.WIFI_AUTH_OPEN:
return 'None'; return 'None';
case WiFiEncryptionType.WIFI_AUTH_WPA3_PSK:
return 'WPA3';
case WiFiEncryptionType.WIFI_AUTH_WPA2_WPA3_PSK:
return 'WPA2/WPA3';
default: default:
return 'Unknown'; return 'Unknown: ' + encryption_type;
} }
}; };

View File

@@ -1,9 +1,8 @@
import DownloadIcon from '@mui/icons-material/GetApp'; import DownloadIcon from '@mui/icons-material/GetApp';
import WarningIcon from '@mui/icons-material/Warning'; import WarningIcon from '@mui/icons-material/Warning';
import { Box, styled, Button, Checkbox, MenuItem, Grid, TextField } from '@mui/material'; import { Box, styled, Button, Checkbox, MenuItem, Grid, TextField } from '@mui/material';
// eslint-disable-next-line import/named
import { useRequest } from 'alova'; import { useRequest } from 'alova';
import { useState, useEffect } from 'react'; import { useState, useEffect, useRef } from 'react';
import { toast } from 'react-toastify'; import { toast } from 'react-toastify';
import type { FC } from 'react'; import type { FC } from 'react';
@@ -58,8 +57,7 @@ const SystemLog: FC = () => {
}); });
// called on page load to reset pointer and fetch all log entries // called on page load to reset pointer and fetch all log entries
// eslint-disable-next-line @typescript-eslint/no-unused-vars useRequest(SystemApi.fetchLog());
const { send: fetchLog } = useRequest(SystemApi.fetchLog());
const [logEntries, setLogEntries] = useState<LogEntry[]>([]); const [logEntries, setLogEntries] = useState<LogEntry[]>([]);
const [lastIndex, setLastIndex] = useState<number>(0); const [lastIndex, setLastIndex] = useState<number>(0);
@@ -108,13 +106,23 @@ const SystemLog: FC = () => {
await saveData(); await saveData();
}; };
const ref = useRef<HTMLDivElement>(null);
useEffect(() => {
if (logEntries.length) {
ref.current?.scrollIntoView({
behavior: 'smooth',
block: 'end'
});
}
}, [logEntries.length]);
useEffect(() => { useEffect(() => {
const es = new EventSource(addAccessTokenParameter(LOG_EVENTSOURCE_URL)); const es = new EventSource(addAccessTokenParameter(LOG_EVENTSOURCE_URL));
es.onmessage = onMessage; es.onmessage = onMessage;
es.onerror = () => { es.onerror = () => {
es.close(); es.close();
toast.error('EventSource failed'); toast.error('EventSource failed');
// window.location.reload();
}; };
return () => { return () => {
@@ -217,6 +225,7 @@ const SystemLog: FC = () => {
<span>{e.m}</span> <span>{e.m}</span>
</LogEntryLine> </LogEntryLine>
))} ))}
<div ref={ref} />
</Box> </Box>
</> </>
); );

View File

@@ -53,8 +53,10 @@ const UploadFileForm: FC = () => {
await sendUpload(files[0]).catch((err) => { await sendUpload(files[0]).catch((err) => {
if (err.message === 'The user aborted a request') { if (err.message === 'The user aborted a request') {
toast.warning(LL.UPLOAD() + ' ' + LL.ABORTED()); toast.warning(LL.UPLOAD() + ' ' + LL.ABORTED());
} else if (err.message === 'Network Error') {
toast.warning('Invalid file extension or incompatible bin file');
} else { } else {
toast.warning(err.message); toast.error(err.message);
} }
}); });
}; };

View File

@@ -45,9 +45,9 @@ const de: Translation = {
CHANGE_VALUE: 'Wert ändern', CHANGE_VALUE: 'Wert ändern',
CANCEL: 'Abbrechen', CANCEL: 'Abbrechen',
RESET: 'Zurücksetzen', RESET: 'Zurücksetzen',
APPLY_CHANGES: 'Apply Changes ({0})', // TODO translate APPLY_CHANGES: 'Änderungen anwenden ({0})',
UPDATE: 'Update', // TODO translate UPDATE: 'Update',
EXECUTE: 'Execute', // TODO translate EXECUTE: 'Ausführen',
REMOVE: 'Entfernen', REMOVE: 'Entfernen',
PROBLEM_UPDATING: 'Problem beim Aktualisieren', PROBLEM_UPDATING: 'Problem beim Aktualisieren',
PROBLEM_LOADING: 'Problem beim Laden', PROBLEM_LOADING: 'Problem beim Laden',
@@ -180,7 +180,7 @@ const de: Translation = {
LOG_OF: '{0} Log', LOG_OF: '{0} Log',
STATUS_OF: '{0} Status', STATUS_OF: '{0} Status',
UPLOAD_DOWNLOAD: 'Hoch-/Herunterladen', UPLOAD_DOWNLOAD: 'Hoch-/Herunterladen',
VERSION_ON: 'You are currently on', // TODO translate VERSION_ON: 'Sie verwenden derzeit',
SYSTEM_APPLY_FIRMWARE: 'um die neue Firmware anzuwenden', SYSTEM_APPLY_FIRMWARE: 'um die neue Firmware anzuwenden',
CLOSE: 'Schließen', CLOSE: 'Schließen',
USE: 'Verwenden Sie', USE: 'Verwenden Sie',
@@ -240,7 +240,7 @@ const de: Translation = {
MQTT_PUBLISH_TEXT_2: 'Veröffentliche als Kommando-Topic (ioBroker)', MQTT_PUBLISH_TEXT_2: 'Veröffentliche als Kommando-Topic (ioBroker)',
MQTT_PUBLISH_TEXT_3: 'Aktiviere `MQTT Discovery`', MQTT_PUBLISH_TEXT_3: 'Aktiviere `MQTT Discovery`',
MQTT_PUBLISH_TEXT_4: 'Prefix für die `Discovery`-Topics', MQTT_PUBLISH_TEXT_4: 'Prefix für die `Discovery`-Topics',
MQTT_PUBLISH_TEXT_5: 'Discovery type', // TODO translate MQTT_PUBLISH_TEXT_5: 'Discovery Typ',
MQTT_PUBLISH_INTERVALS: 'Veröffentlichungs-Intervalle', MQTT_PUBLISH_INTERVALS: 'Veröffentlichungs-Intervalle',
MQTT_INT_BOILER: 'Boiler und Wärmepumpen', MQTT_INT_BOILER: 'Boiler und Wärmepumpen',
MQTT_INT_THERMOSTATS: 'Thermostate', MQTT_INT_THERMOSTATS: 'Thermostate',
@@ -282,7 +282,7 @@ const de: Translation = {
SCAN_AGAIN: 'Erneute Suche', SCAN_AGAIN: 'Erneute Suche',
NETWORK_SCANNER: 'Netzwerk Suche', NETWORK_SCANNER: 'Netzwerk Suche',
NETWORK_NO_WIFI: 'Keine WiFi Netzwerke gefunden', NETWORK_NO_WIFI: 'Keine WiFi Netzwerke gefunden',
NETWORK_BLANK_SSID: 'Freilassen um WiFi zu deaktivieren', NETWORK_BLANK_SSID: 'Freilassen um WiFi zu deaktivieren und ETH zu aktivieren',
TX_POWER: 'Tx Leistung', TX_POWER: 'Tx Leistung',
HOSTNAME: 'Hostname', HOSTNAME: 'Hostname',
NETWORK_DISABLE_SLEEP: 'Deaktiviere WiFi Schlafmodus', NETWORK_DISABLE_SLEEP: 'Deaktiviere WiFi Schlafmodus',
@@ -319,10 +319,11 @@ const de: Translation = {
SCHEDULE_TIMER_3: 'jede Stunde', SCHEDULE_TIMER_3: 'jede Stunde',
CUSTOM_ENTITIES: 'Individuelle Entitäten', CUSTOM_ENTITIES: 'Individuelle Entitäten',
ENTITIES_HELP_1: 'Abfrage von Werten auf dem EMS-Bus', ENTITIES_HELP_1: 'Abfrage von Werten auf dem EMS-Bus',
ENTITIES_UPDATED: 'Entities Updated', // TODO translate ENTITIES_UPDATED: 'Entitäten gespeichert',
WRITEABLE: 'Schreibbar', WRITEABLE: 'Schreibbar',
SHOWING: 'Showing', // TODO translate SHOWING: 'Anzeigen von',
SEARCH: 'Search' // TODO translate SEARCH: 'Suche',
CERT: 'TSL Zertifikat (Freilassen um TSL zu deaktivieren)'
}; };
export default de; export default de;

View File

@@ -282,7 +282,7 @@ const en: Translation = {
SCAN_AGAIN: 'Scan again', SCAN_AGAIN: 'Scan again',
NETWORK_SCANNER: 'Network Scanner', NETWORK_SCANNER: 'Network Scanner',
NETWORK_NO_WIFI: 'No WiFi networks found', NETWORK_NO_WIFI: 'No WiFi networks found',
NETWORK_BLANK_SSID: 'leave blank to disable WiFi', NETWORK_BLANK_SSID: 'leave blank to disable WiFi and enable ETH',
TX_POWER: 'Tx Power', TX_POWER: 'Tx Power',
HOSTNAME: 'Hostname', HOSTNAME: 'Hostname',
NETWORK_DISABLE_SLEEP: 'Disable WiFi Sleep Mode', NETWORK_DISABLE_SLEEP: 'Disable WiFi Sleep Mode',
@@ -322,8 +322,8 @@ const en: Translation = {
ENTITIES_UPDATED: 'Entities Updated', ENTITIES_UPDATED: 'Entities Updated',
WRITEABLE: 'Writeable', WRITEABLE: 'Writeable',
SHOWING: 'Showing', SHOWING: 'Showing',
SEARCH: 'Search' SEARCH: 'Search',
CERT: 'TSL root certificate (leave blank to disable TSL)'
}; };
export default en; export default en;

View File

@@ -282,7 +282,7 @@ const fr: Translation = {
SCAN_AGAIN: 'Rescanner', SCAN_AGAIN: 'Rescanner',
NETWORK_SCANNER: 'Scan réseau', NETWORK_SCANNER: 'Scan réseau',
NETWORK_NO_WIFI: 'Pas de réseau WiFi trouvé', NETWORK_NO_WIFI: 'Pas de réseau WiFi trouvé',
NETWORK_BLANK_SSID: 'laisser vide pour désactiver le WiFi', NETWORK_BLANK_SSID: 'laisser vide pour désactiver le WiFi', // and enable ETH // TODO translate
TX_POWER: 'Puissance Tx', TX_POWER: 'Puissance Tx',
HOSTNAME: 'Nom d\'hôte', HOSTNAME: 'Nom d\'hôte',
NETWORK_DISABLE_SLEEP: 'Désactiver le mode veille du WiFi', NETWORK_DISABLE_SLEEP: 'Désactiver le mode veille du WiFi',
@@ -322,7 +322,8 @@ const fr: Translation = {
ENTITIES_UPDATED: 'Entities Updated', // TODO translate ENTITIES_UPDATED: 'Entities Updated', // TODO translate
WRITEABLE: 'Writeable', // TODO translate WRITEABLE: 'Writeable', // TODO translate
SHOWING: 'Showing', // TODO translate SHOWING: 'Showing', // TODO translate
SEARCH: 'Search' // TODO translate SEARCH: 'Search', // TODO translate
CERT: 'TSL root certificate (leave blank to disable TSL)' // TODO translate
}; };
export default fr; export default fr;

View File

@@ -45,13 +45,13 @@ const it: Translation = {
CHANGE_VALUE: 'Cambia Valore', CHANGE_VALUE: 'Cambia Valore',
CANCEL: 'Annulla', CANCEL: 'Annulla',
RESET: 'Reset', RESET: 'Reset',
APPLY_CHANGES: 'Apply Changes ({0})', APPLY_CHANGES: 'Applica Cambiamenti ({0})',
UPDATE: 'Update', UPDATE: 'Update',
EXECUTE: 'Execute', EXECUTE: 'Execute',
REMOVE: 'Elimina', REMOVE: 'Elimina',
PROBLEM_UPDATING: 'Problema aggiornamento', PROBLEM_UPDATING: 'Problema aggiornamento',
PROBLEM_LOADING: 'Problema caricamento', PROBLEM_LOADING: 'Problema caricamento',
ACCESS_DENIED: 'Access Denied', ACCESS_DENIED: 'Accesso Negato',
ANALOG_SENSOR: 'Sensore Analogico', ANALOG_SENSOR: 'Sensore Analogico',
ANALOG_SENSORS: 'Sensori Analogici', ANALOG_SENSORS: 'Sensori Analogici',
SETTINGS: 'Settings', SETTINGS: 'Settings',
@@ -305,27 +305,27 @@ const it: Translation = {
ENTITY: 'entità', ENTITY: 'entità',
MIN: 'min', MIN: 'min',
MAX: 'max', MAX: 'max',
BLOCK_NAVIGATE_1: 'You have unsaved changes', BLOCK_NAVIGATE_1: 'Hai modifiche non salvate',
BLOCK_NAVIGATE_2: 'If you navigate to a different page, your unsaved changes will be lost. Are you sure you want to leave this page?', BLOCK_NAVIGATE_2: 'Se passi a una pagina diversa, le modifiche non salvate andranno perse. Sei sicuro di voler lasciare questa pagina?',
STAY: 'Stay', STAY: 'Stai',
LEAVE: 'Leave', LEAVE: 'Esci',
SCHEDULER: 'Scheduler', SCHEDULER: 'Programma eventi',
SCHEDULER_HELP_1: 'Automate commands by adding scheduled events below. Set a unique Name to enable/disable activation via API/MQTT.', SCHEDULER_HELP_1: "Automatizza i comandi aggiungendo gli eventi programmati di seguito. Imposta un nome univoco per abilitare/disabilitare l'attivazione tramite API/MQTT.",
SCHEDULER_HELP_2: 'Use 00:00 to trigger once on start-up', SCHEDULER_HELP_2: "per attivare una volta all'avvio",
SCHEDULE: 'Schedule', SCHEDULE: 'Programma',
TIME: 'Time', TIME: 'Ora',
TIMER: 'Timer', TIMER: 'Orologio',
SCHEDULE_UPDATED: 'Schedule updated', SCHEDULE_UPDATED: 'Calendario aggiornato',
SCHEDULE_TIMER_1: 'on startup', SCHEDULE_TIMER_1: 'All avvio',
SCHEDULE_TIMER_2: 'every minute', SCHEDULE_TIMER_2: 'Ogni minuto',
SCHEDULE_TIMER_3: 'every hour', SCHEDULE_TIMER_3: 'Ogni ora',
CUSTOM_ENTITIES: 'Custom Entities', CUSTOM_ENTITIES: 'Entità personalizzate',
ENTITIES_HELP_1: 'Fetch custom entities from the EMS bus', ENTITIES_HELP_1: 'Recupera entità personalizzate dal BUS EMS',
ENTITIES_UPDATED: 'Entities Updated', ENTITIES_UPDATED: 'Entità aggiornate',
WRITEABLE: 'Writeable', WRITEABLE: 'Scrivibile',
SHOWING: 'Showing', SHOWING: 'Visualizza',
SEARCH: 'Search' SEARCH: 'Ricerca',
CERT: 'TSL root certificate (leave blank to disable TSL)' // TODO translate
}; };
export default it; export default it;

View File

@@ -282,7 +282,7 @@ const nl: Translation = {
SCAN_AGAIN: 'Opnieuw scannen', SCAN_AGAIN: 'Opnieuw scannen',
NETWORK_SCANNER: 'Netwerk Scanner', NETWORK_SCANNER: 'Netwerk Scanner',
NETWORK_NO_WIFI: 'Geen WiFi networken gevonden', NETWORK_NO_WIFI: 'Geen WiFi networken gevonden',
NETWORK_BLANK_SSID: 'laat leeg om WiFi uit te schakelen', NETWORK_BLANK_SSID: 'laat leeg om WiFi uit te schakelen', // and enable ETH // TODO translate
TX_POWER: 'Tx Vermogen', TX_POWER: 'Tx Vermogen',
HOSTNAME: 'Hostname', HOSTNAME: 'Hostname',
NETWORK_DISABLE_SLEEP: 'WiFi Sleep Mode uitzetten', NETWORK_DISABLE_SLEEP: 'WiFi Sleep Mode uitzetten',
@@ -322,7 +322,8 @@ const nl: Translation = {
ENTITIES_UPDATED: 'Entiteiten bijgewerkt', ENTITIES_UPDATED: 'Entiteiten bijgewerkt',
WRITEABLE: 'Beschrijfbare', WRITEABLE: 'Beschrijfbare',
SHOWING: 'Tonen', SHOWING: 'Tonen',
SEARCH: 'Zoek' SEARCH: 'Zoek',
CERT: 'TSL root certificate (leave blank to disable TSL)' // TODO translate
}; };
export default nl; export default nl;

View File

@@ -282,7 +282,7 @@ const no: Translation = {
SCAN_AGAIN: 'Søk igjen', SCAN_AGAIN: 'Søk igjen',
NETWORK_SCANNER: 'Nettverk Scanner', NETWORK_SCANNER: 'Nettverk Scanner',
NETWORK_NO_WIFI: 'Ingen trådløse nett funnet', NETWORK_NO_WIFI: 'Ingen trådløse nett funnet',
NETWORK_BLANK_SSID: 'la feltet være blankt for å deaktivisere trådløst nettverk', NETWORK_BLANK_SSID: 'la feltet være blankt for å deaktivisere trådløst nettverk', // TODO translate
TX_POWER: 'Tx Effekt', TX_POWER: 'Tx Effekt',
HOSTNAME: 'Hostname', HOSTNAME: 'Hostname',
NETWORK_DISABLE_SLEEP: 'Hindre at trådløst nettverk går i Sleep Mode', NETWORK_DISABLE_SLEEP: 'Hindre at trådløst nettverk går i Sleep Mode',
@@ -322,8 +322,8 @@ const no: Translation = {
ENTITIES_UPDATED: 'Entities Updated', // TODO translate ENTITIES_UPDATED: 'Entities Updated', // TODO translate
WRITEABLE: 'Writeable', // TODO translate WRITEABLE: 'Writeable', // TODO translate
SHOWING: 'Showing', // TODO translate SHOWING: 'Showing', // TODO translate
SEARCH: 'Search' // TODO translate SEARCH: 'Search', // TODO translate
CERT: 'TSL root certificate (leave blank to disable TSL)' // TODO translate
}; };
export default no; export default no;

View File

@@ -282,7 +282,7 @@ const pl: BaseTranslation = {
SCAN_AGAIN: 'Skanuj ponownie', SCAN_AGAIN: 'Skanuj ponownie',
NETWORK_SCANNER: 'Skaner sieci WiFi', NETWORK_SCANNER: 'Skaner sieci WiFi',
NETWORK_NO_WIFI: 'Brak sieci WiFi w zasięgu', NETWORK_NO_WIFI: 'Brak sieci WiFi w zasięgu',
NETWORK_BLANK_SSID: 'pozostaw puste aby wyłączyć WiFi', NETWORK_BLANK_SSID: 'pozostaw puste aby wyłączyć WiFi', // and enable ETH // TODO translate
TX_POWER: 'Moc nadawania', TX_POWER: 'Moc nadawania',
HOSTNAME: 'Nazwa w sieci', HOSTNAME: 'Nazwa w sieci',
NETWORK_DISABLE_SLEEP: 'Wyłącz tryb uśpienia WiFi', NETWORK_DISABLE_SLEEP: 'Wyłącz tryb uśpienia WiFi',
@@ -322,8 +322,8 @@ const pl: BaseTranslation = {
ENTITIES_UPDATED: 'Niestandardowe encje zostały uaktualnione.', ENTITIES_UPDATED: 'Niestandardowe encje zostały uaktualnione.',
WRITEABLE: 'zapisywalna', WRITEABLE: 'zapisywalna',
SHOWING: 'Wyświetlane', SHOWING: 'Wyświetlane',
SEARCH: 'Szukaj' SEARCH: 'Szukaj',
CERT: 'TSL root certificate (leave blank to disable TSL)' // TODO translate
}; };
export default pl; export default pl;

View File

@@ -282,7 +282,7 @@ const sv: Translation = {
SCAN_AGAIN: 'Sök igen', SCAN_AGAIN: 'Sök igen',
NETWORK_SCANNER: 'Hittade nätverk', NETWORK_SCANNER: 'Hittade nätverk',
NETWORK_NO_WIFI: 'Inga WiFi-nätverk hittades', NETWORK_NO_WIFI: 'Inga WiFi-nätverk hittades',
NETWORK_BLANK_SSID: 'lämna blankt för att inaktivera WiFi', NETWORK_BLANK_SSID: 'lämna blankt för att inaktivera WiFi', // and enable ETH // TODO translate
TX_POWER: 'Tx Effekt', TX_POWER: 'Tx Effekt',
HOSTNAME: 'Värdnamn', HOSTNAME: 'Värdnamn',
NETWORK_DISABLE_SLEEP: 'Inaktivera sömnläge', NETWORK_DISABLE_SLEEP: 'Inaktivera sömnläge',
@@ -322,8 +322,8 @@ const sv: Translation = {
ENTITIES_UPDATED: 'Entities Updated', // TODO translate ENTITIES_UPDATED: 'Entities Updated', // TODO translate
WRITEABLE: 'Writeable', // TODO translate WRITEABLE: 'Writeable', // TODO translate
SHOWING: 'Showing', // TODO translate SHOWING: 'Showing', // TODO translate
SEARCH: 'Search' // TODO translate SEARCH: 'Search', // TODO translate
CERT: 'TSL root certificate (leave blank to disable TSL)' // TODO translate
}; };
export default sv; export default sv;

View File

@@ -282,7 +282,7 @@ const tr: Translation = {
SCAN_AGAIN: 'Tekrar tara', SCAN_AGAIN: 'Tekrar tara',
NETWORK_SCANNER: 'Ağ Tarayıcısı', NETWORK_SCANNER: 'Ağ Tarayıcısı',
NETWORK_NO_WIFI: 'Hiçbir Kablosuz Ağ bulunamadı', NETWORK_NO_WIFI: 'Hiçbir Kablosuz Ağ bulunamadı',
NETWORK_BLANK_SSID: 'Kablosuz ağı devre dışı bırakmak için boş bırakın', NETWORK_BLANK_SSID: 'Kablosuz ağı devre dışı bırakmak için boş bırakın', // TODO translate
TX_POWER: 'Aktarım gücü', TX_POWER: 'Aktarım gücü',
HOSTNAME: 'Ana Makine Adı', HOSTNAME: 'Ana Makine Adı',
NETWORK_DISABLE_SLEEP: 'Kablosuz uyku modunu devre dışına al', NETWORK_DISABLE_SLEEP: 'Kablosuz uyku modunu devre dışına al',
@@ -322,8 +322,8 @@ const tr: Translation = {
ENTITIES_UPDATED: 'Entities Updated', // TODO translate ENTITIES_UPDATED: 'Entities Updated', // TODO translate
WRITEABLE: 'Writeable', // TODO translate WRITEABLE: 'Writeable', // TODO translate
SHOWING: 'Showing', // TODO translate SHOWING: 'Showing', // TODO translate
SEARCH: 'Search' // TODO translate SEARCH: 'Search', // TODO translate
CERT: 'TSL root certificate (leave blank to disable TSL)' // TODO translate
}; };
export default tr; export default tr;

View File

@@ -377,8 +377,8 @@ const SettingsApplication: FC = () => {
margin="normal" margin="normal"
select select
> >
<MenuItem value="en">English (EN)</MenuItem>
<MenuItem value="de">Deutsch (DE)</MenuItem> <MenuItem value="de">Deutsch (DE)</MenuItem>
<MenuItem value="en">English (EN)</MenuItem>
<MenuItem value="fr">Français (FR)</MenuItem> <MenuItem value="fr">Français (FR)</MenuItem>
<MenuItem value="it">Italiano (IT)</MenuItem> <MenuItem value="it">Italiano (IT)</MenuItem>
<MenuItem value="nl">Nederlands (NL)</MenuItem> <MenuItem value="nl">Nederlands (NL)</MenuItem>

View File

@@ -17,7 +17,7 @@ import {
} from '@mui/material'; } from '@mui/material';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { DeviceValueUOM_s } from './types'; import { DeviceValueUOM_s, DeviceValueType } from './types';
import type { EntityItem } from './types'; import type { EntityItem } from './types';
import type Schema from 'async-validator'; import type Schema from 'async-validator';
import type { ValidateFieldsError } from 'async-validator'; import type { ValidateFieldsError } from 'async-validator';
@@ -166,17 +166,18 @@ const SettingsEntitiesDialog = ({
fullWidth fullWidth
select select
> >
<MenuItem value={0}>BOOL</MenuItem> <MenuItem value={DeviceValueType.BOOL}>BOOL</MenuItem>
<MenuItem value={1}>INT</MenuItem> <MenuItem value={DeviceValueType.INT}>INT</MenuItem>
<MenuItem value={2}>UINT</MenuItem> <MenuItem value={DeviceValueType.UINT}>UINT</MenuItem>
<MenuItem value={3}>SHORT</MenuItem> <MenuItem value={DeviceValueType.SHORT}>SHORT</MenuItem>
<MenuItem value={4}>USHORT</MenuItem> <MenuItem value={DeviceValueType.USHORT}>USHORT</MenuItem>
<MenuItem value={5}>ULONG</MenuItem> <MenuItem value={DeviceValueType.ULONG}>ULONG</MenuItem>
<MenuItem value={6}>TIME</MenuItem> <MenuItem value={DeviceValueType.TIME}>TIME</MenuItem>
<MenuItem value={DeviceValueType.STRING}>RAW</MenuItem>
</TextField> </TextField>
</Grid> </Grid>
{editItem.value_type !== 0 && ( {editItem.value_type !== DeviceValueType.BOOL && editItem.value_type !== DeviceValueType.STRING && (
<> <>
<Grid item xs={4}> <Grid item xs={4}>
<TextField <TextField
@@ -210,6 +211,21 @@ const SettingsEntitiesDialog = ({
</Grid> </Grid>
</> </>
)} )}
{editItem.value_type === DeviceValueType.STRING && (
<Grid item xs={4}>
<TextField
name="factor"
label="Bytes"
value={editItem.factor}
variant="outlined"
onChange={updateFormValue}
fullWidth
margin="normal"
type="number"
inputProps={{ min: '1', max: '27', step: '1' }}
/>
</Grid>
)}
</Grid> </Grid>
</DialogContent> </DialogContent>

View File

@@ -243,7 +243,8 @@ export const BOARD_PROFILES: BoardProfiles = {
OLIMEXPOE: 'Olimex ESP32-POE', OLIMEXPOE: 'Olimex ESP32-POE',
C3MINI: 'Wemos C3 Mini', C3MINI: 'Wemos C3 Mini',
S2MINI: 'Wemos S2 Mini', S2MINI: 'Wemos S2 Mini',
S3MINI: 'Liligo S3' S3MINI: 'Liligo S3',
S32S3: 'BBQKees Gateway S3'
}; };
export interface BoardProfile { export interface BoardProfile {
@@ -364,3 +365,17 @@ export const enum DeviceType {
CUSTOM, CUSTOM,
UNKNOWN UNKNOWN
} }
// matches emsdevicevalue.h
export const enum DeviceValueType {
BOOL,
INT,
UINT,
SHORT,
USHORT,
ULONG,
TIME, // same as ULONG (32 bits)
ENUM,
STRING,
CMD
}

View File

@@ -8,7 +8,7 @@ export const GPIO_VALIDATOR = {
if ( if (
value && value &&
(value === 1 || (value === 1 ||
(value >= 10 && value <= 12) || (value >= 6 && value <= 12) ||
(value >= 14 && value <= 15) || (value >= 14 && value <= 15) ||
value === 20 || value === 20 ||
value === 24 || value === 24 ||
@@ -43,6 +43,23 @@ export const GPIO_VALIDATORS2 = {
} }
}; };
export const GPIO_VALIDATORS3 = {
validator(rule: InternalRuleItem, value: number, callback: (error?: string) => void) {
if (
value &&
((value >= 19 && value <= 20) ||
(value >= 22 && value <= 37) ||
(value >= 39 && value <= 42) ||
value > 48 ||
value < 0)
) {
callback('Must be an valid GPIO port');
} else {
callback();
}
}
};
export const createSettingsValidator = (settings: Settings) => export const createSettingsValidator = (settings: Settings) =>
new Schema({ new Schema({
...(settings.board_profile === 'CUSTOM' && ...(settings.board_profile === 'CUSTOM' &&
@@ -69,6 +86,14 @@ export const createSettingsValidator = (settings: Settings) =>
tx_gpio: [{ required: true, message: 'Tx GPIO is required' }, GPIO_VALIDATORS2], tx_gpio: [{ required: true, message: 'Tx GPIO is required' }, GPIO_VALIDATORS2],
rx_gpio: [{ required: true, message: 'Rx GPIO is required' }, GPIO_VALIDATORS2] rx_gpio: [{ required: true, message: 'Rx GPIO is required' }, GPIO_VALIDATORS2]
}), }),
...(settings.board_profile === 'CUSTOM' &&
settings.platform === 'ESP32-S3' && {
led_gpio: [{ required: true, message: 'LED GPIO is required' }, GPIO_VALIDATORS3],
dallas_gpio: [{ required: true, message: 'GPIO is required' }, GPIO_VALIDATORS3],
pbutton_gpio: [{ required: true, message: 'Button GPIO is required' }, GPIO_VALIDATORS3],
tx_gpio: [{ required: true, message: 'Tx GPIO is required' }, GPIO_VALIDATORS3],
rx_gpio: [{ required: true, message: 'Rx GPIO is required' }, GPIO_VALIDATORS3]
}),
...(settings.syslog_enabled && { ...(settings.syslog_enabled && {
syslog_host: [{ required: true, message: 'Host is required' }, IP_OR_HOSTNAME_VALIDATOR], syslog_host: [{ required: true, message: 'Host is required' }, IP_OR_HOSTNAME_VALIDATOR],
syslog_port: [ syslog_port: [

View File

@@ -1,8 +1,4 @@
export interface Features { export interface Features {
project: boolean; version: string;
security: boolean; platform: string; // "ESP32-C3" "ESP32-S2" "ESP32-S3" "ESP32"
mqtt: boolean;
ntp: boolean;
ota: boolean;
upload_firmware: boolean;
} }

View File

@@ -1,12 +1,12 @@
export enum MqttDisconnectReason { export enum MqttDisconnectReason {
TCP_DISCONNECTED = 0, USER_OK = 0,
MQTT_UNACCEPTABLE_PROTOCOL_VERSION = 1, MQTT_UNACCEPTABLE_PROTOCOL_VERSION = 1,
MQTT_IDENTIFIER_REJECTED = 2, MQTT_IDENTIFIER_REJECTED = 2,
MQTT_SERVER_UNAVAILABLE = 3, MQTT_SERVER_UNAVAILABLE = 3,
MQTT_MALFORMED_CREDENTIALS = 4, MQTT_MALFORMED_CREDENTIALS = 4,
MQTT_NOT_AUTHORIZED = 5, MQTT_NOT_AUTHORIZED = 5,
ESP8266_NOT_ENOUGH_SPACE = 6, TLS_BAD_FINGERPRINT = 6,
TLS_BAD_FINGERPRINT = 7 TCP_DISCONNECTED = 7
} }
export interface MqttStatus { export interface MqttStatus {
@@ -24,6 +24,7 @@ export interface MqttSettings {
host: string; host: string;
port: number; port: number;
base: string; base: string;
rootCA?: string;
username: string; username: string;
password: string; password: string;
client_id: string; client_id: string;

View File

@@ -15,7 +15,9 @@ export enum WiFiEncryptionType {
WIFI_AUTH_WPA_PSK = 2, WIFI_AUTH_WPA_PSK = 2,
WIFI_AUTH_WPA2_PSK = 3, WIFI_AUTH_WPA2_PSK = 3,
WIFI_AUTH_WPA_WPA2_PSK = 4, WIFI_AUTH_WPA_WPA2_PSK = 4,
WIFI_AUTH_WPA2_ENTERPRISE = 5 WIFI_AUTH_WPA2_ENTERPRISE = 5,
WIFI_AUTH_WPA3_PSK = 6,
WIFI_AUTH_WPA2_WPA3_PSK = 7
} }
export interface NetworkStatus { export interface NetworkStatus {

View File

@@ -1,21 +1,21 @@
import { defineConfig, type PluginOption } from 'vite'; import { defineConfig, type PluginOption } from 'vite';
import react from '@vitejs/plugin-react-swc';
import viteTsconfigPaths from 'vite-tsconfig-paths'; import viteTsconfigPaths from 'vite-tsconfig-paths';
import svgrPlugin from 'vite-plugin-svgr'; import svgrPlugin from 'vite-plugin-svgr';
import { visualizer } from 'rollup-plugin-visualizer'; import { visualizer } from 'rollup-plugin-visualizer';
import ProgmemGenerator from './progmem-generator'; import ProgmemGenerator from './progmem-generator';
import preact from '@preact/preset-vite';
export default defineConfig(({ command, mode }) => { export default defineConfig(({ command, mode }) => {
if (mode === 'hosted') { if (mode === 'hosted') {
return { return {
// hosted, ignore all errors, output to dist // hosted, ignore all errors, output to dist
plugins: [react(), viteTsconfigPaths(), svgrPlugin(), visualizer({ gzipSize: true }) as PluginOption] plugins: [preact(), viteTsconfigPaths(), svgrPlugin(), visualizer({ gzipSize: true }) as PluginOption]
}; };
} else { } else {
// normal build // normal build
return { return {
plugins: [ plugins: [
react(), preact(),
viteTsconfigPaths(), viteTsconfigPaths(),
svgrPlugin(), svgrPlugin(),
ProgmemGenerator({ outputPath: '../lib/framework/WWWData.h', bytesPerLine: 20 }) ProgmemGenerator({ outputPath: '../lib/framework/WWWData.h', bytesPerLine: 20 })
@@ -26,7 +26,25 @@ export default defineConfig(({ command, mode }) => {
chunkSizeWarningLimit: 1024, chunkSizeWarningLimit: 1024,
sourcemap: false, sourcemap: false,
manifest: false, manifest: false,
minify: mode === 'development' ? false : 'terser' minify: mode === 'development' ? false : 'terser',
rollupOptions: {
/**
* Ignore "use client" waning since we are not using SSR
*/
onwarn(warning, warn) {
if (warning.code === 'MODULE_LEVEL_DIRECTIVE' && warning.message.includes(`"use client"`)) {
return;
}
warn(warning);
}
}
},
onwarn(warning, warn) {
if (warning.code === 'MODULE_LEVEL_DIRECTIVE') {
return;
}
warn(warning);
}, },
server: { server: {

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,15 @@
ArduinoJson: change log ArduinoJson: change log
======================= =======================
v6.21.3 (2023-07-23)
-------
* Fix compatibility with the Blynk libary (issue #1914)
* Fix double lookup in `to<JsonVariant>()`
* Fix double call to `size()` in `serializeMsgPack()`
* Include `ARDUINOJSON_SLOT_OFFSET_SIZE` in the namespace name
* Show a link to the documentation when user passes an unsupported input type
v6.21.2 (2023-04-12) v6.21.2 (2023-04-12)
------- -------

View File

@@ -8,9 +8,9 @@
[![Continuous Integration](https://ci.appveyor.com/api/projects/status/m7s53wav1l0abssg/branch/6.x?svg=true)](https://ci.appveyor.com/project/bblanchon/arduinojson/branch/6.x) [![Continuous Integration](https://ci.appveyor.com/api/projects/status/m7s53wav1l0abssg/branch/6.x?svg=true)](https://ci.appveyor.com/project/bblanchon/arduinojson/branch/6.x)
[![Fuzzing Status](https://oss-fuzz-build-logs.storage.googleapis.com/badges/arduinojson.svg)](https://bugs.chromium.org/p/oss-fuzz/issues/list?sort=-opened&can=1&q=proj:arduinojson) [![Fuzzing Status](https://oss-fuzz-build-logs.storage.googleapis.com/badges/arduinojson.svg)](https://bugs.chromium.org/p/oss-fuzz/issues/list?sort=-opened&can=1&q=proj:arduinojson)
[![Coveralls branch](https://img.shields.io/coveralls/github/bblanchon/ArduinoJson/6.x?logo=coveralls)](https://coveralls.io/github/bblanchon/ArduinoJson?branch=6.x) [![Coveralls branch](https://img.shields.io/coveralls/github/bblanchon/ArduinoJson/6.x?logo=coveralls)](https://coveralls.io/github/bblanchon/ArduinoJson?branch=6.x)
[![Arduino Library Manager](https://img.shields.io/static/v1?label=Arduino&message=v6.21.2&logo=arduino&logoColor=white&color=blue)](https://www.ardu-badge.com/ArduinoJson/6.21.2) [![Arduino Library Manager](https://img.shields.io/static/v1?label=Arduino&message=v6.21.3&logo=arduino&logoColor=white&color=blue)](https://www.ardu-badge.com/ArduinoJson/6.21.3)
[![PlatformIO Registry](https://badges.registry.platformio.org/packages/bblanchon/library/ArduinoJson.svg?version=6.21.2)](https://registry.platformio.org/packages/libraries/bblanchon/ArduinoJson?version=6.21.2) [![PlatformIO Registry](https://badges.registry.platformio.org/packages/bblanchon/library/ArduinoJson.svg?version=6.21.3)](https://registry.platformio.org/packages/libraries/bblanchon/ArduinoJson?version=6.21.3)
[![ESP IDF](https://img.shields.io/static/v1?label=ESP+IDF&message=v6.21.2&logo=cpu&logoColor=white&color=blue)](https://components.espressif.com/components/bblanchon/arduinojson) [![ESP IDF](https://img.shields.io/static/v1?label=ESP+IDF&message=v6.21.3&logo=cpu&logoColor=white&color=blue)](https://components.espressif.com/components/bblanchon/arduinojson)
[![GitHub stars](https://img.shields.io/github/stars/bblanchon/ArduinoJson?style=flat&logo=github&color=orange)](https://github.com/bblanchon/ArduinoJson/stargazers) [![GitHub stars](https://img.shields.io/github/stars/bblanchon/ArduinoJson?style=flat&logo=github&color=orange)](https://github.com/bblanchon/ArduinoJson/stargazers)
[![GitHub Sponsors](https://img.shields.io/github/sponsors/bblanchon?logo=github&color=orange)](https://github.com/sponsors/bblanchon) [![GitHub Sponsors](https://img.shields.io/github/sponsors/bblanchon?logo=github&color=orange)](https://github.com/sponsors/bblanchon)

View File

@@ -1,5 +1,5 @@
name=ArduinoJson name=ArduinoJson
version=6.21.2 version=6.21.3
author=Benoit Blanchon <blog.benoitblanchon.fr> author=Benoit Blanchon <blog.benoitblanchon.fr>
maintainer=Benoit Blanchon <blog.benoitblanchon.fr> maintainer=Benoit Blanchon <blog.benoitblanchon.fr>
sentence=A simple and efficient JSON library for embedded C++. sentence=A simple and efficient JSON library for embedded C++.

View File

@@ -18,7 +18,9 @@ struct Reader {
Reader(TSource& source) : source_(&source) {} Reader(TSource& source) : source_(&source) {}
int read() { int read() {
return source_->read(); // Error here? You passed an unsupported input type // clang-format off
return source_->read(); // Error here? See https://arduinojson.org/v6/invalid-input/
// clang-format on
} }
size_t readBytes(char* buffer, size_t length) { size_t readBytes(char* buffer, size_t length) {

View File

@@ -47,7 +47,7 @@ class MsgPackSerializer : public Visitor<size_t> {
size_t visitArray(const CollectionData& array) { size_t visitArray(const CollectionData& array) {
size_t n = array.size(); size_t n = array.size();
if (n < 0x10) { if (n < 0x10) {
writeByte(uint8_t(0x90 + array.size())); writeByte(uint8_t(0x90 + n));
} else if (n < 0x10000) { } else if (n < 0x10000) {
writeByte(0xDC); writeByte(0xDC);
writeInteger(uint16_t(n)); writeInteger(uint16_t(n));

View File

@@ -11,16 +11,15 @@
#ifndef ARDUINOJSON_VERSION_NAMESPACE #ifndef ARDUINOJSON_VERSION_NAMESPACE
# define ARDUINOJSON_VERSION_NAMESPACE \ # define ARDUINOJSON_VERSION_NAMESPACE \
ARDUINOJSON_CONCAT3( \ ARDUINOJSON_CONCAT4( \
ARDUINOJSON_CONCAT4(V, ARDUINOJSON_VERSION_MAJOR, \ ARDUINOJSON_VERSION_MACRO, \
ARDUINOJSON_VERSION_MINOR, \
ARDUINOJSON_VERSION_REVISION), \
ARDUINOJSON_BIN2ALPHA( \ ARDUINOJSON_BIN2ALPHA( \
ARDUINOJSON_ENABLE_PROGMEM, ARDUINOJSON_USE_LONG_LONG, \ ARDUINOJSON_ENABLE_PROGMEM, ARDUINOJSON_USE_LONG_LONG, \
ARDUINOJSON_USE_DOUBLE, ARDUINOJSON_ENABLE_STRING_DEDUPLICATION), \ ARDUINOJSON_USE_DOUBLE, ARDUINOJSON_ENABLE_STRING_DEDUPLICATION), \
ARDUINOJSON_BIN2ALPHA( \ ARDUINOJSON_BIN2ALPHA( \
ARDUINOJSON_ENABLE_NAN, ARDUINOJSON_ENABLE_INFINITY, \ ARDUINOJSON_ENABLE_NAN, ARDUINOJSON_ENABLE_INFINITY, \
ARDUINOJSON_ENABLE_COMMENTS, ARDUINOJSON_DECODE_UNICODE)) ARDUINOJSON_ENABLE_COMMENTS, ARDUINOJSON_DECODE_UNICODE), \
ARDUINOJSON_SLOT_OFFSET_SIZE)
#endif #endif

View File

@@ -6,8 +6,6 @@
#define ARDUINOJSON_CONCAT_(A, B) A##B #define ARDUINOJSON_CONCAT_(A, B) A##B
#define ARDUINOJSON_CONCAT2(A, B) ARDUINOJSON_CONCAT_(A, B) #define ARDUINOJSON_CONCAT2(A, B) ARDUINOJSON_CONCAT_(A, B)
#define ARDUINOJSON_CONCAT3(A, B, C) \
ARDUINOJSON_CONCAT2(ARDUINOJSON_CONCAT2(A, B), C)
#define ARDUINOJSON_CONCAT4(A, B, C, D) \ #define ARDUINOJSON_CONCAT4(A, B, C, D) \
ARDUINOJSON_CONCAT2(ARDUINOJSON_CONCAT2(A, B), ARDUINOJSON_CONCAT2(C, D)) ARDUINOJSON_CONCAT2(ARDUINOJSON_CONCAT2(A, B), ARDUINOJSON_CONCAT2(C, D))
@@ -17,7 +15,7 @@
#define ARDUINOJSON_BIN2ALPHA_0011() D #define ARDUINOJSON_BIN2ALPHA_0011() D
#define ARDUINOJSON_BIN2ALPHA_0100() E #define ARDUINOJSON_BIN2ALPHA_0100() E
#define ARDUINOJSON_BIN2ALPHA_0101() F #define ARDUINOJSON_BIN2ALPHA_0101() F
#define ARDUINOJSON_BIN2ALPHA_0110() F #define ARDUINOJSON_BIN2ALPHA_0110() G
#define ARDUINOJSON_BIN2ALPHA_0111() H #define ARDUINOJSON_BIN2ALPHA_0111() H
#define ARDUINOJSON_BIN2ALPHA_1000() I #define ARDUINOJSON_BIN2ALPHA_1000() I
#define ARDUINOJSON_BIN2ALPHA_1001() J #define ARDUINOJSON_BIN2ALPHA_1001() J

View File

@@ -138,8 +138,9 @@ template <typename TDerived>
template <typename T> template <typename T>
typename enable_if<is_same<T, JsonVariant>::value, JsonVariant>::type typename enable_if<is_same<T, JsonVariant>::value, JsonVariant>::type
VariantRefBase<TDerived>::to() const { VariantRefBase<TDerived>::to() const {
variantSetNull(getOrCreateData()); auto data = getOrCreateData();
return *this; variantSetNull(data);
return JsonVariant(getPool(), data);
} }
template <typename TDerived> template <typename TDerived>

View File

@@ -4,7 +4,8 @@
#pragma once #pragma once
#define ARDUINOJSON_VERSION "6.21.2" #define ARDUINOJSON_VERSION "6.21.3"
#define ARDUINOJSON_VERSION_MAJOR 6 #define ARDUINOJSON_VERSION_MAJOR 6
#define ARDUINOJSON_VERSION_MINOR 21 #define ARDUINOJSON_VERSION_MINOR 21
#define ARDUINOJSON_VERSION_REVISION 2 #define ARDUINOJSON_VERSION_REVISION 3
#define ARDUINOJSON_VERSION_MACRO V6213

View File

@@ -54,7 +54,7 @@ bool PButton::init(uint8_t pin, bool pullMode) {
#if defined(ESP32) #if defined(ESP32)
pinMode(pin_, pullMode ? INPUT_PULLUP : INPUT_PULLDOWN); pinMode(pin_, pullMode ? INPUT_PULLUP : INPUT_PULLDOWN);
#else // esp8266 and standalone #else // esp8266 and standalone
pinMode(pin_, pullMode ? INPUT_PULLUP : INPUT); pinMode(pin_, pullMode ? INPUT_PULLUP : INPUT);
#endif #endif
enabled_ = (digitalRead(pin_) == pullMode); // see if a button is connected enabled_ = (digitalRead(pin_) == pullMode); // see if a button is connected

View File

@@ -47,21 +47,21 @@ class PButton {
bool pullMode_; bool pullMode_;
bool enabled_; bool enabled_;
bool state_; // Value read from button bool state_; // Value read from button
bool lastState_; // Last value of button state bool lastState_; // Last value of button state
bool dblClickWaiting_; // whether we're waiting for a double click (down) bool dblClickWaiting_; // whether we're waiting for a double click (down)
bool dblClickOnNextUp_; // whether to register a double click on next release, or whether to wait and click bool dblClickOnNextUp_; // whether to register a double click on next release, or whether to wait and click
bool singleClickOK_; // whether it's OK to do a single click bool singleClickOK_; // whether it's OK to do a single click
uint32_t downTime_; // time the button was pressed down uint32_t downTime_; // time the button was pressed down
uint32_t upTime_; // time the button was released uint32_t upTime_; // time the button was released
bool ignoreUP_; // whether to ignore the button release because the click+hold was triggered bool ignoreUP_; // whether to ignore the button release because the click+hold was triggered
bool waitForUP_; // when held, whether to wait for the up event bool waitForUP_; // when held, whether to wait for the up event
bool longPressHappened_; // whether or not the hold event happened already bool longPressHappened_; // whether or not the hold event happened already
bool vLongPressHappened_; // whether or not the long hold event happened already bool vLongPressHappened_; // whether or not the long hold event happened already
bool buttonBusy_; // false if idle bool buttonBusy_; // false if idle
buttonEventHandler cb_onClick, cb_onDblClick, cb_onLongPress, cb_onVLongPress; buttonEventHandler cb_onClick, cb_onDblClick, cb_onLongPress, cb_onVLongPress;
}; };

View File

@@ -9,7 +9,7 @@ the LICENSE file.
#pragma once #pragma once
#ifndef EMC_TX_TIMEOUT #ifndef EMC_TX_TIMEOUT
#define EMC_TX_TIMEOUT 5000 #define EMC_TX_TIMEOUT 2000
#endif #endif
#ifndef EMC_RX_BUFFER_SIZE #ifndef EMC_RX_BUFFER_SIZE
@@ -29,7 +29,7 @@ the LICENSE file.
#endif #endif
#ifndef EMC_MIN_FREE_MEMORY #ifndef EMC_MIN_FREE_MEMORY
#define EMC_MIN_FREE_MEMORY 4096 #define EMC_MIN_FREE_MEMORY 16384
#endif #endif
#ifndef EMC_ESP8266_MULTITHREADING #ifndef EMC_ESP8266_MULTITHREADING

View File

@@ -16,7 +16,7 @@ the LICENSE file.
#define EMC_SEMAPHORE_TAKE() xSemaphoreTake(_xSemaphore, portMAX_DELAY) #define EMC_SEMAPHORE_TAKE() xSemaphoreTake(_xSemaphore, portMAX_DELAY)
#define EMC_SEMAPHORE_GIVE() xSemaphoreGive(_xSemaphore) #define EMC_SEMAPHORE_GIVE() xSemaphoreGive(_xSemaphore)
#define EMC_GET_FREE_MEMORY() std::max(ESP.getMaxAllocHeap(), ESP.getMaxAllocPsram()) #define EMC_GET_FREE_MEMORY() std::max(ESP.getMaxAllocHeap(), ESP.getMaxAllocPsram())
#define EMC_YIELD() taskYIELD() #define EMC_YIELD() vTaskDelay(1)
#define EMC_GENERATE_CLIENTID(x) snprintf(x, EMC_CLIENTID_LENGTH, "esp32%06llx", ESP.getEfuseMac()); #define EMC_GENERATE_CLIENTID(x) snprintf(x, EMC_CLIENTID_LENGTH, "esp32%06llx", ESP.getEfuseMac());
#elif defined(ARDUINO_ARCH_ESP8266) #elif defined(ARDUINO_ARCH_ESP8266)
#include <Arduino.h> // millis(), ESP.getFreeHeap(); #include <Arduino.h> // millis(), ESP.getFreeHeap();

View File

@@ -197,7 +197,7 @@ const char * MqttClient::getClientId() const {
} }
void MqttClient::loop() { void MqttClient::loop() {
switch (_state) { switch ((State)_state) { // modified by proddy for EMS-ESP compiling standalone
case State::disconnected: case State::disconnected:
#if defined(ARDUINO_ARCH_ESP32) #if defined(ARDUINO_ARCH_ESP32)
if (_useInternalTask == espMqttClientTypes::UseInternalTask::YES) { if (_useInternalTask == espMqttClientTypes::UseInternalTask::YES) {
@@ -332,8 +332,8 @@ int MqttClient::_sendPacket() {
EMC_SEMAPHORE_TAKE(); EMC_SEMAPHORE_TAKE();
OutgoingPacket * packet = _outbox.getCurrent(); OutgoingPacket * packet = _outbox.getCurrent();
int32_t wantToWrite = 0; size_t wantToWrite = 0;
int32_t written = 0; size_t written = 0;
if (packet && (wantToWrite == written)) { if (packet && (wantToWrite == written)) {
// mixing signed with unsigned here but safe because of MQTT packet size limits // mixing signed with unsigned here but safe because of MQTT packet size limits
wantToWrite = packet->packet.available(_bytesSent); wantToWrite = packet->packet.available(_bytesSent);
@@ -341,12 +341,7 @@ int MqttClient::_sendPacket() {
EMC_SEMAPHORE_GIVE(); EMC_SEMAPHORE_GIVE();
return 0; return 0;
} }
written = _transport->write(packet->packet.data(_bytesSent), wantToWrite); written = _transport->write(packet->packet.data(_bytesSent), wantToWrite);
if (written < 0) {
emc_log_w("Write error, check connection");
EMC_SEMAPHORE_GIVE();
return -1;
}
packet->timeSent = millis(); packet->timeSent = millis();
_lastClientActivity = millis(); _lastClientActivity = millis();
_bytesSent += written; _bytesSent += written;
@@ -707,7 +702,9 @@ uint16_t MqttClient::getQueue() const {
espMqttClientInternals::Outbox<OutgoingPacket>::Iterator it = _outbox.front(); espMqttClientInternals::Outbox<OutgoingPacket>::Iterator it = _outbox.front();
uint16_t count = 0; uint16_t count = 0;
while (it) { while (it) {
// if (it.get()->packet.packetType() == PacketType.PUBLISH) {
++count; ++count;
// }
++it; ++it;
} }
EMC_SEMAPHORE_GIVE(); EMC_SEMAPHORE_GIVE();

View File

@@ -24,167 +24,170 @@ the LICENSE file.
#include "Transport/Transport.h" #include "Transport/Transport.h"
class MqttClient { class MqttClient {
public: public:
virtual ~MqttClient(); virtual ~MqttClient();
bool connected() const; bool connected() const;
bool disconnected() const; bool disconnected() const;
bool connect(); bool connect();
bool disconnect(bool force = false); bool disconnect(bool force = false);
template <typename... Args> template <typename... Args>
uint16_t subscribe(const char* topic, uint8_t qos, Args&&... args) { uint16_t subscribe(const char * topic, uint8_t qos, Args &&... args) {
uint16_t packetId = _getNextPacketId(); uint16_t packetId = _getNextPacketId();
if (_state != State::connected) { if (_state != State::connected) {
packetId = 0; packetId = 0;
} else { } else {
EMC_SEMAPHORE_TAKE(); EMC_SEMAPHORE_TAKE();
if (!_addPacket(packetId, topic, qos, std::forward<Args>(args) ...)) { if (!_addPacket(packetId, topic, qos, std::forward<Args>(args)...)) {
emc_log_e("Could not create SUBSCRIBE packet"); emc_log_e("Could not create SUBSCRIBE packet");
packetId = 0; packetId = 0;
} }
EMC_SEMAPHORE_GIVE(); EMC_SEMAPHORE_GIVE();
}
return packetId;
} }
return packetId; template <typename... Args>
} uint16_t unsubscribe(const char * topic, Args &&... args) {
template <typename... Args> uint16_t packetId = _getNextPacketId();
uint16_t unsubscribe(const char* topic, Args&&... args) { if (_state != State::connected) {
uint16_t packetId = _getNextPacketId(); packetId = 0;
if (_state != State::connected) { } else {
packetId = 0; EMC_SEMAPHORE_TAKE();
} else { if (!_addPacket(packetId, topic, std::forward<Args>(args)...)) {
EMC_SEMAPHORE_TAKE(); emc_log_e("Could not create UNSUBSCRIBE packet");
if (!_addPacket(packetId, topic, std::forward<Args>(args) ...)) { packetId = 0;
emc_log_e("Could not create UNSUBSCRIBE packet"); }
packetId = 0; EMC_SEMAPHORE_GIVE();
} }
EMC_SEMAPHORE_GIVE(); return packetId;
} }
return packetId; uint16_t publish(const char * topic, uint8_t qos, bool retain, const uint8_t * payload, size_t length);
} uint16_t publish(const char * topic, uint8_t qos, bool retain, const char * payload);
uint16_t publish(const char* topic, uint8_t qos, bool retain, const uint8_t* payload, size_t length); uint16_t publish(const char * topic, uint8_t qos, bool retain, espMqttClientTypes::PayloadCallback callback, size_t length);
uint16_t publish(const char* topic, uint8_t qos, bool retain, const char* payload); void clearQueue(bool deleteSessionData = false); // Not MQTT compliant and may cause unpredictable results when `deleteSessionData` = true!
uint16_t publish(const char* topic, uint8_t qos, bool retain, espMqttClientTypes::PayloadCallback callback, size_t length); const char * getClientId() const;
void clearQueue(bool deleteSessionData = false); // Not MQTT compliant and may cause unpredictable results when `deleteSessionData` = true! uint16_t getQueue() const;
const char* getClientId() const; void loop();
uint16_t getQueue() const;
void loop();
protected: protected:
explicit MqttClient(espMqttClientTypes::UseInternalTask useInternalTask, uint8_t priority = 1, uint8_t core = 1); explicit MqttClient(espMqttClientTypes::UseInternalTask useInternalTask, uint8_t priority = 1, uint8_t core = 1);
espMqttClientTypes::UseInternalTask _useInternalTask; espMqttClientTypes::UseInternalTask _useInternalTask;
espMqttClientInternals::Transport* _transport; espMqttClientInternals::Transport * _transport;
espMqttClientTypes::OnConnectCallback _onConnectCallback; espMqttClientTypes::OnConnectCallback _onConnectCallback;
espMqttClientTypes::OnDisconnectCallback _onDisconnectCallback; espMqttClientTypes::OnDisconnectCallback _onDisconnectCallback;
espMqttClientTypes::OnSubscribeCallback _onSubscribeCallback; espMqttClientTypes::OnSubscribeCallback _onSubscribeCallback;
espMqttClientTypes::OnUnsubscribeCallback _onUnsubscribeCallback; espMqttClientTypes::OnUnsubscribeCallback _onUnsubscribeCallback;
espMqttClientTypes::OnMessageCallback _onMessageCallback; espMqttClientTypes::OnMessageCallback _onMessageCallback;
espMqttClientTypes::OnPublishCallback _onPublishCallback; espMqttClientTypes::OnPublishCallback _onPublishCallback;
espMqttClientTypes::OnErrorCallback _onErrorCallback; espMqttClientTypes::OnErrorCallback _onErrorCallback;
typedef void(*mqttClientHook)(void*); typedef void (*mqttClientHook)(void *);
const char* _clientId; const char * _clientId;
IPAddress _ip; IPAddress _ip;
const char* _host; const char * _host;
uint16_t _port; uint16_t _port;
bool _useIp; bool _useIp;
uint32_t _keepAlive; uint32_t _keepAlive;
bool _cleanSession; bool _cleanSession;
const char* _username; const char * _username;
const char* _password; const char * _password;
const char* _willTopic; const char * _willTopic;
const uint8_t* _willPayload; const uint8_t * _willPayload;
uint16_t _willPayloadLength; uint16_t _willPayloadLength;
uint8_t _willQos; uint8_t _willQos;
bool _willRetain; bool _willRetain;
uint32_t _timeout; uint32_t _timeout;
// state is protected to allow state changes by the transport system, defined in child classes // state is protected to allow state changes by the transport system, defined in child classes
// eg. to allow AsyncTCP // eg. to allow AsyncTCP
enum class State { enum class State {
disconnected = 0, disconnected = 0,
connectingTcp1 = 1, connectingTcp1 = 1,
connectingTcp2 = 2, connectingTcp2 = 2,
connectingMqtt = 3, connectingMqtt = 3,
connected = 4, connected = 4,
disconnectingMqtt1 = 5, disconnectingMqtt1 = 5,
disconnectingMqtt2 = 6, disconnectingMqtt2 = 6,
disconnectingTcp1 = 7, disconnectingTcp1 = 7,
disconnectingTcp2 = 8 disconnectingTcp2 = 8
}; };
std::atomic<State> _state; std::atomic<State> _state;
private: private:
char _generatedClientId[EMC_CLIENTID_LENGTH]; char _generatedClientId[EMC_CLIENTID_LENGTH];
uint16_t _packetId; uint16_t _packetId;
#if defined(ARDUINO_ARCH_ESP32) #if defined(ARDUINO_ARCH_ESP32)
SemaphoreHandle_t _xSemaphore; SemaphoreHandle_t _xSemaphore;
TaskHandle_t _taskHandle; TaskHandle_t _taskHandle;
static void _loop(MqttClient* c); static void _loop(MqttClient * c);
#elif defined(ARDUINO_ARCH_ESP8266) && EMC_ESP8266_MULTITHREADING #elif defined(ARDUINO_ARCH_ESP8266) && EMC_ESP8266_MULTITHREADING
std::atomic<bool> _xSemaphore = false; std::atomic<bool> _xSemaphore = false;
#elif defined(__linux__) #elif defined(__linux__)
std::mutex mtx; mutable std::mutex mtx; // modified by proddy for EMS-ESP compiling standalone
#endif #endif
uint8_t _rxBuffer[EMC_RX_BUFFER_SIZE]; uint8_t _rxBuffer[EMC_RX_BUFFER_SIZE];
struct OutgoingPacket { struct OutgoingPacket {
uint32_t timeSent; uint32_t timeSent;
espMqttClientInternals::Packet packet; espMqttClientInternals::Packet packet;
template <typename... Args>
OutgoingPacket(uint32_t t, espMqttClientTypes::Error error, Args &&... args)
: timeSent(t)
, packet(error, std::forward<Args>(args)...) {
}
};
espMqttClientInternals::Outbox<OutgoingPacket> _outbox;
size_t _bytesSent;
espMqttClientInternals::Parser _parser;
uint32_t _lastClientActivity;
uint32_t _lastServerActivity;
bool _pingSent;
espMqttClientTypes::DisconnectReason _disconnectReason;
uint16_t _getNextPacketId();
template <typename... Args> template <typename... Args>
OutgoingPacket(uint32_t t, espMqttClientTypes::Error error, Args&&... args) : bool _addPacket(Args &&... args) {
timeSent(t), espMqttClientTypes::Error error(espMqttClientTypes::Error::SUCCESS);
packet(error, std::forward<Args>(args) ...) {} espMqttClientInternals::Outbox<OutgoingPacket>::Iterator it = _outbox.emplace(0, error, std::forward<Args>(args)...);
}; if (it && error == espMqttClientTypes::Error::SUCCESS)
espMqttClientInternals::Outbox<OutgoingPacket> _outbox; return true;
size_t _bytesSent; return false;
espMqttClientInternals::Parser _parser; }
uint32_t _lastClientActivity;
uint32_t _lastServerActivity;
bool _pingSent;
espMqttClientTypes::DisconnectReason _disconnectReason;
uint16_t _getNextPacketId(); template <typename... Args>
bool _addPacketFront(Args &&... args) {
espMqttClientTypes::Error error(espMqttClientTypes::Error::SUCCESS);
espMqttClientInternals::Outbox<OutgoingPacket>::Iterator it = _outbox.emplaceFront(0, error, std::forward<Args>(args)...);
if (it && error == espMqttClientTypes::Error::SUCCESS)
return true;
return false;
}
template <typename... Args> void _checkOutbox();
bool _addPacket(Args&&... args) { int _sendPacket();
espMqttClientTypes::Error error(espMqttClientTypes::Error::SUCCESS); bool _advanceOutbox();
espMqttClientInternals::Outbox<OutgoingPacket>::Iterator it = _outbox.emplace(0, error, std::forward<Args>(args) ...); void _checkIncoming();
if (it && error == espMqttClientTypes::Error::SUCCESS) return true; void _checkPing();
return false; void _checkTimeout();
}
template <typename... Args> void _onConnack();
bool _addPacketFront(Args&&... args) { void _onPublish();
espMqttClientTypes::Error error(espMqttClientTypes::Error::SUCCESS); void _onPuback();
espMqttClientInternals::Outbox<OutgoingPacket>::Iterator it = _outbox.emplaceFront(0, error, std::forward<Args>(args) ...); void _onPubrec();
if (it && error == espMqttClientTypes::Error::SUCCESS) return true; void _onPubrel();
return false; void _onPubcomp();
} void _onSuback();
void _onUnsuback();
void _checkOutbox(); void _clearQueue(int clearData); // 0: keep session,
int _sendPacket(); // 1: keep only PUBLISH qos > 0
bool _advanceOutbox(); // 2: delete all
void _checkIncoming(); void _onError(uint16_t packetId, espMqttClientTypes::Error error);
void _checkPing();
void _checkTimeout();
void _onConnack(); #if defined(ARDUINO_ARCH_ESP32)
void _onPublish(); #if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO
void _onPuback(); size_t _highWaterMark;
void _onPubrec(); #endif
void _onPubrel(); #endif
void _onPubcomp();
void _onSuback();
void _onUnsuback();
void _clearQueue(int clearData); // 0: keep session,
// 1: keep only PUBLISH qos > 0
// 2: delete all
void _onError(uint16_t packetId, espMqttClientTypes::Error error);
#if defined(ARDUINO_ARCH_ESP32)
#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO
size_t _highWaterMark;
#endif
#endif
}; };

View File

@@ -38,7 +38,7 @@ bool ClientPosix::connect(IPAddress ip, uint16_t port) {
memset(&_host, 0, sizeof(_host)); memset(&_host, 0, sizeof(_host));
_host.sin_family = AF_INET; _host.sin_family = AF_INET;
_host.sin_addr.s_addr = htonl(uint32_t(ip)); _host.sin_addr.s_addr = htonl(uint32_t(ip));
_host.sin_port = ::htons(port); _host.sin_port = htons(port); // modified by proddy for EMS-ESP compiling standalone
int ret = ::connect(_sockfd, (struct sockaddr *)&_host, sizeof(_host)); int ret = ::connect(_sockfd, (struct sockaddr *)&_host, sizeof(_host));

View File

@@ -1,6 +1,6 @@
#include <APSettingsService.h> #include <APSettingsService.h>
#include "../../src/emsesp_stub.hpp" // proddy added #include "../../src/emsesp_stub.hpp"
APSettingsService::APSettingsService(AsyncWebServer * server, FS * fs, SecurityManager * securityManager) APSettingsService::APSettingsService(AsyncWebServer * server, FS * fs, SecurityManager * securityManager)
: _httpEndpoint(APSettings::read, APSettings::update, this, server, AP_SETTINGS_SERVICE_PATH, securityManager) : _httpEndpoint(APSettings::read, APSettings::update, this, server, AP_SETTINGS_SERVICE_PATH, securityManager)

View File

@@ -59,10 +59,17 @@ class ESP8266React {
return &_mqttSettingsService; return &_mqttSettingsService;
} }
espMqttClient * getMqttClient() { MqttClient * getMqttClient() {
return _mqttSettingsService.getMqttClient(); return _mqttSettingsService.getMqttClient();
} }
void setWill(const char * will_topic) {
_mqttSettingsService.setWill(will_topic);
}
void onMessage(espMqttClientTypes::OnMessageCallback callback) {
_mqttSettingsService.onMessage(callback);
}
void factoryReset() { void factoryReset() {
_factoryResetService.factoryReset(); _factoryResetService.factoryReset();
} }

View File

@@ -18,10 +18,7 @@ void FactoryResetService::handleRequest(AsyncWebServerRequest * request) {
* Delete function assumes that all files are stored flat, within the config directory. * Delete function assumes that all files are stored flat, within the config directory.
*/ */
void FactoryResetService::factoryReset() { void FactoryResetService::factoryReset() {
/* // TODO To replaced with fs.rmdir(FS_CONFIG_DIRECTORY) now we're using IDF 4.2
* Based on LittleFS. Modified by proddy
* Could be replaced with fs.rmdir(FS_CONFIG_DIRECTORY) in IDF 4.2
*/
File root = fs->open(FS_CONFIG_DIRECTORY); File root = fs->open(FS_CONFIG_DIRECTORY);
File file; File file;
while (file = root.openNextFile()) { while (file = root.openNextFile()) {

View File

@@ -1,4 +1,5 @@
#include <FeaturesService.h> #include <FeaturesService.h>
#include "../../src/emsesp_stub.hpp"
using namespace std::placeholders; // for `_1` etc using namespace std::placeholders; // for `_1` etc
@@ -9,36 +10,10 @@ FeaturesService::FeaturesService(AsyncWebServer * server) {
void FeaturesService::features(AsyncWebServerRequest * request) { void FeaturesService::features(AsyncWebServerRequest * request) {
AsyncJsonResponse * response = new AsyncJsonResponse(false, MAX_FEATURES_SIZE); AsyncJsonResponse * response = new AsyncJsonResponse(false, MAX_FEATURES_SIZE);
JsonObject root = response->getRoot(); JsonObject root = response->getRoot();
#if FT_ENABLED(FT_PROJECT)
root["project"] = true; root["version"] = EMSESP_APP_VERSION;
#else root["platform"] = EMSESP_PLATFORM;
root["project"] = false;
#endif
#if FT_ENABLED(FT_SECURITY)
root["security"] = true;
#else
root["security"] = false;
#endif
#if FT_ENABLED(FT_MQTT)
root["mqtt"] = true;
#else
root["mqtt"] = false;
#endif
#if FT_ENABLED(FT_NTP)
root["ntp"] = true;
#else
root["ntp"] = false;
#endif
#if FT_ENABLED(FT_OTA)
root["ota"] = true;
#else
root["ota"] = false;
#endif
#if FT_ENABLED(FT_UPLOAD_FIRMWARE)
root["upload_firmware"] = true;
#else
root["upload_firmware"] = false;
#endif
response->setLength(); response->setLength();
request->send(response); request->send(response);
} }

View File

@@ -115,7 +115,7 @@ class HttpPostEndpoint {
response->setLength(); response->setLength();
if (outcome == StateUpdateResult::CHANGED_RESTART) { if (outcome == StateUpdateResult::CHANGED_RESTART) {
response->setCode(205); // added by proddy, reboot required response->setCode(205); // reboot required
} }
request->send(response); request->send(response);
} }

View File

@@ -1,6 +1,6 @@
#include <MqttSettingsService.h> #include <MqttSettingsService.h>
#include "../../src/emsesp_stub.hpp" // proddy added #include "../../src/emsesp_stub.hpp"
using namespace std::placeholders; // for `_1` etc using namespace std::placeholders; // for `_1` etc
@@ -34,10 +34,8 @@ MqttSettingsService::MqttSettingsService(AsyncWebServer * server, FS * fs, Secur
, _reconfigureMqtt(false) , _reconfigureMqtt(false)
, _disconnectedAt(0) , _disconnectedAt(0)
, _disconnectReason(espMqttClientTypes::DisconnectReason::TCP_DISCONNECTED) , _disconnectReason(espMqttClientTypes::DisconnectReason::TCP_DISCONNECTED)
, _mqttClient(espMqttClientTypes::UseInternalTask::NO) { , _mqttClient(nullptr) {
WiFi.onEvent(std::bind(&MqttSettingsService::WiFiEvent, this, _1, _2)); WiFi.onEvent(std::bind(&MqttSettingsService::WiFiEvent, this, _1, _2));
_mqttClient.onConnect(std::bind(&MqttSettingsService::onMqttConnect, this, _1));
_mqttClient.onDisconnect(std::bind(&MqttSettingsService::onMqttDisconnect, this, _1));
addUpdateHandler([&](const String & originId) { onConfigUpdated(); }, false); addUpdateHandler([&](const String & originId) { onConfigUpdated(); }, false);
} }
@@ -46,18 +44,46 @@ MqttSettingsService::~MqttSettingsService() {
void MqttSettingsService::begin() { void MqttSettingsService::begin() {
_fsPersistence.readFromFS(); _fsPersistence.readFromFS();
startClient();
}
void MqttSettingsService::startClient() {
static bool isSecure = false;
if (_mqttClient != nullptr) {
// do we need to change the client?
if ((isSecure && _state.rootCA.length() > 0) || (!isSecure && _state.rootCA.length() == 0)) {
return;
}
delete _mqttClient;
}
#if CONFIG_IDF_TARGET_ESP32S3
if (_state.rootCA.length() > 0) {
isSecure = true;
_mqttClient = static_cast<MqttClient *>(new espMqttClientSecure(espMqttClientTypes::UseInternalTask::NO));
if (_state.rootCA == "insecure") {
static_cast<espMqttClientSecure *>(_mqttClient)->setInsecure();
} else {
String certificate = "-----BEGIN CERTIFICATE-----\n" + _state.rootCA + "\n-----END CERTIFICATE-----\n";
static_cast<espMqttClientSecure *>(_mqttClient)->setCACert(retainCstr(certificate.c_str(), &_retainedRootCA));
}
static_cast<espMqttClientSecure *>(_mqttClient)->onConnect(std::bind(&MqttSettingsService::onMqttConnect, this, _1));
static_cast<espMqttClientSecure *>(_mqttClient)->onDisconnect(std::bind(&MqttSettingsService::onMqttDisconnect, this, _1));
return;
}
#endif
isSecure = false;
_mqttClient = static_cast<MqttClient *>(new espMqttClient(espMqttClientTypes::UseInternalTask::NO));
static_cast<espMqttClient *>(_mqttClient)->onConnect(std::bind(&MqttSettingsService::onMqttConnect, this, _1));
static_cast<espMqttClient *>(_mqttClient)->onDisconnect(std::bind(&MqttSettingsService::onMqttDisconnect, this, _1));
} }
void MqttSettingsService::loop() { void MqttSettingsService::loop() {
if (_reconfigureMqtt || (_disconnectedAt && (uint32_t)(uuid::get_uptime() - _disconnectedAt) >= MQTT_RECONNECTION_DELAY)) { if (_reconfigureMqtt || (_disconnectedAt && (uint32_t)(uuid::get_uptime() - _disconnectedAt) >= MQTT_RECONNECTION_DELAY)) {
// reconfigure MQTT client // reconfigure MQTT client
_disconnectedAt = uuid::get_uptime(); _disconnectedAt = configureMqtt() ? 0 : uuid::get_uptime();
if (configureMqtt()) {
_disconnectedAt = 0;
}
_reconfigureMqtt = false; _reconfigureMqtt = false;
} }
_mqttClient.loop(); _mqttClient->loop();
} }
bool MqttSettingsService::isEnabled() { bool MqttSettingsService::isEnabled() {
@@ -65,19 +91,39 @@ bool MqttSettingsService::isEnabled() {
} }
bool MqttSettingsService::isConnected() { bool MqttSettingsService::isConnected() {
return _mqttClient.connected(); return _mqttClient->connected();
} }
const char * MqttSettingsService::getClientId() { const char * MqttSettingsService::getClientId() {
return _mqttClient.getClientId(); return _mqttClient->getClientId();
}
void MqttSettingsService::setWill(const char * topic) {
#if CONFIG_IDF_TARGET_ESP32S3
if (_state.rootCA.length() > 0) {
static_cast<espMqttClientSecure *>(_mqttClient)->setWill(topic, 1, true, "offline");
return;
}
#endif
static_cast<espMqttClient *>(_mqttClient)->setWill(topic, 1, true, "offline");
}
void MqttSettingsService::onMessage(espMqttClientTypes::OnMessageCallback callback) {
#if CONFIG_IDF_TARGET_ESP32S3
if (_state.rootCA.length() > 0) {
static_cast<espMqttClientSecure *>(_mqttClient)->onMessage(callback);
return;
}
#endif
static_cast<espMqttClient *>(_mqttClient)->onMessage(callback);
} }
espMqttClientTypes::DisconnectReason MqttSettingsService::getDisconnectReason() { espMqttClientTypes::DisconnectReason MqttSettingsService::getDisconnectReason() {
return _disconnectReason; return _disconnectReason;
} }
espMqttClient * MqttSettingsService::getMqttClient() { MqttClient * MqttSettingsService::getMqttClient() {
return &_mqttClient; return _mqttClient;
} }
void MqttSettingsService::onMqttConnect(bool sessionPresent) { void MqttSettingsService::onMqttConnect(bool sessionPresent) {
@@ -87,7 +133,6 @@ void MqttSettingsService::onMqttConnect(bool sessionPresent) {
} }
void MqttSettingsService::onMqttDisconnect(espMqttClientTypes::DisconnectReason reason) { void MqttSettingsService::onMqttDisconnect(espMqttClientTypes::DisconnectReason reason) {
// emsesp::EMSESP::logger().info("Disconnected from MQTT reason: %d", (uint8_t)reason);
_disconnectReason = reason; _disconnectReason = reason;
if (!_disconnectedAt) { if (!_disconnectedAt) {
_disconnectedAt = uuid::get_uptime(); _disconnectedAt = uuid::get_uptime();
@@ -99,7 +144,7 @@ void MqttSettingsService::onConfigUpdated() {
_reconfigureMqtt = true; _reconfigureMqtt = true;
_disconnectedAt = 0; _disconnectedAt = 0;
// added by proddy startClient();
emsesp::EMSESP::mqtt_.start(); // reload EMS-ESP MQTT settings emsesp::EMSESP::mqtt_.start(); // reload EMS-ESP MQTT settings
} }
@@ -110,15 +155,13 @@ void MqttSettingsService::WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info) {
case ARDUINO_EVENT_ETH_GOT_IP6: case ARDUINO_EVENT_ETH_GOT_IP6:
case ARDUINO_EVENT_WIFI_STA_GOT_IP6: case ARDUINO_EVENT_WIFI_STA_GOT_IP6:
if (_state.enabled) { if (_state.enabled) {
// emsesp::EMSESP::logger().info("IPv4 Network connection found, starting MQTT client");
onConfigUpdated(); onConfigUpdated();
} }
break; break;
case ARDUINO_EVENT_WIFI_STA_DISCONNECTED: case ARDUINO_EVENT_WIFI_STA_DISCONNECTED:
case ARDUINO_EVENT_ETH_DISCONNECTED: case ARDUINO_EVENT_ETH_DISCONNECTED:
if (_state.enabled) { if (_state.enabled) {
// emsesp::EMSESP::logger().info("Network connection dropped, stopping MQTT client"); _mqttClient->disconnect(true);
_mqttClient.disconnect(true);
} }
break; break;
@@ -128,29 +171,54 @@ void MqttSettingsService::WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info) {
} }
bool MqttSettingsService::configureMqtt() { bool MqttSettingsService::configureMqtt() {
// disconnect if connected // disconnect if already connected
_mqttClient.disconnect(true); if (_mqttClient->connected()) {
emsesp::EMSESP::logger().info("Disconnecting to configure");
_mqttClient->disconnect(true);
}
// only connect if WiFi is connected and MQTT is enabled // only connect if WiFi is connected and MQTT is enabled
if (_state.enabled && emsesp::EMSESP::system_.network_connected() && !_state.host.isEmpty()) { if (_state.enabled && emsesp::EMSESP::system_.network_connected() && !_state.host.isEmpty()) {
// emsesp::EMSESP::logger().info("Configuring MQTT client"); _reconfigureMqtt = false;
_mqttClient.setServer(retainCstr(_state.host.c_str(), &_retainedHost), _state.port); #if CONFIG_IDF_TARGET_ESP32S3
if (_state.username.length() > 0) { if (_state.rootCA.length() > 0) {
_mqttClient.setCredentials(retainCstr(_state.username.c_str(), &_retainedUsername), // emsesp::EMSESP::logger().info("Start secure MQTT with rootCA");
retainCstr(_state.password.length() > 0 ? _state.password.c_str() : nullptr, &_retainedPassword)); static_cast<espMqttClientSecure *>(_mqttClient)->setServer(retainCstr(_state.host.c_str(), &_retainedHost), _state.port);
} else { if (_state.username.length() > 0) {
_mqttClient.setCredentials(retainCstr(nullptr, &_retainedUsername), retainCstr(nullptr, &_retainedPassword)); static_cast<espMqttClientSecure *>(_mqttClient)
->setCredentials(retainCstr(_state.username.c_str(), &_retainedUsername),
retainCstr(_state.password.length() > 0 ? _state.password.c_str() : nullptr, &_retainedPassword));
} else {
static_cast<espMqttClientSecure *>(_mqttClient)->setCredentials(retainCstr(nullptr, &_retainedUsername), retainCstr(nullptr, &_retainedPassword));
}
static_cast<espMqttClientSecure *>(_mqttClient)->setClientId(retainCstr(_state.clientId.c_str(), &_retainedClientId));
static_cast<espMqttClientSecure *>(_mqttClient)->setKeepAlive(_state.keepAlive);
static_cast<espMqttClientSecure *>(_mqttClient)->setCleanSession(_state.cleanSession);
return _mqttClient->connect();
} }
_mqttClient.setClientId(retainCstr(_state.clientId.c_str(), &_retainedClientId)); #endif
_mqttClient.setKeepAlive(_state.keepAlive); // emsesp::EMSESP::logger().info("Configuring MQTT client");
_mqttClient.setCleanSession(_state.cleanSession); static_cast<espMqttClient *>(_mqttClient)->setServer(retainCstr(_state.host.c_str(), &_retainedHost), _state.port);
return _mqttClient.connect(); if (_state.username.length() > 0) {
// } else { static_cast<espMqttClient *>(_mqttClient)
// emsesp::EMSESP::logger().info("Error configuring MQTT client"); ->setCredentials(retainCstr(_state.username.c_str(), &_retainedUsername),
retainCstr(_state.password.length() > 0 ? _state.password.c_str() : nullptr, &_retainedPassword));
} else {
static_cast<espMqttClient *>(_mqttClient)->setCredentials(retainCstr(nullptr, &_retainedUsername), retainCstr(nullptr, &_retainedPassword));
}
static_cast<espMqttClient *>(_mqttClient)->setClientId(retainCstr(_state.clientId.c_str(), &_retainedClientId));
static_cast<espMqttClient *>(_mqttClient)->setKeepAlive(_state.keepAlive);
static_cast<espMqttClient *>(_mqttClient)->setCleanSession(_state.cleanSession);
return _mqttClient->connect();
} }
return false; return false;
} }
void MqttSettings::read(MqttSettings & settings, JsonObject & root) { void MqttSettings::read(MqttSettings & settings, JsonObject & root) {
#if CONFIG_IDF_TARGET_ESP32S3
root["rootCA"] = settings.rootCA;
#endif
root["enabled"] = settings.enabled; root["enabled"] = settings.enabled;
root["host"] = settings.host; root["host"] = settings.host;
root["port"] = settings.port; root["port"] = settings.port;
@@ -162,7 +230,6 @@ void MqttSettings::read(MqttSettings & settings, JsonObject & root) {
root["clean_session"] = settings.cleanSession; root["clean_session"] = settings.cleanSession;
root["entity_format"] = settings.entity_format; root["entity_format"] = settings.entity_format;
// added by proddy for EMS-ESP
root["publish_time_boiler"] = settings.publish_time_boiler; root["publish_time_boiler"] = settings.publish_time_boiler;
root["publish_time_thermostat"] = settings.publish_time_thermostat; root["publish_time_thermostat"] = settings.publish_time_thermostat;
root["publish_time_solar"] = settings.publish_time_solar; root["publish_time_solar"] = settings.publish_time_solar;
@@ -185,6 +252,9 @@ StateUpdateResult MqttSettings::update(JsonObject & root, MqttSettings & setting
MqttSettings newSettings = {}; MqttSettings newSettings = {};
bool changed = false; bool changed = false;
#if CONFIG_IDF_TARGET_ESP32S3
newSettings.rootCA = root["rootCA"] | "";
#endif
newSettings.enabled = root["enabled"] | FACTORY_MQTT_ENABLED; newSettings.enabled = root["enabled"] | FACTORY_MQTT_ENABLED;
newSettings.host = root["host"] | FACTORY_MQTT_HOST; newSettings.host = root["host"] | FACTORY_MQTT_HOST;
newSettings.port = root["port"] | FACTORY_MQTT_PORT; newSettings.port = root["port"] | FACTORY_MQTT_PORT;
@@ -300,6 +370,20 @@ StateUpdateResult MqttSettings::update(JsonObject & root, MqttSettings & setting
emsesp::EMSESP::mqtt_.set_publish_time_heartbeat(newSettings.publish_time_heartbeat); emsesp::EMSESP::mqtt_.set_publish_time_heartbeat(newSettings.publish_time_heartbeat);
} }
#if CONFIG_IDF_TARGET_ESP32S3
// strip down to certificate only
newSettings.rootCA.replace("\r", "");
newSettings.rootCA.replace("\n", "");
newSettings.rootCA.replace("-----BEGIN CERTIFICATE-----", "");
newSettings.rootCA.replace("-----END CERTIFICATE-----", "");
newSettings.rootCA.replace(" ", "");
if (newSettings.rootCA.length() == 0 && newSettings.port > 8800) {
newSettings.rootCA = "insecure";
}
if (newSettings.rootCA != settings.rootCA) {
changed = true;
}
#endif
// save the new settings // save the new settings
settings = newSettings; settings = newSettings;

View File

@@ -63,6 +63,7 @@ class MqttSettings {
bool enabled; bool enabled;
String host; String host;
uint16_t port; uint16_t port;
String rootCA;
// username and password // username and password
String username; String username;
@@ -75,7 +76,7 @@ class MqttSettings {
uint16_t keepAlive; uint16_t keepAlive;
bool cleanSession; bool cleanSession;
// proddy EMS-ESP specific // EMS-ESP specific
String base; String base;
uint16_t publish_time_boiler; uint16_t publish_time_boiler;
uint16_t publish_time_thermostat; uint16_t publish_time_thermostat;
@@ -105,12 +106,15 @@ class MqttSettingsService : public StatefulService<MqttSettings> {
~MqttSettingsService(); ~MqttSettingsService();
void begin(); void begin();
void startClient();
void loop(); void loop();
bool isEnabled(); bool isEnabled();
bool isConnected(); bool isConnected();
const char * getClientId(); const char * getClientId();
espMqttClientTypes::DisconnectReason getDisconnectReason(); espMqttClientTypes::DisconnectReason getDisconnectReason();
espMqttClient * getMqttClient(); MqttClient * getMqttClient();
void setWill(const char * topic);
void onMessage(espMqttClientTypes::OnMessageCallback callback);
protected: protected:
void onConfigUpdated(); void onConfigUpdated();
@@ -125,6 +129,7 @@ class MqttSettingsService : public StatefulService<MqttSettings> {
char * _retainedClientId; char * _retainedClientId;
char * _retainedUsername; char * _retainedUsername;
char * _retainedPassword; char * _retainedPassword;
char * _retainedRootCA;
// variable to help manage connection // variable to help manage connection
bool _reconfigureMqtt; bool _reconfigureMqtt;
@@ -134,7 +139,7 @@ class MqttSettingsService : public StatefulService<MqttSettings> {
espMqttClientTypes::DisconnectReason _disconnectReason; espMqttClientTypes::DisconnectReason _disconnectReason;
// the MQTT client instance // the MQTT client instance
espMqttClient _mqttClient; MqttClient * _mqttClient;
void WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info); void WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info);
void onMqttConnect(bool sessionPresent); void onMqttConnect(bool sessionPresent);

View File

@@ -1,6 +1,6 @@
#include <MqttStatus.h> #include <MqttStatus.h>
#include "../../src/emsesp_stub.hpp" // proddy added #include "../../src/emsesp_stub.hpp"
using namespace std::placeholders; // for `_1` etc using namespace std::placeholders; // for `_1` etc
@@ -20,9 +20,9 @@ void MqttStatus::mqttStatus(AsyncWebServerRequest * request) {
root["client_id"] = _mqttSettingsService->getClientId(); root["client_id"] = _mqttSettingsService->getClientId();
root["disconnect_reason"] = (uint8_t)_mqttSettingsService->getDisconnectReason(); root["disconnect_reason"] = (uint8_t)_mqttSettingsService->getDisconnectReason();
root["mqtt_queued"] = emsesp::Mqtt::publish_queued(); // mdvp added root["mqtt_queued"] = emsesp::Mqtt::publish_queued();
root["mqtt_fails"] = emsesp::Mqtt::publish_fails(); // proddy added root["mqtt_fails"] = emsesp::Mqtt::publish_fails();
root["connect_count"] = emsesp::Mqtt::connect_count(); // mdvp added root["connect_count"] = emsesp::Mqtt::connect_count();
response->setLength(); response->setLength();
request->send(response); request->send(response);

View File

@@ -1,7 +1,7 @@
#include <NTPSettingsService.h> #include <NTPSettingsService.h>
#include <esp_sntp.h> #include <esp_sntp.h>
#include "../../src/emsesp_stub.hpp" // proddy added #include "../../src/emsesp_stub.hpp"
using namespace std::placeholders; // for `_1` etc using namespace std::placeholders; // for `_1` etc

View File

@@ -1,5 +1,5 @@
#include <NTPStatus.h> #include <NTPStatus.h>
#include "../../src/emsesp_stub.hpp" // proddy added #include "../../src/emsesp_stub.hpp"
using namespace std::placeholders; // for `_1` etc using namespace std::placeholders; // for `_1` etc

View File

@@ -69,7 +69,7 @@ void NetworkSettingsService::manageSTA() {
esp_wifi_set_bandwidth((wifi_interface_t)ESP_IF_WIFI_STA, WIFI_BW_HT40); esp_wifi_set_bandwidth((wifi_interface_t)ESP_IF_WIFI_STA, WIFI_BW_HT40);
} }
if (networkSettings.nosleep) { if (networkSettings.nosleep) {
WiFi.setSleep(false); // turn off sleep - WIFI_PS_NONE WiFi.setSleep(false); // turn off sleep - WIFI_PS_NONE
} }
WiFi.begin(_state.ssid.c_str(), _state.password.c_str()); // attempt to connect to the network WiFi.begin(_state.ssid.c_str(), _state.password.c_str()); // attempt to connect to the network
esp_wifi_set_max_tx_power(networkSettings.tx_power * 4); // set power after wifi is startet for C3 esp_wifi_set_max_tx_power(networkSettings.tx_power * 4); // set power after wifi is startet for C3

View File

@@ -1,6 +1,6 @@
#include <NetworkStatus.h> #include <NetworkStatus.h>
#include "../../src/emsesp_stub.hpp" // proddy added #include "../../src/emsesp_stub.hpp"
using namespace std::placeholders; // for `_1` etc using namespace std::placeholders; // for `_1` etc

View File

@@ -1,6 +1,6 @@
#include <OTASettingsService.h> #include <OTASettingsService.h>
#include "../../src/emsesp_stub.hpp" // proddy added #include "../../src/emsesp_stub.hpp"
using namespace std::placeholders; // for `_1` etc using namespace std::placeholders; // for `_1` etc

View File

@@ -2,7 +2,7 @@
#if FT_ENABLED(FT_SECURITY) #if FT_ENABLED(FT_SECURITY)
#include "../../src/emsesp_stub.hpp" // proddy added #include "../../src/emsesp_stub.hpp"
SecuritySettingsService::SecuritySettingsService(AsyncWebServer * server, FS * fs) SecuritySettingsService::SecuritySettingsService(AsyncWebServer * server, FS * fs)
: _httpEndpoint(SecuritySettings::read, SecuritySettings::update, this, server, SECURITY_SETTINGS_PATH, this) : _httpEndpoint(SecuritySettings::read, SecuritySettings::update, this, server, SECURITY_SETTINGS_PATH, this)

View File

@@ -19,7 +19,7 @@
enum class StateUpdateResult { enum class StateUpdateResult {
CHANGED = 0, // The update changed the state and propagation should take place if required CHANGED = 0, // The update changed the state and propagation should take place if required
CHANGED_RESTART, // a restart of the device is needed - added by proddy CHANGED_RESTART, // a restart of the device is needed
UNCHANGED, // The state was unchanged, propagation should not take place UNCHANGED, // The state was unchanged, propagation should not take place
ERROR // There was a problem updating the state, propagation should not take place ERROR // There was a problem updating the state, propagation should not take place
}; };

View File

@@ -1,7 +1,7 @@
#include <SystemStatus.h> #include <SystemStatus.h>
#include <esp_ota_ops.h> #include <esp_ota_ops.h>
#include "../../src/emsesp_stub.hpp" // proddy added #include "../../src/emsesp_stub.hpp"
using namespace std::placeholders; // for `_1` etc using namespace std::placeholders; // for `_1` etc
@@ -39,7 +39,7 @@ void SystemStatus::systemStatus(AsyncWebServerRequest * request) {
const esp_partition_t * partition = esp_partition_find_first(ESP_PARTITION_TYPE_APP, ESP_PARTITION_SUBTYPE_APP_FACTORY, NULL); const esp_partition_t * partition = esp_partition_find_first(ESP_PARTITION_TYPE_APP, ESP_PARTITION_SUBTYPE_APP_FACTORY, NULL);
if (partition != NULL) { // factory partition found if (partition != NULL) { // factory partition found
root["has_loader"] = true; root["has_loader"] = true;
} else { // check for not empty, smaller OTA partition } else { // check for not empty, smaller OTA partition
partition = esp_ota_get_next_update_partition(NULL); partition = esp_ota_get_next_update_partition(NULL);
if (partition) { if (partition) {
uint64_t buffer; uint64_t buffer;

View File

@@ -1,4 +1,5 @@
#include <UploadFileService.h> #include <UploadFileService.h>
#include <esp_ota_ops.h>
using namespace std::placeholders; // for `_1` etc using namespace std::placeholders; // for `_1` etc
@@ -42,7 +43,8 @@ void UploadFileService::handleUpload(AsyncWebServerRequest * request, const Stri
return; return;
} else { } else {
md5[0] = '\0'; md5[0] = '\0';
return; // unsupported file type handleError(request, 406); // Not Acceptable - unsupported file type
return;
} }
if (is_firmware) { if (is_firmware) {
@@ -69,7 +71,7 @@ void UploadFileService::handleUpload(AsyncWebServerRequest * request, const Stri
} }
#endif #endif
// it's firmware - initialize the ArduinoOTA updater // it's firmware - initialize the ArduinoOTA updater
if (Update.begin()) { if (Update.begin(fsize - sizeof(esp_image_header_t))) {
if (strlen(md5) == 32) { if (strlen(md5) == 32) {
Update.setMD5(md5); Update.setMD5(md5);
md5[0] = '\0'; md5[0] = '\0';
@@ -87,7 +89,9 @@ void UploadFileService::handleUpload(AsyncWebServerRequest * request, const Stri
if (!is_firmware) { if (!is_firmware) {
if (len) { if (len) {
request->_tempFile.write(data, len); // stream the incoming chunk to the opened file if (len != request->_tempFile.write(data, len)) { // stream the incoming chunk to the opened file
handleError(request, 507); // 507-Insufficient Storage
}
} }
} else { } else {
// if we haven't delt with an error, continue with the firmware update // if we haven't delt with an error, continue with the firmware update
@@ -122,18 +126,17 @@ void UploadFileService::uploadComplete(AsyncWebServerRequest * request) {
request->send(response); request->send(response);
return; return;
} }
if (strlen(md5) == 32) { if (strlen(md5) == 32) {
auto * response = new AsyncJsonResponse(false, 256); auto * response = new AsyncJsonResponse(false, 256);
JsonObject root = response->getRoot(); JsonObject root = response->getRoot();
root["md5"] = md5; root["md5"] = md5;
response->setLength(); response->setLength();
request->send(response); request->send(response);
// AsyncWebServerResponse * response = request->beginResponse(201, "text/plain", md5); // created
// request->send(response);
return; return;
} }
handleError(request, 403); // send the forbidden response handleError(request, 500);
} }
void UploadFileService::handleError(AsyncWebServerRequest * request, int code) { void UploadFileService::handleError(AsyncWebServerRequest * request, int code) {
@@ -143,9 +146,15 @@ void UploadFileService::handleError(AsyncWebServerRequest * request, int code) {
} }
// send the error code to the client and record the error code in the temp object // send the error code to the client and record the error code in the temp object
request->_tempObject = new int(code);
AsyncWebServerResponse * response = request->beginResponse(code); AsyncWebServerResponse * response = request->beginResponse(code);
request->send(response); request->send(response);
// check for invalid extension and immediately kill the connection, which will through an error
// that is caught by the web code. Unfortunately the http error code is not sent to the client on fast network connections
if (code == 406) {
request->client()->close(true);
handleEarlyDisconnect();
}
} }
void UploadFileService::handleEarlyDisconnect() { void UploadFileService::handleEarlyDisconnect() {

View File

@@ -22,7 +22,6 @@
namespace uuid { namespace uuid {
// added by proddy for EMS-ESP
static uint64_t now_millis = 0; static uint64_t now_millis = 0;
// returns system uptime in seconds // returns system uptime in seconds

View File

@@ -21,7 +21,7 @@
namespace uuid { namespace uuid {
void loop() { void loop() {
set_uptime(); // added by proddy set_uptime();
} }
} // namespace uuid } // namespace uuid

View File

@@ -102,8 +102,8 @@ void loop();
*/ */
uint64_t get_uptime_ms(); uint64_t get_uptime_ms();
uint32_t get_uptime(); // added by proddy for EMS-ESP uint32_t get_uptime();
uint32_t get_uptime_sec(); // added by proddy for EMS-ESP uint32_t get_uptime_sec();
void set_uptime(); void set_uptime();

View File

@@ -65,7 +65,6 @@ void Shell::display_prompt() {
std::string context = context_text(); std::string context = context_text();
print(prompt_prefix()); print(prompt_prefix());
// colors added by proddy
if (!hostname.empty()) { if (!hostname.empty()) {
print(COLOR_BRIGHT_GREEN); print(COLOR_BRIGHT_GREEN);
print(COLOR_BOLD_ON); print(COLOR_BOLD_ON);
@@ -78,7 +77,6 @@ void Shell::display_prompt() {
print(COLOR_BOLD_ON); print(COLOR_BOLD_ON);
print(context); print(context);
print(COLOR_RESET); print(COLOR_RESET);
// print(' ');
} }
print(prompt_suffix()); print(prompt_suffix());
print(' '); print(' ');

View File

@@ -365,7 +365,7 @@ bool SyslogService::can_transmit() {
#endif #endif
if (!emsesp::EMSESP::system_.network_connected()) { if (!emsesp::EMSESP::system_.network_connected()) {
return false; // added by proddy. Check Ethernet return false;
} }
const uint64_t now = uuid::get_uptime_ms(); const uint64_t now = uuid::get_uptime_ms();

View File

@@ -21,6 +21,10 @@
#include <stdio.h> #include <stdio.h>
#include <stdarg.h> #include <stdarg.h>
#include <iostream>
#include <thread>
#include <atomic>
#include <string> #include <string>
#include <Network.h> #include <Network.h>
@@ -43,18 +47,34 @@ static unsigned long __millis = 0;
static bool __output_pins[256]; static bool __output_pins[256];
static int __output_level[256]; static int __output_level[256];
int main(int argc __attribute__((unused)), char * argv[] __attribute__((unused))) { std::atomic_bool exitProgram(false);
setup();
while (millis() <= 10 * 1000) { void ClientLoop(void * arg) {
(void)arg;
for (;;) {
loop(); loop();
if (exitProgram)
break;
} }
return 0;
} }
unsigned long millis() { int main(int argc __attribute__((unused)), char * argv[] __attribute__((unused))) {
return __millis; setup();
std::thread t = std::thread(ClientLoop, nullptr);
// while (millis() <= 10 * 1000) {
while (1) {
if (exitProgram)
break;
std::this_thread::yield();
}
t.join();
return EXIT_SUCCESS;
} }
// unsigned long millis() {
// return __millis;
// }
int64_t esp_timer_get_time() { int64_t esp_timer_get_time() {
return __millis; return __millis;
} }
@@ -64,6 +84,7 @@ void delay(unsigned long millis) {
} }
void yield(void) { void yield(void) {
std::this_thread::yield();
} }
int snprintf_P(char * str, size_t size, const char * format, ...) { int snprintf_P(char * str, size_t size, const char * format, ...) {

View File

@@ -35,7 +35,7 @@
#include <iostream> #include <iostream>
// #define IPAddress std::string // #define IPAddress std::string
#define IPAddress String // #define IPAddress String
#define ICACHE_FLASH_ATTR #define ICACHE_FLASH_ATTR
#define ICACHE_RAM_ATTR #define ICACHE_RAM_ATTR
@@ -171,7 +171,13 @@ extern NativeConsole Serial;
extern ETHClass ETH; extern ETHClass ETH;
extern WiFiClass WiFi; extern WiFiClass WiFi;
unsigned long millis(); // unsigned long millis();
#if defined(__linux__)
#include <chrono> // NOLINT [build/c++11]
#include <thread> // NOLINT [build/c++11] for yield()
#define millis() std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now().time_since_epoch()).count()
#endif
int64_t esp_timer_get_time(); int64_t esp_timer_get_time();

View File

@@ -101,7 +101,10 @@ class ESP8266React {
: _settings(server, fs, nullptr) : _settings(server, fs, nullptr)
, _securitySettingsService(server, fs){}; , _securitySettingsService(server, fs){};
void begin(){}; void begin() {
// initialize mqtt
_mqttClient = new espMqttClient();
};
void loop(){}; void loop(){};
SecurityManager * getSecurityManager() { SecurityManager * getSecurityManager() {
@@ -112,6 +115,11 @@ class ESP8266React {
return _mqttClient; return _mqttClient;
} }
void setWill(const char * will_topic) {
}
void onMessage(espMqttClientTypes::OnMessageCallback callback) {
}
StatefulService<DummySettings> * getNetworkSettingsService() { StatefulService<DummySettings> * getNetworkSettingsService() {
return &_settings; return &_settings;
} }

View File

@@ -61,11 +61,11 @@ class File : public Stream {
bool seek(uint32_t pos) { bool seek(uint32_t pos) {
return seek(pos, SeekSet); return seek(pos, SeekSet);
} }
size_t position() const; size_t position() const;
size_t size() const; size_t size() const;
bool setBufferSize(size_t size); bool setBufferSize(size_t size);
void close(); void close();
operator bool() const; operator bool() const;
time_t getLastWrite(); time_t getLastWrite();
const char * path() const; const char * path() const;
const char * name() const; const char * name() const;

View File

@@ -3,6 +3,7 @@
#include <Arduino.h> #include <Arduino.h>
#include <functional> #include <functional>
#include <IPAddress.h>
#define WiFiMode_t wifi_mode_t #define WiFiMode_t wifi_mode_t
#define WIFI_OFF WIFI_MODE_NULL #define WIFI_OFF WIFI_MODE_NULL

View File

@@ -91,7 +91,7 @@ class Stream : public Print {
// initial characters that are not digits (or the minus sign) are skipped // initial characters that are not digits (or the minus sign) are skipped
// integer is terminated by the first character that is not a digit. // integer is terminated by the first character that is not a digit.
float parseFloat(); // float version of parseInt float parseFloat(); // float version of parseInt
virtual size_t readBytes(char * buffer, size_t length) // read chars from stream into buffer virtual size_t readBytes(char * buffer, size_t length) // read chars from stream into buffer
{ {

View File

View File

@@ -1,130 +0,0 @@
#ifndef ESPMQTTCLIENT_H_
#define ESPMQTTCLIENT_H_
#include "Arduino.h"
#include <functional>
namespace espMqttClientTypes {
enum class DisconnectReason : uint8_t {
USER_OK = 0,
MQTT_UNACCEPTABLE_PROTOCOL_VERSION = 1,
MQTT_IDENTIFIER_REJECTED = 2,
MQTT_SERVER_UNAVAILABLE = 3,
MQTT_MALFORMED_CREDENTIALS = 4,
MQTT_NOT_AUTHORIZED = 5,
TLS_BAD_FINGERPRINT = 6,
TCP_DISCONNECTED = 7
};
const char * disconnectReasonToString(DisconnectReason reason);
enum class SubscribeReturncode : uint8_t { QOS0 = 0x00, QOS1 = 0x01, QOS2 = 0x02, FAIL = 0X80 };
const char * subscribeReturncodeToString(SubscribeReturncode returnCode);
enum class Error : uint8_t { SUCCESS = 0, OUT_OF_MEMORY = 1, MAX_RETRIES = 2, MALFORMED_PARAMETER = 3, MISC_ERROR = 4 };
const char * errorToString(Error error);
struct MessageProperties {
uint8_t qos;
bool dup;
bool retain;
uint16_t packetId;
};
typedef std::function<void(bool sessionPresent)> OnConnectCallback;
typedef std::function<void(DisconnectReason reason)> OnDisconnectCallback;
typedef std::function<void(uint16_t packetId, const SubscribeReturncode * returncodes, size_t len)> OnSubscribeCallback;
typedef std::function<void(uint16_t packetId)> OnUnsubscribeCallback;
typedef std::function<void(const MessageProperties & properties, const char * topic, const uint8_t * payload, size_t len, size_t index, size_t total)> OnMessageCallback;
typedef std::function<void(uint16_t packetId)> OnPublishCallback;
typedef std::function<size_t(uint8_t * data, size_t maxSize, size_t index)> PayloadCallback;
typedef std::function<void(uint16_t packetId, Error error)> OnErrorCallback;
} // namespace espMqttClientTypes
class espMqttClient {
public:
espMqttClient();
~espMqttClient();
espMqttClient & setKeepAlive(uint16_t keepAlive);
espMqttClient & setClientId(const char * clientId);
espMqttClient & setCleanSession(bool cleanSession);
espMqttClient & setMaxTopicLength(uint16_t maxTopicLength);
espMqttClient & setCredentials(const char * username, const char * password = nullptr);
espMqttClient & setWill(const char * topic, uint8_t qos, bool retain, const char * payload = nullptr, size_t length = 0) {
return *this;
}
espMqttClient & setServer(IPAddress ip, uint16_t port);
espMqttClient & setServer(const char * host, uint16_t port);
espMqttClient & onConnect(espMqttClientTypes::OnConnectCallback callback) {
return *this;
}
espMqttClient & onDisconnect(espMqttClientTypes::OnDisconnectCallback callback) {
return *this;
}
espMqttClient & onSubscribe(espMqttClientTypes::OnSubscribeCallback callback) {
return *this;
}
espMqttClient & onUnsubscribe(espMqttClientTypes::OnUnsubscribeCallback callback) {
return *this;
}
espMqttClient & onMessage(espMqttClientTypes::OnMessageCallback callback) {
return *this;
}
espMqttClient & onPublish(espMqttClientTypes::OnPublishCallback callback) {
return *this;
}
bool connected() const {
return false;
}
void connect() {
}
void disconnect(bool force = false) {
}
uint16_t subscribe(const char * topic, uint8_t qos) {
return 1;
}
uint16_t unsubscribe(const char * topic) {
return 1;
}
uint16_t publish(const char * topic, uint8_t qos, bool retain, const char * payload = nullptr, size_t length = 0, bool dup = false, uint16_t message_id = 0) {
return 1;
}
const char * getClientId() {
return "12";
}
uint16_t getQueue() const {
return 0;
}
private:
bool _connected;
bool _connectPacketNotEnoughSpace;
bool _disconnectOnPoll;
bool _tlsBadFingerprint;
uint32_t _lastClientActivity;
uint32_t _lastServerActivity;
uint32_t _lastPingRequestTime;
char _generatedClientId[18 + 1]; // esp8266-abc123 and esp32-abcdef123456
IPAddress _ip;
const char * _host;
bool _useIp;
uint16_t _port;
uint16_t _keepAlive;
bool _cleanSession;
const char * _clientId;
const char * _username;
const char * _password;
const char * _willTopic;
const char * _willPayload;
uint16_t _willPayloadLength;
uint8_t _willQos;
bool _willRetain;
};
#endif

View File

@@ -13,7 +13,7 @@
"compression": "^1.7.4", "compression": "^1.7.4",
"express": "^4.18.2", "express": "^4.18.2",
"multer": "^1.4.5-lts.1", "multer": "^1.4.5-lts.1",
"nodemon": "^2.0.22" "nodemon": "^3.0.1"
}, },
"packageManager": "yarn@3.4.1" "packageManager": "yarn@3.4.1"
} }

View File

@@ -323,12 +323,8 @@ security_settings = {
] ]
}; };
const features = { const features = {
project: true, version: 'v3.6.0-demo',
security: true, platform: 'ESP32-S3'
mqtt: true,
ntp: true,
ota: true,
upload_firmware: true
}; };
const verify_authentication = { access_token: '1234' }; const verify_authentication = { access_token: '1234' };
const signin = { const signin = {
@@ -2782,5 +2778,5 @@ rest_server.get(ES_LOG_ENDPOINT, function (req, res) {
log_index = 0; log_index = 0;
} }
fetch_log.events.push(data); // append to buffer fetch_log.events.push(data); // append to buffer
}, 3000); }, 300);
}); });

View File

@@ -118,7 +118,7 @@ __metadata:
compression: ^1.7.4 compression: ^1.7.4
express: ^4.18.2 express: ^4.18.2
multer: ^1.4.5-lts.1 multer: ^1.4.5-lts.1
nodemon: ^2.0.22 nodemon: ^3.0.1
languageName: unknown languageName: unknown
linkType: soft linkType: soft
@@ -1203,23 +1203,23 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"nodemon@npm:^2.0.22": "nodemon@npm:^3.0.1":
version: 2.0.22 version: 3.0.1
resolution: "nodemon@npm:2.0.22" resolution: "nodemon@npm:3.0.1"
dependencies: dependencies:
chokidar: ^3.5.2 chokidar: ^3.5.2
debug: ^3.2.7 debug: ^3.2.7
ignore-by-default: ^1.0.1 ignore-by-default: ^1.0.1
minimatch: ^3.1.2 minimatch: ^3.1.2
pstree.remy: ^1.1.8 pstree.remy: ^1.1.8
semver: ^5.7.1 semver: ^7.5.3
simple-update-notifier: ^1.0.7 simple-update-notifier: ^2.0.0
supports-color: ^5.5.0 supports-color: ^5.5.0
touch: ^3.1.0 touch: ^3.1.0
undefsafe: ^2.0.5 undefsafe: ^2.0.5
bin: bin:
nodemon: bin/nodemon.js nodemon: bin/nodemon.js
checksum: 9c987e139748f5b5c480c6c9080bdc97304ee7d29172b7b3da1a7db590b1323ad57b96346304e9b522b0e445c336dc393ccd3f9f45c73b20d476d2347890dcd0 checksum: 6a5d81855760d6617049eccce10ccf02bddb482dab13ceea5280ae595ec7004eee13e7b934368e3f46c37fe4d970342a8c38c99cae7e93e4d7a3ed1c1ecb6acf
languageName: node languageName: node
linkType: hard linkType: hard
@@ -1483,15 +1483,6 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"semver@npm:^5.7.1":
version: 5.7.1
resolution: "semver@npm:5.7.1"
bin:
semver: ./bin/semver
checksum: 57fd0acfd0bac382ee87cd52cd0aaa5af086a7dc8d60379dfe65fea491fb2489b6016400813930ecd61fd0952dae75c115287a1b16c234b1550887117744dfaf
languageName: node
linkType: hard
"semver@npm:^7.3.5": "semver@npm:^7.3.5":
version: 7.3.8 version: 7.3.8
resolution: "semver@npm:7.3.8" resolution: "semver@npm:7.3.8"
@@ -1503,12 +1494,14 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"semver@npm:~7.0.0": "semver@npm:^7.5.3":
version: 7.0.0 version: 7.5.4
resolution: "semver@npm:7.0.0" resolution: "semver@npm:7.5.4"
dependencies:
lru-cache: ^6.0.0
bin: bin:
semver: bin/semver.js semver: bin/semver.js
checksum: 272c11bf8d083274ef79fe40a81c55c184dff84dd58e3c325299d0927ba48cece1f020793d138382b85f89bab5002a35a5ba59a3a68a7eebbb597eb733838778 checksum: 12d8ad952fa353b0995bf180cdac205a4068b759a140e5d3c608317098b3575ac2f1e09182206bf2eb26120e1c0ed8fb92c48c592f6099680de56bb071423ca3
languageName: node languageName: node
linkType: hard linkType: hard
@@ -1577,12 +1570,12 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"simple-update-notifier@npm:^1.0.7": "simple-update-notifier@npm:^2.0.0":
version: 1.1.0 version: 2.0.0
resolution: "simple-update-notifier@npm:1.1.0" resolution: "simple-update-notifier@npm:2.0.0"
dependencies: dependencies:
semver: ~7.0.0 semver: ^7.5.3
checksum: 1012e9b6c504e559a948078177b3eedbb9d7e4d15878e2bda56314d08db609ca5da485be4ac9f838759faae8057935ee0246fcdf63f1233c86bd9fecb2a5544b checksum: 9ba00d38ce6a29682f64a46213834e4eb01634c2f52c813a9a7b8873ca49cdbb703696f3290f3b27dc067de6d9418b0b84bef22c3eb074acf352529b2d6c27fd
languageName: node languageName: node
linkType: hard linkType: hard

View File

@@ -13,10 +13,16 @@
; -DEMSESP_EN_ONLY ; only EN translated entity names ; -DEMSESP_EN_ONLY ; only EN translated entity names
; my_build_flags = -DEMSESP_DEBUG ; my_build_flags = -DEMSESP_DEBUG
[platformio]
default_envs = esp32_4M
; default_envs = esp32_16M
; default_envs = lolin_s3
; default_envs = standalone
[env:esp32_4M] [env:esp32_4M]
; if using OTA enter your details below ; if using OTA enter your details below
; upload_protocol = espota ; upload_protocol = espota
; upload_flags = ; upload_flags =
; --port=8266 ; --port=8266
; --auth=ems-esp-neo ; --auth=ems-esp-neo
; upload_port = ems-esp.local ; upload_port = ems-esp.local
@@ -25,19 +31,20 @@ upload_port = /dev/ttyUSB*
; upload_port = COM5 ; upload_port = COM5
; override arduino espressif core ; override arduino espressif core
; platform = espressif32 ; take latest platform = espressif32 ; take latest
; platform = espressif32@5.2.0 ; platform = espressif32@5.3.0
extra_scripts = extra_scripts =
pre:scripts/build_interface.py ; comment out if you don't want to re-build the WebUI each time pre:scripts/build_interface.py ; comment out if you don't want to re-build the WebUI each time
scripts/rename_fw.py scripts/rename_fw.py
; post:scripts/app-tls-size.py
[env:esp32_16M] [env:esp32_16M]
upload_port = /dev/ttyUSB*
; upload_port = COM3
[env:lolin_s3]
upload_port = /dev/ttyACM0
extra_scripts = extra_scripts =
; pre:scripts/build_interface.py ; comment out if you don't want to re-build the WebUI each time pre:scripts/build_interface.py ; comment out if you don't want to re-build the WebUI each time
scripts/rename_fw.py scripts/rename_fw.py
; pio run -e debug ; pio run -e debug

View File

@@ -20,7 +20,6 @@ core_build_flags =
; -std=gnu++17 ; -std=gnu++17
; core_unbuild_flags = -std=gnu++11 ; core_unbuild_flags = -std=gnu++11
; core_unbuild_flags = -std=gnu++17
core_unbuild_flags = core_unbuild_flags =
; my_build_flags is set in pio_local.ini ; my_build_flags is set in pio_local.ini
@@ -41,9 +40,12 @@ unbuild_flags =
[espressi32_base] [espressi32_base]
platform = espressif32 platform = espressif32
; platform = espressif32@5.3.0
; platform = espressif32@5.2.0
framework = arduino framework = arduino
build_flags = ${common.build_flags}
build_unflags = ${common.unbuild_flags}
extra_scripts =
pre:scripts/build_interface.py
scripts/rename_fw.py
[env] [env]
monitor_speed = 115200 monitor_speed = 115200
@@ -62,7 +64,7 @@ check_flags =
; build for GitHub Actions CI ; build for GitHub Actions CI
; the Web interface is built seperately ; the Web interface is built seperately
[env:ci] [env:ci]
platform = espressif32@5.2.0 platform = espressif32
framework = arduino framework = arduino
extra_scripts = scripts/rename_fw.py extra_scripts = scripts/rename_fw.py
board = esp32dev board = esp32dev
@@ -84,100 +86,71 @@ build_flags = ${common.build_flags} -O2
build_unflags = ${common.unbuild_flags} build_unflags = ${common.unbuild_flags}
[env:esp32_4M] [env:esp32_4M]
platform = espressif32@5.2.0 extends = espressi32_base
framework = arduino
extra_scripts =
pre:scripts/build_interface.py
scripts/rename_fw.py
board = esp32dev board = esp32dev
board_upload.flash_size = 4MB board_upload.flash_size = 4MB
board_build.partitions = esp32_partition_4M.csv board_build.partitions = esp32_partition_4M.csv
build_flags = ${common.build_flags} -Os build_flags = ${common.build_flags} -Os
build_unflags = ${common.unbuild_flags}
[env:esp32_4Mplus] [env:esp32_4Mplus]
extends = espressi32_base extends = espressi32_base
extra_scripts =
pre:scripts/build_interface.py
scripts/rename_fw.py
board = esp32dev board = esp32dev
board_upload.flash_size = 4MB board_upload.flash_size = 4MB
board_build.partitions = esp32_asym_partition_4M.csv board_build.partitions = esp32_asym_partition_4M.csv
build_flags = ${common.build_flags} build_flags = ${common.build_flags}
build_unflags = ${common.unbuild_flags}
[env:esp32_16M] [env:esp32_16M]
extends = espressi32_base extends = espressi32_base
extra_scripts =
pre:scripts/build_interface.py
scripts/rename_fw.py
board = esp32dev board = esp32dev
board_upload.flash_size = 16MB board_upload.flash_size = 16MB
board_build.partitions = esp32_partition_16M.csv board_build.partitions = esp32_partition_16M.csv
build_flags = ${common.build_flags} build_flags = ${common.build_flags}
build_unflags = ${common.unbuild_flags}
[env:lolin_c3_mini] [env:lolin_c3_mini]
extends = espressi32_base extends = espressi32_base
extra_scripts =
pre:scripts/build_interface.py
scripts/rename_fw.py
board = lolin_c3_mini board = lolin_c3_mini
board_upload.flash_size = 4MB board_upload.flash_size = 4MB
board_build.partitions = esp32_asym_partition_4M.csv board_build.partitions = esp32_asym_partition_4M.csv
build_flags = ${common.build_flags} build_flags = ${common.build_flags}
build_unflags = ${common.unbuild_flags}
; lolin C3 mini v1 needs special wifi init. ; lolin C3 mini v1 needs special wifi init.
; https://www.wemos.cc/en/latest/c3/c3_mini_1_0_0.html#about-wifi ; https://www.wemos.cc/en/latest/c3/c3_mini_1_0_0.html#about-wifi
[env:lolin_c3_mini_v1] [env:lolin_c3_mini_v1]
extends = espressi32_base extends = espressi32_base
extra_scripts =
pre:scripts/build_interface.py
scripts/rename_fw.py
board = lolin_c3_mini board = lolin_c3_mini
board_upload.flash_size = 4MB board_upload.flash_size = 4MB
board_build.partitions = esp32_asym_partition_4M.csv board_build.partitions = esp32_asym_partition_4M.csv
build_flags = ${common.build_flags} -DBOARD_C3_MINI_V1 build_flags = ${common.build_flags} -DBOARD_C3_MINI_V1
build_unflags = ${common.unbuild_flags}
[env:lolin_s2_mini] [env:lolin_s2_mini]
extends = espressi32_base extends = espressi32_base
extra_scripts =
pre:scripts/build_interface.py
scripts/rename_fw.py
board = lolin_s2_mini board = lolin_s2_mini
board_upload.flash_size = 4MB board_upload.flash_size = 4MB
board_build.partitions = esp32_asym_partition_4M.csv board_build.partitions = esp32_asym_partition_4M.csv
build_flags = ${common.build_flags} build_flags = ${common.build_flags}
build_unflags = ${common.unbuild_flags}
[env:lolin_s3] [env:lolin_s3]
extends = espressi32_base extends = espressi32_base
extra_scripts =
pre:scripts/build_interface.py
scripts/rename_fw.py
board = lolin_s3 board = lolin_s3
board_build.f_cpu = 240000000L board_build.f_cpu = 240000000L
board_upload.flash_size = 16MB board_upload.flash_size = 16MB
board_build.partitions = esp32_partition_16M.csv board_build.partitions = esp32_partition_16M.csv
build_flags = ${common.build_flags} -O2
build_unflags = ${common.unbuild_flags}
board_upload.use_1200bps_touch = false board_upload.use_1200bps_touch = false
board_upload.wait_for_upload_port = false board_upload.wait_for_upload_port = false
build_flags = ${common.build_flags} -O2
; to build and run: pio run -e standalone -t exec ; to build and run: pio run -e standalone -t exec
[env:standalone] [env:standalone]
platform = native platform = native
build_flags = build_flags =
-DARDUINO -DARDUINOJSON_ENABLE_STD_STRING=1 -DARDUINOJSON_ENABLE_PROGMEM=1 -DARDUINOJSON_ENABLE_ARDUINO_STRING -DARDUINOJSON_USE_DOUBLE=0
-DARDUINOJSON_ENABLE_STD_STRING=1 -DARDUINOJSON_ENABLE_PROGMEM=1 -DARDUINOJSON_ENABLE_ARDUINO_STRING -DARDUINOJSON_USE_DOUBLE=0 -DEMSESP_DEBUG -DEMSESP_STANDALONE -DEMSESP_TEST -D__linux__
-DEMSESP_DEBUG -DEMSESP_STANDALONE -DEMSESP_TEST
-DEMSESP_DEFAULT_LOCALE=\"en\" -DEMSESP_DEFAULT_TX_MODE=8 -DEMSESP_DEFAULT_VERSION=\"3.6.0-dev\" -DEMSESP_DEFAULT_BOARD_PROFILE=\"S32\" -DEMSESP_DEFAULT_LOCALE=\"en\" -DEMSESP_DEFAULT_TX_MODE=8 -DEMSESP_DEFAULT_VERSION=\"3.6.0-dev\" -DEMSESP_DEFAULT_BOARD_PROFILE=\"S32\"
-lpthread -lpthread
-std=gnu++11 -Og -ggdb -std=gnu++11 -Og -ggdb
build_src_flags = build_src_flags =
-Wall -Wextra -Werror -Wswitch-enum -Wno-unused-parameter -Wno-inconsistent-missing-override -Wno-unused-lambda-capture -Wall -Wextra -Werror -Wswitch-enum -Wno-unused-parameter -Wno-inconsistent-missing-override -Wno-unused-lambda-capture
-Wno-missing-braces
-I./lib_standalone -I./lib_standalone
-I./lib/ArduinoJson/src -I./lib/ArduinoJson/src
-I./lib/uuid-common/src -I./lib/uuid-common/src
@@ -185,6 +158,8 @@ build_src_flags =
-I./lib/uuid-log/src -I./lib/uuid-log/src
-I./lib/semver -I./lib/semver
-I./lib/PButton -I./lib/PButton
-I./lib/espMqttClient/src
-I./lib/espMqttClient/src/Transport
build_src_filter = build_src_filter =
+<*> +<*>
-<.git/> -<.git/>
@@ -194,5 +169,7 @@ build_src_filter =
+<../lib/uuid-log> +<../lib/uuid-log>
+<../lib/semver> +<../lib/semver>
+<../lib/PButton> +<../lib/PButton>
+<../lib/espMqttClient/src>
+<../lib/espMqttClient/src/Transport>
lib_compat_mode = off lib_compat_mode = off
lib_ldf_mode = off lib_ldf_mode = off

View File

@@ -498,7 +498,7 @@ void AnalogSensor::publish_values(const bool force) {
config["val_tpl"] = (std::string) "{{" + val_obj + " if " + val_cond + " else " + sample_val + "}}"; config["val_tpl"] = (std::string) "{{" + val_obj + " if " + val_cond + " else " + sample_val + "}}";
char uniq_s[70]; char uniq_s[70];
if (Mqtt::entity_format() == Mqtt::entitiyFormat::MULTI_SHORT) { if (Mqtt::entity_format() == Mqtt::entityFormat::MULTI_SHORT) {
snprintf(uniq_s, sizeof(uniq_s), "%s_analogsensor_%02d", Mqtt::basename().c_str(), sensor.gpio()); snprintf(uniq_s, sizeof(uniq_s), "%s_analogsensor_%02d", Mqtt::basename().c_str(), sensor.gpio());
} else { } else {
snprintf(uniq_s, sizeof(uniq_s), "analogsensor_%02d", sensor.gpio()); snprintf(uniq_s, sizeof(uniq_s), "analogsensor_%02d", sensor.gpio());
@@ -563,6 +563,17 @@ void AnalogSensor::publish_values(const bool force) {
// config["step"] = sensor.factor(); // config["step"] = sensor.factor();
} else if (sensor.type() == AnalogType::DIGITAL_IN) { } else if (sensor.type() == AnalogType::DIGITAL_IN) {
snprintf(topic, sizeof(topic), "binary_sensor/%s/analogsensor_%02d/config", Mqtt::basename().c_str(), sensor.gpio()); snprintf(topic, sizeof(topic), "binary_sensor/%s/analogsensor_%02d/config", Mqtt::basename().c_str(), sensor.gpio());
if (EMSESP::system_.bool_format() == BOOL_FORMAT_TRUEFALSE) {
config["pl_on"] = true;
config["pl_off"] = false;
} else if (EMSESP::system_.bool_format() == BOOL_FORMAT_10) {
config["pl_on"] = 1;
config["pl_off"] = 0;
} else {
char result[12];
config["pl_on"] = Helpers::render_boolean(result, true);
config["pl_off"] = Helpers::render_boolean(result, false);
}
} else { } else {
snprintf(topic, sizeof(topic), "sensor/%s/analogsensor_%02d/config", Mqtt::basename().c_str(), sensor.gpio()); snprintf(topic, sizeof(topic), "sensor/%s/analogsensor_%02d/config", Mqtt::basename().c_str(), sensor.gpio());
config["stat_cla"] = "measurement"; config["stat_cla"] = "measurement";
@@ -575,9 +586,7 @@ void AnalogSensor::publish_values(const bool force) {
// add "availability" section // add "availability" section
Mqtt::add_avty_to_doc(stat_t, config.as<JsonObject>(), val_cond); Mqtt::add_avty_to_doc(stat_t, config.as<JsonObject>(), val_cond);
Mqtt::queue_ha(topic, config.as<JsonObject>()); sensor.ha_registered = Mqtt::queue_ha(topic, config.as<JsonObject>());
sensor.ha_registered = true;
} }
} }
} }

View File

@@ -410,14 +410,16 @@ static void setup_commands(std::shared_ptr<Commands> & commands) {
if (arguments.size() == 4) { if (arguments.size() == 4) {
uint16_t offset = Helpers::hextoint(arguments[2].c_str()); uint16_t offset = Helpers::hextoint(arguments[2].c_str());
uint8_t length = Helpers::hextoint(arguments.back().c_str()); uint8_t length = Helpers::hextoint(arguments.back().c_str());
to_app(shell).send_read_request(type_id, device_id, offset, length); to_app(shell).send_read_request(type_id, device_id, offset, length, true);
} else if (arguments.size() == 3) { } else if (arguments.size() == 3) {
uint16_t offset = Helpers::hextoint(arguments.back().c_str()); uint16_t offset = Helpers::hextoint(arguments.back().c_str());
to_app(shell).send_read_request(type_id, device_id, offset, EMS_MAX_TELEGRAM_LENGTH); to_app(shell).send_read_request(type_id, device_id, offset, EMS_MAX_TELEGRAM_LENGTH, true);
} else { } else {
// send with length to send immediately and trigger publish read_id // send with length to send immediately and trigger publish read_id
to_app(shell).send_read_request(type_id, device_id, 0, EMS_MAX_TELEGRAM_LENGTH); to_app(shell).send_read_request(type_id, device_id, 0, EMS_MAX_TELEGRAM_LENGTH, true);
} }
to_app(shell).set_read_id(type_id);
}); });
commands->add_command(ShellContext::MAIN, commands->add_command(ShellContext::MAIN,
@@ -620,12 +622,12 @@ void EMSESPShell::stopped() {
// show welcome banner // show welcome banner
void EMSESPShell::display_banner() { void EMSESPShell::display_banner() {
println(); println();
printfln("┌───────────────────────────────────────┐"); printfln("┌───────────────────────────────────────");
printfln("│ %sEMS-ESP version %-12s%s │", COLOR_BOLD_ON, EMSESP_APP_VERSION, COLOR_BOLD_OFF); printfln("│ %sEMS-ESP version %-12s%s │", COLOR_BOLD_ON, EMSESP_APP_VERSION, COLOR_BOLD_OFF);
printfln("│ %s%shttps://github.com/emsesp/EMS-ESP32%s │", COLOR_BRIGHT_GREEN, COLOR_UNDERLINE, COLOR_RESET); printfln("│ %s%shttps://github.com/emsesp/EMS-ESP32%s ", COLOR_BRIGHT_GREEN, COLOR_UNDERLINE, COLOR_RESET);
printfln("│ │"); printfln(" ");
printfln("│ type %shelp%s to show available commands │", COLOR_UNDERLINE, COLOR_RESET); printfln("│ type %shelp%s to show available commands ", COLOR_UNDERLINE, COLOR_RESET);
printfln("└───────────────────────────────────────┘"); printfln("└───────────────────────────────────────");
println(); println();
// set console name // set console name
@@ -640,16 +642,16 @@ std::string EMSESPShell::hostname_text() {
} }
std::string EMSESPShell::context_text() { std::string EMSESPShell::context_text() {
auto shell_context = static_cast<ShellContext>(context()); return std::string{};
if (shell_context == ShellContext::MAIN) { // auto shell_context = static_cast<ShellContext>(context());
return std::string{}; // if (shell_context == ShellContext::MAIN) {
// return std::string{'/'}; // return std::string{'/'};
// } else if (shell_context == ShellContext::FILESYSTEM) { // } else if (shell_context == ShellContext::FILESYSTEM) {
// return "/fs"); // return "/fs");
} else { // } else {
return std::string{}; // return std::string{};
} // }
} }
// when in su (admin) show # as the prompt suffix // when in su (admin) show # as the prompt suffix

View File

@@ -590,7 +590,7 @@ Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const
FL_(tempDiffHeat), FL_(tempDiffHeat),
DeviceValueUOM::K, DeviceValueUOM::K,
MAKE_CF_CB(set_tempDiffHeat), MAKE_CF_CB(set_tempDiffHeat),
3, 2,
10); 10);
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, register_device_value(DeviceValueTAG::TAG_DEVICE_DATA,
&tempDiffCool_, &tempDiffCool_,
@@ -599,7 +599,7 @@ Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const
FL_(tempDiffCool), FL_(tempDiffCool),
DeviceValueUOM::K, DeviceValueUOM::K,
MAKE_CF_CB(set_tempDiffCool), MAKE_CF_CB(set_tempDiffCool),
3, 2,
10); 10);
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &vp_cooling_, DeviceValueType::BOOL, FL_(vp_cooling), DeviceValueUOM::NONE, MAKE_CF_CB(set_vp_cooling)); register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &vp_cooling_, DeviceValueType::BOOL, FL_(vp_cooling), DeviceValueUOM::NONE, MAKE_CF_CB(set_vp_cooling));
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &heatCable_, DeviceValueType::BOOL, FL_(heatCable), DeviceValueUOM::NONE, MAKE_CF_CB(set_heatCable)); register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &heatCable_, DeviceValueType::BOOL, FL_(heatCable), DeviceValueUOM::NONE, MAKE_CF_CB(set_heatCable));

View File

@@ -976,6 +976,7 @@ void Thermostat::process_RC300Monitor(std::shared_ptr<const Telegram> telegram)
} }
has_update(telegram, hc->targetflowtemp, 4); has_update(telegram, hc->targetflowtemp, 4);
has_update(telegram, hc->curroominfl, 27); has_update(telegram, hc->curroominfl, 27);
has_update(telegram, hc->coolingon, 32);
add_ha_climate(hc); add_ha_climate(hc);
} }
@@ -1139,8 +1140,8 @@ void Thermostat::process_RC300Settings(std::shared_ptr<const Telegram> telegram)
// 0x2CC - e.g. wwprio for RC310 hcx parameter // 0x2CC - e.g. wwprio for RC310 hcx parameter
void Thermostat::process_RC300Set2(std::shared_ptr<const Telegram> telegram) { void Thermostat::process_RC300Set2(std::shared_ptr<const Telegram> telegram) {
// typeids are not in a raw. hc:0x2CC, hc2: 0x2CE for RC310 // typeids are not in a raw. hc:0x2CC, hc2: 0x2CE for RC310
// telegram is either offset 3 with data lenght of 1 and values 0/1 (radiators) - 10 0B FF 03 01 CC 01 F6 // telegram is either offset 3 with data length of 1 and values 0/1 (radiators) - 10 0B FF 03 01 CC 01 F6
// or offset 0 with data lenght of 6 bytes - offset 3 values are 0x00 or 0xFF - 10 0B FF 00 01 CE FF 13 0A FF 1E 00 20 // or offset 0 with data length of 6 bytes - offset 3 values are 0x00 or 0xFF - 10 0B FF 00 01 CE FF 13 0A FF 1E 00 20
std::shared_ptr<Thermostat::HeatingCircuit> hc = heating_circuit(telegram); std::shared_ptr<Thermostat::HeatingCircuit> hc = heating_circuit(telegram);
if (hc == nullptr) { if (hc == nullptr) {
@@ -1161,7 +1162,7 @@ void Thermostat::process_HPMode(std::shared_ptr<const Telegram> telegram) {
if (hc == nullptr) { if (hc == nullptr) {
return; return;
} }
has_update(telegram, hc->hpmode, 0); has_update(telegram, hc->hpmode, 5);
} }
// 0x467 ff HP settings // 0x467 ff HP settings
@@ -1170,9 +1171,9 @@ void Thermostat::process_HPSet(std::shared_ptr<const Telegram> telegram) {
if (hc == nullptr) { if (hc == nullptr) {
return; return;
} }
has_update(telegram, hc->dewoffset, 0); // 7-35°C has_update(telegram, hc->dewoffset, 4); // 7-35°C
has_update(telegram, hc->roomtempdiff, 3); // 1-10K has_update(telegram, hc->roomtempdiff, 3); // 1-10K
has_update(telegram, hc->hpminflowtemp, 4); // 2-10K has_update(telegram, hc->hpminflowtemp, 0); // 2-10K
} }
// type 0x41 - data from the RC30 thermostat(0x10) - 14 bytes long // type 0x41 - data from the RC30 thermostat(0x10) - 14 bytes long
@@ -1546,7 +1547,7 @@ bool Thermostat::set_hpmode(const char * value, const int8_t id) {
if (!Helpers::value2enum(value, v, FL_(enum_hpmode))) { if (!Helpers::value2enum(value, v, FL_(enum_hpmode))) {
return false; return false;
} }
write_command(hpmode_typeids[hc->hc()], 0, v, hpmode_typeids[hc->hc()]); write_command(hpmode_typeids[hc->hc()], 5, v, hpmode_typeids[hc->hc()]);
return true; return true;
} }
@@ -2727,7 +2728,7 @@ bool Thermostat::set_controlmode(const char * value, const int8_t id) {
return false; return false;
} }
// sets the thermostat time for nightmode for RC10, telegrm 0xB0 // sets the thermostat time for nightmode for RC10, telegram 0xB0
bool Thermostat::set_reducehours(const char * value, const int8_t id) { bool Thermostat::set_reducehours(const char * value, const int8_t id) {
uint8_t hc_num = (id == -1) ? AUTO_HEATING_CIRCUIT : id; uint8_t hc_num = (id == -1) ? AUTO_HEATING_CIRCUIT : id;
std::shared_ptr<Thermostat::HeatingCircuit> hc = heating_circuit(hc_num); std::shared_ptr<Thermostat::HeatingCircuit> hc = heating_circuit(hc_num);
@@ -4194,9 +4195,10 @@ void Thermostat::register_device_values_hc(std::shared_ptr<Thermostat::HeatingCi
register_device_value(tag, &hc->reducetemp, DeviceValueType::INT, FL_(reducetemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_reducetemp)); register_device_value(tag, &hc->reducetemp, DeviceValueType::INT, FL_(reducetemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_reducetemp));
register_device_value(tag, &hc->wwprio, DeviceValueType::BOOL, FL_(wwprio), DeviceValueUOM::NONE, MAKE_CF_CB(set_wwprio)); register_device_value(tag, &hc->wwprio, DeviceValueType::BOOL, FL_(wwprio), DeviceValueUOM::NONE, MAKE_CF_CB(set_wwprio));
register_device_value(tag, &hc->cooling, DeviceValueType::BOOL, FL_(hpcooling), DeviceValueUOM::NONE, MAKE_CF_CB(set_cooling)); register_device_value(tag, &hc->cooling, DeviceValueType::BOOL, FL_(hpcooling), DeviceValueUOM::NONE, MAKE_CF_CB(set_cooling));
register_device_value(tag, &hc->coolingon, DeviceValueType::BOOL, FL_(coolingOn), DeviceValueUOM::NONE);
register_device_value(tag, &hc->hpmode, DeviceValueType::ENUM, FL_(enum_hpmode), FL_(hpmode), DeviceValueUOM::NONE, MAKE_CF_CB(set_hpmode)); register_device_value(tag, &hc->hpmode, DeviceValueType::ENUM, FL_(enum_hpmode), FL_(hpmode), DeviceValueUOM::NONE, MAKE_CF_CB(set_hpmode));
register_device_value(tag, &hc->dewoffset, DeviceValueType::UINT, FL_(dewoffset), DeviceValueUOM::K, MAKE_CF_CB(set_dewoffset)); register_device_value(tag, &hc->dewoffset, DeviceValueType::UINT, FL_(dewoffset), DeviceValueUOM::K, MAKE_CF_CB(set_dewoffset), 2, 10);
register_device_value(tag, &hc->roomtempdiff, DeviceValueType::UINT, FL_(roomtempdiff), DeviceValueUOM::K, MAKE_CF_CB(set_roomtempdiff)); register_device_value(tag, &hc->roomtempdiff, DeviceValueType::UINT, FL_(roomtempdiff), DeviceValueUOM::K, MAKE_CF_CB(set_roomtempdiff));
register_device_value(tag, &hc->hpminflowtemp, DeviceValueType::UINT, FL_(hpminflowtemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_hpminflowtemp)); register_device_value(tag, &hc->hpminflowtemp, DeviceValueType::UINT, FL_(hpminflowtemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_hpminflowtemp));

View File

@@ -95,6 +95,7 @@ class Thermostat : public EMSdevice {
uint8_t hpminflowtemp; uint8_t hpminflowtemp;
uint8_t hpmode; uint8_t hpmode;
uint8_t cooling; uint8_t cooling;
uint8_t coolingon;
uint8_t hc_num() const { uint8_t hc_num() const {
return hc_num_; return hc_num_;

View File

@@ -1505,6 +1505,10 @@ bool EMSdevice::get_value_info(JsonObject & output, const char * cmd, const int8
output["api_data"] = data; output["api_data"] = data;
return true; return true;
} else { } else {
char error[100];
snprintf(error, sizeof(error), "cannot find attribute %s in entity %s", attribute_s, command_s);
output.clear();
output["message"] = error;
return false; return false;
} }
} }
@@ -1513,6 +1517,9 @@ bool EMSdevice::get_value_info(JsonObject & output, const char * cmd, const int8
} }
} }
char error[100];
snprintf(error, sizeof(error), "cannot find values for entity '%s'", cmd);
json["message"] = error;
return false; return false;
} }
@@ -1645,7 +1652,7 @@ bool EMSdevice::generate_values(JsonObject & output, const uint8_t tag_filter, c
Helpers::translated_word(FL_(minutes))); Helpers::translated_word(FL_(minutes)));
json[name] = time_s; json[name] = time_s;
} else { } else {
json[name] = serialized(Helpers::render_value(val, time_value, 1)); json[name] = serialized(Helpers::render_value(val, time_value, 0));
} }
} }

View File

@@ -76,6 +76,7 @@ uint8_t EMSESP::watch_ = 0; // trace off
uint16_t EMSESP::read_id_ = WATCH_ID_NONE; uint16_t EMSESP::read_id_ = WATCH_ID_NONE;
bool EMSESP::read_next_ = false; bool EMSESP::read_next_ = false;
uint16_t EMSESP::publish_id_ = 0; uint16_t EMSESP::publish_id_ = 0;
uint16_t EMSESP::response_id_ = 0;
bool EMSESP::tap_water_active_ = false; // for when Boiler states we having running warm water. used in Shower() bool EMSESP::tap_water_active_ = false; // for when Boiler states we having running warm water. used in Shower()
uint32_t EMSESP::last_fetch_ = 0; uint32_t EMSESP::last_fetch_ = 0;
uint8_t EMSESP::publish_all_idx_ = 0; uint8_t EMSESP::publish_all_idx_ = 0;
@@ -624,25 +625,36 @@ void EMSESP::publish_sensor_values(const bool time, const bool force) {
// MQTT publish a telegram as raw data to the topic 'response' // MQTT publish a telegram as raw data to the topic 'response'
void EMSESP::publish_response(std::shared_ptr<const Telegram> telegram) { void EMSESP::publish_response(std::shared_ptr<const Telegram> telegram) {
StaticJsonDocument<EMSESP_JSON_SIZE_SMALL> doc; static char * buffer = nullptr;
static uint8_t offset;
if (buffer == nullptr) {
offset = telegram->offset; // store offset from first part
buffer = new char[768]; // max 256 hex-codes, 255 spaces, 1 termination
buffer[0] = '\0';
}
strlcat(buffer, Helpers::data_to_hex(telegram->message_data, telegram->message_length).c_str(), 768);
if (response_id_ != 0) {
strlcat(buffer, " ", 768);
return;
}
DynamicJsonDocument doc(EMSESP_JSON_SIZE_LARGE);
char s[10];
doc["src"] = Helpers::hextoa(s, telegram->src);
doc["dest"] = Helpers::hextoa(s, telegram->dest);
doc["type"] = Helpers::hextoa(s, telegram->type_id);
doc["offset"] = Helpers::hextoa(s, offset);
doc["data"] = buffer;
char buffer[100]; if (telegram->message_length <= 4 && strlen(buffer) <= 11) {
doc["src"] = Helpers::hextoa(buffer, telegram->src);
doc["dest"] = Helpers::hextoa(buffer, telegram->dest);
doc["type"] = Helpers::hextoa(buffer, telegram->type_id);
doc["offset"] = Helpers::hextoa(buffer, telegram->offset);
strlcpy(buffer, Helpers::data_to_hex(telegram->message_data, telegram->message_length).c_str(), sizeof(buffer)); // telegram is without crc
doc["data"] = buffer;
if (telegram->message_length <= 4) {
uint32_t value = 0; uint32_t value = 0;
for (uint8_t i = 0; i < telegram->message_length; i++) { for (uint8_t i = 0; i < telegram->message_length; i++) {
value = (value << 8) + telegram->message_data[i]; value = (value << 8) + telegram->message_data[i];
} }
doc["value"] = value; doc["value"] = value;
} }
Mqtt::queue_publish("response", doc.as<JsonObject>()); Mqtt::queue_publish("response", doc.as<JsonObject>());
delete[] buffer;
buffer = nullptr;
} }
// builds json with the detail of each value, for a specific EMS device type or the temperature sensor // builds json with the detail of each value, for a specific EMS device type or the temperature sensor
@@ -847,12 +859,19 @@ void EMSESP::process_version(std::shared_ptr<const Telegram> telegram) {
// returns false if there are none found // returns false if there are none found
bool EMSESP::process_telegram(std::shared_ptr<const Telegram> telegram) { bool EMSESP::process_telegram(std::shared_ptr<const Telegram> telegram) {
// if watching or reading... // if watching or reading...
if ((telegram->type_id == read_id_) && (telegram->dest == txservice_.ems_bus_id())) { if ((telegram->type_id == read_id_ || telegram->type_id == response_id_) && (telegram->dest == txservice_.ems_bus_id())) {
LOG_INFO("%s", pretty_telegram(telegram).c_str()); if (telegram->type_id == response_id_) {
if (Mqtt::send_response()) { if (!trace_raw_) {
LOG_TRACE("%s", pretty_telegram(telegram).c_str());
}
if (!read_next_) {
response_id_ = 0;
}
publish_response(telegram); publish_response(telegram);
} else {
LOG_NOTICE("%s", pretty_telegram(telegram).c_str());
} }
// check if read is finished or gives more parts
if (!read_next_) { if (!read_next_) {
read_id_ = WATCH_ID_NONE; read_id_ = WATCH_ID_NONE;
} }
@@ -951,7 +970,7 @@ bool EMSESP::device_exists(const uint8_t device_id) {
// for each associated EMS device go and get its system information // for each associated EMS device go and get its system information
void EMSESP::show_devices(uuid::console::Shell & shell) { void EMSESP::show_devices(uuid::console::Shell & shell) {
if (emsdevices.empty()) { if (emsdevices.empty()) {
shell.printfln("No EMS devices detected. Try using 'scan devices' from the ems menu."); shell.printfln("No EMS devices detected");
shell.println(); shell.println();
return; return;
} }
@@ -1233,8 +1252,8 @@ bool EMSESP::command_info(uint8_t device_type, JsonObject & output, const int8_t
} }
// send a read request, passing it into to the Tx Service, with optional offset and length // send a read request, passing it into to the Tx Service, with optional offset and length
void EMSESP::send_read_request(const uint16_t type_id, const uint8_t dest, const uint8_t offset, const uint8_t length) { void EMSESP::send_read_request(const uint16_t type_id, const uint8_t dest, const uint8_t offset, const uint8_t length, const bool front) {
txservice_.read_request(type_id, dest, offset, length); txservice_.read_request(type_id, dest, offset, length, front);
} }
// sends write request // sends write request
@@ -1308,13 +1327,16 @@ void EMSESP::incoming_telegram(uint8_t * data, const uint8_t length) {
if (txservice_.is_last_tx(src, dest)) { if (txservice_.is_last_tx(src, dest)) {
LOG_DEBUG("Last Tx read successful"); LOG_DEBUG("Last Tx read successful");
txservice_.increment_telegram_read_count(); txservice_.increment_telegram_read_count();
txservice_.send_poll(); // close the bus
txservice_.reset_retry_count(); txservice_.reset_retry_count();
tx_successful = true; tx_successful = true;
// if telegram is longer read next part with offset +25 for ems+ or +27 for ems1.0 // if telegram is longer read next part with offset +25 for ems+ or +27 for ems1.0
if ((length >= 31) && (txservice_.read_next_tx(data[3], length) == read_id_)) { // not for response to raw send commands without read_id set
if ((response_id_ == 0 || read_id_ > 0) && (length >= 31) && (txservice_.read_next_tx(data[3], length) == read_id_)) {
read_next_ = true; read_next_ = true;
txservice_.send();
} else {
txservice_.send_poll(); // close the bus
} }
} }
} }

View File

@@ -117,7 +117,7 @@ class EMSESP {
static bool process_telegram(std::shared_ptr<const Telegram> telegram); static bool process_telegram(std::shared_ptr<const Telegram> telegram);
static std::string pretty_telegram(std::shared_ptr<const Telegram> telegram); static std::string pretty_telegram(std::shared_ptr<const Telegram> telegram);
static void send_read_request(const uint16_t type_id, const uint8_t dest, const uint8_t offset = 0, const uint8_t length = 0); static void send_read_request(const uint16_t type_id, const uint8_t dest, const uint8_t offset = 0, const uint8_t length = 0, const bool front = false);
static void send_write_request(const uint16_t type_id, static void send_write_request(const uint16_t type_id,
const uint8_t dest, const uint8_t dest,
const uint8_t offset, const uint8_t offset,
@@ -176,6 +176,10 @@ class EMSESP {
read_id_ = id; read_id_ = id;
} }
static void set_response_id(uint16_t id) {
response_id_ = id;
}
static bool wait_validate() { static bool wait_validate() {
return (wait_validate_ != 0); return (wait_validate_ != 0);
} }
@@ -259,6 +263,7 @@ class EMSESP {
static uint16_t read_id_; static uint16_t read_id_;
static bool read_next_; static bool read_next_;
static uint16_t publish_id_; static uint16_t publish_id_;
static uint16_t response_id_;
static bool tap_water_active_; static bool tap_water_active_;
static uint8_t publish_all_idx_; static uint8_t publish_all_idx_;
static uint8_t unique_id_count_; static uint8_t unique_id_count_;

Some files were not shown because too many files have changed in this diff Show More