8 Commits

Author SHA1 Message Date
Proddy
07d0de0151 Merge pull request #2747 from proddy/dev
MQTT reset button & label color for TextFields when disabled
2025-11-17 12:27:11 +01:00
proddy
0a75dd7e3c new action resetMQTT, called from MQTT Settings page 2025-11-17 12:26:14 +01:00
proddy
88afd3f453 use ValidatedTextField 2025-11-17 12:25:52 +01:00
proddy
9669a044ba comment out log msg 2025-11-17 12:25:20 +01:00
proddy
e31ebab12b grey out label if disabled (for system sensors) 2025-11-17 12:24:58 +01:00
proddy
e68a3a0d3a update 2025-11-17 12:23:30 +01:00
Proddy
4ae09c8766 Merge pull request #2746 from MichaelDvP/dev
validate board gpios in system
2025-11-17 09:35:03 +01:00
MichaelDvP
a693e96248 validate board gpios in system 2025-11-16 20:41:45 +01:00
15 changed files with 168 additions and 145 deletions

View File

@@ -1117,8 +1117,8 @@ packages:
resolution: {integrity: sha512-DLIsRzJVBQu72meAKPkWQOLcujdXT32hwdfnkI1frSiSRMK1MofjKHf+MEx0SB6fjEFXL8fBDv1dKymBlOp4Qw==} resolution: {integrity: sha512-DLIsRzJVBQu72meAKPkWQOLcujdXT32hwdfnkI1frSiSRMK1MofjKHf+MEx0SB6fjEFXL8fBDv1dKymBlOp4Qw==}
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
caniuse-lite@1.0.30001754: caniuse-lite@1.0.30001755:
resolution: {integrity: sha512-x6OeBXueoAceOmotzx3PO4Zpt4rzpeIFsSr6AAePTZxSkXiYDUmpypEl7e2+8NCd9bD7bXjqyef8CJYPC1jfxg==} resolution: {integrity: sha512-44V+Jm6ctPj7R52Na4TLi3Zri4dWUljJd+RDm+j8LtNCc/ihLCT+X1TzoOAkRETEWqjuLnh9581Tl80FvK7jVA==}
caw@2.0.1: caw@2.0.1:
resolution: {integrity: sha512-Cg8/ZSBEa8ZVY9HspcGUYaK63d/bN7rqS3CYCzEGUxuYv6UlmcjzDUz2fCFFHyTvUW5Pk0I+3hkA3iXlIj6guA==} resolution: {integrity: sha512-Cg8/ZSBEa8ZVY9HspcGUYaK63d/bN7rqS3CYCzEGUxuYv6UlmcjzDUz2fCFFHyTvUW5Pk0I+3hkA3iXlIj6guA==}
@@ -1225,8 +1225,8 @@ packages:
resolution: {integrity: sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==} resolution: {integrity: sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==}
engines: {node: '>=8.0.0'} engines: {node: '>=8.0.0'}
csstype@3.2.1: csstype@3.2.2:
resolution: {integrity: sha512-98XGutrXoh75MlgLihlNxAGbUuFQc7l1cqcnEZlLNKc0UrVdPndgmaDmYTDDh929VS/eqTZV0rozmhu2qqT1/g==} resolution: {integrity: sha512-D80T+tiqkd/8B0xNlbstWDG4x6aqVfO52+OlSUNIdkTvmNw0uQpJLeos2J/2XvpyidAFuTPmpad+tUxLndwj6g==}
currently-unhandled@0.4.1: currently-unhandled@0.4.1:
resolution: {integrity: sha512-/fITjgjGU50vjQ4FH6eUoYu+iUoUKIXws2hL15JJpIR+BbTxaXQsMuuyjtNh2WqsSBS5nsaZHFsFecyw5CCAng==} resolution: {integrity: sha512-/fITjgjGU50vjQ4FH6eUoYu+iUoUKIXws2hL15JJpIR+BbTxaXQsMuuyjtNh2WqsSBS5nsaZHFsFecyw5CCAng==}
@@ -1337,8 +1337,8 @@ packages:
duplexer3@0.1.5: duplexer3@0.1.5:
resolution: {integrity: sha512-1A8za6ws41LQgv9HrE/66jyC5yuSjQ3L/KOpFtoBilsAK2iA2wuS5rTt1OCzIvtS2V7nVmedsUU+DGRcjBmOYA==} resolution: {integrity: sha512-1A8za6ws41LQgv9HrE/66jyC5yuSjQ3L/KOpFtoBilsAK2iA2wuS5rTt1OCzIvtS2V7nVmedsUU+DGRcjBmOYA==}
electron-to-chromium@1.5.253: electron-to-chromium@1.5.254:
resolution: {integrity: sha512-O0tpQ/35rrgdiGQ0/OFWhy1itmd9A6TY9uQzlqj3hKSu/aYpe7UIn5d7CU2N9myH6biZiWF3VMZVuup8pw5U9w==} resolution: {integrity: sha512-DcUsWpVhv9svsKRxnSCZ86SjD+sp32SGidNB37KpqXJncp1mfUgKbHvBomE89WJDbfVKw1mdv5+ikrvd43r+Bg==}
emoji-regex@8.0.0: emoji-regex@8.0.0:
resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
@@ -3290,7 +3290,7 @@ snapshots:
'@emotion/memoize': 0.9.0 '@emotion/memoize': 0.9.0
'@emotion/unitless': 0.10.0 '@emotion/unitless': 0.10.0
'@emotion/utils': 1.4.2 '@emotion/utils': 1.4.2
csstype: 3.2.1 csstype: 3.2.2
'@emotion/sheet@1.4.0': {} '@emotion/sheet@1.4.0': {}
@@ -3507,7 +3507,7 @@ snapshots:
'@popperjs/core': 2.11.8 '@popperjs/core': 2.11.8
'@types/react-transition-group': 4.4.12(@types/react@19.2.5) '@types/react-transition-group': 4.4.12(@types/react@19.2.5)
clsx: 2.1.1 clsx: 2.1.1
csstype: 3.2.1 csstype: 3.2.2
prop-types: 15.8.1 prop-types: 15.8.1
react: 19.2.0 react: 19.2.0
react-dom: 19.2.0(react@19.2.0) react-dom: 19.2.0(react@19.2.0)
@@ -3533,7 +3533,7 @@ snapshots:
'@emotion/cache': 11.14.0 '@emotion/cache': 11.14.0
'@emotion/serialize': 1.3.3 '@emotion/serialize': 1.3.3
'@emotion/sheet': 1.4.0 '@emotion/sheet': 1.4.0
csstype: 3.2.1 csstype: 3.2.2
prop-types: 15.8.1 prop-types: 15.8.1
react: 19.2.0 react: 19.2.0
optionalDependencies: optionalDependencies:
@@ -3548,7 +3548,7 @@ snapshots:
'@mui/types': 7.4.8(@types/react@19.2.5) '@mui/types': 7.4.8(@types/react@19.2.5)
'@mui/utils': 7.3.5(@types/react@19.2.5)(react@19.2.0) '@mui/utils': 7.3.5(@types/react@19.2.5)(react@19.2.0)
clsx: 2.1.1 clsx: 2.1.1
csstype: 3.2.1 csstype: 3.2.2
prop-types: 15.8.1 prop-types: 15.8.1
react: 19.2.0 react: 19.2.0
optionalDependencies: optionalDependencies:
@@ -3796,7 +3796,7 @@ snapshots:
'@types/react@19.2.5': '@types/react@19.2.5':
dependencies: dependencies:
csstype: 3.2.1 csstype: 3.2.2
'@types/responselike@1.0.3': '@types/responselike@1.0.3':
dependencies: dependencies:
@@ -4021,8 +4021,8 @@ snapshots:
browserslist@4.28.0: browserslist@4.28.0:
dependencies: dependencies:
baseline-browser-mapping: 2.8.28 baseline-browser-mapping: 2.8.28
caniuse-lite: 1.0.30001754 caniuse-lite: 1.0.30001755
electron-to-chromium: 1.5.253 electron-to-chromium: 1.5.254
node-releases: 2.0.27 node-releases: 2.0.27
update-browserslist-db: 1.1.4(browserslist@4.28.0) update-browserslist-db: 1.1.4(browserslist@4.28.0)
@@ -4080,7 +4080,7 @@ snapshots:
camelcase@2.1.1: {} camelcase@2.1.1: {}
caniuse-lite@1.0.30001754: {} caniuse-lite@1.0.30001755: {}
caw@2.0.1: caw@2.0.1:
dependencies: dependencies:
@@ -4211,7 +4211,7 @@ snapshots:
dependencies: dependencies:
css-tree: 1.1.3 css-tree: 1.1.3
csstype@3.2.1: {} csstype@3.2.2: {}
currently-unhandled@0.4.1: currently-unhandled@0.4.1:
dependencies: dependencies:
@@ -4294,7 +4294,7 @@ snapshots:
dom-helpers@5.2.1: dom-helpers@5.2.1:
dependencies: dependencies:
'@babel/runtime': 7.28.4 '@babel/runtime': 7.28.4
csstype: 3.2.1 csstype: 3.2.2
dom-serializer@1.4.1: dom-serializer@1.4.1:
dependencies: dependencies:
@@ -4367,7 +4367,7 @@ snapshots:
duplexer3@0.1.5: {} duplexer3@0.1.5: {}
electron-to-chromium@1.5.253: {} electron-to-chromium@1.5.254: {}
emoji-regex@8.0.0: {} emoji-regex@8.0.0: {}

