Schlage Lock Notification

Hey Everyone,

I copied an appdaemon app/config created/modified by @kylerw from this post:

https://community.home-assistant.io/t/schlage-lock-notification-when-unlocked-for-a-period-of-time/3697?u=nvinceble

The following is the app:

import appdaemon.appapi as appapi

#
# DoorLeftUnlocked
#
class Notifications(appapi.AppDaemon):
  def initialize(self):
    self.log("Generic Notification App")
    notifyState = self.args["notifyState"]
    stopState = self.args["stopState"]
    entityType = self.args["entityType"]
    self.entity_timer = None
    if "entities" in self.args:
      for entity in self.split_device_list(self.args["entities"]):
        ## If Group - initialize all entities from the group
        if "group" in entity:
          self.log("Group provided.")
          groupitem = self.get_state(entity,"all")
          entity_list = groupitem['attributes']['entity_id']
          for member in entity_list:
		if entityType in member:
		  self.log("Initializing entity: " + self.friendly_name(member))
		  self.listen_state(self.state_changed, member, new=notifyState)
		  self.listen_state(self.state_changed, member, new=stopState)  
    else:
      self.log("Single entity provided.")
      self.log("Initializing entity: " + self.friendly_name(entity))
      self.listen_state(self.state_changed, member, new=notifyState)
      self.listen_state(self.state_changed, member, new=stopState)               
else:
  # This will monitory ALL of the entities in your house
  self.log("No entity provided in cfg, configure for all.")
  self.listen_state(self.state_changed, entityType, new=notifyState)
  self.listen_state(self.state_changed, entityType, new=stopState)
  def state_changed(self, entity, attribute, old, new, kwargs):
    notifyState = self.args["notifyState"]
    entitystate = new 
    if notifyState in entitystate:
      self.run_in(self.start_timer, 0, item=entity, new=entitystate)
    else:
      self.run_in(self.stop_timer, 0, item=entity, new=entitystate)     
  def send_notification(self, kwargs):
   # This sends a notifcation and then starts the timer for another XX minutes
    msg = kwargs["msg"]
    self.call_service("notify/notify", message="Generic: {}".format(msg))
    self.log(msg)
  def start_timer(self, kwargs):
    entity = kwargs["item"]
    entitystate = kwargs["new"]
    if "notifydelaymin" in self.args:
      cfgtimer = self.args["notifydelaymin"]
      notifydelay = int(cfgtimer) * 60
    else:
      notifydelay = 600
    msg = "{} is currently {}. Will alert again in {} seconds.".format(self.friendly_name(entity), entitystate, notifydelay)    
    ## Send notification immediately    
    self.run_in(self.send_notification, 0, new=entitystate, msg=msg)     
    ### Reset timer after XXX seconds
    self.entity_timer = self.run_in(self.start_timer, notifydelay, entity=entity, new=entitystate)
  def stop_timer(self, kwargs):
    entity = kwargs["item"]
    entitystate = kwargs["new"]
    msg = "{} is now {}. Cancelling timer.".format(self.friendly_name(entity), entitystate)
    self.run_in(self.send_notification, 0, new=entitystate, msg=msg)
    # This cancels the timer once the door is entityed
    self.cancel_timer(self.entity_timer)
    self.run_in(self.all_entities_check, 0)
  def all_entities_check(self, kwargs):
    notifyState = self.args["notifyState"]
    stopState = self.args["stopState"]
    entityType = self.args["entityType"]
    ## Will send alert if all entitys are entityed
    if "all_entity_alert" in self.args and self.args["all_entity_alert"] == "true":
      if "entities" in self.args:
        for entity in self.split_device_list(self.args["entities"]):
          allState = self.get_state(entity)
          if allState == stopState:
            break
      else:
        allState = get_state(entityType)
      if notifyState not in allState:
        msg = "All {}s are now {}.".format(entityType, stopState)
        self.run_in(self.send_notification, 0, msg=msg)

Config:

AppDaemon:
  errorfile: STDERR
  logfile: STDOUT
  threads: '10'
HADashboard:
  dash_url: http://10.100.10.30:5050
HASS:
  ha_key: MyKey
  ha_url: https://mydomain.com
hello_world:
  class: HelloWorld
  module: hello
