Add access point IP address configuration in UI

* Allow ap ip address to be configured via ui
* Add missing WiFi status to UI enum
* Update package-lock.json
This commit is contained in:
rjwats 2020-07-07 22:22:38 +01:00 committed by GitHub
parent f2b53a6d53
commit 6ef5df28c1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 2183 additions and 1363 deletions

View File

@ -6,9 +6,12 @@ build_flags =
-D FACTORY_WIFI_HOSTNAME=\"esp-react\" -D FACTORY_WIFI_HOSTNAME=\"esp-react\"
; Access point settings ; Access point settings
-D FACTORY_AP_PROVISION_MODE=AP_MODE_DISCONNECTED
-D FACTORY_AP_SSID=\"ESP8266-React\" ; 1-64 characters -D FACTORY_AP_SSID=\"ESP8266-React\" ; 1-64 characters
-D FACTORY_AP_PASSWORD=\"esp-react\" ; 8-64 characters -D FACTORY_AP_PASSWORD=\"esp-react\" ; 8-64 characters
-D FACTORY_AP_PROVISION_MODE=AP_MODE_DISCONNECTED -D FACTORY_AP_LOCAL_IP=\"192.168.4.1\"
-D FACTORY_AP_GATEWAY_IP=\"192.168.4.1\"
-D FACTORY_AP_SUBNET_MASK=\"255.255.255.0\"
; User credentials for admin and guest user ; User credentials for admin and guest user
-D FACTORY_ADMIN_USERNAME=\"admin\" -D FACTORY_ADMIN_USERNAME=\"admin\"

File diff suppressed because it is too large Load Diff

View File

