1 /*
2  *  Copyright (C) 2012-2018 Team Kodi
3  *  This file is part of Kodi - https://kodi.tv
4  *
5  *  SPDX-License-Identifier: GPL-2.0-or-later
6  *  See LICENSES/README.md for more information.
7  */
8 
9 #include "WeatherJob.h"
10 
11 #include "GUIUserMessages.h"
12 #include "LangInfo.h"
13 #include "ServiceBroker.h"
14 #include "addons/AddonManager.h"
15 #include "guilib/GUIComponent.h"
16 #include "guilib/GUIWindowManager.h"
17 #include "guilib/LocalizeStrings.h"
18 #include "interfaces/generic/ScriptInvocationManager.h"
19 #include "network/Network.h"
20 #include "settings/Settings.h"
21 #include "settings/SettingsComponent.h"
22 #include "settings/lib/Setting.h"
23 #include "utils/POUtils.h"
24 #include "utils/StringUtils.h"
25 #include "utils/URIUtils.h"
26 #include "utils/Variant.h"
27 #include "utils/XTimeUtils.h"
28 #include "utils/log.h"
29 
30 #define LOCALIZED_TOKEN_FIRSTID    370
31 #define LOCALIZED_TOKEN_LASTID     395
32 #define LOCALIZED_TOKEN_FIRSTID2  1350
33 #define LOCALIZED_TOKEN_LASTID2   1449
34 #define LOCALIZED_TOKEN_FIRSTID3    11
35 #define LOCALIZED_TOKEN_LASTID3     17
36 #define LOCALIZED_TOKEN_FIRSTID4    71
37 #define LOCALIZED_TOKEN_LASTID4     97
38 
39 using namespace ADDON;
40 
CWeatherJob(int location)41 CWeatherJob::CWeatherJob(int location)
42 {
43   m_location = location;
44 }
45 
DoWork()46 bool CWeatherJob::DoWork()
47 {
48   // wait for the network
49   if (!CServiceBroker::GetNetwork().IsAvailable())
50     return false;
51 
52   AddonPtr addon;
53   if (!CServiceBroker::GetAddonMgr().GetAddon(
54           CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(
55               CSettings::SETTING_WEATHER_ADDON),
56           addon, ADDON_SCRIPT_WEATHER, OnlyEnabled::YES))
57     return false;
58 
59   // initialize our sys.argv variables
60   std::vector<std::string> argv;
61   argv.push_back(addon->LibPath());
62 
63   std::string strSetting = StringUtils::Format("%i", m_location);
64   argv.push_back(strSetting);
65 
66   // Download our weather
67   CLog::Log(LOGINFO, "WEATHER: Downloading weather");
68   // call our script, passing the areacode
69   int scriptId = -1;
70   if ((scriptId = CScriptInvocationManager::GetInstance().ExecuteAsync(argv[0], addon, argv)) >= 0)
71   {
72     while (true)
73     {
74       if (!CScriptInvocationManager::GetInstance().IsRunning(scriptId))
75         break;
76       KODI::TIME::Sleep(100);
77     }
78 
79     SetFromProperties();
80 
81     // and send a message that we're done
82     CGUIMessage msg(GUI_MSG_NOTIFY_ALL,0,0,GUI_MSG_WEATHER_FETCHED);
83     CServiceBroker::GetGUI()->GetWindowManager().SendThreadMessage(msg);
84   }
85   else
86     CLog::Log(LOGERROR, "WEATHER: Weather download failed!");
87 
88   return true;
89 }
90 
GetInfo() const91 const CWeatherInfo &CWeatherJob::GetInfo() const
92 {
93   return m_info;
94 }
95 
LocalizeOverviewToken(std::string & token)96 void CWeatherJob::LocalizeOverviewToken(std::string &token)
97 {
98   // This routine is case-insensitive.
99   std::string strLocStr;
100   if (!token.empty())
101   {
102     ilocalizedTokens i;
103     i = m_localizedTokens.find(token);
104     if (i != m_localizedTokens.end())
105     {
106       strLocStr = g_localizeStrings.Get(i->second);
107     }
108   }
109   if (strLocStr == "")
110     strLocStr = token; //if not found, let fallback
111   token = strLocStr;
112 }
113 
LocalizeOverview(std::string & str)114 void CWeatherJob::LocalizeOverview(std::string &str)
115 {
116   std::vector<std::string> words = StringUtils::Split(str, " ");
117   for (std::vector<std::string>::iterator i = words.begin(); i != words.end(); ++i)
118     LocalizeOverviewToken(*i);
119   str = StringUtils::Join(words, " ");
120 }
121 
FormatTemperature(std::string & text,double temp)122 void CWeatherJob::FormatTemperature(std::string &text, double temp)
123 {
124   CTemperature temperature = CTemperature::CreateFromCelsius(temp);
125   text = StringUtils::Format("%.0f", temperature.To(g_langInfo.GetTemperatureUnit()));
126 }
127 
LoadLocalizedToken()128 void CWeatherJob::LoadLocalizedToken()
129 {
130   // We load the english strings in to get our tokens
131   std::string language = LANGUAGE_DEFAULT;
132   std::shared_ptr<CSettingString> languageSetting = std::static_pointer_cast<CSettingString>(CServiceBroker::GetSettingsComponent()->GetSettings()->GetSetting(CSettings::SETTING_LOCALE_LANGUAGE));
133   if (languageSetting != NULL)
134     language = languageSetting->GetDefault();
135 
136   // Load the strings.po file
137   CPODocument PODoc;
138   if (PODoc.LoadFile(URIUtils::AddFileToFolder(CLangInfo::GetLanguagePath(language), "strings.po")))
139   {
140     int counter = 0;
141 
142     while (PODoc.GetNextEntry())
143     {
144       if (PODoc.GetEntryType() != ID_FOUND)
145         continue;
146 
147       uint32_t id = PODoc.GetEntryID();
148       PODoc.ParseEntry(ISSOURCELANG);
149 
150       if (id > LOCALIZED_TOKEN_LASTID2) break;
151       if ((LOCALIZED_TOKEN_FIRSTID  <= id && id <= LOCALIZED_TOKEN_LASTID)  ||
152           (LOCALIZED_TOKEN_FIRSTID2 <= id && id <= LOCALIZED_TOKEN_LASTID2) ||
153           (LOCALIZED_TOKEN_FIRSTID3 <= id && id <= LOCALIZED_TOKEN_LASTID3) ||
154           (LOCALIZED_TOKEN_FIRSTID4 <= id && id <= LOCALIZED_TOKEN_LASTID4))
155       {
156         if (!PODoc.GetMsgid().empty())
157         {
158           m_localizedTokens.insert(make_pair(PODoc.GetMsgid(), id));
159           counter++;
160         }
161       }
162     }
163 
164     CLog::Log(LOGDEBUG, "POParser: loaded %i weather tokens", counter);
165     return;
166   }
167 }
168 
ConstructPath(std::string in)169 std::string CWeatherJob::ConstructPath(std::string in) // copy intended
170 {
171   if (in.find('/') != std::string::npos || in.find('\\') != std::string::npos)
172     return in;
173   if (in.empty() || in == "N/A")
174     in = "na.png";
175 
176   return URIUtils::AddFileToFolder(ICON_ADDON_PATH, in);
177 }
178 
SetFromProperties()179 void CWeatherJob::SetFromProperties()
180 {
181   // Load in our tokens if necessary
182   if (m_localizedTokens.empty())
183     LoadLocalizedToken();
184 
185   CGUIWindow* window = CServiceBroker::GetGUI()->GetWindowManager().GetWindow(WINDOW_WEATHER);
186   if (window)
187   {
188     CDateTime time = CDateTime::GetCurrentDateTime();
189     m_info.lastUpdateTime = time.GetAsLocalizedDateTime(false, false);
190     m_info.currentConditions = window->GetProperty("Current.Condition").asString();
191     m_info.currentIcon = ConstructPath(window->GetProperty("Current.OutlookIcon").asString());
192     LocalizeOverview(m_info.currentConditions);
193     FormatTemperature(m_info.currentTemperature,
194                       strtod(window->GetProperty("Current.Temperature").asString().c_str(), nullptr));
195     FormatTemperature(m_info.currentFeelsLike,
196                       strtod(window->GetProperty("Current.FeelsLike").asString().c_str(), nullptr));
197     m_info.currentUVIndex = window->GetProperty("Current.UVIndex").asString();
198     LocalizeOverview(m_info.currentUVIndex);
199     CSpeed speed = CSpeed::CreateFromKilometresPerHour(strtol(window->GetProperty("Current.Wind").asString().c_str(),0,10));
200     std::string direction = window->GetProperty("Current.WindDirection").asString();
201     if (direction == "CALM")
202       m_info.currentWind = g_localizeStrings.Get(1410);
203     else
204     {
205       LocalizeOverviewToken(direction);
206       m_info.currentWind = StringUtils::Format(g_localizeStrings.Get(434).c_str(),
207                                                direction.c_str(), (int)speed.To(g_langInfo.GetSpeedUnit()), g_langInfo.GetSpeedUnitString().c_str());
208     }
209     std::string windspeed = StringUtils::Format("%i %s", (int)speed.To(g_langInfo.GetSpeedUnit()), g_langInfo.GetSpeedUnitString().c_str());
210     window->SetProperty("Current.WindSpeed",windspeed);
211     FormatTemperature(m_info.currentDewPoint,
212                       strtod(window->GetProperty("Current.DewPoint").asString().c_str(), nullptr));
213     if (window->GetProperty("Current.Humidity").asString().empty())
214       m_info.currentHumidity.clear();
215     else
216       m_info.currentHumidity = StringUtils::Format("%s%%", window->GetProperty("Current.Humidity").asString().c_str());
217     m_info.location = window->GetProperty("Current.Location").asString();
218     for (int i=0;i<NUM_DAYS;++i)
219     {
220       std::string strDay = StringUtils::Format("Day%i.Title",i);
221       m_info.forecast[i].m_day = window->GetProperty(strDay).asString();
222       LocalizeOverviewToken(m_info.forecast[i].m_day);
223       strDay = StringUtils::Format("Day%i.HighTemp",i);
224       FormatTemperature(m_info.forecast[i].m_high,
225                         strtod(window->GetProperty(strDay).asString().c_str(), nullptr));
226       strDay = StringUtils::Format("Day%i.LowTemp",i);
227       FormatTemperature(m_info.forecast[i].m_low,
228                         strtod(window->GetProperty(strDay).asString().c_str(), nullptr));
229       strDay = StringUtils::Format("Day%i.OutlookIcon",i);
230       m_info.forecast[i].m_icon = ConstructPath(window->GetProperty(strDay).asString());
231       strDay = StringUtils::Format("Day%i.Outlook",i);
232       m_info.forecast[i].m_overview = window->GetProperty(strDay).asString();
233       LocalizeOverview(m_info.forecast[i].m_overview);
234     }
235   }
236 }
237