import 'dart:convert'; import 'services/fetch_data.dart'; import 'utils/preferences_helper.dart'; import 'package:flutter/material.dart'; import 'package:home_widget/home_widget.dart'; import 'utils/unit_converter.dart'; import 'package:path_provider/path_provider.dart'; import 'package:hive/hive.dart'; import 'utils/condition_label_map.dart'; import 'package:flutter_dotenv/flutter_dotenv.dart'; import 'package:flutter/services.dart' show rootBundle; @pragma('vm:entry-point') Future updateHomeWidget(weather, {bool updatedFromHome = false}) async { try { WidgetsFlutterBinding.ensureInitialized(); await PreferencesHelper.init(); await dotenv.load(fileName: ".env"); double _safeToDouble(dynamic value, [double fallback = 0.0]) { if (value == null) return fallback; if (value is num) return value.toDouble(); return double.tryParse(value.toString()) ?? fallback; } final dynamic temp; final dynamic code; final dynamic maxTemp; final dynamic minTemp; final dynamic isDay; final List hourlyTime; final List hourlyTemps; late final dailyDataMain; final List hourlyWeatherCodes; final String utcOffsetSeconds; final timeUnit = PreferencesHelper.getString("selectedTimeUnit") ?? "12 hr"; final tempUnit = PreferencesHelper.getString("selectedTempUnit") ?? "Celsius"; final triggerFromWorker = PreferencesHelper.getBool("triggerfromWorker") ?? false; String? lastUpdatedString = PreferencesHelper.getString('lastUpdatedFromHome'); if (weather == null && updatedFromHome == false) { if (triggerFromWorker == false) { PreferencesHelper.setBool('triggerfromWorker', true); return; } if (lastUpdatedString != null) { DateTime lastUpdated = DateTime.parse(lastUpdatedString); Duration difference = DateTime.now().difference(lastUpdated); if (difference.inMinutes < 45) { return; } } final weatherService = WeatherService(); final homeLocation = PreferencesHelper.getJson('homeLocation'); if (homeLocation == null) { return; } final result = await weatherService.fetchWeather( homeLocation?['lat']!, homeLocation?['lon']!, locationName: homeLocation?['cacheKey'], context: null, isBackground: true); final current = result!['data']['current']; temp = _safeToDouble(current['temperature_2m'].toDouble()); code = current['weather_code']; final hourly = result!['data']['hourly'] ?? {}; final dailyData = result!['data']['daily']; dailyDataMain = dailyData; hourlyTime = hourly['time']; hourlyTemps = hourly['temperature_2m']; hourlyWeatherCodes = hourly['weather_code']; maxTemp = _safeToDouble(dailyData['temperature_2m_max'][0]); minTemp = _safeToDouble(dailyData['temperature_2m_min'][0]); isDay = current['is_day']; utcOffsetSeconds = result!['data']['utc_offset_seconds'].toString(); } else { final currentData = weather['current']; temp = currentData['temperature_2m'].toDouble(); code = currentData['weather_code']; final dailyData = weather['daily']; final hourly = weather['hourly'] ?? {}; dailyDataMain = dailyData; maxTemp = dailyData['temperature_2m_max'][0]; minTemp = dailyData['temperature_2m_min'][0]; isDay = currentData['is_day']; hourlyTime = hourly['time']; hourlyTemps = hourly['temperature_2m']; hourlyWeatherCodes = hourly['weather_code']; utcOffsetSeconds = weather['utc_offset_seconds'].toString(); } int offsetSeconds = int.parse(utcOffsetSeconds); DateTime utcNow = DateTime.now().toUtc(); DateTime now = utcNow.add(Duration(seconds: offsetSeconds)); now = DateTime(now.year, now.month, now.day, now.hour); // Find the first index in hourlyTime that is >= now int startIndex = hourlyTime.indexWhere((time) { DateTime hourlyTimeItem = DateTime.parse(time); return !hourlyTimeItem.isBefore(now); // same as >= now }); // If for some reason it's not found, default to 0 (safe fallback) if (startIndex == -1) startIndex = 0; for (int i = 0; i < 4; i++) { int currentIndex = startIndex + i; if (currentIndex >= hourlyTemps.length || currentIndex >= hourlyTime.length || currentIndex >= hourlyWeatherCodes.length) break; // Temp conversion final tempValue = hourlyTemps[currentIndex].toDouble(); final formattedTemp = tempUnit == 'Fahrenheit' ? UnitConverter.celsiusToFahrenheit(tempValue).round().toString() : tempValue.round().toString(); // Time formatting final rawTimeString = hourlyTime[currentIndex]; final roundedDisplayTime = DateTime.parse(rawTimeString); final formattedTime = timeUnit == '24 hr' ? "${roundedDisplayTime.hour.toString().padLeft(2, '0')}:00" : UnitConverter.formatTo12Hour(roundedDisplayTime); await HomeWidget.saveWidgetData('hourly_temp_$i', formattedTemp); await HomeWidget.saveWidgetData('hourly_time_$i', formattedTime); await HomeWidget.saveWidgetData( 'hourly_code_$i', hourlyWeatherCodes[currentIndex].toString()); } for (int i = 0; i < 4; i++) { if (i >= dailyDataMain['time'].length) break; final dayMaxTemp = _safeToDouble(dailyDataMain['temperature_2m_max'][i]); final dayMinTemp = _safeToDouble(dailyDataMain['temperature_2m_min'][i]); final dayCode = dailyDataMain['weather_code'][i]; final localeString = PreferencesHelper.getString('locale') ?? 'en'; final conditionKey = WeatherConditionMapper.getConditionLabel(dayCode, 1); Locale locale; if (localeString.contains('-')) { var parts = localeString.split('-'); locale = Locale(parts[0], parts[1]); } else if (localeString.contains('_')) { var parts = localeString.split('_'); locale = Locale(parts[0], parts[1]); } else { locale = Locale(localeString); } String translationFileName = locale.languageCode; if (locale.countryCode != null && locale.countryCode!.isNotEmpty) { translationFileName += '-${locale.countryCode}'; } Map translations; try { final String data = await rootBundle .loadString('assets/translations/$translationFileName.json'); translations = jsonDecode(data); } catch (e) { print( '[Translation] Could not load $translationFileName.json, falling back to en.json'); final String data = await rootBundle.loadString('assets/translations/en.json'); translations = jsonDecode(data); } final conditionNameDaily = translations[conditionKey] ?? conditionKey; final maxTempFormatted = tempUnit == 'Fahrenheit' ? UnitConverter.celsiusToFahrenheit(dayMaxTemp).round().toString() : dayMaxTemp.round().toString(); final minTempFormatted = tempUnit == 'Fahrenheit' ? UnitConverter.celsiusToFahrenheit(dayMinTemp).round().toString() : dayMinTemp.round().toString(); await HomeWidget.saveWidgetData( 'day${i + 1}Max', maxTempFormatted); await HomeWidget.saveWidgetData( 'day${i + 1}Min', minTempFormatted); await HomeWidget.saveWidgetData( 'day${i + 1}Code', dayCode.toString()); final dayDate = DateTime.parse(dailyDataMain['time'][i]); final formattedDay = "${dayDate.month}/${dayDate.day}"; await HomeWidget.saveWidgetData('day${i + 1}Date', formattedDay); await HomeWidget.saveWidgetData( 'day${i + 1}_condition', conditionNameDaily); } final convertedTemp = tempUnit == 'Fahrenheit' ? UnitConverter.celsiusToFahrenheit(temp).round() : temp.round(); final convertedMaxTemp = tempUnit == 'Fahrenheit' ? UnitConverter.celsiusToFahrenheit(maxTemp).round() : maxTemp.round(); final convertedMinTemp = tempUnit == 'Fahrenheit' ? UnitConverter.celsiusToFahrenheit(minTemp).round() : minTemp.round(); final currentTempFormatted = "${convertedTemp.round()}"; final maxTempFormatted = "${convertedMaxTemp.round()}"; final minTempFormatted = "${convertedMinTemp.round()}"; final conditionKey = WeatherConditionMapper.getConditionLabel(code, isDay); final localeString = PreferencesHelper.getString('locale') ?? 'en'; Locale locale; if (localeString.contains('-')) { var parts = localeString.split('-'); locale = Locale(parts[0], parts[1]); } else if (localeString.contains('_')) { var parts = localeString.split('_'); locale = Locale(parts[0], parts[1]); } else { locale = Locale(localeString); } String translationFileName = locale.languageCode; if (locale.countryCode != null && locale.countryCode!.isNotEmpty) { translationFileName += '-${locale.countryCode}'; } Map translations; try { final String data = await rootBundle .loadString('assets/translations/$translationFileName.json'); translations = jsonDecode(data); } catch (e) { print( '[Translation] Could not load $translationFileName.json, falling back to en.json'); final String data = await rootBundle.loadString('assets/translations/en.json'); translations = jsonDecode(data); } final conditionName = translations[conditionKey] ?? conditionKey; await HomeWidget.saveWidgetData( 'temperatureCurrentPill', currentTempFormatted); await HomeWidget.saveWidgetData( 'weather_codeCurrentPill', code.toString()); await HomeWidget.saveWidgetData('todayMax', maxTempFormatted); await HomeWidget.saveWidgetData('todayMin', minTempFormatted); await HomeWidget.saveWidgetData('locationNameWidget', "${PreferencesHelper.getJson('homeLocation')?['city']}, ${PreferencesHelper.getJson('homeLocation')?['country']}"); await HomeWidget.saveWidgetData( 'locationCurrentConditon', conditionName); await HomeWidget.saveWidgetData('isDayWidget', isDay.toString()); await HomeWidget.updateWidget(name: 'WeatherWidgetProvider', iOSName: null); await HomeWidget.updateWidget( name: 'WeatherWidgetCastProvider', iOSName: null); await HomeWidget.updateWidget(name: 'PillWidgetProvider', iOSName: null); await HomeWidget.updateWidget( name: 'clockDateWidgetProvider', iOSName: null); await HomeWidget.updateWidget( name: 'DateCurrentWidgetProvider', iOSName: null); await HomeWidget.updateWidget( name: 'ClockHourlyWidgetProvider', iOSName: null); print('[WidgetUpdate] Called updateWidget'); } catch (e, stack) { print('[WidgetUpdate][ERROR] $e'); print(stack); } }