Letzter Roadmap-Punkt von V3. Foto-basierte Lichtverhältnis-Analyse pro Stellplatz (Cloud Function analyzeLocation) und darauf aufbauende, rein textbasierte Eignungs-Bewertung einer Pflanze (assessPlantFit) — beides ausschließlich auf Abruf, nicht automatisch (Kostenkontrolle, eigener Anthropic-Key). Neue Stellplatz-Detailseite, Verlinkung im Pflanzen-Detail, Erkennung veralteter Bewertungen bei Umzug/Neuanalyse. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
130 lines
4.4 KiB
Dart
130 lines
4.4 KiB
Dart
import 'package:flutter/foundation.dart';
|
||
|
||
/// Eine Pflanze im Haushalt.
|
||
///
|
||
/// Das Modell ist bewusst haushaltszentriert gedacht: Sobald das
|
||
/// Firebase-Backend angebunden ist, leben Pflanzen unter
|
||
/// `households/{householdId}/plants/{plantId}`.
|
||
@immutable
|
||
class Plant {
|
||
const Plant({
|
||
required this.id,
|
||
required this.nickname,
|
||
required this.species,
|
||
required this.wateringIntervalDays,
|
||
required this.fertilizingIntervalDays,
|
||
this.locationId,
|
||
this.description = '',
|
||
this.careNotes = '',
|
||
this.photoUrl,
|
||
this.lastWatered,
|
||
this.lastFertilized,
|
||
this.lastWateredBy,
|
||
this.lastFertilizedBy,
|
||
this.repottingIntervalMonths,
|
||
this.lastRepotted,
|
||
this.lastRepottedBy,
|
||
this.fitStars,
|
||
this.fitReasoning,
|
||
this.fitLocationId,
|
||
this.fitAssessedAt,
|
||
});
|
||
|
||
final String id;
|
||
|
||
/// Vom Nutzer vergebener Name, z. B. "Omas Monstera".
|
||
final String nickname;
|
||
|
||
/// Pflanzenart (deutsch und/oder botanisch). Wird später von der
|
||
/// PlantNet-Erkennung befüllt, kann aber auch frei eingegeben werden.
|
||
final String species;
|
||
|
||
/// Referenz auf einen Stellplatz des Haushalts (optional).
|
||
final String? locationId;
|
||
|
||
/// Beschreibung und Pflegehinweise – von der KI erzeugt oder leer.
|
||
final String description;
|
||
final String careNotes;
|
||
|
||
/// Download-URL des Pflanzenfotos in Firebase Storage (optional).
|
||
final String? photoUrl;
|
||
|
||
final int wateringIntervalDays;
|
||
final int fertilizingIntervalDays;
|
||
|
||
/// `null` bedeutet: noch nie erledigt → Aufgabe ist sofort fällig.
|
||
final DateTime? lastWatered;
|
||
final DateTime? lastFertilized;
|
||
|
||
/// Wer zuletzt bestätigt hat (Anzeigename) – für den Haushalt sichtbar.
|
||
final String? lastWateredBy;
|
||
final String? lastFertilizedBy;
|
||
|
||
/// Umtopf-Erinnerung (V3), optional: `null` = keine Erinnerung gewünscht.
|
||
/// Bei gesetztem Intervall und `lastRepotted == null` ist die Aufgabe
|
||
/// sofort fällig (wie bei Gießen/Düngen: „noch nie erledigt").
|
||
final int? repottingIntervalMonths;
|
||
final DateTime? lastRepotted;
|
||
final String? lastRepottedBy;
|
||
|
||
/// Eignungs-Bewertung (V3, auf Abruf) für den aktuell zugeordneten
|
||
/// Stellplatz: 1-5 Sterne mit kurzer Begründung. Gilt als veraltet, wenn
|
||
/// [fitLocationId] nicht mehr [locationId] entspricht (Pflanze wurde
|
||
/// verschoben) oder der Stellplatz seither neu analysiert wurde.
|
||
final int? fitStars;
|
||
final String? fitReasoning;
|
||
final String? fitLocationId;
|
||
final DateTime? fitAssessedAt;
|
||
|
||
Plant copyWith({
|
||
String? nickname,
|
||
String? species,
|
||
String? Function()? locationId,
|
||
String? description,
|
||
String? careNotes,
|
||
String? photoUrl,
|
||
int? wateringIntervalDays,
|
||
int? fertilizingIntervalDays,
|
||
DateTime? lastWatered,
|
||
DateTime? lastFertilized,
|
||
String? lastWateredBy,
|
||
String? lastFertilizedBy,
|
||
// Als Getter, damit sich die Felder auch auf null zurücksetzen lassen
|
||
// (Intervall entfernen = Erinnerung abschalten).
|
||
int? Function()? repottingIntervalMonths,
|
||
DateTime? Function()? lastRepotted,
|
||
int? fitStars,
|
||
String? fitReasoning,
|
||
String? fitLocationId,
|
||
DateTime? fitAssessedAt,
|
||
}) {
|
||
return Plant(
|
||
id: id,
|
||
nickname: nickname ?? this.nickname,
|
||
species: species ?? this.species,
|
||
locationId: locationId != null ? locationId() : this.locationId,
|
||
description: description ?? this.description,
|
||
careNotes: careNotes ?? this.careNotes,
|
||
photoUrl: photoUrl ?? this.photoUrl,
|
||
wateringIntervalDays: wateringIntervalDays ?? this.wateringIntervalDays,
|
||
fertilizingIntervalDays:
|
||
fertilizingIntervalDays ?? this.fertilizingIntervalDays,
|
||
lastWatered: lastWatered ?? this.lastWatered,
|
||
lastFertilized: lastFertilized ?? this.lastFertilized,
|
||
lastWateredBy: lastWateredBy ?? this.lastWateredBy,
|
||
lastFertilizedBy: lastFertilizedBy ?? this.lastFertilizedBy,
|
||
repottingIntervalMonths: repottingIntervalMonths != null
|
||
? repottingIntervalMonths()
|
||
: this.repottingIntervalMonths,
|
||
lastRepotted:
|
||
lastRepotted != null ? lastRepotted() : this.lastRepotted,
|
||
fitStars: fitStars ?? this.fitStars,
|
||
fitReasoning: fitReasoning ?? this.fitReasoning,
|
||
fitLocationId: fitLocationId ?? this.fitLocationId,
|
||
fitAssessedAt: fitAssessedAt ?? this.fitAssessedAt,
|
||
// Bewusst nicht per copyWith änderbar — wird nur beim Bestätigen
|
||
// einer Umtopf-Aufgabe direkt in Firestore gesetzt.
|
||
lastRepottedBy: lastRepottedBy,
|
||
);
|
||
}
|
||
}
|