Exemplo n.º 1
0
 async def init_osc(self):
     _LOGGER.debug("GUI1: Initing OSC")
     self.oscer = OSCManager(
         hostlisten='127.0.0.1',
         portlisten=int(self.config.get('local', 'frontendport')),
         hostconnect='127.0.0.1',
         portconnect=int(self.config.get('local', 'backendport')))
     await self.oscer.init(on_init_ok=self.on_osc_init_ok,
                           on_connection_timeout=self.on_connection_timeout)
Exemplo n.º 2
0
 def __init__(self,
              hostlisten='127.0.0.1',
              portlisten=33217, **kwargs):
     _LOGGER.warning(f'BluetoothDispatcherWC init L({hostlisten}:{portlisten})')
     self._oscer = OSCManager(
         hostlisten=hostlisten,
         portlisten=portlisten
     )
     super(BluetoothDispatcherWC, self).__init__(**kwargs)
     _LOGGER.warning('BluetoothDispatcherWC init end')
Exemplo n.º 3
0
 def __init__(self,
              hostlisten=None,
              portlisten=33218,
              hostconnect='127.0.0.1',
              portconnect=33217, **kwargs):
     self._init_oscer = False
     if not BluetoothDispatcherW._oscer:
         if hostlisten:
             self._init_oscer = True
             BluetoothDispatcherW._oscer = OSCManager(
                 hostlisten=hostlisten,
                 portlisten=portlisten,
                 hostconnect=hostconnect,
                 portconnect=portconnect
             )
     _LOGGER.info(f'Constructing BluetoothDispatcherW {kwargs} events={self.__events__}')
     super(BluetoothDispatcherW, self).__init__(**kwargs)
Exemplo n.º 4
0
 def generate_uid(self):
     while True:
         uid = OSCManager.generate_uid()
         if uid not in self.devicemanagers_by_uid:
             return uid
Exemplo n.º 5
0
 async def init_osc(self):
     self.oscer = OSCManager(hostlisten=self.hostlisten,
                             portlisten=self.portlisten)
     await self.oscer.init(on_init_ok=self.on_osc_init_ok)
