Skip to content

Commit ce9d9f6

Browse files
committed
Merge branch 'master' of https://github.com/rjwats/esp8266-react
2 parents 4dc5758 + 8a869c9 commit ce9d9f6

12 files changed

+117
-51
lines changed

interface/.env.development

+2-6
Original file line numberDiff line numberDiff line change
@@ -1,8 +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.1.33
4-
#REACT_APP_WEB_SOCKET_ROOT=ws://192.168.1.33
5-
#REACT_APP_HTTP_ROOT=http://192.168.4.1
6-
#REACT_APP_WEB_SOCKET_ROOT=ws://192.168.4.1
7-
REACT_APP_HTTP_ROOT=http://192.168.1.53
8-
REACT_APP_WEB_SOCKET_ROOT=ws://192.168.1.53
3+
REACT_APP_HTTP_ROOT=http://192.168.0.24
4+
REACT_APP_WEB_SOCKET_ROOT=ws://192.168.0.24

interface/package-lock.json

+5-5
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

interface/src/ntp/NTPStatusForm.tsx

+18-19
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import React, { Component, Fragment } from 'react';
2-
import moment from 'moment';
32

43
import { WithTheme, withTheme } from '@material-ui/core/styles';
54
import { Avatar, Divider, List, ListItem, ListItemAvatar, ListItemText, Button } from '@material-ui/core';
@@ -14,7 +13,7 @@ import RefreshIcon from '@material-ui/icons/Refresh';
1413

1514
import { RestFormProps, FormButton, HighlightAvatar } from '../components';
1615
import { isNtpActive, ntpStatusHighlight, ntpStatus } from './NTPStatus';
17-
import { formatIsoDateTime, formatLocalDateTime } from './TimeFormat';
16+
import { formatDuration, formatDateTime, formatLocalDateTime } from './TimeFormat';
1817
import { NTPStatus, Time } from './types';
1918
import { redirectingAuthorizedFetch, withAuthenticatedContext, AuthenticatedContextProps } from '../authentication';
2019
import { TIME_ENDPOINT } from '../api';
@@ -39,34 +38,34 @@ class NTPStatusForm extends Component<NTPStatusFormProps, NTPStatusFormState> {
3938
}
4039

4140
updateLocalTime = (event: React.ChangeEvent<HTMLInputElement>) => {
42-
this.setState({ localTime: event.target.value });
41+
this.setState({
42+
localTime: event.target.value
43+
});
4344
}
4445

4546
openSetTime = () => {
46-
this.setState({ localTime: formatLocalDateTime(moment()), settingTime: true, });
47+
this.setState({
48+
localTime: formatLocalDateTime(new Date()),
49+
settingTime: true
50+
});
4751
}
4852

4953
closeSetTime = () => {
50-
this.setState({ settingTime: false });
54+
this.setState({
55+
settingTime: false
56+
});
5157
}
5258

53-
createAdjustedTime = (): Time => {
54-
const currentLocalTime = moment(this.props.data.time_local);
55-
const newLocalTime = moment(this.state.localTime);
56-
newLocalTime.subtract(currentLocalTime.utcOffset())
57-
newLocalTime.milliseconds(0);
58-
newLocalTime.utc();
59-
return {
60-
time_utc: newLocalTime.format()
61-
}
62-
}
59+
createTime = (): Time => ({
60+
local_time: formatLocalDateTime(new Date(this.state.localTime))
61+
});
6362

