HA scripting: Are real scripting languages possible? What are the performance differences?

I’m a hobby coder, able to easily create code in a couple of languages. Like many coders, I’m not too fond of a UI-method of creating an automation (HA automation / Node-RED), so I’m looking for alternatives.

  1. Is it possible to use a real scripting language for HA automating? Something like JS, C++, lua, etc…
  2. If it is possible, I suspect that there will be quite some overhead. Am I right in assuming that the most efficient way of doing things would be HA automations > Node RED > custom script files?

Check out appdaemon to see if it meets your needs:

1 Like

I’d also recommend AppDaemon if you’re ok with Python. I started out using Node-RED, but I’ve converted all my automations to AppDaemon a while ago and very happy with it so far.

1 Like

Appdaemon is great if you like Python I agree. There’s a newer integration called Pyscript, which I don’t personally use but looked into, looks good for quick one off automations. More difficult than app daemon to create (conceptually, IMO) reusable code, but that maybe my unfamiliarity with it.

Just a note, while Python and Javascript are scripting languages, C++ requires compilation and is therefor not a scripting language.

2 Likes

Thanks for the suggestions. Can you guys tell me something about system performance / automation execution times? If running a python script takes twice as much power or takes twice as long as doing the same thing in the HA automation system, it’s probably not really worth it.

Interesting, where does that leave C++ then?

Home Assistant is written in Python, so that’s the starting point, and anything that you do with integrations or connected devices will still end up routing back through the HA Python code. Running user-provided scripts and automations presumably adds some overhead on top of that, but I expect minimal, if any.

To answer your question directly, you shouldn’t see any meaningful performance difference between running Python code through AppDaemon or PyScript or writing an automation or script.

Interesting, where does that leave C++ then?

A compiled language.

3 Likes
  1. If it is possible, I suspect that there will be quite some overhead. Am I right in assuming that the most efficient way of doing things would be HA automations > Node RED > custom script files?

That’s one way to do it, but not the only way. As already said, appdaemon (+1) and pyscript would do too. You can also use the HA REST interface and/or use MQTT, so you may write code in whatever you like as long as it has libraries to interface with it. Also, dont forget that you can also create your HA automations using yaml directly instead of the UI (all my “standard” HA automations are just yaml files in “automations.d” directory).

Yaml is very quick to learn and pretty powerful in ha.
It’s easy to read and logical.

I have the same question as the OP; what I am currently doing is using node-red but where 99% of the logic is written in big blobs of javascript inside node-red “function” nodes. Still, probably two thirds of the effort is spent writing yucky glue code (and dragging in yucky glue nodes) to jump between these multitude of weird environments. I’d prefer to replace this with native python_script components but the documentation doesn’t seem great and I’ve never coded in python before so it’d be a bit of a curve.

The other thing I’m doing is using esphome on all of my ESP8266 devices and making them as smart as possible at the edge. This means the Home Assistant automations can be as simple as possible.

1 Like

The whole point of using something like AppDaemon (which I’d also recommend to Python-literate people) is to do more than can be done with HA automation — and automation execution times have never been a concern even on some pretty slow hardware.

For example, I use AppDaemon to scrape my bin collection dates from my council website (which is more complex than the standard Scrape sensor can deal with) and populate some sensors in HA. I then use a standard HA YAML automation to send me a notification based on those sensors.

If I may ask, what kind of processing are you doing on the ESP, for what kind of devices? Do you mean something like aggregating temperature, humidity, battery level, etc. into (for example) one message containing JSON, instead of sending multiple separate messages?

Well like I said, I’m new to HA, so I don’t really know what to expect from certain aspects of it. I come from Domoticz, where I wrote all my scripts in DzVents (Lua), but that always had a certain lag in it. For example, I wrote a script that turns on multiple lights at a certain brightness depending on time of day when a PIR is triggered. Between triggering the PIR and the lights turning on there’s about 1 to 1.5 seconds of delay. Sure, some of that is from the wireless communication, but a majority of it was caused by running that Lua code. If I directly linked a device to the PIR device in Domoticz, the response times were much faster.
So basically, I’m just trying to find out what’s best for me, preferring scripts over blockly-like ventures, but really requiring decent response times. If scripts are available with low response times (which seems to be the case), great! If scripts are available, but take considerably longer to run than HA automations, I’d probably want to stick to HA automation, at least for some things.

