Skip to content

Instantly share code, notes, and snippets.

@haberda
Last active March 4, 2024 03:44
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save haberda/b65113a1e505759f333a3c5f8feb4fef to your computer and use it in GitHub Desktop.
Save haberda/b65113a1e505759f333a3c5f8feb4fef to your computer and use it in GitHub Desktop.
AppDaemon Reminder app
import hassapi as hass
import datetime
class reminder(hass.Hass):
def initialize(self):
self.set_namespace("reminder")
self.listen_event(self.set_reminder,'set_reminder', namespace='default')
self.listen_event(self.remove_reminder,'remove_reminder', namespace='default')
domain = 'reminder'
reminder_list = list(self.get_state(domain).keys())
if len(reminder_list) > 0:
for reminder in reminder_list:
time = self.parse_datetime(self.get_state(reminder))
att = self.get_state(reminder, attribute='all')
dt = datetime.datetime.now()
if dt.timestamp() > time.timestamp():
self.remove_entity(reminder, namespace='default')
self.remove_entity(reminder, namespace='reminder')
else:
self.run_at(self.send_reminder, time, entity_id = reminder)
self.set_state(**att, namespace='default')
self.log('Subscribed to ' + reminder)
def set_reminder(self, event, data, kwargs):
"""Creates Reminder Entities to Track"""
if 'name' in data and 'time' in data and 'title' in data and 'recipient' in data and 'message' in data:
entity_name = 'reminder.' + data['name']
entity_name = entity_name.replace(" ", "_")
else:
self.log('No reminder name defined, exiting')
return
self.set_state(entity_name, state=data['time'], attributes = {
"message": data['message'],
"title": data['title'],
"recipient": data['recipient']}, namespace='default')
self.add_entity(entity_name, state=data['time'], attributes = {
"message": data['message'],
"title": data['title'],
"recipient": data['recipient']}, namespace='reminder')
self.restart_app(self.name)
def send_reminder(self, kwargs):
"""Sends reminder then deletes entity"""
self.log('send_reminder')
state=self.get_state(kwargs['entity_id'], attribute="all")
attributes = state['attributes']
self.notify(attributes['message'], title = attributes['title'], name = attributes['recipient'], namespace='default')
self.remove_entity(kwargs['entity_id'], namespace='default')
self.remove_entity(kwargs['entity_id'], namespace='reminder')
def remove_reminder (self, event, data, kwargs):
self.remove_entity(data['entity_id'], namespace='default')
self.remove_entity(data['entity_id'], namespace='reminder')
self.restart_app(self.name)
@clutch70
Copy link

clutch70 commented Oct 13, 2022

Great app! I really wanna get it up and running!

Any advice on the following output? I get it right after restarting appdaemon and the reminder module reports an "initialize_error" state in the AD webapp.

2022-10-13 08:56:54.831828 WARNING reminder: ------------------------------------------------------------
2022-10-13 08:56:54.832331 WARNING reminder: Unexpected error running initialize() for reminder
2022-10-13 08:56:54.832649 WARNING reminder: ------------------------------------------------------------
2022-10-13 08:56:54.835675 WARNING reminder: Traceback (most recent call last):
File "/usr/lib/python3.10/site-packages/appdaemon/app_management.py", line 165, in initialize_app
await utils.run_in_executor(self, init)
File "/usr/lib/python3.10/site-packages/appdaemon/utils.py", line 337, in run_in_executor
response = future.result()
File "/usr/lib/python3.10/concurrent/futures/thread.py", line 58, in run
result = self.fn(*self.args, **self.kwargs)
File "/config/appdaemon/apps/reminder.py", line 11, in initialize
reminder_list = list(self.get_state(domain).keys())
File "/usr/lib/python3.10/site-packages/appdaemon/utils.py", line 226, in inner_sync_wrapper
f = run_coroutine_threadsafe(self, coro(self, *args, **kwargs))
File "/usr/lib/python3.10/site-packages/appdaemon/utils.py", line 346, in run_coroutine_threadsafe
result = future.result(self.AD.internal_function_timeout)
File "/usr/lib/python3.10/concurrent/futures/_base.py", line 446, in result
return self.__get_result()
File "/usr/lib/python3.10/concurrent/futures/_base.py", line 391, in __get_result
raise self._exception
File "/usr/lib/python3.10/site-packages/appdaemon/adapi.py", line 1511, in get_state
return await self.get_entity_api(namespace, entity_id).get_state(attribute, default, copy, **kwargs)
File "/usr/lib/python3.10/site-packages/appdaemon/entity.py", line 149, in get_state
return await self.AD.state.get_state(self.name, namespace, entity_id, attribute, default, copy)
File "/usr/lib/python3.10/site-packages/appdaemon/state.py", line 508, in get_state
for entity_id, state in self.state[namespace].items()
KeyError: 'reminder'
2022-10-13 08:56:54.836159 WARNING reminder: ------------------------------------------------------------
2022-10-13 08:56:54.837033 INFO AppDaemon: App initialization complete

