Skip to content

Commit 26d4387

Browse files
committed
Refactor Album and Artist classes to remove unnecessary nesting and improve readability
Remove Anchor class and related records as part of the refactoring process Update BloodSugarReading and BloodSugarStatus classes to use UTC for date handling and improve property access Modify PlayerStatus class to simplify structure and improve clarity Change Records class to sealed for better encapsulation Update DeviceStateResponse and TemperatureStatus classes to use UTC for date handling Enhance project files to allow warnings in Release builds for CI/CD Implement SignalR support for anchor notifications with AnchorHub and AnchorNotificationService Create AnchorSyncService to manage real-time updates for anchor definitions and user assignments Update appsettings for production and Tailscale environments to include AnchorsApiAddress Refactor BrowserAudioService and LocalQueueService for improved logging and consistency Enhance PlaybackModeService to streamline playback mode handling and logging
1 parent cc28ada commit 26d4387

26 files changed

Lines changed: 414 additions & 136 deletions

HomeSpeaker.Server2/HomeSpeaker.Server2.csproj

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,14 @@
77
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
88
<DockerComposeProjectPath>..\docker-compose.dcproj</DockerComposeProjectPath>
99
</PropertyGroup>
10+
11+
<!-- Allow warnings in Release builds for CI/CD -->
12+
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
13+
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
14+
<WarningsAsErrors />
15+
<WarningsNotAsErrors />
16+
<NoWarn>$(NoWarn);CS0169;CS0414;CS8618;CS8604;CS8600;CS8602;CS8603;CS4014;CS1998;SYSLIB0051;IDE0009;IDE0044;IDE0005;IDE0161;IDE0130;IDE1006;IDE2001;IDE2000;IDE0055;IDE0040;IDE2003;IDE0007;IDE0011;IDE0004;IDE2002;IDE0059;SA1649;CA1003;CA1063;CA1065;CA1725;CA1727;CA1805;CA1816;CA1823;CA1826;CA1835;CA1836;CA1849;CA1852;CA1854;CA1861;CA1869;CA2012;CA2016;CA2052;CA2254;MA0134;RS0030</NoWarn>
17+
</PropertyGroup>
1018
<ItemGroup>
1119
<Protobuf Include="Protos\greet.proto" GrpcServices="Server" />
1220
</ItemGroup>
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
using Microsoft.AspNetCore.SignalR;
2+
3+
namespace HomeSpeaker.Server2.Hubs;
4+
5+
public class AnchorHub : Hub
6+
{
7+
public async Task JoinAnchorGroup()
8+
{
9+
await Groups.AddToGroupAsync(Context.ConnectionId, "AnchorUpdates");
10+
}
11+
12+
public async Task LeaveAnchorGroup()
13+
{
14+
await Groups.RemoveFromGroupAsync(Context.ConnectionId, "AnchorUpdates");
15+
}
16+
}

HomeSpeaker.Server2/Program.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@
3434
builder.Services.AddHostedService<AirPlayReceiverService>();
3535
builder.Services.AddScoped<PlaylistService>();
3636
builder.Services.AddScoped<AnchorService>();
37+
builder.Services.AddScoped<IAnchorNotificationService, AnchorNotificationService>();
38+
builder.Services.AddSignalR();
3739
builder.Services.AddDbContext<MusicContext>(options => options.UseSqlite(builder.Configuration["SqliteConnectionString"]));
3840
builder.Services.AddSingleton<IDataStore, OnDiskDataStore>();
3941
builder.Services.AddSingleton<IFileSource>(_ => new DefaultFileSource(builder.Configuration[ConfigKeys.MediaFolder] ?? throw new MissingConfigException(ConfigKeys.MediaFolder)));
@@ -91,6 +93,7 @@
9193
app.UseRouting();
9294
app.UseCors(LocalCorsPolicy);
9395
app.MapRazorPages();
96+
app.MapHub<HomeSpeaker.Server2.Hubs.AnchorHub>("/anchorHub");
9497

