mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2026-05-08 23:15:52 +00:00
Compare commits
32 Commits
d6d3a034ad
...
latest
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ad5d13168b | ||
|
|
ae5beccb9d | ||
|
|
764c660b14 | ||
|
|
ca0f32b087 | ||
|
|
8ad91de54a | ||
|
|
58da157272 | ||
|
|
b5014bf9ac | ||
|
|
14351436a7 | ||
|
|
469d412951 | ||
|
|
6edbac86e2 | ||
|
|
db2be70d66 | ||
|
|
c36f231990 | ||
|
|
26102121e1 | ||
|
|
8e64c6303e | ||
|
|
74c76eb90b | ||
|
|
daffdcf58e | ||
|
|
4bc4fa903f | ||
|
|
29380f0303 | ||
|
|
6b2370b79d | ||
|
|
dbc636c9bf | ||
|
|
0c0660c04b | ||
|
|
c9fd076394 | ||
|
|
35550553be | ||
|
|
06ff219385 | ||
|
|
e705a5629f | ||
|
|
cb3c9653ce | ||
|
|
0b5a83f6ae | ||
|
|
a079169005 | ||
|
|
845c51d5f9 | ||
|
|
854f4d559a | ||
|
|
d9b6de0652 | ||
|
|
c54da18822 |
@@ -19,6 +19,8 @@ For more details go to [emsesp.org](https://emsesp.org/).
|
||||
|
||||
- SRC climate creation [#2936](https://github.com/emsesp/EMS-ESP32/issues/2936) and [#2960](https://github.com/emsesp/EMS-ESP32/issues/2960)
|
||||
- missing translations [#3015](https://github.com/emsesp/EMS-ESP32/issues/3015)
|
||||
- custom entities check fetch length
|
||||
- modbus initialization [#3064](https://github.com/emsesp/EMS-ESP32/issues/3064)
|
||||
|
||||
## Changed
|
||||
|
||||
@@ -33,3 +35,4 @@ For more details go to [emsesp.org](https://emsesp.org/).
|
||||
- fetch telegrams: set length to fetch [#3017](https://github.com/emsesp/EMS-ESP32/issues/3017)
|
||||
- move http client from stack to heap
|
||||
- heap optimizations [#3021](https://github.com/emsesp/EMS-ESP32/discussions/3021)
|
||||
- check and read 0x470 as summer2_typeids[0] only if received [#2686](https://github.com/emsesp/EMS-ESP32/issues/2686), [#3055](https://github.com/emsesp/EMS-ESP32/issues/3055)
|
||||
|
||||
@@ -112,10 +112,10 @@ telegram_type_id,name,is_fetched
|
||||
0x02A0,RC300Curves,
|
||||
0x02A1,RC300Curves,
|
||||
0x02A2,RC300Curves,
|
||||
0x02A5,RC300Monitor,fetched
|
||||
0x02A6,CRFMonitor,
|
||||
0x02A5,RC300Monitor,
|
||||
0x02A6,RC300Monitor,
|
||||
0x02A7,RC300Monitor,
|
||||
0x02A8,CRFMonitor,
|
||||
0x02A8,RC300Monitor,
|
||||
0x02A9,RC300Monitor,
|
||||
0x02AA,RC300Monitor,
|
||||
0x02AB,RC300Monitor,
|
||||
@@ -171,6 +171,7 @@ telegram_type_id,name,is_fetched
|
||||
0x0468,HPSet,
|
||||
0x0469,HPSet,
|
||||
0x046A,HPSet,
|
||||
0x0470,RC300Summer2,fetched
|
||||
0x0471,RC300Summer2,
|
||||
0x0472,RC300Summer2,
|
||||
0x0473,RC300Summer2,
|
||||
|
||||
|
@@ -26,8 +26,8 @@
|
||||
"@alova/adapter-xhr": "2.3.1",
|
||||
"@emotion/react": "^11.14.0",
|
||||
"@emotion/styled": "^11.14.1",
|
||||
"@mui/icons-material": "^9.0.0",
|
||||
"@mui/material": "^9.0.0",
|
||||
"@mui/icons-material": "^9.0.1",
|
||||
"@mui/material": "^9.0.1",
|
||||
"@preact/compat": "^18.3.2",
|
||||
"@table-library/react-table-library": "4.1.15",
|
||||
"alova": "3.5.1",
|
||||
@@ -38,11 +38,11 @@
|
||||
"magic-string": "^0.30.21",
|
||||
"mime-types": "^3.0.2",
|
||||
"preact": "^10.29.1",
|
||||
"react": "^19.2.5",
|
||||
"react-dom": "^19.2.5",
|
||||
"react": "^19.2.6",
|
||||
"react-dom": "^19.2.6",
|
||||
"react-icons": "^5.6.0",
|
||||
"react-router": "^7.14.1",
|
||||
"react-toastify": "^11.0.5",
|
||||
"react-router": "^7.15.0",
|
||||
"react-toastify": "^11.1.0",
|
||||
"typesafe-i18n": "^5.27.1",
|
||||
"typescript": "^6.0.3"
|
||||
},
|
||||
@@ -55,17 +55,17 @@
|
||||
"@types/node": "^25.6.0",
|
||||
"@types/react": "^19.2.14",
|
||||
"@types/react-dom": "^19.2.3",
|
||||
"axe-core": "^4.11.3",
|
||||
"axe-core": "^4.11.4",
|
||||
"concurrently": "^9.2.1",
|
||||
"eslint": "^10.2.1",
|
||||
"eslint": "^10.3.0",
|
||||
"eslint-config-prettier": "^10.1.8",
|
||||
"prettier": "^3.8.3",
|
||||
"rollup-plugin-visualizer": "^7.0.1",
|
||||
"terser": "^5.46.1",
|
||||
"typescript-eslint": "^8.58.2",
|
||||
"vite": "^8.0.8",
|
||||
"terser": "^5.47.0",
|
||||
"typescript-eslint": "^8.59.2",
|
||||
"vite": "^8.0.11",
|
||||
"vite-plugin-imagemin": "^0.6.1",
|
||||
"vite-tsconfig-paths": "^6.1.1"
|
||||
},
|
||||
"packageManager": "pnpm@10.33.0+sha512.10568bb4a6afb58c9eb3630da90cc9516417abebd3fabbe6739f0ae795728da1491e9db5a544c76ad8eb7570f5c4bb3d6c637b2cb41bfdcdb47fa823c8649319"
|
||||
"packageManager": "pnpm@10.33.4"
|
||||
}
|
||||
|
||||
754
interface/pnpm-lock.yaml
generated
754
interface/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -18,7 +18,7 @@ import { PROJECT_NAME } from 'env';
|
||||
import { useI18nContext } from 'i18n/i18n-react';
|
||||
import type { SignInRequest } from 'types';
|
||||
import { onEnterCallback, updateValue } from 'utils';
|
||||
import { SIGN_IN_REQUEST_VALIDATOR, validate } from 'validators';
|
||||
import { SIGN_IN_REQUEST_VALIDATOR, ValidationError, validate } from 'validators';
|
||||
|
||||
const SignIn = memo(() => {
|
||||
const authenticationContext = useContext(AuthenticationContext);
|
||||
@@ -74,7 +74,7 @@ const SignIn = memo(() => {
|
||||
await validate(SIGN_IN_REQUEST_VALIDATOR, signInRequest);
|
||||
await signIn();
|
||||
} catch (error) {
|
||||
setFieldErrors(error as ValidateFieldsError);
|
||||
setFieldErrors((error as ValidationError).fieldErrors);
|
||||
setProcessing(false);
|
||||
}
|
||||
}, [signInRequest, signIn, LL]);
|
||||
|
||||
@@ -28,7 +28,7 @@ import type { ValidateFieldsError } from 'async-validator';
|
||||
import { BlockFormControlLabel, ValidatedTextField } from 'components';
|
||||
import { useI18nContext } from 'i18n/i18n-react';
|
||||
import { numberValue, updateValue } from 'utils';
|
||||
import { validate } from 'validators';
|
||||
import { ValidationError, validate } from 'validators';
|
||||
|
||||
import { DeviceValueType, DeviceValueTypeNames, DeviceValueUOM_s } from './types';
|
||||
import type { EntityItem } from './types';
|
||||
@@ -136,7 +136,7 @@ const CustomEntitiesDialog = ({
|
||||
}
|
||||
onSave(processedItem);
|
||||
} catch (error) {
|
||||
setFieldErrors(error as ValidateFieldsError);
|
||||
setFieldErrors((error as ValidationError).fieldErrors);
|
||||
}
|
||||
}, [validator, editItem, onSave]);
|
||||
|
||||
@@ -215,7 +215,7 @@ const CustomEntitiesDialog = ({
|
||||
name="value"
|
||||
label={LL.DEFAULT(0) + ' ' + LL.VALUE(0)}
|
||||
type="string"
|
||||
value={editItem.value as string}
|
||||
value={editItem.value}
|
||||
variant="outlined"
|
||||
onChange={updateFormValue}
|
||||
fullWidth
|
||||
@@ -260,7 +260,7 @@ const CustomEntitiesDialog = ({
|
||||
margin="normal"
|
||||
sx={{ width: '11ch' }}
|
||||
type="string"
|
||||
value={editItem.device_id as string}
|
||||
value={editItem.device_id}
|
||||
onChange={updateFormValue}
|
||||
slotProps={{
|
||||
input: {
|
||||
@@ -280,7 +280,7 @@ const CustomEntitiesDialog = ({
|
||||
margin="normal"
|
||||
sx={{ width: '11ch' }}
|
||||
type="string"
|
||||
value={editItem.type_id as string}
|
||||
value={editItem.type_id}
|
||||
onChange={updateFormValue}
|
||||
slotProps={{
|
||||
input: {
|
||||
@@ -381,7 +381,7 @@ const CustomEntitiesDialog = ({
|
||||
fieldErrors={fieldErrors || {}}
|
||||
name="factor"
|
||||
label={LL.BITMASK()}
|
||||
value={editItem.factor as string}
|
||||
value={editItem.factor}
|
||||
sx={{ width: '11ch' }}
|
||||
variant="outlined"
|
||||
onChange={updateFormValue}
|
||||
|
||||
@@ -24,7 +24,7 @@ import type { ValidateFieldsError } from 'async-validator';
|
||||
import { ValidatedTextField } from 'components';
|
||||
import { useI18nContext } from 'i18n/i18n-react';
|
||||
import { numberValue, updateValue } from 'utils';
|
||||
import { validate } from 'validators';
|
||||
import { ValidationError, validate } from 'validators';
|
||||
|
||||
import { DeviceValueUOM, DeviceValueUOM_s } from './types';
|
||||
import type { DeviceValue } from './types';
|
||||
@@ -67,7 +67,7 @@ const DevicesDialog = ({
|
||||
await validate(validator, editItem);
|
||||
onSave(editItem);
|
||||
} catch (error) {
|
||||
setFieldErrors(error as ValidateFieldsError);
|
||||
setFieldErrors((error as ValidationError).fieldErrors);
|
||||
}
|
||||
}, [validator, editItem, onSave]);
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ import type { ValidateFieldsError } from 'async-validator';
|
||||
import { BlockFormControlLabel, ValidatedTextField } from 'components';
|
||||
import { useI18nContext } from 'i18n/i18n-react';
|
||||
import { updateValue } from 'utils';
|
||||
import { validate } from 'validators';
|
||||
import { ValidationError, validate } from 'validators';
|
||||
|
||||
import { ScheduleFlag } from './types';
|
||||
import type { ScheduleItem } from './types';
|
||||
@@ -120,7 +120,7 @@ const SchedulerDialog = ({
|
||||
await validate(validator, itemToSave);
|
||||
onSave(itemToSave);
|
||||
} catch (error) {
|
||||
setFieldErrors(error as ValidateFieldsError);
|
||||
setFieldErrors((error as ValidationError).fieldErrors);
|
||||
}
|
||||
},
|
||||
[validator, onSave]
|
||||
|
||||
@@ -23,7 +23,7 @@ import type { ValidateFieldsError } from 'async-validator';
|
||||
import { ValidatedTextField } from 'components';
|
||||
import { useI18nContext } from 'i18n/i18n-react';
|
||||
import { numberValue, updateValue } from 'utils';
|
||||
import { validate } from 'validators';
|
||||
import { ValidationError, validate } from 'validators';
|
||||
|
||||
import { AnalogType, AnalogTypeNames, DeviceValueUOM_s } from './types';
|
||||
import type { AnalogSensor } from './types';
|
||||
@@ -172,7 +172,7 @@ const SensorsAnalogDialog = ({
|
||||
await validate(validator, editItem);
|
||||
onSave(editItem);
|
||||
} catch (error) {
|
||||
setFieldErrors(error as ValidateFieldsError);
|
||||
setFieldErrors((error as ValidationError).fieldErrors);
|
||||
}
|
||||
}, [validator, editItem, onSave]);
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ import type { ValidateFieldsError } from 'async-validator';
|
||||
import { ValidatedTextField } from 'components';
|
||||
import { useI18nContext } from 'i18n/i18n-react';
|
||||
import { numberValue, updateValue } from 'utils';
|
||||
import { validate } from 'validators';
|
||||
import { ValidationError, validate } from 'validators';
|
||||
|
||||
import type { TemperatureSensor } from './types';
|
||||
|
||||
@@ -84,7 +84,7 @@ const SensorsTemperatureDialog = ({
|
||||
await validate(validator, editItem);
|
||||
onSave(editItem);
|
||||
} catch (error) {
|
||||
setFieldErrors(error as ValidateFieldsError);
|
||||
setFieldErrors((error as ValidationError).fieldErrors);
|
||||
}
|
||||
}, [validator, editItem, onSave]);
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ import { useI18nContext } from 'i18n/i18n-react';
|
||||
import type { APSettingsType } from 'types';
|
||||
import { APProvisionMode } from 'types';
|
||||
import { numberValue, updateValueDirty, useRest } from 'utils';
|
||||
import { createAPSettingsValidator, validate } from 'validators';
|
||||
import { ValidationError, createAPSettingsValidator, validate } from 'validators';
|
||||
|
||||
export const isAPEnabled = ({ provision_mode }: APSettingsType) =>
|
||||
provision_mode === APProvisionMode.AP_MODE_ALWAYS ||
|
||||
@@ -86,7 +86,7 @@ const APSettings = () => {
|
||||
await validate(createAPSettingsValidator(data), data);
|
||||
await saveData();
|
||||
} catch (error) {
|
||||
setFieldErrors(error as ValidateFieldsError);
|
||||
setFieldErrors((error as ValidationError).fieldErrors);
|
||||
}
|
||||
}, [data, saveData]);
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ import {
|
||||
} from 'components';
|
||||
import { useI18nContext } from 'i18n/i18n-react';
|
||||
import { numberValue, updateValueDirty, useRest } from 'utils';
|
||||
import { validate } from 'validators';
|
||||
import { ValidationError, validate } from 'validators';
|
||||
|
||||
import { API, getBoardProfile, readSettings, writeSettings } from '../../api/app';
|
||||
import { BOARD_PROFILES } from '../main/types';
|
||||
@@ -153,7 +153,7 @@ const ApplicationSettings = () => {
|
||||
setFieldErrors(undefined);
|
||||
await validate(createSettingsValidator(data), data);
|
||||
} catch (error) {
|
||||
setFieldErrors(error as ValidateFieldsError);
|
||||
setFieldErrors((error as ValidationError).fieldErrors);
|
||||
} finally {
|
||||
await saveData();
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ import {
|
||||
import { useI18nContext } from 'i18n/i18n-react';
|
||||
import type { MqttSettingsType } from 'types';
|
||||
import { numberValue, updateValueDirty, useRest } from 'utils';
|
||||
import { createMqttSettingsValidator, validate } from 'validators';
|
||||
import { ValidationError, createMqttSettingsValidator, validate } from 'validators';
|
||||
|
||||
import { callAction } from '../../api/app';
|
||||
|
||||
@@ -94,7 +94,7 @@ const MqttSettings = () => {
|
||||
await validate(createMqttSettingsValidator(data), data);
|
||||
await saveData();
|
||||
} catch (error) {
|
||||
setFieldErrors(error as ValidateFieldsError);
|
||||
setFieldErrors((error as ValidationError).fieldErrors);
|
||||
}
|
||||
}, [data, saveData]);
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ import {
|
||||
import { useI18nContext } from 'i18n/i18n-react';
|
||||
import type { NTPSettingsType, Time } from 'types';
|
||||
import { formatLocalDateTime, updateValueDirty, useRest } from 'utils';
|
||||
import { validate } from 'validators';
|
||||
import { ValidationError, validate } from 'validators';
|
||||
import { NTP_SETTINGS_VALIDATOR } from 'validators/ntp';
|
||||
|
||||
import { TIME_ZONES, selectedTimeZone, useTimeZoneSelectItems } from './TZ';
|
||||
@@ -133,7 +133,7 @@ const NTPSettings = () => {
|
||||
await validate(NTP_SETTINGS_VALIDATOR, data);
|
||||
await saveData();
|
||||
} catch (error) {
|
||||
setFieldErrors(error as ValidateFieldsError);
|
||||
setFieldErrors((error as ValidationError).fieldErrors);
|
||||
}
|
||||
}, [data, saveData]);
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ import {
|
||||
import { useI18nContext } from 'i18n/i18n-react';
|
||||
import type { NetworkSettingsType } from 'types';
|
||||
import { updateValueDirty, useRest } from 'utils';
|
||||
import { validate } from 'validators';
|
||||
import { ValidationError, validate } from 'validators';
|
||||
import { createNetworkSettingsValidator } from 'validators/network';
|
||||
|
||||
import SystemMonitor from '../../status/SystemMonitor';
|
||||
@@ -116,7 +116,7 @@ const NetworkSettings = () => {
|
||||
await validate(createNetworkSettingsValidator(data), data);
|
||||
await saveData();
|
||||
} catch (error) {
|
||||
setFieldErrors(error as ValidateFieldsError);
|
||||
setFieldErrors((error as ValidationError).fieldErrors);
|
||||
}
|
||||
deselectNetwork();
|
||||
}, [data, saveData, deselectNetwork]);
|
||||
|
||||
@@ -19,7 +19,7 @@ import { AuthenticatedContext } from 'contexts/authentication';
|
||||
import { useI18nContext } from 'i18n/i18n-react';
|
||||
import type { SecuritySettingsType } from 'types';
|
||||
import { updateValueDirty, useRest } from 'utils';
|
||||
import { SECURITY_SETTINGS_VALIDATOR, validate } from 'validators';
|
||||
import { SECURITY_SETTINGS_VALIDATOR, ValidationError, validate } from 'validators';
|
||||
|
||||
const SecuritySettings = () => {
|
||||
const { LL } = useI18nContext();
|
||||
@@ -58,7 +58,7 @@ const SecuritySettings = () => {
|
||||
await saveData();
|
||||
await authenticatedContext.refresh();
|
||||
} catch (error) {
|
||||
setFieldErrors(error as ValidateFieldsError);
|
||||
setFieldErrors((error as ValidationError).fieldErrors);
|
||||
}
|
||||
}, [data, saveData, authenticatedContext]);
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ import {
|
||||
import { useI18nContext } from 'i18n/i18n-react';
|
||||
import type { UserType } from 'types';
|
||||
import { updateValue } from 'utils';
|
||||
import { validate } from 'validators';
|
||||
import { ValidationError, validate } from 'validators';
|
||||
|
||||
interface UserFormProps {
|
||||
creating: boolean;
|
||||
@@ -69,7 +69,7 @@ const User: FC<UserFormProps> = ({
|
||||
await validate(validator, user);
|
||||
onDoneEditing();
|
||||
} catch (error) {
|
||||
setFieldErrors(error as ValidateFieldsError);
|
||||
setFieldErrors((error as ValidationError).fieldErrors);
|
||||
}
|
||||
}
|
||||
}, [user, validator, onDoneEditing]);
|
||||
|
||||
@@ -187,7 +187,7 @@ const de: Translation = {
|
||||
COMPACT: 'Kompakte Darstellung',
|
||||
DOWNLOAD_SETTINGS_TEXT: 'Erstellen Sie eine Sicherung Ihrer Konfigurationen und Einstellungen',
|
||||
DOWNLOAD_SETTINGS_TEXT2: 'Exportiere alle Werte',
|
||||
DOWNLOAD_SYSTEM_BACKUP: 'System Sicherung',
|
||||
DOWNLOAD_SYSTEM_BACKUP: 'Systemsicherung',
|
||||
UPLOAD_TEXT: 'Laden Sie eine neue Firmware-Datei (.bin) oder eine Sicherungsdatei (.json) hoch',
|
||||
UPLOAD_DROP_TEXT: 'Legen Sie eine Firmware-Datei (.bin) ab oder klicken Sie hier',
|
||||
ERROR: 'Unerwarteter Fehler, bitte versuchen Sie es erneut.',
|
||||
@@ -362,9 +362,9 @@ const de: Translation = {
|
||||
STORED_VERSIONS: 'Gespeicherte Versionen',
|
||||
ONLINE_HELP: 'Online-Hilfe',
|
||||
UPGRADE_IMPORTANT_MESSAGES: 'Wichtige Nachrichten aktualisieren',
|
||||
UPGRADE_IMPORTANT_MESSAGES_1: 'Diese Aktualisierung erfordert eine Werkseinstellung. Stellen Sie sicher, dass Sie zuerst eine Systemsicherung herunterladen, bevor Sie fortfahren, und laden Sie diese Datei dann nach der Installation der neuen Version hoch.',
|
||||
UPGRADE_IMPORTANT_MESSAGES_1: 'Für diese Aktualisierung ist ein Werksreset erforderlich. Stellen Sie sicher, dass Sie zuerst eine Systemsicherung herunterladen, bevor Sie fortfahren, und laden Sie diese Datei dann nach der Installation der neuen Version hoch.',
|
||||
UPGRADE_IMPORTANT_MESSAGES_2: 'Sie aktualisieren auf eine neue Hauptversion. Stellen Sie sicher, dass Sie den ChangeLog für alle wichtigen Änderungen gelesen haben.',
|
||||
WARNING_SYSTEM_BACKUP: 'Dies wird eine Sicherung Ihrer vollständigen Systemkonfiguration und -einstellungen erstellen. Alle Passwörter werden im Sicherungsdatei lesbar sein. Seien Sie vorsichtig beim Teilen! Möchten Sie fortfahren?'
|
||||
WARNING_SYSTEM_BACKUP: 'Dies wird eine Sicherung Ihrer vollständigen Systemkonfiguration und Einstellungen erstellen. Alle Passwörter werden in dieser Sicherungsdatei lesbar sein. Seien Sie vorsichtig beim Teilen! Möchten Sie fortfahren?'
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -1,6 +1,20 @@
|
||||
import type { InternalRuleItem, ValidateOption } from 'async-validator';
|
||||
import type {
|
||||
InternalRuleItem,
|
||||
ValidateFieldsError,
|
||||
ValidateOption
|
||||
} from 'async-validator';
|
||||
import type Schema from 'async-validator';
|
||||
|
||||
export class ValidationError extends Error {
|
||||
readonly fieldErrors: ValidateFieldsError;
|
||||
|
||||
constructor(fieldErrors: ValidateFieldsError) {
|
||||
super('Validation failed');
|
||||
this.name = 'ValidationError';
|
||||
this.fieldErrors = fieldErrors;
|
||||
}
|
||||
}
|
||||
|
||||
export const validate = <T extends object>(
|
||||
validator: Schema,
|
||||
source: Partial<T>,
|
||||
@@ -8,7 +22,7 @@ export const validate = <T extends object>(
|
||||
): Promise<T> =>
|
||||
new Promise((resolve, reject) => {
|
||||
void validator.validate(source, options ?? {}, (errors, fieldErrors) => {
|
||||
errors ? reject(fieldErrors as Error) : resolve(source as T);
|
||||
errors ? reject(new ValidationError(fieldErrors)) : resolve(source as T);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -15,5 +15,5 @@
|
||||
"itty-router": "^5.0.23",
|
||||
"prettier": "^3.8.3"
|
||||
},
|
||||
"packageManager": "pnpm@10.33.0+sha512.10568bb4a6afb58c9eb3630da90cc9516417abebd3fabbe6739f0ae795728da1491e9db5a544c76ad8eb7570f5c4bb3d6c637b2cb41bfdcdb47fa823c8649319"
|
||||
"packageManager": "pnpm@10.33.1+sha512.05ba3c1d5d1c18f68df06470d74055e62d41fc110a0c660db1b2dfb2785327f04cf0f68345d4609bc52089e7fa0343c31593b2f9594e2c5d5da426230acc9820"
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ framework = arduino
|
||||
board_build.partitions = partitions/esp32_partition_16M.csv
|
||||
board_upload.flash_size = 16MB
|
||||
board_build.app_partition_name = app0
|
||||
platform = espressif32@6.13.0 ; Arduino Core 2.0.17 / IDF 4.4.7
|
||||
platform = espressif32@7.0.0 ; Arduino Core 2.0.17 / IDF 4.4.7
|
||||
|
||||
; 32MB Flash variants
|
||||
[espressif32_base_32M]
|
||||
@@ -67,7 +67,7 @@ framework = arduino
|
||||
board_build.partitions = partitions/esp32_partition_32M.csv
|
||||
board_upload.flash_size = 32MB
|
||||
board_build.app_partition_name = app0
|
||||
platform = espressif32@6.13.0 ; Arduino Core 2.0.17 / IDF 4.4.7
|
||||
platform = espressif32@7.0.0 ; Arduino Core 2.0.17 / IDF 4.4.7
|
||||
|
||||
; use Tasmota's library for 4MB Flash variants.
|
||||
; Removes libs (like mbedtsl, so no WiFi_secure.h) to increase available heap
|
||||
@@ -105,7 +105,7 @@ board_build.filesystem = littlefs
|
||||
lib_deps =
|
||||
bblanchon/ArduinoJson @ 7.4.3
|
||||
ESP32Async/AsyncTCP @ 3.4.10
|
||||
ESP32Async/ESPAsyncWebServer @ 3.10.3
|
||||
ESP32Async/ESPAsyncWebServer @ 3.11.0
|
||||
; https://github.com/emsesp/EMS-ESP-Modules.git @ 1.0.8
|
||||
|
||||
; builds the web interface only, not the firmware
|
||||
|
||||
@@ -102,7 +102,7 @@ def buildWeb():
|
||||
return False
|
||||
|
||||
|
||||
def build-webUI(*args, **kwargs):
|
||||
def build_webUI(*args, **kwargs):
|
||||
success = buildWeb()
|
||||
if not success:
|
||||
print("Web interface build failed!")
|
||||
@@ -114,7 +114,7 @@ def build-webUI(*args, **kwargs):
|
||||
env.AddCustomTarget(
|
||||
name="build",
|
||||
dependencies=None,
|
||||
actions=[build-webUI],
|
||||
actions=[build_webUI],
|
||||
title="build web interface",
|
||||
description="installs pnpm packages, updates libraries and builds web UI",
|
||||
always_build=True
|
||||
|
||||
@@ -70,16 +70,12 @@ void ArduinoJsonJWT::parseJWT(String jwt, JsonDocument & jsonDocument) {
|
||||
*/
|
||||
String ArduinoJsonJWT::sign(String & payload) {
|
||||
std::array<unsigned char, 32> hmacResult{};
|
||||
{
|
||||
mbedtls_md_context_t ctx;
|
||||
mbedtls_md_type_t md_type = MBEDTLS_MD_SHA256;
|
||||
mbedtls_md_init(&ctx);
|
||||
mbedtls_md_setup(&ctx, mbedtls_md_info_from_type(md_type), 1);
|
||||
mbedtls_md_hmac_starts(&ctx, reinterpret_cast<const unsigned char *>(_secret.c_str()), _secret.length());
|
||||
mbedtls_md_hmac_update(&ctx, reinterpret_cast<const unsigned char *>(payload.c_str()), payload.length());
|
||||
mbedtls_md_hmac_finish(&ctx, hmacResult.data());
|
||||
mbedtls_md_free(&ctx);
|
||||
}
|
||||
mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256),
|
||||
reinterpret_cast<const unsigned char *>(_secret.c_str()),
|
||||
_secret.length(),
|
||||
reinterpret_cast<const unsigned char *>(payload.c_str()),
|
||||
payload.length(),
|
||||
hmacResult.data());
|
||||
return encode(reinterpret_cast<const char *>(hmacResult.data()), hmacResult.size());
|
||||
}
|
||||
|
||||
|
||||
@@ -185,10 +185,14 @@ bool MqttSettingsService::configureMqtt() {
|
||||
#ifndef TASMOTA_SDK
|
||||
if (_state.enableTLS) {
|
||||
if (_state.rootCA == "insecure") {
|
||||
#if defined(EMSESP_DEBUG)
|
||||
emsesp::EMSESP::logger().debug("Start insecure MQTT");
|
||||
#endif
|
||||
static_cast<espMqttClientSecure *>(_mqttClient)->setInsecure();
|
||||
} else {
|
||||
#if defined(EMSESP_DEBUG)
|
||||
emsesp::EMSESP::logger().debug("Start secure MQTT with rootCA");
|
||||
#endif
|
||||
String certificate = "-----BEGIN CERTIFICATE-----\n" + _state.rootCA + "\n-----END CERTIFICATE-----\n";
|
||||
static_cast<espMqttClientSecure *>(_mqttClient)->setCACert(certificate.c_str());
|
||||
}
|
||||
|
||||
@@ -69,7 +69,9 @@ class EMSESP_Version {
|
||||
return a.major_ < b.major_;
|
||||
if (a.minor_ != b.minor_)
|
||||
return a.minor_ < b.minor_;
|
||||
return a.patch_ < b.patch_;
|
||||
if (a.patch_ != b.patch_)
|
||||
return a.patch_ < b.patch_;
|
||||
return a.prerelease_ < b.prerelease_;
|
||||
}
|
||||
|
||||
friend bool operator>(const EMSESP_Version & a, const EMSESP_Version & b) {
|
||||
@@ -77,7 +79,7 @@ class EMSESP_Version {
|
||||
}
|
||||
|
||||
friend bool operator==(const EMSESP_Version & a, const EMSESP_Version & b) {
|
||||
return a.major_ == b.major_ && a.minor_ == b.minor_ && a.patch_ == b.patch_;
|
||||
return a.major_ == b.major_ && a.minor_ == b.minor_ && a.patch_ == b.patch_ && a.prerelease_ == b.prerelease_;
|
||||
}
|
||||
|
||||
friend bool operator!=(const EMSESP_Version & a, const EMSESP_Version & b) {
|
||||
|
||||
@@ -387,10 +387,10 @@ void EMSdevice::toggle_fetch(uint16_t telegram_id, bool toggle) {
|
||||
}
|
||||
|
||||
// get status of automatic fetch for a telegramID
|
||||
bool EMSdevice::is_fetch(uint16_t telegram_id) const {
|
||||
bool EMSdevice::is_fetch(uint16_t telegram_id, uint8_t len) const {
|
||||
for (const auto & tf : telegram_functions_) {
|
||||
if (tf.telegram_type_id_ == telegram_id) {
|
||||
return tf.fetch_;
|
||||
return tf.fetch_ && tf.length_ >= len;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -362,7 +362,7 @@ class EMSdevice {
|
||||
const char * telegram_type_name(std::shared_ptr<const Telegram> telegram);
|
||||
void fetch_values();
|
||||
void toggle_fetch(uint16_t telegram_id, bool toggle);
|
||||
bool is_fetch(uint16_t telegram_id) const;
|
||||
bool is_fetch(uint16_t telegram_id, uint8_t len = 0) const;
|
||||
bool is_received(uint16_t telegram_id) const;
|
||||
bool has_telegram_id(uint16_t id) const;
|
||||
void ha_config_clear();
|
||||
|
||||
@@ -526,6 +526,29 @@ void System::syslog_init() {
|
||||
#endif
|
||||
}
|
||||
|
||||
// start or reconfigure modbus
|
||||
void System::modbus_init() {
|
||||
EMSESP::webSettingsService.read([&](WebSettings & settings) {
|
||||
if (settings.modbus_enabled) {
|
||||
if (EMSESP::modbus_ == nullptr) {
|
||||
EMSESP::modbus_ = new Modbus;
|
||||
EMSESP::modbus_->start(1, settings.modbus_port, settings.modbus_max_clients, settings.modbus_timeout * 1000);
|
||||
} else if (settings.modbus_port != modbus_port_ || settings.modbus_max_clients != modbus_max_clients_ || settings.modbus_timeout != modbus_timeout_) {
|
||||
EMSESP::modbus_->stop();
|
||||
EMSESP::modbus_->start(1, settings.modbus_port, settings.modbus_max_clients, settings.modbus_timeout * 1000);
|
||||
}
|
||||
} else if (EMSESP::modbus_ != nullptr) {
|
||||
EMSESP::modbus_->stop();
|
||||
delete EMSESP::modbus_;
|
||||
EMSESP::modbus_ = nullptr;
|
||||
}
|
||||
modbus_enabled_ = settings.modbus_enabled;
|
||||
modbus_port_ = settings.modbus_port;
|
||||
modbus_max_clients_ = settings.modbus_max_clients;
|
||||
modbus_timeout_ = settings.modbus_timeout;
|
||||
});
|
||||
}
|
||||
|
||||
// read specific major system settings to store locally for faster access
|
||||
void System::store_settings(WebSettings & settings) {
|
||||
version_ = settings.version;
|
||||
@@ -563,25 +586,6 @@ void System::store_settings(WebSettings & settings) {
|
||||
|
||||
locale_ = settings.locale;
|
||||
developer_mode_ = settings.developer_mode;
|
||||
|
||||
// start services
|
||||
if (settings.modbus_enabled) {
|
||||
if (EMSESP::modbus_ == nullptr) {
|
||||
EMSESP::modbus_ = new Modbus;
|
||||
EMSESP::modbus_->start(1, settings.modbus_port, settings.modbus_max_clients, settings.modbus_timeout * 1000);
|
||||
} else if (settings.modbus_port != modbus_port_ || settings.modbus_max_clients != modbus_max_clients_ || settings.modbus_timeout != modbus_timeout_) {
|
||||
EMSESP::modbus_->stop();
|
||||
EMSESP::modbus_->start(1, settings.modbus_port, settings.modbus_max_clients, settings.modbus_timeout * 1000);
|
||||
}
|
||||
} else if (EMSESP::modbus_ != nullptr) {
|
||||
EMSESP::modbus_->stop();
|
||||
delete EMSESP::modbus_;
|
||||
EMSESP::modbus_ = nullptr;
|
||||
}
|
||||
modbus_enabled_ = settings.modbus_enabled;
|
||||
modbus_port_ = settings.modbus_port;
|
||||
modbus_max_clients_ = settings.modbus_max_clients;
|
||||
modbus_timeout_ = settings.modbus_timeout;
|
||||
}
|
||||
|
||||
// Starts up core services
|
||||
@@ -631,6 +635,7 @@ void System::start() {
|
||||
network_init(); // network
|
||||
uart_init(); // start UART
|
||||
syslog_init(); // start syslog
|
||||
modbus_init(); // start modbus
|
||||
}
|
||||
|
||||
// button single click
|
||||
@@ -1713,7 +1718,7 @@ void System::exportSystemBackup(JsonObject output) {
|
||||
output["version"] = EMSESP_APP_VERSION; // add the version to the output
|
||||
|
||||
#ifndef EMSESP_STANDALONE
|
||||
// add date/time if NTP enabled and active
|
||||
// add date/time if NTP enabled and active
|
||||
if ((esp_sntp_enabled()) && (EMSESP::system_.ntp_connected())) {
|
||||
time_t now = time(nullptr);
|
||||
if (now > 1500000000L) {
|
||||
@@ -1767,7 +1772,7 @@ void System::exportSystemBackup(JsonObject output) {
|
||||
node = nodes.add<JsonObject>();
|
||||
node["type"] = "nvs";
|
||||
|
||||
const char * nvs_part = "nvs";
|
||||
const char * nvs_part = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_NVS, "nvs1") ? "nvs1" : "nvs"; // nvs1 is on 16MBs
|
||||
nvs_iterator_t it = nullptr;
|
||||
#if ESP_IDF_VERSION_MAJOR < 5
|
||||
it = nvs_entry_find(nvs_part, "ems-esp", NVS_TYPE_ANY);
|
||||
@@ -3266,7 +3271,7 @@ void System::set_valid_system_gpios() {
|
||||
valid_system_gpios_ = string_range_to_vector("0-21", "2, 8, 12-17, 18-19");
|
||||
|
||||
#elif CONFIG_IDF_TARGET_ESP32S2
|
||||
// https://docs.espressif.com/projects/esp-idf/en/stable/esp32s2/api-reference/peripherals/gpio.html
|
||||
// https://docs.espressif.com/projects/esp-idf/en/stable/esp32s2/api-reference/peripherals/gpio.html
|
||||
// excluded:
|
||||
// GPIO26 - GPIO32 = SPI flash and PSRAM
|
||||
// GPIO45 - GPIO46 = strapping pins
|
||||
@@ -3279,7 +3284,7 @@ void System::set_valid_system_gpios() {
|
||||
valid_system_gpios_ = string_range_to_vector("0-46", "19, 20, 26-32, 45-46, 39-42, 22-25");
|
||||
|
||||
#elif CONFIG_IDF_TARGET_ESP32S3
|
||||
// https://docs.espressif.com/projects/esp-idf/en/stable/esp32s3/api-reference/peripherals/gpio.html
|
||||
// https://docs.espressif.com/projects/esp-idf/en/stable/esp32s3/api-reference/peripherals/gpio.html
|
||||
// excluded:
|
||||
// GPIO3, GPIO45 - GPIO46 = strapping pins
|
||||
// GPIO26 - GPIO32 = SPI flash and PSRAM and not recommended
|
||||
@@ -3298,7 +3303,7 @@ void System::set_valid_system_gpios() {
|
||||
}
|
||||
|
||||
#elif CONFIG_IDF_TARGET_ESP32
|
||||
// https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/peripherals/gpio.html
|
||||
// https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/peripherals/gpio.html
|
||||
// excluded:
|
||||
// GPIO6 - GPIO11, GPIO16 - GPIO17 = used for SPI flash and PSRAM (dio mode only GPIO06-GPIO08, GPIO11)
|
||||
// GPIO20, GPIO24, GPIO28 - GPIO31 = don't exist
|
||||
@@ -3378,6 +3383,24 @@ void System::remove_gpio(uint8_t pin, bool also_system) {
|
||||
}
|
||||
}
|
||||
|
||||
// remove a gpio that has 0 for disable
|
||||
void System::remove_optional_gpio(uint8_t pin) {
|
||||
if (pin) {
|
||||
remove_gpio(pin, false);
|
||||
}
|
||||
}
|
||||
|
||||
// set unused gpios to default state input high-Z
|
||||
void System::reset_unused_gpios() {
|
||||
for (const auto & pin : valid_system_gpios_) {
|
||||
auto it = std::find_if(used_gpios_.begin(), used_gpios_.end(), [pin](const GpioUsage & usage) { return usage.pin == pin; });
|
||||
if (it == used_gpios_.end()) {
|
||||
LOG_DEBUG("reset pin %d", pin);
|
||||
pinMode(pin, INPUT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// return a list of GPIO's available for use
|
||||
std::vector<uint8_t> System::available_gpios() {
|
||||
std::vector<uint8_t> gpios;
|
||||
|
||||
@@ -122,6 +122,7 @@ class System {
|
||||
void show_mem(const char * note);
|
||||
void store_settings(class WebSettings & settings);
|
||||
void syslog_init();
|
||||
void modbus_init();
|
||||
bool check_upgrade();
|
||||
bool check_restore();
|
||||
void heartbeat_json(JsonObject output);
|
||||
@@ -376,6 +377,8 @@ class System {
|
||||
#endif
|
||||
|
||||
static void remove_gpio(uint8_t pin, bool also_system = false); // remove a gpio from both valid (optional) and used lists
|
||||
static void remove_optional_gpio(uint8_t pin);
|
||||
static void reset_unused_gpios();
|
||||
|
||||
// Partition info map: partition name -> {version, size, install_date}
|
||||
std::map<std::string, PartitionInfo, std::less<>, AllocatorPSRAM<std::pair<const std::string, PartitionInfo>>> partition_info_;
|
||||
|
||||
@@ -21,12 +21,6 @@
|
||||
#include "temperaturesensor.h"
|
||||
#include "emsesp.h"
|
||||
|
||||
#ifdef ESP32
|
||||
#define YIELD
|
||||
#else
|
||||
#define YIELD yield()
|
||||
#endif
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
uuid::log::Logger TemperatureSensor::logger_{F_(temperaturesensor), uuid::log::Facility::DAEMON};
|
||||
@@ -81,7 +75,6 @@ void TemperatureSensor::loop() {
|
||||
LOG_DEBUG("Read sensor temperature");
|
||||
#endif
|
||||
if (bus_.reset() || parasite_) {
|
||||
YIELD;
|
||||
bus_.skip();
|
||||
bus_.write(CMD_CONVERT_TEMP, parasite_ ? 1 : 0);
|
||||
state_ = State::READING;
|
||||
@@ -260,19 +253,16 @@ int16_t TemperatureSensor::get_temperature_c(const uint8_t addr[]) {
|
||||
LOG_ERROR("Bus reset failed before reading scratchpad from %s", Sensor(addr).id());
|
||||
return EMS_VALUE_INT16_NOTSET;
|
||||
}
|
||||
YIELD;
|
||||
|
||||
uint8_t scratchpad[SCRATCHPAD_LEN] = {0};
|
||||
bus_.select(addr);
|
||||
bus_.write(CMD_READ_SCRATCHPAD);
|
||||
bus_.read_bytes(scratchpad, SCRATCHPAD_LEN);
|
||||
YIELD;
|
||||
|
||||
if (!bus_.reset()) {
|
||||
LOG_ERROR("Bus reset failed after reading scratchpad from %s", Sensor(addr).id());
|
||||
return EMS_VALUE_INT16_NOTSET;
|
||||
}
|
||||
YIELD;
|
||||
|
||||
if (bus_.crc8(scratchpad, SCRATCHPAD_LEN - 1) != scratchpad[SCRATCHPAD_LEN - 1]) {
|
||||
LOG_WARNING("Invalid scratchpad CRC: %02X%02X%02X%02X%02X%02X%02X%02X%02X from sensor %s",
|
||||
|
||||
@@ -173,7 +173,7 @@ Thermostat::Thermostat(uint8_t device_type, uint8_t device_id, uint8_t product_i
|
||||
for (uint8_t i = 0; i < monitor_size; i++) {
|
||||
register_telegram_type(monitor_typeids[i], "RC300Monitor", false, MAKE_PF_CB(process_RC300Monitor), 33);
|
||||
register_telegram_type(set_typeids[i], "RC300Set", false, MAKE_PF_CB(process_RC300Set), 29);
|
||||
register_telegram_type(summer_typeids[i], "RC300Summer", false, MAKE_PF_CB(process_RC300Summer), 13);
|
||||
register_telegram_type(summer_typeids[i], "RC300Summer", false, MAKE_PF_CB(process_RC300Summer), 14);
|
||||
register_telegram_type(curve_typeids[i], "RC300Curves", false, MAKE_PF_CB(process_RC300Curve), 9);
|
||||
register_telegram_type(summer2_typeids[i], "RC300Summer2", false, MAKE_PF_CB(process_RC300Summer2), 8);
|
||||
}
|
||||
@@ -202,11 +202,12 @@ Thermostat::Thermostat(uint8_t device_type, uint8_t device_id, uint8_t product_i
|
||||
if (model == EMSdevice::EMS_DEVICE_FLAG_RC100) {
|
||||
register_telegram_type(0x43F, "CRHolidays", true, MAKE_PF_CB(process_RC300Holiday), 6);
|
||||
} else {
|
||||
register_telegram_type(0x269, "RC300Holiday", true, MAKE_PF_CB(process_RC300Holiday), 6);
|
||||
register_telegram_type(0x269, "RC300Holiday", true, MAKE_PF_CB(process_RC300Holiday), 18);
|
||||
}
|
||||
register_telegram_type(0x16E, "Absent", true, MAKE_PF_CB(process_Absent), 1);
|
||||
register_telegram_type(0xBF, "ErrorMessage", false, MAKE_PF_CB(process_ErrorMessageBF));
|
||||
register_telegram_type(0xC0, "RCErrorMessage", false, MAKE_PF_CB(process_RCErrorMessage2));
|
||||
register_telegram_type(0x470, "RC300Summer2", true, MAKE_PF_CB(process_RC300Summer2), 8);
|
||||
EMSESP::send_read_request(0xC0, device_id, 0, 20); // read last errorcode on start (only published on errors)
|
||||
|
||||
// JUNKERS/HT3
|
||||
@@ -1268,7 +1269,13 @@ void Thermostat::process_RC300Summer(std::shared_ptr<const Telegram> telegram) {
|
||||
void Thermostat::process_RC300Summer2(std::shared_ptr<const Telegram> telegram) {
|
||||
auto hc = heating_circuit(telegram);
|
||||
if (hc == nullptr) {
|
||||
return;
|
||||
// telegram 0x470 see https://github.com/emsesp/EMS-ESP32/issues/2686
|
||||
if (telegram->type_id == 0x470 && telegram->message_length > 2) {
|
||||
hc = heating_circuit(1);
|
||||
summer2_typeids[0] = 0x470;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (hc->statusbyte & 1) {
|
||||
has_update(telegram, hc->summersetmode, 0);
|
||||
|
||||
@@ -1 +1 @@
|
||||
#define EMSESP_APP_VERSION "3.8.2-dev.16"
|
||||
#define EMSESP_APP_VERSION "3.8.2-dev.21"
|
||||
|
||||
@@ -166,7 +166,9 @@ StateUpdateResult WebCustomEntity::update(JsonObject root, WebCustomEntity & web
|
||||
bool WebCustomEntityService::command_setvalue(const char * value, const int8_t id, const char * name) {
|
||||
// don't write if there is no value, to prevent setting an empty value by mistake when parsing attributes
|
||||
if (!strlen(value)) {
|
||||
#if defined(EMSESP_DEBUG)
|
||||
EMSESP::logger().debug("can't set empty value!");
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -686,7 +688,7 @@ void WebCustomEntityService::fetch() {
|
||||
uint8_t stop = (entity.offset + len[entity.value_type]) % fetchblock;
|
||||
bool is_fetched = start < fetchblock && stop < fetchblock; // make sure the complete value is a a fetched block
|
||||
for (const auto & emsdevice : EMSESP::emsdevices) {
|
||||
if (emsdevice->is_device_id(entity.device_id) && emsdevice->is_fetch(entity.type_id)
|
||||
if (emsdevice->is_device_id(entity.device_id) && emsdevice->is_fetch(entity.type_id, entity.offset + len[entity.value_type])
|
||||
&& (is_fetched || entity.value_type == DeviceValueType::STRING)) {
|
||||
needFetch = false;
|
||||
break;
|
||||
|
||||
@@ -465,7 +465,9 @@ void WebSchedulerService::condition() {
|
||||
} else if (match.length() == 1 && match[0] == '0' && scheduleItem.retry_cnt == 1) {
|
||||
scheduleItem.retry_cnt = 0xFF;
|
||||
} else if (match.length() != 1) { // the match is not boolean
|
||||
#if defined(EMSESP_DEBUG)
|
||||
EMSESP::logger().debug("condition result: %s", match.c_str());
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
uint8_t WebSettings::flags_ = 0;
|
||||
uint16_t WebSettings::flags_ = 0;
|
||||
|
||||
WebSettingsService::WebSettingsService(AsyncWebServer * server, FS * fs, SecurityManager * securityManager)
|
||||
: _httpEndpoint(WebSettings::read, WebSettings::update, this, server, EMSESP_SETTINGS_SERVICE_PATH, securityManager)
|
||||
@@ -114,11 +114,11 @@ StateUpdateResult WebSettings::update(JsonObject root, WebSettings & settings) {
|
||||
reset_flags();
|
||||
|
||||
// before loading new board profile free old gpios from used list to allow remapping
|
||||
EMSESP::system_.remove_gpio(original_settings.led_gpio);
|
||||
EMSESP::system_.remove_gpio(original_settings.dallas_gpio);
|
||||
EMSESP::system_.remove_optional_gpio(original_settings.led_gpio);
|
||||
EMSESP::system_.remove_optional_gpio(original_settings.dallas_gpio);
|
||||
EMSESP::system_.remove_gpio(original_settings.pbutton_gpio);
|
||||
EMSESP::system_.remove_gpio(original_settings.rx_gpio);
|
||||
EMSESP::system_.remove_gpio(original_settings.tx_gpio);
|
||||
EMSESP::system_.remove_optional_gpio(original_settings.rx_gpio);
|
||||
EMSESP::system_.remove_optional_gpio(original_settings.tx_gpio);
|
||||
|
||||
// see if the user has changed the board profile
|
||||
// this will set: led_gpio, dallas_gpio, rx_gpio, tx_gpio, pbutton_gpio, phy_type, eth_power, eth_phy_addr, eth_clock_mode, led_type
|
||||
@@ -243,13 +243,13 @@ StateUpdateResult WebSettings::update(JsonObject root, WebSettings & settings) {
|
||||
|
||||
// Modbus settings
|
||||
settings.modbus_enabled = root["modbus_enabled"] | EMSESP_DEFAULT_MODBUS_ENABLED;
|
||||
check_flag(original_settings.modbus_enabled, settings.modbus_enabled, ChangeFlags::RESTART);
|
||||
check_flag(original_settings.modbus_enabled, settings.modbus_enabled, ChangeFlags::MODBUS);
|
||||
settings.modbus_port = root["modbus_port"] | EMSESP_DEFAULT_MODBUS_PORT;
|
||||
check_flag(original_settings.modbus_port, settings.modbus_port, ChangeFlags::RESTART);
|
||||
check_flag(original_settings.modbus_port, settings.modbus_port, ChangeFlags::MODBUS);
|
||||
settings.modbus_max_clients = root["modbus_max_clients"] | EMSESP_DEFAULT_MODBUS_MAX_CLIENTS;
|
||||
check_flag(original_settings.modbus_max_clients, settings.modbus_max_clients, ChangeFlags::RESTART);
|
||||
check_flag(original_settings.modbus_max_clients, settings.modbus_max_clients, ChangeFlags::MODBUS);
|
||||
settings.modbus_timeout = root["modbus_timeout"] | EMSESP_DEFAULT_MODBUS_TIMEOUT;
|
||||
check_flag(original_settings.modbus_timeout, settings.modbus_timeout, ChangeFlags::RESTART);
|
||||
check_flag(original_settings.modbus_timeout, settings.modbus_timeout, ChangeFlags::MODBUS);
|
||||
|
||||
//
|
||||
// these may need mqtt restart to rebuild HA discovery topics
|
||||
@@ -372,7 +372,11 @@ void WebSettingsService::onUpdate() {
|
||||
Mqtt::reset_mqtt(); // reload MQTT, init HA etc
|
||||
}
|
||||
|
||||
if (WebSettings::has_flags(WebSettings::ChangeFlags::MODBUS)) {
|
||||
EMSESP::system_.modbus_init();
|
||||
}
|
||||
WebSettings::reset_flags();
|
||||
EMSESP::system_.reset_unused_gpios();
|
||||
}
|
||||
|
||||
void WebSettingsService::begin() {
|
||||
@@ -523,7 +527,7 @@ void WebSettings::set_board_profile(WebSettings & settings) {
|
||||
}
|
||||
|
||||
// returns true if the value was changed
|
||||
bool WebSettings::check_flag(int prev_v, int new_v, uint8_t flag) {
|
||||
bool WebSettings::check_flag(int prev_v, int new_v, uint16_t flag) {
|
||||
if (prev_v != new_v) {
|
||||
add_flags(flag);
|
||||
#if defined(EMSESP_DEBUG)
|
||||
@@ -534,11 +538,11 @@ bool WebSettings::check_flag(int prev_v, int new_v, uint8_t flag) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void WebSettings::add_flags(uint8_t flags) {
|
||||
void WebSettings::add_flags(uint16_t flags) {
|
||||
flags_ |= flags;
|
||||
}
|
||||
|
||||
bool WebSettings::has_flags(uint8_t flags) {
|
||||
bool WebSettings::has_flags(uint16_t flags) {
|
||||
return (flags_ & flags) == flags;
|
||||
}
|
||||
|
||||
@@ -546,7 +550,7 @@ void WebSettings::reset_flags() {
|
||||
flags_ = ChangeFlags::NONE;
|
||||
}
|
||||
|
||||
uint8_t WebSettings::get_flags() {
|
||||
uint16_t WebSettings::get_flags() {
|
||||
return flags_;
|
||||
}
|
||||
|
||||
|
||||
@@ -88,7 +88,7 @@ class WebSettings {
|
||||
static void read(WebSettings & settings, JsonObject root);
|
||||
static StateUpdateResult update(JsonObject root, WebSettings & settings);
|
||||
|
||||
enum ChangeFlags : uint8_t {
|
||||
enum ChangeFlags : uint16_t {
|
||||
NONE = 0,
|
||||
UART = (1 << 0), // 1 - uart
|
||||
SYSLOG = (1 << 1), // 2 - syslog
|
||||
@@ -98,19 +98,20 @@ class WebSettings {
|
||||
LED = (1 << 5), // 32 - led
|
||||
BUTTON = (1 << 6), // 64 - button
|
||||
MQTT = (1 << 7), // 128 - mqtt
|
||||
RESTART = 0xFF // 255 - restart request (all changes)
|
||||
MODBUS = (1 << 8), // 256 - modbus
|
||||
RESTART = 0xFFFF // restart request (all changes)
|
||||
};
|
||||
|
||||
static bool check_flag(int prev_v, int new_v, uint8_t flag);
|
||||
static void add_flags(uint8_t flags);
|
||||
static bool has_flags(uint8_t flags);
|
||||
static void reset_flags();
|
||||
static uint8_t get_flags();
|
||||
static bool check_flag(int prev_v, int new_v, uint16_t flag);
|
||||
static void add_flags(uint16_t flags);
|
||||
static bool has_flags(uint16_t flags);
|
||||
static void reset_flags();
|
||||
static uint16_t get_flags();
|
||||
|
||||
private:
|
||||
static void set_board_profile(WebSettings & settings);
|
||||
|
||||
static uint8_t flags_;
|
||||
static uint16_t flags_;
|
||||
};
|
||||
|
||||
class WebSettingsService : public StatefulService<WebSettings> {
|
||||
|
||||
@@ -296,10 +296,6 @@ uint8_t WebStatusService::upgradeImportantMessages(std::string & version) {
|
||||
|
||||
version::EMSESP_Version current_version(current_version_s); // get current version
|
||||
|
||||
if (latest_version > current_version && current_version.minor() < latest_version.minor()) {
|
||||
return 0; // if it's just a minor version upgrade return 0
|
||||
}
|
||||
|
||||
if ((current_version.major() <= 3 && current_version.minor() <= 8) && (latest_version.major() == 3 && latest_version.minor() == 9)) {
|
||||
return 1; // if moving from below 3.8.x to 3.9.x return 1
|
||||
}
|
||||
@@ -308,6 +304,10 @@ uint8_t WebStatusService::upgradeImportantMessages(std::string & version) {
|
||||
return 2; // if it's a major version upgrade return 2
|
||||
}
|
||||
|
||||
if (latest_version > current_version && current_version.minor() < latest_version.minor()) {
|
||||
return 0; // if it's just a minor version upgrade return 0
|
||||
}
|
||||
|
||||
return 0; // if it's not a valid version upgrade return 0
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user