@aimc Iâd never seen this duration
before, thatâs super cool.
@smolz Did you get your automation working? I converted your specific automation up above into this app.
class BackDoor(appapi.AppDaemon):
def initiatilize(self):
self.listen_state(self.notifications, entity='binary_sensor.door1_sensor_5_0', new='open', duration=60)
def notifications(self, entity, attribute, old, new, kwargs):
title = "Back Door Still Open"
message = "Back Door has been open for more than 60 seconds"
data = {"push": {"badge": 1}}
self.call_service('notify/ios_v1ruexe', title=title, message=message, data=data)
However I think you can build on this idea. You state âif a given door is open for more than a minuteâ which makes me assume you monitor more than just the back door this way? Especially since in your last sentence you say âall the door sensorsâ.
So this is my crack at it! Try your best to understand it before reading my explanation below. Iâll try my best to break it down for you. I am going to assume you know absolutely nothing about Python or programming. If itâs easier, you can also view the syntax highlighted here.
import appdaemon.appapi as appapi
#
# App to send notification when door opened or closed
#
# Args: (set these in appdaemon.cfg)
# ttl = # of seconds to wait until notified
#
class DoorMonitor(appapi.AppDaemon):
def initiatilize(self):
self.door_entities = ['binary_sensor.door1_sensor_5_0', 'binary_sensor.door2_sensor_5_0',
'binary_sensor.door3_sensor_5_0', 'binary_sensor.door4_sensor_5_0']
self.door_timer_library = {}
for door in self.door_entities:
self.listen_state(self.tracker, entity=door)
def tracker(entity, attribute, old, new, kwargs):
friendly_name = self.get_state(entity, attribute='friendly_name')
try:
self.cancel_timer(self.door_timer_library[entity])
except:
self.log('Tried to cancel a timer for {}, but none existed!'.format(entity), level='INFO')
if new == 'on':
self.door_timer_library[entity] = self.run_in(self.notifier, seconds=int(self.args['ttl']),
friendly_name=friendly_name, timer=self.args['ttl'])
def notifier(self, kwargs):
title = "{} Still Open".format(kwargs['friendly_name'])
message = "This door has been open for more than {} seconds".format(kwargs['timer'])
data = {"push": {"badge": 1}}
self.call_service('notify/ios_v1ruexe', title=title, message=message, data=data)
Starting off at the very top, weâll say you have 1 argument you specify, and thatâs the ttl
or âtime to liveâ value. This is the number of seconds, or duration of time that should pass before youâre notified of a doorâs state being on
, or open!
I call your app âDoorMonitorâ as that is essentially what weâre doing here. In my mind, it consists of two parts. A function that should track the state, and then another function that should handle notifying you of the occurrence of the specific door being open for longer than the ttl
value. As such, youâll see we have both tracker
and notifier
functions! Thatâs enough for a quick overview though.
In the initialize
section of your appâŚ
- We define a variable called
door_entities
and in what we call a âlistâ. This is a way to hold our data in memory much like you would hold data on a shopping list on paper. It simply keeps a number of things, in our case various entity_id
s in an easily-accessible area.
- We then define another variable called
door_timer_library
in what we call a âdictionaryâ. Dictionaries in Python are another way to contain our data, except each datapoint will have both a unique identifier called a âkeyâ associated with a âvalueâ. These are called key-value pairs, because the key references a value. To give you a real world example⌠you can simply look at the function of a spoke-language dictionary. Each word has a meaning, and these wordsâ functions are unique. Thereâs more nuance here, but this metaphor does the job well enough.
- Weâll then call
listen_state
for-each entity_id
in the door_entities
list we created earlier, so that whenever their state changes, our tracker
function will fire.
In the tracker
section of your appâŚ
-
@aimc has given us some nice tools to work with here, so thank him already.
- First off weâll pull out the friendly name of the
entity
that weâre working work. Remember, since we technically called listen_state
for each of our entities in door_entities
, this code essentially turns into a skeleton of which all of those entities will fall into. Depending on which entityâs state changed, that entity will show up here in the reference entity
.
- Weâll then do a little bit of Python magic here (
try
/except
is a bit out of scope for a first lesson!) Weâll come back to this in a second, just hold on!
- Where the app says
new == on
, we are checking to see if the new value of entity
is on
, which in our case means open
. What we want to do here is run the notifier
function, in some number of seconds, of which is defined in our config file under the argument ttl
. Youâll see this long line of code actually wraps to the next line⌠weâll do a little bit more Python magic and supply notifier
two keywords (hey, remember dictionaries?) called friendly_name
and timer
, with their respective values.
- Buuuut thatâs not all this line of code is doing! Youâll see at the very front of the line, we are putting something into our
door_timer_library
dictionary. This is exactly how you add to a Python dictionary. Reference the dictionary, create the key inside the dictionary, and then assign that key a value. In our case, our good friend @aimc gives us another tool to work with called a handle
. Which is essentially a reference to the object weâve put in the scheduler: "in <value of ttl>
run self.notifier
with keyword arguments of friendly_name
and timer
"
In the notifier
section of your appâŚ
- This part is simple. We simply set a few variable for
title
, message
, and data
. Iâve made things a bit tidier, by putting a sort skeleton message for the title and message, where our keyword arguments, or kwargs
, fill in the games. Itâs kinda like a boring, practical game of madlibs.
- I didnât really understand your data that you send, so I simply copied it. Sorry 'bout that! If you give me more info, you might be able to customize this variable too!
- Finally weâll call the
notify
service to your device ios_v1ruexe
with the variables we defined earlier.
⌠but wait, thereâs one last thing SupahNoob. We have a way to SET timers ⌠but what happens when the door opens up, and then closes (set to off
) before the we reach the ttl
value?! You havenât set anything in tracker
for when new == 'off'
! Well, remember our little bit of Python magic thatâs out of the scope of the lesson? Itâs all covered there. Whenever a doorâs state is changed, tracker
gets called to run. Weâll then try
to run the cancel_timer
accessing the handle
we placed in the door_timer_libarary
which would remove that timer from our scheduler. If weâve not even set a timer for that entity
just yet, then weâll throw up a log in our log file with an information line that say it doesnât yet exist. Great! So when the door is closed, we donât need to be notified of the state. Case closed. But also, what happens if, somehow, for some reason, we call a door as open 2 times in a row? Well we then would simple ârefreshâ the timer on that door in the schedule. First, it would cancelled, and then it would get re-created as new == 'on'
!
So that was a lot! But I hope you got to learn something if you didnât know Python, and itâs totally possible that others here might have learned something too.
Otherwise, you just get to see my thought process.
//edit
@bbrendon @aimc @rpitera @Bit-River @ReneTode
This seems to have gotten a positive reception! Shoot me a message and let me know if youâd like to see me tackle other problems and explain them in such a way. I think there are a lot of users here that are unfamiliar with Python, but can see the power and flexibility it provides. I wouldnât even mind setting up a series wherein we tackle common problems!