Skip to content

Commit

Permalink
Merge pull request #56 from twrecked/merge-to-0.7.0.17
Browse files Browse the repository at this point in the history
Merge to 0.7.0.18
  • Loading branch information
twrecked committed Feb 12, 2021
2 parents b69b26f + 09aa58c commit 706f27e
Show file tree
Hide file tree
Showing 8 changed files with 156 additions and 57 deletions.
1 change: 1 addition & 0 deletions changelog
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
0.7.0.18: Update user agent and request headers.
0.7.0.6: Stop battery drain on any battery based base station.
0.7.0.4: Handle broken modes fetch.
0.7.0.2: Make mode refresh optional.
Expand Down
22 changes: 15 additions & 7 deletions pyaarlo/backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -599,14 +599,18 @@ def _login(self):
# set agent before starting
if self._arlo.cfg.user_agent == "apple":
self._user_agent = (
"Mozilla/5.0 (iPhone; CPU iPhone OS 11_1_2 like Mac OS X) "
"AppleWebKit/604.3.5 (KHTML, like Gecko) Mobile/15B202 NETGEAR/v1 "
"(iOS Vuezone)"
"Mozilla/5.0 (iPhone; CPU iPhone OS 13_1_3 like Mac OS X) "
"AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.1 Mobile/15E148 Safari/604.1"
)
elif self._arlo.cfg.user_agent == "mac":
self._user_agent = (
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) "
"AppleWebKit/605.1.15 (KHTML, like Gecko) Version/11.1.2 Safari/605.1.15"
)
else:
self._user_agent = (
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/72.0.3626.81 Safari/537.36"
"Mozilla/5.0 (X11; Linux x86_64) "
"AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.96 Safari/537.36"
)

# set up session
Expand Down Expand Up @@ -634,10 +638,14 @@ def _login(self):
# update sessions headers
headers = {
"Accept": "application/json, text/plain, */*",
"Accept-Language": "en-GB,en;q=0.9,en-US;q=0.8",
"Auth-Version": "2",
"schemaVersion": "1",
"Cache-Control": 'no-cache',
"SchemaVersion": "1",
"Host": re.sub("https?://", "", self._arlo.cfg.host),
"Content-Type": "application/json; charset=utf-8;",
"Origin": self._arlo.cfg.host,
"Pragma": "no-cache",
"Referer": self._arlo.cfg.host,
"User-Agent": self._user_agent,
"Authorization": self._token,
Expand Down Expand Up @@ -686,7 +694,7 @@ def _wait_for_transaction(self, tid, timeout):
self._lock.wait(mend - mnow)
mnow = time.monotonic()
response = self._requests.pop(tid)
except KeyError as e:
except KeyError as _e:
self._arlo.debug("got a key error")
response = None
self._arlo.vdebug("finished transaction-->{}".format(tid))
Expand Down
5 changes: 3 additions & 2 deletions pyaarlo/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -444,8 +444,9 @@ def has_capability(self, cap):
return True
if cap in (PING_CAPABILITY,):
# Battery powered wifi devices that act as their own base station don't get pinged.
if self.model_id.startswith(MODEL_BABY):
return True
if self.is_own_parent and self.using_wifi and not self.is_corded:
return False
else:
return True
return True
return super().has_capability(cap)
18 changes: 13 additions & 5 deletions pyaarlo/camera.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
LAST_IMAGE_DATA_KEY,
LAST_IMAGE_KEY,
LAST_IMAGE_SRC_KEY,
LAST_RECORDING_KEY,
LIGHT_BRIGHTNESS_KEY,
LIGHT_MODE_KEY,
MEDIA_COUNT_KEY,
Expand Down Expand Up @@ -316,7 +317,8 @@ def _event_handler(self, resource, event):
if value is not None:
self._save_and_do_callbacks(key, value)

# The last image thumbnail has changed. Queue an update.
# The last image thumbnail has changed. Queue an image or snapshot
# update to download the image and process it.
if LAST_IMAGE_KEY in event:
if not self.is_taking_snapshot:
self._arlo.debug("{} -> thumbnail changed".format(self.name))
Expand All @@ -328,18 +330,24 @@ def _event_handler(self, resource, event):
self._save(SNAPSHOT_KEY, event.get(LAST_IMAGE_KEY, ""))
self._arlo.bg.run_low(self._update_snapshot)

# Recording has stopped so a new video is available. Queue and
# Recording has stopped so a new video is available. Queue an
# media update, this could later trigger a snapshot or image
# update.
if event.get(RECORDING_STOPPED_KEY, False):
self._arlo.debug("{} -> recording stopped".format(self.name))
self._arlo.ml.queue_update(self._update_media)

# A snapshot is ready. Queue an update.
# Examine the URL passed; snapshots contain `/snapshots/` and
# recordings contain `recordings`. For snapshot, save URL and queue
# up an event to download and process it. We do nothing with the
# recording for now, it will come in via a media update.
value = event.get(STREAM_SNAPSHOT_KEY, "")
if "/snapshots/" in value:
self._arlo.debug("{} -> snapshot ready".format(self.name))
self._arlo.debug("{} -> snapshot1 ready".format(self.name))
self._save(SNAPSHOT_KEY, value)
self._arlo.bg.run_low(self._update_snapshot)
if "/recordings/" in value:
self._arlo.debug("{} -> new recording ready".format(self.name))

# Something just happened.
self._set_recent(self._arlo.cfg.recent_time)
Expand Down Expand Up @@ -403,7 +411,7 @@ def _event_handler(self, resource, event):
"presignedFullFrameSnapshotUrl", None
)
if value is not None:
self._arlo.debug("{} -> snapshot ready".format(self.name))
self._arlo.debug("{} -> snapshot2 ready".format(self.name))
self._save(SNAPSHOT_KEY, value)
self._arlo.bg.run_low(self._update_snapshot)

