sync with core3 features

This commit is contained in:
proddy
2026-03-30 23:26:04 +02:00
parent 09473f17a0
commit fb09e10f19
57 changed files with 1467 additions and 709 deletions

View File

@@ -63,8 +63,9 @@ CXX_STANDARD := -std=gnu++17
#---------------------------------------------------------------------- #----------------------------------------------------------------------
# Defined Symbols # Defined Symbols
#---------------------------------------------------------------------- #----------------------------------------------------------------------
DEFINES += -DARDUINOJSON_ENABLE -DARDUINOJSON_ENABLE_ARDUINO_STRING -DARDUINOJSON_USE_DOUBLE=0 DEFINES += -DARDUINOJSON_ENABLE -DARDUINOJSON_ENABLE_ARDUINO_STRING -DARDUINOJSON_USE_DOUBLE=0
DEFINES += -DEMSESP_STANDALONE -DEMSESP_TEST -DEMSESP_DEBUG -DEMC_RX_BUFFER_SIZE=1500 DEFINES += -DEMSESP_STANDALONE -DEMSESP_TEST -DEMSESP_DEBUG -DEMC_RX_BUFFER_SIZE=1500
DEFINES += -DNO_TLS_SUPPORT
DEFINES += $(ARGS) DEFINES += $(ARGS)
DEFAULTS = -DEMSESP_DEFAULT_LOCALE=\"en\" -DEMSESP_DEFAULT_BOARD_PROFILE=\"S32S3\" DEFAULTS = -DEMSESP_DEFAULT_LOCALE=\"en\" -DEMSESP_DEFAULT_BOARD_PROFILE=\"S32S3\"
@@ -79,6 +80,10 @@ SYMBOLS := $(CURDIR)/$(BUILD)/$(TARGET).out
CSOURCES := $(shell find $(SOURCES) -name "*.c" 2>/dev/null) CSOURCES := $(shell find $(SOURCES) -name "*.c" 2>/dev/null)
CXXSOURCES := $(shell find $(SOURCES) -name "*.cpp" 2>/dev/null) CXXSOURCES := $(shell find $(SOURCES) -name "*.cpp" 2>/dev/null)
# Exclude files not needed for standalone build, if they exist
CSOURCES := $(filter-out src/core/ModuleLibrary.c,$(CSOURCES))
CXXSOURCES := $(filter-out src/core/ModuleLibrary.cpp,$(CXXSOURCES))
OBJS := $(patsubst %,$(BUILD)/%.o,$(basename $(CSOURCES)) $(basename $(CXXSOURCES))) OBJS := $(patsubst %,$(BUILD)/%.o,$(basename $(CSOURCES)) $(basename $(CXXSOURCES)))
DEPS := $(patsubst %,$(BUILD)/%.d,$(basename $(CSOURCES)) $(basename $(CXXSOURCES))) DEPS := $(patsubst %,$(BUILD)/%.d,$(basename $(CSOURCES)) $(basename $(CXXSOURCES)))

View File

@@ -1,85 +1,228 @@
{ {
"type": "settings", "type": "systembackup",
"Network": { "version": "3.8.2",
"ssid": "my_wifi_ssid", "date": "2026-03-29T13:28:15",
"bssid": "", "systembackup": [
"password": "my_wifi_password", {
"hostname": "ems-esp" "type": "settings",
"Network": {
"ssid": "",
"bssid": "",
"password": "",
"hostname": "ems-esp",
"static_ip_config": false,
"bandwidth20": false,
"nosleep": true,
"enableMDNS": true,
"enableCORS": false,
"CORSOrigin": "*",
"tx_power": 0
},
"AP": {
"provision_mode": 2,
"ssid": "ems-esp",
"password": "ems-esp-neo",
"channel": 1,
"ssid_hidden": false,
"max_clients": 4,
"local_ip": "192.168.4.1",
"gateway_ip": "192.168.4.1",
"subnet_mask": "255.255.255.0"
},
"MQTT": {
"enableTLS": false,
"rootCA": "",
"enabled": false,
"host": "",
"port": 1883,
"base": "ems-esp",
"username": "",
"password": "",
"client_id": "esp32-b8ffc9ec",
"keep_alive": 60,
"clean_session": false,
"entity_format": 1,
"publish_time_boiler": 10,
"publish_time_thermostat": 10,
"publish_time_solar": 10,
"publish_time_mixer": 10,
"publish_time_water": 10,
"publish_time_other": 60,
"publish_time_sensor": 10,
"publish_time_heartbeat": 60,
"mqtt_qos": 0,
"mqtt_retain": false,
"ha_enabled": false,
"nested_format": 1,
"discovery_prefix": "homeassistant",
"discovery_type": 0,
"ha_number_mode": 0,
"publish_single": false,
"publish_single2cmd": false,
"send_response": false
},
"NTP": {
"enabled": true,
"server": "time.google.com",
"tz_label": "Europe/Amsterdam",
"tz_format": "CET-1CEST,M3.5.0,M10.5.0/3"
},
"Security": {
"jwt_secret": "ems-esp-neo",
"users": [
{
"username": "admin",
"password": "admin",
"admin": true
},
{
"username": "guest",
"password": "guest",
"admin": false
}
]
},
"Settings": {
"version": "3.8.2",
"board_profile": "E32V2_2",
"platform": "ESP32",
"locale": "en",
"tx_mode": 1,
"ems_bus_id": 11,
"syslog_enabled": false,
"syslog_level": 3,
"trace_raw": false,
"syslog_mark_interval": 0,
"syslog_host": "",
"syslog_port": 514,
"boiler_heatingoff": false,
"remote_timeout": 24,
"remote_timeout_en": false,
"shower_timer": false,
"shower_alert": false,
"shower_alert_coldshot": 10,
"shower_alert_trigger": 7,
"shower_min_duration": 180,
"rx_gpio": 4,
"tx_gpio": 5,
"dallas_gpio": 14,
"dallas_parasite": false,
"led_gpio": 32,
"hide_led": false,
"led_type": 1,
"low_clock": false,
"telnet_enabled": true,
"notoken_api": false,
"readonly_mode": false,
"analog_enabled": true,
"pbutton_gpio": 34,
"solar_maxflow": 30,
"fahrenheit": false,
"bool_format": 1,
"bool_dashboard": 1,
"enum_format": 1,
"weblog_level": 6,
"weblog_buffer": 50,
"weblog_compact": true,
"phy_type": 1,
"eth_power": 15,
"eth_phy_addr": 0,
"eth_clock_mode": 1,
"modbus_enabled": false,
"modbus_port": 502,
"modbus_max_clients": 10,
"modbus_timeout": 300,
"developer_mode": true,
"email_enabled": false,
"email_ssl": false,
"email_starttls": true,
"email_server": "smtp.example.net",
"email_port": 587,
"email_login": "",
"email_pass": "",
"email_sender": "ems-esp@example.net",
"email_recp": "",
"email_subject": "ems-esp notification"
}
}, },
"AP": { {
"provision_mode": 2, "type": "schedule",
"ssid": "ems-esp", "Schedule": {
"password": "ems-esp-neo", "schedule": []
"channel": 1, }
"ssid_hidden": false,
"max_clients": 4,
"local_ip": "192.168.4.1",
"gateway_ip": "192.168.4.1",
"subnet_mask": "255.255.255.0"
}, },
"MQTT": { {
"enableTLS": false, "type": "customizations",
"rootCA": "", "Customizations": {
"enabled": false, "ts": [
"host": "127.0.0.1", {
"port": 1883, "id": "28_1767_7B13_2502",
"base": "ems-esp", "name": "gateway_temperature",
"username": "username", "offset": 0,
"password": "password", "is_system": true
"client_id": "ems-esp", }
"entity_format": 1, ],
"publish_time_boiler": 10, "as": [
"publish_time_thermostat": 10, {
"publish_time_solar": 10, "gpio": 39,
"publish_time_mixer": 10, "name": "core_voltage",
"publish_time_water": 10, "offset": 0,
"publish_time_other": 60, "factor": 0.003771,
"publish_time_sensor": 10, "uom": 23,
"publish_time_heartbeat": 60, "type": 3,
"mqtt_qos": 0, "is_system": true
"mqtt_retain": false, },
"ha_enabled": false, {
"nested_format": 1, "gpio": 36,
"discovery_prefix": "homeassistant", "name": "supply_voltage",
"discovery_type": 0, "offset": 0,
"publish_single": false, "factor": 0.017,
"publish_single2cmd": false, "uom": 23,
"send_response": false "type": 3,
"is_system": true
},
{
"gpio": 2,
"name": "led",
"offset": 0,
"factor": 1,
"uom": 0,
"type": 6,
"is_system": true
}
],
"masked_entities": []
}
}, },
"NTP": { {
"enabled": true, "type": "entities",
"server": "time.google.com", "Entities": {
"tz_label": "Europe/Amsterdam", "entities": []
"tz_format": "CET-1CEST,M3.5.0,M10.5.0/3" }
}, },
"Security": { {
"jwt_secret": "ems-esp-neo", "type": "modules",
"users": [ "Modules": {
{ "modules": []
"username": "admin", }
"password": "admin",
"admin": true
},
{
"username": "guest",
"password": "guest",
"admin": false
}
]
}, },
"Settings": { {
"board_profile": "S3", "type": "customSupport",
"locale": "en", "Support": {
"tx_mode": 1, "html": [
"ems_bus_id": 11, "This product is installed and managed by:",
"boiler_heatingoff": false, "",
"hide_led": true, "<b>Bosch Installer Example</b>",
"telnet_enabled": true, "",
"notoken_api": false, "Nefit Road 12",
"analog_enabled": true, "1234 AB Amsterdam",
"fahrenheit": false, "Phone: +31 123 456 789",
"bool_format": 1, "email: support@boschinstaller.nl",
"bool_dashboard": 1, "",
"enum_format": 1 "For help and questions please <a target='_blank' href='https://emsesp.org'>contact</a> your installer."
],
"img_url": "https://emsesp.org/media/images/designer.png"
}
} }
} ]
}

View File

@@ -41,31 +41,31 @@
"react": "^19.2.4", "react": "^19.2.4",
"react-dom": "^19.2.4", "react-dom": "^19.2.4",
"react-icons": "^5.6.0", "react-icons": "^5.6.0",
"react-router": "^7.13.1", "react-router": "^7.13.2",
"react-toastify": "^11.0.5", "react-toastify": "^11.0.5",
"typesafe-i18n": "^5.27.1", "typesafe-i18n": "^5.27.1",
"typescript": "^5.9.3" "typescript": "^6.0.2"
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "^7.29.0", "@babel/core": "^7.29.0",
"@eslint/js": "^10.0.1", "@eslint/js": "^10.0.1",
"@preact/compat": "^18.3.2", "@preact/compat": "^18.3.2",
"@preact/preset-vite": "^2.10.4", "@preact/preset-vite": "^2.10.5",
"@trivago/prettier-plugin-sort-imports": "^6.0.2", "@trivago/prettier-plugin-sort-imports": "^6.0.2",
"@types/node": "^25.5.0", "@types/node": "^25.5.0",
"@types/react": "^19.2.14", "@types/react": "^19.2.14",
"@types/react-dom": "^19.2.3", "@types/react-dom": "^19.2.3",
"axe-core": "^4.11.1", "axe-core": "^4.11.1",
"concurrently": "^9.2.1", "concurrently": "^9.2.1",
"eslint": "^10.0.3", "eslint": "^10.1.0",
"eslint-config-prettier": "^10.1.8", "eslint-config-prettier": "^10.1.8",
"prettier": "^3.8.1", "prettier": "^3.8.1",
"rollup-plugin-visualizer": "^7.0.1", "rollup-plugin-visualizer": "^7.0.1",
"terser": "^5.46.1", "terser": "^5.46.1",
"typescript-eslint": "^8.57.1", "typescript-eslint": "^8.58.0",
"vite": "^7.3.1", "vite": "^8.0.3",
"vite-plugin-imagemin": "^0.6.1", "vite-plugin-imagemin": "^0.6.1",
"vite-tsconfig-paths": "^6.1.1" "vite-tsconfig-paths": "^6.1.1"
}, },
"packageManager": "pnpm@10.32.1" "packageManager": "pnpm@10.33.0+sha512.10568bb4a6afb58c9eb3630da90cc9516417abebd3fabbe6739f0ae795728da1491e9db5a544c76ad8eb7570f5c4bb3d6c637b2cb41bfdcdb47fa823c8649319"
} }