lock_notifications:
  module: notifications_state-timer
  class: Notifications
  entityType: "lock"
  entities: group.all_locks
  notifyState: "unlocked"
  stopState: "locked"
  notifydelaymin: 10
  all_entity_alert: true
cover_notifications:
  module: notifications_state-timer
  class: Notifications
  entityType: "cover"
  entities: group.all_covers
  notifyState: "open"
  stopState: "closed"
  notifydelaymin: 10
  all_entity_alert: true

When I run appdaemon as a service there are no log or error logs which are created. Also, when i run using ā€œsudo ./appdaemon -c /home/pi/appdaemon/confā€, i get the following error:

2017-07-22 21:29:01.035214 INFO AppDaemon Version 2.0.7 starting
2017-07-22 21:29:01.035755 INFO Configuration read from: /home/pi/appdaemon/conf/appdaemon.yaml
2017-07-22 21:29:01.488428 INFO Got initial state
2017-07-22 21:29:01.490487 INFO Loading Module: /home/pi/appdaemon/conf/apps/notifications_state-timer.py
2017-07-22 21:29:01.492798 WARNING ------------------------------------------------------------
2017-07-22 21:29:01.493256 WARNING Unexpected error during loading of notifications_state-timer.py:
2017-07-22 21:29:01.493554 WARNING ------------------------------------------------------------
2017-07-22 21:29:01.496931 WARNING Traceback (most recent call last):
  File "/usr/local/lib/python3.4/dist-packages/appdaemon/appdaemon.py", line 911, in read_app
    conf.modules[module_name] = importlib.import_module(module_name)
  File "/usr/lib/python3.4/importlib/__init__.py", line 109, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "<frozen importlib._bootstrap>", line 2254, in _gcd_import
  File "<frozen importlib._bootstrap>", line 2237, in _find_and_load
  File "<frozen importlib._bootstrap>", line 2226, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 1200, in _load_unlocked
  File "<frozen importlib._bootstrap>", line 1129, in _exec
  File "<frozen importlib._bootstrap>", line 1467, in exec_module
  File "<frozen importlib._bootstrap>", line 1572, in get_code
  File "<frozen importlib._bootstrap>", line 1532, in source_to_code
  File "<frozen importlib._bootstrap>", line 321, in _call_with_frames_removed
  File "/home/pi/appdaemon/conf/apps/notifications_state-timer.py", line 21
    if entityType in member:
                           ^
TabError: inconsistent use of tabs and spaces in indentation

2017-07-22 21:29:01.497427 WARNING ------------------------------------------------------------
2017-07-22 21:29:01.497805 INFO Loading Module: /home/pi/appdaemon/conf/apps/motion.py
2017-07-22 21:29:01.499424 INFO Loading Module: /home/pi/appdaemon/conf/apps/hello.py
2017-07-22 21:29:01.500947 INFO Loading Object hello_world using class HelloWorld from module hello
2017-07-22 21:29:01.658270 INFO hello_world: Hello from AppDaemon
2017-07-22 21:29:01.660860 INFO hello_world: You are now ready to run Apps!
2017-07-22 21:29:01.661286 INFO Loading Module: /home/pi/appdaemon/conf/apps/lights.py
2017-07-22 21:29:01.662945 INFO App initialization complete
2017-07-22 21:29:01.663665 INFO Starting dashboard
2017-07-22 21:29:01.783943 INFO Connected to Home Assistant 0.49.0

anyone see anything iā€™m missing?

Your spacing is off according to the error.

Line 21-24 should be indented past line 20

import appdaemon.appapi as appapi