Expand Down
24 changes: 14 additions & 10 deletions pyaarlo/constant.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
BUTTON_PRESSED_KEY = "buttonPressed"
CHARGER_KEY = "chargerTech"
CHARGING_KEY = "chargingState"
CHIMES_KEY = "chimes"
CONNECTION_KEY = "connectionState"
CRY_DETECTION_KEY = "babyCryDetection"
FLIP_KEY = "flip"
Expand All @@ -83,6 +84,7 @@
SIGNAL_STR_KEY = "signalStrength"
SIREN_STATE_KEY = "sirenState"
TEMPERATURE_KEY = "temperature"
TRADITIONAL_CHIME_KEY = "traditionalChime"
NIGHTLIGHT_KEY = "nightLight"
MEDIA_PLAYER_KEY = "mediaPlayer"
FLOODLIGHT_KEY = "floodlight"
Expand Down Expand Up @@ -132,26 +134,27 @@
RESOURCE_UPDATE_KEYS = [
ACTIVITY_STATE_KEY,
AIR_QUALITY_KEY,
AUDIO_CONFIG_KEY,
AUDIO_DETECTED_KEY,
AUDIO_PLAYLIST_KEY,
AUDIO_POSITION_KEY,
AUDIO_SPEAKER_KEY,
AUDIO_STATUS_KEY,
AUDIO_TRACK_KEY,
BATTERY_KEY,
BATTERY_TECH_KEY,
CHARGER_KEY,
CHARGING_KEY,
CONNECTION_KEY,
LAMP_STATE_KEY,
FLOODLIGHT_KEY,
HUMIDITY_KEY,
LAMP_STATE_KEY,
MOTION_DETECTED_KEY,
PRIVACY_KEY,
SIGNAL_STR_KEY,
SILENT_MODE_KEY,
SIREN_STATE_KEY,
TEMPERATURE_KEY,
AUDIO_CONFIG_KEY,
AUDIO_PLAYLIST_KEY,
AUDIO_STATUS_KEY,
AUDIO_SPEAKER_KEY,
AUDIO_TRACK_KEY,
AUDIO_POSITION_KEY,
FLOODLIGHT_KEY,
]

RECENT_ACTIVITY_KEYS = [AUDIO_DETECTED_KEY, MOTION_DETECTED_KEY]
Expand All @@ -166,6 +169,7 @@
UNIQUE_ID_KEY = "uniqueId"
USER_ID_KEY = "userId"
LAST_IMAGE_KEY = "presignedLastImageUrl"
LAST_RECORDING_KEY = "presignedLastRecordingUrl"
SNAPSHOT_KEY = "presignedFullFrameSnapshotUrl"
STREAM_SNAPSHOT_KEY = "presignedContentUrl"
XCLOUD_ID_KEY = "xCloudId"
Expand All @@ -175,12 +179,12 @@
DEVICE_ID_KEY,
DEVICE_NAME_KEY,
DEVICE_TYPE_KEY,
LAST_IMAGE_KEY,
MEDIA_COUNT_KEY,
PARENT_ID_KEY,
SNAPSHOT_KEY,
UNIQUE_ID_KEY,
USER_ID_KEY,
LAST_IMAGE_KEY,
SNAPSHOT_KEY,
XCLOUD_ID_KEY,
]

