A Python library to communicate with Logi Circle cameras
Python Logi Circle API
Python 3.6+ API for interacting with Logi Circle cameras, written with asyncio and aiohttp.
This library exposes the Logi Circle family of cameras as Python objects. The goal is to expose most of the functionality from Logi's 1st party applications, allowing integration of those features into other projects.
Note that the API this project is based on is not open, and therefore could change/break at any time.
Installing release version
$ pip install logi-circle
Installing development master
$ pip install \ git+https://github.com/evanjd/python-logi-circle
- Download real-time live stream data to disk or serve to your application as a raw bytes object
- Query/filter the activity history by start time and/or activity properties (duration, relevance)
- Download any activity video to disk or serve to your application as a raw bytes object
- Download still images from camera to disk or serve to your application as a raw bytes object
- Set streaming mode, privacy mode, LED status, speaker volume, microphone gain and other properties of camera
- On-demand polling from server to update camera properties
- Camera properties exposed:
- Node ID
- Live image (as JPEG)
- Last activity
- Connected status (is it powered and in range)
- Streaming status (is currently streaming and capable of recording activities)
- Privacy mode (is it recording activities)
- Firmware version
- Battery %
- Charging status
- Model generation (eg. 1st gen, 2nd gen)
- Mount (eg. Wired, Wireless)
- Connected Wifi SSID
- Signal strength %
- IP address
- MAC address
- Microphone status and gain
- Speaker status and volume
- LED enabled
- Plan name
- Temperature (if supported by your device)
- Relative humidity % (if supported by your device)
- Activity properties exposed:
- Start time (local or UTC)
- End time (local or UTC)
- Relevance level (indicating whether people/objects were detected)
- Motion alerts (eventually)
- Logi Circle CLI (eventually)
- Speaker support (maybe)
Setup and authenticate:
import asyncio from logi_circle import Logi logi_api = Logi('firstname.lastname@example.org', 'my-password')
Grab latest still image for each camera:
async def get_snapshot_images(): for camera in await logi_api.cameras: await camera.get_snapshot_image('%s.jpg' % (camera.name)) await logi_api.logout() loop = asyncio.get_event_loop() loop.run_until_complete(get_snapshot_images()) loop.close()
Download latest activity for all cameras:
async def get_latest_activity(): for camera in await logi_api.cameras: last_activity = await camera.last_activity await last_activity.download('%s-last-activity.mp4' % (camera.name)) await logi_api.logout() loop = asyncio.get_event_loop() loop.run_until_complete(get_latest_activity()) loop.close()
Stream live stream data to disk:
async def get_livestream(): camera = (await logi_api.cameras) filename = '%s-livestream.mp4' % (camera.name) # Grab 1 minute of footage from live stream await camera.record_livestream(filename=filename, duration=timedelta(minutes=1)) await logi_api.logout() loop = asyncio.get_event_loop() loop.run_until_complete(get_livestream()) loop.close()
Download last 24 hours activity for the 1st camera (limited to 100, 5 at a time):
from datetime import datetime, timedelta # Don't go nuts with parallelising downloads, you'll probably hit rate limits. semaphore = asyncio.Semaphore(5) async def download(camera, activity): async with semaphore: file_name = '%s - %s.mp4' % (camera.name, activity.start_time.isoformat()) await activity.download(file_name) async def run(): my_camera = (await logi_api.cameras) activities = await my_camera.query_activity_history(date_filter=datetime.now() - timedelta(hours=24), date_operator='>', limit=100) tasks =  for activity in activities: task = asyncio.ensure_future(download(my_camera, activity)) tasks.append(task) await asyncio.gather(*tasks) logi_api.logout() loop = asyncio.get_event_loop() future = asyncio.ensure_future(run()) loop.run_until_complete(future)
Turn off streaming for all cameras
async def disable_streaming_all(): for camera in await logi_api.cameras: if camera.streaming_mode is True: await camera.set_streaming_mode(False) print('%s is now off.' % (camera.name)) else: print('%s is already off.' % (camera.name)) await logi_api.logout() loop = asyncio.get_event_loop() loop.run_until_complete(disable_streaming_all()) loop.close()
Play with camera properties
async def play_with_props(): for camera in await logi_api.cameras: last_activity = await camera.last_activity print('%s: %s' % (camera.name, ('is charging' if camera.is_charging else 'is not charging'))) print('%s: %s%% battery remaining' % (camera.name, camera.battery_level)) print('%s: Battery saving mode is %s' % (camera.name, 'on' if camera.battery_saving else 'off')) print('%s: Model number is %s' % (camera.name, camera.model)) print('%s: Model generation is %s' % (camera.name, camera.model_generation)) 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)) print('%s: last activity was at %s and lasted for %s seconds.' % ( camera.name, last_activity.start_time.isoformat(), last_activity.duration.total_seconds())) print('%s: Firmware version %s' % (camera.name, camera.firmware)) print('%s: IP address is %s' % (camera.name, camera.ip_address)) print('%s: MAC address is %s' % (camera.name, camera.mac_address)) print('%s: Microphone is %s and gain is set to %s (out of 100)' % ( camera.name, 'on' if camera.microphone_on else 'off', camera.microphone_gain)) print('%s: Speaker is %s and volume is set to %s (out of 100)' % ( camera.name, 'on' if camera.speaker_on else 'off', camera.speaker_volume)) print('%s: LED is %s' % ( camera.name, 'on' if camera.led_on else 'off')) print('%s: Privacy mode is %s' % ( camera.name, 'on' if camera.privacy_mode else 'off')) print('%s: Subscribed to plan %s' % ( camera.name, camera.plan_name)) await logi_api.logout() loop = asyncio.get_event_loop() loop.run_until_complete(play_with_props()) loop.close()
- Initial commit
- Added support for querying activity history
- Added support for retrieving the latest still image for a given camera
- Replaced requests with aiohttp
- Added support for turning camera on & off
- Added update() method to Camera object to refresh data from server
- Added preliminary support for live streams (to be improved)
- Fixed timing bug causing live streams to download at half real-time speeds
- Live streams will now automatically append to an existing file (instead of overwriting)
- Added a bunch of new camera properties
- Added support for setting privacy mode, LED status, speaker status, speaker volume, microphone status and microphone gain
is_streamingproperty as I've discovered this is not a binary sensor for 2nd gen cameras. Replaced with
set_streaming_modenow accepts a string instead a boolean.
model_typeto better reflect what the property reports.
- Added rudimentary feature detection, exposed via
supports_featuremethods and derived from model type.
- Fixed missing
last_activity_timesensor on 2nd gen wired cameras.
- Fixed missing
record_livestreammethods to camera object, allowing snapshots (images) and videos of a specified length to be recorded from the camera's livestream (both requiring ffmpeg)
set_streaming_modenow accepts a boolean instead of string.
model_typeproperty, replaced with
- Implemented rudimentary throttling on
- Implemented rudimentary throttling on
Distributed under the MIT license. See
LICENSE for more information.
- This API borrows a lot of design and some utility functions from tchellomello's Python Ring Doorbell project. Our projects are doing similar things with similar devices and I really appreciated how simple and readable python-ring-doorbell is.
- Thanks sergeymaysak for suggesting a switch to aiohttp and for a tip to make downloading snapshot images more reliable.
They're very welcome, every little bit helps! I'm especially keen for help supporting devices that I do not own and cannot test with (eg. Circle 2 wired and wireless cameras).
- 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 run
toxto confirm no test failures.
- Push to the branch (
git push origin feature/fooBar).
- Create a new pull request!
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
|Filename, size & hash SHA256 hash help||File type||Python version||Upload date|
|logi_circle-0.1.7-py3-none-any.whl (19.4 kB) Copy SHA256 hash SHA256||Wheel||py3||Sep 10, 2018|
|logi_circle-0.1.7.tar.gz (20.6 kB) Copy SHA256 hash SHA256||Source||None||Sep 10, 2018|