iobroker.shelly
Version:
Integrate your Shelly devices into ioBroker via MQTT or CoAP
288 lines • 10.9 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.adapterObjects = exports.existingStates = void 0;
exports.setOrUpdateObject = setOrUpdateObject;
exports.deleteObject = deleteObject;
exports.processObjectQueue = processObjectQueue;
exports.getObject = getObject;
exports.loadExistingObjects = loadExistingObjects;
exports.handleStateChange = handleStateChange;
exports.init = init;
let adapter = null;
const stateChangeTrigger = {};
const objectQueue = [];
exports.existingStates = {};
exports.adapterObjects = {};
function setOrUpdateObject(id, obj, obtainCustomFields, value, stateChangeCallback, createNow = true, callback) {
if (!adapter) {
throw new Error('Adapter is not set');
}
obj.type ||= 'state';
obj.common ||= {};
obj.native ||= {};
if (obj.common && obj.common.type === undefined) {
if (value !== null && value !== undefined) {
obj.common.type = typeof value;
}
else if (obj.common.def !== undefined) {
obj.common.type = typeof obj.common.def;
}
else if (obj.type === 'state') {
obj.common.type = 'mixed';
}
}
if (obj.common && obj.common.read === undefined) {
obj.common.read = true; //!(obj.common.type === 'boolean' && !!stateChangeCallback);
}
if (obj.common && obj.common.write === undefined) {
obj.common.write = !!stateChangeCallback || stateChangeTrigger[id];
}
/* if (obj.common && obj.common.def === undefined && value !== null && value !== undefined) {
obj.common.def = value;
}*/
if (obj.common && obj.common.name === undefined) {
obj.common.name = id.split('.').pop() || '';
}
if (!exports.adapterObjects[id] && exports.existingStates[id]) {
exports.adapterObjects[id] = exports.existingStates[id];
if (exports.adapterObjects[id].from) {
delete exports.adapterObjects[id].from;
}
if (exports.adapterObjects[id].ts) {
delete exports.adapterObjects[id].ts;
}
if (exports.adapterObjects[id].acl) {
delete exports.adapterObjects[id].acl;
}
if (exports.adapterObjects[id]._id) {
// @ts-expect-error it is Ok
delete exports.adapterObjects[id]._id;
}
if (obj.common.def === undefined && exports.adapterObjects[id].common.def !== undefined) {
delete exports.adapterObjects[id].common.def;
}
if (obj.common.unit === undefined && exports.adapterObjects[id].common.unit !== undefined) {
delete exports.adapterObjects[id].common.unit;
}
if (obj.common.min === undefined && exports.adapterObjects[id].common.min !== undefined) {
delete exports.adapterObjects[id].common.min;
}
if (obj.common.max === undefined && exports.adapterObjects[id].common.max !== undefined) {
delete exports.adapterObjects[id].common.max;
}
value = undefined; // when exists, and it is first time do not overwrite value!
}
if (exports.existingStates[id]) {
delete exports.existingStates[id];
}
if (exports.adapterObjects[id] && isEquivalent(obj, exports.adapterObjects[id])) {
//adapter.log.debug('Object unchanged for ' + id + ': ' + JSON.stringify(adapterObjects[id]) + ' - update only: ' + JSON.stringify(value));
if (value !== undefined) {
void adapter.setState(id, value, true);
}
if (stateChangeCallback) {
stateChangeTrigger[id] = stateChangeCallback;
}
return;
}
// adapter.log.debug('Add Object for ' + id + ': ' + JSON.stringify(adapterObjects[id]) + '/' + JSON.stringify(obj));
objectQueue.push({
id,
value,
obj,
stateChangeCallback,
obtainCustomFields,
});
exports.adapterObjects[id] = JSON.parse(JSON.stringify(obj));
// adapter.log.debug('Create object for ' + id + ': ' + JSON.stringify(obj) + ' with value: ' + JSON.stringify(value));
if (createNow) {
processObjectQueue(callback);
}
}
function deleteObject(id) {
const obj = exports.adapterObjects[id];
if (!adapter) {
throw new Error('Adapter is not set');
}
if (obj?.type) {
if (obj.type !== 'state') {
Object.keys(exports.adapterObjects).forEach(objId => {
if (objId.startsWith(`${id}.`)) {
adapter.delObject(objId, (err) => {
adapter.log.info(`${exports.adapterObjects[objId].type} ${objId} deleted${err ? ` (${err})` : ''}`);
if (!err) {
delete exports.adapterObjects[objId];
delete stateChangeTrigger[objId];
}
});
}
});
}
adapter.delObject(id, err => {
adapter.log.info(`${exports.adapterObjects[id].type} ${id} deleted (${err})`);
if (!err) {
delete exports.adapterObjects[id];
delete stateChangeTrigger[id];
}
});
}
}
function isEquivalent(a, b) {
//adapter.log.debug('Compare ' + JSON.stringify(a) + ' with ' + JSON.stringify(b));
// Create arrays of property names
if (a === null || a === undefined || b === null || b === undefined) {
return a === b;
}
const aProps = Object.getOwnPropertyNames(a);
const bProps = Object.getOwnPropertyNames(b);
// If number of properties is different,
// objects are not equivalent
if (aProps.length !== bProps.length) {
//console.log('num props different: ' + JSON.stringify(aProps) + ' / ' + JSON.stringify(bProps));
return false;
}
for (let i = 0; i < aProps.length; i++) {
const propName = aProps[i];
if (typeof a[propName] !== typeof b[propName]) {
// console.log('type props ' + propName + ' different');
return false;
}
if (typeof a[propName] === 'object') {
if (!isEquivalent(a[propName], b[propName])) {
return false;
}
}
else {
// If values of same property are not equal,
// objects are not equivalent
if (a[propName] !== b[propName]) {
// console.log('props ' + propName + ' different');
return false;
}
}
}
// If we made it this far, objects
// are considered equivalent
return true;
}
function processObjectQueue(callback) {
if (!objectQueue.length) {
callback?.();
return;
}
function handleObject(queueEntry, callback) {
if (!queueEntry.obj) {
handleValue(queueEntry, () => callback?.());
}
if (!adapter) {
throw new Error('Adapter is not set');
}
adapter.getObject(queueEntry.id, (err, obj) => {
if (!adapter) {
throw new Error('Adapter is not set');
}
if (!err && obj) {
if (Array.isArray(queueEntry.obtainCustomFields)) {
queueEntry.obtainCustomFields.forEach(name => {
if (queueEntry.obj.common?.[name]) {
delete queueEntry.obj.common[name];
}
});
}
adapter.extendObject(queueEntry.id, queueEntry.obj, () => handleValue(queueEntry, () => callback?.()));
}
else {
adapter.setObject(queueEntry.id, queueEntry.obj, () => handleValue(queueEntry, () => callback?.()));
}
});
}
function handleValue(queueEntry, callback) {
if (queueEntry.value === null || queueEntry.value === undefined) {
if (queueEntry.stateChangeCallback) {
stateChangeTrigger[queueEntry.id] = queueEntry.stateChangeCallback;
}
return callback?.();
}
void adapter.setState(queueEntry.id, queueEntry.value, true, () => {
if (queueEntry.stateChangeCallback) {
stateChangeTrigger[queueEntry.id] = queueEntry.stateChangeCallback;
}
return callback?.();
});
}
const queueEntry = objectQueue.shift();
if (queueEntry) {
handleObject(queueEntry, () => processObjectQueue(callback));
}
else {
processObjectQueue(callback);
}
}
function getObject(id) {
return exports.adapterObjects[id];
}
function loadExistingObjects(callback) {
if (!adapter) {
throw new Error('Adapter is not set');
}
void adapter.getAdapterObjects(res => {
if (!adapter) {
throw new Error('Adapter is not set');
}
const objectKeys = Object.keys(res);
for (let i = 0; i < objectKeys.length; i++) {
if (objectKeys[i].indexOf(`${adapter.namespace}.info`) === 0) {
continue;
}
exports.existingStates[objectKeys[i].substr(adapter.namespace.length + 1)] = res[objectKeys[i]];
}
// adapter.log.debug('Existing States: ' + JSON.stringify(Object.keys(existingStates), null, 4));
// devId + '.Bluetooth' = device , ChannelsOd = MACs
// devId + '.Notifications' = channel, statesOf ??
// devId + '.Routines' = channel, statesOf
callback?.();
});
}
function handleStateChange(id, state) {
if (!state || state.ack) {
return;
}
if (!adapter) {
throw new Error('Adapter is not set');
}
id = id.substring(adapter.namespace.length + 1);
if (typeof stateChangeTrigger[id] === 'function') {
const obj = exports.adapterObjects[id];
if (obj?.common?.type && obj.common.type !== 'mixed') {
if (obj.common.type === 'boolean') {
if (state.val === 'true') {
state.val = true;
}
else if (state.val === 'false') {
state.val = false;
}
state.val = !!state.val;
}
if (typeof state.val !== obj.common.type) {
adapter.log.error(`Datatype for ${id} differs from expected, ignore state change! Please write correct datatype (${obj.common.type})`);
return;
}
}
stateChangeTrigger[id](state.val, state);
}
}
function init(adapterInstance) {
adapter = adapterInstance;
}
exports.default = {
init,
setOrUpdateObject,
deleteObject,
processObjectQueue,
loadExistingObjects,
getObject,
handleStateChange,
existingStates: exports.existingStates,
adapterObjects: exports.adapterObjects,
};
//# sourceMappingURL=objectHelper.js.map