mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2025-12-06 15:59:52 +03:00
Merge remote-tracking branch 'origin/dev' into main
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -25,6 +25,6 @@ emsesp
|
|||||||
/data/www
|
/data/www
|
||||||
/lib/framework/WWWData.h
|
/lib/framework/WWWData.h
|
||||||
/interface/build
|
/interface/build
|
||||||
/interface/node_modules
|
node_modules
|
||||||
/interface/.eslintcache
|
/interface/.eslintcache
|
||||||
|
|
||||||
|
|||||||
24
CHANGELOG.md
24
CHANGELOG.md
@@ -5,6 +5,30 @@ All notable changes to this project will be documented in this file.
|
|||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
## [3.1.0] May 4 2021
|
||||||
|
|
||||||
|
- Mock API to simulate an ESP, for testing web
|
||||||
|
- Able to write values from the Web UI
|
||||||
|
- check values with `"cmd":<valuename>` and data empty or `?`
|
||||||
|
- set hc for values and commands by id or prefix `hc<x>`+separator, separator can be any char
|
||||||
|
|
||||||
|
## Fixed
|
||||||
|
|
||||||
|
- Don't create Home Assistant MQTT discovery entries for device values that don't exists (#756 on EMS-ESP repo)
|
||||||
|
- Update shower MQTT when a shower start is detected
|
||||||
|
- S32 board profile
|
||||||
|
|
||||||
|
## Changed
|
||||||
|
|
||||||
|
- Icon for Network
|
||||||
|
- MQTT Formatting payload (nested vs single) is a pull-down option
|
||||||
|
- moved mqtt-topics and texts to local_EN, all topics lower case
|
||||||
|
- Re-enabled Shower Alert (still experimental)
|
||||||
|
- lowercased Flow temp in commands
|
||||||
|
- system console commands to main
|
||||||
|
|
||||||
|
## Removed
|
||||||
|
|
||||||
## [3.0.1] March 30 2021
|
## [3.0.1] March 30 2021
|
||||||
|
|
||||||
## Added
|
## Added
|
||||||
|
|||||||
@@ -2,7 +2,13 @@
|
|||||||
|
|
||||||
**EMS-ESP** is an open-source firmware for the Espressif ESP8266 and ESP32 microcontroller that communicates with **EMS** (Energy Management System) based equipment from manufacturers like Bosch, Buderus, Nefit, Junkers, Worcester and Sieger.
|
**EMS-ESP** is an open-source firmware for the Espressif ESP8266 and ESP32 microcontroller that communicates with **EMS** (Energy Management System) based equipment from manufacturers like Bosch, Buderus, Nefit, Junkers, Worcester and Sieger.
|
||||||
|
|
||||||
This is the firmware for the ESP32.
|
This is the firmware for the ESP32. Compared to version 2 on the ESP8266, this version has
|
||||||
|
- Ethernet Support
|
||||||
|
- Pre-configured board layouts
|
||||||
|
- Writing values directly from the Web UI
|
||||||
|
- Mock API server for faster offline development
|
||||||
|
- Expose to more commands, via MQTT
|
||||||
|
- Improvements to Dallas sensors, Shower service
|
||||||
|
|
||||||
[](https://github.com/emsesp/EMS-ESP32/blob/main/CHANGELOG.md)
|
[](https://github.com/emsesp/EMS-ESP32/blob/main/CHANGELOG.md)
|
||||||
[](https://github.com/emsesp/EMS-ESP32/commits/main)
|
[](https://github.com/emsesp/EMS-ESP32/commits/main)
|
||||||
|
|||||||
@@ -1,11 +1,5 @@
|
|||||||
# Change the IP address to that of your ESP device to enable local development of the UI.
|
# Change the IP address to that of your ESP device to enable local development of the UI
|
||||||
# Remember to also enable CORS in platformio.ini before uploading the code to the device
|
|
||||||
# with -DENABLE_CORS
|
|
||||||
|
|
||||||
# my Wifi
|
# REACT_APP_HTTP_ROOT=http://localhost:3000
|
||||||
#REACT_APP_HTTP_ROOT=http://10.10.10.101
|
# REACT_APP_WEB_SOCKET_ROOT=ws://localhost:3000
|
||||||
#REACT_APP_WEB_SOCKET_ROOT=ws://10.10.10.101
|
|
||||||
|
|
||||||
# my Ethernet
|
|
||||||
REACT_APP_HTTP_ROOT=http://192.168.1.134
|
|
||||||
REACT_APP_WEB_SOCKET_ROOT=ws://http://192.168.1.134
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ const fs = require('fs');
|
|||||||
|
|
||||||
module.exports = function override(config, env) {
|
module.exports = function override(config, env) {
|
||||||
if (env === "production") {
|
if (env === "production") {
|
||||||
// rename the ouput file, we need it's path to be short, for SPIFFS
|
// rename the output file, we need it's path to be short for LittleFS
|
||||||
config.output.filename = 'js/[id].[chunkhash:4].js';
|
config.output.filename = 'js/[id].[chunkhash:4].js';
|
||||||
config.output.chunkFilename = 'js/[id].[chunkhash:4].js';
|
config.output.chunkFilename = 'js/[id].[chunkhash:4].js';
|
||||||
|
|
||||||
|
|||||||
10575
interface/package-lock.json
generated
10575
interface/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -3,37 +3,40 @@
|
|||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@material-ui/core": "^4.11.3",
|
"@material-ui/core": "^4.11.4",
|
||||||
"@material-ui/icons": "^4.11.2",
|
"@material-ui/icons": "^4.11.2",
|
||||||
"@types/lodash": "^4.14.168",
|
"@types/lodash": "^4.14.168",
|
||||||
"@types/node": "^12.20.4",
|
"@types/node": "^15.0.1",
|
||||||
"@types/react": "^17.0.3",
|
"@types/react": "^17.0.4",
|
||||||
"@types/react-dom": "^17.0.1",
|
"@types/react-dom": "^17.0.3",
|
||||||
"@types/react-material-ui-form-validator": "^2.1.0",
|
"@types/react-material-ui-form-validator": "^2.1.0",
|
||||||
"@types/react-router": "^5.1.12",
|
"@types/react-router": "^5.1.13",
|
||||||
"@types/react-router-dom": "^5.1.6",
|
"@types/react-router-dom": "^5.1.7",
|
||||||
"compression-webpack-plugin": "^4.0.0",
|
"compression-webpack-plugin": "^5.0.2",
|
||||||
|
"express": "^4.17.1",
|
||||||
"jwt-decode": "^3.1.2",
|
"jwt-decode": "^3.1.2",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"mime-types": "^2.1.29",
|
"mime-types": "^2.1.30",
|
||||||
"notistack": "^1.0.5",
|
"notistack": "^1.0.6",
|
||||||
"parse-ms": "^2.1.0",
|
"parse-ms": "^3.0.0",
|
||||||
"react": "^17.0.1",
|
"react": "^17.0.2",
|
||||||
"react-dom": "^17.0.1",
|
"react-dom": "^17.0.2",
|
||||||
"react-dropzone": "^11.3.1",
|
"react-dropzone": "^11.3.2",
|
||||||
"react-form-validator-core": "^1.1.1",
|
"react-form-validator-core": "^1.1.1",
|
||||||
"react-material-ui-form-validator": "^2.1.4",
|
"react-material-ui-form-validator": "^2.1.4",
|
||||||
"react-router": "^5.2.0",
|
"react-router": "^5.2.0",
|
||||||
"react-router-dom": "^5.2.0",
|
"react-router-dom": "^5.2.0",
|
||||||
"react-scripts": "4.0.1",
|
"react-scripts": "4.0.3",
|
||||||
"sockette": "^2.0.6",
|
"sockette": "^2.0.6",
|
||||||
"typescript": "4.0.5",
|
"typescript": "4.2.4",
|
||||||
"zlib": "^1.0.5"
|
"zlib": "^1.0.5"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "react-app-rewired start",
|
"start": "react-app-rewired start",
|
||||||
"build": "react-app-rewired build",
|
"build": "react-app-rewired build",
|
||||||
"eject": "react-scripts eject"
|
"eject": "react-scripts eject",
|
||||||
|
"mock-api": "nodemon --watch ../mock-api ../mock-api/server.js",
|
||||||
|
"dev": "run-p start mock-api"
|
||||||
},
|
},
|
||||||
"eslintConfig": {
|
"eslintConfig": {
|
||||||
"extends": "react-app"
|
"extends": "react-app"
|
||||||
@@ -51,6 +54,10 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"concurrently": "^6.0.1",
|
||||||
|
"http-proxy-middleware": "^1.1.1",
|
||||||
|
"nodemon": "^2.0.7",
|
||||||
|
"npm-run-all": "^4.1.5",
|
||||||
"react-app-rewired": "^2.1.8"
|
"react-app-rewired": "^2.1.8"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,5 +18,6 @@ export const SYSTEM_STATUS_ENDPOINT = ENDPOINT_ROOT + "systemStatus";
|
|||||||
export const SIGN_IN_ENDPOINT = ENDPOINT_ROOT + "signIn";
|
export const SIGN_IN_ENDPOINT = ENDPOINT_ROOT + "signIn";
|
||||||
export const VERIFY_AUTHORIZATION_ENDPOINT = ENDPOINT_ROOT + "verifyAuthorization";
|
export const VERIFY_AUTHORIZATION_ENDPOINT = ENDPOINT_ROOT + "verifyAuthorization";
|
||||||
export const SECURITY_SETTINGS_ENDPOINT = ENDPOINT_ROOT + "securitySettings";
|
export const SECURITY_SETTINGS_ENDPOINT = ENDPOINT_ROOT + "securitySettings";
|
||||||
|
export const GENERATE_TOKEN_ENDPOINT = ENDPOINT_ROOT + "generateToken";
|
||||||
export const RESTART_ENDPOINT = ENDPOINT_ROOT + "restart";
|
export const RESTART_ENDPOINT = ENDPOINT_ROOT + "restart";
|
||||||
export const FACTORY_RESET_ENDPOINT = ENDPOINT_ROOT + "factoryReset";
|
export const FACTORY_RESET_ENDPOINT = ENDPOINT_ROOT + "factoryReset";
|
||||||
|
|||||||
@@ -5,10 +5,8 @@ import { withSnackbar, WithSnackbarProps } from 'notistack';
|
|||||||
import * as Authentication from './Authentication';
|
import * as Authentication from './Authentication';
|
||||||
import { withAuthenticationContext, AuthenticationContextProps, AuthenticatedContext, AuthenticatedContextValue } from './AuthenticationContext';
|
import { withAuthenticationContext, AuthenticationContextProps, AuthenticatedContext, AuthenticatedContextValue } from './AuthenticationContext';
|
||||||
|
|
||||||
type ChildComponent = React.ComponentType<RouteComponentProps<any>> | React.ComponentType<any>;
|
|
||||||
|
|
||||||
interface AuthenticatedRouteProps extends RouteProps, WithSnackbarProps, AuthenticationContextProps {
|
interface AuthenticatedRouteProps extends RouteProps, WithSnackbarProps, AuthenticationContextProps {
|
||||||
component: ChildComponent;
|
component: React.ComponentType<RouteComponentProps<any>> | React.ComponentType<any>;
|
||||||
}
|
}
|
||||||
|
|
||||||
type RenderComponent = (props: RouteComponentProps<any>) => React.ReactNode;
|
type RenderComponent = (props: RouteComponentProps<any>) => React.ReactNode;
|
||||||
@@ -27,7 +25,7 @@ export class AuthenticatedRoute extends React.Component<AuthenticatedRouteProps>
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
Authentication.storeLoginRedirect(location);
|
Authentication.storeLoginRedirect(location);
|
||||||
enqueueSnackbar("Please sign in to continue.", { variant: 'info' });
|
enqueueSnackbar("Please sign in to continue", { variant: 'info' });
|
||||||
return (
|
return (
|
||||||
<Redirect to='/' />
|
<Redirect to='/' />
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -101,7 +101,7 @@ class AuthenticationWrapper extends React.Component<AuthenticationWrapperProps,
|
|||||||
me: undefined
|
me: undefined
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
this.props.enqueueSnackbar("You have signed out.", { variant: 'success', });
|
this.props.enqueueSnackbar("You have signed out", { variant: 'success', });
|
||||||
history.push('/');
|
history.push('/');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,9 @@ class UnauthenticatedRoute extends Route<UnauthenticatedRouteProps> {
|
|||||||
if (authenticationContext.me) {
|
if (authenticationContext.me) {
|
||||||
return (<Redirect to={Authentication.fetchLoginRedirect(features)} />);
|
return (<Redirect to={Authentication.fetchLoginRedirect(features)} />);
|
||||||
}
|
}
|
||||||
return (<Component {...props} />);
|
if (Component) {
|
||||||
|
return (<Component {...props} />);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<Route {...rest} render={renderComponent} />
|
<Route {...rest} render={renderComponent} />
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import { Card, CardContent, CardActions } from '@material-ui/core';
|
|||||||
|
|
||||||
import { withStyles, createStyles, Theme, WithTheme, WithStyles, withTheme } from '@material-ui/core/styles';
|
import { withStyles, createStyles, Theme, WithTheme, WithStyles, withTheme } from '@material-ui/core/styles';
|
||||||
|
|
||||||
import WifiIcon from '@material-ui/icons/Wifi';
|
import SettingsEthernetIcon from '@material-ui/icons/SettingsEthernet';
|
||||||
import SettingsIcon from '@material-ui/icons/Settings';
|
import SettingsIcon from '@material-ui/icons/Settings';
|
||||||
import AccessTimeIcon from '@material-ui/icons/AccessTime';
|
import AccessTimeIcon from '@material-ui/icons/AccessTime';
|
||||||
import AccountCircleIcon from '@material-ui/icons/AccountCircle';
|
import AccountCircleIcon from '@material-ui/icons/AccountCircle';
|
||||||
@@ -146,7 +146,7 @@ class MenuAppBar extends React.Component<MenuAppBarProps, MenuAppBarState> {
|
|||||||
<List>
|
<List>
|
||||||
<ListItem to='/network/' selected={path.startsWith('/network/')} button component={Link}>
|
<ListItem to='/network/' selected={path.startsWith('/network/')} button component={Link}>
|
||||||
<ListItemIcon>
|
<ListItemIcon>
|
||||||
<WifiIcon />
|
<SettingsEthernetIcon />
|
||||||
</ListItemIcon>
|
</ListItemIcon>
|
||||||
<ListItemText primary="Network Connection" />
|
<ListItemText primary="Network Connection" />
|
||||||
</ListItem>
|
</ListItem>
|
||||||
|
|||||||
@@ -1,22 +1,31 @@
|
|||||||
import React from 'react';
|
import React from "react";
|
||||||
import { TextValidator, ValidatorForm, SelectValidator } from 'react-material-ui-form-validator';
|
import {
|
||||||
|
TextValidator,
|
||||||
|
ValidatorForm,
|
||||||
|
SelectValidator,
|
||||||
|
} from "react-material-ui-form-validator";
|
||||||
|
|
||||||
import { Checkbox, TextField, Typography } from '@material-ui/core';
|
import { Checkbox, TextField, Typography } from "@material-ui/core";
|
||||||
import SaveIcon from '@material-ui/icons/Save';
|
import SaveIcon from "@material-ui/icons/Save";
|
||||||
import MenuItem from '@material-ui/core/MenuItem';
|
import MenuItem from "@material-ui/core/MenuItem";
|
||||||
|
|
||||||
import { RestFormProps, FormActions, FormButton, BlockFormControlLabel, PasswordValidator } from '../components';
|
import {
|
||||||
import { isIP, isHostname, or, isPath } from '../validators';
|
RestFormProps,
|
||||||
|
FormActions,
|
||||||
|
FormButton,
|
||||||
|
BlockFormControlLabel,
|
||||||
|
PasswordValidator,
|
||||||
|
} from "../components";
|
||||||
|
import { isIP, isHostname, or, isPath } from "../validators";
|
||||||
|
|
||||||
import { MqttSettings } from './types';
|
import { MqttSettings } from "./types";
|
||||||
|
|
||||||
type MqttSettingsFormProps = RestFormProps<MqttSettings>;
|
type MqttSettingsFormProps = RestFormProps<MqttSettings>;
|
||||||
|
|
||||||
class MqttSettingsForm extends React.Component<MqttSettingsFormProps> {
|
class MqttSettingsForm extends React.Component<MqttSettingsFormProps> {
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
ValidatorForm.addValidationRule('isIPOrHostname', or(isIP, isHostname));
|
ValidatorForm.addValidationRule("isIPOrHostname", or(isIP, isHostname));
|
||||||
ValidatorForm.addValidationRule('isPath', isPath);
|
ValidatorForm.addValidationRule("isPath", isPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
@@ -27,44 +36,57 @@ class MqttSettingsForm extends React.Component<MqttSettingsFormProps> {
|
|||||||
control={
|
control={
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={data.enabled}
|
checked={data.enabled}
|
||||||
onChange={handleValueChange('enabled')}
|
onChange={handleValueChange("enabled")}
|
||||||
value="enabled"
|
value="enabled"
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
label="Enable MQTT"
|
label="Enable MQTT"
|
||||||
/>
|
/>
|
||||||
<TextValidator
|
<TextValidator
|
||||||
validators={['required', 'isIPOrHostname']}
|
validators={["required", "isIPOrHostname"]}
|
||||||
errorMessages={['Host is required', "Not a valid IP address or hostname"]}
|
errorMessages={[
|
||||||
|
"Host is required",
|
||||||
|
"Not a valid IP address or hostname",
|
||||||
|
]}
|
||||||
name="host"
|
name="host"
|
||||||
label="Host"
|
label="Host"
|
||||||
fullWidth
|
fullWidth
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
value={data.host}
|
value={data.host}
|
||||||
onChange={handleValueChange('host')}
|
onChange={handleValueChange("host")}
|
||||||
margin="normal"
|
margin="normal"
|
||||||
/>
|
/>
|
||||||
<TextValidator
|
<TextValidator
|
||||||
validators={['required', 'isNumber', 'minNumber:0', 'maxNumber:65535']}
|
validators={[
|
||||||
errorMessages={['Port is required', "Must be a number", "Must be greater than 0 ", "Max value is 65535"]}
|
"required",
|
||||||
|
"isNumber",
|
||||||
|
"minNumber:0",
|
||||||
|
"maxNumber:65535",
|
||||||
|
]}
|
||||||
|
errorMessages={[
|
||||||
|
"Port is required",
|
||||||
|
"Must be a number",
|
||||||
|
"Must be greater than 0 ",
|
||||||
|
"Max value is 65535",
|
||||||
|
]}
|
||||||
name="port"
|
name="port"
|
||||||
label="Port"
|
label="Port"
|
||||||
fullWidth
|
fullWidth
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
value={data.port}
|
value={data.port}
|
||||||
type="number"
|
type="number"
|
||||||
onChange={handleValueChange('port')}
|
onChange={handleValueChange("port")}
|
||||||
margin="normal"
|
margin="normal"
|
||||||
/>
|
/>
|
||||||
<TextValidator
|
<TextValidator
|
||||||
validators={['required', 'isPath']}
|
validators={["required", "isPath"]}
|
||||||
errorMessages={['Base is required', "Not a valid Path"]}
|
errorMessages={["Base is required", "Not a valid Path"]}
|
||||||
name="base"
|
name="base"
|
||||||
label="Base"
|
label="Base"
|
||||||
fullWidth
|
fullWidth
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
value={data.base}
|
value={data.base}
|
||||||
onChange={handleValueChange('base')}
|
onChange={handleValueChange("base")}
|
||||||
margin="normal"
|
margin="normal"
|
||||||
/>
|
/>
|
||||||
<TextField
|
<TextField
|
||||||
@@ -73,7 +95,7 @@ class MqttSettingsForm extends React.Component<MqttSettingsFormProps> {
|
|||||||
fullWidth
|
fullWidth
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
value={data.username}
|
value={data.username}
|
||||||
onChange={handleValueChange('username')}
|
onChange={handleValueChange("username")}
|
||||||
margin="normal"
|
margin="normal"
|
||||||
/>
|
/>
|
||||||
<PasswordValidator
|
<PasswordValidator
|
||||||
@@ -82,7 +104,7 @@ class MqttSettingsForm extends React.Component<MqttSettingsFormProps> {
|
|||||||
fullWidth
|
fullWidth
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
value={data.password}
|
value={data.password}
|
||||||
onChange={handleValueChange('password')}
|
onChange={handleValueChange("password")}
|
||||||
margin="normal"
|
margin="normal"
|
||||||
/>
|
/>
|
||||||
<TextField
|
<TextField
|
||||||
@@ -91,28 +113,40 @@ class MqttSettingsForm extends React.Component<MqttSettingsFormProps> {
|
|||||||
fullWidth
|
fullWidth
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
value={data.client_id}
|
value={data.client_id}
|
||||||
onChange={handleValueChange('client_id')}
|
onChange={handleValueChange("client_id")}
|
||||||
margin="normal"
|
margin="normal"
|
||||||
/>
|
/>
|
||||||
<TextValidator
|
<TextValidator
|
||||||
validators={['required', 'isNumber', 'minNumber:1', 'maxNumber:65535']}
|
validators={[
|
||||||
errorMessages={['Keep alive is required', "Must be a number", "Must be greater than 0", "Max value is 65535"]}
|
"required",
|
||||||
|
"isNumber",
|
||||||
|
"minNumber:1",
|
||||||
|
"maxNumber:65535",
|
||||||
|
]}
|
||||||
|
errorMessages={[
|
||||||
|
"Keep alive is required",
|
||||||
|
"Must be a number",
|
||||||
|
"Must be greater than 0",
|
||||||
|
"Max value is 65535",
|
||||||
|
]}
|
||||||
name="keep_alive"
|
name="keep_alive"
|
||||||
label="Keep Alive (seconds)"
|
label="Keep Alive (seconds)"
|
||||||
fullWidth
|
fullWidth
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
value={data.keep_alive}
|
value={data.keep_alive}
|
||||||
type="number"
|
type="number"
|
||||||
onChange={handleValueChange('keep_alive')}
|
onChange={handleValueChange("keep_alive")}
|
||||||
margin="normal"
|
margin="normal"
|
||||||
/>
|
/>
|
||||||
<SelectValidator name="mqtt_qos"
|
<SelectValidator
|
||||||
|
name="mqtt_qos"
|
||||||
label="QoS"
|
label="QoS"
|
||||||
value={data.mqtt_qos}
|
value={data.mqtt_qos}
|
||||||
fullWidth
|
fullWidth
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
onChange={handleValueChange('mqtt_qos')}
|
onChange={handleValueChange("mqtt_qos")}
|
||||||
margin="normal">
|
margin="normal"
|
||||||
|
>
|
||||||
<MenuItem value={0}>0 (default)</MenuItem>
|
<MenuItem value={0}>0 (default)</MenuItem>
|
||||||
<MenuItem value={1}>1</MenuItem>
|
<MenuItem value={1}>1</MenuItem>
|
||||||
<MenuItem value={2}>2</MenuItem>
|
<MenuItem value={2}>2</MenuItem>
|
||||||
@@ -121,7 +155,7 @@ class MqttSettingsForm extends React.Component<MqttSettingsFormProps> {
|
|||||||
control={
|
control={
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={data.clean_session}
|
checked={data.clean_session}
|
||||||
onChange={handleValueChange('clean_session')}
|
onChange={handleValueChange("clean_session")}
|
||||||
value="clean_session"
|
value="clean_session"
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
@@ -131,55 +165,63 @@ class MqttSettingsForm extends React.Component<MqttSettingsFormProps> {
|
|||||||
control={
|
control={
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={data.mqtt_retain}
|
checked={data.mqtt_retain}
|
||||||
onChange={handleValueChange('mqtt_retain')}
|
onChange={handleValueChange("mqtt_retain")}
|
||||||
value="mqtt_retain"
|
value="mqtt_retain"
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
label="Retain Flag"
|
label="Retain Flag"
|
||||||
/>
|
/>
|
||||||
<br></br>
|
<br></br>
|
||||||
<Typography variant="h6" color="primary" >
|
<Typography variant="h6" color="primary">
|
||||||
Formatting
|
Formatting
|
||||||
</Typography>
|
</Typography>
|
||||||
<BlockFormControlLabel
|
<SelectValidator
|
||||||
control={
|
name="nested_format"
|
||||||
<Checkbox
|
label="Topic/Payload Format"
|
||||||
checked={data.nested_format}
|
value={data.nested_format}
|
||||||
onChange={handleValueChange('nested_format')}
|
fullWidth
|
||||||
value="nested_format"
|
variant="outlined"
|
||||||
/>
|
onChange={handleValueChange("nested_format")}
|
||||||
}
|
margin="normal"
|
||||||
label="Nested format (Thermostat & Mixer only)"
|
>
|
||||||
/>
|
<MenuItem value={1}>nested on a single topic</MenuItem>
|
||||||
<SelectValidator name="dallas_format"
|
<MenuItem value={2}>as individual topics</MenuItem>
|
||||||
|
</SelectValidator>
|
||||||
|
<SelectValidator
|
||||||
|
name="dallas_format"
|
||||||
label="Dallas Sensor Payload Grouping"
|
label="Dallas Sensor Payload Grouping"
|
||||||
value={data.dallas_format}
|
value={data.dallas_format}
|
||||||
fullWidth
|
fullWidth
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
onChange={handleValueChange('dallas_format')}
|
onChange={handleValueChange("dallas_format")}
|
||||||
margin="normal">
|
margin="normal"
|
||||||
|
>
|
||||||
<MenuItem value={1}>by Sensor ID</MenuItem>
|
<MenuItem value={1}>by Sensor ID</MenuItem>
|
||||||
<MenuItem value={2}>by Number</MenuItem>
|
<MenuItem value={2}>by Number</MenuItem>
|
||||||
</SelectValidator>
|
</SelectValidator>
|
||||||
<SelectValidator name="bool_format"
|
<SelectValidator
|
||||||
|
name="bool_format"
|
||||||
label="Boolean Format"
|
label="Boolean Format"
|
||||||
value={data.bool_format}
|
value={data.bool_format}
|
||||||
fullWidth
|
fullWidth
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
onChange={handleValueChange('bool_format')}
|
onChange={handleValueChange("bool_format")}
|
||||||
margin="normal">
|
margin="normal"
|
||||||
|
>
|
||||||
<MenuItem value={1}>"on"/"off"</MenuItem>
|
<MenuItem value={1}>"on"/"off"</MenuItem>
|
||||||
<MenuItem value={2}>true/false</MenuItem>
|
<MenuItem value={2}>true/false</MenuItem>
|
||||||
<MenuItem value={3}>1/0</MenuItem>
|
<MenuItem value={3}>1/0</MenuItem>
|
||||||
<MenuItem value={4}>"ON"/"OFF"</MenuItem>
|
<MenuItem value={4}>"ON"/"OFF"</MenuItem>
|
||||||
</SelectValidator>
|
</SelectValidator>
|
||||||
<SelectValidator name="subscribe_format"
|
<SelectValidator
|
||||||
|
name="subscribe_format"
|
||||||
label="Subscribe Format"
|
label="Subscribe Format"
|
||||||
value={data.subscribe_format}
|
value={data.subscribe_format}
|
||||||
fullWidth
|
fullWidth
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
onChange={handleValueChange('subscribe_format')}
|
onChange={handleValueChange("subscribe_format")}
|
||||||
margin="normal">
|
margin="normal"
|
||||||
|
>
|
||||||
<MenuItem value={0}>general device topic</MenuItem>
|
<MenuItem value={0}>general device topic</MenuItem>
|
||||||
<MenuItem value={1}>individual topics, main heating circuit</MenuItem>
|
<MenuItem value={1}>individual topics, main heating circuit</MenuItem>
|
||||||
<MenuItem value={2}>individual topics, all heating circuits</MenuItem>
|
<MenuItem value={2}>individual topics, all heating circuits</MenuItem>
|
||||||
@@ -188,103 +230,170 @@ class MqttSettingsForm extends React.Component<MqttSettingsFormProps> {
|
|||||||
control={
|
control={
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={data.ha_enabled}
|
checked={data.ha_enabled}
|
||||||
onChange={handleValueChange('ha_enabled')}
|
onChange={handleValueChange("ha_enabled")}
|
||||||
value="ha_enabled"
|
value="ha_enabled"
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
label="Home Assistant MQTT Discovery"
|
label="Use Home Assistant MQTT Discovery"
|
||||||
/>
|
/>
|
||||||
{ data.ha_enabled &&
|
{data.ha_enabled && (
|
||||||
<SelectValidator name="ha_climate_format"
|
<SelectValidator
|
||||||
|
name="ha_climate_format"
|
||||||
label="Thermostat Room Temperature"
|
label="Thermostat Room Temperature"
|
||||||
value={data.ha_climate_format}
|
value={data.ha_climate_format}
|
||||||
fullWidth
|
fullWidth
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
onChange={handleValueChange('ha_climate_format')}
|
onChange={handleValueChange("ha_climate_format")}
|
||||||
margin="normal">
|
margin="normal"
|
||||||
|
>
|
||||||
<MenuItem value={1}>use Current temperature (default)</MenuItem>
|
<MenuItem value={1}>use Current temperature (default)</MenuItem>
|
||||||
<MenuItem value={2}>use Setpoint temperature</MenuItem>
|
<MenuItem value={2}>use Setpoint temperature</MenuItem>
|
||||||
<MenuItem value={3}>Fix to 0</MenuItem>
|
<MenuItem value={3}>Fix to 0</MenuItem>
|
||||||
</SelectValidator>
|
</SelectValidator>
|
||||||
}
|
)}
|
||||||
<br></br>
|
<br></br>
|
||||||
<Typography variant="h6" color="primary" >
|
<Typography variant="h6" color="primary">
|
||||||
Publish Intervals
|
Publish Intervals
|
||||||
</Typography>
|
</Typography>
|
||||||
<TextValidator
|
<TextValidator
|
||||||
validators={['required', 'isNumber', 'minNumber:0', 'maxNumber:65535']}
|
validators={[
|
||||||
errorMessages={['Publish time is required', "Must be a number", "Must be 0 or greater", "Max value is 65535"]}
|
"required",
|
||||||
|
"isNumber",
|
||||||
|
"minNumber:0",
|
||||||
|
"maxNumber:65535",
|
||||||
|
]}
|
||||||
|
errorMessages={[
|
||||||
|
"Publish time is required",
|
||||||
|
"Must be a number",
|
||||||
|
"Must be 0 or greater",
|
||||||
|
"Max value is 65535",
|
||||||
|
]}
|
||||||
name="publish_time_boiler"
|
name="publish_time_boiler"
|
||||||
label="Boiler Publish Interval (seconds, 0=on change)"
|
label="Boiler Publish Interval (seconds, 0=on change)"
|
||||||
fullWidth
|
fullWidth
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
value={data.publish_time_boiler}
|
value={data.publish_time_boiler}
|
||||||
type="number"
|
type="number"
|
||||||
onChange={handleValueChange('publish_time_boiler')}
|
onChange={handleValueChange("publish_time_boiler")}
|
||||||
margin="normal"
|
margin="normal"
|
||||||
/>
|
/>
|
||||||
<TextValidator
|
<TextValidator
|
||||||
validators={['required', 'isNumber', 'minNumber:0', 'maxNumber:65535']}
|
validators={[
|
||||||
errorMessages={['Publish time is required', "Must be a number", "Must be 0 or greater", "Max value is 65535"]}
|
"required",
|
||||||
|
"isNumber",
|
||||||
|
"minNumber:0",
|
||||||
|
"maxNumber:65535",
|
||||||
|
]}
|
||||||
|
errorMessages={[
|
||||||
|
"Publish time is required",
|
||||||
|
"Must be a number",
|
||||||
|
"Must be 0 or greater",
|
||||||
|
"Max value is 65535",
|
||||||
|
]}
|
||||||
name="publish_time_thermostat"
|
name="publish_time_thermostat"
|
||||||
label="Thermostat Publish Interval (seconds, 0=on change)"
|
label="Thermostat Publish Interval (seconds, 0=on change)"
|
||||||
fullWidth
|
fullWidth
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
value={data.publish_time_thermostat}
|
value={data.publish_time_thermostat}
|
||||||
type="number"
|
type="number"
|
||||||
onChange={handleValueChange('publish_time_thermostat')}
|
onChange={handleValueChange("publish_time_thermostat")}
|
||||||
margin="normal"
|
margin="normal"
|
||||||
/>
|
/>
|
||||||
<TextValidator
|
<TextValidator
|
||||||
validators={['required', 'isNumber', 'minNumber:0', 'maxNumber:65535']}
|
validators={[
|
||||||
errorMessages={['Publish time is required', "Must be a number", "Must be 0 or greater", "Max value is 65535"]}
|
"required",
|
||||||
|
"isNumber",
|
||||||
|
"minNumber:0",
|
||||||
|
"maxNumber:65535",
|
||||||
|
]}
|
||||||
|
errorMessages={[
|
||||||
|
"Publish time is required",
|
||||||
|
"Must be a number",
|
||||||
|
"Must be 0 or greater",
|
||||||
|
"Max value is 65535",
|
||||||
|
]}
|
||||||
name="publish_time_solar"
|
name="publish_time_solar"
|
||||||
label="Solar Publish Interval (seconds, 0=on change)"
|
label="Solar Publish Interval (seconds, 0=on change)"
|
||||||
fullWidth
|
fullWidth
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
value={data.publish_time_solar}
|
value={data.publish_time_solar}
|
||||||
type="number"
|
type="number"
|
||||||
onChange={handleValueChange('publish_time_solar')}
|
onChange={handleValueChange("publish_time_solar")}
|
||||||
margin="normal"
|
margin="normal"
|
||||||
/>
|
/>
|
||||||
<TextValidator
|
<TextValidator
|
||||||
validators={['required', 'isNumber', 'minNumber:0', 'maxNumber:65535']}
|
validators={[
|
||||||
errorMessages={['Publish time is required', "Must be a number", "Must be 0 or greater", "Max value is 65535"]}
|
"required",
|
||||||
|
"isNumber",
|
||||||
|
"minNumber:0",
|
||||||
|
"maxNumber:65535",
|
||||||
|
]}
|
||||||
|
errorMessages={[
|
||||||
|
"Publish time is required",
|
||||||
|
"Must be a number",
|
||||||
|
"Must be 0 or greater",
|
||||||
|
"Max value is 65535",
|
||||||
|
]}
|
||||||
name="publish_time_mixer"
|
name="publish_time_mixer"
|
||||||
label="Mixer Publish Interval (seconds, 0=on change)"
|
label="Mixer Publish Interval (seconds, 0=on change)"
|
||||||
fullWidth
|
fullWidth
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
value={data.publish_time_mixer}
|
value={data.publish_time_mixer}
|
||||||
type="number"
|
type="number"
|
||||||
onChange={handleValueChange('publish_time_mixer')}
|
onChange={handleValueChange("publish_time_mixer")}
|
||||||
margin="normal"
|
margin="normal"
|
||||||
/>
|
/>
|
||||||
<TextValidator
|
<TextValidator
|
||||||
validators={['required', 'isNumber', 'minNumber:0', 'maxNumber:65535']}
|
validators={[
|
||||||
errorMessages={['Publish time is required', "Must be a number", "Must be 0 or greater", "Max value is 65535"]}
|
"required",
|
||||||
|
"isNumber",
|
||||||
|
"minNumber:0",
|
||||||
|
"maxNumber:65535",
|
||||||
|
]}
|
||||||
|
errorMessages={[
|
||||||
|
"Publish time is required",
|
||||||
|
"Must be a number",
|
||||||
|
"Must be 0 or greater",
|
||||||
|
"Max value is 65535",
|
||||||
|
]}
|
||||||
name="publish_time_sensor"
|
name="publish_time_sensor"
|
||||||
label="Sensors Publish Interval (seconds, 0=on change)"
|
label="Sensors Publish Interval (seconds, 0=on change)"
|
||||||
fullWidth
|
fullWidth
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
value={data.publish_time_sensor}
|
value={data.publish_time_sensor}
|
||||||
type="number"
|
type="number"
|
||||||
onChange={handleValueChange('publish_time_sensor')}
|
onChange={handleValueChange("publish_time_sensor")}
|
||||||
margin="normal"
|
margin="normal"
|
||||||
/>
|
/>
|
||||||
<TextValidator
|
<TextValidator
|
||||||
validators={['required', 'isNumber', 'minNumber:0', 'maxNumber:65535']}
|
validators={[
|
||||||
errorMessages={['Publish time is required', "Must be a number", "Must be 0 or greater", "Max value is 65535"]}
|
"required",
|
||||||
|
"isNumber",
|
||||||
|
"minNumber:0",
|
||||||
|
"maxNumber:65535",
|
||||||
|
]}
|
||||||
|
errorMessages={[
|
||||||
|
"Publish time is required",
|
||||||
|
"Must be a number",
|
||||||
|
"Must be 0 or greater",
|
||||||
|
"Max value is 65535",
|
||||||
|
]}
|
||||||
name="publish_time_other"
|
name="publish_time_other"
|
||||||
label="All other Modules Publish Interval (seconds, 0=on change)"
|
label="All other Modules Publish Interval (seconds, 0=on change)"
|
||||||
fullWidth
|
fullWidth
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
value={data.publish_time_other}
|
value={data.publish_time_other}
|
||||||
type="number"
|
type="number"
|
||||||
onChange={handleValueChange('publish_time_other')}
|
onChange={handleValueChange("publish_time_other")}
|
||||||
margin="normal"
|
margin="normal"
|
||||||
/>
|
/>
|
||||||
<FormActions>
|
<FormActions>
|
||||||
<FormButton startIcon={<SaveIcon />} variant="contained" color="primary" type="submit">
|
<FormButton
|
||||||
|
startIcon={<SaveIcon />}
|
||||||
|
variant="contained"
|
||||||
|
color="primary"
|
||||||
|
type="submit"
|
||||||
|
>
|
||||||
Save
|
Save
|
||||||
</FormButton>
|
</FormButton>
|
||||||
</FormActions>
|
</FormActions>
|
||||||
|
|||||||
@@ -40,6 +40,6 @@ export interface MqttSettings {
|
|||||||
mqtt_retain: boolean;
|
mqtt_retain: boolean;
|
||||||
ha_enabled: boolean;
|
ha_enabled: boolean;
|
||||||
ha_climate_format: number;
|
ha_climate_format: number;
|
||||||
nested_format: boolean;
|
nested_format: number;
|
||||||
subscribe_format: number;
|
subscribe_format: number;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,22 @@
|
|||||||
import { Theme } from '@material-ui/core';
|
import { Theme } from "@material-ui/core";
|
||||||
import { NetworkStatus, NetworkConnectionStatus } from './types';
|
import { NetworkStatus, NetworkConnectionStatus } from "./types";
|
||||||
|
|
||||||
export const isConnected = ({ status }: NetworkStatus) => {
|
export const isConnected = ({ status }: NetworkStatus) => {
|
||||||
return ((status === NetworkConnectionStatus.WIFI_STATUS_CONNECTED) || (status === NetworkConnectionStatus.ETHERNET_STATUS_CONNECTED));
|
return (
|
||||||
}
|
status === NetworkConnectionStatus.WIFI_STATUS_CONNECTED ||
|
||||||
|
status === NetworkConnectionStatus.ETHERNET_STATUS_CONNECTED
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export const isWiFi = ({ status }: NetworkStatus) => (status === NetworkConnectionStatus.WIFI_STATUS_CONNECTED)
|
export const isWiFi = ({ status }: NetworkStatus) =>
|
||||||
|
status === NetworkConnectionStatus.WIFI_STATUS_CONNECTED;
|
||||||
|
export const isEthernet = ({ status }: NetworkStatus) =>
|
||||||
|
status === NetworkConnectionStatus.ETHERNET_STATUS_CONNECTED;
|
||||||
|
|
||||||
export const networkStatusHighlight = ({ status }: NetworkStatus, theme: Theme) => {
|
export const networkStatusHighlight = (
|
||||||
|
{ status }: NetworkStatus,
|
||||||
|
theme: Theme
|
||||||
|
) => {
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case NetworkConnectionStatus.WIFI_STATUS_IDLE:
|
case NetworkConnectionStatus.WIFI_STATUS_IDLE:
|
||||||
case NetworkConnectionStatus.WIFI_STATUS_DISCONNECTED:
|
case NetworkConnectionStatus.WIFI_STATUS_DISCONNECTED:
|
||||||
@@ -22,7 +31,7 @@ export const networkStatusHighlight = ({ status }: NetworkStatus, theme: Theme)
|
|||||||
default:
|
default:
|
||||||
return theme.palette.warning.main;
|
return theme.palette.warning.main;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
export const networkStatus = ({ status }: NetworkStatus) => {
|
export const networkStatus = ({ status }: NetworkStatus) => {
|
||||||
switch (status) {
|
switch (status) {
|
||||||
@@ -45,4 +54,4 @@ export const networkStatus = ({ status }: NetworkStatus) => {
|
|||||||
default:
|
default:
|
||||||
return "Unknown";
|
return "Unknown";
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|||||||
@@ -1,45 +1,64 @@
|
|||||||
import React, { Component, Fragment } from 'react';
|
import React, { Component, Fragment } from "react";
|
||||||
|
|
||||||
import { WithTheme, withTheme } from '@material-ui/core/styles';
|
import { WithTheme, withTheme } from "@material-ui/core/styles";
|
||||||
import { Avatar, Divider, List, ListItem, ListItemAvatar, ListItemText } from '@material-ui/core';
|
import {
|
||||||
|
Avatar,
|
||||||
|
Divider,
|
||||||
|
List,
|
||||||
|
ListItem,
|
||||||
|
ListItemAvatar,
|
||||||
|
ListItemText,
|
||||||
|
} from "@material-ui/core";
|
||||||
|
|
||||||
import DNSIcon from '@material-ui/icons/Dns';
|
import DNSIcon from "@material-ui/icons/Dns";
|
||||||
import WifiIcon from '@material-ui/icons/Wifi';
|
import WifiIcon from "@material-ui/icons/Wifi";
|
||||||
import SettingsInputComponentIcon from '@material-ui/icons/SettingsInputComponent';
|
import RouterIcon from "@material-ui/icons/Router";
|
||||||
import SettingsInputAntennaIcon from '@material-ui/icons/SettingsInputAntenna';
|
import SettingsInputComponentIcon from "@material-ui/icons/SettingsInputComponent";
|
||||||
import DeviceHubIcon from '@material-ui/icons/DeviceHub';
|
import SettingsInputAntennaIcon from "@material-ui/icons/SettingsInputAntenna";
|
||||||
import RefreshIcon from '@material-ui/icons/Refresh';
|
import DeviceHubIcon from "@material-ui/icons/DeviceHub";
|
||||||
|
import RefreshIcon from "@material-ui/icons/Refresh";
|
||||||
|
|
||||||
import { RestFormProps, FormActions, FormButton, HighlightAvatar } from '../components';
|
import {
|
||||||
import { networkStatus, networkStatusHighlight, isConnected, isWiFi } from './NetworkStatus';
|
RestFormProps,
|
||||||
import { NetworkStatus } from './types';
|
FormActions,
|
||||||
|
FormButton,
|
||||||
|
HighlightAvatar,
|
||||||
|
} from "../components";
|
||||||
|
import {
|
||||||
|
networkStatus,
|
||||||
|
networkStatusHighlight,
|
||||||
|
isConnected,
|
||||||
|
isWiFi,
|
||||||
|
isEthernet,
|
||||||
|
} from "./NetworkStatus";
|
||||||
|
import { NetworkStatus } from "./types";
|
||||||
|
|
||||||
type NetworkStatusFormProps = RestFormProps<NetworkStatus> & WithTheme;
|
type NetworkStatusFormProps = RestFormProps<NetworkStatus> & WithTheme;
|
||||||
|
|
||||||
class NetworkStatusForm extends Component<NetworkStatusFormProps> {
|
class NetworkStatusForm extends Component<NetworkStatusFormProps> {
|
||||||
|
|
||||||
dnsServers(status: NetworkStatus) {
|
dnsServers(status: NetworkStatus) {
|
||||||
if (!status.dns_ip_1) {
|
if (!status.dns_ip_1) {
|
||||||
return "none";
|
return "none";
|
||||||
}
|
}
|
||||||
return status.dns_ip_1 + (status.dns_ip_2 ? ',' + status.dns_ip_2 : '');
|
return status.dns_ip_1 + (status.dns_ip_2 ? "," + status.dns_ip_2 : "");
|
||||||
}
|
}
|
||||||
|
|
||||||
createListItems() {
|
createListItems() {
|
||||||
const { data, theme } = this.props
|
const { data, theme } = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<ListItem>
|
<ListItem>
|
||||||
<ListItemAvatar>
|
<ListItemAvatar>
|
||||||
<HighlightAvatar color={networkStatusHighlight(data, theme)}>
|
<HighlightAvatar color={networkStatusHighlight(data, theme)}>
|
||||||
<WifiIcon />
|
{isWiFi(data) && <WifiIcon />}
|
||||||
|
{isEthernet(data) && <RouterIcon />}
|
||||||
</HighlightAvatar>
|
</HighlightAvatar>
|
||||||
</ListItemAvatar>
|
</ListItemAvatar>
|
||||||
<ListItemText primary="Status" secondary={networkStatus(data)} />
|
<ListItemText primary="Status" secondary={networkStatus(data)} />
|
||||||
</ListItem>
|
</ListItem>
|
||||||
<Divider variant="inset" component="li" />
|
<Divider variant="inset" component="li" />
|
||||||
{
|
{isWiFi(data) && (
|
||||||
isWiFi(data) &&
|
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<ListItem>
|
<ListItem>
|
||||||
<ListItemAvatar>
|
<ListItemAvatar>
|
||||||
@@ -51,8 +70,8 @@ class NetworkStatusForm extends Component<NetworkStatusFormProps> {
|
|||||||
</ListItem>
|
</ListItem>
|
||||||
<Divider variant="inset" component="li" />
|
<Divider variant="inset" component="li" />
|
||||||
</Fragment>
|
</Fragment>
|
||||||
}
|
)}
|
||||||
{ isConnected(data) &&
|
{isConnected(data) && (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<ListItem>
|
<ListItem>
|
||||||
<ListItemAvatar>
|
<ListItemAvatar>
|
||||||
@@ -67,14 +86,20 @@ class NetworkStatusForm extends Component<NetworkStatusFormProps> {
|
|||||||
<DeviceHubIcon />
|
<DeviceHubIcon />
|
||||||
</Avatar>
|
</Avatar>
|
||||||
</ListItemAvatar>
|
</ListItemAvatar>
|
||||||
<ListItemText primary="MAC Address" secondary={data.mac_address} />
|
<ListItemText
|
||||||
|
primary="MAC Address"
|
||||||
|
secondary={data.mac_address}
|
||||||
|
/>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
<Divider variant="inset" component="li" />
|
<Divider variant="inset" component="li" />
|
||||||
<ListItem>
|
<ListItem>
|
||||||
<ListItemAvatar>
|
<ListItemAvatar>
|
||||||
<Avatar>#</Avatar>
|
<Avatar>#</Avatar>
|
||||||
</ListItemAvatar>
|
</ListItemAvatar>
|
||||||
<ListItemText primary="Subnet Mask" secondary={data.subnet_mask} />
|
<ListItemText
|
||||||
|
primary="Subnet Mask"
|
||||||
|
secondary={data.subnet_mask}
|
||||||
|
/>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
<Divider variant="inset" component="li" />
|
<Divider variant="inset" component="li" />
|
||||||
<ListItem>
|
<ListItem>
|
||||||
@@ -83,7 +108,10 @@ class NetworkStatusForm extends Component<NetworkStatusFormProps> {
|
|||||||
<SettingsInputComponentIcon />
|
<SettingsInputComponentIcon />
|
||||||
</Avatar>
|
</Avatar>
|
||||||
</ListItemAvatar>
|
</ListItemAvatar>
|
||||||
<ListItemText primary="Gateway IP" secondary={data.gateway_ip || "none"} />
|
<ListItemText
|
||||||
|
primary="Gateway IP"
|
||||||
|
secondary={data.gateway_ip || "none"}
|
||||||
|
/>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
<Divider variant="inset" component="li" />
|
<Divider variant="inset" component="li" />
|
||||||
<ListItem>
|
<ListItem>
|
||||||
@@ -92,11 +120,14 @@ class NetworkStatusForm extends Component<NetworkStatusFormProps> {
|
|||||||
<DNSIcon />
|
<DNSIcon />
|
||||||
</Avatar>
|
</Avatar>
|
||||||
</ListItemAvatar>
|
</ListItemAvatar>
|
||||||
<ListItemText primary="DNS Server IP" secondary={this.dnsServers(data)} />
|
<ListItemText
|
||||||
|
primary="DNS Server IP"
|
||||||
|
secondary={this.dnsServers(data)}
|
||||||
|
/>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
<Divider variant="inset" component="li" />
|
<Divider variant="inset" component="li" />
|
||||||
</Fragment>
|
</Fragment>
|
||||||
}
|
)}
|
||||||
</Fragment>
|
</Fragment>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -104,18 +135,20 @@ class NetworkStatusForm extends Component<NetworkStatusFormProps> {
|
|||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<List>
|
<List>{this.createListItems()}</List>
|
||||||
{this.createListItems()}
|
|
||||||
</List>
|
|
||||||
<FormActions>
|
<FormActions>
|
||||||
<FormButton startIcon={<RefreshIcon />} variant="contained" color="secondary" onClick={this.props.loadData}>
|
<FormButton
|
||||||
|
startIcon={<RefreshIcon />}
|
||||||
|
variant="contained"
|
||||||
|
color="secondary"
|
||||||
|
onClick={this.props.loadData}
|
||||||
|
>
|
||||||
Refresh
|
Refresh
|
||||||
</FormButton>
|
</FormButton>
|
||||||
</FormActions>
|
</FormActions>
|
||||||
</Fragment>
|
</Fragment>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default withTheme(NetworkStatusForm);
|
export default withTheme(NetworkStatusForm);
|
||||||
|
|||||||
@@ -89,6 +89,8 @@ class NTPStatusForm extends Component<NTPStatusFormProps, NTPStatusFormState> {
|
|||||||
<Dialog
|
<Dialog
|
||||||
open={this.state.settingTime}
|
open={this.state.settingTime}
|
||||||
onClose={this.closeSetTime}
|
onClose={this.closeSetTime}
|
||||||
|
fullWidth
|
||||||
|
maxWidth="sm"
|
||||||
>
|
>
|
||||||
<DialogTitle>Set Time</DialogTitle>
|
<DialogTitle>Set Time</DialogTitle>
|
||||||
<DialogContent dividers>
|
<DialogContent dividers>
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ export const BOARD_PROFILES: BoardProfiles = {
|
|||||||
"S32": "BBQKees Gateway S32",
|
"S32": "BBQKees Gateway S32",
|
||||||
"E32": "BBQKees Gateway E32",
|
"E32": "BBQKees Gateway E32",
|
||||||
"NODEMCU": "NodeMCU 32S",
|
"NODEMCU": "NodeMCU 32S",
|
||||||
"MT-ET": "MT-ET Live D1 Mini",
|
"MH-ET": "MH-ET Live D1 Mini",
|
||||||
"LOLIN": "Lolin D32",
|
"LOLIN": "Lolin D32",
|
||||||
"OLIMEX": "Olimex ESP32-EVB",
|
"OLIMEX": "Olimex ESP32-EVB",
|
||||||
"TLK110": "Generic Ethernet (TLK110)",
|
"TLK110": "Generic Ethernet (TLK110)",
|
||||||
|
|||||||
@@ -8,15 +8,21 @@ import {
|
|||||||
|
|
||||||
import RefreshIcon from "@material-ui/icons/Refresh";
|
import RefreshIcon from "@material-ui/icons/Refresh";
|
||||||
import ListIcon from "@material-ui/icons/List";
|
import ListIcon from "@material-ui/icons/List";
|
||||||
|
import IconButton from '@material-ui/core/IconButton';
|
||||||
|
import EditIcon from '@material-ui/icons/Edit';
|
||||||
|
|
||||||
import { redirectingAuthorizedFetch, withAuthenticatedContext, AuthenticatedContextProps } from "../authentication";
|
import { redirectingAuthorizedFetch, withAuthenticatedContext, AuthenticatedContextProps } from "../authentication";
|
||||||
import { RestFormProps, FormButton } from "../components";
|
import { RestFormProps, FormButton, extractEventValue } from "../components";
|
||||||
|
|
||||||
import { EMSESPDevices, EMSESPDeviceData, Device } from "./EMSESPtypes";
|
import { EMSESPDevices, EMSESPDeviceData, Device, DeviceValue } from "./EMSESPtypes";
|
||||||
|
|
||||||
|
import ValueForm from './ValueForm';
|
||||||
|
|
||||||
import { ENDPOINT_ROOT } from "../api";
|
import { ENDPOINT_ROOT } from "../api";
|
||||||
|
|
||||||
export const SCANDEVICES_ENDPOINT = ENDPOINT_ROOT + "scanDevices";
|
export const SCANDEVICES_ENDPOINT = ENDPOINT_ROOT + "scanDevices";
|
||||||
export const DEVICE_DATA_ENDPOINT = ENDPOINT_ROOT + "deviceData";
|
export const DEVICE_DATA_ENDPOINT = ENDPOINT_ROOT + "deviceData";
|
||||||
|
export const WRITE_VALUE_ENDPOINT = ENDPOINT_ROOT + "writeValue";
|
||||||
|
|
||||||
const StyledTableCell = withStyles((theme: Theme) =>
|
const StyledTableCell = withStyles((theme: Theme) =>
|
||||||
createStyles({
|
createStyles({
|
||||||
@@ -30,6 +36,16 @@ const StyledTableCell = withStyles((theme: Theme) =>
|
|||||||
})
|
})
|
||||||
)(TableCell);
|
)(TableCell);
|
||||||
|
|
||||||
|
const CustomTooltip = withStyles((theme: Theme) => ({
|
||||||
|
tooltip: {
|
||||||
|
backgroundColor: theme.palette.secondary.main,
|
||||||
|
color: 'white',
|
||||||
|
boxShadow: theme.shadows[1],
|
||||||
|
fontSize: 11,
|
||||||
|
border: '1px solid #dadde9',
|
||||||
|
},
|
||||||
|
}))(Tooltip);
|
||||||
|
|
||||||
function compareDevices(a: Device, b: Device) {
|
function compareDevices(a: Device, b: Device) {
|
||||||
if (a.type < b.type) {
|
if (a.type < b.type) {
|
||||||
return -1;
|
return -1;
|
||||||
@@ -44,11 +60,11 @@ interface EMSESPDevicesFormState {
|
|||||||
confirmScanDevices: boolean;
|
confirmScanDevices: boolean;
|
||||||
processing: boolean;
|
processing: boolean;
|
||||||
deviceData?: EMSESPDeviceData;
|
deviceData?: EMSESPDeviceData;
|
||||||
|
selectedDevice?: number;
|
||||||
|
devicevalue?: DeviceValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
type EMSESPDevicesFormProps = RestFormProps<EMSESPDevices> &
|
type EMSESPDevicesFormProps = RestFormProps<EMSESPDevices> & AuthenticatedContextProps & WithWidthProps;
|
||||||
AuthenticatedContextProps &
|
|
||||||
WithWidthProps;
|
|
||||||
|
|
||||||
function formatTemp(t: string) {
|
function formatTemp(t: string) {
|
||||||
if (t == null) {
|
if (t == null) {
|
||||||
@@ -67,9 +83,66 @@ function formatUnit(u: string) {
|
|||||||
class EMSESPDevicesForm extends Component<EMSESPDevicesFormProps, EMSESPDevicesFormState> {
|
class EMSESPDevicesForm extends Component<EMSESPDevicesFormProps, EMSESPDevicesFormState> {
|
||||||
state: EMSESPDevicesFormState = {
|
state: EMSESPDevicesFormState = {
|
||||||
confirmScanDevices: false,
|
confirmScanDevices: false,
|
||||||
processing: false,
|
processing: false
|
||||||
};
|
};
|
||||||
|
|
||||||
|
handleValueChange = (name: keyof DeviceValue) => (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
this.setState({ devicevalue: { ...this.state.devicevalue!, [name]: extractEventValue(event) } });
|
||||||
|
};
|
||||||
|
|
||||||
|
cancelEditingValue = () => {
|
||||||
|
this.setState({
|
||||||
|
devicevalue: undefined
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
doneEditingValue = () => {
|
||||||
|
const { devicevalue } = this.state;
|
||||||
|
|
||||||
|
redirectingAuthorizedFetch(WRITE_VALUE_ENDPOINT, {
|
||||||
|
method: "POST",
|
||||||
|
body: JSON.stringify({ devicevalue: devicevalue }),
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.then((response) => {
|
||||||
|
if (response.status === 200) {
|
||||||
|
this.props.enqueueSnackbar("Write command sent to device", { variant: "success" });
|
||||||
|
} else if (response.status === 204) {
|
||||||
|
this.props.enqueueSnackbar("Write command failed", { variant: "error" });
|
||||||
|
} else if (response.status === 403) {
|
||||||
|
this.props.enqueueSnackbar("Write access denied", { variant: "error" });
|
||||||
|
} else {
|
||||||
|
throw Error("Unexpected response code: " + response.status);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
this.props.enqueueSnackbar(
|
||||||
|
error.message || "Problem writing value", { variant: "error" }
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (devicevalue) {
|
||||||
|
this.setState({
|
||||||
|
devicevalue: undefined
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
sendCommand = (i: any) => {
|
||||||
|
this.setState({
|
||||||
|
devicevalue: {
|
||||||
|
id: this.state.selectedDevice!,
|
||||||
|
data: this.state.deviceData?.data[i]!,
|
||||||
|
uom: this.state.deviceData?.data[i + 1]!,
|
||||||
|
name: this.state.deviceData?.data[i + 2]!,
|
||||||
|
cmd: this.state.deviceData?.data[i + 3]!,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
noDevices = () => {
|
noDevices = () => {
|
||||||
return this.props.data.devices.length === 0;
|
return this.props.data.devices.length === 0;
|
||||||
};
|
};
|
||||||
@@ -95,50 +168,20 @@ class EMSESPDevicesForm extends Component<EMSESPDevicesFormProps, EMSESPDevicesF
|
|||||||
size="small"
|
size="small"
|
||||||
padding={isWidthDown("xs", width!) ? "none" : "default"}
|
padding={isWidthDown("xs", width!) ? "none" : "default"}
|
||||||
>
|
>
|
||||||
<TableHead>
|
|
||||||
<TableRow>
|
|
||||||
<StyledTableCell>Type</StyledTableCell>
|
|
||||||
<StyledTableCell align="center">Brand</StyledTableCell>
|
|
||||||
<StyledTableCell align="center">Model</StyledTableCell>
|
|
||||||
<StyledTableCell align="center">Device ID</StyledTableCell>
|
|
||||||
<StyledTableCell align="center">Product ID</StyledTableCell>
|
|
||||||
<StyledTableCell align="center">Version</StyledTableCell>
|
|
||||||
<StyledTableCell></StyledTableCell>
|
|
||||||
</TableRow>
|
|
||||||
</TableHead>
|
|
||||||
<TableBody>
|
<TableBody>
|
||||||
{data.devices.sort(compareDevices).map((device) => (
|
{data.devices.sort(compareDevices).map((device) => (
|
||||||
<TableRow
|
<TableRow hover key={device.id} onClick={() => this.handleRowClick(device)}>
|
||||||
hover
|
<TableCell>
|
||||||
key={device.id}
|
<CustomTooltip
|
||||||
onClick={() => this.handleRowClick(device.id)}
|
title={"DeviceID:0x" + ("00" + device.deviceid.toString(16).toUpperCase()).slice(-2) + " ProductID:" + device.productid + " Version:" + device.version}
|
||||||
>
|
|
||||||
<TableCell component="th" scope="row">
|
|
||||||
<Tooltip
|
|
||||||
title="click for details..."
|
|
||||||
arrow
|
|
||||||
placement="right-end"
|
placement="right-end"
|
||||||
>
|
>
|
||||||
<Button
|
<Button startIcon={<ListIcon />} size="small" variant="outlined">
|
||||||
startIcon={<ListIcon />}
|
|
||||||
size="small"
|
|
||||||
variant="outlined"
|
|
||||||
>
|
|
||||||
{device.type}
|
{device.type}
|
||||||
</Button>
|
</Button>
|
||||||
</Tooltip>
|
</CustomTooltip>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell align="center">{device.brand}</TableCell>
|
<TableCell align="right">{device.brand + " " + device.name} </TableCell>
|
||||||
<TableCell align="center">{device.name}</TableCell>
|
|
||||||
<TableCell align="center">
|
|
||||||
0x
|
|
||||||
{("00" + device.deviceid.toString(16).toUpperCase()).slice(
|
|
||||||
-2
|
|
||||||
)}
|
|
||||||
</TableCell>
|
|
||||||
<TableCell align="center">{device.productid}</TableCell>
|
|
||||||
<TableCell align="center">{device.version}</TableCell>
|
|
||||||
<TableCell></TableCell>
|
|
||||||
</TableRow>
|
</TableRow>
|
||||||
))}
|
))}
|
||||||
</TableBody>
|
</TableBody>
|
||||||
@@ -148,13 +191,10 @@ class EMSESPDevicesForm extends Component<EMSESPDevicesFormProps, EMSESPDevicesF
|
|||||||
<Box
|
<Box
|
||||||
bgcolor="error.main"
|
bgcolor="error.main"
|
||||||
color="error.contrastText"
|
color="error.contrastText"
|
||||||
p={2}
|
p={2} mt={2} mb={2}
|
||||||
mt={2}
|
|
||||||
mb={2}
|
|
||||||
>
|
>
|
||||||
<Typography variant="body1">
|
<Typography variant="body1">
|
||||||
No EMS devices found. Check the connections and for possible Tx
|
No EMS devices found. Check the connections and for possible Tx errors.
|
||||||
errors.
|
|
||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
@@ -210,28 +250,19 @@ class EMSESPDevicesForm extends Component<EMSESPDevicesFormProps, EMSESPDevicesF
|
|||||||
<Dialog
|
<Dialog
|
||||||
open={this.state.confirmScanDevices}
|
open={this.state.confirmScanDevices}
|
||||||
onClose={this.onScanDevicesRejected}
|
onClose={this.onScanDevicesRejected}
|
||||||
|
fullWidth
|
||||||
|
maxWidth="sm"
|
||||||
>
|
>
|
||||||
<DialogTitle>Confirm Scan Devices</DialogTitle>
|
<DialogTitle>Confirm Scan Devices</DialogTitle>
|
||||||
<DialogContent dividers>
|
<DialogContent dividers>
|
||||||
Are you sure you want to initiate a scan on the EMS bus for all new
|
Are you sure you want to initiate a scan on the EMS bus for all new devices?
|
||||||
devices?
|
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
<DialogActions>
|
<DialogActions>
|
||||||
<Button
|
<Button variant="contained" onClick={this.onScanDevicesRejected} color="secondary">
|
||||||
variant="contained"
|
|
||||||
onClick={this.onScanDevicesRejected}
|
|
||||||
color="secondary"
|
|
||||||
>
|
|
||||||
Cancel
|
Cancel
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
startIcon={<RefreshIcon />}
|
startIcon={<RefreshIcon />} variant="contained" onClick={this.onScanDevicesConfirmed} disabled={this.state.processing} color="primary" autoFocus>
|
||||||
variant="contained"
|
|
||||||
onClick={this.onScanDevicesConfirmed}
|
|
||||||
disabled={this.state.processing}
|
|
||||||
color="primary"
|
|
||||||
autoFocus
|
|
||||||
>
|
|
||||||
Start Scan
|
Start Scan
|
||||||
</Button>
|
</Button>
|
||||||
</DialogActions>
|
</DialogActions>
|
||||||
@@ -268,11 +299,11 @@ class EMSESPDevicesForm extends Component<EMSESPDevicesFormProps, EMSESPDevicesF
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
handleRowClick = (id: any) => {
|
handleRowClick = (device: any) => {
|
||||||
this.setState({ deviceData: undefined });
|
this.setState({ selectedDevice: device.id, deviceData: undefined });
|
||||||
redirectingAuthorizedFetch(DEVICE_DATA_ENDPOINT, {
|
redirectingAuthorizedFetch(DEVICE_DATA_ENDPOINT, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: JSON.stringify({ id: id }),
|
body: JSON.stringify({ id: device.id }),
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
},
|
},
|
||||||
@@ -298,6 +329,7 @@ class EMSESPDevicesForm extends Component<EMSESPDevicesFormProps, EMSESPDevicesF
|
|||||||
renderDeviceData() {
|
renderDeviceData() {
|
||||||
const { deviceData } = this.state;
|
const { deviceData } = this.state;
|
||||||
const { width } = this.props;
|
const { width } = this.props;
|
||||||
|
const me = this.props.authenticatedContext.me;
|
||||||
|
|
||||||
if (this.noDevices()) {
|
if (this.noDevices()) {
|
||||||
return;
|
return;
|
||||||
@@ -321,16 +353,28 @@ class EMSESPDevicesForm extends Component<EMSESPDevicesFormProps, EMSESPDevicesF
|
|||||||
size="small"
|
size="small"
|
||||||
padding={isWidthDown("xs", width!) ? "none" : "default"}
|
padding={isWidthDown("xs", width!) ? "none" : "default"}
|
||||||
>
|
>
|
||||||
<TableHead></TableHead>
|
<TableHead>
|
||||||
|
</TableHead>
|
||||||
<TableBody>
|
<TableBody>
|
||||||
{deviceData.data.map((item, i) => {
|
{deviceData.data.map((item, i) => {
|
||||||
if (i % 3) {
|
if (i % 4) {
|
||||||
return null;
|
return null;
|
||||||
} else {
|
} else {
|
||||||
return (
|
return (
|
||||||
<TableRow key={i}>
|
<TableRow hover key={i}>
|
||||||
<TableCell component="th" scope="row">{deviceData.data[i + 2]}</TableCell>
|
<TableCell padding="checkbox" style={{ width: 18 }} >
|
||||||
<TableCell align="right">{deviceData.data[i]}{formatUnit(deviceData.data[i + 1])}</TableCell>
|
{deviceData.data[i + 3] && me.admin && (
|
||||||
|
<CustomTooltip title="change value" placement="left-end"
|
||||||
|
>
|
||||||
|
<IconButton edge="start" size="small" aria-label="Edit"
|
||||||
|
onClick={() => this.sendCommand(i)}>
|
||||||
|
<EditIcon color="primary" fontSize="small" />
|
||||||
|
</IconButton>
|
||||||
|
</CustomTooltip>
|
||||||
|
)}
|
||||||
|
</TableCell>
|
||||||
|
<TableCell padding="none" component="th" scope="row">{deviceData.data[i + 2]}</TableCell>
|
||||||
|
<TableCell padding="none" align="right">{deviceData.data[i]}{formatUnit(deviceData.data[i + 1])}</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -351,6 +395,7 @@ class EMSESPDevicesForm extends Component<EMSESPDevicesFormProps, EMSESPDevicesF
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
const { devicevalue } = this.state;
|
||||||
return (
|
return (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<br></br>
|
<br></br>
|
||||||
@@ -360,27 +405,26 @@ class EMSESPDevicesForm extends Component<EMSESPDevicesFormProps, EMSESPDevicesF
|
|||||||
<br></br>
|
<br></br>
|
||||||
<Box display="flex" flexWrap="wrap">
|
<Box display="flex" flexWrap="wrap">
|
||||||
<Box flexGrow={1} padding={1}>
|
<Box flexGrow={1} padding={1}>
|
||||||
<FormButton
|
<FormButton startIcon={<RefreshIcon />} variant="contained" color="secondary" onClick={this.props.loadData} >
|
||||||
startIcon={<RefreshIcon />}
|
|
||||||
variant="contained"
|
|
||||||
color="secondary"
|
|
||||||
onClick={this.props.loadData}
|
|
||||||
>
|
|
||||||
Refresh
|
Refresh
|
||||||
</FormButton>
|
</FormButton>
|
||||||
</Box>
|
</Box>
|
||||||
<Box flexWrap="none" padding={1} whiteSpace="nowrap">
|
<Box flexWrap="none" padding={1} whiteSpace="nowrap">
|
||||||
<FormButton
|
<FormButton startIcon={<RefreshIcon />} variant="contained" onClick={this.onScanDevices} >
|
||||||
startIcon={<RefreshIcon />}
|
|
||||||
variant="contained"
|
|
||||||
color="primary"
|
|
||||||
onClick={this.onScanDevices}
|
|
||||||
>
|
|
||||||
Scan Devices
|
Scan Devices
|
||||||
</FormButton>
|
</FormButton>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
{this.renderScanDevicesDialog()}
|
{this.renderScanDevicesDialog()}
|
||||||
|
{
|
||||||
|
devicevalue &&
|
||||||
|
<ValueForm
|
||||||
|
devicevalue={devicevalue}
|
||||||
|
onDoneEditing={this.doneEditingValue}
|
||||||
|
onCancelEditing={this.cancelEditingValue}
|
||||||
|
handleValueChange={this.handleValueChange}
|
||||||
|
/>
|
||||||
|
}
|
||||||
</Fragment>
|
</Fragment>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,417 +1,577 @@
|
|||||||
import React from 'react';
|
import React from "react";
|
||||||
import { ValidatorForm, TextValidator, SelectValidator } from 'react-material-ui-form-validator';
|
import {
|
||||||
|
ValidatorForm,
|
||||||
|
TextValidator,
|
||||||
|
SelectValidator,
|
||||||
|
} from "react-material-ui-form-validator";
|
||||||
|
|
||||||
import { Checkbox, Typography, Box, Link, withWidth, WithWidthProps } from '@material-ui/core';
|
import {
|
||||||
import SaveIcon from '@material-ui/icons/Save';
|
Checkbox,
|
||||||
import MenuItem from '@material-ui/core/MenuItem';
|
Typography,
|
||||||
|
Box,
|
||||||
|
Link,
|
||||||
|
withWidth,
|
||||||
|
WithWidthProps,
|
||||||
|
} from "@material-ui/core";
|
||||||
|
import SaveIcon from "@material-ui/icons/Save";
|
||||||
|
import MenuItem from "@material-ui/core/MenuItem";
|
||||||
|
|
||||||
import Grid from '@material-ui/core/Grid';
|
import Grid from "@material-ui/core/Grid";
|
||||||
|
|
||||||
import { redirectingAuthorizedFetch, withAuthenticatedContext, AuthenticatedContextProps } from "../authentication";
|
import {
|
||||||
|
redirectingAuthorizedFetch,
|
||||||
|
withAuthenticatedContext,
|
||||||
|
AuthenticatedContextProps,
|
||||||
|
} from "../authentication";
|
||||||
|
|
||||||
import { RestFormProps, FormActions, FormButton, BlockFormControlLabel } from '../components';
|
import {
|
||||||
|
RestFormProps,
|
||||||
|
FormActions,
|
||||||
|
FormButton,
|
||||||
|
BlockFormControlLabel,
|
||||||
|
} from "../components";
|
||||||
|
|
||||||
import { isIP, optional } from '../validators';
|
import { isIP, optional } from "../validators";
|
||||||
|
|
||||||
import { EMSESPSettings } from './EMSESPtypes';
|
import { EMSESPSettings } from "./EMSESPtypes";
|
||||||
|
|
||||||
import { boardProfileSelectItems } from './EMSESPBoardProfiles';
|
import { boardProfileSelectItems } from "./EMSESPBoardProfiles";
|
||||||
|
|
||||||
import { ENDPOINT_ROOT } from "../api";
|
import { ENDPOINT_ROOT } from "../api";
|
||||||
export const BOARD_PROFILE_ENDPOINT = ENDPOINT_ROOT + "boardProfile";
|
export const BOARD_PROFILE_ENDPOINT = ENDPOINT_ROOT + "boardProfile";
|
||||||
|
|
||||||
type EMSESPSettingsFormProps = RestFormProps<EMSESPSettings> & AuthenticatedContextProps & WithWidthProps;
|
type EMSESPSettingsFormProps = RestFormProps<EMSESPSettings> &
|
||||||
|
AuthenticatedContextProps &
|
||||||
|
WithWidthProps;
|
||||||
|
|
||||||
interface EMSESPSettingsFormState {
|
interface EMSESPSettingsFormState {
|
||||||
processing: boolean;
|
processing: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
class EMSESPSettingsForm extends React.Component<EMSESPSettingsFormProps> {
|
class EMSESPSettingsForm extends React.Component<EMSESPSettingsFormProps> {
|
||||||
|
state: EMSESPSettingsFormState = {
|
||||||
|
processing: false,
|
||||||
|
};
|
||||||
|
|
||||||
state: EMSESPSettingsFormState = {
|
componentDidMount() {
|
||||||
processing: false
|
ValidatorForm.addValidationRule("isOptionalIP", optional(isIP));
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
changeBoardProfile = (event: React.ChangeEvent<HTMLSelectElement>) => {
|
||||||
ValidatorForm.addValidationRule('isOptionalIP', optional(isIP));
|
const { data, setData } = this.props;
|
||||||
}
|
setData({
|
||||||
|
...data,
|
||||||
|
board_profile: event.target.value,
|
||||||
|
});
|
||||||
|
|
||||||
changeBoardProfile = (event: React.ChangeEvent<HTMLSelectElement>) => {
|
if (event.target.value === "CUSTOM") return;
|
||||||
const { data, setData } = this.props;
|
|
||||||
|
this.setState({ processing: true });
|
||||||
|
redirectingAuthorizedFetch(BOARD_PROFILE_ENDPOINT, {
|
||||||
|
method: "POST",
|
||||||
|
body: JSON.stringify({ code: event.target.value }),
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.then((response) => {
|
||||||
|
if (response.status === 200) {
|
||||||
|
return response.json();
|
||||||
|
}
|
||||||
|
throw Error("Unexpected response code: " + response.status);
|
||||||
|
})
|
||||||
|
.then((json) => {
|
||||||
|
this.props.enqueueSnackbar("Profile loaded", { variant: "success" });
|
||||||
setData({
|
setData({
|
||||||
...data,
|
...data,
|
||||||
board_profile: event.target.value
|
led_gpio: json.led_gpio,
|
||||||
|
dallas_gpio: json.dallas_gpio,
|
||||||
|
rx_gpio: json.rx_gpio,
|
||||||
|
tx_gpio: json.tx_gpio,
|
||||||
|
pbutton_gpio: json.pbutton_gpio,
|
||||||
|
board_profile: event.target.value,
|
||||||
});
|
});
|
||||||
|
this.setState({ processing: false });
|
||||||
if (event.target.value === "CUSTOM")
|
})
|
||||||
return;
|
.catch((error) => {
|
||||||
|
this.props.enqueueSnackbar(
|
||||||
this.setState({ processing: true });
|
error.message || "Problem fetching board profile",
|
||||||
redirectingAuthorizedFetch(BOARD_PROFILE_ENDPOINT, {
|
{ variant: "warning" }
|
||||||
method: "POST",
|
|
||||||
body: JSON.stringify({ code: event.target.value }),
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.then((response) => {
|
|
||||||
if (response.status === 200) {
|
|
||||||
return response.json();
|
|
||||||
}
|
|
||||||
throw Error("Unexpected response code: " + response.status);
|
|
||||||
})
|
|
||||||
.then((json) => {
|
|
||||||
this.props.enqueueSnackbar("Profile loaded", { variant: 'success' });
|
|
||||||
setData({
|
|
||||||
...data,
|
|
||||||
led_gpio: json.led_gpio,
|
|
||||||
dallas_gpio: json.dallas_gpio,
|
|
||||||
rx_gpio: json.rx_gpio,
|
|
||||||
tx_gpio: json.tx_gpio,
|
|
||||||
pbutton_gpio: json.pbutton_gpio,
|
|
||||||
board_profile: event.target.value
|
|
||||||
});
|
|
||||||
this.setState({ processing: false });
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
this.props.enqueueSnackbar(
|
|
||||||
error.message || "Problem fetching board profile",
|
|
||||||
{ variant: "warning" }
|
|
||||||
);
|
|
||||||
this.setState({ processing: false });
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const { data, saveData, handleValueChange } = this.props;
|
|
||||||
return (
|
|
||||||
<ValidatorForm onSubmit={saveData}>
|
|
||||||
<Box bgcolor="info.main" p={2} mt={2} mb={2}>
|
|
||||||
<Typography variant="body1">
|
|
||||||
Modify any of the EMS-ESP settings here. For help refer to the <Link target="_blank" href="https://emsesp.github.io/docs/#/Configure-firmware32?id=ems-esp-settings" color="primary">{'online documentation'}</Link>.
|
|
||||||
</Typography>
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
<br></br>
|
|
||||||
<Typography variant="h6" color="primary" >
|
|
||||||
EMS Bus
|
|
||||||
</Typography>
|
|
||||||
|
|
||||||
<Grid container spacing={1} direction="row" justify="flex-start" alignItems="flex-start">
|
|
||||||
<Grid item xs={5}>
|
|
||||||
<SelectValidator name="tx_mode"
|
|
||||||
label="Tx Mode"
|
|
||||||
value={data.tx_mode}
|
|
||||||
fullWidth
|
|
||||||
variant="outlined"
|
|
||||||
onChange={handleValueChange('tx_mode')}
|
|
||||||
margin="normal">
|
|
||||||
<MenuItem value={0}>Off</MenuItem>
|
|
||||||
<MenuItem value={1}>EMS</MenuItem>
|
|
||||||
<MenuItem value={2}>EMS+</MenuItem>
|
|
||||||
<MenuItem value={3}>HT3</MenuItem>
|
|
||||||
<MenuItem value={4}>Hardware</MenuItem>
|
|
||||||
</SelectValidator>
|
|
||||||
</Grid>
|
|
||||||
<Grid item xs={6}>
|
|
||||||
<SelectValidator name="ems_bus_id"
|
|
||||||
label="Bus ID"
|
|
||||||
value={data.ems_bus_id}
|
|
||||||
fullWidth
|
|
||||||
variant="outlined"
|
|
||||||
onChange={handleValueChange('ems_bus_id')}
|
|
||||||
margin="normal">
|
|
||||||
<MenuItem value={0x0B}>Service Key (0x0B)</MenuItem>
|
|
||||||
<MenuItem value={0x0D}>Modem (0x0D)</MenuItem>
|
|
||||||
<MenuItem value={0x0A}>Terminal (0x0A)</MenuItem>
|
|
||||||
<MenuItem value={0x0F}>Time Module (0x0F)</MenuItem>
|
|
||||||
<MenuItem value={0x12}>Alarm Module (0x12)</MenuItem>
|
|
||||||
</SelectValidator>
|
|
||||||
</Grid>
|
|
||||||
<Grid item xs={6}>
|
|
||||||
<TextValidator
|
|
||||||
validators={['required', 'isNumber', 'minNumber:0', 'maxNumber:120']}
|
|
||||||
errorMessages={['Tx delay is required', "Must be a number", "Must be 0 or higher", "Max value is 120"]}
|
|
||||||
name="tx_delay"
|
|
||||||
label="Tx start delay (seconds)"
|
|
||||||
fullWidth
|
|
||||||
variant="outlined"
|
|
||||||
value={data.tx_delay}
|
|
||||||
type="number"
|
|
||||||
onChange={handleValueChange('tx_delay')}
|
|
||||||
margin="normal"
|
|
||||||
/>
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
|
|
||||||
<br></br>
|
|
||||||
<Typography variant="h6" color="primary" >
|
|
||||||
Board Profile
|
|
||||||
</Typography>
|
|
||||||
|
|
||||||
<Box color="warning.main" p={0} mt={0} mb={0}>
|
|
||||||
<Typography variant="body2">
|
|
||||||
<i>Select a pre-configured board layout to automatically set the GPIO pins, or set your own custom configuration</i>
|
|
||||||
</Typography>
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
<SelectValidator name="board_profile"
|
|
||||||
label="Board Profile"
|
|
||||||
value={data.board_profile}
|
|
||||||
fullWidth
|
|
||||||
variant="outlined"
|
|
||||||
onChange={this.changeBoardProfile}
|
|
||||||
margin="normal">
|
|
||||||
{boardProfileSelectItems()}
|
|
||||||
<MenuItem key={"CUSTOM"} value={"CUSTOM"}>Custom...</MenuItem>
|
|
||||||
</SelectValidator>
|
|
||||||
|
|
||||||
{ (data.board_profile === "CUSTOM") &&
|
|
||||||
<Grid container spacing={1} direction="row" justify="flex-start" alignItems="flex-start">
|
|
||||||
<Grid item xs={4}>
|
|
||||||
<TextValidator
|
|
||||||
validators={['required', 'isNumber', 'minNumber:0', 'maxNumber:40', 'matchRegexp:^((?!6|7|8|9|10|11|12|14|15|20|24|28|29|30|31)[0-9]*)$']}
|
|
||||||
errorMessages={['GPIO is required', "Must be a number", "Must be 0 or higher", "Max value is 40", "Not a valid GPIO"]}
|
|
||||||
name="rx_gpio"
|
|
||||||
label="Rx GPIO"
|
|
||||||
fullWidth
|
|
||||||
variant="outlined"
|
|
||||||
value={data.rx_gpio}
|
|
||||||
type="number"
|
|
||||||
onChange={handleValueChange('rx_gpio')}
|
|
||||||
margin="normal"
|
|
||||||
/>
|
|
||||||
</Grid>
|
|
||||||
<Grid item xs={4}>
|
|
||||||
<TextValidator
|
|
||||||
validators={['required', 'isNumber', 'minNumber:0', 'maxNumber:40', 'matchRegexp:^((?!6|7|8|9|10|11|12|14|15|20|24|28|29|30|31)[0-9]*)$']}
|
|
||||||
errorMessages={['GPIO is required', "Must be a number", "Must be 0 or higher", "Max value is 40", "Not a valid GPIO"]}
|
|
||||||
name="tx_gpio"
|
|
||||||
label="Tx GPIO"
|
|
||||||
fullWidth
|
|
||||||
variant="outlined"
|
|
||||||
value={data.tx_gpio}
|
|
||||||
type="number"
|
|
||||||
onChange={handleValueChange('tx_gpio')}
|
|
||||||
margin="normal"
|
|
||||||
/>
|
|
||||||
</Grid>
|
|
||||||
<Grid item xs={4}>
|
|
||||||
<TextValidator
|
|
||||||
validators={['required', 'isNumber', 'minNumber:0', 'maxNumber:40', 'matchRegexp:^((?!6|7|8|9|10|11|12|14|15|20|24|28|29|30|31)[0-9]*)$']}
|
|
||||||
errorMessages={['GPIO is required', "Must be a number", "Must be 0 or higher", "Max value is 40", "Not a valid GPIO"]}
|
|
||||||
name="pbutton_gpio"
|
|
||||||
label="Button GPIO"
|
|
||||||
fullWidth
|
|
||||||
variant="outlined"
|
|
||||||
value={data.pbutton_gpio}
|
|
||||||
type="number"
|
|
||||||
onChange={handleValueChange('pbutton_gpio')}
|
|
||||||
margin="normal"
|
|
||||||
/>
|
|
||||||
</Grid>
|
|
||||||
<Grid item xs={4}>
|
|
||||||
<TextValidator
|
|
||||||
validators={['required', 'isNumber', 'minNumber:0', 'maxNumber:40', 'matchRegexp:^((?!6|7|8|9|10|11|12|14|15|20|24|28|29|30|31)[0-9]*)$']}
|
|
||||||
errorMessages={['GPIO is required', "Must be a number", "Must be 0 or higher", "Max value is 40", "Not a valid GPIO"]}
|
|
||||||
name="dallas_gpio"
|
|
||||||
label="Dallas GPIO (0=none)"
|
|
||||||
fullWidth
|
|
||||||
variant="outlined"
|
|
||||||
value={data.dallas_gpio}
|
|
||||||
type="number"
|
|
||||||
onChange={handleValueChange('dallas_gpio')}
|
|
||||||
margin="normal"
|
|
||||||
/>
|
|
||||||
</Grid>
|
|
||||||
<Grid item xs={4}>
|
|
||||||
<TextValidator
|
|
||||||
validators={['required', 'isNumber', 'minNumber:0', 'maxNumber:40', 'matchRegexp:^((?!6|7|8|9|10|11|12|14|15|20|24|28|29|30|31)[0-9]*)$']}
|
|
||||||
errorMessages={['GPIO is required', "Must be a number", "Must be 0 or higher", "Max value is 40", "Not a valid GPIO"]}
|
|
||||||
name="led_gpio"
|
|
||||||
label="LED GPIO (0=none)"
|
|
||||||
fullWidth
|
|
||||||
variant="outlined"
|
|
||||||
value={data.led_gpio}
|
|
||||||
type="number"
|
|
||||||
onChange={handleValueChange('led_gpio')}
|
|
||||||
margin="normal"
|
|
||||||
/>
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
}
|
|
||||||
|
|
||||||
<br></br>
|
|
||||||
<Typography variant="h6" color="primary" >
|
|
||||||
Options
|
|
||||||
</Typography>
|
|
||||||
|
|
||||||
{ data.dallas_gpio !== 0 &&
|
|
||||||
<BlockFormControlLabel
|
|
||||||
control={
|
|
||||||
<Checkbox
|
|
||||||
checked={data.dallas_parasite}
|
|
||||||
onChange={handleValueChange('dallas_parasite')}
|
|
||||||
value="dallas_parasite"
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
label="Enable Dallas parasite mode"
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
{ data.led_gpio !== 0 &&
|
|
||||||
<BlockFormControlLabel
|
|
||||||
control={
|
|
||||||
<Checkbox
|
|
||||||
checked={data.hide_led}
|
|
||||||
onChange={handleValueChange('hide_led')}
|
|
||||||
value="hide_led"
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
label = "Hide LED"
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
|
|
||||||
<Grid container spacing={0} direction="row" justify="flex-start" alignItems="flex-start">
|
|
||||||
|
|
||||||
<BlockFormControlLabel
|
|
||||||
control={
|
|
||||||
<Checkbox
|
|
||||||
checked={data.shower_timer}
|
|
||||||
onChange={handleValueChange('shower_timer')}
|
|
||||||
value="shower_timer"
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
label="Shower Timer"
|
|
||||||
/>
|
|
||||||
{/* <BlockFormControlLabel
|
|
||||||
control={
|
|
||||||
<Checkbox
|
|
||||||
checked={data.shower_alert}
|
|
||||||
onChange={handleValueChange('shower_alert')}
|
|
||||||
value="shower_alert"
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
label="Shower Alert"
|
|
||||||
/> */}
|
|
||||||
</Grid>
|
|
||||||
<BlockFormControlLabel
|
|
||||||
control={
|
|
||||||
<Checkbox
|
|
||||||
checked={data.api_enabled}
|
|
||||||
onChange={handleValueChange('api_enabled')}
|
|
||||||
value="api_enabled"
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
label="Enable API write commands"
|
|
||||||
/>
|
|
||||||
<BlockFormControlLabel
|
|
||||||
control={
|
|
||||||
<Checkbox
|
|
||||||
checked={data.analog_enabled}
|
|
||||||
onChange={handleValueChange('analog_enabled')}
|
|
||||||
value="analog_enabled"
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
label="Enable ADC"
|
|
||||||
/>
|
|
||||||
<br></br>
|
|
||||||
<Typography variant="h6" color="primary" >
|
|
||||||
Syslog
|
|
||||||
</Typography>
|
|
||||||
|
|
||||||
<BlockFormControlLabel
|
|
||||||
control={
|
|
||||||
<Checkbox
|
|
||||||
checked={data.syslog_enabled}
|
|
||||||
onChange={handleValueChange('syslog_enabled')}
|
|
||||||
value="syslog_enabled"
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
label="Enable Syslog"
|
|
||||||
/>
|
|
||||||
|
|
||||||
{ data.syslog_enabled &&
|
|
||||||
<Grid container spacing={1} direction="row" justify="flex-start" alignItems="flex-start">
|
|
||||||
<Grid item xs={5}>
|
|
||||||
<TextValidator
|
|
||||||
validators={['isOptionalIP']}
|
|
||||||
errorMessages={["Not a valid IP address"]}
|
|
||||||
name="syslog_host"
|
|
||||||
label="IP"
|
|
||||||
fullWidth
|
|
||||||
variant="outlined"
|
|
||||||
value={data.syslog_host}
|
|
||||||
onChange={handleValueChange('syslog_host')}
|
|
||||||
margin="normal"
|
|
||||||
/>
|
|
||||||
</Grid>
|
|
||||||
<Grid item xs={6}>
|
|
||||||
<TextValidator
|
|
||||||
validators={['required', 'isNumber', 'minNumber:0', 'maxNumber:65535']}
|
|
||||||
errorMessages={['Port is required', "Must be a number", "Must be greater than 0 ", "Max value is 65535"]}
|
|
||||||
name="syslog_port"
|
|
||||||
label="Port"
|
|
||||||
fullWidth
|
|
||||||
variant="outlined"
|
|
||||||
value={data.syslog_port}
|
|
||||||
type="number"
|
|
||||||
onChange={handleValueChange('syslog_port')}
|
|
||||||
margin="normal"
|
|
||||||
/>
|
|
||||||
</Grid>
|
|
||||||
<Grid item xs={5}>
|
|
||||||
<SelectValidator name="syslog_level"
|
|
||||||
label="Log Level"
|
|
||||||
value={data.syslog_level}
|
|
||||||
fullWidth
|
|
||||||
variant="outlined"
|
|
||||||
onChange={handleValueChange('syslog_level')}
|
|
||||||
margin="normal">
|
|
||||||
<MenuItem value={-1}>OFF</MenuItem>
|
|
||||||
<MenuItem value={3}>ERR</MenuItem>
|
|
||||||
<MenuItem value={5}>NOTICE</MenuItem>
|
|
||||||
<MenuItem value={6}>INFO</MenuItem>
|
|
||||||
<MenuItem value={7}>DEBUG</MenuItem>
|
|
||||||
<MenuItem value={8}>ALL</MenuItem>
|
|
||||||
</SelectValidator>
|
|
||||||
</Grid>
|
|
||||||
<Grid item xs={6}>
|
|
||||||
<TextValidator
|
|
||||||
validators={['required', 'isNumber', 'minNumber:0', 'maxNumber:65535']}
|
|
||||||
errorMessages={['Syslog Mark is required', "Must be a number", "Must be 0 or higher", "Max value is 10"]}
|
|
||||||
name="syslog_mark_interval"
|
|
||||||
label="Mark Interval seconds (0=off)"
|
|
||||||
fullWidth
|
|
||||||
variant="outlined"
|
|
||||||
value={data.syslog_mark_interval}
|
|
||||||
type="number"
|
|
||||||
onChange={handleValueChange('syslog_mark_interval')}
|
|
||||||
margin="normal"
|
|
||||||
/>
|
|
||||||
</Grid>
|
|
||||||
<BlockFormControlLabel
|
|
||||||
control={
|
|
||||||
<Checkbox
|
|
||||||
checked={data.trace_raw}
|
|
||||||
onChange={handleValueChange('trace_raw')}
|
|
||||||
value="trace_raw"
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
label="Output EMS telegrams in raw format"
|
|
||||||
/>
|
|
||||||
</Grid>
|
|
||||||
}
|
|
||||||
|
|
||||||
<br></br>
|
|
||||||
<FormActions>
|
|
||||||
<FormButton startIcon={<SaveIcon />} variant="contained" color="primary" type="submit">
|
|
||||||
Save
|
|
||||||
</FormButton>
|
|
||||||
</FormActions>
|
|
||||||
</ValidatorForm >
|
|
||||||
);
|
);
|
||||||
}
|
this.setState({ processing: false });
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { data, saveData, handleValueChange } = this.props;
|
||||||
|
return (
|
||||||
|
<ValidatorForm onSubmit={saveData}>
|
||||||
|
<Box bgcolor="info.main" p={2} mt={2} mb={2}>
|
||||||
|
<Typography variant="body1">
|
||||||
|
Adjust any of the EMS-ESP settings here. For help refer to the{" "}
|
||||||
|
<Link
|
||||||
|
target="_blank"
|
||||||
|
href="https://emsesp.github.io/docs/#/Configure-firmware32?id=ems-esp-settings"
|
||||||
|
color="primary"
|
||||||
|
>
|
||||||
|
{"online documentation"}
|
||||||
|
</Link>
|
||||||
|
.
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<br></br>
|
||||||
|
<Typography variant="h6" color="primary">
|
||||||
|
EMS Bus
|
||||||
|
</Typography>
|
||||||
|
|
||||||
|
<Grid
|
||||||
|
container
|
||||||
|
spacing={1}
|
||||||
|
direction="row"
|
||||||
|
justify="flex-start"
|
||||||
|
alignItems="flex-start"
|
||||||
|
>
|
||||||
|
<Grid item xs={5}>
|
||||||
|
<SelectValidator
|
||||||
|
name="tx_mode"
|
||||||
|
label="Tx Mode"
|
||||||
|
value={data.tx_mode}
|
||||||
|
fullWidth
|
||||||
|
variant="outlined"
|
||||||
|
onChange={handleValueChange("tx_mode")}
|
||||||
|
margin="normal"
|
||||||
|
>
|
||||||
|
<MenuItem value={0}>Off</MenuItem>
|
||||||
|
<MenuItem value={1}>EMS</MenuItem>
|
||||||
|
<MenuItem value={2}>EMS+</MenuItem>
|
||||||
|
<MenuItem value={3}>HT3</MenuItem>
|
||||||
|
<MenuItem value={4}>Hardware</MenuItem>
|
||||||
|
</SelectValidator>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={6}>
|
||||||
|
<SelectValidator
|
||||||
|
name="ems_bus_id"
|
||||||
|
label="Bus ID"
|
||||||
|
value={data.ems_bus_id}
|
||||||
|
fullWidth
|
||||||
|
variant="outlined"
|
||||||
|
onChange={handleValueChange("ems_bus_id")}
|
||||||
|
margin="normal"
|
||||||
|
>
|
||||||
|
<MenuItem value={0x0b}>Service Key (0x0B)</MenuItem>
|
||||||
|
<MenuItem value={0x0d}>Modem (0x0D)</MenuItem>
|
||||||
|
<MenuItem value={0x0a}>Terminal (0x0A)</MenuItem>
|
||||||
|
<MenuItem value={0x0f}>Time Module (0x0F)</MenuItem>
|
||||||
|
<MenuItem value={0x12}>Alarm Module (0x12)</MenuItem>
|
||||||
|
</SelectValidator>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={6}>
|
||||||
|
<TextValidator
|
||||||
|
validators={[
|
||||||
|
"required",
|
||||||
|
"isNumber",
|
||||||
|
"minNumber:0",
|
||||||
|
"maxNumber:120",
|
||||||
|
]}
|
||||||
|
errorMessages={[
|
||||||
|
"Tx delay is required",
|
||||||
|
"Must be a number",
|
||||||
|
"Must be 0 or higher",
|
||||||
|
"Max value is 120",
|
||||||
|
]}
|
||||||
|
name="tx_delay"
|
||||||
|
label="Tx start delay (seconds)"
|
||||||
|
fullWidth
|
||||||
|
variant="outlined"
|
||||||
|
value={data.tx_delay}
|
||||||
|
type="number"
|
||||||
|
onChange={handleValueChange("tx_delay")}
|
||||||
|
margin="normal"
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<br></br>
|
||||||
|
<Typography variant="h6" color="primary">
|
||||||
|
Board Profile
|
||||||
|
</Typography>
|
||||||
|
|
||||||
|
<Box color="warning.main" p={0} mt={0} mb={0}>
|
||||||
|
<Typography variant="body2">
|
||||||
|
<i>
|
||||||
|
Select a pre-configured board layout to automatically set the GPIO
|
||||||
|
pins, or set your own custom configuration
|
||||||
|
</i>
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<SelectValidator
|
||||||
|
name="board_profile"
|
||||||
|
label="Board Profile"
|
||||||
|
value={data.board_profile}
|
||||||
|
fullWidth
|
||||||
|
variant="outlined"
|
||||||
|
onChange={this.changeBoardProfile}
|
||||||
|
margin="normal"
|
||||||
|
>
|
||||||
|
{boardProfileSelectItems()}
|
||||||
|
<MenuItem key={"CUSTOM"} value={"CUSTOM"}>
|
||||||
|
Custom...
|
||||||
|
</MenuItem>
|
||||||
|
</SelectValidator>
|
||||||
|
|
||||||
|
{data.board_profile === "CUSTOM" && (
|
||||||
|
<Grid
|
||||||
|
container
|
||||||
|
spacing={1}
|
||||||
|
direction="row"
|
||||||
|
justify="flex-start"
|
||||||
|
alignItems="flex-start"
|
||||||
|
>
|
||||||
|
<Grid item xs={4}>
|
||||||
|
<TextValidator
|
||||||
|
validators={[
|
||||||
|
"required",
|
||||||
|
"isNumber",
|
||||||
|
"minNumber:0",
|
||||||
|
"maxNumber:40",
|
||||||
|
"matchRegexp:^((?!6|7|8|9|10|11|12|14|15|20|24|28|29|30|31)[0-9]*)$",
|
||||||
|
]}
|
||||||
|
errorMessages={[
|
||||||
|
"GPIO is required",
|
||||||
|
"Must be a number",
|
||||||
|
"Must be 0 or higher",
|
||||||
|
"Max value is 40",
|
||||||
|
"Not a valid GPIO",
|
||||||
|
]}
|
||||||
|
name="rx_gpio"
|
||||||
|
label="Rx GPIO"
|
||||||
|
fullWidth
|
||||||
|
variant="outlined"
|
||||||
|
value={data.rx_gpio}
|
||||||
|
type="number"
|
||||||
|
onChange={handleValueChange("rx_gpio")}
|
||||||
|
margin="normal"
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={4}>
|
||||||
|
<TextValidator
|
||||||
|
validators={[
|
||||||
|
"required",
|
||||||
|
"isNumber",
|
||||||
|
"minNumber:0",
|
||||||
|
"maxNumber:40",
|
||||||
|
"matchRegexp:^((?!6|7|8|9|10|11|12|14|15|20|24|28|29|30|31)[0-9]*)$",
|
||||||
|
]}
|
||||||
|
errorMessages={[
|
||||||
|
"GPIO is required",
|
||||||
|
"Must be a number",
|
||||||
|
"Must be 0 or higher",
|
||||||
|
"Max value is 40",
|
||||||
|
"Not a valid GPIO",
|
||||||
|
]}
|
||||||
|
name="tx_gpio"
|
||||||
|
label="Tx GPIO"
|
||||||
|
fullWidth
|
||||||
|
variant="outlined"
|
||||||
|
value={data.tx_gpio}
|
||||||
|
type="number"
|
||||||
|
onChange={handleValueChange("tx_gpio")}
|
||||||
|
margin="normal"
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={4}>
|
||||||
|
<TextValidator
|
||||||
|
validators={[
|
||||||
|
"required",
|
||||||
|
"isNumber",
|
||||||
|
"minNumber:0",
|
||||||
|
"maxNumber:40",
|
||||||
|
"matchRegexp:^((?!6|7|8|9|10|11|12|14|15|20|24|28|29|30|31)[0-9]*)$",
|
||||||
|
]}
|
||||||
|
errorMessages={[
|
||||||
|
"GPIO is required",
|
||||||
|
"Must be a number",
|
||||||
|
"Must be 0 or higher",
|
||||||
|
"Max value is 40",
|
||||||
|
"Not a valid GPIO",
|
||||||
|
]}
|
||||||
|
name="pbutton_gpio"
|
||||||
|
label="Button GPIO"
|
||||||
|
fullWidth
|
||||||
|
variant="outlined"
|
||||||
|
value={data.pbutton_gpio}
|
||||||
|
type="number"
|
||||||
|
onChange={handleValueChange("pbutton_gpio")}
|
||||||
|
margin="normal"
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={4}>
|
||||||
|
<TextValidator
|
||||||
|
validators={[
|
||||||
|
"required",
|
||||||
|
"isNumber",
|
||||||
|
"minNumber:0",
|
||||||
|
"maxNumber:40",
|
||||||
|
"matchRegexp:^((?!6|7|8|9|10|11|12|14|15|20|24|28|29|30|31)[0-9]*)$",
|
||||||
|
]}
|
||||||
|
errorMessages={[
|
||||||
|
"GPIO is required",
|
||||||
|
"Must be a number",
|
||||||
|
"Must be 0 or higher",
|
||||||
|
"Max value is 40",
|
||||||
|
"Not a valid GPIO",
|
||||||
|
]}
|
||||||
|
name="dallas_gpio"
|
||||||
|
label="Dallas GPIO (0=none)"
|
||||||
|
fullWidth
|
||||||
|
variant="outlined"
|
||||||
|
value={data.dallas_gpio}
|
||||||
|
type="number"
|
||||||
|
onChange={handleValueChange("dallas_gpio")}
|
||||||
|
margin="normal"
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={4}>
|
||||||
|
<TextValidator
|
||||||
|
validators={[
|
||||||
|
"required",
|
||||||
|
"isNumber",
|
||||||
|
"minNumber:0",
|
||||||
|
"maxNumber:40",
|
||||||
|
"matchRegexp:^((?!6|7|8|9|10|11|12|14|15|20|24|28|29|30|31)[0-9]*)$",
|
||||||
|
]}
|
||||||
|
errorMessages={[
|
||||||
|
"GPIO is required",
|
||||||
|
"Must be a number",
|
||||||
|
"Must be 0 or higher",
|
||||||
|
"Max value is 40",
|
||||||
|
"Not a valid GPIO",
|
||||||
|
]}
|
||||||
|
name="led_gpio"
|
||||||
|
label="LED GPIO (0=none)"
|
||||||
|
fullWidth
|
||||||
|
variant="outlined"
|
||||||
|
value={data.led_gpio}
|
||||||
|
type="number"
|
||||||
|
onChange={handleValueChange("led_gpio")}
|
||||||
|
margin="normal"
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<br></br>
|
||||||
|
<Typography variant="h6" color="primary">
|
||||||
|
Options
|
||||||
|
</Typography>
|
||||||
|
|
||||||
|
{data.led_gpio !== 0 && (
|
||||||
|
<BlockFormControlLabel
|
||||||
|
control={
|
||||||
|
<Checkbox
|
||||||
|
checked={data.hide_led}
|
||||||
|
onChange={handleValueChange("hide_led")}
|
||||||
|
value="hide_led"
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
label="Hide LED"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{data.dallas_gpio !== 0 && (
|
||||||
|
<BlockFormControlLabel
|
||||||
|
control={
|
||||||
|
<Checkbox
|
||||||
|
checked={data.dallas_parasite}
|
||||||
|
onChange={handleValueChange("dallas_parasite")}
|
||||||
|
value="dallas_parasite"
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
label="Enable Dallas parasite mode"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<BlockFormControlLabel
|
||||||
|
control={
|
||||||
|
<Checkbox
|
||||||
|
checked={data.api_enabled}
|
||||||
|
onChange={handleValueChange("api_enabled")}
|
||||||
|
value="api_enabled"
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
label="Enable API write commands"
|
||||||
|
/>
|
||||||
|
<BlockFormControlLabel
|
||||||
|
control={
|
||||||
|
<Checkbox
|
||||||
|
checked={data.analog_enabled}
|
||||||
|
onChange={handleValueChange("analog_enabled")}
|
||||||
|
value="analog_enabled"
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
label="Enable ADC"
|
||||||
|
/>
|
||||||
|
<Grid
|
||||||
|
container
|
||||||
|
spacing={0}
|
||||||
|
direction="row"
|
||||||
|
justify="flex-start"
|
||||||
|
alignItems="flex-start"
|
||||||
|
>
|
||||||
|
<BlockFormControlLabel
|
||||||
|
control={
|
||||||
|
<Checkbox
|
||||||
|
checked={data.shower_timer}
|
||||||
|
onChange={handleValueChange("shower_timer")}
|
||||||
|
value="shower_timer"
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
label="Enable Shower Timer"
|
||||||
|
/>
|
||||||
|
<BlockFormControlLabel
|
||||||
|
control={
|
||||||
|
<Checkbox
|
||||||
|
checked={data.shower_alert}
|
||||||
|
onChange={handleValueChange("shower_alert")}
|
||||||
|
value="shower_alert"
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
label="Enable Shower Alert"
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<br></br>
|
||||||
|
<Typography variant="h6" color="primary">
|
||||||
|
Syslog
|
||||||
|
</Typography>
|
||||||
|
|
||||||
|
<BlockFormControlLabel
|
||||||
|
control={
|
||||||
|
<Checkbox
|
||||||
|
checked={data.syslog_enabled}
|
||||||
|
onChange={handleValueChange("syslog_enabled")}
|
||||||
|
value="syslog_enabled"
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
label="Enable Syslog"
|
||||||
|
/>
|
||||||
|
|
||||||
|
{data.syslog_enabled && (
|
||||||
|
<Grid
|
||||||
|
container
|
||||||
|
spacing={1}
|
||||||
|
direction="row"
|
||||||
|
justify="flex-start"
|
||||||
|
alignItems="flex-start"
|
||||||
|
>
|
||||||
|
<Grid item xs={5}>
|
||||||
|
<TextValidator
|
||||||
|
validators={["isOptionalIP"]}
|
||||||
|
errorMessages={["Not a valid IP address"]}
|
||||||
|
name="syslog_host"
|
||||||
|
label="IP"
|
||||||
|
fullWidth
|
||||||
|
variant="outlined"
|
||||||
|
value={data.syslog_host}
|
||||||
|
onChange={handleValueChange("syslog_host")}
|
||||||
|
margin="normal"
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={6}>
|
||||||
|
<TextValidator
|
||||||
|
validators={[
|
||||||
|
"required",
|
||||||
|
"isNumber",
|
||||||
|
"minNumber:0",
|
||||||
|
"maxNumber:65535",
|
||||||
|
]}
|
||||||
|
errorMessages={[
|
||||||
|
"Port is required",
|
||||||
|
"Must be a number",
|
||||||
|
"Must be greater than 0 ",
|
||||||
|
"Max value is 65535",
|
||||||
|
]}
|
||||||
|
name="syslog_port"
|
||||||
|
label="Port"
|
||||||
|
fullWidth
|
||||||
|
variant="outlined"
|
||||||
|
value={data.syslog_port}
|
||||||
|
type="number"
|
||||||
|
onChange={handleValueChange("syslog_port")}
|
||||||
|
margin="normal"
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={5}>
|
||||||
|
<SelectValidator
|
||||||
|
name="syslog_level"
|
||||||
|
label="Log Level"
|
||||||
|
value={data.syslog_level}
|
||||||
|
fullWidth
|
||||||
|
variant="outlined"
|
||||||
|
onChange={handleValueChange("syslog_level")}
|
||||||
|
margin="normal"
|
||||||
|
>
|
||||||
|
<MenuItem value={-1}>OFF</MenuItem>
|
||||||
|
<MenuItem value={3}>ERR</MenuItem>
|
||||||
|
<MenuItem value={5}>NOTICE</MenuItem>
|
||||||
|
<MenuItem value={6}>INFO</MenuItem>
|
||||||
|
<MenuItem value={7}>DEBUG</MenuItem>
|
||||||
|
<MenuItem value={8}>ALL</MenuItem>
|
||||||
|
</SelectValidator>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={6}>
|
||||||
|
<TextValidator
|
||||||
|
validators={[
|
||||||
|
"required",
|
||||||
|
"isNumber",
|
||||||
|
"minNumber:0",
|
||||||
|
"maxNumber:65535",
|
||||||
|
]}
|
||||||
|
errorMessages={[
|
||||||
|
"Syslog Mark is required",
|
||||||
|
"Must be a number",
|
||||||
|
"Must be 0 or higher",
|
||||||
|
"Max value is 10",
|
||||||
|
]}
|
||||||
|
name="syslog_mark_interval"
|
||||||
|
label="Mark Interval seconds (0=off)"
|
||||||
|
fullWidth
|
||||||
|
variant="outlined"
|
||||||
|
value={data.syslog_mark_interval}
|
||||||
|
type="number"
|
||||||
|
onChange={handleValueChange("syslog_mark_interval")}
|
||||||
|
margin="normal"
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
<BlockFormControlLabel
|
||||||
|
control={
|
||||||
|
<Checkbox
|
||||||
|
checked={data.trace_raw}
|
||||||
|
onChange={handleValueChange("trace_raw")}
|
||||||
|
value="trace_raw"
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
label="Output EMS telegrams in raw format"
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<br></br>
|
||||||
|
<FormActions>
|
||||||
|
<FormButton
|
||||||
|
startIcon={<SaveIcon />}
|
||||||
|
variant="contained"
|
||||||
|
color="primary"
|
||||||
|
type="submit"
|
||||||
|
>
|
||||||
|
Save
|
||||||
|
</FormButton>
|
||||||
|
</FormActions>
|
||||||
|
</ValidatorForm>
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default withAuthenticatedContext(withWidth()(EMSESPSettingsForm));
|
export default withAuthenticatedContext(withWidth()(EMSESPSettingsForm));
|
||||||
|
|||||||
@@ -62,3 +62,11 @@ export interface EMSESPDeviceData {
|
|||||||
name: string;
|
name: string;
|
||||||
data: string[];
|
data: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface DeviceValue {
|
||||||
|
id: number;
|
||||||
|
data: string,
|
||||||
|
uom: string,
|
||||||
|
name: string,
|
||||||
|
cmd: string
|
||||||
|
}
|
||||||
|
|||||||
64
interface/src/project/ValueForm.tsx
Normal file
64
interface/src/project/ValueForm.tsx
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
import React, { RefObject } from 'react';
|
||||||
|
import { TextValidator, ValidatorForm } from 'react-material-ui-form-validator';
|
||||||
|
import { Dialog, DialogTitle, DialogContent, DialogActions, Box, Typography } from '@material-ui/core';
|
||||||
|
import { FormButton } from '../components';
|
||||||
|
import { DeviceValue } from './EMSESPtypes';
|
||||||
|
|
||||||
|
interface ValueFormProps {
|
||||||
|
devicevalue: DeviceValue;
|
||||||
|
onDoneEditing: () => void;
|
||||||
|
onCancelEditing: () => void;
|
||||||
|
handleValueChange: (data: keyof DeviceValue) => (event: React.ChangeEvent<HTMLInputElement>) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
class ValueForm extends React.Component<ValueFormProps> {
|
||||||
|
|
||||||
|
formRef: RefObject<any> = React.createRef();
|
||||||
|
|
||||||
|
submit = () => {
|
||||||
|
this.formRef.current.submit();
|
||||||
|
}
|
||||||
|
|
||||||
|
buildLabel = (devicevalue: DeviceValue) => {
|
||||||
|
if ((devicevalue.uom === "") || (!devicevalue.uom)) {
|
||||||
|
return "New value";
|
||||||
|
}
|
||||||
|
return "New value (" + devicevalue.uom + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { devicevalue, handleValueChange, onDoneEditing, onCancelEditing } = this.props;
|
||||||
|
return (
|
||||||
|
<ValidatorForm onSubmit={onDoneEditing} ref={this.formRef}>
|
||||||
|
<Dialog maxWidth="xs" onClose={onCancelEditing} aria-labelledby="user-form-dialog-title" open>
|
||||||
|
<DialogTitle id="user-form-dialog-title">Change the {devicevalue.name}</DialogTitle>
|
||||||
|
<DialogContent dividers>
|
||||||
|
<TextValidator
|
||||||
|
validators={['required']}
|
||||||
|
errorMessages={['is required']}
|
||||||
|
name="data"
|
||||||
|
label={this.buildLabel(devicevalue)}
|
||||||
|
fullWidth
|
||||||
|
variant="outlined"
|
||||||
|
value={devicevalue.data}
|
||||||
|
margin="normal"
|
||||||
|
onChange={handleValueChange('data')}
|
||||||
|
/>
|
||||||
|
<Box color="warning.main" p={1} pl={0} pr={0} mt={0} mb={0}>
|
||||||
|
<Typography variant="body2">
|
||||||
|
<i>Note: it may take a few seconds before the change is visible. If nothing happens check the logs.</i>
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
</DialogContent>
|
||||||
|
|
||||||
|
<DialogActions>
|
||||||
|
<FormButton variant="contained" color="secondary" onClick={onCancelEditing}>Cancel</FormButton>
|
||||||
|
<FormButton variant="contained" color="primary" type="submit" onClick={this.submit}>Done</FormButton>
|
||||||
|
</DialogActions>
|
||||||
|
</Dialog>
|
||||||
|
</ValidatorForm>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ValueForm;
|
||||||
77
interface/src/security/GenerateToken.tsx
Normal file
77
interface/src/security/GenerateToken.tsx
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
import React, { Fragment } from 'react';
|
||||||
|
import { Dialog, DialogTitle, DialogContent, DialogActions, Box, LinearProgress, Typography, TextField } from '@material-ui/core';
|
||||||
|
|
||||||
|
import { FormButton } from '../components';
|
||||||
|
import { redirectingAuthorizedFetch } from '../authentication';
|
||||||
|
import { GENERATE_TOKEN_ENDPOINT } from '../api';
|
||||||
|
import { withSnackbar, WithSnackbarProps } from 'notistack';
|
||||||
|
|
||||||
|
interface GenerateTokenProps extends WithSnackbarProps {
|
||||||
|
username: string;
|
||||||
|
onClose: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface GenerateTokenState {
|
||||||
|
token?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
class GenerateToken extends React.Component<GenerateTokenProps, GenerateTokenState> {
|
||||||
|
|
||||||
|
state: GenerateTokenState = {};
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
const { username } = this.props;
|
||||||
|
redirectingAuthorizedFetch(GENERATE_TOKEN_ENDPOINT + "?" + new URLSearchParams({ username }), { method: 'GET' })
|
||||||
|
.then(response => {
|
||||||
|
if (response.status === 200) {
|
||||||
|
return response.json();
|
||||||
|
} else {
|
||||||
|
throw Error("Error generating token: " + response.status);
|
||||||
|
}
|
||||||
|
}).then(generatedToken => {
|
||||||
|
console.log(generatedToken);
|
||||||
|
this.setState({ token: generatedToken.token });
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
this.props.enqueueSnackbar(error.message || "Problem generating token", { variant: 'error' });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { onClose, username } = this.props;
|
||||||
|
const { token } = this.state;
|
||||||
|
return (
|
||||||
|
<Dialog onClose={onClose} aria-labelledby="generate-token-dialog-title" open fullWidth maxWidth="sm">
|
||||||
|
<DialogTitle id="generate-token-dialog-title">Token for: {username}</DialogTitle>
|
||||||
|
<DialogContent dividers>
|
||||||
|
{token ?
|
||||||
|
<Fragment>
|
||||||
|
<Box bgcolor="primary.main" color="primary.contrastText" p={2} mt={2} mb={2}>
|
||||||
|
<Typography variant="body1">
|
||||||
|
The token below may be used to access the secured APIs. This may be used for bearer authentication with the "Authorization" header or using the "access_token" query paramater.
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
<Box mt={2} mb={2}>
|
||||||
|
<TextField label="Token" multiline value={token} fullWidth contentEditable={false} />
|
||||||
|
</Box>
|
||||||
|
</Fragment>
|
||||||
|
:
|
||||||
|
<Box m={4} textAlign="center">
|
||||||
|
<LinearProgress />
|
||||||
|
<Typography variant="h6">
|
||||||
|
Generating token…
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
}
|
||||||
|
</DialogContent>
|
||||||
|
<DialogActions>
|
||||||
|
<FormButton variant="contained" color="primary" type="submit" onClick={onClose}>
|
||||||
|
Close
|
||||||
|
</FormButton>
|
||||||
|
</DialogActions>
|
||||||
|
</Dialog>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default withSnackbar(GenerateToken);
|
||||||
@@ -11,12 +11,14 @@ import CheckIcon from '@material-ui/icons/Check';
|
|||||||
import IconButton from '@material-ui/core/IconButton';
|
import IconButton from '@material-ui/core/IconButton';
|
||||||
import SaveIcon from '@material-ui/icons/Save';
|
import SaveIcon from '@material-ui/icons/Save';
|
||||||
import PersonAddIcon from '@material-ui/icons/PersonAdd';
|
import PersonAddIcon from '@material-ui/icons/PersonAdd';
|
||||||
|
import VpnKeyIcon from '@material-ui/icons/VpnKey';
|
||||||
|
|
||||||
import { withAuthenticatedContext, AuthenticatedContextProps } from '../authentication';
|
import { withAuthenticatedContext, AuthenticatedContextProps } from '../authentication';
|
||||||
import { RestFormProps, FormActions, FormButton, extractEventValue } from '../components';
|
import { RestFormProps, FormActions, FormButton, extractEventValue } from '../components';
|
||||||
|
|
||||||
import UserForm from './UserForm';
|
import UserForm from './UserForm';
|
||||||
import { SecuritySettings, User } from './types';
|
import { SecuritySettings, User } from './types';
|
||||||
|
import GenerateToken from './GenerateToken';
|
||||||
|
|
||||||
function compareUsers(a: User, b: User) {
|
function compareUsers(a: User, b: User) {
|
||||||
if (a.username < b.username) {
|
if (a.username < b.username) {
|
||||||
@@ -33,6 +35,7 @@ type ManageUsersFormProps = RestFormProps<SecuritySettings> & AuthenticatedConte
|
|||||||
type ManageUsersFormState = {
|
type ManageUsersFormState = {
|
||||||
creating: boolean;
|
creating: boolean;
|
||||||
user?: User;
|
user?: User;
|
||||||
|
generateTokenFor?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
class ManageUsersForm extends React.Component<ManageUsersFormProps, ManageUsersFormState> {
|
class ManageUsersForm extends React.Component<ManageUsersFormProps, ManageUsersFormState> {
|
||||||
@@ -66,6 +69,18 @@ class ManageUsersForm extends React.Component<ManageUsersFormProps, ManageUsersF
|
|||||||
this.props.setData({ ...data, users });
|
this.props.setData({ ...data, users });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
closeGenerateToken = () => {
|
||||||
|
this.setState({
|
||||||
|
generateTokenFor: undefined
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
generateToken = (user: User) => {
|
||||||
|
this.setState({
|
||||||
|
generateTokenFor: user.username
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
startEditingUser = (user: User) => {
|
startEditingUser = (user: User) => {
|
||||||
this.setState({
|
this.setState({
|
||||||
creating: false,
|
creating: false,
|
||||||
@@ -103,7 +118,7 @@ class ManageUsersForm extends React.Component<ManageUsersFormProps, ManageUsersF
|
|||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { width, data } = this.props;
|
const { width, data } = this.props;
|
||||||
const { user, creating } = this.state;
|
const { user, creating, generateTokenFor } = this.state;
|
||||||
return (
|
return (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<ValidatorForm onSubmit={this.onSubmit}>
|
<ValidatorForm onSubmit={this.onSubmit}>
|
||||||
@@ -122,11 +137,12 @@ class ManageUsersForm extends React.Component<ManageUsersFormProps, ManageUsersF
|
|||||||
{user.username}
|
{user.username}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell align="center">
|
<TableCell align="center">
|
||||||
{
|
{user.admin ? <CheckIcon /> : <CloseIcon />}
|
||||||
user.admin ? <CheckIcon /> : <CloseIcon />
|
|
||||||
}
|
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell align="center">
|
<TableCell align="center">
|
||||||
|
<IconButton size="small" aria-label="Generate Token" onClick={() => this.generateToken(user)}>
|
||||||
|
<VpnKeyIcon />
|
||||||
|
</IconButton>
|
||||||
<IconButton size="small" aria-label="Delete" onClick={() => this.removeUser(user)}>
|
<IconButton size="small" aria-label="Delete" onClick={() => this.removeUser(user)}>
|
||||||
<DeleteIcon />
|
<DeleteIcon />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
@@ -164,6 +180,9 @@ class ManageUsersForm extends React.Component<ManageUsersFormProps, ManageUsersF
|
|||||||
</FormButton>
|
</FormButton>
|
||||||
</FormActions>
|
</FormActions>
|
||||||
</ValidatorForm>
|
</ValidatorForm>
|
||||||
|
{
|
||||||
|
generateTokenFor && <GenerateToken username={generateTokenFor} onClose={this.closeGenerateToken} />
|
||||||
|
}
|
||||||
{
|
{
|
||||||
user &&
|
user &&
|
||||||
<UserForm
|
<UserForm
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ class UserForm extends React.Component<UserFormProps> {
|
|||||||
const { user, creating, handleValueChange, onDoneEditing, onCancelEditing } = this.props;
|
const { user, creating, handleValueChange, onDoneEditing, onCancelEditing } = this.props;
|
||||||
return (
|
return (
|
||||||
<ValidatorForm onSubmit={onDoneEditing} ref={this.formRef}>
|
<ValidatorForm onSubmit={onDoneEditing} ref={this.formRef}>
|
||||||
<Dialog onClose={onCancelEditing} aria-labelledby="user-form-dialog-title" open>
|
<Dialog onClose={onCancelEditing} aria-labelledby="user-form-dialog-title" open fullWidth maxWidth="sm">
|
||||||
<DialogTitle id="user-form-dialog-title">{creating ? 'Add' : 'Modify'} User</DialogTitle>
|
<DialogTitle id="user-form-dialog-title">{creating ? 'Add' : 'Modify'} User</DialogTitle>
|
||||||
<DialogContent dividers>
|
<DialogContent dividers>
|
||||||
<TextValidator
|
<TextValidator
|
||||||
|
|||||||
@@ -9,3 +9,6 @@ export interface SecuritySettings {
|
|||||||
jwt_secret: string;
|
jwt_secret: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface GeneratedToken {
|
||||||
|
token: string;
|
||||||
|
}
|
||||||
|
|||||||
12
interface/src/setupProxy.js
Normal file
12
interface/src/setupProxy.js
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
const { createProxyMiddleware } = require('http-proxy-middleware');
|
||||||
|
|
||||||
|
module.exports = function (app) {
|
||||||
|
app.use(
|
||||||
|
'/rest/*',
|
||||||
|
createProxyMiddleware({
|
||||||
|
target: 'http://localhost:3080',
|
||||||
|
secure: false,
|
||||||
|
changeOrigin: true
|
||||||
|
})
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -111,6 +111,8 @@ class SystemStatusForm extends Component<SystemStatusFormProps, SystemStatusForm
|
|||||||
<Dialog
|
<Dialog
|
||||||
open={this.state.confirmRestart}
|
open={this.state.confirmRestart}
|
||||||
onClose={this.onRestartRejected}
|
onClose={this.onRestartRejected}
|
||||||
|
fullWidth
|
||||||
|
maxWidth="sm"
|
||||||
>
|
>
|
||||||
<DialogTitle>Confirm Restart</DialogTitle>
|
<DialogTitle>Confirm Restart</DialogTitle>
|
||||||
<DialogContent dividers>
|
<DialogContent dividers>
|
||||||
@@ -158,6 +160,8 @@ class SystemStatusForm extends Component<SystemStatusFormProps, SystemStatusForm
|
|||||||
<Dialog
|
<Dialog
|
||||||
open={this.state.confirmFactoryReset}
|
open={this.state.confirmFactoryReset}
|
||||||
onClose={this.onFactoryResetRejected}
|
onClose={this.onFactoryResetRejected}
|
||||||
|
fullWidth
|
||||||
|
maxWidth="sm"
|
||||||
>
|
>
|
||||||
<DialogTitle>Confirm Factory Reset</DialogTitle>
|
<DialogTitle>Confirm Factory Reset</DialogTitle>
|
||||||
<DialogContent dividers>
|
<DialogContent dividers>
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
"resolveJsonModule": true,
|
"resolveJsonModule": true,
|
||||||
"isolatedModules": true,
|
"isolatedModules": true,
|
||||||
"noEmit": true,
|
"noEmit": true,
|
||||||
"jsx": "react",
|
"jsx": "react-jsx",
|
||||||
"noFallthroughCasesInSwitch": true
|
"noFallthroughCasesInSwitch": true
|
||||||
},
|
},
|
||||||
"include": [
|
"include": [
|
||||||
|
|||||||
@@ -39,12 +39,12 @@
|
|||||||
#include <Print.h>
|
#include <Print.h>
|
||||||
|
|
||||||
#if ARDUINOJSON_VERSION_MAJOR == 5
|
#if ARDUINOJSON_VERSION_MAJOR == 5
|
||||||
#define ARDUINOJSON_5_COMPATIBILITY
|
#define ARDUINOJSON_5_COMPATIBILITY
|
||||||
#else
|
#else
|
||||||
#define DYNAMIC_JSON_DOCUMENT_SIZE 1024
|
#define DYNAMIC_JSON_DOCUMENT_SIZE 1024
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
constexpr const char* JSON_MIMETYPE = "application/json";
|
constexpr const char * JSON_MIMETYPE = "application/json";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Json Response
|
* Json Response
|
||||||
@@ -52,34 +52,38 @@ constexpr const char* JSON_MIMETYPE = "application/json";
|
|||||||
|
|
||||||
class ChunkPrint : public Print {
|
class ChunkPrint : public Print {
|
||||||
private:
|
private:
|
||||||
uint8_t* _destination;
|
uint8_t * _destination;
|
||||||
size_t _to_skip;
|
size_t _to_skip;
|
||||||
size_t _to_write;
|
size_t _to_write;
|
||||||
size_t _pos;
|
size_t _pos;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ChunkPrint(uint8_t* destination, size_t from, size_t len)
|
ChunkPrint(uint8_t * destination, size_t from, size_t len)
|
||||||
: _destination(destination), _to_skip(from), _to_write(len), _pos{0} {}
|
: _destination(destination)
|
||||||
virtual ~ChunkPrint(){}
|
, _to_skip(from)
|
||||||
size_t write(uint8_t c){
|
, _to_write(len)
|
||||||
if (_to_skip > 0) {
|
, _pos{0} {
|
||||||
_to_skip--;
|
|
||||||
return 1;
|
|
||||||
} else if (_to_write > 0) {
|
|
||||||
_to_write--;
|
|
||||||
_destination[_pos++] = c;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
size_t write(const uint8_t *buffer, size_t size)
|
virtual ~ChunkPrint() {
|
||||||
{
|
}
|
||||||
return this->Print::write(buffer, size);
|
size_t write(uint8_t c) {
|
||||||
|
if (_to_skip > 0) {
|
||||||
|
_to_skip--;
|
||||||
|
return 1;
|
||||||
|
} else if (_to_write > 0) {
|
||||||
|
_to_write--;
|
||||||
|
_destination[_pos++] = c;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
size_t write(const uint8_t * buffer, size_t size) {
|
||||||
|
return this->Print::write(buffer, size);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class AsyncJsonResponse: public AsyncAbstractResponse {
|
class AsyncJsonResponse : public AsyncAbstractResponse {
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
#ifdef ARDUINOJSON_5_COMPATIBILITY
|
#ifdef ARDUINOJSON_5_COMPATIBILITY
|
||||||
DynamicJsonBuffer _jsonBuffer;
|
DynamicJsonBuffer _jsonBuffer;
|
||||||
#else
|
#else
|
||||||
@@ -87,166 +91,201 @@ class AsyncJsonResponse: public AsyncAbstractResponse {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
JsonVariant _root;
|
JsonVariant _root;
|
||||||
bool _isValid;
|
bool _isValid;
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
|
public:
|
||||||
#ifdef ARDUINOJSON_5_COMPATIBILITY
|
#ifdef ARDUINOJSON_5_COMPATIBILITY
|
||||||
AsyncJsonResponse(bool isArray=false): _isValid{false} {
|
AsyncJsonResponse(bool isArray = false)
|
||||||
_code = 200;
|
: _isValid{false} {
|
||||||
_contentType = JSON_MIMETYPE;
|
_code = 200;
|
||||||
if(isArray)
|
_contentType = JSON_MIMETYPE;
|
||||||
_root = _jsonBuffer.createArray();
|
if (isArray)
|
||||||
else
|
_root = _jsonBuffer.createArray();
|
||||||
_root = _jsonBuffer.createObject();
|
else
|
||||||
|
_root = _jsonBuffer.createObject();
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
AsyncJsonResponse(bool isArray=false, size_t maxJsonBufferSize = DYNAMIC_JSON_DOCUMENT_SIZE) : _jsonBuffer(maxJsonBufferSize), _isValid{false} {
|
AsyncJsonResponse(bool isArray = false, size_t maxJsonBufferSize = DYNAMIC_JSON_DOCUMENT_SIZE)
|
||||||
_code = 200;
|
: _jsonBuffer(maxJsonBufferSize)
|
||||||
_contentType = JSON_MIMETYPE;
|
, _isValid{false} {
|
||||||
if(isArray)
|
_code = 200;
|
||||||
_root = _jsonBuffer.createNestedArray();
|
_contentType = JSON_MIMETYPE;
|
||||||
else
|
if (isArray)
|
||||||
_root = _jsonBuffer.createNestedObject();
|
_root = _jsonBuffer.createNestedArray();
|
||||||
|
else
|
||||||
|
_root = _jsonBuffer.createNestedObject();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
~AsyncJsonResponse() {}
|
~AsyncJsonResponse() {
|
||||||
JsonVariant & getRoot() { return _root; }
|
}
|
||||||
bool _sourceValid() const { return _isValid; }
|
JsonVariant & getRoot() {
|
||||||
|
return _root;
|
||||||
|
}
|
||||||
|
bool _sourceValid() const {
|
||||||
|
return _isValid;
|
||||||
|
}
|
||||||
size_t setLength() {
|
size_t setLength() {
|
||||||
|
|
||||||
#ifdef ARDUINOJSON_5_COMPATIBILITY
|
|
||||||
_contentLength = _root.measureLength();
|
|
||||||
#else
|
|
||||||
_contentLength = measureJson(_root);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (_contentLength) { _isValid = true; }
|
|
||||||
return _contentLength;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t getSize() { return _jsonBuffer.size(); }
|
|
||||||
|
|
||||||
size_t _fillBuffer(uint8_t *data, size_t len){
|
|
||||||
ChunkPrint dest(data, _sentLength, len);
|
|
||||||
|
|
||||||
#ifdef ARDUINOJSON_5_COMPATIBILITY
|
|
||||||
_root.printTo( dest ) ;
|
|
||||||
#else
|
|
||||||
serializeJson(_root, dest);
|
|
||||||
#endif
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class PrettyAsyncJsonResponse: public AsyncJsonResponse {
|
|
||||||
public:
|
|
||||||
#ifdef ARDUINOJSON_5_COMPATIBILITY
|
#ifdef ARDUINOJSON_5_COMPATIBILITY
|
||||||
PrettyAsyncJsonResponse (bool isArray=false) : AsyncJsonResponse{isArray} {}
|
_contentLength = _root.measureLength();
|
||||||
#else
|
#else
|
||||||
PrettyAsyncJsonResponse (bool isArray=false, size_t maxJsonBufferSize = DYNAMIC_JSON_DOCUMENT_SIZE) : AsyncJsonResponse{isArray, maxJsonBufferSize} {}
|
_contentLength = measureJson(_root);
|
||||||
#endif
|
|
||||||
size_t setLength () {
|
|
||||||
#ifdef ARDUINOJSON_5_COMPATIBILITY
|
|
||||||
_contentLength = _root.measurePrettyLength ();
|
|
||||||
#else
|
|
||||||
_contentLength = measureJsonPretty(_root);
|
|
||||||
#endif
|
|
||||||
if (_contentLength) {_isValid = true;}
|
|
||||||
return _contentLength;
|
|
||||||
}
|
|
||||||
size_t _fillBuffer (uint8_t *data, size_t len) {
|
|
||||||
ChunkPrint dest (data, _sentLength, len);
|
|
||||||
#ifdef ARDUINOJSON_5_COMPATIBILITY
|
|
||||||
_root.prettyPrintTo (dest);
|
|
||||||
#else
|
|
||||||
serializeJsonPretty(_root, dest);
|
|
||||||
#endif
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef std::function<void(AsyncWebServerRequest *request, JsonVariant &json)> ArJsonRequestHandlerFunction;
|
|
||||||
|
|
||||||
class AsyncCallbackJsonWebHandler: public AsyncWebHandler {
|
|
||||||
private:
|
|
||||||
protected:
|
|
||||||
const String _uri;
|
|
||||||
WebRequestMethodComposite _method;
|
|
||||||
ArJsonRequestHandlerFunction _onRequest;
|
|
||||||
size_t _contentLength;
|
|
||||||
#ifndef ARDUINOJSON_5_COMPATIBILITY
|
|
||||||
const size_t maxJsonBufferSize;
|
|
||||||
#endif
|
|
||||||
size_t _maxContentLength;
|
|
||||||
public:
|
|
||||||
#ifdef ARDUINOJSON_5_COMPATIBILITY
|
|
||||||
AsyncCallbackJsonWebHandler(const String& uri, ArJsonRequestHandlerFunction onRequest)
|
|
||||||
: _uri(uri), _method(HTTP_POST|HTTP_PUT|HTTP_PATCH), _onRequest(onRequest), _maxContentLength(16384) {}
|
|
||||||
#else
|
|
||||||
AsyncCallbackJsonWebHandler(const String& uri, ArJsonRequestHandlerFunction onRequest, size_t maxJsonBufferSize=DYNAMIC_JSON_DOCUMENT_SIZE)
|
|
||||||
: _uri(uri), _method(HTTP_POST|HTTP_PUT|HTTP_PATCH), _onRequest(onRequest), maxJsonBufferSize(maxJsonBufferSize), _maxContentLength(16384) {}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void setMethod(WebRequestMethodComposite method){ _method = method; }
|
|
||||||
void setMaxContentLength(int maxContentLength){ _maxContentLength = maxContentLength; }
|
|
||||||
void onRequest(ArJsonRequestHandlerFunction fn){ _onRequest = fn; }
|
|
||||||
|
|
||||||
virtual bool canHandle(AsyncWebServerRequest *request) override final{
|
|
||||||
if(!_onRequest)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if(!(_method & request->method()))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if(_uri.length() && (_uri != request->url() && !request->url().startsWith(_uri+"/")))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if ( !request->contentType().equalsIgnoreCase(JSON_MIMETYPE) )
|
|
||||||
return false;
|
|
||||||
|
|
||||||
request->addInterestingHeader("ANY");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void handleRequest(AsyncWebServerRequest *request) override final {
|
|
||||||
if(_onRequest) {
|
|
||||||
if (request->_tempObject != NULL) {
|
|
||||||
|
|
||||||
#ifdef ARDUINOJSON_5_COMPATIBILITY
|
|
||||||
DynamicJsonBuffer jsonBuffer;
|
|
||||||
JsonVariant json = jsonBuffer.parse((uint8_t*)(request->_tempObject));
|
|
||||||
if (json.success()) {
|
|
||||||
#else
|
|
||||||
DynamicJsonDocument jsonBuffer(this->maxJsonBufferSize);
|
|
||||||
DeserializationError error = deserializeJson(jsonBuffer, (uint8_t*)(request->_tempObject));
|
|
||||||
if(!error) {
|
|
||||||
JsonVariant json = jsonBuffer.as<JsonVariant>();
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
_onRequest(request, json);
|
if (_contentLength) {
|
||||||
return;
|
_isValid = true;
|
||||||
}
|
}
|
||||||
}
|
return _contentLength;
|
||||||
request->send(_contentLength > _maxContentLength ? 413 : 400);
|
|
||||||
} else {
|
|
||||||
request->send(500);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
virtual void handleUpload(AsyncWebServerRequest *request, const String& filename, size_t index, uint8_t *data, size_t len, bool final) override final {
|
size_t getSize() {
|
||||||
}
|
return _jsonBuffer.size();
|
||||||
virtual void handleBody(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total) override final {
|
}
|
||||||
if (_onRequest) {
|
|
||||||
_contentLength = total;
|
size_t _fillBuffer(uint8_t * data, size_t len) {
|
||||||
if (total > 0 && request->_tempObject == NULL && total < _maxContentLength) {
|
ChunkPrint dest(data, _sentLength, len);
|
||||||
request->_tempObject = malloc(total);
|
|
||||||
}
|
#ifdef ARDUINOJSON_5_COMPATIBILITY
|
||||||
if (request->_tempObject != NULL) {
|
_root.printTo(dest);
|
||||||
memcpy((uint8_t*)(request->_tempObject) + index, data, len);
|
#else
|
||||||
}
|
serializeJson(_root, dest);
|
||||||
|
#endif
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class PrettyAsyncJsonResponse : public AsyncJsonResponse {
|
||||||
|
public:
|
||||||
|
#ifdef ARDUINOJSON_5_COMPATIBILITY
|
||||||
|
PrettyAsyncJsonResponse(bool isArray = false)
|
||||||
|
: AsyncJsonResponse{isArray} {
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
PrettyAsyncJsonResponse(bool isArray = false, size_t maxJsonBufferSize = DYNAMIC_JSON_DOCUMENT_SIZE)
|
||||||
|
: AsyncJsonResponse{isArray, maxJsonBufferSize} {
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
size_t setLength() {
|
||||||
|
#ifdef ARDUINOJSON_5_COMPATIBILITY
|
||||||
|
_contentLength = _root.measurePrettyLength();
|
||||||
|
#else
|
||||||
|
_contentLength = measureJsonPretty(_root);
|
||||||
|
#endif
|
||||||
|
if (_contentLength) {
|
||||||
|
_isValid = true;
|
||||||
|
}
|
||||||
|
return _contentLength;
|
||||||
|
}
|
||||||
|
size_t _fillBuffer(uint8_t * data, size_t len) {
|
||||||
|
ChunkPrint dest(data, _sentLength, len);
|
||||||
|
#ifdef ARDUINOJSON_5_COMPATIBILITY
|
||||||
|
_root.prettyPrintTo(dest);
|
||||||
|
#else
|
||||||
|
serializeJsonPretty(_root, dest);
|
||||||
|
// serializeJson(_root, Serial); // for testing
|
||||||
|
// Serial.println();
|
||||||
|
#endif
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef std::function<void(AsyncWebServerRequest * request, JsonVariant & json)> ArJsonRequestHandlerFunction;
|
||||||
|
|
||||||
|
class AsyncCallbackJsonWebHandler : public AsyncWebHandler {
|
||||||
|
private:
|
||||||
|
protected:
|
||||||
|
const String _uri;
|
||||||
|
WebRequestMethodComposite _method;
|
||||||
|
ArJsonRequestHandlerFunction _onRequest;
|
||||||
|
size_t _contentLength;
|
||||||
|
#ifndef ARDUINOJSON_5_COMPATIBILITY
|
||||||
|
const size_t maxJsonBufferSize;
|
||||||
|
#endif
|
||||||
|
size_t _maxContentLength;
|
||||||
|
|
||||||
|
public:
|
||||||
|
#ifdef ARDUINOJSON_5_COMPATIBILITY
|
||||||
|
AsyncCallbackJsonWebHandler(const String & uri, ArJsonRequestHandlerFunction onRequest)
|
||||||
|
: _uri(uri)
|
||||||
|
, _method(HTTP_POST | HTTP_PUT | HTTP_PATCH)
|
||||||
|
, _onRequest(onRequest)
|
||||||
|
, _maxContentLength(16384) {
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
AsyncCallbackJsonWebHandler(const String & uri, ArJsonRequestHandlerFunction onRequest, size_t maxJsonBufferSize = DYNAMIC_JSON_DOCUMENT_SIZE)
|
||||||
|
: _uri(uri)
|
||||||
|
, _method(HTTP_POST | HTTP_PUT | HTTP_PATCH)
|
||||||
|
, _onRequest(onRequest)
|
||||||
|
, maxJsonBufferSize(maxJsonBufferSize)
|
||||||
|
, _maxContentLength(16384) {
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void setMethod(WebRequestMethodComposite method) {
|
||||||
|
_method = method;
|
||||||
|
}
|
||||||
|
void setMaxContentLength(int maxContentLength) {
|
||||||
|
_maxContentLength = maxContentLength;
|
||||||
|
}
|
||||||
|
void onRequest(ArJsonRequestHandlerFunction fn) {
|
||||||
|
_onRequest = fn;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool canHandle(AsyncWebServerRequest * request) override final {
|
||||||
|
if (!_onRequest)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!(_method & request->method()))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (_uri.length() && (_uri != request->url() && !request->url().startsWith(_uri + "/")))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!request->contentType().equalsIgnoreCase(JSON_MIMETYPE))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
request->addInterestingHeader("ANY");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void handleRequest(AsyncWebServerRequest * request) override final {
|
||||||
|
if (_onRequest) {
|
||||||
|
if (request->_tempObject != NULL) {
|
||||||
|
#ifdef ARDUINOJSON_5_COMPATIBILITY
|
||||||
|
DynamicJsonBuffer jsonBuffer;
|
||||||
|
JsonVariant json = jsonBuffer.parse((uint8_t *)(request->_tempObject));
|
||||||
|
if (json.success()) {
|
||||||
|
#else
|
||||||
|
DynamicJsonDocument jsonBuffer(this->maxJsonBufferSize);
|
||||||
|
DeserializationError error = deserializeJson(jsonBuffer, (uint8_t *)(request->_tempObject));
|
||||||
|
if (!error) {
|
||||||
|
JsonVariant json = jsonBuffer.as<JsonVariant>();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
_onRequest(request, json);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
request->send(_contentLength > _maxContentLength ? 413 : 400);
|
||||||
|
} else {
|
||||||
|
request->send(500);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
virtual void handleUpload(AsyncWebServerRequest * request, const String & filename, size_t index, uint8_t * data, size_t len, bool final) override final {
|
||||||
|
}
|
||||||
|
virtual void handleBody(AsyncWebServerRequest * request, uint8_t * data, size_t len, size_t index, size_t total) override final {
|
||||||
|
if (_onRequest) {
|
||||||
|
_contentLength = total;
|
||||||
|
if (total > 0 && request->_tempObject == NULL && total < _maxContentLength) {
|
||||||
|
request->_tempObject = malloc(total);
|
||||||
|
}
|
||||||
|
if (request->_tempObject != NULL) {
|
||||||
|
memcpy((uint8_t *)(request->_tempObject) + index, data, len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
virtual bool isRequestHandlerTrivial() override final {
|
||||||
|
return _onRequest ? false : true;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
virtual bool isRequestHandlerTrivial() override final {return _onRequest ? false : true;}
|
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -40,13 +40,6 @@ ESP8266React::ESP8266React(AsyncWebServer * server, FS * fs)
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Disable CORS if required
|
|
||||||
#if defined(ENABLE_CORS)
|
|
||||||
DefaultHeaders::Instance().addHeader("Access-Control-Allow-Origin", CORS_ORIGIN);
|
|
||||||
DefaultHeaders::Instance().addHeader("Access-Control-Allow-Headers", "Accept, Content-Type, Authorization");
|
|
||||||
DefaultHeaders::Instance().addHeader("Access-Control-Allow-Credentials", "true");
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ESP8266React::begin() {
|
void ESP8266React::begin() {
|
||||||
|
|||||||
@@ -57,18 +57,6 @@ static String generateClientId() {
|
|||||||
#define FACTORY_MQTT_MAX_TOPIC_LENGTH 128
|
#define FACTORY_MQTT_MAX_TOPIC_LENGTH 128
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define EMSESP_DEFAULT_BOOL_FORMAT 1 // on/off
|
|
||||||
#define EMSESP_DEFAULT_DALLAS_FORMAT 1 // sensorid
|
|
||||||
#define EMSESP_DEFAULT_HA_CLIMATE_FORMAT 1 // current temp
|
|
||||||
#define EMSESP_DEFAULT_MQTT_QOS 0
|
|
||||||
#define EMSESP_DEFAULT_MQTT_RETAIN false
|
|
||||||
#define EMSESP_DEFAULT_HA_ENABLED false
|
|
||||||
#define EMSESP_DEFAULT_PUBLISH_TIME 10
|
|
||||||
#define EMSESP_DEFAULT_NESTED_FORMAT true
|
|
||||||
#define EMSESP_DEFAULT_SUBSCRIBE_FORMAT 0
|
|
||||||
|
|
||||||
#define EMSESP_DEFAULT_BOARD_PROFILE "S32"
|
|
||||||
|
|
||||||
class MqttSettings {
|
class MqttSettings {
|
||||||
public:
|
public:
|
||||||
// host and port - if enabled
|
// host and port - if enabled
|
||||||
@@ -102,7 +90,7 @@ class MqttSettings {
|
|||||||
uint8_t bool_format;
|
uint8_t bool_format;
|
||||||
uint8_t ha_climate_format;
|
uint8_t ha_climate_format;
|
||||||
bool ha_enabled;
|
bool ha_enabled;
|
||||||
bool nested_format;
|
uint8_t nested_format;
|
||||||
uint8_t subscribe_format;
|
uint8_t subscribe_format;
|
||||||
|
|
||||||
static void read(MqttSettings & settings, JsonObject & root);
|
static void read(MqttSettings & settings, JsonObject & root);
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ SecuritySettingsService::SecuritySettingsService(AsyncWebServer * server, FS * f
|
|||||||
, _fsPersistence(SecuritySettings::read, SecuritySettings::update, this, fs, SECURITY_SETTINGS_FILE)
|
, _fsPersistence(SecuritySettings::read, SecuritySettings::update, this, fs, SECURITY_SETTINGS_FILE)
|
||||||
, _jwtHandler(FACTORY_JWT_SECRET) {
|
, _jwtHandler(FACTORY_JWT_SECRET) {
|
||||||
addUpdateHandler([&](const String & originId) { configureJWTHandler(); }, false);
|
addUpdateHandler([&](const String & originId) { configureJWTHandler(); }, false);
|
||||||
|
server->on(GENERATE_TOKEN_PATH, HTTP_GET, wrapRequest(std::bind(&SecuritySettingsService::generateToken, this, std::placeholders::_1), AuthenticationPredicates::IS_ADMIN));
|
||||||
}
|
}
|
||||||
|
|
||||||
void SecuritySettingsService::begin() {
|
void SecuritySettingsService::begin() {
|
||||||
@@ -109,6 +110,21 @@ ArJsonRequestHandlerFunction SecuritySettingsService::wrapCallback(ArJsonRequest
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SecuritySettingsService::generateToken(AsyncWebServerRequest* request) {
|
||||||
|
AsyncWebParameter* usernameParam = request->getParam("username");
|
||||||
|
for (User _user : _state.users) {
|
||||||
|
if (_user.username == usernameParam->value()) {
|
||||||
|
AsyncJsonResponse* response = new AsyncJsonResponse(false, GENERATE_TOKEN_SIZE);
|
||||||
|
JsonObject root = response->getRoot();
|
||||||
|
root["token"] = generateJWT(&_user);
|
||||||
|
response->setLength();
|
||||||
|
request->send(response);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
request->send(401);
|
||||||
|
}
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
User ADMIN_USER = User(FACTORY_ADMIN_USERNAME, FACTORY_ADMIN_PASSWORD, true);
|
User ADMIN_USER = User(FACTORY_ADMIN_USERNAME, FACTORY_ADMIN_PASSWORD, true);
|
||||||
|
|||||||
@@ -25,6 +25,9 @@
|
|||||||
#define SECURITY_SETTINGS_FILE "/config/securitySettings.json"
|
#define SECURITY_SETTINGS_FILE "/config/securitySettings.json"
|
||||||
#define SECURITY_SETTINGS_PATH "/rest/securitySettings"
|
#define SECURITY_SETTINGS_PATH "/rest/securitySettings"
|
||||||
|
|
||||||
|
#define GENERATE_TOKEN_SIZE 512
|
||||||
|
#define GENERATE_TOKEN_PATH "/rest/generateToken"
|
||||||
|
|
||||||
#if FT_ENABLED(FT_SECURITY)
|
#if FT_ENABLED(FT_SECURITY)
|
||||||
|
|
||||||
class SecuritySettings {
|
class SecuritySettings {
|
||||||
@@ -83,6 +86,8 @@ class SecuritySettingsService : public StatefulService<SecuritySettings>, public
|
|||||||
FSPersistence<SecuritySettings> _fsPersistence;
|
FSPersistence<SecuritySettings> _fsPersistence;
|
||||||
ArduinoJsonJWT _jwtHandler;
|
ArduinoJsonJWT _jwtHandler;
|
||||||
|
|
||||||
|
void generateToken(AsyncWebServerRequest * request);
|
||||||
|
|
||||||
void configureJWTHandler();
|
void configureJWTHandler();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -22,44 +22,24 @@
|
|||||||
|
|
||||||
namespace uuid {
|
namespace uuid {
|
||||||
|
|
||||||
#define UPTIME_OVERFLOW 4294967295 // Uptime overflow value
|
// added by proddy, modified
|
||||||
|
static uint64_t now_millis = 0;
|
||||||
|
|
||||||
// returns system uptime in seconds
|
// returns system uptime in seconds
|
||||||
uint32_t get_uptime_sec() {
|
uint32_t get_uptime_sec() {
|
||||||
static uint32_t last_uptime = 0;
|
return (uint32_t)(now_millis / 1000ULL);
|
||||||
static uint8_t uptime_overflows = 0;
|
|
||||||
|
|
||||||
if (millis() < last_uptime) {
|
|
||||||
++uptime_overflows;
|
|
||||||
}
|
|
||||||
last_uptime = millis();
|
|
||||||
uint32_t uptime_seconds = uptime_overflows * (UPTIME_OVERFLOW / 1000) + (last_uptime / 1000);
|
|
||||||
|
|
||||||
return uptime_seconds;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t get_uptime_ms() {
|
uint64_t get_uptime_ms() {
|
||||||
static uint32_t high_millis = 0;
|
|
||||||
static uint32_t low_millis = 0;
|
|
||||||
|
|
||||||
if (get_uptime() < low_millis) {
|
|
||||||
high_millis++;
|
|
||||||
}
|
|
||||||
|
|
||||||
low_millis = get_uptime();
|
|
||||||
|
|
||||||
return ((uint64_t)high_millis << 32) | low_millis;
|
|
||||||
}
|
|
||||||
|
|
||||||
// added by proddy
|
|
||||||
static uint32_t now_millis;
|
|
||||||
|
|
||||||
void set_uptime() {
|
|
||||||
now_millis = ::millis();
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t get_uptime() {
|
|
||||||
return now_millis;
|
return now_millis;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void set_uptime() {
|
||||||
|
now_millis = esp_timer_get_time() / 1000ULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t get_uptime() {
|
||||||
|
return (uint32_t)now_millis;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace uuid
|
} // namespace uuid
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ namespace uuid {
|
|||||||
|
|
||||||
void loop() {
|
void loop() {
|
||||||
set_uptime(); // added by proddy
|
set_uptime(); // added by proddy
|
||||||
get_uptime_ms();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace uuid
|
} // namespace uuid
|
||||||
|
|||||||
@@ -50,10 +50,7 @@ void Commands::add_command(const flash_string_vector & name, const flash_string_
|
|||||||
add_command(0, 0, name, arguments, function, nullptr);
|
add_command(0, 0, name, arguments, function, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Commands::add_command(const flash_string_vector & name,
|
void Commands::add_command(const flash_string_vector & name, const flash_string_vector & arguments, command_function function, argument_completion_function arg_function) {
|
||||||
const flash_string_vector & arguments,
|
|
||||||
command_function function,
|
|
||||||
argument_completion_function arg_function) {
|
|
||||||
add_command(0, 0, name, arguments, function, arg_function);
|
add_command(0, 0, name, arguments, function, arg_function);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,20 +58,11 @@ void Commands::add_command(unsigned int context, unsigned int flags, const flash
|
|||||||
add_command(context, flags, name, flash_string_vector{}, function, nullptr);
|
add_command(context, flags, name, flash_string_vector{}, function, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Commands::add_command(unsigned int context,
|
void Commands::add_command(unsigned int context, unsigned int flags, const flash_string_vector & name, const flash_string_vector & arguments, command_function function) {
|
||||||
unsigned int flags,
|
|
||||||
const flash_string_vector & name,
|
|
||||||
const flash_string_vector & arguments,
|
|
||||||
command_function function) {
|
|
||||||
add_command(context, flags, name, arguments, function, nullptr);
|
add_command(context, flags, name, arguments, function, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Commands::add_command(unsigned int context,
|
void Commands::add_command(unsigned int context, unsigned int flags, const flash_string_vector & name, const flash_string_vector & arguments, command_function function, argument_completion_function arg_function) {
|
||||||
unsigned int flags,
|
|
||||||
const flash_string_vector & name,
|
|
||||||
const flash_string_vector & arguments,
|
|
||||||
command_function function,
|
|
||||||
argument_completion_function arg_function) {
|
|
||||||
commands_.emplace(std::piecewise_construct, std::forward_as_tuple(context), std::forward_as_tuple(flags, name, arguments, function, arg_function));
|
commands_.emplace(std::piecewise_construct, std::forward_as_tuple(context), std::forward_as_tuple(flags, name, arguments, function, arg_function));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -179,8 +167,7 @@ bool Commands::find_longest_common_prefix(const std::multimap<size_t, const Comm
|
|||||||
for (auto command_it = std::next(commands.begin()); command_it != commands.end(); command_it++) {
|
for (auto command_it = std::next(commands.begin()); command_it != commands.end(); command_it++) {
|
||||||
// This relies on the null terminator character limiting the
|
// This relies on the null terminator character limiting the
|
||||||
// length before it becomes longer than any of the strings
|
// length before it becomes longer than any of the strings
|
||||||
if (pgm_read_byte(reinterpret_cast<PGM_P>(first) + length)
|
if (pgm_read_byte(reinterpret_cast<PGM_P>(first) + length) != pgm_read_byte(reinterpret_cast<PGM_P>(*std::next(command_it->second->name_.begin(), component_prefix)) + length)) {
|
||||||
!= pgm_read_byte(reinterpret_cast<PGM_P>(*std::next(command_it->second->name_.begin(), component_prefix)) + length)) {
|
|
||||||
all_match = false;
|
all_match = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -275,8 +262,7 @@ Commands::Completion Commands::complete_command(Shell & shell, const CommandLine
|
|||||||
result.replacement->push_back(std::move(read_flash_string(name)));
|
result.replacement->push_back(std::move(read_flash_string(name)));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (command_line.total_size() > result.replacement->size()
|
if (command_line.total_size() > result.replacement->size() && command_line.total_size() <= matching_command->name_.size() + matching_command->maximum_arguments()) {
|
||||||
&& command_line.total_size() <= matching_command->name_.size() + matching_command->maximum_arguments()) {
|
|
||||||
// Try to auto-complete arguments
|
// Try to auto-complete arguments
|
||||||
std::vector<std::string> arguments{std::next(command_line->cbegin(), result.replacement->size()), command_line->cend()};
|
std::vector<std::string> arguments{std::next(command_line->cbegin(), result.replacement->size()), command_line->cend()};
|
||||||
|
|
||||||
@@ -526,11 +512,7 @@ void Commands::for_each_available_command(Shell & shell, apply_function f) const
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Commands::Command::Command(unsigned int flags,
|
Commands::Command::Command(unsigned int flags, const flash_string_vector name, const flash_string_vector arguments, command_function function, argument_completion_function arg_function)
|
||||||
const flash_string_vector name,
|
|
||||||
const flash_string_vector arguments,
|
|
||||||
command_function function,
|
|
||||||
argument_completion_function arg_function)
|
|
||||||
: flags_(flags)
|
: flags_(flags)
|
||||||
, name_(name)
|
, name_(name)
|
||||||
, arguments_(arguments)
|
, arguments_(arguments)
|
||||||
|
|||||||
@@ -65,8 +65,10 @@ void Shell::start() {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
line_buffer_.reserve(maximum_command_line_length_);
|
line_buffer_.reserve(maximum_command_line_length_);
|
||||||
line_old_.reserve(maximum_command_line_length_);
|
for (uint8_t i = 0; i < MAX_LINES; i++) {
|
||||||
line_old_.clear();
|
line_old_[i].reserve(maximum_command_line_length_);
|
||||||
|
line_old_[i].clear();
|
||||||
|
}
|
||||||
display_banner();
|
display_banner();
|
||||||
display_prompt();
|
display_prompt();
|
||||||
shells_.insert(shared_from_this());
|
shells_.insert(shared_from_this());
|
||||||
@@ -156,7 +158,8 @@ void Shell::loop_normal() {
|
|||||||
// Interrupt (^C)
|
// Interrupt (^C)
|
||||||
line_buffer_.clear();
|
line_buffer_.clear();
|
||||||
println();
|
println();
|
||||||
cursor_ = 0;
|
cursor_ = 0;
|
||||||
|
line_no_ = 0;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case '\x04':
|
case '\x04':
|
||||||
@@ -172,13 +175,15 @@ void Shell::loop_normal() {
|
|||||||
// Del/Backspace (^?)
|
// Del/Backspace (^?)
|
||||||
if (line_buffer_.length() > cursor_) {
|
if (line_buffer_.length() > cursor_) {
|
||||||
line_buffer_.erase(line_buffer_.length() - cursor_ - 1, 1);
|
line_buffer_.erase(line_buffer_.length() - cursor_ - 1, 1);
|
||||||
|
line_no_ = 0;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case '\x09':
|
case '\x09':
|
||||||
// Tab (^I)
|
// Tab (^I)
|
||||||
process_completion();
|
process_completion();
|
||||||
cursor_ = 0;
|
cursor_ = 0;
|
||||||
|
line_no_ = 0;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case '\x0A':
|
case '\x0A':
|
||||||
@@ -198,12 +203,14 @@ void Shell::loop_normal() {
|
|||||||
case '\x15':
|
case '\x15':
|
||||||
// Delete line (^U)
|
// Delete line (^U)
|
||||||
line_buffer_.clear();
|
line_buffer_.clear();
|
||||||
cursor_ = 0;
|
cursor_ = 0;
|
||||||
|
line_no_ = 0;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case '\x17':
|
case '\x17':
|
||||||
// Delete word (^W)
|
// Delete word (^W)
|
||||||
delete_buffer_word(true);
|
delete_buffer_word(true);
|
||||||
|
line_no_ = 0;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case '\033':
|
case '\033':
|
||||||
@@ -214,10 +221,20 @@ void Shell::loop_normal() {
|
|||||||
default:
|
default:
|
||||||
if (esc_) {
|
if (esc_) {
|
||||||
if (c == 'A') { // cursor up
|
if (c == 'A') { // cursor up
|
||||||
line_buffer_ = line_old_;
|
line_buffer_ = line_old_[line_no_];
|
||||||
cursor_ = 0;
|
if (line_no_ < MAX_LINES - 1) {
|
||||||
|
line_no_++;
|
||||||
|
}
|
||||||
|
cursor_ = 0;
|
||||||
} else if (c == 'B') { // cursor down
|
} else if (c == 'B') { // cursor down
|
||||||
line_buffer_.clear();
|
if (line_no_) {
|
||||||
|
line_no_--;
|
||||||
|
}
|
||||||
|
if (line_no_) {
|
||||||
|
line_buffer_ = line_old_[line_no_ - 1];
|
||||||
|
} else {
|
||||||
|
line_buffer_.clear();
|
||||||
|
}
|
||||||
cursor_ = 0;
|
cursor_ = 0;
|
||||||
} else if (c == 'C') { // cursor right
|
} else if (c == 'C') { // cursor right
|
||||||
if (cursor_) {
|
if (cursor_) {
|
||||||
@@ -239,6 +256,7 @@ void Shell::loop_normal() {
|
|||||||
if ((esc_ == 3) && cursor_) { // del
|
if ((esc_ == 3) && cursor_) { // del
|
||||||
cursor_--;
|
cursor_--;
|
||||||
line_buffer_.erase(line_buffer_.length() - cursor_ - 1, 1);
|
line_buffer_.erase(line_buffer_.length() - cursor_ - 1, 1);
|
||||||
|
line_no_ = 0;
|
||||||
} else if (esc_ == 4) { // end
|
} else if (esc_ == 4) { // end
|
||||||
cursor_ = 0;
|
cursor_ = 0;
|
||||||
} else if (esc_ == 1) { // pos1
|
} else if (esc_ == 1) { // pos1
|
||||||
@@ -279,6 +297,7 @@ void Shell::loop_normal() {
|
|||||||
} else if (c >= '\x20' && c <= '\x7E') {
|
} else if (c >= '\x20' && c <= '\x7E') {
|
||||||
if (line_buffer_.length() < maximum_command_line_length_) {
|
if (line_buffer_.length() < maximum_command_line_length_) {
|
||||||
line_buffer_.insert(line_buffer_.length() - cursor_, 1, c);
|
line_buffer_.insert(line_buffer_.length() - cursor_, 1, c);
|
||||||
|
line_no_ = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -498,7 +517,12 @@ void Shell::process_command() {
|
|||||||
println();
|
println();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
line_old_ = line_buffer_;
|
uint8_t no = line_no_ ? line_no_ : MAX_LINES;
|
||||||
|
while (--no) {
|
||||||
|
line_old_[no] = line_old_[no - 1];
|
||||||
|
}
|
||||||
|
line_no_ = 0;
|
||||||
|
line_old_[0] = line_buffer_;
|
||||||
while (!line_buffer_.empty()) {
|
while (!line_buffer_.empty()) {
|
||||||
size_t pos = line_buffer_.find(';');
|
size_t pos = line_buffer_.find(';');
|
||||||
std::string line1;
|
std::string line1;
|
||||||
|
|||||||
@@ -96,7 +96,6 @@ void Shell::output_logs() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
::yield();
|
::yield();
|
||||||
|
|
||||||
}
|
}
|
||||||
display_prompt();
|
display_prompt();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,8 +61,9 @@ class Commands;
|
|||||||
*/
|
*/
|
||||||
class Shell : public std::enable_shared_from_this<Shell>, public uuid::log::Handler, public ::Stream {
|
class Shell : public std::enable_shared_from_this<Shell>, public uuid::log::Handler, public ::Stream {
|
||||||
public:
|
public:
|
||||||
static constexpr size_t MAX_COMMAND_LINE_LENGTH = 80; /*!< Maximum length of a command line. @since 0.1.0 */
|
static constexpr size_t MAX_COMMAND_LINE_LENGTH = 80; /*!< Maximum length of a command line. @since 0.1.0 */
|
||||||
static constexpr size_t MAX_LOG_MESSAGES = 20; /*!< Maximum number of log messages to buffer before they are output. @since 0.1.0 */
|
static constexpr size_t MAX_LOG_MESSAGES = 20; /*!< Maximum number of log messages to buffer before they are output. @since 0.1.0 */
|
||||||
|
static constexpr uint8_t MAX_LINES = 5; /*!< Maximum lines in buffer */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function to handle the response to a password entry prompt.
|
* Function to handle the response to a password entry prompt.
|
||||||
@@ -903,8 +904,9 @@ class Shell : public std::enable_shared_from_this<Shell>, public uuid::log::Hand
|
|||||||
unsigned long log_message_id_ = 0; /*!< The next identifier to use for queued log messages. @since 0.1.0 */
|
unsigned long log_message_id_ = 0; /*!< The next identifier to use for queued log messages. @since 0.1.0 */
|
||||||
std::list<QueuedLogMessage> log_messages_; /*!< Queued log messages, in the order they were received. @since 0.1.0 */
|
std::list<QueuedLogMessage> log_messages_; /*!< Queued log messages, in the order they were received. @since 0.1.0 */
|
||||||
size_t maximum_log_messages_ = MAX_LOG_MESSAGES; /*!< Maximum command line length in bytes. @since 0.6.0 */
|
size_t maximum_log_messages_ = MAX_LOG_MESSAGES; /*!< Maximum command line length in bytes. @since 0.6.0 */
|
||||||
std::string line_buffer_; /*!< Command line buffer. Limited to maximum_command_line_length() bytes. @since 0.1.0 */
|
std::string line_buffer_; /*!< Command line buffer. Limited to maximum_command_line_length() bytes. @since 0.1.0 */
|
||||||
std::string line_old_; /*!< old Command line buffer.*/
|
std::string line_old_[MAX_LINES]; /*!< old Command line buffer.*/
|
||||||
|
uint8_t line_no_ = 0;
|
||||||
size_t maximum_command_line_length_ = MAX_COMMAND_LINE_LENGTH; /*!< Maximum command line length in bytes. @since 0.6.0 */
|
size_t maximum_command_line_length_ = MAX_COMMAND_LINE_LENGTH; /*!< Maximum command line length in bytes. @since 0.6.0 */
|
||||||
unsigned char previous_ = 0; /*!< Previous character that was entered on the command line. Used to detect CRLF line endings. @since 0.1.0 */
|
unsigned char previous_ = 0; /*!< Previous character that was entered on the command line. Used to detect CRLF line endings. @since 0.1.0 */
|
||||||
uint8_t cursor_ = 0; /*!< cursor position from end of line */
|
uint8_t cursor_ = 0; /*!< cursor position from end of line */
|
||||||
|
|||||||
@@ -199,7 +199,6 @@ SyslogService::QueuedLogMessage::QueuedLogMessage(unsigned long id, std::shared_
|
|||||||
time_.tv_sec = time(nullptr);
|
time_.tv_sec = time(nullptr);
|
||||||
time_.tv_usec = 0;
|
time_.tv_usec = 0;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (time_.tv_sec >= 0 && time_.tv_sec < 18140 * 86400) {
|
if (time_.tv_sec >= 0 && time_.tv_sec < 18140 * 86400) {
|
||||||
time_.tv_sec = (time_t)-1;
|
time_.tv_sec = (time_t)-1;
|
||||||
}
|
}
|
||||||
@@ -386,15 +385,20 @@ bool SyslogService::can_transmit() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool SyslogService::transmit(const QueuedLogMessage & message) {
|
bool SyslogService::transmit(const QueuedLogMessage & message) {
|
||||||
/*
|
|
||||||
// modifications by Proddy. From https://github.com/emsesp/EMS-ESP/issues/395#issuecomment-640053528
|
|
||||||
struct tm tm;
|
struct tm tm;
|
||||||
|
int8_t tzh = 0;
|
||||||
|
int8_t tzm = 0;
|
||||||
|
|
||||||
tm.tm_year = 0;
|
tm.tm_year = 0;
|
||||||
if (message.time_.tv_sec != (time_t)-1) {
|
if (message.time_.tv_sec != (time_t)-1) {
|
||||||
gmtime_r(&message.time_.tv_sec, &tm);
|
struct tm utc;
|
||||||
|
gmtime_r(&message.time_.tv_sec, &utc);
|
||||||
|
localtime_r(&message.time_.tv_sec, &tm);
|
||||||
|
int16_t diff = 60 * (tm.tm_hour - utc.tm_hour) + tm.tm_min - utc.tm_min;
|
||||||
|
diff = diff > 720 ? diff - 1440 : diff < -720 ? diff + 1440 : diff;
|
||||||
|
tzh = diff / 60;
|
||||||
|
tzm = diff < 0 ? (0 - diff) % 60 : diff % 60;
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
if (udp_.beginPacket(host_, port_) != 1) {
|
if (udp_.beginPacket(host_, port_) != 1) {
|
||||||
last_transmit_ = uuid::get_uptime_ms();
|
last_transmit_ = uuid::get_uptime_ms();
|
||||||
@@ -402,30 +406,16 @@ bool SyslogService::transmit(const QueuedLogMessage & message) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
udp_.printf_P(PSTR("<%u>1 "), ((unsigned int)message.content_->facility * 8) + std::min(7U, (unsigned int)message.content_->level));
|
udp_.printf_P(PSTR("<%u>1 "), ((unsigned int)message.content_->facility * 8) + std::min(7U, (unsigned int)message.content_->level));
|
||||||
|
|
||||||
/*
|
|
||||||
if (tm.tm_year != 0) {
|
if (tm.tm_year != 0) {
|
||||||
udp_.printf_P(PSTR("%04u-%02u-%02uT%02u:%02u:%02u.%06luZ"),
|
udp_.printf_P(PSTR("%04u-%02u-%02uT%02u:%02u:%02u.%06u%+02d:%02d"), tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, (uint32_t)message.time_.tv_usec, tzh, tzm);
|
||||||
tm.tm_year + 1900,
|
|
||||||
tm.tm_mon + 1,
|
|
||||||
tm.tm_mday,
|
|
||||||
tm.tm_hour,
|
|
||||||
tm.tm_min,
|
|
||||||
tm.tm_sec,
|
|
||||||
(uint32_t)message.time_.tv_usec);
|
|
||||||
} else {
|
} else {
|
||||||
udp_.print('-');
|
udp_.print('-');
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
udp_.print('-');
|
udp_.printf_P(PSTR(" %s %s - - - \xEF\xBB\xBF"), hostname_.c_str(), uuid::read_flash_string(message.content_->name).c_str());
|
||||||
udp_.printf_P(PSTR(" %s - - - - \xEF\xBB\xBF"), hostname_.c_str());
|
|
||||||
udp_.print(uuid::log::format_timestamp_ms(message.content_->uptime_ms, 3).c_str());
|
udp_.print(uuid::log::format_timestamp_ms(message.content_->uptime_ms, 3).c_str());
|
||||||
|
udp_.printf_P(PSTR(" %c %lu: "), uuid::log::format_level_char(message.content_->level), message.id_);
|
||||||
#pragma GCC diagnostic push
|
|
||||||
#pragma GCC diagnostic ignored "-Wformat"
|
|
||||||
udp_.printf_P(PSTR(" %c %lu: [%S] "), uuid::log::format_level_char(message.content_->level), message.id_, message.content_->name);
|
|
||||||
#pragma GCC diagnostic pop
|
|
||||||
udp_.print(message.content_->text.c_str());
|
udp_.print(message.content_->text.c_str());
|
||||||
bool ok = (udp_.endPacket() == 1);
|
bool ok = (udp_.endPacket() == 1);
|
||||||
|
|
||||||
|
|||||||
@@ -61,6 +61,10 @@ unsigned long millis() {
|
|||||||
return __millis;
|
return __millis;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int64_t esp_timer_get_time() {
|
||||||
|
return __millis;
|
||||||
|
}
|
||||||
|
|
||||||
void delay(unsigned long millis) {
|
void delay(unsigned long millis) {
|
||||||
// __millis += millis;
|
// __millis += millis;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,11 +53,7 @@ int digitalRead(uint8_t pin);
|
|||||||
|
|
||||||
#define PROGMEM
|
#define PROGMEM
|
||||||
#define PGM_P const char *
|
#define PGM_P const char *
|
||||||
#define PSTR(s) \
|
#define PSTR(s) s
|
||||||
(__extension__({ \
|
|
||||||
static const char __c[] = (s); \
|
|
||||||
&__c[0]; \
|
|
||||||
}))
|
|
||||||
|
|
||||||
class __FlashStringHelper;
|
class __FlashStringHelper;
|
||||||
#define FPSTR(string_literal) (reinterpret_cast<const __FlashStringHelper *>(string_literal))
|
#define FPSTR(string_literal) (reinterpret_cast<const __FlashStringHelper *>(string_literal))
|
||||||
@@ -196,11 +192,13 @@ class NativeConsole : public Stream {
|
|||||||
#include <Network.h>
|
#include <Network.h>
|
||||||
|
|
||||||
extern NativeConsole Serial;
|
extern NativeConsole Serial;
|
||||||
extern ETHClass ETH;
|
extern ETHClass ETH;
|
||||||
extern WiFiClass WiFi;
|
extern WiFiClass WiFi;
|
||||||
|
|
||||||
unsigned long millis();
|
unsigned long millis();
|
||||||
|
|
||||||
|
int64_t esp_timer_get_time();
|
||||||
|
|
||||||
void delay(unsigned long millis);
|
void delay(unsigned long millis);
|
||||||
|
|
||||||
void yield(void);
|
void yield(void);
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ class DummySettings {
|
|||||||
bool mqtt_retain = false;
|
bool mqtt_retain = false;
|
||||||
bool enabled = true;
|
bool enabled = true;
|
||||||
uint8_t dallas_format = 1;
|
uint8_t dallas_format = 1;
|
||||||
bool nested_format = true;
|
uint8_t nested_format = 1;
|
||||||
uint8_t ha_climate_format = 1;
|
uint8_t ha_climate_format = 1;
|
||||||
bool ha_enabled = true;
|
bool ha_enabled = true;
|
||||||
String base = "ems-esp";
|
String base = "ems-esp";
|
||||||
|
|||||||
2
makefile
2
makefile
@@ -33,7 +33,7 @@ CXX_STANDARD := -std=c++11
|
|||||||
#----------------------------------------------------------------------
|
#----------------------------------------------------------------------
|
||||||
# Defined Symbols
|
# Defined Symbols
|
||||||
#----------------------------------------------------------------------
|
#----------------------------------------------------------------------
|
||||||
DEFINES += -DARDUINOJSON_ENABLE_STD_STRING=1 -DARDUINOJSON_ENABLE_ARDUINO_STRING -DEMSESP_DEBUG -DEMSESP_STANDALONE -DEMSESP_TEST -DEMSESP_DEFAULT_BOARD_PROFILE=\"LOLIN\"
|
DEFINES += -DARDUINOJSON_ENABLE_STD_STRING=1 -DARDUINOJSON_ENABLE_ARDUINO_STRING -DEMSESP_DEBUG -DEMSESP_STANDALONE -DEMSESP_DEFAULT_BOARD_PROFILE=\"LOLIN\"
|
||||||
|
|
||||||
#----------------------------------------------------------------------
|
#----------------------------------------------------------------------
|
||||||
# Sources & Files
|
# Sources & Files
|
||||||
|
|||||||
29
mock-api/README.md
Normal file
29
mock-api/README.md
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
|
||||||
|
(https://github.com/emsesp/EMS-ESP32/issues/41)
|
||||||
|
|
||||||
|
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 set it up it do
|
||||||
|
```sh
|
||||||
|
% cd mock-api
|
||||||
|
% npm install
|
||||||
|
% cd interface
|
||||||
|
% npm install
|
||||||
|
```
|
||||||
|
|
||||||
|
and to run it
|
||||||
|
```sh
|
||||||
|
% cd interface
|
||||||
|
% npm run dev
|
||||||
|
```
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- It's for local development only
|
||||||
|
- `src/.env.development` is no longer required
|
||||||
|
- CORS is removed, also the build flag
|
||||||
|
- new file `interface/src/setupProxy.js`
|
||||||
|
- new files `mock-api/server.js` with the hardcoded data. Requires its own npm packages for express
|
||||||
|
|
||||||
|
## ToDo
|
||||||
|
- add filter rule to prevent from exposing yourself to malicious attacks when running the dev server(https://medium.com/webpack/webpack-dev-server-middleware-security-issues-1489d950874a)
|
||||||
2818
mock-api/package-lock.json
generated
Normal file
2818
mock-api/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
17
mock-api/package.json
Normal file
17
mock-api/package.json
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"name": "api",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"private": "true",
|
||||||
|
"description": "mock api for EMS-ESP",
|
||||||
|
"main": "server.js",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "nodemon ./server.js localhost 3080",
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
|
},
|
||||||
|
"author": "proddy",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"express": "^4.17.1",
|
||||||
|
"nodemon": "^2.0.7"
|
||||||
|
}
|
||||||
|
}
|
||||||
344
mock-api/server.js
Normal file
344
mock-api/server.js
Normal file
@@ -0,0 +1,344 @@
|
|||||||
|
const express = require('express');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
const app = express();
|
||||||
|
const port = process.env.PORT || 3080;
|
||||||
|
|
||||||
|
app.use(express.static(path.join(__dirname, '../interface/build')));
|
||||||
|
app.use(express.json());
|
||||||
|
|
||||||
|
const ENDPOINT_ROOT = "/rest/";
|
||||||
|
|
||||||
|
// NTP
|
||||||
|
const NTP_STATUS_ENDPOINT = ENDPOINT_ROOT + "ntpStatus";
|
||||||
|
const NTP_SETTINGS_ENDPOINT = ENDPOINT_ROOT + "ntpSettings";
|
||||||
|
const TIME_ENDPOINT = ENDPOINT_ROOT + "time";
|
||||||
|
const ntp_settings = {
|
||||||
|
"enabled": true, "server": "time.google.com", "tz_label": "Europe/Amsterdam", "tz_format": "CET-1CEST,M3.5.0,M10.5.0/3"
|
||||||
|
};
|
||||||
|
const ntp_status = {
|
||||||
|
"status": 1, "utc_time": "2021-04-01T14:25:42Z", "local_time": "2021-04-01T16:25:42", "server": "time.google.com", "uptime": 856
|
||||||
|
}
|
||||||
|
|
||||||
|
// AP
|
||||||
|
const AP_SETTINGS_ENDPOINT = ENDPOINT_ROOT + "apSettings";
|
||||||
|
const AP_STATUS_ENDPOINT = ENDPOINT_ROOT + "apStatus";
|
||||||
|
const ap_settings = {
|
||||||
|
"provision_mode": 1, "ssid": "ems-esp", "password": "ems-esp-neo", "local_ip": "192.168.4.1",
|
||||||
|
"gateway_ip": "192.168.4.1", "subnet_mask": "255.255.255.0"
|
||||||
|
};
|
||||||
|
const ap_status = {
|
||||||
|
"status": 1, "ip_address": "192.168.4.1", "mac_address": "3C:61:05:03:AB:2D", "station_num": 0
|
||||||
|
};
|
||||||
|
|
||||||
|
// NETWORK
|
||||||
|
const NETWORK_SETTINGS_ENDPOINT = ENDPOINT_ROOT + "networkSettings";
|
||||||
|
const NETWORK_STATUS_ENDPOINT = ENDPOINT_ROOT + "networkStatus";
|
||||||
|
const SCAN_NETWORKS_ENDPOINT = ENDPOINT_ROOT + "scanNetworks";
|
||||||
|
const LIST_NETWORKS_ENDPOINT = ENDPOINT_ROOT + "listNetworks";
|
||||||
|
const network_settings = {
|
||||||
|
ssid: "myWifi", password: 'myPassword', hostname: 'ems-esp', static_ip_config: false
|
||||||
|
};
|
||||||
|
const network_status =
|
||||||
|
{
|
||||||
|
"status": 3, "local_ip": "10.10.10.101", "mac_address": "3C:61:05:03:AB:2C", "rssi": -41, "ssid": "home",
|
||||||
|
"bssid": "06:ED:DA:FE:B4:68", "channel": 11, "subnet_mask": "255.255.255.0", "gateway_ip": "10.10.10.1",
|
||||||
|
"dns_ip_1": "10.10.10.1", "dns_ip_2": "0.0.0.0"
|
||||||
|
};
|
||||||
|
const list_networks = {
|
||||||
|
"networks": [
|
||||||
|
{ "rssi": -40, "ssid": "", "bssid": "FC:EC:DA:FD:B4:68", "channel": 11, "encryption_type": 3 },
|
||||||
|
{ "rssi": -41, "ssid": "home", "bssid": "02:EC:DA:FD:B4:68", "channel": 11, "encryption_type": 3 },
|
||||||
|
{ "rssi": -42, "ssid": "", "bssid": "06:EC:DA:FD:B4:68", "channel": 11, "encryption_type": 3 },
|
||||||
|
{ "rssi": -73, "ssid": "", "bssid": "FC:EC:DA:17:D4:7E", "channel": 1, "encryption_type": 3 },
|
||||||
|
{ "rssi": -73, "ssid": "office", "bssid": "02:EC:DA:17:D4:7E", "channel": 1, "encryption_type": 3 },
|
||||||
|
{ "rssi": -75, "ssid": "Erica", "bssid": "C8:D7:19:9A:88:BD", "channel": 2, "encryption_type": 3 },
|
||||||
|
{ "rssi": -75, "ssid": "", "bssid": "C6:C9:E3:FF:A5:DE", "channel": 2, "encryption_type": 3 },
|
||||||
|
{ "rssi": -76, "ssid": "Bruin", "bssid": "C0:C9:E3:FF:A5:DE", "channel": 2, "encryption_type": 3 },
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
// OTA
|
||||||
|
const OTA_SETTINGS_ENDPOINT = ENDPOINT_ROOT + "otaSettings";
|
||||||
|
const ota_settings = {
|
||||||
|
"enabled": true, "port": 8266, "password": "ems-esp-neo"
|
||||||
|
};
|
||||||
|
|
||||||
|
// MQTT
|
||||||
|
const MQTT_SETTINGS_ENDPOINT = ENDPOINT_ROOT + "mqttSettings";
|
||||||
|
const MQTT_STATUS_ENDPOINT = ENDPOINT_ROOT + "mqttStatus";
|
||||||
|
const mqtt_settings = {
|
||||||
|
"enabled": true, "host": "192.168.1.4", "port": 1883, "base": "ems-esp32", "username": "", "password": "",
|
||||||
|
"client_id": "ems-esp32", "keep_alive": 60, "clean_session": true, "max_topic_length": 128,
|
||||||
|
"publish_time_boiler": 10, "publish_time_thermostat": 10, "publish_time_solar": 10, "publish_time_mixer": 10,
|
||||||
|
"publish_time_other": 10, "publish_time_sensor": 10, "mqtt_qos": 0, "mqtt_retain": false, "dallas_format": 1,
|
||||||
|
"bool_format": 1, "ha_climate_format": 1, "ha_enabled": true, "nested_format": 1, "subscribe_format": 0
|
||||||
|
};
|
||||||
|
const mqtt_status = {
|
||||||
|
"enabled": true, "connected": true, "client_id": "ems-esp32", "disconnect_reason": 0, "mqtt_fails": 0
|
||||||
|
};
|
||||||
|
|
||||||
|
// SYSTEM
|
||||||
|
const FEATURES_ENDPOINT = ENDPOINT_ROOT + "features";
|
||||||
|
const VERIFY_AUTHORIZATION_ENDPOINT = ENDPOINT_ROOT + "verifyAuthorization";
|
||||||
|
const SYSTEM_STATUS_ENDPOINT = ENDPOINT_ROOT + "systemStatus";
|
||||||
|
const SECURITY_SETTINGS_ENDPOINT = ENDPOINT_ROOT + "securitySettings";
|
||||||
|
const RESTART_ENDPOINT = ENDPOINT_ROOT + "restart";
|
||||||
|
const FACTORY_RESET_ENDPOINT = ENDPOINT_ROOT + "factoryReset";
|
||||||
|
const UPLOAD_FIRMWARE_ENDPOINT = ENDPOINT_ROOT + "uploadFirmware";
|
||||||
|
const SIGN_IN_ENDPOINT = ENDPOINT_ROOT + "signIn";
|
||||||
|
const GENERATE_TOKEN_ENDPOINT = ENDPOINT_ROOT + "generateToken";
|
||||||
|
const system_status = {
|
||||||
|
"esp_platform": "ESP32", "max_alloc_heap": 113792, "psram_size": 0, "free_psram": 0, "cpu_freq_mhz": 240,
|
||||||
|
"free_heap": 193340, "sdk_version": "v3.3.5-1-g85c43024c", "flash_chip_size": 4194304, "flash_chip_speed": 40000000,
|
||||||
|
"fs_total": 65536, "fs_used": 16384, "uptime": "000+00:15:42.707"
|
||||||
|
};
|
||||||
|
const security_settings = {
|
||||||
|
"jwt_secret": "naughty!", "users": [{ "username": "admin", "password": "admin", "admin": true }, { "username": "guest", "password": "guest", "admin": false }]
|
||||||
|
};
|
||||||
|
const features = {
|
||||||
|
"project": true, "security": true, "mqtt": true, "ntp": true, "ota": true, "upload_firmware": true
|
||||||
|
};
|
||||||
|
const verify_authentication = { access_token: '1234' };
|
||||||
|
const signin = {
|
||||||
|
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwiYWRtaW4iOnRydWUsInZlcnNpb24iOiIzLjAuMmIwIn0.MsHSgoJKI1lyYz77EiT5ZN3ECMrb4mPv9FNy3udq0TU"
|
||||||
|
};
|
||||||
|
const generate_token = { token: '1234' };
|
||||||
|
|
||||||
|
// EMS-ESP Project specific
|
||||||
|
const EMSESP_SETTINGS_ENDPOINT = ENDPOINT_ROOT + "emsespSettings";
|
||||||
|
const EMSESP_ALLDEVICES_ENDPOINT = ENDPOINT_ROOT + "allDevices";
|
||||||
|
const EMSESP_SCANDEVICES_ENDPOINT = ENDPOINT_ROOT + "scanDevices";
|
||||||
|
const EMSESP_DEVICEDATA_ENDPOINT = ENDPOINT_ROOT + "deviceData";
|
||||||
|
const EMSESP_STATUS_ENDPOINT = ENDPOINT_ROOT + "emsespStatus";
|
||||||
|
const EMSESP_BOARDPROFILE_ENDPOINT = ENDPOINT_ROOT + "boardProfile";
|
||||||
|
const WRITE_VALUE_ENDPOINT = ENDPOINT_ROOT + "writeValue";
|
||||||
|
const emsesp_settings = {
|
||||||
|
"tx_mode": 1, "tx_delay": 0, "ems_bus_id": 11, "syslog_enabled": false, "syslog_level": 3,
|
||||||
|
"trace_raw": false, "syslog_mark_interval": 0, "syslog_host": "192.168.1.4", "syslog_port": 514,
|
||||||
|
"master_thermostat": 0, "shower_timer": true, "shower_alert": false, "rx_gpio": 23, "tx_gpio": 5,
|
||||||
|
"dallas_gpio": 3, "dallas_parasite": false, "led_gpio": 2, "hide_led": false, "api_enabled": true,
|
||||||
|
"analog_enabled": false, "pbutton_gpio": 0, "board_profile": "S32"
|
||||||
|
};
|
||||||
|
const emsesp_alldevices = {
|
||||||
|
"devices": [{
|
||||||
|
"id": 1, "type": "Thermostat", "brand": "", "name": "RC20/Moduline 300",
|
||||||
|
"deviceid": 23, "productid": 77, "version": "03.03"
|
||||||
|
}, {
|
||||||
|
"id": 2, "type": "Boiler", "brand": "Nefit", "name": "GBx72/Trendline/Cerapur/Greenstar Si/27i",
|
||||||
|
"deviceid": 8, "productid": 123, "version": "06.01"
|
||||||
|
}, {
|
||||||
|
"id": 3, "type": "Controller", "brand": "", "name": "BC10",
|
||||||
|
"deviceid": 9, "productid": 190, "version": "01.03"
|
||||||
|
}],
|
||||||
|
"sensors": []
|
||||||
|
}
|
||||||
|
const emsesp_status = {
|
||||||
|
"status": 0, "rx_received": 344, "tx_sent": 104, "rx_quality": 100, "tx_quality": 100
|
||||||
|
};
|
||||||
|
const emsesp_devicedata_1 = {
|
||||||
|
"name": "Thermostat: RC20/Moduline 300",
|
||||||
|
"data": [
|
||||||
|
"16:28:21 01/04/2021", "", "date/time", "datetime",
|
||||||
|
"(0)", "", "error code", "",
|
||||||
|
15, "°C", "(hc1) setpoint room temperature", "temp",
|
||||||
|
20.5, "°C", "(hc1) current room temperature", "",
|
||||||
|
"auto", "", "(hc1) mode", "mode"
|
||||||
|
]
|
||||||
|
};
|
||||||
|
const emsesp_devicedata_2 = {
|
||||||
|
"name": "Boiler: Nefit GBx72/Trendline/Cerapur/Greenstar Si/27i",
|
||||||
|
"data": [
|
||||||
|
"off", "", "heating active", "",
|
||||||
|
"off", "", "warm water active", "",
|
||||||
|
5, "°C", "selected flow temperature", "selflowtemp",
|
||||||
|
0, "%", "burner selected max power", "",
|
||||||
|
0, "%", "heating pump modulation", "",
|
||||||
|
42.7, "°C", "current flow temperature", "",
|
||||||
|
39, "°C", "return temperature", "",
|
||||||
|
1.2, "bar", "system pressure", "",
|
||||||
|
45.3, "°C", "max boiler temperature", "",
|
||||||
|
"off", "", "gas", "",
|
||||||
|
0, "uA", "flame current", "",
|
||||||
|
"off", "", "heating pump", "",
|
||||||
|
"off", "", "fan", "",
|
||||||
|
"off", "", "ignition", "",
|
||||||
|
"on", "", "heating activated", "",
|
||||||
|
75, "°C", "heating temperature", "",
|
||||||
|
90, "%", "burner pump max power", "",
|
||||||
|
55, "%", "burner pump min power", "",
|
||||||
|
1, null, "pump delay", "",
|
||||||
|
10, null, "burner min period", "",
|
||||||
|
0, "%", "burner min power", "",
|
||||||
|
75, "%", "burner max power", "",
|
||||||
|
-6, "°C", "hysteresis on temperature", "",
|
||||||
|
6, "°C", "hysteresis off temperature", "",
|
||||||
|
0, "%", "burner current power", "",
|
||||||
|
295740, "", "burner # starts", "",
|
||||||
|
"344 days 2 hours 8 minutes", null, "total burner operating time", "",
|
||||||
|
"279 days 11 hours 55 minutes", null, "total heat operating time", "",
|
||||||
|
"2946 days 19 hours 8 minutes", null, "total UBA operating time", "",
|
||||||
|
"1C(210) 06.06.2020 12:07", "", "last error code", "",
|
||||||
|
"0H", "", "service code", "",
|
||||||
|
203, "", "service code number", "",
|
||||||
|
"01.01.2012", "", "maintenance set date", "",
|
||||||
|
"off", "", "maintenance scheduled", "",
|
||||||
|
6000, "hours", "maintenance set time", "",
|
||||||
|
60, "°C", "(warm water) selected temperature", "",
|
||||||
|
62, "°C", "(warm water) set temperature", "",
|
||||||
|
"flow", "", "(warm water) type", "",
|
||||||
|
"hot", "", "(warm water) comfort", "",
|
||||||
|
40, "", "(warm water) flow temperature offset", "",
|
||||||
|
100, "%", "(warm water) max power", "",
|
||||||
|
"off", "", "(warm water) circulation pump available", "",
|
||||||
|
"3-way valve", "", "(warm water) charging type", "",
|
||||||
|
70, "°C", "(warm water) disinfection temperature", "",
|
||||||
|
"off", "", "(warm water) circulation pump freq", "",
|
||||||
|
"off", "", "(warm water) circulation active", "",
|
||||||
|
34.7, "°C", "(warm water) current intern temperature", "",
|
||||||
|
0, "l/min", "(warm water) current tap water flow", "",
|
||||||
|
34.6, "°C", "(warm water) storage intern temperature", "",
|
||||||
|
"on", "", "(warm water) activated", "",
|
||||||
|
"off", "", "(warm water) one time charging", "",
|
||||||
|
"off", "", "(warm water) disinfecting", "",
|
||||||
|
"off", "", "(warm water) charging", "",
|
||||||
|
"off", "", "(warm water) recharging", "",
|
||||||
|
"on", "", "(warm water) temperature ok", "",
|
||||||
|
"off", "", "(warm water) active", "",
|
||||||
|
"on", "", "(warm water) heating", "",
|
||||||
|
262387, "", "(warm water) # starts", "",
|
||||||
|
"64 days 14 hours 13 minutes", null, "(warm water) active time", ""
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
// NETWORK
|
||||||
|
app.get(NETWORK_STATUS_ENDPOINT, (req, res) => { res.json(network_status); });
|
||||||
|
app.get(NETWORK_SETTINGS_ENDPOINT, (req, res) => { res.json(network_settings); });
|
||||||
|
app.post(NETWORK_SETTINGS_ENDPOINT, (req, res) => { res.json(network_settings); });
|
||||||
|
app.get(LIST_NETWORKS_ENDPOINT, (req, res) => { res.json(list_networks); });
|
||||||
|
app.get(SCAN_NETWORKS_ENDPOINT, (req, res) => { res.sendStatus(202); });
|
||||||
|
|
||||||
|
// AP
|
||||||
|
app.get(AP_SETTINGS_ENDPOINT, (req, res) => { res.json(ap_settings); });
|
||||||
|
app.get(AP_STATUS_ENDPOINT, (req, res) => { res.json(ap_status); });
|
||||||
|
app.post(AP_SETTINGS_ENDPOINT, (req, res) => { res.json(ap_settings); });
|
||||||
|
|
||||||
|
// OTA
|
||||||
|
app.get(OTA_SETTINGS_ENDPOINT, (req, res) => { res.json(ota_settings); });
|
||||||
|
app.post(OTA_SETTINGS_ENDPOINT, (req, res) => { res.json(ota_settings); });
|
||||||
|
|
||||||
|
// MQTT
|
||||||
|
app.get(MQTT_SETTINGS_ENDPOINT, (req, res) => { res.json(mqtt_settings); });
|
||||||
|
app.post(MQTT_SETTINGS_ENDPOINT, (req, res) => { res.json(mqtt_settings); });
|
||||||
|
app.get(MQTT_STATUS_ENDPOINT, (req, res) => { res.json(mqtt_status); });
|
||||||
|
|
||||||
|
// NTP
|
||||||
|
app.get(NTP_SETTINGS_ENDPOINT, (req, res) => { res.json(ntp_settings); });
|
||||||
|
app.post(NTP_SETTINGS_ENDPOINT, (req, res) => { res.json(ntp_settings); });
|
||||||
|
app.get(NTP_STATUS_ENDPOINT, (req, res) => { res.json(ntp_status); });
|
||||||
|
app.post(TIME_ENDPOINT, (req, res) => { res.sendStatus(200); });
|
||||||
|
|
||||||
|
// SYSTEM
|
||||||
|
app.get(SYSTEM_STATUS_ENDPOINT, (req, res) => { res.json(system_status); });
|
||||||
|
app.get(SECURITY_SETTINGS_ENDPOINT, (req, res) => { res.json(security_settings); });
|
||||||
|
app.post(SECURITY_SETTINGS_ENDPOINT, (req, res) => { res.json(security_settings); });
|
||||||
|
app.get(FEATURES_ENDPOINT, (req, res) => { res.json(features); });
|
||||||
|
app.get(VERIFY_AUTHORIZATION_ENDPOINT, (req, res) => { res.json(verify_authentication); });
|
||||||
|
app.post(RESTART_ENDPOINT, (req, res) => { res.sendStatus(200); });
|
||||||
|
app.post(FACTORY_RESET_ENDPOINT, (req, res) => { res.sendStatus(200); });
|
||||||
|
app.post(UPLOAD_FIRMWARE_ENDPOINT, (req, res) => { res.sendStatus(200); });
|
||||||
|
app.post(SIGN_IN_ENDPOINT, (req, res) => { res.json(signin); });
|
||||||
|
app.get(GENERATE_TOKEN_ENDPOINT, (req, res) => { res.json(generate_token); });
|
||||||
|
|
||||||
|
// EMS-ESP Project stuff
|
||||||
|
app.get(EMSESP_SETTINGS_ENDPOINT, (req, res) => { res.json(emsesp_settings); });
|
||||||
|
app.post(EMSESP_SETTINGS_ENDPOINT, (req, res) => { res.json(emsesp_settings); });
|
||||||
|
app.get(EMSESP_ALLDEVICES_ENDPOINT, (req, res) => { res.json(emsesp_alldevices); });
|
||||||
|
app.post(EMSESP_SCANDEVICES_ENDPOINT, (req, res) => { res.sendStatus(200); });
|
||||||
|
app.get(EMSESP_STATUS_ENDPOINT, (req, res) => { res.json(emsesp_status); });
|
||||||
|
app.post(EMSESP_DEVICEDATA_ENDPOINT, (req, res) => {
|
||||||
|
const id = req.body.id;
|
||||||
|
if (id == 1) {
|
||||||
|
res.json(emsesp_devicedata_1);
|
||||||
|
}
|
||||||
|
if (id == 2) {
|
||||||
|
res.json(emsesp_devicedata_2);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
app.post(WRITE_VALUE_ENDPOINT, (req, res) => {
|
||||||
|
const devicevalue = req.body.devicevalue;
|
||||||
|
|
||||||
|
console.log(devicevalue);
|
||||||
|
|
||||||
|
res.sendStatus(200);
|
||||||
|
});
|
||||||
|
|
||||||
|
app.post(EMSESP_BOARDPROFILE_ENDPOINT, (req, res) => {
|
||||||
|
const board_profile = req.body.code;
|
||||||
|
|
||||||
|
const data = {
|
||||||
|
led_gpio: 1,
|
||||||
|
dallas_gpio: 2,
|
||||||
|
rx_gpio: 3,
|
||||||
|
tx_gpio: 4,
|
||||||
|
pbutton_gpio: 5
|
||||||
|
};
|
||||||
|
|
||||||
|
if (board_profile == "S32") { // BBQKees Gateway S32
|
||||||
|
data.led_gpio = 2;
|
||||||
|
data.dallas_gpio = 18;
|
||||||
|
data.rx_gpio = 23;
|
||||||
|
data.tx_gpio = 5;
|
||||||
|
data.pbutton_gpio = 0;
|
||||||
|
} else if (board_profile == "E32") { // BBQKees Gateway E32
|
||||||
|
data.led_gpio = 2;
|
||||||
|
data.dallas_gpio = 4;
|
||||||
|
data.rx_gpio = 5;
|
||||||
|
data.tx_gpio = 17;
|
||||||
|
data.pbutton_gpio = 33;
|
||||||
|
} else if (board_profile == "MH-ET") { // MH-ET Live D1 Mini
|
||||||
|
data.led_gpio = 2;
|
||||||
|
data.dallas_gpio = 18;
|
||||||
|
data.rx_gpio = 23;
|
||||||
|
data.tx_gpio = 5;
|
||||||
|
data.pbutton_gpio = 0;
|
||||||
|
} else if (board_profile == "NODEMCU") { // NodeMCU 32S
|
||||||
|
data.led_gpio = 2;
|
||||||
|
data.dallas_gpio = 18;
|
||||||
|
data.rx_gpio = 23;
|
||||||
|
data.tx_gpio = 5;
|
||||||
|
data.pbutton_gpio = 0;
|
||||||
|
} else if (board_profile == "LOLIN") {// Lolin D32
|
||||||
|
data.led_gpio = 2;
|
||||||
|
data.dallas_gpio = 18;
|
||||||
|
data.rx_gpio = 17;
|
||||||
|
data.tx_gpio = 16;
|
||||||
|
data.pbutton_gpio = 0;
|
||||||
|
} else if (board_profile == "OLIMEX") {// Olimex ESP32-EVB (uses U1TXD/U1RXD/BUTTON, no LED or Dallas)
|
||||||
|
data.led_gpio = 0;
|
||||||
|
data.dallas_gpio = 0;
|
||||||
|
data.rx_gpio = 36;
|
||||||
|
data.tx_gpio = 4;
|
||||||
|
data.pbutton_gpio = 34;
|
||||||
|
// data = { 0, 0, 36, 4, 34};
|
||||||
|
} else if (board_profile == "TLK110") {// Generic Ethernet (TLK110)
|
||||||
|
data.led_gpio = 2;
|
||||||
|
data.dallas_gpio = 4;
|
||||||
|
data.rx_gpio = 5;
|
||||||
|
data.tx_gpio = 17;
|
||||||
|
data.pbutton_gpio = 33;
|
||||||
|
} else if (board_profile == "LAN8720") {// Generic Ethernet (LAN8720)
|
||||||
|
data.led_gpio = 2;
|
||||||
|
data.dallas_gpio = 4;
|
||||||
|
data.rx_gpio = 5;
|
||||||
|
data.tx_gpio = 17;
|
||||||
|
data.pbutton_gpio = 33;
|
||||||
|
}
|
||||||
|
|
||||||
|
res.json(data);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
app.listen(port);
|
||||||
|
console.log(`Mock API Server is up and running at: http://localhost:${port}`);
|
||||||
@@ -8,15 +8,15 @@ upload_flags =
|
|||||||
upload_port = 10.10.10.101
|
upload_port = 10.10.10.101
|
||||||
|
|
||||||
[common]
|
[common]
|
||||||
; options are EMSESP_DEBUG EMSESP_UART_DEBUG EMSESP_TEST ENABLE_CORS DEMSESP_DEFAULT_BOARD_PROFILE
|
; options are EMSESP_DEBUG EMSESP_UART_DEBUG EMSESP_DEBUG_SENSOR
|
||||||
; debug_flags = -DENABLE_CORS -DEMSESP_DEBUG -DEMSESP_TEST -DCORS_ORIGIN=\"http://localhost:3000\"
|
; plus all the settings in default_settings.h, e.g. -DEMSESP_DEFAULT_BOARD_PROFILE=\"NODEMCU\"
|
||||||
; debug_flags = -DEMSESP_DEFAULT_BOARD_PROFILE=\"NODEMCU\"
|
; debug_flags = -DEMSESP_DEBUG
|
||||||
|
|
||||||
[env:esp32]
|
[env:esp32]
|
||||||
monitor_filters = esp32_exception_decoder
|
monitor_filters = esp32_exception_decoder
|
||||||
debug_tool = esp-prog
|
debug_tool = esp-prog
|
||||||
debug_init_break = tbreak setup
|
debug_init_break = tbreak setup
|
||||||
extra_scripts =
|
extra_scripts =
|
||||||
|
; to prevent the web UI from building each time, comment out this next line
|
||||||
; pre:scripts/build_interface.py
|
; pre:scripts/build_interface.py
|
||||||
; scripts/upload_fw.py
|
|
||||||
|
|
||||||
|
|||||||
@@ -28,10 +28,6 @@ WebAPIService::WebAPIService(AsyncWebServer * server) {
|
|||||||
|
|
||||||
// e.g. http://ems-esp/api?device=boiler&cmd=wwtemp&data=20&id=1
|
// e.g. http://ems-esp/api?device=boiler&cmd=wwtemp&data=20&id=1
|
||||||
void WebAPIService::webAPIService(AsyncWebServerRequest * request) {
|
void WebAPIService::webAPIService(AsyncWebServerRequest * request) {
|
||||||
// see if the API is enabled
|
|
||||||
bool api_enabled;
|
|
||||||
EMSESP::webSettingsService.read([&](WebSettings & settings) { api_enabled = settings.api_enabled; });
|
|
||||||
|
|
||||||
// must have device and cmd parameters
|
// must have device and cmd parameters
|
||||||
if ((!request->hasParam(F_(device))) || (!request->hasParam(F_(cmd)))) {
|
if ((!request->hasParam(F_(device))) || (!request->hasParam(F_(cmd)))) {
|
||||||
request->send(400, "text/plain", F("Invalid syntax"));
|
request->send(400, "text/plain", F("Invalid syntax"));
|
||||||
@@ -49,12 +45,6 @@ void WebAPIService::webAPIService(AsyncWebServerRequest * request) {
|
|||||||
// get cmd, we know we have one
|
// get cmd, we know we have one
|
||||||
String cmd = request->getParam(F_(cmd))->value();
|
String cmd = request->getParam(F_(cmd))->value();
|
||||||
|
|
||||||
// look up command in our list
|
|
||||||
if (Command::find_command(device_type, cmd.c_str()) == nullptr) {
|
|
||||||
request->send(400, "text/plain", F("Invalid cmd"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
String data;
|
String data;
|
||||||
if (request->hasParam(F_(data))) {
|
if (request->hasParam(F_(data))) {
|
||||||
data = request->getParam(F_(data))->value();
|
data = request->getParam(F_(data))->value();
|
||||||
@@ -77,8 +67,10 @@ void WebAPIService::webAPIService(AsyncWebServerRequest * request) {
|
|||||||
if (data.isEmpty()) {
|
if (data.isEmpty()) {
|
||||||
ok = Command::call(device_type, cmd.c_str(), nullptr, id.toInt(), json); // command only
|
ok = Command::call(device_type, cmd.c_str(), nullptr, id.toInt(), json); // command only
|
||||||
} else {
|
} else {
|
||||||
|
// we only allow commands with parameters if the API is enabled
|
||||||
|
bool api_enabled;
|
||||||
|
EMSESP::webSettingsService.read([&](WebSettings & settings) { api_enabled = settings.api_enabled; });
|
||||||
if (api_enabled) {
|
if (api_enabled) {
|
||||||
// we only allow commands with parameters if the API is enabled
|
|
||||||
ok = Command::call(device_type, cmd.c_str(), data.c_str(), id.toInt(), json); // has cmd, data and id
|
ok = Command::call(device_type, cmd.c_str(), data.c_str(), id.toInt(), json); // has cmd, data and id
|
||||||
} else {
|
} else {
|
||||||
request->send(401, "text/plain", F("Unauthorized"));
|
request->send(401, "text/plain", F("Unauthorized"));
|
||||||
@@ -90,7 +82,7 @@ void WebAPIService::webAPIService(AsyncWebServerRequest * request) {
|
|||||||
doc.shrinkToFit();
|
doc.shrinkToFit();
|
||||||
std::string buffer;
|
std::string buffer;
|
||||||
serializeJsonPretty(doc, buffer);
|
serializeJsonPretty(doc, buffer);
|
||||||
request->send(200, "text/plain", buffer.c_str());
|
request->send(200, "text/plain;charset=utf-8", buffer.c_str());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
request->send(200, "text/plain", ok ? F("OK") : F("Invalid"));
|
request->send(200, "text/plain", ok ? F("OK") : F("Invalid"));
|
||||||
|
|||||||
@@ -23,14 +23,18 @@ namespace emsesp {
|
|||||||
using namespace std::placeholders; // for `_1` etc
|
using namespace std::placeholders; // for `_1` etc
|
||||||
|
|
||||||
WebDevicesService::WebDevicesService(AsyncWebServer * server, SecurityManager * securityManager)
|
WebDevicesService::WebDevicesService(AsyncWebServer * server, SecurityManager * securityManager)
|
||||||
: _device_dataHandler(DEVICE_DATA_SERVICE_PATH, securityManager->wrapCallback(std::bind(&WebDevicesService::device_data, this, _1, _2), AuthenticationPredicates::IS_AUTHENTICATED)) {
|
: _device_dataHandler(DEVICE_DATA_SERVICE_PATH, securityManager->wrapCallback(std::bind(&WebDevicesService::device_data, this, _1, _2), AuthenticationPredicates::IS_AUTHENTICATED))
|
||||||
|
, _writevalue_dataHandler(WRITE_VALUE_SERVICE_PATH, securityManager->wrapCallback(std::bind(&WebDevicesService::write_value, this, _1, _2), AuthenticationPredicates::IS_AUTHENTICATED)) {
|
||||||
server->on(EMSESP_DEVICES_SERVICE_PATH, HTTP_GET, securityManager->wrapRequest(std::bind(&WebDevicesService::all_devices, this, _1), AuthenticationPredicates::IS_AUTHENTICATED));
|
server->on(EMSESP_DEVICES_SERVICE_PATH, HTTP_GET, securityManager->wrapRequest(std::bind(&WebDevicesService::all_devices, this, _1), AuthenticationPredicates::IS_AUTHENTICATED));
|
||||||
|
|
||||||
server->on(SCAN_DEVICES_SERVICE_PATH, HTTP_GET, securityManager->wrapRequest(std::bind(&WebDevicesService::scan_devices, this, _1), AuthenticationPredicates::IS_AUTHENTICATED));
|
server->on(SCAN_DEVICES_SERVICE_PATH, HTTP_GET, securityManager->wrapRequest(std::bind(&WebDevicesService::scan_devices, this, _1), AuthenticationPredicates::IS_AUTHENTICATED));
|
||||||
|
|
||||||
_device_dataHandler.setMethod(HTTP_POST);
|
_device_dataHandler.setMethod(HTTP_POST);
|
||||||
_device_dataHandler.setMaxContentLength(256);
|
_device_dataHandler.setMaxContentLength(256);
|
||||||
server->addHandler(&_device_dataHandler);
|
server->addHandler(&_device_dataHandler);
|
||||||
|
|
||||||
|
_writevalue_dataHandler.setMethod(HTTP_POST);
|
||||||
|
_writevalue_dataHandler.setMaxContentLength(256);
|
||||||
|
server->addHandler(&_writevalue_dataHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebDevicesService::scan_devices(AsyncWebServerRequest * request) {
|
void WebDevicesService::scan_devices(AsyncWebServerRequest * request) {
|
||||||
@@ -96,4 +100,49 @@ void WebDevicesService::device_data(AsyncWebServerRequest * request, JsonVariant
|
|||||||
request->send(response);
|
request->send(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// takes a command and its data value from a specific Device, from the Web
|
||||||
|
void WebDevicesService::write_value(AsyncWebServerRequest * request, JsonVariant & json) {
|
||||||
|
// only issue commands if the API is enabled
|
||||||
|
EMSESP::webSettingsService.read([&](WebSettings & settings) {
|
||||||
|
if (!settings.api_enabled) {
|
||||||
|
request->send(403); // forbidden error
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (json.is<JsonObject>()) {
|
||||||
|
JsonObject dv = json["devicevalue"];
|
||||||
|
|
||||||
|
// using the unique ID from the web find the real device type
|
||||||
|
for (const auto & emsdevice : EMSESP::emsdevices) {
|
||||||
|
if (emsdevice) {
|
||||||
|
if (emsdevice->unique_id() == dv["id"].as<int>()) {
|
||||||
|
const char * cmd = dv["cmd"];
|
||||||
|
uint8_t device_type = emsdevice->device_type();
|
||||||
|
bool ok = false;
|
||||||
|
char s[10];
|
||||||
|
// the data could be in any format, but we need string
|
||||||
|
JsonVariant data = dv["data"];
|
||||||
|
if (data.is<char *>()) {
|
||||||
|
ok = Command::call(device_type, cmd, data.as<const char *>());
|
||||||
|
} else if (data.is<int>()) {
|
||||||
|
ok = Command::call(device_type, cmd, Helpers::render_value(s, data.as<int16_t>(), 0));
|
||||||
|
} else if (data.is<float>()) {
|
||||||
|
ok = Command::call(device_type, cmd, Helpers::render_value(s, (float)data.as<float>(), 1));
|
||||||
|
} else if (data.is<bool>()) {
|
||||||
|
ok = Command::call(device_type, cmd, data.as<bool>() ? "true" : "false");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ok) {
|
||||||
|
request->send(200);
|
||||||
|
}
|
||||||
|
return; // found device, quit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
request->send(204); // no content error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace emsesp
|
} // namespace emsesp
|
||||||
@@ -27,6 +27,8 @@
|
|||||||
#define EMSESP_DEVICES_SERVICE_PATH "/rest/allDevices"
|
#define EMSESP_DEVICES_SERVICE_PATH "/rest/allDevices"
|
||||||
#define SCAN_DEVICES_SERVICE_PATH "/rest/scanDevices"
|
#define SCAN_DEVICES_SERVICE_PATH "/rest/scanDevices"
|
||||||
#define DEVICE_DATA_SERVICE_PATH "/rest/deviceData"
|
#define DEVICE_DATA_SERVICE_PATH "/rest/deviceData"
|
||||||
|
#define WRITE_VALUE_SERVICE_PATH "/rest/writeValue"
|
||||||
|
|
||||||
|
|
||||||
namespace emsesp {
|
namespace emsesp {
|
||||||
|
|
||||||
@@ -35,11 +37,15 @@ class WebDevicesService {
|
|||||||
WebDevicesService(AsyncWebServer * server, SecurityManager * securityManager);
|
WebDevicesService(AsyncWebServer * server, SecurityManager * securityManager);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
// GET
|
||||||
void all_devices(AsyncWebServerRequest * request);
|
void all_devices(AsyncWebServerRequest * request);
|
||||||
void scan_devices(AsyncWebServerRequest * request);
|
void scan_devices(AsyncWebServerRequest * request);
|
||||||
void device_data(AsyncWebServerRequest * request, JsonVariant & json);
|
|
||||||
|
|
||||||
AsyncCallbackJsonWebHandler _device_dataHandler;
|
// POST
|
||||||
|
void device_data(AsyncWebServerRequest * request, JsonVariant & json);
|
||||||
|
void write_value(AsyncWebServerRequest * request, JsonVariant & json);
|
||||||
|
|
||||||
|
AsyncCallbackJsonWebHandler _device_dataHandler, _writevalue_dataHandler;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace emsesp
|
} // namespace emsesp
|
||||||
|
|||||||
@@ -22,39 +22,12 @@
|
|||||||
#include <HttpEndpoint.h>
|
#include <HttpEndpoint.h>
|
||||||
#include <FSPersistence.h>
|
#include <FSPersistence.h>
|
||||||
|
|
||||||
|
#include "default_settings.h"
|
||||||
|
|
||||||
#define EMSESP_SETTINGS_FILE "/config/emsespSettings.json"
|
#define EMSESP_SETTINGS_FILE "/config/emsespSettings.json"
|
||||||
#define EMSESP_SETTINGS_SERVICE_PATH "/rest/emsespSettings"
|
#define EMSESP_SETTINGS_SERVICE_PATH "/rest/emsespSettings"
|
||||||
#define EMSESP_BOARD_PROFILE_SERVICE_PATH "/rest/boardProfile"
|
#define EMSESP_BOARD_PROFILE_SERVICE_PATH "/rest/boardProfile"
|
||||||
|
|
||||||
#define EMSESP_DEFAULT_TX_MODE 1 // EMS1.0
|
|
||||||
#define EMSESP_DEFAULT_TX_DELAY 0 // no delay
|
|
||||||
#define EMSESP_DEFAULT_EMS_BUS_ID 0x0B // service key
|
|
||||||
#define EMSESP_DEFAULT_SYSLOG_ENABLED false
|
|
||||||
#define EMSESP_DEFAULT_SYSLOG_LEVEL 3 // ERR
|
|
||||||
#define EMSESP_DEFAULT_SYSLOG_MARK_INTERVAL 0
|
|
||||||
#define EMSESP_DEFAULT_SYSLOG_HOST ""
|
|
||||||
#define EMSESP_DEFAULT_SYSLOG_PORT 514
|
|
||||||
#define EMSESP_DEFAULT_TRACELOG_RAW false
|
|
||||||
#define EMSESP_DEFAULT_MASTER_THERMOSTAT 0 // not set
|
|
||||||
#define EMSESP_DEFAULT_SHOWER_TIMER false
|
|
||||||
#define EMSESP_DEFAULT_SHOWER_ALERT false
|
|
||||||
#define EMSESP_DEFAULT_HIDE_LED false
|
|
||||||
#define EMSESP_DEFAULT_DALLAS_PARASITE false
|
|
||||||
#define EMSESP_DEFAULT_API_ENABLED false // turn off, because its insecure
|
|
||||||
#define EMSESP_DEFAULT_BOOL_FORMAT 1 // on/off
|
|
||||||
#define EMSESP_DEFAULT_ANALOG_ENABLED false
|
|
||||||
|
|
||||||
#ifndef EMSESP_DEFAULT_BOARD_PROFILE
|
|
||||||
#define EMSESP_DEFAULT_BOARD_PROFILE "S32" // Gateway S32
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Default GPIO PIN definitions - based on Wemos/Nodemcu
|
|
||||||
#define EMSESP_DEFAULT_RX_GPIO 23 // D7
|
|
||||||
#define EMSESP_DEFAULT_TX_GPIO 5 // D8
|
|
||||||
#define EMSESP_DEFAULT_DALLAS_GPIO 18
|
|
||||||
#define EMSESP_DEFAULT_LED_GPIO 2
|
|
||||||
#define EMSESP_DEFAULT_PBUTTON_GPIO 0
|
|
||||||
|
|
||||||
namespace emsesp {
|
namespace emsesp {
|
||||||
|
|
||||||
class WebSettings {
|
class WebSettings {
|
||||||
|
|||||||
@@ -30,14 +30,18 @@ std::vector<Command::CmdFunction> Command::cmdfunctions_;
|
|||||||
// id may be used to represent a heating circuit for example, it's optional
|
// id may be used to represent a heating circuit for example, it's optional
|
||||||
// returns false if error or not found
|
// returns false if error or not found
|
||||||
bool Command::call(const uint8_t device_type, const char * cmd, const char * value, const int8_t id) {
|
bool Command::call(const uint8_t device_type, const char * cmd, const char * value, const int8_t id) {
|
||||||
auto cf = find_command(device_type, cmd);
|
std::string dname = EMSdevice::device_type_2_device_name(device_type);
|
||||||
|
int8_t id_new = id;
|
||||||
|
char cmd_new[20];
|
||||||
|
|
||||||
|
check_command(cmd_new, cmd, id_new);
|
||||||
|
auto cf = find_command(device_type, cmd_new);
|
||||||
if ((cf == nullptr) || (cf->cmdfunction_json_)) {
|
if ((cf == nullptr) || (cf->cmdfunction_json_)) {
|
||||||
LOG_WARNING(F("Command %s not found"), cmd);
|
LOG_WARNING(F("Command %s on %s not found"), cmd, dname.c_str());
|
||||||
return false; // command not found, or requires a json
|
return false; // command not found, or requires a json
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef EMSESP_DEBUG
|
#ifdef EMSESP_DEBUG
|
||||||
std::string dname = EMSdevice::device_type_2_device_name(device_type);
|
|
||||||
if (value == nullptr) {
|
if (value == nullptr) {
|
||||||
LOG_DEBUG(F("[DEBUG] Calling %s command %s"), dname.c_str(), cmd);
|
LOG_DEBUG(F("[DEBUG] Calling %s command %s"), dname.c_str(), cmd);
|
||||||
} else if (id == -1) {
|
} else if (id == -1) {
|
||||||
@@ -47,18 +51,18 @@ bool Command::call(const uint8_t device_type, const char * cmd, const char * val
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return ((cf->cmdfunction_)(value, id));
|
return ((cf->cmdfunction_)(value, id_new));
|
||||||
}
|
}
|
||||||
|
|
||||||
// calls a command. Takes a json object for output.
|
// calls a command. Takes a json object for output.
|
||||||
// id may be used to represent a heating circuit for example
|
// id may be used to represent a heating circuit for example
|
||||||
// returns false if error or not found
|
// returns false if error or not found
|
||||||
bool Command::call(const uint8_t device_type, const char * cmd, const char * value, const int8_t id, JsonObject & json) {
|
bool Command::call(const uint8_t device_type, const char * cmd, const char * value, const int8_t id, JsonObject & json) {
|
||||||
auto cf = find_command(device_type, cmd);
|
int8_t id_new = id;
|
||||||
if (cf == nullptr) {
|
char cmd_new[20];
|
||||||
LOG_WARNING(F("Command %s not found"), cmd);
|
|
||||||
return false; // command not found or not json
|
check_command(cmd_new, cmd, id_new);
|
||||||
}
|
auto cf = find_command(device_type, cmd_new);
|
||||||
|
|
||||||
#ifdef EMSESP_DEBUG
|
#ifdef EMSESP_DEBUG
|
||||||
std::string dname = EMSdevice::device_type_2_device_name(device_type);
|
std::string dname = EMSdevice::device_type_2_device_name(device_type);
|
||||||
@@ -77,13 +81,49 @@ bool Command::call(const uint8_t device_type, const char * cmd, const char * val
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!cf->cmdfunction_json_) {
|
if (cf == nullptr) {
|
||||||
return ((cf->cmdfunction_)(value, id));
|
return EMSESP::get_device_value_info(json, cmd_new, id_new, device_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cf->cmdfunction_json_) {
|
||||||
|
return ((cf->cmdfunction_json_)(value, id_new, json));
|
||||||
} else {
|
} else {
|
||||||
return ((cf->cmdfunction_json_)(value, id, json));
|
if (value == nullptr || strlen(value) == 0 || strcmp(value, "?") == 0 || strcmp(value, "*") == 0) {
|
||||||
|
return EMSESP::get_device_value_info(json, cmd_new, id_new, device_type);
|
||||||
|
}
|
||||||
|
return ((cf->cmdfunction_)(value, id_new));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
char * Command::check_command(char * out, const char * cmd, int8_t & id) {
|
||||||
|
// convert cmd to lowercase
|
||||||
|
strlcpy(out, cmd, 20);
|
||||||
|
for (char * p = out; *p; p++) {
|
||||||
|
*p = tolower(*p);
|
||||||
|
}
|
||||||
|
|
||||||
|
//scan for prefix hc.
|
||||||
|
for (uint8_t i = DeviceValueTAG::TAG_HC1; i <= DeviceValueTAG::TAG_HC4; i++) {
|
||||||
|
if ((strncmp(out, EMSdevice::tag_to_string(i).c_str(), 3) == 0) && (strlen(out) > 4)) {
|
||||||
|
strcpy(out, &out[4]);
|
||||||
|
id = 1 + i - DeviceValueTAG::TAG_HC1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//scan for prefix wwc.
|
||||||
|
for (uint8_t i = DeviceValueTAG::TAG_WWC1; i <= DeviceValueTAG::TAG_WWC4; i++) {
|
||||||
|
if ((strncmp(out, EMSdevice::tag_to_string(i).c_str(), 4) == 0) && (strlen(out) > 5)) {
|
||||||
|
strcpy(out, &out[5]);
|
||||||
|
id = 8 + i - DeviceValueTAG::TAG_WWC1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// add a command to the list, which does not return json
|
// add a command to the list, which does not return json
|
||||||
void Command::add(const uint8_t device_type, const __FlashStringHelper * cmd, cmdfunction_p cb, uint8_t flag) {
|
void Command::add(const uint8_t device_type, const __FlashStringHelper * cmd, cmdfunction_p cb, uint8_t flag) {
|
||||||
// if the command already exists for that device type don't add it
|
// if the command already exists for that device type don't add it
|
||||||
@@ -110,6 +150,7 @@ void Command::add_with_json(const uint8_t device_type, const __FlashStringHelper
|
|||||||
}
|
}
|
||||||
|
|
||||||
// see if a command exists for that device type
|
// see if a command exists for that device type
|
||||||
|
// is not case sensitive
|
||||||
Command::CmdFunction * Command::find_command(const uint8_t device_type, const char * cmd) {
|
Command::CmdFunction * Command::find_command(const uint8_t device_type, const char * cmd) {
|
||||||
if ((cmd == nullptr) || (strlen(cmd) == 0) || (cmdfunctions_.empty())) {
|
if ((cmd == nullptr) || (strlen(cmd) == 0) || (cmdfunctions_.empty())) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
@@ -123,7 +164,7 @@ Command::CmdFunction * Command::find_command(const uint8_t device_type, const ch
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (auto & cf : cmdfunctions_) {
|
for (auto & cf : cmdfunctions_) {
|
||||||
if (!strcmp_P(lowerCmd, reinterpret_cast<PGM_P>(cf.cmd_)) && (cf.device_type_ == device_type)) {
|
if (!strcmp(lowerCmd, Helpers::toLower(uuid::read_flash_string(cf.cmd_)).c_str()) && (cf.device_type_ == device_type)) {
|
||||||
return &cf;
|
return &cf;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ class Command {
|
|||||||
public:
|
public:
|
||||||
struct CmdFunction {
|
struct CmdFunction {
|
||||||
uint8_t device_type_; // DeviceType::
|
uint8_t device_type_; // DeviceType::
|
||||||
uint8_t flag_;
|
uint8_t flag_; // mqtt flags for command subscriptions
|
||||||
const __FlashStringHelper * cmd_;
|
const __FlashStringHelper * cmd_;
|
||||||
cmdfunction_p cmdfunction_;
|
cmdfunction_p cmdfunction_;
|
||||||
cmdfunction_json_p cmdfunction_json_;
|
cmdfunction_json_p cmdfunction_json_;
|
||||||
@@ -65,6 +65,7 @@ class Command {
|
|||||||
static void add_with_json(const uint8_t device_type, const __FlashStringHelper * cmd, cmdfunction_json_p cb);
|
static void add_with_json(const uint8_t device_type, const __FlashStringHelper * cmd, cmdfunction_json_p cb);
|
||||||
static void show_all(uuid::console::Shell & shell);
|
static void show_all(uuid::console::Shell & shell);
|
||||||
static Command::CmdFunction * find_command(const uint8_t device_type, const char * cmd);
|
static Command::CmdFunction * find_command(const uint8_t device_type, const char * cmd);
|
||||||
|
static char * check_command(char * out, const char * cmd, int8_t & id);
|
||||||
|
|
||||||
static void show(uuid::console::Shell & shell, uint8_t device_type);
|
static void show(uuid::console::Shell & shell, uint8_t device_type);
|
||||||
static void show_devices(uuid::console::Shell & shell);
|
static void show_devices(uuid::console::Shell & shell);
|
||||||
|
|||||||
195
src/console.cpp
195
src/console.cpp
@@ -20,7 +20,7 @@
|
|||||||
#include "emsesp.h"
|
#include "emsesp.h"
|
||||||
#include "version.h"
|
#include "version.h"
|
||||||
|
|
||||||
#if defined(EMSESP_TEST)
|
#if defined(EMSESP_DEBUG)
|
||||||
#include "test/test.h"
|
#include "test/test.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -54,9 +54,8 @@ void EMSESPShell::stopped() {
|
|||||||
logger().log(LogLevel::DEBUG, LogFacility::CONSOLE, F("User session closed on console %s"), console_name().c_str());
|
logger().log(LogLevel::DEBUG, LogFacility::CONSOLE, F("User session closed on console %s"), console_name().c_str());
|
||||||
|
|
||||||
// remove all custom contexts
|
// remove all custom contexts
|
||||||
commands->remove_all_commands();
|
// commands->remove_all_commands();
|
||||||
|
// console_commands_loaded_ = false; // make sure they get reloaded next time a console is opened
|
||||||
console_commands_loaded_ = false; // make sure they get reloaded next time a console is opened
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// show welcome banner
|
// show welcome banner
|
||||||
@@ -99,9 +98,8 @@ void EMSESPShell::add_console_commands() {
|
|||||||
if (console_commands_loaded_) {
|
if (console_commands_loaded_) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
console_commands_loaded_ = true;
|
||||||
// just in case, remove everything
|
// just in case, remove everything
|
||||||
// commands->remove_context_commands(ShellContext::MAIN);
|
|
||||||
commands->remove_all_commands();
|
commands->remove_all_commands();
|
||||||
|
|
||||||
commands->add_command(ShellContext::MAIN, CommandFlags::USER, flash_string_vector{F_(show)}, [](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
|
commands->add_command(ShellContext::MAIN, CommandFlags::USER, flash_string_vector{F_(show)}, [](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
|
||||||
@@ -212,6 +210,7 @@ void EMSESPShell::add_console_commands() {
|
|||||||
shell.printfln(F_(bus_id_fmt), settings.ems_bus_id);
|
shell.printfln(F_(bus_id_fmt), settings.ems_bus_id);
|
||||||
char buffer[4];
|
char buffer[4];
|
||||||
shell.printfln(F_(master_thermostat_fmt), settings.master_thermostat == 0 ? uuid::read_flash_string(F_(auto)).c_str() : Helpers::hextoa(buffer, settings.master_thermostat));
|
shell.printfln(F_(master_thermostat_fmt), settings.master_thermostat == 0 ? uuid::read_flash_string(F_(auto)).c_str() : Helpers::hextoa(buffer, settings.master_thermostat));
|
||||||
|
shell.printfln(F_(board_profile_fmt), settings.board_profile.c_str());
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -222,12 +221,16 @@ void EMSESPShell::add_console_commands() {
|
|||||||
[=](Shell & shell __attribute__((unused)), const std::vector<std::string> & arguments) {
|
[=](Shell & shell __attribute__((unused)), const std::vector<std::string> & arguments) {
|
||||||
uint8_t device_id = Helpers::hextoint(arguments.front().c_str());
|
uint8_t device_id = Helpers::hextoint(arguments.front().c_str());
|
||||||
uint16_t type_id = Helpers::hextoint(arguments[1].c_str());
|
uint16_t type_id = Helpers::hextoint(arguments[1].c_str());
|
||||||
EMSESP::set_read_id(type_id);
|
if (arguments.size() == 4) {
|
||||||
if (arguments.size() == 3) {
|
uint16_t offset = Helpers::hextoint(arguments[2].c_str());
|
||||||
|
uint8_t length = Helpers::hextoint(arguments.back().c_str());
|
||||||
|
EMSESP::send_read_request(type_id, device_id, offset, length);
|
||||||
|
} else if (arguments.size() == 3) {
|
||||||
uint16_t offset = Helpers::hextoint(arguments.back().c_str());
|
uint16_t offset = Helpers::hextoint(arguments.back().c_str());
|
||||||
EMSESP::send_read_request(type_id, device_id, offset);
|
EMSESP::send_read_request(type_id, device_id, offset, EMS_MAX_TELEGRAM_LENGTH);
|
||||||
} else {
|
} else {
|
||||||
EMSESP::send_read_request(type_id, device_id);
|
// send with length to send immediatly and trigger publish read_id
|
||||||
|
EMSESP::send_read_request(type_id, device_id, 0, EMS_MAX_TELEGRAM_LENGTH);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -346,11 +349,6 @@ void EMSESPShell::add_console_commands() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const char * cmd = arguments[1].c_str();
|
const char * cmd = arguments[1].c_str();
|
||||||
if (Command::find_command(device_type, cmd) == nullptr) {
|
|
||||||
shell.print(F("Unknown command. Available commands are: "));
|
|
||||||
Command::show(shell, device_type);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
DynamicJsonDocument doc(EMSESP_JSON_SIZE_XLARGE_DYN);
|
DynamicJsonDocument doc(EMSESP_JSON_SIZE_XLARGE_DYN);
|
||||||
JsonObject json = doc.to<JsonObject>();
|
JsonObject json = doc.to<JsonObject>();
|
||||||
@@ -376,6 +374,11 @@ void EMSESPShell::add_console_commands() {
|
|||||||
doc.shrinkToFit();
|
doc.shrinkToFit();
|
||||||
serializeJsonPretty(doc, shell);
|
serializeJsonPretty(doc, shell);
|
||||||
shell.println();
|
shell.println();
|
||||||
|
return;
|
||||||
|
} else if (!ok) {
|
||||||
|
shell.println(F("Unknown command, value, or id."));
|
||||||
|
shell.print(F("Available commands are: "));
|
||||||
|
Command::show(shell, device_type);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[&](Shell & shell __attribute__((unused)), const std::vector<std::string> & arguments) -> std::vector<std::string> {
|
[&](Shell & shell __attribute__((unused)), const std::vector<std::string> & arguments) -> std::vector<std::string> {
|
||||||
@@ -405,45 +408,42 @@ void EMSESPShell::add_console_commands() {
|
|||||||
return {};
|
return {};
|
||||||
});
|
});
|
||||||
|
|
||||||
// System context menu
|
|
||||||
commands->add_command(ShellContext::MAIN, CommandFlags::USER, flash_string_vector{F_(system)}, [](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
|
|
||||||
System::console_commands(shell, ShellContext::SYSTEM);
|
|
||||||
});
|
|
||||||
|
|
||||||
Console::load_standard_commands(ShellContext::MAIN);
|
Console::load_standard_commands(ShellContext::MAIN);
|
||||||
|
Console::load_system_commands(ShellContext::MAIN);
|
||||||
console_commands_loaded_ = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string EMSESPShell::hostname_text() {
|
std::string EMSESPShell::hostname_text() {
|
||||||
return console_hostname_;
|
return console_hostname_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
// remove commands from the current context to save memory before exiting
|
// remove commands from the current context to save memory before exiting
|
||||||
bool EMSESPShell::exit_context() {
|
bool EMSESPShell::exit_context() {
|
||||||
unsigned int current_context = context();
|
unsigned int current_context = context();
|
||||||
commands->remove_context_commands(current_context);
|
|
||||||
|
|
||||||
if (current_context == ShellContext::MAIN) {
|
if (current_context == ShellContext::MAIN) {
|
||||||
Shell::stop();
|
Shell::stop();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return Shell::exit_context();
|
// commands->remove_context_commands(current_context);
|
||||||
|
// return Shell::exit_context();
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// enter a custom context (sub-menu)
|
// enter a custom context (sub-menu)
|
||||||
void Console::enter_custom_context(Shell & shell, unsigned int context) {
|
void Console::enter_custom_context(Shell & shell, unsigned int context) {
|
||||||
load_standard_commands(context);
|
// load_standard_commands(context);
|
||||||
|
|
||||||
// don't go into the new context if it's already the root (to prevent double loading)
|
// don't go into the new context if it's already the root (to prevent double loading)
|
||||||
if (context != ShellContext::MAIN) {
|
if (context != ShellContext::MAIN) {
|
||||||
shell.enter_context(context);
|
shell.enter_context(context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
// each custom context has the common commands like log, help, exit, su etc
|
// each custom context has the common commands like log, help, exit, su etc
|
||||||
void Console::load_standard_commands(unsigned int context) {
|
void Console::load_standard_commands(unsigned int context) {
|
||||||
#if defined(EMSESP_TEST)
|
#if defined(EMSESP_DEBUG)
|
||||||
EMSESPShell::commands->add_command(context, CommandFlags::USER, flash_string_vector{F("test")}, flash_string_vector{F_(name_optional)}, [](Shell & shell, const std::vector<std::string> & arguments) {
|
EMSESPShell::commands->add_command(context, CommandFlags::USER, flash_string_vector{F("test")}, flash_string_vector{F_(name_optional)}, [](Shell & shell, const std::vector<std::string> & arguments) {
|
||||||
if (arguments.size() == 0) {
|
if (arguments.size() == 0) {
|
||||||
Test::run_test(shell, "default");
|
Test::run_test(shell, "default");
|
||||||
@@ -500,9 +500,8 @@ void Console::load_standard_commands(unsigned int context) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
EMSESPShell::commands->add_command(context, CommandFlags::USER, flash_string_vector{F_(exit)}, [=](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
|
EMSESPShell::commands->add_command(context, CommandFlags::USER, flash_string_vector{F_(exit)}, [=](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
|
||||||
// delete MAIN console stuff first to save memory
|
shell.stop();
|
||||||
EMSESPShell::commands->remove_context_commands(context);
|
// shell.exit_context();
|
||||||
shell.exit_context();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
EMSESPShell::commands->add_command(context, CommandFlags::USER, flash_string_vector{F_(su)}, [=](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
|
EMSESPShell::commands->add_command(context, CommandFlags::USER, flash_string_vector{F_(su)}, [=](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
|
||||||
@@ -534,6 +533,139 @@ void Console::load_standard_commands(unsigned int context) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// console commands to add
|
||||||
|
void Console::load_system_commands(unsigned int context) {
|
||||||
|
EMSESPShell::commands->add_command(context,
|
||||||
|
CommandFlags::ADMIN,
|
||||||
|
flash_string_vector{F_(restart)},
|
||||||
|
[](Shell & shell __attribute__((unused)), const std::vector<std::string> & arguments __attribute__((unused))) { EMSESP::system_.restart(); });
|
||||||
|
|
||||||
|
EMSESPShell::commands->add_command(context,
|
||||||
|
CommandFlags::ADMIN,
|
||||||
|
flash_string_vector{F_(wifi), F_(reconnect)},
|
||||||
|
[](Shell & shell __attribute__((unused)), const std::vector<std::string> & arguments __attribute__((unused))) { EMSESP::system_.wifi_reconnect(); });
|
||||||
|
|
||||||
|
EMSESPShell::commands->add_command(ShellContext::MAIN, CommandFlags::ADMIN, flash_string_vector{F_(format)}, [](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
|
||||||
|
shell.enter_password(F_(password_prompt), [=](Shell & shell, bool completed, const std::string & password) {
|
||||||
|
if (completed) {
|
||||||
|
EMSESP::esp8266React.getSecuritySettingsService()->read([&](SecuritySettings & securitySettings) {
|
||||||
|
if (securitySettings.jwtSecret.equals(password.c_str())) {
|
||||||
|
EMSESP::system_.format(shell);
|
||||||
|
} else {
|
||||||
|
shell.println(F("incorrect password"));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
EMSESPShell::commands->add_command(context, CommandFlags::ADMIN, flash_string_vector{F_(passwd)}, [](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
|
||||||
|
shell.enter_password(F_(new_password_prompt1), [](Shell & shell, bool completed, const std::string & password1) {
|
||||||
|
if (completed) {
|
||||||
|
shell.enter_password(F_(new_password_prompt2), [password1](Shell & shell, bool completed, const std::string & password2) {
|
||||||
|
if (completed) {
|
||||||
|
if (password1 == password2) {
|
||||||
|
EMSESP::esp8266React.getSecuritySettingsService()->update(
|
||||||
|
[&](SecuritySettings & securitySettings) {
|
||||||
|
securitySettings.jwtSecret = password2.c_str();
|
||||||
|
return StateUpdateResult::CHANGED;
|
||||||
|
},
|
||||||
|
"local");
|
||||||
|
shell.println(F("su password updated"));
|
||||||
|
} else {
|
||||||
|
shell.println(F("Passwords do not match"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
EMSESPShell::commands->add_command(context, CommandFlags::USER, flash_string_vector{F_(show), F_(system)}, [=](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
|
||||||
|
EMSESP::system_.show_system(shell);
|
||||||
|
shell.println();
|
||||||
|
});
|
||||||
|
|
||||||
|
EMSESPShell::commands->add_command(context,
|
||||||
|
CommandFlags::ADMIN,
|
||||||
|
flash_string_vector{F_(set), F_(hostname)},
|
||||||
|
flash_string_vector{F_(name_mandatory)},
|
||||||
|
[](Shell & shell, const std::vector<std::string> & arguments) {
|
||||||
|
shell.println("The network connection will be reset...");
|
||||||
|
Shell::loop_all();
|
||||||
|
delay(1000); // wait a second
|
||||||
|
EMSESP::esp8266React.getNetworkSettingsService()->update(
|
||||||
|
[&](NetworkSettings & networkSettings) {
|
||||||
|
networkSettings.hostname = arguments.front().c_str();
|
||||||
|
return StateUpdateResult::CHANGED;
|
||||||
|
},
|
||||||
|
"local");
|
||||||
|
});
|
||||||
|
|
||||||
|
EMSESPShell::commands->add_command(context,
|
||||||
|
CommandFlags::ADMIN,
|
||||||
|
flash_string_vector{F_(set), F_(wifi), F_(ssid)},
|
||||||
|
flash_string_vector{F_(name_mandatory)},
|
||||||
|
[](Shell & shell, const std::vector<std::string> & arguments) {
|
||||||
|
EMSESP::esp8266React.getNetworkSettingsService()->updateWithoutPropagation([&](NetworkSettings & networkSettings) {
|
||||||
|
networkSettings.ssid = arguments.front().c_str();
|
||||||
|
return StateUpdateResult::CHANGED;
|
||||||
|
});
|
||||||
|
shell.println("Use `wifi reconnect` to save and apply the new settings");
|
||||||
|
});
|
||||||
|
|
||||||
|
EMSESPShell::commands->add_command(context, CommandFlags::ADMIN, flash_string_vector{F_(set), F_(wifi), F_(password)}, [](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
|
||||||
|
shell.enter_password(F_(new_password_prompt1), [](Shell & shell, bool completed, const std::string & password1) {
|
||||||
|
if (completed) {
|
||||||
|
shell.enter_password(F_(new_password_prompt2), [password1](Shell & shell, bool completed, const std::string & password2) {
|
||||||
|
if (completed) {
|
||||||
|
if (password1 == password2) {
|
||||||
|
EMSESP::esp8266React.getNetworkSettingsService()->updateWithoutPropagation([&](NetworkSettings & networkSettings) {
|
||||||
|
networkSettings.password = password2.c_str();
|
||||||
|
return StateUpdateResult::CHANGED;
|
||||||
|
});
|
||||||
|
shell.println("Use `wifi reconnect` to save and apply the new settings");
|
||||||
|
} else {
|
||||||
|
shell.println(F("Passwords do not match"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
EMSESPShell::commands->add_command(context,
|
||||||
|
CommandFlags::ADMIN,
|
||||||
|
flash_string_vector{F_(set), F_(board_profile)},
|
||||||
|
flash_string_vector{F_(name_mandatory)},
|
||||||
|
[](Shell & shell, const std::vector<std::string> & arguments) {
|
||||||
|
std::vector<uint8_t> data; // led, dallas, rx, tx, button
|
||||||
|
std::string board_profile = Helpers::toUpper(arguments.front());
|
||||||
|
if (!EMSESP::system_.load_board_profile(data, board_profile)) {
|
||||||
|
shell.println(F("Invalid board profile (S32, E32, MH-ET, NODEMCU, OLIMEX, TLK110, LAN8720, CUSTOM)"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
EMSESP::webSettingsService.update(
|
||||||
|
[&](WebSettings & settings) {
|
||||||
|
settings.board_profile = board_profile.c_str();
|
||||||
|
settings.led_gpio = data[0];
|
||||||
|
settings.dallas_gpio = data[1];
|
||||||
|
settings.rx_gpio = data[2];
|
||||||
|
settings.tx_gpio = data[3];
|
||||||
|
settings.pbutton_gpio = data[4];
|
||||||
|
return StateUpdateResult::CHANGED;
|
||||||
|
},
|
||||||
|
"local");
|
||||||
|
shell.printfln("Loaded board profile %s (%d,%d,%d,%d,%d)", board_profile.c_str(), data[0], data[1], data[2], data[3], data[4]);
|
||||||
|
EMSESP::system_.network_init(true);
|
||||||
|
});
|
||||||
|
EMSESPShell::commands->add_command(context, CommandFlags::ADMIN, flash_string_vector{F_(show), F_(users)}, [](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
|
||||||
|
EMSESP::system_.show_users(shell);
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
// prompt, change per context
|
// prompt, change per context
|
||||||
std::string EMSESPShell::context_text() {
|
std::string EMSESPShell::context_text() {
|
||||||
switch (static_cast<ShellContext>(context())) {
|
switch (static_cast<ShellContext>(context())) {
|
||||||
@@ -547,6 +679,7 @@ std::string EMSESPShell::context_text() {
|
|||||||
return std::string{};
|
return std::string{};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
// when in su (admin) show # as the prompt suffix
|
// when in su (admin) show # as the prompt suffix
|
||||||
std::string EMSESPShell::prompt_suffix() {
|
std::string EMSESPShell::prompt_suffix() {
|
||||||
@@ -566,7 +699,7 @@ EMSESPStreamConsole::EMSESPStreamConsole(Stream & stream, bool local)
|
|||||||
, uuid::console::StreamConsole(stream)
|
, uuid::console::StreamConsole(stream)
|
||||||
, EMSESPShell()
|
, EMSESPShell()
|
||||||
, name_(uuid::read_flash_string(F("Serial")))
|
, name_(uuid::read_flash_string(F("Serial")))
|
||||||
, pty_(std::numeric_limits<size_t>::max())
|
, pty_(SIZE_MAX)
|
||||||
, addr_()
|
, addr_()
|
||||||
, port_(0) {
|
, port_(0) {
|
||||||
}
|
}
|
||||||
@@ -602,8 +735,6 @@ EMSESPStreamConsole::~EMSESPStreamConsole() {
|
|||||||
#endif
|
#endif
|
||||||
ptys_[pty_] = false;
|
ptys_[pty_] = false;
|
||||||
ptys_.shrink_to_fit();
|
ptys_.shrink_to_fit();
|
||||||
|
|
||||||
EMSESPShell::commands->remove_all_commands();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -44,6 +44,8 @@ using uuid::log::Level;
|
|||||||
#define LOG_WARNING(...) logger_.warning(__VA_ARGS__)
|
#define LOG_WARNING(...) logger_.warning(__VA_ARGS__)
|
||||||
#define LOG_ERROR(...) logger_.err(__VA_ARGS__)
|
#define LOG_ERROR(...) logger_.err(__VA_ARGS__)
|
||||||
|
|
||||||
|
#define MQTT_TOPIC(list_name) (__pstr__##list_name[0])
|
||||||
|
|
||||||
// clang-format off
|
// clang-format off
|
||||||
// strings stored 32 bit aligned on ESP8266/ESP32
|
// strings stored 32 bit aligned on ESP8266/ESP32
|
||||||
#define MAKE_PSTR(string_name, string_literal) static const char __pstr__##string_name[] __attribute__((__aligned__(sizeof(uint32_t)))) PROGMEM = string_literal;
|
#define MAKE_PSTR(string_name, string_literal) static const char __pstr__##string_name[] __attribute__((__aligned__(sizeof(uint32_t)))) PROGMEM = string_literal;
|
||||||
@@ -99,10 +101,10 @@ class EMSESPShell : virtual public uuid::console::Shell {
|
|||||||
void stopped() override;
|
void stopped() override;
|
||||||
void display_banner() override;
|
void display_banner() override;
|
||||||
std::string hostname_text() override;
|
std::string hostname_text() override;
|
||||||
std::string context_text() override;
|
// std::string context_text() override;
|
||||||
std::string prompt_suffix() override;
|
std::string prompt_suffix() override;
|
||||||
void end_of_transmission() override;
|
void end_of_transmission() override;
|
||||||
bool exit_context() override;
|
// bool exit_context() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void add_console_commands();
|
void add_console_commands();
|
||||||
@@ -134,8 +136,9 @@ class Console {
|
|||||||
|
|
||||||
uuid::log::Level log_level();
|
uuid::log::Level log_level();
|
||||||
|
|
||||||
static void enter_custom_context(Shell & shell, unsigned int context);
|
// static void enter_custom_context(Shell & shell, unsigned int context);
|
||||||
static void load_standard_commands(unsigned int context);
|
static void load_standard_commands(unsigned int context);
|
||||||
|
static void load_system_commands(unsigned int context);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace emsesp
|
} // namespace emsesp
|
||||||
|
|||||||
@@ -308,10 +308,18 @@ bool DallasSensor::command_info(const char * value, const int8_t id, JsonObject
|
|||||||
for (const auto & sensor : sensors_) {
|
for (const auto & sensor : sensors_) {
|
||||||
char sensorID[10]; // sensor{1-n}
|
char sensorID[10]; // sensor{1-n}
|
||||||
snprintf_P(sensorID, 10, PSTR("sensor%d"), i++);
|
snprintf_P(sensorID, 10, PSTR("sensor%d"), i++);
|
||||||
JsonObject dataSensor = json.createNestedObject(sensorID);
|
if (id == 0) {
|
||||||
dataSensor["id"] = sensor.to_string();
|
if (Mqtt::dallas_format() == Mqtt::Dallas_Format::SENSORID && Helpers::hasValue(sensor.temperature_c)) {
|
||||||
if (Helpers::hasValue(sensor.temperature_c)) {
|
json[sensor.to_string()] = (float)(sensor.temperature_c) / 10;
|
||||||
dataSensor["temp"] = (float)(sensor.temperature_c) / 10;
|
} else if (Helpers::hasValue(sensor.temperature_c)) {
|
||||||
|
json[sensorID] = (float)(sensor.temperature_c) / 10;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
JsonObject dataSensor = json.createNestedObject(sensorID);
|
||||||
|
dataSensor["id"] = sensor.to_string();
|
||||||
|
if (Helpers::hasValue(sensor.temperature_c)) {
|
||||||
|
dataSensor["temp"] = (float)(sensor.temperature_c) / 10;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -394,9 +402,9 @@ void DallasSensor::publish_values(const bool force) {
|
|||||||
// use '_' as HA doesn't like '-' in the topic name
|
// use '_' as HA doesn't like '-' in the topic name
|
||||||
std::string topicname = sensor.to_string();
|
std::string topicname = sensor.to_string();
|
||||||
std::replace(topicname.begin(), topicname.end(), '-', '_');
|
std::replace(topicname.begin(), topicname.end(), '-', '_');
|
||||||
snprintf_P(topic, sizeof(topic), PSTR("homeassistant/sensor/%s/dallas_sensor%s/config"), Mqtt::base().c_str(), topicname.c_str());
|
snprintf_P(topic, sizeof(topic), PSTR("sensor/%s/dallas_sensor%s/config"), Mqtt::base().c_str(), topicname.c_str());
|
||||||
} else {
|
} else {
|
||||||
snprintf_P(topic, sizeof(topic), PSTR("homeassistant/sensor/%s/dallas_sensor%d/config"), Mqtt::base().c_str(), sensor_no);
|
snprintf_P(topic, sizeof(topic), PSTR("sensor/%s/dallas_sensor%d/config"), Mqtt::base().c_str(), sensor_no);
|
||||||
}
|
}
|
||||||
Mqtt::publish_ha(topic, config.as<JsonObject>());
|
Mqtt::publish_ha(topic, config.as<JsonObject>());
|
||||||
|
|
||||||
|
|||||||
@@ -68,6 +68,10 @@ class DallasSensor {
|
|||||||
return sensorfails_;
|
return sensorfails_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool dallas_enabled() {
|
||||||
|
return (dallas_gpio_ != 0);
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static constexpr uint8_t MAX_SENSORS = 20;
|
static constexpr uint8_t MAX_SENSORS = 20;
|
||||||
|
|
||||||
|
|||||||
155
src/default_settings.h
Normal file
155
src/default_settings.h
Normal file
@@ -0,0 +1,155 @@
|
|||||||
|
/*
|
||||||
|
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||||
|
* Copyright 2020 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_DEFAULT_SETTINGS_H
|
||||||
|
#define EMSESP_DEFAULT_SETTINGS_H
|
||||||
|
|
||||||
|
// GENERAL SETTINGS
|
||||||
|
|
||||||
|
#ifndef EMSESP_DEFAULT_TX_MODE
|
||||||
|
#define EMSESP_DEFAULT_TX_MODE 1 // EMS1.0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef EMSESP_DEFAULT_TX_DELAY
|
||||||
|
#define EMSESP_DEFAULT_TX_DELAY 0 // no delay
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef EMSESP_DEFAULT_EMS_BUS_ID
|
||||||
|
#define EMSESP_DEFAULT_EMS_BUS_ID 0x0B // service key
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef EMSESP_DEFAULT_SYSLOG_ENABLED
|
||||||
|
#define EMSESP_DEFAULT_SYSLOG_ENABLED false
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef EMSESP_DEFAULT_SYSLOG_LEVEL
|
||||||
|
#define EMSESP_DEFAULT_SYSLOG_LEVEL 3 // ERR
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef EMSESP_DEFAULT_SYSLOG_MARK_INTERVAL
|
||||||
|
#define EMSESP_DEFAULT_SYSLOG_MARK_INTERVAL 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef EMSESP_DEFAULT_SYSLOG_HOST
|
||||||
|
#define EMSESP_DEFAULT_SYSLOG_HOST ""
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef EMSESP_DEFAULT_SYSLOG_PORT
|
||||||
|
#define EMSESP_DEFAULT_SYSLOG_PORT 514
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef EMSESP_DEFAULT_TRACELOG_RAW
|
||||||
|
#define EMSESP_DEFAULT_TRACELOG_RAW false
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef EMSESP_DEFAULT_MASTER_THERMOSTAT
|
||||||
|
#define EMSESP_DEFAULT_MASTER_THERMOSTAT 0 // not set
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef EMSESP_DEFAULT_SHOWER_TIMER
|
||||||
|
#define EMSESP_DEFAULT_SHOWER_TIMER false
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef EMSESP_DEFAULT_SHOWER_ALERT
|
||||||
|
#define EMSESP_DEFAULT_SHOWER_ALERT false
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef EMSESP_DEFAULT_HIDE_LED
|
||||||
|
#define EMSESP_DEFAULT_HIDE_LED false
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef EMSESP_DEFAULT_DALLAS_PARASITE
|
||||||
|
#define EMSESP_DEFAULT_DALLAS_PARASITE false
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef EMSESP_DEFAULT_API_ENABLED
|
||||||
|
#define EMSESP_DEFAULT_API_ENABLED false // turn off, because its insecure
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef EMSESP_DEFAULT_BOOL_FORMAT
|
||||||
|
#define EMSESP_DEFAULT_BOOL_FORMAT 1 // on/off
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef EMSESP_DEFAULT_ANALOG_ENABLED
|
||||||
|
#define EMSESP_DEFAULT_ANALOG_ENABLED false
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef EMSESP_DEFAULT_BOARD_PROFILE
|
||||||
|
#define EMSESP_DEFAULT_BOARD_PROFILE "S32" // Gateway S32
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Default GPIO PIN definitions - based on Wemos/Nodemcu
|
||||||
|
|
||||||
|
#ifndef EMSESP_DEFAULT_RX_GPIO
|
||||||
|
#define EMSESP_DEFAULT_RX_GPIO 23 // D7
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef EMSESP_DEFAULT_TX_GPIO
|
||||||
|
#define EMSESP_DEFAULT_TX_GPIO 5 // D8
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef EMSESP_DEFAULT_DALLAS_GPIO
|
||||||
|
#define EMSESP_DEFAULT_DALLAS_GPIO 18
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef EMSESP_DEFAULT_LED_GPIO
|
||||||
|
#define EMSESP_DEFAULT_LED_GPIO 2
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef EMSESP_DEFAULT_PBUTTON_GPIO
|
||||||
|
#define EMSESP_DEFAULT_PBUTTON_GPIO 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// MQTT
|
||||||
|
|
||||||
|
#ifndef EMSESP_DEFAULT_BOOL_FORMAT
|
||||||
|
#define EMSESP_DEFAULT_BOOL_FORMAT 1 // on/off
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef EMSESP_DEFAULT_DALLAS_FORMAT
|
||||||
|
#define EMSESP_DEFAULT_DALLAS_FORMAT 1 // sensorid
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef EMSESP_DEFAULT_HA_CLIMATE_FORMAT
|
||||||
|
#define EMSESP_DEFAULT_HA_CLIMATE_FORMAT 1 // current temp
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef EMSESP_DEFAULT_MQTT_QOS
|
||||||
|
#define EMSESP_DEFAULT_MQTT_QOS 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef EMSESP_DEFAULT_MQTT_RETAIN
|
||||||
|
#define EMSESP_DEFAULT_MQTT_RETAIN false
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef EMSESP_DEFAULT_HA_ENABLED
|
||||||
|
#define EMSESP_DEFAULT_HA_ENABLED false
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef EMSESP_DEFAULT_PUBLISH_TIME
|
||||||
|
#define EMSESP_DEFAULT_PUBLISH_TIME 10
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef EMSESP_DEFAULT_NESTED_FORMAT
|
||||||
|
#define EMSESP_DEFAULT_NESTED_FORMAT 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef EMSESP_DEFAULT_SUBSCRIBE_FORMAT
|
||||||
|
#define EMSESP_DEFAULT_SUBSCRIBE_FORMAT 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -25,9 +25,9 @@
|
|||||||
|
|
||||||
// Boilers - 0x08
|
// Boilers - 0x08
|
||||||
{ 64, DeviceType::BOILER, F("BK13/BK15/Smartline/GB1x2"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
{ 64, DeviceType::BOILER, F("BK13/BK15/Smartline/GB1x2"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||||
{ 72, DeviceType::BOILER, F("GB125/MC10"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
{ 72, DeviceType::BOILER, F("GB125/MC10"), DeviceFlags::EMS_DEVICE_FLAG_EMS},
|
||||||
{ 84, DeviceType::BOILER, F("Logamax Plus GB022"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
{ 84, DeviceType::BOILER, F("Logamax Plus GB022"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||||
{ 95, DeviceType::BOILER, F("Condens 2500/Logamax/Logomatic/Cerapur Top/Greenstar/Generic HT3"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
{ 95, DeviceType::BOILER, F("Condens 2500/Logamax/Logomatic/Cerapur Top/Greenstar/Generic HT3"), DeviceFlags::EMS_DEVICE_FLAG_HT3},
|
||||||
{115, DeviceType::BOILER, F("Topline/GB162"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
{115, DeviceType::BOILER, F("Topline/GB162"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||||
{122, DeviceType::BOILER, F("Proline"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
{122, DeviceType::BOILER, F("Proline"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||||
{123, DeviceType::BOILER, F("GBx72/Trendline/Cerapur/Greenstar Si/27i"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
{123, DeviceType::BOILER, F("GBx72/Trendline/Cerapur/Greenstar Si/27i"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||||
@@ -35,7 +35,7 @@
|
|||||||
{133, DeviceType::BOILER, F("GB125/Logamatic MC110"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
{133, DeviceType::BOILER, F("GB125/Logamatic MC110"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||||
{167, DeviceType::BOILER, F("Cerapur Aero"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
{167, DeviceType::BOILER, F("Cerapur Aero"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||||
{170, DeviceType::BOILER, F("Logano GB212"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
{170, DeviceType::BOILER, F("Logano GB212"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||||
{172, DeviceType::BOILER, F("Enviline/Compress 6000AW/Hybrid 7000iAW/SupraEco"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
{172, DeviceType::BOILER, F("Enviline/Compress 6000AW/Hybrid 7000iAW/SupraEco"), DeviceFlags::EMS_DEVICE_FLAG_HEATPUMP},
|
||||||
{195, DeviceType::BOILER, F("Condens 5000i/Greenstar 8000"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
{195, DeviceType::BOILER, F("Condens 5000i/Greenstar 8000"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||||
{203, DeviceType::BOILER, F("Logamax U122/Cerapur"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
{203, DeviceType::BOILER, F("Logamax U122/Cerapur"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||||
{208, DeviceType::BOILER, F("Logamax Plus/GB192/Condens GC9000"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
{208, DeviceType::BOILER, F("Logamax Plus/GB192/Condens GC9000"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||||
@@ -68,23 +68,24 @@
|
|||||||
{203, DeviceType::THERMOSTAT, F("EasyControl CT200"), DeviceFlags::EMS_DEVICE_FLAG_EASY | DeviceFlags::EMS_DEVICE_FLAG_NO_WRITE}, // 0x18, cannot write
|
{203, DeviceType::THERMOSTAT, F("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 / 0x38
|
// Thermostat - Buderus/Nefit/Bosch specific - 0x17 / 0x10 / 0x18 / 0x19 / 0x38
|
||||||
{ 67, DeviceType::THERMOSTAT, F("RC30"), DeviceFlags::EMS_DEVICE_FLAG_RC30_1},// 0x10 - based on RC35
|
{ 67, DeviceType::THERMOSTAT, F("RC30"), DeviceFlags::EMS_DEVICE_FLAG_RC30_N},// 0x10 - based on RC35
|
||||||
{ 77, DeviceType::THERMOSTAT, F("RC20/Moduline 300"), DeviceFlags::EMS_DEVICE_FLAG_RC20},// 0x17
|
{ 77, DeviceType::THERMOSTAT, F("RC20/Moduline 300"), DeviceFlags::EMS_DEVICE_FLAG_RC20},// 0x17
|
||||||
{ 78, DeviceType::THERMOSTAT, F("Moduline 400"), DeviceFlags::EMS_DEVICE_FLAG_RC30}, // 0x10
|
{ 78, DeviceType::THERMOSTAT, F("Moduline 400"), DeviceFlags::EMS_DEVICE_FLAG_RC30}, // 0x10
|
||||||
{ 79, DeviceType::THERMOSTAT, F("RC10/Moduline 100"), DeviceFlags::EMS_DEVICE_FLAG_RC10},// 0x17
|
{ 79, DeviceType::THERMOSTAT, F("RC10/Moduline 100"), DeviceFlags::EMS_DEVICE_FLAG_RC10},// 0x17
|
||||||
{ 80, DeviceType::THERMOSTAT, F("Moduline 200"), DeviceFlags::EMS_DEVICE_FLAG_RC10}, // 0x17
|
{ 80, DeviceType::THERMOSTAT, F("Moduline 200"), DeviceFlags::EMS_DEVICE_FLAG_RC10}, // 0x17
|
||||||
{ 86, DeviceType::THERMOSTAT, F("RC35"), DeviceFlags::EMS_DEVICE_FLAG_RC35}, // 0x10
|
{ 86, DeviceType::THERMOSTAT, F("RC35"), DeviceFlags::EMS_DEVICE_FLAG_RC35}, // 0x10
|
||||||
{ 90, DeviceType::THERMOSTAT, F("RC10/Moduline 100"), DeviceFlags::EMS_DEVICE_FLAG_RC20_2}, // 0x17
|
{ 90, DeviceType::THERMOSTAT, F("RC10/Moduline 100"), DeviceFlags::EMS_DEVICE_FLAG_RC20_N}, // 0x17
|
||||||
{ 93, DeviceType::THERMOSTAT, F("RC20RF"), DeviceFlags::EMS_DEVICE_FLAG_RC20}, // 0x19
|
{ 93, DeviceType::THERMOSTAT, F("RC20RF"), DeviceFlags::EMS_DEVICE_FLAG_RC20}, // 0x19
|
||||||
{ 94, DeviceType::THERMOSTAT, F("RFM20 Remote"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x18
|
{ 94, DeviceType::THERMOSTAT, F("RFM20 Remote"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x18
|
||||||
{157, DeviceType::THERMOSTAT, F("RC200/CW100"), DeviceFlags::EMS_DEVICE_FLAG_RC100}, // 0x18
|
{157, DeviceType::THERMOSTAT, F("RC200/CW100"), DeviceFlags::EMS_DEVICE_FLAG_RC100}, // 0x18
|
||||||
{158, DeviceType::THERMOSTAT, F("RC300/RC310/Moduline 3000/1010H/CW400/Sense II"), DeviceFlags::EMS_DEVICE_FLAG_RC300}, // 0x10
|
{158, DeviceType::THERMOSTAT, F("RC300/RC310/Moduline 3000/1010H/CW400/Sense II"), DeviceFlags::EMS_DEVICE_FLAG_RC300}, // 0x10
|
||||||
{165, DeviceType::THERMOSTAT, F("RC100/Moduline 1000/1010"), DeviceFlags::EMS_DEVICE_FLAG_RC100}, // 0x18, 0x38
|
{165, DeviceType::THERMOSTAT, F("RC100/Moduline 1000/1010"), DeviceFlags::EMS_DEVICE_FLAG_RC100}, // 0x18, 0x38
|
||||||
|
{216, DeviceType::THERMOSTAT, F("CRF200S"), DeviceFlags::EMS_DEVICE_FLAG_CRF | DeviceFlags::EMS_DEVICE_FLAG_NO_WRITE}, // 0x18
|
||||||
|
|
||||||
// Thermostat - Sieger - 0x10 / 0x17
|
// Thermostat - Sieger - 0x10 / 0x17
|
||||||
{ 66, DeviceType::THERMOSTAT, F("ES72/RC20"), DeviceFlags::EMS_DEVICE_FLAG_RC20_2}, // 0x17 or remote
|
{ 66, DeviceType::THERMOSTAT, F("ES72/RC20"), DeviceFlags::EMS_DEVICE_FLAG_RC20_N}, // 0x17 or remote
|
||||||
{ 76, DeviceType::THERMOSTAT, F("ES73"), DeviceFlags::EMS_DEVICE_FLAG_RC35}, // 0x10
|
{ 76, DeviceType::THERMOSTAT, F("ES73"), DeviceFlags::EMS_DEVICE_FLAG_RC35}, // 0x10
|
||||||
{113, DeviceType::THERMOSTAT, F("ES72/RC20"), DeviceFlags::EMS_DEVICE_FLAG_RC20_2}, // 0x17
|
{113, DeviceType::THERMOSTAT, F("ES72/RC20"), DeviceFlags::EMS_DEVICE_FLAG_RC20_N}, // 0x17
|
||||||
|
|
||||||
// Thermostat - Junkers - 0x10
|
// Thermostat - Junkers - 0x10
|
||||||
{105, DeviceType::THERMOSTAT, F("FW100"), DeviceFlags::EMS_DEVICE_FLAG_JUNKERS},
|
{105, DeviceType::THERMOSTAT, F("FW100"), DeviceFlags::EMS_DEVICE_FLAG_JUNKERS},
|
||||||
|
|||||||
@@ -33,172 +33,175 @@ Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const
|
|||||||
uint8_t hs = device_id - EMSdevice::EMS_DEVICE_ID_BOILER_1; // heating source id, count from 0
|
uint8_t hs = device_id - EMSdevice::EMS_DEVICE_ID_BOILER_1; // heating source id, count from 0
|
||||||
// Runtime of each heatingsource in 0x06DC, ff
|
// Runtime of each heatingsource in 0x06DC, ff
|
||||||
register_telegram_type(0x6DC + hs, F("CascadeMessage"), false, MAKE_PF_CB(process_CascadeMessage));
|
register_telegram_type(0x6DC + hs, F("CascadeMessage"), false, MAKE_PF_CB(process_CascadeMessage));
|
||||||
register_device_value(TAG_HS1 + hs, &burnWorkMin_, DeviceValueType::TIME, nullptr, F("burnWorkMin"), F("total burner operating time"), DeviceValueUOM::MINUTES);
|
register_device_value(TAG_HS1 + hs, &burnWorkMin_, DeviceValueType::TIME, nullptr, FL_(burnWorkMin), DeviceValueUOM::MINUTES);
|
||||||
// selBurnpower in D2 and E4
|
// selBurnpower in D2 and E4
|
||||||
// register_telegram_type(0xD2, F("CascadePowerMessage"), false, MAKE_PF_CB(process_CascadePowerMessage));
|
// register_telegram_type(0xD2, F("CascadePowerMessage"), false, MAKE_PF_CB(process_CascadePowerMessage));
|
||||||
// individual Flowtemps and powervalues for each heatingsource in E4
|
// individual Flowtemps and powervalues for each heatingsource in E4
|
||||||
register_telegram_type(0xE4, F("UBAMonitorFastPlus"), false, MAKE_PF_CB(process_UBAMonitorFastPlus));
|
register_telegram_type(0xE4, F("UBAMonitorFastPlus"), false, MAKE_PF_CB(process_UBAMonitorFastPlus));
|
||||||
register_device_value(TAG_HS1 + hs, &selFlowTemp_, DeviceValueType::UINT, nullptr, F("selFlowTemp"), F("selected flow temperature"), DeviceValueUOM::DEGREES);
|
register_device_value(TAG_HS1 + hs, &selFlowTemp_, DeviceValueType::UINT, nullptr, FL_(selFlowTemp), DeviceValueUOM::DEGREES);
|
||||||
register_device_value(TAG_HS1 + hs, &selBurnPow_, DeviceValueType::UINT, nullptr, F("selBurnPow"), F("burner selected max power"), DeviceValueUOM::PERCENT);
|
register_device_value(TAG_HS1 + hs, &selBurnPow_, DeviceValueType::UINT, nullptr, FL_(selBurnPow), DeviceValueUOM::PERCENT);
|
||||||
register_device_value(TAG_HS1 + hs, &curFlowTemp_, DeviceValueType::USHORT, FL_(div10), F("curFlowTemp"), F("current flow temperature"), DeviceValueUOM::DEGREES);
|
register_device_value(TAG_HS1 + hs, &curFlowTemp_, DeviceValueType::USHORT, FL_(div10), FL_(curFlowTemp), DeviceValueUOM::DEGREES);
|
||||||
register_device_value(TAG_HS1 + hs, &curBurnPow_, DeviceValueType::UINT, nullptr, F("curBurnPow"), F("burner current power"), DeviceValueUOM::PERCENT);
|
register_device_value(TAG_HS1 + hs, &curBurnPow_, DeviceValueType::UINT, nullptr, FL_(curBurnPow), DeviceValueUOM::PERCENT);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// register values for master boiler/cascade module
|
// register values for master boiler/cascade module
|
||||||
|
|
||||||
reserve_telgram_functions(25); // reserve some space for the telegram registries, to avoid memory fragmentation
|
reserve_telgram_functions(25); // reserve some space for the telegram registries, to avoid memory fragmentation
|
||||||
|
|
||||||
// the telegram handlers...
|
// the telegram handlers...
|
||||||
|
// common for all boilers
|
||||||
register_telegram_type(0x10, F("UBAErrorMessage1"), false, MAKE_PF_CB(process_UBAErrorMessage));
|
register_telegram_type(0x10, F("UBAErrorMessage1"), false, MAKE_PF_CB(process_UBAErrorMessage));
|
||||||
register_telegram_type(0x11, F("UBAErrorMessage2"), false, MAKE_PF_CB(process_UBAErrorMessage));
|
register_telegram_type(0x11, F("UBAErrorMessage2"), false, MAKE_PF_CB(process_UBAErrorMessage));
|
||||||
register_telegram_type(0x14, F("UBATotalUptime"), true, MAKE_PF_CB(process_UBATotalUptime));
|
register_telegram_type(0x14, F("UBATotalUptime"), true, MAKE_PF_CB(process_UBATotalUptime));
|
||||||
register_telegram_type(0x15, F("UBAMaintenanceData"), false, MAKE_PF_CB(process_UBAMaintenanceData));
|
register_telegram_type(0x15, F("UBAMaintenanceData"), false, MAKE_PF_CB(process_UBAMaintenanceData));
|
||||||
register_telegram_type(0x16, F("UBAParameters"), true, MAKE_PF_CB(process_UBAParameters));
|
register_telegram_type(0x1C, F("UBAMaintenanceStatus"), false, MAKE_PF_CB(process_UBAMaintenanceStatus));
|
||||||
|
// EMS1.0 and maybe EMS+?
|
||||||
register_telegram_type(0x18, F("UBAMonitorFast"), false, MAKE_PF_CB(process_UBAMonitorFast));
|
register_telegram_type(0x18, F("UBAMonitorFast"), false, MAKE_PF_CB(process_UBAMonitorFast));
|
||||||
register_telegram_type(0x19, F("UBAMonitorSlow"), true, MAKE_PF_CB(process_UBAMonitorSlow));
|
register_telegram_type(0x19, F("UBAMonitorSlow"), true, MAKE_PF_CB(process_UBAMonitorSlow));
|
||||||
register_telegram_type(0x1A, F("UBASetPoints"), false, MAKE_PF_CB(process_UBASetPoints));
|
register_telegram_type(0x1A, F("UBASetPoints"), false, MAKE_PF_CB(process_UBASetPoints));
|
||||||
register_telegram_type(0x1C, F("UBAMaintenanceStatus"), false, MAKE_PF_CB(process_UBAMaintenanceStatus));
|
register_telegram_type(0x35, F("UBAFlags"), false, MAKE_PF_CB(process_UBAFlags));
|
||||||
register_telegram_type(0x26, F("UBASettingsWW"), true, MAKE_PF_CB(process_UBASettingsWW));
|
// only EMS 1.0
|
||||||
register_telegram_type(0x2A, F("MC10Status"), false, MAKE_PF_CB(process_MC10Status));
|
register_telegram_type(0x16, F("UBAParameters"), true, MAKE_PF_CB(process_UBAParameters));
|
||||||
register_telegram_type(0x33, F("UBAParameterWW"), true, MAKE_PF_CB(process_UBAParameterWW));
|
register_telegram_type(0x33, F("UBAParameterWW"), true, MAKE_PF_CB(process_UBAParameterWW));
|
||||||
register_telegram_type(0x34, F("UBAMonitorWW"), false, MAKE_PF_CB(process_UBAMonitorWW));
|
register_telegram_type(0x34, F("UBAMonitorWW"), false, MAKE_PF_CB(process_UBAMonitorWW));
|
||||||
register_telegram_type(0x35, F("UBAFlags"), false, MAKE_PF_CB(process_UBAFlags));
|
// not ems1.0, but HT3
|
||||||
register_telegram_type(0xD1, F("UBAOutdoorTemp"), false, MAKE_PF_CB(process_UBAOutdoorTemp));
|
if (model() != EMSdevice::EMS_DEVICE_FLAG_EMS) {
|
||||||
register_telegram_type(0xE3, F("UBAMonitorSlowPlus"), false, MAKE_PF_CB(process_UBAMonitorSlowPlus2));
|
register_telegram_type(0x26, F("UBASettingsWW"), true, MAKE_PF_CB(process_UBASettingsWW));
|
||||||
register_telegram_type(0xE4, F("UBAMonitorFastPlus"), false, MAKE_PF_CB(process_UBAMonitorFastPlus));
|
register_telegram_type(0x2A, F("MC110Status"), false, MAKE_PF_CB(process_MC110Status));
|
||||||
register_telegram_type(0xE5, F("UBAMonitorSlowPlus"), false, MAKE_PF_CB(process_UBAMonitorSlowPlus));
|
}
|
||||||
register_telegram_type(0xE6, F("UBAParametersPlus"), true, MAKE_PF_CB(process_UBAParametersPlus));
|
// only EMS+
|
||||||
register_telegram_type(0xE9, F("UBADHWStatus"), false, MAKE_PF_CB(process_UBADHWStatus));
|
if (model() != EMSdevice::EMS_DEVICE_FLAG_EMS && model() != EMSdevice::EMS_DEVICE_FLAG_HT3) {
|
||||||
register_telegram_type(0xEA, F("UBAParameterWWPlus"), true, MAKE_PF_CB(process_UBAParameterWWPlus));
|
register_telegram_type(0xD1, F("UBAOutdoorTemp"), false, MAKE_PF_CB(process_UBAOutdoorTemp));
|
||||||
register_telegram_type(0x494, F("UBAEnergySupplied"), false, MAKE_PF_CB(process_UBAEnergySupplied));
|
register_telegram_type(0xE3, F("UBAMonitorSlowPlus"), false, MAKE_PF_CB(process_UBAMonitorSlowPlus2));
|
||||||
register_telegram_type(0x495, F("UBAInformation"), false, MAKE_PF_CB(process_UBAInformation));
|
register_telegram_type(0xE4, F("UBAMonitorFastPlus"), false, MAKE_PF_CB(process_UBAMonitorFastPlus));
|
||||||
|
register_telegram_type(0xE5, F("UBAMonitorSlowPlus"), false, MAKE_PF_CB(process_UBAMonitorSlowPlus));
|
||||||
|
register_telegram_type(0xE6, F("UBAParametersPlus"), true, MAKE_PF_CB(process_UBAParametersPlus));
|
||||||
|
register_telegram_type(0xE9, F("UBAMonitorWWPlus"), false, MAKE_PF_CB(process_UBAMonitorWWPlus));
|
||||||
|
register_telegram_type(0xEA, F("UBAParameterWWPlus"), true, MAKE_PF_CB(process_UBAParameterWWPlus));
|
||||||
|
}
|
||||||
|
if (model() == EMSdevice::EMS_DEVICE_FLAG_HEATPUMP) {
|
||||||
|
register_telegram_type(0x494, F("UBAEnergySupplied"), false, MAKE_PF_CB(process_UBAEnergySupplied));
|
||||||
|
register_telegram_type(0x495, F("UBAInformation"), false, MAKE_PF_CB(process_UBAInformation));
|
||||||
|
register_telegram_type(0x48D, F("HpPower"), false, MAKE_PF_CB(process_HpPower));
|
||||||
|
register_telegram_type(0x48F, F("HpOutdoor"), false, MAKE_PF_CB(process_HpOutdoor));
|
||||||
|
}
|
||||||
// MQTT commands for boiler topic
|
// MQTT commands for boiler topic
|
||||||
register_mqtt_cmd(F("comfort"), MAKE_CF_CB(set_warmwater_mode));
|
register_device_value(TAG_BOILER_DATA, &dummybool_, DeviceValueType::BOOL, nullptr, FL_(wwtapactivated), DeviceValueUOM::NONE, MAKE_CF_CB(set_tapwarmwater_activated));
|
||||||
register_mqtt_cmd(F("wwactivated"), MAKE_CF_CB(set_warmwater_activated));
|
register_device_value(TAG_BOILER_DATA, &dummy8u_, DeviceValueType::ENUM, FL_(enum_reset), FL_(reset), DeviceValueUOM::NONE, MAKE_CF_CB(set_reset));
|
||||||
register_mqtt_cmd(F("wwtapactivated"), MAKE_CF_CB(set_tapwarmwater_activated));
|
|
||||||
register_mqtt_cmd(F("wwflowtempoffset"), MAKE_CF_CB(set_wWFlowTempOffset));
|
|
||||||
register_mqtt_cmd(F("wwmaxpower"), MAKE_CF_CB(set_warmwater_maxpower));
|
|
||||||
register_mqtt_cmd(F("wwonetime"), MAKE_CF_CB(set_warmwater_onetime));
|
|
||||||
register_mqtt_cmd(F("wwcircpump"), MAKE_CF_CB(set_warmwater_circulation_pump));
|
|
||||||
register_mqtt_cmd(F("wwcirculation"), MAKE_CF_CB(set_warmwater_circulation));
|
|
||||||
register_mqtt_cmd(F("wwcircmode"), MAKE_CF_CB(set_warmwater_circulation_mode));
|
|
||||||
register_mqtt_cmd(F("flowtemp"), MAKE_CF_CB(set_flow_temp));
|
|
||||||
register_mqtt_cmd(F("wwsettemp"), MAKE_CF_CB(set_warmwater_temp));
|
|
||||||
register_mqtt_cmd(F("heatingactivated"), MAKE_CF_CB(set_heating_activated));
|
|
||||||
register_mqtt_cmd(F("heatingtemp"), MAKE_CF_CB(set_heating_temp));
|
|
||||||
register_mqtt_cmd(F("burnmaxpower"), MAKE_CF_CB(set_max_power));
|
|
||||||
register_mqtt_cmd(F("burnminpower"), MAKE_CF_CB(set_min_power));
|
|
||||||
register_mqtt_cmd(F("boilhyston"), MAKE_CF_CB(set_hyst_on));
|
|
||||||
register_mqtt_cmd(F("boilhystoff"), MAKE_CF_CB(set_hyst_off));
|
|
||||||
register_mqtt_cmd(F("burnperiod"), MAKE_CF_CB(set_burn_period));
|
|
||||||
register_mqtt_cmd(F("pumpdelay"), MAKE_CF_CB(set_pump_delay));
|
|
||||||
register_mqtt_cmd(F("maintenance"), MAKE_CF_CB(set_maintenance));
|
|
||||||
register_mqtt_cmd(F("pumpmodmax"), MAKE_CF_CB(set_max_pump));
|
|
||||||
register_mqtt_cmd(F("pumpmodmin"), MAKE_CF_CB(set_min_pump));
|
|
||||||
register_mqtt_cmd(F("reset"), MAKE_CF_CB(set_reset));
|
|
||||||
|
|
||||||
// add values
|
// add values
|
||||||
reserve_device_values(50);
|
// reserve_device_values(90);
|
||||||
|
|
||||||
// main - boiler_data topic
|
// main - boiler_data topic
|
||||||
register_device_value(TAG_BOILER_DATA, &id_, DeviceValueType::UINT, nullptr, F("id"), nullptr); // empty full name to prevent being shown in web or console
|
register_device_value(TAG_BOILER_DATA, &id_, DeviceValueType::UINT, nullptr, FL_(ID), DeviceValueUOM::NONE);
|
||||||
id_ = product_id;
|
id_ = product_id;
|
||||||
|
|
||||||
register_device_value(TAG_BOILER_DATA, &heatingActive_, DeviceValueType::BOOL, nullptr, F("heatingActive"), F("heating active"));
|
register_device_value(TAG_BOILER_DATA, &heatingActive_, DeviceValueType::BOOL, nullptr, FL_(heatingActive), DeviceValueUOM::NONE);
|
||||||
register_device_value(TAG_BOILER_DATA, &tapwaterActive_, DeviceValueType::BOOL, nullptr, F("tapwaterActive"), F("warm water active"));
|
register_device_value(TAG_BOILER_DATA, &tapwaterActive_, DeviceValueType::BOOL, nullptr, FL_(tapwaterActive), DeviceValueUOM::NONE);
|
||||||
register_device_value(TAG_BOILER_DATA, &selFlowTemp_, DeviceValueType::UINT, nullptr, F("selFlowTemp"), F("selected flow temperature"), DeviceValueUOM::DEGREES);
|
register_device_value(TAG_BOILER_DATA, &selFlowTemp_, DeviceValueType::UINT, nullptr, FL_(selFlowTemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_flow_temp));
|
||||||
register_device_value(TAG_BOILER_DATA, &selBurnPow_, DeviceValueType::UINT, nullptr, F("selBurnPow"), F("burner selected max power"), DeviceValueUOM::PERCENT);
|
register_device_value(TAG_BOILER_DATA, &selBurnPow_, DeviceValueType::UINT, nullptr, FL_(selBurnPow), DeviceValueUOM::PERCENT, MAKE_CF_CB(set_burn_power));
|
||||||
register_device_value(TAG_BOILER_DATA, &heatingPumpMod_, DeviceValueType::UINT, nullptr, F("heatingPumpMod"), F("heating pump modulation"), DeviceValueUOM::PERCENT);
|
register_device_value(TAG_BOILER_DATA, &heatingPumpMod_, DeviceValueType::UINT, nullptr, FL_(heatingPumpMod), DeviceValueUOM::PERCENT);
|
||||||
register_device_value(TAG_BOILER_DATA, &heatingPump2Mod_, DeviceValueType::UINT, nullptr, F("heatingPump2Mod"), F("heating pump 2 modulation"), DeviceValueUOM::PERCENT);
|
register_device_value(TAG_BOILER_DATA, &heatingPump2Mod_, DeviceValueType::UINT, nullptr, FL_(heatingPump2Mod), DeviceValueUOM::PERCENT);
|
||||||
register_device_value(TAG_BOILER_DATA, &outdoorTemp_, DeviceValueType::SHORT, FL_(div10), F("outdoorTemp"), F("outside temperature"), DeviceValueUOM::DEGREES);
|
register_device_value(TAG_BOILER_DATA, &outdoorTemp_, DeviceValueType::SHORT, FL_(div10), FL_(outdoorTemp), DeviceValueUOM::DEGREES);
|
||||||
register_device_value(TAG_BOILER_DATA, &curFlowTemp_, DeviceValueType::USHORT, FL_(div10), F("curFlowTemp"), F("current flow temperature"), DeviceValueUOM::DEGREES);
|
register_device_value(TAG_BOILER_DATA, &curFlowTemp_, DeviceValueType::USHORT, FL_(div10), FL_(curFlowTemp), DeviceValueUOM::DEGREES);
|
||||||
register_device_value(TAG_BOILER_DATA, &retTemp_, DeviceValueType::USHORT, FL_(div10), F("retTemp"), F("return temperature"), DeviceValueUOM::DEGREES);
|
register_device_value(TAG_BOILER_DATA, &retTemp_, DeviceValueType::USHORT, FL_(div10), FL_(retTemp), DeviceValueUOM::DEGREES);
|
||||||
register_device_value(TAG_BOILER_DATA, &switchTemp_, DeviceValueType::USHORT, FL_(div10), F("switchTemp"), F("mixing switch temperature"), DeviceValueUOM::DEGREES);
|
register_device_value(TAG_BOILER_DATA, &switchTemp_, DeviceValueType::USHORT, FL_(div10), FL_(switchTemp), DeviceValueUOM::DEGREES);
|
||||||
register_device_value(TAG_BOILER_DATA, &sysPress_, DeviceValueType::UINT, FL_(div10), F("sysPress"), F("system pressure"), DeviceValueUOM::BAR);
|
register_device_value(TAG_BOILER_DATA, &sysPress_, DeviceValueType::UINT, FL_(div10), FL_(sysPress), DeviceValueUOM::BAR);
|
||||||
register_device_value(TAG_BOILER_DATA, &boilTemp_, DeviceValueType::USHORT, FL_(div10), F("boilTemp"), F("max boiler temperature"), DeviceValueUOM::DEGREES);
|
register_device_value(TAG_BOILER_DATA, &boilTemp_, DeviceValueType::USHORT, FL_(div10), FL_(boilTemp), DeviceValueUOM::DEGREES);
|
||||||
register_device_value(TAG_BOILER_DATA, &exhaustTemp_, DeviceValueType::USHORT, FL_(div10), F("exhaustTemp"), F("exhaust temperature"), DeviceValueUOM::DEGREES);
|
register_device_value(TAG_BOILER_DATA, &exhaustTemp_, DeviceValueType::USHORT, FL_(div10), FL_(exhaustTemp), DeviceValueUOM::DEGREES);
|
||||||
register_device_value(TAG_BOILER_DATA, &burnGas_, DeviceValueType::BOOL, nullptr, F("burnGas"), F("gas"));
|
register_device_value(TAG_BOILER_DATA, &burnGas_, DeviceValueType::BOOL, nullptr, FL_(burnGas), DeviceValueUOM::NONE);
|
||||||
register_device_value(TAG_BOILER_DATA, &flameCurr_, DeviceValueType::USHORT, FL_(div10), F("flameCurr"), F("flame current"), DeviceValueUOM::UA);
|
register_device_value(TAG_BOILER_DATA, &flameCurr_, DeviceValueType::USHORT, FL_(div10), FL_(flameCurr), DeviceValueUOM::UA);
|
||||||
register_device_value(TAG_BOILER_DATA, &heatingPump_, DeviceValueType::BOOL, nullptr, F("heatingPump"), F("heating pump"), DeviceValueUOM::PUMP);
|
register_device_value(TAG_BOILER_DATA, &heatingPump_, DeviceValueType::BOOL, nullptr, FL_(heatingPump), DeviceValueUOM::PUMP);
|
||||||
register_device_value(TAG_BOILER_DATA, &fanWork_, DeviceValueType::BOOL, nullptr, F("fanWork"), F("fan"));
|
register_device_value(TAG_BOILER_DATA, &fanWork_, DeviceValueType::BOOL, nullptr, FL_(fanWork), DeviceValueUOM::NONE);
|
||||||
register_device_value(TAG_BOILER_DATA, &ignWork_, DeviceValueType::BOOL, nullptr, F("ignWork"), F("ignition"));
|
register_device_value(TAG_BOILER_DATA, &ignWork_, DeviceValueType::BOOL, nullptr, FL_(ignWork), DeviceValueUOM::NONE);
|
||||||
register_device_value(TAG_BOILER_DATA, &heatingActivated_, DeviceValueType::BOOL, nullptr, F("heatingActivated"), F("heating activated"));
|
register_device_value(TAG_BOILER_DATA, &heatingActivated_, DeviceValueType::BOOL, nullptr, FL_(heatingActivated), DeviceValueUOM::NONE, MAKE_CF_CB(set_heating_activated));
|
||||||
register_device_value(TAG_BOILER_DATA, &heatingTemp_, DeviceValueType::UINT, nullptr, F("heatingTemp"), F("heating temperature"), DeviceValueUOM::DEGREES);
|
register_device_value(TAG_BOILER_DATA, &heatingTemp_, DeviceValueType::UINT, nullptr, FL_(heatingTemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_heating_temp));
|
||||||
register_device_value(TAG_BOILER_DATA, &pumpModMax_, DeviceValueType::UINT, nullptr, F("pumpModMax"), F("burner pump max power"), DeviceValueUOM::PERCENT);
|
register_device_value(TAG_BOILER_DATA, &pumpModMax_, DeviceValueType::UINT, nullptr, FL_(pumpModMax), DeviceValueUOM::PERCENT, MAKE_CF_CB(set_max_pump));
|
||||||
register_device_value(TAG_BOILER_DATA, &pumpModMin_, DeviceValueType::UINT, nullptr, F("pumpModMin"), F("burner pump min power"), DeviceValueUOM::PERCENT);
|
register_device_value(TAG_BOILER_DATA, &pumpModMin_, DeviceValueType::UINT, nullptr, FL_(pumpModMin), DeviceValueUOM::PERCENT, MAKE_CF_CB(set_min_pump));
|
||||||
register_device_value(TAG_BOILER_DATA, &pumpDelay_, DeviceValueType::UINT, nullptr, F("pumpDelay"), F("pump delay"), DeviceValueUOM::MINUTES);
|
register_device_value(TAG_BOILER_DATA, &pumpDelay_, DeviceValueType::UINT, nullptr, FL_(pumpDelay), DeviceValueUOM::MINUTES, MAKE_CF_CB(set_pump_delay));
|
||||||
register_device_value(TAG_BOILER_DATA, &burnMinPeriod_, DeviceValueType::UINT, nullptr, F("burnMinPeriod"), F("burner min period"), DeviceValueUOM::MINUTES);
|
register_device_value(TAG_BOILER_DATA, &burnMinPeriod_, DeviceValueType::UINT, nullptr, FL_(burnMinPeriod), DeviceValueUOM::MINUTES, MAKE_CF_CB(set_burn_period));
|
||||||
register_device_value(TAG_BOILER_DATA, &burnMinPower_, DeviceValueType::UINT, nullptr, F("burnMinPower"), F("burner min power"), DeviceValueUOM::PERCENT);
|
register_device_value(TAG_BOILER_DATA, &burnMinPower_, DeviceValueType::UINT, nullptr, FL_(burnMinPower), DeviceValueUOM::PERCENT, MAKE_CF_CB(set_min_power));
|
||||||
register_device_value(TAG_BOILER_DATA, &burnMaxPower_, DeviceValueType::UINT, nullptr, F("burnMaxPower"), F("burner max power"), DeviceValueUOM::PERCENT);
|
register_device_value(TAG_BOILER_DATA, &burnMaxPower_, DeviceValueType::UINT, nullptr, FL_(burnMaxPower), DeviceValueUOM::PERCENT, MAKE_CF_CB(set_max_power));
|
||||||
register_device_value(TAG_BOILER_DATA, &boilHystOn_, DeviceValueType::INT, nullptr, F("boilHystOn"), F("hysteresis on temperature"), DeviceValueUOM::DEGREES);
|
register_device_value(TAG_BOILER_DATA, &boilHystOn_, DeviceValueType::INT, nullptr, FL_(boilHystOn), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_hyst_on));
|
||||||
register_device_value(TAG_BOILER_DATA, &boilHystOff_, DeviceValueType::INT, nullptr, F("boilHystOff"), F("hysteresis off temperature"), DeviceValueUOM::DEGREES);
|
register_device_value(TAG_BOILER_DATA, &boilHystOff_, DeviceValueType::INT, nullptr, FL_(boilHystOff), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_hyst_off));
|
||||||
register_device_value(TAG_BOILER_DATA, &setFlowTemp_, DeviceValueType::UINT, nullptr, F("setFlowTemp"), F("set flow temperature"), DeviceValueUOM::DEGREES);
|
register_device_value(TAG_BOILER_DATA, &setFlowTemp_, DeviceValueType::UINT, nullptr, FL_(setFlowTemp), DeviceValueUOM::DEGREES);
|
||||||
register_device_value(TAG_BOILER_DATA, &setBurnPow_, DeviceValueType::UINT, nullptr, F("setBurnPow"), F("burner set power"), DeviceValueUOM::PERCENT);
|
register_device_value(TAG_BOILER_DATA, &setBurnPow_, DeviceValueType::UINT, nullptr, FL_(setBurnPow), DeviceValueUOM::PERCENT);
|
||||||
register_device_value(TAG_BOILER_DATA, &curBurnPow_, DeviceValueType::UINT, nullptr, F("curBurnPow"), F("burner current power"), DeviceValueUOM::PERCENT);
|
register_device_value(TAG_BOILER_DATA, &curBurnPow_, DeviceValueType::UINT, nullptr, FL_(curBurnPow), DeviceValueUOM::PERCENT);
|
||||||
register_device_value(TAG_BOILER_DATA, &burnStarts_, DeviceValueType::ULONG, nullptr, F("burnStarts"), F("burner # starts"));
|
register_device_value(TAG_BOILER_DATA, &burnStarts_, DeviceValueType::ULONG, nullptr, FL_(burnStarts), DeviceValueUOM::NONE);
|
||||||
register_device_value(TAG_BOILER_DATA, &burnWorkMin_, DeviceValueType::TIME, nullptr, F("burnWorkMin"), F("total burner operating time"), DeviceValueUOM::MINUTES);
|
register_device_value(TAG_BOILER_DATA, &burnWorkMin_, DeviceValueType::TIME, nullptr, FL_(burnWorkMin), DeviceValueUOM::MINUTES);
|
||||||
register_device_value(TAG_BOILER_DATA, &heatWorkMin_, DeviceValueType::TIME, nullptr, F("heatWorkMin"), F("total heat operating time"), DeviceValueUOM::MINUTES);
|
register_device_value(TAG_BOILER_DATA, &heatWorkMin_, DeviceValueType::TIME, nullptr, FL_(heatWorkMin), DeviceValueUOM::MINUTES);
|
||||||
register_device_value(TAG_BOILER_DATA, &UBAuptime_, DeviceValueType::TIME, nullptr, F("UBAuptime"), F("total UBA operating time"), DeviceValueUOM::MINUTES);
|
register_device_value(TAG_BOILER_DATA, &UBAuptime_, DeviceValueType::TIME, nullptr, FL_(UBAuptime), DeviceValueUOM::MINUTES);
|
||||||
register_device_value(TAG_BOILER_DATA, &lastCode_, DeviceValueType::TEXT, nullptr, F("lastCode"), F("last error code"));
|
register_device_value(TAG_BOILER_DATA, &lastCode_, DeviceValueType::TEXT, nullptr, FL_(lastCode), DeviceValueUOM::NONE);
|
||||||
register_device_value(TAG_BOILER_DATA, &serviceCode_, DeviceValueType::TEXT, nullptr, F("serviceCode"), F("service code"));
|
register_device_value(TAG_BOILER_DATA, &serviceCode_, DeviceValueType::TEXT, nullptr, FL_(serviceCode), DeviceValueUOM::NONE);
|
||||||
register_device_value(TAG_BOILER_DATA, &serviceCodeNumber_, DeviceValueType::USHORT, nullptr, F("serviceCodeNumber"), F("service code number"));
|
register_device_value(TAG_BOILER_DATA, &serviceCodeNumber_, DeviceValueType::USHORT, nullptr, FL_(serviceCodeNumber), DeviceValueUOM::NONE);
|
||||||
|
register_device_value(TAG_BOILER_DATA, &maintenanceMessage_, DeviceValueType::TEXT, nullptr, FL_(maintenanceMessage), DeviceValueUOM::NONE);
|
||||||
// info
|
register_device_value(TAG_BOILER_DATA, &maintenanceDate_, DeviceValueType::TEXT, nullptr, FL_(maintenanceDate), DeviceValueUOM::NONE);
|
||||||
register_device_value(TAG_BOILER_DATA, &upTimeControl_, DeviceValueType::TIME, FL_(div60), F("upTimeControl"), F("operating time total heat"), DeviceValueUOM::MINUTES);
|
register_device_value(TAG_BOILER_DATA, &maintenanceType_, DeviceValueType::ENUM, FL_(enum_off_time_date), FL_(maintenanceType), DeviceValueUOM::NONE, MAKE_CF_CB(set_maintenance));
|
||||||
register_device_value(TAG_BOILER_DATA, &upTimeCompHeating_, DeviceValueType::TIME, FL_(div60), F("upTimeCompHeating"), F("operating time compressor heating"), DeviceValueUOM::MINUTES);
|
register_device_value(TAG_BOILER_DATA, &maintenanceTime_, DeviceValueType::USHORT, nullptr, FL_(maintenanceTime), DeviceValueUOM::HOURS);
|
||||||
register_device_value(TAG_BOILER_DATA, &upTimeCompCooling_, DeviceValueType::TIME, FL_(div60), F("upTimeCompCooling"), F("operating time compressor cooling"), DeviceValueUOM::MINUTES);
|
// heatpump info
|
||||||
register_device_value(TAG_BOILER_DATA, &upTimeCompWw_, DeviceValueType::TIME, FL_(div60), F("upTimeCompWw"), F("operating time compressor warm water"), DeviceValueUOM::MINUTES);
|
if (model() == EMS_DEVICE_FLAG_HEATPUMP) {
|
||||||
register_device_value(TAG_BOILER_DATA, &heatingStarts_, DeviceValueType::ULONG, nullptr, F("heatingStarts"), F("# heating control starts"));
|
register_device_value(TAG_BOILER_DATA, &upTimeControl_, DeviceValueType::TIME, FL_(div60), FL_(upTimeControl), DeviceValueUOM::MINUTES);
|
||||||
register_device_value(TAG_BOILER_DATA, &coolingStarts_, DeviceValueType::ULONG, nullptr, F("coolingStarts"), F("# cooling control starts"));
|
register_device_value(TAG_BOILER_DATA, &upTimeCompHeating_, DeviceValueType::TIME, FL_(div60), FL_(upTimeCompHeating), DeviceValueUOM::MINUTES);
|
||||||
register_device_value(TAG_BOILER_DATA, &nrgConsTotal_, DeviceValueType::ULONG, nullptr, F("nrgConsTotal"), F("total energy consumption"));
|
register_device_value(TAG_BOILER_DATA, &upTimeCompCooling_, DeviceValueType::TIME, FL_(div60), FL_(upTimeCompCooling), DeviceValueUOM::MINUTES);
|
||||||
register_device_value(TAG_BOILER_DATA, &nrgConsCompTotal_, DeviceValueType::ULONG, nullptr, F("nrgConsCompTotal"), F("energy consumption compressor total"));
|
register_device_value(TAG_BOILER_DATA, &upTimeCompWw_, DeviceValueType::TIME, FL_(div60), FL_(upTimeCompWw), DeviceValueUOM::MINUTES);
|
||||||
register_device_value(TAG_BOILER_DATA, &nrgConsCompHeating_, DeviceValueType::ULONG, nullptr, F("nrgConsCompHeating"), F("energy consumption compressor heating"));
|
register_device_value(TAG_BOILER_DATA, &heatingStarts_, DeviceValueType::ULONG, nullptr, FL_(heatingStarts), DeviceValueUOM::NONE);
|
||||||
register_device_value(TAG_BOILER_DATA, &nrgConsCompWw_, DeviceValueType::ULONG, nullptr, F("nrgConsCompWw"), F("energy consumption compressor warm water"));
|
register_device_value(TAG_BOILER_DATA, &coolingStarts_, DeviceValueType::ULONG, nullptr, FL_(coolingStarts), DeviceValueUOM::NONE);
|
||||||
register_device_value(TAG_BOILER_DATA, &nrgConsCompCooling_, DeviceValueType::ULONG, nullptr, F("nrgConsCompCooling"), F("energy consumption compressor cooling"));
|
register_device_value(TAG_BOILER_DATA, &nrgConsTotal_, DeviceValueType::ULONG, nullptr, FL_(nrgConsTotal), DeviceValueUOM::KWH);
|
||||||
register_device_value(TAG_BOILER_DATA, &nrgSuppTotal_, DeviceValueType::ULONG, nullptr, F("nrgSuppTotal"), F("total energy supplied"));
|
register_device_value(TAG_BOILER_DATA, &nrgConsCompTotal_, DeviceValueType::ULONG, nullptr, FL_(nrgConsCompTotal), DeviceValueUOM::KWH);
|
||||||
register_device_value(TAG_BOILER_DATA, &nrgSuppHeating_, DeviceValueType::ULONG, nullptr, F("nrgSuppHeating"), F("total energy supplied heating"));
|
register_device_value(TAG_BOILER_DATA, &nrgConsCompHeating_, DeviceValueType::ULONG, nullptr, FL_(nrgConsCompHeating), DeviceValueUOM::KWH);
|
||||||
register_device_value(TAG_BOILER_DATA, &nrgSuppWw_, DeviceValueType::ULONG, nullptr, F("nrgSuppWw"), F("total energy warm supplied warm water"));
|
register_device_value(TAG_BOILER_DATA, &nrgConsCompWw_, DeviceValueType::ULONG, nullptr, FL_(nrgConsCompWw), DeviceValueUOM::KWH);
|
||||||
register_device_value(TAG_BOILER_DATA, &nrgSuppCooling_, DeviceValueType::ULONG, nullptr, F("nrgSuppCooling"), F("total energy supplied cooling"));
|
register_device_value(TAG_BOILER_DATA, &nrgConsCompCooling_, DeviceValueType::ULONG, nullptr, FL_(nrgConsCompCooling), DeviceValueUOM::KWH);
|
||||||
register_device_value(TAG_BOILER_DATA, &auxElecHeatNrgConsTotal_, DeviceValueType::ULONG, nullptr, F("auxElecHeatNrgConsTotal"), F("auxiliary electrical heater energy consumption total"));
|
register_device_value(TAG_BOILER_DATA, &auxElecHeatNrgConsTotal_, DeviceValueType::ULONG, nullptr, FL_(auxElecHeatNrgConsTotal), DeviceValueUOM::KWH);
|
||||||
register_device_value(TAG_BOILER_DATA, &auxElecHeatNrgConsHeating_, DeviceValueType::ULONG, nullptr, F("auxElecHeatNrgConsHeating"), F("auxiliary electrical heater energy consumption heating"));
|
register_device_value(TAG_BOILER_DATA, &auxElecHeatNrgConsHeating_, DeviceValueType::ULONG, nullptr, FL_(auxElecHeatNrgConsHeating), DeviceValueUOM::KWH);
|
||||||
register_device_value(TAG_BOILER_DATA, &auxElecHeatNrgConsDHW_, DeviceValueType::ULONG, nullptr, F("auxElecHeatNrgConsDHW"), F("auxiliary electrical heater energy consumption DHW"));
|
register_device_value(TAG_BOILER_DATA, &auxElecHeatNrgConsWW_, DeviceValueType::ULONG, nullptr, FL_(auxElecHeatNrgConsWW), DeviceValueUOM::KWH);
|
||||||
register_device_value(TAG_BOILER_DATA, &maintenanceMessage_, DeviceValueType::TEXT, nullptr, F("maintenanceMessage"), F("maintenance message"));
|
register_device_value(TAG_BOILER_DATA, &nrgSuppTotal_, DeviceValueType::ULONG, nullptr, FL_(nrgSuppTotal), DeviceValueUOM::KWH);
|
||||||
register_device_value(TAG_BOILER_DATA, &maintenanceDate_, DeviceValueType::TEXT, nullptr, F("maintenanceDate"), F("maintenance set date"));
|
register_device_value(TAG_BOILER_DATA, &nrgSuppHeating_, DeviceValueType::ULONG, nullptr, FL_(nrgSuppHeating), DeviceValueUOM::KWH);
|
||||||
register_device_value(TAG_BOILER_DATA, &maintenanceType_, DeviceValueType::ENUM, FL_(enum_off_time_date), F("maintenanceType"), F("maintenance scheduled"));
|
register_device_value(TAG_BOILER_DATA, &nrgSuppWw_, DeviceValueType::ULONG, nullptr, FL_(nrgSuppWw), DeviceValueUOM::KWH);
|
||||||
register_device_value(TAG_BOILER_DATA, &maintenanceTime_, DeviceValueType::USHORT, nullptr, F("maintenanceTime"), F("maintenance set time"), DeviceValueUOM::HOURS);
|
register_device_value(TAG_BOILER_DATA, &nrgSuppCooling_, DeviceValueType::ULONG, nullptr, FL_(nrgSuppCooling), DeviceValueUOM::KWH);
|
||||||
|
register_device_value(TAG_BOILER_DATA, &hpPower_, DeviceValueType::UINT, FL_(div10), FL_(hpPower), DeviceValueUOM::KW);
|
||||||
|
register_device_value(TAG_BOILER_DATA, &hpTc0_, DeviceValueType::SHORT, FL_(div10), FL_(hpTc0), DeviceValueUOM::DEGREES);
|
||||||
|
register_device_value(TAG_BOILER_DATA, &hpTc1_, DeviceValueType::SHORT, FL_(div10), FL_(hpTc1), DeviceValueUOM::DEGREES);
|
||||||
|
register_device_value(TAG_BOILER_DATA, &hpTc3_, DeviceValueType::SHORT, FL_(div10), FL_(hpTc3), DeviceValueUOM::DEGREES);
|
||||||
|
register_device_value(TAG_BOILER_DATA, &hpTr3_, DeviceValueType::SHORT, FL_(div10), FL_(hpTr3), DeviceValueUOM::DEGREES);
|
||||||
|
register_device_value(TAG_BOILER_DATA, &hpTr4_, DeviceValueType::SHORT, FL_(div10), FL_(hpTr4), DeviceValueUOM::DEGREES);
|
||||||
|
register_device_value(TAG_BOILER_DATA, &hpTr5_, DeviceValueType::SHORT, FL_(div10), FL_(hpTr5), DeviceValueUOM::DEGREES);
|
||||||
|
register_device_value(TAG_BOILER_DATA, &hpTr6_, DeviceValueType::SHORT, FL_(div10), FL_(hpTr6), DeviceValueUOM::DEGREES);
|
||||||
|
register_device_value(TAG_BOILER_DATA, &hpTr7_, DeviceValueType::SHORT, FL_(div10), FL_(hpTr7), DeviceValueUOM::DEGREES);
|
||||||
|
register_device_value(TAG_BOILER_DATA, &hpTl2_, DeviceValueType::SHORT, FL_(div10), FL_(hpTl2), DeviceValueUOM::DEGREES);
|
||||||
|
register_device_value(TAG_BOILER_DATA, &hpPl1_, DeviceValueType::SHORT, FL_(div10), FL_(hpPl1), DeviceValueUOM::DEGREES);
|
||||||
|
register_device_value(TAG_BOILER_DATA, &hpPh1_, DeviceValueType::SHORT, FL_(div10), FL_(hpPh1), DeviceValueUOM::DEGREES);
|
||||||
|
}
|
||||||
|
|
||||||
// warm water - boiler_data_ww topic
|
// warm water - boiler_data_ww topic
|
||||||
register_device_value(TAG_BOILER_DATA_WW, &wWSelTemp_, DeviceValueType::UINT, nullptr, F("wWSelTemp"), F("selected temperature"), DeviceValueUOM::DEGREES);
|
register_device_value(TAG_DEVICE_DATA_WW, &wWSelTemp_, DeviceValueType::UINT, nullptr, FL_(wWSelTemp), DeviceValueUOM::DEGREES);
|
||||||
register_device_value(TAG_BOILER_DATA_WW, &wWSetTemp_, DeviceValueType::UINT, nullptr, F("wWSetTemp"), F("set temperature"), DeviceValueUOM::DEGREES);
|
register_device_value(TAG_DEVICE_DATA_WW, &wWSetTemp_, DeviceValueType::UINT, nullptr, FL_(wWSetTemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_warmwater_temp));
|
||||||
register_device_value(TAG_BOILER_DATA_WW, &wWType_, DeviceValueType::ENUM, FL_(enum_flow), F("wWType"), F("type"));
|
register_device_value(TAG_DEVICE_DATA_WW, &wWType_, DeviceValueType::ENUM, FL_(enum_flow), FL_(wWType), DeviceValueUOM::NONE);
|
||||||
register_device_value(TAG_BOILER_DATA_WW, &wWComfort_, DeviceValueType::ENUM, FL_(enum_comfort), F("wWComfort"), F("comfort"));
|
register_device_value(TAG_DEVICE_DATA_WW, &wWComfort_, DeviceValueType::ENUM, FL_(enum_comfort), FL_(wWComfort), DeviceValueUOM::NONE, MAKE_CF_CB(set_warmwater_mode));
|
||||||
register_device_value(TAG_BOILER_DATA_WW, &wWFlowTempOffset_, DeviceValueType::UINT, nullptr, F("wWFlowTempOffset"), F("flow temperature offset"));
|
register_device_value(TAG_DEVICE_DATA_WW, &wWFlowTempOffset_, DeviceValueType::UINT, nullptr, FL_(wWFlowTempOffset), DeviceValueUOM::NONE, MAKE_CF_CB(set_wWFlowTempOffset));
|
||||||
register_device_value(TAG_BOILER_DATA_WW, &wWMaxPower_, DeviceValueType::UINT, nullptr, F("wWMaxPower"), F("max power"), DeviceValueUOM::PERCENT);
|
register_device_value(TAG_DEVICE_DATA_WW, &wWMaxPower_, DeviceValueType::UINT, nullptr, FL_(wWMaxPower), DeviceValueUOM::PERCENT, MAKE_CF_CB(set_warmwater_maxpower));
|
||||||
register_device_value(TAG_BOILER_DATA_WW, &wWCircPump_, DeviceValueType::BOOL, nullptr, F("wWCircPump"), F("circulation pump available"));
|
register_device_value(TAG_DEVICE_DATA_WW, &wWCircPump_, DeviceValueType::BOOL, nullptr, FL_(wWCircPump), DeviceValueUOM::NONE, MAKE_CF_CB(set_warmwater_circulation_pump));
|
||||||
register_device_value(TAG_BOILER_DATA_WW, &wWChargeType_, DeviceValueType::BOOL, FL_(enum_charge), F("wWChargeType"), F("charging type"));
|
register_device_value(TAG_DEVICE_DATA_WW, &wWChargeType_, DeviceValueType::BOOL, FL_(enum_charge), FL_(wWChargeType), DeviceValueUOM::NONE);
|
||||||
register_device_value(TAG_BOILER_DATA_WW, &wWDisinfectionTemp_, DeviceValueType::UINT, nullptr, F("wWDisinfectionTemp"), F("disinfection temperature"), DeviceValueUOM::DEGREES);
|
register_device_value(TAG_DEVICE_DATA_WW, &wWDisinfectionTemp_, DeviceValueType::UINT, nullptr, FL_(wWDisinfectionTemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_disinfect_temp));
|
||||||
register_device_value(TAG_BOILER_DATA_WW, &wWCircMode_, DeviceValueType::ENUM, FL_(enum_freq), F("wWCircMode"), F("circulation pump freq"));
|
register_device_value(TAG_DEVICE_DATA_WW, &wWCircMode_, DeviceValueType::ENUM, FL_(enum_freq), FL_(wWCircMode), DeviceValueUOM::NONE, MAKE_CF_CB(set_warmwater_circulation_mode));
|
||||||
register_device_value(TAG_BOILER_DATA_WW, &wWCirc_, DeviceValueType::BOOL, nullptr, F("wWCirc"), F("circulation active"));
|
register_device_value(TAG_DEVICE_DATA_WW, &wWCirc_, DeviceValueType::BOOL, nullptr, FL_(wWCirc), DeviceValueUOM::NONE, MAKE_CF_CB(set_warmwater_circulation));
|
||||||
register_device_value(TAG_BOILER_DATA_WW, &wWCurTemp_, DeviceValueType::USHORT, FL_(div10), F("wWCurTemp"), F("current intern temperature"), DeviceValueUOM::DEGREES);
|
register_device_value(TAG_DEVICE_DATA_WW, &wWCurTemp_, DeviceValueType::USHORT, FL_(div10), FL_(wWCurTemp), DeviceValueUOM::DEGREES);
|
||||||
register_device_value(TAG_BOILER_DATA_WW, &wWCurTemp2_, DeviceValueType::USHORT, FL_(div10), F("wWCurTemp2"), F("current extern temperature"), DeviceValueUOM::DEGREES);
|
register_device_value(TAG_DEVICE_DATA_WW, &wWCurTemp2_, DeviceValueType::USHORT, FL_(div10), FL_(wWCurTemp2), DeviceValueUOM::DEGREES);
|
||||||
register_device_value(TAG_BOILER_DATA_WW, &wWCurFlow_, DeviceValueType::UINT, FL_(div10), F("wWCurFlow"), F("current tap water flow"), DeviceValueUOM::LMIN);
|
register_device_value(TAG_DEVICE_DATA_WW, &wWCurFlow_, DeviceValueType::UINT, FL_(div10), FL_(wWCurFlow), DeviceValueUOM::LMIN);
|
||||||
register_device_value(TAG_BOILER_DATA_WW, &wWStorageTemp1_, DeviceValueType::USHORT, FL_(div10), F("wWStorageTemp1"), F("storage intern temperature"), DeviceValueUOM::DEGREES);
|
register_device_value(TAG_DEVICE_DATA_WW, &wWStorageTemp1_, DeviceValueType::USHORT, FL_(div10), FL_(wWStorageTemp1), DeviceValueUOM::DEGREES);
|
||||||
register_device_value(TAG_BOILER_DATA_WW, &wWStorageTemp2_, DeviceValueType::USHORT, FL_(div10), F("wWStorageTemp2"), F("storage extern temperature"), DeviceValueUOM::DEGREES);
|
register_device_value(TAG_DEVICE_DATA_WW, &wWStorageTemp2_, DeviceValueType::USHORT, FL_(div10), FL_(wWStorageTemp2), DeviceValueUOM::DEGREES);
|
||||||
register_device_value(TAG_BOILER_DATA_WW, &wWActivated_, DeviceValueType::BOOL, nullptr, F("wWActivated"), F("activated"));
|
register_device_value(TAG_DEVICE_DATA_WW, &wWActivated_, DeviceValueType::BOOL, nullptr, FL_(wWActivated), DeviceValueUOM::NONE, MAKE_CF_CB(set_warmwater_activated));
|
||||||
register_device_value(TAG_BOILER_DATA_WW, &wWOneTime_, DeviceValueType::BOOL, nullptr, F("wWOneTime"), F("one time charging"));
|
register_device_value(TAG_DEVICE_DATA_WW, &wWOneTime_, DeviceValueType::BOOL, nullptr, FL_(wWOneTime), DeviceValueUOM::NONE, MAKE_CF_CB(set_warmwater_onetime));
|
||||||
register_device_value(TAG_BOILER_DATA_WW, &wWDisinfecting_, DeviceValueType::BOOL, nullptr, F("wWDisinfecting"), F("disinfecting"));
|
register_device_value(TAG_DEVICE_DATA_WW, &wWDisinfecting_, DeviceValueType::BOOL, nullptr, FL_(wWDisinfecting), DeviceValueUOM::NONE);
|
||||||
register_device_value(TAG_BOILER_DATA_WW, &wWCharging_, DeviceValueType::BOOL, nullptr, F("wWCharging"), F("charging"));
|
register_device_value(TAG_DEVICE_DATA_WW, &wWCharging_, DeviceValueType::BOOL, nullptr, FL_(wWCharging), DeviceValueUOM::NONE);
|
||||||
register_device_value(TAG_BOILER_DATA_WW, &wWRecharging_, DeviceValueType::BOOL, nullptr, F("wWRecharging"), F("recharging"));
|
register_device_value(TAG_DEVICE_DATA_WW, &wWRecharging_, DeviceValueType::BOOL, nullptr, FL_(wWRecharging), DeviceValueUOM::NONE);
|
||||||
register_device_value(TAG_BOILER_DATA_WW, &wWTempOK_, DeviceValueType::BOOL, nullptr, F("wWTempOK"), F("temperature ok"));
|
register_device_value(TAG_DEVICE_DATA_WW, &wWTempOK_, DeviceValueType::BOOL, nullptr, FL_(wWTempOK), DeviceValueUOM::NONE);
|
||||||
register_device_value(TAG_BOILER_DATA_WW, &wWActive_, DeviceValueType::BOOL, nullptr, F("wWActive"), F("active"));
|
register_device_value(TAG_DEVICE_DATA_WW, &wWActive_, DeviceValueType::BOOL, nullptr, FL_(wWActive), DeviceValueUOM::NONE);
|
||||||
register_device_value(TAG_BOILER_DATA_WW, &wWHeat_, DeviceValueType::BOOL, nullptr, F("wWHeat"), F("heating"));
|
register_device_value(TAG_DEVICE_DATA_WW, &wWHeat_, DeviceValueType::BOOL, nullptr, FL_(wWHeat), DeviceValueUOM::NONE);
|
||||||
register_device_value(TAG_BOILER_DATA_WW, &wWSetPumpPower_, DeviceValueType::UINT, nullptr, F("wWSetPumpPower"), F("pump set power"), DeviceValueUOM::PERCENT);
|
register_device_value(TAG_DEVICE_DATA_WW, &wWSetPumpPower_, DeviceValueType::UINT, nullptr, FL_(wWSetPumpPower), DeviceValueUOM::PERCENT);
|
||||||
register_device_value(TAG_BOILER_DATA_WW, &mixerTemp_, DeviceValueType::USHORT, FL_(div10), F("mixerTemp"), F("mixer temperature"), DeviceValueUOM::DEGREES);
|
register_device_value(TAG_DEVICE_DATA_WW, &mixerTemp_, DeviceValueType::USHORT, FL_(div10), FL_(mixerTemp), DeviceValueUOM::DEGREES);
|
||||||
register_device_value(TAG_BOILER_DATA_WW, &tankMiddleTemp_, DeviceValueType::USHORT, FL_(div10), F("tankMiddleTemp"), F("tank middle temperature (TS3)"), DeviceValueUOM::DEGREES);
|
register_device_value(TAG_DEVICE_DATA_WW, &tankMiddleTemp_, DeviceValueType::USHORT, FL_(div10), FL_(tankMiddleTemp), DeviceValueUOM::DEGREES);
|
||||||
register_device_value(TAG_BOILER_DATA_WW, &wWStarts_, DeviceValueType::ULONG, nullptr, F("wWStarts"), F("# starts"));
|
register_device_value(TAG_DEVICE_DATA_WW, &wWStarts_, DeviceValueType::ULONG, nullptr, FL_(wWStarts), DeviceValueUOM::NONE);
|
||||||
register_device_value(TAG_BOILER_DATA_WW, &wWStarts2_, DeviceValueType::ULONG, nullptr, F("wWStarts2"), F("# control starts"));
|
register_device_value(TAG_DEVICE_DATA_WW, &wWStarts2_, DeviceValueType::ULONG, nullptr, FL_(wWStarts2), DeviceValueUOM::NONE);
|
||||||
register_device_value(TAG_BOILER_DATA_WW, &wWWorkM_, DeviceValueType::TIME, nullptr, F("wWWorkM"), F("active time"), DeviceValueUOM::MINUTES);
|
register_device_value(TAG_DEVICE_DATA_WW, &wWWorkM_, DeviceValueType::TIME, nullptr, FL_(wWWorkM), DeviceValueUOM::MINUTES);
|
||||||
|
|
||||||
// fetch some initial data
|
// fetch some initial data
|
||||||
EMSESP::send_read_request(0x10, device_id); // read last errorcode on start (only published on errors)
|
EMSESP::send_read_request(0x10, device_id); // read last errorcode on start (only published on errors)
|
||||||
@@ -227,7 +230,7 @@ bool Boiler::publish_ha_config() {
|
|||||||
ids.add("ems-esp-boiler");
|
ids.add("ems-esp-boiler");
|
||||||
|
|
||||||
char topic[Mqtt::MQTT_TOPIC_MAX_SIZE];
|
char topic[Mqtt::MQTT_TOPIC_MAX_SIZE];
|
||||||
snprintf_P(topic, sizeof(topic), PSTR("homeassistant/sensor/%s/boiler/config"), Mqtt::base().c_str());
|
snprintf_P(topic, sizeof(topic), PSTR("sensor/%s/boiler/config"), Mqtt::base().c_str());
|
||||||
Mqtt::publish_ha(topic, doc.as<JsonObject>()); // publish the config payload with retain flag
|
Mqtt::publish_ha(topic, doc.as<JsonObject>()); // publish the config payload with retain flag
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -249,7 +252,7 @@ void Boiler::check_active(const bool force) {
|
|||||||
if (heatingActive_ != val || force) {
|
if (heatingActive_ != val || force) {
|
||||||
heatingActive_ = val;
|
heatingActive_ = val;
|
||||||
char s[7];
|
char s[7];
|
||||||
Mqtt::publish(F("heating_active"), Helpers::render_boolean(s, b));
|
Mqtt::publish(F_(heating_active), Helpers::render_boolean(s, b));
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if tap water is active, bits 1 and 4 must be set
|
// check if tap water is active, bits 1 and 4 must be set
|
||||||
@@ -268,20 +271,24 @@ void Boiler::check_active(const bool force) {
|
|||||||
if (tapwaterActive_ != val || force) {
|
if (tapwaterActive_ != val || force) {
|
||||||
tapwaterActive_ = val;
|
tapwaterActive_ = val;
|
||||||
char s[7];
|
char s[7];
|
||||||
Mqtt::publish(F("tapwater_active"), Helpers::render_boolean(s, b));
|
Mqtt::publish(F_(tapwater_active), Helpers::render_boolean(s, b));
|
||||||
EMSESP::tap_water_active(b); // let EMS-ESP know, used in the Shower class
|
EMSESP::tap_water_active(b); // let EMS-ESP know, used in the Shower class
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 0x33
|
// 0x33
|
||||||
|
// Boiler(0x08) -> Me(0x0B), UBAParameterWW(0x33), data: 08 FF 30 FB FF 28 FF 07 46 00 00
|
||||||
void Boiler::process_UBAParameterWW(std::shared_ptr<const Telegram> telegram) {
|
void Boiler::process_UBAParameterWW(std::shared_ptr<const Telegram> telegram) {
|
||||||
has_update(telegram->read_value(wWActivated_, 1)); // 0xFF means on
|
// has_update(telegram->read_bitvalue(wwEquipt_,0,3)); // 8=boiler has ww
|
||||||
has_update(telegram->read_value(wWCircPump_, 6)); // 0xFF means on
|
has_update(telegram->read_value(wWActivated_, 1)); // 0xFF means on
|
||||||
has_update(telegram->read_value(wWCircMode_, 7)); // 1=1x3min 6=6x3min 7=continuous
|
|
||||||
has_update(telegram->read_value(wWChargeType_, 10)); // 0 = charge pump, 0xff = 3-way valve
|
|
||||||
has_update(telegram->read_value(wWSelTemp_, 2));
|
has_update(telegram->read_value(wWSelTemp_, 2));
|
||||||
|
// has_update(telegram->read_value(wW?_, 3)); // Hyst on (default -5)
|
||||||
|
// has_update(telegram->read_value(wW?_, 4)); // (0xFF) Maybe: Hyst off -1?
|
||||||
|
has_update(telegram->read_value(wWFlowTempOffset_, 5)); // default 40
|
||||||
|
has_update(telegram->read_value(wWCircPump_, 6)); // 0xFF means on
|
||||||
|
has_update(telegram->read_value(wWCircMode_, 7)); // 1=1x3min 6=6x3min 7=continuous
|
||||||
has_update(telegram->read_value(wWDisinfectionTemp_, 8));
|
has_update(telegram->read_value(wWDisinfectionTemp_, 8));
|
||||||
has_update(telegram->read_value(wWFlowTempOffset_, 5));
|
has_update(telegram->read_value(wWChargeType_, 10)); // 0 = charge pump, 0xff = 3-way valve
|
||||||
|
|
||||||
telegram->read_value(wWComfort_, 9);
|
telegram->read_value(wWComfort_, 9);
|
||||||
if (wWComfort_ == 0x00) {
|
if (wWComfort_ == 0x00) {
|
||||||
@@ -355,6 +362,7 @@ void Boiler::process_UBAParameters(std::shared_ptr<const Telegram> telegram) {
|
|||||||
has_update(telegram->read_value(boilHystOff_, 4));
|
has_update(telegram->read_value(boilHystOff_, 4));
|
||||||
has_update(telegram->read_value(boilHystOn_, 5));
|
has_update(telegram->read_value(boilHystOn_, 5));
|
||||||
has_update(telegram->read_value(burnMinPeriod_, 6));
|
has_update(telegram->read_value(burnMinPeriod_, 6));
|
||||||
|
// has_update(telegram->read_value(pumpType_, 7)); // 0=off, 02=?
|
||||||
has_update(telegram->read_value(pumpDelay_, 8));
|
has_update(telegram->read_value(pumpDelay_, 8));
|
||||||
has_update(telegram->read_value(pumpModMax_, 9));
|
has_update(telegram->read_value(pumpModMax_, 9));
|
||||||
has_update(telegram->read_value(pumpModMin_, 10));
|
has_update(telegram->read_value(pumpModMin_, 10));
|
||||||
@@ -371,14 +379,15 @@ void Boiler::process_UBASettingsWW(std::shared_ptr<const Telegram> telegram) {
|
|||||||
/*
|
/*
|
||||||
* UBAMonitorWW - type 0x34 - warm water monitor. 19 bytes long
|
* UBAMonitorWW - type 0x34 - warm water monitor. 19 bytes long
|
||||||
* received every 10 seconds
|
* received every 10 seconds
|
||||||
*/
|
* Boiler(0x08) -> Me(0x0B), UBAMonitorWW(0x34), data: 30 01 BA 7D 00 21 00 00 03 00 01 22 2B 00 19 5B
|
||||||
|
*/
|
||||||
void Boiler::process_UBAMonitorWW(std::shared_ptr<const Telegram> telegram) {
|
void Boiler::process_UBAMonitorWW(std::shared_ptr<const Telegram> telegram) {
|
||||||
has_update(telegram->read_value(wWSetTemp_, 0));
|
has_update(telegram->read_value(wWSetTemp_, 0));
|
||||||
has_update(telegram->read_value(wWCurTemp_, 1));
|
has_update(telegram->read_value(wWCurTemp_, 1));
|
||||||
has_update(telegram->read_value(wWCurTemp2_, 3));
|
has_update(telegram->read_value(wWCurTemp2_, 3));
|
||||||
has_update(telegram->read_value(wWCurFlow_, 9));
|
|
||||||
has_update(telegram->read_value(wWType_, 8));
|
|
||||||
|
|
||||||
|
has_update(telegram->read_value(wWType_, 8));
|
||||||
|
has_update(telegram->read_value(wWCurFlow_, 9));
|
||||||
has_update(telegram->read_value(wWWorkM_, 10, 3)); // force to 3 bytes
|
has_update(telegram->read_value(wWWorkM_, 10, 3)); // force to 3 bytes
|
||||||
has_update(telegram->read_value(wWStarts_, 13, 3)); // force to 3 bytes
|
has_update(telegram->read_value(wWStarts_, 13, 3)); // force to 3 bytes
|
||||||
|
|
||||||
@@ -504,16 +513,16 @@ void Boiler::process_UBAParametersPlus(std::shared_ptr<const Telegram> telegram)
|
|||||||
|
|
||||||
// 0xEA
|
// 0xEA
|
||||||
void Boiler::process_UBAParameterWWPlus(std::shared_ptr<const Telegram> telegram) {
|
void Boiler::process_UBAParameterWWPlus(std::shared_ptr<const Telegram> telegram) {
|
||||||
has_update(telegram->read_value(wWActivated_, 5)); // 0x01 means on
|
has_update(telegram->read_value(wWActivated_, 5)); // 0x01 means on
|
||||||
has_update(telegram->read_value(wWCircPump_, 10)); // 0x01 means yes
|
has_update(telegram->read_value(wWCircPump_, 10)); // 0x01 means yes
|
||||||
has_update(telegram->read_value(wWCircMode_, 11)); // 1=1x3min... 6=6x3min, 7=continuous
|
has_update(telegram->read_value(wWCircMode_, 11)); // 1=1x3min... 6=6x3min, 7=continuous
|
||||||
// has_update(telegram->read_value(wWDisinfectTemp_, 12)); // settings, status in E9
|
// has_update(telegram->read_value(wWDisinfectTemp_, 12)); // settings, status in E9
|
||||||
// has_update(telegram->read_value(wWSelTemp_, 6)); // settings, status in E9
|
// has_update(telegram->read_value(wWSelTemp_, 6)); // settings, status in E9
|
||||||
}
|
}
|
||||||
|
|
||||||
// 0xE9 - DHW Status
|
// 0xE9 - WW monitor ems+
|
||||||
// e.g. 08 00 E9 00 37 01 F6 01 ED 00 00 00 00 41 3C 00 00 00 00 00 00 00 00 00 00 00 00 37 00 00 00 (CRC=77) #data=27
|
// e.g. 08 00 E9 00 37 01 F6 01 ED 00 00 00 00 41 3C 00 00 00 00 00 00 00 00 00 00 00 00 37 00 00 00 (CRC=77) #data=27
|
||||||
void Boiler::process_UBADHWStatus(std::shared_ptr<const Telegram> telegram) {
|
void Boiler::process_UBAMonitorWWPlus(std::shared_ptr<const Telegram> telegram) {
|
||||||
has_update(telegram->read_value(wWSetTemp_, 0));
|
has_update(telegram->read_value(wWSetTemp_, 0));
|
||||||
has_update(telegram->read_value(wWCurTemp_, 1));
|
has_update(telegram->read_value(wWCurTemp_, 1));
|
||||||
has_update(telegram->read_value(wWCurTemp2_, 3));
|
has_update(telegram->read_value(wWCurTemp2_, 3));
|
||||||
@@ -556,7 +565,7 @@ void Boiler::process_UBAInformation(std::shared_ptr<const Telegram> telegram) {
|
|||||||
|
|
||||||
has_update(telegram->read_value(auxElecHeatNrgConsTotal_, 40));
|
has_update(telegram->read_value(auxElecHeatNrgConsTotal_, 40));
|
||||||
has_update(telegram->read_value(auxElecHeatNrgConsHeating_, 48));
|
has_update(telegram->read_value(auxElecHeatNrgConsHeating_, 48));
|
||||||
has_update(telegram->read_value(auxElecHeatNrgConsDHW_, 44));
|
has_update(telegram->read_value(auxElecHeatNrgConsWW_, 44));
|
||||||
|
|
||||||
has_update(telegram->read_value(nrgConsCompTotal_, 56));
|
has_update(telegram->read_value(nrgConsCompTotal_, 56));
|
||||||
has_update(telegram->read_value(nrgConsCompHeating_, 68));
|
has_update(telegram->read_value(nrgConsCompHeating_, 68));
|
||||||
@@ -578,10 +587,31 @@ void Boiler::process_UBAEnergySupplied(std::shared_ptr<const Telegram> telegram)
|
|||||||
has_update(telegram->read_value(nrgSuppCooling_, 16));
|
has_update(telegram->read_value(nrgSuppCooling_, 16));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 0x2A - MC10Status
|
// Heatpump power - type 0x48D
|
||||||
|
void Boiler::process_HpPower(std::shared_ptr<const Telegram> telegram){
|
||||||
|
has_update(telegram->read_value(hpPower_, 11));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Heatpump outdoor unit - type 0x48F
|
||||||
|
void Boiler::process_HpOutdoor(std::shared_ptr<const Telegram> telegram){
|
||||||
|
has_update(telegram->read_value(hpTc0_, 6));
|
||||||
|
has_update(telegram->read_value(hpTc1_, 4));
|
||||||
|
has_update(telegram->read_value(hpTc3_, 2));
|
||||||
|
has_update(telegram->read_value(hpTr3_, 16));
|
||||||
|
has_update(telegram->read_value(hpTr4_, 18));
|
||||||
|
has_update(telegram->read_value(hpTr5_, 20));
|
||||||
|
has_update(telegram->read_value(hpTr6_, 0));
|
||||||
|
has_update(telegram->read_value(hpTr7_, 30));
|
||||||
|
has_update(telegram->read_value(hpTl2_, 12));
|
||||||
|
has_update(telegram->read_value(hpPl1_, 26));
|
||||||
|
has_update(telegram->read_value(hpPh1_, 28));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 0x2A - MC110Status
|
||||||
// e.g. 88 00 2A 00 00 00 00 00 00 00 00 00 D2 00 00 80 00 00 01 08 80 00 02 47 00
|
// e.g. 88 00 2A 00 00 00 00 00 00 00 00 00 D2 00 00 80 00 00 01 08 80 00 02 47 00
|
||||||
// see https://github.com/emsesp/EMS-ESP/issues/397
|
// see https://github.com/emsesp/EMS-ESP/issues/397
|
||||||
void Boiler::process_MC10Status(std::shared_ptr<const Telegram> telegram) {
|
void Boiler::process_MC110Status(std::shared_ptr<const Telegram> telegram) {
|
||||||
has_update(telegram->read_value(mixerTemp_, 14));
|
has_update(telegram->read_value(mixerTemp_, 14));
|
||||||
has_update(telegram->read_value(tankMiddleTemp_, 18));
|
has_update(telegram->read_value(tankMiddleTemp_, 18));
|
||||||
}
|
}
|
||||||
@@ -595,8 +625,7 @@ void Boiler::process_UBAOutdoorTemp(std::shared_ptr<const Telegram> telegram) {
|
|||||||
|
|
||||||
// UBASetPoint 0x1A
|
// UBASetPoint 0x1A
|
||||||
void Boiler::process_UBASetPoints(std::shared_ptr<const Telegram> telegram) {
|
void Boiler::process_UBASetPoints(std::shared_ptr<const Telegram> telegram) {
|
||||||
has_update(telegram->read_value(setFlowTemp_,
|
has_update(telegram->read_value(setFlowTemp_, 0)); // boiler set temp from thermostat
|
||||||
0)); // boiler set temp from thermostat
|
|
||||||
has_update(telegram->read_value(setBurnPow_, 1)); // max json power in %
|
has_update(telegram->read_value(setBurnPow_, 1)); // max json power in %
|
||||||
has_update(telegram->read_value(wWSetPumpPower_, 2)); // ww pump speed/power?
|
has_update(telegram->read_value(wWSetPumpPower_, 2)); // ww pump speed/power?
|
||||||
}
|
}
|
||||||
@@ -615,6 +644,7 @@ void Boiler::process_CascadeMessage(std::shared_ptr<const Telegram> telegram) {
|
|||||||
// 0x35 - not yet implemented
|
// 0x35 - not yet implemented
|
||||||
void Boiler::process_UBAFlags(std::shared_ptr<const Telegram> telegram) {
|
void Boiler::process_UBAFlags(std::shared_ptr<const Telegram> telegram) {
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma GCC diagnostic pop
|
#pragma GCC diagnostic pop
|
||||||
|
|
||||||
// 0x1C
|
// 0x1C
|
||||||
@@ -696,6 +726,7 @@ bool Boiler::set_warmwater_temp(const char * value, const int8_t id) {
|
|||||||
if (get_toggle_fetch(EMS_TYPE_UBAParametersPlus)) {
|
if (get_toggle_fetch(EMS_TYPE_UBAParametersPlus)) {
|
||||||
write_command(EMS_TYPE_UBAParameterWWPlus, 6, v, EMS_TYPE_UBAParameterWWPlus);
|
write_command(EMS_TYPE_UBAParameterWWPlus, 6, v, EMS_TYPE_UBAParameterWWPlus);
|
||||||
} else {
|
} else {
|
||||||
|
// some boiler have it in 0x33, some in 0x35
|
||||||
write_command(EMS_TYPE_UBAFlags, 3, v, 0x34); // for i9000, see #397
|
write_command(EMS_TYPE_UBAFlags, 3, v, 0x34); // for i9000, see #397
|
||||||
write_command(EMS_TYPE_UBAParameterWW, 2, v, EMS_TYPE_UBAParameterWW); // read seltemp back
|
write_command(EMS_TYPE_UBAParameterWW, 2, v, EMS_TYPE_UBAParameterWW); // read seltemp back
|
||||||
}
|
}
|
||||||
@@ -703,6 +734,23 @@ bool Boiler::set_warmwater_temp(const char * value, const int8_t id) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set the warm water disinfection temperature
|
||||||
|
bool Boiler::set_disinfect_temp(const char * value, const int8_t id) {
|
||||||
|
int v = 0;
|
||||||
|
if (!Helpers::value2number(value, v)) {
|
||||||
|
LOG_WARNING(F("Set boiler warm water disinfect temperature: Invalid value"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_INFO(F("Setting boiler warm water disinfect temperature to %d C"), v);
|
||||||
|
if (get_toggle_fetch(EMS_TYPE_UBAParametersPlus)) {
|
||||||
|
write_command(EMS_TYPE_UBAParameterWWPlus, 12, v, EMS_TYPE_UBAParameterWWPlus);
|
||||||
|
} else {
|
||||||
|
write_command(EMS_TYPE_UBAParameterWW, 8, v, EMS_TYPE_UBAParameterWW);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
// flow temp
|
// flow temp
|
||||||
bool Boiler::set_flow_temp(const char * value, const int8_t id) {
|
bool Boiler::set_flow_temp(const char * value, const int8_t id) {
|
||||||
int v = 0;
|
int v = 0;
|
||||||
@@ -712,9 +760,21 @@ bool Boiler::set_flow_temp(const char * value, const int8_t id) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
LOG_INFO(F("Setting boiler flow temperature to %d C"), v);
|
LOG_INFO(F("Setting boiler flow temperature to %d C"), v);
|
||||||
// some boiler have it in 0x1A, some in 0x35, but both telegrams are sometimes writeonly
|
|
||||||
write_command(EMS_TYPE_UBASetPoints, 0, v, EMS_TYPE_UBASetPoints);
|
write_command(EMS_TYPE_UBASetPoints, 0, v, EMS_TYPE_UBASetPoints);
|
||||||
// write_command(0x35, 3, v, 0x35);
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// set selected burner power
|
||||||
|
bool Boiler::set_burn_power(const char * value, const int8_t id) {
|
||||||
|
int v = 0;
|
||||||
|
if (!Helpers::value2number(value, v)) {
|
||||||
|
LOG_WARNING(F("Set burner max. power: Invalid value"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_INFO(F("Setting burner max. power to %d %"), v);
|
||||||
|
write_command(EMS_TYPE_UBASetPoints, 1, v, EMS_TYPE_UBASetPoints);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -930,7 +990,7 @@ bool Boiler::set_pump_delay(const char * value, const int8_t id) {
|
|||||||
// on a RC35 it's by EMSESP::send_write_request(0x37, 0x10, 2, &set, 1, 0); (set is 1,2,3) 1=hot, 2=eco, 3=intelligent
|
// on a RC35 it's by EMSESP::send_write_request(0x37, 0x10, 2, &set, 1, 0); (set is 1,2,3) 1=hot, 2=eco, 3=intelligent
|
||||||
bool Boiler::set_warmwater_mode(const char * value, const int8_t id) {
|
bool Boiler::set_warmwater_mode(const char * value, const int8_t id) {
|
||||||
uint8_t set;
|
uint8_t set;
|
||||||
if (!Helpers::value2enum(value, set, {F("hot"), F("eco"), F("intelligent")})) {
|
if (!Helpers::value2enum(value, set, FL_(enum_comfort))) {
|
||||||
LOG_WARNING(F("Set boiler warm water mode: Invalid value"));
|
LOG_WARNING(F("Set boiler warm water mode: Invalid value"));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -1102,20 +1162,20 @@ bool Boiler::set_warmwater_circulation_mode(const char * value, const int8_t id)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Reset command
|
// Reset command
|
||||||
// 0 & 1 Reset-Mode (Manuel, others)
|
// 0 & 1 Reset-Mode (Manual, others)
|
||||||
// 8 reset maintenance message Hxx
|
// 8 reset maintenance message Hxx
|
||||||
// 12 & 13 Reset that Error-memory
|
// 12 & 13 Reset that Error-memory
|
||||||
bool Boiler::set_reset(const char * value, const int8_t id) {
|
bool Boiler::set_reset(const char * value, const int8_t id) {
|
||||||
std::string s(12, '\0');
|
uint8_t num;
|
||||||
if (!Helpers::value2string(value, s)) {
|
if (!Helpers::value2enum(value, num, FL_(enum_reset))) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (s == "maintenance") {
|
if (num == 0) {
|
||||||
LOG_INFO(F("Reset boiler maintenance message"));
|
LOG_INFO(F("Reset boiler maintenance message"));
|
||||||
write_command(0x05, 0x08, 0xFF, 0x1C);
|
write_command(0x05, 0x08, 0xFF, 0x1C);
|
||||||
return true;
|
return true;
|
||||||
} else if (s == "error") {
|
} else if (num == 1) {
|
||||||
LOG_INFO(F("Reset boiler error message"));
|
LOG_INFO(F("Reset boiler error message"));
|
||||||
write_command(0x05, 0x00, 0x5A); // error reset
|
write_command(0x05, 0x00, 0x5A); // error reset
|
||||||
return true;
|
return true;
|
||||||
@@ -1127,7 +1187,7 @@ bool Boiler::set_reset(const char * value, const int8_t id) {
|
|||||||
bool Boiler::set_maintenance(const char * value, const int8_t id) {
|
bool Boiler::set_maintenance(const char * value, const int8_t id) {
|
||||||
std::string s(12, '\0');
|
std::string s(12, '\0');
|
||||||
if (Helpers::value2string(value, s)) {
|
if (Helpers::value2string(value, s)) {
|
||||||
if (s == "reset") {
|
if (s == Helpers::toLower(uuid::read_flash_string(F_(reset)))) {
|
||||||
LOG_INFO(F("Reset boiler maintenance message"));
|
LOG_INFO(F("Reset boiler maintenance message"));
|
||||||
write_command(0x05, 0x08, 0xFF, 0x1C);
|
write_command(0x05, 0x08, 0xFF, 0x1C);
|
||||||
return true;
|
return true;
|
||||||
@@ -1160,7 +1220,7 @@ bool Boiler::set_maintenance(const char * value, const int8_t id) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
uint8_t num;
|
uint8_t num;
|
||||||
if (Helpers::value2enum(value, num, {F("off"), F("time"), F("date")})) {
|
if (Helpers::value2enum(value, num, FL_(enum_off_time_date))) {
|
||||||
LOG_INFO(F("Setting maintenance type to %s"), value);
|
LOG_INFO(F("Setting maintenance type to %s"), value);
|
||||||
write_command(0x15, 0, num, 0x15);
|
write_command(0x15, 0, num, 0x15);
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -32,6 +32,11 @@ class Boiler : public EMSdevice {
|
|||||||
private:
|
private:
|
||||||
static uuid::log::Logger logger_;
|
static uuid::log::Logger logger_;
|
||||||
|
|
||||||
|
// specific boiler characteristics, stripping the top 4 bits
|
||||||
|
inline uint8_t model() const {
|
||||||
|
return (flags() & 0x0F);
|
||||||
|
}
|
||||||
|
|
||||||
void check_active(const bool force = false);
|
void check_active(const bool force = false);
|
||||||
|
|
||||||
uint8_t boilerState_ = EMS_VALUE_UINT_NOTSET; // Boiler state flag - FOR INTERNAL USE
|
uint8_t boilerState_ = EMS_VALUE_UINT_NOTSET; // Boiler state flag - FOR INTERNAL USE
|
||||||
@@ -84,6 +89,8 @@ class Boiler : public EMSdevice {
|
|||||||
|
|
||||||
// main
|
// main
|
||||||
uint8_t id_; // product id
|
uint8_t id_; // product id
|
||||||
|
uint8_t dummy8u_; // for commands with no output
|
||||||
|
uint8_t dummybool_; // for commands with no output
|
||||||
uint8_t heatingActive_; // Central heating is on/off
|
uint8_t heatingActive_; // Central heating is on/off
|
||||||
uint8_t tapwaterActive_; // Hot tap water is on/off
|
uint8_t tapwaterActive_; // Hot tap water is on/off
|
||||||
uint8_t selFlowTemp_; // Selected flow temperature
|
uint8_t selFlowTemp_; // Selected flow temperature
|
||||||
@@ -141,12 +148,26 @@ class Boiler : public EMSdevice {
|
|||||||
uint32_t nrgSuppCooling_; // Energy supplied cooling
|
uint32_t nrgSuppCooling_; // Energy supplied cooling
|
||||||
uint32_t auxElecHeatNrgConsTotal_; // Auxiliary electrical heater energy consumption total
|
uint32_t auxElecHeatNrgConsTotal_; // Auxiliary electrical heater energy consumption total
|
||||||
uint32_t auxElecHeatNrgConsHeating_; // Auxiliary electrical heater energy consumption heating
|
uint32_t auxElecHeatNrgConsHeating_; // Auxiliary electrical heater energy consumption heating
|
||||||
uint32_t auxElecHeatNrgConsDHW_; // Auxiliary electrical heater energy consumption DHW
|
uint32_t auxElecHeatNrgConsWW_; // Auxiliary electrical heater energy consumption DHW
|
||||||
char maintenanceMessage_[4];
|
char maintenanceMessage_[4];
|
||||||
char maintenanceDate_[12];
|
char maintenanceDate_[12];
|
||||||
uint8_t maintenanceType_;
|
uint8_t maintenanceType_;
|
||||||
uint16_t maintenanceTime_;
|
uint16_t maintenanceTime_;
|
||||||
|
|
||||||
|
// heatpump
|
||||||
|
uint8_t hpPower_;
|
||||||
|
int16_t hpTc0_;
|
||||||
|
int16_t hpTc1_;
|
||||||
|
int16_t hpTc3_;
|
||||||
|
int16_t hpTr3_;
|
||||||
|
int16_t hpTr4_;
|
||||||
|
int16_t hpTr5_;
|
||||||
|
int16_t hpTr6_;
|
||||||
|
int16_t hpTr7_;
|
||||||
|
int16_t hpTl2_;
|
||||||
|
int16_t hpPl1_;
|
||||||
|
int16_t hpPh1_;
|
||||||
|
|
||||||
void process_UBAParameterWW(std::shared_ptr<const Telegram> telegram);
|
void process_UBAParameterWW(std::shared_ptr<const Telegram> telegram);
|
||||||
void process_UBAMonitorFast(std::shared_ptr<const Telegram> telegram);
|
void process_UBAMonitorFast(std::shared_ptr<const Telegram> telegram);
|
||||||
void process_UBATotalUptime(std::shared_ptr<const Telegram> telegram);
|
void process_UBATotalUptime(std::shared_ptr<const Telegram> telegram);
|
||||||
@@ -161,15 +182,17 @@ class Boiler : public EMSdevice {
|
|||||||
void process_UBAOutdoorTemp(std::shared_ptr<const Telegram> telegram);
|
void process_UBAOutdoorTemp(std::shared_ptr<const Telegram> telegram);
|
||||||
void process_UBASetPoints(std::shared_ptr<const Telegram> telegram);
|
void process_UBASetPoints(std::shared_ptr<const Telegram> telegram);
|
||||||
void process_UBAFlags(std::shared_ptr<const Telegram> telegram);
|
void process_UBAFlags(std::shared_ptr<const Telegram> telegram);
|
||||||
void process_MC10Status(std::shared_ptr<const Telegram> telegram);
|
void process_MC110Status(std::shared_ptr<const Telegram> telegram);
|
||||||
void process_UBAMaintenanceStatus(std::shared_ptr<const Telegram> telegram);
|
void process_UBAMaintenanceStatus(std::shared_ptr<const Telegram> telegram);
|
||||||
void process_UBAMaintenanceData(std::shared_ptr<const Telegram> telegram);
|
void process_UBAMaintenanceData(std::shared_ptr<const Telegram> telegram);
|
||||||
void process_UBAErrorMessage(std::shared_ptr<const Telegram> telegram);
|
void process_UBAErrorMessage(std::shared_ptr<const Telegram> telegram);
|
||||||
void process_UBADHWStatus(std::shared_ptr<const Telegram> telegram);
|
void process_UBAMonitorWWPlus(std::shared_ptr<const Telegram> telegram);
|
||||||
void process_UBAInformation(std::shared_ptr<const Telegram> telegram);
|
void process_UBAInformation(std::shared_ptr<const Telegram> telegram);
|
||||||
void process_UBAEnergySupplied(std::shared_ptr<const Telegram> telegram);
|
void process_UBAEnergySupplied(std::shared_ptr<const Telegram> telegram);
|
||||||
void process_CascadeMessage(std::shared_ptr<const Telegram> telegram);
|
void process_CascadeMessage(std::shared_ptr<const Telegram> telegram);
|
||||||
void process_UBASettingsWW(std::shared_ptr<const Telegram> telegram);
|
void process_UBASettingsWW(std::shared_ptr<const Telegram> telegram);
|
||||||
|
void process_HpPower(std::shared_ptr<const Telegram> telegram);
|
||||||
|
void process_HpOutdoor(std::shared_ptr<const Telegram> telegram);
|
||||||
|
|
||||||
// commands - none of these use the additional id parameter
|
// commands - none of these use the additional id parameter
|
||||||
bool set_warmwater_mode(const char * value, const int8_t id);
|
bool set_warmwater_mode(const char * value, const int8_t id);
|
||||||
@@ -180,9 +203,11 @@ class Boiler : public EMSdevice {
|
|||||||
bool set_warmwater_circulation_pump(const char * value, const int8_t id);
|
bool set_warmwater_circulation_pump(const char * value, const int8_t id);
|
||||||
bool set_warmwater_circulation_mode(const char * value, const int8_t id);
|
bool set_warmwater_circulation_mode(const char * value, const int8_t id);
|
||||||
bool set_warmwater_temp(const char * value, const int8_t id);
|
bool set_warmwater_temp(const char * value, const int8_t id);
|
||||||
|
bool set_disinfect_temp(const char * value, const int8_t id);
|
||||||
bool set_warmwater_maxpower(const char * value, const int8_t id);
|
bool set_warmwater_maxpower(const char * value, const int8_t id);
|
||||||
bool set_wWFlowTempOffset(const char * value, const int8_t id);
|
bool set_wWFlowTempOffset(const char * value, const int8_t id);
|
||||||
bool set_flow_temp(const char * value, const int8_t id);
|
bool set_flow_temp(const char * value, const int8_t id);
|
||||||
|
bool set_burn_power(const char * value, const int8_t id);
|
||||||
bool set_heating_activated(const char * value, const int8_t id);
|
bool set_heating_activated(const char * value, const int8_t id);
|
||||||
bool set_heating_temp(const char * value, const int8_t id);
|
bool set_heating_temp(const char * value, const int8_t id);
|
||||||
bool set_min_power(const char * value, const int8_t id);
|
bool set_min_power(const char * value, const int8_t id);
|
||||||
|
|||||||
@@ -33,9 +33,9 @@ Heatpump::Heatpump(uint8_t device_type, uint8_t device_id, uint8_t product_id, c
|
|||||||
register_telegram_type(0x047B, F("HP2"), true, MAKE_PF_CB(process_HPMonitor2));
|
register_telegram_type(0x047B, F("HP2"), true, MAKE_PF_CB(process_HPMonitor2));
|
||||||
|
|
||||||
// device values
|
// device values
|
||||||
register_device_value(TAG_NONE, &id_, DeviceValueType::UINT, nullptr, F("id"), nullptr); // empty full name to prevent being shown in web or console
|
register_device_value(TAG_NONE, &id_, DeviceValueType::UINT, nullptr, FL_(ID), DeviceValueUOM::NONE);
|
||||||
register_device_value(TAG_NONE, &airHumidity_, DeviceValueType::UINT, FL_(div2), F("airHumidity"), F("relative air humidity"));
|
register_device_value(TAG_NONE, &airHumidity_, DeviceValueType::UINT, FL_(div2), FL_(airHumidity), DeviceValueUOM::PERCENT);
|
||||||
register_device_value(TAG_NONE, &dewTemperature_, DeviceValueType::UINT, nullptr, F("dewTemperature"), F("dew point temperature"));
|
register_device_value(TAG_NONE, &dewTemperature_, DeviceValueType::UINT, nullptr, FL_(dewTemperature), DeviceValueUOM::DEGREES);
|
||||||
|
|
||||||
id_ = product_id;
|
id_ = product_id;
|
||||||
}
|
}
|
||||||
@@ -61,7 +61,7 @@ bool Heatpump::publish_ha_config() {
|
|||||||
ids.add("ems-esp-heatpump");
|
ids.add("ems-esp-heatpump");
|
||||||
|
|
||||||
char topic[Mqtt::MQTT_TOPIC_MAX_SIZE];
|
char topic[Mqtt::MQTT_TOPIC_MAX_SIZE];
|
||||||
snprintf_P(topic, sizeof(topic), PSTR("homeassistant/sensor/%s/heatpump/config"), Mqtt::base().c_str());
|
snprintf_P(topic, sizeof(topic), PSTR("sensor/%s/heatpump/config"), Mqtt::base().c_str());
|
||||||
Mqtt::publish_ha(topic, doc.as<JsonObject>()); // publish the config payload with retain flag
|
Mqtt::publish_ha(topic, doc.as<JsonObject>()); // publish the config payload with retain flag
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -57,20 +57,20 @@ Mixer::Mixer(uint8_t device_type, uint8_t device_id, uint8_t product_id, const s
|
|||||||
type_ = Type::HC;
|
type_ = Type::HC;
|
||||||
hc_ = device_id - 0x20 + 1;
|
hc_ = device_id - 0x20 + 1;
|
||||||
uint8_t tag = TAG_HC1 + hc_ - 1;
|
uint8_t tag = TAG_HC1 + hc_ - 1;
|
||||||
register_device_value(tag, &id_, DeviceValueType::UINT, nullptr, F("id"), nullptr); // empty full name to prevent being shown in web or console
|
register_device_value(tag, &id_, DeviceValueType::UINT, nullptr, FL_(ID), DeviceValueUOM::NONE);
|
||||||
register_device_value(tag, &flowSetTemp_, DeviceValueType::UINT, nullptr, F("flowSetTemp"), F("setpoint flow temperature"), DeviceValueUOM::DEGREES);
|
register_device_value(tag, &flowSetTemp_, DeviceValueType::UINT, nullptr, FL_(flowSetTemp), DeviceValueUOM::DEGREES);
|
||||||
register_device_value(tag, &flowTempHc_, DeviceValueType::USHORT, FL_(div10), F("flowTempHc"), F("flow temperature in assigned hc (TC1)"), DeviceValueUOM::DEGREES);
|
register_device_value(tag, &flowTempHc_, DeviceValueType::USHORT, FL_(div10), FL_(flowTempHc), DeviceValueUOM::DEGREES);
|
||||||
register_device_value(tag, &pumpStatus_, DeviceValueType::BOOL, nullptr, F("pumpStatus"), F("pump status in assigned hc (PC1)"), DeviceValueUOM::PUMP);
|
register_device_value(tag, &pumpStatus_, DeviceValueType::BOOL, nullptr, FL_(pumpStatus), DeviceValueUOM::PUMP);
|
||||||
register_device_value(tag, &status_, DeviceValueType::INT, nullptr, F("valveStatus"), F("mixing valve actuator in assigned hc (VC1)"), DeviceValueUOM::PERCENT);
|
register_device_value(tag, &status_, DeviceValueType::INT, nullptr, FL_(mixerStatus), DeviceValueUOM::PERCENT);
|
||||||
register_device_value(tag, &flowTempVf_, DeviceValueType::USHORT, FL_(div10), F("flowTempVf"), F("flow temperature in header (T0/Vf)"), DeviceValueUOM::DEGREES);
|
register_device_value(tag, &flowTempVf_, DeviceValueType::USHORT, FL_(div10), FL_(flowTempVf), DeviceValueUOM::DEGREES);
|
||||||
} else {
|
} else {
|
||||||
type_ = Type::WWC;
|
type_ = Type::WWC;
|
||||||
hc_ = device_id - 0x28 + 1;
|
hc_ = device_id - 0x28 + 1;
|
||||||
uint8_t tag = TAG_WWC1 + hc_ - 1;
|
uint8_t tag = TAG_WWC1 + hc_ - 1;
|
||||||
register_device_value(tag, &id_, DeviceValueType::UINT, nullptr, F("id"), nullptr); // empty full name to prevent being shown in web or console
|
register_device_value(tag, &id_, DeviceValueType::UINT, nullptr, FL_(ID), DeviceValueUOM::NONE);
|
||||||
register_device_value(tag, &flowTempHc_, DeviceValueType::USHORT, FL_(div10), F("wwTemp"), F("current warm water temperature"), DeviceValueUOM::DEGREES);
|
register_device_value(tag, &flowTempHc_, DeviceValueType::USHORT, FL_(div10), FL_(wwTemp), DeviceValueUOM::DEGREES);
|
||||||
register_device_value(tag, &pumpStatus_, DeviceValueType::BOOL, nullptr, F("pumpStatus"), F("pump status in assigned hc (PC1)"), DeviceValueUOM::PUMP);
|
register_device_value(tag, &pumpStatus_, DeviceValueType::BOOL, nullptr, FL_(wwPumpStatus), DeviceValueUOM::PUMP);
|
||||||
register_device_value(tag, &status_, DeviceValueType::INT, nullptr, F("tempStatus"), F("temperature switch in assigned hc (MC1)"));
|
register_device_value(tag, &status_, DeviceValueType::INT, nullptr, FL_(wwTempStatus), DeviceValueUOM::NONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
id_ = product_id;
|
id_ = product_id;
|
||||||
@@ -109,9 +109,9 @@ bool Mixer::publish_ha_config() {
|
|||||||
// determine the topic, if its HC and WWC. This is determined by the incoming telegram types.
|
// determine the topic, if its HC and WWC. This is determined by the incoming telegram types.
|
||||||
std::string topic(Mqtt::MQTT_TOPIC_MAX_SIZE, '\0');
|
std::string topic(Mqtt::MQTT_TOPIC_MAX_SIZE, '\0');
|
||||||
if (type_ == Type::HC) {
|
if (type_ == Type::HC) {
|
||||||
snprintf_P(&topic[0], topic.capacity() + 1, PSTR("homeassistant/sensor/%s/mixer_hc%d/config"), Mqtt::base().c_str(), hc_);
|
snprintf_P(&topic[0], topic.capacity() + 1, PSTR("sensor/%s/mixer_hc%d/config"), Mqtt::base().c_str(), hc_);
|
||||||
} else {
|
} else {
|
||||||
snprintf_P(&topic[0], topic.capacity() + 1, PSTR("homeassistant/sensor/%s/mixer_wwc%d/config"), Mqtt::base().c_str(), hc_); // WWC
|
snprintf_P(&topic[0], topic.capacity() + 1, PSTR("sensor/%s/mixer_wwc%d/config"), Mqtt::base().c_str(), hc_); // WWC
|
||||||
}
|
}
|
||||||
|
|
||||||
Mqtt::publish_ha(topic, doc.as<JsonObject>()); // publish the config payload with retain flag
|
Mqtt::publish_ha(topic, doc.as<JsonObject>()); // publish the config payload with retain flag
|
||||||
@@ -162,7 +162,6 @@ void Mixer::process_IPMStatusMessage(std::shared_ptr<const Telegram> telegram) {
|
|||||||
// Mixer IPM - 0x001E Temperature Message in unmixed circuits
|
// Mixer IPM - 0x001E Temperature Message in unmixed circuits
|
||||||
// in unmixed circuits FlowTemp in 10C is zero, this is the measured flowtemp in header
|
// in unmixed circuits FlowTemp in 10C is zero, this is the measured flowtemp in header
|
||||||
void Mixer::process_IPMTempMessage(std::shared_ptr<const Telegram> telegram) {
|
void Mixer::process_IPMTempMessage(std::shared_ptr<const Telegram> telegram) {
|
||||||
|
|
||||||
has_update(telegram->read_value(flowTempVf_, 0)); // TC1, is * 10
|
has_update(telegram->read_value(flowTempVf_, 0)); // TC1, is * 10
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -50,8 +50,6 @@ Solar::Solar(uint8_t device_type, uint8_t device_id, uint8_t product_id, const s
|
|||||||
register_telegram_type(0x0380, F("SM100CollectorConfig"), true, MAKE_PF_CB(process_SM100CollectorConfig));
|
register_telegram_type(0x0380, F("SM100CollectorConfig"), true, MAKE_PF_CB(process_SM100CollectorConfig));
|
||||||
register_telegram_type(0x038E, F("SM100Energy"), true, MAKE_PF_CB(process_SM100Energy));
|
register_telegram_type(0x038E, F("SM100Energy"), true, MAKE_PF_CB(process_SM100Energy));
|
||||||
register_telegram_type(0x0391, F("SM100Time"), true, MAKE_PF_CB(process_SM100Time));
|
register_telegram_type(0x0391, F("SM100Time"), true, MAKE_PF_CB(process_SM100Time));
|
||||||
|
|
||||||
register_mqtt_cmd(F("SM100TankBottomMaxTemp"), MAKE_CF_CB(set_SM100TankBottomMaxTemp));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,32 +62,39 @@ Solar::Solar(uint8_t device_type, uint8_t device_id, uint8_t product_id, const s
|
|||||||
|
|
||||||
// special case for a device_id with 0x2A where it's not actual a solar module
|
// special case for a device_id with 0x2A where it's not actual a solar module
|
||||||
if (device_id == 0x2A) {
|
if (device_id == 0x2A) {
|
||||||
register_device_value(TAG_NONE, &type_, DeviceValueType::TEXT, nullptr, F("type"), F("type"));
|
register_device_value(TAG_NONE, &type_, DeviceValueType::TEXT, nullptr, FL_(type), DeviceValueUOM::NONE);
|
||||||
strncpy(type_, "warm water circuit", sizeof(type_));
|
strlcpy(type_, "warm water circuit", sizeof(type_));
|
||||||
|
register_device_value(TAG_DEVICE_DATA_WW, &wwTemp_1_, DeviceValueType::UINT, nullptr, FL_(wwTemp1), DeviceValueUOM::DEGREES);
|
||||||
|
register_device_value(TAG_DEVICE_DATA_WW, &wwTemp_3_, DeviceValueType::UINT, nullptr, FL_(wwTemp3), DeviceValueUOM::DEGREES);
|
||||||
|
register_device_value(TAG_DEVICE_DATA_WW, &wwTemp_4_, DeviceValueType::UINT, nullptr, FL_(wwTemp4), DeviceValueUOM::DEGREES);
|
||||||
|
register_device_value(TAG_DEVICE_DATA_WW, &wwTemp_5_, DeviceValueType::UINT, nullptr, FL_(wwTemp5), DeviceValueUOM::DEGREES);
|
||||||
|
register_device_value(TAG_DEVICE_DATA_WW, &wwTemp_7_, DeviceValueType::UINT, nullptr, FL_(wwTemp7), DeviceValueUOM::DEGREES);
|
||||||
|
register_device_value(TAG_DEVICE_DATA_WW, &wwPump_, DeviceValueType::UINT, nullptr, FL_(wwPump), DeviceValueUOM::DEGREES);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
register_device_value(TAG_NONE, &id_, DeviceValueType::UINT, nullptr, F("id"), nullptr); // empty full name to prevent being shown in web or console
|
register_device_value(TAG_NONE, &id_, DeviceValueType::UINT, nullptr, FL_(ID), DeviceValueUOM::NONE);
|
||||||
id_ = product_id;
|
id_ = product_id;
|
||||||
|
|
||||||
register_device_value(TAG_NONE, &collectorTemp_, DeviceValueType::SHORT, FL_(div10), F("collectorTemp"), F("collector temperature (TS1)"), DeviceValueUOM::DEGREES);
|
register_device_value(TAG_NONE, &collectorTemp_, DeviceValueType::SHORT, FL_(div10), FL_(collectorTemp), DeviceValueUOM::DEGREES);
|
||||||
register_device_value(TAG_NONE, &tankBottomTemp_, DeviceValueType::SHORT, FL_(div10), F("tankBottomTemp"), F("tank bottom temperature (TS2)"), DeviceValueUOM::DEGREES);
|
register_device_value(TAG_NONE, &tankBottomTemp_, DeviceValueType::SHORT, FL_(div10), FL_(tankBottomTemp), DeviceValueUOM::DEGREES);
|
||||||
register_device_value(TAG_NONE, &tankBottomTemp2_, DeviceValueType::SHORT, FL_(div10), F("tank2BottomTemp"), F("second tank bottom temperature (TS5)"), DeviceValueUOM::DEGREES);
|
register_device_value(TAG_NONE, &tankBottomTemp2_, DeviceValueType::SHORT, FL_(div10), FL_(tank2BottomTemp), DeviceValueUOM::DEGREES);
|
||||||
register_device_value(TAG_NONE, &heatExchangerTemp_, DeviceValueType::SHORT, FL_(div10), F("heatExchangerTemp"), F("heat exchanger temperature (TS6)"), DeviceValueUOM::DEGREES);
|
register_device_value(TAG_NONE, &heatExchangerTemp_, DeviceValueType::SHORT, FL_(div10), FL_(heatExchangerTemp), DeviceValueUOM::DEGREES);
|
||||||
|
|
||||||
register_device_value(TAG_NONE, &tankBottomMaxTemp_, DeviceValueType::UINT, nullptr, F("tank1MaxTempCurrent"), F("maximum tank temperature"), DeviceValueUOM::DEGREES);
|
register_device_value(TAG_NONE, &tankBottomMaxTemp_, DeviceValueType::UINT, nullptr, FL_(tankMaxTemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_SM100TankBottomMaxTemp));
|
||||||
register_device_value(TAG_NONE, &solarPumpModulation_, DeviceValueType::UINT, nullptr, F("solarPumpModulation"), F("pump modulation (PS1)"), DeviceValueUOM::PERCENT);
|
register_device_value(TAG_NONE, &solarPumpModulation_, DeviceValueType::UINT, nullptr, FL_(solarPumpModulation), DeviceValueUOM::PERCENT);
|
||||||
register_device_value(TAG_NONE, &cylinderPumpModulation_, DeviceValueType::UINT, nullptr, F("cylinderPumpModulation"), F("cylinder pump modulation (PS5)"), DeviceValueUOM::PERCENT);
|
register_device_value(TAG_NONE, &cylinderPumpModulation_, DeviceValueType::UINT, nullptr, FL_(cylinderPumpModulation), DeviceValueUOM::PERCENT);
|
||||||
|
|
||||||
register_device_value(TAG_NONE, &solarPump_, DeviceValueType::BOOL, nullptr, F("solarPump"), F("pump (PS1)"), DeviceValueUOM::PUMP);
|
register_device_value(TAG_NONE, &solarPump_, DeviceValueType::BOOL, nullptr, FL_(solarPump), DeviceValueUOM::PUMP);
|
||||||
register_device_value(TAG_NONE, &valveStatus_, DeviceValueType::BOOL, nullptr, F("valveStatus"), F("valve status"));
|
register_device_value(TAG_NONE, &valveStatus_, DeviceValueType::BOOL, nullptr, FL_(valveStatus), DeviceValueUOM::NONE);
|
||||||
register_device_value(TAG_NONE, &tankHeated_, DeviceValueType::BOOL, nullptr, F("tankHeated"), F("tank heated"));
|
register_device_value(TAG_NONE, &tankHeated_, DeviceValueType::BOOL, nullptr, FL_(tankHeated), DeviceValueUOM::NONE);
|
||||||
register_device_value(TAG_NONE, &collectorShutdown_, DeviceValueType::BOOL, nullptr, F("collectorShutdown"), F("collector shutdown"));
|
register_device_value(TAG_NONE, &collectorShutdown_, DeviceValueType::BOOL, nullptr, FL_(collectorShutdown), DeviceValueUOM::NONE);
|
||||||
|
|
||||||
register_device_value(TAG_NONE, &pumpWorkTime_, DeviceValueType::TIME, nullptr, F("pumpWorkTime"), F("pump working time"), DeviceValueUOM::MINUTES);
|
register_device_value(TAG_NONE, &pumpWorkTime_, DeviceValueType::TIME, nullptr, FL_(pumpWorkTime), DeviceValueUOM::MINUTES);
|
||||||
|
|
||||||
register_device_value(TAG_NONE, &energyLastHour_, DeviceValueType::ULONG, FL_(div10), F("energyLastHour"), F("energy last hour"), DeviceValueUOM::WH);
|
register_device_value(TAG_NONE, &energyLastHour_, DeviceValueType::ULONG, FL_(div10), FL_(energyLastHour), DeviceValueUOM::WH);
|
||||||
register_device_value(TAG_NONE, &energyTotal_, DeviceValueType::ULONG, FL_(div10), F("energyTotal"), F("energy total"), DeviceValueUOM::KWH);
|
register_device_value(TAG_NONE, &energyTotal_, DeviceValueType::ULONG, FL_(div10), FL_(energyTotal), DeviceValueUOM::KWH);
|
||||||
register_device_value(TAG_NONE, &energyToday_, DeviceValueType::ULONG, nullptr, F("energyToday"), F("energy today"), DeviceValueUOM::WH);
|
register_device_value(TAG_NONE, &energyToday_, DeviceValueType::ULONG, nullptr, FL_(energyToday), DeviceValueUOM::WH);
|
||||||
}
|
}
|
||||||
|
|
||||||
// publish HA config
|
// publish HA config
|
||||||
@@ -112,7 +117,7 @@ bool Solar::publish_ha_config() {
|
|||||||
ids.add("ems-esp-solar");
|
ids.add("ems-esp-solar");
|
||||||
|
|
||||||
char topic[Mqtt::MQTT_TOPIC_MAX_SIZE];
|
char topic[Mqtt::MQTT_TOPIC_MAX_SIZE];
|
||||||
snprintf_P(topic, sizeof(topic), PSTR("homeassistant/sensor/%s/solar/config"), Mqtt::base().c_str());
|
snprintf_P(topic, sizeof(topic), PSTR("sensor/%s/solar/config"), Mqtt::base().c_str());
|
||||||
Mqtt::publish_ha(topic, doc.as<JsonObject>()); // publish the config payload with retain flag
|
Mqtt::publish_ha(topic, doc.as<JsonObject>()); // publish the config payload with retain flag
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -203,6 +208,22 @@ void Solar::process_SM100Monitor(std::shared_ptr<const Telegram> telegram) {
|
|||||||
has_update(telegram->read_value(heatExchangerTemp_, 20)); // is *10 - TS6: Heat exchanger temperature sensor
|
has_update(telegram->read_value(heatExchangerTemp_, 20)); // is *10 - TS6: Heat exchanger temperature sensor
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SM100wwTemperatur - 0x07D6
|
||||||
|
// Solar Module(0x2A) -> (0x00), (0x7D6), data: 01 C1 00 00 02 5B 01 AF 01 AD 80 00 01 90
|
||||||
|
void Solar::process_SM100wwTemperature(std::shared_ptr<const Telegram> telegram) {
|
||||||
|
has_update(telegram->read_value(wwTemp_1_, 0));
|
||||||
|
has_update(telegram->read_value(wwTemp_3_, 4));
|
||||||
|
has_update(telegram->read_value(wwTemp_4_, 6));
|
||||||
|
has_update(telegram->read_value(wwTemp_5_, 8));
|
||||||
|
has_update(telegram->read_value(wwTemp_7_, 12));
|
||||||
|
}
|
||||||
|
|
||||||
|
// SM100wwStatus - 0x07AA
|
||||||
|
// Solar Module(0x2A) -> (0x00), (0x7AA), data: 64 00 04 00 03 00 28 01 0F
|
||||||
|
void Solar::process_SM100wwStatus(std::shared_ptr<const Telegram> telegram) {
|
||||||
|
has_update(telegram->read_value(wwPump_, 0));
|
||||||
|
}
|
||||||
|
|
||||||
#pragma GCC diagnostic push
|
#pragma GCC diagnostic push
|
||||||
#pragma GCC diagnostic ignored "-Wunused-parameter"
|
#pragma GCC diagnostic ignored "-Wunused-parameter"
|
||||||
|
|
||||||
@@ -212,22 +233,6 @@ void Solar::process_SM100Monitor2(std::shared_ptr<const Telegram> telegram) {
|
|||||||
// not implemented yet
|
// not implemented yet
|
||||||
}
|
}
|
||||||
|
|
||||||
// SM100wwTemperatur - 0x07D6
|
|
||||||
// Solar Module(0x2A) -> (0x00), (0x7D6), data: 01 C1 00 00 02 5B 01 AF 01 AD 80 00 01 90
|
|
||||||
void Solar::process_SM100wwTemperature(std::shared_ptr<const Telegram> telegram) {
|
|
||||||
// has_update(telegram->read_value(wwTemp_1_, 0));
|
|
||||||
// has_update(telegram->read_value(wwTemp_3_, 4));
|
|
||||||
// has_update(telegram->read_value(wwTemp_4_, 6));
|
|
||||||
// has_update(telegram->read_value(wwTemp_5_, 8));
|
|
||||||
// has_update(telegram->read_value(wwTemp_7_, 12));
|
|
||||||
}
|
|
||||||
|
|
||||||
// SM100wwStatus - 0x07AA
|
|
||||||
// Solar Module(0x2A) -> (0x00), (0x7AA), data: 64 00 04 00 03 00 28 01 0F
|
|
||||||
void Solar::process_SM100wwStatus(std::shared_ptr<const Telegram> telegram) {
|
|
||||||
// has_update(telegram->read_value(wwPump_, 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
// SM100wwCommand - 0x07AB
|
// SM100wwCommand - 0x07AB
|
||||||
// Thermostat(0x10) -> Solar Module(0x2A), (0x7AB), data: 01 00 01
|
// Thermostat(0x10) -> Solar Module(0x2A), (0x7AB), data: 01 00 01
|
||||||
void Solar::process_SM100wwCommand(std::shared_ptr<const Telegram> telegram) {
|
void Solar::process_SM100wwCommand(std::shared_ptr<const Telegram> telegram) {
|
||||||
|
|||||||
@@ -76,6 +76,16 @@ class Solar : public EMSdevice {
|
|||||||
uint16_t collector1Area_; // Area of collector field 1
|
uint16_t collector1Area_; // Area of collector field 1
|
||||||
uint8_t collector1Type_; // Type of collector field 1, 01=flat, 02=vacuum
|
uint8_t collector1Type_; // Type of collector field 1, 01=flat, 02=vacuum
|
||||||
|
|
||||||
|
// SM100wwTemperature - 0x07D6
|
||||||
|
uint8_t wwTemp_1_;
|
||||||
|
uint8_t wwTemp_3_;
|
||||||
|
uint8_t wwTemp_4_;
|
||||||
|
uint8_t wwTemp_5_;
|
||||||
|
uint8_t wwTemp_7_;
|
||||||
|
|
||||||
|
// SM100wwStatus - 0x07AA
|
||||||
|
uint8_t wwPump_;
|
||||||
|
|
||||||
char type_[20]; // Solar of WWC
|
char type_[20]; // Solar of WWC
|
||||||
uint8_t id_;
|
uint8_t id_;
|
||||||
|
|
||||||
|
|||||||
@@ -34,11 +34,10 @@ Switch::Switch(uint8_t device_type, uint8_t device_id, uint8_t product_id, const
|
|||||||
register_telegram_type(0x9D, F("WM10SetMessage"), false, MAKE_PF_CB(process_WM10SetMessage));
|
register_telegram_type(0x9D, F("WM10SetMessage"), false, MAKE_PF_CB(process_WM10SetMessage));
|
||||||
register_telegram_type(0x1E, F("WM10TempMessage"), false, MAKE_PF_CB(process_WM10TempMessage));
|
register_telegram_type(0x1E, F("WM10TempMessage"), false, MAKE_PF_CB(process_WM10TempMessage));
|
||||||
|
|
||||||
register_device_value(TAG_NONE, &activated_, DeviceValueType::BOOL, nullptr, F("activated"), F("activated"));
|
register_device_value(TAG_NONE, &id_, DeviceValueType::UINT, nullptr, FL_(ID), DeviceValueUOM::NONE);
|
||||||
register_device_value(TAG_NONE, &flowTempHc_, DeviceValueType::USHORT, FL_(div10), F("flowTempHc"), F("flow temperature in assigned hc (TC1)"), DeviceValueUOM::DEGREES);
|
register_device_value(TAG_NONE, &activated_, DeviceValueType::BOOL, nullptr, FL_(activated), DeviceValueUOM::NONE);
|
||||||
register_device_value(TAG_NONE, &status_, DeviceValueType::INT, nullptr, F("status"), F("status"));
|
register_device_value(TAG_NONE, &flowTempHc_, DeviceValueType::USHORT, FL_(div10), FL_(flowTempHc), DeviceValueUOM::DEGREES);
|
||||||
|
register_device_value(TAG_NONE, &status_, DeviceValueType::INT, nullptr, FL_(status), DeviceValueUOM::NONE);
|
||||||
register_device_value(TAG_NONE, &id_, DeviceValueType::UINT, nullptr, F("id"), nullptr); // empty full name to prevent being shown in web or console
|
|
||||||
id_ = product_id;
|
id_ = product_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,7 +66,7 @@ bool Switch::publish_ha_config() {
|
|||||||
ids.add("ems-esp-switch");
|
ids.add("ems-esp-switch");
|
||||||
|
|
||||||
char topic[Mqtt::MQTT_TOPIC_MAX_SIZE];
|
char topic[Mqtt::MQTT_TOPIC_MAX_SIZE];
|
||||||
snprintf_P(topic, sizeof(topic), PSTR("homeassistant/sensor/%s/switch/config"), Mqtt::base().c_str());
|
snprintf_P(topic, sizeof(topic), PSTR("sensor/%s/switch/config"), Mqtt::base().c_str());
|
||||||
Mqtt::publish_ha(topic, doc.as<JsonObject>()); // publish the config payload with retain flag
|
Mqtt::publish_ha(topic, doc.as<JsonObject>()); // publish the config payload with retain flag
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ Thermostat::Thermostat(uint8_t device_type, uint8_t device_id, uint8_t product_i
|
|||||||
}
|
}
|
||||||
|
|
||||||
// RC35
|
// RC35
|
||||||
} else if ((model == EMSdevice::EMS_DEVICE_FLAG_RC35) || (model == EMSdevice::EMS_DEVICE_FLAG_RC30_1)) {
|
} else if ((model == EMSdevice::EMS_DEVICE_FLAG_RC35) || (model == EMSdevice::EMS_DEVICE_FLAG_RC30_N)) {
|
||||||
monitor_typeids = {0x3E, 0x48, 0x52, 0x5C};
|
monitor_typeids = {0x3E, 0x48, 0x52, 0x5C};
|
||||||
set_typeids = {0x3D, 0x47, 0x51, 0x5B};
|
set_typeids = {0x3D, 0x47, 0x51, 0x5B};
|
||||||
timer_typeids = {0x3F, 0x49, 0x53, 0x5D};
|
timer_typeids = {0x3F, 0x49, 0x53, 0x5D};
|
||||||
@@ -84,7 +84,7 @@ Thermostat::Thermostat(uint8_t device_type, uint8_t device_id, uint8_t product_i
|
|||||||
register_telegram_type(0xAF, F("RC20Remote"), false, MAKE_PF_CB(process_RC20Remote));
|
register_telegram_type(0xAF, F("RC20Remote"), false, MAKE_PF_CB(process_RC20Remote));
|
||||||
}
|
}
|
||||||
// RC20 newer
|
// RC20 newer
|
||||||
} else if (model == EMSdevice::EMS_DEVICE_FLAG_RC20_2) {
|
} else if (model == EMSdevice::EMS_DEVICE_FLAG_RC20_N) {
|
||||||
monitor_typeids = {0xAE};
|
monitor_typeids = {0xAE};
|
||||||
set_typeids = {0xAD};
|
set_typeids = {0xAD};
|
||||||
if (actual_master_thermostat == device_id) {
|
if (actual_master_thermostat == device_id) {
|
||||||
@@ -110,6 +110,13 @@ Thermostat::Thermostat(uint8_t device_type, uint8_t device_id, uint8_t product_i
|
|||||||
set_typeids = {};
|
set_typeids = {};
|
||||||
register_telegram_type(monitor_typeids[0], F("EasyMonitor"), true, MAKE_PF_CB(process_EasyMonitor));
|
register_telegram_type(monitor_typeids[0], F("EasyMonitor"), true, MAKE_PF_CB(process_EasyMonitor));
|
||||||
|
|
||||||
|
} else if (model == EMSdevice::EMS_DEVICE_FLAG_CRF) {
|
||||||
|
monitor_typeids = {0x02A5, 0x02A6, 0x02A7, 0x02A8};
|
||||||
|
set_typeids = {};
|
||||||
|
for (uint8_t i = 0; i < monitor_typeids.size(); i++) {
|
||||||
|
register_telegram_type(monitor_typeids[i], F("CRFMonitor"), true, MAKE_PF_CB(process_CRFMonitor));
|
||||||
|
}
|
||||||
|
|
||||||
// RC300/RC100
|
// RC300/RC100
|
||||||
} else if ((model == EMSdevice::EMS_DEVICE_FLAG_RC300) || (model == EMSdevice::EMS_DEVICE_FLAG_RC100)) {
|
} else if ((model == EMSdevice::EMS_DEVICE_FLAG_RC300) || (model == EMSdevice::EMS_DEVICE_FLAG_RC100)) {
|
||||||
monitor_typeids = {0x02A5, 0x02A6, 0x02A7, 0x02A8};
|
monitor_typeids = {0x02A5, 0x02A6, 0x02A7, 0x02A8};
|
||||||
@@ -167,8 +174,6 @@ Thermostat::Thermostat(uint8_t device_type, uint8_t device_id, uint8_t product_i
|
|||||||
// register device values for common values (not heating circuit)
|
// register device values for common values (not heating circuit)
|
||||||
register_device_values();
|
register_device_values();
|
||||||
|
|
||||||
add_commands();
|
|
||||||
|
|
||||||
// only for for the master-thermostat, go a query all the heating circuits. This is only done once.
|
// only for for the master-thermostat, go a query all the heating circuits. This is only done once.
|
||||||
// The automatic fetch will from now on only update the active heating circuits
|
// The automatic fetch will from now on only update the active heating circuits
|
||||||
for (uint8_t i = 0; i < monitor_typeids.size(); i++) {
|
for (uint8_t i = 0; i < monitor_typeids.size(); i++) {
|
||||||
@@ -182,7 +187,9 @@ Thermostat::Thermostat(uint8_t device_type, uint8_t device_id, uint8_t product_i
|
|||||||
EMSESP::send_read_request(set_typeids[i], device_id);
|
EMSESP::send_read_request(set_typeids[i], device_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
EMSESP::send_read_request(EMS_TYPE_RCTime, device_id);
|
||||||
EMSESP::send_read_request(0x12, device_id); // read last error (only published on errors)
|
EMSESP::send_read_request(0x12, device_id); // read last error (only published on errors)
|
||||||
|
EMSESP::send_read_request(0xA2, device_id); // read errorCode (only published on errors)
|
||||||
}
|
}
|
||||||
|
|
||||||
// publish HA config
|
// publish HA config
|
||||||
@@ -205,7 +212,7 @@ bool Thermostat::publish_ha_config() {
|
|||||||
ids.add("ems-esp-thermostat");
|
ids.add("ems-esp-thermostat");
|
||||||
|
|
||||||
char topic[Mqtt::MQTT_TOPIC_MAX_SIZE];
|
char topic[Mqtt::MQTT_TOPIC_MAX_SIZE];
|
||||||
snprintf_P(topic, sizeof(topic), PSTR("homeassistant/sensor/%s/thermostat/config"), Mqtt::base().c_str());
|
snprintf_P(topic, sizeof(topic), PSTR("sensor/%s/thermostat/config"), Mqtt::base().c_str());
|
||||||
Mqtt::publish_ha(topic, doc.as<JsonObject>()); // publish the config payload with retain flag
|
Mqtt::publish_ha(topic, doc.as<JsonObject>()); // publish the config payload with retain flag
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -291,7 +298,8 @@ std::shared_ptr<Thermostat::HeatingCircuit> Thermostat::heating_circuit(std::sha
|
|||||||
|
|
||||||
// not found, search device-id types for remote thermostats
|
// not found, search device-id types for remote thermostats
|
||||||
if (telegram->src >= 0x18 && telegram->src <= 0x1B) {
|
if (telegram->src >= 0x18 && telegram->src <= 0x1B) {
|
||||||
hc_num = telegram->src - 0x17;
|
hc_num = telegram->src - 0x17;
|
||||||
|
toggle_ = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// still didn't recognize it, ignore it
|
// still didn't recognize it, ignore it
|
||||||
@@ -306,12 +314,10 @@ std::shared_ptr<Thermostat::HeatingCircuit> Thermostat::heating_circuit(std::sha
|
|||||||
return heating_circuit;
|
return heating_circuit;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// register new heatingcircuits only on active monitor telegrams
|
// register new heatingcircuits only on active monitor telegrams
|
||||||
if (!toggle_) {
|
if (!toggle_) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* at this point we have discovered a new heating circuit
|
* at this point we have discovered a new heating circuit
|
||||||
*/
|
*/
|
||||||
@@ -378,7 +384,7 @@ void Thermostat::register_mqtt_ha_config_hc(uint8_t hc_num) {
|
|||||||
doc["~"] = Mqtt::base(); // ems-esp
|
doc["~"] = Mqtt::base(); // ems-esp
|
||||||
|
|
||||||
char topic_t[Mqtt::MQTT_TOPIC_MAX_SIZE];
|
char topic_t[Mqtt::MQTT_TOPIC_MAX_SIZE];
|
||||||
if (Mqtt::nested_format()) {
|
if (Mqtt::nested_format() == 1) {
|
||||||
snprintf_P(topic_t, sizeof(topic_t), PSTR("~/%s"), Mqtt::tag_to_topic(EMSdevice::DeviceType::THERMOSTAT, DeviceValueTAG::TAG_NONE).c_str());
|
snprintf_P(topic_t, sizeof(topic_t), PSTR("~/%s"), Mqtt::tag_to_topic(EMSdevice::DeviceType::THERMOSTAT, DeviceValueTAG::TAG_NONE).c_str());
|
||||||
|
|
||||||
char mode_str_tpl[40];
|
char mode_str_tpl[40];
|
||||||
@@ -424,18 +430,13 @@ void Thermostat::register_mqtt_ha_config_hc(uint8_t hc_num) {
|
|||||||
ids.add("ems-esp-thermostat");
|
ids.add("ems-esp-thermostat");
|
||||||
|
|
||||||
char topic[Mqtt::MQTT_TOPIC_MAX_SIZE];
|
char topic[Mqtt::MQTT_TOPIC_MAX_SIZE];
|
||||||
snprintf_P(topic, sizeof(topic), PSTR("homeassistant/climate/%s/thermostat_hc%d/config"), Mqtt::base().c_str(), hc_num);
|
snprintf_P(topic, sizeof(topic), PSTR("climate/%s/thermostat_hc%d/config"), Mqtt::base().c_str(), hc_num);
|
||||||
Mqtt::publish_ha(topic, doc.as<JsonObject>()); // publish the config payload with retain flag
|
Mqtt::publish_ha(topic, doc.as<JsonObject>()); // publish the config payload with retain flag
|
||||||
|
|
||||||
// enable the a special "thermostat_hc<n>" topic to take both mode strings and floats for each of the heating circuits
|
// enable the a special "thermostat_hc<n>" topic to take both mode strings and floats for each of the heating circuits
|
||||||
std::string topic2(Mqtt::MQTT_TOPIC_MAX_SIZE, '\0');
|
std::string topic2(Mqtt::MQTT_TOPIC_MAX_SIZE, '\0');
|
||||||
snprintf_P(&topic2[0], topic2.capacity() + 1, PSTR("thermostat_hc%d"), hc_num);
|
snprintf_P(&topic2[0], topic2.capacity() + 1, PSTR("thermostat_hc%d"), hc_num);
|
||||||
register_mqtt_topic(topic2, [=](const char * m) { return thermostat_ha_cmd(m, hc_num); });
|
register_mqtt_topic(topic2, [=](const char * m) { return thermostat_ha_cmd(m, hc_num); });
|
||||||
|
|
||||||
char hc_name[10]; // hc{1-4}
|
|
||||||
strlcpy(hc_name, "hc", 10);
|
|
||||||
char s[3];
|
|
||||||
strlcat(hc_name, Helpers::itoa(s, hc_num), 10);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// for HA specifically when receiving over MQTT in the thermostat topic
|
// for HA specifically when receiving over MQTT in the thermostat topic
|
||||||
@@ -475,6 +476,12 @@ uint8_t Thermostat::HeatingCircuit::get_mode() const {
|
|||||||
} else if (mode == 2) {
|
} else if (mode == 2) {
|
||||||
return HeatingCircuit::Mode::AUTO;
|
return HeatingCircuit::Mode::AUTO;
|
||||||
}
|
}
|
||||||
|
} else if (model == EMSdevice::EMS_DEVICE_FLAG_CRF) {
|
||||||
|
if (mode == 0) {
|
||||||
|
return HeatingCircuit::Mode::AUTO;
|
||||||
|
} else if (mode == 1) {
|
||||||
|
return HeatingCircuit::Mode::OFF;
|
||||||
|
}
|
||||||
} else if ((model == EMSdevice::EMS_DEVICE_FLAG_RC300) || (model == EMSdevice::EMS_DEVICE_FLAG_RC100)) {
|
} else if ((model == EMSdevice::EMS_DEVICE_FLAG_RC300) || (model == EMSdevice::EMS_DEVICE_FLAG_RC100)) {
|
||||||
if (mode == 0) {
|
if (mode == 0) {
|
||||||
return HeatingCircuit::Mode::MANUAL;
|
return HeatingCircuit::Mode::MANUAL;
|
||||||
@@ -515,12 +522,18 @@ uint8_t Thermostat::HeatingCircuit::get_mode_type() const {
|
|||||||
} else if (modetype == 1) {
|
} else if (modetype == 1) {
|
||||||
return HeatingCircuit::Mode::NOFROST;
|
return HeatingCircuit::Mode::NOFROST;
|
||||||
}
|
}
|
||||||
} else if ((model == EMS_DEVICE_FLAG_RC35) || (model == EMS_DEVICE_FLAG_RC30_1)) {
|
} else if ((model == EMS_DEVICE_FLAG_RC35) || (model == EMS_DEVICE_FLAG_RC30_N)) {
|
||||||
if (modetype == 0) {
|
if (modetype == 0) {
|
||||||
return HeatingCircuit::Mode::NIGHT;
|
return HeatingCircuit::Mode::NIGHT;
|
||||||
} else if (modetype == 1) {
|
} else if (modetype == 1) {
|
||||||
return HeatingCircuit::Mode::DAY;
|
return HeatingCircuit::Mode::DAY;
|
||||||
}
|
}
|
||||||
|
} else if (model == EMS_DEVICE_FLAG_CRF) {
|
||||||
|
if (modetype == 0) {
|
||||||
|
return HeatingCircuit::Mode::OFF;
|
||||||
|
} else if (modetype == 1) {
|
||||||
|
return HeatingCircuit::Mode::ON;
|
||||||
|
}
|
||||||
} else if (model == EMS_DEVICE_FLAG_RC300) {
|
} else if (model == EMS_DEVICE_FLAG_RC300) {
|
||||||
if (modetype == 0) {
|
if (modetype == 0) {
|
||||||
return HeatingCircuit::Mode::ECO;
|
return HeatingCircuit::Mode::ECO;
|
||||||
@@ -539,59 +552,65 @@ uint8_t Thermostat::HeatingCircuit::get_mode_type() const {
|
|||||||
std::string Thermostat::mode_tostring(uint8_t mode) {
|
std::string Thermostat::mode_tostring(uint8_t mode) {
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
case HeatingCircuit::Mode::OFF:
|
case HeatingCircuit::Mode::OFF:
|
||||||
return read_flash_string(F("off"));
|
return read_flash_string(F_(off));
|
||||||
break;
|
break;
|
||||||
case HeatingCircuit::Mode::MANUAL:
|
case HeatingCircuit::Mode::MANUAL:
|
||||||
return read_flash_string(F("manual"));
|
return read_flash_string(F_(manual));
|
||||||
break;
|
break;
|
||||||
case HeatingCircuit::Mode::DAY:
|
case HeatingCircuit::Mode::DAY:
|
||||||
return read_flash_string(F("day"));
|
return read_flash_string(F_(day));
|
||||||
break;
|
break;
|
||||||
case HeatingCircuit::Mode::NIGHT:
|
case HeatingCircuit::Mode::NIGHT:
|
||||||
return read_flash_string(F("night"));
|
return read_flash_string(F_(night));
|
||||||
break;
|
break;
|
||||||
case HeatingCircuit::Mode::ECO:
|
case HeatingCircuit::Mode::ECO:
|
||||||
return read_flash_string(F("eco"));
|
return read_flash_string(F_(eco));
|
||||||
break;
|
break;
|
||||||
case HeatingCircuit::Mode::COMFORT:
|
case HeatingCircuit::Mode::COMFORT:
|
||||||
return read_flash_string(F("comfort"));
|
return read_flash_string(F_(comfort));
|
||||||
break;
|
break;
|
||||||
case HeatingCircuit::Mode::HEAT:
|
case HeatingCircuit::Mode::HEAT:
|
||||||
return read_flash_string(F("heat"));
|
return read_flash_string(F_(heat));
|
||||||
break;
|
break;
|
||||||
case HeatingCircuit::Mode::HOLIDAY:
|
case HeatingCircuit::Mode::HOLIDAY:
|
||||||
return read_flash_string(F("holiday"));
|
return read_flash_string(F_(holiday));
|
||||||
break;
|
break;
|
||||||
case HeatingCircuit::Mode::NOFROST:
|
case HeatingCircuit::Mode::NOFROST:
|
||||||
return read_flash_string(F("nofrost"));
|
return read_flash_string(F_(nofrost));
|
||||||
break;
|
break;
|
||||||
case HeatingCircuit::Mode::AUTO:
|
case HeatingCircuit::Mode::AUTO:
|
||||||
return read_flash_string(F("auto"));
|
return read_flash_string(F_(auto));
|
||||||
break;
|
break;
|
||||||
case HeatingCircuit::Mode::SUMMER:
|
case HeatingCircuit::Mode::SUMMER:
|
||||||
return read_flash_string(F("summer"));
|
return read_flash_string(F_(summer));
|
||||||
break;
|
break;
|
||||||
case HeatingCircuit::Mode::OFFSET:
|
case HeatingCircuit::Mode::OFFSET:
|
||||||
return read_flash_string(F("offset"));
|
return read_flash_string(F_(offset));
|
||||||
break;
|
break;
|
||||||
case HeatingCircuit::Mode::DESIGN:
|
case HeatingCircuit::Mode::DESIGN:
|
||||||
return read_flash_string(F("design"));
|
return read_flash_string(F_(design));
|
||||||
break;
|
break;
|
||||||
case HeatingCircuit::Mode::MINFLOW:
|
case HeatingCircuit::Mode::MINFLOW:
|
||||||
return read_flash_string(F("minflow"));
|
return read_flash_string(F_(minflow));
|
||||||
break;
|
break;
|
||||||
case HeatingCircuit::Mode::MAXFLOW:
|
case HeatingCircuit::Mode::MAXFLOW:
|
||||||
return read_flash_string(F("maxflow"));
|
return read_flash_string(F_(maxflow));
|
||||||
break;
|
break;
|
||||||
case HeatingCircuit::Mode::ROOMINFLUENCE:
|
case HeatingCircuit::Mode::ROOMINFLUENCE:
|
||||||
return read_flash_string(F("roominfluence"));
|
return read_flash_string(F_(roominfluence[0]));
|
||||||
break;
|
break;
|
||||||
case HeatingCircuit::Mode::FLOWOFFSET:
|
case HeatingCircuit::Mode::FLOWOFFSET:
|
||||||
return read_flash_string(F("flowtempoffset"));
|
return read_flash_string(F_(flowtempoffset[0]));
|
||||||
|
break;
|
||||||
|
case HeatingCircuit::Mode::TEMPAUTO:
|
||||||
|
return read_flash_string(F_(tempauto));
|
||||||
|
break;
|
||||||
|
case HeatingCircuit::Mode::NOREDUCE:
|
||||||
|
return read_flash_string(F_(noreduce));
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
case HeatingCircuit::Mode::UNKNOWN:
|
case HeatingCircuit::Mode::UNKNOWN:
|
||||||
return read_flash_string(F("unknown"));
|
return read_flash_string(F_(unknown));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -751,6 +770,19 @@ void Thermostat::process_JunkersMonitor(std::shared_ptr<const Telegram> telegram
|
|||||||
has_update(telegram->read_value(hc->mode, 1)); // 1 = manual, 2 = auto
|
has_update(telegram->read_value(hc->mode, 1)); // 1 = manual, 2 = auto
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// type 0x02A5 - data from Worchester CRF200
|
||||||
|
void Thermostat::process_CRFMonitor(std::shared_ptr<const Telegram> telegram) {
|
||||||
|
std::shared_ptr<Thermostat::HeatingCircuit> hc = heating_circuit(telegram);
|
||||||
|
if (hc == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
has_update(telegram->read_value(hc->curr_roomTemp, 0)); // is * 10
|
||||||
|
has_update(telegram->read_bitvalue(hc->modetype, 2, 0));
|
||||||
|
has_update(telegram->read_bitvalue(hc->mode, 2, 4)); // bit 4, mode (auto=0 or off=1)
|
||||||
|
has_update(telegram->read_value(hc->setpoint_roomTemp, 6, 1)); // is * 2, force as single byte
|
||||||
|
has_update(telegram->read_value(hc->targetflowtemp, 4));
|
||||||
|
}
|
||||||
|
|
||||||
// type 0x02A5 - data from the Nefit RC1010/3000 thermostat (0x18) and RC300/310s on 0x10
|
// type 0x02A5 - data from the Nefit RC1010/3000 thermostat (0x18) and RC300/310s on 0x10
|
||||||
void Thermostat::process_RC300Monitor(std::shared_ptr<const Telegram> telegram) {
|
void Thermostat::process_RC300Monitor(std::shared_ptr<const Telegram> telegram) {
|
||||||
std::shared_ptr<Thermostat::HeatingCircuit> hc = heating_circuit(telegram);
|
std::shared_ptr<Thermostat::HeatingCircuit> hc = heating_circuit(telegram);
|
||||||
@@ -792,8 +824,9 @@ void Thermostat::process_RC300Set(std::shared_ptr<const Telegram> telegram) {
|
|||||||
|
|
||||||
// check why mode is both in the Monitor and Set for the RC300. It'll be read twice!
|
// check why mode is both in the Monitor and Set for the RC300. It'll be read twice!
|
||||||
// has_update(telegram->read_value(hc->mode, 0); // Auto = xFF, Manual = x00 eg. 10 00 FF 08 01 B9 FF
|
// has_update(telegram->read_value(hc->mode, 0); // Auto = xFF, Manual = x00 eg. 10 00 FF 08 01 B9 FF
|
||||||
has_update(telegram->read_value(hc->daytemp, 2)); // is * 2
|
has_update(telegram->read_value(hc->daytemp, 2)); // is * 2
|
||||||
has_update(telegram->read_value(hc->nighttemp, 4)); // is * 2
|
has_update(telegram->read_value(hc->nighttemp, 4)); // is * 2
|
||||||
|
has_update(telegram->read_value(hc->tempautotemp, 8));
|
||||||
has_update(telegram->read_value(hc->manualtemp, 10)); // is * 2
|
has_update(telegram->read_value(hc->manualtemp, 10)); // is * 2
|
||||||
has_update(telegram->read_value(hc->program, 11)); // timer program 1 or 2
|
has_update(telegram->read_value(hc->program, 11)); // timer program 1 or 2
|
||||||
}
|
}
|
||||||
@@ -957,8 +990,10 @@ void Thermostat::process_RC35Set(std::shared_ptr<const Telegram> telegram) {
|
|||||||
has_update(telegram->read_value(hc->nofrosttemp, 23)); // is * 1
|
has_update(telegram->read_value(hc->nofrosttemp, 23)); // is * 1
|
||||||
has_update(telegram->read_value(hc->flowtempoffset, 24)); // is * 1, only in mixed circuits
|
has_update(telegram->read_value(hc->flowtempoffset, 24)); // is * 1, only in mixed circuits
|
||||||
has_update(telegram->read_value(hc->reducemode, 25)); // 0-nofrost, 1-reduce, 2-roomhold, 3-outdoorhold
|
has_update(telegram->read_value(hc->reducemode, 25)); // 0-nofrost, 1-reduce, 2-roomhold, 3-outdoorhold
|
||||||
|
has_update(telegram->read_value(hc->control, 26)); // 0-off, 1-RC20 (remote), 2-RC35
|
||||||
has_update(telegram->read_value(hc->controlmode, 33)); // 0-outdoortemp, 1-roomtemp
|
has_update(telegram->read_value(hc->controlmode, 33)); // 0-outdoortemp, 1-roomtemp
|
||||||
// has_update(telegram->read_value(hc->noreducetemp, 38)); // outdoor temperature for no reduce
|
has_update(telegram->read_value(hc->tempautotemp, 37));
|
||||||
|
has_update(telegram->read_value(hc->noreducetemp, 38)); // outdoor temperature for no reduce
|
||||||
has_update(telegram->read_value(hc->minflowtemp, 16));
|
has_update(telegram->read_value(hc->minflowtemp, 16));
|
||||||
if (hc->heatingtype == 3) {
|
if (hc->heatingtype == 3) {
|
||||||
has_update(telegram->read_value(hc->designtemp, 36)); // is * 1
|
has_update(telegram->read_value(hc->designtemp, 36)); // is * 1
|
||||||
@@ -977,6 +1012,8 @@ void Thermostat::process_RC35Timer(std::shared_ptr<const Telegram> telegram) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
has_update(telegram->read_value(hc->program, 84)); // 0 .. 10, 0-userprogram 1, 10-userprogram 2
|
has_update(telegram->read_value(hc->program, 84)); // 0 .. 10, 0-userprogram 1, 10-userprogram 2
|
||||||
|
has_update(telegram->read_value(hc->pause, 85)); // time in hours
|
||||||
|
has_update(telegram->read_value(hc->party, 86)); // time in hours
|
||||||
}
|
}
|
||||||
|
|
||||||
// process_RCTime - type 0x06 - date and time from a thermostat - 14 bytes long
|
// process_RCTime - type 0x06 - date and time from a thermostat - 14 bytes long
|
||||||
@@ -1135,10 +1172,11 @@ bool Thermostat::set_remotetemp(const char * value, const int8_t id) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (f > 100 || f < 0) {
|
if (f > 100 || f < 0) {
|
||||||
Roomctrl::set_remotetemp(hc->hc_num() - 1, EMS_VALUE_SHORT_NOTSET);
|
hc->remotetemp = EMS_VALUE_SHORT_NOTSET;
|
||||||
} else {
|
} else {
|
||||||
Roomctrl::set_remotetemp(hc->hc_num() - 1, (int16_t)(f * 10));
|
hc->remotetemp = (int16_t)(f * 10);
|
||||||
}
|
}
|
||||||
|
Roomctrl::set_remotetemp(hc->hc_num() - 1, hc->remotetemp);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -1146,26 +1184,27 @@ bool Thermostat::set_remotetemp(const char * value, const int8_t id) {
|
|||||||
// 0xA5 - Set the building settings
|
// 0xA5 - Set the building settings
|
||||||
bool Thermostat::set_building(const char * value, const int8_t id) {
|
bool Thermostat::set_building(const char * value, const int8_t id) {
|
||||||
uint8_t bd = 0;
|
uint8_t bd = 0;
|
||||||
if (!Helpers::value2enum(value, bd, {F("light"), F("medium"), F("heavy")})) {
|
|
||||||
LOG_WARNING(F("Set building: Invalid value"));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
LOG_INFO(F("Setting building to %s"), value);
|
|
||||||
|
|
||||||
if ((model() == EMS_DEVICE_FLAG_RC300) || (model() == EMS_DEVICE_FLAG_RC100)) {
|
if ((model() == EMS_DEVICE_FLAG_RC300) || (model() == EMS_DEVICE_FLAG_RC100)) {
|
||||||
write_command(0x240, 9, bd + 1, 0x240);
|
if (Helpers::value2enum(value, bd, FL_(enum_ibaBuildingType))) {
|
||||||
|
LOG_INFO(F("Setting building to %s"), value);
|
||||||
|
write_command(0x240, 9, bd, 0x240);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
write_command(EMS_TYPE_IBASettings, 6, bd, EMS_TYPE_IBASettings);
|
if (Helpers::value2enum(value, bd, FL_(enum_ibaBuildingType2))) {
|
||||||
|
LOG_INFO(F("Setting building to %s"), value);
|
||||||
|
write_command(EMS_TYPE_IBASettings, 6, bd, EMS_TYPE_IBASettings);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
LOG_WARNING(F("Set building: Invalid value"));
|
||||||
return true;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 0xA5 Set the language settings
|
// 0xA5 Set the language settings
|
||||||
bool Thermostat::set_language(const char * value, const int8_t id) {
|
bool Thermostat::set_language(const char * value, const int8_t id) {
|
||||||
uint8_t lg = 0;
|
uint8_t lg = 0;
|
||||||
if (!Helpers::value2enum(value, lg, {F("german"), F("dutch"), F("french"), F("italian")})) {
|
if (!Helpers::value2enum(value, lg, FL_(enum_ibaLanguage))) {
|
||||||
LOG_WARNING(F("Set language: Invalid value"));
|
LOG_WARNING(F("Set language: Invalid value"));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -1179,7 +1218,7 @@ bool Thermostat::set_language(const char * value, const int8_t id) {
|
|||||||
// Set the control-mode for hc 0-off, 1-RC20, 2-RC3x
|
// Set the control-mode for hc 0-off, 1-RC20, 2-RC3x
|
||||||
bool Thermostat::set_control(const char * value, const int8_t id) {
|
bool Thermostat::set_control(const char * value, const int8_t id) {
|
||||||
uint8_t ctrl = 0;
|
uint8_t ctrl = 0;
|
||||||
if (!Helpers::value2enum(value, ctrl, {F("off"), F("rc20"), F("rc3x")})) {
|
if (!Helpers::value2enum(value, ctrl, FL_(enum_control))) {
|
||||||
LOG_WARNING(F("Set control: Invalid value"));
|
LOG_WARNING(F("Set control: Invalid value"));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -1201,14 +1240,14 @@ bool Thermostat::set_wwmode(const char * value, const int8_t id) {
|
|||||||
uint8_t set = 0xFF;
|
uint8_t set = 0xFF;
|
||||||
|
|
||||||
if ((model() == EMS_DEVICE_FLAG_RC300) || (model() == EMS_DEVICE_FLAG_RC100)) {
|
if ((model() == EMS_DEVICE_FLAG_RC300) || (model() == EMS_DEVICE_FLAG_RC100)) {
|
||||||
if (!Helpers::value2enum(value, set, {F("off"), F("low"), F("high"), F("auto"), F("own")})) {
|
if (!Helpers::value2enum(value, set, FL_(enum_wwMode))) {
|
||||||
LOG_WARNING(F("Set warm water mode: Invalid mode"));
|
LOG_WARNING(F("Set warm water mode: Invalid mode"));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
LOG_INFO(F("Setting warm water mode to %s"), value);
|
LOG_INFO(F("Setting warm water mode to %s"), value);
|
||||||
write_command(0x02F5, 2, set, 0x02F5);
|
write_command(0x02F5, 2, set, 0x02F5);
|
||||||
} else {
|
} else {
|
||||||
if (!Helpers::value2enum(value, set, {F("off"), F("on"), F("auto")})) {
|
if (!Helpers::value2enum(value, set, FL_(enum_wwMode2))) {
|
||||||
LOG_WARNING(F("Set warm water mode: Invalid mode"));
|
LOG_WARNING(F("Set warm water mode: Invalid mode"));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -1260,7 +1299,7 @@ bool Thermostat::set_wwcircmode(const char * value, const int8_t id) {
|
|||||||
uint8_t set = 0xFF;
|
uint8_t set = 0xFF;
|
||||||
|
|
||||||
if ((model() == EMS_DEVICE_FLAG_RC300) || (model() == EMS_DEVICE_FLAG_RC100)) {
|
if ((model() == EMS_DEVICE_FLAG_RC300) || (model() == EMS_DEVICE_FLAG_RC100)) {
|
||||||
if (!Helpers::value2enum(value, set, {F("off"), F("on"), F("auto"), F("own")})) {
|
if (!Helpers::value2enum(value, set, FL_(enum_wwCircMode))) {
|
||||||
LOG_WARNING(F("Set warm water circulation mode: Invalid mode"));
|
LOG_WARNING(F("Set warm water circulation mode: Invalid mode"));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -1268,7 +1307,7 @@ bool Thermostat::set_wwcircmode(const char * value, const int8_t id) {
|
|||||||
write_command(0x02F5, 3, set, 0x02F5);
|
write_command(0x02F5, 3, set, 0x02F5);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (!Helpers::value2enum(value, set, {F("off"), F("on"), F("auto")})) {
|
if (!Helpers::value2enum(value, set, FL_(enum_wwCircMode2))) {
|
||||||
LOG_WARNING(F("Set warm water circulation mode: Invalid mode"));
|
LOG_WARNING(F("Set warm water circulation mode: Invalid mode"));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -1279,34 +1318,39 @@ bool Thermostat::set_wwcircmode(const char * value, const int8_t id) {
|
|||||||
|
|
||||||
// set the holiday as string dd.mm.yyyy-dd.mm.yyyy
|
// set the holiday as string dd.mm.yyyy-dd.mm.yyyy
|
||||||
bool Thermostat::set_holiday(const char * value, const int8_t id) {
|
bool Thermostat::set_holiday(const char * value, const int8_t id) {
|
||||||
std::string hd(30, '\0');
|
|
||||||
if (!Helpers::value2string(value, hd)) {
|
|
||||||
LOG_WARNING(F("Set holiday: Invalid value"));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
uint8_t hc_num = (id == -1) ? AUTO_HEATING_CIRCUIT : id;
|
uint8_t hc_num = (id == -1) ? AUTO_HEATING_CIRCUIT : id;
|
||||||
std::shared_ptr<Thermostat::HeatingCircuit> hc = heating_circuit(hc_num);
|
std::shared_ptr<Thermostat::HeatingCircuit> hc = heating_circuit(hc_num);
|
||||||
if (hc == nullptr) {
|
if (hc == nullptr) {
|
||||||
LOG_WARNING(F("Set holiday: Heating Circuit %d not found or activated for device ID 0x%02X"), hc_num, device_id());
|
LOG_WARNING(F("Set holiday: Heating Circuit %d not found or activated for device ID 0x%02X"), hc_num, device_id());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (value == nullptr || value[0] == '-') {
|
||||||
|
read_command(timer_typeids[hc->hc_num() - 1], 87, 6);
|
||||||
|
return true;
|
||||||
|
} else if (strlen(value) == 1 && value[0] == '+') {
|
||||||
|
read_command(timer_typeids[hc->hc_num() - 1], 93, 6);
|
||||||
|
return true;
|
||||||
|
} else if (strlen(value) != 21) {
|
||||||
|
LOG_WARNING(F("Set holiday: Invalid value"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
uint8_t data[6];
|
uint8_t data[6];
|
||||||
data[0] = (hd[0] - '0') * 10 + (hd[1] - '0');
|
data[0] = (value[0] - '0') * 10 + (value[1] - '0');
|
||||||
data[1] = (hd[3] - '0') * 10 + (hd[4] - '0');
|
data[1] = (value[3] - '0') * 10 + (value[4] - '0');
|
||||||
data[2] = (hd[7] - '0') * 100 + (hd[8] - '0') * 10 + (hd[9] - '0');
|
data[2] = (value[7] - '0') * 100 + (value[8] - '0') * 10 + (value[9] - '0');
|
||||||
data[3] = (hd[11] - '0') * 10 + (hd[12] - '0');
|
data[3] = (value[11] - '0') * 10 + (value[12] - '0');
|
||||||
data[4] = (hd[14] - '0') * 10 + (hd[15] - '0');
|
data[4] = (value[14] - '0') * 10 + (value[15] - '0');
|
||||||
data[5] = (hd[18] - '0') * 100 + (hd[19] - '0') * 10 + (hd[20] - '0');
|
data[5] = (value[18] - '0') * 100 + (value[19] - '0') * 10 + (value[20] - '0');
|
||||||
|
|
||||||
if (hd[10] == '-') {
|
if (value[10] == '-') {
|
||||||
LOG_INFO(F("Setting holiday away from home for hc %d"), hc->hc_num());
|
LOG_INFO(F("Setting holiday away from home for hc %d"), hc->hc_num());
|
||||||
write_command(timer_typeids[hc->hc_num() - 1], 87, data, 6, 0);
|
write_command(timer_typeids[hc->hc_num() - 1], 87, data, 6, 0);
|
||||||
} else if (hd[10] == '+') {
|
} else if (value[10] == '+') {
|
||||||
LOG_INFO(F("Setting holiday at home for hc %d"), hc->hc_num());
|
LOG_INFO(F("Setting holiday at home for hc %d"), hc->hc_num());
|
||||||
write_command(timer_typeids[hc->hc_num() - 1], 93, data, 6, 0);
|
write_command(timer_typeids[hc->hc_num() - 1], 93, data, 6, 0);
|
||||||
} else {
|
} else {
|
||||||
LOG_WARNING(F("Set holiday: Invalid"));
|
LOG_WARNING(F("Set holiday: Invalid value"));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1315,18 +1359,21 @@ bool Thermostat::set_holiday(const char * value, const int8_t id) {
|
|||||||
|
|
||||||
// set pause in hours
|
// set pause in hours
|
||||||
bool Thermostat::set_pause(const char * value, const int8_t id) {
|
bool Thermostat::set_pause(const char * value, const int8_t id) {
|
||||||
int hrs = 0;
|
|
||||||
if (!Helpers::value2number(value, hrs)) {
|
|
||||||
LOG_WARNING(F("Set pause: Invalid value"));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t hc_num = (id == -1) ? AUTO_HEATING_CIRCUIT : id;
|
uint8_t hc_num = (id == -1) ? AUTO_HEATING_CIRCUIT : id;
|
||||||
std::shared_ptr<Thermostat::HeatingCircuit> hc = heating_circuit(hc_num);
|
std::shared_ptr<Thermostat::HeatingCircuit> hc = heating_circuit(hc_num);
|
||||||
if (hc == nullptr) {
|
if (hc == nullptr) {
|
||||||
LOG_WARNING(F("Set pause: Heating Circuit %d not found or activated for device ID 0x%02X"), hc_num, device_id());
|
LOG_WARNING(F("Set pause: Heating Circuit %d not found or activated for device ID 0x%02X"), hc_num, device_id());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (value == nullptr) {
|
||||||
|
read_command(timer_typeids[hc->hc_num() - 1], 85, 1);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
int hrs = 0;
|
||||||
|
if (!Helpers::value2number(value, hrs)) {
|
||||||
|
LOG_WARNING(F("Set pause: Invalid value"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
LOG_INFO(F("Setting pause: %d hours, hc: %d"), hrs, hc->hc_num());
|
LOG_INFO(F("Setting pause: %d hours, hc: %d"), hrs, hc->hc_num());
|
||||||
write_command(timer_typeids[hc->hc_num() - 1], 85, hrs);
|
write_command(timer_typeids[hc->hc_num() - 1], 85, hrs);
|
||||||
@@ -1336,18 +1383,21 @@ bool Thermostat::set_pause(const char * value, const int8_t id) {
|
|||||||
|
|
||||||
// set partymode in hours
|
// set partymode in hours
|
||||||
bool Thermostat::set_party(const char * value, const int8_t id) {
|
bool Thermostat::set_party(const char * value, const int8_t id) {
|
||||||
|
uint8_t hc_num = (id == -1) ? AUTO_HEATING_CIRCUIT : id;
|
||||||
|
std::shared_ptr<Thermostat::HeatingCircuit> hc = heating_circuit(hc_num);
|
||||||
|
if (hc == nullptr) {
|
||||||
|
LOG_WARNING(F("Set party: Heating Circuit %d not found or activated for device ID 0x%02X"), hc_num, device_id());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (value == nullptr) {
|
||||||
|
read_command(timer_typeids[hc->hc_num() - 1], 85, 1);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
int hrs = 0;
|
int hrs = 0;
|
||||||
if (!Helpers::value2number(value, hrs)) {
|
if (!Helpers::value2number(value, hrs)) {
|
||||||
LOG_WARNING(F("Set party: Invalid value"));
|
LOG_WARNING(F("Set party: Invalid value"));
|
||||||
return false;
|
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) {
|
|
||||||
LOG_WARNING(F("Set party: Heating Circuit %d not found or activated for device ID 0x%02X"), hc_num, device_id());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
LOG_INFO(F("Setting party: %d hours, hc: %d"), hrs, hc->hc_num());
|
LOG_INFO(F("Setting party: %d hours, hc: %d"), hrs, hc->hc_num());
|
||||||
write_command(timer_typeids[hc->hc_num() - 1], 86, hrs);
|
write_command(timer_typeids[hc->hc_num() - 1], 86, hrs);
|
||||||
|
|
||||||
@@ -1492,7 +1542,7 @@ bool Thermostat::set_mode_n(const uint8_t mode, const uint8_t hc_num) {
|
|||||||
offset = EMS_OFFSET_RC20Set_mode;
|
offset = EMS_OFFSET_RC20Set_mode;
|
||||||
validate_typeid = set_typeids[hc_p];
|
validate_typeid = set_typeids[hc_p];
|
||||||
break;
|
break;
|
||||||
case EMSdevice::EMS_DEVICE_FLAG_RC20_2: // ES72
|
case EMSdevice::EMS_DEVICE_FLAG_RC20_N: // ES72
|
||||||
offset = EMS_OFFSET_RC20_2_Set_mode;
|
offset = EMS_OFFSET_RC20_2_Set_mode;
|
||||||
validate_typeid = set_typeids[hc_p];
|
validate_typeid = set_typeids[hc_p];
|
||||||
break;
|
break;
|
||||||
@@ -1501,7 +1551,7 @@ bool Thermostat::set_mode_n(const uint8_t mode, const uint8_t hc_num) {
|
|||||||
validate_typeid = set_typeids[hc_p];
|
validate_typeid = set_typeids[hc_p];
|
||||||
break;
|
break;
|
||||||
case EMSdevice::EMS_DEVICE_FLAG_RC35:
|
case EMSdevice::EMS_DEVICE_FLAG_RC35:
|
||||||
case EMSdevice::EMS_DEVICE_FLAG_RC30_1:
|
case EMSdevice::EMS_DEVICE_FLAG_RC30_N:
|
||||||
offset = EMS_OFFSET_RC35Set_mode;
|
offset = EMS_OFFSET_RC35Set_mode;
|
||||||
validate_typeid = set_typeids[hc_p];
|
validate_typeid = set_typeids[hc_p];
|
||||||
break;
|
break;
|
||||||
@@ -1555,7 +1605,7 @@ bool Thermostat::set_summermode(const char * value, const int8_t id) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
uint8_t set = 0xFF;
|
uint8_t set = 0xFF;
|
||||||
if (!Helpers::value2enum(value, set, {F("summer"), F("auto"), F("winter")})) {
|
if (!Helpers::value2enum(value, set, FL_(enum_summermode))) {
|
||||||
LOG_WARNING(F("Setting summer mode: Invalid mode"));
|
LOG_WARNING(F("Setting summer mode: Invalid mode"));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -1573,7 +1623,7 @@ bool Thermostat::set_reducemode(const char * value, const int8_t id) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
uint8_t set = 0xFF;
|
uint8_t set = 0xFF;
|
||||||
if (!Helpers::value2enum(value, set, {F("nofrost"), F("reduce"), F("room"), F("outdoor")})) {
|
if (!Helpers::value2enum(value, set, FL_(enum_reducemode))) {
|
||||||
LOG_WARNING(F("Setting reduce mode: Invalid mode"));
|
LOG_WARNING(F("Setting reduce mode: Invalid mode"));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -1592,13 +1642,13 @@ bool Thermostat::set_controlmode(const char * value, const int8_t id) {
|
|||||||
}
|
}
|
||||||
uint8_t set = 0xFF;
|
uint8_t set = 0xFF;
|
||||||
if (model() == EMS_DEVICE_FLAG_RC300 || model() == EMS_DEVICE_FLAG_RC100) {
|
if (model() == EMS_DEVICE_FLAG_RC300 || model() == EMS_DEVICE_FLAG_RC100) {
|
||||||
if (Helpers::value2enum(value, set, {F("off"), F("outdoor"), F("simple"), F("MPC"), F("room"), F("power"), F("const")})) {
|
if (Helpers::value2enum(value, set, FL_(enum_controlmode))) {
|
||||||
LOG_INFO(F("Setting control mode to %d for heating circuit %d"), set, hc->hc_num());
|
LOG_INFO(F("Setting control mode to %d for heating circuit %d"), set, hc->hc_num());
|
||||||
write_command(curve_typeids[hc->hc_num() - 1], 0, set, curve_typeids[hc->hc_num() - 1]);
|
write_command(curve_typeids[hc->hc_num() - 1], 0, set, curve_typeids[hc->hc_num() - 1]);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
} else if (model() == EMS_DEVICE_FLAG_RC35 || model() == EMS_DEVICE_FLAG_RC30_1) {
|
} else if (model() == EMS_DEVICE_FLAG_RC35 || model() == EMS_DEVICE_FLAG_RC30_N) {
|
||||||
if (Helpers::value2enum(value, set, {F("outdoor"), F("room")})) {
|
if (Helpers::value2enum(value, set, FL_(enum_controlmode2))) {
|
||||||
LOG_INFO(F("Setting control mode to %d for heating circuit %d"), set, hc->hc_num());
|
LOG_INFO(F("Setting control mode to %d for heating circuit %d"), set, hc->hc_num());
|
||||||
write_command(set_typeids[hc->hc_num() - 1], 33, set, set_typeids[hc->hc_num() - 1]);
|
write_command(set_typeids[hc->hc_num() - 1], 33, set, set_typeids[hc->hc_num() - 1]);
|
||||||
return true;
|
return true;
|
||||||
@@ -1617,7 +1667,17 @@ bool Thermostat::set_switchtime(const char * value, const int8_t id) {
|
|||||||
LOG_WARNING(F("Setting switchtime: Heating Circuit %d not found or activated"), hc_num);
|
LOG_WARNING(F("Setting switchtime: Heating Circuit %d not found or activated"), hc_num);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (strlen(value) != 12) {
|
if (value == nullptr) {
|
||||||
|
return false;
|
||||||
|
} else if (strlen(value) == 2) { // query point 01?
|
||||||
|
uint8_t no = (value[0] - '0') * 10 + (value[1] - '0');
|
||||||
|
if (no < 42) {
|
||||||
|
read_command(timer_typeids[hc->hc_num() - 1], 2 * no, 2);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
|
||||||
|
} else if (strlen(value) != 12) {
|
||||||
LOG_WARNING(F("Setting switchtime: Invalid data"));
|
LOG_WARNING(F("Setting switchtime: Invalid data"));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -1637,7 +1697,7 @@ bool Thermostat::set_switchtime(const char * value, const int8_t id) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((model() == EMS_DEVICE_FLAG_RC35 || model() == EMS_DEVICE_FLAG_RC30_1)) {
|
if ((model() == EMS_DEVICE_FLAG_RC35 || model() == EMS_DEVICE_FLAG_RC30_N)) {
|
||||||
write_command(timer_typeids[hc->hc_num() - 1], no * 2, (uint8_t *)&data, 2, timer_typeids[hc->hc_num() - 1]);
|
write_command(timer_typeids[hc->hc_num() - 1], no * 2, (uint8_t *)&data, 2, timer_typeids[hc->hc_num() - 1]);
|
||||||
} else {
|
} else {
|
||||||
LOG_WARNING(F("Setting switchtime: thermostat not supported"));
|
LOG_WARNING(F("Setting switchtime: thermostat not supported"));
|
||||||
@@ -1666,9 +1726,9 @@ bool Thermostat::set_program(const char * value, const int8_t id) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (model() == EMS_DEVICE_FLAG_RC20_2 && set > 0 && set < 10) {
|
if (model() == EMS_DEVICE_FLAG_RC20_N && set > 0 && set < 10) {
|
||||||
write_command(set_typeids[hc->hc_num() - 1], 11, set, set_typeids[hc->hc_num() - 1]);
|
write_command(set_typeids[hc->hc_num() - 1], 11, set, set_typeids[hc->hc_num() - 1]);
|
||||||
} else if ((model() == EMS_DEVICE_FLAG_RC35 || model() == EMS_DEVICE_FLAG_RC30_1) && set < 11) {
|
} else if ((model() == EMS_DEVICE_FLAG_RC35 || model() == EMS_DEVICE_FLAG_RC30_N) && set < 11) {
|
||||||
write_command(timer_typeids[hc->hc_num() - 1], 84, set, timer_typeids[hc->hc_num() - 1]);
|
write_command(timer_typeids[hc->hc_num() - 1], 84, set, timer_typeids[hc->hc_num() - 1]);
|
||||||
} else if ((model() == EMS_DEVICE_FLAG_RC300 || model() == EMS_DEVICE_FLAG_RC100) && (set == 1 || set == 2)) {
|
} else if ((model() == EMS_DEVICE_FLAG_RC300 || model() == EMS_DEVICE_FLAG_RC100) && (set == 1 || set == 2)) {
|
||||||
write_command(set_typeids[hc->hc_num() - 1], 11, set, set_typeids[hc->hc_num() - 1]);
|
write_command(set_typeids[hc->hc_num() - 1], 11, set, set_typeids[hc->hc_num() - 1]);
|
||||||
@@ -1717,6 +1777,12 @@ bool Thermostat::set_temperature(const float temperature, const uint8_t mode, co
|
|||||||
case HeatingCircuit::Mode::MANUAL:
|
case HeatingCircuit::Mode::MANUAL:
|
||||||
offset = 0x0A; // manual offset
|
offset = 0x0A; // manual offset
|
||||||
break;
|
break;
|
||||||
|
case HeatingCircuit::Mode::TEMPAUTO:
|
||||||
|
offset = 0x08; // manual offset
|
||||||
|
if (temperature == -1) {
|
||||||
|
factor = 0xFF; // use factor as value
|
||||||
|
}
|
||||||
|
break;
|
||||||
case HeatingCircuit::Mode::COMFORT:
|
case HeatingCircuit::Mode::COMFORT:
|
||||||
offset = 0x02; // comfort offset level 2
|
offset = 0x02; // comfort offset level 2
|
||||||
break;
|
break;
|
||||||
@@ -1783,7 +1849,7 @@ bool Thermostat::set_temperature(const float temperature, const uint8_t mode, co
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (model == EMS_DEVICE_FLAG_RC20_2) {
|
} else if (model == EMS_DEVICE_FLAG_RC20_N) {
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
case HeatingCircuit::Mode::NIGHT: // change the night temp
|
case HeatingCircuit::Mode::NIGHT: // change the night temp
|
||||||
offset = EMS_OFFSET_RC20_2_Set_temp_night;
|
offset = EMS_OFFSET_RC20_2_Set_temp_night;
|
||||||
@@ -1798,7 +1864,7 @@ bool Thermostat::set_temperature(const float temperature, const uint8_t mode, co
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if ((model == EMS_DEVICE_FLAG_RC35) || (model == EMS_DEVICE_FLAG_RC30_1)) {
|
} else if ((model == EMS_DEVICE_FLAG_RC35) || (model == EMS_DEVICE_FLAG_RC30_N)) {
|
||||||
validate_typeid = set_typeids[hc->hc_num() - 1];
|
validate_typeid = set_typeids[hc->hc_num() - 1];
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
case HeatingCircuit::Mode::NIGHT: // change the night temp
|
case HeatingCircuit::Mode::NIGHT: // change the night temp
|
||||||
@@ -1837,6 +1903,13 @@ bool Thermostat::set_temperature(const float temperature, const uint8_t mode, co
|
|||||||
offset = 4;
|
offset = 4;
|
||||||
factor = 1;
|
factor = 1;
|
||||||
break;
|
break;
|
||||||
|
case HeatingCircuit::Mode::NOREDUCE:
|
||||||
|
offset = EMS_OFFSET_RC35Set_noreducetemp;
|
||||||
|
factor = 1;
|
||||||
|
break;
|
||||||
|
case HeatingCircuit::Mode::TEMPAUTO:
|
||||||
|
offset = EMS_OFFSET_RC35Set_seltemp;
|
||||||
|
break;
|
||||||
case HeatingCircuit::Mode::MINFLOW:
|
case HeatingCircuit::Mode::MINFLOW:
|
||||||
offset = 16;
|
offset = 16;
|
||||||
factor = 1;
|
factor = 1;
|
||||||
@@ -2005,6 +2078,14 @@ bool Thermostat::set_manualtemp(const char * value, const int8_t id) {
|
|||||||
return set_temperature_value(value, id, HeatingCircuit::Mode::MANUAL);
|
return set_temperature_value(value, id, HeatingCircuit::Mode::MANUAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Thermostat::set_tempautotemp(const char * value, const int8_t id) {
|
||||||
|
return set_temperature_value(value, id, HeatingCircuit::Mode::TEMPAUTO);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Thermostat::set_noreducetemp(const char * value, const int8_t id) {
|
||||||
|
return set_temperature_value(value, id, HeatingCircuit::Mode::NOREDUCE);
|
||||||
|
}
|
||||||
|
|
||||||
bool Thermostat::set_flowtempoffset(const char * value, const int8_t id) {
|
bool Thermostat::set_flowtempoffset(const char * value, const int8_t id) {
|
||||||
return set_temperature_value(value, id, HeatingCircuit::Mode::FLOWOFFSET);
|
return set_temperature_value(value, id, HeatingCircuit::Mode::FLOWOFFSET);
|
||||||
}
|
}
|
||||||
@@ -2021,142 +2102,64 @@ bool Thermostat::set_roominfluence(const char * value, const int8_t id) {
|
|||||||
return set_temperature_value(value, id, HeatingCircuit::Mode::ROOMINFLUENCE);
|
return set_temperature_value(value, id, HeatingCircuit::Mode::ROOMINFLUENCE);
|
||||||
}
|
}
|
||||||
|
|
||||||
// API commands for MQTT and Console
|
// register main device values, top level for all thermostats (excluding heating circuits)
|
||||||
void Thermostat::add_commands() {
|
// as these are done in void Thermostat::register_device_values_hc()
|
||||||
// if this thermostat doesn't support write, don't add the commands
|
void Thermostat::register_device_values() {
|
||||||
if (has_flags(EMS_DEVICE_FLAG_NO_WRITE)) {
|
// Common for all thermostats
|
||||||
return;
|
register_device_value(TAG_THERMOSTAT_DATA, &id_, DeviceValueType::UINT, nullptr, FL_(ID), DeviceValueUOM::NONE);
|
||||||
}
|
register_device_value(TAG_THERMOSTAT_DATA, &errorCode_, DeviceValueType::TEXT, nullptr, FL_(errorCode), DeviceValueUOM::NONE);
|
||||||
|
register_device_value(TAG_THERMOSTAT_DATA, &lastCode_, DeviceValueType::TEXT, nullptr, FL_(lastCode), DeviceValueUOM::NONE);
|
||||||
|
|
||||||
// common to all thermostats
|
switch (this->model()) {
|
||||||
register_mqtt_cmd(F("temp"), MAKE_CF_CB(set_temp), FLAG_HC);
|
|
||||||
register_mqtt_cmd(F("mode"), MAKE_CF_CB(set_mode), FLAG_HC);
|
|
||||||
if (model() == EMS_DEVICE_FLAG_RC35) { // section is together with RC30
|
|
||||||
register_mqtt_cmd(F("datetime"), MAKE_CF_CB(set_datetime));
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (model()) {
|
|
||||||
case EMS_DEVICE_FLAG_RC100:
|
case EMS_DEVICE_FLAG_RC100:
|
||||||
case EMS_DEVICE_FLAG_RC300:
|
case EMS_DEVICE_FLAG_RC300:
|
||||||
register_mqtt_cmd(F("datetime"), MAKE_CF_CB(set_datetime));
|
register_device_value(TAG_THERMOSTAT_DATA, &dateTime_, DeviceValueType::TEXT, nullptr, FL_(dateTime), DeviceValueUOM::NONE, MAKE_CF_CB(set_datetime));
|
||||||
register_mqtt_cmd(F("manualtemp"), MAKE_CF_CB(set_manualtemp), FLAG_HC);
|
register_device_value(TAG_THERMOSTAT_DATA, &floordrystatus_, DeviceValueType::ENUM, FL_(enum_floordrystatus), FL_(floordrystatus), DeviceValueUOM::NONE);
|
||||||
register_mqtt_cmd(F("ecotemp"), MAKE_CF_CB(set_ecotemp), FLAG_HC);
|
register_device_value(TAG_THERMOSTAT_DATA, &dampedoutdoortemp2_, DeviceValueType::SHORT, FL_(div10), FL_(dampedoutdoortemp), DeviceValueUOM::DEGREES);
|
||||||
register_mqtt_cmd(F("comforttemp"), MAKE_CF_CB(set_comforttemp), FLAG_HC);
|
register_device_value(TAG_THERMOSTAT_DATA, &floordrytemp_, DeviceValueType::UINT, nullptr, FL_(floordrytemp), DeviceValueUOM::DEGREES);
|
||||||
register_mqtt_cmd(F("summermode"), MAKE_CF_CB(set_summermode), FLAG_HC);
|
register_device_value(TAG_THERMOSTAT_DATA, &ibaBuildingType_, DeviceValueType::ENUM, FL_(enum_ibaBuildingType), FL_(ibaBuildingType), DeviceValueUOM::NONE, MAKE_CF_CB(set_building));
|
||||||
register_mqtt_cmd(F("summertemp"), MAKE_CF_CB(set_summertemp), FLAG_HC);
|
register_device_value(TAG_DEVICE_DATA_WW, &wwSetTemp_, DeviceValueType::UINT, nullptr, FL_(wwSetTemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_wwtemp));
|
||||||
register_mqtt_cmd(F("wwmode"), MAKE_CF_CB(set_wwmode));
|
register_device_value(TAG_DEVICE_DATA_WW, &wwMode_, DeviceValueType::ENUM, FL_(enum_wwMode), FL_(wwMode), DeviceValueUOM::NONE, MAKE_CF_CB(set_wwmode));
|
||||||
register_mqtt_cmd(F("wwsettemp"), MAKE_CF_CB(set_wwtemp));
|
register_device_value(TAG_DEVICE_DATA_WW, &wwSetTempLow_, DeviceValueType::UINT, nullptr, FL_(wwSetTempLow), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_wwtemplow));
|
||||||
register_mqtt_cmd(F("wwsettemplow"), MAKE_CF_CB(set_wwtemplow));
|
register_device_value(TAG_DEVICE_DATA_WW, &wwCircMode_, DeviceValueType::ENUM, FL_(enum_wwCircMode), FL_(wWCircMode), DeviceValueUOM::NONE, MAKE_CF_CB(set_wwcircmode));
|
||||||
register_mqtt_cmd(F("wwonetime"), MAKE_CF_CB(set_wwonetime));
|
register_device_value(TAG_DEVICE_DATA_WW, &wwExtra1_, DeviceValueType::UINT, nullptr, FL_(wwExtra1), DeviceValueUOM::DEGREES);
|
||||||
register_mqtt_cmd(F("wwcircmode"), MAKE_CF_CB(set_wwcircmode));
|
register_device_value(TAG_DEVICE_DATA_WW, &wwExtra2_, DeviceValueType::UINT, nullptr, FL_(wwExtra2), DeviceValueUOM::DEGREES);
|
||||||
register_mqtt_cmd(F("building"), MAKE_CF_CB(set_building));
|
|
||||||
register_mqtt_cmd(F("nofrosttemp"), MAKE_CF_CB(set_nofrosttemp), FLAG_HC);
|
|
||||||
register_mqtt_cmd(F("designtemp"), MAKE_CF_CB(set_designtemp), FLAG_HC);
|
|
||||||
register_mqtt_cmd(F("offsettemp"), MAKE_CF_CB(set_offsettemp), FLAG_HC);
|
|
||||||
register_mqtt_cmd(F("minflowtemp"), MAKE_CF_CB(set_minflowtemp), FLAG_HC);
|
|
||||||
register_mqtt_cmd(F("maxflowtemp"), MAKE_CF_CB(set_maxflowtemp), FLAG_HC);
|
|
||||||
register_mqtt_cmd(F("minexttemp"), MAKE_CF_CB(set_minexttemp));
|
|
||||||
register_mqtt_cmd(F("roominfluence"), MAKE_CF_CB(set_roominfluence), FLAG_HC);
|
|
||||||
register_mqtt_cmd(F("program"), MAKE_CF_CB(set_program), FLAG_HC);
|
|
||||||
register_mqtt_cmd(F("controlmode"), MAKE_CF_CB(set_controlmode), FLAG_HC);
|
|
||||||
break;
|
break;
|
||||||
case EMS_DEVICE_FLAG_RC20_2:
|
case EMS_DEVICE_FLAG_RC20_N:
|
||||||
register_mqtt_cmd(F("nighttemp"), MAKE_CF_CB(set_nighttemp));
|
case EMS_DEVICE_FLAG_RC20:
|
||||||
register_mqtt_cmd(F("daytemp"), MAKE_CF_CB(set_daytemp));
|
register_device_value(TAG_THERMOSTAT_DATA, &dateTime_, DeviceValueType::TEXT, nullptr, FL_(dateTime), DeviceValueUOM::NONE); // can't set datetime
|
||||||
register_mqtt_cmd(F("program"), MAKE_CF_CB(set_program));
|
|
||||||
break;
|
break;
|
||||||
case EMS_DEVICE_FLAG_RC30_1: // only RC30_1
|
case EMS_DEVICE_FLAG_RC30_N:
|
||||||
register_mqtt_cmd(F("clockoffset"), MAKE_CF_CB(set_clockoffset));
|
register_device_value(TAG_THERMOSTAT_DATA, &dateTime_, DeviceValueType::TEXT, nullptr, FL_(dateTime), DeviceValueUOM::NONE); // can't set datetime
|
||||||
register_mqtt_cmd(F("language"), MAKE_CF_CB(set_language));
|
register_device_value(TAG_THERMOSTAT_DATA, &ibaMainDisplay_, DeviceValueType::ENUM, FL_(enum_ibaMainDisplay), FL_(ibaMainDisplay), DeviceValueUOM::NONE);
|
||||||
register_mqtt_cmd(F("display"), MAKE_CF_CB(set_display));
|
register_device_value(TAG_THERMOSTAT_DATA, &ibaLanguage_, DeviceValueType::ENUM, FL_(enum_ibaLanguage), FL_(ibaLanguage), DeviceValueUOM::NONE);
|
||||||
case EMS_DEVICE_FLAG_RC35: // RC30 and RC35
|
register_device_value(TAG_THERMOSTAT_DATA, &ibaClockOffset_, DeviceValueType::UINT, nullptr, FL_(ibaClockOffset), DeviceValueUOM::NONE); // offset (in sec) to clock, 0xff=-1s, 0x02=2s
|
||||||
register_mqtt_cmd(F("nighttemp"), MAKE_CF_CB(set_nighttemp), FLAG_HC);
|
register_device_value(TAG_THERMOSTAT_DATA, &ibaCalIntTemperature_, DeviceValueType::INT, FL_(div2), FL_(ibaCalIntTemperature), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_calinttemp));
|
||||||
register_mqtt_cmd(F("daytemp"), MAKE_CF_CB(set_daytemp), FLAG_HC);
|
register_device_value(TAG_THERMOSTAT_DATA, &ibaMinExtTemperature_, DeviceValueType::INT, nullptr, FL_(ibaMinExtTemperature), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_minexttemp));
|
||||||
register_mqtt_cmd(F("nofrosttemp"), MAKE_CF_CB(set_nofrosttemp), FLAG_HC);
|
register_device_value(TAG_THERMOSTAT_DATA, &dampedoutdoortemp_, DeviceValueType::INT, nullptr, FL_(dampedoutdoortemp), DeviceValueUOM::DEGREES);
|
||||||
register_mqtt_cmd(F("remotetemp"), MAKE_CF_CB(set_remotetemp), FLAG_HC);
|
register_device_value(TAG_THERMOSTAT_DATA, &ibaBuildingType_, DeviceValueType::ENUM, FL_(enum_ibaBuildingType2), FL_(ibaBuildingType), DeviceValueUOM::NONE, MAKE_CF_CB(set_building));
|
||||||
register_mqtt_cmd(F("minexttemp"), MAKE_CF_CB(set_minexttemp));
|
register_device_value(TAG_DEVICE_DATA_WW, &wwMode_, DeviceValueType::ENUM, FL_(enum_wwMode2), FL_(wwMode), DeviceValueUOM::NONE, MAKE_CF_CB(set_wwmode));
|
||||||
register_mqtt_cmd(F("calinttemp"), MAKE_CF_CB(set_calinttemp));
|
register_device_value(TAG_DEVICE_DATA_WW, &wwCircMode_, DeviceValueType::ENUM, FL_(enum_wwCircMode2), FL_(wWCircMode), DeviceValueUOM::NONE, MAKE_CF_CB(set_wwcircmode));
|
||||||
register_mqtt_cmd(F("building"), MAKE_CF_CB(set_building));
|
break;
|
||||||
register_mqtt_cmd(F("control"), MAKE_CF_CB(set_control), FLAG_HC);
|
case EMS_DEVICE_FLAG_RC35:
|
||||||
register_mqtt_cmd(F("pause"), MAKE_CF_CB(set_pause), FLAG_HC);
|
register_device_value(TAG_THERMOSTAT_DATA, &dateTime_, DeviceValueType::TEXT, nullptr, FL_(dateTime), DeviceValueUOM::NONE, MAKE_CF_CB(set_datetime));
|
||||||
register_mqtt_cmd(F("party"), MAKE_CF_CB(set_party), FLAG_HC);
|
register_device_value(TAG_THERMOSTAT_DATA, &ibaCalIntTemperature_, DeviceValueType::INT, FL_(div2), FL_(ibaCalIntTemperature), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_calinttemp));
|
||||||
register_mqtt_cmd(F("holiday"), MAKE_CF_CB(set_holiday), FLAG_HC);
|
register_device_value(TAG_THERMOSTAT_DATA, &ibaMinExtTemperature_, DeviceValueType::INT, nullptr, FL_(ibaMinExtTemperature), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_minexttemp));
|
||||||
register_mqtt_cmd(F("summertemp"), MAKE_CF_CB(set_summertemp), FLAG_HC);
|
register_device_value(TAG_THERMOSTAT_DATA, &tempsensor1_, DeviceValueType::USHORT, FL_(div10), FL_(tempsensor1), DeviceValueUOM::DEGREES);
|
||||||
register_mqtt_cmd(F("designtemp"), MAKE_CF_CB(set_designtemp), FLAG_HC);
|
register_device_value(TAG_THERMOSTAT_DATA, &tempsensor2_, DeviceValueType::USHORT, FL_(div10), FL_(tempsensor2), DeviceValueUOM::DEGREES);
|
||||||
register_mqtt_cmd(F("offsettemp"), MAKE_CF_CB(set_offsettemp), FLAG_HC);
|
register_device_value(TAG_THERMOSTAT_DATA, &dampedoutdoortemp_, DeviceValueType::INT, nullptr, FL_(dampedoutdoortemp), DeviceValueUOM::DEGREES);
|
||||||
register_mqtt_cmd(F("holidaytemp"), MAKE_CF_CB(set_holidaytemp), FLAG_HC);
|
register_device_value(TAG_THERMOSTAT_DATA, &ibaBuildingType_, DeviceValueType::ENUM, FL_(enum_ibaBuildingType2), FL_(ibaBuildingType), DeviceValueUOM::NONE, MAKE_CF_CB(set_building));
|
||||||
register_mqtt_cmd(F("wwmode"), MAKE_CF_CB(set_wwmode));
|
register_device_value(TAG_DEVICE_DATA_WW, &wwMode_, DeviceValueType::ENUM, FL_(enum_wwMode2), FL_(wwMode), DeviceValueUOM::NONE, MAKE_CF_CB(set_wwmode));
|
||||||
register_mqtt_cmd(F("wwcircmode"), MAKE_CF_CB(set_wwcircmode));
|
register_device_value(TAG_DEVICE_DATA_WW, &wwCircMode_, DeviceValueType::ENUM, FL_(enum_wwCircMode2), FL_(wWCircMode), DeviceValueUOM::NONE, MAKE_CF_CB(set_wwcircmode));
|
||||||
register_mqtt_cmd(F("roominfluence"), MAKE_CF_CB(set_roominfluence), FLAG_HC);
|
|
||||||
register_mqtt_cmd(F("flowtempoffset"), MAKE_CF_CB(set_flowtempoffset), FLAG_HC);
|
|
||||||
register_mqtt_cmd(F("minflowtemp"), MAKE_CF_CB(set_minflowtemp), FLAG_HC);
|
|
||||||
register_mqtt_cmd(F("maxflowtemp"), MAKE_CF_CB(set_maxflowtemp), FLAG_HC);
|
|
||||||
register_mqtt_cmd(F("reducemode"), MAKE_CF_CB(set_reducemode), FLAG_HC);
|
|
||||||
register_mqtt_cmd(F("program"), MAKE_CF_CB(set_program), FLAG_HC);
|
|
||||||
register_mqtt_cmd(F("switchtime"), MAKE_CF_CB(set_switchtime), FLAG_HC);
|
|
||||||
register_mqtt_cmd(F("controlmode"), MAKE_CF_CB(set_controlmode), FLAG_HC);
|
|
||||||
break;
|
break;
|
||||||
case EMS_DEVICE_FLAG_JUNKERS:
|
case EMS_DEVICE_FLAG_JUNKERS:
|
||||||
register_mqtt_cmd(F("datetime"), MAKE_CF_CB(set_datetime));
|
register_device_value(TAG_THERMOSTAT_DATA, &dateTime_, DeviceValueType::TEXT, nullptr, FL_(dateTime), DeviceValueUOM::NONE, MAKE_CF_CB(set_datetime));
|
||||||
register_mqtt_cmd(F("nofrosttemp"), MAKE_CF_CB(set_nofrosttemp), FLAG_HC);
|
|
||||||
register_mqtt_cmd(F("ecotemp"), MAKE_CF_CB(set_ecotemp), FLAG_HC);
|
|
||||||
register_mqtt_cmd(F("heattemp"), MAKE_CF_CB(set_heattemp), FLAG_HC);
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// register main device values, top level for all thermostats (excluding heating circuits)
|
|
||||||
void Thermostat::register_device_values() {
|
|
||||||
uint8_t model = this->model();
|
|
||||||
|
|
||||||
// Common for all thermostats
|
|
||||||
register_device_value(TAG_THERMOSTAT_DATA, &id_, DeviceValueType::UINT, nullptr, F("id"), nullptr); // empty full name to prevent being shown in web or console
|
|
||||||
register_device_value(TAG_THERMOSTAT_DATA, &dateTime_, DeviceValueType::TEXT, nullptr, F("dateTime"), F("date/time"));
|
|
||||||
register_device_value(TAG_THERMOSTAT_DATA, &errorCode_, DeviceValueType::TEXT, nullptr, F("errorCode"), F("error code"));
|
|
||||||
register_device_value(TAG_THERMOSTAT_DATA, &lastCode_, DeviceValueType::TEXT, nullptr, F("lastCode"), F("last error"));
|
|
||||||
|
|
||||||
// RC30 only
|
|
||||||
if (model == EMSdevice::EMS_DEVICE_FLAG_RC30_1) {
|
|
||||||
register_device_value(TAG_THERMOSTAT_DATA, &ibaMainDisplay_, DeviceValueType::ENUM, FL_(enum_ibaMainDisplay), F("display"), F("display"));
|
|
||||||
register_device_value(TAG_THERMOSTAT_DATA, &ibaLanguage_, DeviceValueType::ENUM, FL_(enum_ibaLanguage), F("language"), F("language"));
|
|
||||||
register_device_value(TAG_THERMOSTAT_DATA, &ibaClockOffset_, DeviceValueType::UINT, nullptr, F("offsetclock"), F("clock offset")); // offset (in sec) to clock, 0xff=-1s, 0x02=2s
|
|
||||||
}
|
|
||||||
|
|
||||||
// RC300 and RC100
|
|
||||||
if (model == EMS_DEVICE_FLAG_RC300 || model == EMS_DEVICE_FLAG_RC100) {
|
|
||||||
register_device_value(TAG_THERMOSTAT_DATA, &floordrystatus_, DeviceValueType::ENUM, FL_(enum_floordrystatus), F("floordry"), F("floor drying"));
|
|
||||||
register_device_value(TAG_THERMOSTAT_DATA, &dampedoutdoortemp2_, DeviceValueType::SHORT, FL_(div10), F("dampedoutdoortemp"), F("damped outdoor temperature"), DeviceValueUOM::DEGREES);
|
|
||||||
register_device_value(TAG_THERMOSTAT_DATA, &floordrytemp_, DeviceValueType::UINT, nullptr, F("floordrytemp"), F("floor drying temperature"), DeviceValueUOM::DEGREES);
|
|
||||||
register_device_value(TAG_THERMOSTAT_DATA, &ibaBuildingType_, DeviceValueType::ENUM, FL_(enum_ibaBuildingType), F("building"), F("building"));
|
|
||||||
register_device_value(TAG_THERMOSTAT_DATA, &wwSetTemp_, DeviceValueType::UINT, nullptr, F("wwsettemp"), F("warm water set temperature"), DeviceValueUOM::DEGREES);
|
|
||||||
register_device_value(TAG_THERMOSTAT_DATA, &wwMode_, DeviceValueType::ENUM, FL_(enum_wwMode), F("wwmode"), F("warm water mode"));
|
|
||||||
register_device_value(TAG_THERMOSTAT_DATA, &wwSetTempLow_, DeviceValueType::UINT, nullptr, F("wwsettemplow"), F("warm water set temperature low"), DeviceValueUOM::DEGREES);
|
|
||||||
register_device_value(TAG_THERMOSTAT_DATA, &wwCircMode_, DeviceValueType::ENUM, FL_(enum_wwCircMode), F("wwcircmode"), F("warm water circulation mode"));
|
|
||||||
register_device_value(TAG_THERMOSTAT_DATA, &wwExtra1_, DeviceValueType::UINT, nullptr, F("wwextra1"), F("warm water circuit 1 extra"), DeviceValueUOM::DEGREES);
|
|
||||||
register_device_value(TAG_THERMOSTAT_DATA, &wwExtra2_, DeviceValueType::UINT, nullptr, F("wwextra2"), F("warm water circuit 2 extra"), DeviceValueUOM::DEGREES);
|
|
||||||
}
|
|
||||||
|
|
||||||
// RC30 and RC35
|
|
||||||
if (model == EMS_DEVICE_FLAG_RC35 || model == EMS_DEVICE_FLAG_RC30_1) {
|
|
||||||
register_device_value(TAG_THERMOSTAT_DATA, &ibaCalIntTemperature_, DeviceValueType::INT, FL_(div2), F("intoffset"), F("offset internal temperature"), DeviceValueUOM::DEGREES);
|
|
||||||
register_device_value(TAG_THERMOSTAT_DATA,
|
|
||||||
&ibaMinExtTemperature_,
|
|
||||||
DeviceValueType::INT,
|
|
||||||
nullptr,
|
|
||||||
F("minexttemp"),
|
|
||||||
F("min external temperature"),
|
|
||||||
DeviceValueUOM::DEGREES); // min ext temp for heating curve, in deg.
|
|
||||||
register_device_value(TAG_THERMOSTAT_DATA, &tempsensor1_, DeviceValueType::USHORT, FL_(div10), F("inttemp1"), F("temperature sensor 1"), DeviceValueUOM::DEGREES);
|
|
||||||
register_device_value(TAG_THERMOSTAT_DATA, &tempsensor2_, DeviceValueType::USHORT, FL_(div10), F("inttemp2"), F("temperature sensor 2"), DeviceValueUOM::DEGREES);
|
|
||||||
register_device_value(TAG_THERMOSTAT_DATA, &dampedoutdoortemp_, DeviceValueType::INT, nullptr, F("dampedoutdoortemp"), F("damped outdoor temperature"), DeviceValueUOM::DEGREES);
|
|
||||||
register_device_value(TAG_THERMOSTAT_DATA, &ibaBuildingType_, DeviceValueType::ENUM, FL_(enum_ibaBuildingType2), F("building"), F("building"));
|
|
||||||
register_device_value(TAG_THERMOSTAT_DATA, &wwMode_, DeviceValueType::ENUM, FL_(enum_wwMode2), F("wwmode"), F("warm water mode"));
|
|
||||||
register_device_value(TAG_THERMOSTAT_DATA, &wwCircMode_, DeviceValueType::ENUM, FL_(enum_wwCircMode2), F("wwcircmode"), F("warm water circulation mode"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// registers the values for a heating circuit
|
// registers the values for a heating circuit
|
||||||
void Thermostat::register_device_values_hc(std::shared_ptr<emsesp::Thermostat::HeatingCircuit> hc) {
|
void Thermostat::register_device_values_hc(std::shared_ptr<emsesp::Thermostat::HeatingCircuit> hc) {
|
||||||
uint8_t model = hc->get_model();
|
uint8_t model = hc->get_model();
|
||||||
@@ -2178,8 +2181,13 @@ void Thermostat::register_device_values_hc(std::shared_ptr<emsesp::Thermostat::H
|
|||||||
setpoint_temp_divider = FL_(div2);
|
setpoint_temp_divider = FL_(div2);
|
||||||
curr_temp_divider = FL_(div10);
|
curr_temp_divider = FL_(div10);
|
||||||
}
|
}
|
||||||
register_device_value(tag, &hc->setpoint_roomTemp, DeviceValueType::SHORT, setpoint_temp_divider, F("seltemp"), F("setpoint room temperature"), DeviceValueUOM::DEGREES);
|
if (has_flags(EMS_DEVICE_FLAG_NO_WRITE) || device_id() != EMSESP::actual_master_thermostat()) {
|
||||||
register_device_value(tag, &hc->curr_roomTemp, DeviceValueType::SHORT, curr_temp_divider, F("currtemp"), F("current room temperature"), DeviceValueUOM::DEGREES);
|
register_device_value(tag, &hc->setpoint_roomTemp, DeviceValueType::SHORT, setpoint_temp_divider, FL_(setpoint_roomTemp), DeviceValueUOM::DEGREES);
|
||||||
|
} else {
|
||||||
|
register_device_value(tag, &hc->setpoint_roomTemp, DeviceValueType::SHORT, setpoint_temp_divider, FL_(setpoint_roomTemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_temp), 5, 29);
|
||||||
|
register_device_value(tag, &hc->setpoint_roomTemp, DeviceValueType::SHORT, setpoint_temp_divider, FL_(temp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_temp), 5, 29);
|
||||||
|
}
|
||||||
|
register_device_value(tag, &hc->curr_roomTemp, DeviceValueType::SHORT, curr_temp_divider, FL_(curr_roomTemp), DeviceValueUOM::DEGREES);
|
||||||
|
|
||||||
if (device_id() != EMSESP::actual_master_thermostat()) {
|
if (device_id() != EMSESP::actual_master_thermostat()) {
|
||||||
return;
|
return;
|
||||||
@@ -2190,81 +2198,94 @@ void Thermostat::register_device_values_hc(std::shared_ptr<emsesp::Thermostat::H
|
|||||||
if (Mqtt::ha_enabled()) {
|
if (Mqtt::ha_enabled()) {
|
||||||
uint8_t option = Mqtt::ha_climate_format();
|
uint8_t option = Mqtt::ha_climate_format();
|
||||||
if (option == Mqtt::HA_Climate_Format::CURRENT) {
|
if (option == Mqtt::HA_Climate_Format::CURRENT) {
|
||||||
register_device_value(tag, &hc->curr_roomTemp, DeviceValueType::SHORT, curr_temp_divider, F("hatemp"), nullptr);
|
register_device_value(tag, &hc->curr_roomTemp, DeviceValueType::SHORT, curr_temp_divider, FL_(hatemp), DeviceValueUOM::NONE);
|
||||||
} else if (option == Mqtt::HA_Climate_Format::SETPOINT) {
|
} else if (option == Mqtt::HA_Climate_Format::SETPOINT) {
|
||||||
register_device_value(tag, &hc->setpoint_roomTemp, DeviceValueType::SHORT, setpoint_temp_divider, F("hatemp"), nullptr);
|
register_device_value(tag, &hc->setpoint_roomTemp, DeviceValueType::SHORT, setpoint_temp_divider, FL_(hatemp), DeviceValueUOM::NONE);
|
||||||
} else if (option == Mqtt::HA_Climate_Format::ZERO) {
|
} else if (option == Mqtt::HA_Climate_Format::ZERO) {
|
||||||
register_device_value(tag, &zero_value_, DeviceValueType::UINT, nullptr, F("hatemp"), nullptr);
|
register_device_value(tag, &zero_value_, DeviceValueType::UINT, nullptr, FL_(hatemp), DeviceValueUOM::NONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
// if we're sending to HA the only valid mode types are heat, auto and off
|
// if we're sending to HA the only valid mode types are heat, auto and off
|
||||||
// manual & day = heat
|
// manual & day = heat
|
||||||
// night & off = off
|
// night & off = off
|
||||||
// everything else auto
|
// everything else auto
|
||||||
register_device_value(tag, &hc->mode, DeviceValueType::ENUM, FL_(enum_hamode), F("hamode"), nullptr);
|
register_device_value(tag, &hc->mode, DeviceValueType::ENUM, FL_(enum_hamode), FL_(hamode), DeviceValueUOM::NONE);
|
||||||
}
|
}
|
||||||
|
switch (model) {
|
||||||
if (model == EMSdevice::EMS_DEVICE_FLAG_RC300 || model == EMSdevice::EMS_DEVICE_FLAG_RC100) {
|
case EMS_DEVICE_FLAG_RC100:
|
||||||
register_device_value(tag, &hc->mode, DeviceValueType::ENUM, FL_(enum_mode), F("mode"), F("mode"));
|
case EMS_DEVICE_FLAG_RC300:
|
||||||
register_device_value(tag, &hc->modetype, DeviceValueType::ENUM, FL_(enum_modetype), F("modetype"), F("mode type"));
|
register_device_value(tag, &hc->mode, DeviceValueType::ENUM, FL_(enum_mode), FL_(mode), DeviceValueUOM::NONE, MAKE_CF_CB(set_mode));
|
||||||
register_device_value(tag, &hc->nighttemp, DeviceValueType::UINT, FL_(div2), F("ecotemp"), F("eco temperature"), DeviceValueUOM::DEGREES);
|
register_device_value(tag, &hc->modetype, DeviceValueType::ENUM, FL_(enum_modetype), FL_(modetype), DeviceValueUOM::NONE);
|
||||||
register_device_value(tag, &hc->manualtemp, DeviceValueType::UINT, FL_(div2), F("manualtemp"), F("manual temperature"), DeviceValueUOM::DEGREES);
|
register_device_value(tag, &hc->nighttemp, DeviceValueType::UINT, FL_(div2), FL_(ecotemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_ecotemp));
|
||||||
register_device_value(tag, &hc->daytemp, DeviceValueType::UINT, FL_(div2), F("comforttemp"), F("comfort temperature"), DeviceValueUOM::DEGREES);
|
register_device_value(tag, &hc->manualtemp, DeviceValueType::UINT, FL_(div2), FL_(manualtemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_manualtemp));
|
||||||
register_device_value(tag, &hc->summertemp, DeviceValueType::UINT, nullptr, F("summertemp"), F("summer temperature"), DeviceValueUOM::DEGREES);
|
register_device_value(tag, &hc->daytemp, DeviceValueType::UINT, FL_(div2), FL_(comforttemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_comforttemp));
|
||||||
register_device_value(tag, &hc->designtemp, DeviceValueType::UINT, nullptr, F("designtemp"), F("design temperature"), DeviceValueUOM::DEGREES);
|
register_device_value(tag, &hc->summertemp, DeviceValueType::UINT, nullptr, FL_(summertemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_summertemp));
|
||||||
register_device_value(tag, &hc->offsettemp, DeviceValueType::INT, nullptr, F("offsettemp"), F("offset temperature"), DeviceValueUOM::DEGREES);
|
register_device_value(tag, &hc->designtemp, DeviceValueType::UINT, nullptr, FL_(designtemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_designtemp));
|
||||||
register_device_value(tag, &hc->minflowtemp, DeviceValueType::UINT, nullptr, F("minflowtemp"), F("min flow temperature"), DeviceValueUOM::DEGREES);
|
register_device_value(tag, &hc->offsettemp, DeviceValueType::INT, nullptr, FL_(offsettemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_offsettemp));
|
||||||
register_device_value(tag, &hc->maxflowtemp, DeviceValueType::UINT, nullptr, F("maxflowtemp"), F("max flow temperature"), DeviceValueUOM::DEGREES);
|
register_device_value(tag, &hc->minflowtemp, DeviceValueType::UINT, nullptr, FL_(minflowtemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_minflowtemp));
|
||||||
register_device_value(tag, &hc->roominfluence, DeviceValueType::UINT, nullptr, F("roominfluence"), F("room influence"));
|
register_device_value(tag, &hc->maxflowtemp, DeviceValueType::UINT, nullptr, FL_(maxflowtemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_maxflowtemp));
|
||||||
register_device_value(tag, &hc->nofrosttemp, DeviceValueType::INT, nullptr, F("nofrosttemp"), F("nofrost temperature"), DeviceValueUOM::DEGREES);
|
register_device_value(tag, &hc->roominfluence, DeviceValueType::UINT, nullptr, FL_(roominfluence), DeviceValueUOM::NONE, MAKE_CF_CB(set_roominfluence));
|
||||||
register_device_value(tag, &hc->targetflowtemp, DeviceValueType::UINT, nullptr, F("targetflowtemp"), F("target flow temperature"), DeviceValueUOM::DEGREES);
|
register_device_value(tag, &hc->nofrosttemp, DeviceValueType::INT, nullptr, FL_(nofrosttemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_nofrosttemp));
|
||||||
register_device_value(tag, &hc->heatingtype, DeviceValueType::ENUM, FL_(enum_heatingtype), F("heatingtype"), F("heating type"));
|
register_device_value(tag, &hc->targetflowtemp, DeviceValueType::UINT, nullptr, FL_(targetflowtemp), DeviceValueUOM::DEGREES);
|
||||||
register_device_value(tag, &hc->summer_setmode, DeviceValueType::ENUM, FL_(enum_summermode), F("summermode"), F("summer mode"));
|
register_device_value(tag, &hc->heatingtype, DeviceValueType::ENUM, FL_(enum_heatingtype), FL_(heatingtype), DeviceValueUOM::NONE);
|
||||||
register_device_value(tag, &hc->controlmode, DeviceValueType::ENUM, FL_(enum_controlmode), F("controlmode"), F("control mode"));
|
register_device_value(tag, &hc->summer_setmode, DeviceValueType::ENUM, FL_(enum_summermode), FL_(summermode), DeviceValueUOM::NONE, MAKE_CF_CB(set_summermode));
|
||||||
register_device_value(tag, &hc->program, DeviceValueType::UINT, nullptr, F("program"), F("program"));
|
register_device_value(tag, &hc->summermode, DeviceValueType::BOOL, nullptr, FL_(summermode), DeviceValueUOM::NONE);
|
||||||
}
|
register_device_value(tag, &hc->controlmode, DeviceValueType::ENUM, FL_(enum_controlmode), FL_(controlmode), DeviceValueUOM::NONE, MAKE_CF_CB(set_controlmode));
|
||||||
|
register_device_value(tag, &hc->program, DeviceValueType::UINT, nullptr, FL_(program), DeviceValueUOM::NONE, MAKE_CF_CB(set_program));
|
||||||
if (model == EMS_DEVICE_FLAG_RC20) {
|
register_device_value(tag, &hc->tempautotemp, DeviceValueType::UINT, FL_(div2), FL_(tempautotemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_tempautotemp));
|
||||||
register_device_value(tag, &hc->mode, DeviceValueType::ENUM, FL_(enum_mode2), F("mode"), F("mode"));
|
break;
|
||||||
}
|
case EMS_DEVICE_FLAG_CRF:
|
||||||
|
register_device_value(tag, &hc->mode, DeviceValueType::ENUM, FL_(enum_mode5), FL_(mode), DeviceValueUOM::NONE);
|
||||||
if (model == EMS_DEVICE_FLAG_RC20_2) {
|
register_device_value(tag, &hc->modetype, DeviceValueType::ENUM, FL_(enum_modetype5), FL_(modetype), DeviceValueUOM::NONE);
|
||||||
register_device_value(tag, &hc->mode, DeviceValueType::ENUM, FL_(enum_mode2), F("mode"), F("mode"));
|
register_device_value(tag, &hc->targetflowtemp, DeviceValueType::UINT, nullptr, FL_(targetflowtemp), DeviceValueUOM::DEGREES);
|
||||||
register_device_value(tag, &hc->modetype, DeviceValueType::ENUM, FL_(enum_modetype2), F("modetype"), F("mode type"));
|
break;
|
||||||
register_device_value(tag, &hc->daytemp, DeviceValueType::UINT, FL_(div2), F("daytemp"), F("day temperature"), DeviceValueUOM::DEGREES);
|
case EMS_DEVICE_FLAG_RC20:
|
||||||
register_device_value(tag, &hc->nighttemp, DeviceValueType::UINT, FL_(div2), F("nighttemp"), F("night temperature"), DeviceValueUOM::DEGREES);
|
register_device_value(tag, &hc->mode, DeviceValueType::ENUM, FL_(enum_mode2), FL_(mode), DeviceValueUOM::NONE, MAKE_CF_CB(set_mode));
|
||||||
register_device_value(tag, &hc->program, DeviceValueType::UINT, nullptr, F("program"), F("program"));
|
break;
|
||||||
}
|
case EMS_DEVICE_FLAG_RC20_N:
|
||||||
|
register_device_value(tag, &hc->mode, DeviceValueType::ENUM, FL_(enum_mode2), FL_(mode), DeviceValueUOM::NONE, MAKE_CF_CB(set_mode));
|
||||||
if (model == EMS_DEVICE_FLAG_RC35 || model == EMS_DEVICE_FLAG_RC30_1) {
|
register_device_value(tag, &hc->modetype, DeviceValueType::ENUM, FL_(enum_modetype2), FL_(modetype), DeviceValueUOM::NONE);
|
||||||
register_device_value(tag, &hc->mode, DeviceValueType::ENUM, FL_(enum_mode3), F("mode"), F("mode"));
|
register_device_value(tag, &hc->daytemp, DeviceValueType::UINT, FL_(div2), FL_(daytemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_daytemp));
|
||||||
register_device_value(tag, &hc->modetype, DeviceValueType::ENUM, FL_(enum_modetype3), F("modetype"), F("mode type"));
|
register_device_value(tag, &hc->nighttemp, DeviceValueType::UINT, FL_(div2), FL_(nighttemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_nighttemp));
|
||||||
register_device_value(tag, &hc->daytemp, DeviceValueType::UINT, FL_(div2), F("daytemp"), F("day temperature"), DeviceValueUOM::DEGREES);
|
register_device_value(tag, &hc->program, DeviceValueType::UINT, nullptr, FL_(program), DeviceValueUOM::NONE, MAKE_CF_CB(set_program));
|
||||||
register_device_value(tag, &hc->nighttemp, DeviceValueType::UINT, FL_(div2), F("nighttemp"), F("night temperature"), DeviceValueUOM::DEGREES);
|
break;
|
||||||
register_device_value(tag, &hc->designtemp, DeviceValueType::UINT, nullptr, F("designtemp"), F("design temperature"), DeviceValueUOM::DEGREES);
|
case EMS_DEVICE_FLAG_RC30_N:
|
||||||
register_device_value(tag, &hc->offsettemp, DeviceValueType::INT, FL_(div2), F("offsettemp"), F("offset temperature"), DeviceValueUOM::DEGREES);
|
case EMS_DEVICE_FLAG_RC35:
|
||||||
register_device_value(tag, &hc->holidaytemp, DeviceValueType::UINT, FL_(div2), F("holidaytemp"), F("holiday temperature"), DeviceValueUOM::DEGREES);
|
register_device_value(tag, &hc->mode, DeviceValueType::ENUM, FL_(enum_mode3), FL_(mode), DeviceValueUOM::NONE, MAKE_CF_CB(set_mode));
|
||||||
register_device_value(tag, &hc->targetflowtemp, DeviceValueType::UINT, nullptr, F("targetflowtemp"), F("target flow temperature"), DeviceValueUOM::DEGREES);
|
register_device_value(tag, &hc->modetype, DeviceValueType::ENUM, FL_(enum_modetype3), FL_(modetype), DeviceValueUOM::NONE);
|
||||||
register_device_value(tag, &hc->summertemp, DeviceValueType::UINT, nullptr, F("summertemp"), F("summer temperature"), DeviceValueUOM::DEGREES);
|
register_device_value(tag, &hc->daytemp, DeviceValueType::UINT, FL_(div2), FL_(daytemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_daytemp));
|
||||||
register_device_value(tag, &hc->summermode, DeviceValueType::BOOL, nullptr, F("summermode"), F("summer mode"));
|
register_device_value(tag, &hc->nighttemp, DeviceValueType::UINT, FL_(div2), FL_(nighttemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_nighttemp));
|
||||||
register_device_value(tag, &hc->holidaymode, DeviceValueType::BOOL, nullptr, F("holidaymode"), F("holiday mode"));
|
register_device_value(tag, &hc->designtemp, DeviceValueType::UINT, nullptr, FL_(designtemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_designtemp));
|
||||||
register_device_value(tag, &hc->nofrosttemp, DeviceValueType::INT, nullptr, F("nofrosttemp"), F("nofrost temperature"), DeviceValueUOM::DEGREES);
|
register_device_value(tag, &hc->offsettemp, DeviceValueType::INT, FL_(div2), FL_(offsettemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_offsettemp));
|
||||||
register_device_value(tag, &hc->roominfluence, DeviceValueType::UINT, nullptr, F("roominfluence"), F("room influence"));
|
register_device_value(tag, &hc->holidaytemp, DeviceValueType::UINT, FL_(div2), FL_(holidaytemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_holidaytemp));
|
||||||
register_device_value(tag, &hc->minflowtemp, DeviceValueType::UINT, nullptr, F("minflowtemp"), F("min flow temperature"), DeviceValueUOM::DEGREES);
|
register_device_value(tag, &hc->targetflowtemp, DeviceValueType::UINT, nullptr, FL_(targetflowtemp), DeviceValueUOM::DEGREES);
|
||||||
register_device_value(tag, &hc->maxflowtemp, DeviceValueType::UINT, nullptr, F("maxflowtemp"), F("max flow temperature"), DeviceValueUOM::DEGREES);
|
register_device_value(tag, &hc->summertemp, DeviceValueType::UINT, nullptr, FL_(summertemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_summertemp));
|
||||||
register_device_value(tag, &hc->flowtempoffset, DeviceValueType::UINT, nullptr, F("flowtempoffset"), F("flow temperature offset"), DeviceValueUOM::DEGREES);
|
register_device_value(tag, &hc->summermode, DeviceValueType::BOOL, nullptr, FL_(summermode), DeviceValueUOM::NONE);
|
||||||
register_device_value(tag, &hc->heatingtype, DeviceValueType::ENUM, FL_(enum_heatingtype), F("heatingtype"), F("heating type"));
|
register_device_value(tag, &hc->holidaymode, DeviceValueType::BOOL, nullptr, FL_(holidaymode), DeviceValueUOM::NONE, MAKE_CF_CB(set_holiday));
|
||||||
register_device_value(tag, &hc->reducemode, DeviceValueType::ENUM, FL_(enum_reducemode), F("reducemode"), F("reduce mode"));
|
register_device_value(tag, &hc->nofrosttemp, DeviceValueType::INT, nullptr, FL_(nofrosttemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_nofrosttemp));
|
||||||
register_device_value(tag, &hc->controlmode, DeviceValueType::ENUM, FL_(enum_controlmode2), F("controlmode"), F("control mode"));
|
register_device_value(tag, &hc->roominfluence, DeviceValueType::UINT, nullptr, FL_(roominfluence), DeviceValueUOM::NONE, MAKE_CF_CB(set_roominfluence));
|
||||||
register_device_value(tag, &hc->program, DeviceValueType::UINT, nullptr, F("program"), F("program"));
|
register_device_value(tag, &hc->minflowtemp, DeviceValueType::UINT, nullptr, FL_(minflowtemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_minflowtemp));
|
||||||
}
|
register_device_value(tag, &hc->maxflowtemp, DeviceValueType::UINT, nullptr, FL_(maxflowtemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_maxflowtemp));
|
||||||
|
register_device_value(tag, &hc->flowtempoffset, DeviceValueType::UINT, nullptr, FL_(flowtempoffset), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_flowtempoffset));
|
||||||
if (model == EMSdevice::EMS_DEVICE_FLAG_JUNKERS) {
|
register_device_value(tag, &hc->heatingtype, DeviceValueType::ENUM, FL_(enum_heatingtype), FL_(heatingtype), DeviceValueUOM::NONE);
|
||||||
register_device_value(tag, &hc->mode, DeviceValueType::ENUM, FL_(enum_mode4), F("mode"), F("mode"));
|
register_device_value(tag, &hc->reducemode, DeviceValueType::ENUM, FL_(enum_reducemode), FL_(reducemode), DeviceValueUOM::NONE, MAKE_CF_CB(set_reducemode));
|
||||||
register_device_value(tag, &hc->modetype, DeviceValueType::ENUM, FL_(enum_modetype4), F("modetype"), F("mode type"));
|
register_device_value(tag, &hc->controlmode, DeviceValueType::ENUM, FL_(enum_controlmode2), FL_(controlmode), DeviceValueUOM::NONE, MAKE_CF_CB(set_controlmode));
|
||||||
register_device_value(tag, &hc->daytemp, DeviceValueType::UINT, FL_(div2), F("heattemp"), F("heat temperature"), DeviceValueUOM::DEGREES);
|
register_device_value(tag, &hc->control, DeviceValueType::ENUM, FL_(enum_control), FL_(control), DeviceValueUOM::NONE, MAKE_CF_CB(set_control));
|
||||||
register_device_value(tag, &hc->nighttemp, DeviceValueType::UINT, FL_(div2), F("ecotemp"), F("eco temperature"), DeviceValueUOM::DEGREES);
|
register_device_value(tag, &hc->program, DeviceValueType::UINT, nullptr, FL_(program), DeviceValueUOM::NONE, MAKE_CF_CB(set_program));
|
||||||
register_device_value(tag, &hc->nofrosttemp, DeviceValueType::INT, FL_(div2), F("nofrosttemp"), F("nofrost temperature"), DeviceValueUOM::DEGREES);
|
register_device_value(tag, &hc->pause, DeviceValueType::UINT, nullptr, FL_(pause), DeviceValueUOM::HOURS, MAKE_CF_CB(set_pause));
|
||||||
|
register_device_value(tag, &hc->party, DeviceValueType::UINT, nullptr, FL_(party), DeviceValueUOM::HOURS, MAKE_CF_CB(set_party));
|
||||||
|
register_device_value(tag, &hc->tempautotemp, DeviceValueType::UINT, FL_(div2), FL_(tempautotemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_tempautotemp));
|
||||||
|
register_device_value(tag, &hc->noreducetemp, DeviceValueType::INT, nullptr, FL_(noreducetemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_noreducetemp));
|
||||||
|
register_device_value(tag, &hc->remotetemp, DeviceValueType::SHORT, FL_(div10), FL_(remotetemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_remotetemp));
|
||||||
|
register_device_value(tag, &dummychar_, DeviceValueType::TEXT, nullptr, FL_(switchtime), DeviceValueUOM::NONE, MAKE_CF_CB(set_switchtime));
|
||||||
|
break;
|
||||||
|
case EMS_DEVICE_FLAG_JUNKERS:
|
||||||
|
register_device_value(tag, &hc->mode, DeviceValueType::ENUM, FL_(enum_mode4), FL_(mode), DeviceValueUOM::NONE, MAKE_CF_CB(set_mode));
|
||||||
|
register_device_value(tag, &hc->modetype, DeviceValueType::ENUM, FL_(enum_modetype4), FL_(modetype), DeviceValueUOM::NONE);
|
||||||
|
register_device_value(tag, &hc->daytemp, DeviceValueType::UINT, FL_(div2), FL_(heattemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_heattemp));
|
||||||
|
register_device_value(tag, &hc->nighttemp, DeviceValueType::UINT, FL_(div2), FL_(ecotemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_ecotemp));
|
||||||
|
register_device_value(tag, &hc->nofrosttemp, DeviceValueType::INT, FL_(div2), FL_(nofrosttemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_nofrosttemp));
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -36,6 +36,8 @@ class Thermostat : public EMSdevice {
|
|||||||
|
|
||||||
int16_t setpoint_roomTemp;
|
int16_t setpoint_roomTemp;
|
||||||
int16_t curr_roomTemp;
|
int16_t curr_roomTemp;
|
||||||
|
int16_t remotetemp; // for readback
|
||||||
|
uint8_t tempautotemp;
|
||||||
uint8_t mode;
|
uint8_t mode;
|
||||||
uint8_t modetype;
|
uint8_t modetype;
|
||||||
uint8_t summermode;
|
uint8_t summermode;
|
||||||
@@ -58,6 +60,10 @@ class Thermostat : public EMSdevice {
|
|||||||
uint8_t reducemode;
|
uint8_t reducemode;
|
||||||
uint8_t program;
|
uint8_t program;
|
||||||
uint8_t controlmode;
|
uint8_t controlmode;
|
||||||
|
uint8_t control;
|
||||||
|
uint8_t pause;
|
||||||
|
uint8_t party;
|
||||||
|
int8_t noreducetemp; // signed -20°C to +10°C
|
||||||
|
|
||||||
uint8_t hc_num() const {
|
uint8_t hc_num() const {
|
||||||
return hc_num_;
|
return hc_num_;
|
||||||
@@ -93,6 +99,9 @@ class Thermostat : public EMSdevice {
|
|||||||
MINFLOW,
|
MINFLOW,
|
||||||
MAXFLOW,
|
MAXFLOW,
|
||||||
ROOMINFLUENCE,
|
ROOMINFLUENCE,
|
||||||
|
TEMPAUTO,
|
||||||
|
NOREDUCE,
|
||||||
|
ON,
|
||||||
UNKNOWN
|
UNKNOWN
|
||||||
|
|
||||||
};
|
};
|
||||||
@@ -114,8 +123,6 @@ class Thermostat : public EMSdevice {
|
|||||||
private:
|
private:
|
||||||
static uuid::log::Logger logger_;
|
static uuid::log::Logger logger_;
|
||||||
|
|
||||||
void add_commands();
|
|
||||||
|
|
||||||
void register_device_values();
|
void register_device_values();
|
||||||
void register_device_values(uint8_t hc_num);
|
void register_device_values(uint8_t hc_num);
|
||||||
|
|
||||||
@@ -137,7 +144,8 @@ class Thermostat : public EMSdevice {
|
|||||||
char dateTime_[25]; // date and time stamp
|
char dateTime_[25]; // date and time stamp
|
||||||
char errorCode_[15]; // code from 0xA2 as string i.e. "A22(816)"
|
char errorCode_[15]; // code from 0xA2 as string i.e. "A22(816)"
|
||||||
uint16_t errorNumber_; // used internally to build error code
|
uint16_t errorNumber_; // used internally to build error code
|
||||||
char lastCode_[30];
|
char lastCode_[30]; // error log
|
||||||
|
char dummychar_[5]; // for commands with no output
|
||||||
|
|
||||||
// Installation parameters
|
// Installation parameters
|
||||||
uint8_t ibaMainDisplay_; // display on Thermostat: 0 int temp, 1 int setpoint, 2 ext temp, 3 burner temp, 4 ww temp, 5 functioning mode, 6 time, 7 data, 9 smoke temp
|
uint8_t ibaMainDisplay_; // display on Thermostat: 0 int temp, 1 int setpoint, 2 ext temp, 3 burner temp, 4 ww temp, 5 functioning mode, 6 time, 7 data, 9 smoke temp
|
||||||
@@ -200,6 +208,7 @@ class Thermostat : public EMSdevice {
|
|||||||
static constexpr uint8_t EMS_OFFSET_RC35Set_heatingtype = 0; // e.g. floor heating = 3
|
static constexpr uint8_t EMS_OFFSET_RC35Set_heatingtype = 0; // e.g. floor heating = 3
|
||||||
static constexpr uint8_t EMS_OFFSET_RC35Set_targetflowtemp = 14; // target flow temperature
|
static constexpr uint8_t EMS_OFFSET_RC35Set_targetflowtemp = 14; // target flow temperature
|
||||||
static constexpr uint8_t EMS_OFFSET_RC35Set_seltemp = 37; // selected temp
|
static constexpr uint8_t EMS_OFFSET_RC35Set_seltemp = 37; // selected temp
|
||||||
|
static constexpr uint8_t EMS_OFFSET_RC35Set_noreducetemp = 38; // temp to stop reducing
|
||||||
static constexpr uint8_t EMS_OFFSET_RC35Set_temp_offset = 6;
|
static constexpr uint8_t EMS_OFFSET_RC35Set_temp_offset = 6;
|
||||||
static constexpr uint8_t EMS_OFFSET_RC35Set_temp_flowoffset = 24;
|
static constexpr uint8_t EMS_OFFSET_RC35Set_temp_flowoffset = 24;
|
||||||
static constexpr uint8_t EMS_OFFSET_RC35Set_temp_design = 17;
|
static constexpr uint8_t EMS_OFFSET_RC35Set_temp_design = 17;
|
||||||
@@ -268,6 +277,7 @@ class Thermostat : public EMSdevice {
|
|||||||
void process_RC20Set_2(std::shared_ptr<const Telegram> telegram);
|
void process_RC20Set_2(std::shared_ptr<const Telegram> telegram);
|
||||||
void process_RC10Monitor(std::shared_ptr<const Telegram> telegram);
|
void process_RC10Monitor(std::shared_ptr<const Telegram> telegram);
|
||||||
void process_RC10Set(std::shared_ptr<const Telegram> telegram);
|
void process_RC10Set(std::shared_ptr<const Telegram> telegram);
|
||||||
|
void process_CRFMonitor(std::shared_ptr<const Telegram> telegram);
|
||||||
void process_RC300Monitor(std::shared_ptr<const Telegram> telegram);
|
void process_RC300Monitor(std::shared_ptr<const Telegram> telegram);
|
||||||
void process_RC300Set(std::shared_ptr<const Telegram> telegram);
|
void process_RC300Set(std::shared_ptr<const Telegram> telegram);
|
||||||
void process_RC300Summer(std::shared_ptr<const Telegram> telegram);
|
void process_RC300Summer(std::shared_ptr<const Telegram> telegram);
|
||||||
@@ -309,6 +319,8 @@ class Thermostat : public EMSdevice {
|
|||||||
bool set_offsettemp(const char * value, const int8_t id);
|
bool set_offsettemp(const char * value, const int8_t id);
|
||||||
bool set_holidaytemp(const char * value, const int8_t id);
|
bool set_holidaytemp(const char * value, const int8_t id);
|
||||||
bool set_manualtemp(const char * value, const int8_t id);
|
bool set_manualtemp(const char * value, const int8_t id);
|
||||||
|
bool set_tempautotemp(const char * value, const int8_t id);
|
||||||
|
bool set_noreducetemp(const char * value, const int8_t id);
|
||||||
bool set_remotetemp(const char * value, const int8_t id);
|
bool set_remotetemp(const char * value, const int8_t id);
|
||||||
bool set_roominfluence(const char * value, const int8_t id);
|
bool set_roominfluence(const char * value, const int8_t id);
|
||||||
bool set_flowtempoffset(const char * value, const int8_t id);
|
bool set_flowtempoffset(const char * value, const int8_t id);
|
||||||
|
|||||||
@@ -33,7 +33,8 @@ static const __FlashStringHelper * DeviceValueUOM_s[] __attribute__((__aligned__
|
|||||||
F_(hours),
|
F_(hours),
|
||||||
F_(minutes),
|
F_(minutes),
|
||||||
F_(ua),
|
F_(ua),
|
||||||
F_(bar)
|
F_(bar),
|
||||||
|
F_(kw)
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -44,7 +45,7 @@ static const __FlashStringHelper * const DeviceValueTAG_s[] PROGMEM = {
|
|||||||
F_(tag_none), // ""
|
F_(tag_none), // ""
|
||||||
F_(tag_heartbeat), // ""
|
F_(tag_heartbeat), // ""
|
||||||
F_(tag_boiler_data), // ""
|
F_(tag_boiler_data), // ""
|
||||||
F_(tag_boiler_data_ww), // "warm water"
|
F_(tag_device_data_ww), // "warm water"
|
||||||
F_(tag_thermostat_data), // ""
|
F_(tag_thermostat_data), // ""
|
||||||
F_(tag_hc1), // "hc1"
|
F_(tag_hc1), // "hc1"
|
||||||
F_(tag_hc2), // "hc2"
|
F_(tag_hc2), // "hc2"
|
||||||
@@ -77,9 +78,9 @@ static const __FlashStringHelper * const DeviceValueTAG_s[] PROGMEM = {
|
|||||||
static const __FlashStringHelper * const DeviceValueTAG_mqtt[] PROGMEM = {
|
static const __FlashStringHelper * const DeviceValueTAG_mqtt[] PROGMEM = {
|
||||||
|
|
||||||
F_(tag_none), // ""
|
F_(tag_none), // ""
|
||||||
F_(tag_heartbeat_mqtt), // "heartbeat"
|
F_(heartbeat), // "heartbeat"
|
||||||
F_(tag_boiler_data_mqtt), // ""
|
F_(tag_boiler_data_mqtt), // ""
|
||||||
F_(tag_boiler_data_ww_mqtt), // "ww"
|
F_(tag_device_data_ww_mqtt), // "ww"
|
||||||
F_(tag_thermostat_data), // ""
|
F_(tag_thermostat_data), // ""
|
||||||
F_(tag_hc1), // "hc1"
|
F_(tag_hc1), // "hc1"
|
||||||
F_(tag_hc2), // "hc2"
|
F_(tag_hc2), // "hc2"
|
||||||
@@ -149,7 +150,7 @@ std::string EMSdevice::brand_to_string() const {
|
|||||||
break;
|
break;
|
||||||
case EMSdevice::Brand::NO_BRAND:
|
case EMSdevice::Brand::NO_BRAND:
|
||||||
default:
|
default:
|
||||||
return read_flash_string(F("---"));
|
return read_flash_string(F(""));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -211,31 +212,38 @@ std::string EMSdevice::device_type_2_device_name(const uint8_t device_type) {
|
|||||||
|
|
||||||
// returns device_type from a string
|
// returns device_type from a string
|
||||||
uint8_t EMSdevice::device_name_2_device_type(const char * topic) {
|
uint8_t EMSdevice::device_name_2_device_type(const char * topic) {
|
||||||
if (!strcmp_P(topic, reinterpret_cast<PGM_P>(F_(boiler)))) {
|
// convert topic to lowercase and compare
|
||||||
|
char lowtopic[20];
|
||||||
|
strlcpy(lowtopic, topic, sizeof(lowtopic));
|
||||||
|
for (char * p = lowtopic; *p; p++) {
|
||||||
|
*p = tolower(*p);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!strcmp_P(lowtopic, reinterpret_cast<PGM_P>(F_(boiler)))) {
|
||||||
return DeviceType::BOILER;
|
return DeviceType::BOILER;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!strcmp_P(topic, reinterpret_cast<PGM_P>(F_(thermostat)))) {
|
if (!strcmp_P(lowtopic, reinterpret_cast<PGM_P>(F_(thermostat)))) {
|
||||||
return DeviceType::THERMOSTAT;
|
return DeviceType::THERMOSTAT;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!strcmp_P(topic, reinterpret_cast<PGM_P>(F_(system)))) {
|
if (!strcmp_P(lowtopic, reinterpret_cast<PGM_P>(F_(system)))) {
|
||||||
return DeviceType::SYSTEM;
|
return DeviceType::SYSTEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!strcmp_P(topic, reinterpret_cast<PGM_P>(F_(heatpump)))) {
|
if (!strcmp_P(lowtopic, reinterpret_cast<PGM_P>(F_(heatpump)))) {
|
||||||
return DeviceType::HEATPUMP;
|
return DeviceType::HEATPUMP;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!strcmp_P(topic, reinterpret_cast<PGM_P>(F_(solar)))) {
|
if (!strcmp_P(lowtopic, reinterpret_cast<PGM_P>(F_(solar)))) {
|
||||||
return DeviceType::SOLAR;
|
return DeviceType::SOLAR;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!strcmp_P(topic, reinterpret_cast<PGM_P>(F_(mixer)))) {
|
if (!strcmp_P(lowtopic, reinterpret_cast<PGM_P>(F_(mixer)))) {
|
||||||
return DeviceType::MIXER;
|
return DeviceType::MIXER;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!strcmp_P(topic, reinterpret_cast<PGM_P>(F_(dallassensor)))) {
|
if (!strcmp_P(lowtopic, reinterpret_cast<PGM_P>(F_(dallassensor)))) {
|
||||||
return DeviceType::DALLASSENSOR;
|
return DeviceType::DALLASSENSOR;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -405,9 +413,9 @@ void EMSdevice::register_mqtt_topic(const std::string & topic, mqtt_subfunction_
|
|||||||
}
|
}
|
||||||
|
|
||||||
// add command to library
|
// add command to library
|
||||||
void EMSdevice::register_mqtt_cmd(const __FlashStringHelper * cmd, cmdfunction_p f, uint8_t flag) {
|
// void EMSdevice::register_cmd(const __FlashStringHelper * cmd, cmdfunction_p f, uint8_t flag) {
|
||||||
Command::add(device_type_, cmd, f, flag);
|
// Command::add(device_type_, cmd, f, flag);
|
||||||
}
|
// }
|
||||||
|
|
||||||
// register a callback function for a specific telegram type
|
// register a callback function for a specific telegram type
|
||||||
void EMSdevice::register_telegram_type(const uint16_t telegram_type_id, const __FlashStringHelper * telegram_type_name, bool fetch, process_function_p f) {
|
void EMSdevice::register_telegram_type(const uint16_t telegram_type_id, const __FlashStringHelper * telegram_type_name, bool fetch, process_function_p f) {
|
||||||
@@ -423,7 +431,16 @@ void EMSdevice::register_telegram_type(const uint16_t telegram_type_id, const __
|
|||||||
// short_name: used in Mqtt as keys
|
// short_name: used in Mqtt as keys
|
||||||
// full name: used in Web and Console unless empty (nullptr)
|
// full name: used in Web and Console unless empty (nullptr)
|
||||||
// uom: unit of measure from DeviceValueUOM
|
// uom: unit of measure from DeviceValueUOM
|
||||||
void EMSdevice::register_device_value(uint8_t tag, void * value_p, uint8_t type, const __FlashStringHelper * const * options, const __FlashStringHelper * short_name, const __FlashStringHelper * full_name, uint8_t uom) {
|
void EMSdevice::register_device_value(uint8_t tag,
|
||||||
|
void * value_p,
|
||||||
|
uint8_t type,
|
||||||
|
const __FlashStringHelper * const * options,
|
||||||
|
const __FlashStringHelper * short_name,
|
||||||
|
const __FlashStringHelper * full_name,
|
||||||
|
uint8_t uom,
|
||||||
|
bool has_cmd,
|
||||||
|
int32_t min,
|
||||||
|
uint32_t max) {
|
||||||
// init the value depending on it's type
|
// init the value depending on it's type
|
||||||
if (type == DeviceValueType::TEXT) {
|
if (type == DeviceValueType::TEXT) {
|
||||||
*(char *)(value_p) = {'\0'};
|
*(char *)(value_p) = {'\0'};
|
||||||
@@ -435,8 +452,10 @@ void EMSdevice::register_device_value(uint8_t tag, void * value_p, uint8_t type,
|
|||||||
*(uint16_t *)(value_p) = EMS_VALUE_USHORT_NOTSET;
|
*(uint16_t *)(value_p) = EMS_VALUE_USHORT_NOTSET;
|
||||||
} else if ((type == DeviceValueType::ULONG) || (type == DeviceValueType::TIME)) {
|
} else if ((type == DeviceValueType::ULONG) || (type == DeviceValueType::TIME)) {
|
||||||
*(uint32_t *)(value_p) = EMS_VALUE_ULONG_NOTSET;
|
*(uint32_t *)(value_p) = EMS_VALUE_ULONG_NOTSET;
|
||||||
|
} else if (type == DeviceValueType::BOOL) {
|
||||||
|
*(int8_t *)(value_p) = EMS_VALUE_BOOL_NOTSET; // bool is uint8_t, but other initial value
|
||||||
} else {
|
} else {
|
||||||
*(uint8_t *)(value_p) = EMS_VALUE_UINT_NOTSET; // enums, uint8_t, bool behave as uint8_t
|
*(uint8_t *)(value_p) = EMS_VALUE_UINT_NOTSET; // enums behave as uint8_t
|
||||||
}
|
}
|
||||||
|
|
||||||
// count #options
|
// count #options
|
||||||
@@ -448,14 +467,37 @@ void EMSdevice::register_device_value(uint8_t tag, void * value_p, uint8_t type,
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
devicevalues_.emplace_back(device_type_, tag, value_p, type, options, options_size, short_name, full_name, uom);
|
devicevalues_.emplace_back(device_type_, tag, value_p, type, options, options_size, short_name, full_name, uom, 0, has_cmd, min, max);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EMSdevice::register_device_value(uint8_t tag, void * value_p, uint8_t type, const __FlashStringHelper * const * options, const __FlashStringHelper * const * name, uint8_t uom, cmdfunction_p f, int32_t min, uint32_t max) {
|
||||||
|
register_device_value(tag, value_p, type, options, name[0], name[1], uom, (f != nullptr), min, max);
|
||||||
|
if (f != nullptr) {
|
||||||
|
if (tag >= TAG_HC1 && tag <= TAG_HC4) {
|
||||||
|
Command::add(device_type_, name[0], f, FLAG_HC);
|
||||||
|
} else {
|
||||||
|
Command::add(device_type_, name[0], f, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EMSdevice::register_device_value(uint8_t tag, void * value_p, uint8_t type, const __FlashStringHelper * const * options, const __FlashStringHelper * const * name, uint8_t uom, cmdfunction_p f) {
|
||||||
|
register_device_value(tag, value_p, type, options, name, uom, f, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// void EMSdevice::register_device_value(uint8_t tag, void * value_p, uint8_t type, const __FlashStringHelper * const * options, const __FlashStringHelper * const * name, uint8_t uom, int32_t min, uint32_t max) {
|
||||||
|
// register_device_value(tag, value_p, type, options, name, uom, nullptr, min, max);
|
||||||
|
// }
|
||||||
|
|
||||||
|
void EMSdevice::register_device_value(uint8_t tag, void * value_p, uint8_t type, const __FlashStringHelper * const * options, const __FlashStringHelper * const * name, uint8_t uom) {
|
||||||
|
register_device_value(tag, value_p, type, options, name, uom, nullptr, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// looks up the uom (suffix) for a given key from the device value table
|
// looks up the uom (suffix) for a given key from the device value table
|
||||||
std::string EMSdevice::get_value_uom(const char * key) {
|
std::string EMSdevice::get_value_uom(const char * key) {
|
||||||
// the key may have a suffix at the start which is between brackets. remove it.
|
// the key may have a suffix at the start which is between brackets. remove it.
|
||||||
char new_key[80];
|
char new_key[80];
|
||||||
strncpy(new_key, key, sizeof(new_key));
|
strlcpy(new_key, key, sizeof(new_key));
|
||||||
char * p = new_key;
|
char * p = new_key;
|
||||||
if (key[0] == '(') {
|
if (key[0] == '(') {
|
||||||
while ((*p++ != ')') && (*p != '\0'))
|
while ((*p++ != ')') && (*p != '\0'))
|
||||||
@@ -566,7 +608,7 @@ bool EMSdevice::generate_values_json_web(JsonObject & json) {
|
|||||||
if (sz > num_elements) {
|
if (sz > num_elements) {
|
||||||
// add the unit of measure (uom)
|
// add the unit of measure (uom)
|
||||||
if (dv.uom == DeviceValueUOM::MINUTES) {
|
if (dv.uom == DeviceValueUOM::MINUTES) {
|
||||||
data.add(nullptr);
|
data.add(nullptr); // use null for time/date
|
||||||
} else {
|
} else {
|
||||||
data.add(uom_to_string(dv.uom));
|
data.add(uom_to_string(dv.uom));
|
||||||
}
|
}
|
||||||
@@ -579,7 +621,15 @@ bool EMSdevice::generate_values_json_web(JsonObject & json) {
|
|||||||
snprintf_P(name, sizeof(name), "(%s) %s", tag_to_string(dv.tag).c_str(), uuid::read_flash_string(dv.full_name).c_str());
|
snprintf_P(name, sizeof(name), "(%s) %s", tag_to_string(dv.tag).c_str(), uuid::read_flash_string(dv.full_name).c_str());
|
||||||
data.add(name);
|
data.add(name);
|
||||||
}
|
}
|
||||||
num_elements = sz + 2;
|
|
||||||
|
// add the name of the Command function if it exists
|
||||||
|
if (dv.has_cmd) {
|
||||||
|
data.add(dv.short_name);
|
||||||
|
} else {
|
||||||
|
data.add("");
|
||||||
|
}
|
||||||
|
|
||||||
|
num_elements = sz + 3; // increase count by 3
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -587,19 +637,181 @@ bool EMSdevice::generate_values_json_web(JsonObject & json) {
|
|||||||
return (num_elements != 0);
|
return (num_elements != 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool EMSdevice::get_value_info(JsonObject & root, const char * cmd, const int8_t id) {
|
||||||
|
JsonObject json = root;
|
||||||
|
int8_t tag = id;
|
||||||
|
|
||||||
|
// check if we have hc or wwc
|
||||||
|
if (id >= 1 && id <= 4) {
|
||||||
|
tag = DeviceValueTAG::TAG_HC1 + id - 1;
|
||||||
|
} else if (id >= 8 && id <= 11) {
|
||||||
|
tag = DeviceValueTAG::TAG_WWC1 + id - 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
// search device value with this tag
|
||||||
|
for (auto & dv : devicevalues_) {
|
||||||
|
if (strcmp(cmd, Helpers::toLower(uuid::read_flash_string(dv.short_name)).c_str()) == 0 && (tag <= 0 || tag == dv.tag)) {
|
||||||
|
uint8_t divider = (dv.options_size == 1) ? Helpers::atoint(uuid::read_flash_string(dv.options[0]).c_str()) : 0;
|
||||||
|
const char * type = "type";
|
||||||
|
const char * min = "min";
|
||||||
|
const char * max = "max";
|
||||||
|
const char * value = "value";
|
||||||
|
|
||||||
|
json["name"] = dv.short_name;
|
||||||
|
if (!tag_to_mqtt(dv.tag).empty()) {
|
||||||
|
json["circuit"] = tag_to_mqtt(dv.tag);
|
||||||
|
}
|
||||||
|
switch (dv.type) {
|
||||||
|
case DeviceValueType::ENUM: {
|
||||||
|
if (Helpers::hasValue((uint8_t)(*(uint8_t *)(dv.value_p)))) {
|
||||||
|
if (Mqtt::bool_format() == BOOL_FORMAT_10) {
|
||||||
|
json[value] = (uint8_t)(*(uint8_t *)(dv.value_p));
|
||||||
|
} else {
|
||||||
|
json[value] = dv.options[*(uint8_t *)(dv.value_p)]; // text
|
||||||
|
}
|
||||||
|
}
|
||||||
|
json[type] = F_(enum);
|
||||||
|
uint8_t min_ = (uuid::read_flash_string(dv.options[0]) == "") ? 1 : 0;
|
||||||
|
json[min] = min_;
|
||||||
|
json[max] = dv.options_size - 1;
|
||||||
|
JsonArray enum_ = json.createNestedArray(F_(enum));
|
||||||
|
for (uint8_t i = min_; i < dv.options_size; i++) {
|
||||||
|
enum_.add(dv.options[i]);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case DeviceValueType::USHORT:
|
||||||
|
if (Helpers::hasValue(*(uint16_t *)(dv.value_p))) {
|
||||||
|
json[value] = Helpers::round2(*(uint16_t *)(dv.value_p), divider);
|
||||||
|
}
|
||||||
|
json[type] = F_(number);
|
||||||
|
json[min] = 0;
|
||||||
|
json[max] = divider ? EMS_VALUE_USHORT_NOTSET / divider : EMS_VALUE_USHORT_NOTSET;
|
||||||
|
break;
|
||||||
|
case DeviceValueType::UINT:
|
||||||
|
if (Helpers::hasValue(*(uint8_t *)(dv.value_p))) {
|
||||||
|
json[value] = Helpers::round2(*(uint8_t *)(dv.value_p), divider);
|
||||||
|
}
|
||||||
|
json[type] = F_(number);
|
||||||
|
json[min] = 0;
|
||||||
|
if (dv.uom == DeviceValueUOM::PERCENT) {
|
||||||
|
json[max] = 100;
|
||||||
|
} else {
|
||||||
|
json[max] = divider ? EMS_VALUE_UINT_NOTSET / divider : EMS_VALUE_UINT_NOTSET;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case DeviceValueType::SHORT:
|
||||||
|
if (Helpers::hasValue(*(int16_t *)(dv.value_p))) {
|
||||||
|
json[value] = Helpers::round2(*(int16_t *)(dv.value_p), divider);
|
||||||
|
}
|
||||||
|
json[type] = F_(number);
|
||||||
|
json[min] = divider ? -EMS_VALUE_SHORT_NOTSET / divider : -EMS_VALUE_SHORT_NOTSET;
|
||||||
|
json[max] = divider ? EMS_VALUE_SHORT_NOTSET / divider : EMS_VALUE_SHORT_NOTSET;
|
||||||
|
break;
|
||||||
|
case DeviceValueType::INT:
|
||||||
|
if (Helpers::hasValue(*(int8_t *)(dv.value_p))) {
|
||||||
|
json[value] = Helpers::round2(*(int8_t *)(dv.value_p), divider);
|
||||||
|
}
|
||||||
|
json[type] = F_(number);
|
||||||
|
if (dv.uom == DeviceValueUOM::PERCENT) {
|
||||||
|
json[min] = -100;
|
||||||
|
json[max] = 100;
|
||||||
|
} else {
|
||||||
|
json[min] = divider ? -EMS_VALUE_INT_NOTSET / divider : -EMS_VALUE_INT_NOTSET;
|
||||||
|
json[max] = divider ? EMS_VALUE_INT_NOTSET / divider : EMS_VALUE_INT_NOTSET;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case DeviceValueType::ULONG:
|
||||||
|
if (Helpers::hasValue(*(uint32_t *)(dv.value_p))) {
|
||||||
|
json[value] = Helpers::round2(*(uint32_t *)(dv.value_p), divider);
|
||||||
|
}
|
||||||
|
json[type] = F_(number);
|
||||||
|
json[min] = 0;
|
||||||
|
json[max] = divider ? EMS_VALUE_ULONG_NOTSET / divider : EMS_VALUE_ULONG_NOTSET;
|
||||||
|
break;
|
||||||
|
case DeviceValueType::BOOL: {
|
||||||
|
if (Helpers::hasValue(*(uint8_t *)(dv.value_p), EMS_VALUE_BOOL)) {
|
||||||
|
if (dv.options_size == 2) {
|
||||||
|
json[value] = (bool)(*(uint8_t *)(dv.value_p)) ? dv.options[0] : dv.options[1];
|
||||||
|
} else if (Mqtt::bool_format() == BOOL_FORMAT_ONOFF) {
|
||||||
|
json[value] = (bool)(*(uint8_t *)(dv.value_p)) ? F_(on) : F_(off);
|
||||||
|
} else if (Mqtt::bool_format() == BOOL_FORMAT_ONOFF_CAP) {
|
||||||
|
json[value] = (bool)(*(uint8_t *)(dv.value_p)) ? F_(ON) : F_(OFF);
|
||||||
|
} else if (Mqtt::bool_format() == BOOL_FORMAT_TRUEFALSE) {
|
||||||
|
json[value] = (bool)(*(uint8_t *)(dv.value_p)) ? true : false;
|
||||||
|
} else {
|
||||||
|
json[value] = (bool)(*(uint8_t *)(dv.value_p)) ? 1 : 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
json[type] = F("boolean");
|
||||||
|
json[min] = 0;
|
||||||
|
json[max] = 1;
|
||||||
|
JsonArray enum_ = json.createNestedArray(F_(enum));
|
||||||
|
if (dv.options_size == 2) {
|
||||||
|
enum_.add(dv.options[1]);
|
||||||
|
enum_.add(dv.options[0]);
|
||||||
|
} else if (Mqtt::bool_format() == BOOL_FORMAT_ONOFF) {
|
||||||
|
enum_.add(F_(off));
|
||||||
|
enum_.add(F_(on));
|
||||||
|
} else if (Mqtt::bool_format() == BOOL_FORMAT_ONOFF_CAP) {
|
||||||
|
enum_.add(F_(OFF));
|
||||||
|
enum_.add(F_(ON));
|
||||||
|
} else if (Mqtt::bool_format() == BOOL_FORMAT_TRUEFALSE) {
|
||||||
|
enum_.add(false);
|
||||||
|
enum_.add(true);
|
||||||
|
} else {
|
||||||
|
enum_.add(0);
|
||||||
|
enum_.add(1);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case DeviceValueType::TIME:
|
||||||
|
if (Helpers::hasValue(*(uint32_t *)(dv.value_p))) {
|
||||||
|
json[value] = (divider) ? *(uint32_t *)(dv.value_p) / divider : *(uint32_t *)(dv.value_p);
|
||||||
|
}
|
||||||
|
json[type] = F_(number);
|
||||||
|
json[min] = 0;
|
||||||
|
json[max] = divider ? EMS_VALUE_ULONG_NOTSET / divider : EMS_VALUE_ULONG_NOTSET;
|
||||||
|
break;
|
||||||
|
case DeviceValueType::TEXT:
|
||||||
|
if (Helpers::hasValue((char *)(dv.value_p))) {
|
||||||
|
json[value] = (char *)(dv.value_p);
|
||||||
|
}
|
||||||
|
json[type] = F_(text);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
json[type] = F_(unknown);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (dv.uom != DeviceValueUOM::NONE) {
|
||||||
|
json["unit"] = EMSdevice::uom_to_string(dv.uom);
|
||||||
|
}
|
||||||
|
json["writeable"] = dv.has_cmd;
|
||||||
|
// if we have individual limits, overwrite the common limits
|
||||||
|
if (dv.min != 0 || dv.max != 0) {
|
||||||
|
json[min] = dv.min;
|
||||||
|
json[max] = dv.max;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// For each value in the device create the json object pair and add it to given json
|
// For each value in the device create the json object pair and add it to given json
|
||||||
// return false if empty
|
// return false if empty
|
||||||
// this is used to create both the MQTT payloads and Console messages (console = true)
|
// this is used to create both the MQTT payloads and Console messages (console = true)
|
||||||
bool EMSdevice::generate_values_json(JsonObject & root, const uint8_t tag_filter, const bool nested, const bool console) {
|
bool EMSdevice::generate_values_json(JsonObject & root, const uint8_t tag_filter, const bool nested, const bool console) {
|
||||||
bool has_value = false; // to see if we've added a value. it's faster than doing a json.size() at the end
|
bool has_values = false; // to see if we've added a value. it's faster than doing a json.size() at the end
|
||||||
uint8_t old_tag = 255; // NAN
|
uint8_t old_tag = 255; // NAN
|
||||||
JsonObject json = root;
|
JsonObject json = root;
|
||||||
|
|
||||||
for (const auto & dv : devicevalues_) {
|
for (auto & dv : devicevalues_) {
|
||||||
|
bool has_value = false;
|
||||||
// only show if tag is either empty (TAG_NONE) or matches a value
|
// only show if tag is either empty (TAG_NONE) or matches a value
|
||||||
// and don't show if full_name is empty unless we're outputing for mqtt payloads
|
// and don't show if full_name is empty unless we're outputing for mqtt payloads
|
||||||
// for nested we use all values
|
// for nested we use all values, dont show command only (have_cmd and no fulname)
|
||||||
if (((nested) || tag_filter == DeviceValueTAG::TAG_NONE || (tag_filter == dv.tag)) && (dv.full_name != nullptr || !console)) {
|
if (((nested) || tag_filter == DeviceValueTAG::TAG_NONE || (tag_filter == dv.tag)) && (dv.full_name != nullptr || !console) && !(dv.full_name == nullptr && dv.has_cmd)) {
|
||||||
// we have a tag if it matches the filter given, and that the tag name is not empty/""
|
// we have a tag if it matches the filter given, and that the tag name is not empty/""
|
||||||
bool have_tag = ((dv.tag != tag_filter) && !tag_to_string(dv.tag).empty());
|
bool have_tag = ((dv.tag != tag_filter) && !tag_to_string(dv.tag).empty());
|
||||||
|
|
||||||
@@ -626,7 +838,7 @@ bool EMSdevice::generate_values_json(JsonObject & root, const uint8_t tag_filter
|
|||||||
// handle Booleans (true, false)
|
// handle Booleans (true, false)
|
||||||
if ((dv.type == DeviceValueType::BOOL) && Helpers::hasValue(*(uint8_t *)(dv.value_p), EMS_VALUE_BOOL)) {
|
if ((dv.type == DeviceValueType::BOOL) && Helpers::hasValue(*(uint8_t *)(dv.value_p), EMS_VALUE_BOOL)) {
|
||||||
// see if we have options for the bool's
|
// see if we have options for the bool's
|
||||||
if (dv.options_size == 2) {
|
if (dv.options_size == 2 && Mqtt::bool_format() != BOOL_FORMAT_10) {
|
||||||
json[name] = *(uint8_t *)(dv.value_p) ? dv.options[0] : dv.options[1];
|
json[name] = *(uint8_t *)(dv.value_p) ? dv.options[0] : dv.options[1];
|
||||||
has_value = true;
|
has_value = true;
|
||||||
} else {
|
} else {
|
||||||
@@ -727,20 +939,34 @@ bool EMSdevice::generate_values_json(JsonObject & root, const uint8_t tag_filter
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
dv.ha |= has_value ? DeviceValueHA::HA_VALUE : DeviceValueHA::HA_NONE;
|
||||||
|
has_values |= has_value;
|
||||||
}
|
}
|
||||||
|
|
||||||
return has_value;
|
return has_values;
|
||||||
}
|
}
|
||||||
|
|
||||||
// create the Home Assistant configs for each value
|
// create the Home Assistant configs for each value
|
||||||
// this is called when an MQTT publish is done via an EMS Device, and only done once
|
// this is called when an MQTT publish is done via an EMS Device
|
||||||
void EMSdevice::publish_mqtt_ha_sensor() {
|
void EMSdevice::publish_mqtt_ha_sensor() {
|
||||||
for (const auto & dv : devicevalues_) {
|
for (auto & dv : devicevalues_) {
|
||||||
Mqtt::publish_mqtt_ha_sensor(dv.type, dv.tag, dv.full_name, device_type_, dv.short_name, dv.uom);
|
if (dv.ha == DeviceValueHA::HA_VALUE) {
|
||||||
|
Mqtt::publish_mqtt_ha_sensor(dv.type, dv.tag, dv.full_name, device_type_, dv.short_name, dv.uom);
|
||||||
|
dv.ha |= DeviceValueHA::HA_DONE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
if (!ha_config_done()) {
|
||||||
|
bool ok = publish_ha_config();
|
||||||
|
ha_config_done(ok); // see if it worked
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool ok = publish_ha_config();
|
void EMSdevice::ha_config_clear() {
|
||||||
ha_config_done(ok); // see if it worked
|
for (auto & dv : devicevalues_) {
|
||||||
|
// dv.ha &= ~DeviceValueHA::HA_DONE; // repubish all with values
|
||||||
|
dv.ha = DeviceValueHA::HA_NONE; // also wait for new value
|
||||||
|
}
|
||||||
|
ha_config_done(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// return the name of the telegram type
|
// return the name of the telegram type
|
||||||
@@ -800,8 +1026,8 @@ void EMSdevice::write_command(const uint16_t type_id, const uint8_t offset, cons
|
|||||||
}
|
}
|
||||||
|
|
||||||
// send Tx read command to the device
|
// send Tx read command to the device
|
||||||
void EMSdevice::read_command(const uint16_t type_id) {
|
void EMSdevice::read_command(const uint16_t type_id, const uint8_t offset, const uint8_t length) {
|
||||||
EMSESP::send_read_request(type_id, device_id());
|
EMSESP::send_read_request(type_id, device_id(), offset, length);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace emsesp
|
} // namespace emsesp
|
||||||
|
|||||||
104
src/emsdevice.h
104
src/emsdevice.h
@@ -55,61 +55,16 @@ enum DeviceValueType : uint8_t {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Unit Of Measurement mapping - maps to DeviceValueUOM_s in emsdevice.cpp
|
// Unit Of Measurement mapping - maps to DeviceValueUOM_s in emsdevice.cpp
|
||||||
// sequence is important!
|
|
||||||
// uom - also used with HA
|
// uom - also used with HA
|
||||||
MAKE_PSTR(percent, "%")
|
// sequence is important!
|
||||||
MAKE_PSTR(degrees, "°C")
|
enum DeviceValueUOM : uint8_t { NONE = 0, DEGREES, PERCENT, LMIN, KWH, WH, HOURS, MINUTES, UA, BAR, KW, PUMP };
|
||||||
MAKE_PSTR(kwh, "kWh")
|
|
||||||
MAKE_PSTR(wh, "Wh")
|
|
||||||
MAKE_PSTR(bar, "bar")
|
|
||||||
MAKE_PSTR(minutes, "minutes")
|
|
||||||
MAKE_PSTR(hours, "hours")
|
|
||||||
MAKE_PSTR(ua, "uA")
|
|
||||||
MAKE_PSTR(lmin, "l/min")
|
|
||||||
enum DeviceValueUOM : uint8_t { NONE = 0, DEGREES, PERCENT, LMIN, KWH, WH, HOURS, MINUTES, UA, BAR, PUMP };
|
|
||||||
|
|
||||||
// TAG mapping - maps to DeviceValueTAG_s in emsdevice.cpp
|
// TAG mapping - maps to DeviceValueTAG_s in emsdevice.cpp
|
||||||
// use empty string if want to suppress showing tags
|
|
||||||
MAKE_PSTR(tag_none, "")
|
|
||||||
MAKE_PSTR(tag_heartbeat, "")
|
|
||||||
MAKE_PSTR(tag_boiler_data, "")
|
|
||||||
MAKE_PSTR(tag_boiler_data_ww, "warm water")
|
|
||||||
MAKE_PSTR(tag_thermostat_data, "")
|
|
||||||
MAKE_PSTR(tag_hc1, "hc1")
|
|
||||||
MAKE_PSTR(tag_hc2, "hc2")
|
|
||||||
MAKE_PSTR(tag_hc3, "hc3")
|
|
||||||
MAKE_PSTR(tag_hc4, "hc4")
|
|
||||||
MAKE_PSTR(tag_wwc1, "wwc1")
|
|
||||||
MAKE_PSTR(tag_wwc2, "wwc2")
|
|
||||||
MAKE_PSTR(tag_wwc3, "wwc3")
|
|
||||||
MAKE_PSTR(tag_wwc4, "wwc4")
|
|
||||||
MAKE_PSTR(tag_hs1, "hs1")
|
|
||||||
MAKE_PSTR(tag_hs2, "hs2")
|
|
||||||
MAKE_PSTR(tag_hs3, "hs3")
|
|
||||||
MAKE_PSTR(tag_hs4, "hs4")
|
|
||||||
MAKE_PSTR(tag_hs5, "hs5")
|
|
||||||
MAKE_PSTR(tag_hs6, "hs6")
|
|
||||||
MAKE_PSTR(tag_hs7, "hs7")
|
|
||||||
MAKE_PSTR(tag_hs8, "hs8")
|
|
||||||
MAKE_PSTR(tag_hs9, "hs9")
|
|
||||||
MAKE_PSTR(tag_hs10, "hs10")
|
|
||||||
MAKE_PSTR(tag_hs11, "hs11")
|
|
||||||
MAKE_PSTR(tag_hs12, "hs12")
|
|
||||||
MAKE_PSTR(tag_hs13, "hs13")
|
|
||||||
MAKE_PSTR(tag_hs14, "hs14")
|
|
||||||
MAKE_PSTR(tag_hs15, "hs15")
|
|
||||||
MAKE_PSTR(tag_hs16, "hs16")
|
|
||||||
|
|
||||||
// MQTT topic names
|
|
||||||
MAKE_PSTR(tag_heartbeat_mqtt, "heartbeat")
|
|
||||||
MAKE_PSTR(tag_boiler_data_mqtt, "")
|
|
||||||
MAKE_PSTR(tag_boiler_data_ww_mqtt, "ww")
|
|
||||||
|
|
||||||
enum DeviceValueTAG : uint8_t {
|
enum DeviceValueTAG : uint8_t {
|
||||||
TAG_NONE = 0, // wild card
|
TAG_NONE = 0, // wild card
|
||||||
TAG_HEARTBEAT,
|
TAG_HEARTBEAT,
|
||||||
TAG_BOILER_DATA,
|
TAG_BOILER_DATA,
|
||||||
TAG_BOILER_DATA_WW,
|
TAG_DEVICE_DATA_WW,
|
||||||
TAG_THERMOSTAT_DATA,
|
TAG_THERMOSTAT_DATA,
|
||||||
TAG_HC1,
|
TAG_HC1,
|
||||||
TAG_HC2,
|
TAG_HC2,
|
||||||
@@ -139,12 +94,10 @@ enum DeviceValueTAG : uint8_t {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// mqtt flags for command subscriptions
|
// mqtt flags for command subscriptions
|
||||||
enum MqttSubFlag : uint8_t {
|
enum MqttSubFlag : uint8_t { FLAG_NORMAL = 0, FLAG_HC, FLAG_WWC, FLAG_NOSUB };
|
||||||
FLAG_NORMAL = 0,
|
|
||||||
FLAG_HC,
|
// mqtt-HA flags
|
||||||
FLAG_WWC,
|
enum DeviceValueHA : uint8_t { HA_NONE = 0, HA_VALUE, HA_DONE };
|
||||||
FLAG_NOSUB
|
|
||||||
};
|
|
||||||
|
|
||||||
class EMSdevice {
|
class EMSdevice {
|
||||||
public:
|
public:
|
||||||
@@ -265,6 +218,7 @@ class EMSdevice {
|
|||||||
bool handle_telegram(std::shared_ptr<const Telegram> telegram);
|
bool handle_telegram(std::shared_ptr<const Telegram> telegram);
|
||||||
|
|
||||||
std::string get_value_uom(const char * key);
|
std::string get_value_uom(const char * key);
|
||||||
|
bool get_value_info(JsonObject & root, const char * cmd, const int8_t id);
|
||||||
bool generate_values_json(JsonObject & json, const uint8_t tag_filter, const bool nested, const bool console = false);
|
bool generate_values_json(JsonObject & json, const uint8_t tag_filter, const bool nested, const bool console = false);
|
||||||
bool generate_values_json_web(JsonObject & json);
|
bool generate_values_json_web(JsonObject & json);
|
||||||
|
|
||||||
@@ -274,15 +228,22 @@ class EMSdevice {
|
|||||||
const __FlashStringHelper * const * options,
|
const __FlashStringHelper * const * options,
|
||||||
const __FlashStringHelper * short_name,
|
const __FlashStringHelper * short_name,
|
||||||
const __FlashStringHelper * full_name,
|
const __FlashStringHelper * full_name,
|
||||||
uint8_t uom = DeviceValueUOM::NONE);
|
uint8_t uom,
|
||||||
|
bool has_cmd,
|
||||||
|
int32_t min,
|
||||||
|
uint32_t max);
|
||||||
|
void register_device_value(uint8_t tag, void * value_p, uint8_t type, const __FlashStringHelper * const * options, const __FlashStringHelper * const * name, uint8_t uom, cmdfunction_p f, int32_t min, uint32_t max);
|
||||||
|
void register_device_value(uint8_t tag, void * value_p, uint8_t type, const __FlashStringHelper * const * options, const __FlashStringHelper * const * name, uint8_t uom, cmdfunction_p f);
|
||||||
|
void register_device_value(uint8_t tag, void * value_p, uint8_t type, const __FlashStringHelper * const * options, const __FlashStringHelper * const * name, uint8_t uom);
|
||||||
|
// void register_device_value(uint8_t tag, void * value_p, uint8_t type, const __FlashStringHelper * const * options, const __FlashStringHelper * const * name, uint8_t uom, int32_t min, uint32_t max);
|
||||||
|
|
||||||
void write_command(const uint16_t type_id, const uint8_t offset, uint8_t * message_data, const uint8_t message_length, const uint16_t validate_typeid);
|
void write_command(const uint16_t type_id, const uint8_t offset, uint8_t * message_data, const uint8_t message_length, const uint16_t validate_typeid);
|
||||||
void write_command(const uint16_t type_id, const uint8_t offset, const uint8_t value, const uint16_t validate_typeid);
|
void write_command(const uint16_t type_id, const uint8_t offset, const uint8_t value, const uint16_t validate_typeid);
|
||||||
void write_command(const uint16_t type_id, const uint8_t offset, const uint8_t value);
|
void write_command(const uint16_t type_id, const uint8_t offset, const uint8_t value);
|
||||||
void read_command(const uint16_t type_id);
|
void read_command(const uint16_t type_id, uint8_t offset = 0, uint8_t length = 0);
|
||||||
|
|
||||||
void register_mqtt_topic(const std::string & topic, mqtt_subfunction_p f);
|
void register_mqtt_topic(const std::string & topic, mqtt_subfunction_p f);
|
||||||
void register_mqtt_cmd(const __FlashStringHelper * cmd, cmdfunction_p f, uint8_t flag = 0);
|
// void register_cmd(const __FlashStringHelper * cmd, cmdfunction_p f, uint8_t flag = 0);
|
||||||
|
|
||||||
void publish_mqtt_ha_sensor();
|
void publish_mqtt_ha_sensor();
|
||||||
|
|
||||||
@@ -300,6 +261,8 @@ class EMSdevice {
|
|||||||
ha_config_done_ = v;
|
ha_config_done_ = v;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ha_config_clear();
|
||||||
|
|
||||||
enum Brand : uint8_t {
|
enum Brand : uint8_t {
|
||||||
NO_BRAND = 0, // 0
|
NO_BRAND = 0, // 0
|
||||||
BOSCH, // 1
|
BOSCH, // 1
|
||||||
@@ -338,6 +301,12 @@ class EMSdevice {
|
|||||||
// device flags: The lower 4 bits hold the unique identifier, the upper 4 bits are used for specific flags
|
// device flags: The lower 4 bits hold the unique identifier, the upper 4 bits are used for specific flags
|
||||||
static constexpr uint8_t EMS_DEVICE_FLAG_NONE = 0;
|
static constexpr uint8_t EMS_DEVICE_FLAG_NONE = 0;
|
||||||
|
|
||||||
|
// Boiler
|
||||||
|
static constexpr uint8_t EMS_DEVICE_FLAG_EMS = 1;
|
||||||
|
static constexpr uint8_t EMS_DEVICE_FLAG_EMSPLUS = 2;
|
||||||
|
static constexpr uint8_t EMS_DEVICE_FLAG_HT3 = 3;
|
||||||
|
static constexpr uint8_t EMS_DEVICE_FLAG_HEATPUMP = 4;
|
||||||
|
|
||||||
// Solar Module
|
// Solar Module
|
||||||
static constexpr uint8_t EMS_DEVICE_FLAG_SM10 = 1;
|
static constexpr uint8_t EMS_DEVICE_FLAG_SM10 = 1;
|
||||||
static constexpr uint8_t EMS_DEVICE_FLAG_SM100 = 2;
|
static constexpr uint8_t EMS_DEVICE_FLAG_SM100 = 2;
|
||||||
@@ -354,13 +323,14 @@ class EMSdevice {
|
|||||||
static constexpr uint8_t EMS_DEVICE_FLAG_EASY = 1;
|
static constexpr uint8_t EMS_DEVICE_FLAG_EASY = 1;
|
||||||
static constexpr uint8_t EMS_DEVICE_FLAG_RC10 = 2;
|
static constexpr uint8_t EMS_DEVICE_FLAG_RC10 = 2;
|
||||||
static constexpr uint8_t EMS_DEVICE_FLAG_RC20 = 3;
|
static constexpr uint8_t EMS_DEVICE_FLAG_RC20 = 3;
|
||||||
static constexpr uint8_t EMS_DEVICE_FLAG_RC20_2 = 4; // Variation on RC20, Older, like ES72
|
static constexpr uint8_t EMS_DEVICE_FLAG_RC20_N = 4; // Variation on RC20, Older, like ES72
|
||||||
static constexpr uint8_t EMS_DEVICE_FLAG_RC30_1 = 5; // variation on RC30, Newer models
|
static constexpr uint8_t EMS_DEVICE_FLAG_RC30_N = 5; // variation on RC30, Newer models
|
||||||
static constexpr uint8_t EMS_DEVICE_FLAG_RC30 = 6;
|
static constexpr uint8_t EMS_DEVICE_FLAG_RC30 = 6;
|
||||||
static constexpr uint8_t EMS_DEVICE_FLAG_RC35 = 7;
|
static constexpr uint8_t EMS_DEVICE_FLAG_RC35 = 7;
|
||||||
static constexpr uint8_t EMS_DEVICE_FLAG_RC300 = 8;
|
static constexpr uint8_t EMS_DEVICE_FLAG_RC300 = 8;
|
||||||
static constexpr uint8_t EMS_DEVICE_FLAG_RC100 = 9;
|
static constexpr uint8_t EMS_DEVICE_FLAG_RC100 = 9;
|
||||||
static constexpr uint8_t EMS_DEVICE_FLAG_JUNKERS = 10;
|
static constexpr uint8_t EMS_DEVICE_FLAG_JUNKERS = 10;
|
||||||
|
static constexpr uint8_t EMS_DEVICE_FLAG_CRF = 11; // CRF200 only monitor
|
||||||
|
|
||||||
void reserve_device_values(uint8_t elements) {
|
void reserve_device_values(uint8_t elements) {
|
||||||
devicevalues_.reserve(elements);
|
devicevalues_.reserve(elements);
|
||||||
@@ -407,6 +377,10 @@ class EMSdevice {
|
|||||||
const __FlashStringHelper * short_name; // used in MQTT
|
const __FlashStringHelper * short_name; // used in MQTT
|
||||||
const __FlashStringHelper * full_name; // used in Web and Console
|
const __FlashStringHelper * full_name; // used in Web and Console
|
||||||
uint8_t uom; // DeviceValueUOM::*
|
uint8_t uom; // DeviceValueUOM::*
|
||||||
|
uint8_t ha; // DevcieValueHA::
|
||||||
|
bool has_cmd; // true if there is a Console/MQTT command which matches the short_name
|
||||||
|
int32_t min;
|
||||||
|
uint32_t max;
|
||||||
|
|
||||||
DeviceValue(uint8_t device_type,
|
DeviceValue(uint8_t device_type,
|
||||||
uint8_t tag,
|
uint8_t tag,
|
||||||
@@ -416,7 +390,11 @@ class EMSdevice {
|
|||||||
uint8_t options_size,
|
uint8_t options_size,
|
||||||
const __FlashStringHelper * short_name,
|
const __FlashStringHelper * short_name,
|
||||||
const __FlashStringHelper * full_name,
|
const __FlashStringHelper * full_name,
|
||||||
uint8_t uom)
|
uint8_t uom,
|
||||||
|
uint8_t ha,
|
||||||
|
bool has_cmd,
|
||||||
|
int32_t min,
|
||||||
|
uint32_t max)
|
||||||
: device_type(device_type)
|
: device_type(device_type)
|
||||||
, tag(tag)
|
, tag(tag)
|
||||||
, value_p(value_p)
|
, value_p(value_p)
|
||||||
@@ -425,7 +403,11 @@ class EMSdevice {
|
|||||||
, options_size(options_size)
|
, options_size(options_size)
|
||||||
, short_name(short_name)
|
, short_name(short_name)
|
||||||
, full_name(full_name)
|
, full_name(full_name)
|
||||||
, uom(uom) {
|
, uom(uom)
|
||||||
|
, ha(ha)
|
||||||
|
, has_cmd(has_cmd)
|
||||||
|
, min(min)
|
||||||
|
, max(max) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const std::vector<DeviceValue> devicevalues() const;
|
const std::vector<DeviceValue> devicevalues() const;
|
||||||
|
|||||||
@@ -423,7 +423,7 @@ void EMSESP::reset_mqtt_ha() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (const auto & emsdevice : emsdevices) {
|
for (const auto & emsdevice : emsdevices) {
|
||||||
emsdevice->ha_config_done(false);
|
emsdevice->ha_config_clear();
|
||||||
}
|
}
|
||||||
dallassensor_.reload();
|
dallassensor_.reload();
|
||||||
}
|
}
|
||||||
@@ -435,13 +435,13 @@ void EMSESP::publish_device_values(uint8_t device_type) {
|
|||||||
JsonObject json = doc.to<JsonObject>();
|
JsonObject json = doc.to<JsonObject>();
|
||||||
bool need_publish = false;
|
bool need_publish = false;
|
||||||
|
|
||||||
bool nested = Mqtt::nested_format();
|
uint8_t nested = Mqtt::nested_format();
|
||||||
|
|
||||||
// group by device type
|
// group by device type
|
||||||
for (const auto & emsdevice : emsdevices) {
|
for (const auto & emsdevice : emsdevices) {
|
||||||
if (emsdevice && (emsdevice->device_type() == device_type)) {
|
if (emsdevice && (emsdevice->device_type() == device_type)) {
|
||||||
// if we're using HA and it's not already done, send the config topics first. only do this once
|
// if we're using HA, done is checked for each sensor in devices
|
||||||
if (Mqtt::ha_enabled() && (!emsdevice->ha_config_done())) {
|
if (Mqtt::ha_enabled()) {
|
||||||
emsdevice->publish_mqtt_ha_sensor(); // create the configs for each value as a sensor
|
emsdevice->publish_mqtt_ha_sensor(); // create the configs for each value as a sensor
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -450,8 +450,8 @@ void EMSESP::publish_device_values(uint8_t device_type) {
|
|||||||
emsdevice->generate_values_json(json, DeviceValueTAG::TAG_BOILER_DATA, false);
|
emsdevice->generate_values_json(json, DeviceValueTAG::TAG_BOILER_DATA, false);
|
||||||
Mqtt::publish(Mqtt::tag_to_topic(device_type, DeviceValueTAG::TAG_BOILER_DATA), json);
|
Mqtt::publish(Mqtt::tag_to_topic(device_type, DeviceValueTAG::TAG_BOILER_DATA), json);
|
||||||
json.clear();
|
json.clear();
|
||||||
emsdevice->generate_values_json(json, DeviceValueTAG::TAG_BOILER_DATA_WW, false);
|
emsdevice->generate_values_json(json, DeviceValueTAG::TAG_DEVICE_DATA_WW, false);
|
||||||
Mqtt::publish(Mqtt::tag_to_topic(device_type, DeviceValueTAG::TAG_BOILER_DATA_WW), json);
|
Mqtt::publish(Mqtt::tag_to_topic(device_type, DeviceValueTAG::TAG_DEVICE_DATA_WW), json);
|
||||||
need_publish = false;
|
need_publish = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -511,6 +511,10 @@ void EMSESP::publish_other_values() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void EMSESP::publish_sensor_values(const bool time, const bool force) {
|
void EMSESP::publish_sensor_values(const bool time, const bool force) {
|
||||||
|
if (!dallas_enabled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (dallassensor_.updated_values() || time || force) {
|
if (dallassensor_.updated_values() || time || force) {
|
||||||
dallassensor_.publish_values(force);
|
dallassensor_.publish_values(force);
|
||||||
}
|
}
|
||||||
@@ -540,7 +544,37 @@ void EMSESP::publish_response(std::shared_ptr<const Telegram> telegram) {
|
|||||||
doc["value"] = value;
|
doc["value"] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
Mqtt::publish(F("response"), doc.as<JsonObject>());
|
Mqtt::publish(F_(response), doc.as<JsonObject>());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EMSESP::get_device_value_info(JsonObject & root, const char * cmd, const int8_t id, const uint8_t devicetype) {
|
||||||
|
for (const auto & emsdevice : emsdevices) {
|
||||||
|
if (emsdevice->device_type() == devicetype) {
|
||||||
|
return emsdevice->get_value_info(root, cmd, id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (devicetype == DeviceType::DALLASSENSOR) {
|
||||||
|
uint8_t i = 1;
|
||||||
|
for (const auto & sensor : EMSESP::sensor_devices()) {
|
||||||
|
char sensorID[10];
|
||||||
|
snprintf_P(sensorID, 10, PSTR("sensor%d"), i++);
|
||||||
|
if ((strcmp(cmd, sensorID) == 0) || (strcmp(cmd, Helpers::toLower(sensor.to_string()).c_str()) == 0)) {
|
||||||
|
root["name"] = sensor.to_string();
|
||||||
|
if (Helpers::hasValue(sensor.temperature_c)) {
|
||||||
|
root["value"] = (float)(sensor.temperature_c) / 10;
|
||||||
|
}
|
||||||
|
root["type"] = F_(number);
|
||||||
|
root["min"] = -55;
|
||||||
|
root["max"] = 125;
|
||||||
|
root["unit"] = EMSdevice::uom_to_string(DeviceValueUOM::DEGREES);
|
||||||
|
root["writeable"] = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// search for recognized device_ids : Me, All, otherwise print hex value
|
// search for recognized device_ids : Me, All, otherwise print hex value
|
||||||
@@ -947,14 +981,9 @@ bool EMSESP::command_info(uint8_t device_type, JsonObject & json, const int8_t i
|
|||||||
return has_value;
|
return has_value;
|
||||||
}
|
}
|
||||||
|
|
||||||
// send a read request, passing it into to the Tx Service, with offset
|
// send a read request, passing it into to the Tx Service, with optional offset and length
|
||||||
void EMSESP::send_read_request(const uint16_t type_id, const uint8_t dest, const uint8_t offset) {
|
void EMSESP::send_read_request(const uint16_t type_id, const uint8_t dest, const uint8_t offset, const uint8_t length) {
|
||||||
txservice_.read_request(type_id, dest, offset);
|
txservice_.read_request(type_id, dest, offset, length);
|
||||||
}
|
|
||||||
|
|
||||||
// send a read request, passing it into to the Tx Service, with no offset
|
|
||||||
void EMSESP::send_read_request(const uint16_t type_id, const uint8_t dest) {
|
|
||||||
txservice_.read_request(type_id, dest, 0); // 0 = no offset
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// sends write request
|
// sends write request
|
||||||
|
|||||||
11
src/emsesp.h
11
src/emsesp.h
@@ -61,7 +61,7 @@
|
|||||||
#define EMSESP_JSON_SIZE_MEDIUM_DYN 1024 // for large json docs, using DynamicJsonDocument
|
#define EMSESP_JSON_SIZE_MEDIUM_DYN 1024 // for large json docs, using DynamicJsonDocument
|
||||||
#define EMSESP_JSON_SIZE_LARGE_DYN 2048 // for very large json docs, using DynamicJsonDocument
|
#define EMSESP_JSON_SIZE_LARGE_DYN 2048 // for very large json docs, using DynamicJsonDocument
|
||||||
#define EMSESP_JSON_SIZE_XLARGE_DYN 4096 // for very very large json docs, using DynamicJsonDocument
|
#define EMSESP_JSON_SIZE_XLARGE_DYN 4096 // for very very large json docs, using DynamicJsonDocument
|
||||||
#define EMSESP_JSON_SIZE_XXLARGE_DYN 5120 // for extra very very large json docs, using DynamicJsonDocument
|
#define EMSESP_JSON_SIZE_XXLARGE_DYN 8192 // for extra very very large json docs, using DynamicJsonDocument
|
||||||
|
|
||||||
// helpers for callback functions
|
// helpers for callback functions
|
||||||
#define MAKE_PF_CB(__f) [&](std::shared_ptr<const Telegram> t) { __f(t); } // for process function callbacks to register_telegram_type()
|
#define MAKE_PF_CB(__f) [&](std::shared_ptr<const Telegram> t) { __f(t); } // for process function callbacks to register_telegram_type()
|
||||||
@@ -92,8 +92,7 @@ class EMSESP {
|
|||||||
static bool process_telegram(std::shared_ptr<const Telegram> telegram);
|
static bool process_telegram(std::shared_ptr<const Telegram> telegram);
|
||||||
static std::string pretty_telegram(std::shared_ptr<const Telegram> telegram);
|
static std::string pretty_telegram(std::shared_ptr<const Telegram> telegram);
|
||||||
|
|
||||||
static void send_read_request(const uint16_t type_id, const uint8_t dest);
|
static void send_read_request(const uint16_t type_id, const uint8_t dest, const uint8_t offset = 0, const uint8_t length = 0);
|
||||||
static void send_read_request(const uint16_t type_id, const uint8_t dest, const uint8_t offset);
|
|
||||||
static void send_write_request(const uint16_t type_id, const uint8_t dest, const uint8_t offset, uint8_t * message_data, const uint8_t message_length, const uint16_t validate_typeid);
|
static void send_write_request(const uint16_t type_id, const uint8_t dest, const uint8_t offset, uint8_t * message_data, const uint8_t message_length, const uint16_t validate_typeid);
|
||||||
static void send_write_request(const uint16_t type_id, const uint8_t dest, const uint8_t offset, const uint8_t value);
|
static void send_write_request(const uint16_t type_id, const uint8_t dest, const uint8_t offset, const uint8_t value);
|
||||||
static void send_write_request(const uint16_t type_id, const uint8_t dest, const uint8_t offset, const uint8_t value, const uint16_t validate_typeid);
|
static void send_write_request(const uint16_t type_id, const uint8_t dest, const uint8_t offset, const uint8_t value, const uint16_t validate_typeid);
|
||||||
@@ -107,6 +106,8 @@ class EMSESP {
|
|||||||
static void actual_master_thermostat(const uint8_t device_id);
|
static void actual_master_thermostat(const uint8_t device_id);
|
||||||
static uint8_t check_master_device(const uint8_t device_id, const uint16_t type_id, const bool read);
|
static uint8_t check_master_device(const uint8_t device_id, const uint16_t type_id, const bool read);
|
||||||
|
|
||||||
|
static bool get_device_value_info(JsonObject & root, const char * cmd, const int8_t id, const uint8_t devicetype);
|
||||||
|
|
||||||
static void show_device_values(uuid::console::Shell & shell);
|
static void show_device_values(uuid::console::Shell & shell);
|
||||||
static void show_sensor_values(uuid::console::Shell & shell);
|
static void show_sensor_values(uuid::console::Shell & shell);
|
||||||
|
|
||||||
@@ -129,6 +130,10 @@ class EMSESP {
|
|||||||
return dallassensor_.fails();
|
return dallassensor_.fails();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool dallas_enabled() {
|
||||||
|
return (dallassensor_.dallas_enabled());
|
||||||
|
}
|
||||||
|
|
||||||
enum Watch : uint8_t { WATCH_OFF, WATCH_ON, WATCH_RAW, WATCH_UNKNOWN };
|
enum Watch : uint8_t { WATCH_OFF, WATCH_ON, WATCH_RAW, WATCH_UNKNOWN };
|
||||||
static void watch_id(uint16_t id);
|
static void watch_id(uint16_t id);
|
||||||
static uint16_t watch_id() {
|
static uint16_t watch_id() {
|
||||||
|
|||||||
@@ -22,6 +22,7 @@
|
|||||||
#include "mqtt.h"
|
#include "mqtt.h"
|
||||||
#include "dallassensor.h"
|
#include "dallassensor.h"
|
||||||
#include "version.h"
|
#include "version.h"
|
||||||
|
#include "default_settings.h"
|
||||||
|
|
||||||
// forward declarators
|
// forward declarators
|
||||||
// used to bind EMS-ESP functions to external frameworks
|
// used to bind EMS-ESP functions to external frameworks
|
||||||
|
|||||||
@@ -126,9 +126,9 @@ char * Helpers::smallitoa(char * result, const uint16_t value) {
|
|||||||
char * Helpers::render_boolean(char * result, bool value) {
|
char * Helpers::render_boolean(char * result, bool value) {
|
||||||
uint8_t bool_format_ = Mqtt::bool_format();
|
uint8_t bool_format_ = Mqtt::bool_format();
|
||||||
if (bool_format_ == BOOL_FORMAT_ONOFF) {
|
if (bool_format_ == BOOL_FORMAT_ONOFF) {
|
||||||
strlcpy(result, value ? "on" : "off", 5);
|
strlcpy(result, value ? uuid::read_flash_string(F_(on)).c_str() : uuid::read_flash_string(F_(off)).c_str(), 5);
|
||||||
} else if (bool_format_ == BOOL_FORMAT_ONOFF_CAP) {
|
} else if (bool_format_ == BOOL_FORMAT_ONOFF_CAP) {
|
||||||
strlcpy(result, value ? "ON" : "OFF", 5);
|
strlcpy(result, value ? uuid::read_flash_string(F_(ON)).c_str() : uuid::read_flash_string(F_(OFF)).c_str(), 5);
|
||||||
} else if (bool_format_ == BOOL_FORMAT_TRUEFALSE) {
|
} else if (bool_format_ == BOOL_FORMAT_TRUEFALSE) {
|
||||||
strlcpy(result, value ? "true" : "false", 7);
|
strlcpy(result, value ? "true" : "false", 7);
|
||||||
} else {
|
} else {
|
||||||
@@ -457,12 +457,12 @@ bool Helpers::value2bool(const char * v, bool & value) {
|
|||||||
|
|
||||||
std::string bool_str = toLower(v); // convert to lower case
|
std::string bool_str = toLower(v); // convert to lower case
|
||||||
|
|
||||||
if ((bool_str == "on") || (bool_str == "1") or (bool_str == "true")) {
|
if ((bool_str == uuid::read_flash_string(F_(on))) || (bool_str == "1") or (bool_str == "true")) {
|
||||||
value = true;
|
value = true;
|
||||||
return true; // is a bool
|
return true; // is a bool
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((bool_str == "off") || (bool_str == "0") or (bool_str == "false")) {
|
if ((bool_str == uuid::read_flash_string(F_(off))) || (bool_str == "0") or (bool_str == "false")) {
|
||||||
value = false;
|
value = false;
|
||||||
return true; // is a bool
|
return true; // is a bool
|
||||||
}
|
}
|
||||||
@@ -471,14 +471,14 @@ bool Helpers::value2bool(const char * v, bool & value) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// checks to see if a string is member of a vector and return the index, also allow true/false for on/off
|
// checks to see if a string is member of a vector and return the index, also allow true/false for on/off
|
||||||
bool Helpers::value2enum(const char * v, uint8_t & value, const flash_string_vector & strs) {
|
bool Helpers::value2enum(const char * v, uint8_t & value, const __FlashStringHelper * const * strs) {
|
||||||
if ((v == nullptr) || (strlen(v) == 0)) {
|
if ((v == nullptr) || (strlen(v) == 0)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
std::string str = toLower(v);
|
std::string str = toLower(v);
|
||||||
for (value = 0; value < strs.size(); value++) {
|
for (value = 0; strs[value]; value++) {
|
||||||
std::string str1 = uuid::read_flash_string(strs[value]);
|
std::string str1 = toLower(uuid::read_flash_string(strs[value]));
|
||||||
if ((str1 == "off" && str == "false") || (str1 == "on" && str == "true") || (str == str1) || (v[0] == ('0' + value) && v[1] == '\0')) {
|
if ((str1 == uuid::read_flash_string(F_(off)) && str == "false") || (str1 == uuid::read_flash_string(F_(on)) && str == "true") || (str == str1) || (v[0] == ('0' + value) && v[1] == '\0')) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ class Helpers {
|
|||||||
static bool value2float(const char * v, float & value);
|
static bool value2float(const char * v, float & value);
|
||||||
static bool value2bool(const char * v, bool & value);
|
static bool value2bool(const char * v, bool & value);
|
||||||
static bool value2string(const char * v, std::string & value);
|
static bool value2string(const char * v, std::string & value);
|
||||||
static bool value2enum(const char * v, uint8_t & value, const flash_string_vector & strs);
|
static bool value2enum(const char * v, uint8_t & value, const __FlashStringHelper * const * strs);
|
||||||
|
|
||||||
#ifdef EMSESP_STANDALONE
|
#ifdef EMSESP_STANDALONE
|
||||||
static char * ultostr(char * ptr, uint32_t value, const uint8_t base);
|
static char * ultostr(char * ptr, uint32_t value, const uint8_t base);
|
||||||
|
|||||||
373
src/locale_EN.h
373
src/locale_EN.h
@@ -95,22 +95,25 @@ MAKE_PSTR_WORD(generic)
|
|||||||
MAKE_PSTR_WORD(dallassensor)
|
MAKE_PSTR_WORD(dallassensor)
|
||||||
MAKE_PSTR_WORD(unknown)
|
MAKE_PSTR_WORD(unknown)
|
||||||
|
|
||||||
// strings
|
// format strings
|
||||||
MAKE_PSTR(EMSESP, "EMS-ESP")
|
MAKE_PSTR(EMSESP, "EMS-ESP")
|
||||||
MAKE_PSTR(master_thermostat_fmt, "Master Thermostat Device ID = %s")
|
MAKE_PSTR(master_thermostat_fmt, "Master Thermostat Device ID: %s")
|
||||||
MAKE_PSTR(host_fmt, "Host = %s")
|
MAKE_PSTR(host_fmt, "Host: %s")
|
||||||
MAKE_PSTR(port_fmt, "Port = %d")
|
MAKE_PSTR(port_fmt, "Port: %d")
|
||||||
MAKE_PSTR(hostname_fmt, "Hostname = %s")
|
MAKE_PSTR(hostname_fmt, "Hostname: %s")
|
||||||
MAKE_PSTR(board_profile_fmt, "Board Profile = %s")
|
MAKE_PSTR(board_profile_fmt, "Board Profile: %s")
|
||||||
MAKE_PSTR(mark_interval_fmt, "Mark interval = %lus")
|
MAKE_PSTR(mark_interval_fmt, "Mark interval: %lus")
|
||||||
MAKE_PSTR(wifi_ssid_fmt, "WiFi SSID = %s")
|
MAKE_PSTR(wifi_ssid_fmt, "WiFi SSID: %s")
|
||||||
MAKE_PSTR(wifi_password_fmt, "WiFi Password = %S")
|
MAKE_PSTR(wifi_password_fmt, "WiFi Password: %S")
|
||||||
MAKE_PSTR(ethernet_option_fmt, "Ethernet option = %d")
|
MAKE_PSTR(ethernet_option_fmt, "Ethernet option: %d")
|
||||||
|
MAKE_PSTR(tx_mode_fmt, "Tx mode: %d")
|
||||||
|
MAKE_PSTR(bus_id_fmt, "Bus ID: %02X")
|
||||||
|
MAKE_PSTR(log_level_fmt, "Log level: %s")
|
||||||
|
|
||||||
|
//strings
|
||||||
MAKE_PSTR(cmd_optional, "[cmd]")
|
MAKE_PSTR(cmd_optional, "[cmd]")
|
||||||
MAKE_PSTR(ha_optional, "[ha]")
|
MAKE_PSTR(ha_optional, "[ha]")
|
||||||
MAKE_PSTR(deep_optional, "[deep]")
|
MAKE_PSTR(deep_optional, "[deep]")
|
||||||
MAKE_PSTR(tx_mode_fmt, "Tx mode = %d")
|
|
||||||
MAKE_PSTR(bus_id_fmt, "Bus ID = %02X")
|
|
||||||
MAKE_PSTR(watchid_optional, "[ID]")
|
MAKE_PSTR(watchid_optional, "[ID]")
|
||||||
MAKE_PSTR(watch_format_optional, "[off | on | raw | unknown]")
|
MAKE_PSTR(watch_format_optional, "[off | on | raw | unknown]")
|
||||||
MAKE_PSTR(invalid_watch, "Invalid watch type")
|
MAKE_PSTR(invalid_watch, "Invalid watch type")
|
||||||
@@ -124,7 +127,6 @@ MAKE_PSTR(typeid_mandatory, "<type ID>")
|
|||||||
MAKE_PSTR(deviceid_mandatory, "<device ID>")
|
MAKE_PSTR(deviceid_mandatory, "<device ID>")
|
||||||
MAKE_PSTR(device_type_optional, "[device]")
|
MAKE_PSTR(device_type_optional, "[device]")
|
||||||
MAKE_PSTR(invalid_log_level, "Invalid log level")
|
MAKE_PSTR(invalid_log_level, "Invalid log level")
|
||||||
MAKE_PSTR(log_level_fmt, "Log level = %s")
|
|
||||||
MAKE_PSTR(log_level_optional, "[level]")
|
MAKE_PSTR(log_level_optional, "[level]")
|
||||||
MAKE_PSTR(name_mandatory, "<name>")
|
MAKE_PSTR(name_mandatory, "<name>")
|
||||||
MAKE_PSTR(name_optional, "[name]")
|
MAKE_PSTR(name_optional, "[name]")
|
||||||
@@ -133,16 +135,69 @@ MAKE_PSTR(new_password_prompt2, "Retype new password: ")
|
|||||||
MAKE_PSTR(password_prompt, "Password: ")
|
MAKE_PSTR(password_prompt, "Password: ")
|
||||||
MAKE_PSTR(unset, "<unset>")
|
MAKE_PSTR(unset, "<unset>")
|
||||||
|
|
||||||
MAKE_PSTR_WORD(2);
|
MAKE_PSTR_WORD(number)
|
||||||
MAKE_PSTR_WORD(10);
|
MAKE_PSTR_WORD(enum)
|
||||||
MAKE_PSTR_WORD(100);
|
MAKE_PSTR_WORD(text)
|
||||||
MAKE_PSTR_WORD(60);
|
|
||||||
|
MAKE_PSTR_WORD(2)
|
||||||
|
MAKE_PSTR_WORD(10)
|
||||||
|
MAKE_PSTR_WORD(100)
|
||||||
|
MAKE_PSTR_WORD(60)
|
||||||
|
|
||||||
MAKE_PSTR_LIST(div2, F_(2))
|
MAKE_PSTR_LIST(div2, F_(2))
|
||||||
MAKE_PSTR_LIST(div10, F_(10))
|
MAKE_PSTR_LIST(div10, F_(10))
|
||||||
MAKE_PSTR_LIST(div100, F_(100))
|
MAKE_PSTR_LIST(div100, F_(100))
|
||||||
MAKE_PSTR_LIST(div60, F_(60))
|
MAKE_PSTR_LIST(div60, F_(60))
|
||||||
|
|
||||||
|
// Unit Of Measurement mapping - maps to DeviceValueUOM_s in emsdevice.cpp
|
||||||
|
// uom - also used with HA
|
||||||
|
MAKE_PSTR(percent, "%")
|
||||||
|
MAKE_PSTR(degrees, "°C")
|
||||||
|
MAKE_PSTR(kwh, "kWh")
|
||||||
|
MAKE_PSTR(wh, "Wh")
|
||||||
|
MAKE_PSTR(bar, "bar")
|
||||||
|
MAKE_PSTR(minutes, "minutes")
|
||||||
|
MAKE_PSTR(hours, "hours")
|
||||||
|
MAKE_PSTR(ua, "uA")
|
||||||
|
MAKE_PSTR(lmin, "l/min")
|
||||||
|
MAKE_PSTR(kw, "kW")
|
||||||
|
|
||||||
|
// TAG mapping - maps to DeviceValueTAG_s in emsdevice.cpp
|
||||||
|
// use empty string if want to suppress showing tags
|
||||||
|
MAKE_PSTR(tag_none, "")
|
||||||
|
MAKE_PSTR(tag_heartbeat, "")
|
||||||
|
MAKE_PSTR(tag_boiler_data, "")
|
||||||
|
MAKE_PSTR(tag_device_data_ww, "warm water")
|
||||||
|
MAKE_PSTR(tag_thermostat_data, "")
|
||||||
|
MAKE_PSTR(tag_hc1, "hc1")
|
||||||
|
MAKE_PSTR(tag_hc2, "hc2")
|
||||||
|
MAKE_PSTR(tag_hc3, "hc3")
|
||||||
|
MAKE_PSTR(tag_hc4, "hc4")
|
||||||
|
MAKE_PSTR(tag_wwc1, "wwc1")
|
||||||
|
MAKE_PSTR(tag_wwc2, "wwc2")
|
||||||
|
MAKE_PSTR(tag_wwc3, "wwc3")
|
||||||
|
MAKE_PSTR(tag_wwc4, "wwc4")
|
||||||
|
MAKE_PSTR(tag_hs1, "hs1")
|
||||||
|
MAKE_PSTR(tag_hs2, "hs2")
|
||||||
|
MAKE_PSTR(tag_hs3, "hs3")
|
||||||
|
MAKE_PSTR(tag_hs4, "hs4")
|
||||||
|
MAKE_PSTR(tag_hs5, "hs5")
|
||||||
|
MAKE_PSTR(tag_hs6, "hs6")
|
||||||
|
MAKE_PSTR(tag_hs7, "hs7")
|
||||||
|
MAKE_PSTR(tag_hs8, "hs8")
|
||||||
|
MAKE_PSTR(tag_hs9, "hs9")
|
||||||
|
MAKE_PSTR(tag_hs10, "hs10")
|
||||||
|
MAKE_PSTR(tag_hs11, "hs11")
|
||||||
|
MAKE_PSTR(tag_hs12, "hs12")
|
||||||
|
MAKE_PSTR(tag_hs13, "hs13")
|
||||||
|
MAKE_PSTR(tag_hs14, "hs14")
|
||||||
|
MAKE_PSTR(tag_hs15, "hs15")
|
||||||
|
MAKE_PSTR(tag_hs16, "hs16")
|
||||||
|
|
||||||
|
// MQTT topic names
|
||||||
|
// MAKE_PSTR(tag_heartbeat_mqtt, "heartbeat")
|
||||||
|
MAKE_PSTR(tag_boiler_data_mqtt, "")
|
||||||
|
MAKE_PSTR(tag_device_data_ww_mqtt, "ww")
|
||||||
|
|
||||||
// boiler
|
// boiler
|
||||||
MAKE_PSTR_WORD(time)
|
MAKE_PSTR_WORD(time)
|
||||||
@@ -153,7 +208,7 @@ MAKE_PSTR_WORD(3x3min)
|
|||||||
MAKE_PSTR_WORD(4x3min)
|
MAKE_PSTR_WORD(4x3min)
|
||||||
MAKE_PSTR_WORD(5x3min)
|
MAKE_PSTR_WORD(5x3min)
|
||||||
MAKE_PSTR_WORD(6x3min)
|
MAKE_PSTR_WORD(6x3min)
|
||||||
MAKE_PSTR_WORD(continuos);
|
MAKE_PSTR_WORD(continuos)
|
||||||
MAKE_PSTR(3wayvalve, "3-way valve")
|
MAKE_PSTR(3wayvalve, "3-way valve")
|
||||||
MAKE_PSTR(chargepump, "charge pump")
|
MAKE_PSTR(chargepump, "charge pump")
|
||||||
MAKE_PSTR_WORD(hot)
|
MAKE_PSTR_WORD(hot)
|
||||||
@@ -163,6 +218,8 @@ MAKE_PSTR_WORD(flow)
|
|||||||
MAKE_PSTR_WORD(buffer)
|
MAKE_PSTR_WORD(buffer)
|
||||||
MAKE_PSTR(bufferedflow, "buffered flow")
|
MAKE_PSTR(bufferedflow, "buffered flow")
|
||||||
MAKE_PSTR(layeredbuffer, "layered buffer")
|
MAKE_PSTR(layeredbuffer, "layered buffer")
|
||||||
|
MAKE_PSTR_WORD(maintenance)
|
||||||
|
MAKE_PSTR_WORD(error)
|
||||||
|
|
||||||
// boiler lists
|
// boiler lists
|
||||||
MAKE_PSTR_LIST(enum_off_time_date, F_(off), F_(time), F_(date))
|
MAKE_PSTR_LIST(enum_off_time_date, F_(off), F_(time), F_(date))
|
||||||
@@ -170,6 +227,7 @@ MAKE_PSTR_LIST(enum_freq, F_(off), F_(1x3min), F_(2x3min), F_(3x3min), F_(4x3min
|
|||||||
MAKE_PSTR_LIST(enum_charge, F_(3wayvalve), F_(chargepump))
|
MAKE_PSTR_LIST(enum_charge, F_(3wayvalve), F_(chargepump))
|
||||||
MAKE_PSTR_LIST(enum_comfort, F_(hot), F_(eco), F_(intelligent))
|
MAKE_PSTR_LIST(enum_comfort, F_(hot), F_(eco), F_(intelligent))
|
||||||
MAKE_PSTR_LIST(enum_flow, F_(off), F_(flow), F_(bufferedflow), F_(buffer), F_(layeredbuffer))
|
MAKE_PSTR_LIST(enum_flow, F_(off), F_(flow), F_(bufferedflow), F_(buffer), F_(layeredbuffer))
|
||||||
|
MAKE_PSTR_LIST(enum_reset, F_(maintenance), F_(error))
|
||||||
|
|
||||||
// thermostat
|
// thermostat
|
||||||
MAKE_PSTR_WORD(light)
|
MAKE_PSTR_WORD(light)
|
||||||
@@ -182,10 +240,10 @@ MAKE_PSTR_WORD(heat)
|
|||||||
MAKE_PSTR_WORD(hold)
|
MAKE_PSTR_WORD(hold)
|
||||||
MAKE_PSTR_WORD(cool)
|
MAKE_PSTR_WORD(cool)
|
||||||
MAKE_PSTR_WORD(end)
|
MAKE_PSTR_WORD(end)
|
||||||
MAKE_PSTR_WORD(German)
|
MAKE_PSTR_WORD(german)
|
||||||
MAKE_PSTR_WORD(Dutch)
|
MAKE_PSTR_WORD(dutch)
|
||||||
MAKE_PSTR_WORD(French)
|
MAKE_PSTR_WORD(french)
|
||||||
MAKE_PSTR_WORD(Italian)
|
MAKE_PSTR_WORD(italian)
|
||||||
MAKE_PSTR_WORD(high)
|
MAKE_PSTR_WORD(high)
|
||||||
MAKE_PSTR_WORD(low)
|
MAKE_PSTR_WORD(low)
|
||||||
MAKE_PSTR_WORD(radiator)
|
MAKE_PSTR_WORD(radiator)
|
||||||
@@ -194,11 +252,12 @@ MAKE_PSTR_WORD(floor)
|
|||||||
MAKE_PSTR_WORD(summer)
|
MAKE_PSTR_WORD(summer)
|
||||||
MAKE_PSTR_WORD(winter)
|
MAKE_PSTR_WORD(winter)
|
||||||
MAKE_PSTR_WORD(outdoor)
|
MAKE_PSTR_WORD(outdoor)
|
||||||
MAKE_PSTR_WORD(MPC)
|
MAKE_PSTR_WORD(mpc)
|
||||||
MAKE_PSTR_WORD(room)
|
MAKE_PSTR_WORD(room)
|
||||||
MAKE_PSTR_WORD(power)
|
MAKE_PSTR_WORD(power)
|
||||||
MAKE_PSTR_WORD(constant)
|
MAKE_PSTR_WORD(constant)
|
||||||
MAKE_PSTR_WORD(simple)
|
MAKE_PSTR_WORD(simple)
|
||||||
|
MAKE_PSTR_WORD(optimized)
|
||||||
MAKE_PSTR_WORD(nofrost)
|
MAKE_PSTR_WORD(nofrost)
|
||||||
MAKE_PSTR_WORD(comfort)
|
MAKE_PSTR_WORD(comfort)
|
||||||
MAKE_PSTR_WORD(manual)
|
MAKE_PSTR_WORD(manual)
|
||||||
@@ -206,22 +265,31 @@ MAKE_PSTR_WORD(night)
|
|||||||
MAKE_PSTR_WORD(day)
|
MAKE_PSTR_WORD(day)
|
||||||
MAKE_PSTR_WORD(holiday)
|
MAKE_PSTR_WORD(holiday)
|
||||||
MAKE_PSTR_WORD(reduce)
|
MAKE_PSTR_WORD(reduce)
|
||||||
|
MAKE_PSTR_WORD(noreduce)
|
||||||
|
MAKE_PSTR_WORD(offset)
|
||||||
|
MAKE_PSTR_WORD(design)
|
||||||
|
MAKE_PSTR_WORD(tempauto)
|
||||||
|
MAKE_PSTR_WORD(minflow)
|
||||||
|
MAKE_PSTR_WORD(maxflow)
|
||||||
|
|
||||||
|
MAKE_PSTR_WORD(rc3x)
|
||||||
|
MAKE_PSTR_WORD(rc20)
|
||||||
MAKE_PSTR(internal_temperature, "internal temperature")
|
MAKE_PSTR(internal_temperature, "internal temperature")
|
||||||
MAKE_PSTR(internal_setpoint, "internal setpoint")
|
MAKE_PSTR(internal_setpoint, "internal setpoint")
|
||||||
MAKE_PSTR(external_temperature, "external temperature")
|
MAKE_PSTR(external_temperature, "external temperature")
|
||||||
MAKE_PSTR(burner_temperature, "burner temperature")
|
MAKE_PSTR(burner_temperature, "burner temperature")
|
||||||
MAKE_PSTR(WW_temperature, "WW temperature")
|
MAKE_PSTR(ww_temperature, "ww temperature")
|
||||||
MAKE_PSTR(functioning_mode, "functioning mode")
|
MAKE_PSTR(functioning_mode, "functioning mode")
|
||||||
MAKE_PSTR(smoke_temperature, "smoke temperature")
|
MAKE_PSTR(smoke_temperature, "smoke temperature")
|
||||||
|
|
||||||
// thermostat lists
|
// thermostat lists
|
||||||
MAKE_PSTR_LIST(enum_ibaMainDisplay, F_(internal_temperature), F_(internal_setpoint), F_(external_temperature), F_(burner_temperature), F_(WW_temperature), F_(functioning_mode), F_(time), F_(date), F_(smoke_temperature))
|
MAKE_PSTR_LIST(enum_ibaMainDisplay, F_(internal_temperature), F_(internal_setpoint), F_(external_temperature), F_(burner_temperature), F_(ww_temperature), F_(functioning_mode), F_(time), F_(date), F_(smoke_temperature))
|
||||||
MAKE_PSTR_LIST(enum_ibaLanguage, F_(German), F_(Dutch), F_(French), F_(Italian))
|
MAKE_PSTR_LIST(enum_ibaLanguage, F_(german), F_(dutch), F_(french), F_(italian))
|
||||||
MAKE_PSTR_LIST(enum_floordrystatus, F_(off), F_(start), F_(heat), F_(hold), F_(cool), F_(end))
|
MAKE_PSTR_LIST(enum_floordrystatus, F_(off), F_(start), F_(heat), F_(hold), F_(cool), F_(end))
|
||||||
MAKE_PSTR_LIST(enum_ibaBuildingType, F_(blank), F_(light), F_(medium), F_(heavy))
|
MAKE_PSTR_LIST(enum_ibaBuildingType, F_(blank), F_(light), F_(medium), F_(heavy)) // RC300
|
||||||
MAKE_PSTR_LIST(enum_wwMode, F_(off), F_(low), F_(high), F_(auto), F_(own_prog))
|
MAKE_PSTR_LIST(enum_wwMode, F_(off), F_(low), F_(high), F_(auto), F_(own_prog))
|
||||||
MAKE_PSTR_LIST(enum_wwCircMode, F_(off), F_(on), F_(auto), F_(own_prog))
|
MAKE_PSTR_LIST(enum_wwCircMode, F_(off), F_(on), F_(auto), F_(own_prog))
|
||||||
MAKE_PSTR_LIST(enum_ibaBuildingType2, F_(light), F_(medium), F_(heavy))
|
MAKE_PSTR_LIST(enum_ibaBuildingType2, F_(light), F_(medium), F_(heavy)) // RC30, RC35
|
||||||
MAKE_PSTR_LIST(enum_wwMode2, F_(off), F_(on), F_(auto))
|
MAKE_PSTR_LIST(enum_wwMode2, F_(off), F_(on), F_(auto))
|
||||||
MAKE_PSTR_LIST(enum_wwCircMode2, F_(off), F_(on), F_(auto))
|
MAKE_PSTR_LIST(enum_wwCircMode2, F_(off), F_(on), F_(auto))
|
||||||
MAKE_PSTR_LIST(enum_heatingtype, F_(off), F_(radiator), F_(convector), F_(floor))
|
MAKE_PSTR_LIST(enum_heatingtype, F_(off), F_(radiator), F_(convector), F_(floor))
|
||||||
@@ -231,15 +299,262 @@ MAKE_PSTR_LIST(enum_mode, F_(manual), F_(auto))
|
|||||||
MAKE_PSTR_LIST(enum_mode2, F_(off), F_(manual), F_(auto))
|
MAKE_PSTR_LIST(enum_mode2, F_(off), F_(manual), F_(auto))
|
||||||
MAKE_PSTR_LIST(enum_mode3, F_(night), F_(day), F_(auto))
|
MAKE_PSTR_LIST(enum_mode3, F_(night), F_(day), F_(auto))
|
||||||
MAKE_PSTR_LIST(enum_mode4, F_(blank), F_(manual), F_(auto), F_(holiday))
|
MAKE_PSTR_LIST(enum_mode4, F_(blank), F_(manual), F_(auto), F_(holiday))
|
||||||
|
MAKE_PSTR_LIST(enum_mode5, F_(auto), F_(off))
|
||||||
|
|
||||||
MAKE_PSTR_LIST(enum_modetype, F_(eco), F_(comfort))
|
MAKE_PSTR_LIST(enum_modetype, F_(eco), F_(comfort))
|
||||||
MAKE_PSTR_LIST(enum_modetype2, F_(day))
|
MAKE_PSTR_LIST(enum_modetype2, F_(day))
|
||||||
MAKE_PSTR_LIST(enum_modetype3, F_(night), F_(day))
|
MAKE_PSTR_LIST(enum_modetype3, F_(night), F_(day))
|
||||||
MAKE_PSTR_LIST(enum_modetype4, F_(blank), F_(nofrost), F_(eco), F_(heat))
|
MAKE_PSTR_LIST(enum_modetype4, F_(blank), F_(nofrost), F_(eco), F_(heat))
|
||||||
|
MAKE_PSTR_LIST(enum_modetype5, F_(off), F_(on))
|
||||||
|
|
||||||
MAKE_PSTR_LIST(enum_reducemode, F_(nofrost), F_(reduce), F_(room), F_(outdoor))
|
MAKE_PSTR_LIST(enum_reducemode, F_(nofrost), F_(reduce), F_(room), F_(outdoor))
|
||||||
|
|
||||||
MAKE_PSTR_LIST(enum_controlmode, F_(off), F_(outdoor), F_(simple), F_(MPC), F_(room), F_(power), F_(constant))
|
MAKE_PSTR_LIST(enum_controlmode, F_(off), F_(optimized), F_(simple), F_(mpc), F_(room), F_(power), F_(constant))
|
||||||
MAKE_PSTR_LIST(enum_controlmode2, F_(outdoor), F_(room))
|
MAKE_PSTR_LIST(enum_controlmode2, F_(outdoor), F_(room))
|
||||||
|
MAKE_PSTR_LIST(enum_controlmode3, F_(off), F_(room), F_(outdoor), F("room+outdoor"))
|
||||||
|
MAKE_PSTR_LIST(enum_control, F_(off), F_(rc20), F_(rc3x))
|
||||||
|
|
||||||
MAKE_PSTR_LIST(enum_hamode, F_(off), F_(heat), F_(auto), F_(heat), F_(off), F_(heat), F_(auto), F_(auto), F_(auto), F_(auto))
|
MAKE_PSTR_LIST(enum_hamode, F_(off), F_(heat), F_(auto), F_(heat), F_(off), F_(heat), F_(auto), F_(auto), F_(auto), F_(auto))
|
||||||
|
|
||||||
|
/*
|
||||||
|
* MQTT topics and full text for values and commands
|
||||||
|
*/
|
||||||
|
MAKE_PSTR(homeassistant, "homeassistant/")
|
||||||
|
|
||||||
|
// id for all devices
|
||||||
|
// empty full name to prevent being shown in web or console
|
||||||
|
MAKE_PSTR_LIST(ID, F_(id))
|
||||||
|
|
||||||
|
// Boiler
|
||||||
|
// extra commands, no output
|
||||||
|
MAKE_PSTR_LIST(wwtapactivated, F("wwtapactivated"))
|
||||||
|
MAKE_PSTR_LIST(reset, F("reset"))
|
||||||
|
|
||||||
|
// single mqtt topics
|
||||||
|
MAKE_PSTR_WORD(heating_active)
|
||||||
|
MAKE_PSTR_WORD(tapwater_active)
|
||||||
|
MAKE_PSTR_WORD(response)
|
||||||
|
|
||||||
|
// mqtt, commands and text
|
||||||
|
MAKE_PSTR_LIST(heatingActive, F("heatingactive"), F("heating active"))
|
||||||
|
MAKE_PSTR_LIST(tapwaterActive, F("tapwateractive"), F("warm water active"))
|
||||||
|
MAKE_PSTR_LIST(selFlowTemp, F("selflowtemp"), F("selected flow temperature"))
|
||||||
|
MAKE_PSTR_LIST(selBurnPow, F("selburnpow"), F("burner selected max power"))
|
||||||
|
MAKE_PSTR_LIST(heatingPumpMod, F("heatingpumpmod"), F("heating pump modulation"))
|
||||||
|
MAKE_PSTR_LIST(heatingPump2Mod, F("heatingpump2mod"), F("heating pump 2 modulation"))
|
||||||
|
MAKE_PSTR_LIST(outdoorTemp, F("outdoortemp"), F("outside temperature"))
|
||||||
|
MAKE_PSTR_LIST(curFlowTemp, F("curflowtemp"), F("current flow temperature"))
|
||||||
|
MAKE_PSTR_LIST(retTemp, F("rettemp"), F("return temperature"))
|
||||||
|
MAKE_PSTR_LIST(switchTemp, F("switchtemp"), F("mixing switch temperature"))
|
||||||
|
MAKE_PSTR_LIST(sysPress, F("syspress"), F("system pressure"))
|
||||||
|
MAKE_PSTR_LIST(boilTemp, F("boiltemp"), F("max boiler temperature"))
|
||||||
|
MAKE_PSTR_LIST(exhaustTemp, F("exhausttemp"), F("exhaust temperature"))
|
||||||
|
MAKE_PSTR_LIST(burnGas, F("burngas"), F("gas"))
|
||||||
|
MAKE_PSTR_LIST(flameCurr, F("flamecurr"), F("flame current"))
|
||||||
|
MAKE_PSTR_LIST(heatingPump, F("heatingpump"), F("heating pump"))
|
||||||
|
MAKE_PSTR_LIST(fanWork, F("fanwork"), F("fan"))
|
||||||
|
MAKE_PSTR_LIST(ignWork, F("ignwork"), F("ignition"))
|
||||||
|
MAKE_PSTR_LIST(heatingActivated, F("heatingactivated"), F("heating activated"))
|
||||||
|
MAKE_PSTR_LIST(heatingTemp, F("heatingtemp"), F("heating temperature"))
|
||||||
|
MAKE_PSTR_LIST(pumpModMax, F("pumpmodmax"), F("burner pump max power"))
|
||||||
|
MAKE_PSTR_LIST(pumpModMin, F("pumpmodmin"), F("burner pump min power"))
|
||||||
|
MAKE_PSTR_LIST(pumpDelay, F("pumpdelay"), F("pump delay"))
|
||||||
|
MAKE_PSTR_LIST(burnMinPeriod, F("burnminperiod"), F("burner min period"))
|
||||||
|
MAKE_PSTR_LIST(burnMinPower, F("burnminpower"), F("burner min power"))
|
||||||
|
MAKE_PSTR_LIST(burnMaxPower, F("burnmaxpower"), F("burner max power"))
|
||||||
|
MAKE_PSTR_LIST(boilHystOn, F("boilhyston"), F("hysteresis on temperature"))
|
||||||
|
MAKE_PSTR_LIST(boilHystOff, F("boilhystoff"), F("hysteresis off temperature"))
|
||||||
|
MAKE_PSTR_LIST(setFlowTemp, F("setflowtemp"), F("set flow temperature"))
|
||||||
|
MAKE_PSTR_LIST(setBurnPow, F("setburnpow"), F("burner set power"))
|
||||||
|
MAKE_PSTR_LIST(curBurnPow, F("curburnpow"), F("burner current power"))
|
||||||
|
MAKE_PSTR_LIST(burnStarts, F("burnstarts"), F("burner # starts"))
|
||||||
|
MAKE_PSTR_LIST(burnWorkMin, F("burnworkmin"), F("total burner operating time"))
|
||||||
|
MAKE_PSTR_LIST(heatWorkMin, F("heatworkmin"), F("total heat operating time"))
|
||||||
|
MAKE_PSTR_LIST(UBAuptime, F("ubauptime"), F("total UBA operating time"))
|
||||||
|
MAKE_PSTR_LIST(lastCode, F("lastcode"), F("last error code"))
|
||||||
|
MAKE_PSTR_LIST(serviceCode, F("servicecode"), F("service code"))
|
||||||
|
MAKE_PSTR_LIST(serviceCodeNumber, F("servicecodenumber"), F("service code number"))
|
||||||
|
MAKE_PSTR_LIST(maintenanceMessage, F("maintenancemessage"), F("maintenance message"))
|
||||||
|
MAKE_PSTR_LIST(maintenanceDate, F("maintenancedate"), F("maintenance set date"))
|
||||||
|
MAKE_PSTR_LIST(maintenanceType, F_(maintenance), F("maintenance scheduled"))
|
||||||
|
MAKE_PSTR_LIST(maintenanceTime, F("maintenancetime"), F("maintenance set time"))
|
||||||
|
|
||||||
|
MAKE_PSTR_LIST(upTimeControl, F("uptimecontrol"), F("operating time total heat"))
|
||||||
|
MAKE_PSTR_LIST(upTimeCompHeating, F("uptimecompheating"), F("operating time compressor heating"))
|
||||||
|
MAKE_PSTR_LIST(upTimeCompCooling, F("uptimecompcooling"), F("operating time compressor cooling"))
|
||||||
|
MAKE_PSTR_LIST(upTimeCompWw, F("uptimecompww"), F("operating time compressor warm water"))
|
||||||
|
MAKE_PSTR_LIST(heatingStarts, F("heatingstarts"), F("# heating control starts"))
|
||||||
|
MAKE_PSTR_LIST(coolingStarts, F("coolingstarts"), F("# cooling control starts"))
|
||||||
|
MAKE_PSTR_LIST(nrgConsTotal, F("nrgconstotal"), F("total energy consumption"))
|
||||||
|
MAKE_PSTR_LIST(nrgConsCompTotal, F("nrgconscomptotal"), F("energy consumption compressor total"))
|
||||||
|
MAKE_PSTR_LIST(nrgConsCompHeating, F("nrgconscompheating"), F("energy consumption compressor heating"))
|
||||||
|
MAKE_PSTR_LIST(nrgConsCompWw, F("nrgconscompww"), F("energy consumption compressor warm water"))
|
||||||
|
MAKE_PSTR_LIST(nrgConsCompCooling, F("nrgconscompcooling"), F("energy consumption compressor cooling"))
|
||||||
|
MAKE_PSTR_LIST(nrgSuppTotal, F("nrgsupptotal"), F("total energy supplied"))
|
||||||
|
MAKE_PSTR_LIST(nrgSuppHeating, F("nrgsuppheating"), F("total energy supplied heating"))
|
||||||
|
MAKE_PSTR_LIST(nrgSuppWw, F("nrgsuppww"), F("total energy warm supplied warm water"))
|
||||||
|
MAKE_PSTR_LIST(nrgSuppCooling, F("nrgsuppcooling"), F("total energy supplied cooling"))
|
||||||
|
MAKE_PSTR_LIST(auxElecHeatNrgConsTotal, F("auxelecheatnrgconstotal"), F("auxiliary electrical heater energy consumption total"))
|
||||||
|
MAKE_PSTR_LIST(auxElecHeatNrgConsHeating, F("auxelecheatnrgconsheating"), F("auxiliary electrical heater energy consumption heating"))
|
||||||
|
MAKE_PSTR_LIST(auxElecHeatNrgConsWW, F("auxelecheatnrgconsww"), F("auxiliary electrical heater energy consumption"))
|
||||||
|
|
||||||
|
MAKE_PSTR_LIST(hpPower, F("hppower"), F("heatpump power"))
|
||||||
|
MAKE_PSTR_LIST(hpTc0, F("hptc0"), F("water temperature condenser inlet (TC0)"))
|
||||||
|
MAKE_PSTR_LIST(hpTc1, F("hptc1"), F("water temperture condenser output (TC1)"))
|
||||||
|
MAKE_PSTR_LIST(hpTc3, F("hptc3"), F("condenser temperature (TC3)"))
|
||||||
|
MAKE_PSTR_LIST(hpTr3, F("hptr3"), F("refrigerant temperature liquid side (condenser output) (TR3)"))
|
||||||
|
MAKE_PSTR_LIST(hpTr4, F("hptr4"), F("evaporator inlet temperature (TR4)"))
|
||||||
|
MAKE_PSTR_LIST(hpTr5, F("hptr5"), F("compressor Inlet temperature (TR5)"))
|
||||||
|
MAKE_PSTR_LIST(hpTr6, F("hptr6"), F("compressor outlet temperature (TR6)"))
|
||||||
|
MAKE_PSTR_LIST(hpTr7, F("hptr7"), F("refrigerant temperature gas side (condenser input) (TR7)"))
|
||||||
|
MAKE_PSTR_LIST(hpTl2, F("hptl2"), F("air inlet temperature (TL2)"))
|
||||||
|
MAKE_PSTR_LIST(hpPl1, F("hppl1"), F("low pressure side temperature (PL1)"))
|
||||||
|
MAKE_PSTR_LIST(hpPh1, F("hpph1"), F("high pressure side temperature (PH1)"))
|
||||||
|
|
||||||
|
MAKE_PSTR_LIST(wWSelTemp, F("wwseltemp"), F("selected temperature"))
|
||||||
|
MAKE_PSTR_LIST(wWSetTemp, F("wwsettemp"), F("set temperature"))
|
||||||
|
MAKE_PSTR_LIST(wWType, F("wwtype"), F("type"))
|
||||||
|
MAKE_PSTR_LIST(wWComfort, F("wwcomfort"), F("comfort"))
|
||||||
|
MAKE_PSTR_LIST(wWFlowTempOffset, F("wwflowtempoffset"), F("flow temperature offset"))
|
||||||
|
MAKE_PSTR_LIST(wWMaxPower, F("wwmaxpower"), F("max power"))
|
||||||
|
MAKE_PSTR_LIST(wWCircPump, F("wwcircpump"), F("circulation pump available"))
|
||||||
|
MAKE_PSTR_LIST(wWChargeType, F("wwchargetype"), F("charging type"))
|
||||||
|
MAKE_PSTR_LIST(wWDisinfectionTemp, F("wwdisinfectiontemp"), F("disinfection temperature"))
|
||||||
|
MAKE_PSTR_LIST(wWCircMode, F("wwcircmode"), F("circulation pump freq"))
|
||||||
|
MAKE_PSTR_LIST(wWCirc, F("wwcirc"), F("circulation active"))
|
||||||
|
MAKE_PSTR_LIST(wWCurTemp, F("wwcurtemp"), F("current intern temperature"))
|
||||||
|
MAKE_PSTR_LIST(wWCurTemp2, F("wwcurtemp2"), F("current extern temperature"))
|
||||||
|
MAKE_PSTR_LIST(wWCurFlow, F("wwcurflow"), F("current tap water flow"))
|
||||||
|
MAKE_PSTR_LIST(wWStorageTemp1, F("wwstoragetemp1"), F("storage intern temperature"))
|
||||||
|
MAKE_PSTR_LIST(wWStorageTemp2, F("wwstoragetemp2"), F("storage extern temperature"))
|
||||||
|
MAKE_PSTR_LIST(wWActivated, F("wwactivated"), F("activated"))
|
||||||
|
MAKE_PSTR_LIST(wWOneTime, F("wwonetime"), F("one time charging"))
|
||||||
|
MAKE_PSTR_LIST(wWDisinfecting, F("wwdisinfecting"), F("disinfecting"))
|
||||||
|
MAKE_PSTR_LIST(wWCharging, F("wwcharging"), F("charging"))
|
||||||
|
MAKE_PSTR_LIST(wWRecharging, F("wwrecharging"), F("recharging"))
|
||||||
|
MAKE_PSTR_LIST(wWTempOK, F("wwtempok"), F("temperature ok"))
|
||||||
|
MAKE_PSTR_LIST(wWActive, F("wwactive"), F("active"))
|
||||||
|
MAKE_PSTR_LIST(wWHeat, F("wwheat"), F("heating"))
|
||||||
|
MAKE_PSTR_LIST(wWSetPumpPower, F("wwsetpumppower"), F("pump set power"))
|
||||||
|
MAKE_PSTR_LIST(mixerTemp, F("mixertemp"), F("mixer temperature"))
|
||||||
|
MAKE_PSTR_LIST(tankMiddleTemp, F("tankmiddletemp"), F("tank middle temperature (TS3)"))
|
||||||
|
MAKE_PSTR_LIST(wWStarts, F("wwstarts"), F("# starts"))
|
||||||
|
MAKE_PSTR_LIST(wWStarts2, F("wwstarts2"), F("# control starts"))
|
||||||
|
MAKE_PSTR_LIST(wWWorkM, F("wwworkm"), F("active time"))
|
||||||
|
|
||||||
|
// thermostat
|
||||||
|
// extra commands, no long name, does not show on web
|
||||||
|
MAKE_PSTR_LIST(switchtime, F("switchtime"))
|
||||||
|
MAKE_PSTR_LIST(temp, F("temp"))
|
||||||
|
MAKE_PSTR_LIST(hatemp, F("hatemp"))
|
||||||
|
MAKE_PSTR_LIST(hamode, F("hamode"))
|
||||||
|
|
||||||
|
// mqtt values / commands
|
||||||
|
MAKE_PSTR_LIST(dateTime, F("datetime"), F("date/time"))
|
||||||
|
MAKE_PSTR_LIST(errorCode, F("errorcode"), F("error code"))
|
||||||
|
|
||||||
|
MAKE_PSTR_LIST(ibaMainDisplay, F("display"), F("display"))
|
||||||
|
MAKE_PSTR_LIST(ibaLanguage, F("language"), F("language"))
|
||||||
|
MAKE_PSTR_LIST(ibaClockOffset, F("clockoffset"), F("clock offset"))
|
||||||
|
|
||||||
|
MAKE_PSTR_LIST(ibaBuildingType, F("building"), F("building"))
|
||||||
|
MAKE_PSTR_LIST(ibaCalIntTemperature, F("intoffset"), F("offset internal temperature"))
|
||||||
|
MAKE_PSTR_LIST(ibaMinExtTemperature, F("minexttemp"), F("min external temperature"))
|
||||||
|
MAKE_PSTR_LIST(tempsensor1, F("inttemp1"), F("temperature sensor 1"))
|
||||||
|
MAKE_PSTR_LIST(tempsensor2, F("inttemp2"), F("temperature sensor 2"))
|
||||||
|
MAKE_PSTR_LIST(dampedoutdoortemp, F("dampedoutdoortemp"), F("damped outdoor temperature"))
|
||||||
|
|
||||||
|
MAKE_PSTR_LIST(floordrystatus, F("floordry"), F("floor drying"))
|
||||||
|
MAKE_PSTR_LIST(dampedoutdoortemp2, F("dampedoutdoortemp"), F("damped outdoor temperature"))
|
||||||
|
MAKE_PSTR_LIST(floordrytemp, F("floordrytemp"), F("floor drying temperature"))
|
||||||
|
|
||||||
|
MAKE_PSTR_LIST(wwMode, F("wwmode"), F("mode"))
|
||||||
|
MAKE_PSTR_LIST(wwSetTemp, F("wwsettemp"), F("set temperature"))
|
||||||
|
MAKE_PSTR_LIST(wwSetTempLow, F("wwsettemplow"), F("set temperature low"))
|
||||||
|
MAKE_PSTR_LIST(wwExtra1, F("wwextra1"), F("circuit 1 extra"))
|
||||||
|
MAKE_PSTR_LIST(wwExtra2, F("wwextra2"), F("circuit 2 extra"))
|
||||||
|
|
||||||
|
MAKE_PSTR_LIST(setpoint_roomTemp, F("seltemp"), F("selected room temperature"))
|
||||||
|
MAKE_PSTR_LIST(curr_roomTemp, F("currtemp"), F("current room temperature"))
|
||||||
|
|
||||||
|
MAKE_PSTR_LIST(mode, F("mode"), F("mode"))
|
||||||
|
MAKE_PSTR_LIST(modetype, F("modetype"), F("mode type"))
|
||||||
|
|
||||||
|
MAKE_PSTR_LIST(daytemp, F("daytemp"), F("day temperature"))
|
||||||
|
MAKE_PSTR_LIST(heattemp, F("heattemp"), F("heat temperature"))
|
||||||
|
MAKE_PSTR_LIST(nighttemp, F("nighttemp"), F("night temperature"))
|
||||||
|
MAKE_PSTR_LIST(ecotemp, F("ecotemp"), F("eco temperature"))
|
||||||
|
MAKE_PSTR_LIST(manualtemp, F("manualtemp"), F("manual temperature"))
|
||||||
|
MAKE_PSTR_LIST(tempautotemp, F("tempautotemp"), F("temporary room temperature automode"))
|
||||||
|
MAKE_PSTR_LIST(comforttemp, F("comforttemp"), F("comfort temperature"))
|
||||||
|
MAKE_PSTR_LIST(summertemp, F("summertemp"), F("summer temperature"))
|
||||||
|
MAKE_PSTR_LIST(designtemp, F("designtemp"), F("design temperature"))
|
||||||
|
MAKE_PSTR_LIST(offsettemp, F("offsettemp"), F("offset temperature"))
|
||||||
|
MAKE_PSTR_LIST(minflowtemp, F("minflowtemp"), F("min flow temperature"))
|
||||||
|
MAKE_PSTR_LIST(maxflowtemp, F("maxflowtemp"), F("max flow temperature"))
|
||||||
|
MAKE_PSTR_LIST(roominfluence, F("roominfluence"), F("room influence"))
|
||||||
|
MAKE_PSTR_LIST(nofrosttemp, F("nofrosttemp"), F("nofrost temperature"))
|
||||||
|
MAKE_PSTR_LIST(targetflowtemp, F("targetflowtemp"), F("target flow temperature"))
|
||||||
|
MAKE_PSTR_LIST(heatingtype, F("heatingtype"), F("heating type"))
|
||||||
|
MAKE_PSTR_LIST(summersetmode, F("summersetmode"), F("summer set mode"))
|
||||||
|
MAKE_PSTR_LIST(controlmode, F("controlmode"), F("control mode"))
|
||||||
|
MAKE_PSTR_LIST(control, F("control"), F("control device"))
|
||||||
|
MAKE_PSTR_LIST(program, F("program"), F("program"))
|
||||||
|
MAKE_PSTR_LIST(pause, F("pause"), F("pause time"))
|
||||||
|
MAKE_PSTR_LIST(party, F("party"), F("party time"))
|
||||||
|
|
||||||
|
MAKE_PSTR_LIST(holidaytemp, F("holidaytemp"), F("holiday temperature"))
|
||||||
|
MAKE_PSTR_LIST(summermode, F("summermode"), F("summer mode"))
|
||||||
|
MAKE_PSTR_LIST(holidaymode, F("holidaymode"), F("holiday mode"))
|
||||||
|
MAKE_PSTR_LIST(flowtempoffset, F("flowtempoffset"), F("flow temperature offset"))
|
||||||
|
MAKE_PSTR_LIST(reducemode, F("reducemode"), F("reduce mode"))
|
||||||
|
MAKE_PSTR_LIST(noreducetemp, F("noreducetemp"), F("no reduce below temperature"))
|
||||||
|
MAKE_PSTR_LIST(remotetemp, F("remotetemp"), F("room temperature from remote"))
|
||||||
|
|
||||||
|
// heatpump
|
||||||
|
MAKE_PSTR_LIST(airHumidity, F("airhumidity"), F("relative air humidity"))
|
||||||
|
MAKE_PSTR_LIST(dewTemperature, F("dewtemperature"), F("dew point temperature"))
|
||||||
|
|
||||||
|
// mixer
|
||||||
|
MAKE_PSTR_LIST(flowSetTemp, F("flowsettemp"), F("setpoint flow temperature"))
|
||||||
|
MAKE_PSTR_LIST(flowTempHc, F("flowtemphc"), F("flow temperature in assigned hc (TC1)"))
|
||||||
|
MAKE_PSTR_LIST(pumpStatus, F("pumpstatus"), F("pump status in assigned hc (PC1)"))
|
||||||
|
MAKE_PSTR_LIST(mixerStatus, F("valvestatus"), F("mixing valve actuator in assigned hc (VC1)"))
|
||||||
|
MAKE_PSTR_LIST(flowTempVf, F("flowtempvf"), F("flow temperature in header (T0/Vf)"))
|
||||||
|
|
||||||
|
MAKE_PSTR_LIST(wwPumpStatus, F("pumpstatus"), F("pump status in assigned wwc (PC1)"))
|
||||||
|
MAKE_PSTR_LIST(wwTempStatus, F("wwtempstatus"), F("temperature switch in assigned wwc (MC1)"))
|
||||||
|
MAKE_PSTR_LIST(wwTemp, F("wwtemp"), F("current warm water temperature"))
|
||||||
|
|
||||||
|
// solar
|
||||||
|
MAKE_PSTR_LIST(type, F("type"), F("type"))
|
||||||
|
MAKE_PSTR_LIST(collectorTemp, F("collectortemp"), F("collector temperature (TS1)"))
|
||||||
|
MAKE_PSTR_LIST(tankBottomTemp, F("tankbottomtemp"), F("tank bottom temperature (TS2)"))
|
||||||
|
MAKE_PSTR_LIST(tank2BottomTemp, F("tank2bottomtemp"), F("second tank bottom temperature (TS5)"))
|
||||||
|
MAKE_PSTR_LIST(heatExchangerTemp, F("heatexchangertemp"), F("heat exchanger temperature (TS6)"))
|
||||||
|
|
||||||
|
MAKE_PSTR_LIST(tankMaxTemp, F("tankmaxtemp"), F("maximum tank temperature"))
|
||||||
|
MAKE_PSTR_LIST(solarPumpModulation, F("solarpumpmodulation"), F("pump modulation (PS1)"))
|
||||||
|
MAKE_PSTR_LIST(cylinderPumpModulation, F("cylinderpumpmodulation"), F("cylinder pump modulation (PS5)"))
|
||||||
|
|
||||||
|
MAKE_PSTR_LIST(solarPump, F("solarpump"), F("pump (PS1)"))
|
||||||
|
MAKE_PSTR_LIST(valveStatus, F("valvestatus"), F("valve status"))
|
||||||
|
MAKE_PSTR_LIST(tankHeated, F("tankheated"), F("tank heated"))
|
||||||
|
MAKE_PSTR_LIST(collectorShutdown, F("collectorshutdown"), F("collector shutdown"))
|
||||||
|
|
||||||
|
MAKE_PSTR_LIST(pumpWorkTime, F("pumpWorktime"), F("pump working time"))
|
||||||
|
|
||||||
|
MAKE_PSTR_LIST(energyLastHour, F("energylasthour"), F("energy last hour"))
|
||||||
|
MAKE_PSTR_LIST(energyTotal, F("energytotal"), F("energy total"))
|
||||||
|
MAKE_PSTR_LIST(energyToday, F("energytoday"), F("energy today"))
|
||||||
|
|
||||||
|
MAKE_PSTR_LIST(wwTemp1, F("wwtemp1"), F("temperature 1"))
|
||||||
|
MAKE_PSTR_LIST(wwTemp3, F("wwtemp3"), F("temperature 3"))
|
||||||
|
MAKE_PSTR_LIST(wwTemp4, F("wwtemp4"), F("temperature 4"))
|
||||||
|
MAKE_PSTR_LIST(wwTemp5, F("wwtemp5"), F("temperature 5"))
|
||||||
|
MAKE_PSTR_LIST(wwTemp7, F("wwtemp7"), F("temperature 7"))
|
||||||
|
MAKE_PSTR_LIST(wwPump, F("wwpump"), F("pump"))
|
||||||
|
|
||||||
|
// switch
|
||||||
|
MAKE_PSTR_LIST(activated, F("activated"), F("activated"))
|
||||||
|
MAKE_PSTR_LIST(status, F("status"), F("status"))
|
||||||
|
|||||||
62
src/mqtt.cpp
62
src/mqtt.cpp
@@ -39,7 +39,7 @@ uint8_t Mqtt::dallas_format_;
|
|||||||
uint8_t Mqtt::bool_format_;
|
uint8_t Mqtt::bool_format_;
|
||||||
uint8_t Mqtt::ha_climate_format_;
|
uint8_t Mqtt::ha_climate_format_;
|
||||||
bool Mqtt::ha_enabled_;
|
bool Mqtt::ha_enabled_;
|
||||||
bool Mqtt::nested_format_;
|
uint8_t Mqtt::nested_format_;
|
||||||
uint8_t Mqtt::subscribe_format_;
|
uint8_t Mqtt::subscribe_format_;
|
||||||
|
|
||||||
std::deque<Mqtt::QueuedMqttMessage> Mqtt::mqtt_messages_;
|
std::deque<Mqtt::QueuedMqttMessage> Mqtt::mqtt_messages_;
|
||||||
@@ -313,6 +313,7 @@ void Mqtt::on_message(const char * fulltopic, const char * payload, size_t len)
|
|||||||
if (mf.mqtt_subfunction_) {
|
if (mf.mqtt_subfunction_) {
|
||||||
if (!(mf.mqtt_subfunction_)(message)) {
|
if (!(mf.mqtt_subfunction_)(message)) {
|
||||||
LOG_ERROR(F("MQTT error: invalid payload %s for this topic %s"), message, topic);
|
LOG_ERROR(F("MQTT error: invalid payload %s for this topic %s"), message, topic);
|
||||||
|
Mqtt::publish(F_(response), "invalid");
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -327,14 +328,15 @@ void Mqtt::on_message(const char * fulltopic, const char * payload, size_t len)
|
|||||||
}
|
}
|
||||||
cmd_only++; // skip the /
|
cmd_only++; // skip the /
|
||||||
int8_t id = -1;
|
int8_t id = -1;
|
||||||
// check for hcx/ prefix
|
// check for hcx/ prefix, commented out, this is now in command::call
|
||||||
if (cmd_only[0] == 'h' && cmd_only[1] == 'c' && cmd_only[3] == '/') {
|
// if (cmd_only[0] == 'h' && cmd_only[1] == 'c' && cmd_only[3] == '/') {
|
||||||
id = cmd_only[2] - '0';
|
// id = cmd_only[2] - '0';
|
||||||
cmd_only += 4;
|
// cmd_only += 4;
|
||||||
}
|
// }
|
||||||
// LOG_INFO(F("devicetype= %d, topic = %s, cmd = %s, message = %s, id = %d"), mf.device_type_, topic, cmd_only, message, id);
|
// LOG_INFO(F("devicetype= %d, topic = %s, cmd = %s, message = %s, id = %d"), mf.device_type_, topic, cmd_only, message, id);
|
||||||
if (!Command::call(mf.device_type_, cmd_only, message, id)) {
|
if (!Command::call(mf.device_type_, cmd_only, message, id)) {
|
||||||
LOG_ERROR(F("No matching cmd (%s) in topic %s, id %d, or invalid data"), cmd_only, topic, id);
|
LOG_ERROR(F("No matching cmd (%s) in topic %s, id %d, or invalid data"), cmd_only, topic, id);
|
||||||
|
Mqtt::publish(F_(response), "unknown");
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -374,11 +376,18 @@ void Mqtt::on_message(const char * fulltopic, const char * payload, size_t len)
|
|||||||
char data_str[10];
|
char data_str[10];
|
||||||
cmd_known = Command::call(mf.device_type_, command, Helpers::render_value(data_str, (float)data.as<float>(), 2), n);
|
cmd_known = Command::call(mf.device_type_, command, Helpers::render_value(data_str, (float)data.as<float>(), 2), n);
|
||||||
} else if (data.isNull()) {
|
} else if (data.isNull()) {
|
||||||
cmd_known = Command::call(mf.device_type_, command, "", n);
|
DynamicJsonDocument resp(EMSESP_JSON_SIZE_XLARGE_DYN);
|
||||||
|
JsonObject json = resp.to<JsonObject>();
|
||||||
|
cmd_known = Command::call(mf.device_type_, command, "", n, json);
|
||||||
|
if (cmd_known && json.size()) {
|
||||||
|
Mqtt::publish(F_(response), resp.as<JsonObject>());
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!cmd_known) {
|
if (!cmd_known) {
|
||||||
LOG_ERROR(F("No matching cmd (%s) or invalid data"), command);
|
LOG_ERROR(F("No matching cmd (%s) or invalid data"), command);
|
||||||
|
Mqtt::publish(F_(response), "unknown");
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
@@ -672,20 +681,21 @@ void Mqtt::ha_status() {
|
|||||||
ids.add("ems-esp");
|
ids.add("ems-esp");
|
||||||
|
|
||||||
char topic[MQTT_TOPIC_MAX_SIZE];
|
char topic[MQTT_TOPIC_MAX_SIZE];
|
||||||
snprintf_P(topic, sizeof(topic), PSTR("homeassistant/sensor/%s/system/config"), mqtt_base_.c_str());
|
snprintf_P(topic, sizeof(topic), PSTR("sensor/%s/system/config"), mqtt_base_.c_str());
|
||||||
Mqtt::publish_ha(topic, doc.as<JsonObject>()); // publish the config payload with retain flag
|
Mqtt::publish_ha(topic, doc.as<JsonObject>()); // publish the config payload with retain flag
|
||||||
|
|
||||||
// create the sensors
|
// create the sensors
|
||||||
|
// must match the MQTT payload keys
|
||||||
publish_mqtt_ha_sensor(DeviceValueType::INT, DeviceValueTAG::TAG_HEARTBEAT, F("WiFi strength"), EMSdevice::DeviceType::SYSTEM, F("rssi"));
|
publish_mqtt_ha_sensor(DeviceValueType::INT, DeviceValueTAG::TAG_HEARTBEAT, F("WiFi strength"), EMSdevice::DeviceType::SYSTEM, F("rssi"));
|
||||||
publish_mqtt_ha_sensor(DeviceValueType::INT, DeviceValueTAG::TAG_HEARTBEAT, F("Uptime"), EMSdevice::DeviceType::SYSTEM, F("uptime"));
|
publish_mqtt_ha_sensor(DeviceValueType::INT, DeviceValueTAG::TAG_HEARTBEAT, F("Uptime"), EMSdevice::DeviceType::SYSTEM, F("uptime"));
|
||||||
publish_mqtt_ha_sensor(DeviceValueType::INT, DeviceValueTAG::TAG_HEARTBEAT, F("Uptime (sec)"), EMSdevice::DeviceType::SYSTEM, F("uptime_sec"));
|
publish_mqtt_ha_sensor(DeviceValueType::INT, DeviceValueTAG::TAG_HEARTBEAT, F("Uptime (sec)"), EMSdevice::DeviceType::SYSTEM, F("uptime_sec"));
|
||||||
publish_mqtt_ha_sensor(DeviceValueType::INT, DeviceValueTAG::TAG_HEARTBEAT, F("Free heap memory"), EMSdevice::DeviceType::SYSTEM, F("freemem"));
|
publish_mqtt_ha_sensor(DeviceValueType::INT, DeviceValueTAG::TAG_HEARTBEAT, F("Free memory"), EMSdevice::DeviceType::SYSTEM, F("freemem"));
|
||||||
publish_mqtt_ha_sensor(DeviceValueType::INT, DeviceValueTAG::TAG_HEARTBEAT, F("# Failed MQTT publishes"), EMSdevice::DeviceType::SYSTEM, F("mqttfails"));
|
publish_mqtt_ha_sensor(DeviceValueType::INT, DeviceValueTAG::TAG_HEARTBEAT, F("# MQTT fails"), EMSdevice::DeviceType::SYSTEM, F("mqttfails"));
|
||||||
publish_mqtt_ha_sensor(DeviceValueType::INT, DeviceValueTAG::TAG_HEARTBEAT, F("# Rx Sent"), EMSdevice::DeviceType::SYSTEM, F("rxsent"));
|
publish_mqtt_ha_sensor(DeviceValueType::INT, DeviceValueTAG::TAG_HEARTBEAT, F("# Rx received"), EMSdevice::DeviceType::SYSTEM, F("rxreceived"));
|
||||||
publish_mqtt_ha_sensor(DeviceValueType::INT, DeviceValueTAG::TAG_HEARTBEAT, F("# Rx Fails"), EMSdevice::DeviceType::SYSTEM, F("rxfails"));
|
publish_mqtt_ha_sensor(DeviceValueType::INT, DeviceValueTAG::TAG_HEARTBEAT, F("# Rx fails"), EMSdevice::DeviceType::SYSTEM, F("rxfails"));
|
||||||
publish_mqtt_ha_sensor(DeviceValueType::INT, DeviceValueTAG::TAG_HEARTBEAT, F("# Tx Reads"), EMSdevice::DeviceType::SYSTEM, F("txread"));
|
publish_mqtt_ha_sensor(DeviceValueType::INT, DeviceValueTAG::TAG_HEARTBEAT, F("# Tx reads"), EMSdevice::DeviceType::SYSTEM, F("txread"));
|
||||||
publish_mqtt_ha_sensor(DeviceValueType::INT, DeviceValueTAG::TAG_HEARTBEAT, F("# Tx Writes"), EMSdevice::DeviceType::SYSTEM, F("txwrite"));
|
publish_mqtt_ha_sensor(DeviceValueType::INT, DeviceValueTAG::TAG_HEARTBEAT, F("# Tx writes"), EMSdevice::DeviceType::SYSTEM, F("txwrite"));
|
||||||
publish_mqtt_ha_sensor(DeviceValueType::INT, DeviceValueTAG::TAG_HEARTBEAT, F("# Tx Fails"), EMSdevice::DeviceType::SYSTEM, F("txfails"));
|
publish_mqtt_ha_sensor(DeviceValueType::INT, DeviceValueTAG::TAG_HEARTBEAT, F("# Tx fails"), EMSdevice::DeviceType::SYSTEM, F("txfails"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// add sub or pub task to the queue.
|
// add sub or pub task to the queue.
|
||||||
@@ -788,14 +798,15 @@ void Mqtt::publish_ha(const std::string & topic, const JsonObject & payload) {
|
|||||||
payload_text.reserve(measureJson(payload) + 1);
|
payload_text.reserve(measureJson(payload) + 1);
|
||||||
serializeJson(payload, payload_text); // convert json to string
|
serializeJson(payload, payload_text); // convert json to string
|
||||||
|
|
||||||
|
std::string fulltopic = uuid::read_flash_string(F_(homeassistant)) + topic;
|
||||||
#if defined(EMSESP_STANDALONE)
|
#if defined(EMSESP_STANDALONE)
|
||||||
LOG_DEBUG(F("Publishing HA topic=%s, payload=%s"), topic.c_str(), payload_text.c_str());
|
LOG_DEBUG(F("Publishing HA topic=%s, payload=%s"), fulltopic.c_str(), payload_text.c_str());
|
||||||
#elif defined(EMSESP_DEBUG)
|
#elif defined(EMSESP_DEBUG)
|
||||||
LOG_DEBUG(F("[debug] Publishing HA topic=%s, payload=%s"), topic.c_str(), payload_text.c_str());
|
LOG_DEBUG(F("[debug] Publishing HA topic=%s, payload=%s"), fulltopic.c_str(), payload_text.c_str());
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// queue messages if the MQTT connection is not yet established. to ensure we don't miss messages
|
// queue messages if the MQTT connection is not yet established. to ensure we don't miss messages
|
||||||
queue_publish_message(topic, payload_text, true); // with retain true
|
queue_publish_message(fulltopic, payload_text, true); // with retain true
|
||||||
}
|
}
|
||||||
|
|
||||||
// take top from queue and perform the publish or subscribe action
|
// take top from queue and perform the publish or subscribe action
|
||||||
@@ -809,7 +820,7 @@ void Mqtt::process_queue() {
|
|||||||
auto mqtt_message = mqtt_messages_.front();
|
auto mqtt_message = mqtt_messages_.front();
|
||||||
auto message = mqtt_message.content_;
|
auto message = mqtt_message.content_;
|
||||||
char topic[MQTT_TOPIC_MAX_SIZE];
|
char topic[MQTT_TOPIC_MAX_SIZE];
|
||||||
if ((strncmp(message->topic.c_str(), "homeassistant/", 13) == 0)) {
|
if (message->topic.find(uuid::read_flash_string(F_(homeassistant))) == 0) {
|
||||||
// leave topic as it is
|
// leave topic as it is
|
||||||
strcpy(topic, message->topic.c_str());
|
strcpy(topic, message->topic.c_str());
|
||||||
} else {
|
} else {
|
||||||
@@ -833,7 +844,7 @@ void Mqtt::process_queue() {
|
|||||||
// it will have a real packet ID
|
// it will have a real packet ID
|
||||||
if (mqtt_message.packet_id_ > 0) {
|
if (mqtt_message.packet_id_ > 0) {
|
||||||
#if defined(EMSESP_DEBUG)
|
#if defined(EMSESP_DEBUG)
|
||||||
LOG_DEBUG(F("[DEBUG] Waitig for QOS-ACK"));
|
LOG_DEBUG(F("[DEBUG] Waiting for QOS-ACK"));
|
||||||
#endif
|
#endif
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -892,7 +903,7 @@ void Mqtt::publish_mqtt_ha_sensor(uint8_t type, // EMSdevice
|
|||||||
DynamicJsonDocument doc(EMSESP_JSON_SIZE_HA_CONFIG);
|
DynamicJsonDocument doc(EMSESP_JSON_SIZE_HA_CONFIG);
|
||||||
|
|
||||||
bool have_tag = !EMSdevice::tag_to_string(tag).empty() && (device_type != EMSdevice::DeviceType::BOILER); // ignore boiler
|
bool have_tag = !EMSdevice::tag_to_string(tag).empty() && (device_type != EMSdevice::DeviceType::BOILER); // ignore boiler
|
||||||
bool is_nested = nested_format_ || (device_type == EMSdevice::DeviceType::BOILER); // boiler never uses nested
|
bool is_nested = (nested_format_ == 1) || (device_type == EMSdevice::DeviceType::BOILER); // boiler never uses nested
|
||||||
|
|
||||||
// create entity by add the tag if present, seperating with a .
|
// create entity by add the tag if present, seperating with a .
|
||||||
char new_entity[50];
|
char new_entity[50];
|
||||||
@@ -904,7 +915,7 @@ void Mqtt::publish_mqtt_ha_sensor(uint8_t type, // EMSdevice
|
|||||||
|
|
||||||
// device name
|
// device name
|
||||||
char device_name[50];
|
char device_name[50];
|
||||||
strncpy(device_name, EMSdevice::device_type_2_device_name(device_type).c_str(), sizeof(device_name));
|
strlcpy(device_name, EMSdevice::device_type_2_device_name(device_type).c_str(), sizeof(device_name));
|
||||||
|
|
||||||
// build unique identifier which will be used in the topic
|
// build unique identifier which will be used in the topic
|
||||||
// and replacing all . with _ as not to break HA
|
// and replacing all . with _ as not to break HA
|
||||||
@@ -944,7 +955,7 @@ void Mqtt::publish_mqtt_ha_sensor(uint8_t type, // EMSdevice
|
|||||||
// look at the device value type
|
// look at the device value type
|
||||||
if (type == DeviceValueType::BOOL) {
|
if (type == DeviceValueType::BOOL) {
|
||||||
// binary sensor
|
// binary sensor
|
||||||
snprintf_P(topic, sizeof(topic), PSTR("homeassistant/binary_sensor/%s/%s/config"), mqtt_base_.c_str(), uniq.c_str()); // topic
|
snprintf_P(topic, sizeof(topic), PSTR("binary_sensor/%s/%s/config"), mqtt_base_.c_str(), uniq.c_str()); // topic
|
||||||
|
|
||||||
// how to render boolean. HA only accepts String values
|
// how to render boolean. HA only accepts String values
|
||||||
char result[10];
|
char result[10];
|
||||||
@@ -952,7 +963,7 @@ void Mqtt::publish_mqtt_ha_sensor(uint8_t type, // EMSdevice
|
|||||||
doc[F("payload_off")] = Helpers::render_boolean(result, false);
|
doc[F("payload_off")] = Helpers::render_boolean(result, false);
|
||||||
} else {
|
} else {
|
||||||
// normal HA sensor, not a boolean one
|
// normal HA sensor, not a boolean one
|
||||||
snprintf_P(topic, sizeof(topic), PSTR("homeassistant/sensor/%s/%s/config"), mqtt_base_.c_str(), uniq.c_str()); // topic
|
snprintf_P(topic, sizeof(topic), PSTR("sensor/%s/%s/config"), mqtt_base_.c_str(), uniq.c_str()); // topic
|
||||||
|
|
||||||
// unit of measure and map the HA icon
|
// unit of measure and map the HA icon
|
||||||
if (uom != DeviceValueUOM::NONE && uom != DeviceValueUOM::PUMP) {
|
if (uom != DeviceValueUOM::NONE && uom != DeviceValueUOM::PUMP) {
|
||||||
@@ -986,6 +997,7 @@ void Mqtt::publish_mqtt_ha_sensor(uint8_t type, // EMSdevice
|
|||||||
ids.add(ha_device);
|
ids.add(ha_device);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
doc.shrinkToFit();
|
||||||
publish_ha(topic, doc.as<JsonObject>());
|
publish_ha(topic, doc.as<JsonObject>());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -999,7 +1011,7 @@ const std::string Mqtt::tag_to_topic(uint8_t device_type, uint8_t tag) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// if there is a tag add it
|
// if there is a tag add it
|
||||||
if ((EMSdevice::tag_to_mqtt(tag).empty()) || (nested_format_ && (device_type != EMSdevice::DeviceType::BOILER))) {
|
if ((EMSdevice::tag_to_mqtt(tag).empty()) || ((nested_format_ == 1) && (device_type != EMSdevice::DeviceType::BOILER))) {
|
||||||
return EMSdevice::device_type_2_device_name(device_type) + "_data";
|
return EMSdevice::device_type_2_device_name(device_type) + "_data";
|
||||||
} else {
|
} else {
|
||||||
return EMSdevice::device_type_2_device_name(device_type) + "_data_" + EMSdevice::tag_to_mqtt(tag);
|
return EMSdevice::device_type_2_device_name(device_type) + "_data_" + EMSdevice::tag_to_mqtt(tag);
|
||||||
|
|||||||
@@ -158,11 +158,11 @@ class Mqtt {
|
|||||||
return bool_format_;
|
return bool_format_;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool nested_format() {
|
static uint8_t nested_format() {
|
||||||
return nested_format_;
|
return nested_format_;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void nested_format(bool nested_format) {
|
static void nested_format(uint8_t nested_format) {
|
||||||
nested_format_ = nested_format;
|
nested_format_ = nested_format;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -274,7 +274,7 @@ class Mqtt {
|
|||||||
static uint8_t bool_format_;
|
static uint8_t bool_format_;
|
||||||
static uint8_t ha_climate_format_;
|
static uint8_t ha_climate_format_;
|
||||||
static bool ha_enabled_;
|
static bool ha_enabled_;
|
||||||
static bool nested_format_;
|
static uint8_t nested_format_;
|
||||||
static uint8_t subscribe_format_;
|
static uint8_t subscribe_format_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ void Shower::loop() {
|
|||||||
if (!shower_on_ && (time_now - timer_start_) > SHOWER_MIN_DURATION) {
|
if (!shower_on_ && (time_now - timer_start_) > SHOWER_MIN_DURATION) {
|
||||||
shower_on_ = true;
|
shower_on_ = true;
|
||||||
send_mqtt_stat(true);
|
send_mqtt_stat(true);
|
||||||
|
publish_values();
|
||||||
LOG_DEBUG(F("[Shower] hot water still running, starting shower timer"));
|
LOG_DEBUG(F("[Shower] hot water still running, starting shower timer"));
|
||||||
}
|
}
|
||||||
// check if the shower has been on too long
|
// check if the shower has been on too long
|
||||||
@@ -83,12 +84,19 @@ void Shower::loop() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// reset everything
|
// reset everything
|
||||||
timer_start_ = 0;
|
timer_start_ = 0;
|
||||||
timer_pause_ = 0;
|
timer_pause_ = 0;
|
||||||
shower_on_ = false;
|
shower_on_ = false;
|
||||||
shower_alert_stop();
|
doing_cold_shot_ = false;
|
||||||
|
alert_timer_start_ = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// at this point we're in the shower cold shot (doing_cold_shot_ == true)
|
||||||
|
// keep repeating until the time is up
|
||||||
|
if ((time_now - alert_timer_start_) > SHOWER_COLDSHOT_DURATION) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -115,7 +123,7 @@ void Shower::send_mqtt_stat(bool state, bool force) {
|
|||||||
ids.add("ems-esp");
|
ids.add("ems-esp");
|
||||||
|
|
||||||
char topic[Mqtt::MQTT_TOPIC_MAX_SIZE];
|
char topic[Mqtt::MQTT_TOPIC_MAX_SIZE];
|
||||||
snprintf_P(topic, sizeof(topic), PSTR("homeassistant/binary_sensor/%s/shower_active/config"), Mqtt::base().c_str());
|
snprintf_P(topic, sizeof(topic), PSTR("binary_sensor/%s/shower_active/config"), Mqtt::base().c_str());
|
||||||
Mqtt::publish_ha(topic, doc.as<JsonObject>()); // publish the config payload with retain flag
|
Mqtt::publish_ha(topic, doc.as<JsonObject>()); // publish the config payload with retain flag
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -124,20 +132,17 @@ void Shower::send_mqtt_stat(bool state, bool force) {
|
|||||||
void Shower::shower_alert_stop() {
|
void Shower::shower_alert_stop() {
|
||||||
if (doing_cold_shot_) {
|
if (doing_cold_shot_) {
|
||||||
LOG_DEBUG(F("Shower Alert stopped"));
|
LOG_DEBUG(F("Shower Alert stopped"));
|
||||||
// Boiler::set_tapwarmwater_activated(true);
|
Command::call(EMSdevice::DeviceType::BOILER, "wwtapactivated", "true");
|
||||||
doing_cold_shot_ = false;
|
doing_cold_shot_ = false;
|
||||||
// showerColdShotStopTimer.detach(); // disable the timer
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// turn off hot water to send a shot of cold
|
// turn off hot water to send a shot of cold
|
||||||
void Shower::shower_alert_start() {
|
void Shower::shower_alert_start() {
|
||||||
if (shower_alert_) {
|
if (shower_alert_) {
|
||||||
LOG_DEBUG(F("Shower Alert started!"));
|
LOG_DEBUG(F("Shower Alert started"));
|
||||||
// Boiler::set_tapwarmwater_activated(false);
|
Command::call(EMSdevice::DeviceType::BOILER, "wwtapactivated", "false");
|
||||||
doing_cold_shot_ = true;
|
doing_cold_shot_ = true;
|
||||||
// start the timer for n seconds which will reset the water back to hot
|
alert_timer_start_ = uuid::get_uptime(); // timer starts now
|
||||||
// showerColdShotStopTimer.attach(SHOWER_COLDSHOT_DURATION, _showerColdShotStop);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
13
src/shower.h
13
src/shower.h
@@ -52,7 +52,7 @@ class Shower {
|
|||||||
static constexpr uint32_t SHOWER_PAUSE_TIME = 15000; // in ms. 15 seconds, max time if water is switched off & on during a shower
|
static constexpr uint32_t SHOWER_PAUSE_TIME = 15000; // in ms. 15 seconds, max time if water is switched off & on during a shower
|
||||||
static constexpr uint32_t SHOWER_MIN_DURATION = 120000; // in ms. 2 minutes, before recognizing its a shower
|
static constexpr uint32_t SHOWER_MIN_DURATION = 120000; // in ms. 2 minutes, before recognizing its a shower
|
||||||
static constexpr uint32_t SHOWER_OFFSET_TIME = 5000; // in ms. 5 seconds grace time, to calibrate actual time under the shower
|
static constexpr uint32_t SHOWER_OFFSET_TIME = 5000; // in ms. 5 seconds grace time, to calibrate actual time under the shower
|
||||||
static constexpr uint32_t SHOWER_COLDSHOT_DURATION = 10; // in seconds. 10 seconds for cold water before turning back hot water
|
static constexpr uint32_t SHOWER_COLDSHOT_DURATION = 10000; // 10 seconds for cold water before turning back hot water
|
||||||
static constexpr uint32_t SHOWER_MAX_DURATION = 420000; // in ms. 7 minutes, before trigger a shot of cold water
|
static constexpr uint32_t SHOWER_MAX_DURATION = 420000; // in ms. 7 minutes, before trigger a shot of cold water
|
||||||
|
|
||||||
void publish_values();
|
void publish_values();
|
||||||
@@ -63,10 +63,13 @@ class Shower {
|
|||||||
bool shower_alert_; // true if we want the alert of cold water
|
bool shower_alert_; // true if we want the alert of cold water
|
||||||
bool ha_configdone_ = false; // for HA MQTT Discovery
|
bool ha_configdone_ = false; // for HA MQTT Discovery
|
||||||
bool shower_on_;
|
bool shower_on_;
|
||||||
uint32_t timer_start_; // ms
|
uint32_t timer_start_; // ms
|
||||||
uint32_t timer_pause_; // ms
|
uint32_t timer_pause_; // ms
|
||||||
uint32_t duration_; // ms
|
uint32_t duration_; // ms
|
||||||
bool doing_cold_shot_; // true if we've just sent a jolt of cold water
|
|
||||||
|
// cold shot
|
||||||
|
uint32_t alert_timer_start_; // ms
|
||||||
|
bool doing_cold_shot_; // true if we've just sent a jolt of cold water
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace emsesp
|
} // namespace emsesp
|
||||||
|
|||||||
270
src/system.cpp
270
src/system.cpp
@@ -19,7 +19,7 @@
|
|||||||
#include "system.h"
|
#include "system.h"
|
||||||
#include "emsesp.h" // for send_raw_telegram() command
|
#include "emsesp.h" // for send_raw_telegram() command
|
||||||
|
|
||||||
#if defined(EMSESP_TEST)
|
#if defined(EMSESP_DEBUG)
|
||||||
#include "test/test.h"
|
#include "test/test.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -40,6 +40,7 @@ PButton System::myPButton_;
|
|||||||
// e.g. http://ems-esp/api?device=system&cmd=pin&data=1&id=2
|
// e.g. http://ems-esp/api?device=system&cmd=pin&data=1&id=2
|
||||||
bool System::command_pin(const char * value, const int8_t id) {
|
bool System::command_pin(const char * value, const int8_t id) {
|
||||||
if (!is_valid_gpio(id)) {
|
if (!is_valid_gpio(id)) {
|
||||||
|
LOG_INFO(F("invalid GPIO number"));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,12 +70,30 @@ bool System::command_fetch(const char * value, const int8_t id) {
|
|||||||
|
|
||||||
// mqtt publish
|
// mqtt publish
|
||||||
bool System::command_publish(const char * value, const int8_t id) {
|
bool System::command_publish(const char * value, const int8_t id) {
|
||||||
std::string ha(10, '\0');
|
std::string ha(14, '\0');
|
||||||
if (Helpers::value2string(value, ha)) {
|
if (Helpers::value2string(value, ha)) {
|
||||||
if (ha == "ha") {
|
if (ha == "ha") {
|
||||||
EMSESP::publish_all(true); // includes HA
|
EMSESP::publish_all(true); // includes HA
|
||||||
LOG_INFO(F("Publishing all data to MQTT, including HA configs"));
|
LOG_INFO(F("Publishing all data to MQTT, including HA configs"));
|
||||||
return true;
|
return true;
|
||||||
|
} else if (ha == "boiler") {
|
||||||
|
EMSESP::publish_device_values(EMSdevice::DeviceType::BOILER);
|
||||||
|
return true;
|
||||||
|
} else if (ha == "thermostat") {
|
||||||
|
EMSESP::publish_device_values(EMSdevice::DeviceType::THERMOSTAT);
|
||||||
|
return true;
|
||||||
|
} else if (ha == "solar") {
|
||||||
|
EMSESP::publish_device_values(EMSdevice::DeviceType::SOLAR);
|
||||||
|
return true;
|
||||||
|
} else if (ha == "mixer") {
|
||||||
|
EMSESP::publish_device_values(EMSdevice::DeviceType::MIXER);
|
||||||
|
return true;
|
||||||
|
} else if (ha == "other") {
|
||||||
|
EMSESP::publish_other_values();
|
||||||
|
return true;
|
||||||
|
} else if (ha == "dallassensor") {
|
||||||
|
EMSESP::publish_sensor_values(true);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -123,6 +142,7 @@ void System::syslog_start() {
|
|||||||
if (syslog_enabled_) {
|
if (syslog_enabled_) {
|
||||||
#ifndef EMSESP_STANDALONE
|
#ifndef EMSESP_STANDALONE
|
||||||
syslog_.start();
|
syslog_.start();
|
||||||
|
syslog_.log_level((uuid::log::Level)syslog_level_);
|
||||||
#endif
|
#endif
|
||||||
EMSESP::logger().info(F("Starting Syslog"));
|
EMSESP::logger().info(F("Starting Syslog"));
|
||||||
}
|
}
|
||||||
@@ -292,7 +312,7 @@ void System::button_OnVLongPress(PButton & b) {
|
|||||||
LOG_WARNING(F("Performing factory reset..."));
|
LOG_WARNING(F("Performing factory reset..."));
|
||||||
EMSESP::console_.loop();
|
EMSESP::console_.loop();
|
||||||
|
|
||||||
#ifdef EMSESP_TEST
|
#ifdef EMSESP_DEBUG
|
||||||
Test::listDir(LITTLEFS, FS_CONFIG_DIRECTORY, 3);
|
Test::listDir(LITTLEFS, FS_CONFIG_DIRECTORY, 3);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -397,23 +417,16 @@ void System::show_mem(const char * note) {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// send periodic MQTT message with system information
|
// create the json for heartbeat
|
||||||
void System::send_heartbeat() {
|
bool System::heartbeat_json(JsonObject & doc) {
|
||||||
// don't send heartbeat if WiFi or MQTT is not connected
|
|
||||||
if (!Mqtt::connected()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int8_t rssi;
|
int8_t rssi;
|
||||||
if (!ethernet_connected_) {
|
if (!ethernet_connected_) {
|
||||||
rssi = wifi_quality();
|
rssi = wifi_quality();
|
||||||
if (rssi == -1) {
|
if (rssi == -1) {
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
StaticJsonDocument<EMSESP_JSON_SIZE_SMALL> doc;
|
|
||||||
|
|
||||||
uint8_t ems_status = EMSESP::bus_status();
|
uint8_t ems_status = EMSESP::bus_status();
|
||||||
if (ems_status == EMSESP::BUS_STATUS_TX_ERRORS) {
|
if (ems_status == EMSESP::BUS_STATUS_TX_ERRORS) {
|
||||||
doc["status"] = FJSON("txerror");
|
doc["status"] = FJSON("txerror");
|
||||||
@@ -426,15 +439,20 @@ void System::send_heartbeat() {
|
|||||||
if (!ethernet_connected_) {
|
if (!ethernet_connected_) {
|
||||||
doc["rssi"] = rssi;
|
doc["rssi"] = rssi;
|
||||||
}
|
}
|
||||||
doc["uptime"] = uuid::log::format_timestamp_ms(uuid::get_uptime_ms(), 3);
|
doc["uptime"] = uuid::log::format_timestamp_ms(uuid::get_uptime_ms(), 3);
|
||||||
doc["uptime_sec"] = uuid::get_uptime_sec();
|
doc["uptime_sec"] = uuid::get_uptime_sec();
|
||||||
doc["mqttfails"] = Mqtt::publish_fails();
|
|
||||||
doc["rxreceived"] = EMSESP::rxservice_.telegram_count();
|
doc["rxreceived"] = EMSESP::rxservice_.telegram_count();
|
||||||
doc["rxfails"] = EMSESP::rxservice_.telegram_error_count();
|
doc["rxfails"] = EMSESP::rxservice_.telegram_error_count();
|
||||||
doc["txread"] = EMSESP::txservice_.telegram_read_count();
|
doc["txread"] = EMSESP::txservice_.telegram_read_count();
|
||||||
doc["txwrite"] = EMSESP::txservice_.telegram_write_count();
|
doc["txwrite"] = EMSESP::txservice_.telegram_write_count();
|
||||||
doc["txfails"] = EMSESP::txservice_.telegram_fail_count();
|
doc["txfails"] = EMSESP::txservice_.telegram_fail_count();
|
||||||
doc["dallasfails"] = EMSESP::sensor_fails();
|
if (Mqtt::enabled()) {
|
||||||
|
doc["mqttfails"] = Mqtt::publish_fails();
|
||||||
|
}
|
||||||
|
if (EMSESP::dallas_enabled()) {
|
||||||
|
doc["dallasfails"] = EMSESP::sensor_fails();
|
||||||
|
}
|
||||||
#ifndef EMSESP_STANDALONE
|
#ifndef EMSESP_STANDALONE
|
||||||
doc["freemem"] = ESP.getFreeHeap();
|
doc["freemem"] = ESP.getFreeHeap();
|
||||||
#endif
|
#endif
|
||||||
@@ -443,7 +461,22 @@ void System::send_heartbeat() {
|
|||||||
doc["adc"] = analog_;
|
doc["adc"] = analog_;
|
||||||
}
|
}
|
||||||
|
|
||||||
Mqtt::publish(F("heartbeat"), doc.as<JsonObject>()); // send to MQTT with retain off. This will add to MQTT queue.
|
return (doc.size() > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// send periodic MQTT message with system information
|
||||||
|
void System::send_heartbeat() {
|
||||||
|
// don't send heartbeat if WiFi or MQTT is not connected
|
||||||
|
if (!Mqtt::connected()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
StaticJsonDocument<EMSESP_JSON_SIZE_SMALL> doc;
|
||||||
|
JsonObject json = doc.to<JsonObject>();
|
||||||
|
|
||||||
|
if (heartbeat_json(json)) {
|
||||||
|
Mqtt::publish(F_(heartbeat), doc.as<JsonObject>()); // send to MQTT with retain off. This will add to MQTT queue.
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// measure and moving average adc
|
// measure and moving average adc
|
||||||
@@ -572,7 +605,7 @@ void System::commands_init() {
|
|||||||
Command::add(EMSdevice::DeviceType::SYSTEM, F_(fetch), System::command_fetch);
|
Command::add(EMSdevice::DeviceType::SYSTEM, F_(fetch), System::command_fetch);
|
||||||
Command::add_with_json(EMSdevice::DeviceType::SYSTEM, F_(info), System::command_info);
|
Command::add_with_json(EMSdevice::DeviceType::SYSTEM, F_(info), System::command_info);
|
||||||
Command::add_with_json(EMSdevice::DeviceType::SYSTEM, F_(settings), System::command_settings);
|
Command::add_with_json(EMSdevice::DeviceType::SYSTEM, F_(settings), System::command_settings);
|
||||||
#if defined(EMSESP_TEST)
|
#if defined(EMSESP_DEBUG)
|
||||||
Command::add(EMSdevice::DeviceType::SYSTEM, F("test"), System::command_test);
|
Command::add(EMSdevice::DeviceType::SYSTEM, F("test"), System::command_test);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@@ -635,12 +668,12 @@ void System::show_users(uuid::console::Shell & shell) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void System::show_system(uuid::console::Shell & shell) {
|
void System::show_system(uuid::console::Shell & shell) {
|
||||||
shell.printfln(F("Uptime: %s"), uuid::log::format_timestamp_ms(uuid::get_uptime_ms(), 3).c_str());
|
shell.printfln(F("Uptime: %s"), uuid::log::format_timestamp_ms(uuid::get_uptime_ms(), 3).c_str());
|
||||||
|
|
||||||
#ifndef EMSESP_STANDALONE
|
#ifndef EMSESP_STANDALONE
|
||||||
shell.printfln(F("SDK version: %s"), ESP.getSdkVersion());
|
shell.printfln(F("SDK version: %s"), ESP.getSdkVersion());
|
||||||
shell.printfln(F("CPU frequency: %u MHz"), ESP.getCpuFreqMHz());
|
shell.printfln(F("CPU frequency: %lu MHz"), ESP.getCpuFreqMHz());
|
||||||
shell.printfln(F("Free heap: %lu bytes"), (uint32_t)ESP.getFreeHeap());
|
shell.printfln(F("Free heap: %lu bytes"), (uint32_t)ESP.getFreeHeap());
|
||||||
shell.println();
|
shell.println();
|
||||||
|
|
||||||
switch (WiFi.status()) {
|
switch (WiFi.status()) {
|
||||||
@@ -656,7 +689,7 @@ void System::show_system(uuid::console::Shell & shell) {
|
|||||||
shell.printfln(F("WiFi: Network scan complete"));
|
shell.printfln(F("WiFi: Network scan complete"));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case WL_CONNECTED: {
|
case WL_CONNECTED:
|
||||||
shell.printfln(F("WiFi: Connected"));
|
shell.printfln(F("WiFi: Connected"));
|
||||||
shell.printfln(F("SSID: %s"), WiFi.SSID().c_str());
|
shell.printfln(F("SSID: %s"), WiFi.SSID().c_str());
|
||||||
shell.printfln(F("BSSID: %s"), WiFi.BSSIDstr().c_str());
|
shell.printfln(F("BSSID: %s"), WiFi.BSSIDstr().c_str());
|
||||||
@@ -666,7 +699,7 @@ void System::show_system(uuid::console::Shell & shell) {
|
|||||||
shell.printfln(F("IPv4 address: %s/%s"), uuid::printable_to_string(WiFi.localIP()).c_str(), uuid::printable_to_string(WiFi.subnetMask()).c_str());
|
shell.printfln(F("IPv4 address: %s/%s"), uuid::printable_to_string(WiFi.localIP()).c_str(), uuid::printable_to_string(WiFi.subnetMask()).c_str());
|
||||||
shell.printfln(F("IPv4 gateway: %s"), uuid::printable_to_string(WiFi.gatewayIP()).c_str());
|
shell.printfln(F("IPv4 gateway: %s"), uuid::printable_to_string(WiFi.gatewayIP()).c_str());
|
||||||
shell.printfln(F("IPv4 nameserver: %s"), uuid::printable_to_string(WiFi.dnsIP()).c_str());
|
shell.printfln(F("IPv4 nameserver: %s"), uuid::printable_to_string(WiFi.dnsIP()).c_str());
|
||||||
} break;
|
break;
|
||||||
|
|
||||||
case WL_CONNECT_FAILED:
|
case WL_CONNECT_FAILED:
|
||||||
shell.printfln(F("WiFi: Connection failed"));
|
shell.printfln(F("WiFi: Connection failed"));
|
||||||
@@ -718,150 +751,6 @@ void System::show_system(uuid::console::Shell & shell) {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// console commands to add
|
|
||||||
void System::console_commands(Shell & shell, unsigned int context) {
|
|
||||||
EMSESPShell::commands->add_command(ShellContext::SYSTEM,
|
|
||||||
CommandFlags::ADMIN,
|
|
||||||
flash_string_vector{F_(restart)},
|
|
||||||
[](Shell & shell __attribute__((unused)), const std::vector<std::string> & arguments __attribute__((unused))) { EMSESP::system_.restart(); });
|
|
||||||
|
|
||||||
EMSESPShell::commands->add_command(ShellContext::SYSTEM,
|
|
||||||
CommandFlags::ADMIN,
|
|
||||||
flash_string_vector{F_(wifi), F_(reconnect)},
|
|
||||||
[](Shell & shell __attribute__((unused)), const std::vector<std::string> & arguments __attribute__((unused))) { EMSESP::system_.wifi_reconnect(); });
|
|
||||||
|
|
||||||
EMSESPShell::commands->add_command(ShellContext::SYSTEM, CommandFlags::ADMIN, flash_string_vector{F_(format)}, [](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
|
|
||||||
shell.enter_password(F_(password_prompt), [=](Shell & shell, bool completed, const std::string & password) {
|
|
||||||
if (completed) {
|
|
||||||
EMSESP::esp8266React.getSecuritySettingsService()->read([&](SecuritySettings & securitySettings) {
|
|
||||||
if (securitySettings.jwtSecret.equals(password.c_str())) {
|
|
||||||
EMSESP::system_.format(shell);
|
|
||||||
} else {
|
|
||||||
shell.println(F("incorrect password"));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
EMSESPShell::commands->add_command(ShellContext::SYSTEM, CommandFlags::ADMIN, flash_string_vector{F_(passwd)}, [](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
|
|
||||||
shell.enter_password(F_(new_password_prompt1), [](Shell & shell, bool completed, const std::string & password1) {
|
|
||||||
if (completed) {
|
|
||||||
shell.enter_password(F_(new_password_prompt2), [password1](Shell & shell, bool completed, const std::string & password2) {
|
|
||||||
if (completed) {
|
|
||||||
if (password1 == password2) {
|
|
||||||
EMSESP::esp8266React.getSecuritySettingsService()->update(
|
|
||||||
[&](SecuritySettings & securitySettings) {
|
|
||||||
securitySettings.jwtSecret = password2.c_str();
|
|
||||||
return StateUpdateResult::CHANGED;
|
|
||||||
},
|
|
||||||
"local");
|
|
||||||
shell.println(F("su password updated"));
|
|
||||||
} else {
|
|
||||||
shell.println(F("Passwords do not match"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
EMSESPShell::commands->add_command(ShellContext::SYSTEM, CommandFlags::USER, flash_string_vector{F_(show)}, [=](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
|
|
||||||
EMSESP::system_.show_system(shell);
|
|
||||||
shell.println();
|
|
||||||
});
|
|
||||||
|
|
||||||
EMSESPShell::commands->add_command(ShellContext::SYSTEM,
|
|
||||||
CommandFlags::ADMIN,
|
|
||||||
flash_string_vector{F_(set), F_(hostname)},
|
|
||||||
flash_string_vector{F_(name_mandatory)},
|
|
||||||
[](Shell & shell, const std::vector<std::string> & arguments) {
|
|
||||||
shell.println("The network connection will be reset...");
|
|
||||||
Shell::loop_all();
|
|
||||||
delay(1000); // wait a second
|
|
||||||
EMSESP::esp8266React.getNetworkSettingsService()->update(
|
|
||||||
[&](NetworkSettings & networkSettings) {
|
|
||||||
networkSettings.hostname = arguments.front().c_str();
|
|
||||||
return StateUpdateResult::CHANGED;
|
|
||||||
},
|
|
||||||
"local");
|
|
||||||
});
|
|
||||||
|
|
||||||
EMSESPShell::commands->add_command(ShellContext::SYSTEM,
|
|
||||||
CommandFlags::ADMIN,
|
|
||||||
flash_string_vector{F_(set), F_(wifi), F_(ssid)},
|
|
||||||
flash_string_vector{F_(name_mandatory)},
|
|
||||||
[](Shell & shell, const std::vector<std::string> & arguments) {
|
|
||||||
EMSESP::esp8266React.getNetworkSettingsService()->updateWithoutPropagation([&](NetworkSettings & networkSettings) {
|
|
||||||
networkSettings.ssid = arguments.front().c_str();
|
|
||||||
return StateUpdateResult::CHANGED;
|
|
||||||
});
|
|
||||||
shell.println("Use `wifi reconnect` to save and apply the new settings");
|
|
||||||
});
|
|
||||||
|
|
||||||
EMSESPShell::commands->add_command(ShellContext::SYSTEM, CommandFlags::ADMIN, flash_string_vector{F_(set), F_(wifi), F_(password)}, [](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
|
|
||||||
shell.enter_password(F_(new_password_prompt1), [](Shell & shell, bool completed, const std::string & password1) {
|
|
||||||
if (completed) {
|
|
||||||
shell.enter_password(F_(new_password_prompt2), [password1](Shell & shell, bool completed, const std::string & password2) {
|
|
||||||
if (completed) {
|
|
||||||
if (password1 == password2) {
|
|
||||||
EMSESP::esp8266React.getNetworkSettingsService()->updateWithoutPropagation([&](NetworkSettings & networkSettings) {
|
|
||||||
networkSettings.password = password2.c_str();
|
|
||||||
return StateUpdateResult::CHANGED;
|
|
||||||
});
|
|
||||||
shell.println("Use `wifi reconnect` to save and apply the new settings");
|
|
||||||
} else {
|
|
||||||
shell.println(F("Passwords do not match"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
EMSESPShell::commands->add_command(ShellContext::SYSTEM,
|
|
||||||
CommandFlags::ADMIN,
|
|
||||||
flash_string_vector{F_(set), F_(board_profile)},
|
|
||||||
flash_string_vector{F_(name_mandatory)},
|
|
||||||
[](Shell & shell, const std::vector<std::string> & arguments) {
|
|
||||||
std::vector<uint8_t> data; // led, dallas, rx, tx, button
|
|
||||||
std::string board_profile = Helpers::toUpper(arguments.front());
|
|
||||||
if (!load_board_profile(data, board_profile)) {
|
|
||||||
shell.println(F("Invalid board profile"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
EMSESP::webSettingsService.update(
|
|
||||||
[&](WebSettings & settings) {
|
|
||||||
settings.board_profile = board_profile.c_str();
|
|
||||||
settings.led_gpio = data[0];
|
|
||||||
settings.dallas_gpio = data[1];
|
|
||||||
settings.rx_gpio = data[2];
|
|
||||||
settings.tx_gpio = data[3];
|
|
||||||
settings.pbutton_gpio = data[4];
|
|
||||||
return StateUpdateResult::CHANGED;
|
|
||||||
},
|
|
||||||
"local");
|
|
||||||
shell.printfln("Loaded board profile %s (%d,%d,%d,%d,%d)", board_profile.c_str(), data[0], data[1], data[2], data[3], data[4]);
|
|
||||||
EMSESP::system_.network_init(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
EMSESPShell::commands->add_command(ShellContext::SYSTEM, CommandFlags::USER, flash_string_vector{F_(set)}, [](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
|
|
||||||
EMSESP::esp8266React.getNetworkSettingsService()->read([&](NetworkSettings & networkSettings) {
|
|
||||||
shell.printfln(F_(hostname_fmt), networkSettings.hostname.isEmpty() ? uuid::read_flash_string(F_(unset)).c_str() : networkSettings.hostname.c_str());
|
|
||||||
shell.printfln(F_(wifi_ssid_fmt), networkSettings.ssid.isEmpty() ? uuid::read_flash_string(F_(unset)).c_str() : networkSettings.ssid.c_str());
|
|
||||||
shell.printfln(F_(wifi_password_fmt), networkSettings.ssid.isEmpty() ? F_(unset) : F_(asterisks));
|
|
||||||
});
|
|
||||||
EMSESP::webSettingsService.read([&](WebSettings & settings) { shell.printfln(F_(board_profile_fmt), settings.board_profile.c_str()); });
|
|
||||||
});
|
|
||||||
|
|
||||||
EMSESPShell::commands->add_command(ShellContext::SYSTEM, CommandFlags::ADMIN, flash_string_vector{F_(show), F_(users)}, [](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
|
|
||||||
EMSESP::system_.show_users(shell);
|
|
||||||
});
|
|
||||||
|
|
||||||
// enter the context
|
|
||||||
Console::enter_custom_context(shell, context);
|
|
||||||
}
|
|
||||||
|
|
||||||
// upgrade from previous versions of EMS-ESP
|
// upgrade from previous versions of EMS-ESP
|
||||||
// returns true if an upgrade was done
|
// returns true if an upgrade was done
|
||||||
bool System::check_upgrade() {
|
bool System::check_upgrade() {
|
||||||
@@ -971,12 +860,20 @@ bool System::command_settings(const char * value, const int8_t id, JsonObject &
|
|||||||
// export status information including some basic settings
|
// export status information including some basic settings
|
||||||
// e.g. http://ems-esp/api?device=system&cmd=info
|
// e.g. http://ems-esp/api?device=system&cmd=info
|
||||||
bool System::command_info(const char * value, const int8_t id, JsonObject & json) {
|
bool System::command_info(const char * value, const int8_t id, JsonObject & json) {
|
||||||
|
|
||||||
|
if (id == 0) {
|
||||||
|
return EMSESP::system_.heartbeat_json(json);
|
||||||
|
}
|
||||||
|
|
||||||
JsonObject node;
|
JsonObject node;
|
||||||
|
|
||||||
node = json.createNestedObject("System");
|
node = json.createNestedObject("System");
|
||||||
|
|
||||||
node["version"] = EMSESP_APP_VERSION;
|
node["version"] = EMSESP_APP_VERSION;
|
||||||
node["uptime"] = uuid::log::format_timestamp_ms(uuid::get_uptime_ms(), 3);
|
node["uptime"] = uuid::log::format_timestamp_ms(uuid::get_uptime_ms(), 3);
|
||||||
|
#ifndef EMSESP_STANDALONE
|
||||||
|
node["freemem"] = ESP.getFreeHeap();
|
||||||
|
#endif
|
||||||
|
|
||||||
node = json.createNestedObject("Status");
|
node = json.createNestedObject("Status");
|
||||||
|
|
||||||
@@ -999,12 +896,16 @@ bool System::command_info(const char * value, const int8_t id, JsonObject & json
|
|||||||
node["#read requests sent"] = EMSESP::txservice_.telegram_read_count();
|
node["#read requests sent"] = EMSESP::txservice_.telegram_read_count();
|
||||||
node["#write requests sent"] = EMSESP::txservice_.telegram_write_count();
|
node["#write requests sent"] = EMSESP::txservice_.telegram_write_count();
|
||||||
node["#incomplete telegrams"] = EMSESP::rxservice_.telegram_error_count();
|
node["#incomplete telegrams"] = EMSESP::rxservice_.telegram_error_count();
|
||||||
node["#tx fails"] = TxService::MAXIMUM_TX_RETRIES, EMSESP::txservice_.telegram_fail_count();
|
node["#tx fails"] = EMSESP::txservice_.telegram_fail_count();
|
||||||
node["rx line quality"] = EMSESP::rxservice_.quality();
|
node["rx line quality"] = EMSESP::rxservice_.quality();
|
||||||
node["tx line quality"] = EMSESP::txservice_.quality();
|
node["tx line quality"] = EMSESP::txservice_.quality();
|
||||||
node["#MQTT publish fails"] = Mqtt::publish_fails();
|
if (Mqtt::enabled()) {
|
||||||
node["#dallas sensors"] = EMSESP::sensor_devices().size();
|
node["#MQTT publish fails"] = Mqtt::publish_fails();
|
||||||
node["#dallas fails"] = EMSESP::sensor_fails();
|
}
|
||||||
|
if (EMSESP::dallas_enabled()) {
|
||||||
|
node["#dallas sensors"] = EMSESP::sensor_devices().size();
|
||||||
|
node["#dallas fails"] = EMSESP::sensor_fails();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
JsonArray devices2 = json.createNestedArray("Devices");
|
JsonArray devices2 = json.createNestedArray("Devices");
|
||||||
@@ -1020,11 +921,16 @@ bool System::command_info(const char * value, const int8_t id, JsonObject & json
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (EMSESP::sensor_devices().size()) {
|
||||||
|
JsonObject obj = devices2.createNestedObject();
|
||||||
|
obj["type"] = F("Dallassensor");
|
||||||
|
obj["name"] = F("Dallassensor");
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(EMSESP_TEST)
|
#if defined(EMSESP_DEBUG)
|
||||||
// run a test, e.g. http://ems-esp/api?device=system&cmd=test&data=boiler
|
// run a test, e.g. http://ems-esp/api?device=system&cmd=test&data=boiler
|
||||||
bool System::command_test(const char * value, const int8_t id) {
|
bool System::command_test(const char * value, const int8_t id) {
|
||||||
Test::run_test(value, id);
|
Test::run_test(value, id);
|
||||||
@@ -1037,11 +943,11 @@ bool System::command_test(const char * value, const int8_t id) {
|
|||||||
// returns false if profile is not found
|
// returns false if profile is not found
|
||||||
bool System::load_board_profile(std::vector<uint8_t> & data, const std::string & board_profile) {
|
bool System::load_board_profile(std::vector<uint8_t> & data, const std::string & board_profile) {
|
||||||
if (board_profile == "S32") {
|
if (board_profile == "S32") {
|
||||||
data = {2, 3, 23, 5, 0}; // BBQKees Gateway S32
|
data = {2, 18, 23, 5, 0}; // BBQKees Gateway S32
|
||||||
} else if (board_profile == "E32") {
|
} else if (board_profile == "E32") {
|
||||||
data = {2, 4, 5, 17, 33}; // BBQKees Gateway E32
|
data = {2, 4, 5, 17, 33}; // BBQKees Gateway E32
|
||||||
} else if (board_profile == "MT-ET") {
|
} else if (board_profile == "MH-ET") {
|
||||||
data = {2, 18, 23, 5, 0}; // MT-ET Live D1 Mini
|
data = {2, 18, 23, 5, 0}; // MH-ET Live D1 Mini
|
||||||
} else if (board_profile == "NODEMCU") {
|
} else if (board_profile == "NODEMCU") {
|
||||||
data = {2, 18, 23, 5, 0}; // NodeMCU 32S
|
data = {2, 18, 23, 5, 0}; // NodeMCU 32S
|
||||||
} else if (board_profile == "LOLIN") {
|
} else if (board_profile == "LOLIN") {
|
||||||
|
|||||||
10
src/system.h
10
src/system.h
@@ -48,14 +48,13 @@ class System {
|
|||||||
void loop();
|
void loop();
|
||||||
|
|
||||||
// commands
|
// commands
|
||||||
static void console_commands(Shell & shell, unsigned int context);
|
|
||||||
static bool command_pin(const char * value, const int8_t id);
|
static bool command_pin(const char * value, const int8_t id);
|
||||||
static bool command_send(const char * value, const int8_t id);
|
static bool command_send(const char * value, const int8_t id);
|
||||||
static bool command_publish(const char * value, const int8_t id);
|
static bool command_publish(const char * value, const int8_t id);
|
||||||
static bool command_fetch(const char * value, const int8_t id);
|
static bool command_fetch(const char * value, const int8_t id);
|
||||||
static bool command_info(const char * value, const int8_t id, JsonObject & json);
|
static bool command_info(const char * value, const int8_t id, JsonObject & json);
|
||||||
static bool command_settings(const char * value, const int8_t id, JsonObject & json);
|
static bool command_settings(const char * value, const int8_t id, JsonObject & json);
|
||||||
#if defined(EMSESP_TEST)
|
#if defined(EMSESP_DEBUG)
|
||||||
static bool command_test(const char * value, const int8_t id);
|
static bool command_test(const char * value, const int8_t id);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -68,6 +67,7 @@ class System {
|
|||||||
void wifi_tweak();
|
void wifi_tweak();
|
||||||
void syslog_start();
|
void syslog_start();
|
||||||
bool check_upgrade();
|
bool check_upgrade();
|
||||||
|
bool heartbeat_json(JsonObject & json);
|
||||||
void send_heartbeat();
|
void send_heartbeat();
|
||||||
|
|
||||||
void led_init(bool refresh);
|
void led_init(bool refresh);
|
||||||
@@ -103,6 +103,9 @@ class System {
|
|||||||
return true;
|
return true;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
void show_system(uuid::console::Shell & shell);
|
||||||
|
void wifi_reconnect();
|
||||||
|
void show_users(uuid::console::Shell & shell);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static uuid::log::Logger logger_;
|
static uuid::log::Logger logger_;
|
||||||
@@ -135,9 +138,6 @@ class System {
|
|||||||
void system_check();
|
void system_check();
|
||||||
void measure_analog();
|
void measure_analog();
|
||||||
|
|
||||||
void show_system(uuid::console::Shell & shell);
|
|
||||||
void show_users(uuid::console::Shell & shell);
|
|
||||||
void wifi_reconnect();
|
|
||||||
int8_t wifi_quality();
|
int8_t wifi_quality();
|
||||||
|
|
||||||
bool system_healthy_ = false;
|
bool system_healthy_ = false;
|
||||||
|
|||||||
@@ -419,7 +419,7 @@ void TxService::add(uint8_t operation, const uint8_t * data, const uint8_t lengt
|
|||||||
}
|
}
|
||||||
|
|
||||||
// build header. src, dest and offset have fixed positions
|
// build header. src, dest and offset have fixed positions
|
||||||
uint8_t src = data[0];
|
uint8_t src = ems_bus_id(); // data[0]; we can only send data with own bus_id.
|
||||||
uint8_t dest = data[1];
|
uint8_t dest = data[1];
|
||||||
uint8_t offset = data[3];
|
uint8_t offset = data[3];
|
||||||
|
|
||||||
@@ -485,11 +485,16 @@ void TxService::add(uint8_t operation, const uint8_t * data, const uint8_t lengt
|
|||||||
}
|
}
|
||||||
|
|
||||||
// send a Tx telegram to request data from an EMS device
|
// send a Tx telegram to request data from an EMS device
|
||||||
void TxService::read_request(const uint16_t type_id, const uint8_t dest, const uint8_t offset) {
|
void TxService::read_request(const uint16_t type_id, const uint8_t dest, const uint8_t offset, const uint8_t length) {
|
||||||
LOG_DEBUG(F("Tx read request to device 0x%02X for type ID 0x%02X"), dest, type_id);
|
LOG_DEBUG(F("Tx read request to device 0x%02X for type ID 0x%02X"), dest, type_id);
|
||||||
|
|
||||||
uint8_t message_data[1] = {EMS_MAX_TELEGRAM_LENGTH}; // request all data, 32 bytes
|
uint8_t message_data[1] = {EMS_MAX_TELEGRAM_LENGTH}; // request all data, 32 bytes
|
||||||
add(Telegram::Operation::TX_READ, dest, type_id, offset, message_data, 1, 0);
|
// if length set, publish result and set telegram to front
|
||||||
|
if (length) {
|
||||||
|
message_data[0] = length;
|
||||||
|
EMSESP::set_read_id(type_id);
|
||||||
|
}
|
||||||
|
add(Telegram::Operation::TX_READ, dest, type_id, offset, message_data, 1, 0, length != 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send a raw telegram to the bus, telegram is a text string of hex values
|
// Send a raw telegram to the bus, telegram is a text string of hex values
|
||||||
@@ -541,7 +546,7 @@ void TxService::retry_tx(const uint8_t operation, const uint8_t * data, const ui
|
|||||||
reset_retry_count(); // give up
|
reset_retry_count(); // give up
|
||||||
increment_telegram_fail_count(); // another Tx fail
|
increment_telegram_fail_count(); // another Tx fail
|
||||||
|
|
||||||
LOG_ERROR(F("Last Tx %s operation failed after %d retries. Ignoring request."), (operation == Telegram::Operation::TX_WRITE) ? F("Write") : F("Read"), MAXIMUM_TX_RETRIES);
|
LOG_ERROR(F("Last Tx %s operation failed after %d retries. Ignoring request: %s"), (operation == Telegram::Operation::TX_WRITE) ? F("Write") : F("Read"), MAXIMUM_TX_RETRIES), telegram_last_->to_string().c_str();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -264,7 +264,7 @@ class TxService : public EMSbus {
|
|||||||
void send();
|
void send();
|
||||||
void add(const uint8_t operation, const uint8_t dest, const uint16_t type_id, const uint8_t offset, uint8_t * message_data, const uint8_t message_length, const uint16_t validateid, const bool front = false);
|
void add(const uint8_t operation, const uint8_t dest, const uint16_t type_id, const uint8_t offset, uint8_t * message_data, const uint8_t message_length, const uint16_t validateid, const bool front = false);
|
||||||
void add(const uint8_t operation, const uint8_t * data, const uint8_t length, const uint16_t validateid, const bool front = false);
|
void add(const uint8_t operation, const uint8_t * data, const uint8_t length, const uint16_t validateid, const bool front = false);
|
||||||
void read_request(const uint16_t type_id, const uint8_t dest, const uint8_t offset = 0);
|
void read_request(const uint16_t type_id, const uint8_t dest, const uint8_t offset = 0, const uint8_t length = 0);
|
||||||
void send_raw(const char * telegram_data);
|
void send_raw(const char * telegram_data);
|
||||||
void send_poll();
|
void send_poll();
|
||||||
void retry_tx(const uint8_t operation, const uint8_t * data, const uint8_t length);
|
void retry_tx(const uint8_t operation, const uint8_t * data, const uint8_t length);
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#if defined(EMSESP_TEST)
|
#if defined(EMSESP_DEBUG)
|
||||||
|
|
||||||
#include "test.h"
|
#include "test.h"
|
||||||
|
|
||||||
@@ -191,7 +191,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd) {
|
|||||||
|
|
||||||
std::string command(20, '\0');
|
std::string command(20, '\0');
|
||||||
if ((cmd.empty()) || (cmd == "default")) {
|
if ((cmd.empty()) || (cmd == "default")) {
|
||||||
command = EMSESP_TEST_DEFAULT;
|
command = EMSESP_DEBUG_DEFAULT;
|
||||||
} else {
|
} else {
|
||||||
command = cmd;
|
command = cmd;
|
||||||
}
|
}
|
||||||
@@ -376,7 +376,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd) {
|
|||||||
if (command == "boiler") {
|
if (command == "boiler") {
|
||||||
shell.printfln(F("Testing boiler..."));
|
shell.printfln(F("Testing boiler..."));
|
||||||
Mqtt::ha_enabled(false);
|
Mqtt::ha_enabled(false);
|
||||||
Mqtt::nested_format(true);
|
Mqtt::nested_format(1);
|
||||||
|
|
||||||
run_test("boiler");
|
run_test("boiler");
|
||||||
shell.invoke_command("show devices");
|
shell.invoke_command("show devices");
|
||||||
@@ -399,6 +399,15 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd) {
|
|||||||
shell.invoke_command("show mqtt");
|
shell.invoke_command("show mqtt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (command == "shower_alert") {
|
||||||
|
shell.printfln(F("Testing Shower Alert..."));
|
||||||
|
|
||||||
|
run_test("boiler");
|
||||||
|
|
||||||
|
// device type, command, data
|
||||||
|
Command::call(EMSdevice::DeviceType::BOILER, "wwtapactivated", "false");
|
||||||
|
}
|
||||||
|
|
||||||
if (command == "fr120") {
|
if (command == "fr120") {
|
||||||
shell.printfln(F("Testing adding a thermostat FR120..."));
|
shell.printfln(F("Testing adding a thermostat FR120..."));
|
||||||
|
|
||||||
@@ -414,8 +423,8 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd) {
|
|||||||
if (command == "ha") {
|
if (command == "ha") {
|
||||||
shell.printfln(F("Testing HA discovery"));
|
shell.printfln(F("Testing HA discovery"));
|
||||||
Mqtt::ha_enabled(true);
|
Mqtt::ha_enabled(true);
|
||||||
// Mqtt::nested_format(true);
|
// Mqtt::nested_format(1);
|
||||||
Mqtt::nested_format(false);
|
Mqtt::nested_format(2);
|
||||||
|
|
||||||
// run_test("boiler");
|
// run_test("boiler");
|
||||||
run_test("thermostat");
|
run_test("thermostat");
|
||||||
@@ -436,11 +445,11 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd) {
|
|||||||
run_test("mixer");
|
run_test("mixer");
|
||||||
|
|
||||||
// first with nested
|
// first with nested
|
||||||
Mqtt::nested_format(true);
|
Mqtt::nested_format(1);
|
||||||
shell.invoke_command("call system publish");
|
shell.invoke_command("call system publish");
|
||||||
|
|
||||||
// then without nested
|
// then without nested
|
||||||
Mqtt::nested_format(false);
|
Mqtt::nested_format(2);
|
||||||
shell.invoke_command("call system publish");
|
shell.invoke_command("call system publish");
|
||||||
shell.invoke_command("show mqtt");
|
shell.invoke_command("show mqtt");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#if defined(EMSESP_TEST)
|
#if defined(EMSESP_DEBUG)
|
||||||
|
|
||||||
#ifndef EMSESP_TEST_H
|
#ifndef EMSESP_TEST_H
|
||||||
#define EMSESP_TEST_H
|
#define EMSESP_TEST_H
|
||||||
@@ -25,16 +25,18 @@
|
|||||||
|
|
||||||
namespace emsesp {
|
namespace emsesp {
|
||||||
|
|
||||||
// #define EMSESP_TEST_DEFAULT "thermostat"
|
// #define EMSESP_DEBUG_DEFAULT "thermostat"
|
||||||
// #define EMSESP_TEST_DEFAULT "solar"
|
// #define EMSESP_DEBUG_DEFAULT "solar"
|
||||||
// #define EMSESP_TEST_DEFAULT "mixer"
|
// #define EMSESP_DEBUG_DEFAULT "mixer"
|
||||||
// #define EMSESP_TEST_DEFAULT "web"
|
// #define EMSESP_DEBUG_DEFAULT "web"
|
||||||
// #define EMSESP_TEST_DEFAULT "general"
|
// #define EMSESP_DEBUG_DEFAULT "general"
|
||||||
// #define EMSESP_TEST_DEFAULT "boiler"
|
// #define EMSESP_DEBUG_DEFAULT "boiler"
|
||||||
// #define EMSESP_TEST_DEFAULT "mqtt2"
|
// #define EMSESP_DEBUG_DEFAULT "mqtt2"
|
||||||
// #define EMSESP_TEST_DEFAULT "mqtt_nested"
|
// #define EMSESP_DEBUG_DEFAULT "mqtt_nested"
|
||||||
// #define EMSESP_TEST_DEFAULT "ha"
|
// #define EMSESP_DEBUG_DEFAULT "ha"
|
||||||
#define EMSESP_TEST_DEFAULT "board_profile"
|
// #define EMSESP_DEBUG_DEFAULT "board_profile"
|
||||||
|
#define EMSESP_DEBUG_DEFAULT "shower_alert"
|
||||||
|
|
||||||
|
|
||||||
class Test {
|
class Test {
|
||||||
public:
|
public:
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
#define EMSESP_APP_VERSION "3.0.1"
|
#define EMSESP_APP_VERSION "3.1.0"
|
||||||
#define EMSESP_PLATFORM "ESP32"
|
#define EMSESP_PLATFORM "ESP32"
|
||||||
|
|||||||
Reference in New Issue
Block a user