Removed old api calls, using new api module

- Tests currently broken
- wifi_strength is now a number of bars rather than dBm level
- camera no longer has armed or status attributes
- added serial numbers for sync and cameras
This commit is contained in:
Kevin Fronczak
2018-10-13 20:38:01 -04:00
parent 01c6f0b12b
commit 7fd822caf2
5 changed files with 175 additions and 333 deletions
+8 -3
View File
@@ -2,7 +2,8 @@
import logging
from json import dumps
from blinkpy.helpers.util import http_req
import blinkpy.helpers.errors as ERROR
from blinkpy.helpers.util import http_req, BlinkException
from blinkpy.helpers.constants import DEFAULT_URL
_LOGGER = logging.getLogger(__name__)
@@ -31,7 +32,7 @@ def request_networks(blink):
def request_syncmodule(blink, network):
"""Request sync module info."""
url = "{}/network/{}/syncmodule".format(blink.urls.base_url, network)
url = "{}/network/{}/syncmodules".format(blink.urls.base_url, network)
return http_get(blink, url)
@@ -41,7 +42,7 @@ def request_system_arm(blink, network):
return http_post(blink, url)
def request_system_disarm(blink, headers, network):
def request_system_disarm(blink, network):
"""Disarm system."""
url = "{}/network/{}/disarm".format(blink.urls.base_url, network)
return http_post(blink, url)
@@ -141,6 +142,8 @@ def http_get(blink, url, stream=False, json=True):
:param stream: Stream response? True/FALSE
:param json: Return json response? TRUE/False
"""
if blink.auth_header is None:
raise BlinkException(ERROR.AUTH_TOKEN)
return http_req(blink, url=url, headers=blink.auth_header, reqtype='get',
stream=stream, json_resp=json)
@@ -151,4 +154,6 @@ def http_post(blink, url):
:param url: URL to perfom post request.
"""
if blink.auth_header is None:
raise BlinkException(ERROR.AUTH_TOKEN)
return http_req(blink, url=url, headers=blink.auth_header, reqtype='post')
+21 -62
View File
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
"""
blinkpy by Kevin Fronczak - A Blink camera Python library.
@@ -13,16 +12,16 @@ I am in no way affiliated with Blink, nor Immedia Inc.
"""
import time
import json
import getpass
import logging
import blinkpy.helpers.errors as ERROR
from blinkpy import api
from blinkpy.sync_module import BlinkSyncModule
from blinkpy.helpers.util import (
http_req, create_session, BlinkURLHandler,
BlinkException, BlinkAuthenticationException)
create_session, BlinkURLHandler,
BlinkAuthenticationException)
from blinkpy.helpers.constants import (
DEFAULT_URL, BLINK_URL, LOGIN_URL, LOGIN_BACKUP_URL)
BLINK_URL, LOGIN_URL, LOGIN_BACKUP_URL)
REFRESH_RATE = 30
@@ -47,9 +46,6 @@ class Blink():
self._token = None
self._auth_header = None
self._host = None
self._events = []
self._last_summary = None
self._last_events = None
self.network_id = None
self.account_id = None
self.urls = None
@@ -62,9 +58,9 @@ class Blink():
self._login_url = LOGIN_URL
@property
def events(self):
"""Get all events on server."""
return self._events
def auth_header(self):
"""Return the authentication header."""
return self._auth_header
def start(self):
"""
@@ -79,12 +75,7 @@ class Blink():
self.get_auth_token()
self.get_ids()
self.sync = BlinkSyncModule(self, self._auth_header)
self.sync.get_videos()
if self.sync.video_count > 0:
self.sync.get_cameras()
self.sync.set_links()
self._events = self.events_request()
self.sync = BlinkSyncModule(self)
def login(self):
"""Prompt user for username and password."""
@@ -103,16 +94,13 @@ class Blink():
if not isinstance(self._password, str):
raise BlinkAuthenticationException(ERROR.PASSWORD)
headers = {'Host': DEFAULT_URL,
'Content-Type': 'application/json'}
data = json.dumps({
"email": self._username,
"password": self._password,
"client_specifier": "iPhone 9.2 | 2.2 | 222"
})
login_url = LOGIN_URL
self.session = create_session()
response = http_req(self, url=self._login_url, headers=headers,
data=data, json_resp=False, reqtype='post')
response = api.request_login(self,
login_url,
self._username,
self._password)
if response.status_code == 200:
response = response.json()
(self.region_id, self.region), = response['region'].items()
@@ -122,57 +110,30 @@ class Blink():
"when authenticating, "
"trying new url"), response.status_code
)
self._login_url = LOGIN_BACKUP_URL
response = http_req(self, url=self._login_url, headers=headers,
data=data, reqtype='post')
login_url = LOGIN_BACKUP_URL
response = api.request_login(self,
login_url,
self._username,
self._password)
self.region_id = 'piri'
self.region = "UNKNOWN"
self._host = "{}.{}".format(self.region_id, BLINK_URL)
self._token = response['authtoken']['authtoken']
self._auth_header = {'Host': self._host,
'TOKEN_AUTH': self._token}
self.urls = BlinkURLHandler(self.region_id)
self._login_url = login_url
return self._auth_header
def get_ids(self):
"""Set the network ID and Account ID."""
response = self._network_request()
response = api.request_networks(self)
self.network_id = str(response['networks'][0]['id'])
self.account_id = str(response['networks'][0]['account_id'])
def _network_request(self):
"""Get network and account information."""
url = self.urls.networks_url
headers = self._auth_header
if headers is None:
raise BlinkException(ERROR.AUTH_TOKEN)
return http_req(self, url=url, headers=headers, reqtype='get')
def events_request(self, skip_throttle=False):
"""Get events on server."""
url = "{}/{}".format(self.urls.event_url, self.network_id)
headers = self._auth_header
if self.check_if_ok_to_update() or skip_throttle:
self._last_events = http_req(self, url=url,
headers=headers,
reqtype='get')
return self._last_events
def summary_request(self, skip_throttle=False):
"""Get blink summary."""
url = self.urls.home_url
headers = self._auth_header
if headers is None:
raise BlinkException(ERROR.AUTH_TOKEN)
if self.check_if_ok_to_update() or skip_throttle:
self._last_summary = http_req(
self, url=url, headers=headers, reqtype='get')
return self._last_summary
def refresh(self, force_cache=False):
"""
Perform a system refresh.
@@ -181,8 +142,6 @@ class Blink():
"""
if self.check_if_ok_to_update() or force_cache:
_LOGGER.debug("Attempting refresh of cameras.")
self._last_events = self.events_request(skip_throttle=True)
self._last_summary = self.summary_request(skip_throttle=True)
self.sync.refresh(force_cache=force_cache)
def check_if_ok_to_update(self):
+60 -101
View File
@@ -2,7 +2,7 @@
from shutil import copyfileobj
import logging
from requests.exceptions import RequestException
from blinkpy import api
_LOGGER = logging.getLogger(__name__)
@@ -12,30 +12,22 @@ MAX_CLIPS = 5
class BlinkCamera():
"""Class to initialize individual camera."""
def __init__(self, config, sync):
def __init__(self, sync):
"""Initiailize BlinkCamera."""
self.sync = sync
self.urls = self.sync.urls
self.id = str(config['device_id']) # pylint: disable=invalid-name
self.name = config['name']
self._status = config['active']
self.thumbnail = "{}{}.jpg".format(self.urls.base_url,
config['thumbnail'])
self.clip = "{}{}".format(self.urls.base_url, config['video'])
self.temperature = config['temp']
self._battery_string = config['battery']
self.notifications = config['notifications']
self.motion = dict()
self.header = None
self.image_link = None
self.arm_link = None
self.region_id = config['region_id']
self.battery_voltage = -180
self.name = None
self.camera_id = None
self.network_id = None
self.thumbnail = None
self.serial = None
self.motion_enabled = None
self.battery_voltage = None
self.clip = None
self.temperature = None
self.battery_state = None
self.motion_detected = None
self.wifi_strength = None
self.camera_config = dict()
self.motion_enabled = None
self.last_record = list()
self.last_record = []
self._cached_image = None
self._cached_video = None
@@ -44,16 +36,14 @@ class BlinkCamera():
"""Return dictionary of all camera attributes."""
attributes = {
'name': self.name,
'device_id': self.id,
'status': self._status,
'armed': self.armed,
'camera_id': self.camera_id,
'serial': self.serial,
'temperature': self.temperature,
'temperature_c': self.temperature_c,
'battery': self.battery,
'thumbnail': self.thumbnail,
'video': self.clip,
'motion_enabled': self.motion_enabled,
'notifications': self.notifications,
'motion_detected': self.motion_detected,
'wifi_strength': self.wifi_strength,
'network_id': self.sync.network_id,
@@ -61,31 +51,11 @@ class BlinkCamera():
}
return attributes
@property
def status(self):
"""Return camera status."""
return self._status
@property
def armed(self):
"""Return camera arm status."""
return True if self._status == 'armed' else False
@property
def battery(self):
"""Return battery level as percentage."""
return round(self.battery_voltage / 180 * 100)
@property
def battery_string(self):
"""Return string indicating battery status."""
status = "Unknown"
if self._battery_string > 1 and self._battery_string <= 3:
status = "OK"
elif self._battery_string >= 0:
status = "Low"
return status
@property
def temperature_c(self):
"""Return temperature in celcius."""
@@ -107,50 +77,63 @@ class BlinkCamera():
def snap_picture(self):
"""Take a picture with camera to create a new thumbnail."""
self.sync.http_post(self.image_link)
return api.request_new_image(self.sync.blink,
self.network_id,
self.camera_id)
def set_motion_detect(self, enable):
"""Set motion detection."""
url = self.arm_link
if enable:
self.sync.http_post("{}{}".format(url, 'enable'))
else:
self.sync.http_post("{}{}".format(url, 'disable'))
return api.request_motion_detection_enable(self.sync.blink,
self.network_id,
self.camera_id)
return api.request_motion_detection_disable(self.sync.blink,
self.network_id,
self.camera_id)
def update(self, values, force_cache=False, skip_cache=False):
"""Update camera information."""
self.name = values['name']
self._status = values['active']
self.clip = "{}{}".format(
self.urls.base_url, values['video'])
new_thumbnail = "{}{}.jpg".format(
self.urls.base_url, values['thumbnail'])
self._battery_string = values['battery']
self.notifications = values['notifications']
def update(self, config, force_cache=False):
"""Update camera info."""
self.name = config['name']
self.camera_id = str(config['camera_id'])
self.network_id = str(config['network_id'])
self.serial = config['serial']
self.motion_enabled = config['enabled']
self.battery_voltage = config['battery_voltage']
self.battery_state = config['battery_state']
self.temperature = config['temperature']
self.wifi_strength = config['wifi_strength']
if config['thumbnail']:
new_thumbnail = "{}{}.jpg".format(self.sync.urls.base_url,
config['thumbnail'])
self.check_for_motion()
if self.last_record:
clip_addr = self.sync.all_clips[self.name][self.last_record[0]]
self.clip = "{}{}".format(self.sync.urls.base_url,
clip_addr)
update_cached_image = False
if new_thumbnail != self.thumbnail or self._cached_image is None:
update_cached_image = True
self.thumbnail = new_thumbnail
try:
cfg = self.sync.camera_config_request(self.id)
self.camera_config = cfg
except RequestException as err:
_LOGGER.warning("Could not get config for %s with id %s",
self.name, self.id)
_LOGGER.warning("Exception raised: %s", err)
try:
self.battery_voltage = cfg['camera'][0]['battery_voltage']
self.motion_enabled = cfg['camera'][0]['motion_alert']
self.wifi_strength = cfg['camera'][0]['wifi_strength']
self.temperature = cfg['camera'][0]['temperature']
except KeyError:
_LOGGER.warning("Problem extracting config for camera %s",
self.name)
update_cached_video = False
if self._cached_video is None or self.motion_detected:
update_cached_video = True
# Check if the most recent clip is included in the last_record list
# and that the last_record list is populated
if update_cached_image or force_cache:
self._cached_image = api.http_get(self.sync.blink,
url=self.thumbnail,
stream=True,
json=False)
if update_cached_video or force_cache:
self._cached_video = api.http_get(self.sync.blink,
url=self.clip,
stream=True,
json=False)
def check_for_motion(self):
"""Check if motion detected.."""
try:
records = sorted(self.sync.record_dates[self.name])
new_clip = records.pop()
@@ -168,30 +151,6 @@ class BlinkCamera():
_LOGGER.warning("Could not extract clip info from camera %s",
self.name)
if not skip_cache:
if update_cached_image or force_cache:
self._cached_image = self.sync.http_get(
self.image_refresh(), stream=True, json=False)
if (self.clip is None) or self.motion_detected or force_cache:
self._cached_video = self.sync.http_get(
self.clip, stream=True, json=False)
def image_refresh(self):
"""Refresh current thumbnail."""
url = self.urls.home_url
response = self.sync.http_get(url)['devices']
for element in response:
try:
if str(element['device_id']) == self.id:
self.thumbnail = (
"{}{}.jpg".format(
self.urls.base_url, element['thumbnail'])
)
return self.thumbnail
except KeyError:
pass
return None
def image_to_file(self, path):
"""
Write image to file.
-1
View File
@@ -19,7 +19,6 @@ def attempt_reauthorization(blink):
"""Attempt to refresh auth token and links."""
_LOGGER.debug("Auth token expired, attempting reauthorization.")
headers = blink.get_auth_token()
blink.sync.set_links()
return headers
+86 -166
View File
@@ -3,8 +3,8 @@
import logging
from requests.structures import CaseInsensitiveDict
from blinkpy import api
from blinkpy.camera import BlinkCamera
from blinkpy.helpers.util import http_req
from blinkpy.helpers.constants import ONLINE
_LOGGER = logging.getLogger(__name__)
@@ -13,117 +13,112 @@ _LOGGER = logging.getLogger(__name__)
class BlinkSyncModule():
"""Class to initialize sync module."""
def __init__(self, blink, header, urls=None):
def __init__(self, blink):
"""
Initialize Blink sync module.
:param blink: Blink class instantiation
:param header: Blink authentication header
:param urls: URL Handler instantiation (deprecated)
"""
self.blink = blink
self._auth_header = header
self._auth_header = blink.auth_header
self.network_id = blink.network_id
self.region = blink.region
self.region_id = blink.region_id
self.name = 'sync'
self.serial = None
self.status = None
self.sync_id = None
self.region = None
self.region_id = None
self._host = None
self._events = []
self.host = None
self.summary = None
self.homescreen = None
self.record_dates = {}
self.videos = {}
self.events = []
self.cameras = CaseInsensitiveDict({})
self._idlookup = {}
self._video_count = 0
self._all_videos = {}
self._summary = None
self.record_dates = dict()
self.first_init = True
self.all_clips = {}
self.start()
@property
def attributes(self):
"""Return sync attributes."""
attr = {
'name': self.name,
'id': self.sync_id,
'network_id': self.network_id,
'serial': self.serial,
'status': self.status,
'region': self.region,
'region_id': self.region_id,
}
return attr
@property
def urls(self):
"""Return device urls."""
return self.blink.urls
@property
def network_id(self):
"""Return the network id."""
return self.blink.network_id
@property
def camera_thumbs(self):
"""Return camera thumbnails."""
self.blink.refresh()
data = {}
for name, camera in self.cameras.items():
data[name] = camera.thumbnail
return data
@property
def id_table(self):
"""Return id/camera pairs."""
return self._idlookup
@property
def video_count(self):
"""Return number of videos on server."""
url = "{}/count".format(self.urls.video_url)
self._video_count = self.http_get(url)['count']
return self._video_count
@property
def events(self):
"""Get all events on server."""
return self._events
@property
def online(self):
"""Return boolean system online status."""
return ONLINE[self._status_request()['syncmodule']['status']]
@property
def videos(self):
"""Return video list."""
return self._all_videos
@property
def summary(self):
"""Get a full summary of device information."""
return self._summary
return ONLINE[self.status]
@property
def arm(self):
"""Return status of sync module: armed/disarmed."""
return self._summary['network']['armed']
return self.homescreen['network']['armed']
@arm.setter
def arm(self, value):
"""Arm or disarm system."""
if value:
value_to_append = 'arm'
else:
value_to_append = 'disarm'
url = "{}/{}/{}".format(self.urls.network_url,
self.network_id,
value_to_append)
self.http_post(url)
return api.request_system_arm(self.blink, self.network_id)
return api.request_system_disarm(self.blink, self.network_id)
def start(self):
"""Initialize the system."""
response = api.request_syncmodule(self.blink, self.network_id)
self.summary = response['syncmodule']
self.name = self.summary['name']
self.sync_id = self.summary['id']
self.network_id = self.summary['network_id']
self.serial = self.summary['serial']
self.status = self.summary['status']
self.events = self.get_events()
self.homescreen = api.request_homescreen(self.blink, self.network_id)
camera_info = self.get_camera_info()
for camera_config in camera_info:
name = camera_config['name']
self.cameras[name] = BlinkCamera(self)
self.videos = self.get_videos()
for camera_config in camera_info:
name = camera_config['name']
self.cameras[name].update(camera_config, force_cache=True)
def get_events(self):
"""Retrieve events from server."""
response = api.request_sync_events(self.blink, self.network_id)
return response['event']
def get_camera_info(self):
"""Retrieve camera information."""
response = api.request_cameras(self.blink, self.network_id)
return response['devicestatus']
def refresh(self, force_cache=False):
"""Get all blink cameras and pulls their most recent status."""
summary = self._summary_request()
events = self._events_request()
response = summary['devices']
self.get_videos()
for name in self.cameras:
camera = self.cameras[name]
for element in response:
try:
if str(element['device_id']) == camera.id:
element['video'] = self.videos[name][0]['clip']
thumb = self.videos[name][0]['thumb']
element['thumbnail'] = thumb
camera.update(element, force_cache=force_cache)
except KeyError:
pass
self._summary = summary
self._events = events
self.events = self.get_events()
self.videos = self.get_videos()
self.homescreen = api.request_homescreen(self.blink, self.network_id)
camera_info = self.get_camera_info()
for camera_config in camera_info:
name = camera_config['name']
self.cameras[name].update(camera_config, force_cache=force_cache)
def get_videos(self, start_page=0, end_page=1):
"""
@@ -135,8 +130,11 @@ class BlinkSyncModule():
"""
videos = list()
all_dates = dict()
for camera in self.cameras:
self.all_clips[camera] = {}
for page_num in range(start_page, end_page + 1):
this_page = self._video_request(page_num)
this_page = api.request_videos(self.blink, page=page_num)
if not this_page:
break
videos.append(this_page)
@@ -150,18 +148,19 @@ class BlinkSyncModule():
clip_date = clip_addr.split('_')[-6:]
clip_date = '_'.join(clip_date)
clip_date = clip_date.split('.')[0]
self.all_clips[camera_name][clip_date] = clip_addr
if camera_name not in all_dates:
all_dates[camera_name] = list()
all_dates[camera_name].append(clip_date)
try:
self._all_videos[camera_name].append(
self.videos[camera_name].append(
{
'clip': clip_addr,
'thumb': thumb_addr,
}
)
except KeyError:
self._all_videos[camera_name] = [
self.videos[camera_name] = [
{
'clip': clip_addr,
'thumb': thumb_addr,
@@ -169,83 +168,4 @@ class BlinkSyncModule():
]
self.record_dates = all_dates
def get_cameras(self):
"""Find and creates cameras."""
self._summary = self._summary_request()
response = self._summary['devices']
for element in response:
if ('device_type' in element and
element['device_type'] == 'camera'):
# Add region to config
element['region_id'] = self.region_id
try:
name = element['name']
element['video'] = self.videos[name][0]['clip']
element['thumbnail'] = self.videos[name][0]['thumb']
except KeyError:
element['video'] = None
element['thumbnail'] = None
device = BlinkCamera(element, self)
self.cameras[device.name] = device
self._idlookup[device.id] = device.name
self.blink.refresh(force_cache=self.first_init)
self.first_init = False
def set_links(self):
"""Set access links and required headers for each camera in system."""
for name in self.cameras:
camera = self.cameras[name]
network_id_url = "{}/{}".format(self.urls.network_url,
self.network_id)
image_url = "{}/camera/{}/thumbnail".format(network_id_url,
camera.id)
arm_url = "{}/camera/{}/".format(network_id_url,
camera.id)
camera.image_link = image_url
camera.arm_link = arm_url
camera.header = self._auth_header
def _summary_request(self):
"""Request a summary from blink."""
return self.blink.summary_request()
def _events_request(self):
"""Request a list of events from blink."""
return self.blink.events_request()
def _video_request(self, page=0):
"""Perform a request for videos."""
url = "{}/page/{}".format(self.urls.video_url, page)
return self.http_get(url)
def _status_request(self):
"""Get syncmodule status."""
url = "{}/{}/syncmodules".format(self.urls.network_url,
self.network_id)
return self.http_get(url)
def camera_config_request(self, camera_id):
"""Retrieve more info about Blink config."""
url = "{}/network/{}/camera/{}/config".format(self.urls.base_url,
self.network_id,
str(camera_id))
return self.http_get(url)
def http_get(self, url, stream=False, json=True):
"""
Perform an http get request.
:param url: URL to perform get request
:param stream: Stream response? True/FALSE
:param json: Return json response? TRUE/False
"""
return http_req(self.blink, url=url, headers=self._auth_header,
reqtype='get', stream=stream, json_resp=json)
def http_post(self, url):
"""Perform a post request.
:param url: URL to perform post request
"""
return http_req(self.blink, url=url, headers=self._auth_header,
reqtype='post')
return self.videos