mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2025-12-06 07:49:52 +03:00
Merge branch 'dev' of https://github.com/emsesp/EMS-ESP32 into dev2
This commit is contained in:
14
.gitignore
vendored
14
.gitignore
vendored
@@ -2,7 +2,7 @@
|
||||
.vscode/c_cpp_properties.json
|
||||
.vscode/extensions.json
|
||||
.vscode/launch.json
|
||||
# .vscode/settings.json
|
||||
.vscode/settings.json
|
||||
|
||||
# c++ compiling
|
||||
.clang_complete
|
||||
@@ -12,11 +12,11 @@ cppcheck.out.xml
|
||||
# platformio
|
||||
.pio
|
||||
pio_local.ini
|
||||
*_old
|
||||
|
||||
# OS specific
|
||||
.DS_Store
|
||||
*Thumbs.db
|
||||
emsesp
|
||||
|
||||
# web specfic
|
||||
build/
|
||||
@@ -43,7 +43,7 @@ interface/analyse.html
|
||||
test.sh
|
||||
scripts/run.sh
|
||||
scripts/__pycache__
|
||||
/scripts/stackdmp.txt
|
||||
scripts/stackdmp.txt
|
||||
|
||||
# i18n generated files
|
||||
interface/src/i18n/i18n-react.tsx
|
||||
@@ -57,9 +57,5 @@ interface/src/i18n/i18n-util.async.ts
|
||||
sonar/
|
||||
bw-output/
|
||||
|
||||
# entity dump results
|
||||
# dump_entities.csv
|
||||
# dump_entities.xls*
|
||||
|
||||
*_old
|
||||
|
||||
# testing
|
||||
emsesp
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
## **IMPORTANT! BREAKING CHANGES**
|
||||
|
||||
- new device WATER shows dhw entities from MM100 and SM100 in dhw setting
|
||||
- The Wifi Tx Power setting in Network Settings will be reset to Auto
|
||||
|
||||
## Added
|
||||
|
||||
@@ -31,6 +32,9 @@
|
||||
- MQTT autodiscovery in Domoticz not working [#1360](https://github.com/emsesp/EMS-ESP32/issues/1528)
|
||||
- dhw comfort for new ems+, [#1495](https://github.com/emsesp/EMS-ESP32/issues/1495)
|
||||
- added writeable icon to Web's Custom Entity page for each entity shown in the table
|
||||
- Wifi Tx Power not adjusted [#1614](https://github.com/emsesp/EMS-ESP32/issues/1614)
|
||||
- MQTT discovery of custom entity doesn't consider type of data [#1587](https://github.com/emsesp/EMS-ESP32/issues/1587)
|
||||
- WiFi TxPower wasn't correctly used. Added an 'Auto' setting, which is the default.
|
||||
|
||||
## Changed
|
||||
|
||||
@@ -38,3 +42,5 @@
|
||||
- HA don't set entity_category to Diagnostic/Configuration for EMS entities [#1459](https://github.com/emsesp/EMS-ESP32/discussions/1459)
|
||||
- upgraded ArduinoJson to 7.0.0 #1538 and then 7.0.2
|
||||
- small changes to the API for analog and temperature sensors
|
||||
- Length of mqtt Broker adress [#1619](https://github.com/emsesp/EMS-ESP32/issues/1619)
|
||||
- C++ optimizations - see <https://github.com/emsesp/EMS-ESP32/pull/1615>
|
||||
|
||||
@@ -36,7 +36,6 @@ build_flags =
|
||||
-D FACTORY_MQTT_PORT=1883
|
||||
-D FACTORY_MQTT_USERNAME=\"\"
|
||||
-D FACTORY_MQTT_PASSWORD=\"\"
|
||||
-D FACTORY_MQTT_CLIENT_ID=\"ems-esp\"
|
||||
-D FACTORY_MQTT_KEEP_ALIVE=60
|
||||
-D FACTORY_MQTT_CLEAN_SESSION=false
|
||||
-D FACTORY_MQTT_MAX_TOPIC_LENGTH=128
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
},
|
||||
"extends": [
|
||||
"eslint:recommended",
|
||||
"airbnb/hooks",
|
||||
"airbnb-typescript",
|
||||
// "airbnb/hooks",
|
||||
// "airbnb-typescript",
|
||||
"plugin:react/recommended",
|
||||
"plugin:react/jsx-runtime",
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
|
||||
@@ -26,8 +26,8 @@
|
||||
"@babel/core": "^7.23.9",
|
||||
"@emotion/react": "^11.11.3",
|
||||
"@emotion/styled": "^11.11.0",
|
||||
"@mui/icons-material": "^5.15.9",
|
||||
"@mui/material": "^5.15.9",
|
||||
"@mui/icons-material": "^5.15.10",
|
||||
"@mui/material": "^5.15.10",
|
||||
"@table-library/react-table-library": "4.1.7",
|
||||
"@types/imagemin": "^8.0.5",
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
@@ -54,12 +54,10 @@
|
||||
"devDependencies": {
|
||||
"@preact/compat": "^17.1.2",
|
||||
"@preact/preset-vite": "^2.8.1",
|
||||
"@typescript-eslint/eslint-plugin": "^6.21.0",
|
||||
"@typescript-eslint/parser": "^6.21.0",
|
||||
"@typescript-eslint/eslint-plugin": "^7.0.1",
|
||||
"@typescript-eslint/parser": "^7.0.1",
|
||||
"concurrently": "^8.2.2",
|
||||
"eslint": "^8.56.0",
|
||||
"eslint-config-airbnb": "^19.0.4",
|
||||
"eslint-config-airbnb-typescript": "^17.1.0",
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
"eslint-import-resolver-typescript": "^3.6.1",
|
||||
"eslint-plugin-autofix": "^1.1.0",
|
||||
@@ -72,7 +70,7 @@
|
||||
"prettier": "^3.2.5",
|
||||
"rollup-plugin-visualizer": "^5.12.0",
|
||||
"terser": "^5.27.0",
|
||||
"vite": "^5.1.1",
|
||||
"vite": "^5.1.2",
|
||||
"vite-plugin-imagemin": "^0.6.1",
|
||||
"vite-tsconfig-paths": "^4.3.1"
|
||||
},
|
||||
|
||||
@@ -2,6 +2,7 @@ import { readdirSync, existsSync, unlinkSync, readFileSync, createWriteStream }
|
||||
import { resolve, relative, sep } from 'path';
|
||||
import zlib from 'zlib';
|
||||
import mime from 'mime-types';
|
||||
import crypto from 'crypto';
|
||||
|
||||
const ARDUINO_INCLUDES = '#include <Arduino.h>\n\n';
|
||||
const INDENT = ' ';
|
||||
@@ -11,14 +12,17 @@ const bytesPerLine = 20;
|
||||
var totalSize = 0;
|
||||
|
||||
const generateWWWClass = () =>
|
||||
`typedef std::function<void(const String& uri, const String& contentType, const uint8_t * content, size_t len)> RouteRegistrationHandler;
|
||||
`typedef std::function<void(const String& uri, const String& contentType, const uint8_t * content, size_t len, const String& hash)> RouteRegistrationHandler;
|
||||
// Total size is ${totalSize} bytes
|
||||
|
||||
class WWWData {
|
||||
${indent}public:
|
||||
${indent.repeat(2)}static void registerRoutes(RouteRegistrationHandler handler) {
|
||||
${fileInfo
|
||||
.map((file) => `${indent.repeat(3)}handler("${file.uri}", "${file.mimeType}", ${file.variable}, ${file.size});`)
|
||||
.map(
|
||||
(file) =>
|
||||
`${indent.repeat(3)}handler("${file.uri}", "${file.mimeType}", ${file.variable}, ${file.size}, "${file.hash}");`
|
||||
)
|
||||
.join('\n')}
|
||||
${indent.repeat(2)}}
|
||||
};
|
||||
@@ -50,6 +54,12 @@ const writeFile = (relativeFilePath, buffer) => {
|
||||
writeStream.write('const uint8_t ' + variable + '[] = {');
|
||||
// const zipBuffer = zlib.brotliCompressSync(buffer, { quality: 1 });
|
||||
const zipBuffer = zlib.gzipSync(buffer, { level: 9 });
|
||||
|
||||
// create sha
|
||||
const hashSum = crypto.createHash('sha256');
|
||||
hashSum.update(zipBuffer);
|
||||
const hash = hashSum.digest('hex');
|
||||
|
||||
zipBuffer.forEach((b) => {
|
||||
if (!(size % bytesPerLine)) {
|
||||
writeStream.write('\n');
|
||||
@@ -58,15 +68,19 @@ const writeFile = (relativeFilePath, buffer) => {
|
||||
writeStream.write('0x' + ('00' + b.toString(16).toUpperCase()).slice(-2) + ',');
|
||||
size++;
|
||||
});
|
||||
|
||||
if (size % bytesPerLine) {
|
||||
writeStream.write('\n');
|
||||
}
|
||||
|
||||
writeStream.write('};\n\n');
|
||||
|
||||
fileInfo.push({
|
||||
uri: '/' + relativeFilePath.replace(sep, '/'),
|
||||
mimeType,
|
||||
variable,
|
||||
size
|
||||
size,
|
||||
hash
|
||||
});
|
||||
|
||||
// console.log(relativeFilePath + ' (size ' + size + ' bytes)');
|
||||
|
||||
@@ -72,6 +72,7 @@ const MqttSettingsForm: FC = () => {
|
||||
name="host"
|
||||
label={LL.ADDRESS_OF(LL.BROKER())}
|
||||
fullWidth
|
||||
multiline
|
||||
variant="outlined"
|
||||
value={data.host}
|
||||
onChange={updateFormValue}
|
||||
|
||||
@@ -15,8 +15,8 @@ import {
|
||||
ListItemSecondaryAction,
|
||||
ListItemText,
|
||||
Typography,
|
||||
InputAdornment,
|
||||
TextField
|
||||
TextField,
|
||||
MenuItem
|
||||
} from '@mui/material';
|
||||
// eslint-disable-next-line import/named
|
||||
import { updateState, useRequest } from 'alova';
|
||||
@@ -43,7 +43,7 @@ import {
|
||||
} from 'components';
|
||||
import { useI18nContext } from 'i18n/i18n-react';
|
||||
|
||||
import { numberValue, updateValueDirty, useRest } from 'utils';
|
||||
import { updateValueDirty, useRest } from 'utils';
|
||||
|
||||
import { validate } from 'validators';
|
||||
import { createNetworkSettingsValidator } from 'validators/network';
|
||||
@@ -88,7 +88,7 @@ const WiFiSettingsForm: FC = () => {
|
||||
static_ip_config: false,
|
||||
enableIPv6: false,
|
||||
bandwidth20: false,
|
||||
tx_power: 20,
|
||||
tx_power: 0,
|
||||
nosleep: false,
|
||||
enableMDNS: true,
|
||||
enableCORS: false,
|
||||
@@ -196,20 +196,27 @@ const WiFiSettingsForm: FC = () => {
|
||||
margin="normal"
|
||||
/>
|
||||
)}
|
||||
<ValidatedTextField
|
||||
fieldErrors={fieldErrors}
|
||||
<TextField
|
||||
name="tx_power"
|
||||
label={LL.TX_POWER()}
|
||||
InputProps={{
|
||||
endAdornment: <InputAdornment position="end">dBm</InputAdornment>
|
||||
}}
|
||||
fullWidth
|
||||
variant="outlined"
|
||||
value={numberValue(data.tx_power)}
|
||||
value={data.tx_power}
|
||||
onChange={updateFormValue}
|
||||
type="number"
|
||||
margin="normal"
|
||||
/>
|
||||
select
|
||||
>
|
||||
<MenuItem value={0}>Auto</MenuItem>
|
||||
<MenuItem value={78}>19.5 dBm</MenuItem>
|
||||
<MenuItem value={76}>19 dBm</MenuItem>
|
||||
<MenuItem value={74}>18.5 dBm</MenuItem>
|
||||
<MenuItem value={68}>17 dBm</MenuItem>
|
||||
<MenuItem value={60}>15 dBm</MenuItem>
|
||||
<MenuItem value={52}>13 dBm</MenuItem>
|
||||
<MenuItem value={44}>11 dBm</MenuItem>
|
||||
<MenuItem value={34}>8.5 dBm</MenuItem>
|
||||
<MenuItem value={28}>7 dBm</MenuItem>
|
||||
</TextField>
|
||||
<BlockFormControlLabel
|
||||
control={<Checkbox name="nosleep" checked={data.nosleep} onChange={updateFormValue} />}
|
||||
label={LL.NETWORK_DISABLE_SLEEP()}
|
||||
|
||||
@@ -14,9 +14,5 @@ export const createNetworkSettingsValidator = (networkSettings: NetworkSettings)
|
||||
subnet_mask: [{ required: true, message: 'Subnet mask is required' }, IP_ADDRESS_VALIDATOR],
|
||||
dns_ip_1: IP_ADDRESS_VALIDATOR,
|
||||
dns_ip_2: IP_ADDRESS_VALIDATOR
|
||||
}),
|
||||
tx_power: [
|
||||
{ required: true, message: 'Tx Power is required' },
|
||||
{ type: 'number', min: 0, max: 20, message: 'Tx Power must be between 0 and 20dBm' }
|
||||
]
|
||||
})
|
||||
});
|
||||
|
||||
@@ -33,7 +33,7 @@ export const IP_ADDRESS_VALIDATOR = {
|
||||
}
|
||||
};
|
||||
|
||||
const HOSTNAME_LENGTH_REGEXP = /^.{0,63}$/;
|
||||
const HOSTNAME_LENGTH_REGEXP = /^.{0,200}$/;
|
||||
const HOSTNAME_PATTERN_REGEXP =
|
||||
/^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9-]*[A-Za-z0-9])$/;
|
||||
|
||||
@@ -42,7 +42,7 @@ const isValidHostname = (value: string) => HOSTNAME_LENGTH_REGEXP.test(value) &&
|
||||
export const HOSTNAME_VALIDATOR = {
|
||||
validator(rule: InternalRuleItem, value: string, callback: (error?: string) => void) {
|
||||
if (value && !isValidHostname(value)) {
|
||||
callback('Must be a valid hostname of up to 63 characters');
|
||||
callback('Must be a valid hostname');
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
@@ -52,7 +52,7 @@ export const HOSTNAME_VALIDATOR = {
|
||||
export const IP_OR_HOSTNAME_VALIDATOR = {
|
||||
validator(rule: InternalRuleItem, value: string, callback: (error?: string) => void) {
|
||||
if (value && !(isValidIpAddress(value) || isValidHostname(value))) {
|
||||
callback('Must be a valid IP address or hostname of up to 63 characters');
|
||||
callback('Must be a valid IP address or hostname');
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
|
||||
@@ -1014,16 +1014,16 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@mui/core-downloads-tracker@npm:^5.15.9":
|
||||
version: 5.15.9
|
||||
resolution: "@mui/core-downloads-tracker@npm:5.15.9"
|
||||
checksum: 10/f0f7af8e8f6f50df29a4e41ecb59c75869f760f22df0dc534476094089c74edcd7eacb4d17e636e0b7dd06ea1f3bb6564b21dbe072f89d1b9d87373760d69e2b
|
||||
"@mui/core-downloads-tracker@npm:^5.15.10":
|
||||
version: 5.15.10
|
||||
resolution: "@mui/core-downloads-tracker@npm:5.15.10"
|
||||
checksum: 10/aeb16b31f60c08cc03585fedadceadd54aa48dda394fb945ab885f884c1b1692efb72309465641b6ca2367bd53d5fdce15f189d4691f42b59206622ffb2d6f0f
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@mui/icons-material@npm:^5.15.9":
|
||||
version: 5.15.9
|
||||
resolution: "@mui/icons-material@npm:5.15.9"
|
||||
"@mui/icons-material@npm:^5.15.10":
|
||||
version: 5.15.10
|
||||
resolution: "@mui/icons-material@npm:5.15.10"
|
||||
dependencies:
|
||||
"@babel/runtime": "npm:^7.23.9"
|
||||
peerDependencies:
|
||||
@@ -1033,17 +1033,17 @@ __metadata:
|
||||
peerDependenciesMeta:
|
||||
"@types/react":
|
||||
optional: true
|
||||
checksum: 10/bcda24107125108569fe8252d05297f441362d33dbb96f8c32b35ac6d280a3c9a2f03548344c73d316e26c89d2d3e74057b292be6677ab1b582d94b6cf3ba100
|
||||
checksum: 10/ce22c02dc7ed960a21f8d5ea7c4d4fc03d9f71e8a26ced02f75da1ffd6c768e6fa0682a308a03be53bffc2325a5aaf68be69f9e192b0a57c6752f7548e5b9045
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@mui/material@npm:^5.15.9":
|
||||
version: 5.15.9
|
||||
resolution: "@mui/material@npm:5.15.9"
|
||||
"@mui/material@npm:^5.15.10":
|
||||
version: 5.15.10
|
||||
resolution: "@mui/material@npm:5.15.10"
|
||||
dependencies:
|
||||
"@babel/runtime": "npm:^7.23.9"
|
||||
"@mui/base": "npm:5.0.0-beta.36"
|
||||
"@mui/core-downloads-tracker": "npm:^5.15.9"
|
||||
"@mui/core-downloads-tracker": "npm:^5.15.10"
|
||||
"@mui/system": "npm:^5.15.9"
|
||||
"@mui/types": "npm:^7.2.13"
|
||||
"@mui/utils": "npm:^5.15.9"
|
||||
@@ -1066,7 +1066,7 @@ __metadata:
|
||||
optional: true
|
||||
"@types/react":
|
||||
optional: true
|
||||
checksum: 10/fbbb33f83520f2f0a31d7a75be02c3c038da0bd2d2a914eadbe890783f18e9a93f818ea93da21cc6a6c303352662b4da764c67094183cee5133f810ffabead07
|
||||
checksum: 10/a88ad1287a905549ed516742544c8ba32f0cd7e1b184564efc8ceba5f43060d37b5cd113db605f1bb5be6c74cbdad7321d3fd7df410ba33d55548cf7c5bbf8d0
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -1713,15 +1713,15 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/eslint-plugin@npm:^6.21.0":
|
||||
version: 6.21.0
|
||||
resolution: "@typescript-eslint/eslint-plugin@npm:6.21.0"
|
||||
"@typescript-eslint/eslint-plugin@npm:^7.0.1":
|
||||
version: 7.0.1
|
||||
resolution: "@typescript-eslint/eslint-plugin@npm:7.0.1"
|
||||
dependencies:
|
||||
"@eslint-community/regexpp": "npm:^4.5.1"
|
||||
"@typescript-eslint/scope-manager": "npm:6.21.0"
|
||||
"@typescript-eslint/type-utils": "npm:6.21.0"
|
||||
"@typescript-eslint/utils": "npm:6.21.0"
|
||||
"@typescript-eslint/visitor-keys": "npm:6.21.0"
|
||||
"@typescript-eslint/scope-manager": "npm:7.0.1"
|
||||
"@typescript-eslint/type-utils": "npm:7.0.1"
|
||||
"@typescript-eslint/utils": "npm:7.0.1"
|
||||
"@typescript-eslint/visitor-keys": "npm:7.0.1"
|
||||
debug: "npm:^4.3.4"
|
||||
graphemer: "npm:^1.4.0"
|
||||
ignore: "npm:^5.2.4"
|
||||
@@ -1729,73 +1729,73 @@ __metadata:
|
||||
semver: "npm:^7.5.4"
|
||||
ts-api-utils: "npm:^1.0.1"
|
||||
peerDependencies:
|
||||
"@typescript-eslint/parser": ^6.0.0 || ^6.0.0-alpha
|
||||
eslint: ^7.0.0 || ^8.0.0
|
||||
"@typescript-eslint/parser": ^7.0.0
|
||||
eslint: ^8.56.0
|
||||
peerDependenciesMeta:
|
||||
typescript:
|
||||
optional: true
|
||||
checksum: 10/a57de0f630789330204cc1531f86cfc68b391cafb1ba67c8992133f1baa2a09d629df66e71260b040de4c9a3ff1252952037093c4128b0d56c4dbb37720b4c1d
|
||||
checksum: 10/0862e8ec8677fcea794394fc9eab8dba11043c08452722790e0d296d4ee84713180676e1e3135be4203ace7bb73933c94159255cb9190c7bc13bf7f03a361915
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/parser@npm:^6.21.0":
|
||||
version: 6.21.0
|
||||
resolution: "@typescript-eslint/parser@npm:6.21.0"
|
||||
"@typescript-eslint/parser@npm:^7.0.1":
|
||||
version: 7.0.1
|
||||
resolution: "@typescript-eslint/parser@npm:7.0.1"
|
||||
dependencies:
|
||||
"@typescript-eslint/scope-manager": "npm:6.21.0"
|
||||
"@typescript-eslint/types": "npm:6.21.0"
|
||||
"@typescript-eslint/typescript-estree": "npm:6.21.0"
|
||||
"@typescript-eslint/visitor-keys": "npm:6.21.0"
|
||||
"@typescript-eslint/scope-manager": "npm:7.0.1"
|
||||
"@typescript-eslint/types": "npm:7.0.1"
|
||||
"@typescript-eslint/typescript-estree": "npm:7.0.1"
|
||||
"@typescript-eslint/visitor-keys": "npm:7.0.1"
|
||||
debug: "npm:^4.3.4"
|
||||
peerDependencies:
|
||||
eslint: ^7.0.0 || ^8.0.0
|
||||
eslint: ^8.56.0
|
||||
peerDependenciesMeta:
|
||||
typescript:
|
||||
optional: true
|
||||
checksum: 10/4d51cdbc170e72275efc5ef5fce48a81ec431e4edde8374f4d0213d8d370a06823e1a61ae31d502a5f1b0d1f48fc4d29a1b1b5c2dcf809d66d3872ccf6e46ac7
|
||||
checksum: 10/b4ba1743ab730268a1924139f072e4a0a56959526fb6377e1b3964518b6c6851733ae446a44d29fed1cb96669e2913cca524895ce77a6205aaed8bda00e8cd5d
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/scope-manager@npm:6.21.0":
|
||||
version: 6.21.0
|
||||
resolution: "@typescript-eslint/scope-manager@npm:6.21.0"
|
||||
"@typescript-eslint/scope-manager@npm:7.0.1":
|
||||
version: 7.0.1
|
||||
resolution: "@typescript-eslint/scope-manager@npm:7.0.1"
|
||||
dependencies:
|
||||
"@typescript-eslint/types": "npm:6.21.0"
|
||||
"@typescript-eslint/visitor-keys": "npm:6.21.0"
|
||||
checksum: 10/fe91ac52ca8e09356a71dc1a2f2c326480f3cccfec6b2b6d9154c1a90651ab8ea270b07c67df5678956c3bbf0bbe7113ab68f68f21b20912ea528b1214197395
|
||||
"@typescript-eslint/types": "npm:7.0.1"
|
||||
"@typescript-eslint/visitor-keys": "npm:7.0.1"
|
||||
checksum: 10/dade6055bb853adb54de795cc3da5ab8550236d4186f108573fdb02e636ab7fc4300a55b506698ced4087ca43b143a5593931cb3195ab4790470b456d9ff8846
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/type-utils@npm:6.21.0":
|
||||
version: 6.21.0
|
||||
resolution: "@typescript-eslint/type-utils@npm:6.21.0"
|
||||
"@typescript-eslint/type-utils@npm:7.0.1":
|
||||
version: 7.0.1
|
||||
resolution: "@typescript-eslint/type-utils@npm:7.0.1"
|
||||
dependencies:
|
||||
"@typescript-eslint/typescript-estree": "npm:6.21.0"
|
||||
"@typescript-eslint/utils": "npm:6.21.0"
|
||||
"@typescript-eslint/typescript-estree": "npm:7.0.1"
|
||||
"@typescript-eslint/utils": "npm:7.0.1"
|
||||
debug: "npm:^4.3.4"
|
||||
ts-api-utils: "npm:^1.0.1"
|
||||
peerDependencies:
|
||||
eslint: ^7.0.0 || ^8.0.0
|
||||
eslint: ^8.56.0
|
||||
peerDependenciesMeta:
|
||||
typescript:
|
||||
optional: true
|
||||
checksum: 10/d03fb3ee1caa71f3ce053505f1866268d7ed79ffb7fed18623f4a1253f5b8f2ffc92636d6fd08fcbaf5bd265a6de77bf192c53105131e4724643dfc910d705fc
|
||||
checksum: 10/cf20a3c0e56121ac62467e48121e135798db6d2999bd4f96ed44edc39f2597812d12b1bd6a378adec54d6c5e7db75fa5f98a27ce399792a2c8a5bbd3649952f7
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/types@npm:6.21.0":
|
||||
version: 6.21.0
|
||||
resolution: "@typescript-eslint/types@npm:6.21.0"
|
||||
checksum: 10/e26da86d6f36ca5b6ef6322619f8ec55aabcd7d43c840c977ae13ae2c964c3091fc92eb33730d8be08927c9de38466c5323e78bfb270a9ff1d3611fe821046c5
|
||||
"@typescript-eslint/types@npm:7.0.1":
|
||||
version: 7.0.1
|
||||
resolution: "@typescript-eslint/types@npm:7.0.1"
|
||||
checksum: 10/c08b2d34bab2a877a45a1e4c2923f50d03022b682b7aaba929ae2a9a5ad32db0e46265544a6616ccb98654b434250621be0e282fc5b21b8ccaf6b78741d68f67
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/typescript-estree@npm:6.21.0":
|
||||
version: 6.21.0
|
||||
resolution: "@typescript-eslint/typescript-estree@npm:6.21.0"
|
||||
"@typescript-eslint/typescript-estree@npm:7.0.1":
|
||||
version: 7.0.1
|
||||
resolution: "@typescript-eslint/typescript-estree@npm:7.0.1"
|
||||
dependencies:
|
||||
"@typescript-eslint/types": "npm:6.21.0"
|
||||
"@typescript-eslint/visitor-keys": "npm:6.21.0"
|
||||
"@typescript-eslint/types": "npm:7.0.1"
|
||||
"@typescript-eslint/visitor-keys": "npm:7.0.1"
|
||||
debug: "npm:^4.3.4"
|
||||
globby: "npm:^11.1.0"
|
||||
is-glob: "npm:^4.0.3"
|
||||
@@ -1805,34 +1805,34 @@ __metadata:
|
||||
peerDependenciesMeta:
|
||||
typescript:
|
||||
optional: true
|
||||
checksum: 10/b32fa35fca2a229e0f5f06793e5359ff9269f63e9705e858df95d55ca2cd7fdb5b3e75b284095a992c48c5fc46a1431a1a4b6747ede2dd08929dc1cbacc589b8
|
||||
checksum: 10/b0b0adc84502d1ffcf3a0024179e0f2780be5f8b0a18328db46d430efc4e38a7965656b4392dd47d6176bbb1ee200aec6dd8581c39b606e260750574358cde9f
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/utils@npm:6.21.0":
|
||||
version: 6.21.0
|
||||
resolution: "@typescript-eslint/utils@npm:6.21.0"
|
||||
"@typescript-eslint/utils@npm:7.0.1":
|
||||
version: 7.0.1
|
||||
resolution: "@typescript-eslint/utils@npm:7.0.1"
|
||||
dependencies:
|
||||
"@eslint-community/eslint-utils": "npm:^4.4.0"
|
||||
"@types/json-schema": "npm:^7.0.12"
|
||||
"@types/semver": "npm:^7.5.0"
|
||||
"@typescript-eslint/scope-manager": "npm:6.21.0"
|
||||
"@typescript-eslint/types": "npm:6.21.0"
|
||||
"@typescript-eslint/typescript-estree": "npm:6.21.0"
|
||||
"@typescript-eslint/scope-manager": "npm:7.0.1"
|
||||
"@typescript-eslint/types": "npm:7.0.1"
|
||||
"@typescript-eslint/typescript-estree": "npm:7.0.1"
|
||||
semver: "npm:^7.5.4"
|
||||
peerDependencies:
|
||||
eslint: ^7.0.0 || ^8.0.0
|
||||
checksum: 10/b404a2c55a425a79d054346ae123087d30c7ecf7ed7abcf680c47bf70c1de4fabadc63434f3f460b2fa63df76bc9e4a0b9fa2383bb8a9fcd62733fb5c4e4f3e3
|
||||
eslint: ^8.56.0
|
||||
checksum: 10/b7e0cb2994f73b3f416684dc175d4e1da5f8306d6c81abbad2f219fa3e4f29154063a3c9568e4a1f879a38b79c62250e596e4ed7265f7bd1ed9b3db806cb92b7
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/visitor-keys@npm:6.21.0":
|
||||
version: 6.21.0
|
||||
resolution: "@typescript-eslint/visitor-keys@npm:6.21.0"
|
||||
"@typescript-eslint/visitor-keys@npm:7.0.1":
|
||||
version: 7.0.1
|
||||
resolution: "@typescript-eslint/visitor-keys@npm:7.0.1"
|
||||
dependencies:
|
||||
"@typescript-eslint/types": "npm:6.21.0"
|
||||
"@typescript-eslint/types": "npm:7.0.1"
|
||||
eslint-visitor-keys: "npm:^3.4.1"
|
||||
checksum: 10/30422cdc1e2ffad203df40351a031254b272f9c6f2b7e02e9bfa39e3fc2c7b1c6130333b0057412968deda17a3a68a578a78929a8139c6acef44d9d841dc72e1
|
||||
checksum: 10/915c5b19302a4c76e843cd2d04a9a2b11907e658d7018c8b55c338b090d9115d3719809aa05b8af130cc1b216c77626d210c20f705b732e83d04ceae0c112f6b
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -1851,8 +1851,8 @@ __metadata:
|
||||
"@babel/core": "npm:^7.23.9"
|
||||
"@emotion/react": "npm:^11.11.3"
|
||||
"@emotion/styled": "npm:^11.11.0"
|
||||
"@mui/icons-material": "npm:^5.15.9"
|
||||
"@mui/material": "npm:^5.15.9"
|
||||
"@mui/icons-material": "npm:^5.15.10"
|
||||
"@mui/material": "npm:^5.15.10"
|
||||
"@preact/compat": "npm:^17.1.2"
|
||||
"@preact/preset-vite": "npm:^2.8.1"
|
||||
"@table-library/react-table-library": "npm:4.1.7"
|
||||
@@ -1862,14 +1862,12 @@ __metadata:
|
||||
"@types/react": "npm:^18.2.55"
|
||||
"@types/react-dom": "npm:^18.2.19"
|
||||
"@types/react-router-dom": "npm:^5.3.3"
|
||||
"@typescript-eslint/eslint-plugin": "npm:^6.21.0"
|
||||
"@typescript-eslint/parser": "npm:^6.21.0"
|
||||
"@typescript-eslint/eslint-plugin": "npm:^7.0.1"
|
||||
"@typescript-eslint/parser": "npm:^7.0.1"
|
||||
alova: "npm:^2.17.0"
|
||||
async-validator: "npm:^4.2.5"
|
||||
concurrently: "npm:^8.2.2"
|
||||
eslint: "npm:^8.56.0"
|
||||
eslint-config-airbnb: "npm:^19.0.4"
|
||||
eslint-config-airbnb-typescript: "npm:^17.1.0"
|
||||
eslint-config-prettier: "npm:^9.1.0"
|
||||
eslint-import-resolver-typescript: "npm:^3.6.1"
|
||||
eslint-plugin-autofix: "npm:^1.1.0"
|
||||
@@ -1895,7 +1893,7 @@ __metadata:
|
||||
terser: "npm:^5.27.0"
|
||||
typesafe-i18n: "npm:^5.26.2"
|
||||
typescript: "npm:^5.3.3"
|
||||
vite: "npm:^5.1.1"
|
||||
vite: "npm:^5.1.2"
|
||||
vite-plugin-imagemin: "npm:^0.6.1"
|
||||
vite-tsconfig-paths: "npm:^4.3.1"
|
||||
languageName: unknown
|
||||
@@ -2738,13 +2736,6 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"confusing-browser-globals@npm:^1.0.10":
|
||||
version: 1.0.11
|
||||
resolution: "confusing-browser-globals@npm:1.0.11"
|
||||
checksum: 10/3afc635abd37e566477f610e7978b15753f0e84025c25d49236f1f14d480117185516bdd40d2a2167e6bed8048641a9854964b9c067e3dcdfa6b5d0ad3c3a5ef
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"console-control-strings@npm:^1.1.0":
|
||||
version: 1.1.0
|
||||
resolution: "console-control-strings@npm:1.1.0"
|
||||
@@ -3790,52 +3781,6 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"eslint-config-airbnb-base@npm:^15.0.0":
|
||||
version: 15.0.0
|
||||
resolution: "eslint-config-airbnb-base@npm:15.0.0"
|
||||
dependencies:
|
||||
confusing-browser-globals: "npm:^1.0.10"
|
||||
object.assign: "npm:^4.1.2"
|
||||
object.entries: "npm:^1.1.5"
|
||||
semver: "npm:^6.3.0"
|
||||
peerDependencies:
|
||||
eslint: ^7.32.0 || ^8.2.0
|
||||
eslint-plugin-import: ^2.25.2
|
||||
checksum: 10/daa68a1dcb7bff338747a952723b5fa9d159980ec3554c395a4b52a7f7d4f00a45e7b465420eb6d4d87a82cef6361e4cfd6dbb38c2f3f52f2140b6cf13654803
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"eslint-config-airbnb-typescript@npm:^17.1.0":
|
||||
version: 17.1.0
|
||||
resolution: "eslint-config-airbnb-typescript@npm:17.1.0"
|
||||
dependencies:
|
||||
eslint-config-airbnb-base: "npm:^15.0.0"
|
||||
peerDependencies:
|
||||
"@typescript-eslint/eslint-plugin": ^5.13.0 || ^6.0.0
|
||||
"@typescript-eslint/parser": ^5.0.0 || ^6.0.0
|
||||
eslint: ^7.32.0 || ^8.2.0
|
||||
eslint-plugin-import: ^2.25.3
|
||||
checksum: 10/a2238d820909ac005704e04d29ed495cebbe024869c488330273ea108e18cbf74b6b13e09d54d22a598fe793b9ed5ae593a7e8f9bdc6ea17614d5f2add340960
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"eslint-config-airbnb@npm:^19.0.4":
|
||||
version: 19.0.4
|
||||
resolution: "eslint-config-airbnb@npm:19.0.4"
|
||||
dependencies:
|
||||
eslint-config-airbnb-base: "npm:^15.0.0"
|
||||
object.assign: "npm:^4.1.2"
|
||||
object.entries: "npm:^1.1.5"
|
||||
peerDependencies:
|
||||
eslint: ^7.32.0 || ^8.2.0
|
||||
eslint-plugin-import: ^2.25.3
|
||||
eslint-plugin-jsx-a11y: ^6.5.1
|
||||
eslint-plugin-react: ^7.28.0
|
||||
eslint-plugin-react-hooks: ^4.3.0
|
||||
checksum: 10/f2086523cfd20c42fd620c757281bd028aa8ce9dadc7293c5c23ea60947a2d3ca04404ede77b40f5a65250fe3c04502acafc4f2f6946819fe6c257d76d9644e5
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"eslint-config-prettier@npm:^9.1.0":
|
||||
version: 9.1.0
|
||||
resolution: "eslint-config-prettier@npm:9.1.0"
|
||||
@@ -6603,7 +6548,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"object.assign@npm:^4.1.2, object.assign@npm:^4.1.4":
|
||||
"object.assign@npm:^4.1.4":
|
||||
version: 4.1.4
|
||||
resolution: "object.assign@npm:4.1.4"
|
||||
dependencies:
|
||||
@@ -6615,7 +6560,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"object.entries@npm:^1.1.5, object.entries@npm:^1.1.6, object.entries@npm:^1.1.7":
|
||||
"object.entries@npm:^1.1.6, object.entries@npm:^1.1.7":
|
||||
version: 1.1.7
|
||||
resolution: "object.entries@npm:1.1.7"
|
||||
dependencies:
|
||||
@@ -7739,7 +7684,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"semver@npm:^6.0.0, semver@npm:^6.3.0, semver@npm:^6.3.1":
|
||||
"semver@npm:^6.0.0, semver@npm:^6.3.1":
|
||||
version: 6.3.1
|
||||
resolution: "semver@npm:6.3.1"
|
||||
bin:
|
||||
@@ -8793,9 +8738,9 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"vite@npm:^5.1.1":
|
||||
version: 5.1.1
|
||||
resolution: "vite@npm:5.1.1"
|
||||
"vite@npm:^5.1.2":
|
||||
version: 5.1.2
|
||||
resolution: "vite@npm:5.1.2"
|
||||
dependencies:
|
||||
esbuild: "npm:^0.19.3"
|
||||
fsevents: "npm:~2.3.3"
|
||||
@@ -8829,7 +8774,7 @@ __metadata:
|
||||
optional: true
|
||||
bin:
|
||||
vite: bin/vite.js
|
||||
checksum: 10/bdb8e683caddaa0a9adcbf40144ca8ea3660836b208862b07d43787ea867845919af16e58745365bd13ed3b7f66bbf9788a6869ee22cfaacac01645b59729c34
|
||||
checksum: 10/fbfc5a84ee33c01cd2c3109ba08c2f3822df9a85bee79179ba5a812757f895e2da234208881b9943291d48b0a4ef8fb90ffaa790d89888530a2ad6c70c169e12
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
name=ESP Async WebServer
|
||||
version=2.6.1
|
||||
author=Me-No-Dev
|
||||
maintainer=Mathieu Carbou <mathieu.carbou@gmail.com>
|
||||
sentence=Asynchronous HTTP and WebSocket Server Library for ESP32
|
||||
paragraph=Supports: WebSocket, SSE, Authentication, Arduino Json 7, File Upload, Static File serving, URL Rewrite, URL Redirect, etc
|
||||
category=Other
|
||||
url=https://github.com/mathieucarbou/ESPAsyncWebServer
|
||||
architectures=esp32
|
||||
license=LGPL-3.0
|
||||
@@ -11,428 +11,426 @@ the LICENSE file.
|
||||
namespace espMqttClientInternals {
|
||||
|
||||
Packet::~Packet() {
|
||||
free(_data);
|
||||
free(_data);
|
||||
}
|
||||
|
||||
size_t Packet::available(size_t index) {
|
||||
if (index >= _size) return 0;
|
||||
if (!_getPayload) return _size - index;
|
||||
return _chunkedAvailable(index);
|
||||
if (index >= _size)
|
||||
return 0;
|
||||
if (!_getPayload)
|
||||
return _size - index;
|
||||
return _chunkedAvailable(index);
|
||||
}
|
||||
|
||||
const uint8_t* Packet::data(size_t index) const {
|
||||
if (!_getPayload) {
|
||||
if (!_data) return nullptr;
|
||||
if (index >= _size) return nullptr;
|
||||
return &_data[index];
|
||||
}
|
||||
return _chunkedData(index);
|
||||
const uint8_t * Packet::data(size_t index) const {
|
||||
if (!_getPayload) {
|
||||
if (!_data)
|
||||
return nullptr;
|
||||
if (index >= _size)
|
||||
return nullptr;
|
||||
return &_data[index];
|
||||
}
|
||||
return _chunkedData(index);
|
||||
}
|
||||
|
||||
size_t Packet::size() const {
|
||||
return _size;
|
||||
return _size;
|
||||
}
|
||||
|
||||
void Packet::setDup() {
|
||||
if (!_data) return;
|
||||
if (packetType() != PacketType.PUBLISH) return;
|
||||
if (_packetId == 0) return;
|
||||
_data[0] |= 0x08;
|
||||
if (!_data)
|
||||
return;
|
||||
if (packetType() != PacketType.PUBLISH)
|
||||
return;
|
||||
if (_packetId == 0)
|
||||
return;
|
||||
_data[0] |= 0x08;
|
||||
}
|
||||
|
||||
uint16_t Packet::packetId() const {
|
||||
return _packetId;
|
||||
return _packetId;
|
||||
}
|
||||
|
||||
MQTTPacketType Packet::packetType() const {
|
||||
if (_data) return static_cast<MQTTPacketType>(_data[0] & 0xF0);
|
||||
return static_cast<MQTTPacketType>(0);
|
||||
if (_data)
|
||||
return static_cast<MQTTPacketType>(_data[0] & 0xF0);
|
||||
return static_cast<MQTTPacketType>(0);
|
||||
}
|
||||
|
||||
bool Packet::removable() const {
|
||||
if (_packetId == 0) return true;
|
||||
if ((packetType() == PacketType.PUBACK) || (packetType() == PacketType.PUBCOMP)) return true;
|
||||
return false;
|
||||
if (_packetId == 0)
|
||||
return true;
|
||||
if ((packetType() == PacketType.PUBACK) || (packetType() == PacketType.PUBCOMP))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
Packet::Packet(espMqttClientTypes::Error& error,
|
||||
bool cleanSession,
|
||||
const char* username,
|
||||
const char* password,
|
||||
const char* willTopic,
|
||||
bool willRetain,
|
||||
uint8_t willQos,
|
||||
const uint8_t* willPayload,
|
||||
uint16_t willPayloadLength,
|
||||
uint16_t keepAlive,
|
||||
const char* clientId)
|
||||
: _packetId(0)
|
||||
, _data(nullptr)
|
||||
, _size(0)
|
||||
, _payloadIndex(0)
|
||||
, _payloadStartIndex(0)
|
||||
, _payloadEndIndex(0)
|
||||
, _getPayload(nullptr) {
|
||||
if (willPayload && willPayloadLength == 0) {
|
||||
size_t length = strlen(reinterpret_cast<const char*>(willPayload));
|
||||
if (length > UINT16_MAX) {
|
||||
emc_log_w("Payload length truncated (l:%zu)", length);
|
||||
willPayloadLength = UINT16_MAX;
|
||||
} else {
|
||||
willPayloadLength = length;
|
||||
Packet::Packet(espMqttClientTypes::Error & error,
|
||||
bool cleanSession,
|
||||
const char * username,
|
||||
const char * password,
|
||||
const char * willTopic,
|
||||
bool willRetain,
|
||||
uint8_t willQos,
|
||||
const uint8_t * willPayload,
|
||||
uint16_t willPayloadLength,
|
||||
uint16_t keepAlive,
|
||||
const char * clientId)
|
||||
: _packetId(0)
|
||||
, _data(nullptr)
|
||||
, _size(0)
|
||||
, _payloadIndex(0)
|
||||
, _payloadStartIndex(0)
|
||||
, _payloadEndIndex(0)
|
||||
, _getPayload(nullptr) {
|
||||
if (willPayload && willPayloadLength == 0) {
|
||||
size_t length = strlen(reinterpret_cast<const char *>(willPayload));
|
||||
if (length > UINT16_MAX) {
|
||||
emc_log_w("Payload length truncated (l:%zu)", length);
|
||||
willPayloadLength = UINT16_MAX;
|
||||
} else {
|
||||
willPayloadLength = length;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!clientId || strlen(clientId) == 0) {
|
||||
emc_log_w("clientId not set error");
|
||||
error = espMqttClientTypes::Error::MALFORMED_PARAMETER;
|
||||
return;
|
||||
}
|
||||
|
||||
// Calculate size
|
||||
size_t remainingLength =
|
||||
6 + // protocol
|
||||
1 + // protocol level
|
||||
1 + // connect flags
|
||||
2 + // keepalive
|
||||
2 + strlen(clientId) +
|
||||
(willTopic ? 2 + strlen(willTopic) + 2 + willPayloadLength : 0) +
|
||||
(username ? 2 + strlen(username) : 0) +
|
||||
(password ? 2 + strlen(password) : 0);
|
||||
|
||||
// allocate memory
|
||||
if (!_allocate(remainingLength, false)) {
|
||||
error = espMqttClientTypes::Error::OUT_OF_MEMORY;
|
||||
return;
|
||||
}
|
||||
|
||||
// serialize
|
||||
size_t pos = 0;
|
||||
|
||||
// FIXED HEADER
|
||||
_data[pos++] = PacketType.CONNECT | HeaderFlag.CONNECT_RESERVED;
|
||||
pos += encodeRemainingLength(remainingLength, &_data[pos]);
|
||||
pos += encodeString(PROTOCOL, &_data[pos]);
|
||||
_data[pos++] = PROTOCOL_LEVEL;
|
||||
uint8_t connectFlags = 0;
|
||||
if (cleanSession) connectFlags |= espMqttClientInternals::ConnectFlag.CLEAN_SESSION;
|
||||
if (username != nullptr) connectFlags |= espMqttClientInternals::ConnectFlag.USERNAME;
|
||||
if (password != nullptr) connectFlags |= espMqttClientInternals::ConnectFlag.PASSWORD;
|
||||
if (willTopic != nullptr) {
|
||||
connectFlags |= espMqttClientInternals::ConnectFlag.WILL;
|
||||
if (willRetain) connectFlags |= espMqttClientInternals::ConnectFlag.WILL_RETAIN;
|
||||
switch (willQos) {
|
||||
case 0:
|
||||
connectFlags |= espMqttClientInternals::ConnectFlag.WILL_QOS0;
|
||||
break;
|
||||
case 1:
|
||||
connectFlags |= espMqttClientInternals::ConnectFlag.WILL_QOS1;
|
||||
break;
|
||||
case 2:
|
||||
connectFlags |= espMqttClientInternals::ConnectFlag.WILL_QOS2;
|
||||
break;
|
||||
if (!clientId || strlen(clientId) == 0) {
|
||||
emc_log_w("clientId not set error");
|
||||
error = espMqttClientTypes::Error::MALFORMED_PARAMETER;
|
||||
return;
|
||||
}
|
||||
}
|
||||
_data[pos++] = connectFlags;
|
||||
_data[pos++] = keepAlive >> 8;
|
||||
_data[pos++] = keepAlive & 0xFF;
|
||||
|
||||
// PAYLOAD
|
||||
// client ID
|
||||
pos += encodeString(clientId, &_data[pos]);
|
||||
// will
|
||||
if (willTopic != nullptr && willPayload != nullptr) {
|
||||
pos += encodeString(willTopic, &_data[pos]);
|
||||
_data[pos++] = willPayloadLength >> 8;
|
||||
_data[pos++] = willPayloadLength & 0xFF;
|
||||
memcpy(&_data[pos], willPayload, willPayloadLength);
|
||||
pos += willPayloadLength;
|
||||
}
|
||||
// credentials
|
||||
if (username != nullptr) pos += encodeString(username, &_data[pos]);
|
||||
if (password != nullptr) encodeString(password, &_data[pos]);
|
||||
// Calculate size
|
||||
size_t remainingLength = 6 + // protocol
|
||||
1 + // protocol level
|
||||
1 + // connect flags
|
||||
2 + // keepalive
|
||||
2 + strlen(clientId) + (willTopic ? 2 + strlen(willTopic) + 2 + willPayloadLength : 0) + (username ? 2 + strlen(username) : 0)
|
||||
+ (password ? 2 + strlen(password) : 0);
|
||||
|
||||
error = espMqttClientTypes::Error::SUCCESS;
|
||||
// allocate memory
|
||||
if (!_allocate(remainingLength, false)) {
|
||||
error = espMqttClientTypes::Error::OUT_OF_MEMORY;
|
||||
return;
|
||||
}
|
||||
|
||||
// serialize
|
||||
size_t pos = 0;
|
||||
|
||||
// FIXED HEADER
|
||||
_data[pos++] = PacketType.CONNECT | HeaderFlag.CONNECT_RESERVED;
|
||||
pos += encodeRemainingLength(remainingLength, &_data[pos]);
|
||||
pos += encodeString(PROTOCOL, &_data[pos]);
|
||||
_data[pos++] = PROTOCOL_LEVEL;
|
||||
uint8_t connectFlags = 0;
|
||||
if (cleanSession)
|
||||
connectFlags |= espMqttClientInternals::ConnectFlag.CLEAN_SESSION;
|
||||
if (username != nullptr)
|
||||
connectFlags |= espMqttClientInternals::ConnectFlag.USERNAME;
|
||||
if (password != nullptr)
|
||||
connectFlags |= espMqttClientInternals::ConnectFlag.PASSWORD;
|
||||
if (willTopic != nullptr) {
|
||||
connectFlags |= espMqttClientInternals::ConnectFlag.WILL;
|
||||
if (willRetain)
|
||||
connectFlags |= espMqttClientInternals::ConnectFlag.WILL_RETAIN;
|
||||
switch (willQos) {
|
||||
case 0:
|
||||
connectFlags |= espMqttClientInternals::ConnectFlag.WILL_QOS0;
|
||||
break;
|
||||
case 1:
|
||||
connectFlags |= espMqttClientInternals::ConnectFlag.WILL_QOS1;
|
||||
break;
|
||||
case 2:
|
||||
connectFlags |= espMqttClientInternals::ConnectFlag.WILL_QOS2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
_data[pos++] = connectFlags;
|
||||
_data[pos++] = keepAlive >> 8;
|
||||
_data[pos++] = keepAlive & 0xFF;
|
||||
|
||||
// PAYLOAD
|
||||
// client ID
|
||||
pos += encodeString(clientId, &_data[pos]);
|
||||
// will
|
||||
if (willTopic != nullptr && willPayload != nullptr) {
|
||||
pos += encodeString(willTopic, &_data[pos]);
|
||||
_data[pos++] = willPayloadLength >> 8;
|
||||
_data[pos++] = willPayloadLength & 0xFF;
|
||||
memcpy(&_data[pos], willPayload, willPayloadLength);
|
||||
pos += willPayloadLength;
|
||||
}
|
||||
// credentials
|
||||
if (username != nullptr)
|
||||
pos += encodeString(username, &_data[pos]);
|
||||
if (password != nullptr)
|
||||
encodeString(password, &_data[pos]);
|
||||
|
||||
error = espMqttClientTypes::Error::SUCCESS;
|
||||
}
|
||||
|
||||
Packet::Packet(espMqttClientTypes::Error& error,
|
||||
uint16_t packetId,
|
||||
const char* topic,
|
||||
const uint8_t* payload,
|
||||
size_t payloadLength,
|
||||
uint8_t qos,
|
||||
bool retain)
|
||||
: _packetId(packetId)
|
||||
, _data(nullptr)
|
||||
, _size(0)
|
||||
, _payloadIndex(0)
|
||||
, _payloadStartIndex(0)
|
||||
, _payloadEndIndex(0)
|
||||
, _getPayload(nullptr) {
|
||||
size_t remainingLength =
|
||||
2 + strlen(topic) + // topic length + topic
|
||||
2 + // packet ID
|
||||
payloadLength;
|
||||
Packet::Packet(espMqttClientTypes::Error & error, uint16_t packetId, const char * topic, const uint8_t * payload, size_t payloadLength, uint8_t qos, bool retain)
|
||||
: _packetId(packetId)
|
||||
, _data(nullptr)
|
||||
, _size(0)
|
||||
, _payloadIndex(0)
|
||||
, _payloadStartIndex(0)
|
||||
, _payloadEndIndex(0)
|
||||
, _getPayload(nullptr) {
|
||||
size_t remainingLength = 2 + strlen(topic) + // topic length + topic
|
||||
2 + // packet ID
|
||||
payloadLength;
|
||||
|
||||
if (qos == 0) {
|
||||
remainingLength -= 2;
|
||||
_packetId = 0;
|
||||
}
|
||||
if (qos == 0) {
|
||||
remainingLength -= 2;
|
||||
_packetId = 0;
|
||||
}
|
||||
|
||||
if (!_allocate(remainingLength)) {
|
||||
error = espMqttClientTypes::Error::OUT_OF_MEMORY;
|
||||
return;
|
||||
}
|
||||
if (!_allocate(remainingLength)) {
|
||||
error = espMqttClientTypes::Error::OUT_OF_MEMORY;
|
||||
return;
|
||||
}
|
||||
|
||||
size_t pos = _fillPublishHeader(packetId, topic, remainingLength, qos, retain);
|
||||
size_t pos = _fillPublishHeader(packetId, topic, remainingLength, qos, retain);
|
||||
|
||||
// PAYLOAD
|
||||
memcpy(&_data[pos], payload, payloadLength);
|
||||
// PAYLOAD
|
||||
memcpy(&_data[pos], payload, payloadLength);
|
||||
|
||||
error = espMqttClientTypes::Error::SUCCESS;
|
||||
error = espMqttClientTypes::Error::SUCCESS;
|
||||
}
|
||||
|
||||
Packet::Packet(espMqttClientTypes::Error& error,
|
||||
uint16_t packetId,
|
||||
const char* topic,
|
||||
Packet::Packet(espMqttClientTypes::Error & error,
|
||||
uint16_t packetId,
|
||||
const char * topic,
|
||||
espMqttClientTypes::PayloadCallback payloadCallback,
|
||||
size_t payloadLength,
|
||||
uint8_t qos,
|
||||
bool retain)
|
||||
: _packetId(packetId)
|
||||
, _data(nullptr)
|
||||
, _size(0)
|
||||
, _payloadIndex(0)
|
||||
, _payloadStartIndex(0)
|
||||
, _payloadEndIndex(0)
|
||||
, _getPayload(payloadCallback) {
|
||||
size_t remainingLength =
|
||||
2 + strlen(topic) + // topic length + topic
|
||||
2 + // packet ID
|
||||
payloadLength;
|
||||
size_t payloadLength,
|
||||
uint8_t qos,
|
||||
bool retain)
|
||||
: _packetId(packetId)
|
||||
, _data(nullptr)
|
||||
, _size(0)
|
||||
, _payloadIndex(0)
|
||||
, _payloadStartIndex(0)
|
||||
, _payloadEndIndex(0)
|
||||
, _getPayload(payloadCallback) {
|
||||
size_t remainingLength = 2 + strlen(topic) + // topic length + topic
|
||||
2 + // packet ID
|
||||
payloadLength;
|
||||
|
||||
if (qos == 0) {
|
||||
remainingLength -= 2;
|
||||
_packetId = 0;
|
||||
}
|
||||
if (qos == 0) {
|
||||
remainingLength -= 2;
|
||||
_packetId = 0;
|
||||
}
|
||||
|
||||
if (!_allocate(remainingLength - payloadLength + std::min(payloadLength, static_cast<size_t>(EMC_RX_BUFFER_SIZE)))) {
|
||||
error = espMqttClientTypes::Error::OUT_OF_MEMORY;
|
||||
return;
|
||||
}
|
||||
if (!_allocate(remainingLength - payloadLength + std::min(payloadLength, static_cast<size_t>(EMC_RX_BUFFER_SIZE)))) {
|
||||
error = espMqttClientTypes::Error::OUT_OF_MEMORY;
|
||||
return;
|
||||
}
|
||||
|
||||
size_t pos = _fillPublishHeader(packetId, topic, remainingLength, qos, retain);
|
||||
size_t pos = _fillPublishHeader(packetId, topic, remainingLength, qos, retain);
|
||||
|
||||
// payload will be added by 'Packet::available'
|
||||
_size = pos + payloadLength;
|
||||
_payloadIndex = pos;
|
||||
_payloadStartIndex = _payloadIndex;
|
||||
_payloadEndIndex = _payloadIndex;
|
||||
// payload will be added by 'Packet::available'
|
||||
_size = pos + payloadLength;
|
||||
_payloadIndex = pos;
|
||||
_payloadStartIndex = _payloadIndex;
|
||||
_payloadEndIndex = _payloadIndex;
|
||||
|
||||
error = espMqttClientTypes::Error::SUCCESS;
|
||||
error = espMqttClientTypes::Error::SUCCESS;
|
||||
}
|
||||
|
||||
Packet::Packet(espMqttClientTypes::Error& error, uint16_t packetId, const char* topic, uint8_t qos)
|
||||
: _packetId(packetId)
|
||||
, _data(nullptr)
|
||||
, _size(0)
|
||||
, _payloadIndex(0)
|
||||
, _payloadStartIndex(0)
|
||||
, _payloadEndIndex(0)
|
||||
, _getPayload(nullptr) {
|
||||
SubscribeItem list[1] = {topic, qos};
|
||||
_createSubscribe(error, list, 1);
|
||||
Packet::Packet(espMqttClientTypes::Error & error, uint16_t packetId, const char * topic, uint8_t qos)
|
||||
: _packetId(packetId)
|
||||
, _data(nullptr)
|
||||
, _size(0)
|
||||
, _payloadIndex(0)
|
||||
, _payloadStartIndex(0)
|
||||
, _payloadEndIndex(0)
|
||||
, _getPayload(nullptr) {
|
||||
SubscribeItem list[1] = {{topic, qos}};
|
||||
_createSubscribe(error, list, 1);
|
||||
}
|
||||
|
||||
Packet::Packet(espMqttClientTypes::Error& error, MQTTPacketType type, uint16_t packetId)
|
||||
: _packetId(packetId)
|
||||
, _data(nullptr)
|
||||
, _size(0)
|
||||
, _payloadIndex(0)
|
||||
, _payloadStartIndex(0)
|
||||
, _payloadEndIndex(0)
|
||||
, _getPayload(nullptr) {
|
||||
if (!_allocate(2)) {
|
||||
error = espMqttClientTypes::Error::OUT_OF_MEMORY;
|
||||
return;
|
||||
}
|
||||
Packet::Packet(espMqttClientTypes::Error & error, MQTTPacketType type, uint16_t packetId)
|
||||
: _packetId(packetId)
|
||||
, _data(nullptr)
|
||||
, _size(0)
|
||||
, _payloadIndex(0)
|
||||
, _payloadStartIndex(0)
|
||||
, _payloadEndIndex(0)
|
||||
, _getPayload(nullptr) {
|
||||
if (!_allocate(2)) {
|
||||
error = espMqttClientTypes::Error::OUT_OF_MEMORY;
|
||||
return;
|
||||
}
|
||||
|
||||
size_t pos = 0;
|
||||
_data[pos] = type;
|
||||
if (type == PacketType.PUBREL) {
|
||||
_data[pos++] |= HeaderFlag.PUBREL_RESERVED;
|
||||
} else {
|
||||
pos++;
|
||||
}
|
||||
pos += encodeRemainingLength(2, &_data[pos]);
|
||||
_data[pos++] = packetId >> 8;
|
||||
_data[pos] = packetId & 0xFF;
|
||||
size_t pos = 0;
|
||||
_data[pos] = type;
|
||||
if (type == PacketType.PUBREL) {
|
||||
_data[pos++] |= HeaderFlag.PUBREL_RESERVED;
|
||||
} else {
|
||||
pos++;
|
||||
}
|
||||
pos += encodeRemainingLength(2, &_data[pos]);
|
||||
_data[pos++] = packetId >> 8;
|
||||
_data[pos] = packetId & 0xFF;
|
||||
|
||||
error = espMqttClientTypes::Error::SUCCESS;
|
||||
error = espMqttClientTypes::Error::SUCCESS;
|
||||
}
|
||||
|
||||
Packet::Packet(espMqttClientTypes::Error& error, uint16_t packetId, const char* topic)
|
||||
: _packetId(packetId)
|
||||
, _data(nullptr)
|
||||
, _size(0)
|
||||
, _payloadIndex(0)
|
||||
, _payloadStartIndex(0)
|
||||
, _payloadEndIndex(0)
|
||||
, _getPayload(nullptr) {
|
||||
const char* list[1] = {topic};
|
||||
_createUnsubscribe(error, list, 1);
|
||||
Packet::Packet(espMqttClientTypes::Error & error, uint16_t packetId, const char * topic)
|
||||
: _packetId(packetId)
|
||||
, _data(nullptr)
|
||||
, _size(0)
|
||||
, _payloadIndex(0)
|
||||
, _payloadStartIndex(0)
|
||||
, _payloadEndIndex(0)
|
||||
, _getPayload(nullptr) {
|
||||
const char * list[1] = {topic};
|
||||
_createUnsubscribe(error, list, 1);
|
||||
}
|
||||
|
||||
Packet::Packet(espMqttClientTypes::Error& error, MQTTPacketType type)
|
||||
: _packetId(0)
|
||||
, _data(nullptr)
|
||||
, _size(0)
|
||||
, _payloadIndex(0)
|
||||
, _payloadStartIndex(0)
|
||||
, _payloadEndIndex(0)
|
||||
, _getPayload(nullptr) {
|
||||
if (!_allocate(0)) {
|
||||
error = espMqttClientTypes::Error::OUT_OF_MEMORY;
|
||||
return;
|
||||
}
|
||||
_data[0] |= type;
|
||||
Packet::Packet(espMqttClientTypes::Error & error, MQTTPacketType type)
|
||||
: _packetId(0)
|
||||
, _data(nullptr)
|
||||
, _size(0)
|
||||
, _payloadIndex(0)
|
||||
, _payloadStartIndex(0)
|
||||
, _payloadEndIndex(0)
|
||||
, _getPayload(nullptr) {
|
||||
if (!_allocate(0)) {
|
||||
error = espMqttClientTypes::Error::OUT_OF_MEMORY;
|
||||
return;
|
||||
}
|
||||
_data[0] |= type;
|
||||
|
||||
error = espMqttClientTypes::Error::SUCCESS;
|
||||
error = espMqttClientTypes::Error::SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
bool Packet::_allocate(size_t remainingLength, bool check) {
|
||||
if (check && EMC_GET_FREE_MEMORY() < EMC_MIN_FREE_MEMORY) {
|
||||
emc_log_w("Packet buffer not allocated: low memory");
|
||||
return false;
|
||||
}
|
||||
_size = 1 + remainingLengthLength(remainingLength) + remainingLength;
|
||||
_data = reinterpret_cast<uint8_t*>(malloc(_size));
|
||||
if (!_data) {
|
||||
_size = 0;
|
||||
emc_log_w("Alloc failed (l:%zu)", _size);
|
||||
return false;
|
||||
}
|
||||
emc_log_i("Alloc (l:%zu)", _size);
|
||||
memset(_data, 0, _size);
|
||||
return true;
|
||||
if (check && EMC_GET_FREE_MEMORY() < EMC_MIN_FREE_MEMORY) {
|
||||
emc_log_w("Packet buffer not allocated: low memory");
|
||||
return false;
|
||||
}
|
||||
_size = 1 + remainingLengthLength(remainingLength) + remainingLength;
|
||||
_data = reinterpret_cast<uint8_t *>(malloc(_size));
|
||||
if (!_data) {
|
||||
_size = 0;
|
||||
emc_log_w("Alloc failed (l:%zu)", _size);
|
||||
return false;
|
||||
}
|
||||
emc_log_i("Alloc (l:%zu)", _size);
|
||||
memset(_data, 0, _size);
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t Packet::_fillPublishHeader(uint16_t packetId,
|
||||
const char* topic,
|
||||
size_t remainingLength,
|
||||
uint8_t qos,
|
||||
bool retain) {
|
||||
size_t index = 0;
|
||||
size_t Packet::_fillPublishHeader(uint16_t packetId, const char * topic, size_t remainingLength, uint8_t qos, bool retain) {
|
||||
size_t index = 0;
|
||||
|
||||
// FIXED HEADER
|
||||
_data[index] = PacketType.PUBLISH;
|
||||
if (retain) _data[index] |= HeaderFlag.PUBLISH_RETAIN;
|
||||
if (qos == 0) {
|
||||
_data[index++] |= HeaderFlag.PUBLISH_QOS0;
|
||||
} else if (qos == 1) {
|
||||
_data[index++] |= HeaderFlag.PUBLISH_QOS1;
|
||||
} else if (qos == 2) {
|
||||
_data[index++] |= HeaderFlag.PUBLISH_QOS2;
|
||||
}
|
||||
index += encodeRemainingLength(remainingLength, &_data[index]);
|
||||
// FIXED HEADER
|
||||
_data[index] = PacketType.PUBLISH;
|
||||
if (retain)
|
||||
_data[index] |= HeaderFlag.PUBLISH_RETAIN;
|
||||
if (qos == 0) {
|
||||
_data[index++] |= HeaderFlag.PUBLISH_QOS0;
|
||||
} else if (qos == 1) {
|
||||
_data[index++] |= HeaderFlag.PUBLISH_QOS1;
|
||||
} else if (qos == 2) {
|
||||
_data[index++] |= HeaderFlag.PUBLISH_QOS2;
|
||||
}
|
||||
index += encodeRemainingLength(remainingLength, &_data[index]);
|
||||
|
||||
// VARIABLE HEADER
|
||||
index += encodeString(topic, &_data[index]);
|
||||
if (qos > 0) {
|
||||
_data[index++] = packetId >> 8;
|
||||
_data[index++] = packetId & 0xFF;
|
||||
}
|
||||
// VARIABLE HEADER
|
||||
index += encodeString(topic, &_data[index]);
|
||||
if (qos > 0) {
|
||||
_data[index++] = packetId >> 8;
|
||||
_data[index++] = packetId & 0xFF;
|
||||
}
|
||||
|
||||
return index;
|
||||
return index;
|
||||
}
|
||||
|
||||
void Packet::_createSubscribe(espMqttClientTypes::Error& error,
|
||||
SubscribeItem* list,
|
||||
size_t numberTopics) {
|
||||
// Calculate size
|
||||
size_t payload = 0;
|
||||
for (size_t i = 0; i < numberTopics; ++i) {
|
||||
payload += 2 + strlen(list[i].topic) + 1; // length bytes, string, qos
|
||||
}
|
||||
size_t remainingLength = 2 + payload; // packetId + payload
|
||||
void Packet::_createSubscribe(espMqttClientTypes::Error & error, SubscribeItem * list, size_t numberTopics) {
|
||||
// Calculate size
|
||||
size_t payload = 0;
|
||||
for (size_t i = 0; i < numberTopics; ++i) {
|
||||
payload += 2 + strlen(list[i].topic) + 1; // length bytes, string, qos
|
||||
}
|
||||
size_t remainingLength = 2 + payload; // packetId + payload
|
||||
|
||||
// allocate memory
|
||||
if (!_allocate(remainingLength)) {
|
||||
error = espMqttClientTypes::Error::OUT_OF_MEMORY;
|
||||
return;
|
||||
}
|
||||
// allocate memory
|
||||
if (!_allocate(remainingLength)) {
|
||||
error = espMqttClientTypes::Error::OUT_OF_MEMORY;
|
||||
return;
|
||||
}
|
||||
|
||||
// serialize
|
||||
size_t pos = 0;
|
||||
_data[pos++] = PacketType.SUBSCRIBE | HeaderFlag.SUBSCRIBE_RESERVED;
|
||||
pos += encodeRemainingLength(remainingLength, &_data[pos]);
|
||||
_data[pos++] = _packetId >> 8;
|
||||
_data[pos++] = _packetId & 0xFF;
|
||||
for (size_t i = 0; i < numberTopics; ++i) {
|
||||
pos += encodeString(list[i].topic, &_data[pos]);
|
||||
_data[pos++] = list[i].qos;
|
||||
}
|
||||
// serialize
|
||||
size_t pos = 0;
|
||||
_data[pos++] = PacketType.SUBSCRIBE | HeaderFlag.SUBSCRIBE_RESERVED;
|
||||
pos += encodeRemainingLength(remainingLength, &_data[pos]);
|
||||
_data[pos++] = _packetId >> 8;
|
||||
_data[pos++] = _packetId & 0xFF;
|
||||
for (size_t i = 0; i < numberTopics; ++i) {
|
||||
pos += encodeString(list[i].topic, &_data[pos]);
|
||||
_data[pos++] = list[i].qos;
|
||||
}
|
||||
|
||||
error = espMqttClientTypes::Error::SUCCESS;
|
||||
error = espMqttClientTypes::Error::SUCCESS;
|
||||
}
|
||||
|
||||
void Packet::_createUnsubscribe(espMqttClientTypes::Error& error,
|
||||
const char** list,
|
||||
size_t numberTopics) {
|
||||
// Calculate size
|
||||
size_t payload = 0;
|
||||
for (size_t i = 0; i < numberTopics; ++i) {
|
||||
payload += 2 + strlen(list[i]); // length bytes, string
|
||||
}
|
||||
size_t remainingLength = 2 + payload; // packetId + payload
|
||||
void Packet::_createUnsubscribe(espMqttClientTypes::Error & error, const char ** list, size_t numberTopics) {
|
||||
// Calculate size
|
||||
size_t payload = 0;
|
||||
for (size_t i = 0; i < numberTopics; ++i) {
|
||||
payload += 2 + strlen(list[i]); // length bytes, string
|
||||
}
|
||||
size_t remainingLength = 2 + payload; // packetId + payload
|
||||
|
||||
// allocate memory
|
||||
if (!_allocate(remainingLength)) {
|
||||
error = espMqttClientTypes::Error::OUT_OF_MEMORY;
|
||||
return;
|
||||
}
|
||||
// allocate memory
|
||||
if (!_allocate(remainingLength)) {
|
||||
error = espMqttClientTypes::Error::OUT_OF_MEMORY;
|
||||
return;
|
||||
}
|
||||
|
||||
// serialize
|
||||
size_t pos = 0;
|
||||
_data[pos++] = PacketType.UNSUBSCRIBE | HeaderFlag.UNSUBSCRIBE_RESERVED;
|
||||
pos += encodeRemainingLength(remainingLength, &_data[pos]);
|
||||
_data[pos++] = _packetId >> 8;
|
||||
_data[pos++] = _packetId & 0xFF;
|
||||
for (size_t i = 0; i < numberTopics; ++i) {
|
||||
pos += encodeString(list[i], &_data[pos]);
|
||||
}
|
||||
// serialize
|
||||
size_t pos = 0;
|
||||
_data[pos++] = PacketType.UNSUBSCRIBE | HeaderFlag.UNSUBSCRIBE_RESERVED;
|
||||
pos += encodeRemainingLength(remainingLength, &_data[pos]);
|
||||
_data[pos++] = _packetId >> 8;
|
||||
_data[pos++] = _packetId & 0xFF;
|
||||
for (size_t i = 0; i < numberTopics; ++i) {
|
||||
pos += encodeString(list[i], &_data[pos]);
|
||||
}
|
||||
|
||||
error = espMqttClientTypes::Error::SUCCESS;
|
||||
error = espMqttClientTypes::Error::SUCCESS;
|
||||
}
|
||||
|
||||
size_t Packet::_chunkedAvailable(size_t index) {
|
||||
// index vs size check done in 'available(index)'
|
||||
// index vs size check done in 'available(index)'
|
||||
|
||||
// index points to header or first payload byte
|
||||
if (index < _payloadIndex) {
|
||||
if (_size > _payloadIndex && _payloadEndIndex != 0) {
|
||||
size_t copied = _getPayload(&_data[_payloadIndex], std::min(static_cast<size_t>(EMC_TX_BUFFER_SIZE), _size - _payloadStartIndex), index);
|
||||
_payloadStartIndex = _payloadIndex;
|
||||
_payloadEndIndex = _payloadStartIndex + copied - 1;
|
||||
// index points to header or first payload byte
|
||||
if (index < _payloadIndex) {
|
||||
if (_size > _payloadIndex && _payloadEndIndex != 0) {
|
||||
size_t copied = _getPayload(&_data[_payloadIndex], std::min(static_cast<size_t>(EMC_TX_BUFFER_SIZE), _size - _payloadStartIndex), index);
|
||||
_payloadStartIndex = _payloadIndex;
|
||||
_payloadEndIndex = _payloadStartIndex + copied - 1;
|
||||
}
|
||||
|
||||
// index points to payload unavailable
|
||||
} else if (index > _payloadEndIndex || _payloadStartIndex > index) {
|
||||
_payloadStartIndex = index;
|
||||
size_t copied = _getPayload(&_data[_payloadIndex], std::min(static_cast<size_t>(EMC_TX_BUFFER_SIZE), _size - _payloadStartIndex), index);
|
||||
_payloadEndIndex = _payloadStartIndex + copied - 1;
|
||||
}
|
||||
|
||||
// index points to payload unavailable
|
||||
} else if (index > _payloadEndIndex || _payloadStartIndex > index) {
|
||||
_payloadStartIndex = index;
|
||||
size_t copied = _getPayload(&_data[_payloadIndex], std::min(static_cast<size_t>(EMC_TX_BUFFER_SIZE), _size - _payloadStartIndex), index);
|
||||
_payloadEndIndex = _payloadStartIndex + copied - 1;
|
||||
}
|
||||
|
||||
// now index points to header or payload available
|
||||
return _payloadEndIndex - index + 1;
|
||||
// now index points to header or payload available
|
||||
return _payloadEndIndex - index + 1;
|
||||
}
|
||||
|
||||
const uint8_t* Packet::_chunkedData(size_t index) const {
|
||||
// CAUTION!! available(index) has to be called first to check available data and possibly fill payloadbuffer
|
||||
if (index < _payloadIndex) {
|
||||
return &_data[index];
|
||||
}
|
||||
return &_data[index - _payloadStartIndex + _payloadIndex];
|
||||
const uint8_t * Packet::_chunkedData(size_t index) const {
|
||||
// CAUTION!! available(index) has to be called first to check available data and possibly fill payloadbuffer
|
||||
if (index < _payloadIndex) {
|
||||
return &_data[index];
|
||||
}
|
||||
return &_data[index - _payloadStartIndex + _payloadIndex];
|
||||
}
|
||||
|
||||
} // end namespace espMqttClientInternals
|
||||
} // end namespace espMqttClientInternals
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include <APSettingsService.h>
|
||||
#include "APSettingsService.h"
|
||||
|
||||
#include "../../src/emsesp_stub.hpp"
|
||||
|
||||
@@ -9,8 +9,8 @@ APSettingsService::APSettingsService(AsyncWebServer * server, FS * fs, SecurityM
|
||||
, _lastManaged(0)
|
||||
, _reconfigureAp(false)
|
||||
, _connected(0) {
|
||||
addUpdateHandler([&](const String & originId) { reconfigureAP(); }, false);
|
||||
WiFi.onEvent(std::bind(&APSettingsService::WiFiEvent, this, _1));
|
||||
addUpdateHandler([this] { reconfigureAP(); }, false);
|
||||
WiFi.onEvent([this](WiFiEvent_t event, WiFiEventInfo_t info) { WiFiEvent(event); });
|
||||
}
|
||||
|
||||
void APSettingsService::begin() {
|
||||
@@ -53,7 +53,7 @@ void APSettingsService::reconfigureAP() {
|
||||
|
||||
void APSettingsService::loop() {
|
||||
unsigned long currentMillis = uuid::get_uptime();
|
||||
unsigned long manageElapsed = (uint32_t)(currentMillis - _lastManaged);
|
||||
unsigned long manageElapsed = static_cast<uint32_t>(currentMillis - _lastManaged);
|
||||
if (manageElapsed >= MANAGE_NETWORK_DELAY) {
|
||||
_lastManaged = currentMillis;
|
||||
manageAP();
|
||||
@@ -76,7 +76,7 @@ void APSettingsService::manageAP() {
|
||||
|
||||
void APSettingsService::startAP() {
|
||||
WiFi.softAPConfig(_state.localIP, _state.gatewayIP, _state.subnetMask);
|
||||
esp_wifi_set_bandwidth((wifi_interface_t)ESP_IF_WIFI_AP, WIFI_BW_HT20);
|
||||
esp_wifi_set_bandwidth(static_cast<wifi_interface_t>(ESP_IF_WIFI_AP), WIFI_BW_HT20);
|
||||
WiFi.softAP(_state.ssid.c_str(), _state.password.c_str(), _state.channel, _state.ssidHidden, _state.maxClients);
|
||||
#if CONFIG_IDF_TARGET_ESP32C3
|
||||
WiFi.setTxPower(WIFI_POWER_8_5dBm); // https://www.wemos.cc/en/latest/c3/c3_mini_1_0_0.html#about-wifi
|
||||
@@ -108,8 +108,54 @@ void APSettingsService::handleDNS() {
|
||||
APNetworkStatus APSettingsService::getAPNetworkStatus() {
|
||||
WiFiMode_t currentWiFiMode = WiFi.getMode();
|
||||
bool apActive = currentWiFiMode == WIFI_AP || currentWiFiMode == WIFI_AP_STA;
|
||||
|
||||
if (apActive && _state.provisionMode != AP_MODE_ALWAYS && WiFi.status() == WL_CONNECTED) {
|
||||
return APNetworkStatus::LINGERING;
|
||||
}
|
||||
|
||||
return apActive ? APNetworkStatus::ACTIVE : APNetworkStatus::INACTIVE;
|
||||
}
|
||||
|
||||
|
||||
void APSettings::read(const APSettings & settings, JsonObject root) {
|
||||
root["provision_mode"] = settings.provisionMode;
|
||||
root["ssid"] = settings.ssid;
|
||||
root["password"] = settings.password;
|
||||
root["channel"] = settings.channel;
|
||||
root["ssid_hidden"] = settings.ssidHidden;
|
||||
root["max_clients"] = settings.maxClients;
|
||||
root["local_ip"] = settings.localIP.toString();
|
||||
root["gateway_ip"] = settings.gatewayIP.toString();
|
||||
root["subnet_mask"] = settings.subnetMask.toString();
|
||||
}
|
||||
|
||||
StateUpdateResult APSettings::update(JsonObject root, APSettings & settings) {
|
||||
APSettings newSettings = {};
|
||||
newSettings.provisionMode = static_cast<uint8_t>(root["provision_mode"] | FACTORY_AP_PROVISION_MODE);
|
||||
|
||||
switch (settings.provisionMode) {
|
||||
case AP_MODE_ALWAYS:
|
||||
case AP_MODE_DISCONNECTED:
|
||||
case AP_MODE_NEVER:
|
||||
break;
|
||||
default:
|
||||
newSettings.provisionMode = AP_MODE_ALWAYS;
|
||||
}
|
||||
|
||||
newSettings.ssid = root["ssid"] | FACTORY_AP_SSID;
|
||||
newSettings.password = root["password"] | FACTORY_AP_PASSWORD;
|
||||
newSettings.channel = static_cast<uint8_t>(root["channel"] | FACTORY_AP_CHANNEL);
|
||||
newSettings.ssidHidden = root["ssid_hidden"] | FACTORY_AP_SSID_HIDDEN;
|
||||
newSettings.maxClients = static_cast<uint8_t>(root["max_clients"] | FACTORY_AP_MAX_CLIENTS);
|
||||
|
||||
JsonUtils::readIP(root, "local_ip", newSettings.localIP, FACTORY_AP_LOCAL_IP);
|
||||
JsonUtils::readIP(root, "gateway_ip", newSettings.gatewayIP, FACTORY_AP_GATEWAY_IP);
|
||||
JsonUtils::readIP(root, "subnet_mask", newSettings.subnetMask, FACTORY_AP_SUBNET_MASK);
|
||||
|
||||
if (newSettings == settings) {
|
||||
return StateUpdateResult::UNCHANGED;
|
||||
}
|
||||
|
||||
settings = newSettings;
|
||||
return StateUpdateResult::CHANGED;
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
#ifndef APSettingsConfig_h
|
||||
#define APSettingsConfig_h
|
||||
|
||||
#include <HttpEndpoint.h>
|
||||
#include <FSPersistence.h>
|
||||
#include <JsonUtils.h>
|
||||
#include "HttpEndpoint.h"
|
||||
#include "FSPersistence.h"
|
||||
#include "JsonUtils.h"
|
||||
|
||||
#include <DNSServer.h>
|
||||
#include <IPAddress.h>
|
||||
@@ -75,45 +75,8 @@ class APSettings {
|
||||
&& subnetMask == settings.subnetMask;
|
||||
}
|
||||
|
||||
static void read(APSettings & settings, JsonObject root) {
|
||||
root["provision_mode"] = settings.provisionMode;
|
||||
root["ssid"] = settings.ssid;
|
||||
root["password"] = settings.password;
|
||||
root["channel"] = settings.channel;
|
||||
root["ssid_hidden"] = settings.ssidHidden;
|
||||
root["max_clients"] = settings.maxClients;
|
||||
root["local_ip"] = settings.localIP.toString();
|
||||
root["gateway_ip"] = settings.gatewayIP.toString();
|
||||
root["subnet_mask"] = settings.subnetMask.toString();
|
||||
}
|
||||
|
||||
static StateUpdateResult update(JsonObject root, APSettings & settings) {
|
||||
APSettings newSettings = {};
|
||||
newSettings.provisionMode = root["provision_mode"] | FACTORY_AP_PROVISION_MODE;
|
||||
switch (settings.provisionMode) {
|
||||
case AP_MODE_ALWAYS:
|
||||
case AP_MODE_DISCONNECTED:
|
||||
case AP_MODE_NEVER:
|
||||
break;
|
||||
default:
|
||||
newSettings.provisionMode = AP_MODE_ALWAYS;
|
||||
}
|
||||
newSettings.ssid = root["ssid"] | FACTORY_AP_SSID;
|
||||
newSettings.password = root["password"] | FACTORY_AP_PASSWORD;
|
||||
newSettings.channel = root["channel"] | FACTORY_AP_CHANNEL;
|
||||
newSettings.ssidHidden = root["ssid_hidden"] | FACTORY_AP_SSID_HIDDEN;
|
||||
newSettings.maxClients = root["max_clients"] | FACTORY_AP_MAX_CLIENTS;
|
||||
|
||||
JsonUtils::readIP(root, "local_ip", newSettings.localIP, FACTORY_AP_LOCAL_IP);
|
||||
JsonUtils::readIP(root, "gateway_ip", newSettings.gatewayIP, FACTORY_AP_GATEWAY_IP);
|
||||
JsonUtils::readIP(root, "subnet_mask", newSettings.subnetMask, FACTORY_AP_SUBNET_MASK);
|
||||
|
||||
if (newSettings == settings) {
|
||||
return StateUpdateResult::UNCHANGED;
|
||||
}
|
||||
settings = newSettings;
|
||||
return StateUpdateResult::CHANGED;
|
||||
}
|
||||
static void read(const APSettings & settings, JsonObject root);
|
||||
static StateUpdateResult update(JsonObject root, APSettings & settings);
|
||||
};
|
||||
|
||||
class APSettingsService : public StatefulService<APSettings> {
|
||||
|
||||
@@ -1,17 +1,15 @@
|
||||
#include <APStatus.h>
|
||||
|
||||
using namespace std::placeholders; // for `_1` etc
|
||||
#include "APStatus.h"
|
||||
|
||||
APStatus::APStatus(AsyncWebServer * server, SecurityManager * securityManager, APSettingsService * apSettingsService)
|
||||
: _apSettingsService(apSettingsService) {
|
||||
server->on(AP_STATUS_SERVICE_PATH,
|
||||
HTTP_GET,
|
||||
securityManager->wrapRequest(std::bind(&APStatus::apStatus, this, _1), AuthenticationPredicates::IS_AUTHENTICATED));
|
||||
securityManager->wrapRequest([this](AsyncWebServerRequest * request) { apStatus(request); }, AuthenticationPredicates::IS_AUTHENTICATED));
|
||||
}
|
||||
|
||||
void APStatus::apStatus(AsyncWebServerRequest * request) {
|
||||
AsyncJsonResponse * response = new AsyncJsonResponse(false);
|
||||
JsonObject root = response->getRoot();
|
||||
auto * response = new AsyncJsonResponse(false);
|
||||
JsonObject root = response->getRoot();
|
||||
|
||||
root["status"] = _apSettingsService->getAPNetworkStatus();
|
||||
root["ip_address"] = WiFi.softAPIP().toString();
|
||||
|
||||
@@ -7,8 +7,9 @@
|
||||
#include <ArduinoJson.h>
|
||||
#include <ESPAsyncWebServer.h>
|
||||
#include <IPAddress.h>
|
||||
#include <SecurityManager.h>
|
||||
#include <APSettingsService.h>
|
||||
|
||||
#include "SecurityManager.h"
|
||||
#include "APSettingsService.h"
|
||||
|
||||
#define AP_STATUS_SERVICE_PATH "/rest/apStatus"
|
||||
|
||||
|
||||
@@ -1,47 +1,27 @@
|
||||
#include "ArduinoJsonJWT.h"
|
||||
|
||||
#include <array>
|
||||
|
||||
ArduinoJsonJWT::ArduinoJsonJWT(String secret)
|
||||
: _secret(secret) {
|
||||
: _secret(std::move(secret)) {
|
||||
}
|
||||
|
||||
void ArduinoJsonJWT::setSecret(String secret) {
|
||||
_secret = secret;
|
||||
_secret = std::move(secret);
|
||||
}
|
||||
|
||||
String ArduinoJsonJWT::getSecret() {
|
||||
return _secret;
|
||||
}
|
||||
|
||||
/*
|
||||
* ESP32 uses mbedtls, ESP2866 uses bearssl.
|
||||
*
|
||||
* Both come with decent HMAC implementations supporting sha256, as well as others.
|
||||
*
|
||||
* No need to pull in additional crypto libraries - lets use what we already have.
|
||||
*/
|
||||
String ArduinoJsonJWT::sign(String & payload) {
|
||||
unsigned char hmacResult[32];
|
||||
{
|
||||
mbedtls_md_context_t ctx;
|
||||
mbedtls_md_type_t md_type = MBEDTLS_MD_SHA256;
|
||||
mbedtls_md_init(&ctx);
|
||||
mbedtls_md_setup(&ctx, mbedtls_md_info_from_type(md_type), 1);
|
||||
mbedtls_md_hmac_starts(&ctx, (unsigned char *)_secret.c_str(), _secret.length());
|
||||
mbedtls_md_hmac_update(&ctx, (unsigned char *)payload.c_str(), payload.length());
|
||||
mbedtls_md_hmac_finish(&ctx, hmacResult);
|
||||
mbedtls_md_free(&ctx);
|
||||
}
|
||||
return encode((char *)hmacResult, 32);
|
||||
}
|
||||
|
||||
String ArduinoJsonJWT::buildJWT(JsonObject payload) {
|
||||
// serialize, then encode payload
|
||||
String jwt;
|
||||
serializeJson(payload, jwt);
|
||||
jwt = encode(jwt.c_str(), jwt.length());
|
||||
jwt = encode(jwt.c_str(), static_cast<int>(jwt.length()));
|
||||
|
||||
// add the header to payload
|
||||
jwt = JWT_HEADER + '.' + jwt;
|
||||
jwt = getJWTHeader() + '.' + jwt;
|
||||
|
||||
// add signature
|
||||
jwt += '.' + sign(jwt);
|
||||
@@ -53,65 +33,88 @@ void ArduinoJsonJWT::parseJWT(String jwt, JsonDocument & jsonDocument) {
|
||||
// clear json document before we begin, jsonDocument wil be null on failure
|
||||
jsonDocument.clear();
|
||||
|
||||
const String & jwt_header = getJWTHeader();
|
||||
const unsigned int jwt_header_size = jwt_header.length();
|
||||
|
||||
// must have the correct header and delimiter
|
||||
if (!jwt.startsWith(JWT_HEADER) || jwt.indexOf('.') != JWT_HEADER_SIZE) {
|
||||
if (!jwt.startsWith(jwt_header) || jwt.indexOf('.') != static_cast<int>(jwt_header_size)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// check there is a signature delimieter
|
||||
int signatureDelimiterIndex = jwt.lastIndexOf('.');
|
||||
if (signatureDelimiterIndex == JWT_HEADER_SIZE) {
|
||||
const int signatureDelimiterIndex = jwt.lastIndexOf('.');
|
||||
if (signatureDelimiterIndex == static_cast<int>(jwt_header_size)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// check the signature is valid
|
||||
String signature = jwt.substring(signatureDelimiterIndex + 1);
|
||||
jwt = jwt.substring(0, signatureDelimiterIndex);
|
||||
const String signature = jwt.substring(static_cast<unsigned int>(signatureDelimiterIndex) + 1);
|
||||
jwt = jwt.substring(0, static_cast<unsigned int>(signatureDelimiterIndex));
|
||||
if (sign(jwt) != signature) {
|
||||
return;
|
||||
}
|
||||
|
||||
// decode payload
|
||||
jwt = jwt.substring(JWT_HEADER_SIZE + 1);
|
||||
jwt = jwt.substring(jwt_header_size + 1);
|
||||
jwt = decode(jwt);
|
||||
|
||||
// parse payload, clearing json document after failure
|
||||
DeserializationError error = deserializeJson(jsonDocument, jwt);
|
||||
const DeserializationError error = deserializeJson(jsonDocument, jwt);
|
||||
if (error != DeserializationError::Ok || !jsonDocument.is<JsonObject>()) {
|
||||
jsonDocument.clear();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* ESP32 uses mbedtls, ESP2866 uses bearssl.
|
||||
*
|
||||
* Both come with decent HMAC implementations supporting sha256, as well as others.
|
||||
*
|
||||
* No need to pull in additional crypto libraries - lets use what we already have.
|
||||
*/
|
||||
String ArduinoJsonJWT::sign(String & payload) {
|
||||
std::array<unsigned char, 32> hmacResult{};
|
||||
{
|
||||
mbedtls_md_context_t ctx;
|
||||
mbedtls_md_type_t md_type = MBEDTLS_MD_SHA256;
|
||||
mbedtls_md_init(&ctx);
|
||||
mbedtls_md_setup(&ctx, mbedtls_md_info_from_type(md_type), 1);
|
||||
mbedtls_md_hmac_starts(&ctx, reinterpret_cast<const unsigned char *>(_secret.c_str()), _secret.length());
|
||||
mbedtls_md_hmac_update(&ctx, reinterpret_cast<const unsigned char *>(payload.c_str()), payload.length());
|
||||
mbedtls_md_hmac_finish(&ctx, hmacResult.data());
|
||||
mbedtls_md_free(&ctx);
|
||||
}
|
||||
return encode(reinterpret_cast<const char *>(hmacResult.data()), hmacResult.size());
|
||||
}
|
||||
|
||||
String ArduinoJsonJWT::encode(const char * cstr, int inputLen) {
|
||||
// prepare encoder
|
||||
base64_encodestate _state;
|
||||
base64_init_encodestate(&_state);
|
||||
size_t encodedLength = base64_encode_expected_len(inputLen) + 1;
|
||||
// prepare buffer of correct length, returning an empty string on failure
|
||||
char * buffer = (char *)malloc(encodedLength * sizeof(char));
|
||||
if (buffer == nullptr) {
|
||||
return "";
|
||||
}
|
||||
|
||||
// prepare buffer of correct length
|
||||
const auto bufferLength = static_cast<std::size_t>(base64_encode_expected_len(inputLen)) + 1;
|
||||
auto * buffer = new char[bufferLength];
|
||||
|
||||
// encode to buffer
|
||||
int len = base64_encode_block(cstr, inputLen, &buffer[0], &_state);
|
||||
len += base64_encode_blockend(&buffer[len], &_state);
|
||||
buffer[len] = 0;
|
||||
buffer[len] = '\0';
|
||||
|
||||
// convert to arduino string, freeing buffer
|
||||
String value = String(buffer);
|
||||
free(buffer);
|
||||
auto result = String(buffer);
|
||||
delete[] buffer;
|
||||
buffer = nullptr;
|
||||
|
||||
// remove padding and convert to URL safe form
|
||||
while (value.length() > 0 && value.charAt(value.length() - 1) == '=') {
|
||||
value.remove(value.length() - 1);
|
||||
while (result.length() > 0 && result.charAt(result.length() - 1) == '=') {
|
||||
result.remove(result.length() - 1);
|
||||
}
|
||||
value.replace('+', '-');
|
||||
value.replace('/', '_');
|
||||
result.replace('+', '-');
|
||||
result.replace('/', '_');
|
||||
|
||||
// return as string
|
||||
return value;
|
||||
return result;
|
||||
}
|
||||
|
||||
String ArduinoJsonJWT::decode(String value) {
|
||||
@@ -120,12 +123,18 @@ String ArduinoJsonJWT::decode(String value) {
|
||||
value.replace('_', '/');
|
||||
|
||||
// prepare buffer of correct length
|
||||
char buffer[base64_decode_expected_len(value.length()) + 1];
|
||||
const auto bufferLength = static_cast<std::size_t>(base64_decode_expected_len(value.length()) + 1);
|
||||
auto * buffer = new char[bufferLength];
|
||||
|
||||
// decode
|
||||
int len = base64_decode_chars(value.c_str(), value.length(), &buffer[0]);
|
||||
buffer[len] = 0;
|
||||
const int len = base64_decode_chars(value.c_str(), static_cast<int>(value.length()), &buffer[0]);
|
||||
buffer[len] = '\0';
|
||||
|
||||
// convert to arduino string, freeing buffer
|
||||
auto result = String(buffer);
|
||||
delete[] buffer;
|
||||
buffer = nullptr;
|
||||
|
||||
// return as string
|
||||
return String(buffer);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -3,30 +3,33 @@
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <ArduinoJson.h>
|
||||
|
||||
#include <libb64/cdecode.h>
|
||||
#include <libb64/cencode.h>
|
||||
#include <mbedtls/md.h>
|
||||
|
||||
class ArduinoJsonJWT {
|
||||
private:
|
||||
String _secret;
|
||||
|
||||
const String JWT_HEADER = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9";
|
||||
const int JWT_HEADER_SIZE = JWT_HEADER.length();
|
||||
|
||||
String sign(String & value);
|
||||
|
||||
static String encode(const char * cstr, int len);
|
||||
static String decode(String value);
|
||||
|
||||
public:
|
||||
ArduinoJsonJWT(String secret);
|
||||
explicit ArduinoJsonJWT(String secret);
|
||||
|
||||
void setSecret(String secret);
|
||||
String getSecret();
|
||||
|
||||
String buildJWT(JsonObject payload);
|
||||
void parseJWT(String jwt, JsonDocument & jsonDocument);
|
||||
|
||||
private:
|
||||
String _secret;
|
||||
|
||||
String sign(String & value);
|
||||
|
||||
static String encode(const char * cstr, int len);
|
||||
static String decode(String value);
|
||||
|
||||
static const String & getJWTHeader() {
|
||||
static const String JWT_HEADER = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9";
|
||||
return JWT_HEADER;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif
|
||||
@@ -1,11 +1,9 @@
|
||||
#include <AuthenticationService.h>
|
||||
|
||||
using namespace std::placeholders; // for `_1` etc
|
||||
#include "AuthenticationService.h"
|
||||
|
||||
AuthenticationService::AuthenticationService(AsyncWebServer * server, SecurityManager * securityManager)
|
||||
: _securityManager(securityManager)
|
||||
, _signInHandler(SIGN_IN_PATH, std::bind(&AuthenticationService::signIn, this, _1, _2)) {
|
||||
server->on(VERIFY_AUTHORIZATION_PATH, HTTP_GET, std::bind(&AuthenticationService::verifyAuthorization, this, _1));
|
||||
, _signInHandler(SIGN_IN_PATH, [this](AsyncWebServerRequest * request, JsonVariant json) { signIn(request, json); }) {
|
||||
server->on(VERIFY_AUTHORIZATION_PATH, HTTP_GET, [this](AsyncWebServerRequest * request) { verifyAuthorization(request); });
|
||||
_signInHandler.setMethod(HTTP_POST);
|
||||
_signInHandler.setMaxContentLength(MAX_AUTHENTICATION_SIZE);
|
||||
server->addHandler(&_signInHandler);
|
||||
@@ -29,10 +27,10 @@ void AuthenticationService::signIn(AsyncWebServerRequest * request, JsonVariant
|
||||
String password = json["password"];
|
||||
Authentication authentication = _securityManager->authenticate(username, password);
|
||||
if (authentication.authenticated) {
|
||||
User * user = authentication.user;
|
||||
AsyncJsonResponse * response = new AsyncJsonResponse(false);
|
||||
JsonObject jsonObject = response->getRoot();
|
||||
jsonObject["access_token"] = _securityManager->generateJWT(user);
|
||||
User * user = authentication.user;
|
||||
auto * response = new AsyncJsonResponse(false);
|
||||
JsonObject jsonObject = response->getRoot();
|
||||
jsonObject["access_token"] = _securityManager->generateJWT(user);
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
return;
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
#ifndef AuthenticationService_H_
|
||||
#define AuthenticationService_H_
|
||||
|
||||
#include <Features.h>
|
||||
#include "Features.h"
|
||||
#include "SecurityManager.h"
|
||||
|
||||
#include <ESPAsyncWebServer.h>
|
||||
#include <SecurityManager.h>
|
||||
|
||||
#define VERIFY_AUTHORIZATION_PATH "/rest/verifyAuthorization"
|
||||
#define SIGN_IN_PATH "/rest/signIn"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#include <ESP8266React.h>
|
||||
#include "ESP8266React.h"
|
||||
|
||||
#include <WWWData.h>
|
||||
#include "WWWData.h"
|
||||
|
||||
ESP8266React::ESP8266React(AsyncWebServer * server, FS * fs)
|
||||
: _securitySettingsService(server, fs)
|
||||
@@ -19,15 +19,33 @@ ESP8266React::ESP8266React(AsyncWebServer * server, FS * fs)
|
||||
, _restartService(server, &_securitySettingsService)
|
||||
, _factoryResetService(server, fs, &_securitySettingsService)
|
||||
, _systemStatus(server, &_securitySettingsService) {
|
||||
// Serve static resources
|
||||
WWWData::registerRoutes([server, this](const String & uri, const String & contentType, const uint8_t * content, size_t len) {
|
||||
ArRequestHandlerFunction requestHandler = [contentType, content, len](AsyncWebServerRequest * request) {
|
||||
//
|
||||
// Serve static web resources
|
||||
//
|
||||
|
||||
// Populate the last modification date based on build datetime
|
||||
static char last_modified[50];
|
||||
sprintf(last_modified, "%s %s CET", __DATE__, __TIME__);
|
||||
|
||||
WWWData::registerRoutes([server](const String & uri, const String & contentType, const uint8_t * content, size_t len, const String & hash) {
|
||||
ArRequestHandlerFunction requestHandler = [contentType, content, len, hash](AsyncWebServerRequest * request) {
|
||||
// Check if the client already has the same version and respond with a 304 (Not modified)
|
||||
if (request->header("If-Modified-Since").indexOf(last_modified) > 0) {
|
||||
return request->send(304);
|
||||
} else if (request->header("If-None-Match").equals(hash)) {
|
||||
return request->send(304);
|
||||
}
|
||||
|
||||
AsyncWebServerResponse * response = request->beginResponse_P(200, contentType, content, len);
|
||||
response->addHeader("Content-Encoding", "gzip");
|
||||
response->addHeader("Cache-Control", "public, immutable, max-age=31536000");
|
||||
// response->addHeader("Content-Encoding", "br"); // only works over HTTPS
|
||||
// response->addHeader("Cache-Control", "public, immutable, max-age=31536000");
|
||||
response->addHeader("Last-Modified", last_modified);
|
||||
response->addHeader("ETag", hash);
|
||||
|
||||
request->send(response);
|
||||
};
|
||||
|
||||
server->on(uri.c_str(), HTTP_GET, requestHandler);
|
||||
// Serving non matching get requests with "/index.html"
|
||||
// OPTIONS get a straight up 200 response
|
||||
@@ -48,12 +66,13 @@ ESP8266React::ESP8266React(AsyncWebServer * server, FS * fs)
|
||||
void ESP8266React::begin() {
|
||||
_networkSettingsService.begin();
|
||||
_networkSettingsService.read([&](NetworkSettings & networkSettings) {
|
||||
DefaultHeaders & defaultHeaders = DefaultHeaders::Instance();
|
||||
if (networkSettings.enableCORS) {
|
||||
DefaultHeaders::Instance().addHeader("Access-Control-Allow-Origin", networkSettings.CORSOrigin);
|
||||
DefaultHeaders::Instance().addHeader("Access-Control-Allow-Headers", "Accept, Content-Type, Authorization");
|
||||
DefaultHeaders::Instance().addHeader("Access-Control-Allow-Credentials", "true");
|
||||
defaultHeaders.addHeader("Access-Control-Allow-Origin", networkSettings.CORSOrigin);
|
||||
defaultHeaders.addHeader("Access-Control-Allow-Headers", "Accept, Content-Type, Authorization");
|
||||
defaultHeaders.addHeader("Access-Control-Allow-Credentials", "true");
|
||||
}
|
||||
DefaultHeaders::Instance().addHeader("Server", networkSettings.hostname);
|
||||
defaultHeaders.addHeader("Server", networkSettings.hostname);
|
||||
});
|
||||
_apSettingsService.begin();
|
||||
_ntpSettingsService.begin();
|
||||
|
||||
@@ -1,28 +1,27 @@
|
||||
#ifndef ESP8266React_h
|
||||
#define ESP8266React_h
|
||||
|
||||
#include <Arduino.h>
|
||||
#include "APSettingsService.h"
|
||||
#include "APStatus.h"
|
||||
#include "AuthenticationService.h"
|
||||
#include "FactoryResetService.h"
|
||||
#include "MqttSettingsService.h"
|
||||
#include "MqttStatus.h"
|
||||
#include "NTPSettingsService.h"
|
||||
#include "NTPStatus.h"
|
||||
#include "OTASettingsService.h"
|
||||
#include "UploadFileService.h"
|
||||
#include "RestartService.h"
|
||||
#include "SecuritySettingsService.h"
|
||||
#include "SystemStatus.h"
|
||||
#include "WiFiScanner.h"
|
||||
#include "NetworkSettingsService.h"
|
||||
#include "NetworkStatus.h"
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <AsyncTCP.h>
|
||||
#include <WiFi.h>
|
||||
|
||||
#include <APSettingsService.h>
|
||||
#include <APStatus.h>
|
||||
#include <AuthenticationService.h>
|
||||
#include <FactoryResetService.h>
|
||||
#include <MqttSettingsService.h>
|
||||
#include <MqttStatus.h>
|
||||
#include <NTPSettingsService.h>
|
||||
#include <NTPStatus.h>
|
||||
#include <OTASettingsService.h>
|
||||
#include <UploadFileService.h>
|
||||
#include <RestartService.h>
|
||||
#include <SecuritySettingsService.h>
|
||||
#include <SystemStatus.h>
|
||||
#include <WiFiScanner.h>
|
||||
#include <NetworkSettingsService.h>
|
||||
#include <NetworkStatus.h>
|
||||
|
||||
class ESP8266React {
|
||||
public:
|
||||
ESP8266React(AsyncWebServer * server, FS * fs);
|
||||
@@ -66,9 +65,11 @@ class ESP8266React {
|
||||
_mqttSettingsService.setWill(will_topic);
|
||||
}
|
||||
|
||||
#ifndef EMSESP_STANDALONE
|
||||
void factoryReset() {
|
||||
_factoryResetService.factoryReset();
|
||||
}
|
||||
#endif
|
||||
|
||||
private:
|
||||
SecuritySettingsService _securitySettingsService;
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
#ifndef ESPUtils_h
|
||||
#define ESPUtils_h
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
class ESPUtils {
|
||||
public:
|
||||
static String defaultDeviceValue(String prefix = "") {
|
||||
return prefix + String((uint32_t)ESP.getEfuseMac(), HEX);
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,8 +1,8 @@
|
||||
#ifndef FSPersistence_h
|
||||
#define FSPersistence_h
|
||||
|
||||
#include <StatefulService.h>
|
||||
#include <FS.h>
|
||||
#include "StatefulService.h"
|
||||
#include "FS.h"
|
||||
|
||||
template <class T>
|
||||
class FSPersistence {
|
||||
@@ -47,8 +47,8 @@ class FSPersistence {
|
||||
// make directories if required, for new IDF4.2 & LittleFS
|
||||
String path(_filePath);
|
||||
int index = 0;
|
||||
while ((index = path.indexOf('/', index + 1)) != -1) {
|
||||
String segment = path.substring(0, index);
|
||||
while ((index = path.indexOf('/', static_cast<unsigned int>(index) + 1)) != -1) {
|
||||
String segment = path.substring(0, static_cast<unsigned int>(index));
|
||||
if (!_fs->exists(segment)) {
|
||||
_fs->mkdir(segment);
|
||||
}
|
||||
@@ -80,7 +80,7 @@ class FSPersistence {
|
||||
|
||||
void enableUpdateHandler() {
|
||||
if (!_updateHandlerId) {
|
||||
_updateHandlerId = _statefulService->addUpdateHandler([&](const String & originId) { writeToFS(); });
|
||||
_updateHandlerId = _statefulService->addUpdateHandler([&] { writeToFS(); });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,16 +1,14 @@
|
||||
#include <FactoryResetService.h>
|
||||
|
||||
using namespace std::placeholders;
|
||||
#include "FactoryResetService.h"
|
||||
|
||||
FactoryResetService::FactoryResetService(AsyncWebServer * server, FS * fs, SecurityManager * securityManager)
|
||||
: fs(fs) {
|
||||
server->on(FACTORY_RESET_SERVICE_PATH,
|
||||
HTTP_POST,
|
||||
securityManager->wrapRequest(std::bind(&FactoryResetService::handleRequest, this, _1), AuthenticationPredicates::IS_ADMIN));
|
||||
securityManager->wrapRequest([this](AsyncWebServerRequest * request) { handleRequest(request); }, AuthenticationPredicates::IS_ADMIN));
|
||||
}
|
||||
|
||||
void FactoryResetService::handleRequest(AsyncWebServerRequest * request) {
|
||||
request->onDisconnect(std::bind(&FactoryResetService::factoryReset, this));
|
||||
request->onDisconnect([this] { factoryReset(); });
|
||||
request->send(200);
|
||||
}
|
||||
|
||||
@@ -21,7 +19,7 @@ void FactoryResetService::factoryReset() {
|
||||
// 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()) {
|
||||
while ((file = root.openNextFile())) {
|
||||
String path = file.path();
|
||||
file.close();
|
||||
fs->remove(path);
|
||||
|
||||
@@ -3,22 +3,22 @@
|
||||
|
||||
#include <WiFi.h>
|
||||
#include <ESPAsyncWebServer.h>
|
||||
#include <SecurityManager.h>
|
||||
#include <RestartService.h>
|
||||
#include <FS.h>
|
||||
|
||||
#include "SecurityManager.h"
|
||||
#include "RestartService.h"
|
||||
|
||||
#define FS_CONFIG_DIRECTORY "/config"
|
||||
#define FACTORY_RESET_SERVICE_PATH "/rest/factoryReset"
|
||||
|
||||
class FactoryResetService {
|
||||
FS * fs;
|
||||
|
||||
public:
|
||||
FactoryResetService(AsyncWebServer * server, FS * fs, SecurityManager * securityManager);
|
||||
|
||||
void factoryReset();
|
||||
|
||||
private:
|
||||
FS * fs;
|
||||
void handleRequest(AsyncWebServerRequest * request);
|
||||
};
|
||||
|
||||
|
||||
@@ -2,15 +2,13 @@
|
||||
#define HttpEndpoint_h
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include <ESPAsyncWebServer.h>
|
||||
|
||||
#include <SecurityManager.h>
|
||||
#include <StatefulService.h>
|
||||
#include "SecurityManager.h"
|
||||
#include "StatefulService.h"
|
||||
|
||||
#define HTTP_ENDPOINT_ORIGIN_ID "http"
|
||||
|
||||
using namespace std::placeholders; // for `_1` etc
|
||||
#define HTTPS_ENDPOINT_ORIGIN_ID "https"
|
||||
|
||||
template <class T>
|
||||
class HttpEndpoint {
|
||||
@@ -19,8 +17,7 @@ class HttpEndpoint {
|
||||
JsonStateUpdater<T> _stateUpdater;
|
||||
StatefulService<T> * _statefulService;
|
||||
|
||||
AsyncCallbackWebHandler * GEThandler;
|
||||
AsyncCallbackJsonWebHandler * POSThandler;
|
||||
AsyncCallbackJsonWebHandler * handler;
|
||||
|
||||
public:
|
||||
HttpEndpoint(JsonStateReader<T> stateReader,
|
||||
@@ -33,12 +30,12 @@ class HttpEndpoint {
|
||||
: _stateReader(stateReader)
|
||||
, _stateUpdater(stateUpdater)
|
||||
, _statefulService(statefulService) {
|
||||
// Create the GET and POST endpoints
|
||||
POSThandler = new AsyncCallbackJsonWebHandler(servicePath,
|
||||
securityManager->wrapCallback([this](AsyncWebServerRequest * request,
|
||||
JsonVariant json) { handleRequest(request, json); },
|
||||
authenticationPredicate));
|
||||
server->addHandler(POSThandler);
|
||||
// Create hander for both GET and POST endpoints
|
||||
handler = new AsyncCallbackJsonWebHandler(servicePath,
|
||||
securityManager->wrapCallback([this](AsyncWebServerRequest * request,
|
||||
JsonVariant json) { handleRequest(request, json); },
|
||||
authenticationPredicate));
|
||||
server->addHandler(handler);
|
||||
}
|
||||
|
||||
protected:
|
||||
@@ -56,16 +53,18 @@ class HttpEndpoint {
|
||||
if (outcome == StateUpdateResult::ERROR) {
|
||||
request->send(400); // error
|
||||
return;
|
||||
} else if (outcome == StateUpdateResult::CHANGED_RESTART) {
|
||||
request->send(205); // reboot required
|
||||
return;
|
||||
} else if (outcome == StateUpdateResult::CHANGED) {
|
||||
request->onDisconnect([this]() { _statefulService->callUpdateHandlers(HTTP_ENDPOINT_ORIGIN_ID); });
|
||||
} else if (outcome == StateUpdateResult::CHANGED || outcome == StateUpdateResult::CHANGED_RESTART) {
|
||||
// persist changes
|
||||
request->onDisconnect([this] { _statefulService->callUpdateHandlers(); });
|
||||
if (outcome == StateUpdateResult::CHANGED_RESTART) {
|
||||
request->send(205); // reboot required
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AsyncJsonResponse * response = new AsyncJsonResponse(false);
|
||||
JsonObject jsonObject = response->getRoot().to<JsonObject>();
|
||||
auto * response = new AsyncJsonResponse(false);
|
||||
JsonObject jsonObject = response->getRoot().to<JsonObject>();
|
||||
_statefulService->read(jsonObject, _stateReader);
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
|
||||
@@ -3,16 +3,20 @@
|
||||
|
||||
#include <IPAddress.h>
|
||||
|
||||
const IPAddress IP_NOT_SET = IPAddress(INADDR_NONE);
|
||||
|
||||
class IPUtils {
|
||||
public:
|
||||
static bool isSet(const IPAddress & ip) {
|
||||
return ip != IP_NOT_SET;
|
||||
return ip != getNotSetIP();
|
||||
}
|
||||
static bool isNotSet(const IPAddress & ip) {
|
||||
return ip == IP_NOT_SET;
|
||||
return ip == getNotSetIP();
|
||||
}
|
||||
|
||||
private:
|
||||
static const IPAddress & getNotSetIP() {
|
||||
static const IPAddress IP_NOT_SET = IPAddress(INADDR_NONE);
|
||||
return IP_NOT_SET;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif
|
||||
@@ -2,9 +2,10 @@
|
||||
#define JsonUtils_h
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <IPAddress.h>
|
||||
#include <IPUtils.h>
|
||||
#include <ArduinoJson.h>
|
||||
#include <IPAddress.h>
|
||||
|
||||
#include "IPUtils.h"
|
||||
|
||||
class JsonUtils {
|
||||
public:
|
||||
|
||||
@@ -1,45 +1,27 @@
|
||||
#include <MqttSettingsService.h>
|
||||
|
||||
#include "MqttSettingsService.h"
|
||||
#include "../../src/emsesp_stub.hpp"
|
||||
|
||||
using namespace std::placeholders; // for `_1` etc
|
||||
|
||||
/**
|
||||
* Retains a copy of the cstr provided in the pointer provided using dynamic allocation.
|
||||
*
|
||||
* Frees the pointer before allocation and leaves it as nullptr if cstr == nullptr.
|
||||
*/
|
||||
static char * retainCstr(const char * cstr, char ** ptr) {
|
||||
// free up previously retained value if exists
|
||||
free(*ptr);
|
||||
*ptr = nullptr;
|
||||
|
||||
// dynamically allocate and copy cstr (if non null)
|
||||
if (cstr != nullptr) {
|
||||
*ptr = (char *)malloc(strlen(cstr) + 1);
|
||||
strcpy(*ptr, cstr);
|
||||
}
|
||||
|
||||
// return reference to pointer for convenience
|
||||
return *ptr;
|
||||
}
|
||||
|
||||
MqttSettingsService::MqttSettingsService(AsyncWebServer * server, FS * fs, SecurityManager * securityManager)
|
||||
: _httpEndpoint(MqttSettings::read, MqttSettings::update, this, server, MQTT_SETTINGS_SERVICE_PATH, securityManager)
|
||||
, _fsPersistence(MqttSettings::read, MqttSettings::update, this, fs, MQTT_SETTINGS_FILE)
|
||||
, _retainedHost(nullptr)
|
||||
, _retainedClientId(nullptr)
|
||||
, _retainedUsername(nullptr)
|
||||
, _retainedPassword(nullptr)
|
||||
, _reconfigureMqtt(false)
|
||||
, _disconnectedAt(0)
|
||||
, _disconnectReason(espMqttClientTypes::DisconnectReason::TCP_DISCONNECTED)
|
||||
, _mqttClient(nullptr) {
|
||||
WiFi.onEvent(std::bind(&MqttSettingsService::WiFiEvent, this, _1, _2));
|
||||
addUpdateHandler([&](const String & originId) { onConfigUpdated(); }, false);
|
||||
WiFi.onEvent([this](WiFiEvent_t event, WiFiEventInfo_t info) { WiFiEvent(event); });
|
||||
addUpdateHandler([this] { onConfigUpdated(); }, false);
|
||||
}
|
||||
|
||||
static String generateClientId() {
|
||||
#ifdef EMSESP_STANDALONE
|
||||
return "ems-esp";
|
||||
#else
|
||||
return "esp32-" + String(static_cast<uint32_t>(ESP.getEfuseMac()), HEX);
|
||||
#endif
|
||||
}
|
||||
|
||||
MqttSettingsService::~MqttSettingsService() {
|
||||
delete _mqttClient;
|
||||
}
|
||||
|
||||
void MqttSettingsService::begin() {
|
||||
@@ -55,32 +37,41 @@ void MqttSettingsService::startClient() {
|
||||
return;
|
||||
}
|
||||
delete _mqttClient;
|
||||
_mqttClient = nullptr;
|
||||
}
|
||||
#if CONFIG_IDF_TARGET_ESP32S3
|
||||
if (_state.enableTLS) {
|
||||
isSecure = true;
|
||||
_mqttClient = static_cast<MqttClient *>(new espMqttClientSecure(espMqttClientTypes::UseInternalTask::NO));
|
||||
_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)->setCACert(certificate.c_str());
|
||||
}
|
||||
static_cast<espMqttClientSecure *>(_mqttClient)->onConnect(std::bind(&MqttSettingsService::onMqttConnect, this, _1));
|
||||
static_cast<espMqttClientSecure *>(_mqttClient)->onDisconnect(std::bind(&MqttSettingsService::onMqttDisconnect, this, _1));
|
||||
static_cast<espMqttClientSecure *>(_mqttClient)->onMessage(std::bind(&MqttSettingsService::onMqttMessage, this, _1, _2, _3, _4, _5, _6));
|
||||
static_cast<espMqttClientSecure *>(_mqttClient)->onConnect([this](bool sessionPresent) { onMqttConnect(sessionPresent); });
|
||||
static_cast<espMqttClientSecure *>(_mqttClient)->onDisconnect([this](espMqttClientTypes::DisconnectReason reason) { onMqttDisconnect(reason); });
|
||||
static_cast<espMqttClientSecure *>(_mqttClient)
|
||||
->onMessage(
|
||||
[this](const espMqttClientTypes::MessageProperties & properties, const char * topic, const uint8_t * payload, size_t len, size_t index, size_t total) {
|
||||
onMqttMessage(properties, topic, payload, len, index, total);
|
||||
});
|
||||
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));
|
||||
static_cast<espMqttClient *>(_mqttClient)->onMessage(std::bind(&MqttSettingsService::onMqttMessage, this, _1, _2, _3, _4, _5, _6));
|
||||
_mqttClient = new espMqttClient(espMqttClientTypes::UseInternalTask::NO);
|
||||
static_cast<espMqttClient *>(_mqttClient)->onConnect([this](bool sessionPresent) { onMqttConnect(sessionPresent); });
|
||||
static_cast<espMqttClient *>(_mqttClient)->onDisconnect([this](espMqttClientTypes::DisconnectReason reason) { onMqttDisconnect(reason); });
|
||||
static_cast<espMqttClient *>(_mqttClient)
|
||||
->onMessage(
|
||||
[this](const espMqttClientTypes::MessageProperties & properties, const char * topic, const uint8_t * payload, size_t len, size_t index, size_t total) {
|
||||
onMqttMessage(properties, topic, payload, len, index, total);
|
||||
});
|
||||
}
|
||||
|
||||
void MqttSettingsService::loop() {
|
||||
if (_reconfigureMqtt || (_disconnectedAt && (uint32_t)(uuid::get_uptime() - _disconnectedAt) >= MQTT_RECONNECTION_DELAY)) {
|
||||
if (_reconfigureMqtt || (_disconnectedAt && static_cast<uint32_t>(uuid::get_uptime() - _disconnectedAt) >= MQTT_RECONNECTION_DELAY)) {
|
||||
// reconfigure MQTT client
|
||||
_disconnectedAt = configureMqtt() ? 0 : uuid::get_uptime();
|
||||
_reconfigureMqtt = false;
|
||||
@@ -116,6 +107,9 @@ void MqttSettingsService::onMqttMessage(const espMqttClientTypes::MessagePropert
|
||||
size_t len,
|
||||
size_t index,
|
||||
size_t total) {
|
||||
(void)properties;
|
||||
(void)index;
|
||||
(void)total;
|
||||
emsesp::EMSESP::mqtt_.on_message(topic, payload, len);
|
||||
}
|
||||
|
||||
@@ -128,9 +122,12 @@ MqttClient * MqttSettingsService::getMqttClient() {
|
||||
}
|
||||
|
||||
void MqttSettingsService::onMqttConnect(bool sessionPresent) {
|
||||
(void)sessionPresent;
|
||||
// _disconnectedAt = 0;
|
||||
emsesp::EMSESP::mqtt_.on_connect();
|
||||
// emsesp::EMSESP::logger().info("Connected to MQTT, %s", (sessionPresent) ? ("with persistent session") : ("without persistent session"));
|
||||
#ifdef EMSESP_DEBUG
|
||||
emsesp::EMSESP::logger().debug("Connected to MQTT, %s", (sessionPresent) ? ("with persistent session") : ("without persistent session"));
|
||||
#endif
|
||||
}
|
||||
|
||||
void MqttSettingsService::onMqttDisconnect(espMqttClientTypes::DisconnectReason reason) {
|
||||
@@ -149,7 +146,7 @@ void MqttSettingsService::onConfigUpdated() {
|
||||
emsesp::EMSESP::mqtt_.start(); // reload EMS-ESP MQTT settings
|
||||
}
|
||||
|
||||
void MqttSettingsService::WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info) {
|
||||
void MqttSettingsService::WiFiEvent(WiFiEvent_t event) {
|
||||
switch (event) {
|
||||
case ARDUINO_EVENT_WIFI_STA_GOT_IP:
|
||||
case ARDUINO_EVENT_ETH_GOT_IP:
|
||||
@@ -183,31 +180,25 @@ bool MqttSettingsService::configureMqtt() {
|
||||
_reconfigureMqtt = false;
|
||||
#if CONFIG_IDF_TARGET_ESP32S3
|
||||
if (_state.enableTLS) {
|
||||
// emsesp::EMSESP::logger().info("Start secure MQTT with rootCA");
|
||||
static_cast<espMqttClientSecure *>(_mqttClient)->setServer(retainCstr(_state.host.c_str(), &_retainedHost), _state.port);
|
||||
#if EMSESP_DEBUG
|
||||
emsesp::EMSESP::logger().debug("Start secure MQTT with rootCA");
|
||||
#endif
|
||||
static_cast<espMqttClientSecure *>(_mqttClient)->setServer(_state.host.c_str(), _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));
|
||||
->setCredentials(_state.username.c_str(), _state.password.length() > 0 ? _state.password.c_str() : nullptr);
|
||||
}
|
||||
static_cast<espMqttClientSecure *>(_mqttClient)->setClientId(retainCstr(_state.clientId.c_str(), &_retainedClientId));
|
||||
static_cast<espMqttClientSecure *>(_mqttClient)->setClientId(_state.clientId.c_str());
|
||||
static_cast<espMqttClientSecure *>(_mqttClient)->setKeepAlive(_state.keepAlive);
|
||||
static_cast<espMqttClientSecure *>(_mqttClient)->setCleanSession(_state.cleanSession);
|
||||
return _mqttClient->connect();
|
||||
}
|
||||
#endif
|
||||
// emsesp::EMSESP::logger().info("Configuring MQTT client");
|
||||
static_cast<espMqttClient *>(_mqttClient)->setServer(retainCstr(_state.host.c_str(), &_retainedHost), _state.port);
|
||||
static_cast<espMqttClient *>(_mqttClient)->setServer(_state.host.c_str(), _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)->setCredentials(_state.username.c_str(), _state.password.length() > 0 ? _state.password.c_str() : nullptr);
|
||||
}
|
||||
static_cast<espMqttClient *>(_mqttClient)->setClientId(retainCstr(_state.clientId.c_str(), &_retainedClientId));
|
||||
static_cast<espMqttClient *>(_mqttClient)->setClientId(_state.clientId.c_str());
|
||||
static_cast<espMqttClient *>(_mqttClient)->setKeepAlive(_state.keepAlive);
|
||||
static_cast<espMqttClient *>(_mqttClient)->setCleanSession(_state.cleanSession);
|
||||
return _mqttClient->connect();
|
||||
@@ -265,33 +256,33 @@ StateUpdateResult MqttSettings::update(JsonObject root, MqttSettings & settings)
|
||||
#endif
|
||||
newSettings.enabled = root["enabled"] | FACTORY_MQTT_ENABLED;
|
||||
newSettings.host = root["host"] | FACTORY_MQTT_HOST;
|
||||
newSettings.port = root["port"] | FACTORY_MQTT_PORT;
|
||||
newSettings.port = static_cast<uint16_t>(root["port"] | FACTORY_MQTT_PORT);
|
||||
newSettings.base = root["base"] | FACTORY_MQTT_BASE;
|
||||
newSettings.username = root["username"] | FACTORY_MQTT_USERNAME;
|
||||
newSettings.password = root["password"] | FACTORY_MQTT_PASSWORD;
|
||||
newSettings.clientId = root["client_id"] | FACTORY_MQTT_CLIENT_ID;
|
||||
newSettings.keepAlive = root["keep_alive"] | FACTORY_MQTT_KEEP_ALIVE;
|
||||
newSettings.clientId = root["client_id"] | generateClientId();
|
||||
newSettings.keepAlive = static_cast<uint16_t>(root["keep_alive"] | FACTORY_MQTT_KEEP_ALIVE);
|
||||
newSettings.cleanSession = root["clean_session"] | FACTORY_MQTT_CLEAN_SESSION;
|
||||
newSettings.mqtt_qos = root["mqtt_qos"] | EMSESP_DEFAULT_MQTT_QOS;
|
||||
newSettings.mqtt_qos = static_cast<uint8_t>(root["mqtt_qos"] | EMSESP_DEFAULT_MQTT_QOS);
|
||||
newSettings.mqtt_retain = root["mqtt_retain"] | EMSESP_DEFAULT_MQTT_RETAIN;
|
||||
|
||||
newSettings.publish_time_boiler = root["publish_time_boiler"] | EMSESP_DEFAULT_PUBLISH_TIME;
|
||||
newSettings.publish_time_thermostat = root["publish_time_thermostat"] | EMSESP_DEFAULT_PUBLISH_TIME;
|
||||
newSettings.publish_time_solar = root["publish_time_solar"] | EMSESP_DEFAULT_PUBLISH_TIME;
|
||||
newSettings.publish_time_mixer = root["publish_time_mixer"] | EMSESP_DEFAULT_PUBLISH_TIME;
|
||||
newSettings.publish_time_water = root["publish_time_water"] | EMSESP_DEFAULT_PUBLISH_TIME;
|
||||
newSettings.publish_time_other = root["publish_time_other"] | EMSESP_DEFAULT_PUBLISH_TIME;
|
||||
newSettings.publish_time_sensor = root["publish_time_sensor"] | EMSESP_DEFAULT_PUBLISH_TIME;
|
||||
newSettings.publish_time_heartbeat = root["publish_time_heartbeat"] | EMSESP_DEFAULT_PUBLISH_HEARTBEAT;
|
||||
newSettings.publish_time_boiler = static_cast<uint16_t>(root["publish_time_boiler"] | EMSESP_DEFAULT_PUBLISH_TIME);
|
||||
newSettings.publish_time_thermostat = static_cast<uint16_t>(root["publish_time_thermostat"] | EMSESP_DEFAULT_PUBLISH_TIME);
|
||||
newSettings.publish_time_solar = static_cast<uint16_t>(root["publish_time_solar"] | EMSESP_DEFAULT_PUBLISH_TIME);
|
||||
newSettings.publish_time_mixer = static_cast<uint16_t>(root["publish_time_mixer"] | EMSESP_DEFAULT_PUBLISH_TIME);
|
||||
newSettings.publish_time_water = static_cast<uint16_t>(root["publish_time_water"] | EMSESP_DEFAULT_PUBLISH_TIME);
|
||||
newSettings.publish_time_other = static_cast<uint16_t>(root["publish_time_other"] | EMSESP_DEFAULT_PUBLISH_TIME);
|
||||
newSettings.publish_time_sensor = static_cast<uint16_t>(root["publish_time_sensor"] | EMSESP_DEFAULT_PUBLISH_TIME);
|
||||
newSettings.publish_time_heartbeat = static_cast<uint16_t>(root["publish_time_heartbeat"] | EMSESP_DEFAULT_PUBLISH_HEARTBEAT);
|
||||
|
||||
newSettings.ha_enabled = root["ha_enabled"] | EMSESP_DEFAULT_HA_ENABLED;
|
||||
newSettings.nested_format = root["nested_format"] | EMSESP_DEFAULT_NESTED_FORMAT;
|
||||
newSettings.nested_format = static_cast<uint8_t>(root["nested_format"] | EMSESP_DEFAULT_NESTED_FORMAT);
|
||||
newSettings.discovery_prefix = root["discovery_prefix"] | EMSESP_DEFAULT_DISCOVERY_PREFIX;
|
||||
newSettings.discovery_type = root["discovery_type"] | EMSESP_DEFAULT_DISCOVERY_TYPE;
|
||||
newSettings.discovery_type = static_cast<uint8_t>(root["discovery_type"] | EMSESP_DEFAULT_DISCOVERY_TYPE);
|
||||
newSettings.publish_single = root["publish_single"] | EMSESP_DEFAULT_PUBLISH_SINGLE;
|
||||
newSettings.publish_single2cmd = root["publish_single2cmd"] | EMSESP_DEFAULT_PUBLISH_SINGLE2CMD;
|
||||
newSettings.send_response = root["send_response"] | EMSESP_DEFAULT_SEND_RESPONSE;
|
||||
newSettings.entity_format = root["entity_format"] | EMSESP_DEFAULT_ENTITY_FORMAT;
|
||||
newSettings.entity_format = static_cast<uint8_t>(root["entity_format"] | EMSESP_DEFAULT_ENTITY_FORMAT);
|
||||
|
||||
if (newSettings.enabled != settings.enabled) {
|
||||
changed = true;
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
#ifndef MqttSettingsService_h
|
||||
#define MqttSettingsService_h
|
||||
|
||||
#include <StatefulService.h>
|
||||
#include <HttpEndpoint.h>
|
||||
#include <FSPersistence.h>
|
||||
#include "StatefulService.h"
|
||||
#include "HttpEndpoint.h"
|
||||
#include "FSPersistence.h"
|
||||
|
||||
#include <espMqttClient.h>
|
||||
#include <ESPUtils.h>
|
||||
|
||||
#include <uuid/common.h>
|
||||
|
||||
@@ -38,13 +38,6 @@
|
||||
#define FACTORY_MQTT_PASSWORD ""
|
||||
#endif
|
||||
|
||||
#ifndef FACTORY_MQTT_CLIENT_ID
|
||||
#define FACTORY_MQTT_CLIENT_ID generateClientId()
|
||||
static String generateClientId() {
|
||||
return ESPUtils::defaultDeviceValue("esp32-");
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef FACTORY_MQTT_KEEP_ALIVE
|
||||
#define FACTORY_MQTT_KEEP_ALIVE 16
|
||||
#endif
|
||||
@@ -59,21 +52,14 @@ static String generateClientId() {
|
||||
|
||||
class MqttSettings {
|
||||
public:
|
||||
// host and port - if enabled
|
||||
bool enabled;
|
||||
String host;
|
||||
uint16_t port;
|
||||
String rootCA;
|
||||
bool enableTLS;
|
||||
|
||||
// username and password
|
||||
String username;
|
||||
String password;
|
||||
|
||||
// client id settings
|
||||
String clientId;
|
||||
|
||||
// connection settings
|
||||
String username;
|
||||
String password;
|
||||
String clientId;
|
||||
uint16_t keepAlive;
|
||||
bool cleanSession;
|
||||
|
||||
@@ -124,14 +110,6 @@ class MqttSettingsService : public StatefulService<MqttSettings> {
|
||||
HttpEndpoint<MqttSettings> _httpEndpoint;
|
||||
FSPersistence<MqttSettings> _fsPersistence;
|
||||
|
||||
// Pointers to hold retained copies of the mqtt client connection strings.
|
||||
// This is required as espMqttClient holds references to the supplied connection strings.
|
||||
char * _retainedHost;
|
||||
char * _retainedClientId;
|
||||
char * _retainedUsername;
|
||||
char * _retainedPassword;
|
||||
char * _retainedRootCA;
|
||||
|
||||
// variable to help manage connection
|
||||
bool _reconfigureMqtt;
|
||||
unsigned long _disconnectedAt;
|
||||
@@ -142,11 +120,12 @@ class MqttSettingsService : public StatefulService<MqttSettings> {
|
||||
// the MQTT client instance
|
||||
MqttClient * _mqttClient;
|
||||
|
||||
void WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info);
|
||||
void WiFiEvent(WiFiEvent_t event);
|
||||
void onMqttConnect(bool sessionPresent);
|
||||
void onMqttDisconnect(espMqttClientTypes::DisconnectReason reason);
|
||||
void
|
||||
onMqttMessage(const espMqttClientTypes::MessageProperties & properties, const char * topic, const uint8_t * payload, size_t len, size_t index, size_t total);
|
||||
|
||||
bool configureMqtt();
|
||||
};
|
||||
|
||||
|
||||
@@ -1,19 +1,17 @@
|
||||
#include <MqttStatus.h>
|
||||
#include "MqttStatus.h"
|
||||
|
||||
#include "../../src/emsesp_stub.hpp"
|
||||
|
||||
using namespace std::placeholders; // for `_1` etc
|
||||
|
||||
MqttStatus::MqttStatus(AsyncWebServer * server, MqttSettingsService * mqttSettingsService, SecurityManager * securityManager)
|
||||
: _mqttSettingsService(mqttSettingsService) {
|
||||
server->on(MQTT_STATUS_SERVICE_PATH,
|
||||
HTTP_GET,
|
||||
securityManager->wrapRequest(std::bind(&MqttStatus::mqttStatus, this, _1), AuthenticationPredicates::IS_AUTHENTICATED));
|
||||
securityManager->wrapRequest([this](AsyncWebServerRequest * request) { mqttStatus(request); }, AuthenticationPredicates::IS_AUTHENTICATED));
|
||||
}
|
||||
|
||||
void MqttStatus::mqttStatus(AsyncWebServerRequest * request) {
|
||||
AsyncJsonResponse * response = new AsyncJsonResponse(false);
|
||||
JsonObject root = response->getRoot();
|
||||
auto * response = new AsyncJsonResponse(false);
|
||||
JsonObject root = response->getRoot();
|
||||
|
||||
root["enabled"] = _mqttSettingsService->isEnabled();
|
||||
root["connected"] = _mqttSettingsService->isConnected();
|
||||
|
||||
@@ -2,10 +2,11 @@
|
||||
#define MqttStatus_h
|
||||
|
||||
#include <WiFi.h>
|
||||
#include <MqttSettingsService.h>
|
||||
#include <ArduinoJson.h>
|
||||
#include <ESPAsyncWebServer.h>
|
||||
#include <SecurityManager.h>
|
||||
|
||||
#include "MqttSettingsService.h"
|
||||
#include "SecurityManager.h"
|
||||
|
||||
#define MQTT_STATUS_SERVICE_PATH "/rest/mqttStatus"
|
||||
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
#include <NTPSettingsService.h>
|
||||
#include "NTPSettingsService.h"
|
||||
|
||||
#include "../../src/emsesp_stub.hpp"
|
||||
|
||||
using namespace std::placeholders; // for `_1` etc
|
||||
|
||||
NTPSettingsService::NTPSettingsService(AsyncWebServer * server, FS * fs, SecurityManager * securityManager)
|
||||
: _httpEndpoint(NTPSettings::read, NTPSettings::update, this, server, NTP_SETTINGS_SERVICE_PATH, securityManager)
|
||||
, _fsPersistence(NTPSettings::read, NTPSettings::update, this, fs, NTP_SETTINGS_FILE)
|
||||
, _timeHandler(TIME_PATH, securityManager->wrapCallback(std::bind(&NTPSettingsService::configureTime, this, _1, _2), AuthenticationPredicates::IS_ADMIN)) {
|
||||
, _timeHandler(TIME_PATH,
|
||||
securityManager->wrapCallback([this](AsyncWebServerRequest * request, JsonVariant json) { configureTime(request, json); },
|
||||
AuthenticationPredicates::IS_ADMIN))
|
||||
, _connected(false) {
|
||||
_timeHandler.setMethod(HTTP_POST);
|
||||
_timeHandler.setMaxContentLength(MAX_TIME_SIZE);
|
||||
server->addHandler(&_timeHandler);
|
||||
|
||||
WiFi.onEvent(std::bind(&NTPSettingsService::WiFiEvent, this, _1));
|
||||
|
||||
addUpdateHandler([&](const String & originId) { configureNTP(); }, false);
|
||||
WiFi.onEvent([this](WiFiEvent_t event, WiFiEventInfo_t info) { WiFiEvent(event); });
|
||||
addUpdateHandler([this] { configureNTP(); }, false);
|
||||
}
|
||||
|
||||
void NTPSettingsService::begin() {
|
||||
@@ -27,9 +27,9 @@ void NTPSettingsService::WiFiEvent(WiFiEvent_t event) {
|
||||
switch (event) {
|
||||
case ARDUINO_EVENT_WIFI_STA_DISCONNECTED:
|
||||
case ARDUINO_EVENT_ETH_DISCONNECTED:
|
||||
if (connected_) {
|
||||
if (_connected) {
|
||||
emsesp::EMSESP::logger().info("WiFi connection dropped, stopping NTP");
|
||||
connected_ = false;
|
||||
_connected = false;
|
||||
configureNTP();
|
||||
}
|
||||
break;
|
||||
@@ -37,7 +37,7 @@ void NTPSettingsService::WiFiEvent(WiFiEvent_t event) {
|
||||
case ARDUINO_EVENT_WIFI_STA_GOT_IP:
|
||||
case ARDUINO_EVENT_ETH_GOT_IP:
|
||||
// emsesp::EMSESP::logger().info("Got IP address, starting NTP synchronization");
|
||||
connected_ = true;
|
||||
_connected = true;
|
||||
configureNTP();
|
||||
break;
|
||||
|
||||
@@ -49,7 +49,7 @@ void NTPSettingsService::WiFiEvent(WiFiEvent_t event) {
|
||||
// https://werner.rothschopf.net/microcontroller/202103_arduino_esp32_ntp_en.htm
|
||||
void NTPSettingsService::configureNTP() {
|
||||
emsesp::EMSESP::system_.ntp_connected(false);
|
||||
if (connected_ && _state.enabled) {
|
||||
if (_connected && _state.enabled) {
|
||||
emsesp::EMSESP::logger().info("Starting NTP service");
|
||||
esp_sntp_set_sync_interval(3600000); // one hour
|
||||
esp_sntp_set_time_sync_notification_cb(ntp_received);
|
||||
@@ -63,13 +63,13 @@ void NTPSettingsService::configureNTP() {
|
||||
|
||||
void NTPSettingsService::configureTime(AsyncWebServerRequest * request, JsonVariant json) {
|
||||
if (json.is<JsonObject>()) {
|
||||
struct tm tm = {0};
|
||||
struct tm tm = {};
|
||||
String timeLocal = json["local_time"];
|
||||
char * s = strptime(timeLocal.c_str(), "%Y-%m-%dT%H:%M:%S", &tm);
|
||||
if (s != nullptr) {
|
||||
tm.tm_isdst = -1; // not set by strptime, tells mktime to determine daylightsaving
|
||||
time_t time = mktime(&tm);
|
||||
struct timeval now = {.tv_sec = time};
|
||||
struct timeval now = {.tv_sec = time, .tv_usec = {}};
|
||||
settimeofday(&now, nullptr);
|
||||
AsyncWebServerResponse * response = request->beginResponse(200);
|
||||
request->send(response);
|
||||
@@ -82,6 +82,22 @@ void NTPSettingsService::configureTime(AsyncWebServerRequest * request, JsonVari
|
||||
}
|
||||
|
||||
void NTPSettingsService::ntp_received(struct timeval * tv) {
|
||||
(void)tv;
|
||||
// emsesp::EMSESP::logger().info("NTP sync to %d sec", tv->tv_sec);
|
||||
emsesp::EMSESP::system_.ntp_connected(true);
|
||||
}
|
||||
|
||||
void NTPSettings::read(NTPSettings & settings, JsonObject root) {
|
||||
root["enabled"] = settings.enabled;
|
||||
root["server"] = settings.server;
|
||||
root["tz_label"] = settings.tzLabel;
|
||||
root["tz_format"] = settings.tzFormat;
|
||||
}
|
||||
|
||||
StateUpdateResult NTPSettings::update(JsonObject root, NTPSettings & settings) {
|
||||
settings.enabled = root["enabled"] | FACTORY_NTP_ENABLED;
|
||||
settings.server = root["server"] | FACTORY_NTP_SERVER;
|
||||
settings.tzLabel = root["tz_label"] | FACTORY_NTP_TIME_ZONE_LABEL;
|
||||
settings.tzFormat = root["tz_format"] | FACTORY_NTP_TIME_ZONE_FORMAT;
|
||||
return StateUpdateResult::CHANGED;
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
#ifndef NTPSettingsService_h
|
||||
#define NTPSettingsService_h
|
||||
|
||||
#include <HttpEndpoint.h>
|
||||
#include <FSPersistence.h>
|
||||
#include "HttpEndpoint.h"
|
||||
#include "FSPersistence.h"
|
||||
|
||||
#include <time.h>
|
||||
#include <ctime>
|
||||
#include <esp_sntp.h>
|
||||
|
||||
#ifndef FACTORY_NTP_ENABLED
|
||||
@@ -36,20 +36,8 @@ class NTPSettings {
|
||||
String tzFormat;
|
||||
String server;
|
||||
|
||||
static void read(NTPSettings & settings, JsonObject root) {
|
||||
root["enabled"] = settings.enabled;
|
||||
root["server"] = settings.server;
|
||||
root["tz_label"] = settings.tzLabel;
|
||||
root["tz_format"] = settings.tzFormat;
|
||||
}
|
||||
|
||||
static StateUpdateResult update(JsonObject root, NTPSettings & settings) {
|
||||
settings.enabled = root["enabled"] | FACTORY_NTP_ENABLED;
|
||||
settings.server = root["server"] | FACTORY_NTP_SERVER;
|
||||
settings.tzLabel = root["tz_label"] | FACTORY_NTP_TIME_ZONE_LABEL;
|
||||
settings.tzFormat = root["tz_format"] | FACTORY_NTP_TIME_ZONE_FORMAT;
|
||||
return StateUpdateResult::CHANGED;
|
||||
}
|
||||
static void read(NTPSettings & settings, JsonObject root);
|
||||
static StateUpdateResult update(JsonObject root, NTPSettings & settings);
|
||||
};
|
||||
|
||||
class NTPSettingsService : public StatefulService<NTPSettings> {
|
||||
@@ -63,8 +51,8 @@ class NTPSettingsService : public StatefulService<NTPSettings> {
|
||||
HttpEndpoint<NTPSettings> _httpEndpoint;
|
||||
FSPersistence<NTPSettings> _fsPersistence;
|
||||
AsyncCallbackJsonWebHandler _timeHandler;
|
||||
bool _connected;
|
||||
|
||||
bool connected_ = false;
|
||||
void WiFiEvent(WiFiEvent_t event);
|
||||
void configureNTP();
|
||||
void configureTime(AsyncWebServerRequest * request, JsonVariant json);
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
#include <NTPStatus.h>
|
||||
#include "NTPStatus.h"
|
||||
|
||||
#include "../../src/emsesp_stub.hpp"
|
||||
|
||||
using namespace std::placeholders; // for `_1` etc
|
||||
#include <array>
|
||||
|
||||
NTPStatus::NTPStatus(AsyncWebServer * server, SecurityManager * securityManager) {
|
||||
server->on(NTP_STATUS_SERVICE_PATH,
|
||||
HTTP_GET,
|
||||
securityManager->wrapRequest(std::bind(&NTPStatus::ntpStatus, this, _1), AuthenticationPredicates::IS_AUTHENTICATED));
|
||||
securityManager->wrapRequest([this](AsyncWebServerRequest * request) { ntpStatus(request); }, AuthenticationPredicates::IS_AUTHENTICATED));
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -15,9 +16,9 @@ NTPStatus::NTPStatus(AsyncWebServer * server, SecurityManager * securityManager)
|
||||
* Uses a 25 byte buffer, large enough to fit an ISO time string with offset.
|
||||
*/
|
||||
String formatTime(tm * time, const char * format) {
|
||||
char time_string[25];
|
||||
strftime(time_string, 25, format, time);
|
||||
return String(time_string);
|
||||
std::array<char, 25> time_string{};
|
||||
strftime(time_string.data(), time_string.size(), format, time);
|
||||
return {time_string.data()};
|
||||
}
|
||||
|
||||
String toUTCTimeString(tm * time) {
|
||||
@@ -29,14 +30,23 @@ String toLocalTimeString(tm * time) {
|
||||
}
|
||||
|
||||
void NTPStatus::ntpStatus(AsyncWebServerRequest * request) {
|
||||
AsyncJsonResponse * response = new AsyncJsonResponse(false);
|
||||
JsonObject root = response->getRoot();
|
||||
auto * response = new AsyncJsonResponse(false);
|
||||
JsonObject root = response->getRoot();
|
||||
|
||||
// grab the current instant in unix seconds
|
||||
time_t now = time(nullptr);
|
||||
|
||||
// only provide enabled/disabled status for now
|
||||
root["status"] = esp_sntp_enabled() ? emsesp::EMSESP::system_.ntp_connected() ? 2 : 1 : 0;
|
||||
root["status"] = [] {
|
||||
if (esp_sntp_enabled()) {
|
||||
if (emsesp::EMSESP::system_.ntp_connected()) {
|
||||
return 2;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}();
|
||||
|
||||
// the current time in UTC
|
||||
root["utc_time"] = toUTCTimeString(gmtime(&now));
|
||||
@@ -49,4 +59,4 @@ void NTPStatus::ntpStatus(AsyncWebServerRequest * request) {
|
||||
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,14 @@
|
||||
#ifndef NTPStatus_h
|
||||
#define NTPStatus_h
|
||||
|
||||
#include <time.h>
|
||||
|
||||
#include <ctime>
|
||||
#include <WiFi.h>
|
||||
#include <esp_sntp.h>
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <ESPAsyncWebServer.h>
|
||||
#include <SecurityManager.h>
|
||||
|
||||
#include "SecurityManager.h"
|
||||
|
||||
#include <uuid/common.h>
|
||||
|
||||
#define NTP_STATUS_SERVICE_PATH "/rest/ntpStatus"
|
||||
|
||||
@@ -1,13 +1,25 @@
|
||||
#include <NetworkSettingsService.h>
|
||||
#include "NetworkSettingsService.h"
|
||||
|
||||
using namespace std::placeholders; // for `_1` etc
|
||||
#include "../../src/emsesp_stub.hpp"
|
||||
|
||||
NetworkSettingsService::NetworkSettingsService(AsyncWebServer * server, FS * fs, SecurityManager * securityManager)
|
||||
: _httpEndpoint(NetworkSettings::read, NetworkSettings::update, this, server, NETWORK_SETTINGS_SERVICE_PATH, securityManager)
|
||||
, _fsPersistence(NetworkSettings::read, NetworkSettings::update, this, fs, NETWORK_SETTINGS_FILE)
|
||||
, _lastConnectionAttempt(0) {
|
||||
addUpdateHandler([&](const String & originId) { reconfigureWiFiConnection(); }, false);
|
||||
WiFi.onEvent(std::bind(&NetworkSettingsService::WiFiEvent, this, _1));
|
||||
, _lastConnectionAttempt(0)
|
||||
, _stopping(false) {
|
||||
addUpdateHandler([this] { reconfigureWiFiConnection(); }, false);
|
||||
WiFi.onEvent([this](WiFiEvent_t event, WiFiEventInfo_t info) { WiFiEvent(event, info); });
|
||||
}
|
||||
|
||||
static bool formatBssid(const String & bssid, uint8_t (&mac)[6]) {
|
||||
uint tmp[6];
|
||||
if (bssid.isEmpty() || sscanf(bssid.c_str(), "%X:%X:%X:%X:%X:%X", &tmp[0], &tmp[1], &tmp[2], &tmp[3], &tmp[4], &tmp[5]) != 6) {
|
||||
return false;
|
||||
}
|
||||
for (uint8_t i = 0; i < 6; i++) {
|
||||
mac[i] = static_cast<uint8_t>(tmp[i]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void NetworkSettingsService::begin() {
|
||||
@@ -44,7 +56,7 @@ void NetworkSettingsService::reconfigureWiFiConnection() {
|
||||
|
||||
void NetworkSettingsService::loop() {
|
||||
unsigned long currentMillis = millis();
|
||||
if (!_lastConnectionAttempt || (uint32_t)(currentMillis - _lastConnectionAttempt) >= WIFI_RECONNECTION_DELAY) {
|
||||
if (!_lastConnectionAttempt || static_cast<uint32_t>(currentMillis - _lastConnectionAttempt) >= WIFI_RECONNECTION_DELAY) {
|
||||
_lastConnectionAttempt = currentMillis;
|
||||
manageSTA();
|
||||
}
|
||||
@@ -65,49 +77,379 @@ void NetworkSettingsService::manageSTA() {
|
||||
|
||||
// www.esp32.com/viewtopic.php?t=12055
|
||||
if (_state.bandwidth20) {
|
||||
esp_wifi_set_bandwidth((wifi_interface_t)ESP_IF_WIFI_STA, WIFI_BW_HT20);
|
||||
esp_wifi_set_bandwidth(static_cast<wifi_interface_t>(ESP_IF_WIFI_STA), WIFI_BW_HT20);
|
||||
} else {
|
||||
esp_wifi_set_bandwidth((wifi_interface_t)ESP_IF_WIFI_STA, WIFI_BW_HT40);
|
||||
esp_wifi_set_bandwidth(static_cast<wifi_interface_t>(ESP_IF_WIFI_STA), WIFI_BW_HT40);
|
||||
}
|
||||
if (_state.nosleep) {
|
||||
WiFi.setSleep(false); // turn off sleep - WIFI_PS_NONE
|
||||
}
|
||||
|
||||
// attempt to connect to the network
|
||||
uint mac[6];
|
||||
if (!_state.bssid.isEmpty() && sscanf(_state.bssid.c_str(), "%X:%X:%X:%X:%X:%X", &mac[0], &mac[1], &mac[2], &mac[3], &mac[4], &mac[5]) == 6) {
|
||||
uint8_t mac1[6];
|
||||
for (uint8_t i = 0; i < 6; i++) {
|
||||
mac1[i] = (uint8_t)mac[i];
|
||||
}
|
||||
WiFi.begin(_state.ssid.c_str(), _state.password.c_str(), 0, mac1);
|
||||
uint8_t bssid[6];
|
||||
if (formatBssid(_state.bssid, bssid)) {
|
||||
WiFi.begin(_state.ssid.c_str(), _state.password.c_str(), 0, bssid);
|
||||
} else {
|
||||
WiFi.begin(_state.ssid.c_str(), _state.password.c_str());
|
||||
}
|
||||
// set power after wifi is startet, fixed value for C3_V1
|
||||
// if (WiFi.isConnected()) {
|
||||
|
||||
#ifdef BOARD_C3_MINI_V1
|
||||
// always hardcode Tx power for Wemos CS Mini v1
|
||||
// v1 needs this value, see https://github.com/emsesp/EMS-ESP32/pull/620#discussion_r993173979
|
||||
WiFi.setTxPower(WIFI_POWER_8_5dBm); // https://www.wemos.cc/en/latest/c3/c3_mini_1_0_0.html#about-wifi
|
||||
// https://www.wemos.cc/en/latest/c3/c3_mini_1_0_0.html#about-wifi
|
||||
WiFi.setTxPower(WIFI_POWER_8_5dBm);
|
||||
#else
|
||||
// esp_wifi_set_max_tx_power(_state.tx_power * 4);
|
||||
// TODO make it dynamic
|
||||
WiFi.setTxPower((wifi_power_t)(_state.tx_power * 4));
|
||||
if (_state.tx_power != 0) {
|
||||
// if not set to Auto (0) set the Tx power now
|
||||
if (!WiFi.setTxPower(static_cast<wifi_power_t>(_state.tx_power))) {
|
||||
emsesp::EMSESP::logger().warning("Failed to set WiFi Tx Power");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
// }
|
||||
} else { // not connected but STA-mode active => disconnect
|
||||
reconfigureWiFiConnection();
|
||||
}
|
||||
}
|
||||
|
||||
// handles if wifi stopped
|
||||
void NetworkSettingsService::WiFiEvent(WiFiEvent_t event) {
|
||||
if (event == ARDUINO_EVENT_WIFI_STA_STOP) {
|
||||
// set the TxPower based on the RSSI (signal strength), picking the lowest value
|
||||
// code is based of RSSI (signal strength) and copied from Tasmota's WiFiSetTXpowerBasedOnRssi() which is copied ESPEasy's ESPEasyWifi.SetWiFiTXpower() function
|
||||
void NetworkSettingsService::setWiFiPowerOnRSSI() {
|
||||
// Range ESP32 : 2dBm - 20dBm
|
||||
// 802.11b - wifi1
|
||||
// 802.11a - wifi2
|
||||
// 802.11g - wifi3
|
||||
// 802.11n - wifi4
|
||||
// 802.11ac - wifi5
|
||||
// 802.11ax - wifi6
|
||||
|
||||
int max_tx_pwr = MAX_TX_PWR_DBM_n; // assume wifi4
|
||||
int threshold = WIFI_SENSITIVITY_n + 70; // Margin in dBm * 10 on top of threshold
|
||||
|
||||
// Assume AP sends with max set by ETSI standard.
|
||||
// 2.4 GHz: 100 mWatt (20 dBm)
|
||||
// US and some other countries allow 1000 mW (30 dBm)
|
||||
int rssi = WiFi.RSSI() * 10;
|
||||
int newrssi = rssi - 200; // We cannot send with over 20 dBm, thus it makes no sense to force higher TX power all the time.
|
||||
|
||||
int min_tx_pwr = 0;
|
||||
if (newrssi < threshold) {
|
||||
min_tx_pwr = threshold - newrssi;
|
||||
}
|
||||
if (min_tx_pwr > max_tx_pwr) {
|
||||
min_tx_pwr = max_tx_pwr;
|
||||
}
|
||||
|
||||
uint8_t set_power = min_tx_pwr / 10; // this is the recommended power setting to use
|
||||
|
||||
// from WiFIGeneric.h use:
|
||||
// WIFI_POWER_19_5dBm = 78,// 19.5dBm
|
||||
// WIFI_POWER_19dBm = 76,// 19dBm
|
||||
// WIFI_POWER_18_5dBm = 74,// 18.5dBm
|
||||
// WIFI_POWER_17dBm = 68,// 17dBm
|
||||
// WIFI_POWER_15dBm = 60,// 15dBm
|
||||
// WIFI_POWER_13dBm = 52,// 13dBm
|
||||
// WIFI_POWER_11dBm = 44,// 11dBm
|
||||
// WIFI_POWER_8_5dBm = 34,// 8.5dBm
|
||||
// WIFI_POWER_7dBm = 28,// 7dBm
|
||||
// WIFI_POWER_5dBm = 20,// 5dBm
|
||||
// WIFI_POWER_2dBm = 8,// 2dBm
|
||||
// WIFI_POWER_MINUS_1dBm = -4// -1dBm
|
||||
wifi_power_t p = WIFI_POWER_2dBm;
|
||||
if (min_tx_pwr > 185)
|
||||
p = WIFI_POWER_19_5dBm;
|
||||
else if (min_tx_pwr > 170)
|
||||
p = WIFI_POWER_18_5dBm;
|
||||
else if (min_tx_pwr > 150)
|
||||
p = WIFI_POWER_17dBm;
|
||||
else if (min_tx_pwr > 130)
|
||||
p = WIFI_POWER_15dBm;
|
||||
else if (min_tx_pwr > 110)
|
||||
p = WIFI_POWER_13dBm;
|
||||
else if (min_tx_pwr > 85)
|
||||
p = WIFI_POWER_11dBm;
|
||||
else if (min_tx_pwr > 70)
|
||||
p = WIFI_POWER_8_5dBm;
|
||||
else if (min_tx_pwr > 50)
|
||||
p = WIFI_POWER_7dBm;
|
||||
else if (min_tx_pwr > 20)
|
||||
p = WIFI_POWER_5dBm;
|
||||
|
||||
#ifdef EMSESP_DEBUG
|
||||
emsesp::EMSESP::logger().debug("Recommended set WiFi Tx Power (set_power %d, new power %d, rssi %d, threshold %d)", set_power, p, rssi, threshold);
|
||||
#else
|
||||
char result[10];
|
||||
emsesp::EMSESP::logger().info("Setting WiFi Tx Power to %s dBm", emsesp::Helpers::render_value(result, ((double)(p) / 4), 1));
|
||||
#endif
|
||||
|
||||
if (!WiFi.setTxPower(p)) {
|
||||
emsesp::EMSESP::logger().warning("Failed to set WiFi Tx Power");
|
||||
}
|
||||
}
|
||||
|
||||
// start the multicast UDP service so EMS-ESP is discoverable via .local
|
||||
void NetworkSettingsService::mDNS_start() const {
|
||||
#ifndef EMSESP_STANDALONE
|
||||
MDNS.end();
|
||||
|
||||
if (_state.enableMDNS) {
|
||||
if (!MDNS.begin(emsesp::EMSESP::system_.hostname().c_str())) {
|
||||
emsesp::EMSESP::logger().warning("Failed to start mDNS Responder service");
|
||||
return;
|
||||
}
|
||||
|
||||
std::string address_s = emsesp::EMSESP::system_.hostname() + ".local";
|
||||
|
||||
MDNS.addService("http", "tcp", 80); // add our web server and rest API
|
||||
MDNS.addService("telnet", "tcp", 23); // add our telnet console
|
||||
|
||||
MDNS.addServiceTxt("http", "tcp", "version", EMSESP_APP_VERSION);
|
||||
MDNS.addServiceTxt("http", "tcp", "address", address_s.c_str());
|
||||
|
||||
emsesp::EMSESP::logger().info("Starting mDNS Responder service");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
const char * NetworkSettingsService::disconnectReason(uint8_t code) {
|
||||
#ifndef EMSESP_STANDALONE
|
||||
switch (code) {
|
||||
case WIFI_REASON_UNSPECIFIED: // = 1,
|
||||
return "unspecified";
|
||||
case WIFI_REASON_AUTH_EXPIRE: // = 2,
|
||||
return "auth expire";
|
||||
case WIFI_REASON_AUTH_LEAVE: // = 3,
|
||||
return "auth leave";
|
||||
case WIFI_REASON_ASSOC_EXPIRE: // = 4,
|
||||
return "assoc expired";
|
||||
case WIFI_REASON_ASSOC_TOOMANY: // = 5,
|
||||
return "assoc too many";
|
||||
case WIFI_REASON_NOT_AUTHED: // = 6,
|
||||
return "not authenticated";
|
||||
case WIFI_REASON_NOT_ASSOCED: // = 7,
|
||||
return "not assoc";
|
||||
case WIFI_REASON_ASSOC_LEAVE: // = 8,
|
||||
return "assoc leave";
|
||||
case WIFI_REASON_ASSOC_NOT_AUTHED: // = 9,
|
||||
return "assoc not authed";
|
||||
case WIFI_REASON_DISASSOC_PWRCAP_BAD: // = 10,
|
||||
return "disassoc powerCAP bad";
|
||||
case WIFI_REASON_DISASSOC_SUPCHAN_BAD: // = 11,
|
||||
return "disassoc supchan bad";
|
||||
case WIFI_REASON_IE_INVALID: // = 13,
|
||||
return "IE invalid";
|
||||
case WIFI_REASON_MIC_FAILURE: // = 14,
|
||||
return "MIC failure";
|
||||
case WIFI_REASON_4WAY_HANDSHAKE_TIMEOUT: // = 15,
|
||||
return "4way handshake timeout";
|
||||
case WIFI_REASON_GROUP_KEY_UPDATE_TIMEOUT: // = 16,
|
||||
return "group key-update timeout";
|
||||
case WIFI_REASON_IE_IN_4WAY_DIFFERS: // = 17,
|
||||
return "IE in 4way differs";
|
||||
case WIFI_REASON_GROUP_CIPHER_INVALID: // = 18,
|
||||
return "group cipher invalid";
|
||||
case WIFI_REASON_PAIRWISE_CIPHER_INVALID: // = 19,
|
||||
return "pairwise cipher invalid";
|
||||
case WIFI_REASON_AKMP_INVALID: // = 20,
|
||||
return "AKMP invalid";
|
||||
case WIFI_REASON_UNSUPP_RSN_IE_VERSION: // = 21,
|
||||
return "unsupported RSN_IE version";
|
||||
case WIFI_REASON_INVALID_RSN_IE_CAP: // = 22,
|
||||
return "invalid RSN_IE_CAP";
|
||||
case WIFI_REASON_802_1X_AUTH_FAILED: // = 23,
|
||||
return "802 X1 auth failed";
|
||||
case WIFI_REASON_CIPHER_SUITE_REJECTED: // = 24,
|
||||
return "cipher suite rejected";
|
||||
case WIFI_REASON_BEACON_TIMEOUT: // = 200,
|
||||
return "beacon timeout";
|
||||
case WIFI_REASON_NO_AP_FOUND: // = 201,
|
||||
return "no AP found";
|
||||
case WIFI_REASON_AUTH_FAIL: // = 202,
|
||||
return "auth fail";
|
||||
case WIFI_REASON_ASSOC_FAIL: // = 203,
|
||||
return "assoc fail";
|
||||
case WIFI_REASON_HANDSHAKE_TIMEOUT: // = 204,
|
||||
return "handshake timeout";
|
||||
case WIFI_REASON_CONNECTION_FAIL: // 205,
|
||||
return "connection fail";
|
||||
case WIFI_REASON_AP_TSF_RESET: // 206,
|
||||
return "AP tsf reset";
|
||||
case WIFI_REASON_ROAMING: // 207,
|
||||
return "roaming";
|
||||
case WIFI_REASON_ASSOC_COMEBACK_TIME_TOO_LONG: // 208,
|
||||
return "assoc comeback time too long";
|
||||
case WIFI_REASON_SA_QUERY_TIMEOUT: // 209,
|
||||
return "sa query timeout";
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
#endif
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
// handles both WiFI and Ethernet
|
||||
void NetworkSettingsService::WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info) {
|
||||
#ifndef EMSESP_STANDALONE
|
||||
|
||||
switch (event) {
|
||||
case ARDUINO_EVENT_WIFI_STA_STOP:
|
||||
if (_stopping) {
|
||||
_lastConnectionAttempt = 0;
|
||||
_stopping = false;
|
||||
}
|
||||
break;
|
||||
|
||||
case ARDUINO_EVENT_WIFI_STA_DISCONNECTED:
|
||||
emsesp::EMSESP::logger().warning("WiFi disconnected. Reason: %s (%d)",
|
||||
disconnectReason(info.wifi_sta_disconnected.reason),
|
||||
info.wifi_sta_disconnected.reason); // IDF 4.0
|
||||
emsesp::EMSESP::system_.has_ipv6(false);
|
||||
break;
|
||||
|
||||
case ARDUINO_EVENT_WIFI_STA_GOT_IP:
|
||||
char result[10];
|
||||
emsesp::EMSESP::logger().info("WiFi connected (IP=%s, hostname=%s, TxPower=%s dBm)",
|
||||
WiFi.localIP().toString().c_str(),
|
||||
WiFi.getHostname(),
|
||||
emsesp::Helpers::render_value(result, ((double)(WiFi.getTxPower()) / 4), 1));
|
||||
mDNS_start();
|
||||
break;
|
||||
|
||||
case ARDUINO_EVENT_ETH_START:
|
||||
ETH.setHostname(emsesp::EMSESP::system_.hostname().c_str());
|
||||
|
||||
// configure for static IP
|
||||
if (_state.staticIPConfig) {
|
||||
ETH.config(_state.localIP, _state.gatewayIP, _state.subnetMask, _state.dnsIP1, _state.dnsIP2);
|
||||
}
|
||||
break;
|
||||
|
||||
case ARDUINO_EVENT_ETH_GOT_IP:
|
||||
// prevent double calls
|
||||
if (!emsesp::EMSESP::system_.ethernet_connected()) {
|
||||
emsesp::EMSESP::logger().info("Ethernet connected (IP=%s, speed %d Mbps)", ETH.localIP().toString().c_str(), ETH.linkSpeed());
|
||||
emsesp::EMSESP::system_.ethernet_connected(true);
|
||||
mDNS_start();
|
||||
}
|
||||
break;
|
||||
|
||||
case ARDUINO_EVENT_ETH_DISCONNECTED:
|
||||
emsesp::EMSESP::logger().warning("Ethernet disconnected");
|
||||
emsesp::EMSESP::system_.ethernet_connected(false);
|
||||
emsesp::EMSESP::system_.has_ipv6(false);
|
||||
break;
|
||||
|
||||
case ARDUINO_EVENT_ETH_STOP:
|
||||
emsesp::EMSESP::logger().info("Ethernet stopped");
|
||||
emsesp::EMSESP::system_.ethernet_connected(false);
|
||||
emsesp::EMSESP::system_.has_ipv6(false);
|
||||
break;
|
||||
|
||||
case ARDUINO_EVENT_WIFI_STA_CONNECTED:
|
||||
// Set the TxPower after the connection is established, if we're using TxPower = 0 (Auto)
|
||||
if (_state.tx_power == 0) {
|
||||
setWiFiPowerOnRSSI();
|
||||
}
|
||||
if (_state.enableIPv6) {
|
||||
WiFi.enableIpV6();
|
||||
}
|
||||
break;
|
||||
|
||||
case ARDUINO_EVENT_ETH_CONNECTED:
|
||||
if (_state.enableIPv6) {
|
||||
ETH.enableIpV6();
|
||||
}
|
||||
break;
|
||||
|
||||
// IPv6 specific
|
||||
case ARDUINO_EVENT_WIFI_STA_GOT_IP6:
|
||||
case ARDUINO_EVENT_ETH_GOT_IP6:
|
||||
if (emsesp::EMSESP::system_.ethernet_connected()) {
|
||||
emsesp::EMSESP::logger().info("Ethernet connected (IPv6=%s, speed %d Mbps)", ETH.localIPv6().toString().c_str(), ETH.linkSpeed());
|
||||
} else {
|
||||
emsesp::EMSESP::logger().info("WiFi connected (IPv6=%s, hostname=%s, TxPower=%s dBm)",
|
||||
WiFi.localIPv6().toString().c_str(),
|
||||
WiFi.getHostname(),
|
||||
emsesp::Helpers::render_value(result, ((double)(WiFi.getTxPower()) / 4), 1));
|
||||
}
|
||||
mDNS_start();
|
||||
emsesp::EMSESP::system_.has_ipv6(true);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
// if (!_stopping && (event == ARDUINO_EVENT_WIFI_STA_LOST_IP || event == ARDUINO_EVENT_WIFI_STA_DISCONNECTED)) {
|
||||
// reconfigureWiFiConnection();
|
||||
// }
|
||||
#endif
|
||||
}
|
||||
|
||||
void NetworkSettings::read(NetworkSettings & settings, JsonObject root) {
|
||||
// connection settings
|
||||
root["ssid"] = settings.ssid;
|
||||
root["bssid"] = settings.bssid;
|
||||
root["password"] = settings.password;
|
||||
root["hostname"] = settings.hostname;
|
||||
root["static_ip_config"] = settings.staticIPConfig;
|
||||
root["enableIPv6"] = settings.enableIPv6;
|
||||
root["bandwidth20"] = settings.bandwidth20;
|
||||
root["nosleep"] = settings.nosleep;
|
||||
root["enableMDNS"] = settings.enableMDNS;
|
||||
root["enableCORS"] = settings.enableCORS;
|
||||
root["CORSOrigin"] = settings.CORSOrigin;
|
||||
root["tx_power"] = settings.tx_power;
|
||||
|
||||
// extended settings
|
||||
JsonUtils::writeIP(root, "local_ip", settings.localIP);
|
||||
JsonUtils::writeIP(root, "gateway_ip", settings.gatewayIP);
|
||||
JsonUtils::writeIP(root, "subnet_mask", settings.subnetMask);
|
||||
JsonUtils::writeIP(root, "dns_ip_1", settings.dnsIP1);
|
||||
JsonUtils::writeIP(root, "dns_ip_2", settings.dnsIP2);
|
||||
}
|
||||
|
||||
StateUpdateResult NetworkSettings::update(JsonObject root, NetworkSettings & settings) {
|
||||
// keep copy of original settings
|
||||
auto enableCORS = settings.enableCORS;
|
||||
auto CORSOrigin = settings.CORSOrigin;
|
||||
auto ssid = settings.ssid;
|
||||
auto tx_power = settings.tx_power;
|
||||
|
||||
settings.ssid = root["ssid"] | FACTORY_WIFI_SSID;
|
||||
settings.bssid = root["bssid"] | "";
|
||||
settings.password = root["password"] | FACTORY_WIFI_PASSWORD;
|
||||
settings.hostname = root["hostname"] | FACTORY_WIFI_HOSTNAME;
|
||||
settings.staticIPConfig = root["static_ip_config"] | false;
|
||||
settings.enableIPv6 = root["enableIPv6"] | false;
|
||||
settings.bandwidth20 = root["bandwidth20"] | false;
|
||||
settings.tx_power = static_cast<uint8_t>(root["tx_power"] | 0);
|
||||
settings.nosleep = root["nosleep"] | false;
|
||||
settings.enableMDNS = root["enableMDNS"] | true;
|
||||
settings.enableCORS = root["enableCORS"] | false;
|
||||
settings.CORSOrigin = root["CORSOrigin"] | "*";
|
||||
|
||||
// extended settings
|
||||
JsonUtils::readIP(root, "local_ip", settings.localIP);
|
||||
JsonUtils::readIP(root, "gateway_ip", settings.gatewayIP);
|
||||
JsonUtils::readIP(root, "subnet_mask", settings.subnetMask);
|
||||
JsonUtils::readIP(root, "dns_ip_1", settings.dnsIP1);
|
||||
JsonUtils::readIP(root, "dns_ip_2", settings.dnsIP2);
|
||||
|
||||
// Swap around the dns servers if 2 is populated but 1 is not
|
||||
if (IPUtils::isNotSet(settings.dnsIP1) && IPUtils::isSet(settings.dnsIP2)) {
|
||||
settings.dnsIP1 = settings.dnsIP2;
|
||||
settings.dnsIP2 = INADDR_NONE;
|
||||
}
|
||||
|
||||
// Turning off static ip config if we don't meet the minimum requirements
|
||||
// of ipAddress, gateway and subnet. This may change to static ip only
|
||||
// as sensible defaults can be assumed for gateway and subnet
|
||||
if (settings.staticIPConfig && (IPUtils::isNotSet(settings.localIP) || IPUtils::isNotSet(settings.gatewayIP) || IPUtils::isNotSet(settings.subnetMask))) {
|
||||
settings.staticIPConfig = false;
|
||||
}
|
||||
|
||||
// see if we need to inform the user of a restart
|
||||
if (tx_power != settings.tx_power || enableCORS != settings.enableCORS || CORSOrigin != settings.CORSOrigin
|
||||
|| (ssid != settings.ssid && settings.ssid.isEmpty())) {
|
||||
return StateUpdateResult::CHANGED_RESTART; // tell WebUI that a restart is needed
|
||||
}
|
||||
|
||||
return StateUpdateResult::CHANGED;
|
||||
}
|
||||
@@ -1,19 +1,22 @@
|
||||
#ifndef NetworkSettingsService_h
|
||||
#define NetworkSettingsService_h
|
||||
|
||||
#include <StatefulService.h>
|
||||
#include <FSPersistence.h>
|
||||
#include <HttpEndpoint.h>
|
||||
#include <JsonUtils.h>
|
||||
#include "StatefulService.h"
|
||||
#include "FSPersistence.h"
|
||||
#include "HttpEndpoint.h"
|
||||
#include "JsonUtils.h"
|
||||
|
||||
#ifndef EMSESP_STANDALONE
|
||||
#include <esp_wifi.h>
|
||||
#include <ETH.h>
|
||||
#include <WiFi.h>
|
||||
#include <AsyncTCP.h>
|
||||
#include <ESPmDNS.h>
|
||||
#endif
|
||||
|
||||
#define NETWORK_SETTINGS_FILE "/config/networkSettings.json"
|
||||
#define NETWORK_SETTINGS_SERVICE_PATH "/rest/networkSettings"
|
||||
#define WIFI_RECONNECTION_DELAY 1000 * 3
|
||||
#define WIFI_RECONNECTION_DELAY (1000 * 3)
|
||||
|
||||
#ifndef FACTORY_WIFI_SSID
|
||||
#define FACTORY_WIFI_SSID ""
|
||||
@@ -27,21 +30,52 @@
|
||||
#define FACTORY_WIFI_HOSTNAME ""
|
||||
#endif
|
||||
|
||||
// copied from Tasmota
|
||||
#if CONFIG_IDF_TARGET_ESP32S2
|
||||
#define MAX_TX_PWR_DBM_11b 195
|
||||
#define MAX_TX_PWR_DBM_54g 150
|
||||
#define MAX_TX_PWR_DBM_n 130
|
||||
#define WIFI_SENSITIVITY_11b -880
|
||||
#define WIFI_SENSITIVITY_54g -750
|
||||
#define WIFI_SENSITIVITY_n -720
|
||||
#elif CONFIG_IDF_TARGET_ESP32S3
|
||||
#define MAX_TX_PWR_DBM_11b 210
|
||||
#define MAX_TX_PWR_DBM_54g 190
|
||||
#define MAX_TX_PWR_DBM_n 185
|
||||
#define WIFI_SENSITIVITY_11b -880
|
||||
#define WIFI_SENSITIVITY_54g -760
|
||||
#define WIFI_SENSITIVITY_n -720
|
||||
#elif CONFIG_IDF_TARGET_ESP32C2 || CONFIG_IDF_TARGET_ESP32C3
|
||||
#define MAX_TX_PWR_DBM_11b 210
|
||||
#define MAX_TX_PWR_DBM_54g 190
|
||||
#define MAX_TX_PWR_DBM_n 185
|
||||
#define WIFI_SENSITIVITY_11b -880
|
||||
#define WIFI_SENSITIVITY_54g -760
|
||||
#define WIFI_SENSITIVITY_n -730
|
||||
#else
|
||||
#define MAX_TX_PWR_DBM_11b 195
|
||||
#define MAX_TX_PWR_DBM_54g 160
|
||||
#define MAX_TX_PWR_DBM_n 140
|
||||
#define WIFI_SENSITIVITY_11b -880
|
||||
#define WIFI_SENSITIVITY_54g -750
|
||||
#define WIFI_SENSITIVITY_n -700
|
||||
#endif
|
||||
|
||||
class NetworkSettings {
|
||||
public:
|
||||
// core wifi configuration
|
||||
String ssid;
|
||||
String bssid;
|
||||
String password;
|
||||
String hostname;
|
||||
bool staticIPConfig;
|
||||
bool enableIPv6;
|
||||
bool bandwidth20;
|
||||
int8_t tx_power;
|
||||
bool nosleep;
|
||||
bool enableMDNS;
|
||||
bool enableCORS;
|
||||
String CORSOrigin;
|
||||
String ssid;
|
||||
String bssid;
|
||||
String password;
|
||||
String hostname;
|
||||
bool staticIPConfig;
|
||||
bool enableIPv6;
|
||||
bool bandwidth20;
|
||||
uint8_t tx_power;
|
||||
bool nosleep;
|
||||
bool enableMDNS;
|
||||
bool enableCORS;
|
||||
String CORSOrigin;
|
||||
|
||||
// optional configuration for static IP address
|
||||
IPAddress localIP;
|
||||
@@ -50,73 +84,11 @@ class NetworkSettings {
|
||||
IPAddress dnsIP1;
|
||||
IPAddress dnsIP2;
|
||||
|
||||
static void read(NetworkSettings & settings, JsonObject root) {
|
||||
// connection settings
|
||||
root["ssid"] = settings.ssid;
|
||||
root["bssid"] = settings.bssid;
|
||||
root["password"] = settings.password;
|
||||
root["hostname"] = settings.hostname;
|
||||
root["static_ip_config"] = settings.staticIPConfig;
|
||||
root["enableIPv6"] = settings.enableIPv6;
|
||||
root["bandwidth20"] = settings.bandwidth20;
|
||||
root["tx_power"] = settings.tx_power;
|
||||
root["nosleep"] = settings.nosleep;
|
||||
root["enableMDNS"] = settings.enableMDNS;
|
||||
root["enableCORS"] = settings.enableCORS;
|
||||
root["CORSOrigin"] = settings.CORSOrigin;
|
||||
|
||||
// extended settings
|
||||
JsonUtils::writeIP(root, "local_ip", settings.localIP);
|
||||
JsonUtils::writeIP(root, "gateway_ip", settings.gatewayIP);
|
||||
JsonUtils::writeIP(root, "subnet_mask", settings.subnetMask);
|
||||
JsonUtils::writeIP(root, "dns_ip_1", settings.dnsIP1);
|
||||
JsonUtils::writeIP(root, "dns_ip_2", settings.dnsIP2);
|
||||
}
|
||||
|
||||
static StateUpdateResult update(JsonObject root, NetworkSettings & settings) {
|
||||
auto enableCORS = settings.enableCORS;
|
||||
auto CORSOrigin = settings.CORSOrigin;
|
||||
auto ssid = settings.ssid;
|
||||
settings.ssid = root["ssid"] | FACTORY_WIFI_SSID;
|
||||
settings.bssid = root["bssid"] | "";
|
||||
settings.password = root["password"] | FACTORY_WIFI_PASSWORD;
|
||||
settings.hostname = root["hostname"] | FACTORY_WIFI_HOSTNAME;
|
||||
settings.staticIPConfig = root["static_ip_config"] | false;
|
||||
settings.enableIPv6 = root["enableIPv6"] | false;
|
||||
settings.bandwidth20 = root["bandwidth20"] | false;
|
||||
settings.tx_power = root["tx_power"] | 20;
|
||||
settings.nosleep = root["nosleep"] | false;
|
||||
settings.enableMDNS = root["enableMDNS"] | true;
|
||||
settings.enableCORS = root["enableCORS"] | false;
|
||||
settings.CORSOrigin = root["CORSOrigin"] | "*";
|
||||
|
||||
// extended settings
|
||||
JsonUtils::readIP(root, "local_ip", settings.localIP);
|
||||
JsonUtils::readIP(root, "gateway_ip", settings.gatewayIP);
|
||||
JsonUtils::readIP(root, "subnet_mask", settings.subnetMask);
|
||||
JsonUtils::readIP(root, "dns_ip_1", settings.dnsIP1);
|
||||
JsonUtils::readIP(root, "dns_ip_2", settings.dnsIP2);
|
||||
|
||||
// Swap around the dns servers if 2 is populated but 1 is not
|
||||
if (IPUtils::isNotSet(settings.dnsIP1) && IPUtils::isSet(settings.dnsIP2)) {
|
||||
settings.dnsIP1 = settings.dnsIP2;
|
||||
settings.dnsIP2 = INADDR_NONE;
|
||||
}
|
||||
|
||||
// Turning off static ip config if we don't meet the minimum requirements
|
||||
// of ipAddress, gateway and subnet. This may change to static ip only
|
||||
// as sensible defaults can be assumed for gateway and subnet
|
||||
if (settings.staticIPConfig && (IPUtils::isNotSet(settings.localIP) || IPUtils::isNotSet(settings.gatewayIP) || IPUtils::isNotSet(settings.subnetMask))) {
|
||||
settings.staticIPConfig = false;
|
||||
}
|
||||
if (enableCORS != settings.enableCORS || CORSOrigin != settings.CORSOrigin || (ssid != settings.ssid && settings.ssid == "")) {
|
||||
return StateUpdateResult::CHANGED_RESTART; // tell WebUI that a restart is needed
|
||||
}
|
||||
|
||||
return StateUpdateResult::CHANGED;
|
||||
}
|
||||
static void read(NetworkSettings & settings, JsonObject root);
|
||||
static StateUpdateResult update(JsonObject root, NetworkSettings & settings);
|
||||
};
|
||||
|
||||
|
||||
class NetworkSettingsService : public StatefulService<NetworkSettings> {
|
||||
public:
|
||||
NetworkSettingsService(AsyncWebServer * server, FS * fs, SecurityManager * securityManager);
|
||||
@@ -127,13 +99,16 @@ class NetworkSettingsService : public StatefulService<NetworkSettings> {
|
||||
private:
|
||||
HttpEndpoint<NetworkSettings> _httpEndpoint;
|
||||
FSPersistence<NetworkSettings> _fsPersistence;
|
||||
unsigned long _lastConnectionAttempt;
|
||||
|
||||
bool _stopping;
|
||||
void WiFiEvent(WiFiEvent_t event);
|
||||
unsigned long _lastConnectionAttempt;
|
||||
bool _stopping;
|
||||
|
||||
void reconfigureWiFiConnection();
|
||||
void manageSTA();
|
||||
void WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info);
|
||||
void mDNS_start() const;
|
||||
const char * disconnectReason(uint8_t code);
|
||||
void reconfigureWiFiConnection();
|
||||
void manageSTA();
|
||||
void setWiFiPowerOnRSSI();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,18 +1,16 @@
|
||||
#include <NetworkStatus.h>
|
||||
#include "NetworkStatus.h"
|
||||
|
||||
#include "../../src/emsesp_stub.hpp"
|
||||
|
||||
using namespace std::placeholders; // for `_1` etc
|
||||
|
||||
NetworkStatus::NetworkStatus(AsyncWebServer * server, SecurityManager * securityManager) {
|
||||
server->on(NETWORK_STATUS_SERVICE_PATH,
|
||||
HTTP_GET,
|
||||
securityManager->wrapRequest(std::bind(&NetworkStatus::networkStatus, this, _1), AuthenticationPredicates::IS_AUTHENTICATED));
|
||||
securityManager->wrapRequest([this](AsyncWebServerRequest * request) { networkStatus(request); }, AuthenticationPredicates::IS_AUTHENTICATED));
|
||||
}
|
||||
|
||||
void NetworkStatus::networkStatus(AsyncWebServerRequest * request) {
|
||||
AsyncJsonResponse * response = new AsyncJsonResponse(false);
|
||||
JsonObject root = response->getRoot();
|
||||
auto * response = new AsyncJsonResponse(false);
|
||||
JsonObject root = response->getRoot();
|
||||
|
||||
bool ethernet_connected = emsesp::EMSESP::system_.ethernet_connected();
|
||||
wl_status_t wifi_status = WiFi.status();
|
||||
@@ -22,7 +20,7 @@ void NetworkStatus::networkStatus(AsyncWebServerRequest * request) {
|
||||
root["status"] = 10; // custom code #10 - ETHERNET_STATUS_CONNECTED
|
||||
root["hostname"] = ETH.getHostname();
|
||||
} else {
|
||||
root["status"] = (uint8_t)wifi_status;
|
||||
root["status"] = static_cast<uint8_t>(wifi_status);
|
||||
root["hostname"] = WiFi.getHostname();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,16 +1,12 @@
|
||||
#ifndef NetworkStatus_h
|
||||
#define NetworkStatus_h
|
||||
|
||||
#include <WiFi.h>
|
||||
#include <AsyncTCP.h>
|
||||
|
||||
#include <ETH.h>
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <ESPAsyncWebServer.h>
|
||||
#include <IPAddress.h>
|
||||
#include <IPUtils.h>
|
||||
#include <SecurityManager.h>
|
||||
|
||||
#include "IPUtils.h"
|
||||
#include "SecurityManager.h"
|
||||
|
||||
#define MAX_NETWORK_STATUS_SIZE 1024
|
||||
#define NETWORK_STATUS_SERVICE_PATH "/rest/networkStatus"
|
||||
|
||||
@@ -1,15 +1,13 @@
|
||||
#include <OTASettingsService.h>
|
||||
#include "OTASettingsService.h"
|
||||
|
||||
#include "../../src/emsesp_stub.hpp"
|
||||
|
||||
using namespace std::placeholders; // for `_1` etc
|
||||
|
||||
OTASettingsService::OTASettingsService(AsyncWebServer * server, FS * fs, SecurityManager * securityManager)
|
||||
: _httpEndpoint(OTASettings::read, OTASettings::update, this, server, OTA_SETTINGS_SERVICE_PATH, securityManager)
|
||||
, _fsPersistence(OTASettings::read, OTASettings::update, this, fs, OTA_SETTINGS_FILE)
|
||||
, _arduinoOTA(nullptr) {
|
||||
WiFi.onEvent(std::bind(&OTASettingsService::WiFiEvent, this, _1, _2));
|
||||
addUpdateHandler([&](const String & originId) { configureArduinoOTA(); }, false);
|
||||
WiFi.onEvent([this](WiFiEvent_t event, WiFiEventInfo_t info) { WiFiEvent(event); });
|
||||
addUpdateHandler([this] { configureArduinoOTA(); }, false);
|
||||
}
|
||||
|
||||
void OTASettingsService::begin() {
|
||||
@@ -32,11 +30,11 @@ void OTASettingsService::configureArduinoOTA() {
|
||||
|
||||
if (_state.enabled) {
|
||||
_arduinoOTA = new ArduinoOTAClass;
|
||||
_arduinoOTA->setPort(_state.port);
|
||||
_arduinoOTA->setPort(static_cast<uint16_t>(_state.port));
|
||||
_arduinoOTA->setPassword(_state.password.c_str());
|
||||
|
||||
_arduinoOTA->onStart([]() { emsesp::EMSESP::system_.upload_status(true); });
|
||||
_arduinoOTA->onEnd([]() { emsesp::EMSESP::system_.upload_status(false); });
|
||||
_arduinoOTA->onStart([] { emsesp::EMSESP::system_.upload_status(true); });
|
||||
_arduinoOTA->onEnd([] { emsesp::EMSESP::system_.upload_status(false); });
|
||||
|
||||
_arduinoOTA->onProgress([](unsigned int progress, unsigned int total) {
|
||||
// Serial.printf("Progress: %u%%\r\n", (progress / (total / 100)));
|
||||
@@ -64,7 +62,7 @@ void OTASettingsService::configureArduinoOTA() {
|
||||
}
|
||||
}
|
||||
|
||||
void OTASettingsService::WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info) {
|
||||
void OTASettingsService::WiFiEvent(WiFiEvent_t event) {
|
||||
switch (event) {
|
||||
case ARDUINO_EVENT_WIFI_STA_GOT_IP:
|
||||
case ARDUINO_EVENT_ETH_GOT_IP:
|
||||
@@ -74,3 +72,16 @@ void OTASettingsService::WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void OTASettings::read(OTASettings & settings, JsonObject root) {
|
||||
root["enabled"] = settings.enabled;
|
||||
root["port"] = settings.port;
|
||||
root["password"] = settings.password;
|
||||
}
|
||||
|
||||
StateUpdateResult OTASettings::update(JsonObject root, OTASettings & settings) {
|
||||
settings.enabled = root["enabled"] | FACTORY_OTA_ENABLED;
|
||||
settings.port = root["port"] | FACTORY_OTA_PORT;
|
||||
settings.password = root["password"] | FACTORY_OTA_PASSWORD;
|
||||
return StateUpdateResult::CHANGED;
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
#ifndef OTASettingsService_h
|
||||
#define OTASettingsService_h
|
||||
|
||||
#include <HttpEndpoint.h>
|
||||
#include <FSPersistence.h>
|
||||
#include "HttpEndpoint.h"
|
||||
#include "FSPersistence.h"
|
||||
|
||||
#include <ArduinoOTA.h>
|
||||
#include <WiFiUdp.h>
|
||||
@@ -28,18 +28,8 @@ class OTASettings {
|
||||
int port;
|
||||
String password;
|
||||
|
||||
static void read(OTASettings & settings, JsonObject root) {
|
||||
root["enabled"] = settings.enabled;
|
||||
root["port"] = settings.port;
|
||||
root["password"] = settings.password;
|
||||
}
|
||||
|
||||
static StateUpdateResult update(JsonObject root, OTASettings & settings) {
|
||||
settings.enabled = root["enabled"] | FACTORY_OTA_ENABLED;
|
||||
settings.port = root["port"] | FACTORY_OTA_PORT;
|
||||
settings.password = root["password"] | FACTORY_OTA_PASSWORD;
|
||||
return StateUpdateResult::CHANGED;
|
||||
}
|
||||
static void read(OTASettings & settings, JsonObject root);
|
||||
static StateUpdateResult update(JsonObject root, OTASettings & settings);
|
||||
};
|
||||
|
||||
class OTASettingsService : public StatefulService<OTASettings> {
|
||||
@@ -55,7 +45,7 @@ class OTASettingsService : public StatefulService<OTASettings> {
|
||||
ArduinoOTAClass * _arduinoOTA;
|
||||
|
||||
void configureArduinoOTA();
|
||||
void WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info);
|
||||
void WiFiEvent(WiFiEvent_t event);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,15 +1,22 @@
|
||||
#include <RestartService.h>
|
||||
#include "RestartService.h"
|
||||
|
||||
#include <esp_ota_ops.h>
|
||||
|
||||
#include "../../src/emsesp_stub.hpp"
|
||||
|
||||
using namespace std::placeholders; // for `_1` etc
|
||||
|
||||
RestartService::RestartService(AsyncWebServer * server, SecurityManager * securityManager) {
|
||||
server->on(RESTART_SERVICE_PATH, HTTP_POST, securityManager->wrapRequest(std::bind(&RestartService::restart, this, _1), AuthenticationPredicates::IS_ADMIN));
|
||||
server->on(RESTART_SERVICE_PATH,
|
||||
HTTP_POST,
|
||||
securityManager->wrapRequest([this](AsyncWebServerRequest * request) { restart(request); }, AuthenticationPredicates::IS_ADMIN));
|
||||
server->on(PARTITION_SERVICE_PATH,
|
||||
HTTP_POST,
|
||||
securityManager->wrapRequest(std::bind(&RestartService::partition, this, _1), AuthenticationPredicates::IS_ADMIN));
|
||||
securityManager->wrapRequest([this](AsyncWebServerRequest * request) { partition(request); }, AuthenticationPredicates::IS_ADMIN));
|
||||
}
|
||||
|
||||
void RestartService::restartNow() {
|
||||
WiFi.disconnect(true);
|
||||
delay(500);
|
||||
ESP.restart();
|
||||
}
|
||||
|
||||
void RestartService::restart(AsyncWebServerRequest * request) {
|
||||
@@ -19,7 +26,7 @@ void RestartService::restart(AsyncWebServerRequest * request) {
|
||||
}
|
||||
|
||||
void RestartService::partition(AsyncWebServerRequest * request) {
|
||||
const esp_partition_t * factory_partition = esp_partition_find_first(ESP_PARTITION_TYPE_APP, ESP_PARTITION_SUBTYPE_APP_FACTORY, NULL);
|
||||
const esp_partition_t * factory_partition = esp_partition_find_first(ESP_PARTITION_TYPE_APP, ESP_PARTITION_SUBTYPE_APP_FACTORY, nullptr);
|
||||
if (factory_partition) {
|
||||
esp_ota_set_boot_partition(factory_partition);
|
||||
emsesp::EMSESP::system_.store_nvs_values();
|
||||
@@ -27,7 +34,7 @@ void RestartService::partition(AsyncWebServerRequest * request) {
|
||||
request->send(200);
|
||||
return;
|
||||
}
|
||||
const esp_partition_t * ota_partition = esp_ota_get_next_update_partition(NULL);
|
||||
const esp_partition_t * ota_partition = esp_ota_get_next_update_partition(nullptr);
|
||||
if (!ota_partition) {
|
||||
request->send(400); // bad request
|
||||
return;
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
|
||||
#include <WiFi.h>
|
||||
#include <AsyncTCP.h>
|
||||
|
||||
#include <ESPAsyncWebServer.h>
|
||||
#include <SecurityManager.h>
|
||||
|
||||
#include "SecurityManager.h"
|
||||
|
||||
#define RESTART_SERVICE_PATH "/rest/restart"
|
||||
#define PARTITION_SERVICE_PATH "/rest/partition"
|
||||
@@ -14,11 +14,7 @@ class RestartService {
|
||||
public:
|
||||
RestartService(AsyncWebServer * server, SecurityManager * securityManager);
|
||||
|
||||
static void restartNow() {
|
||||
WiFi.disconnect(true);
|
||||
delay(500);
|
||||
ESP.restart();
|
||||
}
|
||||
static void restartNow();
|
||||
|
||||
private:
|
||||
void restart(AsyncWebServerRequest * request);
|
||||
|
||||
@@ -1,17 +1,13 @@
|
||||
#ifndef SecurityManager_h
|
||||
#define SecurityManager_h
|
||||
|
||||
#include <Features.h>
|
||||
#include <ArduinoJsonJWT.h>
|
||||
#include "Features.h"
|
||||
#include "ArduinoJsonJWT.h"
|
||||
|
||||
#include <ESPAsyncWebServer.h>
|
||||
#include <ESPUtils.h>
|
||||
#include <AsyncJson.h>
|
||||
#include <list>
|
||||
|
||||
#ifndef FACTORY_JWT_SECRET
|
||||
#define FACTORY_JWT_SECRET ESPUtils::defaultDeviceValue()
|
||||
#endif
|
||||
|
||||
#define ACCESS_TOKEN_PARAMATER "access_token"
|
||||
|
||||
#define AUTHORIZATION_HEADER "Authorization"
|
||||
@@ -26,28 +22,27 @@ class User {
|
||||
|
||||
public:
|
||||
User(String username, String password, bool admin)
|
||||
: username(username)
|
||||
, password(password)
|
||||
: username(std::move(username))
|
||||
, password(std::move(password))
|
||||
, admin(admin) {
|
||||
}
|
||||
};
|
||||
|
||||
class Authentication {
|
||||
public:
|
||||
User * user;
|
||||
boolean authenticated;
|
||||
User * user = nullptr;
|
||||
boolean authenticated = false;
|
||||
|
||||
public:
|
||||
Authentication(User & user)
|
||||
explicit Authentication(const User & user)
|
||||
: user(new User(user))
|
||||
, authenticated(true) {
|
||||
}
|
||||
Authentication()
|
||||
: user(nullptr)
|
||||
, authenticated(false) {
|
||||
}
|
||||
|
||||
Authentication() = default;
|
||||
|
||||
~Authentication() {
|
||||
delete (user);
|
||||
delete user;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -55,13 +50,14 @@ typedef std::function<boolean(Authentication & authentication)> AuthenticationPr
|
||||
|
||||
class AuthenticationPredicates {
|
||||
public:
|
||||
static bool NONE_REQUIRED(Authentication & authentication) {
|
||||
static bool NONE_REQUIRED(const Authentication & authentication) {
|
||||
(void)authentication;
|
||||
return true;
|
||||
};
|
||||
static bool IS_AUTHENTICATED(Authentication & authentication) {
|
||||
static bool IS_AUTHENTICATED(const Authentication & authentication) {
|
||||
return authentication.authenticated;
|
||||
};
|
||||
static bool IS_ADMIN(Authentication & authentication) {
|
||||
static bool IS_ADMIN(const Authentication & authentication) {
|
||||
return authentication.authenticated && authentication.user->admin;
|
||||
};
|
||||
};
|
||||
@@ -76,7 +72,7 @@ class SecurityManager {
|
||||
/*
|
||||
* Generate a JWT for the user provided
|
||||
*/
|
||||
virtual String generateJWT(User * user) = 0;
|
||||
virtual String generateJWT(const User * user) = 0;
|
||||
|
||||
/*
|
||||
* Check the request header for the Authorization token
|
||||
|
||||
@@ -1,15 +1,13 @@
|
||||
#include <SecuritySettingsService.h>
|
||||
|
||||
#include "../../src/emsesp_stub.hpp"
|
||||
#include "SecuritySettingsService.h"
|
||||
|
||||
SecuritySettingsService::SecuritySettingsService(AsyncWebServer * server, FS * fs)
|
||||
: _httpEndpoint(SecuritySettings::read, SecuritySettings::update, this, server, SECURITY_SETTINGS_PATH, this)
|
||||
, _fsPersistence(SecuritySettings::read, SecuritySettings::update, this, fs, SECURITY_SETTINGS_FILE)
|
||||
, _jwtHandler(FACTORY_JWT_SECRET) {
|
||||
addUpdateHandler([&](const String & originId) { configureJWTHandler(); }, false);
|
||||
addUpdateHandler([this] { configureJWTHandler(); }, false);
|
||||
server->on(GENERATE_TOKEN_PATH,
|
||||
HTTP_GET,
|
||||
wrapRequest(std::bind(&SecuritySettingsService::generateToken, this, std::placeholders::_1), AuthenticationPredicates::IS_ADMIN));
|
||||
SecuritySettingsService::wrapRequest([this](AsyncWebServerRequest * request) { generateToken(request); }, AuthenticationPredicates::IS_ADMIN));
|
||||
}
|
||||
|
||||
void SecuritySettingsService::begin() {
|
||||
@@ -30,7 +28,7 @@ Authentication SecuritySettingsService::authenticateRequest(AsyncWebServerReques
|
||||
String value = tokenParamater->value();
|
||||
return authenticateJWT(value);
|
||||
}
|
||||
return Authentication();
|
||||
return {};
|
||||
}
|
||||
|
||||
void SecuritySettingsService::configureJWTHandler() {
|
||||
@@ -43,37 +41,37 @@ Authentication SecuritySettingsService::authenticateJWT(String & jwt) {
|
||||
if (payloadDocument.is<JsonObject>()) {
|
||||
JsonObject parsedPayload = payloadDocument.as<JsonObject>();
|
||||
String username = parsedPayload["username"];
|
||||
for (User _user : _state.users) {
|
||||
for (const User & _user : _state.users) {
|
||||
if (_user.username == username && validatePayload(parsedPayload, &_user)) {
|
||||
return Authentication(_user);
|
||||
}
|
||||
}
|
||||
}
|
||||
return Authentication();
|
||||
return {};
|
||||
}
|
||||
|
||||
Authentication SecuritySettingsService::authenticate(const String & username, const String & password) {
|
||||
for (User _user : _state.users) {
|
||||
for (const User & _user : _state.users) {
|
||||
if (_user.username == username && _user.password == password) {
|
||||
return Authentication(_user);
|
||||
}
|
||||
}
|
||||
return Authentication();
|
||||
return {};
|
||||
}
|
||||
|
||||
inline void populateJWTPayload(JsonObject payload, User * user) {
|
||||
inline void populateJWTPayload(JsonObject payload, const User * user) {
|
||||
payload["username"] = user->username;
|
||||
payload["admin"] = user->admin;
|
||||
}
|
||||
|
||||
boolean SecuritySettingsService::validatePayload(JsonObject parsedPayload, User * user) {
|
||||
boolean SecuritySettingsService::validatePayload(JsonObject parsedPayload, const User * user) {
|
||||
JsonDocument jsonDocument;
|
||||
JsonObject payload = jsonDocument.to<JsonObject>();
|
||||
populateJWTPayload(payload, user);
|
||||
return payload == parsedPayload;
|
||||
}
|
||||
|
||||
String SecuritySettingsService::generateJWT(User * user) {
|
||||
String SecuritySettingsService::generateJWT(const User * user) {
|
||||
JsonDocument jsonDocument;
|
||||
JsonObject payload = jsonDocument.to<JsonObject>();
|
||||
populateJWTPayload(payload, user);
|
||||
@@ -111,11 +109,11 @@ ArJsonRequestHandlerFunction SecuritySettingsService::wrapCallback(ArJsonRequest
|
||||
|
||||
void SecuritySettingsService::generateToken(AsyncWebServerRequest * request) {
|
||||
AsyncWebParameter * usernameParam = request->getParam("username");
|
||||
for (User _user : _state.users) {
|
||||
for (const User & _user : _state.users) {
|
||||
if (_user.username == usernameParam->value()) {
|
||||
AsyncJsonResponse * response = new AsyncJsonResponse(false);
|
||||
JsonObject root = response->getRoot();
|
||||
root["token"] = generateJWT(&_user);
|
||||
auto * response = new AsyncJsonResponse(false);
|
||||
JsonObject root = response->getRoot();
|
||||
root["token"] = generateJWT(&_user);
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
return;
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
#ifndef SecuritySettingsService_h
|
||||
#define SecuritySettingsService_h
|
||||
|
||||
#include <Features.h>
|
||||
#include <SecurityManager.h>
|
||||
#include <HttpEndpoint.h>
|
||||
#include <FSPersistence.h>
|
||||
#include "Features.h"
|
||||
#include "SecurityManager.h"
|
||||
#include "HttpEndpoint.h"
|
||||
#include "FSPersistence.h"
|
||||
|
||||
#ifndef FACTORY_ADMIN_USERNAME
|
||||
#define FACTORY_ADMIN_USERNAME "admin"
|
||||
@@ -32,7 +32,6 @@ class SecuritySettings {
|
||||
public:
|
||||
String jwtSecret;
|
||||
std::vector<User> users;
|
||||
// std::list<User> users;
|
||||
|
||||
static void read(SecuritySettings & settings, JsonObject root) {
|
||||
// secret
|
||||
@@ -40,7 +39,7 @@ class SecuritySettings {
|
||||
|
||||
// users
|
||||
JsonArray users = root["users"].to<JsonArray>();
|
||||
for (User user : settings.users) {
|
||||
for (const User & user : settings.users) {
|
||||
JsonObject userRoot = users.add<JsonObject>();
|
||||
userRoot["username"] = user.username;
|
||||
userRoot["password"] = user.password;
|
||||
@@ -56,29 +55,29 @@ class SecuritySettings {
|
||||
settings.users.clear();
|
||||
if (root["users"].is<JsonArray>()) {
|
||||
for (JsonVariant user : root["users"].as<JsonArray>()) {
|
||||
settings.users.push_back(User(user["username"], user["password"], user["admin"]));
|
||||
settings.users.emplace_back(user["username"], user["password"], user["admin"]);
|
||||
}
|
||||
} else {
|
||||
settings.users.push_back(User(FACTORY_ADMIN_USERNAME, FACTORY_ADMIN_PASSWORD, true));
|
||||
settings.users.push_back(User(FACTORY_GUEST_USERNAME, FACTORY_GUEST_PASSWORD, false));
|
||||
settings.users.emplace_back(FACTORY_ADMIN_USERNAME, FACTORY_ADMIN_PASSWORD, true);
|
||||
settings.users.emplace_back(FACTORY_GUEST_USERNAME, FACTORY_GUEST_PASSWORD, false);
|
||||
}
|
||||
return StateUpdateResult::CHANGED;
|
||||
}
|
||||
};
|
||||
|
||||
class SecuritySettingsService : public StatefulService<SecuritySettings>, public SecurityManager {
|
||||
class SecuritySettingsService final : public StatefulService<SecuritySettings>, public SecurityManager {
|
||||
public:
|
||||
SecuritySettingsService(AsyncWebServer * server, FS * fs);
|
||||
|
||||
void begin();
|
||||
|
||||
// Functions to implement SecurityManager
|
||||
Authentication authenticate(const String & username, const String & password);
|
||||
Authentication authenticateRequest(AsyncWebServerRequest * request);
|
||||
String generateJWT(User * user);
|
||||
ArRequestFilterFunction filterRequest(AuthenticationPredicate predicate);
|
||||
ArRequestHandlerFunction wrapRequest(ArRequestHandlerFunction onRequest, AuthenticationPredicate predicate);
|
||||
ArJsonRequestHandlerFunction wrapCallback(ArJsonRequestHandlerFunction callback, AuthenticationPredicate predicate);
|
||||
Authentication authenticate(const String & username, const String & password) override;
|
||||
Authentication authenticateRequest(AsyncWebServerRequest * request) override;
|
||||
String generateJWT(const User * user) override;
|
||||
ArRequestFilterFunction filterRequest(AuthenticationPredicate predicate) override;
|
||||
ArRequestHandlerFunction wrapRequest(ArRequestHandlerFunction onRequest, AuthenticationPredicate predicate) override;
|
||||
ArJsonRequestHandlerFunction wrapCallback(ArJsonRequestHandlerFunction callback, AuthenticationPredicate predicate) override;
|
||||
|
||||
private:
|
||||
HttpEndpoint<SecuritySettings> _httpEndpoint;
|
||||
@@ -97,7 +96,7 @@ class SecuritySettingsService : public StatefulService<SecuritySettings>, public
|
||||
/*
|
||||
* Verify the payload is correct
|
||||
*/
|
||||
boolean validatePayload(JsonObject parsedPayload, User * user);
|
||||
boolean validatePayload(JsonObject parsedPayload, const User * user);
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,3 +1,3 @@
|
||||
#include <StatefulService.h>
|
||||
#include "StatefulService.h"
|
||||
|
||||
update_handler_id_t StateUpdateHandlerInfo::currentUpdatedHandlerId = 0;
|
||||
|
||||
@@ -23,8 +23,8 @@ using JsonStateUpdater = std::function<StateUpdateResult(JsonObject root, T & se
|
||||
template <typename T>
|
||||
using JsonStateReader = std::function<void(T & settings, JsonObject root)>;
|
||||
|
||||
typedef size_t update_handler_id_t;
|
||||
typedef std::function<void(const String & originId)> StateUpdateCallback;
|
||||
typedef size_t update_handler_id_t;
|
||||
typedef std::function<void()> StateUpdateCallback;
|
||||
|
||||
typedef struct StateUpdateHandlerInfo {
|
||||
static update_handler_id_t currentUpdatedHandlerId;
|
||||
@@ -33,7 +33,7 @@ typedef struct StateUpdateHandlerInfo {
|
||||
bool _allowRemove;
|
||||
StateUpdateHandlerInfo(StateUpdateCallback cb, bool allowRemove)
|
||||
: _id(++currentUpdatedHandlerId)
|
||||
, _cb(cb)
|
||||
, _cb(std::move(cb))
|
||||
, _allowRemove(allowRemove){};
|
||||
} StateUpdateHandlerInfo_t;
|
||||
|
||||
@@ -41,7 +41,7 @@ template <class T>
|
||||
class StatefulService {
|
||||
public:
|
||||
template <typename... Args>
|
||||
StatefulService(Args &&... args)
|
||||
explicit StatefulService(Args &&... args)
|
||||
: _state(std::forward<Args>(args)...)
|
||||
, _accessMutex(xSemaphoreCreateRecursiveMutex()) {
|
||||
}
|
||||
@@ -50,27 +50,28 @@ class StatefulService {
|
||||
if (!cb) {
|
||||
return 0;
|
||||
}
|
||||
StateUpdateHandlerInfo_t updateHandler(cb, allowRemove);
|
||||
_updateHandlers.push_back(updateHandler);
|
||||
StateUpdateHandlerInfo_t updateHandler(std::move(cb), allowRemove);
|
||||
_updateHandlers.push_back(std::move(updateHandler));
|
||||
return updateHandler._id;
|
||||
}
|
||||
|
||||
void removeUpdateHandler(update_handler_id_t id) {
|
||||
for (auto i = _updateHandlers.begin(); i != _updateHandlers.end();) {
|
||||
if ((*i)._allowRemove && (*i)._id == id) {
|
||||
i = _updateHandlers.erase(i);
|
||||
for (auto it = _updateHandlers.begin(); it != _updateHandlers.end();) {
|
||||
auto & elem = *it;
|
||||
if (elem._allowRemove && elem._id == id) {
|
||||
it = _updateHandlers.erase(it);
|
||||
} else {
|
||||
++i;
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StateUpdateResult update(std::function<StateUpdateResult(T &)> stateUpdater, const String & originId) {
|
||||
StateUpdateResult update(std::function<StateUpdateResult(T &)> stateUpdater) {
|
||||
beginTransaction();
|
||||
StateUpdateResult result = stateUpdater(_state);
|
||||
endTransaction();
|
||||
if (result == StateUpdateResult::CHANGED) {
|
||||
callUpdateHandlers(originId);
|
||||
callUpdateHandlers();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -82,12 +83,12 @@ class StatefulService {
|
||||
return result;
|
||||
}
|
||||
|
||||
StateUpdateResult update(JsonObject jsonObject, JsonStateUpdater<T> stateUpdater, const String & originId) {
|
||||
StateUpdateResult update(JsonObject jsonObject, JsonStateUpdater<T> stateUpdater) {
|
||||
beginTransaction();
|
||||
StateUpdateResult result = stateUpdater(jsonObject, _state);
|
||||
endTransaction();
|
||||
if (result == StateUpdateResult::CHANGED) {
|
||||
callUpdateHandlers(originId);
|
||||
callUpdateHandlers();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -111,9 +112,9 @@ class StatefulService {
|
||||
endTransaction();
|
||||
}
|
||||
|
||||
void callUpdateHandlers(const String & originId) {
|
||||
void callUpdateHandlers() {
|
||||
for (const StateUpdateHandlerInfo_t & updateHandler : _updateHandlers) {
|
||||
updateHandler._cb(originId);
|
||||
updateHandler._cb();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,9 +130,8 @@ class StatefulService {
|
||||
}
|
||||
|
||||
private:
|
||||
SemaphoreHandle_t _accessMutex;
|
||||
SemaphoreHandle_t _accessMutex;
|
||||
std::vector<StateUpdateHandlerInfo_t> _updateHandlers;
|
||||
// std::list<StateUpdateHandlerInfo_t> _updateHandlers;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,23 +1,30 @@
|
||||
#include <SystemStatus.h>
|
||||
#include "SystemStatus.h"
|
||||
|
||||
#include <esp_ota_ops.h>
|
||||
|
||||
#include "../../src/emsesp_stub.hpp"
|
||||
|
||||
using namespace std::placeholders; // for `_1` etc
|
||||
|
||||
SystemStatus::SystemStatus(AsyncWebServer * server, SecurityManager * securityManager) {
|
||||
server->on(SYSTEM_STATUS_SERVICE_PATH,
|
||||
HTTP_GET,
|
||||
securityManager->wrapRequest(std::bind(&SystemStatus::systemStatus, this, _1), AuthenticationPredicates::IS_AUTHENTICATED));
|
||||
securityManager->wrapRequest([this](AsyncWebServerRequest * request) { systemStatus(request); }, AuthenticationPredicates::IS_AUTHENTICATED));
|
||||
}
|
||||
|
||||
void SystemStatus::systemStatus(AsyncWebServerRequest * request) {
|
||||
emsesp::EMSESP::system_.refreshHeapMem(); // refresh free heap and max alloc heap
|
||||
|
||||
AsyncJsonResponse * response = new AsyncJsonResponse(false);
|
||||
JsonObject root = response->getRoot();
|
||||
auto * response = new AsyncJsonResponse(false);
|
||||
JsonObject root = response->getRoot();
|
||||
|
||||
root["emsesp_version"] = EMSESP_APP_VERSION;
|
||||
#ifdef EMSESP_DEBUG
|
||||
root["emsesp_version"] = std::string(EMSESP_APP_VERSION) + " (DEBUG)";
|
||||
#else
|
||||
#ifdef EMSESP_TEST
|
||||
root["emsesp_version"] = std::string(EMSESP_APP_VERSION) + " (TEST)";
|
||||
#else
|
||||
root["emsesp_version"] = EMSESP_APP_VERSION;
|
||||
#endif
|
||||
#endif
|
||||
root["esp_platform"] = EMSESP_PLATFORM;
|
||||
root["cpu_type"] = ESP.getChipModel();
|
||||
root["cpu_rev"] = ESP.getChipRevision();
|
||||
@@ -41,11 +48,11 @@ void SystemStatus::systemStatus(AsyncWebServerRequest * request) {
|
||||
root["psram_size"] = emsesp::EMSESP::system_.PSram();
|
||||
root["free_psram"] = ESP.getFreePsram() / 1024;
|
||||
}
|
||||
const esp_partition_t * partition = esp_partition_find_first(ESP_PARTITION_TYPE_APP, ESP_PARTITION_SUBTYPE_APP_FACTORY, NULL);
|
||||
const esp_partition_t * partition = esp_partition_find_first(ESP_PARTITION_TYPE_APP, ESP_PARTITION_SUBTYPE_APP_FACTORY, nullptr);
|
||||
if (partition != NULL) { // factory partition found
|
||||
root["has_loader"] = true;
|
||||
} else { // check for not empty, smaller OTA partition
|
||||
partition = esp_ota_get_next_update_partition(NULL);
|
||||
partition = esp_ota_get_next_update_partition(nullptr);
|
||||
if (partition) {
|
||||
uint64_t buffer;
|
||||
esp_partition_read(partition, 0, &buffer, 8);
|
||||
|
||||
@@ -5,10 +5,10 @@
|
||||
#include <AsyncTCP.h>
|
||||
#include <FS.h>
|
||||
#include <LittleFS.h>
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <ESPAsyncWebServer.h>
|
||||
#include <SecurityManager.h>
|
||||
|
||||
#include "SecurityManager.h"
|
||||
|
||||
#define SYSTEM_STATUS_SERVICE_PATH "/rest/systemStatus"
|
||||
|
||||
|
||||
@@ -1,20 +1,27 @@
|
||||
#include <UploadFileService.h>
|
||||
#include <esp_ota_ops.h>
|
||||
#include <esp_app_format.h>
|
||||
|
||||
#include "UploadFileService.h"
|
||||
#include "../../src/emsesp_stub.hpp"
|
||||
|
||||
using namespace std::placeholders; // for `_1` etc
|
||||
#include <esp_app_format.h>
|
||||
|
||||
static bool is_firmware = false;
|
||||
static char md5[33] = "\0";
|
||||
static String getFilenameExtension(const String & filename) {
|
||||
const auto pos = filename.lastIndexOf('.');
|
||||
if (pos != -1) {
|
||||
return filename.substring(static_cast<unsigned int>(pos) + 1);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
UploadFileService::UploadFileService(AsyncWebServer * server, SecurityManager * securityManager)
|
||||
: _securityManager(securityManager) {
|
||||
server->on(UPLOAD_FILE_PATH,
|
||||
HTTP_POST,
|
||||
std::bind(&UploadFileService::uploadComplete, this, _1),
|
||||
std::bind(&UploadFileService::handleUpload, this, _1, _2, _3, _4, _5, _6));
|
||||
: _securityManager(securityManager)
|
||||
, _is_firmware(false)
|
||||
, _md5() {
|
||||
server->on(
|
||||
UPLOAD_FILE_PATH,
|
||||
HTTP_POST,
|
||||
[this](AsyncWebServerRequest * request) { uploadComplete(request); },
|
||||
[this](AsyncWebServerRequest * request, const String & filename, size_t index, uint8_t * data, size_t len, bool final) {
|
||||
handleUpload(request, filename, index, data, len, final);
|
||||
});
|
||||
}
|
||||
|
||||
void UploadFileService::handleUpload(AsyncWebServerRequest * request, const String & filename, size_t index, uint8_t * data, size_t len, bool final) {
|
||||
@@ -28,29 +35,27 @@ void UploadFileService::handleUpload(AsyncWebServerRequest * request, const Stri
|
||||
// at init
|
||||
if (!index) {
|
||||
// check details of the file, to see if its a valid bin or json file
|
||||
std::string fname(filename.c_str());
|
||||
auto position = fname.find_last_of(".");
|
||||
std::string extension = fname.substr(position + 1);
|
||||
size_t fsize = request->contentLength();
|
||||
const String extension = getFilenameExtension(filename);
|
||||
const std::size_t filesize = request->contentLength();
|
||||
|
||||
is_firmware = false;
|
||||
if ((extension == "bin") && (fsize > 1000000)) {
|
||||
is_firmware = true;
|
||||
_is_firmware = false;
|
||||
if ((extension == "bin") && (filesize > 1000000)) {
|
||||
_is_firmware = true;
|
||||
} else if (extension == "json") {
|
||||
md5[0] = '\0'; // clear md5
|
||||
_md5[0] = '\0'; // clear md5
|
||||
} else if (extension == "md5") {
|
||||
if (len == 32) {
|
||||
memcpy(md5, data, 32);
|
||||
md5[32] = '\0';
|
||||
if (len == _md5.size() - 1) {
|
||||
std::memcpy(_md5.data(), data, _md5.size() - 1);
|
||||
_md5.back() = '\0';
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
md5[0] = '\0';
|
||||
_md5.front() = '\0';
|
||||
handleError(request, 406); // Not Acceptable - unsupported file type
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_firmware) {
|
||||
if (_is_firmware) {
|
||||
// Check firmware header, 0xE9 magic offset 0 indicates esp bin, chip offset 12: esp32:0, S2:2, C3:5
|
||||
#if CONFIG_IDF_TARGET_ESP32 // ESP32/PICO-D4
|
||||
if (len > 12 && (data[0] != 0xE9 || data[12] != 0)) {
|
||||
@@ -74,12 +79,12 @@ void UploadFileService::handleUpload(AsyncWebServerRequest * request, const Stri
|
||||
}
|
||||
#endif
|
||||
// it's firmware - initialize the ArduinoOTA updater
|
||||
if (Update.begin(fsize - sizeof(esp_image_header_t))) {
|
||||
if (strlen(md5) == 32) {
|
||||
Update.setMD5(md5);
|
||||
md5[0] = '\0';
|
||||
if (Update.begin(filesize - sizeof(esp_image_header_t))) {
|
||||
if (strlen(_md5.data()) == _md5.size() - 1) {
|
||||
Update.setMD5(_md5.data());
|
||||
_md5.front() = '\0';
|
||||
}
|
||||
request->onDisconnect(UploadFileService::handleEarlyDisconnect); // success, let's make sure we end the update if the client hangs up
|
||||
request->onDisconnect([this] { handleEarlyDisconnect(); }); // success, let's make sure we end the update if the client hangs up
|
||||
} else {
|
||||
handleError(request, 507); // failed to begin, send an error response Insufficient Storage
|
||||
return;
|
||||
@@ -90,23 +95,17 @@ void UploadFileService::handleUpload(AsyncWebServerRequest * request, const Stri
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_firmware) {
|
||||
if (len) {
|
||||
if (len != request->_tempFile.write(data, len)) { // stream the incoming chunk to the opened file
|
||||
handleError(request, 507); // 507-Insufficient Storage
|
||||
}
|
||||
if (!_is_firmware) {
|
||||
if (len && 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
|
||||
if (!request->_tempObject) {
|
||||
if (Update.write(data, len) != len) {
|
||||
handleError(request, 500);
|
||||
}
|
||||
if (final) {
|
||||
if (!Update.end(true)) {
|
||||
handleError(request, 500);
|
||||
}
|
||||
}
|
||||
} else if (!request->_tempObject) { // if we haven't delt with an error, continue with the firmware update
|
||||
if (Update.write(data, len) != len) {
|
||||
handleError(request, 500);
|
||||
return;
|
||||
}
|
||||
if (final && !Update.end(true)) {
|
||||
handleError(request, 500);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -124,7 +123,7 @@ void UploadFileService::uploadComplete(AsyncWebServerRequest * request) {
|
||||
|
||||
// check if it was a firmware upgrade
|
||||
// if no error, send the success response as a JSON
|
||||
if (is_firmware && !request->_tempObject) {
|
||||
if (_is_firmware && !request->_tempObject) {
|
||||
emsesp::EMSESP::system_.store_nvs_values();
|
||||
request->onDisconnect(RestartService::restartNow);
|
||||
AsyncWebServerResponse * response = request->beginResponse(200);
|
||||
@@ -132,10 +131,10 @@ void UploadFileService::uploadComplete(AsyncWebServerRequest * request) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (strlen(md5) == 32) {
|
||||
if (strlen(_md5.data()) == _md5.size() - 1) {
|
||||
auto * response = new AsyncJsonResponse(false);
|
||||
JsonObject root = response->getRoot();
|
||||
root["md5"] = md5;
|
||||
root["md5"] = _md5.data();
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
return;
|
||||
@@ -163,6 +162,6 @@ void UploadFileService::handleError(AsyncWebServerRequest * request, int code) {
|
||||
}
|
||||
|
||||
void UploadFileService::handleEarlyDisconnect() {
|
||||
is_firmware = false;
|
||||
_is_firmware = false;
|
||||
Update.abort();
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,16 @@
|
||||
#ifndef UploadFileService_h
|
||||
#define UploadFileService_h
|
||||
|
||||
#include <Arduino.h>
|
||||
#include "RestartService.h"
|
||||
#include "SecurityManager.h"
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <ESPAsyncWebServer.h>
|
||||
#include <LittleFS.h>
|
||||
#include <Update.h>
|
||||
#include <WiFi.h>
|
||||
|
||||
#include <LittleFS.h>
|
||||
|
||||
#include <ESPAsyncWebServer.h>
|
||||
#include <SecurityManager.h>
|
||||
#include <RestartService.h>
|
||||
#include <array>
|
||||
|
||||
#define UPLOAD_FILE_PATH "/rest/uploadFile"
|
||||
#define TEMP_FILENAME_PATH "/tmp_upload"
|
||||
@@ -20,11 +20,14 @@ class UploadFileService {
|
||||
UploadFileService(AsyncWebServer * server, SecurityManager * securityManager);
|
||||
|
||||
private:
|
||||
SecurityManager * _securityManager;
|
||||
void handleUpload(AsyncWebServerRequest * request, const String & filename, size_t index, uint8_t * data, size_t len, bool final);
|
||||
void uploadComplete(AsyncWebServerRequest * request);
|
||||
void handleError(AsyncWebServerRequest * request, int code);
|
||||
static void handleEarlyDisconnect();
|
||||
SecurityManager * _securityManager;
|
||||
bool _is_firmware;
|
||||
std::array<char, 33> _md5;
|
||||
|
||||
void handleUpload(AsyncWebServerRequest * request, const String & filename, size_t index, uint8_t * data, size_t len, bool final);
|
||||
void uploadComplete(AsyncWebServerRequest * request);
|
||||
void handleError(AsyncWebServerRequest * request, int code);
|
||||
void handleEarlyDisconnect();
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif
|
||||
@@ -1,14 +1,12 @@
|
||||
#include <WiFiScanner.h>
|
||||
|
||||
using namespace std::placeholders; // for `_1` etc
|
||||
#include "WiFiScanner.h"
|
||||
|
||||
WiFiScanner::WiFiScanner(AsyncWebServer * server, SecurityManager * securityManager) {
|
||||
server->on(SCAN_NETWORKS_SERVICE_PATH,
|
||||
HTTP_GET,
|
||||
securityManager->wrapRequest(std::bind(&WiFiScanner::scanNetworks, this, _1), AuthenticationPredicates::IS_ADMIN));
|
||||
securityManager->wrapRequest([this](AsyncWebServerRequest * request) { scanNetworks(request); }, AuthenticationPredicates::IS_ADMIN));
|
||||
server->on(LIST_NETWORKS_SERVICE_PATH,
|
||||
HTTP_GET,
|
||||
securityManager->wrapRequest(std::bind(&WiFiScanner::listNetworks, this, _1), AuthenticationPredicates::IS_ADMIN));
|
||||
securityManager->wrapRequest([this](AsyncWebServerRequest * request) { listNetworks(request); }, AuthenticationPredicates::IS_ADMIN));
|
||||
};
|
||||
|
||||
void WiFiScanner::scanNetworks(AsyncWebServerRequest * request) {
|
||||
@@ -21,18 +19,18 @@ void WiFiScanner::scanNetworks(AsyncWebServerRequest * request) {
|
||||
}
|
||||
|
||||
void WiFiScanner::listNetworks(AsyncWebServerRequest * request) {
|
||||
int numNetworks = WiFi.scanComplete();
|
||||
const int numNetworks = WiFi.scanComplete();
|
||||
if (numNetworks > -1) {
|
||||
AsyncJsonResponse * response = new AsyncJsonResponse(false);
|
||||
JsonObject root = response->getRoot();
|
||||
JsonArray networks = root["networks"].to<JsonArray>();
|
||||
for (int i = 0; i < numNetworks; i++) {
|
||||
auto * response = new AsyncJsonResponse(false);
|
||||
JsonObject root = response->getRoot();
|
||||
JsonArray networks = root["networks"].to<JsonArray>();
|
||||
for (uint8_t i = 0; i < numNetworks; i++) {
|
||||
JsonObject network = networks.add<JsonObject>();
|
||||
network["rssi"] = WiFi.RSSI(i);
|
||||
network["ssid"] = WiFi.SSID(i);
|
||||
network["bssid"] = WiFi.BSSIDstr(i);
|
||||
network["channel"] = WiFi.channel(i);
|
||||
network["encryption_type"] = (uint8_t)WiFi.encryptionType(i);
|
||||
network["encryption_type"] = static_cast<uint8_t>(WiFi.encryptionType(i));
|
||||
}
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
|
||||
#include <WiFi.h>
|
||||
#include <AsyncTCP.h>
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <ESPAsyncWebServer.h>
|
||||
#include <SecurityManager.h>
|
||||
|
||||
#include "SecurityManager.h"
|
||||
|
||||
#define SCAN_NETWORKS_SERVICE_PATH "/rest/scanNetworks"
|
||||
#define LIST_NETWORKS_SERVICE_PATH "/rest/listNetworks"
|
||||
|
||||
@@ -936,7 +936,7 @@ class Shell : public std::enable_shared_from_this<Shell>, public uuid::log::Hand
|
||||
* @param[in] message New log message, shared by all handlers.
|
||||
* @since 0.1.0
|
||||
*/
|
||||
virtual void operator<<(std::shared_ptr<uuid::log::Message> message);
|
||||
virtual void operator<<(std::shared_ptr<uuid::log::Message> message) override;
|
||||
/**
|
||||
* Get the current log level.
|
||||
*
|
||||
|
||||
@@ -17,17 +17,16 @@
|
||||
|
||||
#ifdef EMSESP_STANDALONE
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
#include <atomic>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <Network.h>
|
||||
#include "Arduino.h"
|
||||
#include "Network.h"
|
||||
|
||||
NativeConsole Serial;
|
||||
|
||||
|
||||
@@ -25,16 +25,14 @@
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <math.h>
|
||||
|
||||
#include <algorithm> // for count_if
|
||||
|
||||
#include <algorithm>
|
||||
#include <Print.h>
|
||||
#include <Printable.h>
|
||||
#include <Stream.h>
|
||||
#include <WString.h>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "WString.h"
|
||||
|
||||
typedef double double_t;
|
||||
|
||||
#define ICACHE_FLASH_ATTR
|
||||
@@ -188,6 +186,4 @@ void yield(void);
|
||||
void setup(void);
|
||||
void loop(void);
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
|
||||
#ifndef ASYNC_JSON_H_
|
||||
#define ASYNC_JSON_H_
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <ESPAsyncWebServer.h>
|
||||
|
||||
#include "ESPAsyncWebServer.h"
|
||||
|
||||
#define DYNAMIC_JSON_DOCUMENT_SIZE 1024
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#define ASYNCTCP_H_
|
||||
|
||||
#include "Arduino.h"
|
||||
|
||||
#include <functional>
|
||||
|
||||
class AsyncClient;
|
||||
|
||||
@@ -1,21 +1,19 @@
|
||||
#ifndef ESP8266React_h
|
||||
#define ESP8266React_h
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <AsyncJson.h>
|
||||
|
||||
#include <espMqttClient.h>
|
||||
#include <ESPAsyncWebServer.h>
|
||||
|
||||
#include <list>
|
||||
|
||||
#include <FS.h>
|
||||
#include <SecurityManager.h>
|
||||
#include <SecuritySettingsService.h>
|
||||
#include <StatefulService.h>
|
||||
#include <Network.h>
|
||||
#include "Arduino.h"
|
||||
#include "ArduinoJson.h"
|
||||
#include "AsyncJson.h"
|
||||
#include "ESPAsyncWebServer.h"
|
||||
#include "FS.h"
|
||||
#include "SecurityManager.h"
|
||||
#include "SecuritySettingsService.h"
|
||||
#include "StatefulService.h"
|
||||
#include "Network.h"
|
||||
|
||||
#include <espMqttClient.h>
|
||||
|
||||
#define AP_SETTINGS_FILE "/config/apSettings.json"
|
||||
#define MQTT_SETTINGS_FILE "/config/mqttSettings.json"
|
||||
@@ -59,21 +57,22 @@ class DummySettings {
|
||||
uint16_t publish_time_sensor = 10;
|
||||
uint16_t publish_time_heartbeat = 60;
|
||||
|
||||
String hostname = "ems-esp";
|
||||
String jwtSecret = "ems-esp";
|
||||
String ssid = "ems-esp";
|
||||
String password = "ems-esp";
|
||||
String bssid = "";
|
||||
String localIP = "";
|
||||
String gatewayIP = "";
|
||||
String subnetMask = "";
|
||||
bool staticIPConfig = false;
|
||||
String dnsIP1 = "";
|
||||
String dnsIP2 = "";
|
||||
bool enableIPv6 = false;
|
||||
bool enableMDNS = true;
|
||||
bool enableCORS = false;
|
||||
String CORSOrigin = "*";
|
||||
String hostname = "ems-esp";
|
||||
String jwtSecret = "ems-esp";
|
||||
String ssid = "ems-esp";
|
||||
String password = "ems-esp";
|
||||
String bssid = "";
|
||||
String localIP = "";
|
||||
String gatewayIP = "";
|
||||
String subnetMask = "";
|
||||
bool staticIPConfig = false;
|
||||
String dnsIP1 = "";
|
||||
String dnsIP2 = "";
|
||||
bool enableIPv6 = false;
|
||||
bool enableMDNS = true;
|
||||
bool enableCORS = false;
|
||||
String CORSOrigin = "*";
|
||||
uint8_t tx_power = 0;
|
||||
|
||||
static void read(DummySettings & settings, JsonObject root){};
|
||||
static void read(DummySettings & settings){};
|
||||
@@ -103,7 +102,6 @@ class ESP8266React {
|
||||
, _securitySettingsService(server, fs){};
|
||||
|
||||
void begin() {
|
||||
// initialize mqtt
|
||||
_mqttClient = new espMqttClient();
|
||||
};
|
||||
void loop(){};
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
#define _ESPAsyncWebServer_H_
|
||||
|
||||
#include "Arduino.h"
|
||||
#include "AsyncTCP.h"
|
||||
|
||||
#include <functional>
|
||||
#include <AsyncTCP.h>
|
||||
#include <ArduinoJson.h>
|
||||
|
||||
class AsyncWebServer;
|
||||
|
||||
@@ -21,9 +21,10 @@
|
||||
#ifndef FS_H
|
||||
#define FS_H
|
||||
|
||||
#include "Arduino.h"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <Arduino.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
#include <dirent.h>
|
||||
@@ -53,7 +54,7 @@ class File : public Stream {
|
||||
int peek() override;
|
||||
void flush() override;
|
||||
size_t read(uint8_t * buf, size_t size);
|
||||
size_t readBytes(char * buffer, size_t length) {
|
||||
size_t readBytes(char * buffer, size_t length) override {
|
||||
return read((uint8_t *)buffer, length);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#ifndef FSPersistence_h
|
||||
#define FSPersistence_h
|
||||
|
||||
#include <StatefulService.h>
|
||||
#include <FS.h>
|
||||
#include "StatefulService.h"
|
||||
#include "FS.h"
|
||||
|
||||
template <class T>
|
||||
class FSPersistence {
|
||||
@@ -40,7 +40,7 @@ class FSPersistence {
|
||||
|
||||
void enableUpdateHandler() {
|
||||
if (!_updateHandlerId) {
|
||||
_updateHandlerId = _statefulService->addUpdateHandler([&](const String & originId) { writeToFS(); });
|
||||
_updateHandlerId = _statefulService->addUpdateHandler([this] { writeToFS(); });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,11 +3,11 @@
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include <AsyncJson.h>
|
||||
#include <ESPAsyncWebServer.h>
|
||||
#include "AsyncJson.h"
|
||||
#include "ESPAsyncWebServer.h"
|
||||
|
||||
#include <SecurityManager.h>
|
||||
#include <StatefulService.h>
|
||||
#include "SecurityManager.h"
|
||||
#include "StatefulService.h"
|
||||
|
||||
#define HTTP_ENDPOINT_ORIGIN_ID "http"
|
||||
|
||||
|
||||
@@ -17,9 +17,9 @@
|
||||
*/
|
||||
#ifdef ENV_NATIVE
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <FS.h>
|
||||
#include <LittleFS.h>
|
||||
#include "Arduino.h"
|
||||
#include "FS.h"
|
||||
#include "LittleFS.h"
|
||||
|
||||
fs::LittleFSFS LittleFS;
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
#ifndef Network_h
|
||||
#define Network_h
|
||||
|
||||
#include <Arduino.h>
|
||||
#include "Arduino.h"
|
||||
|
||||
#include <functional>
|
||||
#include <IPAddress.h>
|
||||
|
||||
@@ -11,6 +12,21 @@
|
||||
#define WIFI_AP WIFI_MODE_AP
|
||||
#define WIFI_AP_STA WIFI_MODE_APSTA
|
||||
|
||||
typedef enum {
|
||||
WIFI_POWER_19_5dBm = 78, // 19.5dBm
|
||||
WIFI_POWER_19dBm = 76, // 19dBm
|
||||
WIFI_POWER_18_5dBm = 74, // 18.5dBm
|
||||
WIFI_POWER_17dBm = 68, // 17dBm
|
||||
WIFI_POWER_15dBm = 60, // 15dBm
|
||||
WIFI_POWER_13dBm = 52, // 13dBm
|
||||
WIFI_POWER_11dBm = 44, // 11dBm
|
||||
WIFI_POWER_8_5dBm = 34, // 8.5dBm
|
||||
WIFI_POWER_7dBm = 28, // 7dBm
|
||||
WIFI_POWER_5dBm = 20, // 5dBm
|
||||
WIFI_POWER_2dBm = 8, // 2dBm
|
||||
WIFI_POWER_MINUS_1dBm = -4 // -1dBm
|
||||
} wifi_power_t;
|
||||
|
||||
typedef enum {
|
||||
ETH_CLOCK_GPIO0_IN = 0, /*!< RMII clock input to GPIO0 */
|
||||
ETH_CLOCK_GPIO0_OUT = 1, /*!< RMII clock output from GPIO0 */
|
||||
|
||||
@@ -28,7 +28,8 @@
|
||||
#include <string>
|
||||
|
||||
#include <Printable.h>
|
||||
#include <WString.h>
|
||||
|
||||
#include "WString.h"
|
||||
|
||||
int vsnprintf_P(char * str, size_t size, const char * format, va_list ap);
|
||||
|
||||
|
||||
@@ -1,18 +1,15 @@
|
||||
#ifndef SecurityManager_h
|
||||
#define SecurityManager_h
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <Features.h>
|
||||
#include <ESPAsyncWebServer.h>
|
||||
#include <AsyncJson.h>
|
||||
#include "Arduino.h"
|
||||
#include "Features.h"
|
||||
#include "ESPAsyncWebServer.h"
|
||||
#include "AsyncJson.h"
|
||||
|
||||
#include <list>
|
||||
|
||||
#ifndef FACTORY_JWT_SECRET
|
||||
#define FACTORY_JWT_SECRET ESPUtils::defaultDeviceValue()
|
||||
#endif
|
||||
|
||||
#define FACTORY_JWT_SECRET "ems-esp"
|
||||
#define ACCESS_TOKEN_PARAMATER "access_token"
|
||||
|
||||
#define AUTHORIZATION_HEADER "Authorization"
|
||||
#define AUTHORIZATION_HEADER_PREFIX "Bearer "
|
||||
#define AUTHORIZATION_HEADER_PREFIX_LEN 7
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#ifdef EMSESP_STANDALONE
|
||||
|
||||
#include <SecuritySettingsService.h>
|
||||
#include "SecuritySettingsService.h"
|
||||
|
||||
User ADMIN_USER = User(FACTORY_ADMIN_USERNAME, FACTORY_ADMIN_PASSWORD, true);
|
||||
|
||||
@@ -11,7 +11,7 @@ SecuritySettingsService::~SecuritySettingsService() {
|
||||
}
|
||||
|
||||
ArRequestFilterFunction SecuritySettingsService::filterRequest(AuthenticationPredicate predicate) {
|
||||
return [this, predicate](AsyncWebServerRequest * request) { return true; };
|
||||
return [predicate](AsyncWebServerRequest * request) { return true; };
|
||||
}
|
||||
|
||||
// Return the admin user on all request - disabling security features
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
#ifndef SecuritySettingsService_h
|
||||
#define SecuritySettingsService_h
|
||||
|
||||
#include <Features.h>
|
||||
#include <SecurityManager.h>
|
||||
#include <HttpEndpoint.h>
|
||||
#include <FSPersistence.h>
|
||||
#include "Features.h"
|
||||
#include "SecurityManager.h"
|
||||
#include "HttpEndpoint.h"
|
||||
#include "FSPersistence.h"
|
||||
|
||||
#ifndef FACTORY_ADMIN_USERNAME
|
||||
#define FACTORY_ADMIN_USERNAME "admin"
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
#include <StatefulService.h>
|
||||
#include "StatefulService.h"
|
||||
|
||||
update_handler_id_t StateUpdateHandlerInfo::currentUpdatedHandlerId = 0;
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
#ifndef StatefulService_h
|
||||
#define StatefulService_h
|
||||
|
||||
#include <Arduino.h>
|
||||
#include "Arduino.h"
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
|
||||
#include <list>
|
||||
@@ -20,8 +21,8 @@ using JsonStateUpdater = std::function<StateUpdateResult(JsonObject root, T & se
|
||||
template <typename T>
|
||||
using JsonStateReader = std::function<void(T & settings, JsonObject root)>;
|
||||
|
||||
typedef size_t update_handler_id_t;
|
||||
typedef std::function<void(const String & originId)> StateUpdateCallback;
|
||||
typedef size_t update_handler_id_t;
|
||||
typedef std::function<void()> StateUpdateCallback;
|
||||
|
||||
typedef struct StateUpdateHandlerInfo {
|
||||
static update_handler_id_t currentUpdatedHandlerId;
|
||||
@@ -68,12 +69,12 @@ class StatefulService {
|
||||
}
|
||||
}
|
||||
|
||||
StateUpdateResult update(std::function<StateUpdateResult(T &)> stateUpdater, const String & originId) {
|
||||
StateUpdateResult update(std::function<StateUpdateResult(T &)> stateUpdater) {
|
||||
beginTransaction();
|
||||
StateUpdateResult result = stateUpdater(_state);
|
||||
endTransaction();
|
||||
if (result == StateUpdateResult::CHANGED) {
|
||||
callUpdateHandlers(originId);
|
||||
callUpdateHandlers();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -85,12 +86,12 @@ class StatefulService {
|
||||
return result;
|
||||
}
|
||||
|
||||
StateUpdateResult update(JsonObject jsonObject, JsonStateUpdater<T> stateUpdater, const String & originId) {
|
||||
StateUpdateResult update(JsonObject jsonObject, JsonStateUpdater<T> stateUpdater) {
|
||||
beginTransaction();
|
||||
StateUpdateResult result = stateUpdater(jsonObject, _state);
|
||||
endTransaction();
|
||||
if (result == StateUpdateResult::CHANGED) {
|
||||
callUpdateHandlers(originId);
|
||||
callUpdateHandlers();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -114,9 +115,9 @@ class StatefulService {
|
||||
endTransaction();
|
||||
}
|
||||
|
||||
void callUpdateHandlers(const String & originId) {
|
||||
void callUpdateHandlers() {
|
||||
for (const StateUpdateHandlerInfo_t & updateHandler : _updateHandlers) {
|
||||
updateHandler._cb(originId);
|
||||
updateHandler._cb();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
|
||||
#ifdef EMSESP_STANDALONE
|
||||
|
||||
#include <Arduino.h>
|
||||
#include "Arduino.h"
|
||||
#include "WString.h"
|
||||
|
||||
/*
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
#ifndef EMSESP_EMSUART_H
|
||||
#define EMSESP_EMSUART_H
|
||||
|
||||
#include <Arduino.h>
|
||||
#include "Arduino.h"
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
|
||||
@@ -225,7 +225,7 @@ let network_settings = {
|
||||
password: 'myPassword',
|
||||
hostname: 'ems-esp',
|
||||
nosleep: true,
|
||||
tx_power: 20,
|
||||
tx_power: 0,
|
||||
bandwidth20: false,
|
||||
static_ip_config: false,
|
||||
enableMDNS: true,
|
||||
|
||||
@@ -155,7 +155,7 @@ network_settings = {
|
||||
password: 'myPassword',
|
||||
hostname: 'ems-esp',
|
||||
nosleep: true,
|
||||
tx_power: 20,
|
||||
tx_power: 0,
|
||||
bandwidth20: false,
|
||||
static_ip_config: false,
|
||||
enableMDNS: true,
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
[common]
|
||||
; custom build flags to use in my_build_flags
|
||||
; -DEMSESP_WIFI_TWEAK ; experimental WiFi tweaks for stability
|
||||
; -DEMSESP_UART_DEBUG ; debugging UART
|
||||
; -DEMSESP_DEBUG ; enables DEBUG to the log. Will generate a lot of extra traffic on Console and Syslog
|
||||
; -DEMSESP_DEFAULT_BOARD_PROFILE=\"NODEMCU\" ; hard code the default board name
|
||||
|
||||
@@ -13,8 +13,6 @@ extra_configs =
|
||||
|
||||
[common]
|
||||
core_build_flags =
|
||||
-D ARDUINO_ARCH_ESP32=1
|
||||
-D ESP32=1
|
||||
-O2
|
||||
-std=gnu++17
|
||||
|
||||
@@ -50,7 +48,7 @@ extra_scripts =
|
||||
scripts/rename_fw.py
|
||||
|
||||
[espressi32_base_tasmota]
|
||||
; use Tasmota's libary which removes some libs (like mbedtsl) and increases available heap
|
||||
; use Tasmota's library which removes some libs (like mbedtsl) and increases available heap
|
||||
; platform = https://github.com/tasmota/platform-espressif32.git ; latest development
|
||||
; latest release with WiFi_secure.h, Arduino 2.0.14
|
||||
platform = https://github.com/tasmota/platform-espressif32/releases/download/2024.01.01/platform-espressif32.zip
|
||||
@@ -68,11 +66,9 @@ extra_scripts =
|
||||
|
||||
[env]
|
||||
monitor_speed = 115200
|
||||
monitor_filters = esp32_exception_decoder
|
||||
upload_speed = 921600
|
||||
build_type = release
|
||||
lib_ldf_mode = chain+
|
||||
; board_build.flash_mode = qio
|
||||
|
||||
check_tool = cppcheck, clangtidy
|
||||
check_severity = high, medium
|
||||
@@ -169,14 +165,14 @@ build_flags =
|
||||
platform = native
|
||||
build_flags =
|
||||
-DARDUINOJSON_ENABLE_STD_STRING=1 -DARDUINOJSON_ENABLE_PROGMEM=1 -DARDUINOJSON_ENABLE_ARDUINO_STRING -DARDUINOJSON_USE_DOUBLE=0
|
||||
-DEMSESP_DEBUG -DEMSESP_STANDALONE -DEMSESP_TEST -D__linux__
|
||||
-DEMSESP_DEBUG -DEMSESP_STANDALONE -DEMSESP_TEST
|
||||
-DEMSESP_DEFAULT_LOCALE=\"en\" -DEMSESP_DEFAULT_TX_MODE=8 -DEMSESP_DEFAULT_VERSION=\"3.6.5-dev\" -DEMSESP_DEFAULT_BOARD_PROFILE=\"S32\"
|
||||
-lpthread
|
||||
-D__linux__
|
||||
-std=gnu++11 -Og -ggdb
|
||||
build_src_flags =
|
||||
-Wall -Wextra -Werror
|
||||
-Wno-unused-parameter -Wno-sign-compare
|
||||
; -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
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
# for calling dos upload from Window WSL2 Linux, because serial ports are not mapped yet
|
||||
# example file
|
||||
Import('env')
|
||||
from subprocess import call
|
||||
|
||||
def upload(source, target, env):
|
||||
print("bin file: " + str(target[0]))
|
||||
call(["cmd.exe", "/c", "c:\\Users\\paul\\OneDrive\\Desktop\\ems-esp32.bat"])
|
||||
|
||||
env.AddPostAction("$BUILD_DIR/${PROGNAME}.bin", [upload])
|
||||
@@ -324,37 +324,35 @@ void AnalogSensor::loop() {
|
||||
bool AnalogSensor::update(uint8_t gpio, const std::string & name, double offset, double factor, uint8_t uom, int8_t type, bool deleted) {
|
||||
// first see if we can find the sensor in our customization list
|
||||
bool found_sensor = false;
|
||||
EMSESP::webCustomizationService.update(
|
||||
[&](WebCustomization & settings) {
|
||||
for (auto & AnalogCustomization : settings.analogCustomizations) {
|
||||
if (AnalogCustomization.type == AnalogType::COUNTER || AnalogCustomization.type >= AnalogType::DIGITAL_OUT) {
|
||||
Command::erase_command(EMSdevice::DeviceType::ANALOGSENSOR, AnalogCustomization.name.c_str());
|
||||
}
|
||||
if (AnalogCustomization.gpio == gpio) {
|
||||
found_sensor = true; // found the record
|
||||
// see if it's marked for deletion
|
||||
if (deleted) {
|
||||
EMSESP::nvs_.remove(AnalogCustomization.name.c_str());
|
||||
LOG_DEBUG("Removing analog sensor GPIO %02d", gpio);
|
||||
settings.analogCustomizations.remove(AnalogCustomization);
|
||||
} else {
|
||||
// update existing record
|
||||
if (name != AnalogCustomization.name) {
|
||||
EMSESP::nvs_.remove(AnalogCustomization.name.c_str());
|
||||
}
|
||||
AnalogCustomization.name = name;
|
||||
AnalogCustomization.offset = offset;
|
||||
AnalogCustomization.factor = factor;
|
||||
AnalogCustomization.uom = uom;
|
||||
AnalogCustomization.type = type;
|
||||
LOG_DEBUG("Customizing existing analog GPIO %02d", gpio);
|
||||
}
|
||||
return StateUpdateResult::CHANGED; // persist the change
|
||||
}
|
||||
EMSESP::webCustomizationService.update([&](WebCustomization & settings) {
|
||||
for (auto & AnalogCustomization : settings.analogCustomizations) {
|
||||
if (AnalogCustomization.type == AnalogType::COUNTER || AnalogCustomization.type >= AnalogType::DIGITAL_OUT) {
|
||||
Command::erase_command(EMSdevice::DeviceType::ANALOGSENSOR, AnalogCustomization.name.c_str());
|
||||
}
|
||||
return StateUpdateResult::UNCHANGED;
|
||||
},
|
||||
"local");
|
||||
if (AnalogCustomization.gpio == gpio) {
|
||||
found_sensor = true; // found the record
|
||||
// see if it's marked for deletion
|
||||
if (deleted) {
|
||||
EMSESP::nvs_.remove(AnalogCustomization.name.c_str());
|
||||
LOG_DEBUG("Removing analog sensor GPIO %02d", gpio);
|
||||
settings.analogCustomizations.remove(AnalogCustomization);
|
||||
} else {
|
||||
// update existing record
|
||||
if (name != AnalogCustomization.name) {
|
||||
EMSESP::nvs_.remove(AnalogCustomization.name.c_str());
|
||||
}
|
||||
AnalogCustomization.name = name;
|
||||
AnalogCustomization.offset = offset;
|
||||
AnalogCustomization.factor = factor;
|
||||
AnalogCustomization.uom = uom;
|
||||
AnalogCustomization.type = type;
|
||||
LOG_DEBUG("Customizing existing analog GPIO %02d", gpio);
|
||||
}
|
||||
return StateUpdateResult::CHANGED; // persist the change
|
||||
}
|
||||
}
|
||||
return StateUpdateResult::UNCHANGED;
|
||||
});
|
||||
|
||||
// if the sensor exists and we're using HA, delete the old HA record
|
||||
if (found_sensor && Mqtt::ha_enabled()) {
|
||||
@@ -363,20 +361,18 @@ bool AnalogSensor::update(uint8_t gpio, const std::string & name, double offset,
|
||||
|
||||
// we didn't find it, it's new, so create and store it in the customization list
|
||||
if (!found_sensor) {
|
||||
EMSESP::webCustomizationService.update(
|
||||
[&](WebCustomization & settings) {
|
||||
auto newSensor = AnalogCustomization();
|
||||
newSensor.gpio = gpio;
|
||||
newSensor.name = name;
|
||||
newSensor.offset = offset;
|
||||
newSensor.factor = factor;
|
||||
newSensor.uom = uom;
|
||||
newSensor.type = type;
|
||||
settings.analogCustomizations.push_back(newSensor);
|
||||
LOG_DEBUG("Adding new customization for analog sensor GPIO %02d", gpio);
|
||||
return StateUpdateResult::CHANGED; // persist the change
|
||||
},
|
||||
"local");
|
||||
EMSESP::webCustomizationService.update([&](WebCustomization & settings) {
|
||||
auto newSensor = AnalogCustomization();
|
||||
newSensor.gpio = gpio;
|
||||
newSensor.name = name;
|
||||
newSensor.offset = offset;
|
||||
newSensor.factor = factor;
|
||||
newSensor.uom = uom;
|
||||
newSensor.type = type;
|
||||
settings.analogCustomizations.push_back(newSensor);
|
||||
LOG_DEBUG("Adding new customization for analog sensor GPIO %02d", gpio);
|
||||
return StateUpdateResult::CHANGED; // persist the change
|
||||
});
|
||||
}
|
||||
|
||||
// reloads the sensors in the customizations file into the sensors list
|
||||
@@ -819,20 +815,18 @@ bool AnalogSensor::command_setvalue(const char * value, const int8_t gpio) {
|
||||
return false;
|
||||
}
|
||||
if (oldoffset != sensor.offset()) {
|
||||
EMSESP::webCustomizationService.update(
|
||||
[&](WebCustomization & settings) {
|
||||
for (auto & AnalogCustomization : settings.analogCustomizations) {
|
||||
if (AnalogCustomization.gpio == sensor.gpio() && AnalogCustomization.type == sensor.type()) {
|
||||
AnalogCustomization.offset = sensor.offset();
|
||||
}
|
||||
EMSESP::webCustomizationService.update([&](WebCustomization & settings) {
|
||||
for (auto & AnalogCustomization : settings.analogCustomizations) {
|
||||
if (AnalogCustomization.gpio == sensor.gpio() && AnalogCustomization.type == sensor.type()) {
|
||||
AnalogCustomization.offset = sensor.offset();
|
||||
}
|
||||
if (sensor.type() == AnalogType::COUNTER || (sensor.type() == AnalogType::DIGITAL_OUT && sensor.uom() > 0)) {
|
||||
return StateUpdateResult::UNCHANGED; // temporary change
|
||||
} else {
|
||||
return StateUpdateResult::CHANGED; // persist the change
|
||||
}
|
||||
},
|
||||
"local");
|
||||
}
|
||||
if (sensor.type() == AnalogType::COUNTER || (sensor.type() == AnalogType::DIGITAL_OUT && sensor.uom() > 0)) {
|
||||
return StateUpdateResult::UNCHANGED; // temporary change
|
||||
} else {
|
||||
return StateUpdateResult::CHANGED; // persist the change
|
||||
}
|
||||
});
|
||||
publish_sensor(sensor);
|
||||
changed_ = true;
|
||||
}
|
||||
|
||||
@@ -184,12 +184,10 @@ static void setup_commands(std::shared_ptr<Commands> & commands) {
|
||||
shell.enter_password(F_(new_password_prompt2), [password1](Shell & shell, bool completed, const std::string & password2) {
|
||||
if (completed) {
|
||||
if (password1 == password2) {
|
||||
to_app(shell).esp8266React.getSecuritySettingsService()->update(
|
||||
[&](SecuritySettings & securitySettings) {
|
||||
securitySettings.jwtSecret = password2.c_str();
|
||||
return StateUpdateResult::CHANGED;
|
||||
},
|
||||
"local");
|
||||
to_app(shell).esp8266React.getSecuritySettingsService()->update([&](SecuritySettings & securitySettings) {
|
||||
securitySettings.jwtSecret = password2.c_str();
|
||||
return StateUpdateResult::CHANGED;
|
||||
});
|
||||
shell.println("Admin password updated");
|
||||
} else {
|
||||
shell.println("Passwords do not match");
|
||||
@@ -258,12 +256,10 @@ static void setup_commands(std::shared_ptr<Commands> & commands) {
|
||||
shell.println("The network connection will be reset...");
|
||||
Shell::loop_all();
|
||||
delay(1000); // wait a second
|
||||
to_app(shell).esp8266React.getNetworkSettingsService()->update(
|
||||
[&](NetworkSettings & networkSettings) {
|
||||
networkSettings.hostname = arguments.front().c_str();
|
||||
return StateUpdateResult::CHANGED;
|
||||
},
|
||||
"local");
|
||||
to_app(shell).esp8266React.getNetworkSettingsService()->update([&](NetworkSettings & networkSettings) {
|
||||
networkSettings.hostname = arguments.front().c_str();
|
||||
return StateUpdateResult::CHANGED;
|
||||
});
|
||||
});
|
||||
|
||||
commands->add_command(ShellContext::MAIN,
|
||||
@@ -293,21 +289,19 @@ static void setup_commands(std::shared_ptr<Commands> & commands) {
|
||||
if (arguments.size() == 2 && Helpers::toLower(arguments.back()) == "nvs") {
|
||||
to_app(shell).nvs_.putString("boot", board_profile.c_str());
|
||||
}
|
||||
to_app(shell).webSettingsService.update(
|
||||
[&](WebSettings & settings) {
|
||||
settings.board_profile = board_profile.c_str();
|
||||
settings.led_gpio = data[0];
|
||||
settings.dallas_gpio = data[1];
|
||||
settings.rx_gpio = data[2];
|
||||
settings.tx_gpio = data[3];
|
||||
settings.pbutton_gpio = data[4];
|
||||
settings.phy_type = data[5];
|
||||
settings.eth_power = data[6]; // can be -1
|
||||
settings.eth_phy_addr = data[7];
|
||||
settings.eth_clock_mode = data[8];
|
||||
return StateUpdateResult::CHANGED;
|
||||
},
|
||||
"local");
|
||||
to_app(shell).webSettingsService.update([&](WebSettings & settings) {
|
||||
settings.board_profile = board_profile.c_str();
|
||||
settings.led_gpio = data[0];
|
||||
settings.dallas_gpio = data[1];
|
||||
settings.rx_gpio = data[2];
|
||||
settings.tx_gpio = data[3];
|
||||
settings.pbutton_gpio = data[4];
|
||||
settings.phy_type = data[5];
|
||||
settings.eth_power = data[6]; // can be -1
|
||||
settings.eth_phy_addr = data[7];
|
||||
settings.eth_clock_mode = data[8];
|
||||
return StateUpdateResult::CHANGED;
|
||||
});
|
||||
shell.printfln("Loaded board profile %s", board_profile.c_str());
|
||||
to_app(shell).system_.network_init(true);
|
||||
});
|
||||
@@ -320,13 +314,11 @@ static void setup_commands(std::shared_ptr<Commands> & commands) {
|
||||
[](Shell & shell, const std::vector<std::string> & arguments) {
|
||||
uint8_t device_id = Helpers::hextoint(arguments.front().c_str());
|
||||
if ((device_id == 0x0B) || (device_id == 0x0D) || (device_id == 0x0A) || (device_id == 0x0F) || (device_id == 0x12)) {
|
||||
to_app(shell).webSettingsService.update(
|
||||
[&](WebSettings & settings) {
|
||||
settings.ems_bus_id = device_id;
|
||||
shell.printfln(F_(bus_id_fmt), settings.ems_bus_id);
|
||||
return StateUpdateResult::CHANGED;
|
||||
},
|
||||
"local");
|
||||
to_app(shell).webSettingsService.update([&](WebSettings & settings) {
|
||||
settings.ems_bus_id = device_id;
|
||||
shell.printfln(F_(bus_id_fmt), settings.ems_bus_id);
|
||||
return StateUpdateResult::CHANGED;
|
||||
});
|
||||
} else {
|
||||
shell.println("Must be 0B, 0D, 0A, 0E, 0F, or 48 - 4D");
|
||||
}
|
||||
@@ -342,13 +334,11 @@ static void setup_commands(std::shared_ptr<Commands> & commands) {
|
||||
[](Shell & shell, const std::vector<std::string> & arguments) {
|
||||
uint8_t tx_mode = std::strtol(arguments[0].c_str(), nullptr, 10);
|
||||
// save the tx_mode
|
||||
to_app(shell).webSettingsService.update(
|
||||
[&](WebSettings & settings) {
|
||||
settings.tx_mode = tx_mode;
|
||||
shell.printfln(F_(tx_mode_fmt), settings.tx_mode);
|
||||
return StateUpdateResult::CHANGED;
|
||||
},
|
||||
"local");
|
||||
to_app(shell).webSettingsService.update([&](WebSettings & settings) {
|
||||
settings.tx_mode = tx_mode;
|
||||
shell.printfln(F_(tx_mode_fmt), settings.tx_mode);
|
||||
return StateUpdateResult::CHANGED;
|
||||
});
|
||||
to_app(shell).uart_init();
|
||||
});
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ class EMSESPConsole : public EMSESPShell {
|
||||
#endif
|
||||
~EMSESPConsole() override;
|
||||
|
||||
std::string console_name();
|
||||
std::string console_name() override;
|
||||
|
||||
private:
|
||||
#ifndef EMSESP_STANDALONE
|
||||
|
||||
@@ -884,12 +884,10 @@ bool Solar::set_SM10MaxFlow(const char * value, const int8_t id) {
|
||||
return false;
|
||||
}
|
||||
maxFlow_ = (flow * 10);
|
||||
EMSESP::webSettingsService.update(
|
||||
[&](WebSettings & settings) {
|
||||
settings.solar_maxflow = maxFlow_;
|
||||
return StateUpdateResult::CHANGED;
|
||||
},
|
||||
"local");
|
||||
EMSESP::webSettingsService.update([&](WebSettings & settings) {
|
||||
settings.solar_maxflow = maxFlow_;
|
||||
return StateUpdateResult::CHANGED;
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -1492,8 +1492,10 @@ void EMSESP::start() {
|
||||
bool factory_settings = false;
|
||||
#endif
|
||||
|
||||
esp8266React.begin(); // loads core system services settings (network, mqtt, ap, ntp etc)
|
||||
webLogService.begin(); // start web log service. now we can start capturing logs to the web log
|
||||
|
||||
esp8266React.begin(); // loads core system services settings (network, mqtt, ap, ntp etc)
|
||||
|
||||
nvs_.begin("ems-esp", false, "nvs");
|
||||
|
||||
LOG_INFO("Starting EMS-ESP version %s", EMSESP_APP_VERSION); // welcome message
|
||||
|
||||
@@ -38,8 +38,7 @@
|
||||
#endif
|
||||
#include <Preferences.h>
|
||||
|
||||
#include <ESP8266React.h>
|
||||
|
||||
#include "ESP8266React.h"
|
||||
#include "web/WebStatusService.h"
|
||||
#include "web/WebDataService.h"
|
||||
#include "web/WebSettingsService.h"
|
||||
|
||||
@@ -23,8 +23,8 @@
|
||||
#include "temperaturesensor.h"
|
||||
#include "version.h"
|
||||
#include "default_settings.h"
|
||||
|
||||
#include <ESP8266React.h>
|
||||
#include "helpers.h"
|
||||
#include "ESP8266React.h"
|
||||
|
||||
#include <uuid/log.h>
|
||||
|
||||
|
||||
@@ -69,7 +69,7 @@ MAKE_WORD_TRANSLATION(fetch_cmd, "refresh all EMS values", "Lese alle EMS-Werte
|
||||
MAKE_WORD_TRANSLATION(restart_cmd, "restart EMS-ESP", "Neustart", "opnieuw opstarten", "", "uruchom ponownie EMS-ESP", "restart EMS-ESP", "redémarrer EMS-ESP", "EMS-ESPyi yeniden başlat", "riavvia EMS-ESP", "reštart EMS-ESP") // TODO translate
|
||||
MAKE_WORD_TRANSLATION(watch_cmd, "watch incoming telegrams", "Watch auf eingehende Telegramme", "inkomende telegrammen bekijken", "", "obserwuj przyczodzące telegramy", "se innkommende telegrammer", "", "Gelen telegramları", "guardare i telegrammi in arrivo", "sledovať prichádzajúce telegramy") // TODO translate
|
||||
MAKE_WORD_TRANSLATION(publish_cmd, "publish all to MQTT", "Publiziere MQTT", "publiceer alles naar MQTT", "", "opublikuj wszystko na MQTT", "Publiser alt til MQTT", "", "Hepsini MQTTye gönder", "pubblica tutto su MQTT", "zverejniť všetko na MQTT") // TODO translate
|
||||
MAKE_WORD_TRANSLATION(system_info_cmd, "show system status", "Zeige System-Status", "toon systeemstatus", "", "pokaż status systemu", "vis system status", "", "Sistem Durumunu Göster", "visualizza stati di sistema", "zobraziť stav systému") // TODO translate
|
||||
MAKE_WORD_TRANSLATION(system_info_cmd, "show system info", "Zeige System-Status", "toon systeemstatus", "", "pokaż status systemu", "vis system status", "", "Sistem Durumunu Göster", "visualizza stati di sistema", "zobraziť stav systému") // TODO translate
|
||||
MAKE_WORD_TRANSLATION(schedule_cmd, "enable schedule item", "Aktiviere Zeitplan", "activeer tijdschema item", "", "aktywuj wybrany harmonogram", "", "", "program öğesini etkinleştir", "abilitare l'elemento programmato", "povoliť položku plánu") // TODO translate
|
||||
MAKE_WORD_TRANSLATION(entity_cmd, "set custom value on ems", "Sende eigene Entitäten zu EMS", "verstuur custom waarde naar EMS", "", "wyślij własną wartość na EMS", "", "", "emp üzerinde özel değer ayarla", "imposta valori personalizzati su EMS", "nastaviť vlastnú hodnotu na ems") // TODO translate
|
||||
MAKE_WORD_TRANSLATION(commands_response, "get response", "Hole Antwort", "Verzoek om antwoord", "", "uzyskaj odpowiedź", "", "", "gelen cevap", "", "získať odpoveď") // TODO translate
|
||||
|
||||
@@ -201,9 +201,9 @@ void Mqtt::show_mqtt(uuid::console::Shell & shell) {
|
||||
for (const auto & mqtt_subfunction : mqtt_subfunctions_) {
|
||||
shell.printfln(" %s/%s", Mqtt::base().c_str(), mqtt_subfunction.topic_.c_str());
|
||||
}
|
||||
shell.println();
|
||||
|
||||
shell.println();
|
||||
shell.println();
|
||||
}
|
||||
|
||||
#if defined(EMSESP_TEST)
|
||||
@@ -388,7 +388,7 @@ void Mqtt::start() {
|
||||
// add the 'publish' command ('call system publish' in console or via API)
|
||||
Command::add(EMSdevice::DeviceType::SYSTEM, F_(publish), System::command_publish, FL_(publish_cmd));
|
||||
|
||||
// create last will topic with the base prefixed. It has to be static because asyncmqttclient destroys the reference
|
||||
// create last will topic with the base prefixed. It has to be static because the client destroys the reference
|
||||
static char will_topic[MQTT_TOPIC_MAX_SIZE];
|
||||
if (!Mqtt::base().empty()) {
|
||||
snprintf(will_topic, MQTT_TOPIC_MAX_SIZE, "%s/status", Mqtt::base().c_str());
|
||||
@@ -466,6 +466,7 @@ void Mqtt::on_disconnect(espMqttClientTypes::DisconnectReason reason) {
|
||||
return;
|
||||
}
|
||||
connecting_ = false;
|
||||
|
||||
if (reason == espMqttClientTypes::DisconnectReason::TCP_DISCONNECTED) {
|
||||
LOG_WARNING("MQTT disconnected: TCP");
|
||||
} else if (reason == espMqttClientTypes::DisconnectReason::MQTT_UNACCEPTABLE_PROTOCOL_VERSION) {
|
||||
@@ -483,6 +484,7 @@ void Mqtt::on_disconnect(espMqttClientTypes::DisconnectReason reason) {
|
||||
} else {
|
||||
LOG_WARNING("MQTT disconnected: code %d", reason);
|
||||
}
|
||||
|
||||
mqttClient_->clearQueue(true);
|
||||
}
|
||||
|
||||
|
||||
100
src/system.cpp
100
src/system.cpp
@@ -211,8 +211,7 @@ bool System::command_syslog_level(const char * value, const int8_t id) {
|
||||
changed = true;
|
||||
}
|
||||
return StateUpdateResult::CHANGED;
|
||||
},
|
||||
"local");
|
||||
});
|
||||
if (changed) {
|
||||
EMSESP::system_.syslog_init();
|
||||
}
|
||||
@@ -277,11 +276,12 @@ void System::system_restart() {
|
||||
|
||||
// saves all settings
|
||||
void System::wifi_reconnect() {
|
||||
LOG_INFO("WiFi reconnecting...");
|
||||
EMSESP::esp8266React.getNetworkSettingsService()->read(
|
||||
[](NetworkSettings & networkSettings) { LOG_INFO("WiFi reconnecting to SSID '%s'...", networkSettings.ssid.c_str()); });
|
||||
Shell::loop_all();
|
||||
delay(1000); // wait a second
|
||||
EMSESP::webSettingsService.save(); // local settings
|
||||
EMSESP::esp8266React.getNetworkSettingsService()->callUpdateHandlers("local"); // in case we've changed ssid or password
|
||||
delay(1000); // wait a second
|
||||
EMSESP::webSettingsService.save(); // save local settings
|
||||
EMSESP::esp8266React.getNetworkSettingsService()->callUpdateHandlers(); // in case we've changed ssid or password
|
||||
}
|
||||
|
||||
// format the FS. Wipes everything.
|
||||
@@ -388,33 +388,6 @@ void System::reload_settings() {
|
||||
});
|
||||
}
|
||||
|
||||
// adjust WiFi settings
|
||||
// this for problem solving mesh and connection issues, and also get EMS bus-powered more stable by lowering power
|
||||
void System::wifi_tweak() {
|
||||
#if defined(EMSESP_WIFI_TWEAK)
|
||||
// Default Tx Power is 80 = 20dBm <-- default
|
||||
// WIFI_POWER_19_5dBm = 78,// 19.5dBm
|
||||
// WIFI_POWER_19dBm = 76,// 19dBm
|
||||
// WIFI_POWER_18_5dBm = 74,// 18.5dBm
|
||||
// WIFI_POWER_17dBm = 68,// 17dBm
|
||||
// WIFI_POWER_15dBm = 60,// 15dBm
|
||||
// WIFI_POWER_13dBm = 52,// 13dBm
|
||||
// WIFI_POWER_11dBm = 44,// 11dBm
|
||||
// WIFI_POWER_8_5dBm = 34,// 8.5dBm
|
||||
// WIFI_POWER_7dBm = 28,// 7dBm
|
||||
// WIFI_POWER_5dBm = 20,// 5dBm
|
||||
// WIFI_POWER_2dBm = 8,// 2dBm
|
||||
// WIFI_POWER_MINUS_1dBm = -4// -1dBm
|
||||
wifi_power_t p1 = WiFi.getTxPower();
|
||||
(void)WiFi.setTxPower(WIFI_POWER_17dBm);
|
||||
wifi_power_t p2 = WiFi.getTxPower();
|
||||
bool s1 = WiFi.getSleep();
|
||||
WiFi.setSleep(false); // turn off sleep - WIFI_PS_NONE
|
||||
bool s2 = WiFi.getSleep();
|
||||
LOG_DEBUG("Adjusting WiFi - Tx power %d->%d, Sleep %d->%d", p1, p2, s1, s2);
|
||||
#endif
|
||||
}
|
||||
|
||||
// check for valid ESP32 pins. This is very dependent on which ESP32 board is being used.
|
||||
// Typically you can't use 1, 6-11, 20, 24, 28-31 and 40+
|
||||
// we allow 0 as it has a special function on the NodeMCU apparently
|
||||
@@ -472,7 +445,7 @@ void System::start() {
|
||||
|
||||
// button single click
|
||||
void System::button_OnClick(PButton & b) {
|
||||
LOG_DEBUG("Button pressed - single click");
|
||||
LOG_NOTICE("Button pressed - single click - show settings folders");
|
||||
|
||||
#if defined(EMSESP_TEST)
|
||||
#ifndef EMSESP_STANDALONE
|
||||
@@ -483,20 +456,19 @@ void System::button_OnClick(PButton & b) {
|
||||
|
||||
// button double click
|
||||
void System::button_OnDblClick(PButton & b) {
|
||||
LOG_DEBUG("Button pressed - double click - reconnect");
|
||||
LOG_NOTICE("Button pressed - double click - wifi reconnect");
|
||||
EMSESP::system_.wifi_reconnect();
|
||||
}
|
||||
|
||||
// button long press
|
||||
void System::button_OnLongPress(PButton & b) {
|
||||
LOG_DEBUG("Button pressed - long press");
|
||||
LOG_NOTICE("Button pressed - long press");
|
||||
}
|
||||
|
||||
// button indefinite press
|
||||
void System::button_OnVLongPress(PButton & b) {
|
||||
LOG_DEBUG("Button pressed - very long press");
|
||||
LOG_NOTICE("Button pressed - very long press - factory reset");
|
||||
#ifndef EMSESP_STANDALONE
|
||||
LOG_WARNING("Performing factory reset...");
|
||||
EMSESP::esp8266React.factoryReset();
|
||||
#endif
|
||||
}
|
||||
@@ -977,6 +949,8 @@ void System::show_system(uuid::console::Shell & shell) {
|
||||
shell.printfln(" SSID: %s", WiFi.SSID().c_str());
|
||||
shell.printfln(" BSSID: %s", WiFi.BSSIDstr().c_str());
|
||||
shell.printfln(" RSSI: %d dBm (%d %%)", WiFi.RSSI(), wifi_quality(WiFi.RSSI()));
|
||||
char result[10];
|
||||
shell.printfln(" TxPower: %s dBm", emsesp::Helpers::render_value(result, (double)(WiFi.getTxPower() / 4), 1));
|
||||
shell.printfln(" MAC address: %s", WiFi.macAddress().c_str());
|
||||
shell.printfln(" Hostname: %s", WiFi.getHostname());
|
||||
shell.printfln(" IPv4 address: %s/%s", uuid::printable_to_string(WiFi.localIP()).c_str(), uuid::printable_to_string(WiFi.subnetMask()).c_str());
|
||||
@@ -1110,15 +1084,13 @@ bool System::check_upgrade(bool factory_settings) {
|
||||
|
||||
version::Semver200_version settings_version(settingsVersion);
|
||||
|
||||
#if defined(EMSESP_DEBUG)
|
||||
if (!missing_version) {
|
||||
LOG_INFO("Checking version (settings has %d.%d.%d-%s)...",
|
||||
settings_version.major(),
|
||||
settings_version.minor(),
|
||||
settings_version.patch(),
|
||||
settings_version.prerelease().c_str());
|
||||
LOG_DEBUG("Checking version upgrade (settings file is v%d.%d.%d-%s)",
|
||||
settings_version.major(),
|
||||
settings_version.minor(),
|
||||
settings_version.patch(),
|
||||
settings_version.prerelease().c_str());
|
||||
}
|
||||
#endif
|
||||
|
||||
if (factory_settings) {
|
||||
return false; // fresh install, do nothing
|
||||
@@ -1135,14 +1107,23 @@ bool System::check_upgrade(bool factory_settings) {
|
||||
|
||||
// if we're coming from 3.4.4 or 3.5.0b14 which had no version stored then we need to apply new settings
|
||||
if (missing_version) {
|
||||
LOG_DEBUG("Setting MQTT Entity ID format to v3.4 format");
|
||||
EMSESP::esp8266React.getMqttSettingsService()->update(
|
||||
[&](MqttSettings & mqttSettings) {
|
||||
mqttSettings.entity_format = 0; // use old Entity ID format from v3.4
|
||||
return StateUpdateResult::CHANGED;
|
||||
},
|
||||
"local");
|
||||
LOG_INFO("Setting MQTT Entity ID format to v3.4 format");
|
||||
EMSESP::esp8266React.getMqttSettingsService()->update([&](MqttSettings & mqttSettings) {
|
||||
mqttSettings.entity_format = 0; // use old Entity ID format from v3.4
|
||||
return StateUpdateResult::CHANGED;
|
||||
});
|
||||
}
|
||||
|
||||
// Network Settings Wifi tx_power is now using the value * 4.
|
||||
EMSESP::esp8266React.getNetworkSettingsService()->update([&](NetworkSettings & networkSettings) {
|
||||
if (networkSettings.tx_power == 20) {
|
||||
networkSettings.tx_power = WIFI_POWER_19_5dBm; // use 19.5 as we don't have 20 anymore
|
||||
LOG_INFO("Setting WiFi TX Power to Auto");
|
||||
return StateUpdateResult::CHANGED;
|
||||
}
|
||||
return StateUpdateResult::UNCHANGED;
|
||||
});
|
||||
|
||||
} else if (this_version < settings_version) {
|
||||
// need downgrade
|
||||
LOG_NOTICE("Downgrading to version %d.%d.%d-%s", this_version.major(), this_version.minor(), this_version.patch(), this_version.prerelease().c_str());
|
||||
@@ -1153,12 +1134,10 @@ bool System::check_upgrade(bool factory_settings) {
|
||||
|
||||
// if we did a change, set the new version and reboot
|
||||
if (save_version) {
|
||||
EMSESP::webSettingsService.update(
|
||||
[&](WebSettings & settings) {
|
||||
settings.version = EMSESP_APP_VERSION;
|
||||
return StateUpdateResult::CHANGED;
|
||||
},
|
||||
"local");
|
||||
EMSESP::webSettingsService.update([&](WebSettings & settings) {
|
||||
settings.version = EMSESP_APP_VERSION;
|
||||
return StateUpdateResult::CHANGED;
|
||||
});
|
||||
return true; // need reboot
|
||||
}
|
||||
|
||||
@@ -1257,6 +1236,7 @@ bool System::command_info(const char * value, const int8_t id, JsonObject output
|
||||
if (WiFi.status() == WL_CONNECTED && !settings.bssid.isEmpty()) {
|
||||
node["BSSID"] = "set";
|
||||
}
|
||||
node["TxPower setting"] = settings.tx_power;
|
||||
node["static ip config"] = settings.staticIPConfig;
|
||||
node["enable IPv6"] = settings.enableIPv6;
|
||||
node["low bandwidth"] = settings.bandwidth20;
|
||||
@@ -1580,8 +1560,8 @@ std::string System::reset_reason(uint8_t cpu) const {
|
||||
|
||||
// set NTP status
|
||||
void System::ntp_connected(bool b) {
|
||||
if (b != ntp_connected_) {
|
||||
LOG_INFO(b ? "NTP connected" : "NTP disconnected"); // if changed report it
|
||||
if (b != ntp_connected_ && !b) {
|
||||
LOG_WARNING("NTP disconnected"); // if turned off report it
|
||||
}
|
||||
|
||||
ntp_connected_ = b;
|
||||
|
||||
@@ -75,7 +75,6 @@ class System {
|
||||
bool upload_status();
|
||||
void show_mem(const char * note);
|
||||
void reload_settings();
|
||||
void wifi_tweak();
|
||||
void syslog_init();
|
||||
bool check_upgrade(bool factory_settings);
|
||||
bool check_restore();
|
||||
|
||||
@@ -21,16 +21,15 @@
|
||||
|
||||
#include <string>
|
||||
#include <deque>
|
||||
#include <uuid/log.h>
|
||||
|
||||
// UART drivers
|
||||
#if defined(ESP32)
|
||||
#include "uart/emsuart_esp32.h"
|
||||
#elif defined(EMSESP_STANDALONE)
|
||||
#include <emsuart_standalone.h>
|
||||
#include "emsuart_standalone.h"
|
||||
#endif
|
||||
|
||||
#include <uuid/log.h>
|
||||
|
||||
#include "helpers.h"
|
||||
|
||||
#define MAX_RX_TELEGRAMS 10 // size of Rx queue
|
||||
|
||||
@@ -302,31 +302,29 @@ bool TemperatureSensor::update(const std::string & id, const std::string & name,
|
||||
sensor.set_offset(offset);
|
||||
|
||||
// store the new name and offset in our configuration
|
||||
EMSESP::webCustomizationService.update(
|
||||
[&](WebCustomization & settings) {
|
||||
// look it up to see if it exists
|
||||
bool found = false;
|
||||
for (auto & SensorCustomization : settings.sensorCustomizations) {
|
||||
if (SensorCustomization.id == id) {
|
||||
SensorCustomization.name = name;
|
||||
SensorCustomization.offset = offset;
|
||||
found = true;
|
||||
LOG_DEBUG("Customizing existing sensor ID %s", id.c_str());
|
||||
break;
|
||||
}
|
||||
EMSESP::webCustomizationService.update([&](WebCustomization & settings) {
|
||||
// look it up to see if it exists
|
||||
bool found = false;
|
||||
for (auto & SensorCustomization : settings.sensorCustomizations) {
|
||||
if (SensorCustomization.id == id) {
|
||||
SensorCustomization.name = name;
|
||||
SensorCustomization.offset = offset;
|
||||
found = true;
|
||||
LOG_DEBUG("Customizing existing sensor ID %s", id.c_str());
|
||||
break;
|
||||
}
|
||||
if (!found) {
|
||||
SensorCustomization newSensor = SensorCustomization();
|
||||
newSensor.id = id;
|
||||
newSensor.name = name;
|
||||
newSensor.offset = offset;
|
||||
settings.sensorCustomizations.push_back(newSensor);
|
||||
LOG_DEBUG("Adding new customization for sensor ID %s", id.c_str());
|
||||
}
|
||||
sensor.ha_registered = false; // it's changed so we may need to recreate the HA config
|
||||
return StateUpdateResult::CHANGED;
|
||||
},
|
||||
"local");
|
||||
}
|
||||
if (!found) {
|
||||
SensorCustomization newSensor = SensorCustomization();
|
||||
newSensor.id = id;
|
||||
newSensor.name = name;
|
||||
newSensor.offset = offset;
|
||||
settings.sensorCustomizations.push_back(newSensor);
|
||||
LOG_DEBUG("Adding new customization for sensor ID %s", id.c_str());
|
||||
}
|
||||
sensor.ha_registered = false; // it's changed so we may need to recreate the HA config
|
||||
return StateUpdateResult::CHANGED;
|
||||
});
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1978,20 +1978,22 @@ void Test::listDir(fs::FS & fs, const char * dirname, uint8_t levels) {
|
||||
File file = root.openNextFile();
|
||||
while (file) {
|
||||
if (file.isDirectory()) {
|
||||
Serial.print(" DIR : ");
|
||||
Serial.print(" DIR: ");
|
||||
Serial.println(file.name());
|
||||
if (levels) {
|
||||
listDir(fs, file.name(), levels - 1);
|
||||
}
|
||||
Serial.println();
|
||||
} else {
|
||||
Serial.print(" FILE: ");
|
||||
Serial.print(" ");
|
||||
Serial.print(file.name());
|
||||
Serial.print("\tSIZE: ");
|
||||
Serial.println(file.size());
|
||||
Serial.print(" (");
|
||||
Serial.print(file.size());
|
||||
Serial.println(" bytes)");
|
||||
}
|
||||
file = root.openNextFile();
|
||||
}
|
||||
Serial.println();
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -1 +1 @@
|
||||
#define EMSESP_APP_VERSION "3.6.5-test.13"
|
||||
#define EMSESP_APP_VERSION "3.6.5-test.14"
|
||||
|
||||
@@ -18,8 +18,6 @@
|
||||
|
||||
#include "emsesp.h"
|
||||
|
||||
using namespace std::placeholders; // for `_1` etc
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
uint32_t WebAPIService::api_count_ = 0;
|
||||
@@ -27,17 +25,26 @@ uint16_t WebAPIService::api_fails_ = 0;
|
||||
|
||||
WebAPIService::WebAPIService(AsyncWebServer * server, SecurityManager * securityManager)
|
||||
: _securityManager(securityManager)
|
||||
, _apiHandler("/api", std::bind(&WebAPIService::webAPIService_post, this, _1, _2)) { // for POSTS, must use 'Content-Type: application/json' in header
|
||||
server->on("/api", HTTP_GET, std::bind(&WebAPIService::webAPIService_get, this, _1)); // for GETS
|
||||
, _apiHandler(EMSESP_API_SERVICE_PATH, [this](AsyncWebServerRequest * request, JsonVariant json) { webAPIService_post(request, json); }) { // for POSTs
|
||||
server->on(EMSESP_API_SERVICE_PATH, HTTP_GET, [this](AsyncWebServerRequest * request) { webAPIService_get(request); }); // for GETs
|
||||
server->addHandler(&_apiHandler);
|
||||
|
||||
// for settings
|
||||
server->on(GET_SETTINGS_PATH, HTTP_GET, securityManager->wrapRequest(std::bind(&WebAPIService::getSettings, this, _1), AuthenticationPredicates::IS_ADMIN));
|
||||
server->on(GET_SETTINGS_PATH,
|
||||
HTTP_GET,
|
||||
securityManager->wrapRequest([this](AsyncWebServerRequest * request) { getSettings(request); }, AuthenticationPredicates::IS_ADMIN));
|
||||
|
||||
server->on(GET_CUSTOMIZATIONS_PATH,
|
||||
HTTP_GET,
|
||||
securityManager->wrapRequest(std::bind(&WebAPIService::getCustomizations, this, _1), AuthenticationPredicates::IS_ADMIN));
|
||||
server->on(GET_SCHEDULE_PATH, HTTP_GET, securityManager->wrapRequest(std::bind(&WebAPIService::getSchedule, this, _1), AuthenticationPredicates::IS_ADMIN));
|
||||
server->on(GET_ENTITIES_PATH, HTTP_GET, securityManager->wrapRequest(std::bind(&WebAPIService::getEntities, this, _1), AuthenticationPredicates::IS_ADMIN));
|
||||
securityManager->wrapRequest([this](AsyncWebServerRequest * request) { getCustomizations(request); }, AuthenticationPredicates::IS_ADMIN));
|
||||
|
||||
server->on(GET_SCHEDULE_PATH,
|
||||
HTTP_GET,
|
||||
securityManager->wrapRequest([this](AsyncWebServerRequest * request) { getSchedule(request); }, AuthenticationPredicates::IS_ADMIN));
|
||||
|
||||
server->on(GET_ENTITIES_PATH,
|
||||
HTTP_GET,
|
||||
securityManager->wrapRequest([this](AsyncWebServerRequest * request) { getEntities(request); }, AuthenticationPredicates::IS_ADMIN));
|
||||
}
|
||||
|
||||
// HTTP GET
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user