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.workingDirectories": ["interface"],
"prettier.prettierPath": "interface/.yarn/sdks/prettier/index.js",
"prettier.prettierPath": "",
"typescript.tsdk": "interface/.yarn/sdks/typescript/lib",
"typescript.enablePromptUseWorkspaceTsdk": true,
"files.associations": {
@@ -28,5 +28,18 @@
"utility": "cpp",
"string": "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",
"tasks": [
{
"label": "PlatformIO: Execute EMS-ESP (standalone)",
"type": "shell",
"command": "./.pio/build/standalone/program",
"linux": {
"options": {
"env": {
// Workaround for sdl2 `-m32` crash
// https://bugs.launchpad.net/ubuntu/+source/libsdl2/+bug/1775067/comments/7
"DBUS_FATAL_WARNINGS": "0"
}
}
},
"dependsOn": ["PlatformIO: Build EMS-ESP (standalone)"],
"problemMatcher": []
"label": "build standalone emsesp",
"command": "make",
"args": [],
"problemMatcher": ["$gcc"],
"group": {
"kind": "build",
"isDefault": true
}
}
]
}

View File

@@ -4,32 +4,52 @@
## **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 format of the Custom Entities has changed, so you will need to manually re-create them.
- 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.
## Added
- 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)
- Detect old Tado thermostat, device-id 0x19, no entities
- Some more HM200 entities [#500](https://github.com/emsesp/EMS-ESP32/issues/500)
- Custom Scheduler [#701](https://github.com/emsesp/EMS-ESP32/issues/701)
- Custom Entities read from EMS bus
- Added Scheduler [#701](https://github.com/emsesp/EMS-ESP32/issues/701)
- Added Custom Entities read/write from EMS bus
- Build S3 binary with github actions
- Greenstar HIU [#1158](https://github.com/emsesp/EMS-ESP32/issues/1158)
- 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)
- 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)
- 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
- HA-discovery for analog sensor commands [#1035](https://github.com/emsesp/EMS-ESP32/issues/1035)
- Enum order of RC3x nofrost mode
- 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
@@ -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)
- 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
- 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
- Retry timeout for Mqtt-QOS1/2 10seconds
- 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)
- 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 := emsesp
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
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
LIBRARIES :=
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/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 :=
CPPCHECK = cppcheck
# CHECKFLAGS = -q --force --std=c++17
@@ -28,16 +28,18 @@ CHECKFLAGS = -q --force --std=c++11
#----------------------------------------------------------------------
# Languages Standard
#----------------------------------------------------------------------
# C_STANDARD := -std=c17
C_STANDARD := -std=c17
# CXX_STANDARD := -std=c++17
C_STANDARD := -std=c11
CXX_STANDARD := -std=c++11
CXX_STANDARD := -std=gnu++11
# C_STANDARD := -std=c11
# CXX_STANDARD := -std=c++11
#----------------------------------------------------------------------
# Defined Symbols
#----------------------------------------------------------------------
DEFINES += -DARDUINOJSON_ENABLE_STD_STRING=1 -DARDUINOJSON_ENABLE_PROGMEM=1 -DARDUINOJSON_ENABLE_ARDUINO_STRING -DARDUINOJSON_USE_DOUBLE=0 -DARDUINO
DEFINES += -DEMSESP_DEBUG -DEMSESP_STANDALONE -DEMSESP_TEST
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 -D__linux__ -DEMC_RX_BUFFER_SIZE=1500
DEFINES += $(ARGS)
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))
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,$(LIBRARIES),$(wildcard $(dir)/include)))
@@ -79,7 +81,7 @@ CPPFLAGS += -g3
CPPFLAGS += -Os
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

View File

@@ -1,6 +1,6 @@
# Name, Type, SubType, Offset, Size, Flags
nvs, data, nvs, 0x9000, 0x5000,
otadata, data, ota, , 0x2000,
app1, app, ota_1, , 0x140000,
app0, app, ota_0, , 0x2A0000,
app1, app, ota_1, , 0x140000,
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/explicit-function-return-type": "off",
"@typescript-eslint/no-unsafe-call": "off",
"@typescript-eslint/no-unsafe-enum-comparison": "off",
"@typescript-eslint/no-unsafe-assignment": "off",
"@typescript-eslint/no-unsafe-return": "off",
"@typescript-eslint/no-unsafe-member-access": "off",

View File

