Skip to content

Commit

Permalink
Merge pull request #119 from pyalarmdotcom/opened-closed-fix
Browse files Browse the repository at this point in the history
Fixed OpenedClosed contact sensor status.
  • Loading branch information
elahd authored Jun 1, 2023
2 parents 38c7f7f + 4c6dd18 commit f417e0d
Show file tree
Hide file tree
Showing 6 changed files with 57 additions and 30 deletions.
2 changes: 1 addition & 1 deletion pyalarmdotcomajax/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
)
from pyalarmdotcomajax.websockets.client import WebSocketClient, WebSocketState

__version__ = "0.5.1"
__version__ = "0.5.2"

log = logging.getLogger(__name__)

Expand Down
2 changes: 1 addition & 1 deletion pyalarmdotcomajax/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -409,7 +409,7 @@ async def cli() -> None:
attrs=["bold"],
)
cprint(
"(Press Ctrl+C or Cmd+C to exit.)",
"(Press Ctrl+C to exit.)",
attrs=["bold"],
)

Expand Down
11 changes: 6 additions & 5 deletions pyalarmdotcomajax/devices/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""Alarm.com device base devices."""
from __future__ import annotations

import contextlib
import logging
from abc import ABC
from collections.abc import Callable
Expand Down Expand Up @@ -158,12 +159,12 @@ def has_state(self) -> bool:
def state(self) -> Enum | None:
"""Return state."""

try:
state = self.DeviceState(self._attribs_raw.get("state"))
except ValueError:
return None
# Devices that don't report state on Alarm.com still have a value in the state field.
if self.has_state:
with contextlib.suppress(ValueError):
return self.DeviceState(self._attribs_raw.get("state"))

return state
return None

@property
def settings(self) -> dict:
Expand Down
2 changes: 2 additions & 0 deletions pyalarmdotcomajax/websockets/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
"""WebSocket update module."""

# List of device types and the events that they support is listed at https://answers.alarm.com/Customer/Website_and_App/User_Management/Web_App_Users/Do_the_app_and_website_update_in_real_time#To_refresh_the_Customer_Website.
55 changes: 33 additions & 22 deletions pyalarmdotcomajax/websockets/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,62 +25,59 @@ class PropertyChangeType(Enum):


class EventType(Enum):
"""Enum for monitoring event types."""
"""Enum for event types."""

#
# Supported
#
ArmedAway = 10
ArmedNight = 113
ArmedStay = 9
Closed = 0
Disarmed = 8
DoorLocked = 91
DoorUnlocked = 90
ImageSensorUpload = 99
LightTurnedOff = 316
LightTurnedOn = 315
Opened = 15
OpenedClosed = 100
SupervisionFaultArming = 48
SupervisionFaultDisarming = 47
SwitchLevelChanged = 317
ThermostatFanModeChanged = 120
ThermostatModeChanged = 95
ThermostatOffset = 105
ThermostatSetPointChanged = 94

#
# Not Supported
#
# Unsupported
Alarm = 1
BypassStart = 13
BypassEnd = 35
SumpPumpAlertCriticalIssueMalfunction = 118
SumpPumpAlertCriticalIssueOff = 117
SumpPumpAlertIdle = 114
SumpPumpAlertNormalOperation = 115
SumpPumpAlertPossibleIssue = 116
VideoCameraTriggered = 71
VideoEventTriggered = 76
AccessControlDoorAccessGranted = 298
AlarmCancelled = 238
ArmingSupervisionFault = 48
AuxiliaryPanic = 17
AuxPanicPendingAlarm = 61
AuxPanicSuspectedAlarm = 65
BadLockUserCode = 93
Bypassed = 13
CommercialClosedOnTime = 127
CommercialClosedUnexpectedly = 177
CommercialEarlyClose = 125
CommercialEarlyOpen = 122
CommercialLateClose = 126
CommercialLateOpen = 123
CommercialOpenOnTime = 124
DisarmingSupervisionFault = 47
DoorAccessed = 92
DoorAccessedDoubleSwipe = 236
DoorBuzzedFromWebsite = 182
DoorFailedAccess = 180
DoorForcedOpen = 181
DoorHeldOpen = 184
EndOfBypass = 35
ExitButtonPressed = 141
FirePanic = 24
GoogleSdmEvent = 346
ImageSensorUpload = 99
InAppAuxiliaryPanic = 201
InAppFirePanic = 200
InAppPolicePanic = 202
InAppSilentPolicePanic = 203
MonitoringPanic = 2009
NetworkDhcpReservationsUpdated = 433
NetworkDhcpSettingsUpdated = 432
NetworkMapUpdated = 391
Expand All @@ -90,12 +87,29 @@ class EventType(Enum):
PendingAlarm = 62
PolicePanic = 22
PolicePanicSuspectedAlarm = 64
RouterHostsUpdated = 450
RouterProfilesUpdated = 451
SilentPolicePanic = 73
SilentPolicePanicSuspectedAlarm = 172
SpeedTestResultsUpdated = 454
SumpPumpAlertCriticalIssueMalfunction = 118
SumpPumpAlertCriticalIssueOff = 117
SumpPumpAlertIdle = 114
SumpPumpAlertNormalOperation = 115
SumpPumpAlertPossibleIssue = 116
Tamper = 7
UnknownCardFormatRead = 185
VideoAnalytics2Detection = 302
VideoAnalyticsDetection = 210
VideoCameraTriggered = 71
VideoEventTriggered = 76
ViewedByCentralStation = 158
WrongPinCode = 398

# Undocumented
UserLoggedIn = 55
DoorLeftOpenRestoral = 103 # When door is closed after being left open. Paired with a door closed event.
DoorLeftOpen = 101 # When door is left open for 30 minutes.


SUPPORTED_MONITORING_EVENT_TYPES = [
Expand All @@ -106,13 +120,10 @@ class EventType(Enum):
EventType.Disarmed,
EventType.DoorLocked,
EventType.DoorUnlocked,
EventType.ImageSensorUpload,
EventType.LightTurnedOff,
EventType.LightTurnedOn,
EventType.Opened,
EventType.OpenedClosed,
EventType.SupervisionFaultArming,
EventType.SupervisionFaultDisarming,
EventType.SwitchLevelChanged,
EventType.ThermostatFanModeChanged,
EventType.ThermostatModeChanged,
Expand Down
15 changes: 14 additions & 1 deletion pyalarmdotcomajax/websockets/handler/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from __future__ import annotations

import asyncio
import logging

from pyalarmdotcomajax.devices.sensor import Sensor
Expand Down Expand Up @@ -50,9 +51,21 @@ async def process_message(self, message: WebSocketMessage) -> None:
match message:
case EventMessage():
match message.event_type:
case EventType.Closed | EventType.Opened | EventType.OpenedClosed:
case EventType.Closed | EventType.Opened:
state = await self.async_get_state_from_event_type(message)
await message.device.async_handle_external_state_change(state)

case EventType.OpenedClosed:
# Lock ensures that state changes are executed in order.
lock = asyncio.Lock()

async with lock:
await message.device.async_handle_external_state_change(Sensor.DeviceState.OPEN.value)

async with lock:
await message.device.async_handle_external_state_change(
Sensor.DeviceState.CLOSED.value
)
case _:
log.debug(
f"Support for event {message.event_type} ({message.event_type_id}) not yet implemented"
Expand Down

0 comments on commit f417e0d

Please sign in to comment.