6463
configureTime = () => {
6564
this.setState({ processing: true });
6665
redirectingAuthorizedFetch(TIME_ENDPOINT,
6766
{
6867
method: 'POST',
69-
body: JSON.stringify(this.createAdjustedTime()),
68+
body: JSON.stringify(this.createTime()),
7069
headers: {
7170
'Content-Type': 'application/json'
7271
}
@@ -153,7 +152,7 @@ class NTPStatusForm extends Component<NTPStatusFormProps, NTPStatusFormState> {
153152
<AccessTimeIcon />
154153
</Avatar>
155154
</ListItemAvatar>
156-
<ListItemText primary="Local Time" secondary={formatIsoDateTime(data.time_local)} />
155+
<ListItemText primary="Local Time" secondary={formatDateTime(data.local_time)} />
157156
</ListItem>
158157
<Divider variant="inset" component="li" />
159158
<ListItem>
@@ -162,7 +161,7 @@ class NTPStatusForm extends Component<NTPStatusFormProps, NTPStatusFormState> {
162161
<SwapVerticalCircleIcon />
163162
</Avatar>
164163
</ListItemAvatar>
165-
<ListItemText primary="UTC Time" secondary={formatIsoDateTime(data.time_utc)} />
164+
<ListItemText primary="UTC Time" secondary={formatDateTime(data.utc_time)} />
166165
</ListItem>
167166
<Divider variant="inset" component="li" />
168167
<ListItem>
@@ -171,7 +170,7 @@ class NTPStatusForm extends Component<NTPStatusFormProps, NTPStatusFormState> {
171170
<AvTimerIcon />
172171
</Avatar>
173172
</ListItemAvatar>
174-
<ListItemText primary="Uptime" secondary={moment.duration(data.uptime, 'seconds').humanize()} />
173+
<ListItemText primary="Uptime" secondary={formatDuration(data.uptime)} />
175174
</ListItem>
176175
<Divider variant="inset" component="li" />
177176
</List>

interface/src/ntp/TimeFormat.ts

+43-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,45 @@
1-
import moment, { Moment } from 'moment';
1+
import parseMilliseconds from 'parse-ms';
22

3-
export const formatIsoDateTime = (isoDateString: string) => moment.parseZone(isoDateString).format('ll @ HH:mm:ss');
3+
const LOCALE_FORMAT = new Intl.DateTimeFormat(
4+
[...window.navigator.languages],
5+
{
6+
day: 'numeric',
7+
month: 'short',
8+
year: 'numeric',
9+
hour: 'numeric',
10+
minute: 'numeric',
11+
second: 'numeric',
12+
hour12: false
13+
}
14+
);
415

5-
export const formatLocalDateTime = (moment: Moment) => moment.format('YYYY-MM-DDTHH:mm');
16+
export const formatDateTime = (dateTime: string) => {
17+
return LOCALE_FORMAT.format(new Date(dateTime.substr(0, 19)));
18+
}
19+
20+
export const formatLocalDateTime = (date: Date) => {
21+
return new Date(date.getTime() - date.getTimezoneOffset() * 60000)
22+
.toISOString()
23+
.slice(0, -1)
24+
.substr(0, 19);
25+
}
26+
27+
export const formatDuration = (duration: number) => {
28+
const { days, hours, minutes, seconds } = parseMilliseconds(duration * 1000);
29+
var formatted = '';
30+
if (days) {
31+
formatted += pluralize(days, 'day');
32+
}
33+
if (formatted || hours) {
34+
formatted += pluralize(hours, 'hour');
35+
}
36+
if (formatted || minutes) {
37+
formatted += pluralize(minutes, 'minute');
38+
}
39+
if (formatted || seconds) {
40+
formatted += pluralize(seconds, 'second');
41+
}
42+
return formatted;
43+
}
44+
45+
const pluralize = (count: number, noun: string, suffix: string = 's') => ` ${count} ${noun}${count !== 1 ? suffix : ''} `;

interface/src/ntp/types.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ export enum NTPSyncStatus {
55

66
export interface NTPStatus {
77
status: NTPSyncStatus;
8-
time_utc: string;
9-
time_local: string;
8+
utc_time: string;
9+
local_time: string;
1010
server: string;
1111
uptime: number;
1212
}
@@ -19,5 +19,5 @@ export interface NTPSettings {
1919
}
2020

2121
export interface Time {
22-
time_utc: string;
22+
local_time: string;
2323
}

lib/framework/IPUtils.h

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#ifndef IPUtils_h
2+
#define IPUtils_h
3+
4+
#include <IPAddress.h>
5+
6+
const IPAddress IP_NOT_SET = IPAddress(INADDR_NONE);
7+
8+
class IPUtils {
9+
public:
10+
static bool isSet(const IPAddress& ip) {
11+
return ip != IP_NOT_SET;
12+
}
13+
static bool isNotSet(const IPAddress& ip) {
14+
return ip == IP_NOT_SET;
15+
}
16+
};
17+
18+
#endif // end IPUtils_h

lib/framework/JsonUtils.h

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
#define JsonUtils_h
33

44
#include <Arduino.h>
5-
#include <IPAddress.h>
5+
#include <IPUtils.h>
66
#include <ArduinoJson.h>
77

88
class JsonUtils {
@@ -20,7 +20,7 @@ class JsonUtils {
2020
}
2121
}
2222
static void writeIP(JsonObject& root, const String& key, const IPAddress& ip) {
23-
if (ip != INADDR_NONE) {
23+
if (IPUtils::isSet(ip)) {
2424
root[key] = ip.toString();
2525
}
2626
}

lib/framework/NTPSettingsService.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -73,9 +73,9 @@ void NTPSettingsService::configureNTP() {
7373

7474
void NTPSettingsService::configureTime(AsyncWebServerRequest* request, JsonVariant& json) {
7575
if (!sntp_enabled() && json.is<JsonObject>()) {
76-
String timeUtc = json["time_utc"];
7776
struct tm tm = {0};
78-
char* s = strptime(timeUtc.c_str(), "%Y-%m-%dT%H:%M:%SZ", &tm);
77+
String timeLocal = json["local_time"];
78+
char* s = strptime(timeLocal.c_str(), "%Y-%m-%dT%H:%M:%S", &tm);
7979
if (s != nullptr) {
8080
time_t time = mktime(&tm);
8181
struct timeval now = {.tv_sec = time};

lib/framework/NTPStatus.cpp

+18-5
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,25 @@ NTPStatus::NTPStatus(AsyncWebServer* server, SecurityManager* securityManager) {
77
AuthenticationPredicates::IS_AUTHENTICATED));
88
}
99

10-
String toISOString(tm* time, bool incOffset) {
10+
/*
11+
* Formats the time using the format provided.
12+
*
13+
* Uses a 25 byte buffer, large enough to fit an ISO time string with offset.
14+
*/
15+
String formatTime(tm* time, const char* format) {
1116
char time_string[25];
12-
strftime(time_string, 25, incOffset ? "%FT%T%z" : "%FT%TZ", time);
17+
strftime(time_string, 25, format, time);
1318
return String(time_string);
1419
}
1520

21+
String toUTCTimeString(tm* time) {
22+
return formatTime(time, "%FT%TZ");
23+
}
24+
25+
String toLocalTimeString(tm* time) {
26+
return formatTime(time, "%FT%T");
27+
}
28+
1629
void NTPStatus::ntpStatus(AsyncWebServerRequest* request) {
1730
AsyncJsonResponse* response = new AsyncJsonResponse(false, MAX_NTP_STATUS_SIZE);
1831
JsonObject root = response->getRoot();
@@ -24,10 +37,10 @@ void NTPStatus::ntpStatus(AsyncWebServerRequest* request) {
2437
root["status"] = sntp_enabled() ? 1 : 0;
2538

2639
// the current time in UTC
27-
root["time_utc"] = toISOString(gmtime(&now), false);
40+
root["utc_time"] = toUTCTimeString(gmtime(&now));
2841

29-
// local time as ISO String with TZ
30-
root["time_local"] = toISOString(localtime(&now), true);
42+
// local time with offset
43+
root["local_time"] = toLocalTimeString(localtime(&now));
3144

3245
// the sntp server name
3346
root["server"] = sntp_getservername(0);

lib/framework/WiFiSettingsService.h

+3-3
Original file line numberDiff line numberDiff line change
@@ -68,16 +68,16 @@ class WiFiSettings {
6868
JsonUtils::readIP(root, "dns_ip_2", settings.dnsIP2);
6969

7070
// Swap around the dns servers if 2 is populated but 1 is not
71-
if (settings.dnsIP1 == INADDR_NONE && settings.dnsIP2 != INADDR_NONE) {
71+
if (IPUtils::isNotSet(settings.dnsIP1) && IPUtils::isSet(settings.dnsIP2)) {
7272
settings.dnsIP1 = settings.dnsIP2;
7373
settings.dnsIP2 = INADDR_NONE;
7474
}
7575

7676
// Turning off static ip config if we don't meet the minimum requirements
7777
// of ipAddress, gateway and subnet. This may change to static ip only
7878
// as sensible defaults can be assumed for gateway and subnet
79-
if (settings.staticIPConfig &&
80-
(settings.localIP == INADDR_NONE || settings.gatewayIP == INADDR_NONE || settings.subnetMask == INADDR_NONE)) {
79+
if (settings.staticIPConfig && (IPUtils::isNotSet(settings.localIP) || IPUtils::isNotSet(settings.gatewayIP) ||
80+
IPUtils::isNotSet(settings.subnetMask))) {
8181
settings.staticIPConfig = false;
8282
}
8383
return StateUpdateResult::CHANGED;

lib/framework/WiFiStatus.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -63,10 +63,10 @@ void WiFiStatus::wifiStatus(AsyncWebServerRequest* request) {
6363
root["gateway_ip"] = WiFi.gatewayIP().toString();
6464
IPAddress dnsIP1 = WiFi.dnsIP(0);
6565
IPAddress dnsIP2 = WiFi.dnsIP(1);
66-
if (dnsIP1 != INADDR_NONE) {
66+
if (IPUtils::isSet(dnsIP1)) {
6767
root["dns_ip_1"] = dnsIP1.toString();
6868
}
69-
if (dnsIP2 != INADDR_NONE) {
69+
if (IPUtils::isSet(dnsIP2)) {
7070
root["dns_ip_2"] = dnsIP2.toString();
7171
}
7272
}

lib/framework/WiFiStatus.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
#include <ArduinoJson.h>
1313
#include <AsyncJson.h>
1414
#include <ESPAsyncWebServer.h>
15-
#include <IPAddress.h>
15+
#include <IPUtils.h>
1616
#include <SecurityManager.h>
1717

1818
#define MAX_WIFI_STATUS_SIZE 1024

0 commit comments

Comments
 (0)