Valetudo/backend/lib/mqtt/handles/PropertyMqttHandle.js

132 lines
4.7 KiB
JavaScript

/**
* Getter callback for MQTT topics. This callback should return a value that is compatible
* with the specified data type. The library will perform the required conversions.
*
* See comments in homie/DataType for available conversions.
*
* @callback getterCallback
* @return {Promise<*>}
*/
/**
* Setter callback for MQTT topics. This callback will be given a JavaScript value, converted
* based on the specified data type..
*
* @callback setterCallback
* @param {*} value
* @return {Promise<void>}
*/
const DataType = require("../homie/DataType");
const MqttHandle = require("./MqttHandle");
/**
* A PropertyMqttHandle represents one single property that each NodeMqttHandle (capability) provides.
* It is statically typed - as in you specify a data type, you publish data of that type and you expect to receive
* data of the same type.
* Protocol validation shall be performed by this base class. Children may perform their own high-level validation.
*
* Note that there are no arrays. Capabilities that provide lists from which the user may select an option may be
* transformed into enums where possible, they may provide their own special functionality using strings or they may
* add or remove properties dynamically.
*/
class PropertyMqttHandle extends MqttHandle {
/**
* Please see https://homieiot.github.io/specification/spec-core-develop//#property-attributes
* for more details
*
* Setter and getter are mutually optional. At least one of them has to be provided.
* Providing only "getter" will result in a read-only property.
* Providing only "setter" will result in a command property, and a getter will be
* provided that reflects the set value back to MQTT.
*
* topicName must follow the Topic ID format: https://homieiot.github.io/specification/spec-core-develop/#topic-ids
*
* @override
* @param {object} options
* @param {import("./NodeMqttHandle")} options.parent
* @param {import("../MqttController")} options.controller MqttController instance
* @param {string} options.topicName Topic ID following the linked format
* @param {string} options.friendlyName User-friendly name for this property
* @param {getterCallback} [options.getter] Getter coroutine
* @param {setterCallback} [options.setter] Setter coroutine
* @param {import("../homie/DataType")|string} options.datatype Data type for this property
* @param {import("../common/Unit")|string} [options.unit] Optional unit for this property
* @param {string} [options.format] Restrictions or options based on the given datatype
* @param {boolean} [options.retained] Whether to retain this property, default true
* @param {string} [options.helpText] Optional help message to be included in the documentation
* @param {object} [options.helpMayChange] Optional object of what:dueTo pairs explaining stuff that may have a
* different format, unit, children properties and that should be scanned dynamically, to be included in the docs.
*/
constructor(options) {
// noinspection JSCheckFunctionSignatures
// @ts-ignore
super(Object.assign(options, {
gettable: options.getter !== undefined,
settable: options.setter !== undefined,
}));
this.unit = options.unit;
this.getter = options.getter;
this.setter = options.setter;
if (this.setter === undefined && this.getter === undefined) {
throw new Error("At least one of setter and getter must be defined!");
}
}
/**
* Returns Homie attributes for the current property
*
* @return {object}
*/
getHomieAttributes() {
let result = {
"$name": this.friendlyName,
"$datatype": this.dataType,
"$settable": this.marshalHomie(this.settable, DataType.BOOLEAN),
"$retained": this.marshalHomie(this.retained, DataType.BOOLEAN),
};
if (this.unit !== undefined) {
result["$unit"] = this.unit;
}
if (this.format !== undefined) {
result["$format"] = this.format;
}
return result;
}
async get() {
return this.getter();
}
async set(value) {
await this.setter(value);
if (this.gettable) {
await this.refresh();
}
}
/**
* @callback alsoCb
* @param {PropertyMqttHandle} it
*/
/**
* Scope function to easily perform additional operations before registering the property.
*
* @param {alsoCb} cb
* @return {PropertyMqttHandle}
*/
also(cb) {
cb(this);
return this;
}
}
module.exports = PropertyMqttHandle;