AppDaemon Q&A

Thanks to @Robban I got something up and running! Could somebody with python experience tell me if I am doing anything terribly wrong or if I could streamline my code in a better way?

class LivingRoomSceneStore(appapi.AppDaemon):

  def initialize(self):
    self.tv_lightstrip_previous_state = 0
    self.lamp_l_previous_state = 0
    self.lamp_r_previous_state = 0
    self.entryway_1_previous_state = 0
    self.entryway_2_previous_state = 0
    self.listen_state(self.change, "input_boolean.movie_time", attribute = "all")
    self.listen_state(self.restore, "input_boolean.movie_time", attribute = "all")
    self.log("Save and restore initialized!")
  
  def change(self, entity, attribute, old, new, kwargs):
    if ("state" in new and new["state"] == "on" and old["state"] == "off") or new == "on":
      self.tv_lightstrip_previous_state = self.get_state("light.tv_lightstrip", "all")
      self.lamp_l_previous_state = self.get_state("light.floor_lamp_l", "all")
      self.lamp_r_previous_state = self.get_state("light.floor_lamp_r", "all")
      self.entryway_1_previous_state = self.get_state("light.entryway_1", "all")
      self.entryway_2_previous_state = self.get_state("light.entryway_2", "all")
      self.turn_on("light.tv_lightstrip", brightness = 45, rgb_color = [119, 104, 80], transition = 10)
      self.turn_on("light.floor_lamp_l", brightness = 15, rgb_color = [56, 56, 119], transition = 10)
      self.turn_on("light.floor_lamp_r", brightness = 15, rgb_color = [56, 56, 119], transition = 10)
      self.turn_on("light.entryway_1", brightness = 20, transition = 10)
      self.turn_on("light.entryway_2", brightness = 20, transition = 10)

  def restore(self, entity, attribute, old, new, kwargs):
    if ("state" in new and new["state"] == "off" and old["state"] == "on") or new == "off":
      if self.tv_lightstrip_previous_state['state'] == "on":
        self.turn_on("light.tv_lightstrip", brightness = self.tv_lightstrip_previous_state['attributes']['brightness'], rgb_color = self.tv_lightstrip_previous_state['attributes']['rgb_color'])
      else:
        self.turn_off("light.tv_lightstrip")
      if self.lamp_l_previous_state['state'] == "on":
        self.turn_on("light.floor_lamp_l", brightness = self.lamp_l_previous_state['attributes']['brightness'], rgb_color = self.lamp_l_previous_state['attributes']['rgb_color'])
      else:
        self.turn_off("light.floor_lamp_l")
      if self.lamp_r_previous_state['state'] == "on":
        self.turn_on("light.floor_lamp_r", brightness = self.lamp_r_previous_state['attributes']['brightness'], rgb_color = self.lamp_r_previous_state['attributes']['rgb_color'])
      else:
        self.turn_off("light.floor_lamp_r")
      if self.entryway_1_previous_state['state'] == "on":
        self.turn_on("light.entryway_1", brightness = self.entryway_1_previous_state['attributes']['brightness'])
      else:
        self.turn_off("light.entryway_1")
      if self.entryway_2_previous_state['state'] == "on":
        self.turn_on("light.entryway_2", brightness = self.entryway_2_previous_state['attributes']['brightness'])
      else:
        self.turn_off("light.entryway_2")

simple is Always better (i think)