appdaemon.yaml

image

apps.yaml

image

@haberda
Copy link
Author

haberda commented Oct 14, 2022

I think it's fixed. The callback function wasn't taking in the time correctly. I also added some error checking, so if there is a reminder that wasn't cleared for some reason it will just clear it; this usually would happen if it failed to send for some reason. I suppose I could have it send the reminder as soon as it hits that check, maybe I'll do that later.

@byrleyar
Copy link

Thanks for sharing this! I'm getting the same error as @clutch70. There seems to be some issue with the reminder namespace, perhaps? I've edited appdaemon.yml and apps.yml as prescribed. I would appreciate any assistance -- thanks!

2022-11-17 11:51:37.386682 INFO AppDaemon: Reloading Module: /config/appdaemon/apps/reminder.py
2022-11-17 11:51:37.393935 INFO AppDaemon: Initializing app reminder using class reminder from module reminder
2022-11-17 11:51:37.406871 WARNING reminder: ------------------------------------------------------------
2022-11-17 11:51:37.407934 WARNING reminder: Unexpected error running initialize() for reminder
2022-11-17 11:51:37.408694 WARNING reminder: ------------------------------------------------------------
2022-11-17 11:51:37.410679 WARNING reminder: Traceback (most recent call last):
File "/usr/lib/python3.10/site-packages/appdaemon/app_management.py", line 165, in initialize_app
await utils.run_in_executor(self, init)
File "/usr/lib/python3.10/site-packages/appdaemon/utils.py", line 337, in run_in_executor
response = future.result()
File "/usr/lib/python3.10/concurrent/futures/thread.py", line 58, in run
result = self.fn(*self.args, **self.kwargs)
File "/config/appdaemon/apps/reminder.py", line 11, in initialize
reminder_list = list(self.get_state(domain).keys())
File "/usr/lib/python3.10/site-packages/appdaemon/utils.py", line 226, in inner_sync_wrapper
f = run_coroutine_threadsafe(self, coro(self, *args, **kwargs))
File "/usr/lib/python3.10/site-packages/appdaemon/utils.py", line 346, in run_coroutine_threadsafe
result = future.result(self.AD.internal_function_timeout)
File "/usr/lib/python3.10/concurrent/futures/_base.py", line 458, in result
return self.__get_result()
File "/usr/lib/python3.10/concurrent/futures/_base.py", line 403, in __get_result
raise self._exception
File "/usr/lib/python3.10/site-packages/appdaemon/adapi.py", line 1511, in get_state
return await self.get_entity_api(namespace, entity_id).get_state(attribute, default, copy, **kwargs)
File "/usr/lib/python3.10/site-packages/appdaemon/entity.py", line 149, in get_state
return await self.AD.state.get_state(self.name, namespace, entity_id, attribute, default, copy)
File "/usr/lib/python3.10/site-packages/appdaemon/state.py", line 508, in get_state
for entity_id, state in self.state[namespace].items()
KeyError: 'reminder'
2022-11-17 11:51:37.411616 WARNING reminder: ------------------------------------------------------------

@haberda
Copy link
Author

haberda commented Nov 17, 2022

Can you post your full appdaemon.yaml? Make sure you remove your sensitive information. This app works fine for me, and if there is a namespace issue we should start with that file.

@byrleyar
Copy link

byrleyar commented Nov 17, 2022

Sure! Here is my appdaemon.yaml:

---
secrets: /config/secrets.yaml
appdaemon:
  latitude: xxxxxxxxx
  longitude: xxxxxxxxx
  elevation: x
  time_zone: xxtimezonexx
  plugins:
    HASS:
      type: hass
http:
  url: http://127.0.0.1:5050
admin:
api:
hadashboard:
namespaces:
  reminder:
    writeback: safe

Here is apps.yaml:

---
hello_world:
  module: hello
  class: HelloWorld
reminder:
  module: reminder
  class: reminder

@haberda
Copy link
Author

haberda commented Nov 18, 2022

Your namespaces: key is in the wrong place in your appdaemon.yaml, It should reside indented under the appdaemon: key. You have it at the top level.

@byrleyar
Copy link

