Remove OTA feature #1738

This commit is contained in:
proddy
2024-05-04 14:19:19 +02:00
parent 6537abedb9
commit dabb958f06
44 changed files with 291 additions and 824 deletions

View File

@@ -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)

View File

@@ -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=\"\"

View File

@@ -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/*"

View File

@@ -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`);

View File

@@ -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)}

View File

@@ -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;

View File

@@ -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"

View File

@@ -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',

View File

@@ -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',

View File

@@ -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.",

View File

@@ -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',

View File

@@ -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',

View File

@@ -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',

View File

@@ -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!',

View File

@@ -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.',

View File

@@ -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',

View File

@@ -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',

View File

@@ -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;
}

View File

@@ -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';

View File

@@ -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'
}
]
});

View File

@@ -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);
} }

View File

@@ -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();
} }

View File

@@ -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;

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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

View File

@@ -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
} }
} }
} }

View File

@@ -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;
} }

View File

@@ -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

View File

@@ -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)

View File

@@ -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

View File

@@ -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
View File

@@ -0,0 +1,6 @@
requests
termcolor
requests_toolbelt
tqdm

130
scripts/upload.py Normal file
View 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
View 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))

View File

@@ -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.")

View File

@@ -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;

View File

@@ -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")

View File

@@ -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

View File

@@ -1 +1 @@
#define EMSESP_APP_VERSION "3.7.0-dev.8" #define EMSESP_APP_VERSION "3.7.0-dev.9"

View File

@@ -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);

View File

@@ -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

View File

@@ -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": [