Skip to content

Commit 2b3996a

Browse files
committed
Merge fixes from main branch
1 parent dc7b28c commit 2b3996a

21 files changed

+463
-13
lines changed

factory_settings.ini

+3-3
Original file line numberDiff line numberDiff line change
@@ -52,10 +52,10 @@ build_flags =
5252

5353
; Serial settings
5454
-D FACTORY_SERIAL_ENABLED=true
55-
-D FACTORY_SERIAL_RXPIN=15
56-
-D FACTORY_SERIAL_TXPIN=17
55+
-D FACTORY_SERIAL_RXPIN=36
56+
-D FACTORY_SERIAL_TXPIN=39
5757
-D FACTORY_SERIAL_BAUD=0
58-
-D FACTORY_SERIAL_CONFIG=0x06
58+
-D FACTORY_SERIAL_CONFIG=0x800001c
5959
-D FACTORY_SERIAL_INVERTED=false
6060

6161
; JWT Secret

interface/.env.development

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
# Change the IP address to that of your 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.88
4-
REACT_APP_WEB_SOCKET_ROOT=ws://192.168.0.88
3+
REACT_APP_HTTP_ROOT=http://192.168.4.1
4+
REACT_APP_WEB_SOCKET_ROOT=ws://192.168.4.1

interface/src/ntp/TimeFormat.ts

-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import moment, { Moment } from 'moment';
22

33
export const formatIsoDateTime = (isoDateString: string) => moment.parseZone(isoDateString).format('ll @ HH:mm:ss');
4-
54
export const formatLocalDateTime = (moment: Moment) => moment.format('YYYY-MM-DDTHH:mm');
65
export const formatIsoDateTimeToHr = (isoDateString: string) => moment.parseZone(isoDateString).format('YYYY-MM-DD HH:mm:ssZZ');

interface/src/serial/LogEventConsole.tsx

+38-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import React, { FC } from 'react';
22

3-
import { LogEvent } from './types';
3+
import { LogEvent, LogLevel } from './types';
44
import { Theme, makeStyles, Box } from '@material-ui/core';
55
import { useWindowSize } from '../components';
66
import { formatIsoDateTimeToHr } from '../ntp/TimeFormat';
@@ -13,8 +13,8 @@ interface Offsets {
1313
leftOffset: () => number;
1414
}
1515

16-
const topOffset = () => document.getElementById('system-tabs')?.getBoundingClientRect().bottom || 0;
17-
const leftOffset = () => document.getElementById('system-tabs')?.getBoundingClientRect().left || 0;
16+
const topOffset = () => document.getElementById('serial-tabs')?.getBoundingClientRect().bottom || 0;
17+
const leftOffset = () => document.getElementById('serial-tabs')?.getBoundingClientRect().left || 0;
1818

