mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2025-12-07 16:29:51 +03:00
@@ -10,11 +10,13 @@
|
||||
- Add devices: Greenstar 30Ri boiler, Junkers FW500 thermostat, Buderus BC30 controller
|
||||
- Add program memory info
|
||||
- Add mqtt queue and connection infos
|
||||
- Add min/max setting to customizations
|
||||
- Adapt min/max if ems-value is not in this range
|
||||
- Add heat pump settings for inputs and limits [#600](https://github.com/emsesp/EMS-ESP32/issues/600)
|
||||
- Add hybrid heatpump [#500](https://github.com/emsesp/EMS-ESP32/issues/500)
|
||||
- Add translated tags
|
||||
- Add min/max to customization table [#686](https://github.com/emsesp/EMS-ESP32/issues/686)
|
||||
- Add MD5 check [#637](https://github.com/emsesp/EMS-ESP32/issues/637)
|
||||
- Add more bus-ids [#673](https://github.com/emsesp/EMS-ESP32/issues/673)
|
||||
|
||||
## Fixed
|
||||
|
||||
@@ -26,6 +28,7 @@
|
||||
- RF room temperature sensor are shown as thermostat
|
||||
- render mqtt float json values with trailing zero
|
||||
- removed flash strings
|
||||
- reload page after restart button is pressed
|
||||
|
||||
## **BREAKING CHANGES:**
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"adapter": "react",
|
||||
"baseLocale": "pl",
|
||||
"$schema": "https://unpkg.com/typesafe-i18n@5.14.0/schema/typesafe-i18n.json"
|
||||
"baseLocale": "en",
|
||||
"$schema": "https://unpkg.com/typesafe-i18n@5.15.0/schema/typesafe-i18n.json"
|
||||
}
|
||||
|
||||
1430
interface/package-lock.json
generated
1430
interface/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
@@ -35,7 +35,8 @@ const SingleUpload: FC<SingleUploadProps> = ({ onDrop, onCancel, uploading, prog
|
||||
onDrop,
|
||||
accept: {
|
||||
'application/octet-stream': ['.bin'],
|
||||
'application/json': ['.json']
|
||||
'application/json': ['.json'],
|
||||
'text/plain': ['.md5']
|
||||
},
|
||||
disabled: uploading,
|
||||
multiple: false
|
||||
|
||||
@@ -16,6 +16,7 @@ const useFileUpload = ({ upload }: MediaUploadOptions) => {
|
||||
|
||||
const { enqueueSnackbar } = useSnackbar();
|
||||
const [uploading, setUploading] = useState<boolean>(false);
|
||||
const [md5, setMd5] = useState<string>('');
|
||||
const [uploadProgress, setUploadProgress] = useState<AxiosProgressEvent>();
|
||||
const [uploadCancelToken, setUploadCancelToken] = useState<CancelTokenSource>();
|
||||
|
||||
@@ -23,6 +24,7 @@ const useFileUpload = ({ upload }: MediaUploadOptions) => {
|
||||
setUploading(false);
|
||||
setUploadProgress(undefined);
|
||||
setUploadCancelToken(undefined);
|
||||
setMd5('');
|
||||
};
|
||||
|
||||
const cancelUpload = useCallback(() => {
|
||||
@@ -41,12 +43,17 @@ const useFileUpload = ({ upload }: MediaUploadOptions) => {
|
||||
const cancelToken = axios.CancelToken.source();
|
||||
setUploadCancelToken(cancelToken);
|
||||
setUploading(true);
|
||||
await upload(images[0], {
|
||||
const response = await upload(images[0], {
|
||||
onUploadProgress: setUploadProgress,
|
||||
cancelToken: cancelToken.token
|
||||
});
|
||||
resetUploadingStates();
|
||||
enqueueSnackbar(LL.UPLOAD() + ' ' + LL.SUCCESSFUL(), { variant: 'success' });
|
||||
if (response.status === 200) {
|
||||
enqueueSnackbar(LL.UPLOAD() + ' ' + LL.SUCCESSFUL(), { variant: 'success' });
|
||||
} else if (response.status === 201) {
|
||||
setMd5((String)(response.data));
|
||||
enqueueSnackbar(LL.UPLOAD() + ' MD5 ' + LL.SUCCESSFUL(), { variant: 'success' });
|
||||
}
|
||||
} catch (error) {
|
||||
if (axios.isCancel(error)) {
|
||||
enqueueSnackbar(LL.UPLOAD() + ' ' + LL.ABORTED(), { variant: 'warning' });
|
||||
@@ -57,7 +64,7 @@ const useFileUpload = ({ upload }: MediaUploadOptions) => {
|
||||
}
|
||||
};
|
||||
|
||||
return [uploadFile, cancelUpload, uploading, uploadProgress] as const;
|
||||
return [uploadFile, cancelUpload, uploading, uploadProgress, md5] as const;
|
||||
};
|
||||
|
||||
export default useFileUpload;
|
||||
|
||||
@@ -44,7 +44,7 @@ const dnsServers = ({ dns_ip_1, dns_ip_2 }: NetworkStatus) => {
|
||||
if (!dns_ip_1) {
|
||||
return 'none';
|
||||
}
|
||||
return dns_ip_1 + (dns_ip_2 === '0.0.0.0' ? '' : ',' + dns_ip_2);
|
||||
return dns_ip_1 + (!dns_ip_2 || dns_ip_2 === '0.0.0.0' ? '' : ',' + dns_ip_2);
|
||||
};
|
||||
|
||||
const IPs = (status: NetworkStatus) => {
|
||||
|
||||
@@ -4,6 +4,7 @@ import { AxiosPromise } from 'axios';
|
||||
import { Typography, Button, Box } from '@mui/material';
|
||||
|
||||
import { FileUploadConfig } from '../../api/endpoints';
|
||||
|
||||
import { SingleUpload, useFileUpload } from '../../components';
|
||||
|
||||
import DownloadIcon from '@mui/icons-material/GetApp';
|
||||
@@ -21,7 +22,8 @@ interface UploadFileProps {
|
||||
}
|
||||
|
||||
const GeneralFileUpload: FC<UploadFileProps> = ({ uploadGeneralFile }) => {
|
||||
const [uploadFile, cancelUpload, uploading, uploadProgress] = useFileUpload({ upload: uploadGeneralFile });
|
||||
|
||||
const [uploadFile, cancelUpload, uploading, uploadProgress, md5] = useFileUpload({ upload: uploadGeneralFile });
|
||||
|
||||
const { enqueueSnackbar } = useSnackbar();
|
||||
|
||||
@@ -80,6 +82,11 @@ const GeneralFileUpload: FC<UploadFileProps> = ({ uploadGeneralFile }) => {
|
||||
</Box>
|
||||
</>
|
||||
)}
|
||||
{md5 !== '' && (
|
||||
<Box mb={2}>
|
||||
<Typography variant="body2">{'MD5: ' + md5}</Typography>
|
||||
</Box>
|
||||
)}
|
||||
<SingleUpload onDrop={uploadFile} onCancel={cancelUpload} uploading={uploading} progress={uploadProgress} />
|
||||
|
||||
{!uploading && (
|
||||
|
||||
@@ -39,6 +39,7 @@ import { extractErrorMessage, useRest } from '../../utils';
|
||||
import { AuthenticatedContext } from '../../contexts/authentication';
|
||||
|
||||
import axios from 'axios';
|
||||
import RestartMonitor from './RestartMonitor';
|
||||
|
||||
import { useI18nContext } from '../../i18n/i18n-react';
|
||||
|
||||
@@ -52,6 +53,7 @@ function formatNumber(num: number) {
|
||||
|
||||
const SystemStatusForm: FC = () => {
|
||||
const { LL } = useI18nContext();
|
||||
const [restarting, setRestarting] = useState<boolean>();
|
||||
|
||||
const { loadData, data, errorMessage } = useRest<SystemStatus>({ read: SystemApi.readSystemStatus });
|
||||
|
||||
@@ -69,7 +71,8 @@ const SystemStatusForm: FC = () => {
|
||||
setLatestVersion({
|
||||
version: response.data.name,
|
||||
url: response.data.assets[1].browser_download_url,
|
||||
changelog: response.data.html_url
|
||||
changelog: response.data.assets[0].browser_download_url
|
||||
|
||||
});
|
||||
});
|
||||
axios.get(VERSIONCHECK_DEV_ENDPOINT).then((response) => {
|
||||
@@ -86,6 +89,7 @@ const SystemStatusForm: FC = () => {
|
||||
try {
|
||||
await SystemApi.restart();
|
||||
enqueueSnackbar(LL.APPLICATION_RESTARTING(), { variant: 'info' });
|
||||
setRestarting(true);
|
||||
} catch (error) {
|
||||
enqueueSnackbar(extractErrorMessage(error, LL.PROBLEM_LOADING()), { variant: 'error' });
|
||||
} finally {
|
||||
@@ -103,6 +107,7 @@ const SystemStatusForm: FC = () => {
|
||||
startIcon={<CancelIcon />}
|
||||
variant="outlined"
|
||||
onClick={() => setConfirmRestart(false)}
|
||||
disabled={processing}
|
||||
color="secondary"
|
||||
>
|
||||
{LL.CANCEL()}
|
||||
@@ -199,6 +204,7 @@ const SystemStatusForm: FC = () => {
|
||||
startIcon={<CancelIcon />}
|
||||
variant="outlined"
|
||||
onClick={() => setConfirmFactoryReset(false)}
|
||||
disabled={processing}
|
||||
color="secondary"
|
||||
>
|
||||
{LL.CANCEL()}
|
||||
@@ -302,9 +308,7 @@ const SystemStatusForm: FC = () => {
|
||||
</ListItemAvatar>
|
||||
<ListItemText
|
||||
primary={LL.FLASH()}
|
||||
secondary={
|
||||
formatNumber(data.flash_chip_size) + ' KB / ' + (data.flash_chip_speed / 1000000).toFixed(0) + ' MHz'
|
||||
}
|
||||
secondary={formatNumber(data.flash_chip_size) + ' KB / ' + (data.flash_chip_speed / 1000000).toFixed(0) + ' MHz'}
|
||||
/>
|
||||
</ListItem>
|
||||
<Divider variant="inset" component="li" />
|
||||
@@ -373,7 +377,7 @@ const SystemStatusForm: FC = () => {
|
||||
|
||||
return (
|
||||
<SectionContent title={LL.STATUS_OF(LL.SYSTEM(1))} titleGutter>
|
||||
{content()}
|
||||
{restarting ? <RestartMonitor /> : content()}
|
||||
</SectionContent>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -16,7 +16,9 @@ const UploadFileForm: FC = () => {
|
||||
|
||||
const uploadFile = useRef(async (file: File, config?: FileUploadConfig) => {
|
||||
const response = await SystemApi.uploadFile(file, config);
|
||||
setRestarting(true);
|
||||
if (response.status === 200) {
|
||||
setRestarting(true);
|
||||
}
|
||||
return response;
|
||||
});
|
||||
|
||||
|
||||
@@ -105,7 +105,7 @@ const de: Translation = {
|
||||
BOARD_PROFILE: 'Platinenprofil',
|
||||
BUTTON: 'Taste',
|
||||
TEMPERATURE: 'Temperatur',
|
||||
PHY_TYPE: 'Eth PHY Type',
|
||||
PHY_TYPE: 'Eth PHY Typ',
|
||||
DISABLED: 'deaktiviert',
|
||||
TX_MODE: 'Tx Mode',
|
||||
GENERAL_OPTIONS: 'Allgemeine Optionen',
|
||||
@@ -177,7 +177,7 @@ const de: Translation = {
|
||||
SYSTEM_VERSION_RUNNING: 'Sie verwenden die Version',
|
||||
SYSTEM_APPLY_FIRMWARE: 'um die neue Firmware anzuwenden',
|
||||
CLOSE: 'Schließen',
|
||||
USE: 'Verwenden',
|
||||
USE: 'Verwenden Sie',
|
||||
FACTORY_RESET: 'Werkseinstellung',
|
||||
SYSTEM_FACTORY_TEXT: 'EMS-ESP wurde auf Werkseinstellung gesetzt und startet als Zugangspunkt neu',
|
||||
SYSTEM_FACTORY_TEXT_DIALOG: 'Sind Sie sicher alle Einstellungen auf Werkseinstellung zu setzen?',
|
||||
@@ -191,7 +191,7 @@ const de: Translation = {
|
||||
PLATFORM: 'Platform (Platform / SDK)',
|
||||
UPTIME: 'System Betriebszeit',
|
||||
CPU_FREQ: 'CPU Frequenz',
|
||||
HEAP: 'RAM freier Speicher (Gesamt / max. Block)',
|
||||
HEAP: 'freier RAM Speicher (Gesamt / max. Block)',
|
||||
PSRAM: 'PSRAM (Größe / Frei)',
|
||||
FLASH: 'Flash Speicher (Größe / Geschwindigkeit)',
|
||||
APPSIZE: 'Programm (Genutzt / Frei)',
|
||||
@@ -202,7 +202,7 @@ const de: Translation = {
|
||||
DOWNLOAD_CUSTOMIZATION_TEXT: 'Herunterladen der individuellen Entitätsanpassungen',
|
||||
DOWNLOAD_SETTINGS_TEXT:
|
||||
'Herunterladen der Anwendungseinstellungen. Vorsicht beim Teilen der Einstellungen, da sie Passwörter und andere sensitive Einstellungen enthalten',
|
||||
UPLOAD_TEXT: 'Hochladen von neuer Firmware (.bin), Geräte- oder Entitätseinstellungen (.json)',
|
||||
UPLOAD_TEXT: 'Hochladen von neuer Firmware (.bin), Geräte- oder Entitätseinstellungen (.json), zur optionalen Validitätsprüfung zuerst die (.md5) Datei hochladen',
|
||||
UPLOADING: 'Hochladen',
|
||||
UPLOAD_DROP_TEXT: 'Klicken Sie hier, oder ziehen eine Datei hierher',
|
||||
ERROR: 'Unerwarteter Fehler, bitter versuchen Sie es erneut',
|
||||
@@ -259,9 +259,9 @@ const de: Translation = {
|
||||
AP_PROVIDE_TEXT_3: 'Niemals',
|
||||
AP_PREFERRED_CHANNEL: 'Bevorzugter Kanal',
|
||||
AP_HIDE_SSID: 'Verstecke SSID',
|
||||
AP_CLIENTS: 'AP Clients',
|
||||
AP_MAX_CLIENTS: 'Max Clients',
|
||||
AP_LOCAL_IP: 'Local IP',
|
||||
AP_CLIENTS: 'AP-Klienten',
|
||||
AP_MAX_CLIENTS: 'Max Anzahl AP-Klienten',
|
||||
AP_LOCAL_IP: 'Lokale IP',
|
||||
NETWORK_SCAN: 'Suche nach WiFi Netzwerken',
|
||||
IDLE: 'Leerlauf',
|
||||
LOST: 'Verloren',
|
||||
@@ -276,11 +276,11 @@ const de: Translation = {
|
||||
NETWORK_LOW_BAND: 'Verwende niedrige WiFi Bandbreite',
|
||||
NETWORK_USE_DNS: 'Aktiviere mDNS Service',
|
||||
NETWORK_ENABLE_IPV6: 'Aktiviere IPv6 Unterstützung',
|
||||
NETWORK_FIXED_IP: 'Feste IP Addresse',
|
||||
NETWORK_GATEWAY: 'Getaway',
|
||||
NETWORK_SUBNET: 'Subnet Mask',
|
||||
NETWORK_DNS: 'DNS Servers',
|
||||
ADDRESS_OF: '{0} Address',
|
||||
NETWORK_FIXED_IP: 'Feste IP Adresse',
|
||||
NETWORK_GATEWAY: 'Gateway',
|
||||
NETWORK_SUBNET: 'Subnetz Maske',
|
||||
NETWORK_DNS: 'DNS Server',
|
||||
ADDRESS_OF: '{0} Adresse',
|
||||
ADMIN: 'Administrator',
|
||||
GUEST: 'Gast',
|
||||
NEW: 'Neuer',
|
||||
|
||||
@@ -19,8 +19,8 @@ const en: BaseTranslation = {
|
||||
UPLOAD_SUCCESSFUL: 'Upload successful',
|
||||
DOWNLOAD_SUCCESSFUL: 'Download successful',
|
||||
INVALID_LOGIN: 'Invalid login details',
|
||||
NETWORK: 'Network',
|
||||
SECURITY: 'Security',
|
||||
NETWORK: '{{Network}}',
|
||||
SECURITY: '{{Security}}',
|
||||
ONOFF_CAP: 'ON/OFF',
|
||||
ONOFF: 'on/off',
|
||||
TYPE: 'Type',
|
||||
@@ -31,7 +31,7 @@ const en: BaseTranslation = {
|
||||
DEVICE_DETAILS: 'Device Details',
|
||||
BRAND: 'Brand',
|
||||
ENTITY_NAME: 'Entity Name',
|
||||
VALUE: 'Value',
|
||||
VALUE: '{{Value}}',
|
||||
SHOW_FAV: 'only show favorites',
|
||||
DEVICE_SENSOR_DATA: 'Device and Sensor Data',
|
||||
DEVICES_SENSORS: 'Devices & Sensors',
|
||||
@@ -39,7 +39,7 @@ const en: BaseTranslation = {
|
||||
RUN_COMMAND: 'Call Command',
|
||||
CHANGE_VALUE: 'Change Value',
|
||||
CANCEL: 'Cancel',
|
||||
RESET: 'Reset',
|
||||
RESET: '{{Reset}}',
|
||||
SEND: 'Send',
|
||||
SAVE: 'Save',
|
||||
REMOVE: 'Remove',
|
||||
@@ -66,7 +66,7 @@ const en: BaseTranslation = {
|
||||
EMS_BUS_WARNING:
|
||||
'EMS bus disconnected. If this warning still persists after a few seconds please check settings and board profile',
|
||||
EMS_BUS_SCANNING: 'Scanning for EMS devices...',
|
||||
CONNECTED: 'Connected',
|
||||
CONNECTED: '{{Connected}}',
|
||||
TX_ISSUES: 'Tx issues - try a different Tx Mode',
|
||||
DISCONNECTED: 'Disconnected',
|
||||
EMS_SCAN: 'Are you sure you want to initiate a full device scan of the EMS bus?',
|
||||
@@ -171,7 +171,7 @@ const en: BaseTranslation = {
|
||||
ABORTED: 'aborted',
|
||||
FAILED: 'failed',
|
||||
SUCCESSFUL: 'successful',
|
||||
SYSTEM: 'System',
|
||||
SYSTEM: '{{System}}',
|
||||
LOG_OF: '{0} Log',
|
||||
STATUS_OF: '{0} Status',
|
||||
UPLOAD_DOWNLOAD: 'Upload/Download',
|
||||
@@ -182,7 +182,7 @@ const en: BaseTranslation = {
|
||||
FACTORY_RESET: 'Factory Reset',
|
||||
SYSTEM_FACTORY_TEXT: 'Device has been factory reset and will now restart',
|
||||
SYSTEM_FACTORY_TEXT_DIALOG: 'Are you sure you want to reset the device to its factory defaults?',
|
||||
VERSION_CHECK: 'Version Check',
|
||||
VERSION_CHECK: '{{Version Check}}',
|
||||
THE_LATEST: 'The latest',
|
||||
OFFICIAL: 'official',
|
||||
DEVELOPMENT: 'development',
|
||||
@@ -203,20 +203,20 @@ const en: BaseTranslation = {
|
||||
DOWNLOAD_CUSTOMIZATION_TEXT: 'Download the entity customizations',
|
||||
DOWNLOAD_SETTINGS_TEXT:
|
||||
'Download the application settings. Be careful when sharing your settings as this file contains passwords and other sensitive system information',
|
||||
UPLOAD_TEXT: 'Upload a new firmware (.bin) file, settings or customizations (.json) file below',
|
||||
UPLOAD_TEXT: 'Upload a new firmware (.bin) file, settings or customizations (.json) file below, for optional validation upload (.md5) first',
|
||||
UPLOADING: 'Uploading',
|
||||
UPLOAD_DROP_TEXT: 'Drop file or click here',
|
||||
ERROR: 'Unexpected Error, please try again',
|
||||
TIME_SET: 'Time set',
|
||||
MANAGE_USERS: 'Manage Users',
|
||||
IS_ADMIN: 'is Admin',
|
||||
IS_ADMIN: '{{is Admin}}',
|
||||
USER_WARNING: 'You must have at least one admin user configured',
|
||||
ADD: 'Add',
|
||||
ADD: '{{Add}}',
|
||||
ACCESS_TOKEN_FOR: 'Access Token for',
|
||||
ACCESS_TOKEN_TEXT:
|
||||
'The token below is used with REST API calls that require authorization. It can be passed either as a Bearer token in the Authorization header or in the access_token URL query parameter.',
|
||||
GENERATING_TOKEN: 'Generating token',
|
||||
USER: 'User',
|
||||
USER: '{{User}}',
|
||||
MODIFY: 'Modify',
|
||||
SU_TEXT:
|
||||
'The su (super user) password is used to sign authentication tokens and also enable admin privileges within the Console.',
|
||||
@@ -246,14 +246,14 @@ const en: BaseTranslation = {
|
||||
INACTIVE: 'Inactive',
|
||||
ACTIVE: 'Active',
|
||||
UNKNOWN: 'Unknown',
|
||||
SET_TIME: 'Set Time',
|
||||
SET_TIME: '{{Set Time}}',
|
||||
SET_TIME_TEXT: 'Enter local date and time below to set the time',
|
||||
LOCAL_TIME: 'Local Time',
|
||||
UTC_TIME: 'UTC Time',
|
||||
ENABLE_NTP: 'Enable NTP',
|
||||
NTP_SERVER: 'NTP Server',
|
||||
TIME_ZONE: 'Time Zone',
|
||||
ACCESS_POINT: 'Access Point',
|
||||
ACCESS_POINT: '{{Access Point}}',
|
||||
AP_PROVIDE: 'Enable Access Point',
|
||||
AP_PROVIDE_TEXT_1: 'always',
|
||||
AP_PROVIDE_TEXT_2: 'when WiFi is disconnected',
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -14,10 +14,10 @@ const localeTranslationLoaders = {
|
||||
se: () => import('./se'),
|
||||
}
|
||||
|
||||
const updateDictionary = (locale: Locales, dictionary: Partial<Translations>) =>
|
||||
const updateDictionary = (locale: Locales, dictionary: Partial<Translations>): Translations =>
|
||||
loadedLocales[locale] = { ...loadedLocales[locale], ...dictionary }
|
||||
|
||||
export const importLocaleAsync = async (locale: Locales) =>
|
||||
export const importLocaleAsync = async (locale: Locales): Promise<Translations> =>
|
||||
(await localeTranslationLoaders[locale]()).default as unknown as Translations
|
||||
|
||||
export const loadLocaleAsync = async (locale: Locales): Promise<void> => {
|
||||
|
||||
@@ -3,10 +3,11 @@
|
||||
|
||||
import { i18n as initI18n, i18nObject as initI18nObject, i18nString as initI18nString } from 'typesafe-i18n'
|
||||
import type { LocaleDetector } from 'typesafe-i18n/detectors'
|
||||
import type { LocaleTranslationFunctions, TranslateByString } from 'typesafe-i18n'
|
||||
import { detectLocale as detectLocaleFn } from 'typesafe-i18n/detectors'
|
||||
import type { Formatters, Locales, Translations, TranslationFunctions } from './i18n-types'
|
||||
|
||||
export const baseLocale: Locales = 'pl'
|
||||
export const baseLocale: Locales = 'en'
|
||||
|
||||
export const locales: Locales[] = [
|
||||
'de',
|
||||
@@ -17,21 +18,22 @@ export const locales: Locales[] = [
|
||||
'se'
|
||||
]
|
||||
|
||||
export const isLocale = (locale: string) => locales.includes(locale as Locales)
|
||||
export const isLocale = (locale: string): locale is Locales => locales.includes(locale as Locales)
|
||||
|
||||
export const loadedLocales = {} as Record<Locales, Translations>
|
||||
export const loadedLocales: Record<Locales, Translations> = {} as Record<Locales, Translations>
|
||||
|
||||
export const loadedFormatters = {} as Record<Locales, Formatters>
|
||||
export const loadedFormatters: Record<Locales, Formatters> = {} as Record<Locales, Formatters>
|
||||
|
||||
export const i18nString = (locale: Locales) => initI18nString<Locales, Formatters>(locale, loadedFormatters[locale])
|
||||
export const i18nString = (locale: Locales): TranslateByString => initI18nString<Locales, Formatters>(locale, loadedFormatters[locale])
|
||||
|
||||
export const i18nObject = (locale: Locales) =>
|
||||
export const i18nObject = (locale: Locales): TranslationFunctions =>
|
||||
initI18nObject<Locales, Translations, TranslationFunctions, Formatters>(
|
||||
locale,
|
||||
loadedLocales[locale],
|
||||
loadedFormatters[locale]
|
||||
)
|
||||
|
||||
export const i18n = () => initI18n<Locales, Translations, TranslationFunctions, Formatters>(loadedLocales, loadedFormatters)
|
||||
export const i18n = (): LocaleTranslationFunctions<Locales, Translations, TranslationFunctions> =>
|
||||
initI18n<Locales, Translations, TranslationFunctions, Formatters>(loadedLocales, loadedFormatters)
|
||||
|
||||
export const detectLocale = (...detectors: LocaleDetector[]) => detectLocaleFn<Locales>(baseLocale, locales, ...detectors)
|
||||
export const detectLocale = (...detectors: LocaleDetector[]): Locales => detectLocaleFn<Locales>(baseLocale, locales, ...detectors)
|
||||
|
||||
@@ -311,12 +311,17 @@ const SettingsApplication: FC = () => {
|
||||
margin="normal"
|
||||
select
|
||||
>
|
||||
<MenuItem value={0x0a}>Terminal (0x0A)</MenuItem>
|
||||
<MenuItem value={0x0b}>Service Key (0x0B)</MenuItem>
|
||||
<MenuItem value={0x0d}>Modem (0x0D)</MenuItem>
|
||||
<MenuItem value={0x0a}>Terminal (0x0A)</MenuItem>
|
||||
<MenuItem value={0x0e}>Converter (0x0E)</MenuItem>
|
||||
<MenuItem value={0x0f}>Time Module (0x0F)</MenuItem>
|
||||
<MenuItem value={0x12}>Alarm Module (0x12)</MenuItem>
|
||||
<MenuItem value={0x48}>Gateway 1 (0x48)</MenuItem>
|
||||
<MenuItem value={0x49}>Gateway 2 (0x49)</MenuItem>
|
||||
<MenuItem value={0x4A}>Gateway 3 (0x4A)</MenuItem>
|
||||
<MenuItem value={0x4B}>Gateway 4 (0x4B)</MenuItem>
|
||||
<MenuItem value={0x4C}>Gateway 5 (0x4C)</MenuItem>
|
||||
<MenuItem value={0x4D}>Gateway 7 (0x4D)</MenuItem>
|
||||
</ValidatedTextField>
|
||||
</Grid>
|
||||
</Grid>
|
||||
@@ -344,9 +349,7 @@ const SettingsApplication: FC = () => {
|
||||
<MenuItem value="nl">Nederlands (NL)</MenuItem>
|
||||
<MenuItem value="se">Svenska (SE)</MenuItem>
|
||||
<MenuItem value="pl">Polski (PL)</MenuItem>
|
||||
<MenuItem disabled value="no">
|
||||
Norsk (NO)
|
||||
</MenuItem>
|
||||
<MenuItem value="no">Norsk (NO)</MenuItem>
|
||||
</ValidatedTextField>
|
||||
</Box>
|
||||
{data.led_gpio !== 0 && (
|
||||
|
||||
@@ -66,7 +66,7 @@ const SettingsCustomization: FC = () => {
|
||||
|
||||
const entities_theme = useTheme({
|
||||
Table: `
|
||||
--data-table-library_grid-template-columns: 120px repeat(1, minmax(0, 1fr)) 120px;
|
||||
--data-table-library_grid-template-columns: 120px repeat(1, minmax(80px, 1fr)) 40px 40px 120px;
|
||||
`,
|
||||
BaseRow: `
|
||||
font-size: 14px;
|
||||
@@ -75,6 +75,12 @@ const SettingsCustomization: FC = () => {
|
||||
}
|
||||
`,
|
||||
BaseCell: `
|
||||
&:nth-of-type(3) {
|
||||
text-align: right;
|
||||
}
|
||||
&:nth-of-type(4) {
|
||||
text-align: right;
|
||||
}
|
||||
&:last-of-type {
|
||||
text-align: right;
|
||||
}
|
||||
@@ -122,6 +128,12 @@ const SettingsCustomization: FC = () => {
|
||||
&:nth-of-type(2) {
|
||||
padding: 8px;
|
||||
}
|
||||
&:nth-of-type(3) {
|
||||
padding-right: 4px;
|
||||
}
|
||||
&:nth-of-type(4) {
|
||||
padding-right: 4px;
|
||||
}
|
||||
&:last-of-type {
|
||||
padding-right: 8px;
|
||||
}
|
||||
@@ -446,6 +458,8 @@ const SettingsCustomization: FC = () => {
|
||||
{LL.NAME()}
|
||||
</Button>
|
||||
</HeaderCell>
|
||||
<HeaderCell stiff>min</HeaderCell>
|
||||
<HeaderCell stiff>max</HeaderCell>
|
||||
<HeaderCell resize>{LL.VALUE(0)}</HeaderCell>
|
||||
</HeaderRow>
|
||||
</Header>
|
||||
@@ -497,6 +511,8 @@ const SettingsCustomization: FC = () => {
|
||||
</ToggleButtonGroup>
|
||||
</Cell>
|
||||
<Cell>{formatName(de)}</Cell>
|
||||
<Cell>{!(de.m & DeviceEntityMask.DV_READONLY) && (formatValue(de.mi))}</Cell>
|
||||
<Cell>{!(de.m & DeviceEntityMask.DV_READONLY) && (formatValue(de.ma))}</Cell>
|
||||
<Cell>{formatValue(de.v)}</Cell>
|
||||
</Row>
|
||||
))}
|
||||
@@ -626,7 +642,7 @@ const SettingsCustomization: FC = () => {
|
||||
onChange={updateValue(setDeviceEntity)}
|
||||
/>
|
||||
</Grid>
|
||||
{typeof de.v === 'number' && de.w && (
|
||||
{typeof de.v === 'number' && de.w && !(de.m & DeviceEntityMask.DV_READONLY) && (
|
||||
<>
|
||||
<Grid item>
|
||||
<TextField
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
export const extractErrorMessage = (error: any, defaultMessage: string) => {
|
||||
if (error.request) {
|
||||
return defaultMessage + ' (' + error.request.statusText + ')';
|
||||
return defaultMessage + ' (' + error.request.status + ': ' + error.request.statusText + ')';
|
||||
} else if (error instanceof Error) {
|
||||
return defaultMessage + ' (' + error.message + ')';
|
||||
}
|
||||
|
||||
@@ -80,11 +80,11 @@ AsyncMqttClient * MqttSettingsService::getMqttClient() {
|
||||
}
|
||||
|
||||
void MqttSettingsService::onMqttConnect(bool sessionPresent) {
|
||||
// emsesp::EMSESP::logger().info(F("Connected to MQTT, %s"), (sessionPresent) ? F("with persistent session") : F("without persistent session"));
|
||||
// emsesp::EMSESP::logger().info("Connected to MQTT, %s", (sessionPresent) ? ("with persistent session") : ("without persistent session"));
|
||||
}
|
||||
|
||||
void MqttSettingsService::onMqttDisconnect(AsyncMqttClientDisconnectReason reason) {
|
||||
// emsesp::EMSESP::logger().info(F("Disconnected from MQTT reason: %d"), (uint8_t)reason);
|
||||
// emsesp::EMSESP::logger().info("Disconnected from MQTT reason: %d", (uint8_t)reason);
|
||||
_disconnectReason = reason;
|
||||
_disconnectedAt = uuid::get_uptime();
|
||||
}
|
||||
@@ -104,14 +104,14 @@ void MqttSettingsService::WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info) {
|
||||
case ARDUINO_EVENT_ETH_GOT_IP6:
|
||||
case ARDUINO_EVENT_WIFI_STA_GOT_IP6:
|
||||
if (_state.enabled) {
|
||||
// emsesp::EMSESP::logger().info(F("IPv4 Network connection found, starting MQTT client"));
|
||||
// emsesp::EMSESP::logger().info("IPv4 Network connection found, starting MQTT client");
|
||||
onConfigUpdated();
|
||||
}
|
||||
break;
|
||||
case ARDUINO_EVENT_WIFI_STA_DISCONNECTED:
|
||||
case ARDUINO_EVENT_ETH_DISCONNECTED:
|
||||
if (_state.enabled) {
|
||||
// emsesp::EMSESP::logger().info(F("Network connection dropped, stopping MQTT client"));
|
||||
// emsesp::EMSESP::logger().info("Network connection dropped, stopping MQTT client");
|
||||
_mqttClient.disconnect();
|
||||
// onConfigUpdated();
|
||||
}
|
||||
@@ -127,7 +127,7 @@ void MqttSettingsService::configureMqtt() {
|
||||
_mqttClient.disconnect();
|
||||
// only connect if WiFi is connected and MQTT is enabled
|
||||
if (_state.enabled && emsesp::EMSESP::system_.network_connected()) {
|
||||
// emsesp::EMSESP::logger().info(F("Configuring Mqtt client"));
|
||||
// emsesp::EMSESP::logger().info("Configuring Mqtt client");
|
||||
_mqttClient.setServer(retainCstr(_state.host.c_str(), &_retainedHost), _state.port);
|
||||
if (_state.username.length() > 0) {
|
||||
_mqttClient.setCredentials(retainCstr(_state.username.c_str(), &_retainedUsername),
|
||||
@@ -141,7 +141,7 @@ void MqttSettingsService::configureMqtt() {
|
||||
_mqttClient.setMaxTopicLength(_state.maxTopicLength);
|
||||
_mqttClient.connect();
|
||||
// } else {
|
||||
// emsesp::EMSESP::logger().info(F("Error configuring Mqtt client"));
|
||||
// emsesp::EMSESP::logger().info("Error configuring Mqtt client");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -28,14 +28,14 @@ void NTPSettingsService::WiFiEvent(WiFiEvent_t event) {
|
||||
switch (event) {
|
||||
case ARDUINO_EVENT_WIFI_STA_DISCONNECTED:
|
||||
case ARDUINO_EVENT_ETH_DISCONNECTED:
|
||||
emsesp::EMSESP::logger().info(F("WiFi connection dropped, stopping NTP"));
|
||||
emsesp::EMSESP::logger().info("WiFi connection dropped, stopping NTP");
|
||||
connected_ = false;
|
||||
configureNTP();
|
||||
break;
|
||||
|
||||
case ARDUINO_EVENT_WIFI_STA_GOT_IP:
|
||||
case ARDUINO_EVENT_ETH_GOT_IP:
|
||||
// emsesp::EMSESP::logger().info(F("Got IP address, starting NTP synchronization"));
|
||||
// emsesp::EMSESP::logger().info("Got IP address, starting NTP synchronization");
|
||||
connected_ = true;
|
||||
configureNTP();
|
||||
break;
|
||||
@@ -49,7 +49,7 @@ void NTPSettingsService::WiFiEvent(WiFiEvent_t event) {
|
||||
void NTPSettingsService::configureNTP() {
|
||||
emsesp::EMSESP::system_.ntp_connected(false);
|
||||
if (connected_ && _state.enabled) {
|
||||
emsesp::EMSESP::logger().info(F("Starting NTP"));
|
||||
emsesp::EMSESP::logger().info("Starting NTP");
|
||||
sntp_set_sync_interval(3600000); // onehour
|
||||
sntp_set_time_sync_notification_cb(ntp_received);
|
||||
configTzTime(_state.tzFormat.c_str(), _state.server.c_str());
|
||||
@@ -81,6 +81,6 @@ void NTPSettingsService::configureTime(AsyncWebServerRequest * request, JsonVari
|
||||
}
|
||||
|
||||
void NTPSettingsService::ntp_received(struct timeval * tv) {
|
||||
// emsesp::EMSESP::logger().info(F("NTP sync to %d sec"), tv->tv_sec);
|
||||
// emsesp::EMSESP::logger().info("NTP sync to %d sec", tv->tv_sec);
|
||||
emsesp::EMSESP::system_.ntp_connected(true);
|
||||
}
|
||||
|
||||
@@ -47,15 +47,15 @@ void OTASettingsService::configureArduinoOTA() {
|
||||
#if defined(EMSESP_USE_SERIAL)
|
||||
Serial.printf("Error[%u]: ", error);
|
||||
if (error == OTA_AUTH_ERROR)
|
||||
Serial.println(F("Auth Failed"));
|
||||
Serial.println("Auth Failed");
|
||||
else if (error == OTA_BEGIN_ERROR)
|
||||
Serial.println(F("Begin Failed"));
|
||||
Serial.println("Begin Failed");
|
||||
else if (error == OTA_CONNECT_ERROR)
|
||||
Serial.println(F("Connect Failed"));
|
||||
Serial.println("Connect Failed");
|
||||
else if (error == OTA_RECEIVE_ERROR)
|
||||
Serial.println(F("Receive Failed"));
|
||||
Serial.println("Receive Failed");
|
||||
else if (error == OTA_END_ERROR)
|
||||
Serial.println(F("End Failed"));
|
||||
Serial.println("End Failed");
|
||||
#endif
|
||||
});
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
using namespace std::placeholders; // for `_1` etc
|
||||
|
||||
static bool is_firmware = false;
|
||||
static char md5[33] = "\0";
|
||||
|
||||
UploadFileService::UploadFileService(AsyncWebServer * server, SecurityManager * securityManager)
|
||||
: _securityManager(securityManager) {
|
||||
@@ -33,45 +34,53 @@ void UploadFileService::handleUpload(AsyncWebServerRequest * request, const Stri
|
||||
Serial.println();
|
||||
#endif
|
||||
|
||||
is_firmware = false;
|
||||
if ((extension == "bin") && (fsize > 1500000)) {
|
||||
is_firmware = true;
|
||||
} else if (extension == "json") {
|
||||
is_firmware = false;
|
||||
md5[0] = '\0'; // clear md5
|
||||
} else if (extension == "md5") {
|
||||
if (len == 32) {
|
||||
memcpy(md5, data, 32);
|
||||
md5[32] = '\0';
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
is_firmware = false;
|
||||
md5[0] = '\0';
|
||||
return; // not support file type
|
||||
}
|
||||
|
||||
if (is_firmware) {
|
||||
// Check firmware header, 0xE9 magic offset 0 indicates esp bin, chip offset 12: esp32:0, S2:2, C3:5
|
||||
#if CONFIG_IDF_TARGET_ESP32 // ESP32/PICO-D4
|
||||
bool isC3 = (fname.find("C3") != std::string::npos);
|
||||
bool isS2 = (fname.find("S2") != std::string::npos);
|
||||
if (isC3 || isS2 || (len > 12 && (data[0] != 0xE9 || data[12] != 0))) {
|
||||
if (len > 12 && (data[0] != 0xE9 || data[12] != 0)) {
|
||||
handleError(request, 503); // service unavailable
|
||||
return;
|
||||
}
|
||||
#elif CONFIG_IDF_TARGET_ESP32S2
|
||||
bool isS2 = (fname.find("S2") != std::string::npos);
|
||||
if (!isS2 || (len > 12 && (data[0] != 0xE9 || data[12] != 2))) {
|
||||
if (len > 12 && (data[0] != 0xE9 || data[12] != 2)) {
|
||||
handleError(request, 503); // service unavailable
|
||||
return;
|
||||
}
|
||||
#elif CONFIG_IDF_TARGET_ESP32C3
|
||||
bool isC3 = (fname.find("C3") != std::string::npos);
|
||||
if (!isC3 || (len > 12 && (data[0] != 0xE9 || data[12] != 5))) {
|
||||
if (len > 12 && (data[0] != 0xE9 || data[12] != 5)) {
|
||||
handleError(request, 503); // service unavailable
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
// it's firmware - initialize the ArduinoOTA updater
|
||||
if (Update.begin(fsize)) {
|
||||
if (strlen(md5) == 32) {
|
||||
Update.setMD5(md5);
|
||||
md5[0] = '\0';
|
||||
}
|
||||
request->onDisconnect(UploadFileService::handleEarlyDisconnect); // success, let's make sure we end the update if the client hangs up
|
||||
} else {
|
||||
#if defined(EMSESP_USE_SERIAL)
|
||||
Update.printError(Serial);
|
||||
#endif
|
||||
handleError(request, 507); // failed to begin, send an error response Insufficient Storage
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// its a normal file, open a new temp file to write the contents too
|
||||
@@ -83,7 +92,6 @@ void UploadFileService::handleUpload(AsyncWebServerRequest * request, const Stri
|
||||
if (len) {
|
||||
request->_tempFile.write(data, len); // stream the incoming chunk to the opened file
|
||||
}
|
||||
|
||||
} else {
|
||||
// if we haven't delt with an error, continue with the firmware update
|
||||
if (!request->_tempObject) {
|
||||
@@ -123,6 +131,11 @@ void UploadFileService::uploadComplete(AsyncWebServerRequest * request) {
|
||||
request->send(response);
|
||||
return;
|
||||
}
|
||||
if (strlen(md5) == 32) {
|
||||
AsyncWebServerResponse * response = request->beginResponse(201, "text/plain", md5); // created
|
||||
request->send(response);
|
||||
return;
|
||||
}
|
||||
|
||||
handleError(request, 403); // send the forbidden response
|
||||
}
|
||||
|
||||
@@ -1154,7 +1154,7 @@ rest_server.post(EMSESP_BOARDPROFILE_ENDPOINT, (req, res) => {
|
||||
} else if (board_profile == 'C3MINI') {
|
||||
// Lolin C3 mini
|
||||
data.led_gpio = 7
|
||||
data.dallas_gpio = 2
|
||||
data.dallas_gpio = 1
|
||||
data.rx_gpio = 4
|
||||
data.tx_gpio = 5
|
||||
data.pbutton_gpio = 9
|
||||
@@ -1162,6 +1162,17 @@ rest_server.post(EMSESP_BOARDPROFILE_ENDPOINT, (req, res) => {
|
||||
data.eth_power = 0
|
||||
data.eth_phy_addr = 0
|
||||
data.eth_clock_mode = 0
|
||||
} else if (board_profile == 'S2MINI') {
|
||||
// Lolin C3 mini
|
||||
data.led_gpio = 15
|
||||
data.dallas_gpio = 7
|
||||
data.rx_gpio = 11
|
||||
data.tx_gpio = 12
|
||||
data.pbutton_gpio = 0
|
||||
data.phy_type = 0
|
||||
data.eth_power = 0
|
||||
data.eth_phy_addr = 0
|
||||
data.eth_clock_mode = 0
|
||||
}
|
||||
|
||||
console.log('boardProfile POST. Sending back, profile: ' + board_profile + ', ' + 'data: ' + JSON.stringify(data))
|
||||
|
||||
@@ -2,6 +2,7 @@ import shutil
|
||||
import re
|
||||
import os
|
||||
Import("env")
|
||||
import hashlib
|
||||
|
||||
OUTPUT_DIR = "build{}".format(os.path.sep)
|
||||
|
||||
@@ -18,7 +19,6 @@ def bin_copy(source, target, env):
|
||||
bag[var] = m.group(1)
|
||||
|
||||
app_version = bag.get('app_version')
|
||||
|
||||
platform = "ESP32"
|
||||
chip_target = env.get('PIOENV').upper()
|
||||
|
||||
@@ -33,14 +33,13 @@ def bin_copy(source, target, env):
|
||||
|
||||
# alternatively take platform from the pio target
|
||||
# platform = str(target[0]).split(os.path.sep)[2]
|
||||
chip_target = env.get('PIOENV').upper()
|
||||
|
||||
print("app version: " + app_version)
|
||||
print("platform: " + platform)
|
||||
print("chip_target: " + chip_target)
|
||||
|
||||
# convert . to _ so Windows doesn't complain
|
||||
variant = "EMS-ESP-" + chip_target + "-" + app_version.replace(".", "_")
|
||||
variant = "EMS-ESP-" + app_version.replace(".", "_") + "-" + chip_target.replace("CI","ESP32")
|
||||
|
||||
# check if output directories exist and create if necessary
|
||||
if not os.path.isdir(OUTPUT_DIR):
|
||||
@@ -52,15 +51,29 @@ def bin_copy(source, target, env):
|
||||
|
||||
# create string with location and file names based on variant
|
||||
bin_file = "{}firmware{}{}.bin".format(OUTPUT_DIR, os.path.sep, variant)
|
||||
md5_file = "{}firmware{}{}.md5".format(OUTPUT_DIR, os.path.sep, variant)
|
||||
|
||||
# check if new target files exist and remove if necessary
|
||||
for f in [bin_file]:
|
||||
if os.path.isfile(f):
|
||||
os.remove(f)
|
||||
|
||||
# check if new target files exist and remove if necessary
|
||||
for f in [md5_file]:
|
||||
if os.path.isfile(f):
|
||||
os.remove(f)
|
||||
|
||||
print("Renaming file to "+bin_file)
|
||||
|
||||
# copy firmware.bin to firmware/<variant>.bin
|
||||
shutil.copy(str(target[0]), bin_file)
|
||||
|
||||
with open(bin_file,"rb") as f:
|
||||
result = hashlib.md5(f.read())
|
||||
print("Calculating MD5: "+result.hexdigest())
|
||||
file1 = open(md5_file, 'w')
|
||||
file1.write(result.hexdigest())
|
||||
file1.close()
|
||||
|
||||
env.AddPostAction("$BUILD_DIR/${PROGNAME}.bin", [bin_copy])
|
||||
env.AddPostAction("$BUILD_DIR/${PROGNAME}.md5", [bin_copy])
|
||||
|
||||
@@ -151,11 +151,11 @@ void EMSESPShell::add_console_commands() {
|
||||
},
|
||||
"local");
|
||||
} else {
|
||||
shell.println("Must be 0B, 0D, 0A, 0F or 12");
|
||||
shell.println("Must be 0B, 0D, 0A, 0E, 0F, or 48 - 4D");
|
||||
}
|
||||
},
|
||||
[](Shell & shell __attribute__((unused)), const std::vector<std::string> & arguments __attribute__((unused))) -> const std::vector<std::string> {
|
||||
return std::vector<std::string>{"0B", "0D", "0A", "0F", "12"};
|
||||
return std::vector<std::string>{"0B", "0D", "0A", "0E", "0F", "48", "49", "4A", "4B", "4C", "4D"};
|
||||
});
|
||||
|
||||
commands->add_command(ShellContext::MAIN,
|
||||
|
||||
@@ -947,15 +947,14 @@ void EMSdevice::generate_values_web_customization(JsonArray & output) {
|
||||
obj["m"] = dv.state >> 4; // send back the mask state. We're only interested in the high nibble
|
||||
obj["w"] = dv.has_cmd; // if writable
|
||||
|
||||
if (dv.has_cmd) {
|
||||
// set the custom min and max values if there are any
|
||||
if (dv.has_cmd && (obj["v"].is<float>() || obj["v"].is<int>())) {
|
||||
// set the min and max values if there are any and if entity has a value
|
||||
int16_t dv_set_min;
|
||||
uint16_t dv_set_max;
|
||||
if (dv.get_custom_min(dv_set_min)) {
|
||||
obj["mi"] = fahrenheit ? (int)(dv_set_min * 1.8 + 32 * (fahrenheit - 1)) : dv_set_min;
|
||||
}
|
||||
if (dv.get_custom_max(dv_set_max)) {
|
||||
obj["ma"] = fahrenheit ? (int)(dv_set_max * 1.8 + 32 * (fahrenheit - 1)) : dv_set_max;
|
||||
if (dv.get_min_max(dv_set_min, dv_set_max)) {
|
||||
char s[10];
|
||||
obj["mi"] = Helpers::render_value(s, dv_set_min, 0, fahrenheit);
|
||||
obj["ma"] = Helpers::render_value(s, dv_set_max, 0, fahrenheit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -287,13 +287,13 @@ bool DeviceValue::get_min_max(int16_t & dv_set_min, uint16_t & dv_set_max) {
|
||||
|
||||
if (type == DeviceValueType::USHORT) {
|
||||
dv_set_min = Helpers::transformNumFloat(0, numeric_operator, fahrenheit);
|
||||
dv_set_max = Helpers::transformNumFloat(EMS_VALUE_USHORT_NOTSET, numeric_operator, fahrenheit);
|
||||
dv_set_max = Helpers::transformNumFloat(EMS_VALUE_USHORT_NOTSET - 1, numeric_operator, fahrenheit);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (type == DeviceValueType::SHORT) {
|
||||
dv_set_min = Helpers::transformNumFloat(-EMS_VALUE_SHORT_NOTSET, numeric_operator, fahrenheit);
|
||||
dv_set_max = Helpers::transformNumFloat(EMS_VALUE_SHORT_NOTSET, numeric_operator, fahrenheit);
|
||||
dv_set_min = Helpers::transformNumFloat(-EMS_VALUE_SHORT_NOTSET + 1, numeric_operator, fahrenheit);
|
||||
dv_set_max = Helpers::transformNumFloat(EMS_VALUE_SHORT_NOTSET - 1, numeric_operator, fahrenheit);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -301,7 +301,7 @@ bool DeviceValue::get_min_max(int16_t & dv_set_min, uint16_t & dv_set_max) {
|
||||
if (uom == DeviceValueUOM::PERCENT) {
|
||||
dv_set_max = 100;
|
||||
} else {
|
||||
dv_set_max = Helpers::transformNumFloat(EMS_VALUE_UINT_NOTSET, numeric_operator, fahrenheit);
|
||||
dv_set_max = Helpers::transformNumFloat(EMS_VALUE_UINT_NOTSET - 1, numeric_operator, fahrenheit);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -311,19 +311,19 @@ bool DeviceValue::get_min_max(int16_t & dv_set_min, uint16_t & dv_set_max) {
|
||||
dv_set_min = -100;
|
||||
dv_set_max = 100;
|
||||
} else {
|
||||
dv_set_min = Helpers::transformNumFloat(-EMS_VALUE_INT_NOTSET, numeric_operator, fahrenheit);
|
||||
dv_set_max = Helpers::transformNumFloat(EMS_VALUE_INT_NOTSET, numeric_operator, fahrenheit);
|
||||
dv_set_min = Helpers::transformNumFloat(-EMS_VALUE_INT_NOTSET + 1, numeric_operator, fahrenheit);
|
||||
dv_set_max = Helpers::transformNumFloat(EMS_VALUE_INT_NOTSET - 1, numeric_operator, fahrenheit);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (type == DeviceValueType::ULONG) {
|
||||
dv_set_max = Helpers::transformNumFloat(EMS_VALUE_ULONG_NOTSET, numeric_operator);
|
||||
dv_set_max = Helpers::transformNumFloat(EMS_VALUE_ULONG_NOTSET - 1, numeric_operator);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (type == DeviceValueType::TIME) {
|
||||
dv_set_max = Helpers::transformNumFloat(EMS_VALUE_ULONG_NOTSET, numeric_operator);
|
||||
dv_set_max = Helpers::transformNumFloat(EMS_VALUE_ULONG_NOTSET - 1, numeric_operator);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -377,7 +377,11 @@ void System::start() {
|
||||
// disable bluetooth module
|
||||
// periph_module_disable(PERIPH_BT_MODULE);
|
||||
if (low_clock_) {
|
||||
#if CONFIG_IDF_TARGET_ESP32C3
|
||||
setCpuFrequencyMhz(80);
|
||||
#else
|
||||
setCpuFrequencyMhz(160);
|
||||
#endif
|
||||
}
|
||||
fstotal_ = LittleFS.totalBytes() / 1024; // read only once, it takes 500 ms to read
|
||||
psram_ = ESP.getPsramSize() / 1024;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#define EMSESP_APP_VERSION "3.5.0b7"
|
||||
#define EMSESP_APP_VERSION "3.5.0b8"
|
||||
|
||||
#if CONFIG_IDF_TARGET_ESP32C3
|
||||
#define EMSESP_PLATFORM "ESP32-C3";
|
||||
|
||||
@@ -34,7 +34,7 @@ WebStatusService::WebStatusService(AsyncWebServer * server, SecurityManager * se
|
||||
void WebStatusService::WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info) {
|
||||
switch (event) {
|
||||
case ARDUINO_EVENT_WIFI_STA_DISCONNECTED:
|
||||
EMSESP::logger().warning("WiFi disconnected. Reason code=%d", info.wifi_sta_disconnected.reason); // IDF 4.0
|
||||
EMSESP::logger().warning("WiFi disconnected. Reason code=%s", disconnectReason(info.wifi_sta_disconnected.reason)); // IDF 4.0
|
||||
WiFi.disconnect(true);
|
||||
break;
|
||||
|
||||
@@ -42,7 +42,6 @@ void WebStatusService::WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info) {
|
||||
#ifndef EMSESP_STANDALONE
|
||||
EMSESP::logger().info("WiFi connected with IP=%s, hostname=%s", WiFi.localIP().toString().c_str(), WiFi.getHostname());
|
||||
#endif
|
||||
// EMSESP::system_.send_heartbeat(); // send from mqtt start
|
||||
EMSESP::system_.syslog_init();
|
||||
mDNS_start();
|
||||
break;
|
||||
@@ -226,4 +225,68 @@ void WebStatusService::mDNS_start() const {
|
||||
#endif
|
||||
}
|
||||
|
||||
const char * WebStatusService::disconnectReason(uint8_t code) {
|
||||
switch (code) {
|
||||
case WIFI_REASON_UNSPECIFIED: // = 1,
|
||||
return "unspecifiied";
|
||||
case WIFI_REASON_AUTH_EXPIRE: // = 2,
|
||||
return "auth expire";
|
||||
case WIFI_REASON_AUTH_LEAVE: // = 3,
|
||||
return "auth leave";
|
||||
case WIFI_REASON_ASSOC_EXPIRE: // = 4,
|
||||
return "assoc expired";
|
||||
case WIFI_REASON_ASSOC_TOOMANY: // = 5,
|
||||
return "assoc too many";
|
||||
case WIFI_REASON_NOT_AUTHED: // = 6,
|
||||
return "not authed";
|
||||
case WIFI_REASON_NOT_ASSOCED: // = 7,
|
||||
return "not assoced";
|
||||
case WIFI_REASON_ASSOC_LEAVE: // = 8,
|
||||
return "assoc leave";
|
||||
case WIFI_REASON_ASSOC_NOT_AUTHED: // = 9,
|
||||
return "assoc not authed";
|
||||
case WIFI_REASON_DISASSOC_PWRCAP_BAD: // = 10,
|
||||
return "disassoc powerCAP bad";
|
||||
case WIFI_REASON_DISASSOC_SUPCHAN_BAD: // = 11,
|
||||
return "disassoc supchan bad";
|
||||
case WIFI_REASON_IE_INVALID: // = 13,
|
||||
return "IE invalid";
|
||||
case WIFI_REASON_MIC_FAILURE: // = 14,
|
||||
return "MIC failure";
|
||||
case WIFI_REASON_4WAY_HANDSHAKE_TIMEOUT: // = 15,
|
||||
return "4way handshake timeout";
|
||||
case WIFI_REASON_GROUP_KEY_UPDATE_TIMEOUT: // = 16,
|
||||
return "group key-update timeout";
|
||||
case WIFI_REASON_IE_IN_4WAY_DIFFERS: // = 17,
|
||||
return "IE in 4way differs";
|
||||
case WIFI_REASON_GROUP_CIPHER_INVALID: // = 18,
|
||||
return "group cipher invalid";
|
||||
case WIFI_REASON_PAIRWISE_CIPHER_INVALID: // = 19,
|
||||
return "pairwise cipher invalid";
|
||||
case WIFI_REASON_AKMP_INVALID: // = 20,
|
||||
return "AKMP invalid";
|
||||
case WIFI_REASON_UNSUPP_RSN_IE_VERSION: // = 21,
|
||||
return "unsupported RSN_IE version";
|
||||
case WIFI_REASON_INVALID_RSN_IE_CAP: // = 22,
|
||||
return "invalid RSN_IE_CAP";
|
||||
case WIFI_REASON_802_1X_AUTH_FAILED: // = 23,
|
||||
return "802 X1 auth failed";
|
||||
case WIFI_REASON_CIPHER_SUITE_REJECTED: // = 24,
|
||||
return "cipher suite rejected";
|
||||
case WIFI_REASON_BEACON_TIMEOUT: // = 200,
|
||||
return "beacon timeout";
|
||||
case WIFI_REASON_NO_AP_FOUND: // = 201,
|
||||
return "no AP found";
|
||||
case WIFI_REASON_AUTH_FAIL: // = 202,
|
||||
return "auth fail";
|
||||
case WIFI_REASON_ASSOC_FAIL: // = 203,
|
||||
return "assoc fail";
|
||||
case WIFI_REASON_HANDSHAKE_TIMEOUT: // = 204,
|
||||
return "handshake timeout";
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
} // namespace emsesp
|
||||
@@ -33,6 +33,8 @@ class WebStatusService {
|
||||
void webStatusService(AsyncWebServerRequest * request);
|
||||
void WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info);
|
||||
void mDNS_start() const;
|
||||
const char * disconnectReason(uint8_t code);
|
||||
|
||||
};
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
Reference in New Issue
Block a user