Exemple #1
0
 def __init__(self, state):
     self.state = state
     self.cal_check = CalendarCheck(self.state)
     self.config = ConfigControls()
     '''Config'''
     self.c = self.get_config()
     self.RING_TIME_THRESHOLD = 15  #[seconds]
Exemple #2
0
 def __init__(self, bot):
     '''Config'''
     self.max_msg_line_length = 29
     self.max_reply_line_length = 28
     '''Initializations'''
     self.config = ConfigControls()
     self.mopidy = MopidyControls(self)
     self.mopidy.refresh_token()
     self.alarm = AlarmControls(self)
     self.cal_check = CalendarCheck(self)
     self.font = AsciiFont()
     self.bot = bot
     self.default_volume = self.get_default_control("volume")
     self.mopidy.set_volume_sys(self.default_volume)
     self.set_reply_text('')
     '''General variables'''
     self.state = 'music'
     self.dashboard_type = 'music'
     self.keyboard_type = 'home home'
     self.music_status = self.mopidy.get_status()
     self.last_dash_msg = [False]
     self.last_reply_msg = [False]
     self.chat_id_list = []
     self.chat_password = self.get_password()
     self.auto_refresh_enabled = True
     self.week_events = [[], [], [], [], [], [], []]
     self.set_alarm_day = 0
     self.snooze_counts = 0
     self.set_config_name = ' '
     self.alarm_to_edit = [['manual', 'disabled'],
                           datetime.time(hour=0, minute=0)]
     '''Search stored variables'''
     self.search_type = False
     self.search_query = False
     self.options_list = False
     self.uri_list = False
     self.uri = False
