Skip to content

Commit

Permalink
Merge pull request #2 from OpenVoiceOS/add/feat/ovos_backend_api
Browse files Browse the repository at this point in the history
add support for ovos api service and caching
  • Loading branch information
AIIX committed Feb 18, 2022
2 parents 76866b7 + fab8529 commit 9748286
Show file tree
Hide file tree
Showing 5 changed files with 143 additions and 7 deletions.
9 changes: 6 additions & 3 deletions __init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,14 +60,17 @@ class WeatherSkill(MycroftSkill):

def __init__(self):
super().__init__("WeatherSkill")
self.weather_api = OpenWeatherMapApi()
self.weather_api.set_language_parameter(self.lang)
self.platform = self.config_core["enclosure"].get("platform", "unknown")
self.weather_api = None
self.platform = None
self.weather_config = None

def initialize(self):
"""Do these things after the skill is loaded."""
self.weather_api = OpenWeatherMapApi()
self.weather_api.set_language_parameter(self.lang)
self.platform = self.config_core["enclosure"].get("platform", "unknown")
self.weather_config = WeatherConfig(self.config_core, self.settings)


@intent_handler(
AdaptIntent()
Expand Down
1 change: 1 addition & 0 deletions skill/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,4 @@
from .intent import WeatherIntent
from .weather import CURRENT, DAILY, DailyWeather, HOURLY, WeatherReport
from .util import LocationNotFoundError
from .ovosapiservice import OVOSApiService
90 changes: 87 additions & 3 deletions skill/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,14 @@
provided, precluding us from having to do the conversions.
"""
import os
from mycroft.api import Api
from .weather import WeatherReport
from .ovosapiservice import OVOSApiService
from json_database import JsonStorageXDG
import threading
import datetime as dt
import time

OPEN_WEATHER_MAP_LANGUAGES = (
"af",
Expand Down Expand Up @@ -83,6 +89,9 @@ class OpenWeatherMapApi(Api):
def __init__(self):
super().__init__(path="owm")
self.language = "en"
self.localbackend = OVOSApiService()
self.cache_response_location = JsonStorageXDG(
"skill-weather-response-cache")

def get_weather_for_coordinates(
self, measurement_system: str, latitude: float, longitude: float
Expand All @@ -101,12 +110,87 @@ def get_weather_for_coordinates(
lon=longitude,
units=measurement_system
)
api_request = dict(path="/onecall", query=query_parameters)
response = self.request(api_request)
local_weather = WeatherReport(response)
self.clear_cache_timer()
if self.check_if_cached_weather_exist(latitude, longitude):
cache_time = self.get_cached_weather_results(
latitude, longitude)[0]
response = self.get_cached_weather_results(latitude, longitude)[1]

# add additional check for if the time is more than 15 minutes old and if so, refresh the cache
if dt.datetime.now() - dt.datetime.fromtimestamp(cache_time) > dt.timedelta(minutes=15):
try:
api_request = dict(path="/onecall", query=query_parameters)
response = self.request(api_request)
weather_cache_response = {'time': time.mktime(dt.datetime.now(
).timetuple()), 'lat': latitude, 'lon': longitude, 'response': response}
self.cache_weather_results(weather_cache_response)
local_weather = WeatherReport(response)
except:
response = self.localbackend.get_report_for_weather_onecall_type(
query=query_parameters)
weather_cache_response = {'time': time.mktime(dt.datetime.now(
).timetuple()), 'lat': latitude, 'lon': longitude, 'response': response}
self.cache_weather_results(weather_cache_response)
local_weather = WeatherReport(response)
else:
local_weather = WeatherReport(response)
else:
try:
api_request = dict(path="/onecall", query=query_parameters)
response = self.request(api_request)
weather_cache_response = {'time': time.mktime(dt.datetime.now(
).timetuple()), 'lat': latitude, 'lon': longitude, 'response': response}
self.cache_weather_results(weather_cache_response)
local_weather = WeatherReport(response)
except:
response = self.localbackend.get_report_for_weather_onecall_type(
query=query_parameters)
weather_cache_response = {'time': time.mktime(dt.datetime.now(
).timetuple()), 'lat': latitude, 'lon': longitude, 'response': response}
self.cache_weather_results(weather_cache_response)
local_weather = WeatherReport(response)

return local_weather

def cache_weather_results(self, weather_response):
cache_response = {'time': weather_response["time"], 'lat': weather_response["lat"],
'lon': weather_response["lon"], 'response': weather_response["response"]}
if "caches" in self.cache_response_location:
cache_responses = self.cache_response_location["caches"]
else:
cache_responses = []

if cache_response not in cache_responses:
cache_responses.append(cache_response)

self.cache_response_location["caches"] = cache_responses
self.cache_response_location.store()

def check_if_cached_weather_exist(self, latitude, longitude):
if "caches" in self.cache_response_location:
cache_responses = self.cache_response_location["caches"]
for cache_response in cache_responses:
if cache_response["lat"] == latitude and cache_response["lon"] == longitude:
return True
else:
return False
else:
return False

def get_cached_weather_results(self, latitude, longitude):
if "caches" in self.cache_response_location:
cache_responses = self.cache_response_location["caches"]
for cache_response in cache_responses:
if cache_response["lat"] == latitude and cache_response["lon"] == longitude:
return [cache_response['time'], cache_response["response"]]

def clear_cache_timer(self):
if "caches" in self.cache_response_location:
threading.Timer(900, self.clear_cache).start()

def clear_cache(self):
os.remove(self.cache_response_location.path)

def set_language_parameter(self, language_config: str):
"""
OWM supports 31 languages, see https://openweathermap.org/current#multi
Expand Down
48 changes: 48 additions & 0 deletions skill/ovosapiservice.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import json
import requests
from json_database import JsonStorageXDG


class OVOSApiService:

def __init__(self) -> None:
self.uuid_storage = JsonStorageXDG("ovos_api_uuid")
self.token_storage = JsonStorageXDG("ovos_api_token")

def register_device(self):
if self.check_if_uuid_exists():
return
else:
created_challenge = requests.get('https://api.openvoiceos.com/create_challenge')
challenge_response = created_challenge.json()
register_device = requests.get('https://api.openvoiceos.com/register_device/' + challenge_response['challenge'] + '/' + challenge_response['secret'])
register_device_uuid = challenge_response['challenge']
self.uuid_storage['uuid'] = register_device_uuid
self.uuid_storage.store()

def check_if_uuid_exists(self):
if "uuid" in self.uuid_storage:
return True
return False

def get_session_challenge(self):
session_challenge_request = requests.get('https://api.openvoiceos.com/get_session_challenge')
session_challenge_response = session_challenge_request.json()
self.token_storage["challenge"] = session_challenge_response['challenge']
self.token_storage.store()

def get_uuid(self):
return self.uuid_storage.get("uuid", "")

def get_session_token(self):
return self.token_storage.get("challenge", "")

def get_report_for_weather_onecall_type(self, query: dict):
self.register_device()
self.get_session_challenge()

headers = {'session_challenge': self.get_session_token(), 'backend': 'OWM'}
reqdata = {"lat": query.get("lat"), "lon": query.get("lon"), "units": query.get("units"), "lang": query.get("lang")}
onecall_weather_request = requests.post('https://api.openvoiceos.com/weather/onecall_weather_report/' + self.get_uuid(), data=reqdata, headers=headers)
onecall_weather_response = onecall_weather_request.text
return json.loads(onecall_weather_response)
2 changes: 1 addition & 1 deletion skill/weather.py
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,7 @@ def __init__(self, alert: dict, timezone: str):
class WeatherReport:
"""Full representation of the data returned by the Open Weather Maps One Call API"""

def __init__(self, report):
def __init__(self, report):
timezone = report["timezone"]
self.current = CurrentWeather(report["current"], timezone)
self.hourly = [HourlyWeather(hour, timezone) for hour in report["hourly"]]
Expand Down

0 comments on commit 9748286

Please sign in to comment.