digital_out, settings #1375, dev.5b

This commit is contained in:
MichaelDvP
2023-11-02 19:13:22 +01:00
parent 1097b519ae
commit 8307b0920c
21 changed files with 325 additions and 195 deletions

View File

@@ -29,7 +29,7 @@
"@types/imagemin": "^8.0.3",
"@types/lodash-es": "^4.17.10",
"@types/node": "^20.8.10",
"@types/react": "^18.2.33",
"@types/react": "^18.2.34",
"@types/react-dom": "^18.2.14",
"@types/react-router-dom": "^5.3.3",
"alova": "^2.13.1",
@@ -61,7 +61,7 @@
"eslint-import-resolver-typescript": "^3.6.1",
"eslint-plugin-autofix": "^1.1.0",
"eslint-plugin-import": "^2.29.0",
"eslint-plugin-jsx-a11y": "^6.7.1",
"eslint-plugin-jsx-a11y": "^6.8.0",
"eslint-plugin-prettier": "alpha",
"eslint-plugin-react": "^7.33.2",
"eslint-plugin-react-hooks": "^4.6.0",

View File

@@ -323,7 +323,14 @@ const de: Translation = {
WRITEABLE: 'Schreibbar',
SHOWING: 'Anzeigen von',
SEARCH: 'Suche',
CERT: 'TLS Zertifikat (Freilassen um TLS zu deaktivieren)'
CERT: 'TLS Zertifikat (Freilassen um TLS zu deaktivieren)',
ON: 'An',
OFF: 'Aus',
POLARITY: 'Polarität',
ACTIVEHIGH: 'Aktiv Positiv',
ACTIVELOW: 'Aktiv Negativ',
UNCHANGED: 'Unverändert',
ALWAYS: 'Immer'
};
export default de;

View File

@@ -323,7 +323,14 @@ const en: Translation = {
WRITEABLE: 'Writeable',
SHOWING: 'Showing',
SEARCH: 'Search',
CERT: 'TLS root certificate (leave blank to disable TLS)'
CERT: 'TLS root certificate (leave blank to disable TLS)',
ON: 'On',
OFF: 'Off',
POLARITY: 'Polarity',
ACTIVEHIGH: 'Active High',
ACTIVELOW: 'Active Low',
UNCHANGED: 'Unchanged',
ALWAYS: 'Always'
};
export default en;

View File

@@ -323,7 +323,14 @@ const fr: Translation = {
WRITEABLE: 'Writeable', // TODO translate
SHOWING: 'Showing', // TODO translate
SEARCH: 'Search', // TODO translate
CERT: 'TLS root certificate (leave blank to disable TLS)' // TODO translate
CERT: 'TLS root certificate (leave blank to disable TLS)', // TODO translate
ON: 'On', // TODO translate
OFF: 'Off', // TODO translate
POLARITY: 'Polarity', // TODO translate
ACTIVEHIGH: 'Active High', // TODO translate
ACTIVELOW: 'Active Low', // TODO translate
UNCHANGED: 'Unchanged', // TODO translate
ALWAYS: 'Always' // TODO translate
};
export default fr;

View File

@@ -325,7 +325,14 @@ const it: Translation = {
WRITEABLE: 'Scrivibile',
SHOWING: 'Visualizza',
SEARCH: 'Ricerca',
CERT: 'TLS root certificate (leave blank to disable TLS)' // TODO translate
CERT: 'TLS root certificate (leave blank to disable TLS)', // TODO translate
ON: 'On', // TODO translate
OFF: 'Off', // TODO translate
POLARITY: 'Polarity', // TODO translate
ACTIVEHIGH: 'Active High', // TODO translate
ACTIVELOW: 'Active Low', // TODO translate
UNCHANGED: 'Unchanged', // TODO translate
ALWAYS: 'Always' // TODO translate
};
export default it;

View File

@@ -323,7 +323,14 @@ const nl: Translation = {
WRITEABLE: 'Beschrijfbare',
SHOWING: 'Tonen',
SEARCH: 'Zoek',
CERT: 'TLS rootcertificaat (laat leeg om TLS uit te schakelen)'
CERT: 'TLS rootcertificaat (laat leeg om TLS uit te schakelen)',
ON: 'On', // TODO translate
OFF: 'Off', // TODO translate
POLARITY: 'Polarity', // TODO translate
ACTIVEHIGH: 'Active High', // TODO translate
ACTIVELOW: 'Active Low', // TODO translate
UNCHANGED: 'Unchanged', // TODO translate
ALWAYS: 'Always' // TODO translate
};
export default nl;

View File

@@ -323,7 +323,14 @@ const no: Translation = {
WRITEABLE: 'Writeable', // TODO translate
SHOWING: 'Showing', // TODO translate
SEARCH: 'Search', // TODO translate
CERT: 'TLS root certificate (leave blank to disable TLS)' // TODO translate
CERT: 'TLS root certificate (leave blank to disable TLS)', // TODO translate
ON: 'On', // TODO translate
OFF: 'Off', // TODO translate
POLARITY: 'Polarity', // TODO translate
ACTIVEHIGH: 'Active High', // TODO translate
ACTIVELOW: 'Active Low', // TODO translate
UNCHANGED: 'Unchanged', // TODO translate
ALWAYS: 'Always' // TODO translate
};
export default no;

View File

