Instrument Neutral Distributed Interface INDI 2.2.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.
36std::unique_ptr<WunderGround> wunderGround(new WunderGround());
37
38static 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
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
104void WunderGround::ISGetProperties(const char *dev)
105{
107
108 defineProperty(&wunderAPIKeyTP);
109
110 loadConfig(true, "WUNDER_API_KEY");
111}
112
113bool 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
129bool 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() + 1];
166 snprintf(srcBuffer, sizeof(srcBuffer), "%s", readBuffer.c_str());
167 char *source = srcBuffer;
168 char *endptr;
169 JsonValue value;
170 JsonAllocator allocator;
171 int status = jsonParse(source, &endptr, &value, allocator);
172 if (status != JSON_OK)
173 {
174 LOGF_ERROR("%s at %zd", jsonStrError(status), endptr - source);
175 LOGF_DEBUG("%s", requestURL);
176 LOGF_DEBUG("%s", readBuffer.c_str());
177 return IPS_ALERT;
178 }
179
180 JsonIterator it;
181 JsonIterator observationIterator;
182
183 for (it = begin(value); it != end(value); ++it)
184 {
185 if (!strcmp(it->key, "current_observation"))
186 {
187 for (observationIterator = begin(it->value); observationIterator != end(it->value); ++observationIterator)
188 {
189 if (!strcmp(observationIterator->key, "weather"))
190 {
191 char *value = observationIterator->value.toString();
192
193 if (!strcmp(value, "Clear"))
194 setParameterValue("WEATHER_FORECAST", 0);
195 else if (!strcmp(value, "Unknown") || !strcmp(value, "Scattered Clouds") ||
196 !strcmp(value, "Partly Cloudy") || !strcmp(value, "Overcast") ||
197 !strcmp(value, "Patches of Fog") || !strcmp(value, "Partial Fog") ||
198 !strcmp(value, "Light Haze"))
199 setParameterValue("WEATHER_FORECAST", 1);
200 else
201 setParameterValue("WEATHER_FORECAST", 2);
202
203 LOGF_INFO("Weather condition: %s", value);
204 }
205 else if (!strcmp(observationIterator->key, "temp_c"))
206 {
207 if (observationIterator->value.isDouble())
208 setParameterValue("WEATHER_TEMPERATURE", observationIterator->value.toNumber());
209 else
210 setParameterValue("WEATHER_TEMPERATURE", atof(observationIterator->value.toString()));
211 }
212 else if (!strcmp(observationIterator->key, "wind_kph"))
213 {
214 if (observationIterator->value.isDouble())
215 setParameterValue("WEATHER_WIND_SPEED", observationIterator->value.toNumber());
216 else
217 setParameterValue("WEATHER_WIND_SPEED", atof(observationIterator->value.toString()));
218 }
219 else if (!strcmp(observationIterator->key, "wind_gust_kph"))
220 {
221 if (observationIterator->value.isDouble())
222 setParameterValue("WEATHER_WIND_GUST", observationIterator->value.toNumber());
223 else
224 setParameterValue("WEATHER_WIND_GUST", atof(observationIterator->value.toString()));
225 }
226 else if (!strcmp(observationIterator->key, "precip_1hr_metric"))
227 {
228 char *value = observationIterator->value.toString();
229 double mm = -1;
230 if (!strcmp(value, "--"))
231 setParameterValue("WEATHER_RAIN_HOUR", 0);
232 else
233 {
234 mm = atof(value);
235 if (mm >= 0)
236 setParameterValue("WEATHER_RAIN_HOUR", mm);
237 }
238 }
239 }
240 }
241 }
242
243 return IPS_OK;
244}
245
247{
249
250 IUSaveConfigText(fp, &wunderAPIKeyTP);
251
252 return true;
253}
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 addParameter(std::string name, std::string label, double numMinOk, double numMaxOk, double percWarning, bool flipWarning=false)
Add a physical weather measurable parameter to the weather driver.
bool setCriticalParameter(std::string name)
setCriticalParameter Set parameter that is considered critical to the operation of the observatory....
void setParameterValue(std::string name, double value)
setParameterValue Update weather parameter value
virtual bool ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n) override
Process the client newSwitch command.
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 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
Initialize properties initial state and value. The child class must implement this function.
virtual bool saveConfigItems(FILE *fp)
saveConfigItems Save specific properties in the provide config file handler. Child class usually over...
virtual bool initProperties() override
Initialize 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:184
IPState
Property state.
Definition indiapi.h:158
@ IPS_BUSY
Definition indiapi.h:161
@ IPS_ALERT
Definition indiapi.h:162
@ IPS_IDLE
Definition indiapi.h:159
@ IPS_OK
Definition indiapi.h:160
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:509
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:416
void IUSaveConfigText(FILE *fp, const ITextVectorProperty *tvp)
Add a text vector property value to the configuration file.
Definition indidevapi.c:28
#define INDI_UNUSED(x)
Definition indidevapi.h:132
int IUUpdateText(ITextVectorProperty *tvp, char *texts[], char *names[], int n)
Update all text members in a text vector property.
#define MAXRBUF
Definition indidriver.c:52
void IDSetText(const ITextVectorProperty *tvp, const char *fmt,...)
#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 getDeviceName
char name[MAXINDINAME]
Definition indiapi.h:248
std::unique_ptr< WunderGround > wunderGround(new WunderGround())