From 89245284cd7665d6ccb89fd2c6b730f7349886c9 Mon Sep 17 00:00:00 2001 From: idrimi Date: Wed, 22 Jul 2026 13:43:43 +0200 Subject: [PATCH 1/3] fix issues durring setup --- .../ble-wifi-setup/ble-wifi-setup.page.html | 35 ++++++++++++++--- .../ble-wifi-setup/ble-wifi-setup.page.scss | 7 ++++ .../setup/setup-server/setup-server.page.ts | 39 +++++++++++++++++-- .../setup-soundcard/setup-soundcard.page.html | 2 +- 4 files changed, 73 insertions(+), 10 deletions(-) diff --git a/src/app/pages/setup/ble-wifi-setup/ble-wifi-setup.page.html b/src/app/pages/setup/ble-wifi-setup/ble-wifi-setup.page.html index fc20a6b..79efefb 100644 --- a/src/app/pages/setup/ble-wifi-setup/ble-wifi-setup.page.html +++ b/src/app/pages/setup/ble-wifi-setup/ble-wifi-setup.page.html @@ -1,12 +1,12 @@ - + Back - ble-wifi-setup + Network Configuration @@ -28,11 +28,34 @@

- 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".

-

Your smartphone is currently {{ smartphoneNetworkStatus.connected ? 'connected' : 'disconnected' }} to {{smartphoneNetworkStatus.connectionType }}

+ + + + + +

+ Your smartphone is currently connected to {{smartphoneNetworkStatus.connectionType }}. +

+
+ +
+ + + +

+ Your smartphone is currently disconnected from Wi-Fi. Please connect to your home Wi-Fi network + before proceeding. +

+
+
+
+
+
@@ -197,8 +220,8 @@

Connection Status:

