mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2026-09-14 06:04:06 +00:00
Compare commits
174
Commits
core3
...
ef11879dc0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ef11879dc0 | ||
|
|
e3d9f03097 | ||
|
|
f90b7e9d91 | ||
|
|
e489c0f77d | ||
|
|
99dd04cbf5 | ||
|
|
4dac338e2c | ||
|
|
ff47722693 | ||
|
|
6aed13a9b2 | ||
|
|
dbf54f09f5 | ||
|
|
204ad1f8c9 | ||
|
|
928e1e7a69 | ||
|
|
a6ea91439b | ||
|
|
7735c9e8f2 | ||
|
|
a22cab95fa | ||
|
|
629c20ee11 | ||
|
|
5d75583dba | ||
|
|
2ec8450b78 | ||
|
|
f911950aee | ||
|
|
d1b97daff0 | ||
|
|
7caa5a1ba0 | ||
|
|
3ba7099987 | ||
|
|
0a50a9b1fa | ||
|
|
6291bdfa5d | ||
|
|
81fa7c592e | ||
|
|
1b345f788d | ||
|
|
7a709ba497 | ||
|
|
b4548ed257 | ||
|
|
5f548c3287 | ||
|
|
9d3a49a0de | ||
|
|
355029ca9f | ||
|
|
889ddee86e | ||
|
|
e43226af7e | ||
|
|
09e1ee9914 | ||
|
|
dc623cb32a | ||
|
|
c1e514d8bf | ||
|
|
936cbb5427 | ||
|
|
566167c7d9 | ||
|
|
cc2865ebdd | ||
|
|
6a1f4c9941 | ||
|
|
13c6ed9e06 | ||
|
|
c711d1c6de | ||
|
|
71824d1fc7 | ||
|
|
aaea00f943 | ||
|
|
f01b0fc3a7 | ||
|
|
ad62c79a40 | ||
|
|
e3b36a4084 | ||
|
|
ce2b28651d | ||
|
|
518ac0b4b4 | ||
|
|
c5e2ba024c | ||
|
|
c765b5c9b6 | ||
|
|
84d67c0946 | ||
|
|
04f99688eb | ||
|
|
0991ed467e | ||
|
|
4d8d3f0d46 | ||
|
|
db5ccb018f | ||
|
|
4d4d716e5f | ||
|
|
94926b0ca7 | ||
|
|
98a6199a69 | ||
|
|
416fecfaed | ||
|
|
319dfe6944 | ||
|
|
3d0399b3a8 | ||
|
|
8eb1a47b5a | ||
|
|
fe972db8c5 | ||
|
|
be224265f9 | ||
|
|
5fcd9bfd2d | ||
|
|
69b6af4069 | ||
|
|
cdade5f999 | ||
|
|
97af64c53b | ||
|
|
bcb47912c0 | ||
|
|
badd8f2241 | ||
|
|
5aed596140 | ||
|
|
015da730bf | ||
|
|
0b47e5ec05 | ||
|
|
b0255d726b | ||
|
|
fce46f7613 | ||
|
|
c3cd4ac6b6 | ||
|
|
91955689a2 | ||
|
|
89e88135bc | ||
|
|
42f86cd12b | ||
|
|
6efc7113b3 | ||
|
|
48d425e0b6 | ||
|
|
b18b27d2f6 | ||
|
|
5d992e5a70 | ||
|
|
80e48430f5 | ||
|
|
cedafbd462 | ||
|
|
9d3eaa5ff7 | ||
|
|
bc6351cc22 | ||
|
|
9b0ee07ee1 | ||
|
|
4fe64fbff8 | ||
|
|
51b689d91b | ||
|
|
f5189b4d09 | ||
|
|
55b882cec2 | ||
|
|
c16f12990a | ||
|
|
ed4b247d28 | ||
|
|
afb8efec53 | ||
|
|
84b23f19e5 | ||
|
|
668d59a2cd | ||
|
|
88f258d7e9 | ||
|
|
c45441e3be | ||
|
|
2994535158 | ||
|
|
fdfb560cf9 | ||
|
|
d56c5897fa | ||
|
|
cec0fbc95d | ||
|
|
8f118e07a3 | ||
|
|
b05ae6dbbb | ||
|
|
469ca8bf05 | ||
|
|
edd1677526 | ||
|
|
c363e0e2c8 | ||
|
|
d397a14b41 | ||
|
|
822096b635 | ||
|
|
0defebfdb6 | ||
|
|
574c4e45d5 | ||
|
|
48c118fed7 | ||
|
|
ef4335156c | ||
|
|
4d4269cdf7 | ||
|
|
95a8b78d1d | ||
|
|
982b0f304e | ||
|
|
1d9a0b6257 | ||
|
|
52486ed5b4 | ||
|
|
73f2634acc | ||
|
|
2a604008ff | ||
|
|
cd0a51f843 | ||
|
|
34cd93b7be | ||
|
|
d47915d815 | ||
|
|
cb97de7706 | ||
|
|
dbc5a73583 | ||
|
|
4d25c69c84 | ||
|
|
b430866bbb | ||
|
|
553752a7ca | ||
|
|
9c992d8fda | ||
|
|
e4d127db67 | ||
|
|
5b41217a67 | ||
|
|
d913824b35 | ||
|
|
ae3443c637 | ||
|
|
50c34c13f0 | ||
|
|
d8fab910a8 | ||
|
|
4231192991 | ||
|
|
b47e7d1dd6 | ||
|
|
fb55327373 | ||
|
|
4aab9e095a | ||
|
|
12ae358434 | ||
|
|
e22b82e913 | ||
|
|
285361c09f | ||
|
|
16caff789d | ||
|
|
835a8762f6 | ||
|
|
90f0c9c01b | ||
|
|
bde9ab53a1 | ||
|
|
901721fb99 | ||
|
|
dac1e1a69c | ||
|
|
1b2371c67a | ||
|
|
0f396200a2 | ||
|
|
1956ff8386 | ||
|
|
3a81c70018 | ||
|
|
7212439f1b | ||
|
|
6f7f343abd | ||
|
|
42e6e31476 | ||
|
|
96cf019f0a | ||
|
|
59b0417297 | ||
|
|
2369277a9e | ||
|
|
e166db8b70 | ||
|
|
36884867f7 | ||
|
|
7fb8eb3592 | ||
|
|
e0f59a4c31 | ||
|
|
7eba8acbb4 | ||
|
|
d0e6a603bb | ||
|
|
36b4dd55a7 | ||
|
|
341a4d4e1d | ||
|
|
a61745b710 | ||
|
|
b1ebd81ca2 | ||
|
|
6487a3be70 | ||
|
|
2eb804de0b | ||
|
|
1ff53e6ac7 | ||
|
|
9cab3b480e | ||
|
|
bacd3faac2 |
@@ -12,10 +12,20 @@ This release is based on the latest Espressif/Arduino core version 3. It brings
|
|||||||
- Commands Service that can be called via MQTT or API or used in the Scheduler Service
|
- Commands Service that can be called via MQTT or API or used in the Scheduler Service
|
||||||
- option to disable factory reset [#3150](https://github.com/emsesp/EMS-ESP32/issues/3150)
|
- option to disable factory reset [#3150](https://github.com/emsesp/EMS-ESP32/issues/3150)
|
||||||
- TLS support with 4MB boards without PSRAM
|
- TLS support with 4MB boards without PSRAM
|
||||||
|
- added eth_10mbit to Network Settings, which forces 10BASE-T half-duplex & autonegotiation off, halving the PHY’s power draw. It’s off by default as it will cause issues on managed switches with a port forced to 100Mb [#3213](https://github.com/emsesp/EMS-ESP32/pull/3213)
|
||||||
|
- cleaner MD5 and firmware uploading in WebUI [#3222](https://github.com/emsesp/EMS-ESP32/issues/3222)
|
||||||
|
|
||||||
## Fixed
|
## Fixed
|
||||||
|
|
||||||
- shunting yard show json
|
- shunting yard show json
|
||||||
|
- `system/sendmail` called from a Command or the Scheduler computed its value twice, which stripped the quotes from literal text and then failed on characters like `!`. Also shunting yard treated a `?` inside a quoted string as a ternary, so text like `"too hot?"` returned nothing
|
||||||
|
- memory leak when using different timezones for ems-esp and thermostat[#3184](https://github.com/emsesp/EMS-ESP32/issues/3184)
|
||||||
|
- LED stayed off on a healthy system when "Disable LED" was unchecked
|
||||||
|
- Ethernet MAC address changed with the new SDK, breaking DHCP reservations (currently disabled)
|
||||||
|
- "IPv4 nameserver" showed an IPv6 address when IPv6 was in use
|
||||||
|
- HA Discovery warning on Uptime after EMS-ESP boot due to NTP not ready
|
||||||
|
- hc/control setting for UI800 thermostats [#3181](https://github.com/emsesp/EMS-ESP32/discussions/3181)
|
||||||
|
- solar module switches [#3223](https://github.com/emsesp/EMS-ESP32/issues/3223)
|
||||||
|
|
||||||
## Changed
|
## Changed
|
||||||
|
|
||||||
@@ -24,4 +34,10 @@ This release is based on the latest Espressif/Arduino core version 3. It brings
|
|||||||
- network fallback to AP only after start [#3090](https://github.com/emsesp/EMS-ESP32/issues/3090)
|
- network fallback to AP only after start [#3090](https://github.com/emsesp/EMS-ESP32/issues/3090)
|
||||||
- replaced Web async-validator with custom validator and toast with native snackbar to reduce bundle size
|
- replaced Web async-validator with custom validator and toast with native snackbar to reduce bundle size
|
||||||
- Gateway and Connect devices are shown in the Devices page, but disabled [3126](https://github.com/emsesp/EMS-ESP32/discussions/3126)
|
- Gateway and Connect devices are shown in the Devices page, but disabled [3126](https://github.com/emsesp/EMS-ESP32/discussions/3126)
|
||||||
|
- show control setting only for master thermostats (0x10) [#3173](https://github.com/emsesp/EMS-ESP32/issues/3173)
|
||||||
|
- remove devices without entities not listed in 0x07 telegram
|
||||||
|
- changes to the Dialogs in Dashboard page, removed the run icon in the Commands page. Renamed Execute to Run.
|
||||||
|
- set `None` for undefined values in HA `val_tpl`
|
||||||
|
- changes in WebUI - remove multiple calls systemInfo endpoint, Vewrsion pages checks for internet connection [#3195](https://github.com/emsesp/EMS-ESP32/pull/3195)
|
||||||
|
- multiple optimization in network, power down WiFi when Ethernet and WiFi nosleep is instant when selected [#3213](https://github.com/emsesp/EMS-ESP32/pull/3213)
|
||||||
|
|
||||||
|
|||||||
@@ -113,7 +113,7 @@ CPPFLAGS += $(DEFINES) $(DEFAULTS) $(INCLUDE)
|
|||||||
CPPFLAGS += -ggdb -g3 -MMD
|
CPPFLAGS += -ggdb -g3 -MMD
|
||||||
CPPFLAGS += -flto=auto
|
CPPFLAGS += -flto=auto
|
||||||
CPPFLAGS += -Wall -Wextra -Werror
|
CPPFLAGS += -Wall -Wextra -Werror
|
||||||
CPPFLAGS += -Wno-unused-parameter -Wno-missing-braces -Wno-vla-cxx-extension -Wno-switch-enum -Wno-unused-lambda-capture
|
CPPFLAGS += -Wno-unused-parameter -Wno-missing-braces -Wno-vla-cxx-extension -Wno-switch-enum
|
||||||
CPPFLAGS += -ffunction-sections -fdata-sections -fno-exceptions -fno-rtti -fno-threadsafe-statics
|
CPPFLAGS += -ffunction-sections -fdata-sections -fno-exceptions -fno-rtti -fno-threadsafe-statics
|
||||||
CPPFLAGS += -Os -DNDEBUG
|
CPPFLAGS += -Os -DNDEBUG
|
||||||
|
|
||||||
|
|||||||
+2
-1
@@ -16,7 +16,8 @@
|
|||||||
"enableMDNS": true,
|
"enableMDNS": true,
|
||||||
"enableCORS": false,
|
"enableCORS": false,
|
||||||
"CORSOrigin": "*",
|
"CORSOrigin": "*",
|
||||||
"tx_power": 0
|
"tx_power": 0,
|
||||||
|
"eth_10mbit": false
|
||||||
},
|
},
|
||||||
"AP": {
|
"AP": {
|
||||||
"provision_mode": 2,
|
"provision_mode": 2,
|
||||||
|
|||||||
+521
-481
File diff suppressed because it is too large
Load Diff
+494
-481
File diff suppressed because it is too large
Load Diff
+14
-14
@@ -23,40 +23,40 @@
|
|||||||
"standalone-devcontainer": "concurrently -c \"auto\" \"typesafe-i18n\" \"pnpm:mock-rest\" \"vite --host\""
|
"standalone-devcontainer": "concurrently -c \"auto\" \"typesafe-i18n\" \"pnpm:mock-rest\" \"vite --host\""
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@alova/adapter-xhr": "2.3.1",
|
"@alova/adapter-xhr": "2.3.3",
|
||||||
"@emotion/react": "^11.14.0",
|
"@emotion/react": "^11.14.0",
|
||||||
"@emotion/styled": "^11.14.1",
|
"@emotion/styled": "^11.14.1",
|
||||||
"@mui/icons-material": "^9.2.0",
|
"@mui/icons-material": "^9.4.0",
|
||||||
"@mui/material": "^9.2.0",
|
"@mui/material": "^9.4.0",
|
||||||
"@table-library/react-table-library": "4.1.15",
|
"@table-library/react-table-library": "4.1.15",
|
||||||
"alova": "^3.5.3",
|
"alova": "^3.5.5",
|
||||||
"jwt-decode": "^4.0.0",
|
"jwt-decode": "^4.0.0",
|
||||||
"preact": "^10.29.8",
|
"preact": "^10.29.8",
|
||||||
"react": "^19.2.8",
|
"react": "^19.2.8",
|
||||||
"react-dom": "^19.2.8",
|
"react-dom": "^19.2.8",
|
||||||
"react-icons": "^5.7.0",
|
"react-icons": "^5.7.0",
|
||||||
"react-router": "^8.3.0",
|
"react-router": "^8.3.1",
|
||||||
"typesafe-i18n": "^5.27.1"
|
"typesafe-i18n": "^5.27.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/js": "^10.0.1",
|
"@eslint/js": "^10.0.1",
|
||||||
"@preact/preset-vite": "^2.10.6",
|
"@preact/preset-vite": "^2.10.6",
|
||||||
"@trivago/prettier-plugin-sort-imports": "^6.0.2",
|
"@trivago/prettier-plugin-sort-imports": "^6.0.2",
|
||||||
"@types/node": "^26.1.2",
|
"@types/node": "^26.5.0",
|
||||||
"@types/react": "^19.2.18",
|
"@types/react": "^19.2.18",
|
||||||
"@types/react-dom": "^19.2.4",
|
"@types/react-dom": "^19.2.7",
|
||||||
"concurrently": "^10.0.4",
|
"concurrently": "^10.0.5",
|
||||||
"eslint": "^10.8.0",
|
"eslint": "^10.10.0",
|
||||||
"eslint-config-prettier": "^10.1.8",
|
"eslint-config-prettier": "^10.1.8",
|
||||||
"etag": "^1.8.1",
|
"etag": "^1.8.1",
|
||||||
"mime-types": "^3.0.2",
|
"mime-types": "^3.0.2",
|
||||||
"prettier": "^3.9.6",
|
"prettier": "^3.9.6",
|
||||||
"rollup-plugin-visualizer": "^7.0.1",
|
"rollup-plugin-visualizer": "^7.1.1",
|
||||||
"terser": "^5.49.0",
|
"terser": "^5.51.2",
|
||||||
"typescript": "^6.0.3",
|
"typescript": "^6.0.3",
|
||||||
"typescript-eslint": "^8.65.0",
|
"typescript-eslint": "^8.70.0",
|
||||||
"vite": "^8.2.0",
|
"vite": "^8.2.2",
|
||||||
"vite-plugin-imagemin": "^0.6.1"
|
"vite-plugin-imagemin": "^0.6.1"
|
||||||
},
|
},
|
||||||
"packageManager": "pnpm@11.18.0+sha512.33d83c77da82f49fba836925c6f1b841181ec3132b670639bd012f7075f5c7cf634c5f870147c19aae7478fac01df09d8892e880454896edd23ee9b33757563c"
|
"packageManager": "pnpm@12.3.4+sha512.961aa41fb077da3a04a441d9f8e15ebc0c96da8ef710b2eb67bf9ee7cb0610eabd48f1fd85f51cffe73846785fa0f87c56a3a872a1d893f8446741b5cce45457"
|
||||||
}
|
}
|
||||||
|
|||||||
Generated
+570
-509
File diff suppressed because it is too large
Load Diff
Binary file not shown.
|
Before Width: | Height: | Size: 5.6 KiB After Width: | Height: | Size: 2.4 KiB |
@@ -1,4 +1,4 @@
|
|||||||
import { memo, useCallback, useContext, useEffect, useMemo, useState } from 'react';
|
import { memo, useContext, useEffect, useState } from 'react';
|
||||||
|
|
||||||
import ForwardIcon from '@mui/icons-material/Forward';
|
import ForwardIcon from '@mui/icons-material/Forward';
|
||||||
import { Box, Button, Paper, Typography } from '@mui/material';
|
import { Box, Button, Paper, Typography } from '@mui/material';
|
||||||
@@ -43,17 +43,13 @@ const SignIn = memo(() => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const updateLoginRequestValue = useMemo(
|
const updateLoginRequestValue = updateValue((updater) =>
|
||||||
() =>
|
setSignInRequest(
|
||||||
updateValue((updater) =>
|
updater as unknown as (prevState: SignInRequest) => SignInRequest
|
||||||
setSignInRequest(
|
)
|
||||||
updater as unknown as (prevState: SignInRequest) => SignInRequest
|
|
||||||
)
|
|
||||||
),
|
|
||||||
[]
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const signIn = useCallback(async () => {
|
const signIn = async () => {
|
||||||
await callSignIn(signInRequest).catch((event: Error) => {
|
await callSignIn(signInRequest).catch((event: Error) => {
|
||||||
if (event.message === 'Unauthorized') {
|
if (event.message === 'Unauthorized') {
|
||||||
toast.warning(LL.INVALID_LOGIN());
|
toast.warning(LL.INVALID_LOGIN());
|
||||||
@@ -62,7 +58,7 @@ const SignIn = memo(() => {
|
|||||||
}
|
}
|
||||||
setProcessing(false);
|
setProcessing(false);
|
||||||
});
|
});
|
||||||
}, [callSignIn, signInRequest, LL]);
|
};
|
||||||
|
|
||||||
const validateAndSignIn = async () => {
|
const validateAndSignIn = async () => {
|
||||||
setProcessing(true);
|
setProcessing(true);
|
||||||
@@ -78,7 +74,7 @@ const SignIn = memo(() => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const submitOnEnter = useMemo(() => onEnterCallback(signIn), [signIn]);
|
const submitOnEnter = onEnterCallback(signIn);
|
||||||
|
|
||||||
// get rid of scrollbar
|
// get rid of scrollbar
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import type { LogSettings, SystemStatus } from 'types';
|
|||||||
|
|
||||||
import { alovaInstance } from './endpoints';
|
import { alovaInstance } from './endpoints';
|
||||||
|
|
||||||
// systemStatus - also used to ping in System Monitor for pinging
|
// systemStatus - also used in System Monitor to ping the ESP
|
||||||
export const readSystemStatus = () =>
|
export const readSystemStatus = () =>
|
||||||
alovaInstance.Get<SystemStatus>('/rest/systemStatus');
|
alovaInstance.Get<SystemStatus>('/rest/systemStatus');
|
||||||
|
|
||||||
@@ -19,6 +19,7 @@ export const uploadFile = (file: File) => {
|
|||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
formData.append('file', file);
|
formData.append('file', file);
|
||||||
return alovaInstance.Post('/rest/uploadFile', formData, {
|
return alovaInstance.Post('/rest/uploadFile', formData, {
|
||||||
timeout: UPLOAD_TIMEOUT
|
timeout: UPLOAD_TIMEOUT,
|
||||||
|
enableUpload: true
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,11 +3,9 @@ import { useBlocker } from 'react-router';
|
|||||||
|
|
||||||
import AddIcon from '@mui/icons-material/Add';
|
import AddIcon from '@mui/icons-material/Add';
|
||||||
import CancelIcon from '@mui/icons-material/Cancel';
|
import CancelIcon from '@mui/icons-material/Cancel';
|
||||||
import PlayArrowIcon from '@mui/icons-material/PlayArrow';
|
|
||||||
import WarningIcon from '@mui/icons-material/Warning';
|
import WarningIcon from '@mui/icons-material/Warning';
|
||||||
import { Box, Button, IconButton, Typography } from '@mui/material';
|
import { Box, Button, Typography } from '@mui/material';
|
||||||
|
|
||||||
import { callAction } from '@/api/app';
|
|
||||||
import {
|
import {
|
||||||
Body,
|
Body,
|
||||||
Cell,
|
Cell,
|
||||||
@@ -48,7 +46,7 @@ const DEFAULT_COMMAND_ITEM: Omit<CommandItem, 'id'> = {
|
|||||||
|
|
||||||
const commandsTheme = {
|
const commandsTheme = {
|
||||||
Table: `
|
Table: `
|
||||||
--data-table-library_grid-template-columns: repeat(1, minmax(100px, 1fr)) repeat(1, minmax(100px, 1fr)) 160px 32px;
|
--data-table-library_grid-template-columns: repeat(1, minmax(100px, 1fr)) repeat(1, minmax(100px, 1fr)) 160px;
|
||||||
`,
|
`,
|
||||||
BaseRow: `
|
BaseRow: `
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
@@ -143,21 +141,6 @@ const Commands = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const { send: executeCommand } = useRequest(
|
|
||||||
(id: string) => callAction({ action: 'executeCommand', param: id }),
|
|
||||||
{ immediate: false }
|
|
||||||
)
|
|
||||||
.onSuccess(() => {
|
|
||||||
toast.success(LL.EXECUTE_COMMAND_SENT());
|
|
||||||
})
|
|
||||||
.onError((error) => {
|
|
||||||
toast.error(String(error.error?.message || 'An error occurred'));
|
|
||||||
});
|
|
||||||
|
|
||||||
const execute = async (name: string) => {
|
|
||||||
await executeCommand(name);
|
|
||||||
};
|
|
||||||
|
|
||||||
const editItem = (ci: CommandItem) => {
|
const editItem = (ci: CommandItem) => {
|
||||||
setCreating(false);
|
setCreating(false);
|
||||||
setSelectedItem(ci);
|
setSelectedItem(ci);
|
||||||
@@ -222,7 +205,6 @@ const Commands = () => {
|
|||||||
<HeaderCell stiff>{LL.COMMAND(0)}</HeaderCell>
|
<HeaderCell stiff>{LL.COMMAND(0)}</HeaderCell>
|
||||||
<HeaderCell stiff>{LL.VALUE(0)}</HeaderCell>
|
<HeaderCell stiff>{LL.VALUE(0)}</HeaderCell>
|
||||||
<HeaderCell stiff>{LL.NAME(0)}</HeaderCell>
|
<HeaderCell stiff>{LL.NAME(0)}</HeaderCell>
|
||||||
<HeaderCell></HeaderCell>
|
|
||||||
</HeaderRow>
|
</HeaderRow>
|
||||||
</Header>
|
</Header>
|
||||||
<Body>
|
<Body>
|
||||||
@@ -231,16 +213,6 @@ const Commands = () => {
|
|||||||
<Cell>{ci.cmd}</Cell>
|
<Cell>{ci.cmd}</Cell>
|
||||||
<Cell>{ci.value}</Cell>
|
<Cell>{ci.value}</Cell>
|
||||||
<Cell>{ci.name}</Cell>
|
<Cell>{ci.name}</Cell>
|
||||||
<Cell>
|
|
||||||
{numChanges === 0 && (
|
|
||||||
<IconButton
|
|
||||||
onClick={() => execute(ci.name)}
|
|
||||||
style={{ backgroundColor: 'transparent' }}
|
|
||||||
>
|
|
||||||
<PlayArrowIcon color="primary" sx={{ fontSize: 16 }} />
|
|
||||||
</IconButton>
|
|
||||||
)}
|
|
||||||
</Cell>
|
|
||||||
</Row>
|
</Row>
|
||||||
))}
|
))}
|
||||||
</Body>
|
</Body>
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ const CommandsDialog = ({
|
|||||||
{ immediate: false }
|
{ immediate: false }
|
||||||
)
|
)
|
||||||
.onSuccess(() => {
|
.onSuccess(() => {
|
||||||
toast.success(LL.EXECUTE_COMMAND_SENT());
|
toast.success(LL.COMMAND_EXECUTED());
|
||||||
})
|
})
|
||||||
.onError((error) => {
|
.onError((error) => {
|
||||||
toast.error(String(error.error?.message || 'An error occurred'));
|
toast.error(String(error.error?.message || 'An error occurred'));
|
||||||
@@ -121,8 +121,8 @@ const CommandsDialog = ({
|
|||||||
return (
|
return (
|
||||||
<Dialog sx={dialogStyle} open={open} onClose={handleClose}>
|
<Dialog sx={dialogStyle} open={open} onClose={handleClose}>
|
||||||
<DialogTitle>
|
<DialogTitle>
|
||||||
{creating ? `${LL.ADD(1)} ${LL.NEW(0)}` : LL.EDIT()}
|
{creating ? `${LL.ADD(1)} ` : LL.UPDATE() + '/' + LL.RUN()}
|
||||||
{LL.COMMAND(1)}
|
{LL.COMMAND(0)}
|
||||||
</DialogTitle>
|
</DialogTitle>
|
||||||
<DialogContent dividers>
|
<DialogContent dividers>
|
||||||
<ValidatedTextField
|
<ValidatedTextField
|
||||||
@@ -195,7 +195,7 @@ const CommandsDialog = ({
|
|||||||
onClick={execute}
|
onClick={execute}
|
||||||
color="success"
|
color="success"
|
||||||
>
|
>
|
||||||
{LL.EXECUTE()}
|
{LL.RUN()}
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
</DialogActions>
|
</DialogActions>
|
||||||
|
|||||||
@@ -334,7 +334,7 @@ const Dashboard = memo(() => {
|
|||||||
(di.dv.v === '' || di.dv.v === undefined ? (
|
(di.dv.v === '' || di.dv.v === undefined ? (
|
||||||
<IconButton
|
<IconButton
|
||||||
size="small"
|
size="small"
|
||||||
aria-label={LL.RUN_COMMAND()}
|
aria-label={LL.RUN()}
|
||||||
onClick={() => editDashboardValue(di)}
|
onClick={() => editDashboardValue(di)}
|
||||||
style={{ backgroundColor: 'transparent' }}
|
style={{ backgroundColor: 'transparent' }}
|
||||||
>
|
>
|
||||||
@@ -402,7 +402,10 @@ const Dashboard = memo(() => {
|
|||||||
{selectedDashboardItem && selectedDashboardItem.dv && (
|
{selectedDashboardItem && selectedDashboardItem.dv && (
|
||||||
<DevicesDialog
|
<DevicesDialog
|
||||||
open={deviceValueDialogOpen}
|
open={deviceValueDialogOpen}
|
||||||
onClose={() => setDeviceValueDialogOpen(false)}
|
onClose={() => {
|
||||||
|
setDeviceValueDialogOpen(false);
|
||||||
|
setSelectedDashboardItem(undefined);
|
||||||
|
}}
|
||||||
onSave={deviceValueDialogSave}
|
onSave={deviceValueDialogSave}
|
||||||
selectedItem={selectedDashboardItem.dv}
|
selectedItem={selectedDashboardItem.dv}
|
||||||
writeable={true}
|
writeable={true}
|
||||||
|
|||||||
@@ -592,6 +592,7 @@ const Devices = memo(() => {
|
|||||||
|
|
||||||
const deviceValueDialogClose = () => {
|
const deviceValueDialogClose = () => {
|
||||||
setDeviceValueDialogOpen(false);
|
setDeviceValueDialogOpen(false);
|
||||||
|
setSelectedDeviceValue(undefined);
|
||||||
if (selectedDevice !== undefined) {
|
if (selectedDevice !== undefined) {
|
||||||
void sendDeviceData(selectedDevice);
|
void sendDeviceData(selectedDevice);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ import { ValidationError, validate } from 'validators';
|
|||||||
import type Schema from 'validators/schema';
|
import type Schema from 'validators/schema';
|
||||||
import type { ValidateFieldsError } from 'validators/schema';
|
import type { ValidateFieldsError } from 'validators/schema';
|
||||||
|
|
||||||
import { DeviceValueUOM, DeviceValueUOM_s } from './types';
|
import { DeviceValueUOM, DeviceValueUOM_s, getDeviceValueKind } from './types';
|
||||||
import type { DeviceValue } from './types';
|
import type { DeviceValue } from './types';
|
||||||
|
|
||||||
interface DevicesDialogProps {
|
interface DevicesDialogProps {
|
||||||
@@ -70,7 +70,7 @@ const DevicesDialog = ({
|
|||||||
{ immediate: false }
|
{ immediate: false }
|
||||||
)
|
)
|
||||||
.onSuccess(() => {
|
.onSuccess(() => {
|
||||||
toast.success(LL.EXECUTE_COMMAND_SENT());
|
toast.success(LL.COMMAND_EXECUTED());
|
||||||
})
|
})
|
||||||
.onError((error) => {
|
.onError((error) => {
|
||||||
toast.error(String(error.error?.message || 'An error occurred'));
|
toast.error(String(error.error?.message || 'An error occurred'));
|
||||||
@@ -119,18 +119,20 @@ const DevicesDialog = ({
|
|||||||
return undefined;
|
return undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
const isCommand =
|
const kind = getDeviceValueKind(selectedItem);
|
||||||
(selectedItem.v === '' || selectedItem.v === undefined) &&
|
|
||||||
Boolean(selectedItem.c);
|
const dialogTitle =
|
||||||
const isSchedulerImmediate = selectedItem.v === undefined;
|
kind === 'command' || kind === 'deviceCommand'
|
||||||
const dialogTitle = isCommand
|
? LL.RUN() + ' ' + LL.COMMAND(0)
|
||||||
? isSchedulerImmediate
|
: kind === 'scheduler'
|
||||||
? LL.EXECUTE() + ' ' + LL.SCHEDULE(0)
|
? LL.UPDATE() + ' ' + LL.SCHEDULE(0)
|
||||||
: LL.RUN_COMMAND()
|
: writeable
|
||||||
: writeable
|
? LL.CHANGE_VALUE()
|
||||||
? LL.CHANGE_VALUE()
|
: LL.VALUE(0);
|
||||||
: LL.VALUE(0);
|
|
||||||
const buttonLabel = isCommand ? LL.EXECUTE() : LL.UPDATE();
|
const buttonLabel =
|
||||||
|
kind === 'command' || kind === 'deviceCommand' ? LL.RUN() : LL.UPDATE();
|
||||||
|
|
||||||
const helperText = showHelperText(editItem);
|
const helperText = showHelperText(editItem);
|
||||||
|
|
||||||
const valueLabel = LL.VALUE(0);
|
const valueLabel = LL.VALUE(0);
|
||||||
@@ -142,7 +144,7 @@ const DevicesDialog = ({
|
|||||||
<Typography sx={{ mb: 2 }} color="warning" variant="body2">
|
<Typography sx={{ mb: 2 }} color="warning" variant="body2">
|
||||||
{editItem.id.slice(2)}
|
{editItem.id.slice(2)}
|
||||||
</Typography>
|
</Typography>
|
||||||
{!isSchedulerImmediate && (
|
{kind !== 'command' && (
|
||||||
<Grid container>
|
<Grid container>
|
||||||
<Grid size={12}>
|
<Grid size={12}>
|
||||||
{editItem.l ? (
|
{editItem.l ? (
|
||||||
@@ -227,11 +229,15 @@ const DevicesDialog = ({
|
|||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
startIcon={
|
startIcon={
|
||||||
isCommand ? <PlayArrowIcon /> : <WarningIcon color="warning" />
|
kind === 'command' ? (
|
||||||
|
<PlayArrowIcon />
|
||||||
|
) : kind === 'deviceEntity' || kind === 'deviceCommand' ? (
|
||||||
|
<WarningIcon color="warning" />
|
||||||
|
) : undefined
|
||||||
}
|
}
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
onClick={doAction}
|
onClick={doAction}
|
||||||
color={isCommand ? 'success' : 'primary'}
|
color={kind !== 'value' ? 'success' : 'primary'}
|
||||||
>
|
>
|
||||||
{buttonLabel}
|
{buttonLabel}
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -210,6 +210,18 @@ export enum DeviceValueUOM {
|
|||||||
HERTZ
|
HERTZ
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type DeviceValueKind =
|
||||||
|
'value' | 'scheduler' | 'command' | 'deviceCommand' | 'deviceEntity';
|
||||||
|
|
||||||
|
export function getDeviceValueKind(item: DeviceValue): DeviceValueKind {
|
||||||
|
if (!item.c) return 'value';
|
||||||
|
if (item.v !== undefined && item.l !== undefined && item.u === undefined)
|
||||||
|
return 'scheduler';
|
||||||
|
if (item.v === undefined && item.l === undefined) return 'command';
|
||||||
|
if (item.u === DeviceValueUOM.NONE) return 'deviceCommand';
|
||||||
|
return 'deviceEntity';
|
||||||
|
}
|
||||||
|
|
||||||
export const DeviceValueUOM_s = [
|
export const DeviceValueUOM_s = [
|
||||||
'',
|
'',
|
||||||
'°C',
|
'°C',
|
||||||
@@ -295,6 +307,7 @@ export const BOARD_PROFILES = {
|
|||||||
LOLIN: 'Lolin D32',
|
LOLIN: 'Lolin D32',
|
||||||
OLIMEX: 'Olimex ESP32-EVB',
|
OLIMEX: 'Olimex ESP32-EVB',
|
||||||
OLIMEXPOE: 'Olimex ESP32-POE',
|
OLIMEXPOE: 'Olimex ESP32-POE',
|
||||||
|
OLIMEXPOEW: 'Olimex ESP32-POE-ISO WROVER (PSRAM)',
|
||||||
C3MINI: 'Wemos C3 Mini',
|
C3MINI: 'Wemos C3 Mini',
|
||||||
S2MINI: 'Wemos S2 Mini',
|
S2MINI: 'Wemos S2 Mini',
|
||||||
S3MINI: 'Liligo S3'
|
S3MINI: 'Liligo S3'
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { useState } from 'react';
|
import { useContext, useEffect, useState } from 'react';
|
||||||
|
|
||||||
import CancelIcon from '@mui/icons-material/Cancel';
|
import CancelIcon from '@mui/icons-material/Cancel';
|
||||||
import PowerSettingsNewIcon from '@mui/icons-material/PowerSettingsNew';
|
import PowerSettingsNewIcon from '@mui/icons-material/PowerSettingsNew';
|
||||||
@@ -31,6 +31,7 @@ import {
|
|||||||
useLayoutTitle
|
useLayoutTitle
|
||||||
} from 'components';
|
} from 'components';
|
||||||
import { toast } from 'components/toast';
|
import { toast } from 'components/toast';
|
||||||
|
import { AuthenticatedContext } from 'contexts/authentication';
|
||||||
import { useI18nContext } from 'i18n/i18n-react';
|
import { useI18nContext } from 'i18n/i18n-react';
|
||||||
import { numberValue, updateValueDirty, useRest } from 'utils';
|
import { numberValue, updateValueDirty, useRest } from 'utils';
|
||||||
import { ValidationError, validate } from 'validators';
|
import { ValidationError, validate } from 'validators';
|
||||||
@@ -68,6 +69,14 @@ const ApplicationSettings = () => {
|
|||||||
update: writeSettings
|
update: writeSettings
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// the menu drawer reads the system name from the authentication context
|
||||||
|
const { setSystemName } = useContext(AuthenticatedContext);
|
||||||
|
useEffect(() => {
|
||||||
|
if (origData?.system_name !== undefined) {
|
||||||
|
setSystemName(origData.system_name);
|
||||||
|
}
|
||||||
|
}, [origData?.system_name, setSystemName]);
|
||||||
|
|
||||||
const [restarting, setRestarting] = useState<boolean>();
|
const [restarting, setRestarting] = useState<boolean>();
|
||||||
|
|
||||||
const { LL } = useI18nContext();
|
const { LL } = useI18nContext();
|
||||||
@@ -168,7 +177,7 @@ const ApplicationSettings = () => {
|
|||||||
await sendAPI({
|
await sendAPI({
|
||||||
device: 'system',
|
device: 'system',
|
||||||
cmd: 'sendmail',
|
cmd: 'sendmail',
|
||||||
data: 'Email notification test successful!',
|
data: '"Email notification test successful!"',
|
||||||
id: 0
|
id: 0
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
|
|||||||
@@ -165,11 +165,7 @@ const DownloadUpload = () => {
|
|||||||
{LL.UPLOAD()}
|
{LL.UPLOAD()}
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
<Typography sx={{ pb: 2 }} color="warning" variant="body1">
|
<SingleUpload text={LL.UPLOAD_BACKUP_TEXT()} doRestart={doRestart} />
|
||||||
{LL.UPLOAD_TEXT()}:
|
|
||||||
</Typography>
|
|
||||||
|
|
||||||
<SingleUpload doRestart={doRestart} />
|
|
||||||
</SectionContent>
|
</SectionContent>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { memo, useContext, useMemo, useState } from 'react';
|
import { memo, useContext, useEffect, useState } from 'react';
|
||||||
import { Link } from 'react-router';
|
import { Link } from 'react-router';
|
||||||
|
|
||||||
import CancelIcon from '@mui/icons-material/Cancel';
|
import CancelIcon from '@mui/icons-material/Cancel';
|
||||||
@@ -412,7 +412,7 @@ const getPlatform = (data: VersionData): string => {
|
|||||||
|
|
||||||
const Version = () => {
|
const Version = () => {
|
||||||
const { LL, locale } = useI18nContext();
|
const { LL, locale } = useI18nContext();
|
||||||
const { me, versions } = useContext(AuthenticatedContext);
|
const { me, versions, refreshVersions } = useContext(AuthenticatedContext);
|
||||||
|
|
||||||
const [restarting, setRestarting] = useState<boolean>(false);
|
const [restarting, setRestarting] = useState<boolean>(false);
|
||||||
const [confirmFactoryReset, setConfirmFactoryReset] = useState<boolean>(false);
|
const [confirmFactoryReset, setConfirmFactoryReset] = useState<boolean>(false);
|
||||||
@@ -430,25 +430,27 @@ const Version = () => {
|
|||||||
const [showVersionInfo, setShowVersionInfo] = useState<number>(0); // 1 = stable, 2 = dev, 3 = partition
|
const [showVersionInfo, setShowVersionInfo] = useState<number>(0); // 1 = stable, 2 = dev, 3 = partition
|
||||||
const [firmwareSize, setFirmwareSize] = useState<number>(0);
|
const [firmwareSize, setFirmwareSize] = useState<number>(0);
|
||||||
|
|
||||||
const latestVersion = useMemo<VersionInfo | undefined>(
|
const latestVersion: VersionInfo | undefined = versions?.stable
|
||||||
() =>
|
? { version: versions.stable.version, date: versions.stable.date }
|
||||||
versions?.stable
|
: undefined;
|
||||||
? { version: versions.stable.version, date: versions.stable.date }
|
const latestDevVersion: VersionInfo | undefined = versions?.dev
|
||||||
: undefined,
|
? { version: versions.dev.version, date: versions.dev.date }
|
||||||
[versions?.stable]
|
: undefined;
|
||||||
);
|
|
||||||
const latestDevVersion = useMemo<VersionInfo | undefined>(
|
|
||||||
() =>
|
|
||||||
versions?.dev
|
|
||||||
? { version: versions.dev.version, date: versions.dev.date }
|
|
||||||
: undefined,
|
|
||||||
[versions?.dev]
|
|
||||||
);
|
|
||||||
const usingDevVersion = versions?.current?.type === 'dev';
|
const usingDevVersion = versions?.current?.type === 'dev';
|
||||||
const stableUpgradeAvailable = versions?.stable?.upgradeable ?? false;
|
const stableUpgradeAvailable = versions?.stable?.upgradeable ?? false;
|
||||||
const devUpgradeAvailable = versions?.dev?.upgradeable ?? false;
|
const devUpgradeAvailable = versions?.dev?.upgradeable ?? false;
|
||||||
const internetLive = Boolean(versions?.stable || versions?.dev);
|
const internetLive = Boolean(versions?.stable || versions?.dev);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (internetLive) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const interval = setInterval(() => {
|
||||||
|
void refreshVersions();
|
||||||
|
}, 3000);
|
||||||
|
return () => clearInterval(interval);
|
||||||
|
}, [internetLive, refreshVersions]);
|
||||||
|
|
||||||
const { send: sendSetPartition } = useRequest(
|
const { send: sendSetPartition } = useRequest(
|
||||||
(partition: string) => callAction({ action: 'setPartition', param: partition }),
|
(partition: string) => callAction({ action: 'setPartition', param: partition }),
|
||||||
{ immediate: false }
|
{ immediate: false }
|
||||||
@@ -805,8 +807,7 @@ const Version = () => {
|
|||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<Typography sx={{ mt: 2 }} color="warning">
|
<Typography sx={{ mt: 2 }} color="warning">
|
||||||
<WarningIcon color="warning" sx={{ verticalAlign: 'middle', mr: 2 }} />
|
{LL.INTERNET_CONNECTION_CHECK()}…
|
||||||
{LL.INTERNET_CONNECTION_REQUIRED()}
|
|
||||||
</Typography>
|
</Typography>
|
||||||
)}
|
)}
|
||||||
{me.admin && (
|
{me.admin && (
|
||||||
@@ -845,7 +846,7 @@ const Version = () => {
|
|||||||
<Typography sx={{ pt: 2, pb: 2 }} variant="h6" color="primary">
|
<Typography sx={{ pt: 2, pb: 2 }} variant="h6" color="primary">
|
||||||
{LL.UPLOAD()}
|
{LL.UPLOAD()}
|
||||||
</Typography>
|
</Typography>
|
||||||
<SingleUpload doRestart={doRestart} />
|
<SingleUpload text={LL.UPLOAD_FIRMWARE_TEXT()} doRestart={doRestart} />
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
@@ -899,7 +900,7 @@ const Version = () => {
|
|||||||
startIcon={<PowerSettingsNewIcon />}
|
startIcon={<PowerSettingsNewIcon />}
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
onClick={doRestart}
|
onClick={doRestart}
|
||||||
color="error"
|
color="success"
|
||||||
>
|
>
|
||||||
{LL.RESTART()}
|
{LL.RESTART()}
|
||||||
</Button>
|
</Button>
|
||||||
@@ -910,20 +911,13 @@ const Version = () => {
|
|||||||
sx={{
|
sx={{
|
||||||
mt: 2,
|
mt: 2,
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
justifyContent: 'flex-end',
|
justifyContent: 'space-between',
|
||||||
|
alignItems: 'center',
|
||||||
flexWrap: 'nowrap',
|
flexWrap: 'nowrap',
|
||||||
whiteSpace: 'nowrap',
|
whiteSpace: 'nowrap',
|
||||||
gap: 1
|
gap: 1
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Button
|
|
||||||
startIcon={<PowerSettingsNewIcon />}
|
|
||||||
variant="outlined"
|
|
||||||
onClick={handleRestartClick}
|
|
||||||
color="error"
|
|
||||||
>
|
|
||||||
{LL.RESTART()}
|
|
||||||
</Button>
|
|
||||||
{!data.disable_reset && (
|
{!data.disable_reset && (
|
||||||
<Button
|
<Button
|
||||||
startIcon={<SettingsBackupRestoreIcon />}
|
startIcon={<SettingsBackupRestoreIcon />}
|
||||||
@@ -934,6 +928,14 @@ const Version = () => {
|
|||||||
{LL.FACTORY_RESET()}
|
{LL.FACTORY_RESET()}
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
|
<Button
|
||||||
|
startIcon={<PowerSettingsNewIcon />}
|
||||||
|
variant="outlined"
|
||||||
|
onClick={handleRestartClick}
|
||||||
|
color="success"
|
||||||
|
>
|
||||||
|
{LL.RESTART()}
|
||||||
|
</Button>
|
||||||
</Box>
|
</Box>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import {
|
|||||||
|
|
||||||
import * as NetworkApi from 'api/network';
|
import * as NetworkApi from 'api/network';
|
||||||
import { API } from 'api/app';
|
import { API } from 'api/app';
|
||||||
|
import { readSystemStatus } from 'api/system';
|
||||||
|
|
||||||
import { updateState, useRequest } from 'alova/client';
|
import { updateState, useRequest } from 'alova/client';
|
||||||
import type { APIcall } from 'app/main/types';
|
import type { APIcall } from 'app/main/types';
|
||||||
@@ -50,6 +51,8 @@ import { isNetworkOpen, networkSecurityMode } from './WiFiNetworkSelector';
|
|||||||
const NetworkSettings = () => {
|
const NetworkSettings = () => {
|
||||||
const { LL } = useI18nContext();
|
const { LL } = useI18nContext();
|
||||||
|
|
||||||
|
const { data: hardwareData } = useRequest(readSystemStatus);
|
||||||
|
|
||||||
const { selectedNetwork, deselectNetwork } = useContext(WiFiConnectionContext);
|
const { selectedNetwork, deselectNetwork } = useContext(WiFiConnectionContext);
|
||||||
|
|
||||||
const [initialized, setInitialized] = useState(false);
|
const [initialized, setInitialized] = useState(false);
|
||||||
@@ -92,7 +95,8 @@ const NetworkSettings = () => {
|
|||||||
nosleep: true,
|
nosleep: true,
|
||||||
enableMDNS: true,
|
enableMDNS: true,
|
||||||
enableCORS: false,
|
enableCORS: false,
|
||||||
CORSOrigin: '*'
|
CORSOrigin: '*',
|
||||||
|
eth_10mbit: current_data ? current_data.eth_10mbit : false
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -136,7 +140,7 @@ const NetworkSettings = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const content = () => {
|
const content = () => {
|
||||||
if (!data) {
|
if (!data || !hardwareData) {
|
||||||
return <FormLoader onRetry={loadData} errorMessage={errorMessage || ''} />;
|
return <FormLoader onRetry={loadData} errorMessage={errorMessage || ''} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -246,6 +250,23 @@ const NetworkSettings = () => {
|
|||||||
}
|
}
|
||||||
label={LL.NETWORK_LOW_BAND()}
|
label={LL.NETWORK_LOW_BAND()}
|
||||||
/>
|
/>
|
||||||
|
{hardwareData.esp_platform === 'ESP32' && (
|
||||||
|
<>
|
||||||
|
<Typography sx={{ pt: 2 }} variant="h6" color="primary">
|
||||||
|
Ethernet
|
||||||
|
</Typography>
|
||||||
|
<BlockFormControlLabel
|
||||||
|
control={
|
||||||
|
<Checkbox
|
||||||
|
name="eth_10mbit"
|
||||||
|
checked={data.eth_10mbit}
|
||||||
|
onChange={updateFormValue}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
label="Force 10Mbit half-duplex"
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
<Typography sx={{ pt: 2 }} variant="h6" color="primary">
|
<Typography sx={{ pt: 2 }} variant="h6" color="primary">
|
||||||
{LL.GENERAL_OPTIONS()}
|
{LL.GENERAL_OPTIONS()}
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|||||||
@@ -369,7 +369,7 @@ const SystemLog = () => {
|
|||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
{data.developer_mode && (
|
{data.developer_mode && (
|
||||||
<IconButton onClick={sendReadCommand} aria-label={LL.EXECUTE()}>
|
<IconButton onClick={sendReadCommand} aria-label={LL.RUN()}>
|
||||||
<PlayArrowIcon color="primary" />
|
<PlayArrowIcon color="primary" />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
import { memo } from 'react';
|
import { memo, useContext } from 'react';
|
||||||
|
|
||||||
import { Box, Divider, Drawer, Toolbar, Typography, styled } from '@mui/material';
|
import { Box, Divider, Drawer, Toolbar, Typography, styled } from '@mui/material';
|
||||||
|
|
||||||
import { readSettings } from 'api/app';
|
import { AuthenticatedContext } from 'contexts/authentication';
|
||||||
|
|
||||||
import { useRequest } from 'alova/client';
|
|
||||||
import { PROJECT_NAME } from 'env';
|
import { PROJECT_NAME } from 'env';
|
||||||
|
|
||||||
import { DRAWER_WIDTH } from './Layout';
|
import { DRAWER_WIDTH } from './Layout';
|
||||||
@@ -27,8 +25,7 @@ interface LayoutDrawerProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const LayoutDrawerComponent = ({ mobileOpen, onClose }: LayoutDrawerProps) => {
|
const LayoutDrawerComponent = ({ mobileOpen, onClose }: LayoutDrawerProps) => {
|
||||||
const { data: settings } = useRequest(readSettings);
|
const { systemName } = useContext(AuthenticatedContext);
|
||||||
const system_name = settings?.system_name;
|
|
||||||
|
|
||||||
const drawer = (
|
const drawer = (
|
||||||
<>
|
<>
|
||||||
@@ -43,9 +40,9 @@ const LayoutDrawerComponent = ({ mobileOpen, onClose }: LayoutDrawerProps) => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Typography>{PROJECT_NAME}</Typography>
|
<Typography>{PROJECT_NAME}</Typography>
|
||||||
{system_name && (
|
{systemName && (
|
||||||
<Typography color="secondary" variant="body2">
|
<Typography color="secondary" variant="body2">
|
||||||
{system_name}
|
{systemName}
|
||||||
</Typography>
|
</Typography>
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@@ -14,9 +14,10 @@ import { LinearProgressWithLabel } from './LinearProgressWithLabel';
|
|||||||
|
|
||||||
interface SingleUploadProps {
|
interface SingleUploadProps {
|
||||||
doRestart: () => void;
|
doRestart: () => void;
|
||||||
|
text: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const SingleUpload = ({ doRestart }: SingleUploadProps) => {
|
const SingleUpload = ({ doRestart, text }: SingleUploadProps) => {
|
||||||
const [md5, setMd5] = useState<string>();
|
const [md5, setMd5] = useState<string>();
|
||||||
const [file, setFile] = useState<File>();
|
const [file, setFile] = useState<File>();
|
||||||
const { LL } = useI18nContext();
|
const { LL } = useI18nContext();
|
||||||
@@ -29,11 +30,22 @@ const SingleUpload = ({ doRestart }: SingleUploadProps) => {
|
|||||||
} = useRequest(SystemApi.uploadFile, {
|
} = useRequest(SystemApi.uploadFile, {
|
||||||
immediate: false
|
immediate: false
|
||||||
}).onSuccess(({ data }) => {
|
}).onSuccess(({ data }) => {
|
||||||
if (data && typeof data === 'object' && 'md5' in data) {
|
let payload = data;
|
||||||
setMd5((data as { md5: string }).md5);
|
if (typeof payload === 'string' && payload.length > 0) {
|
||||||
toast.success(LL.UPLOAD() + ' MD5 ' + LL.SUCCESSFUL());
|
try {
|
||||||
|
payload = JSON.parse(payload);
|
||||||
|
} catch {
|
||||||
|
payload = data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (payload && typeof payload === 'object' && 'md5' in payload) {
|
||||||
|
setMd5((payload as { md5: string }).md5);
|
||||||
|
toast.success(LL.UPLOAD_MD5_RECEIVED());
|
||||||
setFile(undefined);
|
setFile(undefined);
|
||||||
} else {
|
} else {
|
||||||
|
if (payload && typeof payload === 'object' && 'md5_ok' in payload) {
|
||||||
|
toast.success(LL.UPLOAD_MD5_MATCHED());
|
||||||
|
}
|
||||||
doRestart();
|
doRestart();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -80,12 +92,17 @@ const SingleUpload = ({ doRestart }: SingleUploadProps) => {
|
|||||||
</Button>
|
</Button>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<DragNdrop text={LL.UPLOAD_DROP_TEXT()} onFileSelected={setFile} />
|
<DragNdrop
|
||||||
|
text={(md5 ? LL.UPLOAD_MD5_RECEIVED() : text) + '...'}
|
||||||
|
onFileSelected={setFile}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{md5 && (
|
{md5 && (
|
||||||
<Box sx={{ mt: 2 }}>
|
<Box sx={{ mt: 2 }}>
|
||||||
<Typography variant="body2">{'MD5: ' + md5}</Typography>
|
<Typography variant="body2" color="success">
|
||||||
|
{'MD5: ' + md5}
|
||||||
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
import { useCallback, useEffect, useState } from 'react';
|
||||||
import type { FC } from 'react';
|
import type { FC } from 'react';
|
||||||
import { useNavigate } from 'react-router';
|
import { useNavigate } from 'react-router';
|
||||||
|
|
||||||
@@ -23,6 +23,7 @@ const Authentication: FC<RequiredChildrenProps> = ({ children }) => {
|
|||||||
const [initialized, setInitialized] = useState<boolean>(false);
|
const [initialized, setInitialized] = useState<boolean>(false);
|
||||||
const [me, setMe] = useState<Me>();
|
const [me, setMe] = useState<Me>();
|
||||||
const [versions, setVersions] = useState<VersionsResponse>();
|
const [versions, setVersions] = useState<VersionsResponse>();
|
||||||
|
const [systemName, setSystemName] = useState<string>();
|
||||||
|
|
||||||
const { send: sendVerifyAuthorization } = useRequest(verifyAuthorization(), {
|
const { send: sendVerifyAuthorization } = useRequest(verifyAuthorization(), {
|
||||||
immediate: false
|
immediate: false
|
||||||
@@ -33,7 +34,9 @@ const Authentication: FC<RequiredChildrenProps> = ({ children }) => {
|
|||||||
{ immediate: false }
|
{ immediate: false }
|
||||||
)
|
)
|
||||||
.onSuccess((event) => {
|
.onSuccess((event) => {
|
||||||
setVersions(event.data as VersionsResponse);
|
const response = event.data as VersionsResponse;
|
||||||
|
setVersions(response);
|
||||||
|
setSystemName(response.system_name);
|
||||||
})
|
})
|
||||||
.onError(() => {
|
.onError(() => {
|
||||||
setVersions(undefined);
|
setVersions(undefined);
|
||||||
@@ -60,6 +63,7 @@ const Authentication: FC<RequiredChildrenProps> = ({ children }) => {
|
|||||||
AuthenticationApi.clearAccessToken();
|
AuthenticationApi.clearAccessToken();
|
||||||
setMe(undefined);
|
setMe(undefined);
|
||||||
setVersions(undefined);
|
setVersions(undefined);
|
||||||
|
setSystemName(undefined);
|
||||||
if (doRedirect) {
|
if (doRedirect) {
|
||||||
void navigate('/', { replace: true });
|
void navigate('/', { replace: true });
|
||||||
}
|
}
|
||||||
@@ -88,21 +92,20 @@ const Authentication: FC<RequiredChildrenProps> = ({ children }) => {
|
|||||||
void refresh();
|
void refresh();
|
||||||
}, [refresh]);
|
}, [refresh]);
|
||||||
|
|
||||||
const obj = useMemo(
|
|
||||||
() => ({
|
|
||||||
signIn,
|
|
||||||
signOut,
|
|
||||||
refresh,
|
|
||||||
refreshVersions,
|
|
||||||
...(me && { me }),
|
|
||||||
...(versions && { versions })
|
|
||||||
}),
|
|
||||||
[signIn, signOut, me, refresh, refreshVersions, versions]
|
|
||||||
);
|
|
||||||
|
|
||||||
if (initialized) {
|
if (initialized) {
|
||||||
return (
|
return (
|
||||||
<AuthenticationContext.Provider value={obj}>
|
<AuthenticationContext.Provider
|
||||||
|
value={{
|
||||||
|
signIn,
|
||||||
|
signOut,
|
||||||
|
refresh,
|
||||||
|
refreshVersions,
|
||||||
|
setSystemName,
|
||||||
|
...(me && { me }),
|
||||||
|
...(versions && { versions }),
|
||||||
|
...(systemName !== undefined && { systemName })
|
||||||
|
}}
|
||||||
|
>
|
||||||
{children}
|
{children}
|
||||||
</AuthenticationContext.Provider>
|
</AuthenticationContext.Provider>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ export interface AuthenticationContextValue {
|
|||||||
me?: Me;
|
me?: Me;
|
||||||
versions?: VersionsResponse;
|
versions?: VersionsResponse;
|
||||||
refreshVersions: () => Promise<void>;
|
refreshVersions: () => Promise<void>;
|
||||||
|
systemName?: string;
|
||||||
|
setSystemName: (systemName: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const AuthenticationContextDefaultValue = {} as AuthenticationContextValue;
|
const AuthenticationContextDefaultValue = {} as AuthenticationContextValue;
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import type { Translation } from '../i18n-types';
|
|||||||
const cs: Translation = {
|
const cs: Translation = {
|
||||||
LANGUAGE: 'Jazyk',
|
LANGUAGE: 'Jazyk',
|
||||||
RETRY: 'Zkusit znovu',
|
RETRY: 'Zkusit znovu',
|
||||||
LOADING: 'Načítání',
|
|
||||||
IS_REQUIRED: '{0} je vyžadováno',
|
IS_REQUIRED: '{0} je vyžadováno',
|
||||||
SIGN_IN: 'Přihlásit se',
|
SIGN_IN: 'Přihlásit se',
|
||||||
SIGN_OUT: 'Odhlásit se',
|
SIGN_OUT: 'Odhlásit se',
|
||||||
@@ -37,13 +36,11 @@ const cs: Translation = {
|
|||||||
VALUE: '{{value|Value}}',
|
VALUE: '{{value|Value}}',
|
||||||
DEVICES: 'Zařízení',
|
DEVICES: 'Zařízení',
|
||||||
SENSORS: 'Senzory',
|
SENSORS: 'Senzory',
|
||||||
RUN_COMMAND: 'Zavolat příkaz',
|
|
||||||
CHANGE_VALUE: 'Změnit hodnotu',
|
CHANGE_VALUE: 'Změnit hodnotu',
|
||||||
CANCEL: 'Zrušit',
|
CANCEL: 'Zrušit',
|
||||||
REMOVE_ALL: 'Odebrat vše',
|
REMOVE_ALL: 'Odebrat vše',
|
||||||
APPLY_CHANGES: 'Použít změny ({0})',
|
APPLY_CHANGES: 'Použít změny ({0})',
|
||||||
UPDATE: 'Aktualizovat',
|
UPDATE: 'Aktualizovat',
|
||||||
EXECUTE: 'Provést',
|
|
||||||
REMOVE: 'Odebrat',
|
REMOVE: 'Odebrat',
|
||||||
PROBLEM_UPDATING: 'Problém s aktualizací',
|
PROBLEM_UPDATING: 'Problém s aktualizací',
|
||||||
PROBLEM_LOADING: 'Problém s načítáním',
|
PROBLEM_LOADING: 'Problém s načítáním',
|
||||||
@@ -52,8 +49,6 @@ const cs: Translation = {
|
|||||||
SETTINGS: 'Nastavení',
|
SETTINGS: 'Nastavení',
|
||||||
UPDATED_OF: '{0} Aktualizováno',
|
UPDATED_OF: '{0} Aktualizováno',
|
||||||
UPDATE_OF: '{0} Aktualizace',
|
UPDATE_OF: '{0} Aktualizace',
|
||||||
REMOVED_OF: '{0} Odebráno',
|
|
||||||
DELETION_OF: '{0} Smazání',
|
|
||||||
OFFSET: 'Offset',
|
OFFSET: 'Offset',
|
||||||
FACTOR: 'Faktor',
|
FACTOR: 'Faktor',
|
||||||
FREQ: 'Frekvence',
|
FREQ: 'Frekvence',
|
||||||
@@ -66,17 +61,14 @@ const cs: Translation = {
|
|||||||
TEMP_SENSORS: 'Teplotní senzory',
|
TEMP_SENSORS: 'Teplotní senzory',
|
||||||
WRITE_CMD_SENT: 'Zapisovací příkaz odeslán',
|
WRITE_CMD_SENT: 'Zapisovací příkaz odeslán',
|
||||||
EMS_BUS_WARNING: 'EMS sběrnice je odpojena. Pokud toto varování přetrvává i po několika sekundách, zkontrolujte nastavení a profil desky',
|
EMS_BUS_WARNING: 'EMS sběrnice je odpojena. Pokud toto varování přetrvává i po několika sekundách, zkontrolujte nastavení a profil desky',
|
||||||
EMS_BUS_SCANNING: 'Prohledávání zařízení na EMS sběrnici...',
|
|
||||||
CONNECTED: 'Připojeno',
|
CONNECTED: 'Připojeno',
|
||||||
TX_ISSUES: 'Problémy s Tx - zkontrolujte režim Tx',
|
TX_ISSUES: 'Problémy s Tx - zkontrolujte režim Tx',
|
||||||
DISCONNECTED: 'Odpojeno',
|
DISCONNECTED: 'Odpojeno',
|
||||||
EMS_SCAN: 'Opravdu chcete zahájit úplné skenování zařízení na EMS sběrnici?',
|
|
||||||
DATA_TRAFFIC: 'Datový provoz',
|
DATA_TRAFFIC: 'Datový provoz',
|
||||||
EMS_DEVICE: 'EMS zařízení',
|
EMS_DEVICE: 'EMS zařízení',
|
||||||
SUCCESS: 'ÚSPĚCH',
|
SUCCESS: 'ÚSPĚCH',
|
||||||
FAIL: 'SELHÁNÍ',
|
FAIL: 'SELHÁNÍ',
|
||||||
QUALITY: 'KVALITA',
|
QUALITY: 'KVALITA',
|
||||||
SCAN: 'Skenovat',
|
|
||||||
STATUS_NAMES: [
|
STATUS_NAMES: [
|
||||||
'Přijaté EMS telegramy (Rx)',
|
'Přijaté EMS telegramy (Rx)',
|
||||||
'Čtení EMS (Tx)',
|
'Čtení EMS (Tx)',
|
||||||
@@ -127,7 +119,6 @@ const cs: Translation = {
|
|||||||
ENUM_FORMAT: 'Formát enum v API/MQTT',
|
ENUM_FORMAT: 'Formát enum v API/MQTT',
|
||||||
INDEX: 'Index',
|
INDEX: 'Index',
|
||||||
ENABLE_PARASITE: 'Povolit 1-Wire parazitní napájení',
|
ENABLE_PARASITE: 'Povolit 1-Wire parazitní napájení',
|
||||||
LOGGING: 'Protokolování',
|
|
||||||
LOG_HEX: 'Protokolovat EMS telegramy v hexadecimálním formátu',
|
LOG_HEX: 'Protokolovat EMS telegramy v hexadecimálním formátu',
|
||||||
ENABLE_SYSLOG: 'Povolit Syslog',
|
ENABLE_SYSLOG: 'Povolit Syslog',
|
||||||
LOG_LEVEL: 'Úroveň protokolování',
|
LOG_LEVEL: 'Úroveň protokolování',
|
||||||
@@ -172,7 +163,6 @@ const cs: Translation = {
|
|||||||
CLOSE: 'Zavřít',
|
CLOSE: 'Zavřít',
|
||||||
USE: 'Použít',
|
USE: 'Použít',
|
||||||
FACTORY_RESET: 'Obnovení továrního nastavení',
|
FACTORY_RESET: 'Obnovení továrního nastavení',
|
||||||
SYSTEM_FACTORY_TEXT: 'Zařízení bylo obnoveno do továrního nastavení a nyní se restartuje',
|
|
||||||
SYSTEM_FACTORY_TEXT_DIALOG: 'Opravdu chcete resetovat zařízení EMS-ESP do továrního nastavení?',
|
SYSTEM_FACTORY_TEXT_DIALOG: 'Opravdu chcete resetovat zařízení EMS-ESP do továrního nastavení?',
|
||||||
AVAILABLE_VERSION: 'Nejnovější dostupné verze',
|
AVAILABLE_VERSION: 'Nejnovější dostupné verze',
|
||||||
STABLE: 'Stabilní',
|
STABLE: 'Stabilní',
|
||||||
@@ -188,8 +178,6 @@ const cs: Translation = {
|
|||||||
DOWNLOAD_SETTINGS_TEXT: 'Vytvořte zálohu svého nastavení a konfigurace',
|
DOWNLOAD_SETTINGS_TEXT: 'Vytvořte zálohu svého nastavení a konfigurace',
|
||||||
DOWNLOAD_SETTINGS_TEXT2: 'Exportovat všechny hodnoty',
|
DOWNLOAD_SETTINGS_TEXT2: 'Exportovat všechny hodnoty',
|
||||||
DOWNLOAD_SYSTEM_BACKUP: 'Systémová záloha',
|
DOWNLOAD_SYSTEM_BACKUP: 'Systémová záloha',
|
||||||
UPLOAD_TEXT: 'Nahrajte nový soubor firmwaru (.bin) nebo záložní soubor (.json)',
|
|
||||||
UPLOAD_DROP_TEXT: 'Přetáhněte soubor sem nebo klikněte pro výběr',
|
|
||||||
ERROR: 'Neočekávaná chyba, zkuste to prosím znovu',
|
ERROR: 'Neočekávaná chyba, zkuste to prosím znovu',
|
||||||
TIME_SET: 'Čas nastaven',
|
TIME_SET: 'Čas nastaven',
|
||||||
MANAGE_USERS: 'Spravovat uživatele',
|
MANAGE_USERS: 'Spravovat uživatele',
|
||||||
@@ -257,9 +245,7 @@ const cs: Translation = {
|
|||||||
NETWORK_SCAN: 'Skenovat WiFi sítě',
|
NETWORK_SCAN: 'Skenovat WiFi sítě',
|
||||||
IDLE: 'Nečinný',
|
IDLE: 'Nečinný',
|
||||||
LOST: 'Ztraceno',
|
LOST: 'Ztraceno',
|
||||||
SCANNING: 'Skenování',
|
|
||||||
SCAN_AGAIN: 'Skenovat znovu',
|
SCAN_AGAIN: 'Skenovat znovu',
|
||||||
NETWORK_SCANNER: 'Síťový skener',
|
|
||||||
NETWORK_NO_WIFI: 'Nenalezeny žádné WiFi sítě',
|
NETWORK_NO_WIFI: 'Nenalezeny žádné WiFi sítě',
|
||||||
NETWORK_BLANK_BSSID: 'ponechte prázdné pokud použijete jen SSID',
|
NETWORK_BLANK_BSSID: 'ponechte prázdné pokud použijete jen SSID',
|
||||||
TX_POWER: 'Vysílací výkon',
|
TX_POWER: 'Vysílací výkon',
|
||||||
@@ -294,9 +280,6 @@ const cs: Translation = {
|
|||||||
ONCHANGE: 'Při změně',
|
ONCHANGE: 'Při změně',
|
||||||
CONDITION: 'Podmínka',
|
CONDITION: 'Podmínka',
|
||||||
SCHEDULE_UPDATED: 'Harmonogram aktualizován',
|
SCHEDULE_UPDATED: 'Harmonogram aktualizován',
|
||||||
SCHEDULE_TIMER_1: 'při startu',
|
|
||||||
SCHEDULE_TIMER_2: 'každou minutu',
|
|
||||||
SCHEDULE_TIMER_3: 'každou hodinu',
|
|
||||||
CUSTOM_ENTITIES: 'Vlastní entity',
|
CUSTOM_ENTITIES: 'Vlastní entity',
|
||||||
ENTITIES_HELP_1: 'Definujte vlastní EMS entity nebo dynamické uživatelské proměnné',
|
ENTITIES_HELP_1: 'Definujte vlastní EMS entity nebo dynamické uživatelské proměnné',
|
||||||
ENTITIES_UPDATED: 'Entity aktualizovány',
|
ENTITIES_UPDATED: 'Entity aktualizovány',
|
||||||
@@ -312,9 +295,7 @@ const cs: Translation = {
|
|||||||
ACTIVELOW: 'Aktivní nízká',
|
ACTIVELOW: 'Aktivní nízká',
|
||||||
UNCHANGED: 'Beze změny',
|
UNCHANGED: 'Beze změny',
|
||||||
ALWAYS: 'Vždy',
|
ALWAYS: 'Vždy',
|
||||||
ACTIVITY: 'Aktivita',
|
|
||||||
CONFIGURE: '{0} Konfigurovat',
|
CONFIGURE: '{0} Konfigurovat',
|
||||||
SYSTEM_MEMORY: 'Paměť systému',
|
|
||||||
APPLICATION_SETTINGS_1: 'Upravit nastavení aplikace EMS-ESP',
|
APPLICATION_SETTINGS_1: 'Upravit nastavení aplikace EMS-ESP',
|
||||||
SECURITY_1: 'Přidat nebo odebrat uživatele',
|
SECURITY_1: 'Přidat nebo odebrat uživatele',
|
||||||
DOWNLOAD_UPLOAD_1: 'Stáhnout a nahrát nastavení a firmware',
|
DOWNLOAD_UPLOAD_1: 'Stáhnout a nahrát nastavení a firmware',
|
||||||
@@ -326,7 +307,6 @@ const cs: Translation = {
|
|||||||
RENAME: 'Přejmenovat',
|
RENAME: 'Přejmenovat',
|
||||||
ENABLE_MODBUS: 'Povolit Modbus',
|
ENABLE_MODBUS: 'Povolit Modbus',
|
||||||
VIEW_LOG: 'Zobrazit záznam pro diagnostiku problémů',
|
VIEW_LOG: 'Zobrazit záznam pro diagnostiku problémů',
|
||||||
UPLOAD_DRAG: 'přetáhněte soubor sem nebo klikněte pro výběr',
|
|
||||||
SERVICES: 'Služby',
|
SERVICES: 'Služby',
|
||||||
ALLVALUES: 'Všechny hodnoty',
|
ALLVALUES: 'Všechny hodnoty',
|
||||||
SPECIAL_FUNCTIONS: 'Speciální funkce',
|
SPECIAL_FUNCTIONS: 'Speciální funkce',
|
||||||
@@ -352,10 +332,8 @@ const cs: Translation = {
|
|||||||
THIS_VERSION: 'Tato verze',
|
THIS_VERSION: 'Tato verze',
|
||||||
PLATFORM: 'Platforma',
|
PLATFORM: 'Platforma',
|
||||||
RELEASE_TYPE: 'Typ sestavení',
|
RELEASE_TYPE: 'Typ sestavení',
|
||||||
INTERNET_CONNECTION_REQUIRED: 'Pro automatickou kontrolu a instalaci aktualizací je třeba internetové připojení',
|
|
||||||
SWITCH_RELEASE_TYPE: 'Přepnout na {0} verzi',
|
SWITCH_RELEASE_TYPE: 'Přepnout na {0} verzi',
|
||||||
FIRMWARE_VERSION_INFO: 'Informace o verzi firmwaru',
|
FIRMWARE_VERSION_INFO: 'Informace o verzi firmwaru',
|
||||||
NO_DATA: 'žádné údaje',
|
|
||||||
USER_PROFILE: 'Uživatelský profil',
|
USER_PROFILE: 'Uživatelský profil',
|
||||||
STORED_VERSIONS: 'Uložené verze',
|
STORED_VERSIONS: 'Uložené verze',
|
||||||
ONLINE_HELP: 'online nápověda',
|
ONLINE_HELP: 'online nápověda',
|
||||||
@@ -366,9 +344,14 @@ const cs: Translation = {
|
|||||||
TEST_EMAIL_SUCCESSFUL: 'Test email byl úspěšně odeslán',
|
TEST_EMAIL_SUCCESSFUL: 'Test email byl úspěšně odeslán',
|
||||||
SYSTEM_NAME: 'Název systému',
|
SYSTEM_NAME: 'Název systému',
|
||||||
COMMANDS: 'Příkazy',
|
COMMANDS: 'Příkazy',
|
||||||
COMMANDS_UPDATED: 'Příkazy byly aktualizovány',
|
|
||||||
COMMANDS_HELP_1: 'Definujte vlastní příkazy pro magistrali EMS',
|
COMMANDS_HELP_1: 'Definujte vlastní příkazy pro magistrali EMS',
|
||||||
EXECUTE_COMMAND_SENT: 'Příkaz odeslán',
|
COMMAND_EXECUTED: 'Příkaz úspěšně spuštěn',
|
||||||
|
RUN: 'Spustit příkaz',
|
||||||
|
INTERNET_CONNECTION_CHECK: 'Kontrola online pro dostupné verze',
|
||||||
|
UPLOAD_BACKUP_TEXT: 'Klikněte, abyste vybrali nebo přetáhli a pustili soubor zálohy (.json) sem',
|
||||||
|
UPLOAD_FIRMWARE_TEXT: 'Klikněte, abyste vybrali nebo přetáhli a pustili soubor firmware .bin/.md5 sem',
|
||||||
|
UPLOAD_MD5_RECEIVED: 'Digest přijat, nyní nahrajte firmware .bin',
|
||||||
|
UPLOAD_MD5_MATCHED: 'MD5 souhlasí, firmware se instaluje...'
|
||||||
};
|
};
|
||||||
|
|
||||||
export default cs;
|
export default cs;
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import type { Translation } from '../i18n-types';
|
|||||||
const de: Translation = {
|
const de: Translation = {
|
||||||
LANGUAGE: 'Sprache',
|
LANGUAGE: 'Sprache',
|
||||||
RETRY: 'Neuer Versuch',
|
RETRY: 'Neuer Versuch',
|
||||||
LOADING: 'Laden',
|
|
||||||
IS_REQUIRED: '{0} ist erforderlich',
|
IS_REQUIRED: '{0} ist erforderlich',
|
||||||
SIGN_IN: 'Einloggen',
|
SIGN_IN: 'Einloggen',
|
||||||
SIGN_OUT: 'Ausloggen',
|
SIGN_OUT: 'Ausloggen',
|
||||||
@@ -37,13 +36,11 @@ const de: Translation = {
|
|||||||
VALUE: '{{wert|Wert}}',
|
VALUE: '{{wert|Wert}}',
|
||||||
DEVICES: 'Geräte',
|
DEVICES: 'Geräte',
|
||||||
SENSORS: 'Sensoren',
|
SENSORS: 'Sensoren',
|
||||||
RUN_COMMAND: 'Befehl ausführen',
|
|
||||||
CHANGE_VALUE: 'Wert ändern',
|
CHANGE_VALUE: 'Wert ändern',
|
||||||
CANCEL: 'Abbrechen',
|
CANCEL: 'Abbrechen',
|
||||||
REMOVE_ALL: 'Alles entfernen',
|
REMOVE_ALL: 'Alles entfernen',
|
||||||
APPLY_CHANGES: 'Änderungen anwenden ({0})',
|
APPLY_CHANGES: 'Änderungen anwenden ({0})',
|
||||||
UPDATE: 'Aktualisieren',
|
UPDATE: 'Aktualisieren',
|
||||||
EXECUTE: 'Ausführen',
|
|
||||||
REMOVE: 'Entfernen',
|
REMOVE: 'Entfernen',
|
||||||
PROBLEM_UPDATING: 'Problem beim Aktualisieren',
|
PROBLEM_UPDATING: 'Problem beim Aktualisieren',
|
||||||
PROBLEM_LOADING: 'Problem beim Laden',
|
PROBLEM_LOADING: 'Problem beim Laden',
|
||||||
@@ -52,8 +49,6 @@ const de: Translation = {
|
|||||||
SETTINGS: 'Einstellungen',
|
SETTINGS: 'Einstellungen',
|
||||||
UPDATED_OF: '{0} Aktualisiert',
|
UPDATED_OF: '{0} Aktualisiert',
|
||||||
UPDATE_OF: '{0} Aktualisieren',
|
UPDATE_OF: '{0} Aktualisieren',
|
||||||
REMOVED_OF: '{0} Entfernt',
|
|
||||||
DELETION_OF: '{0} Löschung',
|
|
||||||
OFFSET: 'Position',
|
OFFSET: 'Position',
|
||||||
FACTOR: 'Faktor',
|
FACTOR: 'Faktor',
|
||||||
FREQ: 'Frequenz',
|
FREQ: 'Frequenz',
|
||||||
@@ -66,17 +61,14 @@ const de: Translation = {
|
|||||||
TEMP_SENSORS: 'Temperatursensoren',
|
TEMP_SENSORS: 'Temperatursensoren',
|
||||||
WRITE_CMD_SENT: 'Befehl schreiben wurde gesendet',
|
WRITE_CMD_SENT: 'Befehl schreiben wurde gesendet',
|
||||||
EMS_BUS_WARNING: 'EMS-Bus getrennt. Wenn diese Warnung nach einigen Sekunden immer noch besteht, überprüfen Sie bitte die Einstellungen und das Board-Profil.',
|
EMS_BUS_WARNING: 'EMS-Bus getrennt. Wenn diese Warnung nach einigen Sekunden immer noch besteht, überprüfen Sie bitte die Einstellungen und das Board-Profil.',
|
||||||
EMS_BUS_SCANNING: 'Suche nach EMS Geräten...',
|
|
||||||
CONNECTED: 'Verbunden',
|
CONNECTED: 'Verbunden',
|
||||||
TX_ISSUES: 'Tx-Probleme - Versuchen Sie einen anderen Tx-Modus',
|
TX_ISSUES: 'Tx-Probleme - Versuchen Sie einen anderen Tx-Modus',
|
||||||
DISCONNECTED: 'Getrennt',
|
DISCONNECTED: 'Getrennt',
|
||||||
EMS_SCAN: 'Möchten Sie wirklich eine vollständige Gerätesuche des EMS-Busses starten?',
|
|
||||||
DATA_TRAFFIC: 'Datenverkehr',
|
DATA_TRAFFIC: 'Datenverkehr',
|
||||||
EMS_DEVICE: 'EMS Gerät',
|
EMS_DEVICE: 'EMS Gerät',
|
||||||
SUCCESS: 'ERFOLG',
|
SUCCESS: 'ERFOLG',
|
||||||
FAIL: 'FEHLER',
|
FAIL: 'FEHLER',
|
||||||
QUALITY: 'QUALITÄT',
|
QUALITY: 'QUALITÄT',
|
||||||
SCAN: 'Suche',
|
|
||||||
STATUS_NAMES: [
|
STATUS_NAMES: [
|
||||||
'EMS-Telegramme empfangen (Rx)',
|
'EMS-Telegramme empfangen (Rx)',
|
||||||
'EMS-Telegramme gelesen (Tx)',
|
'EMS-Telegramme gelesen (Tx)',
|
||||||
@@ -127,7 +119,6 @@ const de: Translation = {
|
|||||||
ENUM_FORMAT: 'Enum Format API/MQTT',
|
ENUM_FORMAT: 'Enum Format API/MQTT',
|
||||||
INDEX: 'Index',
|
INDEX: 'Index',
|
||||||
ENABLE_PARASITE: '1-Wire parasitäre Stomversorgung',
|
ENABLE_PARASITE: '1-Wire parasitäre Stomversorgung',
|
||||||
LOGGING: 'Protokollierung',
|
|
||||||
LOG_HEX: 'EMS-Telegramme hexadezimal protokollieren',
|
LOG_HEX: 'EMS-Telegramme hexadezimal protokollieren',
|
||||||
ENABLE_SYSLOG: 'Syslog aktivieren',
|
ENABLE_SYSLOG: 'Syslog aktivieren',
|
||||||
LOG_LEVEL: 'Protokollebene',
|
LOG_LEVEL: 'Protokollebene',
|
||||||
@@ -172,7 +163,6 @@ const de: Translation = {
|
|||||||
CLOSE: 'Schließen',
|
CLOSE: 'Schließen',
|
||||||
USE: 'Verwenden Sie',
|
USE: 'Verwenden Sie',
|
||||||
FACTORY_RESET: 'Werkseinstellung',
|
FACTORY_RESET: 'Werkseinstellung',
|
||||||
SYSTEM_FACTORY_TEXT: 'EMS-ESP wurde auf Werkseinstellung gesetzt und startet als Zugangspunkt neu.',
|
|
||||||
SYSTEM_FACTORY_TEXT_DIALOG: 'Sind Sie sicher, alle Einstellungen auf Werkseinstellung zu setzen?',
|
SYSTEM_FACTORY_TEXT_DIALOG: 'Sind Sie sicher, alle Einstellungen auf Werkseinstellung zu setzen?',
|
||||||
AVAILABLE_VERSION: 'Neuesten verfügbaren Versionen',
|
AVAILABLE_VERSION: 'Neuesten verfügbaren Versionen',
|
||||||
STABLE: 'Stabil',
|
STABLE: 'Stabil',
|
||||||
@@ -188,8 +178,6 @@ const de: Translation = {
|
|||||||
DOWNLOAD_SETTINGS_TEXT: 'Erstellen Sie eine Sicherung Ihrer Konfigurationen und Einstellungen',
|
DOWNLOAD_SETTINGS_TEXT: 'Erstellen Sie eine Sicherung Ihrer Konfigurationen und Einstellungen',
|
||||||
DOWNLOAD_SETTINGS_TEXT2: 'Exportiere alle Werte',
|
DOWNLOAD_SETTINGS_TEXT2: 'Exportiere alle Werte',
|
||||||
DOWNLOAD_SYSTEM_BACKUP: 'Systemsicherung',
|
DOWNLOAD_SYSTEM_BACKUP: 'Systemsicherung',
|
||||||
UPLOAD_TEXT: 'Laden Sie eine neue Firmware-Datei (.bin) oder eine Sicherungsdatei (.json) hoch',
|
|
||||||
UPLOAD_DROP_TEXT: 'Legen Sie eine Firmware-Datei (.bin) ab oder klicken Sie hier',
|
|
||||||
ERROR: 'Unerwarteter Fehler, bitte versuchen Sie es erneut.',
|
ERROR: 'Unerwarteter Fehler, bitte versuchen Sie es erneut.',
|
||||||
TIME_SET: 'Zeit gesetzt',
|
TIME_SET: 'Zeit gesetzt',
|
||||||
MANAGE_USERS: 'Nutzerverwaltung',
|
MANAGE_USERS: 'Nutzerverwaltung',
|
||||||
@@ -257,9 +245,7 @@ const de: Translation = {
|
|||||||
NETWORK_SCAN: 'Suche nach WiFi-Netzwerken',
|
NETWORK_SCAN: 'Suche nach WiFi-Netzwerken',
|
||||||
IDLE: 'Leerlauf',
|
IDLE: 'Leerlauf',
|
||||||
LOST: 'Verloren',
|
LOST: 'Verloren',
|
||||||
SCANNING: 'Suche',
|
|
||||||
SCAN_AGAIN: 'Erneute Suche',
|
SCAN_AGAIN: 'Erneute Suche',
|
||||||
NETWORK_SCANNER: 'Netzwerksuche',
|
|
||||||
NETWORK_NO_WIFI: 'Keine WiFi-Netzwerke gefunden',
|
NETWORK_NO_WIFI: 'Keine WiFi-Netzwerke gefunden',
|
||||||
NETWORK_BLANK_BSSID: 'Freilassen, um nur SSID für die Verbindung zu nutzen.',
|
NETWORK_BLANK_BSSID: 'Freilassen, um nur SSID für die Verbindung zu nutzen.',
|
||||||
TX_POWER: 'Tx Leistung',
|
TX_POWER: 'Tx Leistung',
|
||||||
@@ -294,9 +280,6 @@ const de: Translation = {
|
|||||||
ONCHANGE: 'Bei Änderung',
|
ONCHANGE: 'Bei Änderung',
|
||||||
CONDITION: 'Zustand',
|
CONDITION: 'Zustand',
|
||||||
SCHEDULE_UPDATED: 'Zeitplan aktualisiert',
|
SCHEDULE_UPDATED: 'Zeitplan aktualisiert',
|
||||||
SCHEDULE_TIMER_1: 'beim Start',
|
|
||||||
SCHEDULE_TIMER_2: 'jede Minute',
|
|
||||||
SCHEDULE_TIMER_3: 'jede Stunde',
|
|
||||||
CUSTOM_ENTITIES: 'Individuelle Entitäten',
|
CUSTOM_ENTITIES: 'Individuelle Entitäten',
|
||||||
ENTITIES_HELP_1: 'Definition eigener EMS-Werte oder dynamischer Variablen',
|
ENTITIES_HELP_1: 'Definition eigener EMS-Werte oder dynamischer Variablen',
|
||||||
ENTITIES_UPDATED: 'Entitäten gespeichert',
|
ENTITIES_UPDATED: 'Entitäten gespeichert',
|
||||||
@@ -312,9 +295,7 @@ const de: Translation = {
|
|||||||
ACTIVELOW: 'Aktiv Negativ',
|
ACTIVELOW: 'Aktiv Negativ',
|
||||||
UNCHANGED: 'Unverändert',
|
UNCHANGED: 'Unverändert',
|
||||||
ALWAYS: 'Immer',
|
ALWAYS: 'Immer',
|
||||||
ACTIVITY: 'Aktivität',
|
|
||||||
CONFIGURE: 'Konfiguriere {0}',
|
CONFIGURE: 'Konfiguriere {0}',
|
||||||
SYSTEM_MEMORY: 'Systemspeicher',
|
|
||||||
APPLICATION_SETTINGS_1: 'Ändern Sie die EMS-ESP-Anwendungseinstellungen.',
|
APPLICATION_SETTINGS_1: 'Ändern Sie die EMS-ESP-Anwendungseinstellungen.',
|
||||||
SECURITY_1: 'Benutzer hinzufügen oder entfernen',
|
SECURITY_1: 'Benutzer hinzufügen oder entfernen',
|
||||||
DOWNLOAD_UPLOAD_1: 'Einstellungen und Firmware herunterladen/hochladen',
|
DOWNLOAD_UPLOAD_1: 'Einstellungen und Firmware herunterladen/hochladen',
|
||||||
@@ -326,7 +307,6 @@ const de: Translation = {
|
|||||||
RENAME: 'Umbenennen',
|
RENAME: 'Umbenennen',
|
||||||
ENABLE_MODBUS: 'Modbus aktivieren',
|
ENABLE_MODBUS: 'Modbus aktivieren',
|
||||||
VIEW_LOG: 'Sehen Sie sich das Protokoll an, um Probleme zu diagnostizieren.',
|
VIEW_LOG: 'Sehen Sie sich das Protokoll an, um Probleme zu diagnostizieren.',
|
||||||
UPLOAD_DRAG: 'Ziehen Sie eine Datei hierher oder klicken Sie, um eine auszuwählen.',
|
|
||||||
SERVICES: 'Dienste',
|
SERVICES: 'Dienste',
|
||||||
ALLVALUES: 'Alle Werte',
|
ALLVALUES: 'Alle Werte',
|
||||||
SPECIAL_FUNCTIONS: 'Sonderfunktionen',
|
SPECIAL_FUNCTIONS: 'Sonderfunktionen',
|
||||||
@@ -352,10 +332,8 @@ const de: Translation = {
|
|||||||
THIS_VERSION: 'Diese Version',
|
THIS_VERSION: 'Diese Version',
|
||||||
PLATFORM: 'Plattform',
|
PLATFORM: 'Plattform',
|
||||||
RELEASE_TYPE: 'Release Typ',
|
RELEASE_TYPE: 'Release Typ',
|
||||||
INTERNET_CONNECTION_REQUIRED: 'Für die automatische Versionsprüfung und Aktualisierung ist eine Internetverbindung erforderlich',
|
|
||||||
SWITCH_RELEASE_TYPE: 'Zum {0}-Release wechseln',
|
SWITCH_RELEASE_TYPE: 'Zum {0}-Release wechseln',
|
||||||
FIRMWARE_VERSION_INFO: 'Firmware-Versionsinformation',
|
FIRMWARE_VERSION_INFO: 'Firmware-Versionsinformation',
|
||||||
NO_DATA: 'Keine Daten',
|
|
||||||
USER_PROFILE: 'Benutzerprofil',
|
USER_PROFILE: 'Benutzerprofil',
|
||||||
STORED_VERSIONS: 'Gespeicherte Versionen',
|
STORED_VERSIONS: 'Gespeicherte Versionen',
|
||||||
ONLINE_HELP: 'Online-Hilfe',
|
ONLINE_HELP: 'Online-Hilfe',
|
||||||
@@ -366,9 +344,14 @@ const de: Translation = {
|
|||||||
TEST_EMAIL_SUCCESSFUL: 'Test email erfolgreich gesendet',
|
TEST_EMAIL_SUCCESSFUL: 'Test email erfolgreich gesendet',
|
||||||
SYSTEM_NAME: 'Systemname',
|
SYSTEM_NAME: 'Systemname',
|
||||||
COMMANDS: 'Befehle',
|
COMMANDS: 'Befehle',
|
||||||
COMMANDS_UPDATED: 'Befehle wurden aktualisiert',
|
|
||||||
COMMANDS_HELP_1: 'Definieren Sie eigene Befehle für die EMS-Magistral',
|
COMMANDS_HELP_1: 'Definieren Sie eigene Befehle für die EMS-Magistral',
|
||||||
EXECUTE_COMMAND_SENT: 'Befehl gesendet',
|
COMMAND_EXECUTED: 'Befehl erfolgreich ausgeführt',
|
||||||
|
RUN: 'Befehl ausführen',
|
||||||
|
INTERNET_CONNECTION_CHECK: 'Online-Prüfung auf verfügbare Versionen',
|
||||||
|
UPLOAD_BACKUP_TEXT: 'Klicken Sie, um eine Sicherungsdatei (.json) auszuwählen oder zu ziehen und abzulegen',
|
||||||
|
UPLOAD_FIRMWARE_TEXT: 'Klicken Sie, um eine Firmware-Datei (.bin/.md5) auszuwählen oder zu ziehen und abzulegen',
|
||||||
|
UPLOAD_MD5_RECEIVED: 'Prüfsumme empfangen, jetzt die Firmware-Datei (.bin) hochladen',
|
||||||
|
UPLOAD_MD5_MATCHED: 'MD5 stimmt überein, Firmware wird installiert...'
|
||||||
};
|
};
|
||||||
|
|
||||||
export default de;
|
export default de;
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import type { Translation } from '../i18n-types';
|
|||||||
const en: Translation = {
|
const en: Translation = {
|
||||||
LANGUAGE: 'Language',
|
LANGUAGE: 'Language',
|
||||||
RETRY: 'Retry',
|
RETRY: 'Retry',
|
||||||
LOADING: 'Loading',
|
|
||||||
IS_REQUIRED: '{0} is required',
|
IS_REQUIRED: '{0} is required',
|
||||||
SIGN_IN: 'Sign In',
|
SIGN_IN: 'Sign In',
|
||||||
SIGN_OUT: 'Sign Out',
|
SIGN_OUT: 'Sign Out',
|
||||||
@@ -37,13 +36,11 @@ const en: Translation = {
|
|||||||
VALUE: '{{value|Value}}',
|
VALUE: '{{value|Value}}',
|
||||||
DEVICES: 'Devices',
|
DEVICES: 'Devices',
|
||||||
SENSORS: 'Sensors',
|
SENSORS: 'Sensors',
|
||||||
RUN_COMMAND: 'Call Command',
|
|
||||||
CHANGE_VALUE: 'Change Value',
|
CHANGE_VALUE: 'Change Value',
|
||||||
CANCEL: 'Cancel',
|
CANCEL: 'Cancel',
|
||||||
REMOVE_ALL: 'Remove All',
|
REMOVE_ALL: 'Remove All',
|
||||||
APPLY_CHANGES: 'Apply Changes ({0})',
|
APPLY_CHANGES: 'Apply Changes ({0})',
|
||||||
UPDATE: 'Update',
|
UPDATE: 'Update',
|
||||||
EXECUTE: 'Execute',
|
|
||||||
REMOVE: 'Remove',
|
REMOVE: 'Remove',
|
||||||
PROBLEM_UPDATING: 'Problem updating',
|
PROBLEM_UPDATING: 'Problem updating',
|
||||||
PROBLEM_LOADING: 'Problem loading',
|
PROBLEM_LOADING: 'Problem loading',
|
||||||
@@ -52,8 +49,6 @@ const en: Translation = {
|
|||||||
SETTINGS: 'Settings',
|
SETTINGS: 'Settings',
|
||||||
UPDATED_OF: '{0} Updated',
|
UPDATED_OF: '{0} Updated',
|
||||||
UPDATE_OF: '{0} Update',
|
UPDATE_OF: '{0} Update',
|
||||||
REMOVED_OF: '{0} Removed',
|
|
||||||
DELETION_OF: '{0} Deletion',
|
|
||||||
OFFSET: 'Offset',
|
OFFSET: 'Offset',
|
||||||
FACTOR: 'Factor',
|
FACTOR: 'Factor',
|
||||||
FREQ: 'Frequency',
|
FREQ: 'Frequency',
|
||||||
@@ -66,17 +61,14 @@ const en: Translation = {
|
|||||||
TEMP_SENSORS: 'Temperature Sensors',
|
TEMP_SENSORS: 'Temperature Sensors',
|
||||||
WRITE_CMD_SENT: 'Write command sent',
|
WRITE_CMD_SENT: 'Write command sent',
|
||||||
EMS_BUS_WARNING: 'EMS bus disconnected. If this warning still persists after a few seconds please check settings and board profile',
|
EMS_BUS_WARNING: 'EMS bus disconnected. If this warning still persists after a few seconds please check settings and board profile',
|
||||||
EMS_BUS_SCANNING: 'Scanning for EMS devices...',
|
|
||||||
CONNECTED: 'Connected',
|
CONNECTED: 'Connected',
|
||||||
TX_ISSUES: 'Tx issues - check Tx Mode',
|
TX_ISSUES: 'Tx issues - check Tx Mode',
|
||||||
DISCONNECTED: 'Disconnected',
|
DISCONNECTED: 'Disconnected',
|
||||||
EMS_SCAN: 'Are you sure you want to initiate a full device scan of the EMS bus?',
|
|
||||||
DATA_TRAFFIC: 'Data Traffic',
|
DATA_TRAFFIC: 'Data Traffic',
|
||||||
EMS_DEVICE: 'EMS Device',
|
EMS_DEVICE: 'EMS Device',
|
||||||
SUCCESS: 'SUCCESS',
|
SUCCESS: 'SUCCESS',
|
||||||
FAIL: 'FAIL',
|
FAIL: 'FAIL',
|
||||||
QUALITY: 'QUALITY',
|
QUALITY: 'QUALITY',
|
||||||
SCAN: 'Scan',
|
|
||||||
STATUS_NAMES: [
|
STATUS_NAMES: [
|
||||||
'EMS Telegrams Received (Rx)',
|
'EMS Telegrams Received (Rx)',
|
||||||
'EMS Reads (Tx)',
|
'EMS Reads (Tx)',
|
||||||
@@ -127,7 +119,6 @@ const en: Translation = {
|
|||||||
ENUM_FORMAT: 'Enum Format API/MQTT',
|
ENUM_FORMAT: 'Enum Format API/MQTT',
|
||||||
INDEX: 'Index',
|
INDEX: 'Index',
|
||||||
ENABLE_PARASITE: 'Enable 1-Wire Parasite-Power',
|
ENABLE_PARASITE: 'Enable 1-Wire Parasite-Power',
|
||||||
LOGGING: 'Logging',
|
|
||||||
LOG_HEX: 'Log EMS telegrams in hexadecimal',
|
LOG_HEX: 'Log EMS telegrams in hexadecimal',
|
||||||
ENABLE_SYSLOG: 'Enable Syslog',
|
ENABLE_SYSLOG: 'Enable Syslog',
|
||||||
LOG_LEVEL: 'Log Level',
|
LOG_LEVEL: 'Log Level',
|
||||||
@@ -172,7 +163,6 @@ const en: Translation = {
|
|||||||
CLOSE: 'Close',
|
CLOSE: 'Close',
|
||||||
USE: 'Use',
|
USE: 'Use',
|
||||||
FACTORY_RESET: 'Factory Reset',
|
FACTORY_RESET: 'Factory Reset',
|
||||||
SYSTEM_FACTORY_TEXT: 'Device has been factory reset and will now restart',
|
|
||||||
SYSTEM_FACTORY_TEXT_DIALOG: 'Are you sure you want to reset EMS-ESP to its factory defaults?',
|
SYSTEM_FACTORY_TEXT_DIALOG: 'Are you sure you want to reset EMS-ESP to its factory defaults?',
|
||||||
AVAILABLE_VERSION: 'Latest Available Versions',
|
AVAILABLE_VERSION: 'Latest Available Versions',
|
||||||
STABLE: 'Stable',
|
STABLE: 'Stable',
|
||||||
@@ -188,8 +178,6 @@ const en: Translation = {
|
|||||||
DOWNLOAD_SETTINGS_TEXT: 'Create a backup of your configuration and settings',
|
DOWNLOAD_SETTINGS_TEXT: 'Create a backup of your configuration and settings',
|
||||||
DOWNLOAD_SETTINGS_TEXT2: 'Export all values',
|
DOWNLOAD_SETTINGS_TEXT2: 'Export all values',
|
||||||
DOWNLOAD_SYSTEM_BACKUP: 'System Backup',
|
DOWNLOAD_SYSTEM_BACKUP: 'System Backup',
|
||||||
UPLOAD_TEXT: 'Upload a new firmware file (.bin) or a backup file (.json)',
|
|
||||||
UPLOAD_DROP_TEXT: 'Drop a firmware .bin file or click here',
|
|
||||||
ERROR: 'Unexpected Error, please try again',
|
ERROR: 'Unexpected Error, please try again',
|
||||||
TIME_SET: 'Time set',
|
TIME_SET: 'Time set',
|
||||||
MANAGE_USERS: 'Manage Users',
|
MANAGE_USERS: 'Manage Users',
|
||||||
@@ -257,9 +245,7 @@ const en: Translation = {
|
|||||||
NETWORK_SCAN: 'Scan WiFi Networks',
|
NETWORK_SCAN: 'Scan WiFi Networks',
|
||||||
IDLE: 'Idle',
|
IDLE: 'Idle',
|
||||||
LOST: 'Lost',
|
LOST: 'Lost',
|
||||||
SCANNING: 'Scanning',
|
|
||||||
SCAN_AGAIN: 'Scan again',
|
SCAN_AGAIN: 'Scan again',
|
||||||
NETWORK_SCANNER: 'Network Scanner',
|
|
||||||
NETWORK_NO_WIFI: 'No WiFi networks found',
|
NETWORK_NO_WIFI: 'No WiFi networks found',
|
||||||
NETWORK_BLANK_BSSID: 'leave blank to use only SSID',
|
NETWORK_BLANK_BSSID: 'leave blank to use only SSID',
|
||||||
TX_POWER: 'Tx Power',
|
TX_POWER: 'Tx Power',
|
||||||
@@ -294,9 +280,6 @@ const en: Translation = {
|
|||||||
ONCHANGE: 'On Change',
|
ONCHANGE: 'On Change',
|
||||||
CONDITION: 'Condition',
|
CONDITION: 'Condition',
|
||||||
SCHEDULE_UPDATED: 'Schedule updated',
|
SCHEDULE_UPDATED: 'Schedule updated',
|
||||||
SCHEDULE_TIMER_1: 'on startup',
|
|
||||||
SCHEDULE_TIMER_2: 'every minute',
|
|
||||||
SCHEDULE_TIMER_3: 'every hour',
|
|
||||||
CUSTOM_ENTITIES: 'Custom Entities',
|
CUSTOM_ENTITIES: 'Custom Entities',
|
||||||
ENTITIES_HELP_1: 'Define custom EMS entities or dynamic user variables',
|
ENTITIES_HELP_1: 'Define custom EMS entities or dynamic user variables',
|
||||||
ENTITIES_UPDATED: 'Entities updated',
|
ENTITIES_UPDATED: 'Entities updated',
|
||||||
@@ -312,9 +295,7 @@ const en: Translation = {
|
|||||||
ACTIVELOW: 'Active Low',
|
ACTIVELOW: 'Active Low',
|
||||||
UNCHANGED: 'Unchanged',
|
UNCHANGED: 'Unchanged',
|
||||||
ALWAYS: 'Always',
|
ALWAYS: 'Always',
|
||||||
ACTIVITY: 'Activity',
|
|
||||||
CONFIGURE: 'Configure {0}',
|
CONFIGURE: 'Configure {0}',
|
||||||
SYSTEM_MEMORY: 'System Memory',
|
|
||||||
APPLICATION_SETTINGS_1: 'Modify EMS-ESP Application Settings',
|
APPLICATION_SETTINGS_1: 'Modify EMS-ESP Application Settings',
|
||||||
SECURITY_1: 'Add or remove users',
|
SECURITY_1: 'Add or remove users',
|
||||||
DOWNLOAD_UPLOAD_1: 'Download and Upload Settings and Firmware',
|
DOWNLOAD_UPLOAD_1: 'Download and Upload Settings and Firmware',
|
||||||
@@ -326,7 +307,6 @@ const en: Translation = {
|
|||||||
RENAME: 'Rename',
|
RENAME: 'Rename',
|
||||||
ENABLE_MODBUS: 'Enable Modbus',
|
ENABLE_MODBUS: 'Enable Modbus',
|
||||||
VIEW_LOG: 'View log to diagnose issues',
|
VIEW_LOG: 'View log to diagnose issues',
|
||||||
UPLOAD_DRAG: 'drag and drop a file here or click to select one',
|
|
||||||
SERVICES: 'Services',
|
SERVICES: 'Services',
|
||||||
ALLVALUES: 'All Values',
|
ALLVALUES: 'All Values',
|
||||||
SPECIAL_FUNCTIONS: 'Special Functions',
|
SPECIAL_FUNCTIONS: 'Special Functions',
|
||||||
@@ -352,10 +332,8 @@ const en: Translation = {
|
|||||||
THIS_VERSION: 'This Version',
|
THIS_VERSION: 'This Version',
|
||||||
PLATFORM: 'Platform',
|
PLATFORM: 'Platform',
|
||||||
RELEASE_TYPE: 'Release Type',
|
RELEASE_TYPE: 'Release Type',
|
||||||
INTERNET_CONNECTION_REQUIRED: 'Internet connection required for automatic version checking and upgrading',
|
|
||||||
SWITCH_RELEASE_TYPE: 'Switch to {0} release',
|
SWITCH_RELEASE_TYPE: 'Switch to {0} release',
|
||||||
FIRMWARE_VERSION_INFO: 'Firmware Version Information',
|
FIRMWARE_VERSION_INFO: 'Firmware Version Information',
|
||||||
NO_DATA: 'No data',
|
|
||||||
USER_PROFILE: 'User Profile',
|
USER_PROFILE: 'User Profile',
|
||||||
STORED_VERSIONS: 'Stored Versions',
|
STORED_VERSIONS: 'Stored Versions',
|
||||||
ONLINE_HELP: 'online help',
|
ONLINE_HELP: 'online help',
|
||||||
@@ -366,9 +344,14 @@ const en: Translation = {
|
|||||||
TEST_EMAIL_SUCCESSFUL: 'Test email sent successfully',
|
TEST_EMAIL_SUCCESSFUL: 'Test email sent successfully',
|
||||||
SYSTEM_NAME: 'System Name',
|
SYSTEM_NAME: 'System Name',
|
||||||
COMMANDS: 'Commands',
|
COMMANDS: 'Commands',
|
||||||
COMMANDS_UPDATED: 'Commands updated',
|
|
||||||
COMMANDS_HELP_1: 'Define reusable named commands below. These can be executed from the console, API/MQTT, or referenced by the Scheduler',
|
COMMANDS_HELP_1: 'Define reusable named commands below. These can be executed from the console, API/MQTT, or referenced by the Scheduler',
|
||||||
EXECUTE_COMMAND_SENT: 'Command sent',
|
COMMAND_EXECUTED: 'Command executed successfully',
|
||||||
|
RUN: 'Run',
|
||||||
|
INTERNET_CONNECTION_CHECK: 'Checking online for available versions',
|
||||||
|
UPLOAD_BACKUP_TEXT: 'Click to select or Drag&Drop a backup file (.json) here',
|
||||||
|
UPLOAD_FIRMWARE_TEXT: 'Click to select or Drag&Drop a firmware .bin/.md5 file',
|
||||||
|
UPLOAD_MD5_RECEIVED: 'Digest received, now upload the firmware .bin file',
|
||||||
|
UPLOAD_MD5_MATCHED: 'MD5 matches, installing firmware...'
|
||||||
};
|
};
|
||||||
|
|
||||||
export default en;
|
export default en;
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import type { Translation } from '../i18n-types';
|
|||||||
const fr: Translation = {
|
const fr: Translation = {
|
||||||
LANGUAGE: 'Langue',
|
LANGUAGE: 'Langue',
|
||||||
RETRY: 'Réessayer',
|
RETRY: 'Réessayer',
|
||||||
LOADING: 'Chargement',
|
|
||||||
IS_REQUIRED: '{0} est requis',
|
IS_REQUIRED: '{0} est requis',
|
||||||
SIGN_IN: 'Se connecter',
|
SIGN_IN: 'Se connecter',
|
||||||
SIGN_OUT: 'Se déconnecter',
|
SIGN_OUT: 'Se déconnecter',
|
||||||
@@ -37,13 +36,11 @@ const fr: Translation = {
|
|||||||
VALUE: 'Valeur',
|
VALUE: 'Valeur',
|
||||||
DEVICES: 'Appareils',
|
DEVICES: 'Appareils',
|
||||||
SENSORS: 'Capteurs',
|
SENSORS: 'Capteurs',
|
||||||
RUN_COMMAND: 'Lancer une commande',
|
|
||||||
CHANGE_VALUE: 'Changer la valeur',
|
CHANGE_VALUE: 'Changer la valeur',
|
||||||
CANCEL: 'Annuler',
|
CANCEL: 'Annuler',
|
||||||
REMOVE_ALL: 'Supprimer tout',
|
REMOVE_ALL: 'Supprimer tout',
|
||||||
APPLY_CHANGES: 'Appliquer les changements ({0})',
|
APPLY_CHANGES: 'Appliquer les changements ({0})',
|
||||||
UPDATE: 'Update',
|
UPDATE: 'Update',
|
||||||
EXECUTE: 'Execute',
|
|
||||||
REMOVE: 'Enlever',
|
REMOVE: 'Enlever',
|
||||||
PROBLEM_UPDATING: 'Problème lors de la mise à jour',
|
PROBLEM_UPDATING: 'Problème lors de la mise à jour',
|
||||||
PROBLEM_LOADING: 'Problème lors du chargement',
|
PROBLEM_LOADING: 'Problème lors du chargement',
|
||||||
@@ -52,8 +49,6 @@ const fr: Translation = {
|
|||||||
SETTINGS: 'Paramètres',
|
SETTINGS: 'Paramètres',
|
||||||
UPDATED_OF: '{0} mis à jour',
|
UPDATED_OF: '{0} mis à jour',
|
||||||
UPDATE_OF: 'Mise à jour de {0}',
|
UPDATE_OF: 'Mise à jour de {0}',
|
||||||
REMOVED_OF: '{0} enlevé',
|
|
||||||
DELETION_OF: '{0} supprimé',
|
|
||||||
OFFSET: 'Offset',
|
OFFSET: 'Offset',
|
||||||
FACTOR: 'Facteur',
|
FACTOR: 'Facteur',
|
||||||
FREQ: 'Fréquence',
|
FREQ: 'Fréquence',
|
||||||
@@ -66,17 +61,14 @@ const fr: Translation = {
|
|||||||
TEMP_SENSORS: 'Capteurs de température',
|
TEMP_SENSORS: 'Capteurs de température',
|
||||||
WRITE_CMD_SENT: 'Envoyer la commande sent',
|
WRITE_CMD_SENT: 'Envoyer la commande sent',
|
||||||
EMS_BUS_WARNING: 'Bus EMS déconnecté. Si ce message persiste après quelques secondes, vérifiez les paramètres et la configuration de la carte.',
|
EMS_BUS_WARNING: 'Bus EMS déconnecté. Si ce message persiste après quelques secondes, vérifiez les paramètres et la configuration de la carte.',
|
||||||
EMS_BUS_SCANNING: 'Scan des appareils EMS...',
|
|
||||||
CONNECTED: 'Connecté',
|
CONNECTED: 'Connecté',
|
||||||
TX_ISSUES: 'Problèmes de transmission (Tx) - Essayez un autre mode Tx',
|
TX_ISSUES: 'Problèmes de transmission (Tx) - Essayez un autre mode Tx',
|
||||||
DISCONNECTED: 'Déconnecté',
|
DISCONNECTED: 'Déconnecté',
|
||||||
EMS_SCAN: 'Etes-vous sûr de vouloir lancer un scan complet du bus EMS ?',
|
|
||||||
DATA_TRAFFIC: 'Traffic des données',
|
DATA_TRAFFIC: 'Traffic des données',
|
||||||
EMS_DEVICE: 'Appareils EMS',
|
EMS_DEVICE: 'Appareils EMS',
|
||||||
SUCCESS: 'SUCCÈS',
|
SUCCESS: 'SUCCÈS',
|
||||||
FAIL: 'ÉCHEC',
|
FAIL: 'ÉCHEC',
|
||||||
QUALITY: 'QUALITÉ',
|
QUALITY: 'QUALITÉ',
|
||||||
SCAN: 'Scan',
|
|
||||||
STATUS_NAMES: [
|
STATUS_NAMES: [
|
||||||
'Télégrammes EMS reçus (Rx)',
|
'Télégrammes EMS reçus (Rx)',
|
||||||
'Lectures EMS (Tx)',
|
'Lectures EMS (Tx)',
|
||||||
@@ -127,7 +119,6 @@ const fr: Translation = {
|
|||||||
ENUM_FORMAT: 'Format enum API/MQTT',
|
ENUM_FORMAT: 'Format enum API/MQTT',
|
||||||
INDEX: 'Index',
|
INDEX: 'Index',
|
||||||
ENABLE_PARASITE: 'Activer la puissance 1-wire parasite',
|
ENABLE_PARASITE: 'Activer la puissance 1-wire parasite',
|
||||||
LOGGING: 'Journal',
|
|
||||||
LOG_HEX: 'Enregistrer les télégrammes EMS en hexadécimal',
|
LOG_HEX: 'Enregistrer les télégrammes EMS en hexadécimal',
|
||||||
ENABLE_SYSLOG: 'Activer les logs système',
|
ENABLE_SYSLOG: 'Activer les logs système',
|
||||||
LOG_LEVEL: 'Niveau de log',
|
LOG_LEVEL: 'Niveau de log',
|
||||||
@@ -172,7 +163,6 @@ const fr: Translation = {
|
|||||||
CLOSE: 'Fermer',
|
CLOSE: 'Fermer',
|
||||||
USE: 'Utiliser',
|
USE: 'Utiliser',
|
||||||
FACTORY_RESET: 'Réinitialisation',
|
FACTORY_RESET: 'Réinitialisation',
|
||||||
SYSTEM_FACTORY_TEXT: "L'appareil a été réinitialisé et va maintenant redémarrer",
|
|
||||||
SYSTEM_FACTORY_TEXT_DIALOG: "Êtes-vous sûr de vouloir réinitialiser l'appareil à ses paramètres d'usine ?",
|
SYSTEM_FACTORY_TEXT_DIALOG: "Êtes-vous sûr de vouloir réinitialiser l'appareil à ses paramètres d'usine ?",
|
||||||
AVAILABLE_VERSION: 'Versions disponibles',
|
AVAILABLE_VERSION: 'Versions disponibles',
|
||||||
STABLE: 'Stable',
|
STABLE: 'Stable',
|
||||||
@@ -188,8 +178,6 @@ const fr: Translation = {
|
|||||||
DOWNLOAD_SETTINGS_TEXT: 'Créer une sauvegarde de vos paramètres et configurations',
|
DOWNLOAD_SETTINGS_TEXT: 'Créer une sauvegarde de vos paramètres et configurations',
|
||||||
DOWNLOAD_SETTINGS_TEXT2: 'Exporter toutes les valeurs',
|
DOWNLOAD_SETTINGS_TEXT2: 'Exporter toutes les valeurs',
|
||||||
DOWNLOAD_SYSTEM_BACKUP: 'Sauvegarde système',
|
DOWNLOAD_SYSTEM_BACKUP: 'Sauvegarde système',
|
||||||
UPLOAD_TEXT: 'Télécharger un nouveau fichier firmware (.bin) ou une sauvegarde (.json)',
|
|
||||||
UPLOAD_DROP_TEXT: 'Drop a firmware .bin file or click here',
|
|
||||||
ERROR: 'Erreur inattendue, veuillez réessayer',
|
ERROR: 'Erreur inattendue, veuillez réessayer',
|
||||||
TIME_SET: 'Time set',
|
TIME_SET: 'Time set',
|
||||||
MANAGE_USERS: 'Gérer les utilisateurs',
|
MANAGE_USERS: 'Gérer les utilisateurs',
|
||||||
@@ -257,9 +245,7 @@ const fr: Translation = {
|
|||||||
NETWORK_SCAN: 'Scanner les réseaux WiFi',
|
NETWORK_SCAN: 'Scanner les réseaux WiFi',
|
||||||
IDLE: 'Inactif',
|
IDLE: 'Inactif',
|
||||||
LOST: 'Perdu',
|
LOST: 'Perdu',
|
||||||
SCANNING: 'Scan en cours',
|
|
||||||
SCAN_AGAIN: 'Rescanner',
|
SCAN_AGAIN: 'Rescanner',
|
||||||
NETWORK_SCANNER: 'Scan réseau',
|
|
||||||
NETWORK_NO_WIFI: 'Pas de réseau WiFi trouvé',
|
NETWORK_NO_WIFI: 'Pas de réseau WiFi trouvé',
|
||||||
NETWORK_BLANK_BSSID: 'laisser vide pour utiliser uniquement le SSID',
|
NETWORK_BLANK_BSSID: 'laisser vide pour utiliser uniquement le SSID',
|
||||||
TX_POWER: 'Puissance Tx',
|
TX_POWER: 'Puissance Tx',
|
||||||
@@ -294,9 +280,6 @@ const fr: Translation = {
|
|||||||
ONCHANGE: 'Sur le changement',
|
ONCHANGE: 'Sur le changement',
|
||||||
CONDITION: 'Condition',
|
CONDITION: 'Condition',
|
||||||
SCHEDULE_UPDATED: 'Programme mis à jour',
|
SCHEDULE_UPDATED: 'Programme mis à jour',
|
||||||
SCHEDULE_TIMER_1: 'au démarrage',
|
|
||||||
SCHEDULE_TIMER_2: 'toutes les minutes',
|
|
||||||
SCHEDULE_TIMER_3: 'toutes les heures',
|
|
||||||
CUSTOM_ENTITIES: 'Entités personnalisées',
|
CUSTOM_ENTITIES: 'Entités personnalisées',
|
||||||
ENTITIES_HELP_1: 'Récupérer les entités personnalisées du bus EMS',
|
ENTITIES_HELP_1: 'Récupérer les entités personnalisées du bus EMS',
|
||||||
ENTITIES_UPDATED: 'Entités mises à jour',
|
ENTITIES_UPDATED: 'Entités mises à jour',
|
||||||
@@ -312,9 +295,7 @@ const fr: Translation = {
|
|||||||
ACTIVELOW: 'Actif bas',
|
ACTIVELOW: 'Actif bas',
|
||||||
UNCHANGED: 'Inchangé',
|
UNCHANGED: 'Inchangé',
|
||||||
ALWAYS: 'Toujours',
|
ALWAYS: 'Toujours',
|
||||||
ACTIVITY: 'Activité',
|
|
||||||
CONFIGURE: 'Configurer {0}',
|
CONFIGURE: 'Configurer {0}',
|
||||||
SYSTEM_MEMORY: 'Mémoire système',
|
|
||||||
APPLICATION_SETTINGS_1: 'Modifier les paramètres de l\'application EMS-ESP',
|
APPLICATION_SETTINGS_1: 'Modifier les paramètres de l\'application EMS-ESP',
|
||||||
SECURITY_1: 'Ajouter ou supprimer des utilisateurs',
|
SECURITY_1: 'Ajouter ou supprimer des utilisateurs',
|
||||||
DOWNLOAD_UPLOAD_1: 'Télécharger et mettre à jour les paramètres et le firmware',
|
DOWNLOAD_UPLOAD_1: 'Télécharger et mettre à jour les paramètres et le firmware',
|
||||||
@@ -326,7 +307,6 @@ const fr: Translation = {
|
|||||||
RENAME: 'Renommer',
|
RENAME: 'Renommer',
|
||||||
ENABLE_MODBUS: 'Activer Modbus',
|
ENABLE_MODBUS: 'Activer Modbus',
|
||||||
VIEW_LOG: 'Voir le journal pour diagnostiquer les problèmes',
|
VIEW_LOG: 'Voir le journal pour diagnostiquer les problèmes',
|
||||||
UPLOAD_DRAG: 'glisser-déposer un fichier ici ou cliquer pour en sélectionner un',
|
|
||||||
SERVICES: 'Services',
|
SERVICES: 'Services',
|
||||||
ALLVALUES: 'Toutes les valeurs',
|
ALLVALUES: 'Toutes les valeurs',
|
||||||
SPECIAL_FUNCTIONS: 'Fonctions spéciales',
|
SPECIAL_FUNCTIONS: 'Fonctions spéciales',
|
||||||
@@ -352,10 +332,8 @@ const fr: Translation = {
|
|||||||
THIS_VERSION: 'Cette version',
|
THIS_VERSION: 'Cette version',
|
||||||
PLATFORM: 'Plateforme',
|
PLATFORM: 'Plateforme',
|
||||||
RELEASE_TYPE: 'Type de version',
|
RELEASE_TYPE: 'Type de version',
|
||||||
INTERNET_CONNECTION_REQUIRED: 'Connexion Internet requise pour la vérification automatique des versions et la mise à niveau',
|
|
||||||
SWITCH_RELEASE_TYPE: 'Passer à la version {0}',
|
SWITCH_RELEASE_TYPE: 'Passer à la version {0}',
|
||||||
FIRMWARE_VERSION_INFO: 'Informations sur la version du firmware',
|
FIRMWARE_VERSION_INFO: 'Informations sur la version du firmware',
|
||||||
NO_DATA: 'Aucune donnée',
|
|
||||||
USER_PROFILE: 'Profil utilisateur',
|
USER_PROFILE: 'Profil utilisateur',
|
||||||
STORED_VERSIONS: 'Versions stockées',
|
STORED_VERSIONS: 'Versions stockées',
|
||||||
ONLINE_HELP: 'aide en ligne',
|
ONLINE_HELP: 'aide en ligne',
|
||||||
@@ -366,9 +344,14 @@ const fr: Translation = {
|
|||||||
TEST_EMAIL_SUCCESSFUL: 'Test email envoyé avec succès',
|
TEST_EMAIL_SUCCESSFUL: 'Test email envoyé avec succès',
|
||||||
SYSTEM_NAME: 'Nom du système',
|
SYSTEM_NAME: 'Nom du système',
|
||||||
COMMANDS: 'Commandes',
|
COMMANDS: 'Commandes',
|
||||||
COMMANDS_UPDATED: 'Commandes mises à jour',
|
|
||||||
COMMANDS_HELP_1: 'Définir des commandes personnalisées pour la magistral EMS',
|
COMMANDS_HELP_1: 'Définir des commandes personnalisées pour la magistral EMS',
|
||||||
EXECUTE_COMMAND_SENT: 'Commande envoyée',
|
COMMAND_EXECUTED: 'Commande exécutée avec succès',
|
||||||
|
RUN: 'Exécuter une commande',
|
||||||
|
INTERNET_CONNECTION_CHECK: 'Vérification en ligne pour les versions disponibles',
|
||||||
|
UPLOAD_BACKUP_TEXT: 'Cliquez pour sélectionner ou faites glisser et déposez un fichier de sauvegarde (.json) ici',
|
||||||
|
UPLOAD_FIRMWARE_TEXT: 'Cliquez pour sélectionner ou faites glisser et déposez un fichier firmware .bin/.md5 ici',
|
||||||
|
UPLOAD_MD5_RECEIVED: 'Empreinte reçue, téléversez maintenant le fichier firmware .bin',
|
||||||
|
UPLOAD_MD5_MATCHED: 'MD5 concordant, installation du firmware...'
|
||||||
};
|
};
|
||||||
|
|
||||||
export default fr;
|
export default fr;
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import type { Translation } from '../i18n-types';
|
|||||||
const it: Translation = {
|
const it: Translation = {
|
||||||
LANGUAGE: 'Lingua',
|
LANGUAGE: 'Lingua',
|
||||||
RETRY: 'Riprovare',
|
RETRY: 'Riprovare',
|
||||||
LOADING: 'Caricamento',
|
|
||||||
IS_REQUIRED: '{0} é richiesto',
|
IS_REQUIRED: '{0} é richiesto',
|
||||||
SIGN_IN: 'Connettersi',
|
SIGN_IN: 'Connettersi',
|
||||||
SIGN_OUT: 'Disconnettersi',
|
SIGN_OUT: 'Disconnettersi',
|
||||||
@@ -37,13 +36,11 @@ const it: Translation = {
|
|||||||
VALUE: '{{valore|Valore}}',
|
VALUE: '{{valore|Valore}}',
|
||||||
DEVICES: 'Dispositivi',
|
DEVICES: 'Dispositivi',
|
||||||
SENSORS: 'Sensori',
|
SENSORS: 'Sensori',
|
||||||
RUN_COMMAND: 'Esegui',
|
|
||||||
CHANGE_VALUE: 'Cambia Valore',
|
CHANGE_VALUE: 'Cambia Valore',
|
||||||
CANCEL: 'Annulla',
|
CANCEL: 'Annulla',
|
||||||
REMOVE_ALL: 'Rimuovi tutto',
|
REMOVE_ALL: 'Rimuovi tutto',
|
||||||
APPLY_CHANGES: 'Applica Cambiamenti ({0})',
|
APPLY_CHANGES: 'Applica Cambiamenti ({0})',
|
||||||
UPDATE: 'Update',
|
UPDATE: 'Update',
|
||||||
EXECUTE: 'Execute',
|
|
||||||
REMOVE: 'Elimina',
|
REMOVE: 'Elimina',
|
||||||
PROBLEM_UPDATING: 'Problema aggiornamento',
|
PROBLEM_UPDATING: 'Problema aggiornamento',
|
||||||
PROBLEM_LOADING: 'Problema caricamento',
|
PROBLEM_LOADING: 'Problema caricamento',
|
||||||
@@ -52,8 +49,6 @@ const it: Translation = {
|
|||||||
SETTINGS: 'Settings',
|
SETTINGS: 'Settings',
|
||||||
UPDATED_OF: '{0} Aggiornati',
|
UPDATED_OF: '{0} Aggiornati',
|
||||||
UPDATE_OF: 'Aggiorna {0}',
|
UPDATE_OF: 'Aggiorna {0}',
|
||||||
REMOVED_OF: '{0} Rimossi',
|
|
||||||
DELETION_OF: '{0} Cancellati',
|
|
||||||
OFFSET: 'Offset',
|
OFFSET: 'Offset',
|
||||||
FACTOR: 'Fattore',
|
FACTOR: 'Fattore',
|
||||||
FREQ: 'Frequenza',
|
FREQ: 'Frequenza',
|
||||||
@@ -66,17 +61,14 @@ const it: Translation = {
|
|||||||
TEMP_SENSORS: 'Sensori Temperatura',
|
TEMP_SENSORS: 'Sensori Temperatura',
|
||||||
WRITE_CMD_SENT: 'Scrittura comando inviata',
|
WRITE_CMD_SENT: 'Scrittura comando inviata',
|
||||||
EMS_BUS_WARNING: 'EMS bus disconnesso. Se questo avvertimento persiste dopo alcuni secondi prego verificare impostazioni scheda',
|
EMS_BUS_WARNING: 'EMS bus disconnesso. Se questo avvertimento persiste dopo alcuni secondi prego verificare impostazioni scheda',
|
||||||
EMS_BUS_SCANNING: 'Scansione dispositivi EMS ...',
|
|
||||||
CONNECTED: 'Connesso',
|
CONNECTED: 'Connesso',
|
||||||
TX_ISSUES: 'Problema di Tx - prova una modalità differente',
|
TX_ISSUES: 'Problema di Tx - prova una modalità differente',
|
||||||
DISCONNECTED: 'Disconnesso',
|
DISCONNECTED: 'Disconnesso',
|
||||||
EMS_SCAN: 'Sei sicuro di voler iniziare una scansione completa del bus EMS ?',
|
|
||||||
DATA_TRAFFIC: 'Traffico dati',
|
DATA_TRAFFIC: 'Traffico dati',
|
||||||
EMS_DEVICE: 'Dispositivo EMS ',
|
EMS_DEVICE: 'Dispositivo EMS ',
|
||||||
SUCCESS: 'SUCCESSO',
|
SUCCESS: 'SUCCESSO',
|
||||||
FAIL: 'FALLITO',
|
FAIL: 'FALLITO',
|
||||||
QUALITY: 'QUALITÂ',
|
QUALITY: 'QUALITÂ',
|
||||||
SCAN: 'Scansione',
|
|
||||||
STATUS_NAMES: [
|
STATUS_NAMES: [
|
||||||
'Telegrammi EMS Ricevuti (Rx)',
|
'Telegrammi EMS Ricevuti (Rx)',
|
||||||
'EMS Letti (Tx)',
|
'EMS Letti (Tx)',
|
||||||
@@ -127,7 +119,6 @@ const it: Translation = {
|
|||||||
ENUM_FORMAT: 'Enum Format API/MQTT',
|
ENUM_FORMAT: 'Enum Format API/MQTT',
|
||||||
INDEX: 'Indice',
|
INDEX: 'Indice',
|
||||||
ENABLE_PARASITE: 'Abilita potenza 1-wire parassita',
|
ENABLE_PARASITE: 'Abilita potenza 1-wire parassita',
|
||||||
LOGGING: 'Registrazione',
|
|
||||||
LOG_HEX: 'Registra telegrammi EMS in esadecimale',
|
LOG_HEX: 'Registra telegrammi EMS in esadecimale',
|
||||||
ENABLE_SYSLOG: 'Abilita Syslog',
|
ENABLE_SYSLOG: 'Abilita Syslog',
|
||||||
LOG_LEVEL: 'Livello registrazione',
|
LOG_LEVEL: 'Livello registrazione',
|
||||||
@@ -172,7 +163,6 @@ const it: Translation = {
|
|||||||
CLOSE: 'Chiudere',
|
CLOSE: 'Chiudere',
|
||||||
USE: 'Usa',
|
USE: 'Usa',
|
||||||
FACTORY_RESET: 'Impostazioni di fabbrica',
|
FACTORY_RESET: 'Impostazioni di fabbrica',
|
||||||
SYSTEM_FACTORY_TEXT: 'Il dispositivo è stato ripristinato alle impostazioni di fabbrica e ora verrà riavviato',
|
|
||||||
SYSTEM_FACTORY_TEXT_DIALOG: 'Sei sicuro di voler ripristinare il dispositivo alle impostazioni di fabbrica??',
|
SYSTEM_FACTORY_TEXT_DIALOG: 'Sei sicuro di voler ripristinare il dispositivo alle impostazioni di fabbrica??',
|
||||||
AVAILABLE_VERSION: 'Versioni disponibili',
|
AVAILABLE_VERSION: 'Versioni disponibili',
|
||||||
STABLE: 'Stabile',
|
STABLE: 'Stabile',
|
||||||
@@ -188,8 +178,6 @@ const it: Translation = {
|
|||||||
DOWNLOAD_SETTINGS_TEXT: 'Create a backup of your configuration and settings',
|
DOWNLOAD_SETTINGS_TEXT: 'Create a backup of your configuration and settings',
|
||||||
DOWNLOAD_SETTINGS_TEXT2: 'Esporta tutti i valori',
|
DOWNLOAD_SETTINGS_TEXT2: 'Esporta tutti i valori',
|
||||||
DOWNLOAD_SYSTEM_BACKUP: 'Backup sistema',
|
DOWNLOAD_SYSTEM_BACKUP: 'Backup sistema',
|
||||||
UPLOAD_TEXT: 'Upload a new firmware file (.bin) or a backup file (.json)',
|
|
||||||
UPLOAD_DROP_TEXT: 'Drop a firmware .bin file or click here',
|
|
||||||
ERROR: 'Errore Inaspettato, prego tenta ancora',
|
ERROR: 'Errore Inaspettato, prego tenta ancora',
|
||||||
TIME_SET: 'Imposta Ora',
|
TIME_SET: 'Imposta Ora',
|
||||||
MANAGE_USERS: 'Gestione Utenti',
|
MANAGE_USERS: 'Gestione Utenti',
|
||||||
@@ -257,9 +245,7 @@ const it: Translation = {
|
|||||||
NETWORK_SCAN: 'Scansione reti WiFi',
|
NETWORK_SCAN: 'Scansione reti WiFi',
|
||||||
IDLE: 'Inattivo',
|
IDLE: 'Inattivo',
|
||||||
LOST: 'Perso',
|
LOST: 'Perso',
|
||||||
SCANNING: 'Scansione',
|
|
||||||
SCAN_AGAIN: 'Scansiona ancora',
|
SCAN_AGAIN: 'Scansiona ancora',
|
||||||
NETWORK_SCANNER: 'Scansione Rete',
|
|
||||||
NETWORK_NO_WIFI: 'Nessuana rete WiFi trovata',
|
NETWORK_NO_WIFI: 'Nessuana rete WiFi trovata',
|
||||||
NETWORK_BLANK_BSSID: 'lasciare vuoto per usare solo SSID',
|
NETWORK_BLANK_BSSID: 'lasciare vuoto per usare solo SSID',
|
||||||
TX_POWER: 'Potenza Tx',
|
TX_POWER: 'Potenza Tx',
|
||||||
@@ -294,9 +280,6 @@ const it: Translation = {
|
|||||||
ONCHANGE: 'Sul cambiamento',
|
ONCHANGE: 'Sul cambiamento',
|
||||||
CONDITION: 'Condizione',
|
CONDITION: 'Condizione',
|
||||||
SCHEDULE_UPDATED: 'Calendario aggiornato',
|
SCHEDULE_UPDATED: 'Calendario aggiornato',
|
||||||
SCHEDULE_TIMER_1: 'All avvio',
|
|
||||||
SCHEDULE_TIMER_2: 'Ogni minuto',
|
|
||||||
SCHEDULE_TIMER_3: 'Ogni ora',
|
|
||||||
CUSTOM_ENTITIES: 'Entità personalizzate',
|
CUSTOM_ENTITIES: 'Entità personalizzate',
|
||||||
ENTITIES_HELP_1: 'Recupera entità personalizzate dal BUS EMS',
|
ENTITIES_HELP_1: 'Recupera entità personalizzate dal BUS EMS',
|
||||||
ENTITIES_UPDATED: 'Entità aggiornate',
|
ENTITIES_UPDATED: 'Entità aggiornate',
|
||||||
@@ -312,9 +295,7 @@ const it: Translation = {
|
|||||||
ACTIVELOW: 'Active Low',
|
ACTIVELOW: 'Active Low',
|
||||||
UNCHANGED: 'Unchanged',
|
UNCHANGED: 'Unchanged',
|
||||||
ALWAYS: 'Always',
|
ALWAYS: 'Always',
|
||||||
ACTIVITY: 'Activity',
|
|
||||||
CONFIGURE: 'Configure {0}',
|
CONFIGURE: 'Configure {0}',
|
||||||
SYSTEM_MEMORY: 'System Memory',
|
|
||||||
APPLICATION_SETTINGS_1: 'Modify EMS-ESP Application Settings',
|
APPLICATION_SETTINGS_1: 'Modify EMS-ESP Application Settings',
|
||||||
SECURITY_1: 'Add or remove users',
|
SECURITY_1: 'Add or remove users',
|
||||||
DOWNLOAD_UPLOAD_1: 'Download and Upload Settings and Firmware',
|
DOWNLOAD_UPLOAD_1: 'Download and Upload Settings and Firmware',
|
||||||
@@ -326,7 +307,6 @@ const it: Translation = {
|
|||||||
RENAME: 'Rename',
|
RENAME: 'Rename',
|
||||||
ENABLE_MODBUS: 'Abilita Modbus',
|
ENABLE_MODBUS: 'Abilita Modbus',
|
||||||
VIEW_LOG: 'Visualizza log per diagnosticare problemi',
|
VIEW_LOG: 'Visualizza log per diagnosticare problemi',
|
||||||
UPLOAD_DRAG: 'trascina e rilascia un file qui o clicca per selezionare uno',
|
|
||||||
SERVICES: 'Servizi',
|
SERVICES: 'Servizi',
|
||||||
ALLVALUES: 'Tutti i valori',
|
ALLVALUES: 'Tutti i valori',
|
||||||
SPECIAL_FUNCTIONS: 'Funzioni speciali',
|
SPECIAL_FUNCTIONS: 'Funzioni speciali',
|
||||||
@@ -352,10 +332,8 @@ const it: Translation = {
|
|||||||
THIS_VERSION: 'Questa versione',
|
THIS_VERSION: 'Questa versione',
|
||||||
PLATFORM: 'Piattaforma',
|
PLATFORM: 'Piattaforma',
|
||||||
RELEASE_TYPE: 'Tipo di rilascio',
|
RELEASE_TYPE: 'Tipo di rilascio',
|
||||||
INTERNET_CONNECTION_REQUIRED: 'Connessione internet richiesta per il controllo automatico delle versioni e l\'aggiornamento',
|
|
||||||
SWITCH_RELEASE_TYPE: 'Cambia in {0} rilascio',
|
SWITCH_RELEASE_TYPE: 'Cambia in {0} rilascio',
|
||||||
FIRMWARE_VERSION_INFO: 'Informazioni sulla versione del firmware',
|
FIRMWARE_VERSION_INFO: 'Informazioni sulla versione del firmware',
|
||||||
NO_DATA: 'Nessun dato',
|
|
||||||
USER_PROFILE: 'Profilo utente',
|
USER_PROFILE: 'Profilo utente',
|
||||||
STORED_VERSIONS: 'Versioni memorizzate',
|
STORED_VERSIONS: 'Versioni memorizzate',
|
||||||
ONLINE_HELP: 'aiuto online',
|
ONLINE_HELP: 'aiuto online',
|
||||||
@@ -366,9 +344,14 @@ const it: Translation = {
|
|||||||
TEST_EMAIL_SUCCESSFUL: 'Test email inviata con successo',
|
TEST_EMAIL_SUCCESSFUL: 'Test email inviata con successo',
|
||||||
SYSTEM_NAME: 'Nome del sistema',
|
SYSTEM_NAME: 'Nome del sistema',
|
||||||
COMMANDS: 'Comandi',
|
COMMANDS: 'Comandi',
|
||||||
COMMANDS_UPDATED: 'Comandi aggiornati',
|
|
||||||
COMMANDS_HELP_1: 'Definisci comandi personalizzati per la magistrali EMS',
|
COMMANDS_HELP_1: 'Definisci comandi personalizzati per la magistrali EMS',
|
||||||
EXECUTE_COMMAND_SENT: 'Comando inviato',
|
COMMAND_EXECUTED: 'Comando eseguito con successo',
|
||||||
|
RUN: 'Esegui comando',
|
||||||
|
INTERNET_CONNECTION_CHECK: 'Controllo online per le versioni disponibili',
|
||||||
|
UPLOAD_BACKUP_TEXT: 'Klik per selezionare o trascinare e rilasciare un file di backup (.json) qui',
|
||||||
|
UPLOAD_FIRMWARE_TEXT: 'Klik per selezionare o trascinare e rilasciare un file firmware .bin/.md5 qui',
|
||||||
|
UPLOAD_MD5_RECEIVED: 'Digest ricevuto, ora caricare il file firmware .bin',
|
||||||
|
UPLOAD_MD5_MATCHED: 'MD5 corrispondente, installazione firmware...'
|
||||||
};
|
};
|
||||||
|
|
||||||
export default it;
|
export default it;
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import type { Translation } from '../i18n-types';
|
|||||||
const nl: Translation = {
|
const nl: Translation = {
|
||||||
LANGUAGE: 'Taal',
|
LANGUAGE: 'Taal',
|
||||||
RETRY: 'Opnieuw proberen',
|
RETRY: 'Opnieuw proberen',
|
||||||
LOADING: 'Laden',
|
|
||||||
IS_REQUIRED: '{0} is verplicht',
|
IS_REQUIRED: '{0} is verplicht',
|
||||||
SIGN_IN: 'Inloggen',
|
SIGN_IN: 'Inloggen',
|
||||||
SIGN_OUT: 'Uitloggen',
|
SIGN_OUT: 'Uitloggen',
|
||||||
@@ -37,13 +36,11 @@ const nl: Translation = {
|
|||||||
VALUE: '{{waarde|Waarde}}',
|
VALUE: '{{waarde|Waarde}}',
|
||||||
DEVICES: 'Apparaten',
|
DEVICES: 'Apparaten',
|
||||||
SENSORS: 'Sensoren',
|
SENSORS: 'Sensoren',
|
||||||
RUN_COMMAND: 'Call commando',
|
|
||||||
CHANGE_VALUE: 'Wijzig waarde',
|
CHANGE_VALUE: 'Wijzig waarde',
|
||||||
CANCEL: 'Annuleren',
|
CANCEL: 'Annuleren',
|
||||||
REMOVE_ALL: 'Verwijderen alles',
|
REMOVE_ALL: 'Verwijderen alles',
|
||||||
APPLY_CHANGES: 'Aanpassen ({0})',
|
APPLY_CHANGES: 'Aanpassen ({0})',
|
||||||
UPDATE: 'Update',
|
UPDATE: 'Update',
|
||||||
EXECUTE: 'Uitvoeren',
|
|
||||||
REMOVE: 'Verwijderen',
|
REMOVE: 'Verwijderen',
|
||||||
PROBLEM_UPDATING: 'Probleem met updaten',
|
PROBLEM_UPDATING: 'Probleem met updaten',
|
||||||
PROBLEM_LOADING: 'Probleem met laden',
|
PROBLEM_LOADING: 'Probleem met laden',
|
||||||
@@ -52,8 +49,6 @@ const nl: Translation = {
|
|||||||
SETTINGS: 'Instellingen',
|
SETTINGS: 'Instellingen',
|
||||||
UPDATED_OF: '{0} Bijgewerkt',
|
UPDATED_OF: '{0} Bijgewerkt',
|
||||||
UPDATE_OF: '{0} Bijwerken',
|
UPDATE_OF: '{0} Bijwerken',
|
||||||
REMOVED_OF: '{0} Verwijderd',
|
|
||||||
DELETION_OF: '{0} Verwijder',
|
|
||||||
OFFSET: 'Offset',
|
OFFSET: 'Offset',
|
||||||
FACTOR: 'Factor',
|
FACTOR: 'Factor',
|
||||||
FREQ: 'Frequentie',
|
FREQ: 'Frequentie',
|
||||||
@@ -66,17 +61,14 @@ const nl: Translation = {
|
|||||||
TEMP_SENSORS: 'Temperatuur Sensoren',
|
TEMP_SENSORS: 'Temperatuur Sensoren',
|
||||||
WRITE_CMD_SENT: 'Schrijf commando gestuurd',
|
WRITE_CMD_SENT: 'Schrijf commando gestuurd',
|
||||||
EMS_BUS_WARNING: 'EMS bus niet gevonden. Als deze waarschuwing blijft staan na een paar seconden loop dan de instellingen na en in het bijzonder het apparaat type profiel.',
|
EMS_BUS_WARNING: 'EMS bus niet gevonden. Als deze waarschuwing blijft staan na een paar seconden loop dan de instellingen na en in het bijzonder het apparaat type profiel.',
|
||||||
EMS_BUS_SCANNING: 'Scannen naar EMS apparaten...',
|
|
||||||
CONNECTED: 'Verbonden',
|
CONNECTED: 'Verbonden',
|
||||||
TX_ISSUES: 'Tx bus probleem. Probeer een andere Tx verzendmodus',
|
TX_ISSUES: 'Tx bus probleem. Probeer een andere Tx verzendmodus',
|
||||||
DISCONNECTED: 'Niet verbonden',
|
DISCONNECTED: 'Niet verbonden',
|
||||||
EMS_SCAN: 'Weet je zeker dat je een volledige EMS bus scan uit wilt voeren?',
|
|
||||||
DATA_TRAFFIC: 'Dataverkeer',
|
DATA_TRAFFIC: 'Dataverkeer',
|
||||||
EMS_DEVICE: 'EMS Apparaat',
|
EMS_DEVICE: 'EMS Apparaat',
|
||||||
SUCCESS: 'SUCCESS',
|
SUCCESS: 'SUCCESS',
|
||||||
FAIL: 'MISLUKT',
|
FAIL: 'MISLUKT',
|
||||||
QUALITY: 'KWALITEIT',
|
QUALITY: 'KWALITEIT',
|
||||||
SCAN: 'Scan',
|
|
||||||
STATUS_NAMES: [
|
STATUS_NAMES: [
|
||||||
'EMS Telegrammen ontvangen (Rx)',
|
'EMS Telegrammen ontvangen (Rx)',
|
||||||
'EMS Leesopdrachten (Tx)',
|
'EMS Leesopdrachten (Tx)',
|
||||||
@@ -127,7 +119,6 @@ const nl: Translation = {
|
|||||||
ENUM_FORMAT: 'Enum formaat API/MQTT',
|
ENUM_FORMAT: 'Enum formaat API/MQTT',
|
||||||
INDEX: 'Index',
|
INDEX: 'Index',
|
||||||
ENABLE_PARASITE: 'Activeer 1-wire parasitaire modus',
|
ENABLE_PARASITE: 'Activeer 1-wire parasitaire modus',
|
||||||
LOGGING: 'Logging',
|
|
||||||
LOG_HEX: 'Log EMS telegrammen in hexadecimaal',
|
LOG_HEX: 'Log EMS telegrammen in hexadecimaal',
|
||||||
ENABLE_SYSLOG: 'Activeer Syslog',
|
ENABLE_SYSLOG: 'Activeer Syslog',
|
||||||
LOG_LEVEL: 'Log Level',
|
LOG_LEVEL: 'Log Level',
|
||||||
@@ -172,7 +163,6 @@ const nl: Translation = {
|
|||||||
CLOSE: 'Sluiten',
|
CLOSE: 'Sluiten',
|
||||||
USE: 'Gebruik',
|
USE: 'Gebruik',
|
||||||
FACTORY_RESET: 'Fabrieksinstellingen',
|
FACTORY_RESET: 'Fabrieksinstellingen',
|
||||||
SYSTEM_FACTORY_TEXT: 'Gateway is gereset en start nu weer op met fabrieksinstellingen',
|
|
||||||
SYSTEM_FACTORY_TEXT_DIALOG: 'Weet je zeker dat je een reset naar fabrieksinstellingen uit wilt voeren?',
|
SYSTEM_FACTORY_TEXT_DIALOG: 'Weet je zeker dat je een reset naar fabrieksinstellingen uit wilt voeren?',
|
||||||
AVAILABLE_VERSION: 'Nieuwste beschikbare versies',
|
AVAILABLE_VERSION: 'Nieuwste beschikbare versies',
|
||||||
STABLE: 'Stable',
|
STABLE: 'Stable',
|
||||||
@@ -188,8 +178,6 @@ const nl: Translation = {
|
|||||||
DOWNLOAD_SETTINGS_TEXT: 'Maak een back-up van uw configuratie en instellingen',
|
DOWNLOAD_SETTINGS_TEXT: 'Maak een back-up van uw configuratie en instellingen',
|
||||||
DOWNLOAD_SETTINGS_TEXT2: 'Exporteer alle waarden',
|
DOWNLOAD_SETTINGS_TEXT2: 'Exporteer alle waarden',
|
||||||
DOWNLOAD_SYSTEM_BACKUP: 'Systeem Backup',
|
DOWNLOAD_SYSTEM_BACKUP: 'Systeem Backup',
|
||||||
UPLOAD_TEXT: 'Upload een nieuw firmwarebestand (.bin) of een back-upbestand (.json)',
|
|
||||||
UPLOAD_DROP_TEXT: 'Sleep en firmware .bin bestand hierheen of klik hier',
|
|
||||||
ERROR: 'Onverwachte fout, probeer opnieuw',
|
ERROR: 'Onverwachte fout, probeer opnieuw',
|
||||||
TIME_SET: 'Tijd ingesteld',
|
TIME_SET: 'Tijd ingesteld',
|
||||||
MANAGE_USERS: 'Gebruikersbeheer',
|
MANAGE_USERS: 'Gebruikersbeheer',
|
||||||
@@ -257,9 +245,7 @@ const nl: Translation = {
|
|||||||
NETWORK_SCAN: 'Scan WiFi Netwerken',
|
NETWORK_SCAN: 'Scan WiFi Netwerken',
|
||||||
IDLE: 'Inactief',
|
IDLE: 'Inactief',
|
||||||
LOST: 'Verloren',
|
LOST: 'Verloren',
|
||||||
SCANNING: 'Scannen',
|
|
||||||
SCAN_AGAIN: 'Opnieuw scannen',
|
SCAN_AGAIN: 'Opnieuw scannen',
|
||||||
NETWORK_SCANNER: 'Netwerk Scannen',
|
|
||||||
NETWORK_NO_WIFI: 'Geen WiFi netwerken gevonden',
|
NETWORK_NO_WIFI: 'Geen WiFi netwerken gevonden',
|
||||||
NETWORK_BLANK_BSSID: 'laat leeg om alleen SSID te bebruiken',
|
NETWORK_BLANK_BSSID: 'laat leeg om alleen SSID te bebruiken',
|
||||||
TX_POWER: 'Tx Vermogen',
|
TX_POWER: 'Tx Vermogen',
|
||||||
@@ -294,9 +280,6 @@ const nl: Translation = {
|
|||||||
ONCHANGE: 'Op verandering',
|
ONCHANGE: 'Op verandering',
|
||||||
CONDITION: 'Voorwaarde',
|
CONDITION: 'Voorwaarde',
|
||||||
SCHEDULE_UPDATED: 'Schema bijgewerkt',
|
SCHEDULE_UPDATED: 'Schema bijgewerkt',
|
||||||
SCHEDULE_TIMER_1: 'bij het opstarten',
|
|
||||||
SCHEDULE_TIMER_2: 'elke minuut',
|
|
||||||
SCHEDULE_TIMER_3: 'elke huur',
|
|
||||||
CUSTOM_ENTITIES: 'Eigen entiteiten',
|
CUSTOM_ENTITIES: 'Eigen entiteiten',
|
||||||
ENTITIES_HELP_1: 'Eigen entiteiten ophalen uit de EMS-bus',
|
ENTITIES_HELP_1: 'Eigen entiteiten ophalen uit de EMS-bus',
|
||||||
ENTITIES_UPDATED: 'Entiteiten bijgewerkt',
|
ENTITIES_UPDATED: 'Entiteiten bijgewerkt',
|
||||||
@@ -312,9 +295,7 @@ const nl: Translation = {
|
|||||||
ACTIVELOW: 'Actiev Laag',
|
ACTIVELOW: 'Actiev Laag',
|
||||||
UNCHANGED: 'Ongewijzigd',
|
UNCHANGED: 'Ongewijzigd',
|
||||||
ALWAYS: 'Altijd',
|
ALWAYS: 'Altijd',
|
||||||
ACTIVITY: 'Activiteit',
|
|
||||||
CONFIGURE: '{0} Configureren',
|
CONFIGURE: '{0} Configureren',
|
||||||
SYSTEM_MEMORY: 'System Geheugen',
|
|
||||||
APPLICATION_SETTINGS_1: 'Applicatie-instellingen wijzigen',
|
APPLICATION_SETTINGS_1: 'Applicatie-instellingen wijzigen',
|
||||||
SECURITY_1: 'Gebruikers toevoegen of verwijderen',
|
SECURITY_1: 'Gebruikers toevoegen of verwijderen',
|
||||||
DOWNLOAD_UPLOAD_1: 'Download en upload instellingen en firmware',
|
DOWNLOAD_UPLOAD_1: 'Download en upload instellingen en firmware',
|
||||||
@@ -326,7 +307,6 @@ const nl: Translation = {
|
|||||||
RENAME: 'Hernoemen',
|
RENAME: 'Hernoemen',
|
||||||
ENABLE_MODBUS: 'Activeer Modbus',
|
ENABLE_MODBUS: 'Activeer Modbus',
|
||||||
VIEW_LOG: 'Log weergeven om problemen te diagnosticeren',
|
VIEW_LOG: 'Log weergeven om problemen te diagnosticeren',
|
||||||
UPLOAD_DRAG: 'sleep hier een bestand en zet het neer of klik om er een te selecteren',
|
|
||||||
SERVICES: 'Services',
|
SERVICES: 'Services',
|
||||||
ALLVALUES: 'All waarden',
|
ALLVALUES: 'All waarden',
|
||||||
SPECIAL_FUNCTIONS: 'Speciale functies',
|
SPECIAL_FUNCTIONS: 'Speciale functies',
|
||||||
@@ -352,10 +332,8 @@ const nl: Translation = {
|
|||||||
THIS_VERSION: 'Deze Versie',
|
THIS_VERSION: 'Deze Versie',
|
||||||
PLATFORM: 'Platform',
|
PLATFORM: 'Platform',
|
||||||
RELEASE_TYPE: 'Release Typ',
|
RELEASE_TYPE: 'Release Typ',
|
||||||
INTERNET_CONNECTION_REQUIRED: 'Internetverbinding vereist voor automatische versiecontrole en -upgrade',
|
|
||||||
SWITCH_RELEASE_TYPE: 'Switch naar {0} release',
|
SWITCH_RELEASE_TYPE: 'Switch naar {0} release',
|
||||||
FIRMWARE_VERSION_INFO: 'Informatie over firmwareversie',
|
FIRMWARE_VERSION_INFO: 'Informatie over firmwareversie',
|
||||||
NO_DATA: 'Geen data',
|
|
||||||
USER_PROFILE: 'Gebruikersprofiel',
|
USER_PROFILE: 'Gebruikersprofiel',
|
||||||
STORED_VERSIONS: 'Opgeslagen versies',
|
STORED_VERSIONS: 'Opgeslagen versies',
|
||||||
ONLINE_HELP: 'online help',
|
ONLINE_HELP: 'online help',
|
||||||
@@ -366,9 +344,14 @@ const nl: Translation = {
|
|||||||
TEST_EMAIL_SUCCESSFUL: 'Test email verzonden succesvol',
|
TEST_EMAIL_SUCCESSFUL: 'Test email verzonden succesvol',
|
||||||
SYSTEM_NAME: 'Systeemnaam',
|
SYSTEM_NAME: 'Systeemnaam',
|
||||||
COMMANDS: 'Commando\'s',
|
COMMANDS: 'Commando\'s',
|
||||||
COMMANDS_UPDATED: 'Commando\'s bijgewerkt',
|
|
||||||
COMMANDS_HELP_1: 'Definieer hergebruikbare benoemde commando\'s hieronder. Deze kunnen worden uitgevoerd vanuit de console, API/MQTT, of worden aangeroepen door de Scheduler',
|
COMMANDS_HELP_1: 'Definieer hergebruikbare benoemde commando\'s hieronder. Deze kunnen worden uitgevoerd vanuit de console, API/MQTT, of worden aangeroepen door de Scheduler',
|
||||||
EXECUTE_COMMAND_SENT: 'Commando verzonden',
|
COMMAND_EXECUTED: 'Commando uitgevoerd succesvol',
|
||||||
|
RUN: 'Commando uitvoeren',
|
||||||
|
INTERNET_CONNECTION_CHECK: 'Controleren op online beschikbare versies',
|
||||||
|
UPLOAD_BACKUP_TEXT: 'Klik voor selecteren of sleep en drop een back-upbestand (.json) hier',
|
||||||
|
UPLOAD_FIRMWARE_TEXT: 'Klik voor selecteren of sleep en drop een firmware .bin/.md5 bestand hier',
|
||||||
|
UPLOAD_MD5_RECEIVED: 'Digest ontvangen, upload nu het firmware .bin-bestand',
|
||||||
|
UPLOAD_MD5_MATCHED: 'MD5 komt overeen, firmware wordt geïnstalleerd...'
|
||||||
};
|
};
|
||||||
|
|
||||||
export default nl;
|
export default nl;
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import type { Translation } from '../i18n-types';
|
|||||||
const no: Translation = {
|
const no: Translation = {
|
||||||
LANGUAGE: 'Språk',
|
LANGUAGE: 'Språk',
|
||||||
RETRY: 'Forsøk igjen',
|
RETRY: 'Forsøk igjen',
|
||||||
LOADING: 'Laster',
|
|
||||||
IS_REQUIRED: '{0} er nødvendig',
|
IS_REQUIRED: '{0} er nødvendig',
|
||||||
SIGN_IN: 'Logg inn',
|
SIGN_IN: 'Logg inn',
|
||||||
SIGN_OUT: 'Logg ut',
|
SIGN_OUT: 'Logg ut',
|
||||||
@@ -37,13 +36,11 @@ const no: Translation = {
|
|||||||
VALUE: '{{verdi|Verdi}}',
|
VALUE: '{{verdi|Verdi}}',
|
||||||
DEVICES: 'Enheter',
|
DEVICES: 'Enheter',
|
||||||
SENSORS: 'Sensorer',
|
SENSORS: 'Sensorer',
|
||||||
RUN_COMMAND: 'Kjør kommando',
|
|
||||||
CHANGE_VALUE: 'Endre Verdi',
|
CHANGE_VALUE: 'Endre Verdi',
|
||||||
CANCEL: 'Avbryt',
|
CANCEL: 'Avbryt',
|
||||||
REMOVE_ALL: 'Fjern alles',
|
REMOVE_ALL: 'Fjern alles',
|
||||||
APPLY_CHANGES: 'Utfør endringer({0})',
|
APPLY_CHANGES: 'Utfør endringer({0})',
|
||||||
UPDATE: 'Oppdater',
|
UPDATE: 'Oppdater',
|
||||||
EXECUTE: 'Utfør',
|
|
||||||
REMOVE: 'Fjern',
|
REMOVE: 'Fjern',
|
||||||
PROBLEM_UPDATING: 'Problem med oppdatering',
|
PROBLEM_UPDATING: 'Problem med oppdatering',
|
||||||
PROBLEM_LOADING: 'Problem med opplasting',
|
PROBLEM_LOADING: 'Problem med opplasting',
|
||||||
@@ -52,8 +49,6 @@ const no: Translation = {
|
|||||||
SETTINGS: 'Innstillinger',
|
SETTINGS: 'Innstillinger',
|
||||||
UPDATED_OF: '{0} Oppdatert',
|
UPDATED_OF: '{0} Oppdatert',
|
||||||
UPDATE_OF: '{0} Oppdater',
|
UPDATE_OF: '{0} Oppdater',
|
||||||
REMOVED_OF: '{0} Slettet',
|
|
||||||
DELETION_OF: '{0} Sletting',
|
|
||||||
OFFSET: 'Kompensering',
|
OFFSET: 'Kompensering',
|
||||||
FACTOR: 'Faktor',
|
FACTOR: 'Faktor',
|
||||||
FREQ: 'Frekvens',
|
FREQ: 'Frekvens',
|
||||||
@@ -66,17 +61,14 @@ const no: Translation = {
|
|||||||
TEMP_SENSORS: 'Temperaturesensorer',
|
TEMP_SENSORS: 'Temperaturesensorer',
|
||||||
WRITE_CMD_SENT: 'Skriv kommando sent',
|
WRITE_CMD_SENT: 'Skriv kommando sent',
|
||||||
EMS_BUS_WARNING: 'EMS bussen koblet ned. Hvis denne advarselen fortsetter etter noen f¨sekunder sjekk instillinger og prosessorkort',
|
EMS_BUS_WARNING: 'EMS bussen koblet ned. Hvis denne advarselen fortsetter etter noen f¨sekunder sjekk instillinger og prosessorkort',
|
||||||
EMS_BUS_SCANNING: 'Søker etter EMS enheter...',
|
|
||||||
CONNECTED: 'Tilkoblet',
|
CONNECTED: 'Tilkoblet',
|
||||||
TX_ISSUES: 'Tx problemer - prøv en annen Tx Modus',
|
TX_ISSUES: 'Tx problemer - prøv en annen Tx Modus',
|
||||||
DISCONNECTED: 'Frakoblet',
|
DISCONNECTED: 'Frakoblet',
|
||||||
EMS_SCAN: 'Er du sikker på du vil starte full søking av EMS bussen?',
|
|
||||||
DATA_TRAFFIC: 'Data trafikk',
|
DATA_TRAFFIC: 'Data trafikk',
|
||||||
EMS_DEVICE: 'EMS Enhet',
|
EMS_DEVICE: 'EMS Enhet',
|
||||||
SUCCESS: 'VELLYKKET',
|
SUCCESS: 'VELLYKKET',
|
||||||
FAIL: 'MISLYKKET',
|
FAIL: 'MISLYKKET',
|
||||||
QUALITY: 'KVALITET',
|
QUALITY: 'KVALITET',
|
||||||
SCAN: 'Søk',
|
|
||||||
STATUS_NAMES: [
|
STATUS_NAMES: [
|
||||||
'EMS Telegrammer Mottatt (Rx)',
|
'EMS Telegrammer Mottatt (Rx)',
|
||||||
'EMS Lest (Tx)',
|
'EMS Lest (Tx)',
|
||||||
@@ -127,7 +119,6 @@ const no: Translation = {
|
|||||||
ENUM_FORMAT: 'Enum Format API/MQTT',
|
ENUM_FORMAT: 'Enum Format API/MQTT',
|
||||||
INDEX: 'Indeks',
|
INDEX: 'Indeks',
|
||||||
ENABLE_PARASITE: 'Aktiver 1-wire parasitt strømforsyning',
|
ENABLE_PARASITE: 'Aktiver 1-wire parasitt strømforsyning',
|
||||||
LOGGING: 'Logging',
|
|
||||||
LOG_HEX: 'Logg EMS telegrammer i hexadesimal',
|
LOG_HEX: 'Logg EMS telegrammer i hexadesimal',
|
||||||
ENABLE_SYSLOG: 'Aktiver Syslog',
|
ENABLE_SYSLOG: 'Aktiver Syslog',
|
||||||
LOG_LEVEL: 'Log Level',
|
LOG_LEVEL: 'Log Level',
|
||||||
@@ -172,7 +163,6 @@ const no: Translation = {
|
|||||||
CLOSE: 'Steng',
|
CLOSE: 'Steng',
|
||||||
USE: 'Bruk',
|
USE: 'Bruk',
|
||||||
FACTORY_RESET: 'Sett tilbake til fabrikkinstilling',
|
FACTORY_RESET: 'Sett tilbake til fabrikkinstilling',
|
||||||
SYSTEM_FACTORY_TEXT: 'Enhet har blitt satt tilbake til fabrikkinstilling og vil restarte',
|
|
||||||
SYSTEM_FACTORY_TEXT_DIALOG: 'Er du sikker på at du vil resette enheten til fabrikkinstillinger?',
|
SYSTEM_FACTORY_TEXT_DIALOG: 'Er du sikker på at du vil resette enheten til fabrikkinstillinger?',
|
||||||
AVAILABLE_VERSION: 'Tilgjengelige versjoner',
|
AVAILABLE_VERSION: 'Tilgjengelige versjoner',
|
||||||
STABLE: 'Stabil',
|
STABLE: 'Stabil',
|
||||||
@@ -188,8 +178,6 @@ const no: Translation = {
|
|||||||
DOWNLOAD_SETTINGS_TEXT: 'Lag en sikkerhetskopi av dine konfigurasjon og innstillinger',
|
DOWNLOAD_SETTINGS_TEXT: 'Lag en sikkerhetskopi av dine konfigurasjon og innstillinger',
|
||||||
DOWNLOAD_SETTINGS_TEXT2: 'Eksporter alle verdier',
|
DOWNLOAD_SETTINGS_TEXT2: 'Eksporter alle verdier',
|
||||||
DOWNLOAD_SYSTEM_BACKUP: 'System Sikkerhetskopi',
|
DOWNLOAD_SYSTEM_BACKUP: 'System Sikkerhetskopi',
|
||||||
UPLOAD_TEXT: 'Last opp en ny firmware fil (.bin) eller en sikkerhetskopi fil (.json)',
|
|
||||||
UPLOAD_DROP_TEXT: 'Dropp en firmware fil (.bin) eller klikk her',
|
|
||||||
ERROR: 'Ukjent feil, prøv igjen',
|
ERROR: 'Ukjent feil, prøv igjen',
|
||||||
TIME_SET: 'Still in tid',
|
TIME_SET: 'Still in tid',
|
||||||
MANAGE_USERS: 'Administrer Brukere',
|
MANAGE_USERS: 'Administrer Brukere',
|
||||||
@@ -257,9 +245,7 @@ const no: Translation = {
|
|||||||
NETWORK_SCAN: 'Søk etter trådløst nettverk',
|
NETWORK_SCAN: 'Søk etter trådløst nettverk',
|
||||||
IDLE: 'Klar',
|
IDLE: 'Klar',
|
||||||
LOST: 'Mistet',
|
LOST: 'Mistet',
|
||||||
SCANNING: 'Søker',
|
|
||||||
SCAN_AGAIN: 'Søk igjen',
|
SCAN_AGAIN: 'Søk igjen',
|
||||||
NETWORK_SCANNER: 'Nettverk Scanner',
|
|
||||||
NETWORK_NO_WIFI: 'Ingen trådløse nett funnet',
|
NETWORK_NO_WIFI: 'Ingen trådløse nett funnet',
|
||||||
NETWORK_BLANK_BSSID: 'la feltet være blankt for å bruke kun SSID',
|
NETWORK_BLANK_BSSID: 'la feltet være blankt for å bruke kun SSID',
|
||||||
TX_POWER: 'Tx Effekt',
|
TX_POWER: 'Tx Effekt',
|
||||||
@@ -294,9 +280,6 @@ const no: Translation = {
|
|||||||
ONCHANGE: 'På endring',
|
ONCHANGE: 'På endring',
|
||||||
CONDITION: 'Betingelse',
|
CONDITION: 'Betingelse',
|
||||||
SCHEDULE_UPDATED: 'Planlegger er oppdatert',
|
SCHEDULE_UPDATED: 'Planlegger er oppdatert',
|
||||||
SCHEDULE_TIMER_1: 'ved oppstart',
|
|
||||||
SCHEDULE_TIMER_2: 'hvert minutt',
|
|
||||||
SCHEDULE_TIMER_3: 'hver time',
|
|
||||||
CUSTOM_ENTITIES: 'Personlige entiteter',
|
CUSTOM_ENTITIES: 'Personlige entiteter',
|
||||||
ENTITIES_HELP_1: 'Hent personlige entiteter fra EMS bussen',
|
ENTITIES_HELP_1: 'Hent personlige entiteter fra EMS bussen',
|
||||||
ENTITIES_UPDATED: 'Entiteter oppdatert',
|
ENTITIES_UPDATED: 'Entiteter oppdatert',
|
||||||
@@ -312,9 +295,7 @@ const no: Translation = {
|
|||||||
ACTIVELOW: 'Active Low',
|
ACTIVELOW: 'Active Low',
|
||||||
UNCHANGED: 'Unchanged',
|
UNCHANGED: 'Unchanged',
|
||||||
ALWAYS: 'Always',
|
ALWAYS: 'Always',
|
||||||
ACTIVITY: 'Aktivitet',
|
|
||||||
CONFIGURE: 'Konfigurer {0}',
|
CONFIGURE: 'Konfigurer {0}',
|
||||||
SYSTEM_MEMORY: 'System Memory',
|
|
||||||
APPLICATION_SETTINGS_1: 'Modify EMS-ESP Application Settings',
|
APPLICATION_SETTINGS_1: 'Modify EMS-ESP Application Settings',
|
||||||
SECURITY_1: 'Add or remove users',
|
SECURITY_1: 'Add or remove users',
|
||||||
DOWNLOAD_UPLOAD_1: 'Download and Upload Settings and Firmware',
|
DOWNLOAD_UPLOAD_1: 'Download and Upload Settings and Firmware',
|
||||||
@@ -326,7 +307,6 @@ const no: Translation = {
|
|||||||
RENAME: 'Gi nytt navn',
|
RENAME: 'Gi nytt navn',
|
||||||
ENABLE_MODBUS: 'Aktiver Modbus',
|
ENABLE_MODBUS: 'Aktiver Modbus',
|
||||||
VIEW_LOG: 'Se logg for å diagnostisere problemer',
|
VIEW_LOG: 'Se logg for å diagnostisere problemer',
|
||||||
UPLOAD_DRAG: 'dra og slippe en fil her eller klikk for å velge en',
|
|
||||||
SERVICES: 'Tjenester',
|
SERVICES: 'Tjenester',
|
||||||
ALLVALUES: 'Alle verdier',
|
ALLVALUES: 'Alle verdier',
|
||||||
SPECIAL_FUNCTIONS: 'Spesielle funksjoner',
|
SPECIAL_FUNCTIONS: 'Spesielle funksjoner',
|
||||||
@@ -352,10 +332,8 @@ const no: Translation = {
|
|||||||
THIS_VERSION: 'Denne versjonen',
|
THIS_VERSION: 'Denne versjonen',
|
||||||
PLATFORM: 'Plattform',
|
PLATFORM: 'Plattform',
|
||||||
RELEASE_TYPE: 'Utgivelses type',
|
RELEASE_TYPE: 'Utgivelses type',
|
||||||
INTERNET_CONNECTION_REQUIRED: 'Internettilkobling kreves for automatisk versjonskontroll og oppgradering',
|
|
||||||
SWITCH_RELEASE_TYPE: 'Bytt til {0} utgivelse',
|
SWITCH_RELEASE_TYPE: 'Bytt til {0} utgivelse',
|
||||||
FIRMWARE_VERSION_INFO: 'Informasjon om firmwareversjon',
|
FIRMWARE_VERSION_INFO: 'Informasjon om firmwareversjon',
|
||||||
NO_DATA: 'Ingen data',
|
|
||||||
USER_PROFILE: 'Brukerprofil',
|
USER_PROFILE: 'Brukerprofil',
|
||||||
STORED_VERSIONS: 'Lagret versjoner',
|
STORED_VERSIONS: 'Lagret versjoner',
|
||||||
ONLINE_HELP: 'online hjelp',
|
ONLINE_HELP: 'online hjelp',
|
||||||
@@ -366,9 +344,14 @@ const no: Translation = {
|
|||||||
TEST_EMAIL_SUCCESSFUL: 'Test email sendt suksessfullt',
|
TEST_EMAIL_SUCCESSFUL: 'Test email sendt suksessfullt',
|
||||||
SYSTEM_NAME: 'Systemnavn',
|
SYSTEM_NAME: 'Systemnavn',
|
||||||
COMMANDS: 'Kommandoer',
|
COMMANDS: 'Kommandoer',
|
||||||
COMMANDS_UPDATED: 'Kommandoer oppdatert',
|
|
||||||
COMMANDS_HELP_1: 'Definer egne kommandoer for EMS-Magistral',
|
COMMANDS_HELP_1: 'Definer egne kommandoer for EMS-Magistral',
|
||||||
EXECUTE_COMMAND_SENT: 'Kommando sendt',
|
COMMAND_EXECUTED: 'Kommando kjørt lykkes',
|
||||||
|
RUN: 'Kjør',
|
||||||
|
INTERNET_CONNECTION_CHECK: 'Kontrollerer online for tilgjengelige versjoner',
|
||||||
|
UPLOAD_BACKUP_TEXT: 'Klikk for å velge eller dra og slipp en sikkerhetskopi (.json) her',
|
||||||
|
UPLOAD_FIRMWARE_TEXT: 'Klikk for å velge eller dra og slipp en firmware .bin/.md5 her',
|
||||||
|
UPLOAD_MD5_RECEIVED: 'MD5 mottatt, last nå opp firmware .bin-filen',
|
||||||
|
UPLOAD_MD5_MATCHED: 'MD5 stemmer, firmware installeres...'
|
||||||
};
|
};
|
||||||
|
|
||||||
export default no;
|
export default no;
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import type { BaseTranslation } from '../i18n-types';
|
|||||||
const pl: BaseTranslation = {
|
const pl: BaseTranslation = {
|
||||||
LANGUAGE: 'Język',
|
LANGUAGE: 'Język',
|
||||||
RETRY: 'Ponów',
|
RETRY: 'Ponów',
|
||||||
LOADING: 'Ładowanie',
|
|
||||||
IS_REQUIRED: 'Pole {0} nie może być puste!',
|
IS_REQUIRED: 'Pole {0} nie może być puste!',
|
||||||
SIGN_IN: 'Zaloguj się',
|
SIGN_IN: 'Zaloguj się',
|
||||||
SIGN_OUT: 'Wyloguj się',
|
SIGN_OUT: 'Wyloguj się',
|
||||||
@@ -37,13 +36,11 @@ const pl: BaseTranslation = {
|
|||||||
VALUE: '{{W|w|w}}artość',
|
VALUE: '{{W|w|w}}artość',
|
||||||
DEVICES: 'Urządzenia',
|
DEVICES: 'Urządzenia',
|
||||||
SENSORS: 'Czujniki',
|
SENSORS: 'Czujniki',
|
||||||
RUN_COMMAND: 'Wykonaj komendę',
|
|
||||||
CHANGE_VALUE: 'Zmiana wartości',
|
CHANGE_VALUE: 'Zmiana wartości',
|
||||||
CANCEL: 'Anuluj',
|
CANCEL: 'Anuluj',
|
||||||
REMOVE_ALL: 'Usuń wszystko',
|
REMOVE_ALL: 'Usuń wszystko',
|
||||||
APPLY_CHANGES: 'Zapisz zmiany ({0})',
|
APPLY_CHANGES: 'Zapisz zmiany ({0})',
|
||||||
UPDATE: 'Zmień',
|
UPDATE: 'Zmień',
|
||||||
EXECUTE: 'Wykonaj',
|
|
||||||
REMOVE: 'Usuń',
|
REMOVE: 'Usuń',
|
||||||
PROBLEM_UPDATING: 'Problem z uaktualnieniem!',
|
PROBLEM_UPDATING: 'Problem z uaktualnieniem!',
|
||||||
PROBLEM_LOADING: 'Problem z załadowaniem!',
|
PROBLEM_LOADING: 'Problem z załadowaniem!',
|
||||||
@@ -52,8 +49,6 @@ const pl: BaseTranslation = {
|
|||||||
SETTINGS: '{{U|u|}}stawienia',
|
SETTINGS: '{{U|u|}}stawienia',
|
||||||
UPDATED_OF: 'Zaktualizowano {0}.',
|
UPDATED_OF: 'Zaktualizowano {0}.',
|
||||||
UPDATE_OF: 'Aktualizacja {0}',
|
UPDATE_OF: 'Aktualizacja {0}',
|
||||||
REMOVED_OF: 'Usunięto ustawienia {0}.',
|
|
||||||
DELETION_OF: 'Kasowanie {0}',
|
|
||||||
OFFSET: 'Korekta ±',
|
OFFSET: 'Korekta ±',
|
||||||
FACTOR: 'Mnożnik',
|
FACTOR: 'Mnożnik',
|
||||||
FREQ: 'Częstotliwość',
|
FREQ: 'Częstotliwość',
|
||||||
@@ -66,17 +61,14 @@ const pl: BaseTranslation = {
|
|||||||
TEMP_SENSORS: 'Czujniki temperatury 1-Wire®',
|
TEMP_SENSORS: 'Czujniki temperatury 1-Wire®',
|
||||||
WRITE_CMD_SENT: 'Komenda zapisu została wysłana.',
|
WRITE_CMD_SENT: 'Komenda zapisu została wysłana.',
|
||||||
EMS_BUS_WARNING: 'Brak połączenia z magistralą EMS. Jeśli ten błąd występuje dłużej niż kilka sekund, sprawdź ustawienia oraz profil płytki interfejsu.',
|
EMS_BUS_WARNING: 'Brak połączenia z magistralą EMS. Jeśli ten błąd występuje dłużej niż kilka sekund, sprawdź ustawienia oraz profil płytki interfejsu.',
|
||||||
EMS_BUS_SCANNING: 'Trwa skanowanie urządzeń na magistrali EMS...',
|
|
||||||
CONNECTED: '{{połączono|połączenie|}}',
|
CONNECTED: '{{połączono|połączenie|}}',
|
||||||
TX_ISSUES: 'problem z zapisem na magistralę EMS, spróbuj wybrać inny "Tryb transmisji (Tx)"',
|
TX_ISSUES: 'problem z zapisem na magistralę EMS, spróbuj wybrać inny "Tryb transmisji (Tx)"',
|
||||||
DISCONNECTED: 'brak połączenia',
|
DISCONNECTED: 'brak połączenia',
|
||||||
EMS_SCAN: 'Czy na pewno wykonać pełne skanowanie magistrali EMS?',
|
|
||||||
DATA_TRAFFIC: 'Ruch Danych',
|
DATA_TRAFFIC: 'Ruch Danych',
|
||||||
EMS_DEVICE: 'Urządzenie EMS',
|
EMS_DEVICE: 'Urządzenie EMS',
|
||||||
SUCCESS: 'Udane',
|
SUCCESS: 'Udane',
|
||||||
FAIL: 'Nieudane',
|
FAIL: 'Nieudane',
|
||||||
QUALITY: 'Jakość',
|
QUALITY: 'Jakość',
|
||||||
SCAN: 'Skanuj',
|
|
||||||
STATUS_NAMES: [
|
STATUS_NAMES: [
|
||||||
'EMS, telegramy odebrane (Rx)',
|
'EMS, telegramy odebrane (Rx)',
|
||||||
'EMS, wysłane telegramy "odczyt" (Tx)',
|
'EMS, wysłane telegramy "odczyt" (Tx)',
|
||||||
@@ -127,7 +119,6 @@ const pl: BaseTranslation = {
|
|||||||
ENUM_FORMAT: 'Wartości z listy w API/MQTT',
|
ENUM_FORMAT: 'Wartości z listy w API/MQTT',
|
||||||
INDEX: 'indeks',
|
INDEX: 'indeks',
|
||||||
ENABLE_PARASITE: 'Aktywuj zasilanie 1-wire pasożytnicze',
|
ENABLE_PARASITE: 'Aktywuj zasilanie 1-wire pasożytnicze',
|
||||||
LOGGING: 'Logowanie',
|
|
||||||
LOG_HEX: 'Loguj telegramy EMS w systemie szesnastkowym (hex)',
|
LOG_HEX: 'Loguj telegramy EMS w systemie szesnastkowym (hex)',
|
||||||
ENABLE_SYSLOG: 'Aktywuj SysLog',
|
ENABLE_SYSLOG: 'Aktywuj SysLog',
|
||||||
LOG_LEVEL: 'Poziom logowania',
|
LOG_LEVEL: 'Poziom logowania',
|
||||||
@@ -172,7 +163,6 @@ const pl: BaseTranslation = {
|
|||||||
CLOSE: 'Zamknij',
|
CLOSE: 'Zamknij',
|
||||||
USE: 'Aby zaktualizować firmware skorzystaj z funkcji',
|
USE: 'Aby zaktualizować firmware skorzystaj z funkcji',
|
||||||
FACTORY_RESET: 'Ustawienia fabryczne',
|
FACTORY_RESET: 'Ustawienia fabryczne',
|
||||||
SYSTEM_FACTORY_TEXT: 'Interfejs EMS-ESP został przywrócony do ustawień fabrycznych i zostanie teraz ponownie uruchomiony.',
|
|
||||||
SYSTEM_FACTORY_TEXT_DIALOG: 'Na pewno chcesz przywrócić ustawienia fabryczne interfejsu EMS-ESP?',
|
SYSTEM_FACTORY_TEXT_DIALOG: 'Na pewno chcesz przywrócić ustawienia fabryczne interfejsu EMS-ESP?',
|
||||||
AVAILABLE_VERSION: 'Najnowsze dostępne wersje',
|
AVAILABLE_VERSION: 'Najnowsze dostępne wersje',
|
||||||
STABLE: 'Stabilna',
|
STABLE: 'Stabilna',
|
||||||
@@ -188,8 +178,6 @@ const pl: BaseTranslation = {
|
|||||||
DOWNLOAD_SETTINGS_TEXT: 'Utwórz kopię swoich ustawień i konfiguracji',
|
DOWNLOAD_SETTINGS_TEXT: 'Utwórz kopię swoich ustawień i konfiguracji',
|
||||||
DOWNLOAD_SETTINGS_TEXT2: 'Eksportuj wszystkie wartości',
|
DOWNLOAD_SETTINGS_TEXT2: 'Eksportuj wszystkie wartości',
|
||||||
DOWNLOAD_SYSTEM_BACKUP: 'Kopia zapasowa systemu',
|
DOWNLOAD_SYSTEM_BACKUP: 'Kopia zapasowa systemu',
|
||||||
UPLOAD_TEXT: 'Wgraj nowy plik firmware (.bin) lub kopię ustawień (.json)',
|
|
||||||
UPLOAD_DROP_TEXT: 'Upuść plik firmware .bin lub kliknij tutaj',
|
|
||||||
ERROR: 'Nieoczekiwany błąd, spróbuj ponownie!',
|
ERROR: 'Nieoczekiwany błąd, spróbuj ponownie!',
|
||||||
TIME_SET: 'Zegar został ustawiony.',
|
TIME_SET: 'Zegar został ustawiony.',
|
||||||
MANAGE_USERS: 'Zarządzanie użytkownikami',
|
MANAGE_USERS: 'Zarządzanie użytkownikami',
|
||||||
@@ -257,9 +245,7 @@ const pl: BaseTranslation = {
|
|||||||
NETWORK_SCAN: 'Skanowanie sieci WiFi',
|
NETWORK_SCAN: 'Skanowanie sieci WiFi',
|
||||||
IDLE: 'bezczynna',
|
IDLE: 'bezczynna',
|
||||||
LOST: 'zostało utracone.',
|
LOST: 'zostało utracone.',
|
||||||
SCANNING: 'Skanuję',
|
|
||||||
SCAN_AGAIN: 'Skanuj ponownie',
|
SCAN_AGAIN: 'Skanuj ponownie',
|
||||||
NETWORK_SCANNER: 'Skaner sieci WiFi',
|
|
||||||
NETWORK_NO_WIFI: 'Brak sieci WiFi w zasięgu',
|
NETWORK_NO_WIFI: 'Brak sieci WiFi w zasięgu',
|
||||||
NETWORK_BLANK_BSSID: 'pozostaw puste aby używać tylko SSID',
|
NETWORK_BLANK_BSSID: 'pozostaw puste aby używać tylko SSID',
|
||||||
TX_POWER: 'Moc nadawania',
|
TX_POWER: 'Moc nadawania',
|
||||||
@@ -294,9 +280,6 @@ const pl: BaseTranslation = {
|
|||||||
ONCHANGE: 'O zmianie',
|
ONCHANGE: 'O zmianie',
|
||||||
CONDITION: 'Stan',
|
CONDITION: 'Stan',
|
||||||
SCHEDULE_UPDATED: 'Harmonogram został uaktualniony.',
|
SCHEDULE_UPDATED: 'Harmonogram został uaktualniony.',
|
||||||
SCHEDULE_TIMER_1: 'przy starcie',
|
|
||||||
SCHEDULE_TIMER_2: 'co minutę',
|
|
||||||
SCHEDULE_TIMER_3: 'co godzinę',
|
|
||||||
CUSTOM_ENTITIES: '{{N|n|}}iestandardowe{{|j|}} encj{{e|i|}}',
|
CUSTOM_ENTITIES: '{{N|n|}}iestandardowe{{|j|}} encj{{e|i|}}',
|
||||||
ENTITIES_HELP_1: 'Zdefiniuj niestandardowe encje dla magistrali EMS',
|
ENTITIES_HELP_1: 'Zdefiniuj niestandardowe encje dla magistrali EMS',
|
||||||
ENTITIES_UPDATED: 'Niestandardowe encje zostały uaktualnione.',
|
ENTITIES_UPDATED: 'Niestandardowe encje zostały uaktualnione.',
|
||||||
@@ -312,9 +295,7 @@ const pl: BaseTranslation = {
|
|||||||
ACTIVELOW: 'Wyzwalany stanem niskim',
|
ACTIVELOW: 'Wyzwalany stanem niskim',
|
||||||
UNCHANGED: 'Zachowaj stan',
|
UNCHANGED: 'Zachowaj stan',
|
||||||
ALWAYS: 'Zawsze',
|
ALWAYS: 'Zawsze',
|
||||||
ACTIVITY: 'Aktywność',
|
|
||||||
CONFIGURE: 'Konfiguracja {0}',
|
CONFIGURE: 'Konfiguracja {0}',
|
||||||
SYSTEM_MEMORY: 'Pamięć systemowa',
|
|
||||||
APPLICATION_SETTINGS_1: 'Modyfikacja ustawień aplikacji EMS-ESP',
|
APPLICATION_SETTINGS_1: 'Modyfikacja ustawień aplikacji EMS-ESP',
|
||||||
SECURITY_1: 'Dodawanie i usuwanie użytkowników',
|
SECURITY_1: 'Dodawanie i usuwanie użytkowników',
|
||||||
DOWNLOAD_UPLOAD_1: 'Pobieranie/wysyłanie ustawień i firmware',
|
DOWNLOAD_UPLOAD_1: 'Pobieranie/wysyłanie ustawień i firmware',
|
||||||
@@ -326,7 +307,6 @@ const pl: BaseTranslation = {
|
|||||||
RENAME: 'Zmień nazwę',
|
RENAME: 'Zmień nazwę',
|
||||||
ENABLE_MODBUS: 'Aktywuj Modbus',
|
ENABLE_MODBUS: 'Aktywuj Modbus',
|
||||||
VIEW_LOG: 'Zdiagnozuj problemy',
|
VIEW_LOG: 'Zdiagnozuj problemy',
|
||||||
UPLOAD_DRAG: 'przeciągnij i upuść plik lub kliknij tutaj',
|
|
||||||
SERVICES: 'Usługi',
|
SERVICES: 'Usługi',
|
||||||
ALLVALUES: 'Wszystkie wartości',
|
ALLVALUES: 'Wszystkie wartości',
|
||||||
SPECIAL_FUNCTIONS: 'Specjalne funkcje',
|
SPECIAL_FUNCTIONS: 'Specjalne funkcje',
|
||||||
@@ -352,10 +332,8 @@ const pl: BaseTranslation = {
|
|||||||
THIS_VERSION: 'Ta wersja',
|
THIS_VERSION: 'Ta wersja',
|
||||||
PLATFORM: 'Platforma',
|
PLATFORM: 'Platforma',
|
||||||
RELEASE_TYPE: 'Typ wydania',
|
RELEASE_TYPE: 'Typ wydania',
|
||||||
INTERNET_CONNECTION_REQUIRED: 'Połączenie internetowe jest wymagane do automatycznej kontroli wersji i aktualizacji',
|
|
||||||
SWITCH_RELEASE_TYPE: 'Zmień na {0} wydanie',
|
SWITCH_RELEASE_TYPE: 'Zmień na {0} wydanie',
|
||||||
FIRMWARE_VERSION_INFO: 'Informacje o wersji firmware',
|
FIRMWARE_VERSION_INFO: 'Informacje o wersji firmware',
|
||||||
NO_DATA: 'Brak danych',
|
|
||||||
USER_PROFILE: 'Profil użytkownika',
|
USER_PROFILE: 'Profil użytkownika',
|
||||||
STORED_VERSIONS: 'Zapisane wersje',
|
STORED_VERSIONS: 'Zapisane wersje',
|
||||||
ONLINE_HELP: 'pomoc online',
|
ONLINE_HELP: 'pomoc online',
|
||||||
@@ -366,9 +344,14 @@ const pl: BaseTranslation = {
|
|||||||
TEST_EMAIL_SUCCESSFUL: 'Test email wysłany pomyślnie',
|
TEST_EMAIL_SUCCESSFUL: 'Test email wysłany pomyślnie',
|
||||||
SYSTEM_NAME: 'Nazwa systemu',
|
SYSTEM_NAME: 'Nazwa systemu',
|
||||||
COMMANDS: 'Komendy',
|
COMMANDS: 'Komendy',
|
||||||
COMMANDS_UPDATED: 'Komendy zostały zaktualizowane',
|
|
||||||
COMMANDS_HELP_1: 'Zdefiniuj niestandardowe komendy dla magistrali EMS',
|
COMMANDS_HELP_1: 'Zdefiniuj niestandardowe komendy dla magistrali EMS',
|
||||||
EXECUTE_COMMAND_SENT: 'Komenda wysłana',
|
COMMAND_EXECUTED: 'Komenda wykonana pomyślnie',
|
||||||
|
RUN: 'Wykonaj',
|
||||||
|
INTERNET_CONNECTION_CHECK: 'Sprawdzanie online dla dostępnych wersji',
|
||||||
|
UPLOAD_BACKUP_TEXT: 'Kliknij, aby wybrać lub przeciągnij i upuść plik kopii zapasowej (.json) tutaj',
|
||||||
|
UPLOAD_FIRMWARE_TEXT: 'Kliknij, aby wybrać lub przeciągnij i upuść plik firmware .bin/.md5',
|
||||||
|
UPLOAD_MD5_RECEIVED: 'Suma MD5 odebrana, teraz wgraj plik firmware .bin',
|
||||||
|
UPLOAD_MD5_MATCHED: 'MD5 zgodne, instalowanie firmware...'
|
||||||
};
|
};
|
||||||
|
|
||||||
export default pl;
|
export default pl;
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import type { Translation } from '../i18n-types';
|
|||||||
const sk: Translation = {
|
const sk: Translation = {
|
||||||
LANGUAGE: 'Jazyk',
|
LANGUAGE: 'Jazyk',
|
||||||
RETRY: 'Opakovať',
|
RETRY: 'Opakovať',
|
||||||
LOADING: 'Načítanie',
|
|
||||||
IS_REQUIRED: '{0} je požadovaná',
|
IS_REQUIRED: '{0} je požadovaná',
|
||||||
SIGN_IN: 'Prihlásiť sa',
|
SIGN_IN: 'Prihlásiť sa',
|
||||||
SIGN_OUT: 'Odhlásiť sa',
|
SIGN_OUT: 'Odhlásiť sa',
|
||||||
@@ -37,13 +36,11 @@ const sk: Translation = {
|
|||||||
VALUE: '{{hodnota|Hodnota}}',
|
VALUE: '{{hodnota|Hodnota}}',
|
||||||
DEVICES: 'Zariadenia',
|
DEVICES: 'Zariadenia',
|
||||||
SENSORS: 'Snímače',
|
SENSORS: 'Snímače',
|
||||||
RUN_COMMAND: 'Volať príkaz',
|
|
||||||
CHANGE_VALUE: 'Zmena hodnoty',
|
CHANGE_VALUE: 'Zmena hodnoty',
|
||||||
CANCEL: 'Zrušiť',
|
CANCEL: 'Zrušiť',
|
||||||
REMOVE_ALL: 'Odstrániť všetko',
|
REMOVE_ALL: 'Odstrániť všetko',
|
||||||
APPLY_CHANGES: 'Aplikovať zmeny ({0})',
|
APPLY_CHANGES: 'Aplikovať zmeny ({0})',
|
||||||
UPDATE: 'Aktualizovať',
|
UPDATE: 'Aktualizovať',
|
||||||
EXECUTE: 'Spustiť',
|
|
||||||
REMOVE: 'Odstrániť',
|
REMOVE: 'Odstrániť',
|
||||||
PROBLEM_UPDATING: 'Problém s aktualizáciou',
|
PROBLEM_UPDATING: 'Problém s aktualizáciou',
|
||||||
PROBLEM_LOADING: 'Problém s načítaním',
|
PROBLEM_LOADING: 'Problém s načítaním',
|
||||||
@@ -52,8 +49,6 @@ const sk: Translation = {
|
|||||||
SETTINGS: 'Nastavenia',
|
SETTINGS: 'Nastavenia',
|
||||||
UPDATED_OF: '{0} aktualizovaných',
|
UPDATED_OF: '{0} aktualizovaných',
|
||||||
UPDATE_OF: '{0} aktualizované',
|
UPDATE_OF: '{0} aktualizované',
|
||||||
REMOVED_OF: '{0} odstránených',
|
|
||||||
DELETION_OF: '{0} zmazaných',
|
|
||||||
OFFSET: 'Ofset',
|
OFFSET: 'Ofset',
|
||||||
FACTOR: 'Faktor',
|
FACTOR: 'Faktor',
|
||||||
FREQ: 'Frekvencia',
|
FREQ: 'Frekvencia',
|
||||||
@@ -66,17 +61,14 @@ const sk: Translation = {
|
|||||||
TEMP_SENSORS: 'Snímače teploty',
|
TEMP_SENSORS: 'Snímače teploty',
|
||||||
WRITE_CMD_SENT: 'Príkaz zápisu bol odoslaný',
|
WRITE_CMD_SENT: 'Príkaz zápisu bol odoslaný',
|
||||||
EMS_BUS_WARNING: 'Zbernica EMS odpojená. Ak toto upozornenie pretrváva aj po niekoľkých sekundách, skontrolujte nastavenia a profil dosky',
|
EMS_BUS_WARNING: 'Zbernica EMS odpojená. Ak toto upozornenie pretrváva aj po niekoľkých sekundách, skontrolujte nastavenia a profil dosky',
|
||||||
EMS_BUS_SCANNING: 'Zisťovanie EMS zariadení...',
|
|
||||||
CONNECTED: 'Pripojené',
|
CONNECTED: 'Pripojené',
|
||||||
TX_ISSUES: 'Problémy s Tx – skontrolujte Tx režim',
|
TX_ISSUES: 'Problémy s Tx – skontrolujte Tx režim',
|
||||||
DISCONNECTED: 'Odpojené',
|
DISCONNECTED: 'Odpojené',
|
||||||
EMS_SCAN: 'Naozaj chcete spustiť úplnú kontrolu zariadenia zbernice EMS?',
|
|
||||||
DATA_TRAFFIC: 'Dátová prevádzka',
|
DATA_TRAFFIC: 'Dátová prevádzka',
|
||||||
EMS_DEVICE: 'EMS zariadenie',
|
EMS_DEVICE: 'EMS zariadenie',
|
||||||
SUCCESS: 'ÚSPEŠNÉ',
|
SUCCESS: 'ÚSPEŠNÉ',
|
||||||
FAIL: 'ZLYHANIE',
|
FAIL: 'ZLYHANIE',
|
||||||
QUALITY: 'KVALITA',
|
QUALITY: 'KVALITA',
|
||||||
SCAN: 'Scan',
|
|
||||||
STATUS_NAMES: [
|
STATUS_NAMES: [
|
||||||
'EMS Telegramy prijaté (Rx)',
|
'EMS Telegramy prijaté (Rx)',
|
||||||
'EMS Čítania (Tx)',
|
'EMS Čítania (Tx)',
|
||||||
@@ -127,7 +119,6 @@ const sk: Translation = {
|
|||||||
ENUM_FORMAT: 'Enum formát API/MQTT',
|
ENUM_FORMAT: 'Enum formát API/MQTT',
|
||||||
INDEX: 'Index',
|
INDEX: 'Index',
|
||||||
ENABLE_PARASITE: 'Povoliť 1-wire parazitné napájanie DS18B20',
|
ENABLE_PARASITE: 'Povoliť 1-wire parazitné napájanie DS18B20',
|
||||||
LOGGING: 'Logovanie',
|
|
||||||
LOG_HEX: 'Záznam telegramov EMS v hexadecimálnej sústave',
|
LOG_HEX: 'Záznam telegramov EMS v hexadecimálnej sústave',
|
||||||
ENABLE_SYSLOG: 'Povoliť Syslog',
|
ENABLE_SYSLOG: 'Povoliť Syslog',
|
||||||
LOG_LEVEL: 'Log úroveň',
|
LOG_LEVEL: 'Log úroveň',
|
||||||
@@ -172,7 +163,6 @@ const sk: Translation = {
|
|||||||
CLOSE: 'Zatvoriť',
|
CLOSE: 'Zatvoriť',
|
||||||
USE: 'Použiť',
|
USE: 'Použiť',
|
||||||
FACTORY_RESET: 'Továrenské nastavenia',
|
FACTORY_RESET: 'Továrenské nastavenia',
|
||||||
SYSTEM_FACTORY_TEXT: 'Zariadenie bolo obnovené z výroby a teraz sa reštartuje',
|
|
||||||
SYSTEM_FACTORY_TEXT_DIALOG: 'Naozaj chcete resetovať EMS-ESP na predvolené výrobné nastavenia?',
|
SYSTEM_FACTORY_TEXT_DIALOG: 'Naozaj chcete resetovať EMS-ESP na predvolené výrobné nastavenia?',
|
||||||
AVAILABLE_VERSION: 'Najnovšie dostupné verzie',
|
AVAILABLE_VERSION: 'Najnovšie dostupné verzie',
|
||||||
STABLE: 'Stabilná',
|
STABLE: 'Stabilná',
|
||||||
@@ -188,8 +178,6 @@ const sk: Translation = {
|
|||||||
DOWNLOAD_SETTINGS_TEXT: 'Vytvorte zálohu svojej konfigurácie a nastavení',
|
DOWNLOAD_SETTINGS_TEXT: 'Vytvorte zálohu svojej konfigurácie a nastavení',
|
||||||
DOWNLOAD_SETTINGS_TEXT2: 'Exportovať všetky hodnoty',
|
DOWNLOAD_SETTINGS_TEXT2: 'Exportovať všetky hodnoty',
|
||||||
DOWNLOAD_SYSTEM_BACKUP: 'Systémová záloha',
|
DOWNLOAD_SYSTEM_BACKUP: 'Systémová záloha',
|
||||||
UPLOAD_TEXT: 'Nahrajte nový súbor firmvéru (.bin) alebo súbor zálohy (.json)',
|
|
||||||
UPLOAD_DROP_TEXT: 'Presuňte súbor .bin firmvéru alebo kliknite sem',
|
|
||||||
ERROR: 'Neočakávaná chyba, prosím skúste to znova',
|
ERROR: 'Neočakávaná chyba, prosím skúste to znova',
|
||||||
TIME_SET: 'Nastavený čas',
|
TIME_SET: 'Nastavený čas',
|
||||||
MANAGE_USERS: 'Správa používateľov',
|
MANAGE_USERS: 'Správa používateľov',
|
||||||
@@ -257,9 +245,7 @@ const sk: Translation = {
|
|||||||
NETWORK_SCAN: 'Scan WiFi siete',
|
NETWORK_SCAN: 'Scan WiFi siete',
|
||||||
IDLE: 'Nečinné',
|
IDLE: 'Nečinné',
|
||||||
LOST: 'Stratené',
|
LOST: 'Stratené',
|
||||||
SCANNING: 'Skenovanie',
|
|
||||||
SCAN_AGAIN: 'Skenovať znova',
|
SCAN_AGAIN: 'Skenovať znova',
|
||||||
NETWORK_SCANNER: 'Sieťový skener',
|
|
||||||
NETWORK_NO_WIFI: 'WiFi siete nenájdené',
|
NETWORK_NO_WIFI: 'WiFi siete nenájdené',
|
||||||
NETWORK_BLANK_BSSID: 'ponechajte prázdne, ak chcete používať iba SSID',
|
NETWORK_BLANK_BSSID: 'ponechajte prázdne, ak chcete používať iba SSID',
|
||||||
TX_POWER: 'Tx výkon',
|
TX_POWER: 'Tx výkon',
|
||||||
@@ -294,9 +280,6 @@ const sk: Translation = {
|
|||||||
ONCHANGE: 'Pri zmene',
|
ONCHANGE: 'Pri zmene',
|
||||||
CONDITION: 'Podmienka',
|
CONDITION: 'Podmienka',
|
||||||
SCHEDULE_UPDATED: 'Plánovanie aktualizované',
|
SCHEDULE_UPDATED: 'Plánovanie aktualizované',
|
||||||
SCHEDULE_TIMER_1: 'pri spustení',
|
|
||||||
SCHEDULE_TIMER_2: 'každú minútu',
|
|
||||||
SCHEDULE_TIMER_3: 'každú hodinu',
|
|
||||||
CUSTOM_ENTITIES: 'Vlastné entity',
|
CUSTOM_ENTITIES: 'Vlastné entity',
|
||||||
ENTITIES_HELP_1: 'Získavanie vlastných entít zo zbernice EMS',
|
ENTITIES_HELP_1: 'Získavanie vlastných entít zo zbernice EMS',
|
||||||
ENTITIES_UPDATED: 'Aktualizované entity',
|
ENTITIES_UPDATED: 'Aktualizované entity',
|
||||||
@@ -312,9 +295,7 @@ const sk: Translation = {
|
|||||||
ACTIVELOW: 'Aktívny Nízky',
|
ACTIVELOW: 'Aktívny Nízky',
|
||||||
UNCHANGED: 'Nezmenené',
|
UNCHANGED: 'Nezmenené',
|
||||||
ALWAYS: 'Vždy',
|
ALWAYS: 'Vždy',
|
||||||
ACTIVITY: 'Aktivita',
|
|
||||||
CONFIGURE: 'Konfigurácia {0}',
|
CONFIGURE: 'Konfigurácia {0}',
|
||||||
SYSTEM_MEMORY: 'Systémová pamäť',
|
|
||||||
APPLICATION_SETTINGS_1: 'Zmeniť nastavenia aplikácie EMS-ESP',
|
APPLICATION_SETTINGS_1: 'Zmeniť nastavenia aplikácie EMS-ESP',
|
||||||
SECURITY_1: 'Pridať, alebo odstrániť používateľov',
|
SECURITY_1: 'Pridať, alebo odstrániť používateľov',
|
||||||
DOWNLOAD_UPLOAD_1: 'Stiahnuť a nahrať nastavenia a firmware',
|
DOWNLOAD_UPLOAD_1: 'Stiahnuť a nahrať nastavenia a firmware',
|
||||||
@@ -326,7 +307,6 @@ const sk: Translation = {
|
|||||||
RENAME: 'Premenovať',
|
RENAME: 'Premenovať',
|
||||||
ENABLE_MODBUS: 'Povoliť Modbus',
|
ENABLE_MODBUS: 'Povoliť Modbus',
|
||||||
VIEW_LOG: 'Zobrazte log na diagnostiku problémov',
|
VIEW_LOG: 'Zobrazte log na diagnostiku problémov',
|
||||||
UPLOAD_DRAG: 'presuňte sem súbor alebo ho kliknutím vyberte',
|
|
||||||
SERVICES: 'Služby',
|
SERVICES: 'Služby',
|
||||||
ALLVALUES: 'Všetky hodnoty',
|
ALLVALUES: 'Všetky hodnoty',
|
||||||
SPECIAL_FUNCTIONS: 'Špeciálne funkcie',
|
SPECIAL_FUNCTIONS: 'Špeciálne funkcie',
|
||||||
@@ -352,10 +332,8 @@ const sk: Translation = {
|
|||||||
THIS_VERSION: 'Táto verzia',
|
THIS_VERSION: 'Táto verzia',
|
||||||
PLATFORM: 'Platforma',
|
PLATFORM: 'Platforma',
|
||||||
RELEASE_TYPE: 'Typ vydania',
|
RELEASE_TYPE: 'Typ vydania',
|
||||||
INTERNET_CONNECTION_REQUIRED: 'Internetové pripojenie je potrebné pre automatickú kontrolu a aktualizáciu',
|
|
||||||
SWITCH_RELEASE_TYPE: 'Prepnúť na {0} verziu',
|
SWITCH_RELEASE_TYPE: 'Prepnúť na {0} verziu',
|
||||||
FIRMWARE_VERSION_INFO: 'Informácie o verzii firmware',
|
FIRMWARE_VERSION_INFO: 'Informácie o verzii firmware',
|
||||||
NO_DATA: 'Žiadne dáta',
|
|
||||||
USER_PROFILE: 'Profil používateľa',
|
USER_PROFILE: 'Profil používateľa',
|
||||||
STORED_VERSIONS: 'Uložené verzie',
|
STORED_VERSIONS: 'Uložené verzie',
|
||||||
ONLINE_HELP: 'online pomoc',
|
ONLINE_HELP: 'online pomoc',
|
||||||
@@ -366,9 +344,14 @@ const sk: Translation = {
|
|||||||
TEST_EMAIL_SUCCESSFUL: 'Test email bol úspešne odoslaný',
|
TEST_EMAIL_SUCCESSFUL: 'Test email bol úspešne odoslaný',
|
||||||
SYSTEM_NAME: 'Názov systému',
|
SYSTEM_NAME: 'Názov systému',
|
||||||
COMMANDS: 'Príkazy',
|
COMMANDS: 'Príkazy',
|
||||||
COMMANDS_UPDATED: 'Príkazy aktualizované',
|
|
||||||
COMMANDS_HELP_1: 'Definujte vlastné príkazy pre magistrali EMS',
|
COMMANDS_HELP_1: 'Definujte vlastné príkazy pre magistrali EMS',
|
||||||
EXECUTE_COMMAND_SENT: 'Príkaz odoslaný',
|
COMMAND_EXECUTED: 'Príkaz vykonaný úspešne',
|
||||||
|
RUN: 'Volať príkaz',
|
||||||
|
INTERNET_CONNECTION_CHECK: 'Kontrola online pre dostupné verzie',
|
||||||
|
UPLOAD_BACKUP_TEXT: 'Kliknite, aby ste vybrali alebo presuňte a upustili súbor zálohy (.json) sem',
|
||||||
|
UPLOAD_FIRMWARE_TEXT: 'Kliknite, aby ste vybrali alebo presuňte a upustili súbor firmware .bin/.md5',
|
||||||
|
UPLOAD_MD5_RECEIVED: 'Suma MD5 prijatá, teraz nahrajte firmware .bin',
|
||||||
|
UPLOAD_MD5_MATCHED: 'MD5 súhlasí, firmvér sa inštaluje...'
|
||||||
};
|
};
|
||||||
|
|
||||||
export default sk;
|
export default sk;
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import type { Translation } from '../i18n-types';
|
|||||||
const sv: Translation = {
|
const sv: Translation = {
|
||||||
LANGUAGE: 'Språk',
|
LANGUAGE: 'Språk',
|
||||||
RETRY: 'Försök igen',
|
RETRY: 'Försök igen',
|
||||||
LOADING: 'Laddar',
|
|
||||||
IS_REQUIRED: '{0} Krävs',
|
IS_REQUIRED: '{0} Krävs',
|
||||||
SIGN_IN: 'Logga in',
|
SIGN_IN: 'Logga in',
|
||||||
SIGN_OUT: 'Logga ut',
|
SIGN_OUT: 'Logga ut',
|
||||||
@@ -37,13 +36,11 @@ const sv: Translation = {
|
|||||||
VALUE: '{{värde|Värde}}',
|
VALUE: '{{värde|Värde}}',
|
||||||
DEVICES: 'Enheter',
|
DEVICES: 'Enheter',
|
||||||
SENSORS: 'Sensorer',
|
SENSORS: 'Sensorer',
|
||||||
RUN_COMMAND: 'Kör kommando',
|
|
||||||
CHANGE_VALUE: 'Ändra värde',
|
CHANGE_VALUE: 'Ändra värde',
|
||||||
CANCEL: 'Avbryt',
|
CANCEL: 'Avbryt',
|
||||||
REMOVE_ALL: 'Ta bort allt',
|
REMOVE_ALL: 'Ta bort allt',
|
||||||
APPLY_CHANGES: 'Utför ändringar ({0})',
|
APPLY_CHANGES: 'Utför ändringar ({0})',
|
||||||
UPDATE: 'Uppdatera',
|
UPDATE: 'Uppdatera',
|
||||||
EXECUTE: 'Utför',
|
|
||||||
REMOVE: 'Ta bort',
|
REMOVE: 'Ta bort',
|
||||||
PROBLEM_UPDATING: 'Problem vid uppdatering',
|
PROBLEM_UPDATING: 'Problem vid uppdatering',
|
||||||
PROBLEM_LOADING: 'Problem vid hämtning',
|
PROBLEM_LOADING: 'Problem vid hämtning',
|
||||||
@@ -52,8 +49,6 @@ const sv: Translation = {
|
|||||||
SETTINGS: 'Inställningar',
|
SETTINGS: 'Inställningar',
|
||||||
UPDATED_OF: '{0} Uppdaterad',
|
UPDATED_OF: '{0} Uppdaterad',
|
||||||
UPDATE_OF: '{0} Uppdatera',
|
UPDATE_OF: '{0} Uppdatera',
|
||||||
REMOVED_OF: '{0} Raderad',
|
|
||||||
DELETION_OF: '{0} Radering',
|
|
||||||
OFFSET: 'Offset',
|
OFFSET: 'Offset',
|
||||||
FACTOR: 'Faktor',
|
FACTOR: 'Faktor',
|
||||||
FREQ: 'Frekvens',
|
FREQ: 'Frekvens',
|
||||||
@@ -66,17 +61,14 @@ const sv: Translation = {
|
|||||||
TEMP_SENSORS: 'Temperatursensorer',
|
TEMP_SENSORS: 'Temperatursensorer',
|
||||||
WRITE_CMD_SENT: 'Skrivkommandon skickade',
|
WRITE_CMD_SENT: 'Skrivkommandon skickade',
|
||||||
EMS_BUS_WARNING: 'EMS-buss nedkopplad. Om denna varning kvarstår efter några sekunder, kontrollera inställningar och enhets-profil.',
|
EMS_BUS_WARNING: 'EMS-buss nedkopplad. Om denna varning kvarstår efter några sekunder, kontrollera inställningar och enhets-profil.',
|
||||||
EMS_BUS_SCANNING: 'Söker efter EMS-enheter...',
|
|
||||||
CONNECTED: 'Ansluten',
|
CONNECTED: 'Ansluten',
|
||||||
TX_ISSUES: 'Sändfel - Prova ett annat TX-läge',
|
TX_ISSUES: 'Sändfel - Prova ett annat TX-läge',
|
||||||
DISCONNECTED: 'Nedkopplad',
|
DISCONNECTED: 'Nedkopplad',
|
||||||
EMS_SCAN: 'Är du säker på att du vill starta en full genomsökning av EMS-bussen?',
|
|
||||||
DATA_TRAFFIC: 'Datatrafik',
|
DATA_TRAFFIC: 'Datatrafik',
|
||||||
EMS_DEVICE: 'EMS enhet',
|
EMS_DEVICE: 'EMS enhet',
|
||||||
SUCCESS: 'Lyckades',
|
SUCCESS: 'Lyckades',
|
||||||
FAIL: 'Misslyckades',
|
FAIL: 'Misslyckades',
|
||||||
QUALITY: 'Kvalitet',
|
QUALITY: 'Kvalitet',
|
||||||
SCAN: 'Sök',
|
|
||||||
STATUS_NAMES: [
|
STATUS_NAMES: [
|
||||||
'EMS-telegram (Rx)',
|
'EMS-telegram (Rx)',
|
||||||
'EMS-läsningar (Tx)',
|
'EMS-läsningar (Tx)',
|
||||||
@@ -127,7 +119,6 @@ const sv: Translation = {
|
|||||||
ENUM_FORMAT: 'Enum-format API/MQTT',
|
ENUM_FORMAT: 'Enum-format API/MQTT',
|
||||||
INDEX: 'Index',
|
INDEX: 'Index',
|
||||||
ENABLE_PARASITE: 'Aktivera 1-wire parasitström',
|
ENABLE_PARASITE: 'Aktivera 1-wire parasitström',
|
||||||
LOGGING: 'Loggning',
|
|
||||||
LOG_HEX: 'Logga EMS-telegram i hexadecimalformat',
|
LOG_HEX: 'Logga EMS-telegram i hexadecimalformat',
|
||||||
ENABLE_SYSLOG: 'Aktivera Syslog',
|
ENABLE_SYSLOG: 'Aktivera Syslog',
|
||||||
LOG_LEVEL: 'Loggnivå',
|
LOG_LEVEL: 'Loggnivå',
|
||||||
@@ -172,7 +163,6 @@ const sv: Translation = {
|
|||||||
CLOSE: 'Stäng',
|
CLOSE: 'Stäng',
|
||||||
USE: 'Använd',
|
USE: 'Använd',
|
||||||
FACTORY_RESET: 'Fabriksåterställning',
|
FACTORY_RESET: 'Fabriksåterställning',
|
||||||
SYSTEM_FACTORY_TEXT: 'Enheten har blivit fabriksåterställd och startar nu om',
|
|
||||||
SYSTEM_FACTORY_TEXT_DIALOG: 'Är du säker på att du vill fabriksåterställa enheten?',
|
SYSTEM_FACTORY_TEXT_DIALOG: 'Är du säker på att du vill fabriksåterställa enheten?',
|
||||||
AVAILABLE_VERSION: 'Senaste tillgängliga versioner',
|
AVAILABLE_VERSION: 'Senaste tillgängliga versioner',
|
||||||
STABLE: 'Stabil',
|
STABLE: 'Stabil',
|
||||||
@@ -188,8 +178,6 @@ const sv: Translation = {
|
|||||||
DOWNLOAD_SETTINGS_TEXT: 'Skapa en säkerhetskopia av din konfiguration och inställningar',
|
DOWNLOAD_SETTINGS_TEXT: 'Skapa en säkerhetskopia av din konfiguration och inställningar',
|
||||||
DOWNLOAD_SETTINGS_TEXT2: 'Exportera alla värden',
|
DOWNLOAD_SETTINGS_TEXT2: 'Exportera alla värden',
|
||||||
DOWNLOAD_SYSTEM_BACKUP: 'System säkerhetskopia',
|
DOWNLOAD_SYSTEM_BACKUP: 'System säkerhetskopia',
|
||||||
UPLOAD_TEXT: 'Ladda upp en ny firmwarefil (.bin) eller en säkerhetskopiafil (.json)',
|
|
||||||
UPLOAD_DROP_TEXT: 'Droppa en firmware .bin fil eller klicka här',
|
|
||||||
ERROR: 'Okänt fel, var god försök igen',
|
ERROR: 'Okänt fel, var god försök igen',
|
||||||
TIME_SET: 'Ställ in tid',
|
TIME_SET: 'Ställ in tid',
|
||||||
MANAGE_USERS: 'Användare',
|
MANAGE_USERS: 'Användare',
|
||||||
@@ -257,9 +245,7 @@ const sv: Translation = {
|
|||||||
NETWORK_SCAN: 'Sök efter WiFi-nätverk',
|
NETWORK_SCAN: 'Sök efter WiFi-nätverk',
|
||||||
IDLE: 'Vilande',
|
IDLE: 'Vilande',
|
||||||
LOST: 'Förlorad',
|
LOST: 'Förlorad',
|
||||||
SCANNING: 'Söker',
|
|
||||||
SCAN_AGAIN: 'Sök igen',
|
SCAN_AGAIN: 'Sök igen',
|
||||||
NETWORK_SCANNER: 'Hittade nätverk',
|
|
||||||
NETWORK_NO_WIFI: 'Inga WiFi-nätverk hittades',
|
NETWORK_NO_WIFI: 'Inga WiFi-nätverk hittades',
|
||||||
NETWORK_BLANK_BSSID: 'lämna blankt för att bara använda SSID',
|
NETWORK_BLANK_BSSID: 'lämna blankt för att bara använda SSID',
|
||||||
TX_POWER: 'Tx effekt',
|
TX_POWER: 'Tx effekt',
|
||||||
@@ -294,9 +280,6 @@ const sv: Translation = {
|
|||||||
ONCHANGE: 'Vid förändring',
|
ONCHANGE: 'Vid förändring',
|
||||||
CONDITION: 'Villkor',
|
CONDITION: 'Villkor',
|
||||||
SCHEDULE_UPDATED: 'Schema uppdaterat',
|
SCHEDULE_UPDATED: 'Schema uppdaterat',
|
||||||
SCHEDULE_TIMER_1: 'vid uppstart',
|
|
||||||
SCHEDULE_TIMER_2: 'varje minut',
|
|
||||||
SCHEDULE_TIMER_3: 'varje timme',
|
|
||||||
CUSTOM_ENTITIES: 'Anpassade entiteter',
|
CUSTOM_ENTITIES: 'Anpassade entiteter',
|
||||||
ENTITIES_HELP_1: 'Hämta anpassade entiteter från EMS bussen',
|
ENTITIES_HELP_1: 'Hämta anpassade entiteter från EMS bussen',
|
||||||
ENTITIES_UPDATED: 'Entiteter uppdaterade',
|
ENTITIES_UPDATED: 'Entiteter uppdaterade',
|
||||||
@@ -312,9 +295,7 @@ const sv: Translation = {
|
|||||||
ACTIVELOW: 'Aktivt låg',
|
ACTIVELOW: 'Aktivt låg',
|
||||||
UNCHANGED: 'Oändrad',
|
UNCHANGED: 'Oändrad',
|
||||||
ALWAYS: 'Alltid',
|
ALWAYS: 'Alltid',
|
||||||
ACTIVITY: 'Aktivitet',
|
|
||||||
CONFIGURE: 'Konfigurera {0}',
|
CONFIGURE: 'Konfigurera {0}',
|
||||||
SYSTEM_MEMORY: 'Systemminne',
|
|
||||||
APPLICATION_SETTINGS_1: 'Ändra EMS-ESP Applikationsinställningar',
|
APPLICATION_SETTINGS_1: 'Ändra EMS-ESP Applikationsinställningar',
|
||||||
SECURITY_1: 'Lägg till eller ta bort användare',
|
SECURITY_1: 'Lägg till eller ta bort användare',
|
||||||
DOWNLOAD_UPLOAD_1: 'Ladda ner eller ladda upp inställningar och firmware',
|
DOWNLOAD_UPLOAD_1: 'Ladda ner eller ladda upp inställningar och firmware',
|
||||||
@@ -326,7 +307,6 @@ const sv: Translation = {
|
|||||||
RENAME: 'Byt namn',
|
RENAME: 'Byt namn',
|
||||||
ENABLE_MODBUS: 'Aktivera Modbus',
|
ENABLE_MODBUS: 'Aktivera Modbus',
|
||||||
VIEW_LOG: 'Titta i loggen för att felsöka problem',
|
VIEW_LOG: 'Titta i loggen för att felsöka problem',
|
||||||
UPLOAD_DRAG: 'dra och släpp en fil här eller klicka för att välja en',
|
|
||||||
SERVICES: 'Tjänster',
|
SERVICES: 'Tjänster',
|
||||||
ALLVALUES: 'Alla värden',
|
ALLVALUES: 'Alla värden',
|
||||||
SPECIAL_FUNCTIONS: 'Specialfunktioner',
|
SPECIAL_FUNCTIONS: 'Specialfunktioner',
|
||||||
@@ -352,10 +332,8 @@ const sv: Translation = {
|
|||||||
THIS_VERSION: 'Denna version',
|
THIS_VERSION: 'Denna version',
|
||||||
PLATFORM: 'Plattform',
|
PLATFORM: 'Plattform',
|
||||||
RELEASE_TYPE: 'Utgivelsestyp',
|
RELEASE_TYPE: 'Utgivelsestyp',
|
||||||
INTERNET_CONNECTION_REQUIRED: 'Internetanslutning krävs för automatisk version kontroll och uppdatering',
|
|
||||||
SWITCH_RELEASE_TYPE: 'Byt till {0} utgåva',
|
SWITCH_RELEASE_TYPE: 'Byt till {0} utgåva',
|
||||||
FIRMWARE_VERSION_INFO: 'Information om firmwareversion',
|
FIRMWARE_VERSION_INFO: 'Information om firmwareversion',
|
||||||
NO_DATA: 'Ingen data',
|
|
||||||
USER_PROFILE: 'Användarprofil',
|
USER_PROFILE: 'Användarprofil',
|
||||||
STORED_VERSIONS: 'Lagrad versioner',
|
STORED_VERSIONS: 'Lagrad versioner',
|
||||||
ONLINE_HELP: 'online hjälp',
|
ONLINE_HELP: 'online hjälp',
|
||||||
@@ -366,9 +344,14 @@ const sv: Translation = {
|
|||||||
TEST_EMAIL_SUCCESSFUL: 'Test email skickad lyckades',
|
TEST_EMAIL_SUCCESSFUL: 'Test email skickad lyckades',
|
||||||
SYSTEM_NAME: 'Systemnamn',
|
SYSTEM_NAME: 'Systemnamn',
|
||||||
COMMANDS: 'Kommandon',
|
COMMANDS: 'Kommandon',
|
||||||
COMMANDS_UPDATED: 'Kommandon uppdaterade',
|
|
||||||
COMMANDS_HELP_1: 'Definiera egna kommandon för EMS-Magistral',
|
COMMANDS_HELP_1: 'Definiera egna kommandon för EMS-Magistral',
|
||||||
EXECUTE_COMMAND_SENT: 'Kommando skickat',
|
COMMAND_EXECUTED: 'Kommando kört lyckades',
|
||||||
|
RUN: 'Kör kommando',
|
||||||
|
INTERNET_CONNECTION_CHECK: 'Kontrollerar online för tillgängliga versioner',
|
||||||
|
UPLOAD_BACKUP_TEXT: 'Klicka för att välja eller dra och släpp en säkerhetskopia (.json) här',
|
||||||
|
UPLOAD_FIRMWARE_TEXT: 'Klicka för att välja eller dra och släpp en firmware .bin/.md5 här',
|
||||||
|
UPLOAD_MD5_RECEIVED: 'MD5 mottagen, ladda nu upp firmware .bin-filen',
|
||||||
|
UPLOAD_MD5_MATCHED: 'MD5 stämmer, firmware installeras...'
|
||||||
};
|
};
|
||||||
|
|
||||||
export default sv;
|
export default sv;
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import type { Translation } from '../i18n-types';
|
|||||||
const tr: Translation = {
|
const tr: Translation = {
|
||||||
LANGUAGE: 'Dil',
|
LANGUAGE: 'Dil',
|
||||||
RETRY: 'Tekrar Dene',
|
RETRY: 'Tekrar Dene',
|
||||||
LOADING: 'Yükleniyor',
|
|
||||||
IS_REQUIRED: '{0} gerekiyor',
|
IS_REQUIRED: '{0} gerekiyor',
|
||||||
SIGN_IN: 'Giriş Yap',
|
SIGN_IN: 'Giriş Yap',
|
||||||
SIGN_OUT: 'Çıkış Yap',
|
SIGN_OUT: 'Çıkış Yap',
|
||||||
@@ -37,13 +36,11 @@ const tr: Translation = {
|
|||||||
VALUE: '{{değer|Değer}}',
|
VALUE: '{{değer|Değer}}',
|
||||||
DEVICES: 'Cihazlar',
|
DEVICES: 'Cihazlar',
|
||||||
SENSORS: 'Sensörler',
|
SENSORS: 'Sensörler',
|
||||||
RUN_COMMAND: 'Çalıştırma Komutu',
|
|
||||||
CHANGE_VALUE: 'Değeri Değiştir',
|
CHANGE_VALUE: 'Değeri Değiştir',
|
||||||
CANCEL: 'İptal',
|
CANCEL: 'İptal',
|
||||||
REMOVE_ALL: 'Tümünü Kaldır',
|
REMOVE_ALL: 'Tümünü Kaldır',
|
||||||
APPLY_CHANGES: 'Değişiklikleri Uygula ({0})',
|
APPLY_CHANGES: 'Değişiklikleri Uygula ({0})',
|
||||||
UPDATE: 'Update',
|
UPDATE: 'Update',
|
||||||
EXECUTE: 'Uygulamak',
|
|
||||||
REMOVE: 'Kaldır',
|
REMOVE: 'Kaldır',
|
||||||
PROBLEM_UPDATING: 'Güncelleme Sorunu',
|
PROBLEM_UPDATING: 'Güncelleme Sorunu',
|
||||||
PROBLEM_LOADING: 'Yükleme Sorunu',
|
PROBLEM_LOADING: 'Yükleme Sorunu',
|
||||||
@@ -52,8 +49,6 @@ const tr: Translation = {
|
|||||||
SETTINGS: 'Ayarlar',
|
SETTINGS: 'Ayarlar',
|
||||||
UPDATED_OF: '{0} Güncellendi',
|
UPDATED_OF: '{0} Güncellendi',
|
||||||
UPDATE_OF: '{0} Güncelleme',
|
UPDATE_OF: '{0} Güncelleme',
|
||||||
REMOVED_OF: '{0} Kaldırıldı',
|
|
||||||
DELETION_OF: '{0} Silindi',
|
|
||||||
OFFSET: 'Fark',
|
OFFSET: 'Fark',
|
||||||
FACTOR: 'Etmen',
|
FACTOR: 'Etmen',
|
||||||
FREQ: 'Frekans',
|
FREQ: 'Frekans',
|
||||||
@@ -66,17 +61,14 @@ const tr: Translation = {
|
|||||||
TEMP_SENSORS: 'Sıcaklık Sensörleri',
|
TEMP_SENSORS: 'Sıcaklık Sensörleri',
|
||||||
WRITE_CMD_SENT: 'Yazma komutu gönderildi',
|
WRITE_CMD_SENT: 'Yazma komutu gönderildi',
|
||||||
EMS_BUS_WARNING: 'EMS hat bağlantısı kesildi. Eğer bu uyarı birkaç saniye sonra devam ediyorsa lütfen ayarları ve kart tipini kontrol edin',
|
EMS_BUS_WARNING: 'EMS hat bağlantısı kesildi. Eğer bu uyarı birkaç saniye sonra devam ediyorsa lütfen ayarları ve kart tipini kontrol edin',
|
||||||
EMS_BUS_SCANNING: 'EMS cihazları aranıyor...',
|
|
||||||
CONNECTED: 'Bağlandı',
|
CONNECTED: 'Bağlandı',
|
||||||
TX_ISSUES: 'Tx sorunu - başka bir Tx Modu deneyin',
|
TX_ISSUES: 'Tx sorunu - başka bir Tx Modu deneyin',
|
||||||
DISCONNECTED: 'Bağlantı kesildi',
|
DISCONNECTED: 'Bağlantı kesildi',
|
||||||
EMS_SCAN: 'EMS Hattında tam bir cihaz taraması başlatmak istediğinizden emin misiniz?',
|
|
||||||
DATA_TRAFFIC: 'Veri trafiği',
|
DATA_TRAFFIC: 'Veri trafiği',
|
||||||
EMS_DEVICE: 'EMS Cihazı',
|
EMS_DEVICE: 'EMS Cihazı',
|
||||||
SUCCESS: 'BAŞARILI',
|
SUCCESS: 'BAŞARILI',
|
||||||
FAIL: 'HATA',
|
FAIL: 'HATA',
|
||||||
QUALITY: 'KALİTE',
|
QUALITY: 'KALİTE',
|
||||||
SCAN: 'Tara',
|
|
||||||
STATUS_NAMES: [
|
STATUS_NAMES: [
|
||||||
'EMS Telegramlar Alındı (Rx)',
|
'EMS Telegramlar Alındı (Rx)',
|
||||||
'EMS Okuması (Tx)',
|
'EMS Okuması (Tx)',
|
||||||
@@ -127,7 +119,6 @@ const tr: Translation = {
|
|||||||
ENUM_FORMAT: 'Enum Biçimleme API/MQTT',
|
ENUM_FORMAT: 'Enum Biçimleme API/MQTT',
|
||||||
INDEX: 'İndeks',
|
INDEX: 'İndeks',
|
||||||
ENABLE_PARASITE: '1-wire parazit gücü devreye al',
|
ENABLE_PARASITE: '1-wire parazit gücü devreye al',
|
||||||
LOGGING: 'Kayıt ediliyor',
|
|
||||||
LOG_HEX: 'EMS telegramlarını hexadecimal olarak kayıt et',
|
LOG_HEX: 'EMS telegramlarını hexadecimal olarak kayıt et',
|
||||||
ENABLE_SYSLOG: 'Sistem Kaydını Devreye Al',
|
ENABLE_SYSLOG: 'Sistem Kaydını Devreye Al',
|
||||||
LOG_LEVEL: 'Kayıt Seviyesi',
|
LOG_LEVEL: 'Kayıt Seviyesi',
|
||||||
@@ -172,7 +163,6 @@ const tr: Translation = {
|
|||||||
CLOSE: 'Kapat',
|
CLOSE: 'Kapat',
|
||||||
USE: 'KUllan',
|
USE: 'KUllan',
|
||||||
FACTORY_RESET: 'Fabrika ayarına dönme',
|
FACTORY_RESET: 'Fabrika ayarına dönme',
|
||||||
SYSTEM_FACTORY_TEXT: 'Cihaz fabrika ayarlarına döndü ve şimdi yendiden başlatılacak',
|
|
||||||
SYSTEM_FACTORY_TEXT_DIALOG: 'Cihazı fabrika ayarlarına döndürmek istediğinize emin misiniz?',
|
SYSTEM_FACTORY_TEXT_DIALOG: 'Cihazı fabrika ayarlarına döndürmek istediğinize emin misiniz?',
|
||||||
AVAILABLE_VERSION: 'En son kullanılabilir sürümler',
|
AVAILABLE_VERSION: 'En son kullanılabilir sürümler',
|
||||||
STABLE: 'Kararlı',
|
STABLE: 'Kararlı',
|
||||||
@@ -188,8 +178,6 @@ const tr: Translation = {
|
|||||||
DOWNLOAD_SETTINGS_TEXT: 'Yapılandırma ve ayarlarınızın yedekleme yapın',
|
DOWNLOAD_SETTINGS_TEXT: 'Yapılandırma ve ayarlarınızın yedekleme yapın',
|
||||||
DOWNLOAD_SETTINGS_TEXT2: 'Tüm değerleri dışarı al',
|
DOWNLOAD_SETTINGS_TEXT2: 'Tüm değerleri dışarı al',
|
||||||
DOWNLOAD_SYSTEM_BACKUP: 'Sistem yedekleme',
|
DOWNLOAD_SYSTEM_BACKUP: 'Sistem yedekleme',
|
||||||
UPLOAD_TEXT: 'Yeni bir firmware dosyası (.bin) veya yedek dosyası (.json) yükle',
|
|
||||||
UPLOAD_DROP_TEXT: 'Bir firmware .bin dosyası veya buraya tıklayın',
|
|
||||||
ERROR: 'Beklenemedik hata, lütfen tekrar deneyin.',
|
ERROR: 'Beklenemedik hata, lütfen tekrar deneyin.',
|
||||||
TIME_SET: 'Zaman ayarı',
|
TIME_SET: 'Zaman ayarı',
|
||||||
MANAGE_USERS: 'Kullanıcıları yönet',
|
MANAGE_USERS: 'Kullanıcıları yönet',
|
||||||
@@ -257,9 +245,7 @@ const tr: Translation = {
|
|||||||
NETWORK_SCAN: 'Kablosuz Ağları Tara',
|
NETWORK_SCAN: 'Kablosuz Ağları Tara',
|
||||||
IDLE: 'Serbest',
|
IDLE: 'Serbest',
|
||||||
LOST: 'Kayıp',
|
LOST: 'Kayıp',
|
||||||
SCANNING: 'Tarıyor',
|
|
||||||
SCAN_AGAIN: 'Tekrar tara',
|
SCAN_AGAIN: 'Tekrar tara',
|
||||||
NETWORK_SCANNER: 'Ağ Tarayıcısı',
|
|
||||||
NETWORK_NO_WIFI: 'Hiçbir Kablosuz Ağ bulunamadı',
|
NETWORK_NO_WIFI: 'Hiçbir Kablosuz Ağ bulunamadı',
|
||||||
NETWORK_BLANK_BSSID: 'sadece SSID kullanmak için boş bırakın',
|
NETWORK_BLANK_BSSID: 'sadece SSID kullanmak için boş bırakın',
|
||||||
TX_POWER: 'Aktarım gücü',
|
TX_POWER: 'Aktarım gücü',
|
||||||
@@ -294,9 +280,6 @@ const tr: Translation = {
|
|||||||
ONCHANGE: 'Değişimde',
|
ONCHANGE: 'Değişimde',
|
||||||
CONDITION: 'Durum',
|
CONDITION: 'Durum',
|
||||||
SCHEDULE_UPDATED: 'Zamanlama güncellendi',
|
SCHEDULE_UPDATED: 'Zamanlama güncellendi',
|
||||||
SCHEDULE_TIMER_1: 'Başlangıçta',
|
|
||||||
SCHEDULE_TIMER_2: 'her dakikada',
|
|
||||||
SCHEDULE_TIMER_3: 'her saatte',
|
|
||||||
CUSTOM_ENTITIES: 'Özel Varlıklar',
|
CUSTOM_ENTITIES: 'Özel Varlıklar',
|
||||||
ENTITIES_HELP_1: 'Özel varlıkları EMS hattından alın',
|
ENTITIES_HELP_1: 'Özel varlıkları EMS hattından alın',
|
||||||
ENTITIES_UPDATED: 'Varlıklar güncellendi',
|
ENTITIES_UPDATED: 'Varlıklar güncellendi',
|
||||||
@@ -312,9 +295,7 @@ const tr: Translation = {
|
|||||||
ACTIVELOW: 'Aktif Düşük',
|
ACTIVELOW: 'Aktif Düşük',
|
||||||
UNCHANGED: 'Değiştirilmedi',
|
UNCHANGED: 'Değiştirilmedi',
|
||||||
ALWAYS: 'Her zaman',
|
ALWAYS: 'Her zaman',
|
||||||
ACTIVITY: 'Faaliyet',
|
|
||||||
CONFIGURE: 'Yapılandır {0}',
|
CONFIGURE: 'Yapılandır {0}',
|
||||||
SYSTEM_MEMORY: 'Sistem Belleği',
|
|
||||||
APPLICATION_SETTINGS_1: 'EMS-ESP Uygulama Ayarlarını Değiştir',
|
APPLICATION_SETTINGS_1: 'EMS-ESP Uygulama Ayarlarını Değiştir',
|
||||||
SECURITY_1: 'Kullanıcıları ekle veya kaldır',
|
SECURITY_1: 'Kullanıcıları ekle veya kaldır',
|
||||||
DOWNLOAD_UPLOAD_1: 'Ayarları ve firmware dosyasını indir ve yükle',
|
DOWNLOAD_UPLOAD_1: 'Ayarları ve firmware dosyasını indir ve yükle',
|
||||||
@@ -326,7 +307,6 @@ const tr: Translation = {
|
|||||||
RENAME: 'Yeniden Adlandır',
|
RENAME: 'Yeniden Adlandır',
|
||||||
ENABLE_MODBUS: 'Modbus\'ı etkinleştir',
|
ENABLE_MODBUS: 'Modbus\'ı etkinleştir',
|
||||||
VIEW_LOG: 'Sorunu tanımlamak için günlüğü görüntüleyin',
|
VIEW_LOG: 'Sorunu tanımlamak için günlüğü görüntüleyin',
|
||||||
UPLOAD_DRAG: 'Bir dosya buraya sürükleyip bırakın veya seçmek için tıklayın',
|
|
||||||
SERVICES: 'Hizmetler',
|
SERVICES: 'Hizmetler',
|
||||||
ALLVALUES: 'Tüm Değerler',
|
ALLVALUES: 'Tüm Değerler',
|
||||||
SPECIAL_FUNCTIONS: 'Özel Fonksiyonlar',
|
SPECIAL_FUNCTIONS: 'Özel Fonksiyonlar',
|
||||||
@@ -352,10 +332,8 @@ const tr: Translation = {
|
|||||||
THIS_VERSION: 'Bu Sürüm',
|
THIS_VERSION: 'Bu Sürüm',
|
||||||
PLATFORM: 'Platforma',
|
PLATFORM: 'Platforma',
|
||||||
RELEASE_TYPE: 'Sürüm Tipi',
|
RELEASE_TYPE: 'Sürüm Tipi',
|
||||||
INTERNET_CONNECTION_REQUIRED: 'Otomatik sürüm kontrolü ve güncelleme için internet bağlantısı gereklidir',
|
|
||||||
SWITCH_RELEASE_TYPE: '{0} sürümüne geç',
|
SWITCH_RELEASE_TYPE: '{0} sürümüne geç',
|
||||||
FIRMWARE_VERSION_INFO: 'Firmware Sürüm Bilgisi',
|
FIRMWARE_VERSION_INFO: 'Firmware Sürüm Bilgisi',
|
||||||
NO_DATA: 'Hiçbir veri yok',
|
|
||||||
USER_PROFILE: 'Kullanıcı Profili',
|
USER_PROFILE: 'Kullanıcı Profili',
|
||||||
STORED_VERSIONS: 'Kaydedilmiş Sürümler',
|
STORED_VERSIONS: 'Kaydedilmiş Sürümler',
|
||||||
ONLINE_HELP: 'online yardım',
|
ONLINE_HELP: 'online yardım',
|
||||||
@@ -366,9 +344,14 @@ const tr: Translation = {
|
|||||||
TEST_EMAIL_SUCCESSFUL: 'Test email başarıyla gönderildi',
|
TEST_EMAIL_SUCCESSFUL: 'Test email başarıyla gönderildi',
|
||||||
SYSTEM_NAME: 'Sistem Adı',
|
SYSTEM_NAME: 'Sistem Adı',
|
||||||
COMMANDS: 'Komutlar',
|
COMMANDS: 'Komutlar',
|
||||||
COMMANDS_UPDATED: 'Komutlar güncellendi',
|
|
||||||
COMMANDS_HELP_1: 'Özel komutları EMS hattına tanımlayın',
|
COMMANDS_HELP_1: 'Özel komutları EMS hattına tanımlayın',
|
||||||
EXECUTE_COMMAND_SENT: 'Komut gönderildi',
|
COMMAND_EXECUTED: 'Komut başarıyla çalıştırıldı',
|
||||||
|
RUN: 'Komutu çalıştır',
|
||||||
|
INTERNET_CONNECTION_CHECK: 'Online bağlantısı kontrol ediliyor',
|
||||||
|
UPLOAD_BACKUP_TEXT: 'Yeni bir firmware dosyası (.bin) veya yedek dosyası (.json) yükle',
|
||||||
|
UPLOAD_FIRMWARE_TEXT: 'Bir firmware .bin dosyası veya buraya tıklayın',
|
||||||
|
UPLOAD_MD5_RECEIVED: 'Özet alındı, şimdi firmware .bin dosyasını yükleyin',
|
||||||
|
UPLOAD_MD5_MATCHED: 'MD5 eşleşti, firmware yükleniyor...'
|
||||||
};
|
};
|
||||||
|
|
||||||
export default tr;
|
export default tr;
|
||||||
|
|||||||
@@ -54,6 +54,7 @@ export interface NetworkSettingsType {
|
|||||||
enableMDNS: boolean;
|
enableMDNS: boolean;
|
||||||
enableCORS: boolean;
|
enableCORS: boolean;
|
||||||
CORSOrigin: string;
|
CORSOrigin: string;
|
||||||
|
eth_10mbit: boolean;
|
||||||
[key: string]: unknown;
|
[key: string]: unknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
// Types for the `getVersions` action response coming from the device.
|
// Types for the `getVersions` action response coming from the device.
|
||||||
// The device proxies the request to emsesp.org/versions.json. If the device
|
// The device proxies the request to emsesp.org/versions.json. If the device
|
||||||
// is offline the `stable` and `dev` fields are omitted.
|
// is offline the `stable` and `dev` fields are omitted, but `system_name` is
|
||||||
|
// always sent as it comes from the device itself.
|
||||||
|
|
||||||
export interface VersionInfo {
|
export interface VersionInfo {
|
||||||
version: string;
|
version: string;
|
||||||
@@ -20,4 +21,5 @@ export interface VersionsResponse {
|
|||||||
current: CurrentVersionInfo;
|
current: CurrentVersionInfo;
|
||||||
stable?: RemoteVersionInfo;
|
stable?: RemoteVersionInfo;
|
||||||
dev?: RemoteVersionInfo;
|
dev?: RemoteVersionInfo;
|
||||||
|
system_name?: string;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,6 +57,7 @@
|
|||||||
"assumeChangesOnlyAffectDirectDependencies": true,
|
"assumeChangesOnlyAffectDirectDependencies": true,
|
||||||
|
|
||||||
// Path mapping for cleaner imports
|
// Path mapping for cleaner imports
|
||||||
|
"ignoreDeprecations": "6.0",
|
||||||
"baseUrl": ".",
|
"baseUrl": ".",
|
||||||
"paths": {
|
"paths": {
|
||||||
"@/*": ["src/*"],
|
"@/*": ["src/*"],
|
||||||
|
|||||||
+24
-17
@@ -2,6 +2,7 @@ import preact from '@preact/preset-vite';
|
|||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import { visualizer } from 'rollup-plugin-visualizer';
|
import { visualizer } from 'rollup-plugin-visualizer';
|
||||||
|
import { pathToFileURL } from 'url';
|
||||||
import { Plugin, PluginOption, defineConfig } from 'vite';
|
import { Plugin, PluginOption, defineConfig } from 'vite';
|
||||||
import viteImagemin from 'vite-plugin-imagemin';
|
import viteImagemin from 'vite-plugin-imagemin';
|
||||||
import zlib from 'zlib';
|
import zlib from 'zlib';
|
||||||
@@ -178,24 +179,20 @@ const createProductionTerserOptions = () => ({
|
|||||||
toplevel: true
|
toplevel: true
|
||||||
});
|
});
|
||||||
|
|
||||||
// Image optimization plugin
|
// Image optimization plugin.
|
||||||
|
// Only svgo runs here: the raster optimizers (optipng, pngquant, gifsicle,
|
||||||
|
// jpegtran, mozjpeg) are native binaries whose install scripts are blocked by
|
||||||
|
// `allowBuilds` in pnpm-workspace.yaml, so they would fail on every build.
|
||||||
|
// Raster assets are committed pre-optimized instead.
|
||||||
const imageOptimizationPlugin = {
|
const imageOptimizationPlugin = {
|
||||||
...viteImagemin({
|
...viteImagemin({
|
||||||
verbose: false,
|
verbose: false,
|
||||||
gifsicle: {
|
filter: /\.svg$/i,
|
||||||
optimizationLevel: 7,
|
gifsicle: false,
|
||||||
interlaced: false
|
jpegTran: false,
|
||||||
},
|
mozjpeg: false,
|
||||||
optipng: {
|
optipng: false,
|
||||||
optimizationLevel: 7
|
pngquant: false,
|
||||||
},
|
|
||||||
mozjpeg: {
|
|
||||||
quality: 20
|
|
||||||
},
|
|
||||||
pngquant: {
|
|
||||||
quality: [0.8, 0.9],
|
|
||||||
speed: 4
|
|
||||||
},
|
|
||||||
svgo: {
|
svgo: {
|
||||||
plugins: [
|
plugins: [
|
||||||
{ name: 'removeViewBox' },
|
{ name: 'removeViewBox' },
|
||||||
@@ -210,8 +207,12 @@ export default defineConfig(
|
|||||||
async ({ command, mode }: { command: string; mode: string }) => {
|
async ({ command, mode }: { command: string; mode: string }) => {
|
||||||
if (command === 'serve') {
|
if (command === 'serve') {
|
||||||
console.log(`Preparing for standalone build with server, mode=${mode}`);
|
console.log(`Preparing for standalone build with server, mode=${mode}`);
|
||||||
// @ts-expect-error - mock server doesn't have type declarations
|
const mockServerUrl = pathToFileURL(
|
||||||
const { default: mockServer } = await import('../mock-api/mockServer.js');
|
path.resolve(import.meta.dirname, '../mock-api/mockServer.js')
|
||||||
|
).href;
|
||||||
|
const { default: mockServer } = (await import(
|
||||||
|
/* @vite-ignore */ mockServerUrl
|
||||||
|
)) as { default: () => PluginOption };
|
||||||
return {
|
return {
|
||||||
plugins: [...createBasePlugins(true, true), mockServer()],
|
plugins: [...createBasePlugins(true, true), mockServer()],
|
||||||
resolve: {
|
resolve: {
|
||||||
@@ -253,6 +254,9 @@ export default defineConfig(
|
|||||||
minify: 'terser' as const,
|
minify: 'terser' as const,
|
||||||
terserOptions: createHostedTerserOptions(),
|
terserOptions: createHostedTerserOptions(),
|
||||||
rollupOptions: {
|
rollupOptions: {
|
||||||
|
checks: {
|
||||||
|
pluginTimings: false
|
||||||
|
},
|
||||||
treeshake: {
|
treeshake: {
|
||||||
moduleSideEffects: false
|
moduleSideEffects: false
|
||||||
},
|
},
|
||||||
@@ -288,6 +292,9 @@ export default defineConfig(
|
|||||||
minify: 'terser' as const,
|
minify: 'terser' as const,
|
||||||
terserOptions: createProductionTerserOptions(),
|
terserOptions: createProductionTerserOptions(),
|
||||||
rollupOptions: {
|
rollupOptions: {
|
||||||
|
checks: {
|
||||||
|
pluginTimings: false
|
||||||
|
},
|
||||||
treeshake: {
|
treeshake: {
|
||||||
moduleSideEffects: false,
|
moduleSideEffects: false,
|
||||||
propertyReadSideEffects: false as const,
|
propertyReadSideEffects: false as const,
|
||||||
|
|||||||
@@ -29,6 +29,7 @@
|
|||||||
|
|
||||||
NativeConsole Serial;
|
NativeConsole Serial;
|
||||||
|
|
||||||
|
EspClass ESP;
|
||||||
ETHClass ETH;
|
ETHClass ETH;
|
||||||
WiFiClass WiFi;
|
WiFiClass WiFi;
|
||||||
|
|
||||||
|
|||||||
@@ -34,6 +34,15 @@
|
|||||||
#include "WString.h"
|
#include "WString.h"
|
||||||
#include "Network.h"
|
#include "Network.h"
|
||||||
|
|
||||||
|
// stub of Arduino-ESP32's EspClass, holding only what is called outside of #ifndef EMSESP_STANDALONE
|
||||||
|
class EspClass {
|
||||||
|
public:
|
||||||
|
size_t getPsramSize() {
|
||||||
|
return 0; // standalone has no PSRAM
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
extern EspClass ESP;
|
||||||
extern ETHClass ETH;
|
extern ETHClass ETH;
|
||||||
extern WiFiClass WiFi;
|
extern WiFiClass WiFi;
|
||||||
|
|
||||||
|
|||||||
@@ -22,11 +22,16 @@
|
|||||||
#define NTP_SETTINGS_FILE "/config/ntpSettings.json"
|
#define NTP_SETTINGS_FILE "/config/ntpSettings.json"
|
||||||
#define EMSESP_SETTINGS_FILE "/config/emsespSettings.json"
|
#define EMSESP_SETTINGS_FILE "/config/emsespSettings.json"
|
||||||
|
|
||||||
|
// AP provision modes, mirrors src/ESP32React/APSettingsService.h
|
||||||
|
#define AP_MODE_DISCONNECTED 1
|
||||||
|
#define AP_MODE_NEVER 2
|
||||||
|
|
||||||
class DummySettings {
|
class DummySettings {
|
||||||
public:
|
public:
|
||||||
// SYSTEM
|
// SYSTEM
|
||||||
bool bandwidth20 = false;
|
bool bandwidth20 = false;
|
||||||
bool nosleep = true;
|
bool nosleep = true;
|
||||||
|
bool eth_10mbit = false;
|
||||||
|
|
||||||
// MQTT
|
// MQTT
|
||||||
uint16_t publish_time = 10;
|
uint16_t publish_time = 10;
|
||||||
@@ -81,7 +86,7 @@ class DummySettings {
|
|||||||
String password = "ems-esp";
|
String password = "ems-esp";
|
||||||
|
|
||||||
// AP
|
// AP
|
||||||
uint8_t provisionMode = 0;
|
uint8_t provisionMode = AP_MODE_DISCONNECTED; // always
|
||||||
|
|
||||||
// NTP
|
// NTP
|
||||||
String server = "pool.ntp.org";
|
String server = "pool.ntp.org";
|
||||||
|
|||||||
@@ -64,9 +64,11 @@ class ESP_SSLClient : public Stream {
|
|||||||
}
|
}
|
||||||
void flush() override {
|
void flush() override {
|
||||||
}
|
}
|
||||||
|
|
||||||
void stop() {
|
void stop() {
|
||||||
}
|
}
|
||||||
|
void setTimeout(uint32_t) {
|
||||||
|
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // ESP_SSLClient_h
|
#endif // ESP_SSLClient_h
|
||||||
|
|||||||
@@ -39,7 +39,11 @@ class WiFiClient {
|
|||||||
}
|
}
|
||||||
void stop() {
|
void stop() {
|
||||||
}
|
}
|
||||||
|
void setTimeout(uint32_t){
|
||||||
|
|
||||||
|
}
|
||||||
|
void setConnectionTimeout(uint32_t) {
|
||||||
|
}
|
||||||
// ESP32 socket option passthrough (e.g. TCP_NODELAY)
|
// ESP32 socket option passthrough (e.g. TCP_NODELAY)
|
||||||
int setSocketOption(int, int, const void *, size_t) {
|
int setSocketOption(int, int, const void *, size_t) {
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
+106
-115
@@ -1,27 +1,11 @@
|
|||||||
// Mock server for development
|
// EMS-ESP standalone mode to simulate
|
||||||
// Simulates file uploads and EventSource (SSE) for log messages
|
// a REST server for file uploads
|
||||||
import formidable from 'formidable';
|
// an EventSource (Server-Sent Events) for log messages in EMS-ESP standalone mode
|
||||||
|
|
||||||
// Constants reused across requests
|
|
||||||
const VALID_EXTENSIONS = new Set(['bin', 'json', 'md5']);
|
|
||||||
const ONE_SECOND_MS = 1000;
|
const ONE_SECOND_MS = 1000;
|
||||||
const TEN_PERCENT = 10;
|
|
||||||
|
|
||||||
// padding function
|
// padding function
|
||||||
const pad = (number) => String(number).padStart(2, '0');
|
const pad = (number) => String(number).padStart(2, '0');
|
||||||
|
|
||||||
// Simple throttle helper (time-based)
|
|
||||||
const throttle = (fn, intervalMs) => {
|
|
||||||
let last = 0;
|
|
||||||
return (...args) => {
|
|
||||||
const now = Date.now();
|
|
||||||
if (now - last >= intervalMs) {
|
|
||||||
last = now;
|
|
||||||
fn(...args);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
// Cached date formatter to avoid prototype pollution
|
// Cached date formatter to avoid prototype pollution
|
||||||
const formatDate = (date) => {
|
const formatDate = (date) => {
|
||||||
const year = date.getUTCFullYear();
|
const year = date.getUTCFullYear();
|
||||||
@@ -38,108 +22,115 @@ const formatDate = (date) => {
|
|||||||
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}.${milliseconds}`;
|
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}.${milliseconds}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const MOCK_REST_ORIGIN = 'http://localhost:3080';
|
||||||
|
const UPLOAD_SIMULATED_KB_PER_SEC = 256;
|
||||||
|
const UPLOAD_SLICE_BYTES = 16 * 1024;
|
||||||
|
|
||||||
|
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
||||||
|
|
||||||
|
const readRequestThrottled = (req) =>
|
||||||
|
new Promise((resolve, reject) => {
|
||||||
|
const chunks = [];
|
||||||
|
let ended = false;
|
||||||
|
let settled = false;
|
||||||
|
|
||||||
|
const settle = (settler, value) => {
|
||||||
|
if (settled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
settled = true;
|
||||||
|
settler(value);
|
||||||
|
};
|
||||||
|
|
||||||
|
req.on('error', (error) => settle(reject, error));
|
||||||
|
req.on('end', () => {
|
||||||
|
ended = true;
|
||||||
|
void step();
|
||||||
|
});
|
||||||
|
|
||||||
|
const step = async () => {
|
||||||
|
if (settled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const chunk = req.read(UPLOAD_SLICE_BYTES);
|
||||||
|
if (chunk) {
|
||||||
|
chunks.push(chunk);
|
||||||
|
await sleep((chunk.length / (UPLOAD_SIMULATED_KB_PER_SEC * 1024)) * 1000);
|
||||||
|
void step();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (ended || req.readableEnded) {
|
||||||
|
settle(resolve, Buffer.concat(chunks));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
req.once('readable', () => {
|
||||||
|
void step();
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
settle(reject, error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void step();
|
||||||
|
});
|
||||||
|
|
||||||
|
const forwardThrottledUpload = async (req, res) => {
|
||||||
|
const startedAt = Date.now();
|
||||||
|
try {
|
||||||
|
const body = await readRequestThrottled(req);
|
||||||
|
console.log(
|
||||||
|
`Upload received: ${body.length} bytes in ${((Date.now() - startedAt) / 1000).toFixed(1)}s`
|
||||||
|
);
|
||||||
|
const headers = {};
|
||||||
|
if (req.headers['content-type']) {
|
||||||
|
headers['content-type'] = req.headers['content-type'];
|
||||||
|
}
|
||||||
|
if (req.headers.authorization) {
|
||||||
|
headers.authorization = req.headers.authorization;
|
||||||
|
}
|
||||||
|
headers['content-length'] = String(body.length);
|
||||||
|
|
||||||
|
const bunRes = await fetch(`${MOCK_REST_ORIGIN}${req.url}`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers,
|
||||||
|
body
|
||||||
|
});
|
||||||
|
const buf = Buffer.from(await bunRes.arrayBuffer());
|
||||||
|
res.statusCode = bunRes.status;
|
||||||
|
bunRes.headers.forEach((value, key) => {
|
||||||
|
if (key !== 'transfer-encoding' && key !== 'content-encoding') {
|
||||||
|
res.setHeader(key, value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
res.end(buf);
|
||||||
|
} catch (error) {
|
||||||
|
if (error?.code === 'ECONNRESET' || req.destroyed) {
|
||||||
|
console.log('Upload cancelled by the client');
|
||||||
|
} else {
|
||||||
|
console.error('Throttled upload proxy failed', error);
|
||||||
|
}
|
||||||
|
if (!res.headersSent) {
|
||||||
|
res.statusCode = 502;
|
||||||
|
}
|
||||||
|
if (!res.writableEnded) {
|
||||||
|
res.end();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
export default () => {
|
export default () => {
|
||||||
return {
|
return {
|
||||||
name: 'vite:mockserver',
|
name: 'vite:mockServer',
|
||||||
configureServer: async (server) => {
|
configureServer: async (server) => {
|
||||||
server.middlewares.use(async (req, res, next) => {
|
server.middlewares.use(async (req, res, next) => {
|
||||||
// Handle file uploads
|
if (req.url.startsWith('/rest/uploadFile') && req.method === 'POST') {
|
||||||
if (req.url.startsWith('/rest/uploadFile')) {
|
await forwardThrottledUpload(req, res);
|
||||||
// CORS preflight support
|
return;
|
||||||
if (req.method === 'OPTIONS') {
|
|
||||||
res.writeHead(204, {
|
|
||||||
'Access-Control-Allow-Origin': '*',
|
|
||||||
'Access-Control-Allow-Methods': 'POST, OPTIONS',
|
|
||||||
'Access-Control-Allow-Headers': 'Content-Type, Cache-Control',
|
|
||||||
'Access-Control-Max-Age': '600'
|
|
||||||
});
|
|
||||||
res.end();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (req.method !== 'POST') {
|
|
||||||
res.statusCode = 405;
|
|
||||||
res.setHeader('Allow', 'POST, OPTIONS');
|
|
||||||
res.end('Method Not Allowed');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const fileSize = parseInt(req.headers['content-length'] || '0', 10);
|
|
||||||
let progress = 0;
|
|
||||||
|
|
||||||
// Track upload progress
|
|
||||||
const logThrottled = throttle((percentage) => {
|
|
||||||
console.log(`Upload progress: ${percentage}%`);
|
|
||||||
}, ONE_SECOND_MS);
|
|
||||||
|
|
||||||
req.on('data', (chunk) => {
|
|
||||||
progress += chunk.length;
|
|
||||||
if (fileSize > 0) {
|
|
||||||
const percentage = Math.round((progress / fileSize) * 100);
|
|
||||||
// Only log every ~1s and for meaningful changes (>=10%)
|
|
||||||
if (percentage % TEN_PERCENT === 0) {
|
|
||||||
logThrottled(percentage);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
try {
|
|
||||||
const form = formidable({
|
|
||||||
maxFileSize: 50 * 1024 * 1024, // 50MB limit
|
|
||||||
keepExtensions: true,
|
|
||||||
multiples: false,
|
|
||||||
allowEmptyFiles: false
|
|
||||||
});
|
|
||||||
|
|
||||||
const [fields, files] = await form.parse(req);
|
|
||||||
|
|
||||||
if (Object.keys(files).length === 0) {
|
|
||||||
res.statusCode = 400;
|
|
||||||
res.end('No file uploaded');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const uploadedFile = Array.isArray(files.file)
|
|
||||||
? files.file[0]
|
|
||||||
: files.file;
|
|
||||||
const fileName = uploadedFile.originalFilename;
|
|
||||||
const fileExtension = fileName
|
|
||||||
.substring(fileName.lastIndexOf('.') + 1)
|
|
||||||
.toLowerCase();
|
|
||||||
|
|
||||||
console.log(
|
|
||||||
`File uploaded: ${fileName} (${fileExtension}, ${fileSize} bytes)`
|
|
||||||
);
|
|
||||||
|
|
||||||
// Validate file extension
|
|
||||||
if (!VALID_EXTENSIONS.has(fileExtension)) {
|
|
||||||
res.statusCode = 406;
|
|
||||||
res.end('Invalid file extension');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle different file types
|
|
||||||
if (fileExtension === 'md5') {
|
|
||||||
res.setHeader('Content-Type', 'application/json');
|
|
||||||
res.end(
|
|
||||||
JSON.stringify({
|
|
||||||
md5: 'ef4304fc4d9025a58dcf25d71c882d2c'
|
|
||||||
})
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
console.log('File uploaded successfully!');
|
|
||||||
res.end();
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
console.error('Upload error:', err && err.message ? err.message : err);
|
|
||||||
res.statusCode = err.httpCode || 400;
|
|
||||||
res.setHeader('Content-Type', 'text/plain');
|
|
||||||
res.end(err && err.message ? err.message : 'Upload error');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle Server-Sent Events (SSE) for log streaming
|
// Handle Server-Sent Events (SSE) for log streaming
|
||||||
else if (req.url.startsWith('/es/log')) {
|
if (req.url.startsWith('/es/log')) {
|
||||||
// Set SSE headers
|
// Set SSE headers
|
||||||
res.writeHead(200, {
|
res.writeHead(200, {
|
||||||
Connection: 'keep-alive',
|
Connection: 'keep-alive',
|
||||||
@@ -210,7 +201,7 @@ export default () => {
|
|||||||
res.on('error', cleanup);
|
res.on('error', cleanup);
|
||||||
res.on('finish', cleanup);
|
res.on('finish', cleanup);
|
||||||
} else {
|
} else {
|
||||||
next(); // Continue to next middleware
|
next(); // Continue to next middleware (Vite proxy → restServer)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,12 +11,11 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@msgpack/msgpack": "^3.1.3",
|
"@msgpack/msgpack": "^3.1.3",
|
||||||
"formidable": "^3.5.4",
|
|
||||||
"itty-router": "^5.0.24"
|
"itty-router": "^5.0.24"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@trivago/prettier-plugin-sort-imports": "^6.0.2",
|
"@trivago/prettier-plugin-sort-imports": "^6.0.2",
|
||||||
"prettier": "^3.9.6"
|
"prettier": "^3.9.6"
|
||||||
},
|
},
|
||||||
"packageManager": "pnpm@11.18.0+sha512.33d83c77da82f49fba836925c6f1b841181ec3132b670639bd012f7075f5c7cf634c5f870147c19aae7478fac01df09d8892e880454896edd23ee9b33757563c"
|
"packageManager": "pnpm@12.3.4+sha512.961aa41fb077da3a04a441d9f8e15ebc0c96da8ef710b2eb67bf9ee7cb0610eabd48f1fd85f51cffe73846785fa0f87c56a3a872a1d893f8446741b5cce45457"
|
||||||
}
|
}
|
||||||
|
|||||||
Generated
+106
-56
@@ -1,3 +1,104 @@
|
|||||||
|
---
|
||||||
|
lockfileVersion: '9.0'
|
||||||
|
|
||||||
|
importers:
|
||||||
|
|
||||||
|
.:
|
||||||
|
configDependencies: {}
|
||||||
|
packageManagerDependencies:
|
||||||
|
pnpm:
|
||||||
|
specifier: 12.3.4
|
||||||
|
version: 12.3.4
|
||||||
|
|
||||||
|
packages:
|
||||||
|
|
||||||
|
'@pnpm/exe.darwin-arm64@12.3.4':
|
||||||
|
resolution: {integrity: sha512-PAyUol8T1+/+ViOiXAt51ECA+QnfXCqz6foL4bW+LsoX0NcVd5XVEM2mRQu+LV4oc7uRz9zf9U0P+XFfuQeDAw==}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [darwin]
|
||||||
|
|
||||||
|
'@pnpm/exe.darwin-x64@12.3.4':
|
||||||
|
resolution: {integrity: sha512-fxP9JCk0Cdye+ePuj+GJJLMUMTqHGWRdb1dtv4How876uQ2ehxvenpgiYAir/ceO9PsYUZkFTtyZdx+rRu5QOA==}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [darwin]
|
||||||
|
|
||||||
|
'@pnpm/exe.linux-arm64-musl@12.3.4':
|
||||||
|
resolution: {integrity: sha512-FBOt0/7ye6O6q4AllVV5QMviB6qE6fqkeczV/+MDWQsmo+QJrlfsh6X7CpH/tClVpBZEyIbjpUoT8bNhCYBxEg==}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [linux]
|
||||||
|
libc: [musl]
|
||||||
|
|
||||||
|
'@pnpm/exe.linux-arm64@12.3.4':
|
||||||
|
resolution: {integrity: sha512-t71AVA7LRqiKTyZ5xMYaZc2n5DfdpMbfokZuiIOXHBOM03ECnF0t4iYwaBDqJgVjlKYUOwaF/bRQajGNA4cJ4w==}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [linux]
|
||||||
|
libc: [glibc]
|
||||||
|
|
||||||
|
'@pnpm/exe.linux-x64-musl@12.3.4':
|
||||||
|
resolution: {integrity: sha512-RPmk7Jb/aYaFvL2iyDN/AtMY+hUEsue732WmXpcuQ9tBpMnGyA5py7Z3+e+qmQaJ0zY/4ni9jJiyPBQHujmv6w==}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [linux]
|
||||||
|
libc: [musl]
|
||||||
|
|
||||||
|
'@pnpm/exe.linux-x64@12.3.4':
|
||||||
|
resolution: {integrity: sha512-2ZqOlSPkfwX1h5cR+FPiWf8+F+2hZT/3TvhUK5sigHqwaQCIiq8R7CGxhndKs63JtcLi2a1Qpo+wX/EoyfjyJQ==}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [linux]
|
||||||
|
libc: [glibc]
|
||||||
|
|
||||||
|
'@pnpm/exe.win32-arm64@12.3.4':
|
||||||
|
resolution: {integrity: sha512-ANyrHqyqco6SXBysUTRF74itDyyraea7IbFsKFdNXTjcFnfycTDx37EwuhdpPYFNSIh2JhUG4fByclsRfiHX7w==}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [win32]
|
||||||
|
|
||||||
|
'@pnpm/exe.win32-x64@12.3.4':
|
||||||
|
resolution: {integrity: sha512-WH/KqBPY/hq2Tb7SgQltEZytimcjgKRaCRL/aM9CI0c67iKc5TVmHUhIiL3Ux9FB4bWn36i6XewUcScQI+zG8w==}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [win32]
|
||||||
|
|
||||||
|
pnpm@12.3.4:
|
||||||
|
resolution: {integrity: sha512-lhqkH7B32joEpEHZ+OFevAyW2o73ELLrZ7+e58sGEOq9SPH9hfUc/+c4RnhfoPh8VqOocqHYk/hEZ0G1zORUVw==}
|
||||||
|
engines: {node: '>=18.*'}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
|
snapshots:
|
||||||
|
|
||||||
|
'@pnpm/exe.darwin-arm64@12.3.4':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@pnpm/exe.darwin-x64@12.3.4':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@pnpm/exe.linux-arm64-musl@12.3.4':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@pnpm/exe.linux-arm64@12.3.4':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@pnpm/exe.linux-x64-musl@12.3.4':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@pnpm/exe.linux-x64@12.3.4':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@pnpm/exe.win32-arm64@12.3.4':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@pnpm/exe.win32-x64@12.3.4':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
pnpm@12.3.4:
|
||||||
|
optionalDependencies:
|
||||||
|
'@pnpm/exe.darwin-arm64': 12.3.4
|
||||||
|
'@pnpm/exe.darwin-x64': 12.3.4
|
||||||
|
'@pnpm/exe.linux-arm64': 12.3.4
|
||||||
|
'@pnpm/exe.linux-arm64-musl': 12.3.4
|
||||||
|
'@pnpm/exe.linux-x64': 12.3.4
|
||||||
|
'@pnpm/exe.linux-x64-musl': 12.3.4
|
||||||
|
'@pnpm/exe.win32-arm64': 12.3.4
|
||||||
|
'@pnpm/exe.win32-x64': 12.3.4
|
||||||
|
|
||||||
|
---
|
||||||
lockfileVersion: '9.0'
|
lockfileVersion: '9.0'
|
||||||
|
|
||||||
settings:
|
settings:
|
||||||
@@ -11,9 +112,6 @@ importers:
|
|||||||
'@msgpack/msgpack':
|
'@msgpack/msgpack':
|
||||||
specifier: ^3.1.3
|
specifier: ^3.1.3
|
||||||
version: 3.1.3
|
version: 3.1.3
|
||||||
formidable:
|
|
||||||
specifier: ^3.5.4
|
|
||||||
version: 3.5.4
|
|
||||||
itty-router:
|
itty-router:
|
||||||
specifier: ^5.0.24
|
specifier: ^5.0.24
|
||||||
version: 5.0.24
|
version: 5.0.24
|
||||||
@@ -71,8 +169,8 @@ packages:
|
|||||||
resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==}
|
resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==}
|
||||||
engines: {node: '>=6.0.0'}
|
engines: {node: '>=6.0.0'}
|
||||||
|
|
||||||
'@jridgewell/sourcemap-codec@1.5.5':
|
'@jridgewell/sourcemap-codec@1.6.0':
|
||||||
resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==}
|
resolution: {integrity: sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==}
|
||||||
|
|
||||||
'@jridgewell/trace-mapping@0.3.31':
|
'@jridgewell/trace-mapping@0.3.31':
|
||||||
resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==}
|
resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==}
|
||||||
@@ -81,13 +179,6 @@ packages:
|
|||||||
resolution: {integrity: sha512-47XIizs9XZXvuJgoaJUIE2lFoID8ugvc0jzSHP+Ptfk8nTbnR8g788wv48N03Kx0UkAv559HWRQ3yzOgzlRNUA==}
|
resolution: {integrity: sha512-47XIizs9XZXvuJgoaJUIE2lFoID8ugvc0jzSHP+Ptfk8nTbnR8g788wv48N03Kx0UkAv559HWRQ3yzOgzlRNUA==}
|
||||||
engines: {node: '>= 18'}
|
engines: {node: '>= 18'}
|
||||||
|
|
||||||
'@noble/hashes@1.8.0':
|
|
||||||
resolution: {integrity: sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==}
|
|
||||||
engines: {node: ^14.21.3 || >=16}
|
|
||||||
|
|
||||||
'@paralleldrive/cuid2@2.3.1':
|
|
||||||
resolution: {integrity: sha512-XO7cAxhnTZl0Yggq6jOgjiOHhbgcO4NqFqwSmQpjK3b6TEE6Uj/jfSk6wzYyemh3+I0sHirKSetjQwn5cZktFw==}
|
|
||||||
|
|
||||||
'@trivago/prettier-plugin-sort-imports@6.0.2':
|
'@trivago/prettier-plugin-sort-imports@6.0.2':
|
||||||
resolution: {integrity: sha512-3DgfkukFyC/sE/VuYjaUUWoFfuVjPK55vOFDsxD56XXynFMCZDYFogH2l/hDfOsQAm1myoU/1xByJ3tWqtulXA==}
|
resolution: {integrity: sha512-3DgfkukFyC/sE/VuYjaUUWoFfuVjPK55vOFDsxD56XXynFMCZDYFogH2l/hDfOsQAm1myoU/1xByJ3tWqtulXA==}
|
||||||
engines: {node: '>= 20'}
|
engines: {node: '>= 20'}
|
||||||
@@ -107,9 +198,6 @@ packages:
|
|||||||
svelte:
|
svelte:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
asap@2.0.6:
|
|
||||||
resolution: {integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==}
|
|
||||||
|
|
||||||
balanced-match@1.0.2:
|
balanced-match@1.0.2:
|
||||||
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
|
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
|
||||||
|
|
||||||
@@ -125,13 +213,6 @@ packages:
|
|||||||
supports-color:
|
supports-color:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
dezalgo@1.0.4:
|
|
||||||
resolution: {integrity: sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==}
|
|
||||||
|
|
||||||
formidable@3.5.4:
|
|
||||||
resolution: {integrity: sha512-YikH+7CUTOtP44ZTnUhR7Ic2UASBPOqmaRkRKxRbywPTe5VxF7RRCck4af9wutiZ/QKM5nME9Bie2fFaPz5Gug==}
|
|
||||||
engines: {node: '>=14.0.0'}
|
|
||||||
|
|
||||||
itty-router@5.0.24:
|
itty-router@5.0.24:
|
||||||
resolution: {integrity: sha512-PXij1qGKtE6jg3dLgrTRdh/Frovl5Wc4Av67sD2xfhDPrgNt4u79++/il8/c/OfZGkhD/8uYJub8tjo+H165rA==}
|
resolution: {integrity: sha512-PXij1qGKtE6jg3dLgrTRdh/Frovl5Wc4Av67sD2xfhDPrgNt4u79++/il8/c/OfZGkhD/8uYJub8tjo+H165rA==}
|
||||||
|
|
||||||
@@ -156,9 +237,6 @@ packages:
|
|||||||
ms@2.1.3:
|
ms@2.1.3:
|
||||||
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
|
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
|
||||||
|
|
||||||
once@1.4.0:
|
|
||||||
resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
|
|
||||||
|
|
||||||
parse-imports-exports@0.2.4:
|
parse-imports-exports@0.2.4:
|
||||||
resolution: {integrity: sha512-4s6vd6dx1AotCx/RCI2m7t7GCh5bDRUtGNvRfHSP2wbBQdMi67pPe7mtzmgwcaQ8VKK/6IB7Glfyu3qdZJPybQ==}
|
resolution: {integrity: sha512-4s6vd6dx1AotCx/RCI2m7t7GCh5bDRUtGNvRfHSP2wbBQdMi67pPe7mtzmgwcaQ8VKK/6IB7Glfyu3qdZJPybQ==}
|
||||||
|
|
||||||
@@ -173,9 +251,6 @@ packages:
|
|||||||
engines: {node: '>=14'}
|
engines: {node: '>=14'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
wrappy@1.0.2:
|
|
||||||
resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
|
|
||||||
|
|
||||||
snapshots:
|
snapshots:
|
||||||
|
|
||||||
'@babel/code-frame@7.29.7':
|
'@babel/code-frame@7.29.7':
|
||||||
@@ -227,26 +302,20 @@ snapshots:
|
|||||||
|
|
||||||
'@jridgewell/gen-mapping@0.3.13':
|
'@jridgewell/gen-mapping@0.3.13':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@jridgewell/sourcemap-codec': 1.5.5
|
'@jridgewell/sourcemap-codec': 1.6.0
|
||||||
'@jridgewell/trace-mapping': 0.3.31
|
'@jridgewell/trace-mapping': 0.3.31
|
||||||
|
|
||||||
'@jridgewell/resolve-uri@3.1.2': {}
|
'@jridgewell/resolve-uri@3.1.2': {}
|
||||||
|
|
||||||
'@jridgewell/sourcemap-codec@1.5.5': {}
|
'@jridgewell/sourcemap-codec@1.6.0': {}
|
||||||
|
|
||||||
'@jridgewell/trace-mapping@0.3.31':
|
'@jridgewell/trace-mapping@0.3.31':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@jridgewell/resolve-uri': 3.1.2
|
'@jridgewell/resolve-uri': 3.1.2
|
||||||
'@jridgewell/sourcemap-codec': 1.5.5
|
'@jridgewell/sourcemap-codec': 1.6.0
|
||||||
|
|
||||||
'@msgpack/msgpack@3.1.3': {}
|
'@msgpack/msgpack@3.1.3': {}
|
||||||
|
|
||||||
'@noble/hashes@1.8.0': {}
|
|
||||||
|
|
||||||
'@paralleldrive/cuid2@2.3.1':
|
|
||||||
dependencies:
|
|
||||||
'@noble/hashes': 1.8.0
|
|
||||||
|
|
||||||
'@trivago/prettier-plugin-sort-imports@6.0.2(prettier@3.9.6)':
|
'@trivago/prettier-plugin-sort-imports@6.0.2(prettier@3.9.6)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/generator': 7.29.8
|
'@babel/generator': 7.29.8
|
||||||
@@ -261,8 +330,6 @@ snapshots:
|
|||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
asap@2.0.6: {}
|
|
||||||
|
|
||||||
balanced-match@1.0.2: {}
|
balanced-match@1.0.2: {}
|
||||||
|
|
||||||
brace-expansion@2.1.4:
|
brace-expansion@2.1.4:
|
||||||
@@ -273,17 +340,6 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
ms: 2.1.3
|
ms: 2.1.3
|
||||||
|
|
||||||
dezalgo@1.0.4:
|
|
||||||
dependencies:
|
|
||||||
asap: 2.0.6
|
|
||||||
wrappy: 1.0.2
|
|
||||||
|
|
||||||
formidable@3.5.4:
|
|
||||||
dependencies:
|
|
||||||
'@paralleldrive/cuid2': 2.3.1
|
|
||||||
dezalgo: 1.0.4
|
|
||||||
once: 1.4.0
|
|
||||||
|
|
||||||
itty-router@5.0.24: {}
|
itty-router@5.0.24: {}
|
||||||
|
|
||||||
javascript-natural-sort@0.7.1: {}
|
javascript-natural-sort@0.7.1: {}
|
||||||
@@ -300,10 +356,6 @@ snapshots:
|
|||||||
|
|
||||||
ms@2.1.3: {}
|
ms@2.1.3: {}
|
||||||
|
|
||||||
once@1.4.0:
|
|
||||||
dependencies:
|
|
||||||
wrappy: 1.0.2
|
|
||||||
|
|
||||||
parse-imports-exports@0.2.4:
|
parse-imports-exports@0.2.4:
|
||||||
dependencies:
|
dependencies:
|
||||||
parse-statements: 1.0.11
|
parse-statements: 1.0.11
|
||||||
@@ -313,5 +365,3 @@ snapshots:
|
|||||||
picocolors@1.1.1: {}
|
picocolors@1.1.1: {}
|
||||||
|
|
||||||
prettier@3.9.6: {}
|
prettier@3.9.6: {}
|
||||||
|
|
||||||
wrappy@1.0.2: {}
|
|
||||||
|
|||||||
+82
-11
@@ -65,7 +65,16 @@ let settings = {
|
|||||||
modbus_max_clients: 10,
|
modbus_max_clients: 10,
|
||||||
modbus_timeout: 10000,
|
modbus_timeout: 10000,
|
||||||
developer_mode: true,
|
developer_mode: true,
|
||||||
disable_reset: false
|
disable_reset: false,
|
||||||
|
email_enabled: false,
|
||||||
|
email_security: 2,
|
||||||
|
email_server: 'smtp.gmail.com',
|
||||||
|
email_port: 587,
|
||||||
|
email_login: 'test@emsesp.org',
|
||||||
|
email_pass: 'password',
|
||||||
|
email_sender: 'test@emsesp.org',
|
||||||
|
email_recp: 'test@emsesp.org',
|
||||||
|
email_subject: 'ems-esp notification'
|
||||||
};
|
};
|
||||||
|
|
||||||
// EMS-ESP System Settings
|
// EMS-ESP System Settings
|
||||||
@@ -86,9 +95,9 @@ let system_status = {
|
|||||||
// network_status: 10, // ethernet connected
|
// network_status: 10, // ethernet connected
|
||||||
// network_status: 6, // wifi disconnected
|
// network_status: 6, // wifi disconnected
|
||||||
wifi_rssi: -41,
|
wifi_rssi: -41,
|
||||||
esp_platform: 'ESP32S3',
|
esp_platform: 'ESP32',
|
||||||
build_flags: 'DEMO',
|
build_flags: 'DEMO',
|
||||||
cpu_type: 'ESP32-S3',
|
cpu_type: 'ESP32',
|
||||||
cpu_rev: 0,
|
cpu_rev: 0,
|
||||||
cpu_cores: 2,
|
cpu_cores: 2,
|
||||||
cpu_freq_mhz: 240,
|
cpu_freq_mhz: 240,
|
||||||
@@ -193,8 +202,8 @@ system_status.partitions[0].version = THIS_VERSION; // app0
|
|||||||
|
|
||||||
// set the ESP platform - using ESP32 will disable OTA and automatic version downloading
|
// set the ESP platform - using ESP32 will disable OTA and automatic version downloading
|
||||||
let emulate_esp: string;
|
let emulate_esp: string;
|
||||||
emulate_esp = 'ESP32S3';
|
// emulate_esp = 'ESP32S3';
|
||||||
// emulate_esp = 'ESP32';
|
emulate_esp = 'ESP32';
|
||||||
|
|
||||||
switch (emulate_esp) {
|
switch (emulate_esp) {
|
||||||
// ESP32 4MB
|
// ESP32 4MB
|
||||||
@@ -230,6 +239,17 @@ switch (emulate_esp) {
|
|||||||
// GLOBAL VARIABLES
|
// GLOBAL VARIABLES
|
||||||
let countWifiScanPoll = 0; // wifi network scan
|
let countWifiScanPoll = 0; // wifi network scan
|
||||||
let countHardwarePoll = 0; // for during an upload
|
let countHardwarePoll = 0; // for during an upload
|
||||||
|
let pendingFirmwareMd5 = false; // set by a .md5 upload, cleared by the next .bin
|
||||||
|
|
||||||
|
const VALID_UPLOAD_EXTENSIONS = new Set(['bin', 'json', 'md5']);
|
||||||
|
|
||||||
|
function parseMd5Digest(text: string): string | null {
|
||||||
|
const token = text.trim().split(/\s+/)[0];
|
||||||
|
if (/^[0-9a-fA-F]{32}$/.test(token)) {
|
||||||
|
return token.toLowerCase();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
// DeviceTypes
|
// DeviceTypes
|
||||||
const enum ScheduleFlag {
|
const enum ScheduleFlag {
|
||||||
@@ -425,16 +445,20 @@ function upgradeImportantMessages(version: string) {
|
|||||||
// 0 is do nothing
|
// 0 is do nothing
|
||||||
// 1 means 3.9 and factory reset required
|
// 1 means 3.9 and factory reset required
|
||||||
// 2 means a major version upgrade
|
// 2 means a major version upgrade
|
||||||
|
|
||||||
let upgradeImportantMessageType_n = 0;
|
let upgradeImportantMessageType_n = 0;
|
||||||
|
|
||||||
// see if its a filename with a .bin extension
|
// check file extensions
|
||||||
if (version.endsWith('.bin')) {
|
if (version.endsWith('.md5') || version.endsWith('.json')) {
|
||||||
upgradeImportantMessageType_n = 1; // make it 1, for testing, meaning factory reset required
|
upgradeImportantMessageType_n = 0; // digest / backup restore: no upgrade warning
|
||||||
} else if (version.endsWith('.md')) {
|
} else if (version.endsWith('.bin')) {
|
||||||
upgradeImportantMessageType_n = 0; // use default 0, no message
|
// extract the version number from the filename and if its going from 3.8.x to 3.9.x, then set upgradeImportantMessageType_n to 1
|
||||||
|
const versionNumber = version.split('.');
|
||||||
|
if (versionNumber[0] === '3' && versionNumber[1] === '8') {
|
||||||
|
upgradeImportantMessageType_n = 1; // make it 1, factory reset required
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// this is a version string like "3.9.0"
|
// this is a version string like "3.9.0"
|
||||||
// upgradeImportantMessageType_n = 2; // make it 2, for testing, meaning a major version upgrade
|
|
||||||
upgradeImportantMessageType_n = 1; // make it 1, for testing, meaning a factory reset is required
|
upgradeImportantMessageType_n = 1; // make it 1, for testing, meaning a factory reset is required
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -450,6 +474,8 @@ function upgradeImportantMessages(version: string) {
|
|||||||
// called by Action endpoint getVersions
|
// called by Action endpoint getVersions
|
||||||
// Set MOCK_OFFLINE = true to simulate a device with no internet (omits stable/dev).
|
// Set MOCK_OFFLINE = true to simulate a device with no internet (omits stable/dev).
|
||||||
const MOCK_OFFLINE = false;
|
const MOCK_OFFLINE = false;
|
||||||
|
// const MOCK_OFFLINE = true;
|
||||||
|
|
||||||
function get_versions() {
|
function get_versions() {
|
||||||
const isDev = THIS_VERSION.includes('dev');
|
const isDev = THIS_VERSION.includes('dev');
|
||||||
const currentUpgradeable =
|
const currentUpgradeable =
|
||||||
@@ -457,6 +483,7 @@ function get_versions() {
|
|||||||
(isDev ? DEV_VERSION_IS_UPGRADEABLE : STABLE_VERSION_IS_UPGRADEABLE);
|
(isDev ? DEV_VERSION_IS_UPGRADEABLE : STABLE_VERSION_IS_UPGRADEABLE);
|
||||||
|
|
||||||
const data: {
|
const data: {
|
||||||
|
system_name: string;
|
||||||
current: {
|
current: {
|
||||||
version: string;
|
version: string;
|
||||||
type: 'stable' | 'dev';
|
type: 'stable' | 'dev';
|
||||||
@@ -466,6 +493,7 @@ function get_versions() {
|
|||||||
stable?: { version: string; date: string; upgradeable: boolean };
|
stable?: { version: string; date: string; upgradeable: boolean };
|
||||||
dev?: { version: string; date: string; upgradeable: boolean };
|
dev?: { version: string; date: string; upgradeable: boolean };
|
||||||
} = {
|
} = {
|
||||||
|
system_name: settings.system_name,
|
||||||
current: {
|
current: {
|
||||||
version: THIS_VERSION,
|
version: THIS_VERSION,
|
||||||
type: isDev ? 'dev' : 'stable',
|
type: isDev ? 'dev' : 'stable',
|
||||||
@@ -569,6 +597,7 @@ let network_settings = {
|
|||||||
enableMDNS: true,
|
enableMDNS: true,
|
||||||
enableCORS: false,
|
enableCORS: false,
|
||||||
CORSOrigin: '*',
|
CORSOrigin: '*',
|
||||||
|
eth_10mbit: false,
|
||||||
local_ip: '',
|
local_ip: '',
|
||||||
gateway_ip: '',
|
gateway_ip: '',
|
||||||
subnet_mask: '',
|
subnet_mask: '',
|
||||||
@@ -760,6 +789,7 @@ const EMSESP_COMMANDS_ENDPOINT = REST_ENDPOINT_ROOT + 'commands';
|
|||||||
const EMSESP_CUSTOMENTITIES_ENDPOINT = REST_ENDPOINT_ROOT + 'customEntities';
|
const EMSESP_CUSTOMENTITIES_ENDPOINT = REST_ENDPOINT_ROOT + 'customEntities';
|
||||||
const EMSESP_MODULES_ENDPOINT = REST_ENDPOINT_ROOT + 'modules';
|
const EMSESP_MODULES_ENDPOINT = REST_ENDPOINT_ROOT + 'modules';
|
||||||
const EMSESP_ACTION_ENDPOINT = REST_ENDPOINT_ROOT + 'action';
|
const EMSESP_ACTION_ENDPOINT = REST_ENDPOINT_ROOT + 'action';
|
||||||
|
const EMSESP_UPLOAD_FILE_ENDPOINT = REST_ENDPOINT_ROOT + 'uploadFile';
|
||||||
|
|
||||||
// these are used in the API calls only
|
// these are used in the API calls only
|
||||||
const EMSESP_SYSTEM_INFO_ENDPOINT = API_ENDPOINT_ROOT + 'system/info';
|
const EMSESP_SYSTEM_INFO_ENDPOINT = API_ENDPOINT_ROOT + 'system/info';
|
||||||
@@ -4623,6 +4653,47 @@ router
|
|||||||
|
|
||||||
// SYSTEM and SETTINGS
|
// SYSTEM and SETTINGS
|
||||||
router
|
router
|
||||||
|
.post(EMSESP_UPLOAD_FILE_ENDPOINT, async (request: Request) => {
|
||||||
|
const formData = await request.formData();
|
||||||
|
const uploaded = formData.get('file');
|
||||||
|
if (!uploaded || typeof uploaded === 'string') {
|
||||||
|
return status(400);
|
||||||
|
}
|
||||||
|
|
||||||
|
const fileName = uploaded.name || '';
|
||||||
|
const fileExtension = fileName.includes('.')
|
||||||
|
? fileName.substring(fileName.lastIndexOf('.') + 1).toLowerCase()
|
||||||
|
: '';
|
||||||
|
console.log(
|
||||||
|
`File uploaded: ${fileName} (${fileExtension}, ${uploaded.size} bytes)`
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!VALID_UPLOAD_EXTENSIONS.has(fileExtension)) {
|
||||||
|
console.log('Invalid file extension');
|
||||||
|
return status(406);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fileExtension === 'md5') {
|
||||||
|
const digest = parseMd5Digest(await uploaded.text());
|
||||||
|
if (!digest) {
|
||||||
|
console.log('Invalid MD5 digest file');
|
||||||
|
return status(406);
|
||||||
|
}
|
||||||
|
pendingFirmwareMd5 = true;
|
||||||
|
console.log('MD5 digest received', digest);
|
||||||
|
return { md5: digest };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fileExtension === 'bin' && pendingFirmwareMd5) {
|
||||||
|
pendingFirmwareMd5 = false;
|
||||||
|
console.log('Firmware MD5 matches');
|
||||||
|
return { md5_ok: true };
|
||||||
|
}
|
||||||
|
|
||||||
|
pendingFirmwareMd5 = false;
|
||||||
|
console.log('File uploaded successfully!');
|
||||||
|
return status(200);
|
||||||
|
})
|
||||||
.get(ACTIVITY_ENDPOINT, () => activity)
|
.get(ACTIVITY_ENDPOINT, () => activity)
|
||||||
.get(SYSTEM_STATUS_ENDPOINT, async () => {
|
.get(SYSTEM_STATUS_ENDPOINT, async () => {
|
||||||
if (countHardwarePoll >= 2) {
|
if (countHardwarePoll >= 2) {
|
||||||
|
|||||||
+2
-1
@@ -196,9 +196,10 @@ lib_ldf_mode = off
|
|||||||
lib_deps =
|
lib_deps =
|
||||||
|
|
||||||
; unit tests
|
; unit tests
|
||||||
; The test code is in ./test/test_api.cpp and the test_api.h file is created by the native-test-create environment.
|
; The test code is in ./test/test_api.cpp and the test_api.h file is generated by the native-test-create environment.
|
||||||
; to run use `platformio run -e native-test -t exec`. All tests should PASS.
|
; to run use `platformio run -e native-test -t exec`. All tests should PASS.
|
||||||
; tip: use https://jsondiff.com/ to compare the expected and actual responses.
|
; tip: use https://jsondiff.com/ to compare the expected and actual responses.
|
||||||
|
; to update the test output run `python3 scripts/generate_test_api.py`
|
||||||
[env:native-test]
|
[env:native-test]
|
||||||
platform = native
|
platform = native
|
||||||
test_build_src = true
|
test_build_src = true
|
||||||
|
|||||||
+7
-1
@@ -744,7 +744,7 @@ mlongcalls
|
|||||||
mmio
|
mmio
|
||||||
mmode
|
mmode
|
||||||
mmplus
|
mmplus
|
||||||
mockserver
|
mockServer
|
||||||
modbus
|
modbus
|
||||||
modee
|
modee
|
||||||
modetype
|
modetype
|
||||||
@@ -1344,3 +1344,9 @@ IILE
|
|||||||
Sumr
|
Sumr
|
||||||
eraseap
|
eraseap
|
||||||
roomtempoffset
|
roomtempoffset
|
||||||
|
dhcpc
|
||||||
|
wifioff
|
||||||
|
autonegotiation
|
||||||
|
Mbit
|
||||||
|
WROVER
|
||||||
|
OLIMEXPOEW
|
||||||
@@ -7,7 +7,7 @@ Workflow:
|
|||||||
3. Write that block to test/test_api/test_api.h.
|
3. Write that block to test/test_api/test_api.h.
|
||||||
4. Run `pio run -e native-test -t exec`.
|
4. Run `pio run -e native-test -t exec`.
|
||||||
|
|
||||||
run with `python3 scripts/generate_test_api.py`
|
run with `python3 ./scripts/generate_test_api.py`
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import subprocess
|
import subprocess
|
||||||
|
|||||||
@@ -31,7 +31,8 @@ StateUpdateResult APSettings::update(JsonObject root, APSettings & settings) {
|
|||||||
APSettings newSettings{};
|
APSettings newSettings{};
|
||||||
newSettings.provisionMode = static_cast<uint8_t>(root["provision_mode"] | FACTORY_AP_PROVISION_MODE);
|
newSettings.provisionMode = static_cast<uint8_t>(root["provision_mode"] | FACTORY_AP_PROVISION_MODE);
|
||||||
|
|
||||||
switch (settings.provisionMode) {
|
// coerce removed/invalid modes (e.g. legacy AP_MODE_ALWAYS = 0) to the current default
|
||||||
|
switch (newSettings.provisionMode) {
|
||||||
case AP_MODE_DISCONNECTED:
|
case AP_MODE_DISCONNECTED:
|
||||||
case AP_MODE_NEVER:
|
case AP_MODE_NEVER:
|
||||||
break;
|
break;
|
||||||
@@ -53,11 +54,17 @@ StateUpdateResult APSettings::update(JsonObject root, APSettings & settings) {
|
|||||||
return StateUpdateResult::UNCHANGED;
|
return StateUpdateResult::UNCHANGED;
|
||||||
}
|
}
|
||||||
|
|
||||||
settings = newSettings;
|
const uint8_t previousProvisionMode = settings.provisionMode;
|
||||||
|
settings = newSettings;
|
||||||
|
|
||||||
// if the AP mode has changed, force a disconnect and reconnect
|
// FS load at boot uses this updater before Network::begin(); skip reconnect on that first apply.
|
||||||
if (settings.provisionMode != newSettings.provisionMode) {
|
// Subsequent provision-mode changes (UI/API) must reload Network's cached AP settings.
|
||||||
emsesp::EMSESP::network_.reconnect();
|
static bool appliedOnce = false;
|
||||||
|
const bool modeChanged = previousProvisionMode != settings.provisionMode;
|
||||||
|
if (appliedOnce && modeChanged) {
|
||||||
|
emsesp::EMSESP::network_.schedule_reconnect();
|
||||||
}
|
}
|
||||||
|
appliedOnce = true;
|
||||||
|
|
||||||
return StateUpdateResult::CHANGED;
|
return StateUpdateResult::CHANGED;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ enum APNetworkStatus { ACTIVE = 0, INACTIVE };
|
|||||||
|
|
||||||
class APSettings {
|
class APSettings {
|
||||||
public:
|
public:
|
||||||
uint8_t provisionMode = FACTORY_AP_PROVISION_MODE; // 0 = on, 2 = off
|
uint8_t provisionMode = FACTORY_AP_PROVISION_MODE; // 1 = on disconnect, 2 = off
|
||||||
String ssid = FACTORY_AP_SSID;
|
String ssid = FACTORY_AP_SSID;
|
||||||
String password = FACTORY_AP_PASSWORD;
|
String password = FACTORY_AP_PASSWORD;
|
||||||
uint8_t channel = FACTORY_AP_CHANNEL;
|
uint8_t channel = FACTORY_AP_CHANNEL;
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ MqttSettingsService::MqttSettingsService(AsyncWebServer * server, FS * fs, Secur
|
|||||||
, _fsPersistence(MqttSettings::read, MqttSettings::update, this, fs, MQTT_SETTINGS_FILE)
|
, _fsPersistence(MqttSettings::read, MqttSettings::update, this, fs, MQTT_SETTINGS_FILE)
|
||||||
, _reconfigureMqtt(false)
|
, _reconfigureMqtt(false)
|
||||||
, _disconnectedAt(0)
|
, _disconnectedAt(0)
|
||||||
, _disconnectReason(espMqttClientTypes::DisconnectReason::TCP_DISCONNECTED)
|
, _disconnectReason(espMqttClientTypes::DisconnectReason::USER_OK)
|
||||||
, _mqttClient(nullptr) {
|
, _mqttClient(nullptr) {
|
||||||
addUpdateHandler([this] { onConfigUpdated(); }, false);
|
addUpdateHandler([this] { onConfigUpdated(); }, false);
|
||||||
}
|
}
|
||||||
@@ -41,6 +41,7 @@ void MqttSettingsService::startClient() {
|
|||||||
delete _mqttClient;
|
delete _mqttClient;
|
||||||
_mqttClient = nullptr;
|
_mqttClient = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_state.enableTLS) {
|
if (_state.enableTLS) {
|
||||||
isSecure = true;
|
isSecure = true;
|
||||||
if (emsesp::EMSESP::system_.PSram() == 0) {
|
if (emsesp::EMSESP::system_.PSram() == 0) {
|
||||||
@@ -82,7 +83,10 @@ void MqttSettingsService::loop() {
|
|||||||
_mqttClient->disconnect(true);
|
_mqttClient->disconnect(true);
|
||||||
}
|
}
|
||||||
if (_reconfigureMqtt || (_disconnectedAt && static_cast<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
|
// configure MQTT client
|
||||||
|
if (_reconfigureMqtt && _state.enabled) {
|
||||||
|
emsesp::EMSESP::logger().info("Starting MQTT service");
|
||||||
|
}
|
||||||
_disconnectedAt = configureMqtt() ? 0 : uuid::get_uptime();
|
_disconnectedAt = configureMqtt() ? 0 : uuid::get_uptime();
|
||||||
_reconfigureMqtt = false;
|
_reconfigureMqtt = false;
|
||||||
}
|
}
|
||||||
@@ -126,14 +130,18 @@ MqttClient * MqttSettingsService::getMqttClient() {
|
|||||||
void MqttSettingsService::onMqttConnect(bool sessionPresent) {
|
void MqttSettingsService::onMqttConnect(bool sessionPresent) {
|
||||||
(void)sessionPresent;
|
(void)sessionPresent;
|
||||||
emsesp::EMSESP::mqtt_.on_connect();
|
emsesp::EMSESP::mqtt_.on_connect();
|
||||||
|
_disconnectReason = espMqttClientTypes::DisconnectReason::USER_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MqttSettingsService::onMqttDisconnect(espMqttClientTypes::DisconnectReason reason) {
|
void MqttSettingsService::onMqttDisconnect(espMqttClientTypes::DisconnectReason reason) {
|
||||||
_disconnectReason = reason;
|
if (reason != _disconnectReason) {
|
||||||
|
emsesp::EMSESP::mqtt_.on_disconnect(reason);
|
||||||
|
_disconnectReason = reason;
|
||||||
|
}
|
||||||
|
|
||||||
if (!_disconnectedAt) {
|
if (!_disconnectedAt) {
|
||||||
_disconnectedAt = uuid::get_uptime();
|
_disconnectedAt = uuid::get_uptime();
|
||||||
}
|
}
|
||||||
emsesp::EMSESP::mqtt_.on_disconnect(reason);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MqttSettingsService::onConfigUpdated() {
|
void MqttSettingsService::onConfigUpdated() {
|
||||||
@@ -147,8 +155,13 @@ void MqttSettingsService::onConfigUpdated() {
|
|||||||
bool MqttSettingsService::configureMqtt() {
|
bool MqttSettingsService::configureMqtt() {
|
||||||
// disconnect if already connected
|
// disconnect if already connected
|
||||||
if (_mqttClient->connected()) {
|
if (_mqttClient->connected()) {
|
||||||
// emsesp::EMSESP::logger().info("Disconnecting to configure");
|
#if defined(EMSESP_DEBUG)
|
||||||
|
emsesp::EMSESP::logger().debug("Disconnecting to configure");
|
||||||
|
#endif
|
||||||
_mqttClient->disconnect(true);
|
_mqttClient->disconnect(true);
|
||||||
|
if (!_state.enabled) {
|
||||||
|
emsesp::EMSESP::logger().info("Stopping MQTT client");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// only connect if WiFi is connected and MQTT is enabled
|
// only connect if WiFi is connected and MQTT is enabled
|
||||||
@@ -184,7 +197,7 @@ bool MqttSettingsService::configureMqtt() {
|
|||||||
static_cast<espMqttClientSecure *>(_mqttClient)->setKeepAlive(_state.keepAlive);
|
static_cast<espMqttClientSecure *>(_mqttClient)->setKeepAlive(_state.keepAlive);
|
||||||
static_cast<espMqttClientSecure *>(_mqttClient)->setCleanSession(_state.cleanSession);
|
static_cast<espMqttClientSecure *>(_mqttClient)->setCleanSession(_state.cleanSession);
|
||||||
static_cast<espMqttClientSecure *>(_mqttClient)->setWill(will_topic, 1, true, "offline"); // QOS 1, retain
|
static_cast<espMqttClientSecure *>(_mqttClient)->setWill(will_topic, 1, true, "offline"); // QOS 1, retain
|
||||||
return _mqttClient->connect();
|
return (_mqttClient->connect());
|
||||||
}
|
}
|
||||||
static_cast<espMqttClient *>(_mqttClient)->setServer(_state.host.c_str(), _state.port);
|
static_cast<espMqttClient *>(_mqttClient)->setServer(_state.host.c_str(), _state.port);
|
||||||
if (_state.username.length() > 0) {
|
if (_state.username.length() > 0) {
|
||||||
@@ -194,7 +207,7 @@ bool MqttSettingsService::configureMqtt() {
|
|||||||
static_cast<espMqttClient *>(_mqttClient)->setKeepAlive(_state.keepAlive);
|
static_cast<espMqttClient *>(_mqttClient)->setKeepAlive(_state.keepAlive);
|
||||||
static_cast<espMqttClient *>(_mqttClient)->setCleanSession(_state.cleanSession);
|
static_cast<espMqttClient *>(_mqttClient)->setCleanSession(_state.cleanSession);
|
||||||
static_cast<espMqttClient *>(_mqttClient)->setWill(will_topic, 1, true, "offline"); // QOS 1, retain
|
static_cast<espMqttClient *>(_mqttClient)->setWill(will_topic, 1, true, "offline"); // QOS 1, retain
|
||||||
return _mqttClient->connect();
|
return (_mqttClient->connect());
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -77,13 +77,15 @@ void NTPSettings::read(NTPSettings & settings, JsonObject root) {
|
|||||||
root["enabled"] = settings.enabled;
|
root["enabled"] = settings.enabled;
|
||||||
root["server"] = settings.server;
|
root["server"] = settings.server;
|
||||||
root["tz_label"] = settings.tzLabel;
|
root["tz_label"] = settings.tzLabel;
|
||||||
root["tz_format"] = settings.tzFormat;
|
root["tz_format"] = settings.tzFormat.substring(0, settings.tzFormat.indexOf(" "));
|
||||||
root["thermostat_sync"] = settings.thermostat_sync;
|
root["thermostat_sync"] = settings.thermostat_sync;
|
||||||
root["tz_label_t"] = settings.tzLabelT;
|
root["tz_label_t"] = settings.tzLabelT;
|
||||||
root["tz_format_t"] = settings.tzFormatT;
|
root["tz_format_t"] = settings.tzFormatT.substring(0, settings.tzFormatT.indexOf(" "));
|
||||||
}
|
}
|
||||||
|
|
||||||
StateUpdateResult NTPSettings::update(JsonObject root, NTPSettings & settings) {
|
StateUpdateResult NTPSettings::update(JsonObject root, NTPSettings & settings) {
|
||||||
|
settings.tzFormat.reserve(40);
|
||||||
|
settings.tzFormatT.reserve(40);
|
||||||
settings.enabled = root["enabled"] | FACTORY_NTP_ENABLED;
|
settings.enabled = root["enabled"] | FACTORY_NTP_ENABLED;
|
||||||
settings.server = root["server"] | FACTORY_NTP_SERVER;
|
settings.server = root["server"] | FACTORY_NTP_SERVER;
|
||||||
settings.tzLabel = root["tz_label"] | FACTORY_NTP_TIME_ZONE_LABEL;
|
settings.tzLabel = root["tz_label"] | FACTORY_NTP_TIME_ZONE_LABEL;
|
||||||
@@ -91,5 +93,12 @@ StateUpdateResult NTPSettings::update(JsonObject root, NTPSettings & settings) {
|
|||||||
settings.thermostat_sync = root["thermostat_sync"] | FACTORY_NTP_THERMOSTAT_SYNC;
|
settings.thermostat_sync = root["thermostat_sync"] | FACTORY_NTP_THERMOSTAT_SYNC;
|
||||||
settings.tzLabelT = root["tz_label_t"] | FACTORY_NTP_TIME_ZONE_LABEL;
|
settings.tzLabelT = root["tz_label_t"] | FACTORY_NTP_TIME_ZONE_LABEL;
|
||||||
settings.tzFormatT = root["tz_format_t"] | FACTORY_NTP_TIME_ZONE_FORMAT;
|
settings.tzFormatT = root["tz_format_t"] | FACTORY_NTP_TIME_ZONE_FORMAT;
|
||||||
|
// The format string must have same length to avoid memory leak, #3184
|
||||||
|
while (settings.tzFormat.length() < 40) {
|
||||||
|
settings.tzFormat += " ";
|
||||||
|
}
|
||||||
|
while (settings.tzFormatT.length() < 40) {
|
||||||
|
settings.tzFormatT += " ";
|
||||||
|
}
|
||||||
return StateUpdateResult::CHANGED;
|
return StateUpdateResult::CHANGED;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ void NetworkSettings::read(NetworkSettings & settings, JsonObject root) {
|
|||||||
root["enableCORS"] = settings.enableCORS;
|
root["enableCORS"] = settings.enableCORS;
|
||||||
root["CORSOrigin"] = settings.CORSOrigin;
|
root["CORSOrigin"] = settings.CORSOrigin;
|
||||||
root["tx_power"] = settings.tx_power;
|
root["tx_power"] = settings.tx_power;
|
||||||
|
root["eth_10mbit"] = settings.eth_10mbit;
|
||||||
|
|
||||||
// extended settings
|
// extended settings
|
||||||
JsonUtils::writeIP(root, "local_ip", settings.localIP);
|
JsonUtils::writeIP(root, "local_ip", settings.localIP);
|
||||||
@@ -51,6 +52,7 @@ StateUpdateResult NetworkSettings::update(JsonObject root, NetworkSettings & set
|
|||||||
settings.enableMDNS = root["enableMDNS"] | true;
|
settings.enableMDNS = root["enableMDNS"] | true;
|
||||||
settings.enableCORS = root["enableCORS"];
|
settings.enableCORS = root["enableCORS"];
|
||||||
settings.CORSOrigin = root["CORSOrigin"] | "*";
|
settings.CORSOrigin = root["CORSOrigin"] | "*";
|
||||||
|
settings.eth_10mbit = root["eth_10mbit"];
|
||||||
|
|
||||||
// extended settings
|
// extended settings
|
||||||
JsonUtils::readIP(root, "local_ip", settings.localIP);
|
JsonUtils::readIP(root, "local_ip", settings.localIP);
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ class NetworkSettings {
|
|||||||
bool enableMDNS = true;
|
bool enableMDNS = true;
|
||||||
bool enableCORS = false;
|
bool enableCORS = false;
|
||||||
String CORSOrigin = "*";
|
String CORSOrigin = "*";
|
||||||
|
bool eth_10mbit = false; // force 10BASE-T half-duplex, no auto-negotiation
|
||||||
|
|
||||||
// optional configuration for static IP address
|
// optional configuration for static IP address
|
||||||
IPAddress localIP;
|
IPAddress localIP;
|
||||||
|
|||||||
@@ -13,10 +13,32 @@ static String getFilenameExtension(const String & filename) {
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Accepts a raw 32-char hex digest, optionally with trailing newline or a GNU "hash filename" suffix.
|
||||||
|
static bool parseMd5Digest(const uint8_t * data, size_t len, std::array<char, 33> & out) {
|
||||||
|
size_t i = 0;
|
||||||
|
while (i < len && (data[i] == ' ' || data[i] == '\t' || data[i] == '\r' || data[i] == '\n')) {
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
if (len - i < 32) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (size_t n = 0; n < 32; n++) {
|
||||||
|
const char c = static_cast<char>(data[i + n]);
|
||||||
|
const bool hex = (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F');
|
||||||
|
if (!hex) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
out[n] = (c >= 'A' && c <= 'F') ? static_cast<char>(c - 'A' + 'a') : c;
|
||||||
|
}
|
||||||
|
out[32] = '\0';
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
UploadFileService::UploadFileService(AsyncWebServer * server, SecurityManager * securityManager)
|
UploadFileService::UploadFileService(AsyncWebServer * server, SecurityManager * securityManager)
|
||||||
: _securityManager(securityManager)
|
: _securityManager(securityManager)
|
||||||
, _is_firmware(false)
|
, _is_firmware(false)
|
||||||
, _is_filesystem(false)
|
, _is_filesystem(false)
|
||||||
|
, _md5_applied(false)
|
||||||
, _md5() {
|
, _md5() {
|
||||||
server->on(
|
server->on(
|
||||||
UPLOAD_FILE_PATH,
|
UPLOAD_FILE_PATH,
|
||||||
@@ -48,18 +70,23 @@ void UploadFileService::handleUpload(AsyncWebServerRequest * request, const Stri
|
|||||||
// LittleFS filesystem image
|
// LittleFS filesystem image
|
||||||
_is_filesystem = true;
|
_is_filesystem = true;
|
||||||
_md5[0] = '\0'; // clear any stale md5 so Update.end() doesn't compare against it
|
_md5[0] = '\0'; // clear any stale md5 so Update.end() doesn't compare against it
|
||||||
|
_md5_applied = false;
|
||||||
} else if ((extension == "bin") && (filesize > 1000000)) {
|
} else if ((extension == "bin") && (filesize > 1000000)) {
|
||||||
_is_firmware = true;
|
_is_firmware = true;
|
||||||
} else if (extension == "json") {
|
} else if (extension == "json") {
|
||||||
_md5[0] = '\0'; // clear md5
|
_md5[0] = '\0'; // clear md5
|
||||||
|
_md5_applied = false;
|
||||||
} else if (extension == "md5") {
|
} else if (extension == "md5") {
|
||||||
if (len == _md5.size() - 1) {
|
if (!parseMd5Digest(data, len, _md5)) {
|
||||||
std::memcpy(_md5.data(), data, _md5.size() - 1);
|
emsesp::EMSESP::logger().err("Invalid MD5 digest file");
|
||||||
_md5.back() = '\0';
|
handleError(request, 406); // Not Acceptable
|
||||||
|
} else {
|
||||||
|
emsesp::EMSESP::logger().info("MD5 digest received (%s). Now upload the firmware BIN", _md5.data());
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
_md5.front() = '\0';
|
_md5.front() = '\0';
|
||||||
|
_md5_applied = false;
|
||||||
emsesp::EMSESP::logger().err("Unsupported file type: %s, size: %u", filename.c_str(), filesize);
|
emsesp::EMSESP::logger().err("Unsupported file type: %s, size: %u", filename.c_str(), filesize);
|
||||||
handleError(request, 406); // Not Acceptable - unsupported file type
|
handleError(request, 406); // Not Acceptable - unsupported file type
|
||||||
return;
|
return;
|
||||||
@@ -94,15 +121,20 @@ void UploadFileService::handleUpload(AsyncWebServerRequest * request, const Stri
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
// it's firmware - initialize the ArduinoOTA updater
|
// it's firmware - initialize the ArduinoOTA updater
|
||||||
emsesp::EMSESP::logger().info("Uploading firmware file %s (size: %d KB). Please wait...", filename.c_str(), filesize / 1024);
|
emsesp::EMSESP::logger().info("Firmware uploading (file %s, size: %dKB). Please wait...", filename.c_str(), filesize / 1024);
|
||||||
|
|
||||||
// turn off UART to prevent interference with the upload
|
// turn off UART to prevent interference with the upload
|
||||||
emsesp::EMSuart::stop();
|
emsesp::EMSuart::stop();
|
||||||
|
|
||||||
|
// tell main loop we're uploading so services as paused (e.g. MQTT)
|
||||||
|
emsesp::EMSESP::system_.systemStatus(emsesp::SYSTEM_STATUS::SYSTEM_STATUS_UPLOADING);
|
||||||
|
|
||||||
if (Update.begin(filesize - sizeof(esp_image_header_t))) {
|
if (Update.begin(filesize - sizeof(esp_image_header_t))) {
|
||||||
|
_md5_applied = false;
|
||||||
if (strlen(_md5.data()) == _md5.size() - 1) {
|
if (strlen(_md5.data()) == _md5.size() - 1) {
|
||||||
Update.setMD5(_md5.data());
|
Update.setMD5(_md5.data());
|
||||||
_md5.front() = '\0';
|
_md5_applied = true;
|
||||||
|
emsesp::EMSESP::logger().info("Firmware MD5 check enabled (%s)", _md5.data());
|
||||||
}
|
}
|
||||||
request->onDisconnect([this] { handleDisconnect(); }); // success, let's make sure we end the update if the client hangs up
|
request->onDisconnect([this] { handleDisconnect(); }); // success, let's make sure we end the update if the client hangs up
|
||||||
} else {
|
} else {
|
||||||
@@ -111,7 +143,7 @@ void UploadFileService::handleUpload(AsyncWebServerRequest * request, const Stri
|
|||||||
}
|
}
|
||||||
} else if (_is_filesystem) {
|
} else if (_is_filesystem) {
|
||||||
// LittleFS filesystem image - flash directly to the spiffs/littlefs partition
|
// LittleFS filesystem image - flash directly to the spiffs/littlefs partition
|
||||||
emsesp::EMSESP::logger().info("Uploading filesystem image %s (size: %u KB). Please wait...", filename.c_str(), static_cast<unsigned>(filesize / 1024));
|
emsesp::EMSESP::logger().info("Uploading filesystem file %s (size: %uKB). Please wait...", filename.c_str(), static_cast<unsigned>(filesize / 1024));
|
||||||
emsesp::EMSuart::stop();
|
emsesp::EMSuart::stop();
|
||||||
LittleFS.end(); // unmount LittleFS before we overwrite the partition under it
|
LittleFS.end(); // unmount LittleFS before we overwrite the partition under it
|
||||||
|
|
||||||
@@ -180,8 +212,19 @@ void UploadFileService::uploadComplete(AsyncWebServerRequest * request) {
|
|||||||
emsesp::EMSESP::nvs_.putBool(emsesp::EMSESP_NVS_BOOT_NEW_FIRMWARE, true);
|
emsesp::EMSESP::nvs_.putBool(emsesp::EMSESP_NVS_BOOT_NEW_FIRMWARE, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
AsyncWebServerResponse * response = request->beginResponse(200);
|
if (_is_firmware && _md5_applied) {
|
||||||
request->send(response);
|
emsesp::EMSESP::logger().info("Firmware MD5 matches");
|
||||||
|
auto * response = new emsesp::PsramAsyncJsonResponse(false);
|
||||||
|
JsonObject root = response->getRoot();
|
||||||
|
root["md5_ok"] = true;
|
||||||
|
response->setLength();
|
||||||
|
request->send(response);
|
||||||
|
_md5.front() = '\0';
|
||||||
|
_md5_applied = false;
|
||||||
|
} else {
|
||||||
|
AsyncWebServerResponse * response = request->beginResponse(200);
|
||||||
|
request->send(response);
|
||||||
|
}
|
||||||
emsesp::EMSESP::system_.systemStatus(
|
emsesp::EMSESP::system_.systemStatus(
|
||||||
emsesp::SYSTEM_STATUS::SYSTEM_STATUS_PENDING_RESTART); // will be handled by the main loop. We use pending for the Web's SystemMonitor
|
emsesp::SYSTEM_STATUS::SYSTEM_STATUS_PENDING_RESTART); // will be handled by the main loop. We use pending for the Web's SystemMonitor
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ class UploadFileService {
|
|||||||
SecurityManager * _securityManager;
|
SecurityManager * _securityManager;
|
||||||
bool _is_firmware;
|
bool _is_firmware;
|
||||||
bool _is_filesystem;
|
bool _is_filesystem;
|
||||||
|
bool _md5_applied; // true if an MD5 digest was applied to the current firmware upload
|
||||||
std::array<char, 33> _md5;
|
std::array<char, 33> _md5;
|
||||||
|
|
||||||
void handleUpload(AsyncWebServerRequest * request, const String & filename, size_t index, uint8_t * data, size_t len, bool final);
|
void handleUpload(AsyncWebServerRequest * request, const String & filename, size_t index, uint8_t * data, size_t len, bool final);
|
||||||
|
|||||||
@@ -102,7 +102,7 @@ void AnalogSensor::start(const bool factory_settings) {
|
|||||||
Command::add(
|
Command::add(
|
||||||
EMSdevice::DeviceType::ANALOGSENSOR,
|
EMSdevice::DeviceType::ANALOGSENSOR,
|
||||||
F_(setvalue),
|
F_(setvalue),
|
||||||
[&](const char * value, const int8_t id, JsonObject output) { return command_setvalue(value, id); },
|
[this](const char * value, const int8_t id, JsonObject) { return command_setvalue(value, id); },
|
||||||
FL_(setiovalue_cmd),
|
FL_(setiovalue_cmd),
|
||||||
CommandFlag::ADMIN_ONLY);
|
CommandFlag::ADMIN_ONLY);
|
||||||
|
|
||||||
@@ -111,6 +111,12 @@ void AnalogSensor::start(const bool factory_settings) {
|
|||||||
Mqtt::subscribe(EMSdevice::DeviceType::ANALOGSENSOR, topic, nullptr); // use empty function callback
|
Mqtt::subscribe(EMSdevice::DeviceType::ANALOGSENSOR, topic, nullptr); // use empty function callback
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AnalogSensor::ha_reset() {
|
||||||
|
for (const auto & sensor : sensors_) {
|
||||||
|
remove_ha_topic(sensor.type(), sensor.gpio());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// load settings from the customization file, sorts them and initializes the GPIOs
|
// load settings from the customization file, sorts them and initializes the GPIOs
|
||||||
void AnalogSensor::reload(bool get_nvs) {
|
void AnalogSensor::reload(bool get_nvs) {
|
||||||
exclude_types_.clear();
|
exclude_types_.clear();
|
||||||
@@ -195,7 +201,7 @@ void AnalogSensor::reload(bool get_nvs) {
|
|||||||
Command::add(
|
Command::add(
|
||||||
EMSdevice::DeviceType::ANALOGSENSOR,
|
EMSdevice::DeviceType::ANALOGSENSOR,
|
||||||
sensor.name,
|
sensor.name,
|
||||||
[&](const char * value, const int8_t id, JsonObject output) { return command_setvalue(value, sensor.gpio); },
|
[this, gpio = sensor.gpio](const char * value, const int8_t, JsonObject) { return command_setvalue(value, gpio); },
|
||||||
sensor.type == AnalogType::COUNTER || (sensor.type >= AnalogType::CNT_0 && sensor.type <= AnalogType::CNT_2) ? FL_(counter)
|
sensor.type == AnalogType::COUNTER || (sensor.type >= AnalogType::CNT_0 && sensor.type <= AnalogType::CNT_2) ? FL_(counter)
|
||||||
: sensor.type == AnalogType::DIGITAL_OUT ? FL_(digital_out)
|
: sensor.type == AnalogType::DIGITAL_OUT ? FL_(digital_out)
|
||||||
: sensor.type == AnalogType::RGB ? FL_(RGB)
|
: sensor.type == AnalogType::RGB ? FL_(RGB)
|
||||||
@@ -729,7 +735,7 @@ void AnalogSensor::publish_values(const bool force) {
|
|||||||
}
|
}
|
||||||
// don't bother with value template conditions if using Domoticz which doesn't fully support MQTT Discovery
|
// don't bother with value template conditions if using Domoticz which doesn't fully support MQTT Discovery
|
||||||
if (Mqtt::discovery_type() == Mqtt::discoveryType::HOMEASSISTANT) {
|
if (Mqtt::discovery_type() == Mqtt::discoveryType::HOMEASSISTANT) {
|
||||||
config["val_tpl"] = (std::string) "{{" + val_obj + " if " + val_cond + "}}";
|
config["val_tpl"] = (std::string) "{{" + val_obj + " if " + val_cond + " else 'None'}}";
|
||||||
} else {
|
} else {
|
||||||
config["val_tpl"] = (std::string) "{{" + val_obj + "}}";
|
config["val_tpl"] = (std::string) "{{" + val_obj + "}}";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -144,6 +144,7 @@ class AnalogSensor {
|
|||||||
void publish_values(const bool force);
|
void publish_values(const bool force);
|
||||||
void reload(bool get_nvs = false);
|
void reload(bool get_nvs = false);
|
||||||
bool updated_values();
|
bool updated_values();
|
||||||
|
void ha_reset();
|
||||||
|
|
||||||
// return back reference to the sensor list, used by other classes
|
// return back reference to the sensor list, used by other classes
|
||||||
const std::vector<Sensor, AllocatorPSRAM<Sensor>> & sensors() const {
|
const std::vector<Sensor, AllocatorPSRAM<Sensor>> & sensors() const {
|
||||||
|
|||||||
@@ -492,7 +492,7 @@ uint8_t Command::call(const uint8_t device_type, const char * command, const cha
|
|||||||
}
|
}
|
||||||
|
|
||||||
// add a command to the list, which does not return json
|
// add a command to the list, which does not return json
|
||||||
void Command::add(const uint8_t device_type, const uint8_t device_id, const char * cmd, const cmd_function_p cb, const char * const * description, uint8_t flags) {
|
void Command::add(const uint8_t device_type, const uint8_t device_id, const char * cmd, const cmd_function_p & cb, const char * const * description, uint8_t flags) {
|
||||||
// if the command already exists for that device type don't add it
|
// if the command already exists for that device type don't add it
|
||||||
if (find_command(device_type, device_id, cmd, flags) != nullptr) {
|
if (find_command(device_type, device_id, cmd, flags) != nullptr) {
|
||||||
return;
|
return;
|
||||||
@@ -508,13 +508,13 @@ void Command::add(const uint8_t device_type, const uint8_t device_id, const char
|
|||||||
|
|
||||||
// add a command with no json output
|
// add a command with no json output
|
||||||
// system/temperature/analog devices uses device_id 0
|
// system/temperature/analog devices uses device_id 0
|
||||||
void Command::add(const uint8_t device_type, const char * cmd, const cmd_function_p cb, const char * const * description, uint8_t flags) {
|
void Command::add(const uint8_t device_type, const char * cmd, const cmd_function_p & cb, const char * const * description, uint8_t flags) {
|
||||||
add(device_type, 0, cmd, cb, description, flags);
|
add(device_type, 0, cmd, cb, description, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
// add a command to the list, which does return a json object as output
|
// add a command to the list, which does return a json object as output
|
||||||
// these commands bypass the readonly check (they are actions, not entity setters)
|
// these commands bypass the readonly check (they are actions, not entity setters)
|
||||||
void Command::add_json(const uint8_t device_type, const char * cmd, const cmd_function_p cb, const char * const * description, uint8_t flags) {
|
void Command::add_json(const uint8_t device_type, const char * cmd, const cmd_function_p & cb, const char * const * description, uint8_t flags) {
|
||||||
// if the command already exists for that device type don't add it
|
// if the command already exists for that device type don't add it
|
||||||
if (find_command(device_type, 0, cmd, flags) != nullptr) {
|
if (find_command(device_type, 0, cmd, flags) != nullptr) {
|
||||||
return;
|
return;
|
||||||
@@ -828,7 +828,7 @@ uint8_t Command::json_message(uint8_t error_code, const char * message, const Js
|
|||||||
// e.g. //one/two////three/// becomes /one/two/three
|
// e.g. //one/two////three/// becomes /one/two/three
|
||||||
std::string SUrlParser::path() {
|
std::string SUrlParser::path() {
|
||||||
std::string s = "/"; // set up the beginning slash
|
std::string s = "/"; // set up the beginning slash
|
||||||
for (const std::string & f : m_folders) {
|
for (const auto & f : m_folders) {
|
||||||
s += f;
|
s += f;
|
||||||
s += "/";
|
s += "/";
|
||||||
}
|
}
|
||||||
|
|||||||
+19
-19
@@ -67,13 +67,13 @@ class Command {
|
|||||||
cmd_function_p cmdfunction_;
|
cmd_function_p cmdfunction_;
|
||||||
const char * const * description_;
|
const char * const * description_;
|
||||||
|
|
||||||
CmdFunction(const uint8_t device_type,
|
CmdFunction(const uint8_t device_type,
|
||||||
const uint8_t device_id,
|
const uint8_t device_id,
|
||||||
const uint8_t flags,
|
const uint8_t flags,
|
||||||
const bool has_json_output,
|
const bool has_json_output,
|
||||||
const char * cmd,
|
const char * cmd,
|
||||||
const cmd_function_p cmdfunction,
|
const cmd_function_p & cmdfunction,
|
||||||
const char * const * description)
|
const char * const * description)
|
||||||
: device_type_(device_type)
|
: device_type_(device_type)
|
||||||
, device_id_(device_id)
|
, device_id_(device_id)
|
||||||
, flags_(flags)
|
, flags_(flags)
|
||||||
@@ -105,22 +105,22 @@ class Command {
|
|||||||
static uint8_t call(const uint8_t device_type, const char * cmd, const char * value, const int8_t id = -1);
|
static uint8_t call(const uint8_t device_type, const char * cmd, const char * value, const int8_t id = -1);
|
||||||
|
|
||||||
// with normal call back function taking a value and id
|
// with normal call back function taking a value and id
|
||||||
static void add(const uint8_t device_type,
|
static void add(const uint8_t device_type,
|
||||||
const uint8_t device_id,
|
const uint8_t device_id,
|
||||||
const char * cmd,
|
const char * cmd,
|
||||||
const cmd_function_p cb,
|
const cmd_function_p & cb,
|
||||||
const char * const * description,
|
const char * const * description,
|
||||||
uint8_t flags = CommandFlag::CMD_FLAG_DEFAULT);
|
uint8_t flags = CommandFlag::CMD_FLAG_DEFAULT);
|
||||||
|
|
||||||
// same for system/temperature/analog devices
|
// same for system/temperature/analog devices
|
||||||
static void
|
static void
|
||||||
add(const uint8_t device_type, const char * cmd, const cmd_function_p cb, const char * const * description, uint8_t flags = CommandFlag::CMD_FLAG_DEFAULT);
|
add(const uint8_t device_type, const char * cmd, const cmd_function_p & cb, const char * const * description, uint8_t flags = CommandFlag::CMD_FLAG_DEFAULT);
|
||||||
// command that writes a JSON object as its output; bypasses the readonly check
|
// command that writes a JSON object as its output; bypasses the readonly check
|
||||||
static void add_json(const uint8_t device_type,
|
static void add_json(const uint8_t device_type,
|
||||||
const char * cmd,
|
const char * cmd,
|
||||||
const cmd_function_p cb,
|
const cmd_function_p & cb,
|
||||||
const char * const * description,
|
const char * const * description,
|
||||||
uint8_t flags = CommandFlag::CMD_FLAG_DEFAULT);
|
uint8_t flags = CommandFlag::CMD_FLAG_DEFAULT);
|
||||||
|
|
||||||
static void reserve(size_t num) {
|
static void reserve(size_t num) {
|
||||||
cmdfunctions_.reserve(num);
|
cmdfunctions_.reserve(num);
|
||||||
|
|||||||
+35
-39
@@ -104,7 +104,7 @@ static void setup_commands(std::shared_ptr<Commands> const & commands) {
|
|||||||
shell.printfln("Unknown show command");
|
shell.printfln("Unknown show command");
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[](Shell const & shell, const std::vector<std::string> & current_arguments, const std::string & next_argument) -> std::vector<std::string> {
|
[](Shell const &, const std::vector<std::string> &, const std::string &) -> std::vector<std::string> {
|
||||||
return std::vector<std::string>{"system", "users", "devices", "log", "ems", "values", "mqtt", "commands"};
|
return std::vector<std::string>{"system", "users", "devices", "log", "ems", "values", "mqtt", "commands"};
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -191,7 +191,7 @@ static void setup_commands(std::shared_ptr<Commands> const & commands) {
|
|||||||
CommandFlags::ADMIN,
|
CommandFlags::ADMIN,
|
||||||
{F_(restart)},
|
{F_(restart)},
|
||||||
{F_(partitionname_optional)},
|
{F_(partitionname_optional)},
|
||||||
[](Shell & shell, const std::vector<std::string> & arguments) {
|
[](Shell &, const std::vector<std::string> & arguments) {
|
||||||
if (arguments.size()) {
|
if (arguments.size()) {
|
||||||
EMSESP::system_.system_restart(arguments.front().c_str());
|
EMSESP::system_.system_restart(arguments.front().c_str());
|
||||||
} else {
|
} else {
|
||||||
@@ -199,10 +199,9 @@ static void setup_commands(std::shared_ptr<Commands> const & commands) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
commands->add_command(ShellContext::MAIN,
|
commands->add_command(ShellContext::MAIN, CommandFlags::ADMIN, string_vector{F_(wifi), F_(reconnect)}, [](Shell &, const std::vector<std::string> &) {
|
||||||
CommandFlags::ADMIN,
|
EMSESP::network_.reconnect();
|
||||||
string_vector{F_(wifi), F_(reconnect)},
|
});
|
||||||
[](Shell & shell, const std::vector<std::string> & arguments) { EMSESP::network_.reconnect(); });
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// SET commands
|
// SET commands
|
||||||
@@ -244,7 +243,7 @@ static void setup_commands(std::shared_ptr<Commands> const & commands) {
|
|||||||
commands->add_command(ShellContext::MAIN,
|
commands->add_command(ShellContext::MAIN,
|
||||||
CommandFlags::ADMIN,
|
CommandFlags::ADMIN,
|
||||||
string_vector{F_(set), F_(wifi), F_(password)},
|
string_vector{F_(set), F_(wifi), F_(password)},
|
||||||
[](Shell & shell, const std::vector<std::string> & arguments) {
|
[](Shell & shell, const std::vector<std::string> &) {
|
||||||
shell.enter_password(F_(new_password_prompt1), [](Shell & shell, bool completed, const std::string & password1) {
|
shell.enter_password(F_(new_password_prompt1), [](Shell & shell, bool completed, const std::string & password1) {
|
||||||
if (completed) {
|
if (completed) {
|
||||||
shell.enter_password(F_(new_password_prompt2), [password1](Shell & shell, bool completed, const std::string & password2) {
|
shell.enter_password(F_(new_password_prompt2), [password1](Shell & shell, bool completed, const std::string & password2) {
|
||||||
@@ -296,36 +295,35 @@ static void setup_commands(std::shared_ptr<Commands> const & commands) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
commands->add_command(
|
commands->add_command(ShellContext::MAIN,
|
||||||
ShellContext::MAIN,
|
CommandFlags::ADMIN,
|
||||||
CommandFlags::ADMIN,
|
string_vector{F_(set), F_(board_profile)},
|
||||||
string_vector{F_(set), F_(board_profile)},
|
{F_(name_mandatory)},
|
||||||
{F_(name_mandatory)},
|
[](Shell & shell, const std::vector<std::string> & arguments) {
|
||||||
[](Shell & shell, const std::vector<std::string> & arguments) {
|
std::vector<int8_t> data; // led, dallas, rx, tx, button, phy_type, eth_power, eth_phy_addr, eth_clock_mode
|
||||||
std::vector<int8_t> data; // led, dallas, rx, tx, button, phy_type, eth_power, eth_phy_addr, eth_clock_mode
|
std::string board_profile = Helpers::toUpper(arguments.front());
|
||||||
std::string board_profile = Helpers::toUpper(arguments.front());
|
if (!EMSESP::system_.load_board_profile(data, board_profile)) {
|
||||||
if (!EMSESP::system_.load_board_profile(data, board_profile)) {
|
shell.println("Invalid board profile (S32, E32, E32V2, E32V2_2, MH-ET, NODEMCU, LOLIN, OLIMEX, OLIMEXPOE, OLIMEXPOEW, "
|
||||||
shell.println(
|
"C3MINI, S2MINI, S3MINI, S32S3, CUSTOM)");
|
||||||
"Invalid board profile (S32, E32, E32V2, E32V2_2, MH-ET, NODEMCU, LOLIN, OLIMEX, OLIMEXPOE, C3MINI, S2MINI, S3MINI, S32S3, CUSTOM)");
|
return;
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
EMSESP::webSettingsService.update([&](WebSettings & settings) {
|
EMSESP::webSettingsService.update([&](WebSettings & settings) {
|
||||||
settings.board_profile = board_profile.c_str();
|
settings.board_profile = board_profile.c_str();
|
||||||
settings.led_gpio = data[0];
|
settings.led_gpio = data[0];
|
||||||
settings.dallas_gpio = data[1];
|
settings.dallas_gpio = data[1];
|
||||||
settings.rx_gpio = data[2];
|
settings.rx_gpio = data[2];
|
||||||
settings.tx_gpio = data[3];
|
settings.tx_gpio = data[3];
|
||||||
settings.pbutton_gpio = data[4];
|
settings.pbutton_gpio = data[4];
|
||||||
settings.phy_type = data[5];
|
settings.phy_type = data[5];
|
||||||
settings.eth_power = data[6]; // can be -1
|
settings.eth_power = data[6]; // can be -1
|
||||||
settings.eth_phy_addr = data[7];
|
settings.eth_phy_addr = data[7];
|
||||||
settings.eth_clock_mode = data[8];
|
settings.eth_clock_mode = data[8];
|
||||||
return StateUpdateResult::CHANGED;
|
return StateUpdateResult::CHANGED;
|
||||||
});
|
});
|
||||||
shell.printfln("Loaded board profile %s. Restarting...", board_profile.c_str());
|
shell.printfln("Loaded board profile %s. Restarting...", board_profile.c_str());
|
||||||
EMSESP::system_.system_restart();
|
EMSESP::system_.system_restart();
|
||||||
});
|
});
|
||||||
|
|
||||||
commands->add_command(
|
commands->add_command(
|
||||||
ShellContext::MAIN,
|
ShellContext::MAIN,
|
||||||
@@ -367,9 +365,7 @@ static void setup_commands(std::shared_ptr<Commands> const & commands) {
|
|||||||
// EMS device commands
|
// EMS device commands
|
||||||
//
|
//
|
||||||
|
|
||||||
commands->add_command(ShellContext::MAIN, CommandFlags::ADMIN, {F_(scan)}, [](Shell & shell, const std::vector<std::string> & arguments) {
|
commands->add_command(ShellContext::MAIN, CommandFlags::ADMIN, {F_(scan)}, [](Shell &, const std::vector<std::string> &) { EMSESP::scan_devices(); });
|
||||||
EMSESP::scan_devices();
|
|
||||||
});
|
|
||||||
|
|
||||||
/* removed scan deep
|
/* removed scan deep
|
||||||
commands->add_command(ShellContext::MAIN, CommandFlags::ADMIN, {F_(scan)}, {F_(deep_optional)}, [](Shell & shell, const std::vector<std::string> & arguments) {
|
commands->add_command(ShellContext::MAIN, CommandFlags::ADMIN, {F_(scan)}, {F_(deep_optional)}, [](Shell & shell, const std::vector<std::string> & arguments) {
|
||||||
|
|||||||
+77
-73
@@ -52,7 +52,7 @@ bool EMSdevice::has_entities() const {
|
|||||||
}
|
}
|
||||||
bool found = false;
|
bool found = false;
|
||||||
EMSESP::webCustomizationService.read([&](WebCustomization & settings) {
|
EMSESP::webCustomizationService.read([&](WebCustomization & settings) {
|
||||||
for (EntityCustomization entityCustomization : settings.entityCustomizations) {
|
for (const auto & entityCustomization : settings.entityCustomizations) {
|
||||||
if (entityCustomization.device_id == device_id() && entityCustomization.entity_ids.size()) {
|
if (entityCustomization.device_id == device_id() && entityCustomization.entity_ids.size()) {
|
||||||
found = true;
|
found = true;
|
||||||
break;
|
break;
|
||||||
@@ -564,17 +564,17 @@ void EMSdevice::register_telegram_type(const uint16_t telegram_type_id, const ch
|
|||||||
|
|
||||||
// add to device value library, also know now as a "device entity"
|
// add to device value library, also know now as a "device entity"
|
||||||
// this function will also apply any customizations to the entity
|
// this function will also apply any customizations to the entity
|
||||||
void EMSdevice::add_device_value(int8_t tag, // to be used to group mqtt together, either as separate topics as a nested object
|
void EMSdevice::add_device_value(int8_t tag, // to be used to group mqtt together, either as separate topics as a nested object
|
||||||
void * value_p, // pointer to the value from the .h file
|
void * value_p, // pointer to the value from the .h file
|
||||||
uint8_t type, // one of DeviceValueType
|
uint8_t type, // one of DeviceValueType
|
||||||
const char * const ** options, // options for enum, which are translated as a list of lists
|
const char * const ** options, // options for enum, which are translated as a list of lists
|
||||||
const char * const * options_single, // list of names
|
const char * const * options_single, // list of names
|
||||||
int8_t numeric_operator, // to divide or multiply, see DeviceValueNumOps::
|
int8_t numeric_operator, // to divide or multiply, see DeviceValueNumOps::
|
||||||
const char * const * name, // list of names, including shortname and translations
|
const char * const * name, // list of names, including shortname and translations
|
||||||
uint8_t uom, // unit of measure from DeviceValueUOM
|
uint8_t uom, // unit of measure from DeviceValueUOM
|
||||||
const cmd_function_p f, // command function pointer
|
const cmd_function_p & f, // command function pointer
|
||||||
int16_t min, // min allowed value
|
int16_t min, // min allowed value
|
||||||
uint32_t max // max allowed value
|
uint32_t max // max allowed value
|
||||||
) {
|
) {
|
||||||
uint8_t state = DeviceValueState::DV_DEFAULT; // determine state
|
uint8_t state = DeviceValueState::DV_DEFAULT; // determine state
|
||||||
auto custom_fullname = std::string(""); // custom fullname
|
auto custom_fullname = std::string(""); // custom fullname
|
||||||
@@ -636,7 +636,7 @@ void EMSdevice::add_device_value(int8_t tag, // to b
|
|||||||
|
|
||||||
// scan through customizations to see if it's on the exclusion list by matching the productID and deviceID
|
// scan through customizations to see if it's on the exclusion list by matching the productID and deviceID
|
||||||
EMSESP::webCustomizationService.read([&](WebCustomization & settings) {
|
EMSESP::webCustomizationService.read([&](WebCustomization & settings) {
|
||||||
for (EntityCustomization entityCustomization : settings.entityCustomizations) {
|
for (const auto & entityCustomization : settings.entityCustomizations) {
|
||||||
if ((entityCustomization.product_id == product_id()) && (entityCustomization.device_id == device_id())) {
|
if ((entityCustomization.product_id == product_id()) && (entityCustomization.device_id == device_id())) {
|
||||||
char entity[70];
|
char entity[70];
|
||||||
if (tag < DeviceValueTAG::TAG_HC1) {
|
if (tag < DeviceValueTAG::TAG_HC1) {
|
||||||
@@ -645,7 +645,7 @@ void EMSdevice::add_device_value(int8_t tag, // to b
|
|||||||
snprintf(entity, sizeof(entity), "%s/%s", tag_to_mqtt(tag), short_name);
|
snprintf(entity, sizeof(entity), "%s/%s", tag_to_mqtt(tag), short_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const std::string & entity_id : entityCustomization.entity_ids) {
|
for (const auto & entity_id : entityCustomization.entity_ids) {
|
||||||
// if there is an appended custom name, strip it to get the true entity name
|
// if there is an appended custom name, strip it to get the true entity name
|
||||||
// and extract the new custom name
|
// and extract the new custom name
|
||||||
auto custom_name_pos = entity_id.find('|');
|
auto custom_name_pos = entity_id.find('|');
|
||||||
@@ -693,96 +693,100 @@ void EMSdevice::erase_device_values() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
devicevalues_.clear();
|
devicevalues_.clear();
|
||||||
|
telegram_functions_.clear();
|
||||||
|
handlers_ignored_.clear();
|
||||||
|
handlers_config_.clear();
|
||||||
|
handlers_broadcasted_.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
// single list of options
|
// single list of options
|
||||||
void EMSdevice::register_device_value(int8_t tag,
|
void EMSdevice::register_device_value(int8_t tag,
|
||||||
void * value_p,
|
void * value_p,
|
||||||
uint8_t type,
|
uint8_t type,
|
||||||
const char * const * options_single,
|
const char * const * options_single,
|
||||||
const char * const * name,
|
const char * const * name,
|
||||||
uint8_t uom,
|
uint8_t uom,
|
||||||
const cmd_function_p f) {
|
const cmd_function_p & f) {
|
||||||
// create a multi-list from the options
|
// create a multi-list from the options
|
||||||
add_device_value(tag, value_p, type, nullptr, options_single, 0, name, uom, f, 0, 0);
|
add_device_value(tag, value_p, type, nullptr, options_single, 0, name, uom, f, 0, 0);
|
||||||
};
|
};
|
||||||
|
|
||||||
// single list of options, with no translations, with min and max
|
// single list of options, with no translations, with min and max
|
||||||
void EMSdevice::register_device_value(int8_t tag,
|
void EMSdevice::register_device_value(int8_t tag,
|
||||||
void * value_p,
|
void * value_p,
|
||||||
uint8_t type,
|
uint8_t type,
|
||||||
const char * const * options_single,
|
const char * const * options_single,
|
||||||
const char * const * name,
|
const char * const * name,
|
||||||
uint8_t uom,
|
uint8_t uom,
|
||||||
const cmd_function_p f,
|
const cmd_function_p & f,
|
||||||
int16_t min,
|
int16_t min,
|
||||||
uint32_t max) {
|
uint32_t max) {
|
||||||
// create a multi-list from the options
|
// create a multi-list from the options
|
||||||
add_device_value(tag, value_p, type, nullptr, options_single, 0, name, uom, f, min, max);
|
add_device_value(tag, value_p, type, nullptr, options_single, 0, name, uom, f, min, max);
|
||||||
};
|
};
|
||||||
|
|
||||||
void EMSdevice::register_device_value(int8_t tag,
|
void EMSdevice::register_device_value(int8_t tag,
|
||||||
void * value_p,
|
void * value_p,
|
||||||
uint8_t type,
|
uint8_t type,
|
||||||
int8_t numeric_operator,
|
int8_t numeric_operator,
|
||||||
const char * const * name,
|
const char * const * name,
|
||||||
uint8_t uom,
|
uint8_t uom,
|
||||||
const cmd_function_p f) {
|
const cmd_function_p & f) {
|
||||||
add_device_value(tag, value_p, type, nullptr, nullptr, numeric_operator, name, uom, f, 0, 0);
|
add_device_value(tag, value_p, type, nullptr, nullptr, numeric_operator, name, uom, f, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EMSdevice::register_device_value(int8_t tag,
|
void EMSdevice::register_device_value(int8_t tag,
|
||||||
void * value_p,
|
void * value_p,
|
||||||
uint8_t type,
|
uint8_t type,
|
||||||
int8_t numeric_operator,
|
int8_t numeric_operator,
|
||||||
const char * const * name,
|
const char * const * name,
|
||||||
uint8_t uom,
|
uint8_t uom,
|
||||||
const cmd_function_p f,
|
const cmd_function_p & f,
|
||||||
int16_t min,
|
int16_t min,
|
||||||
uint32_t max) {
|
uint32_t max) {
|
||||||
add_device_value(tag, value_p, type, nullptr, nullptr, numeric_operator, name, uom, f, min, max);
|
add_device_value(tag, value_p, type, nullptr, nullptr, numeric_operator, name, uom, f, min, max);
|
||||||
}
|
}
|
||||||
|
|
||||||
// no options, no function
|
// no options, no function
|
||||||
void EMSdevice::register_device_value(int8_t tag, void * value_p, uint8_t type, const char * const * name, uint8_t uom, const cmd_function_p f) {
|
void EMSdevice::register_device_value(int8_t tag, void * value_p, uint8_t type, const char * const * name, uint8_t uom, const cmd_function_p & f) {
|
||||||
add_device_value(tag, value_p, type, nullptr, nullptr, 0, name, uom, f, 0, 0);
|
add_device_value(tag, value_p, type, nullptr, nullptr, 0, name, uom, f, 0, 0);
|
||||||
};
|
};
|
||||||
|
|
||||||
// no options, with min/max
|
// no options, with min/max
|
||||||
void EMSdevice::register_device_value(int8_t tag,
|
void EMSdevice::register_device_value(int8_t tag,
|
||||||
void * value_p,
|
void * value_p,
|
||||||
uint8_t type,
|
uint8_t type,
|
||||||
const char * const * name,
|
const char * const * name,
|
||||||
uint8_t uom,
|
uint8_t uom,
|
||||||
const cmd_function_p f,
|
const cmd_function_p & f,
|
||||||
int16_t min,
|
int16_t min,
|
||||||
uint32_t max) {
|
uint32_t max) {
|
||||||
add_device_value(tag, value_p, type, nullptr, nullptr, 0, name, uom, f, min, max);
|
add_device_value(tag, value_p, type, nullptr, nullptr, 0, name, uom, f, min, max);
|
||||||
};
|
};
|
||||||
|
|
||||||
// function with min and max values
|
// function with min and max values
|
||||||
// adds a new command to the command list
|
// adds a new command to the command list
|
||||||
// in this function we separate out the short and long names and take any translations
|
// in this function we separate out the short and long names and take any translations
|
||||||
void EMSdevice::register_device_value(int8_t tag,
|
void EMSdevice::register_device_value(int8_t tag,
|
||||||
void * value_p,
|
void * value_p,
|
||||||
uint8_t type,
|
uint8_t type,
|
||||||
const char * const ** options,
|
const char * const ** options,
|
||||||
const char * const * name,
|
const char * const * name,
|
||||||
uint8_t uom,
|
uint8_t uom,
|
||||||
const cmd_function_p f,
|
const cmd_function_p & f,
|
||||||
int16_t min,
|
int16_t min,
|
||||||
uint32_t max) {
|
uint32_t max) {
|
||||||
add_device_value(tag, value_p, type, options, nullptr, 0, name, uom, f, min, max);
|
add_device_value(tag, value_p, type, options, nullptr, 0, name, uom, f, min, max);
|
||||||
}
|
}
|
||||||
|
|
||||||
// function with no min and max values (set to 0)
|
// function with no min and max values (set to 0)
|
||||||
void EMSdevice::register_device_value(int8_t tag,
|
void EMSdevice::register_device_value(int8_t tag,
|
||||||
void * value_p,
|
void * value_p,
|
||||||
uint8_t type,
|
uint8_t type,
|
||||||
const char * const ** options,
|
const char * const ** options,
|
||||||
const char * const * name,
|
const char * const * name,
|
||||||
uint8_t uom,
|
uint8_t uom,
|
||||||
const cmd_function_p f) {
|
const cmd_function_p & f) {
|
||||||
add_device_value(tag, value_p, type, options, nullptr, 0, name, uom, f, 0, 0);
|
add_device_value(tag, value_p, type, options, nullptr, 0, name, uom, f, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1206,10 +1210,10 @@ void EMSdevice::generate_values_web_customization(JsonArray output) {
|
|||||||
// this is when the mask has it's high bit (0x80) set
|
// this is when the mask has it's high bit (0x80) set
|
||||||
// https://github.com/emsesp/EMS-ESP32/issues/891
|
// https://github.com/emsesp/EMS-ESP32/issues/891
|
||||||
EMSESP::webCustomizationService.read([&](WebCustomization & settings) {
|
EMSESP::webCustomizationService.read([&](WebCustomization & settings) {
|
||||||
for (EntityCustomization entityCustomization : settings.entityCustomizations) {
|
for (const auto & entityCustomization : settings.entityCustomizations) {
|
||||||
if (entityCustomization.device_id == device_id()) {
|
if (entityCustomization.device_id == device_id()) {
|
||||||
// entity_ids is a list of all entities with the mask prefixed in the string
|
// entity_ids is a list of all entities with the mask prefixed in the string
|
||||||
for (const std::string & entity_id : entityCustomization.entity_ids) {
|
for (const auto & entity_id : entityCustomization.entity_ids) {
|
||||||
uint8_t mask = Helpers::hextoint(entity_id.substr(0, 2).c_str());
|
uint8_t mask = Helpers::hextoint(entity_id.substr(0, 2).c_str());
|
||||||
if (mask & 0x80) {
|
if (mask & 0x80) {
|
||||||
JsonObject obj = output.add<JsonObject>();
|
JsonObject obj = output.add<JsonObject>();
|
||||||
|
|||||||
+56
-55
@@ -290,78 +290,79 @@ class EMSdevice {
|
|||||||
void generate_values_web(JsonObject output, const bool is_dashboard = false);
|
void generate_values_web(JsonObject output, const bool is_dashboard = false);
|
||||||
void generate_values_web_customization(JsonArray output);
|
void generate_values_web_customization(JsonArray output);
|
||||||
|
|
||||||
void add_device_value(int8_t tag,
|
void add_device_value(int8_t tag,
|
||||||
void * value_p,
|
void * value_p,
|
||||||
uint8_t type,
|
uint8_t type,
|
||||||
const char * const ** options,
|
const char * const ** options,
|
||||||
const char * const * options_single,
|
const char * const * options_single,
|
||||||
int8_t numeric_operator,
|
int8_t numeric_operator,
|
||||||
const char * const * name,
|
const char * const * name,
|
||||||
uint8_t uom,
|
uint8_t uom,
|
||||||
const cmd_function_p f,
|
const cmd_function_p & f,
|
||||||
int16_t min,
|
int16_t min,
|
||||||
uint32_t max);
|
uint32_t max);
|
||||||
|
|
||||||
void register_device_value(int8_t tag,
|
void register_device_value(int8_t tag,
|
||||||
void * value_p,
|
void * value_p,
|
||||||
uint8_t type,
|
uint8_t type,
|
||||||
const char * const ** options,
|
const char * const ** options,
|
||||||
const char * const * name,
|
const char * const * name,
|
||||||
uint8_t uom,
|
uint8_t uom,
|
||||||
const cmd_function_p f,
|
const cmd_function_p & f,
|
||||||
int16_t min,
|
int16_t min,
|
||||||
uint32_t max);
|
uint32_t max);
|
||||||
|
|
||||||
void erase_device_values();
|
void erase_device_values();
|
||||||
|
|
||||||
void
|
void
|
||||||
register_device_value(int8_t tag, void * value_p, uint8_t type, const char * const ** options, const char * const * name, uint8_t uom, const cmd_function_p f);
|
register_device_value(int8_t tag, void * value_p, uint8_t type, const char * const ** options, const char * const * name, uint8_t uom, const cmd_function_p & f);
|
||||||
|
|
||||||
void register_device_value(int8_t tag, void * value_p, uint8_t type, const char * const ** options, const char * const * name, uint8_t uom);
|
void register_device_value(int8_t tag, void * value_p, uint8_t type, const char * const ** options, const char * const * name, uint8_t uom);
|
||||||
|
|
||||||
void register_device_value(int8_t tag,
|
void register_device_value(int8_t tag,
|
||||||
void * value_p,
|
void * value_p,
|
||||||
uint8_t type,
|
uint8_t type,
|
||||||
int8_t numeric_operator,
|
int8_t numeric_operator,
|
||||||
const char * const * name,
|
const char * const * name,
|
||||||
uint8_t uom,
|
uint8_t uom,
|
||||||
const cmd_function_p f = nullptr);
|
const cmd_function_p & f = nullptr);
|
||||||
|
|
||||||
void register_device_value(int8_t tag,
|
void register_device_value(int8_t tag,
|
||||||
void * value_p,
|
void * value_p,
|
||||||
uint8_t type,
|
uint8_t type,
|
||||||
int8_t numeric_operator,
|
int8_t numeric_operator,
|
||||||
const char * const * name,
|
const char * const * name,
|
||||||
uint8_t uom,
|
uint8_t uom,
|
||||||
const cmd_function_p f,
|
const cmd_function_p & f,
|
||||||
int16_t min,
|
int16_t min,
|
||||||
uint32_t max);
|
uint32_t max);
|
||||||
|
|
||||||
// single list of options
|
// single list of options
|
||||||
void register_device_value(int8_t tag,
|
void register_device_value(int8_t tag,
|
||||||
void * value_p,
|
void * value_p,
|
||||||
uint8_t type,
|
uint8_t type,
|
||||||
const char * const * options_single,
|
const char * const * options_single,
|
||||||
const char * const * name,
|
const char * const * name,
|
||||||
uint8_t uom,
|
uint8_t uom,
|
||||||
const cmd_function_p f = nullptr);
|
const cmd_function_p & f = nullptr);
|
||||||
|
|
||||||
// single list of options, with no translations, with min and max
|
// single list of options, with no translations, with min and max
|
||||||
void register_device_value(int8_t tag,
|
void register_device_value(int8_t tag,
|
||||||
void * value_p,
|
void * value_p,
|
||||||
uint8_t type,
|
uint8_t type,
|
||||||
const char * const * options_single,
|
const char * const * options_single,
|
||||||
const char * const * name,
|
const char * const * name,
|
||||||
uint8_t uom,
|
uint8_t uom,
|
||||||
const cmd_function_p f,
|
const cmd_function_p & f,
|
||||||
int16_t min,
|
int16_t min,
|
||||||
uint32_t max);
|
uint32_t max);
|
||||||
|
|
||||||
// no options, optional function f
|
// no options, optional function f
|
||||||
void register_device_value(int8_t tag, void * value_p, uint8_t type, const char * const * name, uint8_t uom, const cmd_function_p f = nullptr);
|
void register_device_value(int8_t tag, void * value_p, uint8_t type, const char * const * name, uint8_t uom, const cmd_function_p & f = nullptr);
|
||||||
|
|
||||||
// no options, with min/max
|
// no options, with min/max
|
||||||
void register_device_value(int8_t tag, void * value_p, uint8_t type, const char * const * name, uint8_t uom, const cmd_function_p f, int16_t min, uint32_t max);
|
void
|
||||||
|
register_device_value(int8_t tag, void * value_p, uint8_t type, const char * const * name, uint8_t uom, const cmd_function_p & f, int16_t min, uint32_t max);
|
||||||
|
|
||||||
void write_command(const uint16_t type_id, const uint8_t offset, uint8_t * message_data, const uint8_t message_length, const uint16_t validate_typeid) const;
|
void write_command(const uint16_t type_id, const uint8_t offset, uint8_t * message_data, const uint8_t message_length, const uint16_t validate_typeid) const;
|
||||||
void write_command(const uint16_t type_id, const uint8_t offset, const uint8_t value, const uint16_t validate_typeid) const;
|
void write_command(const uint16_t type_id, const uint8_t offset, const uint8_t value, const uint16_t validate_typeid) const;
|
||||||
|
|||||||
@@ -192,6 +192,11 @@ class DeviceValue {
|
|||||||
int16_t min; // min range
|
int16_t min; // min range
|
||||||
uint32_t max; // max range
|
uint32_t max; // max range
|
||||||
|
|
||||||
|
// optional custom name from customization. Allocated on heap only when actually set,
|
||||||
|
// so unnamed entities (the vast majority) don't pay for an inline std::string.
|
||||||
|
// Prefer custom_fullname() / set_custom_fullname() / has_custom_fullname() over direct access.
|
||||||
|
std::unique_ptr<std::string> custom_fullname_;
|
||||||
|
|
||||||
DeviceValue(uint8_t device_type, // EMSdevice::DeviceType
|
DeviceValue(uint8_t device_type, // EMSdevice::DeviceType
|
||||||
int8_t tag, // DeviceValueTAG::*
|
int8_t tag, // DeviceValueTAG::*
|
||||||
void * value_p, // pointer to variable of any type
|
void * value_p, // pointer to variable of any type
|
||||||
@@ -245,11 +250,6 @@ class DeviceValue {
|
|||||||
static const char * const * DeviceValueTAG_s[];
|
static const char * const * DeviceValueTAG_s[];
|
||||||
static const char * const DeviceValueTAG_mqtt[];
|
static const char * const DeviceValueTAG_mqtt[];
|
||||||
static uint8_t NUM_TAGS; // # tags
|
static uint8_t NUM_TAGS; // # tags
|
||||||
|
|
||||||
private:
|
|
||||||
// optional custom name from customization. Allocated on heap only when actually set,
|
|
||||||
// so unnamed entities (the vast majority) don't pay for an inline std::string.
|
|
||||||
std::unique_ptr<std::string> custom_fullname_;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}; // namespace emsesp
|
}; // namespace emsesp
|
||||||
|
|||||||
+54
-33
@@ -54,19 +54,19 @@ bool EMSESP::entity_compaction_pending_ = false;
|
|||||||
AsyncWebServer webServer(80);
|
AsyncWebServer webServer(80);
|
||||||
|
|
||||||
#if defined(EMSESP_STANDALONE)
|
#if defined(EMSESP_STANDALONE)
|
||||||
FS dummyFS;
|
FS dummyFS;
|
||||||
auto & fsRef = dummyFS;
|
FS * const fsRef = &dummyFS;
|
||||||
#else
|
#else
|
||||||
auto & fsRef = LittleFS;
|
FS * const fsRef = &LittleFS;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
ESP32React EMSESP::esp32React(&webServer, &fsRef);
|
ESP32React EMSESP::esp32React(&webServer, fsRef);
|
||||||
WebSettingsService EMSESP::webSettingsService = WebSettingsService(&webServer, &fsRef, EMSESP::esp32React.getSecurityManager());
|
WebSettingsService EMSESP::webSettingsService = WebSettingsService(&webServer, fsRef, EMSESP::esp32React.getSecurityManager());
|
||||||
WebCustomizationService EMSESP::webCustomizationService = WebCustomizationService(&webServer, &fsRef, EMSESP::esp32React.getSecurityManager());
|
WebCustomizationService EMSESP::webCustomizationService = WebCustomizationService(&webServer, fsRef, EMSESP::esp32React.getSecurityManager());
|
||||||
WebSchedulerService EMSESP::webSchedulerService = WebSchedulerService(&webServer, &fsRef, EMSESP::esp32React.getSecurityManager());
|
WebSchedulerService EMSESP::webSchedulerService = WebSchedulerService(&webServer, fsRef, EMSESP::esp32React.getSecurityManager());
|
||||||
WebCommandService EMSESP::webCommandService = WebCommandService(&webServer, &fsRef, EMSESP::esp32React.getSecurityManager());
|
WebCommandService EMSESP::webCommandService = WebCommandService(&webServer, fsRef, EMSESP::esp32React.getSecurityManager());
|
||||||
WebCustomEntityService EMSESP::webCustomEntityService = WebCustomEntityService(&webServer, &fsRef, EMSESP::esp32React.getSecurityManager());
|
WebCustomEntityService EMSESP::webCustomEntityService = WebCustomEntityService(&webServer, fsRef, EMSESP::esp32React.getSecurityManager());
|
||||||
WebModulesService EMSESP::webModulesService = WebModulesService(&webServer, &fsRef, EMSESP::esp32React.getSecurityManager());
|
WebModulesService EMSESP::webModulesService = WebModulesService(&webServer, fsRef, EMSESP::esp32React.getSecurityManager());
|
||||||
|
|
||||||
WebActivityService EMSESP::webActivityService = WebActivityService(&webServer, EMSESP::esp32React.getSecurityManager());
|
WebActivityService EMSESP::webActivityService = WebActivityService(&webServer, EMSESP::esp32React.getSecurityManager());
|
||||||
WebStatusService EMSESP::webStatusService = WebStatusService(&webServer, EMSESP::esp32React.getSecurityManager());
|
WebStatusService EMSESP::webStatusService = WebStatusService(&webServer, EMSESP::esp32React.getSecurityManager());
|
||||||
@@ -173,10 +173,23 @@ uint8_t EMSESP::device_id_from_cmd(const uint8_t device_type, const char * cmd,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// clears list of recognized devices
|
// remove a single device with all it's entities, commands, and telegrams
|
||||||
|
void EMSESP::erase_device(const uint8_t type_id) {
|
||||||
|
for (auto it = emsdevices.begin(); it < emsdevices.end(); it++) {
|
||||||
|
if ((*it)->device_id() == type_id) {
|
||||||
|
(*it)->erase_device_values();
|
||||||
|
emsdevices.erase(it);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// clears list of recognized devices, entities and telegrams
|
||||||
void EMSESP::clear_all_devices() {
|
void EMSESP::clear_all_devices() {
|
||||||
// temporarily removed: clearing the list causes a crash, the associated commands and mqtt should also be removed.
|
for (const auto & emsdevice : emsdevices) {
|
||||||
// emsdevices.clear(); // remove entries, but doesn't delete actual devices
|
emsdevice->erase_device_values();
|
||||||
|
}
|
||||||
|
emsdevices.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
// called from EMSdevice/Command whenever an entity or telegram handler is registered.
|
// called from EMSdevice/Command whenever an entity or telegram handler is registered.
|
||||||
@@ -644,8 +657,8 @@ void EMSESP::reset_mqtt_ha() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// force the re-creating of the temperature and analog sensor topics (for HA)
|
// force the re-creating of the temperature and analog sensor topics (for HA)
|
||||||
temperaturesensor_.reload();
|
temperaturesensor_.ha_reset();
|
||||||
analogsensor_.reload();
|
analogsensor_.ha_reset();
|
||||||
|
|
||||||
// rebuild MQTT HA config topics for shower, custom entities and scheduler
|
// rebuild MQTT HA config topics for shower, custom entities and scheduler
|
||||||
shower_.ha_reset();
|
shower_.ha_reset();
|
||||||
@@ -1027,10 +1040,13 @@ void EMSESP::process_UBADevices(const std::shared_ptr<const Telegram> & telegram
|
|||||||
if (next_byte & 0x01) {
|
if (next_byte & 0x01) {
|
||||||
// if we haven't already detected this device, request it's version details, unless its us (EMS-ESP)
|
// if we haven't already detected this device, request it's version details, unless its us (EMS-ESP)
|
||||||
// when the version info is received, it will automagically add the device
|
// when the version info is received, it will automagically add the device
|
||||||
if ((device_id != EMSbus::ems_bus_id()) && !(EMSESP::device_exists(device_id))) {
|
if ((device_id != EMSbus::ems_bus_id()) && !(device_exists(device_id))) {
|
||||||
LOG_DEBUG("New EMS device detected with ID 0x%02X. Requesting version information.", device_id);
|
LOG_DEBUG("New EMS device detected with ID 0x%02X. Requesting version information.", device_id);
|
||||||
send_read_request(EMSdevice::EMS_TYPE_VERSION, device_id);
|
send_read_request(EMSdevice::EMS_TYPE_VERSION, device_id);
|
||||||
}
|
}
|
||||||
|
} else if (device_exists(device_id) && !device_hasEntities(device_id)) {
|
||||||
|
LOG_DEBUG("Remove EMS device with ID 0x%02X", device_id);
|
||||||
|
erase_device(device_id); //
|
||||||
}
|
}
|
||||||
next_byte = next_byte >> 1; // advance 1 bit
|
next_byte = next_byte >> 1; // advance 1 bit
|
||||||
}
|
}
|
||||||
@@ -1237,6 +1253,15 @@ bool EMSESP::process_telegram(const std::shared_ptr<const Telegram> & telegram)
|
|||||||
return telegram_found;
|
return telegram_found;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool EMSESP::device_hasEntities(const uint8_t device_id) {
|
||||||
|
for (const auto & emsdevice : emsdevices) {
|
||||||
|
if (emsdevice->is_device_id(device_id) && (emsdevice->count_entities() > 0)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false; // not found
|
||||||
|
}
|
||||||
|
|
||||||
// return true if we have this device already registered
|
// return true if we have this device already registered
|
||||||
bool EMSESP::device_exists(const uint8_t device_id) {
|
bool EMSESP::device_exists(const uint8_t device_id) {
|
||||||
if (emsdevices.empty()) {
|
if (emsdevices.empty()) {
|
||||||
@@ -1459,7 +1484,7 @@ bool EMSESP::add_device(const uint8_t device_id, const uint8_t product_id, const
|
|||||||
|
|
||||||
// see if we have a custom device name in our Customizations list, and if so set it
|
// see if we have a custom device name in our Customizations list, and if so set it
|
||||||
webCustomizationService.read([&](WebCustomization const & settings) {
|
webCustomizationService.read([&](WebCustomization const & settings) {
|
||||||
for (EntityCustomization e : settings.entityCustomizations) {
|
for (const auto & e : settings.entityCustomizations) {
|
||||||
if ((e.device_id == device_id) && (e.product_id == product_id)) {
|
if ((e.device_id == device_id) && (e.product_id == product_id)) {
|
||||||
LOG_DEBUG("Have customizations for %s with deviceID 0x%02X productID %d", e.custom_name.c_str(), device_id, product_id);
|
LOG_DEBUG("Have customizations for %s with deviceID 0x%02X productID %d", e.custom_name.c_str(), device_id, product_id);
|
||||||
emsdevices.back()->custom_name(e.custom_name);
|
emsdevices.back()->custom_name(e.custom_name);
|
||||||
@@ -1715,15 +1740,16 @@ void EMSESP::start() {
|
|||||||
bool factory_settings = false;
|
bool factory_settings = false;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// #if defined(EMSESP_DEBUG)
|
|
||||||
// LOG_DEBUG("Listing root directory before:");
|
|
||||||
// system_.listDir("/", 3); // show the contents of the root directory
|
|
||||||
// #endif
|
|
||||||
|
|
||||||
// start NVS storage
|
// start NVS storage
|
||||||
if (!nvs_.begin("ems-esp", false, "nvs1")) { // try bigger nvs partition on 16M flash first
|
#ifndef EMSESP_STANDALONE
|
||||||
nvs_.begin("ems-esp", false, "nvs"); // fallback to small nvs
|
if (esp_partition_find(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_NVS, "nvs1")) {
|
||||||
|
nvs_.begin("ems-esp", false, "nvs1");
|
||||||
|
} else {
|
||||||
|
nvs_.begin("ems-esp", false, "nvs"); // fallback to small nvs
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
nvs_.begin("ems-esp", false, "nvs");
|
||||||
|
#endif
|
||||||
|
|
||||||
// set valid GPIOs list based on ESP32 chip/platform type
|
// set valid GPIOs list based on ESP32 chip/platform type
|
||||||
system_.set_valid_system_gpios();
|
system_.set_valid_system_gpios();
|
||||||
@@ -1734,11 +1760,6 @@ void EMSESP::start() {
|
|||||||
// loads core system services settings (mqtt, ap, ntp etc)
|
// loads core system services settings (mqtt, ap, ntp etc)
|
||||||
esp32React.begin();
|
esp32React.begin();
|
||||||
|
|
||||||
// #if defined(EMSESP_DEBUG)
|
|
||||||
// LOG_DEBUG("Listing root directory after:");
|
|
||||||
// system_.listDir("/", 3); // show the contents of the root directory
|
|
||||||
// #endif
|
|
||||||
|
|
||||||
#ifndef EMSESP_STANDALONE
|
#ifndef EMSESP_STANDALONE
|
||||||
if (factory_settings) {
|
if (factory_settings) {
|
||||||
LOG_WARNING("No settings found on filesystem. Using factory settings.");
|
LOG_WARNING("No settings found on filesystem. Using factory settings.");
|
||||||
@@ -1868,7 +1889,7 @@ void EMSESP::shell_prompt() {
|
|||||||
void EMSESP::loop() {
|
void EMSESP::loop() {
|
||||||
uuid::loop(); // store system uptime
|
uuid::loop(); // store system uptime
|
||||||
|
|
||||||
// handle network
|
// handle network (Wifi, ETH and AP)
|
||||||
network_.loop();
|
network_.loop();
|
||||||
|
|
||||||
// handles LED and checks system health, and syslog service
|
// handles LED and checks system health, and syslog service
|
||||||
@@ -1876,14 +1897,13 @@ void EMSESP::loop() {
|
|||||||
return; // LED flashing is active meaning its about to reboot, skip the rest of the loop
|
return; // LED flashing is active meaning its about to reboot, skip the rest of the loop
|
||||||
}
|
}
|
||||||
|
|
||||||
esp32React.loop(); // core services: Network, AP, MQTT and NTP
|
|
||||||
webLogService.loop(); // log in Web UI
|
webLogService.loop(); // log in Web UI
|
||||||
|
|
||||||
// run the loop, unless we're in the middle of an OTA upload
|
// run the main loop, unless we're in the middle of an OTA upload
|
||||||
// if (EMSESP::system_.systemStatus() == SYSTEM_STATUS::SYSTEM_STATUS_NORMAL || EMSESP::system_.systemStatus() == SYSTEM_STATUS::SYSTEM_STATUS_INVALID_GPIO) {
|
// if (EMSESP::system_.systemStatus() == SYSTEM_STATUS::SYSTEM_STATUS_NORMAL || EMSESP::system_.systemStatus() == SYSTEM_STATUS::SYSTEM_STATUS_INVALID_GPIO) {
|
||||||
if (EMSESP::system_.systemStatus() != SYSTEM_STATUS::SYSTEM_STATUS_PENDING_UPLOAD
|
if (EMSESP::system_.systemStatus() != SYSTEM_STATUS::SYSTEM_STATUS_PENDING_UPLOAD
|
||||||
&& EMSESP::system_.systemStatus() != SYSTEM_STATUS::SYSTEM_STATUS_UPLOADING) {
|
&& EMSESP::system_.systemStatus() != SYSTEM_STATUS::SYSTEM_STATUS_UPLOADING) {
|
||||||
// loop through the services
|
esp32React.loop(); // MQTT and NTP
|
||||||
webStatusService.loop(); // periodic refresh of cached versions.json
|
webStatusService.loop(); // periodic refresh of cached versions.json
|
||||||
rxservice_.loop(); // process any incoming Rx telegrams
|
rxservice_.loop(); // process any incoming Rx telegrams
|
||||||
shower_.loop(); // check for shower on/off
|
shower_.loop(); // check for shower on/off
|
||||||
@@ -1896,6 +1916,7 @@ void EMSESP::loop() {
|
|||||||
scheduled_fetch_values(); // force a query on the EMS devices to fetch latest data at a set interval (1 min)
|
scheduled_fetch_values(); // force a query on the EMS devices to fetch latest data at a set interval (1 min)
|
||||||
compact_entities_if_stable(); // reclaim over-reserved entity vector capacity once device discovery settles
|
compact_entities_if_stable(); // reclaim over-reserved entity vector capacity once device discovery settles
|
||||||
}
|
}
|
||||||
|
|
||||||
// check for GPIO Errors - this is called once when booting
|
// check for GPIO Errors - this is called once when booting
|
||||||
if (EMSESP::system_.systemStatus() == SYSTEM_STATUS::SYSTEM_STATUS_INVALID_GPIO) {
|
if (EMSESP::system_.systemStatus() == SYSTEM_STATUS::SYSTEM_STATUS_INVALID_GPIO) {
|
||||||
static bool only_once = false;
|
static bool only_once = false;
|
||||||
|
|||||||
+3
-1
@@ -101,7 +101,7 @@ class Module {}; // forward declaration
|
|||||||
|
|
||||||
// for Command Function callbacks (Command::cmd_function_p). The unified callback takes a JsonObject
|
// for Command Function callbacks (Command::cmd_function_p). The unified callback takes a JsonObject
|
||||||
// output which entity/setter commands ignore.
|
// output which entity/setter commands ignore.
|
||||||
#define MAKE_CF_CB(__f) [&](const char * value, const int8_t id, JsonObject output) { return __f(value, id); }
|
#define MAKE_CF_CB(__f) [this](const char * value, const int8_t id, JsonObject output) { return __f(value, id); }
|
||||||
|
|
||||||
namespace emsesp {
|
namespace emsesp {
|
||||||
|
|
||||||
@@ -152,6 +152,7 @@ class EMSESP {
|
|||||||
static void send_write_request(const uint16_t type_id, const uint8_t dest, const uint8_t offset, const uint8_t value);
|
static void send_write_request(const uint16_t type_id, const uint8_t dest, const uint8_t offset, const uint8_t value);
|
||||||
static void send_write_request(const uint16_t type_id, const uint8_t dest, const uint8_t offset, const uint8_t value, const uint16_t validate_typeid);
|
static void send_write_request(const uint16_t type_id, const uint8_t dest, const uint8_t offset, const uint8_t value, const uint16_t validate_typeid);
|
||||||
|
|
||||||
|
static bool device_hasEntities(const uint8_t device_id);
|
||||||
static bool device_exists(const uint8_t device_id);
|
static bool device_exists(const uint8_t device_id);
|
||||||
static void device_active(const uint8_t device_id, const bool active);
|
static void device_active(const uint8_t device_id, const bool active);
|
||||||
static bool cmd_is_readonly(const uint8_t device_type, const uint8_t device_id, const char * cmd, const int8_t id);
|
static bool cmd_is_readonly(const uint8_t device_type, const uint8_t device_id, const char * cmd, const int8_t id);
|
||||||
@@ -238,6 +239,7 @@ class EMSESP {
|
|||||||
|
|
||||||
static bool add_device(const uint8_t device_id, const uint8_t product_id, const char * version, const uint8_t brand);
|
static bool add_device(const uint8_t device_id, const uint8_t product_id, const char * version, const uint8_t brand);
|
||||||
static void scan_devices();
|
static void scan_devices();
|
||||||
|
static void erase_device(const uint8_t type_id);
|
||||||
static void clear_all_devices();
|
static void clear_all_devices();
|
||||||
|
|
||||||
// called whenever a device entity or telegram handler is registered, so we can
|
// called whenever a device entity or telegram handler is registered, so we can
|
||||||
|
|||||||
@@ -0,0 +1,152 @@
|
|||||||
|
/*
|
||||||
|
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||||
|
* Copyright 2020-2026 emsesp.org
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "emsesp.h"
|
||||||
|
|
||||||
|
#include "httpClient.h"
|
||||||
|
|
||||||
|
#include <WiFiClient.h>
|
||||||
|
#include <ESP_SSLClient.h>
|
||||||
|
|
||||||
|
namespace emsesp {
|
||||||
|
|
||||||
|
int HttpClient::request(std::string url, const std::string & method, const std::string & value, JsonObjectConst headers, std::string & result) {
|
||||||
|
int httpResult = 0;
|
||||||
|
const bool is_post = value.length() || Helpers::toLower(method) == "post";
|
||||||
|
const auto lower_url = Helpers::toLower(url.c_str());
|
||||||
|
|
||||||
|
const bool is_https = lower_url.starts_with("https://");
|
||||||
|
if (!is_https && !lower_url.starts_with("http://")) {
|
||||||
|
return 0; // unsupported scheme
|
||||||
|
}
|
||||||
|
|
||||||
|
WiFiClient * basic_client = new WiFiClient;
|
||||||
|
ESP_SSLClient * ssl_client = new ESP_SSLClient;
|
||||||
|
if (is_https) {
|
||||||
|
ssl_client->setInsecure();
|
||||||
|
// Notes: with root CA we should set here: ssl_client->setCACert(rootCACert);
|
||||||
|
// 1 KB RX buffer is fine for small JSON-style endpoints used by the scheduler/shunting-yard but it is NOT enough for servers that send full-size TLS records (>1 KB)
|
||||||
|
ssl_client->setBufferSizes(16384, 1024);
|
||||||
|
ssl_client->setSessionTimeout(120); // Set the timeout in seconds (>=120 seconds)
|
||||||
|
}
|
||||||
|
// WiFiClient is NetworkClient, which declares no setTimeout() of its own - calling it binds to
|
||||||
|
// Stream::setTimeout() and only affects readBytes(), leaving the socket on the core's 3s default
|
||||||
|
basic_client->setConnectionTimeout(CONNECT_TIMEOUT_MS);
|
||||||
|
ssl_client->setTimeout(5); // seconds, drives BearSSL only - unused on the plain HTTP path
|
||||||
|
ssl_client->setClient(basic_client, is_https); // enableSSL = false for plain HTTP
|
||||||
|
|
||||||
|
url.replace(0, is_https ? 8 : 7, "");
|
||||||
|
std::string host = url;
|
||||||
|
auto index = url.find_first_of('/');
|
||||||
|
if (index != std::string::npos) {
|
||||||
|
host = url.substr(0, index);
|
||||||
|
url.replace(0, index, "");
|
||||||
|
} else {
|
||||||
|
url = "/";
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint16_t port = is_https ? 443 : 80;
|
||||||
|
if (ssl_client->connect(host.c_str(), port)) {
|
||||||
|
bool content_set = false;
|
||||||
|
bool agent_set = false;
|
||||||
|
|
||||||
|
// assemble the request in one buffer. Sent as a dozen small writes it is left to Nagle to
|
||||||
|
// dribble them out an ACK at a time, which on a slow link can cost more than the read budget
|
||||||
|
std::string req = (is_post ? "POST " : "GET ") + url + " HTTP/1.1\r\nHost: " + host + "\r\n";
|
||||||
|
for (JsonPairConst p : headers) {
|
||||||
|
const auto key = Helpers::toLower(p.key().c_str());
|
||||||
|
content_set |= (key == "content-type");
|
||||||
|
agent_set |= (key == "user-agent");
|
||||||
|
req += std::string(p.key().c_str()) + ": " + p.value().as<std::string>() + "\r\n";
|
||||||
|
}
|
||||||
|
if (!agent_set) {
|
||||||
|
req += "User-Agent: EMS-ESP\r\n"; // CDNs are more likely to stall on a request without one
|
||||||
|
}
|
||||||
|
if (is_post) {
|
||||||
|
if (!content_set) {
|
||||||
|
req += "Content-Type: ";
|
||||||
|
req += value.starts_with('{') ? asyncsrv::T_application_json : asyncsrv::T_text_plain;
|
||||||
|
req += "\r\n";
|
||||||
|
}
|
||||||
|
req += "Content-Length: " + std::to_string(value.length()) + "\r\n";
|
||||||
|
}
|
||||||
|
req += "Connection: close\r\n\r\n"; // the blank line terminates the headers - without it the server never responds
|
||||||
|
|
||||||
|
ssl_client->print(req.c_str());
|
||||||
|
if (is_post && value.length()) {
|
||||||
|
ssl_client->print(value.c_str()); // sent separately so the body isn't copied into req
|
||||||
|
}
|
||||||
|
|
||||||
|
// available() drops to zero between TCP segments, so stopping at the first gap truncates
|
||||||
|
// any response that doesn't arrive in a single packet. Keep reading until the peer closes,
|
||||||
|
// the stream goes idle, or the overall budget runs out
|
||||||
|
const uint32_t started = millis();
|
||||||
|
uint32_t last_data = started;
|
||||||
|
while (millis() - started < TOTAL_TIMEOUT_MS) {
|
||||||
|
const int avail = ssl_client->available();
|
||||||
|
if (avail > 0) {
|
||||||
|
uint8_t buf[128];
|
||||||
|
const size_t want = (avail < (int)sizeof(buf)) ? (size_t)avail : sizeof(buf);
|
||||||
|
const int len = ssl_client->read(buf, want);
|
||||||
|
if (len > 0) {
|
||||||
|
result.append(reinterpret_cast<const char *>(buf), len);
|
||||||
|
last_data = millis();
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!ssl_client->connected()) {
|
||||||
|
break; // closed, with nothing left buffered
|
||||||
|
}
|
||||||
|
// the server may take a while to start replying
|
||||||
|
if (millis() - last_data > (result.empty() ? FIRST_BYTE_TIMEOUT_MS : IDLE_TIMEOUT_MS)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
delay(1);
|
||||||
|
}
|
||||||
|
ssl_client->stop();
|
||||||
|
|
||||||
|
const auto received = result.length();
|
||||||
|
|
||||||
|
// parse the status line "HTTP/1.x <code> <reason>". stoi() would abort rather than throw on
|
||||||
|
// a malformed response, since the firmware is built with -fno-exceptions
|
||||||
|
if (result.starts_with("HTTP/")) {
|
||||||
|
index = result.find_first_of(' ');
|
||||||
|
if (index != std::string::npos) {
|
||||||
|
httpResult = Helpers::atoint(result.c_str() + index + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
index = result.find("\r\n\r\n");
|
||||||
|
if (index != std::string::npos) {
|
||||||
|
result.replace(0, index + 4, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (httpResult == 0) {
|
||||||
|
// the TCP connect worked but nothing usable came back
|
||||||
|
EMSESP::logger().warning("%s no valid response from %s (%u bytes)", is_https ? "HTTPS" : "HTTP", host.c_str(), (unsigned)received);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
EMSESP::logger().warning("%s connection to %s failed", is_https ? "HTTPS" : "HTTP", host.c_str());
|
||||||
|
}
|
||||||
|
delete ssl_client;
|
||||||
|
delete basic_client;
|
||||||
|
|
||||||
|
return httpResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace emsesp
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
/*
|
||||||
|
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||||
|
* Copyright 2020-2026 emsesp.org
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef EMSESP_HTTPCLIENT_H
|
||||||
|
#define EMSESP_HTTPCLIENT_H
|
||||||
|
|
||||||
|
#include <ArduinoJson.h>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace emsesp {
|
||||||
|
|
||||||
|
class HttpClient {
|
||||||
|
public:
|
||||||
|
static int request(std::string url, const std::string & method, const std::string & value, JsonObjectConst headers, std::string & result);
|
||||||
|
|
||||||
|
private:
|
||||||
|
static constexpr uint32_t CONNECT_TIMEOUT_MS = 5000; // TCP connect, the core defaults to 3s
|
||||||
|
static constexpr uint32_t FIRST_BYTE_TIMEOUT_MS = 8000; // how long the server may take to start replying
|
||||||
|
static constexpr uint32_t IDLE_TIMEOUT_MS = 500; // gap in the stream that marks the end of a response
|
||||||
|
static constexpr uint32_t TOTAL_TIMEOUT_MS = 10000; // ceiling for the whole read
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace emsesp
|
||||||
|
|
||||||
|
#endif
|
||||||
+48
-29
@@ -59,50 +59,60 @@ bool LED::loop(uint8_t healthcheck, bool button_busy) {
|
|||||||
// the user-requested LED blink always has preference
|
// the user-requested LED blink always has preference
|
||||||
if (!is_user_led_blink_) {
|
if (!is_user_led_blink_) {
|
||||||
// check for button press.
|
// check for button press.
|
||||||
// if button is pressed, show LED (yellow on RGB LED, on/off on standard LED)
|
// while the button is held show the LED (yellow on RGB LED, on/off on standard LED),
|
||||||
// it will turn off on the next loop cycle
|
// and on release go back to the default state and re-check the health on the next cycle
|
||||||
if (last_button_busy_ != button_busy) {
|
if (last_button_busy_ != button_busy) {
|
||||||
last_button_busy_ = button_busy;
|
last_button_busy_ = button_busy;
|
||||||
set_led(button_busy ? Color::OFF : Color::YELLOW); // Yellow
|
if (button_busy) {
|
||||||
|
set_led(Color::YELLOW); // Yellow
|
||||||
|
} else {
|
||||||
|
reset_led();
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// check the system health.
|
// check the system health.
|
||||||
// Set the sequence accordingly, only if the healthcheck is not 0 and has changed
|
// Set the sequence accordingly, only if the healthcheck is not 0 and has changed
|
||||||
if (healthcheck != previous_healthcheck_) {
|
if (healthcheck != previous_healthcheck_) {
|
||||||
previous_healthcheck_ = healthcheck;
|
color_steps_[0] = Color::OFF;
|
||||||
color_steps_[0] = Color::OFF;
|
color_steps_[1] = Color::OFF;
|
||||||
color_steps_[1] = Color::OFF;
|
color_steps_[2] = Color::OFF;
|
||||||
color_steps_[2] = Color::OFF;
|
|
||||||
|
|
||||||
// if the healthcheck is 0, i.e. system is healthy, reset the LED
|
// if the healthcheck is 0, i.e. system is healthy, reset the LED
|
||||||
if (healthcheck == 0) {
|
if (healthcheck == 0) {
|
||||||
reset_led();
|
reset_led();
|
||||||
return false;
|
} else {
|
||||||
|
// 1 flash (blue) is the EMS bus is not connected
|
||||||
|
// 2 flashes (red, red) if the network (wifi or ethernet) is not connected
|
||||||
|
// 3 flashes (red, red, blue) is both the bus and the network are not connected
|
||||||
|
bool no_network = (healthcheck & System::HEALTHCHECK_NO_NETWORK) == System::HEALTHCHECK_NO_NETWORK;
|
||||||
|
bool no_bus = (healthcheck & System::HEALTHCHECK_NO_BUS) == System::HEALTHCHECK_NO_BUS;
|
||||||
|
|
||||||
|
// set step 1
|
||||||
|
if (no_network) {
|
||||||
|
color_steps_[0] = Color::RED; // red, no network
|
||||||
|
} else if (no_bus) {
|
||||||
|
color_steps_[0] = Color::BLUE; // blue, no bus
|
||||||
|
}
|
||||||
|
|
||||||
|
// set step 2
|
||||||
|
if (no_network) {
|
||||||
|
color_steps_[1] = Color::RED; // red, no network
|
||||||
|
}
|
||||||
|
|
||||||
|
// set step 3
|
||||||
|
if (no_network && no_bus) {
|
||||||
|
color_steps_[2] = Color::BLUE; // blue, no network and no bus
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1 flash (blue) is the EMS bus is not connected
|
previous_healthcheck_ = healthcheck; // must be set after reset_led(), which invalidates it
|
||||||
// 2 flashes (red, red) if the network (wifi or ethernet) is not connected
|
}
|
||||||
// 3 flashes (red, red, blue) is both the bus and the network are not connected
|
|
||||||
bool no_network = (healthcheck & System::HEALTHCHECK_NO_NETWORK) == System::HEALTHCHECK_NO_NETWORK;
|
|
||||||
bool no_bus = (healthcheck & System::HEALTHCHECK_NO_BUS) == System::HEALTHCHECK_NO_BUS;
|
|
||||||
|
|
||||||
// set step 1
|
// there's nothing to flash, so leave the LED in the state reset_led() gave it.
|
||||||
if (no_network) {
|
// Running the sequence here would keep writing Color::OFF and a healthy system would never show green.
|
||||||
color_steps_[0] = Color::RED; // red, no network
|
if (healthcheck == 0 || button_busy) {
|
||||||
} else if (no_bus) {
|
return false;
|
||||||
color_steps_[0] = Color::BLUE; // blue, no bus
|
|
||||||
}
|
|
||||||
|
|
||||||
// set step 2
|
|
||||||
if (no_network) {
|
|
||||||
color_steps_[1] = Color::RED; // red, no network
|
|
||||||
}
|
|
||||||
|
|
||||||
// set step 3
|
|
||||||
if (no_network && no_bus) {
|
|
||||||
color_steps_[2] = Color::BLUE; // blue, no network and no bus
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -119,6 +129,15 @@ void LED::reset_led() {
|
|||||||
color_steps_[0] = Color::OFF;
|
color_steps_[0] = Color::OFF;
|
||||||
color_steps_[1] = Color::OFF;
|
color_steps_[1] = Color::OFF;
|
||||||
color_steps_[2] = Color::OFF;
|
color_steps_[2] = Color::OFF;
|
||||||
|
|
||||||
|
// invalidate the last health state so the sequence is rebuilt on the next check,
|
||||||
|
// needed because the color steps above have been cleared
|
||||||
|
previous_healthcheck_ = System::HEALTHCHECK_RESET;
|
||||||
|
|
||||||
|
// rewind the sequence so it starts from the top if the health degrades again
|
||||||
|
led_long_timer_ = 1; // 1 will kick it off immediately
|
||||||
|
led_short_timer_ = 0;
|
||||||
|
led_flash_step_ = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// LED flash every few ms and then perform a factory reset
|
// LED flash every few ms and then perform a factory reset
|
||||||
|
|||||||
@@ -430,7 +430,7 @@ MAKE_TRANSLATION(pumpMode, "pumpmode", "boiler pump mode", "Kesselpumpenmodus",
|
|||||||
MAKE_TRANSLATION(pumpCharacter, "pumpcharacter", "boiler pump characteristic", "Charakteristik der Kesselpumpe", "karakteristiek ketelpomp", "pannpumpsegenskaper", "charakterystyka pompy kotłowej", "kjelepumpekarakteristikk", "caractéristique de la pompe de la chaudière", "kazan pompası karakteristiği", "caratteristica della pompa della caldaia", "charakteristika kotlového čerpadla", "charakteristika čerpadla kotle")
|
MAKE_TRANSLATION(pumpCharacter, "pumpcharacter", "boiler pump characteristic", "Charakteristik der Kesselpumpe", "karakteristiek ketelpomp", "pannpumpsegenskaper", "charakterystyka pompy kotłowej", "kjelepumpekarakteristikk", "caractéristique de la pompe de la chaudière", "kazan pompası karakteristiği", "caratteristica della pompa della caldaia", "charakteristika kotlového čerpadla", "charakteristika čerpadla kotle")
|
||||||
MAKE_TRANSLATION(pumpOnTemp, "pumpontemp", "pump logic temperature", "Pumpenlogiktemperatur", "pomplogica temperatuur", "Pumplogiktemperatur", "temperatura logiki pompy", "pumpelogikktemperatur", "température logique pompe", "pompa mantık sıcaklığı", "temperatura logica pompa", "teplota logiky čerpadla", "teplota logiky čerpadla")
|
MAKE_TRANSLATION(pumpOnTemp, "pumpontemp", "pump logic temperature", "Pumpenlogiktemperatur", "pomplogica temperatuur", "Pumplogiktemperatur", "temperatura logiki pompy", "pumpelogikktemperatur", "température logique pompe", "pompa mantık sıcaklığı", "temperatura logica pompa", "teplota logiky čerpadla", "teplota logiky čerpadla")
|
||||||
MAKE_TRANSLATION(headertemp, "headertemp", "low loss header", "Hydr. Weiche", "open verdeler", "Fördelare", "sprzęgło hydrauliczne", "lav tap header", "bouteille de déc. hydr.", "isı bloğu gidiş suyu sıc.", "comp. idr.", "nízkostratová hlavica", "hydraulický oddělovač")
|
MAKE_TRANSLATION(headertemp, "headertemp", "low loss header", "Hydr. Weiche", "open verdeler", "Fördelare", "sprzęgło hydrauliczne", "lav tap header", "bouteille de déc. hydr.", "isı bloğu gidiş suyu sıc.", "comp. idr.", "nízkostratová hlavica", "hydraulický oddělovač")
|
||||||
MAKE_TRANSLATION(heatblock, "heatblock", "heating block", "Wärmezelle", "Aanvoertemp. warmtecel", "Värmeblock", "blok grzewczy", "varmeblokk", "départ corps de chauffe", "Hid.denge kabı sıcaklığı", "mandata scamb. pr.", "vykurovací blok", "blok topení")
|
MAKE_TRANSLATION(heatblock, "heatblock", "Heatblock temperature", "Wärmeblocktemperatur", "Heatblock temperatuur", "Värmeblocktemperatur", "temperatura bloku grzewczego", "varmeblokk temperatur", "température du bloc chauffant", "ısı bloğu sıcaklığı", "temperatura blocco riscaldante", "teplota tepelného bloku", "teplota tepelného bloku")
|
||||||
MAKE_TRANSLATION(pumpKickHour, "pumpkickhour", "pump kick hour", "Stunde Pumpkick")
|
MAKE_TRANSLATION(pumpKickHour, "pumpkickhour", "pump kick hour", "Stunde Pumpkick")
|
||||||
MAKE_TRANSLATION(pumpKickDay, "pumpkickday", "pump kick day", "Tag Pumpkick")
|
MAKE_TRANSLATION(pumpKickDay, "pumpkickday", "pump kick day", "Tag Pumpkick")
|
||||||
MAKE_TRANSLATION(pumpKickDelay, "pumpkickdelay", "pump kick delay", "Pause vor Pumpkick")
|
MAKE_TRANSLATION(pumpKickDelay, "pumpkickdelay", "pump kick delay", "Pause vor Pumpkick")
|
||||||
@@ -581,7 +581,7 @@ MAKE_TRANSLATION(lowNoiseMode, "lownoisemode", "low noise mode", "Geräuscharmer
|
|||||||
MAKE_TRANSLATION(lowNoiseStart, "lownoisestart", "low noise starttime", "Start geräuscharmer Betrieb", "Start stil bedrijf", "Tyst drift starttid", "początek trybu cichego", "stille modu starttid", "heure démarrage faible bruit", "düşük ses başlangıç", "ora di avvio a basso rumore", "nízka hlučnosť spustenia", "čas začátku nízkého hluku")
|
MAKE_TRANSLATION(lowNoiseStart, "lownoisestart", "low noise starttime", "Start geräuscharmer Betrieb", "Start stil bedrijf", "Tyst drift starttid", "początek trybu cichego", "stille modu starttid", "heure démarrage faible bruit", "düşük ses başlangıç", "ora di avvio a basso rumore", "nízka hlučnosť spustenia", "čas začátku nízkého hluku")
|
||||||
MAKE_TRANSLATION(lowNoiseStop, "lownoisestop", "low noise stoptime", "Stopp geräuscharmer Betrieb", "Stop stil bedrijf", "Tyst drift stopptid", "koniec trybu cichego", "stille modus stopptid", "heure arrêt faible bruit", "düşük ses bitiş", "ora di arresto funzionamento silenzioso", "doba zastavenia s nízkou hlučnosťou", "čas konce nízkého hluku")
|
MAKE_TRANSLATION(lowNoiseStop, "lownoisestop", "low noise stoptime", "Stopp geräuscharmer Betrieb", "Stop stil bedrijf", "Tyst drift stopptid", "koniec trybu cichego", "stille modus stopptid", "heure arrêt faible bruit", "düşük ses bitiş", "ora di arresto funzionamento silenzioso", "doba zastavenia s nízkou hlučnosťou", "čas konce nízkého hluku")
|
||||||
MAKE_TRANSLATION(energyPriceGas, "energypricegas", "energy price gas", "Energiepreis Gas", "Energieprijs gas", "Gaspris", "cena energii z gazu", "energipris gass", "prix énergie gaz", "gaz enerjisi fiyatı", "prezzo energia gas", "cena energie plyn", "cena energie plyn")
|
MAKE_TRANSLATION(energyPriceGas, "energypricegas", "energy price gas", "Energiepreis Gas", "Energieprijs gas", "Gaspris", "cena energii z gazu", "energipris gass", "prix énergie gaz", "gaz enerjisi fiyatı", "prezzo energia gas", "cena energie plyn", "cena energie plyn")
|
||||||
MAKE_TRANSLATION(energyPriceEl, "energypriceel", "energy price electric", "Energiepreis Eletrizität", "energieprijs electriciteit", "Elpris", "cena energii elektrycznej", "strømpris", "prix énergie électrique", "elektrik enerjisi fiyatı", "prezzo energia elettrica", "cena elektrickej energie", "cena energie elektřina")
|
MAKE_TRANSLATION(energyPriceEl, "energypriceel", "energy price electric", "Energiepreis Elektrizität", "energieprijs electriciteit", "Elpris", "cena energii elektrycznej", "strømpris", "prix énergie électrique", "elektrik enerjisi fiyatı", "prezzo energia elettrica", "cena elektrickej energie", "cena energie elektřina")
|
||||||
MAKE_TRANSLATION(energyPricePV, "energyfeedpv", "feed in PV", "PV-Einspeisevergütung", "PV teruglevertarief", "PV Energi", "cena energii PV", "strømpris PV", "alimentation PV", "giren güneş enerjisi", "energia fotovoltaico", "Výkupná cena FV", "přetok FV")
|
MAKE_TRANSLATION(energyPricePV, "energyfeedpv", "feed in PV", "PV-Einspeisevergütung", "PV teruglevertarief", "PV Energi", "cena energii PV", "strømpris PV", "alimentation PV", "giren güneş enerjisi", "energia fotovoltaico", "Výkupná cena FV", "přetok FV")
|
||||||
MAKE_TRANSLATION(hybridDHW, "hybriddhw", "hybrid DHW", "Hybrid-Warmwasser", "Hybride ww", "Hybridläge varmvatten", "hybrydowa c.w.u.", "hybridmodus varmtvann", "ecs hybride", "hibrit SKS", "ACS ibrida", "hybridná TÚV", "hybridní TUV")
|
MAKE_TRANSLATION(hybridDHW, "hybriddhw", "hybrid DHW", "Hybrid-Warmwasser", "Hybride ww", "Hybridläge varmvatten", "hybrydowa c.w.u.", "hybridmodus varmtvann", "ecs hybride", "hibrit SKS", "ACS ibrida", "hybridná TÚV", "hybridní TUV")
|
||||||
MAKE_TRANSLATION(airPurgeMode, "airpurgemode", "air purge mode", "Luftspülung", "Luchtzuivering", "Luftreningsläge", "tryb oczyszczania powietrza", "luftsrensningsmodus", "mode purge air", "hava temizleme modu", "modalita spurgo aria", "režim čistenia vzduchu", "režim odvzdušnění")
|
MAKE_TRANSLATION(airPurgeMode, "airpurgemode", "air purge mode", "Luftspülung", "Luchtzuivering", "Luftreningsläge", "tryb oczyszczania powietrza", "luftsrensningsmodus", "mode purge air", "hava temizleme modu", "modalita spurgo aria", "režim čistenia vzduchu", "režim odvzdušnění")
|
||||||
|
|||||||
@@ -28,4 +28,7 @@ void setup() {
|
|||||||
|
|
||||||
void loop() {
|
void loop() {
|
||||||
application.loop();
|
application.loop();
|
||||||
|
#ifndef EMSESP_STANDALONE
|
||||||
|
delay(1); // block for a tick so the idle task gets to run and the core can clock-gate
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -179,28 +179,29 @@ const std::initializer_list<Modbus::EntityModbusInfo> Modbus::modbus_register_ma
|
|||||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(fanWork), 257, 1), // fanwork
|
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(fanWork), 257, 1), // fanwork
|
||||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(ignWork), 258, 1), // ignwork
|
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(ignWork), 258, 1), // ignwork
|
||||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(oilPreHeat), 259, 1), // oilpreheat
|
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(oilPreHeat), 259, 1), // oilpreheat
|
||||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(burnMaxPower), 260, 1), // burnmaxpower
|
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(burnMinPower), 260, 1), // burnminpower
|
||||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(burnMinPeriod), 261, 1), // burnminperiod
|
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(burnMaxPower), 261, 1), // burnmaxpower
|
||||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(absBurnPow), 262, 1), // absburnpow
|
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(burnMinPeriod), 262, 1), // burnminperiod
|
||||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(heatblock), 263, 1), // heatblock
|
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(absBurnPow), 263, 1), // absburnpow
|
||||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(boilHystOn), 264, 1), // boilhyston
|
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(heatblock), 264, 1), // heatblock
|
||||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(boilHystOff), 265, 1), // boilhystoff
|
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(boilHystOn), 265, 1), // boilhyston
|
||||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(boil2HystOn), 266, 1), // boil2hyston
|
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(boilHystOff), 266, 1), // boilhystoff
|
||||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(boil2HystOff), 267, 1), // boil2hystoff
|
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(boil2HystOn), 267, 1), // boil2hyston
|
||||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(curveOn), 268, 1), // curveon
|
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(boil2HystOff), 268, 1), // boil2hystoff
|
||||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(curveBase), 269, 1), // curvebase
|
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(curveOn), 269, 1), // curveon
|
||||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(curveEnd), 270, 1), // curveend
|
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(curveBase), 270, 1), // curvebase
|
||||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(summertemp), 271, 1), // summertemp
|
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(curveEnd), 271, 1), // curveend
|
||||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(nofrostmode), 272, 1), // nofrostmode
|
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(summertemp), 272, 1), // summertemp
|
||||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(nofrosttemp), 273, 1), // nofrosttemp
|
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(nofrostmode), 273, 1), // nofrostmode
|
||||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(gasMeterHeat), 274, 2), // gasmeterheat
|
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(nofrosttemp), 274, 1), // nofrosttemp
|
||||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(nrgHeat2), 276, 2), // nrgheat2
|
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(gasMeterHeat), 275, 2), // gasmeterheat
|
||||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(nomPower), 278, 1), // nompower
|
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(nrgHeat2), 277, 2), // nrgheat2
|
||||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(netFlowTemp), 279, 1), // netflowtemp
|
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(nomPower), 279, 1), // nompower
|
||||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(heatValve), 280, 1), // heatvalve
|
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(netFlowTemp), 280, 1), // netflowtemp
|
||||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(keepWarmTemp), 281, 1), // keepwarmtemp
|
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(heatValve), 281, 1), // heatvalve
|
||||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(setReturnTemp), 282, 1), // setreturntemp
|
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(keepWarmTemp), 282, 1), // keepwarmtemp
|
||||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(heatingOn), 283, 1), // heating
|
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(setReturnTemp), 283, 1), // setreturntemp
|
||||||
|
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(heatingOn), 284, 1), // heating
|
||||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DHW, FL_(nrgWw), 0, 2), // nrg
|
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DHW, FL_(nrgWw), 0, 2), // nrg
|
||||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DHW, FL_(meterWw), 2, 2), // meter
|
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DHW, FL_(meterWw), 2, 2), // meter
|
||||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DHW, FL_(upTimeCompWw), 4, 2), // uptimecomp
|
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DHW, FL_(upTimeCompWw), 4, 2), // uptimecomp
|
||||||
|
|||||||
+78
-54
@@ -51,7 +51,7 @@ bool Mqtt::publish_single2cmd_;
|
|||||||
std::vector<Mqtt::MQTTSubFunction, AllocatorPSRAM<Mqtt::MQTTSubFunction>> Mqtt::mqtt_subfunctions_;
|
std::vector<Mqtt::MQTTSubFunction, AllocatorPSRAM<Mqtt::MQTTSubFunction>> Mqtt::mqtt_subfunctions_;
|
||||||
|
|
||||||
uint32_t Mqtt::mqtt_publish_fails_ = 0;
|
uint32_t Mqtt::mqtt_publish_fails_ = 0;
|
||||||
bool Mqtt::connecting_ = false;
|
bool Mqtt::connected_ = false;
|
||||||
bool Mqtt::initialized_ = false;
|
bool Mqtt::initialized_ = false;
|
||||||
uint16_t Mqtt::queuecount_ = 0;
|
uint16_t Mqtt::queuecount_ = 0;
|
||||||
uint8_t Mqtt::connectcount_ = 0;
|
uint8_t Mqtt::connectcount_ = 0;
|
||||||
@@ -313,6 +313,31 @@ void Mqtt::on_publish(uint16_t packetId) const {
|
|||||||
LOG_DEBUG("Packet %d sent successful", packetId);
|
LOG_DEBUG("Packet %d sent successful", packetId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// gracefully disconnect from the broker by sending a proper MQTT DISCONNECT packet.
|
||||||
|
// this is called just before a restart so the broker ends our session cleanly
|
||||||
|
// (avoids a lingering session that shows up as "session taken over" on reconnect).
|
||||||
|
//
|
||||||
|
// the disconnect is deferred inside espMqttClient - the DISCONNECT packet is only
|
||||||
|
// queued and actually sent when loop() runs the state machine. On PSRAM boards the
|
||||||
|
// client has its own task that keeps pumping loop(), so it flushes on its own. On
|
||||||
|
// non-PSRAM boards (UseInternalTask::NO) nothing drives loop() once we're mid-restart,
|
||||||
|
// so we pump it here until the client is fully disconnected, bounded by a timeout.
|
||||||
|
void Mqtt::disconnect() {
|
||||||
|
if (!mqttClient_) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mqttClient_->disconnect(); // graceful - queues the DISCONNECT packet
|
||||||
|
|
||||||
|
if (EMSESP::system_.PSram() == 0) {
|
||||||
|
uint32_t start = uuid::get_uptime();
|
||||||
|
while (!mqttClient_->disconnected() && (uuid::get_uptime() - start) < MQTT_DISCONNECT_TIMEOUT) {
|
||||||
|
mqttClient_->loop();
|
||||||
|
delay(2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// called when MQTT settings have changed via the MQTT Settings or Application Settings Web pages
|
// called when MQTT settings have changed via the MQTT Settings or Application Settings Web pages
|
||||||
void Mqtt::reset_mqtt() {
|
void Mqtt::reset_mqtt() {
|
||||||
if (!enabled()) {
|
if (!enabled()) {
|
||||||
@@ -326,6 +351,7 @@ void Mqtt::reset_mqtt() {
|
|||||||
if (mqttClient_->connected()) {
|
if (mqttClient_->connected()) {
|
||||||
mqttClient_->disconnect(true); // force a disconnect
|
mqttClient_->disconnect(true); // force a disconnect
|
||||||
}
|
}
|
||||||
|
load_settings(); // reload MQTT settings
|
||||||
}
|
}
|
||||||
|
|
||||||
// load the settings from service
|
// load the settings from service
|
||||||
@@ -446,30 +472,39 @@ bool Mqtt::get_publish_onchange(uint8_t device_type) {
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
void Mqtt::on_disconnect(espMqttClientTypes::DisconnectReason reason) {
|
|
||||||
// only show the error once, not every 2 seconds
|
|
||||||
if (!connecting_) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
connecting_ = false;
|
|
||||||
connectcount_++; // count # reconnects
|
|
||||||
|
|
||||||
if (reason == espMqttClientTypes::DisconnectReason::TCP_DISCONNECTED) {
|
// human-readable cause for a MQTT disconnect reason, nullptr if not recognized
|
||||||
LOG_WARNING("MQTT disconnected: TCP");
|
static const char * mqtt_disconnect_cause(espMqttClientTypes::DisconnectReason reason) {
|
||||||
} else if (reason == espMqttClientTypes::DisconnectReason::MQTT_UNACCEPTABLE_PROTOCOL_VERSION) {
|
using espMqttClientTypes::DisconnectReason;
|
||||||
LOG_WARNING("MQTT disconnected: Unacceptable protocol version");
|
|
||||||
} else if (reason == espMqttClientTypes::DisconnectReason::MQTT_IDENTIFIER_REJECTED) {
|
switch (reason) {
|
||||||
LOG_WARNING("MQTT disconnected: Identifier Rejected");
|
case DisconnectReason::TCP_DISCONNECTED:
|
||||||
} else if (reason == espMqttClientTypes::DisconnectReason::MQTT_SERVER_UNAVAILABLE) {
|
return "TCP";
|
||||||
LOG_WARNING("MQTT disconnected: Server unavailable");
|
case DisconnectReason::MQTT_UNACCEPTABLE_PROTOCOL_VERSION:
|
||||||
} else if (reason == espMqttClientTypes::DisconnectReason::MQTT_MALFORMED_CREDENTIALS) {
|
return "Unaccepted protocol version";
|
||||||
LOG_WARNING("MQTT disconnected: Malformed credentials");
|
case DisconnectReason::MQTT_IDENTIFIER_REJECTED:
|
||||||
} else if (reason == espMqttClientTypes::DisconnectReason::MQTT_NOT_AUTHORIZED) {
|
return "Identifier Rejected";
|
||||||
LOG_WARNING("MQTT disconnected: Not authorized");
|
case DisconnectReason::MQTT_SERVER_UNAVAILABLE:
|
||||||
} else if (reason == espMqttClientTypes::DisconnectReason::TLS_BAD_FINGERPRINT) {
|
return "Server unavailable";
|
||||||
LOG_WARNING("MQTT disconnected: Server fingerprint invalid");
|
case DisconnectReason::MQTT_MALFORMED_CREDENTIALS:
|
||||||
|
return "Malformed credentials";
|
||||||
|
case DisconnectReason::MQTT_NOT_AUTHORIZED:
|
||||||
|
return "Not authorized";
|
||||||
|
case DisconnectReason::TLS_BAD_FINGERPRINT:
|
||||||
|
return "Server fingerprint invalid";
|
||||||
|
default:
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Mqtt::on_disconnect(espMqttClientTypes::DisconnectReason reason) {
|
||||||
|
connected_ = false;
|
||||||
|
|
||||||
|
const char * cause = mqtt_disconnect_cause(reason);
|
||||||
|
if (cause) {
|
||||||
|
LOG_ERROR("MQTT connection failure: %s. Retrying...", cause);
|
||||||
} else {
|
} else {
|
||||||
LOG_WARNING("MQTT disconnected: code %d", reason);
|
LOG_ERROR("MQTT connection failure: code %d. Retrying...", static_cast<int>(reason));
|
||||||
}
|
}
|
||||||
|
|
||||||
mqttClient_->clearQueue(true);
|
mqttClient_->clearQueue(true);
|
||||||
@@ -477,17 +512,16 @@ void Mqtt::on_disconnect(espMqttClientTypes::DisconnectReason reason) {
|
|||||||
|
|
||||||
// MQTT on_connect - when an MQTT connect is established
|
// MQTT on_connect - when an MQTT connect is established
|
||||||
void Mqtt::on_connect() {
|
void Mqtt::on_connect() {
|
||||||
if (connecting_) {
|
if (connected_) {
|
||||||
return; // prevent duplicated connections
|
return; // prevent duplicated connections
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG_INFO("MQTT connected");
|
LOG_INFO("MQTT connected");
|
||||||
|
|
||||||
connecting_ = true;
|
connected_ = true;
|
||||||
|
connectcount_++;
|
||||||
queuecount_ = mqttClient_->queueSize();
|
queuecount_ = mqttClient_->queueSize();
|
||||||
|
|
||||||
load_settings(); // reload MQTT settings - in case they have changes
|
|
||||||
|
|
||||||
if (ha_enabled_) {
|
if (ha_enabled_) {
|
||||||
queue_unsubscribe_message(discovery_prefix_ + "/+/" + Mqtt::basename() + "/#");
|
queue_unsubscribe_message(discovery_prefix_ + "/+/" + Mqtt::basename() + "/#");
|
||||||
EMSESP::reset_mqtt_ha(); // re-create all HA devices if there are any
|
EMSESP::reset_mqtt_ha(); // re-create all HA devices if there are any
|
||||||
@@ -587,9 +621,11 @@ void Mqtt::ha_status() {
|
|||||||
|
|
||||||
// These come from the info MQTT topic - and handled in the publish_ha_sensor_config function
|
// These come from the info MQTT topic - and handled in the publish_ha_sensor_config function
|
||||||
publish_system_ha_sensor_config(DeviceValueType::STRING, "Version", "version", DeviceValueUOM::NONE);
|
publish_system_ha_sensor_config(DeviceValueType::STRING, "Version", "version", DeviceValueUOM::NONE);
|
||||||
publish_system_ha_sensor_config(DeviceValueType::STRING, "Boot time", "bootTime", DeviceValueUOM::UPTIME);
|
publish_system_ha_sensor_config(DeviceValueType::STRING,
|
||||||
|
"Uptime",
|
||||||
|
"bootTime",
|
||||||
|
DeviceValueUOM::UPTIME); // called Uptime in HA and derives value from bootTime element in info topic
|
||||||
}
|
}
|
||||||
|
|
||||||
// add sub or pub task to the queue.
|
// add sub or pub task to the queue.
|
||||||
// the base is not included in the topic
|
// the base is not included in the topic
|
||||||
bool Mqtt::queue_message(const uint8_t operation, const std::string & topic, const std::string & payload, const bool retain) {
|
bool Mqtt::queue_message(const uint8_t operation, const std::string & topic, const std::string & payload, const bool retain) {
|
||||||
@@ -988,10 +1024,9 @@ bool Mqtt::publish_ha_sensor_config(uint8_t type, // EMSdev
|
|||||||
|
|
||||||
// set the entity_id. This is breaking change in HA 2025.10.0 - see https://github.com/home-assistant/core/pull/151775
|
// set the entity_id. This is breaking change in HA 2025.10.0 - see https://github.com/home-assistant/core/pull/151775
|
||||||
// extract the string from topic up to the / using std::string
|
// extract the string from topic up to the / using std::string
|
||||||
|
// it must be lowercase (although HA will slugify it and not reject it)
|
||||||
std::string topic_str(topic);
|
std::string topic_str(topic);
|
||||||
doc["def_ent_id"] = topic_str.substr(0, topic_str.find("/")) + "." + uniq_id;
|
doc["def_ent_id"] = Helpers::toLower(topic_str.substr(0, topic_str.find("/"))) + "." + Helpers::toLower(uniq_id);
|
||||||
|
|
||||||
char sample_val[30] = "0"; // sample, correct(!) entity value, used only to prevent warning/error in HA if real value is not published yet
|
|
||||||
|
|
||||||
// we add the command topic parameter for commands
|
// we add the command topic parameter for commands
|
||||||
if (has_cmd) {
|
if (has_cmd) {
|
||||||
@@ -1015,13 +1050,11 @@ bool Mqtt::publish_ha_sensor_config(uint8_t type, // EMSdev
|
|||||||
for (uint8_t i = 0; i < options_size; i++) {
|
for (uint8_t i = 0; i < options_size; i++) {
|
||||||
option_list.add(Helpers::itoa(i)); // as a string
|
option_list.add(Helpers::itoa(i)); // as a string
|
||||||
}
|
}
|
||||||
snprintf(sample_val, sizeof(sample_val), "'0'");
|
|
||||||
} else {
|
} else {
|
||||||
// use strings
|
// use strings
|
||||||
for (uint8_t i = 0; i < options_size; i++) {
|
for (uint8_t i = 0; i < options_size; i++) {
|
||||||
option_list.add(Helpers::translated_word(options[i]));
|
option_list.add(Helpers::translated_word(options[i]));
|
||||||
}
|
}
|
||||||
snprintf(sample_val, sizeof(sample_val), "'%s'", Helpers::translated_word(options[0]));
|
|
||||||
}
|
}
|
||||||
} else if (type != DeviceValueType::STRING && type != DeviceValueType::BOOL) {
|
} else if (type != DeviceValueType::STRING && type != DeviceValueType::BOOL) {
|
||||||
// For numeric's add the range and mode
|
// For numeric's add the range and mode
|
||||||
@@ -1043,7 +1076,6 @@ bool Mqtt::publish_ha_sensor_config(uint8_t type, // EMSdev
|
|||||||
if (dv_set_min != 0 || dv_set_max != 0) {
|
if (dv_set_min != 0 || dv_set_max != 0) {
|
||||||
doc["min"] = dv_set_min;
|
doc["min"] = dv_set_min;
|
||||||
doc["max"] = dv_set_max;
|
doc["max"] = dv_set_max;
|
||||||
snprintf(sample_val, sizeof(sample_val), "%i", dv_set_min);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1067,7 +1099,7 @@ bool Mqtt::publish_ha_sensor_config(uint8_t type, // EMSdev
|
|||||||
snprintf(stat_t, sizeof(stat_t), "~/%s", tag_to_topic(device_type, tag).c_str());
|
snprintf(stat_t, sizeof(stat_t), "~/%s", tag_to_topic(device_type, tag).c_str());
|
||||||
|
|
||||||
// Override - there are exceptions for DeviceType::SYSTEM, which uses the heartbeat topic
|
// Override - there are exceptions for DeviceType::SYSTEM, which uses the heartbeat topic
|
||||||
// and when fetching the version and bootTime we want to take this from the info topic instead
|
// and when fetching the version and uptime we want to take this from the info topic instead
|
||||||
if (device_type == EMSdevice::DeviceType::SYSTEM) {
|
if (device_type == EMSdevice::DeviceType::SYSTEM) {
|
||||||
// handle the exceptions
|
// handle the exceptions
|
||||||
if (strncmp(entity, "version", 7) == 0) {
|
if (strncmp(entity, "version", 7) == 0) {
|
||||||
@@ -1095,18 +1127,19 @@ bool Mqtt::publish_ha_sensor_config(uint8_t type, // EMSdev
|
|||||||
// has no unit of measure or icon, and must be true/false (not on/off or 1/0)
|
// has no unit of measure or icon, and must be true/false (not on/off or 1/0)
|
||||||
if (type == DeviceValueType::BOOL) {
|
if (type == DeviceValueType::BOOL) {
|
||||||
add_ha_bool(doc.as<JsonObject>());
|
add_ha_bool(doc.as<JsonObject>());
|
||||||
strlcpy(sample_val, "false", sizeof(sample_val)); // default is "false"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// don't bother with value template conditions if using Domoticz which doesn't fully support MQTT Discovery
|
// don't bother with value template conditions if using Domoticz which doesn't fully support MQTT Discovery
|
||||||
if (discovery_type() == discoveryType::HOMEASSISTANT) {
|
if (discovery_type() == discoveryType::HOMEASSISTANT) {
|
||||||
|
// handle special cases for value_template depending on the uom
|
||||||
if (uom == DeviceValueUOM::TIMESTAMP) {
|
if (uom == DeviceValueUOM::TIMESTAMP) {
|
||||||
// special case for timestamp, using "value_template": "{{ (value_json.bootTime | as_datetime).isoformat() }}",
|
// uptime
|
||||||
char val_tpl[100];
|
char val_tpl[100];
|
||||||
snprintf(val_tpl, sizeof(val_tpl), "{{ (value_json.%s | as_datetime).isoformat() }}", entity);
|
snprintf(val_tpl, sizeof(val_tpl), "{{ (value_json.%s | as_datetime).isoformat() }}", entity);
|
||||||
doc["val_tpl"] = val_tpl;
|
doc["val_tpl"] = val_tpl;
|
||||||
} else {
|
} else {
|
||||||
doc["val_tpl"] = (std::string) "{{" + val_obj + " if " + val_cond + " else " + sample_val + "}}";
|
// default
|
||||||
|
doc["val_tpl"] = (std::string) "{{" + val_obj + " if " + val_cond + " else 'None'}}";
|
||||||
}
|
}
|
||||||
add_ha_avty_section(doc.as<JsonObject>(), stat_t, val_cond); // adds availability section
|
add_ha_avty_section(doc.as<JsonObject>(), stat_t, val_cond); // adds availability section
|
||||||
} else {
|
} else {
|
||||||
@@ -1141,7 +1174,7 @@ bool Mqtt::publish_ha_sensor_config(uint8_t type, // EMSdev
|
|||||||
// https://developers.home-assistant.io/docs/core/entity/sensor/
|
// https://developers.home-assistant.io/docs/core/entity/sensor/
|
||||||
// Home Assistant tracks the min, max and mean value during the statistics period
|
// Home Assistant tracks the min, max and mean value during the statistics period
|
||||||
// For statistics, the sensor must have state_class set to either MEASUREMENT, TOTAL or TOTAL_INCREASING
|
// For statistics, the sensor must have state_class set to either MEASUREMENT, TOTAL or TOTAL_INCREASING
|
||||||
// When set to MEASUREMENT the device_class must cannot be DATE, ENUM, ENERGY, GAS, MONETARY, TIMESTAMP, VOLUME or WATER
|
// When set to MEASUREMENT the device_class must cannot be DATE, ENUM, ENERGY, GAS, MONETARY, TIMESTAMP, UPTIME, VOLUME or WATER
|
||||||
//
|
//
|
||||||
void Mqtt::add_ha_classes(JsonObject doc, const uint8_t device_type, const uint8_t type, const uint8_t uom, const char * entity, bool display_only) {
|
void Mqtt::add_ha_classes(JsonObject doc, const uint8_t device_type, const uint8_t type, const uint8_t uom, const char * entity, bool display_only) {
|
||||||
if (device_type == EMSdevice::DeviceType::SYSTEM) {
|
if (device_type == EMSdevice::DeviceType::SYSTEM) {
|
||||||
@@ -1280,7 +1313,7 @@ void Mqtt::add_ha_classes(JsonObject doc, const uint8_t device_type, const uint8
|
|||||||
doc[sc_ha] = sc_ha_measurement;
|
doc[sc_ha] = sc_ha_measurement;
|
||||||
doc[dc_ha] = "timestamp";
|
doc[dc_ha] = "timestamp";
|
||||||
break;
|
break;
|
||||||
case DeviceValueUOM::UPTIME:
|
case DeviceValueUOM::UPTIME: // no state class for uptime
|
||||||
doc[dc_ha] = "uptime";
|
doc[dc_ha] = "uptime";
|
||||||
break;
|
break;
|
||||||
case DeviceValueUOM::MV:
|
case DeviceValueUOM::MV:
|
||||||
@@ -1403,7 +1436,7 @@ bool Mqtt::publish_ha_climate_config(const DeviceValue & dv, const bool has_room
|
|||||||
|
|
||||||
snprintf(mode_str_tpl,
|
snprintf(mode_str_tpl,
|
||||||
sizeof(mode_str_tpl),
|
sizeof(mode_str_tpl),
|
||||||
"{%%if %s%%}off{%%elif %s=='%s'%%}heat{%%elif %s=='%s'%%}heat{%%elif %s=='%s'%%}heat{%%elif %s=='%s'%%}off{%%elif %s=='%s'%%}off{%%elif "
|
"{%%if %s%%}None{%%elif %s=='%s'%%}heat{%%elif %s=='%s'%%}heat{%%elif %s=='%s'%%}heat{%%elif %s=='%s'%%}off{%%elif %s=='%s'%%}off{%%elif "
|
||||||
"%s=='%s'%%}off{%%else%%}auto{%%endif%%}",
|
"%s=='%s'%%}off{%%else%%}auto{%%endif%%}",
|
||||||
hc_mode_cond,
|
hc_mode_cond,
|
||||||
hc_mode_s,
|
hc_mode_s,
|
||||||
@@ -1440,15 +1473,15 @@ bool Mqtt::publish_ha_climate_config(const DeviceValue & dv, const bool has_room
|
|||||||
doc["mode_stat_tpl"] = mode_str_tpl;
|
doc["mode_stat_tpl"] = mode_str_tpl;
|
||||||
doc["temp_cmd_t"] = temp_cmd_s;
|
doc["temp_cmd_t"] = temp_cmd_s;
|
||||||
doc["temp_stat_t"] = topic_t;
|
doc["temp_stat_t"] = topic_t;
|
||||||
doc["temp_stat_tpl"] = (std::string) "{{" + seltemp_s + " if " + seltemp_cond + " else 0}}";
|
doc["temp_stat_tpl"] = (std::string) "{{" + seltemp_s + " if " + seltemp_cond + " else 'None'}}";
|
||||||
|
|
||||||
if (has_roomtemp) {
|
if (has_roomtemp) {
|
||||||
doc["curr_temp_t"] = topic_t;
|
doc["curr_temp_t"] = topic_t;
|
||||||
doc["curr_temp_tpl"] = (std::string) "{{" + currtemp_s + " if " + currtemp_cond + " else 0}}";
|
doc["curr_temp_tpl"] = (std::string) "{{" + currtemp_s + " if " + currtemp_cond + " else 'None'}}";
|
||||||
}
|
}
|
||||||
if (tag >= DeviceValueTAG::TAG_SRC1) {
|
if (tag >= DeviceValueTAG::TAG_SRC1) {
|
||||||
doc["curr_hum_t"] = topic_t;
|
doc["curr_hum_t"] = topic_t;
|
||||||
doc["curr_hum_tpl"] = (std::string) "{{" + currhum_s + " if " + currhum_cond + " else 0}}";
|
doc["curr_hum_tpl"] = (std::string) "{{" + currhum_s + " if " + currhum_cond + " else 'None'}}";
|
||||||
}
|
}
|
||||||
|
|
||||||
doc["min_temp"] = Helpers::render_value(min_s, min, 0, EMSESP::system_.fahrenheit() ? 2 : 0);
|
doc["min_temp"] = Helpers::render_value(min_s, min, 0, EMSESP::system_.fahrenheit() ? 2 : 0);
|
||||||
@@ -1602,9 +1635,6 @@ void Mqtt::add_ha_avty_section(JsonObject doc, const char * state_t, const char
|
|||||||
avty_json["t"] = state;
|
avty_json["t"] = state;
|
||||||
snprintf(tpl, sizeof(tpl), "{{'online' if %s else 'offline'}}", cond1);
|
snprintf(tpl, sizeof(tpl), "{{'online' if %s else 'offline'}}", cond1);
|
||||||
avty_json["val_tpl"] = tpl;
|
avty_json["val_tpl"] = tpl;
|
||||||
if (!avty.add(avty_json)) {
|
|
||||||
LOG_WARNING("Failed to add availability condition 1 (low memory)");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// condition 2
|
// condition 2
|
||||||
@@ -1613,9 +1643,6 @@ void Mqtt::add_ha_avty_section(JsonObject doc, const char * state_t, const char
|
|||||||
avty_json["t"] = state;
|
avty_json["t"] = state;
|
||||||
snprintf(tpl, sizeof(tpl), "{{'online' if %s else 'offline'}}", cond2);
|
snprintf(tpl, sizeof(tpl), "{{'online' if %s else 'offline'}}", cond2);
|
||||||
avty_json["val_tpl"] = tpl;
|
avty_json["val_tpl"] = tpl;
|
||||||
if (!avty.add(avty_json)) {
|
|
||||||
LOG_WARNING("Failed to add availability condition 2 (low memory)");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// negative condition
|
// negative condition
|
||||||
@@ -1624,9 +1651,6 @@ void Mqtt::add_ha_avty_section(JsonObject doc, const char * state_t, const char
|
|||||||
avty_json["t"] = state;
|
avty_json["t"] = state;
|
||||||
snprintf(tpl, sizeof(tpl), "{{'offline' if %s else 'online'}}", negcond);
|
snprintf(tpl, sizeof(tpl), "{{'offline' if %s else 'online'}}", negcond);
|
||||||
avty_json["val_tpl"] = tpl;
|
avty_json["val_tpl"] = tpl;
|
||||||
if (!avty.add(avty_json)) {
|
|
||||||
LOG_WARNING("Failed to add negative availability condition (low memory)");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+5
-8
@@ -61,8 +61,9 @@ class Mqtt {
|
|||||||
enum Operation : uint8_t { PUBLISH, SUBSCRIBE, UNSUBSCRIBE };
|
enum Operation : uint8_t { PUBLISH, SUBSCRIBE, UNSUBSCRIBE };
|
||||||
enum NestedFormat : uint8_t { NESTED = 1, SINGLE };
|
enum NestedFormat : uint8_t { NESTED = 1, SINGLE };
|
||||||
|
|
||||||
static constexpr uint8_t MQTT_TOPIC_MAX_SIZE = 128; // fixed, not a user setting anymore
|
static constexpr uint8_t MQTT_TOPIC_MAX_SIZE = 128; // fixed, not a user setting anymore
|
||||||
static constexpr uint16_t MQTT_QUEUE_MAX_SIZE = 300;
|
static constexpr uint16_t MQTT_QUEUE_MAX_SIZE = 300;
|
||||||
|
static constexpr uint32_t MQTT_DISCONNECT_TIMEOUT = 1000; // max ms to flush a graceful disconnect on non-PSRAM boards
|
||||||
|
|
||||||
static void on_connect();
|
static void on_connect();
|
||||||
static void on_disconnect(espMqttClientTypes::DisconnectReason reason);
|
static void on_disconnect(espMqttClientTypes::DisconnectReason reason);
|
||||||
@@ -132,11 +133,7 @@ class Mqtt {
|
|||||||
return mqttClient_ ? mqttClient_->connected() : false;
|
return mqttClient_ ? mqttClient_->connected() : false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void disconnect() {
|
static void disconnect();
|
||||||
if (mqttClient_) {
|
|
||||||
mqttClient_->disconnect();
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool enabled() {
|
static bool enabled() {
|
||||||
return mqtt_enabled_;
|
return mqtt_enabled_;
|
||||||
@@ -300,7 +297,7 @@ class Mqtt {
|
|||||||
uint32_t last_publish_heartbeat_ = 0;
|
uint32_t last_publish_heartbeat_ = 0;
|
||||||
// uint32_t last_publish_queue_ = 0;
|
// uint32_t last_publish_queue_ = 0;
|
||||||
|
|
||||||
static bool connecting_;
|
static bool connected_;
|
||||||
static bool initialized_;
|
static bool initialized_;
|
||||||
static uint32_t mqtt_publish_fails_;
|
static uint32_t mqtt_publish_fails_;
|
||||||
static uint16_t queuecount_;
|
static uint16_t queuecount_;
|
||||||
|
|||||||
+190
-48
@@ -49,9 +49,10 @@ void Network::begin() {
|
|||||||
nosleep_ = settings.nosleep;
|
nosleep_ = settings.nosleep;
|
||||||
tx_power_ = settings.tx_power;
|
tx_power_ = settings.tx_power;
|
||||||
bssid_ = settings.bssid;
|
bssid_ = settings.bssid;
|
||||||
|
eth_10mbit_ = settings.eth_10mbit;
|
||||||
});
|
});
|
||||||
|
|
||||||
// read Ethernet settings
|
// read Ethernet hardware settings
|
||||||
EMSESP::webSettingsService.read([&](WebSettings & settings) {
|
EMSESP::webSettingsService.read([&](WebSettings & settings) {
|
||||||
phy_type_ = settings.phy_type;
|
phy_type_ = settings.phy_type;
|
||||||
eth_power_ = settings.eth_power;
|
eth_power_ = settings.eth_power;
|
||||||
@@ -78,33 +79,16 @@ void Network::begin() {
|
|||||||
|
|
||||||
phase_ = initialPhase();
|
phase_ = initialPhase();
|
||||||
|
|
||||||
// Initialise WiFi once when the Network service starts
|
startWiFiRadio();
|
||||||
WiFi.persistent(false);
|
|
||||||
WiFi.setAutoReconnect(false);
|
|
||||||
WiFi.mode(WIFI_STA);
|
|
||||||
WiFi.disconnect(true, true); // wipe old settings in NVS. Will give a warning on boot but can be ignored.
|
|
||||||
WiFi.setScanMethod(WIFI_ALL_CHANNEL_SCAN);
|
|
||||||
WiFi.setHostname(hostname_.c_str()); // updates shared default_hostname buffer
|
|
||||||
WiFi.enableSTA(true); // creates the STA netif
|
|
||||||
WiFi.STA.setHostname(hostname_.c_str()); // pushes to esp_netif_set_hostname
|
|
||||||
WiFi.enableIPv6(true);
|
|
||||||
if (staticIPConfig_) {
|
|
||||||
WiFi.config(localIP_, gatewayIP_, subnetMask_, dnsIP1_, dnsIP2_); // configure for static IP
|
|
||||||
}
|
|
||||||
|
|
||||||
// www.esp32.com/viewtopic.php?t=12055
|
#if CONFIG_IDF_TARGET_ESP32
|
||||||
if (bandwidth20_) {
|
// Ethernet is switched off but the board still has a PHY wired up. Hold its power/oscillator
|
||||||
esp_wifi_set_bandwidth(static_cast<wifi_interface_t>(ESP_IF_WIFI_STA), WIFI_BW_HT20);
|
// enable low so an unused LAN8720 isn't left clocked and burning current for the whole uptime.
|
||||||
} else {
|
if (phy_type_ == PHY_type::PHY_TYPE_NONE && eth_power_ != -1) {
|
||||||
esp_wifi_set_bandwidth(static_cast<wifi_interface_t>(ESP_IF_WIFI_STA), WIFI_BW_HT40);
|
pinMode(eth_power_, OUTPUT);
|
||||||
|
digitalWrite(eth_power_, LOW);
|
||||||
}
|
}
|
||||||
if (nosleep_) {
|
#endif
|
||||||
WiFi.setSleep(false); // turn off sleep - WIFI_PS_NONE
|
|
||||||
}
|
|
||||||
|
|
||||||
// scan settings give connect issues since arduino 2.0.14 and arduino 3.x.x with some wifi systems
|
|
||||||
// WiFi.setScanMethod(WIFI_ALL_CHANNEL_SCAN); // default is FAST_SCAN
|
|
||||||
// WiFi.setSortMethod(WIFI_CONNECT_AP_BY_SIGNAL); // is default, no need to set
|
|
||||||
|
|
||||||
// avoid duplicate registration, so register the lambdas only once across the lifetime of this Network instance
|
// avoid duplicate registration, so register the lambdas only once across the lifetime of this Network instance
|
||||||
if (!wifi_events_registered_) {
|
if (!wifi_events_registered_) {
|
||||||
@@ -152,7 +136,7 @@ void Network::begin() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// format the BSSID (MAC address) of the network interface
|
// format the BSSID (MAC address) of the network interface
|
||||||
bool Network::formatBSSID(const String & bssid, uint8_t (&mac)[6]) {
|
bool Network::formatBSSID([[maybe_unused]] const String & bssid, [[maybe_unused]] uint8_t (&mac)[6]) {
|
||||||
#ifndef EMSESP_STANDALONE
|
#ifndef EMSESP_STANDALONE
|
||||||
uint tmp[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) {
|
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) {
|
||||||
@@ -214,10 +198,10 @@ void Network::reconnect() {
|
|||||||
LOG_DEBUG("Reconnecting all networks");
|
LOG_DEBUG("Reconnecting all networks");
|
||||||
|
|
||||||
#ifndef EMSESP_STANDALONE
|
#ifndef EMSESP_STANDALONE
|
||||||
// disconnect WiFi
|
// Drop STA association but leave the driver running. begin() re-applies hostname
|
||||||
|
// and IPv6; tearing the radio down here races with WIFI_EVENT_STA_START (12289/259).
|
||||||
if (wifi_connected()) {
|
if (wifi_connected()) {
|
||||||
WiFi.disconnect(true, true);
|
WiFi.disconnect(false, true);
|
||||||
WiFi.mode(WIFI_STA); // reset mode
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// disconnect AP
|
// disconnect AP
|
||||||
@@ -243,11 +227,17 @@ void Network::reconnect() {
|
|||||||
|
|
||||||
// pick the first phase that has the hardware/config to even be attempted on this device.
|
// pick the first phase that has the hardware/config to even be attempted on this device.
|
||||||
// boards without an Ethernet PHY skip straight to WIFI; without a configured SSID, straight to AP
|
// boards without an Ethernet PHY skip straight to WIFI; without a configured SSID, straight to AP
|
||||||
|
// (unless AP provision mode is Never — then stay on WIFI and keep waiting)
|
||||||
NetPhase Network::initialPhase() const {
|
NetPhase Network::initialPhase() const {
|
||||||
if (phy_type_ != PHY_type::PHY_TYPE_NONE) {
|
#if CONFIG_IDF_TARGET_ESP32
|
||||||
|
|
||||||
|
bool eth_clock_conflicts_psram =
|
||||||
|
ESP.getPsramSize() && ((eth_clock_mode_t)eth_clock_mode_ == ETH_CLOCK_GPIO16_OUT || (eth_clock_mode_t)eth_clock_mode_ == ETH_CLOCK_GPIO17_OUT);
|
||||||
|
if (phy_type_ != PHY_type::PHY_TYPE_NONE && !eth_clock_conflicts_psram) {
|
||||||
return NetPhase::ETHERNET;
|
return NetPhase::ETHERNET;
|
||||||
}
|
}
|
||||||
if (!ssid_.isEmpty()) {
|
#endif
|
||||||
|
if (!ssid_.isEmpty() || ap_provisionMode_ == AP_MODE_NEVER) {
|
||||||
return NetPhase::WIFI;
|
return NetPhase::WIFI;
|
||||||
}
|
}
|
||||||
return NetPhase::AP;
|
return NetPhase::AP;
|
||||||
@@ -256,6 +246,16 @@ NetPhase Network::initialPhase() const {
|
|||||||
// network loop, looking for new and disconnecting networks
|
// network loop, looking for new and disconnecting networks
|
||||||
void Network::loop() {
|
void Network::loop() {
|
||||||
#ifndef EMSESP_STANDALONE
|
#ifndef EMSESP_STANDALONE
|
||||||
|
// a reconnect was requested from another task, run it here where nothing is mid-request
|
||||||
|
if (reconnect_pending_) {
|
||||||
|
reconnect_pending_ = false;
|
||||||
|
reconnect();
|
||||||
|
}
|
||||||
|
// already have a connection: verify it's still alive
|
||||||
|
if (network_ip_ != 0 || last_disconnect_reason_ != 0) {
|
||||||
|
checkConnection();
|
||||||
|
}
|
||||||
|
|
||||||
// if we already have a Wifi or Ethernet connection then re-check every NETWORK_RECONNECTION_DELAY_LONG, otherwise NETWORK_RECONNECTION_DELAY_SHORT
|
// if we already have a Wifi or Ethernet connection then re-check every NETWORK_RECONNECTION_DELAY_LONG, otherwise NETWORK_RECONNECTION_DELAY_SHORT
|
||||||
const unsigned long currentMillis = uuid::get_uptime_ms();
|
const unsigned long currentMillis = uuid::get_uptime_ms();
|
||||||
const uint32_t reconnectDelay =
|
const uint32_t reconnectDelay =
|
||||||
@@ -268,11 +268,6 @@ void Network::loop() {
|
|||||||
startWIFI(); // WiFi
|
startWIFI(); // WiFi
|
||||||
startAP(); // Captive Portal (AP)
|
startAP(); // Captive Portal (AP)
|
||||||
|
|
||||||
// already have a connection: verify it's still alive
|
|
||||||
if (network_ip_ != 0 || last_disconnect_reason_ != 0) {
|
|
||||||
checkConnection();
|
|
||||||
}
|
|
||||||
|
|
||||||
findNetworks(); // detect any new network connections
|
findNetworks(); // detect any new network connections
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -314,6 +309,10 @@ void Network::checkConnection() {
|
|||||||
network_iface_ = NetIface::NONE;
|
network_iface_ = NetIface::NONE;
|
||||||
if (lost_iface == NetIface::ETHERNET) {
|
if (lost_iface == NetIface::ETHERNET) {
|
||||||
LOG_WARNING("Ethernet connection lost");
|
LOG_WARNING("Ethernet connection lost");
|
||||||
|
// restore the radio now so the WiFi and AP fallbacks have something to work with
|
||||||
|
if (wifi_radio_off_) {
|
||||||
|
startWiFiRadio();
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
begin();
|
begin();
|
||||||
@@ -331,7 +330,7 @@ void Network::checkConnection() {
|
|||||||
// 802.11ac - wifi5
|
// 802.11ac - wifi5
|
||||||
// 802.11ax - wifi6
|
// 802.11ax - wifi6
|
||||||
// tx_power is the Tx power to set, 0 for auto
|
// tx_power is the Tx power to set, 0 for auto
|
||||||
void Network::setWiFiPower(uint8_t tx_power) {
|
void Network::setWiFiPower([[maybe_unused]] uint8_t tx_power) {
|
||||||
#ifndef EMSESP_STANDALONE
|
#ifndef EMSESP_STANDALONE
|
||||||
if (tx_power != 0) {
|
if (tx_power != 0) {
|
||||||
if (!WiFi.setTxPower(static_cast<wifi_power_t>(tx_power))) {
|
if (!WiFi.setTxPower(static_cast<wifi_power_t>(tx_power))) {
|
||||||
@@ -413,7 +412,7 @@ void Network::startmDNS() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// WiFi disconnect reason code to string
|
// WiFi disconnect reason code to string
|
||||||
const char * Network::disconnectReason(uint8_t code) {
|
const char * Network::disconnectReason([[maybe_unused]] uint8_t code) {
|
||||||
#ifndef EMSESP_STANDALONE
|
#ifndef EMSESP_STANDALONE
|
||||||
switch (code) {
|
switch (code) {
|
||||||
case WIFI_REASON_UNSPECIFIED: // = 1,
|
case WIFI_REASON_UNSPECIFIED: // = 1,
|
||||||
@@ -490,6 +489,59 @@ const char * Network::disconnectReason(uint8_t code) {
|
|||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Bring up the STA interface and apply all the radio-level settings to it
|
||||||
|
// Safe to call repeatedly - enableSTA() is a no-op when the STA is already running, and recreates the netif after stopWiFiRadio() has torn it down
|
||||||
|
void Network::startWiFiRadio() {
|
||||||
|
#ifndef EMSESP_STANDALONE
|
||||||
|
WiFi.persistent(false); // keep credentials in RAM so we don't auto-join leftover NVS config
|
||||||
|
WiFi.setAutoReconnect(false);
|
||||||
|
WiFi.mode(WIFI_STA);
|
||||||
|
|
||||||
|
// Keep the radio up. disconnect(wifioff=true) maps to STA.end() -> esp_wifi_stop()
|
||||||
|
if (WiFi.STA.started()) {
|
||||||
|
WiFi.disconnect(false, true); // drop leftover association, wipe RAM config
|
||||||
|
}
|
||||||
|
|
||||||
|
// scan settings give connect issues since arduino 2.0.14 and arduino 3.x.x with some wifi systems
|
||||||
|
WiFi.setScanMethod(WIFI_ALL_CHANNEL_SCAN); // default is FAST_SCAN
|
||||||
|
WiFi.setHostname(hostname_.c_str()); // updates shared default_hostname buffer
|
||||||
|
WiFi.enableSTA(true); // no-op if already STA; recreates netif after a full off
|
||||||
|
WiFi.STA.setHostname(hostname_.c_str()); // pushes to esp_netif_set_hostname
|
||||||
|
WiFi.enableIPv6(true);
|
||||||
|
if (staticIPConfig_) {
|
||||||
|
WiFi.config(localIP_, gatewayIP_, subnetMask_, dnsIP1_, dnsIP2_); // configure for static IP
|
||||||
|
}
|
||||||
|
|
||||||
|
// www.esp32.com/viewtopic.php?t=12055
|
||||||
|
if (bandwidth20_) {
|
||||||
|
esp_wifi_set_bandwidth(static_cast<wifi_interface_t>(ESP_IF_WIFI_STA), WIFI_BW_HT20);
|
||||||
|
} else {
|
||||||
|
esp_wifi_set_bandwidth(static_cast<wifi_interface_t>(ESP_IF_WIFI_STA), WIFI_BW_HT40);
|
||||||
|
}
|
||||||
|
if (nosleep_) {
|
||||||
|
WiFi.setSleep(false); // turn off sleep - WIFI_PS_NONE
|
||||||
|
} else {
|
||||||
|
WiFi.setSleep(true); // WIFI_PS_MIN_MODEM
|
||||||
|
}
|
||||||
|
|
||||||
|
wifi_radio_off_ = false;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// Power down the WiFi radio completely (esp_wifi_stop), used when Ethernet is carrying the traffic.
|
||||||
|
// An idle STA still keeps the receiver powered. It also unloads the driver's DMA buffer pool of around 20KB
|
||||||
|
void Network::stopWiFiRadio() {
|
||||||
|
#ifndef EMSESP_STANDALONE
|
||||||
|
if (wifi_radio_off_) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
WiFi.mode(WIFI_OFF);
|
||||||
|
wifi_radio_off_ = true;
|
||||||
|
wifi_connect_pending_ = false;
|
||||||
|
LOG_DEBUG("Powering down WiFi radio, Ethernet is active");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
// WiFi management
|
// WiFi management
|
||||||
void Network::startWIFI() {
|
void Network::startWIFI() {
|
||||||
#ifndef EMSESP_STANDALONE
|
#ifndef EMSESP_STANDALONE
|
||||||
@@ -498,6 +550,11 @@ void Network::startWIFI() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// the radio was powered down while Ethernet was up, so bring it back before trying to associate
|
||||||
|
if (wifi_radio_off_) {
|
||||||
|
startWiFiRadio();
|
||||||
|
}
|
||||||
|
|
||||||
// exit if WiFi or Ethernet is already connected, or if we have no SSID or another Wifi.begin() is already in progress
|
// exit if WiFi or Ethernet is already connected, or if we have no SSID or another Wifi.begin() is already in progress
|
||||||
if (WiFi.isConnected() || ssid_.isEmpty() || ethernet_connected() || wifi_connect_pending_) {
|
if (WiFi.isConnected() || ssid_.isEmpty() || ethernet_connected() || wifi_connect_pending_) {
|
||||||
return;
|
return;
|
||||||
@@ -516,6 +573,15 @@ void Network::startWIFI() {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if CONFIG_IDF_TARGET_ESP32 && !defined(EMSESP_STANDALONE)
|
||||||
|
static char eth_hostname[33] = {0};
|
||||||
|
static void ethApplyHostname(void * /*arg*/, esp_event_base_t /*base*/, int32_t /*event_id*/, void * /*event_data*/) {
|
||||||
|
if (eth_hostname[0] != '\0' && ETH.netif() != nullptr) {
|
||||||
|
esp_netif_set_hostname(ETH.netif(), eth_hostname);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// Ethernet management
|
// Ethernet management
|
||||||
// Brings up the ETH driver / netif exactly once. After ETH.begin() returns true the driver
|
// Brings up the ETH driver / netif exactly once. After ETH.begin() returns true the driver
|
||||||
// continues to run autonomously (link negotiation, DHCP, etc)
|
// continues to run autonomously (link negotiation, DHCP, etc)
|
||||||
@@ -527,8 +593,8 @@ void Network::startEthernet() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// already up and running, nothing to do
|
// Ensure the Ethernet driver is installed once and then left alone
|
||||||
if (ethernet_connect_pending_ || ethernet_connected()) {
|
if (ethernet_started_ || ethernet_connect_pending_ || ethernet_connected()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -539,6 +605,22 @@ void Network::startEthernet() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ESP.getPsramSize() && ((eth_clock_mode_t)eth_clock_mode_ == ETH_CLOCK_GPIO16_OUT || (eth_clock_mode_t)eth_clock_mode_ == ETH_CLOCK_GPIO17_OUT)) {
|
||||||
|
LOG_ERROR("Ethernet clock mode %d (GPIO16/17) conflicts with PSRAM - disabling Ethernet", eth_clock_mode_);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
// The Tasmota SDK is built with CONFIG_ESP32_UNIVERSAL_MAC_ADDRESSES=2, so the Ethernet MAC is derived as
|
||||||
|
// a locally-administered address from base+1 instead of the universal base+3 that Arduino Core 2 used.
|
||||||
|
// Seed the MAC table with the old value before the EMAC driver reads it, so DHCP reservations survive an upgrade.
|
||||||
|
uint8_t eth_mac[6];
|
||||||
|
if (esp_read_mac(eth_mac, ESP_MAC_BASE) == ESP_OK) {
|
||||||
|
eth_mac[5] += 3;
|
||||||
|
esp_iface_mac_addr_set(eth_mac, ESP_MAC_ETH);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
// reset power and add a delay as ETH doesn't not always start up correctly after a warm boot
|
// reset power and add a delay as ETH doesn't not always start up correctly after a warm boot
|
||||||
if (eth_power_ != -1) {
|
if (eth_power_ != -1) {
|
||||||
pinMode(eth_power_, OUTPUT);
|
pinMode(eth_power_, OUTPUT);
|
||||||
@@ -560,16 +642,52 @@ void Network::startEthernet() {
|
|||||||
eth_phy_type_t type = (phy_type_ == PHY_type::PHY_TYPE_LAN8720) ? ETH_PHY_LAN8720
|
eth_phy_type_t type = (phy_type_ == PHY_type::PHY_TYPE_LAN8720) ? ETH_PHY_LAN8720
|
||||||
: (phy_type_ == PHY_type::PHY_TYPE_TLK110) ? ETH_PHY_TLK110
|
: (phy_type_ == PHY_type::PHY_TYPE_TLK110) ? ETH_PHY_TLK110
|
||||||
: ETH_PHY_RTL8201; // Type of the Ethernet PHY (LAN8720 or TLK110)
|
: ETH_PHY_RTL8201; // Type of the Ethernet PHY (LAN8720 or TLK110)
|
||||||
|
|
||||||
|
// must be in place before ETH.begin() so it beats the esp_netif glue to the START event
|
||||||
|
strlcpy(eth_hostname, hostname_.c_str(), sizeof(eth_hostname));
|
||||||
|
if (!eth_hostname_handler_registered_ && esp_event_handler_register(ETH_EVENT, ETHERNET_EVENT_START, ethApplyHostname, nullptr) == ESP_OK) {
|
||||||
|
eth_hostname_handler_registered_ = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Forcing 10BASE-T roughly halves the PHY's power draw
|
||||||
|
if (eth_10mbit_) {
|
||||||
|
ETH.setAutoNegotiation(false);
|
||||||
|
ETH.setLinkSpeed(10);
|
||||||
|
ETH.setFullDuplex(false);
|
||||||
|
}
|
||||||
|
|
||||||
if (ETH.begin(type, eth_phy_addr_, 23, 18, eth_power_, (eth_clock_mode_t)eth_clock_mode_)) {
|
if (ETH.begin(type, eth_phy_addr_, 23, 18, eth_power_, (eth_clock_mode_t)eth_clock_mode_)) {
|
||||||
LOG_DEBUG("Ethernet module found - starting");
|
// LOG_DEBUG("Ethernet module found - initialising");
|
||||||
|
ethernet_started_ = true;
|
||||||
ethernet_connect_pending_ = true;
|
ethernet_connect_pending_ = true;
|
||||||
ETH.setHostname(hostname_.c_str()); // Push hostname to the ETH netif immediately after it's created
|
|
||||||
|
// check whether DHCP already announced us under lwIP's default hostname, before we correct it
|
||||||
|
esp_netif_t * eth_netif = ETH.netif();
|
||||||
|
const char * old_hostname = nullptr;
|
||||||
|
bool stale_dhcp = false;
|
||||||
|
if (eth_netif != nullptr && !staticIPConfig_) {
|
||||||
|
esp_netif_dhcp_status_t dhcp_status = ESP_NETIF_DHCP_INIT;
|
||||||
|
stale_dhcp = esp_netif_get_hostname(eth_netif, &old_hostname) == ESP_OK && old_hostname != nullptr && hostname_ != old_hostname
|
||||||
|
&& esp_netif_dhcpc_get_status(eth_netif, &dhcp_status) == ESP_OK && dhcp_status == ESP_NETIF_DHCP_STARTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ETH.setHostname(hostname_.c_str())) {
|
||||||
|
LOG_WARNING("Failed to set Ethernet hostname to %s", hostname_.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
// the DHCP server has already been given the wrong name, so re-run the client to re-announce
|
||||||
|
if (stale_dhcp) {
|
||||||
|
LOG_DEBUG("Restarting Ethernet DHCP client to announce hostname %s", hostname_.c_str());
|
||||||
|
esp_netif_dhcpc_stop(eth_netif);
|
||||||
|
esp_netif_dhcpc_start(eth_netif);
|
||||||
|
}
|
||||||
|
|
||||||
ETH.enableIPv6(true);
|
ETH.enableIPv6(true);
|
||||||
if (staticIPConfig_) {
|
if (staticIPConfig_) {
|
||||||
ETH.config(localIP_, gatewayIP_, subnetMask_, dnsIP1_, dnsIP2_);
|
ETH.config(localIP_, gatewayIP_, subnetMask_, dnsIP1_, dnsIP2_);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
LOG_ERROR("Failed to start Ethernet module");
|
LOG_ERROR("Failed to start Ethernet");
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
@@ -614,6 +732,11 @@ void Network::findNetworks() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const NetIface candidate = iface_from_desc(desc);
|
const NetIface candidate = iface_from_desc(desc);
|
||||||
|
// AP is disabled in settings — don't treat SoftAP as a usable network
|
||||||
|
if (candidate == NetIface::AP && ap_provisionMode_ == AP_MODE_NEVER) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (iface_priority(candidate) <= iface_priority(best_iface)) {
|
if (iface_priority(candidate) <= iface_priority(best_iface)) {
|
||||||
continue; // already have something at least as good
|
continue; // already have something at least as good
|
||||||
}
|
}
|
||||||
@@ -662,6 +785,11 @@ void Network::findNetworks() {
|
|||||||
stopAP();
|
stopAP();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ethernet is carrying the traffic so the STA is dead weight - shut the radio down.
|
||||||
|
if (network_iface_ == NetIface::ETHERNET) {
|
||||||
|
stopWiFiRadio();
|
||||||
|
}
|
||||||
|
|
||||||
// count the number of restarts (for Wifi and Eth)
|
// count the number of restarts (for Wifi and Eth)
|
||||||
if (juststopped_) {
|
if (juststopped_) {
|
||||||
juststopped_ = false;
|
juststopped_ = false;
|
||||||
@@ -692,12 +820,17 @@ void Network::findNetworks() {
|
|||||||
: "AP");
|
: "AP");
|
||||||
|
|
||||||
// give up on this interface and try the next one. ETHERNET -> WIFI (or straight to AP if no SSID configured); WIFI -> AP.
|
// give up on this interface and try the next one. ETHERNET -> WIFI (or straight to AP if no SSID configured); WIFI -> AP.
|
||||||
|
// AP_MODE_NEVER: never promote to the AP phase — keep waiting on the current interface.
|
||||||
if (connect_retry_ >= MAX_NETWORK_RECONNECTION_ATTEMPTS && phase_ != NetPhase::AP) {
|
if (connect_retry_ >= MAX_NETWORK_RECONNECTION_ATTEMPTS && phase_ != NetPhase::AP) {
|
||||||
if (phase_ == NetPhase::ETHERNET) {
|
if (phase_ == NetPhase::ETHERNET) {
|
||||||
if (ssid_.isEmpty()) {
|
if (ssid_.isEmpty()) {
|
||||||
if (!ethernet_ever_connected_) {
|
if (!ethernet_ever_connected_) {
|
||||||
LOG_WARNING("Ethernet failed to connect after %u attempts, falling back to AP", connect_retry_);
|
if (ap_provisionMode_ == AP_MODE_NEVER) {
|
||||||
phase_ = NetPhase::AP;
|
LOG_WARNING("Ethernet failed to connect after %u attempts (AP provision mode is Never)", connect_retry_);
|
||||||
|
} else {
|
||||||
|
LOG_WARNING("Ethernet failed to connect after %u attempts, falling back to AP", connect_retry_);
|
||||||
|
phase_ = NetPhase::AP;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
LOG_WARNING("Ethernet failed to connect after %u attempts, switching to WiFi", connect_retry_);
|
LOG_WARNING("Ethernet failed to connect after %u attempts, switching to WiFi", connect_retry_);
|
||||||
@@ -706,8 +839,12 @@ void Network::findNetworks() {
|
|||||||
ethernet_connect_pending_ = false;
|
ethernet_connect_pending_ = false;
|
||||||
} else if (phase_ == NetPhase::WIFI) {
|
} else if (phase_ == NetPhase::WIFI) {
|
||||||
if (!wifi_ever_connected_) {
|
if (!wifi_ever_connected_) {
|
||||||
LOG_WARNING("WiFi failed to connect after %u attempts, falling back to AP", connect_retry_);
|
if (ap_provisionMode_ == AP_MODE_NEVER) {
|
||||||
phase_ = NetPhase::AP;
|
LOG_WARNING("WiFi failed to connect after %u attempts (AP provision mode is Never)", connect_retry_);
|
||||||
|
} else {
|
||||||
|
LOG_WARNING("WiFi failed to connect after %u attempts, falling back to AP", connect_retry_);
|
||||||
|
phase_ = NetPhase::AP;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
wifi_connect_pending_ = false;
|
wifi_connect_pending_ = false;
|
||||||
}
|
}
|
||||||
@@ -737,6 +874,11 @@ void Network::startAP() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// the AP needs the radio, which may have been powered down while Ethernet was up
|
||||||
|
if (wifi_radio_off_) {
|
||||||
|
startWiFiRadio();
|
||||||
|
}
|
||||||
|
|
||||||
if (!WiFi.softAPConfig(ap_localIP_, ap_gatewayIP_, ap_subnetMask_)) {
|
if (!WiFi.softAPConfig(ap_localIP_, ap_gatewayIP_, ap_subnetMask_)) {
|
||||||
LOG_DEBUG("softAPConfig failed");
|
LOG_DEBUG("softAPConfig failed");
|
||||||
return;
|
return;
|
||||||
|
|||||||
+16
-3
@@ -20,6 +20,7 @@
|
|||||||
#define EMSESP_NETWORK_H_
|
#define EMSESP_NETWORK_H_
|
||||||
|
|
||||||
#ifndef EMSESP_STANDALONE
|
#ifndef EMSESP_STANDALONE
|
||||||
|
#include <esp_mac.h>
|
||||||
#include <esp_wifi.h>
|
#include <esp_wifi.h>
|
||||||
#include <ETH.h>
|
#include <ETH.h>
|
||||||
#include <WiFi.h>
|
#include <WiFi.h>
|
||||||
@@ -137,6 +138,11 @@ class Network {
|
|||||||
|
|
||||||
void reconnect();
|
void reconnect();
|
||||||
|
|
||||||
|
// ask for a reconnect() to run from the next Network::loop()
|
||||||
|
void schedule_reconnect() {
|
||||||
|
reconnect_pending_ = true;
|
||||||
|
}
|
||||||
|
|
||||||
// map a netif description string (from esp_netif_get_desc) to a NetIface
|
// map a netif description string (from esp_netif_get_desc) to a NetIface
|
||||||
static NetIface iface_from_desc(const char * desc) {
|
static NetIface iface_from_desc(const char * desc) {
|
||||||
if (!desc) {
|
if (!desc) {
|
||||||
@@ -180,6 +186,8 @@ class Network {
|
|||||||
void startAP();
|
void startAP();
|
||||||
void startWIFI();
|
void startWIFI();
|
||||||
void startEthernet();
|
void startEthernet();
|
||||||
|
void startWiFiRadio();
|
||||||
|
void stopWiFiRadio();
|
||||||
void setWiFiPower(uint8_t tx_power);
|
void setWiFiPower(uint8_t tx_power);
|
||||||
const char * disconnectReason(uint8_t code);
|
const char * disconnectReason(uint8_t code);
|
||||||
void stopAP();
|
void stopAP();
|
||||||
@@ -201,12 +209,16 @@ class Network {
|
|||||||
|
|
||||||
volatile bool wifi_connect_pending_ = false;
|
volatile bool wifi_connect_pending_ = false;
|
||||||
volatile bool ethernet_connect_pending_ = false;
|
volatile bool ethernet_connect_pending_ = false;
|
||||||
|
volatile bool reconnect_pending_ = false; // set by schedule_reconnect(), actioned in loop()
|
||||||
|
|
||||||
NetPhase phase_ = NetPhase::ETHERNET;
|
NetPhase phase_ = NetPhase::ETHERNET;
|
||||||
|
|
||||||
bool wifi_events_registered_ = false; // ensure WiFi.onEvent() handlers are registered only once across begin()/reconnect() cycles
|
bool wifi_events_registered_ = false; // ensure WiFi.onEvent() handlers are registered only once across begin()/reconnect() cycles
|
||||||
bool wifi_ever_connected_ = false; // set true once we've successfully obtained an IP
|
bool eth_hostname_handler_registered_ = false; // for the ETHERNET_EVENT_START hostname handler
|
||||||
bool ethernet_ever_connected_ = false; // set true once we've successfully obtained an IP
|
bool ethernet_started_ = false; // ETH.begin() succeeded; the driver runs for the lifetime of the boot
|
||||||
|
bool wifi_ever_connected_ = false; // set true once we've successfully obtained an IP
|
||||||
|
bool ethernet_ever_connected_ = false; // set true once we've successfully obtained an IP
|
||||||
|
bool wifi_radio_off_ = false; // STA powered down because Ethernet is carrying the traffic
|
||||||
|
|
||||||
// Network and AP settings
|
// Network and AP settings
|
||||||
bool enableMDNS_;
|
bool enableMDNS_;
|
||||||
@@ -227,6 +239,7 @@ class Network {
|
|||||||
int8_t eth_power_;
|
int8_t eth_power_;
|
||||||
uint8_t eth_phy_addr_;
|
uint8_t eth_phy_addr_;
|
||||||
uint8_t eth_clock_mode_;
|
uint8_t eth_clock_mode_;
|
||||||
|
bool eth_10mbit_;
|
||||||
|
|
||||||
// AP settings
|
// AP settings
|
||||||
uint8_t ap_provisionMode_;
|
uint8_t ap_provisionMode_;
|
||||||
|
|||||||
+2
-2
@@ -36,7 +36,7 @@ void Shower::start() {
|
|||||||
Command::add_json(
|
Command::add_json(
|
||||||
EMSdevice::DeviceType::BOILER,
|
EMSdevice::DeviceType::BOILER,
|
||||||
F_(coldshot),
|
F_(coldshot),
|
||||||
[&](const char * value, const int8_t id, JsonObject output) {
|
[this](const char * value, const int8_t id, JsonObject output) {
|
||||||
LOG_INFO("Forcing coldshot...");
|
LOG_INFO("Forcing coldshot...");
|
||||||
if (shower_state_) {
|
if (shower_state_) {
|
||||||
output["message"] = "OK";
|
output["message"] = "OK";
|
||||||
@@ -229,7 +229,7 @@ void Shower::create_ha_discovery() {
|
|||||||
|
|
||||||
// don't bother with value template conditions if using Domoticz which doesn't fully support MQTT Discovery
|
// don't bother with value template conditions if using Domoticz which doesn't fully support MQTT Discovery
|
||||||
if (Mqtt::discovery_type() == Mqtt::discoveryType::HOMEASSISTANT) {
|
if (Mqtt::discovery_type() == Mqtt::discoveryType::HOMEASSISTANT) {
|
||||||
doc["val_tpl"] = "{{value_json.duration if value_json.duration is defined else 0}}";
|
doc["val_tpl"] = "{{value_json.duration if value_json.duration is defined else 'None'}}";
|
||||||
} else {
|
} else {
|
||||||
doc["val_tpl"] = "{{value_json.duration}}";
|
doc["val_tpl"] = "{{value_json.duration}}";
|
||||||
}
|
}
|
||||||
|
|||||||
+28
-104
@@ -22,8 +22,7 @@
|
|||||||
|
|
||||||
#include "shuntingYard.h"
|
#include "shuntingYard.h"
|
||||||
|
|
||||||
#include <WiFiClient.h>
|
#include "httpClient.h"
|
||||||
#include <ESP_SSLClient.h>
|
|
||||||
|
|
||||||
namespace emsesp {
|
namespace emsesp {
|
||||||
|
|
||||||
@@ -336,7 +335,6 @@ bool isnum(const std::string & s) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// replace commands like "<device>/<hc>/<cmd>" with its value"
|
// replace commands like "<device>/<hc>/<cmd>" with its value"
|
||||||
std::string commands(std::string & expr, bool quotes) {
|
std::string commands(std::string & expr, bool quotes) {
|
||||||
auto expr_new = Helpers::toLower(expr);
|
auto expr_new = Helpers::toLower(expr);
|
||||||
@@ -481,14 +479,14 @@ std::string calculate(const std::string & expr) {
|
|||||||
const auto rhs = stack.back();
|
const auto rhs = stack.back();
|
||||||
stack.pop_back();
|
stack.pop_back();
|
||||||
if (token.str[0] == '!') {
|
if (token.str[0] == '!') {
|
||||||
if (to_logic(rhs) < 0) {
|
const auto logic = to_logic(rhs);
|
||||||
}
|
if (logic >= 0) {
|
||||||
if (to_logic(rhs) >= 0) {
|
stack.push_back(logic == 0 ? "1" : "0");
|
||||||
stack.push_back(to_logic(rhs) == 0 ? "1" : "0");
|
|
||||||
} else if (isnum(rhs)) {
|
} else if (isnum(rhs)) {
|
||||||
stack.push_back(std::stod(rhs) == 0 ? "1" : "0");
|
stack.push_back(std::stod(rhs) == 0 ? "1" : "0");
|
||||||
} else {
|
} else {
|
||||||
EMSESP::logger().warning("missing operator");
|
// usually literal text that was left unquoted, so its '!' got read as a logical NOT
|
||||||
|
EMSESP::logger().warning("'!' needs a boolean or numeric operand, got '%s'. Literal text must be quoted", rhs.c_str());
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -691,96 +689,22 @@ std::string calculate(const std::string & expr) {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// perform an HTTP/HTTPS request; returns the HTTP status code (0 on failure or unsupported scheme)
|
// find the next occurrence of c at or after `from` that is not inside a quoted string, so that
|
||||||
// the response headers are always stripped, so `result` contains only the body
|
// literal text like "too hot?" is not mistaken for a ternary. Always scans from the start of the
|
||||||
int http_request(std::string url, const std::string & method, const std::string & value, JsonObjectConst headers, std::string & result) {
|
// string because the quote state at `from` depends on everything before it.
|
||||||
int httpResult = 0;
|
static size_t find_unquoted(const std::string & s, char c, size_t from = 0) {
|
||||||
const bool is_post = value.length() || Helpers::toLower(method) == "post";
|
bool in_single = false;
|
||||||
const auto lower_url = Helpers::toLower(url.c_str());
|
bool in_double = false;
|
||||||
|
for (size_t i = 0; i < s.length(); i++) {
|
||||||
const bool is_https = lower_url.starts_with("https://");
|
if (!in_single && s[i] == '"') {
|
||||||
if (!is_https && !lower_url.starts_with("http://")) {
|
in_double = !in_double;
|
||||||
return 0; // unsupported scheme
|
} else if (!in_double && s[i] == '\'') {
|
||||||
|
in_single = !in_single;
|
||||||
|
} else if (!in_single && !in_double && s[i] == c && i >= from) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return std::string::npos;
|
||||||
WiFiClient * basic_client = new WiFiClient;
|
|
||||||
ESP_SSLClient * ssl_client = new ESP_SSLClient;
|
|
||||||
if (is_https) {
|
|
||||||
ssl_client->setInsecure(); // with root CA we should set here: ssl_client->setCACert(rootCACert);
|
|
||||||
// NOTE: 1 KB RX buffer is fine for small JSON-style endpoints used by the scheduler/shunting-yard,
|
|
||||||
// but it is NOT enough for servers that send full-size TLS records (>1 KB), e.g. GitHub release
|
|
||||||
// assets / large CDN responses. Such servers do not negotiate max_fragment_length, so the body
|
|
||||||
// can't be decoded and reads return 0. If this path is ever used to fetch large or CDN-hosted
|
|
||||||
// payloads, bump the RX buffer to 16384 (see uploadFirmwareURL in core/system.cpp for reference).
|
|
||||||
ssl_client->setBufferSizes(1024, 1024);
|
|
||||||
ssl_client->setSessionTimeout(120); // Set the timeout in seconds (>=120 seconds)
|
|
||||||
}
|
|
||||||
ssl_client->setClient(basic_client, is_https); // enableSSL = false for plain HTTP
|
|
||||||
|
|
||||||
url.replace(0, is_https ? 8 : 7, "");
|
|
||||||
std::string host = url;
|
|
||||||
auto index = url.find_first_of('/');
|
|
||||||
if (index != std::string::npos) {
|
|
||||||
host = url.substr(0, index);
|
|
||||||
url.replace(0, index, "");
|
|
||||||
} else {
|
|
||||||
url = "/";
|
|
||||||
}
|
|
||||||
|
|
||||||
const uint16_t port = is_https ? 443 : 80;
|
|
||||||
if (ssl_client->connect(host.c_str(), port)) {
|
|
||||||
bool content_set = false;
|
|
||||||
ssl_client->print(is_post ? "POST " : "GET ");
|
|
||||||
ssl_client->print(url.c_str());
|
|
||||||
ssl_client->println(" HTTP/1.1");
|
|
||||||
ssl_client->print("Host: ");
|
|
||||||
ssl_client->println(host.c_str());
|
|
||||||
for (JsonPairConst p : headers) {
|
|
||||||
content_set |= (Helpers::toLower(p.key().c_str()) == "content-type");
|
|
||||||
ssl_client->print(p.key().c_str());
|
|
||||||
ssl_client->print(": ");
|
|
||||||
ssl_client->println(p.value().as<std::string>().c_str());
|
|
||||||
}
|
|
||||||
if (is_post) {
|
|
||||||
if (!content_set) {
|
|
||||||
ssl_client->print("Content-Type: ");
|
|
||||||
ssl_client->println(value.starts_with('{') ? asyncsrv::T_application_json : asyncsrv::T_text_plain);
|
|
||||||
}
|
|
||||||
ssl_client->print("Content-Length: ");
|
|
||||||
ssl_client->println(value.length());
|
|
||||||
ssl_client->println("Connection: close");
|
|
||||||
ssl_client->print("\r\n");
|
|
||||||
ssl_client->print(value.c_str());
|
|
||||||
} else {
|
|
||||||
ssl_client->println("Connection: close");
|
|
||||||
ssl_client->print("\r\n"); // terminate headers - without this the server never responds
|
|
||||||
}
|
|
||||||
|
|
||||||
auto ms = millis();
|
|
||||||
while (ssl_client->connected() && !ssl_client->available() && millis() - ms < 3000) {
|
|
||||||
delay(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
while (ssl_client->available()) {
|
|
||||||
result += (char)ssl_client->read();
|
|
||||||
}
|
|
||||||
ssl_client->stop();
|
|
||||||
|
|
||||||
index = result.find_first_of(' ');
|
|
||||||
if (index != std::string::npos) {
|
|
||||||
httpResult = stoi(result.substr(index + 1, 3));
|
|
||||||
}
|
|
||||||
index = result.find("\r\n\r\n");
|
|
||||||
if (index != std::string::npos) {
|
|
||||||
result.replace(0, index + 4, "");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
EMSESP::logger().warning("%s connection failed", is_https ? "HTTPS" : "HTTP");
|
|
||||||
}
|
|
||||||
delete ssl_client;
|
|
||||||
delete basic_client;
|
|
||||||
|
|
||||||
return httpResult;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// check for multiple instances of <cond> ? <expr1> : <expr2>
|
// check for multiple instances of <cond> ? <expr1> : <expr2>
|
||||||
@@ -827,7 +751,7 @@ std::string compute(const std::string & expr) {
|
|||||||
std::string method = doc[method_s] | "GET";
|
std::string method = doc[method_s] | "GET";
|
||||||
std::string result;
|
std::string result;
|
||||||
|
|
||||||
int httpResult = http_request(url, method, value, doc[header_s].as<JsonObjectConst>(), result);
|
int httpResult = HttpClient::request(url, method, value, doc[header_s].as<JsonObjectConst>(), result);
|
||||||
if (httpResult == 200) {
|
if (httpResult == 200) {
|
||||||
std::string key = doc[key_s] | "";
|
std::string key = doc[key_s] | "";
|
||||||
JsonDocument keys_doc; // JsonDocument to hold "keys" after doc is parsed with HTTP body
|
JsonDocument keys_doc; // JsonDocument to hold "keys" after doc is parsed with HTTP body
|
||||||
@@ -866,14 +790,14 @@ std::string compute(const std::string & expr) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// positions: q-questionmark, c-colon
|
// positions: q-questionmark, c-colon
|
||||||
auto q = expr_new.find_first_of('?');
|
auto q = find_unquoted(expr_new, '?');
|
||||||
while (q != std::string::npos) {
|
while (q != std::string::npos) {
|
||||||
// find corresponding colon
|
// find corresponding colon
|
||||||
auto c1 = expr_new.find_first_of(':', q + 1);
|
auto c1 = find_unquoted(expr_new, ':', q + 1);
|
||||||
auto q1 = expr_new.find_first_of('?', q + 1);
|
auto q1 = find_unquoted(expr_new, '?', q + 1);
|
||||||
while (q1 < c1 && q1 != std::string::npos && c1 != std::string::npos) {
|
while (q1 < c1 && q1 != std::string::npos && c1 != std::string::npos) {
|
||||||
q1 = expr_new.find_first_of('?', q1 + 1);
|
q1 = find_unquoted(expr_new, '?', q1 + 1);
|
||||||
c1 = expr_new.find_first_of(':', c1 + 1);
|
c1 = find_unquoted(expr_new, ':', c1 + 1);
|
||||||
}
|
}
|
||||||
if (c1 == std::string::npos) {
|
if (c1 == std::string::npos) {
|
||||||
return ""; // error: missing colon
|
return ""; // error: missing colon
|
||||||
@@ -904,7 +828,7 @@ std::string compute(const std::string & expr) {
|
|||||||
} else {
|
} else {
|
||||||
return ""; // error
|
return ""; // error
|
||||||
}
|
}
|
||||||
q = expr_new.find_first_of('?'); // search next instance
|
q = find_unquoted(expr_new, '?'); // search next instance
|
||||||
}
|
}
|
||||||
|
|
||||||
return calculate(expr_new);
|
return calculate(expr_new);
|
||||||
|
|||||||
@@ -84,8 +84,6 @@ std::string calculate(const std::string & expr);
|
|||||||
// check for multiple instances of <cond> ? <expr1> : <expr2>
|
// check for multiple instances of <cond> ? <expr1> : <expr2>
|
||||||
std::string compute(const std::string & expr);
|
std::string compute(const std::string & expr);
|
||||||
|
|
||||||
int http_request(std::string url, const std::string & method, const std::string & value, JsonObjectConst headers, std::string & result);
|
} // namespace emsesp
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
} // namespace emsesp
|
|
||||||
+120
-94
@@ -40,9 +40,6 @@
|
|||||||
|
|
||||||
#ifndef EMSESP_STANDALONE
|
#ifndef EMSESP_STANDALONE
|
||||||
#define ENABLE_SMTP
|
#define ENABLE_SMTP
|
||||||
#define USE_ESP_SSLCLIENT
|
|
||||||
#define READYCLIENT_SSL_CLIENT ESP_SSLClient
|
|
||||||
#define READYCLIENT_TYPE_1 // TYPE 1 when using ESP_SSLClient
|
|
||||||
#include <ESP_SSLClient.h>
|
#include <ESP_SSLClient.h>
|
||||||
#include <ReadyMail.h>
|
#include <ReadyMail.h>
|
||||||
#endif
|
#endif
|
||||||
@@ -93,6 +90,27 @@ uint32_t System::min_free_mem_;
|
|||||||
std::vector<uint8_t, AllocatorPSRAM<uint8_t>> System::valid_system_gpios_;
|
std::vector<uint8_t, AllocatorPSRAM<uint8_t>> System::valid_system_gpios_;
|
||||||
std::vector<System::GpioUsage, AllocatorPSRAM<System::GpioUsage>> System::used_gpios_;
|
std::vector<System::GpioUsage, AllocatorPSRAM<System::GpioUsage>> System::used_gpios_;
|
||||||
|
|
||||||
|
#ifndef EMSESP_STANDALONE
|
||||||
|
// TLSHandshakeCallback is a C function pointer, so STARTTLS needs a process-wide client slot.
|
||||||
|
static ESP_SSLClient * sendmail_ssl_client = nullptr;
|
||||||
|
|
||||||
|
static void sendmail_tls_handshake(bool & success) {
|
||||||
|
success = sendmail_ssl_client != nullptr && sendmail_ssl_client->connectSSL();
|
||||||
|
}
|
||||||
|
|
||||||
|
// The DNS slots are shared between IPv4 and IPv6, so with IPv6 enabled the first slot can hold an
|
||||||
|
// IPv6 server. Pick the first IPv4 one so it matches the label we print it under.
|
||||||
|
static IPAddress dns_ipv4(const NetworkInterface & netif) {
|
||||||
|
for (uint8_t i = 0; i < ESP_NETIF_DNS_MAX; i++) {
|
||||||
|
IPAddress ip = netif.dnsIP(i);
|
||||||
|
if (ip.type() == IPv4 && static_cast<uint32_t>(ip) != 0) {
|
||||||
|
return ip;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return IPAddress();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// find the index of the language
|
// find the index of the language
|
||||||
// 0 = EN, 1 = DE, etc...
|
// 0 = EN, 1 = DE, etc...
|
||||||
uint8_t System::language_index() {
|
uint8_t System::language_index() {
|
||||||
@@ -110,7 +128,7 @@ bool System::command_send(const char * value, const int8_t id) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// send email via SMTP
|
// send email via SMTP
|
||||||
bool System::command_sendmail(const char * value, const int8_t id) {
|
bool System::command_sendmail(const char * value, const int8_t) {
|
||||||
bool enabled = false;
|
bool enabled = false;
|
||||||
uint8_t security;
|
uint8_t security;
|
||||||
uint16_t port;
|
uint16_t port;
|
||||||
@@ -132,60 +150,89 @@ bool System::command_sendmail(const char * value, const int8_t id) {
|
|||||||
LOG_DEBUG("Command sendmail port %d%s called with '%s'",
|
LOG_DEBUG("Command sendmail port %d%s called with '%s'",
|
||||||
port,
|
port,
|
||||||
security == EMAIL_SECURITY::SSL ? " (SSL)"
|
security == EMAIL_SECURITY::SSL ? " (SSL)"
|
||||||
: security == EMAIL_SECURITY::STARTTLS ? ""
|
: security == EMAIL_SECURITY::STARTTLS ? " (STARTTLS)"
|
||||||
: " (STARTTLS)",
|
: " (plain)",
|
||||||
value);
|
value);
|
||||||
|
|
||||||
bool success = false;
|
// Resolve the message before opening the SMTP session: compute() can do entity lookups and a
|
||||||
|
// blocking {url} fetch, which would otherwise leave the connection idle long enough to time out.
|
||||||
#ifndef EMSESP_STANDALONE
|
// The value is either a plain body or a JSON envelope overriding the configured subject/to/from.
|
||||||
WiFiClient * basic_client = new WiFiClient;
|
|
||||||
ESP_SSLClient * ssl_client = new ESP_SSLClient;
|
|
||||||
ReadyClient * r_client = new ReadyClient(*ssl_client);
|
|
||||||
SMTPClient * smtp = new SMTPClient(*r_client);
|
|
||||||
|
|
||||||
ssl_client->setClient(basic_client);
|
|
||||||
ssl_client->setInsecure();
|
|
||||||
ssl_client->setBufferSizes(1024, 1024);
|
|
||||||
r_client->addPort(port,
|
|
||||||
security == EMAIL_SECURITY::NONE ? readymail_protocol_plain_text
|
|
||||||
: security == EMAIL_SECURITY::SSL ? readymail_protocol_ssl
|
|
||||||
: readymail_protocol_tls);
|
|
||||||
|
|
||||||
|
|
||||||
// smtp->connect(server, port, sendmailCallback);
|
|
||||||
smtp->connect(server, port);
|
|
||||||
if (!smtp->isConnected()) {
|
|
||||||
LOG_ERROR("send email connection error");
|
|
||||||
delete smtp;
|
|
||||||
delete r_client;
|
|
||||||
delete ssl_client;
|
|
||||||
delete basic_client;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// LOG_INFO("authenticate %s:%s", login.c_str(), pass.c_str());
|
|
||||||
smtp->authenticate(login, pass, readymail_auth_password);
|
|
||||||
if (!smtp->isAuthenticated()) {
|
|
||||||
LOG_ERROR("send email authentication error");
|
|
||||||
delete smtp;
|
|
||||||
delete r_client;
|
|
||||||
delete ssl_client;
|
|
||||||
delete basic_client;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
JsonDocument doc(PSRAM_DOC);
|
JsonDocument doc(PSRAM_DOC);
|
||||||
String body = value;
|
String body = value;
|
||||||
if (body.length()) {
|
if (body.length()) {
|
||||||
auto error = deserializeJson(doc, (const char *)value);
|
auto error = deserializeJson(doc, (const char *)value);
|
||||||
if (!error && doc.as<JsonObject>().size() >= 0) {
|
if (!error && doc.is<JsonObject>()) {
|
||||||
subject = doc["subject"] | subject;
|
subject = doc["subject"] | subject;
|
||||||
recp = doc["to"] | recp;
|
recp = doc["to"] | recp;
|
||||||
sender = doc["from"] | sender;
|
sender = doc["from"] | sender;
|
||||||
body = doc["body"] | body;
|
body = doc["body"] | body;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// keep the original body if the calculator returns nothing, so unquoted literal text still gets sent
|
||||||
|
std::string computed_body = compute(body.c_str());
|
||||||
|
if (!computed_body.empty()) {
|
||||||
|
body = computed_body.c_str();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool success = false;
|
||||||
|
|
||||||
|
#ifndef EMSESP_STANDALONE
|
||||||
|
// Do not use ReadyClient: after the 220 greeting it calls connectSSL() whenever
|
||||||
|
// STARTTLS is off, so Security=Off still sends a TLS Client Hello on port 25.
|
||||||
|
auto * basic_client = new WiFiClient;
|
||||||
|
ESP_SSLClient * ssl_client = nullptr;
|
||||||
|
SMTPClient * smtp = nullptr;
|
||||||
|
|
||||||
|
basic_client->setTimeout(5000);
|
||||||
|
|
||||||
|
const bool implicit_ssl = (security == EMAIL_SECURITY::SSL);
|
||||||
|
const bool start_tls = (security == EMAIL_SECURITY::STARTTLS);
|
||||||
|
const bool tls_enabled = (security != EMAIL_SECURITY::NONE);
|
||||||
|
|
||||||
|
if (security == EMAIL_SECURITY::NONE) {
|
||||||
|
smtp = new SMTPClient(*basic_client);
|
||||||
|
} else {
|
||||||
|
ssl_client = new ESP_SSLClient;
|
||||||
|
ssl_client->setInsecure();
|
||||||
|
ssl_client->setBufferSizes(16384, 1024);
|
||||||
|
ssl_client->setTimeout(5);
|
||||||
|
// enableSSL is baked in at setClient() — true only for implicit SSL (port 465)
|
||||||
|
ssl_client->setClient(basic_client, implicit_ssl);
|
||||||
|
if (start_tls) {
|
||||||
|
sendmail_ssl_client = ssl_client;
|
||||||
|
smtp = new SMTPClient(*ssl_client, sendmail_tls_handshake, true);
|
||||||
|
} else {
|
||||||
|
smtp = new SMTPClient(*ssl_client);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto cleanup = [&]() {
|
||||||
|
delete smtp;
|
||||||
|
sendmail_ssl_client = nullptr;
|
||||||
|
delete ssl_client;
|
||||||
|
delete basic_client;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!smtp->connect(server, port, String(""), static_cast<SMTPResponseCallback>(nullptr), tls_enabled)) {
|
||||||
|
LOG_ERROR("send email connection error: %s", smtp->status().text.c_str());
|
||||||
|
cleanup();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!smtp->isConnected()) {
|
||||||
|
LOG_ERROR("send email connection error: %s", smtp->status().text.c_str());
|
||||||
|
cleanup();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// internal/open relays often have no AUTH; empty login means skip it
|
||||||
|
if (!login.isEmpty()) {
|
||||||
|
smtp->authenticate(login, pass, readymail_auth_password);
|
||||||
|
if (!smtp->isAuthenticated()) {
|
||||||
|
LOG_ERROR("send email authentication error: %s", smtp->status().text.c_str());
|
||||||
|
cleanup();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
SMTPMessage & msg = smtp->getMessage();
|
SMTPMessage & msg = smtp->getMessage();
|
||||||
msg.headers.add(rfc822_subject, subject);
|
msg.headers.add(rfc822_subject, subject);
|
||||||
msg.headers.add(rfc822_from, sender);
|
msg.headers.add(rfc822_from, sender);
|
||||||
@@ -195,12 +242,6 @@ bool System::command_sendmail(const char * value, const int8_t id) {
|
|||||||
// msg.headers.addCustom("Importance", PRIORITY);
|
// msg.headers.addCustom("Importance", PRIORITY);
|
||||||
// msg.headers.addCustom("X-MSMail-Priority", PRIORITY);
|
// msg.headers.addCustom("X-MSMail-Priority", PRIORITY);
|
||||||
// msg.headers.addCustom("X-Priority", PRIORITY_NUM);
|
// msg.headers.addCustom("X-Priority", PRIORITY_NUM);
|
||||||
// run the body through the Shunting Yard calculator (entity substitution, expressions, optional {url} fetch)
|
|
||||||
// keep the original body if the calculator returns nothing
|
|
||||||
std::string computed_body = compute(body.c_str());
|
|
||||||
if (!computed_body.empty()) {
|
|
||||||
body = computed_body.c_str();
|
|
||||||
}
|
|
||||||
msg.text.body(body);
|
msg.text.body(body);
|
||||||
|
|
||||||
// bodyText.replace("\r\n", "<br>\r\n");
|
// bodyText.replace("\r\n", "<br>\r\n");
|
||||||
@@ -214,11 +255,11 @@ bool System::command_sendmail(const char * value, const int8_t id) {
|
|||||||
msg.timestamp = time(nullptr);
|
msg.timestamp = time(nullptr);
|
||||||
|
|
||||||
success = smtp->send(msg);
|
success = smtp->send(msg);
|
||||||
|
if (!success) {
|
||||||
|
LOG_ERROR("send email failed: %s", smtp->status().text.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
delete smtp;
|
cleanup();
|
||||||
delete r_client;
|
|
||||||
delete ssl_client;
|
|
||||||
delete basic_client;
|
|
||||||
#endif
|
#endif
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
@@ -487,7 +528,7 @@ void System::set_partition_install_date() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// sets the partition to use on the next restart
|
// sets the partition to use on the next restart
|
||||||
bool System::set_partition(const char * partitionname) {
|
bool System::set_partition([[maybe_unused]] const char * partitionname) {
|
||||||
#ifdef EMSESP_STANDALONE
|
#ifdef EMSESP_STANDALONE
|
||||||
return true;
|
return true;
|
||||||
#else
|
#else
|
||||||
@@ -572,7 +613,7 @@ void System::system_restart(const char * partitionname) {
|
|||||||
}
|
}
|
||||||
Serial.flush(); // wait for hardware TX buffer to drain
|
Serial.flush(); // wait for hardware TX buffer to drain
|
||||||
|
|
||||||
Mqtt::disconnect(); // gracefully disconnect MQTT, needed for QOS1
|
Mqtt::disconnect(); // gracefully disconnect MQTT (flushes the DISCONNECT before reboot, needed for QOS1)
|
||||||
EMSuart::stop(); // stop UART so there is no interference
|
EMSuart::stop(); // stop UART so there is no interference
|
||||||
#ifndef EMSESP_STANDALONE
|
#ifndef EMSESP_STANDALONE
|
||||||
delay(1000); // wait 1 second
|
delay(1000); // wait 1 second
|
||||||
@@ -826,7 +867,7 @@ bool System::loop() {
|
|||||||
void System::send_info_mqtt() {
|
void System::send_info_mqtt() {
|
||||||
static uint8_t _connection = 0;
|
static uint8_t _connection = 0;
|
||||||
uint8_t connection = (EMSESP::network_.ethernet_connected() ? 1 : 0) + (EMSESP::network_.wifi_connected() ? 2 : 0) + (ntp_connected_ ? 4 : 0)
|
uint8_t connection = (EMSESP::network_.ethernet_connected() ? 1 : 0) + (EMSESP::network_.wifi_connected() ? 2 : 0) + (ntp_connected_ ? 4 : 0)
|
||||||
+ (EMSESP::network_.has_ipv6() ? 8 : 0);
|
+ (EMSESP::network_.has_ipv6() ? 8 : 0);
|
||||||
// check if connection status has changed
|
// check if connection status has changed
|
||||||
if (!Mqtt::connected() || connection == _connection) {
|
if (!Mqtt::connected() || connection == _connection) {
|
||||||
return;
|
return;
|
||||||
@@ -839,7 +880,7 @@ void System::send_info_mqtt() {
|
|||||||
|
|
||||||
// if NTP is enabled send the boot_time in local time in ISO 8601 format (eg: 2022-11-15 20:46:38)
|
// if NTP is enabled send the boot_time in local time in ISO 8601 format (eg: 2022-11-15 20:46:38)
|
||||||
// https://github.com/emsesp/EMS-ESP32/issues/751
|
// https://github.com/emsesp/EMS-ESP32/issues/751
|
||||||
if (ntp_connected_) {
|
if (time(nullptr) > 1500000000L) {
|
||||||
char time_string[25];
|
char time_string[25];
|
||||||
time_t now = time(nullptr) - uuid::get_uptime_sec();
|
time_t now = time(nullptr) - uuid::get_uptime_sec();
|
||||||
strftime(time_string, 25, "%FT%T%z", localtime(&now));
|
strftime(time_string, 25, "%FT%T%z", localtime(&now));
|
||||||
@@ -858,8 +899,7 @@ void System::send_info_mqtt() {
|
|||||||
doc["MAC"] = WiFi.macAddress();
|
doc["MAC"] = WiFi.macAddress();
|
||||||
doc["IPv4 address"] = uuid::printable_to_string(WiFi.localIP()) + "/" + uuid::printable_to_string(WiFi.subnetMask());
|
doc["IPv4 address"] = uuid::printable_to_string(WiFi.localIP()) + "/" + uuid::printable_to_string(WiFi.subnetMask());
|
||||||
doc["IPv4 gateway"] = uuid::printable_to_string(WiFi.gatewayIP());
|
doc["IPv4 gateway"] = uuid::printable_to_string(WiFi.gatewayIP());
|
||||||
doc["IPv4 nameserver"] = uuid::printable_to_string(WiFi.dnsIP());
|
doc["IPv4 DNS Server"] = uuid::printable_to_string(dns_ipv4(WiFi.STA));
|
||||||
|
|
||||||
if (WiFi.linkLocalIPv6().toString() != "0000:0000:0000:0000:0000:0000:0000:0000" && WiFi.linkLocalIPv6().toString() != "::") {
|
if (WiFi.linkLocalIPv6().toString() != "0000:0000:0000:0000:0000:0000:0000:0000" && WiFi.linkLocalIPv6().toString() != "::") {
|
||||||
doc["IPv6 address"] = uuid::printable_to_string(WiFi.linkLocalIPv6());
|
doc["IPv6 address"] = uuid::printable_to_string(WiFi.linkLocalIPv6());
|
||||||
}
|
}
|
||||||
@@ -1052,6 +1092,7 @@ void System::show_system(uuid::console::Shell & shell) {
|
|||||||
shell.println();
|
shell.println();
|
||||||
shell.println("System:");
|
shell.println("System:");
|
||||||
shell.printfln(" Version: %s", EMSESP_APP_VERSION);
|
shell.printfln(" Version: %s", EMSESP_APP_VERSION);
|
||||||
|
shell.printfln(" System name: %s", system_name_.c_str());
|
||||||
#ifndef EMSESP_STANDALONE
|
#ifndef EMSESP_STANDALONE
|
||||||
shell.printfln(" Platform: %s (%s)", EMSESP_PLATFORM, ESP.getChipModel());
|
shell.printfln(" Platform: %s (%s)", EMSESP_PLATFORM, ESP.getChipModel());
|
||||||
shell.printfln(" Model: %s", getBBQKeesGatewayDetails().c_str());
|
shell.printfln(" Model: %s", getBBQKeesGatewayDetails().c_str());
|
||||||
@@ -1168,7 +1209,7 @@ void System::show_system(uuid::console::Shell & shell) {
|
|||||||
shell.printfln(" Hostname: %s", WiFi.getHostname());
|
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());
|
shell.printfln(" IPv4 address: %s/%s", uuid::printable_to_string(WiFi.localIP()).c_str(), uuid::printable_to_string(WiFi.subnetMask()).c_str());
|
||||||
shell.printfln(" IPv4 gateway: %s", uuid::printable_to_string(WiFi.gatewayIP()).c_str());
|
shell.printfln(" IPv4 gateway: %s", uuid::printable_to_string(WiFi.gatewayIP()).c_str());
|
||||||
shell.printfln(" IPv4 nameserver: %s", uuid::printable_to_string(WiFi.dnsIP()).c_str());
|
shell.printfln(" IPv4 DNS Server: %s", uuid::printable_to_string(dns_ipv4(WiFi.STA)).c_str());
|
||||||
if (WiFi.linkLocalIPv6().toString() != "0000:0000:0000:0000:0000:0000:0000:0000" && WiFi.linkLocalIPv6().toString() != "::") {
|
if (WiFi.linkLocalIPv6().toString() != "0000:0000:0000:0000:0000:0000:0000:0000" && WiFi.linkLocalIPv6().toString() != "::") {
|
||||||
shell.printfln(" IPv6 address: %s", uuid::printable_to_string(WiFi.linkLocalIPv6()).c_str());
|
shell.printfln(" IPv6 address: %s", uuid::printable_to_string(WiFi.linkLocalIPv6()).c_str());
|
||||||
}
|
}
|
||||||
@@ -1201,7 +1242,7 @@ void System::show_system(uuid::console::Shell & shell) {
|
|||||||
shell.printfln(" Hostname: %s", ETH.getHostname());
|
shell.printfln(" Hostname: %s", ETH.getHostname());
|
||||||
shell.printfln(" IPv4 address: %s/%s", uuid::printable_to_string(ETH.localIP()).c_str(), uuid::printable_to_string(ETH.subnetMask()).c_str());
|
shell.printfln(" IPv4 address: %s/%s", uuid::printable_to_string(ETH.localIP()).c_str(), uuid::printable_to_string(ETH.subnetMask()).c_str());
|
||||||
shell.printfln(" IPv4 gateway: %s", uuid::printable_to_string(ETH.gatewayIP()).c_str());
|
shell.printfln(" IPv4 gateway: %s", uuid::printable_to_string(ETH.gatewayIP()).c_str());
|
||||||
shell.printfln(" IPv4 nameserver: %s", uuid::printable_to_string(ETH.dnsIP()).c_str());
|
shell.printfln(" IPv4 DNS Server: %s", uuid::printable_to_string(dns_ipv4(ETH)).c_str());
|
||||||
if (ETH.linkLocalIPv6().toString() != "0000:0000:0000:0000:0000:0000:0000:0000" && ETH.linkLocalIPv6().toString() != "::") {
|
if (ETH.linkLocalIPv6().toString() != "0000:0000:0000:0000:0000:0000:0000:0000" && ETH.linkLocalIPv6().toString() != "::") {
|
||||||
shell.printfln(" IPv6 address: %s", uuid::printable_to_string(ETH.linkLocalIPv6()).c_str());
|
shell.printfln(" IPv6 address: %s", uuid::printable_to_string(ETH.linkLocalIPv6()).c_str());
|
||||||
}
|
}
|
||||||
@@ -1532,7 +1573,7 @@ bool System::check_upgrade() {
|
|||||||
EMSESP::esp32React.getAPSettingsService()->update([&](APSettings & apSettings) {
|
EMSESP::esp32React.getAPSettingsService()->update([&](APSettings & apSettings) {
|
||||||
if (apSettings.provisionMode == 0) {
|
if (apSettings.provisionMode == 0) {
|
||||||
apSettings.provisionMode = AP_MODE_DISCONNECTED; // AP_MODE_DISCONNECTED is the new default
|
apSettings.provisionMode = AP_MODE_DISCONNECTED; // AP_MODE_DISCONNECTED is the new default
|
||||||
LOG_INFO("Upgrade: Setting AP provision mode to auto");
|
LOG_INFO("Upgrade: Setting AP provision mode to on disconnect");
|
||||||
return StateUpdateResult::CHANGED;
|
return StateUpdateResult::CHANGED;
|
||||||
}
|
}
|
||||||
return StateUpdateResult::UNCHANGED;
|
return StateUpdateResult::UNCHANGED;
|
||||||
@@ -1694,7 +1735,7 @@ static const std::pair<const char *, const char *> SECTION_MAP[] = {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// convert a single config file into a section of the output json object
|
// convert a single config file into a section of the output json object
|
||||||
void System::exportSettings(const std::string & type, const char * filename, JsonObject output) {
|
void System::exportSettings(const std::string & type, [[maybe_unused]] const char * filename, JsonObject output) {
|
||||||
if (type != "settings") {
|
if (type != "settings") {
|
||||||
output["type"] = type; // add the type to the output, not for settings as it's already added because its grouped
|
output["type"] = type; // add the type to the output, not for settings as it's already added because its grouped
|
||||||
}
|
}
|
||||||
@@ -1849,7 +1890,7 @@ void System::exportSystemBackup(JsonObject output) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// write a settings file using input from a json object, called from upload/restore
|
// write a settings file using input from a json object, called from upload/restore
|
||||||
bool System::saveSettings(const char * filename, JsonObject input) {
|
bool System::saveSettings([[maybe_unused]] const char * filename, [[maybe_unused]] JsonObject input) {
|
||||||
#ifndef EMSESP_STANDALONE
|
#ifndef EMSESP_STANDALONE
|
||||||
const char * section = nullptr;
|
const char * section = nullptr;
|
||||||
for (const auto & [f, label] : SECTION_MAP) {
|
for (const auto & [f, label] : SECTION_MAP) {
|
||||||
@@ -1938,24 +1979,6 @@ bool System::command_service(const char * cmd, const char * value) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int n;
|
|
||||||
if (!ok && Helpers::value2number(value, n)) {
|
|
||||||
#ifndef EMSESP_STANDALONE
|
|
||||||
if (!strcmp(cmd, "fuse/mfg")) {
|
|
||||||
ok = esp_efuse_write_reg(EFUSE_BLK3, 0, (uint32_t)n) == ESP_OK;
|
|
||||||
ok ? LOG_INFO("fuse programed with value '%X': successful", n) : LOG_ERROR("fuse programed with value '%X': failed", n);
|
|
||||||
}
|
|
||||||
if (!strcmp(cmd, "fuse/mfgadd")) {
|
|
||||||
uint8_t reg = 0;
|
|
||||||
while (esp_efuse_read_reg(EFUSE_BLK3, reg) != 0 && reg < 7)
|
|
||||||
reg++;
|
|
||||||
ok = esp_efuse_write_reg(EFUSE_BLK3, reg, (uint32_t)n) == ESP_OK;
|
|
||||||
ok ? LOG_INFO("fuse %d programed with value '%X': successful", reg, n) : LOG_ERROR("fuse %d programed with value '%X': failed", reg, n);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ok) {
|
if (ok) {
|
||||||
LOG_INFO("System command '%s' with value '%s'", cmd, value);
|
LOG_INFO("System command '%s' with value '%s'", cmd, value);
|
||||||
}
|
}
|
||||||
@@ -2782,6 +2805,9 @@ bool System::load_board_profile(std::vector<int8_t> & data, const std::string &
|
|||||||
// https://github.com/OLIMEX/ESP32-POE/blob/master/HARDWARE/ESP32-PoE-hardware-revision-L1/ESP32-PoE_Rev_L1.pdf
|
// https://github.com/OLIMEX/ESP32-POE/blob/master/HARDWARE/ESP32-PoE-hardware-revision-L1/ESP32-PoE_Rev_L1.pdf
|
||||||
// uart0 = 1, 3; SD-card = 2, 14, 15; button = 34;
|
// uart0 = 1, 3; SD-card = 2, 14, 15; button = 34;
|
||||||
valid_system_gpios_ = {4, 34, 36, 12, 13, 21, 22, 25, 26, 27, 32, 33, 39};
|
valid_system_gpios_ = {4, 34, 36, 12, 13, 21, 22, 25, 26, 27, 32, 33, 39};
|
||||||
|
} else if (board_profile == "OLIMEXPOEW") {
|
||||||
|
data = {0, 0, 36, 4, 34, PHY_type::PHY_TYPE_LAN8720, 12, 0, 1, 0}; // Olimex ESP32-POE-ISO WROVER (PSRAM)
|
||||||
|
valid_system_gpios_ = {4, 34, 36, 12, 13, 21, 22, 25, 26, 27, 32, 33, 39};
|
||||||
} else if (board_profile == "C3MINI") {
|
} else if (board_profile == "C3MINI") {
|
||||||
#if defined(BOARD_C3_MINI_V1)
|
#if defined(BOARD_C3_MINI_V1)
|
||||||
data = {7, 1, 4, 5, 9, PHY_type::PHY_TYPE_NONE, 0, 0, 0, 0}; // Lolin C3 Mini V1
|
data = {7, 1, 4, 5, 9, PHY_type::PHY_TYPE_NONE, 0, 0, 0, 0}; // Lolin C3 Mini V1
|
||||||
@@ -2819,7 +2845,7 @@ bool System::load_board_profile(std::vector<int8_t> & data, const std::string &
|
|||||||
// rgb for RGB
|
// rgb for RGB
|
||||||
// For example: /api/system/led?data=red:blink1
|
// For example: /api/system/led?data=red:blink1
|
||||||
// For older non-RGB models, the colour would default to just being on.
|
// For older non-RGB models, the colour would default to just being on.
|
||||||
bool System::command_led(const char * value, const int8_t id) {
|
bool System::command_led(const char * value, const int8_t) {
|
||||||
if (!value) {
|
if (!value) {
|
||||||
return false; // no argument
|
return false; // no argument
|
||||||
}
|
}
|
||||||
@@ -2842,7 +2868,7 @@ bool System::command_led(const char * value, const int8_t id) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// txpause command - temporarily pause the TX, by setting Txmode to 0 (disabled)
|
// txpause command - temporarily pause the TX, by setting Txmode to 0 (disabled)
|
||||||
bool System::command_txpause(const char * value, const int8_t id) {
|
bool System::command_txpause(const char * value, const int8_t) {
|
||||||
bool arg;
|
bool arg;
|
||||||
if (!Helpers::value2bool(value, arg)) {
|
if (!Helpers::value2bool(value, arg)) {
|
||||||
return false; // argument not recognized
|
return false; // argument not recognized
|
||||||
@@ -2874,7 +2900,7 @@ bool System::command_txpause(const char * value, const int8_t id) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// format command - factory reset, removing all config files
|
// format command - factory reset, removing all settings files
|
||||||
bool System::command_format(const char * value, const int8_t id) {
|
bool System::command_format(const char * value, const int8_t id) {
|
||||||
if (EMSESP::system_.disable_reset()) {
|
if (EMSESP::system_.disable_reset()) {
|
||||||
LOG_NOTICE("Factory reset disabled");
|
LOG_NOTICE("Factory reset disabled");
|
||||||
@@ -2884,7 +2910,7 @@ bool System::command_format(const char * value, const int8_t id) {
|
|||||||
#if !defined(EMSESP_STANDALONE) && !defined(EMSESP_TEST)
|
#if !defined(EMSESP_STANDALONE) && !defined(EMSESP_TEST)
|
||||||
// don't really format the filesystem in test or standalone mode
|
// don't really format the filesystem in test or standalone mode
|
||||||
if (LittleFS.format()) {
|
if (LittleFS.format()) {
|
||||||
LOG_INFO("Filesystem formatted successfully. All config files removed.");
|
LOG_INFO("Filesystem formatted successfully. All settings files removed.");
|
||||||
} else {
|
} else {
|
||||||
LOG_ERROR("Format failed");
|
LOG_ERROR("Format failed");
|
||||||
}
|
}
|
||||||
@@ -2907,7 +2933,7 @@ bool System::command_restart(const char * value, const int8_t id) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG_INFO("Restarting system immediately");
|
LOG_INFO("Restarting system...");
|
||||||
// restart will be handled by the main loop
|
// restart will be handled by the main loop
|
||||||
EMSESP::system_.systemStatus(SYSTEM_STATUS::SYSTEM_STATUS_RESTART_REQUESTED);
|
EMSESP::system_.systemStatus(SYSTEM_STATUS::SYSTEM_STATUS_RESTART_REQUESTED);
|
||||||
return true;
|
return true;
|
||||||
@@ -2985,7 +3011,7 @@ bool System::ntp_connected() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// see if its a BBQKees Gateway by checking the eFuse values
|
// see if its a BBQKees Gateway by checking the eFuse values
|
||||||
String System::getBBQKeesGatewayDetails(uint8_t detail) {
|
String System::getBBQKeesGatewayDetails([[maybe_unused]] uint8_t detail) {
|
||||||
#ifndef EMSESP_STANDALONE
|
#ifndef EMSESP_STANDALONE
|
||||||
union {
|
union {
|
||||||
struct {
|
struct {
|
||||||
@@ -3095,7 +3121,7 @@ bool System::uploadFirmwareURL(const char * url) {
|
|||||||
ssl_client.setSessionTimeout(120);
|
ssl_client.setSessionTimeout(120);
|
||||||
}
|
}
|
||||||
basic_client.setTimeout(15000); // socket-level read timeout
|
basic_client.setTimeout(15000); // socket-level read timeout
|
||||||
ssl_client.setTimeout(15000); // Stream::readBytes timeout used by Update
|
ssl_client.setTimeout(15); // Stream::readBytes timeout used by Update
|
||||||
ssl_client.setClient(&basic_client, is_https); // enableSSL = false for plain HTTP
|
ssl_client.setClient(&basic_client, is_https); // enableSSL = false for plain HTTP
|
||||||
|
|
||||||
const uint16_t port = is_https ? 443 : 80;
|
const uint16_t port = is_https ? 443 : 80;
|
||||||
@@ -3235,7 +3261,7 @@ bool System::uploadFirmwareURL(const char * url) {
|
|||||||
return false; // error
|
return false; // error
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG_INFO("Firmware uploading (size: %d KB) over %s. Please wait...", firmware_size / 1024, is_https ? "HTTPS" : "HTTP");
|
LOG_INFO("Firmware uploading (size: %dKB) over %s. Please wait...", firmware_size / 1024, is_https ? "HTTPS" : "HTTP");
|
||||||
|
|
||||||
Shell::loop_all(); // flush log buffers so latest messages are shown in console
|
Shell::loop_all(); // flush log buffers so latest messages are shown in console
|
||||||
|
|
||||||
@@ -3401,7 +3427,7 @@ bool System::readCommand(const char * data) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// system read command
|
// system read command
|
||||||
bool System::command_read(const char * value, const int8_t id) {
|
bool System::command_read(const char * value, const int8_t) {
|
||||||
return readCommand(value);
|
return readCommand(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3696,7 +3722,7 @@ void System::restore_snapshot_gpios(std::vector<int8_t> & u_gpios, std::vector<i
|
|||||||
}
|
}
|
||||||
|
|
||||||
// show the contents of a directory in the LittleFS filesystem
|
// show the contents of a directory in the LittleFS filesystem
|
||||||
void System::listDir(const char * dirname, uint8_t levels) {
|
void System::listDir([[maybe_unused]] const char * dirname, [[maybe_unused]] uint8_t levels) {
|
||||||
#if defined(EMSESP_DEBUG)
|
#if defined(EMSESP_DEBUG)
|
||||||
#ifndef EMSESP_STANDALONE
|
#ifndef EMSESP_STANDALONE
|
||||||
|
|
||||||
|
|||||||
@@ -45,17 +45,20 @@ void TemperatureSensor::start(const bool factory_settings) {
|
|||||||
Mqtt::subscribe(EMSdevice::DeviceType::TEMPERATURESENSOR, topic, nullptr); // use empty function callback
|
Mqtt::subscribe(EMSdevice::DeviceType::TEMPERATURESENSOR, topic, nullptr); // use empty function callback
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TemperatureSensor::ha_reset() {
|
||||||
|
for (auto & sensor : sensors_) {
|
||||||
|
remove_ha_topic(sensor.id());
|
||||||
|
sensor.ha_registered = false; // force HA configs to be re-created
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// load settings
|
// load settings
|
||||||
void TemperatureSensor::reload() {
|
void TemperatureSensor::reload() {
|
||||||
EMSESP::webSettingsService.read([&](WebSettings const & settings) {
|
EMSESP::webSettingsService.read([&](WebSettings const & settings) {
|
||||||
dallas_gpio_ = settings.dallas_gpio;
|
dallas_gpio_ = settings.dallas_gpio;
|
||||||
parasite_ = settings.dallas_parasite;
|
parasite_ = settings.dallas_parasite;
|
||||||
});
|
});
|
||||||
|
ha_reset();
|
||||||
for (auto & sensor : sensors_) {
|
|
||||||
remove_ha_topic(sensor.id());
|
|
||||||
sensor.ha_registered = false; // force HA configs to be re-created
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TemperatureSensor::loop() {
|
void TemperatureSensor::loop() {
|
||||||
@@ -543,7 +546,7 @@ void TemperatureSensor::publish_values(const bool force) {
|
|||||||
// for the value template, there's a problem still with Domoticz probably due to the special characters.
|
// for the value template, there's a problem still with Domoticz probably due to the special characters.
|
||||||
// See https://github.com/emsesp/EMS-ESP32/issues/1360
|
// See https://github.com/emsesp/EMS-ESP32/issues/1360
|
||||||
if (Mqtt::discovery_type() == Mqtt::discoveryType::HOMEASSISTANT) {
|
if (Mqtt::discovery_type() == Mqtt::discoveryType::HOMEASSISTANT) {
|
||||||
config["val_tpl"] = (std::string) "{{" + val_obj + " if " + val_cond + " else -55}}";
|
config["val_tpl"] = (std::string) "{{" + val_obj + " if " + val_cond + " else 'None'}}";
|
||||||
} else {
|
} else {
|
||||||
config["val_tpl"] = (std::string) "{{" + val_obj + "}}"; // omit value conditional Jinja2 template code
|
config["val_tpl"] = (std::string) "{{" + val_obj + "}}"; // omit value conditional Jinja2 template code
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -93,6 +93,7 @@ class TemperatureSensor {
|
|||||||
void publish_sensor(const Sensor & sensor);
|
void publish_sensor(const Sensor & sensor);
|
||||||
void publish_values(const bool force);
|
void publish_values(const bool force);
|
||||||
void reload();
|
void reload();
|
||||||
|
void ha_reset();
|
||||||
bool updated_values();
|
bool updated_values();
|
||||||
bool get_value_info(JsonObject output, const char * cmd, const int8_t id = -1);
|
bool get_value_info(JsonObject output, const char * cmd, const int8_t id = -1);
|
||||||
|
|
||||||
|
|||||||
+21
-21
@@ -220,13 +220,13 @@ Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const
|
|||||||
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &fanWork_, DeviceValueType::BOOL, FL_(fanWork), DeviceValueUOM::NONE);
|
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &fanWork_, DeviceValueType::BOOL, FL_(fanWork), DeviceValueUOM::NONE);
|
||||||
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &ignWork_, DeviceValueType::BOOL, FL_(ignWork), DeviceValueUOM::NONE);
|
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &ignWork_, DeviceValueType::BOOL, FL_(ignWork), DeviceValueUOM::NONE);
|
||||||
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &oilPreHeat_, DeviceValueType::BOOL, FL_(oilPreHeat), DeviceValueUOM::NONE);
|
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &oilPreHeat_, DeviceValueType::BOOL, FL_(oilPreHeat), DeviceValueUOM::NONE);
|
||||||
// remove burnMinPower, #2918
|
// remove burnMinPower, #2918, add back for 0xE6, #3193
|
||||||
// register_device_value(DeviceValueTAG::TAG_DEVICE_DATA,
|
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA,
|
||||||
// &burnMinPower_,
|
&burnMinPower_,
|
||||||
// DeviceValueType::UINT8,
|
DeviceValueType::UINT8,
|
||||||
// FL_(burnMinPower),
|
FL_(burnMinPower),
|
||||||
// DeviceValueUOM::PERCENT,
|
DeviceValueUOM::PERCENT,
|
||||||
// MAKE_CF_CB(set_min_power));
|
MAKE_CF_CB(set_min_power));
|
||||||
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA,
|
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA,
|
||||||
&burnMaxPower_,
|
&burnMaxPower_,
|
||||||
DeviceValueType::UINT8,
|
DeviceValueType::UINT8,
|
||||||
@@ -1622,7 +1622,7 @@ void Boiler::process_UBAParametersPlus(const std::shared_ptr<const Telegram> & t
|
|||||||
has_update(telegram, heatingActivated_, 0);
|
has_update(telegram, heatingActivated_, 0);
|
||||||
has_update(telegram, heatingTemp_, 1);
|
has_update(telegram, heatingTemp_, 1);
|
||||||
has_update(telegram, burnMaxPower_, 4);
|
has_update(telegram, burnMaxPower_, 4);
|
||||||
// has_update(telegram, burnMinPower_, 5); // removed #2918
|
has_update(telegram, burnMinPower_, 5); // removed #2918
|
||||||
has_update(telegram, boilHystOff_, 8);
|
has_update(telegram, boilHystOff_, 8);
|
||||||
has_update(telegram, boilHystOn_, 9);
|
has_update(telegram, boilHystOn_, 9);
|
||||||
has_update(telegram, burnMinPeriod_, 10);
|
has_update(telegram, burnMinPeriod_, 10);
|
||||||
@@ -1822,7 +1822,7 @@ void Boiler::process_HpPool(const std::shared_ptr<const Telegram> & telegram) {
|
|||||||
// Boiler(0x08) -> All(0x00), ?(0x04A2), data: 02 01 01 00 01 00
|
// Boiler(0x08) -> All(0x00), ?(0x04A2), data: 02 01 01 00 01 00
|
||||||
// Boiler(0x08) -W-> Me(0x0B), HpInput(0x04A2), data: 20 07 06 01 00 (from #802)
|
// Boiler(0x08) -W-> Me(0x0B), HpInput(0x04A2), data: 20 07 06 01 00 (from #802)
|
||||||
// see https://github.com/emsesp/EMS-ESP32/issues/2844#issuecomment-3689049155
|
// see https://github.com/emsesp/EMS-ESP32/issues/2844#issuecomment-3689049155
|
||||||
void Boiler::process_HpInput(const std::shared_ptr<const Telegram> & telegram) {
|
void Boiler::process_HpInput(const std::shared_ptr<const Telegram> &) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Heatpump inputs settings- type 0x486 (https://github.com/emsesp/EMS-ESP32/issues/600)
|
// Heatpump inputs settings- type 0x486 (https://github.com/emsesp/EMS-ESP32/issues/600)
|
||||||
@@ -1923,7 +1923,7 @@ void Boiler::process_UBASetPoints2(const std::shared_ptr<const Telegram> & teleg
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 0x35 - not yet implemented, not readable, only for settings
|
// 0x35 - not yet implemented, not readable, only for settings
|
||||||
void Boiler::process_UBAFlags(const std::shared_ptr<const Telegram> & telegram) {
|
void Boiler::process_UBAFlags(const std::shared_ptr<const Telegram> &) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 0x1C
|
// 0x1C
|
||||||
@@ -1942,7 +1942,7 @@ void Boiler::process_UBAMaintenanceStatus(const std::shared_ptr<const Telegram>
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 0xBF
|
// 0xBF
|
||||||
void Boiler::process_ErrorMessage(const std::shared_ptr<const Telegram> & telegram) {
|
void Boiler::process_ErrorMessage(const std::shared_ptr<const Telegram> &) {
|
||||||
EMSESP::send_read_request(0xC2, device_id(), 0, 20); // read last errorcode
|
EMSESP::send_read_request(0xC2, device_id(), 0, 20); // read last errorcode
|
||||||
EMSESP::send_read_request(0xC6, device_id(), 0, 21); // read last errorcode
|
EMSESP::send_read_request(0xC6, device_id(), 0, 21); // read last errorcode
|
||||||
}
|
}
|
||||||
@@ -2494,7 +2494,7 @@ bool Boiler::set_ww_disinfect_temp(const char * value, const int8_t id) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Set the dhw priority
|
// Set the dhw priority
|
||||||
bool Boiler::set_ww_prio(const char * value, const int8_t id) {
|
bool Boiler::set_ww_prio(const char * value, const int8_t) {
|
||||||
bool b;
|
bool b;
|
||||||
if (!Helpers::value2bool(value, b)) {
|
if (!Helpers::value2bool(value, b)) {
|
||||||
return false;
|
return false;
|
||||||
@@ -2814,7 +2814,7 @@ bool Boiler::set_pump_delay(const char * value, const int8_t id) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// set pump logic temperature
|
// set pump logic temperature
|
||||||
bool Boiler::set_pumpOnTemp(const char * value, const int8_t id) {
|
bool Boiler::set_pumpOnTemp(const char * value, const int8_t) {
|
||||||
int v;
|
int v;
|
||||||
if (!Helpers::value2temperature(value, v)) {
|
if (!Helpers::value2temperature(value, v)) {
|
||||||
return false;
|
return false;
|
||||||
@@ -3037,7 +3037,7 @@ bool Boiler::set_reset(const char * value, const int8_t id) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Boiler::set_manDefrost(const char * value, const int8_t id) {
|
bool Boiler::set_manDefrost(const char * value, const int8_t) {
|
||||||
bool b;
|
bool b;
|
||||||
if (!Helpers::value2bool(value, b)) {
|
if (!Helpers::value2bool(value, b)) {
|
||||||
return false;
|
return false;
|
||||||
@@ -3046,7 +3046,7 @@ bool Boiler::set_manDefrost(const char * value, const int8_t id) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Boiler::set_chimneysweeper(const char * value, const int8_t id) {
|
bool Boiler::set_chimneysweeper(const char * value, const int8_t) {
|
||||||
bool b;
|
bool b;
|
||||||
if (!Helpers::value2bool(value, b)) {
|
if (!Helpers::value2bool(value, b)) {
|
||||||
return false;
|
return false;
|
||||||
@@ -3325,7 +3325,7 @@ bool Boiler::set_auxLimit(const char * value, const int8_t id) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Boiler::set_auxHeaterSource(const char * value, const int8_t id) {
|
bool Boiler::set_auxHeaterSource(const char * value, const int8_t) {
|
||||||
uint8_t v;
|
uint8_t v;
|
||||||
if (Helpers::value2enum(value, v, FL_(enum_auxHeaterSource))) {
|
if (Helpers::value2enum(value, v, FL_(enum_auxHeaterSource))) {
|
||||||
write_command(0x491, 0, v, 0x491);
|
write_command(0x491, 0, v, 0x491);
|
||||||
@@ -3409,7 +3409,7 @@ bool Boiler::set_hpMaxPower(const char * value, const int8_t id) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Boiler::set_pvMaxComp(const char * value, const int8_t id) {
|
bool Boiler::set_pvMaxComp(const char * value, const int8_t) {
|
||||||
float v;
|
float v;
|
||||||
if (Helpers::value2float(value, v)) {
|
if (Helpers::value2float(value, v)) {
|
||||||
write_command(0x484, 54, (uint8_t)(v * 10), 0x484);
|
write_command(0x484, 54, (uint8_t)(v * 10), 0x484);
|
||||||
@@ -3437,7 +3437,7 @@ bool Boiler::set_hpPowerLimit(const char * value, const int8_t id) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Boiler::set_powerReduction(const char * value, const int8_t id) {
|
bool Boiler::set_powerReduction(const char * value, const int8_t) {
|
||||||
int v;
|
int v;
|
||||||
if (Helpers::value2number(value, v)) {
|
if (Helpers::value2number(value, v)) {
|
||||||
write_command(0x484, 64, v / 10, 0x484);
|
write_command(0x484, 64, v / 10, 0x484);
|
||||||
@@ -3661,7 +3661,7 @@ bool Boiler::set_shutdown(const char * value, const int8_t id) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Boiler::set_pumpKickHour(const char * value, const int8_t id) {
|
bool Boiler::set_pumpKickHour(const char * value, const int8_t) {
|
||||||
int v;
|
int v;
|
||||||
if (Helpers::value2number(value, v, 0, 23)) {
|
if (Helpers::value2number(value, v, 0, 23)) {
|
||||||
write_command(0xEB, 0, v, 0xEB);
|
write_command(0xEB, 0, v, 0xEB);
|
||||||
@@ -3670,7 +3670,7 @@ bool Boiler::set_pumpKickHour(const char * value, const int8_t id) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Boiler::set_pumpKickDay(const char * value, const int8_t id) {
|
bool Boiler::set_pumpKickDay(const char * value, const int8_t) {
|
||||||
uint8_t v;
|
uint8_t v;
|
||||||
if (Helpers::value2enum(value, v, FL_(enum_dayOfWeek))) {
|
if (Helpers::value2enum(value, v, FL_(enum_dayOfWeek))) {
|
||||||
write_command(0xEB, 1, v + 1, 0xEB);
|
write_command(0xEB, 1, v + 1, 0xEB);
|
||||||
@@ -3679,7 +3679,7 @@ bool Boiler::set_pumpKickDay(const char * value, const int8_t id) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Boiler::set_pumpKickDelay(const char * value, const int8_t id) {
|
bool Boiler::set_pumpKickDelay(const char * value, const int8_t) {
|
||||||
int v;
|
int v;
|
||||||
if (Helpers::value2number(value, v, 0, 32767)) {
|
if (Helpers::value2number(value, v, 0, 32767)) {
|
||||||
uint8_t data[2] = {(uint8_t)(v >> 8), (uint8_t)v};
|
uint8_t data[2] = {(uint8_t)(v >> 8), (uint8_t)v};
|
||||||
|
|||||||
@@ -278,7 +278,7 @@ bool Mixer::set_flowTempOffset(const char * value, const int8_t id) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Mixer::set_pressure(const char * value, const int8_t id) {
|
bool Mixer::set_pressure(const char * value, const int8_t) {
|
||||||
int v;
|
int v;
|
||||||
if (!Helpers::value2number(value, v)) {
|
if (!Helpers::value2number(value, v)) {
|
||||||
return false;
|
return false;
|
||||||
@@ -288,7 +288,7 @@ bool Mixer::set_pressure(const char * value, const int8_t id) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Mixer::set_wwprio(const char * value, const int8_t id) {
|
bool Mixer::set_wwprio(const char * value, const int8_t) {
|
||||||
bool b;
|
bool b;
|
||||||
if (!Helpers::value2bool(value, b)) {
|
if (!Helpers::value2bool(value, b)) {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -935,7 +935,7 @@ bool Solar::set_heatTransferSystem(const char * value, const int8_t id) {
|
|||||||
if (!Helpers::value2bool(value, b)) {
|
if (!Helpers::value2bool(value, b)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
write_command(0x358, 5, b ? 0x01 : 0x00, 0x358);
|
write_command(0x358, 5, b ? 0xFF : 0x00); // change is broadcasted
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -945,7 +945,7 @@ bool Solar::set_externalCyl(const char * value, const int8_t id) {
|
|||||||
if (!Helpers::value2bool(value, b)) {
|
if (!Helpers::value2bool(value, b)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
write_command(0x358, 9, b ? 0x01 : 0x00, 0x358);
|
write_command(0x358, 9, b ? 0xFF : 0x00); // change is broadcasted
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -955,7 +955,7 @@ bool Solar::set_thermalDisinfect(const char * value, const int8_t id) {
|
|||||||
if (!Helpers::value2bool(value, b)) {
|
if (!Helpers::value2bool(value, b)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
write_command(0x358, 10, b ? 0x01 : 0x00, 0x358);
|
write_command(0x358, 10, b ? 0xFF : 0x00); // change is broadcasted
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -965,7 +965,7 @@ bool Solar::set_heatMetering(const char * value, const int8_t id) {
|
|||||||
if (!Helpers::value2bool(value, b)) {
|
if (!Helpers::value2bool(value, b)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
write_command(0x358, 14, b ? 0x01 : 0x00, 0x358);
|
write_command(0x358, 14, b ? 0xFF : 0x00); // change is broadcasted
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -978,7 +978,7 @@ bool Solar::set_solarEnabled(const char * value, const int8_t id) {
|
|||||||
if (flags() == EMSdevice::EMS_DEVICE_FLAG_SM10) {
|
if (flags() == EMSdevice::EMS_DEVICE_FLAG_SM10) {
|
||||||
write_command(0x96, 0, b ? 0xFF : 0x00, 0x96);
|
write_command(0x96, 0, b ? 0xFF : 0x00, 0x96);
|
||||||
} else {
|
} else {
|
||||||
write_command(0x358, 19, b ? 0x01 : 0x00, 0x358);
|
write_command(0x358, 19, b ? 0xFF : 0x00); // change is broadcasted
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -1122,7 +1122,7 @@ bool Solar::set_diffControl(const char * value, const int8_t id) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Solar::set_solarHeatAssistOn(const char * value, const int8_t id) {
|
bool Solar::set_solarHeatAssistOn(const char * value, const int8_t) {
|
||||||
float t;
|
float t;
|
||||||
if (!Helpers::value2float(value, t)) {
|
if (!Helpers::value2float(value, t)) {
|
||||||
return false;
|
return false;
|
||||||
@@ -1131,7 +1131,7 @@ bool Solar::set_solarHeatAssistOn(const char * value, const int8_t id) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Solar::set_solarHeatAssistOff(const char * value, const int8_t id) {
|
bool Solar::set_solarHeatAssistOff(const char * value, const int8_t) {
|
||||||
float t;
|
float t;
|
||||||
if (!Helpers::value2float(value, t)) {
|
if (!Helpers::value2float(value, t)) {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
+55
-31
@@ -1663,32 +1663,40 @@ void Thermostat::process_RC35Timer(const std::shared_ptr<const Telegram> & teleg
|
|||||||
has_update(telegram, hc->pause, 85); // time in hours
|
has_update(telegram, hc->pause, 85); // time in hours
|
||||||
has_update(telegram, hc->party, 86); // time in hours
|
has_update(telegram, hc->party, 86); // time in hours
|
||||||
|
|
||||||
if (telegram->message_length + telegram->offset >= 92 && telegram->offset <= 87) {
|
// vacation dates are in positions 87 to 92
|
||||||
char data[sizeof(hc->vacation) + 4]; // avoid compiler warning
|
if (telegram->offset <= 87 && (telegram->offset + telegram->message_length) > 92) {
|
||||||
snprintf(data,
|
const uint8_t pos = 87 - telegram->offset;
|
||||||
sizeof(data),
|
if ((pos + 5) < EMS_MAX_TELEGRAM_MESSAGE_LENGTH) {
|
||||||
"%02d.%02d.%04d-%02d.%02d.%04d",
|
char data[sizeof(hc->vacation) + 4]; // avoid compiler warning
|
||||||
telegram->message_data[87 - telegram->offset],
|
snprintf(data,
|
||||||
telegram->message_data[88 - telegram->offset],
|
sizeof(data),
|
||||||
telegram->message_data[89 - telegram->offset] + 2000,
|
"%02d.%02d.%04d-%02d.%02d.%04d",
|
||||||
telegram->message_data[90 - telegram->offset],
|
telegram->message_data[pos],
|
||||||
telegram->message_data[91 - telegram->offset],
|
telegram->message_data[pos + 1],
|
||||||
telegram->message_data[92 - telegram->offset] + 2000);
|
telegram->message_data[pos + 2] + 2000,
|
||||||
has_update(hc->vacation, data, sizeof(hc->vacation));
|
telegram->message_data[pos + 3],
|
||||||
|
telegram->message_data[pos + 4],
|
||||||
|
telegram->message_data[pos + 5] + 2000);
|
||||||
|
has_update(hc->vacation, data, sizeof(hc->vacation));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (telegram->message_length + telegram->offset >= 98 && telegram->offset <= 93) {
|
// holiday dates are in positions 93 to 98
|
||||||
char data[sizeof(hc->holiday) + 4]; // avoid compiler warning
|
if (telegram->offset <= 93 && (telegram->offset + telegram->message_length) > 98) {
|
||||||
snprintf(data,
|
const uint8_t pos = 93 - telegram->offset;
|
||||||
sizeof(data),
|
if ((pos + 5) < EMS_MAX_TELEGRAM_MESSAGE_LENGTH) {
|
||||||
"%02d.%02d.%04d-%02d.%02d.%04d",
|
char data[sizeof(hc->holiday) + 4]; // avoid compiler warning
|
||||||
telegram->message_data[93 - telegram->offset],
|
snprintf(data,
|
||||||
telegram->message_data[94 - telegram->offset],
|
sizeof(data),
|
||||||
telegram->message_data[95 - telegram->offset] + 2000,
|
"%02d.%02d.%04d-%02d.%02d.%04d",
|
||||||
telegram->message_data[96 - telegram->offset],
|
telegram->message_data[pos],
|
||||||
telegram->message_data[97 - telegram->offset],
|
telegram->message_data[pos + 1],
|
||||||
telegram->message_data[98 - telegram->offset] + 2000);
|
telegram->message_data[pos + 2] + 2000,
|
||||||
has_update(hc->holiday, data, sizeof(hc->holiday));
|
telegram->message_data[pos + 3],
|
||||||
|
telegram->message_data[pos + 4],
|
||||||
|
telegram->message_data[pos + 5] + 2000);
|
||||||
|
has_update(hc->holiday, data, sizeof(hc->holiday));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1862,7 +1870,7 @@ void Thermostat::process_RCErrorMessage(const std::shared_ptr<const Telegram> &
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 0xBF
|
// 0xBF
|
||||||
void Thermostat::process_ErrorMessageBF(const std::shared_ptr<const Telegram> & telegram) {
|
void Thermostat::process_ErrorMessageBF(const std::shared_ptr<const Telegram> &) {
|
||||||
EMSESP::send_read_request(0xC0, device_id(), 0, 20); // read last errorcode
|
EMSESP::send_read_request(0xC0, device_id(), 0, 20); // read last errorcode
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2338,7 +2346,7 @@ bool Thermostat::set_damping(const char * value, const int8_t id) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 0x0241- Set solar
|
// 0x0241- Set solar
|
||||||
bool Thermostat::set_solar(const char * value, const int8_t id) {
|
bool Thermostat::set_solar(const char * value, const int8_t) {
|
||||||
bool b;
|
bool b;
|
||||||
if (Helpers::value2bool(value, b)) {
|
if (Helpers::value2bool(value, b)) {
|
||||||
if (model() == EMSdevice::EMS_DEVICE_FLAG_RC100) {
|
if (model() == EMSdevice::EMS_DEVICE_FLAG_RC100) {
|
||||||
@@ -2393,7 +2401,7 @@ bool Thermostat::set_control(const char * value, const int8_t id) {
|
|||||||
}
|
}
|
||||||
// BC400
|
// BC400
|
||||||
// 1-RC100, 2-RC100H, 3-RC200
|
// 1-RC100, 2-RC100H, 3-RC200
|
||||||
} else if (model() == EMSdevice::EMS_DEVICE_FLAG_BC400 || model() == EMSdevice::EMS_DEVICE_FLAG_UI800) {
|
} else if (model() == EMSdevice::EMS_DEVICE_FLAG_BC400) {
|
||||||
if (Helpers::value2enum(value, ctrl, FL_(enum_control2))) {
|
if (Helpers::value2enum(value, ctrl, FL_(enum_control2))) {
|
||||||
write_command(hpmode_typeids[hc->hc()], 3, ctrl);
|
write_command(hpmode_typeids[hc->hc()], 3, ctrl);
|
||||||
hc->control = ctrl; // set in advance, dont wait for verify
|
hc->control = ctrl; // set in advance, dont wait for verify
|
||||||
@@ -2413,6 +2421,20 @@ bool Thermostat::set_control(const char * value, const int8_t id) {
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
} else if (model() == EMSdevice::EMS_DEVICE_FLAG_UI800) {
|
||||||
|
if (Helpers::value2enum(value, ctrl, FL_(enum_control3))) {
|
||||||
|
write_command(hpmode_typeids[hc->hc()], 3, ctrl);
|
||||||
|
hc->control = ctrl; // set in advance, dont wait for verify
|
||||||
|
if (hc->remotetemp != EMS_VALUE_INT16_NOTSET && ctrl > 0) {
|
||||||
|
if (ctrl == 6) {
|
||||||
|
Roomctrl::set_remotetemp(Roomctrl::RT800, hc->hc(), hc->remotetemp);
|
||||||
|
} else {
|
||||||
|
hc->remotetemp = EMS_VALUE_INT16_NOTSET;
|
||||||
|
Roomctrl::set_remotetemp(0, hc->hc(), hc->remotetemp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
// RC100 or RC300
|
// RC100 or RC300
|
||||||
// 1-RC200, 2-RC100, 3-RC100H
|
// 1-RC200, 2-RC100, 3-RC100H
|
||||||
} else if (isRC300() || model() == EMSdevice::EMS_DEVICE_FLAG_RC100) {
|
} else if (isRC300() || model() == EMSdevice::EMS_DEVICE_FLAG_RC100) {
|
||||||
@@ -3034,7 +3056,7 @@ bool Thermostat::set_party(const char * value, const int8_t id) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Thermostat::set_absent(const char * value, const int8_t id) {
|
bool Thermostat::set_absent(const char * value, const int8_t) {
|
||||||
bool b;
|
bool b;
|
||||||
if (Helpers::value2bool(value, b)) {
|
if (Helpers::value2bool(value, b)) {
|
||||||
write_command(0x16E, 0, b ? 0xFF : 0, 0x16E);
|
write_command(0x16E, 0, b ? 0xFF : 0, 0x16E);
|
||||||
@@ -4693,7 +4715,7 @@ void Thermostat::register_device_values() {
|
|||||||
1,
|
1,
|
||||||
10);
|
10);
|
||||||
register_device_value(
|
register_device_value(
|
||||||
DeviceValueTAG::TAG_DEVICE_DATA, &coolstart, DeviceValueType::UINT8, FL_(coolstart), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_coolstart), 20, 35);
|
DeviceValueTAG::TAG_DEVICE_DATA, &coolstart, DeviceValueType::UINT8, FL_(coolstart), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_coolstart), 24, 35);
|
||||||
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA,
|
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA,
|
||||||
&heatondelay,
|
&heatondelay,
|
||||||
DeviceValueType::UINT8,
|
DeviceValueType::UINT8,
|
||||||
@@ -5172,7 +5194,7 @@ void Thermostat::register_device_values_hc(std::shared_ptr<Thermostat::HeatingCi
|
|||||||
register_device_value(tag, &hc->control, DeviceValueType::ENUM, FL_(enum_control2), FL_(control), DeviceValueUOM::NONE, MAKE_CF_CB(set_control));
|
register_device_value(tag, &hc->control, DeviceValueType::ENUM, FL_(enum_control2), FL_(control), DeviceValueUOM::NONE, MAKE_CF_CB(set_control));
|
||||||
} else if (model == EMSdevice::EMS_DEVICE_FLAG_UI800) {
|
} else if (model == EMSdevice::EMS_DEVICE_FLAG_UI800) {
|
||||||
register_device_value(tag, &hc->control, DeviceValueType::ENUM, FL_(enum_control3), FL_(control), DeviceValueUOM::NONE, MAKE_CF_CB(set_control));
|
register_device_value(tag, &hc->control, DeviceValueType::ENUM, FL_(enum_control3), FL_(control), DeviceValueUOM::NONE, MAKE_CF_CB(set_control));
|
||||||
} else {
|
} else if (device_id() == 0x10) { // only master thermostats have control by remote
|
||||||
register_device_value(tag, &hc->control, DeviceValueType::ENUM, FL_(enum_control1), FL_(control), DeviceValueUOM::NONE, MAKE_CF_CB(set_control));
|
register_device_value(tag, &hc->control, DeviceValueType::ENUM, FL_(enum_control1), FL_(control), DeviceValueUOM::NONE, MAKE_CF_CB(set_control));
|
||||||
}
|
}
|
||||||
if (model != EMSdevice::EMS_DEVICE_FLAG_RC100 && model != EMSdevice::EMS_DEVICE_FLAG_CR120) {
|
if (model != EMSdevice::EMS_DEVICE_FLAG_RC100 && model != EMSdevice::EMS_DEVICE_FLAG_CR120) {
|
||||||
@@ -5476,7 +5498,9 @@ void Thermostat::register_device_values_hc(std::shared_ptr<Thermostat::HeatingCi
|
|||||||
FL_(nofrosttemp),
|
FL_(nofrosttemp),
|
||||||
DeviceValueUOM::DEGREES,
|
DeviceValueUOM::DEGREES,
|
||||||
MAKE_CF_CB(set_nofrosttemp));
|
MAKE_CF_CB(set_nofrosttemp));
|
||||||
register_device_value(tag, &hc->control, DeviceValueType::ENUM, FL_(enum_j_control), FL_(control), DeviceValueUOM::NONE, MAKE_CF_CB(set_control));
|
if (device_id() == 0x10) { // only master thermostats have control by remote
|
||||||
|
register_device_value(tag, &hc->control, DeviceValueType::ENUM, FL_(enum_j_control), FL_(control), DeviceValueUOM::NONE, MAKE_CF_CB(set_control));
|
||||||
|
}
|
||||||
register_device_value(tag, &hc->program, DeviceValueType::ENUM, FL_(enum_progMode4), FL_(program), DeviceValueUOM::NONE, MAKE_CF_CB(set_program));
|
register_device_value(tag, &hc->program, DeviceValueType::ENUM, FL_(enum_progMode4), FL_(program), DeviceValueUOM::NONE, MAKE_CF_CB(set_program));
|
||||||
register_device_value(tag,
|
register_device_value(tag,
|
||||||
&hc->remotetemp,
|
&hc->remotetemp,
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ void Ventilation::process_ModeMessage(const std::shared_ptr<const Telegram> & te
|
|||||||
}
|
}
|
||||||
|
|
||||||
// message 0x0587, data: 00 00 64 00 64 0A 00 01 54 01 00 01 00 00 00 46 00 00 00 02 00 A3 00 A3
|
// message 0x0587, data: 00 00 64 00 64 0A 00 01 54 01 00 01 00 00 00 46 00 00 00 02 00 A3 00 A3
|
||||||
void Ventilation::process_BypassMessage(const std::shared_ptr<const Telegram> & telegram) {
|
void Ventilation::process_BypassMessage(const std::shared_ptr<const Telegram> &) {
|
||||||
// has_update(telegram, bypass_closing, 0);
|
// has_update(telegram, bypass_closing, 0);
|
||||||
// has_update(telegram, bypass_opening, 1);
|
// has_update(telegram, bypass_opening, 1);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
#define EMSESP_APP_VERSION "3.9.0-dev.0"
|
#define EMSESP_APP_VERSION "3.9.0-dev.8"
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
#include "WebCommandService.h"
|
#include "WebCommandService.h"
|
||||||
|
|
||||||
#include "shuntingYard.h"
|
#include "shuntingYard.h"
|
||||||
|
#include "httpClient.h"
|
||||||
|
|
||||||
namespace emsesp {
|
namespace emsesp {
|
||||||
|
|
||||||
@@ -99,6 +100,13 @@ bool WebCommandService::isUrlCommand(const std::string & command) {
|
|||||||
return lower_url.starts_with("http://") || lower_url.starts_with("https://");
|
return lower_url.starts_with("http://") || lower_url.starts_with("https://");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// true if the command runs the shunting-yard on its own argument. Those values must be passed
|
||||||
|
// through raw: a first pass strips the quotes from literal text, so the command's own pass then
|
||||||
|
// re-parses that text as an expression and chokes on characters like '!' or drops the spaces.
|
||||||
|
bool WebCommandService::computesOwnValue(const std::string & cmd) {
|
||||||
|
return cmd == "system/message" || cmd == "system/sendmail";
|
||||||
|
}
|
||||||
|
|
||||||
// true if a value expression contains an embedded {"url":...} JSON snippet, which compute()
|
// true if a value expression contains an embedded {"url":...} JSON snippet, which compute()
|
||||||
// will resolve with a blocking HTTP request. Mirrors the scan compute() does in shuntingYard.cpp
|
// will resolve with a blocking HTTP request. Mirrors the scan compute() does in shuntingYard.cpp
|
||||||
bool WebCommandService::valueContainsUrl(const std::string & value) {
|
bool WebCommandService::valueContainsUrl(const std::string & value) {
|
||||||
@@ -195,7 +203,7 @@ StateUpdateResult WebCommands::update(JsonObject root, WebCommands & webCommands
|
|||||||
Command::add(
|
Command::add(
|
||||||
EMSdevice::DeviceType::COMMAND,
|
EMSdevice::DeviceType::COMMAND,
|
||||||
webCommands.commandItems.back().name,
|
webCommands.commandItems.back().name,
|
||||||
[name = std::string(webCommands.commandItems.back().name)](const char * value, const int8_t id, JsonObject output) {
|
[name = std::string(webCommands.commandItems.back().name)](const char * value, const int8_t, JsonObject) {
|
||||||
return EMSESP::webCommandService.dispatchCommand(name.c_str(), value); // value is optional
|
return EMSESP::webCommandService.dispatchCommand(name.c_str(), value); // value is optional
|
||||||
},
|
},
|
||||||
FL_(command_cmd),
|
FL_(command_cmd),
|
||||||
@@ -238,13 +246,13 @@ bool WebCommandService::executeCommand(const char * name, const std::string & co
|
|||||||
// run the value through the shunting-yard calculator so expressions like "custom/heatcnt + 1"
|
// run the value through the shunting-yard calculator so expressions like "custom/heatcnt + 1"
|
||||||
// are resolved (entity references replaced by their values, then computed). Plain values pass
|
// are resolved (entity references replaced by their values, then computed). Plain values pass
|
||||||
// through unchanged. Applies to both URL and internal commands, like the old scheduler code
|
// through unchanged. Applies to both URL and internal commands, like the old scheduler code
|
||||||
// which computed the value before executing. system/message runs the shunting-yard on its own
|
// which computed the value before executing. Commands that compute their own argument are
|
||||||
// argument, so pre-computing it here would run it twice - pass it through raw.
|
// skipped here so the value is only ever evaluated once.
|
||||||
std::string computed_data = data;
|
std::string computed_data = data;
|
||||||
if (!data.empty() && cmd != "system/message") {
|
if (!data.empty() && !computesOwnValue(cmd)) {
|
||||||
computed_data = compute(data);
|
computed_data = compute(data);
|
||||||
if (computed_data.empty()) {
|
if (computed_data.empty()) {
|
||||||
EMSESP::logger().warning("Command '%s': cannot compute value '%s'", name, data.c_str());
|
EMSESP::logger().warning("Command '%s': cannot compute value '%s'. Literal text must be quoted", name, data.c_str());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -267,7 +275,7 @@ bool WebCommandService::executeCommand(const char * name, const std::string & co
|
|||||||
auto lower_url = Helpers::toLower(url.c_str());
|
auto lower_url = Helpers::toLower(url.c_str());
|
||||||
if (lower_url.starts_with("http://") || lower_url.starts_with("https://")) {
|
if (lower_url.starts_with("http://") || lower_url.starts_with("https://")) {
|
||||||
std::string result;
|
std::string result;
|
||||||
int httpResult = http_request(url, method, value, doc["header"].as<JsonObjectConst>(), result);
|
int httpResult = HttpClient::request(url, method, value, doc["header"].as<JsonObjectConst>(), result);
|
||||||
if (httpResult != 200) {
|
if (httpResult != 200) {
|
||||||
EMSESP::logger().warning("Command '%s': URL command failed with http code %d", name, httpResult);
|
EMSESP::logger().warning("Command '%s': URL command failed with http code %d", name, httpResult);
|
||||||
return false;
|
return false;
|
||||||
@@ -448,7 +456,7 @@ void WebCommandService::load_test_data() {
|
|||||||
Command::add(
|
Command::add(
|
||||||
EMSdevice::DeviceType::COMMAND,
|
EMSdevice::DeviceType::COMMAND,
|
||||||
item.name,
|
item.name,
|
||||||
[name = std::string(item.name)](const char * value, const int8_t id, JsonObject output) {
|
[name = std::string(item.name)](const char * value, const int8_t, JsonObject) {
|
||||||
return EMSESP::webCommandService.dispatchCommand(name.c_str(), value);
|
return EMSESP::webCommandService.dispatchCommand(name.c_str(), value);
|
||||||
},
|
},
|
||||||
FL_(command_cmd),
|
FL_(command_cmd),
|
||||||
|
|||||||
@@ -85,6 +85,7 @@ class WebCommandService : public StatefulService<WebCommands> {
|
|||||||
|
|
||||||
static bool isUrlCommand(const std::string & command); // true if the command definition is a HTTP/URL command
|
static bool isUrlCommand(const std::string & command); // true if the command definition is a HTTP/URL command
|
||||||
static bool valueContainsUrl(const std::string & value); // true if a value embeds a {"url":...} compute() will fetch
|
static bool valueContainsUrl(const std::string & value); // true if a value embeds a {"url":...} compute() will fetch
|
||||||
|
static bool computesOwnValue(const std::string & cmd); // true if the command runs the shunting-yard itself
|
||||||
|
|
||||||
const CommandItem * find(const char * name);
|
const CommandItem * find(const char * name);
|
||||||
|
|
||||||
|
|||||||
@@ -147,7 +147,7 @@ StateUpdateResult WebCustomEntity::update(JsonObject root, WebCustomEntity & web
|
|||||||
Command::add(
|
Command::add(
|
||||||
EMSdevice::DeviceType::CUSTOM,
|
EMSdevice::DeviceType::CUSTOM,
|
||||||
webCustomEntity.customEntityItems.back().name,
|
webCustomEntity.customEntityItems.back().name,
|
||||||
[name = std::string(webCustomEntity.customEntityItems.back().name)](const char * value, const int8_t id, JsonObject output) {
|
[name = std::string(webCustomEntity.customEntityItems.back().name)](const char * value, const int8_t id, JsonObject) {
|
||||||
return EMSESP::webCustomEntityService.command_setvalue(value, id, name.c_str());
|
return EMSESP::webCustomEntityService.command_setvalue(value, id, name.c_str());
|
||||||
},
|
},
|
||||||
FL_(entity_cmd),
|
FL_(entity_cmd),
|
||||||
@@ -501,7 +501,7 @@ void WebCustomEntityService::publish(const bool force) {
|
|||||||
snprintf(val_cond, sizeof(val_cond), "%s is defined", val_obj);
|
snprintf(val_cond, sizeof(val_cond), "%s is defined", val_obj);
|
||||||
// don't bother with value template conditions if using Domoticz which doesn't fully support MQTT Discovery
|
// don't bother with value template conditions if using Domoticz which doesn't fully support MQTT Discovery
|
||||||
if (Mqtt::discovery_type() == Mqtt::discoveryType::HOMEASSISTANT) {
|
if (Mqtt::discovery_type() == Mqtt::discoveryType::HOMEASSISTANT) {
|
||||||
config["val_tpl"] = (std::string) "{{" + val_obj + " if " + val_cond + "}}";
|
config["val_tpl"] = (std::string) "{{" + val_obj + " if " + val_cond + " else 'None'}}";
|
||||||
} else {
|
} else {
|
||||||
config["val_tpl"] = (std::string) "{{" + val_obj + "}}";
|
config["val_tpl"] = (std::string) "{{" + val_obj + "}}";
|
||||||
}
|
}
|
||||||
@@ -798,7 +798,7 @@ void WebCustomEntityService::load_test_data() {
|
|||||||
Command::add(
|
Command::add(
|
||||||
EMSdevice::DeviceType::CUSTOM,
|
EMSdevice::DeviceType::CUSTOM,
|
||||||
webCustomEntity.customEntityItems.back().name,
|
webCustomEntity.customEntityItems.back().name,
|
||||||
[name = std::string(webCustomEntity.customEntityItems.back().name)](const char * value, const int8_t id, JsonObject output) {
|
[name = std::string(webCustomEntity.customEntityItems.back().name)](const char * value, const int8_t id, JsonObject) {
|
||||||
return EMSESP::webCustomEntityService.command_setvalue(value, id, name.c_str());
|
return EMSESP::webCustomEntityService.command_setvalue(value, id, name.c_str());
|
||||||
},
|
},
|
||||||
FL_(entity_cmd),
|
FL_(entity_cmd),
|
||||||
@@ -834,7 +834,7 @@ void WebCustomEntityService::load_test_data() {
|
|||||||
Command::add(
|
Command::add(
|
||||||
EMSdevice::DeviceType::CUSTOM,
|
EMSdevice::DeviceType::CUSTOM,
|
||||||
webCustomEntity.customEntityItems.back().name,
|
webCustomEntity.customEntityItems.back().name,
|
||||||
[name = std::string(webCustomEntity.customEntityItems.back().name)](const char * value, const int8_t id, JsonObject output) {
|
[name = std::string(webCustomEntity.customEntityItems.back().name)](const char * value, const int8_t id, JsonObject) {
|
||||||
return EMSESP::webCustomEntityService.command_setvalue(value, id, name.c_str());
|
return EMSESP::webCustomEntityService.command_setvalue(value, id, name.c_str());
|
||||||
},
|
},
|
||||||
FL_(entity_cmd),
|
FL_(entity_cmd),
|
||||||
@@ -857,7 +857,7 @@ void WebCustomEntityService::load_test_data() {
|
|||||||
Command::add(
|
Command::add(
|
||||||
EMSdevice::DeviceType::CUSTOM,
|
EMSdevice::DeviceType::CUSTOM,
|
||||||
webCustomEntity.customEntityItems.back().name,
|
webCustomEntity.customEntityItems.back().name,
|
||||||
[name = std::string(webCustomEntity.customEntityItems.back().name)](const char * value, const int8_t id, JsonObject output) {
|
[name = std::string(webCustomEntity.customEntityItems.back().name)](const char * value, const int8_t id, JsonObject) {
|
||||||
return EMSESP::webCustomEntityService.command_setvalue(value, id, name.c_str());
|
return EMSESP::webCustomEntityService.command_setvalue(value, id, name.c_str());
|
||||||
},
|
},
|
||||||
FL_(entity_cmd),
|
FL_(entity_cmd),
|
||||||
|
|||||||
@@ -317,7 +317,7 @@ void WebCustomizationService::customization_entities(AsyncWebServerRequest * req
|
|||||||
|
|
||||||
// add deleted entities from file
|
// add deleted entities from file
|
||||||
read([&](WebCustomization & settings) {
|
read([&](WebCustomization & settings) {
|
||||||
for (EntityCustomization entityCustomization : settings.entityCustomizations) {
|
for (const auto & entityCustomization : settings.entityCustomizations) {
|
||||||
if (entityCustomization.device_id == device_id) {
|
if (entityCustomization.device_id == device_id) {
|
||||||
for (const auto & entity_id : entityCustomization.entity_ids) {
|
for (const auto & entity_id : entityCustomization.entity_ids) {
|
||||||
uint8_t mask = Helpers::hextoint(entity_id.substr(0, 2).c_str());
|
uint8_t mask = Helpers::hextoint(entity_id.substr(0, 2).c_str());
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ StateUpdateResult WebScheduler::update(JsonObject root, WebScheduler & webSchedu
|
|||||||
Command::add(
|
Command::add(
|
||||||
EMSdevice::DeviceType::SCHEDULER,
|
EMSdevice::DeviceType::SCHEDULER,
|
||||||
webScheduler.scheduleItems.back().name,
|
webScheduler.scheduleItems.back().name,
|
||||||
[name = std::string(webScheduler.scheduleItems.back().name)](const char * value, const int8_t id, JsonObject output) {
|
[name = std::string(webScheduler.scheduleItems.back().name)](const char * value, const int8_t id, JsonObject) {
|
||||||
return EMSESP::webSchedulerService.command_setvalue(value, id, name.c_str());
|
return EMSESP::webSchedulerService.command_setvalue(value, id, name.c_str());
|
||||||
},
|
},
|
||||||
FL_(schedule_cmd),
|
FL_(schedule_cmd),
|
||||||
@@ -275,7 +275,7 @@ void WebSchedulerService::publish(const bool force) {
|
|||||||
|
|
||||||
char val_tpl[150];
|
char val_tpl[150];
|
||||||
if (Mqtt::discovery_type() == Mqtt::discoveryType::HOMEASSISTANT) {
|
if (Mqtt::discovery_type() == Mqtt::discoveryType::HOMEASSISTANT) {
|
||||||
snprintf(val_tpl, sizeof(val_tpl), "{{%s if %s}}", val_obj, val_cond);
|
snprintf(val_tpl, sizeof(val_tpl), "{{%s if %s else 'None'}}", val_obj, val_cond);
|
||||||
} else {
|
} else {
|
||||||
snprintf(val_tpl, sizeof(val_tpl), "{{%s}}", val_obj); // omit value conditional Jinja2 template code
|
snprintf(val_tpl, sizeof(val_tpl), "{{%s}}", val_obj); // omit value conditional Jinja2 template code
|
||||||
}
|
}
|
||||||
@@ -464,7 +464,7 @@ void WebSchedulerService::load_test_data() {
|
|||||||
Command::add(
|
Command::add(
|
||||||
EMSdevice::DeviceType::SCHEDULER,
|
EMSdevice::DeviceType::SCHEDULER,
|
||||||
item.name,
|
item.name,
|
||||||
[name = std::string(item.name)](const char * value, const int8_t id, JsonObject output) {
|
[name = std::string(item.name)](const char * value, const int8_t id, JsonObject) {
|
||||||
return EMSESP::webSchedulerService.command_setvalue(value, id, name.c_str());
|
return EMSESP::webSchedulerService.command_setvalue(value, id, name.c_str());
|
||||||
},
|
},
|
||||||
FL_(schedule_cmd),
|
FL_(schedule_cmd),
|
||||||
|
|||||||
@@ -316,8 +316,9 @@ StateUpdateResult WebSettings::update(JsonObject root, WebSettings & settings) {
|
|||||||
settings.weblog_level = root["weblog_level"] | EMSESP_DEFAULT_WEBLOG_LEVEL;
|
settings.weblog_level = root["weblog_level"] | EMSESP_DEFAULT_WEBLOG_LEVEL;
|
||||||
settings.weblog_compact = root["weblog_compact"] | EMSESP_DEFAULT_WEBLOG_COMPACT;
|
settings.weblog_compact = root["weblog_compact"] | EMSESP_DEFAULT_WEBLOG_COMPACT;
|
||||||
|
|
||||||
settings.email_enabled = root["email_enabled"] | FACTORY_EMAIL_ENABLE;
|
settings.email_enabled = root["email_enabled"] | FACTORY_EMAIL_ENABLE;
|
||||||
settings.email_security = root["email_security"] | FACTORY_EMAIL_SECURITY;
|
// Off is 0, so `| FACTORY_EMAIL_SECURITY` would replace it with STARTTLS (2)
|
||||||
|
settings.email_security = root["email_security"].isNull() ? FACTORY_EMAIL_SECURITY : root["email_security"].as<uint8_t>();
|
||||||
settings.email_server = root["email_server"] | FACTORY_EMAIL_SERVER;
|
settings.email_server = root["email_server"] | FACTORY_EMAIL_SERVER;
|
||||||
settings.email_port = root["email_port"] | FACTORY_EMAIL_PORT;
|
settings.email_port = root["email_port"] | FACTORY_EMAIL_PORT;
|
||||||
settings.email_login = root["email_login"] | FACTORY_EMAIL_LOGIN;
|
settings.email_login = root["email_login"] | FACTORY_EMAIL_LOGIN;
|
||||||
|
|||||||
@@ -20,9 +20,8 @@
|
|||||||
|
|
||||||
#ifndef EMSESP_STANDALONE
|
#ifndef EMSESP_STANDALONE
|
||||||
#include <esp_ota_ops.h>
|
#include <esp_ota_ops.h>
|
||||||
#include <WiFiClient.h>
|
|
||||||
#include <ESP_SSLClient.h>
|
|
||||||
#endif
|
#endif
|
||||||
|
#include "httpClient.h"
|
||||||
|
|
||||||
namespace emsesp {
|
namespace emsesp {
|
||||||
|
|
||||||
@@ -320,12 +319,15 @@ uint8_t WebStatusService::upgradeImportantMessages(std::string & version) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// action = getVersions
|
// action = getVersions
|
||||||
// returns the device's current version for dev and stable
|
// returns the device's current version for dev and stable, plus the system name
|
||||||
// The remote fetch runs from the main loop task via WebStatusService::loop() so that we never block the AsyncTCP callback
|
// The remote fetch runs from the main loop task via WebStatusService::loop() so that we never block the AsyncTCP callback
|
||||||
void WebStatusService::getVersions(JsonObject root) {
|
void WebStatusService::getVersions(JsonObject root) {
|
||||||
FirmwareVersion current_version(current_version_s);
|
FirmwareVersion current_version(current_version_s);
|
||||||
bool is_dev = current_version.prerelease().find("dev") != std::string::npos;
|
bool is_dev = current_version.prerelease().find("dev") != std::string::npos;
|
||||||
|
|
||||||
|
// the WebUI shows this in the menu drawer. It's included here because this action is called once after login
|
||||||
|
root["system_name"] = EMSESP::system_.system_name();
|
||||||
|
|
||||||
JsonObject current = root["current"].to<JsonObject>();
|
JsonObject current = root["current"].to<JsonObject>();
|
||||||
current["version"] = current_version_s;
|
current["version"] = current_version_s;
|
||||||
current["type"] = is_dev ? "dev" : "stable";
|
current["type"] = is_dev ? "dev" : "stable";
|
||||||
@@ -421,94 +423,17 @@ bool WebStatusService::refresh_versions_cache() {
|
|||||||
#ifdef EMSESP_STANDALONE
|
#ifdef EMSESP_STANDALONE
|
||||||
return false;
|
return false;
|
||||||
#else
|
#else
|
||||||
// detect scheme from EMSESP_VERSIONS_URL (case-insensitive). One code path for HTTP and HTTPS,
|
std::string result;
|
||||||
// using ESP_SSLClient as a plain TCP passthrough when SSL is disabled.
|
JsonDocument doc;
|
||||||
String url = EMSESP_VERSIONS_URL;
|
auto http_code = HttpClient::request(VERSIONS_URL, "GET", "", doc.as<JsonObjectConst>(), result);
|
||||||
String lower = url;
|
|
||||||
lower.toLowerCase();
|
|
||||||
const bool is_https = lower.startsWith("https://");
|
|
||||||
if (!is_https && !lower.startsWith("http://")) {
|
|
||||||
#if defined(EMSESP_DEBUG)
|
|
||||||
EMSESP::logger().debug("versions.json: unsupported scheme");
|
|
||||||
#endif
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const int scheme_len = is_https ? 8 : 7;
|
|
||||||
|
|
||||||
WiFiClient basic_client;
|
|
||||||
ESP_SSLClient ssl_client;
|
|
||||||
if (is_https) {
|
|
||||||
ssl_client.setInsecure();
|
|
||||||
ssl_client.setBufferSizes(16384, 1024); // versions.json fits easily but TLS records may be full-size
|
|
||||||
ssl_client.setSessionTimeout(120);
|
|
||||||
}
|
|
||||||
basic_client.setTimeout(5000);
|
|
||||||
ssl_client.setTimeout(5000);
|
|
||||||
ssl_client.setClient(&basic_client, is_https);
|
|
||||||
|
|
||||||
// split into host and path
|
|
||||||
String rest = url.substring(scheme_len);
|
|
||||||
String host;
|
|
||||||
String path;
|
|
||||||
int s = rest.indexOf('/');
|
|
||||||
if (s < 0) {
|
|
||||||
host = rest;
|
|
||||||
path = "/";
|
|
||||||
} else {
|
|
||||||
host = rest.substring(0, s);
|
|
||||||
path = rest.substring(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!ssl_client.connect(host.c_str(), is_https ? 443 : 80)) {
|
|
||||||
#if defined(EMSESP_DEBUG)
|
|
||||||
EMSESP::logger().debug("versions.json: connection failed");
|
|
||||||
#endif
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// minimal HTTP/1.0 GET so we don't have to handle chunked encoding
|
|
||||||
ssl_client.print("GET ");
|
|
||||||
ssl_client.print(path);
|
|
||||||
ssl_client.println(" HTTP/1.0");
|
|
||||||
ssl_client.print("Host: ");
|
|
||||||
ssl_client.println(host);
|
|
||||||
ssl_client.println("User-Agent: EMS-ESP");
|
|
||||||
ssl_client.println("Connection: close");
|
|
||||||
ssl_client.print("\r\n");
|
|
||||||
|
|
||||||
// wait for the first byte. The 5 seconds is GitHub's SLO
|
|
||||||
uint32_t ms = millis();
|
|
||||||
while (ssl_client.connected() && !ssl_client.available() && millis() - ms < 5000) {
|
|
||||||
delay(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// parse status line
|
|
||||||
String status_line = ssl_client.readStringUntil('\n');
|
|
||||||
int sp = status_line.indexOf(' ');
|
|
||||||
int http_code = (sp >= 0) ? status_line.substring(sp + 1, sp + 4).toInt() : 0;
|
|
||||||
if (http_code != 200) {
|
if (http_code != 200) {
|
||||||
ssl_client.stop();
|
EMSESP::logger().warning("Unable to retrieve online version information (HTTP error code %d)", http_code);
|
||||||
#if defined(EMSESP_DEBUG)
|
|
||||||
EMSESP::logger().debug("versions.json: HTTP error code %d", http_code);
|
|
||||||
#endif
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
DeserializationError err = deserializeJson(doc, result);
|
||||||
// skip headers
|
|
||||||
while (ssl_client.connected() || ssl_client.available()) {
|
|
||||||
String line = ssl_client.readStringUntil('\n');
|
|
||||||
line.trim();
|
|
||||||
if (line.isEmpty()) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
JsonDocument doc(PSRAM_DOC);
|
|
||||||
DeserializationError err = deserializeJson(doc, ssl_client);
|
|
||||||
ssl_client.stop();
|
|
||||||
if (err) {
|
if (err) {
|
||||||
#if defined(EMSESP_DEBUG)
|
#if defined(EMSESP_DEBUG)
|
||||||
EMSESP::logger().debug("versions.json: parse error");
|
EMSESP::logger().debug("refresh_versions_cache() parse error");
|
||||||
#endif
|
#endif
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -531,7 +456,8 @@ bool WebStatusService::refresh_versions_cache() {
|
|||||||
|
|
||||||
versions_cache_valid_ = true;
|
versions_cache_valid_ = true;
|
||||||
#if defined(EMSESP_DEBUG)
|
#if defined(EMSESP_DEBUG)
|
||||||
EMSESP::logger().debug("versions.json: fetched stable=%s, dev=%s, current=%s",
|
EMSESP::logger().debug("Fetched from %s: stable=%s, dev=%s, current=%s",
|
||||||
|
VERSIONS_URL,
|
||||||
versions_stable_.version.c_str(),
|
versions_stable_.version.c_str(),
|
||||||
versions_dev_.version.c_str(),
|
versions_dev_.version.c_str(),
|
||||||
current_version_s.c_str());
|
current_version_s.c_str());
|
||||||
|
|||||||
@@ -4,8 +4,6 @@
|
|||||||
#define EMSESP_SYSTEM_STATUS_SERVICE_PATH "/rest/systemStatus"
|
#define EMSESP_SYSTEM_STATUS_SERVICE_PATH "/rest/systemStatus"
|
||||||
#define EMSESP_ACTION_SERVICE_PATH "/rest/action"
|
#define EMSESP_ACTION_SERVICE_PATH "/rest/action"
|
||||||
|
|
||||||
#define EMSESP_VERSIONS_URL "http://emsesp.org/versions.json"
|
|
||||||
|
|
||||||
#include "../core/firmwareVersion.h"
|
#include "../core/firmwareVersion.h"
|
||||||
#include "../emsesp_version.h"
|
#include "../emsesp_version.h"
|
||||||
|
|
||||||
@@ -72,6 +70,8 @@ class WebStatusService {
|
|||||||
|
|
||||||
bool refresh_versions_cache(); // does the actual HTTPS fetch + parse, returns true on success
|
bool refresh_versions_cache(); // does the actual HTTPS fetch + parse, returns true on success
|
||||||
|
|
||||||
|
static constexpr const char * VERSIONS_URL = "http://emsesp.org/versions.json";
|
||||||
|
|
||||||
static constexpr uint32_t VERSIONS_REFRESH_INTERVAL_MS = 60UL * 60UL * 1000UL; // 1 hour on success
|
static constexpr uint32_t VERSIONS_REFRESH_INTERVAL_MS = 60UL * 60UL * 1000UL; // 1 hour on success
|
||||||
static constexpr uint32_t VERSIONS_RETRY_INTERVAL_MS = 5UL * 60UL * 1000UL; // 5 min after failure
|
static constexpr uint32_t VERSIONS_RETRY_INTERVAL_MS = 5UL * 60UL * 1000UL; // 5 min after failure
|
||||||
static constexpr uint32_t VERSIONS_INITIAL_FETCH_DELAY_MS = 5UL * 1000UL; // 5 s after a link comes up
|
static constexpr uint32_t VERSIONS_INITIAL_FETCH_DELAY_MS = 5UL * 1000UL; // 5 s after a link comes up
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user