2017-04-13 05:26:45 +00:00
|
|
|
# Copyright 2017 Mycroft AI, Inc.
|
|
|
|
#
|
|
|
|
# This file is part of Mycroft Core.
|
|
|
|
#
|
|
|
|
# Mycroft Core is free software: you can redistribute it and/or modify
|
|
|
|
# it under the terms of the GNU General Public License as published by
|
|
|
|
# the Free Software Foundation, either version 3 of the License, or
|
|
|
|
# (at your option) any later version.
|
|
|
|
#
|
|
|
|
# Mycroft Core is distributed in the hope that it will be useful,
|
|
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
# GNU General Public License for more details.
|
|
|
|
#
|
|
|
|
# You should have received a copy of the GNU General Public License
|
|
|
|
# along with Mycroft Core. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
2017-04-24 10:09:23 +00:00
|
|
|
"""
|
2017-04-28 19:51:12 +00:00
|
|
|
This module provides the SkillSettings dictionary, which is a simple
|
2017-04-24 10:09:23 +00:00
|
|
|
extension of the python dict to enable storing.
|
|
|
|
|
|
|
|
Example:
|
|
|
|
from mycroft.skill.settings import SkillSettings
|
|
|
|
|
|
|
|
s = SkillSettings('./settings.json')
|
|
|
|
s['meaning of life'] = 42
|
|
|
|
s['flower pot sayings'] = 'Not again...'
|
|
|
|
s.store()
|
|
|
|
"""
|
|
|
|
|
2017-04-13 05:26:45 +00:00
|
|
|
import json
|
|
|
|
import sys
|
2017-06-28 22:31:35 +00:00
|
|
|
from os.path import isfile, join, exists, expanduser
|
2017-06-28 16:32:19 +00:00
|
|
|
from mycroft.util.log import getLogger
|
|
|
|
from mycroft.api import DeviceApi
|
|
|
|
|
|
|
|
logger = getLogger(__name__)
|
|
|
|
SKILLS_DIR = "/opt/mycroft/skills"
|
2017-04-13 05:26:45 +00:00
|
|
|
|
2017-04-24 10:08:47 +00:00
|
|
|
|
2017-04-13 05:26:45 +00:00
|
|
|
class SkillSettings(dict):
|
2017-04-24 10:09:23 +00:00
|
|
|
"""
|
|
|
|
SkillSettings creates a dictionary that can easily be stored
|
2017-06-28 22:31:35 +00:00
|
|
|
to file, serialized as json. It also syncs to the backend for
|
|
|
|
skill configuration
|
2017-04-24 10:09:23 +00:00
|
|
|
|
|
|
|
Args:
|
|
|
|
settings_file (str): Path to storage file
|
|
|
|
"""
|
2017-06-28 16:32:19 +00:00
|
|
|
def __init__(self, directory, name):
|
2017-04-13 05:26:45 +00:00
|
|
|
super(SkillSettings, self).__init__()
|
2017-06-28 16:32:19 +00:00
|
|
|
self.api = DeviceApi()
|
|
|
|
self.name = name
|
|
|
|
self._device_identity = self.api.identity.uuid
|
|
|
|
self._settings_path = join(directory, 'settings.json')
|
|
|
|
self._meta_path = join(directory, 'settingsmeta.json')
|
2017-06-28 22:31:35 +00:00
|
|
|
self._uuid_path = join(expanduser('~/.mycroft/skills/') + self.name,
|
2017-06-28 16:32:19 +00:00
|
|
|
'identity.json')
|
|
|
|
self._api_path = "/" + self._device_identity + "/skill"
|
2017-06-22 06:36:33 +00:00
|
|
|
self.loaded_hash = hash(str(self))
|
|
|
|
|
2017-06-28 22:31:35 +00:00
|
|
|
self._send_settings_meta()
|
|
|
|
self._request_settings()
|
|
|
|
self._load_settings()
|
|
|
|
|
2017-06-22 06:36:33 +00:00
|
|
|
@property
|
|
|
|
def _is_stored(self):
|
|
|
|
return hash(str(self)) == self.loaded_hash
|
2017-04-24 10:08:47 +00:00
|
|
|
|
2017-04-13 05:26:45 +00:00
|
|
|
def __getitem__(self, key):
|
|
|
|
return super(SkillSettings, self).__getitem__(key)
|
|
|
|
|
|
|
|
def __setitem__(self, key, value):
|
2017-04-24 10:09:23 +00:00
|
|
|
"""
|
|
|
|
Add/Update key and note that the file needs saving.
|
|
|
|
"""
|
2017-04-13 05:26:45 +00:00
|
|
|
return super(SkillSettings, self).__setitem__(key, value)
|
|
|
|
|
2017-06-28 22:31:35 +00:00
|
|
|
def _send_settings_meta(self):
|
2017-04-24 10:09:23 +00:00
|
|
|
"""
|
2017-06-28 22:31:35 +00:00
|
|
|
If settings meta data exists and the skill is loaded for the
|
|
|
|
first time, send settingsmeta.json to the backend and store uuid
|
|
|
|
for skill
|
2017-04-24 10:09:23 +00:00
|
|
|
"""
|
2017-06-28 22:31:35 +00:00
|
|
|
if isfile(self._meta_path):
|
|
|
|
with open(self._meta_path) as f:
|
|
|
|
self.settings_meta = json.load(f)
|
|
|
|
|
|
|
|
# If skill is loaded for the first time post metadata
|
|
|
|
# to backend and store uuid for skill
|
|
|
|
if not isfile(self._uuid_path):
|
|
|
|
response = self._post_metadata(self.settings_meta)
|
|
|
|
self._store_uuid(response)
|
|
|
|
|
|
|
|
def _request_settings(self):
|
|
|
|
"""
|
|
|
|
Poll to backend to request settings and store it if it changes
|
|
|
|
TODO: implement as websocket
|
|
|
|
|
|
|
|
"""
|
|
|
|
# TODO: A get request for specific skill uuid instead of getting all
|
|
|
|
if isfile(self._uuid_path):
|
|
|
|
with open(self._uuid_path, 'r') as f:
|
|
|
|
data = json.load(f)
|
|
|
|
|
|
|
|
response = self._get_settings()
|
|
|
|
|
|
|
|
for skill_setting in response:
|
|
|
|
if skill_setting['uuid'] == data['uuid']:
|
|
|
|
settings_list = skill_setting['skillMetadata']['sections']
|
|
|
|
for section in settings_list:
|
|
|
|
for field in section["fields"]:
|
|
|
|
pass
|
|
|
|
|
|
|
|
def _load_settings(self):
|
|
|
|
"""
|
|
|
|
If settings.json exist, open and read stored values into self
|
|
|
|
"""
|
|
|
|
if isfile(self._settings_path):
|
|
|
|
with open(self._settings_path) as f:
|
|
|
|
try:
|
|
|
|
json_data = json.load(f)
|
|
|
|
for key in json_data:
|
|
|
|
self.__setitem__(key, json_data[key])
|
|
|
|
except Exception as e:
|
|
|
|
# TODO: Show error on webUI. Dev will have to fix
|
|
|
|
# metadata to be able to edit later.
|
|
|
|
logger.error(e)
|
2017-06-28 16:32:19 +00:00
|
|
|
|
|
|
|
def _store_uuid(self, uuid):
|
2017-06-28 22:31:35 +00:00
|
|
|
"""
|
|
|
|
Store uuid as identity.json in ~/.mycroft/skills/{skillname}
|
|
|
|
"""
|
2017-06-28 16:32:19 +00:00
|
|
|
with open(self._uuid_path, 'w') as f:
|
|
|
|
json.dump(uuid, f)
|
|
|
|
|
|
|
|
def _get_settings(self):
|
2017-06-28 22:31:35 +00:00
|
|
|
"""
|
|
|
|
Goes to backend to get skill configurations for this device
|
|
|
|
"""
|
2017-06-28 16:32:19 +00:00
|
|
|
return self.api.request({
|
|
|
|
"method": "GET",
|
|
|
|
"path": self._api_path
|
|
|
|
})
|
|
|
|
|
|
|
|
def _post_metadata(self, settings_meta):
|
2017-06-28 22:31:35 +00:00
|
|
|
"""
|
|
|
|
POST settingsmeta to backend to be configured in home.mycroft.ai
|
|
|
|
"""
|
2017-06-28 16:32:19 +00:00
|
|
|
return self.api.request({
|
|
|
|
"method": "POST",
|
|
|
|
"path": self._api_path,
|
|
|
|
"json": settings_meta
|
|
|
|
})
|
|
|
|
|
2017-06-28 22:31:35 +00:00
|
|
|
def store(self):
|
|
|
|
"""
|
|
|
|
Store dictionary to file if it has changed
|
|
|
|
"""
|
|
|
|
if not self._is_stored:
|
|
|
|
with open(self._settings_path, 'w') as f:
|
|
|
|
json.dump(self, f)
|
|
|
|
self.loaded_hash = hash(str(self))
|
|
|
|
|
|
|
|
def test(self):
|
|
|
|
with open(self._uuid_path, 'r') as f:
|
|
|
|
data = json.load(f)
|
|
|
|
return data
|
|
|
|
|
2017-06-28 16:32:19 +00:00
|
|
|
# account for settings changes through skill ie. change default playlist
|
|
|
|
|
|
|
|
# account for finding a place to store uuid
|