feature: Swapping to use nmcli (#1015)

The reason is that we get better errors and that we get signal strength.
Reworking all the commands to use nmcli instead.
Feat: Wifi List Available
Feat: strength sort for available
fix: Backend to match the frontend asking
feat: New get with all information
chore: Make backend changing country not for NonWire

Co-authored-by: Drew Ansbacher <drew.ansbacher@spiredigital.com>

Co-authored-by: Drew Ansbacher <drew.ansbacher@spiredigital.com>
This commit is contained in:
J M
2022-01-10 12:38:49 -07:00
committed by Aiden McClelland
parent 689b449d7a
commit 1086ce13d2
12 changed files with 795 additions and 412 deletions

270
ui/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -4,6 +4,12 @@
<ion-back-button defaultHref="embassy"></ion-back-button>
</ion-buttons>
<ion-title>WiFi Settings</ion-title>
<ion-buttons slot="end">
<ion-button (click)="getWifi()">
Refresh
<ion-icon slot="end" name="refresh"></ion-icon>
</ion-button>
</ion-buttons>
</ion-toolbar>
</ion-header>
@@ -40,25 +46,43 @@
<ion-skeleton-text animated style="width: 18%;"></ion-skeleton-text>
</ion-label>
</ion-item>
<ion-item-divider>Available Networks</ion-item-divider>
<ion-item *ngFor="let entry of ['', '']" class="skeleton-parts">
<ion-button slot="start" fill="clear">
<ion-skeleton-text animated style="width: 30px; height: 30px; border-radius: 0;"></ion-skeleton-text>
</ion-button>
<ion-label>
<ion-skeleton-text animated style="width: 18%;"></ion-skeleton-text>
</ion-label>
</ion-item>
</ng-container>
<!-- not loading -->
<ng-container *ngIf="!loading && wifi.country">
<ion-item-divider>Saved Networks</ion-item-divider>
<ion-item button detail="false" *ngFor="let ssid of wifi.ssids | keyvalue" (click)="presentAction(ssid.key)">
<div *ngIf="ssid.key !== wifi.connected" slot="start" style="padding-right: 32px;"></div>
<ion-icon *ngIf="ssid.key === wifi.connected" slot="start" size="large" name="checkmark" color="success"></ion-icon>
<ion-label>{{ ssid.key }}</ion-label>
<img *ngIf="ssid.value > 0 && ssid.value < 5" slot="end" src="assets/img/icons/wifi-1.png" style="max-width: 32px;" />
<img *ngIf="ssid.value >= 5 && ssid.value < 50" slot="end" src="assets/img/icons/wifi-1.png" style="max-width: 32px;" />
<img *ngIf="ssid.value >= 50 && ssid.value < 90" slot="end" src="assets/img/icons/wifi-2.png" style="max-width: 32px;" />
<img *ngIf="ssid.value >= 90" slot="end" src="assets/img/icons/wifi-3.png" style="max-width: 32px;" />
</ion-item>
<ion-item-divider>Available Networks</ion-item-divider>
<ion-item button detail="false" *ngFor="let avWifi of wifi['available-wifi']" (click)="presentModalAdd(avWifi.ssid, !!avWifi.security.length)">
<ion-icon slot="start" name="add" size="large"></ion-icon>
<ion-label>{{ avWifi.ssid }}</ion-label>
<img *ngIf="avWifi.strength < 5" slot="end" src="assets/img/icons/wifi-1.png" style="max-width: 32px;" />
<img *ngIf="avWifi.strength >= 5 && avWifi.strength < 50" slot="end" src="assets/img/icons/wifi-1.png" style="max-width: 32px;" />
<img *ngIf="avWifi.strength >= 50 && avWifi.strength < 90" slot="end" src="assets/img/icons/wifi-2.png" style="max-width: 32px;" />
<img *ngIf="avWifi.strength >= 90" slot="end" src="assets/img/icons/wifi-3.png" style="max-width: 32px;" />
</ion-item>
<ion-item button detail="false" (click)="presentModalAdd()">
<ion-icon slot="start" name="add" size="large"></ion-icon>
<ion-label>Add new network</ion-label>
</ion-item>
<ion-item button detail="false" *ngFor="let ssid of wifi.ssids; let i = index;" (click)="presentAction(ssid, i)">
<div *ngIf="ssid !== wifi.connected" slot="start" style="padding-right: 32px;"></div>
<ion-icon *ngIf="ssid === wifi.connected" slot="start" size="large" name="checkmark" color="success"></ion-icon>
<ion-label>{{ ssid }}</ion-label>
<ng-container *ngIf="ssid === wifi.connected && wifi['signal-strength'] as strength">
<img *ngIf="strength < 5" slot="end" src="assets/img/icons/wifi-1.png" style="max-width: 32px;" />
<img *ngIf="strength >= 5 && strength < 50" slot="end" src="assets/img/icons/wifi-1.png" style="max-width: 32px;" />
<img *ngIf="strength >= 50 && strength < 90" slot="end" src="assets/img/icons/wifi-2.png" style="max-width: 32px;" />
<img *ngIf="strength >= 90" slot="end" src="assets/img/icons/wifi-3.png" style="max-width: 32px;" />
</ng-container>
<ion-label>Other</ion-label>
</ion-item>
</ng-container>
</ion-item-group>

View File

@@ -8,6 +8,7 @@ import { ValueSpecObject } from 'src/app/pkg-config/config-types'
import { RR } from 'src/app/services/api/api.types'
import { pauseFor } from 'src/app/util/misc.util'
import { GenericFormPage } from 'src/app/modals/generic-form/generic-form.page'
import { ConfigService } from 'src/app/services/config.service'
@Component({
selector: 'wifi',
@@ -27,6 +28,7 @@ export class WifiPage {
private readonly modalCtrl: ModalController,
private readonly errToast: ErrorToastService,
private readonly actionCtrl: ActionSheetController,
private readonly config: ConfigService,
) { }
async ngOnInit () {
@@ -47,6 +49,23 @@ export class WifiPage {
}
async presentAlertCountry (): Promise<void> {
if (!this.config.isLan) {
const alert = await this.alertCtrl.create({
header: 'Cannot Complete Action',
message: 'You must be connected to your Emassy via LAN to change the country.',
buttons: [
{
text: 'OK',
role: 'cancel',
},
],
cssClass: 'wide-alert enter-click',
})
await alert.present()
return
}
const inputs: AlertInput[] = Object.entries(this.countries).map(([country, fullName]) => {
return {
name: fullName,
@@ -59,6 +78,7 @@ export class WifiPage {
const alert = await this.alertCtrl.create({
header: 'Select Country',
message: 'Warning: Changing the country will delete all saved networks from the Embassy.',
inputs,
buttons: [
{
@@ -77,7 +97,8 @@ export class WifiPage {
await alert.present()
}
async presentModalAdd () {
async presentModalAdd (ssid?: string, needsPW: boolean = true) {
const wifiSpec = getWifiValueSpec(ssid, needsPW)
const modal = await this.modalCtrl.create({
component: GenericFormPage,
componentProps: {
@@ -104,14 +125,14 @@ export class WifiPage {
await modal.present()
}
async presentAction (ssid: string, i: number) {
async presentAction (ssid: string) {
const buttons: ActionSheetButton[] = [
{
text: 'Forget',
icon: 'trash',
role: 'destructive',
handler: () => {
this.delete(ssid, i)
this.delete(ssid)
},
},
]
@@ -147,6 +168,7 @@ export class WifiPage {
try {
await this.api.setWifiCountry({ country })
await this.getWifi(4000)
this.wifi.country = country
} catch (e) {
this.errToast.present(e)
@@ -164,7 +186,7 @@ export class WifiPage {
if (attempts > maxAttempts) {
this.presentToastFail()
if (deleteOnFailure) {
this.wifi.ssids = this.wifi.ssids.filter(s => s !== ssid)
delete this.wifi.ssids[ssid]
}
break
}
@@ -243,7 +265,7 @@ export class WifiPage {
}
}
private async delete (ssid: string, i: number): Promise<void> {
private async delete (ssid: string): Promise<void> {
const loader = await this.loadingCtrl.create({
spinner: 'lines',
message: 'Deleting...',
@@ -253,7 +275,8 @@ export class WifiPage {
try {
await this.api.deleteWifi({ ssid })
this.wifi.ssids = this.wifi.ssids.filter((w, index) => index !== i)
await this.getWifi(4000)
delete this.wifi.ssids[ssid]
} catch (e) {
this.errToast.present(e)
} finally {
@@ -276,7 +299,7 @@ export class WifiPage {
priority: 0,
connect: false,
})
await this.getWifi()
await this.getWifi(4000)
} catch (e) {
this.errToast.present(e)
} finally {
@@ -310,25 +333,29 @@ export class WifiPage {
}
}
const wifiSpec: ValueSpecObject = {
type: 'object',
name: 'WiFi Credentials',
description: 'Enter the network SSID and password. You can connect now or save the network for later.',
'unique-by': null,
spec: {
ssid: {
type: 'string',
name: 'Network SSID',
nullable: false,
masked: false,
copyable: false,
function getWifiValueSpec (ssid?: string, needsPW: boolean = true): ValueSpecObject {
return {
type: 'object',
name: 'WiFi Credentials',
description: 'Enter the network SSID and password. You can connect now or save the network for later.',
'unique-by': null,
spec: {
ssid: {
type: 'string',
name: 'Network SSID',
nullable: false,
masked: false,
copyable: false,
default: ssid,
},
password: {
type: 'string',
name: 'Password',
nullable: !needsPW,
masked: true,
copyable: false,
},
},
password: {
type: 'string',
name: 'Password',
nullable: false,
masked: true,
copyable: false,
},
},
}
}

View File

@@ -983,10 +983,24 @@ export module Mock {
export const Wifi: RR.GetWifiRes = {
ethernet: true,
ssids: ['Goosers', 'Goosers5G'],
ssids: {
'Goosers': 50,
'Goosers5G': 0,
},
connected: 'Goosers',
country: 'US',
'signal-strength': 50,
'available-wifi': [
{
ssid: 'Goosers a billion',
strength: 40,
security: [],
},
{
ssid: 'Bill nye the wifi guy',
strength: 99,
security: ['1', '2', '3'],
},
],
}
export const BackupTargets: RR.GetBackupTargetsRes = {

View File

@@ -83,13 +83,15 @@ export module RR {
export type SetWifiCountryRes = null
export type GetWifiReq = { }
export type GetWifiRes = { // wifi.get
ethernet: boolean
ssids: string[]
connected: string | null
country: string | null
'signal-strength': number
}
export type GetWifiRes = {
ssids: {
[ssid: string]: number
},
connected?: string,
country: string,
ethernet: boolean,
'available-wifi': AvailableWifi[]
}
export type AddWifiReq = { // wifi.add
ssid: string
@@ -442,3 +444,9 @@ export interface BackupReport {
}
}
}
export interface AvailableWifi {
ssid: string
strength: number
security: string []
}