Expand Down
129 changes: 101 additions & 28 deletions pyaarlo/doorbell.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from .constant import (
BATTERY_KEY,
BUTTON_PRESSED_KEY,
CHIMES_KEY,
CONNECTION_KEY,
MODEL_WIRED_VIDEO_DOORBELL,
MOTION_DETECTED_KEY,
Expand All @@ -17,6 +18,8 @@ def __init__(self, name, arlo, attrs):
super().__init__(name, arlo, attrs)
self._motion_time_job = None
self._ding_time_job = None
self._has_motion_detect = False
self._chimes = {}

def _motion_stopped(self):
self._save_and_do_callbacks(MOTION_DETECTED_KEY, False)
Expand All @@ -33,26 +36,45 @@ def _event_handler(self, resource, event):

# create fake motion/button press event...
if resource == self.resource_id:
cons = event.get("properties", {}).get(CONNECTION_KEY, False)
butp = event.get("properties", {}).get(BUTTON_PRESSED_KEY, False)
props = event.get("properties", {})

# acts = event.get('properties',{}).get('activityState',False)
if cons and cons == "available":
self._save_and_do_callbacks(MOTION_DETECTED_KEY, True)
with self._lock:
self._arlo.bg.cancel(self._motion_time_job)
self._motion_time_job = self._arlo.bg.run_in(
self._motion_stopped, self._arlo.cfg.db_motion_time
)
if butp:
# Newer doorbells send a motionDetected True followed by False. If we
# see this then turn off connectionState checking.
if MOTION_DETECTED_KEY in props:
self._arlo.debug(self.name + " has motion detection support")
self._has_motion_detect = True

# Older doorbells signal a connectionState as available when motion
# is detected. We check the properties length to not confuse it
# with a device update. There is no motion stopped event so set a
# timer to turn off the motion detect.
if len(props) == 1 and not self._has_motion_detect:
if props.get(CONNECTION_KEY, "") == "available":
self._save_and_do_callbacks(MOTION_DETECTED_KEY, True)
with self._lock:
self._arlo.bg.cancel(self._motion_time_job)
self._motion_time_job = self._arlo.bg.run_in(
self._motion_stopped, self._arlo.cfg.db_motion_time
)

# For button presses we only get a buttonPressed notification, not
# a "no longer pressed" notification - set a timer to turn off the
# press.
if BUTTON_PRESSED_KEY in props:
self._save_and_do_callbacks(BUTTON_PRESSED_KEY, True)
with self._lock:
self._arlo.bg.cancel(self._ding_time_job)
self._ding_time_job = self._arlo.bg.run_in(
self._button_unpressed, self._arlo.cfg.db_ding_time
)

silent_mode = event.get("properties", {}).get(SILENT_MODE_KEY, {})
# Save out chimes
if CHIMES_KEY in props:
self._chimes = props[CHIMES_KEY]

# Pass silent mode notifications so we can track them in the "ding"
# entity.
silent_mode = props.get(SILENT_MODE_KEY, {})
if silent_mode:
self._save_and_do_callbacks(SILENT_MODE_KEY, silent_mode)

Expand All @@ -79,22 +101,6 @@ def has_capability(self, cap):
return False
return super().has_capability(cap)

def silent_mode(self, active, block_call):
self._arlo.be.notify(
base=self.base_station,
body={
"action": "set",
"properties": {
SILENT_MODE_KEY: {
SILENT_MODE_ACTIVE_KEY: active,
SILENT_MODE_CALL_KEY: block_call,
},
},
"publishResponse": True,
"resource": self.resource_id,
},
)

def update_silent_mode(self):
"""Requests the latest silent mode settings.
Expand All @@ -108,3 +114,70 @@ def update_silent_mode(self):
"publishResponse": False,
},
)

def _build_chimes(self, on_or_off):
chimes = {"traditional": on_or_off}
for chime in self._chimes:
chimes[chime] = on_or_off
return chimes

def _silence(self, active, calls, chimes):

# Build settings
silence_settings = {
SILENT_MODE_ACTIVE_KEY: active,
SILENT_MODE_CALL_KEY: calls,
}
if chimes:
silence_settings[CHIMES_KEY] = chimes

# Build request
properties = {SILENT_MODE_KEY: silence_settings}
self._arlo.debug(self.name + " silence is " + str(properties))

# Send out request.
response = self._arlo.be.notify(
base=self.base_station,
body={
"action": "set",
"properties": properties,
"publishResponse": True,
"resource": self.resource_id,
},
wait_for="response",
)

# Not none means a 200 so we assume it works until told otherwise.
if response is not None:
self._arlo.bg.run(
self._save_and_do_callbacks,
attr=SILENT_MODE_KEY,
value=silence_settings,
)

def silence_off(self):
self._silence(False, False, {})

def silence_on(self):
self._silence(True, True, self._build_chimes(True))

def silence_chimes(self):
self._silence(True, False, self._build_chimes(True))

def silence_calls(self):
self._silence(True, True, self._build_chimes(False))

@property
def is_silenced(self):
return self._load(SILENT_MODE_KEY, {}).get(SILENT_MODE_ACTIVE_KEY, False)

@property
def calls_are_silenced(self):
return self._load(SILENT_MODE_KEY, {}).get(SILENT_MODE_CALL_KEY, False)

@property
def chimes_are_silenced(self):
for on_or_off in self._load(SILENT_MODE_KEY, {}).get(CHIMES_KEY, {}).values():
if on_or_off is True:
return True
return False
Loading

0 comments on commit 706f27e

Please sign in to comment.