Exemple #3
0
class AlarmControls:
    def __init__(self, state):
        self.state = state
        self.cal_check = CalendarCheck(self.state)
        self.config = ConfigControls()
        '''Config'''
        self.c = self.get_config()
        self.RING_TIME_THRESHOLD = 15  #[seconds]

    def ring(self):
        '''Plays the alarm music'''
        self.c = self.get_config(
        )  # probably is better to refresh config in main
        while self.state.auto_refresh_enabled:
            self.state.auto_refresh_enabled = False
        self.state.music_status.mopidy_playing = True
        self.state.mopidy.clear()
        self.state.mopidy.repeat('on')
        self.state.mopidy.random('on')
        if not self.state.mopidy.load(self.c.ring_music, 'uri'):
            logger.warning('Local file loaded for ringing')
            self.state.mopidy.load_local_song()
        self.state.mopidy.shuffle()
        self.state.mopidy.play()
        self.state.mopidy.set_volume_sys(int(self.c.ring_start_volume))
        self.state('ringing')
        self.state.keyboard_type = 'ringing'
        self.state.refresh_dashboard()

    def set_ring_volume(self, ringing_time):
        '''Sets the ring volume according to two linear relationships, one for the first
           half volume augment and the other until the maximum ringing volume'''
        if ringing_time <= self.c.half_ring_volume_sec:
            vol_slope = ((
                (self.c.max_ringing_volume - self.c.ring_start_volume) / 2) /
                         self.c.half_ring_volume_sec)
            volume = int(self.c.ring_start_volume + vol_slope * ringing_time)
        elif ringing_time <= self.c.max_ring_volume_sec:
            vol_slope = (
                ((self.c.max_ringing_volume - self.c.ring_start_volume) / 2) /
                (self.c.max_ring_volume_sec - self.c.half_ring_volume_sec))
            volume = int(
                (self.c.max_ringing_volume + self.c.ring_start_volume) / 2 +
                vol_slope * (ringing_time - self.c.half_ring_volume_sec))
        else:
            volume = int(self.c.max_ringing_volume)
        volume = min(volume, int(self.c.max_ringing_volume))
        self.state.mopidy.set_volume_sys(volume)

    def get_alarms(self):
        '''Gets the alarm string array from the saved file'''
        file = open(os.path.join(self.config.path, "saved_alarms.py"), "rb")
        alarms = pickle.load(file)
        file.close()
        return alarms

    def get_config(self):
        try:
            text_config = self.config.get_section('Alarm Controls')
            config_name_list = []
            for line in text_config:
                config_name_list.append(
                    line.split('=')[0].split('(')[0].strip())
            config_list = namedtuple('config_list', ' '.join(config_name_list))
            for line in text_config:
                for config_name in config_list._fields:
                    if config_name in line:
                        config_value = line.split(' = ')[1].strip()
                        if config_value.isdigit():
                            config_value = int(config_value)
                        setattr(config_list, config_name, config_value)
            return config_list
        except:
            # Put here the default control values
            logger.error(
                "Bad AlarmControls config, default config applied: {}".format(
                    sys._getframe().f_code.co_name))
            return config_list(
                10, 30, 70, 14, 35, 30, 60, 10, 6, 10, "I am awake",
                'spotify:user:2155eprgg73n7x46ybpzpycra:playlist:64xSAgfPBl8HjIKJBi3CIi'
            )

    def get_config_units(self, config_name):
        if 'time' in config_name or 'min' in config_name:
            units = '(minutes)'
        elif 'sec' in config_name:
            units = '(seconds)'
        elif 'volume' in config_name:
            units = '(%)'
        elif 'counts' in config_name:
            units = '(-)'
        else:
            units = ''
        return units

    def set_alarms(self, alarms):
        '''Saves the alarm string array to the file'''
        file = open(os.path.join(self.config.path, "saved_alarms.py"), "wb")
        pickle.dump(alarms, file)
        file.close()
        return

    def set_config(self, config_name, config_value):
        '''Saves the config parameter in the text file and checks the value'''
        if 'ring_music' in config_name:
            if config_value.isdigit():
                return 'Bad URI format'
        elif 'alarm_stop_password' in config_name:
            if len(config_value) > 100:
                return "Don't be silly"
            elif '/' in config_value:
                return 'invalid key "/"'
        else:
            if not config_value.strip().isdigit():
                return 'Must be a positive number'
            else:
                if ('snooze_min' in config_name
                        or 'max_snooze_counts' in config_name):
                    if int(config_value) == 0:
                        return 'Must be greater than 0'
                    if 'snooze_min' in config_name:
                        if int(config_value) > 30:
                            return 'Snooze up to 30 min'
                    elif 'max_snooze_counts' in config_name:
                        if int(config_value) > 1000:
                            return "Don't be silly"
                elif ('ring_start_volume' in config_name
                      or 'max_ringing_volume' in config_name):
                    if int(config_value) > 100:
                        return 'Up to 100%'
                    if 'ring_start_volume' in config_name:
                        if int(config_value) > int(self.c.max_ringing_volume):
                            return "Start Vol(<)Max Vol"
                    elif 'max_ringing_volume' in config_name:
                        if int(config_value) < int(self.c.ring_start_volume):
                            return "Max Vol(>)Start Vol"
                elif 'max_ringing_time' in config_name:
                    if int(config_value) < 5 or int(config_value) > 60:
                        return 'Between 5 and 60'
                elif 'event_time_before' in config_name:
                    #first_event_time_before and other_event_time_before
                    if int(config_value) > 360:
                        return 'Up to 6 hours before'
                elif 'ring_volume_sec' in config_name:
                    #max_ring_volume_time and half_ring_volume_time
                    if int(config_value) > 180:
                        return 'Up to 3 minutes'
                    elif 'max' in config_name:
                        if int(config_value) < int(
                                self.c.half_ring_volume_sec):
                            return 'Max Time(>)Half Time'
                    elif 'half' in config_name:
                        if int(config_value) > int(self.c.max_ring_volume_sec):
                            return 'Half Time(<)Max Time'
                elif 'ring_refresh_sec' in config_name:
                    if int(config_value) > 60 or int(config_value < 5):
                        return 'Betweeen 5 and 60 seconds'
                else:
                    return 'Config name not recognized!'
        unit = self.get_config_units(config_name)
        if unit == '':
            setattr(self.c, config_name, str(config_value))
        else:
            setattr(self.c, config_name, int(config_value))
        config_list = []
        for name in self.c._fields:
            value = getattr(self.c, name)
            units = self.get_config_units(name)
            if isinstance(value, int):
                value = str(value)
            config_list.append(name + ' ' + units + ' = ' + value)
        self.config.set_section('Alarm Controls', config_list)
        return 'Your wishes are orders'

    def is_config(self, config_name):
        '''Says if a name is a configuration name'''
        for name in self.c._fields:
            if config_name in name:
                return True
        return False

    def config_options_list(self, config_name):
        options_list = []
        if 'ring_music' in config_name:
            return False
        else:
            if 'snooze_min' in config_name:
                options_list = list(range(5, 35, 5))
            elif 'max_snooze_counts' in config_name:
                options_list = list(range(1, 11, 1))
            elif 'ring_start_volume' in config_name:
                options_list = list(range(0, int(self.c.max_ringing_volume),
                                          5))
            elif 'max_ringing_volume' in config_name:
                options_list = list(
                    range(int(self.c.ring_start_volume), 105, 5))
            elif 'max_ringing_time' in config_name:
                options_list = list(range(5, 35, 5))
            elif 'event_time_before' in config_name:
                #first_event_time_before and other_event_time_before
                options_list = list(range(0, 135, 15))
            elif 'alarm_stop_password' in config_name:
                options_list = ["I am awake"]
            elif 'half_ring_volume_sec' in config_name:
                options_list = list(
                    range(0, int(self.c.max_ring_volume_sec), 2))
            elif 'max_ring_volume_sec' in config_name:
                options_list = list(
                    range(int(self.c.half_ring_volume_sec), 60, 5))
            elif 'ring_refresh_sec' in config_name:
                options_list = list(range(10, 70, 10))
            else:
                return False
        return options_list

    def temporizer_options_list(self):
        temp_list = list(range(5, 30, 5))
        temp_list.extend(list(range(30, 75, 15)))
        return temp_list

    def alarm_str2time(self, alarm_str):
        ''' Format the alarm string to time '''
        separator = alarm_str.find('-')
        if separator >= 0:
            alarm_type = alarm_str[:separator]
            alarm_type = alarm_type.split(' ')
            alarm_time = alarm_str[(separator + 1):]
            alarm_time = datetime.datetime.strptime(alarm_time, '%c')
            return [alarm_type, alarm_time]
        else:
            logging.error("Bad alarm format: {} ".format(
                sys._getframe().f_code.co_name))
            return [False, False]

    def alarm_time2str(self, alarm_type, alarm_time):
        ''' Format the alarm time to string '''
        alarm_type_str = ' '.join(alarm_type)
        alarm_time_str = alarm_time.strftime('%c')
        alarm_str = '-'.join([alarm_type_str, alarm_time_str])
        return alarm_str

    def to_localtime(self, d):
        '''Localtime defined by the raspberry localtime'''
        utcoffset = datetime.datetime.now(
            datetime.timezone.utc).astimezone().tzinfo.utcoffset(None)
        if d is not None:
            return d + utcoffset
        else:
            return None

    def to_utc(self, d):
        utcoffset = datetime.datetime.now(
            datetime.timezone.utc).astimezone().tzinfo.utcoffset(None)
        if d is not None:
            return d - utcoffset
        else:
            return None

    def set_auto_alarms(self, week_events):
        '''Sets the alarms acording to the first event of the day'''
        alarms = self.get_alarms()
        now_local = self.to_localtime(datetime.datetime.utcnow())
        today = now_local.date()
        new_alarms = []
        first_event_time_before = datetime.timedelta(
            minutes=int(self.c.first_event_time_before))
        auto_alarm_disabled = [False, False, False, False, False, False, False]
        for alarm in alarms:
            [alarm_type, alarm_time] = self.alarm_str2time(alarm)
            if alarm_time.date() - today >= datetime.timedelta(0):
                if 'auto' in alarm_type[0]:
                    if 'enabled' in alarm_type[1]:
                        pass  # We delete enabled auto alarms and update them later
                    else:  #We mark the disabled auto alarms to chekc update
                        days_to_go = (self.to_localtime(alarm_time).weekday() -
                                      today.weekday())
                        if days_to_go < 0:
                            days_to_go += 7
                        auto_alarm_disabled[days_to_go] = alarm_time
                else:
                    # We keep all manual alarms not outdated
                    new_alarm = self.alarm_time2str(alarm_type, alarm_time)
                    new_alarms.append(new_alarm)
            else:
                pass  #if the alarm is outdated we delete it
        first_events_localtime = self.cal_check.get_day_first_events_localtime(
            week_events)
        first_events = [self.to_utc(item) for item in first_events_localtime]
        for day, event in enumerate(first_events):
            if event is not None:  # if there is an event we update the alarm
                if auto_alarm_disabled[day]:
                    if event - first_event_time_before < auto_alarm_disabled[
                            day]:
                        # if the event is earlier than the diabled event we enable it
                        new_alarm = self.alarm_time2str(
                            ['auto', 'enabled'],
                            event - first_event_time_before)
                        new_alarms.append(new_alarm)
                    else:
                        # if the event is later than the disabled event we keep it disabled
                        new_alarm = self.alarm_time2str(
                            ['auto', 'disabled'],
                            event - first_event_time_before)
                        new_alarms.append(new_alarm)
                else:  # if the event was enabled or the event is new we keep it enabled
                    new_alarm = self.alarm_time2str(
                        ['auto', 'enabled'], event - first_event_time_before)
                    new_alarms.append(new_alarm)
            else:  #if there is no event now we delete the auto alarm
                pass
        new_alarms = list(set(new_alarms))  #This removes the duplicates
        self.set_alarms(new_alarms)
        return True

    def build_temporizer_timedelta(self, temp_minutes):
        now = datetime.datetime.utcnow()
        return datetime.timedelta(0,
                                  hours=now.hour,
                                  minutes=now.minute + temp_minutes)

    def set_manual_alarm(self, set_datetime):
        '''Sets a manual alarm some days after at a desired time
           set_datetime = datetime.timedelta(day_increase,set_time)
           '''
        today = datetime.datetime.utcnow().date()
        new_alarm = (datetime.datetime.combine(today, datetime.time(0)) +
                     set_datetime)
        repeated = False
        alarms = self.get_alarms()
        for alarm in alarms:
            [alarm_type, alarm_time] = self.alarm_str2time(alarm)
            if abs((alarm_time - new_alarm).total_seconds()) <= 60:
                repeated = True
        if not repeated:
            new_alarm_str = self.alarm_time2str(['manual', 'enabled'],
                                                new_alarm)
            alarms.append(new_alarm_str)
            self.set_alarms(alarms)
            return
        else:
            return

    def set_manual_alarm_localtime(self, set_datetime):
        '''Sets a manual alarm some days after at a desired time on the local
           timezone
           set_datetime = datetime.timedelta(day_increase,set_time)
           '''
        utcoffset = datetime.datetime.now(
            datetime.timezone.utc).astimezone().tzinfo.utcoffset(None)
        today = (datetime.datetime.utcnow() + utcoffset).date()
        new_alarm = (datetime.datetime.combine(today, datetime.time(0)) +
                     set_datetime - utcoffset)
        repeated = False
        alarms = self.get_alarms()
        for alarm in alarms:
            [alarm_type, alarm_time] = self.alarm_str2time(alarm)
            if abs((alarm_time - new_alarm).total_seconds()) <= 60:
                repeated = True
        if not repeated:
            new_alarm_str = self.alarm_time2str(['manual', 'enabled'],
                                                new_alarm)
            alarms.append(new_alarm_str)
            self.set_alarms(alarms)
            return
        else:
            return

    def alarm_check(self):
        '''Checks if an alarm time has been reached and rings acordingly'''
        alarms = self.get_alarms()
        now = datetime.datetime.utcnow()
        min_time_to_ring = 24 * 60 * 60
        for alarm in alarms:
            [alarm_type, alarm_time] = self.alarm_str2time(alarm)
            ''' Check only enabled alarms '''
            if 'enabled' in alarm_type[1]:
                time_to_ring = (alarm_time - now).total_seconds()
                if (time_to_ring <= self.RING_TIME_THRESHOLD
                        and (now - alarm_time).total_seconds() <= 20 * 60):
                    self.ring()
                    return 0
                if time_to_ring < 0:
                    time_to_ring = min_time_to_ring
                min_time_to_ring = min(min_time_to_ring, time_to_ring)
        return min_time_to_ring

    def edit_closest_alarm(self, action):
        '''Edits the closest alarm acording to the input action,
           useful for disable the alarm that is ringing now or activate snooze mode
           '''
        alarms = self.get_alarms()
        now = datetime.datetime.utcnow()
        success = False
        for index, alarm in enumerate(alarms):
            [alarm_type, alarm_time] = self.alarm_str2time(alarm)
            ''' Check only enabled alarms '''
            if 'enabled' in alarm_type[1]:
                if 'disable' in action:
                    if ((alarm_time - now).total_seconds() <= 11 * 60
                            and (now - alarm_time).total_seconds() <= 20 * 60):
                        alarm_type[1] = 'disabled'
                        success = True
                        logger.info('Alarm disabled')
                elif 'snooze' in action:
                    if ((alarm_time - now).total_seconds() <= 60
                            and (now - alarm_time).total_seconds() <= 20 * 60):
                        if 'manual' in alarm_type[0]:
                            alarm_time = alarm_time + datetime.timedelta(
                                minutes=self.c.snooze_min)
                            logger.info('Manual alarm snooze')
                        else:  # Disable auto alarm and set manual alarm
                            alarm_type[1] = 'disabled'
                            new_alarm_time = alarm_time + datetime.timedelta(
                                minutes=self.c.snooze_min)
                            new_alarm_type = ['manual', 'enabled']
                            alarms.append(
                                self.alarm_time2str(new_alarm_type,
                                                    new_alarm_time))
                            logger.info('Auto alarm snooze')
                        success = True
                alarms[index] = self.alarm_time2str(alarm_type, alarm_time)
        self.set_alarms(alarms)
        return success

    def edit_alarm(self, days_to_go, alarm_time, action, change_time=None):
        '''Edits an alarm configuration acording to the input action'''
        today = self.to_localtime(datetime.datetime.utcnow())
        edit_alarm_time = datetime.datetime.combine(
            today + datetime.timedelta(days=days_to_go), alarm_time)
        alarms = self.get_alarms()
        new_alarms = []
        success = False
        for alarm in alarms:
            [alarm_type, alarm_time] = self.alarm_str2time(alarm)
            if abs((alarm_time - edit_alarm_time).total_seconds()) < 60:
                if 'toggle' in action:
                    if 'disabled' in alarm_type[1]:
                        alarm_type[1] = 'enabled'
                    else:
                        alarm_type[1] = 'disabled'
                elif 'change' in action:
                    if change_time is None:
                        logger.error("Bad imput: {}".format(
                            sys._getframe().f_code.co_name))
                        success = False
                        return success
                    else:
                        if 'manual' in alarm_type[0]:
                            alarm_time = datetime.datetime.combine(
                                today + datetime.timedelta(days=days_to_go),
                                change_time)
                        else:  #When changing auto alarms, disable auto and create new manual
                            alarm_type[1] = 'disabled'
                            new_alarm_time = datetime.datetime.combine(
                                today + datetime.timedelta(days=days_to_go),
                                change_time)
                            new_alarms.append(
                                self.alarm_time2str(['manual', 'enabled'],
                                                    new_alarm_time))
                if not 'delete' in action:
                    new_alarms.append(
                        self.alarm_time2str(alarm_type, alarm_time))
                success = True
            else:
                new_alarms.append(self.alarm_time2str(alarm_type, alarm_time))
        self.set_alarms(new_alarms)
        return success

    def get_week_alarms_localtime(self):
        '''Gets a list of alarms ordered by day, and time,
           today alarms are on week_alarms[0], today first alarm is on
           week_alarms[0][0], tomorrow is on week_alarms[1], etc.
           '''
        today = self.to_localtime(datetime.datetime.utcnow())
        alarms = self.get_alarms()
        week_alarms = [[], [], [], [], [], [], []]
        for alarm in alarms:
            [alarm_type, alarm_time] = self.alarm_str2time(alarm)
            alarm_time = self.to_localtime(alarm_time)
            '''Check within a week'''
            if ((alarm_time.date() - today.date() >= datetime.timedelta(0)) and
                (alarm_time.date() - today.date() < datetime.timedelta(7))):
                weekday = alarm_time.weekday()
                week_alarms[weekday].append([alarm_type, alarm_time.time()])
        sorted_alarms = [[], [], [], [], [], [], []]
        for weekday, day in enumerate(week_alarms):
            sorted_alarms[weekday] = sorted(day, key=lambda day: day[1])
        week_alarms = numpy.roll(sorted_alarms, -today.weekday()).tolist()
        return week_alarms

    def build_msg_text_from_alarms(self):
        '''Builds a msg_txt from the alarms set on the week
           in a very specific format, shows also the alarm type
           '''
        today = self.to_localtime(datetime.datetime.utcnow())
        today_weekday = today.weekday()
        week_alarms = self.get_week_alarms_localtime()
        _type = 0
        _time = 1
        max_length = 0
        for day in week_alarms:
            if len(day) > max_length:
                max_length = len(day)
        alarm_list = []
        for x in range(0, max_length):
            hours_list = []
            minut_list = []
            type_list = []
            for day in week_alarms:
                if x < len(day):
                    if 'disabled' in day[x][_type][1]:
                        hours_list.append(day[x][_time].strftime("x%H|"))
                    else:
                        hours_list.append(day[x][_time].strftime(" %H|"))
                    minut_list.append(day[x][_time].strftime(":%M|"))
                    if 'auto' in day[x][_type][0]:
                        type_list.append('-A-|')
                    else:
                        type_list.append('---|')
                else:
                    hours_list.append('   |')
                    minut_list.append('   |')
                    type_list.append('---|')
            hours_str = ''.join(hours_list)
            minut_str = ''.join(minut_list)
            type_str = ''.join(type_list)
            alarm_list.append('`' + type_str.strip('|') + '`')
            alarm_list.append('`' + hours_str.strip('|') + '`')
            alarm_list.append('`' + minut_str.strip('|') + '`')
        num_list = []
        month_list = []
        for x in range(0, 7):
            day = today + datetime.timedelta(x)
            month_list.append(day.strftime('%B')[:3] + '|')
            num_list.append(day.strftime(' %d|'))
        name_list = ['Mon|', 'Tue|', 'Wed|', 'Thu|', 'Fri|', 'Sat|', 'Sun|']
        name_list = numpy.roll(name_list, -today_weekday).tolist()
        msg_text = ''.join([
            '`' + ''.join(name_list).strip('|') + '`' + '\n',
            '\n'.join(alarm_list) + '\n',
            '`' + ''.join(month_list).strip('|') + '`' + '\n',
            '`' + ''.join(num_list).strip('|') + '`'
        ])
        t = self.alarm_check()
        hours = str(int(t / 3600) % 24)
        if len(hours) < 2:
            hours = '0' + hours
        minutes = str(int(t / 60) % 60)
        if len(minutes) < 2:
            minutes = '0' + minutes
        last_line = ('`--`' + u'\U0001F567' + u'\U0001F552' + u'\U000023F3' +
                     u'\U000027A1' + '` `' + hours[0] + u'\U000020E3' +
                     hours[1] + u'\U000020E3' + '*:*' + minutes[0] +
                     u'\U000020E3' + minutes[1] + u'\U000020E3' +
                     u'\U0001F555' + u'\U0001F55B' + '`--`')
        msg_text = '\n'.join([msg_text, last_line])
        return msg_text
