Files
iot-logic/src/sender/soundsender.py
2021-12-12 19:07:25 +01:00

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()