@@ -323,7 +323,14 @@ const pl: BaseTranslation = {
WRITEABLE: 'zapisywalna',
SHOWING: 'Wyświetlane',
SEARCH: 'Szukaj',
CERT: 'TLS root certificate (leave blank to disable TLS)' // TODO translate
CERT: 'TLS root certificate (leave blank to disable TLS)', // TODO translate
ON: 'On', // TODO translate
OFF: 'Off', // TODO translate
POLARITY: 'Polarity', // TODO translate
ACTIVEHIGH: 'Active High', // TODO translate
ACTIVELOW: 'Active Low', // TODO translate
UNCHANGED: 'Unchanged', // TODO translate
ALWAYS: 'Always' // TODO translate
};
export default pl;

View File

@@ -323,7 +323,14 @@ const sv: Translation = {
WRITEABLE: 'Writeable', // TODO translate
SHOWING: 'Showing', // TODO translate
SEARCH: 'Search', // TODO translate
CERT: 'TLS root certificate (leave blank to disable TLS)' // TODO translate
CERT: 'TLS root certificate (leave blank to disable TLS)', // TODO translate
ON: 'On', // TODO translate
OFF: 'Off', // TODO translate
POLARITY: 'Polarity', // TODO translate
ACTIVEHIGH: 'Active High', // TODO translate
ACTIVELOW: 'Active Low', // TODO translate
UNCHANGED: 'Unchanged', // TODO translate
ALWAYS: 'Always' // TODO translate
};
export default sv;

View File

@@ -323,7 +323,14 @@ const tr: Translation = {
WRITEABLE: 'Writeable', // TODO translate
SHOWING: 'Showing', // TODO translate
SEARCH: 'Search', // TODO translate
CERT: 'TLS root certificate (leave blank to disable TLS)' // TODO translate
CERT: 'TLS root certificate (leave blank to disable TLS)', // TODO translate
ON: 'On', // TODO translate
OFF: 'Off', // TODO translate
POLARITY: 'Polarity', // TODO translate
ACTIVEHIGH: 'Active High', // TODO translate
ACTIVELOW: 'Active Low', // TODO translate
UNCHANGED: 'Unchanged', // TODO translate
ALWAYS: 'Always' // TODO translate
};
export default tr;

View File

