143 lines
4.8 KiB
Python
143 lines
4.8 KiB
Python
#!/usr/bin/env python3
|
|
import os
|
|
import json
|
|
import logging
|
|
from datetime import datetime
|
|
import requests
|
|
from .abcsender import AbcSender
|
|
from utils import config, LoopingTimer
|
|
from urllib.parse import urljoin
|
|
import time
|
|
from threading import Lock
|
|
|
|
"""
|
|
Send a sound sample
|
|
"""
|
|
|
|
__author__ = "@tormakris"
|
|
__copyright__ = "Copyright 2020, Birbnetes Team"
|
|
__module_name__ = "soundsender"
|
|
__version__text__ = "1"
|
|
|
|
|
|
class SoundSender(AbcSender):
|
|
"""
|
|
SoundSender class, responsible for sending sound samples to the cloud.
|
|
"""
|
|
|
|
super_global_multi_sender_preventer = Lock()
|
|
|
|
def __init__(self):
|
|
|
|
self._feed_url = None
|
|
self._reassign_timer = None
|
|
if not config.NO_AUTODISCOVER:
|
|
retry = 0
|
|
timeout = 30
|
|
while True:
|
|
try:
|
|
r = requests.post(
|
|
urljoin(config.API_URL, "/assignment"),
|
|
json={"device_id": config.DEVICE_ID},
|
|
timeout=timeout
|
|
)
|
|
except requests.exceptions.Timeout:
|
|
logging.warning(f"/assignment timed out after {timeout} sec! Retrying...")
|
|
continue
|
|
|
|
if r.status_code not in [200, 404]:
|
|
logging.warning(
|
|
f"Unexpected status code while acquiring an assignment: {r.status_code}. Retrying..."
|
|
)
|
|
time.sleep(5)
|
|
continue
|
|
|
|
if r.status_code == 404:
|
|
logging.info(
|
|
"/assignment returned 404 error. Assuming missing capability... Defaulting to use API_URL"
|
|
)
|
|
self._feed_url = config.API_URL
|
|
break
|
|
|
|
if r.json()['hard_default']:
|
|
retry += 1
|
|
if retry < 10:
|
|
logging.info(f"Still waiting for non hard-default url... Attempt {retry}/10")
|
|
time.sleep(6)
|
|
continue
|
|
else:
|
|
logging.info(f"Given up waiting for non hard-default url... accepting hard default.")
|
|
|
|
logging.info(f"Assigned to {r.json()['site']}")
|
|
self._feed_url = r.json()['url']
|
|
break
|
|
|
|
self._reassign_timer = LoopingTimer(config.REASSIGN_INTERVAL, lambda: self.reassign())
|
|
self._reassign_timer.start()
|
|
|
|
# If the above block failed to assign a feed_url
|
|
if not self._feed_url:
|
|
logging.info("Using API_URL as feed url")
|
|
self._feed_url = config.API_URL
|
|
|
|
def reassign(self):
|
|
logging.debug("Asking for reassign...")
|
|
timeout = 30
|
|
try:
|
|
r = requests.post(
|
|
urljoin(config.API_URL, "/assignment"),
|
|
json={"device_id": config.DEVICE_ID},
|
|
timeout=timeout
|
|
)
|
|
except requests.exceptions.Timeout:
|
|
logging.warning(f"/assignment timed out after {timeout} sec! Ignoring...")
|
|
return
|
|
|
|
if r.status_code == 404:
|
|
logging.debug("/assignment returned 404 status code. Assuming missing capability, and doing nothing...")
|
|
return
|
|
|
|
if r.status_code != 200:
|
|
logging.debug(f"/assignment returned {r.status_code} status code. Ignoring...")
|
|
return
|
|
|
|
logging.debug(f"Received assignment to {r.json()['site']}")
|
|
|
|
if r.json()['url'] != self._feed_url:
|
|
logging.info(f"Reassigned to {r.json()['site']}!")
|
|
# Because of GIL and because this is a reference, we don't really have to be careful assigning this
|
|
self._feed_url = r.json()['url']
|
|
|
|
def sendvalue(self, value: str, decision: bool) -> None:
|
|
"""
|
|
Send a sound sample to the cloud.
|
|
value: is the file name
|
|
decision: if true the sample is sent, if false then not
|
|
:return:
|
|
"""
|
|
if decision:
|
|
files = {
|
|
"file": (
|
|
os.path.basename(value),
|
|
open(value, 'rb').read(),
|
|
'audio/wave',
|
|
{'Content-length': os.path.getsize(value)}
|
|
),
|
|
"description": (
|
|
None,
|
|
json.dumps({'date': datetime.now().isoformat(), 'device_id': config.DEVICE_ID}),
|
|
"application/json"
|
|
)
|
|
}
|
|
|
|
with SoundSender.super_global_multi_sender_preventer:
|
|
r = requests.post(urljoin(self._feed_url, config.FEED_TYPE), files=files)
|
|
|
|
logging.debug(f"Content: {r.content.decode()}")
|
|
logging.debug(f"Headers: {r.headers}")
|
|
r.raise_for_status()
|
|
|
|
def __del__(self):
|
|
if self._reassign_timer:
|
|
self._reassign_timer.stop()
|