skills/__main__.py
Go to the documentation of this file.
1 # Copyright 2017 Mycroft AI Inc.
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 # http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14 #
15 import time
16 from threading import Timer
17 import mycroft.lock
18 from mycroft import dialog
19 from mycroft.api import is_paired, BackendDown
20 from mycroft.enclosure.api import EnclosureAPI
21 from mycroft.configuration import Configuration
22 from mycroft.messagebus.client.ws import WebsocketClient
23 from mycroft.messagebus.message import Message
24 from mycroft.util import (
25  connected, wait_while_speaking, reset_sigint_handler,
26  create_echo_function, create_daemon, wait_for_exit_signal
27 )
28 from mycroft.util.log import LOG
29 from mycroft.util.lang import set_active_lang
30 
31 from .skill_manager import SkillManager, MsmException
32 from .core import FallbackSkill
33 from .event_scheduler import EventScheduler
34 from .intent_service import IntentService
35 from .padatious_service import PadatiousService
36 
37 bus = None # Mycroft messagebus reference, see "mycroft.messagebus"
38 event_scheduler = None
39 skill_manager = None
40 
41 # Remember "now" at startup. Used to detect clock changes.
42 start_ticks = time.monotonic()
43 start_clock = time.time()
44 
45 
46 def connect():
47  global bus
48  bus.run_forever()
49 
50 
52  """
53  Start loading skills.
54 
55  Starts
56  - SkillManager to load/reloading of skills when needed
57  - a timer to check for internet connection
58  - adapt intent service
59  - padatious intent service
60  """
61  global bus, skill_manager, event_scheduler
62 
63  bus.on('intent_failure', FallbackSkill.make_intent_failure_handler(bus))
64 
65  # Create the Intent manager, which converts utterances to intents
66  # This is the heart of the voice invoked skill system
67  service = IntentService(bus)
68  try:
69  PadatiousService(bus, service)
70  except Exception as e:
71  LOG.exception('Failed to create padatious handlers '
72  '({})'.format(repr(e)))
73  event_scheduler = EventScheduler(bus)
74 
75  # Create a thread that monitors the loaded skills, looking for updates
76  try:
77  skill_manager = SkillManager(bus)
78  except MsmException:
79  # skill manager couldn't be created, wait for network connection and
80  # retry
81  LOG.info('Msm is uninitialized and requires network connection',
82  'to fetch skill information\n'
83  'Waiting for network connection...')
84  while not connected():
85  time.sleep(30)
86  skill_manager = SkillManager(bus)
87 
88  skill_manager.daemon = True
89  # Wait until priority skills have been loaded before checking
90  # network connection
91  skill_manager.load_priority()
92  skill_manager.start()
94 
95 
96 def try_update_system(platform):
97  bus.emit(Message('system.update'))
98  msg = Message('system.update', {
99  'paired': is_paired(),
100  'platform': platform
101  })
102  resp = bus.wait_for_response(msg, 'system.update.processing')
103 
104  if resp and (resp.data or {}).get('processing', True):
105  bus.wait_for_response(Message('system.update.waiting'),
106  'system.update.complete', 1000)
107 
108 
110  """
111  Check for network connection. If not paired trigger pairing.
112  Runs as a Timer every second until connection is detected.
113  """
114  if connected():
115  enclosure = EnclosureAPI(bus)
116 
117  if is_paired():
118  # Skip the sync message when unpaired because the prompt to go to
119  # home.mycrof.ai will be displayed by the pairing skill
120  enclosure.mouth_text(dialog.get("message_synching.clock"))
121 
122  # Force a sync of the local clock with the internet
123  config = Configuration.get()
124  platform = config['enclosure'].get("platform", "unknown")
125  if platform in ['mycroft_mark_1', 'picroft']:
126  bus.wait_for_response(Message('system.ntp.sync'),
127  'system.ntp.sync.complete', 15)
128 
129  if not is_paired():
130  try_update_system(platform)
131 
132  # Check if the time skewed significantly. If so, reboot
133  skew = abs((time.monotonic() - start_ticks) -
134  (time.time() - start_clock))
135  if skew > 60 * 60:
136  # Time moved by over an hour in the NTP sync. Force a reboot to
137  # prevent weird things from occcurring due to the 'time warp'.
138  #
139  data = {'utterance': dialog.get("time.changed.reboot")}
140  bus.emit(Message("speak", data))
142 
143  # provide visual indicators of the reboot
144  enclosure.mouth_text(dialog.get("message_rebooting"))
145  enclosure.eyes_color(70, 65, 69) # soft gray
146  enclosure.eyes_spin()
147 
148  # give the system time to finish processing enclosure messages
149  time.sleep(1.0)
150 
151  # reboot
152  bus.emit(Message("system.reboot"))
153  return
154  else:
155  bus.emit(Message("enclosure.mouth.reset"))
156  time.sleep(0.5)
157 
158  enclosure.eyes_color(189, 183, 107) # dark khaki
159  enclosure.mouth_text(dialog.get("message_loading.skills"))
160 
161  bus.emit(Message('mycroft.internet.connected'))
162  # check for pairing, if not automatically start pairing
163  try:
164  if not is_paired(ignore_errors=False):
165  payload = {
166  'utterances': ["pair my device"],
167  'lang': "en-us"
168  }
169  bus.emit(Message("recognizer_loop:utterance", payload))
170  else:
171  from mycroft.api import DeviceApi
172  api = DeviceApi()
173  api.update_version()
174  except BackendDown:
175  data = {'utterance': dialog.get("backend.down")}
176  bus.emit(Message("speak", data))
177  bus.emit(Message("backend.down"))
178 
179  else:
180  thread = Timer(1, check_connection)
181  thread.daemon = True
182  thread.start()
183 
184 
185 def main():
186  global bus
188  # Create PID file, prevent multiple instancesof this service
189  mycroft.lock.Lock('skills')
190  # Connect this Skill management process to the Mycroft Messagebus
191  bus = WebsocketClient()
192  Configuration.init(bus)
193  config = Configuration.get()
194  # Set the active lang to match the configured one
195  set_active_lang(config.get('lang', 'en-us'))
196 
197  bus.on('message', create_echo_function('SKILLS'))
198  # Startup will be called after the connection with the Messagebus is done
199  bus.once('open', _starting_up)
200 
201  create_daemon(bus.run_forever)
203  shutdown()
204 
205 
206 def shutdown():
207  if event_scheduler:
208  event_scheduler.shutdown()
209 
210  # Terminate all running threads that update skills
211  if skill_manager:
212  skill_manager.stop()
213  skill_manager.join()
214 
215 
216 if __name__ == "__main__":
217  main()
def create_daemon(target, args=(), kwargs=None)
def is_paired(ignore_errors=True)
def try_update_system(platform)
def create_echo_function(name, whitelist=None)
def get(phrase, lang=None, context=None)


mycroft_ros
Author(s):
autogenerated on Mon Apr 26 2021 02:35:40