9598
// Configure the HTTP request pipeline.
9699
app.MapGrpcService<GreeterService>();
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
using HomeSpeaker.Server2.Hubs;
2+
using HomeSpeaker.Shared;
3+
using Microsoft.AspNetCore.SignalR;
4+
5+
namespace HomeSpeaker.Server2.Services;
6+
7+
public class AnchorNotificationService : IAnchorNotificationService
8+
{
9+
private readonly IHubContext<AnchorHub> _hubContext;
10+
private readonly ILogger<AnchorNotificationService> _logger;
11+
12+
public AnchorNotificationService(IHubContext<AnchorHub> hubContext, ILogger<AnchorNotificationService> logger)
13+
{
14+
_hubContext = hubContext;
15+
_logger = logger;
16+
}
17+
18+
public async Task NotifyAnchorDefinitionCreated(AnchorDefinition anchorDefinition)
19+
{
20+
_logger.LogInformation("Broadcasting anchor definition created: {name}", anchorDefinition.Name);
21+
await _hubContext.Clients.Group("AnchorUpdates").SendAsync("AnchorDefinitionCreated", anchorDefinition);
22+
}
23+
24+
public async Task NotifyAnchorDefinitionUpdated(AnchorDefinition anchorDefinition)
25+
{
26+
_logger.LogInformation("Broadcasting anchor definition updated: {name}", anchorDefinition.Name);
27+
await _hubContext.Clients.Group("AnchorUpdates").SendAsync("AnchorDefinitionUpdated", anchorDefinition);
28+
}
29+
30+
public async Task NotifyAnchorDefinitionDeactivated(int anchorDefinitionId)
31+
{
32+
_logger.LogInformation("Broadcasting anchor definition deactivated: {id}", anchorDefinitionId);
33+
await _hubContext.Clients.Group("AnchorUpdates").SendAsync("AnchorDefinitionDeactivated", anchorDefinitionId);
34+
}
35+
36+
public async Task NotifyUserAnchorAssigned(UserAnchor userAnchor)
37+
{
38+
_logger.LogInformation("Broadcasting user anchor assigned: user {userId}, anchor {anchorId}", userAnchor.UserId, userAnchor.AnchorDefinitionId);
39+
await _hubContext.Clients.Group("AnchorUpdates").SendAsync("UserAnchorAssigned", userAnchor);
40+
}
41+
42+
public async Task NotifyUserAnchorRemoved(string userId, int anchorDefinitionId)
43+
{
44+
_logger.LogInformation("Broadcasting user anchor removed: user {userId}, anchor {anchorId}", userId, anchorDefinitionId);
45+
await _hubContext.Clients.Group("AnchorUpdates").SendAsync("UserAnchorRemoved", userId, anchorDefinitionId);
46+
}
47+
48+
public async Task NotifyDailyAnchorCompletionUpdated(int dailyAnchorId, bool isCompleted, DateTime? completedAt)
49+
{
50+
_logger.LogInformation("Broadcasting daily anchor completion updated: {dailyAnchorId}, completed: {isCompleted}", dailyAnchorId, isCompleted);
51+
await _hubContext.Clients.Group("AnchorUpdates").SendAsync("DailyAnchorCompletionUpdated", dailyAnchorId, isCompleted, completedAt);
52+
}
53+
}

HomeSpeaker.Server2/Services/AnchorService.cs

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,13 @@ public class AnchorService
88
{
99
private readonly MusicContext _dbContext;
1010
private readonly ILogger<AnchorService> _logger;
11+
private readonly IAnchorNotificationService _notificationService;
1112

12-
public AnchorService(MusicContext dbContext, ILogger<AnchorService> logger)
13+
public AnchorService(MusicContext dbContext, ILogger<AnchorService> logger, IAnchorNotificationService notificationService)
1314
{
1415
_dbContext = dbContext;
1516
_logger = logger;
17+
_notificationService = notificationService;
1618
}
1719

1820
// Anchor Definition Management
@@ -41,7 +43,9 @@ public async Task<AnchorDefinition> CreateAnchorDefinitionAsync(CreateAnchorDefi
4143
await _dbContext.AnchorDefinitions.AddAsync(entity);
4244
await _dbContext.SaveChangesAsync();
4345

44-
return new AnchorDefinition(entity.Id, entity.Name, entity.Description, entity.IsActive);
46+
var result = new AnchorDefinition(entity.Id, entity.Name, entity.Description, entity.IsActive);
47+
await _notificationService.NotifyAnchorDefinitionCreated(result);
48+
return result;
4549
}
4650

4751
public async Task<AnchorDefinition?> UpdateAnchorDefinitionAsync(int id, CreateAnchorDefinitionRequest request)
@@ -58,7 +62,9 @@ public async Task<AnchorDefinition> CreateAnchorDefinitionAsync(CreateAnchorDefi
5862
await _dbContext.SaveChangesAsync();
5963

6064
_logger.LogInformation("Updated anchor definition {id}: {name}", id, request.Name);
61-
return new AnchorDefinition(entity.Id, entity.Name, entity.Description, entity.IsActive);
65+
var result = new AnchorDefinition(entity.Id, entity.Name, entity.Description, entity.IsActive);
66+
await _notificationService.NotifyAnchorDefinitionUpdated(result);
67+
return result;
6268
}
6369

6470
public async Task<bool> DeactivateAnchorDefinitionAsync(int id)
@@ -75,6 +81,7 @@ public async Task<bool> DeactivateAnchorDefinitionAsync(int id)
7581
await _dbContext.SaveChangesAsync();
7682

7783
_logger.LogInformation("Deactivated anchor definition {id}: {name}", id, entity.Name);
84+
await _notificationService.NotifyAnchorDefinitionDeactivated(id);
7885
return true;
7986
}
8087

