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