App #1: Doorbell notification

Hi,

Although I recently started working with HA + AppDeamon, I would like to share some small apps that I have so far, and maybe get some feedback :slightly_smiling_face: (I’m a novice python developer).

App #1: Doorbell notification
Send me a notification when someone presses the doorbell, but don’t notify me after every press. Each notification must have an interval of at least 30 seconds.

Entities

sensor.doorbell

app.yaml

doorbell:
  module: doorbell_notification
  class: DoorBellNotification
  sensor: sensor.doorbell

doorbell_notification.py

import appdaemon.plugins.hass.hassapi as hass
from datetime import datetime, timedelta

class DoorBellNotification(hass.Hass):

    def initialize(self):
        self.log('initializing')
        # initialize last_ring variable to avoid extra `If` condition
        self.last_ring = datetime.now() - timedelta(seconds= 35)
        self.listen_state(self.on_doorbell_press, self.args["sensor"], new="on") 

    def on_doorbell_press(self, entity, attribute, old, new, kwargs):
        if self.last_ring < datetime.now() - timedelta(seconds= 30):
            self.last_ring = datetime.now()
            self.log('sending notification')

Happy coding!
Humberto

Note:
This solution is not final or complete but maybe ca help new members starting with HA+AppDaemon. Suggestions or recommendations to improve the implementation are welcome! (|'m still learning :wink:)

Edit #1: Adopt a more pythonic naming convention. Thanks @SupahNoob :+1:
Edit #2 Avoid hardcode entities names in the automation code. Thanks @swiftlyfalling @Burningstone

Next post: App #2: Smart Light

5 Likes

This is a great start Humberto! What should happen if the doorbell was pressed twice within 30s?

I would write your condition a little bit differently, but your way absolutely works. My take on it would be to care for any failures of the event coming in - this way you can handle it and let everything else act normally. Additionally, I’ve initialized the variable self.last_ring as None when we’ve never seen it pressed. It’s a totally optional, pedantic way of writing the logic, but it’s arguably a bit more pythonic - as is naming the variable with an underscore, and the renaming of the callback from “state changed” to “doorbell pressed”.

    ...

    def initialize(self):
        self.log('initializing')
        self.last_ring = None
        self.listen_state(self.state_change, "sensor.doorbell", new="PRESSED") 
        self.log('listening doorbell PRESSED state change')

    def on_doorbell_press(self, entity, attribute, old, new, kwargs):
        now = datetime.now()

        if self.last_ring is not None:
            if self.last_ring > now - timedelta(seconds=30):
                self.log('what should happen here?')
                return

        self.last_ring = now
        self.log('sending notification')

Again, all of these are totally minor, and you’ve done well to express your idea! Keep it up. Things to think about in the future … a totally different action could be taken if the doorbell was rang twice (or N times) within a short duration. This could give you a “panic mode” or something that’s discreet, but still expressive.

Hi @SupahNoob

HA should send me only one notification. Just imagine the case that I am on way to the door, I don’t want to receive multiple notifications if can reach the door in less than 30 seconds. So, those extra rings should be discarded.

Thanks for all your suggestions to improve the code :+1: . Maybe initializing self.last_ring with None indeed is more pythonic, but in this case, I think it’s more clear without an extra layer of IF conditions (at least for me).

Good idea :+1:

Thanks again for your time!
Humberto

1 Like

Thanks for sharing this! It inspired me to make a script to get a critical alert message via the iOS app when someone presses the doorbell. Yesterday I was in the garden and my phone was on silent and I did not notice the messages that someone pressed the doorbell (although vibration is on).

I used your app and rewrote it so that I get a critical alert (that also makes noise even if the phone is on silent) when I am at home. If I am not at home, I will get a normal message. When I press that message using 3d Touch I get a live stream of the camera at the door :grinning:

Here is the code if someone is interested.

app.yaml

doorbell:
  module: doorbell_notification
  class: DoorBellNotification
  # values used in doorbell_notification
  id: switch.deurbel
  g_home: device_tracker.gerard
  notifier: notify/mobile_app_iphone_7
  title: Deurbel
  message: "Er is aangebeld -> klik om de camera te bekijken"

doorbell_notification.py

import appdaemon.plugins.hass.hassapi as hass
from datetime import datetime, timedelta

class DoorBellNotification(hass.Hass):

    def initialize(self):
        self.last_ring = None
        self.listen_event(self.on_doorbell_press, "button_pressed", entity_id=self.args['id'])
        
    def on_doorbell_press(self, entity, data, kwargs):
        now = datetime.now()

        if self.last_ring is not None:
            if self.last_ring > now - timedelta(seconds=10):
                self.log('De bel is binnen 10 seconden opnieuw ingedrukt, de laatste wordt genegeerd')
                return

        self.last_ring = now
        is_g_home = self.get_state(self.args['g_home'])
        if is_g_home == "home":
            self.call_service(self.args['notifier'],
                              title=self.args['title'],
                              message=self.args['message'],
                              data={"push":
                                      {"sound":
                                          {"critical":1, "name":"default", "volume":0.8},
                                      "category": "camera"},
                                    "entity_id":"camera.camera_buiten"
                                    })
        else:
            self.call_service(self.args['notifier'],
                              title=self.args['title'],
                              message=self.args['message'],
                              data={"push":
                                      {"category": "camera"},
                                    "entity_id":"camera.camera_buiten"
                                   })
1 Like

Hi @gerard33

I’m happy to hear about your extension :+1:

Goed gedaan! :belgium: