Skip to content

feat: add Eversense Now CGM connector#117

Open
ryceg wants to merge 73 commits intomainfrom
feature/eversense-connector
Open

feat: add Eversense Now CGM connector#117
ryceg wants to merge 73 commits intomainfrom
feature/eversense-connector

Conversation

@ryceg
Copy link
Copy Markdown
Collaborator

@ryceg ryceg commented Apr 24, 2026

Summary

  • Adds a new connector that polls the Eversense Now follower API for real-time CGM glucose readings
  • OAuth2 password grant auth with token caching via AuthTokenProviderBase
  • Patient selection: auto-selects if one patient followed, configurable by username if multiple
  • Trend mapping verified through LoopKit's GlucoseTrend enum (from LoopKit/LoopWorkspace#266)
  • Unit conversion (mmol/L → mg/dL) when Eversense reports in mmol
  • Glucose-only, no historical sync (API only returns latest reading)

New files

src/Connectors/Nocturne.Connectors.Eversense/
├── Configurations/EversenseConnectorConfiguration.cs
├── Configurations/EversenseConstants.cs
├── Models/EversenseTokenResponse.cs
├── Models/EversensePatientData.cs
├── Mappers/EversenseSensorGlucoseMapper.cs
├── Services/EversenseAuthTokenProvider.cs
├── Services/EversenseConnectorService.cs
├── EversenseConnectorInstaller.cs
└── Nocturne.Connectors.Eversense.csproj

tests/Unit/Nocturne.Connectors.Eversense.Tests/
├── Mappers/EversenseSensorGlucoseMapperTests.cs (13 tests)
└── Services/EversenseConnectorServiceTests.cs (6 tests)

Modified files

  • ConnectSource.cs — added Eversense enum value
  • DataSources.cs — added EversenseConnector constant + IsConnector() update
  • ServiceNames.cs — added EversenseConnector constant
  • ConnectorPropertyKey.cs — added PatientUsername key

Test plan

  • 19 unit tests passing (13 mapper + 6 service)
  • Full solution builds with 0 errors
  • Manual test with real Eversense Now account credentials
  • Verify connector auto-discovery via IConnectorInstaller in Aspire

ryceg added 30 commits April 20, 2026 16:14
Domain model, service interface, EF entity with tenant scoping,
mapper, and DbContext registration with unique index on
(SubjectId, MarkKey).
ryceg added 25 commits April 23, 2026 15:53
… is set

When running in a worktree with a custom domain (e.g. nocturne.test),
the gateway now binds to port 443 if available, so the domain works
without a port number. Falls back to a dynamic port if 443 is already
taken by the main instance.
# Conflicts:
#	src/API/Nocturne.API/Controllers/V4/DevOnly/DevAdminController.cs
#	src/API/Nocturne.API/Middleware/RecoveryModeMiddleware.cs
#	src/API/Nocturne.API/Middleware/TenantSetupMiddleware.cs
#	src/API/Nocturne.API/Services/Auth/RecoveryModeCheckService.cs
#	src/API/Nocturne.API/Services/Auth/RecoveryModeState.cs
#	src/Core/Nocturne.Core.Contracts/Multitenancy/ITenantService.cs
Replace removed MealCarbIntake type with MealEvent from the generated API
client, updating property accesses across meals UI. Fix implicit any types,
unused imports, enum-to-string coercion, and QuietHours interface gap.
Includes multitenancy redesign merge artifacts and locale updates.
Replace generic Lucide icon wrappers (Radio, Syringe, Droplet, Battery)
with AAPS-sourced SVG paths for device events. Add 11 new therapy event
icon components (BgCheck, Calibration, ExtendedBolus, Note, Announcement,
Activity, ProfileSwitch, QuestionEvent, BolusEvent, CarbsEvent,
SiteRotation). Source SVGs stored in static/icons/aaps/ for reference.
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 24, 2026

Preview Container Images

Published for commit 3773051 with tag pr-117-3773051.

Image Status Package URI
nocturne-api ✅ Published package ghcr.io/nightscout/nocturne/nocturne-api:pr-117-3773051
nocturne-web ✅ Published package ghcr.io/nightscout/nocturne/nocturne-web:pr-117-3773051

This comment is updated on each push to this PR.


_logger.LogInformation(
"Setup: created first owner {SubjectId} ({Username}) for tenant {TenantId}",
subjectId, request.Username.Trim(), tenant!.Id);

_logger.LogInformation(
"Setup OIDC: created first owner {SubjectId} ({Username}) for tenant {TenantId}",
subjectId, request.Username.Trim(), tenant!.Id);
{
if (!string.IsNullOrEmpty(error))
{
_logger.LogWarning("Setup OIDC provider returned error: {Error} - {Description}", error, error_description);
{
if (!string.IsNullOrEmpty(error))
{
_logger.LogWarning("Setup OIDC provider returned error: {Error} - {Description}", error, error_description);
Comment on lines +456 to +463
Response.Cookies.Append("IsAuthenticated", "true", new CookieOptions
{
HttpOnly = false,
Secure = _oidcOptions.Cookie.Secure,
SameSite = cookieSameSite,
Path = "/",
MaxAge = TimeSpan.FromDays(7),
});

_logger.LogDebug(
"Upserting coach mark {MarkKey} to status {Status} for subject {SubjectId}",
markKey,
_logger.LogDebug(
"Upserting coach mark {MarkKey} to status {Status} for subject {SubjectId}",
markKey,
status,
_logger.LogError(
ex,
"Error upserting coach mark {MarkKey} for subject",
markKey
Comment on lines +78 to +81
var request = new HttpRequestMessage(HttpMethod.Post, $"{authBaseUrl}{EversenseConstants.Endpoints.Token}")
{
Content = formContent
};
var result = await ExecuteWithRetryAsync(
async () =>
{
var request = new HttpRequestMessage(HttpMethod.Get, url);
// Assert
ctx.Response.StatusCode.Should().Be(503);
ctx.Response.Body.Seek(0, SeekOrigin.Begin);
var body = await new StreamReader(ctx.Response.Body).ReadToEndAsync();

context.Response.StatusCode.Should().Be(503);
context.Response.Body.Seek(0, SeekOrigin.Begin);
var body = await new StreamReader(context.Response.Body).ReadToEndAsync();
Comment on lines +239 to +243
catch (Exception ex)
{
_logger.LogWarning(ex, "Setup owner passkey registration failed");
return Problem(detail: "Passkey registration failed during setup", statusCode: 400, title: "Registration Failed");
}
Comment on lines +60 to +64
catch (Exception ex)
{
_logger.LogWarning(ex, "Error converting Eversense patient datum");
return null;
}
Comment on lines +148 to +153
catch (Exception ex)
{
_logger.LogError(ex, "[{ConnectorSource}] Error during Eversense sync", ConnectorSource);
result.Success = false;
result.Errors.Add($"Sync error: {ex.Message}");
}
Comment thread src/API/Nocturne.API/Controllers/V4/DevOnly/DevAdminController.cs Fixed
Username = "test@example.com",
Password = "test-password",
}),
new HttpClient(),
return Task.FromResult(kvp.Value);
}

return Task.FromResult(new HttpResponseMessage(HttpStatusCode.NotFound));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants