def getPhonebook(self): if self.__phoneBookFacade is None: self.__phoneBookFacade = PhoneBookFacade(imagepath=__ImageCache__) setting_keys = self.__phoneBookFacade.get_setting_keys() for key in setting_keys: setting_keys[key] = __addon__.getSetting(key) self.__phoneBookFacade.set_settings(setting_keys) if self.__phonebook is None: try: self.__phonebook = self.__phoneBookFacade.getPhonebook() self.notifyLog('%s entries from %s loaded, %s images cached' % ( len(self.__phonebook), self.__server, self.__phoneBookFacade.imagecount())) except self.__phoneBookFacade.HostUnreachableException: self.notifyOSD(__LS__(30030), __LS__(30031) % (self.__server, LISTENPORT), __IconError__) except self.__phoneBookFacade.LoginFailedException: self.notifyOSD(__LS__(30033), __LS__(30034), __IconError__) except self.__phoneBookFacade.InternalServerErrorException: self.notifyOSD(__LS__(30035), __LS__(30036), __IconError__)
class FritzCallmonitor(object): __phoneBookFacade = None __phonebook = None __hide = False __s = None def __init__(self): self.PlayerProps = PlayerProperties() self.Mon = tools.Monitor() self.getPhonebook() self.ScreensaverActive = xbmc.getCondVisibility( 'System.ScreenSaverActive') HOME.setProperty('FritzCallMon.InCall', 'false') class CallMonitorLine(dict): def __init__(self, line): if isinstance(line, str): token = line.split(';') self.command = token[1] self['connection_id'] = token[2] if self.command == 'CALL': self['extension'] = token[3] self['number_used'] = token[4] self['number_called'] = token[5] self['sip'] = token[6] elif self.command == 'RING': self['date'] = token[0] self['number_caller'] = token[3] self['number_called'] = token[4] self['sip'] = token[5] elif self.command == 'CONNECT': self['date'] = token[0] self['extension'] = token[3] self['number'] = token[4] elif self.command == 'DISCONNECT': self['date'] = token[0] self['duration'] = token[3] def __getattr__(self, item): if item in self: return self[item] else: return False # END OF CLASS CallMonitorLine # # Get the Phonebook def getPhonebook(self): if self.__phoneBookFacade is None: self.__phoneBookFacade = PhoneBookFacade(imagepath=IMAGECACHE) setting_keys = self.__phoneBookFacade.get_setting_keys() for key in setting_keys: setting_keys[key] = ADDON.getSetting(key) self.__phoneBookFacade.set_settings(setting_keys) if self.__phonebook is None: try: self.__phonebook = self.__phoneBookFacade.getPhonebook() tools.writeLog( '%s entries from %s loaded, %s images cached' % (len(self.__phonebook), self.Mon.server, self.__phoneBookFacade.imagecount()), xbmc.LOGNOTICE) except self.__phoneBookFacade.HostUnreachableException: tools.writeLog('Host %s unreachable' % (self.Mon.server), level=xbmc.LOGERROR) tools.notify(LOC(30030), LOC(30031) % (self.Mon.server, LISTENPORT), ICON_ERROR) except self.__phoneBookFacade.LoginFailedException: tools.writeLog('Login failed. Check username/password', level=xbmc.LOGERROR) tools.notify(LOC(30033), LOC(30034), ICON_ERROR) except self.__phoneBookFacade.InternalServerErrorException: tools.writeLog('Internal server error', level=xbmc.LOGERROR) tools.notify(LOC(30035), LOC(30036), ICON_ERROR) def getRecordByNumber(self, request_number): name = '' imageBMP = None if isinstance(self.__phonebook, dict): for item in self.__phonebook: for number in self.__phonebook[item]['numbers']: if self.__phoneBookFacade.compareNumbers( number, request_number, ccode=self.Mon.cCode): tools.writeLog( 'Match an entry in database for %s: %s' % (tools.mask(request_number), tools.mask(item)), xbmc.LOGNOTICE) name = item fname = os.path.join( IMAGECACHE, re.sub('\D', '', number.replace('+', '00')) + '.jpg') if os.path.isfile(fname): tools.writeLog('Load image from cache', xbmc.LOGNOTICE) imageBMP = fname break return {'name': name, 'imageBMP': imageBMP} def handlePlayerProps(self, state): tools.writeLog('Handle Player Properties for state \'%s\'' % (state)) try: if self.Mon.optEarlyPause and (state == 'incoming' or state == 'outgoing'): self.PlayerProps.getConnectConditions(state) # # handle sound # if self.Mon.optMute and \ not self.PlayerProps.connCondition.get('muted', False) and \ not self.PlayerProps.connCondition.get('volChanged', False): vol = self.PlayerProps.connCondition[ 'volume'] * self.Mon.volume tools.writeLog('Change volume to %s' % (vol), xbmc.LOGNOTICE) self.PlayerProps.setVolume(vol) self.PlayerProps.connCondition['volChanged'] = True # # handle audio, video & TV # if (self.Mon.optPauseAudio and self.PlayerProps.connCondition['playAudio']) \ or (self.Mon.optPauseVideo and self.PlayerProps.connCondition['playVideo'] and not self.PlayerProps.connCondition['playTV']) \ or (self.Mon.optPauseTV and self.PlayerProps.connCondition['playTV']): tools.writeLog('Pausing audio, video or tv...', xbmc.LOGNOTICE) xbmc.executebuiltin('PlayerControl(Play)') self.PlayerProps.getCallingConditions(state) elif not self.Mon.optEarlyPause and state == 'connected': self.PlayerProps.getConnectConditions(state) # # handle sound # if self.Mon.optMute and \ not self.PlayerProps.connCondition.get('muted', False) and \ not self.PlayerProps.connCondition.get('volChanged', False): vol = self.PlayerProps.connCondition[ 'volume'] * self.Mon.volume tools.writeLog('Change volume to %s' % (vol), xbmc.LOGNOTICE) self.PlayerProps.setVolume(vol) self.PlayerProps.connCondition['volChanged'] = True # # handle audio, video & TV # if (self.Mon.optPauseAudio and self.PlayerProps.connCondition['playAudio']) \ or (self.Mon.optPauseVideo and self.PlayerProps.connCondition['playVideo'] and not self.PlayerProps.connCondition['playTV']) \ or (self.Mon.optPauseTV and self.PlayerProps.connCondition['playTV']): tools.writeLog('Pausing audio, video or tv...', xbmc.LOGNOTICE) xbmc.executebuiltin('PlayerControl(Play)') self.PlayerProps.getCallingConditions(state) elif state == 'disconnected': self.PlayerProps.getDisconnectConditions(state) # # nothing to do, all properties of disconnect are the same as connect properties # if self.PlayerProps.connCondition == self.PlayerProps.discCondition: return # # handle sound # if self.Mon.optMute and not self.PlayerProps.connCondition.get('muted', False) \ and self.PlayerProps.discCondition['volume'] != self.PlayerProps.connCondition['volume']: if self.PlayerProps.callCondition[ 'volume'] == self.PlayerProps.discCondition[ 'volume']: tools.writeLog('Volume hasn\'t changed during call', xbmc.LOGNOTICE) vol = self.PlayerProps.setVolume( self.PlayerProps.connCondition['volume']) tools.writeLog('Changed volume back to %s' % (vol), xbmc.LOGNOTICE) else: tools.writeLog( 'Volume has changed during call, don\'t change it back', xbmc.LOGNOTICE) self.PlayerProps.connCondition['volChanged'] = False # # handle audio, video & TV # if (self.Mon.optPauseAudio and self.PlayerProps.connCondition['playAudio'] and not self.PlayerProps.discCondition['playAudio']) \ or (self.Mon.optPauseVideo and self.PlayerProps.connCondition['playVideo'] and not self.PlayerProps.discCondition['playVideo']) \ or (self.Mon.optPauseTV and self.PlayerProps.connCondition['playTV'] and not self.PlayerProps.discCondition['playTV']): tools.writeLog('Resume audio, video or tv...', xbmc.LOGNOTICE) xbmc.executebuiltin('PlayerControl(Play)') else: tools.writeLog('don\'t handle properties for state %s' % state, xbmc.LOGERROR) # self.PlayerProps.getConnectConditions(state) except Exception, e: tools.writeLog( 'Error at line %s' % (str(sys.exc_info()[-1].tb_lineno)), xbmc.LOGERROR) tools.writeLog(str(type(e).__name__), xbmc.LOGERROR) tools.writeLog(e.message, level=xbmc.LOGERROR)
class FritzCallmonitor(PlayerProperties): __phoneBookFacade = None __phonebook = None __klicktel = None __hide = None __s = None def __init__(self): self.PlayerProperties = PlayerProperties() self.getSettings() self.getPhonebook() self.ScreensaverActive = xbmc.getCondVisibility( 'System.ScreenSaverActive') self.screensaver = None self.connectionEstablished = None self.userActionPlay = None self.userActionMute = None def error(*args, **kwargs): xbmc.log('%s %s' % (args, kwargs), xbmc.LOGERROR) class CallMonitorLine(dict): def __init__(self, line, **kwargs): if isinstance(line, str): token = line.split(';') self.command = token[1] self['connection_id'] = token[2] if self.command == 'CALL': self['extension'] = token[3] self['number_used'] = token[4] self['number_called'] = token[5] self['sip'] = token[6] elif self.command == 'RING': self['date'] = token[0] self['number_caller'] = token[3] self['number_called'] = token[4] self['sip'] = token[5] elif self.command == 'CONNECT': self['date'] = token[0] self['extension'] = token[3] self['number'] = token[4] elif self.command == 'DISCONNECT': self['date'] = token[0] self['duration'] = token[3] def __getattr__(self, item): if item in self: return self[item] else: return False # END OF CLASS CallMonitorLine # # Get the Addon-Settings def getSettings(self): self.__server = __addon__.getSetting('phoneserver') __exnums = __addon__.getSetting('excludeNums') # transform possible userinput from e.g. 'p1, p2,, p3 p4 ' # to a list like this: ['p1','p2','p3','p4'] __exnums = __exnums.replace(',', ' ') __exnums = __exnums.join(' '.join(line.split()) for line in __exnums.splitlines()) self.__exnum_list = __exnums.split(' ') self.__dispMsgTime = int( re.match('\d+', __addon__.getSetting('dispTime')).group()) * 1000 self.__cCode = __addon__.getSetting('cCode') self.__optShowOutgoing = True if __addon__.getSetting( 'showOutgoingCalls').upper() == 'TRUE' else False self.__optMute = True if __addon__.getSetting( 'optMute').upper() == 'TRUE' else False self.__optPauseAudio = True if __addon__.getSetting( 'optPauseAudio').upper() == 'TRUE' else False self.__optPauseVideo = True if __addon__.getSetting( 'optPauseVideo').upper() == 'TRUE' else False self.__optPauseTV = True if __addon__.getSetting( 'optPauseTV').upper() == 'TRUE' else False self.__optEarlyPause = True if __addon__.getSetting( 'optEarlyPause').upper() == 'TRUE' else False self.__useKlickTelReverse = True if __addon__.getSetting( 'useKlickTelReverse').upper() == 'TRUE' else False self.notifyLog('Settings (re)loaded', level=xbmc.LOGDEBUG) # Get the Phonebook def getPhonebook(self): if self.__phoneBookFacade is None: self.__phoneBookFacade = PhoneBookFacade(imagepath=__ImageCache__) setting_keys = self.__phoneBookFacade.get_setting_keys() for key in setting_keys: setting_keys[key] = __addon__.getSetting(key) self.__phoneBookFacade.set_settings(setting_keys) if self.__phonebook is None: try: self.__phonebook = self.__phoneBookFacade.getPhonebook() self.notifyLog('%s entries from %s loaded, %s images cached' % (len(self.__phonebook), self.__server, self.__phoneBookFacade.imagecount())) except self.__phoneBookFacade.HostUnreachableException: self.notifyOSD(__LS__(30030), __LS__(30031) % (self.__server, LISTENPORT), __IconError__) except self.__phoneBookFacade.LoginFailedException: self.notifyOSD(__LS__(30033), __LS__(30034), __IconError__) except self.__phoneBookFacade.InternalServerErrorException: self.notifyOSD(__LS__(30035), __LS__(30036), __IconError__) def getNameByKlickTel(self, request_number): if self.__useKlickTelReverse: if self.__klicktel is None: self.__klicktel = KlickTel.KlickTelReverseSearch() try: return self.__klicktel.search(request_number) except Exception as e: self.notifyLog(str(e), level=xbmc.LOGERROR) return False def getRecordByNumber(self, request_number): name = '' imageBMP = None if isinstance(self.__phonebook, dict): for item in self.__phonebook: for number in self.__phonebook[item]['numbers']: if self.__phoneBookFacade.compareNumbers( number, request_number, ccode=self.__cCode): self.notifyLog( 'Match an entry in database for %s: %s' % (request_number, item)) name = item fname = os.path.join( __ImageCache__, hashlib.md5(item.encode('utf-8')).hexdigest() + '.jpg') if os.path.isfile(fname): self.notifyLog('Load image from cache: %s' % (os.path.basename(fname))) imageBMP = fname break return {'name': name, 'imageBMP': imageBMP} def isExcludedNumber(self, exnum): self.__hide = False if exnum in self.__exnum_list: self.notifyLog('%s is excluded from monitoring, do not notify...' % (exnum)) self.__hide = True return self.__hide def handlePlayerProperties(self): self.PlayerProperties.getConnectConditions() if self.PlayerProperties.isPause != self.PlayerProperties.isConnectPause: self.userActionPlay = True if self.PlayerProperties.isMute != self.PlayerProperties.isConnectMute: self.userActionMute = True # # Save connection for handleDisconnected: # this condition determines whether the play and mute commands has to be executed again # self.connectionEstablished = True # Extra condition: only do this if the user hasn't changed the status of the player if ( self.__optPauseAudio or self.__optPauseVideo ) and not self.PlayerProperties.isPause and not self.userActionPlay: if self.__optPauseTV and self.PlayerProperties.isPlayTV: self.notifyLog('Player is playing TV, pausing...') xbmc.executebuiltin('PlayerControl(Play)') # Save the status of the player for later comparison self.PlayerProperties.isConnectPause = True elif self.__optPauseVideo and self.PlayerProperties.isPlayVideo and not self.PlayerProperties.isPlayTV: self.notifyLog('Player is playing Video, pausing...') xbmc.executebuiltin('PlayerControl(Play)') # Save the status of the player for later comparison self.PlayerProperties.isConnectPause = True elif self.__optPauseAudio and self.PlayerProperties.isPlayAudio and not self.PlayerProperties.isPlayTV: self.notifyLog('Player is playing Audio, pausing...') xbmc.executebuiltin('PlayerControl(Play)') # Save the status of the player for later comparison self.PlayerProperties.isConnectPause = True if self.__optMute and not self.PlayerProperties.isMute and not self.userActionMute: if not (self.__optPauseAudio or self.__optPauseVideo) or ( not self.__optPauseTV and self.PlayerProperties.isPlayTV): self.notifyLog('Muting Volume...') xbmc.executebuiltin('Mute') # Save the status of the player for later comparison self.PlayerProperties.isConnectMute = True def handleOutgoingCall(self, line): self.PlayerProperties.getConditions() self.connectionEstablished = False self.userActionPlay = False self.userActionMute = False if not self.isExcludedNumber(line.number_used): if self.__optShowOutgoing: record = self.getRecordByNumber(line.number_called) name = __LS__( 30012) if record['name'] == '' else record['name'] icon = __IconDefault__ if record['imageBMP'] == '' else record[ 'imageBMP'] self.notifyOSD(__LS__(30013), __LS__(30014) % (name, line.number_called), icon) self.notifyLog('Outgoing call from %s to %s' % (line.number_used, line.number_called)) def handleIncomingCall(self, line): self.PlayerProperties.getConditions() self.connectionEstablished = False self.userActionPlay = False self.userActionMute = False if not self.isExcludedNumber(line.number_called): if len(line.number_caller) > 0: caller_num = line.number_caller self.notifyLog( 'trying to resolve name from incoming number %s' % (caller_num)) record = self.getRecordByNumber(caller_num) name = record['name'] icon = __IconOk__ if record['imageBMP'] == '' else record[ 'imageBMP'] if not name: name = self.getNameByKlickTel(caller_num) if name: icon = __IconKlickTel__ else: icon = __IconUnknown__ name = __LS__(30012) else: name = __LS__(30012) caller_num = __LS__(30016) icon = __IconUnknown__ if self.__optEarlyPause: self.handlePlayerProperties() # TODO: read actual screensaver via JSON, set screensaver to None ''' query = { "jsonrpc": "2.0", "method": "Settings.getSettingValue", "params": {"setting": "screensaver.mode"}, "id": 0 } res = json.loads(xbmc.executeJSONRPC(json.dumps(query, encoding='utf-8'))) self.screensaver = res['result']['value'] query = { "jsonrpc": "2.0", "method": "Settings.setSettingValue", "params": {"setting": "screensaver.mode", "value": ""}, "id": 0 } res = json.loads(xbmc.executeJSONRPC(json.dumps(query, encoding='utf-8'))) self.notifyLog('Setting Screensaver %s to None' % (self.screensaver)) ''' self.notifyOSD(__LS__(30010), __LS__(30011) % (name, caller_num), icon, self.__dispMsgTime) self.notifyLog('Incoming call from %s (%s)' % (name, caller_num)) def handleConnected(self, line): self.notifyLog('Line connected') if not self.__hide: if not self.__optEarlyPause: self.handlePlayerProperties() def handleDisconnected(self, line): if not self.__hide: self.notifyLog('Line disconnected') # Use the conditions before connect. These are the real conditions to give back. # Check whether the status of the player has changed in the meantime by the user self.PlayerProperties.getDisconnectConditions() if self.connectionEstablished and self.PlayerProperties.isConnectPause != self.PlayerProperties.isDisconnectPause: self.userActionPlay = True if self.connectionEstablished and self.PlayerProperties.isConnectMute != self.PlayerProperties.isDisconnectMute: self.userActionMute = True # Use condition before connect. # Also, only do this if connection was established and user hasn't changed the status of the player if self.__optPauseAudio and self.PlayerProperties.isPlayAudio and not self.PlayerProperties.isPause and self.connectionEstablished and not self.userActionPlay: self.notifyLog('Player was not pausing Audio, resume...') xbmc.executebuiltin('PlayerControl(Play)') elif self.__optPauseVideo and self.PlayerProperties.isPlayVideo and not self.PlayerProperties.isPause and self.connectionEstablished and not self.userActionPlay: self.notifyLog('Player was not playing Video, resume...') xbmc.executebuiltin('PlayerControl(Play)') # Use condition before connect. # Also, only do this if connection was established and user hasn't changed the condition of the player if self.__optMute and not self.PlayerProperties.isMute and self.connectionEstablished and not self.userActionMute: # Extra condition: You don't want another condition to unmute than to mute. if not (self.__optPauseAudio or self.__optPauseVideo) or ( not self.__optPauseTV and self.PlayerProperties.isPlayTV): self.notifyLog('Volume was not muted, unmute...') xbmc.executebuiltin('Mute') # TODO: set screensaver to default module ''' query = { "jsonrpc": "2.0", "method": "Settings.setSettingValue", "params": {"setting": "screensaver.mode", "value": self.screensaver}, "id": 0 } res = json.loads(xbmc.executeJSONRPC(json.dumps(query, encoding='utf-8'))) self.notifyLog('Setting Screensaver back to %s' % (self.screensaver)) ''' else: self.notifyLog( 'excluded number seems disconnected, reset status of callmonitor' ) self.__hide = False def notifyOSD(self, header, message, icon=__IconDefault__, dispTime=5000): OSD.notification(header.encode('utf-8'), message.encode('utf-8'), icon, dispTime) def notifyLog(self, message, level=xbmc.LOGNOTICE): xbmc.log('[%s] %s' % (__addonname__, message.encode('utf-8')), level) def connect(self, notify=False): if self.__s is not None: self.__s.close() self.__s = None try: self.__s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.__s.settimeout(30) self.__s.connect((self.__server, LISTENPORT)) except socket.error as e: if notify: self.notifyOSD(__LS__(30030), __LS__(30031) % (self.__server, LISTENPORT), __IconError__) self.notifyLog('Could not connect to %s:%s' % (self.__server, LISTENPORT), level=xbmc.LOGERROR) self.notifyLog('%s' % (e), level=xbmc.LOGERROR) return False except Exception as e: self.notifyLog('%s' % (e), level=xbmc.LOGERROR) return False else: self.notifyLog('Connected, listen to %s on port %s' % (self.__server, LISTENPORT)) self.__s.settimeout(0.2) return True def start(self): if self.connect(notify=True): # MAIN SERVICE Mon = XBMCMonitor() while not Mon.abortRequested(): if Mon.settingsChanged: self.getSettings() Mon.settingsChanged = False # ToDo: investigate more from https://pymotw.com/2/select/index.html#module-select # i.e check exception handling try: fbdata = self.__s.recv(512) line = self.CallMonitorLine(fbdata) { 'CALL': self.handleOutgoingCall, 'RING': self.handleIncomingCall, 'CONNECT': self.handleConnected, 'DISCONNECT': self.handleDisconnected }.get(line.command, self.error)(line) except socket.timeout: pass except socket.error as e: self.notifyLog('No connection to %s, try to respawn' % (self.__server), level=xbmc.LOGERROR) self.notifyLog('%s' % (e), level=xbmc.LOGERROR) self.connect() except IndexError: self.notifyLog('Communication failure', level=xbmc.LOGERROR) self.connect() except Exception as e: self.notifyLog('%s' % (e), level=xbmc.LOGERROR) break self.__s.close()
class FritzCallmonitor(PlayerProperties): __phoneBookFacade = None __phonebook = None __klicktel = None __hide = None __s = None def __init__(self): self.PlayerProperties = PlayerProperties() self.getSettings() self.getPhonebook() self.ScreensaverActive = xbmc.getCondVisibility('System.ScreenSaverActive') self.screensaver = None self.connectionEstablished = None self.userActionPlay = None self.userActionMute = None def error(*args, **kwargs): xbmc.log('%s %s' % (args, kwargs), xbmc.LOGERROR) class CallMonitorLine(dict): def __init__(self, line, **kwargs): if isinstance(line, str): token = line.split(';') self.command = token[1] self['connection_id'] = token[2] if self.command == 'CALL': self['extension'] = token[3] self['number_used'] = token[4] self['number_called'] = token[5] self['sip'] = token[6] elif self.command == 'RING': self['date'] = token[0] self['number_caller'] = token[3] self['number_called'] = token[4] self['sip'] = token[5] elif self.command == 'CONNECT': self['date'] = token[0] self['extension'] = token[3] self['number'] = token[4] elif self.command == 'DISCONNECT': self['date'] = token[0] self['duration'] = token[3] def __getattr__(self, item): if item in self: return self[item] else: return False # END OF CLASS CallMonitorLine # # Get the Addon-Settings def getSettings(self): self.__server = __addon__.getSetting('phoneserver') __exnums = __addon__.getSetting('excludeNums') # transform possible userinput from e.g. 'p1, p2,, p3 p4 ' # to a list like this: ['p1','p2','p3','p4'] __exnums = __exnums.replace(',', ' ') __exnums = __exnums.join(' '.join(line.split()) for line in __exnums.splitlines()) self.__exnum_list = __exnums.split(' ') self.__dispMsgTime = int(re.match('\d+', __addon__.getSetting('dispTime')).group()) * 1000 self.__cCode = __addon__.getSetting('cCode') self.__optShowOutgoing = True if __addon__.getSetting('showOutgoingCalls').upper() == 'TRUE' else False self.__optMute = True if __addon__.getSetting('optMute').upper() == 'TRUE' else False self.__optPauseAudio = True if __addon__.getSetting('optPauseAudio').upper() == 'TRUE' else False self.__optPauseVideo = True if __addon__.getSetting('optPauseVideo').upper() == 'TRUE' else False self.__optPauseTV = True if __addon__.getSetting('optPauseTV').upper() == 'TRUE' else False self.__optEarlyPause = True if __addon__.getSetting('optEarlyPause').upper() == 'TRUE' else False self.__useKlickTelReverse = True if __addon__.getSetting('useKlickTelReverse').upper() == 'TRUE' else False self.notifyLog('Settings (re)loaded', level=xbmc.LOGDEBUG) # Get the Phonebook def getPhonebook(self): if self.__phoneBookFacade is None: self.__phoneBookFacade = PhoneBookFacade(imagepath=__ImageCache__) setting_keys = self.__phoneBookFacade.get_setting_keys() for key in setting_keys: setting_keys[key] = __addon__.getSetting(key) self.__phoneBookFacade.set_settings(setting_keys) if self.__phonebook is None: try: self.__phonebook = self.__phoneBookFacade.getPhonebook() self.notifyLog('%s entries from %s loaded, %s images cached' % ( len(self.__phonebook), self.__server, self.__phoneBookFacade.imagecount())) except self.__phoneBookFacade.HostUnreachableException: self.notifyOSD(__LS__(30030), __LS__(30031) % (self.__server, LISTENPORT), __IconError__) except self.__phoneBookFacade.LoginFailedException: self.notifyOSD(__LS__(30033), __LS__(30034), __IconError__) except self.__phoneBookFacade.InternalServerErrorException: self.notifyOSD(__LS__(30035), __LS__(30036), __IconError__) def getNameByKlickTel(self, request_number): if self.__useKlickTelReverse: if self.__klicktel is None: self.__klicktel = KlickTel.KlickTelReverseSearch() try: return self.__klicktel.search(request_number) except Exception as e: self.notifyLog(str(e), level=xbmc.LOGERROR) return False def getRecordByNumber(self, request_number): name = '' imageBMP = None if isinstance(self.__phonebook, dict): for item in self.__phonebook: for number in self.__phonebook[item]['numbers']: if self.__phoneBookFacade.compareNumbers(number, request_number, ccode=self.__cCode): self.notifyLog('Match an entry in database for %s: %s' % (request_number, item)) name = item fname = os.path.join(__ImageCache__, hashlib.md5(item.encode('utf-8')).hexdigest() + '.jpg') if os.path.isfile(fname): self.notifyLog('Load image from cache: %s' % (os.path.basename(fname))) imageBMP = fname break return {'name': name, 'imageBMP': imageBMP} def isExcludedNumber(self, exnum): self.__hide = False if exnum in self.__exnum_list: self.notifyLog('%s is excluded from monitoring, do not notify...' % (exnum)) self.__hide = True return self.__hide def handlePlayerProperties(self): self.PlayerProperties.getConnectConditions() if self.PlayerProperties.isPause != self.PlayerProperties.isConnectPause: self.userActionPlay = True if self.PlayerProperties.isMute != self.PlayerProperties.isConnectMute: self.userActionMute = True # # Save connection for handleDisconnected: # this condition determines whether the play and mute commands has to be executed again # self.connectionEstablished = True # Extra condition: only do this if the user hasn't changed the status of the player if ( self.__optPauseAudio or self.__optPauseVideo) and not self.PlayerProperties.isPause and not self.userActionPlay: if self.__optPauseTV and self.PlayerProperties.isPlayTV: self.notifyLog('Player is playing TV, pausing...') xbmc.executebuiltin('PlayerControl(Play)') # Save the status of the player for later comparison self.PlayerProperties.isConnectPause = True elif self.__optPauseVideo and self.PlayerProperties.isPlayVideo and not self.PlayerProperties.isPlayTV: self.notifyLog('Player is playing Video, pausing...') xbmc.executebuiltin('PlayerControl(Play)') # Save the status of the player for later comparison self.PlayerProperties.isConnectPause = True elif self.__optPauseAudio and self.PlayerProperties.isPlayAudio and not self.PlayerProperties.isPlayTV: self.notifyLog('Player is playing Audio, pausing...') xbmc.executebuiltin('PlayerControl(Play)') # Save the status of the player for later comparison self.PlayerProperties.isConnectPause = True if self.__optMute and not self.PlayerProperties.isMute and not self.userActionMute: if not (self.__optPauseAudio or self.__optPauseVideo) or ( not self.__optPauseTV and self.PlayerProperties.isPlayTV): self.notifyLog('Muting Volume...') xbmc.executebuiltin('Mute') # Save the status of the player for later comparison self.PlayerProperties.isConnectMute = True def handleOutgoingCall(self, line): self.PlayerProperties.getConditions() self.connectionEstablished = False self.userActionPlay = False self.userActionMute = False if not self.isExcludedNumber(line.number_used): if self.__optShowOutgoing: record = self.getRecordByNumber(line.number_called) name = __LS__(30012) if record['name'] == '' else record['name'] icon = __IconDefault__ if record['imageBMP'] == '' else record['imageBMP'] self.notifyOSD(__LS__(30013), __LS__(30014) % (name, line.number_called), icon) self.notifyLog('Outgoing call from %s to %s' % (line.number_used, line.number_called)) if self.__optEarlyPause: self.handlePlayerProperties() def handleIncomingCall(self, line): self.PlayerProperties.getConditions() self.connectionEstablished = False self.userActionPlay = False self.userActionMute = False if not self.isExcludedNumber(line.number_called): if len(line.number_caller) > 0: caller_num = line.number_caller self.notifyLog('trying to resolve name from incoming number %s' % (caller_num)) record = self.getRecordByNumber(caller_num) name = record['name'] icon = __IconOk__ if record['imageBMP'] == '' else record['imageBMP'] if not name: name = self.getNameByKlickTel(caller_num) if name: icon = __IconKlickTel__ else: icon = __IconUnknown__ name = __LS__(30012) else: name = __LS__(30012) caller_num = __LS__(30016) icon = __IconUnknown__ if self.__optEarlyPause: self.handlePlayerProperties() # TODO: read actual screensaver via JSON, set screensaver to None ''' query = { "jsonrpc": "2.0", "method": "Settings.getSettingValue", "params": {"setting": "screensaver.mode"}, "id": 0 } res = json.loads(xbmc.executeJSONRPC(json.dumps(query, encoding='utf-8'))) self.screensaver = res['result']['value'] query = { "jsonrpc": "2.0", "method": "Settings.setSettingValue", "params": {"setting": "screensaver.mode", "value": ""}, "id": 0 } res = json.loads(xbmc.executeJSONRPC(json.dumps(query, encoding='utf-8'))) self.notifyLog('Setting Screensaver %s to None' % (self.screensaver)) ''' self.notifyOSD(__LS__(30010), __LS__(30011) % (name, caller_num), icon, self.__dispMsgTime) self.notifyLog('Incoming call from %s (%s)' % (name, caller_num)) def handleConnected(self, line): self.notifyLog('Line connected') if not self.__hide: if not self.__optEarlyPause: self.handlePlayerProperties() def handleDisconnected(self, line): if not self.__hide: self.notifyLog('Line disconnected') # Use the conditions before connect. These are the real conditions to give back. # Check whether the status of the player has changed in the meantime by the user self.PlayerProperties.getDisconnectConditions() if self.connectionEstablished and self.PlayerProperties.isConnectPause != self.PlayerProperties.isDisconnectPause: self.userActionPlay = True if self.connectionEstablished and self.PlayerProperties.isConnectMute != self.PlayerProperties.isDisconnectMute: self.userActionMute = True # Use condition before connect. # Also, only do this if connection was established and user hasn't changed the status of the player if self.__optPauseAudio and self.PlayerProperties.isPlayAudio and not self.PlayerProperties.isPause and self.connectionEstablished and not self.userActionPlay: self.notifyLog('Player was not pausing Audio, resume...') xbmc.executebuiltin('PlayerControl(Play)') elif self.__optPauseVideo and self.PlayerProperties.isPlayVideo and not self.PlayerProperties.isPause and self.connectionEstablished and not self.userActionPlay: self.notifyLog('Player was not playing Video, resume...') xbmc.executebuiltin('PlayerControl(Play)') # Use condition before connect. # Also, only do this if connection was established and user hasn't changed the condition of the player if self.__optMute and not self.PlayerProperties.isMute and self.connectionEstablished and not self.userActionMute: # Extra condition: You don't want another condition to unmute than to mute. if not (self.__optPauseAudio or self.__optPauseVideo) or ( not self.__optPauseTV and self.PlayerProperties.isPlayTV): self.notifyLog('Volume was not muted, unmute...') xbmc.executebuiltin('Mute') # TODO: set screensaver to default module ''' query = { "jsonrpc": "2.0", "method": "Settings.setSettingValue", "params": {"setting": "screensaver.mode", "value": self.screensaver}, "id": 0 } res = json.loads(xbmc.executeJSONRPC(json.dumps(query, encoding='utf-8'))) self.notifyLog('Setting Screensaver back to %s' % (self.screensaver)) ''' else: self.notifyLog('excluded number seems disconnected, reset status of callmonitor') self.__hide = False def notifyOSD(self, header, message, icon=__IconDefault__, dispTime=5000): OSD.notification(header.encode('utf-8'), message.encode('utf-8'), icon, dispTime) def notifyLog(self, message, level=xbmc.LOGNOTICE): xbmc.log('[%s] %s' % (__addonname__, message.encode('utf-8')), level) def connect(self, notify=False): if self.__s is not None: self.__s.close() self.__s = None try: self.__s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.__s.settimeout(30) self.__s.connect((self.__server, LISTENPORT)) except socket.error as e: if notify: self.notifyOSD(__LS__(30030), __LS__(30031) % (self.__server, LISTENPORT), __IconError__) self.notifyLog('Could not connect to %s:%s' % (self.__server, LISTENPORT), level=xbmc.LOGERROR) self.notifyLog('%s' % (e), level=xbmc.LOGERROR) return False except Exception as e: self.notifyLog('%s' % (e), level=xbmc.LOGERROR) return False else: self.notifyLog('Connected, listen to %s on port %s' % (self.__server, LISTENPORT)) self.__s.settimeout(0.2) return True def start(self): if self.connect(notify=True): # MAIN SERVICE Mon = XBMCMonitor() while not Mon.abortRequested(): if Mon.settingsChanged: self.getSettings() Mon.settingsChanged = False # ToDo: investigate more from https://pymotw.com/2/select/index.html#module-select # i.e check exception handling try: fbdata = self.__s.recv(512) line = self.CallMonitorLine(fbdata) { 'CALL': self.handleOutgoingCall, 'RING': self.handleIncomingCall, 'CONNECT': self.handleConnected, 'DISCONNECT': self.handleDisconnected }.get(line.command, self.error)(line) except socket.timeout: pass except socket.error as e: self.notifyLog('No connection to %s, try to respawn' % (self.__server), level=xbmc.LOGERROR) self.notifyLog('%s' % (e), level=xbmc.LOGERROR) self.connect() except IndexError: self.notifyLog('Communication failure', level=xbmc.LOGERROR) self.connect() except Exception as e: self.notifyLog('%s' % (e), level=xbmc.LOGERROR) break self.__s.close()
class FritzCallmonitor(object): __phoneBookFacade = None __phonebook = None __klicktel = None __hide = False __s = None def __init__(self): self.PlayerProps = PlayerProperties() self.Mon = tools.Monitor() self.getPhonebook() self.ScreensaverActive = xbmc.getCondVisibility( 'System.ScreenSaverActive') self.screensaver = None class CallMonitorLine(dict): def __init__(self, line, **kwargs): if isinstance(line, str): token = line.split(';') self.command = token[1] self['connection_id'] = token[2] if self.command == 'CALL': self['extension'] = token[3] self['number_used'] = token[4] self['number_called'] = token[5] self['sip'] = token[6] elif self.command == 'RING': self['date'] = token[0] self['number_caller'] = token[3] self['number_called'] = token[4] self['sip'] = token[5] elif self.command == 'CONNECT': self['date'] = token[0] self['extension'] = token[3] self['number'] = token[4] elif self.command == 'DISCONNECT': self['date'] = token[0] self['duration'] = token[3] def __getattr__(self, item): if item in self: return self[item] else: return False # END OF CLASS CallMonitorLine # # Get the Phonebook def getPhonebook(self): if self.__phoneBookFacade is None: self.__phoneBookFacade = PhoneBookFacade(imagepath=__ImageCache__) setting_keys = self.__phoneBookFacade.get_setting_keys() for key in setting_keys: setting_keys[key] = __addon__.getSetting(key) self.__phoneBookFacade.set_settings(setting_keys) if self.__phonebook is None: try: self.__phonebook = self.__phoneBookFacade.getPhonebook() tools.writeLog( '%s entries from %s loaded, %s images cached' % (len(self.__phonebook), self.Mon.server, self.__phoneBookFacade.imagecount()), xbmc.LOGNOTICE) except self.__phoneBookFacade.HostUnreachableException: tools.writeLog('Host %s unreachable' % (self.Mon.server), level=xbmc.LOGERROR) tools.notify(__LS__(30030), __LS__(30031) % (self.Mon.server, LISTENPORT), __IconError__) except self.__phoneBookFacade.LoginFailedException: tools.writeLog('Login failed. Check username/password', level=xbmc.LOGERROR) tools.notify(__LS__(30033), __LS__(30034), __IconError__) except self.__phoneBookFacade.InternalServerErrorException: tools.writeLog('Internal server error', level=xbmc.LOGERROR) tools.notify(__LS__(30035), __LS__(30036), __IconError__) def getNameByKlickTel(self, request_number): if self.Mon.useKlickTelReverse: if self.__klicktel is None: self.__klicktel = KlickTel.KlickTelReverseSearch() try: return self.__klicktel.search(request_number) except Exception, e: tools.writeLog('Error at line %s' % (sys.exc_info()[-1].tb_lineno)) tools.writeLog(e.message, level=xbmc.LOGERROR) return False