PID controller in Appdeamon

I’m trying to make a simple PID input/ouput app using Appdeamon 4
What I am trying to achieve is the following:

  • Input = ‘sensor.trv_zolder’ ( just a temperature sensor )

  • Output = ‘input_number.pid_test’( output value from PID controller which I need to control my boiler )

I’m using the following Python library ; https://github.com/m-lundberg/simple-pid
Since I don’t have any experience using Python ( only experience with programming in C/C++)i’m getting stuck.

This is app code

import appdaemon.plugins.hass.hassapi as hass
from simple_pid import PID

class PIDcalc(hass.Hass):
  def initialize(self):
     state= self.get_state("sensor.trv_zolder")
     self.log(state)
     inputset = state
     pid = PID(5, 0.2, 0.0, setpoint=inputset)controlled_system
     pid.sample_time = 10
     #while True:
     self.log(inputset)
     output = pid(inputset)
     self.log(output)
     self.log("Setting pid.test to PID value")
     self.set_state("input_number.pid_test",state=output)

The following error occurs in the log.

2020-12-03 20:32:40.454192 INFO AppDaemon: Terminating pid_control
2020-12-03 20:32:40.458250 INFO AppDaemon: Reloading Module: /config/appdaemon/apps/pid.py
2020-12-03 20:32:40.462907 INFO AppDaemon: Initializing app pid_control using class PIDcalc from module pid
2020-12-03 20:32:40.469306 INFO pid_control: 17.5
2020-12-03 20:32:40.473642 WARNING pid_control: ------------------------------------------------------------
2020-12-03 20:32:40.474324 WARNING pid_control: Unexpected error in worker for App pid_control:
2020-12-03 20:32:40.474972 WARNING pid_control: Worker Ags: {}
2020-12-03 20:32:40.475503 WARNING pid_control: ------------------------------------------------------------
2020-12-03 20:32:40.476966 WARNING pid_control: Traceback (most recent call last):
  File "/usr/lib/python3.8/site-packages/appdaemon/app_management.py", line 150, in initialize_app
    await utils.run_in_executor(self, init)
  File "/usr/lib/python3.8/site-packages/appdaemon/utils.py", line 290, in run_in_executor
    response = future.result()
  File "/usr/lib/python3.8/concurrent/futures/thread.py", line 57, in run
    result = self.fn(*self.args, **self.kwargs)
  File "/config/appdaemon/apps/pid.py", line 13, in initialize
    output = pid(inputset)
  File "/usr/lib/python3.8/site-packages/simple_pid/PID.py", line 88, in __call__
    error = self.setpoint - input_
TypeError: unsupported operand type(s) for -: 'str' and 'str'
2020-12-03 20:32:40.477675 WARNING pid_control: ------------------------------------------------------------

It looks like the ‘state’ variable=( sensor.trv_zolder ) is not accepted somehow in the Pid controller library? Since making the variable just simply ‘10’ results in some output from the Pid

2020-12-03 20:32:40.477675 WARNING pid_control: ------------------------------------------------------------
2020-12-03 21:49:29.026334 INFO AppDaemon: Terminating pid_control
2020-12-03 21:49:29.034631 INFO AppDaemon: Reloading Module: /config/appdaemon/apps/pid.py
2020-12-03 21:49:29.040271 INFO AppDaemon: Initializing app pid_control using class PIDcalc from module pid
2020-12-03 21:49:29.053253 INFO pid_control: Previous message repeated 1 times
2020-12-03 21:49:29.054360 INFO pid_control: 17.0
2020-12-03 21:49:29.055422 INFO pid_control: 10
2020-12-03 21:49:29.056470 INFO pid_control: 0.0
2020-12-03 21:49:29.060139 INFO pid_control: Setting pid.test to PID value

Can anybody help me in the right direction? I’ve tried to follow from the appdeamon docs, however there are not any examples using external libraries.

Thanks,

2 Likes

Try this:
Remove controlled_system from this line.
pid = PID(5, 0.2, 0.0, setpoint=inputset)controlled_system

Also, change inputset = state to inputset = float(state)

So you end up with:

class PIDcalc(hass.Hass):
  def initialize(self):
     state= self.get_state("sensor.trv_zolder")
     self.log(state)
     inputset = float(state)
     pid = PID(5, 0.2, 0.0, setpoint=inputset)
     pid.sample_time = 10
     #while True:
     self.log(inputset)
     output = pid(inputset)
     self.log(output)
     self.log("Setting pid.test to PID value")
     self.set_state("input_number.pid_test",state=output)

