diff options
Diffstat (limited to 'src/utils/Weather.cpp')
-rw-r--r-- | src/utils/Weather.cpp | 501 |
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(); + } + } +} + |