domotz-remote-pawn
Version:
Domotz Agent
356 lines (341 loc) • 15.4 kB
JavaScript
/**
* This file is part of Domotz Agent.
*
* @license
* Domotz Agent is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Domotz Agent is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Domotz Agent. If not, see <http://www.gnu.org/licenses/>.
*
* @requires sandbox/library/device
* @requires sandbox/library/crypto
* @requires sandbox/library/buffer
* @copyright Copyright (C) Domotz Inc
*/
/**
* The Domotz Context Library
* Used for accessing the domotz context within a custom driver.
* Exposes the device namespace for any device related operations, utility libraries and driver callbacks
* @namespace D
*/
/**
* Custom Driver device credentials
* @typedef {Object} DeviceCredentials
* @readonly
* @property {string} username - The device username
* @property {string} password - The device password
*/
/**
* The Cheerio Library Accepted Options
* @typedef {Object} CheerioOptions
* @see {@link https://cheerio.js.org/interfaces/CheerioOptions.html}
*/
/**
* The Cheerio Library Loaded Document Interface.
* Can be used to traverse and manipulate the loaded document.
* @typedef {Object} CheerioAPI
* @see {@link https://cheerio.js.org/interfaces/CheerioAPI.html}
*/
/**
* Used when the result of a call towards a device is in an erroneous state
* @typedef {Object} ErrorResult
* @property {string} message - The Error Message
*/
const bufferLibrary = require('./library/buffer');
const cryptoLibrary = require('./library/crypto');
const deviceLibrary = require('./library/device');
const parameterLibrary = require('./library/parameter');
const tableLibrary = require('./library/table');
const variableLibrary = require('./library/variable');
const driverTypes = require('./constants').driverTypes;
const errorTypes = require('./constants').errorTypes;
const valueTypes = require('./constants').valueTypes;
var driverVariable;
var driverTable;
var agentDriverSettings = null;
var contextParameterLibrary = null;
module.exports.createDomotzContext = function (message, newConsole) {
agentDriverSettings = message.agentDriverSettings;
contextParameterLibrary = parameterLibrary.parameterLibrary(newConsole, message.parameters);
var DomotzContext = {
/**
* Creates an External IP device object
* @example D.createExternalDevice("1.1.1.1", {"username": "root", "password": D.device.password()})
* @param {string} deviceHost - The IP or Hostname of the external device
* @param {DeviceCredentials} [deviceCredentials] - The credentials for the external device
* @memberof D
* @readonly
* @return {device} - The External Device object
*/
createExternalDevice: function (deviceHost, deviceCredentials) {
var externalDevice = {
ip: deviceHost,
credentials: deviceCredentials,
};
return deviceLibrary.device(externalDevice, agentDriverSettings, newConsole);
},
/**
* The Custom Driver Crypto object.
* Contains utility functions that offer ways of encapsulating secure credentials to be used
* as part of a secure HTTPS net or http connection.
* Offers a set of wrappers for OpenSSL's hash, hmac, cipher methods.<br>
* For more information check the official node documentation:<br>
* {@link https://nodejs.org/docs/latest-v14.x/api/crypto.html}
* @example D.crypto
* @memberof D
* @namespace D.crypto
*/
crypto: cryptoLibrary.cryptoLibrary(newConsole),
/**
* Get a predefined parameter during execution by its name.
* Returns undefined in case the parameter was not provided to the context.
* @example D.getParameter("name")
* @memberof D
* @function
* @param {string} parameterName - Name of the parameter to get
* @return {string|number|Object|undefined} - the value associated with the provided parameter name
*/
getParameter: contextParameterLibrary.getParameter,
/**
* Known Domotz Context Error types
* @example D.errorType.AUTHENTICATION_ERROR
* @memberof D
* @readonly
* @enum {ErrorType}
*/
errorType: errorTypes,
/**
* Domotz Variable Value Types
* @example D.valueType.MONOTONE_RATE
* @memberof D
* @readonly
* @enum {ValueType}
*/
valueType: valueTypes,
/**
* Failure callback in D (D.failure).
* It can be present in any function in order to indicate a failure in the execution
* @example D.failure(D.errorType.AUTHENTICATION_ERROR)
* @memberof D
* @function
* @param {ErrorType} [errorType] - The type of error that caused the failure callback. Must be a member of D.errorType
*/
failure: null,
/**
* Html Parser Library
* @example D.htmlParse()
* @function
* @param {string|Node|Node[]|Buffer} content - Markup to be loaded.
* @param {CheerioOptions} [options] - Options for the created instance.
* @param {boolean} [isDocument=true] - Allows parser to be switched to fragment mode.
* @returns {CheerioAPI} - The loaded document
*/
htmlParse: require('cheerio').load,
/**
* Mathematical Utilities library
* @example D.math
* @namespace D.math
* @memberof D
*/
math: {
/**
* Percentage Calculating Function
* @function
* @example
* // returns 70
* D.math.percent(7, 10)
* @param {number} actual - The actual number
* @param {number} maximum - The maximum number
* @returns {number} - The Percentage
*/
percent: function (actual, maximum) {
return Math.round((10000.0 * parseInt(actual, 10)) / parseInt(maximum, 10)) / 100;
},
},
/**
* NodeJS Lodash Module
* Javascript utility library that delivers modularity, performance and some extra features.
* @example D._
* @memberof D
* @external _
* @see {@link https://lodash.com/docs/4.17.15}
*/
_: require('lodash'),
/**
* Nodejs q Module
* @private
* @example D.q
* @memberof D
* @external q
* @see {@link https://devdocs.io/q/}
*/
q: require('q'),
/**
* Wrapper for unsafe functions which can lead to crashing the agent or other potential problems if not used properly
* @example D._unsafe
* @memberof D
* @namespace D._unsafe
*/
_unsafe: {
buffer: bufferLibrary.bufferLibrary(newConsole),
},
};
/**
* Domotz Generic Context
* @namespace DomotzGeneric
* @extends D
*/
var DomotzGenericContext = Object.create(DomotzContext);
/**
* Success callback in D (D.success).
* It must be present in validate, get_status or all custom action functions code (or their callbacks)
* in order to indicate their successful execution
* @example D.success()
* @example D.success(table)
* @example D.success(variables)
* @example D.success(variables, table)
* @memberof DomotzGeneric
* @function
* @param {Array.<Variable>} [variables] - The variables to return (dry run) or store
* @param {driverTable} [table] - The custom driver table to return (dry run) or store
*/
DomotzGenericContext.success = null;
/**
* Creates a driver table for variable values visualization in a table format
* @example D.createTable("My Table", [{"label": "Column A"}, {"label": "Column B", "unit": "%"}])
* @memberof DomotzGeneric
* @function
* @param {string} label - The Table Label
* @param {Array.<ColumnHeader>} columnHeaders - The List of column header definitions
* @readonly
* @return {driverTable} - The Custom Driver Table object
*/
DomotzGenericContext.createTable = function (label, columnHeaders) {
driverTable = tableLibrary.createTable(label, columnHeaders, newConsole);
return driverTable;
};
/**
* Creates a custom driver variable to be sent in the D.success callback.
* <br> This variable is suited for tracking a single key-value pair, if you want to
* track tabular data, use {@link D.createTable} instead.
* @example
* // returns {"uid": "1a", "unit": "C", "value": 60, "label": "CPU Temperature"}
* D.createVariable('1a', 'CPU Temperature', 60, 'C', D.valueType.NUMBER)
* @memberof DomotzGeneric
* @function
* @readonly
* @param {string} uid - The identifier of the variable. Must be Unique. Max 50 characters
* <br> cannot be one of the following reserved words: "table", "column", "history"
* @param {string} name - The Name/Label of the variable. Max 100 characters
* @param {string} value - The Value of the variable. Max 500 characters
* @param {string} unit - The Unit of measurement of the variable (eg %). Max 10 characters
* @param {ValueType} valueType - The value type of the variable (used for display purposes)
* @return {Variable}
*/
DomotzGenericContext.createVariable = function (uid, name, value, unit, valueType) {
driverVariable = variableLibrary.createVariable(uid, name, value, unit, valueType, agentDriverSettings);
return driverVariable;
};
/**
* Domotz Configuration Management Context
* @namespace DomotzConfigurationManagement
* @extends D
*/
var DomotzConfigurationManagementContext = Object.create(DomotzContext);
/**
* Success callback in D (D.success).
* It must be present in the validate and backup functions (or their callbacks)
* in order to indicate their successful execution
* @memberof DomotzConfigurationManagement
* @example D.success()
* @example D.success(configuration)
* @function
* @param {ConfigurationBackup} [configuration] - The configuration to return (dry run) or store for backup
*/
DomotzConfigurationManagementContext.success = null;
/**
* Function createBackup
* @memberof DomotzConfigurationManagement
* @function
* @param {ConfigurationBackup} configurationBackup - The backup object to be filled in and validated
* @return {ConfigurationBackup}
*/
DomotzConfigurationManagementContext.createBackup = function (configurationBackup) {
// maybe move to a library file
var validatedBackup = {};
if (configurationBackup === undefined || configurationBackup === null) {
throw Error('Invalid configuration backup object - it must not be null or undefined');
}
validatedBackup.label = configurationBackup.label || 'Custom Driver Configuration Backup';
if (configurationBackup.running === undefined || configurationBackup.running === null || !(typeof configurationBackup.running === 'string')) {
throw Error('Invalid running configuration backup content - it must be string and not ' + typeof configurationBackup.running);
} else if (agentDriverSettings.max_config_backup_size < configurationBackup.running.length) {
throw Error(
'Maximum running configuration backup size exceeded: allowed ' +
agentDriverSettings.max_config_backup_size.toString() +
' bytes, provided ' +
configurationBackup.running.length.toString()
);
} else {
validatedBackup.running = configurationBackup.running;
}
if (typeof configurationBackup.startup === 'string') {
if (agentDriverSettings.max_config_backup_size < configurationBackup.startup.length) {
throw Error(
'Maximum startup configuration backup size exceeded: allowed ' +
agentDriverSettings.max_config_backup_size.toString() +
' bytes, provided ' +
configurationBackup.startup.length.toString()
);
} else {
validatedBackup.startup = configurationBackup.startup;
}
} else if (configurationBackup.startup !== undefined || configurationBackup.startup !== null) {
validatedBackup.startup = null;
} else {
throw Error(
'Invalid startup configuration backup content - it must be string, null or undefined and not ' + typeof configurationBackup.startup
);
}
if (configurationBackup.ignoredLines === null || configurationBackup.ignoredLines === undefined) {
validatedBackup.ignoredLines = null;
} else if (!Array.isArray(configurationBackup.ignoredLines)) {
throw Error(
'Invalid ignored lines for configuration backup content - it must be string array, null or undefined and not ' + (typeof configurationBackup.ignoredLines)
)
} else if (configurationBackup.ignoredLines.length > agentDriverSettings.max_ignored_lines) {
throw Error("Too many ignored line regular expressions, max allowed number is " + agentDriverSettings.max_ignored_lines.toString())
} else {
validatedBackup.ignoredLines = [];
for (var i = 0; i < configurationBackup.ignoredLines.length; i++) {
var line = configurationBackup.ignoredLines[i];
if (!(typeof(line) === "string")) {
throw Error("A regex must be a string, and not " + typeof(line))
} else if (line.length > agentDriverSettings.max_ignored_line_len) {
throw Error(
'Line ' + line + ' too long (=' + line.length.toString() + '), '
+ 'max allowed length is ' + agentDriverSettings.max_ignored_line_len.toString()
);
} else {
validatedBackup.ignoredLines.push(line);
}
}
}
return validatedBackup;
};
if (message.driverType === driverTypes.CONFIGURATION_MANAGEMENT) {
return DomotzConfigurationManagementContext;
} else if (message.driverType === driverTypes.GENERIC) {
return DomotzGenericContext;
} else {
return DomotzGenericContext;
}
};