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.

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.

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.

Stop bickering children!

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

1 Like

This is an old and recurring topic.

I ended up developing my own solution for personal use, but I have decided to make it publicly available after more than a year of stable use.

Hopefully this will help.