Tearing my hair out here. Please assist.
config.page.ts
import { Component, OnInit, ChangeDetectorRef } from '@angular/core';
// FIXME Uncomment
import { BtoothService } from '../../btooth/btooth.service';
import { Validators, FormGroup, FormBuilder, FormControl } from '@angular/forms';
import { Router } from '@angular/router';
@Component({
selector: 'app-config',
templateUrl: './config.page.html',
styleUrls: ['./config.page.scss'],
})
export class ConfigPage implements OnInit {
private wifiAPsUUID = '2FE11A47-E4B8-4522-9CFF-AA729B8215D3';
private wifiSSIDsUUID = '2FE11A47-E4B8-4522-9CFF-AA729B8215C2';
private wifiSSIDUUID = '2FE11A47-E4B8-4522-9CFF-AA729B8215C4';
private wifiPassUUID = '2FE11A47-E4B8-4522-9CFF-AA729B8215C5';
private wifiSecTypeUUID = '2FE11A47-E4B8-4522-9CFF-AA729B8215C6';
private wifiHiddenUUID = '2FE11A47-E4B8-4522-9CFF-AA729B8215C7';
private wifiIPAssignmentTypeUUID = '2FE11A47-E4B8-4522-9CFF-AA729B8215D4';
private wifiIPUUID = '2FE11A47-E4B8-4522-9CFF-AA729B8215C8';
private wifiNetmaskUUID = '2FE11A47-E4B8-4522-9CFF-AA729B8215C9';
private wifiGatewayUUID = '2FE11A47-E4B8-4522-9CFF-AA729B8215CA';
private wifiPrimaryDNSUUID = '2FE11A47-E4B8-4522-9CFF-AA729B8215CB';
private wifiSecondaryDNSUUID = '2FE11A47-E4B8-4522-9CFF-AA729B8215CC';
private wifiSaveUUID = '2FE11A47-E4B8-4522-9CFF-AA729B8215CD';
public ssids = [];
public aps = [];
public ap_form: FormGroup;
public encrypted = false;
public display_ip = false;
public selected_ssid = false;
public saved_ssid = '';
validations = {
'ssid_selector': [
{ type: 'required', message: 'Network name is required.' },
],
'hidden_ssid': [
{ type: 'required', message: 'Network name is required.' },
],
'password': [
{ type: 'required', message: 'Password is required.' },
],
'ip': [
{ type: 'required', message: 'IP address is required.' },
{ type: 'pattern', message: 'Must be formatted like 1.2.3.4' },
],
'netmask': [
{ type: 'required', message: 'Netmask is required.' },
{ type: 'pattern', message: 'Must be formatted like 255.255.255.0' },
],
'gateway': [
{ type: 'required', message: 'Gateway is required.' },
{ type: 'pattern', message: 'Must be formatted like 1.2.3.4' },
],
'primary_dns': [
{ type: 'required', message: 'Primary DNS is required.' },
{ type: 'pattern', message: 'Must be formatted like 1.2.3.4' },
],
'secondary_dns': [
{ type: 'required', message: 'Secondary DNS is required.' },
{ type: 'pattern', message: 'Must be formatted like 1.2.3.4' },
],
};
constructor(
private router: Router,
private cdr: ChangeDetectorRef,
// FIXME Uncomment
private btoothService: BtoothService,
private formBuilder: FormBuilder,
) {
this.ap_form = this.formBuilder.group({
ssid_selector: new FormControl(''),
hidden_ssid: new FormControl(''),
password: new FormControl(''),
security_type: new FormControl('wpa'),
ip_assignment_type: new FormControl('dhcp'),
ip: new FormControl('', Validators.pattern('^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$')),
netmask: new FormControl('', Validators.pattern('^(((255\.){3}(255|254|252|248|240|224|192|128|0+))|((255\.){2}(255|254|252|248|240|224|192|128|0+)\.0)|((255\.)(255|254|252|248|240|224|192|128|0+)(\.0+){2})|((255|254|252|248|240|224|192|128|0+)(\.0+){3}))$')),
gateway: new FormControl('', Validators.pattern('^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$')),
primary_dns: new FormControl('', Validators.pattern('^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$')),
secondary_dns: new FormControl('', Validators.pattern('^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$')),
}, {});
}
ngOnInit() {
// FIXME Uncomment
this.btoothService.read(this.wifiSSIDsUUID).then(
value => {
value = this.btoothService.bytesToString(value);
try {
this.ssids = JSON.parse(value);
} catch {
this.ssids = value;
}
console.log('[DEBUG] config ngOnInit() Bluetooth read(this.wifiSSIDsUUID) ssids: ', JSON.stringify(value));
this.cdr.detectChanges();
},
error => {
// FIXME Errors
console.log('[DEBUG] config ngOnInit() Bluetooth read(this.wifiSSIDsUUID) error: ', JSON.stringify(error));
}
);
// FIXME Remove
// this.ssids = ['Home', 'Left neighbor', 'Right neighbor']
// FIXME Uncomment
this.btoothService.read(this.wifiAPsUUID).then(
value => {
value = this.btoothService.bytesToString(value);
try {
this.aps = JSON.parse(value);
} catch {
this.aps = value;
}
console.log('[DEBUG] config ngOnInit() Bluetooth read(this.wifiAPsUUID) aps: ', JSON.stringify(value));
this.cdr.detectChanges();
},
error => {
console.log('[DEBUG] config ngOnInit() Bluetooth read(this.wifiAPsUUID) error: ', JSON.stringify(error));
}
);
// FIXME Remove
// this.encrypted = true;
// FIXME Uncomment
this.btoothService.read(this.wifiSSIDUUID).then(
value => {
value = this.btoothService.bytesToString(value);
try {
value = JSON.parse(value);
} catch {
value = value;
}
// FIXME Also need to decode from wpa_supplicant encoded, I think it's base64?
console.log('[DEBUG] config ngOnInit() ssid from Bluetooth:', JSON.stringify(value));
if (value === '') {
console.log('[DEBUG] config ngOnInit() no ssid from Bluetooth');
return;
}
this.ap_form.get('password').disable();
this.ap_form.patchValue({ ssid_selector: value });
this.cdr.detectChanges();
},
error => {
console.log('[DEBUG] config ngOnInit() Bluetooth read(this.wifiSSIDUUID) error: ', JSON.stringify(error));
}
);
// FIXME Remove
// this.ap_form.patchValue({ 'ssid_selector': 'Home' });
// FIXME Uncomment
this.btoothService.read(this.wifiSecTypeUUID).then(
value => {
console.log('[DEBUG] config ngOnInit() SecType from Bluetooth raw bytes:', JSON.stringify(value));
value = this.btoothService.bytesToString(value);
console.log('[DEBUG] config ngOnInit() SecType from Bluetooth raw string:', JSON.stringify(value));
// FIXME Don't try to parse regular strings. Not sure how to tell the difference. Maybe always send JSON?
// Maybe do like in Python, attempt to parse, if it fails just pass the value.
try {
value = JSON.parse(value);
} catch {
value = value;
}
console.log('[DEBUG] config ngOnInit() SecType from Bluetooth:', JSON.stringify(value));
if (value === '') {
console.log('[DEBUG] config ngOnInit() no SecType from Bluetooth');
return;
}
this.ap_form.patchValue({ security_type: value });
this.cdr.detectChanges();
},
error => {
console.log('[DEBUG] config ngOnInit() Bluetooth read(this.wifiSecTypeUUID) error: ', JSON.stringify(error));
}
);
// FIXME Remove
// this.ap_form.patchValue({ 'security_type': 'wpa' });
// FIXME Uncomment
this.btoothService.read(this.wifiHiddenUUID).then(
value => {
value = this.btoothService.bytesToString(value);
try {
value = JSON.parse(value);
} catch {
value = value;
}
console.log('[DEBUG] config ngOnInit() hidden from Bluetooth:', JSON.stringify(value));
if (value === '') {
console.log('[DEBUG] config ngOnInit() no hidden from Bluetooth');
return;
}
if (value !== 'true') {
console.log('[DEBUG] config ngOnInit() no hidden from Bluetooth');
return;
}
this.ap_form.patchValue({ ssid_selector: '[Hidden]' });
this.ap_form.patchValue({ hidden_ssid: value });
this.cdr.detectChanges();
},
error => {
console.log('[DEBUG] config ngOnInit() Bluetooth read(this.wifiHiddenUUID) error: ', JSON.stringify(error));
}
);
this.btoothService.read(this.wifiIPUUID).then(
value => {
value = this.btoothService.bytesToString(value);
try {
value = JSON.parse(value);
} catch {
value = value;
}
console.log('[DEBUG] config ngOnInit() IP from Bluetooth:', JSON.stringify(value));
if (value === '') {
console.log('[DEBUG] config ngOnInit() no IP from Bluetooth');
return;
}
this.ap_form.patchValue({ ip: value });
this.cdr.detectChanges();
},
error => {
console.log('[DEBUG] config ngOnInit() Bluetooth read(this.wifiIPUUID) error: ', JSON.stringify(error));
}
);
this.btoothService.read(this.wifiNetmaskUUID).then(
value => {
value = this.btoothService.bytesToString(value);
try {
value = JSON.parse(value);
} catch {
value = value;
}
console.log('[DEBUG] config ngOnInit() Netmask from Bluetooth:', JSON.stringify(value));
if (value === '') {
console.log('[DEBUG] config ngOnInit() no Netmask from Bluetooth');
return;
}
this.ap_form.patchValue({ netmask: value });
this.cdr.detectChanges();
},
error => {
console.log('[DEBUG] config ngOnInit() Bluetooth read(this.wifiNetmaskUUID) error: ', JSON.stringify(error));
}
);
this.btoothService.read(this.wifiGatewayUUID).then(
value => {
value = this.btoothService.bytesToString(value);
try {
value = JSON.parse(value);
} catch {
value = value;
}
console.log('[DEBUG] config ngOnInit() Gateway from Bluetooth:', JSON.stringify(value));
if (value === '') {
console.log('[DEBUG] config ngOnInit() no Gateway from Bluetooth');
return;
}
this.ap_form.patchValue({ gateway: value });
this.cdr.detectChanges();
},
error => {
console.log('[DEBUG] config ngOnInit() Bluetooth read(this.wifiGatewayUUID) error: ', JSON.stringify(error));
}
);
this.btoothService.read(this.wifiPrimaryDNSUUID).then(
value => {
value = this.btoothService.bytesToString(value);
try {
value = JSON.parse(value);
} catch {
value = value;
}
console.log('[DEBUG] config ngOnInit() PrimaryDNS from Bluetooth:', JSON.stringify(value));
if (value === '') {
console.log('[DEBUG] config ngOnInit() no PrimaryDNS from Bluetooth');
return;
}
this.ap_form.patchValue({ primary_dns: value });
this.cdr.detectChanges();
},
error => {
console.log('[DEBUG] config ngOnInit() Bluetooth read(this.wifiPrimaryDNSUUID) error: ', JSON.stringify(error));
}
);
this.btoothService.read(this.wifiSecondaryDNSUUID).then(
value => {
value = this.btoothService.bytesToString(value);
try {
value = JSON.parse(value);
} catch {
value = value;
}
console.log('[DEBUG] config ngOnInit() SecondaryDNS from Bluetooth:', JSON.stringify(value));
if (value === '') {
console.log('[DEBUG] config ngOnInit() no SecondaryDNS from Bluetooth');
return;
}
this.ap_form.patchValue({ secondary_dns: value });
this.cdr.detectChanges();
},
error => {
console.log('[DEBUG] config ngOnInit() Bluetooth read(this.wifiSecondaryDNSUUID) error: ', JSON.stringify(error));
}
);
this.btoothService.read(this.wifiIPAssignmentTypeUUID).then(
value => {
value = this.btoothService.bytesToString(value);
try {
value = JSON.parse(value);
} catch {
value = value;
}
console.log('[DEBUG] config ngOnInit() IP assignment type from Bluetooth:', JSON.stringify(value));
if (value === '') {
console.log('[DEBUG] config ngOnInit() no IP assignment type from Bluetooth');
return;
}
this.ap_form.patchValue({ ip_assignment_type: value });
if (value === 'dhcp') {
this.display_ip = false;
} else {
this.display_ip = true;
}
this.cdr.detectChanges();
},
error => {
console.log('[DEBUG] config ngOnInit() Bluetooth read(this.wifiIPAssignmentTypeUUID) error: ', JSON.stringify(error));
}
);
// FIXME Remove
// this.ap_form.patchValue({ 'ip_assignment_type': 'dhcp' });
// this.display_ip = false;
this.cdr.detectChanges();
// TODO Delete selected-ssid service
this.ap_form.get('ssid_selector').valueChanges.subscribe(
ssid => {
this.ap_form.get('password').enable();
console.log('[DEBUG] ssid: ', JSON.stringify(ssid));
if (ssid === '[Hidden]') {
if (this.ap_form.get('security_type').value === 'none') {
this.encrypted = false;
} else {
this.encrypted = true;
}
this.cdr.detectChanges();
return;
}
this.selected_ssid = true;
const selected_AP = this.aps[ssid];
console.log('[DEBUG] config ngOnInit() ap_form.get(ssid_selector) selected_AP: ', JSON.stringify(selected_AP));
this.encrypted = selected_AP[1];
this.cdr.detectChanges();
},
error => {
// FIXME Errors
console.log('[DEBUG] config ngOnInit() ap_form.get(ssid_selector) error: ', JSON.stringify(error));
}
);
this.ap_form.get('hidden_ssid').valueChanges.subscribe(
hidden_ssid => {
if (hidden_ssid !== '') {
this.selected_ssid = true;
}
}
);
this.ap_form.get('security_type').valueChanges.subscribe(
security_type => {
console.log('[DEBUG] security_type: ', JSON.stringify(security_type));
if (security_type === 'none') {
this.encrypted = false;
} else {
this.encrypted = true;
}
this.cdr.detectChanges();
}
);
}
display_ip_form(): void {
this.display_ip = !this.display_ip;
console.log('[DEBUG] config this.display_ip: ', JSON.stringify(this.display_ip));
this.cdr.detectChanges();
}
onFormSubmit(): void {
let ssid = '';
const selected_ssid = this.ap_form.get('ssid_selector').value;
const hidden_ssid = this.ap_form.get('hidden_ssid').value;
if (hidden_ssid === '') {
ssid = selected_ssid;
console.log('[DEBUG] config saving hidden: ', JSON.stringify('false'));
// FIXME Uncomment
const hidden = this.btoothService.stringToBytes('false');
this.btoothService.write(this.wifiHiddenUUID, hidden);
} else {
ssid = hidden_ssid;
console.log('[DEBUG] config saving hidden: ', JSON.stringify('true'));
// FIXME Uncomment
const hidden = this.btoothService.stringToBytes('true');
this.btoothService.write(this.wifiHiddenUUID, hidden);
}
console.log('[DEBUG] config saving ssid: ', JSON.stringify(ssid));
// FIXME Uncomment
const bytes_ssid = this.btoothService.stringToBytes(ssid);
this.btoothService.write(this.wifiSSIDUUID, bytes_ssid);
// FIXME In the BTooth code, if we pass an ssid but no password or security_type, look to see
// if same as existing or in list of APs but if not, if no password assume none, and if
// password error since we don't know if WEP or WPA.
let security_type = this.ap_form.get('security_type').value;
console.log('[DEBUG] config saving security_type: ', JSON.stringify(security_type));
// FIXME Uncomment
security_type = this.btoothService.stringToBytes(this.ap_form.get('security_type').value);
this.btoothService.write(this.wifiSecTypeUUID, security_type);
if (this.encrypted) {
const password = this.ap_form.get('password').value;
console.log('[DEBUG] config saving password: ', JSON.stringify(password));
// FIXME Uncomment
const bytes_password = this.btoothService.stringToBytes(password);
this.btoothService.write(this.wifiPassUUID, bytes_password);
}
const ip_assignment_type = this.ap_form.get('ip_assignment_type').value;
console.log('[DEBUG] config saving ip_assignment_type: ', JSON.stringify(ip_assignment_type));
// FIXME Uncomment
const bytes_ip_assignment_type = this.btoothService.stringToBytes(ip_assignment_type);
this.btoothService.write(this.wifiIPAssignmentTypeUUID, bytes_ip_assignment_type);
if (ip_assignment_type === 'static') {
console.log('[DEBUG] config saving ip: ', JSON.stringify(this.ap_form.get('ip').value));
// FIXME Uncomment
const ip = this.btoothService.stringToBytes(this.ap_form.get('ip').value);
this.btoothService.write(this.wifiIPUUID, ip);
console.log('[DEBUG] config saving netmask: ', JSON.stringify(this.ap_form.get('netmask').value));
// FIXME Uncomment
const netmask = this.btoothService.stringToBytes(this.ap_form.get('netmask').value);
this.btoothService.write(this.wifiNetmaskUUID, netmask);
console.log('[DEBUG] config saving primary_dns: ', JSON.stringify(this.ap_form.get('primary_dns').value));
// FIXME Uncomment
const primary_dns = this.btoothService.stringToBytes(this.ap_form.get('primary_dns').value);
this.btoothService.write(this.wifiPrimaryDNSUUID, primary_dns);
console.log('[DEBUG] config saving secondary_dns: ', JSON.stringify(this.ap_form.get('secondary_dns').value));
// FIXME Uncomment
const secondary_dns = this.btoothService.stringToBytes(this.ap_form.get('secondary_dns').value);
this.btoothService.write(this.wifiSecondaryDNSUUID, secondary_dns);
}
console.log('[DEBUG] config asking the device to save all values');
// FIXME Uncomment
const save = this.btoothService.stringToBytes('save');
this.btoothService.write(this.wifiSaveUUID, save);
let save_successful = false;
// This setTimeout() is for total time
// TODO If only I had notifications working...
setTimeout(() => {
console.log('[DEBUG] config setTimeout() for read save status total timeout');
while (!save_successful) {
// FIXME This just loops
console.log('[DEBUG] config saving !save_successful');
// This setTimeout() is for time between tries
setTimeout(() => {
console.log('[DEBUG] config setTimeout() for read save status retry');
// FIXME Uncomment
this.btoothService.read(this.wifiSaveUUID).then(
value => {
console.log('[DEBUG] config saving read save status from Bluetooth value: ', JSON.stringify(value));
value = this.btoothService.bytesToString(value);
try {
value = JSON.parse(value);
} catch {
value = value;
}
console.log('[DEBUG] config saving read save status from Bluetooth:', JSON.stringify(value));
if (value === '') {
console.log('[DEBUG] config saving read save status from Bluetooth is empty (not saved)');
return;
}
if (value !== 'true') {
console.log('[DEBUG] config saving read save status from Bluetooth is not true (not saved)');
return;
}
save_successful = true;
// FIXME Uncomment
// this.router.navigate(['setup/server-connect']);
},
error => {
console.log('[DEBUG] config saving read save status from Bluetooth error: ', JSON.stringify(error));
}
);
// setTimeout() retry milliseconds
}, 3000);
}
}, 10000);
}
}
config.page.html
<ion-header>
<ion-toolbar color="primary">
<ion-buttons slot="start">
<!-- FIXME Only show during config of existing unit -->
<!-- TODO Allow the user to download an SD card image and flashing software in a single .exe, Mac .dimg, Linux .tar.gz. -->
<ion-menu-button></ion-menu-button>
</ion-buttons>
<ion-title>Wi-Fi</ion-title>
</ion-toolbar>
</ion-header>
<ion-content class="forms-validations-content">
<form class="validations-form" [formGroup]="ap_form" (ngSubmit)="onFormSubmit()">
<ion-list class="inputs-list" lines="full">
<ion-list-header>
<ion-label class="header-title">Select network</ion-label>
</ion-list-header>
<ion-item class="input-item">
<ion-select interface="popover" formControlName="ssid_selector" placeholder="Select network">
<ion-select-option *ngFor="let ssid of ssids" [value]="ssid">
{{ssid}}
</ion-select-option>
<ion-select-option value="[Hidden]">[Hidden]</ion-select-option>
</ion-select>
</ion-item>
<div class="error-container">
<ng-container *ngFor="let validation of validations.ssid_selector">
<div class="error-message"
*ngIf="ap_form.get('ssid_selector').hasError(validation.type) && (ap_form.get('ssid_selector').dirty || ap_form.get('ssid_selector').touched)">
<ion-icon name="information-circle-outline"></ion-icon>
<span>{{ validation.message }}</span>
</div>
</ng-container>
</div>
<div *ngIf="ap_form.controls.ssid_selector.value === '[Hidden]'">
<ion-item class="input-item">
<ion-label position="floating">Hidden network name</ion-label>
<ion-input formControlName="hidden_ssid" clearInput required>
</ion-input>
</ion-item>
<div class="error-container">
<ng-container *ngFor="let validation of validations.hidden_ssid">
<div class="error-message"
*ngIf="ap_form.controls.ssid_selector.value === '[Hidden]' && ap_form.get('hidden_ssid').hasError(validation.type) && (ap_form.get('hidden_ssid').dirty || ap_form.get('hidden_ssid').touched)">
<ion-icon name="information-circle-outline"></ion-icon>
<span>{{ validation.message }}</span>
</div>
</ng-container>
</div>
<ion-label class="header-title">Security type</ion-label>
<ion-item class="input-item">
<ion-label>Security type</ion-label>
<ion-select interface="popover" formControlName="security_type" cancelText="Cancel" okText="OK">
<ion-select-option value="wpa">WPA</ion-select-option>
<ion-select-option value="wep">WEP</ion-select-option>
<!-- FIXME Is it none or open? I can't tell from the code, have to examine a system. -->
<ion-select-option value="none">None</ion-select-option>
</ion-select>
</ion-item>
</div>
<div *ngIf="encrypted">
<ion-item class="input-item">
<!-- FIXME Remove these packages:
@angular/material
@angular/cdk
@angular/animations
-->
<!-- FIXME https://www.chromium.org/developers/design-documents/create-amazing-password-forms -->
<!-- TODO Eye icon to see password being typed -->
<ion-label position="floating">Password</ion-label>
<ion-input type="password" formControlName="password" required></ion-input>
</ion-item>
<div class="error-container">
<ng-container *ngFor="let validation of validations.password">
<div class="error-message"
*ngIf="encrypted && ap_form.get('password').enabled && ap_form.get('password').hasError(validation.type) && (ap_form.get('password').dirty || ap_form.get('password').touched)">
<ion-icon name="information-circle-outline"></ion-icon>
<span>{{ validation.message }}</span>
</div>
</ng-container>
</div>
</div>
<ion-item class="input-item">
<ion-button class="submit-btn" expand="block" fill="outline" (click)="display_ip_form()">
IP address</ion-button>
</ion-item>
<div *ngIf="display_ip">
<ion-list-header>
<ion-label class="header-title">Assignment type</ion-label>
</ion-list-header>
<ion-radio-group class="radio-group" formControlName="ip_assignment_type">
<ion-item class="radio-item">
<ion-label class="radio-label">Automatic (DHCP)</ion-label>
<ion-radio slot="start" color="secondary" value="dhcp"></ion-radio>
</ion-item>
<ion-item class="radio-item">
<ion-label class="radio-label">Manual (Static)</ion-label>
<ion-radio slot="start" color="secondary" value="static"></ion-radio>
</ion-item>
</ion-radio-group>
<div *ngIf="ap_form.controls.ip_assignment_type.value === 'static'">
<ion-item class="input-item">
<ion-label position="floating">IP address</ion-label>
<ion-input formControlName="ip" placeholder="192.168.1.100" clearInput required>
</ion-input>
</ion-item>
<!-- TODO If we start to type something then switch back to DHCP the form won't allow saving -->
<div class="error-container">
<ng-container *ngFor="let validation of validations.ip">
<div class="error-message"
*ngIf="ap_form.controls.ip_assignment_type.value === 'static' && ap_form.get('ip').hasError(validation.type) && (ap_form.get('ip').dirty || ap_form.get('ip').touched)">
<ion-icon name="information-circle-outline"></ion-icon>
<span>{{ validation.message }}</span>
</div>
</ng-container>
</div>
<ion-item class="input-item">
<ion-label position="floating">Netmask</ion-label>
<ion-input formControlName="netmask" placeholder="255.255.255.0" clearInput required>
</ion-input>
</ion-item>
<div class="error-container">
<ng-container *ngFor="let validation of validations.netmask">
<div class="error-message"
*ngIf="ap_form.controls.ip_assignment_type.value === 'static' && ap_form.get('netmask').hasError(validation.type) && (ap_form.get('netmask').dirty || ap_form.get('netmask').touched)">
<ion-icon name="information-circle-outline"></ion-icon>
<span>{{ validation.message }}</span>
</div>
</ng-container>
</div>
<ion-item class="input-item">
<ion-label position="floating">Gateway</ion-label>
<ion-input formControlName="gateway" placeholder="192.168.1.1" clearInput required>
</ion-input>
</ion-item>
<div class="error-container">
<ng-container *ngFor="let validation of validations.gateway">
<div class="error-message"
*ngIf="ap_form.controls.ip_assignment_type.value === 'static' && ap_form.get('gateway').hasError(validation.type) && (ap_form.get('gateway').dirty || ap_form.get('gateway').touched)">
<ion-icon name="information-circle-outline"></ion-icon>
<span>{{ validation.message }}</span>
</div>
</ng-container>
</div>
<ion-item class="input-item">
<ion-label position="floating">Primary DNS</ion-label>
<ion-input formControlName="primary_dns" placeholder="8.8.8.8" clearInput required>
</ion-input>
</ion-item>
<div class="error-container">
<ng-container *ngFor="let validation of validations.primary_dns">
<div class="error-message"
*ngIf="ap_form.controls.ip_assignment_type.value === 'static' && ap_form.get('primary_dns').hasError(validation.type) && (ap_form.get('primary_dns').dirty || ap_form.get('primary_dns').touched)">
<ion-icon name="information-circle-outline"></ion-icon>
<span>{{ validation.message }}</span>
</div>
</ng-container>
</div>
<ion-item class="input-item">
<ion-label position="floating">Secondary DNS</ion-label>
<ion-input formControlName="secondary_dns" placeholder="8.8.4.4" clearInput required>
</ion-input>
</ion-item>
<div class="error-container">
<ng-container *ngFor="let validation of validations.secondary_dns">
<div class="error-message"
*ngIf="ap_form.controls.ip_assignment_type.value === 'static' && ap_form.get('secondary_dns').hasError(validation.type) && (ap_form.get('secondary_dns').dirty || ap_form.get('secondary_dns').touched)">
<ion-icon name="information-circle-outline"></ion-icon>
<span>{{ validation.message }}</span>
</div>
</ng-container>
</div>
</div>
</div>
<ion-item class="input-item">
<ion-button class="submit-btn" type="submit" expand="block" fill="outline"
[disabled]="!ap_form.valid || !this.selected_ssid">Save
</ion-button>
</ion-item>
</ion-list>
</form>
</ion-content>