Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update lock detail with activity #38

Merged
merged 2 commits into from
Feb 20, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions MANIFEST
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ august/exceptions.py
august/keypad.py
august/lock.py
august/pin.py
august/util.py
45 changes: 14 additions & 31 deletions august/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,12 @@
)
from august.doorbell import Doorbell, DoorbellDetail
from august.exceptions import AugustApiHTTPError
from august.lock import Lock, LockDetail, LockDoorStatus, LockStatus
from august.lock import (
Lock,
LockDetail,
determine_lock_status,
determine_door_state,
)
from august.pin import Pin

HEADER_ACCEPT_VERSION = "Accept-Version"
Expand Down Expand Up @@ -75,28 +80,6 @@ def _api_headers(access_token=None):
return headers


LOCKED_STATUS = ("locked", "kAugLockState_Locked")
UNLOCKED_STATUS = ("unlocked", "kAugLockState_Unlocked")
CLOSED_STATUS = ("closed", "kAugLockDoorState_Closed")
OPEN_STATUS = ("open", "kAugLockDoorState_Open")


def _determine_lock_status(status):
if status in LOCKED_STATUS:
return LockStatus.LOCKED
if status in UNLOCKED_STATUS:
return LockStatus.UNLOCKED
return LockStatus.UNKNOWN


def _determine_lock_door_status(status):
if status in CLOSED_STATUS:
return LockDoorStatus.CLOSED
if status in OPEN_STATUS:
return LockDoorStatus.OPEN
return LockDoorStatus.UNKNOWN


class Api:
def __init__(self, timeout=10, command_timeout=60, http_session: Session = None):
self._timeout = timeout
Expand Down Expand Up @@ -228,11 +211,11 @@ def get_lock_status(self, access_token, lock_id, door_status=False):

if door_status:
return (
_determine_lock_status(json_dict.get("status")),
_determine_lock_door_status(json_dict.get("doorState")),
determine_lock_status(json_dict.get("status")),
determine_door_state(json_dict.get("doorState")),
)

return _determine_lock_status(json_dict.get("status"))
return determine_lock_status(json_dict.get("status"))