- - + +
diff --git a/src/app/pages/setup/ble-wifi-setup/ble-wifi-setup.page.scss b/src/app/pages/setup/ble-wifi-setup/ble-wifi-setup.page.scss index 0822df5..e4d7974 100644 --- a/src/app/pages/setup/ble-wifi-setup/ble-wifi-setup.page.scss +++ b/src/app/pages/setup/ble-wifi-setup/ble-wifi-setup.page.scss @@ -90,3 +90,10 @@ margin-bottom: 16px; text-align: center; } + +.wifinote { + margin-top: 16px; + margin-bottom: 16px; + // text-align: center; + // font-size: 14px; +} diff --git a/src/app/pages/setup/setup-server/setup-server.page.ts b/src/app/pages/setup/setup-server/setup-server.page.ts index 63049f7..55e1c5b 100644 --- a/src/app/pages/setup/setup-server/setup-server.page.ts +++ b/src/app/pages/setup/setup-server/setup-server.page.ts @@ -6,7 +6,7 @@ import { SnapcastService } from 'src/app/services/snapcast.service'; import { ServerDetail, SnapCastServerStatusResponse } from 'src/app/model/snapcast.model'; import { ActivatedRoute, Router } from '@angular/router'; import Swiper, { SwiperOptions } from 'swiper'; -import { AlertController, NavController } from '@ionic/angular'; +import { AlertController, LoadingController, NavController } from '@ionic/angular'; import { BeatnikHardwareService, HardwareStatus } from 'src/app/services/beatnik-hardware.service'; import { BeatnikHardware } from 'src/app/model/beatnik-hardware.model'; import { SUPPORTED_HATS } from 'src/app/constant/hat.constant'; @@ -36,7 +36,7 @@ export class SetupServerPage implements OnInit { ip: string | null = null; userPreferenceServerAdress: string = ''; - isFirstDevice: boolean = true; + isFirstDevice: boolean = false; swiperConfig: SwiperOptions = { slidesPerView: 1, @@ -52,6 +52,8 @@ export class SetupServerPage implements OnInit { hats = Object.values(SUPPORTED_HATS); manualHatId: string = ''; + loadingDisplay: HTMLIonLoadingElement | null = null; + constructor( private zeroconf: ZeroconfService, private snapcastService: SnapcastService, @@ -60,7 +62,8 @@ export class SetupServerPage implements OnInit { private beatnikHardwareService: BeatnikHardwareService, private beatnikSnapcastService: BeatnikSnapcastService, private alertController: AlertController, - private router: Router + private router: Router, + private loadingController: LoadingController ) { this.services$ = this.zeroconf.services$; } @@ -235,6 +238,13 @@ export class SetupServerPage implements OnInit { } async setupAsSnapcastServer(): Promise { + this.showLoadingSpinner('Setting up this device as Snapcast server...'); + if (!this.ip) { + console.error('No IP address provided for Snapcast server.'); + this.loadingDisplay?.dismiss(); + return; + } + console.log('Setting up this device as Snapcast server...'); this.beatnikSnapcastService.enable(this.ip || '').subscribe({ next: (response) => { @@ -242,10 +252,12 @@ export class SetupServerPage implements OnInit { console.log('Successfully enabled Snapserver on server at IP', this.ip, response); this.connectToSnapcast(this.selectedService); this.navToSetupSoundcard(); + this.loadingDisplay?.dismiss(); }, error: (err) => { console.error('Failed to enable Snapserver on server at IP', this.ip, err); + this.loadingDisplay?.dismiss(); } }); } @@ -260,19 +272,28 @@ export class SetupServerPage implements OnInit { } async setupAsSecondaryServer(): Promise { + this.showLoadingSpinner('Setting up this device as secondary Snapcast server...'); + if (!this.ip) { + console.error('No IP address provided for Snapcast server.'); + this.loadingDisplay?.dismiss(); + return; + } console.log('Setting up this device as secondary Snapcast server...'); this.beatnikSnapcastService.enable(this.ip || '').subscribe({ next: (response) => { console.log('Successfully enabled Snapserver on server at IP', this.ip, response); this.slideTo(2); + this.loadingDisplay?.dismiss(); }, error: (err) => { console.error('Failed to enable Snapserver on server at IP', this.ip, err); + this.loadingDisplay?.dismiss(); } }); } async setupAsSnapcastClient(): Promise { + this.showLoadingSpinner('Setting up this device as Snapcast client...'); if (!this.ip) { console.error('No IP address provided for Snapcast server.'); return; @@ -282,9 +303,11 @@ export class SetupServerPage implements OnInit { next: (response) => { console.log('Successfully disabled Snapserver on server at IP', this.ip, response); this.navToSetupSoundcard(); + this.loadingDisplay?.dismiss(); }, error: (err) => { console.error('Failed to disable Snapserver on server at IP', this.ip, err); + this.loadingDisplay?.dismiss(); } }); } @@ -359,5 +382,15 @@ export class SetupServerPage implements OnInit { this.router.navigateByUrl(`/setup-soundcard/${this.ip}`); } + async showLoadingSpinner(text: string) { + + this.loadingDisplay = await this.loadingController.create({ + message: text, + spinner: 'dots', + backdropDismiss: false, + }); + await this.loadingDisplay.present(); + } + } diff --git a/src/app/pages/setup/setup-soundcard/setup-soundcard.page.html b/src/app/pages/setup/setup-soundcard/setup-soundcard.page.html index b427d90..f8ac5e7 100644 --- a/src/app/pages/setup/setup-soundcard/setup-soundcard.page.html +++ b/src/app/pages/setup/setup-soundcard/setup-soundcard.page.html @@ -62,7 +62,7 @@

Rebooting client... Please wait.

- + Continue From 787d7f41dbe3d4941ef5180ed0ae92d93850a882 Mon Sep 17 00:00:00 2001 From: idrimi Date: Wed, 22 Jul 2026 16:10:11 +0200 Subject: [PATCH 2/3] rebooting and restaring funcs --- .../client-info/client-info.component.html | 11 ++ .../client-info/client-info.component.ts | 38 ++++++ .../snapcast-status.component.html | 38 +++++- .../snapcast-status.component.ts | 122 ++++++++++++++++++ src/app/services/beatnik-snapcast.service.ts | 14 ++ 5 files changed, 219 insertions(+), 4 deletions(-) diff --git a/src/app/components/client-info/client-info.component.html b/src/app/components/client-info/client-info.component.html index 9044bfa..093aad4 100644 --- a/src/app/components/client-info/client-info.component.html +++ b/src/app/components/client-info/client-info.component.html @@ -201,6 +201,17 @@

Status: Inactive

Enable Snapcast Server
+ + + Restart Snapcast Server + + + + + + Restart Client + Choose Speakers diff --git a/src/app/components/client-info/client-info.component.ts b/src/app/components/client-info/client-info.component.ts index e233413..af47254 100644 --- a/src/app/components/client-info/client-info.component.ts +++ b/src/app/components/client-info/client-info.component.ts @@ -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; + } + }); + } } diff --git a/src/app/components/snapcast-status/snapcast-status.component.html b/src/app/components/snapcast-status/snapcast-status.component.html index bff3846..b516888 100644 --- a/src/app/components/snapcast-status/snapcast-status.component.html +++ b/src/app/components/snapcast-status/snapcast-status.component.html @@ -59,11 +59,41 @@

