8000 Experimenting with event source · adityathakekar/esp8266-react@afbfd99 · GitHub
[go: up one dir, main page]

Skip to content

Commit afbfd99

Browse files
committed
Experimenting with event source
1 parent 35d75d1 commit afbfd99

13 files changed

+140
-25
lines changed

interface/.env.development

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
# Change the IP address to that of your 8000 ESP device to enable local development of the UI.
22
# Remember to also enable CORS in platformio.ini before uploading the code to the device.
3-
REACT_APP_HTTP_ROOT=http://192.168.0.99
4-
REACT_APP_WEB_SOCKET_ROOT=ws://192.168.0.99
3+
REACT_APP_HTTP_ROOT=http://192.168.0.30
4+
REACT_APP_WEB_SOCKET_ROOT=ws://192.168.0.30

interface/src/api/Env.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ export const PROJECT_NAME = process.env.REACT_APP_PROJECT_NAME!;
22
export const PROJECT_PATH = process.env.REACT_APP_PROJECT_PATH!;
33

44
export const ENDPOINT_ROOT = calculateEndpointRoot("/rest/");
5+
export const EVENT_SOURCE_ROOT = calculateEndpointRoot("/es/");
56
export const WEB_SOCKET_ROOT = calculateWebSocketRoot("/ws/");
67

