Hassio and appdaemon

Is there a ‘new’ features thread for 3.0?

There have been 5 betas so far each with features, but here are the combined release notes:

3.0.0b5
--------------------

**Features**

 - Added additional error checking for badly formed RSS feeds

**Fixes**

 - Fixed a bug that broke binary_sensor widget.
 - Fixed a bug that broke retries when connecting to Home Assistant
 - Fixed a bug that could cause lockups during app initialization
 - Fixed a bug for Docker that prevented the initial config from working correctly - contributed by `mradziwo <https://github.com/mradziwo>`__

**Breaking Changes**

None


3.0.0b4 (2018-03-03)
--------------------

**Features**

- Single App dependencies can now be specified on the dependency line itself and don't have to be a list of size 1
- Added ``get_ad_version()``, and ``ad_version`` to the config dictionary
- Added filters for Apps
- Added global module dependency tracking
- Added plugin reload app control
- Added icon widget

**Fixes**

- Apps now correctly reload when HASS comes back up after a restart
- ``get_error()`` now properly returns the error log logger object
- ``get_hass_config()`` is now correctly named
- ``app_args`` now correctly returns args for all apps
- ``get_state()`` now returns fields from the attributes dictionary in preference to the top level dictionary if there is a clash. In particular, this now means it is easier to iterate through group members
- Fixed a bug preventing an objects ``terminate()`` from being called when deleted from apps.yaml
- Fixed a bug in which object info was not being cleaned out at object termination
- Fixed an issue preventing dashboard updates on python 3.6

**Breaking Changes**

None

3.0.0b3 (2018-02-11)
--------------------

**Features**

- Added ``javascript`` widget
- Upgraded MDI Icons to 2.1.19
- Add separate log for diagnostic info
- Per-widget type global parameters
- App level dependencies
- ``listen_state()`` now returns the handle to the callback
- added ``oneshot`` option to ``listen_state()``
- Add step parameter to climate widget - contributed by `Adrian Popa <https://github.com/mad-ady>`__
- Add internationalization options to clock widget - contributed by `Adrian Popa <https://github.com/mad-ady>`__
- Doc improvements - contributed by `Marco <https://github.com/marconett>`__

**Fixes**

- Fixed image path for android devices
- Fix a bug with the time parameter for images
- Fixed ``disable_apps``
- Fixed a bug in ``get_state()`` with ``attributes=all`` returning just the attributes dictionary instead of the entire entity.

**Breaking Changes**

- In apps.yaml, dependencies should now be a proper yaml list rather than a comma separated string
- Dependencies now refer to individual apps rather than modules

3.0.0b2 (2018-01-27)
--------------------

**Features**

- Make int args in appdaemon.yaml a little more robust
- Improve handling for missing app files
- Module loading enhancements
- Moved from requests to aiohttp client for better async behavior
- Added thread monitoring for worker threads
- Give more informative error message if AppDaemon can't locate a valid config dir

**Fixes**

- Fixed a bug that could cause multiple apps.yaml changes or additions to be ignored
- Fixed a bug causing listen_state() callbacks with ``duration`` set to fire immediately
- Pinned yarl library to fix an issue with Docker build
- Fixed a couple of potential event loop hold ups
- Fixed a bug in password security for HADashboard service and state calls
- Changes to apps.yaml now also force a reload of dependent modules
- ``exclude_dirs`` now applies to yaml files as well as python files
- Fixed broken icon on HADashboard logon screen
- Fixed a bug preventing the media title from showing in the media player

**Breaking Changes**

- App modules not listed in an apps.yaml file will no longer be loaded. Python modules may still be imported directly if they are in a directory in which other apps reside.
- ``cert_path`` is deprecated. With the replacement of requests with aiohttp, it is now sufficient to set ``cert_verify`` to False to use a self signed certificate.
- Initial dashboard loads may be slower on less powerful hardware when using password authentication. Updating after the initial load is unaffected.

3.0.0b1 (2018-01-12)
--------------------

**Features**