1919
const useStyles = makeStyles((theme: Theme) => ({
2020
console: {
@@ -60,6 +60,41 @@ const LogEventConsole: FC<LogEventConsoleProps> = (props) => {
6060
const classes = useStyles({ topOffset, leftOffset });
6161
const { events } = props;
6262

63+
const styleLevel = (level: LogLevel) => {
64+
switch (level) {
65+
case LogLevel.DEBUG:
66+
return classes.debug;
67+
case LogLevel.INFO:
68+
return classes.info;
69+
case LogLevel.WARNING:
70+
return classes.warning;
71+
case LogLevel.ERROR:
72+
return classes.error;
73+
default:
74+
return classes.unknown;
75+
}
76+
}
77+
78+
const levelLabel = (level: LogLevel) => {
79+
switch (level) {
80+
case LogLevel.DEBUG:
81+
return "DEBUG";
82+
case LogLevel.INFO:
83+
return "INFO";
84+
case LogLevel.WARNING:
85+
return "WARNING";
86+
case LogLevel.ERROR:
87+
return "ERROR";
88+
default:
89+
return "UNKNOWN";
90+
}
91+
}
92+
93+
const paddedLevelLabel = (level: LogLevel) => {
94+
let label = levelLabel(level);
95+
return label.padStart(7, '\xa0');
96+
}
97+
6398
return (
6499
<Box className={classes.console}>
65100
{events.map(e => (

interface/src/serial/LogEventController.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { LogEvent } from './types';
77
import { WEB_SOCKET_ROOT } from '../api/Env';
88
import LogEventConsole from './LogEventConsole';
99

10-
const LOG_EVENT_WEB_SOCKET_URL = WEB_SOCKET_ROOT + "log";
10+
const LOG_EVENT_WEB_SOCKET_URL = WEB_SOCKET_ROOT + "serial";
1111

1212
interface LogEventControllerState {
1313
ws: Sockette;

interface/src/serial/Serial.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ class Serial extends Component<SerialProps> {
2121
const { authenticatedContext } = this.props;
2222
return (
2323
<MenuAppBar sectionTitle="Serial">
24-
<Tabs value={this.props.match.url} onChange={this.handleTabChange} variant="fullWidth">
24+
<Tabs id="serial-tabs" value={this.props.match.url} onChange={this.handleTabChange} variant="fullWidth">
2525
<Tab value="/serial/status" label="Serial Status" />
2626
<Tab value="/serial/log" label="Remote Log" />
2727
<Tab value="/serial/settings" label="Serial Settings" disabled={!authenticatedContext.me.admin} />

interface/src/serial/SerialSettingsForm.tsx

+12
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,18 @@ class SerialSettingsForm extends React.Component<SerialSettingsFormProps> {
6868
onChange={handleValueChange('baud')}
6969
margin="normal"
7070
/>
71+
<TextValidator
72+
validators={['required', 'isNumber', 'minNumber:134217744', 'maxNumber:134217791']}
73+
errorMessages={['Config is required', "Must be a number", "Must be greater than 134217744 ", "Max value is 134217791"]}
74+
name="config"
75+
label="Config"
76+
fullWidth
77+
variant="outlined"
78+
value={data.config}
79+
type="number"
80+
onChange={handleValueChange('config')}
81+
margin="normal"
82+
/>
7183
<BlockFormControlLabel
7284
control={
7385
<Checkbox

interface/src/serial/types.ts

+7
Original file line numberDiff line numberDiff line change
@@ -43,4 +43,11 @@ export interface SerialSettings {
4343
export interface LogEvent {
4444
time: string;
4545
message: string;
46+
}
47+
48+
export enum LogLevel {
49+
DEBUG = 0,
50+
INFO = 1,
51+
WARNING = 2,
52+
ERROR = 3
4653
}
+114
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
import React, { FC } from 'react';
2+
3+
import { LogEvent, LogLevel } from './types';
4+
import { Theme, makeStyles, Box } from '@material-ui/core';
5+
import { useWindowSize } from '../components';
6+
import { formatIsoDateTimeToHr } from '../ntp/TimeFormat';
7+
8+
interface LogEventConsoleProps {
9+
events: LogEvent[];
10+
}
11+
interface Offsets {
12+
topOffset: () => number;
13+
leftOffset: () => number;
14+
}
15+
16+
const topOffset = () => document.getElementById('system-tabs')?.getBoundingClientRect().bottom || 0;
17+
const leftOffset = () => document.getElementById('system-tabs')?.getBoundingClientRect().left || 0;
18+
19+
const useStyles = makeStyles((theme: Theme) => ({
20+
console: {
21+
padding: theme.spacing(2),
22+
position: "absolute",
23+
left: (offsets: Offsets) => offsets.leftOffset(),
24+
right: 0,
25+
top: (offsets: Offsets) => offsets.topOffset(),
26+
bottom: 0,
27+
backgroundColor: "black",
28+
overflow: "auto"
29+
},
30+
entry: {
31+
color: "#bbbbbb",
32+
fontFamily: "Courier New, monospace",
33+
fontSize: "14px",
34+
letterSpacing: "normal",
35+
whiteSpace: "nowrap"
36+
},
37+
file: {
38+
color: "#00ffff"
39+
},
40+
debug: {
41+
color: "#0000ff"
42+
},
43+
info: {
44+
color: "#00ff00"
45+
},
46+
warning: {
47+
color: "#ffff00"
48+
},
49+
error: {
50+
color: "#ff0000"
51+
},
52+
unknown: {
53+
color: "#ffffff"
54+
}
55+
}));
56+
57+
58+
const LogEventConsole: FC<LogEventConsoleProps> = (props) => {
59+
useWindowSize();
60+
const classes = useStyles({ topOffset, leftOffset });
61+
const { events } = props;
62+
63+
const styleLevel = (level: LogLevel) => {
64+
switch (level) {
65+
case LogLevel.DEBUG:
66+
return classes.debug;
67+
case LogLevel.INFO:
68+
return classes.info;
69+
case LogLevel.WARNING:
70+
return classes.warning;
71+
case LogLevel.ERROR:
72+
return classes.error;
73+
default:
74+
return classes.unknown;
75+
}
76+
}
77+
78+
const levelLabel = (level: LogLevel) => {
79+
switch (level) {
80+
case LogLevel.DEBUG:
81+
return "DEBUG";
82+
case LogLevel.INFO:
83+
return "INFO";
84+
case LogLevel.WARNING:
85+
return "WARNING";
86+
case LogLevel.ERROR:
87+
return "ERROR";
88+
default:
89+
return "UNKNOWN";
90+
}
91+
}
92+
93+
const paddedLevelLabel = (level: LogLevel) => {
94+
let label = levelLabel(level);
95+
return label.padStart(7, '\xa0');
96+
}
97+
98+
return (
99+
<Box className={classes.console}>
100+
{events.map(e => (
101+
<div className={classes.entry}>
102+
<span>{formatIsoDateTimeToHr(e.time)} </span>
103+
<span className={styleLevel(e.level)}>{paddedLevelLabel(e.level)} </span>
104+
<span className={classes.file}>{`${e.file}[${e.line}]`} </span>
105+
<span>{e.message}</span>
106+
</div>
107+
))}
108+
</Box>
109+
);
110+
};
111+
112+
113+
114+
export default LogEventConsole;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import React from 'react';
2+
import Sockette from 'sockette';
3+
import { withSnackbar, WithSnackbarProps } from 'notistack';
4+
5+
import { addAccessTokenParameter } from '../authentication';
6+
import { LogEvent } from './types';
7+
import { WEB_SOCKET_ROOT } from '../api/Env';
8+
import LogEventConsole from './LogEventConsole';
9+
10+
const LOG_EVENT_WEB_SOCKET_URL = WEB_SOCKET_ROOT + "log";
11+
12+
interface LogEventControllerState {
13+
ws: Sockette;
14+
events: LogEvent[];
15+
}
16+
17+
class LogEventController extends React.Component<WithSnackbarProps, LogEventControllerState> {
18+
19+
constructor(props: WithSnackbarProps) {
20+
super(props);
21+
this.state = {
22+
ws: new Sockette(addAccessTokenParameter(LOG_EVENT_WEB_SOCKET_URL), {
23+
onmessage: this.onMessage
24+
}),
25+
events: []
26+
};
27+
}
28+
29+
componentWillUnmount() {
30+
this.state.ws.close();
31+
}
32+
33+
onMessage = (event: MessageEvent) => {
34+
const rawData = event.data;
35+
if (typeof rawData === 'string' || rawData instanceof String) {
36+
const event = JSON.parse(rawData as string) as LogEvent;
37+
this.setState(state => ({ events: [...state.events, event] }));
38+
}
39+
}
40+
41+
render() {
42+
return (
43+
<LogEventConsole events={this.state.events} />
44+
);
45+
}
46+
47+
}
48+
49+
export default withSnackbar(LogEventController);

interface/src/system/System.tsx

+3
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { MenuAppBar } from '../components';
1010

1111
import SystemStatusController from './SystemStatusController';
1212
import OTASettingsController from './OTASettingsController';
13+
import LogEventController from './LogEventController';
1314
import UploadFirmwareController from './UploadFirmwareController';
1415

1516
type SystemProps = AuthenticatedContextProps & RouteComponentProps & WithFeaturesProps;
@@ -26,6 +27,7 @@ class System extends Component<SystemProps> {
2627
<MenuAppBar sectionTitle="System">
2728
<Tabs id="system-tabs" value={this.props.match.url} onChange={this.handleTabChange} variant="fullWidth">
2829
<Tab value="/system/status" label="System Status" />
30+
<Tab value="/system/log" label="Remote Log" />
2931
{features.ota && (
3032
<Tab value="/system/ota" label="OTA Settings" disabled={!authenticatedContext.me.admin} />
3133
)}
@@ -35,6 +37,7 @@ class System extends Component<SystemProps> {
3537
</Tabs>
3638
<Switch>
3739
<AuthenticatedRoute exact path="/system/status" component={SystemStatusController} />
40+
<AuthenticatedRoute exact path="/system/log" component={LogEventController} />
3841
{features.ota && (
3942
<AuthenticatedRoute exact path="/system/ota" component={OTASettingsController} />
4043
)}

lib/framework/ESP8266React.cpp

+10-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ ESP8266React::ESP8266React(AsyncWebServer* server) :
3131
#endif
3232
_restartService(server, &_securitySettingsService),
3333
_factoryResetService(server, &ESPFS, &_securitySettingsService),
34-
_systemStatus(server, &_securitySettingsService) {
34+
_systemStatus(server, &_securitySettingsService),
35+
_webSocketLogHandler(server, &_securitySettingsService) {
3536
#ifdef PROGMEM_WWW
3637
// Serve static resources from PROGMEM
3738
WWWData::registerRoutes(
@@ -90,6 +91,11 @@ void ESP8266React::begin() {
9091
#elif defined(ESP8266)
9192
ESPFS.begin();
9293
#endif
94+
// Begin logging
95+
//Logger::begin(_fs);
96+
//Logger::getInstance()->addEventHandler(SerialLogHandler::logEvent);
97+
_webSocketLogHandler.begin();
98+
9399
_wifiSettingsService.begin();
94100
_apSettingsService.begin();
95101
#if FT_ENABLED(FT_NTP)
@@ -110,6 +116,9 @@ void ESP8266React::begin() {
110116
}
111117

112118
void ESP8266React::loop() {
119+
//Logger::loop();
120+
_webSocketLogHandler.loop();
121+
113122
_wifiSettingsService.loop();
114123
_apSettingsService.loop();
115124
#if FT_ENABLED(FT_OTA)

lib/framework/ESP8266React.h

+3
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
#include <WiFiSettingsService.h>
3434
#include <WiFiStatus.h>
3535
#include <ESPFS.h>
36+
#include <WebSocketLogHandler.h>
3637

3738
#ifdef PROGMEM_WWW
3839
#include <WWWData.h>
@@ -100,6 +101,7 @@ class ESP8266React {
100101
}
101102

102103
private:
104+
FS* _fs;
103105
FeaturesService _featureService;
104106
SecuritySettingsService _securitySettingsService;
105107
WiFiSettingsService _wifiSettingsService;
@@ -131,6 +133,7 @@ class ESP8266React {
131133
RestartService _restartService;
132134
FactoryResetService _factoryResetService;
133135
SystemStatus _systemStatus;
136+
WebSocketLogHandler _webSocketLogHandler;
134137
};
135138

136139
#endif

0 commit comments

Comments
 (0)