/* * 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, telemetryWebsocketService, types) { var deviceAttributesSubscriptionMap = {}; var service = { assignDeviceToCustomer: assignDeviceToCustomer, deleteDevice: deleteDevice, getCustomerDevices: getCustomerDevices, getDevice: getDevice, getDevices: getDevices, processDeviceAliases: processDeviceAliases, checkDeviceAlias: checkDeviceAlias, getDeviceCredentials: getDeviceCredentials, getDeviceKeys: getDeviceKeys, getDeviceTimeseriesValues: getDeviceTimeseriesValues, getTenantDevices: getTenantDevices, saveDevice: saveDevice, saveDeviceCredentials: saveDeviceCredentials, unassignDeviceFromCustomer: unassignDeviceFromCustomer, getDeviceAttributes: getDeviceAttributes, subscribeForDeviceAttributes: subscribeForDeviceAttributes, unsubscribeForDeviceAttributes: unsubscribeForDeviceAttributes, saveDeviceAttributes: saveDeviceAttributes, deleteDeviceAttributes: deleteDeviceAttributes, sendOneWayRpcCommand: sendOneWayRpcCommand, sendTwoWayRpcCommand: sendTwoWayRpcCommand } return service; function getTenantDevices(pageLink) { 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, null).then(function success(response) { deferred.resolve(response.data); }, function fail() { deferred.reject(); }); return deferred.promise; } function getCustomerDevices(customerId, pageLink) { 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, null).then(function success(response) { deferred.resolve(response.data); }, function fail() { deferred.reject(); }); return deferred.promise; } function getDevice(deviceId, ignoreErrors) { var deferred = $q.defer(); var url = '/api/device/' + deviceId; $http.get(url, { ignoreErrors: ignoreErrors }).then(function success(response) { deferred.resolve(response.data); }, function fail(response) { deferred.reject(response.data); }); return deferred.promise; } function getDevices(deviceIds) { var deferred = $q.defer(); var ids = ''; for (var i=0;i0) { ids += ','; } ids += deviceIds[i]; } var url = '/api/devices?deviceIds=' + ids; $http.get(url, null).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) { 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); } else { promise = getTenantDevices(pageLink); } 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 in devices) { 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).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); } 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(response) { deferred.reject(response.data); }); 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(response) { deferred.reject(response.data); }); 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(response) { deferred.reject(response.data); }); 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(response) { deferred.reject(response.data); }); 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() { deferred.resolve(); }, function fail(response) { deferred.reject(response.data); }); return deferred.promise; } function unassignDeviceFromCustomer(deviceId) { var deferred = $q.defer(); var url = '/api/customer/device/' + deviceId; $http.delete(url).then(function success() { deferred.resolve(); }, function fail(response) { deferred.reject(response.data); }); 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 in dataKeys) { 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) { 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, null).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 in attributes) { 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; } }