yoctolib-esm
Version:
Yoctopuce library for TypeScript/JavaScript, as an ECMAScript 2015 module
1,295 lines (1,294 loc) • 559 kB
JavaScript
/*********************************************************************
*
* $Id: yocto_api.ts 72760 2026-04-16 08:58:35Z mvuilleu $
*
* High-level programming interface, common to all modules
*
* - - - - - - - - - License information: - - - - - - - - -
*
* Copyright (C) 2011 and beyond by Yoctopuce Sarl, Switzerland.
*
* Yoctopuce Sarl (hereafter Licensor) grants to you a perpetual
* non-exclusive license to use, modify, copy and integrate http
* file into your software for the sole purpose of interfacing
* with Yoctopuce products.
*
* You may reproduce and distribute copies of this file in
* source or object form, as long as the sole purpose of this
* code is to interface with Yoctopuce products. You must retain
* this notice in the distributed source file.
*
* You should refer to Yoctopuce General Terms and Conditions
* for additional information regarding your rights and
* obligations.
*
* THE SOFTWARE AND DOCUMENTATION ARE PROVIDED "AS IS" WITHOUT
* WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
* WITHOUT LIMITATION, ANY WARRANTY OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO
* EVENT SHALL LICENSOR BE LIABLE FOR ANY INCIDENTAL, SPECIAL,
* INDIRECT OR CONSEQUENTIAL DAMAGES, LOST PROFITS OR LOST DATA,
* COST OF PROCUREMENT OF SUBSTITUTE GOODS, TECHNOLOGY OR
* SERVICES, ANY CLAIMS BY THIRD PARTIES (INCLUDING BUT NOT
* LIMITED TO ANY DEFENSE THEREOF), ANY CLAIMS FOR INDEMNITY OR
* CONTRIBUTION, OR OTHER SIMILAR COSTS, WHETHER ASSERTED ON THE
* BASIS OF CONTRACT, TORT (INCLUDING NEGLIGENCE), BREACH OF
* WARRANTY, OR OTHERWISE.
*
*********************************************************************/
//--- (generated code: YFunction return codes)
// Yoctopuce error codes, also used by default as function return value
export const YAPI_SUCCESS = 0; // everything worked all right
export const YAPI_NOT_INITIALIZED = -1; // call yInitAPI() first !
export const YAPI_INVALID_ARGUMENT = -2; // one of the arguments passed to the function is invalid
export const YAPI_NOT_SUPPORTED = -3; // the operation attempted is (currently) not supported
export const YAPI_DEVICE_NOT_FOUND = -4; // the requested device is not reachable
export const YAPI_VERSION_MISMATCH = -5; // the device firmware is incompatible with this API version
export const YAPI_DEVICE_BUSY = -6; // the device is busy with another task and cannot answer
export const YAPI_TIMEOUT = -7; // the device took too long to provide an answer
export const YAPI_IO_ERROR = -8; // there was an I/O problem while talking to the device
export const YAPI_NO_MORE_DATA = -9; // there is no more data to read from
export const YAPI_EXHAUSTED = -10; // you have run out of a limited resource, check the documentation
export const YAPI_DOUBLE_ACCES = -11; // you have two process that try to access to the same device
export const YAPI_UNAUTHORIZED = -12; // unauthorized access to password-protected device
export const YAPI_RTC_NOT_READY = -13; // real-time clock has not been initialized (or time was lost)
export const YAPI_FILE_NOT_FOUND = -14; // the file is not found
export const YAPI_SSL_ERROR = -15; // Error reported by mbedSSL
export const YAPI_RFID_SOFT_ERROR = -16; // Recoverable error with RFID tag (eg. tag out of reach), check YRfidStatus for details
export const YAPI_RFID_HARD_ERROR = -17; // Serious RFID error (eg. write-protected, out-of-boundary), check YRfidStatus for details
export const YAPI_BUFFER_TOO_SMALL = -18; // The buffer provided is too small
export const YAPI_DNS_ERROR = -19; // Error during name resolutions (invalid hostname or dns communication error)
export const YAPI_SSL_UNK_CERT = -20; // The certificate is not correctly signed by the trusted CA
export const YAPI_UNCONFIGURED = -21; // Remote hub is not yet configured
export const YAPI_INVALID_INT = 0x7fffffff;
export const YAPI_INVALID_UINT = -1;
export const YAPI_INVALID_LONG = 0x7fffffffffffffff;
export const YAPI_INVALID_DOUBLE = -Number.MAX_VALUE;
export const YAPI_INVALID_STRING = '!INVALID!';
// TLS / SSL definitions
export const YAPI_NO_TRUSTED_CA_CHECK = 1; // Disables certificate checking
export const YAPI_NO_EXPIRATION_CHECK = 2; // Disables certificate expiration date checking
export const YAPI_NO_HOSTNAME_CHECK = 4; // Disable hostname checking
export const YAPI_LEGACY = 8; // Allow non-secure connection (similar to v1.10)
//--- (end of generated code: YFunction return codes)
export const YAPI_MIN_DOUBLE = -Number.MAX_VALUE;
export const YAPI_MAX_DOUBLE = Number.MAX_VALUE;
export const Y_FUNCTIONDESCRIPTOR_INVALID = YAPI_INVALID_STRING;
// yInitAPI constants (not really useful in Javascript, but defined for code portability)
export const Y_DETECT_NONE = 0;
export const Y_DETECT_USB = 1;
export const Y_DETECT_NET = 2;
export const Y_DETECT_ALL = (Y_DETECT_USB | Y_DETECT_NET);
const DEFAULT_DEVICE_LIST_VALIDITY_MS = 10000;
const DEFAULT_NETWORK_TIMEOUT_MS = 20000;
// calibration types
const YOCTO_CALIB_TYPE_OFS = 30;
const NOTIFY_NETPKT_NAME = '0';
const NOTIFY_NETPKT_CHILD = '2';
const NOTIFY_NETPKT_FUNCNAME = '4';
const NOTIFY_NETPKT_FUNCVAL = '5';
const NOTIFY_NETPKT_LOG = '7';
const NOTIFY_NETPKT_FUNCNAMEYDX = '8';
const NOTIFY_NETPKT_CONFCHGYDX = 's';
const NOTIFY_NETPKT_FLUSHV2YDX = 't';
const NOTIFY_NETPKT_FUNCV2YDX = 'u';
const NOTIFY_NETPKT_TIMEV2YDX = 'v';
const NOTIFY_NETPKT_DEVLOGYDX = 'w';
const NOTIFY_NETPKT_TIMEVALYDX = 'x';
const NOTIFY_NETPKT_FUNCVALYDX = 'y';
const NOTIFY_NETPKT_TIMEAVGYDX = 'z';
const NOTIFY_NETPKT_NOT_SYNC = '@';
const NOTIFY_NETPKT_STOP = 10; // =\n
const NOTIFY_V2_LEGACY = 0; // unused (reserved for compatibility with legacy notifications)
const NOTIFY_V2_6RAWBYTES = 1; // largest type: data is always 6 bytes
const NOTIFY_V2_TYPEDDATA = 2; // other types: first data byte holds the decoding format
const NOTIFY_V2_FLUSHGROUP = 3; // no data associated
const PUBVAL_LEGACY = 0; // 0-6 ASCII characters (normally sent as YSTREAM_NOTICE)
const PUBVAL_1RAWBYTE = 1; // 1 raw byte (=2 characters)
const PUBVAL_2RAWBYTES = 2; // 2 raw bytes (=4 characters)
const PUBVAL_3RAWBYTES = 3; // 3 raw bytes (=6 characters)
const PUBVAL_4RAWBYTES = 4; // 4 raw bytes (=8 characters)
const PUBVAL_5RAWBYTES = 5; // 5 raw bytes (=10 characters)
const PUBVAL_6RAWBYTES = 6; // 6 hex bytes (=12 characters) (sent as V2_6RAWBYTES)
const PUBVAL_C_LONG = 7; // 32-bit C signed integer
const PUBVAL_C_FLOAT = 8; // 32-bit C float
const PUBVAL_YOCTO_FLOAT_E3 = 9; // 32-bit Yocto fixed-point format (e-3)
const PUBVAL_YOCTO_FLOAT_E6 = 10; // 32-bit Yocto fixed-point format (e-6)
const YOCTO_PUBVAL_LEN = 16;
const YOCTO_PUBVAL_SIZE = 6;
const YOCTO_HASH_BUF_SIZE = 28;
const YOCTO_BASETYPE_FUNCTION = 0;
const YOCTO_BASETYPE_SENSOR = 1;
const Y_BASETYPES = {
Function: YOCTO_BASETYPE_FUNCTION,
Sensor: YOCTO_BASETYPE_SENSOR
};
export class YErrorMsg {
constructor(msg = '') {
this.msg = msg;
}
}
export class YoctoError extends Error {
constructor(...params) {
// Pass remaining arguments (including vendor specific ones) to parent constructor
super(...params);
this.errorMsg = this.name;
this.name = 'YoctoError';
// Maintains proper stack trace for where our error was thrown (only available on V8)
if ('captureStackTrace' in Error) {
// @ts-ignore
Error.captureStackTrace(this, YoctoError);
}
}
}
export class _YY_UrlInfo {
constructor(str_url) {
this.orgUrl = str_url;
let proto = 'auto';
let user = '';
let pass = '';
let port = 4444;
let host;
let dom = '';
if (str_url.slice(0, 7) == 'http://') {
proto = 'http';
str_url = str_url.slice(7);
}
else if (str_url.slice(0, 5) == 'ws://') {
proto = 'ws';
str_url = str_url.slice(5);
}
else if (str_url.slice(0, 8) == 'https://') {
proto = 'https';
port = 4443;
str_url = str_url.slice(8);
}
else if (str_url.slice(0, 6) == 'wss://') {
proto = 'wss';
port = 4443;
str_url = str_url.slice(6);
}
else if (str_url.slice(0, 7) == 'auto://') {
str_url = str_url.slice(7);
}
else if (str_url.slice(0, 9) == 'secure://') {
str_url = str_url.slice(9);
port = 4443;
proto = 'secure';
}
str_url = str_url.replace('/not.byn', '');
if (str_url[str_url.length - 1] == '/') {
str_url = str_url.slice(0, str_url.length - 1);
}
let pos = str_url.indexOf('/');
if (pos > 0) {
dom = str_url.slice(pos);
str_url = str_url.slice(0, pos);
}
let authpos = str_url.indexOf('@');
if (authpos >= 0) {
let auth = str_url.slice(0, authpos);
let passpos = auth.indexOf(':');
if (passpos >= 0) {
user = auth.slice(0, passpos);
pass = auth.slice(passpos + 1);
}
else {
user = auth;
}
str_url = str_url.slice(authpos + 1);
}
let endv6 = str_url.indexOf(']');
pos = str_url.indexOf(':');
if (pos > 0 && endv6 > 0 && pos < endv6) {
// ipv6 URL
pos = str_url.indexOf(':', endv6);
}
if (pos < 0) {
host = str_url;
if (dom != '') {
if (proto == 'http') {
port = 80;
}
else if (proto == 'https') {
port = 443;
}
}
}
else {
host = str_url.slice(0, pos);
port = YAPIContext.imm_atoi(str_url.slice(pos + 1));
}
if (host == 'callback') {
port = 4444;
}
this.proto = proto;
this.user = user;
this.pass = pass;
this.host = host;
this.port = port;
this.domain = dom;
}
imm_getHost() {
return this.host;
}
imm_getPass() {
return this.pass;
}
imm_getPort() {
return this.port;
}
imm_getUser() {
return this.user;
}
imm_getUrl(withProto = false, withUserPass = true, withEndSlash = false) {
if (this.proto == "usb") {
return "usb";
}
let url = "";
if (withProto) {
url += this.proto + "://";
}
if (withUserPass && this.user != "") {
url += this.user;
if (this.pass != "") {
url += ":";
url += this.pass;
}
url += "@";
}
url += this.host;
url += ":";
url += this.port;
url += this.domain;
if (withEndSlash && url[url.length - 1] != '/') {
url += '/';
}
return url;
}
imm_getRootUrl() {
return this.imm_getUrl(true, false, true);
}
imm_getProto() {
return this.proto;
}
imm_useWebSocket() {
return this.proto.startsWith("ws") || this.proto == "auto" || this.proto == "secure";
}
/**
* @return subdomain (starting with a /)
*/
imm_getSubDomain() {
let dom = this.domain;
return dom;
}
imm_hasAuthParam() {
return this.user != "";
}
imm_useSecureSocket() {
return "wss" == this.proto || "https" == this.proto || "secure" == this.proto;
}
imm_testInfoJson() {
return this.proto == "auto" || this.proto == "secure" || this.proto == "http" || this.proto == "https";
}
imm_updateBestProto(proto, port) {
this.port = port;
if (this.proto != "http" && this.proto != "https") {
this.proto = proto;
}
}
imm_updateForRedirect(host, port, is_secure) {
this.host = host;
this.port = port;
if (this.imm_useWebSocket()) {
this.proto = is_secure ? "wss" : "ws";
}
else {
this.proto = is_secure ? "https" : "http";
}
}
imm_updatePortInfo(proto, port) {
this.proto = proto;
this.port = port;
}
imm_getOriginalURL() {
return this.orgUrl;
}
imm_updateFrom(urlInfo) {
this.proto = urlInfo.proto;
this.user = urlInfo.user;
this.pass = urlInfo.pass;
this.host = urlInfo.host;
this.port = urlInfo.port;
this.domain = urlInfo.domain;
this.orgUrl = urlInfo.orgUrl;
}
}
/**
* MD5 hash computation code
*
* This code is derived from the MD5 implementation from Sergey Lyubka, author of SHTTPD.
* Any other implementation would do as well, but we chose to translate this one to JS.
* This code has been published by Sergey under his "THE BEER-WARE LICENSE" (Revision 42):
*
* Sergey Lyubka wrote this software. As long as you retain this notice you
* can do whatever you want with this stuff. If we meet some day, and you think
* this stuff is worth it, you can buy me a beer in return.
*
*/
class Y_MD5Ctx {
constructor() {
this.buf = new Uint32Array(4);
this.bits = new Uint32Array(2);
this.inBuf = new ArrayBuffer(64);
this.in8 = new Uint8Array(this.inBuf);
this.in32 = new Uint32Array(this.inBuf);
this.in32[0] = 1;
this.bigEndian = (this.in8[0] != 1);
this.buf[0] = 0x67452301 >>> 0;
this.buf[1] = 0xefcdab89 >>> 0;
this.buf[2] = 0x98badcfe >>> 0;
this.buf[3] = 0x10325476 >>> 0;
this.bits[0] = 0;
this.bits[1] = 0;
}
_byteReverseIn() {
for (let i = 0; i < 16; i++) {
let a = this.in32[i];
this.in32[i] = ((a >>> 24) | ((a & 0xff) << 24) | ((a & 0xff0000) >>> 8) | ((a & 0xff00) << 8)) >>> 0;
}
}
_transform() {
let F1 = ((x, y, z) => ((z ^ (x & (y ^ z)))) >>> 0);
let F2 = ((x, y, z) => F1(z, x, y));
let F3 = ((x, y, z) => ((x ^ y ^ z)) >>> 0);
let F4 = ((x, y, z) => ((y ^ (x | ~z))) >>> 0);
let MD5STEP = ((f, w, x, y, z, data, s) => {
w = (w + f(x, y, z) + (data >>> 0)) >>> 0;
w = (((w << s) >>> 0) | (w >>> (32 - s))) >>> 0;
return (w + x) >>> 0;
});
let a = this.buf[0];
let b = this.buf[1];
let c = this.buf[2];
let d = this.buf[3];
let dataIn = this.in32;
a = MD5STEP(F1, a, b, c, d, dataIn[0] + 0xd76aa478, 7);
d = MD5STEP(F1, d, a, b, c, dataIn[1] + 0xe8c7b756, 12);
c = MD5STEP(F1, c, d, a, b, dataIn[2] + 0x242070db, 17);
b = MD5STEP(F1, b, c, d, a, dataIn[3] + 0xc1bdceee, 22);
a = MD5STEP(F1, a, b, c, d, dataIn[4] + 0xf57c0faf, 7);
d = MD5STEP(F1, d, a, b, c, dataIn[5] + 0x4787c62a, 12);
c = MD5STEP(F1, c, d, a, b, dataIn[6] + 0xa8304613, 17);
b = MD5STEP(F1, b, c, d, a, dataIn[7] + 0xfd469501, 22);
a = MD5STEP(F1, a, b, c, d, dataIn[8] + 0x698098d8, 7);
d = MD5STEP(F1, d, a, b, c, dataIn[9] + 0x8b44f7af, 12);
c = MD5STEP(F1, c, d, a, b, dataIn[10] + 0xffff5bb1, 17);
b = MD5STEP(F1, b, c, d, a, dataIn[11] + 0x895cd7be, 22);
a = MD5STEP(F1, a, b, c, d, dataIn[12] + 0x6b901122, 7);
d = MD5STEP(F1, d, a, b, c, dataIn[13] + 0xfd987193, 12);
c = MD5STEP(F1, c, d, a, b, dataIn[14] + 0xa679438e, 17);
b = MD5STEP(F1, b, c, d, a, dataIn[15] + 0x49b40821, 22);
a = MD5STEP(F2, a, b, c, d, dataIn[1] + 0xf61e2562, 5);
d = MD5STEP(F2, d, a, b, c, dataIn[6] + 0xc040b340, 9);
c = MD5STEP(F2, c, d, a, b, dataIn[11] + 0x265e5a51, 14);
b = MD5STEP(F2, b, c, d, a, dataIn[0] + 0xe9b6c7aa, 20);
a = MD5STEP(F2, a, b, c, d, dataIn[5] + 0xd62f105d, 5);
d = MD5STEP(F2, d, a, b, c, dataIn[10] + 0x02441453, 9);
c = MD5STEP(F2, c, d, a, b, dataIn[15] + 0xd8a1e681, 14);
b = MD5STEP(F2, b, c, d, a, dataIn[4] + 0xe7d3fbc8, 20);
a = MD5STEP(F2, a, b, c, d, dataIn[9] + 0x21e1cde6, 5);
d = MD5STEP(F2, d, a, b, c, dataIn[14] + 0xc33707d6, 9);
c = MD5STEP(F2, c, d, a, b, dataIn[3] + 0xf4d50d87, 14);
b = MD5STEP(F2, b, c, d, a, dataIn[8] + 0x455a14ed, 20);
a = MD5STEP(F2, a, b, c, d, dataIn[13] + 0xa9e3e905, 5);
d = MD5STEP(F2, d, a, b, c, dataIn[2] + 0xfcefa3f8, 9);
c = MD5STEP(F2, c, d, a, b, dataIn[7] + 0x676f02d9, 14);
b = MD5STEP(F2, b, c, d, a, dataIn[12] + 0x8d2a4c8a, 20);
a = MD5STEP(F3, a, b, c, d, dataIn[5] + 0xfffa3942, 4);
d = MD5STEP(F3, d, a, b, c, dataIn[8] + 0x8771f681, 11);
c = MD5STEP(F3, c, d, a, b, dataIn[11] + 0x6d9d6122, 16);
b = MD5STEP(F3, b, c, d, a, dataIn[14] + 0xfde5380c, 23);
a = MD5STEP(F3, a, b, c, d, dataIn[1] + 0xa4beea44, 4);
d = MD5STEP(F3, d, a, b, c, dataIn[4] + 0x4bdecfa9, 11);
c = MD5STEP(F3, c, d, a, b, dataIn[7] + 0xf6bb4b60, 16);
b = MD5STEP(F3, b, c, d, a, dataIn[10] + 0xbebfbc70, 23);
a = MD5STEP(F3, a, b, c, d, dataIn[13] + 0x289b7ec6, 4);
d = MD5STEP(F3, d, a, b, c, dataIn[0] + 0xeaa127fa, 11);
c = MD5STEP(F3, c, d, a, b, dataIn[3] + 0xd4ef3085, 16);
b = MD5STEP(F3, b, c, d, a, dataIn[6] + 0x04881d05, 23);
a = MD5STEP(F3, a, b, c, d, dataIn[9] + 0xd9d4d039, 4);
d = MD5STEP(F3, d, a, b, c, dataIn[12] + 0xe6db99e5, 11);
c = MD5STEP(F3, c, d, a, b, dataIn[15] + 0x1fa27cf8, 16);
b = MD5STEP(F3, b, c, d, a, dataIn[2] + 0xc4ac5665, 23);
a = MD5STEP(F4, a, b, c, d, dataIn[0] + 0xf4292244, 6);
d = MD5STEP(F4, d, a, b, c, dataIn[7] + 0x432aff97, 10);
c = MD5STEP(F4, c, d, a, b, dataIn[14] + 0xab9423a7, 15);
b = MD5STEP(F4, b, c, d, a, dataIn[5] + 0xfc93a039, 21);
a = MD5STEP(F4, a, b, c, d, dataIn[12] + 0x655b59c3, 6);
d = MD5STEP(F4, d, a, b, c, dataIn[3] + 0x8f0ccc92, 10);
c = MD5STEP(F4, c, d, a, b, dataIn[10] + 0xffeff47d, 15);
b = MD5STEP(F4, b, c, d, a, dataIn[1] + 0x85845dd1, 21);
a = MD5STEP(F4, a, b, c, d, dataIn[8] + 0x6fa87e4f, 6);
d = MD5STEP(F4, d, a, b, c, dataIn[15] + 0xfe2ce6e0, 10);
c = MD5STEP(F4, c, d, a, b, dataIn[6] + 0xa3014314, 15);
b = MD5STEP(F4, b, c, d, a, dataIn[13] + 0x4e0811a1, 21);
a = MD5STEP(F4, a, b, c, d, dataIn[4] + 0xf7537e82, 6);
d = MD5STEP(F4, d, a, b, c, dataIn[11] + 0xbd3af235, 10);
c = MD5STEP(F4, c, d, a, b, dataIn[2] + 0x2ad7d2bb, 15);
b = MD5STEP(F4, b, c, d, a, dataIn[9] + 0xeb86d391, 21);
this.buf[0] = ((this.buf[0] + a) & 0xffffffff) >>> 0;
this.buf[1] = ((this.buf[1] + b) & 0xffffffff) >>> 0;
this.buf[2] = ((this.buf[2] + c) & 0xffffffff) >>> 0;
this.buf[3] = ((this.buf[3] + d) & 0xffffffff) >>> 0;
}
addData(buf) {
let len = buf.length;
let pos = 0;
let t = this.bits[0];
this.bits[0] = (t + (len << 3)) >>> 0;
if (this.bits[0] < t) {
this.bits[1]++;
}
this.bits[1] += len >>> 29;
t = (t >>> 3) & 0x3f;
while (pos < len) {
while (pos < len && t < 64) {
this.in8[t++] = buf[pos++];
}
if (t < 64)
return;
if (this.bigEndian)
this._byteReverseIn();
this._transform();
t = 0;
}
}
calculate() {
let t = (this.bits[0] >>> 3) & 0x3f;
this.in8[t++] = 0x80;
if (t > 56) {
while (t < 64) {
this.in8[t++] = 0;
}
if (this.bigEndian)
this._byteReverseIn();
this._transform();
for (t = 0; t < 14; t++) {
this.in32[t] = 0;
}
}
else {
while (t < 56) {
this.in8[t++] = 0;
}
if (this.bigEndian)
this._byteReverseIn();
}
this.in32[14] = this.bits[0];
this.in32[15] = this.bits[1];
this._transform();
let res = new Uint8Array(16);
for (t = 0; t < 16; t++) {
res[t] = (this.buf[t >>> 2] >>> (8 * (t & 3))) & 0xff;
}
return res;
}
}
//
// YFunctionType Class (used internally)
//
// Instances of this class stores everything we know about a given type of function:
// Mapping between function logical names and Hardware ID as discovered on hubs,
// and existing instances of YFunction (either already connected or simply requested).
// To keep it simple, this implementation separates completely the name resolution
// mechanism, implemented using the yellow pages, and the storage and retrieval of
// existing YFunction instances.
//
class YFunctionType {
constructor(yapi, classname) {
this._yapi = yapi;
this._className = classname;
this._connectedFns = {}; // functions requested and available, by Hardware Id
this._requestedFns = {}; // functions requested but not yet known, by any type of name
this._hwIdByName = {}; // hash table of function Hardware Id by logical name
this._nameByHwId = {}; // hash table of function logical name by Hardware Id
this._valueByHwId = {}; // hash table of function advertised value by logical name
this._baseType = 0; // default to no abstract base type (generic YFunction)
}
/** Index a single function given by HardwareId and logical name; store any advertised value
*
* @returns true iff there was a logical name discrepancy
*/
imm_reindexFunction(str_hwid, str_name, str_val, int_basetype) {
let currname = this._nameByHwId[str_hwid];
let res = false;
if (currname == undefined || currname == '') {
if (str_name != '') {
this._nameByHwId[str_hwid] = str_name;
res = true;
}
}
else if (currname != str_name) {
if (this._hwIdByName[currname] == str_hwid) {
delete this._hwIdByName[currname];
}
if (str_name != '') {
this._nameByHwId[str_hwid] = str_name;
}
else {
delete this._nameByHwId[str_hwid];
}
res = true;
}
if (str_name != '') {
this._hwIdByName[str_name] = str_hwid;
}
if (str_val) {
this._valueByHwId[str_hwid] = str_val;
}
else {
if (this._valueByHwId[str_hwid] == undefined) {
this._valueByHwId[str_hwid] = '';
}
}
if (int_basetype) {
if (this._baseType == 0) {
this._baseType = int_basetype;
}
}
return res;
}
/** Forget a disconnected function given by HardwareId
*/
imm_forgetFunction(str_hwid) {
let currname = this._nameByHwId[str_hwid];
if (currname != undefined) {
if (currname != '' && this._hwIdByName[currname] == str_hwid) {
delete this._hwIdByName[currname];
}
delete this._nameByHwId[str_hwid];
}
if (this._valueByHwId[str_hwid] != undefined) {
delete this._valueByHwId[str_hwid];
}
// Move the function object to the disconnected list
let con_fn = this._connectedFns[str_hwid];
if (con_fn) {
this._requestedFns[str_hwid] = con_fn;
delete this._connectedFns[str_hwid];
}
}
/** Find the exact Hardware Id of the specified function, if currently connected
* If device is not known as connected, return a clean error
* This function will not cause any network access
*/
imm_resolve(str_func) {
let res;
let dotpos = str_func.indexOf('.');
if (dotpos < 0) {
// First case: str_func is the logicalname of a function
res = this._hwIdByName[str_func];
if (res != undefined) {
return {
errorType: YAPI_SUCCESS,
errorMsg: 'no error',
result: String(res)
};
}
// fallback to assuming that str_func is a logicalname or serial number of a module
// with an implicit function name (like serial.module for instance)
dotpos = str_func.length;
str_func += '.' + this._className.substr(0, 1).toLowerCase() + this._className.substr(1);
}
// Second case: str_func is in the form: device_id.function_id
// quick lookup for a known pure hardware id
if (this._valueByHwId[str_func] != undefined) {
return {
errorType: YAPI_SUCCESS,
errorMsg: 'no error',
result: String(str_func)
};
}
let serial = '';
let funcid;
if (dotpos > 0) {
// either the device id is a logical name, or the function is unknown
let devid = str_func.substr(0, dotpos);
funcid = str_func.substr(dotpos + 1);
let dev = this._yapi.imm_getDevice(devid);
if (!dev) {
return {
errorType: YAPI_DEVICE_NOT_FOUND,
errorMsg: 'Device [' + devid + '] not online',
result: ''
};
}
serial = dev.imm_getSerialNumber();
res = serial + '.' + funcid;
if (this._valueByHwId[res] != undefined) {
return {
errorType: YAPI_SUCCESS,
errorMsg: 'no error',
result: String(res)
};
}
// not found neither, may be funcid is a function logicalname
let i, nfun = dev.imm_functionCount();
for (i = 0; i < nfun; i++) {
res = serial + '.' + dev.imm_functionId(i);
let name = this._nameByHwId[res];
if (name != undefined && name == funcid) {
return {
errorType: YAPI_SUCCESS,
errorMsg: 'no error',
result: String(res)
};
}
}
}
else {
funcid = str_func.substr(1);
for (let hwid_str in this._connectedFns) {
let pos = hwid_str.indexOf('.');
let str_function = hwid_str.substr(pos + 1);
if (str_function == funcid) {
return {
errorType: YAPI_SUCCESS,
errorMsg: 'no error',
result: String(hwid_str)
};
}
}
}
return {
errorType: YAPI_DEVICE_NOT_FOUND,
errorMsg: 'No function [' + funcid + '] found on device [' + serial + ']',
result: ''
};
}
/** Find the friendly name (use logical name if available) of the specified function, if currently connected
* If device is not known as connected, return a clean error
* This function will not cause any network access
*/
imm_getFriendlyName(str_func) {
let resolved = this.imm_resolve(str_func);
let name;
if (resolved.errorType != YAPI_SUCCESS) {
return resolved;
}
let hwId = resolved.result;
if (this._className == 'Module') {
let friend = hwId;
name = this._nameByHwId[friend];
if (name != undefined && name != '') {
friend = this._nameByHwId[friend];
}
return {
errorType: YAPI_SUCCESS,
errorMsg: 'no error',
result: String(friend)
};
}
else {
let pos = hwId.indexOf('.');
let str_serialMod = hwId.substr(0, pos);
let str_friendModFull = this._yapi.imm_getFriendlyNameFunction('Module', str_serialMod).result;
let int_friendModDot = str_friendModFull.indexOf('.');
let str_friendMod = (int_friendModDot > 0 ? str_friendModFull.substr(0, int_friendModDot) : str_friendModFull);
let str_friendFunc = hwId.substr(pos + 1);
name = this._nameByHwId[hwId];
if (name != undefined && name != '') {
str_friendFunc = name;
}
return {
errorType: YAPI_SUCCESS,
errorMsg: 'no error',
result: String(str_friendMod + '.' + str_friendFunc)
};
}
}
/** Associates a given function object to a function id
*/
imm_setFunction(str_func, obj_func) {
let funres = this.imm_resolve(str_func);
if (funres.result) {
// the function has been located on a device
this._connectedFns[funres.result] = obj_func;
}
else {
// the function is still abstract
this._requestedFns[str_func] = obj_func;
}
}
/** Retrieve a function object by hardware id, updating the indexes on the fly if needed
*/
imm_getFunction(str_func) {
let funres = this.imm_resolve(str_func);
if (funres.errorType == YAPI_SUCCESS) {
// the function has been located on a device
let conn_fn = this._connectedFns[funres.result];
if (conn_fn != undefined) {
return conn_fn;
}
let req_fn = this._requestedFns[str_func];
if (req_fn != undefined) {
this._connectedFns[funres.result] = req_fn;
delete this._requestedFns[str_func];
}
return req_fn;
}
else {
// the function is still abstract
return this._requestedFns[str_func];
}
}
/** Stores a function advertised value by hardware id, and tell if an event should be queued for it
*/
imm_setFunctionValue(str_hwid, str_pubval) {
let currval = this._valueByHwId[str_hwid];
if (!(currval == undefined) && currval == str_pubval) {
return false;
}
this._valueByHwId[str_hwid] = str_pubval;
return true;
}
/** Retrieve a function advertised value by hardware id
*/
imm_getFunctionValue(str_hwid) {
return this._valueByHwId[str_hwid];
}
/** Return the basetype of this function class
*/
imm_getBaseType() {
return this._baseType;
}
/** Test if function type is compatible with basetype
*/
imm_matchBaseType(baseclass) {
return baseclass == YOCTO_BASETYPE_FUNCTION || baseclass == this._baseType;
}
/** Find the hardwareId of the first instance of a given function class
*/
imm_getFirstHardwareId() {
let res = null;
//noinspection LoopStatementThatDoesntLoopJS
for (res in this._valueByHwId) {
break;
}
return res;
}
/** Find the hardwareId for the next instance of a given function class
*/
imm_getNextHardwareId(str_hwid) {
for (let iter_hwid in this._valueByHwId) {
if (str_hwid == '!') {
return iter_hwid;
}
if (str_hwid == iter_hwid) {
str_hwid = '!';
}
}
return null; // no more instance found
}
}
export class YHTTPBody {
/** Object storing a file to upload
*/
constructor(str_fname, bin_data, fun_progressCb) {
this.fname = str_fname;
this.data = bin_data;
this.progressCb = fun_progressCb;
}
}
export class YHTTPRequest {
/** Object storing the result of any HTTP Query, with status code and error message
*/
constructor(bin_res, int_errType = YAPI_SUCCESS, str_errMsg = 'no error') {
this.devUrl = null;
this.obj_result = null;
this.asyncId = 0;
this.acceptor = null;
this.toBeSent = null;
this.sendPos = 0;
this.progressCb = null;
this.timeoutId = null; /* actually a number | NodeJS.Timeout */
this.sendTimeoutId = null; /* actually a number | NodeJS.Timeout */
this.next = null;
this._creat = '';
this._sent = '';
this.bin_result = bin_res;
this.errorType = int_errType;
this.errorMsg = str_errMsg;
}
}
class YFuncRequest {
/* Object storing the result of a function request, with status code and error message */
constructor(obj_res, int_errType = YAPI_SUCCESS, str_errMsg = 'no error') {
this.errorType = int_errType;
this.errorMsg = str_errMsg;
this.obj_result = obj_res;
}
}
//--- (generated code: YDataStream definitions)
//--- (end of generated code: YDataStream definitions)
//--- (generated code: YDataStream class start)
/**
* YDataStream Class: Unformatted data sequence
*
* DataStream objects represent bare recorded measure sequences,
* exactly as found within the data logger present on Yoctopuce
* sensors.
*
* In most cases, it is not necessary to use DataStream objects
* directly, as the DataSet objects (returned by the
* get_recordedData() method from sensors and the
* get_dataSets() method from the data logger) provide
* a more convenient interface.
*/
//--- (end of generated code: YDataStream class start)
export class YDataStream {
// API symbols as static members
//--- (end of generated code: YDataStream attributes declaration)
constructor(obj_parent, obj_dataset, encoded) {
//--- (generated code: YDataStream constructor)
//--- (end of generated code: YDataStream constructor)
this._cal = null;
this._runNo = 0;
this._utcStamp = 0;
this._nCols = 0;
this._nRows = 0;
this._startTime = 0;
this._duration = 0;
this._dataSamplesInterval = 0;
this._firstMeasureDuration = 0;
this._columnNames = [];
this._functionId = '';
this._isClosed = false;
this._isAvg = false;
this._minVal = 0;
this._avgVal = 0;
this._maxVal = 0;
this._values = [];
this._isLoaded = false;
this.DATA_INVALID = YAPI_INVALID_DOUBLE;
this.DURATION_INVALID = YAPI_INVALID_DOUBLE;
this._parent = obj_parent;
this._yapi = this._parent._yapi;
if (typeof obj_dataset != 'undefined') {
this.imm_initFromDataSet(obj_dataset, encoded);
}
}
//--- (generated code: YDataStream implementation)
_parseCalibArr(iCalib) {
let caltyp;
let calhdl;
let maxpos;
let position;
let calpar = [];
let calraw = [];
let calref = [];
let fRaw;
let fRef;
caltyp = ((iCalib[0] / 1000) >> 0);
if (caltyp < YOCTO_CALIB_TYPE_OFS) {
// Unknown calibration type: calibrated value will be provided by the device
this._cal = null;
return YAPI_SUCCESS;
}
calhdl = this._yapi.imm_getCalibrationHandler(caltyp);
if (!(calhdl != null)) {
// Unknown calibration type: calibrated value will be provided by the device
this._cal = null;
return YAPI_SUCCESS;
}
// New 32 bits text format
maxpos = iCalib.length;
calpar.length = 0;
position = 1;
while (position < maxpos) {
calpar.push(iCalib[position]);
position = position + 1;
}
calraw.length = 0;
calref.length = 0;
position = 1;
while (position + 1 < maxpos) {
fRaw = iCalib[position];
fRaw = fRaw / 1000.0;
fRef = iCalib[position + 1];
fRef = fRef / 1000.0;
calraw.push(fRaw);
calref.push(fRef);
position = position + 2;
}
this._cal = { src: '', hdl: calhdl, typ: caltyp, par: calpar, raw: calraw, cal: calref };
return YAPI_SUCCESS;
}
imm_initFromDataSet(dataset, encoded) {
let val;
let ms_offset;
let samplesPerHour;
let caltyp;
let iCalib = [];
// decode sequence header to extract data
this._runNo = encoded[0] + ((encoded[1] << 16));
this._utcStamp = encoded[2] + ((encoded[3] << 16));
val = encoded[4];
this._isAvg = ((val & 0x100) == 0);
samplesPerHour = (val & 0xff);
if ((val & 0x100) != 0) {
samplesPerHour = samplesPerHour * 3600;
}
else {
if ((val & 0x200) != 0) {
samplesPerHour = samplesPerHour * 60;
}
}
this._dataSamplesInterval = 3600.0 / samplesPerHour;
ms_offset = encoded[6];
if (ms_offset < 1000) {
// new encoding -> add the ms to the UTC timestamp
this._startTime = this._utcStamp + (ms_offset / 1000.0);
}
else {
// legacy encoding subtract the measure interval form the UTC timestamp
this._startTime = this._utcStamp - this._dataSamplesInterval;
}
this._firstMeasureDuration = encoded[5];
if (!(this._isAvg)) {
this._firstMeasureDuration = this._firstMeasureDuration / 1000.0;
}
val = encoded[7];
this._isClosed = (val != 0xffff);
if (val == 0xffff) {
val = 0;
}
this._nRows = val;
if (this._nRows > 0) {
if (this._firstMeasureDuration > 0) {
this._duration = this._firstMeasureDuration + (this._nRows - 1) * this._dataSamplesInterval;
}
else {
this._duration = this._nRows * this._dataSamplesInterval;
}
}
else {
this._duration = 0;
}
// precompute decoding parameters
iCalib = dataset.imm_get_calibration();
caltyp = iCalib[0];
if (caltyp == 0) {
this._cal = null;
}
else {
this._parseCalibArr(iCalib);
}
// preload column names for backward-compatibility
this._functionId = dataset.imm_get_functionId();
if (this._isAvg) {
this._columnNames.length = 0;
this._columnNames.push(this._functionId + '_min');
this._columnNames.push(this._functionId + '_avg');
this._columnNames.push(this._functionId + '_max');
this._nCols = 3;
}
else {
this._columnNames.length = 0;
this._columnNames.push(this._functionId);
this._nCols = 1;
}
// decode min/avg/max values for the sequence
if (this._nRows > 0) {
this._avgVal = this.imm_decodeAvg(encoded[8] + (((encoded[9] ^ 0x8000) << 16)), 1);
this._minVal = this.imm_decodeVal(encoded[10] + ((encoded[11] << 16)));
this._maxVal = this.imm_decodeVal(encoded[12] + ((encoded[13] << 16)));
}
return 0;
}
imm_parseStream(sdata) {
let idx;
let udat = [];
let dat = [];
if (this._isLoaded && !(this._isClosed)) {
return YAPI_SUCCESS;
}
if ((sdata).length == 0) {
this._nRows = 0;
return YAPI_SUCCESS;
}
udat = this._yapi.imm_decodeWords(this._parent.imm_json_get_string(sdata));
this._values.length = 0;
idx = 0;
if (this._isAvg) {
while (idx + 3 < udat.length) {
dat.length = 0;
if ((udat[idx] == 65535) && (udat[idx + 1] == 65535)) {
dat.push(NaN);
dat.push(NaN);
dat.push(NaN);
}
else {
dat.push(this.imm_decodeVal(udat[idx + 2] + (((udat[idx + 3]) << 16))));
dat.push(this.imm_decodeAvg(udat[idx] + ((((udat[idx + 1]) ^ 0x8000) << 16)), 1));
dat.push(this.imm_decodeVal(udat[idx + 4] + (((udat[idx + 5]) << 16))));
}
idx = idx + 6;
this._values.push(dat.slice());
}
}
else {
while (idx + 1 < udat.length) {
dat.length = 0;
if ((udat[idx] == 65535) && (udat[idx + 1] == 65535)) {
dat.push(NaN);
}
else {
dat.push(this.imm_decodeAvg(udat[idx] + ((((udat[idx + 1]) ^ 0x8000) << 16)), 1));
}
this._values.push(dat.slice());
idx = idx + 2;
}
}
this._nRows = this._values.length;
this._isLoaded = true;
return YAPI_SUCCESS;
}
imm_wasLoaded() {
return this._isLoaded;
}
imm_get_url() {
let url;
url = 'logger.json?id=' + this._functionId + '&run=' + String(Math.round(this._runNo)) + '&utc=' + String(Math.round(this._utcStamp));
return url;
}
imm_get_baseurl() {
let url;
url = 'logger.json?id=' + this._functionId + '&run=' + String(Math.round(this._runNo)) + '&utc=';
return url;
}
imm_get_urlsuffix() {
let url;
url = String(Math.round(this._utcStamp));
return url;
}
async loadStream() {
return this.imm_parseStream(await this._parent._download(this.imm_get_url()));
}
imm_decodeVal(w) {
let val;
val = (w) / 1000.0;
if (!(this._cal == null)) {
val = this._cal.hdl(val, this._cal.typ, this._cal.par, this._cal.raw, this._cal.cal);
}
return val;
}
imm_decodeAvg(dw, count) {
let val;
val = (dw) / 1000.0;
if (!(this._cal == null)) {
val = this._cal.hdl(val, this._cal.typ, this._cal.par, this._cal.raw, this._cal.cal);
}
return val;
}
async isClosed() {
return this._isClosed;
}
/**
* Returns the run index of the data stream. A run can be made of
* multiple datastreams, for different time intervals.
*
* @return an unsigned number corresponding to the run index.
*/
async get_runIndex() {
return this._runNo;
}
/**
* Returns the relative start time of the data stream, measured in seconds.
* For recent firmwares, the value is relative to the present time,
* which means the value is always negative.
* If the device uses a firmware older than version 13000, value is
* relative to the start of the time the device was powered on, and
* is always positive.
* If you need an absolute UTC timestamp, use get_realStartTimeUTC().
*
* <b>DEPRECATED</b>: This method has been replaced by get_realStartTimeUTC().
*
* @return an unsigned number corresponding to the number of seconds
* between the start of the run and the beginning of this data
* stream.
*/
async get_startTime() {
return this._utcStamp - ((Date.now() / 1000) >> 0);
}
/**
* Returns the start time of the data stream, relative to the Jan 1, 1970.
* If the UTC time was not set in the datalogger at the time of the recording
* of this data stream, this method returns 0.
*
* <b>DEPRECATED</b>: This method has been replaced by get_realStartTimeUTC().
*
* @return an unsigned number corresponding to the number of seconds
* between the Jan 1, 1970 and the beginning of this data
* stream (i.e. Unix time representation of the absolute time).
*/
async get_startTimeUTC() {
return Math.round(this._startTime);
}
/**
* Returns the start time of the data stream, relative to the Jan 1, 1970.
* If the UTC time was not set in the datalogger at the time of the recording
* of this data stream, this method returns 0.
*
* @return a floating-point number corresponding to the number of seconds
* between the Jan 1, 1970 and the beginning of this data
* stream (i.e. Unix time representation of the absolute time).
*/
async get_realStartTimeUTC() {
return this._startTime;
}
/**
* Returns the number of milliseconds between two consecutive
* rows of this data stream. By default, the data logger records one row
* per second, but the recording frequency can be changed for
* each device function
*
* @return an unsigned number corresponding to a number of milliseconds.
*/
async get_dataSamplesIntervalMs() {
return Math.round(this._dataSamplesInterval * 1000);
}
async get_dataSamplesInterval() {
return this._dataSamplesInterval;
}
async get_firstDataSamplesInterval() {
return this._firstMeasureDuration;
}
/**
* Returns the number of data rows present in this stream.
*
* If the device uses a firmware older than version 13000,
* this method fetches the whole data stream from the device
* if not yet done, which can cause a little delay.
*
* @return an unsigned number corresponding to the number of rows.
*
* On failure, throws an exception or returns zero.
*/
async get_rowCount() {
if ((this._nRows != 0) && this._isClosed) {
return this._nRows;
}
await this.loadStream();
return this._nRows;
}
/**
* Returns the number of data columns present in this stream.
* The meaning of the values present in each column can be obtained
* using the method get_columnNames().
*
* If the device uses a firmware older than version 13000,
* this method fetches the whole data stream from the device
* if not yet done, which can cause a little delay.
*
* @return an unsigned number corresponding to the number of columns.
*
* On failure, throws an exception or returns zero.
*/
async get_columnCount() {
if (this._nCols != 0) {
return this._nCols;
}
await this.loadStream();
return this._nCols;
}
/**
* Returns the title (or meaning) of each data column present in this stream.
* In most case, the title of the data column is the hardware identifier
* of the sensor that produced the data. For streams recorded at a lower
* recording rate, the dataLogger stores the min, average and max value
* during each measure interval into three columns with suffixes _min,
* _avg and _max respectively.
*
* If the device uses a firmware older than version 13000,
* this method fetches the whole data stream from the device
* if not yet done, which can cause a little delay.
*
* @return a list containing as many strings as there are columns in the
* data stream.
*
* On failure, throws an exception or returns an empty array.
*/
async get_columnNames() {
if (this._columnNames.length != 0) {
return this._columnNames;
}
await this.loadStream();
return this._columnNames;
}
/**
* Returns the smallest measure observed within this stream.
* If the device uses a firmware older than version 13000,
* this method will always return YDataStream.DATA_INVALID.
*
* @return a floating-point number corresponding to the smallest value,
* or YDataStream.DATA_INVALID if the stream is not yet complete (still recording).
*
* On failure, throws an exception or returns YDataStream.DATA_INVALID.
*/
async get_minValue() {
return this._minVal;
}
/**
* Returns the average of all measures observed within this stream.
* If the device uses a firmware older than version 13000,
* this method will always return YDataStream.DATA_INVALID.
*
* @return a floating-point number corresponding to the average value,
* or YDataStream.DATA_INVALID if the stream is not yet complete (still recording).
*
* On failure, throws an exception or returns YDataStream.DATA_INVALID.
*/
async get_averageValue() {
return this._avgVal;
}
/**
* Returns the largest measure observed within this stream.
* If the device uses a firmware older than version 13000,
* this method will always return YDataStream.DATA_INVALID.
*
* @return a floating-point number corresponding to the largest value,
* or YDataStream.DATA_INVALID if the stream is not yet complete (still recording).
*
* On failure, throws an exception or returns YDataStream.DATA_INVALID.
*/
async get_maxValue() {
return this._maxVal;
}
async get_realDuration() {
if (this._isClosed) {
return this._duration;
}
return ((Date.now() / 1000) >> 0) - this._utcStamp;
}
/**
* Returns the whole data set contained in the stream, as a bidimensional
* table of numbers.
* The meaning of the values present in each column can be obtained
* using the method get_columnNames().
*
* This method fetches the whole data stream from the device,
* if not yet done.
*
* @return a list containing as many elements as there are rows in the
* data stream. Each row itself is a list of floating-point
* numbers.
*
* On failure, throws an exception or returns an empty array.
*/
async get_dataRows() {
if ((this._values.length == 0) || !(this._isClosed)) {
await this.loadStream();
}
return this._values;