Alarm_reset can't write to DB

I’m running the alarm_reset code by @Soul from here Manual Home Alarm state restoration on HASS restart

My config section looks like this:

# This is to be added after the standard AppDaemon configuration
Alarm_Reset:
  class: AlarmReset
  module: alarmreset
# Choose where to store the states across restarts
  file: /home/homeassistant/.homeassistant/alarm
  delay: '0'

It creates an alarm.db file but doesn’t seem to write to it, nor does it reset the alarm to the previous state on reboot. I’ve tried changing the permissions to 777 with no luck. Any ideas what the problem could be? I’m running AppDaemon 3.0.1, and it’s run on my hassbian based pi3 I think as the pi user. Wondering if the last part is the problem?

no the problem is that the code is old.
it suggests that you need to put that piece in the appdaemon.yaml but nowadays the app configuration goes into a seperate yaml file in the same dir as the apps.

if you have the yaml inside the apps dir then there is another problem.
did you check your errorlog?

Yes, i have an apps.yaml file inside the apps folder. I don’t think anything gets output to the error log. I see this when I change the state:

2018-06-17 08:00:25.013086 INFO Alarm_Reset: Checking for alarm_control_panel
2018-06-17 08:00:25.140298 INFO Alarm_Reset: Adding alarm_control_panel.home_alarm, setting value to current state (disarmed)
2018-06-17 08:01:12.118129 INFO Alarm_Reset: State change: alarm_control_panel.home_alarm to armed_home

and then this on reboot

2018-06-17 08:02:55.805641 INFO Alarm_Reset: Checking for alarm_control_panel
2018-06-17 08:02:55.891898 INFO Alarm_Reset: Adding alarm_control_panel.home_alarm, setting value to current state (disarmed)

this is the normal log.

AD has 4 log types that you can set.

  accessfile: /your/path/to/appdaemon_dashboard.log
  errorfile: /your/path/to/appdaemon_error.log
  logfile: /your/path/to/appdaemon_normal.log
  diagfile:  /your/path/to/appdaemon_diag.log

i have no knowledge from this app, and at the moment no time to debug that, but check if you have errors.
if not then maybe add some extra log lines to see if it really gets put in the db, or that the app stops there without an error.

Nothing in the error log. It’s based on the switch_reset app, which also doesn’t seem to be working. The issue seems to be in here:

for entity in state:

  type, id = entity.split(".")
  if type == "alarm_control_panel":
    if entity in self.device_db:
      if self.device_db[entity] != state[entity]["state"]:
        self.log_notify("Setting {} to {} (was {})".format(entity, self.device_db[entity], state[entity]["state"]))
        new_state = self.set_state(entity, state = self.device_db[entity])
    else:
      self.log_notify("Adding {}, setting value to current state ({})".format(entity, state[entity]["state"]))
      self.device_db[entity] = state[entity]["state"]

It’s picking up the state change, but it’s not writing it to the DB.

it is probably the connection to the db.

does the app have a terminate function?

it seems to me that the app opens a connection to the db in the initialise and then keeps it open.
but if it isnt closed correctly then it cant reconnect.
db use from python with threading is no easy item.

this piece off code in itselve doesnt save anything by the way. so the problem wont be there i think.
but there is another thing that will go wrong with this code.
set_state will work with a sensor, but not with most other type of entities.
i am not sure if set_state will work with an alarm control entity.
isnt there a service to set the alarm control?

Nope, no terminate function that I can see. There is a service to set the alarm control panel, alarm_control_panel.alarm_arm_away, alarm_control_panel.alarm_arm_home, etc. The code can set the alarm state properly (ie, there are times when the DB says it should be set to “alarm home” and on reboot it will set it to that as opposed to disarmed), the issue is saving the state. I’m not a coder really but I’ll see if I can figure out how to improve the DB side of things.

@phantomdarkness,

As @ReneTode rightly said, 1) The code is old, 2) the code doesn’t save anything and 3) the connection to the db file is not closed. So there is a lot that can prevent it from working. I do something similar, but I just store my settings in a .json file as I haven’t used the shelve.open() before but use sqlite in one of my apps heavily; so nice to learn something new. I suggest you do the following:

No need opening the db file in def initialize since technically its of no use.

Listen for when the alarm service is ran, and use that to store like the following

