Merge remote-tracking branch 'upstream/dev' into dev

This commit is contained in:
hpanther
2020-11-14 22:54:53 +01:00
66 changed files with 2648 additions and 1854 deletions

1
.gitignore vendored
View File

@@ -22,3 +22,4 @@ firmware
/lib/framework/WWWData.h
/interface/build/
/interface/node_modules/
.VSCodeCounter/

View File

@@ -1,13 +1,21 @@
# Changelog
### Added
- function keys in editor: cursor, del, pos1, end. F1=help, F2=show, F10=report
- function keys in editor: cursor, del, home, end. F1=help, F2=show, and other shortcuts
- SM100 pump working time and energy units
- heating curve parameters for RC300
- `wwonetime` for RC300 thermostat
### Fixed
- mixer IPM pumpstatus
- Mixing devices in HA were incorrectly named
### Changed
- optimized MQTT for HA to reduce mem fragmentation issues
- change syslog settings without reboot
- HA-config split in smaller blocks
- commands `fetch` and `publish [ha]` as call
- mqtt json package size
### Removed
- old scripts

31
debug_pio_local.ini Normal file
View File

@@ -0,0 +1,31 @@
[platformio]
; default_envs = esp32-local
default_envs = esp8266-local
[env]
; upload_port = COM3
upload_port = COM7
; upload_protocol = espota
; upload_flags =
; --port=8266
; --auth=ems-esp-neo
; upload_port = ems-esp.local
[common]
debug_flags = -DEMSESP_DEBUG
[env:esp32-local]
monitor_filters = esp32_exception_decoder
debug_tool = esp-prog
debug_init_break = tbreak setup
build_type = debug
extra_scripts =
; pre:scripts/build_interface.py
[env:esp8266-local]
monitor_filters = esp8266_exception_decoder
extra_scripts =
; pre:scripts/build_interface.py
scripts/main_script.py

View File

@@ -1,6 +1,25 @@
[platformio]
; default_envs = esp32-local
default_envs = esp8266-local
[env]
upload_protocol = espota
upload_flags =
--port=8266
--auth=ems-esp-neo
upload_port = ems-esp.local
; upload_port = COM3
upload_port = COM7
; upload_protocol = espota
; upload_flags =
; --port=8266
; --auth=ems-esp-neo
; upload_port = ems-esp.local
[common]
[env:esp32-local]
extra_scripts =
; pre:scripts/build_interface.py
[env:esp8266-local]
extra_scripts =
; pre:scripts/build_interface.py
scripts/main_script.py

File diff suppressed because it is too large Load Diff

View File

