Initial commit
commit
8c15977850
@ -0,0 +1,181 @@
|
|||||||
|
"""spectur_live_view
|
||||||
|
|
||||||
|
Open an automated selenium webdriver, and download images from Live View mode.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import time
|
||||||
|
import pytz
|
||||||
|
from io import BytesIO
|
||||||
|
from datetime import datetime
|
||||||
|
import requests
|
||||||
|
import numpy as np
|
||||||
|
from PIL import Image
|
||||||
|
from selenium import webdriver
|
||||||
|
from selenium.webdriver.firefox.options import Options
|
||||||
|
from selenium.common.exceptions import (NoSuchElementException,
|
||||||
|
StaleElementReferenceException,
|
||||||
|
WebDriverException)
|
||||||
|
|
||||||
|
CAM_ID = 1
|
||||||
|
USERNAME = ''
|
||||||
|
PASSWORD = ''
|
||||||
|
URL = 'https://camera.uwatchit.com.au/UWatchitWebV2/User/LiveView#'
|
||||||
|
OUTPUT_DIR = 'images'
|
||||||
|
TIMEZONE = 'Australia/Sydney'
|
||||||
|
WAIT_TIME = 0.1
|
||||||
|
|
||||||
|
|
||||||
|
def start_session():
|
||||||
|
"""Start automated browser session.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
selenium webdriver object
|
||||||
|
"""
|
||||||
|
# Start webdriver
|
||||||
|
options = Options()
|
||||||
|
options.headless = False
|
||||||
|
driver = webdriver.Firefox(options=options)
|
||||||
|
|
||||||
|
return driver
|
||||||
|
|
||||||
|
|
||||||
|
def login(driver):
|
||||||
|
"""Log into Spectur camera portal.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
driver: selenium webdriver object
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
cookies from current browser session
|
||||||
|
"""
|
||||||
|
|
||||||
|
driver.get(URL)
|
||||||
|
|
||||||
|
# Get login fields
|
||||||
|
usr = driver.find_element_by_id('UserName')
|
||||||
|
pss = driver.find_element_by_id('Password')
|
||||||
|
btn = driver.find_element_by_id('submitlogin')
|
||||||
|
|
||||||
|
# Input login details
|
||||||
|
usr.send_keys(USERNAME)
|
||||||
|
pss.send_keys(PASSWORD)
|
||||||
|
btn.click()
|
||||||
|
|
||||||
|
# Extract cookies
|
||||||
|
session = requests.Session()
|
||||||
|
for c in driver.get_cookies():
|
||||||
|
session.cookies.set(c['name'], c['value'])
|
||||||
|
|
||||||
|
return session.cookies
|
||||||
|
|
||||||
|
|
||||||
|
def return_to_live_view(driver):
|
||||||
|
"""Return to live camera feed in Spectur portal.
|
||||||
|
"""
|
||||||
|
|
||||||
|
driver.get(URL)
|
||||||
|
driver.execute_script('ChangeCam({})'.format(CAM_ID))
|
||||||
|
|
||||||
|
|
||||||
|
def get_image_timestamp(driver):
|
||||||
|
"""Get timestamp from image link.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
driver: selenium webdriver object
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
UNIX timestamp, in seconds
|
||||||
|
"""
|
||||||
|
|
||||||
|
im_url = driver.find_element_by_id('camimage').get_property('src')
|
||||||
|
timestamp = int(im_url.rsplit('&', 1)[-1]) / 1000
|
||||||
|
|
||||||
|
return timestamp
|
||||||
|
|
||||||
|
|
||||||
|
def get_next_image(driver, cookies):
|
||||||
|
"""Get next image.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
driver: selenium webdriver object
|
||||||
|
cookies: cookies from current browser session
|
||||||
|
"""
|
||||||
|
# Get timestamp
|
||||||
|
t_current = get_image_timestamp(driver)
|
||||||
|
|
||||||
|
# Wait until image timestamp is updated
|
||||||
|
while t_current == get_image_timestamp(driver):
|
||||||
|
time.sleep(WAIT_TIME)
|
||||||
|
|
||||||
|
# Get image URL
|
||||||
|
im_url = driver.find_element_by_id('camimage').get_property('src')
|
||||||
|
|
||||||
|
# Get jpeg data, and convert to image object
|
||||||
|
page = requests.get(im_url, cookies=cookies)
|
||||||
|
im = Image.open(BytesIO(page.content))
|
||||||
|
|
||||||
|
# Get EXIF timestamp
|
||||||
|
datestr = im._getexif()[36867]
|
||||||
|
|
||||||
|
# Convert to local time
|
||||||
|
t_naive = datetime.strptime(datestr, '%Y:%m:%d %H:%M:%S')
|
||||||
|
t_utc = t_naive.replace(tzinfo=pytz.utc)
|
||||||
|
t_local = t_utc.astimezone(pytz.timezone(TIMEZONE))
|
||||||
|
|
||||||
|
# Save image
|
||||||
|
ts = t_local.strftime
|
||||||
|
jpg_name = os.path.join(OUTPUT_DIR, ts('%Y'), ts('%Y-%m-%d'), ts('%H'),
|
||||||
|
ts('%Y-%m-%d_%H-%M-%S%z') + '.jpg')
|
||||||
|
os.makedirs(os.path.dirname(jpg_name), exist_ok=True)
|
||||||
|
im.save(jpg_name)
|
||||||
|
|
||||||
|
# Get battery level
|
||||||
|
with open(os.path.join(OUTPUT_DIR, 'battery.txt'), 'a') as f:
|
||||||
|
battery_str = driver.find_element_by_id('battery').text
|
||||||
|
f.write('{}\t{}\n'.format(t_local.isoformat(), battery_str[:-1]))
|
||||||
|
|
||||||
|
# Update counter
|
||||||
|
t.append(time.mktime(t_local.timetuple()))
|
||||||
|
|
||||||
|
# Count images collected
|
||||||
|
n = len(t)
|
||||||
|
|
||||||
|
if n > 1:
|
||||||
|
# Get frames per second
|
||||||
|
fps = 1 / np.median(np.diff(t))
|
||||||
|
|
||||||
|
# Count dropped frames
|
||||||
|
n_dropped = np.max([0, int(fps * (t[-1] - t[0]) - n + 1)])
|
||||||
|
|
||||||
|
print('{} total images: {} fps: {} dropped frames: {}'.format(
|
||||||
|
t_local.isoformat(), n, fps, n_dropped),
|
||||||
|
end='\r',
|
||||||
|
flush=True)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
# Start browser session
|
||||||
|
driver = start_session()
|
||||||
|
cookies = login(driver)
|
||||||
|
return_to_live_view(driver)
|
||||||
|
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
# Get latest image
|
||||||
|
get_next_image(driver, cookies)
|
||||||
|
except (NoSuchElementException, StaleElementReferenceException,
|
||||||
|
WebDriverException):
|
||||||
|
# Navigate to Live View
|
||||||
|
return_to_live_view(driver)
|
||||||
|
except ConnectionError:
|
||||||
|
# Attempt to re-login if server goes down
|
||||||
|
time.sleep(5)
|
||||||
|
cookies = login(driver)
|
||||||
|
|
||||||
|
|
||||||
|
# Create counter
|
||||||
|
t = []
|
||||||
|
|
||||||
|
# Open browser
|
||||||
|
main()
|
Loading…
Reference in New Issue