chore: release v1.5.0
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:
co-authored by
Claude Opus 4.5
parent
ea6914b5f4
commit
fffc3d5baf
@@ -1,17 +1,24 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:tatlock_ui/features/front_hall/data/models/environment_model.dart'
|
||||
as api;
|
||||
import 'package:tatlock_ui/shared/theme/stoplight_colors.dart';
|
||||
|
||||
/// Air Quality Index widget displaying current AQI.
|
||||
///
|
||||
/// Currently uses mock data. Will be connected to air quality API in future.
|
||||
/// Displays air quality data from the Core API environment endpoint.
|
||||
/// Only render this widget when air quality data is available.
|
||||
class AirQualityWidget extends StatelessWidget {
|
||||
const AirQualityWidget({
|
||||
super.key,
|
||||
this.apiData,
|
||||
this.data,
|
||||
this.compact = false,
|
||||
});
|
||||
|
||||
/// Air quality data to display. Uses mock data if null.
|
||||
/// Air quality data from API. Takes priority over legacy data.
|
||||
final api.AirQualityData? apiData;
|
||||
|
||||
/// Legacy air quality data to display. Uses mock data if null.
|
||||
final AirQualityData? data;
|
||||
|
||||
/// Whether to use compact layout.
|
||||
@@ -20,7 +27,11 @@ class AirQualityWidget extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
final aqi = data ?? AirQualityData.mock();
|
||||
|
||||
// Convert API data to local model, or use legacy data
|
||||
final aqi = apiData != null
|
||||
? _fromApiData(apiData!)
|
||||
: (data ?? AirQualityData.mock());
|
||||
|
||||
if (compact) {
|
||||
return _buildCompact(context, colorScheme, aqi);
|
||||
@@ -199,6 +210,31 @@ class _PollutantChip extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts API air quality data to local AirQualityData model.
|
||||
AirQualityData _fromApiData(api.AirQualityData data) {
|
||||
final index = data.aqi ?? 0;
|
||||
final pollutants = <Pollutant>[];
|
||||
|
||||
if (data.pm25 != null) {
|
||||
pollutants.add(Pollutant(name: 'PM2.5', value: data.pm25!, unit: 'µg/m³'));
|
||||
}
|
||||
if (data.pm10 != null) {
|
||||
pollutants.add(Pollutant(name: 'PM10', value: data.pm10!, unit: 'µg/m³'));
|
||||
}
|
||||
if (data.o3 != null) {
|
||||
pollutants.add(Pollutant(name: 'O₃', value: data.o3!, unit: 'ppb'));
|
||||
}
|
||||
if (data.no2 != null) {
|
||||
pollutants.add(Pollutant(name: 'NO₂', value: data.no2!, unit: 'ppb'));
|
||||
}
|
||||
|
||||
return AirQualityData(
|
||||
index: index,
|
||||
level: AqiLevel.fromIndex(index),
|
||||
pollutants: pollutants,
|
||||
);
|
||||
}
|
||||
|
||||
/// Air Quality Index levels based on US EPA standard.
|
||||
enum AqiLevel {
|
||||
good(
|
||||
|
||||
Reference in New Issue
Block a user