78
function calculateEndpointRoot(endpointPath: string) {

interface/src/system/LogEventConsole.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ const useStyles = makeStyles((theme: Theme) => ({
2121
console: {
2222
padding: theme.spacing(2),
2323
height: (topOffset: () => number) => `calc(100vh - ${topOffset()}px)`,
24-
backgroundColor: "black"
24+
backgroundColor: "black",
25+
overflowY: "scroll"
2526
},
2627
entry: {
2728
color: "#bbbbbb",

interface/src/system/LogEventController.tsx

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ const LOG_EVENT_WEB_SOCKET_URL = WEB_SOCKET_ROOT + "log";
1111

1212
interface LogEventControllerState {
1313
ws: Sockette;
14-
connected: boolean;
1514
events: LogEvent[];
1615
}
1716

@@ -21,11 +20,8 @@ class LogEventController extends React.Component<WithSnackbarProps, LogEventCont
2120
super(props);
2221
this.state = {
2322
ws: new Sockette(addAccessTokenParameter(LOG_EVENT_WEB_SOCKET_URL), {
24-
onmessage: this.onMessage,
25-
onopen: this.onOpen,
26-
onclose: this.onClose,
23+
onmessage: this.onMessage
2724
}),
28-
connected: false,
2925
events: []
3026
};
3127
}
@@ -42,14 +38,6 @@ class LogEventController extends React.Component<WithSnackbarProps, LogEventCont
4238
}
4339
}
4440

45-
onOpen = () => {
46-
this.setState({ connected: true });
47-
}
48-
49-
onClose = () => {
50-
this.setState({ connected: false });
51-
}
52-
5341
render() {
5442
return (
5543
<LogEventConsole events={this.state.events} />
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import React from 'react';
2+
import { withSnackbar, WithSnackbarProps } from 'notistack';
3+
4+
import { LogEvent } from './types';
5+
import { EVENT_SOURCE_ROOT } from '../api/Env';
6+
import LogEventConsole from './LogEventConsole';
7+
import { addAccessTokenParameter } from '../authentication';
8+
9+
const LOG_EVENT_EVENT_SOURCE_URL = EVENT_SOURCE_ROOT + "log";
10+
11+
interface LogEventController2State {
12+
eventSource?: EventSource;
13+
events: LogEvent[];
14+
}
15+
16+
class LogEventController2 extends React.Component<WithSnackbarProps, LogEventController2State> {
17+
eventSource: EventSource;
18+
19+
constructor(props: WithSnackbarProps) {
20+
super(props);
21+
this.state = {
22+
events: []
23+
};
24+
this.eventSource = new EventSource(addAccessTokenParameter(LOG_EVENT_EVENT_SOURCE_URL));
25+
this.eventSource.onmessage = this.onMessage;
26+
}
27+
28+
componentWillUnmount() {
29+
this.eventSource.close();
30+
}
31+
32+
onMessage = (event: MessageEvent) => {
33+
const rawData = event.data;
34+
if (typeof rawData === 'string' || rawData instanceof String) {
35+
const event = JSON.parse(rawData as string) as LogEvent;
36+
this.setState(state => ({ events: [...state.events, event] }));
37+
}
38+
}
39+
40+
render() {
41+
return (
42+
<LogEventConsole events={this.state.events} />
43+
);
44+
}
45+
46+
}
47+
48+
export default withSnackbar(LogEventController2);

interface/src/system/System.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { MenuAppBar } from '../components';
88

99
import SystemStatusController from './SystemStatusController';
1010
import OTASettingsController from './OTASettingsController';
11+
import LogEventController2 from './LogEventController2';
1112
import LogEventController from './LogEventController';
1213

1314
type SystemProps = AuthenticatedContextProps & RouteComponentProps;
@@ -24,12 +25,14 @@ class System extends Component<SystemProps> {
2425
<MenuAppBar sectionTitle="System">
2526
<Tabs id="system-tabs" value={this.props.match.url} onChange={this.handleTabChange} variant="fullWidth">
2627
<Tab value="/system/status" label="System Status" />
27-
<Tab value="/system/log" label="Log" />
28+
<Tab value="/system/log" label="Log (WebSocket)" />
29+
<Tab value="/system/log2" label="Log (EventSource)" />
2830
<Tab value="/system/ota" label="OTA Settings" disabled={!authenticatedContext.me.admin} />
2931
</Tabs>
3032
<Switch>
3133
<AuthenticatedRoute exact path="/system/status" component={SystemStatusController} />
3234
<AuthenticatedRoute exact path="/system/log" component={LogEventController} />
35+
<AuthenticatedRoute exact path="/system/log2" component={LogEventController2} />
3336
<AuthenticatedRoute exact path="/system/ota" component={OTASettingsController} />
3437
<Redirect to="/system/status" />
3538
</Switch>

lib/framework/ESP8266React.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ ESP8266React::ESP8266React(AsyncWebServer* server, FS* fs) :
1616
_apStatus(server, &_securitySettingsService),
1717
_mqttStatus(server, &_mqttSettingsService, &_securitySettingsService),
1818
_systemStatus(server, &_securitySettingsService),
19-
_webSocketLogHandler(server, &_securitySettingsService) {
19+
_webSocketLogHandler(server, &_securitySettingsService),
20+
_eventSourceLogHandler(server, &_securitySettingsService) {
2021
#ifdef PROGMEM_WWW
2122
// Serve static resources from PROGMEM
2223
WWWData::registerRoutes(

lib/framework/ESP8266React.h

Lines changed: 2 additions & 0 deletions
FEE1
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#include <WiFiStatus.h>
3131
#include <SerialLogHandler.h>
3232
#include <WebSocketLogHandler.h>
33+
#include <EventSourceLogHandler.h>
3334

3435
#ifdef PROGMEM_WWW
3536
#include <WWWData.h>
@@ -97,6 +98,7 @@ class ESP8266React {
9798
MqttStatus _mqttStatus;
9899
SystemStatus _systemStatus;
99100
WebSocketLogHandler _webSocketLogHandler;
101+
EventSourceLogHandler _eventSourceLogHandler;
100102
};
101103

102104
#endif
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#include <EventSourceLogHandler.h>
2+
3+
uint32_t EventSourceLogHandler::eventId = 0;

lib/framework/EventSourceLogHandler.h

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
#ifndef EventSourceLogHandler_h
2+
#define EventSourceLogHandler_h
3+
4+
#include <ESPAsyncWebServer.h>
5+
#include <SecurityManager.h>
6+
#include <Logger.h>
7+
#include <ESPUtils.h>
8+
9+
#define EVENT_SOURCE_LOG_PATH "/es/log"
10+
#define EVENT_SOURCE_LOG_BUFFER 512
11+
12+
class EventSourceLogHandler {
13+
public:
14+
EventSourceLogHandler(AsyncWebServer* server, SecurityManager* securityManager) : _events(EVENT_SOURCE_LOG_PATH) {
15+
_events.setFilter(securityManager->filterRequest(AuthenticationPredicates::IS_ADMIN));
16+
server->addHandler(&_events);
17+
server->on(
18+
EVENT_SOURCE_LOG_PATH, HTTP_GET, std::bind(&EventSourceLogHandler::forbidden, this, std::placeholders::_1));
19+
Logger::addEventHandler(std::bind(&EventSourceLogHandler::logEvent,
20+
this,
21+
std::placeholders::_1,
22+
std::placeholders::_2,
23+
std::placeholders::_3,
24+
std::placeholders::_4,
25+
std::placeholders::_5));
26+
}
27+
28+
private:
29+
AsyncEventSource _events;
30+
static uint32_t eventId;
31+
32+
void forbidden(AsyncWebServerRequest* request) {
33+
request->send(403);
34+
}
35+
36+
void logEvent(const tm* time, LogLevel level, const String& file, const uint16_t line, const String& message) {
37+
// if there are no clients, don't bother doing anything
38+
if (!_events.count()) {
39+
return;
40+
}
41+
42+
// create JsonObject to hold log event
43+
DynamicJsonDocument jsonDocument = DynamicJsonDocument(EVENT_SOURCE_LOG_BUFFER);
44+
JsonObject logEvent = jsonDocument.to<JsonObject>();
45+
logEvent["time"] = ESPUtils::toISOString(time, true);
46+
logEvent["level"] = level;
47+
logEvent["file"] = file;
48+
logEvent["line"] = line;
49+
logEvent["message"] = message;
50+
51+
// transmit log event to all clients
52+
size_t len = measureJson(jsonDocument);
53+
char* buffer = new char[len + 1];
54+
if (buffer) {
55+
serializeJson(jsonDocument, buffer, len + 1);
56+
_events.send(buffer, "message", millis());
57+
}
58+
}
59+
};
60+
61+
#endif

lib/framework/OTASettingsService.cpp

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -60,13 +60,19 @@ void OTASettingsService::configureArduinoOTA() {
6060
_arduinoOTA = new ArduinoOTAClass;
6161
_arduinoOTA->setPort(_state.port);
6262
_arduinoOTA->setPassword(_state.password.c_str());
63-
_arduinoOTA->onStart([]() { LOG_I("Starting OTA update"); });
63+
_arduinoOTA->onStart([this]() {
64+
LOG_I("Starting OTA update");
65+
_progress = 0;
66+
});
6467
_arduinoOTA->onEnd([]() { LOG_I("Ending OTA update"); });
65-
_arduinoOTA->onProgress([](unsigned int progress, unsigned int total) {
66-
LOGF_I("OTA update progress: %u%%", progress / (total / 100));
68+
_arduinoOTA->onProgress([this](unsigned int progress, unsigned int total) {
69+
uint8_t currentProgress = progress / (total / 100);
70+
if (currentProgress != _progress) {
71+
_progress = currentProgress;
72+
LOGF_I("OTA update progress: %u%%", _progress);
73+
}
6774
});
68-
_arduinoOTA->onError(
69-
[](ota_error_t error) { LOGF_E("OTA update error[%u]: %s", error, otaErrorMessage(error)); });
75+
_arduinoOTA->onError([](ota_error_t error) { LOGF_E("OTA update error[%u]: %s", error, otaErrorMessage(error)); });
7076
_arduinoOTA->begin();
7177
}
7278
}

lib/framework/OTASettingsService.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,15 +51,15 @@ class OTASettings {
5151
class OTASettingsService : public StatefulService<OTASettings> {
5252
public:
5353
OTASettingsService(AsyncWebServer* server, FS* fs, SecurityManager* securityManager);
54-
5554
void begin();
5655
void loop();
5756

5857
private:
5958
HttpEndpoint<OTASettings> _httpEndpoint;
6059
FSPersistence<OTASettings> _fsPersistence;
6160
ArduinoOTAClass* _arduinoOTA;
62-
61+
uint8_t _progress;
62+
6363
void configureArduinoOTA();
6464
#ifdef ESP32
6565
void onStationModeGotIP(WiFiEvent_t event, WiFiEventInfo_t info);

lib/framework/WebSocketLogHandler.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#define WebSocketLogHandler_h
33

44
#include <ESPAsyncWebServer.h>
5+
#include <SecurityManager.h>
56
#include <Logger.h>
67
#include <ESPUtils.h>
78

0 commit comments

Comments
 (0)
0