mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2025-12-06 07:49:52 +03:00
adding esp8266-react's latest NTP library
This commit is contained in:
14
.gitignore
vendored
14
.gitignore
vendored
@@ -11,16 +11,18 @@ debug.log
|
|||||||
# platformio
|
# platformio
|
||||||
.pio
|
.pio
|
||||||
pio_local.ini
|
pio_local.ini
|
||||||
.VSCodeCounter/
|
/.VSCodeCounter
|
||||||
|
|
||||||
# OS specific
|
# OS specific
|
||||||
.DS_Store
|
.DS_Store
|
||||||
*Thumbs.db
|
*Thumbs.db
|
||||||
|
|
||||||
# project specfic
|
# project specfic
|
||||||
scripts/stackdmp.txt
|
/scripts/stackdmp.txt
|
||||||
emsesp
|
emsesp
|
||||||
data/www/
|
/data/www
|
||||||
lib/framework/WWWData.h
|
/lib/framework/WWWData.h
|
||||||
interface/build/
|
/interface/build
|
||||||
interface/node_modules/
|
/interface/node_modules
|
||||||
|
/interface/.eslintcache
|
||||||
|
|
||||||
|
|||||||
9715
interface/package-lock.json
generated
9715
interface/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -3,32 +3,31 @@
|
|||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@material-ui/core": "^4.11.0",
|
"@material-ui/core": "^4.11.2",
|
||||||
"@material-ui/icons": "^4.9.1",
|
"@material-ui/icons": "^4.11.2",
|
||||||
"@types/jwt-decode": "^3.1.0",
|
|
||||||
"@types/lodash": "^4.14.165",
|
"@types/lodash": "^4.14.165",
|
||||||
"@types/node": "^12.12.32",
|
"@types/node": "^12.12.32",
|
||||||
"@types/react": "^16.9.56",
|
"@types/react": "^17.0.0",
|
||||||
"@types/react-dom": "^16.9.9",
|
"@types/react-dom": "^17.0.0",
|
||||||
"@types/react-material-ui-form-validator": "^2.1.0",
|
"@types/react-material-ui-form-validator": "^2.1.0",
|
||||||
"@types/react-router": "^5.1.8",
|
"@types/react-router": "^5.1.8",
|
||||||
"@types/react-router-dom": "^5.1.6",
|
"@types/react-router-dom": "^5.1.6",
|
||||||
"compression-webpack-plugin": "^4.0.0",
|
"compression-webpack-plugin": "^4.0.0",
|
||||||
"jwt-decode": "^3.1.1",
|
"jwt-decode": "^3.1.2",
|
||||||
"lodash": "^4.17.20",
|
"lodash": "^4.17.20",
|
||||||
"mime-types": "^2.1.27",
|
"mime-types": "^2.1.28",
|
||||||
"moment": "^2.29.1",
|
"notistack": "^1.0.3",
|
||||||
"notistack": "^1.0.1",
|
"parse-ms": "^2.1.0",
|
||||||
"react": "^16.14.0",
|
"react": "^17.0.1",
|
||||||
"react-dom": "^16.14.0",
|
"react-dom": "^17.0.1",
|
||||||
"react-dropzone": "^11.2.4",
|
"react-dropzone": "^11.2.4",
|
||||||
"react-form-validator-core": "^1.0.0",
|
"react-form-validator-core": "^1.0.0",
|
||||||
"react-material-ui-form-validator": "^2.1.1",
|
"react-material-ui-form-validator": "^2.1.1",
|
||||||
"react-router": "^5.2.0",
|
"react-router": "^5.2.0",
|
||||||
"react-router-dom": "^5.2.0",
|
"react-router-dom": "^5.2.0",
|
||||||
"react-scripts": "3.4.4",
|
"react-scripts": "4.0.1",
|
||||||
"sockette": "^2.0.6",
|
"sockette": "^2.0.6",
|
||||||
"typescript": "^4.0.2",
|
"typescript": "4.0.5",
|
||||||
"zlib": "^1.0.5"
|
"zlib": "^1.0.5"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@@ -52,6 +51,6 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"react-app-rewired": "^2.1.6"
|
"react-app-rewired": "^2.1.8"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ type APSettingsFormProps = RestFormProps<APSettings>;
|
|||||||
|
|
||||||
class APSettingsForm extends React.Component<APSettingsFormProps> {
|
class APSettingsForm extends React.Component<APSettingsFormProps> {
|
||||||
|
|
||||||
componentWillMount() {
|
componentDidMount() {
|
||||||
ValidatorForm.addValidationRule('isIP', isIP);
|
ValidatorForm.addValidationRule('isIP', isIP);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { Redirect, Route, RouteProps, RouteComponentProps } from "react-router-d
|
|||||||
import { withSnackbar, WithSnackbarProps } from 'notistack';
|
import { withSnackbar, WithSnackbarProps } from 'notistack';
|
||||||
|
|
||||||
import * as Authentication from './Authentication';
|
import * as Authentication from './Authentication';
|
||||||
import { withAuthenticationContext, AuthenticationContextProps, AuthenticatedContext } from './AuthenticationContext';
|
import { withAuthenticationContext, AuthenticationContextProps, AuthenticatedContext, AuthenticatedContextValue } from './AuthenticationContext';
|
||||||
|
|
||||||
type ChildComponent = React.ComponentType<RouteComponentProps<any>> | React.ComponentType<any>;
|
type ChildComponent = React.ComponentType<RouteComponentProps<any>> | React.ComponentType<any>;
|
||||||
|
|
||||||
@@ -21,7 +21,7 @@ export class AuthenticatedRoute extends React.Component<AuthenticatedRouteProps>
|
|||||||
const renderComponent: RenderComponent = (props) => {
|
const renderComponent: RenderComponent = (props) => {
|
||||||
if (authenticationContext.me) {
|
if (authenticationContext.me) {
|
||||||
return (
|
return (
|
||||||
<AuthenticatedContext.Provider value={authenticationContext as AuthenticatedContext}>
|
<AuthenticatedContext.Provider value={authenticationContext as AuthenticatedContextValue}>
|
||||||
<Component {...props} />
|
<Component {...props} />
|
||||||
</AuthenticatedContext.Provider>
|
</AuthenticatedContext.Provider>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -6,20 +6,20 @@ export interface Me {
|
|||||||
version: string; // proddy added
|
version: string; // proddy added
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AuthenticationContext {
|
export interface AuthenticationContextValue {
|
||||||
refresh: () => void;
|
refresh: () => void;
|
||||||
signIn: (accessToken: string) => void;
|
signIn: (accessToken: string) => void;
|
||||||
signOut: () => void;
|
signOut: () => void;
|
||||||
me?: Me;
|
me?: Me;
|
||||||
}
|
}
|
||||||
|
|
||||||
const AuthenticationContextDefaultValue = {} as AuthenticationContext
|
const AuthenticationContextDefaultValue = {} as AuthenticationContextValue
|
||||||
export const AuthenticationContext = React.createContext(
|
export const AuthenticationContext = React.createContext(
|
||||||
AuthenticationContextDefaultValue
|
AuthenticationContextDefaultValue
|
||||||
);
|
);
|
||||||
|
|
||||||
export interface AuthenticationContextProps {
|
export interface AuthenticationContextProps {
|
||||||
authenticationContext: AuthenticationContext;
|
authenticationContext: AuthenticationContextValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function withAuthenticationContext<T extends AuthenticationContextProps>(Component: React.ComponentType<T>) {
|
export function withAuthenticationContext<T extends AuthenticationContextProps>(Component: React.ComponentType<T>) {
|
||||||
@@ -34,17 +34,17 @@ export function withAuthenticationContext<T extends AuthenticationContextProps>(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AuthenticatedContext extends AuthenticationContext {
|
export interface AuthenticatedContextValue extends AuthenticationContextValue {
|
||||||
me: Me;
|
me: Me;
|
||||||
}
|
}
|
||||||
|
|
||||||
const AuthenticatedContextDefaultValue = {} as AuthenticatedContext
|
const AuthenticatedContextDefaultValue = {} as AuthenticatedContextValue
|
||||||
export const AuthenticatedContext = React.createContext(
|
export const AuthenticatedContext = React.createContext(
|
||||||
AuthenticatedContextDefaultValue
|
AuthenticatedContextDefaultValue
|
||||||
);
|
);
|
||||||
|
|
||||||
export interface AuthenticatedContextProps {
|
export interface AuthenticatedContextProps {
|
||||||
authenticatedContext: AuthenticatedContext;
|
authenticatedContext: AuthenticatedContextValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function withAuthenticatedContext<T extends AuthenticatedContextProps>(Component: React.ComponentType<T>) {
|
export function withAuthenticatedContext<T extends AuthenticatedContextProps>(Component: React.ComponentType<T>) {
|
||||||
|
|||||||
@@ -5,14 +5,14 @@ import jwtDecode from 'jwt-decode';
|
|||||||
import history from '../history'
|
import history from '../history'
|
||||||
import { VERIFY_AUTHORIZATION_ENDPOINT } from '../api';
|
import { VERIFY_AUTHORIZATION_ENDPOINT } from '../api';
|
||||||
import { ACCESS_TOKEN, authorizedFetch, getStorage } from './Authentication';
|
import { ACCESS_TOKEN, authorizedFetch, getStorage } from './Authentication';
|
||||||
import { AuthenticationContext, Me } from './AuthenticationContext';
|
import { AuthenticationContext, AuthenticationContextValue, Me } from './AuthenticationContext';
|
||||||
import FullScreenLoading from '../components/FullScreenLoading';
|
import FullScreenLoading from '../components/FullScreenLoading';
|
||||||
import { withFeatures, WithFeaturesProps } from '../features/FeaturesContext';
|
import { withFeatures, WithFeaturesProps } from '../features/FeaturesContext';
|
||||||
|
|
||||||
export const decodeMeJWT = (accessToken: string): Me => jwtDecode(accessToken) as Me;
|
export const decodeMeJWT = (accessToken: string): Me => jwtDecode(accessToken) as Me;
|
||||||
|
|
||||||
interface AuthenticationWrapperState {
|
interface AuthenticationWrapperState {
|
||||||
context: AuthenticationContext;
|
context: AuthenticationContextValue;
|
||||||
initialized: boolean;
|
initialized: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,23 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import { Features } from './types';
|
|
||||||
|
|
||||||
export interface ApplicationContext {
|
|
||||||
features: Features;
|
|
||||||
}
|
|
||||||
|
|
||||||
const ApplicationContextDefaultValue = {} as ApplicationContext
|
|
||||||
export const ApplicationContext = React.createContext(
|
|
||||||
ApplicationContextDefaultValue
|
|
||||||
);
|
|
||||||
|
|
||||||
export function withAuthenticatedContexApplicationContext<T extends ApplicationContext>(Component: React.ComponentType<T>) {
|
|
||||||
return class extends React.Component<Omit<T, keyof ApplicationContext>> {
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<ApplicationContext.Consumer>
|
|
||||||
{authenticatedContext => <Component {...this.props as T} features={authenticatedContext} />}
|
|
||||||
</ApplicationContext.Consumer>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Features } from './types';
|
import { Features } from './types';
|
||||||
|
|
||||||
export interface FeaturesContext {
|
export interface FeaturesContextValue {
|
||||||
features: Features;
|
features: Features;
|
||||||
}
|
}
|
||||||
|
|
||||||
const FeaturesContextDefaultValue = {} as FeaturesContext
|
const FeaturesContextDefaultValue = {} as FeaturesContextValue
|
||||||
export const FeaturesContext = React.createContext(
|
export const FeaturesContext = React.createContext(
|
||||||
FeaturesContextDefaultValue
|
FeaturesContextDefaultValue
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -9,12 +9,13 @@ import { MenuAppBar } from '../components';
|
|||||||
import NetworkStatusController from './NetworkStatusController';
|
import NetworkStatusController from './NetworkStatusController';
|
||||||
import NetworkSettingsController from './NetworkSettingsController';
|
import NetworkSettingsController from './NetworkSettingsController';
|
||||||
import WiFiNetworkScanner from './WiFiNetworkScanner';
|
import WiFiNetworkScanner from './WiFiNetworkScanner';
|
||||||
import { NetworkConnectionContext } from './NetworkConnectionContext';
|
import { NetworkConnectionContext, NetworkConnectionContextValue } from './NetworkConnectionContext';
|
||||||
|
|
||||||
import { WiFiNetwork } from './types';
|
import { WiFiNetwork } from './types';
|
||||||
|
|
||||||
type NetworkConnectionProps = AuthenticatedContextProps & RouteComponentProps;
|
type NetworkConnectionProps = AuthenticatedContextProps & RouteComponentProps;
|
||||||
|
|
||||||
class NetworkConnection extends Component<NetworkConnectionProps, NetworkConnectionContext> {
|
class NetworkConnection extends Component<NetworkConnectionProps, NetworkConnectionContextValue> {
|
||||||
|
|
||||||
constructor(props: NetworkConnectionProps) {
|
constructor(props: NetworkConnectionProps) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { WiFiNetwork } from './types';
|
import { WiFiNetwork } from './types';
|
||||||
|
|
||||||
export interface NetworkConnectionContext {
|
export interface NetworkConnectionContextValue {
|
||||||
selectedNetwork?: WiFiNetwork;
|
selectedNetwork?: WiFiNetwork;
|
||||||
selectNetwork: (network: WiFiNetwork) => void;
|
selectNetwork: (network: WiFiNetwork) => void;
|
||||||
deselectNetwork: () => void;
|
deselectNetwork: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const NetworkConnectionContextDefaultValue = {} as NetworkConnectionContext
|
const NetworkConnectionContextDefaultValue = {} as NetworkConnectionContextValue
|
||||||
export const NetworkConnectionContext = React.createContext(
|
export const NetworkConnectionContext = React.createContext(
|
||||||
NetworkConnectionContextDefaultValue
|
NetworkConnectionContextDefaultValue
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import MenuItem from '@material-ui/core/MenuItem';
|
|||||||
import { RestFormProps, PasswordValidator, BlockFormControlLabel, FormActions, FormButton } from '../components';
|
import { RestFormProps, PasswordValidator, BlockFormControlLabel, FormActions, FormButton } from '../components';
|
||||||
import { isIP, isHostname, optional } from '../validators';
|
import { isIP, isHostname, optional } from '../validators';
|
||||||
|
|
||||||
import { NetworkConnectionContext } from './NetworkConnectionContext';
|
import { NetworkConnectionContext, NetworkConnectionContextValue } from './NetworkConnectionContext';
|
||||||
import { isNetworkOpen, networkSecurityMode } from './WiFiSecurityModes';
|
import { isNetworkOpen, networkSecurityMode } from './WiFiSecurityModes';
|
||||||
import { NetworkSettings } from './types';
|
import { NetworkSettings } from './types';
|
||||||
|
|
||||||
@@ -25,7 +25,7 @@ class NetworkSettingsForm extends React.Component<NetworkStatusFormProps> {
|
|||||||
static contextType = NetworkConnectionContext;
|
static contextType = NetworkConnectionContext;
|
||||||
context!: React.ContextType<typeof NetworkConnectionContext>;
|
context!: React.ContextType<typeof NetworkConnectionContext>;
|
||||||
|
|
||||||
constructor(props: NetworkStatusFormProps, context: NetworkConnectionContext) {
|
constructor(props: NetworkStatusFormProps, context: NetworkConnectionContextValue) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
const { selectedNetwork } = context;
|
const { selectedNetwork } = context;
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import React, { Component, Fragment } from 'react';
|
import React, { Component, Fragment } from 'react';
|
||||||
import moment from 'moment';
|
|
||||||
|
|
||||||
import { WithTheme, withTheme } from '@material-ui/core/styles';
|
import { WithTheme, withTheme } from '@material-ui/core/styles';
|
||||||
import { Avatar, Divider, List, ListItem, ListItemAvatar, ListItemText, Button } from '@material-ui/core';
|
import { Avatar, Divider, List, ListItem, ListItemAvatar, ListItemText, Button } from '@material-ui/core';
|
||||||
@@ -14,7 +13,7 @@ import RefreshIcon from '@material-ui/icons/Refresh';
|
|||||||
|
|
||||||
import { RestFormProps, FormButton, HighlightAvatar } from '../components';
|
import { RestFormProps, FormButton, HighlightAvatar } from '../components';
|
||||||
import { isNtpActive, ntpStatusHighlight, ntpStatus } from './NTPStatus';
|
import { isNtpActive, ntpStatusHighlight, ntpStatus } from './NTPStatus';
|
||||||
import { formatIsoDateTime, formatLocalDateTime } from './TimeFormat';
|
import { formatDuration, formatDateTime, formatLocalDateTimeNow, formatLocalDateTime } from './TimeFormat';
|
||||||
import { NTPStatus, Time } from './types';
|
import { NTPStatus, Time } from './types';
|
||||||
import { redirectingAuthorizedFetch, withAuthenticatedContext, AuthenticatedContextProps } from '../authentication';
|
import { redirectingAuthorizedFetch, withAuthenticatedContext, AuthenticatedContextProps } from '../authentication';
|
||||||
import { TIME_ENDPOINT } from '../api';
|
import { TIME_ENDPOINT } from '../api';
|
||||||
@@ -43,30 +42,21 @@ class NTPStatusForm extends Component<NTPStatusFormProps, NTPStatusFormState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
openSetTime = () => {
|
openSetTime = () => {
|
||||||
this.setState({ localTime: formatLocalDateTime(moment()), settingTime: true, });
|
this.setState({ localTime: formatLocalDateTimeNow(), settingTime: true, });
|
||||||
}
|
}
|
||||||
|
|
||||||
closeSetTime = () => {
|
closeSetTime = () => {
|
||||||
this.setState({ settingTime: false });
|
this.setState({ settingTime: false });
|
||||||
}
|
}
|
||||||
|
|
||||||
createAdjustedTime = (): Time => {
|
createTime = (): Time => ({ local_time: formatLocalDateTime(this.state.localTime) });
|
||||||
const currentLocalTime = moment(this.props.data.time_local);
|
|
||||||
const newLocalTime = moment(this.state.localTime);
|
|
||||||
newLocalTime.subtract(currentLocalTime.utcOffset())
|
|
||||||
newLocalTime.milliseconds(0);
|
|
||||||
newLocalTime.utc();
|
|
||||||
return {
|
|
||||||
time_utc: newLocalTime.format()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
configureTime = () => {
|
configureTime = () => {
|
||||||
this.setState({ processing: true });
|
this.setState({ processing: true });
|
||||||
redirectingAuthorizedFetch(TIME_ENDPOINT,
|
redirectingAuthorizedFetch(TIME_ENDPOINT,
|
||||||
{
|
{
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: JSON.stringify(this.createAdjustedTime()),
|
body: JSON.stringify(this.createTime()),
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json'
|
||||||
}
|
}
|
||||||
@@ -153,7 +143,7 @@ class NTPStatusForm extends Component<NTPStatusFormProps, NTPStatusFormState> {
|
|||||||
<AccessTimeIcon />
|
<AccessTimeIcon />
|
||||||
</Avatar>
|
</Avatar>
|
||||||
</ListItemAvatar>
|
</ListItemAvatar>
|
||||||
<ListItemText primary="Local Time" secondary={formatIsoDateTime(data.time_local)} />
|
<ListItemText primary="Local Time" secondary={formatDateTime(data.local_time)} />
|
||||||
</ListItem>
|
</ListItem>
|
||||||
<Divider variant="inset" component="li" />
|
<Divider variant="inset" component="li" />
|
||||||
<ListItem>
|
<ListItem>
|
||||||
@@ -162,7 +152,7 @@ class NTPStatusForm extends Component<NTPStatusFormProps, NTPStatusFormState> {
|
|||||||
<SwapVerticalCircleIcon />
|
<SwapVerticalCircleIcon />
|
||||||
</Avatar>
|
</Avatar>
|
||||||
</ListItemAvatar>
|
</ListItemAvatar>
|
||||||
<ListItemText primary="UTC Time" secondary={formatIsoDateTime(data.time_utc)} />
|
<ListItemText primary="UTC Time" secondary={formatDateTime(data.utc_time)} />
|
||||||
</ListItem>
|
</ListItem>
|
||||||
<Divider variant="inset" component="li" />
|
<Divider variant="inset" component="li" />
|
||||||
<ListItem>
|
<ListItem>
|
||||||
@@ -171,7 +161,7 @@ class NTPStatusForm extends Component<NTPStatusFormProps, NTPStatusFormState> {
|
|||||||
<AvTimerIcon />
|
<AvTimerIcon />
|
||||||
</Avatar>
|
</Avatar>
|
||||||
</ListItemAvatar>
|
</ListItemAvatar>
|
||||||
<ListItemText primary="Uptime" secondary={moment.duration(data.uptime, 'seconds').humanize()} />
|
<ListItemText primary="Uptime" secondary={formatDuration(data.uptime)} />
|
||||||
</ListItem>
|
</ListItem>
|
||||||
<Divider variant="inset" component="li" />
|
<Divider variant="inset" component="li" />
|
||||||
</List>
|
</List>
|
||||||
|
|||||||
@@ -1,5 +1,45 @@
|
|||||||
import moment, { Moment } from 'moment';
|
import parseMilliseconds from 'parse-ms';
|
||||||
|
|
||||||
export const formatIsoDateTime = (isoDateString: string) => moment.parseZone(isoDateString).format('ll @ HH:mm:ss');
|
const LOCALE_FORMAT = new Intl.DateTimeFormat('default', {
|
||||||
|
day: 'numeric',
|
||||||
|
month: 'short',
|
||||||
|
year: 'numeric',
|
||||||
|
hour: 'numeric',
|
||||||
|
minute: 'numeric',
|
||||||
|
second: 'numeric',
|
||||||
|
hour12: false
|
||||||
|
});
|
||||||
|
|
||||||
export const formatLocalDateTime = (moment: Moment) => moment.format('YYYY-MM-DDTHH:mm');
|
export const formatDateTime = (dateTime: string) => {
|
||||||
|
return LOCALE_FORMAT.format(new Date(dateTime.substr(0, 19)));
|
||||||
|
}
|
||||||
|
|
||||||
|
export const formatLocalDateTimeNow = () => {
|
||||||
|
return formatIsoDateTime(new Date()).substr(0, 19);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const formatLocalDateTime = (dateTime: string) => {
|
||||||
|
return formatIsoDateTime(new Date(dateTime)).substr(0, 19);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const formatIsoDateTime = (date: Date) => {
|
||||||
|
return new Date(date.getTime() - date.getTimezoneOffset() * 60000).toISOString().slice(0, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const formatDuration = (duration: number) => {
|
||||||
|
const { days, hours, minutes, seconds } = parseMilliseconds(duration * 1000);
|
||||||
|
var formatted = '';
|
||||||
|
if (days) {
|
||||||
|
formatted += days + ' days ';
|
||||||
|
}
|
||||||
|
if (formatted || hours) {
|
||||||
|
formatted += hours + ' hours ';
|
||||||
|
}
|
||||||
|
if (formatted || minutes) {
|
||||||
|
formatted += minutes + ' minutes ';
|
||||||
|
}
|
||||||
|
if (formatted || seconds) {
|
||||||
|
formatted += seconds + ' seconds';
|
||||||
|
}
|
||||||
|
return formatted;
|
||||||
|
}
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
|
||||||
export enum NTPSyncStatus {
|
export enum NTPSyncStatus {
|
||||||
NTP_INACTIVE = 0,
|
NTP_INACTIVE = 0,
|
||||||
NTP_ACTIVE = 1
|
NTP_ACTIVE = 1
|
||||||
@@ -5,8 +6,8 @@ export enum NTPSyncStatus {
|
|||||||
|
|
||||||
export interface NTPStatus {
|
export interface NTPStatus {
|
||||||
status: NTPSyncStatus;
|
status: NTPSyncStatus;
|
||||||
time_utc: string;
|
utc_time: string;
|
||||||
time_local: string;
|
local_time: string;
|
||||||
server: string;
|
server: string;
|
||||||
uptime: number;
|
uptime: number;
|
||||||
}
|
}
|
||||||
@@ -19,5 +20,5 @@ export interface NTPSettings {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface Time {
|
export interface Time {
|
||||||
time_utc: string;
|
local_time: string;
|
||||||
}
|
}
|
||||||
@@ -1 +1,3 @@
|
|||||||
export default (validator: (value: any) => boolean) => (value: any) => !value || validator(value);
|
const OPTIONAL = (validator: (value: any) => boolean) => (value: any) => !value || validator(value);
|
||||||
|
|
||||||
|
export default OPTIONAL;
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
export default (validator1: (value: any) => boolean, validator2: (value: any) => boolean) => {
|
const OR = (validator1: (value: any) => boolean, validator2: (value: any) => boolean) => {
|
||||||
return (value: any) => validator1(value) || validator2(value);
|
return (value: any) => validator1(value) || validator2(value);
|
||||||
}
|
};
|
||||||
|
|
||||||
|
export default OR;
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,8 @@
|
|||||||
"resolveJsonModule": true,
|
"resolveJsonModule": true,
|
||||||
"isolatedModules": true,
|
"isolatedModules": true,
|
||||||
"noEmit": true,
|
"noEmit": true,
|
||||||
"jsx": "react"
|
"jsx": "react",
|
||||||
|
"noFallthroughCasesInSwitch": true
|
||||||
},
|
},
|
||||||
"include": [
|
"include": [
|
||||||
"src"
|
"src"
|
||||||
|
|||||||
@@ -1,17 +1,28 @@
|
|||||||
#include <NTPStatus.h>
|
#include <NTPStatus.h>
|
||||||
|
|
||||||
NTPStatus::NTPStatus(AsyncWebServer * server, SecurityManager * securityManager) {
|
NTPStatus::NTPStatus(AsyncWebServer * server, SecurityManager * securityManager) {
|
||||||
server->on(NTP_STATUS_SERVICE_PATH,
|
server->on(NTP_STATUS_SERVICE_PATH, HTTP_GET, securityManager->wrapRequest(std::bind(&NTPStatus::ntpStatus, this, std::placeholders::_1), AuthenticationPredicates::IS_AUTHENTICATED));
|
||||||
HTTP_GET,
|
|
||||||
securityManager->wrapRequest(std::bind(&NTPStatus::ntpStatus, this, std::placeholders::_1), AuthenticationPredicates::IS_AUTHENTICATED));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
String toISOString(tm * time, bool incOffset) {
|
/*
|
||||||
|
* Formats the time using the format provided.
|
||||||
|
*
|
||||||
|
* Uses a 25 byte buffer, large enough to fit an ISO time string with offset.
|
||||||
|
*/
|
||||||
|
String formatTime(tm * time, const char * format) {
|
||||||
char time_string[25];
|
char time_string[25];
|
||||||
strftime(time_string, 25, incOffset ? "%FT%T%z" : "%FT%TZ", time);
|
strftime(time_string, 25, format, time);
|
||||||
return String(time_string);
|
return String(time_string);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String toUTCTimeString(tm * time) {
|
||||||
|
return formatTime(time, "%FT%TZ");
|
||||||
|
}
|
||||||
|
|
||||||
|
String toLocalTimeString(tm * time) {
|
||||||
|
return formatTime(time, "%FT%T");
|
||||||
|
}
|
||||||
|
|
||||||
void NTPStatus::ntpStatus(AsyncWebServerRequest * request) {
|
void NTPStatus::ntpStatus(AsyncWebServerRequest * request) {
|
||||||
AsyncJsonResponse * response = new AsyncJsonResponse(false, MAX_NTP_STATUS_SIZE);
|
AsyncJsonResponse * response = new AsyncJsonResponse(false, MAX_NTP_STATUS_SIZE);
|
||||||
JsonObject root = response->getRoot();
|
JsonObject root = response->getRoot();
|
||||||
@@ -23,10 +34,10 @@ void NTPStatus::ntpStatus(AsyncWebServerRequest * request) {
|
|||||||
root["status"] = sntp_enabled() ? 1 : 0;
|
root["status"] = sntp_enabled() ? 1 : 0;
|
||||||
|
|
||||||
// the current time in UTC
|
// the current time in UTC
|
||||||
root["time_utc"] = toISOString(gmtime(&now), false);
|
root["utc_time"] = toUTCTimeString(gmtime(&now));
|
||||||
|
|
||||||
// local time as ISO String with TZ
|
// local time as ISO String with TZ
|
||||||
root["time_local"] = toISOString(localtime(&now), true);
|
root["local_time"] = toLocalTimeString(localtime(&now));
|
||||||
|
|
||||||
// the sntp server name
|
// the sntp server name
|
||||||
root["server"] = sntp_getservername(0);
|
root["server"] = sntp_getservername(0);
|
||||||
|
|||||||
Reference in New Issue
Block a user