#
# DoorLeftUnlocked
#
class Notifications(appapi.AppDaemon):
  def initialize(self):
    self.log("Generic Notification App")
    notifyState = self.args["notifyState"]
    stopState = self.args["stopState"]
    entityType = self.args["entityType"]
    self.entity_timer = None
    
    if "entities" in self.args:
      for entity in self.split_device_list(self.args["entities"]):
    
        ## If Group - initialize all entities from the group
        if "group" in entity:
          self.log("Group provided.")
          groupitem = self.get_state(entity,"all")
          entity_list = groupitem['attributes']['entity_id']
    
          for member in entity_list:
    
            if entityType in member:
              self.log("Initializing entity: " + self.friendly_name(member))
              self.listen_state(self.state_changed, member, new=notifyState)
              self.listen_state(self.state_changed, member, new=stopState)  
            else:
              self.log("Single entity provided.")
              self.log("Initializing entity: " + self.friendly_name(entity))
              self.listen_state(self.state_changed, member, new=notifyState)
              self.listen_state(self.state_changed, member, new=stopState)               

    else:
      # This will monitory ALL of the entities in your house
      self.log("No entity provided in cfg, configure for all.")
      self.listen_state(self.state_changed, entityType, new=notifyState)
      self.listen_state(self.state_changed, entityType, new=stopState)


  def state_changed(self, entity, attribute, old, new, kwargs):
    notifyState = self.args["notifyState"]
    entitystate = new 

    if notifyState in entitystate:
      self.run_in(self.start_timer, 0, item=entity, new=entitystate)
    else:
      self.run_in(self.stop_timer, 0, item=entity, new=entitystate)     


  def send_notification(self, kwargs):
   # This sends a notifcation and then starts the timer for another XX minutes
    msg = kwargs["msg"]
    self.call_service("notify/notify", message="Generic: {}".format(msg))
    self.log(msg)


  def start_timer(self, kwargs):
    entity = kwargs["item"]
    entitystate = kwargs["new"]

    if "notifydelaymin" in self.args:
      cfgtimer = self.args["notifydelaymin"]
      notifydelay = int(cfgtimer) * 60
    else:
      notifydelay = 600

    msg = "{} is currently {}. Will alert again in {} seconds.".format(self.friendly_name(entity), entitystate, notifydelay)    

    ## Send notification immediately    
    self.run_in(self.send_notification, 0, new=entitystate, msg=msg)     

    ### Reset timer after XXX seconds
    self.entity_timer = self.run_in(self.start_timer, notifydelay, entity=entity, new=entitystate)


  def stop_timer(self, kwargs):
    entity = kwargs["item"]
    entitystate = kwargs["new"]
    msg = "{} is now {}. Cancelling timer.".format(self.friendly_name(entity), entitystate)
    self.run_in(self.send_notification, 0, new=entitystate, msg=msg)

    # This cancels the timer once the door is entityed
    self.cancel_timer(self.entity_timer)
    self.run_in(self.all_entities_check, 0)


  def all_entities_check(self, kwargs):
    notifyState = self.args["notifyState"]
    stopState = self.args["stopState"]
    entityType = self.args["entityType"]

    ## Will send alert if all entitys are entityed
    if "all_entity_alert" in self.args and self.args["all_entity_alert"] == "true":
      if "entities" in self.args:
        for entity in self.split_device_list(self.args["entities"]):
          allState = self.get_state(entity)
          if allState == stopState:
            break

      else:
        allState = get_state(entityType)

      if notifyState not in allState:
        msg = "All {}s are now {}.".format(entityType, stopState)
        self.run_in(self.send_notification, 0, msg=msg)
2 Likes

Wow quick responses! OK thanks, now I am seeing the following, also shouldnā€™t my log and error files be created in the appdaemon directory? canā€™t find them anywhere.

here is my error:

2017-07-22 21:36:45.015127 INFO Configuration read from: /home/pi/appdaemon/conf/appdaemon.yaml
2017-07-22 21:36:46.117059 INFO Got initial state
2017-07-22 21:36:46.121027 INFO Loading Module: /home/pi/appdaemon/conf/apps/notifications_state-timer.py
2017-07-22 21:36:46.134704 INFO Loading Object lock_notifications using class Notifications from module notifications_state-timer
2017-07-22 21:36:46.468750 INFO lock_notifications: Generic Notification App
2017-07-22 21:36:46.474176 INFO lock_notifications: Group provided.
2017-07-22 21:36:46.480666 INFO lock_notifications: Initializing entity: Outside Garage Door Lock Locked
2017-07-22 21:36:46.482231 INFO Loading Object cover_notifications using class Notifications from module notifications_state-timer
2017-07-22 21:36:46.488077 INFO cover_notifications: Generic Notification App
2017-07-22 21:36:46.493803 INFO cover_notifications: Group provided.
2017-07-22 21:36:46.494974 WARNING ------------------------------------------------------------
2017-07-22 21:36:46.495700 WARNING Unexpected error during loading of cover_notifications:
2017-07-22 21:36:46.496468 WARNING ------------------------------------------------------------
2017-07-22 21:36:46.498286 WARNING Traceback (most recent call last):
  File "/usr/local/lib/python3.4/dist-packages/appdaemon/appdaemon.py", line 921, in read_app
    init_object(name, class_name, module_name, config[name])
  File "/usr/local/lib/python3.4/dist-packages/appdaemon/appdaemon.py", line 595, in init_object
    conf.objects[name]["object"].initialize()
  File "/home/pi/appdaemon/conf/apps/notifications_state-timer.py", line 21, in initialize
    entity_list = groupitem['attributes']['entity_id']