def initialize(self):
    self.alarm_handler = self.listen_event(self.store_alarm_state, "call_service", domain = "alarm_control_panel")
    self.listen_event(self.restore_alarm_state, "plugin_started")

def store_alarm_state(self, event_name, data, kwargs):
    with shelve.open(self.args["file"]) as db:
        db["alarm setting"] = {'state': data['service'], 'entity': data['service_data']['entity_id']}

def restore_alram_state(self, event_name, data, kwargs):
    self.cancel_listen_event(self.alarm_handler) # to avoid a potential loop, not so bad though
    with shelve.open(self.args["file"]) as db:
        state = db["alarm setting"]['state']
        entity_id = db["alarm setting"]['entity']
        self.call_service("alarm_control_panel/{}".format(state), entity_id = entity_id)
    self.alarm_handler = self.listen_event(self.store_alarm_state, "call_service", domain = "alarm_control_panel")

To be honest, I am not certain what data['service_data']['entity_id'] will give you, but this is where you will have to send it to the logs then work on it from there.

Hope it helps. @ReneTode will be able to confirm if I am missing anything.

@ReneTode I actually forgot there was a terminate() in AppD, thanks for the reminder. Should have been using it :thinking:

Regards

1 Like

Hmmm…got this error when I rebooted:

2018-06-18 10:38:26.780112 WARNING AppDaemon: ------------------------------------------------------------
2018-06-18 10:38:26.780869 WARNING AppDaemon: Unexpected error in worker for App Alarm_Reset:
2018-06-18 10:38:26.781479 WARNING AppDaemon: Worker Ags: {'event': 'plugin_started', 'kwargs': {}, 'name': 'Alarm_Reset', 'data': {'name': 'default'}, 'type': 'event', 'id': UUID('30ad14d6-7088-47ad-ae52-c2e52ed2ac4d'), 'function': <bound method AlarmReset.restore_alarm_state of <alarmreset.AlarmReset object at 0x700414b0>>}
2018-06-18 10:38:26.781919 WARNING AppDaemon: ------------------------------------------------------------
2018-06-18 10:38:26.783452 WARNING AppDaemon: Traceback (most recent call last):
  File "/usr/local/lib/python3.5/dist-packages/appdaemon/appdaemon.py", line 595, in worker
    funcref(args["event"], data, args["kwargs"])
  File "/home/homeassistant/.homeassistant/apps/alarmreset.py", line 29, in restore_alarm_state
    self.cancel_listen_event(self.self.alarm_handler) # to avoid a potential loop, not so bad though
AttributeError: 'AlarmReset' object has no attribute 'self'

2018-06-18 10:38:26.784006 WARNING AppDaemon: ------------------------------------------------------------

Din’t see anything in the log saying it was writing to the DB either on alarm state change. I appreciate the help!

Oh apologies I made an error in the code.

instead of self.cancel_listen_event(self.self.alarm_handler) should be self.cancel_listen_event(self.alarm_handler)

Please mind you, I haven’t tested this code, so will need a lot of walk thru :wink:. Will update what I wrote above

Regards

Nope :frowning: no error this time but still not saving. I’m doing this remotely atm, i’ll look more into it this evening.

Did you try to make changes to the alarm state? If you don’t it won’t save anything.

Also in the function def store_alarm_state(self, event_name, data, kwargs):, it will be nice to see the output of data like adding the lines self.log(data), so one can be sure the right info is being retrieved and saved.

Regards

2018-06-18 13:03:46.094755 INFO Alarm_Reset: {'service_data': {'entity_id': 'alarm_control_panel.home_alarm', 'code': 'XXXX'}, 'service_call_id': '1976341200-39', 'service': 'alarm_arm_home', 'domain': 'alarm_control_panel'}

So it’s picking up on the change but not writing to the db (though it did appear to modify the DB file, but nothing I can see in there).

Ok, try this code then

import json

def initialize(self):
    self.alarm_handler = self.listen_event(self.store_alarm_state, "call_service", domain = "alarm_control_panel")
    self.listen_event(self.restore_alarm_state, "plugin_started")

def store_alarm_state(self, event_name, data, kwargs):
    with shelve.open(self.args["file"]) as db:
        db["alarm setting"] = json.dumps({'state': data['service'], 'entity': data['service_data']['entity_id']})