class SceneStore(appapi.AppDaemon):

  def initialize(self):
    self.previous_state = 0
    self.listen_state(self.change, self.args["inputboolean"])
    self.log("Save and restore initialized " + self.args["light"], "INFO")

  def change(self, entity, attribute, old, new, kwargs):
    if  new == "on":
      self.previous_state = self.get_state(self.args["light"], "all")
      self.turn_on(self.args["light"], brightness = int(self.args["brightness"], rgb_color = [self.args["rgb1"],self.args["rgb2"], self.args["rgb3"]], transition = self.args["transition"])
    else:
      if self.previous_state['state'] == "on":
        self.turn_on(self.args["light"], brightness = self.previous_state['attributes']['brightness'], rgb_color = self.tv_lightstrip_previous_state['attributes']['rgb_color'])
      else:
        self.turn_off(self.args["light"])

and then in your cfg file you put in for every light you want to restore:

[scene1storelight1]
module = yourapp
class = SceneStore
inputboolean = input_boolean.movie_time
light = light.tv_lightstrip
brightness = 45
rgb1 = 119
rgb2 = 104
rgb3 = 80
transition = 10

[scene1storelight2]
module = yourapp
class = SceneStore
inputboolean = input_boolean.movie_time
light = light.floor_lamp_1
brightness = 15
rgb1 = 56
rgb2 = 56
rgb3 = 119
transition = 10

that way you can Always add lights as many as you want without changing the code.
you can also use another input boolean to store and bring back a second scene (for kitchen for instance when you want a lot off light for a short time and then back to before)

hope this helps.

2 Likes

Nice work!

I’ve also gotten some more time playing with save and restore. I am just learning appdaemon and not as experienced with it as Rene so listen to him. :slight_smile: Anyway, here is my current concept, it works on a group containing only lights, it will dim and reset between them. Currently it is triggered by an input_boolean.

  def initialize(self):
    self.brightness = {}
    self.lights = self.resolve_group(self.args["Group"])
    self.listen_state(self.trigger, self.args["appdaemon_trigger"])
    self.log("Auto dim lights in {} initialized!".format(self.args["Group"]))

  def trigger(self, entity, attribute, old, new, kwargs):
    if new == "on":
      self.dim()
    else:
      self.reset()
  
  def dim(self, dim_level = 30):
    for light in self.lights:
      self.brightness[light] = self.get_state(light, "brightness")
      self.turn_on(light, brightness = dim_level)
    self.log("Dimmed lights")

  def reset(self):
    if self.brightness:
      for light in self.lights:
        self.turn_on(light, brightness = self.brightness[light])
      self.log("Reset lights brightness")
    else:
      self.log("Tried to reset brightness, but dict was empty")

  # Returns a list of entity_ids from the group
  def resolve_group(self, group):
    group = self.get_state(group, attribute = "all")
    return group["attributes"]["entity_id"]
1 Like

Rene, have you ever considered writing up some tutorials for absolute beginners with AppDaemon?

I’m not a programmer and so I find AD to be very intimidating and while I support it in any way I can, I don’t actually use it. I don’t think Andrew has the time between all the updating and supporting this and HADashboard and you seem to have a real knack for explaining it. It would be awesome if you could do a tutorial for people like me who aren’t python devs but can hack some code.

1 Like

robert, have you done any programming at all?

i know you have learned your way around in yaml.
and python is actually not that hard at all.

there are a lot off good python tutorials for beginners out there, and with the api from andrew at the side it isnt that hard.

off course i can try to write something down the way i think. i will give it a try but reading a python tutorial for beginner is really a must.

to get you to make your own app i will write “appdaemon for beginners” :wink:

3 Likes

I’ve taken a couple of Python tutorial classes and I have hacked programs others have written in PHP, JavaScript and Python, but never written a program from concept to finish. My experience has been more on the web side of things. I understand basic logic but when I look at AD code my eyes glaze over. I’ve really tried to support Andrew’s efforts in any way I can, in fact I developed a website for him that we’re hoping to use in the future for an app repo.

But I feel kind of lost whenever I read the threads here. If you’d like, I’d be glad to team up with you in some way so that we can present this as a way to convert people like me from being afraid of AD to being AD users, if you think that might be an approach.

Please feel free to PM me if you’d like to do something like this or I’ll wait to see what you have in mind. All I know is that advanced HA users keep saying “You can do that easier/better/faster/more flexibly in AppDaemon” and I think that if someone like myself can get into the approach, we can help build a more critical mass.

i started writing already :wink:

when i have a part ready i will present it to you so you can help me presenting it in a decent way, and maybe then you can correct some failure i make with my english :wink:

i think the best approach is to make it nice and easy and fun to read, and not to technical.

so i start like this:

AppDaemon for beginner


Any automation you can think off in HomeAssistant can be done with Appdaemon.
The first question that could probably be asked is: "Why use Appdaemon when it is all 
possible in Home Assistent itselve?"

for me the answer is: "I have some experience in python (not all that much, but some) 
and very little experience with yaml, jinja, json and all other thing that are used to 
configurate Home Assistant.
And allthough things like yaml, jinja, etc. are not very hard to get to know, 
i think they are not very easy to use to create a little more complicated automations
and scripts.

So if you know your way around in yaml, jinja, etc. there is no actual need to start 
using AppDaemon unless you run into some bounderies.

Now you know why i use AppDaemon, so lets start with some basic understanding.

3 Likes

Rene - I like the intro very much and I can definitely help you with the editing as writing end user documentation and tutorials is what I used to do for a living. But I wouldn’t touch a thing on your intro (except a few spelling errors that we all make) as I think it captures exactly why I asked you to think about doing this project. I stand ready to help any way that I can.

1 Like

i guess you are reading your PM at this moment, which contains the first appdaemon tutorial :wink:

1 Like

Super that you are joining forces on This!

1 Like

dont overreact, i am just offering a small helping hand :wink:

Over react if you like Ty - Rene is writing this up, I am only editing his work and I think you’ll be real happy with this. He’s only started the first parts of it and already I feel a lot more at ease with AD. I have to put in some time later today on this but he’s doing a great job with it.

1 Like

Well I for one appreciate it. I’ll definitely be making use of it @ReneTode

I’m not really sure how to search this 400 post thread, but would doing the beginners python courses on a site like codeacademy be a good starting point for a non programmer like me to get the gist of this - and templates?

1 Like

I dont know exactly where and how i will place the tutorial. but i think that i will start collecting my creations on my own website.

As soon as robert is ready reviewing it, i will release the first part.

After restarting my Docker container, the Appdaemon reinitialized / reïnstalled the required Python modules. Unfortunately, what I see now is that the latest version of sseclient (0.0.13, released on November 14th) seems to be incompatible with Appdaemon, and results in the following exception:

File "/usr/local/lib/python3.5/dist-packages/sseclient.py", line 64, in __next__
    self.buf += nextline
TypeError: Can't convert 'bytes' object to str implicitly

This problem did not occur when using sseclient 0.0.12. Maybe the requirements.txt can be changed for now to always use version 0.0.12?

@aimc After a few months I’m a bit frustrated that I can’t get a well working “keep lights on while motion detected” automation done. Endless amount of posts about this but nobody seems to have a decent working one.

I now use Appdaemon for state restore & occupancy simulation but I can’t (yet) make the from scratch.

In your examples I also see a ‘motion lights’ example ;does this keep the lights on or is it a ‘simple’ timer? Is there a way you can help me a bit with a “keep lights on while motion” app? I think it will make many happy.

I am gonna use motion with light this week for the first time.
im gonna use the example from andrew as base, but i think i need to change some parts.
when i get it ready i will post it on the forum.

Python3 compatibility seems to have been fixed by sseclient==0.0.14, so all is well again :slight_smile:

Hi guys!

Starting to get a grasp of what I want to achieve with my current hardware at home in HASS + AppDaemon.

Focusing mainly on getting some good schedule control as well as media play control moved over to AppDaemon due to the much freer nature of Python compared to yaml. My goal is to have most configurations inside HASS but let AppDaemon do the harder work, i.e. no automations and maybe move other parts over later on.

My question right now regards media play light control and scheduled lighting; so I have a system working that properly sets and resets brightness based on state of Plex. But my scheduling script (even though it is in AppDaemon) is pretty dumb. Since the schedules will kill the lights regardless of a movie playing or not. What I would like is for the scheduling to abort when triggered if a movie is playing, and a new schedule to be set for 10 minutes after end of movie. I can add all logic in to the schedule code, but it feels kind of ugly.

Right now these controls are in different .py’s and regardless of that would be different classes (I guess), but how can I in a simple fashion share information between these state machines to not duplicate stuff that would really be irrelevant for a clean single use class. What is the proper pythonic way of doing stuff. Should I keep them completely separated or is it a nice way of achieving this?

/R

the easieast way i can think of:

  1. an input boolean which is on as a movie is playing.
  2. that input boolean could be used as an callback constraint for existing lightschedules.
  3. when the input boolean turns off you can then activate a new schedule.

and for sharing between apps there is a part in the API.

other_apps_arg = self.config["some_app"]["some_parameter"].

To get AppDaemon's config parameters, use the key "AppDaemon", e.g.:
app_timezone = self.config["AppDaemon"]["time_zone"]

And finally, it is also possible to use the AppDaemon as a global area for sharing parameters across Apps.
Simply add the required parameters to the AppDaemon section of your config:

[AppDaemon]
ha_url = <some url>
ha_key = <some key>
...
global_var = hello world
Then access it as follows:
my_global_var = conf.config["AppDaemon"]["global_var"]