View File

@@ -14,7 +14,6 @@ import {
Grid, Grid,
InputAdornment, InputAdornment,
MenuItem, MenuItem,
TextField,
Typography Typography
} from '@mui/material'; } from '@mui/material';
@@ -172,42 +171,32 @@ const SensorsAnalogDialog = ({
<DialogTitle>{dialogTitle}</DialogTitle> <DialogTitle>{dialogTitle}</DialogTitle>
<DialogContent dividers> <DialogContent dividers>
<Grid container spacing={2}> <Grid container spacing={2}>
{editItem.s ? ( <ValidatedTextField
<TextField name="g"
name="g" label="GPIO"
label="GPIO" value={editItem.g}
value={editItem.g} sx={{ width: '8ch' }}
sx={{ width: '8ch' }} disabled={editItem.s}
disabled={true} select
></TextField> onChange={updateFormValue}
) : ( >
<ValidatedTextField {availableGPIOs?.map((gpio: number) => (
name="g" <MenuItem key={gpio} value={gpio}>
label="GPIO" {gpio}
value={editItem.g} </MenuItem>
sx={{ width: '8ch' }} ))}
select </ValidatedTextField>
onChange={updateFormValue}
>
{availableGPIOs?.map((gpio: number) => (
<MenuItem key={gpio} value={gpio}>
{gpio}
</MenuItem>
))}
</ValidatedTextField>
)}
<Grid> <Grid>
<ValidatedTextField <ValidatedTextField
name="n" name="n"
label={LL.NAME(0)} label={LL.NAME(0)}
value={editItem.n} value={editItem.n}
fullWidth fullWidth
variant="outlined"
onChange={updateFormValue} onChange={updateFormValue}
/> />
</Grid> </Grid>
<Grid> <Grid>
<TextField <ValidatedTextField
name="t" name="t"
label={LL.TYPE(0)} label={LL.TYPE(0)}
value={editItem.t} value={editItem.t}
@@ -217,11 +206,11 @@ const SensorsAnalogDialog = ({
disabled={editItem.s} disabled={editItem.s}
> >
{analogTypeMenuItems} {analogTypeMenuItems}
</TextField> </ValidatedTextField>
</Grid> </Grid>
{(isCounterOrRate || isFreqType) && ( {(isCounterOrRate || isFreqType) && (
<Grid> <Grid>
<TextField <ValidatedTextField
name="u" name="u"
label={LL.UNIT()} label={LL.UNIT()}
value={editItem.u} value={editItem.u}
@@ -231,18 +220,17 @@ const SensorsAnalogDialog = ({
disabled={editItem.s} disabled={editItem.s}
> >
{uomMenuItems} {uomMenuItems}
</TextField> </ValidatedTextField>
</Grid> </Grid>
)} )}
{editItem.t === AnalogType.ADC && ( {editItem.t === AnalogType.ADC && (
<Grid> <Grid>
<TextField <ValidatedTextField
name="o" name="o"
label={LL.OFFSET()} label={LL.OFFSET()}
value={numberValue(editItem.o)} value={numberValue(editItem.o)}
type="number" type="number"
sx={{ width: '11ch' }} sx={{ width: '11ch' }}
variant="outlined"
onChange={updateFormValue} onChange={updateFormValue}
disabled={editItem.s} disabled={editItem.s}
slotProps={{ slotProps={{
@@ -258,13 +246,12 @@ const SensorsAnalogDialog = ({
)} )}
{editItem.t === AnalogType.NTC && ( {editItem.t === AnalogType.NTC && (
<Grid> <Grid>
<TextField <ValidatedTextField
name="o" name="o"
label={LL.OFFSET()} label={LL.OFFSET()}
value={numberValue(editItem.o)} value={numberValue(editItem.o)}
sx={{ width: '11ch' }} sx={{ width: '11ch' }}
type="number" type="number"
variant="outlined"
onChange={updateFormValue} onChange={updateFormValue}
disabled={editItem.s} disabled={editItem.s}
slotProps={{ slotProps={{
@@ -280,13 +267,12 @@ const SensorsAnalogDialog = ({
)} )}
{editItem.t === AnalogType.COUNTER && ( {editItem.t === AnalogType.COUNTER && (
<Grid> <Grid>
<TextField <ValidatedTextField
name="o" name="o"
label={LL.STARTVALUE()} label={LL.STARTVALUE()}
value={numberValue(editItem.o)} value={numberValue(editItem.o)}
type="number" type="number"
sx={{ width: '11ch' }} sx={{ width: '11ch' }}
variant="outlined"
onChange={updateFormValue} onChange={updateFormValue}
disabled={editItem.s} disabled={editItem.s}
slotProps={{ slotProps={{
@@ -297,13 +283,12 @@ const SensorsAnalogDialog = ({
)} )}
{editItem.t === AnalogType.RGB && ( {editItem.t === AnalogType.RGB && (
<Grid> <Grid>
<TextField <ValidatedTextField
name="o" name="o"
label={'RGB ' + LL.VALUE(0)} label={'RGB ' + LL.VALUE(0)}
value={numberValue(editItem.o)} value={numberValue(editItem.o)}
type="number" type="number"
sx={{ width: '11ch' }} sx={{ width: '11ch' }}
variant="outlined"
onChange={updateFormValue} onChange={updateFormValue}
disabled={editItem.s} disabled={editItem.s}
/> />
@@ -311,13 +296,12 @@ const SensorsAnalogDialog = ({
)} )}
{isCounterOrRate && ( {isCounterOrRate && (
<Grid> <Grid>
<TextField <ValidatedTextField
name="f" name="f"
label={LL.FACTOR()} label={LL.FACTOR()}
value={numberValue(editItem.f)} value={numberValue(editItem.f)}
sx={{ width: '14ch' }} sx={{ width: '14ch' }}
type="number" type="number"
variant="outlined"
onChange={updateFormValue} onChange={updateFormValue}
disabled={editItem.s} disabled={editItem.s}
slotProps={{ slotProps={{
@@ -328,13 +312,12 @@ const SensorsAnalogDialog = ({
)} )}
{isDigitalOutGPIO && ( {isDigitalOutGPIO && (
<Grid> <Grid>
<TextField <ValidatedTextField
name="o" name="o"
label={LL.VALUE(0)} label={LL.VALUE(0)}
value={numberValue(editItem.o)} value={numberValue(editItem.o)}
sx={{ width: '11ch' }} sx={{ width: '11ch' }}
type="number" type="number"
variant="outlined"
onChange={updateFormValue} onChange={updateFormValue}
disabled={editItem.s} disabled={editItem.s}
slotProps={{ slotProps={{
@@ -346,21 +329,20 @@ const SensorsAnalogDialog = ({
{isDigitalOutNonGPIO && ( {isDigitalOutNonGPIO && (
<> <>
<Grid> <Grid>
<TextField <ValidatedTextField
name="o" name="o"
label={LL.VALUE(0)} label={LL.VALUE(0)}
value={numberValue(editItem.o)} value={numberValue(editItem.o)}
select select
variant="outlined"
onChange={updateFormValue} onChange={updateFormValue}
disabled={editItem.s} disabled={editItem.s}
> >
<MenuItem value={0}>{LL.OFF()}</MenuItem> <MenuItem value={0}>{LL.OFF()}</MenuItem>
<MenuItem value={1}>{LL.ON()}</MenuItem> <MenuItem value={1}>{LL.ON()}</MenuItem>
</TextField> </ValidatedTextField>
</Grid> </Grid>
<Grid> <Grid>
<TextField <ValidatedTextField
name="f" name="f"
label={LL.POLARITY()} label={LL.POLARITY()}
value={editItem.f} value={editItem.f}
@@ -371,10 +353,10 @@ const SensorsAnalogDialog = ({
> >
<MenuItem value={1}>{LL.ACTIVEHIGH()}</MenuItem> <MenuItem value={1}>{LL.ACTIVEHIGH()}</MenuItem>
<MenuItem value={-1}>{LL.ACTIVELOW()}</MenuItem> <MenuItem value={-1}>{LL.ACTIVELOW()}</MenuItem>
</TextField> </ValidatedTextField>
</Grid> </Grid>
<Grid> <Grid>
<TextField <ValidatedTextField
name="u" name="u"
label={LL.STARTVALUE()} label={LL.STARTVALUE()}
sx={{ width: '15ch' }} sx={{ width: '15ch' }}
@@ -390,19 +372,18 @@ const SensorsAnalogDialog = ({
<MenuItem value={2}> <MenuItem value={2}>
{LL.ALWAYS()}&nbsp;{LL.ON()} {LL.ALWAYS()}&nbsp;{LL.ON()}
</MenuItem> </MenuItem>
</TextField> </ValidatedTextField>
</Grid> </Grid>
</> </>
)} )}
{isPWM && ( {isPWM && (
<> <>
<Grid> <Grid>
<TextField <ValidatedTextField
name="f" name="f"
label={LL.FREQ()} label={LL.FREQ()}
value={numberValue(editItem.f)} value={numberValue(editItem.f)}
type="number" type="number"
variant="outlined"
sx={{ width: '11ch' }} sx={{ width: '11ch' }}
onChange={updateFormValue} onChange={updateFormValue}
disabled={editItem.s} disabled={editItem.s}
@@ -417,13 +398,12 @@ const SensorsAnalogDialog = ({
/> />
</Grid> </Grid>
<Grid> <Grid>
<TextField <ValidatedTextField
name="o" name="o"
label={LL.DUTY_CYCLE()} label={LL.DUTY_CYCLE()}
value={numberValue(editItem.o)} value={numberValue(editItem.o)}
type="number" type="number"
sx={{ width: '11ch' }} sx={{ width: '11ch' }}
variant="outlined"
onChange={updateFormValue} onChange={updateFormValue}
disabled={editItem.s} disabled={editItem.s}
slotProps={{ slotProps={{
@@ -441,7 +421,7 @@ const SensorsAnalogDialog = ({
{editItem.t === AnalogType.PULSE && ( {editItem.t === AnalogType.PULSE && (
<> <>
<Grid> <Grid>
<TextField <ValidatedTextField
name="o" name="o"
label={LL.POLARITY()} label={LL.POLARITY()}
value={editItem.o} value={editItem.o}
@@ -452,16 +432,15 @@ const SensorsAnalogDialog = ({
> >
<MenuItem value={0}>{LL.ACTIVEHIGH()}</MenuItem> <MenuItem value={0}>{LL.ACTIVEHIGH()}</MenuItem>
<MenuItem value={1}>{LL.ACTIVELOW()}</MenuItem> <MenuItem value={1}>{LL.ACTIVELOW()}</MenuItem>
</TextField> </ValidatedTextField>
</Grid> </Grid>
<Grid> <Grid>
<TextField <ValidatedTextField
name="f" name="f"
label="Pulse" label="Pulse"
value={numberValue(editItem.f)} value={numberValue(editItem.f)}
type="number" type="number"
sx={{ width: '15ch' }} sx={{ width: '15ch' }}
variant="outlined"
onChange={updateFormValue} onChange={updateFormValue}
disabled={editItem.s} disabled={editItem.s}
slotProps={{ slotProps={{

View File

@@ -1,8 +1,11 @@
import { useCallback, useMemo, useState } from 'react'; import { useCallback, useMemo, useState } from 'react';
import { toast } from 'react-toastify';
import CancelIcon from '@mui/icons-material/Cancel'; import CancelIcon from '@mui/icons-material/Cancel';
import SettingsBackupRestoreIcon from '@mui/icons-material/SettingsBackupRestore';
import WarningIcon from '@mui/icons-material/Warning'; import WarningIcon from '@mui/icons-material/Warning';
import { import {
Box,
Button, Button,
Checkbox, Checkbox,
Grid, Grid,
@@ -30,6 +33,8 @@ import type { MqttSettingsType } from 'types';
import { numberValue, updateValueDirty, useRest } from 'utils'; import { numberValue, updateValueDirty, useRest } from 'utils';
import { createMqttSettingsValidator, validate } from 'validators'; import { createMqttSettingsValidator, validate } from 'validators';
import { callAction } from '../../api/app';
const MqttSettings = () => { const MqttSettings = () => {
const { const {
loadData, loadData,
@@ -52,6 +57,16 @@ const MqttSettings = () => {
const [fieldErrors, setFieldErrors] = useState<ValidateFieldsError>(); const [fieldErrors, setFieldErrors] = useState<ValidateFieldsError>();
const sendResetMQTT = useCallback(() => {
void callAction({ action: 'resetMQTT' })
.then(() => {
toast.success('MQTT ' + LL.REFRESH() + ' successful');
})
.catch((error) => {
toast.error(String(error.error?.message || 'An error occurred'));
});
}, []);
const updateFormValue = useMemo( const updateFormValue = useMemo(
() => () =>
updateValueDirty( updateValueDirty(
@@ -114,16 +129,28 @@ const MqttSettings = () => {
<SectionContent> <SectionContent>
{blocker ? <BlockNavigation blocker={blocker} /> : null} {blocker ? <BlockNavigation blocker={blocker} /> : null}
<> <>
<BlockFormControlLabel <Box display="flex" gap={2} mb={1}>
control={ <BlockFormControlLabel
<Checkbox control={
name="enabled" <Checkbox
checked={data.enabled} name="enabled"
onChange={updateFormValue} checked={data.enabled}
/> onChange={updateFormValue}
} />
label={LL.ENABLE_MQTT()} }
/> label={LL.ENABLE_MQTT()}
/>
{data.enabled && (
<Button
startIcon={<SettingsBackupRestoreIcon />}
color="secondary"
variant="outlined"
onClick={sendResetMQTT}
>
{LL.REFRESH() + ' MQTT'}
</Button>
)}
</Box>
<Grid container spacing={2} rowSpacing={0}> <Grid container spacing={2} rowSpacing={0}>
<Grid> <Grid>
<ValidatedTextField <ValidatedTextField

View File

@@ -21,7 +21,16 @@ const ValidatedTextField: FC<ValidatedTextFieldProps> = ({
return ( return (
<> <>
<TextField error={!!errors} {...rest} aria-label="Error" /> <TextField
error={!!errors}
{...rest}
aria-label="Error"
slotProps={{
inputLabel: {
style: rest.disabled ? { color: 'grey' } : undefined
}
}}
/>
{errors?.map((e) => ( {errors?.map((e) => (
<FormHelperText key={e.message} sx={{ color: 'rgb(250, 95, 84)' }}> <FormHelperText key={e.message} sx={{ color: 'rgb(250, 95, 84)' }}>
{e.message} {e.message}

View File

@@ -5106,6 +5106,10 @@ router
// upload URL // upload URL
console.log('upload File from URL', content.param); console.log('upload File from URL', content.param);
return status(200); return status(200);
} else if (action === 'resetMQTT') {
// reset MQTT
console.log('resetting MQTT...');
return status(200);
} }
} }
return status(404); // cmd not found return status(404); // cmd not found

View File

@@ -167,7 +167,7 @@ void MqttSettingsService::WiFiEvent(WiFiEvent_t event) {
bool MqttSettingsService::configureMqtt() { bool MqttSettingsService::configureMqtt() {
// disconnect if already connected // disconnect if already connected
if (_mqttClient->connected()) { if (_mqttClient->connected()) {
emsesp::EMSESP::logger().info("Disconnecting to configure"); // emsesp::EMSESP::logger().info("Disconnecting to configure");
_mqttClient->disconnect(true); _mqttClient->disconnect(true);
} }

View File

@@ -357,7 +357,7 @@ static void setup_commands(std::shared_ptr<Commands> const & commands) {
shell.printfln(F_(tx_mode_fmt), settings.tx_mode); shell.printfln(F_(tx_mode_fmt), settings.tx_mode);
return StateUpdateResult::CHANGED; return StateUpdateResult::CHANGED;
}); });
EMSESP::uart_init(); EMSESP::system_.uart_init(false);
}); });
// //

View File

@@ -240,36 +240,6 @@ void EMSESP::watch_id(uint16_t watch_id) {
watch_id_ = watch_id; watch_id_ = watch_id;
} }
// resets all counters and bumps the UART
// this is called when the tx_mode is persisted in the FS either via Web UI or the console
void EMSESP::uart_init() {
uint8_t tx_mode = 0;
uint8_t rx_gpio = 0;
uint8_t tx_gpio = 0;
EMSESP::webSettingsService.read([&](WebSettings const & settings) {
tx_mode = settings.tx_mode;
rx_gpio = settings.rx_gpio;
tx_gpio = settings.tx_gpio;
});
EMSuart::stop();
// don't start UART if we have invalid GPIOs
if (EMSESP::system_.is_valid_gpio(rx_gpio) && EMSESP::system_.is_valid_gpio(tx_gpio)) {
EMSuart::start(tx_mode, rx_gpio, tx_gpio); // start UART
} else {
LOG_WARNING("Invalid UART Rx/Tx GPIOs. Check config.");
}
txservice_.start(); // sends out request to EMS bus for all devices
txservice_.tx_mode(tx_mode);
// force a fetch for all new values, unless Tx is set to off
// if (tx_mode != 0) {
// EMSESP::fetch_device_values();
// }
}
// return status of bus: connected (0), connected but Tx is broken (1), disconnected (2) // return status of bus: connected (0), connected but Tx is broken (1), disconnected (2)
uint8_t EMSESP::bus_status() { uint8_t EMSESP::bus_status() {
if (!rxservice_.bus_connected()) { if (!rxservice_.bus_connected()) {

View File

@@ -147,8 +147,6 @@ class EMSESP {
static void dump_all_entities(uuid::console::Shell & shell); static void dump_all_entities(uuid::console::Shell & shell);
static void dump_all_telegrams(uuid::console::Shell & shell); static void dump_all_telegrams(uuid::console::Shell & shell);
static void uart_init();
static void incoming_telegram(uint8_t * data, const uint8_t length); static void incoming_telegram(uint8_t * data, const uint8_t length);
static bool sensor_enabled() { static bool sensor_enabled() {

View File

@@ -321,8 +321,12 @@ void Mqtt::on_publish(uint16_t packetId) const {
LOG_DEBUG("Packet %d sent successful", packetId); LOG_DEBUG("Packet %d sent successful", packetId);
} }
// called when MQTT settings have changed via the Web forms // called when MQTT settings have changed via the MQTT Settings or Application Settings Web pages
void Mqtt::reset_mqtt() { void Mqtt::reset_mqtt() {
if (!enabled()) {
return;
}
if (!mqttClient_) { if (!mqttClient_) {
return; return;
} }

View File

@@ -406,15 +406,21 @@ void System::syslog_init() {
// read some specific system settings to store locally for faster access // read some specific system settings to store locally for faster access
void System::reload_settings() { void System::reload_settings() {
// set gpios to zero for valid check
led_gpio_ = 0;
rx_gpio_ = 0;
tx_gpio_ = 0;
pbutton_gpio_ = 0;
dallas_gpio_ = 0;
EMSESP::webSettingsService.read([&](WebSettings & settings) { EMSESP::webSettingsService.read([&](WebSettings & settings) {
version_ = settings.version; version_ = settings.version;
pbutton_gpio_ = settings.pbutton_gpio; pbutton_gpio_ = is_valid_gpio(settings.pbutton_gpio) ? settings.pbutton_gpio : 0;
analog_enabled_ = settings.analog_enabled; analog_enabled_ = settings.analog_enabled;
low_clock_ = settings.low_clock; low_clock_ = settings.low_clock;
hide_led_ = settings.hide_led; hide_led_ = settings.hide_led;
led_type_ = settings.led_type; led_type_ = settings.led_type;
led_gpio_ = settings.led_gpio; led_gpio_ = is_valid_gpio(settings.led_gpio) ? settings.led_gpio : 0;
board_profile_ = settings.board_profile; board_profile_ = settings.board_profile;
telnet_enabled_ = settings.telnet_enabled; telnet_enabled_ = settings.telnet_enabled;
@@ -423,10 +429,10 @@ void System::reload_settings() {
modbus_max_clients_ = settings.modbus_max_clients; modbus_max_clients_ = settings.modbus_max_clients;
modbus_timeout_ = settings.modbus_timeout; modbus_timeout_ = settings.modbus_timeout;
rx_gpio_ = settings.rx_gpio; tx_mode_ = settings.tx_mode;
tx_gpio_ = settings.tx_gpio; rx_gpio_ = is_valid_gpio(settings.rx_gpio) ? settings.rx_gpio : 0;
dallas_gpio_ = settings.dallas_gpio; tx_gpio_ = is_valid_gpio(settings.tx_gpio) ? settings.tx_gpio : 0;
dallas_gpio_ = is_valid_gpio(settings.dallas_gpio) ? settings.dallas_gpio : 0;
syslog_enabled_ = settings.syslog_enabled; syslog_enabled_ = settings.syslog_enabled;
syslog_level_ = settings.syslog_level; syslog_level_ = settings.syslog_level;
syslog_mark_interval_ = settings.syslog_mark_interval; syslog_mark_interval_ = settings.syslog_mark_interval;
@@ -497,7 +503,7 @@ void System::start() {
led_init(false); // init LED led_init(false); // init LED
button_init(false); // the special button button_init(false); // the special button
network_init(false); // network network_init(false); // network
EMSESP::uart_init(); // start UART uart_init(false); // start UART
syslog_init(); // start syslog syslog_init(); // start syslog
} }
@@ -550,11 +556,6 @@ void System::button_init(bool refresh) {
} }
#ifndef EMSESP_STANDALONE #ifndef EMSESP_STANDALONE
if (!is_valid_gpio(pbutton_gpio_)) {
LOG_WARNING("Invalid button GPIO. Check config.");
myPButton_.init(255, HIGH); // disable
return;
}
if (!myPButton_.init(pbutton_gpio_, HIGH)) { if (!myPButton_.init(pbutton_gpio_, HIGH)) {
LOG_WARNING("Multi-functional button not detected"); LOG_WARNING("Multi-functional button not detected");
return; return;
@@ -572,18 +573,17 @@ void System::button_init(bool refresh) {
void System::led_init(bool refresh) { void System::led_init(bool refresh) {
if (refresh) { if (refresh) {
// disabled old led port before setting new one // disabled old led port before setting new one
if ((led_gpio_ != 0) && is_valid_gpio(led_gpio_)) { if (led_gpio_) {
#if ESP_ARDUINO_VERSION_MAJOR < 3 #if ESP_ARDUINO_VERSION_MAJOR < 3
led_type_ ? neopixelWrite(led_gpio_, 0, 0, 0) : digitalWrite(led_gpio_, !LED_ON); led_type_ ? neopixelWrite(led_gpio_, 0, 0, 0) : digitalWrite(led_gpio_, !LED_ON);
#else #else
led_type_ ? rgbLedWrite(led_gpio_, 0, 0, 0) : digitalWrite(led_gpio_, !LED_ON); led_type_ ? rgbLedWrite(led_gpio_, 0, 0, 0) : digitalWrite(led_gpio_, !LED_ON);
#endif #endif
pinMode(led_gpio_, INPUT);
} }
reload_settings(); reload_settings();
} }
if ((led_gpio_ != 0) && is_valid_gpio(led_gpio_)) { // 0 means disabled if ((led_gpio_)) { // 0 means disabled
if (led_type_) { if (led_type_) {
// rgb LED WS2812B, use Neopixel // rgb LED WS2812B, use Neopixel
#if ESP_ARDUINO_VERSION_MAJOR < 3 #if ESP_ARDUINO_VERSION_MAJOR < 3
@@ -595,9 +595,27 @@ void System::led_init(bool refresh) {
pinMode(led_gpio_, OUTPUT); pinMode(led_gpio_, OUTPUT);
digitalWrite(led_gpio_, !LED_ON); // start with LED off digitalWrite(led_gpio_, !LED_ON); // start with LED off
} }
} else {
LOG_INFO("LED disabled");
} }
} }
void System::uart_init(bool refresh) {
if (refresh) {
reload_settings();
}
EMSuart::stop();
// don't start UART if we have invalid GPIOs
if (rx_gpio_ && tx_gpio_) {
EMSuart::start(tx_mode_, rx_gpio_, tx_gpio_); // start UART
} else {
LOG_WARNING("Invalid UART Rx/Tx GPIOs. Check config.");
}
EMSESP::txservice_.start(); // reset counters and send devices request
}
// checks system health and handles LED flashing wizardry // checks system health and handles LED flashing wizardry
void System::loop() { void System::loop() {
// check if we're supposed to do a reset/restart // check if we're supposed to do a reset/restart
@@ -1802,11 +1820,12 @@ bool System::command_info(const char * value, const int8_t id, JsonObject output
node["ethPhyAddr"] = settings.eth_phy_addr; node["ethPhyAddr"] = settings.eth_phy_addr;
node["ethClockMmode"] = settings.eth_clock_mode; node["ethClockMmode"] = settings.eth_clock_mode;
} }
node["rxGPIO"] = settings.rx_gpio; node["rxGPIO"] = EMSESP::system_.rx_gpio_;
node["txGPIO"] = settings.tx_gpio; node["txGPIO"] = EMSESP::system_.tx_gpio_;
node["dallasGPIO"] = settings.dallas_gpio; node["dallasGPIO"] = EMSESP::system_.dallas_gpio_;
node["pbuttonGPIO"] = settings.pbutton_gpio; node["pbuttonGPIO"] = EMSESP::system_.pbutton_gpio_;
node["ledGPIO"] = settings.led_gpio; node["ledGPIO"] = EMSESP::system_.led_gpio_;
node["ledType"] = settings.led_type;
node["ledType"] = settings.led_type; node["ledType"] = settings.led_type;
} }
node["hideLed"] = settings.hide_led; node["hideLed"] = settings.hide_led;
@@ -2369,10 +2388,12 @@ std::vector<uint8_t> System::valid_gpio_list() {
valid_gpios.erase(std::remove(valid_gpios.begin(), valid_gpios.end(), 22), valid_gpios.end()); valid_gpios.erase(std::remove(valid_gpios.begin(), valid_gpios.end(), 22), valid_gpios.end());
} }
// filter out GPIOs already used in application settings // filter out GPIOs already used in application settings, gpio 0 means disabled, except for pbutton
for (const auto & gpio : valid_gpios) { for (const auto & gpio : valid_gpios) {
if (gpio == EMSESP::system_.pbutton_gpio_ || gpio == EMSESP::system_.led_gpio_ || gpio == EMSESP::system_.dallas_gpio_ if (gpio == EMSESP::system_.pbutton_gpio_
|| gpio == EMSESP::system_.rx_gpio_ || gpio == EMSESP::system_.tx_gpio_) { || (gpio
&& (gpio == EMSESP::system_.led_gpio_ || gpio == EMSESP::system_.dallas_gpio_ || gpio == EMSESP::system_.rx_gpio_
|| gpio == EMSESP::system_.tx_gpio_))) {
valid_gpios.erase(std::remove(valid_gpios.begin(), valid_gpios.end(), gpio), valid_gpios.end()); valid_gpios.erase(std::remove(valid_gpios.begin(), valid_gpios.end(), gpio), valid_gpios.end());
} }
} }

View File

@@ -133,6 +133,7 @@ class System {
void network_init(bool refresh); void network_init(bool refresh);
void button_init(bool refresh); void button_init(bool refresh);
void commands_init(); void commands_init();
void uart_init(bool refresh);
void systemStatus(uint8_t status_code); void systemStatus(uint8_t status_code);
uint8_t systemStatus(); uint8_t systemStatus();
@@ -145,6 +146,10 @@ class System {
static bool readCommand(const char * data); static bool readCommand(const char * data);
void dallas_gpio(uint8_t gpio) {
dallas_gpio_ = gpio;
}
bool telnet_enabled() { bool telnet_enabled() {
return telnet_enabled_; return telnet_enabled_;
} }
@@ -413,6 +418,7 @@ class System {
uint8_t pbutton_gpio_; uint8_t pbutton_gpio_;
uint8_t rx_gpio_; uint8_t rx_gpio_;
uint8_t tx_gpio_; uint8_t tx_gpio_;
uint8_t tx_mode_;
uint8_t dallas_gpio_; uint8_t dallas_gpio_;
bool telnet_enabled_; bool telnet_enabled_;
bool syslog_enabled_; bool syslog_enabled_;

View File

@@ -55,10 +55,12 @@ void TemperatureSensor::start(const bool factory_settings) {
// load settings // load settings
void TemperatureSensor::reload() { void TemperatureSensor::reload() {
// load the service settings // load the service settings
EMSESP::system_.dallas_gpio(0); // reset in system to check valid sensor
EMSESP::webSettingsService.read([&](WebSettings const & settings) { EMSESP::webSettingsService.read([&](WebSettings const & settings) {
dallas_gpio_ = settings.dallas_gpio; dallas_gpio_ = EMSESP::system_.is_valid_gpio(settings.dallas_gpio) ? settings.dallas_gpio : 0;
parasite_ = settings.dallas_parasite; parasite_ = settings.dallas_parasite;
}); });
EMSESP::system_.dallas_gpio(dallas_gpio_); // set to system for checks
for (auto & sensor : sensors_) { for (auto & sensor : sensors_) {
remove_ha_topic(sensor.id()); remove_ha_topic(sensor.id());

View File

@@ -409,7 +409,7 @@ void WebSettingsService::onUpdate() {
} }
if (WebSettings::has_flags(WebSettings::ChangeFlags::UART)) { if (WebSettings::has_flags(WebSettings::ChangeFlags::UART)) {
EMSESP::uart_init(); EMSESP::system_.uart_init(true);
} }
if (WebSettings::has_flags(WebSettings::ChangeFlags::SYSLOG)) { if (WebSettings::has_flags(WebSettings::ChangeFlags::SYSLOG)) {

View File

@@ -195,6 +195,9 @@ void WebStatusService::action(AsyncWebServerRequest * request, JsonVariant json)
ok = uploadURL(param.c_str()); ok = uploadURL(param.c_str());
} else if (action == "systemStatus" && is_admin) { } else if (action == "systemStatus" && is_admin) {
ok = setSystemStatus(param.c_str()); ok = setSystemStatus(param.c_str());
} else if (action == "resetMQTT" && is_admin) {
emsesp::EMSESP::mqtt_.reset_mqtt();
ok = true;
} }
#if defined(EMSESP_STANDALONE) && !defined(EMSESP_UNITY) #if defined(EMSESP_STANDALONE) && !defined(EMSESP_UNITY)