Merge pull request #1369 from MichaelDvP/dev2

Testbuild
This commit is contained in:
Proddy
2023-10-30 16:43:33 +01:00
committed by GitHub
89 changed files with 5806 additions and 2153 deletions

View File

@@ -0,0 +1,24 @@
name: 'github-releases-to-discord'
on:
release:
types: [published]
jobs:
github-releases-to-discord:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Github Releases To Discord
uses: SethCohen/github-releases-to-discord@v1.13.1
with:
webhook_url: ${{ secrets.WEBHOOK_URL }}
color: "2105893"
username: "Release Changelog"
avatar_url: "https://cdn.discordapp.com/icons/816637840644505620/0b14718532d855c452903851b4f0c9a2.png"
content: "||@everyone||"
footer_title: "Changelog"
footer_icon_url: "https://cdn.discordapp.com/icons/816637840644505620/0b14718532d855c452903851b4f0c9a2.png"
footer_timestamp: true

View File

@@ -14,6 +14,8 @@ jobs:
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
with:
python-version: '3.11'
- uses: actions/setup-node@v3
with:
node-version: '18'
@@ -33,9 +35,10 @@ jobs:
run: |
cd interface
yarn install
yarn run typesafe-i18n --no-watch
yarn typesafe-i18n --no-watch
sed -i "s/= 'pl'/= 'en'/" ./src/i18n/i18n-util.ts
yarn run build
yarn build
yarn webUI
- name: Build firmware
run: |

View File

@@ -13,6 +13,8 @@ jobs:
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
with:
python-version: '3.11'
- uses: actions/setup-node@v3
with:
node-version: '18'
@@ -28,9 +30,10 @@ jobs:
run: |
cd interface
yarn install
yarn run typesafe-i18n --no-watch
yarn typesafe-i18n --no-watch
sed -i "s/= 'pl'/= 'en'/" ./src/i18n/i18n-util.ts
yarn run build
yarn build
yarn webUI
- name: Build firmware
run: |

View File

@@ -14,6 +14,8 @@ jobs:
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
with:
python-version: '3.11'
- uses: actions/setup-node@v3
with:
node-version: '18'
@@ -33,9 +35,10 @@ jobs:
run: |
cd interface
yarn install
yarn run typesafe-i18n --no-watch
yarn typesafe-i18n --no-watch
sed -i "s/= 'pl'/= 'en'/" ./src/i18n/i18n-util.ts
yarn run build
yarn build
yarn webUI
- name: Build firmware
run: |

3
.gitignore vendored
View File

@@ -36,6 +36,8 @@ stats.html
!.yarn/releases
!.yarn/sdks
!.yarn/versions
yarn.lock
interface/analyse.html
# scripts
test.sh
@@ -59,4 +61,3 @@ bw-output/
# dump_entities.csv
# dump_entities.xls*
yarn.lock

View File

@@ -13,7 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Power entities
- Optional input of BSSID for AP connection
- Return empty json if no entries in scheduler/custom/analogsnesor/temperaturesensor
- Return empty json if no entries in scheduler/custom/analogsensor/temperaturesensor
## Fixed

View File

