diff options
Diffstat (limited to 'xbmc/weather')
-rw-r--r-- | xbmc/weather/CMakeLists.txt | 9 | ||||
-rw-r--r-- | xbmc/weather/GUIWindowWeather.cpp | 299 | ||||
-rw-r--r-- | xbmc/weather/GUIWindowWeather.h | 31 | ||||
-rw-r--r-- | xbmc/weather/WeatherJob.cpp | 243 | ||||
-rw-r--r-- | xbmc/weather/WeatherJob.h | 63 | ||||
-rw-r--r-- | xbmc/weather/WeatherManager.cpp | 189 | ||||
-rw-r--r-- | xbmc/weather/WeatherManager.h | 108 |
7 files changed, 942 insertions, 0 deletions
diff --git a/xbmc/weather/CMakeLists.txt b/xbmc/weather/CMakeLists.txt new file mode 100644 index 0000000..c79cab1 --- /dev/null +++ b/xbmc/weather/CMakeLists.txt @@ -0,0 +1,9 @@ +set(SOURCES GUIWindowWeather.cpp + WeatherJob.cpp + WeatherManager.cpp) + +set(HEADERS GUIWindowWeather.h + WeatherJob.h + WeatherManager.h) + +core_add_library(weather) diff --git a/xbmc/weather/GUIWindowWeather.cpp b/xbmc/weather/GUIWindowWeather.cpp new file mode 100644 index 0000000..66b93b5 --- /dev/null +++ b/xbmc/weather/GUIWindowWeather.cpp @@ -0,0 +1,299 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "GUIWindowWeather.h" + +#include "GUIUserMessages.h" +#include "LangInfo.h" +#include "ServiceBroker.h" +#include "guilib/WindowIDs.h" +#include "utils/StringUtils.h" +#include "utils/URIUtils.h" +#include "utils/Variant.h" +#include "weather/WeatherManager.h" + +#include <utility> + +using namespace ADDON; + +#define CONTROL_BTNREFRESH 2 +#define CONTROL_SELECTLOCATION 3 +#define CONTROL_LABELUPDATED 11 + +#define CONTROL_STATICTEMP 223 +#define CONTROL_STATICFEEL 224 +#define CONTROL_STATICUVID 225 +#define CONTROL_STATICWIND 226 +#define CONTROL_STATICDEWP 227 +#define CONTROL_STATICHUMI 228 + +#define CONTROL_LABELD0DAY 31 +#define CONTROL_LABELD0HI 32 +#define CONTROL_LABELD0LOW 33 +#define CONTROL_LABELD0GEN 34 +#define CONTROL_IMAGED0IMG 35 + +#define LOCALIZED_TOKEN_FIRSTID 370 +#define LOCALIZED_TOKEN_LASTID 395 + +/* +FIXME'S +>strings are not centered +*/ + +CGUIWindowWeather::CGUIWindowWeather(void) + : CGUIWindow(WINDOW_WEATHER, "MyWeather.xml") +{ + m_loadType = KEEP_IN_MEMORY; +} + +CGUIWindowWeather::~CGUIWindowWeather(void) = default; + +bool CGUIWindowWeather::OnMessage(CGUIMessage& message) +{ + switch ( message.GetMessage() ) + { + case GUI_MSG_CLICKED: + { + int iControl = message.GetSenderId(); + if (iControl == CONTROL_BTNREFRESH) + { + CServiceBroker::GetWeatherManager().Refresh(); // Refresh clicked so do a complete update + } + else if (iControl == CONTROL_SELECTLOCATION) + { + CGUIMessage msg(GUI_MSG_ITEM_SELECTED,GetID(),CONTROL_SELECTLOCATION); + OnMessage(msg); + + SetLocation(msg.GetParam1()); + } + } + break; + case GUI_MSG_NOTIFY_ALL: + if (message.GetParam1() == GUI_MSG_WINDOW_RESET) + { + CServiceBroker::GetWeatherManager().Reset(); + return true; + } + else if (message.GetParam1() == GUI_MSG_WEATHER_FETCHED) + { + UpdateLocations(); + SetProperties(); + } + break; + case GUI_MSG_ITEM_SELECT: + { + if (message.GetSenderId() == 0) //handle only message from builtin + { + SetLocation(message.GetParam1()); + return true; + } + } + break; + case GUI_MSG_MOVE_OFFSET: + { + if (message.GetSenderId() == 0 && m_maxLocation > 0) //handle only message from builtin + { + // Clamp location between 1 and m_maxLocation + int v = (CServiceBroker::GetWeatherManager().GetArea() + message.GetParam1() - 1) % m_maxLocation + 1; + if (v < 1) v += m_maxLocation; + SetLocation(v); + return true; + } + } + break; + default: + break; + } + + return CGUIWindow::OnMessage(message); +} + +void CGUIWindowWeather::OnInitWindow() +{ + // call UpdateButtons() so that we start with our initial stuff already present + UpdateButtons(); + UpdateLocations(); + CGUIWindow::OnInitWindow(); +} + +void CGUIWindowWeather::UpdateLocations() +{ + if (!IsActive()) return; + m_maxLocation = strtol(GetProperty("Locations").asString().c_str(),0,10); + if (m_maxLocation < 1) return; + + std::vector< std::pair<std::string, int> > labels; + + unsigned int iCurWeather = CServiceBroker::GetWeatherManager().GetArea(); + + if (iCurWeather > m_maxLocation) + { + CServiceBroker::GetWeatherManager().SetArea(m_maxLocation); + iCurWeather = m_maxLocation; + ClearProperties(); + CServiceBroker::GetWeatherManager().Refresh(); + } + + for (unsigned int i = 1; i <= m_maxLocation; i++) + { + std::string strLabel = CServiceBroker::GetWeatherManager().GetLocation(i); + if (strLabel.size() > 1) //got the location string yet? + { + size_t iPos = strLabel.rfind(", "); + if (iPos != std::string::npos) + { + std::string strLabel2(strLabel); + strLabel = strLabel2.substr(0,iPos); + } + labels.emplace_back(strLabel, i); + } + else + { + strLabel = StringUtils::Format("AreaCode {}", i); + labels.emplace_back(strLabel, i); + } + // in case it's a button, set the label + if (i == iCurWeather) + SET_CONTROL_LABEL(CONTROL_SELECTLOCATION,strLabel); + } + + SET_CONTROL_LABELS(CONTROL_SELECTLOCATION, iCurWeather, &labels); +} + +void CGUIWindowWeather::UpdateButtons() +{ + CONTROL_ENABLE(CONTROL_BTNREFRESH); + + SET_CONTROL_LABEL(CONTROL_BTNREFRESH, 184); //Refresh + + SET_CONTROL_LABEL(WEATHER_LABEL_LOCATION, CServiceBroker::GetWeatherManager().GetLocation(CServiceBroker::GetWeatherManager().GetArea())); + SET_CONTROL_LABEL(CONTROL_LABELUPDATED, CServiceBroker::GetWeatherManager().GetLastUpdateTime()); + + SET_CONTROL_LABEL(WEATHER_LABEL_CURRENT_COND, CServiceBroker::GetWeatherManager().GetInfo(WEATHER_LABEL_CURRENT_COND)); + SET_CONTROL_LABEL(WEATHER_LABEL_CURRENT_TEMP, CServiceBroker::GetWeatherManager().GetInfo(WEATHER_LABEL_CURRENT_TEMP) + g_langInfo.GetTemperatureUnitString()); + SET_CONTROL_LABEL(WEATHER_LABEL_CURRENT_FEEL, CServiceBroker::GetWeatherManager().GetInfo(WEATHER_LABEL_CURRENT_FEEL) + g_langInfo.GetTemperatureUnitString()); + SET_CONTROL_LABEL(WEATHER_LABEL_CURRENT_UVID, CServiceBroker::GetWeatherManager().GetInfo(WEATHER_LABEL_CURRENT_UVID)); + SET_CONTROL_LABEL(WEATHER_LABEL_CURRENT_WIND, CServiceBroker::GetWeatherManager().GetInfo(WEATHER_LABEL_CURRENT_WIND)); + SET_CONTROL_LABEL(WEATHER_LABEL_CURRENT_DEWP, CServiceBroker::GetWeatherManager().GetInfo(WEATHER_LABEL_CURRENT_DEWP) + g_langInfo.GetTemperatureUnitString()); + SET_CONTROL_LABEL(WEATHER_LABEL_CURRENT_HUMI, CServiceBroker::GetWeatherManager().GetInfo(WEATHER_LABEL_CURRENT_HUMI)); + SET_CONTROL_FILENAME(WEATHER_IMAGE_CURRENT_ICON, CServiceBroker::GetWeatherManager().GetInfo(WEATHER_IMAGE_CURRENT_ICON)); + + //static labels + SET_CONTROL_LABEL(CONTROL_STATICTEMP, 401); //Temperature + SET_CONTROL_LABEL(CONTROL_STATICFEEL, 402); //Feels Like + SET_CONTROL_LABEL(CONTROL_STATICUVID, 403); //UV Index + SET_CONTROL_LABEL(CONTROL_STATICWIND, 404); //Wind + SET_CONTROL_LABEL(CONTROL_STATICDEWP, 405); //Dew Point + SET_CONTROL_LABEL(CONTROL_STATICHUMI, 406); //Humidity + + for (int i = 0; i < NUM_DAYS; i++) + { + SET_CONTROL_LABEL(CONTROL_LABELD0DAY + (i*10), CServiceBroker::GetWeatherManager().GetForecast(i).m_day); + SET_CONTROL_LABEL(CONTROL_LABELD0HI + (i*10), CServiceBroker::GetWeatherManager().GetForecast(i).m_high + g_langInfo.GetTemperatureUnitString()); + SET_CONTROL_LABEL(CONTROL_LABELD0LOW + (i*10), CServiceBroker::GetWeatherManager().GetForecast(i).m_low + g_langInfo.GetTemperatureUnitString()); + SET_CONTROL_LABEL(CONTROL_LABELD0GEN + (i*10), CServiceBroker::GetWeatherManager().GetForecast(i).m_overview); + SET_CONTROL_FILENAME(CONTROL_IMAGED0IMG + (i*10), CServiceBroker::GetWeatherManager().GetForecast(i).m_icon); + } +} + +void CGUIWindowWeather::FrameMove() +{ + // update our controls + UpdateButtons(); + + CGUIWindow::FrameMove(); +} + +/*! + \brief Sets the location to the specified index and refreshes the weather + \param loc the location index (in the range [1..MAXLOCATION]) + */ +void CGUIWindowWeather::SetLocation(int loc) +{ + if (loc < 1 || loc > (int)m_maxLocation) + return; + // Avoid a settings write if old location == new location + if (CServiceBroker::GetWeatherManager().GetArea() != loc) + { + ClearProperties(); + CServiceBroker::GetWeatherManager().SetArea(loc); + std::string strLabel = CServiceBroker::GetWeatherManager().GetLocation(loc); + size_t iPos = strLabel.rfind(", "); + if (iPos != std::string::npos) + strLabel = strLabel.substr(0, iPos); + SET_CONTROL_LABEL(CONTROL_SELECTLOCATION, strLabel); + } + CServiceBroker::GetWeatherManager().Refresh(); +} + +void CGUIWindowWeather::SetProperties() +{ + // Current weather + int iCurWeather = CServiceBroker::GetWeatherManager().GetArea(); + SetProperty("Location", CServiceBroker::GetWeatherManager().GetLocation(iCurWeather)); + SetProperty("LocationIndex", iCurWeather); + SetProperty("Updated", CServiceBroker::GetWeatherManager().GetLastUpdateTime()); + SetProperty("Current.ConditionIcon", CServiceBroker::GetWeatherManager().GetInfo(WEATHER_IMAGE_CURRENT_ICON)); + SetProperty("Current.Condition", CServiceBroker::GetWeatherManager().GetInfo(WEATHER_LABEL_CURRENT_COND)); + SetProperty("Current.Temperature", CServiceBroker::GetWeatherManager().GetInfo(WEATHER_LABEL_CURRENT_TEMP)); + SetProperty("Current.FeelsLike", CServiceBroker::GetWeatherManager().GetInfo(WEATHER_LABEL_CURRENT_FEEL)); + SetProperty("Current.UVIndex", CServiceBroker::GetWeatherManager().GetInfo(WEATHER_LABEL_CURRENT_UVID)); + SetProperty("Current.Wind", CServiceBroker::GetWeatherManager().GetInfo(WEATHER_LABEL_CURRENT_WIND)); + SetProperty("Current.DewPoint", CServiceBroker::GetWeatherManager().GetInfo(WEATHER_LABEL_CURRENT_DEWP)); + SetProperty("Current.Humidity", CServiceBroker::GetWeatherManager().GetInfo(WEATHER_LABEL_CURRENT_HUMI)); + // we use the icons code number for fanart as it's the safest way + std::string fanartcode = URIUtils::GetFileName(CServiceBroker::GetWeatherManager().GetInfo(WEATHER_IMAGE_CURRENT_ICON)); + URIUtils::RemoveExtension(fanartcode); + SetProperty("Current.FanartCode", fanartcode); + + // Future weather + std::string day; + for (int i = 0; i < NUM_DAYS; i++) + { + day = StringUtils::Format("Day{}.", i); + SetProperty(day + "Title", CServiceBroker::GetWeatherManager().GetForecast(i).m_day); + SetProperty(day + "HighTemp", CServiceBroker::GetWeatherManager().GetForecast(i).m_high); + SetProperty(day + "LowTemp", CServiceBroker::GetWeatherManager().GetForecast(i).m_low); + SetProperty(day + "Outlook", CServiceBroker::GetWeatherManager().GetForecast(i).m_overview); + SetProperty(day + "OutlookIcon", CServiceBroker::GetWeatherManager().GetForecast(i).m_icon); + fanartcode = URIUtils::GetFileName(CServiceBroker::GetWeatherManager().GetForecast(i).m_icon); + URIUtils::RemoveExtension(fanartcode); + SetProperty(day + "FanartCode", fanartcode); + } +} + +void CGUIWindowWeather::ClearProperties() +{ + // Current weather + SetProperty("Location", ""); + SetProperty("LocationIndex", ""); + SetProperty("Updated", ""); + SetProperty("Current.ConditionIcon", ""); + SetProperty("Current.Condition", ""); + SetProperty("Current.Temperature", ""); + SetProperty("Current.FeelsLike", ""); + SetProperty("Current.UVIndex", ""); + SetProperty("Current.Wind", ""); + SetProperty("Current.DewPoint", ""); + SetProperty("Current.Humidity", ""); + SetProperty("Current.FanartCode", ""); + + // Future weather + std::string day; + for (int i = 0; i < NUM_DAYS; i++) + { + day = StringUtils::Format("Day{}.", i); + SetProperty(day + "Title", ""); + SetProperty(day + "HighTemp", ""); + SetProperty(day + "LowTemp", ""); + SetProperty(day + "Outlook", ""); + SetProperty(day + "OutlookIcon", ""); + SetProperty(day + "FanartCode", ""); + } +} diff --git a/xbmc/weather/GUIWindowWeather.h b/xbmc/weather/GUIWindowWeather.h new file mode 100644 index 0000000..1ae1828 --- /dev/null +++ b/xbmc/weather/GUIWindowWeather.h @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "guilib/GUIWindow.h" + +class CGUIWindowWeather : public CGUIWindow +{ +public: + CGUIWindowWeather(void); + ~CGUIWindowWeather(void) override; + bool OnMessage(CGUIMessage& message) override; + void FrameMove() override; + +protected: + void OnInitWindow() override; + + void UpdateButtons(); + void UpdateLocations(); + void SetProperties(); + void ClearProperties(); + void SetLocation(int loc); + + unsigned int m_maxLocation = 0; +}; diff --git a/xbmc/weather/WeatherJob.cpp b/xbmc/weather/WeatherJob.cpp new file mode 100644 index 0000000..17a0995 --- /dev/null +++ b/xbmc/weather/WeatherJob.cpp @@ -0,0 +1,243 @@ +/* + * Copyright (C) 2012-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "WeatherJob.h" + +#include "GUIUserMessages.h" +#include "LangInfo.h" +#include "ServiceBroker.h" +#include "XBDateTime.h" +#include "addons/AddonManager.h" +#include "addons/addoninfo/AddonType.h" +#include "guilib/GUIComponent.h" +#include "guilib/GUIWindowManager.h" +#include "guilib/LocalizeStrings.h" +#include "interfaces/generic/ScriptInvocationManager.h" +#include "network/Network.h" +#include "settings/Settings.h" +#include "settings/SettingsComponent.h" +#include "settings/lib/Setting.h" +#include "utils/POUtils.h" +#include "utils/StringUtils.h" +#include "utils/URIUtils.h" +#include "utils/Variant.h" +#include "utils/XTimeUtils.h" +#include "utils/log.h" + +#define LOCALIZED_TOKEN_FIRSTID 370 +#define LOCALIZED_TOKEN_LASTID 395 +#define LOCALIZED_TOKEN_FIRSTID2 1350 +#define LOCALIZED_TOKEN_LASTID2 1449 +#define LOCALIZED_TOKEN_FIRSTID3 11 +#define LOCALIZED_TOKEN_LASTID3 17 +#define LOCALIZED_TOKEN_FIRSTID4 71 +#define LOCALIZED_TOKEN_LASTID4 97 + +using namespace ADDON; + +using namespace std::chrono_literals; + +CWeatherJob::CWeatherJob(int location) +{ + m_location = location; +} + +bool CWeatherJob::DoWork() +{ + // wait for the network + if (!CServiceBroker::GetNetwork().IsAvailable()) + return false; + + AddonPtr addon; + if (!CServiceBroker::GetAddonMgr().GetAddon( + CServiceBroker::GetSettingsComponent()->GetSettings()->GetString( + CSettings::SETTING_WEATHER_ADDON), + addon, AddonType::SCRIPT_WEATHER, OnlyEnabled::CHOICE_YES)) + return false; + + // initialize our sys.argv variables + std::vector<std::string> argv; + argv.push_back(addon->LibPath()); + + std::string strSetting = std::to_string(m_location); + argv.push_back(strSetting); + + // Download our weather + CLog::Log(LOGINFO, "WEATHER: Downloading weather"); + // call our script, passing the areacode + int scriptId = -1; + if ((scriptId = CScriptInvocationManager::GetInstance().ExecuteAsync(argv[0], addon, argv)) >= 0) + { + while (true) + { + if (!CScriptInvocationManager::GetInstance().IsRunning(scriptId)) + break; + KODI::TIME::Sleep(100ms); + } + + SetFromProperties(); + + // and send a message that we're done + CGUIMessage msg(GUI_MSG_NOTIFY_ALL,0,0,GUI_MSG_WEATHER_FETCHED); + CServiceBroker::GetGUI()->GetWindowManager().SendThreadMessage(msg); + } + else + CLog::Log(LOGERROR, "WEATHER: Weather download failed!"); + + return true; +} + +const CWeatherInfo &CWeatherJob::GetInfo() const +{ + return m_info; +} + +void CWeatherJob::LocalizeOverviewToken(std::string &token) +{ + // This routine is case-insensitive. + std::string strLocStr; + if (!token.empty()) + { + ilocalizedTokens i; + i = m_localizedTokens.find(token); + if (i != m_localizedTokens.end()) + { + strLocStr = g_localizeStrings.Get(i->second); + } + } + if (strLocStr == "") + strLocStr = token; //if not found, let fallback + token = strLocStr; +} + +void CWeatherJob::LocalizeOverview(std::string &str) +{ + std::vector<std::string> words = StringUtils::Split(str, " "); + for (std::vector<std::string>::iterator i = words.begin(); i != words.end(); ++i) + LocalizeOverviewToken(*i); + str = StringUtils::Join(words, " "); +} + +void CWeatherJob::FormatTemperature(std::string &text, double temp) +{ + CTemperature temperature = CTemperature::CreateFromCelsius(temp); + text = StringUtils::Format("{:.0f}", temperature.To(g_langInfo.GetTemperatureUnit())); +} + +void CWeatherJob::LoadLocalizedToken() +{ + // We load the english strings in to get our tokens + std::string language = LANGUAGE_DEFAULT; + std::shared_ptr<CSettingString> languageSetting = std::static_pointer_cast<CSettingString>(CServiceBroker::GetSettingsComponent()->GetSettings()->GetSetting(CSettings::SETTING_LOCALE_LANGUAGE)); + if (languageSetting != NULL) + language = languageSetting->GetDefault(); + + // Load the strings.po file + CPODocument PODoc; + if (PODoc.LoadFile(URIUtils::AddFileToFolder(CLangInfo::GetLanguagePath(language), "strings.po"))) + { + int counter = 0; + + while (PODoc.GetNextEntry()) + { + if (PODoc.GetEntryType() != ID_FOUND) + continue; + + uint32_t id = PODoc.GetEntryID(); + PODoc.ParseEntry(ISSOURCELANG); + + if (id > LOCALIZED_TOKEN_LASTID2) break; + if ((LOCALIZED_TOKEN_FIRSTID <= id && id <= LOCALIZED_TOKEN_LASTID) || + (LOCALIZED_TOKEN_FIRSTID2 <= id && id <= LOCALIZED_TOKEN_LASTID2) || + (LOCALIZED_TOKEN_FIRSTID3 <= id && id <= LOCALIZED_TOKEN_LASTID3) || + (LOCALIZED_TOKEN_FIRSTID4 <= id && id <= LOCALIZED_TOKEN_LASTID4)) + { + if (!PODoc.GetMsgid().empty()) + { + m_localizedTokens.insert(make_pair(PODoc.GetMsgid(), id)); + counter++; + } + } + } + + CLog::Log(LOGDEBUG, "POParser: loaded {} weather tokens", counter); + return; + } +} + +std::string CWeatherJob::ConstructPath(std::string in) // copy intended +{ + if (in.find('/') != std::string::npos || in.find('\\') != std::string::npos) + return in; + if (in.empty() || in == "N/A") + in = "na.png"; + + return URIUtils::AddFileToFolder(ICON_ADDON_PATH, in); +} + +void CWeatherJob::SetFromProperties() +{ + // Load in our tokens if necessary + if (m_localizedTokens.empty()) + LoadLocalizedToken(); + + CGUIWindow* window = CServiceBroker::GetGUI()->GetWindowManager().GetWindow(WINDOW_WEATHER); + if (window) + { + CDateTime time = CDateTime::GetCurrentDateTime(); + m_info.lastUpdateTime = time.GetAsLocalizedDateTime(false, false); + m_info.currentConditions = window->GetProperty("Current.Condition").asString(); + m_info.currentIcon = ConstructPath(window->GetProperty("Current.OutlookIcon").asString()); + LocalizeOverview(m_info.currentConditions); + FormatTemperature(m_info.currentTemperature, + strtod(window->GetProperty("Current.Temperature").asString().c_str(), nullptr)); + FormatTemperature(m_info.currentFeelsLike, + strtod(window->GetProperty("Current.FeelsLike").asString().c_str(), nullptr)); + m_info.currentUVIndex = window->GetProperty("Current.UVIndex").asString(); + LocalizeOverview(m_info.currentUVIndex); + CSpeed speed = CSpeed::CreateFromKilometresPerHour(strtol(window->GetProperty("Current.Wind").asString().c_str(),0,10)); + std::string direction = window->GetProperty("Current.WindDirection").asString(); + if (direction == "CALM") + m_info.currentWind = g_localizeStrings.Get(1410); + else + { + LocalizeOverviewToken(direction); + m_info.currentWind = StringUtils::Format(g_localizeStrings.Get(434), direction, + (int)speed.To(g_langInfo.GetSpeedUnit()), + g_langInfo.GetSpeedUnitString()); + } + std::string windspeed = StringUtils::Format("{} {}", (int)speed.To(g_langInfo.GetSpeedUnit()), + g_langInfo.GetSpeedUnitString()); + window->SetProperty("Current.WindSpeed",windspeed); + FormatTemperature(m_info.currentDewPoint, + strtod(window->GetProperty("Current.DewPoint").asString().c_str(), nullptr)); + if (window->GetProperty("Current.Humidity").asString().empty()) + m_info.currentHumidity.clear(); + else + m_info.currentHumidity = + StringUtils::Format("{}%", window->GetProperty("Current.Humidity").asString()); + m_info.location = window->GetProperty("Current.Location").asString(); + for (int i=0;i<NUM_DAYS;++i) + { + std::string strDay = StringUtils::Format("Day{}.Title", i); + m_info.forecast[i].m_day = window->GetProperty(strDay).asString(); + LocalizeOverviewToken(m_info.forecast[i].m_day); + strDay = StringUtils::Format("Day{}.HighTemp", i); + FormatTemperature(m_info.forecast[i].m_high, + strtod(window->GetProperty(strDay).asString().c_str(), nullptr)); + strDay = StringUtils::Format("Day{}.LowTemp", i); + FormatTemperature(m_info.forecast[i].m_low, + strtod(window->GetProperty(strDay).asString().c_str(), nullptr)); + strDay = StringUtils::Format("Day{}.OutlookIcon", i); + m_info.forecast[i].m_icon = ConstructPath(window->GetProperty(strDay).asString()); + strDay = StringUtils::Format("Day{}.Outlook", i); + m_info.forecast[i].m_overview = window->GetProperty(strDay).asString(); + LocalizeOverview(m_info.forecast[i].m_overview); + } + } +} diff --git a/xbmc/weather/WeatherJob.h b/xbmc/weather/WeatherJob.h new file mode 100644 index 0000000..989dac3 --- /dev/null +++ b/xbmc/weather/WeatherJob.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2012-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "WeatherManager.h" + +#include <map> +#include <string> + +class CWeatherJob : public CJob +{ +public: + explicit CWeatherJob(int location); + + bool DoWork() override; + + const CWeatherInfo &GetInfo() const; +private: + static std::string ConstructPath(std::string in); + void LocalizeOverview(std::string &str); + void LocalizeOverviewToken(std::string &str); + void LoadLocalizedToken(); + static int ConvertSpeed(int speed); + + void SetFromProperties(); + + /*! \brief Formats a celsius temperature into a string based on the users locale + \param text the string to format + \param temp the temperature (in degrees celsius). + */ + static void FormatTemperature(std::string &text, double temp); + + struct ci_less + { + // case-independent (ci) compare_less binary function + struct nocase_compare + { + bool operator() (const unsigned char& c1, const unsigned char& c2) const { + return tolower(c1) < tolower(c2); + } + }; + bool operator()(const std::string & s1, const std::string & s2) const { + return std::lexicographical_compare + (s1.begin(), s1.end(), + s2.begin(), s2.end(), + nocase_compare()); + } + }; + + std::map<std::string, int, ci_less> m_localizedTokens; + typedef std::map<std::string, int, ci_less>::const_iterator ilocalizedTokens; + + CWeatherInfo m_info; + int m_location; + + static bool m_imagesOkay; +}; diff --git a/xbmc/weather/WeatherManager.cpp b/xbmc/weather/WeatherManager.cpp new file mode 100644 index 0000000..98f576f --- /dev/null +++ b/xbmc/weather/WeatherManager.cpp @@ -0,0 +1,189 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "WeatherManager.h" + +#include "LangInfo.h" +#include "ServiceBroker.h" +#include "WeatherJob.h" +#include "addons/AddonManager.h" +#include "addons/addoninfo/AddonType.h" +#include "addons/gui/GUIDialogAddonSettings.h" +#include "guilib/GUIComponent.h" +#include "guilib/GUIWindowManager.h" +#include "guilib/WindowIDs.h" +#include "settings/Settings.h" +#include "settings/SettingsComponent.h" +#include "settings/lib/Setting.h" +#include "settings/lib/SettingsManager.h" +#include "utils/StringUtils.h" +#include "utils/URIUtils.h" +#include "utils/Variant.h" +#include "utils/XMLUtils.h" + +using namespace ADDON; + +CWeatherManager::CWeatherManager(void) : CInfoLoader(30 * 60 * 1000) // 30 minutes +{ + CServiceBroker::GetSettingsComponent()->GetSettings()->GetSettingsManager()->RegisterCallback(this, { + CSettings::SETTING_WEATHER_ADDON, + CSettings::SETTING_WEATHER_ADDONSETTINGS + }); + + Reset(); +} + +CWeatherManager::~CWeatherManager(void) +{ + const auto settingsComponent = CServiceBroker::GetSettingsComponent(); + if (!settingsComponent) + return; + + const std::shared_ptr<CSettings> settings = settingsComponent->GetSettings(); + if (!settings) + return; + + settings->GetSettingsManager()->UnregisterCallback(this); +} + +std::string CWeatherManager::BusyInfo(int info) const +{ + if (info == WEATHER_IMAGE_CURRENT_ICON) + return URIUtils::AddFileToFolder(ICON_ADDON_PATH, "na.png"); + + return CInfoLoader::BusyInfo(info); +} + +std::string CWeatherManager::TranslateInfo(int info) const +{ + switch (info) { + case WEATHER_LABEL_CURRENT_COND: + return m_info.currentConditions; + case WEATHER_IMAGE_CURRENT_ICON: + return m_info.currentIcon; + case WEATHER_LABEL_CURRENT_TEMP: + return m_info.currentTemperature; + case WEATHER_LABEL_CURRENT_FEEL: + return m_info.currentFeelsLike; + case WEATHER_LABEL_CURRENT_UVID: + return m_info.currentUVIndex; + case WEATHER_LABEL_CURRENT_WIND: + return m_info.currentWind; + case WEATHER_LABEL_CURRENT_DEWP: + return m_info.currentDewPoint; + case WEATHER_LABEL_CURRENT_HUMI: + return m_info.currentHumidity; + case WEATHER_LABEL_LOCATION: + return m_info.location; + default: + return ""; + } +} + +/*! + \brief Retrieve the city name for the specified location from the settings + \param iLocation the location index (can be in the range [1..MAXLOCATION]) + \return the city name (without the accompanying region area code) + */ +std::string CWeatherManager::GetLocation(int iLocation) +{ + CGUIWindow* window = CServiceBroker::GetGUI()->GetWindowManager().GetWindow(WINDOW_WEATHER); + if (window) + { + std::string setting = StringUtils::Format("Location{}", iLocation); + return window->GetProperty(setting).asString(); + } + return ""; +} + +void CWeatherManager::Reset() +{ + m_info.Reset(); +} + +bool CWeatherManager::IsFetched() +{ + // call GetInfo() to make sure that we actually start up + GetInfo(0); + return !m_info.lastUpdateTime.empty(); +} + +const ForecastDay &CWeatherManager::GetForecast(int day) const +{ + return m_info.forecast[day]; +} + +/*! + \brief Saves the specified location index to the settings. Call Refresh() + afterwards to update weather info for the new location. + \param iLocation the new location index (can be in the range [1..MAXLOCATION]) + */ +void CWeatherManager::SetArea(int iLocation) +{ + const std::shared_ptr<CSettings> settings = CServiceBroker::GetSettingsComponent()->GetSettings(); + settings->SetInt(CSettings::SETTING_WEATHER_CURRENTLOCATION, iLocation); + settings->Save(); +} + +/*! + \brief Retrieves the current location index from the settings + \return the active location index (will be in the range [1..MAXLOCATION]) + */ +int CWeatherManager::GetArea() const +{ + return CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(CSettings::SETTING_WEATHER_CURRENTLOCATION); +} + +CJob *CWeatherManager::GetJob() const +{ + return new CWeatherJob(GetArea()); +} + +void CWeatherManager::OnJobComplete(unsigned int jobID, bool success, CJob *job) +{ + m_info = static_cast<CWeatherJob*>(job)->GetInfo(); + CInfoLoader::OnJobComplete(jobID, success, job); +} + +void CWeatherManager::OnSettingChanged(const std::shared_ptr<const CSetting>& setting) +{ + if (setting == NULL) + return; + + const std::string settingId = setting->GetId(); + if (settingId == CSettings::SETTING_WEATHER_ADDON) + { + // clear "WeatherProviderLogo" property that some weather addons set + CGUIWindow* window = CServiceBroker::GetGUI()->GetWindowManager().GetWindow(WINDOW_WEATHER); + if (window != nullptr) + window->SetProperty("WeatherProviderLogo", ""); + Refresh(); + } +} + +void CWeatherManager::OnSettingAction(const std::shared_ptr<const CSetting>& setting) +{ + if (setting == NULL) + return; + + const std::string settingId = setting->GetId(); + if (settingId == CSettings::SETTING_WEATHER_ADDONSETTINGS) + { + AddonPtr addon; + if (CServiceBroker::GetAddonMgr().GetAddon( + CServiceBroker::GetSettingsComponent()->GetSettings()->GetString( + CSettings::SETTING_WEATHER_ADDON), + addon, AddonType::SCRIPT_WEATHER, OnlyEnabled::CHOICE_YES) && + addon != NULL) + { //! @todo maybe have ShowAndGetInput return a bool if settings changed, then only reset weather if true. + CGUIDialogAddonSettings::ShowForAddon(addon); + Refresh(); + } + } +} + diff --git a/xbmc/weather/WeatherManager.h b/xbmc/weather/WeatherManager.h new file mode 100644 index 0000000..c729f4c --- /dev/null +++ b/xbmc/weather/WeatherManager.h @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "settings/lib/ISettingCallback.h" +#include "utils/InfoLoader.h" + +#include <string> + +#define WEATHER_LABEL_LOCATION 10 +#define WEATHER_IMAGE_CURRENT_ICON 21 +#define WEATHER_LABEL_CURRENT_COND 22 +#define WEATHER_LABEL_CURRENT_TEMP 23 +#define WEATHER_LABEL_CURRENT_FEEL 24 +#define WEATHER_LABEL_CURRENT_UVID 25 +#define WEATHER_LABEL_CURRENT_WIND 26 +#define WEATHER_LABEL_CURRENT_DEWP 27 +#define WEATHER_LABEL_CURRENT_HUMI 28 + +static const std::string ICON_ADDON_PATH = "resource://resource.images.weathericons.default"; + +struct ForecastDay +{ + std::string m_icon; + std::string m_overview; + std::string m_day; + std::string m_high; + std::string m_low; +}; + +#define NUM_DAYS 7 + +class CWeatherInfo +{ +public: + ForecastDay forecast[NUM_DAYS]; + + void Reset() + { + lastUpdateTime.clear(); + currentIcon.clear(); + currentConditions.clear(); + currentTemperature.clear(); + currentFeelsLike.clear(); + currentWind.clear(); + currentHumidity.clear(); + currentUVIndex.clear(); + currentDewPoint.clear(); + + for (ForecastDay& f : forecast) + { + f.m_icon.clear(); + f.m_overview.clear(); + f.m_day.clear(); + f.m_high.clear(); + f.m_low.clear(); + } + }; + + std::string lastUpdateTime; + std::string location; + std::string currentIcon; + std::string currentConditions; + std::string currentTemperature; + std::string currentFeelsLike; + std::string currentUVIndex; + std::string currentWind; + std::string currentDewPoint; + std::string currentHumidity; + std::string busyString; + std::string naIcon; +}; + +class CWeatherManager +: public CInfoLoader, public ISettingCallback +{ +public: + CWeatherManager(void); + ~CWeatherManager(void) override; + static bool GetSearchResults(const std::string &strSearch, std::string &strResult); + + std::string GetLocation(int iLocation); + const std::string& GetLastUpdateTime() const { return m_info.lastUpdateTime; } + const ForecastDay &GetForecast(int day) const; + bool IsFetched(); + void Reset(); + + void SetArea(int iLocation); + int GetArea() const; +protected: + CJob *GetJob() const override; + std::string TranslateInfo(int info) const override; + std::string BusyInfo(int info) const override; + void OnJobComplete(unsigned int jobID, bool success, CJob *job) override; + + void OnSettingChanged(const std::shared_ptr<const CSetting>& setting) override; + void OnSettingAction(const std::shared_ptr<const CSetting>& setting) override; + +private: + + CWeatherInfo m_info; +}; |