Exemplo n.º 6
0
class DeviceManagerService(object):
    def __init__(self, **kwargs):
        self.debug_params = dict()
        self.addit_params = dict()
        for key, val in kwargs.items():
            mo = re.search('^debug_([^_]+)_(.+)', key)
            if mo:
                kk = mo.group(1)
                ll = self.debug_params.get(kk, dict())
                ll[mo.group(2)] = val
                self.debug_params[kk] = ll
            elif key.startswith('ab_'):
                self.addit_params[key[3:]] = val
            else:
                setattr(self, key, val)
        _LOGGER.debug(
            f'Addit params for DM {self.addit_params} AND {self.debug_params}')
        self.db = None
        self.oscer = None
        self.notification_formatter_info = dict()
        self.connectors_format = False
        self.devicemanager_class_by_type = dict()
        self.devicemanagers_pre_actions = dict()
        self.devicemanagers_by_id = dict()
        self.devicemanagers_by_uid = dict()
        self.connectors_info = []
        self.users = []
        self.views = []
        self.devices = []
        self.devicemanagers_active = []
        self.devicemanagers_active_done = []
        self.timer_obj = None
        self.main_session = None
        self.last_user = None
        self.devicemanagers_active_info = dict()
        self.stop_event = asyncio.Event()
        self.last_notify_ms = time() * 1000
        if self.android:
            from jnius import autoclass
            from android.broadcast import BroadcastReceiver
            self.Context = autoclass('android.content.Context')
            self.AndroidString = autoclass('java.lang.String')
            self.NotificationCompatInboxStyle = autoclass(
                'android.app.Notification$InboxStyle')
            NotificationBuilder = autoclass('android.app.Notification$Builder')
            self.PythonActivity = autoclass('org.kivy.android.PythonActivity')
            self.service = autoclass('org.kivy.android.PythonService').mService
            NOTIFICATION_CHANNEL_ID = self.AndroidString(
                self.service.getPackageName().encode('utf-8'))
            self.NOTIFICATION_GROUP = 'pyMovizGroup'
            self.FOREGROUND_NOTIFICATION_ID = 4563
            app_context = self.service.getApplication().getApplicationContext()
            self.app_context = app_context
            self.notification_service = self.service.getSystemService(
                self.Context.NOTIFICATION_SERVICE)
            self.CONNECT_ACTION = 'device_manager_service.view.CONNECT'
            self.DISCONNECT_ACTION = 'device_manager_service.view.DISCONNECT'
            self.STOP_ACTION = 'device_manager_service.STOP'

            self.br = BroadcastReceiver(self.on_broadcast,
                                        actions=[
                                            self.CONNECT_ACTION,
                                            PRESENCE_REQUEST_ACTION,
                                            self.DISCONNECT_ACTION,
                                            self.STOP_ACTION
                                        ])
            self.br.start()

            Intent = autoclass('android.content.Intent')
            self.Intent = Intent
            PendingIntent = autoclass('android.app.PendingIntent')
            NotificationActionBuilder = autoclass(
                'android.app.Notification$Action$Builder')
            Notification = autoclass('android.app.Notification')
            Color = autoclass("android.graphics.Color")
            NotificationChannel = autoclass('android.app.NotificationChannel')
            NotificationManager = autoclass('android.app.NotificationManager')
            channelName = self.AndroidString(
                'DeviceManagerService'.encode('utf-8'))
            chan = NotificationChannel(NOTIFICATION_CHANNEL_ID, channelName,
                                       NotificationManager.IMPORTANCE_DEFAULT)
            chan.setLightColor(Color.BLUE)
            chan.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE)
            self.notification_service.createNotificationChannel(chan)
            BitmapFactory = autoclass("android.graphics.BitmapFactory")
            Icon = autoclass("android.graphics.drawable.Icon")
            BitmapFactoryOptions = autoclass(
                "android.graphics.BitmapFactory$Options")
            # Drawable = jnius.autoclass("{}.R$drawable".format(service.getPackageName()))
            # icon = getattr(Drawable, 'icon')
            options = BitmapFactoryOptions()
            # options.inMutable = True
            # declaredField = options.getClass().getDeclaredField("inPreferredConfig")
            # declaredField.set(cast('java.lang.Object',options), cast('java.lang.Object', BitmapConfig.ARGB_8888))
            # options.inPreferredConfig = BitmapConfig.ARGB_8888;
            notification_image = join(dirname(__file__), '..', 'images',
                                      'device_manager.png')
            bm = BitmapFactory.decodeFile(notification_image, options)
            notification_icon = Icon.createWithBitmap(bm)

            notification_image = join(dirname(__file__), '..', 'images',
                                      'lan-connect.png')
            bm = BitmapFactory.decodeFile(notification_image, options)
            icon = Icon.createWithBitmap(bm)
            broadcastIntent = Intent(self.CONNECT_ACTION)
            actionIntent = PendingIntent.getBroadcast(
                self.service, 0, broadcastIntent,
                PendingIntent.FLAG_UPDATE_CURRENT)
            connect_action = NotificationActionBuilder(
                icon, self.AndroidString('CONNECT'.encode('utf-8')),
                actionIntent).build()

            notification_image = join(dirname(__file__), '..', 'images',
                                      'lan-disconnect.png')
            bm = BitmapFactory.decodeFile(notification_image, options)
            icon = Icon.createWithBitmap(bm)
            broadcastIntent = Intent(self.DISCONNECT_ACTION)
            actionIntent = PendingIntent.getBroadcast(
                self.service, 0, broadcastIntent,
                PendingIntent.FLAG_UPDATE_CURRENT)
            disconnect_action = NotificationActionBuilder(
                icon, self.AndroidString('DISCONNECT'.encode('utf-8')),
                actionIntent).build()

            notification_image = join(dirname(__file__), '..', 'images',
                                      'stop.png')
            bm = BitmapFactory.decodeFile(notification_image, options)
            icon = Icon.createWithBitmap(bm)
            broadcastIntent = Intent(self.STOP_ACTION)
            actionIntent = PendingIntent.getBroadcast(
                self.service, 0, broadcastIntent,
                PendingIntent.FLAG_UPDATE_CURRENT)
            stop_action = NotificationActionBuilder(
                icon, self.AndroidString('STOP'.encode('utf-8')),
                actionIntent).build()

            notification_intent = Intent(app_context, self.PythonActivity)
            notification_intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP
                                         | Intent.FLAG_ACTIVITY_SINGLE_TOP
                                         | Intent.FLAG_ACTIVITY_NEW_TASK)
            notification_intent.setAction(Intent.ACTION_MAIN)
            notification_intent.addCategory(Intent.CATEGORY_LAUNCHER)
            notification_intent = PendingIntent.getActivity(
                self.service, 0, notification_intent, 0)
            self.notification_builder = NotificationBuilder(app_context, NOTIFICATION_CHANNEL_ID)\
                .setContentIntent(notification_intent)\
                .setSmallIcon(notification_icon)\
                .addAction(connect_action)\
                .addAction(disconnect_action)\
                .addAction(stop_action)
            self.notification_builder_no_action = NotificationBuilder(app_context, NOTIFICATION_CHANNEL_ID)\
                .setContentIntent(notification_intent)\
                .setSmallIcon(notification_icon)

    def on_broadcast(self, context, intent):
        action = intent.getAction()
        _LOGGER.info(f'on_broadcast action {action}')
        if action == self.CONNECT_ACTION:
            self.loop.call_soon_threadsafe(self.on_command_condisc, 'c',
                                           self.last_user)
        elif action == self.DISCONNECT_ACTION:
            self.loop.call_soon_threadsafe(self.on_command_condisc, 'd')
        elif action == PRESENCE_REQUEST_ACTION:
            self.app_context.sendBroadcast(
                self.Intent(PRESENCE_RESPONSE_ACTION))
        else:
            self.loop.call_soon_threadsafe(self.on_command_stop)

    def change_service_notification(self, dm, **kwargs):
        if self.android:
            alias = dm.get_device().get_alias()
            if alias not in self.notification_formatter_info:
                idnot = self.FOREGROUND_NOTIFICATION_ID + len(
                    self.notification_formatter_info)
                self.notification_formatter_info[alias] =\
                    DeviceNotiication(dm,
                                      idnot,
                                      self)
            self.notification_formatter_info[alias].format(**kwargs)

    async def init_osc(self):
        self.oscer = OSCManager(hostlisten=self.hostlisten,
                                portlisten=self.portlisten)
        await self.oscer.init(on_init_ok=self.on_osc_init_ok)

    def on_osc_init_ok(self, exception=None):
        if not exception:
            self.oscer.handle(COMMAND_STOP, self.on_command_stop)
            self.oscer.handle(COMMAND_LOGLEVEL, self.on_command_loglevel)
            self.oscer.handle(COMMAND_NEWDEVICE, self.on_command_newdevice)
            self.oscer.handle(COMMAND_QUERY,
                              self.on_command_query,
                              do_split=True)
            self.oscer.handle(COMMAND_CONNECT, self.on_command_condisc, 'c')
            self.oscer.handle(COMMAND_DISCONNECT, self.on_command_condisc, 'd')
            self.oscer.handle(COMMAND_CONNECTORS, self.on_command_connectors)
            self.oscer.handle(COMMAND_LISTDEVICES, self.on_command_listdevices)
            self.oscer.handle(COMMAND_LISTUSERS, self.on_command_listusers)
            self.oscer.handle(COMMAND_LISTVIEWS, self.on_command_listviews)
            self.oscer.handle(
                COMMAND_SAVEVIEW,
                partial(self.on_command_dbelem,
                        asyncmethod=self.on_command_saveelem_async,
                        lst=self.views,
                        cls=View,
                        on_ok=self.set_devicemanagers_active))
            self.oscer.handle(
                COMMAND_DELVIEW,
                partial(self.on_command_dbelem,
                        asyncmethod=self.on_command_delelem_async,
                        lst=self.views,
                        cls=View,
                        on_ok=self.set_devicemanagers_active))
            self.oscer.handle(
                COMMAND_SAVEUSER,
                partial(self.on_command_dbelem,
                        asyncmethod=self.on_command_saveelem_async,
                        lst=self.users,
                        cls=User))
            self.oscer.handle(
                COMMAND_DELUSER,
                partial(self.on_command_dbelem,
                        asyncmethod=self.on_command_delelem_async,
                        lst=self.users,
                        cls=User))
            self.create_device_managers()

    def on_command_condisc(self, cmd, *args, sender=None, **kwargs):
        _LOGGER.info(f'On Command condisc: {cmd}')
        if cmd == 'c':
            if args and isinstance(args[0], User) and args[0] in self.users:
                self.last_user = args[0]
            else:
                self.oscer.send(COMMAND_CONFIRM,
                                CONFIRM_FAILED_1,
                                MSG_INVALID_USER,
                                dest=sender)
                return
        self.oscer.send(COMMAND_CONFIRM, CONFIRM_OK, cmd, dest=sender)
        for dm in self.devicemanagers_active_done.copy():
            self.devicemanagers_active.append(dm)
            self.devicemanagers_active_done.remove(dm)
        from device.manager import GenericDeviceManager
        GenericDeviceManager.sort(self.devicemanagers_active)
        for _, info in self.devicemanagers_active_info.items():
            info['operation'] = cmd
            info['retry'] = 0
        Timer(
            0,
            partial(self.start_remaining_connection_operations, bytimer=False))

    def on_command_listviews(self, *args, sender=None, **kwargs):
        _LOGGER.info('List view before send:')
        for v in self.views:
            _LOGGER.info(f'View = {v}')
        self.oscer.send(COMMAND_LISTVIEWS_RV, *self.views, dest=sender)

    def on_command_listusers(self, *args, sender=None, **kwargs):
        self.oscer.send(COMMAND_LISTUSERS_RV, *self.users, dest=sender)

    def on_command_connectors(self,
                              connectors_info,
                              *args,
                              sender=None,
                              **kwargs):
        connectors_info = json.loads(connectors_info)
        if connectors_info:
            for ci in connectors_info:
                if not exists(ci['temp']):
                    self.oscer.send(COMMAND_CONFIRM,
                                    CONFIRM_FAILED_1,
                                    dest=sender)
                    return
            self.connectors_format = True
            Timer(
                0,
                partial(TcpClient.init_connectors_async, self.loop,
                        connectors_info))
        self.oscer.send(COMMAND_CONFIRM, CONFIRM_OK, dest=sender)

    def on_command_listdevices(self, *args, sender=None, **kwargs):
        out = []
        for uid, dm in self.devicemanagers_by_uid.items():
            out.append(uid)
            out.append(dm.get_device())
        self.oscer.send(COMMAND_LISTDEVICES_RV, *out, dest=sender)

    async def on_command_delelem_async(self,
                                       elem,
                                       *args,
                                       lst=None,
                                       on_ok=None,
                                       sender=None,
                                       **kwargs):
        try:
            _LOGGER.info(f'on_command_delelem_async {elem}')
            rv = await elem.delete(self.db)
            if rv:
                if elem in lst:
                    lst.remove(elem)
                self.oscer.send(COMMAND_CONFIRM, CONFIRM_OK, elem, dest=sender)
                if on_ok:
                    on_ok(elem)
                TcpClient.reset_templates()
            else:
                self.oscer.send(COMMAND_CONFIRM,
                                CONFIRM_FAILED_1,
                                MSG_DB_SAVE_ERROR % str(elem),
                                dest=sender)
        except Exception:
            _LOGGER.error(
                f'on_command_delelem_async exception {traceback.format_exc()}')

    async def on_command_saveelem_async(self,
                                        elem,
                                        *args,
                                        lst=None,
                                        on_ok=None,
                                        sender=None,
                                        **kwargs):
        try:
            _LOGGER.info(f'on_command_saveelem_async {elem}')
            rv = await elem.to_db(self.db)
            if rv:
                if elem not in lst:
                    lst.append(elem)
                else:
                    lst[lst.index(elem)] = elem
                self.oscer.send(COMMAND_CONFIRM, CONFIRM_OK, elem, dest=sender)
                if on_ok:
                    on_ok(elem)
                TcpClient.reset_templates()
            else:
                self.oscer.send(COMMAND_CONFIRM,
                                CONFIRM_FAILED_1,
                                MSG_DB_SAVE_ERROR % str(elem),
                                dest=sender)
        except Exception:
            _LOGGER.error(
                f'on_command_saveelem_async exception {traceback.format_exc()}'
            )

    def on_command_dbelem(self,
                          elem,
                          *args,
                          asyncmethod=None,
                          lst=None,
                          cls=None,
                          on_ok=None,
                          sender=None,
                          **kwargs):
        _LOGGER.info(f'on_command_dbelem {elem}')
        if isinstance(elem, cls):
            if self.devicemanagers_all_stopped():
                Timer(0, partial(asyncmethod, elem, lst=lst, on_ok=on_ok))
            else:
                self.oscer.send(COMMAND_CONFIRM,
                                CONFIRM_FAILED_2,
                                MSG_CONNECTION_STATE_INVALID,
                                dest=sender)
        else:
            self.oscer.send(COMMAND_CONFIRM,
                            CONFIRM_FAILED_1,
                            MSG_INVALID_ITEM,
                            dest=sender)

    def on_event_command_handle(self, dm, command, *args):
        _LOGGER.debug(f'On Command Handle dm={dm} comm={command} a={args}')
        exitv = args[0] if args else None
        if command == COMMAND_NEWSESSION and exitv == CONFIRM_OK:
            if self.connectors_format:
                TcpClient.format(dm.get_device(),
                                 manager=dm,
                                 session=args[2],
                                 user=self.last_user)
            self.change_service_notification(dm,
                                             manager=dm,
                                             session=args[2],
                                             user=self.last_user)
            if self.main_session:
                args[1].set_main_session_id(self.main_session.get_id())
            else:
                self.main_session = args[2]
        elif command == COMMAND_DEVICEFIT and exitv == CONFIRM_OK:
            if self.connectors_format:
                TcpClient.format(args[1],
                                 fitobj=args[2],
                                 manager=dm,
                                 device=args[1])
            self.change_service_notification(dm, fitobj=args[2], manager=dm)
        elif command == COMMAND_DELDEVICE and exitv == CONFIRM_OK:
            ids = f'{dm.get_id()}'
            if ids in self.devicemanagers_by_id:
                del self.devicemanagers_by_id[ids]
            ids = dm.get_uid()
            if ids in self.devicemanagers_active_info:
                del self.devicemanagers_active_info[ids]
            if ids in self.devicemanagers_by_uid:
                del self.devicemanagers_by_uid[ids]
            if dm in self.devicemanagers_active:
                self.devicemanagers_active.remove(dm)
            if dm in self.devicemanagers_active_done:
                self.devicemanagers_active_done.remove(dm)
            self.set_formatters_device()
        elif command == COMMAND_SAVEDEVICE and exitv == CONFIRM_OK:
            ids = f'{dm.get_id()}'
            if ids not in self.devicemanagers_by_id:
                self.devicemanagers_by_id[ids] = dm
            TcpClient.reset_templates()
            self.set_formatters_device()
            self.on_command_listviews()
        elif command == COMMAND_SEARCH and exitv == CONFIRM_OK:
            if dm.get_state() != DEVSTATE_SEARCHING:
                if self.devicemanagers_all_stopped():
                    dm.search(True)
            else:
                dm.search(False)

    def generate_uid(self):
        while True:
            uid = OSCManager.generate_uid()
            if uid not in self.devicemanagers_by_uid:
                return uid

    def devicemanagers_all_stopped(self):
        if not self.devicemanagers_active or not self.timer_obj:
            for _, x in self.devicemanagers_by_uid.items():
                if not x.is_stopped_state():
                    return False
            return True
        else:
            return False

    def on_command_newdevice(self, typev, *args, sender=None, **kwargs):
        if typev not in self.devicemanager_class_by_type:
            self.oscer.send(COMMAND_CONFIRM,
                            CONFIRM_FAILED_1,
                            MSG_TYPE_DEVICE_UNKNOWN,
                            dest=sender)
        elif not self.devicemanagers_all_stopped():
            self.oscer.send(COMMAND_CONFIRM,
                            CONFIRM_FAILED_1,
                            MSG_CONNECTION_STATE_INVALID,
                            dest=sender)
        else:
            uid = self.generate_uid()
            self.devicemanagers_by_uid[uid] = self.devicemanager_class_by_type[
                typev](self.oscer,
                       uid,
                       service=True,
                       db=self.db,
                       loop=self.loop,
                       params=self.addit_params,
                       debug_params=self.debug_params.get(typev, dict()),
                       on_command_handle=self.on_event_command_handle,
                       on_state_transition=self.on_event_state_transition)
            self.oscer.send(COMMAND_CONFIRM, CONFIRM_OK, uid, dest=sender)

    async def db_query_single(self, txt):
        result = dict(error='',
                      rows=[],
                      cols=[],
                      rowcount=0,
                      changes=0,
                      lastrowid=-1)
        try:
            async with self.db.cursor() as cursor:
                result['changes_before'] = self.db.total_changes
                await cursor.execute(txt)
                lst = result['rows']
                result['rowcount'] = cursor.rowcount
                result['lastrowid'] = cursor.lastrowid
                result['changes_after'] = self.db.total_changes
                async for row in cursor:
                    if not result['cols']:
                        result['cols'] = list(row.keys())
                    item = ''
                    for r in result['cols']:
                        item += f'\t{row[r]}'
                    lst.append(item.strip())
            await self.db.commit()
        except Exception as ex:
            result['error'] = str(ex)
            _LOGGER.error(f'Query Error {traceback.format_exc()}')
        _LOGGER.info(f'Query {txt} result obtained')
        _LOGGER.debug(f'Query result {result}')
        return result

    async def db_query(self, txt, sender=None):
        queries = re.split(r';[\r\n]*', txt)
        results = []
        for q in queries:
            q = q.strip()
            if q:
                r = await self.db_query_single(q)
                results.append(r)
        self.oscer.send(COMMAND_CONFIRM,
                        CONFIRM_OK,
                        results,
                        do_split=True,
                        dest=sender)

    def on_command_query(self, txt, *args, sender=None, **kwargs):
        _LOGGER.debug(f'on_command_query {txt}')
        if not self.db:
            self.oscer.send(COMMAND_CONFIRM,
                            CONFIRM_FAILED_1,
                            MSG_DB_SAVE_ERROR % self.db_fname,
                            do_split=True,
                            dest=sender)
        elif not txt:
            self.oscer.send(COMMAND_CONFIRM,
                            CONFIRM_FAILED_2,
                            MSG_INVALID_PARAM,
                            do_split=True,
                            dest=sender)
        elif self.devicemanagers_all_stopped():
            Timer(0, partial(self.db_query, txt, sender=None))
        else:
            self.oscer.send(COMMAND_CONFIRM,
                            CONFIRM_FAILED_2,
                            MSG_CONNECTION_STATE_INVALID,
                            do_split=True,
                            dest=sender)

    def on_command_loglevel(self,
                            level,
                            notify_screen_on,
                            notify_every_ms,
                            *args,
                            sender=None,
                            **kwargs):
        init_logger(__name__, level)
        self.verbose = level
        if notify_screen_on >= 0:
            self.notify_screen_on = notify_screen_on
        if notify_every_ms >= 0:
            self.notify_every_ms = notify_every_ms

    def on_command_stop(self, *args, sender=None, **kwargs):
        self.loop.stop()

    def reset_service_notifications(self):
        for _, no in self.notification_formatter_info.items():
            no.clear()
            self.cancel_service_notification(no.idnot)
        if len(self.notification_formatter_info) > 1:
            self.cancel_service_notification(self.FOREGROUND_NOTIFICATION_ID -
                                             1)
        self.notification_formatter_info.clear()

    def cancel_service_notification(self, idnot):
        if idnot == self.FOREGROUND_NOTIFICATION_ID:
            self.set_service_notification(idnot,
                                          self.build_service_notification())
        else:
            self.notification_service.cancel(idnot)

    def set_service_notification(self, idnot, notif):
        self.notification_service.notify(idnot, notif)

    def set_summary_notification(self):
        if len(self.notification_formatter_info) > 1:
            summary = ''
            lines = []
            message = f'{len(self.notification_formatter_info)} active devices'
            for t, no in self.notification_formatter_info.items():
                if no.last_txt:
                    lines.append(no.last_txt)
                summary += f' {no.title}'
            if summary and len(lines) > 1:
                summary = summary[1:]
                self.set_service_notification(
                    self.FOREGROUND_NOTIFICATION_ID - 1,
                    self.build_service_notification(summary, message, lines))

    def build_service_notification(self,
                                   title=None,
                                   message=None,
                                   lines=None,
                                   idnot=0):
        group = None
        nb = self.notification_builder
        if not title and not message:
            title = "Fit.py"
            message = "DeviceManagerService"
        elif len(self.notification_formatter_info) > 1:
            nb = self.notification_builder if idnot == self.FOREGROUND_NOTIFICATION_ID else self.notification_builder_no_action
            group = self.NOTIFICATION_GROUP
        title = self.AndroidString((title if title else 'N/A').encode('utf-8'))
        message = self.AndroidString(message.encode('utf-8'))
        nb.setContentTitle(title)\
            .setGroup(group)\
            .setContentText(message)\
            .setOnlyAlertOnce(self.notify_screen_on <= 0)
        if lines is not None:
            style = self.NotificationCompatInboxStyle()\
                .setSummaryText(title)\
                .setBigContentTitle(message)
            for l in lines:
                style.addLine(self.AndroidString(l.encode('utf-8')))
            nb.setStyle(style)\
                .setGroupSummary(True)
        else:
            nb.setStyle(None)\
                .setGroupSummary(False)
        return nb.getNotification()

    def insert_service_notification(self):
        self.service.setAutoRestartService(False)
        self.service.startForeground(self.FOREGROUND_NOTIFICATION_ID,
                                     self.build_service_notification())

    async def start(self):
        if self.android:
            self.insert_service_notification()
        self.devicemanager_class_by_type = find_devicemanager_classes(_LOGGER)
        for tp, cls in self.devicemanager_class_by_type.items():
            if cls.__pre_action__:
                nm = cls.__pre_action__.__name__
                if nm not in self.devicemanagers_pre_actions:
                    self.devicemanagers_pre_actions[nm] = cls.__pre_action__
        await self.init_db(self.db_fname)
        await self.load_db()
        await self.init_osc()

    def set_devicemanagers_active(self, *args, **kwargs):
        del self.devicemanagers_active_done[:]
        del self.devicemanagers_active[:]
        self.devicemanagers_active_info.clear()
        self.reset_service_notifications()
        for v in self.views:
            if v.active:
                for c in v.get_connected_devices():
                    d = self.devicemanagers_by_id[str(c)]
                    if d not in self.devicemanagers_active_done:
                        self.devicemanagers_active_done.append(d)
        for d in self.devicemanagers_active_done:
            self.devicemanagers_active_info[d.get_uid()] = dict(retry=0,
                                                                operation='')

    def set_formatters_device(self):
        views2save = []
        for v in self.views:
            saveview = False
            for i in range(len(v.items) - 1, -1, -1):
                lab = v.items[i]
                ids = str(lab.device)
                if ids in self.devicemanagers_by_id:
                    lab.set_device(self.devicemanagers_by_id[ids].get_device())
                else:
                    del v.items[i]
                    saveview = True
            if saveview:
                views2save.append(v)
        if views2save:
            Timer(0, partial(self.save_modified_views, views2save))

    async def save_modified_views(self, views2save):
        for v in views2save:
            rv = await v.to_db(self.db)
            _LOGGER.info(f'Saving view {v} -> {rv}')
        self.on_command_listviews()

    def create_device_managers(self):
        for d in self.devices:
            uid = self.generate_uid()
            typev = d.get_type()
            if typev in self.devicemanager_class_by_type:
                dm = self.devicemanager_class_by_type[typev](
                    self.oscer,
                    uid,
                    service=True,
                    loop=self.loop,
                    db=self.db,
                    device=d,
                    params=self.addit_params,
                    debug_params=self.debug_params.get(typev, dict()),
                    on_command_handle=self.on_event_command_handle,
                    on_state_transition=self.on_event_state_transition)
                self.devicemanagers_by_id[f'{d.get_id()}'] = dm
                self.devicemanagers_by_uid[uid] = dm
        self.set_devicemanagers_active()
        self.set_formatters_device()

    async def load_db(self):
        try:
            self.users = await User.loadbyid(self.db)
            self.devices = await Device.loadbyid(self.db)
            self.views = await View.load1m(self.db)
            # _LOGGER.debug(f'List view[0] {self.views[0]}')
        except Exception:
            _LOGGER.error(f'Load DB error {traceback.format_exc()}')

    async def start_remaining_connection_operations(self, bytimer=True):
        try:
            _LOGGER.info(f'Starting remaining con timer={bytimer}')
            if bytimer:
                self.timer_obj = None
            elif self.timer_obj:
                self.timer_obj.cancel()
                self.timer_obj = None
            for dm in self.devicemanagers_active.copy():
                info = self.devicemanagers_active_info[dm.get_uid()]
                _LOGGER.info(
                    f'Processing[{dm.get_uid()}] {dm.get_device()} -> {info["operation"]}'
                )
                if info['operation'] == 'd':
                    if dm.get_state() == DEVSTATE_CONNECTING:
                        self.oscer.send(
                            COMMAND_PRINTMSG,
                            MSG_WAITING_FOR_CONNECTING.format(
                                dm.get_device().get_alias()))
                        break
                    elif not dm.is_connected_state():
                        self.devicemanagers_active.remove(dm)
                        self.devicemanagers_active_done.append(dm)
                    else:
                        dm.disconnect()
                        break
                elif info['operation'] == 'c':
                    if dm.is_connected_state():
                        self.devicemanagers_active.remove(dm)
                        self.devicemanagers_active_done.append(dm)
                    elif bytimer:
                        dm.set_user(self.last_user)
                        if dm.connect():
                            break
                        else:
                            self.oscer.send(
                                COMMAND_PRINTMSG,
                                MSG_DEVICE_NOT_STOPPED.format(
                                    dm.get_device().get_alias()))
                    else:
                        if info['retry'] < self.connect_retry:
                            self.timer_obj = Timer(
                                0 if not info['retry'] else self.connect_secs,
                                self.start_remaining_connection_operations)
                            info['retry'] += 1
                            break
                        else:
                            _LOGGER.info(
                                f'Retry FINISH for device[{dm.get_uid()}] {dm.get_device()}'
                            )
        except Exception:
            _LOGGER.error(f'Rem op error {traceback.format_exc()}')

    def set_operation_ended(self, info):
        info['operation'] = ''
        info['retry'] = 0

    def on_event_state_transition(self, dm, oldstate, newstate, reason):
        if self.connectors_format:
            TcpClient.format(dm.get_device(), state=newstate, manager=dm)
        self.change_service_notification(dm, state=newstate, manager=dm)
        uid = dm.get_uid()
        if uid in self.devicemanagers_active_info:  # assenza significa che stiamo facendo una ricerca
            info = self.devicemanagers_active_info[uid]
            from device.manager import GenericDeviceManager
            if GenericDeviceManager.is_connected_state_s(
                    newstate) and oldstate == DEVSTATE_CONNECTING:
                if info['operation'] == 'c':
                    if dm in self.devicemanagers_active:
                        self.devicemanagers_active.remove(dm)
                        self.devicemanagers_active_done.append(dm)
                    self.set_operation_ended(info)
                Timer(
                    0,
                    partial(self.start_remaining_connection_operations,
                            bytimer=False))
            elif oldstate == DEVSTATE_CONNECTING and newstate == DEVSTATE_DISCONNECTED:
                if reason == DEVREASON_PREPARE_ERROR or reason == DEVREASON_BLE_DISABLED:
                    for dm in self.devicemanagers_active:
                        info = self.devicemanagers_active_info[dm.get_uid()]
                        self.set_operation_ended(info)
                        if dm not in self.devicemanagers_active_done:
                            self.devicemanagers_active_done.append(dm)
                    del self.devicemanagers_active[:]
                else:
                    Timer(
                        0,
                        partial(self.start_remaining_connection_operations,
                                bytimer=False))
            elif (GenericDeviceManager.is_connected_state_s(oldstate)
                  or oldstate == DEVSTATE_DISCONNECTING
                  ) and newstate == DEVSTATE_DISCONNECTED:
                oper = 'c' if info['operation'] != 'd' else 'd'
                if reason != DEVREASON_REQUESTED:
                    info['operation'] = oper
                    if dm in self.devicemanagers_active_done:
                        self.devicemanagers_active_done.remove(dm)
                    if dm not in self.devicemanagers_active:
                        self.devicemanagers_active.append(dm)
                        GenericDeviceManager.sort(self.devicemanagers_active)
                else:
                    self.set_operation_ended(info)
                    self.main_session = None
                    if dm in self.devicemanagers_active:
                        self.devicemanagers_active.remove(dm)
                        self.devicemanagers_active_done.append(dm)
                Timer(
                    0,
                    partial(self.start_remaining_connection_operations,
                            bytimer=False))

    async def init_db(self, file):
        self.db = await aiosqlite.connect(file)
        if not isinstance(self.db, aiosqlite.Connection):
            self.db = None
        else:
            self.db.row_factory = aiosqlite.Row
            modules = glob.glob(join(dirname(__file__), "..", "db", "*.py*"))
            pls = [splitext(basename(f))[0] for f in modules if isfile(f)]
            import importlib
            import inspect
            for x in pls:
                try:
                    m = importlib.import_module(f"db.{x}")
                    clsmembers = inspect.getmembers(m, inspect.isclass)
                    for cla in clsmembers:
                        query = getattr(cla[1], '__create_table_query__')
                        if query:
                            # _LOGGER.debug(f'Executing query {query}')
                            await self.db.execute(query)
                            cla[1].set_update_columns()
                    await self.db.execute('PRAGMA foreign_keys = ON')
                except Exception:
                    _LOGGER.warning(traceback.format_exc())
            await self.db.commit()

    def on_bluetooth_disabled(self, inst, wasdisabled, ok):
        self.undo_enable_operations()

    def undo_enable_operations(self):
        for nm, act in self.devicemanagers_pre_actions.items():
            if nm in self.undo_info and self.undo_info[nm]:
                del self.undo_info[nm]
                preact = act(self.loop)
                preact.undo(self.on_bluetooth_disabled)
                break
        self.stop_event.set()

    async def uninit_db(self):
        if self.db:
            await self.db.commit()
            await self.db.close()

    async def stop(self):
        self.undo_enable_operations()
        await self.stop_event.wait()
        self.oscer.uninit()
        await self.uninit_db()
        if self.android:
            self.br.stop()
        self.stop_service()

    def stop_service(self):
        if self.android:
            self.reset_service_notifications()
            self.service.stopForeground(True)
            self.service.stopSelf()
