summaryrefslogtreecommitdiffstats
path: root/xbmc/weather
diff options
context:
space:
mode:
Diffstat (limited to 'xbmc/weather')
-rw-r--r--xbmc/weather/CMakeLists.txt9
-rw-r--r--xbmc/weather/GUIWindowWeather.cpp299
-rw-r--r--xbmc/weather/GUIWindowWeather.h31
-rw-r--r--xbmc/weather/WeatherJob.cpp243
-rw-r--r--xbmc/weather/WeatherJob.h63
-rw-r--r--xbmc/weather/WeatherManager.cpp189
-rw-r--r--xbmc/weather/WeatherManager.h108
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;
+};