removing deleted files
parent
6cab281987
commit
66d601aaf2
|
@ -1,569 +0,0 @@
|
|||
import json
|
||||
import logging
|
||||
import requests
|
||||
from .settings import *
|
||||
|
||||
from datetime import datetime, timedelta
|
||||
from pytz import timezone
|
||||
|
||||
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
|
||||
|
||||
class EagleEyev3():
|
||||
"""
|
||||
Class representing the EagleEyev3 client.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
"""
|
||||
Initializes the EagleEyev3 client object.
|
||||
"""
|
||||
self.client_id = None
|
||||
self.client_secret = None
|
||||
self.access_token = None
|
||||
self.refresh_token = None
|
||||
self.redirect_uri = None
|
||||
|
||||
self._load_vars_from_settings()
|
||||
|
||||
self.user_base_url = None
|
||||
self.current_user = None
|
||||
self.users = []
|
||||
self.bridges = []
|
||||
self.cameras = []
|
||||
self.switches = []
|
||||
self.users = []
|
||||
self.accounts = []
|
||||
self.user_tz_obj = None
|
||||
|
||||
self.lazy_login = True
|
||||
|
||||
if self.lazy_login:
|
||||
try:
|
||||
self._load_access_token()
|
||||
except FileNotFoundError as e:
|
||||
logging.warn("self.lazy_login is set to {self.lazy_login} but could not find .lazy_login file to load")
|
||||
|
||||
def _load_vars_from_settings(self):
|
||||
"""
|
||||
Load variables from the settings module.
|
||||
"""
|
||||
self.client_id = settings.client_id
|
||||
self.client_secret = settings.client_secret
|
||||
self.server_protocol = settings.server_protocol
|
||||
self.server_host = settings.server_host
|
||||
self.server_port = settings.server_port
|
||||
self.server_path = settings.server_path
|
||||
|
||||
# Combine server_protocol, server_host, and server_port to make the redirect_uri
|
||||
# Note: Please see the note in settings.py about trailing slashes and modify this line if needed
|
||||
|
||||
self.redirect_uri = f"{self.server_protocol}://{self.server_host}:{self.server_port}/{self.server_path}"
|
||||
|
||||
|
||||
def _save_access_token(self):
|
||||
with open(".lazy_login", "w") as json_file:
|
||||
json.dump({
|
||||
'access_token': self.access_token,
|
||||
'refresh_token': self.refresh_token,
|
||||
'current_user': self.current_user
|
||||
}, json_file)
|
||||
|
||||
def _load_access_token(self):
|
||||
with open(".lazy_login", "r") as json_file:
|
||||
saved = json.load(json_file)
|
||||
if 'access_token' in saved:
|
||||
self.access_token = saved['access_token']
|
||||
if 'refresh_token' in saved:
|
||||
self.refresh_token = saved['refresh_token']
|
||||
|
||||
self.get_base_url(cascade=True)
|
||||
|
||||
|
||||
def time_now(self):
|
||||
return datetime.now(tz=self.user_tz_obj).isoformat(timespec='milliseconds')
|
||||
|
||||
|
||||
def time_before(self, ts=None, hours=6):
|
||||
if ts == None:
|
||||
ts = datetime.now(tz=self.user_tz_obj)
|
||||
|
||||
if type(ts) == str:
|
||||
ts = datetime.fromisoformat(ts)
|
||||
|
||||
return (ts - timedelta(hours=hours)).isoformat(timespec='milliseconds')
|
||||
|
||||
|
||||
|
||||
def login_tokens(self, code=None, cascade=True):
|
||||
"""
|
||||
Obtains login tokens using the authorization code.
|
||||
|
||||
Args:
|
||||
code (str): The authorization code.
|
||||
cascade (bool): Indicates whether to cascade and get the base URL and current user information.
|
||||
|
||||
Returns:
|
||||
dict: Dictionary containing the success status, response HTTP status code, data, and current user information.
|
||||
"""
|
||||
baseUrl = "https://auth.eagleeyenetworks.com/oauth2/token"
|
||||
pathUrl = f"?grant_type=authorization_code&scope=vms.all&code={code}&redirect_uri={self.redirect_uri}" # Note the trailing slash, make sure it matches the whitelist
|
||||
url = baseUrl + pathUrl
|
||||
|
||||
# Send a POST request to obtain login tokens
|
||||
response = requests.post(url, auth=(self.client_id, self.client_secret))
|
||||
response_json = response.json()
|
||||
|
||||
logging.info(f"{response.status_code} in login_tokens")
|
||||
|
||||
if response.status_code == 200:
|
||||
success = True
|
||||
self.access_token = response_json['access_token']
|
||||
self.refresh_token = response_json['refresh_token']
|
||||
|
||||
if self.lazy_login:
|
||||
self._save_access_token()
|
||||
|
||||
if cascade:
|
||||
self.get_base_url()
|
||||
else:
|
||||
success = False
|
||||
|
||||
return {
|
||||
"success": success,
|
||||
"response_http_status": response.status_code,
|
||||
"data": response_json,
|
||||
'current_user': self.current_user
|
||||
}
|
||||
|
||||
def logout(self):
|
||||
"""
|
||||
Revokes token.
|
||||
|
||||
Returns:
|
||||
dict: Dictionary containing the success status, response HTTP status code, and data.
|
||||
"""
|
||||
url = "https://auth.eagleeyenetworks.com/oauth2/revoke"
|
||||
|
||||
payload = {
|
||||
"token": self.access_token,
|
||||
"token_type_hint": "access_token"
|
||||
}
|
||||
|
||||
headers = {
|
||||
"Authorization": f"Bearer {self.access_token}",
|
||||
"Accept": "application/json",
|
||||
"Content-type": "application/json"
|
||||
}
|
||||
|
||||
# Send a POST request to obtain the base URL
|
||||
response = requests.post(url, json=payload, headers=headers)
|
||||
response_json = response.json()
|
||||
|
||||
logging.info(f"{response.status_code} in logout")
|
||||
|
||||
if response.status_code == 200:
|
||||
success = True
|
||||
else:
|
||||
success = False
|
||||
logging.info(f"call to logout: {response_json}")
|
||||
|
||||
self.access_token = None
|
||||
self.refresh_token = None
|
||||
|
||||
return {
|
||||
"success": success,
|
||||
"response_http_status": response.status_code,
|
||||
"data": response_json
|
||||
}
|
||||
|
||||
def get_base_url(self, cascade=True):
|
||||
"""
|
||||
Obtains the base URL for the user.
|
||||
|
||||
Args:
|
||||
cascade (bool): Indicates whether to cascade and get the current user information.
|
||||
|
||||
Returns:
|
||||
dict: Dictionary containing the success status, response HTTP status code, and data.
|
||||
"""
|
||||
url = "https://api.eagleeyenetworks.com/api/v3.0/clientSettings"
|
||||
headers = {
|
||||
"Authorization": f"Bearer {self.access_token}",
|
||||
"Accept": "application/json"
|
||||
}
|
||||
|
||||
# Send a GET request to obtain the base URL
|
||||
response = requests.get(url, headers=headers)
|
||||
response_json = response.json()
|
||||
|
||||
logging.info(f"{response.status_code} in get_base_url")
|
||||
|
||||
if response.status_code == 200:
|
||||
success = True
|
||||
if 'httpsBaseUrl' in response_json and 'hostname' in response_json['httpsBaseUrl']:
|
||||
self.user_base_url = response_json['httpsBaseUrl']['hostname']
|
||||
if cascade:
|
||||
self.get_current_user()
|
||||
else:
|
||||
success = False
|
||||
|
||||
return {
|
||||
"success": success,
|
||||
"response_http_status": response.status_code,
|
||||
"data": response_json
|
||||
}
|
||||
|
||||
def get_current_user(self):
|
||||
"""
|
||||
Obtains the information of the current user.
|
||||
|
||||
Returns:
|
||||
dict: Dictionary containing the success status, response HTTP status code, and data.
|
||||
"""
|
||||
url = f"https://{self.user_base_url}/api/v3.0/users/self?include=timeZone"
|
||||
headers = {
|
||||
"Authorization": f"Bearer {self.access_token}",
|
||||
"Accept": "application/json"
|
||||
}
|
||||
|
||||
# Send a GET request to obtain the current user information
|
||||
response = requests.get(url, headers=headers)
|
||||
response_json = response.json()
|
||||
|
||||
logging.info(f"{response.status_code} in get_current_user")
|
||||
|
||||
if response.status_code == 200:
|
||||
success = True
|
||||
self.current_user = response_json
|
||||
self.user_tz_obj = timezone(response_json['timeZone']['timeZone'])
|
||||
else:
|
||||
success = False
|
||||
|
||||
return {
|
||||
"success": success,
|
||||
"response_http_status": response.status_code,
|
||||
"data": response_json
|
||||
}
|
||||
|
||||
def get_list_of_users(self):
|
||||
"""
|
||||
Obtains the list of users.
|
||||
|
||||
Returns:
|
||||
dict: Dictionary containing the success status, response HTTP status code, and data.
|
||||
"""
|
||||
url = f"https://{self.user_base_url}/api/v3.0/users?include=timeZone"
|
||||
headers = {
|
||||
"Authorization": f"Bearer {self.access_token}",
|
||||
"Accept": "application/json"
|
||||
}
|
||||
|
||||
response = requests.get(url, headers=headers)
|
||||
response_json = response.json()
|
||||
|
||||
logging.info(f"{response.status_code} in get_list_of_users")
|
||||
|
||||
if response.status_code == 200:
|
||||
success = True
|
||||
self.users = [i for i in response_json['results']]
|
||||
else:
|
||||
success = False
|
||||
|
||||
return {
|
||||
"success": success,
|
||||
"response_http_status": response.status_code,
|
||||
"data": response_json
|
||||
}
|
||||
|
||||
def get_list_of_cameras(self):
|
||||
"""
|
||||
Obtains the list of cameras.
|
||||
|
||||
Returns:
|
||||
dict: Dictionary containing the success status, response HTTP status code, and data.
|
||||
"""
|
||||
url = f"https://{self.user_base_url}/api/v3.0/cameras?include=status"
|
||||
headers = {
|
||||
"Authorization": f"Bearer {self.access_token}",
|
||||
"Accept": "application/json"
|
||||
}
|
||||
|
||||
response = requests.get(url, headers=headers)
|
||||
response_json = response.json()
|
||||
|
||||
logging.info(f"{response.status_code} in get_list_of_cameras")
|
||||
|
||||
if response.status_code == 200:
|
||||
success = True
|
||||
self.cameras = [
|
||||
Camera(id=i['id'],\
|
||||
name=i['name'],\
|
||||
status=i['status'],\
|
||||
account_id=i['accountId'],\
|
||||
bridge_id=i['bridgeId'],\
|
||||
user_base_url=self.user_base_url,\
|
||||
een_instance=self)
|
||||
for i in response_json['results']]
|
||||
for camera in self.cameras:
|
||||
camera.user_base_url = self.user_base_url
|
||||
else:
|
||||
success = False
|
||||
|
||||
return {
|
||||
"success": success,
|
||||
"response_http_status": response.status_code,
|
||||
"data": response_json
|
||||
}
|
||||
|
||||
def get_list_of_bridges(self):
|
||||
"""
|
||||
Obtains the list of bridges.
|
||||
|
||||
Returns:
|
||||
dict: Dictionary containing the success status, response HTTP status code, and data.
|
||||
"""
|
||||
url = f"https://{self.user_base_url}/api/v3.0/bridges"
|
||||
headers = {
|
||||
"Authorization": f"Bearer {self.access_token}",
|
||||
"Accept": "application/json"
|
||||
}
|
||||
|
||||
response = requests.get(url, headers=headers)
|
||||
response_json = response.json()
|
||||
|
||||
logging.info(f"{response.status_code} in get_list_of_bridges")
|
||||
|
||||
if response.status_code == 200:
|
||||
success = True
|
||||
self.bridges = [i for i in response_json['results']]
|
||||
else:
|
||||
success = False
|
||||
|
||||
return {
|
||||
"success": success,
|
||||
"response_http_status": response.status_code,
|
||||
"data": response_json
|
||||
}
|
||||
|
||||
def get_list_of_switches(self):
|
||||
"""
|
||||
Obtains the list of switches.
|
||||
|
||||
Returns:
|
||||
dict: Dictionary containing the success status, response HTTP status code, and data.
|
||||
"""
|
||||
url = f"https://{self.user_base_url}/api/v3.0/switches"
|
||||
headers = {
|
||||
"Authorization": f"Bearer {self.access_token}",
|
||||
"Accept": "application/json"
|
||||
}
|
||||
|
||||
response = requests.get(url, headers=headers)
|
||||
response_json = response.json()
|
||||
|
||||
logging.info(f"{response.status_code} in get_list_of_switches")
|
||||
|
||||
if response.status_code == 200:
|
||||
success = True
|
||||
self.switches = [i for i in response_json['results']]
|
||||
else:
|
||||
success = False
|
||||
|
||||
return {
|
||||
"success": success,
|
||||
"response_http_status": response.status_code,
|
||||
"data": response_json
|
||||
}
|
||||
|
||||
def get_list_of_available_devices(self, deviceType__in="camera"):
|
||||
"""
|
||||
Obtains the list of available devices.
|
||||
|
||||
Returns:
|
||||
dict: Dictionary containing the success status, response HTTP status code, and data.
|
||||
"""
|
||||
url = f"https://{self.user_base_url}/api/v3.0/availableDevices?deviceType__in={deviceType__in}"
|
||||
headers = {
|
||||
"Authorization": f"Bearer {self.access_token}",
|
||||
"Accept": "application/json"
|
||||
}
|
||||
|
||||
response = requests.get(url, headers=headers)
|
||||
response_json = response.json()
|
||||
|
||||
logging.info(f"{response.status_code} in get_list_of_available_devices")
|
||||
|
||||
if response.status_code == 200:
|
||||
success = True
|
||||
else:
|
||||
success = False
|
||||
|
||||
return {
|
||||
"success": success,
|
||||
"response_http_status": response.status_code,
|
||||
"data": response_json
|
||||
}
|
||||
|
||||
def get_list_of_multi_cameras(self):
|
||||
"""
|
||||
Obtains the list of multi-cameras.
|
||||
|
||||
Returns:
|
||||
dict: Dictionary containing the success status, response HTTP status code, and data.
|
||||
"""
|
||||
url = f"https://{self.user_base_url}/api/v3.0/multiCameras"
|
||||
headers = {
|
||||
"Authorization": f"Bearer {self.access_token}",
|
||||
"Accept": "application/json"
|
||||
}
|
||||
|
||||
response = requests.get(url, headers=headers)
|
||||
response_json = response.json()
|
||||
|
||||
logging.info(f"{response.status_code} in get_list_of_multi_cameras")
|
||||
|
||||
if response.status_code == 200:
|
||||
success = True
|
||||
else:
|
||||
success = False
|
||||
|
||||
return {
|
||||
"success": success,
|
||||
"response_http_status": response.status_code,
|
||||
"data": response_json
|
||||
}
|
||||
|
||||
def get_list_of_feeds(self):
|
||||
"""
|
||||
Obtains the list of feeds.
|
||||
|
||||
Returns:
|
||||
dict: Dictionary containing the success status, response HTTP status code, and data.
|
||||
"""
|
||||
url = f"https://{self.user_base_url}/api/v3.0/feeds"
|
||||
headers = {
|
||||
"Authorization": f"Bearer {self.access_token}",
|
||||
"Accept": "application/json"
|
||||
}
|
||||
|
||||
response = requests.get(url, headers=headers)
|
||||
response_json = response.json()
|
||||
|
||||
logging.info(f"{response.status_code} in get_list_of_feeds")
|
||||
|
||||
if response.status_code == 200:
|
||||
success = True
|
||||
else:
|
||||
success = False
|
||||
|
||||
return {
|
||||
"success": success,
|
||||
"response_http_status": response.status_code,
|
||||
"data": response_json
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class Device():
|
||||
|
||||
def __init__(self, id=None, name=None, status=None, account_id=None, user_base_url=None, een_instance=None):
|
||||
|
||||
self.id = id
|
||||
self.name = name
|
||||
self.status = status
|
||||
self.account_id = account_id
|
||||
self.user_base_url = user_base_url,
|
||||
self.een_instance = een_instance
|
||||
|
||||
def get_id(self):
|
||||
return self.id
|
||||
|
||||
def get_status(self):
|
||||
return self.status['connectionStatus']
|
||||
|
||||
def is_online(self):
|
||||
return self.status['connectionStatus'] == "online"
|
||||
|
||||
def is_offline(self):
|
||||
return self.status['connectionStatus'] == "deviceOffline" or self.status['connectionStatus'] == "bridgeOffline"
|
||||
|
||||
def __repr__(self):
|
||||
if self.is_online():
|
||||
online = '✅'
|
||||
elif self.is_offline():
|
||||
online = '❌'
|
||||
else:
|
||||
online = '?'
|
||||
return f"{online} [{self.id}] - {self.name}"
|
||||
|
||||
|
||||
|
||||
class Camera(Device):
|
||||
|
||||
def __init__(self, id=None, name=None, status=None, account_id=None, bridge_id=None, user_base_url=None, een_instance=None):
|
||||
super().__init__(id=id, name=name, status=status, account_id=account_id, user_base_url=user_base_url, een_instance=een_instance)
|
||||
self.bridge_id = bridge_id
|
||||
self.previews = []
|
||||
self.videos = []
|
||||
self.events = {
|
||||
'status': [],
|
||||
'motion': []
|
||||
}
|
||||
|
||||
|
||||
def get_list_of_events(self, start_timestamp=None, end_timestamp=None):
|
||||
"""
|
||||
Obtains the list of events.
|
||||
|
||||
Returns:
|
||||
dict: Dictionary containing the success status, response HTTP status code, and data.
|
||||
"""
|
||||
|
||||
if start_timestamp == None or end_timestamp == None:
|
||||
return {
|
||||
"success": False,
|
||||
"response_http_status": None,
|
||||
"data": { 'msg': 'get_list_of_events called without required args, needs start_timestamp, end_timestamp' }
|
||||
}
|
||||
|
||||
url = f"https://{self.user_base_url}/api/v3.0/events?pageSize=100&include=een.deviceCloudStatusUpdate.v1&startTimestamp__gte={start_timestamp}&endTimestamp__lte={end_timestamp}&actor=camera%3A{self.id}&type__in=een.deviceCloudStatusUpdateEvent.v1"
|
||||
|
||||
headers = {
|
||||
"Authorization": f"Bearer {self.een_instance.access_token}",
|
||||
"Accept": "application/json"
|
||||
}
|
||||
|
||||
response = requests.get(url, headers=headers)
|
||||
response_json = response.json()
|
||||
|
||||
logging.debug(f"{response.status_code} returned from {url} with {headers} and {response.text}")
|
||||
logging.info(f"{response.status_code} in get_list_of_events")
|
||||
|
||||
if response.status_code == 200:
|
||||
success = True
|
||||
# filter events by type
|
||||
[self.events['status'].append(i) for i in response.json()['results'] if i['type'] == 'een.deviceCloudStatusUpdateEvent.v1']
|
||||
[self.events['motion'].append(i) for i in response.json()['results'] if i['type'] == 'een.motionDetectionEvent.v1']
|
||||
|
||||
# remove duplicates
|
||||
seen = set()
|
||||
self.events['status'] = [event for event in self.events['status'] if event['endTimestamp'] and event['id'] not in seen and not seen.add(event['id'])]
|
||||
seen = set()
|
||||
self.events['motion'] = [event for event in self.events['motion'] if event['id'] not in seen and not seen.add(event['id'])]
|
||||
|
||||
# sort by event startTimestamp descending
|
||||
self.events['status'] = sorted(self.events['status'], key=lambda x: x['startTimestamp'], reverse=True)
|
||||
self.events['motion'] = sorted(self.events['motion'], key=lambda x: x['startTimestamp'], reverse=True)
|
||||
else:
|
||||
success = False
|
||||
|
||||
return {
|
||||
"success": success,
|
||||
"response_http_status": response.status_code,
|
||||
"data": response_json
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
|
||||
# Set up your application and get client id/secrete first
|
||||
# https://developerv3.eagleeyenetworks.com/page/my-application
|
||||
client_id = ""
|
||||
client_secret = ""
|
||||
|
||||
# you will need to add approved redirect_uris in your application
|
||||
# this examples assumes you've added http://127.0.0.1:3333/login_callback
|
||||
# change the following variables if you did something different
|
||||
# Note: do not use localhost for server_host, use 127.0.0.1 instead
|
||||
server_protocol = "http"
|
||||
server_host = "127.0.0.1"
|
||||
server_port = "3333"
|
||||
server_path = "login_callback"
|
459
Playground.ipynb
459
Playground.ipynb
|
@ -1,459 +0,0 @@
|
|||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "d4582341",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# EagleEyev3 Playground\n",
|
||||
"\n",
|
||||
"To make this playground work, it is easier to read the `access_token` off the filesystem but you can always run the example server `python server.py` to go thorugh the Oauth2 flow. By default it will save the `access_token` into a file named `.lazy_login`. The module looks for that file and tries reading t"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "8355d241",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Import Module"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"id": "1394471a",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from EagleEyev3 import EagleEyev3"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"id": "486a2537",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stderr",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"INFO:root:200 in get_base_url\n",
|
||||
"INFO:root:200 in get_current_user\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"een = EagleEyev3()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "51b8b66e",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Adjust Log Level"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 3,
|
||||
"id": "06d91db2",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import logging\n",
|
||||
"logger = logging.getLogger()\n",
|
||||
"#logger.setLevel('DEBUG')\n",
|
||||
"#logger.setLevel('INFO')\n",
|
||||
"logger.setLevel('WARN')\n",
|
||||
"#logger.setLevel('ERROR')\n",
|
||||
"#logger.setLevel('CRITICAL')"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "0311109c-869c-4190-97c1-a6e717a8eeba",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Who am I"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 4,
|
||||
"id": "e14e2be5-a5f9-4b8c-ae60-76c61cb61b8b",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"'Mark Cotton - mcotton@mcottondesign.com'"
|
||||
]
|
||||
},
|
||||
"execution_count": 4,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"f\"{een.current_user['firstName']} {een.current_user['lastName']} - {een.current_user['email']}\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 5,
|
||||
"id": "4ef47ae2-a010-4b7e-87f6-3dbf0a047e16",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"'eyJraWQiOiI2ODYxYjBjYS0wZjI2LTExZWQtODYxZC0wMjQyYWMxMjAwMDIiLCJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJjYWZlZGVmMiIsImF1ZCI6InZtcy5hcGkiLCJpc3MiOiJ2bXMuYXV0aC52MSIsInZtc19hY2NvdW50IjoiMDAwMjgyMDEiLCJleHAiOjE2ODkzNzA1MTIsImlhdCI6MTY4ODc2NTcxMiwianRpIjoiYTM1Nzk3MmRmNzdiNTAwYTQ3NjBlNGY1MGYwNjNhMjUiLCJjbGllbnRfaWQiOiIzMmRhYzMzMDY0OWI0ODI3OWY5Y2FjYzJiNmY1N2FlNSIsInZtc19jbHVzdGVyIjoiYzAxMiJ9.EEmCaFqduOV5_cQ-VejeBXGbKLj1yHrxGkcMgqPUN1jYY0wR9bO7rkEwQh59Dj-1fr8pKsbrUr6DPDLfkaSIRlxpjdlsEBdzAmoZUpzPUfL9QzJu0C04OJU0gcBh7fwur7L2fMVthaZ6OKfThdM29qzRH5RR9gSAJGeNe08n4IvFVvuL80yvczgOiQwSzkg4PuHpbImDa44U7-qC8CrRvQ_TqsX6ziNzw-XmxZxXEwtZxkAOFDFaIsA8V4ezbQ_TSY6EElCnyyKLLI46-KIsGevx8dxa2NrhPFvC725dMhg-OPaCEF62sOlTrlHVBcv_e9MBk1VZLoDLStkSvU7Dxg'"
|
||||
]
|
||||
},
|
||||
"execution_count": 5,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"een.access_token"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "a22ff6c2",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Get Cameras"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 6,
|
||||
"id": "bb457850",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"ret = een.get_list_of_cameras()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 7,
|
||||
"id": "c43f1db1",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"[✅ [1001423e] - ATM & Wine,\n",
|
||||
" ✅ [100d8666] - Cash Register,\n",
|
||||
" ✅ [10012735] - Fuel Dock,\n",
|
||||
" ✅ [1002584c] - Safe]"
|
||||
]
|
||||
},
|
||||
"execution_count": 7,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"[i for i in een.cameras if i.is_online()]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 8,
|
||||
"id": "f4c6fe67",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"[? [10090759] - Benny Camera,\n",
|
||||
" ? [1003e10b] - Driveway,\n",
|
||||
" ? [100ba388] - Front Door,\n",
|
||||
" ? [100b7b3b] - Max Camera,\n",
|
||||
" ? [1009ae55] - Office]"
|
||||
]
|
||||
},
|
||||
"execution_count": 8,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"[i for i in een.cameras if not i.is_online()]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "8c140aaf-766f-4255-94ef-199d17cbc7a6",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Getting list of Events"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 9,
|
||||
"id": "74e78ee1-33b8-4a88-9d23-cd6281603a5b",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"2023-07-07T16:41:31.990-05:00 2023-07-07T10:41:31.990-05:00\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"print(een.time_now(), een.time_before())"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 11,
|
||||
"id": "c84c30dd-4b7c-415b-8e6f-e77de70d1924",
|
||||
"metadata": {
|
||||
"scrolled": true
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"for i in range(0,4):\n",
|
||||
" ts = een.time_now()\n",
|
||||
"\n",
|
||||
" for cam in een.cameras:\n",
|
||||
" blah = cam.get_list_of_events(end_timestamp=een.time_before(ts=ts, hours=(6*i)), \\\n",
|
||||
" start_timestamp=een.time_before(ts=ts, hours=(6*(i+1))) )"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 12,
|
||||
"id": "13809cc7-9ec2-4e15-9495-e64feaecca6d",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"18"
|
||||
]
|
||||
},
|
||||
"execution_count": 12,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"len(een.cameras[2].events['status'])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 13,
|
||||
"id": "67d4f79b-2b43-4bdb-9068-28ac9d8d921c",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"0"
|
||||
]
|
||||
},
|
||||
"execution_count": 13,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"len(een.cameras[0].events['motion'])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 14,
|
||||
"id": "8be8d503-b46d-4ba7-884e-2c21c3987129",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"ATM & Wine - 2023-07-07T18:49:29.664+00:00 - deviceOffline \n",
|
||||
"ATM & Wine - 2023-07-07T18:48:51.444+00:00 - error \n",
|
||||
"ATM & Wine - 2023-07-07T16:22:04.146+00:00 - online \n",
|
||||
"ATM & Wine - 2023-07-07T16:21:41.960+00:00 - deviceOffline \n",
|
||||
"ATM & Wine - 2023-07-07T16:18:16.383+00:00 - error \n",
|
||||
"ATM & Wine - 2023-07-07T16:16:31.511+00:00 - deviceOffline \n",
|
||||
"ATM & Wine - 2023-07-07T16:15:57.174+00:00 - error \n",
|
||||
"ATM & Wine - 2023-07-07T15:44:21.759+00:00 - online \n",
|
||||
"ATM & Wine - 2023-07-07T15:41:31.997+00:00 - online \n",
|
||||
"ATM & Wine - 2023-07-07T12:46:47.672+00:00 - bridgeOffline \n",
|
||||
"ATM & Wine - 2023-07-07T11:24:46.454+00:00 - online \n",
|
||||
"ATM & Wine - 2023-07-07T11:22:37.461+00:00 - deviceOffline \n",
|
||||
"ATM & Wine - 2023-07-07T11:21:59.127+00:00 - error \n",
|
||||
"ATM & Wine - 2023-07-07T09:44:24.462+00:00 - online \n",
|
||||
"ATM & Wine - 2023-07-07T09:41:33.395+00:00 - online \n",
|
||||
"ATM & Wine - 2023-07-07T06:00:38.322+00:00 - deviceOffline \n",
|
||||
"ATM & Wine - 2023-07-07T06:00:00.175+00:00 - error \n",
|
||||
"ATM & Wine - 2023-07-07T04:07:20.003+00:00 - online \n",
|
||||
"ATM & Wine - 2023-07-07T04:05:21.238+00:00 - deviceOffline \n",
|
||||
"ATM & Wine - 2023-07-07T04:04:42.160+00:00 - error \n",
|
||||
"ATM & Wine - 2023-07-07T03:44:26.904+00:00 - online \n",
|
||||
"ATM & Wine - 2023-07-07T03:41:56.630+00:00 - online \n",
|
||||
"ATM & Wine - 2023-07-07T01:48:05.792+00:00 - deviceOffline \n",
|
||||
"ATM & Wine - 2023-07-07T01:47:35.155+00:00 - error \n",
|
||||
"ATM & Wine - 2023-07-06T23:19:05.335+00:00 - online \n",
|
||||
"ATM & Wine - 2023-07-06T23:16:58.926+00:00 - deviceOffline \n",
|
||||
"ATM & Wine - 2023-07-06T23:16:28.421+00:00 - error \n",
|
||||
"ATM & Wine - 2023-07-06T23:10:32.790+00:00 - deviceOffline \n",
|
||||
"ATM & Wine - 2023-07-06T21:44:28.864+00:00 - online \n",
|
||||
"ATM & Wine - 2023-07-06T21:41:57.860+00:00 - online \n",
|
||||
"ATM & Wine - 2023-07-06T20:08:47.208+00:00 - deviceOffline \n",
|
||||
"ATM & Wine - 2023-07-06T20:08:16.933+00:00 - error \n",
|
||||
"ATM & Wine - 2023-07-06T20:03:05.801+00:00 - online \n",
|
||||
"ATM & Wine - 2023-07-06T20:02:43.659+00:00 - deviceOffline \n",
|
||||
"ATM & Wine - 2023-07-06T20:01:41.457+00:00 - error \n",
|
||||
"ATM & Wine - 2023-07-06T19:59:57.405+00:00 - deviceOffline \n",
|
||||
"ATM & Wine - 2023-07-06T19:59:24.184+00:00 - error \n",
|
||||
"ATM & Wine - 2023-07-06T19:31:55.867+00:00 - online \n",
|
||||
"ATM & Wine - 2023-07-06T19:29:46.708+00:00 - deviceOffline \n",
|
||||
"ATM & Wine - 2023-07-06T19:29:16.013+00:00 - error \n",
|
||||
"ATM & Wine - 2023-07-06T18:24:27.389+00:00 - online \n",
|
||||
"ATM & Wine - 2023-07-06T18:22:22.328+00:00 - deviceOffline \n",
|
||||
"ATM & Wine - 2023-07-06T18:21:52.102+00:00 - error \n",
|
||||
"ATM & Wine - 2023-07-06T15:52:12.759+00:00 - online \n",
|
||||
"ATM & Wine - 2023-07-06T15:50:09.509+00:00 - deviceOffline \n",
|
||||
"ATM & Wine - 2023-07-06T15:49:39.152+00:00 - error \n",
|
||||
"ATM & Wine - 2023-07-06T15:41:58.888+00:00 - online \n",
|
||||
"ATM & Wine - 2023-07-06T15:11:05.317+00:00 - deviceOffline \n",
|
||||
"ATM & Wine - 2023-07-06T15:10:35.056+00:00 - error \n",
|
||||
"ATM & Wine - 2023-07-06T14:59:51.787+00:00 - online \n",
|
||||
"ATM & Wine - 2023-07-06T14:58:55.970+00:00 - bridgeOffline \n",
|
||||
"ATM & Wine - 2023-07-06T14:58:40.967+00:00 - deviceOffline \n",
|
||||
"ATM & Wine - 2023-07-06T12:58:36.884+00:00 - online \n",
|
||||
"ATM & Wine - 2023-07-06T12:56:38.478+00:00 - deviceOffline \n",
|
||||
"ATM & Wine - 2023-07-06T12:56:07.792+00:00 - error \n",
|
||||
"ATM & Wine - 2023-07-06T12:49:42.742+00:00 - online \n",
|
||||
"ATM & Wine - 2023-07-06T12:47:32.312+00:00 - deviceOffline \n",
|
||||
"ATM & Wine - 2023-07-06T12:47:01.925+00:00 - error \n",
|
||||
"ATM & Wine - 2023-07-06T11:40:03.505+00:00 - online \n",
|
||||
"ATM & Wine - 2023-07-06T11:38:03.703+00:00 - deviceOffline \n",
|
||||
"ATM & Wine - 2023-07-06T11:37:23.038+00:00 - error \n",
|
||||
"ATM & Wine - 2023-07-06T11:31:52.611+00:00 - online \n",
|
||||
"ATM & Wine - 2023-07-06T11:29:42.541+00:00 - deviceOffline \n",
|
||||
"ATM & Wine - 2023-07-06T11:29:08.199+00:00 - error \n",
|
||||
"ATM & Wine - 2023-07-06T09:42:01.013+00:00 - online \n",
|
||||
"ATM & Wine - 2023-07-06T09:38:38.302+00:00 - deviceOffline \n",
|
||||
"ATM & Wine - 2023-07-06T09:38:07.937+00:00 - error \n",
|
||||
"ATM & Wine - 2023-07-06T08:31:02.432+00:00 - online \n",
|
||||
"ATM & Wine - 2023-07-06T08:28:53.086+00:00 - deviceOffline \n",
|
||||
"ATM & Wine - 2023-07-06T08:28:22.667+00:00 - error \n",
|
||||
"ATM & Wine - 2023-07-06T04:07:10.931+00:00 - online \n",
|
||||
"ATM & Wine - 2023-07-06T04:05:11.397+00:00 - deviceOffline \n",
|
||||
"ATM & Wine - 2023-07-06T04:04:38.124+00:00 - error \n",
|
||||
"ATM & Wine - 2023-07-06T03:43:19.799+00:00 - online \n",
|
||||
"ATM & Wine - 2023-07-06T02:15:51.335+00:00 - deviceOffline \n",
|
||||
"ATM & Wine - 2023-07-06T02:15:19.584+00:00 - error \n",
|
||||
"ATM & Wine - 2023-07-05T21:43:39.111+00:00 - online \n",
|
||||
"ATM & Wine - 2023-07-05T18:35:23.024+00:00 - deviceOffline \n",
|
||||
"ATM & Wine - 2023-07-05T18:34:44.881+00:00 - error \n",
|
||||
"ATM & Wine - 2023-07-05T17:13:14.356+00:00 - online \n",
|
||||
"ATM & Wine - 2023-07-05T17:11:09.341+00:00 - deviceOffline \n",
|
||||
"ATM & Wine - 2023-07-05T17:10:29.222+00:00 - error \n",
|
||||
"ATM & Wine - 2023-07-05T15:43:44.544+00:00 - online \n",
|
||||
"ATM & Wine - 2023-07-05T15:33:04.629+00:00 - deviceOffline \n",
|
||||
"ATM & Wine - 2023-07-05T15:32:34.148+00:00 - error \n",
|
||||
"ATM & Wine - 2023-07-05T15:13:36.151+00:00 - online \n",
|
||||
"ATM & Wine - 2023-07-05T15:11:33.954+00:00 - deviceOffline \n",
|
||||
"ATM & Wine - 2023-07-05T15:10:54.632+00:00 - error \n",
|
||||
"ATM & Wine - 2023-07-05T14:24:50.082+00:00 - online \n",
|
||||
"ATM & Wine - 2023-07-05T14:22:50.393+00:00 - deviceOffline \n",
|
||||
"ATM & Wine - 2023-07-05T14:22:12.040+00:00 - error \n",
|
||||
"ATM & Wine - 2023-07-05T14:02:42.746+00:00 - online \n",
|
||||
"ATM & Wine - 2023-07-05T14:00:34.085+00:00 - deviceOffline \n",
|
||||
"ATM & Wine - 2023-07-05T13:59:55.709+00:00 - error \n",
|
||||
"ATM & Wine - 2023-07-05T10:43:54.157+00:00 - online \n",
|
||||
"ATM & Wine - 2023-07-05T10:41:56.025+00:00 - deviceOffline \n",
|
||||
"ATM & Wine - 2023-07-05T10:41:23.556+00:00 - error \n",
|
||||
"ATM & Wine - 2023-07-05T10:10:27.404+00:00 - online \n",
|
||||
"ATM & Wine - 2023-07-05T10:08:19.943+00:00 - deviceOffline \n",
|
||||
"ATM & Wine - 2023-07-05T10:07:50.455+00:00 - error \n",
|
||||
"ATM & Wine - 2023-07-05T09:44:11.782+00:00 - online \n",
|
||||
"Cash Register - 2023-07-07T20:02:49.237+00:00 - bridgeOffline \n",
|
||||
"Cash Register - 2023-07-07T20:02:31.354+00:00 - deviceOffline \n",
|
||||
"Cash Register - 2023-07-07T16:01:54.202+00:00 - online \n",
|
||||
"Cash Register - 2023-07-07T16:01:52.891+00:00 - bridgeOffline \n",
|
||||
"Cash Register - 2023-07-07T15:44:21.759+00:00 - online \n",
|
||||
"Cash Register - 2023-07-07T15:41:31.997+00:00 - online \n",
|
||||
"Cash Register - 2023-07-06T23:01:59.550+00:00 - bridgeOffline \n",
|
||||
"Cash Register - 2023-07-06T21:44:28.864+00:00 - online \n",
|
||||
"Cash Register - 2023-07-06T21:41:57.860+00:00 - online \n",
|
||||
"Cash Register - 2023-07-06T14:58:55.385+00:00 - bridgeOffline \n",
|
||||
"Cash Register - 2023-07-06T14:58:40.382+00:00 - deviceOffline \n",
|
||||
"Cash Register - 2023-07-06T10:01:40.245+00:00 - online \n",
|
||||
"Cash Register - 2023-07-06T10:01:39.158+00:00 - bridgeOffline \n",
|
||||
"Cash Register - 2023-07-06T09:42:01.013+00:00 - online \n",
|
||||
"Cash Register - 2023-07-05T20:06:43.349+00:00 - bridgeOffline \n",
|
||||
"Cash Register - 2023-07-05T18:01:54.584+00:00 - online \n",
|
||||
"Cash Register - 2023-07-05T18:01:53.582+00:00 - bridgeOffline \n",
|
||||
"Cash Register - 2023-07-05T15:43:44.544+00:00 - online \n",
|
||||
"Safe - 2023-07-06T14:58:55.537+00:00 - bridgeOffline \n",
|
||||
"Safe - 2023-07-06T14:58:40.535+00:00 - deviceOffline \n",
|
||||
"Safe - 2023-07-06T09:42:01.013+00:00 - online \n",
|
||||
"Safe - 2023-07-06T07:27:05.695+00:00 - bridgeOffline \n",
|
||||
"Safe - 2023-07-06T03:43:19.799+00:00 - online \n",
|
||||
"Safe - 2023-07-06T02:05:12.252+00:00 - bridgeOffline \n",
|
||||
"Safe - 2023-07-05T21:43:39.111+00:00 - online \n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"for cam in een.cameras:\n",
|
||||
" for i in cam.events['status']:\n",
|
||||
" print(f\"{cam.name} - {i['startTimestamp']} - {i['data'][0]['newStatus']['connectionStatus']} \")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "1cebbb4e-4c4e-4ab0-9627-251eb812b2f1",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "d8a69185-54cc-4c74-9c9b-a807e601be83",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3 (ipykernel)",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.11.3"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
85
app.py
85
app.py
|
@ -1,85 +0,0 @@
|
|||
|
||||
import json, requests
|
||||
from flask import Flask, request, session, render_template, redirect, url_for
|
||||
from flask_session import Session
|
||||
|
||||
import logging
|
||||
logger = logging.getLogger()
|
||||
|
||||
#logger.setLevel('DEBUG')
|
||||
logger.setLevel('INFO')
|
||||
#logger.setLevel('WARN')
|
||||
#logger.setLevel('ERROR')
|
||||
#logger.setLevel('CRITICAL')
|
||||
|
||||
from EagleEyev3 import EagleEyev3
|
||||
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
SESSION_TYPE = 'filesystem'
|
||||
app.config.from_object(__name__)
|
||||
Session(app)
|
||||
|
||||
@app.route('/')
|
||||
def index():
|
||||
|
||||
if 'een' in session:
|
||||
een = session['een']
|
||||
else:
|
||||
een = EagleEyev3()
|
||||
session['een'] = een
|
||||
|
||||
# using current_user as a proxy for an established valid session
|
||||
if een.access_token == None:
|
||||
base_url = "https://auth.eagleeyenetworks.com/oauth2/authorize"
|
||||
path_url = f"?client_id={een.client_id}&response_type=code&scope=vms.all&redirect_uri={een.redirect_uri}"
|
||||
return redirect(f"{base_url}{path_url}")
|
||||
|
||||
# call this to check if session is actually valid
|
||||
check = een.get_current_user()
|
||||
if 'success' not in check or check['success'] == False:
|
||||
base_url = "https://auth.eagleeyenetworks.com/oauth2/authorize"
|
||||
path_url = f"?client_id={een.client_id}&response_type=code&scope=vms.all&redirect_uri={een.redirect_uri}"
|
||||
return redirect(f"{base_url}{path_url}")
|
||||
else:
|
||||
logging.info(f"{check['success']} - check get_current_user")
|
||||
|
||||
|
||||
values = {
|
||||
"current_user": een.current_user
|
||||
}
|
||||
|
||||
return render_template('index.html', template_values=values)
|
||||
|
||||
|
||||
@app.route('/login_callback')
|
||||
def login_callback():
|
||||
# This is getting the ?code= querystring value from the HTTP request.
|
||||
code = request.args.get('code')
|
||||
|
||||
if 'een' in session:
|
||||
een = session['een']
|
||||
else:
|
||||
een = EagleEyev3()
|
||||
|
||||
|
||||
if (code):
|
||||
# use the include code parameter to complete login process
|
||||
oauth_object = een.login_tokens(code)
|
||||
|
||||
|
||||
return redirect(url_for('index'))
|
||||
|
||||
|
||||
@app.route('/logout')
|
||||
def logout():
|
||||
if 'een' in session:
|
||||
een = session['een']
|
||||
een.logout()
|
||||
|
||||
return redirect(url_for('index'))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.run(host=een.server_host, port=een.server_port)
|
|
@ -1,15 +0,0 @@
|
|||
pytz==2023.3
|
||||
requests==2.29.0
|
||||
blinker==1.6.2
|
||||
certifi==2023.5.7
|
||||
charset-normalizer==3.1.0
|
||||
click==8.1.3
|
||||
Flask==2.3.2
|
||||
idna==3.4
|
||||
itsdangerous==2.1.2
|
||||
Jinja2==3.1.2
|
||||
MarkupSafe==2.1.3
|
||||
pytz==2023.3
|
||||
requests==2.29.0
|
||||
urllib3==1.26.16
|
||||
Werkzeug==2.3.6
|
|
@ -1,131 +0,0 @@
|
|||
|
||||
{% if "X-PJAX" not in request.headers %}
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
<meta name="description" content="">
|
||||
<meta name="author" content="">
|
||||
|
||||
<link rel="shortcut icon" href="{{ url_for('static', filename='favicon.ico') }}">
|
||||
{% block title %}
|
||||
<title>classifier.een.cloud</title>
|
||||
{% endblock %}
|
||||
|
||||
<!-- Bootstrap core CSS -->
|
||||
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">
|
||||
|
||||
<link rel="stylesheet" href="//cdn.datatables.net/1.10.19/css/jquery.dataTables.min.css">
|
||||
<link href="/static/dataTables.bootstrap4.min.css">
|
||||
|
||||
<!-- Custom styles for this template -->
|
||||
<!-- <link href="album.css" rel="stylesheet">
|
||||
<link href="custom.css" rel="stylesheet"> -->
|
||||
<link href="/static/style.css" rel="stylesheet">
|
||||
|
||||
<!-- self-hosted fontawesome -->
|
||||
<link href="/static/css/all.css" rel="stylesheet">
|
||||
|
||||
{% block style %}
|
||||
{% endblock %}
|
||||
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<header>
|
||||
|
||||
|
||||
<nav class="navbar navbar-expand-lg navbar-light bg-light">
|
||||
|
||||
|
||||
<div class="collapse navbar-collapse">
|
||||
<ul class="navbar-nav mr-auto mt-2 mt-lg-0">
|
||||
<li class="nav-item active">
|
||||
<a href="/" class="navbar-brand d-flex align-items-center">
|
||||
<strong><small>status</small>.mcotton.space</strong>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
<span class="navbar-text">
|
||||
{% block current_user %}
|
||||
{% endblock %}
|
||||
</span>
|
||||
</form>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
</header>
|
||||
|
||||
|
||||
<main role="main">
|
||||
{% endif %}
|
||||
|
||||
{% block main %}
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% if "X-PJAX" not in request.headers %}
|
||||
|
||||
</main>
|
||||
|
||||
<footer class="text-muted">
|
||||
<div class="container">
|
||||
<p class="float-right">
|
||||
</p>
|
||||
<p></p>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<!-- Bootstrap core JavaScript
|
||||
================================================== -->
|
||||
<!-- Placed at the end of the document so the pages load faster -->
|
||||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js"></script>
|
||||
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js"></script>
|
||||
|
||||
<script src="//cdn.datatables.net/1.10.19/js/jquery.dataTables.min.js"></script>
|
||||
<script src="https://cdn.datatables.net/1.10.19/js/dataTables.bootstrap4.min.js"></script>
|
||||
<script src="/static/js/jquery.pjax.js"></script>
|
||||
|
||||
<script>
|
||||
|
||||
$(document).ready(function() {
|
||||
|
||||
$('#user_mode_toggle').on('click', function(event) {
|
||||
event.stopPropagation();
|
||||
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: '/api/v1/user_mode/toggle',
|
||||
dataType: "text",
|
||||
contentType : 'application/json',
|
||||
success: function(data) {
|
||||
//$('#labelModal-add').text('Saved');
|
||||
//$('#labelModal').modal('hide');
|
||||
location.reload();
|
||||
},
|
||||
error: function(data) {
|
||||
console.log(data)
|
||||
$('#labelModal-add').text('Failed');
|
||||
//$('#labelModal').modal('hide');
|
||||
//location.reload();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
{% block script %}
|
||||
|
||||
{% endblock%}
|
||||
</body>
|
||||
</html>
|
||||
{% endif %}
|
|
@ -1,21 +0,0 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block style %}
|
||||
{% endblock %}
|
||||
|
||||
{% block current_user %}
|
||||
{{ template_values['current_user']['email'] }}
|
||||
<a href="/logout" tabindex="-1">Logout</a>
|
||||
{% endblock %}
|
||||
|
||||
{% block main %}
|
||||
|
||||
<div class="container text-center">
|
||||
<div class="row">
|
||||
<div class="col-md-6 offset-3">
|
||||
<h1>Hello {{ template_values['current_user']['firstName'] }} {{ template_values['current_user']['lastName'] }}</h1>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
115
tests.py
115
tests.py
|
@ -1,115 +0,0 @@
|
|||
import unittest
|
||||
from unittest.mock import MagicMock, patch
|
||||
from EagleEyev3 import EagleEyev3
|
||||
|
||||
class TestEagleEyev3(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
# Create an instance of EagleEyev3 for testing
|
||||
self.eagle_eye = EagleEyev3()
|
||||
|
||||
@patch('EagleEyev3.requests')
|
||||
def test_login_tokens_success(self, mock_requests):
|
||||
# Mock the requests.post method to return a successful response
|
||||
mock_response = MagicMock()
|
||||
mock_response.status_code = 200
|
||||
mock_response.json.return_value = {'access_token': 'token', 'refresh_token': 'refresh_token'}
|
||||
mock_requests.post.return_value = mock_response
|
||||
|
||||
result = self.eagle_eye.login_tokens(code='authorization_code', cascade=True)
|
||||
|
||||
self.assertTrue(result['success'])
|
||||
self.assertEqual(result['response_http_status'], 200)
|
||||
self.assertEqual(result['data']['access_token'], 'token')
|
||||
self.assertEqual(result['data']['refresh_token'], 'refresh_token')
|
||||
|
||||
@patch('EagleEyev3.requests')
|
||||
def test_login_tokens_failure(self, mock_requests):
|
||||
# Mock the requests.post method to return an unsuccessful response
|
||||
mock_response = MagicMock()
|
||||
mock_response.status_code = 400
|
||||
mock_response.json.return_value = {}
|
||||
mock_requests.post.return_value = mock_response
|
||||
|
||||
result = self.eagle_eye.login_tokens(code='authorization_code', cascade=True)
|
||||
|
||||
self.assertFalse(result['success'])
|
||||
self.assertEqual(result['response_http_status'], 400)
|
||||
self.assertEqual(result['data'], {})
|
||||
|
||||
@patch('EagleEyev3.requests')
|
||||
def test_get_base_url_success(self, mock_requests):
|
||||
# Set access_token for testing
|
||||
self.eagle_eye.access_token = 'access_token'
|
||||
|
||||
# Mock the requests.get method to return a successful response
|
||||
mock_response = MagicMock()
|
||||
mock_response.status_code = 200
|
||||
mock_response.json.return_value = {'httpsBaseUrl': {'hostname': 'base_url'}}
|
||||
mock_requests.get.return_value = mock_response
|
||||
|
||||
result = self.eagle_eye.get_base_url(cascade=True)
|
||||
|
||||
self.assertTrue(result['success'])
|
||||
self.assertEqual(result['response_http_status'], 200)
|
||||
self.assertEqual(self.eagle_eye.user_base_url, 'base_url')
|
||||
self.assertIsNotNone(result['data'])
|
||||
|
||||
@patch('EagleEyev3.requests')
|
||||
def test_get_base_url_failure(self, mock_requests):
|
||||
# Set access_token for testing
|
||||
self.eagle_eye.access_token = 'access_token'
|
||||
|
||||
# Mock the requests.get method to return an unsuccessful response
|
||||
mock_response = MagicMock()
|
||||
mock_response.status_code = 400
|
||||
mock_response.json.return_value = {}
|
||||
mock_requests.get.return_value = mock_response
|
||||
|
||||
result = self.eagle_eye.get_base_url(cascade=True)
|
||||
|
||||
self.assertFalse(result['success'])
|
||||
self.assertEqual(result['response_http_status'], 400)
|
||||
self.assertEqual(self.eagle_eye.user_base_url, None)
|
||||
self.assertEqual(result['data'], {})
|
||||
|
||||
@patch('EagleEyev3.requests')
|
||||
def test_get_current_user_success(self, mock_requests):
|
||||
# Set access_token and user_base_url for testing
|
||||
self.eagle_eye.access_token = 'access_token'
|
||||
self.eagle_eye.user_base_url = 'base_url'
|
||||
|
||||
# Mock the requests.get method to return a successful response
|
||||
mock_response = MagicMock()
|
||||
mock_response.status_code = 200
|
||||
mock_response.json.return_value = {'user_id': 'user123'}
|
||||
mock_requests.get.return_value = mock_response
|
||||
|
||||
result = self.eagle_eye.get_current_user()
|
||||
|
||||
self.assertTrue(result['success'])
|
||||
self.assertEqual(result['response_http_status'], 200)
|
||||
self.assertEqual(result['data']['user_id'], 'user123')
|
||||
|
||||
@patch('EagleEyev3.requests')
|
||||
def test_get_current_user_failure(self, mock_requests):
|
||||
# Set access_token and user_base_url for testing
|
||||
self.eagle_eye.access_token = 'access_token'
|
||||
self.eagle_eye.user_base_url = 'base_url'
|
||||
|
||||
# Mock the requests.get method to return an unsuccessful response
|
||||
mock_response = MagicMock()
|
||||
mock_response.status_code = 400
|
||||
mock_response.json.return_value = {}
|
||||
mock_requests.get.return_value = mock_response
|
||||
|
||||
result = self.eagle_eye.get_current_user()
|
||||
|
||||
self.assertFalse(result['success'])
|
||||
self.assertEqual(result['response_http_status'], 400)
|
||||
self.assertEqual(result['data'], {})
|
||||
|
||||
# Write similar tests for the remaining methods
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
Loading…
Reference in New Issue