Exemplo n.º 7
0
 async def init_osc(self):
     _LOGGER.debug("Initing OSC")
     self.oscer = OSCManager('127.0.0.1', self.portlistenlocal)
     await self.oscer.init(on_init_ok=self.on_osc_init_ok)
Exemplo n.º 8
0
class BluetoothService(object):
    def __init__(self, **kwargs):
        self.oscer = None
        self.bluetooth = None
        for key in kwargs:
            setattr(self, key, kwargs[key])

    async def init_osc(self):
        _LOGGER.debug("Initing OSC")
        self.oscer = OSCManager('127.0.0.1', self.portlistenlocal)
        await self.oscer.init(on_init_ok=self.on_osc_init_ok)

    def on_osc_init_ok(self, exception=None):
        if not exception:
            _LOGGER.debug("OSC init ok")
            try:
                self.oscer.handle(COMMAND_STOP, self.on_command_stop)
                _LOGGER.debug(
                    f"Trying to construct BluetoothDispatcherWC: L({self.hostlisten}:{self.portlisten})"
                )
                self.bluetooth = BluetoothDispatcherWC(
                    portlisten=self.portlisten, hostlisten=self.hostlisten)
                _LOGGER.debug('Constructed BluetoothDispatcherWC')
            except Exception:
                _LOGGER.error(
                    f"BluetoothDispatcher construct error {traceback.format_exc()}"
                )

    def on_command_stop(self, *args):
        self.loop.stop()

    def insert_notification(self):
        from jnius import autoclass
        fim = join(dirname(__file__), '..', '..', 'images', 'bluetooth.png')
        Context = autoclass('android.content.Context')
        Color = autoclass("android.graphics.Color")
        Intent = autoclass('android.content.Intent')
        PendingIntent = autoclass('android.app.PendingIntent')
        AndroidString = autoclass('java.lang.String')
        NotificationBuilder = autoclass('android.app.Notification$Builder')
        Notification = autoclass('android.app.Notification')
        NotificationChannel = autoclass('android.app.NotificationChannel')
        NotificationManager = autoclass('android.app.NotificationManager')
        PythonActivity = autoclass('org.kivy.android.PythonActivity')
        service = autoclass('org.kivy.android.PythonService').mService

        NOTIFICATION_CHANNEL_ID = AndroidString(
            service.getPackageName().encode('utf-8'))
        channelName = AndroidString('BluetoothService'.encode('utf-8'))
        chan = NotificationChannel(NOTIFICATION_CHANNEL_ID, channelName,
                                   NotificationManager.IMPORTANCE_DEFAULT)
        chan.setLightColor(Color.BLUE)
        chan.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE)
        manager = service.getSystemService(Context.NOTIFICATION_SERVICE)
        manager.createNotificationChannel(chan)
        app_context = service.getApplication().getApplicationContext()
        notification_builder = NotificationBuilder(app_context,
                                                   NOTIFICATION_CHANNEL_ID)
        title = AndroidString("Fit.py".encode('utf-8'))
        message = AndroidString("BluetoothService".encode('utf-8'))
        # app_class = service.getApplication().getClass()
        notification_intent = Intent(app_context, PythonActivity)
        notification_intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP
                                     | Intent.FLAG_ACTIVITY_SINGLE_TOP
                                     | Intent.FLAG_ACTIVITY_NEW_TASK)
        notification_intent.setAction(Intent.ACTION_MAIN)
        notification_intent.addCategory(Intent.CATEGORY_LAUNCHER)
        intent = PendingIntent.getActivity(service, 0, notification_intent, 0)
        notification_builder.setContentTitle(title)
        notification_builder.setContentText(message)
        notification_builder.setContentIntent(intent)
        BitmapFactory = autoclass("android.graphics.BitmapFactory")
        Icon = autoclass("android.graphics.drawable.Icon")
        BitmapFactoryOptions = autoclass(
            "android.graphics.BitmapFactory$Options")
        # Drawable = jnius.autoclass("{}.R$drawable".format(service.getPackageName()))
        # icon = getattr(Drawable, 'icon')
        options = BitmapFactoryOptions()
        # options.inMutable = True
        # declaredField = options.getClass().getDeclaredField("inPreferredConfig")
        # declaredField.set(cast('java.lang.Object',options), cast('java.lang.Object', BitmapConfig.ARGB_8888))
        # options.inPreferredConfig = BitmapConfig.ARGB_8888;
        bm = BitmapFactory.decodeFile(fim, options)
        notification_builder.setSmallIcon(Icon.createWithBitmap(bm))
        notification_builder.setAutoCancel(True)
        new_notification = notification_builder.getNotification()
        # Below sends the notification to the notification bar; nice but not a foreground service.
        # notification_service.notify(0, new_noti)
        service.setAutoRestartService(False)
        service.startForeground(1, new_notification)

    async def start(self):
        _LOGGER.debug("Starting...")
        self.insert_notification()
        await self.init_osc()

    async def stop(self):
        self.oscer.uninit()
        from jnius import autoclass
        service = autoclass('org.kivy.android.PythonService').mService
        service.stopForeground(True)
        service.stopSelf()
