Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add support for ovos api service and caching #2

Merged
merged 6 commits into from
Feb 18, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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