2016-12-29 13:26:23 +00:00
|
|
|
unit foobot_utility;
|
|
|
|
|
2016-12-30 07:28:09 +00:00
|
|
|
{ Foobot Utilities
|
2016-12-29 13:26:23 +00:00
|
|
|
|
|
|
|
Copyright (C)2016 Gordon Bamber minsadorada@charcodelvalle.com
|
|
|
|
|
|
|
|
This source 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; either version 2 of the License, or (at your option)
|
|
|
|
any later version.
|
|
|
|
|
|
|
|
This code 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.
|
|
|
|
|
|
|
|
A copy of the GNU General Public License is available on the World Wide Web
|
|
|
|
at <http://www.gnu.org/copyleft/gpl.html>. You can also obtain it by writing
|
|
|
|
to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
|
|
|
|
MA 02111-1307, USA.
|
|
|
|
|
|
|
|
VERSION HISTORY
|
|
|
|
===============
|
|
|
|
* HighLow routines
|
|
|
|
* Use GetAppGonfigFile for IniFile location
|
|
|
|
}
|
|
|
|
|
|
|
|
{$mode objfpc}{$H+}
|
|
|
|
|
|
|
|
interface
|
|
|
|
|
|
|
|
uses
|
|
|
|
Classes, SysUtils, Dialogs,
|
|
|
|
foobot_httpclient, foobot_objects, fpjson, fpjsonrtti, base64, variants,
|
2016-12-30 09:34:07 +00:00
|
|
|
DateUtils, INIFiles;
|
2016-12-29 13:26:23 +00:00
|
|
|
|
|
|
|
const
|
|
|
|
FOOBOT_USER_URL = 'https://api.foobot.io/v2/user/%s/login/';
|
|
|
|
FOOBOT_IDENTITY_URL = 'https://api.foobot.io/v2/owner/%s/device/';
|
|
|
|
FOOBOT_DATA_LAST_URL = 'https://api.foobot.io/v2/device/%s/datapoint/%s/%s/%s/';
|
|
|
|
FOOBOT_DATA_START_FINISH_URL =
|
|
|
|
'https://api.foobot.io/v2/device/%s/datapoint/%s/%s/%s/';
|
2017-01-08 18:49:20 +00:00
|
|
|
|
|
|
|
// Used throughout in arrays, counts etc.
|
2017-01-08 16:05:33 +00:00
|
|
|
C_TIME = 0;
|
|
|
|
C_PM = 1;
|
|
|
|
C_TMP = 2;
|
|
|
|
C_HUM = 3;
|
|
|
|
C_CO2 = 4;
|
|
|
|
C_VOC = 5;
|
|
|
|
C_ALLPOLLU = 6;
|
2017-01-09 07:56:13 +00:00
|
|
|
// Used in Trigger functions
|
|
|
|
C_HIGH = 0;
|
|
|
|
C_LOW = 1;
|
2016-12-29 13:26:23 +00:00
|
|
|
|
|
|
|
type
|
2017-01-08 18:49:20 +00:00
|
|
|
TDataFetchType = (dfLast, dfStartEnd); // FetchFoobotData
|
2017-01-08 21:18:05 +00:00
|
|
|
|
|
|
|
// Unused
|
2017-01-09 07:56:13 +00:00
|
|
|
TSensorType = (st_time, st_pm, st_tmp, st_hum, st_co2, st_voc, st_allpollu);
|
2017-01-08 21:18:05 +00:00
|
|
|
// Unused
|
2017-01-09 07:56:13 +00:00
|
|
|
TAlertType = (at_high, at_low);
|
2017-01-08 16:05:33 +00:00
|
|
|
|
|
|
|
TAlertRec = record
|
|
|
|
AlertTriggered: boolean;
|
|
|
|
AlertTime: TDateTime;
|
2017-01-08 21:18:05 +00:00
|
|
|
AlertType: Integer;
|
2017-01-08 16:05:33 +00:00
|
|
|
AlertValue: variant;
|
|
|
|
end;
|
2016-12-29 13:26:23 +00:00
|
|
|
|
|
|
|
function EncodeStringBase64(const s: string): string;
|
|
|
|
function FetchAuthenticationKey(aUsername, aUserPassword: string): boolean;
|
|
|
|
|
|
|
|
// Populates FoobotIdentityObject.TFoobotIdentityList collection
|
|
|
|
function FetchFoobotIdentity(aUsername, aSecretKey: string): boolean;
|
|
|
|
|
2016-12-30 16:24:25 +00:00
|
|
|
// Populates FoobotDataObject
|
2016-12-29 13:26:23 +00:00
|
|
|
function FetchFoobotData(DataFetchType: TDataFetchType = dfLast;
|
|
|
|
iCurrentFoobot: integer = 0; iLastIntervalSeconds: integer = 3600;
|
|
|
|
iLastAverageBySeconds: integer = 0; iStartTimeSeconds: int64 = 0;
|
|
|
|
iEndTimeSeconds: int64 = 0; aSecretKey: string = 'unknown'): boolean;
|
|
|
|
|
|
|
|
// Populates datapoint arrays from FoobotIdentityObject for easy access
|
|
|
|
// - also populates HighLow arrays
|
|
|
|
function FoobotDataObjectToArrays: boolean;
|
|
|
|
|
2017-01-08 21:18:05 +00:00
|
|
|
// Gets/Sets internal FooBotTriggerArray which can be tested against in FoobotDataObjectToArrays
|
2017-01-08 18:49:20 +00:00
|
|
|
// aSensor use consts: C_PM,C_TMP etc.
|
|
|
|
function SetHighTrigger(const aSensor: integer; const aValue: variant): boolean;
|
|
|
|
function SetLowTrigger(const aSensor: integer; const aValue: variant): boolean;
|
2017-01-08 21:18:05 +00:00
|
|
|
function GetHighTrigger(Const aSensor:Integer):Variant;
|
|
|
|
function GetLowTrigger(Const aSensor:Integer):Variant;
|
2017-01-08 16:05:33 +00:00
|
|
|
|
2016-12-29 13:26:23 +00:00
|
|
|
// Utility functions
|
|
|
|
function ResetArrays: boolean;
|
|
|
|
function ResetObjects: boolean;
|
2016-12-30 09:34:07 +00:00
|
|
|
function ResetHighLows: boolean;
|
|
|
|
function SaveHighLows: boolean;
|
|
|
|
function LoadHighLows: boolean;
|
2017-01-08 18:49:20 +00:00
|
|
|
function SaveTriggers: boolean;
|
|
|
|
function LoadTriggers: boolean;
|
2016-12-29 13:26:23 +00:00
|
|
|
|
|
|
|
var
|
2017-01-08 16:05:33 +00:00
|
|
|
// Used to fetch server data
|
2016-12-29 13:26:23 +00:00
|
|
|
HttpClient: TFPHTTPClient;
|
2017-01-08 18:49:20 +00:00
|
|
|
// Holds identity values for multiple Foobots (FoobotIdentityObject.FoobotIdentityList)
|
2016-12-29 13:26:23 +00:00
|
|
|
FoobotIdentityObject: TFoobotIdentityObject;
|
2017-01-08 16:05:33 +00:00
|
|
|
// Holds data for current Foobot
|
2016-12-29 13:26:23 +00:00
|
|
|
FoobotDataObject: TFoobotDataObject;
|
2017-01-08 18:49:20 +00:00
|
|
|
// Used in FetchAuthenticationKey
|
2016-12-29 13:26:23 +00:00
|
|
|
sAuthenticationKey: string;
|
2017-01-08 16:05:33 +00:00
|
|
|
// Boolean to enable/disable serialisation of HighLows. Default = TRUE
|
2016-12-30 09:34:07 +00:00
|
|
|
SaveLoadHighLows: boolean;
|
2017-01-08 18:49:20 +00:00
|
|
|
// Boolean to enable/disable Trigger functions. Default = FALSE
|
2017-01-08 16:05:33 +00:00
|
|
|
UseTriggers: boolean;
|
|
|
|
// Used in data fetch
|
2016-12-30 09:34:07 +00:00
|
|
|
TheCurrentFoobot: integer;
|
2017-01-08 16:05:33 +00:00
|
|
|
// INIFile located in GetAppConfig(false) folder
|
2016-12-30 09:34:07 +00:00
|
|
|
HLINI: TIniFile;
|
2017-01-08 16:05:33 +00:00
|
|
|
// Dynamic arrays - easier access to datapoints
|
2016-12-29 13:26:23 +00:00
|
|
|
// Call FoobotDataObjectToArrays to populate them
|
|
|
|
FoobotData_time: array of TDateTime;
|
|
|
|
FoobotData_pm: array of double;
|
|
|
|
FoobotData_tmp: array of double;
|
|
|
|
FoobotData_hum: array of double;
|
|
|
|
FoobotData_co2: array of integer;
|
|
|
|
FoobotData_voc: array of integer;
|
|
|
|
FoobotData_allpollu: array of double;
|
|
|
|
// Set in FoobotDataObjectToArrays
|
2017-01-08 18:49:20 +00:00
|
|
|
FoobotDataHighs: array[C_TIME..C_ALLPOLLU] of variant;
|
|
|
|
FoobotDataLows: array[C_TIME..C_ALLPOLLU] of variant;
|
|
|
|
FoobotDataHighTimes: array[C_TIME..C_ALLPOLLU] of variant;
|
|
|
|
FoobotDataLowTimes: array[C_TIME..C_ALLPOLLU] of variant;
|
2016-12-29 13:26:23 +00:00
|
|
|
|
2017-01-08 16:05:33 +00:00
|
|
|
// [0=Low(at_low)..1=High(at_high), C_PM..C_ALLPOLLU]
|
|
|
|
// Dynamic in case of future changes (e.g. more triggers)
|
|
|
|
FooBotTriggerArray: array of array of variant;
|
|
|
|
// Set by FoobotDataObjectToArrays
|
|
|
|
AlertRec: array [C_TIME..C_ALLPOLLU] of TAlertRec;
|
|
|
|
giCount: integer; // used in initialization
|
|
|
|
|
2016-12-29 13:26:23 +00:00
|
|
|
implementation
|
2016-12-30 09:34:07 +00:00
|
|
|
|
2017-01-08 18:49:20 +00:00
|
|
|
function SaveTriggers: boolean;
|
|
|
|
// To foobotmonitor.ini
|
|
|
|
var
|
|
|
|
sFoobotName: string;
|
|
|
|
begin
|
|
|
|
Result:=FALSE; // assume failure
|
|
|
|
if FoobotIdentityObject.FoobotIdentityList.Count = 0 then Exit(FALSE);
|
|
|
|
sFoobotName := FoobotIdentityObject.FoobotIdentityList[TheCurrentFoobot].Name;
|
|
|
|
if (sFoobotName = '') then
|
|
|
|
Exit(False);
|
|
|
|
if not Assigned(HLINI) then
|
|
|
|
HLINI := TIniFile.Create(ChangeFileExt(GetAppConfigFile(False), '.ini'));
|
|
|
|
try
|
|
|
|
// Store current Foobot triggers
|
|
|
|
with HLINI do
|
|
|
|
begin
|
|
|
|
WriteInteger('Foobot', 'CurrentFoobot', TheCurrentFoobot);
|
|
|
|
WriteString('Foobot', 'CurrentFoobotName', sFoobotName);
|
|
|
|
WriteFloat(sFoobotName, 'pmTriggerHigh',
|
2017-01-09 07:56:13 +00:00
|
|
|
double(FooBotTriggerArray[C_HIGH, C_PM]));
|
2017-01-08 18:49:20 +00:00
|
|
|
WriteFloat(sFoobotName, 'pmTriggerLow',
|
2017-01-09 07:56:13 +00:00
|
|
|
double(FooBotTriggerArray[C_LOW, C_PM]));
|
2017-01-08 18:49:20 +00:00
|
|
|
WriteFloat(sFoobotName, 'tmpTriggerHigh',
|
2017-01-09 07:56:13 +00:00
|
|
|
double(FooBotTriggerArray[C_HIGH, C_TMP]));
|
2017-01-08 18:49:20 +00:00
|
|
|
WriteFloat(sFoobotName, 'tmpTriggerLow',
|
2017-01-09 07:56:13 +00:00
|
|
|
double(FooBotTriggerArray[C_LOW, C_TMP]));
|
2017-01-08 18:49:20 +00:00
|
|
|
WriteFloat(sFoobotName, 'humTriggerHigh',
|
2017-01-09 07:56:13 +00:00
|
|
|
double(FooBotTriggerArray[C_HIGH, C_HUM]));
|
2017-01-08 18:49:20 +00:00
|
|
|
WriteFloat(sFoobotName, 'humTriggerLow',
|
2017-01-09 07:56:13 +00:00
|
|
|
double(FooBotTriggerArray[C_LOW, C_HUM]));
|
2017-01-08 18:49:20 +00:00
|
|
|
WriteFloat(sFoobotName, 'co2TriggerHigh',
|
2017-01-09 07:56:13 +00:00
|
|
|
Integer(FooBotTriggerArray[C_HIGH, C_CO2]));
|
2017-01-08 18:49:20 +00:00
|
|
|
WriteFloat(sFoobotName, 'co2TriggerLow',
|
2017-01-09 07:56:13 +00:00
|
|
|
Integer(FooBotTriggerArray[C_LOW, C_CO2]));
|
2017-01-08 18:49:20 +00:00
|
|
|
WriteFloat(sFoobotName, 'vocTriggerHigh',
|
2017-01-09 07:56:13 +00:00
|
|
|
Integer(FooBotTriggerArray[C_HIGH, C_VOC]));
|
2017-01-08 18:49:20 +00:00
|
|
|
WriteFloat(sFoobotName, 'vocTriggerLow',
|
2017-01-09 07:56:13 +00:00
|
|
|
Integer(FooBotTriggerArray[C_LOW, C_VOC]));
|
2017-01-08 18:49:20 +00:00
|
|
|
WriteFloat(sFoobotName, 'allpolluTriggerHigh',
|
2017-01-09 07:56:13 +00:00
|
|
|
double(FooBotTriggerArray[C_HIGH, C_ALLPOLLU]));
|
2017-01-08 18:49:20 +00:00
|
|
|
WriteFloat(sFoobotName, 'allpolluTriggerLow',
|
2017-01-09 07:56:13 +00:00
|
|
|
double(FooBotTriggerArray[C_LOW, C_ALLPOLLU]));
|
2017-01-08 18:49:20 +00:00
|
|
|
end;
|
|
|
|
Result := True;
|
|
|
|
except
|
|
|
|
raise Exception.Create('Could not save Triggers');
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
function LoadTriggers: boolean;
|
|
|
|
// From foobotmonitor.ini
|
|
|
|
var
|
|
|
|
sFoobotName: string;
|
|
|
|
begin
|
|
|
|
Result:=FALSE; // assume failure
|
|
|
|
if FoobotIdentityObject.FoobotIdentityList.Count = 0 then Exit(FALSE);
|
|
|
|
sFoobotName := FoobotIdentityObject.FoobotIdentityList[TheCurrentFoobot].Name;
|
|
|
|
if (sFoobotName = '') then
|
|
|
|
Exit(False);
|
|
|
|
if not Assigned(HLINI) then
|
|
|
|
HLINI := TIniFile.Create(ChangeFileExt(GetAppConfigFile(False), '.ini'));
|
|
|
|
// Make sure the High-Lows are for the current Foobot
|
|
|
|
if (HLINI.ReadString('Foobot', 'CurrentFoobotName', 'unknown') <> sFoobotName) then
|
|
|
|
Exit(False);
|
|
|
|
try
|
|
|
|
// Load current Foobot triggers
|
|
|
|
with HLINI do
|
|
|
|
begin
|
2017-01-09 07:56:13 +00:00
|
|
|
FooBotTriggerArray[C_HIGH, C_PM] := ReadFloat(sFoobotName, 'pmTriggerHigh', 0);
|
|
|
|
FooBotTriggerArray[C_LOW, C_PM] := ReadFloat(sFoobotName, 'pmTriggerLow', 0);
|
|
|
|
FooBotTriggerArray[C_HIGH, C_TMP] := ReadFloat(sFoobotName, 'tmpTriggerHigh', 0);
|
|
|
|
FooBotTriggerArray[C_LOW, C_TMP] := ReadFloat(sFoobotName, 'tmpTriggerLow', 0);
|
|
|
|
FooBotTriggerArray[C_HIGH, C_HUM] := ReadFloat(sFoobotName, 'humTriggerHigh', 0);
|
|
|
|
FooBotTriggerArray[C_LOW, C_HUM] := ReadFloat(sFoobotName, 'humTriggerLow', 0);
|
|
|
|
FooBotTriggerArray[C_HIGH, C_CO2] := ReadFloat(sFoobotName, 'co2TriggerHigh', 0);
|
|
|
|
FooBotTriggerArray[C_LOW, C_CO2] := ReadFloat(sFoobotName, 'co2TriggerLow', 0);
|
|
|
|
FooBotTriggerArray[C_HIGH, C_VOC] := ReadFloat(sFoobotName, 'vocTriggerHigh', 0);
|
|
|
|
FooBotTriggerArray[C_LOW, C_VOC] := ReadFloat(sFoobotName, 'vocTriggerLow', 0);
|
|
|
|
FooBotTriggerArray[C_HIGH, C_ALLPOLLU] :=
|
2017-01-08 18:49:20 +00:00
|
|
|
ReadFloat(sFoobotName, 'allpolluTriggerHigh', 0);
|
2017-01-09 07:56:13 +00:00
|
|
|
FooBotTriggerArray[C_LOW, C_ALLPOLLU] :=
|
2017-01-08 18:49:20 +00:00
|
|
|
ReadFloat(sFoobotName, 'allpolluTriggerLow', 0);
|
|
|
|
end;
|
|
|
|
Result := True;
|
|
|
|
except
|
|
|
|
raise Exception.Create('Could not load Triggers');
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
2016-12-30 09:34:07 +00:00
|
|
|
function SaveHighLows: boolean;
|
2017-01-08 16:05:33 +00:00
|
|
|
// Save values to an INI data file
|
2017-01-08 18:49:20 +00:00
|
|
|
// To foobotmonitor.ini
|
2016-12-30 09:34:07 +00:00
|
|
|
var
|
|
|
|
sFoobotName: string;
|
2016-12-29 13:26:23 +00:00
|
|
|
begin
|
2017-01-08 18:49:20 +00:00
|
|
|
Result:=FALSE; // assume failure
|
2016-12-30 09:34:07 +00:00
|
|
|
if SaveLoadHighLows = False then
|
|
|
|
Exit(False);
|
2017-01-08 18:49:20 +00:00
|
|
|
if FoobotIdentityObject.FoobotIdentityList.Count = 0 then Exit(FALSE);
|
2016-12-30 09:34:07 +00:00
|
|
|
sFoobotName := FoobotIdentityObject.FoobotIdentityList[TheCurrentFoobot].Name;
|
2017-01-08 18:49:20 +00:00
|
|
|
if (sFoobotName = '') then
|
|
|
|
Exit(False);
|
2016-12-30 09:34:07 +00:00
|
|
|
if not Assigned(HLINI) then
|
|
|
|
HLINI := TIniFile.Create(ChangeFileExt(GetAppConfigFile(False), '.ini'));
|
2016-12-29 13:26:23 +00:00
|
|
|
// Store current Foobot info
|
2017-01-08 18:49:20 +00:00
|
|
|
try
|
|
|
|
with HLINI do
|
|
|
|
begin
|
|
|
|
WriteInteger('Foobot', 'CurrentFoobot', TheCurrentFoobot);
|
|
|
|
WriteString('Foobot', 'CurrentFoobotName', sFoobotName);
|
|
|
|
|
|
|
|
// Particulates
|
|
|
|
WriteFloat(sFoobotName, 'pmHigh', double(FoobotDataHighs[C_PM]));
|
|
|
|
WriteDateTime(sFoobotName, 'pmHighTime', TDateTime(FoobotDataHighTimes[C_PM]));
|
|
|
|
WriteFloat(sFoobotName, 'pmLow', double(FoobotDataLows[C_PM]));
|
|
|
|
WriteDateTime(sFoobotName, 'pmLowTime', TDateTime(FoobotDataLowTimes[C_PM]));
|
|
|
|
// Temp
|
|
|
|
WriteFloat(sFoobotName, 'tmpHigh', double(FoobotDataHighs[C_TMP]));
|
|
|
|
WriteDateTime(sFoobotName, 'tmpHighTime', TDateTime(FoobotDataHighTimes[C_TMP]));
|
|
|
|
WriteFloat(sFoobotName, 'tmpLow', double(FoobotDataLows[C_TMP]));
|
|
|
|
WriteDateTime(sFoobotName, 'tmpLowTime', TDateTime(FoobotDataLowTimes[C_TMP]));
|
|
|
|
// Humidity
|
|
|
|
WriteFloat(sFoobotName, 'humHigh', double(FoobotDataHighs[C_HUM]));
|
|
|
|
WriteDateTime(sFoobotName, 'humHighTime', TDateTime(FoobotDataHighTimes[C_HUM]));
|
|
|
|
WriteFloat(sFoobotName, 'humLow', double(FoobotDataLows[C_HUM]));
|
|
|
|
WriteDateTime(sFoobotName, 'humLowTime', TDateTime(FoobotDataLowTimes[C_HUM]));
|
|
|
|
// CO2
|
|
|
|
WriteInteger(sFoobotName, 'co2High', integer(FoobotDataHighs[C_CO2]));
|
|
|
|
WriteDateTime(sFoobotName, 'co2HighTime', TDateTime(FoobotDataHighTimes[C_CO2]));
|
|
|
|
WriteInteger(sFoobotName, 'co2Low', integer(FoobotDataLows[C_CO2]));
|
|
|
|
WriteDateTime(sFoobotName, 'co2LowTime', TDateTime(FoobotDataLowTimes[C_CO2]));
|
|
|
|
// Volatile Compounds
|
|
|
|
WriteInteger(sFoobotName, 'vocHigh', integer(FoobotDataHighs[C_VOC]));
|
|
|
|
WriteDateTime(sFoobotName, 'vocHighTime', TDateTime(FoobotDataHighTimes[C_VOC]));
|
|
|
|
WriteInteger(sFoobotName, 'vocLow', integer(FoobotDataLows[C_VOC]));
|
|
|
|
WriteDateTime(sFoobotName, 'vocLowTime', TDateTime(FoobotDataLowTimes[C_VOC]));
|
|
|
|
// All Pollution
|
|
|
|
WriteFloat(sFoobotName, 'allpolluHigh', double(FoobotDataHighs[C_ALLPOLLU]));
|
|
|
|
WriteDateTime(sFoobotName, 'allpolluHighTime',
|
|
|
|
TDateTime(FoobotDataHighTimes[C_ALLPOLLU]));
|
|
|
|
WriteFloat(sFoobotName, 'allpolluLow', double(FoobotDataLows[C_ALLPOLLU]));
|
|
|
|
WriteDateTime(sFoobotName, 'allpolluLowTime',
|
|
|
|
TDateTime(FoobotDataLowTimes[C_ALLPOLLU]));
|
|
|
|
Result := True;
|
|
|
|
end;
|
|
|
|
except
|
|
|
|
raise Exception.Create('Could not save HighLows');
|
|
|
|
end;
|
2016-12-29 13:26:23 +00:00
|
|
|
end;
|
|
|
|
|
2016-12-30 09:34:07 +00:00
|
|
|
function LoadHighLows: boolean;
|
2017-01-08 16:05:33 +00:00
|
|
|
// Load values from an INI data file
|
2017-01-08 18:49:20 +00:00
|
|
|
// From foobotmonitor.ini
|
2016-12-30 09:34:07 +00:00
|
|
|
var
|
|
|
|
sFoobotName: string;
|
2016-12-29 13:26:23 +00:00
|
|
|
begin
|
2017-01-08 18:49:20 +00:00
|
|
|
Result:=FALSE; // assume failure
|
2016-12-30 09:34:07 +00:00
|
|
|
if SaveLoadHighLows = False then
|
2017-01-08 16:05:33 +00:00
|
|
|
begin
|
|
|
|
ShowMessage('Unable to load All-Time stats');
|
|
|
|
Exit(False);
|
|
|
|
end;
|
2017-01-08 18:49:20 +00:00
|
|
|
if FoobotIdentityObject.FoobotIdentityList.Count = 0 then Exit(FALSE);
|
2016-12-30 09:34:07 +00:00
|
|
|
sFoobotName := FoobotIdentityObject.FoobotIdentityList[TheCurrentFoobot].Name;
|
2017-01-08 18:49:20 +00:00
|
|
|
if (sFoobotName = '') then
|
|
|
|
Exit(False);
|
2016-12-30 09:34:07 +00:00
|
|
|
if not Assigned(HLINI) then
|
|
|
|
HLINI := TIniFile.Create(ChangeFileExt(GetAppConfigFile(False), '.ini'));
|
2016-12-29 13:26:23 +00:00
|
|
|
// Make sure the High-Lows are for the current Foobot
|
2016-12-30 09:34:07 +00:00
|
|
|
if (HLINI.ReadString('Foobot', 'CurrentFoobotName', 'unknown') <> sFoobotName) then
|
|
|
|
Exit(False);
|
2017-01-08 18:49:20 +00:00
|
|
|
try
|
|
|
|
with HLINI do
|
|
|
|
begin
|
|
|
|
// Particulates
|
|
|
|
FoobotDataHighs[C_PM] := ReadFloat(sFoobotName, 'pmHigh', 0);
|
|
|
|
FoobotDataHighTimes[C_PM] := ReadDateTime(sFoobotName, 'pmHighTime', Now);
|
|
|
|
FoobotDataLows[C_PM] := ReadFloat(sFoobotName, 'pmLow', 0);
|
|
|
|
FoobotDataLowTimes[C_PM] := ReadDateTime(sFoobotName, 'pmLowTime', Now);
|
|
|
|
// Temp
|
|
|
|
FoobotDataHighs[C_TMP] := ReadFloat(sFoobotName, 'tmpHigh', 0);
|
|
|
|
FoobotDataHighTimes[C_TMP] := ReadDateTime(sFoobotName, 'tmpHighTime', Now);
|
|
|
|
FoobotDataLows[C_TMP] := ReadFloat(sFoobotName, 'tmpLow', 0);
|
|
|
|
FoobotDataLowTimes[C_TMP] := ReadDateTime(sFoobotName, 'tmpLowTime', Now);
|
|
|
|
// Humidity
|
|
|
|
FoobotDataHighs[C_HUM] := ReadFloat(sFoobotName, 'humHigh', 0);
|
|
|
|
FoobotDataHighTimes[C_HUM] := ReadDateTime(sFoobotName, 'humHighTime', Now);
|
|
|
|
FoobotDataLows[C_HUM] := ReadFloat(sFoobotName, 'humLow', 0);
|
|
|
|
FoobotDataLowTimes[C_HUM] := ReadDateTime(sFoobotName, 'humLowTime', Now);
|
|
|
|
// CO2
|
|
|
|
FoobotDataHighs[C_CO2] := ReadInteger(sFoobotName, 'co2High', 0);
|
|
|
|
FoobotDataHighTimes[C_CO2] := ReadDateTime(sFoobotName, 'co2HighTime', Now);
|
|
|
|
FoobotDataLows[C_CO2] := ReadInteger(sFoobotName, 'co2Low', 0);
|
|
|
|
FoobotDataLowTimes[C_CO2] := ReadDateTime(sFoobotName, 'co2LowTime', Now);
|
|
|
|
// Volatile Compounds
|
|
|
|
FoobotDataHighs[C_VOC] := ReadInteger(sFoobotName, 'vocHigh', 0);
|
|
|
|
FoobotDataHighTimes[C_VOC] := ReadDateTime(sFoobotName, 'vocHighTime', Now);
|
|
|
|
FoobotDataLows[C_VOC] := ReadInteger(sFoobotName, 'vocLow', 0);
|
|
|
|
FoobotDataLowTimes[C_VOC] := ReadDateTime(sFoobotName, 'vocLowTime', Now);
|
|
|
|
// All Pollution
|
|
|
|
FoobotDataHighs[C_ALLPOLLU] := ReadFloat(sFoobotName, 'allpolluHigh', 0);
|
|
|
|
FoobotDataHighTimes[C_ALLPOLLU] :=
|
|
|
|
ReadDateTime(sFoobotName, 'allpolluHighTime', Now);
|
|
|
|
FoobotDataLows[C_ALLPOLLU] := ReadFloat(sFoobotName, 'allpolluLow', 0);
|
|
|
|
FoobotDataLowTimes[C_ALLPOLLU] :=
|
|
|
|
ReadDateTime(sFoobotName, 'allpolluLowTime', Now);
|
|
|
|
Result := True;
|
|
|
|
end;
|
|
|
|
except
|
|
|
|
raise Exception.Create('Could not Load HighLows');
|
|
|
|
end;
|
2016-12-29 13:26:23 +00:00
|
|
|
end;
|
|
|
|
|
2017-01-08 16:05:33 +00:00
|
|
|
// Function to make the Foobot data accessible
|
|
|
|
// Also sets the HighLow array values
|
|
|
|
// Also sets triggers
|
|
|
|
{
|
|
|
|
TAlertRec = Record
|
|
|
|
Triggered:Boolean;
|
|
|
|
AlertTime:TDateTime;
|
|
|
|
AlertType:TAlertType;
|
|
|
|
AlertValue:Variant;
|
|
|
|
end;
|
|
|
|
}
|
2016-12-29 13:26:23 +00:00
|
|
|
function FoobotDataObjectToArrays: boolean;
|
|
|
|
var
|
|
|
|
J, K: integer;
|
|
|
|
Mydatapoint: variant;
|
|
|
|
iUnixSecs: int64;
|
2016-12-30 09:34:07 +00:00
|
|
|
// ========= Internal routines start ===========
|
|
|
|
procedure SetHigh(iMember: integer; aValue: variant; aDateTime: TDateTime);
|
|
|
|
begin
|
2017-01-08 21:18:05 +00:00
|
|
|
// Do High/Low
|
2016-12-30 09:34:07 +00:00
|
|
|
if aValue > FoobotDataHighs[iMember] then
|
|
|
|
begin
|
|
|
|
FoobotDataHighs[iMember] := aValue;
|
|
|
|
FoobotDataHighTimes[iMember] := aDateTime;
|
2017-01-08 16:05:33 +00:00
|
|
|
SaveHighLows;
|
|
|
|
end;
|
2017-01-09 07:56:13 +00:00
|
|
|
if ((UseTriggers = True) and (FooBotTriggerArray[C_HIGH, iMember] <> 0)) then
|
2017-01-08 16:05:33 +00:00
|
|
|
begin
|
2017-01-08 21:18:05 +00:00
|
|
|
// Do High Trigger
|
2017-01-08 16:05:33 +00:00
|
|
|
// Sets AlertRec record
|
2017-01-09 07:56:13 +00:00
|
|
|
if (aValue > FooBotTriggerArray[C_HIGH, iMember]) then
|
|
|
|
begin
|
|
|
|
AlertRec[iMember].AlertTriggered := True;
|
|
|
|
AlertRec[iMember].AlertTime := aDateTime;
|
|
|
|
AlertRec[iMember].AlertType := C_HIGH;
|
|
|
|
AlertRec[iMember].AlertValue := aValue;
|
|
|
|
end;
|
2016-12-30 09:34:07 +00:00
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure SetLow(iMember: integer; aValue: variant; aDateTime: TDateTime);
|
|
|
|
begin
|
2017-01-08 21:18:05 +00:00
|
|
|
// Do High/Low
|
2016-12-30 09:34:07 +00:00
|
|
|
if (aValue < FoobotDataLows[iMember]) or (FoobotDataLows[iMember] = 0) then
|
|
|
|
begin
|
|
|
|
FoobotDataLows[iMember] := aValue;
|
|
|
|
FoobotDataLowTimes[iMember] := aDateTime;
|
2017-01-08 16:05:33 +00:00
|
|
|
SaveHighLows;
|
|
|
|
end;
|
2017-01-09 07:56:13 +00:00
|
|
|
if ((UseTriggers = True) and (FooBotTriggerArray[C_LOW, iMember] <> 0)) then
|
2017-01-08 16:05:33 +00:00
|
|
|
begin
|
2017-01-08 21:18:05 +00:00
|
|
|
// Do Low Trigger
|
2017-01-08 16:05:33 +00:00
|
|
|
// Sets AlertRec record
|
2017-01-09 07:56:13 +00:00
|
|
|
if (aValue < FooBotTriggerArray[C_LOW, iMember]) then
|
2017-01-08 16:05:33 +00:00
|
|
|
begin
|
|
|
|
AlertRec[iMember].AlertTriggered := True;
|
|
|
|
AlertRec[iMember].AlertTime := aDateTime;
|
2017-01-09 07:56:13 +00:00
|
|
|
AlertRec[iMember].AlertType :=C_LOW;
|
2017-01-08 16:05:33 +00:00
|
|
|
AlertRec[iMember].AlertValue := aValue;
|
2017-01-09 07:56:13 +00:00
|
|
|
end;
|
2016-12-30 09:34:07 +00:00
|
|
|
end;
|
|
|
|
end;
|
2017-01-09 07:56:13 +00:00
|
|
|
|
|
|
|
procedure ClearTriggers;
|
|
|
|
Var iCount:Integer;
|
|
|
|
begin
|
|
|
|
for iCount := C_PM to C_ALLPOLLU do
|
|
|
|
begin
|
|
|
|
AlertRec[iCount].AlertTriggered:=False;
|
|
|
|
AlertRec[iCount].AlertValue := 0;
|
|
|
|
end;
|
|
|
|
end;
|
2016-12-30 09:34:07 +00:00
|
|
|
// ========== Internal routines end =============
|
2016-12-29 13:26:23 +00:00
|
|
|
begin
|
|
|
|
ResetArrays;
|
|
|
|
Result := True;
|
|
|
|
LoadHighLows;
|
|
|
|
if FoobotIdentityObject.FoobotIdentityList.Count = 0 then
|
|
|
|
Exit(False);
|
|
|
|
if FooBotDataObject.sensors.Count = 0 then
|
|
|
|
Exit(False);
|
|
|
|
if FooBotDataObject.units.Count = 0 then
|
|
|
|
Exit(False);
|
2017-01-09 07:56:13 +00:00
|
|
|
// Reset all triggers (if on)
|
|
|
|
If UseTriggers then ClearTriggers;
|
2016-12-29 13:26:23 +00:00
|
|
|
// J=Column, K=Row
|
|
|
|
for K := VarArrayLowBound(FoobotDataObject.datapoints, 1)
|
|
|
|
to VarArrayHighBound(FoobotDataObject.datapoints, 1) do
|
|
|
|
begin
|
|
|
|
for J := VarArrayLowBound(FoobotDataObject.datapoints[K], 1)
|
|
|
|
to VarArrayHighBound(FoobotDataObject.datapoints[K], 1) do
|
|
|
|
begin
|
|
|
|
Mydatapoint := FoobotDataObject.datapoints[K][J];
|
|
|
|
case J of
|
2017-01-08 16:05:33 +00:00
|
|
|
C_TIME: // First field is a DateTime
|
2016-12-29 13:26:23 +00:00
|
|
|
begin
|
|
|
|
iUnixSecs := int64(Mydatapoint);
|
|
|
|
SetLength(FoobotData_time, K + 1);
|
|
|
|
FoobotData_time[K] := UnixToDateTime(iUnixSecs);
|
|
|
|
end;
|
2017-01-08 16:05:33 +00:00
|
|
|
C_PM: // Particulate matter
|
2016-12-29 13:26:23 +00:00
|
|
|
begin
|
|
|
|
SetLength(FoobotData_pm, K + 1);
|
|
|
|
FoobotData_pm[K] := double(MyDataPoint);
|
2016-12-30 09:34:07 +00:00
|
|
|
SetHigh(J, FoobotData_pm[K], FoobotData_time[K]);
|
|
|
|
SetLow(J, FoobotData_pm[K], FoobotData_time[K]);
|
2016-12-29 13:26:23 +00:00
|
|
|
end;
|
2017-01-08 16:05:33 +00:00
|
|
|
C_TMP: // Temperature
|
2016-12-29 13:26:23 +00:00
|
|
|
begin
|
|
|
|
SetLength(FoobotData_tmp, K + 1);
|
|
|
|
FoobotData_tmp[K] := double(MyDataPoint);
|
2016-12-30 09:34:07 +00:00
|
|
|
SetHigh(J, FoobotData_tmp[K], FoobotData_time[K]);
|
|
|
|
SetLow(J, FoobotData_tmp[K], FoobotData_time[K]);
|
2016-12-29 13:26:23 +00:00
|
|
|
end;
|
2017-01-08 16:05:33 +00:00
|
|
|
C_HUM: // Humidity
|
2016-12-29 13:26:23 +00:00
|
|
|
begin
|
|
|
|
SetLength(FoobotData_hum, K + 1);
|
|
|
|
FoobotData_hum[K] := double(MyDataPoint);
|
2016-12-30 09:34:07 +00:00
|
|
|
SetHigh(J, FoobotData_hum[K], FoobotData_time[K]);
|
|
|
|
SetLow(J, FoobotData_hum[K], FoobotData_time[K]);
|
2016-12-29 13:26:23 +00:00
|
|
|
end;
|
2017-01-08 16:05:33 +00:00
|
|
|
C_CO2: // CO2
|
2016-12-29 13:26:23 +00:00
|
|
|
begin
|
|
|
|
SetLength(FoobotData_co2, K + 1);
|
|
|
|
FoobotData_co2[K] := integer(MyDataPoint);
|
2016-12-30 09:34:07 +00:00
|
|
|
SetHigh(J, FoobotData_co2[K], FoobotData_time[K]);
|
|
|
|
SetLow(J, FoobotData_co2[K], FoobotData_time[K]);
|
2016-12-29 13:26:23 +00:00
|
|
|
end;
|
2017-01-08 16:05:33 +00:00
|
|
|
C_VOC: // Volatile compounds
|
2016-12-29 13:26:23 +00:00
|
|
|
begin
|
|
|
|
SetLength(FoobotData_voc, K + 1);
|
|
|
|
FoobotData_voc[K] := integer(MyDataPoint);
|
2016-12-30 09:34:07 +00:00
|
|
|
SetHigh(J, FoobotData_voc[K], FoobotData_time[K]);
|
|
|
|
SetLow(J, FoobotData_voc[K], FoobotData_time[K]);
|
2016-12-29 13:26:23 +00:00
|
|
|
end;
|
2017-01-08 16:05:33 +00:00
|
|
|
C_ALLPOLLU: // All Pollution
|
2016-12-29 13:26:23 +00:00
|
|
|
begin
|
|
|
|
SetLength(FoobotData_allpollu, K + 1);
|
|
|
|
FoobotData_allpollu[K] := double(MyDataPoint);
|
2016-12-30 09:34:07 +00:00
|
|
|
SetHigh(J, FoobotData_allpollu[K], FoobotData_time[K]);
|
|
|
|
SetLow(J, FoobotData_allpollu[K], FoobotData_time[K]);
|
2016-12-29 13:26:23 +00:00
|
|
|
end;
|
2017-01-08 18:49:20 +00:00
|
|
|
else
|
|
|
|
raise Exception.Create('Error in FoobotDataObjectToArrays Case');
|
2016-12-29 13:26:23 +00:00
|
|
|
end; // of Case
|
|
|
|
end;
|
|
|
|
end;
|
2017-01-08 16:05:33 +00:00
|
|
|
|
|
|
|
end;
|
|
|
|
|
2017-01-08 21:18:05 +00:00
|
|
|
function GetHighTrigger(Const aSensor:Integer):Variant;
|
|
|
|
begin
|
|
|
|
Result:=0;
|
|
|
|
if UseTriggers = False then Exit;
|
|
|
|
If ((aSensor < C_PM) or (aSensor > C_ALLPOLLU)) then exit;
|
2017-01-09 07:56:13 +00:00
|
|
|
Result:=FooBotTriggerArray[C_HIGH, aSensor];
|
2017-01-08 21:18:05 +00:00
|
|
|
end;
|
|
|
|
|
|
|
|
function GetLowTrigger(Const aSensor:Integer):Variant;
|
|
|
|
begin
|
|
|
|
Result:=0;
|
|
|
|
if UseTriggers = False then Exit;
|
|
|
|
If ((aSensor < C_PM) or (aSensor > C_ALLPOLLU)) then exit;
|
2017-01-09 07:56:13 +00:00
|
|
|
Result:=FooBotTriggerArray[C_LOW, aSensor];
|
2017-01-08 21:18:05 +00:00
|
|
|
end;
|
|
|
|
|
2017-01-08 18:49:20 +00:00
|
|
|
function SetHighTrigger(const aSensor: integer; const aValue: variant): boolean;
|
2017-01-08 16:05:33 +00:00
|
|
|
begin
|
|
|
|
Result := False;
|
|
|
|
if UseTriggers = False then
|
|
|
|
Exit;
|
2017-01-09 07:56:13 +00:00
|
|
|
if aValue <> FooBotTriggerArray[C_HIGH, aSensor] then
|
2017-01-08 16:05:33 +00:00
|
|
|
begin
|
2017-01-09 07:56:13 +00:00
|
|
|
FooBotTriggerArray[C_HIGH, aSensor] := aValue;
|
2017-01-08 16:05:33 +00:00
|
|
|
Result := True;
|
|
|
|
end;
|
2016-12-29 13:26:23 +00:00
|
|
|
end;
|
2016-12-30 09:34:07 +00:00
|
|
|
|
2017-01-08 18:49:20 +00:00
|
|
|
function SetLowTrigger(const aSensor: integer; const aValue: variant): boolean;
|
2017-01-08 16:05:33 +00:00
|
|
|
begin
|
|
|
|
Result := False;
|
|
|
|
if UseTriggers = False then
|
|
|
|
Exit;
|
2017-01-09 07:56:13 +00:00
|
|
|
if aValue <> FooBotTriggerArray[C_LOW, Ord(aSensor)] then
|
2017-01-08 16:05:33 +00:00
|
|
|
begin
|
2017-01-09 07:56:13 +00:00
|
|
|
FooBotTriggerArray[C_LOW, aSensor] := aValue;
|
2017-01-08 16:05:33 +00:00
|
|
|
Result := True;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
// Data cleaning functions
|
2016-12-30 09:34:07 +00:00
|
|
|
function ResetHighLows: boolean;
|
|
|
|
var
|
|
|
|
iCount: integer;
|
2016-12-29 13:26:23 +00:00
|
|
|
begin
|
2017-01-08 18:49:20 +00:00
|
|
|
for iCount := C_TIME to C_ALLPOLLU do
|
2016-12-30 09:34:07 +00:00
|
|
|
begin
|
|
|
|
FoobotDataHighs[iCount] := 0;
|
|
|
|
FoobotDataLows[iCount] := 0;
|
2016-12-29 13:26:23 +00:00
|
|
|
end;
|
2016-12-30 09:34:07 +00:00
|
|
|
Result := True;
|
2016-12-29 13:26:23 +00:00
|
|
|
end;
|
|
|
|
|
|
|
|
function ResetArrays: boolean;
|
|
|
|
begin
|
|
|
|
Result := True;
|
|
|
|
try
|
|
|
|
SetLength(FoobotData_time, 0);
|
|
|
|
SetLength(FoobotData_pm, 0);
|
|
|
|
SetLength(FoobotData_tmp, 0);
|
|
|
|
SetLength(FoobotData_hum, 0);
|
|
|
|
SetLength(FoobotData_co2, 0);
|
|
|
|
SetLength(FoobotData_voc, 0);
|
|
|
|
SetLength(FoobotData_allpollu, 0);
|
|
|
|
except
|
|
|
|
Result := False;
|
|
|
|
raise;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
function ResetObjects: boolean;
|
|
|
|
var
|
|
|
|
J, K: integer;
|
|
|
|
begin
|
|
|
|
Result := True;
|
|
|
|
try
|
|
|
|
for K := VarArrayLowBound(FoobotDataObject.datapoints, 1)
|
|
|
|
to VarArrayHighBound(FoobotDataObject.datapoints, 1) do
|
|
|
|
for J := VarArrayLowBound(FoobotDataObject.datapoints[K], 1)
|
|
|
|
to VarArrayHighBound(FoobotDataObject.datapoints[K], 1) do
|
|
|
|
FoobotDataObject.datapoints[K][J] := 0;
|
|
|
|
FooBotDataObject.sensors.Clear;
|
|
|
|
FooBotDataObject.units.Clear;
|
|
|
|
FoobotIdentityObject.FoobotIdentityList.Clear;
|
|
|
|
except
|
|
|
|
Result := False;
|
|
|
|
raise;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
2017-01-08 16:05:33 +00:00
|
|
|
// Authentication functions
|
2016-12-29 13:26:23 +00:00
|
|
|
function EncodeStringBase64(const s: string): string;
|
|
|
|
|
|
|
|
var
|
|
|
|
outstream: TStringStream;
|
|
|
|
encoder: TBase64EncodingStream;
|
|
|
|
begin
|
|
|
|
outstream := TStringStream.Create('');
|
|
|
|
try
|
|
|
|
encoder := TBase64EncodingStream.Create(outstream);
|
|
|
|
try
|
|
|
|
encoder.Write(s[1], length(s));
|
|
|
|
finally
|
|
|
|
encoder.Free;
|
|
|
|
end;
|
|
|
|
outstream.position := 0;
|
|
|
|
Result := outstream.readstring(outstream.size);
|
|
|
|
finally
|
|
|
|
outstream.Free;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
function FetchAuthenticationKey(aUsername, aUserPassword: string): boolean;
|
|
|
|
var
|
|
|
|
sRequestURL: string;
|
|
|
|
iCount: integer;
|
2016-12-30 09:34:07 +00:00
|
|
|
JSON: TJSONStringType;
|
2016-12-29 13:26:23 +00:00
|
|
|
begin
|
|
|
|
// FOOBOT_USER_URL = 'http://api.foobot.io/v2/user/%s/login/';
|
|
|
|
// sAuthenticationKey
|
|
|
|
// Looking for "x-auth-token"
|
|
|
|
Result := False;
|
|
|
|
try
|
|
|
|
with httpclient do
|
|
|
|
begin
|
|
|
|
ResponseHeaders.NameValueSeparator := ':';
|
2016-12-30 09:34:07 +00:00
|
|
|
AddHeader('password', aUserPassword);
|
|
|
|
// ShowMessage(EncodeURLElement(aUsername));
|
|
|
|
sRequestURL := Format(FOOBOT_USER_URL, [EncodeURLElement(aUsername)]);
|
|
|
|
try
|
|
|
|
JSON := Get(sRequestURL);
|
|
|
|
if ResponseStatusCode <> 200 then
|
|
|
|
begin
|
|
|
|
ShowMessageFmt('Failed - Foobot server refused with code %d',
|
|
|
|
[ResponseStatusCode]);
|
|
|
|
Exit(False);
|
|
|
|
end;
|
|
|
|
finally
|
|
|
|
ShowMessage(JSON);
|
|
|
|
for iCount := 0 to Pred(ResponseHeaders.Count) do
|
|
|
|
ShowMessage(ResponseHeaders[iCount]);
|
2016-12-29 13:26:23 +00:00
|
|
|
end;
|
|
|
|
Result := True;
|
|
|
|
end;
|
|
|
|
finally
|
2017-01-08 16:05:33 +00:00
|
|
|
// Empty
|
2016-12-29 13:26:23 +00:00
|
|
|
end;
|
|
|
|
|
|
|
|
end;
|
|
|
|
|
2017-01-08 16:05:33 +00:00
|
|
|
// This function must succeed before calling FetchFoobotData
|
2016-12-29 13:26:23 +00:00
|
|
|
function FetchFoobotIdentity(aUsername, aSecretKey: string): boolean;
|
|
|
|
var
|
|
|
|
sUserNameURL: string;
|
|
|
|
JSON: TJSONStringType;
|
|
|
|
DeStreamer: TJSONDeStreamer;
|
|
|
|
begin
|
|
|
|
Result := True; // Assume success: Look for failure
|
2016-12-30 09:34:07 +00:00
|
|
|
sAuthenticationKey := aSecretKey;
|
2016-12-29 13:26:23 +00:00
|
|
|
try
|
|
|
|
with httpclient do
|
|
|
|
begin
|
|
|
|
DeStreamer := TJSONDeStreamer.Create(nil);
|
|
|
|
DeStreamer.Options := [jdoIgnorePropertyErrors];
|
|
|
|
sUserNameURL := Format(FOOBOT_IDENTITY_URL, [aUsername]);
|
|
|
|
ResponseHeaders.NameValueSeparator := ':';
|
|
|
|
AddHeader('Accept', 'application/json;charset=UTF-8');
|
|
|
|
AddHeader('X-API-KEY-TOKEN', aSecretKey);
|
|
|
|
JSON := Get(sUserNameURL);
|
|
|
|
if (ResponseStatusCode <> 200) then
|
|
|
|
case ResponseStatusCode of
|
|
|
|
429:
|
|
|
|
begin
|
|
|
|
ShowMessageFmt('Cannot retieve data - too many requests to the Foobot server%s%s',
|
|
|
|
[LineEnding, JSON]);
|
|
|
|
Exit(False);
|
|
|
|
end;
|
|
|
|
else
|
|
|
|
begin
|
|
|
|
ShowMessageFmt('Cannot retieve data - Foobot server refused with code %d',
|
|
|
|
[ResponseStatusCode]);
|
|
|
|
Exit(False);
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
try
|
|
|
|
// Stream it to the object list
|
|
|
|
DeStreamer.JSONToObject(JSON, FoobotIdentityObject.FoobotIdentityList);
|
|
|
|
except
|
|
|
|
On E: Exception do
|
2016-12-30 09:34:07 +00:00
|
|
|
showmessagefmt('Cannot retieve data - Foobot server refused with code %s',
|
|
|
|
[E.Message]);
|
2016-12-29 13:26:23 +00:00
|
|
|
On E: Exception do
|
|
|
|
Result := False;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
finally
|
|
|
|
DeStreamer.Free;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
2017-01-08 16:05:33 +00:00
|
|
|
// Function FetchFoobotIdentity must be called before this one
|
2016-12-29 13:26:23 +00:00
|
|
|
function FetchFoobotData(DataFetchType: TDataFetchType;
|
|
|
|
iCurrentFoobot, iLastIntervalSeconds, iLastAverageBySeconds: integer;
|
|
|
|
iStartTimeSeconds, iEndTimeSeconds: int64; aSecretKey: string): boolean;
|
|
|
|
var
|
|
|
|
sUserNameURL: string;
|
|
|
|
JSON: TJSONStringType;
|
|
|
|
DeStreamer: TJSONDeStreamer;
|
|
|
|
uuid: string;
|
|
|
|
//FOOBOT_DATA_LAST_URL = 'http://api.foobot.io/v2/device/%s/datapoint/%s/%s/%s/';
|
|
|
|
//FOOBOT_DATA_START_FINISH_URL = 'http://api.foobot.io/v2/device/%s/datapoint/%s/%s/%s/';
|
|
|
|
begin
|
|
|
|
Result := True; // Assume success: Look for failure
|
2016-12-30 09:34:07 +00:00
|
|
|
TheCurrentFoobot := iCurrentFoobot;
|
2016-12-29 13:26:23 +00:00
|
|
|
// Checks for integrity
|
|
|
|
if (FoobotIdentityObject.FoobotIdentityList.Count = 0) then
|
|
|
|
Exit(False);
|
|
|
|
if (DataFetchType = dfStartEnd) and ((iStartTimeSeconds = 0) or
|
|
|
|
(iEndTimeSeconds = 0)) then
|
|
|
|
Exit(False);
|
|
|
|
if (aSecretKey = 'unknown') then
|
|
|
|
Exit(False);
|
|
|
|
|
|
|
|
try
|
|
|
|
with httpclient do
|
|
|
|
begin
|
|
|
|
DeStreamer := TJSONDeStreamer.Create(nil);
|
|
|
|
DeStreamer.Options := [jdoIgnorePropertyErrors];
|
|
|
|
// secretkey := INI.ReadString('Foobot', 'Secret Key', '');
|
|
|
|
uuid := FoobotIdentityObject.FoobotIdentityList.Items[iCurrentFoobot].uuid;
|
|
|
|
case DataFetchType of
|
|
|
|
dfLast:
|
|
|
|
sUserNameURL := Format(FOOBOT_DATA_LAST_URL,
|
|
|
|
[uuid, IntToStr(iLastIntervalSeconds), 'last',
|
|
|
|
IntToStr(iLastAverageBySeconds)]);
|
|
|
|
dfStartEnd:
|
|
|
|
sUserNameURL := Format(FOOBOT_DATA_START_FINISH_URL,
|
|
|
|
[uuid, IntToStr(iStartTimeSeconds), IntToStr(iEndTimeSeconds),
|
|
|
|
IntToStr(iLastAverageBySeconds)]);
|
|
|
|
else
|
|
|
|
begin
|
|
|
|
Result := False;
|
|
|
|
Exit;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
ResponseHeaders.NameValueSeparator := ':';
|
|
|
|
|
|
|
|
AddHeader('Accept', 'application/json;charset=UTF-8');
|
|
|
|
AddHeader('X-API-KEY-TOKEN', aSecretKey);
|
|
|
|
JSON := Get(sUserNameURL);
|
|
|
|
if (ResponseStatusCode <> 200) then
|
|
|
|
case ResponseStatusCode of
|
|
|
|
429:
|
|
|
|
begin
|
|
|
|
ShowMessageFmt('Failed - Too many requests to the Foobot server%s%s',
|
|
|
|
[LineEnding, JSON]);
|
|
|
|
Exit(False);
|
|
|
|
end;
|
|
|
|
else
|
|
|
|
begin
|
|
|
|
ShowMessageFmt('Failed - Foobot server refused with code %d',
|
|
|
|
[ResponseStatusCode]);
|
|
|
|
Exit(False);
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
try
|
|
|
|
// Stream it to the object list
|
|
|
|
DeStreamer.JSONToObject(JSON, FoobotDataObject);
|
|
|
|
except
|
|
|
|
On E: Exception do
|
|
|
|
showmessagefmt('Failed - Foobot server refused with code %s', [E.Message]);
|
|
|
|
On E: Exception do
|
|
|
|
Result := False;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
finally
|
|
|
|
DeStreamer.Free;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
initialization
|
|
|
|
begin
|
|
|
|
HttpClient := TFPHTTPClient.Create(nil);
|
|
|
|
FoobotIdentityObject := TFoobotIdentityObject.Create;
|
|
|
|
FoobotDataObject := TFoobotDataObject.Create;
|
2016-12-30 09:34:07 +00:00
|
|
|
TheCurrentFoobot := 0;
|
2017-01-08 16:05:33 +00:00
|
|
|
SetLength(FooBotTriggerArray, 2, Succ(C_ALLPOLLU));
|
|
|
|
SaveLoadHighLows := True; // Default
|
2017-01-08 21:18:05 +00:00
|
|
|
UseTriggers := False; // Default
|
2017-01-08 16:05:33 +00:00
|
|
|
for giCount := C_PM to C_ALLPOLLU do
|
|
|
|
begin
|
|
|
|
AlertRec[giCount].AlertTriggered := False;
|
|
|
|
AlertRec[giCount].AlertTime := Now;
|
2017-01-08 21:18:05 +00:00
|
|
|
AlertRec[giCount].AlertType := 0;
|
2017-01-08 16:05:33 +00:00
|
|
|
AlertRec[giCount].AlertValue := 0;
|
|
|
|
end;
|
2016-12-29 13:26:23 +00:00
|
|
|
end;
|
|
|
|
|
|
|
|
finalization
|
|
|
|
begin
|
2016-12-30 09:34:07 +00:00
|
|
|
if Assigned(HLINI) then
|
|
|
|
FreeAndNil(HLINI);
|
2016-12-29 13:26:23 +00:00
|
|
|
FreeAndNil(HttpClient);
|
|
|
|
FreeAndNil(FoobotIdentityObject);
|
|
|
|
FreeAndNil(FoobotDataObject);
|
|
|
|
SetLength(FoobotData_time, 0);
|
|
|
|
SetLength(FoobotData_pm, 0);
|
|
|
|
SetLength(FoobotData_tmp, 0);
|
|
|
|
SetLength(FoobotData_hum, 0);
|
|
|
|
SetLength(FoobotData_co2, 0);
|
|
|
|
SetLength(FoobotData_voc, 0);
|
|
|
|
SetLength(FoobotData_allpollu, 0);
|
2017-01-08 16:05:33 +00:00
|
|
|
SetLength(FooBotTriggerArray, 0, 0);
|
2016-12-29 13:26:23 +00:00
|
|
|
end;
|
|
|
|
|
|
|
|
end.
|