Exemple #4
0
class State(object):
    def __init__(self, bot):
        '''Config'''
        self.max_msg_line_length = 29
        self.max_reply_line_length = 28
        '''Initializations'''
        self.config = ConfigControls()
        self.mopidy = MopidyControls(self)
        self.mopidy.refresh_token()
        self.alarm = AlarmControls(self)
        self.cal_check = CalendarCheck(self)
        self.font = AsciiFont()
        self.bot = bot
        self.default_volume = self.get_default_control("volume")
        self.mopidy.set_volume_sys(self.default_volume)
        self.set_reply_text('')
        '''General variables'''
        self.state = 'music'
        self.dashboard_type = 'music'
        self.keyboard_type = 'home home'
        self.music_status = self.mopidy.get_status()
        self.last_dash_msg = [False]
        self.last_reply_msg = [False]
        self.chat_id_list = []
        self.chat_password = self.get_password()
        self.auto_refresh_enabled = True
        self.week_events = [[], [], [], [], [], [], []]
        self.set_alarm_day = 0
        self.snooze_counts = 0
        self.set_config_name = ' '
        self.alarm_to_edit = [['manual', 'disabled'],
                              datetime.time(hour=0, minute=0)]
        '''Search stored variables'''
        self.search_type = False
        self.search_query = False
        self.options_list = False
        self.uri_list = False
        self.uri = False

    def __call__(self, state=None):
        if state is None:
            return self.state
        else:
            self.state = state
            logger.info('State changed to ' + state)

    def get_password(self):
        '''Returns the password from the config file'''
        password = self.config.get_section('Password')
        if len(password) > 1:
            logger.warning('Only first chat password is accepted')
        return password[0].strip(' ')

    def get_default_control(self, control):
        '''Returns the desired default control from the config file'''
        text = self.config.get_section('Default Controls')
        for line in text:
            if control in line:
                default_control = line.split(" = ")[1]
                if default_control.isdigit():
                    default_control = int(default_control)
                return default_control
        return False

    def set_default_control(self, control, value):
        '''Changesthe desired default control in the config file'''
        text = self.config.get_section('Default Controls')
        new_text = []
        for line in text:
            if control in line:
                new_text.append(control + ' = ' + str(value))
            else:
                new_text.append(line)
        self.config.set_section('Default Controls', new_text)
        return True

    def dashboard(self):
        '''Selects a dashboard to output'''
        dashboard_type = self.dashboard_type
        if 'music' in dashboard_type:
            status = self.music_status
            if status.random:
                random_indicator = u'\U00002714 \U0001F500'
            else:
                random_indicator = u'\U0000274C \U0001F500'
            if status.repeat:
                repeat_indicator = u'\U00002714 \U0001F501'
            else:
                repeat_indicator = u'\U0000274C \U0001F501'
            if status.playing:
                play_indicator = u'\U000023F8'
            else:
                play_indicator = u'\U000025B6'
            keyboard = InlineKeyboardMarkup(inline_keyboard=[
                [
                    InlineKeyboardButton(text=u'\U000023EE',
                                         callback_data='/prev'),
                    InlineKeyboardButton(text=play_indicator,
                                         callback_data='/play'),
                    InlineKeyboardButton(text=u'\U000023ED',
                                         callback_data='/next')
                ],
                [
                    InlineKeyboardButton(text=u'\U000023EA',
                                         callback_data='/step_prev'),
                    InlineKeyboardButton(text=u'\U000023F9',
                                         callback_data='/stop'),
                    InlineKeyboardButton(text=u'\U000023E9',
                                         callback_data='/step_next')
                ],
                [
                    InlineKeyboardButton(text=random_indicator,
                                         callback_data='/random_mode'),
                    InlineKeyboardButton(text=u'\U0001F508 \U0001F53B',
                                         callback_data='/volume_down'),
                    InlineKeyboardButton(text=u'\U0001F50A \U0001F53A',
                                         callback_data='/volume_up'),
                    InlineKeyboardButton(text=repeat_indicator,
                                         callback_data='/repeat_mode')
                ],
            ])
        elif 'ring' in dashboard_type:
            keyboard = InlineKeyboardMarkup(inline_keyboard=[
                [
                    InlineKeyboardButton(text=u'\U0001F634' + '/snooze' +
                                         u'\U0001F634',
                                         callback_data='/snooze')
                ],
            ])
        elif 'snooze' in dashboard_type:
            keyboard = InlineKeyboardMarkup(inline_keyboard=[
                [
                    InlineKeyboardButton(text='refresh time counter',
                                         callback_data='/time')
                ],
            ])
        elif 'alarm view' in dashboard_type:
            keyboard = InlineKeyboardMarkup(inline_keyboard=[
                [
                    InlineKeyboardButton(text=u'\U0001F527',
                                         callback_data='/set_alarm:0'),
                    InlineKeyboardButton(text=u'\U0001F527',
                                         callback_data='/set_alarm:1'),
                    InlineKeyboardButton(text=u'\U0001F527',
                                         callback_data='/set_alarm:2'),
                    InlineKeyboardButton(text=u'\U0001F527',
                                         callback_data='/set_alarm:3'),
                    InlineKeyboardButton(text=u'\U0001F527',
                                         callback_data='/set_alarm:4'),
                    InlineKeyboardButton(text=u'\U0001F527',
                                         callback_data='/set_alarm:5'),
                    InlineKeyboardButton(text=u'\U0001F527',
                                         callback_data='/set_alarm:6'),
                    InlineKeyboardButton(text=u'\U00002699',
                                         callback_data='/change_config'),
                ],
            ])
        elif 'alarm config' in dashboard_type:
            inline_keyboard_array = []
            config_list = self.alarm.get_config()
            for name in config_list._fields:
                if not 'ring_music' in name:
                    value = getattr(config_list, name)
                    units = self.alarm.get_config_units(name)
                    if isinstance(value, int):
                        value = str(value)
                    button_text = u'\U00002699' + name + ' = ' + value + units
                    inline_keyboard_array.append([
                        InlineKeyboardButton(text=button_text,
                                             callback_data=name)
                    ])
            keyboard = InlineKeyboardMarkup(
                inline_keyboard=inline_keyboard_array)
        elif 'general_config' in dashboard_type:
            keyboard = InlineKeyboardMarkup(inline_keyboard=[
                [
                    InlineKeyboardButton(text='Password',
                                         callback_data='set_password')
                ],
                [
                    InlineKeyboardButton(text='Add calendar',
                                         callback_data='add_calendard')
                ],
                [
                    InlineKeyboardButton(text='Remove calendar',
                                         callback_data='remove_calendar')
                ],
                [
                    InlineKeyboardButton(text='Default Volume = ' +
                                         str(self.default_volume),
                                         callback_data='default_volume')
                ],
            ])
        else:
            keyboard = False
        return keyboard

    def keyboard(self):
        '''Selects a reply keyboard to output'''
        keyboard_type = self.keyboard_type
        if 'home home' in keyboard_type:
            keyboard = ReplyKeyboardMarkup(
                keyboard=[
                    [KeyboardButton(text='menu')],
                ],
                resize_keyboard=True,
                #on_time_keyboard = True, (not supported by current telepot version)
            )
        elif 'home menu' in keyboard_type:
            keyboard = ReplyKeyboardMarkup(
                keyboard=[
                    [KeyboardButton(text=u'\U000023EF' + 'music')],
                    [KeyboardButton(text=u'\U000023F0' + 'alarm')],
                    [KeyboardButton(text=u'\U0001F529' + 'configuration')],
                    [KeyboardButton(text=u'\U0001F519' + '/return')],
                ],
                resize_keyboard=True,
            )
        elif 'music' in keyboard_type:
            keyboard = ReplyKeyboardMarkup(
                keyboard=[
                    [
                        KeyboardButton(text=u'\U0001F3A7 \U0001F4DD' +
                                       'playlists')
                    ],
                    [KeyboardButton(text=u'\U0001F50E' + 'search')],
                    [KeyboardButton(text=u'\U0001F534' + 'youtube help')],
                    [
                        KeyboardButton(text=u'\U0001F4FB' +
                                       'spotify device not found?')
                    ],
                    [KeyboardButton(text=u'\U0001F519' + '/return')],
                ],
                resize_keyboard=True,
            )
        elif 'search type' in keyboard_type:
            keyboard = ReplyKeyboardMarkup(
                keyboard=[
                    [KeyboardButton(text=u'\U0000270F \U0001F3B5' + 'track')],
                    [KeyboardButton(text=u'\U0001F919 \U0001F399' + 'artist')],
                    [KeyboardButton(text=u'\U0001F3B6 \U0001F4BF' + 'album')],
                    [
                        KeyboardButton(text=u'\U0001F3A7 \U0001F4DD' +
                                       'playlist')
                    ],
                    [KeyboardButton(text=u'\U0001F519' + '/return')],
                ],
                resize_keyboard=True,
            )
        elif 'options list' in keyboard_type:
            if self.options_list:
                keyboard_buttons_list = []
                for option in self.options_list:
                    keyboard_buttons_list.append([KeyboardButton(text=option)])
                keyboard_buttons_list.append(
                    [KeyboardButton(text=u'\U0001F519' + '/return')])
                keyboard = ReplyKeyboardMarkup(
                    keyboard=keyboard_buttons_list,
                    resize_keyboard=False,
                )
            else:
                logger.warning('Options_list is empty')
                keyboard = ReplyKeyboardMarkup(
                    keyboard=[
                        [KeyboardButton(text=u'\U0001F519' + '/return')],
                    ],
                    resize_keyboard=True,
                )
        elif 'action' in keyboard_type:
            if self.uri:
                action_list = [u'\U000023E9' + 'play', u'\U000023CF' + 'add']
                if ('artist' in self.uri or 'playlist' in self.uri
                        or 'album' in self.uri):
                    action_list.append(u'\U0001F500' + 'shuffle')
                    action_list.append(u'\U0000270F \U0001F3B5' + 'tracks')
                if 'artist' in self.uri:
                    action_list.append(u'\U0001F3B6 \U0001F4BF' + 'albums')
                if 'playlist' in self.uri:
                    action_list.append(u'\U0001F634' + 'wake up sound' +
                                       u'\U0001F918')
                keyboard_buttons_list = []
                for option in action_list:
                    keyboard_buttons_list.append([KeyboardButton(text=option)])
                keyboard_buttons_list.append(
                    [KeyboardButton(text=u'\U0001F519' + '/return')])
                keyboard = ReplyKeyboardMarkup(
                    keyboard=keyboard_buttons_list,
                    resize_keyboard=False,
                )
            else:
                keyboard = ReplyKeyboardMarkup(
                    keyboard=[
                        [KeyboardButton(text=u'\U0001F519' + '/return')],
                    ],
                    resize_keyboard=True,
                )
        elif 'alarm menu' in keyboard_type:
            keyboard = ReplyKeyboardMarkup(
                keyboard=[
                    [KeyboardButton(text=u'\U000023F1' + 'set_alarm')],
                    [KeyboardButton(text=u'\U000023F3' + 'temporizer')],
                    [
                        KeyboardButton(text=u'\U0001F501' +
                                       'refresh auto alarms')
                    ],
                    [KeyboardButton(text=u'\U0001F519' + '/return')],
                ],
                resize_keyboard=True,
            )
        elif 'alarm set on_day' in keyboard_type:
            keyboard_buttons_list = []
            for event in self.week_events[self.set_alarm_day]:
                event = (event - datetime.timedelta(
                    minutes=int(self.alarm.c.other_event_time_before)))
                keyboard_buttons_list.append([
                    KeyboardButton(text=event.strftime('set ' + u'\U0001F5D3' +
                                                       u'\U0001F519' +
                                                       "-%H:%M"))
                ])
            week_alarms = self.alarm.get_week_alarms_localtime()
            _type = 0
            _time = 1
            for alarm_info in week_alarms[self.set_alarm_day]:
                if 'auto' in alarm_info[_type][0]:
                    a_type0 = u'\U0001F3E7 '
                else:
                    a_type0 = u'\U000024C2 '
                if 'disabled' in alarm_info[_type][1]:
                    a_type1 = u'\U0001F4F4 '
                else:
                    a_type1 = u'\U0001F51B '
                keyboard_buttons_list.append([
                    KeyboardButton(
                        text=alarm_info[_time].strftime("edit " + a_type0 +
                                                        a_type1 +
                                                        u'\U000023F0' +
                                                        "-%H:%M"))
                ])
            keyboard_buttons_list.append([
                KeyboardButton(text=u'\U000023F1' + 'add new' + u'\U0001F195')
            ])
            keyboard_buttons_list.append(
                [KeyboardButton(text=u'\U0001F519' + '/return')])
            keyboard = ReplyKeyboardMarkup(
                keyboard=keyboard_buttons_list,
                resize_keyboard=False,
            )
        elif 'alarm edit' in keyboard_type:
            if 'enabled' in self.alarm_to_edit[0][1]:
                toggle_text = u'\U0001F4F4' + 'disable'
            else:
                toggle_text = u'\U0001F51B' + 'enable'
            keyboard = ReplyKeyboardMarkup(
                keyboard=[
                    [KeyboardButton(text=toggle_text)],
                    [KeyboardButton(text=u'\U0001F527' + 'change')],
                    [KeyboardButton(text=u'\U00002620' + 'delete')],
                    [KeyboardButton(text=u'\U0001F519' + '/return')],
                ],
                resize_keyboard=True,
            )
        elif 'ringing' in keyboard_type:
            keyboard = ReplyKeyboardMarkup(
                keyboard=[
                    [
                        KeyboardButton(text=u'\U0001F634' + '/snooze' +
                                       u'\U0001F634')
                    ],
                ],
                resize_keyboard=True,
            )
        elif 'return' in keyboard_type:
            keyboard = ReplyKeyboardMarkup(
                keyboard=[
                    [KeyboardButton(text=u'\U0001F519' + '/return')],
                ],
                resize_keyboard=True,
            )
        elif 'nothing' in keyboard_type:
            keyboard = ReplyKeyboardRemove(remove_keyboard=True)
        else:
            keyboard = False
        return keyboard

    def refresh_dashboard(self):
        '''Deletes last message and sends a new one'''
        msg_text = self.get_msg_text()
        tries = 0
        chat_count = 0
        for chat_id in self.chat_id_list:
            '''Sends the message with dashboard and the message with custom keyboard
               to every allowed chat id
               '''
            while tries < 2:
                try:
                    dashboard = self.dashboard()
                    keyboard = self.keyboard()
                    dash_msg_id = self.bot.sendMessage(chat_id,
                                                       msg_text,
                                                       parse_mode='Markdown',
                                                       reply_markup=dashboard)
                    reply_msg_id = self.bot.sendMessage(chat_id,
                                                        self.reply_text,
                                                        parse_mode='Markdown',
                                                        reply_markup=keyboard)
                    self.delete_last_msg(self.bot, chat_count)
                    self.set_reply_text('')
                    self.store_dash_msg(
                        chat_count, telepot.message_identifier(dash_msg_id))
                    self.store_reply_msg(
                        chat_count, telepot.message_identifier(reply_msg_id))
                    logger.info('Refresh')
                    break
                except Exception as error:
                    tries = tries + 1
                    logger.error("Could not refresh: {}\n {}".format(
                        sys._getframe().f_code.co_name, error))
            chat_count += 1
        return

    def get_msg_text(self):
        if 'music' in self.state:
            self.music_status = self.mopidy.get_status()
            status = self.music_status
            if status.playing:
                playing_indicator = u'\U0001F918'
            else:
                playing_indicator = u'\U0000270B'
            if len(status.song) > self.max_msg_line_length:
                status.song = ''.join(
                    [status.song[:self.max_msg_line_length - 3], '...'])
            if len(status.artist) > self.max_msg_line_length:
                status.artist = ''.join(
                    [status.artist[:self.max_msg_line_length - 2], '...'])
            msg_text = ''.join([
                '`' + (self.max_msg_line_length - 2) * '-' + '`' + '\n',
                playing_indicator, '*', status.song, '*', playing_indicator,
                '\n', u'\U0001F3A4', '_', status.artist, '_', u'\U0001F3B8',
                '\n', u'\U0001F3B6', '`', status.song_number, '` ',
                u'\U000023F3', '_', status.song_time, '_ ', u'\U0001F509', '`',
                status.volume, '`'
            ])
            self.dashboard_type = 'music'
        elif 'alarm' in self.state:
            if 'change_config' in self.state:
                text = 'Alarm Configuration'
                msg_text = (
                    '`' + int(
                        (self.max_reply_line_length - len(text)) / 2) * '-' +
                    text + int(
                        (self.max_reply_line_length + 1 - len(text)) / 2) * '-'
                    + '`')
                self.dashboard_type = 'alarm config'
            else:
                msg_text = self.alarm.build_msg_text_from_alarms()
                self.dashboard_type = 'alarm view'
        elif 'ringing' in self.state:
            now = self.alarm.to_localtime(datetime.datetime.utcnow())
            hour = now.strftime("%H")
            minu = now.strftime("%M")
            msg_list = []
            hour0 = self.font(int(hour[0]))
            hour1 = self.font(int(hour[1]))
            for index in range(0, len(hour0)):
                msg_list.append('`' + '  ' + hour0[index] + ' ' +
                                hour1[index] + '`')
            msg_list.append(' ')
            minu0 = self.font(int(minu[0]))
            minu1 = self.font(int(minu[1]))
            point = ['  ', '  ', '# ', '  ', '# ', '  ', '  ']
            for index in range(0, len(minu0)):
                msg_list.append('`' + point[index] + minu0[index] + ' ' +
                                minu1[index] + '`')
            msg_text = '\n'.join(msg_list)
            self.dashboard_type = 'ring'
        elif 'snooze' in self.state:
            t = self.alarm.alarm_check()
            seconds = str(int(t % 60))
            if len(seconds) < 2:
                seconds = '0' + seconds
            minutes = str(int(t / 60) % 60)
            if len(minutes) < 2:
                minutes = '0' + minutes
            msg_text = (u'\U0001F634' + u'\U0001F634' + u'\U000023F3' +
                        u'\U000027A1' + '` `' + minutes[0] + u'\U000020E3' +
                        minutes[1] + u'\U000020E3' + '*:*' + seconds[0] +
                        u'\U000020E3' + seconds[1] + u'\U000020E3' +
                        u'\U0001F634' + u'\U0001F634')
            self.dashboard_type = 'snooze'
        elif 'general_config' in self.state:
            text = 'General Configuration'
            msg_text = (
                '`' + int((self.max_reply_line_length - len(text)) / 2) * '-' +
                text + int(
                    (self.max_reply_line_length + 1 - len(text)) / 2) * '-' +
                '`')
            self.dashboard_type = 'general_config'
        else:
            msg_text = '***WIP***'
            self.dashboard_type = ' '
        return msg_text

    def set_reply_text(self, text=None, dash_join=True):
        if text is None:
            return self.reply_text
        else:
            if dash_join:
                text = '-'.join(text.strip(' ').split(' '))
            else:
                text = ' '.join(text.strip(' ').split(' '))
            self.reply_text = (
                '`' + int((self.max_reply_line_length - len(text)) / 2) * '-' +
                text + int(
                    (self.max_reply_line_length + 1 - len(text)) / 2) * '-' +
                '`')

    def auto_refresh_dashboard(self):
        ''' Process performed in parallel with the 'mpc idle' command
            that waits until something changes in the music player
            '''
        while True:
            try:
                subprocess.check_output(['mpc', 'idle'])
                if self.auto_refresh_enabled:
                    logger.info('Auto')
                    self.refresh_dashboard()
                else:
                    logger.info('Auto not enabled')
            except Exception as error:
                logger.warning('Mopidy not enabled \n' + error)
                self.send_chat_action('upload_audio')
                sleep(2)  # HUGE BLOCK

    def auto_refresh(self, enabled=None):
        '''Enables or disables auto refresh process'''
        if enabled is None:
            return self.auto_refresh_enabled
        else:
            self.auto_refresh_enabled = enabled

    def chat_ids(self, new_chat_id=None, command=None):
        '''Returns the chat ids allowed or adds a new chat id allowed
           if the password given is correct
           '''
        if new_chat_id is None:
            return self.chat_id_list
        else:
            is_new = True
            for chat_id in self.chat_id_list:
                if new_chat_id == chat_id:
                    if command is not None:
                        if '/exit' in command:
                            self.chat_id_list.remove(new_chat_id)
                            self.bot.sendMessage(new_chat_id, 'Goodbye')
                            logger.info('Allowed chat ids:')
                            logger.debug(self.chat_id_list)
                            return False
                        else:
                            is_new = False
                    else:
                        is_new = False
            if is_new:
                if self.chat_password in command:
                    self.chat_id_list.append(new_chat_id)
                    logger.info('Allowed chat ids:')
                    logger.debug(self.chat_id_list)
                    self.set_reply_text('Welcome')
                    self.refresh_dashboard()
                else:
                    self.bot.sendMessage(new_chat_id, 'Type correct password')
                return False
            else:
                return True

    def store_dash_msg(self, chat_count, msg=None):
        '''Returns and stores the last message id to
           delete the message afterwards
           '''
        if len(self.last_dash_msg) > chat_count:
            if msg is None:
                return self.last_dash_msg[chat_count]
            else:
                self.last_dash_msg[chat_count] = msg
        elif len(self.last_dash_msg) == chat_count:
            if msg is None:
                return False
            else:
                self.last_dash_msg.append(msg)
        else:
            logger.error("Store last msg: {}".format(
                sys._getframe().f_code.co_name))
            return False

    def store_reply_msg(self, chat_count, msg=None):
        '''Returns and stores the last message id to
           delete the message afterwards
           '''
        if len(self.last_reply_msg) > chat_count:
            if msg is None:
                return self.last_reply_msg[chat_count]
            else:
                self.last_reply_msg[chat_count] = msg
        elif len(self.last_reply_msg) == chat_count:
            if msg is None:
                return False
            else:
                self.last_reply_msg.append(msg)
        else:
            logger.error("Store last msg: {}".format(
                sys._getframe().f_code.co_name))
            return False

    def delete_last_msg(self, bot, chat_count):
        '''Deletes last message sent'''
        try:
            bot.deleteMessage(self.last_dash_msg[chat_count])
            bot.deleteMessage(self.last_reply_msg[chat_count])
        except Exception:
            logger.warning('First msg?')

    def send_msg(self, message):
        for chat_id in self.chat_id_list:
            self.bot.sendMessage(chat_id, message)

    def send_chat_action(self, action):
        '''Valid actions: upload_audio, typing
           '''
        for chat_id in self.chat_id_list:
            self.bot.sendChatAction(chat_id, action)
