Writing automations in pure JavaScript—is this possible?

Hey there, new to HA. I spent the last week or so setting up HA and toying around with Node-RED. I created a very convoluted flow that handles my motion sensors, but all throughout I kept asking myself why I couldn’t just write a bunch of JavaScript code to the same end. I am a programmer, so that would feel more natural to me, and most of the time I spent using Node-RED was actually me trying to wrangle its nodes and data flow to do what I wanted it to do. Storing state was a particularly difficult bit, I solved it by using the state machine node but it took a lot of tinkering. Debugging is another particularly painful story in my experience with Node-RED, as it’s hard to inspect exactly what you want to inspect or curate the debug messages that you get, unless you spend effort setting up and tearing down debug nodes.

If I could write the same code using JavaScript and the async/await pattern, I feel I’d end up more productive and with a resulting automation that is easier for me to inspect.

I tried searching around a bit, but I haven’t found much to this end. What I’m picturing is something just like Node-RED, where you can write scripts using a simple API that lets you access events and entities. I really love the simplicity of Node-RED where deploying a flow just takes a button click, something similar to that but with code would be awesome.

I imagine this all could be done by writing my own software and using the Websocket JS API but again that would require me to manage the scripts themselves, set them up and launch them as daemons, and manage their lifecycle if I have to update them or if something crashes. It’d all be easier with a “managed” approach similar to what Node-RED provides.

I wonder if anything like this exists for HA?

3 Likes

I’m not sure about JS specifically, but you could certainly write a connector using the HA API. Would probably be a lot of work. Maybe someone did this already.

That said, there’s something like that for Python at least:

It’s really nice, I use it for almost all my automation and scripts now. It just feels so much more natural to write it in code compared to the built-in HA automations or NodeRed.

You might want to take a look at Tileboard too. It’s a Javascript UI over Home Assistant, using the Websocket API. I use it as my main UI. Not exactly what you needed, but it could be a starting point for writing your own JS code around the HA websockets API.

1 Like

There is also a C# api. New NetDaemon Release: Use C# to automate Home Assistant - Home Assistant

If you use the function node then you can write JavaScript.
Regarding debugs being hard? Tell us the problems instead of dramatizing it and we can help you how to use the debug tools in Node red.

No drama intended. The main struggle I’ve had with debug nodes is how much effort it takes to set them up and make sure they output the right thing (since my choice is between a single field on the message object, or the whole object, or writing an expression using jsonata). Next to that, the debug panel has been slightly difficult to use mostly because it’s cramped and can get crowded quickly when some of your nodes are overly chatty (whereas for example the chrome devtools console collapses multiple messages into one when they’re the same). So it’s just a bunch of paper cuts adding up to some friction. Compared to a scripting language where you can just print the data you’re interested in with very few keystrokes, they are a bit of extra effort in my opinion.

3 Likes

Thanks! My python is a bit rusty but that API looks very good. In the absence of a JavaScript version that might be what I use.

If you have a chatty node then I suggest you use the trigger block node.

This will let one message pass and block all others meaning you can inspect the debug for any length of time then reset the block when you need a new debug message.

