mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2025-12-07 16:29:51 +03:00
auto formatting
This commit is contained in:
@@ -1,37 +1,51 @@
|
||||
const ManifestPlugin = require('webpack-manifest-plugin');
|
||||
const WorkboxWebpackPlugin = require('workbox-webpack-plugin');
|
||||
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
||||
const CompressionPlugin = require('compression-webpack-plugin');
|
||||
const ProgmemGenerator = require('./progmem-generator.js');
|
||||
const ManifestPlugin = require('webpack-manifest-plugin')
|
||||
const WorkboxWebpackPlugin = require('workbox-webpack-plugin')
|
||||
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
|
||||
const CompressionPlugin = require('compression-webpack-plugin')
|
||||
const ProgmemGenerator = require('./progmem-generator.js')
|
||||
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const path = require('path')
|
||||
const fs = require('fs')
|
||||
|
||||
module.exports = function override(config, env) {
|
||||
if (env === "production") {
|
||||
if (env === 'production') {
|
||||
// rename the output file, we need it's path to be short for LittleFS
|
||||
config.output.filename = 'js/[id].[chunkhash:4].js';
|
||||
config.output.chunkFilename = 'js/[id].[chunkhash:4].js';
|
||||
config.output.filename = 'js/[id].[chunkhash:4].js'
|
||||
config.output.chunkFilename = 'js/[id].[chunkhash:4].js'
|
||||
|
||||
// take out the manifest and service worker plugins
|
||||
config.plugins = config.plugins.filter(plugin => !(plugin instanceof ManifestPlugin));
|
||||
config.plugins = config.plugins.filter(plugin => !(plugin instanceof WorkboxWebpackPlugin.GenerateSW));
|
||||
config.plugins = config.plugins.filter(
|
||||
(plugin) => !(plugin instanceof ManifestPlugin),
|
||||
)
|
||||
config.plugins = config.plugins.filter(
|
||||
(plugin) => !(plugin instanceof WorkboxWebpackPlugin.GenerateSW),
|
||||
)
|
||||
|
||||
// shorten css filenames
|
||||
const miniCssExtractPlugin = config.plugins.find((plugin) => plugin instanceof MiniCssExtractPlugin);
|
||||
miniCssExtractPlugin.options.filename = "css/[id].[contenthash:4].css";
|
||||
miniCssExtractPlugin.options.chunkFilename = "css/[id].[contenthash:4].c.css";
|
||||
const miniCssExtractPlugin = config.plugins.find(
|
||||
(plugin) => plugin instanceof MiniCssExtractPlugin,
|
||||
)
|
||||
miniCssExtractPlugin.options.filename = 'css/[id].[contenthash:4].css'
|
||||
miniCssExtractPlugin.options.chunkFilename =
|
||||
'css/[id].[contenthash:4].c.css'
|
||||
|
||||
// build progmem data files
|
||||
config.plugins.push(new ProgmemGenerator({ outputPath: "../lib/framework/WWWData.h", bytesPerLine: 20 }));
|
||||
config.plugins.push(
|
||||
new ProgmemGenerator({
|
||||
outputPath: '../lib/framework/WWWData.h',
|
||||
bytesPerLine: 20,
|
||||
}),
|
||||
)
|
||||
|
||||
// add compression plugin, compress javascript
|
||||
config.plugins.push(new CompressionPlugin({
|
||||
filename: "[path].gz[query]",
|
||||
algorithm: "gzip",
|
||||
test: /\.(js)$/,
|
||||
deleteOriginalAssets: true
|
||||
}));
|
||||
config.plugins.push(
|
||||
new CompressionPlugin({
|
||||
filename: '[path].gz[query]',
|
||||
algorithm: 'gzip',
|
||||
test: /\.(js)$/,
|
||||
deleteOriginalAssets: true,
|
||||
}),
|
||||
)
|
||||
}
|
||||
return config;
|
||||
return config
|
||||
}
|
||||
|
||||
@@ -1,91 +1,108 @@
|
||||
const { resolve, relative, sep } = require('path');
|
||||
const { readdirSync, existsSync, unlinkSync, readFileSync, createWriteStream } = require('fs');
|
||||
var zlib = require('zlib');
|
||||
var mime = require('mime-types');
|
||||
const { resolve, relative, sep } = require('path')
|
||||
const {
|
||||
readdirSync,
|
||||
existsSync,
|
||||
unlinkSync,
|
||||
readFileSync,
|
||||
createWriteStream,
|
||||
} = require('fs')
|
||||
var zlib = require('zlib')
|
||||
var mime = require('mime-types')
|
||||
|
||||
const ARDUINO_INCLUDES = "#include <Arduino.h>\n\n";
|
||||
const ARDUINO_INCLUDES = '#include <Arduino.h>\n\n'
|
||||
|
||||
function getFilesSync(dir, files = []) {
|
||||
readdirSync(dir, { withFileTypes: true }).forEach(entry => {
|
||||
const entryPath = resolve(dir, entry.name);
|
||||
readdirSync(dir, { withFileTypes: true }).forEach((entry) => {
|
||||
const entryPath = resolve(dir, entry.name)
|
||||
if (entry.isDirectory()) {
|
||||
getFilesSync(entryPath, files);
|
||||
getFilesSync(entryPath, files)
|
||||
} else {
|
||||
files.push(entryPath);
|
||||
files.push(entryPath)
|
||||
}
|
||||
})
|
||||
return files;
|
||||
return files
|
||||
}
|
||||
|
||||
function coherseToBuffer(input) {
|
||||
return Buffer.isBuffer(input) ? input : Buffer.from(input);
|
||||
return Buffer.isBuffer(input) ? input : Buffer.from(input)
|
||||
}
|
||||
|
||||
function cleanAndOpen(path) {
|
||||
if (existsSync(path)) {
|
||||
unlinkSync(path);
|
||||
unlinkSync(path)
|
||||
}
|
||||
return createWriteStream(path, { flags: "w+" });
|
||||
return createWriteStream(path, { flags: 'w+' })
|
||||
}
|
||||
|
||||
class ProgmemGenerator {
|
||||
|
||||
constructor(options = {}) {
|
||||
const { outputPath, bytesPerLine = 20, indent = " ", includes = ARDUINO_INCLUDES } = options;
|
||||
this.options = { outputPath, bytesPerLine, indent, includes };
|
||||
const {
|
||||
outputPath,
|
||||
bytesPerLine = 20,
|
||||
indent = ' ',
|
||||
includes = ARDUINO_INCLUDES,
|
||||
} = options
|
||||
this.options = { outputPath, bytesPerLine, indent, includes }
|
||||
}
|
||||
|
||||
apply(compiler) {
|
||||
compiler.hooks.emit.tapAsync(
|
||||
{ name: 'ProgmemGenerator' },
|
||||
(compilation, callback) => {
|
||||
const { outputPath, bytesPerLine, indent, includes } = this.options;
|
||||
const fileInfo = [];
|
||||
const writeStream = cleanAndOpen(resolve(compilation.options.context, outputPath));
|
||||
const { outputPath, bytesPerLine, indent, includes } = this.options
|
||||
const fileInfo = []
|
||||
const writeStream = cleanAndOpen(
|
||||
resolve(compilation.options.context, outputPath),
|
||||
)
|
||||
try {
|
||||
const writeIncludes = () => {
|
||||
writeStream.write(includes);
|
||||
writeStream.write(includes)
|
||||
}
|
||||
|
||||
const writeFile = (relativeFilePath, buffer) => {
|
||||
const variable = "ESP_REACT_DATA_" + fileInfo.length;
|
||||
const mimeType = mime.lookup(relativeFilePath);
|
||||
var size = 0;
|
||||
writeStream.write("const uint8_t " + variable + "[] PROGMEM = {");
|
||||
const zipBuffer = zlib.gzipSync(buffer);
|
||||
const variable = 'ESP_REACT_DATA_' + fileInfo.length
|
||||
const mimeType = mime.lookup(relativeFilePath)
|
||||
var size = 0
|
||||
writeStream.write('const uint8_t ' + variable + '[] PROGMEM = {')
|
||||
const zipBuffer = zlib.gzipSync(buffer)
|
||||
zipBuffer.forEach((b) => {
|
||||
if (!(size % bytesPerLine)) {
|
||||
writeStream.write("\n");
|
||||
writeStream.write(indent);
|
||||
writeStream.write('\n')
|
||||
writeStream.write(indent)
|
||||
}
|
||||
writeStream.write("0x" + ("00" + b.toString(16).toUpperCase()).substr(-2) + ",");
|
||||
size++;
|
||||
});
|
||||
writeStream.write(
|
||||
'0x' + ('00' + b.toString(16).toUpperCase()).substr(-2) + ',',
|
||||
)
|
||||
size++
|
||||
})
|
||||
if (size % bytesPerLine) {
|
||||
writeStream.write("\n");
|
||||
writeStream.write('\n')
|
||||
}
|
||||
writeStream.write("};\n\n");
|
||||
writeStream.write('};\n\n')
|
||||
fileInfo.push({
|
||||
uri: '/' + relativeFilePath.replace(sep, '/'),
|
||||
mimeType,
|
||||
variable,
|
||||
size
|
||||
});
|
||||
};
|
||||
size,
|
||||
})
|
||||
}
|
||||
|
||||
const writeFiles = () => {
|
||||
// process static files
|
||||
const buildPath = compilation.options.output.path;
|
||||
const buildPath = compilation.options.output.path
|
||||
for (const filePath of getFilesSync(buildPath)) {
|
||||
const readStream = readFileSync(filePath);
|
||||
const relativeFilePath = relative(buildPath, filePath);
|
||||
writeFile(relativeFilePath, readStream);
|
||||
const readStream = readFileSync(filePath)
|
||||
const relativeFilePath = relative(buildPath, filePath)
|
||||
writeFile(relativeFilePath, readStream)
|
||||
}
|
||||
// process assets
|
||||
const { assets } = compilation;
|
||||
const { assets } = compilation
|
||||
Object.keys(assets).forEach((relativeFilePath) => {
|
||||
writeFile(relativeFilePath, coherseToBuffer(assets[relativeFilePath].source()));
|
||||
});
|
||||
writeFile(
|
||||
relativeFilePath,
|
||||
coherseToBuffer(assets[relativeFilePath].source()),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
const generateWWWClass = () => {
|
||||
@@ -93,30 +110,39 @@ class ProgmemGenerator {
|
||||
|
||||
class WWWData {
|
||||
${indent}public:
|
||||
${indent.repeat(2)}static void registerRoutes(RouteRegistrationHandler handler) {
|
||||
${fileInfo.map(file => `${indent.repeat(3)}handler("${file.uri}", "${file.mimeType}", ${file.variable}, ${file.size});`).join('\n')}
|
||||
${indent.repeat(
|
||||
2,
|
||||
)}static void registerRoutes(RouteRegistrationHandler handler) {
|
||||
${fileInfo
|
||||
.map(
|
||||
(file) =>
|
||||
`${indent.repeat(3)}handler("${file.uri}", "${file.mimeType}", ${
|
||||
file.variable
|
||||
}, ${file.size});`,
|
||||
)
|
||||
.join('\n')}
|
||||
${indent.repeat(2)}}
|
||||
};
|
||||
`;
|
||||
`
|
||||
}
|
||||
|
||||
const writeWWWClass = () => {
|
||||
writeStream.write(generateWWWClass());
|
||||
writeStream.write(generateWWWClass())
|
||||
}
|
||||
|
||||
writeIncludes();
|
||||
writeFiles();
|
||||
writeWWWClass();
|
||||
writeIncludes()
|
||||
writeFiles()
|
||||
writeWWWClass()
|
||||
|
||||
writeStream.on('finish', () => {
|
||||
callback();
|
||||
});
|
||||
callback()
|
||||
})
|
||||
} finally {
|
||||
writeStream.end();
|
||||
writeStream.end()
|
||||
}
|
||||
}
|
||||
);
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ProgmemGenerator;
|
||||
module.exports = ProgmemGenerator
|
||||
|
||||
@@ -9,4 +9,4 @@
|
||||
"start_url": "/",
|
||||
"display": "fullscreen",
|
||||
"orientation": "any"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,20 +3,26 @@
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
src: local('Roboto Light'), local('Roboto-Light'), url(../fonts/li.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2212, U+2215;
|
||||
src: local('Roboto Light'), local('Roboto-Light'),
|
||||
url(../fonts/li.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
|
||||
U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2212, U+2215;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Roboto'), local('Roboto-Regular'), url(../fonts/re.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2212, U+2215;
|
||||
src: local('Roboto'), local('Roboto-Regular'),
|
||||
url(../fonts/re.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
|
||||
U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2212, U+2215;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
src: local('Roboto Medium'), local('Roboto-Medium'), url(../fonts/me.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2212, U+2215;
|
||||
}
|
||||
src: local('Roboto Medium'), local('Roboto-Medium'),
|
||||
url(../fonts/me.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
|
||||
U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2212, U+2215;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import { APSettings, APProvisionMode } from "./types";
|
||||
import { APSettings, APProvisionMode } from './types'
|
||||
|
||||
export const isAPEnabled = ({ provision_mode }: APSettings) => {
|
||||
return provision_mode === APProvisionMode.AP_MODE_ALWAYS || provision_mode === APProvisionMode.AP_MODE_DISCONNECTED;
|
||||
return (
|
||||
provision_mode === APProvisionMode.AP_MODE_ALWAYS ||
|
||||
provision_mode === APProvisionMode.AP_MODE_DISCONNECTED
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,28 +1,28 @@
|
||||
import { Theme } from "@material-ui/core";
|
||||
import { APStatus, APNetworkStatus } from "./types";
|
||||
import { Theme } from '@material-ui/core'
|
||||
import { APStatus, APNetworkStatus } from './types'
|
||||
|
||||
export const apStatusHighlight = ({ status }: APStatus, theme: Theme) => {
|
||||
switch (status) {
|
||||
case APNetworkStatus.ACTIVE:
|
||||
return theme.palette.success.main;
|
||||
return theme.palette.success.main
|
||||
case APNetworkStatus.INACTIVE:
|
||||
return theme.palette.info.main;
|
||||
return theme.palette.info.main
|
||||
case APNetworkStatus.LINGERING:
|
||||
return theme.palette.warning.main;
|
||||
return theme.palette.warning.main
|
||||
default:
|
||||
return theme.palette.warning.main;
|
||||
return theme.palette.warning.main
|
||||
}
|
||||
}
|
||||
|
||||
export const apStatus = ({ status }: APStatus) => {
|
||||
switch (status) {
|
||||
case APNetworkStatus.ACTIVE:
|
||||
return "Active";
|
||||
return 'Active'
|
||||
case APNetworkStatus.INACTIVE:
|
||||
return "Inactive";
|
||||
return 'Inactive'
|
||||
case APNetworkStatus.LINGERING:
|
||||
return "Lingering until idle";
|
||||
return 'Lingering until idle'
|
||||
default:
|
||||
return "Unknown";
|
||||
return 'Unknown'
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,27 +1,27 @@
|
||||
export enum APProvisionMode {
|
||||
AP_MODE_ALWAYS = 0,
|
||||
AP_MODE_DISCONNECTED = 1,
|
||||
AP_NEVER = 2
|
||||
AP_NEVER = 2,
|
||||
}
|
||||
|
||||
export enum APNetworkStatus {
|
||||
ACTIVE = 0,
|
||||
INACTIVE = 1,
|
||||
LINGERING = 2
|
||||
LINGERING = 2,
|
||||
}
|
||||
|
||||
export interface APStatus {
|
||||
status: APNetworkStatus;
|
||||
ip_address: string;
|
||||
mac_address: string;
|
||||
station_num: number;
|
||||
status: APNetworkStatus
|
||||
ip_address: string
|
||||
mac_address: string
|
||||
station_num: number
|
||||
}
|
||||
|
||||
export interface APSettings {
|
||||
provision_mode: APProvisionMode;
|
||||
ssid: string;
|
||||
password: string;
|
||||
local_ip: string;
|
||||
gateway_ip: string;
|
||||
subnet_mask: string;
|
||||
provision_mode: APProvisionMode
|
||||
ssid: string
|
||||
password: string
|
||||
local_ip: string
|
||||
gateway_ip: string
|
||||
subnet_mask: string
|
||||
}
|
||||
|
||||
@@ -1,23 +1,24 @@
|
||||
import { ENDPOINT_ROOT } from './Env';
|
||||
import { ENDPOINT_ROOT } from './Env'
|
||||
|
||||
export const FEATURES_ENDPOINT = ENDPOINT_ROOT + "features";
|
||||
export const NTP_STATUS_ENDPOINT = ENDPOINT_ROOT + "ntpStatus";
|
||||
export const NTP_SETTINGS_ENDPOINT = ENDPOINT_ROOT + "ntpSettings";
|
||||
export const TIME_ENDPOINT = ENDPOINT_ROOT + "time";
|
||||
export const AP_SETTINGS_ENDPOINT = ENDPOINT_ROOT + "apSettings";
|
||||
export const AP_STATUS_ENDPOINT = ENDPOINT_ROOT + "apStatus";
|
||||
export const SCAN_NETWORKS_ENDPOINT = ENDPOINT_ROOT + "scanNetworks";
|
||||
export const LIST_NETWORKS_ENDPOINT = ENDPOINT_ROOT + "listNetworks";
|
||||
export const NETWORK_SETTINGS_ENDPOINT = ENDPOINT_ROOT + "networkSettings";
|
||||
export const NETWORK_STATUS_ENDPOINT = ENDPOINT_ROOT + "networkStatus";
|
||||
export const OTA_SETTINGS_ENDPOINT = ENDPOINT_ROOT + "otaSettings";
|
||||
export const UPLOAD_FIRMWARE_ENDPOINT = ENDPOINT_ROOT + "uploadFirmware";
|
||||
export const MQTT_SETTINGS_ENDPOINT = ENDPOINT_ROOT + "mqttSettings";
|
||||
export const MQTT_STATUS_ENDPOINT = ENDPOINT_ROOT + "mqttStatus";
|
||||
export const SYSTEM_STATUS_ENDPOINT = ENDPOINT_ROOT + "systemStatus";
|
||||
export const SIGN_IN_ENDPOINT = ENDPOINT_ROOT + "signIn";
|
||||
export const VERIFY_AUTHORIZATION_ENDPOINT = ENDPOINT_ROOT + "verifyAuthorization";
|
||||
export const SECURITY_SETTINGS_ENDPOINT = ENDPOINT_ROOT + "securitySettings";
|
||||
export const GENERATE_TOKEN_ENDPOINT = ENDPOINT_ROOT + "generateToken";
|
||||
export const RESTART_ENDPOINT = ENDPOINT_ROOT + "restart";
|
||||
export const FACTORY_RESET_ENDPOINT = ENDPOINT_ROOT + "factoryReset";
|
||||
export const FEATURES_ENDPOINT = ENDPOINT_ROOT + 'features'
|
||||
export const NTP_STATUS_ENDPOINT = ENDPOINT_ROOT + 'ntpStatus'
|
||||
export const NTP_SETTINGS_ENDPOINT = ENDPOINT_ROOT + 'ntpSettings'
|
||||
export const TIME_ENDPOINT = ENDPOINT_ROOT + 'time'
|
||||
export const AP_SETTINGS_ENDPOINT = ENDPOINT_ROOT + 'apSettings'
|
||||
export const AP_STATUS_ENDPOINT = ENDPOINT_ROOT + 'apStatus'
|
||||
export const SCAN_NETWORKS_ENDPOINT = ENDPOINT_ROOT + 'scanNetworks'
|
||||
export const LIST_NETWORKS_ENDPOINT = ENDPOINT_ROOT + 'listNetworks'
|
||||
export const NETWORK_SETTINGS_ENDPOINT = ENDPOINT_ROOT + 'networkSettings'
|
||||
export const NETWORK_STATUS_ENDPOINT = ENDPOINT_ROOT + 'networkStatus'
|
||||
export const OTA_SETTINGS_ENDPOINT = ENDPOINT_ROOT + 'otaSettings'
|
||||
export const UPLOAD_FIRMWARE_ENDPOINT = ENDPOINT_ROOT + 'uploadFirmware'
|
||||
export const MQTT_SETTINGS_ENDPOINT = ENDPOINT_ROOT + 'mqttSettings'
|
||||
export const MQTT_STATUS_ENDPOINT = ENDPOINT_ROOT + 'mqttStatus'
|
||||
export const SYSTEM_STATUS_ENDPOINT = ENDPOINT_ROOT + 'systemStatus'
|
||||
export const SIGN_IN_ENDPOINT = ENDPOINT_ROOT + 'signIn'
|
||||
export const VERIFY_AUTHORIZATION_ENDPOINT =
|
||||
ENDPOINT_ROOT + 'verifyAuthorization'
|
||||
export const SECURITY_SETTINGS_ENDPOINT = ENDPOINT_ROOT + 'securitySettings'
|
||||
export const GENERATE_TOKEN_ENDPOINT = ENDPOINT_ROOT + 'generateToken'
|
||||
export const RESTART_ENDPOINT = ENDPOINT_ROOT + 'restart'
|
||||
export const FACTORY_RESET_ENDPOINT = ENDPOINT_ROOT + 'factoryReset'
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
export const PROJECT_NAME = process.env.REACT_APP_PROJECT_NAME!;
|
||||
export const PROJECT_PATH = process.env.REACT_APP_PROJECT_PATH!;
|
||||
export const PROJECT_NAME = process.env.REACT_APP_PROJECT_NAME!
|
||||
export const PROJECT_PATH = process.env.REACT_APP_PROJECT_PATH!
|
||||
|
||||
export const ENDPOINT_ROOT = calculateEndpointRoot("/rest/");
|
||||
export const WEB_SOCKET_ROOT = calculateWebSocketRoot("/ws/");
|
||||
export const ENDPOINT_ROOT = calculateEndpointRoot('/rest/')
|
||||
export const WEB_SOCKET_ROOT = calculateWebSocketRoot('/ws/')
|
||||
|
||||
function calculateEndpointRoot(endpointPath: string) {
|
||||
const httpRoot = process.env.REACT_APP_HTTP_ROOT;
|
||||
if (httpRoot) {
|
||||
return httpRoot + endpointPath;
|
||||
}
|
||||
const location = window.location;
|
||||
return location.protocol + "//" + location.host + endpointPath;
|
||||
const httpRoot = process.env.REACT_APP_HTTP_ROOT
|
||||
if (httpRoot) {
|
||||
return httpRoot + endpointPath
|
||||
}
|
||||
const location = window.location
|
||||
return location.protocol + '//' + location.host + endpointPath
|
||||
}
|
||||
|
||||
function calculateWebSocketRoot(webSocketPath: string) {
|
||||
const webSocketRoot = process.env.REACT_APP_WEB_SOCKET_ROOT;
|
||||
if (webSocketRoot) {
|
||||
return webSocketRoot + webSocketPath;
|
||||
}
|
||||
const location = window.location;
|
||||
const webProtocol = location.protocol === "https:" ? "wss:" : "ws:";
|
||||
return webProtocol + "//" + location.host + webSocketPath;
|
||||
const webSocketRoot = process.env.REACT_APP_WEB_SOCKET_ROOT
|
||||
if (webSocketRoot) {
|
||||
return webSocketRoot + webSocketPath
|
||||
}
|
||||
const location = window.location
|
||||
const webProtocol = location.protocol === 'https:' ? 'wss:' : 'ws:'
|
||||
return webProtocol + '//' + location.host + webSocketPath
|
||||
}
|
||||
|
||||
@@ -1,114 +1,129 @@
|
||||
import * as H from 'history';
|
||||
import * as H from 'history'
|
||||
|
||||
import history from '../history';
|
||||
import { Features } from '../features/types';
|
||||
import { getDefaultRoute } from '../AppRouting';
|
||||
import history from '../history'
|
||||
import { Features } from '../features/types'
|
||||
import { getDefaultRoute } from '../AppRouting'
|
||||
|
||||
export const ACCESS_TOKEN = 'access_token';
|
||||
export const SIGN_IN_PATHNAME = 'signInPathname';
|
||||
export const SIGN_IN_SEARCH = 'signInSearch';
|
||||
export const ACCESS_TOKEN = 'access_token'
|
||||
export const SIGN_IN_PATHNAME = 'signInPathname'
|
||||
export const SIGN_IN_SEARCH = 'signInSearch'
|
||||
|
||||
/**
|
||||
* Fallback to sessionStorage if localStorage is absent. WebView may not have local storage enabled.
|
||||
*/
|
||||
export function getStorage() {
|
||||
return localStorage || sessionStorage;
|
||||
return localStorage || sessionStorage
|
||||
}
|
||||
|
||||
export function storeLoginRedirect(location?: H.Location) {
|
||||
if (location) {
|
||||
getStorage().setItem(SIGN_IN_PATHNAME, location.pathname);
|
||||
getStorage().setItem(SIGN_IN_SEARCH, location.search);
|
||||
getStorage().setItem(SIGN_IN_PATHNAME, location.pathname)
|
||||
getStorage().setItem(SIGN_IN_SEARCH, location.search)
|
||||
}
|
||||
}
|
||||
|
||||
export function clearLoginRedirect() {
|
||||
getStorage().removeItem(SIGN_IN_PATHNAME);
|
||||
getStorage().removeItem(SIGN_IN_SEARCH);
|
||||
getStorage().removeItem(SIGN_IN_PATHNAME)
|
||||
getStorage().removeItem(SIGN_IN_SEARCH)
|
||||
}
|
||||
|
||||
export function fetchLoginRedirect(features: Features): H.LocationDescriptorObject {
|
||||
const signInPathname = getStorage().getItem(SIGN_IN_PATHNAME);
|
||||
const signInSearch = getStorage().getItem(SIGN_IN_SEARCH);
|
||||
clearLoginRedirect();
|
||||
export function fetchLoginRedirect(
|
||||
features: Features,
|
||||
): H.LocationDescriptorObject {
|
||||
const signInPathname = getStorage().getItem(SIGN_IN_PATHNAME)
|
||||
const signInSearch = getStorage().getItem(SIGN_IN_SEARCH)
|
||||
clearLoginRedirect()
|
||||
return {
|
||||
pathname: signInPathname || getDefaultRoute(features),
|
||||
search: (signInPathname && signInSearch) || undefined
|
||||
};
|
||||
search: (signInPathname && signInSearch) || undefined,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps the normal fetch routine with one with provides the access token if present.
|
||||
*/
|
||||
export function authorizedFetch(url: RequestInfo, params?: RequestInit): Promise<Response> {
|
||||
const accessToken = getStorage().getItem(ACCESS_TOKEN);
|
||||
export function authorizedFetch(
|
||||
url: RequestInfo,
|
||||
params?: RequestInit,
|
||||
): Promise<Response> {
|
||||
const accessToken = getStorage().getItem(ACCESS_TOKEN)
|
||||
if (accessToken) {
|
||||
params = params || {};
|
||||
params.credentials = 'include';
|
||||
params = params || {}
|
||||
params.credentials = 'include'
|
||||
params.headers = {
|
||||
...params.headers,
|
||||
"Authorization": 'Bearer ' + accessToken
|
||||
};
|
||||
Authorization: 'Bearer ' + accessToken,
|
||||
}
|
||||
}
|
||||
return fetch(url, params);
|
||||
return fetch(url, params)
|
||||
}
|
||||
|
||||
/**
|
||||
* fetch() does not yet support upload progress, this wrapper allows us to configure the xhr request
|
||||
* for a single file upload and takes care of adding the Authorization header and redirecting on
|
||||
* fetch() does not yet support upload progress, this wrapper allows us to configure the xhr request
|
||||
* for a single file upload and takes care of adding the Authorization header and redirecting on
|
||||
* authorization errors as we do for normal fetch operations.
|
||||
*/
|
||||
export function redirectingAuthorizedUpload(xhr: XMLHttpRequest, url: string, file: File, onProgress: (event: ProgressEvent<EventTarget>) => void): Promise<void> {
|
||||
export function redirectingAuthorizedUpload(
|
||||
xhr: XMLHttpRequest,
|
||||
url: string,
|
||||
file: File,
|
||||
onProgress: (event: ProgressEvent<EventTarget>) => void,
|
||||
): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
xhr.open("POST", url, true);
|
||||
const accessToken = getStorage().getItem(ACCESS_TOKEN);
|
||||
xhr.open('POST', url, true)
|
||||
const accessToken = getStorage().getItem(ACCESS_TOKEN)
|
||||
if (accessToken) {
|
||||
xhr.withCredentials = true;
|
||||
xhr.setRequestHeader("Authorization", 'Bearer ' + accessToken);
|
||||
xhr.withCredentials = true
|
||||
xhr.setRequestHeader('Authorization', 'Bearer ' + accessToken)
|
||||
}
|
||||
xhr.upload.onprogress = onProgress;
|
||||
xhr.upload.onprogress = onProgress
|
||||
xhr.onload = function () {
|
||||
if (xhr.status === 401 || xhr.status === 403) {
|
||||
history.push("/unauthorized");
|
||||
history.push('/unauthorized')
|
||||
} else {
|
||||
resolve();
|
||||
resolve()
|
||||
}
|
||||
};
|
||||
}
|
||||
xhr.onerror = function (event: ProgressEvent<EventTarget>) {
|
||||
reject(new DOMException('Error', 'UploadError'));
|
||||
};
|
||||
reject(new DOMException('Error', 'UploadError'))
|
||||
}
|
||||
xhr.onabort = function () {
|
||||
reject(new DOMException('Aborted', 'AbortError'));
|
||||
};
|
||||
const formData = new FormData();
|
||||
formData.append('file', file);
|
||||
xhr.send(formData);
|
||||
});
|
||||
reject(new DOMException('Aborted', 'AbortError'))
|
||||
}
|
||||
const formData = new FormData()
|
||||
formData.append('file', file)
|
||||
xhr.send(formData)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps the normal fetch routene which redirects on 401 response.
|
||||
*/
|
||||
export function redirectingAuthorizedFetch(url: RequestInfo, params?: RequestInit): Promise<Response> {
|
||||
export function redirectingAuthorizedFetch(
|
||||
url: RequestInfo,
|
||||
params?: RequestInit,
|
||||
): Promise<Response> {
|
||||
return new Promise<Response>((resolve, reject) => {
|
||||
authorizedFetch(url, params).then(response => {
|
||||
if (response.status === 401 || response.status === 403) {
|
||||
history.push("/unauthorized");
|
||||
} else {
|
||||
resolve(response);
|
||||
}
|
||||
}).catch(error => {
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
authorizedFetch(url, params)
|
||||
.then((response) => {
|
||||
if (response.status === 401 || response.status === 403) {
|
||||
history.push('/unauthorized')
|
||||
} else {
|
||||
resolve(response)
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
reject(error)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
export function addAccessTokenParameter(url: string) {
|
||||
const accessToken = getStorage().getItem(ACCESS_TOKEN);
|
||||
const accessToken = getStorage().getItem(ACCESS_TOKEN)
|
||||
if (!accessToken) {
|
||||
return url;
|
||||
return url
|
||||
}
|
||||
const parsedUrl = new URL(url);
|
||||
parsedUrl.searchParams.set(ACCESS_TOKEN, accessToken);
|
||||
return parsedUrl.toString();
|
||||
const parsedUrl = new URL(url)
|
||||
parsedUrl.searchParams.set(ACCESS_TOKEN, accessToken)
|
||||
return parsedUrl.toString()
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
export { default as AuthenticatedRoute } from './AuthenticatedRoute';
|
||||
export { default as AuthenticationWrapper } from './AuthenticationWrapper';
|
||||
export { default as UnauthenticatedRoute } from './UnauthenticatedRoute';
|
||||
export { default as AuthenticatedRoute } from './AuthenticatedRoute'
|
||||
export { default as AuthenticationWrapper } from './AuthenticationWrapper'
|
||||
export { default as UnauthenticatedRoute } from './UnauthenticatedRoute'
|
||||
|
||||
export * from './Authentication';
|
||||
export * from './AuthenticationContext';
|
||||
export * from './Authentication'
|
||||
export * from './AuthenticationContext'
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
export { default as BlockFormControlLabel } from './BlockFormControlLabel';
|
||||
export { default as FormActions } from './FormActions';
|
||||
export { default as FormButton } from './FormButton';
|
||||
export { default as HighlightAvatar } from './HighlightAvatar';
|
||||
export { default as MenuAppBar } from './MenuAppBar';
|
||||
export { default as PasswordValidator } from './PasswordValidator';
|
||||
export { default as RestFormLoader } from './RestFormLoader';
|
||||
export { default as SectionContent } from './SectionContent';
|
||||
export { default as WebSocketFormLoader } from './WebSocketFormLoader';
|
||||
export { default as ErrorButton } from './ErrorButton';
|
||||
export { default as SingleUpload } from './SingleUpload';
|
||||
export { default as BlockFormControlLabel } from './BlockFormControlLabel'
|
||||
export { default as FormActions } from './FormActions'
|
||||
export { default as FormButton } from './FormButton'
|
||||
export { default as HighlightAvatar } from './HighlightAvatar'
|
||||
export { default as MenuAppBar } from './MenuAppBar'
|
||||
export { default as PasswordValidator } from './PasswordValidator'
|
||||
export { default as RestFormLoader } from './RestFormLoader'
|
||||
export { default as SectionContent } from './SectionContent'
|
||||
export { default as WebSocketFormLoader } from './WebSocketFormLoader'
|
||||
export { default as ErrorButton } from './ErrorButton'
|
||||
export { default as SingleUpload } from './SingleUpload'
|
||||
|
||||
export * from './RestFormLoader';
|
||||
export * from './RestController';
|
||||
export * from './RestFormLoader'
|
||||
export * from './RestController'
|
||||
|
||||
export * from './WebSocketFormLoader';
|
||||
export * from './WebSocketController';
|
||||
export * from './WebSocketFormLoader'
|
||||
export * from './WebSocketController'
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
export interface Features {
|
||||
project: boolean;
|
||||
security: boolean;
|
||||
mqtt: boolean;
|
||||
ntp: boolean;
|
||||
ota: boolean;
|
||||
upload_firmware: boolean;
|
||||
project: boolean
|
||||
security: boolean
|
||||
mqtt: boolean
|
||||
ntp: boolean
|
||||
ota: boolean
|
||||
upload_firmware: boolean
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { createBrowserHistory } from 'history';
|
||||
import { createBrowserHistory } from 'history'
|
||||
|
||||
export default createBrowserHistory({
|
||||
/* pass a configuration object here if needed */
|
||||
|
||||
@@ -1,56 +1,59 @@
|
||||
import { Theme } from "@material-ui/core";
|
||||
import { MqttStatus, MqttDisconnectReason } from "./types";
|
||||
import { Theme } from '@material-ui/core'
|
||||
import { MqttStatus, MqttDisconnectReason } from './types'
|
||||
|
||||
export const mqttStatusHighlight = ({ enabled, connected }: MqttStatus, theme: Theme) => {
|
||||
export const mqttStatusHighlight = (
|
||||
{ enabled, connected }: MqttStatus,
|
||||
theme: Theme,
|
||||
) => {
|
||||
if (!enabled) {
|
||||
return theme.palette.info.main;
|
||||
return theme.palette.info.main
|
||||
}
|
||||
if (connected) {
|
||||
return theme.palette.success.main;
|
||||
return theme.palette.success.main
|
||||
}
|
||||
return theme.palette.error.main;
|
||||
return theme.palette.error.main
|
||||
}
|
||||
|
||||
export const mqttStatus = ({ enabled, connected }: MqttStatus) => {
|
||||
if (!enabled) {
|
||||
return "Not enabled";
|
||||
return 'Not enabled'
|
||||
}
|
||||
if (connected) {
|
||||
return "Connected";
|
||||
return 'Connected'
|
||||
}
|
||||
return "Disconnected";
|
||||
return 'Disconnected'
|
||||
}
|
||||
|
||||
export const disconnectReason = ({ disconnect_reason }: MqttStatus) => {
|
||||
switch (disconnect_reason) {
|
||||
case MqttDisconnectReason.TCP_DISCONNECTED:
|
||||
return "TCP disconnected";
|
||||
return 'TCP disconnected'
|
||||
case MqttDisconnectReason.MQTT_UNACCEPTABLE_PROTOCOL_VERSION:
|
||||
return "Unacceptable protocol version";
|
||||
return 'Unacceptable protocol version'
|
||||
case MqttDisconnectReason.MQTT_IDENTIFIER_REJECTED:
|
||||
return "Client ID rejected";
|
||||
return 'Client ID rejected'
|
||||
case MqttDisconnectReason.MQTT_SERVER_UNAVAILABLE:
|
||||
return "Server unavailable";
|
||||
return 'Server unavailable'
|
||||
case MqttDisconnectReason.MQTT_MALFORMED_CREDENTIALS:
|
||||
return "Malformed credentials";
|
||||
return 'Malformed credentials'
|
||||
case MqttDisconnectReason.MQTT_NOT_AUTHORIZED:
|
||||
return "Not authorized";
|
||||
return 'Not authorized'
|
||||
case MqttDisconnectReason.ESP8266_NOT_ENOUGH_SPACE:
|
||||
return "Device out of memory";
|
||||
return 'Device out of memory'
|
||||
case MqttDisconnectReason.TLS_BAD_FINGERPRINT:
|
||||
return "Server fingerprint invalid";
|
||||
return 'Server fingerprint invalid'
|
||||
default:
|
||||
return "Unknown"
|
||||
return 'Unknown'
|
||||
}
|
||||
}
|
||||
|
||||
export const mqttPublishHighlight = ({ mqtt_fails }: MqttStatus, theme: Theme) => {
|
||||
export const mqttPublishHighlight = (
|
||||
{ mqtt_fails }: MqttStatus,
|
||||
theme: Theme,
|
||||
) => {
|
||||
if (mqtt_fails === 0) return theme.palette.success.main
|
||||
|
||||
if (mqtt_fails === 0)
|
||||
return theme.palette.success.main;
|
||||
if (mqtt_fails < 10) return theme.palette.warning.main
|
||||
|
||||
if (mqtt_fails < 10)
|
||||
return theme.palette.warning.main;
|
||||
|
||||
return theme.palette.error.main;
|
||||
}
|
||||
return theme.palette.error.main
|
||||
}
|
||||
|
||||
@@ -6,40 +6,40 @@ export enum MqttDisconnectReason {
|
||||
MQTT_MALFORMED_CREDENTIALS = 4,
|
||||
MQTT_NOT_AUTHORIZED = 5,
|
||||
ESP8266_NOT_ENOUGH_SPACE = 6,
|
||||
TLS_BAD_FINGERPRINT = 7
|
||||
TLS_BAD_FINGERPRINT = 7,
|
||||
}
|
||||
|
||||
export interface MqttStatus {
|
||||
enabled: boolean;
|
||||
connected: boolean;
|
||||
client_id: string;
|
||||
disconnect_reason: MqttDisconnectReason;
|
||||
mqtt_fails: number;
|
||||
enabled: boolean
|
||||
connected: boolean
|
||||
client_id: string
|
||||
disconnect_reason: MqttDisconnectReason
|
||||
mqtt_fails: number
|
||||
}
|
||||
|
||||
export interface MqttSettings {
|
||||
enabled: boolean;
|
||||
host: string;
|
||||
port: number;
|
||||
base: string;
|
||||
username: string;
|
||||
password: string;
|
||||
client_id: string;
|
||||
keep_alive: number;
|
||||
clean_session: boolean;
|
||||
max_topic_length: number;
|
||||
publish_time_boiler: number;
|
||||
publish_time_thermostat: number;
|
||||
publish_time_solar: number;
|
||||
publish_time_mixer: number;
|
||||
publish_time_other: number;
|
||||
publish_time_sensor: number;
|
||||
dallas_format: number;
|
||||
bool_format: number;
|
||||
mqtt_qos: number;
|
||||
mqtt_retain: boolean;
|
||||
ha_enabled: boolean;
|
||||
ha_climate_format: number;
|
||||
nested_format: number;
|
||||
subscribe_format: number;
|
||||
enabled: boolean
|
||||
host: string
|
||||
port: number
|
||||
base: string
|
||||
username: string
|
||||
password: string
|
||||
client_id: string
|
||||
keep_alive: number
|
||||
clean_session: boolean
|
||||
max_topic_length: number
|
||||
publish_time_boiler: number
|
||||
publish_time_thermostat: number
|
||||
publish_time_solar: number
|
||||
publish_time_mixer: number
|
||||
publish_time_other: number
|
||||
publish_time_sensor: number
|
||||
dallas_format: number
|
||||
bool_format: number
|
||||
mqtt_qos: number
|
||||
mqtt_retain: boolean
|
||||
ha_enabled: boolean
|
||||
ha_climate_format: number
|
||||
nested_format: number
|
||||
subscribe_format: number
|
||||
}
|
||||
|
||||
@@ -1,57 +1,57 @@
|
||||
import { Theme } from "@material-ui/core";
|
||||
import { NetworkStatus, NetworkConnectionStatus } from "./types";
|
||||
import { Theme } from '@material-ui/core'
|
||||
import { NetworkStatus, NetworkConnectionStatus } from './types'
|
||||
|
||||
export const isConnected = ({ status }: NetworkStatus) => {
|
||||
return (
|
||||
status === NetworkConnectionStatus.WIFI_STATUS_CONNECTED ||
|
||||
status === NetworkConnectionStatus.ETHERNET_STATUS_CONNECTED
|
||||
);
|
||||
};
|
||||
)
|
||||
}
|
||||
|
||||
export const isWiFi = ({ status }: NetworkStatus) =>
|
||||
status === NetworkConnectionStatus.WIFI_STATUS_CONNECTED;
|
||||
status === NetworkConnectionStatus.WIFI_STATUS_CONNECTED
|
||||
export const isEthernet = ({ status }: NetworkStatus) =>
|
||||
status === NetworkConnectionStatus.ETHERNET_STATUS_CONNECTED;
|
||||
status === NetworkConnectionStatus.ETHERNET_STATUS_CONNECTED
|
||||
|
||||
export const networkStatusHighlight = (
|
||||
{ status }: NetworkStatus,
|
||||
theme: Theme
|
||||
theme: Theme,
|
||||
) => {
|
||||
switch (status) {
|
||||
case NetworkConnectionStatus.WIFI_STATUS_IDLE:
|
||||
case NetworkConnectionStatus.WIFI_STATUS_DISCONNECTED:
|
||||
case NetworkConnectionStatus.WIFI_STATUS_NO_SHIELD:
|
||||
return theme.palette.info.main;
|
||||
return theme.palette.info.main
|
||||
case NetworkConnectionStatus.WIFI_STATUS_CONNECTED:
|
||||
case NetworkConnectionStatus.ETHERNET_STATUS_CONNECTED:
|
||||
return theme.palette.success.main;
|
||||
return theme.palette.success.main
|
||||
case NetworkConnectionStatus.WIFI_STATUS_CONNECT_FAILED:
|
||||
case NetworkConnectionStatus.WIFI_STATUS_CONNECTION_LOST:
|
||||
return theme.palette.error.main;
|
||||
return theme.palette.error.main
|
||||
default:
|
||||
return theme.palette.warning.main;
|
||||
return theme.palette.warning.main
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export const networkStatus = ({ status }: NetworkStatus) => {
|
||||
switch (status) {
|
||||
case NetworkConnectionStatus.WIFI_STATUS_NO_SHIELD:
|
||||
return "Inactive";
|
||||
return 'Inactive'
|
||||
case NetworkConnectionStatus.WIFI_STATUS_IDLE:
|
||||
return "Idle";
|
||||
return 'Idle'
|
||||
case NetworkConnectionStatus.WIFI_STATUS_NO_SSID_AVAIL:
|
||||
return "No SSID Available";
|
||||
return 'No SSID Available'
|
||||
case NetworkConnectionStatus.WIFI_STATUS_CONNECTED:
|
||||
return "Connected (WiFi)";
|
||||
return 'Connected (WiFi)'
|
||||
case NetworkConnectionStatus.ETHERNET_STATUS_CONNECTED:
|
||||
return "Connected (Ethernet)";
|
||||
return 'Connected (Ethernet)'
|
||||
case NetworkConnectionStatus.WIFI_STATUS_CONNECT_FAILED:
|
||||
return "Connection Failed";
|
||||
return 'Connection Failed'
|
||||
case NetworkConnectionStatus.WIFI_STATUS_CONNECTION_LOST:
|
||||
return "Connection Lost";
|
||||
return 'Connection Lost'
|
||||
case NetworkConnectionStatus.WIFI_STATUS_DISCONNECTED:
|
||||
return "Disconnected";
|
||||
return 'Disconnected'
|
||||
default:
|
||||
return "Unknown";
|
||||
return 'Unknown'
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,22 +1,23 @@
|
||||
import { WiFiNetwork, WiFiEncryptionType } from "./types";
|
||||
import { WiFiNetwork, WiFiEncryptionType } from './types'
|
||||
|
||||
export const isNetworkOpen = ({ encryption_type }: WiFiNetwork) => encryption_type === WiFiEncryptionType.WIFI_AUTH_OPEN;
|
||||
export const isNetworkOpen = ({ encryption_type }: WiFiNetwork) =>
|
||||
encryption_type === WiFiEncryptionType.WIFI_AUTH_OPEN
|
||||
|
||||
export const networkSecurityMode = ({ encryption_type }: WiFiNetwork) => {
|
||||
switch (encryption_type) {
|
||||
case WiFiEncryptionType.WIFI_AUTH_WEP:
|
||||
return "WEP";
|
||||
return 'WEP'
|
||||
case WiFiEncryptionType.WIFI_AUTH_WPA_PSK:
|
||||
return "WPA";
|
||||
return 'WPA'
|
||||
case WiFiEncryptionType.WIFI_AUTH_WPA2_PSK:
|
||||
return "WPA2";
|
||||
return 'WPA2'
|
||||
case WiFiEncryptionType.WIFI_AUTH_WPA_WPA2_PSK:
|
||||
return "WPA/WPA2";
|
||||
return 'WPA/WPA2'
|
||||
case WiFiEncryptionType.WIFI_AUTH_WPA2_ENTERPRISE:
|
||||
return "WPA2 Enterprise";
|
||||
return 'WPA2 Enterprise'
|
||||
case WiFiEncryptionType.WIFI_AUTH_OPEN:
|
||||
return "None";
|
||||
return 'None'
|
||||
default:
|
||||
return "Unknown";
|
||||
return 'Unknown'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ export enum NetworkConnectionStatus {
|
||||
WIFI_STATUS_CONNECTION_LOST = 5,
|
||||
WIFI_STATUS_DISCONNECTED = 6,
|
||||
ETHERNET_STATUS_CONNECTED = 10,
|
||||
WIFI_STATUS_NO_SHIELD = 255
|
||||
WIFI_STATUS_NO_SHIELD = 255,
|
||||
}
|
||||
|
||||
export enum WiFiEncryptionType {
|
||||
@@ -15,43 +15,43 @@ export enum WiFiEncryptionType {
|
||||
WIFI_AUTH_WPA_PSK = 2,
|
||||
WIFI_AUTH_WPA2_PSK = 3,
|
||||
WIFI_AUTH_WPA_WPA2_PSK = 4,
|
||||
WIFI_AUTH_WPA2_ENTERPRISE = 5
|
||||
WIFI_AUTH_WPA2_ENTERPRISE = 5,
|
||||
}
|
||||
|
||||
export interface NetworkStatus {
|
||||
status: NetworkConnectionStatus;
|
||||
local_ip: string;
|
||||
mac_address: string;
|
||||
rssi: number;
|
||||
ssid: string;
|
||||
bssid: string;
|
||||
channel: number;
|
||||
subnet_mask: string;
|
||||
gateway_ip: string;
|
||||
dns_ip_1: string;
|
||||
dns_ip_2: string;
|
||||
status: NetworkConnectionStatus
|
||||
local_ip: string
|
||||
mac_address: string
|
||||
rssi: number
|
||||
ssid: string
|
||||
bssid: string
|
||||
channel: number
|
||||
subnet_mask: string
|
||||
gateway_ip: string
|
||||
dns_ip_1: string
|
||||
dns_ip_2: string
|
||||
}
|
||||
|
||||
export interface NetworkSettings {
|
||||
ssid: string;
|
||||
password: string;
|
||||
hostname: string;
|
||||
static_ip_config: boolean;
|
||||
local_ip?: string;
|
||||
gateway_ip?: string;
|
||||
subnet_mask?: string;
|
||||
dns_ip_1?: string;
|
||||
dns_ip_2?: string;
|
||||
ssid: string
|
||||
password: string
|
||||
hostname: string
|
||||
static_ip_config: boolean
|
||||
local_ip?: string
|
||||
gateway_ip?: string
|
||||
subnet_mask?: string
|
||||
dns_ip_1?: string
|
||||
dns_ip_2?: string
|
||||
}
|
||||
|
||||
export interface WiFiNetworkList {
|
||||
networks: WiFiNetwork[];
|
||||
networks: WiFiNetwork[]
|
||||
}
|
||||
|
||||
export interface WiFiNetwork {
|
||||
rssi: number;
|
||||
ssid: string;
|
||||
bssid: string;
|
||||
channel: number;
|
||||
encryption_type: WiFiEncryptionType;
|
||||
rssi: number
|
||||
ssid: string
|
||||
bssid: string
|
||||
channel: number
|
||||
encryption_type: WiFiEncryptionType
|
||||
}
|
||||
|
||||
@@ -1,26 +1,27 @@
|
||||
import { Theme } from "@material-ui/core";
|
||||
import { NTPStatus, NTPSyncStatus } from "./types";
|
||||
import { Theme } from '@material-ui/core'
|
||||
import { NTPStatus, NTPSyncStatus } from './types'
|
||||
|
||||
export const isNtpActive = ({ status }: NTPStatus) => status === NTPSyncStatus.NTP_ACTIVE;
|
||||
export const isNtpActive = ({ status }: NTPStatus) =>
|
||||
status === NTPSyncStatus.NTP_ACTIVE
|
||||
|
||||
export const ntpStatusHighlight = ({ status }: NTPStatus, theme: Theme) => {
|
||||
switch (status) {
|
||||
case NTPSyncStatus.NTP_INACTIVE:
|
||||
return theme.palette.info.main;
|
||||
return theme.palette.info.main
|
||||
case NTPSyncStatus.NTP_ACTIVE:
|
||||
return theme.palette.success.main;
|
||||
return theme.palette.success.main
|
||||
default:
|
||||
return theme.palette.error.main;
|
||||
return theme.palette.error.main
|
||||
}
|
||||
}
|
||||
|
||||
export const ntpStatus = ({ status }: NTPStatus) => {
|
||||
switch (status) {
|
||||
case NTPSyncStatus.NTP_INACTIVE:
|
||||
return "Inactive";
|
||||
return 'Inactive'
|
||||
case NTPSyncStatus.NTP_ACTIVE:
|
||||
return "Active";
|
||||
return 'Active'
|
||||
default:
|
||||
return "Unknown";
|
||||
return 'Unknown'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,45 +1,43 @@
|
||||
import parseMilliseconds from 'parse-ms';
|
||||
import parseMilliseconds from 'parse-ms'
|
||||
|
||||
const LOCALE_FORMAT = new Intl.DateTimeFormat(
|
||||
[...window.navigator.languages],
|
||||
{
|
||||
day: 'numeric',
|
||||
month: 'short',
|
||||
year: 'numeric',
|
||||
hour: 'numeric',
|
||||
minute: 'numeric',
|
||||
second: 'numeric',
|
||||
hour12: false
|
||||
}
|
||||
);
|
||||
const LOCALE_FORMAT = new Intl.DateTimeFormat([...window.navigator.languages], {
|
||||
day: 'numeric',
|
||||
month: 'short',
|
||||
year: 'numeric',
|
||||
hour: 'numeric',
|
||||
minute: 'numeric',
|
||||
second: 'numeric',
|
||||
hour12: false,
|
||||
})
|
||||
|
||||
export const formatDateTime = (dateTime: string) => {
|
||||
return LOCALE_FORMAT.format(new Date(dateTime.substr(0, 19)));
|
||||
return LOCALE_FORMAT.format(new Date(dateTime.substr(0, 19)))
|
||||
}
|
||||
|
||||
export const formatLocalDateTime = (date: Date) => {
|
||||
return new Date(date.getTime() - date.getTimezoneOffset() * 60000)
|
||||
.toISOString()
|
||||
.slice(0, -1)
|
||||
.substr(0, 19);
|
||||
.substr(0, 19)
|
||||
}
|
||||
|
||||
export const formatDuration = (duration: number) => {
|
||||
const { days, hours, minutes, seconds } = parseMilliseconds(duration * 1000);
|
||||
var formatted = '';
|
||||
const { days, hours, minutes, seconds } = parseMilliseconds(duration * 1000)
|
||||
var formatted = ''
|
||||
if (days) {
|
||||
formatted += pluralize(days, 'day');
|
||||
formatted += pluralize(days, 'day')
|
||||
}
|
||||
if (formatted || hours) {
|
||||
formatted += pluralize(hours, 'hour');
|
||||
formatted += pluralize(hours, 'hour')
|
||||
}
|
||||
if (formatted || minutes) {
|
||||
formatted += pluralize(minutes, 'minute');
|
||||
formatted += pluralize(minutes, 'minute')
|
||||
}
|
||||
if (formatted || seconds) {
|
||||
formatted += pluralize(seconds, 'second');
|
||||
formatted += pluralize(seconds, 'second')
|
||||
}
|
||||
return formatted;
|
||||
return formatted
|
||||
}
|
||||
|
||||
const pluralize = (count: number, noun: string, suffix: string = 's') => ` ${count} ${noun}${count !== 1 ? suffix : ''} `;
|
||||
const pluralize = (count: number, noun: string, suffix: string = 's') =>
|
||||
` ${count} ${noun}${count !== 1 ? suffix : ''} `
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
export enum NTPSyncStatus {
|
||||
NTP_INACTIVE = 0,
|
||||
NTP_ACTIVE = 1
|
||||
NTP_ACTIVE = 1,
|
||||
}
|
||||
|
||||
export interface NTPStatus {
|
||||
status: NTPSyncStatus;
|
||||
utc_time: string;
|
||||
local_time: string;
|
||||
server: string;
|
||||
uptime: number;
|
||||
status: NTPSyncStatus
|
||||
utc_time: string
|
||||
local_time: string
|
||||
server: string
|
||||
uptime: number
|
||||
}
|
||||
|
||||
export interface NTPSettings {
|
||||
enabled: boolean;
|
||||
server: string;
|
||||
tz_label: string;
|
||||
tz_format: string;
|
||||
enabled: boolean
|
||||
server: string
|
||||
tz_label: string
|
||||
tz_format: string
|
||||
}
|
||||
|
||||
export interface Time {
|
||||
local_time: string;
|
||||
}
|
||||
local_time: string
|
||||
}
|
||||
|
||||
@@ -1,40 +1,39 @@
|
||||
import { Theme } from '@material-ui/core';
|
||||
import { EMSESPStatus, busConnectionStatus } from './EMSESPtypes';
|
||||
import { Theme } from '@material-ui/core'
|
||||
import { EMSESPStatus, busConnectionStatus } from './EMSESPtypes'
|
||||
|
||||
export const isConnected = ({ status }: EMSESPStatus) => status !== busConnectionStatus.BUS_STATUS_OFFLINE;
|
||||
export const isConnected = ({ status }: EMSESPStatus) =>
|
||||
status !== busConnectionStatus.BUS_STATUS_OFFLINE
|
||||
|
||||
export const busStatusHighlight = ({ status }: EMSESPStatus, theme: Theme) => {
|
||||
|
||||
switch (status) {
|
||||
case busConnectionStatus.BUS_STATUS_TX_ERRORS:
|
||||
return theme.palette.warning.main;
|
||||
return theme.palette.warning.main
|
||||
case busConnectionStatus.BUS_STATUS_CONNECTED:
|
||||
return theme.palette.success.main;
|
||||
return theme.palette.success.main
|
||||
case busConnectionStatus.BUS_STATUS_OFFLINE:
|
||||
return theme.palette.error.main;
|
||||
return theme.palette.error.main
|
||||
default:
|
||||
return theme.palette.warning.main;
|
||||
return theme.palette.warning.main
|
||||
}
|
||||
}
|
||||
|
||||
export const busStatus = ({ status }: EMSESPStatus) => {
|
||||
switch (status) {
|
||||
case busConnectionStatus.BUS_STATUS_CONNECTED:
|
||||
return "Connected";
|
||||
return 'Connected'
|
||||
case busConnectionStatus.BUS_STATUS_TX_ERRORS:
|
||||
return "Tx Errors";
|
||||
return 'Tx Errors'
|
||||
case busConnectionStatus.BUS_STATUS_OFFLINE:
|
||||
return "Disconnected";
|
||||
return 'Disconnected'
|
||||
default:
|
||||
return "Unknown";
|
||||
return 'Unknown'
|
||||
}
|
||||
}
|
||||
|
||||
export const qualityHighlight = (value: number, theme: Theme) => {
|
||||
if (value >= 95) {
|
||||
return theme.palette.success.main;
|
||||
return theme.palette.success.main
|
||||
}
|
||||
|
||||
return theme.palette.error.main;
|
||||
return theme.palette.error.main
|
||||
}
|
||||
|
||||
|
||||
@@ -1,72 +1,72 @@
|
||||
export interface EMSESPSettings {
|
||||
tx_mode: number;
|
||||
tx_delay: number;
|
||||
ems_bus_id: number;
|
||||
syslog_enabled: boolean;
|
||||
syslog_level: number;
|
||||
syslog_mark_interval: number;
|
||||
syslog_host: string;
|
||||
syslog_port: number;
|
||||
master_thermostat: number;
|
||||
shower_timer: boolean;
|
||||
shower_alert: boolean;
|
||||
rx_gpio: number;
|
||||
tx_gpio: number;
|
||||
dallas_gpio: number;
|
||||
dallas_parasite: boolean;
|
||||
led_gpio: number;
|
||||
hide_led: boolean;
|
||||
notoken_api: boolean;
|
||||
analog_enabled: boolean;
|
||||
pbutton_gpio: number;
|
||||
trace_raw: boolean;
|
||||
board_profile: string;
|
||||
tx_mode: number
|
||||
tx_delay: number
|
||||
ems_bus_id: number
|
||||
syslog_enabled: boolean
|
||||
syslog_level: number
|
||||
syslog_mark_interval: number
|
||||
syslog_host: string
|
||||
syslog_port: number
|
||||
master_thermostat: number
|
||||
shower_timer: boolean
|
||||
shower_alert: boolean
|
||||
rx_gpio: number
|
||||
tx_gpio: number
|
||||
dallas_gpio: number
|
||||
dallas_parasite: boolean
|
||||
led_gpio: number
|
||||
hide_led: boolean
|
||||
notoken_api: boolean
|
||||
analog_enabled: boolean
|
||||
pbutton_gpio: number
|
||||
trace_raw: boolean
|
||||
board_profile: string
|
||||
}
|
||||
|
||||
export enum busConnectionStatus {
|
||||
BUS_STATUS_CONNECTED = 0,
|
||||
BUS_STATUS_TX_ERRORS = 1,
|
||||
BUS_STATUS_OFFLINE = 2
|
||||
BUS_STATUS_OFFLINE = 2,
|
||||
}
|
||||
|
||||
export interface EMSESPStatus {
|
||||
status: busConnectionStatus;
|
||||
rx_received: number;
|
||||
tx_sent: number;
|
||||
rx_quality: number;
|
||||
tx_quality: number;
|
||||
status: busConnectionStatus
|
||||
rx_received: number
|
||||
tx_sent: number
|
||||
rx_quality: number
|
||||
tx_quality: number
|
||||
}
|
||||
|
||||
export interface Device {
|
||||
id: number;
|
||||
type: string;
|
||||
brand: string;
|
||||
name: string;
|
||||
deviceid: number;
|
||||
productid: number;
|
||||
version: string;
|
||||
id: number
|
||||
type: string
|
||||
brand: string
|
||||
name: string
|
||||
deviceid: number
|
||||
productid: number
|
||||
version: string
|
||||
}
|
||||
|
||||
export interface Sensor {
|
||||
no: number;
|
||||
id: string;
|
||||
temp: string;
|
||||
no: number
|
||||
id: string
|
||||
temp: string
|
||||
}
|
||||
|
||||
export interface EMSESPDevices {
|
||||
devices: Device[];
|
||||
sensors: Sensor[];
|
||||
devices: Device[]
|
||||
sensors: Sensor[]
|
||||
}
|
||||
|
||||
export interface EMSESPDeviceData {
|
||||
name: string;
|
||||
data: string[];
|
||||
name: string
|
||||
data: string[]
|
||||
}
|
||||
|
||||
export interface DeviceValue {
|
||||
id: number;
|
||||
data: string,
|
||||
uom: string,
|
||||
name: string,
|
||||
id: number
|
||||
data: string
|
||||
uom: string
|
||||
name: string
|
||||
cmd: string
|
||||
}
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
export interface User {
|
||||
username: string;
|
||||
password: string;
|
||||
admin: boolean;
|
||||
username: string
|
||||
password: string
|
||||
admin: boolean
|
||||
}
|
||||
|
||||
export interface SecuritySettings {
|
||||
users: User[];
|
||||
jwt_secret: string;
|
||||
users: User[]
|
||||
jwt_secret: string
|
||||
}
|
||||
|
||||
export interface GeneratedToken {
|
||||
token: string;
|
||||
token: string
|
||||
}
|
||||
|
||||
@@ -12,64 +12,61 @@
|
||||
|
||||
const isLocalhost = Boolean(
|
||||
window.location.hostname === 'localhost' ||
|
||||
// [::1] is the IPv6 localhost address.
|
||||
window.location.hostname === '[::1]' ||
|
||||
// 127.0.0.0/8 are considered localhost for IPv4.
|
||||
window.location.hostname.match(
|
||||
/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
|
||||
)
|
||||
);
|
||||
// [::1] is the IPv6 localhost address.
|
||||
window.location.hostname === '[::1]' ||
|
||||
// 127.0.0.0/8 are considered localhost for IPv4.
|
||||
window.location.hostname.match(
|
||||
/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/,
|
||||
),
|
||||
)
|
||||
|
||||
type Config = {
|
||||
onSuccess?: (registration: ServiceWorkerRegistration) => void;
|
||||
onUpdate?: (registration: ServiceWorkerRegistration) => void;
|
||||
};
|
||||
onSuccess?: (registration: ServiceWorkerRegistration) => void
|
||||
onUpdate?: (registration: ServiceWorkerRegistration) => void
|
||||
}
|
||||
|
||||
export function register(config?: Config) {
|
||||
if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
|
||||
// The URL constructor is available in all browsers that support SW.
|
||||
const publicUrl = new URL(
|
||||
process.env.PUBLIC_URL,
|
||||
window.location.href
|
||||
);
|
||||
const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href)
|
||||
if (publicUrl.origin !== window.location.origin) {
|
||||
// Our service worker won't work if PUBLIC_URL is on a different origin
|
||||
// from what our page is served on. This might happen if a CDN is used to
|
||||
// serve assets; see https://github.com/facebook/create-react-app/issues/2374
|
||||
return;
|
||||
return
|
||||
}
|
||||
|
||||
window.addEventListener('load', () => {
|
||||
const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
|
||||
const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`
|
||||
|
||||
if (isLocalhost) {
|
||||
// This is running on localhost. Let's check if a service worker still exists or not.
|
||||
checkValidServiceWorker(swUrl, config);
|
||||
checkValidServiceWorker(swUrl, config)
|
||||
|
||||
// Add some additional logging to localhost, pointing developers to the
|
||||
// service worker/PWA documentation.
|
||||
navigator.serviceWorker.ready.then(() => {
|
||||
console.log(
|
||||
'This web app is being served cache-first by a service ' +
|
||||
'worker. To learn more, visit https://bit.ly/CRA-PWA'
|
||||
);
|
||||
});
|
||||
'worker. To learn more, visit https://bit.ly/CRA-PWA',
|
||||
)
|
||||
})
|
||||
} else {
|
||||
// Is not localhost. Just register service worker
|
||||
registerValidSW(swUrl, config);
|
||||
registerValidSW(swUrl, config)
|
||||
}
|
||||
});
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function registerValidSW(swUrl: string, config?: Config) {
|
||||
navigator.serviceWorker
|
||||
.register(swUrl)
|
||||
.then(registration => {
|
||||
.then((registration) => {
|
||||
registration.onupdatefound = () => {
|
||||
const installingWorker = registration.installing;
|
||||
const installingWorker = registration.installing
|
||||
if (installingWorker == null) {
|
||||
return;
|
||||
return
|
||||
}
|
||||
installingWorker.onstatechange = () => {
|
||||
if (installingWorker.state === 'installed') {
|
||||
@@ -79,67 +76,67 @@ function registerValidSW(swUrl: string, config?: Config) {
|
||||
// content until all client tabs are closed.
|
||||
console.log(
|
||||
'New content is available and will be used when all ' +
|
||||
'tabs for this page are closed. See https://bit.ly/CRA-PWA.'
|
||||
);
|
||||
'tabs for this page are closed. See https://bit.ly/CRA-PWA.',
|
||||
)
|
||||
|
||||
// Execute callback
|
||||
if (config && config.onUpdate) {
|
||||
config.onUpdate(registration);
|
||||
config.onUpdate(registration)
|
||||
}
|
||||
} else {
|
||||
// At this point, everything has been precached.
|
||||
// It's the perfect time to display a
|
||||
// "Content is cached for offline use." message.
|
||||
console.log('Content is cached for offline use.');
|
||||
console.log('Content is cached for offline use.')
|
||||
|
||||
// Execute callback
|
||||
if (config && config.onSuccess) {
|
||||
config.onSuccess(registration);
|
||||
config.onSuccess(registration)
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Error during service worker registration:', error)
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error during service worker registration:', error);
|
||||
});
|
||||
}
|
||||
|
||||
function checkValidServiceWorker(swUrl: string, config?: Config) {
|
||||
// Check if the service worker can be found. If it can't reload the page.
|
||||
fetch(swUrl, {
|
||||
headers: { 'Service-Worker': 'script' }
|
||||
headers: { 'Service-Worker': 'script' },
|
||||
})
|
||||
.then(response => {
|
||||
.then((response) => {
|
||||
// Ensure service worker exists, and that we really are getting a JS file.
|
||||
const contentType = response.headers.get('content-type');
|
||||
const contentType = response.headers.get('content-type')
|
||||
if (
|
||||
response.status === 404 ||
|
||||
(contentType != null && contentType.indexOf('javascript') === -1)
|
||||
) {
|
||||
// No service worker found. Probably a different app. Reload the page.
|
||||
navigator.serviceWorker.ready.then(registration => {
|
||||
navigator.serviceWorker.ready.then((registration) => {
|
||||
registration.unregister().then(() => {
|
||||
window.location.reload();
|
||||
});
|
||||
});
|
||||
window.location.reload()
|
||||
})
|
||||
})
|
||||
} else {
|
||||
// Service worker found. Proceed as normal.
|
||||
registerValidSW(swUrl, config);
|
||||
registerValidSW(swUrl, config)
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
console.log(
|
||||
'No internet connection found. App is running in offline mode.'
|
||||
);
|
||||
});
|
||||
'No internet connection found. App is running in offline mode.',
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
export function unregister() {
|
||||
if ('serviceWorker' in navigator) {
|
||||
navigator.serviceWorker.ready.then(registration => {
|
||||
registration.unregister();
|
||||
});
|
||||
navigator.serviceWorker.ready.then((registration) => {
|
||||
registration.unregister()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
const { createProxyMiddleware } = require('http-proxy-middleware');
|
||||
const { createProxyMiddleware } = require('http-proxy-middleware')
|
||||
|
||||
module.exports = function (app) {
|
||||
app.use(
|
||||
'/rest/*',
|
||||
createProxyMiddleware({
|
||||
target: 'http://localhost:3080',
|
||||
secure: false,
|
||||
changeOrigin: true
|
||||
})
|
||||
);
|
||||
};
|
||||
app.use(
|
||||
'/rest/*',
|
||||
createProxyMiddleware({
|
||||
target: 'http://localhost:3080',
|
||||
secure: false,
|
||||
changeOrigin: true,
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,37 +1,37 @@
|
||||
export enum EspPlatform {
|
||||
ESP8266 = "esp8266",
|
||||
ESP32 = "esp32"
|
||||
ESP8266 = 'esp8266',
|
||||
ESP32 = 'esp32',
|
||||
}
|
||||
|
||||
interface ESPSystemStatus {
|
||||
esp_platform: EspPlatform;
|
||||
max_alloc_heap: number;
|
||||
cpu_freq_mhz: number;
|
||||
free_heap: number;
|
||||
sdk_version: string;
|
||||
flash_chip_size: number;
|
||||
flash_chip_speed: number;
|
||||
fs_used: number;
|
||||
fs_total: number;
|
||||
uptime: string;
|
||||
free_mem: number;
|
||||
esp_platform: EspPlatform
|
||||
max_alloc_heap: number
|
||||
cpu_freq_mhz: number
|
||||
free_heap: number
|
||||
sdk_version: string
|
||||
flash_chip_size: number
|
||||
flash_chip_speed: number
|
||||
fs_used: number
|
||||
fs_total: number
|
||||
uptime: string
|
||||
free_mem: number
|
||||
}
|
||||
|
||||
export interface ESP32SystemStatus extends ESPSystemStatus {
|
||||
esp_platform: EspPlatform.ESP32;
|
||||
psram_size: number;
|
||||
free_psram: number;
|
||||
esp_platform: EspPlatform.ESP32
|
||||
psram_size: number
|
||||
free_psram: number
|
||||
}
|
||||
|
||||
export interface ESP8266SystemStatus extends ESPSystemStatus {
|
||||
esp_platform: EspPlatform.ESP8266;
|
||||
heap_fragmentation: number;
|
||||
esp_platform: EspPlatform.ESP8266
|
||||
heap_fragmentation: number
|
||||
}
|
||||
|
||||
export type SystemStatus = ESP8266SystemStatus | ESP32SystemStatus;
|
||||
export type SystemStatus = ESP8266SystemStatus | ESP32SystemStatus
|
||||
|
||||
export interface OTASettings {
|
||||
enabled: boolean;
|
||||
port: number;
|
||||
password: string;
|
||||
enabled: boolean
|
||||
port: number
|
||||
password: string
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
export { default as isHostname } from './isHostname';
|
||||
export { default as isIP } from './isIP';
|
||||
export { default as optional } from './optional';
|
||||
export { default as or } from './or';
|
||||
export { default as isPath } from './isPath';
|
||||
export { default as isHostname } from './isHostname'
|
||||
export { default as isIP } from './isIP'
|
||||
export { default as optional } from './optional'
|
||||
export { default as or } from './or'
|
||||
export { default as isPath } from './isPath'
|
||||
|
||||
@@ -2,5 +2,7 @@ const hostnameLengthRegex = /^.{0,48}$/
|
||||
const hostnamePatternRegex = /^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9-]*[A-Za-z0-9])$/
|
||||
|
||||
export default function isHostname(hostname: string) {
|
||||
return hostnameLengthRegex.test(hostname) && hostnamePatternRegex.test(hostname);
|
||||
return (
|
||||
hostnameLengthRegex.test(hostname) && hostnamePatternRegex.test(hostname)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
const ipAddressRegexp = /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/
|
||||
|
||||
export default function isIp(ipAddress: string) {
|
||||
return ipAddressRegexp.test(ipAddress);
|
||||
}
|
||||
return ipAddressRegexp.test(ipAddress)
|
||||
}
|
||||
|
||||
@@ -2,5 +2,5 @@ const pathLengthRegex = /^[^.]{0,108}$/
|
||||
const pathPatternRegex = /^([a-zA-Z0-9_][a-zA-Z0-9/_-]*[a-zA-Z0-9_])$/
|
||||
|
||||
export default function isPath(path: string) {
|
||||
return pathLengthRegex.test(path) && pathPatternRegex.test(path);
|
||||
}
|
||||
return pathLengthRegex.test(path) && pathPatternRegex.test(path)
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
const OPTIONAL = (validator: (value: any) => boolean) => (value: any) => !value || validator(value);
|
||||
const OPTIONAL = (validator: (value: any) => boolean) => (value: any) =>
|
||||
!value || validator(value)
|
||||
|
||||
export default OPTIONAL;
|
||||
export default OPTIONAL
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
const OR = (validator1: (value: any) => boolean, validator2: (value: any) => boolean) => {
|
||||
return (value: any) => validator1(value) || validator2(value);
|
||||
};
|
||||
|
||||
export default OR;
|
||||
const OR = (
|
||||
validator1: (value: any) => boolean,
|
||||
validator2: (value: any) => boolean,
|
||||
) => {
|
||||
return (value: any) => validator1(value) || validator2(value)
|
||||
}
|
||||
|
||||
export default OR
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"lib": [
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"esnext"
|
||||
],
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"esModuleInterop": true,
|
||||
@@ -20,7 +16,5 @@
|
||||
"jsx": "react-jsx",
|
||||
"noFallthroughCasesInSwitch": true
|
||||
},
|
||||
"include": [
|
||||
"src"
|
||||
]
|
||||
}
|
||||
"include": ["src"]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user