@ -8,11 +8,16 @@ import {PasswordValidator, RestFormProps, FormActions, FormButton} from '../comp
import { isAPEnabled } from './APModes'; import { isAPEnabled } from './APModes';
import { APSettings, APProvisionMode } from './types'; import { APSettings, APProvisionMode } from './types';
import { isIP } from '../validators';
type APSettingsFormProps = RestFormProps<APSettings>; type APSettingsFormProps = RestFormProps<APSettings>;
class APSettingsForm extends React.Component<APSettingsFormProps> { class APSettingsForm extends React.Component<APSettingsFormProps> {
componentWillMount() {
ValidatorForm.addValidationRule('isIP', isIP);
}
render() { render() {
const { data, handleValueChange, saveData, loadData } = this.props; const { data, handleValueChange, saveData, loadData } = this.props;
return ( return (
@ -53,6 +58,39 @@ class APSettingsForm extends React.Component<APSettingsFormProps> {
onChange={handleValueChange('password')} onChange={handleValueChange('password')}
margin="normal" margin="normal"
/> />
<TextValidator
validators={['required', 'isIP']}
errorMessages={['Local IP is required', 'Must be an IP address']}
name="local_ip"
label="Local IP"
fullWidth
variant="outlined"
value={data.local_ip}
onChange={handleValueChange('local_ip')}
margin="normal"
/>
<TextValidator
validators={['required', 'isIP']}
errorMessages={['Gateway IP is required', 'Must be an IP address']}
name="gateway_ip"
label="Gateway"
fullWidth
variant="outlined"
value={data.gateway_ip}
onChange={handleValueChange('gateway_ip')}
margin="normal"
/>
<TextValidator
validators={['required', 'isIP']}
errorMessages={['Subnet mask is required', 'Must be an IP address']}
name="subnet_mask"
label="Subnet"
fullWidth
variant="outlined"
value={data.subnet_mask}
onChange={handleValueChange('subnet_mask')}
margin="normal"
/>
</Fragment> </Fragment>
} }
<FormActions> <FormActions>

View File

@ -21,4 +21,7 @@ export interface APSettings {
provision_mode: APProvisionMode; provision_mode: APProvisionMode;
ssid: string; ssid: string;
password: string; password: string;
local_ip: string;
gateway_ip: string;
subnet_mask: string;
} }

View File

@ -60,7 +60,7 @@ const SingleUpload: FC<SingleUploadProps> = ({ onDrop, onCancel, accept, uploadi
} }
return "Uploading\u2026"; return "Uploading\u2026";
} }
return "Drop file here or click to browse"; return "Drop file or click here";
} }
const renderProgress = (progress?: ProgressEvent) => ( const renderProgress = (progress?: ProgressEvent) => (

View File

@ -7,6 +7,7 @@ export const wifiStatusHighlight = ({ status }: WiFiStatus, theme: Theme) => {
switch (status) { switch (status) {
case WiFiConnectionStatus.WIFI_STATUS_IDLE: case WiFiConnectionStatus.WIFI_STATUS_IDLE:
case WiFiConnectionStatus.WIFI_STATUS_DISCONNECTED: case WiFiConnectionStatus.WIFI_STATUS_DISCONNECTED:
case WiFiConnectionStatus.WIFI_STATUS_NO_SHIELD:
return theme.palette.info.main; return theme.palette.info.main;
case WiFiConnectionStatus.WIFI_STATUS_CONNECTED: case WiFiConnectionStatus.WIFI_STATUS_CONNECTED:
return theme.palette.success.main; return theme.palette.success.main;
@ -20,6 +21,8 @@ export const wifiStatusHighlight = ({ status }: WiFiStatus, theme: Theme) => {
export const wifiStatus = ({ status }: WiFiStatus) => { export const wifiStatus = ({ status }: WiFiStatus) => {
switch (status) { switch (status) {
case WiFiConnectionStatus.WIFI_STATUS_NO_SHIELD:
return "Inactive";
case WiFiConnectionStatus.WIFI_STATUS_IDLE: case WiFiConnectionStatus.WIFI_STATUS_IDLE:
return "Idle"; return "Idle";
case WiFiConnectionStatus.WIFI_STATUS_NO_SSID_AVAIL: case WiFiConnectionStatus.WIFI_STATUS_NO_SSID_AVAIL:

View File

@ -4,7 +4,8 @@ export enum WiFiConnectionStatus {
WIFI_STATUS_CONNECTED = 3, WIFI_STATUS_CONNECTED = 3,
WIFI_STATUS_CONNECT_FAILED = 4, WIFI_STATUS_CONNECT_FAILED = 4,
WIFI_STATUS_CONNECTION_LOST = 5, WIFI_STATUS_CONNECTION_LOST = 5,
WIFI_STATUS_DISCONNECTED = 6 WIFI_STATUS_DISCONNECTED = 6,
WIFI_STATUS_NO_SHIELD = 255
} }
export enum WiFiEncryptionType { export enum WiFiEncryptionType {

View File

@ -45,6 +45,7 @@ void APSettingsService::manageAP() {
void APSettingsService::startAP() { void APSettingsService::startAP() {
Serial.println(F("Starting software access point")); Serial.println(F("Starting software access point"));
WiFi.softAPConfig(_state.localIP, _state.gatewayIP, _state.subnetMask);
WiFi.softAP(_state.ssid.c_str(), _state.password.c_str()); WiFi.softAP(_state.ssid.c_str(), _state.password.c_str());
if (!_dnsServer) { if (!_dnsServer) {
IPAddress apIp = WiFi.softAPIP(); IPAddress apIp = WiFi.softAPIP();

View File

@ -3,6 +3,7 @@
#include <HttpEndpoint.h> #include <HttpEndpoint.h>
#include <FSPersistence.h> #include <FSPersistence.h>
#include <JsonUtils.h>
#include <DNSServer.h> #include <DNSServer.h>
#include <IPAddress.h> #include <IPAddress.h>
@ -15,6 +16,10 @@
#define DNS_PORT 53 #define DNS_PORT 53
#ifndef FACTORY_AP_PROVISION_MODE
#define FACTORY_AP_PROVISION_MODE AP_MODE_DISCONNECTED
#endif
#ifndef FACTORY_AP_SSID #ifndef FACTORY_AP_SSID
#define FACTORY_AP_SSID "ESP8266-React" #define FACTORY_AP_SSID "ESP8266-React"
#endif #endif
@ -23,29 +28,44 @@
#define FACTORY_AP_PASSWORD "esp-react" #define FACTORY_AP_PASSWORD "esp-react"
#endif #endif
#ifndef FACTORY_AP_PROVISION_MODE #ifndef FACTORY_AP_LOCAL_IP
#define FACTORY_AP_PROVISION_MODE AP_MODE_DISCONNECTED #define FACTORY_AP_LOCAL_IP "192.168.4.1"
#endif
#ifndef FACTORY_AP_GATEWAY_IP
#define FACTORY_AP_GATEWAY_IP "192.168.4.1"
#endif
#ifndef FACTORY_AP_SUBNET_MASK
#define FACTORY_AP_SUBNET_MASK "255.255.255.0"
#endif #endif
#define AP_SETTINGS_FILE "/config/apSettings.json" #define AP_SETTINGS_FILE "/config/apSettings.json"
#define AP_SETTINGS_SERVICE_PATH "/rest/apSettings" #define AP_SETTINGS_SERVICE_PATH "/rest/apSettings"
enum APNetworkStatus { enum APNetworkStatus { ACTIVE = 0, INACTIVE, LINGERING };
ACTIVE = 0,
INACTIVE,
LINGERING
};
class APSettings { class APSettings {
public: public:
uint8_t provisionMode; uint8_t provisionMode;
String ssid; String ssid;
String password; String password;
IPAddress localIP;
IPAddress gatewayIP;
IPAddress subnetMask;
bool operator==(const APSettings& settings) const {
return provisionMode == settings.provisionMode && ssid == settings.ssid && password == settings.password &&
localIP == settings.localIP && gatewayIP == settings.gatewayIP && subnetMask == settings.subnetMask;
}
static void read(APSettings& settings, JsonObject& root) { static void read(APSettings& settings, JsonObject& root) {
root["provision_mode"] = settings.provisionMode; root["provision_mode"] = settings.provisionMode;
root["ssid"] = settings.ssid; root["ssid"] = settings.ssid;
root["password"] = settings.password; root["password"] = settings.password;
root["local_ip"] = settings.localIP.toString();
root["gateway_ip"] = settings.gatewayIP.toString();
root["subnet_mask"] = settings.subnetMask.toString();
} }
static StateUpdateResult update(JsonObject& root, APSettings& settings) { static StateUpdateResult update(JsonObject& root, APSettings& settings) {
@ -61,8 +81,12 @@ class APSettings {
} }
newSettings.ssid = root["ssid"] | FACTORY_AP_SSID; newSettings.ssid = root["ssid"] | FACTORY_AP_SSID;
newSettings.password = root["password"] | FACTORY_AP_PASSWORD; newSettings.password = root["password"] | FACTORY_AP_PASSWORD;
if (newSettings.provisionMode == settings.provisionMode && newSettings.ssid.equals(settings.ssid) &&
newSettings.password.equals(settings.password)) { JsonUtils::readIP(root, "local_ip", newSettings.localIP, FACTORY_AP_LOCAL_IP);
JsonUtils::readIP(root, "gateway_ip", newSettings.gatewayIP, FACTORY_AP_GATEWAY_IP);
JsonUtils::readIP(root, "subnet_mask", newSettings.subnetMask, FACTORY_AP_SUBNET_MASK);
if (newSettings == settings) {
return StateUpdateResult::UNCHANGED; return StateUpdateResult::UNCHANGED;
} }
settings = newSettings; settings = newSettings;

View File

@ -7,9 +7,16 @@
class JsonUtils { class JsonUtils {
public: public:
static void readIP(JsonObject& root, const String& key, IPAddress& ip) { static void readIP(JsonObject& root, const String& key, IPAddress& ip, const String& def) {
IPAddress defaultIp = {};
if (!defaultIp.fromString(def)) {
defaultIp = INADDR_NONE;
}
readIP(root, key, ip, defaultIp);
}
static void readIP(JsonObject& root, const String& key, IPAddress& ip, const IPAddress& defaultIp = INADDR_NONE) {
if (!root[key].is<String>() || !ip.fromString(root[key].as<String>())) { if (!root[key].is<String>() || !ip.fromString(root[key].as<String>())) {
ip = INADDR_NONE; ip = defaultIp;
} }
} }
static void writeIP(JsonObject& root, const String& key, const IPAddress& ip) { static void writeIP(JsonObject& root, const String& key, const IPAddress& ip) {