craigjmidwinter / total-connect-client

Python Client for TotalConnect based alarm systems
MIT License
13 stars 14 forks source link

[Feature Request] Implement Custom Arm #38

Closed uchagani closed 1 year ago

uchagani commented 5 years ago

Would be cool if we implemented Custom Arming https://rs.alarmnet.com/TC21api/tc2.asmx?op=CustomArmSecuritySystem that way we can arm certain zones through Home Assistant. We will have to implement getting zones (this may be done already).

uchagani commented 4 years ago

@austinmroczek the work you've done with zones, can that be used to implement this feature?

austinmroczek commented 4 years ago

We have the zone info needed, it will be a question of how to select or enter what zones you want armed. Think it should be fairly straightforward within this code, but at the moment don’t see how we do it using the HA UI, other than one zone at a time

And the usual caution that the documentation is not very good..so hopefully this function does what it says

Sent from my iPhone

On Feb 19, 2020, at 10:53 AM, uchagani notifications@github.com wrote:

@austinmroczek the work you've done with zones, can that be used to implement this feature?

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub, or unsubscribe.

uchagani commented 4 years ago

We really just need a service call in HA that can take a list of zones. Selecting in the UI would be up to the user to figure out.

In total-connect-client, do we have the ability to arm custom with a list of zones?

On Wed, Feb 19, 2020 at 2:47 PM Austin Mroczek notifications@github.com wrote:

We have the zone info needed, it will be a question of how to select or enter what zones you want armed. Think it should be fairly straightforward within this code, but at the moment don’t see how we do it using the HA UI, other than one zone at a time

And the usual caution that the documentation is not very good..so hopefully this function does what it says

Sent from my iPhone

On Feb 19, 2020, at 10:53 AM, uchagani notifications@github.com wrote:

@austinmroczek the work you've done with zones, can that be used to implement this feature?

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub, or unsubscribe.

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/craigjmidwinter/total-connect-client/issues/38?email_source=notifications&email_token=AAIBHEALRTW36VPJZ42GAITRDWEGDA5CNFSM4HMT5EGKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEMJH2FI#issuecomment-588414229, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAIBHEASNWD2UD5RAKODUETRDWEGDANCNFSM4HMT5EGA .

austinmroczek commented 4 years ago

It should be pretty quick to build a call from total-connect-client. I'd need some help testing.

uchagani commented 4 years ago

i use that feature all the time so if you have the time to write it up i'll gladly help test it

austinmroczek commented 4 years ago

Looking at the SOAP call, the one piece of info I don't think we have is the "ArmMode". I assume it's different than ArmType, which we have most of the codes for. We may have to figure that out by trial and error, unless Honeywell gave @craigjmidwinter some documentation that is not available to the general public.

dulitz commented 3 years ago

How is custom arm different than just bypassing some zones and then doing a normal arm? All those features are already available in the client, so maybe all you need to do is add the service call in HA?

I like how Lyric seems to handle it according to alarmgrid: you select the arm custom button, it puts up a list of zones, you select the ones you want to bypass, and then the next time you press arm custom you get those zones as the default. That sounds like a great experience and having a specific "arm custom" function (in addition to the zone bypass function we have now) wouldn't help get there.

Since I'm on a cleanup binge, the unfinished and not-working "arm custom" code is yanking my chain a little bit. :) I think we should only widen the interface if it adds functionality, and it's not clear to me right now that it does.

dulitz commented 3 years ago

As an alternative to arm custom (in the client API) I would like to see an argument to the normal arming methods where you specify which zones to bypass. That is, you could choose between:

We'd have to think about what to do with low battery zones when auto-bypass-low-battery is set, and when it's not.

We could do that using only the operations we use now, without adding custom arm. And then if there was some advantage to using the custom arm operation, we could add it later without changing our API, as an optimization.

essvee0 commented 1 year ago

Adding my 2 cents of support for this feature! Personally I have 1 zone that I bypass quite often (and am comfortable with always bypassing if necessary). I have attempted to hack the totalconnect HA integration to bypass that zone before arming, but I am seeing errors. This is what my code looks like:

def _arm_night(self, code=None):
   self._location.zone_bypass("1")

   """Arm night synchronous."""
   ArmingHelper(self._partition).arm_stay_night()

And upon arming, I get this error in the log:

2022-09-23 18:37:06 ERROR (MainThread) [homeassistant.core] Error executing service: <ServiceCall alarm_control_panel.alarm_arm_night (c:<# removed>): entity_id=['alarm_control_panel.home']>
Traceback (most recent call last):
  File "/usr/src/homeassistant/homeassistant/core.py", line 1722, in catch_exceptions
    await coro_or_task
  File "/usr/src/homeassistant/homeassistant/core.py", line 1741, in _execute_service
    await cast(Callable[[ServiceCall], Awaitable[None]], handler.job.target)(
  File "/usr/src/homeassistant/homeassistant/helpers/entity_component.py", line 204, in handle_service
    await service.entity_service_call(
  File "/usr/src/homeassistant/homeassistant/helpers/service.py", line 680, in entity_service_call
    future.result()  # pop exception if have
  File "/usr/src/homeassistant/homeassistant/helpers/entity.py", line 964, in async_request_call
    await coro
  File "/usr/src/homeassistant/homeassistant/helpers/service.py", line 717, in _handle_entity_call
    await result
  File "/config/custom_components/totalconnect/alarm_control_panel.py", line 227, in async_alarm_arm_night
    await self.hass.async_add_executor_job(self._arm_home_instant)
  File "/usr/local/lib/python3.9/concurrent/futures/thread.py", line 58, in run
    result = self.fn(*self.args, **self.kwargs)
  File "/config/custom_components/totalconnect/alarm_control_panel.py", line 261, in _arm_home_instant
  File "/usr/local/lib/python3.9/site-packages/total_connect_client/location.py", line 209, in zone_bypass
    self.zones[zone_id]._mark_as_bypassed()
KeyError: '1'

Am I calling zone_bypass correctly? Or is there a need for an ArmingHelper that also takes a list of zones to bypass? Thank you!

austinmroczek commented 1 year ago

KeyError means you're asking for a zone_id that is not in the zones dictionary. I'm not in a good place to confirm (not with my normal coding machine) but please try passing the zone_id as a number instead of a string, like this

self._location.zone_bypass(1)
austinmroczek commented 1 year ago

It seems like we may have two different concepts here: a) The original above asks about 'ArmCustom'. The API seems to allow for various settings, one of which is to bypass certain zones while arming. b) A request to bypass zones prior to arming.

For a) We still need to figure out values for "ArmMode". I would have a hard time testing any of this because my panel doesn't have an ArmCustom capability as far as I know.