@@ -16,7 +16,7 @@ import DashboardSensorsAnalogDialog from './DashboardSensorsAnalogDialog';
import DashboardSensorsTemperatureDialog from './DashboardSensorsTemperatureDialog';
import * as EMSESP from './api';
import { DeviceValueUOM, DeviceValueUOM_s, AnalogTypeNames } from './types';
import { DeviceValueUOM, DeviceValueUOM_s, AnalogTypeNames, AnalogType } from './types';
import { temperatureSensorItemValidation, analogSensorItemValidation } from './validators';
import type { TemperatureSensor, AnalogSensor } from './types';
import type { FC } from 'react';
@@ -38,7 +38,8 @@ const DashboardSensors: FC = () => {
initialData: {
ts: [],
as: [],
analog_enabled: false
analog_enabled: false,
platform: 'ESP32'
}
});
@@ -391,7 +392,11 @@ const DashboardSensors: FC = () => {
<Cell stiff>{a.g}</Cell>
<Cell>{a.n}</Cell>
<Cell stiff>{AnalogTypeNames[a.t]} </Cell>
{a.t === AnalogType.DIGITAL_OUT || a.t === AnalogType.DIGITAL_IN ? (
<Cell stiff>{a.v ? LL.ON() : LL.OFF()}</Cell>
) : (
<Cell stiff>{a.t ? formatValue(a.v, a.u) : ''}</Cell>
)}
</Row>
))}
</Body>
@@ -402,6 +407,8 @@ const DashboardSensors: FC = () => {
return (
<SectionContent title={LL.SENSOR_DATA()} titleGutter>
{sensorData.ts.length > 0 && (
<>
<Typography sx={{ pt: 2, pb: 1 }} variant="h6" color="secondary">
{LL.TEMP_SENSORS()}
</Typography>
@@ -415,6 +422,8 @@ const DashboardSensors: FC = () => {
validator={temperatureSensorItemValidation()}
/>
)}
</>
)}
{sensorData?.analog_enabled === true && (
<>
@@ -429,7 +438,7 @@ const DashboardSensors: FC = () => {
onSave={onAnalogDialogSave}
creating={creating}
selectedItem={selectedAnalogSensor}
validator={analogSensorItemValidation(sensorData.as, creating)}
validator={analogSensorItemValidation(sensorData.as, creating, sensorData.platform)}
/>
)}
</>
@@ -442,6 +451,7 @@ const DashboardSensors: FC = () => {
{LL.REFRESH()}
</Button>
</Box>
{sensorData?.analog_enabled === true && (
<Button
variant="outlined"
color="primary"
@@ -450,6 +460,7 @@ const DashboardSensors: FC = () => {
>
{LL.ADD(0) + ' ' + LL.ANALOG_SENSOR(1)}
</Button>
)}
</Box>
</ButtonRow>
</SectionContent>

View File

@@ -89,7 +89,6 @@ const DashboardSensorsAnalogDialog = ({
fieldErrors={fieldErrors}
name="g"
label="GPIO"
disabled={!creating}
value={numberValue(editItem.g)}
type="number"
variant="outlined"
@@ -124,7 +123,6 @@ const DashboardSensorsAnalogDialog = ({
</TextField>
</Grid>
{editItem.t >= AnalogType.COUNTER && editItem.t <= AnalogType.RATE && (
<>
<Grid item xs={4}>
<TextField name="u" label={LL.UNIT()} value={editItem.u} fullWidth select onChange={updateFormValue}>
{DeviceValueUOM_s.map((val, i) => (
@@ -134,6 +132,7 @@ const DashboardSensorsAnalogDialog = ({
))}
</TextField>
</Grid>
)}
{editItem.t === AnalogType.ADC && (
<Grid item xs={4}>
<TextField
@@ -165,6 +164,7 @@ const DashboardSensorsAnalogDialog = ({
/>
</Grid>
)}
{editItem.t >= AnalogType.COUNTER && editItem.t <= AnalogType.RATE && (
<Grid item xs={4}>
<TextField
name="f"
@@ -177,13 +177,12 @@ const DashboardSensorsAnalogDialog = ({
inputProps={{ step: '0.001' }}
/>
</Grid>
</>
)}
{editItem.t === AnalogType.DIGITAL_OUT && (editItem.g === 25 || editItem.g === 26) && (
<Grid item xs={4}>
<TextField
name="o"
label={LL.VALUE(1)}
label={LL.VALUE(0)}
value={numberValue(editItem.o)}
fullWidth
type="number"
@@ -194,20 +193,55 @@ const DashboardSensorsAnalogDialog = ({
</Grid>
)}
{editItem.t === AnalogType.DIGITAL_OUT && editItem.g !== 25 && editItem.g !== 26 && (
<>
<Grid item xs={4}>
<TextField
name="o"
label={LL.VALUE(1)}
label={LL.VALUE(0)}
value={numberValue(editItem.o)}
fullWidth
type="number"
select
variant="outlined"
onChange={updateFormValue}
inputProps={{ min: '0', max: '1', step: '1' }}
/>
>
<MenuItem value={0}>{LL.OFF()}</MenuItem>
<MenuItem value={1}>{LL.ON()}</MenuItem>
</TextField>
</Grid>
<Grid item xs={4}>
<TextField
name="f"
label={LL.POLARITY()}
value={editItem.f}
fullWidth
select
onChange={updateFormValue}
>
<MenuItem value={1}>{LL.ACTIVEHIGH()}</MenuItem>
<MenuItem value={-1}>{LL.ACTIVELOW()}</MenuItem>
</TextField>
</Grid>
<Grid item xs={4}>
<TextField
name="u"
label={LL.STARTVALUE()}
value={editItem.u}
fullWidth
select
onChange={updateFormValue}
>
<MenuItem value={0}>{LL.UNCHANGED()}</MenuItem>
<MenuItem value={1}>
{LL.ALWAYS()}&nbsp;{LL.OFF()}
</MenuItem>
<MenuItem value={2}>
{LL.ALWAYS()}&nbsp;{LL.ON()}
</MenuItem>
</TextField>
</Grid>
</>
)}
{editItem.t >= AnalogType.PWM_0 && (
{(editItem.t === AnalogType.PWM_0 || editItem.t === AnalogType.PWM_1 || editItem.t === AnalogType.PWM_2) && (
<>
<Grid item xs={4}>
<TextField

View File

@@ -102,6 +102,7 @@ export interface SensorData {
ts: TemperatureSensor[];
as: AnalogSensor[];
analog_enabled: boolean;
platform: string;
}
export interface CoreData {

View File

@@ -188,12 +188,12 @@ export const isGPIOUniqueValidator = (sensors: AnalogSensor[]) => ({
}
});
export const analogSensorItemValidation = (sensors: AnalogSensor[], creating: boolean) =>
export const analogSensorItemValidation = (sensors: AnalogSensor[], creating: boolean, platform: string) =>
new Schema({
n: [{ required: true, message: 'Name is required' }],
g: [
{ required: true, message: 'GPIO is required' },
GPIO_VALIDATOR,
platform === 'ESP32-S3' ? GPIO_VALIDATORS3 : platform === 'ESP32-C3' ? GPIO_VALIDATORC3 : GPIO_VALIDATOR,
...(creating ? [isGPIOUniqueValidator(sensors)] : [])
]
});

View File

@@ -267,7 +267,7 @@ __metadata:
languageName: node
linkType: hard
"@babel/runtime@npm:^7.0.0, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.18.3, @babel/runtime@npm:^7.20.7, @babel/runtime@npm:^7.21.0, @babel/runtime@npm:^7.23.2, @babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.7.6, @babel/runtime@npm:^7.8.7":
"@babel/runtime@npm:^7.0.0, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.18.3, @babel/runtime@npm:^7.21.0, @babel/runtime@npm:^7.23.2, @babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.7.6, @babel/runtime@npm:^7.8.7":
version: 7.23.2
resolution: "@babel/runtime@npm:7.23.2"
dependencies:
@@ -1359,7 +1359,7 @@ __metadata:
languageName: node
linkType: hard
"@types/react@npm:*, @types/react@npm:^18.2.33":
"@types/react@npm:*":
version: 18.2.33
resolution: "@types/react@npm:18.2.33"
dependencies:
@@ -1370,6 +1370,17 @@ __metadata:
languageName: node
linkType: hard
"@types/react@npm:^18.2.34":
version: 18.2.34
resolution: "@types/react@npm:18.2.34"
dependencies:
"@types/prop-types": "npm:*"
"@types/scheduler": "npm:*"
csstype: "npm:^3.0.2"
checksum: 6d16f86b384e829edc3710b1dd9ec4eb1d6b26bc079c5cf605bd0cbf77ae6224f15c76949afadb7f53df4544cfe4025c1111fbe36732cd7f660a320fbc2e5866
languageName: node
linkType: hard
"@types/responselike@npm:^1.0.0":
version: 1.0.2
resolution: "@types/responselike@npm:1.0.2"
@@ -1547,7 +1558,7 @@ __metadata:
"@types/imagemin": "npm:^8.0.3"
"@types/lodash-es": "npm:^4.17.10"
"@types/node": "npm:^20.8.10"
"@types/react": "npm:^18.2.33"
"@types/react": "npm:^18.2.34"
"@types/react-dom": "npm:^18.2.14"
"@types/react-router-dom": "npm:^5.3.3"
"@typescript-eslint/eslint-plugin": "npm:^6.9.1"
@@ -1562,7 +1573,7 @@ __metadata:
eslint-import-resolver-typescript: "npm:^3.6.1"
eslint-plugin-autofix: "npm:^1.1.0"
eslint-plugin-import: "npm:^2.29.0"
eslint-plugin-jsx-a11y: "npm:^6.7.1"
eslint-plugin-jsx-a11y: "npm:^6.8.0"
eslint-plugin-prettier: "npm:alpha"
eslint-plugin-react: "npm:^7.33.2"
eslint-plugin-react-hooks: "npm:^4.6.0"
@@ -1754,7 +1765,7 @@ __metadata:
languageName: node
linkType: hard
"aria-query@npm:^5.1.3":
"aria-query@npm:^5.3.0":
version: 5.3.0
resolution: "aria-query@npm:5.3.0"
dependencies:
@@ -1865,10 +1876,10 @@ __metadata:
languageName: node
linkType: hard
"ast-types-flow@npm:^0.0.7":
version: 0.0.7
resolution: "ast-types-flow@npm:0.0.7"
checksum: 663b90e99b56ee2d7f736a6b6fff8b3c5404f28fa1860bb8d83ee5a9bff9e687520d0d6d9db6edff5a34fd4d3c0c11a3beb1cf75e43c9a880cca04371cc99808
"ast-types-flow@npm:^0.0.8":
version: 0.0.8
resolution: "ast-types-flow@npm:0.0.8"
checksum: 85a1c24af4707871c27cfe456bd2ff7fcbe678f3d1c878ac968c9557735a171a17bdcc8c8f903ceab3fc3c49d5b3da2194e6ab0a6be7fec0e133fa028f21ba1b
languageName: node
linkType: hard
@@ -1902,14 +1913,14 @@ __metadata:
languageName: node
linkType: hard
"axe-core@npm:^4.6.2":
version: 4.8.2
resolution: "axe-core@npm:4.8.2"
checksum: 3e8dbf264a57767713daa77fe04bbabd71a956b08b99c2eb0ec61b75852f21190653f557d2da623dea9e0a7555460deaac71d9f6a9125c0b52576f8581bfbe52
"axe-core@npm:=4.7.0":
version: 4.7.0
resolution: "axe-core@npm:4.7.0"
checksum: 615c0f7722c3c9fcf353dbd70b00e2ceae234d4c17cbc839dd85c01d16797c4e4da45f8d27c6118e9e6b033fb06efd196106e13651a1b2f3a10e0f11c7b2f660
languageName: node
linkType: hard
"axobject-query@npm:^3.1.1":
"axobject-query@npm:^3.2.1":
version: 3.2.1
resolution: "axobject-query@npm:3.2.1"
dependencies:
@@ -3034,7 +3045,7 @@ __metadata:
languageName: node
linkType: hard
"es-iterator-helpers@npm:^1.0.12":
"es-iterator-helpers@npm:^1.0.12, es-iterator-helpers@npm:^1.0.15":
version: 1.0.15
resolution: "es-iterator-helpers@npm:1.0.15"
dependencies:
@@ -3539,29 +3550,29 @@ __metadata:
languageName: node
linkType: hard
"eslint-plugin-jsx-a11y@npm:^6.7.1":
version: 6.7.1
resolution: "eslint-plugin-jsx-a11y@npm:6.7.1"
"eslint-plugin-jsx-a11y@npm:^6.8.0":
version: 6.8.0
resolution: "eslint-plugin-jsx-a11y@npm:6.8.0"
dependencies:
"@babel/runtime": "npm:^7.20.7"
aria-query: "npm:^5.1.3"
array-includes: "npm:^3.1.6"
array.prototype.flatmap: "npm:^1.3.1"
ast-types-flow: "npm:^0.0.7"
axe-core: "npm:^4.6.2"
axobject-query: "npm:^3.1.1"
"@babel/runtime": "npm:^7.23.2"
aria-query: "npm:^5.3.0"
array-includes: "npm:^3.1.7"
array.prototype.flatmap: "npm:^1.3.2"
ast-types-flow: "npm:^0.0.8"
axe-core: "npm:=4.7.0"
axobject-query: "npm:^3.2.1"
damerau-levenshtein: "npm:^1.0.8"
emoji-regex: "npm:^9.2.2"
has: "npm:^1.0.3"
jsx-ast-utils: "npm:^3.3.3"
language-tags: "npm:=1.0.5"
es-iterator-helpers: "npm:^1.0.15"
hasown: "npm:^2.0.0"
jsx-ast-utils: "npm:^3.3.5"
language-tags: "npm:^1.0.9"
minimatch: "npm:^3.1.2"
object.entries: "npm:^1.1.6"
object.fromentries: "npm:^2.0.6"
semver: "npm:^6.3.0"
object.entries: "npm:^1.1.7"
object.fromentries: "npm:^2.0.7"
peerDependencies:
eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8
checksum: b7eb451304dc27c9552649a716be1de3b5d577f39e53f6da6a2dac084b84b349b0224be3020439f99c2b3bf417a13c5591326f1ce6af8d74f1cb5d5d95c4222b
checksum: 7a8e4498531a43d988ce2f12502a3f5ce96eacfec13f956cf927f24bb041b724fb7fc0f0306ea19d143bfc79e138bf25e25acca0822847206ac6bf5ce095e846
languageName: node
linkType: hard
@@ -4625,13 +4636,6 @@ __metadata:
languageName: node
linkType: hard
"has@npm:^1.0.3":
version: 1.0.4
resolution: "has@npm:1.0.4"
checksum: c245f332fe78c7b6b8753857240ac12b3286f995f656a33c77e0f5baab7d0157e6ddb1c34940ffd2bffc51f75ede50cd8b29ff65c13e336376aca8cf3df58043
languageName: node
linkType: hard
"hasown@npm:^2.0.0":
version: 2.0.0
resolution: "hasown@npm:2.0.0"
@@ -5493,7 +5497,7 @@ __metadata:
languageName: node
linkType: hard
"jsx-ast-utils@npm:^2.4.1 || ^3.0.0, jsx-ast-utils@npm:^3.3.3":
"jsx-ast-utils@npm:^2.4.1 || ^3.0.0, jsx-ast-utils@npm:^3.3.5":
version: 3.3.5
resolution: "jsx-ast-utils@npm:3.3.5"
dependencies:
@@ -5544,19 +5548,19 @@ __metadata:
languageName: node
linkType: hard
"language-subtag-registry@npm:~0.3.2":
"language-subtag-registry@npm:^0.3.20":
version: 0.3.22
resolution: "language-subtag-registry@npm:0.3.22"
checksum: 5591f4abd775d1ab5945355a5ba894327d2d94c900607bdb69aac1bc5bb921dbeeeb5f616df95e8c0ae875501d19c1cfa0e852ece822121e95048deb34f2b4d2
languageName: node
linkType: hard
"language-tags@npm:=1.0.5":
version: 1.0.5
resolution: "language-tags@npm:1.0.5"
"language-tags@npm:^1.0.9":
version: 1.0.9
resolution: "language-tags@npm:1.0.9"
dependencies:
language-subtag-registry: "npm:~0.3.2"
checksum: 2161292ddae73ff2f5a15fd2d753b21096b81324337dff4ad78d702c63210d5beb18892cd53a3455ee6e88065807c8e285e82c40503678951d2071d101a473b4
language-subtag-registry: "npm:^0.3.20"
checksum: d3a7c14b694e67f519153d6df6cb200681648d38d623c3bfa9d6a66a5ec5493628acb88e9df5aceef3cf1902ab263a205e7d59ee4cf1d6bb67e707b83538bd6d
languageName: node
linkType: hard
@@ -6196,7 +6200,7 @@ __metadata:
languageName: node
linkType: hard
"object.entries@npm:^1.1.5, object.entries@npm:^1.1.6":
"object.entries@npm:^1.1.5, object.entries@npm:^1.1.6, object.entries@npm:^1.1.7":
version: 1.1.7
resolution: "object.entries@npm:1.1.7"
dependencies:

View File

@@ -57,7 +57,9 @@ void AnalogSensor::start() {
[&](const char * value, const int8_t id, JsonObject & output) { return command_commands(value, id, output); },
FL_(commands_cmd));
Mqtt::subscribe(EMSdevice::DeviceType::ANALOGSENSOR, "analogsensor/#", nullptr); // use empty function callback
char topic[Mqtt::MQTT_TOPIC_MAX_SIZE];
snprintf(topic, sizeof(topic), "%s/#", F_(analogsensor));
Mqtt::subscribe(EMSdevice::DeviceType::ANALOGSENSOR, topic, nullptr); // use empty function callback
}
// load settings from the customization file, sorts them and initializes the GPIOs
@@ -206,10 +208,9 @@ void AnalogSensor::reload() {
} else
#endif
{
digitalWrite(sensor.gpio(), sensor.offset() > 0 ? 1 : 0);
digitalWrite(sensor.gpio(), sensor.offset() * sensor.factor() > 0 ? 1 : 0);
sensor.set_value(digitalRead(sensor.gpio()));
}
sensor.set_uom(0); // no uom, just for safe measures
publish_sensor(sensor);
} else if (sensor.type() >= AnalogType::PWM_0) {
LOG_DEBUG("Adding PWM output sensor on GPIO %02d", sensor.gpio());
@@ -434,15 +435,15 @@ void AnalogSensor::remove_ha_topic(const int8_t type, const uint8_t gpio) const
#else
if (type == AnalogType::DIGITAL_OUT) {
#endif
snprintf(topic, sizeof(topic), "switch/%s/analogsensor_%02d/config", Mqtt::basename().c_str(), gpio);
snprintf(topic, sizeof(topic), "switch/%s/%s_%02d/config", Mqtt::basename().c_str(), F_(analogsensor), gpio);
} else if (type == AnalogType::DIGITAL_OUT) { // DAC
snprintf(topic, sizeof(topic), "number/%s/analogsensor_%02d/config", Mqtt::basename().c_str(), gpio);
snprintf(topic, sizeof(topic), "number/%s/%s_%02d/config", Mqtt::basename().c_str(), F_(analogsensor), gpio);
} else if (type >= AnalogType::PWM_0) {
snprintf(topic, sizeof(topic), "number/%s/analogsensor_%02d/config", Mqtt::basename().c_str(), gpio);
snprintf(topic, sizeof(topic), "number/%s/%s_%02d/config", Mqtt::basename().c_str(), F_(analogsensor), gpio);
} else if (type == AnalogType::DIGITAL_IN) {
snprintf(topic, sizeof(topic), "binary_sensor/%s/analogsensor_%02d/config", Mqtt::basename().c_str(), gpio);
snprintf(topic, sizeof(topic), "binary_sensor/%s/%s_%02d/config", Mqtt::basename().c_str(), F_(analogsensor), gpio);
} else {
snprintf(topic, sizeof(topic), "sensor/%s/analogsensor_%02d/config", Mqtt::basename().c_str(), gpio);
snprintf(topic, sizeof(topic), "sensor/%s/%s_%02d/config", Mqtt::basename().c_str(), F_(analogsensor), gpio);
}
Mqtt::queue_remove_topic(topic);
}
@@ -514,7 +515,7 @@ void AnalogSensor::publish_values(const bool force) {
StaticJsonDocument<EMSESP_JSON_SIZE_MEDIUM> config;
char stat_t[50];
snprintf(stat_t, sizeof(stat_t), "%s/analogsensor_data", Mqtt::basename().c_str()); // use basename
snprintf(stat_t, sizeof(stat_t), "%s/%s_data", Mqtt::basename().c_str(), F_(analogsensor)); // use base path
config["stat_t"] = stat_t;
char val_obj[50];
@@ -534,9 +535,9 @@ void AnalogSensor::publish_values(const bool force) {
char uniq_s[70];
if (Mqtt::entity_format() == Mqtt::entityFormat::MULTI_SHORT) {
snprintf(uniq_s, sizeof(uniq_s), "%s_analogsensor_%02d", Mqtt::basename().c_str(), sensor.gpio());
snprintf(uniq_s, sizeof(uniq_s), "%s_%s_%02d", Mqtt::basename().c_str(), F_(analogsensor), sensor.gpio());
} else {
snprintf(uniq_s, sizeof(uniq_s), "analogsensor_%02d", sensor.gpio());
snprintf(uniq_s, sizeof(uniq_s), "%s_%02d", F_(analogsensor), sensor.gpio());
}
config["obj_id"] = uniq_s;
@@ -559,8 +560,8 @@ void AnalogSensor::publish_values(const bool force) {
#else
if (sensor.type() == AnalogType::DIGITAL_OUT) {
#endif
snprintf(topic, sizeof(topic), "switch/%s/analogsensor_%02d/config", Mqtt::basename().c_str(), sensor.gpio());
snprintf(command_topic, sizeof(command_topic), "%s/analogsensor/%s", Mqtt::basename().c_str(), sensor.name().c_str());
snprintf(topic, sizeof(topic), "switch/%s/%s_%02d/config", Mqtt::basename().c_str(), F_(analogsensor), sensor.gpio());
snprintf(command_topic, sizeof(command_topic), "%s/%s/%s", Mqtt::basename().c_str(), F_(analogsensor), sensor.name().c_str());
config["cmd_t"] = command_topic;
if (EMSESP::system_.bool_format() == BOOL_FORMAT_TRUEFALSE) {
config["pl_on"] = true;
@@ -574,30 +575,30 @@ void AnalogSensor::publish_values(const bool force) {
config["pl_off"] = Helpers::render_boolean(result, false);
}
} else if (sensor.type() == AnalogType::DIGITAL_OUT) { // DAC
snprintf(topic, sizeof(topic), "number/%s/analogsensor_%02d/config", Mqtt::basename().c_str(), sensor.gpio());
snprintf(command_topic, sizeof(command_topic), "%s/analogsensor/%s", Mqtt::basename().c_str(), sensor.name().c_str());
snprintf(topic, sizeof(topic), "number/%s/%s_%02d/config", Mqtt::basename().c_str(), F_(analogsensor), sensor.gpio());
snprintf(command_topic, sizeof(command_topic), "%s/%s/%s", Mqtt::basename().c_str(), F_(analogsensor), sensor.name().c_str());
config["cmd_t"] = command_topic;
config["min"] = 0;
config["max"] = 255;
config["mode"] = "box"; // auto, slider or box
config["step"] = 1;
} else if (sensor.type() >= AnalogType::PWM_0) {
snprintf(topic, sizeof(topic), "number/%s/analogsensor_%02d/config", Mqtt::basename().c_str(), sensor.gpio());
snprintf(command_topic, sizeof(command_topic), "%s/analogsensor/%s", Mqtt::basename().c_str(), sensor.name().c_str());
snprintf(topic, sizeof(topic), "number/%s/%s_%02d/config", Mqtt::basename().c_str(), F_(analogsensor), sensor.gpio());
snprintf(command_topic, sizeof(command_topic), "%s/%s/%s", Mqtt::basename().c_str(), F_(analogsensor), sensor.name().c_str());
config["cmd_t"] = command_topic;
config["min"] = 0;
config["max"] = 100;
config["mode"] = "box"; // auto, slider or box
config["step"] = 0.1;
} else if (sensor.type() == AnalogType::COUNTER) {
snprintf(topic, sizeof(topic), "sensor/%s/analogsensor_%02d/config", Mqtt::basename().c_str(), sensor.gpio());
snprintf(command_topic, sizeof(command_topic), "%s/analogsensor/%s", Mqtt::basename().c_str(), sensor.name().c_str());
snprintf(topic, sizeof(topic), "sensor/%s/%s_%02d/config", Mqtt::basename().c_str(), F_(analogsensor), sensor.gpio());
snprintf(command_topic, sizeof(command_topic), "%s/%s/%s", Mqtt::basename().c_str(), F_(analogsensor), sensor.name().c_str());
config["cmd_t"] = command_topic;
config["stat_cla"] = "total_increasing";
// config["mode"] = "box"; // auto, slider or box
// config["step"] = sensor.factor();
} else if (sensor.type() == AnalogType::DIGITAL_IN) {
snprintf(topic, sizeof(topic), "binary_sensor/%s/analogsensor_%02d/config", Mqtt::basename().c_str(), sensor.gpio());
snprintf(topic, sizeof(topic), "binary_sensor/%s/%s_%02d/config", Mqtt::basename().c_str(), F_(analogsensor), sensor.gpio());
if (EMSESP::system_.bool_format() == BOOL_FORMAT_TRUEFALSE) {
config["pl_on"] = true;
config["pl_off"] = false;
@@ -610,7 +611,7 @@ void AnalogSensor::publish_values(const bool force) {
config["pl_off"] = Helpers::render_boolean(result, false);
}
} else {
snprintf(topic, sizeof(topic), "sensor/%s/analogsensor_%02d/config", Mqtt::basename().c_str(), sensor.gpio());
snprintf(topic, sizeof(topic), "sensor/%s/%s_%02d/config", Mqtt::basename().c_str(), F_(analogsensor), sensor.gpio());
config["stat_cla"] = "measurement";
}
@@ -626,7 +627,9 @@ void AnalogSensor::publish_values(const bool force) {
}
}
Mqtt::queue_publish("analogsensor_data", doc.as<JsonObject>());
char topic[Mqtt::MQTT_TOPIC_MAX_SIZE];
snprintf(topic, sizeof(topic), "%s_data", F_(analogsensor));
Mqtt::queue_publish(topic, doc.as<JsonObject>());
}
// called from emsesp.cpp, similar to the emsdevice->get_value_info
@@ -702,6 +705,7 @@ bool AnalogSensor::command_info(const char * value, const int8_t id, JsonObject
JsonObject dataSensor = output.createNestedObject(sensor.name());
dataSensor["gpio"] = sensor.gpio();
dataSensor["type"] = F_(number);
dataSensor["value"] = sensor.value();
dataSensor["analog"] = FL_(list_sensortype)[sensor.type()];
if (sensor.type() == AnalogType::ADC) {
dataSensor["uom"] = EMSdevice::uom_to_string(sensor.uom());
@@ -717,7 +721,6 @@ bool AnalogSensor::command_info(const char * value, const int8_t id, JsonObject
dataSensor["uom"] = EMSdevice::uom_to_string(sensor.uom());
dataSensor["frequency"] = sensor.factor();
}
dataSensor["value"] = sensor.value();
} else if (id == 0) { // output values command
output[sensor.name()] = sensor.value();
} else { // if someone wants gpio numbers
@@ -763,6 +766,7 @@ bool AnalogSensor::command_setvalue(const char * value, const int8_t gpio) {
}
for (auto & sensor : sensors_) {
if (sensor.gpio() == gpio) {
double oldoffset = sensor.offset();
if (sensor.type() == AnalogType::COUNTER) {
if (val < 0 || value[0] == '+') { // sign corrects values
sensor.set_offset(sensor.value() + val);
@@ -771,11 +775,8 @@ bool AnalogSensor::command_setvalue(const char * value, const int8_t gpio) {
sensor.set_offset(val);
sensor.set_value(val);
}
publish_sensor(sensor);
return true;
} else if (sensor.type() == AnalogType::ADC) {
sensor.set_offset(val);
return true;
} else if (sensor.type() == AnalogType::DIGITAL_OUT) {
uint8_t v = val;
#if CONFIG_IDF_TARGET_ESP32
@@ -784,8 +785,6 @@ bool AnalogSensor::command_setvalue(const char * value, const int8_t gpio) {
sensor.set_value(v);
pinMode(sensor.gpio(), OUTPUT);
dacWrite(sensor.gpio(), sensor.offset());
publish_sensor(sensor);
return true;
} else
#elif CONFIG_IDF_TARGET_ESP32S2
if ((sensor.gpio() == 23 || sensor.gpio() == 24) && v <= 255) {
@@ -801,9 +800,7 @@ bool AnalogSensor::command_setvalue(const char * value, const int8_t gpio) {
sensor.set_offset(v);
sensor.set_value(v);
pinMode(sensor.gpio(), OUTPUT);
digitalWrite(sensor.gpio(), sensor.offset() > 0 ? 1 : 0);
publish_sensor(sensor);
return true;
digitalWrite(sensor.gpio(), sensor.offset() * sensor.factor() > 0 ? 1 : 0);
}
} else if (sensor.type() >= AnalogType::PWM_0) {
if (val > 100) {
@@ -819,9 +816,18 @@ bool AnalogSensor::command_setvalue(const char * value, const int8_t gpio) {
uint8_t channel = sensor.type() - AnalogType::PWM_0;
ledcWrite(channel, (uint32_t)(val * 8191 / 100));
#endif
publish_sensor(sensor);
return true;
} else {
return false;
}
if (oldoffset != sensor.offset()) {
// don't save state of digital out if fixed value on reboot is selected
if (sensor.type() != AnalogType::DIGITAL_OUT || sensor.uom() == 0) {
update(sensor.gpio(), sensor.name(), sensor.offset(), sensor.factor(), sensor.uom(), sensor.type(), false);
}
publish_sensor(sensor);
changed_ = true;
}
return true;
}
}
return false;

View File

@@ -1597,6 +1597,7 @@ bool EMSdevice::generate_values(JsonObject & output, const uint8_t tag_filter, c
}
// do not overwrite
if (json.containsKey(name)) {
EMSESP::logger().debug("double json key: %s", name);
continue;
}
// handle Booleans

View File

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

View File

@@ -22,6 +22,8 @@ namespace emsesp {
using namespace std::placeholders; // for `_1` etc
bool WebCustomization::_start = true;
WebCustomizationService::WebCustomizationService(AsyncWebServer * server, FS * fs, SecurityManager * securityManager)
: _httpEndpoint(WebCustomization::read,
WebCustomization::update,
@@ -132,10 +134,13 @@ StateUpdateResult WebCustomization::update(JsonObject & root, WebCustomization &
sensor.factor = analogJson["factor"];
sensor.uom = analogJson["uom"];
sensor.type = analogJson["type"];
if (_start && sensor.type == EMSESP::analogsensor_.AnalogType::DIGITAL_OUT && sensor.uom > DeviceValue::DeviceValueUOM::NONE) {
sensor.offset = sensor.uom - 1;
}
customizations.analogCustomizations.push_back(sensor); // add to list
}
}
_start = false;
// load array of entities id's with masks, building up the object class
customizations.entityCustomizations.clear();
if (root["masked_entities"].is<JsonArray>()) {
@@ -203,7 +208,7 @@ void WebCustomizationService::device_entities(AsyncWebServerRequest * request) {
size_t buffer = EMSESP_JSON_SIZE_XXXXLARGE;
auto * response = new MsgpackAsyncJsonResponse(true, buffer);
while (!response->getSize()) {
while (!response) {
delete response;
buffer -= 1024;
response = new MsgpackAsyncJsonResponse(true, buffer);
@@ -259,7 +264,7 @@ void WebCustomizationService::customization_entities(AsyncWebServerRequest * req
// emsesp::EMSESP::logger().info(id.as<const char *>());
}
// add deleted entities from file
EMSESP::webCustomizationService.read([&](WebCustomization & settings) {
read([&](WebCustomization & settings) {
for (EntityCustomization entityCustomization : settings.entityCustomizations) {
if (entityCustomization.device_id == device_id) {
for (std::string entity_id : entityCustomization.entity_ids) {
@@ -288,7 +293,7 @@ void WebCustomizationService::customization_entities(AsyncWebServerRequest * req
emsdevice->getCustomizationEntities(entity_ids);
// Save the list to the customization file
EMSESP::webCustomizationService.update(
update(
[&](WebCustomization & settings) {
// see if we already have a mask list for this device, if so remove it
for (auto it = settings.entityCustomizations.begin(); it != settings.entityCustomizations.end();) {

View File

@@ -73,6 +73,9 @@ class WebCustomization {
std::list<EntityCustomization> entityCustomizations; // for a list of entities that have a special mask set
static void read(WebCustomization & customizations, JsonObject & root);
static StateUpdateResult update(JsonObject & root, WebCustomization & customizations);
private:
static bool _start;
};
class WebCustomizationService : public StatefulService<WebCustomization> {

View File

@@ -93,7 +93,8 @@ void WebDataService::core_data(AsyncWebServerRequest * request) {
}
// add any custom entities
if (EMSESP::webCustomEntityService.count_entities()) {
uint8_t customEntities = EMSESP::webCustomEntityService.count_entities();
if (customEntities) {
JsonObject obj = devices.createNestedObject();
obj["id"] = 99; // the last unique id
obj["tn"] = Helpers::translated_word(FL_(custom_device)); // translated device type name
@@ -103,7 +104,7 @@ void WebDataService::core_data(AsyncWebServerRequest * request) {
obj["d"] = 0; // deviceid
obj["p"] = 0; // productid
obj["v"] = 0; // version
obj["e"] = EMSESP::webCustomEntityService.count_entities(); // number of custom entities
obj["e"] = customEntities; // number of custom entities
}
root["connected"] = EMSESP::bus_status() != 2;
@@ -163,7 +164,8 @@ void WebDataService::sensor_data(AsyncWebServerRequest * request) {
}
}
root["analog_enabled"] = EMSESP::analogsensor_.analog_enabled();
root["analog_enabled"] = EMSESP::analog_enabled();
root["platform"] = EMSESP_PLATFORM;
response->setLength();
request->send(response);
@@ -180,7 +182,7 @@ void WebDataService::device_data(AsyncWebServerRequest * request) {
auto * response = new MsgpackAsyncJsonResponse(false, buffer);
// check size
while (!response->getSize()) {
while (!response) {
delete response;
buffer -= 1024;
response = new MsgpackAsyncJsonResponse(false, buffer);