- Refactored pluggable architecture
- Support for multiple HASS instances
- Custom constraints
- Namespaces
- Path of Secret file can now be specified
- apps.yaml can now be split across multiple files and directories
- Apps can now establish loading priorities to influence their loading order
- IFRAME Refreshes should now be more reliable
- Added calls to access the underlying logger objects for the main and error logs
- Add the ability to ignore specific subdirectories under appdir
- Added error handling for apps that can't be read or have broken links
- Added london Underground Widget - contributed by `mmmmmmtasty <https://github.com/mmmmmtasty>`__
- Added ability to display sensor attributes - contributed by `mmmmmmtasty <https://github.com/mmmmmtasty>`__
- Added Weather Summary Widget - contributed by `mmmmmmtasty <https://github.com/mmmmmtasty>`__
- Added Sticky navigation - contributed by `Lars Englund <https://github.com/larsenglund>`__
- Added Input Select widget - contributed by `Rene Tode <https://github.com/ReneTode>`__
- Redesigned Input Number widget (old is still available as ``input_slider``) - contributed by `Rene Tode <https://github.com/ReneTode>`__
- Added Radial widget - contributed by `Rene Tode <https://github.com/ReneTode>`__
- Added Temperature widget - contributed by `Rene Tode <https://github.com/ReneTode>`__
- Added container style to sensor widget - contributed by `Rene Tode <https://github.com/ReneTode>`__

**Fixes**

- Fixed an issue with the compiled directory not being created early enough

**Breaking Changes**

- Apps need to change the import and super class
- ``info_listen_state()`` now returns the namespace in addition to the previous parameters
- AppDaemon no longer supports python 3.4
- --commtype command line argument has been moved to the appdaemon.cfg file
- The "ha_started" event has been renamed to "plugin_started"
- RSS Feed parameters have been moved to the hadashboard section
- Log directives now have their own section
- `AppDaemon` section renamed to `appdaemon`, `HADashboard` section renamed to `hadashboard`
- Accessing other Apps arguments is now via the ``app_config`` attribute, ``config`` retains just the AppDaemon configuration parameters
- Plugins (such as the HASS plugin now have their own parameters under the plugin section of the config file
- The !secret directive has been moved to the top level of appdaemon.yaml
- the self.ha_config attribute has been replaced by the ``self.get_hass_config()`` api call and now supports namespaces.
- apps.yaml in the config directory has now been deprecated
- select_value() has been renamed to set_value() to harmonize with HASS
- It is no longer possible to automatically migrate from the legacy cfg style of config, and support for cfg files has been dropped.

4 Likes

Thank you all for the awesome information greatly appreciated. My main thinking is that for some weird reason right now around sunset appdaemon just dies. Nothing is in the logs for it, it just shutsdown so I was wondering if I upgraded my version if that would help resolve this.

I’m not aware of anything specific, but there are a few areas that might have helped. In any case, if the issue persists, I’ll fix it in 3.0 not 2.x. - all good reasons to upgrade :slight_smile:

Yep yep, I’ll be doing that for sure. Just need to find the thread that explains what I have to do

Start here:

Then search for the other threads and make sure you work through the breaking changes in order - many of them only apply to a subset of functionality so that first thread has by far most of the work.

1 Like

So for now I’m on the latest build of V2 and I’m ready to go to v3 once your release is ready for me :slight_smile: Thank you for your awesome work @aimc

3 Likes

So I decided to take the plunge and dive into V3. I’ve got it running along side my old version at this time while I watch the error log and make adjustments to the apps. The nice thing is that they both have their own dirs so I can keep stuff functioning. Like right now I have an issue with something telling me it only wanted 1 or 2 args but 3 were passed in so need to figure out what this 3rd item was that was passed in. Its with the get_state so time to debug.

Just post the code and error if you need help.

1 Like

So here is the error

-------------------
2018-03-14 01:27:55.121592 WARNING AppDaemon: Unexpected error in worker for App basement_wall_lights:
2018-03-14 01:27:55.122897 WARNING AppDaemon: Worker Ags: {'name': 'basement_wall_lights', 'id': UUID('73aeb0be-0682-409c-90fa-4787be82705b'), 'type': 'attr', 'function': <bound method keep_light_below.adjust_light_function of <basement_wall_lights_max_90.keep_light_below object at 0x747f86b0>>, 'attribute': 'state', 'entity': 'light.basement_wall_lights', 'new_state': 'on', 'old_state': 'on', 'kwargs': {'handle': UUID('c7f4c9ac-7775-4ec9-ad3d-0be06591423e')}}
2018-03-14 01:27:55.123765 WARNING AppDaemon: ------------------------------------------------------------
2018-03-14 01:27:55.125869 WARNING AppDaemon: Traceback (most recent call last):
  File "/usr/lib/python3.6/site-packages/appdaemon/appdaemon.py", line 536, in worker
    self.sanitize_state_kwargs(app, args["kwargs"]))
  File "/config/appdaemon/apps/basement_wall_lights_max_90.py", line 17, in adjust_light_function
    bright_level = self.get_state(self.args["lightID"], "brightness")
TypeError: get_state() takes from 1 to 2 positional arguments but 3 were given

2018-03-14 01:27:55.138626 WARNING AppDaemon: ------------------------------------------------------------

and here is the script

