def __init__(self, **kwargs): super(RaceCaptureApp, self).__init__(**kwargs) # We do this because when this app is bundled into a standalone app # by pyinstaller we must reference all files by their absolute paths # sys._MEIPASS is provided by pyinstaller if getattr(sys, 'frozen', False): self.base_dir = sys._MEIPASS else: self.base_dir = os.path.dirname(os.path.abspath(__file__)) #RaceCapture serial I/O self._rc_api = RcpApi(on_disconnect=self._on_rcp_disconnect) #self._keyboard = Window.request_keyboard(self._keyboard_closed, self) #self._keyboard.bind(on_key_down=self._on_keyboard_down) self.settings = SystemSettings(self.user_data_dir, base_dir=self.base_dir) self._databus = DataBusFactory().create_standard_databus( self.settings.systemChannels) self.settings.runtimeChannels.data_bus = self._databus Window.bind(on_keyboard=self._on_keyboard) self.register_event_type('on_tracks_updated') self.processArgs() self.settings.appConfig.setUserDir(self.user_data_dir) self.trackManager = TrackManager( user_dir=self.settings.get_default_data_dir(), base_dir=self.base_dir) self.setup_telemetry()
def __init__(self, **kwargs): super(RaceCaptureApp, self).__init__(**kwargs) if kivy.platform in ['ios', 'macosx', 'linux']: kivy.resources.resource_add_path( os.path.join(os.path.dirname(os.path.realpath(__file__)), "data")) # We do this because when this app is bundled into a standalone app # by pyinstaller we must reference all files by their absolute paths # sys._MEIPASS is provided by pyinstaller if getattr(sys, 'frozen', False): self.base_dir = sys._MEIPASS else: self.base_dir = os.path.dirname(os.path.abspath(__file__)) self.settings = SystemSettings(self.user_data_dir, base_dir=self.base_dir) self.settings.userPrefs.bind(on_pref_change=self._on_preference_change) self.track_manager = TrackManager( user_dir=self.settings.get_default_data_dir(), base_dir=self.base_dir) self.preset_manager = PresetManager( user_dir=self.settings.get_default_data_dir(), base_dir=self.base_dir) # RaceCapture communications API self._rc_api = RcpApi(on_disconnect=self._on_rcp_disconnect, settings=self.settings) self._databus = DataBusFactory().create_standard_databus( self.settings.systemChannels) self.settings.runtimeChannels.data_bus = self._databus self._datastore = CachingAnalysisDatastore(databus=self._databus) self._session_recorder = SessionRecorder(self._datastore, self._databus, self._rc_api, self.settings, self.track_manager, self._status_pump) self._session_recorder.bind(on_recording=self._on_session_recording) HelpInfo.settings = self.settings # Ensure soft input mode text inputs aren't obstructed Window.softinput_mode = 'below_target' # Capture keyboard events for handling escape / back Window.bind(on_keyboard=self._on_keyboard) self.register_event_type('on_tracks_updated') self.processArgs() self.settings.appConfig.setUserDir(self.user_data_dir) self.setup_telemetry()
class RaceCaptureApp(App): #things that care about configuration being loaded config_listeners = [] #things that care about tracks being loaded tracks_listeners = [] #map of view keys to factory functions for building top level views view_builders = {} #container for all settings settings = None #Central RCP configuration object rc_config = RcpConfig() #RaceCapture serial I/O _rc_api = RcpApi() #dataBus provides an eventing / polling mechanism to parts of the system that care _databus = None #pumps data from rcApi to dataBus. kind of like a bridge dataBusPump = DataBusPump() _status_pump = StatusPump() #Track database manager trackManager = None #Application Status bars status_bar = None #main navigation menu mainNav = None #Main Screen Manager screenMgr = None #main view references for dispatching notifications mainViews = {} #application arguments - initialized upon startup app_args = [] use_kivy_settings = False base_dir = None _telemetry_connection = None def __init__(self, **kwargs): super(RaceCaptureApp, self).__init__(**kwargs) # We do this because when this app is bundled into a standalone app # by pyinstaller we must reference all files by their absolute paths # sys._MEIPASS is provided by pyinstaller if getattr(sys, 'frozen', False): self.base_dir = sys._MEIPASS else: self.base_dir = os.path.dirname(os.path.abspath(__file__)) #self._keyboard = Window.request_keyboard(self._keyboard_closed, self) #self._keyboard.bind(on_key_down=self._on_keyboard_down) self.settings = SystemSettings(self.user_data_dir, base_dir=self.base_dir) self._databus = DataBusFactory().create_standard_databus( self.settings.systemChannels) self.settings.runtimeChannels.data_bus = self._databus Window.bind(on_keyboard=self._on_keyboard) self.register_event_type('on_tracks_updated') self.processArgs() self.settings.appConfig.setUserDir(self.user_data_dir) self.trackManager = TrackManager( user_dir=self.settings.get_default_data_dir(), base_dir=self.base_dir) self.setup_telemetry() def on_pause(self): return True def _on_keyboard(self, keyboard, keycode, *args): if keycode == 27: self.switchMainView('home') def processArgs(self): parser = argparse.ArgumentParser( description='Autosport Labs Race Capture App') parser.add_argument('-p', '--port', help='Port', required=False) parser.add_argument('--telemetryhost', help='Telemetry host', required=False) if sys.platform == 'win32': parser.add_argument('--multiprocessing-fork', required=False, action='store_true') self.app_args = vars(parser.parse_args()) def getAppArg(self, name): return self.app_args.get(name, None) def first_time_setup(self): popup = None def _on_answer(instance, answer): popup.dismiss() if answer: self.showMainView('tracks') Clock.schedule_once( lambda dt: self.mainViews['tracks'].check_for_update(), 0.5) popup = confirmPopup( 'Race Tracks', 'Looks like this is your first time running.\n\nShould I update the Race Track database?', _on_answer) self.settings.userPrefs.set_pref('preferences', 'first_time_setup', False) def loadCurrentTracksSuccess(self): Logger.info('RaceCaptureApp: Current Tracks Loaded') Clock.schedule_once(lambda dt: self.notifyTracksUpdated()) def loadCurrentTracksError(self, details): alertPopup('Error Loading Tracks', str(details)) def init_data(self): self.trackManager.init(None, self.loadCurrentTracksSuccess, self.loadCurrentTracksError) def _serial_warning(self): alertPopup( 'Warning', 'Command failed. Ensure you have selected a correct serial port') #Logfile def on_poll_logfile(self, instance): try: self._rc_api.getLogfile() except: pass def on_set_logfile_level(self, instance, level): try: self._rc_api.setLogfileLevel(level, None, self.on_set_logfile_level_error) except: logging.exception('') self._serial_warning() def on_set_logfile_level_error(self, detail): alertPopup('Error', 'Error Setting Logfile Level:\n\n' + str(detail)) #Run Script def on_run_script(self, instance): self._rc_api.runScript(self.on_run_script_complete, self.on_run_script_error) def on_run_script_complete(self, result): Logger.info('RaceCaptureApp: run script complete: ' + str(result)) def on_run_script_error(self, detail): alertPopup('Error Running', 'Error Running Script:\n\n' + str(detail)) #Write Configuration def on_write_config(self, instance, *args): rcpConfig = self.rc_config try: self._rc_api.writeRcpCfg(rcpConfig, self.on_write_config_complete, self.on_write_config_error) self.showActivity("Writing configuration") except: logging.exception('') self._serial_warning() def on_write_config_complete(self, result): Logger.info("RaceCaptureApp: Config written") self.showActivity("Writing completed") self.rc_config.stale = False self.dataBusPump.meta_is_stale() for listener in self.config_listeners: Clock.schedule_once( lambda dt, inner_listener=listener: inner_listener.dispatch( 'on_config_written', self.rc_config)) Clock.schedule_once(lambda dt: self.showActivity(''), 5.0) def on_write_config_error(self, detail): alertPopup('Error Writing', 'Could not write configuration:\n\n' + str(detail)) #Read Configuration def on_read_config(self, instance, *args): try: self._rc_api.getRcpCfg(self.rc_config, self.on_read_config_complete, self.on_read_config_error) self.showActivity("Reading configuration") except: logging.exception('') self._serial_warning() def on_read_config_complete(self, rcpCfg): for listener in self.config_listeners: Clock.schedule_once( lambda dt, inner_listener=listener: inner_listener.dispatch( 'on_config_updated', self.rc_config)) self.rc_config.stale = False self.showActivity('') def on_read_config_error(self, detail): alertPopup('Error Reading', 'Could not read configuration:\n\n' + str(detail)) def on_tracks_updated(self, track_manager): for view in self.tracks_listeners: view.dispatch('on_tracks_updated', track_manager) def notifyTracksUpdated(self): self.dispatch('on_tracks_updated', self.trackManager) def on_main_menu_item(self, instance, value): self.switchMainView(value) def on_main_menu(self, instance, *args): self.mainNav.toggle_state() def showStatus(self, status, isAlert): self.status_bar.dispatch('on_status', status, isAlert) def showActivity(self, status): self.status_bar.dispatch('on_activity', status) def _setX(self, x): pass def _getX(self): pass def on_start(self): pass def on_stop(self): self._rc_api.cleanup_comms() self._telemetry_connection.telemetry_enabled = False def showMainView(self, view_name): try: view = self.mainViews.get(view_name) if not view: view = self.view_builders[view_name]() self.screenMgr.add_widget(view) self.mainViews[view_name] = view self.screenMgr.current = view_name except Exception as detail: Logger.info('RaceCaptureApp: Failed to load main view ' + str(view_name) + ' ' + str(detail)) def switchMainView(self, view_name): self.mainNav.anim_to_state('closed') Clock.schedule_once(lambda dt: self.showMainView(view_name), 0.25) def build_config_view(self): config_view = ConfigView(name='config', rcpConfig=self.rc_config, rc_api=self._rc_api, databus=self._databus, settings=self.settings, base_dir=self.base_dir, track_manager=self.trackManager) config_view.bind(on_read_config=self.on_read_config) config_view.bind(on_write_config=self.on_write_config) config_view.bind(on_run_script=self.on_run_script) config_view.bind(on_poll_logfile=self.on_poll_logfile) config_view.bind(on_set_logfile_level=self.on_set_logfile_level) self._rc_api.addListener( 'logfile', lambda value: Clock.schedule_once(lambda dt: config_view .on_logfile(value))) self.config_listeners.append(config_view) self.tracks_listeners.append(config_view) return config_view def build_status_view(self): status_view = StatusView(self.trackManager, self._status_pump, name='status') self.tracks_listeners.append(status_view) return status_view def build_tracks_view(self): tracks_view = TracksView(name='tracks', track_manager=self.trackManager) self.tracks_listeners.append(tracks_view) return tracks_view def build_dash_view(self): dash_view = DashboardView(name='dash', dataBus=self._databus, settings=self.settings) self.tracks_listeners.append(dash_view) return dash_view def build_analysis_view(self): analysis_view = AnalysisView(name='analysis', data_bus=self._databus, settings=self.settings) self.tracks_listeners.append(analysis_view) return analysis_view def build_preferences_view(self): preferences_view = PreferencesView(name='preferences', settings=self.settings, base_dir=self.base_dir) preferences_view.settings_view.bind( on_config_change=self._on_preferences_change) return preferences_view def build_homepage_view(self): homepage_view = HomePageView(name='home') homepage_view.bind(on_select_view=lambda instance, view_name: self. switchMainView(view_name)) return homepage_view def init_view_builders(self): self.view_builders = { 'config': self.build_config_view, 'tracks': self.build_tracks_view, 'dash': self.build_dash_view, 'analysis': self.build_analysis_view, 'preferences': self.build_preferences_view, 'status': self.build_status_view, 'home': self.build_homepage_view } def build(self): self.init_view_builders() Builder.load_file('racecapture.kv') root = self.root status_bar = root.ids.status_bar status_bar.bind(on_main_menu=self.on_main_menu) self.status_bar = status_bar root.ids.main_menu.bind(on_main_menu_item=self.on_main_menu_item) self.mainNav = root.ids.main_nav #reveal_below_anim #reveal_below_simple #slide_above_anim #slide_above_simple #fade_in self.mainNav.anim_type = 'slide_above_anim' rc_api = self._rc_api rc_api.on_progress = lambda value: status_bar.dispatch( 'on_progress', value) rc_api.on_rx = lambda value: status_bar.dispatch('on_rc_rx', value) rc_api.on_tx = lambda value: status_bar.dispatch('on_rc_tx', value) screenMgr = root.ids.main #NoTransition #SlideTransition #SwapTransition #FadeTransition #WipeTransition #FallOutTransition #RiseInTransition screenMgr.transition = NoTransition() self.screenMgr = screenMgr self.icon = ('resource/images/app_icon_128x128.ico' if sys.platform == 'win32' else 'resource/images/app_icon_128x128.png') Clock.schedule_once(lambda dt: self.post_launch(), 1.0) def post_launch(self): self.showMainView('home') Clock.schedule_once(lambda dt: self.init_data()) Clock.schedule_once(lambda dt: self.init_rc_comms()) self.check_first_time_setup() def check_first_time_setup(self): if self.settings.userPrefs.get_pref('preferences', 'first_time_setup') == 'True': Clock.schedule_once(lambda dt: self.first_time_setup(), 0.5) def init_rc_comms(self): port = self.getAppArg('port') comms = comms_factory(port) rc_api = self._rc_api rc_api.detect_win_callback = self.rc_detect_win rc_api.detect_fail_callback = self.rc_detect_fail rc_api.detect_activity_callback = self.rc_detect_activity rc_api.init_comms(comms) rc_api.run_auto_detect() def rc_detect_win(self, version): if version.is_compatible_version(): self.showStatus( "{} v{}.{}.{}".format(version.friendlyName, version.major, version.minor, version.bugfix), False) self.dataBusPump.startDataPump(self._databus, self._rc_api) self._status_pump.start(self._rc_api) if self.rc_config.loaded == False: Clock.schedule_once(lambda dt: self.on_read_config(self)) else: self.showActivity('Connected') else: alertPopup( 'Incompatible Firmware', 'Detected {} v{}\n\nPlease upgrade firmware to {} or higher'. format(version.friendlyName, version.version_string(), VersionConfig.get_minimum_version().version_string())) def rc_detect_fail(self): def re_detect(): if not self._rc_api.comms.isOpen(): self._rc_api.run_auto_detect() self.showStatus("Could not detect RaceCapture/Pro", True) Clock.schedule_once(lambda dt: re_detect(), 1.0) def rc_detect_activity(self, info): self.showActivity('Searching {}'.format(info)) def open_settings(self, *largs): self.switchMainView('preferences') def setup_telemetry(self): host = self.getAppArg('telemetryhost') telemetry_enabled = True if self.settings.userPrefs.get_pref( 'preferences', 'send_telemetry') == "1" else False self._telemetry_connection = TelemetryManager( self._databus, host=host, telemetry_enabled=telemetry_enabled) self.config_listeners.append(self._telemetry_connection) self._telemetry_connection.bind( on_connecting=self.telemetry_connecting) self._telemetry_connection.bind(on_connected=self.telemetry_connected) self._telemetry_connection.bind( on_disconnected=self.telemetry_disconnected) self._telemetry_connection.bind(on_streaming=self.telemetry_streaming) self._telemetry_connection.bind(on_error=self.telemetry_error) self._telemetry_connection.bind( on_auth_error=self.telemetry_auth_error) def telemetry_connecting(self, instance, msg): self.status_bar.dispatch('on_tele_status', ToolbarView.TELEMETRY_CONNECTING) self.showActivity(msg) def telemetry_connected(self, instance, msg): self.status_bar.dispatch('on_tele_status', ToolbarView.TELEMETRY_CONNECTING) self.showActivity(msg) def telemetry_disconnected(self, instance, msg): self.status_bar.dispatch('on_tele_status', ToolbarView.TELEMETRY_IDLE) self.showActivity(msg) def telemetry_streaming(self, instance, msg): self.status_bar.dispatch('on_tele_status', ToolbarView.TELEMETRY_ACTIVE) def telemetry_auth_error(self, instance, msg): self.status_bar.dispatch('on_tele_status', ToolbarView.TELEMETRY_ERROR) self.showActivity(msg) def telemetry_error(self, instance, msg): self.showActivity(msg) self.status_bar.dispatch('on_tele_status', ToolbarView.TELEMETRY_ERROR) def _on_preferences_change(self, menu, config, section, key, value): """Called any time the app preferences are changed """ token = (section, key) if token == ('preferences', 'send_telemetry'): if value == "1": # Boolean settings values are 1/0, not True/False if self.rc_config.connectivityConfig.cellConfig.cellEnabled: alertPopup( 'Telemetry error', "Turn off RaceCapture's telemetry module for app to stream telemetry." ) self._telemetry_connection.telemetry_enabled = True else: self._telemetry_connection.telemetry_enabled = False
def test_is_firmware_update_available_on_wireless(self): self.comms.is_wireless = Mock(return_value=True) rcpapi = RcpApi(settings=self.settings, comms=self.comms) self.assertFalse(rcpapi.is_firmware_update_supported())
def test_is_firmware_update_available_no_comms(self): rcpapi = RcpApi(settings=self.settings, comms=None) self.assertFalse(rcpapi.is_firmware_update_supported())
class RaceCaptureApp(App): #container for all settings settings = None #Central RCP configuration object rc_config = RcpConfig() #RaceCapture serial I/O _rc_api = RcpApi() #dataBus provides an eventing / polling mechanism to parts of the system that care _data_bus = None #pumps data from rcApi to dataBus. kind of like a bridge dataBusPump = DataBusPump() #Track database manager trackManager = None #Application Status bars statusBar = None #Main Views configView = None #main navigation menu mainNav = None #Main Screen Manager screenMgr = None #main view references for dispatching notifications mainViews = None #application arguments - initialized upon startup app_args = [] use_kivy_settings = False def __init__(self, **kwargs): super(RaceCaptureApp, self).__init__(**kwargs) #self._keyboard = Window.request_keyboard(self._keyboard_closed, self) #self._keyboard.bind(on_key_down=self._on_keyboard_down) self.settings = SystemSettings(self.user_data_dir) self._data_bus = DataBusFactory().create_standard_databus( self.settings.systemChannels) Window.bind(on_key_down=self._on_keyboard_down) self.register_event_type('on_tracks_updated') self.processArgs() self.settings.appConfig.setUserDir(self.user_data_dir) self.trackManager = TrackManager(user_dir=self.user_data_dir) def _on_keyboard_down(self, keyboard, keycode, *args): if keycode == 27: self.switchMainView('home') def processArgs(self): parser = argparse.ArgumentParser( description='Autosport Labs Race Capture App') parser.add_argument('-p', '--port', help='Port', required=False) if sys.platform == 'win32': parser.add_argument('--multiprocessing-fork', required=False, action='store_true') self.app_args = vars(parser.parse_args()) def getAppArg(self, name): return self.app_args.get(name, None) def loadCurrentTracksSuccess(self): print('Curent Tracks Loaded') Clock.schedule_once(lambda dt: self.notifyTracksUpdated()) def loadCurrentTracksError(self, details): alertPopup('Error Loading Tracks', str(details)) def init_data(self): self.trackManager.init(None, self.loadCurrentTracksSuccess, self.loadCurrentTracksError) def _serial_warning(self): alertPopup( 'Warning', 'Command failed. Ensure you have selected a correct serial port') #Logfile def on_poll_logfile(self, instance): try: self._rc_api.getLogfile() except: pass def on_set_logfile_level(self, instance, level): try: self._rc_api.setLogfileLevel(level, None, self.on_set_logfile_level_error) except: logging.exception('') self._serial_warning() def on_set_logfile_level_error(self, detail): alertPopup('Error', 'Error Setting Logfile Level:\n\n' + str(detail)) #Run Script def on_run_script(self, instance): self._rc_api.runScript(self.on_run_script_complete, self.on_run_script_error) def on_run_script_complete(self, result): print('run script complete: ' + str(result)) def on_run_script_error(self, detail): alertPopup('Error Running', 'Error Running Script:\n\n' + str(detail)) #Write Configuration def on_write_config(self, instance, *args): rcpConfig = self.rc_config try: self._rc_api.writeRcpCfg(rcpConfig, self.on_write_config_complete, self.on_write_config_error) self.showActivity("Writing configuration") except: logging.exception('') self._serial_warning() def on_write_config_complete(self, result): self.rc_config.stale = False Clock.schedule_once( lambda dt: self.configView.dispatch('on_config_written')) def on_write_config_error(self, detail): alertPopup('Error Writing', 'Could not write configuration:\n\n' + str(detail)) #Read Configuration def on_read_config(self, instance, *args): try: self._rc_api.getRcpCfg(self.rc_config, self.on_read_config_complete, self.on_read_config_error) self.showActivity("Reading configuration") except: logging.exception('') self._serial_warning() def on_read_config_complete(self, rcpCfg): Clock.schedule_once(lambda dt: self.configView.dispatch( 'on_config_updated', self.rc_config)) self.rc_config.stale = False self.showActivity('') def on_read_config_error(self, detail): alertPopup('Error Reading', 'Could not read configuration:\n\n' + str(detail)) def on_tracks_updated(self, track_manager): for view in self.mainViews.itervalues(): view.dispatch('on_tracks_updated', track_manager) def notifyTracksUpdated(self): self.dispatch('on_tracks_updated', self.trackManager) def on_main_menu_item(self, instance, value): self.switchMainView(value) def on_main_menu(self, instance, *args): self.mainNav.toggle_state() def showMainView(self, viewKey): try: self.screenMgr.current = viewKey except Exception as detail: print('Failed to load main view ' + str(viewKey) + ' ' + str(detail)) def switchMainView(self, viewKey): self.mainNav.anim_to_state('closed') Clock.schedule_once(lambda dt: self.showMainView(viewKey), 0.25) def showStatus(self, status, isAlert): self.statusBar.dispatch('on_status', status, isAlert) def showActivity(self, status): self.statusBar.dispatch('on_activity', status) def _setX(self, x): pass def _getX(self): pass def on_start(self): Clock.schedule_once(lambda dt: self.init_data()) Clock.schedule_once(lambda dt: self.init_rc_comms()) def on_stop(self): self._rc_api.cleanup_comms() def build(self): Builder.load_file('racecapture.kv') statusBar = kvFind(self.root, 'rcid', 'statusbar') statusBar.bind(on_main_menu=self.on_main_menu) self.statusBar = statusBar mainMenu = kvFind(self.root, 'rcid', 'mainMenu') mainMenu.bind(on_main_menu_item=self.on_main_menu_item) self.mainNav = kvFind(self.root, 'rcid', 'mainNav') #reveal_below_anim #reveal_below_simple #slide_above_anim #slide_above_simple #fade_in self.mainNav.anim_type = 'slide_above_anim' configView = ConfigView(name='config', rcpConfig=self.rc_config, rc_api=self._rc_api, dataBusPump=self.dataBusPump, settings=self.settings) configView.bind(on_read_config=self.on_read_config) configView.bind(on_write_config=self.on_write_config) configView.bind(on_run_script=self.on_run_script) configView.bind(on_poll_logfile=self.on_poll_logfile) configView.bind(on_set_logfile_level=self.on_set_logfile_level) rcComms = self._rc_api rcComms.addListener( 'logfile', lambda value: Clock.schedule_once(lambda dt: configView. on_logfile(value))) rcComms.on_progress = lambda value: statusBar.dispatch( 'on_progress', value) rcComms.on_rx = lambda value: statusBar.dispatch('on_rc_rx', value) rcComms.on_tx = lambda value: statusBar.dispatch('on_rc_tx', value) tracksView = TracksView(name='tracks') dashView = DashboardView(name='dash', dataBus=self._data_bus, settings=self.settings) homepageView = HomePageView(name='home') homepageView.bind(on_select_view=lambda instance, viewKey: self. switchMainView(viewKey)) analysisView = AnalysisView(name='analysis', data_bus=self._data_bus, settings=self.settings) preferences_view = PreferencesView(self.settings, name='preferences') screenMgr = kvFind(self.root, 'rcid', 'main') #NoTransition #SlideTransition #SwapTransition #FadeTransition #WipeTransition #FallOutTransition #RiseInTransition screenMgr.transition = NoTransition() screenMgr.add_widget(homepageView) screenMgr.add_widget(configView) screenMgr.add_widget(tracksView) screenMgr.add_widget(dashView) screenMgr.add_widget(analysisView) screenMgr.add_widget(preferences_view) self.mainViews = { 'config': configView, 'tracks': tracksView, 'dash': dashView, 'analysis': analysisView, 'preferences': preferences_view } self.screenMgr = screenMgr self.configView = configView self.icon = ('resource/images/app_icon_128x128.ico' if sys.platform == 'win32' else 'resource/images/app_icon_128x128.png') def init_rc_comms(self): port = self.getAppArg('port') comms = comms_factory(port) rc_api = self._rc_api rc_api.detect_win_callback = self.rc_detect_win rc_api.detect_fail_callback = self.rc_detect_fail rc_api.detect_activity_callback = self.rc_detect_activity rc_api.init_comms(comms) rc_api.run_auto_detect() def rc_detect_win(self, rcpVersion): self.showStatus( "{} v{}.{}.{}".format(rcpVersion.friendlyName, rcpVersion.major, rcpVersion.minor, rcpVersion.bugfix), False) self.dataBusPump.startDataPump(self._data_bus, self._rc_api) if self.rc_config.loaded == False: Clock.schedule_once(lambda dt: self.on_read_config(self)) else: self.showActivity('Connected') def rc_detect_fail(self): self.showStatus("Could not detect RaceCapture/Pro", True) Clock.schedule_once(lambda dt: self._rc_api.run_auto_detect(), 1.0) def rc_detect_activity(self, info): self.showActivity('Searching {}'.format(info)) def open_settings(self, *largs): self.switchMainView('preferences')