Exemple #5
0
 def __init__(self, state):
     self.state = state
     self.config = ConfigControls()
     self.MAX_SEARCH_RESULTS = 20
     self.spotipy_VERSION = spotipy.VERSION
Exemple #6
0
class MopidyControls:
    def __init__(self, state):
        self.state = state
        self.config = ConfigControls()
        self.MAX_SEARCH_RESULTS = 20
        self.spotipy_VERSION = spotipy.VERSION

    def refresh_token(self):
        ''' Get credentials from config.txt '''
        text_spotipy = self.config.get_section('Spotipy')
        for line in text_spotipy:
            if 'username' in line:
                my_username = line.split(' = ')[1].strip()
            elif 'client_id' in line:
                my_client_id = line.split(' = ')[1].strip()
            elif 'client_secret' in line:
                my_client_secret = line.split(' = ')[1].strip()
            elif 'redirect_uri' in line:
                my_redirect_uri = line.split(' = ')[1].strip()
        self.token = util.prompt_for_user_token(
            my_username,
            'user-read-private ' + 'user-read-currently-playing ' +
            'user-modify-playback-state ' + 'user-read-playback-state ' +
            'user-read-recently-played',
            client_id=my_client_id,
            client_secret=my_client_secret,
            redirect_uri=my_redirect_uri)
        self.spotify = spotipy.Spotify(auth=self.token)
        logger.info('Spotify token refreshed')
        return self.token

    def play(self, playlist=None):
        '''play the currently active track'''
        if self.state.music_status.mopidy_playing:
            try:
                if playlist is None:
                    output = subprocess.check_output(['mpc', 'play'])
                else:
                    output = subprocess.check_output(['mpc', 'play', playlist])
                return False
            except:
                logger.error("Mopidy control error: {}".format(
                    sys._getframe().f_code.co_name))
                return False
        else:
            try:
                if not self.state.music_status.playing:
                    self.spotify.start_playback()
                return True
            except:
                logger.error("Mopidy control error: {}".format(
                    sys._getframe().f_code.co_name))
                return False

    def pause(self):
        '''Pause playback'''
        if self.state.music_status.mopidy_playing:
            try:
                output = subprocess.check_output(['mpc', 'pause'])
                return False
            except:
                logger.error("Mopidy control error: {}".format(
                    sys._getframe().f_code.co_name))
                return False
        else:
            try:
                if self.state.music_status.playing:
                    self.spotify.pause_playback()
                return True
            except:
                logger.error("Mopidy control error: {}".format(
                    sys._getframe().f_code.co_name))
                return False

    def toggle(self):
        '''Toggles play and pause'''
        if self.state.music_status.mopidy_playing:
            try:
                output = subprocess.check_output(['mpc', 'toggle'])
                return False
            except:
                logger.error("Mopidy control error: {}".format(
                    sys._getframe().f_code.co_name))
                return False
        else:
            try:
                if self.state.music_status.playing:
                    self.spotify.pause_playback()
                else:
                    self.spotify.start_playback()
                return True
            except:
                logger.error("Mopidy control error: {}".format(
                    sys._getframe().f_code.co_name))
                return False

    def list_playlist(self):
        '''Outputs a list of playlists'''
        try:
            output = subprocess.check_output(['mpc', 'lsplaylist'])
            return output.decode('utf-8')
        except:
            logger.error("Mopidy control error: {}".format(
                sys._getframe().f_code.co_name))
            return False

    def search(self, query, type_):
        '''Outputs a list of the search'''
        try:
            output = subprocess.check_output(['mpc', 'search', type_, query])
            return output.decode('utf-8')
        except:
            logger.error("Mopidy control error: {}".format(
                sys._getframe().f_code.co_name))
            return False

    def mopidy_status(self):
        '''Outputs the state'''
        try:
            output = subprocess.check_output(['mpc'])
            return output.decode('utf-8')
        except:
            logger.error("Mopidy control error: {}".format(
                sys._getframe().f_code.co_name))
            return False

    def load(self, name, name_type):
        LOADING_STATUS_REFRESH_COUNTS = 3
        try:
            if 'playlist' in name_type or 'pl' in name_type:
                output = subprocess.check_output(['mpc', 'load', name])
            elif 'uri' in name_type:
                logger.info('load: ' + name)
                auto_refresh_enabled = self.state.auto_refresh_enabled
                if 'spotify:user:spotify' in name:  #Solves the error adding playlists created by spotify
                    while self.state.auto_refresh_enabled:
                        self.state.auto_refresh_enabled = False
                    [tracks_list, uri_list] = self.tracks_spotify(name)
                    if not tracks_list and not uri_list:
                        logger.error("Mopidy control error: {}".format(
                            sys._getframe().f_code.co_name))
                        return False
                    uri_counts = LOADING_STATUS_REFRESH_COUNTS
                    for uri in uri_list:
                        if uri_counts % LOADING_STATUS_REFRESH_COUNTS == 0:
                            self.state.send_chat_action('upload_audio')
                            uri_counts = 0
                        uri_counts += 1
                        output = subprocess.check_output(['mpc', 'add', uri])
                    self.state.auto_refresh_enabled = auto_refresh_enabled
                else:
                    try:
                        output = subprocess.check_output(['mpc', 'add', name])
                    except:
                        if not 'youtube.com' in name:
                            logger.warning('Alternative loading')
                            while self.state.auto_refresh_enabled:
                                self.state.auto_refresh_enabled = False
                            [tracks_list, uri_list] = self.tracks_spotify(name)
                            if not tracks_list and not uri_list:
                                logger.error("Mopidy control error: {}".format(
                                    sys._getframe().f_code.co_name))
                                return False
                            uri_counts = LOADING_STATUS_REFRESH_COUNTS
                            for uri in uri_list:
                                if uri_counts % LOADING_STATUS_REFRESH_COUNTS == 0:
                                    self.state.send_chat_action('upload_audio')
                                    uri_counts = 0
                                uri_counts += 1
                                output = subprocess.check_output(
                                    ['mpc', 'add', uri])
                            self.state.auto_refresh_enabled = auto_refresh_enabled
                        else:
                            logger.error("Mopidy control error: {}".format(
                                sys._getframe().f_code.co_name))
                            return False
            else:
                logger.error("Mopidy control error: Bad input {}".format(
                    sys._getframe().f_code.co_name))
                output = ''
                return False
            logger.info('Load success')
            return True
        except Exception as error:
            logger.error(('Mopidy control error {} \n' + error).format(
                sys._getframe().f_code.co_name))
            while not self.state.auto_refresh_enabled:
                self.state.auto_refresh_enabled = True
            return False

    def load_local_song(self):
        '''Used when no internet connection, loads a local file song'''
        output = subprocess.check_output(
            ['mpc', 'add', 'local:track:WakePi_local_song.mp3'])
        return True

    def clear(self):
        '''Clears the current play'''
        if self.state.music_status.mopidy_playing:
            try:
                output = subprocess.check_output(['mpc', 'clear'])
                return False
            except:
                logger.error("Mopidy control error: {}".format(
                    sys._getframe().f_code.co_name))
                return False
        else:
            try:
                if self.state.music_status.playing:
                    self.spotify.pause_playback()
                return True
            except:
                logger.error("Mopidy control error: {}".format(
                    sys._getframe().f_code.co_name))
                return False

    def random(self, mode=None):
        '''Toggles random mode'''
        if self.state.music_status.mopidy_playing:
            try:
                if mode is None:
                    output = subprocess.check_output(['mpc', 'random'])
                elif (mode is 'on') or (mode is 'off'):
                    output = subprocess.check_output(['mpc', 'random', mode])
                return False
            except:
                logger.error("Mopidy control error: {}".format(
                    sys._getframe().f_code.co_name))
                return False
        else:
            try:
                if self.state.music_status.random or mode is 'off':
                    self.spotify.shuffle(False)
                else:
                    self.spotify.shuffle(True)
                return True
            except:
                logger.error("Mopidy control error: {}".format(
                    sys._getframe().f_code.co_name))
                return False

    def repeat(self, mode=None):
        '''Toggles repeat mode'''
        if self.state.music_status.mopidy_playing:
            try:
                if mode is None:
                    output = subprocess.check_output(['mpc', 'repeat'])
                elif (mode is 'on') or (mode is 'off'):
                    output = subprocess.check_output(['mpc', 'repeat', mode])
                return False
            except:
                logger.error("Mopidy control error: {}".format(
                    sys._getframe().f_code.co_name))
                return False
        else:
            try:
                if self.state.music_status.repeat or mode is 'off':
                    self.spotify.repeat('off')
                else:
                    self.spotify.repeat('context')
                return False
            except:
                logger.error("Mopidy control error: {}".format(
                    sys._getframe().f_code.co_name))
                return False

    def next(self):
        '''Next song in list'''
        if self.state.music_status.mopidy_playing:
            try:
                output = subprocess.check_output(['mpc', 'next'])
                return False
            except:
                logger.error("Mopidy control error: {}".format(
                    sys._getframe().f_code.co_name))
                return False
        else:
            try:
                self.spotify.next_track()
                return True
            except:
                logger.error("Mopidy control error: {}".format(
                    sys._getframe().f_code.co_name))
                return False

    def prev(self):
        '''Previous song'''
        if self.state.music_status.mopidy_playing:
            status = self.get_status()
            song_percentage = status.song_percentage
            try:
                song_percentage = max(min(song_percentage, 100), 0)
                if song_percentage < 10:
                    output = subprocess.check_output(['mpc', 'prev'])
                else:
                    output = subprocess.check_output(['mpc', 'cdprev'])
                return False
            except:
                logger.error("Mopidy control error: {}".format(
                    sys._getframe().f_code.co_name))
                return False
        else:
            try:
                self.spotify.previous_track()
                return True
            except:
                logger.error("Mopidy control error: {}".format(
                    sys._getframe().f_code.co_name))
                return False

    def seek(self, increment):
        '''Seeks a percentage of the song'''
        status = self.get_status()
        song_percentage = status.song_percentage + increment
        if self.state.music_status.mopidy_playing:
            try:
                song_percentage = max(min(song_percentage, 100), 0)
                song_percentage = str(song_percentage) + '%'
                output = subprocess.check_output(
                    ['mpc', 'seek', song_percentage])
                return False
            except:
                logger.error("Mopidy control error: {}".format(
                    sys._getframe().f_code.co_name))
                return False
        else:
            try:
                position_ms = status.duration_s * 10 * song_percentage
                self.spotify.seek_track(position_ms)
                return True
            except:
                logger.error("Mopidy control error: {}".format(
                    sys._getframe().f_code.co_name))
                return False

    def volume_mopidy(self, volume, increase=None):
        '''Changes the volume'''
        try:
            volume = max(min(volume, 100), 0)
            if increase is None:
                volume_change = str(volume)
            else:
                if increase:
                    volume_change = '+' + str(volume)
                else:
                    volume_change = '-' + str(volume)
            output = subprocess.check_output(['mpc', 'volume', volume_change])
            return output.decode('utf-8')
        except:
            logger.error("Mopidy control error: {}".format(
                sys._getframe().f_code.co_name))
            return False

    def volume_sys(self, volume_change):
        try:
            volume_perc = self.get_volume_sys()
            new_volume_perc = max(min(volume_perc + volume_change, 100), 0)
            if self.set_volume_sys(new_volume_perc):
                return True
            else:
                return False
        except:
            return False

    def set_volume_sys(self, volume_perc):
        try:
            db = int((106 * (volume_perc / 2 + 50))) - 10200
            subprocess.check_output(
                ['sudo', 'amixer', 'set', 'PCM', '--',
                 str(db)])
            return True
        except:
            return False

    def get_volume_sys(self):
        output = subprocess.check_output(['sudo', 'amixer', 'get', 'PCM'])
        db = int(
            output.decode("utf-8").split('\n')[-2].split('[')[0].split(
                'Playback')[1].strip(' '))
        volume_perc = int(((db + 10200) / 106 - 50) * 2)
        return volume_perc

    def shuffle(self):
        '''Shuffles the current playlist'''
        try:
            output = subprocess.check_output(['mpc', 'shuffle'])
            return True
        except:
            logger.error("Mopidy control error: {}".format(
                sys._getframe().f_code.co_name))
            return False

    #This function only works with spotify
    def search_spotify(self, query, type_):
        '''Improved search function that returns a name and a URI list'''
        try:
            uri_list = []
            search_list = []
            try:
                info = self.spotify.search(q=query,
                                           limit=self.MAX_SEARCH_RESULTS,
                                           type=type_)
            except:
                self.refresh_token()
                info = self.spotify.search(q=query,
                                           limit=self.MAX_SEARCH_RESULTS,
                                           type=type_)
            if 'playlist' in type_:
                for playlist in info['playlists']['items']:
                    uri_list.append(playlist['uri'])
                    info_text = u'\U0001F3A7 \U0001F4DD' + playlist['name']
                    search_list.append(info_text)
            elif 'track' in type_:
                for track in info['tracks']['items']:
                    uri_list.append(track['uri'])
                    info_text = (u'\U0000270F \U0001F3B5' + track['name'] +
                                 ' ' + u'\U0001F919 \U0001F399' +
                                 track['artists'][0]['name'])
                    search_list.append(info_text)
            elif 'artist' in type_:
                for artist in info['artists']['items']:
                    uri_list.append(artist['uri'])
                    info_text = u'\U0001F919 \U0001F399' + artist['name']
                    search_list.append(info_text)
            elif 'album' in type_:
                for album in info['albums']['items']:
                    uri_list.append(album['uri'])
                    info_text = (u'\U0001F3B6 \U0001F4BF' + album['name'] +
                                 ' ' + u'\U0001F919 \U0001F399' +
                                 album['artists'][0]['name'])
                    search_list.append(info_text)
            return [search_list, uri_list]
        except:
            logger.error("Mopidy control error: {}".format(
                sys._getframe().f_code.co_name))
            return [False, False]

    #This function only works with spotify
    def list_playlist_spotify(self):
        '''Outputs the current spotify user list of playlists'''
        try:
            uri_list = []
            playlist_list = []
            try:
                info = self.spotify.current_user_playlists()
            except:
                self.refresh_token()
                info = self.spotify.current_user_playlists()
            for playlist in info['items']:
                uri_list.append(playlist['uri'])
                info_text = u'\U0001F3A7 \U0001F4DD' + playlist['name']
                playlist_list.append(info_text)
            return [playlist_list, uri_list]
        except:
            logger.error("Mopidy control error: {}".format(
                sys._getframe().f_code.co_name))
            return [False, False]

    #This function only works with spotify
    def tracks_spotify(self, uri):
        '''Outputs the track list of an album, playlist, or artist uri'''
        try:
            tracks_list = []
            uri_list = []
            if 'playlist' in uri:
                username = uri.split(':')[2]
                playlist_id = uri.split(':')[4]
                try:
                    info = self.spotify.user_playlist(username, playlist_id)
                except:
                    self.refresh_token()
                    info = self.spotify.user_playlist(username, playlist_id)
                for track in info['tracks']['items']:
                    info_text = u'\U0000270F \U0001F3B5' + track['track'][
                        'name']
                    tracks_list.append(info_text)
                    uri_list.append(track['track']['uri'])
            elif 'artist' in uri:
                try:
                    info = self.spotify.artist_top_tracks(uri)
                except:
                    self.refresh_token()
                    info = self.spotify.artist_top_tracks(uri)
                for track in info['tracks']:
                    info_text = u'\U0000270F \U0001F3B5' + track['name']
                    tracks_list.append(info_text)
                    uri_list.append(track['uri'])
            elif 'album' in uri:
                try:
                    info = self.spotify.album(uri)
                except:
                    self.refresh_token()
                    info = self.spotify.album(uri)
                for track in info['tracks']['items']:
                    info_text = u'\U0000270F \U0001F3B5' + track['name']
                    tracks_list.append(info_text)
                    uri_list.append(track['uri'])
            return [tracks_list, uri_list]
        except:
            logger.error("Mopidy control error: {}".format(
                sys._getframe().f_code.co_name))
            return [False, False]

    def albums_spotify(self, uri):
        '''Outputs the album list of an artist uri'''
        try:
            album_list = []
            uri_list = []
            if 'artist' in uri:
                try:
                    info = self.spotify.artist_albums(uri)
                except:
                    self.refresh_token()
                    info = self.spotify.artist_albums(uri)
                for album in info['items']:
                    info_text = u'\U0001F3B6 \U0001F4BF' + album['name']
                    album_list.append(info_text)
                    uri_list.append(album['uri'])
            else:
                logger.error('Cannot search albums of a non artist uri')
            return [album_list, uri_list]
        except:
            logger.error("Mopidy control error: {}".format(
                sys._getframe().f_code.co_name))
            return [False, False]

    def get_status(self):
        '''Build the status namedtuple'''
        status_full = self.mopidy_status()
        status = namedtuple(
            "status", ' '.join([
                "mopidy_playing", "playing", "song", "volume", "repeat",
                "random", "single", "consume", "artist", "song_number",
                "song_time", "song_percentage", "progress_s", "duration_s"
            ]))
        if not status_full:
            '''Mopidy not enabled'''
            status.playing = False
            status.song = ' *!!* '
            status.artist = 'Music server off'
            status.song_number = '#0/0'
            status.song_time = '0:00/0:00'
            status.volume = 'xx'
            status.repeat = False
            status.random = False
            status.single = False
            status.consume = False
            status.song_percentage = 0
            return status
        if len(status_full.splitlines()) == 1:  #Mopidy has no playback
            status = self.get_status_spotify(status)
        else:
            status.mopidy_playing = True
            for index, line in enumerate(status_full.splitlines()):
                if index == 2:
                    for section in line.split('   '):
                        if 'volume:' in section:
                            status.volume = str(self.get_volume_sys()) + '%'
                        elif 'repeat' in section:
                            if 'on' in section:
                                status.repeat = True
                            else:
                                status.repeat = False
                        elif 'random' in section:
                            if 'on' in section:
                                status.random = True
                            else:
                                status.random = False
                        elif 'single' in section:
                            if 'on' in section:
                                status.single = True
                            else:
                                status.single = False
                        elif 'consume' in section:
                            if 'off' in section:
                                status.consume = False
                            else:
                                status.consume = True
                elif index == 0:
                    section = line.split('-')
                    if len(section) > 1:
                        status.song = section[1].strip(' ')
                        status.artist = section[0].strip(' ')
                    else:
                        status.song = section[0].strip(' ')
                        status.artist = 'unknown'
                elif index == 1:
                    '''Remove silly blanks'''
                    sections = line.split(' ')
                    deleted = 0
                    for index, section in enumerate(sections):
                        if sections[index - deleted].strip(' ') == '':
                            del sections[index - deleted]
                            deleted += 1
                    '''Analyze line'''
                    if '[playing]' in line:
                        status.playing = True
                    else:
                        status.playing = False
                    status.song_number = sections[1]
                    status.song_time = sections[2]
                    status.song_percentage = int(sections[3].strip(' ').strip(
                        '(').strip(')').strip('%'))
                    status.progress_s = (
                        int(sections[2].split('/')[0].split(':')[0]) * 60 +
                        int(sections[2].split('/')[0].split(':')[1]))
                    status.duration_s = (
                        int(sections[2].split('/')[1].split(':')[0]) * 60 +
                        int(sections[2].split('/')[1].split(':')[1]))
        return status

    def get_status_spotify(self, status):
        '''Returns the status of the spotify playback, called when the mopidy
           status is empty'''
        logger.info('Getting spotify playback')
        try:
            status_full = self.spotify.current_playback()
        except:
            self.refresh_token()
            status_full = self.spotify.current_playback()
        if status_full:
            status.mopidy_playing = False
            if status_full['is_playing']:
                status.playing = True
            else:
                status.playing = False
            status.song = status_full['item']['name']
            status.artist = status_full['item']['artists'][0]['name']
            status.song_number = '#' + str(
                status_full['item']['track_number']) + '/?'
            status.progress_s = int(int(status_full['progress_ms']) / 1000)
            status.duration_s = int(
                int(status_full['item']['duration_ms']) / 1000)
            status.song_time = (str(int(status.progress_s / 60)) + ':' +
                                ('0' + str(int(status.progress_s % 60)))[-2:] +
                                '/' + str(int(status.duration_s / 60)) + ':' +
                                ('0' + str(int(status.duration_s % 60)))[-2:])
            status.volume = str(self.get_volume_sys()) + '%'
            if 'context' in status_full['repeat_state']:
                status.repeat = True
            else:
                status.repeat = False
            if status_full['shuffle_state']:
                status.random = True
            else:
                status.random = False
            status.single = False
            status.consume = False
            status.song_percentage = int(status.progress_s /
                                         status.duration_s * 100)
        else:
            status.mopidy_playing = True
            status.playing = False
            status.song = 'No song'
            status.artist = 'No artist'
            status.song_number = '#0/0'
            status.song_time = '0:00/0:00'
            status.volume = str(self.get_volume_sys()) + '%'
            status.repeat = False
            status.random = False
            status.single = False
            status.consume = False
            status.song_percentage = 0
            status.progress_s = 0
            status.duration_s = 0
        logger.info('Got spotify playback')
        return status
