You've already forked lazarus-ccr
Update with readme.txt
git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@5570 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
1969
applications/foobot/foobot_httpclient.pas
Normal file
1969
applications/foobot/foobot_httpclient.pas
Normal file
File diff suppressed because it is too large
Load Diff
197
applications/foobot/foobot_objects.pas
Normal file
197
applications/foobot/foobot_objects.pas
Normal file
@ -0,0 +1,197 @@
|
||||
unit foobot_objects;
|
||||
{ Objects for Foobot Interrogator
|
||||
|
||||
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.
|
||||
}
|
||||
|
||||
{$mode objfpc}{$H+}
|
||||
|
||||
interface
|
||||
|
||||
uses
|
||||
Classes, SysUtils, ugenericcollection, fpjsonrtti;
|
||||
|
||||
{TFoobotIdentities}
|
||||
type
|
||||
TFoobotIdentities = class(TCollectionItem)
|
||||
// JSON fields here as properties
|
||||
private
|
||||
Fuuid: string;
|
||||
FuserId: integer;
|
||||
FMac: string;
|
||||
FName: string;
|
||||
public
|
||||
published
|
||||
property uuid: string read Fuuid write Fuuid;
|
||||
property userId: integer read FuserId write FuserId;
|
||||
property mac: string read FMac write FMac;
|
||||
property name: string read FName write FName;
|
||||
end;
|
||||
|
||||
{TFoobotIdentityList}
|
||||
TFoobotIdentityList = specialize TGenericCollection<TFoobotIdentities>;
|
||||
|
||||
{TFoobotIdentityObject}
|
||||
// Contains a list of TFoobotIdentities as a TCollection
|
||||
type
|
||||
TFoobotIdentityObject = class(TPersistent)
|
||||
private
|
||||
FFoobotIdentityList: TFoobotIdentityList;
|
||||
public
|
||||
constructor Create;
|
||||
destructor Destroy; override;
|
||||
function SaveToFile(const AFilename: string): boolean;
|
||||
function LoadFromFile(const AFileName: string): boolean;
|
||||
published
|
||||
property FoobotIdentityList: TFoobotIdentityList
|
||||
read FFoobotIdentityList write FFoobotIdentityList;
|
||||
end;
|
||||
|
||||
|
||||
type
|
||||
TFoobotDataObject = class(TPersistent)
|
||||
private
|
||||
FDataPoints:Variant;
|
||||
FSensors:TStrings;
|
||||
FUnits:TStrings;
|
||||
Fuuid:String;
|
||||
FStart:Int64;
|
||||
FEnd:Int64;
|
||||
public
|
||||
constructor Create;
|
||||
Destructor Destroy; override;
|
||||
function SaveToFile(const AFilename: string): boolean;
|
||||
published
|
||||
property uuid:String read Fuuid write Fuuid;
|
||||
property start:Int64 read FStart write FStart;
|
||||
property &end:Int64 read FEnd write FEnd;
|
||||
property sensors:TStrings
|
||||
read FSensors write FSensors;
|
||||
property units:TStrings
|
||||
read FUnits write FUnits;
|
||||
property datapoints : Variant read FDataPoints write FDataPoints;
|
||||
end;
|
||||
|
||||
|
||||
implementation
|
||||
|
||||
constructor TFoobotDataObject.Create;
|
||||
begin
|
||||
inherited;
|
||||
FSensors:=TStringList.Create;
|
||||
FUnits:=TstringList.Create;
|
||||
end;
|
||||
|
||||
Destructor TFoobotDataObject.Destroy;
|
||||
begin
|
||||
FSensors.Free;
|
||||
FUnits.Free;
|
||||
inherited Destroy;
|
||||
end;
|
||||
|
||||
{TFoobotIdentityObject}
|
||||
constructor TFoobotIdentityObject.Create;
|
||||
begin
|
||||
inherited;
|
||||
FFoobotIdentityList := TFoobotIdentityList.Create;
|
||||
end;
|
||||
|
||||
destructor TFoobotIdentityObject.Destroy;
|
||||
var
|
||||
c: TCollectionItem;
|
||||
begin
|
||||
for c in FFoobotIdentityList do
|
||||
c.Free;
|
||||
FFoobotIdentityList.Free;
|
||||
inherited Destroy;
|
||||
end;
|
||||
|
||||
function TFoobotIdentityObject.LoadFromFile(const AFileName: string): boolean;
|
||||
var
|
||||
DeStreamer: TJSONDeStreamer;
|
||||
s: TStringList;
|
||||
begin
|
||||
Result := True;
|
||||
s := TStringList.Create;
|
||||
try
|
||||
s.LoadFromFile(AFileName);
|
||||
DeStreamer := TJSONDeStreamer.Create(nil);
|
||||
try
|
||||
DeStreamer.JSONToObject(s.Text, Self);
|
||||
except
|
||||
// Eat the exception
|
||||
On E: Exception do
|
||||
Result := False;
|
||||
end;
|
||||
finally
|
||||
DeStreamer.Free;
|
||||
s.Free;
|
||||
end;
|
||||
|
||||
end;
|
||||
|
||||
function TFoobotIdentityObject.SaveToFile(const AFilename: string): boolean;
|
||||
var
|
||||
Streamer: TJSONStreamer;
|
||||
s: TStringList;
|
||||
begin
|
||||
Result := True;
|
||||
s := TStringList.Create;
|
||||
try
|
||||
Streamer := TJSONStreamer.Create(nil);
|
||||
Streamer.Options := Streamer.Options + [jsoUseFormatString];
|
||||
s.AddText(Streamer.ObjectToJSONString(Self));
|
||||
try
|
||||
s.SaveToFile(AFileName);
|
||||
except
|
||||
// Eat the exception
|
||||
On E: Exception do
|
||||
Result := False;
|
||||
end;
|
||||
finally
|
||||
Streamer.Free;
|
||||
s.Free;
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
function TFoobotDataObject.SaveToFile(const AFilename: string): boolean;
|
||||
var
|
||||
Streamer: TJSONStreamer;
|
||||
s: TStringList;
|
||||
begin
|
||||
Result := True;
|
||||
s := TStringList.Create;
|
||||
try
|
||||
Streamer := TJSONStreamer.Create(nil);
|
||||
Streamer.Options := Streamer.Options + [jsoUseFormatString];
|
||||
s.AddText(Streamer.ObjectToJSONString(Self));
|
||||
try
|
||||
s.SaveToFile(AFileName);
|
||||
except
|
||||
// Eat the exception
|
||||
On E: Exception do
|
||||
Result := False;
|
||||
end;
|
||||
finally
|
||||
Streamer.Free;
|
||||
s.Free;
|
||||
end;
|
||||
end;
|
||||
|
||||
end.
|
533
applications/foobot/foobot_utility.pas
Normal file
533
applications/foobot/foobot_utility.pas
Normal file
@ -0,0 +1,533 @@
|
||||
unit foobot_utility;
|
||||
|
||||
{ Foobot Interrogator Utilities
|
||||
|
||||
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,
|
||||
DateUtils,INIFiles;
|
||||
|
||||
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/';
|
||||
HIGHLOWMAX = 6;
|
||||
|
||||
type
|
||||
TDataFetchType = (dfLast, dfStartEnd);
|
||||
TSensorType = (st_time,st_pm,st_tmp,st_hum,st_co2,st_voc,st_allpollu);
|
||||
|
||||
function EncodeStringBase64(const s: string): string;
|
||||
function FetchAuthenticationKey(aUsername, aUserPassword: string): boolean;
|
||||
|
||||
// Populates FoobotIdentityObject.TFoobotIdentityList collection
|
||||
function FetchFoobotIdentity(aUsername, aSecretKey: string): boolean;
|
||||
|
||||
// Populates FoobotIdentityObject
|
||||
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;
|
||||
|
||||
// Utility functions
|
||||
function ResetArrays: boolean;
|
||||
function ResetObjects: boolean;
|
||||
Function ResetHighLows:Boolean;
|
||||
function SaveHighLows:Boolean;
|
||||
Function LoadHighLows:Boolean;
|
||||
|
||||
var
|
||||
HttpClient: TFPHTTPClient;
|
||||
FoobotIdentityObject: TFoobotIdentityObject;
|
||||
FoobotDataObject: TFoobotDataObject;
|
||||
sAuthenticationKey: string;
|
||||
SensorType:TSensorType;
|
||||
SaveLoadHighLows:Boolean;
|
||||
TheCurrentFoobot:Integer;
|
||||
HLINI:TIniFile;
|
||||
// Easier access to datapoints
|
||||
// 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
|
||||
FoobotDataHighs:Array[0..HIGHLOWMAX]of Variant;
|
||||
FoobotDataLows:Array[0..HIGHLOWMAX]of Variant;
|
||||
FoobotDataHighTimes:Array[0..HIGHLOWMAX]of Variant;
|
||||
FoobotDataLowTimes:Array[0..HIGHLOWMAX]of Variant;
|
||||
|
||||
implementation
|
||||
function SaveHighLows:Boolean;
|
||||
Var sFoobotName:String;
|
||||
begin
|
||||
If SaveLoadHighLows=FALSE then Exit(FALSE);
|
||||
sFoobotName:=FoobotIdentityObject.FoobotIdentityList[TheCurrentFoobot].name;
|
||||
If Not Assigned(HLINI) then
|
||||
HLINI:=TIniFile.Create(ChangeFileExt(GetAppConfigFile(False),'.ini'));
|
||||
// Store current Foobot info
|
||||
HLINI.WriteInteger('Foobot','CurrentFoobot',TheCurrentFoobot);
|
||||
HLINI.WriteString('Foobot','CurrentFoobotName',sFoobotName);
|
||||
|
||||
// Particulates
|
||||
HLINI.WriteFloat(sFoobotName,'pmHigh',Double(FoobotDataHighs[1]));
|
||||
HLINI.WriteDateTime(sFoobotName,'pmHighTime',TDateTime(FoobotDataHighTimes[1]));
|
||||
HLINI.WriteFloat(sFoobotName,'pmLow',Double(FoobotDataLows[1]));
|
||||
HLINI.WriteDateTime(sFoobotName,'pmLowTime',TDateTime(FoobotDataLowTimes[1]));
|
||||
// Temp
|
||||
HLINI.WriteFloat(sFoobotName,'tmpHigh',Double(FoobotDataHighs[2]));
|
||||
HLINI.WriteDateTime(sFoobotName,'tmpHighTime',TDateTime(FoobotDataHighTimes[2]));
|
||||
HLINI.WriteFloat(sFoobotName,'tmpLow',Double(FoobotDataLows[2]));
|
||||
HLINI.WriteDateTime(sFoobotName,'tmpLowTime',TDateTime(FoobotDataLowTimes[2]));
|
||||
// Humidity
|
||||
HLINI.WriteFloat(sFoobotName,'humHigh',Double(FoobotDataHighs[3]));
|
||||
HLINI.WriteDateTime(sFoobotName,'humHighTime',TDateTime(FoobotDataHighTimes[3]));
|
||||
HLINI.WriteFloat(sFoobotName,'humLow',Double(FoobotDataLows[3]));
|
||||
HLINI.WriteDateTime(sFoobotName,'humLowTime',TDateTime(FoobotDataLowTimes[3]));
|
||||
// CO2
|
||||
HLINI.WriteInteger(sFoobotName,'co2High',Integer(FoobotDataHighs[4]));
|
||||
HLINI.WriteDateTime(sFoobotName,'co2HighTime',TDateTime(FoobotDataHighTimes[4]));
|
||||
HLINI.WriteInteger(sFoobotName,'co2Low',Integer(FoobotDataLows[4]));
|
||||
HLINI.WriteDateTime(sFoobotName,'co2LowTime',TDateTime(FoobotDataLowTimes[4]));
|
||||
// Volatile Compounds
|
||||
HLINI.WriteInteger(sFoobotName,'vocHigh',Integer(FoobotDataHighs[5]));
|
||||
HLINI.WriteDateTime(sFoobotName,'vocHighTime',TDateTime(FoobotDataHighTimes[5]));
|
||||
HLINI.WriteInteger(sFoobotName,'vocLow',Integer(FoobotDataLows[5]));
|
||||
HLINI.WriteDateTime(sFoobotName,'vocLowTime',TDateTime(FoobotDataLowTimes[5]));
|
||||
// All Pollution
|
||||
HLINI.WriteFloat(sFoobotName,'allpolluHigh',Double(FoobotDataHighs[6]));
|
||||
HLINI.WriteDateTime(sFoobotName,'allpolluHighTime',TDateTime(FoobotDataHighTimes[6]));
|
||||
HLINI.WriteFloat(sFoobotName,'allpolluLow',Double(FoobotDataLows[6]));
|
||||
HLINI.WriteDateTime(sFoobotName,'allpolluLowTime',TDateTime(FoobotDataLowTimes[6]));
|
||||
end;
|
||||
|
||||
Function LoadHighLows:Boolean;
|
||||
Var sFoobotName:String;
|
||||
begin
|
||||
If SaveLoadHighLows=FALSE then Exit(FALSE);
|
||||
sFoobotName:=FoobotIdentityObject.FoobotIdentityList[TheCurrentFoobot].name;
|
||||
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);
|
||||
|
||||
// Particulates
|
||||
FoobotDataHighs[1]:=HLINI.ReadFloat(sFoobotName,'pmHigh',0);
|
||||
FoobotDataHighTimes[1]:=HLINI.ReadDateTime(sFoobotName,'pmHighTime',Now);
|
||||
FoobotDataLows[1]:=HLINI.ReadFloat(sFoobotName,'pmLow',0);
|
||||
FoobotDataLowTimes[1]:=HLINI.ReadDateTime(sFoobotName,'pmLowTime',Now);
|
||||
// Temp
|
||||
FoobotDataHighs[2]:=HLINI.ReadFloat(sFoobotName,'tmpHigh',0);
|
||||
FoobotDataHighTimes[2]:=HLINI.ReadDateTime(sFoobotName,'tmpHighTime',Now);
|
||||
FoobotDataLows[2]:=HLINI.ReadFloat(sFoobotName,'tmpLow',0);
|
||||
FoobotDataLowTimes[2]:=HLINI.ReadDateTime(sFoobotName,'tmpLowTime',Now);
|
||||
// Humidity
|
||||
FoobotDataHighs[3]:=HLINI.ReadFloat(sFoobotName,'humHigh',0);
|
||||
FoobotDataHighTimes[3]:=HLINI.ReadDateTime(sFoobotName,'humHighTime',Now);
|
||||
FoobotDataLows[3]:=HLINI.ReadFloat(sFoobotName,'humLow',0);
|
||||
FoobotDataLowTimes[3]:=HLINI.ReadDateTime(sFoobotName,'humLowTime',Now);
|
||||
// CO2
|
||||
FoobotDataHighs[4]:=HLINI.ReadInteger(sFoobotName,'co2High',0);
|
||||
FoobotDataHighTimes[4]:=HLINI.ReadDateTime(sFoobotName,'co2HighTime',Now);
|
||||
FoobotDataLows[4]:=HLINI.ReadInteger(sFoobotName,'co2Low',0);
|
||||
FoobotDataLowTimes[4]:=HLINI.ReadDateTime(sFoobotName,'co2LowTime',Now);
|
||||
// Volatile Compounds
|
||||
FoobotDataHighs[5]:=HLINI.ReadInteger(sFoobotName,'vocHigh',0);
|
||||
FoobotDataHighTimes[5]:=HLINI.ReadDateTime(sFoobotName,'vocHighTime',Now);
|
||||
FoobotDataLows[5]:=HLINI.ReadInteger(sFoobotName,'vocLow',0);
|
||||
FoobotDataLowTimes[5]:=HLINI.ReadDateTime(sFoobotName,'vocLowTime',Now);
|
||||
// All Pollution
|
||||
FoobotDataHighs[6]:=HLINI.ReadFloat(sFoobotName,'allpolluHigh',0);
|
||||
FoobotDataHighTimes[6]:=HLINI.ReadDateTime(sFoobotName,'allpolluHighTime',Now);
|
||||
FoobotDataLows[6]:=HLINI.ReadFloat(sFoobotName,'allpolluLow',0);
|
||||
FoobotDataLowTimes[6]:=HLINI.ReadDateTime(sFoobotName,'allpolluLowTime',Now);
|
||||
end;
|
||||
|
||||
// ToDo: Multiple Foobots?
|
||||
function FoobotDataObjectToArrays: boolean;
|
||||
var
|
||||
J, K: integer;
|
||||
Mydatapoint: variant;
|
||||
{
|
||||
dtDate, dtStart, dtEnd: TDateTime;
|
||||
sStart, sEnd: string;
|
||||
}
|
||||
iUnixSecs: int64;
|
||||
// ========= Internal routines start ===========
|
||||
procedure SetHigh(iMember:Integer;aValue:Variant;aDateTime:TDateTime);
|
||||
begin
|
||||
If aValue > FoobotDataHighs[iMember] then
|
||||
begin
|
||||
FoobotDataHighs[iMember]:=aValue;
|
||||
FoobotDataHighTimes[iMember]:=aDateTime;
|
||||
end;
|
||||
end;
|
||||
procedure SetLow(iMember:Integer;aValue:Variant;aDateTime:TDateTime);
|
||||
begin
|
||||
If (aValue < FoobotDataLows[iMember]) OR (FoobotDataLows[iMember] = 0) then
|
||||
begin
|
||||
FoobotDataLows[iMember]:=aValue;
|
||||
FoobotDataLowTimes[iMember]:=aDateTime;
|
||||
end;
|
||||
end;
|
||||
// ========== Internal routines end =============
|
||||
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);
|
||||
// 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
|
||||
0: // First field is a DateTime
|
||||
begin
|
||||
iUnixSecs := int64(Mydatapoint);
|
||||
SetLength(FoobotData_time, K + 1);
|
||||
FoobotData_time[K] := UnixToDateTime(iUnixSecs);
|
||||
end;
|
||||
1: // Particulate matter
|
||||
begin
|
||||
SetLength(FoobotData_pm, K + 1);
|
||||
FoobotData_pm[K] := double(MyDataPoint);
|
||||
SetHigh(J,FoobotData_pm[K],FoobotData_time[K]);
|
||||
SetLow(J,FoobotData_pm[K],FoobotData_time[K]);
|
||||
end;
|
||||
2: // Temperature
|
||||
begin
|
||||
SetLength(FoobotData_tmp, K + 1);
|
||||
FoobotData_tmp[K] := double(MyDataPoint);
|
||||
SetHigh(J,FoobotData_tmp[K],FoobotData_time[K]);
|
||||
SetLow(J,FoobotData_tmp[K],FoobotData_time[K]);
|
||||
end;
|
||||
3: // Humidity
|
||||
begin
|
||||
SetLength(FoobotData_hum, K + 1);
|
||||
FoobotData_hum[K] := double(MyDataPoint);
|
||||
SetHigh(J,FoobotData_hum[K],FoobotData_time[K]);
|
||||
SetLow(J,FoobotData_hum[K],FoobotData_time[K]);
|
||||
end;
|
||||
4: // CO2
|
||||
begin
|
||||
SetLength(FoobotData_co2, K + 1);
|
||||
FoobotData_co2[K] := integer(MyDataPoint);
|
||||
SetHigh(J,FoobotData_co2[K],FoobotData_time[K]);
|
||||
SetLow(J,FoobotData_co2[K],FoobotData_time[K]);
|
||||
end;
|
||||
5: // Volatile compounds
|
||||
begin
|
||||
SetLength(FoobotData_voc, K + 1);
|
||||
FoobotData_voc[K] := integer(MyDataPoint);
|
||||
SetHigh(J,FoobotData_voc[K],FoobotData_time[K]);
|
||||
SetLow(J,FoobotData_voc[K],FoobotData_time[K]);
|
||||
end;
|
||||
6: // All Pollution
|
||||
begin
|
||||
SetLength(FoobotData_allpollu, K + 1);
|
||||
FoobotData_allpollu[K] := double(MyDataPoint);
|
||||
SetHigh(J,FoobotData_allpollu[K],FoobotData_time[K]);
|
||||
SetLow(J,FoobotData_allpollu[K],FoobotData_time[K]);
|
||||
end;
|
||||
end; // of Case
|
||||
end;
|
||||
end;
|
||||
SaveHighLows;
|
||||
end;
|
||||
Function ResetHighLows:Boolean;
|
||||
Var iCount:Integer;
|
||||
begin
|
||||
For iCount:=0 to HIGHLOWMAX do begin
|
||||
FoobotDataHighs[iCount]:=0;
|
||||
FoobotDataLows[iCount]:=0;
|
||||
end;
|
||||
Result:=TRUE;
|
||||
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;
|
||||
|
||||
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;
|
||||
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 := ':';
|
||||
AddHeader('Authorization', EncodeStringBase64(aUsername + ':' + aUserPassword));
|
||||
sRequestURL := Format(FOOBOT_USER_URL, [aUsername]);
|
||||
Get(sRequestURL);
|
||||
if ResponseStatusCode <> 200 then
|
||||
begin
|
||||
ShowMessageFmt('Failed - Foobot server refused with code %d',
|
||||
[ResponseStatusCode]);
|
||||
Exit(False);
|
||||
end;
|
||||
for iCount := 0 to ResponseHeaders.Count do
|
||||
ShowMessage(ResponseHeaders[iCount]);
|
||||
Result := True;
|
||||
end;
|
||||
finally
|
||||
end;
|
||||
|
||||
end;
|
||||
|
||||
function FetchFoobotIdentity(aUsername, aSecretKey: string): boolean;
|
||||
var
|
||||
sUserNameURL: string;
|
||||
JSON: TJSONStringType;
|
||||
DeStreamer: TJSONDeStreamer;
|
||||
begin
|
||||
Result := True; // Assume success: Look for failure
|
||||
sAuthenticationKey:=aSecretKey;
|
||||
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
|
||||
showmessagefmt('Cannot retieve data - Foobot server refused with code %s', [E.Message]);
|
||||
On E: Exception do
|
||||
Result := False;
|
||||
end;
|
||||
end;
|
||||
finally
|
||||
DeStreamer.Free;
|
||||
end;
|
||||
end;
|
||||
|
||||
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
|
||||
TheCurrentFoobot:=iCurrentFoobot;
|
||||
// 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;
|
||||
SaveLoadHighLows:=TRUE;
|
||||
TheCurrentFoobot:=0;
|
||||
end;
|
||||
|
||||
finalization
|
||||
begin
|
||||
If Assigned(HLINI) then FreeAndNil(HLINI);
|
||||
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);
|
||||
end;
|
||||
|
||||
end.
|
20
applications/foobot/readme.txt
Normal file
20
applications/foobot/readme.txt
Normal file
@ -0,0 +1,20 @@
|
||||
Foobot project
|
||||
==============
|
||||
|
||||
The main application Foobot Interregator shows how to pull all the data available from the Foobot API
|
||||
The monitor application shows how to write a practical graphical Foobot Monitor for one foobot
|
||||
Both applications have dependency for CryptIni - available via OnlinePackageManager
|
||||
|
||||
If you want to build your own GUI, then you need three files:
|
||||
1. foobot_objects.pas - Object definitions
|
||||
2. ugenericcollections.pas - helper file for foobot_objects
|
||||
3. foobot_utilities.pas - the main API
|
||||
|
||||
Use the routines in foobot_utilities.pas to:
|
||||
1. Fetch Foobot identities via Username + API Key
|
||||
2. Fetch data from an identity
|
||||
|
||||
In all cases, you will need a foobot account and an API key.
|
||||
The API key is freely available from the Foobot website
|
||||
|
||||
- minesadorada@charcodelvalle.com
|
50
applications/foobot/ugenericcollection.pas
Normal file
50
applications/foobot/ugenericcollection.pas
Normal file
@ -0,0 +1,50 @@
|
||||
unit ugenericcollection;
|
||||
|
||||
{$mode objfpc}{$H+}
|
||||
|
||||
interface
|
||||
|
||||
uses
|
||||
Classes, SysUtils;
|
||||
|
||||
type
|
||||
|
||||
{ TGenericCollection }
|
||||
|
||||
generic TGenericCollection<T> = class(TCollection)
|
||||
private
|
||||
function GetItems(Index: integer): T;
|
||||
procedure SetItems(Index: integer; AValue: T);
|
||||
public
|
||||
constructor Create;
|
||||
public
|
||||
function Add: T;
|
||||
public
|
||||
property Items[Index: integer]: T read GetItems write SetItems; default;
|
||||
end;
|
||||
|
||||
implementation
|
||||
|
||||
{ TGenericCollection }
|
||||
|
||||
function TGenericCollection.GetItems(Index: integer): T;
|
||||
begin
|
||||
Result := T(inherited Items[Index]);
|
||||
end;
|
||||
|
||||
procedure TGenericCollection.SetItems(Index: integer; AValue: T);
|
||||
begin
|
||||
Items[Index].Assign(AValue);
|
||||
end;
|
||||
|
||||
constructor TGenericCollection.Create;
|
||||
begin
|
||||
inherited Create(T);
|
||||
end;
|
||||
|
||||
function TGenericCollection.Add: T;
|
||||
begin
|
||||
Result := T(inherited Add);
|
||||
end;
|
||||
|
||||
end.
|
Reference in New Issue
Block a user