Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions src/app/components/client-info/client-info.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,17 @@ <h3>Status: Inactive</h3>
<ion-spinner *ngIf="isLoading['enableSnapcast']" name="crescent" class="ion-margin-end"></ion-spinner>
Enable Snapcast Server
</ion-button>
<ion-button *ngIf="snapcastServerStatus as status" expand="full" color="warning" (click)="restartSnapcastServer()"
[disabled]="isLoading['restartSnapcast'] || !status.active">
<ion-spinner *ngIf="isLoading['restartSnapcast']" name="crescent" class="ion-margin-end"></ion-spinner>
Restart Snapcast Server
</ion-button>

<!-- restart client -->
<ion-button expand="full" color="warning" (click)="restartSnapcastClient()" [disabled]="isLoading['restartSnapcastClient']">
<ion-spinner *ngIf="isLoading['restartSnapcastClient']" name="crescent" class="ion-margin-end"></ion-spinner>
Restart Client
</ion-button>

<ion-button expand="full" color="primary" (click)="chooseSpeakers()">
Choose Speakers
Expand Down
38 changes: 38 additions & 0 deletions src/app/components/client-info/client-info.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,44 @@ export class ClientInfoComponent implements OnInit {
}
});
}

async restartSnapcastServer() {
if (!this.client) return;
this.isLoading['restartSnapcast'] = true;
const localHostName = await this.getUrl();
this.beatnikSnapcastService.restartServer(localHostName).subscribe({
next: (response) => {
console.log('Successfully restarted Snapcast server:', response);
this.presentToast('Snapcast Server Restarted', 'success');
this.refreshSnapcastStatus();
this.isLoading['restartSnapcast'] = false;
},
error: (err) => {
console.error('Failed to restart Snapcast server:', err);
this.presentToast('Failed to Restart Snapcast Server', 'danger');
this.isLoading['restartSnapcast'] = false;
}
});
}

async restartSnapcastClient() {
if (!this.client) return;
this.isLoading['restartSnapcastClient'] = true;
const localHostName = await this.getUrl();
this.beatnikSnapcastService.restartClient(localHostName).subscribe({
next: (response) => {
console.log('Successfully restarted Snapcast client:', response);
this.presentToast('Snapcast Client Restarted', 'success');
this.refreshSnapcastStatus();
this.isLoading['restartSnapcastClient'] = false;
},
error: (err) => {
console.error('Failed to restart Snapcast client:', err);
this.presentToast('Failed to Restart Snapcast Client', 'danger');
this.isLoading['restartSnapcastClient'] = false;
}
});
}
}


Expand Down
38 changes: 34 additions & 4 deletions src/app/components/snapcast-status/snapcast-status.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -59,11 +59,41 @@ <h3>Streams</h3>
<pre>{{ state | json }}</pre>
</div>
</ion-accordion>
<!-- reboot server and all clients -->

<ion-accordion value="restart">
<ion-item slot="header" color="light">
<ion-label>Restart Snapserver and Snapclients</ion-label>
</ion-item>
<div class="ion-padding" slot="content" style="overflow-x: auto;">
<p>Restart the Snapserver and Snapclient services. This will not reboot your device.</p>
<!-- restarting services -->
<ion-button color="primary" expand="block" (click)="showRestartSnapserverAlert()">Restart
Snapserver</ion-button>
<ion-button color="primary" expand="block" (click)="showRestartSnapclientAlert()">Restart All
Snapclients</ion-button>
<ion-button color="primary" expand="block" (click)="showRestartSnapserverAndClientsAlert()">Restart Snapserver
and
All Snapclients</ion-button>
</div>
</ion-accordion>
<ion-accordion value="reboot">
<ion-item slot="header" color="light">
<ion-label>Reboot Server and Clients</ion-label>
</ion-item>
<div class="ion-padding" slot="content" style="overflow-x: auto;">
<p>Physically reboot your devices. Devices will rebbot and restart all Services.</p>
<ion-button color="danger" expand="block" (click)="showRebootServerAlert()">Reboot Server</ion-button>
<ion-button color="danger" expand="block" (click)="showRebootClientsAlert()">Reboot All Clients</ion-button>
<ion-button color="danger" expand="block" (click)="showRebootServerAndClientsAlert()">Reboot Server and All
Clients</ion-button>
</div>
</ion-accordion>



