Appdaemon/Python newbie - is this possible?

I have a number of Amazon Dash buttons, which I am trying to move from triggering HA automations to using Appdaemon. I would like to have an apps.yaml which has the form

dash_button1:
  module: dash
  class: simple_dash_class
  dash_name: switch.dash_button1           
  dash_action: some_appdaemon_function

with a python of

import appdaemon.plugins.hass.hassapi as hass

class simple_dash_class(hass.Hass):

  def initialize(self):
    self.listen_state(self.run_action, self.args["dash_name"])

  def run_action(self, entity, attribute, old, new, kwargs):
    if self.get_state(self.args["dash_name"]) == "on": 
      self.args["dash_action"] #ie run the function specified in the yaml

  def some_appdaemon_function(self, entity, attribute, old, new, kwargs):
    [some code here]
 

So the intention is that for any dash button I can easily specify the Appdaemon function that is called when the button is pushed.

Is this possible? I suspect that the line self.args["some_appdaemon_function"] is completely wrong, but can’t work out if it can be changed to do what I want.

Firstly, you can simplify this to:

if new == "on":

And ditch the condition entirely if you specify the desired state in the listen_state()

self.listen_state(self.run_dash_action, self.args["dash_name"], new="on")

In the above case, run_dash_action() will only trigger if the new state is on.

Regarding your callback, there are a couple of ways to do it, but this might work:

# Get a reference for the function
function = getattr(self, self.args["dash_action"])
# Run it
function()

You would need to give it the right arguments etc. but there is no need for it to have the same args as a listen_state() callback.

I haven’t tested the above but it should be ok :slight_smile:

2 Likes
  def initialize(self):
    self.listen_state(self.run_dash_action, self.args["dash_name"], new="on")

  def run_action(self, entity, attribute, old, new, kwargs):
    [some code here]

will do just what you want

1 Like

Thanks both!

@aimc - that looks great, I’ll give getattr a go

@ReneTode - not sure that is quite what I am after. It doesn’t pick up the function to be run from the yaml, if I understand you correctly?

but if you really want to take the code to your yaml you also could use:

  def run_action(self, entity, attribute, old, new, kwargs):
    eval(self.args["dash_action"]) #ie run the function specified in the yaml

  def some_appdaemon_function(self, entity, attribute, old, new, kwargs):
    [some code here]

  def some_other_appdaemon_function(self, entity, attribute, old, new, kwargs):
    [some code here]

with this in the yaml

dash_button1:
  module: dash
  class: simple_dash_class
  dash_name: switch.dash_button1           
  dash_action: "self.some_appdaemon_function()"

dash_button1:
  module: dash
  class: simple_dash_class
  dash_name: switch.dash_button2           
  dash_action: "self.some_other_appdaemon_function()"
1 Like

but because your code then will run 1 on 1 with the yaml i would go like:

  def initialize(self):
    self.listen_state(self.run_action1, "switch.dash_button1", new="on")
    self.listen_state(self.run_action2, "switch.dash_button2", new="on")

  def run_action1(self, entity, attribute, old, new, kwargs):
    [some code here]

  def run_action2(self, entity, attribute, old, new, kwargs):
    [some code here]

why create complicated yaml if it doesnt bring an advantage?

My thinking is that Dash buttons are essentially disposable, and - in my house at least - they tend to get used for something for a few weeks then re-purposed.

So keeping track of which button is associated with which function is probably easiest to do in a simple, dedicated .yaml file. I can then keep all the various code in the .py which will hopefully in time become static …

in case of appdaemon it doesnt matter if you change the yaml or the code. (thats different then in HA)

if a button gets repurrposed, you need to change the code
if you do it with a function in the yaml, you need to check the yaml first to know what you want to change in the code. its not the code but the yaml that is getting static :wink:

unless you have a few functions that you connect at 1 time to 1 button and at another time to another button

but it can be done :wink:

Let me give you a concrete example -