TypeError: 'NoneType' object is not subscriptable

2017-07-22 21:36:46.499538 WARNING ------------------------------------------------------------
2017-07-22 21:36:46.500490 INFO Loading Module: /home/pi/appdaemon/conf/apps/motion.py
2017-07-22 21:36:46.503969 INFO Loading Module: /home/pi/appdaemon/conf/apps/hello.py
2017-07-22 21:36:46.507008 INFO Loading Object hello_world using class HelloWorld from module hello
2017-07-22 21:36:46.522142 INFO hello_world: Hello from AppDaemon
2017-07-22 21:36:46.527589 INFO hello_world: You are now ready to run Apps!
2017-07-22 21:36:46.528449 INFO Loading Module: /home/pi/appdaemon/conf/apps/lights.py
2017-07-22 21:36:46.531982 INFO App initialization complete
2017-07-22 21:36:46.533654 INFO Starting dashboard
2017-07-22 21:36:46.784164 INFO Connected to Home Assistant 0.49.0

scratch thatā€¦ i know why iā€™m getting the errorā€¦ i donā€™t have a cover.

Thanks! On mobile so couldnā€™t make the edit myself.

Just ran into this error while testing the app:

Jul 22 22:00:19 RPi3 appdaemon[28656]: 2017-07-22 22:00:19.009623 WARNING Args: {'basetime': 1500786019, 'timestamp': 1500786019, 'name': 'lock_notifications', 'type': None, 'id': UUID('1dcf992b-b248-495f-b24a-4dfadsff'), 'repeat': False, 'offset': 0, 'interval': 0, 'kwargs': {'new': 'unlocked', 'entity': 'lock.outside_garage_door_lock_locked'}, 'callback': <bound method Notifications.start_timer of <notifications_state-timer.Notifications object at 0x75a23ef0>>}
Jul 22 22:00:19 RPi3 appdaemon[28656]: 2017-07-22 22:00:19.010380 WARNING ------------------------------------------------------------
Jul 22 22:00:19 RPi3 appdaemon[28656]: 2017-07-22 22:00:19.011461 WARNING Traceback (most recent call last):
Jul 22 22:00:19 RPi3 appdaemon[28656]: File "/usr/local/lib/python3.4/dist-packages/appdaemon/appdaemon.py", line 316, in exec_schedule
Jul 22 22:00:19 RPi3 appdaemon[28656]: "attribute": args["kwargs"]["attribute"],
Jul 22 22:00:19 RPi3 appdaemon[28656]: KeyError: 'attribute'
Jul 22 22:00:19 RPi3 appdaemon[28656]: 2017-07-22 22:00:19.012238 WARNING ------------------------------------------------------------
Jul 22 22:00:19 RPi3 appdaemon[28656]: 2017-07-22 22:00:19.013288 WARNING Scheduler entry has been deleted
Jul 22 22:00:19 RPi3 appdaemon[28656]: 2017-07-22 22:00:19.014090 WARNING ------------------------------------------------------------

This normally means that it canā€™t find an entry named ā€˜attributeā€™ in the structure.

Just a brief look at the code, but it seems you were using attributes elsewhere?

Create a directory for your logs, make sure the user running appdameon has the proper perms to that directory, then put the full path into your AD config like so:

AppDaemon:
  errorfile: /home/pi/adconf/logs/error.log
  logfile: /home/pi/adconf/logs/appdaemon.log
1 Like

I literally copied that code from @kylerw so iā€™m not sure. iā€™m reading through the code to try and understand how it works.

Thanks for the help @rpitera

1 Like

Ok so after going through the code several times, I was able to find the problem (i think). Iā€™m not a programming guy so take this with some skepticism.

