UNPKG

balena-sdk

Version:
1,280 lines (1,277 loc) • 105 kB
"use strict"; /* Copyright 2016 Balena 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. */ Object.defineProperty(exports, "__esModule", { value: true }); exports.default = exports.DeviceState = void 0; const tslib_1 = require("tslib"); const device_overall_status_1 = require("../types/device-overall-status"); const url = tslib_1.__importStar(require("url")); const once_1 = tslib_1.__importDefault(require("lodash/once")); const bSemver = tslib_1.__importStar(require("balena-semver")); const errors = tslib_1.__importStar(require("balena-errors")); const memoizee_1 = tslib_1.__importDefault(require("memoizee")); const util_1 = require("../util"); const device_os_version_1 = require("../util/device-os-version"); const device_service_details_1 = require("../util/device-service-details"); const local_mode_1 = require("../util/local-mode"); const device_supervisor_api_partial_1 = require("./device.supervisor-api.partial"); const MIN_OS_MC = '2.12.0'; const OVERRIDE_LOCK_ENV_VAR = 'RESIN_OVERRIDE_LOCK'; exports.DeviceState = tslib_1.__importStar(require("../types/device-state")); const DEFAULT_DAYS_OF_REQUESTED_HISTORY_MS = 7 * 24 * 60 * 60 * 1000; const getDeviceModel = function (deps, opts) { var _a; const { pine, request, // Do not destructure sub-modules, to allow lazy loading only when needed. sdkInstance, } = deps; const { apiUrl, deviceUrlsBase } = opts; /* eslint-disable @typescript-eslint/no-require-imports */ const registerDevice = (0, once_1.default)(() => require('balena-register-device').getRegisterDevice({ request })); const { buildDependentResource } = require('../util/dependent-resource'); const hupActionHelper = (0, once_1.default)(() => require('../util/device-actions/os-update/utils').hupActionHelper); const dateUtils = (0, once_1.default)(() => require('../util/date')); const batchDeviceOperation = (0, once_1.default)(() => require('../util/request-batching').batchResourceOperationFactory({ getAll, NotFoundError: errors.BalenaDeviceNotFound, AmbiguousResourceError: errors.BalenaAmbiguousDevice, chunkSize: opts.requestBatchingChunkSize, })); const getOsUpdateHelper = (0, once_1.default)(async () => { const $deviceUrlsBase = await getDeviceUrlsBase(); const _getOsUpdateHelper = require('../util/device-actions/os-update').getOsUpdateHelper; return _getOsUpdateHelper($deviceUrlsBase, request); }); /* eslint-enable @typescript-eslint/no-require-imports */ const tagsModel = buildDependentResource({ pine }, { resourceName: 'device_tag', resourceKeyField: 'tag_key', parentResourceName: 'device', async getResourceId(uuidOrId) { if (typeof uuidOrId !== 'string' && typeof uuidOrId !== 'number') { throw new Error(`Unexpected type for uuidOrId provided in device tagsModel getResourceId: ${typeof uuidOrId}`); } const { id } = await exports.get(uuidOrId, { $select: 'id' }); return id; }, }); const configVarModel = buildDependentResource({ pine }, { resourceName: 'device_config_variable', resourceKeyField: 'name', parentResourceName: 'device', async getResourceId(uuidOrId) { if (typeof uuidOrId !== 'string' && typeof uuidOrId !== 'number') { throw new Error(`Unexpected type for uuidOrId provided in device configVarModel getResourceId: ${typeof uuidOrId}`); } const { id } = await exports.get(uuidOrId, { $select: 'id' }); return id; }, }); const envVarModel = buildDependentResource({ pine }, { resourceName: 'device_environment_variable', resourceKeyField: 'name', parentResourceName: 'device', async getResourceId(uuidOrId) { if (typeof uuidOrId !== 'string' && typeof uuidOrId !== 'number') { throw new Error(`Unexpected type for uuidOrId provided in device envVarModel getResourceId: ${typeof uuidOrId}`); } const { id } = await exports.get(uuidOrId, { $select: 'id' }); return id; }, }); // Infer dashboardUrl from apiUrl if former is undefined const dashboardUrl = (_a = opts.dashboardUrl) !== null && _a !== void 0 ? _a : apiUrl.replace(/api/, 'dashboard'); const getDeviceUrlsBase = (0, once_1.default)(async function () { if (deviceUrlsBase != null) { return deviceUrlsBase; } return (await sdkInstance.models.config.getAll()).deviceUrlsBase; }); const getAppliedConfigVariableValue = async (uuidOrId, name) => { var _a; const options = { $expand: { device_config_variable: { $select: 'value', $filter: { name, }, }, belongs_to__application: { $select: 'id', $expand: { application_config_variable: { $select: 'value', $filter: { name, }, }, }, }, }, }; const { device_config_variable: [deviceConfig], belongs_to__application: [{ application_config_variable: [appConfig], },], } = await exports.get(uuidOrId, options); return (_a = (deviceConfig !== null && deviceConfig !== void 0 ? deviceConfig : appConfig)) === null || _a === void 0 ? void 0 : _a.value; }; const set = async (uuidOrIdOrArray, body) => { await batchDeviceOperation()({ uuidOrIdOrArray, fn: async (devices) => { await pine.patch({ resource: 'device', options: { $filter: { id: { $in: devices.map((d) => d.id) }, }, }, body, }); }, }); }; const historyTimeRangeFilterWithGuard = (fromDate, toDate) => { let fromDateFilter; let toDateFilter; if (fromDate != null) { if (!(fromDate instanceof Date)) { throw new errors.BalenaInvalidParameterError('fromDate', fromDate); } fromDateFilter = { $ge: fromDate }; } if (toDate != null) { if (!(toDate instanceof Date)) { throw new errors.BalenaInvalidParameterError('toDate', toDate); } toDateFilter = { $le: toDate }; } const timeRangeFilter = fromDateFilter || toDateFilter ? { created_at: { ...fromDateFilter, ...toDateFilter } } : {}; return timeRangeFilter; }; async function getAll(options) { return await pine.get({ resource: 'device', options: (0, util_1.mergePineOptions)({ $orderby: { device_name: 'asc' } }, options), }); } async function startOsUpdate(uuidOrUuids, targetOsVersion, options = { runDetached: true }) { var _a, _b; if (!targetOsVersion) { throw new errors.BalenaInvalidParameterError('targetOsVersion', targetOsVersion); } const isDraft = ((_b = (_a = bSemver.parse(targetOsVersion)) === null || _a === void 0 ? void 0 : _a.prerelease.length) !== null && _b !== void 0 ? _b : 0) > 0; const getDeviceType = (0, memoizee_1.default)(async (deviceTypeId) => await sdkInstance.models.deviceType.get(deviceTypeId, { $select: 'slug', }), { primitive: true, promise: true }); const getAvailableOsVersions = (0, memoizee_1.default)(async (slug, includeDraft) => await sdkInstance.models.os.getAvailableOsVersions(slug, { includeDraft, }), { primitive: true, promise: true }); const osUpdateHelper = await getOsUpdateHelper(); const results = {}; await batchDeviceOperation()({ uuidOrIdOrArray: uuidOrUuids, options: { $select: [ 'uuid', 'is_online', 'os_version', 'supervisor_version', 'os_variant', ], }, groupByNavigationPoperty: 'is_of__device_type', fn: async (devices, deviceTypeId) => { const dt = await getDeviceType(deviceTypeId); for (const device of devices) { // this will throw an error if the action isn't available exports._checkOsUpdateTarget({ ...device, is_of__device_type: [dt], }, targetOsVersion); const osVersions = await getAvailableOsVersions(dt.slug, isDraft); if (!osVersions.some((v) => bSemver.compare(v.raw_version, targetOsVersion) === 0)) { throw new errors.BalenaInvalidParameterError('targetOsVersion', targetOsVersion); } } // use the v2 device actions api for detached updates await (0, util_1.limitedMap)(devices, async (device) => { results[device.uuid] = await osUpdateHelper.startOsUpdate(device.uuid, targetOsVersion, options.runDetached === true ? 'v2' : 'v1'); }); }, }); if (Array.isArray(uuidOrUuids)) { return results; } return results[uuidOrUuids]; } const exports = { OverallStatus: device_overall_status_1.DeviceOverallStatus, /** * @summary Get Dashboard URL for a specific device * @function getDashboardUrl * @memberof balena.models.device * * @param {String} uuid - Device uuid * * @returns {String} - Dashboard URL for the specific device * @throws Exception if the uuid is empty * * @example * dashboardDeviceUrl = balena.models.device.getDashboardUrl('a44b544b8cc24d11b036c659dfeaccd8') */ getDashboardUrl(uuid) { if (typeof uuid !== 'string' || uuid.length === 0) { throw new Error('The uuid option should be a non empty string'); } return url.resolve(dashboardUrl, `/devices/${uuid}/summary`); }, /** * @summary Get all devices by application * @name getAllByApplication * @public * @function * @memberof balena.models.device * * @description * This method returns all devices of a specific application. * In order to have the following computed properties in the result * you have to explicitly define them in a `$select` in the extra options: * * `overall_status` * * `overall_progress` * * `should_be_running__release` * * @param {String|Number} slugOrUuidOrId - application slug (string), uuid (string) or id (number) * @param {Object} [options={}] - extra pine options to use * @fulfil {Object[]} - devices * @returns {Promise} * * @example * balena.models.device.getAllByApplication('myorganization/myapp').then(function(devices) { * console.log(devices); * }); * * @example * balena.models.device.getAllByApplication(123).then(function(devices) { * console.log(devices); * }); * * @example * balena.models.device.getAllByApplication('myorganization/myapp', { $select: ['overall_status', 'overall_progress'] }).then(function(device) { * console.log(device); * }) */ async getAllByApplication(slugOrUuidOrId, options) { const { id } = await sdkInstance.models.application.get(slugOrUuidOrId, { $select: 'id', }); return await getAll((0, util_1.mergePineOptions)({ $filter: { belongs_to__application: id } }, options)); }, /** * @summary Get all devices by organization * @name getAllByOrganization * @public * @function * @memberof balena.models.device * * @description * This method returns all devices of a specific application. * In order to have the following computed properties in the result * you have to explicitly define them in a `$select` in the extra options: * * `overall_status` * * `overall_progress` * * `should_be_running__release` * * @param {String|Number} handleOrId - organization handle (string) or id (number). * @param {Object} [options={}] - extra pine options to use * @fulfil {Object[]} - devices * @returns {Promise} * * @example * balena.models.device.getAllByOrganization('myorganization').then(function(devices) { * console.log(devices); * }); * * @example * balena.models.device.getAllByOrganization(123).then(function(devices) { * console.log(devices); * }); * * @example * balena.models.device.getAllByOrganization('myorganization', { $select: ['overall_status', 'overall_progress'] }).then(function(device) { * console.log(device); * }) */ async getAllByOrganization(handleOrId, options) { const { id } = await sdkInstance.models.organization.get(handleOrId, { $select: 'id', }); return await getAll((0, util_1.mergePineOptions)({ $filter: { belongs_to__application: { $any: { $alias: 'bta', $expr: { bta: { organization: id, }, }, }, }, }, }, options)); }, /** * @summary Get a single device * @name get * @public * @function * @memberof balena.models.device * * @description * This method returns a single device by id or uuid. * In order to have the following computed properties in the result * you have to explicitly define them in a `$select` in the extra options: * * `overall_status` * * `overall_progress` * * `should_be_running__release` * * @param {String|Number} uuidOrId - device uuid (string) or id (number) * @param {Object} [options={}] - extra pine options to use * @fulfil {Object} - device * @returns {Promise} * * @example * balena.models.device.get('7cf02a6').then(function(device) { * console.log(device); * }) * * @example * balena.models.device.get(123).then(function(device) { * console.log(device); * }) * * @example * balena.models.device.get('7cf02a6', { $select: ['overall_status', 'overall_progress'] }).then(function(device) { * console.log(device); * }) */ async get(uuidOrId, options) { if (uuidOrId == null) { throw new errors.BalenaDeviceNotFound(uuidOrId); } if (uuidOrId === '') { throw new errors.BalenaInvalidParameterError('uuidOrId', uuidOrId); } let device; const isPotentiallyFullUuid = (0, util_1.isFullUuid)(uuidOrId); if (isPotentiallyFullUuid || (0, util_1.isId)(uuidOrId)) { device = await pine.get({ resource: 'device', id: isPotentiallyFullUuid ? { uuid: uuidOrId } : uuidOrId, options, }); } else { const devices = await pine.get({ resource: 'device', options: (0, util_1.mergePineOptions)({ $filter: { uuid: { $startswith: uuidOrId }, }, }, options), }); if (devices.length > 1) { throw new errors.BalenaAmbiguousDevice(uuidOrId); } device = devices[0]; } if (device == null) { throw new errors.BalenaDeviceNotFound(uuidOrId); } return device; }, /** * @summary Get a single device along with its associated services' details, * including their associated commit * @name getWithServiceDetails * @public * @function * @memberof balena.models.device * * @description * This method does not map exactly to the underlying model: it runs a * larger prebuilt query, and reformats it into an easy to use and * understand format. If you want more control, or to see the raw model * directly, use `device.get(uuidOrId, options)` instead. * * @param {String|Number} uuidOrId - device uuid (string) or id (number) * @param {Object} [options={}] - extra pine options to use * @fulfil {Object} - device with service details * @returns {Promise} * * @example * balena.models.device.getWithServiceDetails('7cf02a6').then(function(device) { * console.log(device); * }) * * @example * balena.models.device.getWithServiceDetails(123).then(function(device) { * console.log(device); * }) */ async getWithServiceDetails(uuidOrId, options) { const device = await exports.get(uuidOrId, (0, util_1.mergePineOptions)({ $expand: device_service_details_1.getCurrentServiceDetailsPineExpand }, options)); return (0, device_service_details_1.generateCurrentServiceDetails)(device); }, /** * @summary Get devices by name * @name getByName * @public * @function * @memberof balena.models.device * * @param {String} name - device name * @fulfil {Object[]} - devices * @returns {Promise} * * @example * balena.models.device.getByName('MyDevice').then(function(devices) { * console.log(devices); * }); */ async getByName(name, options) { const devices = await getAll((0, util_1.mergePineOptions)({ $filter: { device_name: name } }, options)); if (devices.length === 0) { throw new errors.BalenaDeviceNotFound(name); } return devices; }, /** * @summary Get the name of a device * @name getName * @public * @function * @memberof balena.models.device * * @param {String|Number} uuidOrId - device uuid (string) or id (number) * @fulfil {String} - device name * @returns {Promise} * * @example * balena.models.device.getName('7cf02a6').then(function(deviceName) { * console.log(deviceName); * }); * * @example * balena.models.device.getName(123).then(function(deviceName) { * console.log(deviceName); * }); */ getName: async (uuidOrId) => { const { device_name } = await exports.get(uuidOrId, { $select: 'device_name', }); return device_name; }, /** * @summary Get application name * @name getApplicationName * @public * @function * @memberof balena.models.device * * @param {String|Number} uuidOrId - device uuid (string) or id (number) * @fulfil {String} - application name * @returns {Promise} * * @example * balena.models.device.getApplicationName('7cf02a6').then(function(applicationName) { * console.log(applicationName); * }); * * @example * balena.models.device.getApplicationName(123).then(function(applicationName) { * console.log(applicationName); * }); */ getApplicationName: async (uuidOrId) => { const device = await exports.get(uuidOrId, { $select: 'id', $expand: { belongs_to__application: { $select: 'app_name' } }, }); return device.belongs_to__application[0].app_name; }, /** * @summary Check if a device exists * @name has * @public * @function * @memberof balena.models.device * * @param {String|Number} uuidOrId - device uuid (string) or id (number) * @fulfil {Boolean} - has device * @returns {Promise} * * @example * balena.models.device.has('7cf02a6').then(function(hasDevice) { * console.log(hasDevice); * }); * * @example * balena.models.device.has(123).then(function(hasDevice) { * console.log(hasDevice); * }); */ has: async (uuidOrId) => { try { await exports.get(uuidOrId, { $select: ['id'] }); return true; } catch (err) { if (err instanceof errors.BalenaDeviceNotFound) { return false; } throw err; } }, /** * @summary Check if a device is online * @name isOnline * @public * @function * @memberof balena.models.device * * @param {String|Number} uuidOrId - device uuid (string) or id (number) * @fulfil {Boolean} - is device online * @returns {Promise} * * @example * balena.models.device.isOnline('7cf02a6').then(function(isOnline) { * console.log('Is device online?', isOnline); * }); * * @example * balena.models.device.isOnline(123).then(function(isOnline) { * console.log('Is device online?', isOnline); * }); */ isOnline: async (uuidOrId) => { const { is_online } = await exports.get(uuidOrId, { $select: 'is_online', }); return is_online; }, /** * @summary Get the local IP addresses of a device * @name getLocalIPAddresses * @public * @function * @memberof balena.models.device * * @param {String|Number} uuidOrId - device uuid (string) or id (number) * @fulfil {String[]} - local ip addresses * @reject {Error} Will reject if the device is offline * @returns {Promise} * * @example * balena.models.device.getLocalIPAddresses('7cf02a6').then(function(localIPAddresses) { * localIPAddresses.forEach(function(localIP) { * console.log(localIP); * }); * }); * * @example * balena.models.device.getLocalIPAddresses(123).then(function(localIPAddresses) { * localIPAddresses.forEach(function(localIP) { * console.log(localIP); * }); * }); */ getLocalIPAddresses: async (uuidOrId) => { const { is_online, ip_address } = await exports.get(uuidOrId, { $select: ['is_online', 'ip_address'], }); if (!is_online) { throw new Error(`The device is offline: ${uuidOrId}`); } return (ip_address !== null && ip_address !== void 0 ? ip_address : '').split(' '); }, /** * @summary Get the MAC addresses of a device * @name getMACAddresses * @public * @function * @memberof balena.models.device * * @param {String|Number} uuidOrId - device uuid (string) or id (number) * @fulfil {String[]} - mac addresses * @returns {Promise} * * @example * balena.models.device.getMACAddresses('7cf02a6').then(function(macAddresses) { * macAddresses.forEach(function(mac) { * console.log(mac); * }); * }); * * @example * balena.models.device.getMACAddresses(123).then(function(macAddresses) { * macAddresses.forEach(function(mac) { * console.log(mac); * }); * }); */ getMACAddresses: async (uuidOrId) => { const { mac_address } = await exports.get(uuidOrId, { $select: ['mac_address'], }); if (mac_address == null) { return []; } return mac_address.split(' '); }, /** * @summary Get the metrics related information for a device * @name getMetrics * @public * @function * @memberof balena.models.device * * @param {String|Number} uuidOrId - device uuid (string) or id (number) * @fulfil {Object} - device metrics * @returns {Promise} * * @example * balena.models.device.getMetrics('7cf02a6').then(function(deviceMetrics) { * console.log(deviceMetrics); * }); * * @example * balena.models.device.getMetrics(123).then(function(deviceMetrics) { * console.log(deviceMetrics); * }); */ getMetrics: async (uuidOrId) => { const device = await exports.get(uuidOrId, { $select: [ 'memory_usage', 'memory_total', 'storage_block_device', 'storage_usage', 'storage_total', 'cpu_usage', 'cpu_temp', 'cpu_id', 'is_undervolted', ], }); return device; }, /** * @summary Remove device * @name remove * @public * @function * @memberof balena.models.device * * @param {String|String[]|Number|Number[]} uuidOrIdOrArray - device uuid (string) or id (number) or array of full uuids or ids * @returns {Promise} * * @example * balena.models.device.remove('7cf02a6'); * * @example * balena.models.device.remove(123); */ remove: async (uuidOrIdOrArray) => { await batchDeviceOperation()({ uuidOrIdOrArray, fn: async (devices) => { await pine.delete({ resource: 'device', options: { $filter: { id: { $in: devices.map((d) => d.id) }, }, }, }); }, }); }, /** * @summary Deactivate device * @name deactivate * @public * @function * @memberof balena.models.device * * @param {String|String[]|Number|Number[]} uuidOrIdOrArray - device uuid (string) or id (number) or array of full uuids or ids * @returns {Promise} * * @example * balena.models.device.deactivate('7cf02a6'); * * @example * balena.models.device.deactivate(123); */ deactivate: async (uuidOrIdOrArray) => { await set(uuidOrIdOrArray, { is_active: false, }); }, /** * @summary Rename device * @name rename * @public * @function * @memberof balena.models.device * * @param {String|Number} uuidOrId - device uuid (string) or id (number) * @param {String} newName - the device new name * * @returns {Promise} * * @example * balena.models.device.rename('7cf02a6', 'NewName'); * * @example * balena.models.device.rename(123, 'NewName'); */ rename: async (uuidOrId, newName) => { await set(uuidOrId, { device_name: newName, }); }, /** * @summary Note a device * @name setNote * @public * @function * @memberof balena.models.device * * @param {String|String[]|Number|Number[]} uuidOrIdOrArray - device uuid (string) or id (number) or array of full uuids or ids * @param {String} note - the note * * @returns {Promise} * * @example * balena.models.device.setNote('7cf02a6', 'My useful note'); * * @example * balena.models.device.setNote(123, 'My useful note'); */ setNote: async (uuidOrIdOrArray, note) => { await set(uuidOrIdOrArray, { note }); }, /** * @summary Set a custom location for a device * @name setCustomLocation * @public * @function * @memberof balena.models.device * * @param {String|String[]|Number|Number[]} uuidOrIdOrArray - device uuid (string) or id (number) or array of full uuids or ids * @param {Object} location - the location ({ latitude: 123, longitude: 456 }) * * @returns {Promise} * * @example * balena.models.device.setCustomLocation('7cf02a6', { latitude: 123, longitude: 456 }); * * @example * balena.models.device.setCustomLocation(123, { latitude: 123, longitude: 456 }); */ setCustomLocation: async (uuidOrIdOrArray, location) => { await set(uuidOrIdOrArray, { custom_latitude: String(location.latitude), custom_longitude: String(location.longitude), }); }, /** * @summary Clear the custom location of a device * @name unsetCustomLocation * @public * @function * @memberof balena.models.device * * @param {String|String[]|Number|Number[]} uuidOrIdOrArray - device uuid (string) or id (number) or array of full uuids or ids * * @returns {Promise} * * @example * balena.models.device.unsetCustomLocation('7cf02a6'); * * @example * balena.models.device.unsetCustomLocation(123); */ unsetCustomLocation: async (uuidOrIdOrArray) => { await exports.setCustomLocation(uuidOrIdOrArray, { latitude: '', longitude: '', }); }, /** * @summary Move a device to another application * @name move * @public * @function * @memberof balena.models.device * * @param {String|String[]|Number|Number[]} uuidOrIdOrArray - device uuid (string) or id (number) or array of full uuids or ids * @param {String|Number} applicationSlugOrUuidOrId - application slug (string), uuid (string) or id (number) * * @returns {Promise} * * @example * balena.models.device.move('7cf02a6', 'myorganization/myapp'); * * @example * balena.models.device.move(123, 'myorganization/myapp'); * * @example * balena.models.device.move(123, 456); */ move: async (uuidOrIdOrArray, applicationSlugOrUuidOrId) => { const applicationOptions = { $select: 'id', $expand: { is_for__device_type: { $select: 'id', $expand: { is_of__cpu_architecture: { $select: 'slug', }, }, }, }, }; const application = await sdkInstance.models.application.get(applicationSlugOrUuidOrId, applicationOptions); const appCpuArchSlug = application.is_for__device_type[0].is_of__cpu_architecture[0].slug; const deviceOptions = { $select: 'is_of__device_type', $expand: { is_of__device_type: { $select: 'is_of__cpu_architecture', $expand: { is_of__cpu_architecture: { $select: 'slug', }, }, }, }, }; await batchDeviceOperation()({ uuidOrIdOrArray, options: deviceOptions, groupByNavigationPoperty: 'belongs_to__application', fn: async (devices) => { for (const device of devices) { const isCompatibleMove = sdkInstance.models.os.isArchitectureCompatibleWith(device.is_of__device_type[0].is_of__cpu_architecture[0].slug, appCpuArchSlug); if (!isCompatibleMove) { throw new errors.BalenaInvalidDeviceType(`Incompatible application: ${applicationSlugOrUuidOrId}`); } } await pine.patch({ resource: 'device', options: { $filter: { id: { $in: devices.map((d) => d.id) }, }, }, body: { belongs_to__application: application.id, }, }); }, }); }, ...(0, device_supervisor_api_partial_1.getSupervisorApiHelper)(deps, opts), /** * @summary Get the target supervisor state on a device * @name getSupervisorTargetState * @public * @function * @memberof balena.models.device * * @param {String|Number} uuidOrId - device uuid (string) or id (number) * @param {Number} version - (optional) target state version (2 or 3), default to 2 * @returns {Promise} * * @example * balena.models.device.getSupervisorTargetState('7cf02a6').then(function(state) { * console.log(state); * }); * * @example * balena.models.device.getSupervisorTargetState(123).then(function(state) { * console.log(state); * }); * * @example * balena.models.device.getSupervisorTargetState(123, 3).then(function(state) { * console.log(state); * }); */ getSupervisorTargetState: async (uuidOrId, version = 2) => { const { uuid } = await exports.get(uuidOrId, { $select: 'uuid' }); const { body } = await request.send({ url: `/device/v${version}/${uuid}/state`, baseUrl: apiUrl, }); return body; }, /** * @summary Get the target supervisor state on a "generic" device on a fleet * @name getSupervisorTargetStateForApp * @public * @function * @memberof balena.models.device * * @param {String|Number} uuidOrId - fleet uuid (string) or id (number) * @param {String} release - (optional) release uuid (default tracked) * @returns {Promise} * * @example * balena.models.device.getSupervisorTargetStateForApp('7cf02a6').then(function(state) { * console.log(state); * }); * * @example * balena.models.device.getSupervisorTargetStateForApp(123).then(function(state) { * console.log(state); * }); * * @example * balena.models.device.getSupervisorTargetStateForApp(123, '7cf02a6').then(function(state) { * console.log(state); * }); * */ getSupervisorTargetStateForApp: async (slugOrUuidOrId, release) => { const { uuid } = await sdkInstance.models.application.get(slugOrUuidOrId, { $select: 'uuid', }); const { body } = await request.send({ url: `/device/v3/fleet/${uuid}/state/?releaseUuid=${release !== null && release !== void 0 ? release : ''}`, baseUrl: apiUrl, }); return body; }, /** * @summary Generate a random key, useful for both uuid and api key. * @name generateUniqueKey * @function * @public * @memberof balena.models.device * * @returns {String} A generated key * * @example * randomKey = balena.models.device.generateUniqueKey(); * // randomKey is a randomly generated key that can be used as either a uuid or an api key * console.log(randomKey); */ generateUniqueKey() { // Wrap with a function so that we can lazy load registerDevice return registerDevice().generateUniqueKey(); }, /** * @summary Register a new device with a Balena application. * @name register * @public * @function * @memberof balena.models.device * * @param {String|Number} applicationSlugOrUuidOrId - application slug (string), uuid (string) or id (number) * @param {String} uuid - device uuid * @param {String} [deviceTypeSlug] - device type slug (string) or alias (string) * * @fulfil {Object} Device registration info ({ id: "...", uuid: "...", api_key: "..." }) * @returns {Promise} * * @example * var uuid = balena.models.device.generateUniqueKey(); * balena.models.device.register('myorganization/myapp', uuid).then(function(registrationInfo) { * console.log(registrationInfo); * }); * * @example * var uuid = balena.models.device.generateUniqueKey(); * balena.models.device.register('myorganization/myapp', uuid, 'raspberry-pi').then(function(registrationInfo) { * console.log(registrationInfo); * }); * * @example * var uuid = balena.models.device.generateUniqueKey(); * balena.models.device.register(123, uuid).then(function(registrationInfo) { * console.log(registrationInfo); * }); */ async register(applicationSlugOrUuidOrId, uuid, deviceTypeSlug) { const deviceTypeOptions = { $select: 'slug', $expand: { is_of__cpu_architecture: { $select: 'slug', }, }, }; const applicationOptions = { $select: 'id', $expand: { is_for__device_type: deviceTypeOptions }, }; const [{ id: userId }, apiKey, application, deviceType] = await Promise.all([ sdkInstance.auth.getUserInfo(), sdkInstance.models.application.generateProvisioningKey({ slugOrUuidOrId: applicationSlugOrUuidOrId, // Use 10 minute expiry date as we will immediately use the provisioning key to create a device and then not need it keyExpiryDate: new Date(Date.now() + 1000 * 60 * 10).toISOString(), keyDescription: 'Created by SDK to register a device', }), sdkInstance.models.application.get(applicationSlugOrUuidOrId, applicationOptions), typeof deviceTypeSlug === 'string' ? sdkInstance.models.deviceType.get(deviceTypeSlug, { $select: 'slug', $expand: { is_of__cpu_architecture: { $select: 'slug', }, }, }) : null, ]); if (deviceType != null) { const isCompatibleParameter = sdkInstance.models.os.isArchitectureCompatibleWith(deviceType.is_of__cpu_architecture[0].slug, application.is_for__device_type[0].is_of__cpu_architecture[0].slug); if (!isCompatibleParameter) { throw new errors.BalenaInvalidDeviceType(`Incompatible device type: ${deviceTypeSlug}`); } } return await registerDevice().register({ userId, applicationId: application.id, uuid, deviceType: (deviceType !== null && deviceType !== void 0 ? deviceType : application.is_for__device_type[0]).slug, provisioningApiKey: apiKey, apiEndpoint: apiUrl, }); }, /** * @summary Generate a device key * @name generateDeviceKey * @public * @function * @memberof balena.models.device * * @param {String|Number} uuidOrId - device uuid (string) or id (number) * @param {String} [keyName] - Device key name * @param {String} [keyDescription] - Description for device key * @returns {Promise} * * @example * balena.models.device.generateDeviceKey('7cf02a6').then(function(deviceApiKey) { * console.log(deviceApiKey); * }); * * @example * balena.models.device.generateDeviceKey(123).then(function(deviceApiKey) { * console.log(deviceApiKey); * }); */ generateDeviceKey: async (uuidOrId, keyName, keyDescription, keyExpiryDate) => { const deviceId = (await sdkInstance.models.device.get(uuidOrId, { $select: 'id' })).id; const { body } = await request.send({ method: 'POST', url: `/api-key/device/${deviceId}/device-key`, baseUrl: apiUrl, body: { name: keyName, description: keyDescription, expiryDate: keyExpiryDate, }, }); return body; }, /** * @summary Check if a device is web accessible with device utls * @name hasDeviceUrl * @public * @function * @memberof balena.models.device * * @param {String|Number} uuidOrId - device uuid (string) or id (number) * @fulfil {Boolean} - has device url * @returns {Promise} * * @example * balena.models.device.hasDeviceUrl('7cf02a6').then(function(hasDeviceUrl) { * if (hasDeviceUrl) { * console.log('The device has device URL enabled'); * } * }); * * @example * balena.models.device.hasDeviceUrl(123).then(function(hasDeviceUrl) { * if (hasDeviceUrl) { * console.log('The device has device URL enabled'); * } * }); */ hasDeviceUrl: async (uuidOrId) => { const { is_web_accessible } = await exports.get(uuidOrId, { $select: 'is_web_accessible', }); return is_web_accessible; }, /** * @summary Get a device url * @name getDeviceUrl * @public * @function * @memberof balena.models.device * * @param {String|Number} uuidOrId - device uuid (string) or id (number) * @fulfil {String} - device url * @returns {Promise} * * @example * balena.models.device.getDeviceUrl('7cf02a6').then(function(url) { * console.log(url); * }); * * @example * balena.models.device.getDeviceUrl(123).then(function(url) { * console.log(url); * }); */ getDeviceUrl: async (uuidOrId) => { const { is_web_accessible, uuid } = await exports.get(uuidOrId, { $select: ['is_web_accessible', 'uuid'], }); if (!is_web_accessible) { throw new Error(`Device is not web accessible: ${uuidOrId}`); } const $deviceUrlsBase = await getDeviceUrlsBase(); return `https://${uuid}.${$deviceUrlsBase}`; }, /** * @summary Enable device url for a device * @name enableDeviceUrl * @public * @function * @memberof balena.models.device * * @param {String|String[]|Number|Number[]} uuidOrIdOrArray - device uuid (string) or id (number) or array of full uuids or ids * @returns {Promise} * * @example * balena.models.device.enableDeviceUrl('7cf02a6'); * * @example * balena.models.device.enableDeviceUrl(123); */ enableDeviceUrl: async (uuidOrIdOrArray) => { await set(uuidOrIdOrArray, { is_web_accessible: true, }); }, /** * @summary Disable device url for a device * @name disableDeviceUrl * @public * @function * @memberof balena.models.device * * @param {String|String[]|Number|Number[]} uuidOrIdOrArray - device uuid (string) or id (number) or array of full uuids or ids * @returns {Promise} * * @example * balena.models.device.disableDeviceUrl('7cf02a6'); * * @example * balena.models.device.disableDeviceUrl(123); */ disableDeviceUrl: async (uuidOrIdOrArray) => { await set(uuidOrIdOrArray, { is_web_accessible: false, }); }, /** * @summary Enable local mode * @name enableLocalMode * @public * @function * @memberof balena.models.device * * @param {String|Number} uuidOrId - device uuid (string) or id (number) * @returns {Promise} * * @example * balena.models.device.enableLocalMode('7cf02a6'); * * @example * balena.models.device.enableLocalMode(123); */ async enableLocalMode(uuidOrId) { const selectedProps = ['id', ...local_mode_1.LOCAL_MODE_SUPPORT_PROPERTIES]; const device = await exports.get(uuidOrId, { $select: selectedProps }); (0, local_mode_1.checkLocalModeSupported)(device); await exports.configVar.set(device.id, local_mode_1.LOCAL_MODE_ENV_VAR, '1'); }, /** * @summary Disable local mode * @name disableLocalMode * @public * @function * @memberof balena.models.device * * @param {String|Number} uuidOrId - device uuid (string) or id (number) * @returns {Promise} * * @example * balena.models.device.disableLocalMode('7cf02a6'); * * @example * balena.models.device.disableLocalMode(123); */ disableLocalMode: (uuidOrId) => exports.configVar.set(uuidOrId, local_mode_1.LOCAL_MODE_ENV_VAR, '0'), /** * @summary Check if local mode is enabled on the device * @name isInLocalMode * @public * @function * @memberof balena.models.device * * @param {String|Number} uuidOrId - device uuid (string) or id (number) * @fulfil {Boolean} - has device url * @returns {Promise} * * @example * balena.models.device.isInLocalMode('7cf02a6').then(function(isInLocalMode) { * if (isInLocalMode) { * console.log('The device has local mode enabled'); * } * }); * * @example * balena.models.device.isInLocalMode(123).then(function(isInLocalMode) { * if (isInLocalMode) { * console.log('The device has local mode enabled'); * } * }); */ isInLocalMode: async (uuidOrId) => { const value = await exports.configVar.get(uuidOrId, local_mode_1.LOCAL_MODE_ENV_VAR); return value === '1'; }, /** * @summary Returns whether local mode is supported along with a message describing the reason