mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2025-12-06 07:49:52 +03:00
Remove OTA feature #1738
This commit is contained in:
@@ -17,6 +17,7 @@
|
|||||||
- heatpump entities `fan` and `shutdown` [#1690](https://github.com/emsesp/EMS-ESP32/discussions/1690)
|
- heatpump entities `fan` and `shutdown` [#1690](https://github.com/emsesp/EMS-ESP32/discussions/1690)
|
||||||
- mqtt HA-mode 3 for v3.6 compatible HA entities, set on update v3.6->v3.7
|
- mqtt HA-mode 3 for v3.6 compatible HA entities, set on update v3.6->v3.7
|
||||||
- HP input states [#1723](https://github.com/emsesp/EMS-ESP32/discussions/1723)
|
- HP input states [#1723](https://github.com/emsesp/EMS-ESP32/discussions/1723)
|
||||||
|
- Added scripts for OTA (scripts/upload.py and upload_cli.py) [#1738](https://github.com/emsesp/EMS-ESP32/issues/1738)
|
||||||
|
|
||||||
## Fixed
|
## Fixed
|
||||||
|
|
||||||
@@ -31,3 +32,4 @@
|
|||||||
- Refresh UI - moving settings to one location [#1665](https://github.com/emsesp/EMS-ESP32/issues/1665)
|
- Refresh UI - moving settings to one location [#1665](https://github.com/emsesp/EMS-ESP32/issues/1665)
|
||||||
- rename DeviceValueTypes, add UINT32 for custom entities
|
- rename DeviceValueTypes, add UINT32 for custom entities
|
||||||
- dynamic register dhw circuits for thermostat
|
- dynamic register dhw circuits for thermostat
|
||||||
|
- removed OTA feature [#1738](https://github.com/emsesp/EMS-ESP32/issues/1738)
|
||||||
|
|||||||
@@ -25,11 +25,6 @@ build_flags =
|
|||||||
-D FACTORY_NTP_TIME_ZONE_FORMAT=\"CET-1CEST,M3.5.0,M10.5.0/3\"
|
-D FACTORY_NTP_TIME_ZONE_FORMAT=\"CET-1CEST,M3.5.0,M10.5.0/3\"
|
||||||
-D FACTORY_NTP_SERVER=\"time.google.com\"
|
-D FACTORY_NTP_SERVER=\"time.google.com\"
|
||||||
|
|
||||||
; OTA settings
|
|
||||||
-D FACTORY_OTA_PORT=8266
|
|
||||||
-D FACTORY_OTA_PASSWORD=\"ems-esp-neo\"
|
|
||||||
-D FACTORY_OTA_ENABLED=false
|
|
||||||
|
|
||||||
; MQTT settings
|
; MQTT settings
|
||||||
-D FACTORY_MQTT_ENABLED=false
|
-D FACTORY_MQTT_ENABLED=false
|
||||||
-D FACTORY_MQTT_HOST=\"\"
|
-D FACTORY_MQTT_HOST=\"\"
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import AccessPoint from 'framework/ap/AccessPoint';
|
|||||||
import Mqtt from 'framework/mqtt/Mqtt';
|
import Mqtt from 'framework/mqtt/Mqtt';
|
||||||
import Network from 'framework/network/Network';
|
import Network from 'framework/network/Network';
|
||||||
import NetworkTime from 'framework/ntp/NetworkTime';
|
import NetworkTime from 'framework/ntp/NetworkTime';
|
||||||
import OTASettings from 'framework/ota/OTASettings';
|
|
||||||
import Security from 'framework/security/Security';
|
import Security from 'framework/security/Security';
|
||||||
import ESPSystemStatus from 'framework/system/ESPSystemStatus';
|
import ESPSystemStatus from 'framework/system/ESPSystemStatus';
|
||||||
import System from 'framework/system/System';
|
import System from 'framework/system/System';
|
||||||
@@ -43,7 +42,6 @@ const AuthenticatedRouting: FC = () => {
|
|||||||
<Route path="/settings/ap/*" element={<AccessPoint />} />
|
<Route path="/settings/ap/*" element={<AccessPoint />} />
|
||||||
<Route path="/settings/ntp/*" element={<NetworkTime />} />
|
<Route path="/settings/ntp/*" element={<NetworkTime />} />
|
||||||
<Route path="/settings/mqtt/*" element={<Mqtt />} />
|
<Route path="/settings/mqtt/*" element={<Mqtt />} />
|
||||||
<Route path="/settings/ota/*" element={<OTASettings />} />
|
|
||||||
<Route path="/settings/security/*" element={<Security />} />
|
<Route path="/settings/security/*" element={<Security />} />
|
||||||
<Route
|
<Route
|
||||||
path="/settings/espsystemstatus/*"
|
path="/settings/espsystemstatus/*"
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-unsafe-call */
|
/* eslint-disable @typescript-eslint/no-unsafe-call */
|
||||||
|
|
||||||
/* eslint-disable @typescript-eslint/no-unsafe-return */
|
/* eslint-disable @typescript-eslint/no-unsafe-return */
|
||||||
import type { ESPSystemStatus, LogSettings, OTASettings, SystemStatus } from 'types';
|
import type { ESPSystemStatus, LogSettings, SystemStatus } from 'types';
|
||||||
|
|
||||||
import { alovaInstance, alovaInstanceGH } from './endpoints';
|
import { alovaInstance, alovaInstanceGH } from './endpoints';
|
||||||
|
|
||||||
@@ -20,12 +20,6 @@ export const restart = () => alovaInstance.Post('/rest/restart');
|
|||||||
export const partition = () => alovaInstance.Post('/rest/partition');
|
export const partition = () => alovaInstance.Post('/rest/partition');
|
||||||
export const factoryReset = () => alovaInstance.Post('/rest/factoryReset');
|
export const factoryReset = () => alovaInstance.Post('/rest/factoryReset');
|
||||||
|
|
||||||
// OTA
|
|
||||||
export const readOTASettings = () =>
|
|
||||||
alovaInstance.Get<OTASettings>(`/rest/otaSettings`);
|
|
||||||
export const updateOTASettings = (data: OTASettings) =>
|
|
||||||
alovaInstance.Post('/rest/otaSettings', data);
|
|
||||||
|
|
||||||
// SystemLog
|
// SystemLog
|
||||||
export const readLogSettings = () =>
|
export const readLogSettings = () =>
|
||||||
alovaInstance.Get<LogSettings>(`/rest/logSettings`);
|
alovaInstance.Get<LogSettings>(`/rest/logSettings`);
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import { toast } from 'react-toastify';
|
|||||||
|
|
||||||
import AccessTimeIcon from '@mui/icons-material/AccessTime';
|
import AccessTimeIcon from '@mui/icons-material/AccessTime';
|
||||||
import CancelIcon from '@mui/icons-material/Cancel';
|
import CancelIcon from '@mui/icons-material/Cancel';
|
||||||
import CastIcon from '@mui/icons-material/Cast';
|
|
||||||
import DeviceHubIcon from '@mui/icons-material/DeviceHub';
|
import DeviceHubIcon from '@mui/icons-material/DeviceHub';
|
||||||
import ImportExportIcon from '@mui/icons-material/ImportExport';
|
import ImportExportIcon from '@mui/icons-material/ImportExport';
|
||||||
import LockIcon from '@mui/icons-material/Lock';
|
import LockIcon from '@mui/icons-material/Lock';
|
||||||
@@ -212,13 +211,7 @@ const Settings: FC = () => {
|
|||||||
text={LL.CONFIGURE('MQTT')}
|
text={LL.CONFIGURE('MQTT')}
|
||||||
to="mqtt"
|
to="mqtt"
|
||||||
/>
|
/>
|
||||||
<ListMenuItem
|
|
||||||
icon={CastIcon}
|
|
||||||
bgcolor="#efc34b"
|
|
||||||
label="OTA"
|
|
||||||
text={LL.CONFIGURE('OTA')}
|
|
||||||
to="ota"
|
|
||||||
/>
|
|
||||||
<ListMenuItem
|
<ListMenuItem
|
||||||
icon={LockIcon}
|
icon={LockIcon}
|
||||||
label={LL.SECURITY(0)}
|
label={LL.SECURITY(0)}
|
||||||
|
|||||||
@@ -1,141 +0,0 @@
|
|||||||
import { useState } from 'react';
|
|
||||||
import type { FC } from 'react';
|
|
||||||
|
|
||||||
import CancelIcon from '@mui/icons-material/Cancel';
|
|
||||||
import WarningIcon from '@mui/icons-material/Warning';
|
|
||||||
import { Button, Checkbox } from '@mui/material';
|
|
||||||
|
|
||||||
import * as SystemApi from 'api/system';
|
|
||||||
|
|
||||||
import type { ValidateFieldsError } from 'async-validator';
|
|
||||||
import {
|
|
||||||
BlockFormControlLabel,
|
|
||||||
BlockNavigation,
|
|
||||||
ButtonRow,
|
|
||||||
FormLoader,
|
|
||||||
SectionContent,
|
|
||||||
ValidatedPasswordField,
|
|
||||||
ValidatedTextField,
|
|
||||||
useLayoutTitle
|
|
||||||
} from 'components';
|
|
||||||
import { useI18nContext } from 'i18n/i18n-react';
|
|
||||||
import type { OTASettingsType } from 'types';
|
|
||||||
import { numberValue, updateValueDirty, useRest } from 'utils';
|
|
||||||
import { validate } from 'validators';
|
|
||||||
import { OTA_SETTINGS_VALIDATOR } from 'validators/system';
|
|
||||||
|
|
||||||
const OTASettings: FC = () => {
|
|
||||||
const {
|
|
||||||
loadData,
|
|
||||||
saveData,
|
|
||||||
saving,
|
|
||||||
updateDataValue,
|
|
||||||
data,
|
|
||||||
origData,
|
|
||||||
dirtyFlags,
|
|
||||||
setDirtyFlags,
|
|
||||||
blocker,
|
|
||||||
errorMessage
|
|
||||||
} = useRest<OTASettingsType>({
|
|
||||||
read: SystemApi.readOTASettings,
|
|
||||||
update: SystemApi.updateOTASettings
|
|
||||||
});
|
|
||||||
|
|
||||||
const { LL } = useI18nContext();
|
|
||||||
|
|
||||||
const updateFormValue = updateValueDirty(
|
|
||||||
origData,
|
|
||||||
dirtyFlags,
|
|
||||||
setDirtyFlags,
|
|
||||||
updateDataValue
|
|
||||||
);
|
|
||||||
|
|
||||||
const [fieldErrors, setFieldErrors] = useState<ValidateFieldsError>();
|
|
||||||
|
|
||||||
const content = () => {
|
|
||||||
if (!data) {
|
|
||||||
return <FormLoader onRetry={loadData} errorMessage={errorMessage} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
const validateAndSubmit = async () => {
|
|
||||||
try {
|
|
||||||
setFieldErrors(undefined);
|
|
||||||
await validate(OTA_SETTINGS_VALIDATOR, data);
|
|
||||||
await saveData();
|
|
||||||
} catch (error) {
|
|
||||||
setFieldErrors(error as ValidateFieldsError);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
useLayoutTitle('OTA');
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<BlockFormControlLabel
|
|
||||||
control={
|
|
||||||
<Checkbox
|
|
||||||
name="enabled"
|
|
||||||
checked={data.enabled}
|
|
||||||
onChange={updateFormValue}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
label={LL.ENABLE_OTA()}
|
|
||||||
/>
|
|
||||||
<ValidatedTextField
|
|
||||||
fieldErrors={fieldErrors}
|
|
||||||
name="port"
|
|
||||||
label="Port"
|
|
||||||
fullWidth
|
|
||||||
variant="outlined"
|
|
||||||
value={numberValue(data.port)}
|
|
||||||
type="number"
|
|
||||||
onChange={updateFormValue}
|
|
||||||
margin="normal"
|
|
||||||
/>
|
|
||||||
<ValidatedPasswordField
|
|
||||||
fieldErrors={fieldErrors}
|
|
||||||
name="password"
|
|
||||||
label={LL.PASSWORD()}
|
|
||||||
fullWidth
|
|
||||||
variant="outlined"
|
|
||||||
value={data.password}
|
|
||||||
onChange={updateFormValue}
|
|
||||||
margin="normal"
|
|
||||||
/>
|
|
||||||
{dirtyFlags && dirtyFlags.length !== 0 && (
|
|
||||||
<ButtonRow>
|
|
||||||
<Button
|
|
||||||
startIcon={<CancelIcon />}
|
|
||||||
disabled={saving}
|
|
||||||
variant="outlined"
|
|
||||||
color="primary"
|
|
||||||
type="submit"
|
|
||||||
onClick={loadData}
|
|
||||||
>
|
|
||||||
{LL.CANCEL()}
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
startIcon={<WarningIcon color="warning" />}
|
|
||||||
disabled={saving}
|
|
||||||
variant="contained"
|
|
||||||
color="info"
|
|
||||||
type="submit"
|
|
||||||
onClick={validateAndSubmit}
|
|
||||||
>
|
|
||||||
{LL.APPLY_CHANGES(dirtyFlags.length)}
|
|
||||||
</Button>
|
|
||||||
</ButtonRow>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<SectionContent>
|
|
||||||
{blocker ? <BlockNavigation blocker={blocker} /> : null}
|
|
||||||
{content()}
|
|
||||||
</SectionContent>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default OTASettings;
|
|
||||||
@@ -4,7 +4,6 @@ import { toast } from 'react-toastify';
|
|||||||
import AccessTimeIcon from '@mui/icons-material/AccessTime';
|
import AccessTimeIcon from '@mui/icons-material/AccessTime';
|
||||||
import BuildIcon from '@mui/icons-material/Build';
|
import BuildIcon from '@mui/icons-material/Build';
|
||||||
import CancelIcon from '@mui/icons-material/Cancel';
|
import CancelIcon from '@mui/icons-material/Cancel';
|
||||||
import CastIcon from '@mui/icons-material/Cast';
|
|
||||||
import DeviceHubIcon from '@mui/icons-material/DeviceHub';
|
import DeviceHubIcon from '@mui/icons-material/DeviceHub';
|
||||||
import DirectionsBusIcon from '@mui/icons-material/DirectionsBus';
|
import DirectionsBusIcon from '@mui/icons-material/DirectionsBus';
|
||||||
import MemoryIcon from '@mui/icons-material/Memory';
|
import MemoryIcon from '@mui/icons-material/Memory';
|
||||||
@@ -277,20 +276,10 @@ const SystemStatus: FC = () => {
|
|||||||
/>
|
/>
|
||||||
<Divider variant="inset" component="li" />
|
<Divider variant="inset" component="li" />
|
||||||
|
|
||||||
<ListMenuItem
|
|
||||||
disabled={!me.admin}
|
|
||||||
icon={CastIcon}
|
|
||||||
bgcolor={activeHighlight(data.ota_status)}
|
|
||||||
label={LL.STATUS_OF('OTA')}
|
|
||||||
text={data.ota_status ? LL.ACTIVE() : LL.INACTIVE(0)}
|
|
||||||
to="/settings/ota"
|
|
||||||
/>
|
|
||||||
<Divider variant="inset" component="li" />
|
|
||||||
|
|
||||||
<ListMenuItem
|
<ListMenuItem
|
||||||
disabled={!me.admin}
|
disabled={!me.admin}
|
||||||
icon={SettingsInputAntennaIcon}
|
icon={SettingsInputAntennaIcon}
|
||||||
bgcolor={activeHighlight(data.ota_status)}
|
bgcolor={activeHighlight(data.ap_status)}
|
||||||
label={LL.ACCESS_POINT(0)}
|
label={LL.ACCESS_POINT(0)}
|
||||||
text={data.ap_status ? LL.ACTIVE() : LL.INACTIVE(0)}
|
text={data.ap_status ? LL.ACTIVE() : LL.INACTIVE(0)}
|
||||||
to="/settings/ap/status"
|
to="/settings/ap/status"
|
||||||
|
|||||||
@@ -192,7 +192,6 @@ const de: Translation = {
|
|||||||
FILESYSTEM: 'Dateisystem (Genutzt / Frei)',
|
FILESYSTEM: 'Dateisystem (Genutzt / Frei)',
|
||||||
BUFFER_SIZE: 'max. Puffergröße',
|
BUFFER_SIZE: 'max. Puffergröße',
|
||||||
COMPACT: 'Kompakte Darstellung',
|
COMPACT: 'Kompakte Darstellung',
|
||||||
ENABLE_OTA: 'OTA Updates verwenden',
|
|
||||||
DOWNLOAD_CUSTOMIZATION_TEXT: 'Herunterladen der individuellen Entitätsanpassungen',
|
DOWNLOAD_CUSTOMIZATION_TEXT: 'Herunterladen der individuellen Entitätsanpassungen',
|
||||||
DOWNLOAD_SCHEDULE_TEXT: 'Herunterladen geplanter Befehle',
|
DOWNLOAD_SCHEDULE_TEXT: 'Herunterladen geplanter Befehle',
|
||||||
DOWNLOAD_SETTINGS_TEXT: 'Herunterladen der Anwendungseinstellungen. Vorsicht beim Teilen der Einstellungen, da sie Passwörter und andere sensitive Einstellungen enthalten',
|
DOWNLOAD_SETTINGS_TEXT: 'Herunterladen der Anwendungseinstellungen. Vorsicht beim Teilen der Einstellungen, da sie Passwörter und andere sensitive Einstellungen enthalten',
|
||||||
|
|||||||
@@ -192,7 +192,6 @@ const en: Translation = {
|
|||||||
FILESYSTEM: 'File System (Used / Free)',
|
FILESYSTEM: 'File System (Used / Free)',
|
||||||
BUFFER_SIZE: 'Max Buffer Size',
|
BUFFER_SIZE: 'Max Buffer Size',
|
||||||
COMPACT: 'Compact',
|
COMPACT: 'Compact',
|
||||||
ENABLE_OTA: 'Enable OTA Updates',
|
|
||||||
DOWNLOAD_CUSTOMIZATION_TEXT: 'Download the entity customizations',
|
DOWNLOAD_CUSTOMIZATION_TEXT: 'Download the entity customizations',
|
||||||
DOWNLOAD_SCHEDULE_TEXT: 'Download Scheduler Events',
|
DOWNLOAD_SCHEDULE_TEXT: 'Download Scheduler Events',
|
||||||
DOWNLOAD_SETTINGS_TEXT: 'Download the application settings. Be careful when sharing your settings as this file contains passwords and other sensitive system information',
|
DOWNLOAD_SETTINGS_TEXT: 'Download the application settings. Be careful when sharing your settings as this file contains passwords and other sensitive system information',
|
||||||
|
|||||||
@@ -192,7 +192,6 @@ const fr: Translation = {
|
|||||||
FILESYSTEM: 'File System (Utilisée / Libre)',
|
FILESYSTEM: 'File System (Utilisée / Libre)',
|
||||||
BUFFER_SIZE: 'Max taille du buffer',
|
BUFFER_SIZE: 'Max taille du buffer',
|
||||||
COMPACT: 'Compact',
|
COMPACT: 'Compact',
|
||||||
ENABLE_OTA: 'Activer les updates OTA',
|
|
||||||
DOWNLOAD_CUSTOMIZATION_TEXT: "Télécharger les personnalisations d'entités",
|
DOWNLOAD_CUSTOMIZATION_TEXT: "Télécharger les personnalisations d'entités",
|
||||||
DOWNLOAD_SCHEDULE_TEXT: 'Download Scheduler Events', // TODO translate
|
DOWNLOAD_SCHEDULE_TEXT: 'Download Scheduler Events', // TODO translate
|
||||||
DOWNLOAD_SETTINGS_TEXT: "Téléchargez les paramètres de l'application. Soyez prudent lorsque vous partagez vos paramètres car ce fichier contient des mots de passe et d'autres informations système sensibles.",
|
DOWNLOAD_SETTINGS_TEXT: "Téléchargez les paramètres de l'application. Soyez prudent lorsque vous partagez vos paramètres car ce fichier contient des mots de passe et d'autres informations système sensibles.",
|
||||||
|
|||||||
@@ -192,7 +192,6 @@ const it: Translation = {
|
|||||||
FILESYSTEM: 'Memoria Sistema (Usata / Libera)',
|
FILESYSTEM: 'Memoria Sistema (Usata / Libera)',
|
||||||
BUFFER_SIZE: 'Max Buffer Size',
|
BUFFER_SIZE: 'Max Buffer Size',
|
||||||
COMPACT: 'Compact',
|
COMPACT: 'Compact',
|
||||||
ENABLE_OTA: 'Abilita aggiornamenti OTA',
|
|
||||||
DOWNLOAD_CUSTOMIZATION_TEXT: 'Scarica personalizzazioni entità',
|
DOWNLOAD_CUSTOMIZATION_TEXT: 'Scarica personalizzazioni entità',
|
||||||
DOWNLOAD_SCHEDULE_TEXT: 'Download Scheduler Events',
|
DOWNLOAD_SCHEDULE_TEXT: 'Download Scheduler Events',
|
||||||
DOWNLOAD_SETTINGS_TEXT: 'Scarica le impostazioni dell applicazione. Fai attenzione quando condividi le tue impostazioni poiché questo file contiene password e altre informazioni di sistema riservate',
|
DOWNLOAD_SETTINGS_TEXT: 'Scarica le impostazioni dell applicazione. Fai attenzione quando condividi le tue impostazioni poiché questo file contiene password e altre informazioni di sistema riservate',
|
||||||
|
|||||||
@@ -192,7 +192,6 @@ const nl: Translation = {
|
|||||||
FILESYSTEM: 'File System (Used / Free)',
|
FILESYSTEM: 'File System (Used / Free)',
|
||||||
BUFFER_SIZE: 'Max Buffer Size',
|
BUFFER_SIZE: 'Max Buffer Size',
|
||||||
COMPACT: 'Compact',
|
COMPACT: 'Compact',
|
||||||
ENABLE_OTA: 'Acitveer OTA Updates',
|
|
||||||
DOWNLOAD_CUSTOMIZATION_TEXT: 'Download alle custom instellingen',
|
DOWNLOAD_CUSTOMIZATION_TEXT: 'Download alle custom instellingen',
|
||||||
DOWNLOAD_SCHEDULE_TEXT: 'Download Scheduler Events',
|
DOWNLOAD_SCHEDULE_TEXT: 'Download Scheduler Events',
|
||||||
DOWNLOAD_SETTINGS_TEXT: 'Download de applicatie settings. Wees voorzichting met het delen van dit bestand want het bevat o.a. de wachtwoorden in plain text',
|
DOWNLOAD_SETTINGS_TEXT: 'Download de applicatie settings. Wees voorzichting met het delen van dit bestand want het bevat o.a. de wachtwoorden in plain text',
|
||||||
|
|||||||
@@ -192,7 +192,6 @@ const no: Translation = {
|
|||||||
FILESYSTEM: 'File System (Brukt / Ledig)',
|
FILESYSTEM: 'File System (Brukt / Ledig)',
|
||||||
BUFFER_SIZE: 'Max Buffer Størrelse',
|
BUFFER_SIZE: 'Max Buffer Størrelse',
|
||||||
COMPACT: 'Komprimere',
|
COMPACT: 'Komprimere',
|
||||||
ENABLE_OTA: 'Aktiviser OTA oppdateringer',
|
|
||||||
DOWNLOAD_CUSTOMIZATION_TEXT: 'Last ned objektstilpasninger',
|
DOWNLOAD_CUSTOMIZATION_TEXT: 'Last ned objektstilpasninger',
|
||||||
DOWNLOAD_SCHEDULE_TEXT: 'Last ned planlagte oppgaver',
|
DOWNLOAD_SCHEDULE_TEXT: 'Last ned planlagte oppgaver',
|
||||||
DOWNLOAD_SETTINGS_TEXT: 'Last ned applikasjonskonfigurasjon. Vær varsom med å dele fila da den inneholder passord og annen sensitiv system informasjon',
|
DOWNLOAD_SETTINGS_TEXT: 'Last ned applikasjonskonfigurasjon. Vær varsom med å dele fila da den inneholder passord og annen sensitiv system informasjon',
|
||||||
|
|||||||
@@ -192,7 +192,6 @@ const pl: BaseTranslation = {
|
|||||||
FILESYSTEM: 'System plików (wykorzystane / wolne)',
|
FILESYSTEM: 'System plików (wykorzystane / wolne)',
|
||||||
BUFFER_SIZE: 'Maksymalna pojemność bufora (ilość wpisów)',
|
BUFFER_SIZE: 'Maksymalna pojemność bufora (ilość wpisów)',
|
||||||
COMPACT: 'Kompaktowy',
|
COMPACT: 'Kompaktowy',
|
||||||
ENABLE_OTA: 'Aktywuj aktualizację OTA',
|
|
||||||
DOWNLOAD_CUSTOMIZATION_TEXT: 'Pobierz personalizacje.',
|
DOWNLOAD_CUSTOMIZATION_TEXT: 'Pobierz personalizacje.',
|
||||||
DOWNLOAD_SCHEDULE_TEXT: 'Pobierz harmonogram zdarzeń.',
|
DOWNLOAD_SCHEDULE_TEXT: 'Pobierz harmonogram zdarzeń.',
|
||||||
DOWNLOAD_SETTINGS_TEXT: 'Pobierz ustawienia aplikacji. Uwaga! Plik z ustawieniami zawiera hasła oraz inne wrażliwe informacje systemowe! Nie udostepniaj go pochopnie!',
|
DOWNLOAD_SETTINGS_TEXT: 'Pobierz ustawienia aplikacji. Uwaga! Plik z ustawieniami zawiera hasła oraz inne wrażliwe informacje systemowe! Nie udostepniaj go pochopnie!',
|
||||||
|
|||||||
@@ -192,7 +192,6 @@ const sk: Translation = {
|
|||||||
FILESYSTEM: 'Súborový systém (Použité / Voľné)',
|
FILESYSTEM: 'Súborový systém (Použité / Voľné)',
|
||||||
BUFFER_SIZE: 'Buffer-max.veľkosť',
|
BUFFER_SIZE: 'Buffer-max.veľkosť',
|
||||||
COMPACT: 'Kompaktné',
|
COMPACT: 'Kompaktné',
|
||||||
ENABLE_OTA: 'Povoliť OTA aktualizácie',
|
|
||||||
DOWNLOAD_CUSTOMIZATION_TEXT: 'Stiahnutie prispôsobení entity',
|
DOWNLOAD_CUSTOMIZATION_TEXT: 'Stiahnutie prispôsobení entity',
|
||||||
DOWNLOAD_SCHEDULE_TEXT: 'Stiahnutie plánovača udalostí',
|
DOWNLOAD_SCHEDULE_TEXT: 'Stiahnutie plánovača udalostí',
|
||||||
DOWNLOAD_SETTINGS_TEXT: 'Stiahnite si nastavenia aplikácie. Pri zdieľaní nastavení buďte opatrní, pretože tento súbor obsahuje heslá a iné citlivé systémové informácie.',
|
DOWNLOAD_SETTINGS_TEXT: 'Stiahnite si nastavenia aplikácie. Pri zdieľaní nastavení buďte opatrní, pretože tento súbor obsahuje heslá a iné citlivé systémové informácie.',
|
||||||
|
|||||||
@@ -192,7 +192,6 @@ const sv: Translation = {
|
|||||||
FILESYSTEM: 'Filsystem (Använt / Ledigt)',
|
FILESYSTEM: 'Filsystem (Använt / Ledigt)',
|
||||||
BUFFER_SIZE: 'Max Bufferstorlek',
|
BUFFER_SIZE: 'Max Bufferstorlek',
|
||||||
COMPACT: 'Komprimera',
|
COMPACT: 'Komprimera',
|
||||||
ENABLE_OTA: 'Aktivera OTA-uppdateringar',
|
|
||||||
DOWNLOAD_CUSTOMIZATION_TEXT: 'Ladda ner entitetsanpassningar',
|
DOWNLOAD_CUSTOMIZATION_TEXT: 'Ladda ner entitetsanpassningar',
|
||||||
DOWNLOAD_SCHEDULE_TEXT: 'Download Scheduler Events', // TODO translate
|
DOWNLOAD_SCHEDULE_TEXT: 'Download Scheduler Events', // TODO translate
|
||||||
DOWNLOAD_SETTINGS_TEXT: 'Ladda ner applikationsinställningar. Var försiktig om du delar dina iställlningar då de innehåller lösenord och annan känslig systeminformation',
|
DOWNLOAD_SETTINGS_TEXT: 'Ladda ner applikationsinställningar. Var försiktig om du delar dina iställlningar då de innehåller lösenord och annan känslig systeminformation',
|
||||||
|
|||||||
@@ -192,7 +192,6 @@ const tr: Translation = {
|
|||||||
FILESYSTEM: 'Dosya Sistemi (Kullanılmış / Boş)',
|
FILESYSTEM: 'Dosya Sistemi (Kullanılmış / Boş)',
|
||||||
BUFFER_SIZE: 'En fazla bellek boyutu',
|
BUFFER_SIZE: 'En fazla bellek boyutu',
|
||||||
COMPACT: 'Sıkışık',
|
COMPACT: 'Sıkışık',
|
||||||
ENABLE_OTA: 'OTA Güncellemelerine izin ver',
|
|
||||||
DOWNLOAD_CUSTOMIZATION_TEXT: 'Varlık özelleştirmelerini indir',
|
DOWNLOAD_CUSTOMIZATION_TEXT: 'Varlık özelleştirmelerini indir',
|
||||||
DOWNLOAD_SCHEDULE_TEXT: 'Download Scheduler Events', // TODO translate
|
DOWNLOAD_SCHEDULE_TEXT: 'Download Scheduler Events', // TODO translate
|
||||||
DOWNLOAD_SETTINGS_TEXT: 'Uygulama ayarlarını indir. Bu dosya hassas sistem bilgileri ve şifrelerinizi içerdiğinden ayarlarınızı paylaşırken dikkatli olun',
|
DOWNLOAD_SETTINGS_TEXT: 'Uygulama ayarlarını indir. Bu dosya hassas sistem bilgileri ve şifrelerinizi içerdiğinden ayarlarınızı paylaşırken dikkatli olun',
|
||||||
|
|||||||
@@ -35,17 +35,10 @@ export interface SystemStatus {
|
|||||||
num_analogs: number;
|
num_analogs: number;
|
||||||
free_heap: number;
|
free_heap: number;
|
||||||
ntp_status: number;
|
ntp_status: number;
|
||||||
ota_status: boolean;
|
|
||||||
mqtt_status: boolean;
|
mqtt_status: boolean;
|
||||||
ap_status: boolean;
|
ap_status: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface OTASettingsType {
|
|
||||||
enabled: boolean;
|
|
||||||
port: number;
|
|
||||||
password: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export enum LogLevel {
|
export enum LogLevel {
|
||||||
ERROR = 3,
|
ERROR = 3,
|
||||||
WARNING = 4,
|
WARNING = 4,
|
||||||
@@ -69,9 +62,3 @@ export interface LogSettings {
|
|||||||
max_messages: number;
|
max_messages: number;
|
||||||
compact: boolean;
|
compact: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface OTASettings {
|
|
||||||
enabled: boolean;
|
|
||||||
port: number;
|
|
||||||
password: string;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -4,5 +4,4 @@ export * from './mqtt';
|
|||||||
export * from './ntp';
|
export * from './ntp';
|
||||||
export * from './security';
|
export * from './security';
|
||||||
export * from './shared';
|
export * from './shared';
|
||||||
export * from './system';
|
|
||||||
export * from './network';
|
export * from './network';
|
||||||
|
|||||||
@@ -1,22 +0,0 @@
|
|||||||
import Schema from 'async-validator';
|
|
||||||
|
|
||||||
export const OTA_SETTINGS_VALIDATOR = new Schema({
|
|
||||||
port: [
|
|
||||||
{ required: true, message: 'Port is required' },
|
|
||||||
{
|
|
||||||
type: 'number',
|
|
||||||
min: 1025,
|
|
||||||
max: 65535,
|
|
||||||
message: 'Port must be between 1025 and 65535'
|
|
||||||
}
|
|
||||||
],
|
|
||||||
password: [
|
|
||||||
{ required: true, message: 'Password is required' },
|
|
||||||
{
|
|
||||||
type: 'string',
|
|
||||||
min: 1,
|
|
||||||
max: 64,
|
|
||||||
message: 'Password must be between 1 and 64 characters'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
@@ -6,18 +6,13 @@ AuthenticationService::AuthenticationService(AsyncWebServer * server, SecurityMa
|
|||||||
server->on(SIGN_IN_PATH, [this](AsyncWebServerRequest * request, JsonVariant json) { signIn(request, json); });
|
server->on(SIGN_IN_PATH, [this](AsyncWebServerRequest * request, JsonVariant json) { signIn(request, json); });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// Verifies that the request supplied a valid JWT.
|
||||||
* Verifies that the request supplied a valid JWT.
|
|
||||||
*/
|
|
||||||
void AuthenticationService::verifyAuthorization(AsyncWebServerRequest * request) {
|
void AuthenticationService::verifyAuthorization(AsyncWebServerRequest * request) {
|
||||||
Authentication authentication = _securityManager->authenticateRequest(request);
|
Authentication authentication = _securityManager->authenticateRequest(request);
|
||||||
request->send(authentication.authenticated ? 200 : 401);
|
request->send(authentication.authenticated ? 200 : 401);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// Signs in a user if the username and password match. Provides a JWT to be used in the Authorization header in subsequent requests.
|
||||||
* Signs in a user if the username and password match. Provides a JWT to be used in the Authorization header in
|
|
||||||
* subsequent requests.
|
|
||||||
*/
|
|
||||||
void AuthenticationService::signIn(AsyncWebServerRequest * request, JsonVariant json) {
|
void AuthenticationService::signIn(AsyncWebServerRequest * request, JsonVariant json) {
|
||||||
if (json.is<JsonObject>()) {
|
if (json.is<JsonObject>()) {
|
||||||
String username = json["username"];
|
String username = json["username"];
|
||||||
@@ -33,6 +28,7 @@ void AuthenticationService::signIn(AsyncWebServerRequest * request, JsonVariant
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AsyncWebServerResponse * response = request->beginResponse(401);
|
AsyncWebServerResponse * response = request->beginResponse(401);
|
||||||
request->send(response);
|
request->send(response);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ ESP8266React::ESP8266React(AsyncWebServer * server, FS * fs)
|
|||||||
, _apStatus(server, &_securitySettingsService, &_apSettingsService)
|
, _apStatus(server, &_securitySettingsService, &_apSettingsService)
|
||||||
, _ntpSettingsService(server, fs, &_securitySettingsService)
|
, _ntpSettingsService(server, fs, &_securitySettingsService)
|
||||||
, _ntpStatus(server, &_securitySettingsService)
|
, _ntpStatus(server, &_securitySettingsService)
|
||||||
, _otaSettingsService(server, fs, &_securitySettingsService)
|
|
||||||
, _uploadFileService(server, &_securitySettingsService)
|
, _uploadFileService(server, &_securitySettingsService)
|
||||||
, _mqttSettingsService(server, fs, &_securitySettingsService)
|
, _mqttSettingsService(server, fs, &_securitySettingsService)
|
||||||
, _mqttStatus(server, &_mqttSettingsService, &_securitySettingsService)
|
, _mqttStatus(server, &_mqttSettingsService, &_securitySettingsService)
|
||||||
@@ -76,7 +75,6 @@ void ESP8266React::begin() {
|
|||||||
});
|
});
|
||||||
_apSettingsService.begin();
|
_apSettingsService.begin();
|
||||||
_ntpSettingsService.begin();
|
_ntpSettingsService.begin();
|
||||||
_otaSettingsService.begin();
|
|
||||||
_mqttSettingsService.begin();
|
_mqttSettingsService.begin();
|
||||||
_securitySettingsService.begin();
|
_securitySettingsService.begin();
|
||||||
}
|
}
|
||||||
@@ -84,6 +82,5 @@ void ESP8266React::begin() {
|
|||||||
void ESP8266React::loop() {
|
void ESP8266React::loop() {
|
||||||
_networkSettingsService.loop();
|
_networkSettingsService.loop();
|
||||||
_apSettingsService.loop();
|
_apSettingsService.loop();
|
||||||
_otaSettingsService.loop();
|
|
||||||
_mqttSettingsService.loop();
|
_mqttSettingsService.loop();
|
||||||
}
|
}
|
||||||
@@ -9,7 +9,6 @@
|
|||||||
#include "MqttStatus.h"
|
#include "MqttStatus.h"
|
||||||
#include "NTPSettingsService.h"
|
#include "NTPSettingsService.h"
|
||||||
#include "NTPStatus.h"
|
#include "NTPStatus.h"
|
||||||
#include "OTASettingsService.h"
|
|
||||||
#include "UploadFileService.h"
|
#include "UploadFileService.h"
|
||||||
#include "RestartService.h"
|
#include "RestartService.h"
|
||||||
#include "SecuritySettingsService.h"
|
#include "SecuritySettingsService.h"
|
||||||
@@ -48,10 +47,6 @@ class ESP8266React {
|
|||||||
return &_ntpSettingsService;
|
return &_ntpSettingsService;
|
||||||
}
|
}
|
||||||
|
|
||||||
StatefulService<OTASettings> * getOTASettingsService() {
|
|
||||||
return &_otaSettingsService;
|
|
||||||
}
|
|
||||||
|
|
||||||
StatefulService<MqttSettings> * getMqttSettingsService() {
|
StatefulService<MqttSettings> * getMqttSettingsService() {
|
||||||
return &_mqttSettingsService;
|
return &_mqttSettingsService;
|
||||||
}
|
}
|
||||||
@@ -88,7 +83,6 @@ class ESP8266React {
|
|||||||
APStatus _apStatus;
|
APStatus _apStatus;
|
||||||
NTPSettingsService _ntpSettingsService;
|
NTPSettingsService _ntpSettingsService;
|
||||||
NTPStatus _ntpStatus;
|
NTPStatus _ntpStatus;
|
||||||
OTASettingsService _otaSettingsService;
|
|
||||||
UploadFileService _uploadFileService;
|
UploadFileService _uploadFileService;
|
||||||
MqttSettingsService _mqttSettingsService;
|
MqttSettingsService _mqttSettingsService;
|
||||||
MqttStatus _mqttStatus;
|
MqttStatus _mqttStatus;
|
||||||
|
|||||||
@@ -21,15 +21,9 @@
|
|||||||
#define FT_NTP 1
|
#define FT_NTP 1
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// ota feature on by default
|
|
||||||
#ifndef FT_OTA
|
|
||||||
#define FT_OTA 1
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// upload firmware/file feature on by default
|
// upload firmware/file feature on by default
|
||||||
#ifndef FT_UPLOAD_FIRMWARE
|
#ifndef FT_UPLOAD_FIRMWARE
|
||||||
#define FT_UPLOAD_FIRMWARE 1
|
#define FT_UPLOAD_FIRMWARE 1
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -1,87 +0,0 @@
|
|||||||
#include "OTASettingsService.h"
|
|
||||||
|
|
||||||
#include "../../src/emsesp_stub.hpp"
|
|
||||||
|
|
||||||
OTASettingsService::OTASettingsService(AsyncWebServer * server, FS * fs, SecurityManager * securityManager)
|
|
||||||
: _httpEndpoint(OTASettings::read, OTASettings::update, this, server, OTA_SETTINGS_SERVICE_PATH, securityManager)
|
|
||||||
, _fsPersistence(OTASettings::read, OTASettings::update, this, fs, OTA_SETTINGS_FILE)
|
|
||||||
, _arduinoOTA(nullptr) {
|
|
||||||
WiFi.onEvent([this](WiFiEvent_t event, WiFiEventInfo_t info) { WiFiEvent(event); });
|
|
||||||
addUpdateHandler([this] { configureArduinoOTA(); }, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
void OTASettingsService::begin() {
|
|
||||||
_fsPersistence.readFromFS();
|
|
||||||
configureArduinoOTA();
|
|
||||||
}
|
|
||||||
|
|
||||||
void OTASettingsService::loop() {
|
|
||||||
if (_state.enabled && _arduinoOTA) {
|
|
||||||
_arduinoOTA->handle();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void OTASettingsService::configureArduinoOTA() {
|
|
||||||
if (_arduinoOTA) {
|
|
||||||
_arduinoOTA->end();
|
|
||||||
delete _arduinoOTA;
|
|
||||||
_arduinoOTA = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_state.enabled) {
|
|
||||||
_arduinoOTA = new ArduinoOTAClass;
|
|
||||||
_arduinoOTA->setPort(static_cast<uint16_t>(_state.port));
|
|
||||||
_arduinoOTA->setPassword(_state.password.c_str());
|
|
||||||
|
|
||||||
_arduinoOTA->onStart([] { emsesp::EMSESP::system_.upload_status(true); });
|
|
||||||
_arduinoOTA->onEnd([] { emsesp::EMSESP::system_.upload_status(false); });
|
|
||||||
|
|
||||||
_arduinoOTA->onProgress([](unsigned int progress, unsigned int total) {
|
|
||||||
// Serial.printf("Progress: %u%%\r\n", (progress / (total / 100)));
|
|
||||||
});
|
|
||||||
_arduinoOTA->onError([](ota_error_t error) {
|
|
||||||
/*
|
|
||||||
#if defined(EMSESP_DEBUG)
|
|
||||||
Serial.printf("Error[%u]: ", error);
|
|
||||||
if (error == OTA_AUTH_ERROR)
|
|
||||||
Serial.println("Auth Failed");
|
|
||||||
else if (error == OTA_BEGIN_ERROR)
|
|
||||||
Serial.println("Begin Failed");
|
|
||||||
else if (error == OTA_CONNECT_ERROR)
|
|
||||||
Serial.println("Connect Failed");
|
|
||||||
else if (error == OTA_RECEIVE_ERROR)
|
|
||||||
Serial.println("Receive Failed");
|
|
||||||
else if (error == OTA_END_ERROR)
|
|
||||||
Serial.println("End Failed");
|
|
||||||
#endif
|
|
||||||
*/
|
|
||||||
});
|
|
||||||
|
|
||||||
_arduinoOTA->setMdnsEnabled(false); // disable as handled in NetworkSettingsService.cpp. https://github.com/emsesp/EMS-ESP32/issues/161
|
|
||||||
_arduinoOTA->begin();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void OTASettingsService::WiFiEvent(WiFiEvent_t event) {
|
|
||||||
switch (event) {
|
|
||||||
case ARDUINO_EVENT_WIFI_STA_GOT_IP:
|
|
||||||
case ARDUINO_EVENT_ETH_GOT_IP:
|
|
||||||
configureArduinoOTA();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void OTASettings::read(OTASettings & settings, JsonObject root) {
|
|
||||||
root["enabled"] = settings.enabled;
|
|
||||||
root["port"] = settings.port;
|
|
||||||
root["password"] = settings.password;
|
|
||||||
}
|
|
||||||
|
|
||||||
StateUpdateResult OTASettings::update(JsonObject root, OTASettings & settings) {
|
|
||||||
settings.enabled = root["enabled"] | FACTORY_OTA_ENABLED;
|
|
||||||
settings.port = root["port"] | FACTORY_OTA_PORT;
|
|
||||||
settings.password = root["password"] | FACTORY_OTA_PASSWORD;
|
|
||||||
return StateUpdateResult::CHANGED;
|
|
||||||
}
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
#ifndef OTASettingsService_h
|
|
||||||
#define OTASettingsService_h
|
|
||||||
|
|
||||||
#include "HttpEndpoint.h"
|
|
||||||
#include "FSPersistence.h"
|
|
||||||
|
|
||||||
#include <ArduinoOTA.h>
|
|
||||||
#include <WiFiUdp.h>
|
|
||||||
|
|
||||||
#ifndef FACTORY_OTA_PORT
|
|
||||||
#define FACTORY_OTA_PORT 8266
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef FACTORY_OTA_PASSWORD
|
|
||||||
#define FACTORY_OTA_PASSWORD "ems-esp-neo"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef FACTORY_OTA_ENABLED
|
|
||||||
#define FACTORY_OTA_ENABLED false
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define OTA_SETTINGS_FILE "/config/otaSettings.json"
|
|
||||||
#define OTA_SETTINGS_SERVICE_PATH "/rest/otaSettings"
|
|
||||||
|
|
||||||
class OTASettings {
|
|
||||||
public:
|
|
||||||
bool enabled;
|
|
||||||
int port;
|
|
||||||
String password;
|
|
||||||
|
|
||||||
static void read(OTASettings & settings, JsonObject root);
|
|
||||||
static StateUpdateResult update(JsonObject root, OTASettings & settings);
|
|
||||||
};
|
|
||||||
|
|
||||||
class OTASettingsService : public StatefulService<OTASettings> {
|
|
||||||
public:
|
|
||||||
OTASettingsService(AsyncWebServer * server, FS * fs, SecurityManager * securityManager);
|
|
||||||
|
|
||||||
void begin();
|
|
||||||
void loop();
|
|
||||||
|
|
||||||
private:
|
|
||||||
HttpEndpoint<OTASettings> _httpEndpoint;
|
|
||||||
FSPersistence<OTASettings> _fsPersistence;
|
|
||||||
ArduinoOTAClass * _arduinoOTA;
|
|
||||||
|
|
||||||
void configureArduinoOTA();
|
|
||||||
void WiFiEvent(WiFiEvent_t event);
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -71,7 +71,6 @@ class SecuritySettingsService final : public StatefulService<SecuritySettings>,
|
|||||||
|
|
||||||
void begin();
|
void begin();
|
||||||
|
|
||||||
// Functions to implement SecurityManager
|
|
||||||
Authentication authenticate(const String & username, const String & password) override;
|
Authentication authenticate(const String & username, const String & password) override;
|
||||||
Authentication authenticateRequest(AsyncWebServerRequest * request) override;
|
Authentication authenticateRequest(AsyncWebServerRequest * request) override;
|
||||||
String generateJWT(const User * user) override;
|
String generateJWT(const User * user) override;
|
||||||
@@ -88,15 +87,9 @@ class SecuritySettingsService final : public StatefulService<SecuritySettings>,
|
|||||||
|
|
||||||
void configureJWTHandler();
|
void configureJWTHandler();
|
||||||
|
|
||||||
/*
|
Authentication authenticateJWT(String & jwt); // Lookup the user by JWT
|
||||||
* Lookup the user by JWT
|
|
||||||
*/
|
|
||||||
Authentication authenticateJWT(String & jwt);
|
|
||||||
|
|
||||||
/*
|
boolean validatePayload(JsonObject parsedPayload, const User * user); // Verify the payload is correct
|
||||||
* Verify the payload is correct
|
|
||||||
*/
|
|
||||||
boolean validatePayload(JsonObject parsedPayload, const User * user);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
#include "UploadFileService.h"
|
#include "UploadFileService.h"
|
||||||
|
|
||||||
#include "../../src/emsesp_stub.hpp"
|
#include "../../src/emsesp_stub.hpp"
|
||||||
|
|
||||||
#include <esp_app_format.h>
|
#include <esp_app_format.h>
|
||||||
@@ -84,6 +85,7 @@ void UploadFileService::handleUpload(AsyncWebServerRequest * request, const Stri
|
|||||||
Update.setMD5(_md5.data());
|
Update.setMD5(_md5.data());
|
||||||
_md5.front() = '\0';
|
_md5.front() = '\0';
|
||||||
}
|
}
|
||||||
|
// emsesp::EMSESP::system_.upload_status(true); // force just in case, this is stop UART, MQTT and other services
|
||||||
request->onDisconnect([this] { handleEarlyDisconnect(); }); // success, let's make sure we end the update if the client hangs up
|
request->onDisconnect([this] { handleEarlyDisconnect(); }); // success, let's make sure we end the update if the client hangs up
|
||||||
} else {
|
} else {
|
||||||
handleError(request, 507); // failed to begin, send an error response Insufficient Storage
|
handleError(request, 507); // failed to begin, send an error response Insufficient Storage
|
||||||
@@ -101,11 +103,11 @@ void UploadFileService::handleUpload(AsyncWebServerRequest * request, const Stri
|
|||||||
}
|
}
|
||||||
} else if (!request->_tempObject) { // if we haven't delt with an error, continue with the firmware update
|
} else if (!request->_tempObject) { // if we haven't delt with an error, continue with the firmware update
|
||||||
if (Update.write(data, len) != len) {
|
if (Update.write(data, len) != len) {
|
||||||
handleError(request, 500);
|
handleError(request, 500); // internal error, failed
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (final && !Update.end(true)) {
|
if (final && !Update.end(true)) {
|
||||||
handleError(request, 500);
|
handleError(request, 500); // internal error, failed
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,6 @@
|
|||||||
#define NETWORK_SETTINGS_FILE "/config/networkSettings.json"
|
#define NETWORK_SETTINGS_FILE "/config/networkSettings.json"
|
||||||
#define NTP_SETTINGS_FILE "/config/ntpSettings.json"
|
#define NTP_SETTINGS_FILE "/config/ntpSettings.json"
|
||||||
#define EMSESP_SETTINGS_FILE "/config/emsespSettings.json"
|
#define EMSESP_SETTINGS_FILE "/config/emsespSettings.json"
|
||||||
#define OTA_SETTINGS_FILE "/config/otaSettings.json"
|
|
||||||
|
|
||||||
class DummySettings {
|
class DummySettings {
|
||||||
public:
|
public:
|
||||||
@@ -97,10 +96,8 @@ class DummySettingsService : public StatefulService<DummySettings> {
|
|||||||
#define SecuritySettings DummySettings
|
#define SecuritySettings DummySettings
|
||||||
#define MqttSettings DummySettings
|
#define MqttSettings DummySettings
|
||||||
#define NTPSettings DummySettings
|
#define NTPSettings DummySettings
|
||||||
#define OTASettings DummySettings
|
|
||||||
#define APSettings DummySettings
|
#define APSettings DummySettings
|
||||||
|
|
||||||
|
|
||||||
class ESP8266React {
|
class ESP8266React {
|
||||||
public:
|
public:
|
||||||
ESP8266React(AsyncWebServer * server, FS * fs)
|
ESP8266React(AsyncWebServer * server, FS * fs)
|
||||||
@@ -145,10 +142,6 @@ class ESP8266React {
|
|||||||
return &_settings;
|
return &_settings;
|
||||||
}
|
}
|
||||||
|
|
||||||
StatefulService<DummySettings> * getOTASettingsService() {
|
|
||||||
return &_settings;
|
|
||||||
}
|
|
||||||
|
|
||||||
StatefulService<DummySettings> * getAPSettingsService() {
|
StatefulService<DummySettings> * getAPSettingsService() {
|
||||||
return &_settings;
|
return &_settings;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,11 +21,6 @@
|
|||||||
#define FT_NTP 0
|
#define FT_NTP 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// mqtt feature on by default
|
|
||||||
#ifndef FT_OTA
|
|
||||||
#define FT_OTA 0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// upload firmware/file feature off by default
|
// upload firmware/file feature off by default
|
||||||
#ifndef FT_UPLOAD_FIRMWARE
|
#ifndef FT_UPLOAD_FIRMWARE
|
||||||
#define FT_UPLOAD_FIRMWARE 0
|
#define FT_UPLOAD_FIRMWARE 0
|
||||||
|
|||||||
@@ -291,14 +291,6 @@ const list_networks = {
|
|||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
// OTA
|
|
||||||
const OTA_SETTINGS_ENDPOINT = REST_ENDPOINT_ROOT + 'otaSettings';
|
|
||||||
let ota_settings = {
|
|
||||||
enabled: false,
|
|
||||||
port: 8266,
|
|
||||||
password: 'ems-esp-neo'
|
|
||||||
};
|
|
||||||
|
|
||||||
// MQTT
|
// MQTT
|
||||||
const MQTT_SETTINGS_ENDPOINT = REST_ENDPOINT_ROOT + 'mqttSettings';
|
const MQTT_SETTINGS_ENDPOINT = REST_ENDPOINT_ROOT + 'mqttSettings';
|
||||||
const MQTT_STATUS_ENDPOINT = REST_ENDPOINT_ROOT + 'mqttStatus';
|
const MQTT_STATUS_ENDPOINT = REST_ENDPOINT_ROOT + 'mqttStatus';
|
||||||
@@ -389,7 +381,6 @@ const system_status = {
|
|||||||
num_analogs: 1,
|
num_analogs: 1,
|
||||||
free_heap: 143,
|
free_heap: 143,
|
||||||
ntp_status: 2,
|
ntp_status: 2,
|
||||||
ota_status: false,
|
|
||||||
mqtt_status: true,
|
mqtt_status: true,
|
||||||
ap_status: false
|
ap_status: false
|
||||||
};
|
};
|
||||||
@@ -2339,15 +2330,6 @@ router
|
|||||||
return status(200);
|
return status(200);
|
||||||
});
|
});
|
||||||
|
|
||||||
// OTA
|
|
||||||
router
|
|
||||||
.get(OTA_SETTINGS_ENDPOINT, () => ota_settings)
|
|
||||||
.post(OTA_SETTINGS_ENDPOINT, async (request: any) => {
|
|
||||||
ota_settings = await request.json();
|
|
||||||
console.log('ota settings saved', ota_settings);
|
|
||||||
return status(200);
|
|
||||||
});
|
|
||||||
|
|
||||||
// MQTT
|
// MQTT
|
||||||
router
|
router
|
||||||
.get(MQTT_SETTINGS_ENDPOINT, () => mqtt_settings)
|
.get(MQTT_SETTINGS_ENDPOINT, () => mqtt_settings)
|
||||||
|
|||||||
@@ -23,21 +23,24 @@ default_envs = esp32_4M
|
|||||||
; default_envs = debug
|
; default_envs = debug
|
||||||
; default_envs = custom
|
; default_envs = custom
|
||||||
|
|
||||||
|
[env]
|
||||||
|
; upload settings
|
||||||
|
upload_protocol = custom
|
||||||
|
custom_emsesp_ip = 10.10.10.173
|
||||||
|
custom_username = admin
|
||||||
|
custom_password = admin
|
||||||
|
upload_port = /dev/ttyUSB*
|
||||||
|
|
||||||
[env:esp32_4M]
|
[env:esp32_4M]
|
||||||
; if using OTA enter your details below
|
|
||||||
; upload_protocol = espota
|
|
||||||
; upload_flags =
|
|
||||||
; --port=8266
|
|
||||||
; --auth=ems-esp-neo
|
|
||||||
; upload_port = ems-esp.local
|
|
||||||
; for USB, here are some examples:
|
|
||||||
; upload_port = /dev/ttyUSB*
|
|
||||||
; upload_port = COM5
|
|
||||||
extra_scripts =
|
extra_scripts =
|
||||||
pre:scripts/build_interface.py ; comment out if you don't want to re-build the WebUI each time
|
pre:scripts/build_interface.py ; comment out if you don't want to re-build the WebUI each time
|
||||||
scripts/rename_fw.py
|
scripts/rename_fw.py
|
||||||
|
scripts/upload.py
|
||||||
|
|
||||||
[env:esp32_16M]
|
[env:lolin_s3]
|
||||||
|
extra_scripts =
|
||||||
|
; pre:scripts/build_interface.py ; comment out if you don't want to re-build the WebUI each time
|
||||||
|
scripts/rename_fw.py
|
||||||
|
|
||||||
[env:custom]
|
[env:custom]
|
||||||
; use for basic ESP boards with 4MB flash
|
; use for basic ESP boards with 4MB flash
|
||||||
@@ -75,12 +78,6 @@ build_flags =
|
|||||||
-D CONFIG_ASYNC_TCP_STACK_SIZE=8192
|
-D CONFIG_ASYNC_TCP_STACK_SIZE=8192
|
||||||
'-DEMSESP_DEFAULT_BOARD_PROFILE="Test"'
|
'-DEMSESP_DEFAULT_BOARD_PROFILE="Test"'
|
||||||
|
|
||||||
[env:lolin_s3]
|
|
||||||
upload_port = /dev/ttyUSB0
|
|
||||||
extra_scripts =
|
|
||||||
pre:scripts/build_interface.py ; comment out if you don't want to re-build the WebUI each time
|
|
||||||
scripts/rename_fw.py
|
|
||||||
|
|
||||||
; pio run -e debug
|
; pio run -e debug
|
||||||
; or from Visual Studio Code do PIO -> Project Tasks -> debug -> General -> Upload and Monitor
|
; or from Visual Studio Code do PIO -> Project Tasks -> debug -> General -> Upload and Monitor
|
||||||
; options for debugging are: EMSESP_DEBUG EMSESP_UART_DEBUG EMSESP_DEBUG_SENSOR
|
; options for debugging are: EMSESP_DEBUG EMSESP_UART_DEBUG EMSESP_DEBUG_SENSOR
|
||||||
|
|||||||
@@ -1,343 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
#
|
|
||||||
# Original espota.py by Ivan Grokhotkov:
|
|
||||||
# https://gist.github.com/igrr/d35ab8446922179dc58c
|
|
||||||
#
|
|
||||||
# Modified since 2015-09-18 from Pascal Gollor (https://github.com/pgollor)
|
|
||||||
# Modified since 2015-11-09 from Hristo Gochkov (https://github.com/me-no-dev)
|
|
||||||
# Modified since 2016-01-03 from Matthew O'Gorman (https://githumb.com/mogorman)
|
|
||||||
#
|
|
||||||
# This script will push an OTA update to the ESP
|
|
||||||
# use it like: python3 espota.py -i <ESP_IP_address> -I <Host_IP_address> -p <ESP_port> -P <Host_port> [-a password] -f <sketch.bin>
|
|
||||||
# Or to upload SPIFFS image:
|
|
||||||
# python3 espota.py -i <ESP_IP_address> -I <Host_IP_address> -p <ESP_port> -P <HOST_port> [-a password] -s -f <spiffs.bin>
|
|
||||||
#
|
|
||||||
# Changes
|
|
||||||
# 2015-09-18:
|
|
||||||
# - Add option parser.
|
|
||||||
# - Add logging.
|
|
||||||
# - Send command to controller to differ between flashing and transmitting SPIFFS image.
|
|
||||||
#
|
|
||||||
# Changes
|
|
||||||
# 2015-11-09:
|
|
||||||
# - Added digest authentication
|
|
||||||
# - Enhanced error tracking and reporting
|
|
||||||
#
|
|
||||||
# Changes
|
|
||||||
# 2016-01-03:
|
|
||||||
# - Added more options to parser.
|
|
||||||
#
|
|
||||||
|
|
||||||
from __future__ import print_function
|
|
||||||
import socket
|
|
||||||
import sys
|
|
||||||
import os
|
|
||||||
import optparse
|
|
||||||
import logging
|
|
||||||
import hashlib
|
|
||||||
import random
|
|
||||||
|
|
||||||
# Commands
|
|
||||||
FLASH = 0
|
|
||||||
SPIFFS = 100
|
|
||||||
AUTH = 200
|
|
||||||
PROGRESS = False
|
|
||||||
# update_progress() : Displays or updates a console progress bar
|
|
||||||
## Accepts a float between 0 and 1. Any int will be converted to a float.
|
|
||||||
## A value under 0 represents a 'halt'.
|
|
||||||
## A value at 1 or bigger represents 100%
|
|
||||||
def update_progress(progress):
|
|
||||||
if (PROGRESS):
|
|
||||||
barLength = 60 # Modify this to change the length of the progress bar
|
|
||||||
status = ""
|
|
||||||
if isinstance(progress, int):
|
|
||||||
progress = float(progress)
|
|
||||||
if not isinstance(progress, float):
|
|
||||||
progress = 0
|
|
||||||
status = "error: progress var must be float\r\n"
|
|
||||||
if progress < 0:
|
|
||||||
progress = 0
|
|
||||||
status = "Halt...\r\n"
|
|
||||||
if progress >= 1:
|
|
||||||
progress = 1
|
|
||||||
status = "Done...\r\n"
|
|
||||||
block = int(round(barLength*progress))
|
|
||||||
text = "\rUploading: [{0}] {1}% {2}".format( "="*block + " "*(barLength-block), int(progress*100), status)
|
|
||||||
sys.stderr.write(text)
|
|
||||||
sys.stderr.flush()
|
|
||||||
else:
|
|
||||||
sys.stderr.write('.')
|
|
||||||
sys.stderr.flush()
|
|
||||||
|
|
||||||
def serve(remoteAddr, localAddr, remotePort, localPort, password, filename, command = FLASH):
|
|
||||||
# Create a TCP/IP socket
|
|
||||||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
||||||
server_address = (localAddr, localPort)
|
|
||||||
logging.info('Starting on %s:%s', str(server_address[0]), str(server_address[1]))
|
|
||||||
try:
|
|
||||||
sock.bind(server_address)
|
|
||||||
sock.listen(1)
|
|
||||||
except:
|
|
||||||
logging.error("Listen Failed")
|
|
||||||
return 1
|
|
||||||
|
|
||||||
# Check whether Signed Update is used.
|
|
||||||
if ( os.path.isfile(filename + '.signed') ):
|
|
||||||
filename = filename + '.signed'
|
|
||||||
file_check_msg = 'Detected Signed Update. %s will be uploaded instead.' % (filename)
|
|
||||||
sys.stderr.write(file_check_msg + '\n')
|
|
||||||
sys.stderr.flush()
|
|
||||||
logging.info(file_check_msg)
|
|
||||||
|
|
||||||
content_size = os.path.getsize(filename)
|
|
||||||
f = open(filename,'rb')
|
|
||||||
file_md5 = hashlib.md5(f.read()).hexdigest()
|
|
||||||
f.close()
|
|
||||||
logging.info('Upload size: %d', content_size)
|
|
||||||
message = '%d %d %d %s\n' % (command, localPort, content_size, file_md5)
|
|
||||||
|
|
||||||
# Wait for a connection
|
|
||||||
logging.info('Sending invitation to: %s', remoteAddr)
|
|
||||||
sock2 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
|
||||||
remote_address = (remoteAddr, int(remotePort))
|
|
||||||
sent = sock2.sendto(message.encode(), remote_address)
|
|
||||||
sock2.settimeout(10)
|
|
||||||
try:
|
|
||||||
data = sock2.recv(128).decode()
|
|
||||||
except:
|
|
||||||
logging.error('No Answer')
|
|
||||||
sock2.close()
|
|
||||||
return 1
|
|
||||||
if (data != "OK"):
|
|
||||||
if(data.startswith('AUTH')):
|
|
||||||
nonce = data.split()[1]
|
|
||||||
cnonce_text = '%s%u%s%s' % (filename, content_size, file_md5, remoteAddr)
|
|
||||||
cnonce = hashlib.md5(cnonce_text.encode()).hexdigest()
|
|
||||||
passmd5 = hashlib.md5(password.encode()).hexdigest()
|
|
||||||
result_text = '%s:%s:%s' % (passmd5 ,nonce, cnonce)
|
|
||||||
result = hashlib.md5(result_text.encode()).hexdigest()
|
|
||||||
sys.stderr.write('Authenticating...')
|
|
||||||
sys.stderr.flush()
|
|
||||||
message = '%d %s %s\n' % (AUTH, cnonce, result)
|
|
||||||
sock2.sendto(message.encode(), remote_address)
|
|
||||||
sock2.settimeout(10)
|
|
||||||
try:
|
|
||||||
data = sock2.recv(32).decode()
|
|
||||||
except:
|
|
||||||
sys.stderr.write('FAIL\n')
|
|
||||||
logging.error('No Answer to our Authentication')
|
|
||||||
sock2.close()
|
|
||||||
return 1
|
|
||||||
if (data != "OK"):
|
|
||||||
sys.stderr.write('FAIL\n')
|
|
||||||
logging.error('%s', data)
|
|
||||||
sock2.close()
|
|
||||||
sys.exit(1)
|
|
||||||
return 1
|
|
||||||
sys.stderr.write('OK\n')
|
|
||||||
else:
|
|
||||||
logging.error('Bad Answer: %s', data)
|
|
||||||
sock2.close()
|
|
||||||
return 1
|
|
||||||
sock2.close()
|
|
||||||
|
|
||||||
logging.info('Waiting for device...')
|
|
||||||
try:
|
|
||||||
sock.settimeout(10)
|
|
||||||
connection, client_address = sock.accept()
|
|
||||||
sock.settimeout(None)
|
|
||||||
connection.settimeout(None)
|
|
||||||
except:
|
|
||||||
logging.error('No response from device')
|
|
||||||
sock.close()
|
|
||||||
return 1
|
|
||||||
|
|
||||||
received_ok = False
|
|
||||||
|
|
||||||
try:
|
|
||||||
f = open(filename, "rb")
|
|
||||||
if (PROGRESS):
|
|
||||||
update_progress(0)
|
|
||||||
else:
|
|
||||||
sys.stderr.write('Uploading')
|
|
||||||
sys.stderr.flush()
|
|
||||||
offset = 0
|
|
||||||
while True:
|
|
||||||
chunk = f.read(1460)
|
|
||||||
if not chunk: break
|
|
||||||
offset += len(chunk)
|
|
||||||
update_progress(offset/float(content_size))
|
|
||||||
connection.settimeout(10)
|
|
||||||
try:
|
|
||||||
connection.sendall(chunk)
|
|
||||||
if connection.recv(32).decode().find('O') >= 0:
|
|
||||||
# connection will receive only digits or 'OK'
|
|
||||||
received_ok = True
|
|
||||||
except:
|
|
||||||
sys.stderr.write('\n')
|
|
||||||
logging.error('Error Uploading')
|
|
||||||
connection.close()
|
|
||||||
f.close()
|
|
||||||
sock.close()
|
|
||||||
return 1
|
|
||||||
|
|
||||||
sys.stderr.write('\n')
|
|
||||||
logging.info('Waiting for result...')
|
|
||||||
# libraries/ArduinoOTA/ArduinoOTA.cpp L311 L320
|
|
||||||
# only sends digits or 'OK'. We must not not close
|
|
||||||
# the connection before receiving the 'O' of 'OK'
|
|
||||||
try:
|
|
||||||
connection.settimeout(60)
|
|
||||||
received_ok = False
|
|
||||||
received_error = False
|
|
||||||
while not (received_ok or received_error):
|
|
||||||
reply = connection.recv(64).decode()
|
|
||||||
# Look for either the "E" in ERROR or the "O" in OK response
|
|
||||||
# Check for "E" first, since both strings contain "O"
|
|
||||||
if reply.find('E') >= 0:
|
|
||||||
sys.stderr.write('\n')
|
|
||||||
logging.error('%s', reply)
|
|
||||||
received_error = True
|
|
||||||
elif reply.find('O') >= 0:
|
|
||||||
logging.info('Result: OK')
|
|
||||||
received_ok = True
|
|
||||||
connection.close()
|
|
||||||
f.close()
|
|
||||||
sock.close()
|
|
||||||
if received_ok:
|
|
||||||
return 0
|
|
||||||
return 1
|
|
||||||
except:
|
|
||||||
logging.error('No Result!')
|
|
||||||
connection.close()
|
|
||||||
f.close()
|
|
||||||
sock.close()
|
|
||||||
return 1
|
|
||||||
|
|
||||||
finally:
|
|
||||||
connection.close()
|
|
||||||
f.close()
|
|
||||||
|
|
||||||
sock.close()
|
|
||||||
return 1
|
|
||||||
# end serve
|
|
||||||
|
|
||||||
|
|
||||||
def parser(unparsed_args):
|
|
||||||
parser = optparse.OptionParser(
|
|
||||||
usage = "%prog [options]",
|
|
||||||
description = "Transmit image over the air to the esp8266 module with OTA support."
|
|
||||||
)
|
|
||||||
|
|
||||||
# destination ip and port
|
|
||||||
group = optparse.OptionGroup(parser, "Destination")
|
|
||||||
group.add_option("-i", "--ip",
|
|
||||||
dest = "esp_ip",
|
|
||||||
action = "store",
|
|
||||||
help = "ESP8266 IP Address.",
|
|
||||||
default = False
|
|
||||||
)
|
|
||||||
group.add_option("-I", "--host_ip",
|
|
||||||
dest = "host_ip",
|
|
||||||
action = "store",
|
|
||||||
help = "Host IP Address.",
|
|
||||||
default = "0.0.0.0"
|
|
||||||
)
|
|
||||||
group.add_option("-p", "--port",
|
|
||||||
dest = "esp_port",
|
|
||||||
type = "int",
|
|
||||||
help = "ESP8266 ota Port. Default 8266",
|
|
||||||
default = 8266
|
|
||||||
)
|
|
||||||
group.add_option("-P", "--host_port",
|
|
||||||
dest = "host_port",
|
|
||||||
type = "int",
|
|
||||||
help = "Host server ota Port. Default random 10000-60000",
|
|
||||||
default = random.randint(10000,60000)
|
|
||||||
)
|
|
||||||
parser.add_option_group(group)
|
|
||||||
|
|
||||||
# auth
|
|
||||||
group = optparse.OptionGroup(parser, "Authentication")
|
|
||||||
group.add_option("-a", "--auth",
|
|
||||||
dest = "auth",
|
|
||||||
help = "Set authentication password.",
|
|
||||||
action = "store",
|
|
||||||
default = ""
|
|
||||||
)
|
|
||||||
parser.add_option_group(group)
|
|
||||||
|
|
||||||
# image
|
|
||||||
group = optparse.OptionGroup(parser, "Image")
|
|
||||||
group.add_option("-f", "--file",
|
|
||||||
dest = "image",
|
|
||||||
help = "Image file.",
|
|
||||||
metavar="FILE",
|
|
||||||
default = None
|
|
||||||
)
|
|
||||||
group.add_option("-s", "--spiffs",
|
|
||||||
dest = "spiffs",
|
|
||||||
action = "store_true",
|
|
||||||
help = "Use this option to transmit a SPIFFS image and do not flash the module.",
|
|
||||||
default = False
|
|
||||||
)
|
|
||||||
parser.add_option_group(group)
|
|
||||||
|
|
||||||
# output group
|
|
||||||
group = optparse.OptionGroup(parser, "Output")
|
|
||||||
group.add_option("-d", "--debug",
|
|
||||||
dest = "debug",
|
|
||||||
help = "Show debug output. And override loglevel with debug.",
|
|
||||||
action = "store_true",
|
|
||||||
default = False
|
|
||||||
)
|
|
||||||
group.add_option("-r", "--progress",
|
|
||||||
dest = "progress",
|
|
||||||
help = "Show progress output. Does not work for ArduinoIDE",
|
|
||||||
action = "store_true",
|
|
||||||
default = False
|
|
||||||
)
|
|
||||||
parser.add_option_group(group)
|
|
||||||
|
|
||||||
(options, args) = parser.parse_args(unparsed_args)
|
|
||||||
|
|
||||||
return options
|
|
||||||
# end parser
|
|
||||||
|
|
||||||
|
|
||||||
def main(args):
|
|
||||||
# get options
|
|
||||||
options = parser(args)
|
|
||||||
|
|
||||||
# adapt log level
|
|
||||||
loglevel = logging.WARNING
|
|
||||||
if (options.debug):
|
|
||||||
loglevel = logging.DEBUG
|
|
||||||
# end if
|
|
||||||
|
|
||||||
# logging
|
|
||||||
logging.basicConfig(level = loglevel, format = '%(asctime)-8s [%(levelname)s]: %(message)s', datefmt = '%H:%M:%S')
|
|
||||||
|
|
||||||
logging.debug("Options: %s", str(options))
|
|
||||||
|
|
||||||
# check options
|
|
||||||
global PROGRESS
|
|
||||||
PROGRESS = options.progress
|
|
||||||
if (not options.esp_ip or not options.image):
|
|
||||||
logging.critical("Not enough arguments.")
|
|
||||||
|
|
||||||
return 1
|
|
||||||
# end if
|
|
||||||
|
|
||||||
command = FLASH
|
|
||||||
if (options.spiffs):
|
|
||||||
command = SPIFFS
|
|
||||||
# end if
|
|
||||||
|
|
||||||
return serve(options.esp_ip, options.host_ip, options.esp_port, options.host_port, options.auth, options.image, command)
|
|
||||||
# end main
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
sys.exit(main(sys.argv))
|
|
||||||
# end if
|
|
||||||
6
scripts/requirements.txt
Normal file
6
scripts/requirements.txt
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
requests
|
||||||
|
termcolor
|
||||||
|
requests_toolbelt
|
||||||
|
tqdm
|
||||||
|
|
||||||
|
|
||||||
130
scripts/upload.py
Normal file
130
scripts/upload.py
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
|
||||||
|
# Modified from https://github.com/ayushsharma82/ElegantOTA
|
||||||
|
|
||||||
|
# This is called during the platformIO upload process
|
||||||
|
# To use create a pio_local.ini file in the project root and add the following:
|
||||||
|
# [env]
|
||||||
|
# upload_protocol = custom
|
||||||
|
# custom_emsesp_ip = 10.10.10.173
|
||||||
|
# custom_username = admin
|
||||||
|
# custom_password = admin
|
||||||
|
#
|
||||||
|
# and
|
||||||
|
# extra_scripts = scripts/upload.py
|
||||||
|
|
||||||
|
import requests
|
||||||
|
import hashlib
|
||||||
|
from urllib.parse import urlparse
|
||||||
|
import time
|
||||||
|
Import("env")
|
||||||
|
|
||||||
|
try:
|
||||||
|
from requests_toolbelt import MultipartEncoder, MultipartEncoderMonitor
|
||||||
|
from tqdm import tqdm
|
||||||
|
from termcolor import cprint
|
||||||
|
except ImportError:
|
||||||
|
env.Execute("$PYTHONEXE -m pip install requests_toolbelt")
|
||||||
|
env.Execute("$PYTHONEXE -m pip install tqdm")
|
||||||
|
env.Execute("$PYTHONEXE -m pip install termcolor")
|
||||||
|
from requests_toolbelt import MultipartEncoder, MultipartEncoderMonitor
|
||||||
|
from tqdm import tqdm
|
||||||
|
from termcolor import cprint
|
||||||
|
|
||||||
|
def print_success(x): return cprint(x, 'green')
|
||||||
|
def print_fail(x): return cprint(x, 'red')
|
||||||
|
|
||||||
|
def on_upload(source, target, env):
|
||||||
|
|
||||||
|
# first check authentication
|
||||||
|
try:
|
||||||
|
username = env.GetProjectOption('custom_username')
|
||||||
|
password = env.GetProjectOption('custom_password')
|
||||||
|
except:
|
||||||
|
print('No authentication settings specified. Please, add these to your pio_local.ini file: \n\ncustom_username=username\ncustom_password=password\n')
|
||||||
|
return
|
||||||
|
|
||||||
|
emsesp_url = "http://" + env.GetProjectOption('custom_emsesp_ip')
|
||||||
|
parsed_url = urlparse(emsesp_url)
|
||||||
|
host_ip = parsed_url.netloc
|
||||||
|
|
||||||
|
signon_url = f"{emsesp_url}/rest/signIn"
|
||||||
|
|
||||||
|
signon_headers = {
|
||||||
|
'Host': host_ip,
|
||||||
|
'User-Agent': 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/118.0',
|
||||||
|
'Accept': '*/*',
|
||||||
|
'Accept-Language': 'de,en-US;q=0.7,en;q=0.3',
|
||||||
|
'Accept-Encoding': 'gzip, deflate',
|
||||||
|
'Referer': f'{emsesp_url}',
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Connection': 'keep-alive'
|
||||||
|
}
|
||||||
|
|
||||||
|
username_password = {
|
||||||
|
"username": username,
|
||||||
|
"password": password
|
||||||
|
}
|
||||||
|
|
||||||
|
response = requests.post(signon_url, json=username_password, headers=signon_headers, auth=None)
|
||||||
|
|
||||||
|
if response.status_code != 200:
|
||||||
|
print_fail("Authentication failed (code " + str(response.status_code) + ")")
|
||||||
|
return
|
||||||
|
|
||||||
|
print_success("Authentication successful")
|
||||||
|
access_token = response.json().get('access_token')
|
||||||
|
|
||||||
|
# start the upload
|
||||||
|
firmware_path = str(source[0])
|
||||||
|
|
||||||
|
with open(firmware_path, 'rb') as firmware:
|
||||||
|
md5 = hashlib.md5(firmware.read()).hexdigest()
|
||||||
|
|
||||||
|
firmware.seek(0)
|
||||||
|
|
||||||
|
encoder = MultipartEncoder(fields={
|
||||||
|
'MD5': md5,
|
||||||
|
'file': (firmware_path, firmware, 'application/octet-stream')}
|
||||||
|
)
|
||||||
|
|
||||||
|
bar = tqdm(desc='Upload Progress',
|
||||||
|
total=encoder.len,
|
||||||
|
dynamic_ncols=True,
|
||||||
|
unit='B',
|
||||||
|
unit_scale=True,
|
||||||
|
unit_divisor=1024
|
||||||
|
)
|
||||||
|
|
||||||
|
monitor = MultipartEncoderMonitor(encoder, lambda monitor: bar.update(monitor.bytes_read - bar.n))
|
||||||
|
|
||||||
|
post_headers = {
|
||||||
|
'Host': host_ip,
|
||||||
|
'User-Agent': 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/118.0',
|
||||||
|
'Accept': '*/*',
|
||||||
|
'Accept-Language': 'de,en-US;q=0.7,en;q=0.3',
|
||||||
|
'Accept-Encoding': 'gzip, deflate',
|
||||||
|
'Referer': f'{emsesp_url}',
|
||||||
|
'Connection': 'keep-alive',
|
||||||
|
'Content-Type': monitor.content_type,
|
||||||
|
'Content-Length': str(monitor.len),
|
||||||
|
'Origin': f'{emsesp_url}',
|
||||||
|
'Authorization': 'Bearer ' + f'{access_token}'
|
||||||
|
}
|
||||||
|
|
||||||
|
upload_url = f"{emsesp_url}/rest/uploadFile"
|
||||||
|
|
||||||
|
response = requests.post(upload_url, data=monitor, headers=post_headers, auth=None)
|
||||||
|
|
||||||
|
bar.close()
|
||||||
|
time.sleep(0.1)
|
||||||
|
|
||||||
|
print()
|
||||||
|
|
||||||
|
if response.status_code != 200:
|
||||||
|
print_fail("Upload failed (code " + response.status.code + ").")
|
||||||
|
else:
|
||||||
|
print_success("Upload successful.")
|
||||||
|
|
||||||
|
print()
|
||||||
|
|
||||||
|
env.Replace(UPLOADCMD=on_upload)
|
||||||
119
scripts/upload_cli.py
Normal file
119
scripts/upload_cli.py
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
|
||||||
|
# Modified from https://github.com/ayushsharma82/ElegantOTA
|
||||||
|
#
|
||||||
|
# Requires Python (sudo apt install python3-pip)
|
||||||
|
# `python3 -m venv venv` to create the virtual environment
|
||||||
|
# `source ./venv/bin/activate` to enter it
|
||||||
|
# `pip install -r requirements.txt` to install the libraries
|
||||||
|
#
|
||||||
|
# Run using for example:
|
||||||
|
# python3 upload_cli.py -i 10.10.10.173 -f ../build/firmware/EMS-ESP-3_7_0-dev_8-ESP32_4M.bin
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import requests
|
||||||
|
import hashlib
|
||||||
|
from urllib.parse import urlparse
|
||||||
|
import time
|
||||||
|
from requests_toolbelt import MultipartEncoder, MultipartEncoderMonitor
|
||||||
|
from tqdm import tqdm
|
||||||
|
from termcolor import cprint
|
||||||
|
|
||||||
|
def print_success(x): return cprint(x, 'green')
|
||||||
|
def print_fail(x): return cprint(x, 'red')
|
||||||
|
|
||||||
|
def upload(file, ip, username, password):
|
||||||
|
|
||||||
|
# Print welcome message
|
||||||
|
print()
|
||||||
|
print("EMS-ESP Firmware Upload")
|
||||||
|
|
||||||
|
# first check authentication
|
||||||
|
emsesp_url = "http://" + f'{ip}'
|
||||||
|
parsed_url = urlparse(emsesp_url)
|
||||||
|
host_ip = parsed_url.netloc
|
||||||
|
|
||||||
|
signon_url = f"{emsesp_url}/rest/signIn"
|
||||||
|
|
||||||
|
signon_headers = {
|
||||||
|
'Host': host_ip,
|
||||||
|
'User-Agent': 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/118.0',
|
||||||
|
'Accept': '*/*',
|
||||||
|
'Accept-Language': 'de,en-US;q=0.7,en;q=0.3',
|
||||||
|
'Accept-Encoding': 'gzip, deflate',
|
||||||
|
'Referer': f'{emsesp_url}',
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Connection': 'keep-alive'
|
||||||
|
}
|
||||||
|
|
||||||
|
username_password = {
|
||||||
|
"username": username,
|
||||||
|
"password": password
|
||||||
|
}
|
||||||
|
|
||||||
|
response = requests.post(signon_url, json=username_password, headers=signon_headers, auth=None)
|
||||||
|
|
||||||
|
if response.status_code != 200:
|
||||||
|
print_fail("Authentication failed (code " + str(response.status_code) + ")")
|
||||||
|
return
|
||||||
|
|
||||||
|
print_success("Authentication successful")
|
||||||
|
access_token = response.json().get('access_token')
|
||||||
|
|
||||||
|
# start the upload
|
||||||
|
with open(file, 'rb') as firmware:
|
||||||
|
md5 = hashlib.md5(firmware.read()).hexdigest()
|
||||||
|
|
||||||
|
firmware.seek(0)
|
||||||
|
|
||||||
|
encoder = MultipartEncoder(fields={
|
||||||
|
'MD5': md5,
|
||||||
|
'file': (file, firmware, 'application/octet-stream')}
|
||||||
|
)
|
||||||
|
|
||||||
|
bar = tqdm(desc='Upload Progress',
|
||||||
|
total=encoder.len,
|
||||||
|
dynamic_ncols=True,
|
||||||
|
unit='B',
|
||||||
|
unit_scale=True,
|
||||||
|
unit_divisor=1024
|
||||||
|
)
|
||||||
|
|
||||||
|
monitor = MultipartEncoderMonitor(encoder, lambda monitor: bar.update(monitor.bytes_read - bar.n))
|
||||||
|
|
||||||
|
post_headers = {
|
||||||
|
'Host': host_ip,
|
||||||
|
'User-Agent': 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/118.0',
|
||||||
|
'Accept': '*/*',
|
||||||
|
'Accept-Language': 'de,en-US;q=0.7,en;q=0.3',
|
||||||
|
'Accept-Encoding': 'gzip, deflate',
|
||||||
|
'Referer': f'{emsesp_url}',
|
||||||
|
'Connection': 'keep-alive',
|
||||||
|
'Content-Type': monitor.content_type,
|
||||||
|
'Content-Length': str(monitor.len),
|
||||||
|
'Origin': f'{emsesp_url}',
|
||||||
|
'Authorization': 'Bearer ' + f'{access_token}'
|
||||||
|
}
|
||||||
|
|
||||||
|
upload_url = f"{emsesp_url}/rest/uploadFile"
|
||||||
|
|
||||||
|
response = requests.post(upload_url, data=monitor, headers=post_headers, auth=None)
|
||||||
|
|
||||||
|
bar.close()
|
||||||
|
time.sleep(0.1)
|
||||||
|
|
||||||
|
if response.status_code != 200:
|
||||||
|
print_fail("Upload failed (code " + response.status.code + ").")
|
||||||
|
else:
|
||||||
|
print_success("Upload successful.")
|
||||||
|
|
||||||
|
print()
|
||||||
|
|
||||||
|
# main
|
||||||
|
parser = argparse.ArgumentParser(description="EMS-ESP Firmware Upload")
|
||||||
|
parser.add_argument("-f", "--file", metavar="FILE", required=True, type=str, help="firmware file")
|
||||||
|
parser.add_argument("-i", "--ip", metavar="IP", type=str, default="ems-esp.local", help="IP address of EMS-ESP")
|
||||||
|
parser.add_argument("-u", "--username", metavar="USERNAME", type=str, default="admin", help="admin user")
|
||||||
|
parser.add_argument("-p", "--password", metavar="PASSWORD", type=str, default="admin", help="admin password")
|
||||||
|
args = parser.parse_args()
|
||||||
|
upload(**vars(args))
|
||||||
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
import subprocess
|
|
||||||
import os, argparse
|
|
||||||
|
|
||||||
print("\n** Starting upload...")
|
|
||||||
|
|
||||||
ap = argparse.ArgumentParser()
|
|
||||||
ap.add_argument("-p", "--port", required=True, help="port")
|
|
||||||
args = vars(ap.parse_args())
|
|
||||||
|
|
||||||
# esptool.py --chip esp32 --port "COM4" --baud 921600 --before default_reset --after hard_reset write_flash -z --flash_mode dio --flash_freq 40m --flash_size detect 0x1000 bootloader_dio_40m.bin 0x8000 partitions.bin 0xe000 boot_app0.bin 0x10000 EMS-ESP-dev-esp32.bin
|
|
||||||
|
|
||||||
subprocess.call(["esptool.py", "--chip esp32", "-p", args['port'], "--baud", "921600", "--before", "default_reset", "--after", "hard_reset", "write_flash", "-z", "--flash_mode", "dio", "--flash_freq", "40m","--flash_size", "detect", "0x1000", "bootloader_dio_40m.bin", "0x8000", "partitions.bin","0xe000", "boot_app0.bin", "0x10000", "EMS-ESP-dev-esp32.bin"])
|
|
||||||
|
|
||||||
print("\n** Finished upload.")
|
|
||||||
@@ -364,11 +364,6 @@ static void setup_commands(std::shared_ptr<Commands> & commands) {
|
|||||||
Settings.enabled = enable;
|
Settings.enabled = enable;
|
||||||
return StateUpdateResult::CHANGED;
|
return StateUpdateResult::CHANGED;
|
||||||
});
|
});
|
||||||
} else if (arguments.front() == "ota") {
|
|
||||||
to_app(shell).esp8266React.getOTASettingsService()->update([&](OTASettings & Settings) {
|
|
||||||
Settings.enabled = enable;
|
|
||||||
return StateUpdateResult::CHANGED;
|
|
||||||
});
|
|
||||||
} else if (arguments.front() == "ntp") {
|
} else if (arguments.front() == "ntp") {
|
||||||
to_app(shell).esp8266React.getNTPSettingsService()->update([&](NTPSettings & Settings) {
|
to_app(shell).esp8266React.getNTPSettingsService()->update([&](NTPSettings & Settings) {
|
||||||
Settings.enabled = enable;
|
Settings.enabled = enable;
|
||||||
|
|||||||
@@ -160,7 +160,7 @@ MAKE_WORD_CUSTOM(new_password_prompt2, "Retype new password: ")
|
|||||||
MAKE_WORD_CUSTOM(password_prompt, "Password: ")
|
MAKE_WORD_CUSTOM(password_prompt, "Password: ")
|
||||||
MAKE_WORD_CUSTOM(unset, "<unset>")
|
MAKE_WORD_CUSTOM(unset, "<unset>")
|
||||||
MAKE_WORD_CUSTOM(enable_mandatory, "<enable | disable>")
|
MAKE_WORD_CUSTOM(enable_mandatory, "<enable | disable>")
|
||||||
MAKE_WORD_CUSTOM(service_mandatory, "<ota | ap | mqtt | ntp>")
|
MAKE_WORD_CUSTOM(service_mandatory, "<ap | mqtt | ntp>")
|
||||||
|
|
||||||
// more common names that don't need translations
|
// more common names that don't need translations
|
||||||
MAKE_NOTRANSLATION(1x3min, "1x3min")
|
MAKE_NOTRANSLATION(1x3min, "1x3min")
|
||||||
|
|||||||
@@ -836,6 +836,9 @@ void System::commands_init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// uses LED to show system health
|
// uses LED to show system health
|
||||||
|
// 1 x flash = the EMS bus is not connected
|
||||||
|
// 2 x flash = the network (wifi or ethernet) is not connected
|
||||||
|
// 3 x flash = both EMS bus and network are failing. This is a critical error!
|
||||||
void System::led_monitor() {
|
void System::led_monitor() {
|
||||||
// we only need to run the LED healthcheck if there are errors
|
// we only need to run the LED healthcheck if there are errors
|
||||||
if (!healthcheck_ || !led_gpio_) {
|
if (!healthcheck_ || !led_gpio_) {
|
||||||
@@ -849,7 +852,6 @@ void System::led_monitor() {
|
|||||||
|
|
||||||
// first long pause before we start flashing
|
// first long pause before we start flashing
|
||||||
if (led_long_timer_ && (uint32_t)(current_time - led_long_timer_) >= HEALTHCHECK_LED_LONG_DUARATION) {
|
if (led_long_timer_ && (uint32_t)(current_time - led_long_timer_) >= HEALTHCHECK_LED_LONG_DUARATION) {
|
||||||
// Serial.println("starting the flash check");
|
|
||||||
led_short_timer_ = current_time; // start the short timer
|
led_short_timer_ = current_time; // start the short timer
|
||||||
led_long_timer_ = 0; // stop long timer
|
led_long_timer_ = 0; // stop long timer
|
||||||
led_flash_step_ = 1; // enable the short flash timer
|
led_flash_step_ = 1; // enable the short flash timer
|
||||||
@@ -863,7 +865,6 @@ void System::led_monitor() {
|
|||||||
|
|
||||||
if (++led_flash_step_ == 8) {
|
if (++led_flash_step_ == 8) {
|
||||||
// reset the whole sequence
|
// reset the whole sequence
|
||||||
// Serial.println("resetting flash check");
|
|
||||||
led_long_timer_ = uuid::get_uptime();
|
led_long_timer_ = uuid::get_uptime();
|
||||||
led_flash_step_ = 0;
|
led_flash_step_ = 0;
|
||||||
#if defined(ARDUINO_LOLIN_C3_MINI) && !defined(BOARD_C3_MINI_V1)
|
#if defined(ARDUINO_LOLIN_C3_MINI) && !defined(BOARD_C3_MINI_V1)
|
||||||
@@ -1100,7 +1101,6 @@ bool System::check_restore() {
|
|||||||
reboot_required |= saveSettings(NTP_SETTINGS_FILE, "NTP", input);
|
reboot_required |= saveSettings(NTP_SETTINGS_FILE, "NTP", input);
|
||||||
reboot_required |= saveSettings(SECURITY_SETTINGS_FILE, "Security", input);
|
reboot_required |= saveSettings(SECURITY_SETTINGS_FILE, "Security", input);
|
||||||
reboot_required |= saveSettings(EMSESP_SETTINGS_FILE, "Settings", input);
|
reboot_required |= saveSettings(EMSESP_SETTINGS_FILE, "Settings", input);
|
||||||
reboot_required |= saveSettings(OTA_SETTINGS_FILE, "OTA", input);
|
|
||||||
} else if (settings_type == "customizations") {
|
} else if (settings_type == "customizations") {
|
||||||
// it's a customization file, just replace it and there's no need to reboot
|
// it's a customization file, just replace it and there's no need to reboot
|
||||||
saveSettings(EMSESP_CUSTOMIZATION_FILE, "Customizations", input);
|
saveSettings(EMSESP_CUSTOMIZATION_FILE, "Customizations", input);
|
||||||
@@ -1343,13 +1343,6 @@ bool System::command_info(const char * value, const int8_t id, JsonObject output
|
|||||||
node["tz label"] = settings.tzLabel;
|
node["tz label"] = settings.tzLabel;
|
||||||
// node["tz format"] = settings.tzFormat;
|
// node["tz format"] = settings.tzFormat;
|
||||||
});
|
});
|
||||||
|
|
||||||
// OTA status
|
|
||||||
node = output["OTA Info"].to<JsonObject>();
|
|
||||||
EMSESP::esp8266React.getOTASettingsService()->read([&](OTASettings & settings) {
|
|
||||||
node["enabled"] = settings.enabled;
|
|
||||||
node["port"] = settings.port;
|
|
||||||
});
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// MQTT Status
|
// MQTT Status
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
#define EMSESP_APP_VERSION "3.7.0-dev.8"
|
#define EMSESP_APP_VERSION "3.7.0-dev.9"
|
||||||
|
|||||||
@@ -181,7 +181,6 @@ void WebAPIService::getSettings(AsyncWebServerRequest * request) {
|
|||||||
System::extractSettings(AP_SETTINGS_FILE, "AP", root);
|
System::extractSettings(AP_SETTINGS_FILE, "AP", root);
|
||||||
System::extractSettings(MQTT_SETTINGS_FILE, "MQTT", root);
|
System::extractSettings(MQTT_SETTINGS_FILE, "MQTT", root);
|
||||||
System::extractSettings(NTP_SETTINGS_FILE, "NTP", root);
|
System::extractSettings(NTP_SETTINGS_FILE, "NTP", root);
|
||||||
System::extractSettings(OTA_SETTINGS_FILE, "OTA", root);
|
|
||||||
System::extractSettings(SECURITY_SETTINGS_FILE, "Security", root);
|
System::extractSettings(SECURITY_SETTINGS_FILE, "Security", root);
|
||||||
System::extractSettings(EMSESP_SETTINGS_FILE, "Settings", root);
|
System::extractSettings(EMSESP_SETTINGS_FILE, "Settings", root);
|
||||||
|
|
||||||
|
|||||||
@@ -53,7 +53,6 @@ void WebStatusService::systemStatus(AsyncWebServerRequest * request) {
|
|||||||
root["num_analogs"] = EMSESP::analogsensor_.no_sensors();
|
root["num_analogs"] = EMSESP::analogsensor_.no_sensors();
|
||||||
root["free_heap"] = EMSESP::system_.getHeapMem();
|
root["free_heap"] = EMSESP::system_.getHeapMem();
|
||||||
root["uptime"] = uuid::get_uptime_sec();
|
root["uptime"] = uuid::get_uptime_sec();
|
||||||
EMSESP::esp8266React.getOTASettingsService()->read([root](OTASettings & otaSettings) { root["ota_status"] = otaSettings.enabled; });
|
|
||||||
root["mqtt_status"] = EMSESP::mqtt_.connected();
|
root["mqtt_status"] = EMSESP::mqtt_.connected();
|
||||||
|
|
||||||
#ifndef EMSESP_STANDALONE
|
#ifndef EMSESP_STANDALONE
|
||||||
|
|||||||
@@ -62,11 +62,6 @@
|
|||||||
"tz_label": "Europe/Amsterdam",
|
"tz_label": "Europe/Amsterdam",
|
||||||
"tz_format": "CET-1CEST,M3.5.0,M10.5.0/3"
|
"tz_format": "CET-1CEST,M3.5.0,M10.5.0/3"
|
||||||
},
|
},
|
||||||
"OTA": {
|
|
||||||
"enabled": false,
|
|
||||||
"port": 8266,
|
|
||||||
"password": "ems-esp-neo"
|
|
||||||
},
|
|
||||||
"Security": {
|
"Security": {
|
||||||
"jwt_secret": "ems-esp-neo",
|
"jwt_secret": "ems-esp-neo",
|
||||||
"users": [
|
"users": [
|
||||||
|
|||||||
Reference in New Issue
Block a user