A Python library to communicate with Logi Circle cameras
Project description
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.
Installation
Installing release version
$ pip install logi-circle
Installing development master
$ pip install \
git+https://github.com/evanjd/python-logi-circle
Features available
- 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:
- ID
- Node ID
- Name
- Live image (as JPEG)
- Last activity
- Timezone
- 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
- Model type (eg. 1st gen, 2nd gen wired, etc)
- 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)
- Duration
- Relevance level (indicating whether people/objects were detected)
Features planned
- Motion alerts (eventually)
- Logi Circle CLI (eventually)
- Speaker support (maybe)
Usage example
Setup and authenticate:
import asyncio
from logi_circle import Logi
logi_api = Logi('my@email.com', '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)[0]
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)[0]
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 != 'off':
await camera.set_streaming_mode('off')
print('%s is now %s.' %
(camera.name, camera.streaming_mode))
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: Model number is %s' % (camera.name, camera.model))
print('%s: Model type is %s' % (camera.name, camera.model_type))
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()
Release History
- 0.0.1
- Initial commit
- 0.0.2
- Added support for querying activity history
- 0.0.3
- Added support for retrieving the latest still image for a given camera
- 0.0.4
- Replaced requests with aiohttp
- Added support for turning camera on & off
- Added update() method to Camera object to refresh data from server
- 0.1.0
- Added preliminary support for live streams (to be improved)
- 0.1.1
- 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
- 0.1.2
- Removed
is_streaming
property as I've discovered this is not a binary sensor for 2nd gen cameras. Replaced withstreaming_mode
. set_streaming_mode
now accepts a string instead a boolean.- Added
model_name
property.
- Removed
- 0.1.3
- Renamed
model_name
tomodel_type
to better reflect what the property reports. - Added rudimentary feature detection, exposed via
supported_features
andsupports_feature
methods and derived from model type.
- Renamed
- 0.1.4
- Fixed missing
last_activity_time
sensor on 2nd gen wired cameras.
- Fixed missing
- 0.1.5
- Added
get_livestream_image
andrecord_livestream
methods to camera object, allowing snapshots (images) and videos of a specified length to be recorded from the camera's livestream (both requiring ffmpeg)
- Added
Meta
Evan Bruhn – @evanjd – evan.bruhn@gmail.com
Distributed under the MIT license. See LICENSE
for more information.
Thanks
- 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.
Contributing
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
tox
to confirm no test failures. - Push to the branch (
git push origin feature/fooBar
). - Create a new pull request!
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Hashes for logi_circle-0.1.5-py3-none-any.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | b5137527843cde1795172ca067aae0870e09e706148b36073165ab4c542e2963 |
|
MD5 | df91a7a9fc1ba94edac9681757535a99 |
|
BLAKE2b-256 | 45b7ef36611bf82328a5bb34869b7f93e13820a17d49c4a4a9ae3d74a1cc1e29 |