Commit 90424fe0 authored by aloz77's avatar aloz77
Browse files

First commit to Github

parent 9fb4a937
/*
* Based on wwsr - Wireless Weather Station Reader
* Michael Pendec (michael.pendec@gmail.com)
* Svend Skafte (svend@skafte.net)
* Adam Pribyl (covex@lowlevel.cz)
* Bradley Jarvis (bradley.jarvis@dcsi.net.au)
* Lukas Zvonar (lukic@mag-net.sk)
*Petr Zitny (petr@zitny.net)
* Alexey Ozerov (alexey@ozerov.de)
* 2011-12-20 some code cleaning and restructure, read_period, km/h wind speed
* 2011-12-21 introduce ws_parse(), struct wrecord
* 2011-12-22 flexible read period, server url, curl post data
* 2011-12-25 add inches, mph, utc, introduce ws_format(), urlencode, additional url
* 2011-12-26 readd rain 60 min calculation
* 2011-12-28 read config vars from cfg file, multiple add_url, date DD.MM.YYYY and time HH:MM formats
* 2011-12-29 read read_period from WS, add rain from 0h calculation
* 2012-01-01 fix error handling in main(), handle ws_submit feedback, get lasttime, submit multiple positions, submit error
* 2012-01-02 submit last error text to frewe-server
* 2012-01-03 add support for wetter.com, more accurate time calculation for position shift, accurate cfg read
* 2012-01-05 don't encode : in HH:MM
* 2012-01-05 rewrite read_cfg(), add station type, support WH3080, calibration, read data_count
* 2012-01-08 check data_count when looking for old records, plausi check for age, read illumination & UV
* 2012-01-14 Better check for invalid values, skip record if too many errors, fix rain calibration
* 2012-01-15 Double read the data from USB device
* 2012-01-16 Fix time calculation for submission to frewe-server
* 2012-01-19 Fix linebreak after OK problem on frewe-server responce
* 2012-01-24 Integrate weather service URLs, fix bad data prevents position loop problem
* 2012-01-25 Read weather service access data from cfg
* 2012-01-26 No formatting in ws_parse, different errorstrings for different services
* 2012-01-27 Fix address calculation on address overflow
* 2012-01-28 Handle signals, fix ws_close(), fix address calculation again, multiple tries on USB read problems, recognize sensor loss, station type %K
* 2012-01-28 Don't handle SIGHUP
* 2012-02-01 Use http_fetcher instead of curl (saves 200 KB, however is not IPv6 capable)
* 2012-02-03 Resubmit data also to known weather services
* 2012-02-04 Handle DOS encoded lines in cfg file, read alarm config, introduce ws_alarm, clean up some code
* 2012-02-05 Configure emails for errors and alarms in cfg file, enable multiple alarms of each type, fix memory in URLencode
* 2012-02-09 Fix UV value output
* 2012-02-11 Add Sauerlandwetter
* 2012-02-23 Don't drop record if sensors are lost, NB: total rain, indoor values and abs. pressure (?) are usable even if sensors are lost
info: frewe.ws_dump - 0xD6C0: 0x05 0x38 0xEE 0x00 0xFF 0xFF 0xFF 0xEE 0x27 0xFF 0xFF 0xFF 0x8C 0x6B 0x03 0x40
info: frewe.ws_dump - 0xD6D0: 0x05 0x38 0xEE 0x00 0x35 0x67 0x00 0xEC 0x27 0x11 0x18 0x00 0x08 0x6B 0x03 0x00
* 2012-02-24 Only resend data to services which support it, don't resend if sensor lost (sensor lost = good data)
* 2012-03-06 Handle HighIndoorTemp and LowIndoorTemp Alarms
* 2012-04-04 Continue sending to other weather services on error
* 2012-05-24 Don't run out of available data on rain calculation
* 2012-06-11 Flush the stdout (enable data logging into a file), use stderr for debug and info output, wetter.com replaced wetter.de
* 2012-06-13 Include age of last record last_age in record datetime, pos60 and pos0h calculation, fix rainday calculation for old records
* 2012-06-15 Ignore get lasttime errors if frewe-server is not available
* 2012-07-27 make unit conversions in ws_format() instead of ws_parse()
* 2012-07-27 WH3080: Transmit illumination in W/m instead of Lux to WUnderground, PWS, Awekas, METOffice
* 2012-09-01 Remove Wetterpool.de, add Wetternetz Sachsen
* 2013-03-16 Remove Sauerlandwetter, rename time_interval to read_period, increase http timeout to 15 sec, print version info with -H
* 2013-03-16 Trying to fix double submissions with -59 logical seconds, later changed back to -1
* 2013-04-11 Fix SIGSERV by getlasttime from wrong input, fix pause calculation (int overflow)
* 2013-04-25 Fix pause calculation again
* 2013-05-23 Fix pause calculation again (use usleep), use getaddrinfo in http_fetcher.c, better validation rainday and rainhour
* 2013-06-22 Trying to fix stability issue when FreweServer fails, ignore FreweServer submission fault
* 2013-07-01 Fix stability issue within logger function (use strcpy instead of strcat)
* 2013-07-12 Try resetting USB port on errno 62 when setting alt device
* 2013-10-13 New interface URL for Wetternetz Sachsen (2.1)
* 2014-08-10 Add fhem file
* 2015-03-25 Send UTC time to frewe-server
* 2015-04-24 Handle lasttime as UTC
* 2015-08-20 Don't write fhem.txt for older records
* TODO: Handle rain counter overflow
*/
// #define _XOPEN_SOURCE 1 /* was needed for strptime? */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <signal.h>
#include <usb.h>
#include <time.h>
#include <math.h>
#include <elf.h>
#include <openssl/md5.h>
#include "http_fetcher.h"
#define PROGRAM_VERSION "1.18"
#define DEFAULT_VENDOR 0x1941
#define DEFAULT_PRODUCT 0x8021
#define DEFAULT_FORMAT (char *)"\ntime: %N\nage: %a min\nin humidity: %h %%\nout humidity: %H %%\nin temperature: %I C\nout temperature: %O C\ndewpoint temperature: %E C\nwindchill temperature: %C C\nwind speed: %W km/h\nwind gust: %G km/h\nwind direction: %D\npressure: %P hPa\nrel. pressure: %L hPa\nrain total: %R mm\nrain 60 min: %S mm\nrain since 0h: %T mm\nillumination: %M lux\nUV: %U\n\n"
// Look http://www.jim-easterbrook.me.uk/weather/mm/ for memory map
#define WS_READ_PERIOD_ADDRESS 16
#define WS_DATA_COUNT_ADDRESS 27
#define WS_CURRENT_POSITION_ADDRESS 30
#define WS_MIN_ENTRY_ADDR 0x0100
#define WS_MAX_ENTRY_ADDR 0x10000
#define WS_TOTAL_ENTRIES ((WS_MAX_ENTRY_ADDR-WS_MIN_ENTRY_ADDR)/ws_entry_size)
#define MAX_ALARMS 20
#define MAX_ADD_URLS 10
// extern double round (double __x) __attribute__ ((__nothrow__)) __attribute__ ((__const__));
int ws_open(usb_dev_handle **dev,uint16_t vendor,uint16_t product,int reset_done);
int ws_close(usb_dev_handle **dev);
int ws_read(usb_dev_handle *dev,uint16_t address,uint8_t *data,uint16_t size);
int ws_reset(usb_dev_handle *dev);
int ws_format(char *format, char *output, unsigned char urlencode, char *user, char *pass, char *error);
int ws_dump(uint16_t address,uint8_t *buffer,uint16_t size,uint8_t width);
uint16_t get_address(uint16_t base, int position);
void strcatenc(char *out,char *text,unsigned char urlencode);
void signal_handler(int signal);
long diff_time(const struct timeval *tact, const struct timeval *tlast);
char* URLencode(char *str);
char* URLdecode(char *str);
struct wrecord
{ time_t datetime;
char winddir[4];
float tempout,tempin,windspeed,windspeedms,windgust,tempchill,tempdew,pressabs,pressrel,rain,rainhour,rainday;
float illu;
short humin,humout,age;
short uv,winddeg;
char ok;
} w,w1;
struct calib
{ float tempin_factor,tempin_offset,tempout_factor,tempout_offset,humin_factor,humin_offset,humout_factor,humout_offset;
float windspeed_factor,windspeed_offset,windgust_factor,windgust_offset,pressabs_factor,pressabs_offset,rain_factor,rain_offset,winddir_offset;
float illu_factor,illu_offset,uv_factor,uv_offset;
} c = {1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,0,1,0,1,0};
typedef enum log_event
{ LOG_DEBUG=1,
LOG_WARNING=2,
LOG_ERROR=4,
LOG_INFO=8
} log_event;
FILE *_log_debug=NULL,*_log_warning=NULL,*_log_error=NULL,*_log_info=NULL;
void logger(log_event event,char *function,char *msg,...);
char *errorstring="N/A"; // What to write if value is not available or out of range
char *format=DEFAULT_FORMAT, *add_url[MAX_ADD_URLS];
char *FHEM_errorstring="";
char *FHEM_format=DEFAULT_FORMAT;
char *FHEM_file=NULL;
char *frewe_server_url=NULL, *frewe_server_key=NULL, *frewe_server_url_submit=NULL, *frewe_server_url_lasttime=NULL, *frewe_server_url_error=NULL, *frewe_server_url_alarm=NULL;
char *frewe_server_senddata=NULL, *frewe_server_resend=NULL, *error_email=NULL;
char *ws_type="WH1080"; // default to WH1080
char ws_entry_size;
int run_interval=0; // in seconds, 0 means run once and exit, change it by -r option or RunInterval cfg
int read_period; // Minutes between each stored reading (set in the WS configuration)
int altitude=0; // default altitude is sea level in meter - change it by -A option or Altitude cfg
uint16_t vendor=DEFAULT_VENDOR,product=DEFAULT_PRODUCT;
char add_url_counter=0, alm_counter=0;
usb_dev_handle *dev=NULL;
//char filebuf[256];
char *filebuf=NULL; // Will be allocated by html_fetcher
char *frewe_server_url_submit_template = "%s?serverkey=%s&action=addrecord&datetime=%%n&tempin=%%I&tempout=%%O&tempdew=%%E&tempchill=%%C&humin=%%h&humout=%%H&windgust=%%G&windspeed=%%W&winddir=%%D&pressabs=%%P&pressrel=%%L&rain=%%R&illu=%%M&uv=%%U&rainrate=%%S";
char *frewe_server_url_lasttime_template = "%s?serverkey=%s&action=getlasttime";
char *frewe_server_url_error_template = "%s?serverkey=%s&action=submiterror&email=%s";
char *frewe_server_url_alarm_template = "%s?serverkey=%s&action=alarm";
struct wservice
{ char *name;
char *userkey;
char *passkey;
char *url;
char *user;
char *pass;
char md5;
char *error;
char resend;
} ws[] =
{ { "Weather Underground", "WUnderground_StationID", "WUnderground_Password", "http://weatherstation.wunderground.com/weatherstation/updateweatherstation.php?action=updateraw&ID=%x&PASSWORD=%X&dateutc=%n&winddir=%d&windspeedmph=%w&windgustmph=%g&humidity=%H&tempf=%o&dewptf=%e&baromin=%l&indoortempf=%i&indoorhumidity=%h&rainin=%s&dailyrainin=%t&solarradiation=%m&UV=%U&softwaretype=Freetz%%20Weather", NULL, NULL, 0, "", 1},
{ "PWS Weather", "PWSWeather_StationID", "PWSWeather_Password", "http://www.pwsweather.com/pwsupdate/pwsupdate.php?action=updateraw&ID=%x&PASSWORD=%X&dateutc=%n&winddir=%d&windspeedmph=%w&windgustmph=%g&humidity=%H&tempf=%o&dewptf=%e&baromin=%l&indoortempf=%i&indoorhumidity=%h&rainin=%s&dailyrainin=%t&solarradiation=%m&UV=%U&softwaretype=Freetz%%20Weather%%20on%%20%K", NULL, NULL, 0, "", 0},
{ "Awekas", "Awekas_Username", "Awekas_Password", "http://www.awekas.at/extern/eingabe_pruefung.php?val=%x;%X;%Y;%Z;%O;%H;%L;%T;%W;%d;;;;de;;%G;%m;%U;;;;%S;", NULL, NULL, 1, "", 0},
{ "MET Office", "METOffice_SiteID", "METOffice_SiteAuthKey", "http://wow.metoffice.gov.uk/automaticreading?siteid=%x&siteAuthenticationKey=%X&dateutc=%n&winddir=%d&windspeedmph=%w&windgustmph=%g&humidity=%H&tempf=%o&dewptf=%e&baromin=%l&indoortempf=%i&indoorhumidity=%h&rainin=%s&dailyrainin=%t&solarradiation=%m&UV=%U&softwaretype=Freetz%%20Weather%%20on%%20%K", NULL, NULL, 0, "", 1},
// { "Wetternetz Sachsen", "WetternetzSachsen_UserID", "WetternetzSachsen_Password", "http://www.wetternetz-sachsen.de/get_daten_20.php?var=%x;%X;V2.0;%K;%Z;%Y;%O;--;--;--;--;--;%H;%E;--;--;--;--;--;--;--;%S;--;--;--;--;%T;--;%W;%d;%G;--;--;--;--;--;--;--;--;--;%C;--;--;%L;%P;--;--;--;--;--;--;--;--;--;--;--;--;--;--;%m;--;--;%U;--;%M;--;--;--;--;--;--;--;--;--;--;--;--;--;--;--;--;%R;--;--;--;--;--;--;--;--;--;--;--;--;--", NULL, NULL, 0, "", 0},
{ "Wetternetz Sachsen", "WetternetzSachsen_UserID", "WetternetzSachsen_Password", "http://www.wetternetz-sachsen.de/get_daten_21.php?var=%x;%X;V2.1;FreetzW;%Z;%Y;-2;%O;--;--;--;--;--;%H;--;--;%S;--;--;--;%W;%d;%G;--;--;--;--;--;%C;--;--;%L;%P;--;--;--;--;--;--;--;%m;--;--;%U;--;--;--;--;--;--;--;--;--;--;--;--;--;--;--;--;--;--;--;--;--;", NULL, NULL, 0, "", 0},
{ "Wetter.com", "Wetter.com_Username", "Wetter.com_Password", "http://www.wetterarchiv.de/interface/http/input.php?benutzername=%x&passwort=%X&datum=%y&feuchtigkeit=%H&temperatur=%O&windrichtung=%d&windstaerke=%v&luftdruck=%L&niederschlagsmenge=%S&niederschlagsmenge_zeit=60", NULL, NULL, 0, "", 1},
{ "Wetter.de OBSOLETED", "Wetter.de_Username", "Wetter.de_Password", "http://www.wetterarchiv.de/interface/http/input.php?benutzername=%x&passwort=%X&datum=%y&feuchtigkeit=%H&temperatur=%O&windrichtung=%d&windstaerke=%v&luftdruck=%L&niederschlagsmenge=%S&niederschlagsmenge_zeit=60", NULL, NULL, 0, "", 1}, // This is only for compatibility to pre 1.11 version config files
{ "Wedaal", "Wedaal_Username", "Wedaal_StationPass", "http://www.wedaal.de/get_wetter.php?val=%x;%X;%O;%H;%L;%T;%W;%d;%Z;%Y;;;;;;;;;;;;;;;", NULL, NULL, 1, "", 0}
};
char *walarm_type[] =
{ "HighOutdoorTemp", "LowOutdoorTemp", "HighWindchillTemp", "LowWindchillTemp", "HighDewTemp", "LowDewTemp",
"HighIndoorTemp", "LowIndoorTemp", "HighOutdoorHumidity", "LowOutdoorHumidity", "HighIndoorHumidity", "LowIndoorHumidity",
"HighRelPressure", "LowRelPressure", "HighWind", "LowWind", "HighGust", "LowGust",
"HighRainHour", "LowRainHour", "HighRainDay", "LowRainDay", "HighIllumination", "LowIllumination", "HighUV", "LowUV"
};
struct walarm
{ char *type;
float threshold;
char set;
char *url;
char *run;
char *email;
} alm[MAX_ALARMS];
//***************************************************************
// MAIN
//***************************************************************
int main(int argc, char **argv)
{
int rv=0,c,i,l;
uint8_t help=0,dump=0, md5[16];
char *cp, md5str[33];
int position=0,startpos,endpos,curpos; // default position is 0 (=now) - altering this by -p option can lead to read some of stored values
int pos60, pos0h;
int data_count;
int last_age;
uint16_t address,address0,address60,address0h;
long pause;
time_t starttime,curtime,lasttime;
struct timeval tact, tlast;
struct tm *tmptr, tm;
char *output;
FILE *fd;
_log_error=stderr;
_log_info=stderr;
// Handle signals, don't break on SIGHUP!
signal(SIGTERM, signal_handler);
signal(SIGQUIT, signal_handler);
signal(SIGINT, signal_handler);
signal(SIGFPE, signal_handler);
signal(SIGILL, signal_handler);
signal(SIGSEGV, signal_handler);
signal(SIGBUS, signal_handler);
// Init add_url array, need it?
for (i=0;i<MAX_ADD_URLS;i++) add_url[i]=NULL;
// Parse options
while (rv==0 && (c=getopt(argc,argv,"hH?vxf:d:a:A:p:e:t:s:c:u:r:t:k:"))!=-1)
{
switch (c)
{
case 'a': // set device id
sscanf(optarg,"%hX:%hX",&vendor,&product);
logger(LOG_DEBUG,"main","USB device set to vendor=%04X product=%04X",vendor,product);
break;
case 'A': // set altitude
sscanf(optarg,"%d",&altitude);
logger(LOG_DEBUG,"main","altitude set to %d",altitude);
break;
case 'p': // set position
sscanf(optarg,"%d",&position);
logger(LOG_DEBUG,"main","weather station log position set to %d",position);
break;
case 'c': // read configuration from file
i=read_cfg(optarg);
logger(LOG_DEBUG,"main","reading cfg from file %s returned %d",optarg,i);
break;
case 'r': // set continuous run with time interval
sscanf(optarg,"%d",&run_interval);
logger(LOG_DEBUG,"main","continuos run interval set to %d seconds",run_interval);
break;
case 'v': // verbose messages
_log_debug=_log_warning=stderr;
logger(LOG_DEBUG,"main","Verbose messaging turned on");
break;
case 'x': // XML export
optarg = "<data>\
\n\t<timestamp>\n\t\t<data>%N</data>\n\t</timestamp>\
\n\t<temp>\n\t\t<indoor>\n\t\t\t<data>%I</data>\n\t\t\t<unit>C</unit>\n\t\t</indoor>\n\t\t<outdoor>\n\t\t\t<data>%O</data>\n\t\t\t<unit>C</unit>\n\t\t</outdoor>\n\t\t<windchill>\n\t\t\t<data>%C</data>\n\t\t\t<unit>C</unit>\n\t\t</windchill>\n\t\t<dewpoint>\n\t\t\t<data>%E</data>\n\t\t\t<unit>C</unit>\n\t\t</dewpoint>\n\t</temp>\
\n\t<wind>\n\t\t<speed>\n\t\t\t<data>%W</data>\n\t\t\t<unit>km/h</unit>\n\t\t</speed>\n\t\t<gust>\n\t\t\t<data>%G</data>\n\t\t\t<unit>km/h</unit>\n\t\t</gust>\n\t\t<direct>\n\t\t\t<data>%d</data>\n\t\t\t<unit>degrees</unit>\n\t\t</direct>\n\t\t<direct_str>\n\t\t\t<data>%D</data>\n\t\t\t<unit>Str</unit>\n\t\t</direct_str>\n\t</wind>\
\n\t<pressure>\n\t\t<abs>\n\t\t\t<data>%P</data>\n\t\t\t<unit>hPa</unit>\n\t\t</abs>\n\t\t<rel>\n\t\t\t<data>%L</data>\n\t\t\t<unit>hPa</unit>\n\t\t</rel>\n\t</pressure>\
\n\t<rain>\n\t\t<hour>\n\t\t\t<data>%?</data>\n\t\t\t<unit>mm</unit>\n\t\t</hour>\n\t\t<day>\n\t\t\t<data>%?</data>\n\t\t\t<unit>mm</unit>\n\t\t</day>\n\t\t<total>\n\t\t\t<data>%R</data>\n\t\t\t<unit>mm</unit>\n\t\t</total>\n\t</rain>\
\n\t<humidity>\n\t\t<indoor>\n\t\t\t<data>%h</data>\n\t\t\t<unit>%%</unit>\n\t\t</indoor>\n\t\t<outdoor>\n\t\t\t<data>%H</data>\n\t\t\t<unit>%%</unit>\n\t\t</outdoor>\n\t</humidity>\
\n</data>\n";
case 'f': // Format output
logger(LOG_DEBUG,"main","Format output using '%s'",optarg);
format=optarg;
break;
case 's': // Server URL
logger(LOG_DEBUG,"main","Server URL set to '%s'",optarg);
frewe_server_url=optarg;
break;
case 'k': // Server Key
logger(LOG_DEBUG,"main","Server Key set to '%s'",optarg);
frewe_server_key=optarg;
break;
case 't': // Device Type
logger(LOG_DEBUG,"main","Device type set to '%s'",optarg);
ws_type=optarg;
break;
case 'u': // Additional URL
logger(LOG_DEBUG,"main","Additional URL set to '%s'",optarg);
if (add_url_counter<MAX_ADD_URLS) add_url[add_url_counter++]=optarg;
break;
case 'e': // Error string
logger(LOG_DEBUG,"main","Error string set to: '%s'",optarg);
errorstring=optarg;
break;
case 'd': // Dump raw data from weather station
{
uint16_t a,s,w;
dump=1;
a=0;
s=0x100;
w=16;
// sscanf(optarg,"0x%hX:0x%hX",&a,&s);
if (sscanf(optarg,"0x%hX:0x%hX",&a,&s)<2)
if (sscanf(optarg,"0x%hX:%hu",&a,&s)<2)
if (sscanf(optarg,"%hu:0x%hX",&a,&s)<2)
if (sscanf(optarg,"%hu:%hu",&a,&s)<2)
if (sscanf(optarg,":0x%hX",&s)<1)
sscanf(optarg,":%hu",&s);
logger(LOG_DEBUG,"main","Dump options address=%u size=%u",a,s);
if (dev==NULL) rv=ws_open(&dev,vendor,product,0);
if (dev)
{
uint8_t *b;
logger(LOG_DEBUG,"main","Allocating %u bytes for read buffer",s);
b=malloc(s);
if (!b)
logger(LOG_ERROR,"main","Could not allocate %u bytes for read buffer",s);
else
{
logger(LOG_DEBUG,"main","Allocated %u bytes for read buffer",s);
ws_read(dev,a,b,s);
ws_dump(a,b,s,w);
free(b);
}
}
if (dev) ws_close(&dev);
break;
}
case 'H':
{
help=1;
printf("Freetz Weather Client (frewe-client) for FRITZ!Box\n");
printf("Alexey Ozerov (c) 2014 - ver. %s\n\n", PROGRAM_VERSION);
break;
}
case '?':
case 'h':
help=1;
printf("Freetz Weather Client (frewe-client) for FRITZ!Box\n");
printf("Alexey Ozerov (c) 2014 - ver. %s\n\n", PROGRAM_VERSION);
printf("Options\n");
printf(" -? -h Display this help\n");
printf(" -a <v>:<p> Change the vendor:product address of the usb device from the default\n");
printf(" -A <alt in m> Change altitude\n");
printf(" -c <filename> Read configuration from cfg file\n");
printf(" -p <pos> Alter position in weather station log from current position (can be +- value)\n");
printf(" -v Verbose output, enable debug and warning messages\n");
printf(" -d [addr]:[len] Dump length bytes from address\n");
printf(" -x XML output\n");
printf(" -r <sec> Run continuosly with given interval in seconds\n");
printf(" -s <url> Freetz weather server URL\n");
printf(" -k <key> Freetz weather server key\n");
printf(" -t <type> Weather Station Type: WH1080 (default) or WH3080\n");
printf(" -u <url> Additional URL to submit the data (format like -f)\n");
printf(" -e <errstr> Write this errstr if measured value is out of range (e.g. outdoor unit is disconnected)\n");
printf(" -f <string> Format output to user defined string\n");
printf(" %%a - record age\n");
printf(" %%C - outside wind chill temperature C\n");
printf(" %%c - outside wind chill temperature F\n");
printf(" %%D - wind direction - named\n");
printf(" %%d - wind direction - degrees\n");
printf(" %%E - outside dew temperature C\n");
printf(" %%e - outside dew temperature F\n");
printf(" %%G - wind gust in km/h\n");
printf(" %%g - wind gust in mph\n");
printf(" %%h - inside humidity\n");
printf(" %%H - outside humidity\n");
printf(" %%I - inside temperature C\n");
printf(" %%i - inside temperature F\n");
printf(" %%K - weather station type\n");
printf(" %%L - relative pressure in hPa\n");
printf(" %%l - relative pressure in inch\n");
printf(" %%m - illumination in W/m (WH3080 only)\n");
printf(" %%M - illumination in lux (WH3080 only)\n");
printf(" %%N - date/time string YYYY-MM-DD HH:MM:SS local time\n");
printf(" %%n - date/time string YYYY-MM-DD HH:MM:SS UTC time\n");
printf(" %%O - outside temperature C\n");
printf(" %%o - outside temperature F\n");
printf(" %%P - absolute pressure in hPa\n");
printf(" %%p - absolute pressure in inch\n");
printf(" %%R - rain total from meteostation start in mm\n");
printf(" %%r - rain total from meteostation start in inch\n");
printf(" %%S - rain last 60 min in mm\n");
printf(" %%s - rain last 60 min in inch\n");
printf(" %%T - rain from 0h local time in mm\n");
printf(" %%t - rain from 0h local time in inch\n");
printf(" %%U - UV radiation (WH3080 only)\n");
printf(" %%v - wind speed in m/s\n");
printf(" %%W - wind speed in km/h\n");
printf(" %%w - wind speed in mph\n");
printf(" %%Y - date string DD.MM.YYYY local time\n");
printf(" %%y - date/time string YYYYMMDDhhmm local time\n");
printf(" %%Z - time string HH:MM local time\n");
}
}
if (rv==0 && help==0 && dump==0)
{
// Set entry size according to device type and create buffers
if(strcasecmp(ws_type,"WH3080")==0 || strcasecmp(ws_type,"WH3081")==0)
ws_entry_size=0x14;
else
ws_entry_size=0x10;
uint8_t buffer[ws_entry_size],buffer60[ws_entry_size],buffer0h[ws_entry_size];
// Prepare frewe-server URLs
if (frewe_server_url!=NULL && frewe_server_key!=NULL)
{
l=strlen(frewe_server_url)+strlen(frewe_server_key);
if (strcasecmp(frewe_server_senddata,"On")==0)
{ frewe_server_url_submit = malloc(l+strlen(frewe_server_url_submit_template));
if (!frewe_server_url_submit)
logger(LOG_ERROR,"main","Could not allocate %u bytes for frewe-server URL",l+strlen(frewe_server_url_submit_template));
else
sprintf(frewe_server_url_submit,frewe_server_url_submit_template,frewe_server_url,frewe_server_key);
}
if (strcasecmp(frewe_server_resend,"On")==0)
{ frewe_server_url_lasttime = malloc(l+strlen(frewe_server_url_lasttime_template));
if (!frewe_server_url_lasttime)
logger(LOG_ERROR,"main","Could not allocate %u bytes for frewe-server URL",l+strlen(frewe_server_url_lasttime_template));
else
sprintf(frewe_server_url_lasttime,frewe_server_url_lasttime_template,frewe_server_url,frewe_server_key);
}
if (error_email!=NULL)
{ frewe_server_url_error = malloc(l+strlen(frewe_server_url_error_template)+strlen(error_email));
if (!frewe_server_url_error)
logger(LOG_ERROR,"main","Could not allocate %u bytes for frewe-server URL",l+strlen(frewe_server_url_error_template)+strlen(error_email));
else
sprintf(frewe_server_url_error,frewe_server_url_error_template,frewe_server_url,frewe_server_key,error_email);
}
if (1)
{ frewe_server_url_alarm = malloc(l+strlen(frewe_server_url_alarm_template));
if (!frewe_server_url_alarm)
logger(LOG_ERROR,"main","Could not allocate %u bytes for frewe-server URL",l+strlen(frewe_server_url_alarm_template));
else
sprintf(frewe_server_url_alarm,frewe_server_url_alarm_template,frewe_server_url,frewe_server_key);
}
}
// Make a pause for the Fritzbox to set time and connect to internet
time(&starttime);
while (starttime<10000)
{
logger(LOG_WARNING,"main","Time is still unset: %d, wait 10 secs...", starttime);
sleep(10);
time(&starttime);
}
// Get read period from WS
rv=ws_open(&dev,vendor,product,0);
if (rv==0) rv=ws_read(dev,WS_READ_PERIOD_ADDRESS,buffer,1);
if (rv==0)
{ read_period=buffer[0];
logger(LOG_DEBUG,"main","Weather station read period is %d minutes",read_period);
}
ws_close(&dev);
if (rv!=0)
{ logger(LOG_ERROR,"main","Can't get read period from weather station. Stopped!");
return rv;
}
// Start main loop for run_interval repetitions
w.ok=w1.ok=0;
do
{
rv=0; // reset errors
startpos=endpos=position; // default
// Read current time, this will be the time for record in position 0
time(&curtime);
tmptr=localtime(&curtime);
gettimeofday(&tlast, NULL);
// Get the current data count (records actually saved on ws)
if (rv==0)
{ rv=ws_open(&dev,vendor,product,0);
if (rv==0) rv=ws_read(dev,WS_DATA_COUNT_ADDRESS,buffer,2);
if (rv==0)
{ data_count=buffer[0]+buffer[1]*256;
if (data_count<0 || data_count>WS_TOTAL_ENTRIES) data_count=WS_TOTAL_ENTRIES;
logger(LOG_DEBUG,"main","Data count is %d",data_count);
}
ws_close(&dev);
}
// Get last time saved on frewe-server and calculate positions
if (rv==0 && frewe_server_url_lasttime!=NULL)
{
logger(LOG_DEBUG,"main","Getting lasttime from server URL: %s", frewe_server_url_lasttime);
rv=ws_submit(frewe_server_url_lasttime,&filebuf);
if (rv==0 && strlen(filebuf)>25) rv=1; // Got some buggy output which can cause SIGSERV in strptime
if (rv==0 && strncasecmp(filebuf,"Not found",9)==0) // If lasttime not found try to read all records from WS
{
startpos=1-data_count;
endpos=0;
logger(LOG_INFO,"main","Will now read ALL entries from %d to %d, this will take time...",startpos,endpos);
}
else if (rv==0) // If lasttime found read only newer records
{
logger(LOG_DEBUG,"main","Last record datetime is %s",filebuf);
if (!strptime(filebuf, "%Y-%m-%d %H:%M:%S", &tm)) rv=1;
if (rv!=0) logger(LOG_ERROR,"main","Failed to convert lasttime from %s", filebuf);
if (rv==0)
{ tm.tm_isdst = -1; // tells mktime() to determine whether daylight saving time is in effect
lasttime = timegm(&tm); // mktime assumes the time in tm struct is localtime, but this time it's UTC
if (lasttime == -1) rv=1;
if (rv!=0) logger(LOG_ERROR,"main","Failed to get lasttime seconds from %s", filebuf);
}
if (rv==0)
{ logger(LOG_DEBUG,"main","Lasttime on frewe-server is %d, time gap is %d",lasttime,curtime-lasttime);
startpos=floor((float)(curtime-lasttime-1)/read_period/60)*-1; // -1 logical seconds to avoid double submittions
if (startpos<1-data_count) startpos=1-data_count;
endpos=0;
logger(LOG_WARNING,"main","Will now read entries from %d to %d",startpos,endpos);
}
else
rv=0; // Ignore this error and read the current position
}
else
{ logger(LOG_ERROR,"main","Failed to get lasttime from %s", frewe_server_url_lasttime);
rv=0; // Ignore this error and get the data only for the current position
}
}
// Warn if position doesn't meet a real record
if (rv==0 && (startpos>0 || startpos<1-data_count || endpos >0 || endpos<1-data_count))
logger(LOG_INFO,"main","Position is out of available data, %d records are saved on device",data_count);
// Read last record address & age
if (rv==0)
{
rv=ws_open(&dev,vendor,product,0);
if (rv==0) rv=ws_read(dev,WS_CURRENT_POSITION_ADDRESS,buffer,2);
if (rv==0) address=buffer[0]+buffer[1]*256;
if (rv==0) rv=ws_read(dev,address,buffer,sizeof(buffer));
if (rv==0) last_age = (int) buffer[0x00];
if (rv!=0) logger(LOG_ERROR,"main","Can't read last position address or age from WS");
ws_close(&dev);
}
// Positions loop
if (rv==0)
{
for (curpos=startpos;curpos<=endpos;curpos++) // NB: data errors don't break this loop
{
rv=ws_open(&dev,vendor,product,0); // rv is reset here
// Read record for the current position
address0=get_address(address,curpos);
if (rv==0) rv=ws_read(dev,address0,buffer,sizeof(buffer));