Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4a4811bee2 | ||
|
|
ad7ece4d45 | ||
|
|
17d1aa9690 | ||
|
|
b00839ecb3 | ||
|
|
4900ea8ba6 | ||
|
|
4fb16398e4 | ||
|
|
a1fc0c5a2b | ||
|
|
6adb252cdd | ||
|
|
3637bbad8d | ||
|
|
694efe71f8 | ||
|
|
315735bc2f | ||
|
|
db6403b468 | ||
|
|
92dda40e84 | ||
|
|
6c8fc0abce | ||
|
|
2d74b12d43 | ||
|
|
197695ee40 | ||
|
|
490c42550d | ||
|
|
7367eb6e87 | ||
|
|
414b647dc8 | ||
|
|
57882bb811 | ||
|
|
bb76f09f31 | ||
|
|
741cf489c8 | ||
|
|
7ce332b3ca | ||
|
|
8b7a491c89 | ||
|
|
604eebd1b7 | ||
|
|
c2f44b3dfd | ||
|
|
ccad3dcb0c |
+11
@@ -3,6 +3,17 @@ Changelog
|
||||
|
||||
A list of changes between each release
|
||||
|
||||
0.14.2 (2019-10-12)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
- Update dependencies
|
||||
- Dockerize `(@3ch01c <https://github.com/fronzbot/blinkpy/pull/198>__)`
|
||||
|
||||
0.14.1 (2019-06-20)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
- Fix timeout problems blocking blinkpy startup
|
||||
- Updated login urls using ``rest-region`` subdomain
|
||||
- Removed deprecated thumbanil recovery from homescreen
|
||||
|
||||
0.14.0 (2019-05-23)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
**Breaking Changes:**
|
||||
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
FROM python:3.7-alpine
|
||||
LABEL maintainer="Kevin Fronczak <kfronczak@gmail.com>"
|
||||
|
||||
VOLUME /media
|
||||
|
||||
RUN python -m pip install --upgrade pip
|
||||
RUN pip3 install blinkpy
|
||||
|
||||
COPY app/ .
|
||||
|
||||
ENTRYPOINT ["python", "./app.py"]
|
||||
CMD []
|
||||
@@ -0,0 +1 @@
|
||||
"""Python init file for app.py."""
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
"""Script to run blinkpy as an app."""
|
||||
from os import environ
|
||||
from datetime import datetime, timedelta
|
||||
from blinkpy import blinkpy
|
||||
|
||||
|
||||
USERNAME = environ.get('USERNAME')
|
||||
PASSWORD = environ.get('PASSWORD')
|
||||
TIMEDELTA = timedelta(environ.get('TIMEDELTA', 1))
|
||||
|
||||
|
||||
def get_date():
|
||||
"""Return now - timedelta for blinkpy."""
|
||||
return (datetime.now() - TIMEDELTA).isoformat()
|
||||
|
||||
|
||||
def download_videos(blink, save_dir='/media'):
|
||||
"""Make request to download videos."""
|
||||
blink.download_videos(save_dir, since=get_date())
|
||||
|
||||
|
||||
def start():
|
||||
"""Startup blink app."""
|
||||
blink = blinkpy.Blink(username=USERNAME, password=PASSWORD)
|
||||
blink.start()
|
||||
return blink
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
BLINK = start()
|
||||
download_videos(BLINK)
|
||||
@@ -0,0 +1,2 @@
|
||||
#!/bin/bash
|
||||
docker build -t fronzbot/blinkpy:latest ./
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
#!/bin/bash
|
||||
# bash run.sh [username] [password]
|
||||
|
||||
if [ "$#" -ne 2 ]; then
|
||||
echo ""
|
||||
echo "ERROR: Requries Blink username and password as arguments."
|
||||
echo "bash run.sh [username] [password]"
|
||||
echo ""
|
||||
exit 1
|
||||
fi
|
||||
|
||||
set -ex
|
||||
USER=fronzbot
|
||||
IMAGE=blinkpy
|
||||
CONFIG=$HOME/blinkpy_media
|
||||
USERNAME=$1
|
||||
PASSWORD=$2
|
||||
|
||||
mkdir -p $CONFIG
|
||||
|
||||
result=$(docker images -q $IMAGE)
|
||||
if [ $result ]; then
|
||||
docker rm $IMAGE
|
||||
fi
|
||||
docker run -it --name ${IMAGE} \
|
||||
-v $CONFIG:/media \
|
||||
-e USERNAME=${USERNAME} \
|
||||
-e PASSWORD=${PASSWORD} \
|
||||
$USER/$IMAGE \
|
||||
/bin/bash
|
||||
|
||||
+7
-7
@@ -78,6 +78,7 @@ class Blink():
|
||||
self.cameras = CaseInsensitiveDict({})
|
||||
self.video_list = CaseInsensitiveDict({})
|
||||
self._login_url = LOGIN_URL
|
||||
self.login_urls = []
|
||||
self.motion_interval = motion_interval
|
||||
self.version = __version__
|
||||
self.legacy = legacy_subdomain
|
||||
@@ -131,9 +132,9 @@ class Blink():
|
||||
if not isinstance(self._password, str):
|
||||
raise BlinkAuthenticationException(ERROR.PASSWORD)
|
||||
|
||||
login_urls = [LOGIN_URL, OLD_LOGIN_URL, LOGIN_BACKUP_URL]
|
||||
self.login_urls = [LOGIN_URL, OLD_LOGIN_URL, LOGIN_BACKUP_URL]
|
||||
|
||||
response = self.login_request(login_urls, is_retry=is_retry)
|
||||
response = self.login_request(is_retry=is_retry)
|
||||
|
||||
if not response:
|
||||
return False
|
||||
@@ -148,10 +149,10 @@ class Blink():
|
||||
|
||||
return self._auth_header
|
||||
|
||||
def login_request(self, login_urls, is_retry=False):
|
||||
def login_request(self, is_retry=False):
|
||||
"""Make a login request."""
|
||||
try:
|
||||
login_url = login_urls.pop(0)
|
||||
login_url = self.login_urls.pop(0)
|
||||
except IndexError:
|
||||
_LOGGER.error("Could not login to blink servers.")
|
||||
return False
|
||||
@@ -165,14 +166,13 @@ class Blink():
|
||||
is_retry=is_retry)
|
||||
try:
|
||||
if response.status_code != 200:
|
||||
response = self.login_request(login_urls)
|
||||
response = self.login_request(is_retry=True)
|
||||
response = response.json()
|
||||
(self.region_id, self.region), = response['region'].items()
|
||||
|
||||
except AttributeError:
|
||||
_LOGGER.error("Login API endpoint failed with response %s",
|
||||
response,
|
||||
exc_info=True)
|
||||
response)
|
||||
return False
|
||||
|
||||
except KeyError:
|
||||
|
||||
+5
-18
@@ -117,13 +117,16 @@ class BlinkCamera():
|
||||
_LOGGER.warning("Could not retrieve calibrated temperature.")
|
||||
|
||||
# Check if thumbnail exists in config, if not try to
|
||||
# get it from the homescreen info in teh sync module
|
||||
# get it from the homescreen info in the sync module
|
||||
# otherwise set it to None and log an error
|
||||
new_thumbnail = None
|
||||
thumb_addr = None
|
||||
if config['thumbnail']:
|
||||
thumb_addr = config['thumbnail']
|
||||
else:
|
||||
thumb_addr = self.get_thumb_from_homescreen()
|
||||
_LOGGER.warning("Could not find thumbnail for camera %s",
|
||||
self.name,
|
||||
exc_info=True)
|
||||
|
||||
if thumb_addr is not None:
|
||||
new_thumbnail = "{}{}.jpg".format(self.sync.urls.base_url,
|
||||
@@ -192,19 +195,3 @@ class BlinkCamera():
|
||||
return
|
||||
with open(path, 'wb') as vidfile:
|
||||
copyfileobj(response.raw, vidfile)
|
||||
|
||||
def get_thumb_from_homescreen(self):
|
||||
"""Retrieve thumbnail from homescreen."""
|
||||
for device in self.sync.homescreen['devices']:
|
||||
try:
|
||||
device_type = device['device_type']
|
||||
device_name = device['name']
|
||||
device_thumb = device['thumbnail']
|
||||
if device_type == 'camera' and device_name == self.name:
|
||||
return device_thumb
|
||||
except KeyError:
|
||||
pass
|
||||
_LOGGER.error("Could not find thumbnail for camera %s",
|
||||
self.name,
|
||||
exc_info=True)
|
||||
return None
|
||||
|
||||
@@ -4,7 +4,7 @@ import os
|
||||
|
||||
MAJOR_VERSION = 0
|
||||
MINOR_VERSION = 14
|
||||
PATCH_VERSION = 0
|
||||
PATCH_VERSION = 2
|
||||
|
||||
__version__ = '{}.{}.{}'.format(MAJOR_VERSION, MINOR_VERSION, PATCH_VERSION)
|
||||
|
||||
@@ -33,6 +33,7 @@ PROJECT_CLASSIFIERS = [
|
||||
'Programming Language :: Python :: 3.5',
|
||||
'Programming Language :: Python :: 3.6',
|
||||
'Programming Language :: Python :: 3.7',
|
||||
'Programming Language :: Python :: 3.8',
|
||||
'Topic :: Home Automation'
|
||||
]
|
||||
|
||||
@@ -45,11 +46,11 @@ PYPI_URL = 'https://pypi.python.org/pypi/{}'.format(PROJECT_PACKAGE_NAME)
|
||||
URLS
|
||||
'''
|
||||
BLINK_URL = 'immedia-semi.com'
|
||||
DEFAULT_URL = "{}.{}".format('prod', BLINK_URL)
|
||||
DEFAULT_URL = "{}.{}".format('rest-prod', BLINK_URL)
|
||||
BASE_URL = "https://{}".format(DEFAULT_URL)
|
||||
LOGIN_URL = "{}/api/v2/login".format(BASE_URL)
|
||||
OLD_LOGIN_URL = "{}/login".format(BASE_URL)
|
||||
LOGIN_BACKUP_URL = "https://{}.{}/login".format('rest.piri', BLINK_URL)
|
||||
LOGIN_BACKUP_URL = "https://{}.{}/login".format('rest-piri', BLINK_URL)
|
||||
|
||||
'''
|
||||
Dictionaries
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import logging
|
||||
import time
|
||||
from functools import wraps
|
||||
from functools import partial, wraps
|
||||
from requests import Request, Session, exceptions
|
||||
from blinkpy.helpers.constants import BLINK_URL, TIMESTAMP_FORMAT
|
||||
import blinkpy.helpers.errors as ERROR
|
||||
@@ -28,8 +28,14 @@ def merge_dicts(dict_a, dict_b):
|
||||
|
||||
|
||||
def create_session():
|
||||
"""Create a session for blink communication."""
|
||||
"""
|
||||
Create a session for blink communication.
|
||||
|
||||
From @ericfrederich via
|
||||
https://github.com/kennethreitz/requests/issues/2011
|
||||
"""
|
||||
sess = Session()
|
||||
sess.get = partial(sess.get, timeout=5)
|
||||
return sess
|
||||
|
||||
|
||||
@@ -65,7 +71,7 @@ def http_req(blink, url='http://example.com', data=None, headers=None,
|
||||
prepped = req.prepare()
|
||||
|
||||
try:
|
||||
response = blink.session.send(prepped, stream=stream, timeout=10)
|
||||
response = blink.session.send(prepped, stream=stream)
|
||||
if json_resp and 'code' in response.json():
|
||||
if is_retry:
|
||||
_LOGGER.error("Cannot obtain new token for server auth.")
|
||||
|
||||
+3
-3
@@ -1,4 +1,4 @@
|
||||
python-dateutil==2.7.5
|
||||
requests>=2.20.0
|
||||
python-slugify==3.0.2
|
||||
python-dateutil~=2.8.0
|
||||
requests==2.22.0
|
||||
python-slugify~=3.0.2
|
||||
testtools==2.3.0
|
||||
|
||||
@@ -69,7 +69,8 @@ class TestBlinkFunctions(unittest.TestCase):
|
||||
bad_req,
|
||||
new_req
|
||||
]
|
||||
self.blink.login_request(['test1', 'test2', 'test3'])
|
||||
self.blink.login_urls = ['test1', 'test2', 'test3']
|
||||
self.blink.login_request()
|
||||
# pylint: disable=protected-access
|
||||
self.assertEqual(self.blink._login_url, 'test3')
|
||||
|
||||
@@ -78,7 +79,8 @@ class TestBlinkFunctions(unittest.TestCase):
|
||||
new_req,
|
||||
bad_req
|
||||
]
|
||||
self.blink.login_request(['test1', 'test2', 'test3'])
|
||||
self.blink.login_urls = ['test1', 'test2', 'test3']
|
||||
self.blink.login_request()
|
||||
# pylint: disable=protected-access
|
||||
self.assertEqual(self.blink._login_url, 'test2')
|
||||
|
||||
|
||||
+1
-40
@@ -99,45 +99,6 @@ class TestBlinkCameraSetup(unittest.TestCase):
|
||||
self.assertEqual(self.camera.image_from_cache, 'test')
|
||||
self.assertEqual(self.camera.video_from_cache, 'foobar')
|
||||
|
||||
def test_thumbnail_not_in_info(self, mock_sess):
|
||||
"""Test that we grab thumbanil if not in camera_info."""
|
||||
mock_sess.side_effect = [
|
||||
mresp.MockResponse({'temp': 71}, 200),
|
||||
'foobar',
|
||||
'barfoo'
|
||||
]
|
||||
self.camera.last_record = ['1']
|
||||
self.camera.sync.last_record = {
|
||||
'new': {
|
||||
'clip': '/test.mp4',
|
||||
'time': '1970-01-01T00:00:00'
|
||||
}
|
||||
}
|
||||
config = {
|
||||
'name': 'new',
|
||||
'id': 1234,
|
||||
'network_id': 5678,
|
||||
'serial': '12345678',
|
||||
'enabled': False,
|
||||
'battery_voltage': 90,
|
||||
'battery_state': 'ok',
|
||||
'temperature': 68,
|
||||
'wifi_strength': 4,
|
||||
'thumbnail': '',
|
||||
}
|
||||
self.camera.sync.homescreen = {
|
||||
'devices': [
|
||||
{'foo': 'bar'},
|
||||
{'device_type': 'foobar'},
|
||||
{'device_type': 'camera',
|
||||
'name': 'new',
|
||||
'thumbnail': '/new/thumb'}
|
||||
]
|
||||
}
|
||||
self.camera.update(config)
|
||||
self.assertEqual(self.camera.thumbnail,
|
||||
'https://rest-test.immedia-semi.com/new/thumb.jpg')
|
||||
|
||||
def test_no_thumbnails(self, mock_sess):
|
||||
"""Tests that thumbnail is 'None' if none found."""
|
||||
mock_sess.return_value = 'foobar'
|
||||
@@ -167,7 +128,7 @@ class TestBlinkCameraSetup(unittest.TestCase):
|
||||
logrecord.output,
|
||||
[("WARNING:blinkpy.camera:Could not retrieve calibrated "
|
||||
"temperature."),
|
||||
("ERROR:blinkpy.camera:Could not find thumbnail for camera new"
|
||||
("WARNING:blinkpy.camera:Could not find thumbnail for camera new"
|
||||
"\nNoneType: None")]
|
||||
)
|
||||
|
||||
|
||||
@@ -20,9 +20,9 @@ deps =
|
||||
basepython = python3
|
||||
ignore_errors = True
|
||||
commands =
|
||||
pylint --rcfile={toxinidir}/pylintrc blinkpy tests
|
||||
flake8 blinkpy tests
|
||||
pydocstyle blinkpy tests
|
||||
pylint --rcfile={toxinidir}/pylintrc blinkpy tests app
|
||||
flake8 blinkpy tests app
|
||||
pydocstyle blinkpy tests app
|
||||
rst-lint README.rst
|
||||
rst-lint CHANGES.rst
|
||||
|
||||
|
||||
Reference in New Issue
Block a user