def restore_alram_state(self, event_name, data, kwargs):
    self.cancel_listen_event(self.alarm_handler) # to avoid a potential loop, not so bad though
    with shelve.open(self.args["file"]) as db:
        alarm_settings = json.loads(db["alarm setting"])
        self.log(alarm_settings)
        state = alarm_settings['state']
        entity_id = alarm_settings['entity']
        self.call_service("alarm_control_panel/{}".format(state), entity_id = entity_id)
    self.alarm_handler = self.listen_event(self.store_alarm_state, "call_service", domain = "alarm_control_panel")

2 things:

if you take the efford to make an app that should work, then you probably also should give the line import hass and the class line to avoid that people make errors there :wink:

and the second is a question:
is shelve default python?
i guess i need to read up on that soon then :wink:
i just write my data to files. (but want to change that for a long time now)

1 Like

Getting there! DB part seems fine. Just not setting the alarm on reboot (i can confirm it’s trying to set to the proper state, just that it’s not doing so).

@ReneTode, now that is a very good point :laughing:. And yes its standard python and as I writing this, already moving all my json files to shelve now as I think its cool.

@phantomdarkness, well just noticed I did something silly which of course will make it now work as I forgot to add the code. So try this code instead

import appdaemon.plugins.hass.hassapi as hass
import shelve
import json

class AlarmReset(hass.Hass):

    def initialize(self):
        self.alarm_handler = self.listen_event(self.store_alarm_state, "call_service", domain = "alarm_control_panel")
        self.listen_event(self.restore_alarm_state, "plugin_started")

    def store_alarm_state(self, event_name, data, kwargs):
        with shelve.open(self.args["file"]) as db:
            db["alarm setting"] = json.dumps({'state': data['service'], 'entity': data['service_data']['entity_id'], 'code': data['service_data']['code'] })

    def restore_alarm_state(self, event_name, data, kwargs):
        self.cancel_listen_event(self.alarm_handler) # to avoid a potential loop, not so bad though
        with shelve.open(self.args["file"]) as db:
            alarm_settings = json.loads(db["alarm setting"])
            self.log(alarm_settings)
            state = alarm_settings['state']
            entity_id = alarm_settings['entity']
            code = alarm_settings['code']
            self.call_service("alarm_control_panel/{}".format(state), entity_id = entity_id, code = code)
        self.alarm_handler = self.listen_event(self.store_alarm_state, "call_service", domain = "alarm_control_panel")

Now this should go thru, as I hope it doesn’t expect any encryption from the code side of things.

Regards

1 Like

Success!!! THANK YOU SO MUCH!!! Just fyi, you have a typo in “def restore_alarm_state” (you have “alram”). I also made a small tweak to the log output on restore so it doesn’t output the alarm code in the log. Here’s my full code.

import appdaemon.plugins.hass.hassapi as hass
import shelve
import time
import json

#
# App to reset alarm_control_panel to previous state after HA restart
#
# Args:
#
# file - file in which to save state
#

class AlarmReset(hass.Hass):

  def initialize(self):
    self.alarm_handler = self.listen_event(self.store_alarm_state, "call_service", domain = "alarm_control_panel")
    self.listen_event(self.restore_alarm_state, "plugin_started")

  def store_alarm_state(self, event_name, data, kwargs):
    with shelve.open(self.args["file"]) as db:
        db["alarm setting"] = json.dumps({'state': data['service'], 'entity': data['service_data']['entity_id'], 'code': data['service_data']['code'] })
    self.log("Alarm state changed to {}".format(data['service']))

  def restore_alarm_state(self, event_name, data, kwargs):
      self.cancel_listen_event(self.alarm_handler) # to avoid a potential loop, not so bad though
      with shelve.open(self.args["file"]) as db:
          alarm_settings = json.loads(db["alarm setting"])
          self.log("Restoring state to {}".format(alarm_settings['state']))
          state = alarm_settings['state']
          entity_id = alarm_settings['entity']
          code = alarm_settings['code']
          self.call_service("alarm_control_panel/{}".format(state), entity_id = entity_id, code = code)
      self.alarm_handler = self.listen_event(self.store_alarm_state, "call_service", domain = "alarm_control_panel")
1 Like

Glad it helped mate, and thanks for the correction.

Regards

1 Like