Package: logi-circle
Python Logi Circle APIPython 3.6+ API for interacting with Logi Circle cameras, written with asyncio and aiohttp.This library exposes theLogi Circlefamily of cameras as Python objects, wrapping Logi Circle's official API.Now available as a Home Assistant integration!:tada:There are two versions of this API:2.x- which targets the public API(you are here)1.x- which targets the private API (private-apibranch)To access the public API, you must request access from Logitech. Unfortunately, it's become increasingly difficult to get API keys issued by Logitech. Initially, API key requests were turned around in a few business days. Now, the wait time is several months.If you still want to request access, please refer to the instructionsherefor more information. Otherwise, please refer to theprivate-apibranch.Features implementedDownload real-time live stream data to disk or serve to your application as a raw bytes objectDownload any activity video to disk or serve to your application as a raw bytes objectDownload still images from camera to disk or serve to your application as a raw bytes objectQuery/filter the activity history by start time and/or activity properties (duration, relevance)Set name, timezone, streaming mode and privacy mode of a given cameraOn-demand polling from server to update camera propertiesSubscribe to WebSocket API to handle camera property updates and activities pushed from APIRead camera properties (see "play with props" example)Usage exampleSetup and authenticate:importasynciofromlogi_circleimportLogiCirclelogi=LogiCircle(client_id='your-client-id',client_secret='your-client-secret',redirect_uri='https://your-redirect-uri',api_key='your-api-key')ifnotlogi.authorized:print('Navigate to%sand enter the authorization code passed back to your redirect URI'%(logi.authorize_url))code=input('Code: ')asyncdefauthorize():awaitlogi.authorize(code)awaitlogi.close()asyncio.get_event_loop().run_until_complete(authorize())Grab latest still image:asyncdefget_snapshot_images():forcamerainawaitlogi.cameras:ifcamera.streaming:awaitcamera.live_stream.download_jpeg(filename='%s.jpg'%(camera.name),quality=75,# JPEG compression %refresh=False)# Don't force cameras to wakeawaitlogi.close()asyncio.get_event_loop().run_until_complete(get_snapshot_images())Download 30s of live stream video from 1st camera (requires ffmpeg):asyncdefget_livestream():camera=(awaitlogi.cameras)[0]filename='%s-livestream.mp4'%(camera.name)awaitcamera.live_stream.download_rtsp(filename=filename,duration=30)awaitlogi.close()asyncio.get_event_loop().run_until_complete(get_livestream())Download latest activity for all cameras:asyncdefget_latest_activity():forcamerainawaitlogi.cameras:last_activity=awaitcamera.last_activityiflast_activity:# Get activity as imageawaitlast_activity.download_jpeg(filename='%s-last-activity.jpg'%(camera.name))# Get activity as videoawaitlast_activity.download_mp4(filename='%s-last-activity.mp4'%(camera.name))awaitlogi.close()asyncio.get_event_loop().run_until_complete(get_latest_activity())Turn off streaming for all cameras:asyncdefdisable_streaming_all():forcamerainawaitlogi.cameras:ifcamera.streaming:awaitcamera.set_config(prop='streaming',value=False)print('%sis now off.'%(camera.name))else:print('%sis already off.'%(camera.name))awaitlogi.close()asyncio.get_event_loop().run_until_complete(disable_streaming_all())Subscribe to camera events with WS API:asyncdefsubscribe_to_events():subscription=awaitlogi.subscribe(['accessory_settings_changed',"activity_created","activity_updated","activity_finished"])whileTrue:awaitsubscription.get_next_event()asyncio.get_event_loop().run_until_complete(subscribe_to_events())Play with props:asyncdefplay_with_props():forcamerainawaitlogi.cameras:last_activity=awaitcamera.get_last_activity()print('%s:%s'%(camera.name,('is charging'ifcamera.chargingelse'is not charging')))ifcamera.battery_level>=0:print('%s:%s%%battery remaining'%(camera.name,camera.battery_level))print('%s: Battery saving mode is%s'%(camera.name,'on'ifcamera.battery_savingelse'off'))print('%s: Model number is%s'%(camera.name,camera.model))print('%s: Mount is%s'%(camera.name,camera.mount))print('%s: Signal strength is%s%%(%s)'%(camera.name,camera.signal_strength_percentage,camera.signal_strength_category))iflast_activity:print('%s: last activity was at%sand lasted for%sseconds.'%(camera.name,last_activity.start_time.isoformat(),last_activity.duration.total_seconds()))print('%s: Firmware version%s'%(camera.name,camera.firmware))print('%s: MAC address is%s'%(camera.name,camera.mac_address))print('%s: Microphone is%sand gain is set to%s(out of 100)'%(camera.name,'on'ifcamera.microphoneelse'off',camera.microphone_gain))print('%s: Speaker is%sand volume is set to%s(out of 100)'%(camera.name,'on'ifcamera.speakerelse'off',camera.speaker_volume))print('%s: LED is%s'%(camera.name,'on'ifcamera.ledelse'off'))print('%s: Recording mode is%s'%(camera.name,'on'ifcamera.recordingelse'off'))awaitlogi.close()asyncio.get_event_loop().run_until_complete(play_with_props())ThanksThis first version of API borrowed a lot of the design and some utility functions fromtchellomello'sPython Ring Doorbellproject. It made a great template for how to implement a project like this, so thanks!Thankssergeymaysakfor suggesting a switch to aiohttp, it made integrating with Home Assistant much easier.Logitech for reaching out and providing support to reimplement this library using their official API.ContributingPull requests are very welcome, every little bit helps!Raise an issue with your feature request or bug before starting work.Fork it (https://github.com/evanjd/python-logi-circle/fork).Create your feature branch (git checkout -b feature/fooBar).Commit your changes (git commit -am 'Add some fooBar').Add/update tests if needed, then runtoxto confirm no test failures.Push to the branch (git push origin feature/fooBar).Create a new pull request!MetaEvan Bruhn –@evanjd–evan.bruhn@gmail.comDistributed under the MIT license. SeeLICENSEfor more information.