chore: release v1.5.0
Build and Push / release (push) Successful in 3s
Build and Push / build (push) Successful in 3m12s

feat: add dynamic environment widgets

Add live weather, sun position, and forecast widgets to Front Hall dashboard,
powered by data from the Qdrant volatile collection via core-api.

New widgets:
- SunPositionWidget: Animated arc showing sun/moon position with gradient colors
- ForecastWidget: Multi-day weather outlook
- Updated WeatherWidget and AirQualityWidget to accept API data

Infrastructure:
- Environment datasource calling GET /tools/environment
- Environment provider with 5-minute auto-refresh
- Freezed models for environment data

Tests:
- 12 widget tests for environment section
- Updated existing tests with givenEnvironment() harness method

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Jeroen Schweitzer
2026-01-06 23:05:24 +01:00
co-authored by Claude Opus 4.5
parent ea6914b5f4
commit fffc3d5baf
14 changed files with 1469 additions and 30 deletions
+89
View File
@@ -318,4 +318,93 @@ class Fixtures {
static Map<String, dynamic> quickLink(int index) =>
Map<String, dynamic>.from(quickLinks[index]);
// ============================================================
// Environment Data
// ============================================================
static const environment = {
'weather': {
'temperature': 8.5,
'feels_like': 5.0,
'conditions': 'Partly Cloudy',
'humidity': 72,
'wind_speed': 18.5,
'wind_direction': 'SW',
'location': 'Rotterdam, NL',
'icon': '03d',
},
'forecast': [
{
'date': '2026-01-06',
'high': 10.0,
'low': 4.0,
'conditions': 'Cloudy',
'precipitation_chance': 20,
},
{
'date': '2026-01-07',
'high': 12.0,
'low': 5.0,
'conditions': 'Partly Cloudy',
'precipitation_chance': 10,
},
{
'date': '2026-01-08',
'high': 9.0,
'low': 3.0,
'conditions': 'Rain',
'precipitation_chance': 80,
},
{
'date': '2026-01-09',
'high': 11.0,
'low': 6.0,
'conditions': 'Sunny',
'precipitation_chance': 0,
},
],
'sun_times': {
'sunrise': '2026-01-06T08:45:00Z',
'sunset': '2026-01-06T16:50:00Z',
'daylight_minutes': 485,
'solar_noon': '2026-01-06T12:47:30Z',
},
'air_quality': {
'aqi': 42,
'quality': 'Good',
'pm25': 8.5,
'pm10': 15.0,
'o3': 32.0,
},
'updated_at': '2026-01-06T10:30:00Z',
'user': 'default',
};
/// Environment data without air quality (for conditional rendering test).
static const environmentNoAirQuality = {
'weather': {
'temperature': 8.5,
'conditions': 'Partly Cloudy',
'humidity': 72,
'wind_speed': 18.5,
'location': 'Rotterdam, NL',
},
'forecast': [
{
'date': '2026-01-06',
'high': 10.0,
'low': 4.0,
'conditions': 'Cloudy',
},
],
'sun_times': {
'sunrise': '2026-01-06T08:45:00Z',
'sunset': '2026-01-06T16:50:00Z',
'daylight_minutes': 485,
},
'air_quality': null,
'updated_at': '2026-01-06T10:30:00Z',
'user': 'default',
};
}
+10
View File
@@ -172,6 +172,16 @@ class TestHarness {
api.whenGet('/tools/system/stats', stats ?? Fixtures.systemStats);
}
/// Set up mock environment response.
void givenEnvironment([Map<String, dynamic>? environment]) {
api.whenGet('/tools/environment', environment ?? Fixtures.environment);
}
/// Set up mock environment response without air quality.
void givenEnvironmentNoAirQuality() {
api.whenGet('/tools/environment', Fixtures.environmentNoAirQuality);
}
/// Set up theme mode.
void givenThemeMode(ThemeMode mode) {
_themeMode = mode;