Exemplo n.º 9
0
    class BluetoothDispatcherWC(BluetoothDispatcher):
        def __init__(self,
                     hostlisten='127.0.0.1',
                     portlisten=33217, **kwargs):
            _LOGGER.warning(f'BluetoothDispatcherWC init L({hostlisten}:{portlisten})')
            self._oscer = OSCManager(
                hostlisten=hostlisten,
                portlisten=portlisten
            )
            super(BluetoothDispatcherWC, self).__init__(**kwargs)
            _LOGGER.warning('BluetoothDispatcherWC init end')

        def _set_ble_interface(self):
            _LOGGER.warning('Set BLE Interface full')
            super(BluetoothDispatcherWC, self)._set_ble_interface()
            Timer(0, partial(
                self._oscer.init,
                on_init_ok=self.on_osc_init_ok))

        def start_scan_wrap(self, ssett, sfilt):
            ssett = json.loads(ssett)
            sfilt = json.loads(sfilt)
            self.start_scan(ssett, sfilt)

        def connect_gatt_wrap(self, devicedict):
            devicedict = json.loads(devicedict)
            self.connect_gatt(self._ble.getDevice(devicedict['address']))

        def write_descriptor_wrap(self, descriptor, value):
            self.write_descriptor(
                BluetoothDispatcherWC.descriptorfromdict(json.loads(descriptor)),
                json.loads(value)
            )

        def read_characteristic_wrap(self, characteristic):
            self.read_characteristic(
                BluetoothDispatcherWC.characteristicfromdict(json.loads(characteristic))
            )

        def write_characteristic_wrap(self, characteristic, value):
            self.write_characteristic(
                BluetoothDispatcherWC.characteristicfromdict(json.loads(characteristic)),
                value
            )

        def stop_scan_wrap(self, *args):
            _LOGGER.info('Calling stop_scan')
            self.stop_scan()

        def on_osc_init_ok(self):
            self._oscer.handle(COMMAND_WBD_CONNECTGATT, self.connect_gatt_wrap)
            self._oscer.handle(COMMAND_WBD_DISCONNECTGATT, self.close_gatt)
            self._oscer.handle(COMMAND_WBD_DISCOVERSERVICES, self.discover_services)
            self._oscer.handle(COMMAND_WBD_STARTSCAN, self.start_scan_wrap)
            self._oscer.handle(COMMAND_WBD_STOPSCAN, self.stop_scan_wrap)
            self._oscer.handle(COMMAND_WBD_WRITEDESCRIPTOR, self.write_descriptor_wrap)
            self._oscer.handle(COMMAND_WBD_WRITECHARACTERISTIC, self.write_characteristic_wrap)
            self._oscer.handle(COMMAND_WBD_READCHARACTERISTIC, self.read_characteristic_wrap)

        def on_gatt_release(self):
            """`gatt_release` event handler.
            Event is dispatched at every read/write completed operation
            """
            self._oscer.send(COMMAND_WBD_GATTRELEASE)

        def on_scan_started(self, success):
            """`scan_started` event handler

            :param success: true, if scan was started successfully
            """
            self._oscer.send(COMMAND_CONFIRM,
                             CONFIRM_OK if success else CONFIRM_FAILED_1,
                             MSG_OK if success else MSG_ERROR)

        def on_scan_completed(self):
            """`scan_completed` event handler
            """
            self._oscer.send(COMMAND_WBD_STOPSCAN_RV)

        def on_device(self, device, rssi, advertisement):
            """`device` event handler.
            Event is dispatched when device is found during a scan.

            :param device: BluetoothDevice Java object
            :param rssi: the RSSI value for the remote device
            :param advertisement: :class:`Advertisement` data record
            """
            self._oscer.send(COMMAND_WBD_DEVICEFOUND,
                             json.dumps(dict(name=device.getName(), address=device.getAddress())),
                             rssi,
                             json.dumps([x for x in advertisement.data] if advertisement.data else []))

        def on_connection_state_change(self, status, state):
            """`connection_state_change` event handler

            :param status: status of the operation,
                           `GATT_SUCCESS` if the operation succeeds
            :param state: STATE_CONNECTED or STATE_DISCONNECTED
            """
            self._oscer.send(COMMAND_WBD_CONNECTSTATECHANGE, status, state)

        @staticmethod
        def characteristic2dict(ch):
            descs = []
            descsj = ch.getDescriptors()
            for i in range(descsj.getSize()):
                descs.append(BluetoothDispatcherWC.descriptor2dict(descsj.get(i)))
            return dict(
                uuid=ch.getUuid(),
                value=ch.getValue(),
                descriptors=descs,
                permissions=ch.getPermissions(),
                properties=ch.getProperties(),
            )

        @staticmethod
        def characteristicfromdict(ch):
            BluetoothGattCharacteristic = autoclass('	android.bluetooth.BluetoothGattCharacteristic')
            bgd = BluetoothGattCharacteristic(ch['uuid'], ch['properties'], ch['permissions'])
            for i in ch['descriptors']:
                bgd.addDescriptor(BluetoothDispatcherWC.descriptorfromdict(i))
            bgd.setValue(ch['value'])
            return bgd

        @staticmethod
        def descriptor2dict(ch):
            return dict(
                uuid=ch.getUuid(),
                value=ch.getValue(),
                permissions=ch.getPermissions()
            )

        @staticmethod
        def descriptorfromdict(ch):
            BluetoothGattDescriptor = autoclass('android.bluetooth.BluetoothGattDescriptor')
            bgd = BluetoothGattDescriptor(ch['uuid'], ch['permissions'])
            if ch['value']:
                bgd.setValue(ch['value'])
            return bgd

        def on_services(self, services, status):
            """`services` event handler

            :param services: :class:`Services` dict filled with discovered
                             characteristics
                             (BluetoothGattCharacteristic Java objects)
            :param status: status of the operation,
                           `GATT_SUCCESS` if the operation succeeds
            """
            dct = dict()
            for sn, ch in services.items():
                dct[sn] = BluetoothDispatcherWC.characteristic2dict(ch)
            self._oscer.send(COMMAND_WBD_SERVICES, json.dumps(dct), status)

        def on_characteristic_changed(self, characteristic):
            """`characteristic_changed` event handler

            :param characteristic: BluetoothGattCharacteristic Java object
            """
            self._oscer.send(COMMAND_WBD_CHARACTERISTICCHANGED,
                             json.dumps(BluetoothDispatcherWC.characteristic2dict(characteristic)))

        def on_characteristic_read(self, characteristic, status):
            """`characteristic_read` event handler

            :param characteristic: BluetoothGattCharacteristic Java object
            :param status: status of the operation,
                           `GATT_SUCCESS` if the operation succeeds
            """
            self._oscer.send(COMMAND_WBD_CHARACTERISTICREAD,
                             json.dumps(BluetoothDispatcherWC.characteristic2dict(characteristic)),
                             status)

        def on_characteristic_write(self, characteristic, status):
            """`characteristic_write` event handler

            :param characteristic: BluetoothGattCharacteristic Java object
            :param status: status of the operation,
                           `GATT_SUCCESS` if the operation succeeds
            """
            self._oscer.send(COMMAND_WBD_CHARACTERISTICWRITTEN,
                             json.dumps(BluetoothDispatcherWC.characteristic2dict(characteristic)),
                             status)

        def on_descriptor_read(self, descriptor, status):
            """`descriptor_read` event handler

            :param descriptor: BluetoothGattDescriptor Java object
            :param status: status of the operation,
                           `GATT_SUCCESS` if the operation succeeds
            """
            self._oscer.send(COMMAND_WBD_DESCRIPTORREAD,
                             json.dumps(BluetoothDispatcherWC.descriptor2dict(descriptor)),
                             status)

        def on_descriptor_write(self, descriptor, status):
            """`descriptor_write` event handler

            :param descriptor: BluetoothGattDescriptor Java object
            :param status: status of the operation,
                           `GATT_SUCCESS` if the operation succeeds
            """
            self._oscer.send(COMMAND_WBD_DESCRIPTORWRITTEN,
                             json.dumps(BluetoothDispatcherWC.descriptor2dict(descriptor)),
                             status)
