aboutsummaryrefslogtreecommitdiff
path: root/src/utils/Weather.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/utils/Weather.cpp')
-rw-r--r--src/utils/Weather.cpp501
1 files changed, 501 insertions, 0 deletions
diff --git a/src/utils/Weather.cpp b/src/utils/Weather.cpp
new file mode 100644
index 0000000000..a85cb15538
--- /dev/null
+++ b/src/utils/Weather.cpp
@@ -0,0 +1,501 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#if (defined HAVE_CONFIG_H) && (!defined TARGET_WINDOWS)
+ #include "config.h"
+#endif
+#include "Weather.h"
+#include "filesystem/ZipManager.h"
+#include "XMLUtils.h"
+#include "utils/POUtils.h"
+#include "Temperature.h"
+#include "network/Network.h"
+#include "Application.h"
+#include "settings/lib/Setting.h"
+#include "settings/Settings.h"
+#include "guilib/GUIWindowManager.h"
+#include "GUIUserMessages.h"
+#include "XBDateTime.h"
+#include "LangInfo.h"
+#include "guilib/WindowIDs.h"
+#include "guilib/LocalizeStrings.h"
+#include "filesystem/Directory.h"
+#include "StringUtils.h"
+#include "URIUtils.h"
+#include "log.h"
+#include "addons/AddonManager.h"
+#include "interfaces/generic/ScriptInvocationManager.h"
+#include "CharsetConverter.h"
+#include "addons/GUIDialogAddonSettings.h"
+
+using namespace std;
+using namespace ADDON;
+using namespace XFILE;
+
+#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
+
+/*
+FIXME'S
+>strings are not centered
+*/
+
+#define WEATHER_BASE_PATH "special://temp/weather/"
+#define WEATHER_ICON_PATH "special://temp/weather/"
+#define WEATHER_SOURCE_FILE "special://xbmc/media/weather.zip"
+
+bool CWeatherJob::m_imagesOkay = false;
+
+CWeatherJob::CWeatherJob(int location)
+{
+ m_location = location;
+}
+
+bool CWeatherJob::DoWork()
+{
+ // wait for the network
+ if (!g_application.getNetwork().IsAvailable(true))
+ return false;
+
+ AddonPtr addon;
+ if (!ADDON::CAddonMgr::Get().GetAddon(CSettings::Get().GetString("weather.addon"), addon, ADDON_SCRIPT_WEATHER))
+ return false;
+
+ // initialize our sys.argv variables
+ std::vector<std::string> argv;
+ argv.push_back(addon->LibPath());
+
+ std::string strSetting = StringUtils::Format("%i", 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::Get().Execute(argv[0], addon, argv)) >= 0)
+ {
+ while (true)
+ {
+ if (!CScriptInvocationManager::Get().IsRunning(scriptId))
+ break;
+ Sleep(100);
+ }
+ if (!m_imagesOkay)
+ {
+ CDirectory::Create(WEATHER_BASE_PATH);
+ g_ZipManager.ExtractArchive(WEATHER_SOURCE_FILE, WEATHER_BASE_PATH);
+ m_imagesOkay = true;
+ }
+
+ SetFromProperties();
+
+ // and send a message that we're done
+ CGUIMessage msg(GUI_MSG_NOTIFY_ALL,0,0,GUI_MSG_WEATHER_FETCHED);
+ g_windowManager.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)
+{
+ vector<string> words = StringUtils::Split(str, " ");
+ for (vector<string>::iterator i = words.begin(); i != words.end(); ++i)
+ LocalizeOverviewToken(*i);
+ str = StringUtils::Join(words, " ");
+}
+
+// input param must be kmh
+int CWeatherJob::ConvertSpeed(int curSpeed)
+{
+ switch (g_langInfo.GetSpeedUnit())
+ {
+ case CLangInfo::SPEED_UNIT_KMH:
+ break;
+ case CLangInfo::SPEED_UNIT_MPS:
+ curSpeed=(int)(curSpeed * (1000.0 / 3600.0) + 0.5);
+ break;
+ case CLangInfo::SPEED_UNIT_MPH:
+ curSpeed=(int)(curSpeed / (8.0 / 5.0));
+ break;
+ case CLangInfo::SPEED_UNIT_MPMIN:
+ curSpeed=(int)(curSpeed * (1000.0 / 3600.0) + 0.5*60);
+ break;
+ case CLangInfo::SPEED_UNIT_FTH:
+ curSpeed=(int)(curSpeed * 3280.8398888889f);
+ break;
+ case CLangInfo::SPEED_UNIT_FTMIN:
+ curSpeed=(int)(curSpeed * 54.6805555556f);
+ break;
+ case CLangInfo::SPEED_UNIT_FTS:
+ curSpeed=(int)(curSpeed * 0.911344f);
+ break;
+ case CLangInfo::SPEED_UNIT_KTS:
+ curSpeed=(int)(curSpeed * 0.5399568f);
+ break;
+ case CLangInfo::SPEED_UNIT_INCHPS:
+ curSpeed=(int)(curSpeed * 10.9361388889f);
+ break;
+ case CLangInfo::SPEED_UNIT_YARDPS:
+ curSpeed=(int)(curSpeed * 0.3037814722f);
+ break;
+ case CLangInfo::SPEED_UNIT_FPF:
+ curSpeed=(int)(curSpeed * 1670.25f);
+ break;
+ case CLangInfo::SPEED_UNIT_BEAUFORT:
+ {
+ float knot=(float)curSpeed * 0.5399568f; // to kts first
+ if(knot<=1.0) curSpeed=0;
+ if(knot>1.0 && knot<3.5) curSpeed=1;
+ if(knot>=3.5 && knot<6.5) curSpeed=2;
+ if(knot>=6.5 && knot<10.5) curSpeed=3;
+ if(knot>=10.5 && knot<16.5) curSpeed=4;
+ if(knot>=16.5 && knot<21.5) curSpeed=5;
+ if(knot>=21.5 && knot<27.5) curSpeed=6;
+ if(knot>=27.5 && knot<33.5) curSpeed=7;
+ if(knot>=33.5 && knot<40.5) curSpeed=8;
+ if(knot>=40.5 && knot<47.5) curSpeed=9;
+ if(knot>=47.5 && knot<55.5) curSpeed=10;
+ if(knot>=55.5 && knot<63.5) curSpeed=11;
+ if(knot>=63.5 && knot<74.5) curSpeed=12;
+ if(knot>=74.5 && knot<80.5) curSpeed=13;
+ if(knot>=80.5 && knot<89.5) curSpeed=14;
+ if(knot>=89.5) curSpeed=15;
+ }
+ break;
+ default:
+ assert(false);
+ }
+
+ return curSpeed;
+}
+
+void CWeatherJob::FormatTemperature(std::string &text, int temp)
+{
+ CTemperature temperature = CTemperature::CreateFromCelsius(temp);
+ text = StringUtils::Format("%.0f", temperature.ToLocale());
+}
+
+void CWeatherJob::LoadLocalizedToken()
+{
+ // We load the english strings in to get our tokens
+
+ // Try the strings PO file first
+ CPODocument PODoc;
+ if (PODoc.LoadFile("special://xbmc/language/English/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 %i weather tokens", counter);
+ return;
+ }
+
+ CLog::Log(LOGDEBUG,
+ "Weather: no PO string file available, to load English tokens, "
+ "fallback to strings.xml file");
+
+ // We load the tokens from the strings.xml file
+ std::string strLanguagePath = "special://xbmc/language/English/strings.xml";
+
+ CXBMCTinyXML xmlDoc;
+ if (!xmlDoc.LoadFile(strLanguagePath) || !xmlDoc.RootElement())
+ {
+ CLog::Log(LOGERROR, "Weather: unable to load %s: %s at line %d", strLanguagePath.c_str(), xmlDoc.ErrorDesc(), xmlDoc.ErrorRow());
+ return;
+ }
+
+ TiXmlElement* pRootElement = xmlDoc.RootElement();
+ if (pRootElement->ValueStr() != "strings")
+ return;
+
+ const TiXmlElement *pChild = pRootElement->FirstChildElement();
+ while (pChild)
+ {
+ std::string strValue = pChild->ValueStr();
+ if (strValue == "string")
+ { // Load new style language file with id as attribute
+ const char* attrId = pChild->Attribute("id");
+ if (attrId && !pChild->NoChildren())
+ {
+ int id = atoi(attrId);
+ 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))
+ {
+ std::string utf8Label(pChild->FirstChild()->ValueStr());
+ if (!utf8Label.empty())
+ m_localizedTokens.insert(make_pair(utf8Label, id));
+ }
+ }
+ }
+ pChild = pChild->NextSiblingElement();
+ }
+}
+
+static std::string 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(WEATHER_ICON_PATH,in);
+}
+
+void CWeatherJob::SetFromProperties()
+{
+ // Load in our tokens if necessary
+ if (m_localizedTokens.empty())
+ LoadLocalizedToken();
+
+ CGUIWindow* window = g_windowManager.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,
+ strtol(window->GetProperty("Current.Temperature").asString().c_str(),0,10));
+ FormatTemperature(m_info.currentFeelsLike,
+ strtol(window->GetProperty("Current.FeelsLike").asString().c_str(),0,10));
+ m_info.currentUVIndex = window->GetProperty("Current.UVIndex").asString();
+ LocalizeOverview(m_info.currentUVIndex);
+ int speed = ConvertSpeed(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).c_str(),
+ direction.c_str(), speed, g_langInfo.GetSpeedUnitString().c_str());
+ }
+ std::string windspeed = StringUtils::Format("%i %s",speed,g_langInfo.GetSpeedUnitString().c_str());
+ window->SetProperty("Current.WindSpeed",windspeed);
+ FormatTemperature(m_info.currentDewPoint,
+ strtol(window->GetProperty("Current.DewPoint").asString().c_str(),0,10));
+ if (window->GetProperty("Current.Humidity").asString().empty())
+ m_info.currentHumidity.clear();
+ else
+ m_info.currentHumidity = StringUtils::Format("%s%%", window->GetProperty("Current.Humidity").asString().c_str());
+ m_info.location = window->GetProperty("Current.Location").asString();
+ for (int i=0;i<NUM_DAYS;++i)
+ {
+ std::string strDay = StringUtils::Format("Day%i.Title",i);
+ m_info.forecast[i].m_day = window->GetProperty(strDay).asString();
+ LocalizeOverviewToken(m_info.forecast[i].m_day);
+ strDay = StringUtils::Format("Day%i.HighTemp",i);
+ FormatTemperature(m_info.forecast[i].m_high,
+ strtol(window->GetProperty(strDay).asString().c_str(),0,10));
+ strDay = StringUtils::Format("Day%i.LowTemp",i);
+ FormatTemperature(m_info.forecast[i].m_low,
+ strtol(window->GetProperty(strDay).asString().c_str(),0,10));
+ strDay = StringUtils::Format("Day%i.OutlookIcon",i);
+ m_info.forecast[i].m_icon = ConstructPath(window->GetProperty(strDay).asString());
+ strDay = StringUtils::Format("Day%i.Outlook",i);
+ m_info.forecast[i].m_overview = window->GetProperty(strDay).asString();
+ LocalizeOverview(m_info.forecast[i].m_overview);
+ }
+ }
+}
+
+CWeather::CWeather(void) : CInfoLoader(30 * 60 * 1000) // 30 minutes
+{
+ Reset();
+}
+
+CWeather::~CWeather(void)
+{
+}
+
+std::string CWeather::BusyInfo(int info) const
+{
+ if (info == WEATHER_IMAGE_CURRENT_ICON)
+ return URIUtils::AddFileToFolder(WEATHER_ICON_PATH,"na.png");
+
+ return CInfoLoader::BusyInfo(info);
+}
+
+std::string CWeather::TranslateInfo(int info) const
+{
+ if (info == WEATHER_LABEL_CURRENT_COND) return m_info.currentConditions;
+ else if (info == WEATHER_IMAGE_CURRENT_ICON) return m_info.currentIcon;
+ else if (info == WEATHER_LABEL_CURRENT_TEMP) return m_info.currentTemperature;
+ else if (info == WEATHER_LABEL_CURRENT_FEEL) return m_info.currentFeelsLike;
+ else if (info == WEATHER_LABEL_CURRENT_UVID) return m_info.currentUVIndex;
+ else if (info == WEATHER_LABEL_CURRENT_WIND) return m_info.currentWind;
+ else if (info == WEATHER_LABEL_CURRENT_DEWP) return m_info.currentDewPoint;
+ else if (info == WEATHER_LABEL_CURRENT_HUMI) return m_info.currentHumidity;
+ else if (info == WEATHER_LABEL_LOCATION) return m_info.location;
+ 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 CWeather::GetLocation(int iLocation)
+{
+ CGUIWindow* window = g_windowManager.GetWindow(WINDOW_WEATHER);
+ if (window)
+ {
+ std::string setting = StringUtils::Format("Location%i", iLocation);
+ return window->GetProperty(setting).asString();
+ }
+ return "";
+}
+
+void CWeather::Reset()
+{
+ m_info.Reset();
+}
+
+bool CWeather::IsFetched()
+{
+ // call GetInfo() to make sure that we actually start up
+ GetInfo(0);
+ return !m_info.lastUpdateTime.empty();
+}
+
+const day_forecast &CWeather::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 CWeather::SetArea(int iLocation)
+{
+ CSettings::Get().SetInt("weather.currentlocation", iLocation);
+ CSettings::Get().Save();
+}
+
+/*!
+ \brief Retrieves the current location index from the settings
+ \return the active location index (will be in the range [1..MAXLOCATION])
+ */
+int CWeather::GetArea() const
+{
+ return CSettings::Get().GetInt("weather.currentlocation");
+}
+
+CJob *CWeather::GetJob() const
+{
+ return new CWeatherJob(GetArea());
+}
+
+void CWeather::OnJobComplete(unsigned int jobID, bool success, CJob *job)
+{
+ m_info = ((CWeatherJob *)job)->GetInfo();
+ CInfoLoader::OnJobComplete(jobID, success, job);
+}
+
+void CWeather::OnSettingChanged(const CSetting *setting)
+{
+ if (setting == NULL)
+ return;
+
+ const std::string settingId = setting->GetId();
+ if (settingId == "weather.addon")
+ {
+ // clear "WeatherProviderLogo" property that some weather addons set
+ CGUIWindow* window = g_windowManager.GetWindow(WINDOW_WEATHER);
+ window->SetProperty("WeatherProviderLogo", "");
+ Refresh();
+ }
+}
+
+void CWeather::OnSettingAction(const CSetting *setting)
+{
+ if (setting == NULL)
+ return;
+
+ const std::string settingId = setting->GetId();
+ if (settingId == "weather.addonsettings")
+ {
+ AddonPtr addon;
+ if (CAddonMgr::Get().GetAddon(CSettings::Get().GetString("weather.addon"), addon, ADDON_SCRIPT_WEATHER) && addon != NULL)
+ { // TODO: maybe have ShowAndGetInput return a bool if settings changed, then only reset weather if true.
+ CGUIDialogAddonSettings::ShowAndGetInput(addon);
+ Refresh();
+ }
+ }
+}
+