node-red-contrib-smartnora
Version:
Google Smart Home integration via Smart Nora https://smart-nora.eu/
176 lines (175 loc) • 8.66 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
const nora_firebase_common_1 = require("@andrei-tatar/nora-firebase-common");
const rxjs_1 = require("rxjs");
const util_1 = require("./util");
module.exports = function (RED) {
RED.nodes.registerType('noraf-openclose', function (config) {
var _a, _b, _c, _d;
RED.nodes.createNode(this, config);
const noraConfig = RED.nodes.getNode(config.nora);
if (!(noraConfig === null || noraConfig === void 0 ? void 0 : noraConfig.valid)) {
return;
}
const deviceType = `action.devices.types.${config.openclosetype}`;
if (!(0, nora_firebase_common_1.isDeviceType)(deviceType)) {
this.warn(`Device type not supported: ${deviceType}`);
return;
}
const directions = config.directions;
let openCloseDirections = (_a = directions === null || directions === void 0 ? void 0 : directions.split(',')) === null || _a === void 0 ? void 0 : _a.filter(f => !!f);
if (!(openCloseDirections === null || openCloseDirections === void 0 ? void 0 : openCloseDirections.length)) {
openCloseDirections = undefined;
}
if (openCloseDirections === null || openCloseDirections === void 0 ? void 0 : openCloseDirections.some(d => !(0, nora_firebase_common_1.isOpenCloseDirection)(d))) {
this.warn(`Open/Close direction not supported: ${directions}`);
return;
}
const useDiscreteValues = (_b = config.discrete) !== null && _b !== void 0 ? _b : true;
const useOpenCloseDefinedValues = useDiscreteValues && !openCloseDirections && !config.lockunlock;
const { value: openValue, type: openType, } = (0, util_1.convertValueType)(RED, config.openvalue, config.openvalueType, { defaultValue: true });
const { value: closeValue, type: closeType, } = (0, util_1.convertValueType)(RED, config.closevalue, config.closevalueType, { defaultValue: false });
const deviceConfig = {
type: deviceType,
traits: ['action.devices.traits.OpenClose'],
name: {
name: config.devicename,
},
roomHint: config.roomhint,
willReportState: true,
state: Object.assign({ online: true }, ((openCloseDirections === null || openCloseDirections === void 0 ? void 0 : openCloseDirections.length)
? {
openState: openCloseDirections.map(direction => ({
openDirection: direction,
openPercent: 0,
}))
}
: {
openPercent: 0,
})),
noraSpecific: {
returnOpenCloseErrorCodeIfStateAlreadySet: !!config.errorifstateunchaged,
},
attributes: {
discreteOnlyOpenClose: useDiscreteValues,
openDirection: openCloseDirections,
commandOnlyOpenClose: (_c = config.commandonly) !== null && _c !== void 0 ? _c : false,
queryOnlyOpenClose: (_d = config.queryonly) !== null && _d !== void 0 ? _d : false,
},
};
if (config.lockunlock) {
deviceConfig.traits.push('action.devices.traits.LockUnlock');
if ((0, nora_firebase_common_1.isLockUnlock)(deviceConfig)) {
deviceConfig.state.isLocked = false;
deviceConfig.state.isJammed = false;
}
}
(0, util_1.registerNoraDevice)(this, RED, config, {
deviceConfig,
updateStatus: ({ state, update }) => {
let stateString = 'openPercent' in state
? openPercent(state.openPercent)
: state.openState.map(s => `${s.openDirection}:${openPercent(s.openPercent)}`).join(', ');
if (isLockUnlockState(deviceConfig, state)) {
if (state.isJammed) {
stateString += ' jammed';
}
else {
stateString += ` ${state.isLocked ? 'locked' : 'unlocked'}`;
}
}
update(stateString);
},
mapStateToOutput: state => {
if (useOpenCloseDefinedValues && 'openPercent' in state) {
if (state.openPercent === 0) {
return {
payload: (0, util_1.getValue)(RED, this, closeValue, closeType),
};
}
else {
return {
payload: (0, util_1.getValue)(RED, this, openValue, openType),
};
}
}
else {
const payload = { online: state.online };
if (isLockUnlockState(deviceConfig, state)) {
payload.locked = state.isLocked;
payload.jammed = state.isJammed;
}
if ('openPercent' in state) {
payload.open = state.openPercent;
return {
payload,
};
}
else {
for (const directionState of state.openState) {
return {
payload: Object.assign(Object.assign({}, payload), { open: directionState.openPercent, direction: directionState.openDirection }),
};
}
}
}
},
handleNodeInput: async ({ msg, updateState, state$ }) => {
var _a;
if (!useOpenCloseDefinedValues) {
const state = await (0, rxjs_1.firstValueFrom)(state$);
const payload = Object.assign({}, msg.payload);
if ((openCloseDirections === null || openCloseDirections === void 0 ? void 0 : openCloseDirections.length) && 'openState' in state) {
if (typeof payload === 'object' && 'open' in payload) {
const payloadDirection = (_a = msg === null || msg === void 0 ? void 0 : msg.payload) === null || _a === void 0 ? void 0 : _a.direction;
const direction = typeof payloadDirection === 'string'
? payloadDirection.trim().toUpperCase()
: null;
payload.openState = state.openState.map(st => ({
openDirection: st.openDirection,
openPercent: st.openDirection === direction || direction == null
? msg.payload.open
: st.openPercent,
}));
delete payload.open;
delete payload.direction;
}
}
await updateState(payload, [{
from: 'open',
to: 'openPercent',
}, {
from: 'locked',
to: 'isLocked',
}, {
from: 'jammed',
to: 'isJammed',
}]);
}
else {
const myOpenValue = (0, util_1.getValue)(RED, this, openValue, openType);
const myCloseValue = (0, util_1.getValue)(RED, this, closeValue, closeType);
if (RED.util.compareObjects(myOpenValue, msg.payload)) {
await updateState({ openPercent: 100 });
}
else if (RED.util.compareObjects(myCloseValue, msg.payload)) {
await updateState({ openPercent: 0 });
}
else {
await updateState(msg.payload);
}
}
},
});
function openPercent(percent) {
switch (percent) {
case 0: return 'closed';
case 100: return 'open';
default: return `${percent}%`;
}
}
function isLockUnlockState(device, _state) {
return (0, nora_firebase_common_1.isLockUnlock)(device);
}
});
};