UNPKG

node-red-contrib-home-assistant-websocket

Version:
198 lines (197 loc) 6.83 kB
"use strict"; const slugify = require('slugify'); const EntityNode = require('../EntityNode'); const nodeOptions = { config: { state: {}, stateType: {}, attributes: (nodeConfig) => nodeConfig.attributes || [], resend: {}, outputLocation: {}, outputLocationType: {}, inputOverride: {}, }, input: { state: { messageProp: 'payload.state', configProp: 'state', default: 'payload', }, stateType: { messageProp: 'payload.stateType', configProp: 'stateType', default: 'msg', }, attributes: { messageProp: 'payload.attributes', configProp: 'attributes', default: [], }, }, }; class Sensor extends EntityNode { constructor({ node, config, RED, status }) { super({ node, config, RED, status, nodeOptions }); } async registerEntity() { if (!this.isIntegrationLoaded) { this.error(this.integrationErrorMessage); this.status.setFailed('Error'); return; } if (this.registered) { return; } const config = {}; this.nodeConfig.config .filter((c) => c.value.length) .forEach((e) => (config[e.property] = e.value)); const payload = { type: 'nodered/discovery', server_id: this.nodeConfig.server.id, node_id: this.node.id, component: this.nodeConfig.entityType, config, }; // Add state and attributes to payload if resend enabled if (this.nodeConfig.resend && this.lastPayload) { payload.state = this.lastPayload.state; payload.attributes = this.lastPayload.attributes; } this.debugToClient(payload); this.node.debug(`Registering ${this.nodeConfig.entityType} with HA`); try { await this.homeAssistant.send(payload); this.status.setSuccess('Registered'); this.registered = true; } catch (err) { this.status.setFailed('Error registering'); this.node.error(`Error registering entity. ${err.message ? ` Error Message: ${err.message}` : ''}`); } } onClose(removed) { super.onClose(removed); if (this.registered && this.isIntegrationLoaded && removed) { const payload = { type: 'nodered/discovery', server_id: this.nodeConfig.server.id, node_id: this.node.id, component: this.nodeConfig.entityType, remove: true, }; this.node.debug(`Unregistering ${this.nodeConfig.entityType} from HA`); this.homeAssistant.send(payload); } } onInput({ parsedMessage, message, send, done }) { if (!this.isConnected) { this.status.setFailed('No Connection'); done('Sensor update attempted without connection to server.'); return; } if (!this.isIntegrationLoaded) { this.status.setFailed('Error'); done(this.integrationErrorMessage); return; } let state = parsedMessage.state.value; let stateType = parsedMessage.stateType.value; if (this.nodeConfig.inputOverride === 'block') { state = this.nodeConfig.state; stateType = this.nodeConfig.stateType; } else if (parsedMessage.state.source === 'message' && stateType !== 'message') { // Set default for state from input to string stateType = 'str'; } try { state = this.getTypedInputValue(state, stateType, { message }); } catch (e) { this.status.setFailed('Error'); done(`State: ${e.message}`); return; } if (state === undefined) { this.status.setFailed('Error'); done('State must be defined.'); return; } const attributes = this.getAttributes(parsedMessage); const attr = {}; try { attributes.forEach((x) => { // Change string to lower-case and remove unwanted characters const property = slugify(x.property, { replacement: '_', remove: /[^A-Za-z0-9-_~ ]/, lower: true, }); attr[property] = this.getTypedInputValue(x.value, x.valueType, { message, }); }); } catch (e) { this.status.setFailed('Error'); done(`Attribute: ${e.message}`); return; } const payload = { type: 'nodered/entity', server_id: this.nodeConfig.server.id, node_id: this.node.id, state, attributes: attr, }; if (this.nodeConfig.resend) { this.lastPayload = { state, attributes: attr, }; this.state.setLastPayload(this.lastPayload); } this.debugToClient(payload); this.homeAssistant .send(payload) .then(() => { this.status.setSuccess(state); if (this.nodeConfig.outputLocationType !== 'none') { this.setContextValue(payload, this.nodeConfig.outputLocationType || 'msg', this.nodeConfig.outputLocation || 'payload', message); } send(message); done(); }) .catch((err) => { this.status.setFailed('API Error'); done(`Entity API error. ${err.message ? ` Error Message: ${err.message}` : ''}`); }); } getAttributes(parsedMessage) { let attributes = []; if (parsedMessage.attributes.source !== 'message' || this.nodeConfig.inputOverride === 'block') { attributes = this.nodeConfig.attributes; } else { if (this.nodeConfig.inputOverride === 'merge') { const keys = Object.keys(parsedMessage.attributes.value).map((e) => e.toLowerCase()); this.nodeConfig.attributes.forEach((ele) => { if (!keys.includes(ele.property.toLowerCase())) { attributes.push(ele); } }); } for (const [prop, val] of Object.entries(parsedMessage.attributes.value)) { attributes.push({ property: prop, value: val, }); } } return attributes; } } module.exports = Sensor;