Instrument Neutral Distributed Interface INDI  1.9.5
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 }
WunderGround::~WunderGround
virtual ~WunderGround()
Definition: wunderground.cpp:54
WunderGround::initProperties
virtual bool initProperties() override
Initilize properties initial state and value. The child class must implement this function.
Definition: wunderground.cpp:80
WunderGround::ISGetProperties
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...
Definition: wunderground.cpp:104
jsonParse
int jsonParse(char *s, char **endptr, JsonValue *value, JsonAllocator &allocator)
Definition: gason.cpp:182
INDI::WeatherInterface::setCriticalParameter
bool setCriticalParameter(std::string param)
setCriticalParameter Set parameter that is considered critical to the operation of the observatory....
Definition: indiweatherinterface.cpp:167
IPState
IPState
Property state.
Definition: indiapi.h:158
LOGF_ERROR
#define LOGF_ERROR(fmt,...)
Definition: indilogger.h:80
IPS_OK
@ IPS_OK
Definition: indiapi.h:161
JsonValue::isDouble
bool isDouble() const
Definition: gason.h:60
INDI::Weather::initProperties
virtual bool initProperties() override
Initilize properties initial state and value. The child class must implement this function.
Definition: indiweather.cpp:41
locale_compat.h
JsonValue
Definition: gason.h:48
IDSetText
void IDSetText(const ITextVectorProperty *t, const char *msg,...) ATTRIBUTE_FORMAT_PRINTF(2
Tell client to update an existing text vector property.
WunderGround::Disconnect
bool Disconnect()
Disconnect from device.
Definition: wunderground.cpp:75
IPS_ALERT
@ IPS_ALERT
Definition: indiapi.h:163
INDI::DefaultDevice::defineProperty
void defineProperty(INumberVectorProperty *property)
Definition: defaultdevice.cpp:997
OPTIONS_TAB
const char * OPTIONS_TAB
OPTIONS_TAB Where all the driver's options are located. Those may include auxiliary controls,...
Definition: defaultdevice.cpp:39
IUFillTextVector
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: indidriver.c:477
INDI_UNUSED
#define INDI_UNUSED(x)
Definition: indidevapi.h:799
INDI::DefaultDevice::setVersion
void setVersion(uint16_t vMajor, uint16_t vMinor)
Set driver version information to be defined in DRIVER_INFO property as vMajor.vMinor.
Definition: defaultdevice.cpp:1219
INDI::BaseDevice::getDeviceName
const char * getDeviceName() const
Definition: basedevice.cpp:799
IUFillText
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: indidriver.c:369
WunderGround::WunderGround
WunderGround()
Definition: wunderground.cpp:44
IUUpdateText
int IUUpdateText(ITextVectorProperty *tvp, char *texts[], char *names[], int n)
Update all text members in a text vector property.
Definition: indidriver.c:259
MAXRBUF
#define MAXRBUF
Definition: indidriver.c:52
JsonAllocator
Definition: gason.h:135
jsonStrError
const char * jsonStrError(int err)
Definition: gason.cpp:31
LOGF_DEBUG
#define LOGF_DEBUG(fmt,...)
Definition: indilogger.h:83
WunderGround::getDefaultName
const char * getDefaultName()
Definition: wunderground.cpp:58
wunderGround
std::unique_ptr< WunderGround > wunderGround(new WunderGround())
INDI::Weather::setWeatherConnection
void setWeatherConnection(const uint8_t &value)
setWeatherConnection Set Weather connection mode. Child class should call this in the constructor bef...
Definition: indiweather.cpp:402
begin
JsonIterator begin(JsonValue o)
Definition: gason.h:104
INDI::Weather::saveConfigItems
virtual bool saveConfigItems(FILE *fp) override
saveConfigItems Save specific properties in the provide config file handler. Child class usually over...
Definition: indiweather.cpp:369
IUSaveConfigText
void IUSaveConfigText(FILE *fp, const ITextVectorProperty *tvp)
Add a text vector property value to the configuration file.
Definition: indicom.c:1460
JsonNode::value
JsonValue value
Definition: gason.h:89
WunderGround::updateLocation
virtual bool updateLocation(double latitude, double longitude, double elevation)
Update weather station location.
Definition: wunderground.cpp:129
IPS_BUSY
@ IPS_BUSY
Definition: indiapi.h:162
IPS_IDLE
@ IPS_IDLE
Definition: indiapi.h:160
INDI::DefaultDevice::loadConfig
virtual bool loadConfig(bool silent=false, const char *property=nullptr)
Load the last saved configuration file.
Definition: defaultdevice.cpp:147
end
JsonIterator end(JsonValue)
Definition: gason.h:108
_ITextVectorProperty::name
char name[MAXINDINAME]
Definition: indiapi.h:249
gason.h
LOGF_INFO
#define LOGF_INFO(fmt,...)
Definition: indilogger.h:82
WunderGround::ISNewText
virtual bool ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n) override
Process the client newSwitch command.
Definition: wunderground.cpp:113
INDI::Weather::ISNewText
virtual bool ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n) override
Process the client newSwitch command.
Definition: indiweather.cpp:228
LOG_ERROR
#define LOG_ERROR(txt)
Shorter logging macros. In order to use these macros, the function (or method) "getDeviceName()" must...
Definition: indilogger.h:72
WunderGround
Definition: wunderground.h:29
INDI::WeatherInterface::addParameter
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...
Definition: indiweatherinterface.cpp:131
INDI::DefaultDevice::ISGetProperties
virtual void ISGetProperties(const char *dev)
define the driver's properties to the client. Usually, only a minimum set of properties are defined t...
Definition: defaultdevice.cpp:750
name
const char * name
Definition: indiserver.c:116
_ITextVectorProperty::s
IPState s
Definition: indiapi.h:259
INDI::Weather::CONNECTION_NONE
@ CONNECTION_NONE
Definition: indiweather.h:83
IP_RW
@ IP_RW
Definition: indiapi.h:185
JsonIterator
Definition: gason.h:94
WunderGround::saveConfigItems
virtual bool saveConfigItems(FILE *fp)
saveConfigItems Save specific properties in the provide config file handler. Child class usually over...
Definition: wunderground.cpp:247
JsonValue::toString
char * toString() const
Definition: gason.h:75
JsonValue::toNumber
double toNumber() const
Definition: gason.h:70
wunderground.h
INDI::DefaultDevice::addDebugControl
void addDebugControl()
Add Debug control to the driver.
Definition: defaultdevice.cpp:639
JsonNode::key
char * key
Definition: gason.h:91
WunderGround::updateWeather
virtual IPState updateWeather()
updateWeather Update weather conditions from device or service. The function should not change the st...
Definition: wunderground.cpp:139
WunderGround::Connect
bool Connect()
Connect to the device. INDI::DefaultDevice implementation connects to appropriate connection interfac...
Definition: wunderground.cpp:63
INDI::WeatherInterface::setParameterValue
void setParameterValue(std::string name, double value)
setParameterValue Update weather parameter value
Definition: indiweatherinterface.cpp:155