For b) I think it would be straightforward to add a location.bypass_all_and_arm() function. We already know what zones are faulted, so we would not need any other user inputs. The only issue would be if some zones are not able to be bypassed.

essvee0 commented 1 year ago

KeyError means you're asking for a zone_id that is not in the zones dictionary. I'm not in a good place to confirm (not with my normal coding machine) but please try passing the zone_id as a number instead of a string, like this

self._location.zone_bypass(1)

Thanks, yes I actually tried with and without quotes and the same error occurred.

essvee0 commented 1 year ago

It seems like we may have two different concepts here: a) The original above asks about 'ArmCustom'. The API seems to allow for various settings, one of which is to bypass certain zones while arming. b) A request to bypass zones prior to arming.

For a) We still need to figure out values for "ArmMode". I would have a hard time testing any of this because my panel doesn't have an ArmCustom capability as far as I know.

For b) I think it would be straightforward to add a location.bypass_all_and_arm() function. We already know what zones are faulted, so we would not need any other user inputs. The only issue would be if some zones are not able to be bypassed.

I also don’t have “arm custom” option on my panel, so what I would love is b). I didn’t even consider that it could all be done based on what was already faulted. That would be perfect. I’d be happy to test it if that’s the path you go down. Thanks so much!

dulitz commented 1 year ago

My current understanding is that ArmCustom is the same thing as arming with zones bypassed. Which is also what this article says. ArmCustom remembers the same set of bypasses that you used last time.

We could do "bypass all and arm" in the API but that is dangerous in that you don't know what zones were bypassed. Maybe you looked at it 10 seconds ago and only the windows were faulted and then you did "bypass all and arm," but the back door faulted right when you were calling that and now your back door is bypassed.

I have the following suggestion for what it's worth:

dulitz commented 1 year ago

Oh, and essvee0 -- I think the issue with your hack is that you must call get_zone_details() on the location before you can call zone_bypass().

Possibly we should have zone_bypass() call get_zone_details() if the zone dictionary isn't initialized -- it doesn't make sense for each caller to just know you're supposed to do that. And then raise an exception if the zone to be bypassed doesn't exist in the zone dictionary.

austinmroczek commented 1 year ago

We can see the status of each zone individually, but at the moment there is no easy way to see what zones are faulted or bypassed in the HA UI (at least on the alarm panel widget).

I'll need to think about potential UI implementation

essvee0 commented 1 year ago

Thanks, @dulitz ! Unfortunately I tried this and although it eliminates the KeyError, oddly enough, it doesn't bypass the zone anymore. (note that before I added the get_zone_details call, it did actually bypass the zone, it just got the KeyError and didn't proceed to arm the system)

        self._location.get_zone_details()
        self._location.zone_bypass(1)
dulitz commented 1 year ago

@essvee0 I have questions.

  1. When you run the code above (with get_zone_details() and zone_bypass(1)) no errors are returned but the zone is not bypassed and arming subsequently fails because a zone is faulted?

  2. Previously when you tried self._location.zone_bypass('1') without getting zone details, it raised KeyError but the zone was bypassed and you could subsequently arm the system?

  3. Previously when you tried self._location.zone_bypass(1) without getting zone details, it raised KeyError and then what?

zone_bypass() makes a direct API call that doesn't depend on prior state so it's highly unlikely that get_zone_details() would cause that direct API call to behave differently.

Based on your report, I'm suspicious that the API call might require the zone ID to be a string (failing silently if it's an integer). So far that's my best theory here...

essvee0 commented 1 year ago

@dulitz , Ok so this took a lot of trial and error and I think the initial problem I was having is that I was using the "reload" option on my custom Total Connect component, which doesn't actually appear to pull in the new changes. When I started noticing the same behavior no matter what I changed in the code, I started to reboot the whole device after every change. Finally I have found that this is really the working combination:

        self._location.get_zone_details()
        self._location.zone_bypass(1)

Thanks again for your help. I'll stop hijacking this thread and leave it back to the discussion of how this can be done in HA without hacked code!!!

dulitz commented 1 year ago

Awesome, thank you for the feedback. It's clear now how to make the API clearer, and great to see that it does take a zone number, not a zone string.

thor0215 commented 1 year ago

I would love this feature but somehow integrate it into Home Assistant. I have a scenario where I'd like to disable our kitchen glass break when my son is home. Cause when he's having late night snacks, sometimes closing the microwave is loud enough to set the glass break off.

austinmroczek commented 1 year ago

So this issue was getting long with lots of concepts intertwined. I split it into multiple issues (referenced above) so we can tackle one thing at a time.

I'm going to close this issue. The original request here will move forward in #201.