def get_lock_door_status(self, access_token, lock_id, lock_status=False):
json_dict = self._call_api(
Expand All @@ -243,11 +226,11 @@ def get_lock_door_status(self, access_token, lock_id, lock_status=False):

if lock_status:
return (
_determine_lock_door_status(json_dict.get("doorState")),
_determine_lock_status(json_dict.get("status")),
determine_door_state(json_dict.get("doorState")),
determine_lock_status(json_dict.get("status")),
)

return _determine_lock_door_status(json_dict.get("doorState"))
return determine_door_state(json_dict.get("doorState"))

def get_pins(self, access_token, lock_id):
json_dict = self._call_api(
Expand All @@ -264,7 +247,7 @@ def lock(self, access_token, lock_id):
timeout=self._command_timeout,
).json()

return _determine_lock_status(json_dict.get("status"))
return determine_lock_status(json_dict.get("status"))

def unlock(self, access_token, lock_id):
json_dict = self._call_api(
Expand All @@ -274,7 +257,7 @@ def unlock(self, access_token, lock_id):
timeout=self._command_timeout,
).json()

return _determine_lock_status(json_dict.get("status"))
return determine_lock_status(json_dict.get("status"))

def refresh_access_token(self, access_token):
response = self._call_api("get", API_GET_HOUSES_URL, access_token=access_token)
Expand Down
89 changes: 85 additions & 4 deletions august/lock.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
from enum import Enum

import datetime
import dateutil.parser

from august.bridge import BridgeDetail
from august.device import Device, DeviceDetail
from august.keypad import KeypadDetail

LOCKED_STATUS = ("locked", "kAugLockState_Locked")
UNLOCKED_STATUS = ("unlocked", "kAugLockState_Unlocked")
CLOSED_STATUS = ("closed", "kAugLockDoorState_Closed")
OPEN_STATUS = ("open", "kAugLockDoorState_Open")


class Lock(Device):
def __init__(self, device_id, data):
Expand Down Expand Up @@ -38,11 +46,24 @@ def __init__(self, data):
self._bridge = None

self._doorsense = False
self._lock_status = LockStatus.UNKNOWN
self._door_state = LockDoorStatus.UNKNOWN
self._lock_status_datetime = None
self._door_state_datetime = None

if "LockStatus" in data:
if (
"doorState" in data["LockStatus"]
and data["LockStatus"]["doorState"] != "init"
):
lock_status = data["LockStatus"]

self._lock_status = determine_lock_status(lock_status.get("status"))
self._door_state = determine_door_state(lock_status.get("doorState"))

if "dateTime" in lock_status:
self._lock_status_datetime = dateutil.parser.parse(
lock_status["dateTime"]
)
self._door_state_datetime = self._lock_status_datetime

if "doorState" in lock_status and lock_status["doorState"] != "init":
self._doorsense = True

if "keypad" in data:
Expand All @@ -68,6 +89,50 @@ def bridge(self):
def doorsense(self):
return self._doorsense

@property
def lock_status(self):
return self._lock_status

@lock_status.setter
def lock_status(self, var):
"""Update the lock status (usually form the activity log)."""
if var not in LockStatus:
raise ValueError
self._lock_status = var

@property
def lock_status_datetime(self):
return self._lock_status_datetime

@lock_status_datetime.setter
def lock_status_datetime(self, var):
"""Update the lock status datetime (usually form the activity log)."""
if not isinstance(var, datetime.date):
raise ValueError
self._lock_status_datetime = var

@property
def door_state(self):
return self._door_state

@door_state.setter
def door_state(self, var):
"""Update the door state (usually form the activity log)."""
if var not in LockDoorStatus:
raise ValueError
self._door_state = var

@property
def door_state_datetime(self):
return self._door_state_datetime

@door_state_datetime.setter
def door_state_datetime(self, var):
"""Update the door state datetime (usually form the activity log)."""
if not isinstance(var, datetime.date):
raise ValueError
self._door_state_datetime = var


class LockStatus(Enum):
LOCKED = "locked"
Expand All @@ -79,3 +144,19 @@ class LockDoorStatus(Enum):
CLOSED = "closed"
OPEN = "open"
UNKNOWN = "unknown"


def determine_lock_status(status):
if status in LOCKED_STATUS:
return LockStatus.LOCKED
if status in UNLOCKED_STATUS:
return LockStatus.UNLOCKED
return LockStatus.UNKNOWN


def determine_door_state(status):
if status in CLOSED_STATUS:
return LockDoorStatus.CLOSED
if status in OPEN_STATUS:
return LockDoorStatus.OPEN
return LockDoorStatus.UNKNOWN
35 changes: 35 additions & 0 deletions august/util.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import datetime

from august.activity import (
ACTIVITY_ACTION_STATES,
DoorOperationActivity,
LockOperationActivity,
)


def update_lock_detail_from_activity(lock_detail, activity):
"""Update the LockDetail from an activity."""
activity_end_time_utc = as_utc_from_local(activity.activity_end_time)
if activity.house_id != lock_detail.house_id:
raise ValueError
if activity.device_id != lock_detail.device_id:
raise ValueError
if isinstance(activity, LockOperationActivity):
if lock_detail.lock_status_datetime >= activity_end_time_utc:
return False
lock_detail.lock_status = ACTIVITY_ACTION_STATES[activity.action]
lock_detail.lock_status_datetime = activity_end_time_utc
elif isinstance(activity, DoorOperationActivity):
if lock_detail.door_state_datetime >= activity_end_time_utc:
return False
lock_detail.door_state = ACTIVITY_ACTION_STATES[activity.action]
lock_detail.door_state_datetime = activity_end_time_utc
else:
raise ValueError

return True


def as_utc_from_local(dtime):
"""Converts the datetime returned from an activity to UTC."""
return dtime.astimezone(tz=datetime.timezone.utc)
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

setup(
name='py-august',
version='0.14.0',
version='0.16.0',
packages=['august'],
url='https://github.com/snjoetw/py-august',
license='MIT',
Expand Down
35 changes: 35 additions & 0 deletions tests/fixtures/door_closed_activity.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{
"action" : "doorclosed",
"callingUser" : {
"FirstName" : "Unknown",
"LastName" : "User",
"PhoneNo" : "deleted",
"UserID" : "deleted",
"UserName" : "deleteduser"
},
"dateTime" : 1582007217000,
"deviceID" : "ABC",
"deviceName" : "MockHouse Tech Room Door",
"deviceType" : "lock",
"entities" : {
"activity" : "activityId",
"callingUser" : "deleted",
"device" : "ABC",
"house" : "123",
"otherUser" : "deleted"
},
"house" : {
"houseID" : "123",
"houseName" : "MockHouse"
},
"info" : {
"DateLogActionID" : "ABC+Time"
},
"otherUser" : {
"FirstName" : "Unknown",
"LastName" : "User",
"PhoneNo" : "deleted",
"UserID" : "deleted",
"UserName" : "deleteduser"
}
}
35 changes: 35 additions & 0 deletions tests/fixtures/door_closed_activity_wrong_deviceid.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{
"action" : "doorclosed",
"callingUser" : {
"FirstName" : "Unknown",
"LastName" : "User",
"PhoneNo" : "deleted",
"UserID" : "deleted",
"UserName" : "deleteduser"
},
"dateTime" : 1582007217000,
"deviceID" : "notABC",
"deviceName" : "MockHouse Tech Room Door",
"deviceType" : "lock",
"entities" : {
"activity" : "activityId",
"callingUser" : "deleted",
"device" : "ABC",
"house" : "123",
"otherUser" : "deleted"
},
"house" : {
"houseID" : "123",
"houseName" : "MockHouse"
},
"info" : {
"DateLogActionID" : "ABC+Time"
},
"otherUser" : {
"FirstName" : "Unknown",
"LastName" : "User",
"PhoneNo" : "deleted",
"UserID" : "deleted",
"UserName" : "deleteduser"
}
}
35 changes: 35 additions & 0 deletions tests/fixtures/door_closed_activity_wrong_houseid.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{
"action" : "doorclosed",
"callingUser" : {
"FirstName" : "Unknown",
"LastName" : "User",
"PhoneNo" : "deleted",
"UserID" : "deleted",
"UserName" : "deleteduser"
},
"dateTime" : 1582007217000,
"deviceID" : "ABC",
"deviceName" : "MockHouse Tech Room Door",
"deviceType" : "lock",
"entities" : {
"activity" : "activityId",
"callingUser" : "deleted",
"device" : "ABC",
"house" : "not123",
"otherUser" : "deleted"
},
"house" : {
"houseID" : "not123",
"houseName" : "MockHouse"
},
"info" : {
"DateLogActionID" : "ABC+Time"
},
"otherUser" : {
"FirstName" : "Unknown",
"LastName" : "User",
"PhoneNo" : "deleted",
"UserID" : "deleted",
"UserName" : "deleteduser"
}
}
Loading