Instrument Neutral Distributed Interface INDI  1.9.5
weatherwatcher.cpp
Go to the documentation of this file.
1 /*******************************************************************************
2  Copyright(c) 2018 Jasem Mutlaq. All rights reserved.
3 
4  INDI Weather Watcher 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 "weatherwatcher.h"
26 #include "locale_compat.h"
27 
28 #include <curl/curl.h>
29 
30 #include <memory>
31 #include <cstring>
32 
33 // We declare an auto pointer to WeatherWatcher.
34 static std::unique_ptr<WeatherWatcher> weatherWatcher(new WeatherWatcher());
35 
36 static size_t write_data(void *contents, size_t size, size_t nmemb, void *userp)
37 {
38  ((std::string *)userp)->append((char *)contents, size * nmemb);
39  return size * nmemb;
40 }
41 
43 {
44  setVersion(1, 2);
45 
47 }
48 
50 {
51  return "Weather Watcher";
52 }
53 
55 {
56  if (watchFileT[0].text == nullptr || watchFileT[0].text[0] == '\0')
57  {
58  LOG_ERROR("Watch file must be specified first in options.");
59  return false;
60  }
61 
62  return createPropertiesFromMap();
63 }
64 
66 {
67  return true;
68 }
69 
70 bool WeatherWatcher::createPropertiesFromMap()
71 {
72  // already parsed
73  if (initialParse)
74  return true;
75 
76  if (readWatchFile() == false)
77  return false;
78 
79  double minOK = 0, maxOK = 0, percWarn = 15;
80  for (auto const &x : weatherMap)
81  {
82  if (x.first == keywordT[0].text)
83  {
84  minOK = 0;
85  maxOK = 0;
86  percWarn = 15;
87  IUGetConfigNumber(getDeviceName(), "WEATHER_RAIN_HOUR", "MIN_OK", &minOK);
88  IUGetConfigNumber(getDeviceName(), "WEATHER_RAIN_HOUR", "MAX_OK", &maxOK);
89  IUGetConfigNumber(getDeviceName(), "WEATHER_RAIN_HOUR", "PERC_WARN", &percWarn);
90 
91  addParameter("WEATHER_RAIN_HOUR", "Rain (mm)", minOK, maxOK, percWarn);
92  setCriticalParameter("WEATHER_RAIN_HOUR");
93  }
94  else if (x.first == keywordT[1].text)
95  {
96  minOK = -10;
97  maxOK = 30;
98  percWarn = 15;
99  IUGetConfigNumber(getDeviceName(), "WEATHER_TEMPERATURE", "MIN_OK", &minOK);
100  IUGetConfigNumber(getDeviceName(), "WEATHER_TEMPERATURE", "MAX_OK", &maxOK);
101  IUGetConfigNumber(getDeviceName(), "WEATHER_TEMPERATURE", "PERC_WARN", &percWarn);
102 
103  addParameter("WEATHER_TEMPERATURE", "Temperature (C)", minOK, maxOK, percWarn);
104  setCriticalParameter("WEATHER_TEMPERATURE");
105  }
106  else if (x.first == keywordT[2].text)
107  {
108  minOK = 0;
109  maxOK = 20;
110  percWarn = 15;
111  IUGetConfigNumber(getDeviceName(), "WEATHER_WIND_SPEED", "MIN_OK", &minOK);
112  IUGetConfigNumber(getDeviceName(), "WEATHER_WIND_SPEED", "MAX_OK", &maxOK);
113  IUGetConfigNumber(getDeviceName(), "WEATHER_WIND_SPEED", "PERC_WARN", &percWarn);
114 
115  addParameter("WEATHER_WIND_SPEED", "Wind (kph)", minOK, maxOK, percWarn);
116  setCriticalParameter("WEATHER_WIND_SPEED");
117  }
118  else if (x.first == keywordT[3].text)
119  {
120  minOK = 0;
121  maxOK = 20;
122  percWarn = 15;
123  IUGetConfigNumber(getDeviceName(), "WEATHER_WIND_GUST", "MIN_OK", &minOK);
124  IUGetConfigNumber(getDeviceName(), "WEATHER_WIND_GUST", "MAX_OK", &maxOK);
125  IUGetConfigNumber(getDeviceName(), "WEATHER_WIND_GUST", "PERC_WARN", &percWarn);
126 
127  addParameter("WEATHER_WIND_GUST", "Gust (kph)", minOK, maxOK, percWarn);
128  }
129  else if (x.first == keywordT[4].text)
130  {
131  minOK = 0;
132  maxOK = 20;
133  percWarn = 15;
134  IUGetConfigNumber(getDeviceName(), "WEATHER_CLOUDS", "MIN_OK", &minOK);
135  IUGetConfigNumber(getDeviceName(), "WEATHER_CLOUDS", "MAX_OK", &maxOK);
136  IUGetConfigNumber(getDeviceName(), "WEATHER_CLOUDS", "PERC_WARN", &percWarn);
137 
138  addParameter("WEATHER_CLOUDS", "Clouds (%)", minOK, maxOK, percWarn);
139  setCriticalParameter("WEATHER_CLOUDS");
140  }
141  else if (x.first == keywordT[5].text)
142  {
143  minOK = 0;
144  maxOK = 100;
145  percWarn = 15;
146  IUGetConfigNumber(getDeviceName(), "WEATHER_HUMIDITY", "MIN_OK", &minOK);
147  IUGetConfigNumber(getDeviceName(), "WEATHER_HUMIDITY", "MAX_OK", &maxOK);
148  IUGetConfigNumber(getDeviceName(), "WEATHER_HUMIDITY", "PERC_WARN", &percWarn);
149 
150  addParameter("WEATHER_HUMIDITY", "Humidity (%)", minOK, maxOK, percWarn);
151  setCriticalParameter("WEATHER_HUMIDITY");
152  }
153  else if (x.first == keywordT[6].text)
154  {
155  minOK = 983;
156  maxOK = 1043;
157  percWarn = 15;
158  IUGetConfigNumber(getDeviceName(), "WEATHER_PRESSURE", "MIN_OK", &minOK);
159  IUGetConfigNumber(getDeviceName(), "WEATHER_PRESSURE", "MAX_OK", &maxOK);
160  IUGetConfigNumber(getDeviceName(), "WEATHER_PRESSURE", "PERC_WARN", &percWarn);
161 
162  addParameter("WEATHER_PRESSURE", "Pressure (hPa)", minOK, maxOK, percWarn);
163  setCriticalParameter("WEATHER_PRESSURE");
164  }
165  else if (x.first == keywordT[7].text)
166  {
167  minOK = 0;
168  maxOK = 0;
169  percWarn = 15;
170  IUGetConfigNumber(getDeviceName(), "WEATHER_FORECAST", "MIN_OK", &minOK);
171  IUGetConfigNumber(getDeviceName(), "WEATHER_FORECAST", "MAX_OK", &maxOK);
172  IUGetConfigNumber(getDeviceName(), "WEATHER_FORECAST", "PERC_WARN", &percWarn);
173 
174  addParameter("WEATHER_FORECAST", "Weather", minOK, maxOK, percWarn);
175  setCriticalParameter("WEATHER_FORECAST");
176  }
177  }
178 
179  initialParse = true;
180 
181  return true;
182 }
183 
185 {
187 
188  IUFillText(&keywordT[0], "RAIN", "Rain", "precip");
189  IUFillText(&keywordT[1], "TEMP", "Temperature", "temperature");
190  IUFillText(&keywordT[2], "WIND", "Wind", "wind");
191  IUFillText(&keywordT[3], "GUST", "Gust", "gust");
192  IUFillText(&keywordT[4], "CLOUDS", "Clouds", "clouds");
193  IUFillText(&keywordT[5], "HUMIDITY", "Humidity", "humidity");
194  IUFillText(&keywordT[6], "PRESSURE", "Pressure", "pressure");
195  IUFillText(&keywordT[7], "FORECAST", "Forecast", "forecast");
196  IUFillTextVector(&keywordTP, keywordT, 8, getDeviceName(), "KEYWORD", "Keywords", OPTIONS_TAB, IP_RW,
197  60, IPS_IDLE);
198 
199  IUFillText(&watchFileT[0], "URL", "File", nullptr);
200  IUFillTextVector(&watchFileTP, watchFileT, 1, getDeviceName(), "WATCH_SOURCE", "Source", OPTIONS_TAB, IP_RW,
201  60, IPS_IDLE);
202 
203  IUFillText(&separatorT[0], "SEPARATOR", "Separator", "=");
204  IUFillTextVector(&separatorTP, separatorT, 1, getDeviceName(), "SEPARATOR_KEYWORD", "Separator", OPTIONS_TAB, IP_RW,
205  60, IPS_IDLE);
206 
207  addDebugControl();
208 
209  return true;
210 }
211 
212 void WeatherWatcher::ISGetProperties(const char *dev)
213 {
215 
216  defineProperty(&watchFileTP);
217  loadConfig(true, "WATCH_SOURCE");
218 
220  loadConfig(true, "KEYWORD");
221 
223  loadConfig(true, "SEPARATOR_KEYWORD");
224 
225 }
226 
227 bool WeatherWatcher::ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n)
228 {
229  if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
230  {
231  if (!strcmp(watchFileTP.name, name))
232  {
233  IUUpdateText(&watchFileTP, texts, names, n);
234  watchFileTP.s = IPS_OK;
235  IDSetText(&watchFileTP, nullptr);
236  return true;
237  }
238  if (!strcmp(keywordTP.name, name))
239  {
240  IUUpdateText(&keywordTP, texts, names, n);
241  keywordTP.s = IPS_OK;
242  IDSetText(&keywordTP, nullptr);
243  return true;
244  }
245 
246  if (!strcmp(separatorTP.name, name))
247  {
248  IUUpdateText(&separatorTP, texts, names, n);
249  separatorTP.s = IPS_OK;
250  IDSetText(&separatorTP, nullptr);
251  return true;
252  }
253  }
254 
255  return INDI::Weather::ISNewText(dev, name, texts, names, n);
256 }
257 
259 {
260 
261  if (readWatchFile() == false)
262  return IPS_BUSY;
263 
264  for (auto const &x : weatherMap)
265  {
266  if (x.first == keywordT[0].text)
267  {
268  setParameterValue("WEATHER_RAIN_HOUR", std::strtod(x.second.c_str(), nullptr));
269  }
270  else if (x.first == keywordT[1].text)
271  {
272  setParameterValue("WEATHER_TEMPERATURE", std::strtod(x.second.c_str(), nullptr));
273  }
274  else if (x.first == keywordT[2].text)
275  {
276  setParameterValue("WEATHER_WIND_SPEED", std::strtod(x.second.c_str(), nullptr));
277  }
278  else if (x.first == keywordT[3].text)
279  {
280  setParameterValue("WEATHER_WIND_GUST", std::strtod(x.second.c_str(), nullptr));
281  }
282  else if (x.first == keywordT[4].text)
283  {
284  setParameterValue("WEATHER_CLOUDS", std::strtod(x.second.c_str(), nullptr));
285  }
286  else if (x.first == keywordT[5].text)
287  {
288  setParameterValue("WEATHER_HUMIDITY", std::strtod(x.second.c_str(), nullptr));
289  }
290  else if (x.first == keywordT[6].text)
291  {
292  setParameterValue("WEATHER_PRESSURE", std::strtod(x.second.c_str(), nullptr));
293  }
294  else if (x.first == keywordT[7].text)
295  {
296  setParameterValue("WEATHER_FORECAST", std::strtod(x.second.c_str(), nullptr));
297  }
298  }
299 
300  return IPS_OK;
301 }
302 
303 bool WeatherWatcher::readWatchFile()
304 {
305  CURL *curl;
306  CURLcode res;
307  bool rc = false;
308  char requestURL[MAXRBUF];
309 
310  AutoCNumeric locale;
311 
312  if (std::string(watchFileT[0].text).find("http") == 0)
313  snprintf(requestURL, MAXRBUF, "%s", watchFileT[0].text);
314  else
315  snprintf(requestURL, MAXRBUF, "file://%s", watchFileT[0].text);
316 
317  curl = curl_easy_init();
318 
319  if (curl)
320  {
321  curl_easy_setopt(curl, CURLOPT_URL, requestURL);
322  curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);
323  curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer);
324  res = curl_easy_perform(curl);
325 
326  if (res == CURLE_OK)
327  {
328  weatherMap = createMap(readBuffer);
329  rc = true;
330  }
331  curl_easy_cleanup(curl);
332  }
333 
334  return rc;
335 }
336 
338 {
340 
341  IUSaveConfigText(fp, &watchFileTP);
344 
345  return true;
346 }
347 
348 std::map<std::string, std::string> WeatherWatcher::createMap(std::string const &s)
349 {
350  std::map<std::string, std::string> m;
351 
352  std::string key, val;
353  std::istringstream iss(s);
354 
355  while(std::getline(std::getline(iss, key, separatorT[0].text[0]) >> std::ws, val))
356  m[key] = val;
357 
358  return m;
359 }
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
IUGetConfigNumber
int IUGetConfigNumber(const char *dev, const char *property, const char *member, double *value)
IUGetConfigNumber Opens configuration file and reads single number property.
Definition: indidriver.c:1390
IPState
IPState
Property state.
Definition: indiapi.h:158
IPS_OK
@ IPS_OK
Definition: indiapi.h:161
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
IDSetText
void IDSetText(const ITextVectorProperty *t, const char *msg,...) ATTRIBUTE_FORMAT_PRINTF(2
Tell client to update an existing text vector property.
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
weatherwatcher.h
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::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
WeatherWatcher::initProperties
virtual bool initProperties() override
Initilize properties initial state and value. The child class must implement this function.
Definition: weatherwatcher.cpp:184
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
WeatherWatcher::ISNewText
virtual bool ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n) override
Process the client newSwitch command.
Definition: weatherwatcher.cpp:227
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
WeatherWatcher::WeatherWatcher
WeatherWatcher()
Definition: weatherwatcher.cpp:42
WeatherWatcher::keywordTP
ITextVectorProperty keywordTP
Definition: weatherwatcher.h:72
WeatherWatcher::Connect
bool Connect() override
Connect to the device. INDI::DefaultDevice implementation connects to appropriate connection interfac...
Definition: weatherwatcher.cpp:54
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
WeatherWatcher::separatorT
IText separatorT[1]
Definition: weatherwatcher.h:74
WeatherWatcher::keywordT
IText keywordT[8]
Definition: weatherwatcher.h:71
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
IPS_BUSY
@ IPS_BUSY
Definition: indiapi.h:162
IPS_IDLE
@ IPS_IDLE
Definition: indiapi.h:160
WeatherWatcher::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: weatherwatcher.cpp:212
INDI::DefaultDevice::loadConfig
virtual bool loadConfig(bool silent=false, const char *property=nullptr)
Load the last saved configuration file.
Definition: defaultdevice.cpp:147
WeatherWatcher::updateWeather
virtual IPState updateWeather() override
updateWeather Update weather conditions from device or service. The function should not change the st...
Definition: weatherwatcher.cpp:258
_ITextVectorProperty::name
char name[MAXINDINAME]
Definition: indiapi.h:249
WeatherWatcher::Disconnect
bool Disconnect() override
Disconnect from device.
Definition: weatherwatcher.cpp:65
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
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
WeatherWatcher::saveConfigItems
virtual bool saveConfigItems(FILE *fp) override
saveConfigItems Save specific properties in the provide config file handler. Child class usually over...
Definition: weatherwatcher.cpp:337
IP_RW
@ IP_RW
Definition: indiapi.h:185
WeatherWatcher
Definition: weatherwatcher.h:31
INDI::DefaultDevice::addDebugControl
void addDebugControl()
Add Debug control to the driver.
Definition: defaultdevice.cpp:639
WeatherWatcher::separatorTP
ITextVectorProperty separatorTP
Definition: weatherwatcher.h:75
WeatherWatcher::getDefaultName
const char * getDefaultName() override
Definition: weatherwatcher.cpp:49
INDI::WeatherInterface::setParameterValue
void setParameterValue(std::string name, double value)
setParameterValue Update weather parameter value
Definition: indiweatherinterface.cpp:155