I changed the value ā€˜entityā€™ with ā€˜itemā€™ in the following functions:

  • def start_timer
  • def stop_timer
  • def all_entities_check

Here is the updated code that works for me:

import appdaemon.appapi as appapi

#
# DoorLeftUnlocked
#
class Notifications(appapi.AppDaemon):
  def initialize(self):
    self.log("Generic Notification App")
    notifyState = self.args["notifyState"]
    stopState = self.args["stopState"]
    entityType = self.args["entityType"]
    self.entity_timer = None
    
    if "entities" in self.args:
      for entity in self.split_device_list(self.args["entities"]):
    
        ## If Group - initialize all entities from the group
        if "group" in entity:
          self.log("Group provided.")
          groupitem = self.get_state(entity,"all")
          entity_list = groupitem['attributes']['entity_id']
    
          for member in entity_list:
    
            if entityType in member:
              self.log("Initializing entity: " + self.friendly_name(member))
              self.listen_state(self.state_changed, member, new=notifyState)
              self.listen_state(self.state_changed, member, new=stopState)  
            else:
              self.log("Single entity provided.")
              self.log("Initializing entity: " + self.friendly_name(entity))
              self.listen_state(self.state_changed, member, new=notifyState)
              self.listen_state(self.state_changed, member, new=stopState)               

    else:
      # This will monitory ALL of the entities in your house
      self.log("No entity provided in cfg, configure for all.")
      self.listen_state(self.state_changed, entityType, new=notifyState)
      self.listen_state(self.state_changed, entityType, new=stopState)


  def state_changed(self, entity, attribute, old, new, kwargs):
    notifyState = self.args["notifyState"]
    entitystate = new 

    if notifyState in entitystate:
      self.run_in(self.start_timer, 0, item=entity, new=entitystate)
    else:
      self.run_in(self.stop_timer, 0, item=entity, new=entitystate)     


  def send_notification(self, kwargs):
   # This sends a notifcation and then starts the timer for another XX minutes
    msg = kwargs["msg"]
    self.call_service("notify/pushbullet", message="Generic: {}".format(msg))
    self.log(msg)


  def start_timer(self, kwargs):
    item = kwargs["item"]
    entitystate = kwargs["new"]

    if "notifydelaymin" in self.args:
      cfgtimer = self.args["notifydelaymin"]
      notifydelay = int(cfgtimer) * 60
    else:
      notifydelay = 600

    msg = "{} is currently {}. Will alert again in {} seconds.".format(self.friendly_name(item), entitystate, notifydelay)    

    ## Send notification immediately    
    self.run_in(self.send_notification, 0, new=entitystate, msg=msg)     

    ### Reset timer after XXX seconds
    self.entity_timer = self.run_in(self.start_timer, notifydelay, item=item, new=entitystate)


  def stop_timer(self, kwargs):
    item = kwargs["item"]
    entitystate = kwargs["new"]
    msg = "{} is now {}. Cancelling timer.".format(self.friendly_name(item), entitystate)
    self.run_in(self.send_notification, 0, new=entitystate, msg=msg)

    # This cancels the timer once the door is entityed
    self.cancel_timer(self.entity_timer)
    self.run_in(self.all_entities_check, 0)


  def all_entities_check(self, kwargs):
    notifyState = self.args["notifyState"]
    stopState = self.args["stopState"]
    entityType = self.args["entityType"]

    ## Will send alert if all entitys are entityed
    if "all_entity_alert" in self.args and self.args["all_entity_alert"] == "true":
      if "entities" in self.args:
        for item in self.split_device_list(self.args["entities"]):
          allState = self.get_state(item)
          if allState == stopState:
            break

      else:
        allState = get_state(entityType)

      if notifyState not in allState:
        msg = "All {}s are now {}.".format(entityType, stopState)
        self.run_in(self.send_notification, 0, msg=msg)
1 Like

Quick questionā€¦ not sure why, but every time I restart HA, i get a notification that my lock is locked and the timer is cancelling. Would someone know why this would be happening?

code:

import appdaemon.appapi as appapi
import datetime