@@ -1,5 +1,5 @@
{
"adapter": "react",
"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">
<head>
<meta charset="UTF-8" />

View File

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

View File

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

View File

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

View File

@@ -3,6 +3,7 @@ import { Box, Fab, Paper, Typography, Button } from '@mui/material';
import { useRequest } from 'alova';
import { useContext, useState } from 'react';
import { toast } from 'react-toastify';
import { FeaturesContext } from './contexts/features';
import type { ValidateFieldsError } from 'async-validator';
import type { Locales } from 'i18n/i18n-types';
@@ -33,6 +34,8 @@ const SignIn: FC = () => {
const { LL, setLocale, locale } = useContext(I18nContext);
const { features } = useContext(FeaturesContext);
const [signInRequest, setSignInRequest] = useState<SignInRequest>({
username: '',
password: ''
@@ -55,7 +58,7 @@ const SignIn: FC = () => {
const signIn = async () => {
await callSignIn(signInRequest).catch((event) => {
if (event.message === 'Unauthorized') {
toast.warn(LL.INVALID_LOGIN());
toast.warning(LL.INVALID_LOGIN());
} else {
toast.error(LL.ERROR() + ' ' + event.message);
}
@@ -107,6 +110,7 @@ const SignIn: FC = () => {
})}
>
<Typography variant="h4">{PROJECT_NAME}</Typography>
<Typography variant="subtitle2">{features.version}</Typography>
<Box
mt={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')}>
<DEflag style={{ width: 24 }} />
&nbsp;DE
</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')}>
<FRflag style={{ width: 24 }} />
&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.
// TODO how best to handle http errors like 401 (unauthorized)
// but I think this is handled correctly in AppRouting? See AuthenticatedRouting()
// http errors like 401 (unauthorized) are handled either in the methods or AuthenticatedRouting()
// onError: (error, method) => {
// 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 listNetworks = () =>
alovaInstance.Get<WiFiNetworkList>('/rest/listNetworks', {
name: 'listNetworks'
name: 'listNetworks',
timeout: 20000 // timeout 20 seconds
});
export const readNetworkSettings = () =>
alovaInstance.Get<NetworkSettings>('/rest/networkSettings', { name: 'networkSettings' });

View File

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

View File

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

View File

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

View File

@@ -168,7 +168,21 @@ const MqttSettingsForm: FC = () => {
<MenuItem value={2}>2</MenuItem>
</TextField>
</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>
<BlockFormControlLabel
control={<Checkbox name="clean_session" checked={data.clean_session} onChange={updateFormValue} />}
label={LL.MQTT_CLEAN_SESSION()}

View File

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

View File

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

View File

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

View File

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

View File

@@ -45,9 +45,9 @@ const de: Translation = {
CHANGE_VALUE: 'Wert ändern',
CANCEL: 'Abbrechen',
RESET: 'Zurücksetzen',
APPLY_CHANGES: 'Apply Changes ({0})', // TODO translate
UPDATE: 'Update', // TODO translate
EXECUTE: 'Execute', // TODO translate
APPLY_CHANGES: 'Änderungen anwenden ({0})',
UPDATE: 'Update',
EXECUTE: 'Ausführen',
REMOVE: 'Entfernen',
PROBLEM_UPDATING: 'Problem beim Aktualisieren',
PROBLEM_LOADING: 'Problem beim Laden',
@@ -180,7 +180,7 @@ const de: Translation = {
LOG_OF: '{0} Log',
STATUS_OF: '{0} Status',
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',
CLOSE: 'Schließen',
USE: 'Verwenden Sie',
@@ -240,7 +240,7 @@ const de: Translation = {
MQTT_PUBLISH_TEXT_2: 'Veröffentliche als Kommando-Topic (ioBroker)',
MQTT_PUBLISH_TEXT_3: 'Aktiviere `MQTT Discovery`',
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_INT_BOILER: 'Boiler und Wärmepumpen',
MQTT_INT_THERMOSTATS: 'Thermostate',
@@ -282,7 +282,7 @@ const de: Translation = {
SCAN_AGAIN: 'Erneute Suche',
NETWORK_SCANNER: 'Netzwerk Suche',
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',
HOSTNAME: 'Hostname',
NETWORK_DISABLE_SLEEP: 'Deaktiviere WiFi Schlafmodus',
@@ -319,10 +319,11 @@ const de: Translation = {
SCHEDULE_TIMER_3: 'jede Stunde',
CUSTOM_ENTITIES: 'Individuelle Entitäten',
ENTITIES_HELP_1: 'Abfrage von Werten auf dem EMS-Bus',
ENTITIES_UPDATED: 'Entities Updated', // TODO translate
ENTITIES_UPDATED: 'Entitäten gespeichert',
WRITEABLE: 'Schreibbar',
SHOWING: 'Showing', // TODO translate
SEARCH: 'Search' // TODO translate
SHOWING: 'Anzeigen von',
SEARCH: 'Suche',
CERT: 'TSL Zertifikat (Freilassen um TSL zu deaktivieren)'
};
export default de;

View File

@@ -282,7 +282,7 @@ const en: Translation = {
SCAN_AGAIN: 'Scan again',
NETWORK_SCANNER: 'Network Scanner',
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',
HOSTNAME: 'Hostname',
NETWORK_DISABLE_SLEEP: 'Disable WiFi Sleep Mode',
@@ -322,8 +322,8 @@ const en: Translation = {
ENTITIES_UPDATED: 'Entities Updated',
WRITEABLE: 'Writeable',
SHOWING: 'Showing',
SEARCH: 'Search'
SEARCH: 'Search',
CERT: 'TSL root certificate (leave blank to disable TSL)'
};
export default en;

View File

@@ -282,7 +282,7 @@ const fr: Translation = {
SCAN_AGAIN: 'Rescanner',
NETWORK_SCANNER: 'Scan réseau',
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',
HOSTNAME: 'Nom d\'hôte',
NETWORK_DISABLE_SLEEP: 'Désactiver le mode veille du WiFi',
@@ -322,7 +322,8 @@ const fr: Translation = {
ENTITIES_UPDATED: 'Entities Updated', // TODO translate
WRITEABLE: 'Writeable', // 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;

View File

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

View File

@@ -282,7 +282,7 @@ const nl: Translation = {
SCAN_AGAIN: 'Opnieuw scannen',
NETWORK_SCANNER: 'Netwerk Scanner',
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',
HOSTNAME: 'Hostname',
NETWORK_DISABLE_SLEEP: 'WiFi Sleep Mode uitzetten',
@@ -322,7 +322,8 @@ const nl: Translation = {
ENTITIES_UPDATED: 'Entiteiten bijgewerkt',
WRITEABLE: 'Beschrijfbare',
SHOWING: 'Tonen',
SEARCH: 'Zoek'
SEARCH: 'Zoek',
CERT: 'TSL root certificate (leave blank to disable TSL)' // TODO translate
};
export default nl;

View File

@@ -282,7 +282,7 @@ const no: Translation = {
SCAN_AGAIN: 'Søk igjen',
NETWORK_SCANNER: 'Nettverk Scanner',
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',
HOSTNAME: 'Hostname',
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
WRITEABLE: 'Writeable', // 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;

View File

@@ -282,7 +282,7 @@ const pl: BaseTranslation = {
SCAN_AGAIN: 'Skanuj ponownie',
NETWORK_SCANNER: 'Skaner sieci WiFi',
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',
HOSTNAME: 'Nazwa w sieci',
NETWORK_DISABLE_SLEEP: 'Wyłącz tryb uśpienia WiFi',
@@ -322,8 +322,8 @@ const pl: BaseTranslation = {
ENTITIES_UPDATED: 'Niestandardowe encje zostały uaktualnione.',
WRITEABLE: 'zapisywalna',
SHOWING: 'Wyświetlane',
SEARCH: 'Szukaj'
SEARCH: 'Szukaj',
CERT: 'TSL root certificate (leave blank to disable TSL)' // TODO translate
};
export default pl;

View File

@@ -282,7 +282,7 @@ const sv: Translation = {
SCAN_AGAIN: 'Sök igen',
NETWORK_SCANNER: 'Hittade nätverk',
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',
HOSTNAME: 'Värdnamn',
NETWORK_DISABLE_SLEEP: 'Inaktivera sömnläge',
@@ -322,8 +322,8 @@ const sv: Translation = {
ENTITIES_UPDATED: 'Entities Updated', // TODO translate
WRITEABLE: 'Writeable', // 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;

View File

@@ -282,7 +282,7 @@ const tr: Translation = {
SCAN_AGAIN: 'Tekrar tara',
NETWORK_SCANNER: 'Ağ Tarayıcısı',
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ü',
HOSTNAME: 'Ana Makine Adı',
NETWORK_DISABLE_SLEEP: 'Kablosuz uyku modunu devre dışına al',
@@ -322,8 +322,8 @@ const tr: Translation = {
ENTITIES_UPDATED: 'Entities Updated', // TODO translate
WRITEABLE: 'Writeable', // 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;

View File

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

View File

@@ -17,7 +17,7 @@ import {
} from '@mui/material';
import { useEffect, useState } from 'react';
import { DeviceValueUOM_s } from './types';
import { DeviceValueUOM_s, DeviceValueType } from './types';
import type { EntityItem } from './types';
import type Schema from 'async-validator';
import type { ValidateFieldsError } from 'async-validator';
@@ -166,17 +166,18 @@ const SettingsEntitiesDialog = ({
fullWidth
select
>
<MenuItem value={0}>BOOL</MenuItem>
<MenuItem value={1}>INT</MenuItem>
<MenuItem value={2}>UINT</MenuItem>
<MenuItem value={3}>SHORT</MenuItem>
<MenuItem value={4}>USHORT</MenuItem>
<MenuItem value={5}>ULONG</MenuItem>
<MenuItem value={6}>TIME</MenuItem>
<MenuItem value={DeviceValueType.BOOL}>BOOL</MenuItem>
<MenuItem value={DeviceValueType.INT}>INT</MenuItem>
<MenuItem value={DeviceValueType.UINT}>UINT</MenuItem>
<MenuItem value={DeviceValueType.SHORT}>SHORT</MenuItem>
<MenuItem value={DeviceValueType.USHORT}>USHORT</MenuItem>
<MenuItem value={DeviceValueType.ULONG}>ULONG</MenuItem>
<MenuItem value={DeviceValueType.TIME}>TIME</MenuItem>
<MenuItem value={DeviceValueType.STRING}>RAW</MenuItem>
</TextField>
</Grid>
{editItem.value_type !== 0 && (
{editItem.value_type !== DeviceValueType.BOOL && editItem.value_type !== DeviceValueType.STRING && (
<>
<Grid item xs={4}>
<TextField
@@ -210,6 +211,21 @@ const SettingsEntitiesDialog = ({
</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>
</DialogContent>

View File

@@ -243,7 +243,8 @@ export const BOARD_PROFILES: BoardProfiles = {
OLIMEXPOE: 'Olimex ESP32-POE',
C3MINI: 'Wemos C3 Mini',
S2MINI: 'Wemos S2 Mini',
S3MINI: 'Liligo S3'
S3MINI: 'Liligo S3',
S32S3: 'BBQKees Gateway S3'
};
export interface BoardProfile {
@@ -364,3 +365,17 @@ export const enum DeviceType {
CUSTOM,
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 (
value &&
(value === 1 ||
(value >= 10 && value <= 12) ||
(value >= 6 && value <= 12) ||
(value >= 14 && value <= 15) ||
value === 20 ||
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) =>
new Schema({
...(settings.board_profile === 'CUSTOM' &&
@@ -69,6 +86,14 @@ export const createSettingsValidator = (settings: Settings) =>
tx_gpio: [{ required: true, message: 'Tx 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 && {
syslog_host: [{ required: true, message: 'Host is required' }, IP_OR_HOSTNAME_VALIDATOR],
syslog_port: [

View File

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

View File

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

View File

@@ -15,7 +15,9 @@ export enum WiFiEncryptionType {
WIFI_AUTH_WPA_PSK = 2,
WIFI_AUTH_WPA2_PSK = 3,
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 {

View File

@@ -1,21 +1,21 @@
import { defineConfig, type PluginOption } from 'vite';
import react from '@vitejs/plugin-react-swc';
import viteTsconfigPaths from 'vite-tsconfig-paths';
import svgrPlugin from 'vite-plugin-svgr';
import { visualizer } from 'rollup-plugin-visualizer';
import ProgmemGenerator from './progmem-generator';
import preact from '@preact/preset-vite';
export default defineConfig(({ command, mode }) => {
if (mode === 'hosted') {
return {
// 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 {
// normal build
return {
plugins: [
react(),
preact(),
viteTsconfigPaths(),
svgrPlugin(),
ProgmemGenerator({ outputPath: '../lib/framework/WWWData.h', bytesPerLine: 20 })
@@ -26,7 +26,25 @@ export default defineConfig(({ command, mode }) => {
chunkSizeWarningLimit: 1024,
sourcemap: 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: {

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,15 @@
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)
-------

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)
[![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)
[![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)
[![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)
[![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)
[![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.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.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 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
version=6.21.2
version=6.21.3
author=Benoit Blanchon <blog.benoitblanchon.fr>
maintainer=Benoit Blanchon <blog.benoitblanchon.fr>
sentence=A simple and efficient JSON library for embedded C++.

View File

@@ -18,7 +18,9 @@ struct Reader {
Reader(TSource& source) : source_(&source) {}
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) {

View File

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

View File

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

View File

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

View File

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

View File

@@ -4,7 +4,8 @@
#pragma once
#define ARDUINOJSON_VERSION "6.21.2"
#define ARDUINOJSON_VERSION "6.21.3"
#define ARDUINOJSON_VERSION_MAJOR 6
#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)
pinMode(pin_, pullMode ? INPUT_PULLUP : INPUT_PULLDOWN);
#else // esp8266 and standalone
#else // esp8266 and standalone
pinMode(pin_, pullMode ? INPUT_PULLUP : INPUT);
#endif
enabled_ = (digitalRead(pin_) == pullMode); // see if a button is connected

View File

@@ -47,21 +47,21 @@ class PButton {
bool pullMode_;
bool enabled_;
bool state_; // Value read from button
bool lastState_; // Last value of button state
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 singleClickOK_; // whether it's OK to do a single click
bool state_; // Value read from button
bool lastState_; // Last value of button state
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 singleClickOK_; // whether it's OK to do a single click
uint32_t downTime_; // time the button was pressed down
uint32_t upTime_; // time the button was released
uint32_t downTime_; // time the button was pressed down
uint32_t upTime_; // time the button was released
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 longPressHappened_; // whether or not the 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;
};

View File

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

View File

@@ -16,7 +16,7 @@ the LICENSE file.
#define EMC_SEMAPHORE_TAKE() xSemaphoreTake(_xSemaphore, portMAX_DELAY)
#define EMC_SEMAPHORE_GIVE() xSemaphoreGive(_xSemaphore)
#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());
#elif defined(ARDUINO_ARCH_ESP8266)
#include <Arduino.h> // millis(), ESP.getFreeHeap();

View File

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

View File

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

View File

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

View File

@@ -59,10 +59,17 @@ class ESP8266React {
return &_mqttSettingsService;
}
espMqttClient * getMqttClient() {
MqttClient * getMqttClient() {
return _mqttSettingsService.getMqttClient();
}
void setWill(const char * will_topic) {
_mqttSettingsService.setWill(will_topic);
}
void onMessage(espMqttClientTypes::OnMessageCallback callback) {
_mqttSettingsService.onMessage(callback);
}
void 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.
*/
void FactoryResetService::factoryReset() {
/*
* Based on LittleFS. Modified by proddy
* Could be replaced with fs.rmdir(FS_CONFIG_DIRECTORY) in IDF 4.2
*/
// TODO To replaced with fs.rmdir(FS_CONFIG_DIRECTORY) now we're using IDF 4.2
File root = fs->open(FS_CONFIG_DIRECTORY);
File file;
while (file = root.openNextFile()) {

View File

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

View File

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

View File

@@ -1,6 +1,6 @@
#include <MqttSettingsService.h>
#include "../../src/emsesp_stub.hpp" // proddy added
#include "../../src/emsesp_stub.hpp"
using namespace std::placeholders; // for `_1` etc
@@ -34,10 +34,8 @@ MqttSettingsService::MqttSettingsService(AsyncWebServer * server, FS * fs, Secur
, _reconfigureMqtt(false)
, _disconnectedAt(0)
, _disconnectReason(espMqttClientTypes::DisconnectReason::TCP_DISCONNECTED)
, _mqttClient(espMqttClientTypes::UseInternalTask::NO) {
, _mqttClient(nullptr) {
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);
}
@@ -46,18 +44,46 @@ MqttSettingsService::~MqttSettingsService() {
void MqttSettingsService::begin() {
_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() {
if (_reconfigureMqtt || (_disconnectedAt && (uint32_t)(uuid::get_uptime() - _disconnectedAt) >= MQTT_RECONNECTION_DELAY)) {
// reconfigure MQTT client
_disconnectedAt = uuid::get_uptime();
if (configureMqtt()) {
_disconnectedAt = 0;
}
_disconnectedAt = configureMqtt() ? 0 : uuid::get_uptime();
_reconfigureMqtt = false;
}
_mqttClient.loop();
_mqttClient->loop();
}
bool MqttSettingsService::isEnabled() {
@@ -65,19 +91,39 @@ bool MqttSettingsService::isEnabled() {
}
bool MqttSettingsService::isConnected() {
return _mqttClient.connected();
return _mqttClient->connected();
}
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() {
return _disconnectReason;
}
espMqttClient * MqttSettingsService::getMqttClient() {
return &_mqttClient;
MqttClient * MqttSettingsService::getMqttClient() {
return _mqttClient;
}
void MqttSettingsService::onMqttConnect(bool sessionPresent) {
@@ -87,7 +133,6 @@ void MqttSettingsService::onMqttConnect(bool sessionPresent) {
}
void MqttSettingsService::onMqttDisconnect(espMqttClientTypes::DisconnectReason reason) {
// emsesp::EMSESP::logger().info("Disconnected from MQTT reason: %d", (uint8_t)reason);
_disconnectReason = reason;
if (!_disconnectedAt) {
_disconnectedAt = uuid::get_uptime();
@@ -99,7 +144,7 @@ void MqttSettingsService::onConfigUpdated() {
_reconfigureMqtt = true;
_disconnectedAt = 0;
// added by proddy
startClient();
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_WIFI_STA_GOT_IP6:
if (_state.enabled) {
// emsesp::EMSESP::logger().info("IPv4 Network connection found, starting MQTT client");
onConfigUpdated();
}
break;
case ARDUINO_EVENT_WIFI_STA_DISCONNECTED:
case ARDUINO_EVENT_ETH_DISCONNECTED:
if (_state.enabled) {
// emsesp::EMSESP::logger().info("Network connection dropped, stopping MQTT client");
_mqttClient.disconnect(true);
_mqttClient->disconnect(true);
}
break;
@@ -128,29 +171,54 @@ void MqttSettingsService::WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info) {
}
bool MqttSettingsService::configureMqtt() {
// disconnect if connected
_mqttClient.disconnect(true);
// disconnect if already connected
if (_mqttClient->connected()) {
emsesp::EMSESP::logger().info("Disconnecting to configure");
_mqttClient->disconnect(true);
}
// only connect if WiFi is connected and MQTT is enabled
if (_state.enabled && emsesp::EMSESP::system_.network_connected() && !_state.host.isEmpty()) {
// emsesp::EMSESP::logger().info("Configuring MQTT client");
_mqttClient.setServer(retainCstr(_state.host.c_str(), &_retainedHost), _state.port);
if (_state.username.length() > 0) {
_mqttClient.setCredentials(retainCstr(_state.username.c_str(), &_retainedUsername),
retainCstr(_state.password.length() > 0 ? _state.password.c_str() : nullptr, &_retainedPassword));
} else {
_mqttClient.setCredentials(retainCstr(nullptr, &_retainedUsername), retainCstr(nullptr, &_retainedPassword));
_reconfigureMqtt = false;
#if CONFIG_IDF_TARGET_ESP32S3
if (_state.rootCA.length() > 0) {
// emsesp::EMSESP::logger().info("Start secure MQTT with rootCA");
static_cast<espMqttClientSecure *>(_mqttClient)->setServer(retainCstr(_state.host.c_str(), &_retainedHost), _state.port);
if (_state.username.length() > 0) {
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));
_mqttClient.setKeepAlive(_state.keepAlive);
_mqttClient.setCleanSession(_state.cleanSession);
return _mqttClient.connect();
// } else {
// emsesp::EMSESP::logger().info("Error configuring MQTT client");
#endif
// emsesp::EMSESP::logger().info("Configuring MQTT client");
static_cast<espMqttClient *>(_mqttClient)->setServer(retainCstr(_state.host.c_str(), &_retainedHost), _state.port);
if (_state.username.length() > 0) {
static_cast<espMqttClient *>(_mqttClient)
->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;
}
void MqttSettings::read(MqttSettings & settings, JsonObject & root) {
#if CONFIG_IDF_TARGET_ESP32S3
root["rootCA"] = settings.rootCA;
#endif
root["enabled"] = settings.enabled;
root["host"] = settings.host;
root["port"] = settings.port;
@@ -162,7 +230,6 @@ void MqttSettings::read(MqttSettings & settings, JsonObject & root) {
root["clean_session"] = settings.cleanSession;
root["entity_format"] = settings.entity_format;
// added by proddy for EMS-ESP
root["publish_time_boiler"] = settings.publish_time_boiler;
root["publish_time_thermostat"] = settings.publish_time_thermostat;
root["publish_time_solar"] = settings.publish_time_solar;
@@ -185,6 +252,9 @@ StateUpdateResult MqttSettings::update(JsonObject & root, MqttSettings & setting
MqttSettings newSettings = {};
bool changed = false;
#if CONFIG_IDF_TARGET_ESP32S3
newSettings.rootCA = root["rootCA"] | "";
#endif
newSettings.enabled = root["enabled"] | FACTORY_MQTT_ENABLED;
newSettings.host = root["host"] | FACTORY_MQTT_HOST;
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);
}
#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
settings = newSettings;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -19,7 +19,7 @@
enum class StateUpdateResult {
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
ERROR // There was a problem updating the state, propagation should not take place
};

View File

@@ -1,7 +1,7 @@
#include <SystemStatus.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
@@ -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);
if (partition != NULL) { // factory partition found
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);
if (partition) {
uint64_t buffer;

View File

@@ -1,4 +1,5 @@
#include <UploadFileService.h>
#include <esp_ota_ops.h>
using namespace std::placeholders; // for `_1` etc
@@ -42,7 +43,8 @@ void UploadFileService::handleUpload(AsyncWebServerRequest * request, const Stri
return;
} else {
md5[0] = '\0';
return; // unsupported file type
handleError(request, 406); // Not Acceptable - unsupported file type
return;
}
if (is_firmware) {
@@ -69,7 +71,7 @@ void UploadFileService::handleUpload(AsyncWebServerRequest * request, const Stri
}
#endif
// it's firmware - initialize the ArduinoOTA updater
if (Update.begin()) {
if (Update.begin(fsize - sizeof(esp_image_header_t))) {
if (strlen(md5) == 32) {
Update.setMD5(md5);
md5[0] = '\0';
@@ -87,7 +89,9 @@ void UploadFileService::handleUpload(AsyncWebServerRequest * request, const Stri
if (!is_firmware) {
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 {
// 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);
return;
}
if (strlen(md5) == 32) {
auto * response = new AsyncJsonResponse(false, 256);
JsonObject root = response->getRoot();
root["md5"] = md5;
response->setLength();
request->send(response);
// AsyncWebServerResponse * response = request->beginResponse(201, "text/plain", md5); // created
// request->send(response);
return;
}
handleError(request, 403); // send the forbidden response
handleError(request, 500);
}
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
request->_tempObject = new int(code);
AsyncWebServerResponse * response = request->beginResponse(code);
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() {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -35,7 +35,7 @@
#include <iostream>
// #define IPAddress std::string
#define IPAddress String
// #define IPAddress String
#define ICACHE_FLASH_ATTR
#define ICACHE_RAM_ATTR
@@ -171,7 +171,13 @@ extern NativeConsole Serial;
extern ETHClass ETH;
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();

View File

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

View File

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

View File

@@ -3,6 +3,7 @@
#include <Arduino.h>
#include <functional>
#include <IPAddress.h>
#define WiFiMode_t wifi_mode_t
#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
// 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
{

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",
"express": "^4.18.2",
"multer": "^1.4.5-lts.1",
"nodemon": "^2.0.22"
"nodemon": "^3.0.1"
},
"packageManager": "yarn@3.4.1"
}

View File

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

View File

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

View File

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

View File

@@ -20,7 +20,6 @@ core_build_flags =
; -std=gnu++17
; core_unbuild_flags = -std=gnu++11
; core_unbuild_flags = -std=gnu++17
core_unbuild_flags =
; my_build_flags is set in pio_local.ini
@@ -41,9 +40,12 @@ unbuild_flags =
[espressi32_base]
platform = espressif32
; platform = espressif32@5.3.0
; platform = espressif32@5.2.0
framework = arduino
build_flags = ${common.build_flags}
build_unflags = ${common.unbuild_flags}
extra_scripts =
pre:scripts/build_interface.py
scripts/rename_fw.py
[env]
monitor_speed = 115200
@@ -62,7 +64,7 @@ check_flags =
; build for GitHub Actions CI
; the Web interface is built seperately
[env:ci]
platform = espressif32@5.2.0
platform = espressif32
framework = arduino
extra_scripts = scripts/rename_fw.py
board = esp32dev
@@ -84,100 +86,71 @@ build_flags = ${common.build_flags} -O2
build_unflags = ${common.unbuild_flags}
[env:esp32_4M]
platform = espressif32@5.2.0
framework = arduino
extra_scripts =
pre:scripts/build_interface.py
scripts/rename_fw.py
extends = espressi32_base
board = esp32dev
board_upload.flash_size = 4MB
board_build.partitions = esp32_partition_4M.csv
build_flags = ${common.build_flags} -Os
build_unflags = ${common.unbuild_flags}
[env:esp32_4Mplus]
extends = espressi32_base
extra_scripts =
pre:scripts/build_interface.py
scripts/rename_fw.py
board = esp32dev
board_upload.flash_size = 4MB
board_build.partitions = esp32_asym_partition_4M.csv
build_flags = ${common.build_flags}
build_unflags = ${common.unbuild_flags}
[env:esp32_16M]
extends = espressi32_base
extra_scripts =
pre:scripts/build_interface.py
scripts/rename_fw.py
board = esp32dev
board_upload.flash_size = 16MB
board_build.partitions = esp32_partition_16M.csv
build_flags = ${common.build_flags}
build_unflags = ${common.unbuild_flags}
[env:lolin_c3_mini]
extends = espressi32_base
extra_scripts =
pre:scripts/build_interface.py
scripts/rename_fw.py
board = lolin_c3_mini
board_upload.flash_size = 4MB
board_build.partitions = esp32_asym_partition_4M.csv
build_flags = ${common.build_flags}
build_unflags = ${common.unbuild_flags}
; lolin C3 mini v1 needs special wifi init.
; https://www.wemos.cc/en/latest/c3/c3_mini_1_0_0.html#about-wifi
[env:lolin_c3_mini_v1]
extends = espressi32_base
extra_scripts =
pre:scripts/build_interface.py
scripts/rename_fw.py
board = lolin_c3_mini
board_upload.flash_size = 4MB
board_build.partitions = esp32_asym_partition_4M.csv
build_flags = ${common.build_flags} -DBOARD_C3_MINI_V1
build_unflags = ${common.unbuild_flags}
[env:lolin_s2_mini]
extends = espressi32_base
extra_scripts =
pre:scripts/build_interface.py
scripts/rename_fw.py
board = lolin_s2_mini
board_upload.flash_size = 4MB
board_build.partitions = esp32_asym_partition_4M.csv
build_flags = ${common.build_flags}
build_unflags = ${common.unbuild_flags}
[env:lolin_s3]
extends = espressi32_base
extra_scripts =
pre:scripts/build_interface.py
scripts/rename_fw.py
board = lolin_s3
board_build.f_cpu = 240000000L
board_upload.flash_size = 16MB
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.wait_for_upload_port = false
build_flags = ${common.build_flags} -O2
; to build and run: pio run -e standalone -t exec
[env:standalone]
platform = native
build_flags =
-DARDUINO
-DARDUINOJSON_ENABLE_STD_STRING=1 -DARDUINOJSON_ENABLE_PROGMEM=1 -DARDUINOJSON_ENABLE_ARDUINO_STRING -DARDUINOJSON_USE_DOUBLE=0
-DEMSESP_DEBUG -DEMSESP_STANDALONE -DEMSESP_TEST
-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_DEFAULT_LOCALE=\"en\" -DEMSESP_DEFAULT_TX_MODE=8 -DEMSESP_DEFAULT_VERSION=\"3.6.0-dev\" -DEMSESP_DEFAULT_BOARD_PROFILE=\"S32\"
-lpthread
-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
-Wno-missing-braces
-I./lib_standalone
-I./lib/ArduinoJson/src
-I./lib/uuid-common/src
@@ -185,6 +158,8 @@ build_src_flags =
-I./lib/uuid-log/src
-I./lib/semver
-I./lib/PButton
-I./lib/espMqttClient/src
-I./lib/espMqttClient/src/Transport
build_src_filter =
+<*>
-<.git/>
@@ -194,5 +169,7 @@ build_src_filter =
+<../lib/uuid-log>
+<../lib/semver>
+<../lib/PButton>
+<../lib/espMqttClient/src>
+<../lib/espMqttClient/src/Transport>
lib_compat_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 + "}}";
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());
} else {
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();
} else if (sensor.type() == AnalogType::DIGITAL_IN) {
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 {
snprintf(topic, sizeof(topic), "sensor/%s/analogsensor_%02d/config", Mqtt::basename().c_str(), sensor.gpio());
config["stat_cla"] = "measurement";
@@ -575,9 +586,7 @@ void AnalogSensor::publish_values(const bool force) {
// add "availability" section
Mqtt::add_avty_to_doc(stat_t, config.as<JsonObject>(), val_cond);
Mqtt::queue_ha(topic, config.as<JsonObject>());
sensor.ha_registered = true;
sensor.ha_registered = Mqtt::queue_ha(topic, config.as<JsonObject>());
}
}
}

View File

@@ -410,14 +410,16 @@ static void setup_commands(std::shared_ptr<Commands> & commands) {
if (arguments.size() == 4) {
uint16_t offset = Helpers::hextoint(arguments[2].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) {
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 {
// 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,
@@ -620,12 +622,12 @@ void EMSESPShell::stopped() {
// show welcome banner
void EMSESPShell::display_banner() {
println();
printfln("┌───────────────────────────────────────┐");
printfln("┌───────────────────────────────────────");
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("│ │");
printfln("│ type %shelp%s to show available commands │", COLOR_UNDERLINE, COLOR_RESET);
printfln("└───────────────────────────────────────┘");
printfln("│ %s%shttps://github.com/emsesp/EMS-ESP32%s ", COLOR_BRIGHT_GREEN, COLOR_UNDERLINE, COLOR_RESET);
printfln(" ");
printfln("│ type %shelp%s to show available commands ", COLOR_UNDERLINE, COLOR_RESET);
printfln("└───────────────────────────────────────");
println();
// set console name
@@ -640,16 +642,16 @@ std::string EMSESPShell::hostname_text() {
}
std::string EMSESPShell::context_text() {
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) {
// return "/fs");
} else {
return std::string{};
}
// auto shell_context = static_cast<ShellContext>(context());
// if (shell_context == ShellContext::MAIN) {
// return std::string{'/'};
// } else if (shell_context == ShellContext::FILESYSTEM) {
// return "/fs");
// } else {
// return std::string{};
// }
}
// 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),
DeviceValueUOM::K,
MAKE_CF_CB(set_tempDiffHeat),
3,
2,
10);
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA,
&tempDiffCool_,
@@ -599,7 +599,7 @@ Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const
FL_(tempDiffCool),
DeviceValueUOM::K,
MAKE_CF_CB(set_tempDiffCool),
3,
2,
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, &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->curroominfl, 27);
has_update(telegram, hc->coolingon, 32);
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
void Thermostat::process_RC300Set2(std::shared_ptr<const Telegram> telegram) {
// 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
// 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
// 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 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);
if (hc == nullptr) {
@@ -1161,7 +1162,7 @@ void Thermostat::process_HPMode(std::shared_ptr<const Telegram> telegram) {
if (hc == nullptr) {
return;
}
has_update(telegram, hc->hpmode, 0);
has_update(telegram, hc->hpmode, 5);
}
// 0x467 ff HP settings
@@ -1170,9 +1171,9 @@ void Thermostat::process_HPSet(std::shared_ptr<const Telegram> telegram) {
if (hc == nullptr) {
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->hpminflowtemp, 4); // 2-10K
has_update(telegram, hc->hpminflowtemp, 0); // 2-10K
}
// 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))) {
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;
}
@@ -2727,7 +2728,7 @@ bool Thermostat::set_controlmode(const char * value, const int8_t id) {
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) {
uint8_t hc_num = (id == -1) ? AUTO_HEATING_CIRCUIT : id;
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->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->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->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->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 hpmode;
uint8_t cooling;
uint8_t coolingon;
uint8_t hc_num() const {
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;
return true;
} 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;
}
}
@@ -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;
}
@@ -1645,7 +1652,7 @@ bool EMSdevice::generate_values(JsonObject & output, const uint8_t tag_filter, c
Helpers::translated_word(FL_(minutes)));
json[name] = time_s;
} 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;
bool EMSESP::read_next_ = false;
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()
uint32_t EMSESP::last_fetch_ = 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'
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];
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) {
if (telegram->message_length <= 4 && strlen(buffer) <= 11) {
uint32_t value = 0;
for (uint8_t i = 0; i < telegram->message_length; i++) {
value = (value << 8) + telegram->message_data[i];
}
doc["value"] = value;
}
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
@@ -847,12 +859,19 @@ void EMSESP::process_version(std::shared_ptr<const Telegram> telegram) {
// returns false if there are none found
bool EMSESP::process_telegram(std::shared_ptr<const Telegram> telegram) {
// if watching or reading...
if ((telegram->type_id == read_id_) && (telegram->dest == txservice_.ems_bus_id())) {
LOG_INFO("%s", pretty_telegram(telegram).c_str());
if (Mqtt::send_response()) {
if ((telegram->type_id == read_id_ || telegram->type_id == response_id_) && (telegram->dest == txservice_.ems_bus_id())) {
if (telegram->type_id == response_id_) {
if (!trace_raw_) {
LOG_TRACE("%s", pretty_telegram(telegram).c_str());
}
if (!read_next_) {
response_id_ = 0;
}
publish_response(telegram);
} else {
LOG_NOTICE("%s", pretty_telegram(telegram).c_str());
}
// check if read is finished or gives more parts
if (!read_next_) {
read_id_ = WATCH_ID_NONE;
}
@@ -951,7 +970,7 @@ bool EMSESP::device_exists(const uint8_t device_id) {
// for each associated EMS device go and get its system information
void EMSESP::show_devices(uuid::console::Shell & shell) {
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();
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
void EMSESP::send_read_request(const uint16_t type_id, const uint8_t dest, const uint8_t offset, const uint8_t length) {
txservice_.read_request(type_id, dest, offset, 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, front);
}
// 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)) {
LOG_DEBUG("Last Tx read successful");
txservice_.increment_telegram_read_count();
txservice_.send_poll(); // close the bus
txservice_.reset_retry_count();
tx_successful = true;
// 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;
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 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,
const uint8_t dest,
const uint8_t offset,
@@ -176,6 +176,10 @@ class EMSESP {
read_id_ = id;
}
static void set_response_id(uint16_t id) {
response_id_ = id;
}
static bool wait_validate() {
return (wait_validate_ != 0);
}
@@ -259,6 +263,7 @@ class EMSESP {
static uint16_t read_id_;
static bool read_next_;
static uint16_t publish_id_;
static uint16_t response_id_;
static bool tap_water_active_;
static uint8_t publish_all_idx_;
static uint8_t unique_id_count_;

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