diff --git a/CHANGELOG_LATEST.md b/CHANGELOG_LATEST.md index fbc73103e..e9c0e154f 100644 --- a/CHANGELOG_LATEST.md +++ b/CHANGELOG_LATEST.md @@ -35,6 +35,7 @@ - Buderus RC25 - "hc1 mode type" incorrect value [#273](https://github.com/emsesp/EMS-ESP32/issues/273) - Increased number of Mixers and Heating Circuits [#294](https://github.com/emsesp/EMS-ESP32/issues/294) - Check receive status before removing a telegram fetch [#268](https://github.com/emsesp/EMS-ESP32/issues/268), [#282](https://github.com/emsesp/EMS-ESP32/issues/282) +- Fix uploading firmware on OSX [#345](https://github.com/emsesp/EMS-ESP32/issues/345) ### Changed diff --git a/interface/package-lock.json b/interface/package-lock.json index 064da39f6..f944ce0ce 100644 --- a/interface/package-lock.json +++ b/interface/package-lock.json @@ -12,10 +12,10 @@ "@emotion/styled": "^11.6.0", "@msgpack/msgpack": "^2.7.1", "@mui/icons-material": "^5.3.1", - "@mui/material": "^5.3.1", + "@mui/material": "^5.4.0", "@types/lodash": "^4.14.178", - "@types/node": "^17.0.10", - "@types/react": "^17.0.38", + "@types/node": "^17.0.15", + "@types/react": "^17.0.39", "@types/react-dom": "^17.0.11", "@types/react-router-dom": "^5.3.3", "async-validator": "^4.0.7", @@ -28,7 +28,7 @@ "react": "^17.0.2", "react-app-rewired": "^2.1.11", "react-dom": "^17.0.2", - "react-dropzone": "^11.5.1", + "react-dropzone": "^12.0.0", "react-icons": "^4.3.1", "react-router-dom": "^6.2.1", "react-scripts": "5.0.0", @@ -2707,9 +2707,9 @@ } }, "node_modules/@mui/base": { - "version": "5.0.0-alpha.66", - "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-alpha.66.tgz", - "integrity": "sha512-LARfVx0HmGV5YwU2pdIqEApQwz/CtEnYtKkV856hlY0cgi5NQL2htzZ/9ujKz0j3LFUaMYiYuJ2AOwrNtGFGrw==", + "version": "5.0.0-alpha.67", + "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-alpha.67.tgz", + "integrity": "sha512-yK2++NivZUitAVpheMc5QVuwrVCphrTw85L6qjKcvnSpB8wmVYne58CY2vzMCNEuHkuHG2jccq9/JlRZFGAanw==", "dependencies": { "@babel/runtime": "^7.16.7", "@emotion/is-prop-valid": "^1.1.1", @@ -2763,13 +2763,13 @@ } }, "node_modules/@mui/material": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.3.1.tgz", - "integrity": "sha512-XWPsJ2jet2zfnKojth5d2IaHIJPpJnHq1ACCSlNf898BjYh1j50gRWsPpIHiptQ0oc0pdWmMcmrXbdANKR1ybw==", + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.4.0.tgz", + "integrity": "sha512-vfBIAMsRNWI/A4p/eP01MjqhSACwxRGYp/2Yi7WAU64PpI/TXR4b9SRl+XJMMJXVC7+abu4E3hTdF3oqwMCSYA==", "dependencies": { "@babel/runtime": "^7.16.7", - "@mui/base": "5.0.0-alpha.66", - "@mui/system": "^5.3.0", + "@mui/base": "5.0.0-alpha.67", + "@mui/system": "^5.4.0", "@mui/types": "^7.1.0", "@mui/utils": "^5.3.0", "@types/react-transition-group": "^4.4.4", @@ -2863,9 +2863,9 @@ } }, "node_modules/@mui/system": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.3.0.tgz", - "integrity": "sha512-mblz3EObrhhIMPwSEe2Az7MbMaXOFgrvItPOzZwcY5O9qERB7Rr8KQgbU8VouWLUqyV2i8BaFpLrkKPA6eX2Aw==", + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.4.0.tgz", + "integrity": "sha512-LX7g5gK5yCwiueSUVG73uVNc0yeHjsWUIFLrnPjP3m+J7O38RkPqyao5nZahhaSL1PGNbR9+zfkxljXthO9QqA==", "dependencies": { "@babel/runtime": "^7.16.7", "@mui/private-theming": "^5.3.0", @@ -3597,9 +3597,9 @@ "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==" }, "node_modules/@types/node": { - "version": "17.0.10", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.10.tgz", - "integrity": "sha512-S/3xB4KzyFxYGCppyDt68yzBU9ysL88lSdIah4D6cptdcltc4NCPCAMc0+PCpg/lLIyC7IPvj2Z52OJWeIUkog==" + "version": "17.0.15", + "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.15.tgz", + "integrity": "sha512-zWt4SDDv1S9WRBNxLFxFRHxdD9tvH8f5/kg5/IaLFdnSNXsDY4eL3Q3XXN+VxUnWIhyVFDwcsmAprvwXoM/ClA==" }, "node_modules/@types/parse-json": { "version": "4.0.0", @@ -3632,9 +3632,9 @@ "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==" }, "node_modules/@types/react": { - "version": "17.0.38", - "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.38.tgz", - "integrity": "sha512-SI92X1IA+FMnP3qM5m4QReluXzhcmovhZnLNm3pyeQlooi02qI7sLiepEYqT678uNiyc25XfCqxREFpy3W7YhQ==", + "version": "17.0.39", + "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.39.tgz", + "integrity": "sha512-UVavlfAxDd/AgAacMa60Azl7ygyQNRwC/DsHZmKgNvPmRR5p70AJ5Q9EAmL2NWOJmeV+vVUI4IAP7GZrN8h8Ug==", "dependencies": { "@types/prop-types": "*", "@types/scheduler": "*", @@ -7858,9 +7858,9 @@ } }, "node_modules/file-selector": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/file-selector/-/file-selector-0.2.4.tgz", - "integrity": "sha512-ZDsQNbrv6qRi1YTDOEWzf5J2KjZ9KMI1Q2SGeTkCJmNNW25Jg4TW4UMcmoqcg4WrAyKRcpBXdbWRxkfrOzVRbA==", + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/file-selector/-/file-selector-0.4.0.tgz", + "integrity": "sha512-iACCiXeMYOvZqlF1kTiYINzgepRBymz1wwjiuup9u9nayhb6g4fSwiyJ/6adli+EPwrWtpgQAh2PoS7HukEGEg==", "dependencies": { "tslib": "^2.0.3" }, @@ -14227,16 +14227,16 @@ } }, "node_modules/react-dropzone": { - "version": "11.5.1", - "resolved": "https://registry.npmjs.org/react-dropzone/-/react-dropzone-11.5.1.tgz", - "integrity": "sha512-eNhttdq4ZDe3eKbXAe54Opt+sbtqmNK5NWTHf/l5d+1TdZqShJ8gMjBrya00qx5zkI//TYxRhu1d9pemTgaWwg==", + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/react-dropzone/-/react-dropzone-12.0.0.tgz", + "integrity": "sha512-xGZr9YFTLqLkYDSx7CMRhjW4rIEqE9w2DhJB0CIEslPzUj3OQQcg7fK8uW85exB+UaJoPfk74lnKi0kv7q6rDw==", "dependencies": { - "attr-accept": "^2.2.1", - "file-selector": "^0.2.2", - "prop-types": "^15.7.2" + "attr-accept": "^2.2.2", + "file-selector": "^0.4.0", + "prop-types": "^15.8.1" }, "engines": { - "node": ">= 10" + "node": ">= 10.13" }, "peerDependencies": { "react": ">= 16.8" @@ -19333,9 +19333,9 @@ "integrity": "sha512-ApwiSL2c9ObewdOE/sqt788P1C5lomBOHyO8nUBCr4ofErBCnYQ003NtJ8lS9OQZc11ximkbmgAZJjB8y6cCdA==" }, "@mui/base": { - "version": "5.0.0-alpha.66", - "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-alpha.66.tgz", - "integrity": "sha512-LARfVx0HmGV5YwU2pdIqEApQwz/CtEnYtKkV856hlY0cgi5NQL2htzZ/9ujKz0j3LFUaMYiYuJ2AOwrNtGFGrw==", + "version": "5.0.0-alpha.67", + "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-alpha.67.tgz", + "integrity": "sha512-yK2++NivZUitAVpheMc5QVuwrVCphrTw85L6qjKcvnSpB8wmVYne58CY2vzMCNEuHkuHG2jccq9/JlRZFGAanw==", "requires": { "@babel/runtime": "^7.16.7", "@emotion/is-prop-valid": "^1.1.1", @@ -19355,13 +19355,13 @@ } }, "@mui/material": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.3.1.tgz", - "integrity": "sha512-XWPsJ2jet2zfnKojth5d2IaHIJPpJnHq1ACCSlNf898BjYh1j50gRWsPpIHiptQ0oc0pdWmMcmrXbdANKR1ybw==", + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.4.0.tgz", + "integrity": "sha512-vfBIAMsRNWI/A4p/eP01MjqhSACwxRGYp/2Yi7WAU64PpI/TXR4b9SRl+XJMMJXVC7+abu4E3hTdF3oqwMCSYA==", "requires": { "@babel/runtime": "^7.16.7", - "@mui/base": "5.0.0-alpha.66", - "@mui/system": "^5.3.0", + "@mui/base": "5.0.0-alpha.67", + "@mui/system": "^5.4.0", "@mui/types": "^7.1.0", "@mui/utils": "^5.3.0", "@types/react-transition-group": "^4.4.4", @@ -19394,9 +19394,9 @@ } }, "@mui/system": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.3.0.tgz", - "integrity": "sha512-mblz3EObrhhIMPwSEe2Az7MbMaXOFgrvItPOzZwcY5O9qERB7Rr8KQgbU8VouWLUqyV2i8BaFpLrkKPA6eX2Aw==", + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.4.0.tgz", + "integrity": "sha512-LX7g5gK5yCwiueSUVG73uVNc0yeHjsWUIFLrnPjP3m+J7O38RkPqyao5nZahhaSL1PGNbR9+zfkxljXthO9QqA==", "requires": { "@babel/runtime": "^7.16.7", "@mui/private-theming": "^5.3.0", @@ -19902,9 +19902,9 @@ "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==" }, "@types/node": { - "version": "17.0.10", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.10.tgz", - "integrity": "sha512-S/3xB4KzyFxYGCppyDt68yzBU9ysL88lSdIah4D6cptdcltc4NCPCAMc0+PCpg/lLIyC7IPvj2Z52OJWeIUkog==" + "version": "17.0.15", + "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.15.tgz", + "integrity": "sha512-zWt4SDDv1S9WRBNxLFxFRHxdD9tvH8f5/kg5/IaLFdnSNXsDY4eL3Q3XXN+VxUnWIhyVFDwcsmAprvwXoM/ClA==" }, "@types/parse-json": { "version": "4.0.0", @@ -19937,9 +19937,9 @@ "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==" }, "@types/react": { - "version": "17.0.38", - "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.38.tgz", - "integrity": "sha512-SI92X1IA+FMnP3qM5m4QReluXzhcmovhZnLNm3pyeQlooi02qI7sLiepEYqT678uNiyc25XfCqxREFpy3W7YhQ==", + "version": "17.0.39", + "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.39.tgz", + "integrity": "sha512-UVavlfAxDd/AgAacMa60Azl7ygyQNRwC/DsHZmKgNvPmRR5p70AJ5Q9EAmL2NWOJmeV+vVUI4IAP7GZrN8h8Ug==", "requires": { "@types/prop-types": "*", "@types/scheduler": "*", @@ -23086,9 +23086,9 @@ } }, "file-selector": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/file-selector/-/file-selector-0.2.4.tgz", - "integrity": "sha512-ZDsQNbrv6qRi1YTDOEWzf5J2KjZ9KMI1Q2SGeTkCJmNNW25Jg4TW4UMcmoqcg4WrAyKRcpBXdbWRxkfrOzVRbA==", + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/file-selector/-/file-selector-0.4.0.tgz", + "integrity": "sha512-iACCiXeMYOvZqlF1kTiYINzgepRBymz1wwjiuup9u9nayhb6g4fSwiyJ/6adli+EPwrWtpgQAh2PoS7HukEGEg==", "requires": { "tslib": "^2.0.3" } @@ -27560,13 +27560,13 @@ } }, "react-dropzone": { - "version": "11.5.1", - "resolved": "https://registry.npmjs.org/react-dropzone/-/react-dropzone-11.5.1.tgz", - "integrity": "sha512-eNhttdq4ZDe3eKbXAe54Opt+sbtqmNK5NWTHf/l5d+1TdZqShJ8gMjBrya00qx5zkI//TYxRhu1d9pemTgaWwg==", + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/react-dropzone/-/react-dropzone-12.0.0.tgz", + "integrity": "sha512-xGZr9YFTLqLkYDSx7CMRhjW4rIEqE9w2DhJB0CIEslPzUj3OQQcg7fK8uW85exB+UaJoPfk74lnKi0kv7q6rDw==", "requires": { - "attr-accept": "^2.2.1", - "file-selector": "^0.2.2", - "prop-types": "^15.7.2" + "attr-accept": "^2.2.2", + "file-selector": "^0.4.0", + "prop-types": "^15.8.1" } }, "react-error-overlay": { diff --git a/interface/package.json b/interface/package.json index db5a461f2..57b9388fc 100644 --- a/interface/package.json +++ b/interface/package.json @@ -8,10 +8,10 @@ "@emotion/styled": "^11.6.0", "@msgpack/msgpack": "^2.7.1", "@mui/icons-material": "^5.3.1", - "@mui/material": "^5.3.1", + "@mui/material": "^5.4.0", "@types/lodash": "^4.14.178", - "@types/node": "^17.0.10", - "@types/react": "^17.0.38", + "@types/node": "^17.0.15", + "@types/react": "^17.0.39", "@types/react-dom": "^17.0.11", "@types/react-router-dom": "^5.3.3", "async-validator": "^4.0.7", @@ -24,7 +24,7 @@ "react": "^17.0.2", "react-app-rewired": "^2.1.11", "react-dom": "^17.0.2", - "react-dropzone": "^11.5.1", + "react-dropzone": "^12.0.0", "react-icons": "^4.3.1", "react-router-dom": "^6.2.1", "react-scripts": "5.0.0", diff --git a/interface/src/framework/ntp/NTPStatusForm.tsx b/interface/src/framework/ntp/NTPStatusForm.tsx index 0f0659954..22ce282d4 100644 --- a/interface/src/framework/ntp/NTPStatusForm.tsx +++ b/interface/src/framework/ntp/NTPStatusForm.tsx @@ -23,13 +23,12 @@ import AccessTimeIcon from '@mui/icons-material/AccessTime'; import SwapVerticalCircleIcon from '@mui/icons-material/SwapVerticalCircle'; import UpdateIcon from '@mui/icons-material/Update'; import DnsIcon from '@mui/icons-material/Dns'; -import AvTimerIcon from '@mui/icons-material/AvTimer'; import CancelIcon from '@mui/icons-material/Cancel'; import * as NTPApi from '../../api/ntp'; import { NTPStatus, NTPSyncStatus } from '../../types'; import { ButtonRow, FormLoader, SectionContent } from '../../components'; -import { extractErrorMessage, formatDateTime, formatDuration, formatLocalDateTime, useRest } from '../../utils'; +import { extractErrorMessage, formatDateTime, formatLocalDateTime, useRest } from '../../utils'; import { AuthenticatedContext } from '../../contexts/authentication'; export const isNtpActive = ({ status }: NTPStatus) => status === NTPSyncStatus.NTP_ACTIVE; @@ -175,15 +174,6 @@ const NTPStatusForm: FC = () => { - - - - - - - - - diff --git a/interface/src/framework/system/FirmwareFileUpload.tsx b/interface/src/framework/system/FirmwareFileUpload.tsx index 824ed3cdf..79da9fc48 100644 --- a/interface/src/framework/system/FirmwareFileUpload.tsx +++ b/interface/src/framework/system/FirmwareFileUpload.tsx @@ -19,7 +19,8 @@ const FirmwareFileUpload: FC = ({ uploadFirmware }) => { my={2} /> { const desktopWindow = useMediaQuery('(min-width:600px)'); - const refreshData = () => { + const refreshAllData = () => { if (analog || sensor || deviceValue) { return; } loadData(); - if (sensorData) { fetchSensorData(); } else if (selectedDevice) { @@ -111,6 +110,19 @@ const DashboardData: FC = () => { } }; + const refreshData = () => { + if (analog || sensor || deviceValue) { + return; + } + if (sensorData) { + fetchSensorData(); + } else if (selectedDevice) { + fetchDeviceData(selectedDevice); + } else { + loadData(); + } + }; + useEffect(() => { const timer = setInterval(() => refreshData(), 60000); return () => { @@ -140,33 +152,15 @@ const DashboardData: FC = () => { } }; - const pluralize = (count: number, noun: string, suffix = 's') => - ` ${Intl.NumberFormat().format(count)} ${noun}${count !== 1 ? suffix : ''} `; - - const formatDuration = (duration_min: number) => { - const { days, hours, minutes } = parseMilliseconds(duration_min * 60000); - let formatted = ''; - if (days) { - formatted += pluralize(days, 'day'); - } - if (hours) { - formatted += pluralize(hours, 'hour'); - } - if (minutes) { - formatted += pluralize(minutes, 'minute'); - } - return formatted; - }; - function formatValue(value: any, uom: number) { if (value === undefined) { return ''; } switch (uom) { case DeviceValueUOM.HOURS: - return value ? formatDuration(value * 60) : '0 hours'; + return value ? formatDurationMin(value * 60) : '0 hours'; case DeviceValueUOM.MINUTES: - return value ? formatDuration(value) : '0 minutes'; + return value ? formatDurationMin(value) : '0 minutes'; case DeviceValueUOM.NONE: if (typeof value === 'number') { return new Intl.NumberFormat().format(value); @@ -239,7 +233,7 @@ const DashboardData: FC = () => { { }; const toggleDeviceData = (index: number) => { + loadData(); if (selectedDevice === index) { setSelectedDevice(undefined); } else { @@ -415,6 +410,7 @@ const DashboardData: FC = () => { }; const toggleSensorData = () => { + loadData(); if (sensorData) { setSensorData(undefined); } else { @@ -810,7 +806,7 @@ const DashboardData: FC = () => { {renderSensorDialog()} {renderAnalogDialog()} - diff --git a/interface/src/project/DashboardStatus.tsx b/interface/src/project/DashboardStatus.tsx index 2ed4c980b..6f81074ad 100644 --- a/interface/src/project/DashboardStatus.tsx +++ b/interface/src/project/DashboardStatus.tsx @@ -34,6 +34,8 @@ import { ButtonRow, FormLoader, SectionContent } from '../components'; import { Status, busConnectionStatus } from './types'; +import { formatDurationSec, pluralize } from '../utils'; + import * as EMSESP from './api'; import { extractErrorMessage, useRest } from '../utils'; @@ -66,27 +68,6 @@ const busStatus = ({ status }: Status) => { } }; -const pluralize = (count: number, noun: string) => - `${Intl.NumberFormat().format(count)} ${noun}${count !== 1 ? 's' : ''}`; - -const formatDuration = (duration_sec: number) => { - if (duration_sec === 0) { - return ' '; - } - const roundTowardsZero = duration_sec > 0 ? Math.floor : Math.ceil; - return ( - ', ' + - roundTowardsZero(duration_sec / 86400) + - 'd ' + - (roundTowardsZero(duration_sec / 3600) % 24) + - 'h ' + - (roundTowardsZero(duration_sec / 60) % 60) + - 'm ' + - (roundTowardsZero(duration_sec) % 60) + - 's' - ); -}; - const formatRow = (name: string, success: number, fail: number, quality: number) => { if (success === 0 && fail === 0) { return ( @@ -182,7 +163,7 @@ const DashboardStatus: FC = () => { - + diff --git a/interface/src/project/SettingsApplication.tsx b/interface/src/project/SettingsApplication.tsx index 169a61818..2213634ac 100644 --- a/interface/src/project/SettingsApplication.tsx +++ b/interface/src/project/SettingsApplication.tsx @@ -292,7 +292,6 @@ const SettingsApplication: FC = () => { margin="normal" select > - Off EMS EMS+ HT3 diff --git a/interface/src/utils/time.ts b/interface/src/utils/time.ts index ba4a841df..7a7986b52 100644 --- a/interface/src/utils/time.ts +++ b/interface/src/utils/time.ts @@ -18,23 +18,38 @@ export const formatLocalDateTime = (date: Date) => { return new Date(date.getTime() - date.getTimezoneOffset() * 60000).toISOString().slice(0, -1).substr(0, 19); }; -export const formatDuration = (duration: number) => { - const { days, hours, minutes, seconds } = parseMilliseconds(duration * 1000); - var formatted = ''; +export const pluralize = (count: number, noun: string) => + `${Intl.NumberFormat().format(count)} ${noun}${count !== 1 ? 's' : ''}`; + +export const formatDurationMin = (duration_min: number) => { + const { days, hours, minutes } = parseMilliseconds(duration_min * 60000); + let formatted = ''; if (days) { - formatted += pluralize(days, 'day'); + formatted += pluralize(days, 'day') + ' '; } - if (formatted || hours) { - formatted += pluralize(hours, 'hour'); + if (hours) { + formatted += pluralize(hours, 'hour') + ' '; } - if (formatted || minutes) { - formatted += pluralize(minutes, 'minute'); - } - if (formatted || seconds) { - formatted += pluralize(seconds, 'second'); + if (minutes) { + formatted += pluralize(minutes, 'minute') + ' '; } return formatted; }; -const pluralize = (count: number, noun: string, suffix: string = 's') => - ` ${count} ${noun}${count !== 1 ? suffix : ''} `; +export const formatDurationSec = (duration_sec: number) => { + if (duration_sec === 0) { + return ' '; + } + const roundTowardsZero = duration_sec > 0 ? Math.floor : Math.ceil; + return ( + ', ' + + roundTowardsZero(duration_sec / 86400) + + 'd ' + + (roundTowardsZero(duration_sec / 3600) % 24) + + 'h ' + + (roundTowardsZero(duration_sec / 60) % 60) + + 'm ' + + (roundTowardsZero(duration_sec) % 60) + + 's' + ); +}; diff --git a/lib/framework/NTPSettingsService.cpp b/lib/framework/NTPSettingsService.cpp index 8ac57dfaf..e00a84c2b 100644 --- a/lib/framework/NTPSettingsService.cpp +++ b/lib/framework/NTPSettingsService.cpp @@ -44,6 +44,7 @@ void NTPSettingsService::WiFiEvent(WiFiEvent_t event) { } } +// https://werner.rothschopf.net/microcontroller/202103_arduino_esp32_ntp_en.htm void NTPSettingsService::configureNTP() { if (connected_ && _state.enabled) { emsesp::EMSESP::logger().info(F("Starting NTP")); @@ -69,6 +70,7 @@ void NTPSettingsService::configureTime(AsyncWebServerRequest * request, JsonVari return; } } + AsyncWebServerResponse * response = request->beginResponse(400); request->send(response); } diff --git a/lib/framework/NTPStatus.cpp b/lib/framework/NTPStatus.cpp index 1073572d5..217345d44 100644 --- a/lib/framework/NTPStatus.cpp +++ b/lib/framework/NTPStatus.cpp @@ -46,9 +46,6 @@ void NTPStatus::ntpStatus(AsyncWebServerRequest * request) { // the sntp server name root["server"] = sntp_getservername(0); - // device uptime in seconds - root["uptime"] = millis() / 1000; - response->setLength(); request->send(response); } diff --git a/src/analogsensor.cpp b/src/analogsensor.cpp index 9f314eb5f..734b9e441 100644 --- a/src/analogsensor.cpp +++ b/src/analogsensor.cpp @@ -335,9 +335,8 @@ void AnalogSensor::publish_values(const bool force) { doc[sensor.name()] = sensor.value(); } } - - Mqtt::publish(F("analogsensor_data"), doc.as()); } + Mqtt::publish(F("analogsensor_data"), doc.as()); } // called from emsesp.cpp, similar to the emsdevice->get_value_info @@ -369,7 +368,7 @@ bool AnalogSensor::command_info(const char * value, const int8_t id, JsonObject if (id == -1) { // show number and id JsonObject dataSensor = output.createNestedObject(sensor.name()); dataSensor["id"] = sensor.id(); - dataSensor["type"] = FL_(enum_sensortype)[sensor.type()]; + dataSensor["type"] = FL_(enum_sensortype)[sensor.type()]; if (sensor.type() == AnalogType::ADC) { dataSensor["uom"] = EMSdevice::uom_to_string(sensor.uom()); dataSensor["offset"] = sensor.offset(); diff --git a/src/console.cpp b/src/console.cpp index 63068352e..9bf0c7048 100644 --- a/src/console.cpp +++ b/src/console.cpp @@ -54,10 +54,6 @@ void EMSESPShell::stopped() { logger().log(LogLevel::DEBUG, LogFacility::AUTH, F("su session closed on console %s"), console_name().c_str()); } logger().log(LogLevel::DEBUG, LogFacility::CONSOLE, F("User session closed on console %s"), console_name().c_str()); - - // remove all custom contexts - // commands->remove_all_commands(); - // console_commands_loaded_ = false; // make sure they get reloaded next time a console is opened } // show welcome banner @@ -77,8 +73,6 @@ void EMSESPShell::display_banner() { if (console_hostname_.empty()) { console_hostname_ = "ems-esp"; - // console_hostname_.resize(16, '\0'); - // snprintf(&console_hostname_[0], console_hostname_.capacity() + 1, "ems-esp"); } // load the list of commands @@ -465,31 +459,6 @@ std::string EMSESPShell::hostname_text() { return console_hostname_; } -/* -// remove commands from the current context to save memory before exiting -bool EMSESPShell::exit_context() { - unsigned int current_context = context(); - - if (current_context == ShellContext::MAIN) { - Shell::stop(); - return true; - } - // commands->remove_context_commands(current_context); - // return Shell::exit_context(); - return false; -} - -// enter a custom context (sub-menu) -void Console::enter_custom_context(Shell & shell, unsigned int context) { - // load_standard_commands(context); - - // don't go into the new context if it's already the root (to prevent double loading) - if (context != ShellContext::MAIN) { - shell.enter_context(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) @@ -771,22 +740,6 @@ void Console::load_system_commands(unsigned int context) { }); } -/* -// prompt, change per context -std::string EMSESPShell::context_text() { - switch (static_cast(context())) { - case ShellContext::MAIN: - return std::string{'/'}; - - case ShellContext::SYSTEM: - return std::string{"/system"}; - - default: - return std::string{}; - } -} -*/ - // when in su (admin) show # as the prompt suffix std::string EMSESPShell::prompt_suffix() { if (has_flags(CommandFlags::ADMIN)) { @@ -848,12 +801,11 @@ std::string EMSESPStreamConsole::console_name() { return name_; } -// Start up telnet and logging -// Log order is off, err, warning, notice, info, debug, trace, all -void Console::start(bool telnet_enabled) { - telnet_enabled_ = telnet_enabled; +// Start serial console +void Console::start_serial() { + Serial.begin(115200); - // Serial Console + // Serial Console - is always active shell = std::make_shared(Serial, true); shell->maximum_log_messages(100); shell->start(); @@ -865,20 +817,20 @@ void Console::start(bool telnet_enabled) { #if defined(EMSESP_STANDALONE) shell->add_flags(CommandFlags::ADMIN); // always start in su/admin mode when running tests #endif +} + +// Start up telnet +void Console::start_telnet() { + telnet_enabled_ = true; // telnet is enabled when calling this function // start the telnet service // default idle is 10 minutes, default write timeout is 0 (automatic) // note, this must be started after the network/wifi for ESP32 otherwise it'll crash #ifndef EMSESP_STANDALONE - if (telnet_enabled) { - telnet_.start(); - telnet_.initial_idle_timeout(3600); // in sec, one hour idle timeout - telnet_.default_write_timeout(1000); // in ms, socket timeout 1 second - } + telnet_.start(); + telnet_.initial_idle_timeout(3600); // in sec, one hour idle timeout + telnet_.default_write_timeout(1000); // in ms, socket timeout 1 second #endif - - // turn watch off in case it was still set in the last session - // EMSESP::watch(EMSESP::WATCH_OFF); } // handles telnet sync and logging to console diff --git a/src/console.h b/src/console.h index bf7253b78..025255944 100644 --- a/src/console.h +++ b/src/console.h @@ -99,8 +99,6 @@ class EMSESPShell : virtual public uuid::console::Shell { std::string prompt_suffix() override; void end_of_transmission() override; - // std::string context_text() override; - // bool exit_context() override; private: void add_console_commands(); @@ -128,16 +126,16 @@ class EMSESPStreamConsole : public uuid::console::StreamConsole, public EMSESPSh class Console { public: void loop(); - void start(bool telnet_enabled = true); + void start_serial(); + void start_telnet(); uuid::log::Level log_level(); - // static void enter_custom_context(Shell & shell, unsigned int context); static void load_standard_commands(unsigned int context); static void load_system_commands(unsigned int context); private: - bool telnet_enabled_; + bool telnet_enabled_ = false; // telnet is default off }; } // namespace emsesp diff --git a/src/default_settings.h b/src/default_settings.h index 702289957..d399f9ace 100644 --- a/src/default_settings.h +++ b/src/default_settings.h @@ -93,7 +93,7 @@ #endif #ifndef EMSESP_DEFAULT_BOARD_PROFILE -#define EMSESP_DEFAULT_BOARD_PROFILE "S32" // Gateway S32 +#define EMSESP_DEFAULT_BOARD_PROFILE "default" #endif // Default GPIO PIN definitions diff --git a/src/device_library.h b/src/device_library.h index 937b2e0f6..9d56e4602 100644 --- a/src/device_library.h +++ b/src/device_library.h @@ -101,6 +101,8 @@ {106, DeviceType::THERMOSTAT, F("FW200"), DeviceFlags::EMS_DEVICE_FLAG_JUNKERS}, {107, DeviceType::THERMOSTAT, F("FR100"), DeviceFlags::EMS_DEVICE_FLAG_JUNKERS | DeviceFlags::EMS_DEVICE_FLAG_JUNKERS_OLD}, // older model {108, DeviceType::THERMOSTAT, F("FR110"), DeviceFlags::EMS_DEVICE_FLAG_JUNKERS | DeviceFlags::EMS_DEVICE_FLAG_JUNKERS_OLD}, // older model +{109, DeviceType::THERMOSTAT, F("FB10"), DeviceFlags::EMS_DEVICE_FLAG_JUNKERS}, +{110, DeviceType::THERMOSTAT, F("FB100"), DeviceFlags::EMS_DEVICE_FLAG_JUNKERS}, {111, DeviceType::THERMOSTAT, F("FR10"), DeviceFlags::EMS_DEVICE_FLAG_JUNKERS | DeviceFlags::EMS_DEVICE_FLAG_JUNKERS_OLD}, // older model {147, DeviceType::THERMOSTAT, F("FR50"), DeviceFlags::EMS_DEVICE_FLAG_JUNKERS | DeviceFlags::EMS_DEVICE_FLAG_JUNKERS_OLD}, {191, DeviceType::THERMOSTAT, F("FR120"), DeviceFlags::EMS_DEVICE_FLAG_JUNKERS | DeviceFlags::EMS_DEVICE_FLAG_JUNKERS_OLD}, // older model diff --git a/src/devices/thermostat.cpp b/src/devices/thermostat.cpp index 4ca8ef419..0c6e14ea1 100644 --- a/src/devices/thermostat.cpp +++ b/src/devices/thermostat.cpp @@ -999,13 +999,17 @@ void Thermostat::process_RC300Set(std::shared_ptr telegram) { // has_update(telegram, hc->mode, 0); // Auto = xFF, Manual = x00 eg. 10 00 FF 08 01 B9 FF has_update(telegram, hc->daytemp, 2); // is * 2 has_update(telegram, hc->nighttemp, 4); // is * 2 - has_update(telegram, hc->tempautotemp, 8); - // uint8_t tat = hc->tempautotemp; - // telegram->read_value(tat, 8); - // if (tat == 0xFF) { - // tat = 0; - // } - // has_update(hc->tempautotemp, tat); + + // has_update(telegram, hc->tempautotemp, 8); // is * 2 + // manipulate tempautotemp to show -1°C (with scale 0.5°C) if value is 0xFF + // see https://github.com/emsesp/EMS-ESP32/issues/321 + int8_t tat = hc->tempautotemp; + telegram->read_value(tat, 8); + if ((uint8_t)tat == 0xFF) { + tat = -2; + } + has_update(hc->tempautotemp, tat); + has_update(telegram, hc->manualtemp, 10); // is * 2 has_enumupdate(telegram, hc->program, 11, 1); // timer program 1 or 2 } @@ -3277,7 +3281,7 @@ void Thermostat::register_device_values_hc(std::shared_ptrcontrolmode, DeviceValueType::ENUM, FL_(enum_controlmode), FL_(controlmode), DeviceValueUOM::NONE, MAKE_CF_CB(set_controlmode)); register_device_value(tag, &hc->program, DeviceValueType::ENUM, FL_(enum_progMode), FL_(program), DeviceValueUOM::NONE, MAKE_CF_CB(set_program)); register_device_value( - tag, &hc->tempautotemp, DeviceValueType::UINT, FL_(div2), FL_(tempautotemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_tempautotemp), 5, 30); + tag, &hc->tempautotemp, DeviceValueType::INT, FL_(div2), FL_(tempautotemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_tempautotemp), -1, 30); register_device_value(tag, &hc->fastHeatup, DeviceValueType::UINT, nullptr, FL_(fastheatup), DeviceValueUOM::PERCENT, MAKE_CF_CB(set_fastheatup)); break; case EMS_DEVICE_FLAG_CRF: diff --git a/src/emsesp.cpp b/src/emsesp.cpp index 1485f7150..b40c6dd64 100644 --- a/src/emsesp.cpp +++ b/src/emsesp.cpp @@ -20,12 +20,6 @@ namespace emsesp { -#if defined(EMSESP_STANDALONE) -uint32_t heap_start = 0; -#else -uint32_t heap_start = ESP.getFreeHeap(); // get initial available heap memory -#endif - AsyncWebServer webServer(80); #if defined(EMSESP_STANDALONE) @@ -174,6 +168,9 @@ void EMSESP::scan_devices() { * we send to right device and match all reads to 0x18 */ uint8_t EMSESP::check_master_device(const uint8_t device_id, const uint16_t type_id, const bool read) { + if (device_id != 0x10 && (device_id < 0x18 || device_id > 0x1F)) { + return device_id; + } if (actual_master_thermostat_ == 0x18) { uint16_t mon_ids[] = {0x02A5, 0x02A6, 0x02A7, 0x02A8, 0x02A9, 0x02AA, 0x02AB, 0x02AC}; uint16_t set_ids[] = {0x02B9, 0x02BA, 0x02BB, 0x02BC, 0x02BD, 0x02BE, 0x02BF, 0x02C0}; @@ -199,6 +196,17 @@ uint8_t EMSESP::check_master_device(const uint8_t device_id, const uint16_t type return 0x18; } } + } else if (actual_master_thermostat_ == 0x10) { + // Junkers FW200 supports hc1/hc2, hc3/hc4 handled by devices 0x1A... + // see https://github.com/emsesp/EMS-ESP32/issues/336 + uint16_t mon_ids[] = {0x0171, 0x0172}; + uint16_t set_ids[] = {0x0167, 0x0168}; + for (uint8_t i = 0; i < sizeof(mon_ids) / 2; i++) { + if (type_id == mon_ids[i] || type_id == set_ids[i]) { + // reads to master thermostat, writes to remote thermostats + return (read ? actual_master_thermostat_ : 0x1A + i); + } + } } return device_id; @@ -219,7 +227,7 @@ void EMSESP::watch_id(uint16_t watch_id) { // resets all counters and bumps the UART // this is called when the tx_mode is persisted in the FS either via Web UI or the console -void EMSESP::init_uart() { +void EMSESP::uart_init() { uint8_t tx_mode; uint8_t rx_gpio; uint8_t tx_gpio; @@ -1355,7 +1363,7 @@ void EMSESP::send_raw_telegram(const char * data) { // start all the core services // the services must be loaded in the correct order void EMSESP::start() { - Serial.begin(115200); + console_.start_serial(); // start the file system #ifndef EMSESP_STANDALONE @@ -1365,38 +1373,41 @@ void EMSESP::start() { } #endif - esp8266React.begin(); // loads core system services settings (network, mqtt, ap, ntp etc) - system_.check_upgrade(); // do any system upgrades - webSettingsService.begin(); // load EMS-ESP Application settings... - system_.get_settings(); // ...and store some of the settings locally for future reference - console_.start(system_.telnet_enabled()); // telnet and serial console, from here we can start logging events - webLogService.start(); // start web log service - webCustomizationService.begin(); // load the customizations - - // welcome message - LOG_INFO(F("Starting EMS-ESP version %s (hostname: %s)"), EMSESP_APP_VERSION, system_.hostname().c_str()); - LOG_INFO(F("Configuring for interface board profile %s"), system_.board_profile().c_str()); - - // start all the EMS-ESP services - mqtt_.start(); // mqtt init - system_.start(heap_start); // starts commands, led, adc, button, network, syslog & uart - shower_.start(); // initialize shower timer and shower alert - dallassensor_.start(); // Dallas external sensors - analogsensor_.start(); // Analog external sensors - webServer.begin(); // start the web server - // emsdevices.reserve(5); // reserve space for initially 5 devices to avoid mem frag issues - + esp8266React.begin(); // loads core system services settings (network, mqtt, ap, ntp etc) + webLogService.begin(); // start web log service. now we can start capturing logs to the web log + LOG_INFO(F("Starting EMS-ESP version %s (hostname: %s)"), EMSESP_APP_VERSION, system_.hostname().c_str()); // welcome message LOG_INFO(F("Last system reset reason Core0: %s, Core1: %s"), system_.reset_reason(0).c_str(), system_.reset_reason(1).c_str()); - // Load our library of known devices into stack mem. Names are stored in Flash memory (takes up about 1kb) + webSettingsService.begin(); // load EMS-ESP Application settings... + system_.reload_settings(); // ... and store some of the settings locally + webCustomizationService.begin(); // load the customizations + + // start telnet service if it's enabled + if (system_.telnet_enabled()) { + console_.start_telnet(); + } + + system_.check_upgrade(); // do any system upgrades + + // start all the EMS-ESP services + mqtt_.start(); // mqtt init + system_.start(); // starts commands, led, adc, button, network, syslog & uart + shower_.start(); // initialize shower timer and shower alert + dallassensor_.start(); // Dallas external sensors + analogsensor_.start(); // Analog external sensors + webLogService.start(); // apply settings to weblog service + + // Load our library of known devices into stack mem. Names are stored in Flash memory device_library_ = { #include "device_library.h" }; - LOG_INFO(F("EMS device library loaded with %d records"), device_library_.size()); + LOG_INFO(F("Loaded EMS device library (%d records)"), device_library_.size()); #if defined(EMSESP_STANDALONE) Mqtt::on_connect(); // simulate an MQTT connection #endif + + webServer.begin(); // start the web server } // main loop calling all services diff --git a/src/emsesp.h b/src/emsesp.h index 31ec98965..f9fc2fc31 100644 --- a/src/emsesp.h +++ b/src/emsesp.h @@ -145,7 +145,7 @@ class EMSESP { static void show_devices(uuid::console::Shell & shell); static void show_ems(uuid::console::Shell & shell); - static void init_uart(); + static void uart_init(); static void incoming_telegram(uint8_t * data, const uint8_t length); diff --git a/src/locale_EN.h b/src/locale_EN.h index f28d9f5ce..e1356b759 100644 --- a/src/locale_EN.h +++ b/src/locale_EN.h @@ -375,7 +375,7 @@ MAKE_PSTR_LIST(enum_controlmode, F_(off), F_(optimized), F_(simple), F_(mpc), F_ MAKE_PSTR_LIST(enum_controlmode2, F_(outdoor), F_(room)) // MAKE_PSTR_LIST(enum_controlmode3, F_(off), F_(room), F_(outdoor), F("room+outdoor")) MAKE_PSTR_LIST(enum_control, F_(off), F_(rc20), F_(rc3x)) -MAKE_PSTR_LIST(enum_j_control, F_(off), F("fb10"), F("fb110")) +MAKE_PSTR_LIST(enum_j_control, F_(off), F("fb10"), F("fb100")) MAKE_PSTR_LIST(enum_wwProgMode, F("std_prog"), F_(own_prog)) MAKE_PSTR_LIST(enum_dayOfWeek, F("mo"), F("tu"), F("we"), F("th"), F("fr"), F("sa"), F("so"), F("all")) diff --git a/src/system.cpp b/src/system.cpp index a21b9a4fc..6b56724a9 100644 --- a/src/system.cpp +++ b/src/system.cpp @@ -48,9 +48,8 @@ uuid::syslog::SyslogService System::syslog_; #endif // init statics -uint32_t System::heap_start_ = 1; // avoid using 0 to divide-by-zero later -PButton System::myPButton_; -bool System::restart_requested_ = false; +PButton System::myPButton_; +bool System::restart_requested_ = false; // send on/off to a gpio pin // value: true = HIGH, false = LOW @@ -169,7 +168,7 @@ bool System::command_syslog_level(const char * value, const int8_t id) { return StateUpdateResult::CHANGED; }, "local"); - EMSESP::system_.syslog_start(); + EMSESP::system_.syslog_init(); return true; } return false; @@ -240,7 +239,7 @@ void System::format(uuid::console::Shell & shell) { System::system_restart(); } -void System::syslog_start() { +void System::syslog_init() { bool was_enabled = syslog_enabled_; EMSESP::webSettingsService.read([&](WebSettings & settings) { syslog_enabled_ = settings.syslog_enabled; @@ -287,7 +286,7 @@ void System::syslog_start() { } // read some specific system settings to store locally for faster access -void System::get_settings() { +void System::reload_settings() { EMSESP::webSettingsService.read([&](WebSettings & settings) { pbutton_gpio_ = settings.pbutton_gpio; analog_enabled_ = settings.analog_enabled; @@ -360,20 +359,8 @@ bool System::is_valid_gpio(uint8_t pin) { return true; } -// first call. Sets memory and starts up the UART Serial bridge -void System::start(uint32_t heap_start) { -#if defined(EMSESP_DEBUG) - show_mem("Startup"); -#endif - - // set the inital free mem, only on first boot - if (heap_start_ < 2) { - heap_start_ = heap_start; - } - - // load in all the settings first - get_settings(); - +// Starts up the UART Serial bridge +void System::start() { #ifndef EMSESP_STANDALONE // disable bluetooth module // periph_module_disable(PERIPH_BT_MODULE); @@ -390,9 +377,9 @@ void System::start(uint32_t heap_start) { led_init(false); // init LED button_init(false); // the special button network_init(false); // network - syslog_start(); // start Syslog + syslog_init(); // start Syslog - EMSESP::init_uart(); // start UART + EMSESP::uart_init(); // start UART } // button single click @@ -429,7 +416,7 @@ void System::button_OnVLongPress(PButton & b) { // push button void System::button_init(bool refresh) { if (refresh) { - get_settings(); + reload_settings(); } if (is_valid_gpio(pbutton_gpio_)) { @@ -451,7 +438,7 @@ void System::button_init(bool refresh) { // set the LED to on or off when in normal operating mode void System::led_init(bool refresh) { if (refresh) { - get_settings(); + reload_settings(); } if ((led_gpio_ != 0) && is_valid_gpio(led_gpio_)) { @@ -517,15 +504,6 @@ void System::loop() { #endif } -void System::show_mem(const char * note) { -#ifndef EMSESP_STANDALONE - static uint32_t old_free_heap = 0; - uint32_t free_heap = ESP.getFreeHeap(); - LOG_INFO(F("(%s) Free heap: %lu (~%lu)"), note, free_heap, (uint32_t)Helpers::abs(free_heap - old_free_heap)); - old_free_heap = free_heap; -#endif -} - // create the json for heartbeat bool System::heartbeat_json(JsonObject & output) { uint8_t bus_status = EMSESP::bus_status(); @@ -590,7 +568,7 @@ void System::send_heartbeat() { // initializes network void System::network_init(bool refresh) { if (refresh) { - get_settings(); + reload_settings(); } last_system_check_ = 0; // force the LED to go from fast flash to pulse @@ -785,80 +763,82 @@ void System::show_users(uuid::console::Shell & shell) { } void System::show_system(uuid::console::Shell & shell) { - shell.printfln(F("Uptime: %s"), uuid::log::format_timestamp_ms(uuid::get_uptime_ms(), 3).c_str()); - + shell.println("System:"); + shell.printfln(F(" Board profile: %s"), board_profile().c_str()); + shell.printfln(F(" Uptime: %s"), uuid::log::format_timestamp_ms(uuid::get_uptime_ms(), 3).c_str()); #ifndef EMSESP_STANDALONE - shell.printfln(F("SDK version: %s"), ESP.getSdkVersion()); - shell.printfln(F("CPU frequency: %lu MHz"), ESP.getCpuFreqMHz()); - shell.printfln(F("Free heap: %lu bytes"), (uint32_t)ESP.getFreeHeap()); + shell.printfln(F(" SDK version: %s"), ESP.getSdkVersion()); + shell.printfln(F(" CPU frequency: %lu MHz"), ESP.getCpuFreqMHz()); + shell.printfln(F(" Free heap: %lu bytes"), (uint32_t)ESP.getFreeHeap()); shell.println(); + shell.println("Network:"); switch (WiFi.status()) { case WL_IDLE_STATUS: - shell.printfln(F("Network: Idle")); + shell.printfln(F(" Network: Idle")); break; case WL_NO_SSID_AVAIL: - shell.printfln(F("Network: Network not found")); + shell.printfln(F(" Network: Network not found")); break; case WL_SCAN_COMPLETED: - shell.printfln(F("Network: Network scan complete")); + shell.printfln(F(" Network: Network scan complete")); break; case WL_CONNECTED: - shell.printfln(F("Network: connected")); - shell.printfln(F("SSID: %s"), WiFi.SSID().c_str()); - shell.printfln(F("BSSID: %s"), WiFi.BSSIDstr().c_str()); - shell.printfln(F("RSSI: %d dBm (%d %%)"), WiFi.RSSI(), wifi_quality(WiFi.RSSI())); - shell.printfln(F("MAC address: %s"), WiFi.macAddress().c_str()); - shell.printfln(F("Hostname: %s"), WiFi.getHostname()); - shell.printfln(F("IPv4 address: %s/%s"), uuid::printable_to_string(WiFi.localIP()).c_str(), uuid::printable_to_string(WiFi.subnetMask()).c_str()); - shell.printfln(F("IPv4 gateway: %s"), uuid::printable_to_string(WiFi.gatewayIP()).c_str()); - shell.printfln(F("IPv4 nameserver: %s"), uuid::printable_to_string(WiFi.dnsIP()).c_str()); + shell.printfln(F(" Network: connected")); + shell.printfln(F(" SSID: %s"), WiFi.SSID().c_str()); + shell.printfln(F(" BSSID: %s"), WiFi.BSSIDstr().c_str()); + shell.printfln(F(" RSSI: %d dBm (%d %%)"), WiFi.RSSI(), wifi_quality(WiFi.RSSI())); + shell.printfln(F(" MAC address: %s"), WiFi.macAddress().c_str()); + shell.printfln(F(" Hostname: %s"), WiFi.getHostname()); + shell.printfln(F(" IPv4 address: %s/%s"), uuid::printable_to_string(WiFi.localIP()).c_str(), uuid::printable_to_string(WiFi.subnetMask()).c_str()); + shell.printfln(F(" IPv4 gateway: %s"), uuid::printable_to_string(WiFi.gatewayIP()).c_str()); + shell.printfln(F(" IPv4 nameserver: %s"), uuid::printable_to_string(WiFi.dnsIP()).c_str()); if (WiFi.localIPv6().toString() != "0000:0000:0000:0000:0000:0000:0000:0000") { - shell.printfln(F("IPv6 address: %s"), uuid::printable_to_string(WiFi.localIPv6()).c_str()); + shell.printfln(F(" IPv6 address: %s"), uuid::printable_to_string(WiFi.localIPv6()).c_str()); } break; case WL_CONNECT_FAILED: - shell.printfln(F("WiFi Network: Connection failed")); + shell.printfln(F(" WiFi Network: Connection failed")); break; case WL_CONNECTION_LOST: - shell.printfln(F("WiFi Network: Connection lost")); + shell.printfln(F(" WiFi Network: Connection lost")); break; case WL_DISCONNECTED: - shell.printfln(F("WiFi Network: Disconnected")); + shell.printfln(F(" WiFi Network: Disconnected")); break; case WL_NO_SHIELD: default: - shell.printfln(F("WiFi Network: Unknown")); + shell.printfln(F(" WiFi Network: Unknown")); break; } - shell.println(); - // show Ethernet if connected if (ethernet_connected_) { - shell.printfln(F("Wired Network: connected")); - shell.printfln(F("MAC address: %s"), ETH.macAddress().c_str()); - shell.printfln(F("Hostname: %s"), ETH.getHostname()); - shell.printfln(F("IPv4 address: %s/%s"), uuid::printable_to_string(ETH.localIP()).c_str(), uuid::printable_to_string(ETH.subnetMask()).c_str()); - shell.printfln(F("IPv4 gateway: %s"), uuid::printable_to_string(ETH.gatewayIP()).c_str()); - shell.printfln(F("IPv4 nameserver: %s"), uuid::printable_to_string(ETH.dnsIP()).c_str()); + shell.println(); + shell.printfln(F(" Wired Network: connected")); + shell.printfln(F(" MAC address: %s"), ETH.macAddress().c_str()); + shell.printfln(F(" Hostname: %s"), ETH.getHostname()); + shell.printfln(F(" IPv4 address: %s/%s"), uuid::printable_to_string(ETH.localIP()).c_str(), uuid::printable_to_string(ETH.subnetMask()).c_str()); + shell.printfln(F(" IPv4 gateway: %s"), uuid::printable_to_string(ETH.gatewayIP()).c_str()); + shell.printfln(F(" IPv4 nameserver: %s"), uuid::printable_to_string(ETH.dnsIP()).c_str()); if (ETH.localIPv6().toString() != "0000:0000:0000:0000:0000:0000:0000:0000") { - shell.printfln(F("IPv6 address: %s"), uuid::printable_to_string(ETH.localIPv6()).c_str()); + shell.printfln(F(" IPv6 address: %s"), uuid::printable_to_string(ETH.localIPv6()).c_str()); } } - shell.println(); + + shell.println("Syslog:"); if (!syslog_enabled_) { - shell.printfln(F("Syslog: disabled")); + shell.printfln(F(" Syslog: disabled")); } else { - shell.printfln(F("Syslog: %s"), syslog_.started() ? "started" : "stopped"); + shell.printfln(F(" Syslog: %s"), syslog_.started() ? "started" : "stopped"); shell.print(F(" ")); shell.printfln(F_(host_fmt), !syslog_host_.isEmpty() ? syslog_host_.c_str() : read_flash_string(F_(unset)).c_str()); shell.printfln(F(" IP: %s"), uuid::printable_to_string(syslog_.ip()).c_str()); @@ -1207,15 +1187,15 @@ bool System::command_test(const char * value, const int8_t id) { #endif // takes a board profile and populates a data array with GPIO configurations -// returns false if profile is not found +// returns false if profile is unknown // // data = led, dallas, rx, tx, button, phy_type, eth_power, eth_phy_addr, eth_clock_mode // // clock modes: -// ETH_CLOCK_GPIO0_IN = 0 RMII clock input to GPIO0 -// ETH_CLOCK_GPIO0_OUT = 1 RMII clock output from GPIO0 -// ETH_CLOCK_GPIO16_OUT = 2 RMII clock output from GPIO16 -// ETH_CLOCK_GPIO17_OUT = 3 RMII clock output from GPIO17, for 50hz inverted cloc +// 0 = RMII clock input to GPIO0 +// 1 = RMII clock output from GPIO0 +// 2 = RMII clock output from GPIO16 +// 3 = RMII clock output from GPIO17, for 50hz inverted clock bool System::load_board_profile(std::vector & data, const std::string & board_profile) { if (board_profile == "S32") { data = {2, 18, 23, 5, 0, PHY_type::PHY_TYPE_NONE, 0, 0, 0}; // BBQKees Gateway S32 @@ -1243,7 +1223,7 @@ bool System::load_board_profile(std::vector & data, const std::string & (int8_t)EMSESP::system_.eth_phy_addr_, (int8_t)EMSESP::system_.eth_clock_mode_}; } else { - // unknown, use defaults + // unknown, use defaults and return false data = { EMSESP_DEFAULT_LED_GPIO, EMSESP_DEFAULT_DALLAS_GPIO, diff --git a/src/system.h b/src/system.h index 3edb2d6a0..d87e6e737 100644 --- a/src/system.h +++ b/src/system.h @@ -45,7 +45,7 @@ enum PHY_type : uint8_t { PHY_TYPE_NONE = 0, PHY_TYPE_LAN8720, PHY_TYPE_TLK110 } class System { public: - void start(uint32_t heap_start); + void start(); void loop(); // commands @@ -72,9 +72,9 @@ class System { void upload_status(bool in_progress); bool upload_status(); void show_mem(const char * note); - void get_settings(); + void reload_settings(); void wifi_tweak(); - void syslog_start(); + void syslog_init(); bool check_upgrade(); bool heartbeat_json(JsonObject & output); void send_heartbeat(); @@ -180,7 +180,6 @@ class System { private: static uuid::log::Logger logger_; - static uint32_t heap_start_; static bool restart_requested_; // button @@ -220,7 +219,7 @@ class System { bool ethernet_connected_ = false; // EMS-ESP settings - // copies from WebSettings class in WebSettingsService.h + // copies from WebSettings class in WebSettingsService.h and loaded with reload_settings() std::string hostname_ = FACTORY_WIFI_HOSTNAME; bool hide_led_; uint8_t led_gpio_; diff --git a/src/telegram.cpp b/src/telegram.cpp index 8414f1333..0fa1b531e 100644 --- a/src/telegram.cpp +++ b/src/telegram.cpp @@ -359,8 +359,9 @@ void TxService::send_telegram(const QueuedTxTelegram & tx_telegram) { telegram_raw[message_p++] = telegram->message_data[i]; } } - - telegram_last_ = std::make_shared(*telegram); // make a copy of the telegram + // make a copy of the telegram with new dest (without read-flag) + telegram_last_ = std::make_shared( + telegram->operation, telegram->src, dest & 0x7F, telegram->type_id, telegram->offset, telegram->message_data, telegram->message_length); uint8_t length = message_p; telegram_raw[length] = calculate_crc(telegram_raw, length); // generate and append CRC to the end diff --git a/src/version.h b/src/version.h index 1157224e3..a8f070baa 100644 --- a/src/version.h +++ b/src/version.h @@ -1 +1 @@ -#define EMSESP_APP_VERSION "3.4.0b2" +#define EMSESP_APP_VERSION "3.4.0b4" diff --git a/src/web/WebLogService.cpp b/src/web/WebLogService.cpp index 18c25b03e..133986d9d 100644 --- a/src/web/WebLogService.cpp +++ b/src/web/WebLogService.cpp @@ -45,7 +45,12 @@ void WebLogService::forbidden(AsyncWebServerRequest * request) { request->send(403); } -// start event source service +// start the log service with INFO level +void WebLogService::begin() { + uuid::log::Logger::register_handler(this, uuid::log::Level::INFO); +} + +// apply the user settings void WebLogService::start() { EMSESP::webSettingsService.read([&](WebSettings & settings) { maximum_log_messages_ = settings.weblog_buffer; @@ -105,14 +110,6 @@ WebLogService::QueuedLogMessage::QueuedLogMessage(unsigned long id, std::shared_ } void WebLogService::operator<<(std::shared_ptr message) { - /* - // special case for trace, show trace and notice messages only - // added by mvdp - if (log_level() == uuid::log::Level::TRACE && message->level != uuid::log::Level::TRACE && message->level != uuid::log::Level::NOTICE) { - return; - } - */ - if (log_messages_.size() >= maximum_log_messages_) { log_messages_.pop_front(); } diff --git a/src/web/WebLogService.h b/src/web/WebLogService.h index 0686f6e42..5e3ae234f 100644 --- a/src/web/WebLogService.h +++ b/src/web/WebLogService.h @@ -32,6 +32,7 @@ class WebLogService : public uuid::log::Handler { WebLogService(AsyncWebServer * server, SecurityManager * securityManager); + void begin(); void start(); uuid::log::Level log_level() const; void log_level(uuid::log::Level level); diff --git a/src/web/WebSettingsService.cpp b/src/web/WebSettingsService.cpp index e20cf12be..e2652fecb 100644 --- a/src/web/WebSettingsService.cpp +++ b/src/web/WebSettingsService.cpp @@ -78,10 +78,12 @@ void WebSettings::read(WebSettings & settings, JsonObject & root) { StateUpdateResult WebSettings::update(JsonObject & root, WebSettings & settings) { // load default GPIO configuration based on board profile std::vector data; // // led, dallas, rx, tx, button, phy_type, eth_power, eth_phy_addr, eth_clock_mode - settings.board_profile = root["board_profile"] | EMSESP_DEFAULT_BOARD_PROFILE; if (!System::load_board_profile(data, settings.board_profile.c_str())) { - settings.board_profile = EMSESP_DEFAULT_BOARD_PROFILE; // invalid board configuration, override the default in case it has been misspelled + settings.board_profile = "CUSTOM"; + EMSESP::logger().info("No board profile found. Re-setting to %s", settings.board_profile.c_str()); + } else { + EMSESP::logger().info("Loading board profile %s", settings.board_profile.c_str()); } uint8_t default_led_gpio = data[0]; @@ -112,15 +114,12 @@ StateUpdateResult WebSettings::update(JsonObject & root, WebSettings & settings) prev = settings.syslog_enabled; settings.syslog_enabled = root["syslog_enabled"] | EMSESP_DEFAULT_SYSLOG_ENABLED; check_flag(prev, settings.syslog_enabled, ChangeFlags::SYSLOG); - prev = settings.syslog_level; settings.syslog_level = root["syslog_level"] | EMSESP_DEFAULT_SYSLOG_LEVEL; check_flag(prev, settings.syslog_level, ChangeFlags::SYSLOG); - prev = settings.syslog_mark_interval; settings.syslog_mark_interval = root["syslog_mark_interval"] | EMSESP_DEFAULT_SYSLOG_MARK_INTERVAL; check_flag(prev, settings.syslog_mark_interval, ChangeFlags::SYSLOG); - prev = settings.syslog_port; settings.syslog_port = root["syslog_port"] | EMSESP_DEFAULT_SYSLOG_PORT; check_flag(prev, settings.syslog_port, ChangeFlags::SYSLOG); @@ -167,8 +166,22 @@ StateUpdateResult WebSettings::update(JsonObject & root, WebSettings & settings) settings.analog_enabled = root["analog_enabled"] | EMSESP_DEFAULT_ANALOG_ENABLED; check_flag(prev, settings.analog_enabled, ChangeFlags::ADC); + // ethernet + prev = settings.phy_type; + settings.phy_type = root["phy_type"] | default_phy_type; + check_flag(prev, settings.phy_type, ChangeFlags::RESTART); + prev = settings.eth_power; + settings.eth_power = root["eth_power"] | default_eth_power; + check_flag(prev, settings.eth_power, ChangeFlags::RESTART); + prev = settings.eth_phy_addr; + settings.eth_phy_addr = root["eth_phy_addr"] | default_eth_phy_addr; + check_flag(prev, settings.eth_phy_addr, ChangeFlags::RESTART); + prev = settings.eth_clock_mode; + settings.eth_clock_mode = root["eth_clock_mode"] | default_eth_clock_mode; + check_flag(prev, settings.eth_clock_mode, ChangeFlags::RESTART); + // - // these need reboots to be applied... + // these need system restarts first before settings are activated... // prev = settings.telnet_enabled; settings.telnet_enabled = root["telnet_enabled"] | EMSESP_DEFAULT_TELNET_ENABLED; @@ -186,25 +199,9 @@ StateUpdateResult WebSettings::update(JsonObject & root, WebSettings & settings) settings.master_thermostat = root["master_thermostat"] | EMSESP_DEFAULT_MASTER_THERMOSTAT; check_flag(prev, settings.master_thermostat, ChangeFlags::RESTART); - // use whatever came from the board profile - prev = settings.phy_type; - settings.phy_type = root["phy_type"] | default_phy_type; - check_flag(prev, settings.phy_type, ChangeFlags::RESTART); - - prev = settings.eth_power; - settings.eth_power = root["eth_power"] | default_eth_power; - check_flag(prev, settings.eth_power, ChangeFlags::RESTART); - - prev = settings.eth_phy_addr; - settings.eth_phy_addr = root["eth_phy_addr"] | default_eth_phy_addr; - check_flag(prev, settings.eth_phy_addr, ChangeFlags::RESTART); - - prev = settings.eth_clock_mode; - settings.eth_clock_mode = root["eth_clock_mode"] | default_eth_clock_mode; - check_flag(prev, settings.eth_clock_mode, ChangeFlags::RESTART); - + // // without checks... - + // settings.trace_raw = root["trace_raw"] | EMSESP_DEFAULT_TRACELOG_RAW; EMSESP::trace_raw(settings.trace_raw); @@ -231,7 +228,6 @@ StateUpdateResult WebSettings::update(JsonObject & root, WebSettings & settings) if (flags_ == WebSettings::ChangeFlags::RESTART) { return StateUpdateResult::CHANGED_RESTART; // tell WebUI that a restart is needed } - return StateUpdateResult::CHANGED; } @@ -247,11 +243,11 @@ void WebSettingsService::onUpdate() { } if (WebSettings::has_flags(WebSettings::ChangeFlags::UART)) { - EMSESP::init_uart(); + EMSESP::uart_init(); } if (WebSettings::has_flags(WebSettings::ChangeFlags::SYSLOG)) { - EMSESP::system_.syslog_start(); // re-start (or stop) + EMSESP::system_.syslog_init(); // re-start (or stop) } if (WebSettings::has_flags(WebSettings::ChangeFlags::ADC)) { diff --git a/src/web/WebStatusService.cpp b/src/web/WebStatusService.cpp index 114fcaa97..34be9a29b 100644 --- a/src/web/WebStatusService.cpp +++ b/src/web/WebStatusService.cpp @@ -45,7 +45,7 @@ void WebStatusService::WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info) { EMSESP::esp8266React.getNetworkSettingsService()->read([&](NetworkSettings & networkSettings) { if (!networkSettings.enableIPv6) { EMSESP::system_.send_heartbeat(); - EMSESP::system_.syslog_start(); + EMSESP::system_.syslog_init(); } }); mDNS_start(); @@ -73,7 +73,7 @@ void WebStatusService::WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info) { EMSESP::esp8266React.getNetworkSettingsService()->read([&](NetworkSettings & networkSettings) { if (!networkSettings.enableIPv6) { EMSESP::system_.send_heartbeat(); - EMSESP::system_.syslog_start(); + EMSESP::system_.syslog_init(); } }); EMSESP::system_.ethernet_connected(true); @@ -115,7 +115,7 @@ void WebStatusService::WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info) { EMSESP::logger().info(F("WiFi connected with IP=%s, hostname=%s"), WiFi.localIPv6().toString().c_str(), WiFi.getHostname()); } EMSESP::system_.send_heartbeat(); - EMSESP::system_.syslog_start(); + EMSESP::system_.syslog_init(); mDNS_start(); break; #endif