Exemple #7
0
 def __init__(self, state):
     self.state = state
     self.config = ConfigControls()
Exemple #8
0
class CalendarCheck:
    def __init__(self, state):
        self.state = state
        self.config = ConfigControls()

    def get_cal_urls(self):
        '''Gets the calendars urls from the config.txt'''
        urls = self.config.get_section('Calendar')
        return urls

    def add_cal_url(self, url):
        '''Adds a url to the config file'''
        urls = self.get_cal_urls()
        urls.append(url)
        self.config.set_section('Calendar', urls)
        return True

    def remove_cal_url(self, url):
        urls = self.get_cal_urls()
        try:
            urls.remove(url)
            self.config.set_section('Calendar', urls)
            return True
        except:
            return False

    def get_week_cal(self):
        '''Extracts the weekly calendar from the ical urls on the config file'''
        logger.info('Getting week calendar')
        today = datetime.datetime.utcnow().date()
        next_week = today + datetime.timedelta(7)
        week_cal = Calendar()
        week_cal.add('x-wr-calname', "Weekly Combined Calendar")
        urls = self.get_cal_urls()
        for url in urls:
            logger.debug(url)
            try:
                # Error avoider: Tries to read the calendar several times
                attempts = 0
                while attempts < 5:
                    if attempts < 4:
                        try:
                            req = requests.get(url)
                            attempts = 5
                        except:
                            attempts += 1
                    else:
                        req = requests.get(url)
                        attempts = 5
                # Error avoider finished
                if req.status_code != 200:
                    logger.error("Error {} fetching {}: {}".format(
                        url, req.status_code, req.text))
                    continue
                cal = Calendar.from_ical(req.text)
                for event in cal.walk("VEVENT"):
                    end = event.get('dtend')
                    if end:
                        WEEK_EVENT = False
                        if hasattr(end.dt, 'date'):
                            date = end.dt.date()
                        else:
                            date = end.dt
                        if date >= today and date < next_week:
                            WEEK_EVENT = True
                        elif 'RRULE' in event:
                            try:
                                rrule = event['RRULE']
                                until = rrule.get('until')[0]
                                if today < until.date():
                                    # Only weekly repeated events are supported
                                    if 'WEEKLY' in rrule.get('freq')[0]:
                                        WEEK_EVENT = True
                            except Exception as error:
                                logger.error(("{} rrule\n" + error).format(
                                    sys._getframe().f_code.co_name))
                        if WEEK_EVENT:
                            copied_event = Event()
                            for attr in event:
                                if type(event[attr]) is list:
                                    for element in event[attr]:
                                        copied_event.add(attr, element)
                                else:
                                    copied_event.add(attr, event[attr])
                            week_cal.add_component(copied_event)
            except Exception as error:
                # Add counter to avoid false errors
                logger.warning('Invalid calendar, removing\n' + url + '\n' +
                               error)
                self.remove_cal_url(url)
                for chat_id in self.state.chat_id_list:
                    self.state.bot.sendMessage(
                        chat_id, 'Removed invalid calendar url:\n' + url)
        logger.info('Got calendars')
        return week_cal

    def get_week_events_local_times(self):
        '''Gets the starting times of the next week events and returns them
           in a list ordered in days remaining
           events = [[datetime.datetime,...], [...], ...]
           '''
        week_cal = self.get_week_cal()
        now = datetime.datetime.utcnow()
        utcoffset = datetime.datetime.now(
            datetime.timezone.utc).astimezone().tzinfo.utcoffset(None)
        today_local = (datetime.datetime.utcnow() + utcoffset).date()
        events = [[], [], [], [], [], [], []]
        for event in week_cal.walk("VEVENT"):
            if 'RRULE' in event:
                # Only weekly repeated events are supported
                try:
                    ref_start = event.get('dtstart')
                    if isinstance(ref_start.dt, datetime.datetime
                                  ):  # Check is not a full day event
                        reference = ref_start.dt.replace(tzinfo=None)
                        rrule = event['RRULE']
                        if 'BYDAY' in rrule:
                            day_list = rrule.get('byday')
                        elif 'WKST' in rrule:
                            day_list = rrule.get('wkst')
                        for day in day_list:
                            for index, day_string in enumerate(
                                ['MO', 'TU', 'WE', 'TH', 'FR', 'SA', 'SU']):
                                if day in day_string:
                                    reference_weekday = index
                                    break
                            event_add_day = reference_weekday - now.weekday()
                            if event_add_day < 0:
                                event_add_day += 7
                            event_day = now.day + event_add_day
                            event_start = datetime.datetime(
                                now.year, now.month, event_day, reference.hour,
                                reference.minute)
                            #Check for excluded dates
                            is_excluded = False
                            if 'EXDATE' in event:
                                exclusions = event.get('exdate')
                                if not isinstance(exclusions, list):
                                    exclusions = [exclusions]
                                for ex_list in exclusions:
                                    for ex_day in ex_list.dts:
                                        if ex_day.dt.replace(
                                                tzinfo=None
                                        ) - event_start == datetime.timedelta(
                                                0):
                                            is_excluded = True
                                            break
                            if not is_excluded:
                                event_start_local = event_start  # + utcoffset '''I don't know why it is already local'''
                                days_to_go = event_start_local.weekday(
                                ) - today_local.weekday()
                                if days_to_go < 0:
                                    days_to_go += 7
                                events[days_to_go].append(event_start_local)
                except Exception as error:
                    logger.error(" {}: rrule\n{}".format(
                        sys._getframe().f_code.co_name, error))
            else:
                start = event.get('dtstart')
                if start:
                    try:
                        if isinstance(start.dt, datetime.datetime
                                      ):  # Check is not a full day event
                            event_start = start.dt.replace(tzinfo=None)
                            event_start_local = event_start + utcoffset
                            days_to_go = event_start_local.weekday(
                            ) - today_local.weekday()
                            if days_to_go < 0:
                                days_to_go += 7
                            events[days_to_go].append(event_start_local)
                    except Exception as error:
                        logger.error(" {}: not rrule\n{}".format(
                            sys._getframe().f_code.co_name, error))
        sorted_events = [[], [], [], [], [], [], []]
        for weekday, day in enumerate(events):
            sorted_events[weekday] = sorted(day)
        return sorted_events

    def get_day_first_events_localtime(self, week_events):
        '''Gets a list with the first events of each weekday
           first_events = [datetime.time,...]
           '''
        first_events = [None, None, None, None, None, None, None]
        for days_to_go, event in enumerate(week_events):
            if len(event) > 0:
                first_events[days_to_go] = event[0]
        return first_events
Exemple #9
0
now = datetime.datetime.utcnow()
file_log = now.strftime('%d_%m_%y %H_%M') + '.log'
logger = logging.getLogger('WakePi')
logger.setLevel(logging.DEBUG)
fh = logging.FileHandler(file_log)
fh.setLevel(logging.INFO)
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)
formatter = logging.Formatter('[%(levelname)7s] %(asctime)s: %(message)s')
fh.setFormatter(formatter)
ch.setFormatter(formatter)
logger.addHandler(fh)
logger.addHandler(ch)
logger.info('Start initialization')
'''Initialize modules'''
config = ConfigControls()
bot = telepot.Bot(get_bot_token())
state = State(bot)
cp = CommandProcess(state, bot)
mopidy = MopidyControls(state)
logger.info('Spotipy version ' + mopidy.spotipy_VERSION)
cal_check = CalendarCheck(state)
alarm = AlarmControls(state)
MessageLoop(bot, {
    'chat': on_chat_message,
    'callback_query': on_callback_query
}).run_as_thread()
state.week_events = state.cal_check.get_week_events_local_times()
alarm.set_auto_alarms(state.week_events)
'''Parallel process'''
if __name__ == "__main__":