Scaling Arduino Sensor Values to 4-20 with Python Script or Template Sensor

@gpbenton & other people who know a little python or Jinja2

I am using the arduino component to bring in analog sensor values. These values are scaled differently then the needed outcome. With a sensor connected to the arduino I get a range of ~190 to 965 but the required scale is 4 to 20.

My script below uses the homeassistant.remote module to read the current values and create a new entity with the correct state.

As you can see from the image there are 8 total inputs that need to be handled but right now my script is only taking care of one of them sensor.A0 creates sensor.analog…

image
My question is: What is the most efficient way to handle 8-16 sensors in this fashion?

It would be nice to make the transaction occur only when a A* sensor changes state.
It should also be possible to create a single function with the equation and then pass the state of the changed entity through that function. Correct?

Or would be best thing be to repeat my script for every A* sensor?

*Side note: I tried a template sensor in the beginning but I couldn’t figure out how to accomplish the math with Jinja2. I am open to other resolutions…

Regards,
Mike

import string
import math
import time

from decimal import getcontext, Decimal
getcontext().prec = 4

import homeassistant.remote as remote
api = remote.API('127.0.0.1')

while True:
    # get state of arduino analog sensor
    a0 = int(remote.get_state(api, 'sensor.a0').state)

    # perform scale equation 
    iMin = int(190)
    iMax = int(965)

    oMin = int(4)
    oMax = int(20)

    ispan = int(iMax - iMin)
    ospan = int(oMax - oMin)

    a0_scaled = Decimal((a0 - iMin) / ispan)
    a0_output = Decimal(oMin + (a0_scaled * ospan))

    #update state of new entity with scaled values between 4 and 20 "mA"
    if a0_output <= 4:
        remote.set_state(api, 'sensor.analog', new_state=str(4.0), attributes={'unit_of_measurement': 'mA'})
        time.sleep(30)
    elif a0_output >= 20:
        remote.set_state(api, 'sensor.analog', new_state=str(20.0), attributes={'unit_of_measurement': 'mA'})
        time.sleep(30)
    else:
        remote.set_state(api, 'sensor.analog', new_state=str(a0_output), attributes={'unit_of_measurement': 'mA'})
        time.sleep(30)

The standard way of doing the sort of manipulation in HA is to use a template sensor, with the template doing the calculations in jinja2. I felt any time spent learning to use jinja2 was a complete waste, so I will let someone else describe how to do it that way.

Personally, I would use an appdaemon app to do this. You could trigger the app to run only when the state of a sensor changed, and use the same code but with different sensor parameters. I use appdaemon to write all my automations in python, and I highly recommend it.

If you still want to carry on with using your script, the simplest way would be to create a list of the sensors you wish check, and then loop through that list within your while True: loop. And certainly create a separate function for doing the actual calculation.

You could create a separate process for each update, and call the script with a parameter of the sensor, but that would just give you more processes to manage - you will need to start these processes on startup, and make sure they don’t stop.

Getting your script to only run when the input state changes is tricky - that is what appdaemon is for :grinning:

1 Like

Appdaemon seems a bit scary at first glance but I will look into it and give it a shot before I come back with a ton of questions.

The template sensor would be the easiest but appdaemon would be the cleanest and I could use it for some of the other scripts I have made.

I will keep you posted on my progress.

Thanks you!