class GeofenceRuntimeState { final String? armedHomeId; final DateTime? lastCheckAt; final double? lastDistanceMeters; final String? triggeredHomeId; final DateTime? triggeredAt; final String? lastSuccessHomeId; final DateTime? lastSuccessAt; final String? lastFailureHomeId; final DateTime? lastFailureAt; final String? lastFailureMessage; const GeofenceRuntimeState({ this.armedHomeId, this.lastCheckAt, this.lastDistanceMeters, this.triggeredHomeId, this.triggeredAt, this.lastSuccessHomeId, this.lastSuccessAt, this.lastFailureHomeId, this.lastFailureAt, this.lastFailureMessage, }); bool isTriggeredFor(String homeId) => triggeredHomeId == homeId && triggeredAt != null; DateTime? failureAtFor(String homeId) { if (lastFailureHomeId != homeId) return null; return lastFailureAt; } String? failureMessageFor(String homeId) { if (lastFailureHomeId != homeId) return null; return lastFailureMessage; } GeofenceRuntimeState armForHome(String homeId) { if (armedHomeId == homeId) return this; return GeofenceRuntimeState( armedHomeId: homeId, lastSuccessHomeId: lastSuccessHomeId, lastSuccessAt: lastSuccessAt, lastFailureHomeId: lastFailureHomeId, lastFailureAt: lastFailureAt, lastFailureMessage: lastFailureMessage, ); } GeofenceRuntimeState disarm() { return GeofenceRuntimeState( lastSuccessHomeId: lastSuccessHomeId, lastSuccessAt: lastSuccessAt, lastFailureHomeId: lastFailureHomeId, lastFailureAt: lastFailureAt, lastFailureMessage: lastFailureMessage, ); } GeofenceRuntimeState recordInsideHome( String homeId, { required DateTime checkedAt, required double distanceMeters, }) { return GeofenceRuntimeState( armedHomeId: armedHomeId, lastCheckAt: checkedAt, lastDistanceMeters: distanceMeters, lastSuccessHomeId: lastSuccessHomeId, lastSuccessAt: lastSuccessAt, lastFailureHomeId: lastFailureHomeId == homeId ? null : lastFailureHomeId, lastFailureAt: lastFailureHomeId == homeId ? null : lastFailureAt, lastFailureMessage: lastFailureHomeId == homeId ? null : lastFailureMessage, ); } GeofenceRuntimeState recordOutsideCheck( String homeId, { required DateTime checkedAt, required double distanceMeters, }) { return GeofenceRuntimeState( armedHomeId: armedHomeId ?? homeId, lastCheckAt: checkedAt, lastDistanceMeters: distanceMeters, triggeredHomeId: triggeredHomeId, triggeredAt: triggeredAt, lastSuccessHomeId: lastSuccessHomeId, lastSuccessAt: lastSuccessAt, lastFailureHomeId: lastFailureHomeId, lastFailureAt: lastFailureAt, lastFailureMessage: lastFailureMessage, ); } GeofenceRuntimeState recordSuccess( String homeId, { required DateTime triggeredAt, required double distanceMeters, }) { return GeofenceRuntimeState( armedHomeId: homeId, lastCheckAt: triggeredAt, lastDistanceMeters: distanceMeters, triggeredHomeId: homeId, triggeredAt: triggeredAt, lastSuccessHomeId: homeId, lastSuccessAt: triggeredAt, lastFailureHomeId: lastFailureHomeId == homeId ? null : lastFailureHomeId, lastFailureAt: lastFailureHomeId == homeId ? null : lastFailureAt, lastFailureMessage: lastFailureHomeId == homeId ? null : lastFailureMessage, ); } GeofenceRuntimeState recordFailure( String homeId, { required DateTime failedAt, required double distanceMeters, required String message, }) { return GeofenceRuntimeState( armedHomeId: homeId, lastCheckAt: failedAt, lastDistanceMeters: distanceMeters, triggeredHomeId: triggeredHomeId == homeId ? null : triggeredHomeId, triggeredAt: triggeredHomeId == homeId ? null : triggeredAt, lastSuccessHomeId: lastSuccessHomeId, lastSuccessAt: lastSuccessAt, lastFailureHomeId: homeId, lastFailureAt: failedAt, lastFailureMessage: message, ); } GeofenceRuntimeState removeHome(String homeId) { return GeofenceRuntimeState( armedHomeId: armedHomeId == homeId ? null : armedHomeId, lastCheckAt: armedHomeId == homeId ? null : lastCheckAt, lastDistanceMeters: armedHomeId == homeId ? null : lastDistanceMeters, triggeredHomeId: triggeredHomeId == homeId ? null : triggeredHomeId, triggeredAt: triggeredHomeId == homeId ? null : triggeredAt, lastSuccessHomeId: lastSuccessHomeId == homeId ? null : lastSuccessHomeId, lastSuccessAt: lastSuccessHomeId == homeId ? null : lastSuccessAt, lastFailureHomeId: lastFailureHomeId == homeId ? null : lastFailureHomeId, lastFailureAt: lastFailureHomeId == homeId ? null : lastFailureAt, lastFailureMessage: lastFailureHomeId == homeId ? null : lastFailureMessage, ); } Map toJson() => { if (armedHomeId != null) 'armedHomeId': armedHomeId, if (lastCheckAt != null) 'lastCheckAt': lastCheckAt!.millisecondsSinceEpoch, if (lastDistanceMeters != null) 'lastDistanceMeters': lastDistanceMeters, if (triggeredHomeId != null) 'triggeredHomeId': triggeredHomeId, if (triggeredAt != null) 'triggeredAt': triggeredAt!.millisecondsSinceEpoch, if (lastSuccessHomeId != null) 'lastSuccessHomeId': lastSuccessHomeId, if (lastSuccessAt != null) 'lastSuccessAt': lastSuccessAt!.millisecondsSinceEpoch, if (lastFailureHomeId != null) 'lastFailureHomeId': lastFailureHomeId, if (lastFailureAt != null) 'lastFailureAt': lastFailureAt!.millisecondsSinceEpoch, if (lastFailureMessage != null) 'lastFailureMessage': lastFailureMessage, }; factory GeofenceRuntimeState.fromJson(Map json) { return GeofenceRuntimeState( armedHomeId: json['armedHomeId'] as String?, lastCheckAt: _fromMillis(json['lastCheckAt']), lastDistanceMeters: (json['lastDistanceMeters'] as num?)?.toDouble(), triggeredHomeId: json['triggeredHomeId'] as String?, triggeredAt: _fromMillis(json['triggeredAt']), lastSuccessHomeId: json['lastSuccessHomeId'] as String?, lastSuccessAt: _fromMillis(json['lastSuccessAt']), lastFailureHomeId: json['lastFailureHomeId'] as String?, lastFailureAt: _fromMillis(json['lastFailureAt']), lastFailureMessage: json['lastFailureMessage'] as String?, ); } static DateTime? _fromMillis(Object? value) { final millis = (value as num?)?.toInt(); if (millis == null) return null; return DateTime.fromMillisecondsSinceEpoch(millis); } }