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