Add back pipfiles, README
This commit is contained in:
16
Pipfile
Normal file
16
Pipfile
Normal file
@@ -0,0 +1,16 @@
|
||||
[[source]]
|
||||
name = "pypi"
|
||||
url = "https://pypi.org/simple"
|
||||
verify_ssl = true
|
||||
|
||||
[dev-packages]
|
||||
|
||||
[packages]
|
||||
pillow = "*"
|
||||
pyyaml = "*"
|
||||
requests = "*"
|
||||
numpy = "*"
|
||||
opencv-python = "*"
|
||||
pysocks = "*"
|
||||
|
||||
[requires]
|
||||
169
Pipfile.lock
generated
Normal file
169
Pipfile.lock
generated
Normal file
@@ -0,0 +1,169 @@
|
||||
{
|
||||
"_meta": {
|
||||
"hash": {
|
||||
"sha256": "6700bce6ed08db166eff9d3105158923ffd2ffbf35c814a4d0133552bda03b5a"
|
||||
},
|
||||
"pipfile-spec": 6,
|
||||
"requires": {},
|
||||
"sources": [
|
||||
{
|
||||
"name": "pypi",
|
||||
"url": "https://pypi.org/simple",
|
||||
"verify_ssl": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"default": {
|
||||
"certifi": {
|
||||
"hashes": [
|
||||
"sha256:017c25db2a153ce562900032d5bc68e9f191e44e9a0f762f373977de9df1fbb3",
|
||||
"sha256:25b64c7da4cd7479594d035c08c2d809eb4aab3a26e5a990ea98cc450c320f1f"
|
||||
],
|
||||
"version": "==2019.11.28"
|
||||
},
|
||||
"chardet": {
|
||||
"hashes": [
|
||||
"sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae",
|
||||
"sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"
|
||||
],
|
||||
"version": "==3.0.4"
|
||||
},
|
||||
"idna": {
|
||||
"hashes": [
|
||||
"sha256:7588d1c14ae4c77d74036e8c22ff447b26d0fde8f007354fd48a7814db15b7cb",
|
||||
"sha256:a068a21ceac8a4d63dbfd964670474107f541babbd2250d61922f029858365fa"
|
||||
],
|
||||
"version": "==2.9"
|
||||
},
|
||||
"numpy": {
|
||||
"hashes": [
|
||||
"sha256:1786a08236f2c92ae0e70423c45e1e62788ed33028f94ca99c4df03f5be6b3c6",
|
||||
"sha256:17aa7a81fe7599a10f2b7d95856dc5cf84a4eefa45bc96123cbbc3ebc568994e",
|
||||
"sha256:20b26aaa5b3da029942cdcce719b363dbe58696ad182aff0e5dcb1687ec946dc",
|
||||
"sha256:2d75908ab3ced4223ccba595b48e538afa5ecc37405923d1fea6906d7c3a50bc",
|
||||
"sha256:39d2c685af15d3ce682c99ce5925cc66efc824652e10990d2462dfe9b8918c6a",
|
||||
"sha256:56bc8ded6fcd9adea90f65377438f9fea8c05fcf7c5ba766bef258d0da1554aa",
|
||||
"sha256:590355aeade1a2eaba17617c19edccb7db8d78760175256e3cf94590a1a964f3",
|
||||
"sha256:70a840a26f4e61defa7bdf811d7498a284ced303dfbc35acb7be12a39b2aa121",
|
||||
"sha256:77c3bfe65d8560487052ad55c6998a04b654c2fbc36d546aef2b2e511e760971",
|
||||
"sha256:9537eecf179f566fd1c160a2e912ca0b8e02d773af0a7a1120ad4f7507cd0d26",
|
||||
"sha256:9acdf933c1fd263c513a2df3dceecea6f3ff4419d80bf238510976bf9bcb26cd",
|
||||
"sha256:ae0975f42ab1f28364dcda3dde3cf6c1ddab3e1d4b2909da0cb0191fa9ca0480",
|
||||
"sha256:b3af02ecc999c8003e538e60c89a2b37646b39b688d4e44d7373e11c2debabec",
|
||||
"sha256:b6ff59cee96b454516e47e7721098e6ceebef435e3e21ac2d6c3b8b02628eb77",
|
||||
"sha256:b765ed3930b92812aa698a455847141869ef755a87e099fddd4ccf9d81fffb57",
|
||||
"sha256:c98c5ffd7d41611407a1103ae11c8b634ad6a43606eca3e2a5a269e5d6e8eb07",
|
||||
"sha256:cf7eb6b1025d3e169989416b1adcd676624c2dbed9e3bcb7137f51bfc8cc2572",
|
||||
"sha256:d92350c22b150c1cae7ebb0ee8b5670cc84848f6359cf6b5d8f86617098a9b73",
|
||||
"sha256:e422c3152921cece8b6a2fb6b0b4d73b6579bd20ae075e7d15143e711f3ca2ca",
|
||||
"sha256:e840f552a509e3380b0f0ec977e8124d0dc34dc0e68289ca28f4d7c1d0d79474",
|
||||
"sha256:f3d0a94ad151870978fb93538e95411c83899c9dc63e6fb65542f769568ecfa5"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==1.18.1"
|
||||
},
|
||||
"opencv-python": {
|
||||
"hashes": [
|
||||
"sha256:0f2e739c582e8c5e432130648bc6d66a56bc65f4cd9ff0bc7033033d2130c7a3",
|
||||
"sha256:0f3d159ad6cb9cbd188c726f87485f0799a067a0a15f34c25d7b5c8db3cb2e50",
|
||||
"sha256:167a6aff9bd124a3a67e0ec25d0da5ecdc8d96a56405e3e5e7d586c4105eb1bb",
|
||||
"sha256:1b90d50bc7a31e9573a8da1b80fcd1e4d9c86c0e5f76387858e1b87eb8b0332b",
|
||||
"sha256:2baf1213ae2fd678991f905d7b2b94eddfdfb5f75757db0f0b31eebd48ca200d",
|
||||
"sha256:312dda54c7e809c20d7409418060ae0e9cdbe82975e7ced429eb3c234ffc0d4a",
|
||||
"sha256:32384e675f7cefe707cac40a95eeb142d6869065e39c5500374116297cd8ca6d",
|
||||
"sha256:5c50634dd8f2f866fd99fd939292ce10e52bef82804ebc4e7f915221c3b7e951",
|
||||
"sha256:6841bb9cc24751dbdf94e7eefc4e6d70ec297952501954471299fd12ab67391c",
|
||||
"sha256:68c1c846dd267cd7e293d3fc0bb238db0a744aa1f2e721e327598f00cb982098",
|
||||
"sha256:703910aaa1dcd25a412f78a190fb7a352d9a64ee7d9a35566d786f3cc66ebf20",
|
||||
"sha256:8002959146ed21959e3118c60c8e94ceac02eea15b691da6c62cff4787c63f7f",
|
||||
"sha256:889eef049d38488b5b4646c48a831feed37c0fd44f3d83c05cff80f4baded145",
|
||||
"sha256:8c76983c9ec3e4cf3a4c1d172ec4285332d9fb1c7194d724aff0c518437471ee",
|
||||
"sha256:9cd9bd72f4a9743ef6f11f0f96784bd215a542e996db1717d4c2d3d03eb81a1b",
|
||||
"sha256:a1a5517301dc8d56243a14253d231ec755b94486b4fff2ae68269bc941bb1f2e",
|
||||
"sha256:a2b08aec2eacae868723136383d9eb84a33062a7a7ec5ec3bd2c423bd1355946",
|
||||
"sha256:a8529a79233f3581a66984acd16bce52ab0163f6f77568dd69e9ee4956d2e1db",
|
||||
"sha256:afbc81a3870739610a9f9a1197374d6a45892cf1933c90fc5617d39790991ed3",
|
||||
"sha256:baeb5dd8b21c718580687f5b4efd03f8139b1c56239cdf6b9805c6946e80f268",
|
||||
"sha256:db1d49b753e6e6c76585f21d09c7e9812176732baa9bddb64bc2fc6cd24d4179",
|
||||
"sha256:e242ed419aeb2488e0f9ee6410a34917f0f8d62b3ae96aa3170d83bae75004e2",
|
||||
"sha256:e36a8857be2c849e54009f1bee25e8c34fbc683fcd38c6c700af4cba5f8d57c2",
|
||||
"sha256:e699232fd033ef0053efec2cba0a7505514f374ba7b18c732a77cb5304311ef9",
|
||||
"sha256:eae3da9231d87980f8082d181c276a04f7a6fdac130cebd467390b96dd05f944",
|
||||
"sha256:ee6814c94dbf1cae569302afef9dd29efafc52373e8770ded0db549a3b6e0c00",
|
||||
"sha256:f01a87a015227d8af407161eb48222fc3c8b01661cdc841e2b86eee4f1a7a417"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==4.2.0.32"
|
||||
},
|
||||
"pillow": {
|
||||
"hashes": [
|
||||
"sha256:0a628977ac2e01ca96aaae247ec2bd38e729631ddf2221b4b715446fd45505be",
|
||||
"sha256:4d9ed9a64095e031435af120d3c910148067087541131e82b3e8db302f4c8946",
|
||||
"sha256:54ebae163e8412aff0b9df1e88adab65788f5f5b58e625dc5c7f51eaf14a6837",
|
||||
"sha256:5bfef0b1cdde9f33881c913af14e43db69815c7e8df429ceda4c70a5e529210f",
|
||||
"sha256:5f3546ceb08089cedb9e8ff7e3f6a7042bb5b37c2a95d392fb027c3e53a2da00",
|
||||
"sha256:5f7ae9126d16194f114435ebb79cc536b5682002a4fa57fa7bb2cbcde65f2f4d",
|
||||
"sha256:62a889aeb0a79e50ecf5af272e9e3c164148f4bd9636cc6bcfa182a52c8b0533",
|
||||
"sha256:7406f5a9b2fd966e79e6abdaf700585a4522e98d6559ce37fc52e5c955fade0a",
|
||||
"sha256:8453f914f4e5a3d828281a6628cf517832abfa13ff50679a4848926dac7c0358",
|
||||
"sha256:87269cc6ce1e3dee11f23fa515e4249ae678dbbe2704598a51cee76c52e19cda",
|
||||
"sha256:875358310ed7abd5320f21dd97351d62de4929b0426cdb1eaa904b64ac36b435",
|
||||
"sha256:8ac6ce7ff3892e5deaab7abaec763538ffd011f74dc1801d93d3c5fc541feee2",
|
||||
"sha256:91b710e3353aea6fc758cdb7136d9bbdcb26b53cefe43e2cba953ac3ee1d3313",
|
||||
"sha256:9d2ba4ed13af381233e2d810ff3bab84ef9f18430a9b336ab69eaf3cd24299ff",
|
||||
"sha256:a62ec5e13e227399be73303ff301f2865bf68657d15ea50b038d25fc41097317",
|
||||
"sha256:ab76e5580b0ed647a8d8d2d2daee170e8e9f8aad225ede314f684e297e3643c2",
|
||||
"sha256:bf4003aa538af3f4205c5fac56eacaa67a6dd81e454ffd9e9f055fff9f1bc614",
|
||||
"sha256:bf598d2e37cf8edb1a2f26ed3fb255191f5232badea4003c16301cb94ac5bdd0",
|
||||
"sha256:c18f70dc27cc5d236f10e7834236aff60aadc71346a5bc1f4f83a4b3abee6386",
|
||||
"sha256:c5ed816632204a2fc9486d784d8e0d0ae754347aba99c811458d69fcdfd2a2f9",
|
||||
"sha256:dc058b7833184970d1248135b8b0ab702e6daa833be14035179f2acb78ff5636",
|
||||
"sha256:ff3797f2f16bf9d17d53257612da84dd0758db33935777149b3334c01ff68865"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==7.0.0"
|
||||
},
|
||||
"pysocks": {
|
||||
"hashes": [
|
||||
"sha256:08e69f092cc6dbe92a0fdd16eeb9b9ffbc13cadfe5ca4c7bd92ffb078b293299",
|
||||
"sha256:2725bd0a9925919b9b51739eea5f9e2bae91e83288108a9ad338b2e3a4435ee5",
|
||||
"sha256:3f8804571ebe159c380ac6de37643bb4685970655d3bba243530d6558b799aa0"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==1.7.1"
|
||||
},
|
||||
"pyyaml": {
|
||||
"hashes": [
|
||||
"sha256:059b2ee3194d718896c0ad077dd8c043e5e909d9180f387ce42012662a4946d6",
|
||||
"sha256:1cf708e2ac57f3aabc87405f04b86354f66799c8e62c28c5fc5f88b5521b2dbf",
|
||||
"sha256:24521fa2890642614558b492b473bee0ac1f8057a7263156b02e8b14c88ce6f5",
|
||||
"sha256:4fee71aa5bc6ed9d5f116327c04273e25ae31a3020386916905767ec4fc5317e",
|
||||
"sha256:70024e02197337533eef7b85b068212420f950319cc8c580261963aefc75f811",
|
||||
"sha256:74782fbd4d4f87ff04159e986886931456a1894c61229be9eaf4de6f6e44b99e",
|
||||
"sha256:940532b111b1952befd7db542c370887a8611660d2b9becff75d39355303d82d",
|
||||
"sha256:cb1f2f5e426dc9f07a7681419fe39cee823bb74f723f36f70399123f439e9b20",
|
||||
"sha256:dbbb2379c19ed6042e8f11f2a2c66d39cceb8aeace421bfc29d085d93eda3689",
|
||||
"sha256:e3a057b7a64f1222b56e47bcff5e4b94c4f61faac04c7c4ecb1985e18caa3994",
|
||||
"sha256:e9f45bd5b92c7974e59bcd2dcc8631a6b6cc380a904725fce7bc08872e691615"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==5.3"
|
||||
},
|
||||
"requests": {
|
||||
"hashes": [
|
||||
"sha256:43999036bfa82904b6af1d99e4882b560e5e2c68e5c4b0aa03b655f3d7d73fee",
|
||||
"sha256:b3f43d496c6daba4493e7c431722aeb7dbc6288f52a6e04e7b6023b0247817e6"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==2.23.0"
|
||||
},
|
||||
"urllib3": {
|
||||
"hashes": [
|
||||
"sha256:2f3db8b19923a873b3e5256dc9c2dedfa883e33d87c690d9c7913e1f40673cdc",
|
||||
"sha256:87716c2d2a7121198ebcb7ce7cccf6ce5e9ba539041cfbaeecfb641dc0bf6acc"
|
||||
],
|
||||
"version": "==1.25.8"
|
||||
}
|
||||
},
|
||||
"develop": {}
|
||||
}
|
||||
149
README.md
149
README.md
@@ -1,15 +1,146 @@
|
||||
# (Forked) Reolink Python Api Client
|
||||
A Reolink Camera client written in Python.
|
||||
<h1 align="center"> Reolink Python Api Client </h1>
|
||||
|
||||
_NB! for the original API client of this fork, go [here](https://github.com/ReolinkCameraAPI/reolink-python-api)_
|
||||
<p align="center">
|
||||
<img alt="Reolink Approval" src="https://img.shields.io/badge/reolink-approved-blue?style=flat-square">
|
||||
<img alt="GitHub" src="https://img.shields.io/github/license/ReolinkCameraApi/reolink-python-api?style=flat-square">
|
||||
<img alt="GitHub tag (latest SemVer)" src="https://img.shields.io/github/v/tag/ReolinkCameraApi/reolink-python-api?style=flat-square">
|
||||
<img alt="PyPI" src="https://img.shields.io/pypi/v/reolink-api?style=flat-square">
|
||||
<img alt="Discord" src="https://img.shields.io/discord/773257004911034389?style=flat-square">
|
||||
</p>
|
||||
|
||||
### Purpose
|
||||
---
|
||||
|
||||
This repository's purpose is to deliver a complete API for the Reolink Camera's, (tested on RLC-522)
|
||||
A Reolink Camera client written in Python. This repository's purpose **(with Reolink's full support)** is to deliver a complete API for the Reolink Cameras,
|
||||
although they have a basic API document - it does not satisfy the need for extensive camera communication.
|
||||
|
||||
### Installation
|
||||
Check out our documentation for more information on how to use the software at [https://reolink.oleaintueri.com](https://reolink.oleaintueri.com)
|
||||
|
||||
```bash
|
||||
python3 -m pip install git+https://github.com/barretobrock/reolink-python-api.git
|
||||
```
|
||||
|
||||
Other Supported Languages:
|
||||
- Go: [reolinkapigo](https://github.com/ReolinkCameraAPI/reolinkapigo)
|
||||
|
||||
### Join us on Discord
|
||||
|
||||
https://discord.gg/8z3fdAmZJP
|
||||
|
||||
|
||||
### Sponsorship
|
||||
|
||||
<a href="https://oleaintueri.com"><img src="https://oleaintueri.com/images/oliv.svg" width="60px"/><img width="200px" style="padding-bottom: 10px" src="https://oleaintueri.com/images/oleaintueri.svg"/></a>
|
||||
|
||||
[Oleaintueri](https://oleaintueri.com) is sponsoring the development and maintenance of these projects within their organisation.
|
||||
|
||||
|
||||
---
|
||||
|
||||
### Get started
|
||||
|
||||
Implement a "Camera" object by passing it an IP address, Username and Password. By instantiating the object, it will try retrieve a login token from the Reolink Camera. This token is necessary to interact with the Camera using other commands.
|
||||
|
||||
See the `examples` directory.
|
||||
|
||||
### Using the library as a Python Module
|
||||
|
||||
Install the package via Pip
|
||||
|
||||
pip install reolink-api==0.0.5
|
||||
|
||||
Install from GitHub
|
||||
|
||||
pip install git+https://github.com/ReolinkCameraAPI/reolink-python-api.git
|
||||
|
||||
## Contributors
|
||||
|
||||
---
|
||||
|
||||
### Styling and Standards
|
||||
|
||||
This project intends to stick with [PEP8](https://www.python.org/dev/peps/pep-0008/)
|
||||
|
||||
### How can I become a contributor?
|
||||
|
||||
#### Step 1
|
||||
|
||||
Get the Restful API calls by looking through the HTTP Requests made in the camera's web UI. I use Google Chrome developer mode (ctr + shift + i) -> Network.
|
||||
|
||||
#### Step 2
|
||||
|
||||
Fork the repository and make your changes.
|
||||
|
||||
#### Step 3
|
||||
|
||||
Make a pull request.
|
||||
|
||||
### API Requests Implementation Plan:
|
||||
|
||||
Stream:
|
||||
- [X] Blocking RTSP stream
|
||||
- [X] Non-Blocking RTSP stream
|
||||
|
||||
GET:
|
||||
- [X] Login
|
||||
- [X] Logout
|
||||
- [X] Display -> OSD
|
||||
- [X] Recording -> Encode (Clear and Fluent Stream)
|
||||
- [X] Recording -> Advance (Scheduling)
|
||||
- [X] Network -> General
|
||||
- [X] Network -> Advanced
|
||||
- [X] Network -> DDNS
|
||||
- [X] Network -> NTP
|
||||
- [X] Network -> E-mail
|
||||
- [X] Network -> FTP
|
||||
- [X] Network -> Push
|
||||
- [X] Network -> WIFI
|
||||
- [X] Alarm -> Motion
|
||||
- [X] System -> General
|
||||
- [X] System -> DST
|
||||
- [X] System -> Information
|
||||
- [ ] System -> Maintenance
|
||||
- [X] System -> Performance
|
||||
- [ ] System -> Reboot
|
||||
- [X] User -> Online User
|
||||
- [X] User -> Add User
|
||||
- [X] User -> Manage User
|
||||
- [X] Device -> HDD/SD Card
|
||||
- [ ] Zoom
|
||||
- [ ] Focus
|
||||
- [ ] Image (Brightness, Contrast, Saturation, Hue, Sharp, Mirror, Rotate)
|
||||
- [ ] Advanced Image (Anti-flicker, Exposure, White Balance, DayNight, Backlight, LED light, 3D-NR)
|
||||
- [X] Image Data -> "Snap" Frame from Video Stream
|
||||
|
||||
SET:
|
||||
- [X] Display -> OSD
|
||||
- [X] Recording -> Encode (Clear and Fluent Stream)
|
||||
- [ ] Recording -> Advance (Scheduling)
|
||||
- [X] Network -> General
|
||||
- [X] Network -> Advanced
|
||||
- [ ] Network -> DDNS
|
||||
- [ ] Network -> NTP
|
||||
- [ ] Network -> E-mail
|
||||
- [ ] Network -> FTP
|
||||
- [ ] Network -> Push
|
||||
- [X] Network -> WIFI
|
||||
- [ ] Alarm -> Motion
|
||||
- [ ] System -> General
|
||||
- [ ] System -> DST
|
||||
- [X] System -> Reboot
|
||||
- [X] User -> Online User
|
||||
- [X] User -> Add User
|
||||
- [X] User -> Manage User
|
||||
- [X] Device -> HDD/SD Card (Format)
|
||||
- [x] PTZ
|
||||
- [x] Zoom
|
||||
- [x] Focus
|
||||
- [X] Image (Brightness, Contrast, Saturation, Hue, Sharp, Mirror, Rotate)
|
||||
- [X] Advanced Image (Anti-flicker, Exposure, White Balance, DayNight, Backlight, LED light, 3D-NR)
|
||||
|
||||
### Supported Cameras
|
||||
|
||||
Any Reolink camera that has a web UI should work. The other's requiring special Reolink clients
|
||||
do not work and is not supported here.
|
||||
|
||||
- RLC-411WS
|
||||
- RLC-423
|
||||
- RLC-420-5MP
|
||||
- RLC-410-5MP
|
||||
- RLC-520
|
||||
|
||||
@@ -1,133 +0,0 @@
|
||||
import requests
|
||||
from reolink_api.resthandle import Request
|
||||
from .alarm import AlarmAPIMixin
|
||||
from .device import DeviceAPIMixin
|
||||
from .display import DisplayAPIMixin
|
||||
from .download import DownloadAPIMixin
|
||||
from .image import ImageAPIMixin
|
||||
from .motion import MotionAPIMixin
|
||||
from .network import NetworkAPIMixin
|
||||
from .ptz import PtzAPIMixin
|
||||
from .recording import RecordingAPIMixin
|
||||
from .system import SystemAPIMixin
|
||||
from .user import UserAPIMixin
|
||||
from .zoom import ZoomAPIMixin
|
||||
|
||||
|
||||
class APIHandler(AlarmAPIMixin,
|
||||
DeviceAPIMixin,
|
||||
DisplayAPIMixin,
|
||||
DownloadAPIMixin,
|
||||
ImageAPIMixin,
|
||||
MotionAPIMixin,
|
||||
NetworkAPIMixin,
|
||||
PtzAPIMixin,
|
||||
RecordingAPIMixin,
|
||||
SystemAPIMixin,
|
||||
UserAPIMixin,
|
||||
ZoomAPIMixin):
|
||||
"""
|
||||
The APIHandler class is the backend part of the API, the actual API calls
|
||||
are implemented in Mixins.
|
||||
This handles communication directly with the camera.
|
||||
Current camera's tested: RLC-411WS
|
||||
|
||||
All Code will try to follow the PEP 8 standard as described here: https://www.python.org/dev/peps/pep-0008/
|
||||
"""
|
||||
|
||||
def __init__(self, ip: str, username: str, password: str, https=False, **kwargs):
|
||||
"""
|
||||
Initialise the Camera API Handler (maps api calls into python)
|
||||
:param ip:
|
||||
:param username:
|
||||
:param password:
|
||||
:param proxy: Add a proxy dict for requests to consume.
|
||||
eg: {"http":"socks5://[username]:[password]@[host]:[port], "https": ...}
|
||||
More information on proxies in requests: https://stackoverflow.com/a/15661226/9313679
|
||||
"""
|
||||
scheme = 'https' if https else 'http'
|
||||
self.url = f"{scheme}://{ip}/cgi-bin/api.cgi"
|
||||
self.ip = ip
|
||||
self.token = None
|
||||
self.username = username
|
||||
self.password = password
|
||||
Request.proxies = kwargs.get("proxy") # Defaults to None if key isn't found
|
||||
|
||||
def login(self) -> bool:
|
||||
"""
|
||||
Get login token
|
||||
Must be called first, before any other operation can be performed
|
||||
:return: bool
|
||||
"""
|
||||
try:
|
||||
body = [{"cmd": "Login", "action": 0,
|
||||
"param": {"User": {"userName": self.username, "password": self.password}}}]
|
||||
param = {"cmd": "Login", "token": "null"}
|
||||
response = Request.post(self.url, data=body, params=param)
|
||||
if response is not None:
|
||||
data = response.json()[0]
|
||||
code = data["code"]
|
||||
if int(code) == 0:
|
||||
self.token = data["value"]["Token"]["name"]
|
||||
print("Login success")
|
||||
return True
|
||||
print(self.token)
|
||||
return False
|
||||
else:
|
||||
print("Failed to login\nStatus Code:", response.status_code)
|
||||
return False
|
||||
except Exception as e:
|
||||
print("Error Login\n", e)
|
||||
raise
|
||||
|
||||
def logout(self) -> bool:
|
||||
"""
|
||||
Logout of the camera
|
||||
:return: bool
|
||||
"""
|
||||
try:
|
||||
data = [{"cmd": "Logout", "action": 0}]
|
||||
self._execute_command('Logout', data)
|
||||
# print(ret)
|
||||
return True
|
||||
except Exception as e:
|
||||
print("Error Logout\n", e)
|
||||
return False
|
||||
|
||||
def _execute_command(self, command, data, multi=False):
|
||||
"""
|
||||
Send a POST request to the IP camera with given data.
|
||||
:param command: name of the command to send
|
||||
:param data: object to send to the camera (send as json)
|
||||
:param multi: whether the given command name should be added to the
|
||||
url parameters of the request. Defaults to False. (Some multi-step
|
||||
commands seem to not have a single command name)
|
||||
:return: response JSON as python object
|
||||
"""
|
||||
params = {"token": self.token, 'cmd': command}
|
||||
if multi:
|
||||
del params['cmd']
|
||||
try:
|
||||
if self.token is None:
|
||||
raise ValueError("Login first")
|
||||
if command == 'Download':
|
||||
# Special handling for downloading an mp4
|
||||
# Pop the filepath from data
|
||||
tgt_filepath = data[0].pop('filepath')
|
||||
# Apply the data to the params
|
||||
params.update(data[0])
|
||||
with requests.get(self.url, params=params, stream=True) as req:
|
||||
if req.status_code == 200:
|
||||
with open(tgt_filepath, 'wb') as f:
|
||||
f.write(req.content)
|
||||
return True
|
||||
else:
|
||||
print(f'Error received: {req.status_code}')
|
||||
return False
|
||||
|
||||
else:
|
||||
response = Request.post(self.url, data=data, params=params)
|
||||
return response.json()
|
||||
except Exception as e:
|
||||
print(f"Command {command} failed: {e}")
|
||||
raise
|
||||
@@ -1,24 +0,0 @@
|
||||
from reolink_api import APIHandler
|
||||
|
||||
|
||||
class Camera(APIHandler):
|
||||
|
||||
def __init__(self, ip: str, username: str = "admin", password: str = "", https: bool = False):
|
||||
"""
|
||||
Initialise the Camera object by passing the ip address.
|
||||
The default details {"username":"admin", "password":""} will be used if nothing passed
|
||||
:param ip:
|
||||
:param username:
|
||||
:param password:
|
||||
"""
|
||||
# For when you need to connect to a camera behind a proxy, pass
|
||||
# a proxy argument: proxy={"http": "socks5://127.0.0.1:8000"}
|
||||
APIHandler.__init__(self, ip, username, password, https=https)
|
||||
|
||||
# Normal call without proxy:
|
||||
# APIHandler.__init__(self, ip, username, password)
|
||||
|
||||
self.ip = ip
|
||||
self.username = username
|
||||
self.password = password
|
||||
super().login()
|
||||
@@ -1,16 +0,0 @@
|
||||
import io
|
||||
import yaml
|
||||
|
||||
|
||||
class ConfigHandler:
|
||||
camera_settings = {}
|
||||
|
||||
@staticmethod
|
||||
def load() -> yaml or None:
|
||||
try:
|
||||
stream = io.open("config.yml", 'r', encoding='utf8')
|
||||
data = yaml.safe_load(stream)
|
||||
return data
|
||||
except Exception as e:
|
||||
print("Config Property Error\n", e)
|
||||
return None
|
||||
@@ -1,106 +0,0 @@
|
||||
import os
|
||||
from threading import ThreadError
|
||||
import cv2
|
||||
from reolink_api.util import threaded
|
||||
|
||||
|
||||
class RtspClient:
|
||||
"""
|
||||
Inspiration from:
|
||||
- https://benhowell.github.io/guide/2015/03/09/opencv-and-web-cam-streaming
|
||||
- https://stackoverflow.com/questions/19846332/python-threading-inside-a-class
|
||||
- https://stackoverflow.com/questions/55828451/video-streaming-from-ip-camera-in-python-using-opencv-cv2-videocapture
|
||||
"""
|
||||
|
||||
def __init__(self, ip, username, password, port=554, profile="main", use_udp=True, callback=None, **kwargs):
|
||||
"""
|
||||
RTSP client is used to retrieve frames from the camera in a stream
|
||||
|
||||
:param ip: Camera IP
|
||||
:param username: Camera Username
|
||||
:param password: Camera User Password
|
||||
:param port: RTSP port
|
||||
:param profile: "main" or "sub"
|
||||
:param use_upd: True to use UDP, False to use TCP
|
||||
:param proxies: {"host": "localhost", "port": 8000}
|
||||
"""
|
||||
self.capture = None
|
||||
self.thread_cancelled = False
|
||||
self.callback = callback
|
||||
|
||||
capture_options = 'rtsp_transport;'
|
||||
self.ip = ip
|
||||
self.username = username
|
||||
self.password = password
|
||||
self.port = port
|
||||
self.proxy = kwargs.get("proxies")
|
||||
self.url = "rtsp://" + self.username + ":" + self.password + "@" + \
|
||||
self.ip + ":" + str(self.port) + "//h264Preview_01_" + profile
|
||||
if use_udp:
|
||||
capture_options = capture_options + 'udp'
|
||||
else:
|
||||
capture_options = capture_options + 'tcp'
|
||||
|
||||
os.environ["OPENCV_FFMPEG_CAPTURE_OPTIONS"] = capture_options
|
||||
|
||||
# opens the stream capture, but does not retrieve any frames yet.
|
||||
self._open_video_capture()
|
||||
|
||||
def _open_video_capture(self):
|
||||
# To CAP_FFMPEG or not To ?
|
||||
self.capture = cv2.VideoCapture(self.url, cv2.CAP_FFMPEG)
|
||||
|
||||
def _stream_blocking(self):
|
||||
while True:
|
||||
try:
|
||||
if self.capture.isOpened():
|
||||
ret, frame = self.capture.read()
|
||||
if ret:
|
||||
yield frame
|
||||
else:
|
||||
print("stream closed")
|
||||
self.capture.release()
|
||||
return
|
||||
except Exception as e:
|
||||
print(e)
|
||||
self.capture.release()
|
||||
return
|
||||
|
||||
@threaded
|
||||
def _stream_non_blocking(self):
|
||||
while not self.thread_cancelled:
|
||||
try:
|
||||
if self.capture.isOpened():
|
||||
ret, frame = self.capture.read()
|
||||
if ret:
|
||||
self.callback(frame)
|
||||
else:
|
||||
print("stream is closed")
|
||||
self.stop_stream()
|
||||
except ThreadError as e:
|
||||
print(e)
|
||||
self.stop_stream()
|
||||
|
||||
def stop_stream(self):
|
||||
self.capture.release()
|
||||
self.thread_cancelled = True
|
||||
|
||||
def open_stream(self):
|
||||
"""
|
||||
Opens OpenCV Video stream and returns the result according to the OpenCV documentation
|
||||
https://docs.opencv.org/3.4/d8/dfe/classcv_1_1VideoCapture.html#a473055e77dd7faa4d26d686226b292c1
|
||||
"""
|
||||
|
||||
# Reset the capture object
|
||||
if self.capture is None or not self.capture.isOpened():
|
||||
self._open_video_capture()
|
||||
|
||||
print("opening stream")
|
||||
|
||||
if self.callback is None:
|
||||
return self._stream_blocking()
|
||||
else:
|
||||
# reset the thread status if the object was not re-created
|
||||
if not self.thread_cancelled:
|
||||
self.thread_cancelled = False
|
||||
return self._stream_non_blocking()
|
||||
Reference in New Issue
Block a user