Instrument Neutral Distributed Interface INDI  2.0.2
wunderground.cpp
Go to the documentation of this file.
1 /*******************************************************************************
2  Copyright(c) 2015 Jasem Mutlaq. All rights reserved.
3 
4  INDI Weather Underground (TM) Weather Driver
5 
6  This program is free software; you can redistribute it and/or modify it
7  under the terms of the GNU General Public License as published by the Free
8  Software Foundation; either version 2 of the License, or (at your option)
9  any later version.
10 
11  This program is distributed in the hope that it will be useful, but WITHOUT
12  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14  more details.
15 
16  You should have received a copy of the GNU Library General Public License
17  along with this library; see the file COPYING.LIB. If not, write to
18  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19  Boston, MA 02110-1301, USA.
20 
21  The full GNU General Public License is included in this distribution in the
22  file called LICENSE.
23 *******************************************************************************/
24 
25 #include "wunderground.h"
26 
27 #include "gason.h"
28 #include "locale_compat.h"
29 
30 #include <curl/curl.h>
31 
32 #include <memory>
33 #include <cstring>
34 
35 // We declare an auto pointer to WunderGround.
36 std::unique_ptr<WunderGround> wunderGround(new WunderGround());
37 
38 static size_t WriteCallback(void *contents, size_t size, size_t nmemb, void *userp)
39 {
40  ((std::string *)userp)->append((char *)contents, size * nmemb);
41  return size * nmemb;
42 }
43 
45 {
46  setVersion(1, 0);
47 
48  wunderLat = -1000;
49  wunderLong = -1000;
50 
52 }
53 
55 {
56 }
57 
59 {
60  return (const char *)"WunderGround";
61 }
62 
64 {
65  if (wunderAPIKeyT[0].text == nullptr)
66  {
67  LOG_ERROR("Weather Underground API Key is not available. Please register your API key at "
68  "www.wunderground.com and save it under Options.");
69  return false;
70  }
71 
72  return true;
73 }
74 
76 {
77  return true;
78 }
79 
81 {
83 
84  IUFillText(&wunderAPIKeyT[0], "API_KEY", "API Key", nullptr);
85  IUFillTextVector(&wunderAPIKeyTP, wunderAPIKeyT, 1, getDeviceName(), "WUNDER_API_KEY", "Wunder", OPTIONS_TAB, IP_RW,
86  60, IPS_IDLE);
87 
88  addParameter("WEATHER_FORECAST", "Weather", 0, 0, 15);
89  addParameter("WEATHER_TEMPERATURE", "Temperature (C)", -10, 30, 15);
90  addParameter("WEATHER_WIND_SPEED", "Wind (kph)", 0, 20, 15);
91  addParameter("WEATHER_WIND_GUST", "Gust (kph)", 0, 20, 15);
92  addParameter("WEATHER_RAIN_HOUR", "Precip (mm)", 0, 0, 15);
93 
94  setCriticalParameter("WEATHER_FORECAST");
95  setCriticalParameter("WEATHER_TEMPERATURE");
96  setCriticalParameter("WEATHER_WIND_SPEED");
97  setCriticalParameter("WEATHER_RAIN_HOUR");
98 
100 
101  return true;
102 }
103 
104 void WunderGround::ISGetProperties(const char *dev)
105 {
107 
108  defineProperty(&wunderAPIKeyTP);
109 
110  loadConfig(true, "WUNDER_API_KEY");
111 }
112 
113 bool WunderGround::ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n)
114 {
115  if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
116  {
117  if (!strcmp(wunderAPIKeyTP.name, name))
118  {
119  IUUpdateText(&wunderAPIKeyTP, texts, names, n);
120  wunderAPIKeyTP.s = IPS_OK;
121  IDSetText(&wunderAPIKeyTP, nullptr);
122  return true;
123  }
124  }
125 
126  return INDI::Weather::ISNewText(dev, name, texts, names, n);
127 }
128 
129 bool WunderGround::updateLocation(double latitude, double longitude, double elevation)
130 {
131  INDI_UNUSED(elevation);
132 
133  wunderLat = latitude;
134  wunderLong = (longitude > 180) ? (longitude - 360) : longitude;
135 
136  return true;
137 }
138 
140 {
141  CURL *curl;
142  CURLcode res;
143  std::string readBuffer;
144  char requestURL[MAXRBUF];
145 
146  // If location is not updated yet, return busy
147  if (wunderLat == -1000 || wunderLong == -1000)
148  return IPS_BUSY;
149 
150  AutoCNumeric locale;
151 
152  snprintf(requestURL, MAXRBUF, "http://api.wunderground.com/api/%s/conditions/q/%g,%g.json", wunderAPIKeyT[0].text,
153  wunderLat, wunderLong);
154 
155  curl = curl_easy_init();
156  if (curl)
157  {
158  curl_easy_setopt(curl, CURLOPT_URL, requestURL);
159  curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
160  curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer);
161  res = curl_easy_perform(curl);
162  curl_easy_cleanup(curl);
163  }
164 
165  char srcBuffer[readBuffer.size()];
166  strncpy(srcBuffer, readBuffer.c_str(), readBuffer.size());
167  char *source = srcBuffer;
168  // do not forget terminate source string with 0
169  char *endptr;
170  JsonValue value;
171  JsonAllocator allocator;
172  int status = jsonParse(source, &endptr, &value, allocator);
173  if (status != JSON_OK)
174  {
175  LOGF_ERROR("%s at %zd", jsonStrError(status), endptr - source);
176  LOGF_DEBUG("%s", requestURL);
177  LOGF_DEBUG("%s", readBuffer.c_str());
178  return IPS_ALERT;
179  }
180 
181  JsonIterator it;
182  JsonIterator observationIterator;
183 
184  for (it = begin(value); it != end(value); ++it)
185  {
186  if (!strcmp(it->key, "current_observation"))
187  {
188  for (observationIterator = begin(it->value); observationIterator != end(it->value); ++observationIterator)
189  {
190  if (!strcmp(observationIterator->key, "weather"))
191  {
192  char *value = observationIterator->value.toString();
193 
194  if (!strcmp(value, "Clear"))
195  setParameterValue("WEATHER_FORECAST", 0);
196  else if (!strcmp(value, "Unknown") || !strcmp(value, "Scattered Clouds") ||
197  !strcmp(value, "Partly Cloudy") || !strcmp(value, "Overcast") ||
198  !strcmp(value, "Patches of Fog") || !strcmp(value, "Partial Fog") ||
199  !strcmp(value, "Light Haze"))
200  setParameterValue("WEATHER_FORECAST", 1);
201  else
202  setParameterValue("WEATHER_FORECAST", 2);
203 
204  LOGF_INFO("Weather condition: %s", value);
205  }
206  else if (!strcmp(observationIterator->key, "temp_c"))
207  {
208  if (observationIterator->value.isDouble())
209  setParameterValue("WEATHER_TEMPERATURE", observationIterator->value.toNumber());
210  else
211  setParameterValue("WEATHER_TEMPERATURE", atof(observationIterator->value.toString()));
212  }
213  else if (!strcmp(observationIterator->key, "wind_kph"))
214  {
215  if (observationIterator->value.isDouble())
216  setParameterValue("WEATHER_WIND_SPEED", observationIterator->value.toNumber());
217  else
218  setParameterValue("WEATHER_WIND_SPEED", atof(observationIterator->value.toString()));
219  }
220  else if (!strcmp(observationIterator->key, "wind_gust_kph"))
221  {
222  if (observationIterator->value.isDouble())
223  setParameterValue("WEATHER_WIND_GUST", observationIterator->value.toNumber());
224  else
225  setParameterValue("WEATHER_WIND_GUST", atof(observationIterator->value.toString()));
226  }
227  else if (!strcmp(observationIterator->key, "precip_1hr_metric"))
228  {
229  char *value = observationIterator->value.toString();
230  double mm = -1;
231  if (!strcmp(value, "--"))
232  setParameterValue("WEATHER_RAIN_HOUR", 0);
233  else
234  {
235  mm = atof(value);
236  if (mm >= 0)
237  setParameterValue("WEATHER_RAIN_HOUR", mm);
238  }
239  }
240  }
241  }
242  }
243 
244  return IPS_OK;
245 }
246 
248 {
250 
251  IUSaveConfigText(fp, &wunderAPIKeyTP);
252 
253  return true;
254 }
const char * getDeviceName() const
Definition: basedevice.cpp:821
virtual void ISGetProperties(const char *dev)
define the driver's properties to the client. Usually, only a minimum set of properties are defined t...
void setVersion(uint16_t vMajor, uint16_t vMinor)
Set driver version information to be defined in DRIVER_INFO property as vMajor.vMinor.
virtual bool loadConfig(bool silent=false, const char *property=nullptr)
Load the last saved configuration file.
void defineProperty(INumberVectorProperty *property)
void addDebugControl()
Add Debug control to the driver.
void setParameterValue(std::string name, double value)
setParameterValue Update weather parameter value
bool setCriticalParameter(std::string param)
setCriticalParameter Set parameter that is considered critical to the operation of the observatory....
void addParameter(std::string name, std::string label, double numMinOk, double numMaxOk, double percWarning)
addParameter Add a physical weather measurable parameter to the weather driver. The weather value has...
virtual bool ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n) override
Process the client newSwitch command.
virtual bool saveConfigItems(FILE *fp) override
saveConfigItems Save specific properties in the provide config file handler. Child class usually over...
void setWeatherConnection(const uint8_t &value)
setWeatherConnection Set Weather connection mode. Child class should call this in the constructor bef...
virtual bool initProperties() override
Initilize properties initial state and value. The child class must implement this function.
Definition: indiweather.cpp:41
virtual bool saveConfigItems(FILE *fp)
saveConfigItems Save specific properties in the provide config file handler. Child class usually over...
virtual bool initProperties() override
Initilize properties initial state and value. The child class must implement this function.
const char * getDefaultName()
virtual void ISGetProperties(const char *dev) override
define the driver's properties to the client. Usually, only a minimum set of properties are defined t...
virtual bool updateLocation(double latitude, double longitude, double elevation)
Update weather station location.
virtual IPState updateWeather()
updateWeather Update weather conditions from device or service. The function should not change the st...
virtual ~WunderGround()
bool Connect()
Connect to the device. INDI::DefaultDevice implementation connects to appropriate connection interfac...
virtual bool ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n) override
Process the client newSwitch command.
bool Disconnect()
Disconnect from device.
const char * OPTIONS_TAB
OPTIONS_TAB Where all the driver's options are located. Those may include auxiliary controls,...
@ IP_RW
Definition: indiapi.h:186
IPState
Property state.
Definition: indiapi.h:160
@ IPS_BUSY
Definition: indiapi.h:163
@ IPS_ALERT
Definition: indiapi.h:164
@ IPS_IDLE
Definition: indiapi.h:161
@ IPS_OK
Definition: indiapi.h:162
void IUFillTextVector(ITextVectorProperty *tvp, IText *tp, int ntp, const char *dev, const char *name, const char *label, const char *group, IPerm p, double timeout, IPState s)
Assign attributes for a text vector property. The vector's auxiliary elements will be set to NULL.
Definition: indidevapi.c:291
void IUFillText(IText *tp, const char *name, const char *label, const char *initialText)
Assign attributes for a text property. The text's auxiliary elements will be set to NULL.
Definition: indidevapi.c:198
void IUSaveConfigText(FILE *fp, const ITextVectorProperty *tvp)
Add a text vector property value to the configuration file.
Definition: indidevapi.c:20
#define INDI_UNUSED(x)
Definition: indidevapi.h:131
int IUUpdateText(ITextVectorProperty *tvp, char *texts[], char *names[], int n)
Update all text members in a text vector property.
Definition: indidriver.c:1396
void IDSetText(const ITextVectorProperty *tvp, const char *fmt,...)
Definition: indidriver.c:1191
#define LOGF_INFO(fmt,...)
Definition: indilogger.h:82
#define LOGF_DEBUG(fmt,...)
Definition: indilogger.h:83
#define LOG_ERROR(txt)
Shorter logging macros. In order to use these macros, the function (or method) "getDeviceName()" must...
Definition: indilogger.h:72
#define LOGF_ERROR(fmt,...)
Definition: indilogger.h:80
#define MAXRBUF
Definition: indiserver.cpp:102
char name[MAXINDINAME]
Definition: indiapi.h:250
std::unique_ptr< WunderGround > wunderGround(new WunderGround())