#
# DoorLeftUnlocked
#
class Notifications(appapi.AppDaemon):
  def initialize(self):
    self.log("Generic Notification App")
    notifyState = self.args["notifyState"]
    stopState = self.args["stopState"]
    entityType = self.args["entityType"]
    self.entity_timer = None
    
    if "entities" in self.args:
      for entity in self.split_device_list(self.args["entities"]):
    
        ## If Group - initialize all entities from the group
        if "group" in entity:
          self.log("Group provided.")
          groupitem = self.get_state(entity,"all")
          entity_list = groupitem['attributes']['entity_id']
    
          for member in entity_list:
    
            if entityType in member:
              self.log("Initializing entity: " + self.friendly_name(member))
              self.listen_state(self.state_changed, member, new=notifyState)
              self.listen_state(self.state_changed, member, new=stopState)  
            else:
              self.log("Single entity provided.")
              self.log("Initializing entity: " + self.friendly_name(entity))
              self.listen_state(self.state_changed, member, new=notifyState)
              self.listen_state(self.state_changed, member, new=stopState)               

    else:
      # This will monitory ALL of the entities in your house
      self.log("No entity provided in cfg, configure for all.")
      self.listen_state(self.state_changed, entityType, new=notifyState)
      self.listen_state(self.state_changed, entityType, new=stopState)


  def state_changed(self, entity, attribute, old, new, kwargs):
    notifyState = self.args["notifyState"]
    entitystate = new 

    if notifyState in entitystate:
      self.run_in(self.start_timer, 0, item=entity, new=entitystate)
    else:
      self.run_in(self.stop_timer, 0, item=entity, new=entitystate)     


  def send_notification(self, kwargs):
   # This sends a notifcation and then starts the timer for another XX minutes
    msg = kwargs["msg"]
    item = kwargs["item"]
    self.call_service("notify/pushbullet", title="{}".format(self.friendly_name(item)), message="{}".format(msg))
    self.log(msg)


  def start_timer(self, kwargs):
    item = kwargs["item"]
    entitystate = kwargs["new"]

    if "notifydelaymin" in self.args:
      cfgtimer = self.args["notifydelaymin"]
      notifydelay = int(cfgtimer) * 60
    else:
      notifydelay = 600

    msg = "It is {} and {} is currently {}. Will alert again in {} seconds.".format(datetime.datetime.now().strftime("%I:%M%p on %B %d, %Y"), self.friendly_name(item), entitystate, notifydelay)    

    ## Send notification immediately    
    self.run_in(self.send_notification, 0, new=entitystate, item=item, msg=msg)     

    ### Reset timer after XXX seconds
    self.entity_timer = self.run_in(self.start_timer, notifydelay, item=item, new=entitystate)


  def stop_timer(self, kwargs):
    item = kwargs["item"]
    entitystate = kwargs["new"]
    msg = "It is {} and {} is now {}. Cancelling timer.".format(datetime.datetime.now().strftime("%I:%M%p on %B %d, %Y"), self.friendly_name(item), entitystate)
    self.run_in(self.send_notification, 0, new=entitystate, item=item, msg=msg)

    # This cancels the timer once the door is entityed
    self.cancel_timer(self.entity_timer)
    self.run_in(self.all_entities_check, 0)


  def all_entities_check(self, kwargs):
    notifyState = self.args["notifyState"]
    stopState = self.args["stopState"]
    entityType = self.args["entityType"]

    ## Will send alert if all entitys are entityed
    if "all_entity_alert" in self.args and self.args["all_entity_alert"] == "true":
      if "entities" in self.args:
        for item in self.split_device_list(self.args["entities"]):
          allState = self.get_state(item)
          if allState == stopState:
            break

      else:
        allState = get_state(entityType)

      if notifyState not in allState:
        msg = "All {}s are now {}.".format(entityType, stopState)
        self.run_in(self.send_notification, 0, msg=msg)

as i see it there is a state change during startup from HA.
and the state that is given by HA differs from the argument you gave for notifystate.

you could check that out by putting in a line like

self.log("new state provided by HA: " + entitystate + " and new state provided by args: " + notifystate)

before the if statement in state_changed

1 Like

Ok so when i unlocked the door:
entitystate = unlocked
notifyState = unlocked

relocked the door:
entitystate = locked
notifyState = unlocked

restarted HA without restarting appdaemon:
entitystate = locked
notifyState = unlocked

Also, the notifications donā€™t stop after i re-lock the door.

HA gives a statechange to locked on restart.

in the state_changed function that results in run the stop timer.
so you probably want to add a check there if HA is restarted.