thingsboard_api
Version:
Thingsboard REST API implementation
415 lines (383 loc) • 14.7 kB
JavaScript
const fetch = require('node-fetch');
const axios = require('axios');
const get = require('./get.js');
const possibleEntityTypes = {
device: 'DEVICE',
asset: 'ASSET',
view: 'ENTITY_VIEW',
};
/**
* @param {String} customType Such as Mobile, PC, not asset/device
* @param {String} entityType asset/device/entity_vew
* @param {String} attributes If exist - push to new object
* @param {String} parentName for entity_view - required, for other - "inheritance"
* @param {String} parentType for entity_view - required(asset,device), for other - "inheritance"
* @param {String} parentKeys for entity_view - required(server_scope keys only), for other - "inheritance"(override) if null - try to get all parent's keys
* @param {String} parentRelation boolean - calls createRelation
*/
async function createEntity(name, customType, attributes = null, entityType, parentName, parentType, parentKeys = null, parentRelation = null) {
// check only two params name or type
if (((typeof name === 'undefined') || (name === null)) || ((typeof customType === 'undefined') || (customType === null))) {
return { error: true, message: 'createEntity(), Error: name or type are not defined or null' };
}
if (parentKeys != null && !Array.isArray(parentKeys)) {
if (parentKeys.indexOf(',') !== -1) {
parentKeys = parentKeys.split(',');
} else {
parentKeys = [parentKeys];
}
}
if (parentKeys !== false && parentKeys !== null) {
const parentAttributes = await get.objectIDandKeys(parentName, parentType);
if (parentAttributes.error) {
return { error: true, message: parentAttributes.message };
}
for (const key in parentAttributes) {
if (key === 'id' || key === 'name' || key === 'type') {
continue;
}
attributes[key] = parentAttributes[key];
}
}
let url = '';
switch (entityType.toUpperCase()) {
case possibleEntityTypes.device:
url = `http://${process.env.TB_HOST}:${process.env.TB_PORT}/api/device`;
break;
case possibleEntityTypes.asset:
url = `http://${process.env.TB_HOST}:${process.env.TB_PORT}/api/asset`;
break;
case possibleEntityTypes.view: {
const ans = await createEntityView(name, customType, attributes, parentName, parentType, parentKeys, parentRelation);
return ans;
}
default:
return { error: true, message: `Get unknown entityType: ${entityType}` };
}
const body = {
name: name,
type: customType,
};
try {
const post = await fetch(url, {
method: 'post',
body: JSON.stringify(body),
headers: {
'Content-Type': 'application/json',
'X-Authorization': `Bearer ${process.env[tokenRand+'TB_TOKEN']}`,
},
});
const response = await post.json();
if (typeof response === 'undefined') {
return { error: true, message: 'Create entity response is not defined or null' };
}
if (response.status === 400) {
return { error: true, message: `Create entity error: ${response.message} ` };
}
if (attributes == null && (!parentRelation || parentRelation == null)) {
return response.id.id;
}
// Далее идём только если нужно пушить аттрибуты или создавать отношение
const { id } = response.id;
let statusRelation = false;
let statusAttributes = false;
if (parentRelation) statusRelation = await createRelation(name, entityType, parentName, parentType);
if (attributes != null || parentKeys != null) {
statusAttributes = await pushAttributes(name, entityType, attributes);
}
const answer = {
id,
statusAttributes,
statusRelation,
};
return answer;
} catch (err) {
return { error: true, message: `createEntity(), While creating entity error: ${err}` };
}
}
/**
* @param {String} type Such as Mobile, PC, not asset/device
* @param {String} attributes If exist - push to new object
* @param {String} parentName for entity_view - required, for other - "inheritance"
* @param {String} parentType for entity_view - required(asset,device), for other - "inheritance"
* @param {String} parentKeys for entity_view - required(server_scope keys only), for other - "inheritance"(override) if null - try to get all parent's keys
* @param {String} parentRelation boolean - calls createRelation
*/
async function createEntityView(name, type, attributes = null, parentName, parentType, parentKeys, parentRelation = false) {
// check only two params name or type! Maybe extends in future!
if (((typeof name === 'undefined') || (name === null)) || ((typeof type === 'undefined') || (type === null))
|| ((typeof parentName === 'undefined') || (parentName === null)) || ((typeof parentType === 'undefined') || (parentType === null))) {
return { error: true, message: 'createEntityView(), Error: name or type; or parentName or parentType are not defined or null' };
}
parentType = parentType.toUpperCase();
const url = `http://${process.env.TB_HOST}:${process.env.TB_PORT}/api/entityView`;
let body = {};
try {
body = {
keys: {
timeseries: [],
attributes: {
ss: parentKeys, // Какие аттрибуты берем
cs: [],
sh: [],
},
},
endTimeMs: 0,
startTimeMs: 0,
name, // Имя представления
type, // Тип представления
entityId: {
entityType: parentType, // от какого типа берем аттрибуты ASSET/DEVICE
id: await get.objectID(parentName, parentType), // От кого берем аттрибуты
},
};
} catch (err) {
return { error: true, message: `createEntityView(), Error: ${err}` };
}
try {
const response = await axios(url, {
method: 'post',
data: JSON.stringify(body),
headers: {
'Content-Type': 'application/json',
'X-Authorization': `Bearer ${process.env[tokenRand+'TB_TOKEN']}`,
},
});
// new entity_view id
const payload = response.data.id.id;
if (payload.length === 0) {
return { error: true, message: 'createEntityView(), Error while creating new entity_view! ' };
}
if (attributes == null && (!parentRelation || parentRelation == null)) {
return payload;
}
// Далее идём только если нужно пушить аттрибуты или создавать отношение
const id = payload;
let statusRelation = false;
let statusAttributes = false;
if (parentRelation) {
statusRelation = await createRelation(name, 'entity_view', parentName, parentType);
// error while creating relations!
if (statusRelation.error) {
return { error: true, message: statusRelation.message };
}
}
if (attributes !== null) {
statusAttributes = await pushAttributes(name, 'entity_view', attributes);
// error while pushing attributes!
if (statusAttributes.error) {
return { error: true, message: statusAttributes.message };
}
}
const answer = {
id,
statusAttributes,
statusRelation,
};
return answer;
} catch (err) {
return { error: true, message: `createEntityView(), Error: ${err}` };
}
}
async function createRelation(name, entity_type, parentName, parentType,id=null,parentId=null) {
//if (((typeof name === 'undefined') || (name === null)) || ((typeof entity_type === 'undefined') || (entity_type === null))
// || ((typeof parentName === 'undefined') || (parentName === null)) || ((typeof parentType === 'undefined') || (parentType === null))) {
// return { error: true, message: 'createRelation(), Error: name or type; or parentName or parentType are not defined or null' };
// }
// to - child, from - parent
const url = `http://${process.env.TB_HOST}:${process.env.TB_PORT}/api/relation`;
let body = {};
if(id === null)
id = await get.objectID(name, entity_type)
if(parentId === null)
parentId = await get.objectID(parentName, parentType)
try {
body = {
to: {
entityType: entity_type.toUpperCase(),
id: id,
},
from: {
entityType: parentType.toUpperCase(),
id: parentId,
},
type: 'Contains',
};
} catch (err) {
return { error: true, message: `createRelation(), Error: ${err}` };
}
if (JSON.stringify(body) === '{}') {
return { error: true, message: 'createRelation(), Error: empty request object for relation creation!' };
}
try {
const response = await axios(url, {
method: 'post',
data: JSON.stringify(body),
headers: {
'Content-Type': 'application/json',
'X-Authorization': `Bearer ${process.env[tokenRand+'TB_TOKEN']}`,
},
});
if (response.data === '') {
return { success: true };
}
} catch (err) {
return { error: true, message: `createRelation(), Error: ${err}` };
}
}
async function pushAttributes(name = null, entityType = null, attributes = null, telemetry = null, ts = null, deviceToken = null, objectId = null) {
// Push telemetry by token
// Works only for devices!
if (deviceToken != null && entityType.toUpperCase() === 'DEVICE') {
const url = `http://${process.env.TB_HOST}:${process.env.TB_PORT}/api/v1/${deviceToken}/telemetry`;
const payload = {
method: 'post',
url,
data: JSON.stringify({ ts: ts || Date.now(), values: telemetry }),
headers: {
'Content-type': 'application/json',
Accept: 'application/json',
},
};
try {
const response = await axios(payload);
if (response.status === 200) {
return { success: true };
}
} catch (err) {
return { error: true, message: `pushAttributes(), Push telemetry by token error: ${err}` };
}
}
let entityId = objectId;
if (entityId === null || typeof entityId === 'undefined') {
entityId = await get.objectID(name, entityType);
if (entityId.error) {
return { error: true, message: `Error while getting entity id using name ${name}. Error: ${entityId.message}` };
}
}
let attributesStatus = false
if ((typeof attributes !== 'undefined') && (attributes !== null)) {
const url = `http://${process.env.TB_HOST}:${process.env.TB_PORT}/api/plugins/telemetry/${entityType.toUpperCase()}/${entityId}/attributes/SERVER_SCOPE`;
try {
await axios(url, {
method: 'post',
data: JSON.stringify(attributes),
headers: {
'Content-Type': 'application/json',
'X-Authorization': `Bearer ${process.env[tokenRand+'TB_TOKEN']}`,
},
});
attributesStatus = true
} catch (err) {
return { error: true, message: `Push attributes failed! Error: ${err}` };
}
}else
attributesStatus = true
let telemetryStatus = false
if ((telemetry !== null || typeof telemetry !== 'undefined')) {
let url = '';
let payload = {};
switch (entityType.toUpperCase()) {
case possibleEntityTypes.asset: {
url = `http://${process.env.TB_HOST}:${process.env.TB_PORT}/api/plugins/telemetry/${entityType.toUpperCase()}/${entityId}/timeseries/SERVER_SCOPE`;
payload = {
method: 'post',
url,
data: JSON.stringify({ ts: ts || Date.now(), values: telemetry }),
headers: {
'Content-type': 'application/json',
'X-Authorization': `Bearer ${process.env[tokenRand+'TB_TOKEN']}`,
},
};
break;
}
case possibleEntityTypes.device: {
const token = await get.getDeviceToken(entityId);
// If error happened, getDeviceToken() return .error
if (token.error) {
return { error: true, message: deviceToken.message };
}
url = `http://${process.env.TB_HOST}:${process.env.TB_PORT}/api/v1/${token}/telemetry`;
payload = {
method: 'post',
url,
data: JSON.stringify({ ts: ts || Date.now(), values: telemetry }),
headers: {
'Content-type': 'application/json',
Accept: 'application/json',
},
};
break;
}
default:
return { error: true, message: `Get unknown TB type ${entityType} !` };
}
try {
const response = await axios(payload);
if (response.status === 200) {
telemetryStatus = true
}
} catch (err) {
return { error: true, message: `pushAttributes(), Error: ${err}` };
}
}
else
telemetryStatus = true
if(telemetryStatus && attributesStatus)
return {result: true}
}
async function pushAlarm(entityName, entityType, alarmDetails, alarmType, ts, tenantId, entityId = null) {
if ((typeof entityName === 'undefined' || entityName === null) || (typeof entityType === 'undefined' || entityType === null)) {
return { error: true, message: 'Error: entityName or entityType are not defined or null!' };
}
if (entityId === null) {
entityId = await get.objectID(entityName, entityType);
}
const logAttrs = {
entityId: {
entityType: entityType.toUpperCase(),
id: {
id: new Date().getTime(),
},
},
originator: {
entityType: entityType.toUpperCase(),
id: entityId,
},
tenantId: {
entityType: 'TENANT',
id: tenantId,
},
severity: 'MINOR',
type: `${alarmType} hide: ${new Date().getTime()}`,
ackTime: `hide: ${new Date(ts).getTime()}`,
status: 'ACTIVE_UNACK',
createdTime: new Date(ts).getTime(),
details: alarmDetails,
};
const urlLog = `http://${process.env.TB_HOST}:${process.env.TB_PORT}/api/alarm`;
try {
let ans = await fetch(urlLog, {
method: 'post',
body: JSON.stringify(logAttrs),
headers: {
'Content-Type': 'application/json',
'X-Authorization': `Bearer ${process.env[tokenRand+'TB_TOKEN']}`,
},
});
ans = await ans.json();
return {
entityId: ans.originator.id,
alarmId: ans.id.id,
};
} catch (err) {
return { error: true, message: `pushAlarm(), Error: ${err}` };
}
}
module.exports = {
createRelation,
pushAttributes,
createEntityView,
createEntity,
pushAlarm,
};