Streams

{{ state | json }}
+ + + + + Restart Snapserver and Snapclients + +
+

Restart the Snapserver and Snapclient services. This will not reboot your device.

+ + Restart + Snapserver + Restart All + Snapclients + Restart Snapserver + and + All Snapclients +
+
+ + + Reboot Server and Clients + +
+

Physically reboot your devices. Devices will rebbot and restart all Services.

+ Reboot Server + Reboot All Clients + Reboot Server and All + Clients +
+
+ + + - - Reboot Server - Reboot All Clients - Reboot Server and All Clients + diff --git a/src/app/components/snapcast-status/snapcast-status.component.ts b/src/app/components/snapcast-status/snapcast-status.component.ts index bdcc734..131905b 100644 --- a/src/app/components/snapcast-status/snapcast-status.component.ts +++ b/src/app/components/snapcast-status/snapcast-status.component.ts @@ -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', @@ -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) { @@ -264,6 +266,126 @@ export class SnapcastStatusComponent implements OnInit, OnDestroy { }).then(alert => alert.present()); } + async showRestartSnapserverAndClientsAlert(): Promise { + 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 { + 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 { + 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 { + 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(); } diff --git a/src/app/services/beatnik-snapcast.service.ts b/src/app/services/beatnik-snapcast.service.ts index 9e9f86e..f62df23 100644 --- a/src/app/services/beatnik-snapcast.service.ts +++ b/src/app/services/beatnik-snapcast.service.ts @@ -43,4 +43,18 @@ export class BeatnikSnapcastService { disable(host: string): Observable { return this.http.post(`${this.getApiUrl(host)}/disable`, {}); } + + /** + * Restart Snapserver + */ + restartServer(host: string): Observable<{ success: boolean; message: string }> { + return this.http.post<{ success: boolean; message: string }>(`${this.getApiUrl(host)}/restart-server`, {}); + } + + /** + * Restart Snapclient + */ + restartClient(host: string): Observable<{ success: boolean; message: string }> { + return this.http.post<{ success: boolean; message: string }>(`${this.getApiUrl(host)}/restart-client`, {}); + } } From 36c3b7d7942062bd4c4c9e065e0339f75c03c4ce Mon Sep 17 00:00:00 2001 From: idrimi Date: Wed, 22 Jul 2026 16:31:32 +0200 Subject: [PATCH 3/3] split lists --- src/app/pages/zeroconf/zeroconf.page.html | 49 +++++++++++++++-------- 1 file changed, 33 insertions(+), 16 deletions(-) diff --git a/src/app/pages/zeroconf/zeroconf.page.html b/src/app/pages/zeroconf/zeroconf.page.html index 9894278..e649dbd 100644 --- a/src/app/pages/zeroconf/zeroconf.page.html +++ b/src/app/pages/zeroconf/zeroconf.page.html @@ -45,28 +45,45 @@ Searching for services... - - -

{{ service.hostname }}

-

{{ service.name }}

-

{{ service.type }}

-

{{ service.ipv4Addresses[0] }}:{{ service.port }}

- + + Servers + + + + +

{{ service.hostname }}

+

{{ service.name }}

+

{{ service.type }}

+

{{ service.ipv4Addresses[0] }}:{{ service.port }}

+
+ + Set as Server + +
+
+ + + + Clients + + + + +

{{ service.hostname }}

+

{{ service.name }}

+

{{ service.type }}

+

{{ service.ipv4Addresses[0] }}:{{ service.port }}

Beatnik Device - MAC: {{ service.txtRecord['mac'] }}

Beatnik Pi: {{ service.txtRecord['model'] }}

Beatnik Version: {{ service.txtRecord['version'] }}

RAM: {{ service.txtRecord['ram'] }}

-
-
- - + + + View Device - - - Set as Server - -
+ +