@@ -7,13 +7,26 @@
## Added
- humidity for ventilation devices
- telegrams for RC100H, hc2 (seen on discord)
- names for BC400, GB192i, read temperatures for low loss header and heatblock [#1317](https://github.com/emsesp/EMS-ESP32/discussions/1317)
- telegrams for RC100H, hc2, etc. (seen on discord, not tested)
- names for BC400, GB192i.2, read temperatures for low loss header and heatblock [#1317](https://github.com/emsesp/EMS-ESP32/discussions/1317)
- option for `forceheatingoff` [#1262](https://github.com/emsesp/EMS-ESP32/issues/1262)
- remote thermostat emulation RC100H for RC3xx [#1278](https://github.com/emsesp/EMS-ESP32/discussions/1278)
- publish time for shower
- autodetect board_profile, store in nvs, add telnet command option, add E32V2
- heatpump high res energy counters [#1348, #1349. #1350](https://github.com/emsesp/EMS-ESP32/issues/1348)
- optional bssid in network settings
- extension module EM100 [#1315](https://github.com/emsesp/EMS-ESP32/discussions/1315)
## Fixed
- remove command `remoteseltemp`, thermostat accept it only from remote thermostat
- shower_data MQTT payload contains the timestamp [#1329](https://github.com/emsesp/EMS-ESP32/issues/1329)
- fixed helper text in Web Device Entity dialog box for numerical ranges
## Changed
- update to arduino 2.0.14 / idf 4.4.6
- small changes for arduino 3.0.0 / idf 5.1 compatibility
- update to platform 6.4.0, arduino 2.0.14 / idf 4.4.6
- small changes for arduino 3.0.0 / idf 5.1 compatibility (not backward compatible to platform 6.3.2 and before)
- AP start after 10 sec, stay until station/eth connected
- tested wifi-all-channel-scan (3.6.3-dev4 a-e), removed again because of connect issues
- mqtt disconnect stops queue

File diff suppressed because one or more lines are too long

View File

@@ -4,4 +4,4 @@ enableGlobalCache: false
nodeLinker: node-modules
yarnPath: .yarn/releases/yarn-4.0.0-rc.53.cjs
yarnPath: .yarn/releases/yarn-4.0.1.cjs

View File

@@ -1,77 +1,77 @@
{
"name": "EMS-ESP",
"version": "3.6.0",
"version": "3.6.3",
"description": "build EMS-ESP WebUI",
"homepage": "https://emsesp.github.io/docs",
"author": "proddy",
"license": "MIT",
"private": true,
"scripts": {
"dev": "vite",
"build": "vite build",
"build-hosted": "vite build --mode hosted",
"preview": "vite preview",
"preview-standalone": "npm-run-all -p preview typesafe-i18n mock-api",
"build-hosted": "typesafe-i18n --no-watch && vite build --mode hosted",
"preview-standalone": "typesafe-i18n --no-watch && vite build && concurrently -c \"auto\" \"npm:mock-api\" \"vite preview\"",
"mock-api": "node --watch ../mock-api ../mock-api/server.js",
"standalone": "npm-run-all -p dev typesafe-i18n mock-api",
"typesafe-i18n": "typesafe-i18n",
"standalone": "concurrently -c \"auto\" \"typesafe-i18n\" \"npm:mock-api\" \"vite\"",
"typesafe-i18n": "typesafe-i18n --no-watch",
"webUI": "node progmem-generator.js",
"format": "prettier --write '**/*.{ts,tsx,js,css,json,md}'",
"lint": "eslint . --cache --fix"
},
"dependencies": {
"@alova/adapter-xhr": "^1.0.1",
"@babel/core": "^7.23.2",
"@emotion/react": "^11.11.1",
"@emotion/styled": "^11.11.0",
"@mui/icons-material": "^5.14.13",
"@mui/material": "^5.14.13",
"@mui/icons-material": "^5.14.15",
"@mui/material": "^5.14.15",
"@table-library/react-table-library": "4.1.7",
"@types/lodash-es": "^4.17.9",
"@types/node": "^20.8.6",
"@types/react": "^18.2.28",
"@types/react-dom": "^18.2.13",
"@types/imagemin": "^8.0.3",
"@types/lodash-es": "^4.17.10",
"@types/node": "^20.8.9",
"@types/react": "^18.2.33",
"@types/react-dom": "^18.2.14",
"@types/react-router-dom": "^5.3.3",
"alova": "^2.13.1",
"async-validator": "^4.2.5",
"history": "^5.3.0",
"jwt-decode": "^3.1.2",
"jwt-decode": "^4.0.0",
"lodash-es": "^4.17.21",
"mime-types": "^2.1.35",
"react": "latest",
"react-dom": "latest",
"react-dropzone": "^14.2.3",
"react-icons": "^4.11.0",
"react-router-dom": "^6.16.0",
"react-router-dom": "^6.17.0",
"react-toastify": "^9.1.3",
"sockette": "^2.0.6",
"typesafe-i18n": "^5.26.2",
"typescript": "^5.2.2"
},
"devDependencies": {
"@babel/core": "^7.23.2",
"@preact/compat": "^17.1.2",
"@preact/preset-vite": "^2.6.0",
"@types/babel__core": "^7",
"@typescript-eslint/eslint-plugin": "^6.7.5",
"@typescript-eslint/parser": "^6.7.5",
"eslint": "^8.51.0",
"@typescript-eslint/eslint-plugin": "^6.9.0",
"@typescript-eslint/parser": "^6.9.0",
"concurrently": "^8.2.2",
"eslint": "^8.52.0",
"eslint-config-airbnb": "^19.0.4",
"eslint-config-airbnb-typescript": "^17.1.0",
"eslint-config-prettier": "^9.0.0",
"eslint-import-resolver-typescript": "^3.6.1",
"eslint-plugin-autofix": "^1.1.0",
"eslint-plugin-import": "^2.28.1",
"eslint-plugin-import": "^2.29.0",
"eslint-plugin-jsx-a11y": "^6.7.1",
"eslint-plugin-prettier": "alpha",
"eslint-plugin-react": "^7.33.2",
"eslint-plugin-react-hooks": "^4.6.0",
"npm-run-all": "^4.1.5",
"preact": "^10.18.1",
"prettier": "^3.0.3",
"rollup-plugin-visualizer": "^5.9.2",
"terser": "^5.21.0",
"vite": "^4.4.11",
"vite-plugin-svgr": "^4.1.0",
"terser": "^5.22.0",
"vite": "^4.5.0",
"vite-plugin-imagemin": "^0.6.1",
"vite-tsconfig-paths": "^4.2.1"
},
"packageManager": "yarn@4.0.0-rc.53"
"packageManager": "yarn@4.0.1"
}

View File

@@ -5,6 +5,23 @@ var mime = require('mime-types');
const ARDUINO_INCLUDES = '#include <Arduino.h>\n\n';
const INDENT = ' ';
const outputPath = '../lib/framework/WWWData.h';
const sourcePath = './dist';
const bytesPerLine = 20;
var totalSize = 0;
const generateWWWClass = () =>
`typedef std::function<void(const String& uri, const String& contentType, const uint8_t * content, size_t len)> RouteRegistrationHandler;
class WWWData {
${indent}public:
${indent.repeat(2)}static void registerRoutes(RouteRegistrationHandler handler) {
${fileInfo
.map((file) => `${indent.repeat(3)}handler("${file.uri}", "${file.mimeType}", ${file.variable}, ${file.size});`)
.join('\n')}
${indent.repeat(2)}}
};
`;
function getFilesSync(dir, files = []) {
readdirSync(dir, { withFileTypes: true }).forEach((entry) => {
@@ -18,10 +35,6 @@ function getFilesSync(dir, files = []) {
return files;
}
// function coherseToBuffer(input) {
// return Buffer.isBuffer(input) ? input : Buffer.from(input);
// }
function cleanAndOpen(path) {
if (existsSync(path)) {
unlinkSync(path);
@@ -29,90 +42,58 @@ function cleanAndOpen(path) {
return createWriteStream(path, { flags: 'w+' });
}
export default function ProgmemGenerator({ outputPath = './WWWData.h', bytesPerLine = 20 }) {
return {
name: 'ProgmemGenerator',
writeBundle: () => {
console.log('Generating ' + outputPath);
const includes = ARDUINO_INCLUDES;
const indent = INDENT;
const fileInfo = [];
const writeStream = cleanAndOpen(resolve(outputPath));
try {
const writeIncludes = () => {
writeStream.write(includes);
};
const writeFile = (relativeFilePath, buffer) => {
const variable = 'ESP_REACT_DATA_' + fileInfo.length;
const mimeType = mime.lookup(relativeFilePath);
var size = 0;
writeStream.write('const uint8_t ' + variable + '[] = {');
// const zipBuffer = zlib.brotliCompressSync(buffer, { quality: 1 });
const zipBuffer = zlib.gzipSync(buffer);
zipBuffer.forEach((b) => {
if (!(size % bytesPerLine)) {
writeStream.write('\n');
writeStream.write(indent);
}
writeStream.write('0x' + ('00' + b.toString(16).toUpperCase()).substr(-2) + ',');
size++;
});
if (size % bytesPerLine) {
writeStream.write('\n');
}
writeStream.write('};\n\n');
fileInfo.push({
uri: '/' + relativeFilePath.replace(sep, '/'),
mimeType,
variable,
size
});
};
const writeFiles = () => {
// process static files
const buildPath = resolve('build');
for (const filePath of getFilesSync(buildPath)) {
const readStream = readFileSync(filePath);
const relativeFilePath = relative(buildPath, filePath);
writeFile(relativeFilePath, readStream);
}
// process assets
// const { assets } = compilation;
// Object.keys(assets).forEach((relativeFilePath) => {
// writeFile(relativeFilePath, coherseToBuffer(assets[relativeFilePath].source()));
// });
};
const generateWWWClass = () =>
`typedef std::function<void(const String& uri, const String& contentType, const uint8_t * content, size_t len)> RouteRegistrationHandler;
class WWWData {
${indent}public:
${indent.repeat(2)}static void registerRoutes(RouteRegistrationHandler handler) {
${fileInfo
.map((file) => `${indent.repeat(3)}handler("${file.uri}", "${file.mimeType}", ${file.variable}, ${file.size});`)
.join('\n')}
${indent.repeat(2)}}
};
`;
const writeWWWClass = () => {
writeStream.write(generateWWWClass());
};
writeIncludes();
writeFiles();
writeWWWClass();
writeStream.on('finish', () => {
// callback();
});
} finally {
writeStream.end();
}
const writeFile = (relativeFilePath, buffer) => {
const variable = 'ESP_REACT_DATA_' + fileInfo.length;
const mimeType = mime.lookup(relativeFilePath);
var size = 0;
writeStream.write('const uint8_t ' + variable + '[] = {');
// const zipBuffer = zlib.brotliCompressSync(buffer, { quality: 1 });
const zipBuffer = zlib.gzipSync(buffer, { level: 9 });
zipBuffer.forEach((b) => {
if (!(size % bytesPerLine)) {
writeStream.write('\n');
writeStream.write(indent);
}
};
writeStream.write('0x' + ('00' + b.toString(16).toUpperCase()).slice(-2) + ',');
size++;
});
if (size % bytesPerLine) {
writeStream.write('\n');
}
writeStream.write('};\n\n');
fileInfo.push({
uri: '/' + relativeFilePath.replace(sep, '/'),
mimeType,
variable,
size
});
// console.log(relativeFilePath + ' (size ' + size + ' bytes)');
totalSize += size;
};
// start
console.log('Generating ' + outputPath + ' from ' + sourcePath);
const includes = ARDUINO_INCLUDES;
const indent = INDENT;
const fileInfo = [];
const writeStream = cleanAndOpen(resolve(outputPath));
// includes
writeStream.write(includes);
// process static files
const buildPath = resolve(sourcePath);
for (const filePath of getFilesSync(buildPath)) {
const readStream = readFileSync(filePath);
const relativeFilePath = relative(buildPath, filePath);
writeFile(relativeFilePath, readStream);
}
// add class
writeStream.write(generateWWWClass());
// end
writeStream.end();
console.log('Total size: ' + totalSize / 1000 + ' KB');

View File

@@ -1,4 +1,4 @@
import jwtDecode from 'jwt-decode';
import { jwtDecode } from 'jwt-decode';
import { ACCESS_TOKEN, alovaInstance } from './endpoints';
import type * as H from 'history';
import type { Path } from 'react-router-dom';

View File

@@ -1,6 +1,6 @@
import { useRequest } from 'alova';
import { useCallback, useEffect, useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { redirect } from 'react-router-dom';
import { toast } from 'react-toastify';
import { AuthenticationContext } from './context';
import type { FC } from 'react';
@@ -15,8 +15,6 @@ import { useI18nContext } from 'i18n/i18n-react';
const Authentication: FC<RequiredChildrenProps> = ({ children }) => {
const { LL } = useI18nContext();
const navigate = useNavigate();
const [initialized, setInitialized] = useState<boolean>(false);
const [me, setMe] = useState<Me>();
@@ -36,11 +34,12 @@ const Authentication: FC<RequiredChildrenProps> = ({ children }) => {
}
};
const signOut = (redirect: boolean) => {
const signOut = (doRedirect: boolean) => {
AuthenticationApi.clearAccessToken();
setMe(undefined);
if (redirect) {
navigate('/');
if (doRedirect) {
// navigate('/');
redirect('/');
}
};

View File

@@ -69,7 +69,7 @@ const MqttStatusForm: FC = () => {
case MqttDisconnectReason.MQTT_NOT_AUTHORIZED:
return 'Not authorized';
case MqttDisconnectReason.TLS_BAD_FINGERPRINT:
return 'TSL fingerprint invalid';
return 'TLS fingerprint invalid';
default:
return 'Unknown';
}

View File

@@ -13,7 +13,7 @@ import * as EMSESP from 'project/api';
const UploadFileForm: FC = () => {
const { LL } = useI18nContext();
const [restarting, setRestarting] = useState<boolean>(false);
const [restarting, setRestarting] = useState<boolean>();
const [md5, setMd5] = useState<string>();
const { send: getSettings, onSuccess: onSuccessGetSettings } = useRequest(EMSESP.getSettings(), {
@@ -28,6 +28,9 @@ const UploadFileForm: FC = () => {
const { send: getSchedule, onSuccess: onSuccessGetSchedule } = useRequest(EMSESP.getSchedule(), {
immediate: false
});
const { send: getInfo, onSuccess: onSuccessGetInfo } = useRequest((data) => EMSESP.API(data), {
immediate: false
});
const {
loading: isUploading,
@@ -86,6 +89,9 @@ const UploadFileForm: FC = () => {
onSuccessGetSchedule((event) => {
saveFile(event.data, 'schedule');
});
onSuccessGetInfo((event) => {
saveFile(event.data, 'info');
});
const downloadSettings = async () => {
await getSettings().catch((error) => {
@@ -111,13 +117,23 @@ const UploadFileForm: FC = () => {
});
};
const downloadInfo = async () => {
await getInfo({ device: 'system', entity: 'info', id: 0 }).catch((error) => {
toast.error(error.message);
});
};
const content = () => (
<>
<Typography sx={{ pt: 2, pb: 2 }} variant="h6" color="primary">
{LL.UPLOAD()}
</Typography>
<Box mb={2} color="warning.main">
<Typography variant="body2">{LL.UPLOAD_TEXT()} </Typography>
<Typography variant="body2">
{LL.UPLOAD_TEXT()}
<br />
{LL.RESTART_TEXT()}.
</Typography>
</Box>
{md5 && (
<Box mb={2}>
@@ -140,7 +156,7 @@ const UploadFileForm: FC = () => {
</Button>
<Box color="warning.main">
<Typography mt={2} mb={1} variant="body2">
{LL.DOWNLOAD_CUSTOMIZATION_TEXT()}{' '}
{LL.DOWNLOAD_CUSTOMIZATION_TEXT()}
</Typography>
</Box>
<Button startIcon={<DownloadIcon />} variant="outlined" color="primary" onClick={downloadCustomizations}>
@@ -157,12 +173,20 @@ const UploadFileForm: FC = () => {
</Button>
<Box color="warning.main">
<Typography mt={2} mb={1} variant="body2">
{LL.DOWNLOAD_SCHEDULE_TEXT()}{' '}
{LL.DOWNLOAD_SCHEDULE_TEXT()}
</Typography>
</Box>
<Button startIcon={<DownloadIcon />} variant="outlined" color="primary" onClick={downloadSchedule}>
{LL.SCHEDULE(0)}
</Button>
<Box color="warning.main">
<Typography mt={2} mb={1} variant="body2">
{LL.DOWNLOAD(0)}&nbsp;{LL.SUPPORT_INFORMATION()}
</Typography>
</Box>
<Button startIcon={<DownloadIcon />} variant="outlined" color="primary" onClick={downloadInfo}>
{LL.SUPPORT_INFORMATION()}
</Button>
</>
)}
</>

View File

@@ -171,7 +171,6 @@ const de: Translation = {
HELP_INFORMATION_3: 'Um neue Funktionen anzufragen oder Fehler zu melden, eröffnen Sie ein Issue auf Github',
HELP_INFORMATION_4: 'Bitte laden Sie die System-Details und hängen Sie sie an das Support-Issue an. ',
HELP_INFORMATION_5: 'EMS-ESP ist ein freies Open-Source Projekt. Bitte unterstützen Sie die zukünftige Entwicklung mit einem "Star" auf Github!',
SUPPORT_INFO: 'Support Info',
UPLOAD: 'Hochladen',
DOWNLOAD: '{{H|h|h}}erunterladen',
ABORTED: 'abgebrochen',
@@ -231,7 +230,7 @@ const de: Translation = {
BROKER: 'Broker',
CLIENT: 'Client',
BASE_TOPIC: 'Base',
OPTIONAL: 'Optional',
OPTIONAL: 'Optional', // TODO translate
FORMATTING: 'Formattierung',
MQTT_FORMAT: 'Topic/Payload Format',
MQTT_NEST_1: 'Eingebettet in einem Gesamttopic',
@@ -324,7 +323,7 @@ const de: Translation = {
WRITEABLE: 'Schreibbar',
SHOWING: 'Anzeigen von',
SEARCH: 'Suche',
CERT: 'TSL Zertifikat (Freilassen um TSL zu deaktivieren)'
CERT: 'TLS Zertifikat (Freilassen um TLS zu deaktivieren)'
};
export default de;

View File

@@ -171,7 +171,6 @@ const en: Translation = {
HELP_INFORMATION_3: 'To request a feature or report a bug',
HELP_INFORMATION_4: 'remember to download and attach your system information for a faster response when reporting an issue',
HELP_INFORMATION_5: 'EMS-ESP is a free and open-source project. Please support its future development by giving it a star on Github!',
SUPPORT_INFO: 'Support Info',
UPLOAD: 'Upload',
DOWNLOAD: '{{D|d|d}}ownload',
ABORTED: 'aborted',
@@ -231,7 +230,7 @@ const en: Translation = {
BROKER: 'Broker',
CLIENT: 'Client',
BASE_TOPIC: 'Base',
OPTIONAL: 'Optional',
OPTIONAL: 'optional',
FORMATTING: 'Formatting',
MQTT_FORMAT: 'Topic/Payload Format',
MQTT_NEST_1: 'Nested in a single topic',
@@ -324,7 +323,7 @@ const en: Translation = {
WRITEABLE: 'Writeable',
SHOWING: 'Showing',
SEARCH: 'Search',
CERT: 'TSL root certificate (leave blank to disable TSL)'
CERT: 'TLS root certificate (leave blank to disable TLS)'
};
export default en;

View File

@@ -171,7 +171,6 @@ const fr: Translation = {
HELP_INFORMATION_3: 'Pour demander une fonctionnalité ou signaler un problème',
HELP_INFORMATION_4: 'n\'oubliez pas de télécharger et de joindre les informations relatives à votre système pour obtenir une réponse plus rapide lorsque vous signalez un problème',
HELP_INFORMATION_5: 'EMS-ESP est un projet libre et open-source. Merci de soutenir son développement futur en lui donnant une étoile sur Github !',
SUPPORT_INFO: 'Information de support',
UPLOAD: 'Upload',
DOWNLOAD: '{{D|d|d}}ownload',
ABORTED: 'annulé',
@@ -231,7 +230,7 @@ const fr: Translation = {
BROKER: 'Broker',
CLIENT: 'Client',
BASE_TOPIC: 'Base',
OPTIONAL: 'Optionnel',
OPTIONAL: 'optionnel',
FORMATTING: 'Mise en forme',
MQTT_FORMAT: 'Format du Topic/Payload',
MQTT_NEST_1: 'Englobé dans un topic unique',
@@ -324,7 +323,7 @@ const fr: Translation = {
WRITEABLE: 'Writeable', // TODO translate
SHOWING: 'Showing', // TODO translate
SEARCH: 'Search', // TODO translate
CERT: 'TSL root certificate (leave blank to disable TSL)' // TODO translate
CERT: 'TLS root certificate (leave blank to disable TLS)' // TODO translate
};
export default fr;

View File

@@ -173,7 +173,6 @@ const it: Translation = {
HELP_INFORMATION_3: 'Per richiedere una funzionalità o segnalare un errore',
HELP_INFORMATION_4: 'ricordati di scaricare e allegare le informazioni del tuo sistema per una risposta più rapida quando segnali un problema',
HELP_INFORMATION_5: 'EMS-ESP è un progetto gratuito e open-source. Supporta il suo sviluppo futuro assegnandogli una stella su Github!',
SUPPORT_INFO: 'Info Supporto',
UPLOAD: 'Carica',
DOWNLOAD: 'Scarica',
ABORTED: 'Annullato',
@@ -233,7 +232,7 @@ const it: Translation = {
BROKER: 'Broker',
CLIENT: 'Cliente',
BASE_TOPIC: 'Base',
OPTIONAL: 'Opzionale',
OPTIONAL: 'opzionale',
FORMATTING: 'Formattazione',
MQTT_FORMAT: 'Formato Topic/Payload ',
MQTT_NEST_1: 'Inserito in un singolo argomento',
@@ -326,7 +325,7 @@ const it: Translation = {
WRITEABLE: 'Scrivibile',
SHOWING: 'Visualizza',
SEARCH: 'Ricerca',
CERT: 'TSL root certificate (leave blank to disable TSL)' // TODO translate
CERT: 'TLS root certificate (leave blank to disable TLS)' // TODO translate
};
export default it;

View File

@@ -171,7 +171,6 @@ const nl: Translation = {
HELP_INFORMATION_3: 'Om een nieuwe feature te vragen of een bug te rapporteren',
HELP_INFORMATION_4: 'zorg dat je ook je systeem details zijn toevoeged voor een sneller antwoord',
HELP_INFORMATION_5: 'EMS-ESP is een gratis en open source project. Steun ons met een Star op Github!',
SUPPORT_INFO: 'Support Info',
UPLOAD: 'Upload',
DOWNLOAD: '{{D|d|d}}ownload',
ABORTED: 'afgebroken',
@@ -231,7 +230,7 @@ const nl: Translation = {
BROKER: 'Broker',
CLIENT: 'Client',
BASE_TOPIC: 'Base',
OPTIONAL: 'Optioneel',
OPTIONAL: 'optioneel',
FORMATTING: 'Formatteren',
MQTT_FORMAT: 'Topic/Payload Formattering',
MQTT_NEST_1: 'Genest in 1 topic',
@@ -324,7 +323,7 @@ const nl: Translation = {
WRITEABLE: 'Beschrijfbare',
SHOWING: 'Tonen',
SEARCH: 'Zoek',
CERT: 'TSL rootcertificaat (laat leeg om TSL uit te schakelen)'
CERT: 'TLS rootcertificaat (laat leeg om TLS uit te schakelen)'
};
export default nl;

View File

@@ -171,7 +171,6 @@ const no: Translation = {
HELP_INFORMATION_3: 'For å be om en ny funksjon eller melde feil',
HELP_INFORMATION_4: 'husk å laste ned og legg ved din systeminformasjon for en raskere respons når du rapporterer et problem',
HELP_INFORMATION_5: 'EMS-ESP er gratis og åpen kildekode. Bidra til utviklingen ved å gi oss en stjerne på GitHub!',
SUPPORT_INFO: 'Supportinfo',
UPLOAD: 'Opplasning',
DOWNLOAD: '{{N|n|n}}edlasting',
ABORTED: 'avbrutt',
@@ -231,7 +230,7 @@ const no: Translation = {
BROKER: 'Broker',
CLIENT: 'Client',
BASE_TOPIC: 'Base',
OPTIONAL: 'Valgfritt',
OPTIONAL: 'valgfritt',
FORMATTING: 'Formatering',
MQTT_FORMAT: 'Topic/Payload Format',
MQTT_NEST_1: 'Nestet i en topic',
@@ -324,7 +323,7 @@ const no: Translation = {
WRITEABLE: 'Writeable', // TODO translate
SHOWING: 'Showing', // TODO translate
SEARCH: 'Search', // TODO translate
CERT: 'TSL root certificate (leave blank to disable TSL)' // TODO translate
CERT: 'TLS root certificate (leave blank to disable TLS)' // TODO translate
};
export default no;

View File

@@ -171,7 +171,6 @@ const pl: BaseTranslation = {
HELP_INFORMATION_3: 'Aby zaproponować nową funkcjonalność lub zgłosić problem',
HELP_INFORMATION_4: 'Zgłaszając problem, nie zapomnij dołączyć informacji o swoim systemie!',
HELP_INFORMATION_5: 'EMS-ESP jest darmowym projektem typu open-source. Aby go wesprzeć, rozważ przyznanie nam gwiazdki na Github!',
SUPPORT_INFO: 'Pobierz informacje',
UPLOAD: 'Wysyłanie',
DOWNLOAD: '{{P|p||P}}obier{{anie|z||z}}',
ABORTED: 'zostało przerwane!',
@@ -324,7 +323,7 @@ const pl: BaseTranslation = {
WRITEABLE: 'zapisywalna',
SHOWING: 'Wyświetlane',
SEARCH: 'Szukaj',
CERT: 'TSL root certificate (leave blank to disable TSL)' // TODO translate
CERT: 'TLS root certificate (leave blank to disable TLS)' // TODO translate
};
export default pl;

View File

@@ -171,7 +171,6 @@ const sv: Translation = {
HELP_INFORMATION_3: 'Önska en ny funktion eller rapportera en bugg',
HELP_INFORMATION_4: 'Bifoga din systeminformation för snabbare hantering när du rapporterar ett problem',
HELP_INFORMATION_5: 'EMS-ESP är gratis och är öppen källkod. Bidra till utvecklingen genom att ge oss en stjärna på GitHub!',
SUPPORT_INFO: 'Supportinfo',
UPLOAD: 'Uppladdning',
DOWNLOAD: '{{N|n|n}}edladdning',
ABORTED: 'Avbruten',
@@ -231,7 +230,7 @@ const sv: Translation = {
BROKER: 'Broker',
CLIENT: 'Client',
BASE_TOPIC: 'Base',
OPTIONAL: 'Valfritt',
OPTIONAL: 'valfritt',
FORMATTING: 'Formatering',
MQTT_FORMAT: 'Topic/Payload Format',
MQTT_NEST_1: 'Nestlat i en topic.',
@@ -324,7 +323,7 @@ const sv: Translation = {
WRITEABLE: 'Writeable', // TODO translate
SHOWING: 'Showing', // TODO translate
SEARCH: 'Search', // TODO translate
CERT: 'TSL root certificate (leave blank to disable TSL)' // TODO translate
CERT: 'TLS root certificate (leave blank to disable TLS)' // TODO translate
};
export default sv;

View File

@@ -171,7 +171,6 @@ const tr: Translation = {
HELP_INFORMATION_3: 'Yeni bir özellik talep etmek yada hata bildirmek için',
HELP_INFORMATION_4: 'Bir sorun bildirirken daha hızlı bir dönüş için sistem bilginizi indirip eklemeyi unutmayın',
HELP_INFORMATION_5: 'EMS-ESP ücretsiz ve açık kaynaklı bir projedir. Lütfen geliştirmeyi desteklemek için Githubda projeye yıldız verin!',
SUPPORT_INFO: 'Destek Bilgisi',
UPLOAD: 'Yükleme',
DOWNLOAD: '{{İ|i|i}}İndirme',
ABORTED: 'iptal edildi',
@@ -231,7 +230,7 @@ const tr: Translation = {
BROKER: 'Aracı',
CLIENT: 'İstemci',
BASE_TOPIC: 'Merkez',
OPTIONAL: 'Seçenekli',
OPTIONAL: 'seçenekli',
FORMATTING: 'Biçimlendiriliyor',
MQTT_FORMAT: 'Konu/Mesaj Biçimi',
MQTT_NEST_1: 'Tek konu üzerine yerleşmiş',
@@ -324,7 +323,7 @@ const tr: Translation = {
WRITEABLE: 'Writeable', // TODO translate
SHOWING: 'Showing', // TODO translate
SEARCH: 'Search', // TODO translate
CERT: 'TSL root certificate (leave blank to disable TSL)' // TODO translate
CERT: 'TLS root certificate (leave blank to disable TLS)' // TODO translate
};
export default tr;

View File

@@ -1,8 +1,9 @@
import CancelIcon from '@mui/icons-material/Cancel';
import CommentsDisabledOutlinedIcon from '@mui/icons-material/CommentsDisabledOutlined';
import EditIcon from '@mui/icons-material/Edit';
import EditOffOutlinedIcon from '@mui/icons-material/EditOffOutlined';
import FormatListNumberedIcon from '@mui/icons-material/FormatListNumbered';
import DownloadIcon from '@mui/icons-material/GetApp';
import HighlightOffIcon from '@mui/icons-material/HighlightOff';
import InfoOutlinedIcon from '@mui/icons-material/InfoOutlined';
import KeyboardArrowDownOutlinedIcon from '@mui/icons-material/KeyboardArrowDownOutlined';
import KeyboardArrowUpOutlinedIcon from '@mui/icons-material/KeyboardArrowUpOutlined';
@@ -26,6 +27,7 @@ import {
Grid,
Typography
} from '@mui/material';
import { useRowSelect } from '@table-library/react-table-library/select';
import { useSort, SortToggleType } from '@table-library/react-table-library/sort';
import { Table, Header, HeaderRow, HeaderCell, Body, Row, Cell } from '@table-library/react-table-library/table';
@@ -34,6 +36,7 @@ import { useRequest } from 'alova';
import { useState, useContext, useEffect, useCallback, useLayoutEffect } from 'react';
import { IconContext } from 'react-icons';
import { useNavigate } from 'react-router-dom';
import { toast } from 'react-toastify';
import DashboardDevicesDialog from './DashboardDevicesDialog';
import DeviceIcon from './DeviceIcon';
@@ -52,15 +55,17 @@ import { AuthenticatedContext } from 'contexts/authentication';
import { useI18nContext } from 'i18n/i18n-react';
const DashboardDevices: FC = () => {
const [size, setSize] = useState([0, 0]);
const { me } = useContext(AuthenticatedContext);
const { LL } = useI18nContext();
const [size, setSize] = useState([0, 0]);
const [selectedDeviceValue, setSelectedDeviceValue] = useState<DeviceValue>();
const [onlyFav, setOnlyFav] = useState(false);
const [deviceValueDialogOpen, setDeviceValueDialogOpen] = useState(false);
const [showDeviceInfo, setShowDeviceInfo] = useState<boolean>(false);
const [selectedDevice, setSelectedDevice] = useState<number>();
const navigate = useNavigate();
const { data: coreData, send: readCoreData } = useRequest(() => EMSESP.readCoreData(), {
initialData: {
connected: true,
@@ -263,13 +268,16 @@ const DashboardDevices: FC = () => {
}, [escFunction]);
const refreshData = () => {
if (deviceValueDialogOpen) {
return;
if (!deviceValueDialogOpen) {
selectedDevice ? void readDeviceData(selectedDevice) : void readCoreData();
}
if (selectedDevice) {
void readDeviceData(selectedDevice);
};
const customize = () => {
if (selectedDevice == 99) {
navigate('/settings/customentities');
} else {
void readCoreData();
navigate('/settings/customization', { state: selectedDevice });
}
};
@@ -287,48 +295,50 @@ const DashboardDevices: FC = () => {
return sc;
};
const makeCsvData = (columns: any, data: any) =>
data.reduce(
(csvString: any, rowItem: any) =>
csvString + columns.map(({ accessor }: any) => escapeCsvCell(accessor(rowItem))).join(';') + '\r\n',
columns.map(({ name }: any) => escapeCsvCell(name)).join(';') + '\r\n'
);
const downloadAsCsv = (columns: any, data: any, filename: string) => {
const csvData = makeCsvData(columns, data);
const csvFile = new Blob([csvData], { type: 'text/csv;charset:utf-8' });
const downloadLink = document.createElement('a');
downloadLink.download = filename;
downloadLink.href = window.URL.createObjectURL(csvFile);
document.body.appendChild(downloadLink);
downloadLink.click();
document.body.removeChild(downloadLink);
};
const hasMask = (id: string, mask: number) => (parseInt(id.slice(0, 2), 16) & mask) === mask;
const handleDownloadCsv = () => {
const columns = [
{ accessor: (dv: any) => dv.id.slice(2), name: LL.ENTITY_NAME(0) },
{
accessor: (dv: any) => (typeof dv.v === 'number' ? new Intl.NumberFormat().format(dv.v) : dv.v),
name: LL.VALUE(0)
},
{ accessor: (dv: any) => DeviceValueUOM_s[dv.u], name: 'UoM' }
];
const deviceIndex = coreData.devices.findIndex((d) => d.id === device_select.state.id);
if (deviceIndex === -1) {
return;
}
const filename = coreData.devices[deviceIndex].tn + '_' + coreData.devices[deviceIndex].n;
downloadAsCsv(
columns,
onlyFav ? deviceData.data.filter((dv) => hasMask(dv.id, DeviceEntityMask.DV_FAVORITE)) : deviceData.data,
filename
const columns = [
{ accessor: (dv: DeviceValue) => dv.id.slice(2), name: LL.ENTITY_NAME(0) },
{
accessor: (dv: DeviceValue) => (typeof dv.v === 'number' ? new Intl.NumberFormat().format(dv.v) : dv.v),
name: LL.VALUE(1)
},
{ accessor: (dv: DeviceValue) => DeviceValueUOM_s[dv.u].replace(/[^a-zA-Z0-9]/g, ''), name: 'UoM' },
{
accessor: (dv: DeviceValue) => (dv.c && !hasMask(dv.id, DeviceEntityMask.DV_READONLY) ? 'yes' : 'no'),
name: LL.WRITEABLE()
},
{
accessor: (dv: DeviceValue) =>
dv.h ? dv.h : dv.l ? dv.l.join(' | ') : dv.m !== undefined && dv.x !== undefined ? dv.m + ', ' + dv.x : '',
name: 'Range'
}
];
const data = onlyFav
? deviceData.data.filter((dv) => hasMask(dv.id, DeviceEntityMask.DV_FAVORITE))
: deviceData.data;
const csvData = data.reduce(
(csvString: any, rowItem: any) =>
csvString + columns.map(({ accessor }: any) => escapeCsvCell(accessor(rowItem))).join(';') + '\r\n',
columns.map(({ name }: any) => escapeCsvCell(name)).join(';') + '\r\n'
);
const csvFile = new Blob([csvData], { type: 'text/csv;charset:utf-8' });
const downloadLink = document.createElement('a');
downloadLink.download = filename;
downloadLink.href = window.URL.createObjectURL(csvFile);
document.body.appendChild(downloadLink);
downloadLink.click();
document.body.removeChild(downloadLink);
};
useEffect(() => {
@@ -426,7 +436,10 @@ const DashboardDevices: FC = () => {
<Cell stiff>
<DeviceIcon type_id={device.t} />
</Cell>
<Cell>{device.n}</Cell>
<Cell>
{device.n}
<span style={{ color: 'lightblue' }}>&nbsp;&nbsp;({device.e})</span>
</Cell>
<Cell stiff>{device.tn}</Cell>
</Row>
))}
@@ -480,21 +493,31 @@ const DashboardDevices: FC = () => {
right: 16,
bottom: 0,
top: 128,
maxHeight: () => size[1] - 210,
zIndex: 'modal'
zIndex: 'modal',
maxHeight: () => size[1] - 189,
border: '1px solid #177ac9'
}}
>
<Box sx={{ border: '1px solid #177ac9' }}>
<Typography noWrap variant="subtitle1" color="warning.main" sx={{ mx: 1 }}>
{coreData.devices[deviceIndex].n}
<Typography noWrap variant="subtitle1" color="warning.main" sx={{ ml: 1 }}>
{coreData.devices[deviceIndex].tn}&nbsp;&#124;&nbsp;{coreData.devices[deviceIndex].n}
</Typography>
<Grid container justifyContent="space-between">
<Typography sx={{ ml: 1 }} variant="subtitle2" color="primary">
{shown_data.length + ' ' + LL.ENTITIES(shown_data.length)}
{LL.SHOWING() +
' ' +
shown_data.length +
'/' +
coreData.devices[deviceIndex].e +
' ' +
LL.ENTITIES(shown_data.length)}
<IconButton onClick={() => setShowDeviceInfo(true)}>
<InfoOutlinedIcon color="primary" sx={{ fontSize: 18, verticalAlign: 'middle' }} />
</IconButton>
<IconButton onClick={customize}>
<FormatListNumberedIcon color="primary" sx={{ fontSize: 18, verticalAlign: 'middle' }} />
</IconButton>
<IconButton onClick={handleDownloadCsv}>
<DownloadIcon color="primary" sx={{ fontSize: 18, verticalAlign: 'middle' }} />
</IconButton>
@@ -511,7 +534,7 @@ const DashboardDevices: FC = () => {
</Typography>
<Grid item zeroMinWidth justifyContent="flex-end">
<IconButton onClick={resetDeviceSelect}>
<CancelIcon color="info" sx={{ fontSize: 18, verticalAlign: 'middle' }} />
<HighlightOffIcon color="primary" sx={{ fontSize: 18, verticalAlign: 'middle' }} />
</IconButton>
</Grid>
</Grid>

View File

@@ -89,27 +89,16 @@ const DashboardDevicesDialog = ({
}
};
const showHelperText = (dv: DeviceValue) => {
if (dv.h) {
return dv.h;
}
if (dv.l) {
return '[ ' + dv.l.join(' | ') + ' ]';
}
let helperText = '<';
if (dv.s) {
helperText += 'n';
if (dv.m !== undefined && dv.x !== undefined) {
helperText += ' between ' + dv.m + ' and ' + dv.x;
} else {
helperText += ' , step ' + dv.s;
}
} else {
helperText += 'text';
}
return helperText + '>';
};
const showHelperText = (dv: DeviceValue) =>
dv.h ? (
dv.h
) : dv.l ? (
dv.l.join(' | ')
) : dv.m !== undefined && dv.x !== undefined ? (
<>
{dv.m}&nbsp;&rarr;&nbsp;{dv.x}
</>
) : undefined;
return (
<Dialog sx={dialogStyle} open={open} onClose={close}>
@@ -171,7 +160,7 @@ const DashboardDevicesDialog = ({
</Grid>
{writeable && (
<Grid item>
<FormHelperText>format:&nbsp;{showHelperText(editItem)}</FormHelperText>
<FormHelperText>{showHelperText(editItem)}</FormHelperText>
</Grid>
)}
</Grid>

View File

@@ -1,9 +1,9 @@
import { AiOutlineControl, AiOutlineGateway, AiOutlineAlert, AiOutlineChrome } from 'react-icons/ai';
import { AiOutlineControl, AiOutlineGateway, AiOutlineAlert } from 'react-icons/ai';
import { CgSmartHomeBoiler } from 'react-icons/cg';
import { FaSolarPanel } from 'react-icons/fa';
import { GiHeatHaze } from 'react-icons/gi';
import { MdThermostatAuto, MdOutlineSensors, MdOutlineExtension } from 'react-icons/md';
import { MdThermostatAuto, MdOutlineSensors, MdOutlineExtension, MdOutlineDevices } from 'react-icons/md';
import { TiFlowSwitch } from 'react-icons/ti';
import { VscVmConnect } from 'react-icons/vsc';
import { DeviceType } from './types';
@@ -38,8 +38,8 @@ const DeviceIcon: FC<DeviceIconProps> = ({ type_id }) => {
return <VscVmConnect />;
case DeviceType.ALERT:
return <AiOutlineAlert />;
case DeviceType.PUMP:
return <AiOutlineChrome />;
case DeviceType.EXTENSION:
return <MdOutlineDevices />;
case DeviceType.CUSTOM:
return <MdOutlineExtension />;
default:

View File

@@ -1,12 +1,8 @@
import CommentIcon from '@mui/icons-material/CommentTwoTone';
import EastIcon from '@mui/icons-material/East';
import DownloadIcon from '@mui/icons-material/GetApp';
import GitHubIcon from '@mui/icons-material/GitHub';
import MenuBookIcon from '@mui/icons-material/MenuBookTwoTone';
import { Typography, Button, Box, List, ListItem, ListItemText, Link, ListItemAvatar } from '@mui/material';
import { useRequest } from 'alova';
import { toast } from 'react-toastify';
import * as EMSESP from './api';
import { Typography, Box, List, ListItem, ListItemText, Link, ListItemAvatar } from '@mui/material';
import type { FC } from 'react';
import { SectionContent } from 'components';
@@ -16,30 +12,7 @@ import { useI18nContext } from 'i18n/i18n-react';
const HelpInformation: FC = () => {
const { LL } = useI18nContext();
const { send: API, onSuccess: onSuccessAPI } = useRequest((data) => EMSESP.API(data), {
immediate: false
});
onSuccessAPI((event) => {
const a = document.createElement('a');
const filename = 'emsesp_info.txt';
a.href = URL.createObjectURL(
new Blob([JSON.stringify(event.data, null, 2)], {
type: 'text/plain'
})
);
a.setAttribute('download', filename);
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
toast.info(LL.DOWNLOAD_SUCCESSFUL());
});
const callAPI = async () => {
await API({ device: 'system', entity: 'info', id: 0 }).catch((error) => {
toast.error(error.message);
});
};
const uploadURL = window.location.origin + '/system/upload';
return (
<SectionContent title={LL.SUPPORT_INFORMATION()} titleGutter>
@@ -83,17 +56,11 @@ const HelpInformation: FC = () => {
{LL.CLICK_HERE()}
</Link>
<br />
<i>({LL.HELP_INFORMATION_4()}</i>&nbsp;&nbsp;
<Button
startIcon={<DownloadIcon />}
size="small"
variant="outlined"
color="primary"
onClick={() => callAPI()}
>
{LL.SUPPORT_INFO()}
</Button>
&nbsp;)
<i>({LL.HELP_INFORMATION_4()}</i>&nbsp;
<Link href={uploadURL} color="primary">
{LL.UPLOAD()}
</Link>
)
</ListItemText>
</ListItem>
</List>

View File

@@ -23,7 +23,7 @@ import { Table, Header, HeaderRow, HeaderCell, Body, Row, Cell } from '@table-li
import { useTheme } from '@table-library/react-table-library/theme';
import { useRequest } from 'alova';
import { useState, useEffect, useCallback } from 'react';
import { unstable_useBlocker as useBlocker } from 'react-router-dom';
import { unstable_useBlocker as useBlocker, useLocation } from 'react-router-dom';
import { toast } from 'react-toastify';
import EntityMaskToggle from './EntityMaskToggle';
@@ -52,19 +52,23 @@ const SettingsCustomization: FC = () => {
const [restarting, setRestarting] = useState<boolean>(false);
const [restartNeeded, setRestartNeeded] = useState<boolean>(false);
const [deviceEntities, setDeviceEntities] = useState<DeviceEntity[]>([]);
const [selectedDevice, setSelectedDevice] = useState<number>(-1);
const [confirmReset, setConfirmReset] = useState<boolean>(false);
const [selectedFilters, setSelectedFilters] = useState<number>(0);
const [search, setSearch] = useState('');
const [selectedDeviceEntity, setSelectedDeviceEntity] = useState<DeviceEntity>();
const [dialogOpen, setDialogOpen] = useState<boolean>(false);
// fetch devices first
const { data: devices } = useRequest(EMSESP.readDevices);
// const { state } = useLocation();
const [selectedDevice, setSelectedDevice] = useState<number>(useLocation().state || -1);
const [selectedDeviceName, setSelectedDeviceName] = useState<string>('');
const { send: resetCustomizations } = useRequest(EMSESP.resetCustomizations(), {
immediate: false
});
const { data: devices } = useRequest(EMSESP.readDevices);
const { send: writeCustomizationEntities } = useRequest((data) => EMSESP.writeCustomizationEntities(data), {
immediate: false
});
@@ -176,6 +180,22 @@ const SettingsCustomization: FC = () => {
}
}, [deviceEntities]);
useEffect(() => {
if (devices && selectedDevice !== -1) {
void readDeviceEntities(selectedDevice);
const id = devices.devices.findIndex((d) => d.i === selectedDevice);
if (id === -1) {
setSelectedDevice(-1);
setSelectedDeviceName('');
} else {
setSelectedDeviceName(devices.devices[id].tn || '');
setNumChanges(0);
setRestartNeeded(false);
}
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [devices, selectedDevice]);
const restart = async () => {
await restartCommand().catch((error) => {
toast.error(error.message);
@@ -246,16 +266,6 @@ const SettingsCustomization: FC = () => {
);
};
const changeSelectedDevice = (event: React.ChangeEvent<HTMLInputElement>) => {
if (devices) {
const selected_device = parseInt(event.target.value, 10);
setSelectedDevice(selected_device);
setNumChanges(0);
void readDeviceEntities(devices?.devices[selected_device].i);
setRestartNeeded(false);
}
};
const resetCustomization = async () => {
try {
await resetCustomizations();
@@ -314,30 +324,21 @@ const SettingsCustomization: FC = () => {
return;
}
await writeCustomizationEntities({ id: devices?.devices[selectedDevice].i, entity_ids: masked_entities }).catch(
(error) => {
if (error.message === 'Reboot required') {
setRestartNeeded(true);
} else {
toast.error(error.message);
}
await writeCustomizationEntities({ id: selectedDevice, entity_ids: masked_entities }).catch((error) => {
if (error.message === 'Reboot required') {
setRestartNeeded(true);
} else {
toast.error(error.message);
}
);
});
setOriginalSettings(deviceEntities);
}
};
const renderDeviceList = () => (
<>
<Box mb={2} color="warning.main">
<Box mb={1} color="warning.main">
<Typography variant="body2">{LL.CUSTOMIZATIONS_HELP_1()}.</Typography>
<Typography variant="body2" mt={1}>
<OptionIcon type="favorite" isSet={true} />={LL.CUSTOMIZATIONS_HELP_2()}&nbsp;&nbsp;
<OptionIcon type="readonly" isSet={true} />={LL.CUSTOMIZATIONS_HELP_3()}&nbsp;&nbsp;
<OptionIcon type="api_mqtt_exclude" isSet={true} />={LL.CUSTOMIZATIONS_HELP_4()}&nbsp;&nbsp;
<OptionIcon type="web_exclude" isSet={true} />={LL.CUSTOMIZATIONS_HELP_5()}&nbsp;&nbsp;
<OptionIcon type="deleted" isSet={true} />={LL.CUSTOMIZATIONS_HELP_6()}
</Typography>
</Box>
<TextField
name="device"
@@ -346,15 +347,15 @@ const SettingsCustomization: FC = () => {
fullWidth
value={selectedDevice}
disabled={numChanges !== 0}
onChange={changeSelectedDevice}
onChange={(e) => setSelectedDevice(parseInt(e.target.value))}
margin="normal"
select
>
<MenuItem disabled key={-1} value={-1}>
{LL.SELECT_DEVICE()}...
</MenuItem>
{devices.devices.map((device: DeviceShort, index) => (
<MenuItem key={index} value={index}>
{devices.devices.map((device: DeviceShort) => (
<MenuItem key={device.i} value={device.i}>
{device.s}
</MenuItem>
))}
@@ -363,14 +364,19 @@ const SettingsCustomization: FC = () => {
);
const renderDeviceData = () => {
if (deviceEntities.length === 0) {
return;
}
const shown_data = deviceEntities.filter((de) => filter_entity(de));
return (
<>
<Box color="warning.main">
<Typography variant="body2" mt={1}>
<OptionIcon type="favorite" isSet={true} />={LL.CUSTOMIZATIONS_HELP_2()}&nbsp;&nbsp;
<OptionIcon type="readonly" isSet={true} />={LL.CUSTOMIZATIONS_HELP_3()}&nbsp;&nbsp;
<OptionIcon type="api_mqtt_exclude" isSet={true} />={LL.CUSTOMIZATIONS_HELP_4()}&nbsp;&nbsp;
<OptionIcon type="web_exclude" isSet={true} />={LL.CUSTOMIZATIONS_HELP_5()}&nbsp;&nbsp;
<OptionIcon type="deleted" isSet={true} />={LL.CUSTOMIZATIONS_HELP_6()}
</Typography>
</Box>
<Grid container mb={1} mt={0} spacing={1} direction="row" justifyContent="flex-start" alignItems="center">
<Grid item xs={2}>
<TextField
@@ -443,7 +449,7 @@ const SettingsCustomization: FC = () => {
</Grid>
<Grid item>
<Typography variant="subtitle2" color="primary">
{LL.SHOWING()}&nbsp;{shown_data.length}/{deviceEntities.length}
{LL.SHOWING()}&nbsp;{shown_data.length}/{deviceEntities.length}&nbsp;{LL.ENTITIES(deviceEntities.length)}
</Typography>
</Grid>
</Grid>
@@ -467,7 +473,7 @@ const SettingsCustomization: FC = () => {
</Cell>
<Cell>
{formatName(de, false)}&nbsp;(
<Link target="_blank" href={APIURL + devices?.devices[selectedDevice].tn + '/' + de.id}>
<Link target="_blank" href={APIURL + selectedDeviceName + '/' + de.id}>
{de.id}
</Link>
)
@@ -506,7 +512,7 @@ const SettingsCustomization: FC = () => {
{LL.DEVICE_ENTITIES()}
</Typography>
{devices && renderDeviceList()}
{renderDeviceData()}
{deviceEntities && renderDeviceData()}
{restartNeeded && (
<MessageBox my={2} level="warning" message={LL.RESTART_TEXT()}>
<Button startIcon={<PowerSettingsNewIcon />} variant="contained" color="error" onClick={restart}>
@@ -523,7 +529,7 @@ const SettingsCustomization: FC = () => {
startIcon={<CancelIcon />}
variant="outlined"
color="secondary"
onClick={() => devices && readDeviceEntities(devices.devices[selectedDevice].i)}
onClick={() => devices && readDeviceEntities(selectedDevice)}
>
{LL.CANCEL()}
</Button>

View File

@@ -69,6 +69,7 @@ export interface Device {
d: number; // deviceid
p: number; // productid
v: string; // version
e: number; // entities
}
export interface TemperatureSensor {
@@ -174,7 +175,8 @@ export enum DeviceValueUOM {
M3,
L,
KMIN,
K
K,
VOLTS
}
export const DeviceValueUOM_s = [
@@ -200,7 +202,8 @@ export const DeviceValueUOM_s = [
'm³',
'l',
'K*min',
'K'
'K',
'V'
];
export enum AnalogType {
@@ -237,6 +240,7 @@ type BoardProfiles = {
export const BOARD_PROFILES: BoardProfiles = {
S32: 'BBQKees Gateway S32',
E32: 'BBQKees Gateway E32',
E32V2: 'BBQKees Gateway E32 V2',
NODEMCU: 'NodeMCU 32S',
'MH-ET': 'MH-ET Live D1 Mini',
LOLIN: 'Lolin D32',
@@ -360,7 +364,7 @@ export const enum DeviceType {
CONTROLLER,
CONNECT,
ALERT,
PUMP,
EXTENSION,
GENERIC,
HEATSOURCE,
CUSTOM,

View File

@@ -8,8 +8,7 @@ export const GPIO_VALIDATOR = {
if (
value &&
(value === 1 ||
(value >= 6 && value <= 12) ||
(value >= 14 && value <= 15) ||
(value >= 6 && value <= 11) ||
value === 20 ||
value === 24 ||
(value >= 28 && value <= 31) ||

View File

@@ -3,7 +3,7 @@
"target": "ESNext",
"useDefineForClassFields": true,
"lib": ["DOM", "DOM.Iterable", "ESNext"],
"types": ["vite/client", "vite-plugin-svgr/client", "node"],
"types": ["node"],
"allowJs": false,
"skipLibCheck": true,
"esModuleInterop": false,
@@ -26,6 +26,6 @@
"@/*": ["src/*"]
}
},
"include": ["src/**/*", "vite.config.ts", "progmem-generator.js"],
"include": ["src/**/*", "vite.config.ts"],
"exclude": ["node_modules", "dist", "src/**/*.test.tsx", "src/**/*.test.ts"]
}

View File

@@ -1,58 +1,18 @@
import { defineConfig, type PluginOption } from 'vite';
import { defineConfig } from 'vite';
import viteTsconfigPaths from 'vite-tsconfig-paths';
import svgrPlugin from 'vite-plugin-svgr';
import { visualizer } from 'rollup-plugin-visualizer';
import ProgmemGenerator from './progmem-generator';
import preact from '@preact/preset-vite';
import viteImagemin from 'vite-plugin-imagemin';
import { visualizer } from 'rollup-plugin-visualizer';
export default defineConfig(({ command, mode }) => {
if (mode === 'hosted') {
// standalone build for development - runs the server
if (command === 'serve') {
console.log('Preparing for standalone build with server, mode=' + mode);
return {
// hosted, ignore all errors, output to dist
plugins: [preact(), viteTsconfigPaths(), svgrPlugin(), visualizer({ gzipSize: true }) as PluginOption]
};
} else {
// normal build
return {
plugins: [
preact(),
viteTsconfigPaths(),
svgrPlugin(),
ProgmemGenerator({ outputPath: '../lib/framework/WWWData.h', bytesPerLine: 20 })
],
build: {
outDir: 'build',
chunkSizeWarningLimit: 1024,
sourcemap: false,
manifest: false,
minify: mode === 'development' ? false : 'terser',
rollupOptions: {
/**
* Ignore "use client" waning since we are not using SSR
*/
onwarn(warning, warn) {
if (warning.code === 'MODULE_LEVEL_DIRECTIVE' && warning.message.includes(`"use client"`)) {
return;
}
warn(warning);
}
}
},
onwarn(warning, warn) {
if (warning.code === 'MODULE_LEVEL_DIRECTIVE') {
return;
}
warn(warning);
},
plugins: [preact(), viteTsconfigPaths()],
server: {
open: true,
port: 3000,
// watch: {
// usePolling: true
// },
port: mode == 'production' ? 4173 : 3000,
proxy: {
'/rest': 'http://localhost:3080',
'/api': {
@@ -69,4 +29,98 @@ export default defineConfig(({ command, mode }) => {
}
};
}
// production build, both for hosted and building the firmware
if (command === 'build') {
console.log('Preparing for production build, mode is ' + mode);
return {
plugins: [
preact(),
viteTsconfigPaths(),
{
...viteImagemin({
verbose: false,
gifsicle: {
optimizationLevel: 7,
interlaced: false
},
optipng: {
optimizationLevel: 7
},
mozjpeg: {
quality: 20
},
pngquant: {
quality: [0.8, 0.9],
speed: 4
},
svgo: {
plugins: [
{
name: 'removeViewBox'
},
{
name: 'removeEmptyAttrs',
active: false
}
]
}
}),
enforce: 'pre'
},
visualizer({
template: 'treemap', // or sunburst
open: false,
gzipSize: true,
brotliSize: true,
filename: 'analyse.html' // will be saved in project's root
})
],
build: {
// target: 'es2022',
outDir: 'dist',
reportCompressedSize: false,
chunkSizeWarningLimit: 1024,
sourcemap: false,
manifest: false,
minify: 'terser',
terserOptions: {
compress: {
passes: 4,
arrows: true,
drop_console: true,
drop_debugger: true,
sequences: true
},
mangle: {
// toplevel: true
// module: true
// properties: {
// regex: /^_/
// }
},
ecma: 5,
enclose: false,
keep_classnames: false,
keep_fnames: false,
ie8: false,
module: false,
nameCache: null,
safari10: false,
toplevel: false
},
rollupOptions: {
// Ignore "use client" waning since we are not using SSR
onwarn(warning, warn) {
if (warning.code === 'MODULE_LEVEL_DIRECTIVE' && warning.message.includes(`"use client"`)) {
return;
}
warn(warning);
}
}
}
};
}
});

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,9 @@
name=AsyncTCP
version=1.1.1
author=Me-No-Dev
maintainer=Me-No-Dev
sentence=Async TCP Library for ESP32
paragraph=Async TCP Library for ESP32
category=Other
url=https://github.com/me-no-dev/AsyncTCP
architectures=*

View File

@@ -85,7 +85,7 @@ typedef struct {
} lwip_event_packet_t;
static QueueHandle_t _async_queue;
static TaskHandle_t _async_service_task_handle = NULL;
static TaskHandle_t _async_service_task_handle = NULL;
SemaphoreHandle_t _slots_lock;
@@ -103,7 +103,7 @@ static uint32_t _closed_index = []() {
static inline bool _init_async_event_queue() {
if (!_async_queue) {
_async_queue = xQueueCreate(128, sizeof(lwip_event_packet_t *)); // double queue see https://github.com/emsesp/EMS-ESP32/issues/177
_async_queue = xQueueCreate(CONFIG_ASYNC_TCP_QUEUE, sizeof(lwip_event_packet_t *)); // double queue to 128 see https://github.com/emsesp/EMS-ESP32/issues/177
if (!_async_queue) {
return false;
}
@@ -226,7 +226,8 @@ static bool _start_async_task() {
return false;
}
if (!_async_service_task_handle) {
xTaskCreateUniversal(_async_service_task, "async_tcp", 8192 * 2, NULL, 3, &_async_service_task_handle, CONFIG_ASYNC_TCP_RUNNING_CORE);
// xTaskCreateUniversal(_async_service_task, "async_tcp", 8192 * 2, NULL, 3, &_async_service_task_handle, CONFIG_ASYNC_TCP_RUNNING_CORE);
xTaskCreate(_async_service_task, "async_tcp", CONFIG_ASYNC_TCP_STACK, NULL, CONFIG_ASYNC_TCP_TASK_PRIORITY, &_async_service_task_handle);
if (!_async_service_task_handle) {
return false;
}
@@ -1040,9 +1041,12 @@ size_t AsyncClient::write(const char * data) {
size_t AsyncClient::write(const char * data, size_t size, uint8_t apiflags) {
size_t will_send = add(data, size, apiflags);
if (!will_send || !send()) {
if (!will_send) {
return 0;
}
while (connected() && !send()) {
taskYIELD();
}
return will_send;
}
@@ -1080,6 +1084,18 @@ bool AsyncClient::getNoDelay() {
return tcp_nagle_disabled(_pcb);
}
void AsyncClient::setKeepAlive(uint32_t ms, uint8_t cnt) {
if (ms != 0) {
_pcb->so_options |= SOF_KEEPALIVE; //Turn on TCP Keepalive for the given pcb
// Set the time between keepalive messages in milli-seconds
_pcb->keep_idle = ms;
_pcb->keep_intvl = ms;
_pcb->keep_cnt = cnt; //The number of unanswered probes required to force closure of the socket
} else {
_pcb->so_options &= ~SOF_KEEPALIVE; //Turn off TCP Keepalive for the given pcb
}
}
uint16_t AsyncClient::getMss() {
if (!_pcb) {
return 0;

View File

@@ -27,6 +27,7 @@
#include "sdkconfig.h"
#include <functional>
extern "C" {
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include "lwip/pbuf.h"
#include "lwip/ip_addr.h"
@@ -36,7 +37,19 @@ extern "C" {
//If core is not defined, then we are running in Arduino or PIO
#ifndef CONFIG_ASYNC_TCP_RUNNING_CORE
#define CONFIG_ASYNC_TCP_RUNNING_CORE -1 //any available core
#define CONFIG_ASYNC_TCP_USE_WDT 1 //if enabled, adds between 33us and 200us per event
#define CONFIG_ASYNC_TCP_USE_WDT 0 //if enabled, adds between 33us and 200us per event
#endif
#ifndef CONFIG_ASYNC_TCP_TASK_PRIORITY
#define CONFIG_ASYNC_TCP_TASK_PRIORITY 5
#endif
#ifndef CONFIG_ASYNC_TCP_STACK
#define CONFIG_ASYNC_TCP_STACK 8192
#endif
#ifndef CONFIG_ASYNC_TCP_QUEUE
#define CONFIG_ASYNC_TCP_QUEUE 128
#endif
class AsyncClient;
@@ -103,6 +116,8 @@ class AsyncClient {
void setNoDelay(bool nodelay);
bool getNoDelay();
void setKeepAlive(uint32_t ms, uint8_t cnt);
uint32_t getRemoteAddress();
ip6_addr_t getRemoteAddress6();
uint16_t getRemotePort();

View File

@@ -46,6 +46,7 @@ PButton::PButton() {
longPressHappened_ = false; // whether or not the hold event happened already
vLongPressHappened_ = false; // whether or not the long hold event happened already
buttonBusy_ = false; // idle
pin_ = 255; // undefined
}
bool PButton::init(uint8_t pin, bool pullMode) {
@@ -83,7 +84,7 @@ void PButton::onVLongPress(uint16_t t, buttonEventHandler handler) {
}
bool PButton::check(void) {
if (!enabled_) {
if (!enabled_ || pin_ == 255) {
return false;
}

View File

@@ -7,13 +7,43 @@ APSettingsService::APSettingsService(AsyncWebServer * server, FS * fs, SecurityM
, _fsPersistence(APSettings::read, APSettings::update, this, fs, AP_SETTINGS_FILE)
, _dnsServer(nullptr)
, _lastManaged(0)
, _reconfigureAp(false) {
, _reconfigureAp(false)
, _connected(0) {
addUpdateHandler([&](const String & originId) { reconfigureAP(); }, false);
WiFi.onEvent(std::bind(&APSettingsService::WiFiEvent, this, _1));
}
void APSettingsService::begin() {
_fsPersistence.readFromFS();
reconfigureAP();
// disabled for delayed start, first try station mode
// reconfigureAP();
}
// wait 10 sec on STA disconnect before starting AP
void APSettingsService::WiFiEvent(WiFiEvent_t event) {
uint8_t was_connected = _connected;
switch (event) {
case ARDUINO_EVENT_WIFI_STA_DISCONNECTED:
_connected &= ~1;
break;
case ARDUINO_EVENT_ETH_DISCONNECTED:
_connected &= ~2;
break;
case ARDUINO_EVENT_WIFI_STA_GOT_IP:
case ARDUINO_EVENT_WIFI_STA_GOT_IP6:
_connected |= 1;
break;
case ARDUINO_EVENT_ETH_GOT_IP:
case ARDUINO_EVENT_ETH_GOT_IP6:
_connected |= 2;
break;
default:
break;
}
// wait 10 sec before starting AP
if (was_connected && !_connected) {
_lastManaged = uuid::get_uptime();
}
}
void APSettingsService::reconfigureAP() {
@@ -33,13 +63,12 @@ void APSettingsService::loop() {
}
void APSettingsService::manageAP() {
WiFiMode_t currentWiFiMode = WiFi.getMode();
bool network_connected = (emsesp::EMSESP::system_.ethernet_connected() || (WiFi.status() == WL_CONNECTED));
if (_state.provisionMode == AP_MODE_ALWAYS || (_state.provisionMode == AP_MODE_DISCONNECTED && !network_connected)) {
WiFiMode_t currentWiFiMode = WiFi.getMode();
if (_state.provisionMode == AP_MODE_ALWAYS || (_state.provisionMode == AP_MODE_DISCONNECTED && !_connected)) {
if (_reconfigureAp || currentWiFiMode == WIFI_OFF || currentWiFiMode == WIFI_STA) {
startAP();
}
} else if ((currentWiFiMode == WIFI_AP || currentWiFiMode == WIFI_AP_STA) && (_reconfigureAp || !WiFi.softAPgetStationNum())) {
} else if ((currentWiFiMode == WIFI_AP || currentWiFiMode == WIFI_AP_STA) && _connected && (_reconfigureAp || !WiFi.softAPgetStationNum())) {
stopAP();
}
_reconfigureAp = false;

View File

@@ -134,12 +134,14 @@ class APSettingsService : public StatefulService<APSettings> {
// for the management delay loop
volatile unsigned long _lastManaged;
volatile boolean _reconfigureAp;
uint8_t _connected;
void reconfigureAP();
void manageAP();
void startAP();
void stopAP();
void handleDNS();
void WiFiEvent(WiFiEvent_t event);
};
#endif

View File

@@ -112,11 +112,10 @@ class HttpPostEndpoint {
AsyncJsonResponse * response = new AsyncJsonResponse(false, _bufferSize);
jsonObject = response->getRoot().to<JsonObject>();
_statefulService->read(jsonObject, _stateReader);
response->setLength();
if (outcome == StateUpdateResult::CHANGED_RESTART) {
response->setCode(205); // reboot required
}
response->setLength();
request->send(response);
}
};

View File

@@ -154,7 +154,7 @@ void MqttSettingsService::WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info) {
case ARDUINO_EVENT_ETH_GOT_IP:
case ARDUINO_EVENT_ETH_GOT_IP6:
case ARDUINO_EVENT_WIFI_STA_GOT_IP6:
if (_state.enabled) {
if (_state.enabled && !_mqttClient->connected()) {
onConfigUpdated();
}
break;

View File

@@ -27,9 +27,11 @@ void NTPSettingsService::WiFiEvent(WiFiEvent_t event) {
switch (event) {
case ARDUINO_EVENT_WIFI_STA_DISCONNECTED:
case ARDUINO_EVENT_ETH_DISCONNECTED:
emsesp::EMSESP::logger().info("WiFi connection dropped, stopping NTP");
connected_ = false;
configureNTP();
if (connected_) {
emsesp::EMSESP::logger().info("WiFi connection dropped, stopping NTP");
connected_ = false;
configureNTP();
}
break;
case ARDUINO_EVENT_WIFI_STA_GOT_IP:
@@ -82,5 +84,4 @@ void NTPSettingsService::configureTime(AsyncWebServerRequest * request, JsonVari
void NTPSettingsService::ntp_received(struct timeval * tv) {
// emsesp::EMSESP::logger().info("NTP sync to %d sec", tv->tv_sec);
emsesp::EMSESP::system_.ntp_connected(true);
emsesp::EMSESP::system_.send_info_mqtt("connected", true); // send info topic with NTP time
}

View File

@@ -7,6 +7,7 @@ NetworkSettingsService::NetworkSettingsService(AsyncWebServer * server, FS * fs,
, _fsPersistence(NetworkSettings::read, NetworkSettings::update, this, fs, NETWORK_SETTINGS_FILE)
, _lastConnectionAttempt(0) {
addUpdateHandler([&](const String & originId) { reconfigureWiFiConnection(); }, false);
WiFi.onEvent(std::bind(&NetworkSettingsService::WiFiEvent, this, _1));
}
void NetworkSettingsService::begin() {
@@ -22,19 +23,19 @@ void NetworkSettingsService::begin() {
WiFi.mode(WIFI_MODE_MAX);
WiFi.mode(WIFI_MODE_NULL);
WiFi.setScanMethod(WIFI_ALL_CHANNEL_SCAN); // default is FAST_SCAN
WiFi.setSortMethod(WIFI_CONNECT_AP_BY_SIGNAL); // is default, no need to set
// WiFi.setScanMethod(WIFI_ALL_CHANNEL_SCAN); // default is FAST_SCAN, connect issues in 2.0.14
// WiFi.setSortMethod(WIFI_CONNECT_AP_BY_SIGNAL); // is default, no need to set
WiFi.onEvent(std::bind(&NetworkSettingsService::WiFiEvent, this, _1));
_fsPersistence.readFromFS();
reconfigureWiFiConnection();
// reconfigureWiFiConnection();
}
void NetworkSettingsService::reconfigureWiFiConnection() {
// reset last connection attempt to force loop to reconnect immediately
_lastConnectionAttempt = 0;
// do not disconnect for switching to eth, restart is needed
if (WiFi.isConnected() && _state.ssid.length() == 0) {
return;
}
// disconnect and de-configure wifi
if (WiFi.disconnect(true)) {
_stopping = true;
@@ -82,15 +83,18 @@ void NetworkSettingsService::manageSTA() {
} else {
WiFi.begin(_state.ssid.c_str(), _state.password.c_str());
}
// set power after wifi is startet, fixed value for C3_V1
if (WiFi.isConnected()) {
#ifdef BOARD_C3_MINI_V1
// v1 needs this value, see https://github.com/emsesp/EMS-ESP32/pull/620#discussion_r993173979
WiFi.setTxPower(WIFI_POWER_8_5dBm); // https://www.wemos.cc/en/latest/c3/c3_mini_1_0_0.html#about-wifi
// v1 needs this value, see https://github.com/emsesp/EMS-ESP32/pull/620#discussion_r993173979
WiFi.setTxPower(WIFI_POWER_8_5dBm); // https://www.wemos.cc/en/latest/c3/c3_mini_1_0_0.html#about-wifi
#else
// esp_wifi_set_max_tx_power(_state.tx_power * 4);
WiFi.setTxPower((wifi_power_t)(_state.tx_power * 4));
// esp_wifi_set_max_tx_power(_state.tx_power * 4);
WiFi.setTxPower((wifi_power_t)(_state.tx_power * 4));
#endif
}
} else { // not connected but STA-mode active => disconnect
reconfigureWiFiConnection();
}
}
@@ -102,8 +106,7 @@ void NetworkSettingsService::WiFiEvent(WiFiEvent_t event) {
_stopping = false;
}
}
// wait 3 seconds before reconnecting
// if (event == ARDUINO_EVENT_WIFI_STA_DISCONNECTED) {
// _lastConnectionAttempt = millis();
// if (!_stopping && (event == ARDUINO_EVENT_WIFI_STA_LOST_IP || event == ARDUINO_EVENT_WIFI_STA_DISCONNECTED)) {
// reconfigureWiFiConnection();
// }
}

View File

@@ -76,6 +76,7 @@ class NetworkSettings {
static StateUpdateResult update(JsonObject & root, NetworkSettings & settings) {
auto enableCORS = settings.enableCORS;
auto CORSOrigin = settings.CORSOrigin;
auto ssid = settings.ssid;
settings.ssid = root["ssid"] | FACTORY_WIFI_SSID;
settings.bssid = root["bssid"] | "";
settings.password = root["password"] | FACTORY_WIFI_PASSWORD;
@@ -108,7 +109,7 @@ class NetworkSettings {
if (settings.staticIPConfig && (IPUtils::isNotSet(settings.localIP) || IPUtils::isNotSet(settings.gatewayIP) || IPUtils::isNotSet(settings.subnetMask))) {
settings.staticIPConfig = false;
}
if (enableCORS != settings.enableCORS || CORSOrigin != settings.CORSOrigin) {
if (enableCORS != settings.enableCORS || CORSOrigin != settings.CORSOrigin || (ssid != settings.ssid && settings.ssid == "")) {
return StateUpdateResult::CHANGED_RESTART; // tell WebUI that a restart is needed
}

File diff suppressed because one or more lines are too long

View File

@@ -4,4 +4,4 @@ enableGlobalCache: false
nodeLinker: node-modules
yarnPath: .yarn/releases/yarn-4.0.0-rc.53.cjs
yarnPath: .yarn/releases/yarn-4.0.1.cjs

View File

@@ -2,4 +2,4 @@
When developing and testing the web interface, it's handy not to bother with re-flashing an ESP32 each time. The idea is to mimic the ESP using a mock/stub server that responds to the REST (HTTP POST & GET) and WebSocket calls.
To use first make sure you have nodejs installed (>v18) then install yarn (`npm install -g yarn`). Now type `yarn` from this `mock-api` folder. To run EMS-ESP's WebUI navigate up to the `interface` folder and type `yarn run standalone`. This will start the mock API server on port 3080 using static dummy data from `mock-api/server.js` and also the web server which is at <http://localhost:3000>.
To use first make sure you have nodejs installed (>v18) then install yarn (`npm install -g yarn`). Now type `yarn` from this `mock-api` folder. To run EMS-ESP's WebUI navigate up to the `interface` folder and type `yarn standalone`. This will start the mock API server on port 3080 using static dummy data from `mock-api/server.js` and also the web server which is at <http://localhost:3000>.

View File

@@ -1,4 +1,19 @@
{
"name": "mock-api",
"packageManager": "yarn@4.0.0-rc.53"
"name": "api",
"version": "1.0.0",
"description": "mock api for EMS-ESP",
"author": "proddy",
"main": "server.js",
"license": "MIT",
"scripts": {
"dev": "node --watch ./server.js localhost 3080",
"test": "echo \"Error: no test specified\" && exit 1"
},
"dependencies": {
"@msgpack/msgpack": "^2.8.0",
"compression": "^1.7.4",
"express": "^4.18.2",
"multer": "^1.4.5-lts.1"
},
"packageManager": "yarn@4.0.1"
}

View File

@@ -269,6 +269,7 @@ mqtt_settings = {
publish_time_sensor: 10,
publish_time_heartbeat: 60,
mqtt_qos: 0,
rootCA: '',
mqtt_retain: false,
ha_enabled: true,
nested_format: 1,
@@ -426,7 +427,8 @@ const emsesp_coredata = {
// n: 'Enviline/Compress 6000AW/Hybrid 3000-7000iAW/SupraEco/Geo 5xx/WLW196i/WSW196i',
d: 8,
p: 123,
v: '06.01'
v: '06.01',
e: 69
},
{
id: 3,
@@ -436,7 +438,8 @@ const emsesp_coredata = {
n: 'GB125/GB135/MC10',
d: 8,
p: 123,
v: '06.01'
v: '06.01',
e: 73
},
{
id: 1,
@@ -446,7 +449,8 @@ const emsesp_coredata = {
n: 'RC35',
d: 24,
p: 86,
v: '04.01'
v: '04.01',
e: 57
},
{
id: 2,
@@ -456,7 +460,8 @@ const emsesp_coredata = {
n: 'RC20/Moduline 300',
d: 23,
p: 77,
v: '03.03'
v: '03.03',
e: 6
},
{
id: 4,
@@ -466,7 +471,8 @@ const emsesp_coredata = {
n: 'RC100/Moduline 1000/1010',
d: 16,
p: 165,
v: '04.01'
v: '04.01',
e: 3
},
{
id: 5,
@@ -476,7 +482,8 @@ const emsesp_coredata = {
n: 'MM10',
d: 32,
p: 69,
v: '01.01'
v: '01.01',
e: 6
},
{
id: 6,
@@ -486,7 +493,8 @@ const emsesp_coredata = {
n: 'SM10',
d: 48,
p: 73,
v: '01.02'
v: '01.02',
e: 22
},
{
id: 99,
@@ -496,7 +504,8 @@ const emsesp_coredata = {
n: 'User defined entities',
d: 1,
p: 1,
v: ''
v: '',
e: 1
}
]
};
@@ -569,7 +578,7 @@ const emsesp_devicedata_1 = {
c: 'intoffset',
m: -5,
x: 5,
s: '0.1'
s: 0.1
},
{
v: -11,
@@ -578,7 +587,7 @@ const emsesp_devicedata_1 = {
c: 'minexttemp',
m: -30,
x: 0,
s: '1'
s: 1
},
{
v: 29.5,
@@ -658,7 +667,7 @@ const emsesp_devicedata_1 = {
c: 'wwdisinfecthour',
m: 0,
x: 23,
s: '1'
s: 1
},
{
v: 60,
@@ -667,7 +676,7 @@ const emsesp_devicedata_1 = {
c: 'wwmaxtemp',
m: 60,
x: 80,
s: '1'
s: 1
},
{
v: 'on',
@@ -695,14 +704,14 @@ const emsesp_devicedata_1 = {
u: 0,
id: '00dhw holiday dates',
c: 'wwholidays',
h: '< dd.mm.yyyy-dd.mm.yyyy >'
h: 'dd.mm.yyyy-dd.mm.yyyy'
},
{
v: '01.01.2019-12.01.2019',
u: 0,
id: '00dhw vacation dates',
c: 'wwvacations',
h: '< dd.mm.yyyy-dd.mm.yyyy >'
h: 'dd.mm.yyyy-dd.mm.yyyy'
},
{
v: 21,
@@ -711,7 +720,7 @@ const emsesp_devicedata_1 = {
c: 'hc2/seltemp',
m: 0,
x: 30,
s: '0.5'
s: 0.5
},
{
v: 'auto',
@@ -732,7 +741,7 @@ const emsesp_devicedata_1 = {
c: 'hc2/daytemp',
m: 5,
x: 30,
s: '0.5'
s: 0.5
},
{
v: 17,
@@ -741,7 +750,7 @@ const emsesp_devicedata_1 = {
c: 'hc2/nighttemp',
m: 5,
x: 30,
s: '0.5'
s: 0.5
},
{
v: 58,
@@ -750,7 +759,7 @@ const emsesp_devicedata_1 = {
c: 'hc2/designtemp',
m: 30,
x: 90,
s: '1'
s: 1
},
{
v: 0,
@@ -759,7 +768,7 @@ const emsesp_devicedata_1 = {
c: 'hc2/offsettemp',
m: -5,
x: 5,
s: '0.5'
s: 0.5
},
{
v: 15,
@@ -768,7 +777,7 @@ const emsesp_devicedata_1 = {
c: 'hc2/holidaytemp',
m: 5,
x: 30,
s: '0.5'
s: 0.5
},
{
v: 34,
@@ -782,7 +791,7 @@ const emsesp_devicedata_1 = {
c: 'hc2/summertemp',
m: 9,
x: 25,
s: '1'
s: 1
},
{
v: 'winter',
@@ -801,7 +810,7 @@ const emsesp_devicedata_1 = {
c: 'hc2/nofrosttemp',
m: -20,
x: 10,
s: '1'
s: 1
},
{
v: 'outdoor',
@@ -817,7 +826,7 @@ const emsesp_devicedata_1 = {
c: 'hc2/roominfluence',
m: 0,
x: 10,
s: '1'
s: 1
},
{
v: 15,
@@ -826,7 +835,7 @@ const emsesp_devicedata_1 = {
c: 'hc2/minflowtemp',
m: 5,
x: 70,
s: '1'
s: 1
},
{
v: 85,
@@ -835,7 +844,7 @@ const emsesp_devicedata_1 = {
c: 'hc2/maxflowtemp',
m: 30,
x: 90,
s: '1'
s: 1
},
{
v: 0,
@@ -844,7 +853,7 @@ const emsesp_devicedata_1 = {
c: 'hc2/flowtempoffset',
m: 0,
x: 20,
s: '1'
s: 1
},
{
v: 'radiator',
@@ -879,14 +888,14 @@ const emsesp_devicedata_1 = {
u: 0,
id: '00hc2 holiday dates',
c: 'hc2/holidays',
h: '< dd.mm.yyyy-dd.mm.yyyy >'
h: 'dd.mm.yyyy-dd.mm.yyyy'
},
{
v: '01.01.2020-12.01.2020',
u: 0,
id: '00hc2 vacation dates',
c: 'hc2/vacations',
h: '< dd.mm.yyyy-dd.mm.yyyy >'
h: 'dd.mm.yyyy-dd.mm.yyyy'
},
{
v: 'own 1',
@@ -902,7 +911,7 @@ const emsesp_devicedata_1 = {
c: 'hc2/pause',
m: 0,
x: 99,
s: '1'
s: 1
},
{
v: 0,
@@ -911,7 +920,7 @@ const emsesp_devicedata_1 = {
c: 'hc2/party',
m: 0,
x: 99,
s: '1'
s: 1
},
{
v: 0,
@@ -920,7 +929,7 @@ const emsesp_devicedata_1 = {
c: 'hc2/tempautotemp',
m: 0,
x: 30,
s: '0.5'
s: 0.5
},
{
v: -20,
@@ -929,7 +938,7 @@ const emsesp_devicedata_1 = {
c: 'hc2/noreducetemp',
m: -30,
x: 10,
s: '1'
s: 1
},
{
v: 8,
@@ -938,7 +947,7 @@ const emsesp_devicedata_1 = {
c: 'hc2/reducetemp',
m: -20,
x: 10,
s: '1'
s: 1
},
{
v: 5,
@@ -947,7 +956,7 @@ const emsesp_devicedata_1 = {
c: 'hc2/vacreducetemp',
m: -20,
x: 10,
s: '1'
s: 1
},
{
v: 'outdoor',
@@ -1039,7 +1048,7 @@ const emsesp_devicedata_3 = {
c: 'selflowtemp',
m: 0,
x: 90,
s: '1'
s: 1
},
{
v: 30.7,
@@ -1142,7 +1151,7 @@ const emsesp_devicedata_3 = {
c: 'heatingtemp',
m: 0,
x: 90,
s: '1'
s: 1
},
{
v: 100,
@@ -1151,7 +1160,7 @@ const emsesp_devicedata_3 = {
c: 'pumpmodmax',
m: 0,
x: 100,
s: '1'
s: 1
},
{
v: 100,
@@ -1160,7 +1169,7 @@ const emsesp_devicedata_3 = {
c: 'pumpmodmin',
m: 0,
x: 100,
s: '1'
s: 1
},
{
v: 'deltaP-2',
@@ -1176,7 +1185,7 @@ const emsesp_devicedata_3 = {
c: 'pumpdelay',
m: 0,
x: 60,
s: '1'
s: 1
},
{
v: 15,
@@ -1185,7 +1194,7 @@ const emsesp_devicedata_3 = {
c: 'burnminperiod',
m: 0,
x: 120,
s: '1'
s: 1
},
{
v: 0,
@@ -1194,7 +1203,7 @@ const emsesp_devicedata_3 = {
c: 'burnminpower',
m: 0,
x: 100,
s: '1'
s: 1
},
{
v: 100,
@@ -1203,7 +1212,7 @@ const emsesp_devicedata_3 = {
c: 'burnmaxpower',
m: 0,
x: 254,
s: '1'
s: 1
},
{
v: -8,
@@ -1212,7 +1221,7 @@ const emsesp_devicedata_3 = {
c: 'boilhyston',
m: -20,
x: 0,
s: '1'
s: 1
},
{
v: 15,
@@ -1221,7 +1230,7 @@ const emsesp_devicedata_3 = {
c: 'boilhystoff',
m: 0,
x: 20,
s: '1'
s: 1
},
{
v: -8,
@@ -1230,7 +1239,7 @@ const emsesp_devicedata_3 = {
c: 'boil2hyston',
m: -20,
x: 0,
s: '1'
s: 1
},
{
v: 8,
@@ -1239,7 +1248,7 @@ const emsesp_devicedata_3 = {
c: 'boil2hystoff',
m: 0,
x: 20,
s: '1'
s: 1
},
{
v: 34,
@@ -1258,7 +1267,7 @@ const emsesp_devicedata_3 = {
c: 'selburnpow',
m: 0,
x: 254,
s: '1'
s: 1
},
{
v: 0,
@@ -1319,14 +1328,14 @@ const emsesp_devicedata_3 = {
c: 'maintenancetime',
m: 0,
x: 31999,
s: '1'
s: 1
},
{
v: '30.06.2023',
u: 0,
id: '00next maintenance date',
c: 'maintenancedate',
h: '< dd.mm.yyyy >'
h: 'dd.mm.yyyy'
},
{
v: 46,
@@ -1340,7 +1349,7 @@ const emsesp_devicedata_3 = {
c: 'wwseltemp',
m: 0,
x: 254,
s: '1'
s: 1
},
{
v: 'buffer',
@@ -1361,7 +1370,7 @@ const emsesp_devicedata_3 = {
c: 'wwflowtempoffset',
m: 0,
x: 100,
s: '1'
s: 1
},
{
v: 'on',
@@ -1382,7 +1391,7 @@ const emsesp_devicedata_3 = {
c: 'wwhyston',
m: -126,
x: 126,
s: '1'
s: 1
},
{
v: -1,
@@ -1391,7 +1400,7 @@ const emsesp_devicedata_3 = {
c: 'wwhystoff',
m: -126,
x: 126,
s: '1'
s: 1
},
{
v: 70,
@@ -1400,7 +1409,7 @@ const emsesp_devicedata_3 = {
c: 'wwdisinfectiontemp',
m: 0,
x: 254,
s: '1'
s: 1
},
{
v: 'continuous',
@@ -1538,7 +1547,7 @@ const emsesp_devicedata_5 = {
c: 'hc2/flowsettemp',
m: 0,
x: 254,
s: '1'
s: 1
},
{
v: 'off',
@@ -1561,7 +1570,7 @@ const emsesp_devicedata_5 = {
c: 'hc2/valvesettime',
m: 10,
x: 120,
s: '10'
s: 10
}
]
};
@@ -1595,7 +1604,7 @@ const emsesp_devicedata_6 = {
c: 'cylmaxtemp',
m: 0,
x: 254,
s: '1'
s: 1
},
{
v: 'off',
@@ -1619,7 +1628,7 @@ const emsesp_devicedata_6 = {
c: 'pumpminmod',
m: 0,
x: 100,
s: '1'
s: 1
},
{
v: 10,
@@ -1628,7 +1637,7 @@ const emsesp_devicedata_6 = {
c: 'turnondiff',
m: 0,
x: 254,
s: '1'
s: 1
},
{
v: 5,
@@ -1637,7 +1646,7 @@ const emsesp_devicedata_6 = {
c: 'turnoffdiff',
m: 0,
x: 254,
s: '1'
s: 1
},
{
v: 899,
@@ -1656,7 +1665,7 @@ const emsesp_devicedata_6 = {
c: 'maxflow',
m: 0,
x: 25,
s: '0.1'
s: 0.1
},
{
v: 37,
@@ -1665,7 +1674,7 @@ const emsesp_devicedata_6 = {
c: 'wwmintemp',
m: 0,
x: 254,
s: '1'
s: 1
},
{
v: 'on',
@@ -1681,7 +1690,7 @@ const emsesp_devicedata_6 = {
c: 'setting3',
m: 0,
x: 254,
s: '1'
s: 1
},
{
v: 2,
@@ -1690,7 +1699,7 @@ const emsesp_devicedata_6 = {
c: 'setting4',
m: 0,
x: 254,
s: '1'
s: 1
},
{
v: 0,
@@ -1757,7 +1766,7 @@ const emsesp_devicedata_7 = {
{ v: 'H00', u: 0, id: '00maintenance message' },
{ v: 'manual', u: 0, id: '00maintenance scheduled', c: 'maintenance', l: ['off', 'time', 'date', 'manual'] },
{ v: 6000, u: 7, id: '00time to next maintenance', c: 'maintenancetime' },
{ v: '01.01.2012', u: 0, id: '00next maintenance date', c: 'maintenancedate', o: 'Format: < dd.mm.yyyy >' },
{ v: '01.01.2012', u: 0, id: '00next maintenance date', c: 'maintenancedate', h: 'dd.mm.yyyy' },
{ v: 'on', u: 0, id: '00dhw turn on/off', c: 'wwtapactivated', l: ['off', 'on'] },
{ v: 62, u: 1, id: '00dhw set temperature' },
{ v: 60, u: 1, id: '00dhw selected temperature', c: 'wwseltemp' },
@@ -1807,7 +1816,7 @@ const emsesp_devicedata_99 = {
};
// CUSTOM ENTITIES
let emsesp_entities = {
let emsesp_customentities = {
// entities: []
entities: [
{
@@ -1867,10 +1876,10 @@ let emsesp_schedule = {
};
// CUSTOMIZATIONS
const emsesp_deviceentities_1 = [{}];
const emsesp_deviceentities_3 = [{}];
const emsesp_deviceentities_5 = [{}];
const emsesp_deviceentities_6 = [{}];
const emsesp_deviceentities_1 = [{ v: 'dummy value', n: 'dummy name', id: 'dummy', m: 0, w: false }];
const emsesp_deviceentities_3 = [{ v: 'dummy value', n: 'dummy name', id: 'dummy', m: 0, w: false }];
const emsesp_deviceentities_5 = [{ v: 'dummy value', n: 'dummy name', id: 'dummy', m: 0, w: false }];
const emsesp_deviceentities_6 = [{ v: 'dummy value', n: 'dummy name', id: 'dummy', m: 0, w: false }];
const emsesp_deviceentities_2 = [
{
@@ -2153,7 +2162,7 @@ rest_server.post(UPLOAD_FILE_ENDPOINT, progress_middleware, upload.single('file'
console.log(req.file);
console.log('ext: ' + ext);
if (ext === 'bin') {
if (ext === 'bin' || ext === 'json') {
return res.sendStatus(200);
} else if (ext === 'md5') {
return res.json({ md5: 'ef4304fc4d9025a58dcf25d71c882d2c' });
@@ -2196,6 +2205,7 @@ rest_server.get(EMSESP_SENSOR_DATA_ENDPOINT, (req, res) => {
res.json(emsesp_sensordata);
});
rest_server.get(EMSESP_DEVICES_ENDPOINT, (req, res) => {
console.log('send back list of devices...');
res.json(emsesp_devices);
});
rest_server.post(EMSESP_SCANDEVICES_ENDPOINT, (req, res) => {
@@ -2389,7 +2399,7 @@ rest_server.post(EMSESP_WRITE_SCHEDULE_ENDPOINT, (req, res) => {
rest_server.post(EMSESP_WRITE_ENTITIES_ENDPOINT, (req, res) => {
console.log('write entities');
console.log(req.body);
emsesp_entities = req.body;
emsesp_customentities = req.body;
res.sendStatus(200);
});
@@ -2721,7 +2731,7 @@ rest_server.get(GET_CUSTOMIZATIONS_ENDPOINT, (req, res) => {
const GET_ENTITIES_ENDPOINT = REST_ENDPOINT_ROOT + 'getEntities';
rest_server.get(GET_ENTITIES_ENDPOINT, (req, res) => {
console.log('getEntities');
res.json(emsesp_entities);
res.json(emsesp_customentities);
});
const GET_SCHEDULE_ENDPOINT = REST_ENDPOINT_ROOT + 'getSchedule';
@@ -2736,10 +2746,10 @@ rest_server.get(SCHEDULE_ENDPOINT, (req, res) => {
res.json(emsesp_schedule);
});
const ENTITIES_ENDPOINT = REST_ENDPOINT_ROOT + 'entities';
const ENTITIES_ENDPOINT = REST_ENDPOINT_ROOT + 'customentities';
rest_server.get(ENTITIES_ENDPOINT, (req, res) => {
console.log('Sending Custom Entities data');
res.json(emsesp_entities);
res.json(emsesp_customentities);
});
// start server

View File

@@ -5,8 +5,737 @@ __metadata:
version: 8
cacheKey: 10
"mock-api@workspace:.":
"@msgpack/msgpack@npm:^2.8.0":
version: 2.8.0
resolution: "@msgpack/msgpack@npm:2.8.0"
checksum: d90ab780c2c96fa5af22f38e0b76871d7c77d06fcf40786b64ada4e0ae02e17b216b38a5505fb4b7d1c339d95caee0669f5ec9004a2b392ce0cbe16afdbd9333
languageName: node
linkType: hard
"accepts@npm:~1.3.5, accepts@npm:~1.3.8":
version: 1.3.8
resolution: "accepts@npm:1.3.8"
dependencies:
mime-types: "npm:~2.1.34"
negotiator: "npm:0.6.3"
checksum: 67eaaa90e2917c58418e7a9b89392002d2b1ccd69bcca4799135d0c632f3b082f23f4ae4ddeedbced5aa59bcc7bdf4699c69ebed4593696c922462b7bc5744d6
languageName: node
linkType: hard
"api@workspace:.":
version: 0.0.0-use.local
resolution: "mock-api@workspace:."
resolution: "api@workspace:."
dependencies:
"@msgpack/msgpack": "npm:^2.8.0"
compression: "npm:^1.7.4"
express: "npm:^4.18.2"
multer: "npm:^1.4.5-lts.1"
languageName: unknown
linkType: soft
"append-field@npm:^1.0.0":
version: 1.0.0
resolution: "append-field@npm:1.0.0"
checksum: afb50f5ff668af1cb66bc5cfebb55ed9a1d99e24901782ee83d00aed1a499835f9375a149cf27b17f79595ecfcc3d1de0cd5b020b210a5359c43eaf607c217de
languageName: node
linkType: hard
"array-flatten@npm:1.1.1":
version: 1.1.1
resolution: "array-flatten@npm:1.1.1"
checksum: e13c9d247241be82f8b4ec71d035ed7204baa82fae820d4db6948d30d3c4a9f2b3905eb2eec2b937d4aa3565200bd3a1c500480114cff649fa748747d2a50feb
languageName: node
linkType: hard
"body-parser@npm:1.20.1":
version: 1.20.1
resolution: "body-parser@npm:1.20.1"
dependencies:
bytes: "npm:3.1.2"
content-type: "npm:~1.0.4"
debug: "npm:2.6.9"
depd: "npm:2.0.0"
destroy: "npm:1.2.0"
http-errors: "npm:2.0.0"
iconv-lite: "npm:0.4.24"
on-finished: "npm:2.4.1"
qs: "npm:6.11.0"
raw-body: "npm:2.5.1"
type-is: "npm:~1.6.18"
unpipe: "npm:1.0.0"
checksum: 5f8d128022a2fb8b6e7990d30878a0182f300b70e46b3f9d358a9433ad6275f0de46add6d63206da3637c01c3b38b6111a7480f7e7ac2e9f7b989f6133fe5510
languageName: node
linkType: hard
"buffer-from@npm:^1.0.0":
version: 1.1.2
resolution: "buffer-from@npm:1.1.2"
checksum: 0448524a562b37d4d7ed9efd91685a5b77a50672c556ea254ac9a6d30e3403a517d8981f10e565db24e8339413b43c97ca2951f10e399c6125a0d8911f5679bb
languageName: node
linkType: hard
"busboy@npm:^1.0.0":
version: 1.6.0
resolution: "busboy@npm:1.6.0"
dependencies:
streamsearch: "npm:^1.1.0"
checksum: bee10fa10ea58e7e3e7489ffe4bda6eacd540a17de9f9cd21cc37e297b2dd9fe52b2715a5841afaec82900750d810d01d7edb4b2d456427f449b92b417579763
languageName: node
linkType: hard
"bytes@npm:3.0.0":
version: 3.0.0
resolution: "bytes@npm:3.0.0"
checksum: a2b386dd8188849a5325f58eef69c3b73c51801c08ffc6963eddc9be244089ba32d19347caf6d145c86f315ae1b1fc7061a32b0c1aa6379e6a719090287ed101
languageName: node
linkType: hard
"bytes@npm:3.1.2":
version: 3.1.2
resolution: "bytes@npm:3.1.2"
checksum: a10abf2ba70c784471d6b4f58778c0beeb2b5d405148e66affa91f23a9f13d07603d0a0354667310ae1d6dc141474ffd44e2a074be0f6e2254edb8fc21445388
languageName: node
linkType: hard
"call-bind@npm:^1.0.0":
version: 1.0.2
resolution: "call-bind@npm:1.0.2"
dependencies:
function-bind: "npm:^1.1.1"
get-intrinsic: "npm:^1.0.2"
checksum: ca787179c1cbe09e1697b56ad499fd05dc0ae6febe5081d728176ade699ea6b1589240cb1ff1fe11fcf9f61538c1af60ad37e8eb2ceb4ef21cd6085dfd3ccedd
languageName: node
linkType: hard
"compressible@npm:~2.0.16":
version: 2.0.18
resolution: "compressible@npm:2.0.18"
dependencies:
mime-db: "npm:>= 1.43.0 < 2"
checksum: 58321a85b375d39230405654721353f709d0c1442129e9a17081771b816302a012471a9b8f4864c7dbe02eef7f2aaac3c614795197092262e94b409c9be108f0
languageName: node
linkType: hard
"compression@npm:^1.7.4":
version: 1.7.4
resolution: "compression@npm:1.7.4"
dependencies:
accepts: "npm:~1.3.5"
bytes: "npm:3.0.0"
compressible: "npm:~2.0.16"
debug: "npm:2.6.9"
on-headers: "npm:~1.0.2"
safe-buffer: "npm:5.1.2"
vary: "npm:~1.1.2"
checksum: 469cd097908fe1d3ff146596d4c24216ad25eabb565c5456660bdcb3a14c82ebc45c23ce56e19fc642746cf407093b55ab9aa1ac30b06883b27c6c736e6383c2
languageName: node
linkType: hard
"concat-stream@npm:^1.5.2":
version: 1.6.2
resolution: "concat-stream@npm:1.6.2"
dependencies:
buffer-from: "npm:^1.0.0"
inherits: "npm:^2.0.3"
readable-stream: "npm:^2.2.2"
typedarray: "npm:^0.0.6"
checksum: 71db903c84fc073ca35a274074e8d26c4330713d299f8623e993c448c1f6bf8b967806dd1d1a7b0f8add6f15ab1af7435df21fe79b4fe7efd78420c89e054e28
languageName: node
linkType: hard
"content-disposition@npm:0.5.4":
version: 0.5.4
resolution: "content-disposition@npm:0.5.4"
dependencies:
safe-buffer: "npm:5.2.1"
checksum: b7f4ce176e324f19324be69b05bf6f6e411160ac94bc523b782248129eb1ef3be006f6cff431aaea5e337fe5d176ce8830b8c2a1b721626ead8933f0cbe78720
languageName: node
linkType: hard
"content-type@npm:~1.0.4":
version: 1.0.5
resolution: "content-type@npm:1.0.5"
checksum: 585847d98dc7fb8035c02ae2cb76c7a9bd7b25f84c447e5ed55c45c2175e83617c8813871b4ee22f368126af6b2b167df655829007b21aa10302873ea9c62662
languageName: node
linkType: hard
"cookie-signature@npm:1.0.6":
version: 1.0.6
resolution: "cookie-signature@npm:1.0.6"
checksum: f4e1b0a98a27a0e6e66fd7ea4e4e9d8e038f624058371bf4499cfcd8f3980be9a121486995202ba3fca74fbed93a407d6d54d43a43f96fd28d0bd7a06761591a
languageName: node
linkType: hard
"cookie@npm:0.5.0":
version: 0.5.0
resolution: "cookie@npm:0.5.0"
checksum: aae7911ddc5f444a9025fbd979ad1b5d60191011339bce48e555cb83343d0f98b865ff5c4d71fecdfb8555a5cafdc65632f6fce172f32aaf6936830a883a0380
languageName: node
linkType: hard
"core-util-is@npm:~1.0.0":
version: 1.0.3
resolution: "core-util-is@npm:1.0.3"
checksum: 9de8597363a8e9b9952491ebe18167e3b36e7707569eed0ebf14f8bba773611376466ae34575bca8cfe3c767890c859c74056084738f09d4e4a6f902b2ad7d99
languageName: node
linkType: hard
"debug@npm:2.6.9":
version: 2.6.9
resolution: "debug@npm:2.6.9"
dependencies:
ms: "npm:2.0.0"
checksum: e07005f2b40e04f1bd14a3dd20520e9c4f25f60224cb006ce9d6781732c917964e9ec029fc7f1a151083cd929025ad5133814d4dc624a9aaf020effe4914ed14
languageName: node
linkType: hard
"depd@npm:2.0.0":
version: 2.0.0
resolution: "depd@npm:2.0.0"
checksum: c0c8ff36079ce5ada64f46cc9d6fd47ebcf38241105b6e0c98f412e8ad91f084bcf906ff644cc3a4bd876ca27a62accb8b0fff72ea6ed1a414b89d8506f4a5ca
languageName: node
linkType: hard
"destroy@npm:1.2.0":
version: 1.2.0
resolution: "destroy@npm:1.2.0"
checksum: 0acb300b7478a08b92d810ab229d5afe0d2f4399272045ab22affa0d99dbaf12637659411530a6fcd597a9bdac718fc94373a61a95b4651bbc7b83684a565e38
languageName: node
linkType: hard
"ee-first@npm:1.1.1":
version: 1.1.1
resolution: "ee-first@npm:1.1.1"
checksum: 1b4cac778d64ce3b582a7e26b218afe07e207a0f9bfe13cc7395a6d307849cfe361e65033c3251e00c27dd060cab43014c2d6b2647676135e18b77d2d05b3f4f
languageName: node
linkType: hard
"encodeurl@npm:~1.0.2":
version: 1.0.2
resolution: "encodeurl@npm:1.0.2"
checksum: e50e3d508cdd9c4565ba72d2012e65038e5d71bdc9198cb125beb6237b5b1ade6c0d343998da9e170fb2eae52c1bed37d4d6d98a46ea423a0cddbed5ac3f780c
languageName: node
linkType: hard
"escape-html@npm:~1.0.3":
version: 1.0.3
resolution: "escape-html@npm:1.0.3"
checksum: 6213ca9ae00d0ab8bccb6d8d4e0a98e76237b2410302cf7df70aaa6591d509a2a37ce8998008cbecae8fc8ffaadf3fb0229535e6a145f3ce0b211d060decbb24
languageName: node
linkType: hard
"etag@npm:~1.8.1":
version: 1.8.1
resolution: "etag@npm:1.8.1"
checksum: 571aeb3dbe0f2bbd4e4fadbdb44f325fc75335cd5f6f6b6a091e6a06a9f25ed5392f0863c5442acb0646787446e816f13cbfc6edce5b07658541dff573cab1ff
languageName: node
linkType: hard
"express@npm:^4.18.2":
version: 4.18.2
resolution: "express@npm:4.18.2"
dependencies:
accepts: "npm:~1.3.8"
array-flatten: "npm:1.1.1"
body-parser: "npm:1.20.1"
content-disposition: "npm:0.5.4"
content-type: "npm:~1.0.4"
cookie: "npm:0.5.0"
cookie-signature: "npm:1.0.6"
debug: "npm:2.6.9"
depd: "npm:2.0.0"
encodeurl: "npm:~1.0.2"
escape-html: "npm:~1.0.3"
etag: "npm:~1.8.1"
finalhandler: "npm:1.2.0"
fresh: "npm:0.5.2"
http-errors: "npm:2.0.0"
merge-descriptors: "npm:1.0.1"
methods: "npm:~1.1.2"
on-finished: "npm:2.4.1"
parseurl: "npm:~1.3.3"
path-to-regexp: "npm:0.1.7"
proxy-addr: "npm:~2.0.7"
qs: "npm:6.11.0"
range-parser: "npm:~1.2.1"
safe-buffer: "npm:5.2.1"
send: "npm:0.18.0"
serve-static: "npm:1.15.0"
setprototypeof: "npm:1.2.0"
statuses: "npm:2.0.1"
type-is: "npm:~1.6.18"
utils-merge: "npm:1.0.1"
vary: "npm:~1.1.2"
checksum: 869ae89ed6ff4bed7b373079dc58e5dddcf2915a2669b36037ff78c99d675ae930e5fe052b35c24f56557d28a023bb1cbe3e2f2fb87eaab96a1cedd7e597809d
languageName: node
linkType: hard
"finalhandler@npm:1.2.0":
version: 1.2.0
resolution: "finalhandler@npm:1.2.0"
dependencies:
debug: "npm:2.6.9"
encodeurl: "npm:~1.0.2"
escape-html: "npm:~1.0.3"
on-finished: "npm:2.4.1"
parseurl: "npm:~1.3.3"
statuses: "npm:2.0.1"
unpipe: "npm:~1.0.0"
checksum: 635718cb203c6d18e6b48dfbb6c54ccb08ea470e4f474ddcef38c47edcf3227feec316f886dd701235997d8af35240cae49856721ce18f539ad038665ebbf163
languageName: node
linkType: hard
"forwarded@npm:0.2.0":
version: 0.2.0
resolution: "forwarded@npm:0.2.0"
checksum: 29ba9fd347117144e97cbb8852baae5e8b2acb7d1b591ef85695ed96f5b933b1804a7fac4a15dd09ca7ac7d0cdc104410e8102aae2dd3faa570a797ba07adb81
languageName: node
linkType: hard
"fresh@npm:0.5.2":
version: 0.5.2
resolution: "fresh@npm:0.5.2"
checksum: 64c88e489b5d08e2f29664eb3c79c705ff9a8eb15d3e597198ef76546d4ade295897a44abb0abd2700e7ef784b2e3cbf1161e4fbf16f59129193fd1030d16da1
languageName: node
linkType: hard
"function-bind@npm:^1.1.1":
version: 1.1.2
resolution: "function-bind@npm:1.1.2"
checksum: 185e20d20f10c8d661d59aac0f3b63b31132d492e1b11fcc2a93cb2c47257ebaee7407c38513efd2b35cafdf972d9beb2ea4593c1e0f3bf8f2744836928d7454
languageName: node
linkType: hard
"get-intrinsic@npm:^1.0.2":
version: 1.2.1
resolution: "get-intrinsic@npm:1.2.1"
dependencies:
function-bind: "npm:^1.1.1"
has: "npm:^1.0.3"
has-proto: "npm:^1.0.1"
has-symbols: "npm:^1.0.3"
checksum: aee631852063f8ad0d4a374970694b5c17c2fb5c92bd1929476d7eb8798ce7aebafbf9a34022c05fd1adaa2ce846d5877a627ce1986f81fc65adf3b81824bd54
languageName: node
linkType: hard
"has-proto@npm:^1.0.1":
version: 1.0.1
resolution: "has-proto@npm:1.0.1"
checksum: eab2ab0ed1eae6d058b9bbc4c1d99d2751b29717be80d02fd03ead8b62675488de0c7359bc1fdd4b87ef6fd11e796a9631ad4d7452d9324fdada70158c2e5be7
languageName: node
linkType: hard
"has-symbols@npm:^1.0.3":
version: 1.0.3
resolution: "has-symbols@npm:1.0.3"
checksum: 464f97a8202a7690dadd026e6d73b1ceeddd60fe6acfd06151106f050303eaa75855aaa94969df8015c11ff7c505f196114d22f7386b4a471038da5874cf5e9b
languageName: node
linkType: hard
"has@npm:^1.0.3":
version: 1.0.4
resolution: "has@npm:1.0.4"
checksum: c245f332fe78c7b6b8753857240ac12b3286f995f656a33c77e0f5baab7d0157e6ddb1c34940ffd2bffc51f75ede50cd8b29ff65c13e336376aca8cf3df58043
languageName: node
linkType: hard
"http-errors@npm:2.0.0":
version: 2.0.0
resolution: "http-errors@npm:2.0.0"
dependencies:
depd: "npm:2.0.0"
inherits: "npm:2.0.4"
setprototypeof: "npm:1.2.0"
statuses: "npm:2.0.1"
toidentifier: "npm:1.0.1"
checksum: 0e7f76ee8ff8a33e58a3281a469815b893c41357378f408be8f6d4aa7d1efafb0da064625518e7078381b6a92325949b119dc38fcb30bdbc4e3a35f78c44c439
languageName: node
linkType: hard
"iconv-lite@npm:0.4.24":
version: 0.4.24
resolution: "iconv-lite@npm:0.4.24"
dependencies:
safer-buffer: "npm:>= 2.1.2 < 3"
checksum: 6d3a2dac6e5d1fb126d25645c25c3a1209f70cceecc68b8ef51ae0da3cdc078c151fade7524a30b12a3094926336831fca09c666ef55b37e2c69638b5d6bd2e3
languageName: node
linkType: hard
"inherits@npm:2.0.4, inherits@npm:^2.0.3, inherits@npm:~2.0.3":
version: 2.0.4
resolution: "inherits@npm:2.0.4"
checksum: cd45e923bee15186c07fa4c89db0aace24824c482fb887b528304694b2aa6ff8a898da8657046a5dcf3e46cd6db6c61629551f9215f208d7c3f157cf9b290521
languageName: node
linkType: hard
"ipaddr.js@npm:1.9.1":
version: 1.9.1
resolution: "ipaddr.js@npm:1.9.1"
checksum: 864d0cced0c0832700e9621913a6429ccdc67f37c1bd78fb8c6789fff35c9d167cb329134acad2290497a53336813ab4798d2794fd675d5eb33b5fdf0982b9ca
languageName: node
linkType: hard
"isarray@npm:~1.0.0":
version: 1.0.0
resolution: "isarray@npm:1.0.0"
checksum: f032df8e02dce8ec565cf2eb605ea939bdccea528dbcf565cdf92bfa2da9110461159d86a537388ef1acef8815a330642d7885b29010e8f7eac967c9993b65ab
languageName: node
linkType: hard
"media-typer@npm:0.3.0":
version: 0.3.0
resolution: "media-typer@npm:0.3.0"
checksum: 38e0984db39139604756903a01397e29e17dcb04207bb3e081412ce725ab17338ecc47220c1b186b6bbe79a658aad1b0d41142884f5a481f36290cdefbe6aa46
languageName: node
linkType: hard
"merge-descriptors@npm:1.0.1":
version: 1.0.1
resolution: "merge-descriptors@npm:1.0.1"
checksum: 5abc259d2ae25bb06d19ce2b94a21632583c74e2a9109ee1ba7fd147aa7362b380d971e0251069f8b3eb7d48c21ac839e21fa177b335e82c76ec172e30c31a26
languageName: node
linkType: hard
"methods@npm:~1.1.2":
version: 1.1.2
resolution: "methods@npm:1.1.2"
checksum: a385dd974faa34b5dd021b2bbf78c722881bf6f003bfe6d391d7da3ea1ed625d1ff10ddd13c57531f628b3e785be38d3eed10ad03cebd90b76932413df9a1820
languageName: node
linkType: hard
"mime-db@npm:1.52.0, mime-db@npm:>= 1.43.0 < 2":
version: 1.52.0
resolution: "mime-db@npm:1.52.0"
checksum: 54bb60bf39e6f8689f6622784e668a3d7f8bed6b0d886f5c3c446cb3284be28b30bf707ed05d0fe44a036f8469976b2629bbea182684977b084de9da274694d7
languageName: node
linkType: hard
"mime-types@npm:~2.1.24, mime-types@npm:~2.1.34":
version: 2.1.35
resolution: "mime-types@npm:2.1.35"
dependencies:
mime-db: "npm:1.52.0"
checksum: 89aa9651b67644035de2784a6e665fc685d79aba61857e02b9c8758da874a754aed4a9aced9265f5ed1171fd934331e5516b84a7f0218031b6fa0270eca1e51a
languageName: node
linkType: hard
"mime@npm:1.6.0":
version: 1.6.0
resolution: "mime@npm:1.6.0"
bin:
mime: cli.js
checksum: b7d98bb1e006c0e63e2c91b590fe1163b872abf8f7ef224d53dd31499c2197278a6d3d0864c45239b1a93d22feaf6f9477e9fc847eef945838150b8c02d03170
languageName: node
linkType: hard
"minimist@npm:^1.2.6":
version: 1.2.8
resolution: "minimist@npm:1.2.8"
checksum: 908491b6cc15a6c440ba5b22780a0ba89b9810e1aea684e253e43c4e3b8d56ec1dcdd7ea96dde119c29df59c936cde16062159eae4225c691e19c70b432b6e6f
languageName: node
linkType: hard
"mkdirp@npm:^0.5.4":
version: 0.5.6
resolution: "mkdirp@npm:0.5.6"
dependencies:
minimist: "npm:^1.2.6"
bin:
mkdirp: bin/cmd.js
checksum: 0c91b721bb12c3f9af4b77ebf73604baf350e64d80df91754dc509491ae93bf238581e59c7188360cec7cb62fc4100959245a42cfe01834efedc5e9d068376c2
languageName: node
linkType: hard
"ms@npm:2.0.0":
version: 2.0.0
resolution: "ms@npm:2.0.0"
checksum: 0e6a22b8b746d2e0b65a430519934fefd41b6db0682e3477c10f60c76e947c4c0ad06f63ffdf1d78d335f83edee8c0aa928aa66a36c7cd95b69b26f468d527f4
languageName: node
linkType: hard
"ms@npm:2.1.3":
version: 2.1.3
resolution: "ms@npm:2.1.3"
checksum: aa92de608021b242401676e35cfa5aa42dd70cbdc082b916da7fb925c542173e36bce97ea3e804923fe92c0ad991434e4a38327e15a1b5b5f945d66df615ae6d
languageName: node
linkType: hard
"multer@npm:^1.4.5-lts.1":
version: 1.4.5-lts.1
resolution: "multer@npm:1.4.5-lts.1"
dependencies:
append-field: "npm:^1.0.0"
busboy: "npm:^1.0.0"
concat-stream: "npm:^1.5.2"
mkdirp: "npm:^0.5.4"
object-assign: "npm:^4.1.1"
type-is: "npm:^1.6.4"
xtend: "npm:^4.0.0"
checksum: 957c09956f3b7f79d8586cac5e2a50e9a5c3011eb841667b5e4590c5f31d9464f5b46aecd399c83e183a15b88b019cccf0e4fa5620db40bf16b9e3af7fab3ac6
languageName: node
linkType: hard
"negotiator@npm:0.6.3":
version: 0.6.3
resolution: "negotiator@npm:0.6.3"
checksum: 2723fb822a17ad55c93a588a4bc44d53b22855bf4be5499916ca0cab1e7165409d0b288ba2577d7b029f10ce18cf2ed8e703e5af31c984e1e2304277ef979837
languageName: node
linkType: hard
"object-assign@npm:^4.1.1":
version: 4.1.1
resolution: "object-assign@npm:4.1.1"
checksum: fcc6e4ea8c7fe48abfbb552578b1c53e0d194086e2e6bbbf59e0a536381a292f39943c6e9628af05b5528aa5e3318bb30d6b2e53cadaf5b8fe9e12c4b69af23f
languageName: node
linkType: hard
"object-inspect@npm:^1.9.0":
version: 1.13.0
resolution: "object-inspect@npm:1.13.0"
checksum: d64609c3738a916d4c6d8306436427bfab87d5a68c017306aee9e8ebf39c9c08e6619f8397e520a64ff9b8545a2dcee9f4704157020700e324ccd179bd5ef931
languageName: node
linkType: hard
"on-finished@npm:2.4.1":
version: 2.4.1
resolution: "on-finished@npm:2.4.1"
dependencies:
ee-first: "npm:1.1.1"
checksum: 8e81472c5028125c8c39044ac4ab8ba51a7cdc19a9fbd4710f5d524a74c6d8c9ded4dd0eed83f28d3d33ac1d7a6a439ba948ccb765ac6ce87f30450a26bfe2ea
languageName: node
linkType: hard
"on-headers@npm:~1.0.2":
version: 1.0.2
resolution: "on-headers@npm:1.0.2"
checksum: 870766c16345855e2012e9422ba1ab110c7e44ad5891a67790f84610bd70a72b67fdd71baf497295f1d1bf38dd4c92248f825d48729c53c0eae5262fb69fa171
languageName: node
linkType: hard
"parseurl@npm:~1.3.3":
version: 1.3.3
resolution: "parseurl@npm:1.3.3"
checksum: 407cee8e0a3a4c5cd472559bca8b6a45b82c124e9a4703302326e9ab60fc1081442ada4e02628efef1eb16197ddc7f8822f5a91fd7d7c86b51f530aedb17dfa2
languageName: node
linkType: hard
"path-to-regexp@npm:0.1.7":
version: 0.1.7
resolution: "path-to-regexp@npm:0.1.7"
checksum: 701c99e1f08e3400bea4d701cf6f03517474bb1b608da71c78b1eb261415b645c5670dfae49808c89e12cea2dccd113b069f040a80de012da0400191c6dbd1c8
languageName: node
linkType: hard
"process-nextick-args@npm:~2.0.0":
version: 2.0.1
resolution: "process-nextick-args@npm:2.0.1"
checksum: 1d38588e520dab7cea67cbbe2efdd86a10cc7a074c09657635e34f035277b59fbb57d09d8638346bf7090f8e8ebc070c96fa5fd183b777fff4f5edff5e9466cf
languageName: node
linkType: hard
"proxy-addr@npm:~2.0.7":
version: 2.0.7
resolution: "proxy-addr@npm:2.0.7"
dependencies:
forwarded: "npm:0.2.0"
ipaddr.js: "npm:1.9.1"
checksum: f24a0c80af0e75d31e3451398670d73406ec642914da11a2965b80b1898ca6f66a0e3e091a11a4327079b2b268795f6fa06691923fef91887215c3d0e8ea3f68
languageName: node
linkType: hard
"qs@npm:6.11.0":
version: 6.11.0
resolution: "qs@npm:6.11.0"
dependencies:
side-channel: "npm:^1.0.4"
checksum: 5a3bfea3e2f359ede1bfa5d2f0dbe54001aa55e40e27dc3e60fab814362d83a9b30758db057c2011b6f53a2d4e4e5150194b5bac45372652aecb3e3c0d4b256e
languageName: node
linkType: hard
"range-parser@npm:~1.2.1":
version: 1.2.1
resolution: "range-parser@npm:1.2.1"
checksum: ce21ef2a2dd40506893157970dc76e835c78cf56437e26e19189c48d5291e7279314477b06ac38abd6a401b661a6840f7b03bd0b1249da9b691deeaa15872c26
languageName: node
linkType: hard
"raw-body@npm:2.5.1":
version: 2.5.1
resolution: "raw-body@npm:2.5.1"
dependencies:
bytes: "npm:3.1.2"
http-errors: "npm:2.0.0"
iconv-lite: "npm:0.4.24"
unpipe: "npm:1.0.0"
checksum: 280bedc12db3490ecd06f740bdcf66093a07535374b51331242382c0e130bb273ebb611b7bc4cba1b4b4e016cc7b1f4b05a6df885a6af39c2bc3b94c02291c84
languageName: node
linkType: hard
"readable-stream@npm:^2.2.2":
version: 2.3.8
resolution: "readable-stream@npm:2.3.8"
dependencies:
core-util-is: "npm:~1.0.0"
inherits: "npm:~2.0.3"
isarray: "npm:~1.0.0"
process-nextick-args: "npm:~2.0.0"
safe-buffer: "npm:~5.1.1"
string_decoder: "npm:~1.1.1"
util-deprecate: "npm:~1.0.1"
checksum: 8500dd3a90e391d6c5d889256d50ec6026c059fadee98ae9aa9b86757d60ac46fff24fafb7a39fa41d54cb39d8be56cc77be202ebd4cd8ffcf4cb226cbaa40d4
languageName: node
linkType: hard
"safe-buffer@npm:5.1.2, safe-buffer@npm:~5.1.0, safe-buffer@npm:~5.1.1":
version: 5.1.2
resolution: "safe-buffer@npm:5.1.2"
checksum: 7eb5b48f2ed9a594a4795677d5a150faa7eb54483b2318b568dc0c4fc94092a6cce5be02c7288a0500a156282f5276d5688bce7259299568d1053b2150ef374a
languageName: node
linkType: hard
"safe-buffer@npm:5.2.1":
version: 5.2.1
resolution: "safe-buffer@npm:5.2.1"
checksum: 32872cd0ff68a3ddade7a7617b8f4c2ae8764d8b7d884c651b74457967a9e0e886267d3ecc781220629c44a865167b61c375d2da6c720c840ecd73f45d5d9451
languageName: node
linkType: hard
"safer-buffer@npm:>= 2.1.2 < 3":
version: 2.1.2
resolution: "safer-buffer@npm:2.1.2"
checksum: 7eaf7a0cf37cc27b42fb3ef6a9b1df6e93a1c6d98c6c6702b02fe262d5fcbd89db63320793b99b21cb5348097d0a53de81bd5f4e8b86e20cc9412e3f1cfb4e83
languageName: node
linkType: hard
"send@npm:0.18.0":
version: 0.18.0
resolution: "send@npm:0.18.0"
dependencies:
debug: "npm:2.6.9"
depd: "npm:2.0.0"
destroy: "npm:1.2.0"
encodeurl: "npm:~1.0.2"
escape-html: "npm:~1.0.3"
etag: "npm:~1.8.1"
fresh: "npm:0.5.2"
http-errors: "npm:2.0.0"
mime: "npm:1.6.0"
ms: "npm:2.1.3"
on-finished: "npm:2.4.1"
range-parser: "npm:~1.2.1"
statuses: "npm:2.0.1"
checksum: ec66c0ad109680ad8141d507677cfd8b4e40b9559de23191871803ed241718e99026faa46c398dcfb9250676076573bd6bfe5d0ec347f88f4b7b8533d1d391cb
languageName: node
linkType: hard
"serve-static@npm:1.15.0":
version: 1.15.0
resolution: "serve-static@npm:1.15.0"
dependencies:
encodeurl: "npm:~1.0.2"
escape-html: "npm:~1.0.3"
parseurl: "npm:~1.3.3"
send: "npm:0.18.0"
checksum: 699b2d4c29807a51d9b5e0f24955346911437aebb0178b3c4833ad30d3eca93385ff9927254f5c16da345903cad39d9cd4a532198c95a5129cc4ed43911b15a4
languageName: node
linkType: hard
"setprototypeof@npm:1.2.0":
version: 1.2.0
resolution: "setprototypeof@npm:1.2.0"
checksum: fde1630422502fbbc19e6844346778f99d449986b2f9cdcceb8326730d2f3d9964dbcb03c02aaadaefffecd0f2c063315ebea8b3ad895914bf1afc1747fc172e
languageName: node
linkType: hard
"side-channel@npm:^1.0.4":
version: 1.0.4
resolution: "side-channel@npm:1.0.4"
dependencies:
call-bind: "npm:^1.0.0"
get-intrinsic: "npm:^1.0.2"
object-inspect: "npm:^1.9.0"
checksum: c4998d9fc530b0e75a7fd791ad868fdc42846f072734f9080ff55cc8dc7d3899abcda24fd896aa6648c3ab7021b4bb478073eb4f44dfd55bce9714bc1a7c5d45
languageName: node
linkType: hard
"statuses@npm:2.0.1":
version: 2.0.1
resolution: "statuses@npm:2.0.1"
checksum: 18c7623fdb8f646fb213ca4051be4df7efb3484d4ab662937ca6fbef7ced9b9e12842709872eb3020cc3504b93bde88935c9f6417489627a7786f24f8031cbcb
languageName: node
linkType: hard
"streamsearch@npm:^1.1.0":
version: 1.1.0
resolution: "streamsearch@npm:1.1.0"
checksum: 612c2b2a7dbcc859f74597112f80a42cbe4d448d03da790d5b7b39673c1197dd3789e91cd67210353e58857395d32c1e955a9041c4e6d5bae723436b3ed9ed14
languageName: node
linkType: hard
"string_decoder@npm:~1.1.1":
version: 1.1.1
resolution: "string_decoder@npm:1.1.1"
dependencies:
safe-buffer: "npm:~5.1.0"
checksum: 7c41c17ed4dea105231f6df208002ebddd732e8e9e2d619d133cecd8e0087ddfd9587d2feb3c8caf3213cbd841ada6d057f5142cae68a4e62d3540778d9819b4
languageName: node
linkType: hard
"toidentifier@npm:1.0.1":
version: 1.0.1
resolution: "toidentifier@npm:1.0.1"
checksum: 952c29e2a85d7123239b5cfdd889a0dde47ab0497f0913d70588f19c53f7e0b5327c95f4651e413c74b785147f9637b17410ac8c846d5d4a20a5a33eb6dc3a45
languageName: node
linkType: hard
"type-is@npm:^1.6.4, type-is@npm:~1.6.18":
version: 1.6.18
resolution: "type-is@npm:1.6.18"
dependencies:
media-typer: "npm:0.3.0"
mime-types: "npm:~2.1.24"
checksum: 0bd9eeae5efd27d98fd63519f999908c009e148039d8e7179a074f105362d4fcc214c38b24f6cda79c87e563cbd12083a4691381ed28559220d4a10c2047bed4
languageName: node
linkType: hard
"typedarray@npm:^0.0.6":
version: 0.0.6
resolution: "typedarray@npm:0.0.6"
checksum: 2cc1bcf7d8c1237f6a16c04efc06637b2c5f2d74e58e84665445cf87668b85a21ab18dd751fa49eee6ae024b70326635d7b79ad37b1c370ed2fec6aeeeb52714
languageName: node
linkType: hard
"unpipe@npm:1.0.0, unpipe@npm:~1.0.0":
version: 1.0.0
resolution: "unpipe@npm:1.0.0"
checksum: 4fa18d8d8d977c55cb09715385c203197105e10a6d220087ec819f50cb68870f02942244f1017565484237f1f8c5d3cd413631b1ae104d3096f24fdfde1b4aa2
languageName: node
linkType: hard
"util-deprecate@npm:~1.0.1":
version: 1.0.2
resolution: "util-deprecate@npm:1.0.2"
checksum: 474acf1146cb2701fe3b074892217553dfcf9a031280919ba1b8d651a068c9b15d863b7303cb15bd00a862b498e6cf4ad7b4a08fb134edd5a6f7641681cb54a2
languageName: node
linkType: hard
"utils-merge@npm:1.0.1":
version: 1.0.1
resolution: "utils-merge@npm:1.0.1"
checksum: 5d6949693d58cb2e636a84f3ee1c6e7b2f9c16cb1d42d0ecb386d8c025c69e327205aa1c69e2868cc06a01e5e20681fbba55a4e0ed0cce913d60334024eae798
languageName: node
linkType: hard
"vary@npm:~1.1.2":
version: 1.1.2
resolution: "vary@npm:1.1.2"
checksum: 31389debef15a480849b8331b220782230b9815a8e0dbb7b9a8369559aed2e9a7800cd904d4371ea74f4c3527db456dc8e7ac5befce5f0d289014dbdf47b2242
languageName: node
linkType: hard
"xtend@npm:^4.0.0":
version: 4.0.2
resolution: "xtend@npm:4.0.2"
checksum: ac5dfa738b21f6e7f0dd6e65e1b3155036d68104e67e5d5d1bde74892e327d7e5636a076f625599dc394330a731861e87343ff184b0047fef1360a7ec0a5a36a
languageName: node
linkType: hard

View File

@@ -33,7 +33,7 @@ build_flags =
-D NO_GLOBAL_ARDUINOOTA
-D ARDUINOJSON_ENABLE_STD_STRING=1
-D ARDUINOJSON_USE_DOUBLE=0
-D CONFIG_UART_ISR_IN_IRAM
; -D CONFIG_UART_ISR_IN_IRAM
unbuild_flags =
${common.core_unbuild_flags}
@@ -67,7 +67,7 @@ monitor_raw = yes
upload_speed = 921600
build_type = release
lib_ldf_mode = chain+
board_build.flash_mode = qio
; board_build.flash_mode = qio
check_tool = cppcheck, clangtidy
check_severity = high, medium
@@ -82,6 +82,7 @@ extends = espressi32_base_tasmota
extra_scripts = scripts/rename_fw.py
board = esp32dev
board_build.partitions = esp32_partition_4M.csv
board_build.extra_flags = -DBOARD_HAS_PSRAM
build_flags = ${common.build_flags}
build_unflags = ${common.unbuild_flags}
@@ -116,6 +117,7 @@ build_flags = ${common.build_flags}
[env:esp32_16M]
extends = espressi32_base_tasmota
board = esp32dev
board_build.extra_flags = -DBOARD_HAS_PSRAM
board_upload.flash_size = 16MB
board_build.partitions = esp32_partition_16M.csv
build_flags = ${common.build_flags}
@@ -190,4 +192,4 @@ build_src_filter =
+<../lib/espMqttClient/src>
+<../lib/espMqttClient/src/Transport>
lib_compat_mode = off
lib_ldf_mode = off
lib_ldf_mode = off

View File

@@ -1,6 +1,7 @@
# testing EMS-ESP API
# use with "REST Client" extension in Visual Studio Code
# https://marketplace.visualstudio.com/items?itemName=humao.rest-client
# to be used with "REST Client" extension in Visual Studio Code (https://marketplace.visualstudio.com/items?itemName=humao.rest-client)
# Open this file in VSC, modify the token, go to the API call and click on 'Send Request' (or Ctrl+Alt+R)
# The response will be shown in the right panel
@host = http://ems-esp.local
@host_dev = http://ems-esp2.local

View File

@@ -1,61 +1,25 @@
from pathlib import Path
from shutil import copytree, rmtree, copyfileobj
import os
import gzip
# brotli has better compression than gzip but requires https so leaving here for future
# import brotli
Import("env")
def gzipFile(file):
with open(file, 'rb') as f_in:
with gzip.open(file + '.gz', 'wb') as f_out:
copyfileobj(f_in, f_out)
os.remove(file)
# brotli version:
# with open(file + '.br', 'wb') as f_out:
# with open(file, 'rb') as f_in:
# f_out.write(brotli.compress(f_in.read(), quality=11))
# os.remove(file)
def flagExists(flag):
buildFlags = env.ParseFlags(env["BUILD_FLAGS"])
for define in buildFlags.get("CPPDEFINES"):
if (define == flag or (isinstance(define, list) and define[0] == flag)):
return True
def buildWeb():
os.chdir("interface")
print("Building web interface...")
try:
env.Execute("yarn")
env.Execute("yarn run typesafe-i18n --no-watch")
env.Execute("yarn typesafe-i18n --no-watch")
with open("./src/i18n/i18n-util.ts") as r:
text = r.read().replace("Locales = 'pl'", "Locales = 'en'")
with open("./src/i18n/i18n-util.ts", "w") as w:
w.write(text)
print("Setting locale to 'en'")
env.Execute("yarn run build")
buildPath = Path("build")
wwwPath = Path("../data/www")
if wwwPath.exists() and wwwPath.is_dir():
rmtree(wwwPath)
print("Copying web files from build to data/www...")
copytree(buildPath, wwwPath)
for currentpath, folders, files in os.walk(wwwPath):
for file in files:
gzipFile(os.path.join(currentpath, file))
print("Setting WebUI locale to 'en'")
env.Execute("yarn build")
env.Execute("yarn webUI")
finally:
os.chdir("..")
if not (env.IsCleanTarget()):
buildWeb()

View File

@@ -317,12 +317,13 @@ uint8_t Command::call(const uint8_t device_type, const char * cmd, const char *
// check if we have a matching command
if (!cf) {
// we didn't find the command, report error
LOG_DEBUG("Command failed: invalid command '%s'", cmd ? cmd : "");
LOG_WARNING("Command failed: invalid command '%s'", cmd ? cmd : "");
return message(CommandRet::NOT_FOUND, "invalid command", output);
}
// check permissions and abort if not authorized
if (cf->has_flags(CommandFlag::ADMIN_ONLY) && !is_admin) {
LOG_WARNING("Command failed: authentication failed");
output["message"] = "authentication failed";
return CommandRet::NOT_ALLOWED; // command not allowed
}
@@ -338,12 +339,12 @@ uint8_t Command::call(const uint8_t device_type, const char * cmd, const char *
snprintf(info_s, sizeof(info_s), "'%s/%s'", dname, cmd);
}
if ((value == nullptr) || (strlen(value) == 0)) {
LOG_WARNING(("%sCalling command %s"), ro.c_str(), info_s);
LOG_DEBUG(("%sCalling command %s"), ro.c_str(), info_s);
} else {
if (id > 0) {
LOG_WARNING(("%sCalling command %s with value %s and id %d on device 0x%02X"), ro.c_str(), info_s, value, id, device_id);
LOG_INFO(("%sCalling command %s with value %s and id %d on device 0x%02X"), ro.c_str(), info_s, value, id, device_id);
} else {
LOG_WARNING(("%sCalling command %s with value %s"), ro.c_str(), info_s, value);
LOG_INFO(("%sCalling command %s with value %s"), ro.c_str(), info_s, value);
}
}
@@ -362,6 +363,11 @@ uint8_t Command::call(const uint8_t device_type, const char * cmd, const char *
// report back. If not OK show output from error, other return the HTTP code
if (return_code != CommandRet::OK) {
if (value == nullptr) {
LOG_ERROR("Command '%s' failed with code: %d", cmd, return_code);
} else {
LOG_ERROR("Command '%s:%s' failed with code: %d", cmd, value, return_code);
}
return message(return_code, "callback function failed", output);
}
return return_code;

View File

@@ -279,14 +279,17 @@ static void setup_commands(std::shared_ptr<Commands> & commands) {
commands->add_command(ShellContext::MAIN,
CommandFlags::ADMIN,
string_vector{F_(set), F_(board_profile)},
string_vector{F_(name_mandatory)},
string_vector{F_(name_mandatory), F_(nvs_optional)},
[](Shell & shell, const std::vector<std::string> & arguments) {
std::vector<int8_t> data; // led, dallas, rx, tx, button, phy_type, eth_power, eth_phy_addr, eth_clock_mode
std::string board_profile = Helpers::toUpper(arguments.front());
if (!to_app(shell).system_.load_board_profile(data, board_profile)) {
shell.println("Invalid board profile (S32, E32, MH-ET, NODEMCU, OLIMEX, OLIMEXPOE, C3MINI, S2MINI, S3MINI, CUSTOM)");
shell.println("Invalid board profile (S32, E32, E32V2, MH-ET, NODEMCU, OLIMEX, OLIMEXPOE, C3MINI, S2MINI, S3MINI, CUSTOM)");
return;
}
if (arguments.size() == 2 && Helpers::toLower(arguments.back()) == "nvs") {
to_app(shell).nvs_.putString("boot", board_profile.c_str());
}
to_app(shell).webSettingsService.update(
[&](WebSettings & settings) {
settings.board_profile = board_profile.c_str();
@@ -360,7 +363,7 @@ static void setup_commands(std::shared_ptr<Commands> & commands) {
commands->add_command(ShellContext::MAIN,
CommandFlags::ADMIN,
string_vector{F_(scan), F_(devices)},
string_vector{F_(scan)},
string_vector{F_(deep_optional)},
[](Shell & shell, const std::vector<std::string> & arguments) {
if (arguments.size() == 0) {
@@ -368,29 +371,11 @@ static void setup_commands(std::shared_ptr<Commands> & commands) {
} else {
shell.printfln("Performing a deep scan...");
to_app(shell).clear_all_devices();
std::vector<uint8_t> Device_Ids;
Device_Ids.push_back(0x08); // Boilers - 0x08
Device_Ids.push_back(0x38); // HeatPump - 0x38
Device_Ids.push_back(0x30); // Solar Module - 0x30
Device_Ids.push_back(0x09); // Controllers - 0x09
Device_Ids.push_back(0x02); // Connect - 0x02
Device_Ids.push_back(0x48); // Gateway - 0x48
Device_Ids.push_back(0x20); // Mixer Devices - 0x20
Device_Ids.push_back(0x21); // Mixer Devices - 0x21
Device_Ids.push_back(0x22); // Mixer Devices - 0x22
Device_Ids.push_back(0x23); // Mixer Devices - 0x23
Device_Ids.push_back(0x28); // Mixer Devices WW- 0x28
Device_Ids.push_back(0x29); // Mixer Devices WW- 0x29
Device_Ids.push_back(0x10); // Thermostats - 0x10
Device_Ids.push_back(0x17); // Thermostats - 0x17
Device_Ids.push_back(0x18); // Thermostat remote - 0x18
Device_Ids.push_back(0x19); // Thermostat remote - 0x19
Device_Ids.push_back(0x1A); // Thermostat remote - 0x1A
Device_Ids.push_back(0x1B); // Thermostat remote - 0x1B
Device_Ids.push_back(0x11); // Switches - 0x11
// device IDs taken from device_library.h
// send the read command with Version command
const std::vector<uint8_t> Device_Ids = {0x02, 0x08, 0x09, 0x10, 0x11, 0x12, 0x15, 0x17, 0x18, 0x19, 0x1A,
0x1B, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29,
0x2A, 0x30, 0x38, 0x40, 0x41, 0x48, 0x50, 0x51, 0x60};
for (const uint8_t device_id : Device_Ids) {
to_app(shell).send_read_request(EMSdevice::EMS_TYPE_VERSION, device_id);
}

View File

@@ -24,6 +24,7 @@
*/
// Boilers - 0x08
{ 8, DeviceType::BOILER, "CS6800i/WLW176i", DeviceFlags::EMS_DEVICE_FLAG_HEATPUMP},
{ 64, DeviceType::BOILER, "BK13/BK15/Smartline/GB1x2", DeviceFlags::EMS_DEVICE_FLAG_NONE},
{ 72, DeviceType::BOILER, "GB125/GB135/MC10", DeviceFlags::EMS_DEVICE_FLAG_EMS},
{ 81, DeviceType::BOILER, "Cascade CM10", DeviceFlags::EMS_DEVICE_FLAG_NONE},
@@ -48,7 +49,7 @@
{208, DeviceType::BOILER, "Logamax Plus/GB192/Condens GC9000/Greenstar ErP", DeviceFlags::EMS_DEVICE_FLAG_NONE},
{210, DeviceType::BOILER, "Cascade MC400", DeviceFlags::EMS_DEVICE_FLAG_NONE},
{211, DeviceType::BOILER, "EasyControl Adapter", DeviceFlags::EMS_DEVICE_FLAG_NONE},
{219, DeviceType::BOILER, "Greenstar HIU", DeviceFlags::EMS_DEVICE_FLAG_HIU},
{219, DeviceType::BOILER, "Greenstar HIU/Logamax kompakt WS170", DeviceFlags::EMS_DEVICE_FLAG_HIU},
{234, DeviceType::BOILER, "Logamax Plus GB122/Condense 2300", DeviceFlags::EMS_DEVICE_FLAG_NONE},
// Controllers - 0x09 / 0x10 / 0x50
@@ -78,7 +79,7 @@
// Thermostat - not currently supporting write operations, like the Easy/100 types - 0x18
{202, DeviceType::THERMOSTAT, "Logamatic TC100/Moduline Easy", DeviceFlags::EMS_DEVICE_FLAG_EASY | DeviceFlags::EMS_DEVICE_FLAG_NO_WRITE}, // 0x18, cannot write
{203, DeviceType::THERMOSTAT, "EasyControl CT200", DeviceFlags::EMS_DEVICE_FLAG_EASY | DeviceFlags::EMS_DEVICE_FLAG_NO_WRITE}, // 0x18, cannot write
{203, DeviceType::THERMOSTAT, "EasyControl/CT200", DeviceFlags::EMS_DEVICE_FLAG_EASY | DeviceFlags::EMS_DEVICE_FLAG_NO_WRITE}, // 0x18, cannot write
// Thermostat - Buderus/Nefit/Bosch specific - 0x17 / 0x10 / 0x18 / 0x19-0x1B for hc2-4 / 0x38
{ 4, DeviceType::THERMOSTAT, "UI800/BC400", DeviceFlags::EMS_DEVICE_FLAG_RC300}, // 0x10
@@ -100,7 +101,7 @@
{215, DeviceType::THERMOSTAT, "Comfort RF", DeviceFlags::EMS_DEVICE_FLAG_CRF | DeviceFlags::EMS_DEVICE_FLAG_NO_WRITE}, // 0x18
{216, DeviceType::THERMOSTAT, "CRF200S", DeviceFlags::EMS_DEVICE_FLAG_CRF | DeviceFlags::EMS_DEVICE_FLAG_NO_WRITE}, // 0x18
{246, DeviceType::THERMOSTAT, "Comfort+2RF", DeviceFlags::EMS_DEVICE_FLAG_CRF | DeviceFlags::EMS_DEVICE_FLAG_NO_WRITE}, // 0x18
{253, DeviceType::THERMOSTAT, "Rego 3000/UI800", DeviceFlags::EMS_DEVICE_FLAG_RC300}, // 0x10
{253, DeviceType::THERMOSTAT, "Rego 3000/UI800/WSW196i/BC400", DeviceFlags::EMS_DEVICE_FLAG_RC300}, // 0x10
// Thermostat - Sieger - 0x10 / 0x17
{ 66, DeviceType::THERMOSTAT, "ES72/RC20", DeviceFlags::EMS_DEVICE_FLAG_RC20_N}, // 0x17 or remote
@@ -168,8 +169,8 @@
// Switches - 0x11
{ 71, DeviceType::SWITCH, "WM10", DeviceFlags::EMS_DEVICE_FLAG_NONE},
// PM10 Pump module - 0x15
{ 243, DeviceType::PUMP, "PM10", DeviceFlags::EMS_DEVICE_FLAG_NONE},
// EM10/100 extension module, pump module - 0x15
{ 243, DeviceType::EXTENSION, "EM10/EM100", DeviceFlags::EMS_DEVICE_FLAG_NONE},
// EM10 error contact and analog flowtemp control- 0x12
{ 74, DeviceType::ALERT, "EM10", DeviceFlags::EMS_DEVICE_FLAG_NONE},

View File

@@ -24,6 +24,19 @@ REGISTER_FACTORY(Alert, EMSdevice::DeviceType::ALERT);
Alert::Alert(uint8_t device_type, uint8_t device_id, uint8_t product_id, const char * version, const char * name, uint8_t flags, uint8_t brand)
: EMSdevice(device_type, device_id, product_id, version, name, flags, brand) {
}
// EM10, device-id 0x12, listens to error messages, measures voltage input and send 0x1A to the boiler.
// values already shown in boiler
// see https://github.com/emsesp/EMS-ESP32/issues/575
register_telegram_type(0x1A, "UBASetPoints", false, MAKE_PF_CB(process_UBASetPoints));
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &setFlowTemp_, DeviceValueType::UINT, FL_(setFlowTemp), DeviceValueUOM::DEGREES);
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &setBurnPow_, DeviceValueType::UINT, FL_(setBurnPow), DeviceValueUOM::PERCENT);
}
// UBASetPoint 0x1A
void Alert::process_UBASetPoints(std::shared_ptr<const Telegram> telegram) {
has_update(telegram, setFlowTemp_, 0); // boiler set temp from thermostat
has_update(telegram, setBurnPow_, 1); // max burner power in %
}
} // namespace emsesp

View File

@@ -26,6 +26,12 @@ namespace emsesp {
class Alert : public EMSdevice {
public:
Alert(uint8_t device_type, uint8_t device_id, uint8_t product_id, const char * version, const char * name, uint8_t flags, uint8_t brand);
private:
void process_UBASetPoints(std::shared_ptr<const Telegram> telegram);
uint8_t setFlowTemp_; // boiler setpoint temp
uint8_t setBurnPow_; // Burner power %
};
} // namespace emsesp

View File

@@ -84,12 +84,34 @@ Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const
register_telegram_type(0x499, "HPDhwSettings", true, MAKE_PF_CB(process_HpDhwSettings));
register_telegram_type(0x49C, "HPSettings2", true, MAKE_PF_CB(process_HpSettings2));
register_telegram_type(0x49D, "HPSettings3", true, MAKE_PF_CB(process_HpSettings3));
register_telegram_type(0x4AE, "HPEnergy", true, MAKE_PF_CB(process_HpEnergy));
register_telegram_type(0x4AF, "HPMeters", true, MAKE_PF_CB(process_HpMeters));
}
if (model() == EMSdevice::EMS_DEVICE_FLAG_HIU) {
register_telegram_type(0x772, "HIUSettings", false, MAKE_PF_CB(process_HIUSettings));
register_telegram_type(0x779, "HIUMonitor", false, MAKE_PF_CB(process_HIUMonitor));
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA,
&netFlowTemp_,
DeviceValueType::USHORT,
DeviceValueNumOp::DV_NUMOP_DIV10,
FL_(netFlowTemp),
DeviceValueUOM::DEGREES);
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA,
&retTemp_,
DeviceValueType::USHORT,
DeviceValueNumOp::DV_NUMOP_DIV10,
FL_(retTemp),
DeviceValueUOM::DEGREES);
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &heatValve_, DeviceValueType::UINT, FL_(heatValve), DeviceValueUOM::PERCENT);
register_device_value(DeviceValueTAG::TAG_BOILER_DATA_WW, &wwValve_, DeviceValueType::UINT, FL_(wwValve), DeviceValueUOM::PERCENT);
register_device_value(DeviceValueTAG::TAG_BOILER_DATA_WW,
&wwCurFlow_,
DeviceValueType::UINT,
DeviceValueNumOp::DV_NUMOP_DIV10,
FL_(wwCurFlow),
DeviceValueUOM::LMIN);
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA,
&keepWarmTemp_,
DeviceValueType::UINT,
@@ -102,8 +124,6 @@ Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const
FL_(setReturnTemp),
DeviceValueUOM::DEGREES,
MAKE_CF_CB(set_returnTemp));
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &cwFlowRate_, DeviceValueType::USHORT, FL_(cwFlowRate), DeviceValueUOM::LMIN);
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &netFlowTemp_, DeviceValueType::USHORT, FL_(netFlowTemp), DeviceValueUOM::DEGREES);
}
/*
@@ -127,7 +147,8 @@ Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const
MAKE_CF_CB(set_forceHeatingOff));
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &heatingActive_, DeviceValueType::BOOL, FL_(heatingActive), DeviceValueUOM::NONE);
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &tapwaterActive_, DeviceValueType::BOOL, FL_(tapwaterActive), DeviceValueUOM::NONE);
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &selFlowTemp_, DeviceValueType::UINT, FL_(selFlowTemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_flow_temp));
register_device_value(
DeviceValueTAG::TAG_DEVICE_DATA, &selFlowTemp_, DeviceValueType::UINT, FL_(selFlowTemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_flow_temp), 0, 90);
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &heatingPumpMod_, DeviceValueType::UINT, FL_(heatingPumpMod), DeviceValueUOM::PERCENT);
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA,
&outdoorTemp_,
@@ -186,23 +207,22 @@ Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const
FL_(heatingActivated),
DeviceValueUOM::NONE,
MAKE_CF_CB(set_heating_activated));
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA,
&heatingTemp_,
DeviceValueType::UINT,
FL_(heatingTemp),
DeviceValueUOM::DEGREES,
MAKE_CF_CB(set_heating_temp));
register_device_value(
DeviceValueTAG::TAG_DEVICE_DATA, &heatingTemp_, DeviceValueType::UINT, FL_(heatingTemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_heating_temp), 0, 90);
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &pumpModMax_, DeviceValueType::UINT, FL_(pumpModMax), DeviceValueUOM::PERCENT, MAKE_CF_CB(set_max_pump));
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &pumpModMin_, DeviceValueType::UINT, FL_(pumpModMin), DeviceValueUOM::PERCENT, MAKE_CF_CB(set_min_pump));
register_device_value(
DeviceValueTAG::TAG_DEVICE_DATA, &pumpMode_, DeviceValueType::ENUM, FL_(enum_pumpMode), FL_(pumpMode), DeviceValueUOM::NONE, MAKE_CF_CB(set_pumpMode));
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &pumpDelay_, DeviceValueType::UINT, FL_(pumpDelay), DeviceValueUOM::MINUTES, MAKE_CF_CB(set_pump_delay));
register_device_value(
DeviceValueTAG::TAG_DEVICE_DATA, &pumpDelay_, DeviceValueType::UINT, FL_(pumpDelay), DeviceValueUOM::MINUTES, MAKE_CF_CB(set_pump_delay), 0, 60);
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA,
&burnMinPeriod_,
DeviceValueType::UINT,
FL_(burnMinPeriod),
DeviceValueUOM::MINUTES,
MAKE_CF_CB(set_burn_period));
MAKE_CF_CB(set_burn_period),
0,
120);
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA,
&burnMinPower_,
DeviceValueType::UINT,
@@ -211,8 +231,10 @@ Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const
MAKE_CF_CB(set_min_power));
register_device_value(
DeviceValueTAG::TAG_DEVICE_DATA, &burnMaxPower_, DeviceValueType::UINT, FL_(burnMaxPower), DeviceValueUOM::PERCENT, MAKE_CF_CB(set_max_power), 0, 254);
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &boilHystOn_, DeviceValueType::INT, FL_(boilHystOn), DeviceValueUOM::DEGREES_R, MAKE_CF_CB(set_hyst_on));
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &boilHystOff_, DeviceValueType::INT, FL_(boilHystOff), DeviceValueUOM::DEGREES_R, MAKE_CF_CB(set_hyst_off));
register_device_value(
DeviceValueTAG::TAG_DEVICE_DATA, &boilHystOn_, DeviceValueType::INT, FL_(boilHystOn), DeviceValueUOM::DEGREES_R, MAKE_CF_CB(set_hyst_on), -20, 0);
register_device_value(
DeviceValueTAG::TAG_DEVICE_DATA, &boilHystOff_, DeviceValueType::INT, FL_(boilHystOff), DeviceValueUOM::DEGREES_R, MAKE_CF_CB(set_hyst_off), 0, 20);
register_device_value(
DeviceValueTAG::TAG_DEVICE_DATA, &boil2HystOn_, DeviceValueType::INT, FL_(boil2HystOn), DeviceValueUOM::DEGREES_R, MAKE_CF_CB(set_hyst2_on), -20, 0);
register_device_value(
@@ -267,7 +289,6 @@ Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const
MAKE_CF_CB(set_emergency_temp),
15,
70);
/*
* Hybrid heatpump with telegram 0xBB is readable and writeable in boiler and thermostat
* thermostat always overwrites settings in boiler
@@ -341,6 +362,37 @@ Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const
// heatpump info
if (model() == EMS_DEVICE_FLAG_HEATPUMP) {
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA,
&nrgTotal_,
DeviceValueType::ULONG,
DeviceValueNumOp::DV_NUMOP_DIV100,
FL_(nrgTotal),
DeviceValueUOM::KWH);
register_device_value(DeviceValueTAG::TAG_BOILER_DATA_WW, &nrgWw_, DeviceValueType::ULONG, DeviceValueNumOp::DV_NUMOP_DIV100, FL_(nrgWw), DeviceValueUOM::KWH);
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA,
&nrgHeat_,
DeviceValueType::ULONG,
DeviceValueNumOp::DV_NUMOP_DIV100,
FL_(nrgHeat),
DeviceValueUOM::KWH);
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA,
&meterTotal_,
DeviceValueType::ULONG,
DeviceValueNumOp::DV_NUMOP_DIV100,
FL_(meterTotal),
DeviceValueUOM::KWH);
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA,
&meterComp_,
DeviceValueType::ULONG,
DeviceValueNumOp::DV_NUMOP_DIV100,
FL_(meterComp),
DeviceValueUOM::KWH);
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA,
&meterEHeat_,
DeviceValueType::ULONG,
DeviceValueNumOp::DV_NUMOP_DIV100,
FL_(meterEHeat),
DeviceValueUOM::KWH);
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA,
&upTimeControl_,
DeviceValueType::TIME,
@@ -430,6 +482,8 @@ Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &hpTl2_, DeviceValueType::SHORT, DeviceValueNumOp::DV_NUMOP_DIV10, FL_(hpTl2), DeviceValueUOM::DEGREES);
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &hpPl1_, DeviceValueType::SHORT, DeviceValueNumOp::DV_NUMOP_DIV10, FL_(hpPl1), DeviceValueUOM::DEGREES);
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &hpPh1_, DeviceValueType::SHORT, DeviceValueNumOp::DV_NUMOP_DIV10, FL_(hpPh1), DeviceValueUOM::DEGREES);
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &hpTa4_, DeviceValueType::SHORT, DeviceValueNumOp::DV_NUMOP_DIV10, FL_(hpTa4), DeviceValueUOM::DEGREES);
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &hpTw1_, DeviceValueType::SHORT, DeviceValueNumOp::DV_NUMOP_DIV10, FL_(hpTw1), DeviceValueUOM::DEGREES);
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA,
&poolSetTemp_,
DeviceValueType::UINT,
@@ -787,7 +841,9 @@ Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const
DeviceValueType::UINT,
FL_(wwDisinfectionTemp),
DeviceValueUOM::DEGREES,
MAKE_CF_CB(set_ww_disinfect_temp));
MAKE_CF_CB(set_ww_disinfect_temp),
60,
80);
register_device_value(DeviceValueTAG::TAG_BOILER_DATA_WW,
&wwCircMode_,
DeviceValueType::ENUM,
@@ -871,6 +927,12 @@ Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const
if (model() != EMS_DEVICE_FLAG_HEATPUMP) {
register_telegram_type(0x04, "UBAFactory", true, MAKE_PF_CB(process_UBAFactory));
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &nomPower_, DeviceValueType::UINT, FL_(nomPower), DeviceValueUOM::KW, MAKE_CF_CB(set_nomPower));
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA,
&nrgTotal_,
DeviceValueType::ULONG,
DeviceValueNumOp::DV_NUMOP_DIV100,
FL_(nrgTotal),
DeviceValueUOM::KWH);
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA,
&nrgHeat_,
DeviceValueType::ULONG,
@@ -965,13 +1027,22 @@ void Boiler::check_active() {
tapwaterActive_ = val;
char s[12];
Mqtt::queue_publish(F_(tapwater_active), Helpers::render_boolean(s, b));
// if (flowsensor) {
EMSESP::tap_water_active(b); // let EMS-ESP know, used in the Shower class
// }
}
// check forceheatingoff option
if (!Helpers::hasValue(forceHeatingOff_, EMS_VALUE_BOOL)) {
EMSESP::webSettingsService.read([&](WebSettings & settings) { forceHeatingOff_ = (settings.boiler_heatingoff || selFlowTemp_ == 0) ? 1 : 0; });
EMSESP::webSettingsService.read([&](WebSettings & settings) { forceHeatingOff_ = settings.boiler_heatingoff ? EMS_VALUE_BOOL_ON : 0; });
has_update(&forceHeatingOff_);
}
static uint32_t lastSendHeatingOff = 0;
if (forceHeatingOff_ == EMS_VALUE_BOOL_ON && (uuid::get_uptime_sec() - lastSendHeatingOff) >= 60) {
lastSendHeatingOff = uuid::get_uptime_sec();
uint8_t data[] = {0, 0, 0, 0};
write_command(EMS_TYPE_UBASetPoints, 0, data, sizeof(data), 0);
}
// calculate energy for boiler 0x08 from stored modulation an time in units of 0.01 Wh
if (model() != EMS_DEVICE_FLAG_HEATPUMP) {
@@ -985,6 +1056,7 @@ void Boiler::check_active() {
nrgWwF_ += ((double)((uint32_t)wwBurnPow * nomPower_ * (uuid::get_uptime() - powLastReadTime_)) / 3600) / 1000UL;
has_update(nrgHeat_, (uint32_t)(nrgHeatF_ + 0.5));
has_update(nrgWw_, (uint32_t)(nrgWwF_ + 0.5));
has_update(nrgTotal_, (uint32_t)(nrgHeatF_ + nrgWwF_ + 0.5));
// check for store values
time_t now = time(nullptr);
tm * tm_ = localtime(&now);
@@ -995,7 +1067,7 @@ void Boiler::check_active() {
store_energy();
}
// store new modulation and time
heatBurnPow = heatingActive_ ? curBurnPow_ : 0;
heatBurnPow = heatingActive_ && !tapwaterActive_ ? curBurnPow_ : 0;
wwBurnPow = tapwaterActive_ ? curBurnPow_ : 0;
powLastReadTime_ = uuid::get_uptime();
}
@@ -1030,8 +1102,7 @@ void Boiler::process_UBAMonitorFast(std::shared_ptr<const Telegram> telegram) {
has_update(telegram, curFlowTemp_, 1);
has_update(telegram, selBurnPow_, 3); // burn power max setting
has_update(telegram, curBurnPow_, 4);
has_update(telegram, boilerState_, 5);
has_update(telegram, boilerState_, 5); // bits 0-heat, 1-dhw, 2-service, 3-flame, 4-preheat, 5-lock-Err, 6-block-err, 7-maint
has_bitupdate(telegram, burnGas_, 7, 0);
has_bitupdate(telegram, burnGas2_, 7, 1);
has_bitupdate(telegram, fanWork_, 7, 2);
@@ -1075,6 +1146,10 @@ void Boiler::process_UBAMonitorFast(std::shared_ptr<const Telegram> telegram) {
*/
void Boiler::process_UBATotalUptime(std::shared_ptr<const Telegram> telegram) {
has_update(telegram, UBAuptime_, 0, 3); // force to 3 bytes
// if broadcasted there is no need to fetch
if (telegram->dest == 0) {
toggle_fetch(0x14, false);
}
}
/*
@@ -1099,7 +1174,7 @@ void Boiler::process_UBAParameters(std::shared_ptr<const Telegram> telegram) {
}
/*
* UBASettingsWW - type 0x26 - max power on offset 7, #740
* UBASettingsWW - type 0x26 - max power on offset 7, https://github.com/emsesp/EMS-ESP/issues/740
* Boiler(0x08) -> Me(0x0B), ?(0x26), data: 01 05 00 0F 00 1E 58 5A
*/
void Boiler::process_UBASettingsWW(std::shared_ptr<const Telegram> telegram) {
@@ -1162,7 +1237,7 @@ void Boiler::process_UBAMonitorWW(std::shared_ptr<const Telegram> telegram) {
/*
* UBAMonitorFastPlus - type 0xE4 - central heating monitor EMS+
* temperatures at 7 and 23 always identical
+ * Bosch Logamax Plus GB122: issue #620
+ * Bosch Logamax Plus GB122: https://github.com/emsesp/EMS-ESP/issues/620
+ * 88 00 E4 00 00 2D 2D 00 00 C9 34 02 21 64 3D 05 02 01 DE 00 00 00 00 03 62 14 00 02 21 00 00 00 00 00 00 00 2B 2B 83
+ * GB125/Logamatic MC110: issue #650: add retTemp & sysPress
+ * 08 00 E4 00 10 20 2D 48 00 C8 38 02 37 3C 27 03 00 00 00 00 00 01 7B 01 8F 11 00 02 37 80 00 02 1B 80 00 7F FF 80 00
@@ -1176,14 +1251,24 @@ void Boiler::process_UBAMonitorFastPlus(std::shared_ptr<const Telegram> telegram
has_update(telegram, selBurnPow_, 9);
has_update(telegram, curFlowTemp_, 7);
has_update(telegram, flameCurr_, 19);
has_update(telegram, retTemp_, 17); // can be 0 if no sensor, handled in export_values
has_update(telegram, sysPress_, 21);
uint16_t rettemp = retTemp_;
telegram->read_value(rettemp, 17); // 0 means no sensor
if (rettemp == 0) {
rettemp = EMS_VALUE_USHORT_NOTSET;
}
has_update(retTemp_, rettemp);
uint8_t syspress = sysPress_;
telegram->read_value(syspress, 21); // 0 means no sensor
if (syspress == 0) {
sysPress_ = EMS_VALUE_UINT_NOTSET;
}
has_update(sysPress_, syspress);
//has_update(telegram, temperatur_, 13); // unknown temperature
has_update(telegram, heatblock_, 23); // see #1317
has_update(telegram, headertemp_, 25); // see #1317
//has_update(telegram, temperatur_, 27); // unknown temperature
has_update(telegram, exhaustTemp_, 31);
// read 3 char service code / installation status as appears on the display
@@ -1230,11 +1315,6 @@ void Boiler::process_UBAMonitorSlow(std::shared_ptr<const Telegram> telegram) {
has_update(telegram, burn2WorkMin_, 16, 3); // force to 3 bytes
has_update(telegram, heatWorkMin_, 19, 3); // force to 3 bytes
has_update(telegram, heatStarts_, 22, 3); // force to 3 bytes
if (forceHeatingOff_ == EMS_VALUE_BOOL_ON && telegram->dest == 0) {
uint8_t data[] = {0, 0, 0, 0};
write_command(EMS_TYPE_UBASetPoints, 0, data, sizeof(data), 0);
}
}
/*
@@ -1257,19 +1337,14 @@ void Boiler::process_UBAMonitorSlowPlus(std::shared_ptr<const Telegram> telegram
has_bitupdate(telegram, ignWork_, 2, 3);
has_bitupdate(telegram, heatingPump_, 2, 5);
has_bitupdate(telegram, wwCirc_, 2, 7);
// has_update(telegram, exhaustTemp_, 6); // Disabled until verified as valid location, see #1147.
// temperature measurements at offset 4 unknown, see https://github.com/emsesp/EMS-ESP/issues/620
// exhaustTemp was offset 6, now in e4. See #1147, #1150, #1326
has_update(telegram, burnStarts_, 10, 3); // force to 3 bytes
has_update(telegram, burnWorkMin_, 13, 3); // force to 3 bytes
has_update(telegram, burn2WorkMin_, 16, 3); // force to 3 bytes
has_update(telegram, heatWorkMin_, 19, 3); // force to 3 bytes
has_update(telegram, heatStarts_, 22, 3); // force to 3 bytes
has_update(telegram, heatingPumpMod_, 25);
// temperature measurements at 4, see #620
if (forceHeatingOff_ == EMS_VALUE_BOOL_ON && telegram->dest == 0) {
uint8_t data[] = {0, 0, 0, 0};
write_command(EMS_TYPE_UBASetPoints, 0, data, sizeof(data), 0);
}
}
/*
@@ -1433,6 +1508,8 @@ void Boiler::process_HpTemperatures(std::shared_ptr<const Telegram> telegram) {
has_update(telegram, hpTr6_, 0);
has_update(telegram, hpTr7_, 30);
has_update(telegram, hpTl2_, 12);
has_update(telegram, hpTa4_, 22);
has_update(telegram, hpTw1_, 24);
has_update(telegram, hpPl1_, 26);
has_update(telegram, hpPh1_, 28);
has_update(telegram, hpBrineIn_, 8);
@@ -1521,7 +1598,7 @@ void Boiler::process_UBASetPoints(std::shared_ptr<const Telegram> telegram) {
has_update(telegram, wwSetPumpPower_, 2); // ww pump speed/power?
// overwrite other settings on receive?
if (forceHeatingOff_ == EMS_VALUE_BOOL_ON && telegram->dest == 0x08) {
if (forceHeatingOff_ == EMS_VALUE_BOOL_ON && telegram->dest == 0x08 && (setFlowTemp_ + setBurnPow_ + wwSetPumpPower_) != 0) {
uint8_t data[] = {0, 0, 0, 0};
write_command(EMS_TYPE_UBASetPoints, 0, data, sizeof(data), 0);
}
@@ -1562,45 +1639,46 @@ void Boiler::process_UBAErrorMessage(std::shared_ptr<const Telegram> telegram) {
return;
}
// data: displaycode(2), errornumber(2), year, month, hour, day, minute, duration(2), src-addr
if (telegram->message_data[4] & 0x80) { // valid date
static uint32_t lastCodeDate_ = 0; // last code date
char code[3] = {0};
uint16_t codeNo = EMS_VALUE_SHORT_NOTSET;
code[0] = telegram->message_data[0];
code[1] = telegram->message_data[1];
code[2] = 0;
telegram->read_value(codeNo, 2);
uint16_t year = (telegram->message_data[4] & 0x7F) + 2000;
uint8_t month = telegram->message_data[5];
uint8_t day = telegram->message_data[7];
uint8_t hour = telegram->message_data[6];
uint8_t min = telegram->message_data[8];
uint32_t date = (year - 2000) * 535680UL + month * 44640UL + day * 1440UL + hour * 60 + min;
uint16_t duration = EMS_VALUE_SHORT_NOTSET;
telegram->read_value(duration, 9);
// store only the newest code from telegrams 10 and 11
if (date > lastCodeDate_) {
lastCodeDate_ = date;
snprintf(lastCode_, sizeof(lastCode_), "%s(%d) %02d.%02d.%d %02d:%02d (%d min)", code, codeNo, day, month, year, hour, min, duration);
has_update(lastCode_);
}
static uint32_t lastCodeDate_ = 0; // last code date
char code[3] = {0};
uint16_t codeNo = EMS_VALUE_SHORT_NOTSET;
code[0] = telegram->message_data[0];
code[1] = telegram->message_data[1];
code[2] = 0;
telegram->read_value(codeNo, 2);
uint16_t year = (telegram->message_data[4] & 0x7F) + 2000;
uint8_t month = telegram->message_data[5];
uint8_t day = telegram->message_data[7];
uint8_t hour = telegram->message_data[6];
uint8_t min = telegram->message_data[8];
uint16_t duration = telegram->message_data[9];
uint32_t date = (year - 2000) * 535680UL + month * 44640UL + day * 1440UL + hour * 60 + min + duration;
// store only the newest code from telegrams 10 and 11
if (date > lastCodeDate_ && lastCodeDate_) {
lastCodeDate_ = date;
snprintf(lastCode_, sizeof(lastCode_), "%s(%d) %02d.%02d.%d %02d:%02d (%d min)", code, codeNo, day, month, year, hour, min, duration);
has_update(lastCode_);
} else if (!lastCodeDate_) { // no publish for first read
lastCodeDate_ = 1;
}
}
// 0xC2, without clock in system it stores 3 bytes uptime in 11 and 16, with clock date in 10-14, and 15-19
// date is marked with 0x80 to year-field
// also C6, C7 https://github.com/emsesp/EMS-ESP32/issues/938#issuecomment-1425813815
void Boiler::process_UBAErrorMessage2(std::shared_ptr<const Telegram> telegram) {
if (telegram->offset > 0 || telegram->message_length < 20) {
return;
}
char code[sizeof(lastCode_)] = {0};
uint16_t codeNo = EMS_VALUE_SHORT_NOTSET;
code[0] = telegram->message_data[5];
code[1] = telegram->message_data[6];
code[2] = telegram->message_data[7];
code[3] = 0;
static uint32_t lastCodeDate_ = 0; // last code date
uint32_t date = 0;
char code[sizeof(lastCode_)] = {0};
uint16_t codeNo = EMS_VALUE_SHORT_NOTSET;
code[0] = telegram->message_data[5];
code[1] = telegram->message_data[6];
code[2] = telegram->message_data[7];
code[3] = 0;
telegram->read_value(codeNo, 8);
// check for valid date, https://github.com/emsesp/EMS-ESP32/issues/204
@@ -1617,6 +1695,7 @@ void Boiler::process_UBAErrorMessage2(std::shared_ptr<const Telegram> telegram)
uint8_t end_min = telegram->message_data[19];
if (telegram->message_data[15] & 0x80) { //valid end date
date = (end_year - 2000) * 535680UL + end_month * 44640UL + end_day * 1440UL + end_hour * 60 + end_min;
snprintf(&code[3],
sizeof(code) - 3,
"(%d) %02d.%02d.%04d %02d:%02d - %02d.%02d.%04d %02d:%02d",
@@ -1632,6 +1711,7 @@ void Boiler::process_UBAErrorMessage2(std::shared_ptr<const Telegram> telegram)
end_hour,
end_min);
} else { // no valid end date means error still persists
date = (start_year - 2000) * 535680UL + start_month * 44640UL + start_day * 1440UL + start_hour * 60 + start_min;
snprintf(&code[3], sizeof(code) - 3, "(%d) %02d.%02d.%04d %02d:%02d - now", codeNo, start_day, start_month, start_year, start_hour, start_min);
}
} else { // no clock, the uptime is stored https://github.com/emsesp/EMS-ESP32/issues/121
@@ -1639,9 +1719,15 @@ void Boiler::process_UBAErrorMessage2(std::shared_ptr<const Telegram> telegram)
uint32_t endtime = 0;
telegram->read_value(starttime, 11, 3);
telegram->read_value(endtime, 16, 3);
snprintf(&code[3], sizeof(code) - 3, "(%d) @uptime %d - %d min", codeNo, starttime, endtime);
snprintf(&code[3], sizeof(code) - 3, "(%d) @uptime %lu - %lu min", codeNo, starttime, endtime);
date = starttime;
}
if (date > lastCodeDate_ && lastCodeDate_) {
lastCodeDate_ = date;
has_update(lastCode_, code, sizeof(lastCode_));
} else if (!lastCodeDate_) {
lastCodeDate_ = 1;
}
has_update(lastCode_, code, sizeof(lastCode_));
}
// 0x15 maintenance data
@@ -1740,12 +1826,32 @@ void Boiler::process_HpSettings3(std::shared_ptr<const Telegram> telegram) {
has_update(telegram, elHeatStep3_, 9);
}
// boiler(0x08) -W-> Me(0x0B), ?(0x04AE), data: 00 00 BD C4 00 00 5B 6A 00 00 00 24 00 00 62 59 00 00 00 00 00 00 00 00
// boiler(0x08) -W-> Me(0x0B), ?(0x04AE), data: 00 00 00 00 00 00 00 00 (offset 24)
void Boiler::process_HpEnergy(std::shared_ptr<const Telegram> telegram) {
has_update(telegram, nrgTotal_, 0);
has_update(telegram, nrgHeat_, 4);
has_update(telegram, nrgWw_, 12);
}
// boiler(0x08) -W-> Me(0x0B), ?(0x04AF), data: 00 00 48 B2 00 00 48 55 00 00 00 5D 00 00 01 78 00 00 00 00 00 00 07 61
// boiler(0x08) -W-> Me(0x0B), ?(0x04AF), data: 00 00 24 B0 00 00 00 12 00 00 23 A5 00 00 00 4B 00 00 00 00 00 00 00 00 (offset 24)
// boiler(0x08) -W-> Me(0x0B), ?(0x04AF), data: 00 00 00 00 00 00 00 00 (offset 48)
void Boiler::process_HpMeters(std::shared_ptr<const Telegram> telegram) {
has_update(telegram, meterTotal_, 0);
has_update(telegram, meterComp_, 4);
has_update(telegram, meterEHeat_, 8);
}
// HIU unit
// boiler(0x08) -B-> All(0x00), ?(0x0779), data: 06 05 01 01 AD 02 EF FF FF 00 00 7F FF
void Boiler::process_HIUMonitor(std::shared_ptr<const Telegram> telegram) {
has_update(telegram, retTemp_, 3); // is * 10
has_update(telegram, netFlowTemp_, 5); // is * 10
has_update(telegram, cwFlowRate_, 9); // is * 10
has_update(telegram, heatValve_, 7); // is %
has_update(telegram, wwValve_, 8); // is %
has_update(telegram, wwCurFlow_, 10); // is * 10 (for HIU 16 bit at offset 9, use 8 bit for compatibility to other boilers)
}
// Boiler(0x08) -W-> ME(0x0x), ?(0x0772), data: 00 00 00 00 00
@@ -1923,7 +2029,8 @@ bool Boiler::set_flow_temp(const char * value, const int8_t id) {
return false;
}
// no write/verify if there is no change, see https://github.com/emsesp/EMS-ESP32/issues/654
// no verify if value is unchanged, put it to end of tx-queue, no priority
// see https://github.com/emsesp/EMS-ESP32/issues/654, https://github.com/emsesp/EMS-ESP32/issues/954
if (v == selFlowTemp_) {
EMSESP::txservice_.add(Telegram::Operation::TX_WRITE, device_id(), EMS_TYPE_UBASetPoints, 0, (uint8_t *)&v, 1, 0, false);
return true;

View File

@@ -68,7 +68,7 @@ class Boiler : public EMSdevice {
uint8_t wwCirc_; // DHW circulation on/off
uint16_t wwCurTemp_; // DHW current temperature
uint16_t wwCurTemp2_; // DHW current temperature storage
uint8_t wwCurFlow_; // DHW current flow temp in l/min
uint8_t wwCurFlow_; // DHW current flow in l/min
uint16_t wwStorageTemp1_; // DHW storage temp 1
uint16_t wwStorageTemp2_; // DHW storage temp 2
uint8_t wwActivated_; // DHW activated
@@ -207,6 +207,14 @@ class Boiler : public EMSdevice {
int16_t hpTl2_;
int16_t hpPl1_;
int16_t hpPh1_;
int16_t hpTa4_;
int16_t hpTw1_;
uint32_t nrgTotal_;
uint32_t nrgWw_;
uint32_t nrgHeat_;
uint32_t meterTotal_;
uint32_t meterComp_;
uint32_t meterEHeat_;
// Pool unit
int8_t poolSetTemp_;
@@ -259,17 +267,17 @@ class Boiler : public EMSdevice {
uint8_t elHeatStep3_;
// HIU
uint16_t cwFlowRate_; // cold water flow rate *10
// uint16_t cwFlowRate_; // cold water flow rate *10
uint16_t netFlowTemp_; // heat network flow temperature *10
uint8_t keepWarmTemp_;
uint8_t setReturnTemp_;
uint8_t heatValve_;
uint8_t wwValve_;
// special
double nrgHeatF_;
double nrgWwF_;
uint32_t nrgHeat_;
uint32_t nrgWw_;
uint8_t nomPower_;
double nrgHeatF_; // double calcutate for nrgHeat
double nrgWwF_; // double calcutate for nrgWw
uint8_t nomPower_;
/*
// Hybrid heatpump with telegram 0xBB is readable and writeable in boiler and thermostat
@@ -326,6 +334,8 @@ class Boiler : public EMSdevice {
void process_HpDhwSettings(std::shared_ptr<const Telegram> telegram);
void process_HpSettings2(std::shared_ptr<const Telegram> telegram);
void process_HpSettings3(std::shared_ptr<const Telegram> telegram);
void process_HpEnergy(std::shared_ptr<const Telegram> telegram);
void process_HpMeters(std::shared_ptr<const Telegram> telegram);
// HIU
void process_HIUSettings(std::shared_ptr<const Telegram> telegram);
void process_HIUMonitor(std::shared_ptr<const Telegram> telegram);

132
src/devices/extension.cpp Normal file
View File

@@ -0,0 +1,132 @@
/*
* EMS-ESP - https://github.com/emsesp/EMS-ESP
* Copyright 2020-2023 Paul Derbyshire
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "extension.h"
namespace emsesp {
REGISTER_FACTORY(Extension, EMSdevice::DeviceType::EXTENSION);
Extension::Extension(uint8_t device_type, uint8_t device_id, uint8_t product_id, const char * version, const char * name, uint8_t flags, uint8_t brand)
: EMSdevice(device_type, device_id, product_id, version, name, flags, brand) {
register_telegram_type(0x935, "EM100SetMessage", true, MAKE_PF_CB(process_EM100SetMessage));
register_telegram_type(0x937, "EM100TempMessage", false, MAKE_PF_CB(process_EM100TempMessage));
register_telegram_type(0x938, "EM100InputMessage", false, MAKE_PF_CB(process_EM100InputMessage));
register_telegram_type(0x939, "EM100MonitorMessage", false, MAKE_PF_CB(process_EM100MonitorMessage));
register_telegram_type(0x93A, "EM100ConfigMessage", false, MAKE_PF_CB(process_EM100ConfigMessage));
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA,
&headerTemp_,
DeviceValueType::SHORT,
DeviceValueNumOp::DV_NUMOP_DIV10,
FL_(flowTempVf),
DeviceValueUOM::DEGREES);
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &input_, DeviceValueType::USHORT, DeviceValueNumOp::DV_NUMOP_DIV10, FL_(input), DeviceValueUOM::VOLTS);
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &outPower_, DeviceValueType::UINT, FL_(outPower), DeviceValueUOM::PERCENT);
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &setPower_, DeviceValueType::UINT, FL_(setPower), DeviceValueUOM::PERCENT);
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &setPoint_, DeviceValueType::UINT, FL_(setPoint), DeviceValueUOM::DEGREES);
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA,
&minV_,
DeviceValueType::UINT,
DeviceValueNumOp::DV_NUMOP_DIV10,
FL_(minV),
DeviceValueUOM::VOLTS,
MAKE_CF_CB(set_minV));
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA,
&maxV_,
DeviceValueType::UINT,
DeviceValueNumOp::DV_NUMOP_DIV10,
FL_(maxV),
DeviceValueUOM::VOLTS,
MAKE_CF_CB(set_maxV));
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &minT_, DeviceValueType::UINT, FL_(minT), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_minT));
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &maxT_, DeviceValueType::UINT, FL_(maxT), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_maxT));
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &dip_, DeviceValueType::UINT, FL_(mode), DeviceValueUOM::NONE);
// register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &errorState_, DeviceValueType::BOOL, FL_(error), DeviceValueUOM::NONE);
}
// 0x935 needs fetch
void Extension::process_EM100SetMessage(std::shared_ptr<const Telegram> telegram) {
has_update(telegram, minV_, 1); // Input for off, is / 10
has_update(telegram, maxV_, 2); // Input for 100%, is / 10
has_update(telegram, minT_, 3); // min temp
has_update(telegram, maxT_, 4); // max temp
}
// alert(0x15) -B-> All(0x00), ?(0x093A), data: 00 00 00 00 00 00 00 00 00 03 01
void Extension::process_EM100ConfigMessage(std::shared_ptr<const Telegram> telegram) {
has_update(telegram, dip_, 9);
}
// alert(0x15) -B-> All(0x00), ?(0x0938), data: 01 62
void Extension::process_EM100InputMessage(std::shared_ptr<const Telegram> telegram) {
has_update(telegram, outPower_, 0); // IO1
has_update(telegram, input_, 1);
}
// alert(0x15) -B-> All(0x00), ?(0x0939), data: 64 4E 00 00
void Extension::process_EM100MonitorMessage(std::shared_ptr<const Telegram> telegram) {
has_update(telegram, setPower_, 0); // percent
has_update(telegram, setPoint_, 1); // °C
// has_update(telegram, errorState_, 2); // OE1
// has_update(telegram, errorPump_, 3); // IE0
}
// alert(0x15) -B-> All(0x00), ?(0x0937), data: 80 00
void Extension::process_EM100TempMessage(std::shared_ptr<const Telegram> telegram) {
has_update(telegram, headerTemp_, 0);
}
bool Extension::set_minV(const char * value, const int8_t id) {
float v;
if (!Helpers::value2float(value, v)) {
return false;
}
write_command(0x935, 1, (uint8_t)(v * 10));
return true;
}
bool Extension::set_maxV(const char * value, const int8_t id) {
float v;
if (!Helpers::value2float(value, v)) {
return false;
}
write_command(0x935, 2, (uint8_t)(v * 10));
return true;
}
bool Extension::set_minT(const char * value, const int8_t id) {
int v;
if (!Helpers::value2temperature(value, v)) {
return false;
}
write_command(0x935, 3, v);
return true;
}
bool Extension::set_maxT(const char * value, const int8_t id) {
int v;
if (!Helpers::value2temperature(value, v)) {
return false;
}
write_command(0x935, 4, v);
return true;
}
} // namespace emsesp

59
src/devices/extension.h Normal file
View File

@@ -0,0 +1,59 @@
/*
* EMS-ESP - https://github.com/emsesp/EMS-ESP
* Copyright 2020-2023 Paul Derbyshire
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef EMSESP_EXTENSION_H
#define EMSESP_EXTENSION_H
#include "emsesp.h"
namespace emsesp {
class Extension : public EMSdevice {
public:
Extension(uint8_t device_type, uint8_t device_id, uint8_t product_id, const char * version, const char * name, uint8_t flags, uint8_t brand);
private:
void process_EM100SetMessage(std::shared_ptr<const Telegram> telegram);
void process_EM100MonitorMessage(std::shared_ptr<const Telegram> telegram);
void process_EM100TempMessage(std::shared_ptr<const Telegram> telegram);
void process_EM100InputMessage(std::shared_ptr<const Telegram> telegram);
void process_EM100ConfigMessage(std::shared_ptr<const Telegram> telegram);
bool set_minV(const char * value, const int8_t id);
bool set_maxV(const char * value, const int8_t id);
bool set_minT(const char * value, const int8_t id);
bool set_maxT(const char * value, const int8_t id);
int16_t headerTemp_; // T0
int16_t input_; // IO1
uint8_t errorState_; // OE1
uint8_t errorPump_; // IE0
uint8_t outPower_; // IO1
uint8_t setPower_; // request
uint8_t setPoint_;
uint8_t dip_; // dip switch
uint8_t minV_;
uint8_t maxV_;
uint8_t minT_;
uint8_t maxT_;
};
} // namespace emsesp
#endif

View File

@@ -44,7 +44,7 @@ Mixer::Mixer(uint8_t device_type, uint8_t device_id, uint8_t product_id, const c
if (flags == EMSdevice::EMS_DEVICE_FLAG_MMPLUS) {
if (device_id >= 0x20 && device_id <= 0x27) {
register_telegram_type(device_id - 0x20 + 0x02D7, "MMPLUSStatusMessage_HC", false, MAKE_PF_CB(process_MMPLUSStatusMessage_HC));
// register_telegram_type(device_id - 0x20 + 0x02E1, "MMPLUSStetMessage_HC", true, MAKE_PF_CB(process_MMPLUSSetMessage_HC));
// register_telegram_type(device_id - 0x20 + 0x02E1, "MMPLUSSetMessage_HC", true, MAKE_PF_CB(process_MMPLUSSetMessage_HC));
type_ = Type::HC;
hc_ = device_id - 0x20 + 1;
uint8_t tag = DeviceValueTAG::TAG_HC1 + hc_ - 1;

View File

@@ -1,29 +0,0 @@
/*
* EMS-ESP - https://github.com/emsesp/EMS-ESP
* Copyright 2020-2023 Paul Derbyshire
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "pump.h"
namespace emsesp {
REGISTER_FACTORY(Pump, EMSdevice::DeviceType::PUMP);
Pump::Pump(uint8_t device_type, uint8_t device_id, uint8_t product_id, const char * version, const char * name, uint8_t flags, uint8_t brand)
: EMSdevice(device_type, device_id, product_id, version, name, flags, brand) {
}
} // namespace emsesp

View File

@@ -1,33 +0,0 @@
/*
* EMS-ESP - https://github.com/emsesp/EMS-ESP
* Copyright 2020-2023 Paul Derbyshire
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef EMSESP_PUMP_H
#define EMSESP_PUMP_H
#include "emsesp.h"
namespace emsesp {
class Pump : public EMSdevice {
public:
Pump(uint8_t device_type, uint8_t device_id, uint8_t product_id, const char * version, const char * name, uint8_t flags, uint8_t brand);
};
} // namespace emsesp
#endif

View File

@@ -698,8 +698,9 @@ void Thermostat::process_RemoteTemp(std::shared_ptr<const Telegram> telegram) {
// 0x47B, ff - for reading humidity from the RC100H remote thermostat (0x38, 0x39, ..)
// e.g. "38 10 FF 00 03 7B 08 24 00 4B"
void Thermostat::process_RemoteHumidity(std::shared_ptr<const Telegram> telegram) {
has_update(telegram, dewtemperature_, 0);
// has_update(telegram, dewtemperature_, 0); // this is int8
has_update(telegram, humidity_, 1);
has_update(telegram, dewtemperature_, 2); // this is int16
}
// 0x273 - for reading temperaturcorrection from the RC100H remote thermostat (0x38, 0x39, ..)
@@ -1143,6 +1144,7 @@ void Thermostat::process_RC300OutdoorTemp(std::shared_ptr<const Telegram> telegr
}
// 0x240 RC300 parameter
// RC300Settings(0x240), data: 26 00 03 00 00 00 00 00 FF 01 F6 06 FF 00 00 00 00 00 00 00 00 00 00
void Thermostat::process_RC300Settings(std::shared_ptr<const Telegram> telegram) {
has_update(telegram, ibaCalIntTemperature_, 7);
has_update(telegram, ibaDamping_, 8);
@@ -1175,6 +1177,7 @@ void Thermostat::process_HPMode(std::shared_ptr<const Telegram> telegram) {
if (hc == nullptr) {
return;
}
has_update(telegram, hc->control, 3);
has_update(telegram, hc->hpmode, 5);
}
@@ -1752,11 +1755,42 @@ bool Thermostat::set_remotetemp(const char * value, const int8_t id) {
hc->remotetemp = (int16_t)(f * 10);
}
Roomctrl::set_remotetemp(hc->hc(), hc->remotetemp);
if (model() == EMSdevice::EMS_DEVICE_FLAG_JUNKERS) {
Roomctrl::set_remotetemp(Roomctrl::FB10, hc->hc(), hc->remotetemp); // FB10
} else if (model() == EMSdevice::EMS_DEVICE_FLAG_RC35 || model() == EMSdevice::EMS_DEVICE_FLAG_RC30_N) {
Roomctrl::set_remotetemp(Roomctrl::RC20, hc->hc(), hc->remotetemp); // RC20
} else if (model() == EMSdevice::EMS_DEVICE_FLAG_RC300) {
Roomctrl::set_remotetemp(Roomctrl::RC100H, hc->hc(), hc->remotetemp); // RC100H
}
return true;
}
bool Thermostat::set_remotehum(const char * value, const int8_t id) {
int h;
if (!Helpers::value2number(value, h)) {
return false;
}
uint8_t hc_num = (id == -1) ? AUTO_HEATING_CIRCUIT : id;
std::shared_ptr<Thermostat::HeatingCircuit> hc = heating_circuit(hc_num);
if (hc == nullptr) {
return false;
}
if (h > 100 || h < 0) {
hc->remotehum = EMS_VALUE_UINT_NOTSET;
} else {
hc->remotehum = h;
}
if (model() == EMSdevice::EMS_DEVICE_FLAG_RC300) {
Roomctrl::set_remotehum(Roomctrl::RC100H, hc->hc(), hc->remotehum); // RC100H
return true;
}
return false;
}
// 0xA5/0xA7 - Set the building settings
bool Thermostat::set_building(const char * value, const int8_t id) {
uint8_t bd;
@@ -1841,6 +1875,11 @@ bool Thermostat::set_control(const char * value, const int8_t id) {
write_command(set_typeids[hc->hc()], 1, ctrl);
return true;
}
} else if (model() == EMS_DEVICE_FLAG_RC300 || model() == EMS_DEVICE_FLAG_RC100) {
if (Helpers::value2enum(value, ctrl, FL_(enum_control1))) {
write_command(hpmode_typeids[hc->hc()], 3, ctrl);
return true;
}
} else if (Helpers::value2enum(value, ctrl, FL_(enum_control))) {
write_command(set_typeids[hc->hc()], EMS_OFFSET_RC35Set_control, ctrl);
return true;
@@ -3447,8 +3486,8 @@ void Thermostat::register_device_values() {
// each device controls only one hc, so we tag the values
uint8_t tag = DeviceValueTAG::TAG_HC1 + device_id() - 0x38;
register_device_value(tag, &tempsensor1_, DeviceValueType::SHORT, DeviceValueNumOp::DV_NUMOP_DIV10, FL_(remotetemp), DeviceValueUOM::DEGREES);
register_device_value(tag, &dewtemperature_, DeviceValueType::INT, FL_(dewTemperature), DeviceValueUOM::DEGREES);
register_device_value(tag, &humidity_, DeviceValueType::INT, FL_(airHumidity), DeviceValueUOM::PERCENT);
register_device_value(tag, &dewtemperature_, DeviceValueType::SHORT, DeviceValueNumOp::DV_NUMOP_DIV10, FL_(dewTemperature), DeviceValueUOM::DEGREES);
register_device_value(tag, &humidity_, DeviceValueType::UINT, FL_(airHumidity), DeviceValueUOM::PERCENT);
register_device_value(tag,
&ibaCalIntTemperature_,
DeviceValueType::INT,
@@ -4189,10 +4228,8 @@ void Thermostat::register_device_values_hc(std::shared_ptr<Thermostat::HeatingCi
DeviceValueType::INT,
DeviceValueNumOp::DV_NUMOP_DIV2,
FL_(remoteseltemp),
DeviceValueUOM::DEGREES,
MAKE_CF_CB(set_remoteseltemp),
-1,
30);
DeviceValueUOM::DEGREES);
// a command is only accepted from the remote device, not from ems-esp.
register_device_value(tag, &hc->fastHeatup, DeviceValueType::UINT, FL_(fastheatup), DeviceValueUOM::PERCENT, MAKE_CF_CB(set_fastheatup));
register_device_value(tag,
&hc->switchonoptimization,
@@ -4211,6 +4248,24 @@ void Thermostat::register_device_values_hc(std::shared_ptr<Thermostat::HeatingCi
register_device_value(tag, &hc->dewoffset, DeviceValueType::UINT, FL_(dewoffset), DeviceValueUOM::K, MAKE_CF_CB(set_dewoffset), 2, 10);
register_device_value(tag, &hc->roomtempdiff, DeviceValueType::UINT, FL_(roomtempdiff), DeviceValueUOM::K, MAKE_CF_CB(set_roomtempdiff));
register_device_value(tag, &hc->hpminflowtemp, DeviceValueType::UINT, FL_(hpminflowtemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_hpminflowtemp));
register_device_value(tag, &hc->control, DeviceValueType::ENUM, FL_(enum_control1), FL_(control), DeviceValueUOM::NONE, MAKE_CF_CB(set_control));
register_device_value(tag,
&hc->remotetemp,
DeviceValueType::SHORT,
DeviceValueNumOp::DV_NUMOP_DIV10,
FL_(remotetemp),
DeviceValueUOM::DEGREES,
MAKE_CF_CB(set_remotetemp),
-1,
101);
register_device_value(tag,
&hc->remotehum,
DeviceValueType::UINT,
FL_(remotehum),
DeviceValueUOM::PERCENT,
MAKE_CF_CB(set_remotehum),
-1,
101);
break;
case EMS_DEVICE_FLAG_CRF:
@@ -4389,7 +4444,9 @@ void Thermostat::register_device_values_hc(std::shared_ptr<Thermostat::HeatingCi
DeviceValueNumOp::DV_NUMOP_DIV10,
FL_(remotetemp),
DeviceValueUOM::DEGREES,
MAKE_CF_CB(set_remotetemp));
MAKE_CF_CB(set_remotetemp),
-1,
101);
register_device_value(tag, &hc->wwprio, DeviceValueType::BOOL, FL_(wwprio), DeviceValueUOM::NONE, MAKE_CF_CB(set_wwprio));
register_device_value(
tag, &hc->switchtime1, DeviceValueType::STRING, FL_(tpl_switchtime), FL_(switchtime1), DeviceValueUOM::NONE, MAKE_CF_CB(set_switchtime1));

View File

@@ -37,6 +37,7 @@ class Thermostat : public EMSdevice {
int16_t selTemp;
int16_t roomTemp;
int16_t remotetemp; // for readback
uint8_t remotehum; // for readback
uint8_t tempautotemp;
int8_t remoteseltemp;
uint8_t mode;
@@ -208,7 +209,7 @@ class Thermostat : public EMSdevice {
int16_t dampedoutdoortemp2_;
uint8_t floordrystatus_;
uint8_t floordrytemp_;
uint8_t dewtemperature_;
int16_t dewtemperature_;
uint8_t humidity_;
uint8_t battery_;
@@ -426,6 +427,7 @@ class Thermostat : public EMSdevice {
bool set_vacreducemode(const char * value, const int8_t id);
bool set_nofrostmode(const char * value, const int8_t id);
bool set_remotetemp(const char * value, const int8_t id);
bool set_remotehum(const char * value, const int8_t id);
bool set_roominfl_factor(const char * value, const int8_t id);
bool set_reducemode(const char * value, const int8_t id);
bool set_switchtime1(const char * value, const int8_t id);

View File

@@ -131,8 +131,8 @@ const char * EMSdevice::device_type_2_device_name(const uint8_t device_type) {
return F_(gateway);
case DeviceType::ALERT:
return F_(alert);
case DeviceType::PUMP:
return F_(pump);
case DeviceType::EXTENSION:
return F_(extension);
case DeviceType::HEATSOURCE:
return F_(heatsource);
case DeviceType::CUSTOM:
@@ -168,8 +168,8 @@ const char * EMSdevice::device_type_2_device_name_translated() {
return Helpers::translated_word(FL_(gateway_device));
case DeviceType::ALERT:
return Helpers::translated_word(FL_(alert_device));
case DeviceType::PUMP:
return Helpers::translated_word(FL_(pump_device));
case DeviceType::EXTENSION:
return Helpers::translated_word(FL_(extension_device));
case DeviceType::HEATSOURCE:
return Helpers::translated_word(FL_(heatsource_device));
case DeviceType::VENTILATION:
@@ -229,8 +229,8 @@ uint8_t EMSdevice::device_name_2_device_type(const char * topic) {
if (!strcmp(lowtopic, F_(alert))) {
return DeviceType::ALERT;
}
if (!strcmp(lowtopic, F_(pump))) {
return DeviceType::PUMP;
if (!strcmp(lowtopic, F_(extension))) {
return DeviceType::EXTENSION;
}
if (!strcmp(lowtopic, F_(heatsource))) {
return DeviceType::HEATSOURCE;
@@ -941,14 +941,15 @@ void EMSdevice::generate_values_web(JsonObject & output) {
}
}
// handle INTs
// add min and max values and steps, as integer values
else {
// add step if it's not 1
if (dv.numeric_operator > 0) {
obj["s"] = (float)1 / dv.numeric_operator;
} else if (dv.numeric_operator < 0) {
obj["s"] = (float)(-1) * dv.numeric_operator;
}
// add min and max values, if available
int16_t dv_set_min;
uint32_t dv_set_max;
if (dv.get_min_max(dv_set_min, dv_set_max)) {

View File

@@ -350,7 +350,7 @@ class EMSdevice {
CONTROLLER,
CONNECT,
ALERT,
PUMP,
EXTENSION,
GENERIC,
HEATSOURCE,
CUSTOM,
@@ -375,7 +375,7 @@ class EMSdevice {
static constexpr uint8_t EMS_DEVICE_ID_CLOCK = 0x0F;
static constexpr uint8_t EMS_DEVICE_ID_SWITCH = 0x11; // Switch WM10
static constexpr uint8_t EMS_DEVICE_ID_ALERT = 0x12; // Error module EM10
static constexpr uint8_t EMS_DEVICE_ID_PUMP = 0x15; // Pump module PM10
static constexpr uint8_t EMS_DEVICE_ID_EXTENSION = 0x15; // Extension module EM1000, Pump module PM10
static constexpr uint8_t EMS_DEVICE_ID_MODEM = 0x48;
static constexpr uint8_t EMS_DEVICE_ID_RFSENSOR = 0x40; // RF sensor only sending, no reply
static constexpr uint8_t EMS_DEVICE_ID_RFBASE = 0x50;

View File

@@ -108,9 +108,9 @@ DeviceValue::DeviceValue(uint8_t device_type,
const char * DeviceValue::DeviceValueUOM_s[] = {
F_(uom_blank), // 0
F_(uom_degrees), F_(uom_degrees), F_(uom_percent), F_(uom_lmin), F_(uom_kwh), F_(uom_wh), FL_(hours)[0], FL_(minutes)[0],
F_(uom_ua), F_(uom_bar), F_(uom_kw), F_(uom_w), F_(uom_kb), FL_(seconds)[0], F_(uom_dbm), F_(uom_fahrenheit),
F_(uom_mv), F_(uom_sqm), F_(uom_m3), F_(uom_l), F_(uom_kmin), F_(uom_k), F_(uom_blank)
F_(uom_degrees), F_(uom_degrees), F_(uom_percent), F_(uom_lmin), F_(uom_kwh), F_(uom_wh), FL_(hours)[0], FL_(minutes)[0], F_(uom_ua),
F_(uom_bar), F_(uom_kw), F_(uom_w), F_(uom_kb), FL_(seconds)[0], F_(uom_dbm), F_(uom_fahrenheit), F_(uom_mv), F_(uom_sqm),
F_(uom_m3), F_(uom_l), F_(uom_kmin), F_(uom_k), F_(uom_volts), F_(uom_blank)
};
@@ -243,7 +243,7 @@ bool DeviceValue::hasValue() const {
has_value = Helpers::hasValue(*(uint32_t *)(value_p));
break;
case DeviceValueType::CMD:
has_value = false; // commands don't have values!
has_value = true; // we count command as an actual entity
break;
default:
break;

View File

@@ -70,7 +70,8 @@ class DeviceValue {
L, // 20 - L
KMIN, // 21 - K*min
K, // 22 - K
CONNECTIVITY // 23 - used in HA
VOLTS, // 23 - V
CONNECTIVITY // 24 - used in HA
};
// TAG mapping - maps to DeviceValueTAG_s in emsdevice.cpp

View File

@@ -600,11 +600,11 @@ void EMSESP::publish_other_values() {
publish_device_values(EMSdevice::DeviceType::HEATPUMP);
publish_device_values(EMSdevice::DeviceType::HEATSOURCE);
publish_device_values(EMSdevice::DeviceType::VENTILATION);
publish_device_values(EMSdevice::DeviceType::EXTENSION);
// other devices without values yet
// publish_device_values(EMSdevice::DeviceType::GATEWAY);
// publish_device_values(EMSdevice::DeviceType::CONNECT);
// publish_device_values(EMSdevice::DeviceType::ALERT);
// publish_device_values(EMSdevice::DeviceType::PUMP);
// publish_device_values(EMSdevice::DeviceType::GENERIC);
webCustomEntityService.publish();
}
@@ -716,7 +716,6 @@ std::string EMSESP::pretty_telegram(std::shared_ptr<const Telegram> telegram) {
uint8_t offset = telegram->offset;
// find name for src and dest by looking up known devices
std::string src_name("");
std::string dest_name("");
std::string type_name("");
@@ -1292,7 +1291,7 @@ void EMSESP::incoming_telegram(uint8_t * data, const uint8_t length) {
uint8_t first_value = data[0];
if (((first_value & 0x7F) == txservice_.ems_bus_id()) && (length > 1)) {
// if we ask ourself at roomcontrol for version e.g. 0B 98 02 00 20
Roomctrl::check((data[1] ^ 0x80 ^ rxservice_.ems_mask()), data);
Roomctrl::check((data[1] ^ 0x80 ^ rxservice_.ems_mask()), data, length);
#ifdef EMSESP_UART_DEBUG
// get_uptime is only updated once per loop, does not give the right time
LOG_TRACE("[UART_DEBUG] Echo after %d ms: %s", ::millis() - rx_time_, Helpers::data_to_hex(data, length).c_str());
@@ -1392,7 +1391,7 @@ void EMSESP::incoming_telegram(uint8_t * data, const uint8_t length) {
#ifdef EMSESP_UART_DEBUG
LOG_TRACE("[UART_DEBUG] Reply after %d ms: %s", ::millis() - rx_time_, Helpers::data_to_hex(data, length).c_str());
#endif
Roomctrl::check((data[1] ^ 0x80 ^ rxservice_.ems_mask()), data); // check if there is a message for the roomcontroller
Roomctrl::check((data[1] ^ 0x80 ^ rxservice_.ems_mask()), data, length); // check if there is a message for the roomcontroller
rxservice_.add(data, length); // add to RxQueue
}

View File

@@ -97,6 +97,7 @@ MAKE_WORD(analogsensor)
MAKE_WORD(temperaturesensor)
MAKE_WORD(alert)
MAKE_WORD(pump)
MAKE_WORD(extension)
MAKE_WORD(heatsource)
MAKE_WORD(scheduler)
MAKE_WORD(custom)
@@ -141,6 +142,7 @@ MAKE_WORD_CUSTOM(n_mandatory, "<n>")
MAKE_WORD_CUSTOM(sensorid_optional, "[sensor ID]")
MAKE_WORD_CUSTOM(id_optional, "[id|hc]")
MAKE_WORD_CUSTOM(data_optional, "[data]")
MAKE_WORD_CUSTOM(nvs_optional, "[nvs]")
MAKE_WORD_CUSTOM(offset_optional, "[offset]")
MAKE_WORD_CUSTOM(length_optional, "[length]")
MAKE_WORD_CUSTOM(typeid_mandatory, "<type ID>")
@@ -167,6 +169,11 @@ MAKE_NOTRANSLATION(rc3x, "RC3x")
MAKE_NOTRANSLATION(rc20, "RC20")
MAKE_NOTRANSLATION(fb10, "FB10")
MAKE_NOTRANSLATION(fb100, "FB100")
MAKE_NOTRANSLATION(rc310, "RC310")
MAKE_NOTRANSLATION(rc200, "RC200")
MAKE_NOTRANSLATION(rc100, "RC100")
MAKE_NOTRANSLATION(rc100h, "RC100H")
MAKE_NOTRANSLATION(tc100, "TC100")
MAKE_NOTRANSLATION(dash, "-")
MAKE_NOTRANSLATION(BLANK, "")
MAKE_NOTRANSLATION(pwm, "pwm")
@@ -200,11 +207,11 @@ MAKE_NOTRANSLATION(L3, "L3")
MAKE_NOTRANSLATION(L4, "L4")
// templates - this are not translated and will be saved under options_single
MAKE_NOTRANSLATION(tpl_datetime, "< NTP | dd.mm.yyyy-hh:mm:ss-day(0-6)-dst(0/1) >")
MAKE_NOTRANSLATION(tpl_datetime, "NTP | dd.mm.yyyy-hh:mm:ss-day(0-6)-dst(0/1)")
MAKE_NOTRANSLATION(tpl_switchtime, "<nn> [ not_set | day hh:mm on|off ]")
MAKE_NOTRANSLATION(tpl_switchtime1, "<nn> [ not_set | day hh:mm Tn ]")
MAKE_NOTRANSLATION(tpl_holidays, "< dd.mm.yyyy-dd.mm.yyyy >")
MAKE_NOTRANSLATION(tpl_date, "< dd.mm.yyyy >")
MAKE_NOTRANSLATION(tpl_holidays, "dd.mm.yyyy-dd.mm.yyyy")
MAKE_NOTRANSLATION(tpl_date, "dd.mm.yyyy")
MAKE_NOTRANSLATION(tpl_input, "<inv>[<evu1><evu2><evu3><comp><aux><cool><heat><dhw><pv>]")
MAKE_NOTRANSLATION(tpl_input4, "<inv>[<comp><aux><cool><heat><dhw><pv>]")
@@ -240,6 +247,7 @@ MAKE_WORD_CUSTOM(uom_m3, "m³")
MAKE_WORD_CUSTOM(uom_l, "l")
MAKE_WORD_CUSTOM(uom_kmin, "K*min")
MAKE_WORD_CUSTOM(uom_k, "K")
MAKE_WORD_CUSTOM(uom_volts, "V")
// MQTT topics and prefixes
MAKE_WORD_CUSTOM(heating_active, "heating_active")
@@ -319,6 +327,7 @@ MAKE_ENUM(enum_controlmode2, FL_(outdoor), FL_(room))
MAKE_ENUM(enum_control, FL_(off), FL_(rc20), FL_(rc3x))
MAKE_ENUM(enum_j_control, FL_(off), FL_(fb10), FL_(fb100))
MAKE_ENUM(enum_roomsensor, FL_(extern), FL_(intern), FL_(auto))
MAKE_ENUM(enum_control1, FL_(rc310), FL_(rc200), FL_(rc100), FL_(rc100h), FL_(tc100))
MAKE_ENUM(enum_switchmode, FL_(off), FL_(eco), FL_(comfort), FL_(heat))

View File

@@ -46,7 +46,8 @@ MAKE_WORD_TRANSLATION(controller_device, "Controller Module", "Kontrollmodul", "
MAKE_WORD_TRANSLATION(switch_device, "Switch Module", "Schaltmodul", "Switch Module", "Relämodul", "Moduł przełączający", "Switch modul", "", "Anahtar", "Modulo Switch") // TODO translate
MAKE_WORD_TRANSLATION(gateway_device, "Gateway Module", "Gateway Modul", "Gateway Module", "Gateway", "Moduł IP", "Gateway", "", "Ağ Geçidi", "Modulo Gateway") // TODO translate
MAKE_WORD_TRANSLATION(alert_device, "Alert Module", "Alarmmodul", "Alert Module", "Larmmodul", "Moduł alarmowy", "Alarmmodul", "", "Alarm Cihazı", "Module Avviso") // TODO translate
MAKE_WORD_TRANSLATION(pump_device, "Pump Module", "Pumpenmodul", "Pump Module", "Pumpmodul", "Moduł pompy", "Pumpemodul", "", "Pompa", "Module Pompa") // TODO translate
//MAKE_WORD_TRANSLATION(pump_device, "Pump Module", "Pumpenmodul", "Pump Module", "Pumpmodul", "Moduł pompy", "Pumpemodul", "", "Pompa", "Module Pompa") // TODO translate
MAKE_WORD_TRANSLATION(extension_device, "Extension Module", "Erweiterungsnmodul", "Module", "Modul", "Moduł", "Modul", "", "", "Module") // TODO translate
MAKE_WORD_TRANSLATION(heatsource_device, "Heatsource", "Heizquelle", "Heatsource", "Värmekälla", "Źródło ciepła", "Varmekilde", "", "Isı Kaynağı", "Fonte di calore") // TODO translate
MAKE_WORD_TRANSLATION(sensors_device, "Sensors", "Sensoren", "Sensoren", "Sensorer", "Czujniki", "Sensorer", "Capteurs", "Sensör Cihazı", "Sensori")
MAKE_WORD_TRANSLATION(unknown_device, "Unknown", "Unbekannt", "Onbekend", "Okänt", "Nieznane urządzenie", "Ukjent", "Inconnu", "Bilinmeyen", "Sconosciuto")
@@ -344,7 +345,7 @@ MAKE_TRANSLATION(heatblock, "heatblock", "heating block", "Wärmezelle", "Aanvoe
MAKE_TRANSLATION(upTimeControl, "uptimecontrol", "total operating time heat", "Betriebszeit Heizen gesamt", "Totale bedrijfstijd", "Total tid uppvärmning", "łączny czas generowania ciepła", "total driftstid", "durée totale de fonctionnement chauffage", "ısınma toplam işletme süresi", "Tempo di funzionamento totale riscaldamento")
MAKE_TRANSLATION(upTimeCompHeating, "uptimecompheating", "operating time compressor heating", "Betriebszeit Kompressor heizen", "Bedrijfstijd compressor verwarmingsbedrijf", "Total tid kompressor uppvärmning", "łączny czas ogrzewania (sprężarka)", "totaltid kompressor", "durée de fonctionnement compresseur chauffage", "ısı pompası ısınma işletme süresi", "tempo di funzionamento del compressore riscaldamento")
MAKE_TRANSLATION(upTimeCompCooling, "uptimecompcooling", "operating time compressor cooling", "Betriebszeit Kompressor kühlen", "Bedrijfstijd compressor koelbedrijf", "Total tid kompressor kyla", "łączny czas chłodzenia (sprężarka)", "Total tid kompressor kjøling", "durée de fonctionnement compresseur refroidissement", "ısı pompası soğuma işletme süresi", "tempo di funzionamento del compressore raffreddamento")
MAKE_TRANSLATION(upTimeCompWw, "uptimecompww", "operating time compressor dhw", "Betriebszeit Kompressor", "Bedrijfstijd compressor warmwaterbedrijf", "Total tid kompressor varmvatten", "łączny czas grzania c.w.u. (sprężarka)", "Total tid kompressor varmtvann", "durée de fonctionnement compresseur ecs", "ısı pompası sıcak kullanım suyu işletme süresi", "tempo di funzionamento del compressore")
MAKE_TRANSLATION(upTimeCompWw, "uptimecompww", "operating time compressor", "Betriebszeit Kompressor", "Bedrijfstijd compressor", "Total tid kompressor", "łączny czas grzania c.w.u. (sprężarka)", "Total tid kompressor", "durée de fonctionnement compresseur", "ısı pompası sıcak kullanım suyu işletme süresi", "tempo di funzionamento del compressore")
MAKE_TRANSLATION(upTimeCompPool, "uptimecomppool", "operating time compressor pool", "Betriebszeit Kompressor Pool", "Bedrijfstijd compressor voor zwembadbedrijf", "Total tid kompressor pool", "łączny czas podgrzewania basenu (sprężarka)", "Total tid kompressor basseng", "durée de fonctionnement compresseur piscine", "ısı pompası havuz işletme süresi", "tempo di funzionamento del compressore piscina")
MAKE_TRANSLATION(totalCompStarts, "totalcompstarts", "total compressor control starts", "Kompressor Starts gesamt", "Totaal compressorstarts", "Kompressorstarter Totalt", "liczba załączeń sprężarki", "kompressorstarter totalt", "nombre démarrages total contrôle compresseur", "ısı pompası kontrolü toplam başlatma", "avvii totali del compressore")
MAKE_TRANSLATION(heatingStarts, "heatingstarts", "heating control starts", "Heizen Starts", "Starts verwarmingsbedrijf", "Kompressorstarter Uppvärmning", "liczba załączeń ogrzewania", "kompressorstarter oppvarming", "démarrages contrôle chauffage", "ısıtma kontrolü toplam başlatma", "avvii riscaldamento")
@@ -353,24 +354,24 @@ MAKE_TRANSLATION(poolStarts, "poolstarts", "pool control starts", "Pool Starts",
MAKE_TRANSLATION(nrgConsTotal, "nrgconstotal", "total energy consumption", "Energieverbrauch gesamt", "Energieverbrauch gesamt", "Energiförbrukning totalt", "energia pobrana (sumarycznie)", "energiforbruk totalt", "consommation totale énergie", "toplam enerji tüketimi", "totale energia consumata")
MAKE_TRANSLATION(nrgConsCompTotal, "nrgconscomptotal", "total energy consumption compressor", "Energieverbrauch Kompressor gesamt", "Energieverbruik compressor totaal", "Energiförbrukning kompressor", "energia pobrana przez sprężarkę", "energiforbruk kompressor", "consommation totale énergie compresseur", "ısı pompası toplam enerji tüketimi", "totale energia consumata compressore")
MAKE_TRANSLATION(nrgConsCompHeating, "nrgconscompheating", "energy consumption compressor heating", "Energieverbrauch Kompressor heizen", "Energieverbruik compressor verwarmingsbedrijf", "Energiförbrukning uppvärmning", "energia pobrana przez sprężarkę na ogrzewanie", "energiforbruk oppvarming", "consommation énergie compresseur chauffage", "ısı pompası ısıtma toplam enerji tüketimi", "consumo energia compressore riscaldamento")
MAKE_TRANSLATION(nrgConsCompWw, "nrgconscompww", "energy consumption compressor dhw", "Energieverbrauch Kompressor", "Energieverbruik compressor warmwaterbedrijf", "Energiförbrukning varmvatten", "energia pobrana przez sprężarkę na c.w.u.", "energiforbruk varmvann", "consommation énergie compresseur ecs", "ısı pompası sıcak kullanım suyu toplam enerji tüketimi", "consumo energia compressore ACS")
MAKE_TRANSLATION(nrgConsCompWw, "nrgconscompww", "energy consumption compressor", "Energieverbrauch Kompressor", "Energieverbruik compressor", "Energiförbrukning", "energia pobrana przez sprężarkę na c.w.u.", "energiforbruk", "consommation énergie compresseur", "ısı pompası sıcak kullanım suyu toplam enerji tüketimi", "consumo energia compressore")
MAKE_TRANSLATION(nrgConsCompCooling, "nrgconscompcooling", "energy consumption compressor cooling", "Energieverbrauch Kompressor kühlen", "Energieverbruik compressor koelbedrijf", "Energiförbrukning kyla", "energia pobrana przez sprężarkę na chłodzenie", "energiforbruk kjøling", "consommation énergie compresseur refroidissement", "ısı pompası soğutma toplam enerji tüketimi", "consumo energia compressore raffreddamento")
MAKE_TRANSLATION(nrgConsCompPool, "nrgconscomppool", "energy consumption compressor pool", "Energieverbrauch Kompressor Pool", "Energiebedrijf compressor zwembadbedrijf", "Energiförbrukning pool", "energia pobrana przez sprężarkę na podgrzewanie basenu", "energiforbruk basseng", "consommation énergie compresseur piscine", "ısı pompası havuz toplam enerji tüketimi", "consumo energia compressore piscina")
MAKE_TRANSLATION(nrgSuppTotal, "nrgsupptotal", "total energy supplied", "gesamte Energieabgabe", "Totaal opgewekte energie", "Genererad energi", "energia oddana (sumarycznie)", "tilført energi", "énergie totale fournie", "sağlanan toplam enerji", "totale energia fornita")
MAKE_TRANSLATION(nrgSuppHeating, "nrgsuppheating", "total energy supplied heating", "gesamte Energieabgabe heizen", "Opgewekte energie verwarmingsbedrijf", "Genererad energi Uppvärmning", "energia oddana na ogrzewanie", "tilført energi oppvarming", "énergie totale fournie chauffage", "ısıtma sağlanan toplam enerji", "energia totale fornita - riscaldamento")
MAKE_TRANSLATION(nrgSuppWw, "nrgsuppww", "total energy warm supplied dhw", "gesamte Energieabgabe", "Opgewekte energie warmwaterbedrijf", "Genererad energi Varmvatten", "energia oddana na c.w.u.", "tilført energi varmvann", "énergie chaude totale fournie ecs", "sıcak kullanım suyu sağlanan toplam enerji", "totale energia calorica fornita ACS")
MAKE_TRANSLATION(nrgSuppWw, "nrgsuppww", "total energy warm supplied", "gesamte Energieabgabe", "Opgewekte energie", "Genererad energi", "energia oddana na c.w.u.", "tilført energi", "énergie chaude totale fournie", "sıcak kullanım suyu sağlanan toplam enerji", "totale energia calorica fornita")
MAKE_TRANSLATION(nrgSuppCooling, "nrgsuppcooling", "total energy supplied cooling", "gesamte Energieabgabe kühlen", "Opgewekte energie koelbedrijf", "Genererad energi Kyla", "energia oddana na chłodzenie", "Tillført energi kjøling", "énergie totale fournie refroidissement", "soğutma sağlanan toplam enerji", "energia totale fornita - raffreddamento")
MAKE_TRANSLATION(nrgSuppPool, "nrgsupppool", "total energy supplied pool", "gesamte Energieabgabe Pool", "Opgewekte energie zwembadbedrijf", "Genererad energi Pool", "energia oddana na podgrzewanie basenu", "tilført energi basseng", "énergie totale fournie piscine", "havuz sağlanan toplam enerji", "totale di energia fornita- piscina")
MAKE_TRANSLATION(auxElecHeatNrgConsTotal, "auxelecheatnrgconstotal", "total aux elec. heater energy consumption", "Energieverbrauch el. Zusatzheizung", "Totaal energieverbruik electrisch verwarmingselement", "Energiförbrukning Eltillkott", "energia pobrana przez grzałki", "energiforbruk varmekolbe", "consommation totale énergie electrique auxiliaire chauffage", "ilave elektrikli ısıtıcı toplam enerji tüketimi", "consumo energetico riscaldamento elettrico supplementare")
MAKE_TRANSLATION(auxElecHeatNrgConsHeating, "auxelecheatnrgconsheating", "aux elec. heater energy consumption heating", "Energieverbrauch el. Zusatzheizung Heizen", "Energieverbruik electrisch verwarmingselement voor verwarmingsbedrijf", "Energiförbrukning Eltillskott Uppvärmning", "energia pobrana przez grzałki na ogrzewanie", "energiforbruk varmekolbe oppvarming", "consommation énergie electrique auxiliaire chauffage", "ilave elektrikli ısıtıcı ısınma toplam enerji tüketimi", "consumo di energia riscaldamento elettrico ausiliario")
MAKE_TRANSLATION(auxElecHeatNrgConsWW, "auxelecheatnrgconsww", "aux elec. heater energy consumption dhw", "Energieverbrauch el. Zusatzheizung", "Energieverbruik electrisch verwarmingselement voor warmwaterbedrijf", "Energiförbrukning Eltillskott Varmvatten", "energia pobrana przez grzałki na c.w.u.", "energiförbruk varmekolbe varmvann", "consommation énergie electrique auxiliaire chauffage ecs", "ilave elektrikli ısıtıcı sıcak kullanım suyu toplam enerji tüketimi", "consumo di energia riscaldamento elettrico ausiliario ACS")
MAKE_TRANSLATION(auxElecHeatNrgConsWW, "auxelecheatnrgconsww", "aux elec. heater energy consumption", "Energieverbrauch el. Zusatzheizung", "Energieverbruik electrisch verwarmingselement voor", "Energiförbrukning Eltillskott", "energia pobrana przez grzałki na c.w.u.", "energiförbruk varmekolbe", "consommation énergie electrique auxiliaire chauffage", "ilave elektrikli ısıtıcı sıcak kullanım suyu toplam enerji tüketimi", "consumo di energia riscaldamento elettrico ausiliario")
MAKE_TRANSLATION(auxElecHeatNrgConsPool, "auxelecheatnrgconspool", "aux elec. heater energy consumption pool", "Energieverbrauch el. Zusatzheizung Pool", "Energieverbruik electrisch verwarmingselement voor zwembadbedrijf", "Energiförbrukning Eltillskott Pool", "energia pobrana przez grzałki na podgrzewanie basenu", "energiforbruk el. tilleggsvarme basseng", "consommation énergie electrique auxiliaire chauffage piscine", "ilave elektrikli ısıtıcı havuz toplam enerji tüketimi", "consumo di energia riscaldamento elettrico ausiliario piscina")
MAKE_TRANSLATION(hpCompOn, "hpcompon", "hp compressor", "WP Kompressor", "WP compressor", "VP Kompressor", "sprężarka pompy ciepła", "vp kompressor", "compresseur pompe à chaleur", "hp ısı pompası", "compressore pompa calore")
MAKE_TRANSLATION(hpHeatingOn, "hpheatingon", "hp heating", "WP Heizen", "WP verwarmingsbedrijf", "VP Uppvärmning", "pompa ciepła, ogrzewanie", "vp oppvarmning", "hp ısınıyor", "riscaldamento pompa calore")
MAKE_TRANSLATION(hpCoolingOn, "hpcoolingon", "hp cooling", "WP Kühlen", "WP koelbedrijf", "VP Kyla", "pompa ciepła, chłodzenie", "vp kjøling", "hp soğuyor", "raffreddamento pompa calore")
MAKE_TRANSLATION(coolingOn, "coolingon", "cooling", "Kühlen", "koelbedrijf", "Kyla", "chłodzenie", "kjøling", "refroidissement", "hp sıcak kullanım suyu", "") // TODO translate
MAKE_TRANSLATION(hpWwOn, "hpwwon", "hp dhw", "WP Warmwasser", "WP warmwaterbedrijf", "VP Varmvatten", "pompa ciepła, c.w.u.", "vp varmvatten", "hp havuz", "acqua calda pompa calore")
MAKE_TRANSLATION(hpWwOn, "hpwwon", "hp", "WP", "WP", "VP", "pompa ciepła", "vp", "hp", "pompa calore")
MAKE_TRANSLATION(hpPoolOn, "hppoolon", "hp pool", "WP Pool", "WP zwembadbedrijf", "VP Pool", "pompa ciepła, podgrzewanie basenu", "vp basseng", "tuzlu su pompası hızı", "pompa calore piscina")
MAKE_TRANSLATION(hpBrinePumpSpd, "hpbrinepumpspd", "brine pump speed", "Solepumpen-Geschw.", "Snelheid pekelpomp", "Hastighet Brine-pump", "wysterowanie pompy glikolu", "hastighet brine-pumpe", "vitesse pompe à saumure", "ısı pompası hızı", "velocità pompa sbrinamento")
MAKE_TRANSLATION(hpCompSpd, "hpcompspd", "compressor speed", "Kompressor-Geschw.", "Snelheid compressor", "Kompressorhastighet", "wysterowanie sprężarki", "kompressorhastighet", "vitesse du compresseur", "sirkülasyon pompası hızı", "velocità compressore")
@@ -393,7 +394,8 @@ MAKE_TRANSLATION(hpTr7, "hptr7", "refrigerant temperature gas side (condenser in
MAKE_TRANSLATION(hpTl2, "hptl2", "air inlet temperature (TL2)", "Außenluft-Einlasstemperatur (TL2)", "Temperatuur luchtinlaat (TL2)", "Luftintagstemperatur (TL2)", "temperatura wlotu powietrza (TL2)", "luftinntakstemperatur (TL2)", "température entrée air (TL2)", "hava giriş sıcaklığı (TL2)", "temperatura ingresso aria (TL2)")
MAKE_TRANSLATION(hpPl1, "hppl1", "low pressure side temperature (PL1)", "Niederdruckfühler (PL1)", "Temperatuur lage drukzijde (PL1)", "Temperatur Lågtryckssidan (PL1)", "temperatura po stronie niskiego ciśnienia (PL1)", "temperatur lavtrykksiden (PL1)", "température côté basse pression (PL1)", "düşük basınç tarafı sıcaklığı (PL1)", "temperatura lato bassa pressione (PL1)")
MAKE_TRANSLATION(hpPh1, "hpph1", "high pressure side temperature (PH1)", "Hochdruckfühler (PH1)", "Temperatuur hoge drukzijde (PH1)", "Temperatur Högtryckssidan (PH1)", "temperatura po stronie wysokiego ciśnienia (PH1)", "Temperatur Høytrykksiden (PH1)", "température côté bhauteasse pression (PH1)", "yüksek basınç tarafı sıcaklığı (PH1)", "temperatura lato alta pressione (PH1)")
MAKE_TRANSLATION(hpTa4, "hpta4", "drain pan temp (TA4)", "Kondensatorwanne (TA4)", "Temperatuur condensorafvoerbak", "", "temperatura ociekacza (TA4)", "kondens temperatur", "", "tahliye sıcaklığı (TA4)", "temperatura condensatore (TA4)") // TODO translate
MAKE_TRANSLATION(hpTa4, "hpta4", "drain pan temp (TA4)", "Kondensatorwanne (TA4)", "Temperatuur condensorafvoerbak (TA4)", " (TA4)", "temperatura ociekacza (TA4)", "kondens temperatur (TA4)", " (TA4)", "tahliye sıcaklığı (TA4)", "temperatura condensatore (TA4)") // TODO translate
MAKE_TRANSLATION(hpTw1, "hptw1", "reservoir temp (TW1)", "WW Reservoir (TW1)", "(TW1)", "(TW1)", "(TW1)", "(TW1)", "(TW1)", "(TW1)", "(TW1)") // TODO translate
MAKE_TRANSLATION(hpInput1, "hpin1", "input 1 state", "Eingang 1 Status", "Status input 1", "Status Ingång 1", "stan wejścia 1", "status inggang 1", "état entrée 1", "giriş 1 durumu", "stato ingresso 1")
MAKE_TRANSLATION(hpInput2, "hpin2", "input 2 state", "Eingang 2 Status", "Status input 2", "Status Ingång 2", "stan wejścia 2", "status inggang 2", "état entrée 2", "giriş 2 durumu", "stato ingresso 2")
@@ -405,7 +407,7 @@ MAKE_TRANSLATION(hpIn3Opt, "hpin3opt", "input 3 options", "Eingang 3 Einstellung
MAKE_TRANSLATION(hpIn4Opt, "hpin4opt", "input 4 options", "Eingang 4 Einstellung", "Instelling input 4", "Inställningar Ingång 4", "opcje wejścia 4", "innstillinger inngang 4", "options entrée 4", "giriş 4 seçenekleri", "impostazioni ingresso 4")
MAKE_TRANSLATION(maxHeatComp, "maxheatcomp", "heat limit compressor", "Heizgrenze Kompressor", "heat limit compressor", "heat limit compressor", "ograniczenie mocy sprężarki", "max varmegrense kompressor", "limite chaleur compresseur", "ısı pompası ısıtma sınırı", "limite riscaldamento compressore")
MAKE_TRANSLATION(maxHeatHeat, "maxheatheat", "heat limit heating", "Heizgrenze Heizen", "heat limit heating", "heat limit heating", "ograniczenie mocy w trybie ogrzewania", "maks varmegrense oppvarming", "limite chaleur chauffage", "ısınma ısıtma sınırı", "limite calore riscaldamento")
MAKE_TRANSLATION(maxHeatDhw, "maxheatdhw", "heat limit dhw", "Heizgrenze Warmwasser", "heat limit dhw", "heat limit dhw", "ograniczenie mocy w trybie c.w.u.", "varmegrense varmtvann", "limite chaleur ecs", "sıcak kullanım suyu ısınma sınırı", "limite calore ACS")
MAKE_TRANSLATION(maxHeatDhw, "maxheatdhw", "heat limit", "Heizgrenze", "heat limit", "heat limit", "ograniczenie mocy w trybie c.w.u.", "varmegrense", "limite chaleur", "sıcak kullanım suyu ısınma sınırı", "limite calore")
MAKE_TRANSLATION(auxHeaterOff, "auxheateroff", "disable aux heater", "Verbiete Zusatzheizer", "Bijverwarming uitsc", "Blockera eltillskott", "wyłącz dogrzewacz", "deaktiver tilleggsvarme", "Désactiver chauff. d'app", "ilave ısıtıcıyı kapat", "disattivare i riscaldatori addizionali")
MAKE_TRANSLATION(auxHeaterStatus, "auxheaterstatus", "aux heater status", "Status Zusatzheizer", "Bijverwarming", "Eltillskott Status", "status dogrzewacza", "status el. tillegsvarme", "Chauffage auxiliaire", "ilave ısıtıcı durumu", "stato riscaldatori addizionali")
@@ -504,14 +506,20 @@ MAKE_TRANSLATION(blockHyst, "blockhyst", "hyst. for boiler block", "Hysterese Sp
MAKE_TRANSLATION(releaseWait, "releasewait", "boiler release wait time", "Wartezeit Kessel-Freigabe", "Wachttijd ketel vrijgave", "Väntetid Frisläppning", "czas oczekiwania na zwolnienie kotła", "kjele frigjøringsventetid", "temps attente libération chaudière", "kazan tahliyesi bekleme süresi", "tempo di attesa sblocco caldaia")
// energy
MAKE_TRANSLATION(nrgTotal, "nrgtotal", "total energy", "Energie gesamt", "", "", "", "", "", "", "") // TODO translate
MAKE_TRANSLATION(nrgHeat, "nrgheat", "energy heating", "Energie Heizen", "", "", "", "", "", "ısıtma enerjisi", "") // TODO translate
MAKE_TRANSLATION(nrgWw, "nrgww", "energy dhw", "Energie Warmwasser", "", "", "", "", "", "sıcak kullanım suyu enerjisi", "") // TODO translate
MAKE_TRANSLATION(nrgWw, "nrgww", "energy", "Energie", "", "", "", "", "", "sıcak kullanım suyu enerjisi", "") // TODO translate
MAKE_TRANSLATION(nomPower, "nompower", "nominal Power", "Brennerleistung", "", "", "", "", "", "nominal güç", "") // TODO translate
MAKE_TRANSLATION(meterTotal, "metertotal", "meter total", "Messung gesamt", "", "", "", "", "", "", "") // TODO translate
MAKE_TRANSLATION(meterComp, "metercomp", "meter compressor", "Messung Kompressor", "", "", "", "", "", "", "") // TODO translate
MAKE_TRANSLATION(meterEHeat, "metereheat", "meter e-heater", "Messung E-Heizer", "", "", "", "", "", "", "") // TODO translate
// HIU
MAKE_TRANSLATION(netFlowTemp, "netflowtemp", "heat network flow temp", "System Vorlauftemperatur", "Netto aanvoertemperatuur", "", "", "", "", "ısıtma şebekesi akış derecesi", "temperatura di mandata della rete di riscaldamento") // TODO translate
MAKE_TRANSLATION(cwFlowRate, "cwflowrate", "cold water flow rate", "Kaltwasser Durchfluss", "Stroomsnelheid koud water ", "", "", "", "", "soğuk su akış hızı", "portata acqua fredda") // TODO translate
// MAKE_TRANSLATION(cwFlowRate, "cwflowrate", "cold water flow rate", "Kaltwasser Durchfluss", "Stroomsnelheid koud water ", "", "", "", "", "soğuk su akış hızı", "portata acqua fredda") // TODO translate
MAKE_TRANSLATION(keepWarmTemp, "keepwarmtemp", "keep warm temperature","Warmhaltetemperatur", "Warmhoudtemperatuur", "", "", "", "", "sıcaklığı koruma derecesi", "mantenere la temperatura calda") // TODO translate
MAKE_TRANSLATION(heatValve, "heatvalve", "heating valve", "Ventil Heizen", "", "", "", "", "", "", "") // TODO translate
MAKE_TRANSLATION(wwValve, "wwvalve", "valve", "Ventil", "", "", "", "", "", "", "") // TODO translate
// the following are dhw for the boiler and automatically tagged with 'dhw'
MAKE_TRANSLATION(wwSelTemp, "wwseltemp", "selected temperature", "gewählte Temperatur", "Geselecteerd temperatuur", "Vald Temperatur", "temperatura wyższa/komfort", "valgt temperatur", "température sélectionnée", "seçili sıcaklık", "temperatura selezionata")
@@ -795,6 +803,17 @@ MAKE_TRANSLATION(ventInSpeed, "ventinspeed", "in blower speed", "Zuluft-Drehzahl
MAKE_TRANSLATION(ventOutSpeed, "ventoutspeed", "out blower speed", "Abluft-Drehzahl", "toerental afvoerventilator", "", "", "", "", "dış fan hızı", "velocità aria di scarico") // TODO translate
MAKE_TRANSLATION(airquality, "airquality", "air quality (voc)", "Luftqualität (VOC)", "luchtkwaliteit (VOC)", "", "", "", "", "hava kalitesi(voc)", "qualità aria (VOC)") // TODO translate
// EM100
MAKE_TRANSLATION(minV, "minv", "min volt.", "min Spannung", "", "", "", "", "", "", "") // TODO translate
MAKE_TRANSLATION(maxV, "maxv", "max volt.", "max Spannung", "", "", "", "", "", "", "") // TODO translate
MAKE_TRANSLATION(minT, "mint", "min temp.", "min Temperatur", "", "", "", "", "", "", "") // TODO translate
MAKE_TRANSLATION(maxT, "maxt", "max temp.", "max Temperatur", "", "", "", "", "", "", "") // TODO translate
MAKE_TRANSLATION(setPoint, "setpoint", "set temp.", "Sollemperatur", "", "", "", "", "", "", "") // TODO translate
MAKE_TRANSLATION(setPower, "setpower", "request power", "Sollleistung", "", "", "", "", "", "", "") // TODO translate
MAKE_TRANSLATION(dip, "dip", "dip switch", "dip Schalter", "", "", "", "", "", "", "") // TODO translate
MAKE_TRANSLATION(outPower, "outpow", "output IO1", "Ausgang IO1", "", "", "", "", "", "", "") // TODO translate
MAKE_TRANSLATION(input, "input", "input", "Eingang", "", "", "", "", "", "", "") // TODO translate
/*
// unknown fields to track (SM10), only for testing
// **** NO TRANSLATION NEEDED ****

View File

@@ -472,6 +472,7 @@ void Mqtt::on_disconnect(espMqttClientTypes::DisconnectReason reason) {
} else {
LOG_WARNING("MQTT disconnected: code %d", reason);
}
mqttClient_->clearQueue(true);
}
// MQTT on_connect - when an MQTT connect is established
@@ -589,7 +590,7 @@ bool Mqtt::queue_message(const uint8_t operation, const std::string & topic, con
return true;
}
}
if (!mqtt_enabled_ || topic.empty()) {
if (!mqtt_enabled_ || topic.empty() || !connected()) {
return false; // quit, not using MQTT
}
// check free mem
@@ -633,10 +634,12 @@ bool Mqtt::queue_message(const uint8_t operation, const std::string & topic, con
packet_id = mqttClient_->unsubscribe(fulltopic);
LOG_DEBUG("Unsubscribing to topic '%s', pid %d", fulltopic, packet_id);
}
#ifndef EMSESP_STANDALONE
if (packet_id == 0) {
LOG_WARNING("%s failed: %s", operation == Operation::PUBLISH ? "Publish" : operation == Operation::SUBSCRIBE ? "Subscribe" : "Unsubscribe", fulltopic);
mqtt_publish_fails_++;
}
#endif
return (packet_id != 0);
}

View File

@@ -24,38 +24,97 @@ namespace emsesp {
bool Roomctrl::switch_off_[HCS] = {false, false, false, false};
uint32_t Roomctrl::rc_time_[HCS] = {0, 0, 0, 0};
int16_t Roomctrl::remotetemp_[HCS] = {EMS_VALUE_SHORT_NOTSET, EMS_VALUE_SHORT_NOTSET, EMS_VALUE_SHORT_NOTSET, EMS_VALUE_SHORT_NOTSET};
uint8_t Roomctrl::remotehum_[HCS] = {EMS_VALUE_UINT_NOTSET, EMS_VALUE_UINT_NOTSET, EMS_VALUE_UINT_NOTSET, EMS_VALUE_UINT_NOTSET};
uint8_t Roomctrl::sendcnt[] = {0, 0, 0, 0};
uint8_t Roomctrl::type_ = RC20;
/**
* set the temperature,
*/
void Roomctrl::set_remotetemp(const uint8_t hc, const int16_t temp) {
if (hc >= HCS) {
void Roomctrl::set_remotetemp(const uint8_t type, const uint8_t hc, const int16_t temp) {
if (hc >= HCS || (type != RC20 && type != FB10 && type != RC100H && type != SENSOR)) {
return;
}
type_ = type;
if (remotetemp_[hc] != EMS_VALUE_SHORT_NOTSET && temp == EMS_VALUE_SHORT_NOTSET) {
switch_off_[hc] = true;
}
if (remotetemp_[hc] != temp) {
rc_time_[hc] = uuid::get_uptime() - SEND_INTERVAL; // send now
sendcnt[hc] = 0;
}
remotetemp_[hc] = temp;
}
void Roomctrl::set_remotehum(const uint8_t type, const uint8_t hc, const int8_t hum) {
if (hc >= HCS || (type != RC20 && type != FB10 && type != RC100H && type != SENSOR)) {
return;
}
type_ = type;
if (remotehum_[hc] != EMS_VALUE_UINT_NOTSET && hum == EMS_VALUE_UINT_NOTSET) {
switch_off_[hc] = true;
}
if (remotehum_[hc] != hum) {
rc_time_[hc] = uuid::get_uptime() - SEND_INTERVAL; // send now
sendcnt[hc] = 1;
}
remotehum_[hc] = hum;
}
uint8_t Roomctrl::get_hc(uint8_t addr) {
switch (type_) {
case SENSOR:
return addr - 0x40;
case RC100H:
return addr - 0x38;
case FB10:
case RC20:
default:
return addr - 0x18;
}
}
/**
* if remote control is active send the temperature every minute
*/
void Roomctrl::send(const uint8_t addr) {
uint8_t hc = addr - ADDR;
// check address, reply only on addresses 0x18..0x1B
uint8_t hc = get_hc(addr);
// check address, reply only on addresses 0x18..0x1B or 0x40..0x43
if (hc >= HCS) {
return;
}
// no reply if the temperature is not set
if (remotetemp_[hc] == EMS_VALUE_SHORT_NOTSET && !switch_off_[hc]) {
if (!switch_off_[hc] && remotetemp_[hc] == EMS_VALUE_SHORT_NOTSET && remotehum_[hc] == EMS_VALUE_UINT_NOTSET) {
return;
}
if (uuid::get_uptime() - rc_time_[hc] > SEND_INTERVAL) { // send every minute
rc_time_[hc] = uuid::get_uptime(); // use EMS-ESP's millis() to prevent overhead
temperature(addr, 0x00); // send to all
switch_off_[hc] = false;
if (type_ == RC100H) {
if (sendcnt[hc] == 1) { // second telegram for humidity
if (switch_off_[hc]) {
remotehum_[hc] = EMS_VALUE_UINT_NOTSET;
}
rc_time_[hc] = uuid::get_uptime();
humidity(addr, 0x10, hc);
sendcnt[hc] = 0;
} else { // temperature telegram
if (remotehum_[hc] != EMS_VALUE_UINT_NOTSET) {
sendcnt[hc] = 1;
} else {
rc_time_[hc] = uuid::get_uptime();
}
temperature(addr, 0x10, hc); // send to master-thermostat (https://github.com/emsesp/EMS-ESP32/issues/336)
}
} else if (type_ == FB10) {
rc_time_[hc] = uuid::get_uptime();
temperature(addr, 0x10, hc); // send to master-thermostat (https://github.com/emsesp/EMS-ESP32/issues/336)
} else { // type==RC20
rc_time_[hc] = uuid::get_uptime();
temperature(addr, 0x00, hc); // send to all
}
if (remotehum_[hc] == EMS_VALUE_UINT_NOTSET) {
switch_off_[hc] = false;
}
} else {
// acknowledge every poll, otherwise the master shows error A22-816
EMSuart::send_poll(addr);
@@ -65,11 +124,11 @@ void Roomctrl::send(const uint8_t addr) {
/**
* check if there is a message for the remote room controller
*/
void Roomctrl::check(const uint8_t addr, const uint8_t * data) {
uint8_t hc = (addr & 0x7F) - ADDR;
void Roomctrl::check(const uint8_t addr, const uint8_t * data, const uint8_t length) {
uint8_t hc = get_hc(addr & 0x7F);
// check address, reply only on addresses 0x18..0x1B
if (hc >= HCS) {
if (hc >= HCS || length < 5) {
return;
}
// no reply if the temperature is not set
@@ -83,14 +142,24 @@ void Roomctrl::check(const uint8_t addr, const uint8_t * data) {
}
// reads: for now we only reply to version and remote temperature
// empty message back if temperature not set or unknown message type
if (data[2] == 0x02) {
if (data[2] == EMSdevice::EMS_TYPE_VERSION) {
version(addr, data[0]);
} else if (remotetemp_[hc] == EMS_VALUE_SHORT_NOTSET) {
} else if (length == 5 && remotetemp_[hc] == EMS_VALUE_SHORT_NOTSET) {
unknown(addr, data[0], data[2], data[3]);
} else if (length == 7 && remotetemp_[hc] == EMS_VALUE_SHORT_NOTSET) {
unknown(addr, data[0], data[3], data[5], data[6]);
} else if (data[2] == 0xAF && data[3] == 0) {
temperature(addr, data[0]);
} else {
temperature(addr, data[0], hc);
} else if (length == 7 && data[2] == 0xFF && data[3] == 0 && data[5] == 0 && data[6] == 0x23) { // Junkers
temperature(addr, data[0], hc);
} else if (length == 7 && data[2] == 0xFF && data[3] == 0 && data[5] == 3 && data[6] == 0x2B) { // EMS+ temperature
temperature(addr, data[0], hc);
} else if (length == 7 && data[2] == 0xFF && data[3] == 0 && data[5] == 3 && data[6] == 0x7B && remotehum_[hc] != EMS_VALUE_UINT_NOTSET) { // EMS+ humidity
humidity(addr, data[0], hc);
} else if (length == 5) { // ems query
unknown(addr, data[0], data[2], data[3]);
} else if (length == 7 && data[2] == 0xFF) { // ems+ query
unknown(addr, data[0], data[3], data[5], data[6]);
}
}
@@ -103,9 +172,9 @@ void Roomctrl::version(uint8_t addr, uint8_t dst) {
data[1] = dst;
data[2] = 0x02;
data[3] = 0;
data[4] = 113; // set RC20 id 113, Ver 02.01
data[5] = 0x02;
data[6] = 0x01;
data[4] = type_; // set RC20 id 113, Ver 02.01 or Junkers FB10 id 109, Ver 16.05, RC100H id 200 ver 40.04
data[5] = type_ == RC20 ? 2 : type_ == FB10 ? 16 : 40;
data[6] = type_ == RC20 ? 1 : type_ == FB10 ? 5 : 4;
data[7] = EMSbus::calculate_crc(data, 7); // apppend CRC
EMSuart::transmit(data, 8);
}
@@ -123,22 +192,83 @@ void Roomctrl::unknown(uint8_t addr, uint8_t dst, uint8_t type, uint8_t offset)
EMSuart::transmit(data, 5);
}
void Roomctrl::unknown(uint8_t addr, uint8_t dst, uint8_t offset, uint8_t typeh, uint8_t typel) {
uint8_t data[10];
data[0] = addr;
data[1] = dst;
data[2] = 0xFF;
data[3] = offset;
data[4] = typeh;
data[5] = typel;
data[6] = EMSbus::calculate_crc(data, 6); // apppend CRC
EMSuart::transmit(data, 7);
}
/**
* send the room temperature in message 0xAF
*/
void Roomctrl::temperature(uint8_t addr, uint8_t dst) {
void Roomctrl::temperature(uint8_t addr, uint8_t dst, uint8_t hc) {
uint8_t data[10];
uint8_t hc = addr - ADDR;
data[0] = addr;
data[1] = dst;
data[2] = 0xAF;
data[3] = 0;
data[4] = (uint8_t)(remotetemp_[hc] >> 8);
data[5] = (uint8_t)(remotetemp_[hc] & 0xFF);
data[6] = 0;
data[7] = EMSbus::calculate_crc(data, 7); // apppend CRC
EMSuart::transmit(data, 8);
data[0] = addr;
data[1] = dst;
if (type_ == RC20) { // RC20, telegram 0xAF
data[2] = 0xAF;
data[3] = 0;
data[4] = (uint8_t)(remotetemp_[hc] >> 8);
data[5] = (uint8_t)(remotetemp_[hc] & 0xFF);
data[6] = 0;
data[7] = EMSbus::calculate_crc(data, 7); // apppend CRC
EMSuart::transmit(data, 8);
} else if (type_ == FB10) { // Junkers FB10, telegram 0x0123
data[2] = 0xFF;
data[3] = 0;
data[4] = 0;
data[5] = 0x23; // is this always 0x23 or count with hc?
data[6] = (uint8_t)(remotetemp_[hc] >> 8);
data[7] = (uint8_t)(remotetemp_[hc] & 0xFF);
data[8] = EMSbus::calculate_crc(data, 8); // apppend CRC
EMSuart::transmit(data, 9);
} else if (type_ == RC100H) { // RC100H, telegram 42B
data[2] = 0xFF;
data[3] = 0;
data[4] = 3;
data[5] = 0x2B;
data[6] = (uint8_t)(remotetemp_[hc] >> 8);
data[7] = (uint8_t)(remotetemp_[hc] & 0xFF);
data[8] = EMSbus::calculate_crc(data, 8); // apppend CRC
EMSuart::transmit(data, 9);
} else if (type_ == SENSOR) { // wireless sensor, broadcast id 435
data[2] = 0xFF;
data[3] = 0;
data[4] = 3;
data[5] = 0x35;
data[6] = (uint8_t)(remotetemp_[hc] >> 8);
data[7] = (uint8_t)(remotetemp_[hc] & 0xFF);
data[8] = EMSbus::calculate_crc(data, 8); // apppend CRC
EMSuart::transmit(data, 9);
}
}
// send telegram 0x047B only for RC100H
void Roomctrl::humidity(uint8_t addr, uint8_t dst, uint8_t hc) {
uint8_t data[11];
data[0] = addr;
data[1] = dst;
uint16_t dew = calc_dew(remotetemp_[hc], remotehum_[hc]);
if (type_ == RC100H) { // RC100H, telegram 47B
data[2] = 0xFF;
data[3] = 0;
data[4] = 3;
data[5] = 0x7B;
data[6] = dew == EMS_VALUE_SHORT_NOTSET ? EMS_VALUE_INT_NOTSET : (uint8_t)((dew + 5) / 10);
data[7] = remotehum_[hc];
data[8] = (uint8_t)(dew << 8);
data[9] = (uint8_t)(dew & 0xFF);
data[10] = EMSbus::calculate_crc(data, 10); // apppend CRC
EMSuart::transmit(data, 11);
}
}
/**
* send a nack if someone want to write to us.
*/
@@ -148,4 +278,17 @@ void Roomctrl::nack_write() {
EMSuart::transmit(data, 1);
}
int16_t Roomctrl::calc_dew(int16_t temp, uint8_t humi) {
if (humi == EMS_VALUE_UINT_NOTSET || temp == EMS_VALUE_SHORT_NOTSET) {
return EMS_VALUE_SHORT_NOTSET;
}
const float k2 = 17.62;
const float k3 = 243.12;
const float t = (float)temp / 10;
const float h = (float)humi / 100;
int16_t dt = (10 * k3 * (((k2 * t) / (k3 + t)) + log(h)) / (((k2 * k3) / (k3 + t)) - log(h)));
return dt;
}
} // namespace emsesp

View File

@@ -25,22 +25,31 @@ namespace emsesp {
class Roomctrl {
public:
static void send(const uint8_t addr);
static void check(const uint8_t addr, const uint8_t * data);
static void set_remotetemp(const uint8_t hc, const int16_t temp);
static void check(const uint8_t addr, const uint8_t * data, const uint8_t length);
static void set_remotetemp(const uint8_t type, const uint8_t hc, const int16_t temp);
static void set_remotehum(const uint8_t type, const uint8_t hc, const int8_t hum);
enum : uint8_t { RC20 = 113, FB10 = 109, RC100H = 200, SENSOR = 0x40 };
private:
static constexpr uint8_t ADDR = 0x18;
static constexpr uint8_t ADDR = 0x18; // address for hc1
static constexpr uint32_t SEND_INTERVAL = 60000; // 1 minute
static constexpr uint8_t HCS = 4; // max 4 heating circuits
static void version(uint8_t addr, uint8_t dst);
static void unknown(uint8_t addr, uint8_t dst, uint8_t type, uint8_t offset);
static void temperature(uint8_t addr, uint8_t dst);
static void nack_write();
static uint8_t get_hc(const uint8_t addr);
static void version(uint8_t addr, uint8_t dst);
static void unknown(uint8_t addr, uint8_t dst, uint8_t type, uint8_t offset);
static void unknown(uint8_t addr, uint8_t dst, uint8_t offset, uint8_t typeh, uint8_t typel);
static void temperature(uint8_t addr, uint8_t dst, uint8_t hc);
static void humidity(uint8_t addr, uint8_t dst, uint8_t hc);
static void nack_write();
static int16_t calc_dew(int16_t temp, uint8_t hum);
static bool switch_off_[HCS];
static uint32_t rc_time_[HCS];
static int16_t remotetemp_[HCS];
static uint8_t remotehum_[HCS];
static uint8_t sendcnt[HCS];
static uint8_t type_; // type is product-id 113 for RC20 or 109 for Junkers FB10
};
} // namespace emsesp

View File

@@ -113,7 +113,7 @@ void Shower::loop() {
doc["timestamp"] = dt;
}
Mqtt::queue_publish("shower_data", doc.as<JsonObject>());
LOG_INFO("finished with duration %lu seconds", duration_);
LOG_INFO("finished with duration %lu seconds", duration_ / 1000UL);
}
}

View File

@@ -385,13 +385,13 @@ void System::wifi_tweak() {
}
// check for valid ESP32 pins. This is very dependent on which ESP32 board is being used.
// Typically you can't use 1, 6-11, 12, 14, 15, 20, 24, 28-31 and 40+
// Typically you can't use 1, 6-11, 20, 24, 28-31 and 40+
// we allow 0 as it has a special function on the NodeMCU apparently
// See https://diyprojects.io/esp32-how-to-use-gpio-digital-io-arduino-code/#.YFpVEq9KhjG
// and https://nodemcu.readthedocs.io/en/dev-esp32/modules/gpio/
bool System::is_valid_gpio(uint8_t pin) {
#if CONFIG_IDF_TARGET_ESP32 || EMSESP_STANDALONE
if ((pin == 1) || (pin >= 6 && pin <= 12) || (pin >= 14 && pin <= 15) || (pin == 20) || (pin == 24) || (pin >= 28 && pin <= 31) || (pin > 40)) {
if ((pin == 1) || (pin >= 6 && pin <= 11) || (pin == 20) || (pin == 24) || (pin >= 28 && pin <= 31) || (pin > 40)) {
#elif CONFIG_IDF_TARGET_ESP32S2
if ((pin >= 19 && pin <= 20) || (pin >= 22 && pin <= 32) || (pin > 40)) {
#elif CONFIG_IDF_TARGET_ESP32C3
@@ -475,15 +475,16 @@ void System::button_init(bool refresh) {
reload_settings();
}
if (is_valid_gpio(pbutton_gpio_)) {
if (!myPButton_.init(pbutton_gpio_, HIGH)) {
LOG_DEBUG("Multi-functional button not detected");
} else {
LOG_DEBUG("Multi-functional button enabled");
}
} else {
if (!is_valid_gpio(pbutton_gpio_)) {
LOG_WARNING("Invalid button GPIO. Check config.");
myPButton_.init(255, HIGH); // disable
return;
}
if (!myPButton_.init(pbutton_gpio_, HIGH)) {
LOG_WARNING("Multi-functional button not detected");
return;
}
LOG_DEBUG("Multi-functional button enabled");
myPButton_.onClick(BUTTON_Debounce, button_OnClick);
myPButton_.onDblClick(BUTTON_DblClickDelay, button_OnDblClick);
@@ -541,19 +542,26 @@ void System::loop() {
led_monitor(); // check status and report back using the LED
system_check(); // check system health
send_info_mqtt();
#endif
}
// send MQTT info topic appended with the version information as JSON, as a retained flag
void System::send_info_mqtt(const char * event_str, bool send_ntp) {
// use dynamic json because it is called from NTP-callback from lwip task with small stack
DynamicJsonDocument doc = DynamicJsonDocument(EMSESP_JSON_SIZE_MEDIUM);
doc["event"] = event_str;
doc["version"] = EMSESP_APP_VERSION;
void System::send_info_mqtt() {
static uint8_t _connection = 0;
uint8_t connection = (ethernet_connected() ? 1 : 0) + ((WiFi.status() == WL_CONNECTED) ? 2 : 0) + (ntp_connected_ ? 4 : 0) + (has_ipv6_ ? 8 : 0);
// check if connection status has changed
if (!Mqtt::connected() || connection == _connection) {
return;
}
_connection = connection;
StaticJsonDocument<EMSESP_JSON_SIZE_MEDIUM> doc;
doc["event"] = "connected";
doc["version"] = EMSESP_APP_VERSION;
// if NTP is enabled send the boot_time in local time in ISO 8601 format (eg: 2022-11-15 20:46:38)
// https://github.com/emsesp/EMS-ESP32/issues/751
if (send_ntp) {
if (ntp_connected_) {
char time_string[25];
time_t now = time(nullptr) - uuid::get_uptime_sec();
strftime(time_string, 25, "%FT%T%z", localtime(&now));
@@ -1057,7 +1065,7 @@ bool System::check_upgrade(bool factory_settings) {
// see if we're missing a version, will be < 3.5.0b13 from Dec 23 2022
missing_version = (settingsVersion.empty() || (settingsVersion.length() < 5));
if (missing_version) {
LOG_DEBUG("No version information found (%s)", settingsVersion.c_str());
LOG_WARNING("No version information found (%s)", settingsVersion.c_str());
settingsVersion = "3.6.2"; // this was the last stable version
}
}
@@ -1200,6 +1208,9 @@ bool System::command_info(const char * value, const int8_t id, JsonObject & outp
}
#endif
EMSESP::esp8266React.getNetworkSettingsService()->read([&](NetworkSettings & settings) {
if (WiFi.status() == WL_CONNECTED && !settings.bssid.isEmpty()) {
node["BSSID"] = "set";
}
node["static ip config"] = settings.staticIPConfig;
node["enable IPv6"] = settings.enableIPv6;
node["low bandwidth"] = settings.bandwidth20;
@@ -1430,6 +1441,8 @@ bool System::load_board_profile(std::vector<int8_t> & data, const std::string &
data = {2, 18, 23, 5, 0, PHY_type::PHY_TYPE_NONE, 0, 0, 0}; // BBQKees Gateway S32
} else if (board_profile == "E32") {
data = {2, 4, 5, 17, 33, PHY_type::PHY_TYPE_LAN8720, 16, 1, 0}; // BBQKees Gateway E32
} else if (board_profile == "E32V2") {
data = {2, 14, 4, 5, 12, PHY_type::PHY_TYPE_LAN8720, 15, 0, 1}; // BBQKees Gateway E32 V2
} else if (board_profile == "MH-ET") {
data = {2, 18, 23, 5, 0, PHY_type::PHY_TYPE_NONE, 0, 0, 0}; // MH-ET Live D1 Mini
} else if (board_profile == "NODEMCU") {

View File

@@ -79,7 +79,7 @@ class System {
bool check_restore();
bool heartbeat_json(JsonObject & output);
void send_heartbeat();
void send_info_mqtt(const char * event_str, bool send_ntp = false);
void send_info_mqtt();
bool syslog_enabled() {
return syslog_enabled_;
@@ -181,6 +181,10 @@ class System {
ethernet_connected_ = b;
}
void has_ipv6(bool b) {
has_ipv6_ = b;
}
void ntp_connected(bool b);
bool ntp_connected();
@@ -297,6 +301,7 @@ class System {
bool upload_status_ = false; // true if we're in the middle of a OTA firmware upload
bool ethernet_connected_ = false;
bool has_ipv6_ = false;
bool ntp_connected_ = false;
uint32_t ntp_last_check_ = 0;

View File

@@ -113,7 +113,8 @@ class Telegram {
return (val != value);
}
// read a value from a telegram. We always store the value, regardless if its garbage
// read a value from a telegram if its not out of bounds.
// Then we update the value, regardless if its garbage
template <typename Value>
// assuming negative numbers are stored as 2's-complement
// https://medium.com/@LeeJulija/how-integers-are-stored-in-memory-using-twos-complement-5ba04d61a56c
@@ -124,6 +125,20 @@ class Telegram {
uint8_t num_bytes = (!s) ? sizeof(Value) : s;
// check for out of bounds, if so don't modify the value
auto msg_size = (index - this->offset + num_bytes - 1);
#ifdef EMSESP_DEBUG
// Serial.print(" index: ");
// Serial.print(index);
// Serial.print(" offset: ");
// Serial.print(offset);
// Serial.print(" index: ");
// Serial.print(" message_length: ");
// Serial.print(this->message_length);
// Serial.print(" msg_size: ");
// Serial.print(msg_size);
// Serial.println();
#endif
if ((index < this->offset) || (msg_size >= this->message_length) || (msg_size > EMS_MAX_TELEGRAM_MESSAGE_LENGTH)) {
return false;
}

View File

@@ -74,6 +74,18 @@ bool Test::run_test(const char * command, int8_t id) {
//
#ifdef EMSESP_STANDALONE
if (strcmp(command, "heat_exchange") == 0) {
EMSESP::logger().info("Testing heating exchange...");
add_device(0x08, 219); // Greenstar HIU/Logamax kompakt WS170
// [emsesp] boiler(0x08) -W-> Me(0x0B), UBAMonitorFastPlus(0xE4), data: 00 01 35 00 00 00 00 00 00 00 00 80 00 00 00 00 00 00 00 80 00 (offset 6)
uart_telegram({0x08, 0x00, 0xE4, 0x00, //
00, 01, 0x35, 00, 00, 00, 00, 00, 00, 00, 00, 0x80, 00, 00, 00, 00, 00, 00, 00, 0x80, 00});
return true;
}
if (strcmp(command, "2thermostats") == 0) {
EMSESP::logger().info("Testing with multiple thermostats...");
@@ -493,16 +505,24 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
shell.printfln("Testing RC310...");
run_test("310");
shell.invoke_command("show devices");
shell.invoke_command("show");
shell.invoke_command("show values");
shell.invoke_command("call system publish");
shell.invoke_command("show mqtt");
ok = true;
}
if (command == "heat_exchange") {
shell.printfln("Testing heat exchange...");
run_test("heat_exchange");
shell.invoke_command("show devices");
shell.invoke_command("show values");
ok = true;
}
if (command == "2thermostats") {
shell.printfln("Testing multiple thermostats...");
run_test("2thermostats");
shell.invoke_command("show");
shell.invoke_command("show values");
shell.invoke_command("show devices");
ok = true;
}
@@ -571,7 +591,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
run_test("boiler");
shell.invoke_command("show devices");
shell.invoke_command("show");
shell.invoke_command("show values");
shell.invoke_command("call boiler info");
shell.invoke_command("call system publish");
@@ -610,7 +630,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
// HC1
uart_telegram({0x90, 0x00, 0xFF, 0x00, 0x00, 0x6F, 0x01, 0x02, 0x00, 0xCF, 0x00, 0xE6});
shell.invoke_command("show");
shell.invoke_command("show values");
shell.invoke_command("show devices");
ok = true;
}
@@ -653,7 +673,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
uart_telegram(
{0x08, 0x0B, 0xC2, 0, 0x08, 0xAC, 00, 0x10, 0x31, 0x48, 0x30, 0x31, 0x15, 0x80, 0x95, 0x0B, 0x0E, 0x10, 0x38, 00, 0x7F, 0xFF, 0xFF, 0xFF});
// shell.invoke_command("show");
// shell.invoke_command("show values");
ok = true;
}
@@ -686,12 +706,12 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
// Mqtt::nested_format(0);
emsesp::EMSESP::temperaturesensor_.test();
shell.invoke_command("show");
shell.invoke_command("show values");
shell.invoke_command("call system publish");
// rename
EMSESP::temperaturesensor_.update("01-0203-0405-0607", "testtemperature", 2);
shell.invoke_command("show");
shell.invoke_command("show values");
shell.invoke_command("call system publish");
ok = true;
}
@@ -704,14 +724,14 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
// Mqtt::nested_format(0);
emsesp::EMSESP::analogsensor_.test();
shell.invoke_command("show");
shell.invoke_command("show values");
// shell.invoke_command("call system publish");
// shell.invoke_command("show mqtt");
// rename
// bool update(uint8_t id, const std::string & name, int16_t offset, float factor, uint8_t uom, uint8_t type);
EMSESP::analogsensor_.update(36, "analogtest", 2, 0.7, 17, 1);
shell.invoke_command("show");
shell.invoke_command("show values");
// shell.invoke_command("call system publish");
ok = true;
}
@@ -1174,7 +1194,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
shell.printfln("Testing adding a thermostat FW120...");
run_test("thermostat");
shell.invoke_command("show");
shell.invoke_command("show values");
shell.invoke_command("call system publish");
EMSESP::mqtt_.incoming("ems-esp/thermostat_hc1", "heat");
@@ -1243,7 +1263,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
uart_telegram("30 00 FF 0A 02 6A 03"); // SM100 pump off 0
shell.invoke_command("show");
shell.invoke_command("show values");
ok = true;
}
@@ -1676,7 +1696,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
// 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("show values");
shell.invoke_command("call mixer info");
shell.invoke_command("call system publish");
shell.invoke_command("show mqtt");

View File

@@ -56,6 +56,8 @@ namespace emsesp {
// #define EMSESP_DEBUG_DEFAULT "memory"
// #define EMSESP_DEBUG_DEFAULT "coldshot"
// #define EMSESP_DEBUG_DEFAULT "custom_entities"
// #define EMSESP_DEBUG_DEFAULT "heat_exchange"
class Test {
public:

View File

@@ -1 +1 @@
#define EMSESP_APP_VERSION "3.6.3-dev.2"
#define EMSESP_APP_VERSION "3.6.3-dev.5a"

View File

@@ -88,6 +88,7 @@ void WebDataService::core_data(AsyncWebServerRequest * request) {
obj["d"] = emsdevice->device_id(); // deviceid
obj["p"] = emsdevice->product_id(); // productid
obj["v"] = emsdevice->version(); // version
obj["e"] = emsdevice->count_entities(); // number of entities
}
}
@@ -102,6 +103,7 @@ void WebDataService::core_data(AsyncWebServerRequest * request) {
obj["d"] = 0; // deviceid
obj["p"] = 0; // productid
obj["v"] = 0; // version
obj["e"] = EMSESP::webCustomEntityService.count_entities(); // number of custom entities
}
root["connected"] = EMSESP::bus_status() != 2;

View File

@@ -174,17 +174,21 @@ char * WebLogService::messagetime(char * out, const uint64_t t, const size_t buf
strlcpy(out, uuid::log::format_timestamp_ms(t, 3).c_str(), bufsize);
} else {
time_t t1 = time_offset_ + t / 1000ULL;
strftime(out, bufsize, "%F %T", localtime(&t1));
snprintf(out, bufsize, "%s.%03d", out, (uint16_t)(t % 1000));
char timestr[bufsize];
strftime(timestr, bufsize, "%F %T", localtime(&t1));
snprintf(out, bufsize, "%s.%03d", timestr, (uint16_t)(t % 1000));
}
return out;
}
// send to web eventsource
void WebLogService::transmit(const QueuedLogMessage & message) {
StaticJsonDocument<EMSESP_JSON_SIZE_MEDIUM> jsonDocument;
JsonObject logEvent = jsonDocument.to<JsonObject>();
char time_string[25];
DynamicJsonDocument jsonDocument(EMSESP_JSON_SIZE_LARGE);
if (jsonDocument.capacity() == 0) {
return;
}
JsonObject logEvent = jsonDocument.to<JsonObject>();
char time_string[25];
logEvent["t"] = messagetime(time_string, message.content_->uptime_ms, sizeof(time_string));
logEvent["l"] = message.content_->level;

View File

@@ -99,30 +99,31 @@ StateUpdateResult WebSettings::update(JsonObject & root, WebSettings & settings)
#endif
if (!System::load_board_profile(data, settings.board_profile.c_str())) {
// unknown, check for ethernet, use default E32/S32
// data is led, dallas, rx, tx, pbutton, phy, eth_power, eth_addr, eth_clock
#ifndef EMSESP_STANDALONE
#if CONFIG_IDF_TARGET_ESP32
if (ETH.begin((eth_phy_type_t)1, 16, 23, 18, ETH_PHY_LAN8720, ETH_CLOCK_GPIO0_IN)) {
// BBQKees Gateway E32
data = {EMSESP_DEFAULT_LED_GPIO, 4, 5, 17, 33, PHY_type::PHY_TYPE_LAN8720, 16, 1, 0};
settings.board_profile = "E32";
} else
#endif
#endif
{
// BBQKees Gateway S32
data = {EMSESP_DEFAULT_LED_GPIO,
EMSESP_DEFAULT_DALLAS_GPIO,
EMSESP_DEFAULT_RX_GPIO,
EMSESP_DEFAULT_TX_GPIO,
EMSESP_DEFAULT_PBUTTON_GPIO,
EMSESP_DEFAULT_PHY_TYPE,
0,
0,
0};
settings.board_profile = "S32";
// unknown, check for NVS or scan for ethernet, use default E32/E32V2/S32
#if CONFIG_IDF_TARGET_ESP32 && !defined(EMSESP_STANDALONE)
settings.board_profile = EMSESP::nvs_.getString("boot");
if (!System::load_board_profile(data, settings.board_profile.c_str())) {
if (settings.board_profile == "") { // empty: new test
if (ETH.begin((eth_phy_type_t)1, 16, 23, 18, ETH_PHY_LAN8720, ETH_CLOCK_GPIO0_IN)) {
EMSESP::nvs_.putString("boot", "E32");
} else {
EMSESP::nvs_.putString("boot", "Test");
}
} else if (settings.board_profile == "Test") {
if (ETH.begin((eth_phy_type_t)0, 15, 23, 18, ETH_PHY_LAN8720, ETH_CLOCK_GPIO0_OUT)) {
EMSESP::nvs_.putString("boot", "E32V2");
} else {
EMSESP::nvs_.putString("boot", "S32");
}
} else {
EMSESP::nvs_.putString("boot", "S32");
}
ESP.restart();
}
#else
settings.board_profile = "S32";
System::load_board_profile(data, settings.board_profile.c_str());
#endif
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());

View File

@@ -36,15 +36,13 @@ void WebStatusService::WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info) {
switch (event) {
case ARDUINO_EVENT_WIFI_STA_DISCONNECTED:
EMSESP::logger().warning("WiFi disconnected. Reason code=%s", disconnectReason(info.wifi_sta_disconnected.reason)); // IDF 4.0
WiFi.disconnect(true);
EMSESP::logger().warning("WiFi disconnected. Reason: %s (%d)", disconnectReason(info.wifi_sta_disconnected.reason), info.wifi_sta_disconnected.reason); // IDF 4.0
EMSESP::system_.has_ipv6(false);
break;
case ARDUINO_EVENT_WIFI_STA_GOT_IP:
EMSESP::logger().info("WiFi connected with IP=%s, hostname=%s", WiFi.localIP().toString().c_str(), WiFi.getHostname());
// EMSESP::system_.syslog_init();
mDNS_start();
EMSESP::system_.send_info_mqtt("connected");
break;
case ARDUINO_EVENT_ETH_START:
@@ -64,22 +62,21 @@ void WebStatusService::WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info) {
// prevent double calls
if (!EMSESP::system_.ethernet_connected()) {
EMSESP::logger().info("Ethernet connected with IP=%s, speed %d Mbps", ETH.localIP().toString().c_str(), ETH.linkSpeed());
// EMSESP::system_.send_heartbeat();
// EMSESP::system_.syslog_init();
EMSESP::system_.ethernet_connected(true);
mDNS_start();
EMSESP::system_.send_info_mqtt("connected");
}
break;
case ARDUINO_EVENT_ETH_DISCONNECTED:
EMSESP::logger().warning("Ethernet disconnected");
EMSESP::system_.ethernet_connected(false);
EMSESP::system_.has_ipv6(false);
break;
case ARDUINO_EVENT_ETH_STOP:
EMSESP::logger().info("Ethernet stopped");
EMSESP::system_.ethernet_connected(false);
EMSESP::system_.has_ipv6(false);
break;
case ARDUINO_EVENT_WIFI_STA_CONNECTED:
@@ -105,9 +102,8 @@ void WebStatusService::WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info) {
} else {
EMSESP::logger().info("WiFi connected with IPv6=%s, hostname=%s", WiFi.localIPv6().toString().c_str(), WiFi.getHostname());
}
// EMSESP::system_.syslog_init();
mDNS_start();
EMSESP::system_.send_info_mqtt("connected");
EMSESP::system_.has_ipv6(true);
break;
default:
@@ -151,7 +147,7 @@ void WebStatusService::webStatusService(AsyncWebServerRequest * request) {
if (EMSESP::sensor_enabled()) {
statJson = statsJson.createNestedObject();
statJson["id"] = 3;
statJson["s"] = EMSESP::temperaturesensor_.reads();
statJson["s"] = EMSESP::temperaturesensor_.reads() - EMSESP::temperaturesensor_.fails();
statJson["f"] = EMSESP::temperaturesensor_.fails();
statJson["q"] =
EMSESP::temperaturesensor_.reads() == 0 ? 100 : 100 - (uint8_t)((100 * EMSESP::temperaturesensor_.fails()) / EMSESP::temperaturesensor_.reads());
@@ -159,7 +155,7 @@ void WebStatusService::webStatusService(AsyncWebServerRequest * request) {
if (EMSESP::analog_enabled()) {
statJson = statsJson.createNestedObject();
statJson["id"] = 4;
statJson["s"] = EMSESP::analogsensor_.reads();
statJson["s"] = EMSESP::analogsensor_.reads() - EMSESP::analogsensor_.fails();
statJson["f"] = EMSESP::analogsensor_.fails();
statJson["q"] = EMSESP::analogsensor_.reads() == 0 ? 100 : 100 - (uint8_t)((100 * EMSESP::analogsensor_.fails()) / EMSESP::analogsensor_.reads());
}
@@ -285,6 +281,16 @@ const char * WebStatusService::disconnectReason(uint8_t code) {
return "assoc fail";
case WIFI_REASON_HANDSHAKE_TIMEOUT: // = 204,
return "handshake timeout";
case WIFI_REASON_CONNECTION_FAIL: // 205,
return "connection fail";
case WIFI_REASON_AP_TSF_RESET: // 206,
return "AP tsf reset";
case WIFI_REASON_ROAMING: // 207,
return "roaming";
case WIFI_REASON_ASSOC_COMEBACK_TIME_TOO_LONG: // 208,
return "assoc comeback time too long";
case WIFI_REASON_SA_QUERY_TIMEOUT: // 209,
return "sa query timeout";
default:
return "unknown";
}