624 lines
24 KiB
JavaScript
624 lines
24 KiB
JavaScript
/*
|
|
* Copyright © 2016-2017 The Thingsboard Authors
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
import thingsboardTypes from '../common/types.constant';
|
|
|
|
export default angular.module('thingsboard.api.device', [thingsboardTypes])
|
|
.factory('deviceService', DeviceService)
|
|
.name;
|
|
|
|
/*@ngInject*/
|
|
function DeviceService($http, $q, $filter, userService, customerService, telemetryWebsocketService, types) {
|
|
|
|
|
|
var deviceAttributesSubscriptionMap = {};
|
|
|
|
var service = {
|
|
assignDeviceToCustomer: assignDeviceToCustomer,
|
|
deleteDevice: deleteDevice,
|
|
getCustomerDevices: getCustomerDevices,
|
|
getDevice: getDevice,
|
|
getDevices: getDevices,
|
|
processDeviceAliases: processDeviceAliases,
|
|
checkDeviceAlias: checkDeviceAlias,
|
|
fetchAliasDeviceByNameFilter: fetchAliasDeviceByNameFilter,
|
|
getDeviceCredentials: getDeviceCredentials,
|
|
getDeviceKeys: getDeviceKeys,
|
|
getDeviceTimeseriesValues: getDeviceTimeseriesValues,
|
|
getTenantDevices: getTenantDevices,
|
|
saveDevice: saveDevice,
|
|
saveDeviceCredentials: saveDeviceCredentials,
|
|
unassignDeviceFromCustomer: unassignDeviceFromCustomer,
|
|
makeDevicePublic: makeDevicePublic,
|
|
getDeviceAttributes: getDeviceAttributes,
|
|
subscribeForDeviceAttributes: subscribeForDeviceAttributes,
|
|
unsubscribeForDeviceAttributes: unsubscribeForDeviceAttributes,
|
|
saveDeviceAttributes: saveDeviceAttributes,
|
|
deleteDeviceAttributes: deleteDeviceAttributes,
|
|
sendOneWayRpcCommand: sendOneWayRpcCommand,
|
|
sendTwoWayRpcCommand: sendTwoWayRpcCommand
|
|
}
|
|
|
|
return service;
|
|
|
|
function getTenantDevices(pageLink, applyCustomersInfo, config) {
|
|
var deferred = $q.defer();
|
|
var url = '/api/tenant/devices?limit=' + pageLink.limit;
|
|
if (angular.isDefined(pageLink.textSearch)) {
|
|
url += '&textSearch=' + pageLink.textSearch;
|
|
}
|
|
if (angular.isDefined(pageLink.idOffset)) {
|
|
url += '&idOffset=' + pageLink.idOffset;
|
|
}
|
|
if (angular.isDefined(pageLink.textOffset)) {
|
|
url += '&textOffset=' + pageLink.textOffset;
|
|
}
|
|
$http.get(url, config).then(function success(response) {
|
|
if (applyCustomersInfo) {
|
|
customerService.applyAssignedCustomersInfo(response.data.data).then(
|
|
function success(data) {
|
|
response.data.data = data;
|
|
deferred.resolve(response.data);
|
|
},
|
|
function fail() {
|
|
deferred.reject();
|
|
}
|
|
);
|
|
} else {
|
|
deferred.resolve(response.data);
|
|
}
|
|
}, function fail() {
|
|
deferred.reject();
|
|
});
|
|
return deferred.promise;
|
|
}
|
|
|
|
function getCustomerDevices(customerId, pageLink, applyCustomersInfo, config) {
|
|
var deferred = $q.defer();
|
|
var url = '/api/customer/' + customerId + '/devices?limit=' + pageLink.limit;
|
|
if (angular.isDefined(pageLink.textSearch)) {
|
|
url += '&textSearch=' + pageLink.textSearch;
|
|
}
|
|
if (angular.isDefined(pageLink.idOffset)) {
|
|
url += '&idOffset=' + pageLink.idOffset;
|
|
}
|
|
if (angular.isDefined(pageLink.textOffset)) {
|
|
url += '&textOffset=' + pageLink.textOffset;
|
|
}
|
|
$http.get(url, config).then(function success(response) {
|
|
if (applyCustomersInfo) {
|
|
customerService.applyAssignedCustomerInfo(response.data.data, customerId).then(
|
|
function success(data) {
|
|
response.data.data = data;
|
|
deferred.resolve(response.data);
|
|
},
|
|
function fail() {
|
|
deferred.reject();
|
|
}
|
|
);
|
|
} else {
|
|
deferred.resolve(response.data);
|
|
}
|
|
}, function fail() {
|
|
deferred.reject();
|
|
});
|
|
|
|
return deferred.promise;
|
|
}
|
|
|
|
function getDevice(deviceId, ignoreErrors, config) {
|
|
var deferred = $q.defer();
|
|
var url = '/api/device/' + deviceId;
|
|
if (!config) {
|
|
config = {};
|
|
}
|
|
config = Object.assign(config, { ignoreErrors: ignoreErrors });
|
|
$http.get(url, config).then(function success(response) {
|
|
deferred.resolve(response.data);
|
|
}, function fail(response) {
|
|
deferred.reject(response.data);
|
|
});
|
|
return deferred.promise;
|
|
}
|
|
|
|
function getDevices(deviceIds, config) {
|
|
var deferred = $q.defer();
|
|
var ids = '';
|
|
for (var i=0;i<deviceIds.length;i++) {
|
|
if (i>0) {
|
|
ids += ',';
|
|
}
|
|
ids += deviceIds[i];
|
|
}
|
|
var url = '/api/devices?deviceIds=' + ids;
|
|
$http.get(url, config).then(function success(response) {
|
|
var devices = response.data;
|
|
devices.sort(function (device1, device2) {
|
|
var id1 = device1.id.id;
|
|
var id2 = device2.id.id;
|
|
var index1 = deviceIds.indexOf(id1);
|
|
var index2 = deviceIds.indexOf(id2);
|
|
return index1 - index2;
|
|
});
|
|
deferred.resolve(devices);
|
|
}, function fail(response) {
|
|
deferred.reject(response.data);
|
|
});
|
|
return deferred.promise;
|
|
}
|
|
|
|
function fetchAliasDeviceByNameFilter(deviceNameFilter, limit, applyCustomersInfo, config) {
|
|
var deferred = $q.defer();
|
|
var user = userService.getCurrentUser();
|
|
var promise;
|
|
var pageLink = {limit: limit, textSearch: deviceNameFilter};
|
|
if (user.authority === 'CUSTOMER_USER') {
|
|
var customerId = user.customerId;
|
|
promise = getCustomerDevices(customerId, pageLink, applyCustomersInfo, config);
|
|
} else {
|
|
promise = getTenantDevices(pageLink, applyCustomersInfo, config);
|
|
}
|
|
promise.then(
|
|
function success(result) {
|
|
if (result.data && result.data.length > 0) {
|
|
deferred.resolve(result.data);
|
|
} else {
|
|
deferred.resolve(null);
|
|
}
|
|
},
|
|
function fail() {
|
|
deferred.resolve(null);
|
|
}
|
|
);
|
|
return deferred.promise;
|
|
}
|
|
|
|
function deviceToDeviceInfo(device) {
|
|
return { name: device.name, id: device.id.id };
|
|
}
|
|
|
|
function devicesToDevicesInfo(devices) {
|
|
var devicesInfo = [];
|
|
for (var d = 0; d < devices.length; d++) {
|
|
devicesInfo.push(deviceToDeviceInfo(devices[d]));
|
|
}
|
|
return devicesInfo;
|
|
}
|
|
|
|
function processDeviceAlias(index, aliasIds, deviceAliases, resolution, deferred) {
|
|
if (index < aliasIds.length) {
|
|
var aliasId = aliasIds[index];
|
|
var deviceAlias = deviceAliases[aliasId];
|
|
var alias = deviceAlias.alias;
|
|
if (!deviceAlias.deviceFilter) {
|
|
getDevice(deviceAlias.deviceId).then(
|
|
function success(device) {
|
|
var resolvedAlias = {alias: alias, deviceId: device.id.id};
|
|
resolution.aliasesInfo.deviceAliases[aliasId] = resolvedAlias;
|
|
resolution.aliasesInfo.deviceAliasesInfo[aliasId] = [
|
|
deviceToDeviceInfo(device)
|
|
];
|
|
index++;
|
|
processDeviceAlias(index, aliasIds, deviceAliases, resolution, deferred);
|
|
},
|
|
function fail() {
|
|
if (!resolution.error) {
|
|
resolution.error = 'dashboard.invalid-aliases-config';
|
|
}
|
|
index++;
|
|
processDeviceAlias(index, aliasIds, deviceAliases, resolution, deferred);
|
|
}
|
|
);
|
|
} else {
|
|
var deviceFilter = deviceAlias.deviceFilter;
|
|
if (deviceFilter.useFilter) {
|
|
var deviceNameFilter = deviceFilter.deviceNameFilter;
|
|
fetchAliasDeviceByNameFilter(deviceNameFilter, 100, false).then(
|
|
function(devices) {
|
|
if (devices && devices != null) {
|
|
var resolvedAlias = {alias: alias, deviceId: devices[0].id.id};
|
|
resolution.aliasesInfo.deviceAliases[aliasId] = resolvedAlias;
|
|
resolution.aliasesInfo.deviceAliasesInfo[aliasId] = devicesToDevicesInfo(devices);
|
|
index++;
|
|
processDeviceAlias(index, aliasIds, deviceAliases, resolution, deferred);
|
|
} else {
|
|
if (!resolution.error) {
|
|
resolution.error = 'dashboard.invalid-aliases-config';
|
|
}
|
|
index++;
|
|
processDeviceAlias(index, aliasIds, deviceAliases, resolution, deferred);
|
|
}
|
|
});
|
|
} else {
|
|
var deviceList = deviceFilter.deviceList;
|
|
getDevices(deviceList).then(
|
|
function success(devices) {
|
|
if (devices && devices.length > 0) {
|
|
var resolvedAlias = {alias: alias, deviceId: devices[0].id.id};
|
|
resolution.aliasesInfo.deviceAliases[aliasId] = resolvedAlias;
|
|
resolution.aliasesInfo.deviceAliasesInfo[aliasId] = devicesToDevicesInfo(devices);
|
|
index++;
|
|
processDeviceAlias(index, aliasIds, deviceAliases, resolution, deferred);
|
|
} else {
|
|
if (!resolution.error) {
|
|
resolution.error = 'dashboard.invalid-aliases-config';
|
|
}
|
|
index++;
|
|
processDeviceAlias(index, aliasIds, deviceAliases, resolution, deferred);
|
|
}
|
|
},
|
|
function fail() {
|
|
if (!resolution.error) {
|
|
resolution.error = 'dashboard.invalid-aliases-config';
|
|
}
|
|
index++;
|
|
processDeviceAlias(index, aliasIds, deviceAliases, resolution, deferred);
|
|
}
|
|
);
|
|
}
|
|
}
|
|
} else {
|
|
deferred.resolve(resolution);
|
|
}
|
|
}
|
|
|
|
function processDeviceAliases(deviceAliases) {
|
|
var deferred = $q.defer();
|
|
var resolution = {
|
|
aliasesInfo: {
|
|
deviceAliases: {},
|
|
deviceAliasesInfo: {}
|
|
}
|
|
};
|
|
var aliasIds = [];
|
|
if (deviceAliases) {
|
|
for (var aliasId in deviceAliases) {
|
|
aliasIds.push(aliasId);
|
|
}
|
|
}
|
|
processDeviceAlias(0, aliasIds, deviceAliases, resolution, deferred);
|
|
return deferred.promise;
|
|
}
|
|
|
|
function checkDeviceAlias(deviceAlias) {
|
|
var deferred = $q.defer();
|
|
var deviceFilter;
|
|
if (deviceAlias.deviceId) {
|
|
deviceFilter = {
|
|
useFilter: false,
|
|
deviceNameFilter: '',
|
|
deviceList: [deviceAlias.deviceId]
|
|
}
|
|
} else {
|
|
deviceFilter = deviceAlias.deviceFilter;
|
|
}
|
|
var promise;
|
|
if (deviceFilter.useFilter) {
|
|
var deviceNameFilter = deviceFilter.deviceNameFilter;
|
|
promise = fetchAliasDeviceByNameFilter(deviceNameFilter, 1, false);
|
|
} else {
|
|
var deviceList = deviceFilter.deviceList;
|
|
promise = getDevices(deviceList);
|
|
}
|
|
promise.then(
|
|
function success(devices) {
|
|
if (devices && devices.length > 0) {
|
|
deferred.resolve(true);
|
|
} else {
|
|
deferred.resolve(false);
|
|
}
|
|
},
|
|
function fail() {
|
|
deferred.resolve(false);
|
|
}
|
|
);
|
|
return deferred.promise;
|
|
}
|
|
|
|
function saveDevice(device) {
|
|
var deferred = $q.defer();
|
|
var url = '/api/device';
|
|
$http.post(url, device).then(function success(response) {
|
|
deferred.resolve(response.data);
|
|
}, function fail() {
|
|
deferred.reject();
|
|
});
|
|
return deferred.promise;
|
|
}
|
|
|
|
function deleteDevice(deviceId) {
|
|
var deferred = $q.defer();
|
|
var url = '/api/device/' + deviceId;
|
|
$http.delete(url).then(function success() {
|
|
deferred.resolve();
|
|
}, function fail() {
|
|
deferred.reject();
|
|
});
|
|
return deferred.promise;
|
|
}
|
|
|
|
function getDeviceCredentials(deviceId) {
|
|
var deferred = $q.defer();
|
|
var url = '/api/device/' + deviceId + '/credentials';
|
|
$http.get(url, null).then(function success(response) {
|
|
deferred.resolve(response.data);
|
|
}, function fail() {
|
|
deferred.reject();
|
|
});
|
|
return deferred.promise;
|
|
}
|
|
|
|
function saveDeviceCredentials(deviceCredentials) {
|
|
var deferred = $q.defer();
|
|
var url = '/api/device/credentials';
|
|
$http.post(url, deviceCredentials).then(function success(response) {
|
|
deferred.resolve(response.data);
|
|
}, function fail() {
|
|
deferred.reject();
|
|
});
|
|
return deferred.promise;
|
|
}
|
|
|
|
function assignDeviceToCustomer(customerId, deviceId) {
|
|
var deferred = $q.defer();
|
|
var url = '/api/customer/' + customerId + '/device/' + deviceId;
|
|
$http.post(url, null).then(function success(response) {
|
|
deferred.resolve(response.data);
|
|
}, function fail() {
|
|
deferred.reject();
|
|
});
|
|
return deferred.promise;
|
|
}
|
|
|
|
function unassignDeviceFromCustomer(deviceId) {
|
|
var deferred = $q.defer();
|
|
var url = '/api/customer/device/' + deviceId;
|
|
$http.delete(url).then(function success(response) {
|
|
deferred.resolve(response.data);
|
|
}, function fail() {
|
|
deferred.reject();
|
|
});
|
|
return deferred.promise;
|
|
}
|
|
|
|
function makeDevicePublic(deviceId) {
|
|
var deferred = $q.defer();
|
|
var url = '/api/customer/public/device/' + deviceId;
|
|
$http.post(url, null).then(function success(response) {
|
|
deferred.resolve(response.data);
|
|
}, function fail() {
|
|
deferred.reject();
|
|
});
|
|
return deferred.promise;
|
|
}
|
|
|
|
function getDeviceKeys(deviceId, query, type) {
|
|
var deferred = $q.defer();
|
|
var url = '/api/plugins/telemetry/' + deviceId + '/keys/';
|
|
if (type === types.dataKeyType.timeseries) {
|
|
url += 'timeseries';
|
|
} else if (type === types.dataKeyType.attribute) {
|
|
url += 'attributes';
|
|
}
|
|
$http.get(url, null).then(function success(response) {
|
|
var result = [];
|
|
if (response.data) {
|
|
if (query) {
|
|
var dataKeys = response.data;
|
|
var lowercaseQuery = angular.lowercase(query);
|
|
for (var i=0; i<dataKeys.length;i++) {
|
|
if (angular.lowercase(dataKeys[i]).indexOf(lowercaseQuery) === 0) {
|
|
result.push(dataKeys[i]);
|
|
}
|
|
}
|
|
} else {
|
|
result = response.data;
|
|
}
|
|
}
|
|
deferred.resolve(result);
|
|
}, function fail(response) {
|
|
deferred.reject(response.data);
|
|
});
|
|
return deferred.promise;
|
|
}
|
|
|
|
function getDeviceTimeseriesValues(deviceId, keys, startTs, endTs, limit) {
|
|
var deferred = $q.defer();
|
|
var url = '/api/plugins/telemetry/' + deviceId + '/values/timeseries';
|
|
url += '?keys=' + keys;
|
|
url += '&startTs=' + startTs;
|
|
url += '&endTs=' + endTs;
|
|
if (angular.isDefined(limit)) {
|
|
url += '&limit=' + limit;
|
|
}
|
|
$http.get(url, null).then(function success(response) {
|
|
deferred.resolve(response.data);
|
|
}, function fail(response) {
|
|
deferred.reject(response.data);
|
|
});
|
|
return deferred.promise;
|
|
}
|
|
|
|
function processDeviceAttributes(attributes, query, deferred, successCallback, update, apply) {
|
|
attributes = $filter('orderBy')(attributes, query.order);
|
|
if (query.search != null) {
|
|
attributes = $filter('filter')(attributes, {key: query.search});
|
|
}
|
|
var responseData = {
|
|
count: attributes.length
|
|
}
|
|
var startIndex = query.limit * (query.page - 1);
|
|
responseData.data = attributes.slice(startIndex, startIndex + query.limit);
|
|
successCallback(responseData, update, apply);
|
|
if (deferred) {
|
|
deferred.resolve();
|
|
}
|
|
}
|
|
|
|
function getDeviceAttributes(deviceId, attributeScope, query, successCallback, config) {
|
|
var deferred = $q.defer();
|
|
var subscriptionId = deviceId + attributeScope;
|
|
var das = deviceAttributesSubscriptionMap[subscriptionId];
|
|
if (das) {
|
|
if (das.attributes) {
|
|
processDeviceAttributes(das.attributes, query, deferred, successCallback);
|
|
das.subscriptionCallback = function(attributes) {
|
|
processDeviceAttributes(attributes, query, null, successCallback, true, true);
|
|
}
|
|
} else {
|
|
das.subscriptionCallback = function(attributes) {
|
|
processDeviceAttributes(attributes, query, deferred, successCallback, false, true);
|
|
das.subscriptionCallback = function(attributes) {
|
|
processDeviceAttributes(attributes, query, null, successCallback, true, true);
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
var url = '/api/plugins/telemetry/' + deviceId + '/values/attributes/' + attributeScope;
|
|
$http.get(url, config).then(function success(response) {
|
|
processDeviceAttributes(response.data, query, deferred, successCallback);
|
|
}, function fail() {
|
|
deferred.reject();
|
|
});
|
|
}
|
|
return deferred;
|
|
}
|
|
|
|
function onSubscriptionData(data, subscriptionId) {
|
|
var deviceAttributesSubscription = deviceAttributesSubscriptionMap[subscriptionId];
|
|
if (deviceAttributesSubscription) {
|
|
if (!deviceAttributesSubscription.attributes) {
|
|
deviceAttributesSubscription.attributes = [];
|
|
deviceAttributesSubscription.keys = {};
|
|
}
|
|
var attributes = deviceAttributesSubscription.attributes;
|
|
var keys = deviceAttributesSubscription.keys;
|
|
for (var key in data) {
|
|
var index = keys[key];
|
|
var attribute;
|
|
if (index > -1) {
|
|
attribute = attributes[index];
|
|
} else {
|
|
attribute = {
|
|
key: key
|
|
};
|
|
index = attributes.push(attribute)-1;
|
|
keys[key] = index;
|
|
}
|
|
var attrData = data[key][0];
|
|
attribute.lastUpdateTs = attrData[0];
|
|
attribute.value = attrData[1];
|
|
}
|
|
if (deviceAttributesSubscription.subscriptionCallback) {
|
|
deviceAttributesSubscription.subscriptionCallback(attributes);
|
|
}
|
|
}
|
|
}
|
|
|
|
function subscribeForDeviceAttributes(deviceId, attributeScope) {
|
|
var subscriptionId = deviceId + attributeScope;
|
|
var deviceAttributesSubscription = deviceAttributesSubscriptionMap[subscriptionId];
|
|
if (!deviceAttributesSubscription) {
|
|
var subscriptionCommand = {
|
|
deviceId: deviceId,
|
|
scope: attributeScope
|
|
};
|
|
|
|
var type = attributeScope === types.latestTelemetry.value ?
|
|
types.dataKeyType.timeseries : types.dataKeyType.attribute;
|
|
|
|
var subscriber = {
|
|
subscriptionCommand: subscriptionCommand,
|
|
type: type,
|
|
onData: function (data) {
|
|
if (data.data) {
|
|
onSubscriptionData(data.data, subscriptionId);
|
|
}
|
|
}
|
|
};
|
|
deviceAttributesSubscription = {
|
|
subscriber: subscriber,
|
|
attributes: null
|
|
}
|
|
deviceAttributesSubscriptionMap[subscriptionId] = deviceAttributesSubscription;
|
|
telemetryWebsocketService.subscribe(subscriber);
|
|
}
|
|
return subscriptionId;
|
|
}
|
|
function unsubscribeForDeviceAttributes(subscriptionId) {
|
|
var deviceAttributesSubscription = deviceAttributesSubscriptionMap[subscriptionId];
|
|
if (deviceAttributesSubscription) {
|
|
telemetryWebsocketService.unsubscribe(deviceAttributesSubscription.subscriber);
|
|
delete deviceAttributesSubscriptionMap[subscriptionId];
|
|
}
|
|
}
|
|
|
|
function saveDeviceAttributes(deviceId, attributeScope, attributes) {
|
|
var deferred = $q.defer();
|
|
var attributesData = {};
|
|
for (var a=0; a<attributes.length;a++) {
|
|
attributesData[attributes[a].key] = attributes[a].value;
|
|
}
|
|
var url = '/api/plugins/telemetry/' + deviceId + '/' + attributeScope;
|
|
$http.post(url, attributesData).then(function success(response) {
|
|
deferred.resolve(response.data);
|
|
}, function fail(response) {
|
|
deferred.reject(response.data);
|
|
});
|
|
return deferred.promise;
|
|
}
|
|
|
|
function deleteDeviceAttributes(deviceId, attributeScope, attributes) {
|
|
var deferred = $q.defer();
|
|
var keys = '';
|
|
for (var i = 0; i < attributes.length; i++) {
|
|
if (i > 0) {
|
|
keys += ',';
|
|
}
|
|
keys += attributes[i].key;
|
|
}
|
|
var url = '/api/plugins/telemetry/' + deviceId + '/' + attributeScope + '?keys=' + keys;
|
|
$http.delete(url).then(function success() {
|
|
deferred.resolve();
|
|
}, function fail() {
|
|
deferred.reject();
|
|
});
|
|
return deferred.promise;
|
|
}
|
|
|
|
function sendOneWayRpcCommand(deviceId, requestBody) {
|
|
var deferred = $q.defer();
|
|
var url = '/api/plugins/rpc/oneway/' + deviceId;
|
|
$http.post(url, requestBody).then(function success(response) {
|
|
deferred.resolve(response.data);
|
|
}, function fail(rejection) {
|
|
deferred.reject(rejection);
|
|
});
|
|
return deferred.promise;
|
|
}
|
|
|
|
function sendTwoWayRpcCommand(deviceId, requestBody) {
|
|
var deferred = $q.defer();
|
|
var url = '/api/plugins/rpc/twoway/' + deviceId;
|
|
$http.post(url, requestBody).then(function success(response) {
|
|
deferred.resolve(response.data);
|
|
}, function fail(rejection) {
|
|
deferred.reject(rejection);
|
|
});
|
|
return deferred.promise;
|
|
}
|
|
|
|
}
|