import appdaemon.plugins.hass.hassapi as hass

class keep_light_below(hass.Hass):

  def initialize(self):

    self.listen_state(self.adjust_light_function,(self.args["lightID"]))

# Found this cool function to convert the brightness to a percentage because well thats easier than doing math
  def percent_to_brightness(self, brightness):
      return int(round((int(brightness)/255)*100))

  def brightness_to_percent(self, brightness):
      return 255/100 * int(brightness)

  def adjust_light_function(self, entity, attribute, old, new, kwargs):
      bright_level = self.get_state(self.args["lightID"], "brightness")
      bright_level_percent = self.percent_to_brightness(bright_level)
      self.log(bright_level_percent)
      if bright_level_percent > 90:
          brightness90 = self.brightness_to_percent(90)
          self.turn_on(self.args["lightID"], brightness=brightness90)

It works like a champ under v2 but I don’t get it with v3. Thanks for the help @aimc. @ReneTode helped me write this up and get it working for v2 :slight_smile:

Instead of the above, try this:

bright_level = self.get_state(self.args["lightID"], attribute="brightness")

Ah a little syntax change I see. Sorry I really hate posting simple things I don’t want to be a burden on anyone and I did look in the docs and whatnot but I couldn’t find anything wrong with the syntax I had. Thank you I’ve got it building now and will be pushed out to my pi here shortly.

The syntax change was unintentional - what you had never should have worked, or worked accidentally :wink: The new form is how it should have been all along. If you are working from docs that don;t reflect that could you post a link please so I can fix them?

Sure can here ya go

https://appdaemon.readthedocs.io/en/latest/APIREFERENCE.html?highlight=get_state%20brightness

1 Like

OK, I fixed that in the docs for the next version :slight_smile:

2 Likes

BTW changing that line did the trick. Thank you very much.

1 Like

Hi Andrew,
trying to use oneshot feature for listen_state but getting below error:

2018-05-20 13:29:31.884222 WARNING AppDaemon: ------------------------------------------------------------
2018-05-20 13:29:31.910179 WARNING AppDaemon: ------------------------------------------------------------
2018-05-20 13:29:31.911813 WARNING AppDaemon: Unexpected error during state_update()
2018-05-20 13:29:31.913343 WARNING AppDaemon: ------------------------------------------------------------
2018-05-20 13:29:31.916119 WARNING AppDaemon: Traceback (most recent call last):
File “/usr/lib/python3.6/site-packages/appdaemon/appdaemon.py”, line 2333, in state_update
self.process_state_change(namespace, data)
File “/usr/lib/python3.6/site-packages/appdaemon/appdaemon.py”, line 2309, in process_state_change
removes.append({“name”: callback[“name”], “uuid”: callback[“kwargs”][“handle”]})
KeyError: ‘handle’

2018-05-20 13:29:31.917726 WARNING AppDaemon: ------------------------------------------------------------

