Merge branch 'main' of github.com:koush/scrypted

This commit is contained in:
Koushik Dutta
2022-05-22 00:31:54 -07:00
6 changed files with 23 additions and 37 deletions

View File

@@ -1,19 +1,19 @@
{
"name": "@scrypted/arlo",
"version": "0.2.9",
"version": "0.2.13",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@scrypted/arlo",
"version": "0.2.9",
"version": "0.2.13",
"devDependencies": {
"@scrypted/sdk": "file:../../sdk"
}
},
"../../sdk": {
"name": "@scrypted/sdk",
"version": "0.0.192",
"version": "0.0.196",
"dev": true,
"license": "ISC",
"dependencies": {

View File

@@ -1,6 +1,6 @@
{
"name": "@scrypted/arlo",
"version": "0.2.9",
"version": "0.2.13",
"description": "Arlo Plugin for Scrypted",
"keywords": [
"scrypted",

View File

@@ -94,6 +94,7 @@ class Arlo(object):
def UseExistingAuth(self, user_id, headers):
self.user_id = user_id
self.request.session.headers.update(headers)
self.BASE_URL = 'myapi.arlo.com'
def LoginMFA(self):
self.request = Request()
@@ -1580,7 +1581,7 @@ class Arlo(object):
return await self.TriggerAndHandleEvent(basestation, resource, ["is"], trigger, callback)
def StopStream(self, basestation, camera):
return self.request.post(f'https://{self.BASE_URL}/hmsweb/users/devices/stopStream', {"to":camera.get('parentId'),"from":self.user_id+"_web","resource":"cameras/"+camera. get('deviceId'),"action":"set","responseUrl":"", "publishResponse":True,"transId":self.genTransId(),"properties":{"activityState":"stopUserStream","cameraId":camera.get('deviceId')}}, headers={"xcloudId": camera.get('xCloudId')})
return self.request.post(f'https://{self.BASE_URL}/hmsweb/users/devices/stopStream', {"to":camera.get('parentId'),"from":self.user_id+"_web","resource":"cameras/"+camera.get('deviceId'),"action":"set","responseUrl":"", "publishResponse":True,"transId":self.genTransId(),"properties":{"activityState":"stopUserStream","cameraId":camera.get('deviceId')}}, headers={"xcloudId": camera.get('xCloudId')})
# nonlocal variable hack for Python 2.x.
class nl:
@@ -1630,15 +1631,19 @@ class Arlo(object):
resource = f"cameras/{camera.get('deviceId')}"
def trigger(self):
self.request.post("https://my.arlo.com/hmsweb/users/devices/fullFrameSnapshot", {"to":camera.get("parentId"),"from":self.user_id+"_web","resource":"cameras/"+camera.get("deviceId"),"action":"set","publishResponse":True,"transId":self.genTransId(),"properties":{"activityState":"fullFrameSnapshot"}}, headers={"xcloudId":camera.get("xCloudId")})
self.request.post(f"https://{self.BASE_URL}/hmsweb/users/devices/fullFrameSnapshot", {"to":camera.get("parentId"),"from":self.user_id+"_web","resource":"cameras/"+camera.get("deviceId"),"action":"set","publishResponse":True,"transId":self.genTransId(),"properties":{"activityState":"fullFrameSnapshot"}}, headers={"xcloudId":camera.get("xCloudId")})
def callback(self, event):
url = event.get("properties", {}).get("presignedFullFrameSnapshotUrl")
properties = event.get("properties", {})
url = properties.get("presignedFullFrameSnapshotUrl")
if url:
return url
url = properties.get("presignedLastImageUrl")
if url:
return url
return None
return await self.TriggerAndHandleEvent(basestation, resource, ["fullFrameSnapshotAvailable", "is"], trigger, callback)
return await self.TriggerAndHandleEvent(basestation, resource, ["fullFrameSnapshotAvailable", "lastImageSnapshotAvailable", "is"], trigger, callback)
# def StartRecording(self, basestation, camera):
# """

View File

@@ -32,7 +32,7 @@ from .logging import logger
class EventStream:
"""This class provides a queue-based EventStream object."""
def __init__(self, arlo, expire=15):
def __init__(self, arlo, expire=30):
self.event_stream = None
self.initializing = True
self.connected = False

View File

@@ -2,11 +2,12 @@ import asyncio
import scrypted_sdk
from scrypted_sdk import ScryptedDeviceBase
from scrypted_sdk.types import Camera, VideoCamera, MotionSensor, Battery, Refresh, ScryptedMimeTypes
from scrypted_sdk.types import Camera, VideoCamera, MotionSensor, Battery, ScryptedMimeTypes
from .logging import ScryptedDeviceLoggerMixin
class ArloCamera(ScryptedDeviceBase, Camera, VideoCamera, MotionSensor, Battery, Refresh, ScryptedDeviceLoggerMixin):
class ArloCamera(ScryptedDeviceBase, Camera, VideoCamera, MotionSensor, Battery, ScryptedDeviceLoggerMixin):
timeout = 30
nativeId = None
arlo_device = None
arlo_basestation = None
@@ -22,7 +23,7 @@ class ArloCamera(ScryptedDeviceBase, Camera, VideoCamera, MotionSensor, Battery,
self.provider = provider
self.logger.setLevel(self.provider.get_current_log_level())
self.update_device_details(arlo_device)
self._update_device_details(arlo_device)
self.stop_motion_subscription = False
self.start_motion_subscription()
@@ -49,7 +50,7 @@ class ArloCamera(ScryptedDeviceBase, Camera, VideoCamera, MotionSensor, Battery,
self.logger.info("Getting snapshot from prebuffer")
return await real_device.getVideoStream({"refresh": False})
pic_url = await asyncio.wait_for(self.provider.arlo.TriggerFullFrameSnapshot(self.arlo_basestation, self.arlo_device), timeout=30)
pic_url = await asyncio.wait_for(self.provider.arlo.TriggerFullFrameSnapshot(self.arlo_basestation, self.arlo_device), timeout=self.timeout)
self.logger.debug(f"Got snapshot URL for at {pic_url}")
if pic_url is None:
@@ -78,29 +79,10 @@ class ArloCamera(ScryptedDeviceBase, Camera, VideoCamera, MotionSensor, Battery,
async def getVideoStream(self, options=None):
self.logger.info("Requesting stream")
rtsp_url = await asyncio.wait_for(self.provider.arlo.StartStream(self.arlo_basestation, self.arlo_device), timeout=30)
rtsp_url = await asyncio.wait_for(self.provider.arlo.StartStream(self.arlo_basestation, self.arlo_device), timeout=self.timeout)
self.logger.debug(f"Got stream URL at {rtsp_url}")
return await scrypted_sdk.mediaManager.createMediaObject(str.encode(rtsp_url), ScryptedMimeTypes.Url.value)
async def getRefreshFrequency(self):
return 60
async def refresh(self, refreshInterface, userInitiated):
self.logger.info(f"{refreshInterface} requested refresh" + userInitiated * " (user initiated)")
devices = self.provider.arlo.GetDevices('camera')
arlo_device = None
for device in devices:
if device["deviceId"] == self.nativeId:
arlo_device = device
break
if arlo_device is None:
raise Exception(f"Device {self.nativeId} not found in GetDevices call to Arlo")
self.update_device_details(arlo_device)
self.arlo_device = arlo_device
def update_device_details(self, arlo_device):
def _update_device_details(self, arlo_device):
self.batteryLevel = arlo_device["properties"].get("batteryLevel")

View File

@@ -93,13 +93,13 @@ class ArloProvider(ScryptedDeviceBase, Settings, DeviceProvider, DeviceDiscovery
headers = self.arlo_auth_headers
if headers:
self._arlo.UseExistingAuth(self.arlo_user_id, json.loads(headers))
self.logger.info(f"Initialized Arlo client for {self.arlo_username}, reusing stored auth headers")
self.logger.info(f"Initialized Arlo client, reusing stored auth headers")
asyncio.get_event_loop().create_task(self.do_arlo_setup())
return self._arlo
else:
self._arlo_mfa_complete_auth = self._arlo.LoginMFA()
self.logger.info(f"Initialized Arlo client for {self.arlo_username}, waiting for MFA code")
self.logger.info(f"Initialized Arlo client, waiting for MFA code")
return None
except Exception as e:
self.logger.error(f"Error initializing Arlo client: {type(e)} with message {str(e)}")
@@ -219,7 +219,6 @@ class ArloProvider(ScryptedDeviceBase, Settings, DeviceProvider, DeviceDiscovery
ScryptedInterface.VideoCamera.value,
ScryptedInterface.Camera.value,
ScryptedInterface.MotionSensor.value,
ScryptedInterface.Refresh.value,
],
"type": ScryptedDeviceType.Camera.value,
"providerNativeId": self.nativeId,