/*
Weather Protocol plugin for Miranda IM
Copyright (c) 2012 Miranda NG team
Copyright (c) 2005-2011 Boris Krasnovskiy All Rights Reserved
Copyright (c) 2002-2005 Calvin Che
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; version 2
of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
/*
This file contain the source related loading, obtaining, and
saving individual weather data for a weather contact.
*/
#include "stdafx.h"
/////////////////////////////////////////////////////////////////////////////////////////
// initialize weather info by loading values from database
// hContact = current contact handle
// return value = the current weather information in WEATHERINFO struct
WEATHERINFO CWeatherProto::LoadWeatherInfo(MCONTACT hContact)
{
// obtaining values from the DB
// assuming station ID must exist at all time, but others does not have to
// if the string is not found in database, a value of "N/A" is stored in the field
WEATHERINFO winfo;
winfo.hContact = hContact;
wcsncpy_s(winfo.id, getMStringW(hContact, "ID"), _countof(winfo.id));
if (db_get_wstatic(hContact, m_szModuleName, "Nick", winfo.city, _countof(winfo.city)))
wcsncpy(winfo.city, NODATA, _countof(winfo.city) - 1);
if (db_get_wstatic(hContact, WEATHERCONDITION, "Update", winfo.update, _countof(winfo.update)))
wcsncpy(winfo.update, NODATA, _countof(winfo.update) - 1);
if (db_get_wstatic(hContact, WEATHERCONDITION, "Condition", winfo.cond, _countof(winfo.cond)))
wcsncpy(winfo.cond, NODATA, _countof(winfo.cond) - 1);
if (db_get_wstatic(hContact, WEATHERCONDITION, "Temperature", winfo.temp, _countof(winfo.temp)))
wcsncpy(winfo.temp, NODATA, _countof(winfo.temp) - 1);
if (db_get_wstatic(hContact, WEATHERCONDITION, "High", winfo.high, _countof(winfo.high)))
wcsncpy(winfo.high, NODATA, _countof(winfo.high) - 1);
if (db_get_wstatic(hContact, WEATHERCONDITION, "Low", winfo.low, _countof(winfo.low)))
wcsncpy(winfo.low, NODATA, _countof(winfo.low) - 1);
if (db_get_wstatic(hContact, WEATHERCONDITION, "Sunset", winfo.sunset, _countof(winfo.sunset)))
wcsncpy(winfo.sunset, NODATA, _countof(winfo.sunset) - 1);
if (db_get_wstatic(hContact, WEATHERCONDITION, "Sunrise", winfo.sunrise, _countof(winfo.sunrise)))
wcsncpy(winfo.sunrise, NODATA, _countof(winfo.sunrise) - 1);
if (db_get_wstatic(hContact, WEATHERCONDITION, "Wind speed", winfo.wind, _countof(winfo.wind)))
wcsncpy(winfo.wind, NODATA, _countof(winfo.wind) - 1);
if (db_get_wstatic(hContact, WEATHERCONDITION, "Wind direction", winfo.winddir, _countof(winfo.winddir)))
wcsncpy(winfo.winddir, NODATA, _countof(winfo.winddir) - 1);
if (db_get_wstatic(hContact, WEATHERCONDITION, "Dew point", winfo.dewpoint, _countof(winfo.dewpoint)))
wcsncpy(winfo.dewpoint, NODATA, _countof(winfo.dewpoint) - 1);
if (db_get_wstatic(hContact, WEATHERCONDITION, "Pressure", winfo.pressure, _countof(winfo.pressure)))
wcsncpy(winfo.pressure, NODATA, _countof(winfo.pressure) - 1);
if (db_get_wstatic(hContact, WEATHERCONDITION, "Visibility", winfo.vis, _countof(winfo.vis)))
wcsncpy(winfo.vis, NODATA, _countof(winfo.vis) - 1);
if (db_get_wstatic(hContact, WEATHERCONDITION, "Humidity", winfo.humid, _countof(winfo.humid)))
wcsncpy(winfo.humid, NODATA, _countof(winfo.humid) - 1);
if (db_get_wstatic(hContact, WEATHERCONDITION, "Feel", winfo.feel, _countof(winfo.feel)))
wcsncpy(winfo.feel, NODATA, _countof(winfo.feel) - 1);
return winfo;
}
/////////////////////////////////////////////////////////////////////////////////////////
// erase all current weather information from database
// lastver = the last used version number in dword (using PLUGIN_MAKE_VERSION)
void CWeatherProto::EraseAllInfo()
{
wchar_t str[255];
int ContactCount = 0;
MCONTACT LastContact = NULL;
DBVARIANT dbv;
// loop through all contacts
for (auto &hContact : AccContacts()) {
setWord(hContact, "Status", ID_STATUS_OFFLINE);
setWord(hContact, "StatusIcon", -1);
db_unset(hContact, "CList", "MyHandle");
// clear all data
if (getWString(hContact, "Nick", &dbv)) {
setWString(hContact, "Nick", TranslateT(""));
setString(hContact, "LastLog", "never");
setString(hContact, "LastCondition", "None");
setString(hContact, "LastTemperature", "None");
}
else db_free(&dbv);
db_delete_module(hContact, WEATHERCONDITION);
db_set_s(hContact, "UserInfo", "MyNotes", "");
// reset update tag
setByte(hContact, "IsUpdated", FALSE);
// reset logging settings
if (!getWString(hContact, "Log", &dbv)) {
setByte(hContact, "File", (uint8_t)(dbv.pwszVal[0] != 0));
db_free(&dbv);
}
else setByte(hContact, "File", FALSE);
// if no default station find, assign a new one
if (opt.Default[0] == 0) {
wcsncpy_s(opt.Default, getMStringW(hContact, "ID"), _countof(opt.Default));
opt.DefStn = hContact;
if (!getWString(hContact, "Nick", &dbv)) {
mir_snwprintf(str, TranslateT("%s is now the default weather station"), dbv.pwszVal);
db_free(&dbv);
MessageBox(nullptr, str, TranslateT("Weather Protocol"), MB_OK | MB_ICONINFORMATION);
}
}
// get the handle of the default station
if (opt.DefStn == NULL) {
if (!getWString(hContact, "ID", &dbv)) {
if (!mir_wstrcmp(dbv.pwszVal, opt.Default))
opt.DefStn = hContact;
db_free(&dbv);
}
}
ContactCount++; // increment counter
LastContact = hContact;
}
// if weather contact exists, set the status to online so it is ready for update
// if (ContactCount != 0) status = ONLINE;
// in case where the default station is missing
if (opt.DefStn == NULL && ContactCount != 0) {
if (!getWString(LastContact, "ID", &dbv)) {
wcsncpy(opt.Default, dbv.pwszVal, _countof(opt.Default) - 1);
db_free(&dbv);
}
opt.DefStn = LastContact;
if (!getWString(LastContact, "Nick", &dbv)) {
mir_snwprintf(str, TranslateT("%s is now the default weather station"), dbv.pwszVal);
db_free(&dbv);
MessageBox(nullptr, str, TranslateT("Weather Protocol"), MB_OK | MB_ICONINFORMATION);
}
}
// save option in case of default station changed
setWString("Default", opt.Default);
}
/////////////////////////////////////////////////////////////////////////////////////////
static wchar_t rumbs[][16] = {
LPGENW("N"), LPGENW("NNE"), LPGENW("NE"), LPGENW("ENE"),
LPGENW("E"), LPGENW("ESE"), LPGENW("ES"), LPGENW("SSE"),
LPGENW("S"), LPGENW("SSW"), LPGENW("SW"), LPGENW("WSW"),
LPGENW("W"), LPGENW("WNW"), LPGENW("WN"), LPGENW("NNW")
};
static wchar_t *degree2str(double angle)
{
double a = 11.25;
for (int i = 0; i < _countof(rumbs); i++, a += 22.5)
if (angle < a)
return TranslateW(rumbs[i]);
// area between 348.75 & 360 degrees
return TranslateT("N");
}
void CWeatherProto::ConvertDataValue(WIDATAITEM *p)
{
wchar_t str[MAX_DATA_LEN];
// temperature
if (!mir_wstrcmp(p->Name, L"Temperature") || !mir_wstrcmp(p->Name, L"High") ||
!mir_wstrcmp(p->Name, L"Low") || !mir_wstrcmp(p->Name, L"Feel") ||
!mir_wstrcmp(p->Name, L"Dew point") ||
!mir_wstrcmpi(p->Unit, L"C") || !mir_wstrcmpi(p->Unit, L"F") ||
!mir_wstrcmpi(p->Unit, L"K")) {
GetTemp(p->Value, p->Unit, str);
p->Value = str;
}
// pressure
else if (!mir_wstrcmp(p->Name, L"Pressure") || !mir_wstrcmpi(p->Unit, L"HPA") ||
!mir_wstrcmpi(p->Unit, L"KPA") || !mir_wstrcmpi(p->Unit, L"MB") ||
!mir_wstrcmpi(p->Unit, L"TORR") || !mir_wstrcmpi(p->Unit, L"IN") ||
!mir_wstrcmpi(p->Unit, L"MM")) {
GetPressure(p->Value, p->Unit, str);
p->Value = str;
}
// speed
else if (!mir_wstrcmp(p->Name, L"Wind Speed") || !mir_wstrcmpi(p->Unit, L"KM/H") ||
!mir_wstrcmpi(p->Unit, L"M/S") || !mir_wstrcmpi(p->Unit, L"MPH") ||
!mir_wstrcmpi(p->Unit, L"KNOTS")) {
GetSpeed(p->Value, p->Unit, str);
p->Value = str;
}
// visibility
else if (!mir_wstrcmp(p->Name, L"Visibility") || !mir_wstrcmpi(p->Unit, L"KM") ||
!mir_wstrcmpi(p->Unit, L"MILES")) {
GetDist(p->Value, p->Unit, str);
p->Value = str;
}
// elevation
else if (!mir_wstrcmp(p->Name, L"Elevation") || !mir_wstrcmpi(p->Unit, L"FT") ||
!mir_wstrcmpi(p->Unit, L"M")) {
GetElev(p->Value, p->Unit, str);
p->Value = str;
}
// convert degrees to compass
else if (!mir_wstrcmpi(p->Unit, L"GRAD")) {
p->Value = degree2str(_wtof(p->Value));
}
// degree sign
else if (!mir_wstrcmpi(p->Unit, L"DEG")) {
if (!opt.DoNotAppendUnit)
p->Value.Append(opt.DegreeSign);
}
// percent sign
else if (!mir_wstrcmpi(p->Unit, L"%")) {
if (!opt.DoNotAppendUnit)
p->Value.Append(L"%");
}
// truncating strings for day/month to 2 or 3 characters
else if (!mir_wstrcmpi(p->Unit, L"DAY") || !mir_wstrcmpi(p->Unit, L"MONTH"))
if (opt.dUnit > 1 && mir_wstrlen(p->Value) > opt.dUnit)
p->Value.SetAt(opt.dUnit, '\0');
}
/////////////////////////////////////////////////////////////////////////////////////////
// data query
MHttpResponse* CWeatherProto::RunQuery(const wchar_t *id, int days)
{
wchar_t *pKey = m_szApiKey;
if (!mir_wstrlen(pKey)) {
WPShowMessage(TranslateT("You need to obtain the personal key and enter it in the account's Options dialog"), SM_WARNING);
return nullptr;
}
auto *pReq = new MHttpRequest(REQUEST_GET);
pReq->flags = NLHRF_HTTP11 | NLHRF_DUMPASTEXT;
pReq->m_szUrl = "https://weather.visualcrossing.com/VisualCrossingWebServices/rest/services/timeline/" + mir_urlEncode(T2Utf(id).get());
if (days) {
time_t today = time(0);
struct tm *p = localtime(&today);
pReq->m_szUrl.AppendFormat("/%04d-%02d-%02d", p->tm_year + 1900, p->tm_mon + 1, p->tm_mday);
today += 86400 * 7; // add one week
p = localtime(&today);
pReq->m_szUrl.AppendFormat("/%04d-%02d-%02d", p->tm_year + 1900, p->tm_mon + 1, p->tm_mday);
}
pReq << CHAR_PARAM("unitGroup", "metric") << WCHAR_PARAM("key", pKey) << CHAR_PARAM("contentType", "json");
if (days)
pReq << CHAR_PARAM("elements", "+elevation");
auto *ret = Netlib_HttpTransaction(m_hNetlibUser, pReq);
delete pReq;
return ret;
}