below is my script:

    > import appdaemon.plugins.hass.hassapi as hass
    > from datetime import datetime
    > from myglobals import logger as logger
    > import myglobals
    > #
    > # Tracker App
    > #
    > # Args:
    > # 
    > 
    > class Tracker3(hass.Hass):
    > 
    >   def initialize(self):
    >     self.handler          = None
    >     self.message          = None
    >     self.tracker          = self.args["tracker"]
    >     self.geomap           = self.args["geomap"]
    >     self.proximity_home   = self.args["proximity_home"]
    >     self.proximity_work   = self.args["proximity_work"]
    >     self.home_towards     = self.args["home_towards"]
    >     self.work_towards     = self.args["work_towards"]
    >     self.time_to_home     = self.args["time_to_home"]
    >     self.time_to_work     = self.args["time_to_work"]
    >     self.pname            = self.args["name"]
    >     self.work_zone        = self.args["work_zone"]
    >     self.interval         = self.args["ttls"]
    >   
    >     # global flags
    >     self._last_location   = None
    >     self._left_home       = False
    >     self._left_work       = False
    >     self._reached_home    = False
    >     self._reached_work    = False
    >     
    >     #self._home_towards_handler = None
    >     #self._work_towards_handler = None
    > 
    > 
    >     self.log("##############################################################################################")
    >     for a in self.args:
    >       self.message="{}={}={}".format(a, self.args[a], self.get_state(self.args[a]))
    >       logger(self)
    >     
    >     #print myglobals
    >     self.message="myglobals.notify_service={}".format(myglobals.notify_service)
    >     logger(self)  
    >     self.log("##############################################################################################")
    >     
    >     self.listen_state(self.track_state, self.tracker, old="home", new="not_home", duration=120)
    >     self.listen_state(self.track_state, self.tracker, old="not_home", new="home", duration=120)
    >     self.listen_state(self.track_state, self.tracker, old=self.work_zone, new="not_home", duration=120)
    >     self.listen_state(self.track_state, self.tracker, old="not_home", new=self.work_zone, duration=120)
    > 
    >     #self.listen_state(self.track_state, self.tracker)
    >     self.listen_state(self.towards_callback, self.home_towards, old="off", new="on", duration=60, oneshot=True, to="home")
    >     self.listen_state(self.towards_callback, self.work_towards, old="off", new="on", duration=60, oneshot=True, to="work")
    > 
    > ##############################################################################################
    >   def track_state (self, entity, attribute, old, new, kwargs): 
    >     self.message = "old={},new={}".format(old,new)
    >     logger(self)
    > 
    >     if old == new:
    >       return
    > 
    >     #reachead or left home or work
    >     if old == "home" and new == "not_home":
    >       self._left_home = True
    >       self.message = "{} has left home".format(self.pname)
    >       logger(self,flag=1)
    >       #self.handler = self.run_every (self.notify_travel, datetime.now(), int(float(self.get_state(self.interval))*60))
    >     elif old == self.work_zone and new == "not_home":
    >       self._left_work = True
    >       self.message = "{} has left work".format(self.pname)
    >       logger(self,flag=1)
    >       #self.handler = self.run_every (self.notify_travel, datetime.now(), int(float(self.get_state(self.interval))*60))
    >     elif old == "not_home" and new == self.work_zone:
    >       self._reached_work = True
    >       self.message = "{} has reached work".format(self.pname)
    >       self.cancel_tracker()
    >       logger(self,flag=1)
    >     elif old == "not_home" and new == "home":
    >       self._reached_home = True
    >       self.message = "{} has reached home".format(self.pname)
    >       logger(self,flag=1)
    >       self.cancel_tracker()
    >     else:
    >       self.message = "{} has reached {} now".format(self.pname, new)
    >       logger(self,flag=1)
    >    
    > #####################################################################################################################################
    >   def notify_travel (self, kwargs):
    >     #to = "somewhere"
    >     to = kwargs["to"]
    >     #if self.get_state(self.home_towards) == "on" and self.get_state(self.work_towards) == "on":
    >     #  return
    >     #elif self.get_state(self.home_towards) == "on" and self.get_state(self.work_towards) == "off":
    >     #  to = "home"
    >     #elif self.get_state(self.work_towards) == "on" and self.get_state(self.home_towards) == "off":
    >     #  to = "work"
    > 
    >     state = self.get_tracker_state(self.tracker)
    >     self.message = "to={} and state={}".format(to,state)
    >     logger(self)
    >     location = self.get_state(self.geomap).split(",")[1]
    >     zone = self.get_tracker_state(self.tracker)
    >     #annouce zone name if available
    > 
    >     if zone != "not_home":
    >       location = zone
    > 
    >     if location != self._last_location:
    >       distance_to_home = self.get_state(self.proximity_home)
    >       time_to_home = self.get_state(self.time_to_home)
    >       time_to_work = self.get_state(self.time_to_work)
    >       distance_to_work = self.get_state(self.proximity_work)
    > 
    >       if to == "home":
    >         self.message = "{} is traveling {}. Estimated distance & time is {}km and {}min. {} has reached {} now".format(self.pname,to,distance_to_home,time_to_home,self.pname,location)
    >       elif to == "work":
    >         self.message = "{} is traveling {}. Estimated distance & time is {}km and {}min. {} has reached {} now".format(self.pname,to,distance_to_work,time_to_work,self.pname,location)
    >       else:
    >         self.message = "{} has reached {} now".format(self.pname,location)
    > 
    >       logger(self,flag=1)
    >       self._last_location = location
    > #####################################################################################################################################      
    >   def cancel_tracker (self): 
    >     try:
    >         self.message="cancelling tracker now"
    >         logger(self)
    >         self.cancel_timer(self.handler)
    >         self.handler =None
    >         
    >         self.message="done"   
    >         logger(self)
    >     except KeyError:
    >         self.message='Tried to cancel a timer for {}, but none existed!'.format(self.tracker)
    >         logger(self)
    > 
    > #####################################################################################################################################
    >   def towards_callback (self, entity, attribute, old, new, kwargs):
    >     self.handler = self.run_every (self.notify_travel, datetime.now(), int(float(self.get_state(self.interval))*60), to=kwargs["to"])

There were couple of bugfixes in this area recently - what version of AppDaemon are you using?

Im using the latest version of add on from frank that is 1.1.0.

What version of AppDaemon is showing in your logs?