Exemplo n.º 10
0
class MainApp(MDApp):
    def __init__(self, *args, **kwargs):
        super(MainApp, self).__init__(*args, **kwargs)
        self.loop = asyncio.get_event_loop()
        self.pbd = PreBluetoothDispatcher(on_finish_handler=self.on_pre_finish,
                                          on_undo=self.on_undo,
                                          loop=self.loop)

    def build(self):
        """
        Build and return the root widget.
        """
        self.theme_cls.theme_style = "Light"
        self.theme_cls.primary_palette = "LightBlue"
        # The line below is optional. You could leave it out or use one of the
        # standard options, such as SettingsWithSidebar, SettingsWithSpinner
        # etc.
        self.settings_cls = SettingsWithSpinner

        # We apply the saved configuration settings or the defaults
        Builder.load_string(KV)  # (client=self.client)
        root = SimpleGUI()
        _LOGGER.debug(f'Building gui: {type(root)}')
        return root

    async def init_osc(self):
        _LOGGER.debug("GUI1: Initing OSC")
        self.oscer = OSCManager(
            hostlisten='127.0.0.1',
            portlisten=int(self.config.get('local', 'frontendport')),
            hostconnect='127.0.0.1',
            portconnect=int(self.config.get('local', 'backendport')))
        await self.oscer.init(on_init_ok=self.on_osc_init_ok,
                              on_connection_timeout=self.on_connection_timeout)

    def on_osc_init_ok(self, exception=None):
        if exception:
            toast('OSC bind error: {0}.'.format(exception) +
                  '\nWrong IP/Port?')
        else:
            toast('OSC Init OK')
            _LOGGER.debug("GUI1: OSC init ok")

    def on_connection_timeout(self, hp, is_timeout):
        if is_timeout:
            if not self.server_started:
                _LOGGER.debug("GUI1: Starting service")
                self.start_server()
            toast(f'Timeout comunicating with server ({hp[0]}:{hp[1]})')
            _LOGGER.debug(f"GUI1: OSC timeout ({hp[0]}:{hp[1]})")
            self.server_started = True
        else:
            _LOGGER.debug(f"GUI1: OSC timeout OK ({hp[0]}:{hp[1]})")
            toast(f'OSC comunication OK ({hp[0]}:{hp[1]})')

    def do_pre(self):
        _LOGGER.info("Starting scan")
        self.pbd.start_scan()

    def on_start(self):
        init_logger(__name__, get_verbosity(self.config))
        _LOGGER.debug("On Start")
        if self.check_host_port_config('frontend') and self.check_host_port_config('backend') and\
           self.check_other_config():
            _LOGGER.debug("On Start conf OK")
            self.do_pre()

    def on_pre_finish(self, success, undo, *args):
        _LOGGER.info(f"On pre init finish loop {success} / {undo}")
        if success:
            _LOGGER.debug("GUI1: Starting osc init in loop")
            Timer(0, self.init_osc)

    def on_nav_exit(self, *args, **kwargs):
        self.true_stop()

    def on_nav_settings(self, *args, **kwargs):
        self.open_settings()

    def true_stop(self):
        self.stop_server()
        self.pbd.undo_enable_operations()

    def on_undo(self, done):
        self.stop()

    def build_config(self, config):
        """
        Set the default values for the configs sections.
        """
        config.setdefaults('frontend', {'host': '127.0.0.1', 'port': 33218})
        config.setdefaults('log', {'verbosity': 'INFO'})
        config.setdefaults('local', {
            'frontendport': 9002,
            'backendport': 9001
        })
        config.setdefaults('backend', {'host': '127.0.0.1', 'port': 33217})
        self._init_fields()

    def _init_fields(self):
        self.oscer = None
        self.server_started = False

    def build_settings(self, settings):
        """
        Add our custom section to the default configuration object.
        """
        dn = join(dirname(__file__), '..', '..', 'config')
        # We use the string defined above for our JSON, but it could also be
        # loaded from a file as follows:
        #     settings.add_json_panel('My Label', self.config, 'settings.json')
        settings.add_json_panel('Backend', self.config,
                                join(dn, 'backend.json'))  # data=json)
        settings.add_json_panel('Frontend', self.config,
                                join(dn, 'frontend.json'))  # data=json)
        settings.add_json_panel('Log', self.config,
                                join(dn, 'log.json'))  # data=json)
        settings.add_json_panel('Local', self.config,
                                join(dn, 'local.json'))  # data=json)
        # settings.add_json_panel('Bluetooth', self.config, join(dn, 'bluetooth.json'))  # data=json)

    def check_host_port_config(self, name):
        host = self.config.get(name, "host")
        if not host:
            snack_open(f"{name.title()} Host cannot be empty", "Settings",
                       self.on_nav_settings)
            return False
        port = self.config.getint(name, "port")
        if not port or port > 65535 or port <= 0:
            snack_open(
                f"{name.title()} Port should be in the range [1, 65535]",
                "Settings", self.on_nav_settings)
            return False
        return True

    def check_other_config(self):
        try:
            port = int(self.config.get("local", "frontendport"))
        except Exception:
            port = -1
        if not port or port > 65535 or port <= 0:
            snack_open(
                f"Local Frontend Port should be in the range [1, 65535]",
                "Settings", self.on_nav_settings)
            return False
        try:
            port = int(self.config.get("local", "backendport"))
        except Exception:
            port = -1
        if not port or port > 65535 or port <= 0:
            snack_open(f"Local Backend Port should be in the range [1, 65535]",
                       "Settings", self.on_nav_settings)
            return False
        return True

    def start_server(self):
        if platform == 'android':
            try:
                from jnius import autoclass
                package_name = 'org.kivymfz.pymoviz.test'
                service_name = 'BluetoothService'
                service_class = '{}.Service{}'.format(package_name,
                                                      service_name.title())
                service = autoclass(service_class)
                mActivity = autoclass(
                    'org.kivy.android.PythonActivity').mActivity

                arg = dict(hostlisten=self.config.get('backend', 'host'),
                           portlisten=self.config.getint('backend', 'port'),
                           portlistenlocal=int(
                               self.config.getint('local', 'backendport')),
                           verbose=get_verbosity(self.config))
                argument = json.dumps(arg)
                _LOGGER.info("Starting %s [%s]" % (service_class, argument))
                service.start(mActivity, argument)
            except Exception:
                _LOGGER.error(traceback.format_exc())

    def stop_server(self):
        if self.oscer:
            self.oscer.send(COMMAND_STOP)
            self.oscer.uninit()

    def on_config_change(self, config, section, key, value):
        """
        Respond to changes in the configuration.
        """
        _LOGGER.info(
            "main.py: App.on_config_change: {0}, {1}, {2}, {3}".format(
                config, section, key, value))
        if self.check_host_port_config('frontend') and self.check_host_port_config('backend') and\
           self.check_host_port_config('log') and self.check_other_config():
            if self.oscer:
                snack_open(
                    "Configuration changes will be effective on restart",
                    "Quit", self.on_nav_exit)
            else:
                Timer(0, self.init_osc)

    def close_settings(self, settings=None):
        """
        The settings panel has been closed.
        """
        _LOGGER.info("main.py: App.close_settings: {0}".format(settings))
        super(MainApp, self).close_settings(settings)