@@ -3,30 +3,30 @@
"version": "0.1.0",
"private": true,
"dependencies": {
"@material-ui/core": "^4.9.8",
"@material-ui/core": "^4.11.0",
"@material-ui/icons": "^4.9.1",
"@types/jwt-decode": "^2.2.1",
"@types/lodash": "^4.14.157",
"@types/jwt-decode": "^3.1.0",
"@types/lodash": "^4.14.165",
"@types/node": "^12.12.32",
"@types/react": "^16.9.27",
"@types/react-dom": "^16.9.5",
"@types/react-material-ui-form-validator": "^2.0.5",
"@types/react-router": "^5.1.3",
"@types/react-router-dom": "^5.1.3",
"@types/react": "^16.9.56",
"@types/react-dom": "^16.9.9",
"@types/react-material-ui-form-validator": "^2.1.0",
"@types/react-router": "^5.1.8",
"@types/react-router-dom": "^5.1.6",
"compression-webpack-plugin": "^4.0.0",
"jwt-decode": "^2.2.0",
"lodash": "^4.17.19",
"mime-types": "^2.1.25",
"moment": "^2.26.0",
"notistack": "^0.9.17",
"react": "^16.13.1",
"react-dom": "^16.13.1",
"react-dropzone": "^11.0.1",
"react-form-validator-core": "^0.6.4",
"react-material-ui-form-validator": "^2.0.10",
"react-router": "^5.1.2",
"react-router-dom": "^5.1.2",
"react-scripts": "3.4.3",
"jwt-decode": "^3.1.1",
"lodash": "^4.17.20",
"mime-types": "^2.1.27",
"moment": "^2.29.1",
"notistack": "^1.0.1",
"react": "^16.14.0",
"react-dom": "^16.14.0",
"react-dropzone": "^11.2.4",
"react-form-validator-core": "^1.0.0",
"react-material-ui-form-validator": "^2.1.1",
"react-router": "^5.2.0",
"react-router-dom": "^5.2.0",
"react-scripts": "3.4.4",
"sockette": "^2.0.6",
"typescript": "^4.0.2",
"zlib": "^1.0.5"

View File

@@ -9,7 +9,7 @@ import { AuthenticationContext, Me } from './AuthenticationContext';
import FullScreenLoading from '../components/FullScreenLoading';
import { withFeatures, WithFeaturesProps } from '../features/FeaturesContext';
export const decodeMeJWT = (accessToken: string): Me => jwtDecode(accessToken);
export const decodeMeJWT = (accessToken: string): Me => jwtDecode(accessToken) as Me;
interface AuthenticationWrapperState {
context: AuthenticationContext;
@@ -59,7 +59,7 @@ class AuthenticationWrapper extends React.Component<AuthenticationWrapperProps,
}
refresh = () => {
// proddy removed
// commented out, always need security - proddy
// if (!this.props.features.security) {
// this.setState({ initialized: true, context: { ...this.state.context, me: { admin: true, username: "admin" } } });
// return;
@@ -107,4 +107,4 @@ class AuthenticationWrapper extends React.Component<AuthenticationWrapperProps,
}
export default withFeatures(withSnackbar(AuthenticationWrapper))
export default withFeatures(withSnackbar(AuthenticationWrapper))

View File

@@ -1,8 +1,8 @@
import React from 'react';
import MenuItem from '@material-ui/core/MenuItem';
type TimeZones = {
[name: string]: string
type TimeZones = {
[name: string]: string
};
export const TIME_ZONES: TimeZones = {
@@ -19,14 +19,14 @@ export const TIME_ZONES: TimeZones = {
"Africa/Brazzaville": "WAT-1",
"Africa/Bujumbura": "CAT-2",
"Africa/Cairo": "EET-2",
"Africa/Casablanca": "<+01>-1",
"Africa/Casablanca": "UNK-1",
"Africa/Ceuta": "CET-1CEST,M3.5.0,M10.5.0/3",
"Africa/Conakry": "GMT0",
"Africa/Dakar": "GMT0",
"Africa/Dar_es_Salaam": "EAT-3",
"Africa/Djibouti": "EAT-3",
"Africa/Douala": "WAT-1",
"Africa/El_Aaiun": "<+01>-1",
"Africa/El_Aaiun": "UNK-1",
"Africa/Freetown": "GMT0",
"Africa/Gaborone": "CAT-2",
"Africa/Harare": "CAT-2",
@@ -62,63 +62,63 @@ export const TIME_ZONES: TimeZones = {
"America/Anchorage": "AKST9AKDT,M3.2.0,M11.1.0",
"America/Anguilla": "AST4",
"America/Antigua": "AST4",
"America/Araguaina": "<-03>3",
"America/Argentina/Buenos_Aires": "<-03>3",
"America/Argentina/Catamarca": "<-03>3",
"America/Argentina/Cordoba": "<-03>3",
"America/Argentina/Jujuy": "<-03>3",
"America/Argentina/La_Rioja": "<-03>3",
"America/Argentina/Mendoza": "<-03>3",
"America/Argentina/Rio_Gallegos": "<-03>3",
"America/Argentina/Salta": "<-03>3",
"America/Argentina/San_Juan": "<-03>3",
"America/Argentina/San_Luis": "<-03>3",
"America/Argentina/Tucuman": "<-03>3",
"America/Argentina/Ushuaia": "<-03>3",
"America/Araguaina": "UNK3",
"America/Argentina/Buenos_Aires": "UNK3",
"America/Argentina/Catamarca": "UNK3",
"America/Argentina/Cordoba": "UNK3",
"America/Argentina/Jujuy": "UNK3",
"America/Argentina/La_Rioja": "UNK3",
"America/Argentina/Mendoza": "UNK3",
"America/Argentina/Rio_Gallegos": "UNK3",
"America/Argentina/Salta": "UNK3",
"America/Argentina/San_Juan": "UNK3",
"America/Argentina/San_Luis": "UNK3",
"America/Argentina/Tucuman": "UNK3",
"America/Argentina/Ushuaia": "UNK3",
"America/Aruba": "AST4",
"America/Asuncion": "<-04>4<-03>,M10.1.0/0,M3.4.0/0",
"America/Asuncion": "UNK4UNK,M10.1.0/0,M3.4.0/0",
"America/Atikokan": "EST5",
"America/Bahia": "<-03>3",
"America/Bahia": "UNK3",
"America/Bahia_Banderas": "CST6CDT,M4.1.0,M10.5.0",
"America/Barbados": "AST4",
"America/Belem": "<-03>3",
"America/Belem": "UNK3",
"America/Belize": "CST6",
"America/Blanc-Sablon": "AST4",
"America/Boa_Vista": "<-04>4",
"America/Bogota": "<-05>5",
"America/Boa_Vista": "UNK4",
"America/Bogota": "UNK5",
"America/Boise": "MST7MDT,M3.2.0,M11.1.0",
"America/Cambridge_Bay": "MST7MDT,M3.2.0,M11.1.0",
"America/Campo_Grande": "<-04>4",
"America/Campo_Grande": "UNK4",
"America/Cancun": "EST5",
"America/Caracas": "<-04>4",
"America/Cayenne": "<-03>3",
"America/Caracas": "UNK4",
"America/Cayenne": "UNK3",
"America/Cayman": "EST5",
"America/Chicago": "CST6CDT,M3.2.0,M11.1.0",
"America/Chihuahua": "MST7MDT,M4.1.0,M10.5.0",
"America/Costa_Rica": "CST6",
"America/Creston": "MST7",
"America/Cuiaba": "<-04>4",
"America/Cuiaba": "UNK4",
"America/Curacao": "AST4",
"America/Danmarkshavn": "GMT0",
"America/Dawson": "PST8PDT,M3.2.0,M11.1.0",
"America/Dawson": "MST7",
"America/Dawson_Creek": "MST7",
"America/Denver": "MST7MDT,M3.2.0,M11.1.0",
"America/Detroit": "EST5EDT,M3.2.0,M11.1.0",
"America/Dominica": "AST4",
"America/Edmonton": "MST7MDT,M3.2.0,M11.1.0",
"America/Eirunepe": "<-05>5",
"America/Eirunepe": "UNK5",
"America/El_Salvador": "CST6",
"America/Fortaleza": "<-03>3",
"America/Fort_Nelson": "MST7",
"America/Fortaleza": "UNK3",
"America/Glace_Bay": "AST4ADT,M3.2.0,M11.1.0",
"America/Godthab": "<-03>3<-02>,M3.5.0/-2,M10.5.0/-1",
"America/Godthab": "UNK3UNK,M3.5.0/-2,M10.5.0/-1",
"America/Goose_Bay": "AST4ADT,M3.2.0,M11.1.0",
"America/Grand_Turk": "EST5EDT,M3.2.0,M11.1.0",
"America/Grenada": "AST4",
"America/Guadeloupe": "AST4",
"America/Guatemala": "CST6",
"America/Guayaquil": "<-05>5",
"America/Guyana": "<-04>4",
"America/Guayaquil": "UNK5",
"America/Guyana": "UNK4",
"America/Halifax": "AST4ADT,M3.2.0,M11.1.0",
"America/Havana": "CST5CDT,M3.2.0/0,M11.1.0/1",
"America/Hermosillo": "MST7",
@@ -137,13 +137,13 @@ export const TIME_ZONES: TimeZones = {
"America/Kentucky/Louisville": "EST5EDT,M3.2.0,M11.1.0",
"America/Kentucky/Monticello": "EST5EDT,M3.2.0,M11.1.0",
"America/Kralendijk": "AST4",
"America/La_Paz": "<-04>4",
"America/Lima": "<-05>5",
"America/La_Paz": "UNK4",
"America/Lima": "UNK5",
"America/Los_Angeles": "PST8PDT,M3.2.0,M11.1.0",
"America/Lower_Princes": "AST4",
"America/Maceio": "<-03>3",
"America/Maceio": "UNK3",
"America/Managua": "CST6",
"America/Manaus": "<-04>4",
"America/Manaus": "UNK4",
"America/Marigot": "AST4",
"America/Martinique": "AST4",
"America/Matamoros": "CST6CDT,M3.2.0,M11.1.0",
@@ -152,41 +152,41 @@ export const TIME_ZONES: TimeZones = {
"America/Merida": "CST6CDT,M4.1.0,M10.5.0",
"America/Metlakatla": "AKST9AKDT,M3.2.0,M11.1.0",
"America/Mexico_City": "CST6CDT,M4.1.0,M10.5.0",
"America/Miquelon": "<-03>3<-02>,M3.2.0,M11.1.0",
"America/Miquelon": "UNK3UNK,M3.2.0,M11.1.0",
"America/Moncton": "AST4ADT,M3.2.0,M11.1.0",
"America/Monterrey": "CST6CDT,M4.1.0,M10.5.0",
"America/Montevideo": "<-03>3",
"America/Montevideo": "UNK3",
"America/Montreal": "EST5EDT,M3.2.0,M11.1.0",
"America/Montserrat": "AST4",
"America/Nassau": "EST5EDT,M3.2.0,M11.1.0",
"America/New_York": "EST5EDT,M3.2.0,M11.1.0",
"America/Nipigon": "EST5EDT,M3.2.0,M11.1.0",
"America/Nome": "AKST9AKDT,M3.2.0,M11.1.0",
"America/Noronha": "<-02>2",
"America/Noronha": "UNK2",
"America/North_Dakota/Beulah": "CST6CDT,M3.2.0,M11.1.0",
"America/North_Dakota/Center": "CST6CDT,M3.2.0,M11.1.0",
"America/North_Dakota/New_Salem": "CST6CDT,M3.2.0,M11.1.0",
"America/Ojinaga": "MST7MDT,M3.2.0,M11.1.0",
"America/Panama": "EST5",
"America/Pangnirtung": "EST5EDT,M3.2.0,M11.1.0",
"America/Paramaribo": "<-03>3",
"America/Paramaribo": "UNK3",
"America/Phoenix": "MST7",
"America/Port-au-Prince": "EST5EDT,M3.2.0,M11.1.0",
"America/Port_of_Spain": "AST4",
"America/Porto_Velho": "<-04>4",
"America/Porto_Velho": "UNK4",
"America/Puerto_Rico": "AST4",
"America/Punta_Arenas": "<-03>3",
"America/Punta_Arenas": "UNK3",
"America/Rainy_River": "CST6CDT,M3.2.0,M11.1.0",
"America/Rankin_Inlet": "CST6CDT,M3.2.0,M11.1.0",
"America/Recife": "<-03>3",
"America/Recife": "UNK3",
"America/Regina": "CST6",
"America/Resolute": "CST6CDT,M3.2.0,M11.1.0",
"America/Rio_Branco": "<-05>5",
"America/Santarem": "<-03>3",
"America/Santiago": "<-04>4<-03>,M9.1.6/24,M4.1.6/24",
"America/Rio_Branco": "UNK5",
"America/Santarem": "UNK3",
"America/Santiago": "UNK4UNK,M9.1.6/24,M4.1.6/24",
"America/Santo_Domingo": "AST4",
"America/Sao_Paulo": "<-03>3",
"America/Scoresbysund": "<-01>1<+00>,M3.5.0/0,M10.5.0/1",
"America/Sao_Paulo": "UNK3",
"America/Scoresbysund": "UNK1UNK,M3.5.0/0,M10.5.0/1",
"America/Sitka": "AKST9AKDT,M3.2.0,M11.1.0",
"America/St_Barthelemy": "AST4",
"America/St_Johns": "NST3:30NDT,M3.2.0,M11.1.0",
@@ -202,129 +202,164 @@ export const TIME_ZONES: TimeZones = {
"America/Toronto": "EST5EDT,M3.2.0,M11.1.0",
"America/Tortola": "AST4",
"America/Vancouver": "PST8PDT,M3.2.0,M11.1.0",
"America/Whitehorse": "PST8PDT,M3.2.0,M11.1.0",
"America/Whitehorse": "MST7",
"America/Winnipeg": "CST6CDT,M3.2.0,M11.1.0",
"America/Yakutat": "AKST9AKDT,M3.2.0,M11.1.0",
"America/Yellowknife": "MST7MDT,M3.2.0,M11.1.0",
"Antarctica/Casey": "<+08>-8",
"Antarctica/Davis": "<+07>-7",
"Antarctica/DumontDUrville": "<+10>-10",
"Antarctica/Macquarie": "<+11>-11",
"Antarctica/Mawson": "<+05>-5",
"Antarctica/Casey": "UNK-8",
"Antarctica/Davis": "UNK-7",
"Antarctica/DumontDUrville": "UNK-10",
"Antarctica/Macquarie": "UNK-11",
"Antarctica/Mawson": "UNK-5",
"Antarctica/McMurdo": "NZST-12NZDT,M9.5.0,M4.1.0/3",
"Antarctica/Palmer": "<-03>3",
"Antarctica/Rothera": "<-03>3",
"Antarctica/Syowa": "<+03>-3",
"Antarctica/Troll": "<+00>0<+02>-2,M3.5.0/1,M10.5.0/3",
"Antarctica/Vostok": "<+06>-6",
"Antarctica/Palmer": "UNK3",
"Antarctica/Rothera": "UNK3",
"Antarctica/Syowa": "UNK-3",
"Antarctica/Troll": "UNK0UNK-2,M3.5.0/1,M10.5.0/3",
"Antarctica/Vostok": "UNK-6",
"Arctic/Longyearbyen": "CET-1CEST,M3.5.0,M10.5.0/3",
"Asia/Aden": "<+03>-3",
"Asia/Almaty": "<+06>-6",
"Asia/Aden": "UNK-3",
"Asia/Almaty": "UNK-6",
"Asia/Amman": "EET-2EEST,M3.5.4/24,M10.5.5/1",
"Asia/Anadyr": "<+12>-12",
"Asia/Aqtau": "<+05>-5",
"Asia/Aqtobe": "<+05>-5",
"Asia/Ashgabat": "<+05>-5",
"Asia/Atyrau": "<+05>-5",
"Asia/Baghdad": "<+03>-3",
"Asia/Bahrain": "<+03>-3",
"Asia/Baku": "<+04>-4",
"Asia/Bangkok": "<+07>-7",
"Asia/Barnaul": "<+07>-7",
"Asia/Anadyr": "UNK-12",
"Asia/Aqtau": "UNK-5",
"Asia/Aqtobe": "UNK-5",
"Asia/Ashgabat": "UNK-5",
"Asia/Atyrau": "UNK-5",
"Asia/Baghdad": "UNK-3",
"Asia/Bahrain": "UNK-3",
"Asia/Baku": "UNK-4",
"Asia/Bangkok": "UNK-7",
"Asia/Barnaul": "UNK-7",
"Asia/Beirut": "EET-2EEST,M3.5.0/0,M10.5.0/0",
"Asia/Bishkek": "<+06>-6",
"Asia/Brunei": "<+08>-8",
"Asia/Chita": "<+09>-9",
"Asia/Choibalsan": "<+08>-8",
"Asia/Colombo": "<+0530>-5:30",
"Asia/Bishkek": "UNK-6",
"Asia/Brunei": "UNK-8",
"Asia/Chita": "UNK-9",
"Asia/Choibalsan": "UNK-8",
"Asia/Colombo": "UNK-5:30",
"Asia/Damascus": "EET-2EEST,M3.5.5/0,M10.5.5/0",
"Asia/Dhaka": "<+06>-6",
"Asia/Dili": "<+09>-9",
"Asia/Dubai": "<+04>-4",
"Asia/Dushanbe": "<+05>-5",
"Asia/Dhaka": "UNK-6",
"Asia/Dili": "UNK-9",
"Asia/Dubai": "UNK-4",
"Asia/Dushanbe": "UNK-5",
"Asia/Famagusta": "EET-2EEST,M3.5.0/3,M10.5.0/4",
"Asia/Gaza": "EET-2EEST,M3.5.5/0,M10.5.6/1",
"Asia/Hebron": "EET-2EEST,M3.5.5/0,M10.5.6/1",
"Asia/Ho_Chi_Minh": "<+07>-7",
"Asia/Ho_Chi_Minh": "UNK-7",
"Asia/Hong_Kong": "HKT-8",
"Asia/Hovd": "<+07>-7",
"Asia/Irkutsk": "<+08>-8",
"Asia/Hovd": "UNK-7",
"Asia/Irkutsk": "UNK-8",
"Asia/Jakarta": "WIB-7",
"Asia/Jayapura": "WIT-9",
"Asia/Jerusalem": "IST-2IDT,M3.4.4/26,M10.5.0",
"Asia/Kabul": "<+0430>-4:30",
"Asia/Kamchatka": "<+12>-12",
"Asia/Kabul": "UNK-4:30",
"Asia/Kamchatka": "UNK-12",
"Asia/Karachi": "PKT-5",
"Asia/Kathmandu": "<+0545>-5:45",
"Asia/Khandyga": "<+09>-9",
"Asia/Kathmandu": "UNK-5:45",
"Asia/Khandyga": "UNK-9",
"Asia/Kolkata": "IST-5:30",
"Asia/Krasnoyarsk": "<+07>-7",
"Asia/Kuala_Lumpur": "<+08>-8",
"Asia/Kuching": "<+08>-8",
"Asia/Kuwait": "<+03>-3",
"Asia/Krasnoyarsk": "UNK-7",
"Asia/Kuala_Lumpur": "UNK-8",
"Asia/Kuching": "UNK-8",
"Asia/Kuwait": "UNK-3",
"Asia/Macau": "CST-8",
"Asia/Magadan": "<+11>-11",
"Asia/Magadan": "UNK-11",
"Asia/Makassar": "WITA-8",
"Asia/Manila": "PST-8",
"Asia/Muscat": "<+04>-4",
"Asia/Muscat": "UNK-4",
"Asia/Nicosia": "EET-2EEST,M3.5.0/3,M10.5.0/4",
"Asia/Novokuznetsk": "<+07>-7",
"Asia/Novosibirsk": "<+07>-7",
"Asia/Omsk": "<+06>-6",
"Asia/Oral": "<+05>-5",
"Asia/Phnom_Penh": "<+07>-7",
"Asia/Novokuznetsk": "UNK-7",
"Asia/Novosibirsk": "UNK-7",
"Asia/Omsk": "UNK-6",
"Asia/Oral": "UNK-5",
"Asia/Phnom_Penh": "UNK-7",
"Asia/Pontianak": "WIB-7",
"Asia/Pyongyang": "KST-9",
"Asia/Qatar": "<+03>-3",
"Asia/Qyzylorda": "<+05>-5",
"Asia/Riyadh": "<+03>-3",
"Asia/Sakhalin": "<+11>-11",
"Asia/Samarkand": "<+05>-5",
"Asia/Qatar": "UNK-3",
"Asia/Qyzylorda": "UNK-5",
"Asia/Riyadh": "UNK-3",
"Asia/Sakhalin": "UNK-11",
"Asia/Samarkand": "UNK-5",
"Asia/Seoul": "KST-9",
"Asia/Shanghai": "CST-8",
"Asia/Singapore": "<+08>-8",
"Asia/Srednekolymsk": "<+11>-11",
"Asia/Singapore": "UNK-8",
"Asia/Srednekolymsk": "UNK-11",
"Asia/Taipei": "CST-8",
"Asia/Tashkent": "<+05>-5",
"Asia/Tbilisi": "<+04>-4",
"Asia/Tehran": "<+0330>-3:30<+0430>,J79/24,J263/24",
"Asia/Thimphu": "<+06>-6",
"Asia/Tashkent": "UNK-5",
"Asia/Tbilisi": "UNK-4",
"Asia/Tehran": "UNK-3:30UNK,J79/24,J263/24",
"Asia/Thimphu": "UNK-6",
"Asia/Tokyo": "JST-9",
"Asia/Tomsk": "<+07>-7",
"Asia/Ulaanbaatar": "<+08>-8",
"Asia/Urumqi": "<+06>-6",
"Asia/Ust-Nera": "<+10>-10",
"Asia/Vientiane": "<+07>-7",
"Asia/Vladivostok": "<+10>-10",
"Asia/Yakutsk": "<+09>-9",
"Asia/Yangon": "<+0630>-6:30",
"Asia/Yekaterinburg": "<+05>-5",
"Asia/Yerevan": "<+04>-4",
"Atlantic/Azores": "<-01>1<+00>,M3.5.0/0,M10.5.0/1",
"Asia/Tomsk": "UNK-7",
"Asia/Ulaanbaatar": "UNK-8",
"Asia/Urumqi": "UNK-6",
"Asia/Ust-Nera": "UNK-10",
"Asia/Vientiane": "UNK-7",
"Asia/Vladivostok": "UNK-10",
"Asia/Yakutsk": "UNK-9",
"Asia/Yangon": "UNK-6:30",
"Asia/Yekaterinburg": "UNK-5",
"Asia/Yerevan": "UNK-4",
"Atlantic/Azores": "UNK1UNK,M3.5.0/0,M10.5.0/1",
"Atlantic/Bermuda": "AST4ADT,M3.2.0,M11.1.0",
"Atlantic/Canary": "WET0WEST,M3.5.0/1,M10.5.0",
"Atlantic/Cape_Verde": "<-01>1",
"Atlantic/Cape_Verde": "UNK1",
"Atlantic/Faroe": "WET0WEST,M3.5.0/1,M10.5.0",
"Atlantic/Madeira": "WET0WEST,M3.5.0/1,M10.5.0",
"Atlantic/Reykjavik": "GMT0",
"Atlantic/South_Georgia": "<-02>2",
"Atlantic/Stanley": "<-03>3",
"Atlantic/South_Georgia": "UNK2",
"Atlantic/St_Helena": "GMT0",
"Atlantic/Stanley": "UNK3",
"Australia/Adelaide": "ACST-9:30ACDT,M10.1.0,M4.1.0/3",
"Australia/Brisbane": "AEST-10",
"Australia/Broken_Hill": "ACST-9:30ACDT,M10.1.0,M4.1.0/3",
"Australia/Currie": "AEST-10AEDT,M10.1.0,M4.1.0/3",
"Australia/Darwin": "ACST-9:30",
"Australia/Eucla": "<+0845>-8:45",
"Australia/Eucla": "UNK-8:45",
"Australia/Hobart": "AEST-10AEDT,M10.1.0,M4.1.0/3",
"Australia/Lindeman": "AEST-10",
"Australia/Lord_Howe": "<+1030>-10:30<+11>-11,M10.1.0,M4.1.0",
"Australia/Lord_Howe": "UNK-10:30UNK-11,M10.1.0,M4.1.0",
"Australia/Melbourne": "AEST-10AEDT,M10.1.0,M4.1.0/3",
"Australia/Perth": "AWST-8",
"Australia/Sydney": "AEST-10AEDT,M10.1.0,M4.1.0/3",
"Etc/GMT": "GMT0",
"Etc/GMT+0": "GMT0",
"Etc/GMT+1": "UNK1",
"Etc/GMT+10": "UNK10",
"Etc/GMT+11": "UNK11",
"Etc/GMT+12": "UNK12",
"Etc/GMT+2": "UNK2",
"Etc/GMT+3": "UNK3",
"Etc/GMT+4": "UNK4",
"Etc/GMT+5": "UNK5",
"Etc/GMT+6": "UNK6",
"Etc/GMT+7": "UNK7",
"Etc/GMT+8": "UNK8",
"Etc/GMT+9": "UNK9",
"Etc/GMT-0": "GMT0",
"Etc/GMT-1": "UNK-1",
"Etc/GMT-10": "UNK-10",
"Etc/GMT-11": "UNK-11",
"Etc/GMT-12": "UNK-12",
"Etc/GMT-13": "UNK-13",
"Etc/GMT-14": "UNK-14",
"Etc/GMT-2": "UNK-2",
"Etc/GMT-3": "UNK-3",
"Etc/GMT-4": "UNK-4",
"Etc/GMT-5": "UNK-5",
"Etc/GMT-6": "UNK-6",
"Etc/GMT-7": "UNK-7",
"Etc/GMT-8": "UNK-8",
"Etc/GMT-9": "UNK-9",
"Etc/GMT0": "GMT0",
"Etc/Greenwich": "GMT0",
"Etc/UCT": "UTC0",
"Etc/UTC": "UTC0",
"Etc/Universal": "UTC0",
"Etc/Zulu": "UTC0",
"Europe/Amsterdam": "CET-1CEST,M3.5.0,M10.5.0/3",
"Europe/Andorra": "CET-1CEST,M3.5.0,M10.5.0/3",
"Europe/Astrakhan": "<+04>-4",
"Europe/Astrakhan": "UNK-4",
"Europe/Athens": "EET-2EEST,M3.5.0/3,M10.5.0/4",
"Europe/Belgrade": "CET-1CEST,M3.5.0,M10.5.0/3",
"Europe/Berlin": "CET-1CEST,M3.5.0,M10.5.0/3",
@@ -340,11 +375,11 @@ export const TIME_ZONES: TimeZones = {
"Europe/Guernsey": "GMT0BST,M3.5.0/1,M10.5.0",
"Europe/Helsinki": "EET-2EEST,M3.5.0/3,M10.5.0/4",
"Europe/Isle_of_Man": "GMT0BST,M3.5.0/1,M10.5.0",
"Europe/Istanbul": "<+03>-3",
"Europe/Istanbul": "UNK-3",
"Europe/Jersey": "GMT0BST,M3.5.0/1,M10.5.0",
"Europe/Kaliningrad": "EET-2",
"Europe/Kiev": "EET-2EEST,M3.5.0/3,M10.5.0/4",
"Europe/Kirov": "<+03>-3",
"Europe/Kirov": "UNK-3",
"Europe/Lisbon": "WET0WEST,M3.5.0/1,M10.5.0",
"Europe/Ljubljana": "CET-1CEST,M3.5.0,M10.5.0/3",
"Europe/London": "GMT0BST,M3.5.0/1,M10.5.0",
@@ -352,7 +387,7 @@ export const TIME_ZONES: TimeZones = {
"Europe/Madrid": "CET-1CEST,M3.5.0,M10.5.0/3",
"Europe/Malta": "CET-1CEST,M3.5.0,M10.5.0/3",
"Europe/Mariehamn": "EET-2EEST,M3.5.0/3,M10.5.0/4",
"Europe/Minsk": "<+03>-3",
"Europe/Minsk": "UNK-3",
"Europe/Monaco": "CET-1CEST,M3.5.0,M10.5.0/3",
"Europe/Moscow": "MSK-3",
"Europe/Oslo": "CET-1CEST,M3.5.0,M10.5.0/3",
@@ -361,111 +396,76 @@ export const TIME_ZONES: TimeZones = {
"Europe/Prague": "CET-1CEST,M3.5.0,M10.5.0/3",
"Europe/Riga": "EET-2EEST,M3.5.0/3,M10.5.0/4",
"Europe/Rome": "CET-1CEST,M3.5.0,M10.5.0/3",
"Europe/Samara": "<+04>-4",
"Europe/Samara": "UNK-4",
"Europe/San_Marino": "CET-1CEST,M3.5.0,M10.5.0/3",
"Europe/Sarajevo": "CET-1CEST,M3.5.0,M10.5.0/3",
"Europe/Saratov": "<+04>-4",
"Europe/Saratov": "UNK-4",
"Europe/Simferopol": "MSK-3",
"Europe/Skopje": "CET-1CEST,M3.5.0,M10.5.0/3",
"Europe/Sofia": "EET-2EEST,M3.5.0/3,M10.5.0/4",
"Europe/Stockholm": "CET-1CEST,M3.5.0,M10.5.0/3",
"Europe/Tallinn": "EET-2EEST,M3.5.0/3,M10.5.0/4",
"Europe/Tirane": "CET-1CEST,M3.5.0,M10.5.0/3",
"Europe/Ulyanovsk": "<+04>-4",
"Europe/Ulyanovsk": "UNK-4",
"Europe/Uzhgorod": "EET-2EEST,M3.5.0/3,M10.5.0/4",
"Europe/Vaduz": "CET-1CEST,M3.5.0,M10.5.0/3",
"Europe/Vatican": "CET-1CEST,M3.5.0,M10.5.0/3",
"Europe/Vienna": "CET-1CEST,M3.5.0,M10.5.0/3",
"Europe/Vilnius": "EET-2EEST,M3.5.0/3,M10.5.0/4",
"Europe/Volgograd": "<+04>-4",
"Europe/Volgograd": "UNK-4",
"Europe/Warsaw": "CET-1CEST,M3.5.0,M10.5.0/3",
"Europe/Zagreb": "CET-1CEST,M3.5.0,M10.5.0/3",
"Europe/Zaporozhye": "EET-2EEST,M3.5.0/3,M10.5.0/4",
"Europe/Zurich": "CET-1CEST,M3.5.0,M10.5.0/3",
"Indian/Antananarivo": "EAT-3",
"Indian/Chagos": "<+06>-6",
"Indian/Christmas": "<+07>-7",
"Indian/Cocos": "<+0630>-6:30",
"Indian/Chagos": "UNK-6",
"Indian/Christmas": "UNK-7",
"Indian/Cocos": "UNK-6:30",
"Indian/Comoro": "EAT-3",
"Indian/Kerguelen": "<+05>-5",
"Indian/Mahe": "<+04>-4",
"Indian/Maldives": "<+05>-5",
"Indian/Mauritius": "<+04>-4",
"Indian/Kerguelen": "UNK-5",
"Indian/Mahe": "UNK-4",
"Indian/Maldives": "UNK-5",
"Indian/Mauritius": "UNK-4",
"Indian/Mayotte": "EAT-3",
"Indian/Reunion": "<+04>-4",
"Pacific/Apia": "<+13>-13<+14>,M9.5.0/3,M4.1.0/4",
"Indian/Reunion": "UNK-4",
"Pacific/Apia": "UNK-13UNK,M9.5.0/3,M4.1.0/4",
"Pacific/Auckland": "NZST-12NZDT,M9.5.0,M4.1.0/3",
"Pacific/Bougainville": "<+11>-11",
"Pacific/Chatham": "<+1245>-12:45<+1345>,M9.5.0/2:45,M4.1.0/3:45",
"Pacific/Chuuk": "<+10>-10",
"Pacific/Easter": "<-06>6<-05>,M9.1.6/22,M4.1.6/22",
"Pacific/Efate": "<+11>-11",
"Pacific/Enderbury": "<+13>-13",
"Pacific/Fakaofo": "<+13>-13",
"Pacific/Fiji": "<+12>-12<+13>,M11.2.0,M1.2.3/99",
"Pacific/Funafuti": "<+12>-12",
"Pacific/Galapagos": "<-06>6",
"Pacific/Gambier": "<-09>9",
"Pacific/Guadalcanal": "<+11>-11",
"Pacific/Bougainville": "UNK-11",
"Pacific/Chatham": "UNK-12:45UNK,M9.5.0/2:45,M4.1.0/3:45",
"Pacific/Chuuk": "UNK-10",
"Pacific/Easter": "UNK6UNK,M9.1.6/22,M4.1.6/22",
"Pacific/Efate": "UNK-11",
"Pacific/Enderbury": "UNK-13",
"Pacific/Fakaofo": "UNK-13",
"Pacific/Fiji": "UNK-12UNK,M11.2.0,M1.2.3/99",
"Pacific/Funafuti": "UNK-12",
"Pacific/Galapagos": "UNK6",
"Pacific/Gambier": "UNK9",
"Pacific/Guadalcanal": "UNK-11",
"Pacific/Guam": "ChST-10",
"Pacific/Honolulu": "HST10",
"Pacific/Kiritimati": "<+14>-14",
"Pacific/Kosrae": "<+11>-11",
"Pacific/Kwajalein": "<+12>-12",
"Pacific/Majuro": "<+12>-12",
"Pacific/Marquesas": "<-0930>9:30",
"Pacific/Kiritimati": "UNK-14",
"Pacific/Kosrae": "UNK-11",
"Pacific/Kwajalein": "UNK-12",
"Pacific/Majuro": "UNK-12",
"Pacific/Marquesas": "UNK9:30",
"Pacific/Midway": "SST11",
"Pacific/Nauru": "<+12>-12",
"Pacific/Niue": "<-11>11",
"Pacific/Norfolk": "<+11>-11<+12>,M10.1.0,M4.1.0/3",
"Pacific/Noumea": "<+11>-11",
"Pacific/Nauru": "UNK-12",
"Pacific/Niue": "UNK11",
"Pacific/Norfolk": "UNK-11UNK,M10.1.0,M4.1.0/3",
"Pacific/Noumea": "UNK-11",
"Pacific/Pago_Pago": "SST11",
"Pacific/Palau": "<+09>-9",
"Pacific/Pitcairn": "<-08>8",
"Pacific/Pohnpei": "<+11>-11",
"Pacific/Port_Moresby": "<+10>-10",
"Pacific/Rarotonga": "<-10>10",
"Pacific/Palau": "UNK-9",
"Pacific/Pitcairn": "UNK8",
"Pacific/Pohnpei": "UNK-11",
"Pacific/Port_Moresby": "UNK-10",
"Pacific/Rarotonga": "UNK10",
"Pacific/Saipan": "ChST-10",
"Pacific/Tahiti": "<-10>10",
"Pacific/Tarawa": "<+12>-12",
"Pacific/Tongatapu": "<+13>-13",
"Pacific/Wake": "<+12>-12",
"Pacific/Wallis": "<+12>-12",
"Etc/GMT": "GMT0",
"Etc/GMT-0": "GMT0",
"Etc/GMT-1": "<+01>-1",
"Etc/GMT-2": "<+02>-2",
"Etc/GMT-3": "<+03>-3",
"Etc/GMT-4": "<+04>-4",
"Etc/GMT-5": "<+05>-5",
"Etc/GMT-6": "<+06>-6",
"Etc/GMT-7": "<+07>-7",
"Etc/GMT-8": "<+08>-8",
"Etc/GMT-9": "<+09>-9",
"Etc/GMT-10": "<+10>-10",
"Etc/GMT-11": "<+11>-11",
"Etc/GMT-12": "<+12>-12",
"Etc/GMT-13": "<+13>-13",
"Etc/GMT-14": "<+14>-14",
"Etc/GMT0": "GMT0",
"Etc/GMT+0": "GMT0",
"Etc/GMT+1": "<-01>1",
"Etc/GMT+2": "<-02>2",
"Etc/GMT+3": "<-03>3",
"Etc/GMT+4": "<-04>4",
"Etc/GMT+5": "<-05>5",
"Etc/GMT+6": "<-06>6",
"Etc/GMT+7": "<-07>7",
"Etc/GMT+8": "<-08>8",
"Etc/GMT+9": "<-09>9",
"Etc/GMT+10": "<-10>10",
"Etc/GMT+11": "<-11>11",
"Etc/GMT+12": "<-12>12",
"Etc/UCT": "UTC0",
"Etc/UTC": "UTC0",
"Etc/Greenwich": "GMT0",
"Etc/Universal": "UTC0",
"Etc/Zulu": "UTC0"
"Pacific/Tahiti": "UNK10",
"Pacific/Tarawa": "UNK-12",
"Pacific/Tongatapu": "UNK-13",
"Pacific/Wake": "UNK-12",
"Pacific/Wallis": "UNK-12"
}
export function selectedTimeZone(label: string, format: string) {
@@ -476,4 +476,4 @@ export function timeZoneSelectItems() {
return Object.keys(TIME_ZONES).map(label => (
<MenuItem key={label} value={label}>{label}</MenuItem>
));
}
}

View File

@@ -209,11 +209,21 @@ function EMSESPSettingsControllerForm(props: EMSESPSettingsControllerFormProps)
<Typography variant="h6" color="primary" >
Syslog
</Typography>
<BlockFormControlLabel
control={
<Checkbox
checked={data.syslog_enabled}
onChange={handleValueChange('syslog_enabled')}
value="syslog_enabled"
/>
}
label="Enable Syslog"
/>
<TextValidator
validators={['isIPOrHostname']}
errorMessages={["Not a valid IP address or hostname"]}
name="syslog_host"
label="Syslog IP/Host (optional)"
label="Syslog IP/Host"
fullWidth
variant="outlined"
value={data.syslog_host}
@@ -229,12 +239,13 @@ function EMSESPSettingsControllerForm(props: EMSESPSettingsControllerFormProps)
margin="normal">
<MenuItem value={-1}>OFF</MenuItem>
<MenuItem value={3}>ERR</MenuItem>
<MenuItem value={5}>NOTICE</MenuItem>
<MenuItem value={6}>INFO</MenuItem>
<MenuItem value={7}>DEBUG</MenuItem>
</SelectValidator>
<TextValidator
validators={['required', 'isNumber', 'minNumber:0', 'maxNumber:65535']}
errorMessages={['Syslog Mark is required', "Must be a number", "Must be 0 or higher (0=off)", "Max value is 65535"]}
errorMessages={['Syslog Mark is required', "Must be a number", "Must be 0 or higher", "Max value is 10"]}
name="syslog_mark_interval"
label="Syslog Mark Interval (seconds, 0=off)"
fullWidth

View File

@@ -1,6 +1,7 @@
export interface EMSESPSettings {
tx_mode: number;
ems_bus_id: number;
syslog_enabled: boolean;
syslog_level: number;
syslog_mark_interval: number;
syslog_host: string;

View File

@@ -1,6 +1,13 @@
ArduinoJson: change log
=======================
v6.17.1 (2020-11-07)
-------
* Fixed error `ambiguous overload for 'operator|'` (issue #1411)
* Fixed `operator|(MemberProxy, JsonObject)` (issue #1415)
* Allowed more than 32767 values in non-embedded mode (issue #1414)
v6.17.0 (2020-10-19)
-------

View File

@@ -0,0 +1,11 @@
# Contribution to ArduinoJson
First, thank you for taking the time to contribute to this project.
You can submit changes via GitHub Pull Requests.
Please:
1. Unit test every change in behavior
2. Use clang-format in "file" mode to format the code
3. Consider using the Continuous Integration (Travis and AppVeyor)

View File

@@ -2,7 +2,7 @@
---
[![arduino-library-badge](https://www.ardu-badge.com/badge/ArduinoJson.svg?version=6.17.0)](https://www.ardu-badge.com/ArduinoJson/6.17.0)
[![arduino-library-badge](https://www.ardu-badge.com/badge/ArduinoJson.svg?version=6.17.1)](https://www.ardu-badge.com/ArduinoJson/6.17.1)
[![Build Status](https://ci.appveyor.com/api/projects/status/m7s53wav1l0abssg/branch/6.x?svg=true)](https://ci.appveyor.com/project/bblanchon/arduinojson/branch/6.x)
[![Build Status](https://travis-ci.org/bblanchon/ArduinoJson.svg?branch=6.x)](https://travis-ci.org/bblanchon/ArduinoJson)
[![Fuzzing Status](https://oss-fuzz-build-logs.storage.googleapis.com/badges/arduinojson.svg)](https://bugs.chromium.org/p/oss-fuzz/issues/list?sort=-opened&can=1&q=proj:arduinojson)

View File

@@ -19,10 +19,13 @@ namespace ARDUINOJSON_NAMESPACE {
template <typename TArray>
class ElementProxy : public VariantOperators<ElementProxy<TArray> >,
public VariantShortcuts<ElementProxy<TArray> >,
public Visitable {
public Visitable,
public VariantTag {
typedef ElementProxy<TArray> this_type;
public:
typedef VariantRef variant_type;
FORCE_INLINE ElementProxy(TArray array, size_t index)
: _array(array), _index(index) {}

View File

@@ -83,6 +83,18 @@
#define ARDUINOJSON_DEFAULT_NESTING_LIMIT 10
#endif
// Number of bits to store the pointer to next node
// (saves RAM but limits the number of values in a document)
#ifndef ARDUINOJSON_SLOT_OFFSET_SIZE
#if defined(__SIZEOF_POINTER__) && __SIZEOF_POINTER__ == 2
// Address space == 16-bit => max 127 values
#define ARDUINOJSON_SLOT_OFFSET_SIZE 1
#else
// Address space > 16-bit => max 32767 values
#define ARDUINOJSON_SLOT_OFFSET_SIZE 2
#endif
#endif
#else // ARDUINOJSON_EMBEDDED_MODE
// On a computer we have plenty of memory so we can use doubles
@@ -114,6 +126,11 @@
#define ARDUINOJSON_DEFAULT_NESTING_LIMIT 50
#endif
// Number of bits to store the pointer to next node
#ifndef ARDUINOJSON_SLOT_OFFSET_SIZE
#define ARDUINOJSON_SLOT_OFFSET_SIZE 4
#endif
#endif // ARDUINOJSON_EMBEDDED_MODE
#ifdef ARDUINO

View File

@@ -48,7 +48,7 @@ class MsgPackDeserializer {
template <typename TFilter>
bool parseVariant(VariantData &variant, TFilter filter,
NestingLimit nestingLimit) {
uint8_t code = 0;
uint8_t code = 0;
if (!readByte(code))
return false;

View File

@@ -21,10 +21,13 @@ namespace ARDUINOJSON_NAMESPACE {
template <typename TObject, typename TStringRef>
class MemberProxy : public VariantOperators<MemberProxy<TObject, TStringRef> >,
public VariantShortcuts<MemberProxy<TObject, TStringRef> >,
public Visitable {
public Visitable,
public VariantTag {
typedef MemberProxy<TObject, TStringRef> this_type;
public:
typedef VariantRef variant_type;
FORCE_INLINE MemberProxy(TObject variant, TStringRef key)
: _object(variant), _key(key) {}

View File

@@ -0,0 +1,30 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2020
// MIT License
#pragma once
#include <stdint.h> // int8_t, int16_t
#include <ArduinoJson/Namespace.hpp>
namespace ARDUINOJSON_NAMESPACE {
template <int Bits>
struct int_t;
template <>
struct int_t<8> {
typedef int8_t type;
};
template <>
struct int_t<16> {
typedef int16_t type;
};
template <>
struct int_t<32> {
typedef int32_t type;
};
} // namespace ARDUINOJSON_NAMESPACE

View File

@@ -9,6 +9,7 @@
#include <ArduinoJson/Polyfills/attributes.hpp>
#include <ArduinoJson/Polyfills/type_traits.hpp>
#include <ArduinoJson/Variant/VariantAs.hpp>
#include <ArduinoJson/Variant/VariantTag.hpp>
namespace ARDUINOJSON_NAMESPACE {
@@ -17,23 +18,23 @@ CompareResult compare(const T1 &lhs, const T2 &rhs); // VariantCompare.cpp
template <typename TVariant>
struct VariantOperators {
// Returns the default value if the VariantRef is undefined of incompatible
// Returns the default value if the VariantRef is undefined or incompatible
template <typename T>
friend typename enable_if<!IsVisitable<T>::value, T>::type operator|(
const TVariant &variant, const T &defaultValue) {
friend typename enable_if<!IsVariant<T>::value, T>::type operator|(
const TVariant &variant, T defaultValue) {
if (variant.template is<T>())
return variant.template as<T>();
else
return defaultValue;
}
// Returns the default value if the VariantRef is undefined of incompatible
// Special case for string: null is treated as undefined
// Returns the default value if the VariantRef is undefined or incompatible
template <typename T>
friend typename enable_if<is_same<T, const char *>::value, T>::type operator|(
const TVariant &variant, T defaultValue) {
const char *value = variant.template as<const char *>();
return value ? value : defaultValue;
friend typename enable_if<IsVariant<T>::value, typename T::variant_type>::type
operator|(const TVariant &variant, T defaultValue) {
if (variant)
return variant;
else
return defaultValue;
}
// value == TVariant

View File

@@ -16,6 +16,7 @@
#include <ArduinoJson/Variant/VariantOperators.hpp>
#include <ArduinoJson/Variant/VariantRef.hpp>
#include <ArduinoJson/Variant/VariantShortcuts.hpp>
#include <ArduinoJson/Variant/VariantTag.hpp>
namespace ARDUINOJSON_NAMESPACE {
@@ -23,12 +24,9 @@ namespace ARDUINOJSON_NAMESPACE {
class ArrayRef;
class ObjectRef;
template <typename, typename>
class MemberProxy;
// Contains the methods shared by VariantRef and VariantConstRef
template <typename TData>
class VariantRefBase {
class VariantRefBase : public VariantTag {
public:
// Tells wether the variant has the specified type.
// Returns true if the variant has type type T, false otherwise.

View File

@@ -4,15 +4,15 @@
#pragma once
#include <stdint.h> // int8_t, int16_t
#include <ArduinoJson/Polyfills/integer.hpp>
#include <ArduinoJson/Polyfills/limits.hpp>
#include <ArduinoJson/Polyfills/type_traits.hpp>
#include <ArduinoJson/Strings/StoragePolicy.hpp>
#include <ArduinoJson/Variant/VariantContent.hpp>
namespace ARDUINOJSON_NAMESPACE {
typedef conditional<sizeof(void*) <= 2, int8_t, int16_t>::type VariantSlotDiff;
typedef int_t<ARDUINOJSON_SLOT_OFFSET_SIZE * 8>::type VariantSlotDiff;
class VariantSlot {
// CAUTION: same layout as VariantData
@@ -61,11 +61,19 @@ class VariantSlot {
}
void setNext(VariantSlot* slot) {
ARDUINOJSON_ASSERT(!slot || slot - this >=
numeric_limits<VariantSlotDiff>::lowest());
ARDUINOJSON_ASSERT(!slot || slot - this <=
numeric_limits<VariantSlotDiff>::highest());
_next = VariantSlotDiff(slot ? slot - this : 0);
}
void setNextNotNull(VariantSlot* slot) {
ARDUINOJSON_ASSERT(slot != 0);
ARDUINOJSON_ASSERT(slot - this >=
numeric_limits<VariantSlotDiff>::lowest());
ARDUINOJSON_ASSERT(slot - this <=
numeric_limits<VariantSlotDiff>::highest());
_next = VariantSlotDiff(slot - this);
}

View File

@@ -0,0 +1,16 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2020
// MIT License
#pragma once
#include <ArduinoJson/Namespace.hpp>
namespace ARDUINOJSON_NAMESPACE {
struct VariantTag {};
template <typename T>
struct IsVariant : is_base_of<VariantTag, T> {};
} // namespace ARDUINOJSON_NAMESPACE

View File

@@ -4,7 +4,7 @@
#pragma once
#define ARDUINOJSON_VERSION "6.17.0"
#define ARDUINOJSON_VERSION "6.17.1"
#define ARDUINOJSON_VERSION_MAJOR 6
#define ARDUINOJSON_VERSION_MINOR 17
#define ARDUINOJSON_VERSION_REVISION 0
#define ARDUINOJSON_VERSION_REVISION 1

View File

@@ -1,90 +0,0 @@
# ArduinoJson - arduinojson.org
# Copyright Benoit Blanchon 2014-2020
# MIT License
# I have no idea what this is about, I simply followed the instructions from:
# https://dominikberner.ch/cmake-interface-lib/
add_library(ArduinoJson INTERFACE)
include(GNUInstallDirs)
# Adding the install interface generator expression makes sure that the include
# files are installed to the proper location (provided by GNUInstallDirs)
target_include_directories(ArduinoJson
INTERFACE
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
)
target_compile_definitions(ArduinoJson
INTERFACE
ARDUINOJSON_DEBUG=$<CONFIG:Debug>
)
# locations are provided by GNUInstallDirs
install(
TARGETS
ArduinoJson
EXPORT
ArduinoJson_Targets
ARCHIVE DESTINATION
${CMAKE_INSTALL_LIBDIR}
LIBRARY DESTINATION
${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION
${CMAKE_INSTALL_BINDIR}
)
include(CMakePackageConfigHelpers)
if(${CMAKE_VERSION} VERSION_GREATER "3.14.0")
set(ARCH_INDEPENDENT "ARCH_INDEPENDENT")
endif()
write_basic_package_version_file(
"${PROJECT_BINARY_DIR}/ArduinoJsonConfigVersion.cmake"
VERSION
${PROJECT_VERSION}
COMPATIBILITY
SameMajorVersion
${ARCH_INDEPENDENT}
)
configure_package_config_file(
"${PROJECT_SOURCE_DIR}/extras/ArduinoJsonConfig.cmake.in"
"${PROJECT_BINARY_DIR}/ArduinoJsonConfig.cmake"
INSTALL_DESTINATION
${CMAKE_INSTALL_DATAROOTDIR}/ArduinoJson/cmake)
install(
EXPORT
ArduinoJson_Targets
FILE
ArduinoJsonTargets.cmake
DESTINATION
${CMAKE_INSTALL_DATAROOTDIR}/ArduinoJson/cmake
)
install(
FILES
"${PROJECT_BINARY_DIR}/ArduinoJsonConfig.cmake"
"${PROJECT_BINARY_DIR}/ArduinoJsonConfigVersion.cmake"
DESTINATION
"${CMAKE_INSTALL_DATAROOTDIR}/ArduinoJson/cmake"
)
install(
FILES
ArduinoJson.h
ArduinoJson.hpp
DESTINATION
include
)
install(
DIRECTORY
"${CMAKE_CURRENT_SOURCE_DIR}/ArduinoJson"
DESTINATION
include
)

View File

@@ -33,10 +33,12 @@ class FSPersistence {
jsonDocument.shrinkToFit(); // added by proddy
JsonObject jsonObject = jsonDocument.as<JsonObject>();
// debug added by Proddy
// Serial.printf("Read File: %s: ", _filePath);
// serializeJson(jsonDocument, Serial);
// Serial.println();
// debug added by Proddy
#if defined(EMSESP_FORCE_SERIAL)
Serial.printf("Read File: %s: ", _filePath);
serializeJson(jsonDocument, Serial);
Serial.println();
#endif
_statefulService->updateWithoutPropagation(jsonObject, _stateUpdater);
settingsFile.close();
@@ -65,7 +67,7 @@ class FSPersistence {
}
// debug added by Proddy
#if defined(EMSESP_DEBUG)
#if defined(EMSESP_FORCE_SERIAL)
Serial.printf("Write File: %s: ", _filePath);
serializeJson(jsonDocument, Serial);
Serial.println();

View File

@@ -4,7 +4,6 @@
namespace emsesp {
class EMSESP {
public:
static System system_;
static Mqtt mqtt_;
static DallasSensor dallassensor_;
};
@@ -93,14 +92,12 @@ AsyncMqttClient * MqttSettingsService::getMqttClient() {
}
void MqttSettingsService::onMqttConnect(bool sessionPresent) {
/*
Serial.print(F("Connected to MQTT, "));
if (sessionPresent) {
Serial.println(F("with persistent session"));
} else {
Serial.println(F("without persistent session"));
}
*/
// Serial.print(F("Connected to MQTT, "));
// if (sessionPresent) {
// Serial.println(F("with persistent session"));
// } else {
// Serial.println(F("without persistent session"));
// }
}
void MqttSettingsService::onMqttDisconnect(AsyncMqttClientDisconnectReason reason) {
@@ -116,6 +113,7 @@ void MqttSettingsService::onConfigUpdated() {
// added by proddy
// reload EMS-ESP MQTT settings
emsesp::EMSESP::mqtt_.start();
}
#ifdef ESP32

View File

@@ -46,24 +46,23 @@ void OTASettingsService::configureArduinoOTA() {
emsesp::System::upload_status(false);
});
/*
_arduinoOTA->onProgress([](unsigned int progress, unsigned int total) {
Serial.printf_P(PSTR("Progress: %u%%\r\n"), (progress / (total / 100)));
});
_arduinoOTA->onError([](ota_error_t error) {
Serial.printf("Error[%u]: ", error);
if (error == OTA_AUTH_ERROR)
Serial.println(F("Auth Failed"));
else if (error == OTA_BEGIN_ERROR)
Serial.println(F("Begin Failed"));
else if (error == OTA_CONNECT_ERROR)
Serial.println(F("Connect Failed"));
else if (error == OTA_RECEIVE_ERROR)
Serial.println(F("Receive Failed"));
else if (error == OTA_END_ERROR)
Serial.println(F("End Failed"));
});
*/
// _arduinoOTA->onProgress([](unsigned int progress, unsigned int total) {
// Serial.printf_P(PSTR("Progress: %u%%\r\n"), (progress / (total / 100)));
// });
// _arduinoOTA->onError([](ota_error_t error) {
// Serial.printf("Error[%u]: ", error);
// if (error == OTA_AUTH_ERROR)
// Serial.println(F("Auth Failed"));
// else if (error == OTA_BEGIN_ERROR)
// Serial.println(F("Begin Failed"));
// else if (error == OTA_CONNECT_ERROR)
// Serial.println(F("Connect Failed"));
// else if (error == OTA_RECEIVE_ERROR)
// Serial.println(F("Receive Failed"));
// else if (error == OTA_END_ERROR)
// Serial.println(F("End Failed"));
// });
_arduinoOTA->begin();
}
}

View File

@@ -213,82 +213,70 @@ void Shell::loop_normal() {
default:
if (esc_) {
if ((c == '[') || (c == 'O')) {
// start of sequence
} else if (c >= '0' && (c <= '9')) {
// numbers
esc_ = (esc_ & 0x7F) * 10 + c - '0';
} else if (c == 'A') {
// cursor up
if (c == 'A') { // cursor up
line_buffer_ = line_old_;
cursor_ = 0;
esc_ = 0;
} else if (c == 'B') {
// cursor down
} else if (c == 'B') { // cursor down
line_buffer_.clear();
cursor_ = 0;
esc_ = 0;
} else if (c == 'C') {
// cursor right
} else if (c == 'C') { // cursor right
if (cursor_) {
cursor_--;
}
esc_ = 0;
} else if (c == 'D') {
// cursor left
} else if (c == 'D') { // cursor left
if (cursor_ < line_buffer_.length()) {
cursor_++;
}
esc_ = 0;
} else if (c == 'H') {
// Home
} else if (c == 'H') { // Home
cursor_ = line_buffer_.length();
esc_ = 0;
} else if (c == 'F') {
// End
} else if (c == 'F') { // End
cursor_ = 0;
esc_ = 0;
} else if (c == 'P') {
// F1
set_command_str(F("help"));
esc_ = 0;
} else if (c == 'Q') {
// F2
set_command_str(F("show"));
esc_ = 0;
} else if (c == '~') {
// function keys with number
if ((esc_ == 3) && cursor_) {
// del
} else if (c >= 'P' && c <= 'Z') { // F1 - F11, Linux, VT100
// set esc-number like ESCn~
esc_ = 11 + c - 'P';
}
if (c == '~' || (c >= 'P' && c <= 'Z')) { // function keys with number ESCn~
if ((esc_ == 3) && cursor_) { // del
cursor_--;
line_buffer_.erase(line_buffer_.length() - cursor_ - 1, 1);
} else if (esc_ == 4) {
// end
} else if (esc_ == 4) { // end
cursor_ = 0;
} else if (esc_ == 1) {
// pos1
} else if (esc_ == 1) { // pos1
cursor_ = line_buffer_.length();
} else if (esc_ == 11) {
// F1 and F10
} else if (esc_ == 11) { // F1
set_command_str(F("help"));
} else if (esc_ == 12) {
// F2
} else if (esc_ == 12) { // F2
set_command_str(F("show"));
} else if (esc_ == 20) {
// F9
} else if (esc_ == 13) { // F3
set_command_str(F("log notice"));
} else if (esc_ == 14) { // F4
set_command_str(F("log info"));
} else if (esc_ == 15) { // F5
set_command_str(F("log debug"));
} else if (esc_ == 17) { // F6
set_command_str(F("watch off"));
} else if (esc_ == 18) { // F7
set_command_str(F("watch on"));
} else if (esc_ == 19) { // F8
set_command_str(F("watch raw"));
} else if (esc_ == 20) { // F9
set_command_str(F("call system info"));
} else if (esc_ == 21) { // F10
set_command_str(F("call system report"));
} else if (esc_ == 23) { // F11
line_buffer_ = read_flash_string(F("send telegram \"0B \""));
cursor_ = 1;
} else if (esc_ == 15) {
// F5
set_command_str(F("call system report"));
} else if (esc_ == 24) { // F12
set_command_str(F("log debug; watch raw"));
}
esc_ = 0;
} else {
// all other chars end sequence
} else if (c >= '0' && (c <= '9')) { // numbers
esc_ = (esc_ & 0x7F) * 10 + c - '0';
} else if ((c != '[') && (c != 'O')) { // all other chars except start of sequence
esc_ = 0;
}
// process normal ascii text
} else if (c >= '\x20' && c <= '\x7E') {
// ASCII text
if (line_buffer_.length() < maximum_command_line_length_) {
line_buffer_.insert(line_buffer_.length() - cursor_, 1, c);
}
@@ -506,31 +494,42 @@ void Shell::maximum_command_line_length(size_t length) {
}
void Shell::process_command() {
CommandLine command_line{line_buffer_};
println();
prompt_displayed_ = false;
if (!command_line->empty()) {
if (commands_) {
auto execution = commands_->execute_command(*this, std::move(command_line));
if (execution.error != nullptr) {
println(execution.error);
}
if (line_buffer_.empty()) {
println();
return;
}
line_old_ = line_buffer_;
while (!line_buffer_.empty()) {
size_t pos = line_buffer_.find(';');
std::string line1;
if (pos < line_buffer_.length()) {
line1 = line_buffer_.substr(0, pos);
line_buffer_.erase(0, pos + 1);
} else {
println(F("No commands configured"));
line1 = line_buffer_;
line_buffer_.clear();
cursor_ = 0;
}
line_old_ = line_buffer_;
}
CommandLine command_line{line1};
cursor_ = 0;
line_buffer_.clear();
println();
prompt_displayed_ = false;
if (running()) {
display_prompt();
if (!command_line->empty()) {
if (commands_) {
auto execution = commands_->execute_command(*this, std::move(command_line));
if (execution.error != nullptr) {
println(execution.error);
}
} else {
println(F("No commands configured"));
}
}
::yield();
}
::yield();
// if (running()) {
// display_prompt();
// }
}
void Shell::process_completion() {
@@ -538,11 +537,9 @@ void Shell::process_completion() {
if (!command_line->empty() && commands_) {
auto completion = commands_->complete_command(*this, command_line);
bool redisplay = false;
if (!completion.help.empty()) {
println();
redisplay = true;
for (auto & help : completion.help) {
std::string help_line = help.to_string(maximum_command_line_length_);
@@ -552,18 +549,8 @@ void Shell::process_completion() {
}
if (!completion.replacement->empty()) {
if (!redisplay) {
erase_current_line();
prompt_displayed_ = false;
redisplay = true;
}
line_buffer_ = completion.replacement.to_string(maximum_command_line_length_);
}
if (redisplay) {
display_prompt();
}
}
::yield();
@@ -587,15 +574,10 @@ void Shell::process_password(bool completed) {
}
void Shell::invoke_command(const std::string & line) {
if (!line_buffer_.empty()) {
println();
prompt_displayed_ = false;
}
if (!prompt_displayed_) {
display_prompt();
}
erase_current_line();
prompt_displayed_ = false;
line_buffer_ = line;
print(line_buffer_);
display_prompt();
process_command();
}

View File

@@ -15,7 +15,8 @@ class DummySettings {
public:
uint8_t tx_mode = 1;
uint8_t ems_bus_id = 0x0B;
int8_t syslog_level = 1; // uuid::log::Level
bool syslog_enabled = false;
int8_t syslog_level = 3; // uuid::log::Level
uint32_t syslog_mark_interval = 0;
String syslog_host = "192.168.1.4";
uint8_t master_thermostat = 0;

View File

@@ -26,7 +26,7 @@ CXX_STANDARD := -std=c++11
#----------------------------------------------------------------------
# Defined Symbols
#----------------------------------------------------------------------
DEFINES += -DARDUINOJSON_ENABLE_STD_STRING=1 -DARDUINOJSON_ENABLE_ARDUINO_STRING -DEMSESP_DEBUG -DEMSESP_STANDALONE
DEFINES += -DARDUINOJSON_ENABLE_STD_STRING=1 -DARDUINOJSON_ENABLE_ARDUINO_STRING -DEMSESP_DEBUG -DEMSESP_STANDALONE -DEMSESP_TEST
#----------------------------------------------------------------------
# Sources & Files

View File

@@ -13,6 +13,7 @@ extra_configs =
debug_flags =
; -D EMSESP_DEBUG
; -D EMSESP_UART_DEBUG
; -D EMSESP_TEST
; -D EMSESP_FORCE_SERIAL
; -D ENABLE_CORS

View File

@@ -0,0 +1,26 @@
#!/usr/bin/env python
from subprocess import call
import os
# example stackdmp.txt would contain text like below copied & pasted from a 'crash dump' command:
# >>>stack>>>
# 3fffff20: 3fff32f0 00000003 3fff3028 402101b2
# 3fffff30: 3fffdad0 3fff3280 0000000d 402148aa
# 3fffff40: 3fffdad0 3fff3280 3fff326c 3fff32f0
# 3fffff50: 0000000d 3fff326c 3fff3028 402103bd
# 3fffff60: 0000000d 3fff34cc 40211de4 3fff34cc
# 3fffff70: 3fff3028 3fff14c4 3fff301c 3fff34cc
# 3fffff80: 3fffdad0 3fff14c4 3fff3028 40210493
# 3fffff90: 3fffdad0 00000000 3fff14c4 4020a738
# 3fffffa0: 3fffdad0 00000000 3fff349c 40211e90
# 3fffffb0: feefeffe feefeffe 3ffe8558 40100b01
# <<<stack<<<
call(['python', 'scripts/decoder.py ', '-s', '-e', os.getcwd()+"/.pio/build/esp12e/firmware.elf", 'scripts/stackdmp.txt'])
# example for linux:
# % cd EMS-ESP
# % python scripts/decoder_linux.py -s -e .pio/build/esp12e/firmware.elf scripts/stackdmp.txt
# python decoder_linux.py -s -e ../.pio/build/esp8266-debug/firmware.elf stackdmp.txt

319
scripts/decoder.py Normal file
View File

@@ -0,0 +1,319 @@
#!/usr/bin/env python3
"""ESP Exception Decoder
github: https://github.com/janLo/EspArduinoExceptionDecoder
license: GPL v3
author: Jan Losinski
"""
import argparse
import re
import subprocess
from collections import namedtuple
import sys
import os
EXCEPTIONS = [
"Illegal instruction",
"SYSCALL instruction",
"InstructionFetchError: Processor internal physical address or data error during instruction fetch",
"LoadStoreError: Processor internal physical address or data error during load or store",
"Level1Interrupt: Level-1 interrupt as indicated by set level-1 bits in the INTERRUPT register",
"Alloca: MOVSP instruction, if caller's registers are not in the register file",
"IntegerDivideByZero: QUOS, QUOU, REMS, or REMU divisor operand is zero",
"reserved",
"Privileged: Attempt to execute a privileged operation when CRING ? 0",
"LoadStoreAlignmentCause: Load or store to an unaligned address",
"reserved",
"reserved",
"InstrPIFDataError: PIF data error during instruction fetch",
"LoadStorePIFDataError: Synchronous PIF data error during LoadStore access",
"InstrPIFAddrError: PIF address error during instruction fetch",
"LoadStorePIFAddrError: Synchronous PIF address error during LoadStore access",
"InstTLBMiss: Error during Instruction TLB refill",
"InstTLBMultiHit: Multiple instruction TLB entries matched",
"InstFetchPrivilege: An instruction fetch referenced a virtual address at a ring level less than CRING",
"reserved",
"InstFetchProhibited: An instruction fetch referenced a page mapped with an attribute that does not permit instruction fetch",
"reserved",
"reserved",
"reserved",
"LoadStoreTLBMiss: Error during TLB refill for a load or store",
"LoadStoreTLBMultiHit: Multiple TLB entries matched for a load or store",
"LoadStorePrivilege: A load or store referenced a virtual address at a ring level less than CRING",
"reserved",
"LoadProhibited: A load referenced a page mapped with an attribute that does not permit loads",
"StoreProhibited: A store referenced a page mapped with an attribute that does not permit stores"
]
PLATFORMS = {
"ESP8266": "lx106",
"ESP32": "esp32"
}
BACKTRACE_REGEX = re.compile(r"(?:\s+(0x40[0-2](?:\d|[a-f]|[A-F]){5}):0x(?:\d|[a-f]|[A-F]){8})\b")
EXCEPTION_REGEX = re.compile("^Exception \\((?P<exc>[0-9]*)\\):$")
COUNTER_REGEX = re.compile('^epc1=(?P<epc1>0x[0-9a-f]+) epc2=(?P<epc2>0x[0-9a-f]+) epc3=(?P<epc3>0x[0-9a-f]+) '
'excvaddr=(?P<excvaddr>0x[0-9a-f]+) depc=(?P<depc>0x[0-9a-f]+)$')
CTX_REGEX = re.compile("^ctx: (?P<ctx>.+)$")
POINTER_REGEX = re.compile('^sp: (?P<sp>[0-9a-f]+) end: (?P<end>[0-9a-f]+) offset: (?P<offset>[0-9a-f]+)$')
STACK_BEGIN = '>>>stack>>>'
STACK_END = '<<<stack<<<'
STACK_REGEX = re.compile(
'^(?P<off>[0-9a-f]+):\W+(?P<c1>[0-9a-f]+) (?P<c2>[0-9a-f]+) (?P<c3>[0-9a-f]+) (?P<c4>[0-9a-f]+)(\W.*)?$')
StackLine = namedtuple("StackLine", ["offset", "content"])
class ExceptionDataParser(object):
def __init__(self):
self.exception = None
self.epc1 = None
self.epc2 = None
self.epc3 = None
self.excvaddr = None
self.depc = None
self.ctx = None
self.sp = None
self.end = None
self.offset = None
self.stack = []
def _parse_backtrace(self, line):
if line.startswith('Backtrace:'):
self.stack = [StackLine(offset=0, content=(addr,)) for addr in BACKTRACE_REGEX.findall(line)]
return None
return self._parse_backtrace
def _parse_exception(self, line):
match = EXCEPTION_REGEX.match(line)
if match is not None:
self.exception = int(match.group('exc'))
return self._parse_counters
return self._parse_exception
def _parse_counters(self, line):
match = COUNTER_REGEX.match(line)
if match is not None:
self.epc1 = match.group("epc1")
self.epc2 = match.group("epc2")
self.epc3 = match.group("epc3")
self.excvaddr = match.group("excvaddr")
self.depc = match.group("depc")
return self._parse_ctx
return self._parse_counters
def _parse_ctx(self, line):
match = CTX_REGEX.match(line)
if match is not None:
self.ctx = match.group("ctx")
return self._parse_pointers
return self._parse_ctx
def _parse_pointers(self, line):
match = POINTER_REGEX.match(line)
if match is not None:
self.sp = match.group("sp")
self.end = match.group("end")
self.offset = match.group("offset")
return self._parse_stack_begin
return self._parse_pointers
def _parse_stack_begin(self, line):
if line == STACK_BEGIN:
return self._parse_stack_line
return self._parse_stack_begin
def _parse_stack_line(self, line):
if line != STACK_END:
match = STACK_REGEX.match(line)
if match is not None:
self.stack.append(StackLine(offset=match.group("off"),
content=(match.group("c1"), match.group("c2"), match.group("c3"),
match.group("c4"))))
return self._parse_stack_line
return None
def parse_file(self, file, platform, stack_only=False):
if platform == 'ESP32':
func = self._parse_backtrace
else:
func = self._parse_exception
if stack_only:
func = self._parse_stack_begin
for line in file:
func = func(line.strip())
if func is None:
break
if func is not None:
print("ERROR: Parser not complete!")
sys.exit(1)
class AddressResolver(object):
def __init__(self, tool_path, elf_path):
self._tool = tool_path
self._elf = elf_path
self._address_map = {}
def _lookup(self, addresses):
cmd = [self._tool, "-aipfC", "-e", self._elf] + [addr for addr in addresses if addr is not None]
if sys.version_info[0] < 3:
output = subprocess.check_output(cmd)
else:
output = subprocess.check_output(cmd, encoding="utf-8")
line_regex = re.compile("^(?P<addr>[0-9a-fx]+): (?P<result>.+)$")
last = None
for line in output.splitlines():
line = line.strip()
match = line_regex.match(line)
if match is None:
if last is not None and line.startswith('(inlined by)'):
line = line [12:].strip()
self._address_map[last] += ("\n \-> inlined by: " + line)
continue
if match.group("result") == '?? ??:0':
continue
self._address_map[match.group("addr")] = match.group("result")
last = match.group("addr")
def fill(self, parser):
addresses = [parser.epc1, parser.epc2, parser.epc3, parser.excvaddr, parser.sp, parser.end, parser.offset]
for line in parser.stack:
addresses.extend(line.content)
self._lookup(addresses)
def _sanitize_addr(self, addr):
if addr.startswith("0x"):
addr = addr[2:]
fill = "0" * (8 - len(addr))
return "0x" + fill + addr
def resolve_addr(self, addr):
out = self._sanitize_addr(addr)
if out in self._address_map:
out += ": " + self._address_map[out]
return out
def resolve_stack_addr(self, addr, full=True):
addr = self._sanitize_addr(addr)
if addr in self._address_map:
return addr + ": " + self._address_map[addr]
if full:
return "[DATA (0x" + addr + ")]"
return None
def print_addr(name, value, resolver):
print("{}:{} {}".format(name, " " * (8 - len(name)), resolver.resolve_addr(value)))
def print_stack_full(lines, resolver):
print("stack:")
for line in lines:
print(line.offset + ":")
for content in line.content:
print(" " + resolver.resolve_stack_addr(content))
def print_stack(lines, resolver):
print("stack:")
for line in lines:
for content in line.content:
out = resolver.resolve_stack_addr(content, full=False)
if out is None:
continue
print(out)
def print_result(parser, resolver, platform, full=True, stack_only=False):
if platform == 'ESP8266' and not stack_only:
print('Exception: {} ({})'.format(parser.exception, EXCEPTIONS[parser.exception]))
print("")
print_addr("epc1", parser.epc1, resolver)
print_addr("epc2", parser.epc2, resolver)
print_addr("epc3", parser.epc3, resolver)
print_addr("excvaddr", parser.excvaddr, resolver)
print_addr("depc", parser.depc, resolver)
print("")
print("ctx: " + parser.ctx)
print("")
print_addr("sp", parser.sp, resolver)
print_addr("end", parser.end, resolver)
print_addr("offset", parser.offset, resolver)
print("")
if full:
print_stack_full(parser.stack, resolver)
else:
print_stack(parser.stack, resolver)
def parse_args():
parser = argparse.ArgumentParser(description="decode ESP Stacktraces.")
parser.add_argument("-p", "--platform", help="The platform to decode from", choices=PLATFORMS.keys(),
default="ESP8266")
parser.add_argument("-t", "--tool", help="Path to the xtensa toolchain",
default="~/.platformio/packages/toolchain-xtensa/")
parser.add_argument("-e", "--elf", help="path to elf file", required=True)
parser.add_argument("-f", "--full", help="Print full stack dump", action="store_true")
parser.add_argument("-s", "--stack_only", help="Decode only a stractrace", action="store_true")
parser.add_argument("file", help="The file to read the exception data from ('-' for STDIN)", default="-")
return parser.parse_args()
if __name__ == "__main__":
args = parse_args()
if args.file == "-":
file = sys.stdin
else:
if not os.path.exists(args.file):
print("ERROR: file " + args.file + " not found")
sys.exit(1)
file = open(args.file, "r")
addr2line = os.path.join(os.path.abspath(os.path.expanduser(args.tool)),
"bin/xtensa-" + PLATFORMS[args.platform] + "-elf-addr2line")
if os.name == 'nt':
addr2line += '.exe'
if not os.path.exists(addr2line):
print("ERROR: addr2line not found (" + addr2line + ")")
elf_file = os.path.abspath(os.path.expanduser(args.elf))
if not os.path.exists(elf_file):
print("ERROR: elf file not found (" + elf_file + ")")
parser = ExceptionDataParser()
resolver = AddressResolver(addr2line, elf_file)
parser.parse_file(file, args.platform, args.stack_only)
resolver.fill(parser)
print_result(parser, resolver, args.platform, args.full, args.stack_only)

4
scripts/decoder.sh Normal file
View File

@@ -0,0 +1,4 @@
# python decoder.py -s -e ../.pio/build/esp8266-local/firmware.elf stackdmp.txt
python decoder.py -p ESP32 -e ../.pio/build/esp32-local/firmware.elf stackdmp.txt -t ~/.platformio/packages/toolchain-xtensa32

View File

@@ -71,7 +71,7 @@ void WebAPIService::webAPIService(AsyncWebServerRequest * request) {
id = "-1";
}
DynamicJsonDocument doc(EMSESP_MAX_JSON_SIZE_LARGE);
DynamicJsonDocument doc(EMSESP_MAX_JSON_SIZE_DYN);
JsonObject json = doc.to<JsonObject>();
bool ok = false;
@@ -98,10 +98,10 @@ void WebAPIService::webAPIService(AsyncWebServerRequest * request) {
cmd.c_str(),
data.c_str(),
id.c_str(),
ok ? F("OK") : F("Invalid"));
ok ? PSTR("OK") : PSTR("Invalid"));
EMSESP::logger().info(debug.c_str());
if (json.size()) {
char buffer2[EMSESP_MAX_JSON_SIZE_LARGE];
char buffer2[EMSESP_MAX_JSON_SIZE_DYN];
serializeJson(doc, buffer2);
EMSESP::logger().info("json (max 255 chars): %s", buffer2);
}
@@ -110,7 +110,7 @@ void WebAPIService::webAPIService(AsyncWebServerRequest * request) {
// if we have returned data in JSON format, send this to the WEB
if (json.size()) {
doc.shrinkToFit();
char buffer[EMSESP_MAX_JSON_SIZE_LARGE];
char buffer[EMSESP_MAX_JSON_SIZE_DYN];
serializeJsonPretty(doc, buffer);
request->send(200, "text/plain", buffer);
} else {

View File

@@ -30,6 +30,7 @@ WebSettingsService::WebSettingsService(AsyncWebServer * server, FS * fs, Securit
void WebSettings::read(WebSettings & settings, JsonObject & root) {
root["tx_mode"] = settings.tx_mode;
root["ems_bus_id"] = settings.ems_bus_id;
root["syslog_enabled"] = settings.syslog_enabled;
root["syslog_level"] = settings.syslog_level;
root["syslog_mark_interval"] = settings.syslog_mark_interval;
root["syslog_host"] = settings.syslog_host;
@@ -50,6 +51,7 @@ void WebSettings::read(WebSettings & settings, JsonObject & root) {
StateUpdateResult WebSettings::update(JsonObject & root, WebSettings & settings) {
settings.tx_mode = root["tx_mode"] | EMSESP_DEFAULT_TX_MODE;
settings.ems_bus_id = root["ems_bus_id"] | EMSESP_DEFAULT_EMS_BUS_ID;
settings.syslog_enabled = root["syslog_enabled"] | EMSESP_DEFAULT_SYSLOG_ENABLED;
settings.syslog_level = root["syslog_level"] | EMSESP_DEFAULT_SYSLOG_LEVEL;
settings.syslog_mark_interval = root["syslog_mark_interval"] | EMSESP_DEFAULT_SYSLOG_MARK_INTERVAL;
settings.syslog_host = root["syslog_host"] | EMSESP_DEFAULT_SYSLOG_HOST;

View File

@@ -27,7 +27,8 @@
#define EMSESP_DEFAULT_TX_MODE 1 // EMS1.0
#define EMSESP_DEFAULT_EMS_BUS_ID 0x0B // service key
#define EMSESP_DEFAULT_SYSLOG_LEVEL -1 // OFF
#define EMSESP_DEFAULT_SYSLOG_ENABLED false
#define EMSESP_DEFAULT_SYSLOG_LEVEL 3 // ERR
#define EMSESP_DEFAULT_SYSLOG_MARK_INTERVAL 0
#define EMSESP_DEFAULT_SYSLOG_HOST ""
#define EMSESP_DEFAULT_MASTER_THERMOSTAT 0 // not set
@@ -67,6 +68,7 @@ class WebSettings {
uint8_t master_thermostat;
bool shower_timer;
bool shower_alert;
bool syslog_enabled;
int8_t syslog_level; // uuid::log::Level
uint32_t syslog_mark_interval;
String syslog_host;

View File

@@ -68,7 +68,9 @@ void Command::add(const uint8_t device_type, const uint8_t device_id, const __Fl
cmdfunctions_.emplace_back(device_type, cmd, cb, nullptr);
// see if we need to subscribe
Mqtt::register_command(device_type, device_id, cmd, cb);
if (Mqtt::enabled()) {
Mqtt::register_command(device_type, device_id, cmd, cb);
}
}
// add a command to the list, which does return json object as output

View File

@@ -20,7 +20,7 @@
#include "emsesp.h"
#include "version.h"
#if defined(EMSESP_DEBUG)
#if defined(EMSESP_TEST)
#include "test/test.h"
#endif
@@ -110,28 +110,6 @@ void EMSESPShell::add_console_commands() {
// commands->remove_context_commands(ShellContext::MAIN);
commands->remove_all_commands();
commands->add_command(ShellContext::MAIN,
CommandFlags::USER,
flash_string_vector{F_(fetch)},
[&](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
shell.printfln(F("Requesting data from EMS devices"));
EMSESP::fetch_device_values();
});
commands->add_command(ShellContext::MAIN,
CommandFlags::ADMIN,
flash_string_vector{F_(publish)},
flash_string_vector{F_(ha_optional)},
[&](Shell & shell, const std::vector<std::string> & arguments) {
if (arguments.empty()) {
EMSESP::publish_all();
shell.printfln(F("Published all data to MQTT"));
} else {
EMSESP::publish_all(true);
shell.printfln(F("Published all data to MQTT, including HA configs"));
}
});
commands->add_command(ShellContext::MAIN,
CommandFlags::USER,
flash_string_vector{F_(show)},
@@ -302,13 +280,17 @@ void EMSESPShell::add_console_commands() {
"local");
});
#ifndef EMSESP_STANDALONE
commands->add_command(ShellContext::MAIN,
CommandFlags::ADMIN,
flash_string_vector{F_(send), F_(telegram)},
flash_string_vector{F_(data_mandatory)},
[](Shell & shell __attribute__((unused)), const std::vector<std::string> & arguments) {
EMSESP::send_raw_telegram(arguments.front().c_str());
CommandFlags::USER,
flash_string_vector{F_(set), F_(timeout)},
flash_string_vector{F_(n_mandatory)},
[](Shell & shell, const std::vector<std::string> & arguments) {
uint16_t value = Helpers::atoint(arguments.front().c_str());
telnet_.initial_idle_timeout(value * 60);
shell.printfln(F("Telnet timout is %d minutes"), value);
});
#endif
commands->add_command(ShellContext::MAIN,
CommandFlags::USER,
@@ -446,11 +428,7 @@ void EMSESPShell::add_console_commands() {
return {};
});
/*
* add all the submenu contexts...
*/
// System
// System context menu
commands->add_command(ShellContext::MAIN,
CommandFlags::USER,
flash_string_vector{F_(system)},
@@ -491,33 +469,21 @@ void Console::enter_custom_context(Shell & shell, unsigned int context) {
// each custom context has the common commands like log, help, exit, su etc
void Console::load_standard_commands(unsigned int context) {
#if defined(EMSESP_DEBUG)
#if defined(EMSESP_TEST)
EMSESPShell::commands->add_command(context,
CommandFlags::USER,
flash_string_vector{F_(test)},
flash_string_vector{F_(name_optional)},
[](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
[](Shell & shell, const std::vector<std::string> & arguments) {
if (arguments.size() == 0) {
Test::run_test(shell, "default");
Test::run_test_shell(shell, "default");
} else {
Test::run_test(shell, arguments.front());
Test::run_test_shell(shell, arguments.front());
}
});
#endif
#if defined(EMSESP_STANDALONE)
EMSESPShell::commands->add_command(context,
CommandFlags::USER,
flash_string_vector{F("t")},
flash_string_vector{F_(name_optional)},
[](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
if (arguments.size() == 0) {
Test::run_test(shell, "default");
} else {
Test::run_test(shell, arguments.front());
}
});
#endif
EMSESPShell::commands->add_command(
context,

View File

@@ -84,12 +84,12 @@ void DallasSensor::loop() {
bus_.reset_search();
state_ = State::SCANNING;
} else if (time_now - last_activity_ > READ_TIMEOUT_MS) {
LOG_ERROR(F("Sensor read timeout"));
LOG_WARNING(F("Dallas sensor read timeout"));
state_ = State::IDLE;
}
} else if (state_ == State::SCANNING) {
if (time_now - last_activity_ > SCAN_TIMEOUT_MS) {
LOG_ERROR(F("Sensor scan timeout"));
LOG_ERROR(F("Dallas sensor scan timeout"));
state_ = State::IDLE;
} else {
uint8_t addr[ADDR_LEN] = {0};
@@ -129,11 +129,11 @@ void DallasSensor::loop() {
break;
default:
LOG_ERROR(F("Unknown sensor %s"), Sensor(addr).to_string().c_str());
LOG_ERROR(F("Unknown dallas sensor %s"), Sensor(addr).to_string().c_str());
break;
}
} else {
LOG_ERROR(F("Invalid sensor %s"), Sensor(addr).to_string().c_str());
LOG_ERROR(F("Invalid dallas sensor %s"), Sensor(addr).to_string().c_str());
}
} else {
if (!parasite_) {
@@ -309,7 +309,7 @@ bool DallasSensor::export_values(JsonObject & json) {
}
// send all dallas sensor values as a JSON package to MQTT
void DallasSensor::publish_values() {
void DallasSensor::publish_values(const bool force) {
uint8_t num_sensors = sensors_.size();
if (num_sensors == 0) {
@@ -340,7 +340,7 @@ void DallasSensor::publish_values() {
// create the HA MQTT config
// to e.g. homeassistant/sensor/ems-esp/dallas_28-233D-9497-0C03/config
if (mqtt_format_ == Mqtt::Format::HA) {
if (!(registered_ha_[sensor_no - 1])) {
if (!(registered_ha_[sensor_no - 1]) || force) {
StaticJsonDocument<EMSESP_MAX_JSON_SIZE_MEDIUM> config;
config["dev_cla"] = F("temperature");

View File

@@ -58,7 +58,7 @@ class DallasSensor {
void start();
void loop();
void publish_values();
void publish_values(const bool force);
void reload();
bool updated_values();

View File

@@ -87,7 +87,7 @@
{107, DeviceType::THERMOSTAT, F("FR100"), DeviceFlags::EMS_DEVICE_FLAG_JUNKERS | DeviceFlags::EMS_DEVICE_FLAG_JUNKERS_2}, // older model
{108, DeviceType::THERMOSTAT, F("FR110"), DeviceFlags::EMS_DEVICE_FLAG_JUNKERS | DeviceFlags::EMS_DEVICE_FLAG_JUNKERS_2}, // older model
{111, DeviceType::THERMOSTAT, F("FR10"), DeviceFlags::EMS_DEVICE_FLAG_JUNKERS},
{147, DeviceType::THERMOSTAT, F("FR50"), DeviceFlags::EMS_DEVICE_FLAG_JUNKERS},
{147, DeviceType::THERMOSTAT, F("FR50"), DeviceFlags::EMS_DEVICE_FLAG_JUNKERS | DeviceFlags::EMS_DEVICE_FLAG_JUNKERS_2},
{191, DeviceType::THERMOSTAT, F("FR120"), DeviceFlags::EMS_DEVICE_FLAG_JUNKERS | DeviceFlags::EMS_DEVICE_FLAG_JUNKERS_2}, // older model
{192, DeviceType::THERMOSTAT, F("FW120"), DeviceFlags::EMS_DEVICE_FLAG_JUNKERS},

View File

@@ -74,11 +74,7 @@ Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const
// create the config topics for Home Assistant MQTT Discovery
// for each of the main elements
void Boiler::register_mqtt_ha_config(bool force) {
if ((mqtt_ha_config_ && !force)) {
return;
}
void Boiler::register_mqtt_ha_config() {
if (!Mqtt::connected()) {
return;
}
@@ -141,8 +137,17 @@ void Boiler::register_mqtt_ha_config(bool force) {
Mqtt::register_mqtt_ha_sensor(nullptr, nullptr, F_(burnWorkMin), this->device_type(), "burnWorkMin", F_(min), nullptr);
Mqtt::register_mqtt_ha_sensor(nullptr, nullptr, F_(heatWorkMin), this->device_type(), "heatWorkMin", F_(min), nullptr);
Mqtt::register_mqtt_ha_sensor(nullptr, nullptr, F_(UBAuptime), this->device_type(), "UBAuptime", F_(min), nullptr);
mqtt_ha_config_ = true; // done
}
// ww
// create the config topics for Home Assistant MQTT Discovery
// for each of the ww elements
void Boiler::register_mqtt_ha_config_ww() {
if (!Mqtt::connected()) {
return;
}
// ww
Mqtt::register_mqtt_ha_sensor(nullptr, F_(mqtt_suffix_ww), F_(wWSelTemp), this->device_type(), "wWSelTemp", F_(degrees), F_(iconcruise));
Mqtt::register_mqtt_ha_sensor(nullptr, F_(mqtt_suffix_ww), F_(wWSetTemp), this->device_type(), "wWSetTemp", F_(degrees), F_(icontemperature));
Mqtt::register_mqtt_ha_sensor(nullptr, F_(mqtt_suffix_ww), F_(wWDisinfectionTemp), this->device_type(), "wWDisinfectionTemp", F_(degrees), F_(icontemperature));
@@ -170,14 +175,15 @@ void Boiler::register_mqtt_ha_config(bool force) {
Mqtt::register_mqtt_ha_sensor(nullptr, F_(mqtt_suffix_ww), F_(wWStarts), this->device_type(), "wWStarts", nullptr, nullptr);
Mqtt::register_mqtt_ha_sensor(nullptr, F_(mqtt_suffix_ww), F_(wWWorkM), this->device_type(), "wWWorkM", F_(min), nullptr);
mqtt_ha_config_ = true; // done
mqtt_ha_config_ww_ = true; // done
}
// send stuff to the Web UI
void Boiler::device_info_web(JsonArray & root) {
// fetch the values into a JSON document
DynamicJsonDocument doc(EMSESP_MAX_JSON_SIZE_LARGE);
JsonObject json = doc.to<JsonObject>();
// DynamicJsonDocument doc(EMSESP_MAX_JSON_SIZE_LARGE);
StaticJsonDocument<EMSESP_MAX_JSON_SIZE_LARGE> doc;
JsonObject json = doc.to<JsonObject>();
if (!export_values_main(json)) {
return; // empty
}
@@ -618,19 +624,29 @@ bool Boiler::export_values_main(JsonObject & json) {
void Boiler::publish_values(JsonObject & json, bool force) {
// handle HA first
if (Mqtt::mqtt_format() == Mqtt::Format::HA) {
register_mqtt_ha_config(force);
if (force) {
mqtt_ha_config_ = false;
mqtt_ha_config_ww_ = false;
}
// register ww in next cycle if both unregistered
if (!mqtt_ha_config_) {
register_mqtt_ha_config();
return;
} else if (!mqtt_ha_config_ww_) {
register_mqtt_ha_config_ww();
return;
}
}
DynamicJsonDocument doc_main(EMSESP_MAX_JSON_SIZE_LARGE);
JsonObject json_main = doc_main.to<JsonObject>();
if (export_values_main(json_main)) {
Mqtt::publish(F("boiler_data"), doc_main.as<JsonObject>());
StaticJsonDocument<EMSESP_MAX_JSON_SIZE_LARGE> doc;
JsonObject json_data = doc.to<JsonObject>();
if (export_values_main(json_data)) {
Mqtt::publish(F("boiler_data"), json_data);
}
json_data.clear();
DynamicJsonDocument doc_ww(EMSESP_MAX_JSON_SIZE_LARGE);
JsonObject json_ww = doc_ww.to<JsonObject>();
if (export_values_ww(json_ww)) {
Mqtt::publish(F("boiler_data_ww"), doc_ww.as<JsonObject>());
if (export_values_ww(json_data)) {
Mqtt::publish(F("boiler_data_ww"), json_data);
}
// send out heating and tapwater status
@@ -651,13 +667,14 @@ void Boiler::show_values(uuid::console::Shell & shell) {
EMSdevice::show_values(shell); // for showing the header
// fetch the values into a JSON document
DynamicJsonDocument doc(EMSESP_MAX_JSON_SIZE_LARGE);
JsonObject json = doc.to<JsonObject>();
// DynamicJsonDocument doc(EMSESP_MAX_JSON_SIZE_LARGE);
StaticJsonDocument<EMSESP_MAX_JSON_SIZE_LARGE> doc;
JsonObject json = doc.to<JsonObject>();
if (!export_values_main(json)) {
return; // empty
}
export_values_ww(json); // append ww values
doc.shrinkToFit();
// doc.shrinkToFit();
print_value_json(shell, F("heatingActive"), nullptr, F_(heatingActive), nullptr, json);
print_value_json(shell, F("tapwaterActive"), nullptr, F_(tapwaterActive), nullptr, json);

View File

@@ -47,13 +47,15 @@ class Boiler : public EMSdevice {
private:
static uuid::log::Logger logger_;
void register_mqtt_ha_config(bool force);
void register_mqtt_ha_config();
void register_mqtt_ha_config_ww();
void check_active();
bool export_values_main(JsonObject & doc);
bool export_values_ww(JsonObject & doc);
bool changed_ = false;
bool mqtt_ha_config_ = false; // HA MQTT Discovery
bool changed_ = false;
bool mqtt_ha_config_ = false; // HA MQTT Discovery
bool mqtt_ha_config_ww_ = false; // HA MQTT Discovery
static constexpr uint8_t EMS_TYPE_UBAParameterWW = 0x33;
static constexpr uint8_t EMS_TYPE_UBAFunctionTest = 0x1D;

View File

@@ -49,8 +49,8 @@ bool Heatpump::export_values(JsonObject & json) {
void Heatpump::device_info_web(JsonArray & root) {
// fetch the values into a JSON document
StaticJsonDocument<EMSESP_MAX_JSON_SIZE_MEDIUM> doc;
JsonObject json = doc.to<JsonObject>();
StaticJsonDocument<EMSESP_MAX_JSON_SIZE_SMALL> doc;
JsonObject json = doc.to<JsonObject>();
if (!export_values(json)) {
return; // empty
}
@@ -64,8 +64,8 @@ void Heatpump::show_values(uuid::console::Shell & shell) {
EMSdevice::show_values(shell); // always call this to show header
// fetch the values into a JSON document
StaticJsonDocument<EMSESP_MAX_JSON_SIZE_MEDIUM> doc;
JsonObject json = doc.to<JsonObject>();
StaticJsonDocument<EMSESP_MAX_JSON_SIZE_SMALL> doc;
JsonObject json = doc.to<JsonObject>();
if (!export_values(json)) {
return; // empty
}
@@ -78,27 +78,26 @@ void Heatpump::show_values(uuid::console::Shell & shell) {
void Heatpump::publish_values(JsonObject & json, bool force) {
// handle HA first
if (Mqtt::mqtt_format() == Mqtt::Format::HA) {
register_mqtt_ha_config(force);
if (!mqtt_ha_config_ || force) {
register_mqtt_ha_config();
return;
}
}
StaticJsonDocument<EMSESP_MAX_JSON_SIZE_MEDIUM> doc;
JsonObject json_data = doc.to<JsonObject>();
StaticJsonDocument<EMSESP_MAX_JSON_SIZE_SMALL> doc;
JsonObject json_data = doc.to<JsonObject>();
if (export_values(json_data)) {
Mqtt::publish(F("heatpump_data"), doc.as<JsonObject>());
}
}
void Heatpump::register_mqtt_ha_config(bool force) {
if ((mqtt_ha_config_ && !force)) {
return;
}
void Heatpump::register_mqtt_ha_config() {
if (!Mqtt::connected()) {
return;
}
// Create the Master device
StaticJsonDocument<EMSESP_MAX_JSON_SIZE_MEDIUM> doc;
StaticJsonDocument<EMSESP_MAX_JSON_SIZE_SMALL> doc;
doc["name"] = F_(EMSESP);
doc["uniq_id"] = F_(heatpump);
doc["ic"] = F_(iconheatpump);

View File

@@ -45,7 +45,7 @@ class Heatpump : public EMSdevice {
private:
static uuid::log::Logger logger_;
void register_mqtt_ha_config(bool force);
void register_mqtt_ha_config();
uint8_t airHumidity_ = EMS_VALUE_UINT_NOTSET;
uint8_t dewTemperature_ = EMS_VALUE_UINT_NOTSET;

View File

@@ -62,7 +62,7 @@ void Mixer::device_info_web(JsonArray & root) {
}
// fetch the values into a JSON document
StaticJsonDocument<EMSESP_MAX_JSON_SIZE_MEDIUM> doc;
StaticJsonDocument<EMSESP_MAX_JSON_SIZE_SMALL> doc;
JsonObject json = doc.to<JsonObject>();
if (!export_values_format(Mqtt::Format::SINGLE, json)) {
return; // empty
@@ -101,7 +101,7 @@ void Mixer::show_values(uuid::console::Shell & shell) {
}
// fetch the values into a JSON document
StaticJsonDocument<EMSESP_MAX_JSON_SIZE_MEDIUM> doc;
StaticJsonDocument<EMSESP_MAX_JSON_SIZE_SMALL> doc;
JsonObject json = doc.to<JsonObject>();
if (!export_values_format(Mqtt::Format::SINGLE, json)) {
return; // empty
@@ -128,13 +128,16 @@ void Mixer::show_values(uuid::console::Shell & shell) {
void Mixer::publish_values(JsonObject & json, bool force) {
// handle HA first
if (Mqtt::mqtt_format() == Mqtt::Format::HA) {
register_mqtt_ha_config(force);
if (!mqtt_ha_config_ || force) {
register_mqtt_ha_config();
return;
}
}
if (Mqtt::mqtt_format() == Mqtt::Format::SINGLE) {
StaticJsonDocument<EMSESP_MAX_JSON_SIZE_SMALL> doc;
JsonObject json = doc.to<JsonObject>();
if (export_values_format(Mqtt::mqtt_format(), json)) {
JsonObject json_data = doc.to<JsonObject>();
if (export_values_format(Mqtt::mqtt_format(), json_data)) {
char topic[30];
if (type() == Type::HC) {
snprintf_P(topic, 30, PSTR("mixer_data_hc%d"), hc_);
@@ -150,18 +153,13 @@ void Mixer::publish_values(JsonObject & json, bool force) {
}
// publish config topic for HA MQTT Discovery
void Mixer::register_mqtt_ha_config(bool force) {
if ((mqtt_ha_config_ && !force)) {
return;
}
void Mixer::register_mqtt_ha_config() {
if (!Mqtt::connected()) {
return;
}
// if we don't have valid values for this HC don't add it ever again
if (!Helpers::hasValue(status_)) {
mqtt_ha_config_ = true;
if (!Helpers::hasValue(pumpStatus_)) {
return;
}
@@ -194,7 +192,7 @@ void Mixer::register_mqtt_ha_config(bool force) {
std::string topic(100, '\0');
if (this->type() == Type::HC) {
snprintf_P(&topic[0], topic.capacity() + 1, PSTR("homeassistant/climate/ems-esp/mixer_hc%d/config"), hc_);
snprintf_P(&topic[0], topic.capacity() + 1, PSTR("homeassistant/sensor/ems-esp/mixer_hc%d/config"), hc_);
Mqtt::publish_retain(topic, doc.as<JsonObject>(), true); // publish the config payload with retain flag
char hc_name[10];
snprintf_P(hc_name, sizeof(hc_name), PSTR("hc%d"), hc_);
@@ -204,7 +202,7 @@ void Mixer::register_mqtt_ha_config(bool force) {
Mqtt::register_mqtt_ha_sensor(hc_name, nullptr, F_(valveStatus), this->device_type(), "valveStatus", nullptr, nullptr);
} else {
// WWC
snprintf_P(&topic[0], topic.capacity() + 1, PSTR("homeassistant/climate/ems-esp/mixer_wwc%d/config"), hc_);
snprintf_P(&topic[0], topic.capacity() + 1, PSTR("homeassistant/sensor/ems-esp/mixer_wwc%d/config"), hc_);
Mqtt::publish_retain(topic, doc.as<JsonObject>(), true); // publish the config payload with retain flag
char wwc_name[10];
snprintf_P(wwc_name, sizeof(wwc_name), PSTR("wwc%d"), hc_);

View File

@@ -46,7 +46,7 @@ class Mixer : public EMSdevice {
static uuid::log::Logger logger_;
bool export_values_format(uint8_t mqtt_format, JsonObject & doc);
void register_mqtt_ha_config(bool force);
void register_mqtt_ha_config();
void process_MMPLUSStatusMessage_HC(std::shared_ptr<const Telegram> telegram);
void process_MMPLUSStatusMessage_WWC(std::shared_ptr<const Telegram> telegram);

View File

@@ -49,6 +49,7 @@ Solar::Solar(uint8_t device_type, uint8_t device_id, uint8_t product_id, const s
register_telegram_type(0x036A, F("SM100Status2"), false, [&](std::shared_ptr<const Telegram> t) { process_SM100Status2(t); });
register_telegram_type(0x0380, F("SM100CollectorConfig"), true, [&](std::shared_ptr<const Telegram> t) { process_SM100CollectorConfig(t); });
register_telegram_type(0x038E, F("SM100Energy"), true, [&](std::shared_ptr<const Telegram> t) { process_SM100Energy(t); });
register_telegram_type(0x0391, F("SM100Time"), true, [&](std::shared_ptr<const Telegram> t) { process_SM100Time(t); });
register_mqtt_cmd(F("SM100Tank1MaxTemp"), [&](const char * value, const int8_t id) { return set_SM100Tank1MaxTemp(value, id); });
}
@@ -83,6 +84,7 @@ void Solar::device_info_web(JsonArray & root) {
print_value_json(root, F("energyLastHour"), nullptr, F_(energyLastHour), F_(wh), json);
print_value_json(root, F("energyToday"), nullptr, F_(energyToday), F_(wh), json);
print_value_json(root, F("energyTotal"), nullptr, F_(energyTotal), F_(kwh), json);
print_value_json(root, F("pumpWorkMin"), nullptr, F_(pumpWorkMin), F_(min), json);
if (Helpers::hasValue(pumpWorkMin_)) {
JsonObject dataElement = root.createNestedObject();
@@ -118,6 +120,7 @@ void Solar::show_values(uuid::console::Shell & shell) {
print_value_json(shell, F("energyLastHour"), nullptr, F_(energyLastHour), F_(wh), json);
print_value_json(shell, F("energyToday"), nullptr, F_(energyToday), F_(wh), json);
print_value_json(shell, F("energyTotal"), nullptr, F_(energyTotal), F_(kwh), json);
print_value_json(shell, F("pumpWorkMin"), nullptr, F_(pumpWorkMin), F_(min), json);
if (Helpers::hasValue(pumpWorkMin_)) {
shell.printfln(F(" %s: %d days %d hours %d minutes"),
@@ -132,7 +135,10 @@ void Solar::show_values(uuid::console::Shell & shell) {
void Solar::publish_values(JsonObject & json, bool force) {
// handle HA first
if (Mqtt::mqtt_format() == Mqtt::Format::HA) {
register_mqtt_ha_config(force);
if ((!mqtt_ha_config_ || force)) {
register_mqtt_ha_config();
return;
}
}
StaticJsonDocument<EMSESP_MAX_JSON_SIZE_MEDIUM> doc;
@@ -147,17 +153,13 @@ void Solar::publish_values(JsonObject & json, bool force) {
}
// publish config topic for HA MQTT Discovery
void Solar::register_mqtt_ha_config(bool force) {
if ((mqtt_ha_config_ && !force)) {
return;
}
void Solar::register_mqtt_ha_config() {
if (!Mqtt::connected()) {
return;
}
// Create the Master device
StaticJsonDocument<EMSESP_MAX_JSON_SIZE_MEDIUM> doc;
StaticJsonDocument<EMSESP_MAX_JSON_SIZE_SMALL> doc;
doc["name"] = F_(EMSESP);
doc["uniq_id"] = F_(solar);
doc["ic"] = F_(iconthermostat);
@@ -183,10 +185,10 @@ void Solar::register_mqtt_ha_config(bool force) {
Mqtt::register_mqtt_ha_sensor(nullptr, nullptr, F_(heatExchangerTemp), this->device_type(), "heatExchangerTemp", F_(degrees), nullptr);
Mqtt::register_mqtt_ha_sensor(nullptr, nullptr, F_(solarPumpModulation), this->device_type(), "solarPumpModulation", F_(percent), nullptr);
Mqtt::register_mqtt_ha_sensor(nullptr, nullptr, F_(cylinderPumpModulation), this->device_type(), "cylinderPumpModulation", F_(percent), nullptr);
Mqtt::register_mqtt_ha_sensor(nullptr, nullptr, F_(pumpWorkMin), this->device_type(), "pumpWorkMin", nullptr, nullptr);
Mqtt::register_mqtt_ha_sensor(nullptr, nullptr, F_(energyLastHour), this->device_type(), "energyLastHour", nullptr, nullptr);
Mqtt::register_mqtt_ha_sensor(nullptr, nullptr, F_(energyToday), this->device_type(), "energyToday", nullptr, nullptr);
Mqtt::register_mqtt_ha_sensor(nullptr, nullptr, F_(energyTotal), this->device_type(), "energyTotal", nullptr, nullptr);
Mqtt::register_mqtt_ha_sensor(nullptr, nullptr, F_(pumpWorkMin), this->device_type(), "pumpWorkMin", F_(min), nullptr);
Mqtt::register_mqtt_ha_sensor(nullptr, nullptr, F_(energyLastHour), this->device_type(), "energyLastHour", F_(wh), nullptr);
Mqtt::register_mqtt_ha_sensor(nullptr, nullptr, F_(energyToday), this->device_type(), "energyToday", F_(wh), nullptr);
Mqtt::register_mqtt_ha_sensor(nullptr, nullptr, F_(energyTotal), this->device_type(), "energyTotal", F_(kwh), nullptr);
Mqtt::register_mqtt_ha_sensor(nullptr, nullptr, F_(solarPump), this->device_type(), "solarPump", nullptr, nullptr);
Mqtt::register_mqtt_ha_sensor(nullptr, nullptr, F_(valveStatus), this->device_type(), "valveStatus", nullptr, nullptr);
Mqtt::register_mqtt_ha_sensor(nullptr, nullptr, F_(tankHeated), this->device_type(), "tankHeated", nullptr, nullptr);
@@ -450,6 +452,13 @@ void Solar::process_SM100Energy(std::shared_ptr<const Telegram> telegram) {
changed_ |= telegram->read_value(energyTotal_, 8); // total / 10 in kWh
}
/*
* SM100Time - type 0x0391 EMS+ for pump working time
*/
void Solar::process_SM100Time(std::shared_ptr<const Telegram> telegram) {
changed_ |= telegram->read_value(pumpWorkMin_, 1, 3);
}
/*
* Junkers ISM1 Solar Module - type 0x0103 EMS+ for energy readings
* e.g. B0 00 FF 00 00 03 32 00 00 00 00 13 00 D6 00 00 00 FB D0 F0

View File

@@ -44,7 +44,7 @@ class Solar : public EMSdevice {
private:
static uuid::log::Logger logger_;
void register_mqtt_ha_config(bool force);
void register_mqtt_ha_config();
int16_t collectorTemp_ = EMS_VALUE_SHORT_NOTSET; // TS1: Temperature sensor for collector array 1
int16_t tankBottomTemp_ = EMS_VALUE_SHORT_NOTSET; // TS2: Temperature sensor 1 cylinder, bottom (solar thermal system)
@@ -106,6 +106,7 @@ class Solar : public EMSdevice {
void process_SM100Status2(std::shared_ptr<const Telegram> telegram);
void process_SM100CollectorConfig(std::shared_ptr<const Telegram> telegram);
void process_SM100Energy(std::shared_ptr<const Telegram> telegram);
void process_SM100Time(std::shared_ptr<const Telegram> telegram);
void process_SM100wwTemperature(std::shared_ptr<const Telegram> telegram);
void process_SM100wwStatus(std::shared_ptr<const Telegram> telegram);

View File

@@ -114,10 +114,12 @@ Thermostat::Thermostat(uint8_t device_type, uint8_t device_id, uint8_t product_i
monitor_typeids = {0x02A5, 0x02A6, 0x02A7, 0x02A8};
set_typeids = {0x02B9, 0x02BA, 0x02BB, 0x02BC};
summer_typeids = {0x02AF, 0x02B0, 0x02B1, 0x02B2};
curve_typeids = {0x029B, 0x029C, 0x029D, 0x029E};
for (uint8_t i = 0; i < monitor_typeids.size(); i++) {
register_telegram_type(monitor_typeids[i], F("RC300Monitor"), false, [&](std::shared_ptr<const Telegram> t) { process_RC300Monitor(t); });
register_telegram_type(set_typeids[i], F("RC300Set"), false, [&](std::shared_ptr<const Telegram> t) { process_RC300Set(t); });
register_telegram_type(summer_typeids[i], F("RC300Summer"), false, [&](std::shared_ptr<const Telegram> t) { process_RC300Summer(t); });
register_telegram_type(curve_typeids[i], F("RC300Curves"), false, [&](std::shared_ptr<const Telegram> t) { process_RC300Curve(t); });
}
register_telegram_type(0x2F5, F("RC300WWmode"), true, [&](std::shared_ptr<const Telegram> t) { process_RC300WWmode(t); });
register_telegram_type(0x31B, F("RC300WWtemp"), true, [&](std::shared_ptr<const Telegram> t) { process_RC300WWtemp(t); });
@@ -171,12 +173,15 @@ Thermostat::Thermostat(uint8_t device_type, uint8_t device_id, uint8_t product_i
for (uint8_t i = 0; i < summer_typeids.size(); i++) {
EMSESP::send_read_request(summer_typeids[i], device_id);
}
} // namespace emsesp
for (uint8_t i = 0; i < curve_typeids.size(); i++) {
EMSESP::send_read_request(curve_typeids[i], device_id);
}
}
// prepare data for Web UI
void Thermostat::device_info_web(JsonArray & root) {
StaticJsonDocument<EMSESP_MAX_JSON_SIZE_SMALL> doc_main;
JsonObject json_main = doc_main.to<JsonObject>();
StaticJsonDocument<EMSESP_MAX_JSON_SIZE_MEDIUM> doc_main;
JsonObject json_main = doc_main.to<JsonObject>();
if (export_values_main(json_main)) {
print_value_json(root, F("time"), nullptr, F_(time), nullptr, json_main);
print_value_json(root, F("errorcode"), nullptr, F_(error), nullptr, json_main);
@@ -194,6 +199,7 @@ void Thermostat::device_info_web(JsonArray & root) {
print_value_json(root, F("wwmode"), nullptr, F_(wwmode), nullptr, json_main);
print_value_json(root, F("wwtemp"), nullptr, F_(wwtemp), nullptr, json_main);
print_value_json(root, F("wwtemplow"), nullptr, F_(wwtemplow), nullptr, json_main);
print_value_json(root, F("wwextra1"), nullptr, F_(wwextra1), nullptr, json_main);
print_value_json(root, F("wwcircmode"), nullptr, F_(wwcircmode), nullptr, json_main);
}
@@ -226,6 +232,8 @@ void Thermostat::device_info_web(JsonArray & root) {
print_value_json(root, F("designtemp"), FPSTR(prefix_str), F_(designtemp), F_(degrees), json);
print_value_json(root, F("roominfluence"), FPSTR(prefix_str), F_(roominfluence), F_(degrees), json);
print_value_json(root, F("flowtempoffset"), FPSTR(prefix_str), F_(flowtempoffset), F_(degrees), json);
print_value_json(root, F("minflowtemp"), FPSTR(prefix_str), F_(minflowtemp), F_(degrees), json);
print_value_json(root, F("maxflowtemp"), FPSTR(prefix_str), F_(maxflowtemp), F_(degrees), json);
print_value_json(root, F("summertemp"), FPSTR(prefix_str), F_(summertemp), F_(degrees), json);
print_value_json(root, F("summermode"), FPSTR(prefix_str), F_(summermode), F_(degrees), json);
print_value_json(root, F("mode"), FPSTR(prefix_str), F_(mode), nullptr, json);
@@ -278,6 +286,7 @@ void Thermostat::show_values(uuid::console::Shell & shell) {
print_value_json(shell, F("wwmode"), nullptr, F_(wwmode), nullptr, json_main);
print_value_json(shell, F("wwtemp"), nullptr, F_(wwtemp), nullptr, json_main);
print_value_json(shell, F("wwtemplow"), nullptr, F_(wwtemplow), nullptr, json_main);
print_value_json(shell, F("wwextra1"), nullptr, F_(wwextra1), nullptr, json_main);
print_value_json(shell, F("wwcircmode"), nullptr, F_(wwcircmode), nullptr, json_main);
}
@@ -310,6 +319,8 @@ void Thermostat::show_values(uuid::console::Shell & shell) {
print_value_json(shell, F("designtemp"), F_(2spaces), F_(designtemp), F_(degrees), json);
print_value_json(shell, F("roominfluence"), F_(2spaces), F_(roominfluence), F_(degrees), json);
print_value_json(shell, F("flowtempoffset"), F_(2spaces), F_(flowtempoffset), F_(degrees), json);
print_value_json(shell, F("minflowtemp"), F_(2spaces), F_(minflowtemp), F_(degrees), json);
print_value_json(shell, F("maxflowtemp"), F_(2spaces), F_(maxflowtemp), F_(degrees), json);
print_value_json(shell, F("summertemp"), F_(2spaces), F_(summertemp), F_(degrees), json);
print_value_json(shell, F("summermode"), F_(2spaces), F_(summermode), F_(degrees), json);
print_value_json(shell, F("mode"), F_(2spaces), F_(mode), nullptr, json);
@@ -325,10 +336,16 @@ void Thermostat::publish_values(JsonObject & json, bool force) {
if (EMSESP::actual_master_thermostat() != this->device_id()) {
return;
}
// see if we have already registered this with HA MQTT Discovery, if not send the config
if (Mqtt::mqtt_format() == Mqtt::Format::HA) {
if (!ha_config(force)) {
return;
}
}
StaticJsonDocument<EMSESP_MAX_JSON_SIZE_MEDIUM> doc;
JsonObject json_data = doc.to<JsonObject>();
bool has_data = false;
StaticJsonDocument<EMSESP_MAX_JSON_SIZE_LARGE> doc;
JsonObject json_data = doc.to<JsonObject>();
bool has_data = false;
// if MQTT is in single mode send out the main data to the thermostat_data topic
has_data |= export_values_main(json_data);
@@ -343,10 +360,6 @@ void Thermostat::publish_values(JsonObject & json, bool force) {
// if we're in HA or CUSTOM, send out the complete topic with all the data
if (Mqtt::mqtt_format() != Mqtt::Format::SINGLE && has_data) {
// see if we have already registered this with HA MQTT Discovery, if not send the config
if (Mqtt::mqtt_format() == Mqtt::Format::HA) {
ha_config(force);
}
Mqtt::publish(F("thermostat_data"), json_data);
}
}
@@ -473,6 +486,16 @@ bool Thermostat::export_values_main(JsonObject & rootThermostat) {
rootThermostat["wwtemplow"] = wwTempLow_;
}
// Warm water extra1
if (Helpers::hasValue(wwExtra1_)) {
rootThermostat["wwextra1"] = wwExtra1_;
}
// Warm water extra2
if (Helpers::hasValue(wwExtra2_)) {
rootThermostat["wwextra2"] = wwExtra2_;
}
// Warm Water circulation mode
if (Helpers::hasValue(wwCircMode_)) {
char s[7];
@@ -600,6 +623,16 @@ bool Thermostat::export_values_hc(uint8_t mqtt_format, JsonObject & rootThermost
dataThermostat["flowtempoffset"] = hc->flowtempoffset;
}
// Min Flow temperature offset
if (Helpers::hasValue(hc->minflowtemp)) {
dataThermostat["minflowtemp"] = hc->minflowtemp;
}
// Max Flow temperature offset
if (Helpers::hasValue(hc->maxflowtemp)) {
dataThermostat["maxflowtemp"] = hc->maxflowtemp;
}
// Summer temperature
if (Helpers::hasValue(hc->summertemp)) {
dataThermostat["summertemp"] = hc->summertemp;
@@ -654,23 +687,32 @@ bool Thermostat::export_values_hc(uint8_t mqtt_format, JsonObject & rootThermost
}
// set up HA MQTT Discovery
void Thermostat::ha_config(bool force) {
bool Thermostat::ha_config(bool force) {
if (!Mqtt::connected()) {
return;
return false;
}
if (force) {
for (const auto & hc : heating_circuits_) {
hc->ha_registered(false);
}
ha_registered(false);
}
if (force || !ha_registered()) {
if (!ha_registered()) {
register_mqtt_ha_config();
ha_registered(true);
return false;
}
// check to see which heating circuits need publishing
for (const auto & hc : heating_circuits_) {
if (force || (hc->is_active() && !hc->ha_registered())) {
if (hc->is_active() && !hc->ha_registered()) {
register_mqtt_ha_config(hc->hc_num());
hc->ha_registered(true);
return false;
}
}
return true;
}
// returns the heating circuit object based on the hc number
@@ -679,13 +721,15 @@ std::shared_ptr<Thermostat::HeatingCircuit> Thermostat::heating_circuit(const ui
// if hc_num is 0 then return the first existing hc in the list
if (hc_num == AUTO_HEATING_CIRCUIT) {
for (const auto & heating_circuit : heating_circuits_) {
return heating_circuit;
if (heating_circuit->is_active()) {
return heating_circuit;
}
}
}
// otherwise find a match
for (const auto & heating_circuit : heating_circuits_) {
if (heating_circuit->hc_num() == hc_num) {
if ((heating_circuit->hc_num() == hc_num) && heating_circuit->is_active()) {
return heating_circuit;
}
}
@@ -773,10 +817,10 @@ std::shared_ptr<Thermostat::HeatingCircuit> Thermostat::heating_circuit(std::sha
return heating_circuits_.back(); // even after sorting, this should still point back to the newly created HC
}
// publish config topic for HA MQTT Discovery
// publish config topic for HA MQTT Discovery for main thermostat values
// homeassistant/climate/ems-esp/thermostat/config
void Thermostat::register_mqtt_ha_config() {
StaticJsonDocument<EMSESP_MAX_JSON_SIZE_MEDIUM> doc;
StaticJsonDocument<EMSESP_MAX_JSON_SIZE_SMALL> doc;
doc["uniq_id"] = F("thermostat");
doc["ic"] = F("mdi:home-thermometer-outline");
@@ -808,6 +852,7 @@ void Thermostat::register_mqtt_ha_config() {
if (model == EMS_DEVICE_FLAG_RC300 || model == EMS_DEVICE_FLAG_RC100) {
Mqtt::register_mqtt_ha_sensor(nullptr, nullptr, F_(dampedtemp), this->device_type(), "dampedtemp", F_(degrees), F_(icontemperature));
Mqtt::register_mqtt_ha_sensor(nullptr, nullptr, F_(building), this->device_type(), "building", nullptr, nullptr);
Mqtt::register_mqtt_ha_sensor(nullptr, nullptr, F_(minexttemp), this->device_type(), "minexttemp", F_(degrees), F_(icontemperature));
Mqtt::register_mqtt_ha_sensor(nullptr, nullptr, F_(floordry), this->device_type(), "floordry", nullptr, nullptr);
Mqtt::register_mqtt_ha_sensor(nullptr, nullptr, F_(floordrytemp), this->device_type(), "floordrytemp", F_(degrees), F_(icontemperature));
Mqtt::register_mqtt_ha_sensor(nullptr, nullptr, F_(wwmode), this->device_type(), "wwmode", nullptr, nullptr);
@@ -826,18 +871,18 @@ void Thermostat::register_mqtt_ha_config() {
}
}
// publish config topic for HA MQTT Discovery
// publish config topic for HA MQTT Discovery for each of the heating circuit
// e.g. homeassistant/climate/ems-esp/thermostat_hc1/config
void Thermostat::register_mqtt_ha_config(uint8_t hc_num) {
StaticJsonDocument<EMSESP_MAX_JSON_SIZE_MEDIUM> doc;
char str1[40];
char str1[20];
snprintf_P(str1, sizeof(str1), PSTR("Thermostat hc%d"), hc_num);
char str2[40];
char str2[20];
snprintf_P(str2, sizeof(str2), PSTR("thermostat_hc%d"), hc_num);
char str3[40];
char str3[25];
snprintf_P(str3, sizeof(str3), PSTR("~/%s"), str2);
doc["mode_cmd_t"] = str3;
doc["temp_cmd_t"] = str3;
@@ -909,7 +954,7 @@ void Thermostat::register_mqtt_ha_config(uint8_t hc_num) {
// for each of the heating circuits
std::string topic2(100, '\0');
snprintf_P(&topic2[0], topic2.capacity() + 1, PSTR("thermostat_hc%d"), hc_num);
register_mqtt_topic(topic2, [=](const char * m) { return thermostat_ha_cmd(m, hc_num); });
register_mqtt_topic(topic2, [&](const char * m) { return thermostat_ha_cmd(m, hc_num); });
char hc_name[10]; // hc{1-4}
strlcpy(hc_name, "hc", 10);
@@ -918,6 +963,9 @@ void Thermostat::register_mqtt_ha_config(uint8_t hc_num) {
Mqtt::register_mqtt_ha_sensor(hc_name, nullptr, F_(mode), this->device_type(), "mode", nullptr, nullptr);
Mqtt::register_mqtt_ha_sensor(hc_name, nullptr, F_(seltemp), this->device_type(), "seltemp", F_(degrees), F_(icontemperature));
Mqtt::register_mqtt_ha_sensor(hc_name, nullptr, F_(currtemp), this->device_type(), "currtemp", F_(degrees), F_(icontemperature));
uint8_t model = this->model();
switch (model) {
case EMS_DEVICE_FLAG_RC100:
@@ -927,6 +975,12 @@ void Thermostat::register_mqtt_ha_config(uint8_t hc_num) {
Mqtt::register_mqtt_ha_sensor(hc_name, nullptr, F_(manualtemp), this->device_type(), "manualtemp", F_(degrees), F_(icontemperature));
Mqtt::register_mqtt_ha_sensor(hc_name, nullptr, F_(comforttemp), this->device_type(), "comforttemp", F_(degrees), F_(icontemperature));
Mqtt::register_mqtt_ha_sensor(hc_name, nullptr, F_(summertemp), this->device_type(), "summertemp", F_(degrees), F_(icontemperature));
Mqtt::register_mqtt_ha_sensor(hc_name, nullptr, F_(designtemp), this->device_type(), "designtemp", F_(degrees), F_(icontemperature));
Mqtt::register_mqtt_ha_sensor(hc_name, nullptr, F_(offsettemp), this->device_type(), "offsettemp", F_(degrees), F_(icontemperature));
Mqtt::register_mqtt_ha_sensor(hc_name, nullptr, F_(minflowtemp), this->device_type(), "minflowtemp", F_(degrees), F_(icontemperature));
Mqtt::register_mqtt_ha_sensor(hc_name, nullptr, F_(maxflowtemp), this->device_type(), "maxflowtemp", F_(degrees), F_(icontemperature));
Mqtt::register_mqtt_ha_sensor(hc_name, nullptr, F_(roominfluence), this->device_type(), "roominfluence", F_(degrees), F_(icontemperature));
Mqtt::register_mqtt_ha_sensor(hc_name, nullptr, F_(nofrosttemp), this->device_type(), "nofrosttemp", F_(degrees), F_(icontemperature));
break;
case EMS_DEVICE_FLAG_RC20_2:
Mqtt::register_mqtt_ha_sensor(hc_name, nullptr, F_(daytemp), this->device_type(), "daytemp", F_(degrees), F_(icontemperature));
@@ -944,6 +998,8 @@ void Thermostat::register_mqtt_ha_config(uint8_t hc_num) {
Mqtt::register_mqtt_ha_sensor(hc_name, nullptr, F_(summertemp), this->device_type(), "summertemp", F_(degrees), F_(icontemperature));
Mqtt::register_mqtt_ha_sensor(hc_name, nullptr, F_(nofrosttemp), this->device_type(), "nofrosttemp", F_(degrees), F_(icontemperature));
Mqtt::register_mqtt_ha_sensor(hc_name, nullptr, F_(roominfluence), this->device_type(), "roominfluence", F_(degrees), F_(icontemperature));
Mqtt::register_mqtt_ha_sensor(hc_name, nullptr, F_(minflowtemp), this->device_type(), "minflowtemp", F_(degrees), F_(icontemperature));
Mqtt::register_mqtt_ha_sensor(hc_name, nullptr, F_(maxflowtemp), this->device_type(), "maxflowtemp", F_(degrees), F_(icontemperature));
break;
case EMS_DEVICE_FLAG_JUNKERS:
Mqtt::register_mqtt_ha_sensor(hc_name, nullptr, F_(modetype), this->device_type(), "modetype", nullptr, nullptr);
@@ -1094,6 +1150,15 @@ std::string Thermostat::mode_tostring(uint8_t mode) {
case HeatingCircuit::Mode::DESIGN:
return read_flash_string(F("design"));
break;
case HeatingCircuit::Mode::MINFLOW:
return read_flash_string(F("minflow"));
break;
case HeatingCircuit::Mode::MAXFLOW:
return read_flash_string(F("maxflow"));
break;
case HeatingCircuit::Mode::ROOMINFLUENCE:
return read_flash_string(F("roominfluence"));
break;
default:
case HeatingCircuit::Mode::UNKNOWN:
return read_flash_string(F("unknown"));
@@ -1268,8 +1333,28 @@ void Thermostat::process_RC300Set(std::shared_ptr<const Telegram> telegram) {
// types 0x2AF ff
void Thermostat::process_RC300Summer(std::shared_ptr<const Telegram> telegram) {
std::shared_ptr<Thermostat::HeatingCircuit> hc = heating_circuit(telegram);
changed_ |= telegram->read_value(hc->roominfluence, 0);
changed_ |= telegram->read_value(hc->offsettemp, 2);
changed_ |= telegram->read_value(hc->summertemp, 6);
changed_ |= telegram->read_value(hc->summer_setmode, 7);
if (hc->heatingtype < 3) {
changed_ |= telegram->read_value(hc->designtemp, 4);
} else {
changed_ |= telegram->read_value(hc->designtemp, 5);
}
changed_ |= telegram->read_value(hc->minflowtemp, 8);
}
// types 0x29B ff
void Thermostat::process_RC300Curve(std::shared_ptr<const Telegram> telegram) {
std::shared_ptr<Thermostat::HeatingCircuit> hc = heating_circuit(telegram);
changed_ |= telegram->read_value(hc->heatingtype, 1); // 1=radiator, 2=convector, 3=floor
changed_ |= telegram->read_value(hc->nofrosttemp, 6);
if (hc->heatingtype < 3) {
changed_ |= telegram->read_value(hc->maxflowtemp, 8);
} else {
changed_ |= telegram->read_value(hc->maxflowtemp, 7);
}
}
// types 0x31B (and 0x31C?)
@@ -1306,6 +1391,7 @@ void Thermostat::process_RC300OutdoorTemp(std::shared_ptr<const Telegram> telegr
// 0x240 RC300 parameter
void Thermostat::process_RC300Settings(std::shared_ptr<const Telegram> telegram) {
changed_ |= telegram->read_value(ibaBuildingType_, 9); // 1=light, 2=medium, 3=heavy
changed_ |= telegram->read_value(ibaMinExtTemperature_, 10);
}
// 0x267 RC300 floordrying
@@ -1370,10 +1456,13 @@ void Thermostat::process_RC35Set(std::shared_ptr<const Telegram> telegram) {
changed_ |= telegram->read_value(hc->summertemp, 22); // is * 1
changed_ |= telegram->read_value(hc->nofrosttemp, 23); // is * 1
changed_ |= telegram->read_value(hc->flowtempoffset, 24); // is * 1, only in mixed circuits
changed_ |= telegram->read_value(hc->minflowtemp, 16);
if (hc->heatingtype == 3) {
changed_ |= telegram->read_value(hc->designtemp, 36); // is * 1
changed_ |= telegram->read_value(hc->designtemp, 36); // is * 1
changed_ |= telegram->read_value(hc->maxflowtemp, 35); // is * 1
} else {
changed_ |= telegram->read_value(hc->designtemp, 17); // is * 1
changed_ |= telegram->read_value(hc->designtemp, 17); // is * 1
changed_ |= telegram->read_value(hc->maxflowtemp, 15); // is * 1
}
}
@@ -1564,24 +1653,6 @@ bool Thermostat::set_control(const char * value, const int8_t id) {
return true;
}
// Set roominfluence
bool Thermostat::set_roominfluence(const char * value, const int8_t id) {
uint8_t hc_num = (id == -1) ? AUTO_HEATING_CIRCUIT : id;
std::shared_ptr<Thermostat::HeatingCircuit> hc = heating_circuit(hc_num);
if (hc == nullptr) {
LOG_WARNING(F("Set roominfluence: Heating Circuit %d not found or activated"), hc_num);
return false;
}
int t = 0;
if (!Helpers::value2number(value, t)) {
LOG_WARNING(F("Set roominfluence: Invalid value"));
return false;
}
LOG_INFO(F("Setting roominfluence to %d"), t);
write_command(set_typeids[hc->hc_num() - 1], 4, t, set_typeids[hc->hc_num() - 1]);
return true;
}
// sets the thermostat ww working mode, where mode is a string, ems and ems+
bool Thermostat::set_wwmode(const char * value, const int8_t id) {
uint8_t set = 0xFF;
@@ -1627,6 +1698,19 @@ bool Thermostat::set_wwtemplow(const char * value, const int8_t id) {
return true;
}
// Set ww onetime RC300, ems+
bool Thermostat::set_wwonetime(const char * value, const int8_t id) {
bool b = false;
if (!Helpers::value2bool(value, b)) {
LOG_WARNING(F("Set warm water onetime: Invalid value"));
return false;
}
char s[7];
LOG_INFO(F("Setting warm water onetime to %s"), Helpers::render_boolean(s, b));
write_command(0x02F5, 11, b ? 0xFF : 0x00, 0x031D);
return true;
}
// sets the thermostat ww circulation working mode, where mode is a string
bool Thermostat::set_wwcircmode(const char * value, const int8_t id) {
@@ -1957,6 +2041,15 @@ bool Thermostat::set_temperature(const float temperature, const std::string & mo
if (mode_tostring(HeatingCircuit::Mode::DESIGN) == mode) {
return set_temperature(temperature, HeatingCircuit::Mode::DESIGN, hc_num);
}
if (mode_tostring(HeatingCircuit::Mode::MINFLOW) == mode) {
return set_temperature(temperature, HeatingCircuit::Mode::MINFLOW, hc_num);
}
if (mode_tostring(HeatingCircuit::Mode::MAXFLOW) == mode) {
return set_temperature(temperature, HeatingCircuit::Mode::MAXFLOW, hc_num);
}
if (mode_tostring(HeatingCircuit::Mode::ROOMINFLUENCE) == mode) {
return set_temperature(temperature, HeatingCircuit::Mode::ROOMINFLUENCE, hc_num);
}
LOG_WARNING(F("Set temperature: Invalid mode"));
return false;
@@ -2005,6 +2098,49 @@ bool Thermostat::set_temperature(const float temperature, const uint8_t mode, co
case HeatingCircuit::Mode::ECO:
offset = 0x04; // eco offset
break;
case HeatingCircuit::Mode::OFFSET:
offset = 2;
set_typeid = summer_typeids[hc->hc_num() - 1];
validate_typeid = set_typeid;
break;
case HeatingCircuit::Mode::DESIGN:
set_typeid = summer_typeids[hc->hc_num() - 1];
validate_typeid = set_typeid;
if (hc->heatingtype == 3) {
offset = 5;
} else {
offset = 4;
}
factor = 1;
break;
case HeatingCircuit::Mode::MINFLOW:
set_typeid = summer_typeids[hc->hc_num() - 1];
validate_typeid = set_typeid;
offset = 8;
factor = 1;
break;
case HeatingCircuit::Mode::MAXFLOW:
set_typeid = curve_typeids[hc->hc_num() - 1];
validate_typeid = set_typeid;
if (hc->heatingtype == 3) {
offset = 7;
} else {
offset = 8;
}
factor = 1;
break;
case HeatingCircuit::Mode::NOFROST:
set_typeid = curve_typeids[hc->hc_num() - 1];
validate_typeid = set_typeid;
offset = 6;
factor = 1;
break;
case HeatingCircuit::Mode::ROOMINFLUENCE:
set_typeid = summer_typeids[hc->hc_num() - 1];
validate_typeid = set_typeid;
offset = 0;
factor = 1;
break;
default:
case HeatingCircuit::Mode::AUTO:
uint8_t mode_ = hc->get_mode(this->flags());
@@ -2067,6 +2203,22 @@ bool Thermostat::set_temperature(const float temperature, const uint8_t mode, co
offset = EMS_OFFSET_RC35Set_temp_nofrost;
factor = 1;
break;
case HeatingCircuit::Mode::ROOMINFLUENCE:
offset = 4;
factor = 1;
break;
case HeatingCircuit::Mode::MINFLOW:
offset = 16;
factor = 1;
break;
case HeatingCircuit::Mode::MAXFLOW:
if (hc->heatingtype == 3) {
offset = 35;
} else {
offset = 15;
}
factor = 1;
break;
default:
case HeatingCircuit::Mode::AUTO: // automatic selection, if no type is defined, we use the standard code
validate_typeid = monitor_typeids[hc->hc_num() - 1]; //get setpoint roomtemp back
@@ -2226,6 +2378,18 @@ bool Thermostat::set_flowtempoffset(const char * value, const int8_t id) {
return set_temperature_value(value, id, HeatingCircuit::Mode::FLOWOFFSET);
}
bool Thermostat::set_maxflowtemp(const char * value, const int8_t id) {
return set_temperature_value(value, id, HeatingCircuit::Mode::MAXFLOW);
}
bool Thermostat::set_minflowtemp(const char * value, const int8_t id) {
return set_temperature_value(value, id, HeatingCircuit::Mode::MINFLOW);
}
bool Thermostat::set_roominfluence(const char * value, const int8_t id) {
return set_temperature_value(value, id, HeatingCircuit::Mode::ROOMINFLUENCE);
}
// API commands for MQTT and Console
void Thermostat::add_commands() {
// if this thermostat doesn't support write, don't add the commands
@@ -2250,7 +2414,15 @@ void Thermostat::add_commands() {
register_mqtt_cmd(F("wwmode"), [&](const char * value, const int8_t id) { return set_wwmode(value, id); });
register_mqtt_cmd(F("wwtemp"), [&](const char * value, const int8_t id) { return set_wwtemp(value, id); });
register_mqtt_cmd(F("wwtemplow"), [&](const char * value, const int8_t id) { return set_wwtemplow(value, id); });
register_mqtt_cmd(F("wwonetime"), [&](const char * value, const int8_t id) { return set_wwonetime(value, id); });
register_mqtt_cmd(F("building"), [&](const char * value, const int8_t id) { return set_building(value, id); });
register_mqtt_cmd(F("nofrosttemp"), [&](const char * value, const int8_t id) { return set_nofrosttemp(value, id); });
register_mqtt_cmd(F("designtemp"), [&](const char * value, const int8_t id) { return set_designtemp(value, id); });
register_mqtt_cmd(F("offsettemp"), [&](const char * value, const int8_t id) { return set_offsettemp(value, id); });
register_mqtt_cmd(F("minflowtemp"), [&](const char * value, const int8_t id) { return set_minflowtemp(value, id); });
register_mqtt_cmd(F("maxflowtemp"), [&](const char * value, const int8_t id) { return set_maxflowtemp(value, id); });
register_mqtt_cmd(F("minexttemp"), [&](const char * value, const int8_t id) { return set_minexttemp(value, id); });
register_mqtt_cmd(F("roominfluence"), [&](const char * value, const int8_t id) { return set_roominfluence(value, id); });
break;
case EMS_DEVICE_FLAG_RC20_2:
register_mqtt_cmd(F("nighttemp"), [&](const char * value, const int8_t id) { return set_nighttemp(value, id); });
@@ -2280,6 +2452,8 @@ void Thermostat::add_commands() {
register_mqtt_cmd(F("wwcircmode"), [&](const char * value, const int8_t id) { return set_wwcircmode(value, id); });
register_mqtt_cmd(F("roominfluence"), [&](const char * value, const int8_t id) { return set_roominfluence(value, id); });
register_mqtt_cmd(F("flowtempoffset"), [&](const char * value, const int8_t id) { return set_flowtempoffset(value, id); });
register_mqtt_cmd(F("minflowtemp"), [&](const char * value, const int8_t id) { return set_minflowtemp(value, id); });
register_mqtt_cmd(F("maxflowtemp"), [&](const char * value, const int8_t id) { return set_maxflowtemp(value, id); });
break;
case EMS_DEVICE_FLAG_JUNKERS:
register_mqtt_cmd(F("nofrosttemp"), [&](const char * value, const int8_t id) { return set_nofrosttemp(value, id); });
@@ -2291,5 +2465,4 @@ void Thermostat::add_commands() {
}
}
} // namespace emsesp

View File

@@ -58,13 +58,15 @@ class Thermostat : public EMSdevice {
uint8_t heatingtype = EMS_VALUE_UINT_NOTSET; // type of heating: 1 radiator, 2 convectors, 3 floors, 4 room supply
uint8_t targetflowtemp = EMS_VALUE_UINT_NOTSET;
uint8_t summertemp = EMS_VALUE_UINT_NOTSET;
uint8_t nofrosttemp = EMS_VALUE_UINT_NOTSET;
int8_t nofrosttemp = EMS_VALUE_INT_NOTSET; // signed -20°C to +10°C
uint8_t designtemp = EMS_VALUE_UINT_NOTSET; // heating curve design temp at MinExtTemp
int8_t offsettemp = EMS_VALUE_INT_NOTSET; // heating curve offest temp at roomtemp signed!
uint8_t manualtemp = EMS_VALUE_UINT_NOTSET;
uint8_t summer_setmode = EMS_VALUE_UINT_NOTSET;
uint8_t roominfluence = EMS_VALUE_UINT_NOTSET;
uint8_t flowtempoffset = EMS_VALUE_UINT_NOTSET;
uint8_t minflowtemp = EMS_VALUE_UINT_NOTSET;
uint8_t maxflowtemp = EMS_VALUE_UINT_NOTSET;
uint8_t hc_num() const {
return hc_num_;
@@ -86,7 +88,7 @@ class Thermostat : public EMSdevice {
uint8_t get_mode(uint8_t flags) const;
uint8_t get_mode_type(uint8_t flags) const;
enum Mode : uint8_t { UNKNOWN, OFF, MANUAL, AUTO, DAY, NIGHT, HEAT, NOFROST, ECO, HOLIDAY, COMFORT, OFFSET, DESIGN, SUMMER, FLOWOFFSET };
enum Mode : uint8_t { UNKNOWN, OFF, MANUAL, AUTO, DAY, NIGHT, HEAT, NOFROST, ECO, HOLIDAY, COMFORT, OFFSET, DESIGN, SUMMER, FLOWOFFSET, MINFLOW, MAXFLOW, ROOMINFLUENCE };
// for sorting based on hc number
friend inline bool operator<(const std::shared_ptr<HeatingCircuit> & lhs, const std::shared_ptr<HeatingCircuit> & rhs) {
@@ -131,6 +133,7 @@ class Thermostat : public EMSdevice {
std::vector<uint16_t> set_typeids;
std::vector<uint16_t> timer_typeids;
std::vector<uint16_t> summer_typeids;
std::vector<uint16_t> curve_typeids;
std::string datetime_; // date and time stamp
std::string errorCode_; // code from 0xA2 as string i.e. "A22(816)"
@@ -245,7 +248,7 @@ class Thermostat : public EMSdevice {
void register_mqtt_ha_config();
void register_mqtt_ha_config(uint8_t hc_num);
void ha_config(bool force = false);
bool ha_config(bool force = false);
bool thermostat_ha_cmd(const char * message, uint8_t hc_num);
void process_RCOutdoorTemp(std::shared_ptr<const Telegram> telegram);
@@ -273,6 +276,7 @@ class Thermostat : public EMSdevice {
void process_RC300OutdoorTemp(std::shared_ptr<const Telegram> telegram);
void process_RC300Settings(std::shared_ptr<const Telegram> telegram);
void process_RC300Floordry(std::shared_ptr<const Telegram> telegram);
void process_RC300Curve(std::shared_ptr<const Telegram> telegram);
void process_JunkersMonitor(std::shared_ptr<const Telegram> telegram);
void process_JunkersSet(std::shared_ptr<const Telegram> telegram);
void process_JunkersSet2(std::shared_ptr<const Telegram> telegram);
@@ -308,11 +312,14 @@ class Thermostat : public EMSdevice {
bool set_remotetemp(const char * value, const int8_t id);
bool set_roominfluence(const char * value, const int8_t id);
bool set_flowtempoffset(const char * value, const int8_t id);
bool set_minflowtemp(const char * value, const int8_t id);
bool set_maxflowtemp(const char * value, const int8_t id);
// set functions - these don't use the id/hc, the parameters are ignored
bool set_wwmode(const char * value, const int8_t id);
bool set_wwtemp(const char * value, const int8_t id);
bool set_wwtemplow(const char * value, const int8_t id);
bool set_wwonetime(const char * value, const int8_t id);
bool set_wwcircmode(const char * value, const int8_t id);
bool set_datetime(const char * value, const int8_t id);
bool set_minexttemp(const char * value, const int8_t id);

View File

@@ -284,8 +284,7 @@ class EMSdevice {
uint16_t telegram_type_id_; // it's type_id
const __FlashStringHelper * telegram_type_name_; // e.g. RC20Message
bool fetch_; // if this type_id be queried automatically
process_function_p process_function_;
process_function_p process_function_;
TelegramFunction(uint16_t telegram_type_id, const __FlashStringHelper * telegram_type_name, bool fetch, process_function_p process_function)
: telegram_type_id_(telegram_type_id)

View File

@@ -63,6 +63,7 @@ bool EMSESP::read_next_ = false;
uint16_t EMSESP::publish_id_ = 0;
bool EMSESP::tap_water_active_ = false; // for when Boiler states we having running warm water. used in Shower()
uint32_t EMSESP::last_fetch_ = 0;
uint8_t EMSESP::publish_all_idx_ = 0;
uint8_t EMSESP::unique_id_count_ = 0;
// for a specific EMS device go and request data values
@@ -215,10 +216,9 @@ void EMSESP::show_ems(uuid::console::Shell & shell) {
shell.printfln(F(" #tx fails (after %d retries): %d"), TxService::MAXIMUM_TX_RETRIES, txservice_.telegram_fail_count());
shell.printfln(F(" Rx line quality: %d%%"), rxservice_.quality());
shell.printfln(F(" Tx line quality: %d%%"), txservice_.quality());
shell.println();
}
shell.println();
// Rx queue
auto rx_telegrams = rxservice_.queue();
if (rx_telegrams.empty()) {
@@ -290,29 +290,74 @@ void EMSESP::show_sensor_values(uuid::console::Shell & shell) {
// MQTT publish everything, immediately
void EMSESP::publish_all(bool force) {
if (force) {
publish_all_idx_ = 1;
return;
}
if (Mqtt::connected()) {
publish_device_values(EMSdevice::DeviceType::BOILER, force);
publish_device_values(EMSdevice::DeviceType::THERMOSTAT, force);
publish_device_values(EMSdevice::DeviceType::SOLAR, force);
publish_device_values(EMSdevice::DeviceType::MIXER, force);
publish_device_values(EMSdevice::DeviceType::BOILER, false);
publish_device_values(EMSdevice::DeviceType::THERMOSTAT, false);
publish_device_values(EMSdevice::DeviceType::SOLAR, false);
publish_device_values(EMSdevice::DeviceType::MIXER, false);
publish_other_values();
publish_sensor_values(true);
publish_sensor_values(true, false);
system_.send_heartbeat();
}
}
// on command "publish HA" loop and wait between devices for publishing all sensors
void EMSESP::publish_all_loop() {
static uint32_t last = 0;
if (!Mqtt::connected() || !publish_all_idx_) {
return;
}
// every HA-sensor takes 20 ms, wait ~2 sec to finish (boiler have ~70 sensors)
if ((uuid::get_uptime() - last < 2000)) {
return;
}
last = uuid::get_uptime();
switch (publish_all_idx_++) {
case 1:
publish_device_values(EMSdevice::DeviceType::BOILER, true);
break;
case 2:
publish_device_values(EMSdevice::DeviceType::THERMOSTAT, true);
break;
case 3:
publish_device_values(EMSdevice::DeviceType::SOLAR, true);
break;
case 4:
publish_device_values(EMSdevice::DeviceType::MIXER, true);
break;
case 5:
publish_other_values();
break;
case 6:
publish_sensor_values(true, true);
break;
case 7:
system_.send_heartbeat();
break;
default:
// all finished
publish_all_idx_ = 0;
last = 0;
}
}
// create json doc for the devices values and add to MQTT publish queue
// special case for Mixer units, since we want to bundle all devices together into one payload
void EMSESP::publish_device_values(uint8_t device_type, bool force) {
if (device_type == EMSdevice::DeviceType::MIXER && Mqtt::mqtt_format() != Mqtt::Format::SINGLE) {
DynamicJsonDocument doc(EMSESP_MAX_JSON_SIZE_LARGE);
JsonObject json = doc.to<JsonObject>();
// DynamicJsonDocument doc(EMSESP_MAX_JSON_SIZE_LARGE);
StaticJsonDocument<EMSESP_MAX_JSON_SIZE_LARGE> doc;
JsonObject json = doc.to<JsonObject>();
for (const auto & emsdevice : emsdevices) {
if (emsdevice && (emsdevice->device_type() == device_type)) {
emsdevice->publish_values(json, force);
}
}
doc.shrinkToFit();
// doc.shrinkToFit();
Mqtt::publish("mixer_data", doc.as<JsonObject>());
return;
}
@@ -335,9 +380,9 @@ void EMSESP::publish_other_values() {
}
}
void EMSESP::publish_sensor_values(const bool force) {
if (dallassensor_.updated_values() || force) {
dallassensor_.publish_values();
void EMSESP::publish_sensor_values(const bool time, const bool force) {
if (dallassensor_.updated_values() || time || force) {
dallassensor_.publish_values(force);
}
}
@@ -649,7 +694,7 @@ void EMSESP::show_devices(uuid::console::Shell & shell) {
// shell.printf(F("[factory ID: %d] "), device_class.first);
for (const auto & emsdevice : emsdevices) {
if ((emsdevice) && (emsdevice->device_type() == device_class.first)) {
shell.printf(F("%s: %s"), emsdevice->device_type_name().c_str(), emsdevice->to_string().c_str());
shell.printf(F("(%d) %s: %s"), emsdevice->unique_id(), emsdevice->device_type_name().c_str(), emsdevice->to_string().c_str());
if ((emsdevice->device_type() == EMSdevice::DeviceType::THERMOSTAT) && (emsdevice->device_id() == actual_master_thermostat())) {
shell.printf(F(" ** master device **"));
}
@@ -722,17 +767,20 @@ bool EMSESP::add_device(const uint8_t device_id, const uint8_t product_id, std::
return false; // not found
}
std::string name = uuid::read_flash_string(device_p->name);
emsdevices.push_back(EMSFactory::add(device_p->device_type, device_id, device_p->product_id, version, name, device_p->flags, brand));
emsdevices.back()->unique_id(++unique_id_count_);
auto name = uuid::read_flash_string(device_p->name);
auto device_type = device_p->device_type;
auto flags = device_p->flags;
LOG_DEBUG(F("Adding new device %s (device ID 0x%02X, product ID %d, version %s)"), name.c_str(), device_id, product_id, version.c_str());
emsdevices.push_back(EMSFactory::add(device_type, device_id, product_id, version, name, flags, brand));
emsdevices.back()->unique_id(++unique_id_count_);
fetch_device_values(device_id); // go and fetch its data
// add info command, but not for all devices
uint8_t device_type = device_p->device_type;
if ((device_type == DeviceType::CONNECT) || (device_type == DeviceType::CONTROLLER) || (device_type == DeviceType::GATEWAY)) {
return true;
}
Command::add_with_json(device_type, F_(info), [device_type](const char * value, const int8_t id, JsonObject & json) {
return command_info(device_type, json);
});
@@ -934,11 +982,12 @@ void EMSESP::loop() {
}
system_.loop(); // does LED and checks system health, and syslog service
rxservice_.loop(); // process any incoming Rx telegrams
shower_.loop(); // check for shower on/off
dallassensor_.loop(); // this will also send out via MQTT
mqtt_.loop(); // sends out anything in the queue via MQTT
console_.loop(); // telnet/serial console
rxservice_.loop(); // process any incoming Rx telegrams
publish_all_loop();
mqtt_.loop(); // sends out anything in the queue via MQTT
console_.loop(); // telnet/serial console
// force a query on the EMS devices to fetch latest data at a set interval (1 min)
if ((uuid::get_uptime() - last_fetch_ > EMS_FETCH_FREQUENCY)) {

View File

@@ -63,7 +63,7 @@ class EMSESP {
static void publish_device_values(uint8_t device_type, bool force = false);
static void publish_other_values();
static void publish_sensor_values(const bool force = false);
static void publish_sensor_values(const bool time, const bool force = false);
static void publish_all(bool force = false);
#ifdef EMSESP_STANDALONE
@@ -184,6 +184,7 @@ class EMSESP {
static void process_UBADevices(std::shared_ptr<const Telegram> telegram);
static void process_version(std::shared_ptr<const Telegram> telegram);
static void publish_response(std::shared_ptr<const Telegram> telegram);
static void publish_all_loop();
static bool command_info(uint8_t device_type, JsonObject & json);
@@ -206,6 +207,7 @@ class EMSESP {
static bool read_next_;
static uint16_t publish_id_;
static bool tap_water_active_;
static uint8_t publish_all_idx_;
static uint8_t unique_id_count_;
};

View File

@@ -66,10 +66,7 @@ MAKE_PSTR_WORD(publish)
MAKE_PSTR_WORD(bar)
MAKE_PSTR_WORD(min)
MAKE_PSTR_WORD(uA)
#if defined(EMSESP_DEBUG)
MAKE_PSTR_WORD(test)
#endif
MAKE_PSTR_WORD(timeout)
// for commands
MAKE_PSTR_WORD(call)
@@ -81,6 +78,7 @@ MAKE_PSTR_WORD(command)
MAKE_PSTR_WORD(commands)
MAKE_PSTR_WORD(info)
MAKE_PSTR_WORD(report)
MAKE_PSTR_WORD(test)
// devices
MAKE_PSTR_WORD(boiler)
@@ -116,7 +114,7 @@ MAKE_PSTR(bus_id_fmt, "Bus ID = %02X")
MAKE_PSTR(watchid_optional, "[ID]")
MAKE_PSTR(watch_format_optional, "[off | on | raw]")
MAKE_PSTR(invalid_watch, "Invalid watch type")
MAKE_PSTR(data_mandatory, "<\"XX XX ...\">")
MAKE_PSTR(data_mandatory, "\"XX XX ...\"")
MAKE_PSTR(percent, "%")
MAKE_PSTR(degrees, "°C")
MAKE_PSTR(asterisks, "********")
@@ -246,6 +244,8 @@ MAKE_PSTR(floordrytemp, "Floordrying temperature")
MAKE_PSTR(wwmode, "Warm water mode")
MAKE_PSTR(wwtemp, "Warm water high temperature")
MAKE_PSTR(wwtemplow, "Warm water low temperature")
MAKE_PSTR(wwextra1, "Warm water circuit 1 extra")
MAKE_PSTR(wwextra2, "Warm water circuit 2 extra")
MAKE_PSTR(wwcircmode, "Warm water circulation mode")
// thermostat - per heating circuit
@@ -267,6 +267,8 @@ MAKE_PSTR(summertemp, "Summer temperature")
MAKE_PSTR(summermode, "Summer mode")
MAKE_PSTR(roominfluence, "Room influence")
MAKE_PSTR(flowtempoffset, "Flow temperature offset")
MAKE_PSTR(minflowtemp, "Min. flow temperature")
MAKE_PSTR(maxflowtemp, "Max. flow temperature")
MAKE_PSTR(mode, "Mode")
MAKE_PSTR(modetype, "Mode type")

View File

@@ -39,9 +39,9 @@ bool Mqtt::mqtt_enabled_;
std::vector<Mqtt::MQTTSubFunction> Mqtt::mqtt_subfunctions_;
uint16_t Mqtt::mqtt_publish_fails_ = 0;
size_t Mqtt::maximum_mqtt_messages_ = Mqtt::MAX_MQTT_MESSAGES;
uint16_t Mqtt::mqtt_message_id_ = 0;
uint16_t Mqtt::mqtt_publish_fails_ = 0;
// size_t Mqtt::maximum_mqtt_messages_ = Mqtt::MAX_MQTT_MESSAGES;
uint16_t Mqtt::mqtt_message_id_ = 0;
std::list<Mqtt::QueuedMqttMessage> Mqtt::mqtt_messages_;
char will_topic_[Mqtt::MQTT_TOPIC_MAX_SIZE]; // because MQTT library keeps only char pointer
@@ -181,7 +181,7 @@ void Mqtt::show_mqtt(uuid::console::Shell & shell) {
return;
}
shell.printfln(F("MQTT queue (%d messages):"), mqtt_messages_.size());
shell.printfln(F("MQTT queue (%d/%d messages):"), mqtt_messages_.size(), MAX_MQTT_MESSAGES);
for (const auto & message : mqtt_messages_) {
auto content = message.content_;
@@ -211,14 +211,12 @@ void Mqtt::show_mqtt(uuid::console::Shell & shell) {
}
}
shell.println();
} // namespace emsesp
}
#if defined(EMSESP_DEBUG)
// simulate receiving a MQTT message, used only for testing
void Mqtt::incoming(const char * topic, const char * payload) {
on_message(topic, payload, strlen(payload));
}
#endif
// received an MQTT message that we subscribed too
void Mqtt::on_message(const char * topic, const char * payload, size_t len) {
@@ -358,6 +356,11 @@ void Mqtt::start() {
mqtt_enabled_ = mqttSettings.enabled;
});
// if MQTT disabled, quit
if (!mqtt_enabled_) {
return;
}
mqttClient_->onConnect([this](bool sessionPresent) { on_connect(); });
mqttClient_->onDisconnect([this](AsyncMqttClientDisconnectReason reason) {
@@ -529,7 +532,7 @@ std::shared_ptr<const MqttMessage> Mqtt::queue_message(const uint8_t operation,
}
// if the queue is full, make room but removing the last one
if (mqtt_messages_.size() >= maximum_mqtt_messages_) {
if (mqtt_messages_.size() >= MAX_MQTT_MESSAGES) {
mqtt_messages_.pop_front();
}
mqtt_messages_.emplace_back(mqtt_message_id_++, std::move(message));
@@ -611,26 +614,6 @@ void Mqtt::process_queue() {
return;
}
// show queue - Debug only
/*
Serial.printf("MQTT queue:\n\r");
for (const auto & message : mqtt_messages_) {
auto content = message.content_;
if (content->operation == Operation::PUBLISH) {
// Publish messages
Serial.printf(" [%02d] (Pub) topic=%s payload=%s (pid %d, retry #%d)\n\r",
message.id_,
content->topic.c_str(),
content->payload.c_str(),
message.packet_id_,
message.retry_count_);
} else {
// Subscribe messages
Serial.printf(" [%02d] (Sub) topic=%s\n\r", message.id_, content->topic.c_str());
}
}
*/
// fetch first from queue and create the full topic name
auto mqtt_message = mqtt_messages_.front();
auto message = mqtt_message.content_;
@@ -697,9 +680,8 @@ void Mqtt::register_mqtt_ha_binary_sensor(const __FlashStringHelper * name, cons
return;
}
return; // TODO
StaticJsonDocument<EMSESP_MAX_JSON_SIZE_SMALL> doc;
DynamicJsonDocument doc(EMSESP_MAX_JSON_SIZE_SMALL);
// StaticJsonDocument<EMSESP_MAX_JSON_SIZE_SMALL> doc;
doc["name"] = name;
doc["uniq_id"] = entity;
@@ -727,11 +709,13 @@ void Mqtt::register_mqtt_ha_binary_sensor(const __FlashStringHelper * name, cons
snprintf_P(ha_device, sizeof(ha_device), PSTR("ems-esp-%s"), EMSdevice::device_type_2_device_name(device_type).c_str());
ids.add(ha_device);
doc.shrinkToFit();
char topic[MQTT_TOPIC_MAX_SIZE];
snprintf_P(topic, sizeof(topic), PSTR("homeassistant/binary_sensor/ems-esp/%s/config"), entity);
// convert json to string and publish immediately with retain forced to true
char payload_text[300];
char payload_text[256];
serializeJson(doc, payload_text); // convert json to string
uint16_t packet_id = mqttClient_->publish(topic, 0, true, payload_text);
#if defined(EMSESP_STANDALONE)
@@ -744,7 +728,8 @@ void Mqtt::register_mqtt_ha_binary_sensor(const __FlashStringHelper * name, cons
}
#endif
delay(MQTT_PUBLISH_WAIT);
// delay(MQTT_PUBLISH_WAIT);
delay(50);
}
// HA config for a normal 'sensor' type
@@ -765,14 +750,15 @@ void Mqtt::register_mqtt_ha_sensor(const char * prefix,
if (prefix != nullptr) {
snprintf_P(new_entity, sizeof(new_entity), PSTR("%s.%s"), prefix, entity);
} else {
strcpy(new_entity, entity);
strncpy(new_entity, entity, sizeof(new_entity));
}
std::string device_name = EMSdevice::device_type_2_device_name(device_type);
char device_name[50];
strncpy(device_name, EMSdevice::device_type_2_device_name(device_type).c_str(), sizeof(device_name));
// build unique identifier, replacing all . with _ as not to break HA
std::string uniq(50, '\0');
snprintf_P(&uniq[0], uniq.capacity() + 1, PSTR("%s_%s"), device_name.c_str(), new_entity);
snprintf_P(&uniq[0], uniq.capacity() + 1, PSTR("%s_%s"), device_name, new_entity);
std::replace(uniq.begin(), uniq.end(), '.', '_');
// topic
@@ -782,9 +768,9 @@ void Mqtt::register_mqtt_ha_sensor(const char * prefix,
// state topic
char stat_t[MQTT_TOPIC_MAX_SIZE];
if (suffix != nullptr) {
snprintf_P(stat_t, sizeof(stat_t), PSTR("%s/%s_data%s"), hostname_.c_str(), device_name.c_str(), uuid::read_flash_string(suffix).c_str());
snprintf_P(stat_t, sizeof(stat_t), PSTR("%s/%s_data%s"), hostname_.c_str(), device_name, uuid::read_flash_string(suffix).c_str());
} else {
snprintf_P(stat_t, sizeof(stat_t), PSTR("%s/%s_data"), hostname_.c_str(), device_name.c_str());
snprintf_P(stat_t, sizeof(stat_t), PSTR("%s/%s_data"), hostname_.c_str(), device_name);
}
// value template
@@ -793,20 +779,22 @@ void Mqtt::register_mqtt_ha_sensor(const char * prefix,
// ha device
char ha_device[40];
snprintf_P(ha_device, sizeof(ha_device), PSTR("ems-esp-%s"), device_name.c_str());
snprintf_P(ha_device, sizeof(ha_device), PSTR("ems-esp-%s"), device_name);
// name
char new_name[50];
if (prefix != nullptr) {
snprintf_P(new_name, sizeof(new_name), PSTR("%s %s %s"), device_name.c_str(), prefix, uuid::read_flash_string(name).c_str());
snprintf_P(new_name, sizeof(new_name), PSTR("%s %s %s"), device_name, prefix, uuid::read_flash_string(name).c_str());
} else {
snprintf_P(new_name, sizeof(new_name), PSTR("%s %s"), device_name.c_str(), uuid::read_flash_string(name).c_str());
snprintf_P(new_name, sizeof(new_name), PSTR("%s %s"), device_name, uuid::read_flash_string(name).c_str());
}
new_name[0] = toupper(new_name[0]); // capitalize first letter
StaticJsonDocument<EMSESP_MAX_JSON_SIZE_SMALL> doc;
DynamicJsonDocument doc(EMSESP_MAX_JSON_SIZE_SMALL);
// StaticJsonDocument<EMSESP_MAX_JSON_SIZE_SMALL> doc;
doc["name"] = new_name;
doc["uniq_id"] = uniq;
doc["uniq_id"] = uniq.c_str();
if (uom != nullptr) {
doc["unit_of_meas"] = uom;
}
@@ -819,9 +807,9 @@ void Mqtt::register_mqtt_ha_sensor(const char * prefix,
JsonArray ids = dev.createNestedArray("ids");
ids.add(ha_device);
doc.shrinkToFit();
// convert json to string and publish immediately with retain forced to true
// std::string payload_text;
char payload_text[300];
char payload_text[256];
serializeJson(doc, payload_text); // convert json to string
uint16_t packet_id = mqttClient_->publish(topic, 0, true, payload_text);
@@ -836,6 +824,6 @@ void Mqtt::register_mqtt_ha_sensor(const char * prefix,
}
// delay(MQTT_PUBLISH_WAIT); // don't flood asynctcp
delay(50); // enough time to send the short message out
}
} // namespace emsesp

View File

@@ -38,9 +38,10 @@
using uuid::console::Shell;
#define EMSESP_MAX_JSON_SIZE_SMALL 256 // for smaller json docs when using StaticJsonDocument
#define EMSESP_MAX_JSON_SIZE_MEDIUM 768 // for smaller json docs from ems devices, when using StaticJsonDocument
#define EMSESP_MAX_JSON_SIZE_LARGE 2048 // for large json docs from ems devices, like boiler or thermostat data. Using DynamicJsonDocument
#define EMSESP_MAX_JSON_SIZE_SMALL 384 // for smaller json docs when using StaticJsonDocument
#define EMSESP_MAX_JSON_SIZE_MEDIUM 768 // for medium json docs from ems devices, when using StaticJsonDocument
#define EMSESP_MAX_JSON_SIZE_LARGE 1024 // for large json docs from ems devices, like boiler or thermostat data. Using StaticJsonDocument
#define EMSESP_MAX_JSON_SIZE_DYN 2048 // for large json docs from web. Using DynamicJsonDocument
namespace emsesp {
@@ -118,9 +119,7 @@ class Mqtt {
mqttClient_->disconnect();
}
#if defined(EMSESP_DEBUG)
void incoming(const char * topic, const char * payload); // for testing only
#endif
static bool connected() {
#if defined(EMSESP_STANDALONE)
@@ -171,11 +170,14 @@ class Mqtt {
static std::list<QueuedMqttMessage> mqtt_messages_;
static AsyncMqttClient * mqttClient_;
static uint16_t mqtt_message_id_;
static size_t maximum_mqtt_messages_;
static uint16_t mqtt_message_id_;
#if defined(EMSESP_STANDALONE)
static constexpr size_t MAX_MQTT_MESSAGES = 70; // size of queue
#else
static constexpr size_t MAX_MQTT_MESSAGES = 20; // size of queue
#endif
static constexpr size_t MAX_MQTT_MESSAGES = 70; // size of queue
static constexpr uint32_t MQTT_PUBLISH_WAIT = 200; // delay between sending publishes, to account for large payloads
static constexpr uint8_t MQTT_PUBLISH_MAX_RETRY = 3; // max retries for giving up on publishing

View File

@@ -28,7 +28,9 @@ void Shower::start() {
shower_alert_ = settings.shower_alert;
});
send_mqtt_stat(false); // send first MQTT publish
if (Mqtt::enabled()) {
send_mqtt_stat(false); // send first MQTT publish
}
}
void Shower::loop() {

View File

@@ -21,6 +21,10 @@
#include "version.h" // firmware version of EMS-ESP
#if defined(EMSESP_TEST)
#include "test/test.h"
#endif
namespace emsesp {
uuid::log::Logger System::logger_{F_(system), uuid::log::Facility::KERN};
@@ -37,11 +41,22 @@ bool System::hide_led_ = false;
uint8_t System::led_gpio_ = 0;
uint16_t System::analog_ = 0;
bool System::analog_enabled_ = false;
bool System::syslog_enabled_ = false;
std::string System::hostname_;
int8_t System::syslog_level_ = -1;
uint32_t System::syslog_mark_interval_ = 0;
String System::syslog_host_;
// send on/off to a gpio pin
// value: true = HIGH, false = LOW
// http://ems-esp/api?device=system&cmd=pin&data=1&id=2
bool System::command_pin(const char * value, const int8_t id) {
if (id < 0) {
return false;
}
bool v = false;
if (Helpers::value2bool(value, v)) {
pinMode(id, OUTPUT);
@@ -53,12 +68,34 @@ bool System::command_pin(const char * value, const int8_t id) {
return false;
}
// send raw
// send raw to ems
bool System::command_send(const char * value, const int8_t id) {
EMSESP::send_raw_telegram(value); // ignore id
return true;
}
// fetch device values
bool System::command_fetch(const char * value, const int8_t id) {
LOG_INFO(F("Requesting data from EMS devices"));
EMSESP::fetch_device_values();
return true;
}
// mqtt publish
bool System::command_publish(const char * value, const int8_t id) {
std::string ha(10, '\0');
if (Helpers::value2string(value, ha)) {
if (ha == "ha") {
EMSESP::publish_all(true); // includes HA
LOG_INFO(F("Publishing all data to MQTT, including HA configs"));
return true;
}
}
EMSESP::publish_all(); // ignore value and id
LOG_INFO(F("Publishing all data to MQTT"));
return true;
}
// restart EMS-ESP
void System::restart() {
LOG_NOTICE(F("Restarting system..."));
@@ -112,12 +149,20 @@ uint8_t System::free_mem() {
void System::syslog_init() {
// fetch settings
EMSESP::webSettingsService.read([&](WebSettings & settings) {
syslog_enabled_ = settings.syslog_enabled;
syslog_level_ = settings.syslog_level;
syslog_mark_interval_ = settings.syslog_mark_interval;
syslog_host_ = settings.syslog_host;
});
#ifndef EMSESP_STANDALONE
if (!syslog_enabled_) {
syslog_.log_level((uuid::log::Level)-1);
syslog_.mark_interval(0);
syslog_.destination((IPAddress)((uint32_t)0));
return;
}
syslog_.start(); // syslog service re-start
// configure syslog
@@ -151,11 +196,16 @@ void System::start() {
EMSESP::webSettingsService.read([&](WebSettings & settings) {
Command::add(EMSdevice::DeviceType::SYSTEM, settings.ems_bus_id, F_(pin), System::command_pin);
Command::add(EMSdevice::DeviceType::SYSTEM, settings.ems_bus_id, F_(send), System::command_send);
Command::add(EMSdevice::DeviceType::SYSTEM, settings.ems_bus_id, F_(publish), System::command_publish);
Command::add(EMSdevice::DeviceType::SYSTEM, settings.ems_bus_id, F_(fetch), System::command_fetch);
Command::add_with_json(EMSdevice::DeviceType::SYSTEM, F_(info), System::command_info);
Command::add_with_json(EMSdevice::DeviceType::SYSTEM, F_(report), System::command_report);
#if defined(EMSESP_TEST)
Command::add(EMSdevice::DeviceType::SYSTEM, settings.ems_bus_id, F_(test), System::command_test);
#endif
});
syslog_init(); // init SysLog
init();
}
@@ -169,6 +219,7 @@ void System::init() {
Helpers::bool_format(settings.bool_format);
analog_enabled_ = settings.analog_enabled;
});
syslog_init(); // init SysLog
EMSESP::esp8266React.getWiFiSettingsService()->read([&](WiFiSettings & settings) { hostname(settings.hostname.c_str()); });
@@ -207,8 +258,11 @@ void System::upload_status(bool in_progress) {
// checks system health and handles LED flashing wizardry
void System::loop() {
#ifndef EMSESP_STANDALONE
syslog_.loop();
#endif
if (syslog_enabled_) {
syslog_.loop();
}
led_monitor(); // check status and report back using the LED
system_check(); // check system health
if (analog_enabled_) {
@@ -230,6 +284,8 @@ void System::loop() {
show_mem("core");
}
#endif
#endif
#endif
}
@@ -481,13 +537,18 @@ void System::show_system(uuid::console::Shell & shell) {
EMSESP::webSettingsService.read([&](WebSettings & settings) {
shell.println();
shell.printfln(F("Syslog:"));
shell.print(F_(1space));
shell.printfln(F_(host_fmt), !settings.syslog_host.isEmpty() ? settings.syslog_host.c_str() : uuid::read_flash_string(F_(unset)).c_str());
shell.print(F_(1space));
shell.printfln(F_(log_level_fmt), uuid::log::format_level_lowercase(static_cast<uuid::log::Level>(settings.syslog_level)));
shell.print(F_(1space));
shell.printfln(F_(mark_interval_fmt), settings.syslog_mark_interval);
if (!settings.syslog_enabled) {
shell.printfln(F("Syslog: disabled"));
} else {
shell.printfln(F("Syslog:"));
shell.print(F_(1space));
shell.printfln(F_(host_fmt), !settings.syslog_host.isEmpty() ? settings.syslog_host.c_str() : uuid::read_flash_string(F_(unset)).c_str());
shell.print(F_(1space));
shell.printfln(F_(log_level_fmt), uuid::log::format_level_lowercase(static_cast<uuid::log::Level>(settings.syslog_level)));
shell.print(F_(1space));
shell.printfln(F_(mark_interval_fmt), settings.syslog_mark_interval);
}
});
#endif
@@ -652,7 +713,7 @@ bool System::check_upgrade() {
l_cfg.setAutoFormat(false);
LittleFS.setConfig(l_cfg); // do not auto format if it can't find LittleFS
if (LittleFS.begin()) {
#if defined(EMSESP_DEBUG)
#if defined(EMSESP_FORCE_SERIAL)
Serial.begin(115200);
Serial.println(F("FS is Littlefs"));
Serial.end();
@@ -667,7 +728,7 @@ bool System::check_upgrade() {
cfg.setAutoFormat(false); // prevent formatting when opening SPIFFS filesystem
SPIFFS.setConfig(cfg);
if (!SPIFFS.begin()) {
#if defined(EMSESP_DEBUG)
#if defined(EMSESP_FORCE_SERIAL)
Serial.begin(115200);
Serial.println(F("No old SPIFFS found!"));
Serial.end();
@@ -766,9 +827,7 @@ bool System::check_upgrade() {
file.close();
if (failed) {
#if defined(EMSESP_DEBUG)
Serial.println(F("Failed to read system config. Quitting."));
#endif
SPIFFS.end();
Serial.end();
return false;
@@ -791,10 +850,6 @@ bool System::check_upgrade() {
Serial.printf(PSTR("Error. Failed to deserialize custom json, error %s\n"), error.c_str());
failed = true;
} else {
#if defined(EMSESP_DEBUG)
serializeJson(doc, Serial);
Serial.println();
#endif
custom_settings = doc["settings"];
EMSESP::webSettingsService.update(
[&](WebSettings & settings) {
@@ -803,6 +858,7 @@ bool System::check_upgrade() {
settings.shower_timer = custom_settings["shower_timer"] | EMSESP_DEFAULT_SHOWER_TIMER;
settings.master_thermostat = custom_settings["master_thermostat"] | EMSESP_DEFAULT_MASTER_THERMOSTAT;
settings.ems_bus_id = custom_settings["bus_id"] | EMSESP_DEFAULT_EMS_BUS_ID;
settings.syslog_enabled = false;
settings.syslog_host = EMSESP_DEFAULT_SYSLOG_HOST;
settings.syslog_level = EMSESP_DEFAULT_SYSLOG_LEVEL;
settings.syslog_mark_interval = EMSESP_DEFAULT_SYSLOG_MARK_INTERVAL;
@@ -821,9 +877,7 @@ bool System::check_upgrade() {
SPIFFS.end();
if (failed) {
#if defined(EMSESP_DEBUG)
Serial.println(F("Failed to read custom config. Quitting."));
#endif
Serial.end();
return false;
}
@@ -918,6 +972,7 @@ bool System::command_info(const char * value, const int8_t id, JsonObject & json
JsonObject node = json.createNestedObject("Settings");
node["tx_mode"] = settings.tx_mode;
node["ems_bus_id"] = settings.ems_bus_id;
node["syslog_enabled"] = settings.syslog_enabled;
node["syslog_level"] = settings.syslog_level;
node["syslog_mark_interval"] = settings.syslog_mark_interval;
node["syslog_host"] = settings.syslog_host;
@@ -1030,4 +1085,13 @@ bool System::command_report(const char * value, const int8_t id, JsonObject & js
return true;
}
#if defined(EMSESP_TEST)
// run a test
// e.g. http://ems-esp/api?device=system&cmd=test&data=boiler
bool System::command_test(const char * value, const int8_t id) {
Test::run_test(value, id);
return true;
}
#endif
} // namespace emsesp

View File

@@ -50,18 +50,24 @@ class System {
static bool command_pin(const char * value, const int8_t id);
static bool command_send(const char * value, const int8_t id);
static bool command_publish(const char * value, const int8_t id);
static bool command_fetch(const char * value, const int8_t id);
static bool command_info(const char * value, const int8_t id, JsonObject & json);
static bool command_report(const char * value, const int8_t id, JsonObject & json);
#if defined(EMSESP_TEST)
static bool command_test(const char * value, const int8_t id);
#endif
static uint8_t free_mem();
static void upload_status(bool in_progress);
static bool upload_status();
static void show_mem(const char * note);
static void set_led();
static void init();
static void syslog_init();
bool check_upgrade();
void syslog_init();
void send_heartbeat();
static std::string hostname() {
@@ -108,12 +114,13 @@ class System {
static std::string hostname_;
// settings
static bool hide_led_;
uint8_t syslog_level_;
uint32_t syslog_mark_interval_;
String syslog_host_;
static uint8_t led_gpio_;
static bool analog_enabled_;
static bool hide_led_;
static bool syslog_enabled_;
static int8_t syslog_level_;
static uint32_t syslog_mark_interval_;
static String syslog_host_;
static uint8_t led_gpio_;
static bool analog_enabled_;
};
} // namespace emsesp

View File

@@ -429,6 +429,11 @@ void TxService::add(const uint8_t operation,
// format is EMS 1.0 (src, dest, type_id, offset, data)
// length is the length of the whole telegram data, excluding the CRC
void TxService::add(uint8_t operation, const uint8_t * data, const uint8_t length, const bool front) {
// check length
if (length < 5) {
return;
}
// build header. src, dest and offset have fixed positions
uint8_t src = data[0];
uint8_t dest = data[1];

View File

@@ -384,7 +384,7 @@ class TxService : public EMSbus {
void send_telegram(const QueuedTxTelegram & tx_telegram);
void send_telegram(const uint8_t * data, const uint8_t length);
}; // namespace emsesp
};
} // namespace emsesp

View File

@@ -17,24 +17,145 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#if defined(EMSESP_DEBUG)
#if defined(EMSESP_TEST)
#include "test.h"
// create some fake test data
namespace emsesp {
// create some fake test data
// used with the 'test' command, under su/admin
void Test::run_test(uuid::console::Shell & shell, const std::string & command) {
if (command == "default") {
run_test(shell, "mixer"); // add the default test case here
// no shell
void Test::run_test(const char * command, int8_t id) {
if ((command == nullptr) || (strlen(command) == 0)) {
return;
}
if (command.empty()) {
run_test(shell, "default");
if (strcmp(command, "general") == 0) {
EMSESP::logger().info(F("Testing general..."));
add_device(0x08, 123); // Nefit Trendline
add_device(0x18, 157); // Bosch CR100
// add some data
// Boiler -> Me, UBAMonitorFast(0x18), telegram: 08 00 18 00 00 02 5A 73 3D 0A 10 65 40 02 1A 80 00 01 E1 01 76 0E 3D 48 00 C9 44 02 00 (#data=25)
uart_telegram({0x08, 0x00, 0x18, 0x00, 0x00, 0x02, 0x5A, 0x73, 0x3D, 0x0A, 0x10, 0x65, 0x40, 0x02, 0x1A,
0x80, 0x00, 0x01, 0xE1, 0x01, 0x76, 0x0E, 0x3D, 0x48, 0x00, 0xC9, 0x44, 0x02, 0x00});
// Boiler -> Thermostat, UBAParameterWW(0x33), telegram: 08 97 33 00 23 24 (#data=2)
uart_telegram({0x08, 0x98, 0x33, 0x00, 0x23, 0x24});
// Boiler -> Me, UBAParameterWW(0x33), telegram: 08 0B 33 00 08 FF 34 FB 00 28 00 00 46 00 FF FF 00 (#data=13)
uart_telegram({0x08, 0x0B, 0x33, 0x00, 0x08, 0xFF, 0x34, 0xFB, 0x00, 0x28, 0x00, 0x00, 0x46, 0x00, 0xFF, 0xFF, 0x00});
// Thermostat RCPLUSStatusMessage_HC1(0x01A5)
uart_telegram({0x98, 0x00, 0xFF, 0x00, 0x01, 0xA5, 0x00, 0xCF, 0x21, 0x2E, 0x00, 0x00, 0x2E, 0x24,
0x03, 0x25, 0x03, 0x03, 0x01, 0x03, 0x25, 0x00, 0xC8, 0x00, 0x00, 0x11, 0x01, 0x03});
return;
}
if (strcmp(command, "gateway") == 0) {
EMSESP::logger().info(F("Testing gateway..."));
// add 0x48 KM200, via a version command
rx_telegram({0x48, 0x0B, 0x02, 0x00, 0xBD, 0x04, 0x06, 00, 00, 00, 00, 00, 00, 00});
// Boiler(0x08) -> All(0x00), UBADevices(0x07), data: 09 01 00 00 00 00 00 00 01 00 00 00 00
// check: make sure 0x48 is not detected again !
rx_telegram({0x08, 0x00, 0x07, 0x00, 0x09, 01, 00, 00, 00, 00, 00, 00, 01, 00, 00, 00, 00});
// add thermostat - Thermostat: RC300/RC310/Moduline 3000/CW400/Sense II (DeviceID:0x10, ProductID:158, Version:03.03) ** master device **
add_device(0x10, 158); // Nefit Trendline
// simulate incoming telegram
// Thermostat(0x10) -> 48(0x48), ?(0x26B), data: 6B 08 4F 00 00 00 02 00 00 00 02 00 03 00 03 00 03
rx_telegram({0x10, 0x48, 0xFF, 00, 01, 0x6B, 00, 0x6B, 0x08, 0x4F, 00, 00, 00, 02, 00, 00, 00, 02, 00, 03, 00, 03, 00, 03});
return;
}
if (strcmp(command, "boiler") == 0) {
// EMSESP::logger().info(F("Testing boiler..."));
add_device(0x08, 123); // Nefit Trendline
// UBAuptime
uart_telegram({0x08, 0x0B, 0x14, 00, 0x3C, 0x1F, 0xAC, 0x70});
return;
}
if (strcmp(command, "thermostat") == 0) {
EMSESP::logger().info(F("Testing thermostat..."));
add_device(0x10, 192); // FW120
// HC1
uart_telegram({0x90, 0x00, 0xFF, 0x00, 0x00, 0x6F, 0x00, 0xCF, 0x21, 0x2E, 0x20, 0x00, 0x2E, 0x24,
0x03, 0x25, 0x03, 0x03, 0x01, 0x03, 0x25, 0x00, 0xC8, 0x00, 0x00, 0x11, 0x01, 0x03});
// HC2
uart_telegram({0x90, 0x00, 0xFF, 0x00, 0x00, 0x70, 0x00, 0xCF, 0x22, 0x2F, 0x10, 0x00, 0x2E, 0x24,
0x03, 0x25, 0x03, 0x03, 0x01, 0x03, 0x25, 0x00, 0xC8, 0x00, 0x00, 0x11, 0x01, 0x03});
// HC3
uart_telegram({0x90, 0x00, 0xFF, 0x00, 0x00, 0x71, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00});
return;
}
if (strcmp(command, "solar") == 0) {
EMSESP::logger().info(F("Testing solar..."));
EMSESP::rxservice_.ems_mask(EMSbus::EMS_MASK_BUDERUS);
add_device(0x30, 163); // SM100
// SM100Monitor - type 0x0362 EMS+ - for SM100 and SM200
// B0 0B FF 00 02 62 00 44 02 7A 80 00 80 00 80 00 80 00 80 00 80 00 00 7C 80 00 80 00 80 00 80
rx_telegram({0xB0, 0x0B, 0xFF, 00, 0x02, 0x62, 00, 0x44, 0x02, 0x7A, 0x80, 00, 0x80, 0x00, 0x80, 00,
0x80, 00, 0x80, 00, 0x80, 00, 00, 0x7C, 0x80, 00, 0x80, 00, 0x80, 00, 0x80});
rx_telegram({0xB0, 0x0B, 0xFF, 0x00, 0x02, 0x62, 0x01, 0x44, 0x03, 0x30, 0x80, 00, 0x80, 00, 0x80, 00,
0x80, 00, 0x80, 00, 0x80, 00, 0x80, 00, 0x80, 00, 0x80, 00, 0x80, 00, 0x80, 0x33});
rx_telegram({0xB0, 00, 0xFF, 0x18, 02, 0x62, 0x80, 00, 0xB8});
EMSESP::send_raw_telegram("B0 00 FF 18 02 62 80 00 B8");
return;
}
if (strcmp(command, "heatpump") == 0) {
EMSESP::logger().info(F("Testing heatpump..."));
EMSESP::rxservice_.ems_mask(EMSbus::EMS_MASK_BUDERUS);
add_device(0x38, 200); // Enviline module
add_device(0x10, 192); // FW120 thermostat
uart_telegram({0x90, 0x00, 0xFF, 0x00, 0x00, 0x6F, 0x00, 0xCF, 0x21, 0x2E, 0x20, 0x00, 0x2E, 0x24,
0x03, 0x25, 0x03, 0x03, 0x01, 0x03, 0x25, 0x00, 0xC8, 0x00, 0x00, 0x11, 0x01, 0x03}); // HC1
uart_telegram("38 0B FF 00 03 7B 0C 34 00 74");
return;
}
}
// used with the 'test' command, under su/admin
void Test::run_test_shell(uuid::console::Shell & shell, const std::string & command) {
// switch to su
shell.add_flags(CommandFlags::ADMIN);
if ((command == "default") || (command == "general") || (command.empty())) {
shell.printfln(F("Testing adding a general boiler & thermostat..."));
run_test("general");
shell.invoke_command("show devices");
shell.invoke_command("show");
shell.invoke_command("call system publish");
shell.invoke_command("show mqtt");
}
if (command == "render") {
shell.printfln(F("Testing render..."));
uint8_t test1 = 12;
int8_t test2 = -12;
uint16_t test3 = 456;
@@ -130,47 +251,34 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & command) {
}
if (command == "devices") {
EMSESP::rxservice_.ems_mask(EMSbus::EMS_MASK_BUDERUS); // this is important otherwise nothing will be picked up!
shell.printfln(F("Testing devices..."));
//emsdevices.push_back(EMSFactory::add(EMSdevice::DeviceType::BOILER, EMSdevice::EMS_DEVICE_ID_BOILER, 0, "", "My Boiler", 0, 0));
EMSESP::rxservice_.ems_mask(EMSbus::EMS_MASK_BUDERUS); // this is important otherwise nothing will be picked up!
// A fake response - UBADevices(0x07)
rx_telegram({0x08, 0x00, 0x07, 0x00, 0x0B, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00});
}
if (command == "boiler") {
// question: do we need to set the mask?
std::string version("1.2.3");
EMSESP::add_device(0x08, 123, version, EMSdevice::Brand::BUDERUS); // Nefit Trendline
// UBAuptime
uart_telegram({0x08, 0x0B, 0x14, 00, 0x3C, 0x1F, 0xAC, 0x70});
shell.invoke_command("show");
shell.invoke_command("call boiler info");
}
// check for boiler and controller on same product_id
if (command == "double") {
// question: do we need to set the mask?
std::string version("1.2.3");
EMSESP::add_device(0x08, 206, version, EMSdevice::Brand::BUDERUS); // Nefit Excellent HR30
EMSESP::add_device(0x09, 206, version, EMSdevice::Brand::BUDERUS); // Nefit Excellent HR30 Controller
shell.printfln(F("Testing double..."));
add_device(0x08, 206); // Nefit Excellent HR30
add_device(0x09, 206); // Nefit Excellent HR30 Controller
// UBAuptime
uart_telegram({0x08, 0x0B, 0x14, 00, 0x3C, 0x1F, 0xAC, 0x70});
}
// unknown device -
// unknown device
if (command == "unknown") {
// question: do we need to set the mask?
std::string version("1.2.3");
shell.printfln(F("Testing unknown..."));
// add boiler
EMSESP::add_device(0x08, 84, version, EMSdevice::Brand::BUDERUS);
add_device(0x08, 84);
// add Controller - BC10 GB142 - but using the same device_id to see what happens
EMSESP::add_device(0x09, 84, version, EMSdevice::Brand::BUDERUS);
// add Controller - BC10 GB142 - but using the same product_id to see what happens
add_device(0x09, 84);
// simulate getting version information back from an unknown device
// note there is no brand (byte 9)
@@ -181,33 +289,22 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & command) {
}
if (command == "unknown2") {
shell.printfln(F("Testing unknown2..."));
// simulate getting version information back from an unknown device
rx_telegram({0x09, 0x0B, 0x02, 0x00, 0x5A, 0x01, 0x02}); // product id is 90 which doesn't exist
}
if (command == "gateway") {
// add 0x48 KM200, via a version command
rx_telegram({0x48, 0x0B, 0x02, 0x00, 0xBD, 0x04, 0x06, 00, 00, 00, 00, 00, 00, 00});
// Boiler(0x08) -> All(0x00), UBADevices(0x07), data: 09 01 00 00 00 00 00 00 01 00 00 00 00
// check: make sure 0x48 is not detected again !
rx_telegram({0x08, 0x00, 0x07, 0x00, 0x09, 01, 00, 00, 00, 00, 00, 00, 01, 00, 00, 00, 00});
// add thermostat - Thermostat: RC300/RC310/Moduline 3000/CW400/Sense II (DeviceID:0x10, ProductID:158, Version:03.03) ** master device **
std::string version("01.03");
EMSESP::add_device(0x10, 158, version, EMSdevice::Brand::BUDERUS);
// simulate incoming telegram
// Thermostat(0x10) -> 48(0x48), ?(0x26B), data: 6B 08 4F 00 00 00 02 00 00 00 02 00 03 00 03 00 03
rx_telegram({0x10, 0x48, 0xFF, 00, 01, 0x6B, 00, 0x6B, 0x08, 0x4F, 00, 00, 00, 02, 00, 00, 00, 02, 00, 03, 00, 03, 00, 03});
shell.printfln(F("Testing Gateway..."));
run_test("gateway");
}
if (command == "web") {
shell.printfln(F("Testing Web..."));
std::string version("1.2.3");
EMSESP::add_device(0x08, 123, version, EMSdevice::Brand::BUDERUS); // Nefit Trendline
EMSESP::add_device(0x18, 157, version, EMSdevice::Brand::BOSCH); // Bosch CR100 - https://github.com/proddy/EMS-ESP/issues/355
add_device(0x08, 123); // Nefit Trendline
add_device(0x18, 157); // Bosch CR100
// add some data
// Boiler -> Me, UBAMonitorFast(0x18), telegram: 08 00 18 00 00 02 5A 73 3D 0A 10 65 40 02 1A 80 00 01 E1 01 76 0E 3D 48 00 C9 44 02 00 (#data=25)
@@ -228,57 +325,25 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & command) {
uart_telegram({0x98, 0x00, 0x06, 0x00, 0x00, 0x03, 0x04, 0x0C, 0x02, 0x33, 0x06, 00, 00, 00, 00, 00, 00});
shell.invoke_command("show");
StaticJsonDocument<2000> doc;
JsonObject root = doc.to<JsonObject>();
StaticJsonDocument<500> doc;
JsonObject root = doc.to<JsonObject>();
EMSESP::device_info_web(2, root); // show thermostat. use 1 for boiler
serializeJsonPretty(doc, shell);
shell.println();
}
if (command == "general") {
shell.printfln(F("Testing adding a boiler & thermostat..."));
std::string version("1.2.3");
EMSESP::add_device(0x08, 123, version, EMSdevice::Brand::BUDERUS); // Nefit Trendline
EMSESP::add_device(0x18, 157, version, EMSdevice::Brand::BOSCH); // Bosch CR100 - https://github.com/proddy/EMS-ESP/issues/355
// add some data
// Boiler -> Me, UBAMonitorFast(0x18), telegram: 08 00 18 00 00 02 5A 73 3D 0A 10 65 40 02 1A 80 00 01 E1 01 76 0E 3D 48 00 C9 44 02 00 (#data=25)
uart_telegram({0x08, 0x00, 0x18, 0x00, 0x00, 0x02, 0x5A, 0x73, 0x3D, 0x0A, 0x10, 0x65, 0x40, 0x02, 0x1A,
0x80, 0x00, 0x01, 0xE1, 0x01, 0x76, 0x0E, 0x3D, 0x48, 0x00, 0xC9, 0x44, 0x02, 0x00});
// Boiler -> Thermostat, UBAParameterWW(0x33), telegram: 08 97 33 00 23 24 (#data=2)
uart_telegram({0x08, 0x98, 0x33, 0x00, 0x23, 0x24});
// Boiler -> Me, UBAParameterWW(0x33), telegram: 08 0B 33 00 08 FF 34 FB 00 28 00 00 46 00 FF FF 00 (#data=13)
uart_telegram({0x08, 0x0B, 0x33, 0x00, 0x08, 0xFF, 0x34, 0xFB, 0x00, 0x28, 0x00, 0x00, 0x46, 0x00, 0xFF, 0xFF, 0x00});
// Thermostat RCPLUSStatusMessage_HC1(0x01A5)
uart_telegram({0x98, 0x00, 0xFF, 0x00, 0x01, 0xA5, 0x00, 0xCF, 0x21, 0x2E, 0x00, 0x00, 0x2E, 0x24,
0x03, 0x25, 0x03, 0x03, 0x01, 0x03, 0x25, 0x00, 0xC8, 0x00, 0x00, 0x11, 0x01, 0x03});
if (command == "boiler") {
shell.printfln(F("Testing boiler..."));
run_test("boiler");
shell.invoke_command("show");
shell.invoke_command("publish");
shell.invoke_command("show mqtt");
shell.invoke_command("call boiler info");
}
if (command == "fr120") {
shell.printfln(F("Testing adding a thermostat FR120..."));
// add_device(0x10, 165, version, EMSdevice::Brand::BUDERUS);
// add_device(0x17, 125, version, EMSdevice::Brand::BUDERUS); // test unknown class test
// add_device(0x17, 93, version, EMSdevice::Brand::BUDERUS);
// add_device(0x17, 254, version, EMSdevice::Brand::BUDERUS); // test unknown product_id
// EMSESP::add_device(0x18, 157, version, EMSdevice::Brand::BOSCH); // Bosch CR100 - https://github.com/proddy/EMS-ESP/issues/355
std::string version("1.2.3");
// add a boiler
// EMSESP::add_device(0x08, 123, version, EMSdevice::Brand::BUDERUS); // Nefit Trendline
// add a thermostat
EMSESP::add_device(0x10, 191, version, EMSdevice::Brand::JUNKERS); // FR120
add_device(0x10, 191); // FR120 thermostat
// HC1
uart_telegram({0x90, 0x00, 0xFF, 0x00, 0x00, 0x6F, 0x00, 0xCF, 0x21, 0x2E, 0x20, 0x00, 0x2E, 0x24,
@@ -290,35 +355,8 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & command) {
if (command == "thermostat") {
shell.printfln(F("Testing adding a thermostat FW120..."));
// add_device(0x10, 165, version, EMSdevice::Brand::BUDERUS);
// add_device(0x17, 125, version, EMSdevice::Brand::BUDERUS); // test unknown class test
// add_device(0x17, 93, version, EMSdevice::Brand::BUDERUS);
// add_device(0x17, 254, version, EMSdevice::Brand::BUDERUS); // test unknown product_id
// EMSESP::add_device(0x18, 157, version, EMSdevice::Brand::BOSCH); // Bosch CR100 - https://github.com/proddy/EMS-ESP/issues/355
std::string version("1.2.3");
// add a boiler
// EMSESP::add_device(0x08, 123, version, EMSdevice::Brand::BUDERUS); // Nefit Trendline
// add a thermostat
EMSESP::add_device(0x10, 192, version, EMSdevice::Brand::JUNKERS); // FW120
// HC1
uart_telegram({0x90, 0x00, 0xFF, 0x00, 0x00, 0x6F, 0x00, 0xCF, 0x21, 0x2E, 0x20, 0x00, 0x2E, 0x24,
0x03, 0x25, 0x03, 0x03, 0x01, 0x03, 0x25, 0x00, 0xC8, 0x00, 0x00, 0x11, 0x01, 0x03});
// HC2
uart_telegram({0x90, 0x00, 0xFF, 0x00, 0x00, 0x70, 0x00, 0xCF, 0x22, 0x2F, 0x10, 0x00, 0x2E, 0x24,
0x03, 0x25, 0x03, 0x03, 0x01, 0x03, 0x25, 0x00, 0xC8, 0x00, 0x00, 0x11, 0x01, 0x03});
// HC3
uart_telegram({0x90, 0x00, 0xFF, 0x00, 0x00, 0x71, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00});
run_test("thermostat");
shell.invoke_command("show");
EMSESP::mqtt_.incoming("ems-esp/thermostat_hc1", "heat");
EMSESP::mqtt_.incoming("ems-esp/thermostat_hc2", "28.8");
EMSESP::mqtt_.incoming("ems-esp/thermostat", "{\"cmd\":\"temp\",\"id\":2,\"data\":22}");
@@ -327,13 +365,8 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & command) {
if (command == "tc100") {
shell.printfln(F("Testing adding a TC100 thermostat to the EMS bus..."));
std::string version("02.21");
// add a boiler
// EMSESP::add_device(0x08, 123, version, EMSdevice::Brand::BUDERUS); // Nefit Trendline
// add a thermostat
EMSESP::add_device(0x18, 202, version, EMSdevice::Brand::BOSCH); // Bosch TC100 - https://github.com/proddy/EMS-ESP/issues/474
add_device(0x18, 202); // Bosch TC100 - https://github.com/proddy/EMS-ESP/issues/474
// 0x0A
uart_telegram({0x98, 0x0B, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -342,51 +375,19 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & command) {
if (command == "solar") {
shell.printfln(F("Testing Solar"));
EMSESP::rxservice_.ems_mask(EMSbus::EMS_MASK_BUDERUS);
std::string version("1.2.3");
EMSESP::add_device(0x30, 163, version, EMSdevice::Brand::BUDERUS); // SM100
// SM100Monitor - type 0x0362 EMS+ - for SM100 and SM200
// B0 0B FF 00 02 62 00 44 02 7A 80 00 80 00 80 00 80 00 80 00 80 00 00 7C 80 00 80 00 80 00 80
rx_telegram({0xB0, 0x0B, 0xFF, 00, 0x02, 0x62, 00, 0x44, 0x02, 0x7A, 0x80, 00, 0x80, 0x00, 0x80, 00,
0x80, 00, 0x80, 00, 0x80, 00, 00, 0x7C, 0x80, 00, 0x80, 00, 0x80, 00, 0x80});
rx_telegram({0xB0, 0x0B, 0xFF, 0x00, 0x02, 0x62, 0x01, 0x44, 0x03, 0x30, 0x80, 00, 0x80, 00, 0x80, 00,
0x80, 00, 0x80, 00, 0x80, 00, 0x80, 00, 0x80, 00, 0x80, 00, 0x80, 00, 0x80, 0x33});
rx_telegram({0xB0, 00, 0xFF, 0x18, 02, 0x62, 0x80, 00, 0xB8});
EMSESP::send_raw_telegram("B0 00 FF 18 02 62 80 00 B8");
run_test("solar");
uart_telegram("30 00 FF 0A 02 6A 04"); // SM100 pump on 1
uart_telegram("30 00 FF 00 02 64 00 00 00 04 00 00 FF 00 00 1E 0B 09 64 00 00 00 00"); // SM100 modulation
EMSESP::show_device_values(shell);
uart_telegram("30 00 FF 0A 02 6A 03"); // SM100 pump off 0
EMSESP::show_device_values(shell);
}
if (command == "heatpump") {
shell.printfln(F("Testing Heat Pump"));
EMSESP::rxservice_.ems_mask(EMSbus::EMS_MASK_BUDERUS);
std::string version("1.2.3");
// add heatpump
EMSESP::add_device(0x38, 200, version, EMSdevice::Brand::BUDERUS); // Enviline module
// add a thermostat
EMSESP::add_device(0x10, 192, version, EMSdevice::Brand::JUNKERS); // FW120
uart_telegram({0x90, 0x00, 0xFF, 0x00, 0x00, 0x6F, 0x00, 0xCF, 0x21, 0x2E, 0x20, 0x00, 0x2E, 0x24,
0x03, 0x25, 0x03, 0x03, 0x01, 0x03, 0x25, 0x00, 0xC8, 0x00, 0x00, 0x11, 0x01, 0x03}); // HC1
uart_telegram("38 0B FF 00 03 7B 0C 34 00 74");
run_test("heatpump");
shell.invoke_command("call");
shell.invoke_command("call heatpump info");
EMSESP::show_device_values(shell);
}
if (command == "solar200") {
@@ -394,32 +395,26 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & command) {
EMSESP::rxservice_.ems_mask(EMSbus::EMS_MASK_BUDERUS);
std::string version("1.2.3");
EMSESP::add_device(0x30, 164, version, EMSdevice::Brand::BUDERUS); // SM200
add_device(0x30, 164); // SM200
// SM100Monitor - type 0x0362 EMS+ - for SM100 and SM200
// B0 0B FF 00 02 62 00 44 02 7A 80 00 80 00 80 00 80 00 80 00 80 00 00 7C 80 00 80 00 80 00 80
rx_telegram({0xB0, 0x0B, 0xFF, 00, 0x02, 0x62, 00, 0x44, 0x02, 0x7A, 0x80, 00, 0x80, 0x00, 0x80, 00,
0x80, 00, 0x80, 00, 0x80, 00, 00, 0x7C, 0x80, 00, 0x80, 00, 0x80, 00, 0x80});
EMSESP::show_device_values(shell);
rx_telegram({0xB0, 0x0B, 0xFF, 0x00, 0x02, 0x62, 0x01, 0x44, 0x03, 0x30, 0x80, 00, 0x80, 00, 0x80, 00,
0x80, 00, 0x80, 00, 0x80, 00, 0x80, 00, 0x80, 00, 0x80, 00, 0x80, 00, 0x80, 0x33});
EMSESP::show_device_values(shell);
rx_telegram({0xB0, 00, 0xFF, 0x18, 02, 0x62, 0x80, 00, 0xB8});
EMSESP::show_device_values(shell);
EMSESP::send_raw_telegram("B0 00 FF 18 02 62 80 00 B8");
uart_telegram("30 00 FF 0A 02 6A 04"); // SM100 pump on 1
uart_telegram("30 00 FF 00 02 64 00 00 00 04 00 00 FF 00 00 1E 0B 09 64 00 00 00 00"); // SM100 modulation
EMSESP::show_device_values(shell);
uart_telegram("30 00 FF 0A 02 6A 03"); // SM100 pump off 0
EMSESP::show_device_values(shell);
shell.invoke_command("show");
}
if (command == "km") {
@@ -429,13 +424,11 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & command) {
EMSESP::rxservice_.ems_mask(EMSbus::EMS_MASK_BUDERUS);
std::string version("1.2.3");
EMSESP::add_device(0x10, 158, version, EMSdevice::Brand::BUDERUS); // RC300
EMSESP::add_device(0x48, 189, version, EMSdevice::Brand::BUDERUS); // KM200
add_device(0x10, 158); // RC300
add_device(0x48, 189); // KM200
// see https://github.com/proddy/EMS-ESP/issues/390
/*
uart_telegram_withCRC("90 48 FF 04 01 A6 5C");
uart_telegram_withCRC("90 48 FF 00 01 A6 4C");
uart_telegram_withCRC("90 48 FF 08 01 A7 6D");
@@ -473,7 +466,6 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & command) {
uart_telegram_withCRC("C8 90 FF 00 02 01 A6 D0");
// uart_telegram_withCRC("10 00 FF 00 01 A5 00 D7 21 00 00 00 00 30 01 84 01 01 03 01 84 01 F1 00 00 11 01 00 08 63 00");
*/
uart_telegram_withCRC("C8 90 F7 02 01 FF 01 A6 BA");
@@ -494,8 +486,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & command) {
EMSESP::rxservice_.ems_mask(EMSbus::EMS_MASK_HT3); // switch to junkers
std::string version("1.2.3");
EMSESP::add_device(0x18, 157, version, EMSdevice::Brand::BOSCH); // Bosch CR100 - https://github.com/proddy/EMS-ESP/issues/355
add_device(0x18, 157); // Bosch CR100 - https://github.com/proddy/EMS-ESP/issues/355
// RCPLUSStatusMessage_HC1(0x01A5)
// 98 00 FF 00 01 A5 00 CF 21 2E 00 00 2E 24 03 25 03 03 01 03 25 00 C8 00 00 11 01 03 (no CRC)
@@ -582,12 +573,6 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & command) {
uart_telegram({0x88, 00, 0x2A, 00, 00, 00, 00, 00, 00, 00, 00, 00, 0xD2, 00, 00, 0x80, 00, 00, 01, 0x9D, 0x80, 0x00, 0x02, 0x79, 00});
}
if (command == "send") {
shell.printfln(F("Sending to Tx..."));
EMSESP::show_ems(shell);
EMSESP::txservice_.send(); // send it to UART
}
if (command == "tx") {
shell.printfln(F("Testing Tx..."));
@@ -626,8 +611,6 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & command) {
EMSESP::txservice_.send(); // send it to UART
}
shell.loop_all();
EMSESP::txservice_.flush_tx_queue();
}
@@ -665,8 +648,8 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & command) {
shell.printfln(F("Testing Commands..."));
// add a thermostat with 3 HCs
std::string version("1.2.3");
EMSESP::add_device(0x10, 192, version, EMSdevice::Brand::JUNKERS); // FW120
add_device(0x10, 192); // FW120
uart_telegram({0x90, 0x00, 0xFF, 0x00, 0x00, 0x6F, 0x00, 0xCF, 0x21, 0x2E, 0x20, 0x00, 0x2E, 0x24,
0x03, 0x25, 0x03, 0x03, 0x01, 0x03, 0x25, 0x00, 0xC8, 0x00, 0x00, 0x11, 0x01, 0x03}); // HC1
uart_telegram({0x90, 0x00, 0xFF, 0x00, 0x00, 0x70, 0x00, 0xCF, 0x22, 0x2F, 0x10, 0x00, 0x2E, 0x24,
@@ -678,9 +661,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & command) {
shell.invoke_command("call");
shell.invoke_command("call system info");
char system_topic[Mqtt::MQTT_TOPIC_MAX_SIZE];
strcpy(system_topic, "ems-esp/system");
EMSESP::mqtt_.incoming(system_topic, "{\"cmd\":\"info\"}"); // this should fail
EMSESP::mqtt_.incoming("ems-esp/system", "{\"cmd\":\"info\"}"); // this should fail
shell.invoke_command("call thermostat wwmode"); // should do nothing
shell.invoke_command("call thermostat mode auto 2"); // should error, no hc2
@@ -689,8 +670,6 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & command) {
if (command == "pin") {
shell.printfln(F("Testing pin..."));
shell.invoke_command("su");
shell.invoke_command("call system pin");
shell.invoke_command("call system pin 1 true");
}
@@ -707,12 +686,10 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & command) {
});
// add a boiler
// question: do we need to set the mask?
std::string version("1.2.3");
EMSESP::add_device(0x08, 123, version, EMSdevice::Brand::BUDERUS); // Nefit Trendline
add_device(0x08, 123); // Nefit Trendline
// add a thermostat
EMSESP::add_device(0x18, 157, version, EMSdevice::Brand::BOSCH); // Bosch CR100 - https://github.com/proddy/EMS-ESP/issues/355
add_device(0x18, 157); // Bosch CR100 - https://github.com/proddy/EMS-ESP/issues/355
// RCPLUSStatusMessage_HC1(0x01A5) - HC1
uart_telegram({0x98, 0x00, 0xFF, 0x00, 0x01, 0xA5, 0x00, 0xCF, 0x21, 0x2E, 0x00, 0x00, 0x2E, 0x24,
@@ -787,23 +764,31 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & command) {
}
if (command == "rx2") {
shell.printfln(F("Testing rx2..."));
uart_telegram({0x1B, 0x5B, 0xFD, 0x2D, 0x9E, 0x3A, 0xB6, 0xE5, 0x02, 0x20, 0x33, 0x30, 0x32, 0x3A, 0x20, 0x5B,
0x73, 0xFF, 0xFF, 0xCB, 0xDF, 0xB7, 0xA7, 0xB5, 0x67, 0x77, 0x77, 0xE4, 0xFF, 0xFD, 0x77, 0xFF});
}
// https://github.com/proddy/EMS-ESP/issues/380#issuecomment-633663007
if (command == "rx3") {
shell.printfln(F("Testing rx3..."));
uart_telegram({0x21, 0x0B, 0xFF, 0x00});
}
// testing the UART tx command, without a queue
if (command == "tx2") {
shell.printfln(F("Testing tx2..."));
uint8_t t[] = {0x0B, 0x88, 0x18, 0x00, 0x20, 0xD4}; // including CRC
EMSuart::transmit(t, sizeof(t));
}
// send read request with offset
if (command == "offset") {
shell.printfln(F("Testing offset..."));
// send_read_request(0x18, 0x08);
EMSESP::txservice_.read_request(0x18, 0x08, 27); // no offset
}
@@ -820,14 +805,13 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & command) {
});
EMSESP::rxservice_.ems_mask(EMSbus::EMS_MASK_BUDERUS);
std::string version("1.2.3");
// add controller
EMSESP::add_device(0x09, 114, version, EMSdevice::Brand::BUDERUS);
add_device(0x09, 114);
EMSESP::add_device(0x28, 160, version, EMSdevice::Brand::BUDERUS); // MM100, WWC
EMSESP::add_device(0x29, 161, version, EMSdevice::Brand::BUDERUS); // MM200, WWC
EMSESP::add_device(0x20, 160, version, EMSdevice::Brand::BOSCH); // MM100
add_device(0x28, 160); // MM100, WWC
add_device(0x29, 161); // MM200, WWC
add_device(0x20, 160); // MM100
// WWC1 on 0x29
uart_telegram({0xA9, 0x00, 0xFF, 0x00, 0x02, 0x32, 0x02, 0x6C, 0x00, 0x3C, 0x00, 0x3C, 0x3C, 0x46, 0x02, 0x03, 0x03, 0x00, 0x3C});
@@ -838,16 +822,11 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & command) {
// check for error "No telegram type handler found for ID 0x255 (src 0x20)"
uart_telegram({0xA0, 0x00, 0xFF, 0x00, 0x01, 0x55, 0x00, 0x1A});
shell.invoke_command("show");
shell.invoke_command("call");
shell.invoke_command("call mixer info");
shell.invoke_command("publish");
shell.invoke_command("show mqtt");
shell.invoke_command("call mixer");
// shell.invoke_command("show");
// shell.invoke_command("call mixer info");
// shell.invoke_command("call system publish");
// shell.invoke_command("show mqtt");
}
// finally dump to console
EMSESP::loop();
}
// simulates a telegram in the Rx queue, but without the CRC which is added automatically
@@ -861,6 +840,7 @@ void Test::rx_telegram(const std::vector<uint8_t> & rx_data) {
}
data[i] = EMSESP::rxservice_.calculate_crc(data, i);
EMSESP::rxservice_.add(data, len + 1);
EMSESP::loop();
}
// simulates a telegram straight from UART, but without the CRC which is added automatically
@@ -874,7 +854,7 @@ void Test::uart_telegram(const std::vector<uint8_t> & rx_data) {
}
data[i] = EMSESP::rxservice_.calculate_crc(data, i);
EMSESP::incoming_telegram(data, i + 1);
EMSESP::rxservice_.loop();
EMSESP::loop();
}
// takes raw string, assuming it contains the CRC. This is what is output from 'watch raw'
@@ -912,7 +892,7 @@ void Test::uart_telegram_withCRC(const char * rx_data) {
}
EMSESP::incoming_telegram(data, count + 1);
EMSESP::rxservice_.loop();
EMSESP::loop();
}
// takes raw string, adds CRC to end
@@ -952,14 +932,14 @@ void Test::uart_telegram(const char * rx_data) {
data[count + 1] = EMSESP::rxservice_.calculate_crc(data, count + 1); // add CRC
EMSESP::incoming_telegram(data, count + 2);
EMSESP::rxservice_.loop();
EMSESP::loop();
}
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
#pragma GCC diagnostic pop
// Sends version telegram. Version is hardcoded to 1.0
void Test::add_device(uint8_t device_id, uint8_t product_id) {
// Send version: 09 0B 02 00 PP V1 V2
uart_telegram({device_id, EMSESP_DEFAULT_EMS_BUS_ID, EMSdevice::EMS_TYPE_VERSION, 0, product_id, 1, 0});
}
} // namespace emsesp

View File

@@ -16,7 +16,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#if defined(EMSESP_DEBUG)
#if defined(EMSESP_TEST)
#ifndef EMSESP_TEST_H
#define EMSESP_TEST_H
@@ -40,12 +40,14 @@ namespace emsesp {
class Test {
public:
static void run_test(uuid::console::Shell & shell, const std::string & command); // only for testing
static void run_test_shell(uuid::console::Shell & shell, const std::string & command);
static void run_test(const char * command, int8_t id = 0);
static void dummy_mqtt_commands(const char * message);
static void rx_telegram(const std::vector<uint8_t> & data);
static void uart_telegram(const std::vector<uint8_t> & rx_data);
static void uart_telegram(const char * rx_data);
static void uart_telegram_withCRC(const char * rx_data);
static void add_device(uint8_t device_id, uint8_t product_id);
};
} // namespace emsesp

View File

@@ -1 +1 @@
#define EMSESP_APP_VERSION "2.1.1b1"
#define EMSESP_APP_VERSION "2.1.1b2"