589
interface/pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -262,10 +262,6 @@ const Dashboard = memo(() => {
return ( return (
<> <>
{!data.connected && (
<MessageBox level="error" message={LL.EMS_BUS_WARNING()} />
)}
{data.connected && data.nodes.length > 0 && !hasFavEntities && ( {data.connected && data.nodes.length > 0 && !hasFavEntities && (
<MessageBox mb={2} level="warning"> <MessageBox mb={2} level="warning">
<Typography> <Typography>

View File

@@ -8,7 +8,7 @@ import {
useState useState
} from 'react'; } from 'react';
import { IconContext } from 'react-icons'; import { IconContext } from 'react-icons';
import { useNavigate } from 'react-router'; import { Link, useNavigate } from 'react-router';
import { toast } from 'react-toastify'; import { toast } from 'react-toastify';
import CommentsDisabledOutlinedIcon from '@mui/icons-material/CommentsDisabledOutlined'; import CommentsDisabledOutlinedIcon from '@mui/icons-material/CommentsDisabledOutlined';
@@ -534,7 +534,17 @@ const Devices = memo(() => {
const renderCoreData = () => ( const renderCoreData = () => (
<> <>
{!coreData.connected ? ( {!coreData.connected ? (
<MessageBox level="error" message={LL.EMS_BUS_WARNING()} /> <MessageBox level="error" message={LL.EMS_BUS_WARNING() + '.'}>
&nbsp;(
<Link
target="_blank"
to="https://docs.emsesp.org/Troubleshooting#ems-bus-is-not-connecting"
style={{ color: 'white' }}
>
{LL.ONLINE_HELP()}
</Link>
)
</MessageBox>
) : ( ) : (
<Box justifyContent="center" flexDirection="column"> <Box justifyContent="center" flexDirection="column">
<IconContext.Provider <IconContext.Provider

View File

@@ -20,7 +20,6 @@ import { useI18nContext } from 'i18n/i18n-react';
import { saveFile } from 'utils'; import { saveFile } from 'utils';
interface DownloadButton { interface DownloadButton {
key: string;
type: string; type: string;
label: string | number; label: string | number;
isGridButton: boolean; isGridButton: boolean;
@@ -66,31 +65,31 @@ const DownloadUpload = () => {
const downloadButtons: DownloadButton[] = useMemo( const downloadButtons: DownloadButton[] = useMemo(
() => [ () => [
{ {
key: 'settings',
type: 'settings', type: 'settings',
label: LL.SETTINGS_OF(LL.APPLICATION()), label: LL.SETTINGS_OF(LL.APPLICATION()),
isGridButton: true isGridButton: true
}, },
{ {
key: 'customizations',
type: 'customizations', type: 'customizations',
label: LL.CUSTOMIZATIONS(), label: LL.CUSTOMIZATIONS(),
isGridButton: true isGridButton: true
}, },
{ {
key: 'entities',
type: 'entities', type: 'entities',
label: LL.CUSTOM_ENTITIES(0), label: LL.CUSTOM_ENTITIES(0),
isGridButton: true isGridButton: true
}, },
{ {
key: 'schedule',
type: 'schedule', type: 'schedule',
label: LL.SCHEDULE(0), label: LL.SCHEDULE(0),
isGridButton: true isGridButton: true
}, },
{ {
key: 'allvalues', type: 'systembackup',
label: LL.DOWNLOAD_SYSTEM_BACKUP(),
isGridButton: true
},
{
type: 'allvalues', type: 'allvalues',
label: LL.ALLVALUES(), label: LL.ALLVALUES(),
isGridButton: false isGridButton: false
@@ -133,7 +132,7 @@ const DownloadUpload = () => {
<Grid container spacing={2}> <Grid container spacing={2}>
{gridButtons.map((button) => ( {gridButtons.map((button) => (
<Grid key={button.key}> <Grid key={button.type}>
<Button <Button
startIcon={<DownloadIcon />} startIcon={<DownloadIcon />}
variant="outlined" variant="outlined"

View File

@@ -60,18 +60,16 @@ const SystemMonitor = () => {
const { statusMessage, isUploading, progressValue } = useMemo(() => { const { statusMessage, isUploading, progressValue } = useMemo(() => {
const status = data?.status; const status = data?.status;
let message = ''; const message =
if (status && status >= SystemStatusCodes.SYSTEM_STATUS_UPLOADING) { status && status >= SystemStatusCodes.SYSTEM_STATUS_UPLOADING
message = LL.WAIT_FIRMWARE(); ? LL.WAIT_FIRMWARE()
} else if (status === SystemStatusCodes.SYSTEM_STATUS_PENDING_RESTART) { : status === SystemStatusCodes.SYSTEM_STATUS_PENDING_RESTART
message = LL.APPLICATION_RESTARTING(); ? LL.APPLICATION_RESTARTING()
} else if (status === SystemStatusCodes.SYSTEM_STATUS_NORMAL) { : status === SystemStatusCodes.SYSTEM_STATUS_NORMAL
message = LL.RESTARTING_PRE(); ? LL.RESTARTING_PRE()
} else if (status === SystemStatusCodes.SYSTEM_STATUS_ERROR_UPLOAD) { : status === SystemStatusCodes.SYSTEM_STATUS_ERROR_UPLOAD
message = 'Upload Failed'; ? 'Upload Failed'
} else { : LL.RESTARTING_POST();
message = LL.RESTARTING_POST();
}
const uploading = const uploading =
status !== undefined && status >= SystemStatusCodes.SYSTEM_STATUS_UPLOADING; status !== undefined && status >= SystemStatusCodes.SYSTEM_STATUS_UPLOADING;

View File

@@ -186,7 +186,8 @@ const cz: Translation = {
BUFFER_SIZE: 'Maximální velikost vyrovnávací paměti', BUFFER_SIZE: 'Maximální velikost vyrovnávací paměti',
COMPACT: 'Kompaktní', COMPACT: 'Kompaktní',
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šechna data', DOWNLOAD_SETTINGS_TEXT2: 'Exportovat všechny hodnoty',
DOWNLOAD_SYSTEM_BACKUP: 'Systémová záloha',
UPLOAD_TEXT: 'Nahrajte nový soubor firmwaru (.bin) nebo záložní soubor (.json)', 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', 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',
@@ -357,7 +358,8 @@ const cz: Translation = {
FIRMWARE_VERSION_INFO: 'Informace o verzi firmwaru', FIRMWARE_VERSION_INFO: 'Informace o verzi firmwaru',
NO_DATA: 'Žádná data', NO_DATA: 'Žádná data',
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'
}; };
export default cz; export default cz;

View File

@@ -186,7 +186,8 @@ const de: Translation = {
BUFFER_SIZE: 'Max. Puffergröße', BUFFER_SIZE: 'Max. Puffergröße',
COMPACT: 'Kompakte Darstellung', COMPACT: 'Kompakte Darstellung',
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 Daten', DOWNLOAD_SETTINGS_TEXT2: 'Exportiere alle Werte',
DOWNLOAD_SYSTEM_BACKUP: 'System Sicherung',
UPLOAD_TEXT: 'Laden Sie eine neue Firmware-Datei (.bin) oder eine Sicherungsdatei (.json) hoch', 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', 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.',
@@ -357,7 +358,8 @@ const de: Translation = {
FIRMWARE_VERSION_INFO: 'Firmware-Versionsinformation', FIRMWARE_VERSION_INFO: 'Firmware-Versionsinformation',
NO_DATA: 'Keine Daten', NO_DATA: 'Keine Daten',
USER_PROFILE: 'Benutzerprofil', USER_PROFILE: 'Benutzerprofil',
STORED_VERSIONS: 'Gespeicherte Versionen' STORED_VERSIONS: 'Gespeicherte Versionen',
ONLINE_HELP: 'Online-Hilfe'
}; };
export default de; export default de;

View File

@@ -186,7 +186,8 @@ const en: Translation = {
BUFFER_SIZE: 'Max Buffer Size', BUFFER_SIZE: 'Max Buffer Size',
COMPACT: 'Compact', COMPACT: 'Compact',
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 data', DOWNLOAD_SETTINGS_TEXT2: 'Export all values',
DOWNLOAD_SYSTEM_BACKUP: 'System Backup',
UPLOAD_TEXT: 'Upload a new firmware file (.bin) or a backup file (.json)', UPLOAD_TEXT: 'Upload a new firmware file (.bin) or a backup file (.json)',
UPLOAD_DROP_TEXT: 'Drop a firmware .bin file or click here', UPLOAD_DROP_TEXT: 'Drop a firmware .bin file or click here',
ERROR: 'Unexpected Error, please try again', ERROR: 'Unexpected Error, please try again',
@@ -357,7 +358,8 @@ const en: Translation = {
FIRMWARE_VERSION_INFO: 'Firmware Version Information', FIRMWARE_VERSION_INFO: 'Firmware Version Information',
NO_DATA: 'No data', NO_DATA: 'No data',
USER_PROFILE: 'User Profile', USER_PROFILE: 'User Profile',
STORED_VERSIONS: 'Stored Versions' STORED_VERSIONS: 'Stored Versions',
ONLINE_HELP: 'online help'
}; };
export default en; export default en;

View File

@@ -186,7 +186,8 @@ const fr: Translation = {
BUFFER_SIZE: 'Max taille du buffer', BUFFER_SIZE: 'Max taille du buffer',
COMPACT: 'Compact', COMPACT: 'Compact',
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 données', DOWNLOAD_SETTINGS_TEXT2: 'Exporter toutes les valeurs',
DOWNLOAD_SYSTEM_BACKUP: 'Sauvegarde système',
UPLOAD_TEXT: 'Télécharger un nouveau fichier firmware (.bin) ou une sauvegarde (.json)', 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', UPLOAD_DROP_TEXT: 'Drop a firmware .bin file or click here',
ERROR: 'Erreur inattendue, veuillez réessayer', ERROR: 'Erreur inattendue, veuillez réessayer',
@@ -357,7 +358,8 @@ const fr: Translation = {
FIRMWARE_VERSION_INFO: 'Informations sur la version du firmware', FIRMWARE_VERSION_INFO: 'Informations sur la version du firmware',
NO_DATA: 'Aucune donnée', 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'
}; };
export default fr; export default fr;

View File

@@ -186,7 +186,8 @@ const it: Translation = {
BUFFER_SIZE: 'Max Buffer Size', BUFFER_SIZE: 'Max Buffer Size',
COMPACT: 'Compatto', COMPACT: 'Compatto',
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 dati', DOWNLOAD_SETTINGS_TEXT2: 'Esporta tutti i valori',
DOWNLOAD_SYSTEM_BACKUP: 'Backup sistema',
UPLOAD_TEXT: 'Upload a new firmware file (.bin) or a backup file (.json)', UPLOAD_TEXT: 'Upload a new firmware file (.bin) or a backup file (.json)',
UPLOAD_DROP_TEXT: 'Drop a firmware .bin file or click here', UPLOAD_DROP_TEXT: 'Drop a firmware .bin file or click here',
ERROR: 'Errore Inaspettato, prego tenta ancora', ERROR: 'Errore Inaspettato, prego tenta ancora',
@@ -357,7 +358,8 @@ const it: Translation = {
FIRMWARE_VERSION_INFO: 'Informazioni sulla versione del firmware', FIRMWARE_VERSION_INFO: 'Informazioni sulla versione del firmware',
NO_DATA: 'Nessun dato', NO_DATA: 'Nessun dato',
USER_PROFILE: 'Profilo utente', USER_PROFILE: 'Profilo utente',
STORED_VERSIONS: 'Versioni memorizzate' STORED_VERSIONS: 'Versioni memorizzate',
ONLINE_HELP: 'aiuto online'
}; };
export default it; export default it;

View File

@@ -186,7 +186,8 @@ const nl: Translation = {
BUFFER_SIZE: 'Max buffer grootte', BUFFER_SIZE: 'Max buffer grootte',
COMPACT: 'Compact', COMPACT: 'Compact',
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 data', DOWNLOAD_SETTINGS_TEXT2: 'Exporteer alle waarden',
DOWNLOAD_SYSTEM_BACKUP: 'Systeem Backup',
UPLOAD_TEXT: 'Upload een nieuw firmwarebestand (.bin) of een back-upbestand (.json)', UPLOAD_TEXT: 'Upload een nieuw firmwarebestand (.bin) of een back-upbestand (.json)',
UPLOAD_DROP_TEXT: 'Sleep en firmware .bin bestand hierheen of klik hier', UPLOAD_DROP_TEXT: 'Sleep en firmware .bin bestand hierheen of klik hier',
ERROR: 'Onverwachte fout, probeer opnieuw', ERROR: 'Onverwachte fout, probeer opnieuw',
@@ -357,7 +358,8 @@ const nl: Translation = {
FIRMWARE_VERSION_INFO: 'Informatie over firmwareversie', FIRMWARE_VERSION_INFO: 'Informatie over firmwareversie',
NO_DATA: 'Geen data', NO_DATA: 'Geen data',
USER_PROFILE: 'Gebruikersprofiel', USER_PROFILE: 'Gebruikersprofiel',
STORED_VERSIONS: 'Opgeslagen versies' STORED_VERSIONS: 'Opgeslagen versies',
ONLINE_HELP: 'online help'
}; };
export default nl; export default nl;

View File

@@ -186,7 +186,8 @@ const no: Translation = {
BUFFER_SIZE: 'Max Buffer Størrelse', BUFFER_SIZE: 'Max Buffer Størrelse',
COMPACT: 'Komprimere', COMPACT: 'Komprimere',
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 data', DOWNLOAD_SETTINGS_TEXT2: 'Eksporter alle verdier',
DOWNLOAD_SYSTEM_BACKUP: 'System Sikkerhetskopi',
UPLOAD_TEXT: 'Last opp en ny firmware fil (.bin) eller en sikkerhetskopi fil (.json)', 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', UPLOAD_DROP_TEXT: 'Dropp en firmware fil (.bin) eller klikk her',
ERROR: 'Ukjent feil, prøv igjen', ERROR: 'Ukjent feil, prøv igjen',
@@ -357,7 +358,8 @@ const no: Translation = {
FIRMWARE_VERSION_INFO: 'Informasjon om firmwareversjon', FIRMWARE_VERSION_INFO: 'Informasjon om firmwareversjon',
NO_DATA: 'Ingen data', NO_DATA: 'Ingen data',
USER_PROFILE: 'Brukerprofil', USER_PROFILE: 'Brukerprofil',
STORED_VERSIONS: 'Lagret versjoner' STORED_VERSIONS: 'Lagret versjoner',
ONLINE_HELP: 'online hjelp'
}; };
export default no; export default no;

View File

@@ -186,7 +186,8 @@ const pl: BaseTranslation = {
BUFFER_SIZE: 'Maksymalna pojemność bufora (ilość wpisów)', BUFFER_SIZE: 'Maksymalna pojemność bufora (ilość wpisów)',
COMPACT: 'Kompaktowy', COMPACT: 'Kompaktowy',
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 dane', DOWNLOAD_SETTINGS_TEXT2: 'Eksportuj wszystkie wartości',
DOWNLOAD_SYSTEM_BACKUP: 'Kopia zapasowa systemu',
UPLOAD_TEXT: 'Wgraj nowy plik firmware (.bin) lub kopię ustawień (.json)', UPLOAD_TEXT: 'Wgraj nowy plik firmware (.bin) lub kopię ustawień (.json)',
UPLOAD_DROP_TEXT: 'Upuść plik firmware .bin lub kliknij tutaj', UPLOAD_DROP_TEXT: 'Upuść plik firmware .bin lub kliknij tutaj',
ERROR: 'Nieoczekiwany błąd, spróbuj ponownie!', ERROR: 'Nieoczekiwany błąd, spróbuj ponownie!',
@@ -357,7 +358,8 @@ const pl: BaseTranslation = {
FIRMWARE_VERSION_INFO: 'Informacje o wersji firmware', FIRMWARE_VERSION_INFO: 'Informacje o wersji firmware',
NO_DATA: 'Brak danych', 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'
}; };
export default pl; export default pl;

View File

@@ -186,7 +186,8 @@ const sk: Translation = {
BUFFER_SIZE: 'Buffer-max. veľkosť', BUFFER_SIZE: 'Buffer-max. veľkosť',
COMPACT: 'Kompaktné', COMPACT: 'Kompaktné',
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 dáta', DOWNLOAD_SETTINGS_TEXT2: 'Exportovať všetky hodnoty',
DOWNLOAD_SYSTEM_BACKUP: 'Systémová záloha',
UPLOAD_TEXT: 'Nahrajte nový súbor firmvéru (.bin) alebo súbor zálohy (.json)', 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', 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',
@@ -357,7 +358,8 @@ const sk: Translation = {
FIRMWARE_VERSION_INFO: 'Informácie o verzii firmware', FIRMWARE_VERSION_INFO: 'Informácie o verzii firmware',
NO_DATA: 'Žiadne dáta', 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'
}; };
export default sk; export default sk;

View File

@@ -186,7 +186,8 @@ const sv: Translation = {
BUFFER_SIZE: 'Max bufferstorlek', BUFFER_SIZE: 'Max bufferstorlek',
COMPACT: 'Komprimerad', COMPACT: 'Komprimerad',
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 data', DOWNLOAD_SETTINGS_TEXT2: 'Exportera alla värden',
DOWNLOAD_SYSTEM_BACKUP: 'System säkerhetskopia',
UPLOAD_TEXT: 'Ladda upp en ny firmwarefil (.bin) eller en säkerhetskopiafil (.json)', 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', 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',
@@ -357,7 +358,8 @@ const sv: Translation = {
FIRMWARE_VERSION_INFO: 'Information om firmwareversion', FIRMWARE_VERSION_INFO: 'Information om firmwareversion',
NO_DATA: 'Ingen data', 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'
}; };
export default sv; export default sv;

View File

@@ -186,7 +186,8 @@ const tr: Translation = {
BUFFER_SIZE: 'En fazla bellek boyutu', BUFFER_SIZE: 'En fazla bellek boyutu',
COMPACT: 'Sıkışık', COMPACT: 'Sıkışık',
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 verileri dışarı al', DOWNLOAD_SETTINGS_TEXT2: 'Tüm değerleri dışarı al',
DOWNLOAD_SYSTEM_BACKUP: 'Sistem yedekleme',
UPLOAD_TEXT: 'Yeni bir firmware dosyası (.bin) veya yedek dosyası (.json) yükle', 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', 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.',
@@ -357,7 +358,8 @@ const tr: Translation = {
FIRMWARE_VERSION_INFO: 'Firmware Sürüm Bilgisi', FIRMWARE_VERSION_INFO: 'Firmware Sürüm Bilgisi',
NO_DATA: 'Hiçbir veri yok', 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'
}; };
export default tr; export default tr;

View File

@@ -23,6 +23,8 @@ export const saveFile = (
}, 100); }, 100);
} catch (error) { } catch (error) {
console.error('Failed to save file:', error); console.error('Failed to save file:', error);
throw new Error(`Unable to save file: ${filename}${extension}`); throw new Error(`Unable to save file: ${filename}${extension}`, {
cause: error
});
} }
}; };

View File

@@ -1,6 +1,6 @@
import { useEffect, useRef } from 'react'; import { useEffect, useRef } from 'react';
const DEFAULT_DELAY = 3000; const DEFAULT_DELAY = 5000;
/** /**
* Custom hook for setting up an interval with proper cleanup * Custom hook for setting up an interval with proper cleanup

View File

@@ -2,9 +2,8 @@ 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 { Plugin, defineConfig } from 'vite'; import { Plugin, PluginOption, defineConfig } from 'vite';
import viteImagemin from 'vite-plugin-imagemin'; import viteImagemin from 'vite-plugin-imagemin';
import viteTsconfigPaths from 'vite-tsconfig-paths';
import zlib from 'zlib'; import zlib from 'zlib';
// @ts-expect-error - mock server doesn't have type declarations // @ts-expect-error - mock server doesn't have type declarations
@@ -99,16 +98,31 @@ const createPreactPlugin = (devToolsEnabled: boolean) =>
prefreshEnabled: false prefreshEnabled: false
}); });
// Patch preact/compat to export stub React 19 APIs (use, useOptimistic) so that
// react-router v7 doesn't trigger IMPORT_IS_UNDEFINED warnings from Rolldown.
const preactCompatPatchPlugin = (): Plugin => ({
name: 'preact-compat-react19-patch',
transform(code, id) {
if (id.includes('preact') && id.includes('compat.module.js')) {
return {
code:
code +
'\nexport var use = undefined;\nexport var useOptimistic = undefined;\n',
map: null
};
}
return undefined;
}
});
// Common base plugins // Common base plugins
const createBasePlugins = ( const createBasePlugins = (
devToolsEnabled: boolean, devToolsEnabled: boolean,
includeBundleReporter = true includeBundleReporter = true
) => { ): PluginOption[] => {
const plugins = [ const plugins: PluginOption[] = [
createPreactPlugin(devToolsEnabled), createPreactPlugin(devToolsEnabled),
viteTsconfigPaths({ preactCompatPatchPlugin()
projects: ['./tsconfig.json']
})
]; ];
if (includeBundleReporter) { if (includeBundleReporter) {
plugins.push(bundleSizeReporter()); plugins.push(bundleSizeReporter());
@@ -234,7 +248,8 @@ export default defineConfig(
plugins: [...createBasePlugins(true, true), mockServer()], plugins: [...createBasePlugins(true, true), mockServer()],
resolve: { resolve: {
alias: RESOLVE_ALIASES, alias: RESOLVE_ALIASES,
extensions: RESOLVE_EXTENSIONS extensions: RESOLVE_EXTENSIONS,
tsconfigPaths: true
}, },
server: { server: {
open: true, open: true,
@@ -263,7 +278,8 @@ export default defineConfig(
plugins: createBasePlugins(false, true), plugins: createBasePlugins(false, true),
resolve: { resolve: {
alias: RESOLVE_ALIASES, alias: RESOLVE_ALIASES,
extensions: RESOLVE_EXTENSIONS extensions: RESOLVE_EXTENSIONS,
tsconfigPaths: true
}, },
build: { build: {
...createBaseBuildConfig(), ...createBaseBuildConfig(),
@@ -297,7 +313,8 @@ export default defineConfig(
], ],
resolve: { resolve: {
alias: RESOLVE_ALIASES, alias: RESOLVE_ALIASES,
extensions: RESOLVE_EXTENSIONS extensions: RESOLVE_EXTENSIONS,
tsconfigPaths: true
}, },
build: { build: {
...createBaseBuildConfig(), ...createBaseBuildConfig(),
@@ -306,8 +323,7 @@ export default defineConfig(
rollupOptions: { rollupOptions: {
treeshake: { treeshake: {
moduleSideEffects: false, moduleSideEffects: false,
propertyReadSideEffects: false, propertyReadSideEffects: false as const,
tryCatchDeoptimization: false,
unknownGlobalSideEffects: false unknownGlobalSideEffects: false
}, },
output: { output: {

View File

@@ -148,9 +148,7 @@
#elif defined(ARDUINO_ARCH_ESP32) #elif defined(ARDUINO_ARCH_ESP32)
#include <driver/rtc_io.h> #include <driver/rtc_io.h>
#include <soc/gpio_struct.h> #include <soc/gpio_struct.h>
#if ESP_IDF_VERSION_MAJOR >= 5
#include "soc/gpio_periph.h" #include "soc/gpio_periph.h"
#endif // ESP_IDF_VERSION_MAJOR >= 5
#define PIN_TO_BASEREG(pin) (0) #define PIN_TO_BASEREG(pin) (0)
#define PIN_TO_BITMASK(pin) (pin) #define PIN_TO_BITMASK(pin) (pin)
#define IO_REG_TYPE uint32_t #define IO_REG_TYPE uint32_t

View File

@@ -12,7 +12,9 @@
// Include all library components // Include all library components
#include "esp32-psram/AllocatorPSRAM.h" // PSRAM-backed vector #include "esp32-psram/AllocatorPSRAM.h" // PSRAM-backed vector
#include "esp32-psram/VectorPSRAM.h" // PSRAM-backed vector #include "esp32-psram/VectorPSRAM.h" // PSRAM-backed vector
#include "esp32-psram/VectorHIMEM.h" // HIMEM-backed vector #if CONFIG_IDF_TARGET_ESP32
#include "esp32-psram/VectorHIMEM.h" // HIMEM-backed vector (ESP32 only)
#endif
// #include "esp32-psram/InMemoryFile.h" // File interface using vectors // #include "esp32-psram/InMemoryFile.h" // File interface using vectors
// #include "esp32-psram/PSRAM.h" // PSRAM file system // #include "esp32-psram/PSRAM.h" // PSRAM file system
// #include "esp32-psram/HIMEM.h" // HIMEM file system // #include "esp32-psram/HIMEM.h" // HIMEM file system

View File

@@ -1,5 +1,8 @@
#pragma once #pragma once
// HIMEM is only available on original ESP32
#if CONFIG_IDF_TARGET_ESP32
#include <algorithm> #include <algorithm>
#include <limits> #include <limits>
#include <memory> #include <memory>
@@ -360,3 +363,5 @@ class HimemBlock {
}; };
} // namespace esp32_psram } // namespace esp32_psram
#endif // CONFIG_IDF_TARGET_ESP32

View File

@@ -238,7 +238,9 @@ using RingBufferStreamPSRAM = RingBufferStream<VectorPSRAM<uint8_t>>;
/** /**
* @brief Type alias for a RingBufferStream that uses HIMEM-backed vector storage * @brief Type alias for a RingBufferStream that uses HIMEM-backed vector storage
*/ */
#if CONFIG_IDF_TARGET_ESP32
using RingBufferStreamHIMEM = RingBufferStream<VectorHIMEM<uint8_t>>; using RingBufferStreamHIMEM = RingBufferStream<VectorHIMEM<uint8_t>>;
#endif
/** /**
* @brief Type alias for a RingBufferStream that uses std::vector storage * @brief Type alias for a RingBufferStream that uses std::vector storage

View File

@@ -209,8 +209,10 @@ using TypedRingBufferRAM = TypedRingBuffer<T, std::vector<T>>;
/** /**
* @brief Type alias for a typed ring buffer that uses HIMEM-backed vector storage * @brief Type alias for a typed ring buffer that uses HIMEM-backed vector storage
*/ */
#if CONFIG_IDF_TARGET_ESP32
template<typename T> template<typename T>
using TypedRingBufferHIMEM = TypedRingBuffer<T, VectorHIMEM<T>>; using TypedRingBufferHIMEM = TypedRingBuffer<T, VectorHIMEM<T>>;
#endif
/** /**
* @brief Type alias for a typed ring buffer that uses PSRAM-backed vector storage * @brief Type alias for a typed ring buffer that uses PSRAM-backed vector storage

View File

@@ -1,5 +1,8 @@
#pragma once #pragma once
// HIMEM is only available on original ESP32
#if CONFIG_IDF_TARGET_ESP32
#include "HimemBlock.h" #include "HimemBlock.h"
namespace esp32_psram { namespace esp32_psram {
@@ -526,4 +529,6 @@ void swap(VectorHIMEM<T>& lhs, VectorHIMEM<T>& rhs) noexcept {
lhs.swap(rhs); lhs.swap(rhs);
} }
} // namespace esp32_psram } // namespace esp32_psram
#endif // CONFIG_IDF_TARGET_ESP32

View File

@@ -20,7 +20,7 @@
#include "../../src/core/emsesp.h" #include "../../src/core/emsesp.h"
#ifndef UUID_SYSLOG_HAVE_GETTIMEOFDAY #ifndef UUID_SYSLOG_HAVE_GETTIMEOFDAY
#if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32) #if defined(ARDUINO_ARCH_ESP8266)
// time() does not return UTC on the ESP8266: https://github.com/esp8266/Arduino/issues/4637 // time() does not return UTC on the ESP8266: https://github.com/esp8266/Arduino/issues/4637
#define UUID_SYSLOG_HAVE_GETTIMEOFDAY 1 #define UUID_SYSLOG_HAVE_GETTIMEOFDAY 1
#endif #endif

View File

@@ -145,6 +145,7 @@ double ledcSetup(uint8_t chan, double freq, uint8_t bit_num) {
return 0; return 0;
}; };
void ledcAttachPin(uint8_t pin, uint8_t chan) {}; void ledcAttachPin(uint8_t pin, uint8_t chan) {};
void ledcAttach(uint8_t pin, uint8_t chan, uint8_t bit_num) {};
void ledcWrite(uint8_t chan, uint32_t duty) {}; void ledcWrite(uint8_t chan, uint32_t duty) {};
void neopixelWrite(uint8_t pin, uint8_t red_val, uint8_t green_val, uint8_t blue_val) {}; void neopixelWrite(uint8_t pin, uint8_t red_val, uint8_t green_val, uint8_t blue_val) {};
void rgbLedWrite(uint8_t pin, uint8_t red_val, uint8_t green_val, uint8_t blue_val) {}; void rgbLedWrite(uint8_t pin, uint8_t red_val, uint8_t green_val, uint8_t blue_val) {};

View File

@@ -72,6 +72,7 @@ void analogSetAttenuation(adc_attenuation_t attenuation);
void dacWrite(uint8_t pin, uint8_t value); void dacWrite(uint8_t pin, uint8_t value);
double ledcSetup(uint8_t chan, double freq, uint8_t bit_num); double ledcSetup(uint8_t chan, double freq, uint8_t bit_num);
void ledcAttachPin(uint8_t pin, uint8_t chan); void ledcAttachPin(uint8_t pin, uint8_t chan);
void ledcAttach(uint8_t pin, uint8_t chan, uint8_t bit_num);
void ledcWrite(uint8_t chan, uint32_t duty); void ledcWrite(uint8_t chan, uint32_t duty);
void neopixelWrite(uint8_t pin, uint8_t red_val, uint8_t green_val, uint8_t blue_val); void neopixelWrite(uint8_t pin, uint8_t red_val, uint8_t green_val, uint8_t blue_val);
void rgbLedWrite(uint8_t pin, uint8_t red_val, uint8_t green_val, uint8_t blue_val); void rgbLedWrite(uint8_t pin, uint8_t red_val, uint8_t green_val, uint8_t blue_val);

View File

@@ -1,5 +1,5 @@
// ArduinoJson - https://arduinojson.org // ArduinoJson - https://arduinojson.org
// Copyright © 2014-2025, Benoit BLANCHON // Copyright © 2014-2026, Benoit BLANCHON
// MIT License // MIT License
#pragma once #pragma once
@@ -239,11 +239,11 @@
#define ARDUINOJSON_BIN2ALPHA_1111() P #define ARDUINOJSON_BIN2ALPHA_1111() P
#define ARDUINOJSON_BIN2ALPHA_(A, B, C, D) ARDUINOJSON_BIN2ALPHA_##A##B##C##D() #define ARDUINOJSON_BIN2ALPHA_(A, B, C, D) ARDUINOJSON_BIN2ALPHA_##A##B##C##D()
#define ARDUINOJSON_BIN2ALPHA(A, B, C, D) ARDUINOJSON_BIN2ALPHA_(A, B, C, D) #define ARDUINOJSON_BIN2ALPHA(A, B, C, D) ARDUINOJSON_BIN2ALPHA_(A, B, C, D)
#define ARDUINOJSON_VERSION "7.4.2" #define ARDUINOJSON_VERSION "7.4.3"
#define ARDUINOJSON_VERSION_MAJOR 7 #define ARDUINOJSON_VERSION_MAJOR 7
#define ARDUINOJSON_VERSION_MINOR 4 #define ARDUINOJSON_VERSION_MINOR 4
#define ARDUINOJSON_VERSION_REVISION 2 #define ARDUINOJSON_VERSION_REVISION 3
#define ARDUINOJSON_VERSION_MACRO V742 #define ARDUINOJSON_VERSION_MACRO V743
#ifndef ARDUINOJSON_VERSION_NAMESPACE #ifndef ARDUINOJSON_VERSION_NAMESPACE
#define ARDUINOJSON_VERSION_NAMESPACE \ #define ARDUINOJSON_VERSION_NAMESPACE \
ARDUINOJSON_CONCAT5(ARDUINOJSON_VERSION_MACRO, \ ARDUINOJSON_CONCAT5(ARDUINOJSON_VERSION_MACRO, \
@@ -1536,11 +1536,12 @@ template <typename T, size_t = sizeof(T)>
struct FloatTraits {}; struct FloatTraits {};
template <typename T> template <typename T>
struct FloatTraits<T, 8 /*64bits*/> { struct FloatTraits<T, 8 /*64bits*/> {
using mantissa_type = uint64_t; using mantissa_type = uint64_t;
static const short mantissa_bits = 52; static const short mantissa_bits = 52;
static const mantissa_type mantissa_max = (mantissa_type(1) << mantissa_bits) - 1; static const mantissa_type mantissa_max = (mantissa_type(1) << mantissa_bits) - 1;
using exponent_type = int16_t; using exponent_type = int16_t;
static const exponent_type exponent_max = 308; static const exponent_type exponent_max = 308;
static const size_t binaryPowersOfTen = 9;
static pgm_ptr<T> positiveBinaryPowersOfTen() { static pgm_ptr<T> positiveBinaryPowersOfTen() {
ARDUINOJSON_DEFINE_PROGMEM_ARRAY( // ARDUINOJSON_DEFINE_PROGMEM_ARRAY( //
uint64_t, uint64_t,
@@ -1601,11 +1602,12 @@ struct FloatTraits<T, 8 /*64bits*/> {
}; };
template <typename T> template <typename T>
struct FloatTraits<T, 4 /*32bits*/> { struct FloatTraits<T, 4 /*32bits*/> {
using mantissa_type = uint32_t; using mantissa_type = uint32_t;
static const short mantissa_bits = 23; static const short mantissa_bits = 23;
static const mantissa_type mantissa_max = (mantissa_type(1) << mantissa_bits) - 1; static const mantissa_type mantissa_max = (mantissa_type(1) << mantissa_bits) - 1;
using exponent_type = int8_t; using exponent_type = int8_t;
static const exponent_type exponent_max = 38; static const exponent_type exponent_max = 38;
static const size_t binaryPowersOfTen = 6;
static pgm_ptr<T> positiveBinaryPowersOfTen() { static pgm_ptr<T> positiveBinaryPowersOfTen() {
ARDUINOJSON_DEFINE_PROGMEM_ARRAY(uint32_t, ARDUINOJSON_DEFINE_PROGMEM_ARRAY(uint32_t,
factors, factors,
@@ -1668,9 +1670,12 @@ template <typename TFloat, typename TExponent>
inline TFloat make_float(TFloat m, TExponent e) { inline TFloat make_float(TFloat m, TExponent e) {
using traits = FloatTraits<TFloat>; using traits = FloatTraits<TFloat>;
auto powersOfTen = e > 0 ? traits::positiveBinaryPowersOfTen() : traits::negativeBinaryPowersOfTen(); auto powersOfTen = e > 0 ? traits::positiveBinaryPowersOfTen() : traits::negativeBinaryPowersOfTen();
auto count = traits::binaryPowersOfTen;
if (e <= 0) if (e <= 0)
e = TExponent(-e); e = TExponent(-e);
for (uint8_t index = 0; e != 0; index++) { for (uint8_t index = 0; e != 0; index++) {
if (index >= count)
return traits::nan();
if (e & 1) if (e & 1)
m *= powersOfTen[index]; m *= powersOfTen[index];
e >>= 1; e >>= 1;
@@ -2422,7 +2427,7 @@ class VariantData {
return; return;
var->removeMember(key, resources); var->removeMember(key, resources);
} }
void reset() { void reset() { // TODO: remove
type_ = VariantType::Null; type_ = VariantType::Null;
} }
void setBoolean(bool value) { void setBoolean(bool value) {
@@ -4214,7 +4219,7 @@ template <typename T, typename Enable = void>
struct Comparer; struct Comparer;
template <typename T> template <typename T>
struct Comparer<T, enable_if_t<IsString<T>::value>> : ComparerBase { struct Comparer<T, enable_if_t<IsString<T>::value>> : ComparerBase {
T rhs; T rhs; // TODO: store adapted string?
explicit Comparer(T value) explicit Comparer(T value)
: rhs(value) { : rhs(value) {
} }
@@ -5298,7 +5303,7 @@ class StringBuilder {
append(*s++); append(*s++);
} }
void append(const char * s, size_t n) { void append(const char * s, size_t n) {
while (n-- > 0) while (n-- > 0) // TODO: memcpy
append(*s++); append(*s++);
} }
void append(char c) { void append(char c) {

View File

@@ -12,8 +12,8 @@
"@msgpack/msgpack": "^3.1.3", "@msgpack/msgpack": "^3.1.3",
"@trivago/prettier-plugin-sort-imports": "^6.0.2", "@trivago/prettier-plugin-sort-imports": "^6.0.2",
"formidable": "^3.5.4", "formidable": "^3.5.4",
"itty-router": "^5.0.22", "itty-router": "^5.0.23",
"prettier": "^3.8.1" "prettier": "^3.8.1"
}, },
"packageManager": "pnpm@10.28.2+sha512.41872f037ad22f7348e3b1debbaf7e867cfd448f2726d9cf74c08f19507c31d2c8e7a11525b983febc2df640b5438dee6023ebb1f84ed43cc2d654d2bc326264" "packageManager": "pnpm@10.33.0+sha512.10568bb4a6afb58c9eb3630da90cc9516417abebd3fabbe6739f0ae795728da1491e9db5a544c76ad8eb7570f5c4bb3d6c637b2cb41bfdcdb47fa823c8649319"
} }

View File

@@ -18,8 +18,8 @@ importers:
specifier: ^3.5.4 specifier: ^3.5.4
version: 3.5.4 version: 3.5.4
itty-router: itty-router:
specifier: ^5.0.22 specifier: ^5.0.23
version: 5.0.22 version: 5.0.23
prettier: prettier:
specifier: ^3.8.1 specifier: ^3.8.1
version: 3.8.1 version: 3.8.1
@@ -30,8 +30,8 @@ packages:
resolution: {integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==} resolution: {integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==}
engines: {node: '>=6.9.0'} engines: {node: '>=6.9.0'}
'@babel/generator@7.29.0': '@babel/generator@7.29.1':
resolution: {integrity: sha512-vSH118/wwM/pLR38g/Sgk05sNtro6TlTJKuiMXDaZqPUfjTFcudpCOt00IhOfj+1BFAX+UFAlzCU+6WXr3GLFQ==} resolution: {integrity: sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==}
engines: {node: '>=6.9.0'} engines: {node: '>=6.9.0'}
'@babel/helper-globals@7.28.0': '@babel/helper-globals@7.28.0':
@@ -46,8 +46,8 @@ packages:
resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==}
engines: {node: '>=6.9.0'} engines: {node: '>=6.9.0'}
'@babel/parser@7.29.0': '@babel/parser@7.29.2':
resolution: {integrity: sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==} resolution: {integrity: sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==}
engines: {node: '>=6.0.0'} engines: {node: '>=6.0.0'}
hasBin: true hasBin: true
@@ -112,8 +112,8 @@ packages:
balanced-match@1.0.2: balanced-match@1.0.2:
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
brace-expansion@2.0.2: brace-expansion@2.0.3:
resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} resolution: {integrity: sha512-MCV/fYJEbqx68aE58kv2cA/kiky1G8vux3OR6/jbS+jIMe/6fJWa0DTzJU7dqijOWYwHi1t29FlfYI9uytqlpA==}
debug@4.4.3: debug@4.4.3:
resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==}
@@ -131,8 +131,8 @@ packages:
resolution: {integrity: sha512-YikH+7CUTOtP44ZTnUhR7Ic2UASBPOqmaRkRKxRbywPTe5VxF7RRCck4af9wutiZ/QKM5nME9Bie2fFaPz5Gug==} resolution: {integrity: sha512-YikH+7CUTOtP44ZTnUhR7Ic2UASBPOqmaRkRKxRbywPTe5VxF7RRCck4af9wutiZ/QKM5nME9Bie2fFaPz5Gug==}
engines: {node: '>=14.0.0'} engines: {node: '>=14.0.0'}
itty-router@5.0.22: itty-router@5.0.23:
resolution: {integrity: sha512-9hmdGErWdYDOurGYxSbqLhy4EFReIwk71hMZTJ5b+zfa2zjMNV1ftFno2b8VjAQvX615gNB8Qxbl9JMRqHnIVA==} resolution: {integrity: sha512-i49WU+SNPrwOZA4Z61En1RYd5h2Lcqa+5IvCpMrNi4dxymzJK15ozUUnRrWIUAv95Zamd4eJPAot2UvHRrQg7w==}
javascript-natural-sort@0.7.1: javascript-natural-sort@0.7.1:
resolution: {integrity: sha512-nO6jcEfZWQXDhOiBtG2KvKyEptz7RVbpGP4vTD2hLBdmNQSsCiicO2Ioinv6UI4y9ukqnBpy+XZ9H6uLNgJTlw==} resolution: {integrity: sha512-nO6jcEfZWQXDhOiBtG2KvKyEptz7RVbpGP4vTD2hLBdmNQSsCiicO2Ioinv6UI4y9ukqnBpy+XZ9H6uLNgJTlw==}
@@ -148,8 +148,8 @@ packages:
lodash-es@4.17.23: lodash-es@4.17.23:
resolution: {integrity: sha512-kVI48u3PZr38HdYz98UmfPnXl2DXrpdctLrFLCd3kOx1xUkOmpFPx7gCWWM5MPkL/fD8zb+Ph0QzjGFs4+hHWg==} resolution: {integrity: sha512-kVI48u3PZr38HdYz98UmfPnXl2DXrpdctLrFLCd3kOx1xUkOmpFPx7gCWWM5MPkL/fD8zb+Ph0QzjGFs4+hHWg==}
minimatch@9.0.5: minimatch@9.0.9:
resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} resolution: {integrity: sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==}
engines: {node: '>=16 || 14 >=14.17'} engines: {node: '>=16 || 14 >=14.17'}
ms@2.1.3: ms@2.1.3:
@@ -183,9 +183,9 @@ snapshots:
js-tokens: 4.0.0 js-tokens: 4.0.0
picocolors: 1.1.1 picocolors: 1.1.1
'@babel/generator@7.29.0': '@babel/generator@7.29.1':
dependencies: dependencies:
'@babel/parser': 7.29.0 '@babel/parser': 7.29.2
'@babel/types': 7.29.0 '@babel/types': 7.29.0
'@jridgewell/gen-mapping': 0.3.13 '@jridgewell/gen-mapping': 0.3.13
'@jridgewell/trace-mapping': 0.3.31 '@jridgewell/trace-mapping': 0.3.31
@@ -197,22 +197,22 @@ snapshots:
'@babel/helper-validator-identifier@7.28.5': {} '@babel/helper-validator-identifier@7.28.5': {}
'@babel/parser@7.29.0': '@babel/parser@7.29.2':
dependencies: dependencies:
'@babel/types': 7.29.0 '@babel/types': 7.29.0
'@babel/template@7.28.6': '@babel/template@7.28.6':
dependencies: dependencies:
'@babel/code-frame': 7.29.0 '@babel/code-frame': 7.29.0
'@babel/parser': 7.29.0 '@babel/parser': 7.29.2
'@babel/types': 7.29.0 '@babel/types': 7.29.0
'@babel/traverse@7.29.0': '@babel/traverse@7.29.0':
dependencies: dependencies:
'@babel/code-frame': 7.29.0 '@babel/code-frame': 7.29.0
'@babel/generator': 7.29.0 '@babel/generator': 7.29.1
'@babel/helper-globals': 7.28.0 '@babel/helper-globals': 7.28.0
'@babel/parser': 7.29.0 '@babel/parser': 7.29.2
'@babel/template': 7.28.6 '@babel/template': 7.28.6
'@babel/types': 7.29.0 '@babel/types': 7.29.0
debug: 4.4.3 debug: 4.4.3
@@ -248,13 +248,13 @@ snapshots:
'@trivago/prettier-plugin-sort-imports@6.0.2(prettier@3.8.1)': '@trivago/prettier-plugin-sort-imports@6.0.2(prettier@3.8.1)':
dependencies: dependencies:
'@babel/generator': 7.29.0 '@babel/generator': 7.29.1
'@babel/parser': 7.29.0 '@babel/parser': 7.29.2
'@babel/traverse': 7.29.0 '@babel/traverse': 7.29.0
'@babel/types': 7.29.0 '@babel/types': 7.29.0
javascript-natural-sort: 0.7.1 javascript-natural-sort: 0.7.1
lodash-es: 4.17.23 lodash-es: 4.17.23
minimatch: 9.0.5 minimatch: 9.0.9
parse-imports-exports: 0.2.4 parse-imports-exports: 0.2.4
prettier: 3.8.1 prettier: 3.8.1
transitivePeerDependencies: transitivePeerDependencies:
@@ -264,7 +264,7 @@ snapshots:
balanced-match@1.0.2: {} balanced-match@1.0.2: {}
brace-expansion@2.0.2: brace-expansion@2.0.3:
dependencies: dependencies:
balanced-match: 1.0.2 balanced-match: 1.0.2
@@ -283,7 +283,7 @@ snapshots:
dezalgo: 1.0.4 dezalgo: 1.0.4
once: 1.4.0 once: 1.4.0
itty-router@5.0.22: {} itty-router@5.0.23: {}
javascript-natural-sort@0.7.1: {} javascript-natural-sort@0.7.1: {}
@@ -293,9 +293,9 @@ snapshots:
lodash-es@4.17.23: {} lodash-es@4.17.23: {}
minimatch@9.0.5: minimatch@9.0.9:
dependencies: dependencies:
brace-expansion: 2.0.2 brace-expansion: 2.0.3
ms@2.1.3: {} ms@2.1.3: {}

View File

@@ -363,6 +363,8 @@ function export_data(type: string) {
return emsesp_modules; return emsesp_modules;
case 'allvalues': case 'allvalues':
return emsesp_allvalues; return emsesp_allvalues;
case 'systembackup':
return emsesp_systembackup;
default: default:
return status(404); return status(404);
} }
@@ -732,6 +734,8 @@ const emsesp_info = {
] ]
}; };
const emsesp_systembackup = {};
const emsesp_allvalues = { const emsesp_allvalues = {
'Boiler Nefit Trendline HRC30 (DeviceID:0x08, ProductID:123, Version:06.01)': { 'Boiler Nefit Trendline HRC30 (DeviceID:0x08, ProductID:123, Version:06.01)': {
'force heating off': 'off', 'force heating off': 'off',
@@ -4564,7 +4568,7 @@ router
let sorted_devices = [...emsesp_coredata.devices].sort((a, b) => a.t - b.t); let sorted_devices = [...emsesp_coredata.devices].sort((a, b) => a.t - b.t);
// append emsesp_coredata to sorted_devices so Custom is always at the end of the list // append emsesp_coredata to sorted_devices so Custom is always at the end of the list
sorted_devices.push(emsesp_coredata_custom); sorted_devices.push(emsesp_coredata_custom);
// sorted_devices = []; // uncomment if simulating no devices... // return { connected: false, devices: [] }; // uncomment if simulating no devices...
return { connected: true, devices: sorted_devices }; return { connected: true, devices: sorted_devices };
}) })
.get(EMSESP_SENSOR_DATA_ENDPOINT, () => { .get(EMSESP_SENSOR_DATA_ENDPOINT, () => {

View File

@@ -15,15 +15,14 @@ description = EMS-ESP Firmware for the ESP32
src_dir = src src_dir = src
lib_dir = lib lib_dir = lib
boards_dir = boards boards_dir = boards
; build_cache_dir = .pio/build_cache
extra_configs = extra_configs =
factory_settings.ini factory_settings.ini
pio_local.ini pio_local.ini
[common] [common]
core_build_flags = -std=c++17 -std=gnu++17 -O3 -flto=auto -Wno-type-limits -Wall -Wextra -Wno-unused-parameter -Wno-unused-variable -Wno-format core_build_flags = -std=c++17 -std=gnu++17 -O3 -Wno-type-limits -Wall -Wextra -Wno-unused-parameter -Wno-unused-variable -Wno-format -Wno-missing-field-initializers
core_unbuild_flags = -std=gnu++11 -fno-lto core_unbuild_flags = -std=gnu++11
my_build_flags = my_build_flags =
@@ -104,11 +103,10 @@ monitor_filters = direct
build_type = release build_type = release
board_build.filesystem = littlefs board_build.filesystem = littlefs
lib_deps = lib_deps =
bblanchon/ArduinoJson @ 7.4.2 bblanchon/ArduinoJson @ 7.4.3
ESP32Async/AsyncTCP @ 3.4.10 ESP32Async/AsyncTCP @ 3.4.10
ESP32Async/ESPAsyncWebServer @ 3.10.3 ESP32Async/ESPAsyncWebServer @ 3.10.3
https://github.com/emsesp/EMS-ESP-Modules.git @ 1.0.8 ; https://github.com/emsesp/EMS-ESP-Modules.git @ 1.0.8
; builds the web interface only, not the firmware ; builds the web interface only, not the firmware
[env:build_webUI] [env:build_webUI]
@@ -199,6 +197,7 @@ build_src_filter =
-<../lib/uuid-syslog> -<../lib/uuid-syslog>
-<../lib/eModbus> -<../lib/eModbus>
-<../lib/OneWire> -<../lib/OneWire>
-<../src/core/ModuleLibrary.cpp>
lib_ldf_mode = off lib_ldf_mode = off
lib_deps = lib_deps =
@@ -241,6 +240,7 @@ build_src_filter =
-<../lib/uuid-syslog> -<../lib/uuid-syslog>
-<../lib/eModbus> -<../lib/eModbus>
-<../lib/OneWire> -<../lib/OneWire>
-<../src/core/ModuleLibrary.cpp>
lib_ldf_mode = off lib_ldf_mode = off
lib_deps = Unity lib_deps = Unity
test_testing_command = test_testing_command =

View File

@@ -6,6 +6,9 @@ abluftqualität
abluftqualitätsfühler abluftqualitätsfühler
abrt abrt
absburnpow absburnpow
ACAC
ACAH
ACAO
accomodate accomodate
aceotrope aceotrope
activehigh activehigh
@@ -64,7 +67,6 @@ asyncwebsocket
asyncwebsynchronization asyncwebsynchronization
atoint atoint
ausreichend ausreichend
außenluft
authmode authmode
autodst autodst
automode automode
@@ -82,6 +84,7 @@ auxheatmix
auxheatrmode auxheatrmode
auxlimitstart auxlimitstart
auxmaxlimit auxmaxlimit
außenluft
avty avty
awsc awsc
azubuike azubuike
@@ -137,7 +140,6 @@ calinttemp
catagory catagory
cbuf cbuf
cerapur cerapur
česky
cfamily cfamily
changeloglevel changeloglevel
chargeduration chargeduration
@@ -192,6 +194,7 @@ controlmode
coolingcircuit coolingcircuit
coolingon coolingon
coolingstarts coolingstarts
coolingtype
cooloffdelay cooloffdelay
coolondelay coolondelay
coolstart coolstart
@@ -669,12 +672,12 @@ lowpressure
lowtopic lowtopic
lufqualität lufqualität
luft luft
lüfterstufe
luftfeuchte luftfeuchte
luftfeuchtefühler luftfeuchtefühler
luftqualfühl luftqualfühl
lüftungsfrostschutz
lukás lukás
lüfterstufe
lüftungsfrostschutz
maintenancedate maintenancedate
maintenancemessage maintenancemessage
maintenancetime maintenancetime
@@ -827,7 +830,6 @@ nwcl
nystrom nystrom
odata odata
oddparity oddparity
öffnen
offsettemp offsettemp
offtemp offtemp
oilpreheat oilpreheat
@@ -942,6 +944,9 @@ rawstr
rcplus rcplus
readback readback
readelf readelf
READYCLIENT
readymail
recp
recved recved
redtemp redtemp
redthreshold redthreshold
@@ -1090,17 +1095,18 @@ staipassigned
startshp startshp
starttemp starttemp
starttemperature starttemperature
starttls
startvalue startvalue
statusbyte statusbyte
stickbreaker stickbreaker
stoffregen stoffregen
stoptime stoptime
storagetemp storagetemp
störung
stringarray stringarray
stringize stringize
studt studt
studt's studt's
störung
suback suback
substract substract
summermode summermode
@@ -1126,6 +1132,7 @@ switchtimeww
sycle sycle
syspress syspress
sysrettemp sysrettemp
systembackup
systemstatus systemstatus
tado tado
tapactivated tapactivated
@@ -1182,7 +1189,6 @@ trocken
truefalse truefalse
tsens tsens
tseslint tseslint
türk
turnoffdiff turnoffdiff
turnondiff turnondiff
txen txen
@@ -1200,6 +1206,7 @@ typehh
typeids typeids
typel typel
typesafe typesafe
türk
uart uart
ubauptime ubauptime
ucrt ucrt
@@ -1252,7 +1259,6 @@ vorheizreg
vpcooling vpcooling
vrey vrey
vybm vybm
wärmetauscher
washingmachine washingmachine
watchid watchid
wayon wayon
@@ -1301,6 +1307,7 @@ wwtemp
wwtemplow wwtemplow
wwvacations wwvacations
wwwhenmodeoff wwwhenmodeoff
wärmetauscher
xclnt xclnt
xiao xiao
xrtu xrtu
@@ -1311,7 +1318,6 @@ yerger's
yubox yubox
zadig zadig
zivanovic zivanovic
živanović
zlcn zlcn
zolder zolder
zqhi zqhi
@@ -1321,6 +1327,7 @@ zuluftebläse
zuluftgebläse zuluftgebläse
zulufttemp zulufttemp
zyxwvutsrqponmlkjihgfedcba zyxwvutsrqponmlkjihgfedcba
ACAO öffnen
ACAH česky
ACAC živanović
MWDT

View File

@@ -66,21 +66,16 @@ void ArduinoJsonJWT::parseJWT(String jwt, JsonDocument & jsonDocument) {
} }
/* /*
* ESP32 uses mbedtls, with decent HMAC implementations supporting sha256, as well as others. * HMAC-SHA256 using mbedtls
* No need to pull in additional crypto libraries - lets use what we already have.
*/ */
String ArduinoJsonJWT::sign(String & payload) { String ArduinoJsonJWT::sign(String & payload) {
std::array<unsigned char, 32> hmacResult{}; std::array<unsigned char, 32> hmacResult{};
{ mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256),
mbedtls_md_context_t ctx; reinterpret_cast<const unsigned char *>(_secret.c_str()),
mbedtls_md_type_t md_type = MBEDTLS_MD_SHA256; _secret.length(),
mbedtls_md_init(&ctx); reinterpret_cast<const unsigned char *>(payload.c_str()),
mbedtls_md_setup(&ctx, mbedtls_md_info_from_type(md_type), 1); payload.length(),
mbedtls_md_hmac_starts(&ctx, reinterpret_cast<const unsigned char *>(_secret.c_str()), _secret.length()); hmacResult.data());
mbedtls_md_hmac_update(&ctx, reinterpret_cast<const unsigned char *>(payload.c_str()), payload.length());
mbedtls_md_hmac_finish(&ctx, hmacResult.data());
mbedtls_md_free(&ctx);
}
return encode(reinterpret_cast<const char *>(hmacResult.data()), hmacResult.size()); return encode(reinterpret_cast<const char *>(hmacResult.data()), hmacResult.size());
} }

View File

@@ -61,22 +61,27 @@ void UploadFileService::handleUpload(AsyncWebServerRequest * request, const Stri
if (_is_firmware) { if (_is_firmware) {
// Check firmware header, 0xE9 magic offset 0 indicates esp bin, chip offset 12: esp32:0, S2:2, C3:5 // Check firmware header, 0xE9 magic offset 0 indicates esp bin, chip offset 12: esp32:0, S2:2, C3:5
#if CONFIG_IDF_TARGET_ESP32 // ESP32/PICO-D4 #if CONFIG_IDF_TARGET_ESP32 // ESP32/PICO-D4
if (len > 12 && (data[0] != 0xE9 || data[12] != 0)) { if (len > 12 && (data[0] != ESP_IMAGE_HEADER_MAGIC || data[12] != ESP_CHIP_ID_ESP32)) {
handleError(request, 503); // service unavailable handleError(request, 503); // service unavailable
return; return;
} }
#elif CONFIG_IDF_TARGET_ESP32S2 #elif CONFIG_IDF_TARGET_ESP32S2
if (len > 12 && (data[0] != 0xE9 || data[12] != 2)) { if (len > 12 && (data[0] != ESP_IMAGE_HEADER_MAGIC || data[12] != ESP_CHIP_ID_ESP32S2)) {
handleError(request, 503); // service unavailable handleError(request, 503); // service unavailable
return; return;
} }
#elif CONFIG_IDF_TARGET_ESP32C3 #elif CONFIG_IDF_TARGET_ESP32C3
if (len > 12 && (data[0] != 0xE9 || data[12] != 5)) { if (len > 12 && (data[0] != ESP_IMAGE_HEADER_MAGIC || data[12] != ESP_CHIP_ID_ESP32C3)) {
handleError(request, 503); // service unavailable handleError(request, 503); // service unavailable
return; return;
} }
#elif CONFIG_IDF_TARGET_ESP32S3 #elif CONFIG_IDF_TARGET_ESP32S3
if (len > 12 && (data[0] != 0xE9 || data[12] != 9)) { if (len > 12 && (data[0] != ESP_IMAGE_HEADER_MAGIC || data[12] != ESP_CHIP_ID_ESP32S3)) {
handleError(request, 503); // service unavailable
return;
}
#elif CONFIG_IDF_TARGET_ESP32C6
if (len > 12 && (data[0] != ESP_IMAGE_HEADER_MAGIC || data[12] != ESP_CHIP_ID_ESP32C6)) {
handleError(request, 503); // service unavailable handleError(request, 503); // service unavailable
return; return;
} }
@@ -179,7 +184,7 @@ void UploadFileService::handleError(AsyncWebServerRequest * request, int code) {
} }
void UploadFileService::handleEarlyDisconnect() { void UploadFileService::handleEarlyDisconnect() {
emsesp::EMSESP::logger().info("Upload ended"); emsesp::EMSESP::logger().info("Upload completed");
emsesp::EMSESP::system_.uart_init(); // re-enable UART emsesp::EMSESP::system_.uart_init(); // re-enable UART
_is_firmware = false; _is_firmware = false;

View File

@@ -0,0 +1,31 @@
/*
* EMS-ESP - https://github.com/emsesp/EMS-ESP
* Copyright 2020-2025 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 <Arduino.h>
#include <emsesp.h>
void ModuleLibrary::list(JsonObject output) {};
void ModuleLibrary::loop() {};
void ModuleLibrary::start(emsesp::EMSESP * emsesp_main, bool test_mode) {};
bool ModuleLibrary::enable(const char * key, const char * license, bool enable) {
return true;
};

52
src/core/ModuleLibrary.h Normal file
View File

@@ -0,0 +1,52 @@
/*
* EMS-ESP - https://github.com/emsesp/EMS-ESP
* Copyright 2020-2025 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 MODULELIBRARY_H
#define MODULELIBRARY_H
#include <Arduino.h>
#include <memory>
#include <vector>
#include <emsesp.h>
class ModuleLibrary {
public:
class Modules {
public:
Modules(const char * key, std::unique_ptr<Module> module)
: key(key)
, module(std::move(module)) {
}
const char * key;
std::unique_ptr<Module> module;
};
void start(emsesp::EMSESP * emsesp_main, bool test_mode = false);
void loop();
void list(JsonObject output);
bool enable(const char * key, const char * license, bool enable);
static uuid::log::Logger logger_;
private:
std::vector<Modules> modules_;
};
#endif

View File

@@ -56,6 +56,7 @@ void IRAM_ATTR AnalogSensor::freqIrq2() {
#endif #endif
void AnalogSensor::start(const bool factory_settings) { void AnalogSensor::start(const bool factory_settings) {
// add hardcoded sensors for BBQKees gateway boards
if (factory_settings && EMSESP::system_.board_profile() == "E32V2_2") { if (factory_settings && EMSESP::system_.board_profile() == "E32V2_2") {
EMSESP::webCustomizationService.update([&](WebCustomization & settings) { EMSESP::webCustomizationService.update([&](WebCustomization & settings) {
auto newSensor = AnalogCustomization(); auto newSensor = AnalogCustomization();
@@ -896,9 +897,9 @@ std::string AnalogSensor::get_metrics_prometheus() {
result += (std::string) "\n# TYPE emsesp_" + sensor.name() + " gauge\n"; result += (std::string) "\n# TYPE emsesp_" + sensor.name() + " gauge\n";
result += (std::string) "emsesp_" + sensor.name() + " "; result += (std::string) "emsesp_" + sensor.name() + " ";
if (sensor.type() != AnalogType::DIGITAL_OUT && sensor.type() != AnalogType::DIGITAL_IN) { if (sensor.type() != AnalogType::DIGITAL_OUT && sensor.type() != AnalogType::DIGITAL_IN) {
result += (std::string) Helpers::render_value(val, sensor.value(), 2) + "\n"; result += (std::string)Helpers::render_value(val, sensor.value(), 2) + "\n";
} else { } else {
result += (std::string) (sensor.value() == 0 ? "0\n" : "1\n"); result += (std::string)(sensor.value() == 0 ? "0\n" : "1\n");
} }
} }
return result; return result;

View File

@@ -84,7 +84,7 @@ uuid::log::Logger EMSESP::logger() {
RxService EMSESP::rxservice_; // incoming Telegram Rx handler RxService EMSESP::rxservice_; // incoming Telegram Rx handler
TxService EMSESP::txservice_; // outgoing Telegram Tx handler TxService EMSESP::txservice_; // outgoing Telegram Tx handler
Mqtt EMSESP::mqtt_; // mqtt handler Mqtt EMSESP::mqtt_; // mqtt handler
Modbus * EMSESP::modbus_; // modbus handler Modbus * EMSESP::modbus_ = nullptr; // modbus handler
System EMSESP::system_; // core system services System EMSESP::system_; // core system services
TemperatureSensor EMSESP::temperaturesensor_; // Temperature sensors TemperatureSensor EMSESP::temperaturesensor_; // Temperature sensors
AnalogSensor EMSESP::analogsensor_; // Analog sensors AnalogSensor EMSESP::analogsensor_; // Analog sensors
@@ -1600,10 +1600,11 @@ void EMSESP::incoming_telegram(uint8_t * data, const uint8_t length) {
wait_km_ = true; wait_km_ = true;
connect_time = uuid::get_uptime_sec(); connect_time = uuid::get_uptime_sec();
} }
if (poll_id == EMSbus::ems_bus_id()) { if (poll_id == EMSbus::ems_bus_id()) {
// TODO this could also be by coincidence, so we should add a counter to the EMSbus class to check if the poll_id is the same as the EMS_BUS_ID for a certain number of times
EMSbus::last_bus_activity(uuid::get_uptime()); // set the flag indication the EMS bus is active EMSbus::last_bus_activity(uuid::get_uptime()); // set the flag indication the EMS bus is active
} }
if (wait_km_) { if (wait_km_) {
if (poll_id != 0x48 && (uuid::get_uptime_sec() - connect_time) < EMS_WAIT_KM_TIMEOUT) { if (poll_id != 0x48 && (uuid::get_uptime_sec() - connect_time) < EMS_WAIT_KM_TIMEOUT) {
return; return;
@@ -1709,6 +1710,11 @@ void EMSESP::start() {
bool factory_settings = false; bool factory_settings = false;
#endif #endif
// start NVS storage
if (!nvs_.begin("ems-esp", false, "nvs1")) { // try bigger nvs partition on 16M flash first
nvs_.begin("ems-esp", false, "nvs"); // fallback to small nvs
}
// 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();
@@ -1757,7 +1763,7 @@ void EMSESP::start() {
LOG_INFO("Last system reset reason Core0: %s, Core1: %s", system_.reset_reason(0).c_str(), system_.reset_reason(1).c_str()); LOG_INFO("Last system reset reason Core0: %s, Core1: %s", system_.reset_reason(0).c_str(), system_.reset_reason(1).c_str());
#endif #endif
// see if we're restoring a settings file // see if we're restoring a settings file
#ifndef EMSESP_STANDALONE #ifndef EMSESP_STANDALONE
if (system_.check_restore()) { if (system_.check_restore()) {
LOG_WARNING("EMS-ESP will restart to apply new settings. Please wait."); LOG_WARNING("EMS-ESP will restart to apply new settings. Please wait.");
@@ -1765,11 +1771,7 @@ void EMSESP::start() {
}; };
#endif #endif
if (!nvs_.begin("ems-esp", false, "nvs1")) { // try bigger nvs partition on 16M flash first LOG_DEBUG("eFuse device information: %s", system_.getBBQKeesGatewayDetails().isEmpty() ? "not set" : system_.getBBQKeesGatewayDetails().c_str());
nvs_.begin("ems-esp", false, "nvs"); // fallback to small nvs
}
LOG_DEBUG("Fuse device information: %s", system_.getBBQKeesGatewayDetails().isEmpty() ? "not set" : system_.getBBQKeesGatewayDetails().c_str());
webSettingsService.begin(); // load EMS-ESP Application settings webSettingsService.begin(); // load EMS-ESP Application settings
@@ -1802,12 +1804,6 @@ void EMSESP::start() {
#endif #endif
} }
// start services
if (system_.modbus_enabled()) {
modbus_ = new Modbus;
modbus_->start(1, system_.modbus_port(), system_.modbus_max_clients(), system_.modbus_timeout() * 1000);
}
mqtt_.start(); // mqtt init mqtt_.start(); // mqtt init
system_.start(); // starts commands, led, adc, button, network (sets hostname), syslog & uart system_.start(); // starts commands, led, adc, button, network (sets hostname), syslog & uart
shower_.start(); // initialize shower timer and shower alert shower_.start(); // initialize shower timer and shower alert
@@ -1854,7 +1850,7 @@ void EMSESP::loop() {
return; // LED flashing is active, skip the rest of the loop return; // LED flashing is active, skip the rest of the loop
} }
esp32React.loop(); // web services like network, AP, MQTT 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 loop, unless we're in the middle of an OTA upload

91
src/core/emsesp_common.h Normal file
View File

@@ -0,0 +1,91 @@
/*
* EMS-ESP - https://github.com/emsesp/EMS-ESP
* Copyright 2020-2025 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_COMMON_H
#define EMSESP_COMMON_H
#include <uuid/log.h>
using uuid::log::Level;
#if defined(EMSESP_DEBUG)
#define LOG_DEBUG(...) logger_.debug(__VA_ARGS__)
#else
#define LOG_DEBUG(...)
#endif
#define LOG_INFO(...) logger_.info(__VA_ARGS__)
#define LOG_TRACE(...) logger_.trace(__VA_ARGS__)
#define LOG_NOTICE(...) logger_.notice(__VA_ARGS__)
#define LOG_WARNING(...) logger_.warning(__VA_ARGS__)
#define LOG_ERROR(...) logger_.err(__VA_ARGS__)
// flash strings
using uuid::string_vector;
using string_vector = std::vector<const char *>;
#ifdef FPSTR
#undef FPSTR
#endif
// clang-format off
#define FPSTR(pstr_pointer) pstr_pointer
#define MAKE_WORD_CUSTOM(string_name, string_literal) static const char __pstr__##string_name[] = string_literal;
#define MAKE_WORD(string_name) MAKE_WORD_CUSTOM(string_name, #string_name)
#define F_(string_name) (__pstr__##string_name)
#define FL_(list_name) (__pstr__L_##list_name)
// Translation counter - capture baseline before any MAKE_TRANSLATION calls
enum { EMSESP_TRANSLATION_COUNT_START = __COUNTER__ };
// The language settings below must match system.cpp
#if defined(EMSESP_EN_ONLY)
// EN only
#define MAKE_WORD_TRANSLATION(list_name, en, ...) static const char * const __pstr__L_##list_name[] = {en, nullptr};
#define MAKE_TRANSLATION(list_name, shortname, en, ...) static constexpr int __translation_counter_##list_name = __COUNTER__; static const char * const __pstr__L_##list_name[] = {shortname, en, nullptr};
#elif defined(EMSESP_TEST) || defined(EMSESP_DE_ONLY)
// EN + DE (Test mode uses two languages to save flash memory)
#define MAKE_WORD_TRANSLATION(list_name, en, de, ...) static const char * const __pstr__L_##list_name[] = {en, de, nullptr};
#define MAKE_TRANSLATION(list_name, shortname, en, de, ...) static constexpr int __translation_counter_##list_name = __COUNTER__; static const char * const __pstr__L_##list_name[] = {shortname, en, de, nullptr};
#else
// All languages
#define MAKE_WORD_TRANSLATION(list_name, ...) static const char * const __pstr__L_##list_name[] = {__VA_ARGS__, nullptr};
#define MAKE_TRANSLATION(list_name, ...) static constexpr int __translation_counter_##list_name = __COUNTER__; static const char * const __pstr__L_##list_name[] = {__VA_ARGS__, nullptr};
#endif
#define MAKE_NOTRANSLATION(list_name, ...) static const char * const __pstr__L_##list_name[] = {__VA_ARGS__, nullptr};
// fixed strings, no translations
#define MAKE_ENUM_FIXED(enum_name, ...) static const char * const __pstr__L_##enum_name[] = {__VA_ARGS__, nullptr};
// with translations
#define MAKE_ENUM(enum_name, ...) static const char * const * __pstr__L_##enum_name[] = {__VA_ARGS__, nullptr};
// clang-format on
// load translations
#include "locale_translations.h"
#include "locale_common.h"
// Translation count - dynamically calculated at compile-time
enum { EMSESP_TRANSLATION_COUNT_END = __COUNTER__ };
static constexpr uint16_t EMSESP_TRANSLATION_COUNT = static_cast<int>(EMSESP_TRANSLATION_COUNT_END) - static_cast<int>(EMSESP_TRANSLATION_COUNT_START) - 1;
#endif

View File

@@ -20,7 +20,7 @@
#define EMSESP_HELPERS_H #define EMSESP_HELPERS_H
#include "telegram.h" // for EMS_VALUE_* settings #include "telegram.h" // for EMS_VALUE_* settings
#include "common.h" #include "emsesp_common.h"
namespace emsesp { namespace emsesp {

View File

@@ -47,6 +47,7 @@ MAKE_WORD(raw)
MAKE_WORD(watch) MAKE_WORD(watch)
MAKE_WORD(syslog) MAKE_WORD(syslog)
MAKE_WORD(send) MAKE_WORD(send)
MAKE_WORD(sendmail)
MAKE_WORD(telegram) MAKE_WORD(telegram)
MAKE_WORD(bus_id) MAKE_WORD(bus_id)
MAKE_WORD(tx_mode) MAKE_WORD(tx_mode)

View File

@@ -32,10 +32,10 @@
#define EMSESP_LOCALE_SK "sk" #define EMSESP_LOCALE_SK "sk"
#define EMSESP_LOCALE_CZ "cz" #define EMSESP_LOCALE_CZ "cz"
// IMPORTANT! translations are in the order:,en, de, nl, sv, pl, no, fr, tr, it, sk, cz // IMPORTANT! translations are in the order: en, de, nl, sv, pl, no, fr, tr, it, sk, cz
// //
// if there is no translation, it will default to en // if there is no translation, it will default to en
//
// device types, as display in Web and Console // device types, as display in Web and Console
MAKE_WORD_TRANSLATION(boiler_hp_device, "Boiler/HP", "Kessel/WP", "CV ketel/WP", "Värmepanna/VP", "Kocioł/PC", "Varmekjele/VP", "Chaudière/PC", "Kazan/IP", "Caldaia/PC", "Kotol/TČ", "Kotel/TČ") MAKE_WORD_TRANSLATION(boiler_hp_device, "Boiler/HP", "Kessel/WP", "CV ketel/WP", "Värmepanna/VP", "Kocioł/PC", "Varmekjele/VP", "Chaudière/PC", "Kazan/IP", "Caldaia/PC", "Kotol/TČ", "Kotel/TČ")
MAKE_WORD_TRANSLATION(boiler_device, "Boiler", "Kessel", "CV ketel", "Värmepanna", "Kocioł", "Varmekjele", "Chaudière", "Kazan", "Caldaia", "Kotol", "Kotel") MAKE_WORD_TRANSLATION(boiler_device, "Boiler", "Kessel", "CV ketel", "Värmepanna", "Kocioł", "Varmekjele", "Chaudière", "Kazan", "Caldaia", "Kotol", "Kotel")
@@ -65,6 +65,7 @@ MAKE_WORD_TRANSLATION(commands_cmd, "list all commands", "Liste aller Kommandos"
MAKE_WORD_TRANSLATION(entities_cmd, "list all entities", "Liste aller Entitäten", "lijst van alle entiteiten", "lista all entiteter", "wyświetl wszsytkie encje", "Viser alle enheter", "lister toutes les entités", "Tüm varlıkları listele", "elenca tutte le entità", "zobraziť všetky entity", "vypsat všechny entity") MAKE_WORD_TRANSLATION(entities_cmd, "list all entities", "Liste aller Entitäten", "lijst van alle entiteiten", "lista all entiteter", "wyświetl wszsytkie encje", "Viser alle enheter", "lister toutes les entités", "Tüm varlıkları listele", "elenca tutte le entità", "zobraziť všetky entity", "vypsat všechny entity")
MAKE_WORD_TRANSLATION(metrics_cmd, "list all prometheus metrics", "Liste aller Prometheus Metriken", "lijst van alle Prometheus metriken", "lista alla Prometheus metriker", "wyświetl wszystkie Prometheus metryki", "Viser alle Prometheus metrikker", "lister toutes les métriques Prometheus", "Tüm Prometheus metriklerini listele", "elenca tutte le metriche Prometheus", "zobraziť všetky Prometheus metriky", "vypsat všechny Prometheus metriky") MAKE_WORD_TRANSLATION(metrics_cmd, "list all prometheus metrics", "Liste aller Prometheus Metriken", "lijst van alle Prometheus metriken", "lista alla Prometheus metriker", "wyświetl wszystkie Prometheus metryki", "Viser alle Prometheus metrikker", "lister toutes les métriques Prometheus", "Tüm Prometheus metriklerini listele", "elenca tutte le metriche Prometheus", "zobraziť všetky Prometheus metriky", "vypsat všechny Prometheus metriky")
MAKE_WORD_TRANSLATION(send_cmd, "send a telegram", "Sende EMS-Telegramm", "stuur een telegram", "skicka ett telegram", "wyślij telegram", "send et telegram", "envoyer un télégramme", "Bir telegram gönder", "invia un telegramma", "poslať telegram", "odeslat telegram") MAKE_WORD_TRANSLATION(send_cmd, "send a telegram", "Sende EMS-Telegramm", "stuur een telegram", "skicka ett telegram", "wyślij telegram", "send et telegram", "envoyer un télégramme", "Bir telegram gönder", "invia un telegramma", "poslať telegram", "odeslat telegram")
MAKE_WORD_TRANSLATION(sendmail_cmd, "send email", "Sende eMail", "stuur email", "skicka email", "wyślij email", "send e-post", "envoyer un email", "email gönder", "invia email", "poslať email", "odeslat email")
MAKE_WORD_TRANSLATION(read_cmd, "send read request", "Sende Leseanfrage", "stuur leesaanvraag", "skicka en läsförfrågan", "wyślij żądanie odczytu", "send leseforespørsel", "envoyer une demande de lecture", "okuma isteği gönder", "invia richiesta di lettura", "odoslať žiadosť o prečítanie", "odeslat požadavek na čtení") MAKE_WORD_TRANSLATION(read_cmd, "send read request", "Sende Leseanfrage", "stuur leesaanvraag", "skicka en läsförfrågan", "wyślij żądanie odczytu", "send leseforespørsel", "envoyer une demande de lecture", "okuma isteği gönder", "invia richiesta di lettura", "odoslať žiadosť o prečítanie", "odeslat požadavek na čtení")
MAKE_WORD_TRANSLATION(setiovalue_cmd, "set I/O value", "Setze Werte E/A", "instellen standaardwaarde", "sätt ett I/O-värde", "ustaw wartość", "sett en io verdi", "définir valeur E/S", "Giriş/Çıkış değerlerini ayarla", "imposta valore io", "nastaviť hodnotu io", "nastavit hodnotu I/O") MAKE_WORD_TRANSLATION(setiovalue_cmd, "set I/O value", "Setze Werte E/A", "instellen standaardwaarde", "sätt ett I/O-värde", "ustaw wartość", "sett en io verdi", "définir valeur E/S", "Giriş/Çıkış değerlerini ayarla", "imposta valore io", "nastaviť hodnotu io", "nastavit hodnotu I/O")
MAKE_WORD_TRANSLATION(changeloglevel_cmd, "change log level", "Ändere Protokollebene", "aanpassen log niveau", "ändra logg-nivå", "zmień poziom log-u", "endre loggnivå", "changer le niveau de journal", "Kayıt seviyesini değiştir", "cambia livello registrazione", "zmeniť úroveň protokolu", "změnit úroveň protokolování") MAKE_WORD_TRANSLATION(changeloglevel_cmd, "change log level", "Ändere Protokollebene", "aanpassen log niveau", "ändra logg-nivå", "zmień poziom log-u", "endre loggnivå", "changer le niveau de journal", "Kayıt seviyesini değiştir", "cambia livello registrazione", "zmeniť úroveň protokolu", "změnit úroveň protokolování")
@@ -104,6 +105,7 @@ MAKE_WORD_TRANSLATION(tag_dhw7, "dhw7", "WWK7", "dhw7", "VVK7", "CWU7", "dhw7",
MAKE_WORD_TRANSLATION(tag_dhw8, "dhw8", "WWK8", "dhw8", "VVK8", "CWU8", "dhw8", "ecs8", "SKS8", "dhw8", "TÚV8", "TUV8") MAKE_WORD_TRANSLATION(tag_dhw8, "dhw8", "WWK8", "dhw8", "VVK8", "CWU8", "dhw8", "ecs8", "SKS8", "dhw8", "TÚV8", "TUV8")
MAKE_WORD_TRANSLATION(tag_dhw9, "dhw9", "WWK9", "dhw9", "VVK9", "CWU9", "dhw9", "ecs9", "SKS9", "dhw9", "TÚV9", "TUV9") MAKE_WORD_TRANSLATION(tag_dhw9, "dhw9", "WWK9", "dhw9", "VVK9", "CWU9", "dhw9", "ecs9", "SKS9", "dhw9", "TÚV9", "TUV9")
MAKE_WORD_TRANSLATION(tag_dhw10, "dhw10", "WWK10", "dhw10", "VVK10", "CWU10", "dhw10", "ecs10", "SKS10", "dhw10", "TÚV10", "TUV10") MAKE_WORD_TRANSLATION(tag_dhw10, "dhw10", "WWK10", "dhw10", "VVK10", "CWU10", "dhw10", "ecs10", "SKS10", "dhw10", "TÚV10", "TUV10")
// heatingsources // heatingsources
MAKE_WORD_TRANSLATION(tag_ahs1, "ahs1", "AHQ1", "ahs1", "AVK1", "AŹC1", "ahs1", "ahs1", "ahs1", "ahs1", "ahs1", "ahs1") MAKE_WORD_TRANSLATION(tag_ahs1, "ahs1", "AHQ1", "ahs1", "AVK1", "AŹC1", "ahs1", "ahs1", "ahs1", "ahs1", "ahs1", "ahs1")
MAKE_WORD_TRANSLATION(tag_hs1, "hs1", "HQ1", "hs1", "VK1", "ŹC1", "hs1", "hs1", "hs1", "hs1", "hs1", "hs1") MAKE_WORD_TRANSLATION(tag_hs1, "hs1", "HQ1", "hs1", "VK1", "ŹC1", "hs1", "hs1", "hs1", "hs1", "hs1", "hs1")
@@ -122,6 +124,7 @@ MAKE_WORD_TRANSLATION(tag_hs13, "hs13", "HQ13", "hs13", "VK13", "ŹC13", "hs13",
MAKE_WORD_TRANSLATION(tag_hs14, "hs14", "HQ14", "hs14", "VK14", "ŹC14", "hs14", "hs14", "hs14", "hs14", "hs14", "hs14") MAKE_WORD_TRANSLATION(tag_hs14, "hs14", "HQ14", "hs14", "VK14", "ŹC14", "hs14", "hs14", "hs14", "hs14", "hs14", "hs14")
MAKE_WORD_TRANSLATION(tag_hs15, "hs15", "HQ15", "hs15", "VK15", "ŹC15", "hs15", "hs15", "hs15", "hs15", "hs15", "hs15") MAKE_WORD_TRANSLATION(tag_hs15, "hs15", "HQ15", "hs15", "VK15", "ŹC15", "hs15", "hs15", "hs15", "hs15", "hs15", "hs15")
MAKE_WORD_TRANSLATION(tag_hs16, "hs16", "HQ16", "hs16", "VK16", "ŹC16", "hs16", "hs16", "hs16", "hs16", "hs16", "hs16") MAKE_WORD_TRANSLATION(tag_hs16, "hs16", "HQ16", "hs16", "VK16", "ŹC16", "hs16", "hs16", "hs16", "hs16", "hs16", "hs16")
// single room thermostats // single room thermostats
MAKE_WORD_TRANSLATION(tag_src1, "src1", "SRC1") MAKE_WORD_TRANSLATION(tag_src1, "src1", "SRC1")
MAKE_WORD_TRANSLATION(tag_src2, "src2", "SRC2") MAKE_WORD_TRANSLATION(tag_src2, "src2", "SRC2")
@@ -426,11 +429,9 @@ MAKE_TRANSLATION(pumpCharacter, "pumpcharacter", "boiler pump characteristic", "
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", "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(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")
MAKE_TRANSLATION(curveOn, "curveon", "heatingcurve on", "Heizkurve an", "stookkromme aan", "Värmekurva På", "krzywa grzewcza włączona", "varmekurve på", "courbe de chauffage activée", "ısıtma eğrisi açık", "curva di riscaldamento attiva", "vykurovacia krivka zapnutá", "topná křivka zapnutá") MAKE_TRANSLATION(curveOn, "curveon", "heatingcurve on", "Heizkurve an", "stookkromme aan", "Värmekurva På", "krzywa grzewcza włączona", "varmekurve på", "courbe de chauffage activée", "ısıtma eğrisi açık", "curva di riscaldamento attiva", "vykurovacia krivka zapnutá", "topná křivka zapnutá")
MAKE_TRANSLATION(curveBase, "curvebase", "heatingcurve base", "Heizkurve Basis", "stookkromme basis", "Värmekurva Bas", "podstawa krzywej grzewczej", "varmekurve basis", "base de courbe de chauffage", "ısıtma eğrisi tabanı", "base curva di riscaldamento", "základňa vykurovacej krivky", "základ topné křivky") MAKE_TRANSLATION(curveBase, "curvebase", "heatingcurve base", "Heizkurve Basis", "stookkromme basis", "Värmekurva Bas", "podstawa krzywej grzewczej", "varmekurve basis", "base de courbe de chauffage", "ısıtma eğrisi tabanı", "base curva di riscaldamento", "základňa vykurovacej krivky", "základ topné křivky")
MAKE_TRANSLATION(curveEnd, "curveend", "heatingcurve end", "Heizkurve Ende", "stookkromme einde", "Värmekurva Slut", "koniec krzywej grzewczej", "varmekurve slutt", "fin de courbe de chauffage", "ısıtma eğrisi sonu", "fine curva di riscaldamento", "koniec vykurovacej krivky", "konec topné křivky") MAKE_TRANSLATION(curveEnd, "curveend", "heatingcurve end", "Heizkurve Ende", "stookkromme einde", "Värmekurva Slut", "koniec krzywej grzewczej", "varmekurve slutt", "fin de courbe de chauffage", "ısıtma eğrisi sonu", "fine curva di riscaldamento", "koniec vykurovacej krivky", "konec topné křivky")
@@ -478,7 +479,6 @@ MAKE_TRANSLATION(hpActivity, "hpactivity", "compressor activity", "Kompressorakt
MAKE_TRANSLATION(hpTargetSpd, "hptargetspd", "compressor target speed", "Kompressorsolldrehzahl", "", "", "", "", "", "", "", "kompresor cieľové otáčky", "cílová rychlost kompresoru") MAKE_TRANSLATION(hpTargetSpd, "hptargetspd", "compressor target speed", "Kompressorsolldrehzahl", "", "", "", "", "", "", "", "kompresor cieľové otáčky", "cílová rychlost kompresoru")
MAKE_TRANSLATION(receiverValveVr0, "recvalve", "receiver valve VR0", "Eingangsventil VR0", "", "", "", "", "", "", "", "prijímač ventil VR0", "přijímací ventil VR0") MAKE_TRANSLATION(receiverValveVr0, "recvalve", "receiver valve VR0", "Eingangsventil VR0", "", "", "", "", "", "", "", "prijímač ventil VR0", "přijímací ventil VR0")
MAKE_TRANSLATION(expansionValveVr1, "expvalve", "expansion valve VR1", "Ausdehnungsventil VR1", "", "", "", "", "", "", "", "prijímač ventil VR1", "přijímací ventil VR1") MAKE_TRANSLATION(expansionValveVr1, "expvalve", "expansion valve VR1", "Ausdehnungsventil VR1", "", "", "", "", "", "", "", "prijímač ventil VR1", "přijímací ventil VR1")
MAKE_TRANSLATION(hpMaxPower, "hpmaxpower", "compressor max power", "max. Kompressorleistung", "", "Max. Kompressoreffekt", "maksymalna wydajność sprężarki", "", "", "", "", "max výkon kompresora", "maximální výkon kompresoru") MAKE_TRANSLATION(hpMaxPower, "hpmaxpower", "compressor max power", "max. Kompressorleistung", "", "Max. Kompressoreffekt", "maksymalna wydajność sprężarki", "", "", "", "", "max výkon kompresora", "maximální výkon kompresoru")
MAKE_TRANSLATION(pvMaxComp, "pvmaxcomp", "pv compressor max power", "PV max. Kompressorleistung", "", "PV Max. Kompressoreffekt", "maksymalna wydajność sprężarki", "", "", "", "", "pv max výkon kompresora", "maximální výkon PV kompresoru") MAKE_TRANSLATION(pvMaxComp, "pvmaxcomp", "pv compressor max power", "PV max. Kompressorleistung", "", "PV Max. Kompressoreffekt", "maksymalna wydajność sprężarki", "", "", "", "", "pv max výkon kompresora", "maximální výkon PV kompresoru")
MAKE_TRANSLATION(hpPower, "hppower", "compressor power output", "Kompressorleistung", "Compressorvermogen", "Kompressoreffekt", "moc wyjściowa sprężarki", "kompressoreffekt", "puissance de sortie compresseur", "ısı pompası güç çıkışı", "potenza uscita compressore", "výkon kompresora", "výstupní výkon kompresoru") MAKE_TRANSLATION(hpPower, "hppower", "compressor power output", "Kompressorleistung", "Compressorvermogen", "Kompressoreffekt", "moc wyjściowa sprężarki", "kompressoreffekt", "puissance de sortie compresseur", "ısı pompası güç çıkışı", "potenza uscita compressore", "výkon kompresora", "výstupní výkon kompresoru")
@@ -496,7 +496,6 @@ MAKE_TRANSLATION(hpPl1, "hppl1", "low pressure side temperature (PL1)", "Niederd
MAKE_TRANSLATION(hpPh1, "hpph1", "high pressure side temperature (PH1)", "Hochdrucktemperatur (PH1)", "Temperatuur hoge drukzijde (PH1)", "Temperatur Högtryckssidan (PH1)", "temperatura po stronie wysokiego ciśnienia (PH1)", "Temperatur Høytrykksiden (PH1)", "température côté bhauteasse pression (PH1)", "yüksek basınç tarafı sıcaklığı (PH1)", "temperatura lato alta pressione (PH1)", "teplota na strane vysokého tlaku (PH1)", "teplota na vysokotlaké straně (PH1)") MAKE_TRANSLATION(hpPh1, "hpph1", "high pressure side temperature (PH1)", "Hochdrucktemperatur (PH1)", "Temperatuur hoge drukzijde (PH1)", "Temperatur Högtryckssidan (PH1)", "temperatura po stronie wysokiego ciśnienia (PH1)", "Temperatur Høytrykksiden (PH1)", "température côté bhauteasse pression (PH1)", "yüksek basınç tarafı sıcaklığı (PH1)", "temperatura lato alta pressione (PH1)", "teplota na strane vysokého tlaku (PH1)", "teplota na vysokotlaké straně (PH1)")
MAKE_TRANSLATION(hpTa4, "hpta4", "drain pan temp (TA4)", "Kondensatorwanne (TA4)", "Temperatuur condensorafvoerbak (TA4)", " (TA4)", "temperatura ociekacza (TA4)", "kondenstråg temperatur (TA4)", " (TA4)", "tahliye sıcaklığı (TA4)", "temperatura condensatore (TA4)", "teplota vypúšťacej misky (TA4)", "teplota odvodňovací vany (TA4)") MAKE_TRANSLATION(hpTa4, "hpta4", "drain pan temp (TA4)", "Kondensatorwanne (TA4)", "Temperatuur condensorafvoerbak (TA4)", " (TA4)", "temperatura ociekacza (TA4)", "kondenstråg temperatur (TA4)", " (TA4)", "tahliye sıcaklığı (TA4)", "temperatura condensatore (TA4)", "teplota vypúšťacej misky (TA4)", "teplota odvodňovací vany (TA4)")
MAKE_TRANSLATION(hpTw1, "hptw1", "reservoir temp (TW1)", "DHW Reservoir (TW1)", "(TW1)", "Varmvattentank Temperatur (TW1)", "temperatura zbiornika (TW1)", "(TW1)", "(TW1)", "(TW1)", "(TW1)", "teplota zásobníka (TW1)", "teplota v nádrži (TW1)") MAKE_TRANSLATION(hpTw1, "hptw1", "reservoir temp (TW1)", "DHW Reservoir (TW1)", "(TW1)", "Varmvattentank Temperatur (TW1)", "temperatura zbiornika (TW1)", "(TW1)", "(TW1)", "(TW1)", "(TW1)", "teplota zásobníka (TW1)", "teplota v nádrži (TW1)")
MAKE_TRANSLATION(hpInput1, "hpin1", "input 1 state", "Status Eingang 1", "Status input 1", "Status Ingång 1", "stan wejścia 1", "status inggang 1", "état entrée 1", "giriş 1 durumu", "stato ingresso 1", "stav vstupu 1", "stav vstupu 1") MAKE_TRANSLATION(hpInput1, "hpin1", "input 1 state", "Status Eingang 1", "Status input 1", "Status Ingång 1", "stan wejścia 1", "status inggang 1", "état entrée 1", "giriş 1 durumu", "stato ingresso 1", "stav vstupu 1", "stav vstupu 1")
MAKE_TRANSLATION(hpInput2, "hpin2", "input 2 state", "Status Eingang 2", "Status input 2", "Status Ingång 2", "stan wejścia 2", "status inggang 2", "état entrée 2", "giriş 2 durumu", "stato ingresso 2", "stav vstupu 2", "stav vstupu 2") MAKE_TRANSLATION(hpInput2, "hpin2", "input 2 state", "Status Eingang 2", "Status input 2", "Status Ingång 2", "stan wejścia 2", "status inggang 2", "état entrée 2", "giriş 2 durumu", "stato ingresso 2", "stav vstupu 2", "stav vstupu 2")
MAKE_TRANSLATION(hpInput3, "hpin3", "input 3 state", "Status Eingang 3", "Status input 3", "Status Ingång 3", "stan wejścia 3", "status inggang 3", "état entrée 3", "giriş 3 durumu", "stato ingresso 3", "stav vstupu 3", "stav vstupu 3") MAKE_TRANSLATION(hpInput3, "hpin3", "input 3 state", "Status Eingang 3", "Status input 3", "Status Ingång 3", "stan wejścia 3", "status inggang 3", "état entrée 3", "giriş 3 durumu", "stato ingresso 3", "stav vstupu 3", "stav vstupu 3")
@@ -508,7 +507,6 @@ MAKE_TRANSLATION(hpIn4Opt, "hpin4opt", "input 4 options", "Einstellung Eingang 4
MAKE_TRANSLATION(maxHeatComp, "maxheatcomp", "heat limit compressor", "Heizstab Limit mit Kompressor", "heat limit compressor", "Max. Värmegräns Kompressor", "ograniczenie mocy sprężarki", "max varmegrense kompressor", "limite chaleur compresseur", "ısı pompası ısıtma sınırı", "limite riscaldamento compressore", "tepelný limit kompresora", "tepelný limit kompresoru") MAKE_TRANSLATION(maxHeatComp, "maxheatcomp", "heat limit compressor", "Heizstab Limit mit Kompressor", "heat limit compressor", "Max. Värmegräns Kompressor", "ograniczenie mocy sprężarki", "max varmegrense kompressor", "limite chaleur compresseur", "ısı pompası ısıtma sınırı", "limite riscaldamento compressore", "tepelný limit kompresora", "tepelný limit kompresoru")
MAKE_TRANSLATION(maxHeatHeat, "maxheatheat", "heat limit heating", "Heizstab Limit Leistung", "Max, Värmegräns Uppvärmning", "heat limit heating", "ograniczenie mocy w trybie ogrzewania", "maks varmegrense oppvarming", "limite chaleur chauffage", "ısınma ısıtma sınırı", "limite calore riscaldamento", "vyhrievanie limitu tepla", "tepelný limit topení") MAKE_TRANSLATION(maxHeatHeat, "maxheatheat", "heat limit heating", "Heizstab Limit Leistung", "Max, Värmegräns Uppvärmning", "heat limit heating", "ograniczenie mocy w trybie ogrzewania", "maks varmegrense oppvarming", "limite chaleur chauffage", "ısınma ısıtma sınırı", "limite calore riscaldamento", "vyhrievanie limitu tepla", "tepelný limit topení")
MAKE_TRANSLATION(maxHeatDhw, "maxheat", "heat limit", "Heizstab Limit für WW", "heat limit", "Max. Värmegräns Varmvatten", "ograniczenie mocy w trybie c.w.u.", "varmegrense", "limite chaleur", "sıcak kullanım suyu ısınma sınırı", "limite calore", "tepelný limit", "tepelný limit") MAKE_TRANSLATION(maxHeatDhw, "maxheat", "heat limit", "Heizstab Limit für WW", "heat limit", "Max. Värmegräns Varmvatten", "ograniczenie mocy w trybie c.w.u.", "varmegrense", "limite chaleur", "sıcak kullanım suyu ısınma sınırı", "limite calore", "tepelný limit", "tepelný limit")
MAKE_TRANSLATION(auxHeaterOff, "auxheateroff", "disable aux heater", "Zusatzheizer deaktivieren", "Bijverwarming uitsc", "Blockera eltillskott", "wyłącz dogrzewacz", "deaktiver tilleggsvarme", "Désactiver chauff. d'app", "ilave ısıtıcıyı kapat", "disattivare i riscaldatori addizionali", "vypnúť pomocný ohrievač", "zakázat pomocné topení") MAKE_TRANSLATION(auxHeaterOff, "auxheateroff", "disable aux heater", "Zusatzheizer deaktivieren", "Bijverwarming uitsc", "Blockera eltillskott", "wyłącz dogrzewacz", "deaktiver tilleggsvarme", "Désactiver chauff. d'app", "ilave ısıtıcıyı kapat", "disattivare i riscaldatori addizionali", "vypnúť pomocný ohrievač", "zakázat pomocné topení")
MAKE_TRANSLATION(auxHeaterStatus, "auxheaterstatus", "aux heater status", "Zusatzheizerstatus", "Bijverwarming", "Eltillskott Status", "status dogrzewacza", "status el. tillegsvarme", "Chauffage auxiliaire", "ilave ısıtıcı durumu", "stato riscaldatori addizionali", "stav pomocného ohrievača", "stav pomocného topení") MAKE_TRANSLATION(auxHeaterStatus, "auxheaterstatus", "aux heater status", "Zusatzheizerstatus", "Bijverwarming", "Eltillskott Status", "status dogrzewacza", "status el. tillegsvarme", "Chauffage auxiliaire", "ilave ısıtıcı durumu", "stato riscaldatori addizionali", "stav pomocného ohrievača", "stav pomocného topení")
MAKE_TRANSLATION(auxHeaterLevel, "auxheaterlevel", "aux heater level", "Zusatzheizer", "Bijverwarming", "Eltillskott", "dogrzewacza", "el. tillegsvarme", "Chauffage auxiliaire", "ilave ısıtıcı durumu", "riscaldatori addizionali", "pomocného ohrievača", "pomocného topení") MAKE_TRANSLATION(auxHeaterLevel, "auxheaterlevel", "aux heater level", "Zusatzheizer", "Bijverwarming", "Eltillskott", "dogrzewacza", "el. tillegsvarme", "Chauffage auxiliaire", "ilave ısıtıcı durumu", "riscaldatori addizionali", "pomocného ohrievača", "pomocného topení")
@@ -525,7 +523,6 @@ MAKE_TRANSLATION(tempDiffHeat, "tempdiffheat", "temp diff TC3/TC0 heat", "Temp.d
MAKE_TRANSLATION(tempDiffCool, "tempdiffcool", "temp diff TC3/TC0 cool", "Temp.diff. TC3/TC0 Kühlen", "Temp.vers. TC3/TC0 koel.", "Temperaturskillnad TC3/TC0 Kyla", "różnica temperatur TC3/TC0 w trakcie chłodzenia", "temp. diff. TC3/TC0 kjøling", "Delta T TC3/TC0 Refroid.", "TC3-TC0 soğutma sıcaklık farkı", "Delta T raffreddamento TC3/TC0", "teplotný rozdiel TC3/TC0 chladenie", "rozdíl teplot TC3/TC0 pro chlazení") MAKE_TRANSLATION(tempDiffCool, "tempdiffcool", "temp diff TC3/TC0 cool", "Temp.diff. TC3/TC0 Kühlen", "Temp.vers. TC3/TC0 koel.", "Temperaturskillnad TC3/TC0 Kyla", "różnica temperatur TC3/TC0 w trakcie chłodzenia", "temp. diff. TC3/TC0 kjøling", "Delta T TC3/TC0 Refroid.", "TC3-TC0 soğutma sıcaklık farkı", "Delta T raffreddamento TC3/TC0", "teplotný rozdiel TC3/TC0 chladenie", "rozdíl teplot TC3/TC0 pro chlazení")
MAKE_TRANSLATION(silentFrom, "silentfrom", "silent mode from", "Silentmodus Start", "Start stille modus", "Tyst drift starttid", "początek trybu cichego", "stillemodus starter", "", "sessiz mod başlangıcı", "avvio della modalità silenziosa", "tichý režim od", "tichý režim od") MAKE_TRANSLATION(silentFrom, "silentfrom", "silent mode from", "Silentmodus Start", "Start stille modus", "Tyst drift starttid", "początek trybu cichego", "stillemodus starter", "", "sessiz mod başlangıcı", "avvio della modalità silenziosa", "tichý režim od", "tichý režim od")
MAKE_TRANSLATION(silentTo, "silentto", "silent mode to", "Silentmodus Ende", "Einde stille modus", "Tyst drift stopptid", "koniec trybu cichego", "komfortmodus av", "", "sessiz mod bitişi", "spegnere modalità silenziosa", "tichý režim do", "tichý režim do") MAKE_TRANSLATION(silentTo, "silentto", "silent mode to", "Silentmodus Ende", "Einde stille modus", "Tyst drift stopptid", "koniec trybu cichego", "komfortmodus av", "", "sessiz mod bitişi", "spegnere modalità silenziosa", "tichý režim do", "tichý režim do")
MAKE_TRANSLATION(wwComfOffTemp, "comfoff", "comfort switch off", "Komfort Ausschalttemp.", "Comfort Uitschakeltemp.", "Komfort frånkopplingstemperatur", "temperatura wyłączania w trybie komfort", "eco modus utkoblingstem", "Confort Temp. d'arrêt", "konfor kapalı", "spegnimento modalità comfort", "komfortné vypnutie", "komfortní vypnutí") MAKE_TRANSLATION(wwComfOffTemp, "comfoff", "comfort switch off", "Komfort Ausschalttemp.", "Comfort Uitschakeltemp.", "Komfort frånkopplingstemperatur", "temperatura wyłączania w trybie komfort", "eco modus utkoblingstem", "Confort Temp. d'arrêt", "konfor kapalı", "spegnimento modalità comfort", "komfortné vypnutie", "komfortní vypnutí")
MAKE_TRANSLATION(wwEcoOffTemp, "ecooff", "eco switch off", "ECO Ausschalttemp.", "Eco Uitschakeltemp.", "Eko frånkopplingstemperatur", "temperatura wyłączania w trybie eko", "Øko avstengningstemp.", "Eco Temp. d'arrêt", "eko kapalı", "spegnimento modalità ECO", "eko vypínač", "eko vypnutí") MAKE_TRANSLATION(wwEcoOffTemp, "ecooff", "eco switch off", "ECO Ausschalttemp.", "Eco Uitschakeltemp.", "Eko frånkopplingstemperatur", "temperatura wyłączania w trybie eko", "Øko avstengningstemp.", "Eco Temp. d'arrêt", "eko kapalı", "spegnimento modalità ECO", "eko vypínač", "eko vypnutí")
MAKE_TRANSLATION(wwEcoPlusOffTemp, "ecoplusoff", "eco+ switch off", "ECO+ Ausschalttemp.", "Eco+ Uitschakeltemp.", "Eko+ frånkopplingstemperatur", "temperatura wyłączania w trybie eko+", "Øko+ avstengningstemp.", "Eco+ Temp. d'arrêt", "eko+ kapalı", "spegnimento modalità ECO+", "eko+ vypnutie", "eko+ vypnutí") MAKE_TRANSLATION(wwEcoPlusOffTemp, "ecoplusoff", "eco+ switch off", "ECO+ Ausschalttemp.", "Eco+ Uitschakeltemp.", "Eko+ frånkopplingstemperatur", "temperatura wyłączania w trybie eko+", "Øko+ avstengningstemp.", "Eco+ Temp. d'arrêt", "eko+ kapalı", "spegnimento modalità ECO+", "eko+ vypnutie", "eko+ vypnutí")
@@ -535,7 +532,6 @@ MAKE_TRANSLATION(wwEcoPlusDiffTemp, "ecoplusdiff", "eco+ diff", "ECO+ Differenzt
MAKE_TRANSLATION(wwComfStopTemp, "comfstop", "comfort stop temp", "Komfort Stopptemp.", "", "Komfort stopptemperatur", "", "", "", "", "", "komfortná stop teplota", "komfortní teplota vypnutí") MAKE_TRANSLATION(wwComfStopTemp, "comfstop", "comfort stop temp", "Komfort Stopptemp.", "", "Komfort stopptemperatur", "", "", "", "", "", "komfortná stop teplota", "komfortní teplota vypnutí")
MAKE_TRANSLATION(wwEcoStopTemp, "ecostop", "eco stop temp", "ECO Stopptemp.", "", "Eko stopptemperatur", "", "", "", "", "", "ECO stop teplota", "eko teplota vypnutí") MAKE_TRANSLATION(wwEcoStopTemp, "ecostop", "eco stop temp", "ECO Stopptemp.", "", "Eko stopptemperatur", "", "", "", "", "", "ECO stop teplota", "eko teplota vypnutí")
MAKE_TRANSLATION(wwEcoPlusStopTemp, "ecoplusstop", "eco+ stop temp", "ECO+ Stopptemp.", "", "Eko+ stopptemperatur", "", "", "", "", "", "ECO+ stop teplota", "eko+ teplota vypnutí") MAKE_TRANSLATION(wwEcoPlusStopTemp, "ecoplusstop", "eco+ stop temp", "ECO+ Stopptemp.", "", "Eko+ stopptemperatur", "", "", "", "", "", "ECO+ stop teplota", "eko+ teplota vypnutí")
MAKE_TRANSLATION(auxHeatMode, "auxheatrmode", "aux heater mode", "Zusatzheizungsmodus", "Modus bijverwarmer", "Eltillskott Läge", "tryb pracy dogrzewacza po blokadzie z Zakładu Energetycznego", "tilleggsvarmer modus", "", "ilave ısıtıcı modu", "modalità riscaldatore addizionale", "režim pomocného ohrievača", "režim pomocného topení") MAKE_TRANSLATION(auxHeatMode, "auxheatrmode", "aux heater mode", "Zusatzheizungsmodus", "Modus bijverwarmer", "Eltillskott Läge", "tryb pracy dogrzewacza po blokadzie z Zakładu Energetycznego", "tilleggsvarmer modus", "", "ilave ısıtıcı modu", "modalità riscaldatore addizionale", "režim pomocného ohrievača", "režim pomocného topení")
MAKE_TRANSLATION(auxMaxLimit, "auxmaxlimit", "aux heater max limit", "Zusatzheizer max. Grenze", "Bijverwarmer grensinstelling maximaal", "Eltillskott max begränsning", "dogrzewacz, maksymalny limit", "tillegsvarme maksgrense", "ilave ısıtıcı maks limit", "limite massimo riscaldatore addizionale", "maximálny limit pomocného ohrievača", "maximální limit pomocného topení") MAKE_TRANSLATION(auxMaxLimit, "auxmaxlimit", "aux heater max limit", "Zusatzheizer max. Grenze", "Bijverwarmer grensinstelling maximaal", "Eltillskott max begränsning", "dogrzewacz, maksymalny limit", "tillegsvarme maksgrense", "ilave ısıtıcı maks limit", "limite massimo riscaldatore addizionale", "maximálny limit pomocného ohrievača", "maximální limit pomocného topení")
MAKE_TRANSLATION(auxLimitStart, "auxlimitstart", "aux heater limit start", "Zusatzheizer Grenze Start", "Bijverwarmer grens voor start", "Eltillskott begränsningsstart", "dogrzewacz, początek ograniczenia", "tillegsvarme startgrense", "ilave ısıtıcı limir başlangıcı", "avvio limite massimo riscaldatore addizionale", "spustenie limitu pomocného ohrievača", "startovací limit pomocného topení") MAKE_TRANSLATION(auxLimitStart, "auxlimitstart", "aux heater limit start", "Zusatzheizer Grenze Start", "Bijverwarmer grens voor start", "Eltillskott begränsningsstart", "dogrzewacz, początek ograniczenia", "tillegsvarme startgrense", "ilave ısıtıcı limir başlangıcı", "avvio limite massimo riscaldatore addizionale", "spustenie limitu pomocného ohrievača", "startovací limit pomocného topení")
@@ -832,7 +828,6 @@ MAKE_TRANSLATION(absent, "absent", "absent", "Abwesend", "", "Frånvarande", "",
MAKE_TRANSLATION(redthreshold, "redthreshold", "reduction threshold", "Absenkschwelle", "", "Tröskel för sänkning", "", "", "", "", "", "zníženie tresholdu", "práh snížení") MAKE_TRANSLATION(redthreshold, "redthreshold", "reduction threshold", "Absenkschwelle", "", "Tröskel för sänkning", "", "", "", "", "", "zníženie tresholdu", "práh snížení")
MAKE_TRANSLATION(solarinfl, "solarinfl", "solar influence", "Solareinfluß", "", "", "", "", "", "", "", "slnečný vplyv", "sluneční vliv") MAKE_TRANSLATION(solarinfl, "solarinfl", "solar influence", "Solareinfluß", "", "", "", "", "", "", "", "slnečný vplyv", "sluneční vliv")
MAKE_TRANSLATION(currsolarinfl, "currsolarinfl", "current solar influence", "akt. Solareinfluß", "", "", "", "", "", "", "", "aktuálny slnečný vplyv", "aktuální sluneční vliv") MAKE_TRANSLATION(currsolarinfl, "currsolarinfl", "current solar influence", "akt. Solareinfluß", "", "", "", "", "", "", "", "aktuálny slnečný vplyv", "aktuální sluneční vliv")
MAKE_TRANSLATION(hpmode, "hpmode", "HP Mode", "WP-Modus", "Modus warmtepomp", "Värmepumpsläge", "tryb pracy pompy ciepła", "", "", "yüksek güç modu", "Modalità Termopompa", "Režim TČ", "režim tepelného čerpadla") MAKE_TRANSLATION(hpmode, "hpmode", "HP Mode", "WP-Modus", "Modus warmtepomp", "Värmepumpsläge", "tryb pracy pompy ciepła", "", "", "yüksek güç modu", "Modalità Termopompa", "Režim TČ", "režim tepelného čerpadla")
MAKE_TRANSLATION(dewoffset, "dewoffset", "dew point offset", "Taupunktdifferenz", "Offset dauwpunt", "Daggpunktsförskjutning", "przesunięcie punktu rosy", "", "", "çiğ noktası göreli", "differenza del punto di rugiada", "posun rosného bodu", "offset rosného bodu") MAKE_TRANSLATION(dewoffset, "dewoffset", "dew point offset", "Taupunktdifferenz", "Offset dauwpunt", "Daggpunktsförskjutning", "przesunięcie punktu rosy", "", "", "çiğ noktası göreli", "differenza del punto di rugiada", "posun rosného bodu", "offset rosného bodu")
MAKE_TRANSLATION(roomtempdiff, "roomtempdiff", "room temp difference", "Raumtemperaturdifferenz", "Verschiltemperatuur kamertemp", "Rumstemperaturskillnad", "różnica temp. pomieszczenia", "", "", "oda sıcaklığı farkı", "differenza temperatura ambiente", "rozdiel izbovej teploty", "rozdíl teploty místnosti") MAKE_TRANSLATION(roomtempdiff, "roomtempdiff", "room temp difference", "Raumtemperaturdifferenz", "Verschiltemperatuur kamertemp", "Rumstemperaturskillnad", "różnica temp. pomieszczenia", "", "", "oda sıcaklığı farkı", "differenza temperatura ambiente", "rozdiel izbovej teploty", "rozdíl teploty místnosti")

View File

@@ -52,7 +52,7 @@ void Modbus::stop() {
#ifndef EMSESP_STANDALONE #ifndef EMSESP_STANDALONE
modbusServer_->stop(); modbusServer_->stop();
// virtual ~ModbusServerTCPasync() = default; // virtual ~ModbusServerTCPasync() = default;
// delete modbusServer_; delete modbusServer_;
modbusServer_ = nullptr; modbusServer_ = nullptr;
#endif #endif
} }

View File

@@ -1184,7 +1184,8 @@ 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] = "temperature"; doc[dc_ha] = "temperature";
// override uom if fahrenheit // override uom if fahrenheit
doc[uom_ha] = EMSESP::system_.fahrenheit() ? DeviceValue::DeviceValueUOM_s[DeviceValueUOM::FAHRENHEIT] : DeviceValue::DeviceValueUOM_s[uom]; doc[uom_ha] = EMSESP::system_.fahrenheit() && uom != DeviceValueUOM::K ? DeviceValue::DeviceValueUOM_s[DeviceValueUOM::FAHRENHEIT]
: DeviceValue::DeviceValueUOM_s[uom];
break; break;
case DeviceValueUOM::PERCENT: case DeviceValueUOM::PERCENT:
if (display_only) { if (display_only) {

View File

@@ -20,8 +20,13 @@
#include "emsesp.h" // for send_raw_telegram() command #include "emsesp.h" // for send_raw_telegram() command
#ifndef EMSESP_STANDALONE #ifndef EMSESP_STANDALONE
#include "esp_image_format.h"
#include "esp_ota_ops.h" #include "esp_ota_ops.h"
#include "esp_partition.h" #include "esp_partition.h"
#include <esp_mac.h>
#include "esp_efuse.h"
#include <nvs.h>
#include <mbedtls/base64.h>
#endif #endif
#include <HTTPClient.h> #include <HTTPClient.h>
@@ -33,11 +38,6 @@
#include "../test/test.h" #include "../test/test.h"
#endif #endif
#ifndef EMSESP_STANDALONE
#include <esp_mac.h>
#include "esp_efuse.h"
#endif
namespace emsesp { namespace emsesp {
// Languages supported. Note: the order is important // Languages supported. Note: the order is important
@@ -338,7 +338,13 @@ void System::get_partition_info() {
strftime(time_string, sizeof(time_string), "%FT%T", localtime(&d)); strftime(time_string, sizeof(time_string), "%FT%T", localtime(&d));
p_info.install_date = d > 1500000000L ? time_string : ""; p_info.install_date = d > 1500000000L ? time_string : "";
p_info.size = part->size / 1024; // set size in KB esp_image_metadata_t meta = {};
esp_partition_pos_t part_pos = {.offset = part->address, .size = part->size};
if (esp_image_verify(ESP_IMAGE_VERIFY_SILENT, &part_pos, &meta) == ESP_OK) {
p_info.size = meta.image_len / 1024; // actual firmware size in KB
} else {
p_info.size = 0;
}
partition_info_[part->label] = p_info; partition_info_[part->label] = p_info;
} }
@@ -362,7 +368,7 @@ void System::set_partition_install_date() {
snprintf(c, sizeof(c), "d_%s", current_partition); snprintf(c, sizeof(c), "d_%s", current_partition);
time_t d = EMSESP::nvs_.getULong(c, 0); time_t d = EMSESP::nvs_.getULong(c, 0);
if (d < 1500000000L) { if (d < 1500000000L) {
LOG_INFO("Firmware is fresh, setting the new install date in partition %s", current_partition); LOG_DEBUG("Setting the install date in partition %s", current_partition);
auto t = time(nullptr) - uuid::get_uptime_sec(); auto t = time(nullptr) - uuid::get_uptime_sec();
EMSESP::nvs_.putULong(c, t); EMSESP::nvs_.putULong(c, t);
} }
@@ -406,9 +412,9 @@ bool System::set_partition(const char * partitionname) {
// restart EMS-ESP // restart EMS-ESP
// app0 or app1, or boot/factory on 16MB boards // app0 or app1, or boot/factory on 16MB boards
void System::system_restart(const char * partitionname) { void System::system_restart(const char * partitionname) {
#ifndef EMSESP_STANDALONE
// see if we are forcing a partition to use // see if we are forcing a partition to use
if (partitionname != nullptr) { if (partitionname != nullptr) {
#ifndef EMSESP_STANDALONE
// Factory partition - label will be "factory" // Factory partition - label will be "factory"
const esp_partition_t * partition = esp_partition_find_first(ESP_PARTITION_TYPE_APP, ESP_PARTITION_SUBTYPE_APP_FACTORY, NULL); const esp_partition_t * partition = esp_partition_find_first(ESP_PARTITION_TYPE_APP, ESP_PARTITION_SUBTYPE_APP_FACTORY, NULL);
if (partition && !strcmp(partition->label, partitionname)) { if (partition && !strcmp(partition->label, partitionname)) {
@@ -439,27 +445,28 @@ void System::system_restart(const char * partitionname) {
// set the boot partition // set the boot partition
esp_ota_set_boot_partition(partition); esp_ota_set_boot_partition(partition);
} }
#endif
LOG_INFO("Restarting EMS-ESP from %s partition", partitionname); LOG_INFO("Restarting EMS-ESP from %s partition", partitionname);
} else { } else {
LOG_INFO("Restarting EMS-ESP..."); LOG_INFO("Restarting EMS-ESP...");
} }
// make sure it's only executed once
EMSESP::system_.systemStatus(SYSTEM_STATUS::SYSTEM_STATUS_NORMAL);
store_nvs_values(); // save any NVS values store_nvs_values(); // save any NVS values
Shell::loop_all(); // flush log to output
// flush all the log
EMSESP::webLogService.loop(); // dump all to web log
for (int i = 0; i < 10; i++) {
Shell::loop_all();
delay(10); // give telnet TCP stack time to transmit
}
Serial.flush(); // wait for hardware TX buffer to drain
Mqtt::disconnect(); // gracefully disconnect MQTT, needed for QOS1 Mqtt::disconnect(); // gracefully disconnect MQTT, needed for QOS1
EMSuart::stop(); // stop UART so there is no interference EMSuart::stop(); // stop UART so there is no interference
delay(1000); // wait 1 second
ESP.restart(); // ka-boom! #ifndef EMSESP_STANDALONE
#else delay(1000); // wait 1 second
EMSESP::system_.systemStatus(SYSTEM_STATUS::SYSTEM_STATUS_NORMAL); ESP.restart(); // ka-boom! - this is the only place where the ESP32 restart is called
if (partitionname != nullptr) {
LOG_INFO("Restarting EMS-ESP from %s partition", partitionname);
} else {
LOG_INFO("Restarting EMS-ESP...");
}
#endif #endif
} }
@@ -538,11 +545,6 @@ void System::store_settings(WebSettings & settings) {
board_profile_ = settings.board_profile; board_profile_ = settings.board_profile;
telnet_enabled_ = settings.telnet_enabled; telnet_enabled_ = settings.telnet_enabled;
modbus_enabled_ = settings.modbus_enabled;
modbus_port_ = settings.modbus_port;
modbus_max_clients_ = settings.modbus_max_clients;
modbus_timeout_ = settings.modbus_timeout;
tx_mode_ = settings.tx_mode; tx_mode_ = settings.tx_mode;
syslog_enabled_ = settings.syslog_enabled; syslog_enabled_ = settings.syslog_enabled;
syslog_level_ = settings.syslog_level; syslog_level_ = settings.syslog_level;
@@ -563,6 +565,25 @@ void System::store_settings(WebSettings & settings) {
locale_ = settings.locale; locale_ = settings.locale;
developer_mode_ = settings.developer_mode; developer_mode_ = settings.developer_mode;
// start services
if (settings.modbus_enabled) {
if (EMSESP::modbus_ == nullptr) {
EMSESP::modbus_ = new Modbus;
EMSESP::modbus_->start(1, settings.modbus_port, settings.modbus_max_clients, settings.modbus_timeout * 1000);
} else if (settings.modbus_port != modbus_port_ || settings.modbus_max_clients != modbus_max_clients_ || settings.modbus_timeout != modbus_timeout_) {
EMSESP::modbus_->stop();
EMSESP::modbus_->start(1, settings.modbus_port, settings.modbus_max_clients, settings.modbus_timeout * 1000);
}
} else if (EMSESP::modbus_ != nullptr) {
EMSESP::modbus_->stop();
delete EMSESP::modbus_;
EMSESP::modbus_ = nullptr;
}
modbus_enabled_ = settings.modbus_enabled;
modbus_port_ = settings.modbus_port;
modbus_max_clients_ = settings.modbus_max_clients;
modbus_timeout_ = settings.modbus_timeout;
} }
// Starts up core services // Starts up core services
@@ -1195,9 +1216,16 @@ void System::show_system(uuid::console::Shell & shell) {
} }
// GPIOs // GPIOs
shell.println(" GPIOs:"); shell.println(" GPIOs:");
shell.printf(" allowed:");
for (const auto & gpio : valid_system_gpios_) {
shell.printf(" %d", gpio);
}
shell.printfln(" [total %d]", valid_system_gpios_.size());
shell.printf(" in use:"); shell.printf(" in use:");
for (const auto & usage : used_gpios_) { auto sorted_gpios = used_gpios_;
shell.printf(" %d(%s)", usage.pin, usage.source.c_str()); std::sort(sorted_gpios.begin(), sorted_gpios.end(), [](const GpioUsage & a, const GpioUsage & b) { return a.pin < b.pin; });
for (const auto & gpio : sorted_gpios) {
shell.printf(" %d(%s)", gpio.pin, gpio.source.c_str());
} }
shell.printfln(" [total %d]", used_gpios_.size()); shell.printfln(" [total %d]", used_gpios_.size());
auto available = available_gpios(); auto available = available_gpios();
@@ -1216,7 +1244,7 @@ void System::show_system(uuid::console::Shell & shell) {
partition.first.c_str(), partition.first.c_str(),
partition.second.version.c_str(), partition.second.version.c_str(),
partition.second.size, partition.second.size,
partition.second.install_date.empty() ? "" : (std::string(", installed ") + partition.second.install_date).c_str(), partition.second.install_date.empty() ? "" : (std::string(", installed on ") + partition.second.install_date).c_str(),
(strcmp(esp_ota_get_running_partition()->label, partition.first.c_str()) == 0) ? "** active **" : ""); (strcmp(esp_ota_get_running_partition()->label, partition.first.c_str()) == 0) ? "** active **" : "");
} }
@@ -1335,23 +1363,134 @@ bool System::check_restore() {
JsonObject input = jsonDocument.as<JsonObject>(); JsonObject input = jsonDocument.as<JsonObject>();
// see what type of file it is, either settings or customization. anything else is ignored // see what type of file it is, either settings or customization. anything else is ignored
std::string settings_type = input["type"]; std::string settings_type = input["type"];
if (settings_type == "settings") { LOG_INFO("Restoring '%s' settings...", settings_type.c_str());
// It's a settings file. Parse each section separately. If it's system related it will require a reboot
reboot_required = saveSettings(NETWORK_SETTINGS_FILE, "Network", input); // system backup, which is a consolidated json object with all the settings files
reboot_required |= saveSettings(AP_SETTINGS_FILE, "AP", input); if (settings_type == "systembackup") {
reboot_required |= saveSettings(MQTT_SETTINGS_FILE, "MQTT", input); reboot_required = true;
reboot_required |= saveSettings(NTP_SETTINGS_FILE, "NTP", input); JsonArray sections = input["systembackup"].as<JsonArray>();
reboot_required |= saveSettings(SECURITY_SETTINGS_FILE, "Security", input); for (JsonObject section : sections) {
reboot_required |= saveSettings(EMSESP_SETTINGS_FILE, "Settings", input); std::string section_type = section["type"];
LOG_DEBUG("Restoring '%s' section...", section_type.c_str());
if (section_type == "settings") {
saveSettings(NETWORK_SETTINGS_FILE, section);
saveSettings(AP_SETTINGS_FILE, section);
saveSettings(MQTT_SETTINGS_FILE, section);
saveSettings(NTP_SETTINGS_FILE, section);
saveSettings(SECURITY_SETTINGS_FILE, section);
saveSettings(EMSESP_SETTINGS_FILE, section);
}
if (section_type == "schedule") {
saveSettings(EMSESP_SCHEDULER_FILE, section);
}
if (section_type == "customizations") {
saveSettings(EMSESP_CUSTOMIZATION_FILE, section);
}
if (section_type == "entities") {
saveSettings(EMSESP_CUSTOMENTITY_FILE, section);
}
if (section_type == "modules") {
saveSettings(EMSESP_MODULES_FILE, section);
}
if (section_type == "customSupport") {
// it's a custom support, extract json and write to /config/customSupport.json file
File customSupportFile = LittleFS.open(EMSESP_CUSTOMSUPPORT_FILE, "w");
if (customSupportFile) {
serializeJson(section, customSupportFile);
customSupportFile.close();
LOG_INFO("Custom support file updated");
} else {
LOG_ERROR("Failed to save custom support file");
}
}
if (section_type == "nvs") {
// Restore NVS values
JsonArray nvs_entries = section["nvs"].as<JsonArray>();
for (JsonObject entry : nvs_entries) {
std::string key = entry["key"] | "";
int type = entry["type"] | NVS_TYPE_ANY;
switch (type) {
case NVS_TYPE_I8:
if (entry["value"].is<JsonVariantConst>()) {
int8_t v = entry["value"];
EMSESP::nvs_.putChar(key.c_str(), v);
LOG_DEBUG("Restored NVS value: %s = %d", key.c_str(), v);
}
break;
case NVS_TYPE_U8:
if (entry["value"].is<JsonVariantConst>()) {
uint8_t v = entry["value"];
EMSESP::nvs_.putUChar(key.c_str(), v);
LOG_DEBUG("Restored NVS value: %s = %d", key.c_str(), v);
}
break;
case NVS_TYPE_I32:
if (entry["value"].is<JsonVariantConst>()) {
int32_t v = entry["value"];
EMSESP::nvs_.putInt(key.c_str(), v);
LOG_DEBUG("Restored NVS value: %s = %d", key.c_str(), v);
}
break;
case NVS_TYPE_U32:
if (entry["value"].is<JsonVariantConst>()) {
uint32_t v = entry["value"];
EMSESP::nvs_.putUInt(key.c_str(), v);
LOG_DEBUG("Restored NVS value: %s = %d", key.c_str(), v);
}
break;
case NVS_TYPE_I64:
if (entry["value"].is<JsonVariantConst>()) {
int64_t v = entry["value"];
EMSESP::nvs_.putLong64(key.c_str(), v);
LOG_DEBUG("Restored NVS value: %s = %d", key.c_str(), v);
}
break;
case NVS_TYPE_U64:
if (entry["value"].is<JsonVariantConst>()) {
uint64_t v = entry["value"];
EMSESP::nvs_.putULong64(key.c_str(), v);
LOG_DEBUG("Restored NVS value: %s = %d", key.c_str(), v);
}
break;
case NVS_TYPE_BLOB:
// used for double values
if (entry["value"].is<JsonVariantConst>()) {
double v = entry["value"];
EMSESP::nvs_.putDouble(key.c_str(), v);
LOG_DEBUG("Restored NVS value: %s = %d", key.c_str(), v);
}
break;
case NVS_TYPE_STR:
case NVS_TYPE_ANY:
default:
if (entry["value"].is<JsonVariantConst>()) {
std::string v = entry["value"];
EMSESP::nvs_.putString(key.c_str(), v.c_str());
LOG_DEBUG("Restored NVS value: %s = %s", key.c_str(), v.c_str());
}
break;
}
}
}
}
}
// It's a single settings file. Parse each section separately. If it's system related it will require a reboot
else if (settings_type == "settings") {
reboot_required = saveSettings(NETWORK_SETTINGS_FILE, input);
reboot_required |= saveSettings(AP_SETTINGS_FILE, input);
reboot_required |= saveSettings(MQTT_SETTINGS_FILE, input);
reboot_required |= saveSettings(NTP_SETTINGS_FILE, input);
reboot_required |= saveSettings(SECURITY_SETTINGS_FILE, input);
reboot_required |= saveSettings(EMSESP_SETTINGS_FILE, input);
} else if (settings_type == "customizations") { } else if (settings_type == "customizations") {
// it's a customization file, just replace it and there's no need to reboot saveSettings(EMSESP_CUSTOMIZATION_FILE, input);
saveSettings(EMSESP_CUSTOMIZATION_FILE, "Customizations", input);
} else if (settings_type == "schedule") { } else if (settings_type == "schedule") {
// it's a schedule file, just replace it and there's no need to reboot saveSettings(EMSESP_SCHEDULER_FILE, input);
saveSettings(EMSESP_SCHEDULER_FILE, "Schedule", input);
} else if (settings_type == "entities") { } else if (settings_type == "entities") {
// it's a entity file, just replace it and there's no need to reboot saveSettings(EMSESP_CUSTOMENTITY_FILE, input);
saveSettings(EMSESP_CUSTOMENTITY_FILE, "Entities", input);
} else if (settings_type == "customSupport") { } else if (settings_type == "customSupport") {
// it's a custom support file - save it to /config // it's a custom support file - save it to /config
new_file.close(); new_file.close();
@@ -1509,39 +1648,216 @@ bool System::check_upgrade() {
return false; // no reboot required return false; // no reboot required
} }
// convert settings file into json object // map each config filename to its human-readable section key
void System::extractSettings(const char * filename, const char * section, JsonObject output) { static const std::pair<const char *, const char *> SECTION_MAP[] = {
{NETWORK_SETTINGS_FILE, "Network"},
{AP_SETTINGS_FILE, "AP"},
{MQTT_SETTINGS_FILE, "MQTT"},
{NTP_SETTINGS_FILE, "NTP"},
{SECURITY_SETTINGS_FILE, "Security"},
{EMSESP_SETTINGS_FILE, "Settings"},
{EMSESP_SCHEDULER_FILE, "Schedule"},
{EMSESP_CUSTOMIZATION_FILE, "Customizations"},
{EMSESP_CUSTOMENTITY_FILE, "Entities"},
{EMSESP_MODULES_FILE, "Modules"},
};
// 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) {
if (type != "settings") {
output["type"] = type; // add the type to the output, not for settings as it's already added because its grouped
}
#ifndef EMSESP_STANDALONE #ifndef EMSESP_STANDALONE
const char * section = nullptr;
for (const auto & [f, label] : SECTION_MAP) {
if (strcmp(f, filename) == 0) {
section = label;
break;
}
}
if (!section) {
return;
}
File settingsFile = LittleFS.open(filename); File settingsFile = LittleFS.open(filename);
if (settingsFile) { if (settingsFile) {
JsonDocument jsonDocument; JsonDocument jsonDocument;
DeserializationError error = deserializeJson(jsonDocument, settingsFile); DeserializationError error = deserializeJson(jsonDocument, settingsFile);
if (error == DeserializationError::Ok && jsonDocument.is<JsonObject>()) { if (error == DeserializationError::Ok && jsonDocument.is<JsonObject>()) {
JsonObject jsonObject = jsonDocument.as<JsonObject>(); JsonObject node = output[section].to<JsonObject>();
JsonObject node = output[section].to<JsonObject>(); for (JsonPair kvp : jsonDocument.as<JsonObject>()) {
for (JsonPair kvp : jsonObject) {
node[kvp.key()] = kvp.value(); node[kvp.key()] = kvp.value();
} }
} else {
LOG_ERROR("Failed to deserialize settings file %s", filename);
} }
LOG_DEBUG("Exported %s settings from file %s", section, filename);
settingsFile.close();
} else {
LOG_ERROR("No settings file for %s found", filename);
} }
settingsFile.close();
#endif #endif
} }
// save settings file using input from a json object // full system backup of all settings files
bool System::saveSettings(const char * filename, const char * section, JsonObject input) { void System::exportSystemBackup(JsonObject output) {
output["type"] = "systembackup"; // add the type to the output
output["version"] = EMSESP_APP_VERSION; // add the version to the output
#ifndef EMSESP_STANDALONE #ifndef EMSESP_STANDALONE
// add date/time if NTP enabled and active
if ((esp_sntp_enabled()) && (EMSESP::system_.ntp_connected())) {
time_t now = time(nullptr);
if (now > 1500000000L) {
char t[25];
strftime(t, sizeof(t), "%FT%T", localtime(&now));
output["date"] = t;
}
}
#endif
// create an array of objects for each settings file
JsonArray nodes = output["systembackup"].to<JsonArray>();
// start with settings by grouping them together
JsonObject node = nodes.add<JsonObject>();
node["type"] = "settings"; // add type once for this group
exportSettings("settings", NETWORK_SETTINGS_FILE, node);
exportSettings("settings", AP_SETTINGS_FILE, node);
exportSettings("settings", MQTT_SETTINGS_FILE, node);
exportSettings("settings", NTP_SETTINGS_FILE, node);
exportSettings("settings", SECURITY_SETTINGS_FILE, node);
exportSettings("settings", EMSESP_SETTINGS_FILE, node);
node = nodes.add<JsonObject>();
exportSettings("schedule", EMSESP_SCHEDULER_FILE, node);
node = nodes.add<JsonObject>();
exportSettings("customizations", EMSESP_CUSTOMIZATION_FILE, node);
node = nodes.add<JsonObject>();
exportSettings("entities", EMSESP_CUSTOMENTITY_FILE, node);
node = nodes.add<JsonObject>();
exportSettings("modules", EMSESP_MODULES_FILE, node);
#ifndef EMSESP_STANDALONE
// special case for custom support
File file = LittleFS.open(EMSESP_CUSTOMSUPPORT_FILE, "r");
if (file) {
JsonDocument jsonDocument;
DeserializationError error = deserializeJson(jsonDocument, file);
if (error == DeserializationError::Ok && jsonDocument.is<JsonObject>()) {
JsonObject node = nodes.add<JsonObject>();
node["type"] = "customSupport";
node["data"] = jsonDocument.as<JsonObject>();
}
file.close();
LOG_DEBUG("Exported custom support file %s", EMSESP_CUSTOMSUPPORT_FILE);
}
// Backup NVS values
node = nodes.add<JsonObject>();
node["type"] = "nvs";
const char * nvs_part = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_NVS, "nvs1") ? "nvs1" : "nvs"; // nvs1 is on 16MBs
nvs_iterator_t it = nullptr;
#if ESP_IDF_VERSION_MAJOR < 5
it = nvs_entry_find(nvs_part, "ems-esp", NVS_TYPE_ANY);
if (it == nullptr) {
#else
esp_err_t err = nvs_entry_find(nvs_part, "ems-esp", NVS_TYPE_ANY, &it);
if (err != ESP_OK) {
#endif
LOG_ERROR("Failed to find NVS entry for %s", nvs_part);
return;
}
JsonArray entries = node["nvs"].to<JsonArray>();
#if ESP_IDF_VERSION_MAJOR < 5
while (it != nullptr) {
nvs_entry_info_t info;
nvs_entry_info(it, &info);
#else
while (err == ESP_OK) {
nvs_entry_info_t info;
nvs_entry_info(it, &info);
#endif
JsonObject entry = entries.add<JsonObject>();
entry["type"] = info.type;
entry["key"] = info.key;
LOG_DEBUG("Exporting NVS value: %s = %d", info.key, info.type);
switch (info.type) {
case NVS_TYPE_I8:
entry["value"] = EMSESP::nvs_.getChar(info.key);
break;
case NVS_TYPE_U8:
entry["value"] = EMSESP::nvs_.getUChar(info.key);
break;
case NVS_TYPE_I32:
entry["value"] = EMSESP::nvs_.getInt(info.key);
break;
case NVS_TYPE_U32:
entry["value"] = EMSESP::nvs_.getUInt(info.key);
break;
case NVS_TYPE_I64:
entry["value"] = EMSESP::nvs_.getLong64(info.key);
break;
case NVS_TYPE_U64:
entry["value"] = EMSESP::nvs_.getULong64(info.key);
break;
case NVS_TYPE_BLOB:
entry["value"] = EMSESP::nvs_.getDouble(info.key); // bytes used for double values
break;
case NVS_TYPE_STR:
case NVS_TYPE_ANY:
default:
entry["value"] = EMSESP::nvs_.getString(info.key);
break;
}
#if ESP_IDF_VERSION_MAJOR < 5
it = nvs_entry_next(it);
}
#else
err = nvs_entry_next(&it);
}
#endif
if (it != nullptr) {
nvs_release_iterator(it);
}
#endif
}
// write a settings file using input from a json object, called from upload/restore
bool System::saveSettings(const char * filename, JsonObject input) {
#ifndef EMSESP_STANDALONE
const char * section = nullptr;
for (const auto & [f, label] : SECTION_MAP) {
if (strcmp(f, filename) == 0) {
section = label;
break;
}
}
if (!section) {
return false;
}
JsonObject section_json = input[section]; JsonObject section_json = input[section];
if (section_json) { if (section_json) {
File section_file = LittleFS.open(filename, "w"); File section_file = LittleFS.open(filename, "w");
if (section_file) { if (section_file) {
LOG_INFO("Applying new uploaded %s data", section); LOG_DEBUG("Applying new uploaded %s data", section);
serializeJson(section_json, section_file); serializeJson(section_json, section_file);
section_file.close(); section_file.close();
return true; // reboot required return true; // reboot required
} }
} }
#endif #endif
return false; // not found return false; // not found
} }
@@ -2047,17 +2363,28 @@ bool System::command_info(const char * value, const int8_t id, JsonObject output
node["txpause"] = EMSbus::tx_mode() == EMS_TXMODE_OFF; node["txpause"] = EMSbus::tx_mode() == EMS_TXMODE_OFF;
// GPIO information // GPIO information
std::string gpios_allowed_str;
for (const auto & gpio : valid_system_gpios_) {
if (!gpios_allowed_str.empty()) {
gpios_allowed_str += ", ";
}
gpios_allowed_str += Helpers::itoa(gpio);
}
node["gpios_allowed"] = gpios_allowed_str;
std::string gpios_in_use_str; std::string gpios_in_use_str;
for (const auto & usage : EMSESP::system_.used_gpios_) { auto sorted_gpios = used_gpios_;
std::sort(sorted_gpios.begin(), sorted_gpios.end(), [](const GpioUsage & a, const GpioUsage & b) { return a.pin < b.pin; });
for (const auto & gpio : sorted_gpios) {
if (!gpios_in_use_str.empty()) { if (!gpios_in_use_str.empty()) {
gpios_in_use_str += ", "; gpios_in_use_str += ", ";
} }
gpios_in_use_str += Helpers::itoa(usage.pin); gpios_in_use_str += Helpers::itoa(gpio.pin);
} }
node["gpios_in_use"] = gpios_in_use_str; node["gpios_in_use"] = gpios_in_use_str;
std::string gpios_available_str; std::string gpios_available_str;
for (const auto & gpio : EMSESP::system_.available_gpios()) { for (const auto & gpio : available_gpios()) {
if (!gpios_available_str.empty()) { if (!gpios_available_str.empty()) {
gpios_available_str += ", "; gpios_available_str += ", ";
} }
@@ -2193,6 +2520,15 @@ bool System::command_info(const char * value, const int8_t id, JsonObject output
} }
#endif #endif
// Modbus Status
node = output["modbus"].to<JsonObject>();
node["enabled"] = EMSESP::system_.modbus_enabled_;
if (EMSESP::system_.modbus_enabled_) {
node["maxClients"] = EMSESP::system_.modbus_max_clients_;
node["port"] = EMSESP::system_.modbus_port_;
node["timeout"] = EMSESP::system_.modbus_timeout_;
}
// Sensor Status // Sensor Status
node = output["sensor"].to<JsonObject>(); node = output["sensor"].to<JsonObject>();
if (EMSESP::sensor_enabled()) { if (EMSESP::sensor_enabled()) {
@@ -2200,6 +2536,10 @@ bool System::command_info(const char * value, const int8_t id, JsonObject output
node["temperatureSensorReads"] = EMSESP::temperaturesensor_.reads(); node["temperatureSensorReads"] = EMSESP::temperaturesensor_.reads();
node["temperatureSensorFails"] = EMSESP::temperaturesensor_.fails(); node["temperatureSensorFails"] = EMSESP::temperaturesensor_.fails();
} }
// Analog Status
node = output["analog"].to<JsonObject>();
node["enabled"] = EMSESP::analog_enabled();
if (EMSESP::analog_enabled()) { if (EMSESP::analog_enabled()) {
node["analogSensors"] = EMSESP::analogsensor_.count_entities(); node["analogSensors"] = EMSESP::analogsensor_.count_entities();
node["analogSensorReads"] = EMSESP::analogsensor_.reads(); node["analogSensorReads"] = EMSESP::analogsensor_.reads();
@@ -2528,7 +2868,7 @@ std::string System::reset_reason(uint8_t cpu) const {
case RESET_REASON_CORE_DEEP_SLEEP: case RESET_REASON_CORE_DEEP_SLEEP:
return ("Deep sleep reset"); return ("Deep sleep reset");
case 6: // RESET_REASON_CORE_SDIO: // not on S2, S3, C3 case 6: // RESET_REASON_CORE_SDIO: // not on S2, S3, C3
return ("Reset by SDIO"); return ("Reset by SDIO");
case RESET_REASON_CORE_MWDT0: case RESET_REASON_CORE_MWDT0:
return ("Timer group0 watch dog reset"); return ("Timer group0 watch dog reset");
case RESET_REASON_CORE_MWDT1: case RESET_REASON_CORE_MWDT1:
@@ -2582,7 +2922,7 @@ bool System::ntp_connected() {
return ntp_connected_; return ntp_connected_;
} }
// see if its a BBQKees Gateway by checking the nvs values // see if its a BBQKees Gateway by checking the eFuse values
String System::getBBQKeesGatewayDetails(uint8_t detail) { String System::getBBQKeesGatewayDetails(uint8_t detail) {
#ifndef EMSESP_STANDALONE #ifndef EMSESP_STANDALONE
union { union {
@@ -2918,7 +3258,7 @@ void System::set_valid_system_gpios() {
valid_system_gpios_ = string_range_to_vector("0-21", "2, 8, 12-17, 18-19"); valid_system_gpios_ = string_range_to_vector("0-21", "2, 8, 12-17, 18-19");
#elif CONFIG_IDF_TARGET_ESP32S2 #elif CONFIG_IDF_TARGET_ESP32S2
// https://docs.espressif.com/projects/esp-idf/en/stable/esp32s2/api-reference/peripherals/gpio.html // https://docs.espressif.com/projects/esp-idf/en/stable/esp32s2/api-reference/peripherals/gpio.html
// excluded: // excluded:
// GPIO26 - GPIO32 = SPI flash and PSRAM // GPIO26 - GPIO32 = SPI flash and PSRAM
// GPIO45 - GPIO46 = strapping pins // GPIO45 - GPIO46 = strapping pins
@@ -2931,7 +3271,7 @@ void System::set_valid_system_gpios() {
valid_system_gpios_ = string_range_to_vector("0-46", "19, 20, 26-32, 45-46, 39-42, 22-25"); valid_system_gpios_ = string_range_to_vector("0-46", "19, 20, 26-32, 45-46, 39-42, 22-25");
#elif CONFIG_IDF_TARGET_ESP32S3 #elif CONFIG_IDF_TARGET_ESP32S3
// https://docs.espressif.com/projects/esp-idf/en/stable/esp32s3/api-reference/peripherals/gpio.html // https://docs.espressif.com/projects/esp-idf/en/stable/esp32s3/api-reference/peripherals/gpio.html
// excluded: // excluded:
// GPIO3, GPIO45 - GPIO46 = strapping pins // GPIO3, GPIO45 - GPIO46 = strapping pins
// GPIO26 - GPIO32 = SPI flash and PSRAM and not recommended // GPIO26 - GPIO32 = SPI flash and PSRAM and not recommended
@@ -2950,7 +3290,7 @@ void System::set_valid_system_gpios() {
} }
#elif CONFIG_IDF_TARGET_ESP32 #elif CONFIG_IDF_TARGET_ESP32
// https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/peripherals/gpio.html // https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/peripherals/gpio.html
// excluded: // excluded:
// GPIO6 - GPIO11, GPIO16 - GPIO17 = used for SPI flash and PSRAM (dio mode only GPIO06-GPIO08, GPIO11) // GPIO6 - GPIO11, GPIO16 - GPIO17 = used for SPI flash and PSRAM (dio mode only GPIO06-GPIO08, GPIO11)
// GPIO20, GPIO24, GPIO28 - GPIO31 = don't exist // GPIO20, GPIO24, GPIO28 - GPIO31 = don't exist

View File

@@ -156,8 +156,9 @@ class System {
void systemStatus(uint8_t status_code); void systemStatus(uint8_t status_code);
uint8_t systemStatus(); uint8_t systemStatus();
static void extractSettings(const char * filename, const char * section, JsonObject output); static void exportSettings(const std::string & type, const char * filename, JsonObject output);
static bool saveSettings(const char * filename, const char * section, JsonObject input); static void exportSystemBackup(JsonObject output);
static bool saveSettings(const char * filename, JsonObject input);
static bool add_gpio(uint8_t pin, const char * source_name); static bool add_gpio(uint8_t pin, const char * source_name);
static std::vector<uint8_t> available_gpios(); static std::vector<uint8_t> available_gpios();

View File

@@ -1759,8 +1759,12 @@ void Thermostat::process_RCTime(std::shared_ptr<const Telegram> telegram) {
ttime = mktime(tm_); // thermostat time ttime = mktime(tm_); // thermostat time
} }
struct timeval newnow = {.tv_sec = ttime, .tv_usec = 0}; struct timeval newnow = {.tv_sec = ttime, .tv_usec = 0};
#if CONFIG_IDF_TARGET_ESP32C3
// unknown how to set time on C3
#else
settimeofday(&newnow, nullptr); settimeofday(&newnow, nullptr);
LOG_INFO("ems-esp time set from thermostat"); LOG_INFO("ems-esp time set from thermostat");
#endif
} }
#endif #endif
} }

View File

@@ -1 +1 @@
#define EMSESP_APP_VERSION "3.8.2-dev.12" #define EMSESP_APP_VERSION "3.8.2-dev.13"

View File

@@ -68,6 +68,14 @@ void EMSuart::uart_event_task(void * pvParameters) {
// initialize UART driver // initialize UART driver
void EMSuart::start(const uint8_t tx_mode, const uint8_t rx_gpio, const uint8_t tx_gpio) { void EMSuart::start(const uint8_t tx_mode, const uint8_t rx_gpio, const uint8_t tx_gpio) {
if (tx_mode_ == EMS_TXMODE_INIT) { if (tx_mode_ == EMS_TXMODE_INIT) {
#if CONFIG_IDF_TARGET_ESP32C6
uart_config_t uart_config = {.baud_rate = EMSUART_BAUD,
.data_bits = UART_DATA_8_BITS,
.parity = UART_PARITY_DISABLE,
.stop_bits = UART_STOP_BITS_1,
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
.rx_flow_ctrl_thresh = 0};
#else
uart_config_t uart_config = {.baud_rate = EMSUART_BAUD, uart_config_t uart_config = {.baud_rate = EMSUART_BAUD,
.data_bits = UART_DATA_8_BITS, .data_bits = UART_DATA_8_BITS,
.parity = UART_PARITY_DISABLE, .parity = UART_PARITY_DISABLE,
@@ -80,6 +88,7 @@ void EMSuart::start(const uint8_t tx_mode, const uint8_t rx_gpio, const uint8_t
.flags = {0, 0} .flags = {0, 0}
#endif #endif
}; };
#endif
#if defined(EMSUART_RX_INVERT) #if defined(EMSUART_RX_INVERT)
inverse_mask |= UART_SIGNAL_RXD_INV; inverse_mask |= UART_SIGNAL_RXD_INV;
#endif #endif

View File

@@ -491,6 +491,8 @@ void WebSettings::set_board_profile(WebSettings & settings) {
settings.board_profile = "S2MINI"; settings.board_profile = "S2MINI";
#elif CONFIG_IDF_TARGET_ESP32S3 #elif CONFIG_IDF_TARGET_ESP32S3
settings.board_profile = "S32S3"; // BBQKees Gateway S3 settings.board_profile = "S32S3"; // BBQKees Gateway S3
#elif CONFIG_IDF_TARGET_ESP32C6
settings.board_profile = "CUSTOM";
#endif #endif
// apply the new board profile setting // apply the new board profile setting
System::load_board_profile(data, settings.board_profile.c_str()); System::load_board_profile(data, settings.board_profile.c_str());

View File

@@ -310,26 +310,24 @@ void WebStatusService::allvalues(JsonObject output) {
// action = export // action = export
// returns data for a specific feature/settings as a json object // returns data for a specific feature/settings as a json object
bool WebStatusService::exportData(JsonObject root, std::string & type) { bool WebStatusService::exportData(JsonObject root, std::string & type) {
root["type"] = type;
if (type == "settings") { if (type == "settings") {
JsonObject node = root["System"].to<JsonObject>(); root["type"] = type; // add settings as a group
node["version"] = EMSESP_APP_VERSION; System::exportSettings(type, NETWORK_SETTINGS_FILE, root);
System::extractSettings(NETWORK_SETTINGS_FILE, "Network", root); System::exportSettings(type, AP_SETTINGS_FILE, root);
System::extractSettings(AP_SETTINGS_FILE, "AP", root); System::exportSettings(type, MQTT_SETTINGS_FILE, root);
System::extractSettings(MQTT_SETTINGS_FILE, "MQTT", root); System::exportSettings(type, NTP_SETTINGS_FILE, root);
System::extractSettings(NTP_SETTINGS_FILE, "NTP", root); System::exportSettings(type, SECURITY_SETTINGS_FILE, root);
System::extractSettings(SECURITY_SETTINGS_FILE, "Security", root); System::exportSettings(type, EMSESP_SETTINGS_FILE, root);
System::extractSettings(EMSESP_SETTINGS_FILE, "Settings", root);
} else if (type == "schedule") { } else if (type == "schedule") {
System::extractSettings(EMSESP_SCHEDULER_FILE, "Schedule", root); System::exportSettings(type, EMSESP_SCHEDULER_FILE, root);
} else if (type == "customizations") { } else if (type == "customizations") {
System::extractSettings(EMSESP_CUSTOMIZATION_FILE, "Customizations", root); System::exportSettings(type, EMSESP_CUSTOMIZATION_FILE, root);
} else if (type == "entities") { } else if (type == "entities") {
System::extractSettings(EMSESP_CUSTOMENTITY_FILE, "Entities", root); System::exportSettings(type, EMSESP_CUSTOMENTITY_FILE, root);
} else if (type == "allvalues") { } else if (type == "allvalues") {
root.clear(); // don't need the "type" key added to the output
allvalues(root); allvalues(root);
} else if (type == "systembackup") {
System::exportSystemBackup(root);
} else { } else {
return false; // error return false; // error
} }
@@ -367,7 +365,7 @@ bool WebStatusService::getCustomSupport(JsonObject root) {
#endif #endif
#if defined(EMSESP_DEBUG) #if defined(EMSESP_DEBUG)
EMSESP::logger().debug("Showing custom support page"); EMSESP::logger().debug("Sending custom support page");
#endif #endif
root.set(doc.as<JsonObject>()); // add to web response root object root.set(doc.as<JsonObject>()); // add to web response root object

View File

@@ -17,10 +17,9 @@ void test_2() {
"[{\"info\":\"list all values (verbose)\",\"values\":\"list all values\",\"commands\":\"list all commands\",\"entities\":\"list all " "[{\"info\":\"list all values (verbose)\",\"values\":\"list all values\",\"commands\":\"list all commands\",\"entities\":\"list all "
"entities\",\"boil2hystoff\":\"hysteresis stage 2 off temperature\",\"boil2hyston\":\"hysteresis stage 2 on temperature\",\"boilhystoff\":\"hysteresis " "entities\",\"boil2hystoff\":\"hysteresis stage 2 off temperature\",\"boil2hyston\":\"hysteresis stage 2 on temperature\",\"boilhystoff\":\"hysteresis "
"off temperature\",\"boilhyston\":\"hysteresis on temperature\",\"burnmaxpower\":\"burner max power\",\"burnminperiod\":\"burner min " "off temperature\",\"boilhyston\":\"hysteresis on temperature\",\"burnmaxpower\":\"burner max power\",\"burnminperiod\":\"burner min "
"period\",\"chimneysweeper\":\"chimney sweeper\",\"coldshot\":\"send a cold shot of " "period\",\"chimneysweeper\":\"chimney sweeper\",\"coldshot\":\"send a cold shot of water\",\"curvebase\":\"heatingcurve "
"water\",\"curvebase\":\"heatingcurve base\",\"curveend\":\"heatingcurve end\",\"curveon\":\"heatingcurve " "base\",\"curveend\":\"heatingcurve end\",\"curveon\":\"heatingcurve on\",\"dhw[n].activated\":\"activated\",\"dhw[n].chargeoptimization\":\"charge "
"on\",\"dhw[n].activated\":\"activated\",\"dhw[n].chargeoptimization\":\"charge optimization\",\"dhw[n].circ\":\"circulation " "optimization\",\"dhw[n].circ\":\"circulation active\",\"dhw[n].circmode\":\"circulation pump mode\",\"dhw[n].circpump\":\"circulation pump "
"active\",\"dhw[n].circmode\":\"circulation pump mode\",\"dhw[n].circpump\":\"circulation pump "
"available\",\"dhw[n].comfort\":\"comfort\",\"dhw[n].comfort1\":\"comfort mode\",\"dhw[n].dhwprio\":\"dhw " "available\",\"dhw[n].comfort\":\"comfort\",\"dhw[n].comfort1\":\"comfort mode\",\"dhw[n].dhwprio\":\"dhw "
"priority\",\"dhw[n].disinfecting\":\"disinfecting\",\"dhw[n].disinfectiontemp\":\"disinfection temperature\",\"dhw[n].flowtempoffset\":\"flow " "priority\",\"dhw[n].disinfecting\":\"disinfecting\",\"dhw[n].disinfectiontemp\":\"disinfection temperature\",\"dhw[n].flowtempoffset\":\"flow "
"temperature offset\",\"dhw[n].hystoff\":\"hysteresis off temperature\",\"dhw[n].hyston\":\"hysteresis on temperature\",\"dhw[n].maxpower\":\"max " "temperature offset\",\"dhw[n].hystoff\":\"hysteresis off temperature\",\"dhw[n].hyston\":\"hysteresis on temperature\",\"dhw[n].maxpower\":\"max "
@@ -32,9 +31,9 @@ void test_2() {
"date\",\"maintenancetime\":\"time to next maintenance\",\"nofrostmode\":\"nofrost mode\",\"nofrosttemp\":\"nofrost " "date\",\"maintenancetime\":\"time to next maintenance\",\"nofrostmode\":\"nofrost mode\",\"nofrosttemp\":\"nofrost "
"temperature\",\"nompower\":\"nominal Power\",\"nrgheat\":\"energy heating\",\"pumpcharacter\":\"boiler pump characteristic\",\"pumpdelay\":\"pump " "temperature\",\"nompower\":\"nominal Power\",\"nrgheat\":\"energy heating\",\"pumpcharacter\":\"boiler pump characteristic\",\"pumpdelay\":\"pump "
"delay\",\"pumpkickday\":\"pump kick day\",\"pumpkickdelay\":\"pump kick delay\",\"pumpkickhour\":\"pump kick hour\",\"pumpmode\":\"boiler pump " "delay\",\"pumpkickday\":\"pump kick day\",\"pumpkickdelay\":\"pump kick delay\",\"pumpkickhour\":\"pump kick hour\",\"pumpmode\":\"boiler pump "
"mode\",\"pumpmodmax\":\"boiler pump max power\",\"pumpmodmin\":\"boiler pump min power\",\"pumpontemp\":\"pump " "mode\",\"pumpmodmax\":\"boiler pump max power\",\"pumpmodmin\":\"boiler pump min power\",\"pumpontemp\":\"pump logic "
"logic temperature\",\"reset\":\"reset\",\"selburnpow\":\"burner selected max power\",\"selflowtemp\":\"selected flow " "temperature\",\"reset\":\"reset\",\"selburnpow\":\"burner selected max power\",\"selflowtemp\":\"selected flow temperature\",\"summertemp\":\"summer "
"temperature\",\"summertemp\":\"summer temperature\"}]"; "temperature\"}]";
TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/boiler/commands")); TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/boiler/commands"));
} }
@@ -149,9 +148,9 @@ void test_12() {
} }
void test_13() { void test_13() {
auto expected_response = "[{\"hc1\":{\"seltemp\":20.5,\"currtemp\":22.8,\"modetype\":\"heat\",\"remotetemp\":null},\"hc2\":{" auto expected_response =
"\"seltemp\":20.6,\"currtemp\":22.9,\"modetype\":\"eco\",\"remotetemp\":null},\"hc3\":{\"seltemp\":20." "[{\"hc1\":{\"seltemp\":20.5,\"currtemp\":22.8,\"modetype\":\"heat\",\"remotetemp\":null},\"hc2\":{\"seltemp\":20.6,\"currtemp\":22.9,\"modetype\":"
"7,\"currtemp\":23,\"modetype\":\"nofrost\",\"remotetemp\":null},\"dhw\":{}}]"; "\"eco\",\"remotetemp\":null},\"hc3\":{\"seltemp\":20.7,\"currtemp\":23,\"modetype\":\"nofrost\",\"remotetemp\":null},\"dhw\":{}}]";
TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/thermostat")); TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/thermostat"));
} }
@@ -164,11 +163,10 @@ void test_15() {
auto expected_response = auto expected_response =
"[{\"api_data\":\"# HELP emsesp_seltemp selected room temperature, °C, readable, writeable, visible\\n# TYPE emsesp_seltemp " "[{\"api_data\":\"# HELP emsesp_seltemp selected room temperature, °C, readable, writeable, visible\\n# TYPE emsesp_seltemp "
"gauge\\nemsesp_seltemp{circuit=\\\"hc1\\\"} 20.50\\n# HELP emsesp_currtemp current room temperature, °C, readable, visible\\n# TYPE emsesp_currtemp " "gauge\\nemsesp_seltemp{circuit=\\\"hc1\\\"} 20.50\\n# HELP emsesp_currtemp current room temperature, °C, readable, visible\\n# TYPE emsesp_currtemp "
"gauge\\nemsesp_currtemp{circuit=\\\"hc1\\\"} 22.80\\n# HELP emsesp_modetype mode type, enum, (0: " "gauge\\nemsesp_currtemp{circuit=\\\"hc1\\\"} 22.80\\n# HELP emsesp_modetype mode type, enum, (0: nofrost; 1: eco; 2: heat), readable, visible\\n# "
"nofrost; 1: eco; 2: heat), readable, visible\\n# TYPE emsesp_modetype gauge\\nemsesp_modetype{circuit=\\\"hc1\\\"} " "TYPE emsesp_modetype gauge\\nemsesp_modetype{circuit=\\\"hc1\\\"} 2\\nemsesp_seltemp{circuit=\\\"hc2\\\"} "
"2\\nemsesp_seltemp{circuit=\\\"hc2\\\"} 20.60\\nemsesp_currtemp{circuit=\\\"hc2\\\"} " "20.60\\nemsesp_currtemp{circuit=\\\"hc2\\\"} 22.90\\nemsesp_modetype{circuit=\\\"hc2\\\"} 1\\nemsesp_seltemp{circuit=\\\"hc3\\\"} "
"22.90\\nemsesp_modetype{circuit=\\\"hc2\\\"} 1\\nemsesp_seltemp{circuit=\\\"hc3\\\"} 20.70\\nemsesp_currtemp{circuit=\\\"hc3\\\"} " "20.70\\nemsesp_currtemp{circuit=\\\"hc3\\\"} 23\\nemsesp_modetype{circuit=\\\"hc3\\\"} 0\\n\"}]";
"23\\nemsesp_modetype{circuit=\\\"hc3\\\"} 0\\n\"}]";
TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/thermostat/metrics")); TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/thermostat/metrics"));
} }
@@ -217,23 +215,24 @@ void test_22() {
void test_23() { void test_23() {
auto expected_response = auto expected_response =
"[{\"system\":{\"version\":\"dev\",\"uptime\":\"000+00:00:00.000\",\"uptimeSec\":0,\"resetReason\":\"Unknown / " "[{\"system\":{\"version\":\"dev\",\"uptime\":\"000+00:00:00.000\",\"uptimeSec\":0,\"resetReason\":\"Unknown / "
"Unknown\",\"txpause\":false,\"gpios_in_use\":\"23, 5, 2, 18, 0\",\"gpios_available\":\"\"}," "Unknown\",\"txpause\":false,\"gpios_allowed\":\"0, 2, 5, 18, 23\",\"gpios_in_use\":\"0, 2, 5, 18, "
"\"network\":{\"network\":\"WiFi\",\"hostname\":\"ems-esp\",\"RSSI\":-23,\"TxPowerSetting\":0,\"staticIP\":false,\"lowBandwidth\":false," "23\",\"gpios_available\":\"\"},\"network\":{\"network\":\"WiFi\",\"hostname\":\"ems-esp\",\"RSSI\":-23,\"TxPowerSetting\":0,\"staticIP\":false,"
"\"disableSleep\":true,\"enableMDNS\":true,\"enableCORS\":false},\"ntp\":{\"NTPstatus\":\"disconnected\",\"enabled\":true,\"server\":\"pool.ntp.org\"," "\"lowBandwidth\":false,\"disableSleep\":true,\"enableMDNS\":true,\"enableCORS\":false},\"ntp\":{\"NTPstatus\":\"disconnected\",\"enabled\":true,"
"\"tzLabel\":\"Europe/" "\"server\":\"pool.ntp.org\",\"tzLabel\":\"Europe/"
"London\",\"NTPStatus\":\"disconnected\"},\"ap\":{\"provisionMode\":\"always\",\"ssid\":\"ems-esp\"},\"mqtt\":{\"MQTTStatus\":\"disconnected\"," "London\",\"NTPStatus\":\"disconnected\"},\"ap\":{\"provisionMode\":\"always\",\"ssid\":\"ems-esp\"},\"mqtt\":{\"MQTTStatus\":\"disconnected\","
"\"MQTTPublishes\":0,\"MQTTQueued\":0,\"MQTTPublishFails\":0,\"MQTTReconnects\":0,\"enabled\":true,\"clientID\":\"ems-esp\",\"keepAlive\":60," "\"MQTTPublishes\":0,\"MQTTQueued\":0,\"MQTTPublishFails\":0,\"MQTTReconnects\":0,\"enabled\":true,\"clientID\":\"ems-esp\",\"keepAlive\":60,"
"\"cleanSession\":false,\"entityFormat\":1,\"base\":\"ems-esp\",\"discoveryPrefix\":\"homeassistant\",\"discoveryType\":0,\"nestedFormat\":1," "\"cleanSession\":false,\"entityFormat\":1,\"base\":\"ems-esp\",\"discoveryPrefix\":\"homeassistant\",\"discoveryType\":0,\"nestedFormat\":1,"
"\"haEnabled\":true,\"mqttQos\":0,\"mqttRetain\":false,\"publishTimeHeartbeat\":60,\"publishTimeBoiler\":10,\"publishTimeThermostat\":10," "\"haEnabled\":true,\"mqttQos\":0,\"mqttRetain\":false,\"publishTimeHeartbeat\":60,\"publishTimeBoiler\":10,\"publishTimeThermostat\":10,"
"\"publishTimeSolar\":10,\"publishTimeMixer\":10,\"publishTimeWater\":0,\"publishTimeOther\":10,\"publishTimeSensor\":10,\"publishSingle\":false," "\"publishTimeSolar\":10,\"publishTimeMixer\":10,\"publishTimeWater\":0,\"publishTimeOther\":10,\"publishTimeSensor\":10,\"publishSingle\":false,"
"\"publish2command\":false,\"sendResponse\":false},\"syslog\":{\"enabled\":false},\"sensor\":{\"temperatureSensors\":3,\"temperatureSensorReads\":0," "\"publish2command\":false,\"sendResponse\":false},\"syslog\":{\"enabled\":false},\"modbus\":{\"enabled\":false},\"sensor\":{\"temperatureSensors\":3,"
"\"temperatureSensorFails\":0,\"analogSensors\":5,\"analogSensorReads\":0,\"analogSensorFails\":0},\"api\":{\"APICalls\":0,\"APIFails\":0},\"bus\":{" "\"temperatureSensorReads\":0,\"temperatureSensorFails\":0},\"analog\":{\"enabled\":true,\"analogSensors\":5,\"analogSensorReads\":0,"
"\"busStatus\":\"connected\",\"busProtocol\":\"Buderus\",\"busTelegramsReceived\":8,\"busReads\":0,\"busWrites\":0,\"busIncompleteTelegrams\":0," "\"analogSensorFails\":0},\"api\":{\"APICalls\":0,\"APIFails\":0},\"bus\":{\"busStatus\":\"connected\",\"busProtocol\":\"Buderus\","
"\"busReadsFailed\":0,\"busWritesFailed\":0,\"busRxLineQuality\":100,\"busTxLineQuality\":100},\"settings\":{\"boardProfile\":\"S32\",\"locale\":" "\"busTelegramsReceived\":8,\"busReads\":0,\"busWrites\":0,\"busIncompleteTelegrams\":0,\"busReadsFailed\":0,\"busWritesFailed\":0,"
"\"en\",\"txMode\":1,\"emsBusID\":11,\"showerTimer\":false,\"showerMinDuration\":180,\"showerAlert\":false,\"hideLed\":false,\"noTokenApi\":false," "\"busRxLineQuality\":100,\"busTxLineQuality\":100},\"settings\":{\"boardProfile\":\"S32\",\"locale\":\"en\",\"txMode\":1,\"emsBusID\":11,"
"\"readonlyMode\":false,\"fahrenheit\":false,\"dallasParasite\":false,\"boolFormat\":1,\"boolDashboard\":1,\"enumFormat\":1,\"analogEnabled\":true," "\"showerTimer\":false,\"showerMinDuration\":180,\"showerAlert\":false,\"hideLed\":false,\"noTokenApi\":false,\"readonlyMode\":false,\"fahrenheit\":"
"\"telnetEnabled\":true,\"maxWebLogBuffer\":25,\"modbusEnabled\":false,\"forceHeatingOff\":false,\"developerMode\":false},\"devices\":[{\"type\":" "false,\"dallasParasite\":false,\"boolFormat\":1,\"boolDashboard\":1,\"enumFormat\":1,\"analogEnabled\":true,\"telnetEnabled\":true,"
"\"boiler\",\"name\":\"My Custom " "\"maxWebLogBuffer\":25,\"modbusEnabled\":false,\"forceHeatingOff\":false,\"developerMode\":false},\"devices\":[{\"type\":\"boiler\",\"name\":\"My "
"Custom "
"Boiler\",\"deviceID\":\"0x08\",\"productID\":123,\"brand\":\"\",\"version\":\"01.00\",\"entities\":39,\"handlersReceived\":\"0x18\"," "Boiler\",\"deviceID\":\"0x08\",\"productID\":123,\"brand\":\"\",\"version\":\"01.00\",\"entities\":39,\"handlersReceived\":\"0x18\","
"\"handlersFetched\":\"0x14 0x33\",\"handlersPending\":\"0xBF 0x10 0x11 0xC2 0xC6 0x15 0x1C 0x19 0x1A 0x35 0x34 0x2A 0xD1 0xE3 0xE4 0xE5 0xE9 0x02E0 " "\"handlersFetched\":\"0x14 0x33\",\"handlersPending\":\"0xBF 0x10 0x11 0xC2 0xC6 0x15 0x1C 0x19 0x1A 0x35 0x34 0x2A 0xD1 0xE3 0xE4 0xE5 0xE9 0x02E0 "
"0x2E " "0x2E "
@@ -247,23 +246,24 @@ void test_23() {
void test_24() { void test_24() {
auto expected_response = auto expected_response =
"[{\"system\":{\"version\":\"dev\",\"uptime\":\"000+00:00:00.000\",\"uptimeSec\":0,\"resetReason\":\"Unknown / " "[{\"system\":{\"version\":\"dev\",\"uptime\":\"000+00:00:00.000\",\"uptimeSec\":0,\"resetReason\":\"Unknown / "
"Unknown\",\"txpause\":false,\"gpios_in_use\":\"23, 5, 2, 18, 0\",\"gpios_available\":\"\"}," "Unknown\",\"txpause\":false,\"gpios_allowed\":\"0, 2, 5, 18, 23\",\"gpios_in_use\":\"0, 2, 5, 18, "
"\"network\":{\"network\":\"WiFi\",\"hostname\":\"ems-esp\",\"RSSI\":-23,\"TxPowerSetting\":0,\"staticIP\":false,\"lowBandwidth\":false," "23\",\"gpios_available\":\"\"},\"network\":{\"network\":\"WiFi\",\"hostname\":\"ems-esp\",\"RSSI\":-23,\"TxPowerSetting\":0,\"staticIP\":false,"
"\"disableSleep\":true,\"enableMDNS\":true,\"enableCORS\":false},\"ntp\":{\"NTPstatus\":\"disconnected\",\"enabled\":true,\"server\":\"pool.ntp.org\"," "\"lowBandwidth\":false,\"disableSleep\":true,\"enableMDNS\":true,\"enableCORS\":false},\"ntp\":{\"NTPstatus\":\"disconnected\",\"enabled\":true,"
"\"tzLabel\":\"Europe/" "\"server\":\"pool.ntp.org\",\"tzLabel\":\"Europe/"
"London\",\"NTPStatus\":\"disconnected\"},\"ap\":{\"provisionMode\":\"always\",\"ssid\":\"ems-esp\"},\"mqtt\":{\"MQTTStatus\":\"disconnected\"," "London\",\"NTPStatus\":\"disconnected\"},\"ap\":{\"provisionMode\":\"always\",\"ssid\":\"ems-esp\"},\"mqtt\":{\"MQTTStatus\":\"disconnected\","
"\"MQTTPublishes\":0,\"MQTTQueued\":0,\"MQTTPublishFails\":0,\"MQTTReconnects\":0,\"enabled\":true,\"clientID\":\"ems-esp\",\"keepAlive\":60," "\"MQTTPublishes\":0,\"MQTTQueued\":0,\"MQTTPublishFails\":0,\"MQTTReconnects\":0,\"enabled\":true,\"clientID\":\"ems-esp\",\"keepAlive\":60,"
"\"cleanSession\":false,\"entityFormat\":1,\"base\":\"ems-esp\",\"discoveryPrefix\":\"homeassistant\",\"discoveryType\":0,\"nestedFormat\":1," "\"cleanSession\":false,\"entityFormat\":1,\"base\":\"ems-esp\",\"discoveryPrefix\":\"homeassistant\",\"discoveryType\":0,\"nestedFormat\":1,"
"\"haEnabled\":true,\"mqttQos\":0,\"mqttRetain\":false,\"publishTimeHeartbeat\":60,\"publishTimeBoiler\":10,\"publishTimeThermostat\":10," "\"haEnabled\":true,\"mqttQos\":0,\"mqttRetain\":false,\"publishTimeHeartbeat\":60,\"publishTimeBoiler\":10,\"publishTimeThermostat\":10,"
"\"publishTimeSolar\":10,\"publishTimeMixer\":10,\"publishTimeWater\":0,\"publishTimeOther\":10,\"publishTimeSensor\":10,\"publishSingle\":false," "\"publishTimeSolar\":10,\"publishTimeMixer\":10,\"publishTimeWater\":0,\"publishTimeOther\":10,\"publishTimeSensor\":10,\"publishSingle\":false,"
"\"publish2command\":false,\"sendResponse\":false},\"syslog\":{\"enabled\":false},\"sensor\":{\"temperatureSensors\":3,\"temperatureSensorReads\":0," "\"publish2command\":false,\"sendResponse\":false},\"syslog\":{\"enabled\":false},\"modbus\":{\"enabled\":false},\"sensor\":{\"temperatureSensors\":3,"
"\"temperatureSensorFails\":0,\"analogSensors\":5,\"analogSensorReads\":0,\"analogSensorFails\":0},\"api\":{\"APICalls\":0,\"APIFails\":0},\"bus\":{" "\"temperatureSensorReads\":0,\"temperatureSensorFails\":0},\"analog\":{\"enabled\":true,\"analogSensors\":5,\"analogSensorReads\":0,"
"\"busStatus\":\"connected\",\"busProtocol\":\"Buderus\",\"busTelegramsReceived\":8,\"busReads\":0,\"busWrites\":0,\"busIncompleteTelegrams\":0," "\"analogSensorFails\":0},\"api\":{\"APICalls\":0,\"APIFails\":0},\"bus\":{\"busStatus\":\"connected\",\"busProtocol\":\"Buderus\","
"\"busReadsFailed\":0,\"busWritesFailed\":0,\"busRxLineQuality\":100,\"busTxLineQuality\":100},\"settings\":{\"boardProfile\":\"S32\",\"locale\":" "\"busTelegramsReceived\":8,\"busReads\":0,\"busWrites\":0,\"busIncompleteTelegrams\":0,\"busReadsFailed\":0,\"busWritesFailed\":0,"
"\"en\",\"txMode\":1,\"emsBusID\":11,\"showerTimer\":false,\"showerMinDuration\":180,\"showerAlert\":false,\"hideLed\":false,\"noTokenApi\":false," "\"busRxLineQuality\":100,\"busTxLineQuality\":100},\"settings\":{\"boardProfile\":\"S32\",\"locale\":\"en\",\"txMode\":1,\"emsBusID\":11,"
"\"readonlyMode\":false,\"fahrenheit\":false,\"dallasParasite\":false,\"boolFormat\":1,\"boolDashboard\":1,\"enumFormat\":1,\"analogEnabled\":true," "\"showerTimer\":false,\"showerMinDuration\":180,\"showerAlert\":false,\"hideLed\":false,\"noTokenApi\":false,\"readonlyMode\":false,\"fahrenheit\":"
"\"telnetEnabled\":true,\"maxWebLogBuffer\":25,\"modbusEnabled\":false,\"forceHeatingOff\":false,\"developerMode\":false},\"devices\":[{\"type\":" "false,\"dallasParasite\":false,\"boolFormat\":1,\"boolDashboard\":1,\"enumFormat\":1,\"analogEnabled\":true,\"telnetEnabled\":true,"
"\"boiler\",\"name\":\"My Custom " "\"maxWebLogBuffer\":25,\"modbusEnabled\":false,\"forceHeatingOff\":false,\"developerMode\":false},\"devices\":[{\"type\":\"boiler\",\"name\":\"My "
"Custom "
"Boiler\",\"deviceID\":\"0x08\",\"productID\":123,\"brand\":\"\",\"version\":\"01.00\",\"entities\":39,\"handlersReceived\":\"0x18\"," "Boiler\",\"deviceID\":\"0x08\",\"productID\":123,\"brand\":\"\",\"version\":\"01.00\",\"entities\":39,\"handlersReceived\":\"0x18\","
"\"handlersFetched\":\"0x14 0x33\",\"handlersPending\":\"0xBF 0x10 0x11 0xC2 0xC6 0x15 0x1C 0x19 0x1A 0x35 0x34 0x2A 0xD1 0xE3 0xE4 0xE5 0xE9 0x02E0 " "\"handlersFetched\":\"0x14 0x33\",\"handlersPending\":\"0xBF 0x10 0x11 0xC2 0xC6 0x15 0x1C 0x19 0x1A 0x35 0x34 0x2A 0xD1 0xE3 0xE4 0xE5 0xE9 0x02E0 "
"0x2E " "0x2E "
@@ -278,12 +278,12 @@ void test_25() {
auto expected_response = auto expected_response =
"[{\"api_data\":\"# HELP emsesp_system_uptimesec uptimeSec\\n# TYPE emsesp_system_uptimesec gauge\\nemsesp_system_uptimesec 0\\n# HELP " "[{\"api_data\":\"# HELP emsesp_system_uptimesec uptimeSec\\n# TYPE emsesp_system_uptimesec gauge\\nemsesp_system_uptimesec 0\\n# HELP "
"emsesp_system_txpause txpause\\n# TYPE emsesp_system_txpause gauge\\nemsesp_system_txpause 0\\n# HELP emsesp_system_info info\\n# TYPE " "emsesp_system_txpause txpause\\n# TYPE emsesp_system_txpause gauge\\nemsesp_system_txpause 0\\n# HELP emsesp_system_info info\\n# TYPE "
"emsesp_system_info gauge\\nemsesp_system_info{version=\\\"dev\\\", resetreason=\\\"Unknown / Unknown\\\", gpios_in_use=\\\"23, 5, 2, 18, 0" "emsesp_system_info gauge\\nemsesp_system_info{version=\\\"dev\\\", resetreason=\\\"Unknown / Unknown\\\", gpios_allowed=\\\"0, 2, 5, 18, 23\\\", "
"\\\"} 1\\n# HELP emsesp_network_rssi RSSI\\n# TYPE emsesp_network_rssi gauge\\nemsesp_network_rssi -23\\n# HELP emsesp_network_txpowersetting " "gpios_in_use=\\\"0, 2, 5, 18, 23\\\"} 1\\n# HELP emsesp_network_rssi RSSI\\n# TYPE emsesp_network_rssi gauge\\nemsesp_network_rssi -23\\n# HELP "
"TxPowerSetting\\n# TYPE emsesp_network_txpowersetting gauge\\nemsesp_network_txpowersetting 0\\n# HELP emsesp_network_staticip staticIP\\n# TYPE " "emsesp_network_txpowersetting TxPowerSetting\\n# TYPE emsesp_network_txpowersetting gauge\\nemsesp_network_txpowersetting 0\\n# HELP "
"emsesp_network_staticip gauge\\nemsesp_network_staticip 0\\n# HELP emsesp_network_lowbandwidth lowBandwidth\\n# TYPE emsesp_network_lowbandwidth " "emsesp_network_staticip staticIP\\n# TYPE emsesp_network_staticip gauge\\nemsesp_network_staticip 0\\n# HELP emsesp_network_lowbandwidth "
"gauge\\nemsesp_network_lowbandwidth 0\\n# HELP emsesp_network_disablesleep disableSleep\\n# TYPE emsesp_network_disablesleep " "lowBandwidth\\n# TYPE emsesp_network_lowbandwidth gauge\\nemsesp_network_lowbandwidth 0\\n# HELP emsesp_network_disablesleep disableSleep\\n# TYPE "
"gauge\\nemsesp_network_disablesleep 1\\n# HELP emsesp_network_enablemdns enableMDNS\\n# TYPE emsesp_network_enablemdns " "emsesp_network_disablesleep gauge\\nemsesp_network_disablesleep 1\\n# HELP emsesp_network_enablemdns enableMDNS\\n# TYPE emsesp_network_enablemdns "
"gauge\\nemsesp_network_enablemdns 1\\n# HELP emsesp_network_enablecors enableCORS\\n# TYPE emsesp_network_enablecors " "gauge\\nemsesp_network_enablemdns 1\\n# HELP emsesp_network_enablecors enableCORS\\n# TYPE emsesp_network_enablecors "
"gauge\\nemsesp_network_enablecors 0\\n# HELP emsesp_network_info info\\n# TYPE emsesp_network_info gauge\\nemsesp_network_info{network=\\\"WiFi\\\", " "gauge\\nemsesp_network_enablecors 0\\n# HELP emsesp_network_info info\\n# TYPE emsesp_network_info gauge\\nemsesp_network_info{network=\\\"WiFi\\\", "
"hostname=\\\"ems-esp\\\"} 1\\n# HELP emsesp_ntp_enabled enabled\\n# TYPE emsesp_ntp_enabled gauge\\nemsesp_ntp_enabled 1\\n# HELP emsesp_ntp_info " "hostname=\\\"ems-esp\\\"} 1\\n# HELP emsesp_ntp_enabled enabled\\n# TYPE emsesp_ntp_enabled gauge\\nemsesp_ntp_enabled 1\\n# HELP emsesp_ntp_info "
@@ -312,41 +312,43 @@ void test_25() {
"emsesp_mqtt_publish2command gauge\\nemsesp_mqtt_publish2command 0\\n# HELP emsesp_mqtt_sendresponse sendResponse\\n# TYPE emsesp_mqtt_sendresponse " "emsesp_mqtt_publish2command gauge\\nemsesp_mqtt_publish2command 0\\n# HELP emsesp_mqtt_sendresponse sendResponse\\n# TYPE emsesp_mqtt_sendresponse "
"gauge\\nemsesp_mqtt_sendresponse 0\\n# HELP emsesp_mqtt_info info\\n# TYPE emsesp_mqtt_info gauge\\nemsesp_mqtt_info{mqttstatus=\\\"disconnected\\\", " "gauge\\nemsesp_mqtt_sendresponse 0\\n# HELP emsesp_mqtt_info info\\n# TYPE emsesp_mqtt_info gauge\\nemsesp_mqtt_info{mqttstatus=\\\"disconnected\\\", "
"clientid=\\\"ems-esp\\\", base=\\\"ems-esp\\\", discoveryprefix=\\\"homeassistant\\\"} 1\\n# HELP emsesp_syslog_enabled enabled\\n# TYPE " "clientid=\\\"ems-esp\\\", base=\\\"ems-esp\\\", discoveryprefix=\\\"homeassistant\\\"} 1\\n# HELP emsesp_syslog_enabled enabled\\n# TYPE "
"emsesp_syslog_enabled gauge\\nemsesp_syslog_enabled 0\\n# HELP emsesp_sensor_temperaturesensors temperatureSensors\\n# TYPE " "emsesp_syslog_enabled gauge\\nemsesp_syslog_enabled 0\\n# HELP emsesp_modbus_enabled enabled\\n# TYPE emsesp_modbus_enabled "
"emsesp_sensor_temperaturesensors gauge\\nemsesp_sensor_temperaturesensors 3\\n# HELP emsesp_sensor_temperaturesensorreads temperatureSensorReads\\n# " "gauge\\nemsesp_modbus_enabled 0\\n# HELP emsesp_sensor_temperaturesensors temperatureSensors\\n# TYPE emsesp_sensor_temperaturesensors "
"TYPE emsesp_sensor_temperaturesensorreads gauge\\nemsesp_sensor_temperaturesensorreads 0\\n# HELP emsesp_sensor_temperaturesensorfails " "gauge\\nemsesp_sensor_temperaturesensors 3\\n# HELP emsesp_sensor_temperaturesensorreads temperatureSensorReads\\n# TYPE "
"temperatureSensorFails\\n# TYPE emsesp_sensor_temperaturesensorfails gauge\\nemsesp_sensor_temperaturesensorfails 0\\n# HELP " "emsesp_sensor_temperaturesensorreads gauge\\nemsesp_sensor_temperaturesensorreads 0\\n# HELP emsesp_sensor_temperaturesensorfails "
"emsesp_sensor_analogsensors analogSensors\\n# TYPE emsesp_sensor_analogsensors gauge\\nemsesp_sensor_analogsensors 5\\n# HELP " "temperatureSensorFails\\n# TYPE emsesp_sensor_temperaturesensorfails gauge\\nemsesp_sensor_temperaturesensorfails 0\\n# HELP emsesp_analog_enabled "
"emsesp_sensor_analogsensorreads analogSensorReads\\n# TYPE emsesp_sensor_analogsensorreads gauge\\nemsesp_sensor_analogsensorreads 0\\n# HELP " "enabled\\n# TYPE emsesp_analog_enabled gauge\\nemsesp_analog_enabled 1\\n# HELP emsesp_analog_analogsensors analogSensors\\n# TYPE "
"emsesp_sensor_analogsensorfails analogSensorFails\\n# TYPE emsesp_sensor_analogsensorfails gauge\\nemsesp_sensor_analogsensorfails 0\\n# HELP " "emsesp_analog_analogsensors gauge\\nemsesp_analog_analogsensors 5\\n# HELP emsesp_analog_analogsensorreads analogSensorReads\\n# TYPE "
"emsesp_api_apicalls APICalls\\n# TYPE emsesp_api_apicalls gauge\\nemsesp_api_apicalls 0\\n# HELP emsesp_api_apifails APIFails\\n# TYPE " "emsesp_analog_analogsensorreads gauge\\nemsesp_analog_analogsensorreads 0\\n# HELP emsesp_analog_analogsensorfails analogSensorFails\\n# TYPE "
"emsesp_api_apifails gauge\\nemsesp_api_apifails 0\\n# HELP emsesp_bus_bustelegramsreceived busTelegramsReceived\\n# TYPE " "emsesp_analog_analogsensorfails gauge\\nemsesp_analog_analogsensorfails 0\\n# HELP emsesp_api_apicalls APICalls\\n# TYPE emsesp_api_apicalls "
"emsesp_bus_bustelegramsreceived gauge\\nemsesp_bus_bustelegramsreceived 8\\n# HELP emsesp_bus_busreads busReads\\n# TYPE emsesp_bus_busreads " "gauge\\nemsesp_api_apicalls 0\\n# HELP emsesp_api_apifails APIFails\\n# TYPE emsesp_api_apifails gauge\\nemsesp_api_apifails 0\\n# HELP "
"gauge\\nemsesp_bus_busreads 0\\n# HELP emsesp_bus_buswrites busWrites\\n# TYPE emsesp_bus_buswrites gauge\\nemsesp_bus_buswrites 0\\n# HELP " "emsesp_bus_bustelegramsreceived busTelegramsReceived\\n# TYPE emsesp_bus_bustelegramsreceived gauge\\nemsesp_bus_bustelegramsreceived 8\\n# HELP "
"emsesp_bus_busincompletetelegrams busIncompleteTelegrams\\n# TYPE emsesp_bus_busincompletetelegrams gauge\\nemsesp_bus_busincompletetelegrams 0\\n# " "emsesp_bus_busreads busReads\\n# TYPE emsesp_bus_busreads gauge\\nemsesp_bus_busreads 0\\n# HELP emsesp_bus_buswrites busWrites\\n# TYPE "
"HELP emsesp_bus_busreadsfailed busReadsFailed\\n# TYPE emsesp_bus_busreadsfailed gauge\\nemsesp_bus_busreadsfailed 0\\n# HELP " "emsesp_bus_buswrites gauge\\nemsesp_bus_buswrites 0\\n# HELP emsesp_bus_busincompletetelegrams busIncompleteTelegrams\\n# TYPE "
"emsesp_bus_buswritesfailed busWritesFailed\\n# TYPE emsesp_bus_buswritesfailed gauge\\nemsesp_bus_buswritesfailed 0\\n# HELP " "emsesp_bus_busincompletetelegrams gauge\\nemsesp_bus_busincompletetelegrams 0\\n# HELP emsesp_bus_busreadsfailed busReadsFailed\\n# TYPE "
"emsesp_bus_busrxlinequality busRxLineQuality\\n# TYPE emsesp_bus_busrxlinequality gauge\\nemsesp_bus_busrxlinequality 100\\n# HELP " "emsesp_bus_busreadsfailed gauge\\nemsesp_bus_busreadsfailed 0\\n# HELP emsesp_bus_buswritesfailed busWritesFailed\\n# TYPE emsesp_bus_buswritesfailed "
"emsesp_bus_bustxlinequality busTxLineQuality\\n# TYPE emsesp_bus_bustxlinequality gauge\\nemsesp_bus_bustxlinequality 100\\n# HELP emsesp_bus_info " "gauge\\nemsesp_bus_buswritesfailed 0\\n# HELP emsesp_bus_busrxlinequality busRxLineQuality\\n# TYPE emsesp_bus_busrxlinequality "
"info\\n# TYPE emsesp_bus_info gauge\\nemsesp_bus_info{busstatus=\\\"connected\\\", busprotocol=\\\"Buderus\\\"} 1\\n# HELP emsesp_settings_txmode " "gauge\\nemsesp_bus_busrxlinequality 100\\n# HELP emsesp_bus_bustxlinequality busTxLineQuality\\n# TYPE emsesp_bus_bustxlinequality "
"txMode\\n# TYPE emsesp_settings_txmode gauge\\nemsesp_settings_txmode 1\\n# HELP emsesp_settings_emsbusid emsBusID\\n# TYPE emsesp_settings_emsbusid " "gauge\\nemsesp_bus_bustxlinequality 100\\n# HELP emsesp_bus_info info\\n# TYPE emsesp_bus_info gauge\\nemsesp_bus_info{busstatus=\\\"connected\\\", "
"gauge\\nemsesp_settings_emsbusid 11\\n# HELP emsesp_settings_showertimer showerTimer\\n# TYPE emsesp_settings_showertimer " "busprotocol=\\\"Buderus\\\"} 1\\n# HELP emsesp_settings_txmode txMode\\n# TYPE emsesp_settings_txmode gauge\\nemsesp_settings_txmode 1\\n# HELP "
"gauge\\nemsesp_settings_showertimer 0\\n# HELP emsesp_settings_showerminduration showerMinDuration\\n# TYPE emsesp_settings_showerminduration " "emsesp_settings_emsbusid emsBusID\\n# TYPE emsesp_settings_emsbusid gauge\\nemsesp_settings_emsbusid 11\\n# HELP emsesp_settings_showertimer "
"gauge\\nemsesp_settings_showerminduration 180\\n# HELP emsesp_settings_showeralert showerAlert\\n# TYPE emsesp_settings_showeralert " "showerTimer\\n# TYPE emsesp_settings_showertimer gauge\\nemsesp_settings_showertimer 0\\n# HELP emsesp_settings_showerminduration "
"gauge\\nemsesp_settings_showeralert 0\\n# HELP emsesp_settings_hideled hideLed\\n# TYPE emsesp_settings_hideled gauge\\nemsesp_settings_hideled 0\\n# " "showerMinDuration\\n# TYPE emsesp_settings_showerminduration gauge\\nemsesp_settings_showerminduration 180\\n# HELP emsesp_settings_showeralert "
"HELP emsesp_settings_notokenapi noTokenApi\\n# TYPE emsesp_settings_notokenapi gauge\\nemsesp_settings_notokenapi 0\\n# HELP " "showerAlert\\n# TYPE emsesp_settings_showeralert gauge\\nemsesp_settings_showeralert 0\\n# HELP emsesp_settings_hideled hideLed\\n# TYPE "
"emsesp_settings_readonlymode readonlyMode\\n# TYPE emsesp_settings_readonlymode gauge\\nemsesp_settings_readonlymode 0\\n# HELP " "emsesp_settings_hideled gauge\\nemsesp_settings_hideled 0\\n# HELP emsesp_settings_notokenapi noTokenApi\\n# TYPE emsesp_settings_notokenapi "
"emsesp_settings_fahrenheit fahrenheit\\n# TYPE emsesp_settings_fahrenheit gauge\\nemsesp_settings_fahrenheit 0\\n# HELP " "gauge\\nemsesp_settings_notokenapi 0\\n# HELP emsesp_settings_readonlymode readonlyMode\\n# TYPE emsesp_settings_readonlymode "
"emsesp_settings_dallasparasite dallasParasite\\n# TYPE emsesp_settings_dallasparasite gauge\\nemsesp_settings_dallasparasite 0\\n# HELP " "gauge\\nemsesp_settings_readonlymode 0\\n# HELP emsesp_settings_fahrenheit fahrenheit\\n# TYPE emsesp_settings_fahrenheit "
"emsesp_settings_boolformat boolFormat\\n# TYPE emsesp_settings_boolformat gauge\\nemsesp_settings_boolformat 1\\n# HELP emsesp_settings_booldashboard " "gauge\\nemsesp_settings_fahrenheit 0\\n# HELP emsesp_settings_dallasparasite dallasParasite\\n# TYPE emsesp_settings_dallasparasite "
"boolDashboard\\n# TYPE emsesp_settings_booldashboard gauge\\nemsesp_settings_booldashboard 1\\n# HELP emsesp_settings_enumformat enumFormat\\n# TYPE " "gauge\\nemsesp_settings_dallasparasite 0\\n# HELP emsesp_settings_boolformat boolFormat\\n# TYPE emsesp_settings_boolformat "
"emsesp_settings_enumformat gauge\\nemsesp_settings_enumformat 1\\n# HELP emsesp_settings_analogenabled analogEnabled\\n# TYPE " "gauge\\nemsesp_settings_boolformat 1\\n# HELP emsesp_settings_booldashboard boolDashboard\\n# TYPE emsesp_settings_booldashboard "
"emsesp_settings_analogenabled gauge\\nemsesp_settings_analogenabled 1\\n# HELP emsesp_settings_telnetenabled telnetEnabled\\n# TYPE " "gauge\\nemsesp_settings_booldashboard 1\\n# HELP emsesp_settings_enumformat enumFormat\\n# TYPE emsesp_settings_enumformat "
"emsesp_settings_telnetenabled gauge\\nemsesp_settings_telnetenabled 1\\n# HELP emsesp_settings_maxweblogbuffer maxWebLogBuffer\\n# TYPE " "gauge\\nemsesp_settings_enumformat 1\\n# HELP emsesp_settings_analogenabled analogEnabled\\n# TYPE emsesp_settings_analogenabled "
"emsesp_settings_maxweblogbuffer gauge\\nemsesp_settings_maxweblogbuffer 25\\n# HELP emsesp_settings_modbusenabled modbusEnabled\\n# TYPE " "gauge\\nemsesp_settings_analogenabled 1\\n# HELP emsesp_settings_telnetenabled telnetEnabled\\n# TYPE emsesp_settings_telnetenabled "
"emsesp_settings_modbusenabled gauge\\nemsesp_settings_modbusenabled 0\\n# HELP emsesp_settings_forceheatingoff forceHeatingOff\\n# TYPE " "gauge\\nemsesp_settings_telnetenabled 1\\n# HELP emsesp_settings_maxweblogbuffer maxWebLogBuffer\\n# TYPE emsesp_settings_maxweblogbuffer "
"emsesp_settings_forceheatingoff gauge\\nemsesp_settings_forceheatingoff 0\\n# HELP emsesp_settings_developermode developerMode\\n# TYPE " "gauge\\nemsesp_settings_maxweblogbuffer 25\\n# HELP emsesp_settings_modbusenabled modbusEnabled\\n# TYPE emsesp_settings_modbusenabled "
"emsesp_settings_developermode gauge\\nemsesp_settings_developermode 0\\n# HELP emsesp_settings_info info\\n# TYPE emsesp_settings_info " "gauge\\nemsesp_settings_modbusenabled 0\\n# HELP emsesp_settings_forceheatingoff forceHeatingOff\\n# TYPE emsesp_settings_forceheatingoff "
"gauge\\nemsesp_settings_forceheatingoff 0\\n# HELP emsesp_settings_developermode developerMode\\n# TYPE emsesp_settings_developermode "
"gauge\\nemsesp_settings_developermode 0\\n# HELP emsesp_settings_info info\\n# TYPE emsesp_settings_info "
"gauge\\nemsesp_settings_info{boardprofile=\\\"S32\\\", locale=\\\"en\\\"} 1\\n# HELP emsesp_device_productid productID\\n# TYPE " "gauge\\nemsesp_settings_info{boardprofile=\\\"S32\\\", locale=\\\"en\\\"} 1\\n# HELP emsesp_device_productid productID\\n# TYPE "
"emsesp_device_productid gauge\\nemsesp_device_productid{type=\\\"boiler\\\", name=\\\"My Custom Boiler\\\", deviceid=\\\"0x08\\\", " "emsesp_device_productid gauge\\nemsesp_device_productid{type=\\\"boiler\\\", name=\\\"My Custom Boiler\\\", deviceid=\\\"0x08\\\", "
"version=\\\"01.00\\\"} 123\\n# HELP emsesp_device_entities entities\\n# TYPE emsesp_device_entities " "version=\\\"01.00\\\"} 123\\n# HELP emsesp_device_entities entities\\n# TYPE emsesp_device_entities "