1 #include "stdafx.h"
2 #include "AccuWeather.h"
3 #include "../main/Helper.h"
4 #include "../main/Logger.h"
5 #include "../httpclient/UrlEncode.h"
6 #include "hardwaretypes.h"
7 #include "../main/localtime_r.h"
8 #include "../httpclient/HTTPClient.h"
9 #include "../main/json_helper.h"
10 #include "../main/RFXtrx.h"
11 #include "../main/mainworker.h"
12
13 #define round(a) ( int ) ( a + .5 )
14
15 #ifdef _DEBUG
16 //#define DEBUG_AccuWeatherR
17 //#define DEBUG_AccuWeatherW
18 //#define DEBUG_AccuWeatherW2
19 #endif
20
21 #if defined(DEBUG_AccuWeatherW) || defined(DEBUG_AccuWeatherW2)
SaveString2Disk(std::string str,std::string filename)22 void SaveString2Disk(std::string str, std::string filename)
23 {
24 FILE *fOut = fopen(filename.c_str(), "wb+");
25 if (fOut)
26 {
27 fwrite(str.c_str(), 1, str.size(), fOut);
28 fclose(fOut);
29 }
30 }
31 #endif
32 #ifdef DEBUG_AccuWeatherR
ReadFile(std::string filename)33 std::string ReadFile(std::string filename)
34 {
35 std::ifstream file;
36 std::string sResult = "";
37 file.open(filename.c_str());
38 if (!file.is_open())
39 return "";
40 std::string sLine;
41 while (!file.eof())
42 {
43 getline(file, sLine);
44 sResult += sLine;
45 }
46 file.close();
47 return sResult;
48 }
49 #endif
50
CAccuWeather(const int ID,const std::string & APIKey,const std::string & Location)51 CAccuWeather::CAccuWeather(const int ID, const std::string &APIKey, const std::string &Location) :
52 m_APIKey(APIKey),
53 m_Location(Location),
54 m_LocationKey("")
55 {
56 m_HwdID=ID;
57 Init();
58 }
59
~CAccuWeather(void)60 CAccuWeather::~CAccuWeather(void)
61 {
62 }
63
Init()64 void CAccuWeather::Init()
65 {
66 }
67
StartHardware()68 bool CAccuWeather::StartHardware()
69 {
70 RequestStart();
71
72 Init();
73
74 //Start worker thread
75 m_thread = std::make_shared<std::thread>(&CAccuWeather::Do_Work, this);
76 SetThreadNameInt(m_thread->native_handle());
77 m_bIsStarted=true;
78 sOnConnected(this);
79 return (m_thread != nullptr);
80 }
81
StopHardware()82 bool CAccuWeather::StopHardware()
83 {
84 if (m_thread)
85 {
86 RequestStop();
87 m_thread->join();
88 m_thread.reset();
89 }
90 m_bIsStarted=false;
91 return true;
92 }
93
Do_Work()94 void CAccuWeather::Do_Work()
95 {
96 int sec_counter = 595;
97 while (!IsStopRequested(1000))
98 {
99 sec_counter++;
100 if (sec_counter % 12 == 0) {
101 m_LastHeartbeat = mytime(NULL);
102 }
103 if (sec_counter % 1800 == 0) //50 free calls a day.. thats not much guy's!
104 {
105 if (m_LocationKey.empty())
106 {
107 m_LocationKey = GetLocationKey();
108 if (m_LocationKey.empty())
109 continue;
110 }
111 GetMeterDetails();
112 }
113 }
114 Log(LOG_STATUS,"Worker stopped...");
115 }
116
WriteToHardware(const char *,const unsigned char)117 bool CAccuWeather::WriteToHardware(const char* /*pdata*/, const unsigned char /*length*/)
118 {
119 return false;
120 }
121
GetForecastURL()122 std::string CAccuWeather::GetForecastURL()
123 {
124 return m_ForecastURL;
125 }
126
GetLocationKey()127 std::string CAccuWeather::GetLocationKey()
128 {
129 std::string sResult;
130 #ifdef DEBUG_AccuWeatherR
131 sResult = ReadFile("E:\\AccuWeather_LocationSearch.json");
132 #else
133 std::stringstream sURL;
134 std::string szLoc = CURLEncode::URLEncode(m_Location);
135
136 sURL << "https://dataservice.accuweather.com/locations/v1/search?apikey=" << m_APIKey << "&q=" << szLoc;
137 try
138 {
139 if (!HTTPClient::GET(sURL.str(), sResult))
140 {
141 Log(LOG_ERROR, "Error getting http data!");
142 return "";
143 }
144 }
145 catch (...)
146 {
147 Log(LOG_ERROR, "Error getting http data!");
148 return "";
149 }
150 #endif
151 #ifdef DEBUG_AccuWeatherW2
152 SaveString2Disk(sResult, "E:\\AccuWeather_LocationSearch.json");
153 #endif
154 try
155 {
156 Json::Value root;
157 bool ret = ParseJSon(sResult, root);
158 if (!ret)
159 {
160 Log(LOG_ERROR, "Invalid data received!");
161 return "";
162 }
163 if (!root.empty())
164 {
165 if (root.isArray())
166 root = root[0];
167 if (!root.isObject())
168 {
169 Log(LOG_ERROR, "Invalid data received, or unknown location!");
170 return "";
171 }
172 if (root["Key"].empty())
173 {
174 Log(LOG_ERROR, "Invalid data received, or unknown location!");
175 return "";
176 }
177 return root["Key"].asString();
178 }
179 else
180 {
181 Log(LOG_ERROR, "Invalid data received, unknown location or API key!");
182 return "";
183 }
184 }
185 catch (...)
186 {
187 Log(LOG_ERROR, "Error parsing JSon data!");
188 }
189 return "";
190 }
191
GetMeterDetails()192 void CAccuWeather::GetMeterDetails()
193 {
194 std::string sResult;
195 #ifdef DEBUG_AccuWeatherR
196 sResult=ReadFile("E:\\AccuWeather.json");
197 #else
198 std::stringstream sURL;
199 std::string szLoc = CURLEncode::URLEncode(m_LocationKey);
200 sURL << "https://dataservice.accuweather.com/currentconditions/v1/" << szLoc << "?apikey=" << m_APIKey << "&details=true";
201 try
202 {
203 if (!HTTPClient::GET(sURL.str(), sResult))
204 {
205 Log(LOG_ERROR, "Error getting http data!");
206 return;
207 }
208 }
209 catch (...)
210 {
211 Log(LOG_ERROR, "Error getting http data!");
212 return;
213 }
214 #endif
215 #ifdef DEBUG_AccuWeatherW
216 SaveString2Disk(sResult, "E:\\AccuWeather.json");
217 #endif
218
219 try
220 {
221 Json::Value root;
222 bool ret = ParseJSon(sResult, root);
223 if (!ret)
224 {
225 Log(LOG_ERROR, "Invalid data received!");
226 return;
227 }
228
229 if (root.size() < 1)
230 {
231 Log(LOG_ERROR, "Invalid data received!");
232 return;
233 }
234 root = root[0];
235
236 if (root["LocalObservationDateTime"].empty())
237 {
238 Log(LOG_ERROR, "Invalid data received, or unknown location!");
239 return;
240 }
241
242 float temp = 0;
243 int humidity = 0;
244 int barometric = 0;
245 int barometric_forcast = baroForecastNoInfo;
246
247 if (!root["Temperature"].empty())
248 {
249 temp = root["Temperature"]["Metric"]["Value"].asFloat();
250 }
251
252 if (!root["RelativeHumidity"].empty())
253 {
254 humidity = root["RelativeHumidity"].asInt();
255 }
256 if (!root["Pressure"].empty())
257 {
258 barometric = atoi(root["Pressure"]["Metric"]["Value"].asString().c_str());
259 if (barometric < 1000)
260 barometric_forcast = baroForecastRain;
261 else if (barometric < 1020)
262 barometric_forcast = baroForecastCloudy;
263 else if (barometric < 1030)
264 barometric_forcast = baroForecastPartlyCloudy;
265 else
266 barometric_forcast = baroForecastSunny;
267
268 if (!root["WeatherIcon"].empty())
269 {
270 int forcasticon = atoi(root["WeatherIcon"].asString().c_str());
271 switch (forcasticon)
272 {
273 case 1:
274 case 2:
275 case 3:
276 barometric_forcast = baroForecastSunny;
277 break;
278 case 4:
279 case 5:
280 case 6:
281 barometric_forcast = baroForecastCloudy;
282 break;
283 case 7:
284 case 8:
285 case 9:
286 case 10:
287 case 11:
288 case 20:
289 case 21:
290 case 22:
291 case 23:
292 case 24:
293 case 25:
294 case 26:
295 case 27:
296 case 28:
297 case 29:
298 case 39:
299 case 40:
300 case 41:
301 case 42:
302 case 43:
303 case 44:
304 barometric_forcast = baroForecastRain;
305 break;
306 case 12:
307 case 13:
308 case 14:
309 case 15:
310 case 16:
311 case 17:
312 case 18:
313 case 19:
314 barometric_forcast = baroForecastCloudy;
315 break;
316 }
317 }
318 }
319
320 if (barometric != 0)
321 {
322 //Add temp+hum+baro device
323 SendTempHumBaroSensor(1, 255, temp, humidity, static_cast<float>(barometric), barometric_forcast, "THB");
324 }
325 else if (humidity != 0)
326 {
327 //add temp+hum device
328 SendTempHumSensor(1, 255, temp, humidity, "TempHum");
329 }
330 else
331 {
332 //add temp device
333 SendTempSensor(1, 255, temp, "Temperature");
334 }
335
336 //Wind
337 if (!root["Wind"].empty())
338 {
339 int wind_degrees = -1;
340 float windspeed_ms = 0;
341 float windgust_ms = 0;
342 //float wind_temp = temp;
343 float wind_chill = temp;
344
345 if (!root["Wind"]["Direction"].empty())
346 {
347 wind_degrees = root["Wind"]["Direction"]["Degrees"].asInt();
348 }
349 if (!root["Wind"]["Speed"].empty())
350 {
351 windspeed_ms = root["Wind"]["Speed"]["Metric"]["Value"].asFloat() / 3.6f; //km/h to m/s
352 }
353 if (!root["WindGust"].empty())
354 {
355 if (!root["WindGust"]["Speed"].empty())
356 {
357 windgust_ms = root["WindGust"]["Speed"]["Metric"]["Value"].asFloat() / 3.6f; //km/h to m/s
358 }
359 }
360 if (!root["RealFeelTemperature"].empty())
361 {
362 wind_chill = root["RealFeelTemperature"]["Metric"]["Value"].asFloat();
363 }
364 if (wind_degrees != -1)
365 {
366 SendWind(1, 255, wind_degrees, windspeed_ms, windgust_ms, temp, wind_chill, true, true, "Wind");
367 }
368 }
369
370 //UV
371 if (!root["UVIndex"].empty())
372 {
373 float UV = static_cast<float>(atof(root["UVIndex"].asString().c_str()));
374 if ((UV < 16) && (UV >= 0))
375 {
376 SendUVSensor(0, 1, 255, UV, "UV");
377 }
378 }
379
380 //Rain
381 if (!root["PrecipitationSummary"].empty())
382 {
383 if (!root["PrecipitationSummary"]["Precipitation"].empty())
384 {
385 float RainCount = static_cast<float>(atof(root["PrecipitationSummary"]["Precipitation"]["Metric"]["Value"].asString().c_str()));
386 if ((RainCount != -9999.00f) && (RainCount >= 0.00f))
387 {
388 RBUF tsen;
389 memset(&tsen, 0, sizeof(RBUF));
390 tsen.RAIN.packetlength = sizeof(tsen.RAIN) - 1;
391 tsen.RAIN.packettype = pTypeRAIN;
392 tsen.RAIN.subtype = sTypeRAINWU;
393 tsen.RAIN.battery_level = 9;
394 tsen.RAIN.rssi = 12;
395 tsen.RAIN.id1 = 0;
396 tsen.RAIN.id2 = 1;
397
398 tsen.RAIN.rainrateh = 0;
399 tsen.RAIN.rainratel = 0;
400
401 if (!root["PrecipitationSummary"]["PastHour"].empty())
402 {
403 float rainrateph = static_cast<float>(atof(root["PrecipitationSummary"]["PastHour"]["Metric"]["Value"].asString().c_str()));
404 if (rainrateph != -9999.00f)
405 {
406 int at10 = round(std::abs(rainrateph*10.0f));
407 tsen.RAIN.rainrateh = (BYTE)(at10 / 256);
408 at10 -= (tsen.RAIN.rainrateh * 256);
409 tsen.RAIN.rainratel = (BYTE)(at10);
410 }
411 }
412
413 int tr10 = int((float(RainCount)*10.0f));
414 tsen.RAIN.raintotal1 = 0;
415 tsen.RAIN.raintotal2 = (BYTE)(tr10 / 256);
416 tr10 -= (tsen.RAIN.raintotal2 * 256);
417 tsen.RAIN.raintotal3 = (BYTE)(tr10);
418
419 sDecodeRXMessage(this, (const unsigned char *)&tsen.RAIN, NULL, 255);
420 }
421 }
422 }
423
424 //Visibility
425 if (!root["Visibility"].empty())
426 {
427 if (!root["Visibility"]["Metric"].empty())
428 {
429 float visibility = root["Visibility"]["Metric"]["Value"].asFloat();
430 if (visibility >= 0)
431 {
432 _tGeneralDevice gdevice;
433 gdevice.subtype = sTypeVisibility;
434 gdevice.floatval1 = visibility;
435 sDecodeRXMessage(this, (const unsigned char *)&gdevice, NULL, 255);
436 }
437 }
438 }
439
440 //Forecast URL
441 if (!root["Link"].empty())
442 {
443 m_ForecastURL = root["Link"].asString();
444 }
445 }
446 catch (...)
447 {
448 Log(LOG_ERROR, "Error parsing JSon data!");
449 }
450 }
451
452