@@ -113,7 +120,9 @@ public async Task<UserAnchor> AssignAnchorToUserAsync(AssignAnchorToUserRequest
113120
await _dbContext.SaveChangesAsync();
114121

115122
_logger.LogInformation("Assigned anchor {anchorId} to user {userId}", request.AnchorDefinitionId, request.UserId);
116-
return new UserAnchor(entity.Id, entity.UserId, entity.AnchorDefinitionId, entity.CreatedAt);
123+
var result = new UserAnchor(entity.Id, entity.UserId, entity.AnchorDefinitionId, entity.CreatedAt);
124+
await _notificationService.NotifyUserAnchorAssigned(result);
125+
return result;
117126
}
118127

119128
public async Task<bool> RemoveAnchorFromUserAsync(string userId, int anchorDefinitionId)
@@ -131,6 +140,7 @@ public async Task<bool> RemoveAnchorFromUserAsync(string userId, int anchorDefin
131140
await _dbContext.SaveChangesAsync();
132141

133142
_logger.LogInformation("Removed anchor {anchorId} from user {userId}", anchorDefinitionId, userId);
143+
await _notificationService.NotifyUserAnchorRemoved(userId, anchorDefinitionId);
134144
return true;
135145
}
136146

@@ -216,6 +226,7 @@ public async Task<bool> UpdateAnchorCompletionAsync(UpdateAnchorCompletionReques
216226
await _dbContext.SaveChangesAsync();
217227

218228
_logger.LogInformation("Updated daily anchor {id} completion to {completed}", request.DailyAnchorId, request.IsCompleted);
229+
await _notificationService.NotifyDailyAnchorCompletionUpdated(request.DailyAnchorId, request.IsCompleted, entity.CompletedAt);
219230
return true;
220231
}
221232

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
using HomeSpeaker.Shared;
2+
3+
namespace HomeSpeaker.Server2.Services;
4+
5+
public interface IAnchorNotificationService
6+
{
7+
Task NotifyAnchorDefinitionCreated(AnchorDefinition anchorDefinition);
8+
Task NotifyAnchorDefinitionUpdated(AnchorDefinition anchorDefinition);
9+
Task NotifyAnchorDefinitionDeactivated(int anchorDefinitionId);
10+
Task NotifyUserAnchorAssigned(UserAnchor userAnchor);
11+
Task NotifyUserAnchorRemoved(string userId, int anchorDefinitionId);
12+
Task NotifyDailyAnchorCompletionUpdated(int dailyAnchorId, bool isCompleted, DateTime? completedAt);
13+
}

HomeSpeaker.Shared/Album.cs

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,12 @@
1-
using System;
2-
using System.Collections.Generic;
3-
using System.Linq;
4-
using System.Text;
1+
using System.Linq;
52

6-
namespace HomeSpeaker.Shared
3+
namespace HomeSpeaker.Shared;
4+
5+
public class Album
76
{
8-
public class Album
9-
{
10-
public int AlbumId { get; set; }
11-
public string Name { get; set; }
12-
public IQueryable<Song> Songs { get; set; }
13-
public Artist Artist { get; set; }
14-
public int ArtistId { get; set; }
15-
}
7+
public int AlbumId { get; set; }
8+
public string Name { get; set; }
9+
public IQueryable<Song> Songs { get; set; }
10+
public Artist Artist { get; set; }
11+
public int ArtistId { get; set; }
1612
}

HomeSpeaker.Shared/Artist.cs

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,11 @@
1-
using System;
2-
using System.Collections.Generic;
3-
using System.Linq;
4-
using System.Text;
1+
using System.Linq;
52

6-
namespace HomeSpeaker.Shared
3+
namespace HomeSpeaker.Shared;
4+
5+
public class Artist
76
{
8-
public class Artist
9-
{
10-
public int ArtistId { get; set; }
11-
public string Name { get; set; }
12-
public IQueryable<Album> Albums { get; set; }
13-
public IQueryable<Song> Songs { get; set; }
14-
}
7+
public int ArtistId { get; set; }
8+
public string Name { get; set; }
9+
public IQueryable<Album> Albums { get; set; }
10+
public IQueryable<Song> Songs { get; set; }
1511
}

HomeSpeaker.Shared/BloodSugar/BloodSugarReading.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,12 @@ public sealed class BloodSugarReading
88
{
99
public double Sgv { get; set; }
1010
//public DateTime Date { get; set; }
11-
public DateTime Date => DateString;
11+
public DateTime Date => this.DateString;
1212
public string Direction { get; set; } = string.Empty;
1313
public string Type { get; set; } = string.Empty;
1414
public DateTime DateString { get; set; }
1515

16-
public string DirectionIcon => Direction switch
16+
public string DirectionIcon => this.Direction switch
1717
{
1818
"Flat" => "→",
1919
"SingleUp" => "↗",
@@ -25,7 +25,7 @@ public sealed class BloodSugarReading
2525
_ => "?"
2626
};
2727

28-
public string DirectionDescription => Direction switch
28+
public string DirectionDescription => this.Direction switch
2929
{
3030
"Flat" => "Stable",
3131
"SingleUp" => "Rising slowly",

0 commit comments

Comments
 (0)