You should then feed the pid controller in a callback whenever the sensor.trv_zolder state changes. Let me know if you need help with that.

@tjntomas Thanks you very much for your help :)!

Got it working with the following code :

import appdaemon.plugins.hass.hassapi as hass
from simple_pid import PID

class PIDcalc(hass.Hass):
  def initialize(self):
    self.run_every(self.PIDrun, "now", 10)
    
  def PIDrun(self, kwargs):    
     currenttemp= float(self.get_state("climate.trv_zolder", attribute = "current_temperature"))
     settemp = float(self.get_state("climate.trv_zolder", attribute = "temperature"))
     self.log(currenttemp)
     self.log(settemp)
     pid = PID(5, 0.2, 0.0, setpoint=settemp)
     pid.sample_time = 1
     output = round(pid(currenttemp),2)
     self.log(output)
     self.log("Setting pid.test to PID value")
     self.set_state("sensor.pid_zolder",state=output)

It updates every 10 second now.

However it now appears in home assistant with ‘colors’ == state( like on/off )? Instead of a ‘value’( that creates a graph) like normally for a sensor?( check screenshot )state instead of value

i’ve tried to use the set_value command, however this won’t create a sensor automatically like set_state does? I’ve read that creating a template_sensor manually in HA and then updating it trough AD can cause vague behaviour?

Does my code look OK? I’m starting to understand the AD way of coding I think :)!

Anyway, thanks alot for helping me out in the right direction!!

When you are setting the state of the sensor.pid_zolder, you need to also set the unit_of_measurement attribute so HA knows to make a graph of the values.

attributes = {}
attributes['unit_of_measurement'] = '°C' # Or any other unit but not empty.
self.set_state("sensor.pid_zolder",state=output, attributes=attributes)

Got it working like a charm! Thanks a lot for helping me out :slight_smile:

PID_controller

Hi, I’m trying to learn Appdeamon.
I put in the following (after research )
Can you try to help me out.
I don’t understand the error.

I installed appdeamon from the store.
then I created a file names pid.py and put in this.

import appdaemon.plugins.hass.hassapi as hass
from simple_pid import PID

class PIDcalc(hass.Hass):
  def initialize(self):
    self.run_every(self.PIDrun, "now", 10)

  def PIDrun(self, kwargs):    
     currenttemp= float(self.get_state("climate.woonkamer", attribute = "current_temperature"))
     settemp = float(self.get_state("climate.woonkamer", attribute = "temperature"))
     self.log(currenttemp)
     self.log(settemp)
     pid = PID(5, 0.2, 0.0, setpoint=settemp)
     pid.sample_time = 1
     output = round(pid(currenttemp),2)
     self.log(output)
     self.log("Setting pid.test to PID value")
     self.set_state("sensor.thermostat_hc1_current_room_temperature",state=output)`

Then in apps.yaml I put this:

simple_pid:
  module: pid
  class: PIDcalc

error

2021-10-17 13:38:08.688469 WARNING Error: ------------------------------------------------------------
2021-10-17 13:38:08.687925 WARNING Error: Traceback (most recent call last):
  File "/usr/lib/python3.9/site-packages/appdaemon/app_management.py", line 970, in check_app_updates
    await utils.run_in_executor(self, self.read_app, mod["name"], mod["reload"])
  File "/usr/lib/python3.9/site-packages/appdaemon/utils.py", line 308, in run_in_executor
    response = future.result()
  File "/usr/lib/python3.9/concurrent/futures/thread.py", line 52, in run
    result = self.fn(*self.args, **self.kwargs)
  File "/usr/lib/python3.9/site-packages/appdaemon/app_management.py", line 747, in read_app
    importlib.reload(self.modules[module_name])
  File "/usr/lib/python3.9/importlib/__init__.py", line 169, in reload
    _bootstrap._exec(spec, module)
  File "", line 613, in _exec
  File "", line 855, in exec_module
  File "", line 228, in _call_with_frames_removed
  File "/config/appdaemon/apps/pid.py", line 2, in 
    from simple_pid import PID
ModuleNotFoundError: No module named 'simple_pid'

2021-10-17 13:38:08.685306 WARNING Error: ------------------------------------------------------------
2021-10-17 13:38:08.684813 WARNING Error: Unexpected error loading module: /config/appdaemon/apps/pid.py:
2021-10-17 13:38:08.684150 WARNING Error: ------------------------------------------------------------

But now I don’t see the PID working…
Please advice .

thanks Remco

You need to make sure the simple_pid module is imported, see example here Import non standard python module in Appdaemon - #2 by imaikel