First, I tend to buy a Dash button or two when they are cheap, so always have spares lying around. When they arrive I tell HA about them, but don’t usually associate them with any functionality. (I actually use them as MQTT publishers, then tell use the HA MQTT platform to listen for presses. I am an MQTT fan and prefer to use it as a base where possible.) It’s a pain having to re-start HA when I want to associate an automation, so I am keen to move them all to Appdaemon.

What are they used for? One of my HA devices is some external gates. It is really useful to be able use a button to open these gates, so I can give a button to eg kids, guests to use. But when the guests have gone it may be that I want to use the same button to boost the heating upstairs, if it’s winter time. But come the spring then maybe I need to open my garage more often to get gardening kit out, and want to use a button for opening the garage doors. Also, my kids seem to be able to lose Dash buttons (don’t ask me how) and then ask for a replacement.

So there are a (relatively) large number of interchangeable buttons and a relatively small number of functions/automations that I want to call with them. I could do it all by editing the .py, but it still seems to me to be easier to keep track of which button does what by having a simple, readable .yaml

1 Like

yeah if you have just a few type off actions then you are right.
then you can use the eval.

Or the get_attr() :slight_smile:

1 Like

whats the difference for those 2?
i might want to change my thinking :wink:

Not a lot, in your way the whole command must be accurate, in mine it’s just the name, but that’s all I can think of …

1 Like

Thanks again guys.

I went the getattr() way - mainly to make the .yaml slightly easier to read (ie to avoid the self. and the brackets.)

For reasons that rather escape me, even though I call function() (with no arguments) the number of arguments errors I was getting were only solved by defining def some_appdaemon_function (self)

off course.
a function needs as many arguments as it is defined with.
so if you define it with 5 arguments, then you need to provide 5 or it will complain.

Hello @ReneTode,

I have a question. I have seen examples of when one wants to re-use an app for different entities, one tends to write the same app multiple times in the apps.yaml file. But what I tend to do, is to declare the app once, then have all my entities as those that use it. For example instead of having it the way you did, I might write instead

dash_button:
  module: dash
  class: simple_dash_class           
  dash_buttons:
     - dash_button1: Gate
     - dash_button2: Heating

Then in code, I look up what was pressed by reading the MQTT payload, and check what it is assigned to. Like if one is assigned to the “Gate” or “Heating” as he said.

My question is, is there a downside to how I am doing it? Will it make my code run slower or something? I am just trying to learn more efficient programming without making my apps.yaml file long.

Hi @ReneTode - my point was that to make it work I had to define the function with one argument (I used self) and call it with zero. I am sure this is just my Python ignorance causing me confusion…

If I might, I’d like to be cheeky and ask an off-topic unrelated question. I am trying to get notification to my iphone working in Appdaemon, and have succeeded. However, I would like to add a sound option. In HA I can do this by eg the following service call to notify.ios_myiphone

{"title":"title goes here", 
"message":"message goes here",
"data":{
   "push":{
      "sound": "US-EN-Morgan-Freeman-Welcome-Home.wav"}}}

and in Appdaemon with no sound I use
self.call_service('notify/ios_myiphone', title = "title goes here", message = "message goes here")
but I am struggling to find the right syntax to pass the sound file down - any hints? :slight_smile:

it simply depends on what you want to do.
the code isnt going to be slower (or not noticable)

if I want to do the same thing for a lot of entities i would also go for an entitylist.

in most times the decision comes down to: do i make my code more complicated or do i make my yaml more complicated. and which complicates less.

Hello @Odianosen25

If you’d like to help out a newbie, I’d be very interested to see your python code for this.

a function inside a class always expects self in the def, but not in the call (allthough it wont hurt to put it there too.)

i think it will be like this:

self.call_service('notify/ios_myiphone', title = "title goes here", message = "message ,data = {"push":{"sound": "US-EN-Morgan-Freeman-Welcome-Home.wav"}})
1 Like