keytalk-api v1.0.15
KeyTalk-API 1.0
Typescript definition included.
Installation
Using npm:
npm install keytalk-api --saveIn Node.js:
// Plain JS
const kt = require("keytalk-api");
// ES6/Typescript
import * as kt from "keytalk-api";Include additional modules as you need them:
import * as trend from "keytalk-api/trend";
import * as sh from "keytalk-api/dist/scriptHelper";
import * as x2j from "keytalk-api/dist/excel2json";Basic usage
Setup
import kt from "keytalk-api";
let engine:kt.engine.KeyTalkEngine=new kt.engine.KeyTalkEngine();Connect and login
engine.url="https://mysite.com/";
engine.login("user", "password").then(
() => {
// CONNECTED
// ..code goes here..
},
() => {
// FAILED
}
);Logout
engine.logout((ci,kv)=> {
// DISCONNECTED
// ..code goes here..
});Read value
engine.requestAsync("*time", undefined).promise().then((kv:kt.engine.IKeyTalkValue )=> {
if(kv.isValue()) {
// .. code goes here..
console.log(kv.asJavascriptValue().toString());
} else {
if(kv.isError()) {
console.log(kv.error);
}
}
});Request will return object (variable kv above) of type kt.IKeyTalkValue
Notable members of this object are:
typ: KeyTalkValueTyp;
error?: string;
asJavascriptValue(): any;
asBool(def?: boolean): boolean;
asNumber(): number;
asString(): string;
asDate(): Date;
asDateTime(): Date;
asTimeSpan(): number;
isError(): boolean;
isNull(): boolean;
isValue(): boolean;KeyTalkValueTyp is a string defined in typescript as
declare type KeyTalkValueTyp = "BOOL" | "STRING" | "INT8" | "UINT8" | "INT16" | "UINT16" | "INT32" | "UINT32" | "SINGLE" | "DOUBLE" | "TIMESPAN" | "DATE" | "TIME" | "DATETIME" | "NULL" | "RAW";isValue() can be used to quickly determine if the received value is valid (not error & not null).
Subscribe to value
// ... code to connect engine goes here ...
let cci=engine.subscribe("*time", (ci: kt.engine.IKeyTalkClientItem, kv: kt.engine.IKeyTalkValue)=> {
if(kv.isValue()) {
// .. code goes here..
console.log(kv.asJavascriptValue());
} else {
if(kv.isError()) {
console.log(kv.error);
}
}
});
// ... code to disconnect engine goes here ...cci will referencing the same object as the callback parameter ci.
To stop subscribing call
cci.cancel();Write value
// ... code to connect engine goes here ...
engine.pokeAsync("tag", "SINGLE", "1.3",undefined).promise().then( (pr)=> {
if(pr.isError()) {
console.log(pr.error);
}
});
// ... code to disconnect engine goes here ...The third argument to pokeAsync is the value to be written. It need to be specified as a string and formatted correctly depending on it's type (the second parameter). If the type is unknown, use requestAsync to read it's type in the returned typ field.
SINGLEandDOUBLEshould use . as decimal separator, no thousands separator characters. Example:"1.23"BOOLEANshould be set as"true"or"false"RAWshould be Base64 encodedDATEshould be as"yyyy-MM-dd"DATETIMEshould be as"yyyy-MM-dd HH:mm:ss"TIMESPANshould be a string with time length units, examples: "10ms", "1s", "1m", "1h", "1d", "2d4h", "5m 2000ms"
Eventlog
Changes of values, logins (and more) are stored in an events log. Sometime it's better to avoid this, for example if a script logon ever 10 minutes to read a value, the events log would be drowned with uninteresting logons entires.
engine.noEvLog=true;Trenddata (Historic data)
Get raw data
KeyTalk stores it's trend data as events. Values are periodic read, but only when the value is changes more than a threshold is a new event stored.
import * as trend from "keytalk-api/trend";
// ... code to connect engine goes here ...
let lh = new trend.v3.LoggDownloadHandler(engine, tag);
return lh.query(startDate.valueOf(), endDate).then(
(decoder: trend.trend.IDecoder) => {
while (!decoder.eof()) {
console.log(decoder.getData())
decoder.next();
}
},
(err: string) => {
console.error("Error, "+err);
return err;
}
);
// ... code to disconnect engine goes here ...Observe, data entries outside the passed start and end range may also be returned. Data is stored in lager "blocks", and the all downloaded data will be returned.
Observe, the ´tag´ must refer to a "log object". It is most often named the same as the value tag appended with "/log". Not all value has a log object.
The getData() function of the IDecode returns a LogEntry
enum LoggerTyp {
empty = 0,
start = 1,
stop = 2,
chg = 3,
err = 4
}
interface LogEntry {
ltyp: LoggerTyp;
stamp: number; // ms sedan 1970
ktyp: KeyTyp;
val: any;
}Helpers
Convert event data to periodic data
enum LoggStatus {
ok = 0,
error = 1,
closed = 2
}
interface LogV {
stamp: number; // ms sedan 1970
ktyp: KeyTyp;
val: any;
dval: number;
status: LoggStatus;
}
function getAllInRange2(startX: number, endX: number, timeRef: number, interv: number, ld: trend.IDecoder, onlyPositiveChange?: boolean, callback?: ((v: trend.LogV) => boolean)): trend.LogV[];This helper function will calculate the value at every (timeRef + interv * N) inside startX and endX.
let lh = new trend.v3.LoggDownloadHandler(engine, tag);
return lh.query(startDate.valueOf(), endDate).then(
(decoder: trend.trend.IDecoder) =>
trend.calc.getAllInRange2(startDate, endDate,
new Date(2000, 0, 1, 0, 0).valueOf(),
30 * 60 * 1000,
decoder)
)
.then( (data:trend.trend.LogV[]) => {
data.forEach(lv=>console.log(lv));
})When reading meter values (energy, volume, ...) the values are stored as meter readings, to get the delta values between time intervals
the getAllInRangeMeterReading function can be used.
getAllInRangeMeterReading(start: number, end: number, timeref: number, interv: number, ld: trend.IDecoder, isGrouped: boolean, callback?: ((v: trend.LogV) => boolean)): trend.LogV[];isGrouped should be set to false if group calculation isn't used (not yet documented)
Grouping and group functions
Need to get the average temperature per week?
Use the helper calc to apply grouping on a list of values.
enum LoggFunc {
none = 0,
sum = 1,
average = 2,
min = 3,
max = 4
}
enum LoggGroup {
none = 0,
hour = 1,
day = 2,
week = 3,
month = 4,
year = 5
}
function calc(data: trend.LogV[], f: trend.LoggFunc, g: trend.LoggGroup,callback?:((v:trend.LogV)=>boolean)): trend.LogV[] {