File indexing completed on 2024-12-01 08:02:41

0001 # Copyright 2020 Aditya Mehra (aix.m@outlook.com).
0002 #
0003 # Licensed under the Apache License, Version 2.0 (the "License");
0004 # you may not use this file except in compliance with the License.
0005 # You may obtain a copy of the License at
0006 #
0007 #    http://www.apache.org/licenses/LICENSE-2.0
0008 #
0009 # Unless required by applicable law or agreed to in writing, software
0010 # distributed under the License is distributed on an "AS IS" BASIS,
0011 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0012 # See the License for the specific language governing permissions and
0013 # limitations under the License.
0014 
0015 import astral
0016 import time
0017 import arrow
0018 import json
0019 from pytz import timezone
0020 from datetime import datetime
0021 
0022 from mycroft.messagebus.message import Message
0023 from mycroft.skills.core import MycroftSkill
0024 from mycroft.util.log import LOG
0025 from mycroft.util.parse import normalize
0026 from mycroft import intent_file_handler
0027 
0028 from threading import Thread, Lock
0029 
0030 
0031 class BigscreenPlatform(MycroftSkill):
0032     """
0033         The BigscreenPlatform skill handles much of the gui activities related to
0034         Skill Pages timeout functionality.
0035     """
0036 
0037     def __init__(self):
0038         super().__init__('BigscreenPlatform')
0039 
0040         self.override_idle = None
0041         self.interaction_without_idle = True
0042         self.interaction_skill_id = None
0043         self.idle_next = 0  # Next time the idle screen should trigger
0044         self.idle_lock = Lock()
0045         self.override_set_time = time.monotonic()
0046 
0047         self.has_show_page = False  # resets with each handler
0048 
0049     def initialize(self):
0050         """ Perform initalization.
0051 
0052             Registers messagebus handlers and sets default gui values.
0053         """
0054         self.gui.register_handler(
0055             'mycroft.gui.screen.close', self.close_window_by_event)
0056         self.add_event('mycroft.gui.screen.close', self.close_window_by_event)
0057         self.bus.on('mycroft.gui.screen.close', self.close_window_by_event)
0058         self.add_event('mycroft.gui.force.screenclose', self.close_window_by_force)
0059         self.bus.on('mycroft.gui.force.screenclose', self.close_window_by_force)
0060 
0061         try:
0062             self.bus.on('gui.page.show', self.on_gui_page_show)
0063             self.bus.on('gui.page_interaction', self.on_gui_page_interaction)
0064         
0065         except:
0066             LOG.info('could not register on bus')
0067 
0068     def shutdown(self):
0069         self.bus.remove('gui.page.show', self.on_gui_page_show)
0070         self.bus.remove('gui.page_interaction', self.on_gui_page_interaction)
0071         self.bus.remove('mycroft.gui.screen.close', self.close_window_by_event)
0072         self.bus.remove('mycroft.gui.force.screenclose', self.close_window_by_force)
0073 
0074     def override(self, message=None):
0075         """Override the resting screen.
0076         Arguments:
0077             message: Optional message to use for to restore
0078                      the expected override screen after
0079                      another screen has been displayed.
0080         """
0081         self.override_set_time = time.monotonic()
0082         if message:
0083             self.override_idle = (message, time.monotonic())
0084 
0085     def on_gui_page_interaction(self, message):
0086         """ Reset idle timer to 30 seconds when page is flipped. """
0087         skill_id = message.data.get('skill_id')
0088         self.interaction_skill_id = skill_id
0089         if self.interaction_without_idle is False:
0090             self.log.info("Resetting Timeout Counter To 30 Seconds")
0091             self.start_idle_event(30, skid=skill_id)
0092 
0093     def on_gui_page_show(self, message):
0094         if 'BigscreenPlatform' not in message.data.get('__from', ''):
0095             # Some skill other than the handler is showing a page
0096             self.has_show_page = True
0097 
0098             # If a skill overrides the idle do not switch page
0099             override_idle = message.data.get('__idle')
0100             skill_id = message.data.get('__from', '')
0101             if override_idle is True:
0102                 self.interaction_without_idle = True
0103                 self.cancel_idle_event()
0104                 self.log.info('Overriding Till Further Notice')
0105                 self.override(message)
0106             elif isinstance(override_idle, int) and not (override_idle, bool) and override_idle is not False:
0107                 # Set the indicated idle timeout
0108                 self.log.info('Got Override With Idle Type Int')
0109                 self.interaction_without_idle = True
0110                 self.log.info('Overriding idle timer to'
0111                               ' {} seconds'.format(override_idle))
0112                 self.start_idle_event(override_idle, skid=skill_id)
0113             elif (message.data['page']):
0114                 # Set default idle screen timer
0115                 self.log.info('Got Override Without Idle Page')
0116                 if not isinstance(override_idle, bool) or not isinstance(override_idle, int):
0117                     self.interaction_without_idle = False
0118                     self.start_idle_event(30, skid=skill_id)
0119 
0120     # Manage "idle" visual state
0121     def cancel_idle_event(self):
0122         self.idle_next = 0
0123         self.cancel_scheduled_event('IdleCheck')
0124 
0125     def start_idle_event(self, offset=60, weak=False, skid=None):
0126         """ Start an event for showing the idle screen.
0127 
0128         Arguments:
0129             offset: How long until the idle screen should be shown
0130             weak: set to true if the time should be able to be overridden
0131         """
0132         with self.idle_lock:
0133             if time.monotonic() + offset < self.idle_next:
0134                 self.log.info('No update, before next time')
0135                 return
0136 
0137             self.log.info('Starting idle event')
0138             try:
0139                 if not weak:
0140                     self.idle_next = time.monotonic() + offset
0141 
0142                 self.cancel_scheduled_event('IdleCheck')
0143                 time.sleep(0.5)
0144                 self.schedule_event(self.close_current_window, int(offset),
0145                                     name='IdleCheck', data={'skill_id': skid})
0146                 self.log.info('Closing screen in '
0147                               '{} seconds'.format(offset))
0148             except Exception as e:
0149                 self.log.exception(repr(e))
0150 
0151     def close_current_window(self, message):
0152         if not self.interaction_without_idle:
0153             self.bus.emit(Message('screen.close.idle.event', 
0154                                   data={"skill_idle_event_id": message.data.get('skill_id')}))
0155 
0156     def close_window_by_event(self, message):
0157         self.interaction_without_idle = False
0158         #self.log.info("Got Screen Exit CMD")
0159         self.bus.emit(Message('screen.close.idle.event', 
0160                               data={"skill_idle_event_id": self.interaction_skill_id}))
0161 
0162     def close_window_by_force(self, message):
0163         skill_id_from_message = message.data["skill_id"]
0164         #self.log.info(skill_id_from_message, "sent a force close request")
0165         self.bus.emit(Message('screen.close.idle.event', 
0166                               data={"skill_idle_event_id": skill_id_from_message}))
0167 
0168 
0169 def create_skill():
0170     return BigscreenPlatform()