Amazing - thank you for helping with this! I've got it working now and notifications are coming through. If you're still open to troubleshooting, it appears that reminders aren't getting deleted after they are firing. For example, reminder.name1 isn't deleting (though to be honest, I'm not sure which namespace it's failing to delete).

2022-11-19 12:45:08.621274 INFO AppDaemon: Initializing app hello_world using class HelloWorld from module hello
2022-11-19 12:45:08.629780 INFO AppDaemon: Initializing app reminder using class reminder from module reminder
2022-11-19 12:45:08.638049 INFO hello_world: Hello from AppDaemon
2022-11-19 12:45:08.641464 INFO hello_world: You are now ready to run Apps!
2022-11-19 13:28:59.773275 INFO HASS: Registering new service notify/po_andrewonly
2022-11-19 13:32:54.905814 WARNING reminder: reminder: Entity reminder.name1 not found in namespace default
2022-11-19 13:32:54.908232 INFO AppDaemon: reminder: Entity reminder.name1 created in namespace: default
2022-11-19 13:32:54.959953 INFO AppDaemon: Terminating reminder
2022-11-19 13:32:54.963842 INFO AppDaemon: Initializing app reminder using class reminder from module reminder
2022-11-19 13:32:55.015042 INFO reminder: Subscribed to reminder.name1
2022-11-19 13:34:00.018140 INFO reminder: send_reminder
2022-11-19 13:34:00.044620 WARNING HASS: Error Removing Home Assistant entity reminder.name1
2022-11-19 13:34:00.046224 WARNING HASS: Code: 405, error: 405: Method Not Allowed

image

Any thoughts?

@haberda
Copy link
Author

haberda commented Nov 19, 2022

Not sure why it wouldn't delete it. Couple things to try:

  1. Make sure you have the latest code from the gist
  2. Make sure there are no special characters in the names or messages
  3. See if you can use the remove_reminder event to delete the event
  4. When the app starts one of the first things it does is try and see if there are any orphaned reminders and delete them; see what happens when you manually restart it.
  5. Look in the AppDaemon web interface at the entities, you can look a the reminder namespace and see if it's getting stuck there
  6. You can comment out some of those delete commands and see if one will delete but not the other too. For example, lines 49 and 50.

See if any of those help.

@byrleyar
Copy link

  1. I am using the latest revision that adds an underscore to the entity name.
  2. I am only using lowercase, spaces, and numbers in names and messages.
  3. When I use remove_reminder, the front end says it fires successfully, but the logs are throwing the same Method Not Allowed warning:
    image
2022-11-20 09:49:13.035382 WARNING HASS: Error Removing Home Assistant entity reminder.name1
2022-11-20 09:49:13.036443 WARNING HASS: Code: 405, error: 405: Method Not Allowed
  1. When I manually restart, there are no errors or warnings, which is interesting since it also runs remove_reminder.
  2. Within the reminder namespace of the AppDaemon web interface, there are no entities listed. There are also no reminder. entities within the default namespace of the AppDaemon web interface, which is interesting. Despite this, home assistant still seems to think they exist.
  3. Here is what I did for your sixth point: When I comment out the line to delete from the default namespace and run remove_reminder, there are no warnings.
    def remove_reminder (self, event, data, kwargs):
        #self.remove_entity(data['entity_id'], namespace='default')
        self.remove_entity(data['entity_id'], namespace='reminder')
        self.restart_app(self.name)

Conversely, when I comment out the line to delete from the reminder namespace and run remove_reminder, I do get get the method not allowed errors.

    def remove_reminder (self, event, data, kwargs):
        self.remove_entity(data['entity_id'], namespace='default')
        #self.remove_entity(data['entity_id'], namespace='reminder')
        self.restart_app(self.name)

Do you think it has anything to do with not setting a unique_id?

Would you mind telling me how you setup the Active Reminders card in your example? Perhaps I'm just being overly fussy about this and the home assistant database will take care of cleanup over time?
image

@haberda
Copy link
Author

haberda commented Nov 26, 2022

I added some gists for the UI stuff. There are 2 scripts and an automation. The automation builds and maintains an input select with a list of the active reminders. However in the UI I use a custom card called autoentities that just populates any active reminders.

Automation:
https://gist.github.com/haberda/e107c442dee75c58bcd2db322da3a17e

Sctipts:
https://gist.github.com/haberda/b509816370af6c421b36dc8049a0a47f

UI:
https://gist.github.com/haberda/07a3a28e4bd2efc82754382cc0fe34d0

It could be done better, but this works for now. The automation, scripts, and helpers should probably be in a package for distribution, but none of this is required to make the appdaemon app work so I never packaged it up.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment