npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

epdoc-node-red-hassio

v1.0.0

Published

Node-RED code for my own Home Assistant deployment

Downloads

8

Readme

epdoc-node-red-hautil

General purpose utilities for use in Function Nodes with Node-RED and Home Assistant.

  • Service wrapper, to generate payloads for use with the Call Service node.
  • HA wrapper, to retrieve state from home assistant

Developer Notes

This module was originally written in ES6 and transpiled using Babel to generate a module that could be loaded using require or import. Soon thereafter it was migrated to TypeScript (developer hint: this resulted in catching quite a few bugs). It was also migrated to Bun for package management and unit testing, however the Typescript Compiler (tsc) is used for module generation, due to limitations in bun's bundling options .

OUTDATED SINCE MOVING TO TSC: Bun generates a different type of module that can only be loaded in Node-RED using a dynamic import, as you will see in the next section.

git clone epdoc-node-red-hautil
cd epdoc-node-red-hautil
bun install
bun test
bun run build

Installation and Use

Perhaps the most predictable way to install this package with Home Assistant is to manually add this dependency to the Node-RED package.json file and restart Node-RED. Node-RED is restarted from Settings > Add-ons > Node-Red. The restart should cause the module to be installed and available. For module updates you can edit the version number in package.json, delete the corresponding folder under node_modules, then restart Node-RED. The Node-RED add-on will install the missing packages as part of it's startup procedure.

    "dependencies": {
        "epdoc-node-red-hassio": "^0.19.2",
        "epdoc-node-red-hautil": "^0.17.6",
        "epdoc-timeutil": "^2.3.6",
        "epdoc-util": "^0.5.1",
        ...
    }    

For convenience you can add the module to globals, so that you don't need to specify the module in each Function Node where it is used. Here are the required changes to /config/Node-RED/settings.json for this to work:

let settings = {

  ...
  
  functionGlobalContext: {
    // os:require('os'),
    "epdoc-util": require('epdoc-util'),
    "epdoc-timeutil": require('epdoc-timeutil'),
    "epdoc-node-red-hautil": require('epdoc-node-red-hautil'),
    "epdoc-node-red-hassio": require('epdoc-node-red-hassio')
  },

  ...

};

module.exports = settings;

A convenient way of making these modules available in Function Nodes is to initialize them in a launch-time script. Here is an example of such a script. Paste this code into a new Function Node and use an Inject Node to execute the function. The inject node should be set to inject once, after maybe 3 seconds.

const names = {
    util: 'epdoc-util',
    timeutil: 'epdoc-timeutil',
    hassio: 'epdoc-node-red-hassio',
    hautil: 'epdoc-node-red-hautil'
};
const lib = {};
const fail = [];
Object.keys(names).forEach(key => {
    const name = names[key];
    lib[key] = global.get(name);
    if (!lib[key]) {
        fail.push(name);
    }
});
if (fail.length && flow.get('load_error') !== true) {
    flow.set('load_error', true);
    node.warn(`Error loading modules ${fail.join(', ')}`);
    lib.fail = true;
}
lib.haFactory = lib.hautil.newHAFactory(global);
lib.utilFactory = lib.hassio.NodeRedFlowFactory(global);

global.set('epdoc', lib);
return msg;

This can be shortened using this supplied module loading function.

const modules = {
    util: 'epdoc-util',
    timeutil: 'epdoc-timeutil',
    hassio: 'epdoc-node-red-hassio',
    hautil: 'epdoc-node-red-hautil'
};
const lib = global.get('epdoc-node-red-hautil').loadModules(global,modules);
lib.utilFactory = lib.hassio.newNodeRedFlowFactory(global);
if( lib.load_errors.length ) {
    node.warn(`Error loading modules ${lib.load_errors.join(', ')}`);
}
global.set('epdoc',lib);

And finally, to use the modules in Function Nodes, it's simply a matter of accessing the global context to get the module. In this first example, the Function Node has two outputs, with the 2nd output wired to a Call Service node.

const lib = global.get("epdoc");
const payload = lib.hautil.newLightService('master_bedroom').on().payload();
node.send([null,{payload:payload}]);
node.send([msg,null]);
node.done();

Also

const lib = global.get("epdoc");
const ha = lib.haFactory.make();
node.warn( `Living room light is ${ha.entity('light.living_room').value()}` );

Or

const lib = global.get("epdoc");
const fanControl = lib.utilFactory.makeFanControl({env:env,flow:flow,node:node});

Unfortunately there is no code completion for these imported modules from within Node-RED's Function Node editor.

You can find a more exhaustive and OUTDATED discussion of various ways to use your own libraries in Node-RED here.

Service Class

The Service object is used to build a payload that can be passed to the Call Service node. Provided too are a number of subclasses for specific types of entities, including SwitchService, LightService, AlarmService, CoverService, FanService and, finally FanSpeed6Service, which is a 6-speed fan that uses a Bond Bridge to set the fan speed and a smart switch to turn the fans on and off.

There is the possibility for many more subclasses to be written, or you can build your service payload directly using the base Service class, or one of the other subclasses.

The following shows the code for a function node that uses three equivalent implementations to tell a Cover to stop.

let payload = newService('cover.garage').service('stop_cover').payload();

payload = new CoverService('garage').stop().payload();

let payloadBuilder = newCoverService('garage');
payload = payloadBuilder.stop().payload();
msg.payload = payload;
return msg;

The following function node code creates a payload that can be used to set a light's brightness to 50%.

msg.payload = new LightService('bedroom').percentage(50).payload();
return msg;

The following function node code shows several ways to create a payload that turns a light on.

// In this example we directly use the LightService, 
// which will set the domain to `light` for us. 
// The LightService is a subclass of SwitchService.
msg.payload = new LightService('bedroom').on().payload();

// In this example we use the SwitchService, but change it's default
// domain from `switch` to `light` by specifying the full `entity_id`.
msg.payload = new SwitchService('light.bedroom').on().payload();

// Override the default domain using the `domain` method.
msg.payload = new SwitchService('bedroom').domain('light').on().payload();
return msg;

HA Class

The HA class is again meant for use in Function Nodes. It provides a wrapper for a Home Assistant instance, and has methods to access the state of Home Assitant entities.

Example retrieves the state of a light.

const gHA = global.get('homeassistant');

const ha = new HA(gHA);
const lightEntity = ha.entity('light.bedroom');
const isOn = lightEntity.isOn();
node.warn(`The ${lightEntity.id} is ${isOn?'on':'off'}`)

HA retrieveSensorsData method

This method takes a dictionary containing an id field and optional type field and retrieves sensor data for the listed sensors. This is a shortcut that you might use when you have multiple sensors that you efficiently want to get data for, and you need to access that data more than once.

const gHA = global.get('homeassistant');
const ha = new HA(gHA);

const sensorDict = {
  sensor1: { id: 'input_boolean.evening', type: 'boolean' },
  sensor2: { id: 'sensor.outdoor_temperature', type: 'number' }
};

ha.retrieveSensorsData(sensorDict);
if( sensorDict.sensor1.on ) {
  console.log('It is the evening');
}
if( sensorDict.sensor2.val > 30 ) {
  console.log('It is hot today');
}

The above code is equivalent to the following:

const gHA = global.get('homeassistant');
const ha = new HA(gHA);

if( ha.entity('input_boolean.evening').isOn() ) {
  console.log('It is the evening');
}
if( ha.entity('sensor.outdoor_temperature').asNumber() > 30 ) {
  console.log('It is hot today');
}