Added server assisted roaming capability

This commit is contained in:
Pünkösd Marcell 2021-12-11 01:07:04 +01:00
parent e609411897
commit 46503b5d45
2 changed files with 104 additions and 7 deletions

View File

@ -5,8 +5,9 @@ import logging
from datetime import datetime
import requests
from .abcsender import AbcSender
from utils import config
from utils import config, LoopingTimer
from urllib.parse import urljoin
import time
"""
Send a sound sample
@ -23,6 +24,87 @@ class SoundSender(AbcSender):
SoundSender class, responsible for sending sound samples to the cloud.
"""
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.
@ -32,14 +114,24 @@ class SoundSender(AbcSender):
"""
if decision:
files = {
"file": (os.path.basename(value), open(value, 'rb').read(), 'audio/wave',
{'Content-length': os.path.getsize(value)}),
"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")
None,
json.dumps({'date': datetime.now().isoformat(), 'device_id': config.DEVICE_ID}),
"application/json"
)
}
r = requests.post(urljoin(config.API_URL, config.FEED_TYPE), files=files)
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()

View File

@ -37,7 +37,12 @@ DISABLE_AI = os.environ.get("DISABLE_AI", 'no').lower() in ['yes', '1', 'true']
PLATFORM = os.environ.get("PLATFORM", "raspberry")
FEED_TYPE = os.environ.get("FEED_TYPE", "input") # probably the worst naming the type of the accepting service.
FEED_TYPE = os.environ.get("FEED_TYPE", "input") # probably the worst naming the type of the accepting service.
if FEED_TYPE not in ['input', 'filter']:
raise ValueError("FEED_TYPE must be either input or filter")
# Skip fetching /assignment endpoint
NO_AUTODISCOVER = os.environ.get("NO_AUTODISCOVER", 'no').lower() in ['yes', '1', 'true']
REASSIGN_INTERVAL = int(os.environ.get("REASSIGN_INTERVAL", 120))