[{"id":"faa7554e3b2390b7","type":"debug","z":"14ca354715bc92f3","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":610,"y":680,"wires":[]},{"id":"c4ed3ba8895f36e7","type":"inject","z":"14ca354715bc92f3","name":"Chatty node","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payloadType":"date","x":190,"y":680,"wires":[["fe8428efd0cbd83e"]]},{"id":"9909ce9dbfca246a","type":"inject","z":"14ca354715bc92f3","name":"reset","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"reset","payloadType":"str","x":220,"y":580,"wires":[["fe8428efd0cbd83e"]]},{"id":"fe8428efd0cbd83e","type":"trigger","z":"14ca354715bc92f3","name":"","op1":"","op2":"0","op1type":"pay","op2type":"str","duration":"0","extend":false,"overrideDelay":false,"units":"ms","reset":"reset","bytopic":"all","topic":"topic","outputs":1,"x":420,"y":680,"wires":[["faa7554e3b2390b7"]]}]

I always use complete message since only getting one part of the message will cause you to make a mistake.
Although I’m not a JavaScript developer (but many other languages), I do enjoy Node Red for the mix of coding and visual aid of what is going on, message variables and the global variables. It all makes it perfect in my opinion.

Thankfully you can use NodeJS to do basically anything you want using the node-homeassistant API which is extremely easy to use. If anyone knows how to use this API or another method to get “push” updates from HA, please share and try to include as complete an example as possible, really appreciated.

NodeJS is great for performing calculations, managing/transforming data, routing information as well as adding extended functionality to HA.

Personally neither Automations nor NodeRed worked for me, way too inconcise and fiddly. In my limited experience, Automations seemed to be very unreliable and flaky and managing data, performing calculations and creating functions in NodeRed is probably the most agonizing experience I think I’ve ever had in my life.

If you know javascript, i wouldn’t even bother using automations and helpers, etc because its just so difficult to do anything complicated. If you dont know JS/NodeJS and automations arn’t working for you, i don’t suggest NodeRed, it’s so cumbersome and data management and logic is far more difficult than just doing in JS. So i really don’t see the point and system impact of NodeRed is hardly justified. In the same amount of time you could just as well get comfortable with JS/NodeJS and be far better off. It’s the most well documented and explained language on earth, you’ll be just fine.

Lets have a look:

Install NodeJS, and do a local install of “homeassistant” via NPM

Simple example usage of the API is here: https://www.npmjs.com/package/homeassistant
Just generate your token from your profile settings in HA, make sure to copy paste the WHOLE long line.

API is extremely straight forward. I found it easy enough in just minutes to:

  • Verify API is connected to the HA instance
hass.status()
    .then(data => { console.log('HA Status: ', data.message); })
    .catch(err => console.error(err));
  • List out all names of entities you can interact with using:
hass.states.list()
    .then(data => console.log(data))
  • Read the state of a sensor, ESP Sensor or switch
hass.states.get('sensor', 'sensor.esp_filter_psi_filter')
    .then(data => press.filter = Number(data.state))
//consider using a catcher for when HA starts dropping the ball it wont crash your script
    .catch(err => console.error(err));
  • Set the state of a switch or input boolean “switch helper”
hass.services.call('turn_off', 'switch', { entity_id: 'switch.esp_tazok_relay_luckypro' })
hass.services.call('turn_on, 'input_boolean', { entity_id: 'input_boolean.auto_pump' })
  • Automatically create a sensor inside HA and continually update that sensor
hass.states.update('sensor', 'flow_hour', {
   state: Number(flow.hour).toFixed(2), attributes: { unit_of_measurement: 'm3' }
});
  • Update the value of a “number helper” inside HA
hass.services.call('set_value', 'input_number', { entity_id: 'input_number.tank_mountain', value: tank.mountain.level })

So with that alone, you could do just about anything you wanted. If any knows how to get push data from HA into NodeJS using the “homeassistant” NPM or via another means like websocket etc, please share that.

That being said, in order to have a functioning automation without push capability (which i dont know how to do), then obviously you need to “get” the state of the things you want from HA in some interval so you can do something with it. Once a second seems to work fine for me but you could go less if you had the need.

 setTimeout(() => { 
        try {
            hass.states.get('sensor', 'sensor.esp_filter_flow_filter')
                .then(data => flow.lm = Number(data.state))
            hass.states.get('sensor', 'sensor.esp_filter_flow_total_filter')
                .then(data => isNaN(Number(data.state)) == false ? flow.temp = Number(data.state) : flow.temp = 0);
            hass.states.get('sensor', 'sensor.esp_tazok_psi_tank_tazok')
                .then(data => press.tazok = Number(data.state))
            hass.states.get('input_boolean', 'input_boolean.auto_pump')
                .then(data => data.state == "on" ? auto.tazok.state = true : auto.tazok.state = false)
            hass.states.get('switch', 'switch.esp_tazok_relay_compressor')
                .then(data => data.state == "on" ? pump.air = true : pump.air = false)
        }
        catch (error) { console.log(error) }
 }, 1000);

For users new to NodeJS and want a more complete example, just ask.

You’re constantly polling an event driven architecture. While this can work for some very specific scenarios, it’s generally a bad idea in terms of performance and missing of momentary state changes that happen faster than your poll rate.

And while that may be subjective, the syntax used doesn’t look elegant or intuitive at all, but really bloated and confusing. That could probably be improved by wrapper functions, but still. For a real automation replacement you will need to support event driven triggers.

Pulling event data from anything running python admittedly isn’t preferable but the point is, it works completely fine. It has already corrected issues that were not even worth fixing within HA itself nor NodeRed. Besides, the HA websocket API should work perfectly fine in NodeJS and I’m very excited about perusing that further. But something that could be considered a “bad idea” is superseded by tangible value, which is the point Im making here and in direct anecdote to the authors question.

Well thats a direct transplant of the authors own example. I find a two line function to be perfectly straight forward and intuitive. There’s no intention or possibility here of making non JS programmers forgo the necessity of learning the language first before replacing the entire HA automation function with node JS. Thats the only context in which your comment makes sense witch itself doesnt make any sense.

That being said., the subject of this thread is “Writing Automations in Pure JavaScript”. The answer is unequivocally yes and on multiple levels, event pulling and websocket being just two. I have shared some examples and shown how this idea actually has a lot of merit and should be pursued and therefore have brought value to the discussion.

When i have a working example of websocket, ill be sure and share it here, though it’ll be oriented towards JS programmers who are comfortable with NodeJS because that’s the subject of this thread.

Even for JS programmers (and actually specifically for them), this convoluted syntax to access entity state looks horrible. There’s too much repetition, too much boilerplate code and running your entire automation logic over an async promise on the state getter is, well, really weird. Aside from the strange syntax, it can also create race conditions and is non-deterministic if one automation depends on the result of another, or one using the output of an automation as input, as execution order will depend on how long it takes the API to fulfill the individual promises (ie. it’s random).

Of course this can be improved by providing a better interface around this API.

No it’s not, at least not like this. It cannot be a replacement without being entirely event driven. As mentioned, for certain specific cases brute force polling may work (more or less). But it’s far from being a general replacement.

It working for your specific use case doesn’t invalidate the fact that doing this a bad idea.

Dude, im not the author of this API so why you talking to me like i am?

HeyImAlex
While I really appreciate feedback, you should target your disgust towards the author of the API’s and the examples they have provided as i said twice now its was the example they provided. Nit picking the style of a few lines of example code brings value to nobody and is basically just rude; i wouldn’t suggest making a habit of that.

For whatever reason you’ve now twice ignored my mention of the websocket API which is obviously more preferable than pulling API but yet insist on blasting even my mention of it. Obviously the creator felt it had merit for their use case so just leave it at that.

Your getting further and further off the threads topic. I never meant to imply “replacement”, I’m simply providing input about “Writing Automations in Pure JavaScript” in a way that I’ve been experimenting with lately. I just want to share my experience with the community.

Being rude, intentionally ignoring half of what the person says just so you can walk all over them, providing no alternative examples in return and putting words in a persons mouth is a “bad idea”

And to the rest of the world. I will continue to explore using the HA websocket API with NodeJS for automations and post here when i have a good working example. I’ll also link to or provide a more complete example on the pulling method thats currently working well for me.

Personally, I dont want to use HA for anything but an android and web GUI. I dislike its automation logic and data management scheme; using helpers and performing data manipulation is awful as far as im concerned but for a GUI its really nice. I think using NodeJS for logic and Data manipulation and HA as only a front end makes a lot of sense. i appreciate anyone who can provide some ideas on how to go about that. Brainstorming flourishes when the discussion is constructive, mutual and courteous.

1 Like

Stop bickering children!

The fact that the JS implementation hasn’t been updated in half a decade is surely a worry.

1 Like

Persons interested in using NodeJS, have a look at my NodeJS Home Assistant example framework.

To discuss this further, go to this topic.