It’s fractions of a second here on my Synology NAS to receive a PIR signal from an ESP8266 that triggers an automation, check a light level coming in from another ESP8266 as a condition, and flip the relay on the Shelly 1 that controls the light as the action.

I also have the physical light switch input to that Shelly turning the light on which it does via communication with HA rather than directly, and it is only just about perceptible that it’s not a direct connection.

HA automations are fast, and can (for now, at least) be written manually in YAML rather than via the UI. Almost all of mine are hand-written.

For those use cases I prefer to write custom_components. The first one you wrote is kind hard, but then all the other follow the same pattern: scrape something from the web/api, update the sensors.

I have my garage door sensor able to control my one-button motor fairly well. It also does presence detection and distinguishes between two different cars. I also have template sensors which change state when certain unexpected behaviours occur such as the door sensor state changing without motor operation.

One of the things I’m often doing is replicating the behaviour of sensor lights and bathroom exhaust fan timers. For those switches I have esphome fully managing the timer so that HA doesn’t need to. It also looks after repeated triggers (these just restart the countdown timer) or when the light/fan was already switched on manually (the trigger is a no-op) or when the light/fan is switched on/off while the timer is running (the timer is cancelled). All this makes the HA automations dead simple—a fully functional, fully resolved sensor light is nothing more than “when motion is detected, turn this switch on”.

The main bit of complex code I still have running in Node-Red is my script to manage the ducted air conditioner. I’ve got a few bits of logic running in tandem here—effectively bypassing its internal thermostat in favour of temperature sensors in multiple rooms. The system smoothly transitions between multiple temperature sensors AND a varying target temperature throughout the day and night.

This AC controller used to be a huge mess of Node-Red nodes; now it has been simplified into a single function node. (Plus a few other nodes for input/output.)

2 Likes

Would you mind sharing your ESPHome YAML for such an example. I currently have an automation in HA to handle that functionality, but it might be nice to move it to the switch directly.

Thanks!

Here’s an excerpt from my Sonoff Mini module. Should apply to anything you want to expose as a light (rather than a switch) in HA.

#================== LIGHTS ==================
output:
  - platform: gpio
    id: the_relay
    pin: ${pin_relay_1}
    
light:
  - platform: binary
    id: the_light
    output: the_relay
    name: ${entity_prefix}
    on_turn_off: # Any manual toggle kills the timer
      - script.stop: timer_script
    on_turn_on:
      - script.stop: timer_script

#================== TIMERS ==================
# Emulates a timed switch
switch:
  - platform: template
    id: timer_switch
    name: ${entity_prefix} Timer
    turn_on_action:
      - if:
          condition:
            light.is_off: the_light
          then: # If light is off, turn it on and start the timer
            - light.turn_on: the_light # This also kills the timer
            - script.execute: timer_script # So let's stary it again
          else: # If light is already on
            - if:
                condition:
                  script.is_running: timer_script
                then: # If light is on because of timer, restart timer
                  - script.stop: timer_script
                  - script.execute: timer_script
                # else:
                #   Do nothing
      - delay: 1s
      - switch.turn_off: timer_switch # The switch doesn't stay on

script:
  - id: timer_script
    then:
      - delay: ${timer_duration}
      - light.turn_off: the_light
2 Likes

Thanks. That is an elegant solution.

I will be borrowing that for sure.

Sorry to hijack this thread but this is something Im interested in.

Ive managed to write my first python code to scrape my council for rubbish days and it works well in a python environment but Im stuck in getting this into AppDaemon to populate sensors in HA.
I have been reading up and playing around with AppDaemon in HA but wondered if you can advise on how to go about this please.

This is perhaps another route, but again I’m confused how to integrate the python code I’ve written.

Any help would be appreciated.

take a look into to another sensor or custom_component sensor code. And publish your code on github so others can see/help.

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.