</ion-accordion-group>
<!-- reboot server and all clients -->
<ion-button color="primary" expand="block" (click)="showRebootServerAlert()">Reboot Server</ion-button>
<ion-button color="primary" expand="block" (click)="showRebootClientsAlert()">Reboot All Clients</ion-button>
<ion-button color="primary" expand="block" (click)="showRebootServerAndClientsAlert()">Reboot Server and All Clients</ion-button>

</ng-container>

<ng-template #loadingOrError>
Expand Down
122 changes: 122 additions & 0 deletions src/app/components/snapcast-status/snapcast-status.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { BeatnikHardwareService } from 'src/app/services/beatnik-hardware.servic
import { UserPreference } from 'src/app/enum/user-preference.enum';
import { Preferences } from '@capacitor/preferences';
import { AlertController, ToastController } from '@ionic/angular';
import { BeatnikSnapcastService } from 'src/app/services/beatnik-snapcast.service';

@Component({
selector: 'app-snapcast-status',
Expand All @@ -26,6 +27,7 @@ export class SnapcastStatusComponent implements OnInit, OnDestroy {
constructor(
public snapcastService: SnapcastService,
private beatnikHardwareService: BeatnikHardwareService,
private beatnikSnapcastService: BeatnikSnapcastService,
private toastController: ToastController,
private alertController: AlertController) {

Expand Down Expand Up @@ -264,6 +266,126 @@ export class SnapcastStatusComponent implements OnInit, OnDestroy {
}).then(alert => alert.present());
}

async showRestartSnapserverAndClientsAlert(): Promise<void> {
const alert = await this.alertController.create({
header: 'Confirm Restart',
message: 'Are you sure you want to restart the Snapserver and all Snapclients? This will cause temporary disruption of audio playback.',
buttons: [
{
text: 'Cancel',
role: 'cancel'
},
{
text: 'Restart',
handler: () => {
this.restartSnapserverAndClients();
}
}
]
});

await alert.present();
}

async restartSnapserverAndClients(): Promise<void> {
await this.restartSnapserver();
// wait 5 seconds before restarting clients to give the server time to come back online
await new Promise(resolve => setTimeout(resolve, 5000));
await this.restartSnapclients();
}

async restartSnapserver(): Promise<void> {
const serverIp = await Preferences.get({ key: UserPreference.SERVER_URL }).then((result) => result.value);
if (!serverIp) {
console.error('No server IP found in user preferences.');
return;
}

this.isLoading['restartServer'] = true;
this.beatnikSnapcastService.restartServer(serverIp).subscribe({
next: () => {
console.log(`Successfully restarted Snapserver on server at IP ${serverIp}`);
this.isLoading['restartServer'] = false;
this.presentToast('Snapserver Restart Initiated', 'success');
},
error: (err) => {
console.error(`Failed to restart Snapserver on server at IP ${serverIp}`, err);
this.isLoading['restartServer'] = false;
this.presentToast('Failed to Restart Snapserver', 'danger');
}
});
}

async restartSnapclients(): Promise<void> {
const state = await firstValueFrom(this.displayState!);
if (!state) return;

const clientIps = state.server.groups.flatMap(group => group.clients?.map(client => client.host.ip) || []);
for (const ip of clientIps) {
const formattedIp = await this.getUrl(ip);

try {
// if ip adress is server ip from user preferences, skip restarting since it will be handled in restartSnapserver
const serverIp = await Preferences.get({ key: UserPreference.SERVER_URL }).then(result => result.value);
if (formattedIp === serverIp) {
console.log(`Skipping restart for client ${formattedIp} since it matches the server IP`);
continue;
}

this.isLoading[`restartClient-${formattedIp}`] = true;
await firstValueFrom(this.beatnikSnapcastService.restartClient(formattedIp));
console.log(`Successfully restarted Snapclient on client ${formattedIp}`);
this.isLoading[`restartClient-${formattedIp}`] = false;
this.presentToast(`Snapclient Restart Initiated for ${formattedIp}`, 'success');
} catch (err) {
console.error(`Failed to restart Snapclient on client ${formattedIp}`, err);
this.isLoading[`restartClient-${formattedIp}`] = false;
this.presentToast(`Failed to Restart Snapclient for ${formattedIp}`, 'danger');
}
}
}

showRestartSnapserverAlert(): void {
this.alertController.create({
header: 'Confirm Restart',
message: 'Are you sure you want to restart the Snapserver? This will cause temporary disruption of audio playback.',
buttons: [
{
text: 'Cancel',
role: 'cancel'
},
{
text: 'Restart',
handler: () => {
this.restartSnapserver();
}
}
]
}).then(alert => alert.present());
}

showRestartSnapclientAlert(): void {
this.alertController.create({
header: 'Confirm Restart',
message: 'Are you sure you want to restart all Snapclients? This will cause temporary disruption of audio playback on all clients.',
buttons: [
{
text: 'Cancel',
role: 'cancel'
},
{
text: 'Restart',
handler: () => {
this.restartSnapclients();
}
}
]
}).then(alert => alert.present());
}




ngOnDestroy(): void {
this.subscriptions.unsubscribe();
}
Expand Down
35 changes: 29 additions & 6 deletions src/app/pages/setup/ble-wifi-setup/ble-wifi-setup.page.html
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
<ion-header [translucent]="true">
<ion-toolbar>
<ion-toolbar class="ion-no-border">
<ion-buttons slot="start">
<ion-button (click)="handleBack()">
<ion-icon slot="start" name="chevron-back"></ion-icon>
Back
</ion-button>
</ion-buttons>
<ion-title>ble-wifi-setup</ion-title>
<ion-title>Network Configuration</ion-title>
</ion-toolbar>
</ion-header>

Expand All @@ -28,11 +28,34 @@
<div class="ion-margin">
<ion-text color="medium">
<p>
Plug in your Beatnik. Make sure you're connected to your home Wi-Fi. Hold the setup button for 5 seconds until the LED pulses blue. Then tap "Scan
Plug in your Beatnik. Make sure you're connected to your home Wi-Fi. Hold the setup button for 5 seconds
until the LED pulses blue. Then tap "Scan
for
Devices".
</p>
<p *ngIf="smartphoneNetworkStatus">Your smartphone is currently {{ smartphoneNetworkStatus.connected ? 'connected' : 'disconnected' }} to {{smartphoneNetworkStatus.connectionType }}</p>
<ng-container *ngIf="smartphoneNetworkStatus">
<ng-switch [ngSwitch]="smartphoneNetworkStatus.connectionType">
<ion-item lines="none" *ngSwitchCase="'wifi'">
<ion-icon color="success" slot="start" name="checkmark-circle"></ion-icon>
<ion-label>
<p class="wifinote">
Your smartphone is currently connected to {{smartphoneNetworkStatus.connectionType }}.
</p>
</ion-label>

</ion-item>
<ion-item lines="none" *ngSwitchCase="'cellular'">
<ion-icon color="danger" slot="start" name="alert-circle"></ion-icon>
<ion-label>
<p class="wifinote">
Your smartphone is currently disconnected from Wi-Fi. Please connect to your home Wi-Fi network
before proceeding.
</p>
</ion-label>
</ion-item>
</ng-switch>
</ng-container>

</ion-text>
</div>
<div class="animation-container">
Expand Down Expand Up @@ -197,8 +220,8 @@ <h2 class="ion-margin-horizontal">Connection Status:</h2>

</ion-content>

<ion-footer>
<ion-toolbar color="light" class="ion-no-lines ion-padding-horizontal">
<ion-footer class="ion-no-border">
<ion-toolbar color="light" class="ion-no-border ion-padding-horizontal">
<div [ngSwitch]="slideIndex">
<div *ngSwitchCase="0">
<div>
Expand Down
7 changes: 7 additions & 0 deletions src/app/pages/setup/ble-wifi-setup/ble-wifi-setup.page.scss
Original file line number Diff line number Diff line change
Expand Up @@ -90,3 +90,10 @@
margin-bottom: 16px;
text-align: center;
}

.wifinote {
margin-top: 16px;
margin-bottom: 16px;
// text-align: center;
// font-size: 14px;
}
Loading
Loading