Exemplo n.º 1
0
 def __update_volume(self, level=0):
     mixer = Mixer()
     volume = mixer.getvolume()[0]
     code = self.get_volume_code(volume) + level
     code = self.fix_code(code)
     if code in self.VOLUMES:
         volume = self.VOLUMES[code]
         mixer.setvolume(volume)
     return code, volume
Exemplo n.º 2
0
 def __update_volume(self, change=0):
     """
     Tries to change volume level
     :param change: +1 or -1; the step to change by
     :return: new code (0..11), whether volume changed
     """
     mixer = Mixer()
     old_level = self.volume_to_level(mixer.getvolume()[0])
     new_level = self.bound_level(old_level + change)
     self.enclosure.eyes_volume(new_level)
     mixer.setvolume(self.level_to_volume(new_level))
     return new_level, new_level != old_level
Exemplo n.º 3
0
Arquivo: volume.py Projeto: Ferada/nih
def set_volume(request, username, value):
    global volume_who, volume_direction
    m = Mixer()
    if value > m.getvolume()[0]:
        volume_direction = "up"
        volume_who = username
    elif value < m.getvolume()[0]:
        volume_direction = "down"
    else:
        return volume()  # no change, quit

    volume_who = username
    m.setvolume(value)
    return volume()
Exemplo n.º 4
0
    def __update_volume(self, level=0):
        """
        Tries to change volume level
        :param level: +1 or -1; the step to change by
        :return: new code (0..11), whether volume changed
        """
        mixer = Mixer()
        volume = mixer.getvolume()[0]
        old_code = self.get_volume_code(volume)

        new_code = self.fix_code(old_code + level)
        if new_code in self.VOLUMES:
            volume = self.VOLUMES[new_code]
            mixer.setvolume(volume)
        return new_code, new_code != old_code
Exemplo n.º 5
0
 def __init__(self, mplfifo, ser, mpout, beep=True):
     self.playing = False
     self.mixer = Mixer(control='PCM')
     self.fifo = mplfifo
     self.ser = ser
     self.mpout = mpout
     self.beep = beep
Exemplo n.º 6
0
 def __init__(self, player):
     self.player = player
     self._alsa_mixer = Mixer(control="PCM")
     self.is_pressed = False
     GPIO.setmode(GPIO.BOARD)
     input_buttons = [k for k in self.buttons.keys()]
     GPIO.setup(input_buttons, GPIO.IN, pull_up_down=GPIO.PUD_UP)
Exemplo n.º 7
0
class RaspberryGPIOPlugin:

    buttons = {
        12: "volume_up",
        11: "volume_down",
        8: "skip",
        10: "play_pause",
    }

    def __init__(self, player):
        self.player = player
        self._alsa_mixer = Mixer(control="PCM")
        self.is_pressed = False
        GPIO.setmode(GPIO.BOARD)
        input_buttons = [k for k in self.buttons.keys()]
        GPIO.setup(input_buttons, GPIO.IN, pull_up_down=GPIO.PUD_UP)

    def __call__(self):
        for port_number, callback_name in self.buttons.items():
            if GPIO.input(port_number) == 0:
                getattr(self, callback_name)()
                return
        else:
            self.is_pressed = False

    def _change_volume(self, change):
        new_volume = self.current_volume + change
        self._alsa_mixer.setvolume(new_volume)

    @property
    def current_volume(self):
        return self._alsa_mixer.getvolume()[0]

    @debounced
    def skip(self):
        self.player.skip()

    @debounced
    def play_pause(self):
        self.player.play_pause()

    def volume_up(self):
        self._change_volume(1)

    def volume_down(self):
        self._change_volume(-1)
Exemplo n.º 8
0
 def __init__(self, mplfifo, ser, mpout, beep=True, override = [], blacklist = []):
     self.playing = False
     self.mixer = Mixer(control='PCM')
     self.fifo = mplfifo
     self.ser = ser
     self.mpout = mpout
     self.beep = beep
     self.override = override
     self.blacklist = blacklist
     self.current_user = None
Exemplo n.º 9
0
    def notify(self, timestamp):
        with self.LOCK:
            if self.data.__contains__(timestamp):
                volume = None
                self.alarm_on = True
                delay = self.__calculate_delay(self.max_delay)

                while self.alarm_on and datetime.now() < delay:
                    play_mp3(self.file_path).communicate()
                    self.speak_dialog('alarm.stop')
                    time.sleep(self.repeat_time + 2)
                    if not volume and datetime.now() >= delay:
                        mixer = Mixer()
                        volume = mixer.getvolume()[0]
                        mixer.setvolume(100)
                        delay = self.__calculate_delay(self.extended_delay)
                if volume:
                    Mixer().setvolume(volume)
                self.remove(timestamp)
                self.alarm_on = False
                self.save()
Exemplo n.º 10
0
    def process(self, data):
        self.client.emit(Message(data))

        if "mycroft.stop" in data:
            self.client.emit(Message("mycroft.stop"))

        if "volume.up" in data:
            self.client.emit(
                Message("IncreaseVolumeIntent", metadata={'play_sound': True}))

        if "volume.down" in data:
            self.client.emit(
                Message("DecreaseVolumeIntent", metadata={'play_sound': True}))

        if "system.test.begin" in data:
            self.client.emit(Message('recognizer_loop:sleep'))

        if "system.test.end" in data:
            self.client.emit(Message('recognizer_loop:wake_up'))

        if "mic.test" in data:
            mixer = Mixer()
            prev_vol = mixer.getvolume()[0]
            mixer.setvolume(35)
            self.client.emit(Message("speak", metadata={
                'utterance': "I am testing one two three"}))

            time.sleep(0.5)  # Prevents recording the loud button press
            record("/tmp/test.wav", 3.0)
            mixer.setvolume(prev_vol)
            play_wav("/tmp/test.wav")
            time.sleep(3.5)  # Pause between tests so it's not so fast

            # Test audio muting on arduino
            subprocess.call('speaker-test -P 10 -l 0 -s 1', shell=True)
Exemplo n.º 11
0
    def process(self, data):
        self.client.emit(Message(data))

        if "mycroft.stop" in data:
            self.client.emit(Message("mycroft.stop"))

        if "volume.up" in data:
            self.client.emit(
                Message("IncreaseVolumeIntent", metadata={'play_sound': True}))

        if "volume.down" in data:
            self.client.emit(
                Message("DecreaseVolumeIntent", metadata={'play_sound': True}))

        if "system.test.begin" in data:
            self.client.emit(Message('recognizer_loop:sleep'))

        if "system.test.end" in data:
            self.client.emit(Message('recognizer_loop:wake_up'))

        if "mic.test" in data:
            mixer = Mixer()
            prev_vol = mixer.getvolume()[0]
            mixer.setvolume(35)
            self.client.emit(Message("speak", metadata={
                'utterance': "I am testing one two three"}))

            time.sleep(0.5)  # Prevents recording the loud button press
            record("/tmp/test.wav", 3.0)
            mixer.setvolume(prev_vol)
            play_wav("/tmp/test.wav")
            time.sleep(3.5)  # Pause between tests so it's not so fast

            # Test audio muting on arduino
            subprocess.call('speaker-test -P 10 -l 0 -s 1', shell=True)

        if "unit.shutdown" in data:
            self.client.emit(
                Message("enclosure.eyes.timedspin",
                        metadata={'length': 12000}))
            self.client.emit(Message("enclosure.mouth.reset"))
            subprocess.call('systemctl poweroff -i', shell=True)

        if "unit.reboot" in data:
            self.client.emit(
                Message("enclosure.eyes.spin"))
            self.client.emit(Message("enclosure.mouth.reset"))
            subprocess.call('systemctl reboot -i', shell=True)

        if "unit.setwifi" in data:
            self.client.emit(Message("wifisetup.start"))

        if "unit.factory-reset" in data:
            subprocess.call(
                'rm ~/.mycroft/identity/identity.json',
                shell=True)
            self.client.emit(
                Message("enclosure.eyes.spin"))
            self.client.emit(Message("enclosure.mouth.reset"))
            subprocess.call('systemctl reboot -i', shell=True)
Exemplo n.º 12
0
    def process(self, data):
        self.client.emit(Message(data))

        if "mycroft.stop" in data:
            self.client.emit(Message("mycroft.stop"))

        if "volume.up" in data:
            self.client.emit(
                Message("IncreaseVolumeIntent", metadata={'play_sound': True}))

        if "volume.down" in data:
            self.client.emit(
                Message("DecreaseVolumeIntent", metadata={'play_sound': True}))

        if "system.test.begin" in data:
            self.client.emit(Message('recognizer_loop:sleep'))

        if "system.test.end" in data:
            self.client.emit(Message('recognizer_loop:wake_up'))

        if "mic.test" in data:
            mixer = Mixer()
            prev_vol = mixer.getvolume()[0]
            mixer.setvolume(35)
            self.client.emit(Message("speak", metadata={
                'utterance': "I am testing one two three"}))
            record("/tmp/test.wav", 3.5)
            play_wav("/tmp/test.wav")

            # Test audio muting on arduino
            self.client.emit(Message("speak", metadata={
                'utterance': "LOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONG"}))

            mixer.setvolume(prev_vol)
Exemplo n.º 13
0
    def notify(self, timestamp):
        with self.LOCK:
            if self.data.__contains__(timestamp):
                volume = None
                self.reminder_on = True
                delay = self.__calculate_delay(self.max_delay)

                while self.reminder_on and datetime.now() < delay:
                    self.speak_dialog(
                        'reminder.notify',
                        data=self.build_feedback_payload(timestamp))
                    time.sleep(1)
                    self.speak_dialog('reminder.stop')
                    time.sleep(self.repeat_time)
                    if not volume and datetime.now() >= delay:
                        mixer = Mixer()
                        volume = mixer.getvolume()[0]
                        mixer.setvolume(100)
                        delay = self.__calculate_delay(self.extended_delay)
                if volume:
                    Mixer().setvolume(volume)
                self.remove(timestamp)
                self.reminder_on = False
                self.save()
Exemplo n.º 14
0
    def run(self, commandlist):
        mixer = Mixer()

        if len(commandlist) == 1:
            if mixer.getmute():
                mixer.setmute(0)
            else:
                mixer.setmute(1)
        else:
            command = {'mode':'set'}
            for i in xrange(0, len(commandlist)-1):
                pass
Exemplo n.º 15
0
    def __init__(self):   
        self.logged_in = threading.Event()
        self.end_of_track = threading.Event()

        self.queue_playlist = []
        self.prev_playlist = []
        
        self.min_volume = 70
        
        config = spotify.Config()
        config.load_application_key_file(filename='/home/pi/django_projects/mysite/spotify_app/spotify_appkey.key')
        self.session = spotify.Session(config=config)
        
        self.loop = spotify.EventLoop(self.session)
        self.loop.start()
        
        self.current_track = None

        # Connect an audio sink
        self.audio = spotify.AlsaSink(self.session)
        self.mixer = Mixer('PCM', 0)
        
        self.session.on(spotify.SessionEvent.CONNECTION_STATE_UPDATED, self.on_connection_state_updated)
        self.session.on(spotify.SessionEvent.END_OF_TRACK, self.on_end_of_track)
Exemplo n.º 16
0
 def create_mixer(self):
     self.alsamixer = Mixer(control=self.mixer,
                            id=self.mixer_id,
                            cardindex=self.card)
Exemplo n.º 17
0
 def handle_set_volume(self, message):
     mixer = Mixer()
     level = self.get_volume_level(message, mixer.getvolume()[0])
     mixer.setvolume(self.level_to_volume(level))
     self.speak_dialog('set.volume', data={'volume': level})
Exemplo n.º 18
0
def get_volume():
	id = alsaaudio.mixers(SOUND_CARD).index(SOUND_MIXER)
	mixer = Mixer(SOUND_MIXER, id, SOUND_CARD)
	return mixer.getvolume()[0]
Exemplo n.º 19
0
class ALSA(IntervalModule):
    """
    Shows volume of ALSA mixer. You can also use this for inputs, btw.

    Requires pyalsaaudio

    Available formatters:

    * `{volume}` — the current volume in percent
    * `{muted}` — the value of one of the `muted` or `unmuted` settings
    * `{card}` — the associated soundcard
    * `{mixer}` — the associated ALSA mixer
    """

    interval = 1

    settings = (
        "format",
        ("format_muted", "optional format string to use when muted"),
        ("mixer", "ALSA mixer"),
        ("mixer_id", "ALSA mixer id"),
        ("card", "ALSA sound card"),
        "muted", "unmuted",
        "color_muted", "color",
        "channel"
    )

    muted = "M"
    unmuted = ""
    color_muted = "#AAAAAA"
    color = "#FFFFFF"
    format = "♪: {volume}"
    format_muted = None
    mixer = "Master"
    mixer_id = 0
    card = 0
    channel = 0

    alsamixer = None
    has_mute = True

    def init(self):
        self.create_mixer()
        try:
            self.alsamixer.getmute()
        except ALSAAudioError:
            self.has_mute = False

        self.fdict = {
            "card": self.alsamixer.cardname(),
            "mixer": self.mixer,
        }

    def create_mixer(self):
        self.alsamixer = Mixer(
            control=self.mixer, id=self.mixer_id, cardindex=self.card)

    def run(self):
        self.create_mixer()

        muted = False
        if self.has_mute:
            muted = self.alsamixer.getmute()[self.channel] == 1

        self.fdict["volume"] = self.alsamixer.getvolume()[self.channel]
        self.fdict["muted"] = self.muted if muted else self.unmuted

        if muted and self.format_muted is not None:
            output_format = self.format_muted
        else:
            output_format = self.format

        self.output = {
            "full_text": output_format.format(**self.fdict),
            "color": self.color_muted if muted else self.color,
        }
Exemplo n.º 20
0
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# [email protected]

import sys

from alsaaudio import Mixer


m = Mixer()


if __name__ == '__main__':
    output = ["{}   {}".format(u'\uf001', m.getvolume()[0])]
    color = False
    if m.getmute()[0]:
        color = '#666666'
    output += output
    if color:
        output.append(color)
    print('\n'.join(output))
Exemplo n.º 21
0
 def handle_reset_volume(self, message):
     Mixer().setvolume(self.default_volume)
     self.speak_dialog('reset.volume', data={'volume': self.get_volume_code(self.default_volume)})
Exemplo n.º 22
0
    def process(self, data):
        # TODO: Look into removing this emit altogether.
        # We need to check if any other serial bus messages
        # are handled by other parts of the code
        if "mycroft.stop" not in data:
            self.ws.emit(Message(data))

        if "Command: system.version" in data:
            # This happens in response to the "system.version" message
            # sent during the construction of Enclosure()
            self.ws.emit(Message("enclosure.started"))

        if "mycroft.stop" in data:
            if has_been_paired():
                create_signal('buttonPress')
                self.ws.emit(Message("mycroft.stop"))

        if "volume.up" in data:
            self.ws.emit(
                Message("VolumeSkill:IncreaseVolumeIntent",
                        {'play_sound': True}))

        if "volume.down" in data:
            self.ws.emit(
                Message("VolumeSkill:DecreaseVolumeIntent",
                        {'play_sound': True}))

        if "system.test.begin" in data:
            self.ws.emit(Message('recognizer_loop:sleep'))

        if "system.test.end" in data:
            self.ws.emit(Message('recognizer_loop:wake_up'))

        if "mic.test" in data:
            mixer = Mixer()
            prev_vol = mixer.getvolume()[0]
            mixer.setvolume(35)
            self.ws.emit(Message("speak", {
                'utterance': "I am testing one two three"}))

            time.sleep(0.5)  # Prevents recording the loud button press
            record("/tmp/test.wav", 3.0)
            mixer.setvolume(prev_vol)
            play_wav("/tmp/test.wav").communicate()

            # Test audio muting on arduino
            subprocess.call('speaker-test -P 10 -l 0 -s 1', shell=True)

        if "unit.shutdown" in data:
            self.ws.emit(
                Message("enclosure.eyes.timedspin",
                        {'length': 12000}))
            self.ws.emit(Message("enclosure.mouth.reset"))
            subprocess.call('systemctl poweroff -i', shell=True)

        if "unit.reboot" in data:
            self.ws.emit(Message("enclosure.eyes.spin"))
            self.ws.emit(Message("enclosure.mouth.reset"))
            subprocess.call('systemctl reboot -i', shell=True)

        if "unit.setwifi" in data:
            self.ws.emit(Message("mycroft.wifi.start"))

        if "unit.factory-reset" in data:
            self.ws.emit(Message("enclosure.eyes.spin"))
            subprocess.call(
                'rm ~/.mycroft/identity/identity2.json',
                shell=True)
            self.ws.emit(Message("mycroft.wifi.reset"))
            self.ws.emit(Message("mycroft.disable.ssh"))
            self.ws.emit(Message("speak", {
                'utterance': mycroft.dialog.get("reset to factory defaults")}))
            wait_while_speaking()
            self.ws.emit(Message("enclosure.mouth.reset"))
            self.ws.emit(Message("enclosure.eyes.spin"))
            self.ws.emit(Message("enclosure.mouth.reset"))
            subprocess.call('systemctl reboot -i', shell=True)

        if "unit.enable-ssh" in data:
            # This is handled by the wifi client
            self.ws.emit(Message("mycroft.enable.ssh"))
            self.ws.emit(Message("speak", {
                'utterance': mycroft.dialog.get("ssh enabled")}))

        if "unit.disable-ssh" in data:
            # This is handled by the wifi client
            self.ws.emit(Message("mycroft.disable.ssh"))
            self.ws.emit(Message("speak", {
                'utterance': mycroft.dialog.get("ssh disabled")}))
Exemplo n.º 23
0
class ALSA():

    def init(self):

        self.muted = "M"
        self.unmuted = ""
        self.color_muted = "#AAAAAA"
        self.color = "#FFFFFF"
        self.format = "♪: {volume}"
        self.format_muted = None
        self.mixer = "Master"
        self.mixer_id = 0
        self.card = 0
        self.channel = 0
        self.increment = 5

        self.map_volume = False

        self.alsamixer = None
        self.has_mute = True



        self.create_mixer()
        try:
            self.alsamixer.getmute()
        except ALSAAudioError:
            self.has_mute = False

        self.fdict = {
            "card": self.alsamixer.cardname(),
            "mixer": self.mixer,
        }

        self.dbRng = self.alsamixer.getrange()

        self.dbMin = self.dbRng[0]
        self.dbMax = self.dbRng[1]

    def create_mixer(self):
        self.alsamixer = Mixer(
            control=self.mixer, id=self.mixer_id, cardindex=self.card)

    def run(self):
        self.create_mixer()

        muted = False
        if self.has_mute:
            muted = self.alsamixer.getmute()[self.channel] == 1

        self.fdict["volume"] = self.get_cur_volume()
        self.fdict["muted"] = self.muted if muted else self.unmuted
        self.fdict["db"] = self.get_db()

        if muted and self.format_muted is not None:
            output_format = self.format_muted
        else:
            output_format = self.format

        self.output = {
            "full_text": output_format.format(**self.fdict),
            "color": self.color_muted if muted else self.color,
        }

    def switch_mute(self):
        if self.has_mute:
            muted = self.alsamixer.getmute()[self.channel]
            self.alsamixer.setmute(not muted)

    def get_cur_volume(self):
        if self.map_volume:
            dbCur = self.get_db() * 100.0
            dbMin = self.dbMin * 100.0
            dbMax = self.dbMax * 100.0

            dbCur_norm = self.exp10((dbCur - dbMax) / 6000.0)
            dbMin_norm = self.exp10((dbMin - dbMax) / 6000.0)

            vol = (dbCur_norm - dbMin_norm) / (1 - dbMin_norm)
            vol = int(round(vol * 100, 0))

            return vol
        else:
            return self.alsamixer.getvolume()[self.channel]

    def get_new_volume(self, direction):
        if direction == "inc":
            volume = (self.fdict["volume"] + 1) / 100
        elif direction == "dec":
            volume = (self.fdict["volume"] - 1) / 100

        dbMin = self.dbMin * 100
        dbMax = self.dbMax * 100

        dbMin_norm = self.exp10((dbMin - dbMax) / 6000.0)

        vol = volume * (1 - dbMin_norm) + dbMin_norm

        if direction == "inc":
            dbNew = min(self.dbMax, ceil(((6000.0 * log10(vol)) + dbMax) / 100))
        elif direction == "dec":
            dbNew = max(self.dbMin, floor(((6000.0 * log10(vol)) + dbMax) / 100))

        volNew = int(round(self.map_db(dbNew, self.dbMin, self.dbMax, 0, 100), 0))

        return volNew

    def increase_volume(self, delta=None):
        if self.map_volume:
            vol = self.get_new_volume("inc")

            self.alsamixer.setvolume(vol)
        else:
            vol = self.alsamixer.getvolume()[self.channel]
            self.alsamixer.setvolume(min(100, vol + (delta if delta else self.increment)))

    def decrease_volume(self, delta=None):
        if self.map_volume:
            vol = self.get_new_volume("dec")

            self.alsamixer.setvolume(vol)
        else:
            vol = self.alsamixer.getvolume()[self.channel]
            self.alsamixer.setvolume(max(0, vol - (delta if delta else self.increment)))

    def get_db(self):
        db = (((self.dbMax - self.dbMin) / 100) * self.alsamixer.getvolume()[self.channel]) + self.dbMin
        db = int(round(db, 0))

        return db

    def map_db(self, value, dbMin, dbMax, volMin, volMax):
        dbRange = dbMax - dbMin
        volRange = volMax - volMin

        volScaled = float(value - dbMin) / float(dbRange)

        return volMin + (volScaled * volRange)

    def exp10(self, x):
        return exp(x * log(10))
Exemplo n.º 24
0
    def process(self, data):
        # TODO: Look into removing this emit altogether.
        # We need to check if any other serial bus messages
        # are handled by other parts of the code
        if "mycroft.stop" not in data:
            self.bus.emit(Message(data))

        if "Command: system.version" in data:
            # This happens in response to the "system.version" message
            # sent during the construction of Enclosure()
            self.bus.emit(Message("enclosure.started"))

        if "mycroft.stop" in data:
            if has_been_paired():
                create_signal('buttonPress')
                self.bus.emit(Message("mycroft.stop"))

        if "volume.up" in data:
            self.bus.emit(
                Message("mycroft.volume.increase", {'play_sound': True}))

        if "volume.down" in data:
            self.bus.emit(
                Message("mycroft.volume.decrease", {'play_sound': True}))

        if "system.test.begin" in data:
            self.bus.emit(Message('recognizer_loop:sleep'))

        if "system.test.end" in data:
            self.bus.emit(Message('recognizer_loop:wake_up'))

        if "mic.test" in data:
            mixer = Mixer()
            prev_vol = mixer.getvolume()[0]
            mixer.setvolume(35)
            self.bus.emit(
                Message("speak", {'utterance': "I am testing one two three"}))

            time.sleep(0.5)  # Prevents recording the loud button press
            record("/tmp/test.wav", 3.0)
            mixer.setvolume(prev_vol)
            play_wav("/tmp/test.wav").communicate()

            # Test audio muting on arduino
            subprocess.call('speaker-test -P 10 -l 0 -s 1', shell=True)

        if "unit.shutdown" in data:
            # Eyes to soft gray on shutdown
            self.bus.emit(
                Message("enclosure.eyes.color", {
                    'r': 70,
                    'g': 65,
                    'b': 69
                }))
            self.bus.emit(
                Message("enclosure.eyes.timedspin", {'length': 12000}))
            self.bus.emit(Message("enclosure.mouth.reset"))
            time.sleep(0.5)  # give the system time to pass the message
            self.bus.emit(Message("system.shutdown"))

        if "unit.reboot" in data:
            # Eyes to soft gray on reboot
            self.bus.emit(
                Message("enclosure.eyes.color", {
                    'r': 70,
                    'g': 65,
                    'b': 69
                }))
            self.bus.emit(Message("enclosure.eyes.spin"))
            self.bus.emit(Message("enclosure.mouth.reset"))
            time.sleep(0.5)  # give the system time to pass the message
            self.bus.emit(Message("system.reboot"))

        if "unit.setwifi" in data:
            self.bus.emit(Message("system.wifi.setup", {'lang': self.lang}))

        if "unit.factory-reset" in data:
            self.bus.emit(
                Message("speak", {
                    'utterance':
                    mycroft.dialog.get("reset to factory defaults")
                }))
            subprocess.call('rm ~/.mycroft/identity/identity2.json',
                            shell=True)
            self.bus.emit(Message("system.wifi.reset"))
            self.bus.emit(Message("system.ssh.disable"))
            wait_while_speaking()
            self.bus.emit(Message("enclosure.mouth.reset"))
            self.bus.emit(Message("enclosure.eyes.spin"))
            self.bus.emit(Message("enclosure.mouth.reset"))
            time.sleep(5)  # give the system time to process all messages
            self.bus.emit(Message("system.reboot"))

        if "unit.enable-ssh" in data:
            # This is handled by the wifi client
            self.bus.emit(Message("system.ssh.enable"))
            self.bus.emit(
                Message("speak",
                        {'utterance': mycroft.dialog.get("ssh enabled")}))

        if "unit.disable-ssh" in data:
            # This is handled by the wifi client
            self.bus.emit(Message("system.ssh.disable"))
            self.bus.emit(
                Message("speak",
                        {'utterance': mycroft.dialog.get("ssh disabled")}))

        if "unit.enable-learning" in data or "unit.disable-learning" in data:
            enable = 'enable' in data
            word = 'enabled' if enable else 'disabled'

            LOG.info("Setting opt_in to: " + word)
            new_config = {'opt_in': enable}
            user_config = LocalConf(USER_CONFIG)
            user_config.merge(new_config)
            user_config.store()

            self.bus.emit(
                Message("speak",
                        {'utterance': mycroft.dialog.get("learning " + word)}))
Exemplo n.º 25
0
class Harold(object):

    def __init__(self, mplfifo, ser, mpout, beep=True):
        self.playing = False
        self.mixer = Mixer(control='PCM')
        self.fifo = mplfifo
        self.ser = ser
        self.mpout = mpout
        self.beep = beep

    def write(self, *args, **kwargs):
        delay = kwargs.pop("delay", 0.5)
        kws = {"file": self.fifo}
        kws.update(kwargs)
        print(*args, **kws)
        time.sleep(delay)

    def __call__(self):
        if not self.playing:
            userlog = open("/home/pi/logs/user_log.csv", "a")
            # Lower the volume during quiet hours... Don't piss off the RA!
            self.mixer.setvolume(85 if quiet_hours() else 100)
            varID = self.ser.readline()
            print(varID)
            # mplayer will play any files sent to the FIFO file.
            if self.beep:
                self.write("loadfile", DING_SONG)
            if "ready" not in varID:
                # Turn the LEDs off
                LED.on(False)
                print("Turning off LEDs")
                # Get the username from the ibutton
                print("Getting User Data")
                uid, homedir = read_ibutton(varID)
                # Print the user's name (Super handy for debugging...)
                print("User: '******'\n")
                song = get_user_song(homedir, uid)
                print("Now playing '" + song + "'...\n")
                varID = varID[:-2]
                userlog.write("\n" + time.strftime('%Y/%m/%d %H:%M:%S') + "," + uid + "," + song)
                self.write("loadfile '" + song.replace("'", "\\'") + "'\nget_time_length",
                           delay=0.0)

                line = self.mpout.readline().strip()
                # timeout = time.time() + 5  # Five second time out to wait for song time.

                while not line.startswith("ANS_LENGTH="):
                    line = self.mpout.readline().strip()
                    if line.startswith("Playing"):  # Check if mplayer can play file.
                        if self.mpout.readline().strip() == "":
                            self.starttime = time.time()
                            self.endtime = time.time()
                            self.playing = True
                            break
                    elif line.startswith("ANS_LENGTH="):
                        duration = float(line.strip().split("=")[-1])
                        self.starttime = time.time()
                        self.endtime = time.time() + min(30, duration)
                        self.playing = True
                    else:
                        pass

                userlog.close()
        elif time.time() >= self.endtime:
            self.write("stop")
            self.playing = False
            self.ser.flushInput()
            LED.on(True)
            print("Stopped\n")

        elif time.time() >= self.starttime+28:
            # Fade out the music at the end.
            vol = int(self.mixer.getvolume()[0])
            while vol > 60:
                vol -= 1 + (100 - vol)/30.
                self.mixer.setvolume(int(vol))
                time.sleep(0.1)
Exemplo n.º 26
0
class ALSA(IntervalModule):
    """
    Shows volume of ALSA mixer. You can also use this for inputs, btw.

    Requires pyalsaaudio

    Available formatters:

    * `{volume}` — the current volume in percent
    * `{muted}` — the value of one of the `muted` or `unmuted` settings
    * `{card}` — the associated soundcard
    * `{mixer}` — the associated ALSA mixer
    """

    interval = 1

    settings = ("format", ("mixer", "ALSA mixer"),
                ("mixer_id", "ALSA mixer id"), ("card", "ALSA sound card"),
                "muted", "unmuted", "color_muted", "color", "channel")

    muted = "M"
    unmuted = ""
    color_muted = "#AAAAAA"
    color = "#FFFFFF"
    format = "♪: {volume}"
    mixer = "Master"
    mixer_id = 0
    card = 0
    channel = 0

    alsamixer = None
    has_mute = True

    def init(self):
        self.create_mixer()
        try:
            self.alsamixer.getmute()
        except ALSAAudioError:
            self.has_mute = False

        self.fdict = {
            "card": self.alsamixer.cardname(),
            "mixer": self.mixer,
        }

    def create_mixer(self):
        self.alsamixer = Mixer(control=self.mixer,
                               id=self.mixer_id,
                               cardindex=self.card)

    def run(self):
        self.create_mixer()

        muted = False
        if self.has_mute:
            muted = self.alsamixer.getmute()[self.channel] == 1

        self.fdict["volume"] = self.alsamixer.getvolume()[self.channel]
        self.fdict["muted"] = self.muted if muted else self.unmuted

        self.output = {
            "full_text": self.format.format(**self.fdict),
            "color": self.color_muted if muted else self.color,
        }
Exemplo n.º 27
0
class TelegramSpamSkill(MycroftSkill):
    def __init__(self):
        super(TelegramSpamSkill, self).__init__(name="TelegramSpamSkill")

    def initialize(self):
        self.mute = str(self.settings.get('MuteIt', ''))
        if (self.mute == 'True') or (self.mute == 'true'):
            try:
                self.mixer = Mixer()
                msg = "Telegram Messages will temporary Mute Mycroft"
                logger.info(msg)
            except:
                msg = "There is a problem with alsa audio, mute is not working!"
                logger.info(
                    "There is a problem with alsaaudio, mute is not working!")
                self.sendMycroftSay(msg)
                self.mute = 'false'
        else:
            logger.info("Telegram: Muting is off")
            self.mute = "false"
        self.add_event('recognizer_loop:utterance', self.utteranceHandler)
        self.add_event('telegram-skill:response', self.sendHandler)
        self.add_event('speak', self.responseHandler)
        user_id1 = self.settings.get('TeleID1', '')
        self.chat_id = user_id1
        self.chat_whitelist = [user_id1]
        # Get Bot Token from settings.json
        UnitName = DeviceApi().get()['name']
        MyCroftDevice1 = self.settings.get('MDevice1', '')
        self.bottoken = ""
        if MyCroftDevice1 == UnitName:
            logger.debug("Found MyCroft Unit 1: " + UnitName)
            self.bottoken = self.settings.get('TeleToken1', '')
        else:
            msg = (
                "No or incorrect Device Name specified! Your DeviceName is: " +
                UnitName)
            logger.info(msg)
            self.sendMycroftSay(msg)

        # Connection to Telegram API
        self.telegram_updater = Updater(
            token=self.bottoken)  # get telegram Updates
        self.telegram_dispatcher = self.telegram_updater.dispatcher
        receive_handler = MessageHandler(
            Filters.text, self.TelegramMessages
        )  # TODO: Make audio Files as Input possible: Filters.text | Filters.audio
        self.telegram_dispatcher.add_handler(receive_handler)
        self.telegram_updater.start_polling(
            clean=True)  # start clean and look for messages
        wbot = telegram.Bot(token=self.bottoken)
        global loaded  # get global variable
        if loaded == 0:  # check if bot is just started
            loaded = 1  # make sure that users gets this message only once bot is newly loaded
            if self.mute == "false":
                msg = "Telegram Skill is loaded"
                self.sendMycroftSay(msg)
            loadedmessage = "Telegram-Skill on Mycroft Unit \"" + UnitName + "\" is loaded and ready to use!"  # give User a nice message
            try:
                wbot.send_message(
                    chat_id=user_id1,
                    text=loadedmessage)  # send welcome message to user 1
            except:
                pass

    def TelegramMessages(self, bot, update):
        msg = update.message.text
        if self.chat_id in self.chat_whitelist:
            logger.info("Telegram-Message from User: "******"', '\\\"').replace(
                '(', ' ').replace(')', ' ').replace('{',
                                                    ' ').replace('}', ' ')
            msg = msg.casefold(
            )  # some skills need lowercase (eg. the cows list)
            self.add_event('recognizer_loop:audio_output_start',
                           self.muteHandler)
            self.sendMycroftUtt(msg)

        else:
            logger.info("Chat ID " + self.chat_id +
                        " is not whitelisted, i don't process it")
            nowhite = ("This is your ChatID: " + self.chat_id)
            bot.send_message(chat_id=self.chat_id, text=nowhite)

    def sendMycroftUtt(self, msg):
        uri = 'ws://localhost:8181/core'
        ws = create_connection(uri)
        utt = '{"context": null, "type": "recognizer_loop:utterance", "data": {"lang": "' + self.lang + '", "utterances": ["' + msg + '"]}}'
        ws.send(utt)
        ws.close()

    def sendMycroftSay(self, msg):
        uri = 'ws://localhost:8181/core'
        ws = create_connection(uri)
        msg = "say " + msg
        utt = '{"context": null, "type": "recognizer_loop:utterance", "data": {"lang": "' + self.lang + '", "utterances": ["' + msg + '"]}}'
        ws.send(utt)
        ws.close()

    def responseHandler(self, message):
        response = message.data.get("utterance")
        self.bus.emit(
            Message("telegram-skill:response", {
                "intent_name": "telegram-response",
                "utterance": response
            }))

    def utteranceHandler(self, message):
        response = str(message.data.get("utterances"))
        self.bus.emit(
            Message("telegram-skill:response", {
                "intent_name": "telegram-response",
                "utterance": response
            }))

    def sendHandler(self, message):
        sendData = message.data.get("utterance")
        logger.info("Sending to Telegram-User: " + sendData)
        sendbot = telegram.Bot(token=self.bottoken)
        sendbot.send_message(chat_id=self.chat_id, text=sendData)

    def muteHandler(self, message):
        if (self.mute == 'true') or (self.mute == 'True'):
            self.mixer.setmute(1)
            wait_while_speaking()
            self.mixer.setmute(0)
        self.remove_event('recognizer_loop:audio_output_start')

    def shutdown(self):  # shutdown routine
        self.telegram_updater.stop()  # will stop update and dispatcher
        self.telegram_updater.is_idle = False
        super(TelegramSpamSkill, self).shutdown()

    def stop(self):
        pass
Exemplo n.º 28
0
 def handle_set_volume(self, message):
     mixer = Mixer()
     code, volume = self.get_volume(message, mixer.getvolume()[0])
     mixer.setvolume(volume)
     self.speak_dialog('set.volume', data={'volume': code})
Exemplo n.º 29
0
 def handle_set_volume(self, message):
     mixer = Mixer()
     code, volume = self.get_volume(message, mixer.getvolume()[0])
     mixer.setvolume(volume)
     self.speak_dialog('set.volume', data={'volume': code})
Exemplo n.º 30
0
class ALSA(IntervalModule):
    """
    Shows volume of ALSA mixer. You can also use this for inputs, btw.

    Requires pyalsaaudio

    .. rubric:: Available formatters

    * `{volume}` — the current volume in percent
    * `{muted}` — the value of one of the `muted` or `unmuted` settings
    * `{card}` — the associated soundcard
    * `{mixer}` — the associated ALSA mixer
    """

    interval = 1

    settings = (
        "format",
        ("format_muted", "optional format string to use when muted"),
        ("mixer", "ALSA mixer"),
        ("mixer_id", "ALSA mixer id"),
        ("card", "ALSA sound card"),
        ("increment", "integer percentage of max volume to in/decrement volume on mousewheel"),
        "muted", "unmuted",
        "color_muted", "color",
        "channel",
        ("map_volume", "volume display/setting as in AlsaMixer. increment option is ignored then.")
    )

    muted = "M"
    unmuted = ""
    color_muted = "#AAAAAA"
    color = "#FFFFFF"
    format = "♪: {volume}"
    format_muted = None
    mixer = "Master"
    mixer_id = 0
    card = 0
    channel = 0
    increment = 5

    map_volume = False

    alsamixer = None
    has_mute = True

    on_upscroll = "increase_volume"
    on_downscroll = "decrease_volume"
    on_leftclick = "switch_mute"
    on_rightclick = on_leftclick

    def init(self):
        self.create_mixer()
        try:
            self.alsamixer.getmute()
        except ALSAAudioError:
            self.has_mute = False

        self.fdict = {
            "card": self.alsamixer.cardname(),
            "mixer": self.mixer,
        }

        self.dbRng = self.alsamixer.getrange()

        self.dbMin = self.dbRng[0]
        self.dbMax = self.dbRng[1]

    def create_mixer(self):
        self.alsamixer = Mixer(
            control=self.mixer, id=self.mixer_id, cardindex=self.card)

    def run(self):
        self.create_mixer()

        muted = False
        if self.has_mute:
            muted = self.alsamixer.getmute()[self.channel] == 1

        self.fdict["volume"] = self.get_cur_volume()
        self.fdict["muted"] = self.muted if muted else self.unmuted
        self.fdict["db"] = self.get_db()

        if muted and self.format_muted is not None:
            output_format = self.format_muted
        else:
            output_format = self.format

        self.data = self.fdict
        self.output = {
            "full_text": output_format.format(**self.fdict),
            "color": self.color_muted if muted else self.color,
        }

    def switch_mute(self):
        if self.has_mute:
            muted = self.alsamixer.getmute()[self.channel]
            self.alsamixer.setmute(not muted)

    def get_cur_volume(self):
        if self.map_volume:
            dbCur = self.get_db() * 100.0
            dbMin = self.dbMin * 100.0
            dbMax = self.dbMax * 100.0

            dbCur_norm = self.exp10((dbCur - dbMax) / 6000.0)
            dbMin_norm = self.exp10((dbMin - dbMax) / 6000.0)

            vol = (dbCur_norm - dbMin_norm) / (1 - dbMin_norm)
            vol = int(round(vol * 100, 0))

            return vol
        else:
            return self.alsamixer.getvolume()[self.channel]

    def get_new_volume(self, direction):
        if direction == "inc":
            volume = (self.fdict["volume"] + 1) / 100
        elif direction == "dec":
            volume = (self.fdict["volume"] - 1) / 100

        dbMin = self.dbMin * 100
        dbMax = self.dbMax * 100

        dbMin_norm = self.exp10((dbMin - dbMax) / 6000.0)

        vol = volume * (1 - dbMin_norm) + dbMin_norm

        if direction == "inc":
            dbNew = min(self.dbMax, ceil(((6000.0 * log10(vol)) + dbMax) / 100))
        elif direction == "dec":
            dbNew = max(self.dbMin, floor(((6000.0 * log10(vol)) + dbMax) / 100))

        volNew = int(round(self.map_db(dbNew, self.dbMin, self.dbMax, 0, 100), 0))

        return volNew

    def increase_volume(self, delta=None):
        if self.map_volume:
            vol = self.get_new_volume("inc")

            self.alsamixer.setvolume(vol)
        else:
            vol = self.alsamixer.getvolume()[self.channel]
            self.alsamixer.setvolume(min(100, vol + (delta if delta else self.increment)))

    def decrease_volume(self, delta=None):
        if self.map_volume:
            vol = self.get_new_volume("dec")

            self.alsamixer.setvolume(vol)
        else:
            vol = self.alsamixer.getvolume()[self.channel]
            self.alsamixer.setvolume(max(0, vol - (delta if delta else self.increment)))

    def get_db(self):
        db = (((self.dbMax - self.dbMin) / 100) * self.alsamixer.getvolume()[self.channel]) + self.dbMin
        db = int(round(db, 0))

        return db

    def map_db(self, value, dbMin, dbMax, volMin, volMax):
        dbRange = dbMax - dbMin
        volRange = volMax - volMin

        volScaled = float(value - dbMin) / float(dbRange)

        return volMin + (volScaled * volRange)

    def exp10(self, x):
        return exp(x * log(10))
Exemplo n.º 31
0
 def handle_mute_volume(self, message):
     self.speak_dialog('mute.volume')
     time.sleep(2)
     Mixer().setvolume(0)
Exemplo n.º 32
0
		background.blit(image_weather, image_weather.get_rect())
                
		render.render(datetime.today().strftime("%H:%M"), font, background, hex_to_rgb(conf["general"]["front_color"]), 0, 120, 320, 120)

		table.update([{"image": image_temp, "data": str(round(data_forecast["list"][0]["temp"]["day"], 1))}, {"image": image_rise, "data": datetime.fromtimestamp(data["sys"]["sunrise"]).strftime("%H:%M")}, {"image": image_rise, "data": datetime.fromtimestamp(data["sys"]["sunset"]).strftime("%H:%M")}, {"image": image_mail, "data": str(unread[0])}, {"image": image_news, "data": str(news)}, {"image": image_cal, "data": str(cal[0])}])

		screen.blit(background, (0, 0))
		display.flip()
	else :
		render.render(get_ip_address('eth0'), font, background, hex_to_rgb(conf["general"]["front_color"]), 0, 0, 320, 240)
		screen.blit(background, (0, 0))
                display.flip()

# Boucle de rafraichissement

mixer = Mixer(control="PCM")

i = 0

while True : 
	sleep(0.1)
	update()
	
	i+=1
	if i >= 20 : 
		conf_file = open("../conf/wake.json")
		conf = jload(conf_file)

	s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
	s.connect("mastersocket")
	s.send(jdumps({"request": "get_delta"}))
Exemplo n.º 33
0
 def create_mixer(self):
     self.alsamixer = Mixer(
         control=self.mixer, id=self.mixer_id, cardindex=self.card)
Exemplo n.º 34
0
    def process(self, data):
        # TODO: Look into removing this emit altogether.
        # We need to check if any other serial bus messages
        # are handled by other parts of the code
        if "mycroft.stop" not in data:
            self.ws.emit(Message(data))

        if "Command: system.version" in data:
            # This happens in response to the "system.version" message
            # sent during the construction of Enclosure()
            self.ws.emit(Message("enclosure.started"))

        if "mycroft.stop" in data:
            if has_been_paired():
                create_signal('buttonPress')
                self.ws.emit(Message("mycroft.stop"))

        if "volume.up" in data:
            self.ws.emit(Message("mycroft.volume.increase",
                                 {'play_sound': True}))

        if "volume.down" in data:
            self.ws.emit(Message("mycroft.volume.decrease",
                                 {'play_sound': True}))

        if "system.test.begin" in data:
            self.ws.emit(Message('recognizer_loop:sleep'))

        if "system.test.end" in data:
            self.ws.emit(Message('recognizer_loop:wake_up'))

        if "mic.test" in data:
            mixer = Mixer()
            prev_vol = mixer.getvolume()[0]
            mixer.setvolume(35)
            self.ws.emit(Message("speak", {
                'utterance': "I am testing one two three"}))

            time.sleep(0.5)  # Prevents recording the loud button press
            record("/tmp/test.wav", 3.0)
            mixer.setvolume(prev_vol)
            play_wav("/tmp/test.wav").communicate()

            # Test audio muting on arduino
            subprocess.call('speaker-test -P 10 -l 0 -s 1', shell=True)

        if "unit.shutdown" in data:
            # Eyes to soft gray on shutdown
            self.ws.emit(Message("enclosure.eyes.color",
                                 {'r': 70, 'g': 65, 'b': 69}))
            self.ws.emit(
                Message("enclosure.eyes.timedspin",
                        {'length': 12000}))
            self.ws.emit(Message("enclosure.mouth.reset"))
            time.sleep(0.5)  # give the system time to pass the message
            self.ws.emit(Message("system.shutdown"))

        if "unit.reboot" in data:
            # Eyes to soft gray on reboot
            self.ws.emit(Message("enclosure.eyes.color",
                                 {'r': 70, 'g': 65, 'b': 69}))
            self.ws.emit(Message("enclosure.eyes.spin"))
            self.ws.emit(Message("enclosure.mouth.reset"))
            time.sleep(0.5)  # give the system time to pass the message
            self.ws.emit(Message("system.reboot"))

        if "unit.setwifi" in data:
            self.ws.emit(Message("system.wifi.setup", {'lang': self.lang}))

        if "unit.factory-reset" in data:
            self.ws.emit(Message("speak", {
                'utterance': mycroft.dialog.get("reset to factory defaults")}))
            subprocess.call(
                'rm ~/.mycroft/identity/identity2.json',
                shell=True)
            self.ws.emit(Message("system.wifi.reset"))
            self.ws.emit(Message("system.ssh.disable"))
            wait_while_speaking()
            self.ws.emit(Message("enclosure.mouth.reset"))
            self.ws.emit(Message("enclosure.eyes.spin"))
            self.ws.emit(Message("enclosure.mouth.reset"))
            time.sleep(5)  # give the system time to process all messages
            self.ws.emit(Message("system.reboot"))

        if "unit.enable-ssh" in data:
            # This is handled by the wifi client
            self.ws.emit(Message("system.ssh.enable"))
            self.ws.emit(Message("speak", {
                'utterance': mycroft.dialog.get("ssh enabled")}))

        if "unit.disable-ssh" in data:
            # This is handled by the wifi client
            self.ws.emit(Message("system.ssh.disable"))
            self.ws.emit(Message("speak", {
                'utterance': mycroft.dialog.get("ssh disabled")}))

        if "unit.enable-learning" in data or "unit.disable-learning" in data:
            enable = 'enable' in data
            word = 'enabled' if enable else 'disabled'

            LOG.info("Setting opt_in to: " + word)
            new_config = {'opt_in': enable}
            user_config = LocalConf(USER_CONFIG)
            user_config.merge(new_config)
            user_config.store()

            self.ws.emit(Message("speak", {
                'utterance': mycroft.dialog.get("learning " + word)}))
Exemplo n.º 35
0
def set_volume(val):
	id = alsaaudio.mixers(SOUND_CARD).index(SOUND_MIXER)
	mixer = Mixer(SOUND_MIXER, id, SOUND_CARD)
	mixer.setvolume(val)
Exemplo n.º 36
0
    def process(self, data):
        self.ws.emit(Message(data))

        if "Command: system.version" in data:
            self.ws.emit(Message("enclosure.start"))

        if "mycroft.stop" in data:
            create_signal('buttonPress')  # FIXME - Must use WS instead
            self.ws.emit(Message("mycroft.stop"))

        if "volume.up" in data:
            self.ws.emit(
                Message("IncreaseVolumeIntent", {'play_sound': True}))

        if "volume.down" in data:
            self.ws.emit(
                Message("DecreaseVolumeIntent", {'play_sound': True}))

        if "system.test.begin" in data:
            self.ws.emit(Message('recognizer_loop:sleep'))

        if "system.test.end" in data:
            self.ws.emit(Message('recognizer_loop:wake_up'))

        if "mic.test" in data:
            mixer = Mixer()
            prev_vol = mixer.getvolume()[0]
            mixer.setvolume(35)
            self.ws.emit(Message("speak", {
                'utterance': "I am testing one two three"}))

            time.sleep(0.5)  # Prevents recording the loud button press
            record("/tmp/test.wav", 3.0)
            mixer.setvolume(prev_vol)
            play_wav("/tmp/test.wav").communicate()

            # Test audio muting on arduino
            subprocess.call('speaker-test -P 10 -l 0 -s 1', shell=True)

        if "unit.shutdown" in data:
            self.ws.emit(
                Message("enclosure.eyes.timedspin",
                        {'length': 12000}))
            self.ws.emit(Message("enclosure.mouth.reset"))
            subprocess.call('systemctl poweroff -i', shell=True)

        if "unit.reboot" in data:
            self.ws.emit(
                Message("enclosure.eyes.spin"))
            self.ws.emit(Message("enclosure.mouth.reset"))
            subprocess.call('systemctl reboot -i', shell=True)

        if "unit.setwifi" in data:
            self.ws.emit(Message("mycroft.wifi.start"))

        if "unit.factory-reset" in data:
            subprocess.call(
                'rm ~/.mycroft/identity/identity2.json',
                shell=True)
            self.ws.emit(
                Message("enclosure.eyes.spin"))
            self.ws.emit(Message("enclosure.mouth.reset"))
            subprocess.call('systemctl reboot -i', shell=True)
Exemplo n.º 37
0
class Player:
    def __init__(self):   
        self.logged_in = threading.Event()
        self.end_of_track = threading.Event()

        self.queue_playlist = []
        self.prev_playlist = []
        
        self.min_volume = 70
        
        config = spotify.Config()
        config.load_application_key_file(filename='/home/pi/django_projects/mysite/spotify_app/spotify_appkey.key')
        self.session = spotify.Session(config=config)
        
        self.loop = spotify.EventLoop(self.session)
        self.loop.start()
        
        self.current_track = None

        # Connect an audio sink
        self.audio = spotify.AlsaSink(self.session)
        self.mixer = Mixer('PCM', 0)
        
        self.session.on(spotify.SessionEvent.CONNECTION_STATE_UPDATED, self.on_connection_state_updated)
        self.session.on(spotify.SessionEvent.END_OF_TRACK, self.on_end_of_track)
        
    def login(self, username, password):
        self.session.login(username, password)
        self.logged_in.wait()
        self.player = self.session.player
    
    def logout(self):
        self.session.logout()
        
    def play(self, track_uri):
        track = self.session.get_track(track_uri).load()
        self.current_track = track
        self.player.load(track)
        self.player.play()
        
    def queue(self, track_uri):
        track = self.session.get_track(track_uri).load()
        self.queue_playlist.append(track)
        
    def pause(self):
        self.player.pause()
    
    def stop(self):
        self.current_track = None
        self.player.unload()
        
    def play_prev(self):
        if len(self.prev_playlist) == 0:
            self.play(self.current_track.link.uri)
        else:
            self.queue_playlist.insert(0, self.current_track)
            self.play(self.prev_playlist.pop(0).link.uri)
        
    def play_next(self):
        self.prev_playlist.insert(0, self.current_track)
        if len(self.queue_playlist) == 0:
            self.current_track = None
        else:
            self.play(self.queue_playlist.pop(0).link.uri);
    
    def set_audio_output(self, output):
        subprocess.call(["amixer", "cset", "numid=3", str(output)])
    
    def search(self, query):
        search = self.session.search(query);
        search.load()
        return search.tracks
    
    def get_playlist(self):
        return self.queue_playlist
        
    def is_playing(self):
        if self.current_track == None:
            return False
        else:
            return True
    
    def remove_from_queue(self, index):
        self.queue_playlist.pop(int(index))
    
    def set_volume(self, volume):
        if int(volume) == self.min_volume:
            volume = 0
        self.mixer.setvolume(int(volume))

    def get_volume(self):
        return self.mixer.getvolume()[0]
    
    def on_connection_state_updated(self, session):
        if session.connection.state is spotify.ConnectionState.LOGGED_IN:
            self.logged_in.set()
        else:
            self.logged_in.clear()

    def on_end_of_track(self, self2):
        self.play_next()
Exemplo n.º 38
0
class AlarmSkill(MycroftSkill):
    # seconds between end of a beep and the start of next
    # must be bigger than the max listening time (10 sec)
    beep_gap = 15
    default_sound = "constant_beep"
    threshold = 0.7  # Threshold for word fuzzy matching

    def __init__(self):
        super(AlarmSkill, self).__init__()
        self.beep_process = None
        self.beep_start_time = None
        self.flash_state = 0

        # Seconds of gap between sound repeats.
        # The value name must match an option from the 'sound' value of the
        # settingmeta.json, which also corresponds to the name of an mp3
        # file in the skill's sounds/ folder.  E.g. <skill>/sounds/bell.mp3
        #
        self.sounds = {
            "bell": 5.0,
            "escalate": 32.0,
            "constant_beep": 5.0,
            "beep4": 4.0,
            "chimes": 22.0
        }

        #initialize alarm settings
        self.init_settings()
        try:
            self.mixer = Mixer()
        except Exception:
            # Retry instanciating the mixer
            try:
                self.mixer = Mixer()
            except Exception as e:
                self.log.error('Couldn\'t allocate mixer, {}'.format(repr(e)))
                self.mixer = None
        self.saved_volume = None

        # Alarm list format [{
        #                      "timestamp": float,
        #                      "repeat_rule": str,
        #                      "name": str,
        #                      "snooze": float
        #                    }, ...]
        # where:
        #  timestamp is a POSIX timestamp float assumed to
        #       be in the utc timezone.
        #
        #  repeat_rule is for generating the next in a series.  Valid
        #       repeat_rules include None for a one-shot alarm or any other
        #       iCalendar rule from RFC <https://tools.ietf.org/html/rfc5545>.
        #
        #  name is the name of the alarm. There can be multiple alarms of the
        #       same name.
        #
        #  snooze is the POSIX timestamp float of the Snooze assumed to
        #       be in the utc timezone.
        #
        # NOTE: Using list instead of tuple because of serialization

    def init_settings(self):
        """Add any missing default settings."""
        # default sound is 'constant_beep'
        self.settings.setdefault('max_alarm_secs', 10 * 60)  # Beep for 10 min.
        self.settings.setdefault('sound', AlarmSkill.default_sound)
        self.settings.setdefault('start_quiet', True)
        self.settings.setdefault('alarm', [])

    def dump_alarms(self, tag=""):
        # Useful when debugging
        dump = "\n" + "=" * 30 + " ALARMS " + tag + " " + "=" * 30 + "\n"
        dump += "raw = " + str(self.settings["alarm"]) + "\n\n"

        now_ts = to_utc(now_utc()).timestamp()
        dt = datetime.fromtimestamp(now_ts)
        dump += "now = {} ({})\n".format(
            nice_time(self.get_alarm_local(timestamp=now_ts),
                      speech=False,
                      use_ampm=True), now_ts)
        dump += "      U{} L{}\n".format(to_utc(dt), to_local(dt))
        dump += "\n\n"

        idx = 0
        for alarm in self.settings["alarm"]:
            dt = self.get_alarm_local(alarm)
            dump += "alarm[{}] - {} \n".format(idx, alarm)
            dump += "           Next: {} {}\n".format(
                nice_time(dt, speech=False, use_ampm=True),
                nice_date(dt, now=now_local()))
            dump += "                 U{} L{}\n".format(dt, to_local(dt))
            if 'snooze' in alarm:
                dtOrig = self.get_alarm_local(timestamp=alarm['snooze'])
                dump += "           Orig: {} {}\n".format(
                    nice_time(dtOrig, speech=False, use_ampm=True),
                    nice_date(dtOrig, now=now_local()))
            idx += 1

        dump += "=" * 75

        self.log.info(dump)

    def initialize(self):
        self.register_entity_file('daytype.entity')  # TODO: Keep?
        self.recurrence_dict = self.translate_namedvalues('recurring')

        # Time is the first value, so this will sort alarms by time
        self.settings["alarm"] = sorted(self.settings["alarm"],
                                        key=lambda a: a["timestamp"])

        # This will reschedule alarms which have expired within the last
        # 5 minutes, and cull anything older.
        self._curate_alarms(5 * 60)

        self._schedule()

        # Support query for active alarms from other skills
        self.add_event('private.mycroftai.has_alarm', self.on_has_alarm)

    def on_has_alarm(self, message):
        # Reply to requests for alarm on/off status
        total = len(self.settings["alarm"])
        self.bus.emit(message.response(data={"active_alarms": total}))

    def get_alarm_local(self, alarm=None, timestamp=None):
        if timestamp:
            ts = timestamp
        else:
            ts = alarm["timestamp"]
        return datetime.fromtimestamp(ts, default_timezone())

    def set_alarm(self, when, name=None, repeat=None):
        time = when.replace(second=0, microsecond=0)
        if repeat:
            alarm = self._create_recurring_alarm(time, repeat)
            alarm = {
                "timestamp": alarm["timestamp"],
                "repeat_rule": alarm["repeat_rule"],
                "name": name or ""
            }
        else:
            alarm = {
                "timestamp": to_utc(time).timestamp(),
                "repeat_rule": "",
                "name": name or ""
            }

        for existing in self.settings["alarm"]:
            if alarm == existing:
                self.speak_dialog("alarm.already.exists")
                return None
        self.settings["alarm"].append(alarm)
        self._schedule()
        return alarm

    def _schedule(self):
        # cancel any existing timed event
        self.cancel_scheduled_event('NextAlarm')
        self._curate_alarms()

        # set timed event for next alarm (if it exists)
        if self.settings["alarm"]:
            dt = self.get_alarm_local(self.settings["alarm"][0])
            self.schedule_event(self._alarm_expired,
                                to_system(dt),
                                name='NextAlarm')

    def _curate_alarms(self, curation_limit=1):
        """[summary]
            curation_limit (int, optional): Seconds past expired at which to
                                            remove the alarm
        """
        alarms = []
        now_ts = to_utc(now_utc()).timestamp()

        for alarm in self.settings["alarm"]:
            # Alarm format == [timestamp, repeat_rule[, orig_alarm_timestamp]]
            if alarm["timestamp"] < now_ts:
                if alarm["timestamp"] < (now_ts - curation_limit):
                    # skip playing an old alarm
                    if alarm["repeat_rule"]:
                        # reschedule in future if repeat rule exists
                        alarms.append(self._next_repeat(alarm))
                else:
                    # schedule for right now, with the
                    # third entry as the original base time
                    base = alarm["name"] if alarm["name"] == ''\
                                         else alarm["timestamp"]
                    alarms.append({
                        "timestamp": now_ts + 1,
                        "repeat_rule": alarm["repeat_rule"],
                        "name": alarm["name"],
                        "snooze": base
                    })
            else:
                alarms.append(alarm)

        alarms = sorted(alarms, key=lambda a: a["timestamp"])
        self.settings["alarm"] = alarms

    def _next_repeat(self, alarm):
        # evaluate recurrence to the next instance
        if 'snooze' in alarm:
            # repeat from original time (it was snoozed)
            ref = datetime.fromtimestamp(alarm["repeat_rule"])
        else:
            ref = datetime.fromtimestamp(alarm["timestamp"])

        # Create a repeat rule and get the next alarm occurrance after that
        start = to_utc(ref)
        rr = rrulestr("RRULE:" + alarm["repeat_rule"], dtstart=start)
        now = to_utc(now_utc())
        next = rr.after(now)

        self.log.debug("     Now={}".format(now))
        self.log.debug("Original={}".format(start))
        self.log.debug("    Next={}".format(next))

        return {
            "timestamp": to_utc(next).timestamp(),
            "repeat_rule": alarm["repeat_rule"],
            "name": alarm["name"],
        }

    def _create_recurring_alarm(self, when, recur):
        # 'recur' is a set of day index strings, e.g. {"3", "4"}
        # convert rule into an iCal rrule
        # TODO: Support more complex alarms, e.g. first monday, monthly, etc
        rule = ""
        abbr = ["SU", "MO", "TU", "WE", "TH", "FR", "SA"]
        days = []
        for day in recur:
            days.append(abbr[int(day)])
        if days:
            rule = "FREQ=WEEKLY;INTERVAL=1;BYDAY=" + ",".join(days)

        if when and rule:
            when = to_utc(when)

            # Create a repeating rule that starts in the past, enough days
            # back that it encompasses any repeat.
            past = when + timedelta(days=-45)
            rr = rrulestr("RRULE:" + rule, dtstart=past)
            now = to_utc(now_utc())
            # Get the first repeat that happens after right now
            next = rr.after(now)
            return {
                "timestamp": to_utc(next).timestamp(),
                "repeat_rule": rule,
            }
        else:
            return {
                "timestamp": None,
                "repeat_rule": rule,
            }

    def has_expired_alarm(self):
        # True is an alarm should be 'going off' now.  Snoozed alarms don't
        # count until they are triggered again.
        if not self.settings["alarm"]:
            return False

        now_ts = to_utc(now_utc()).timestamp()
        for alarm in self.settings["alarm"]:
            if alarm['timestamp'] <= now_ts:
                return True

        return False

    # Wake me on ... (hard to match with Adapt entities)
    @intent_handler(
        IntentBuilder("").require("WakeMe").optionally("Recurring").optionally(
            "Recurrence"))
    def handle_wake_me(self, message):
        self.handle_set_alarm(message)

    def _create_day_set(self, phrase):
        recur = set()
        for r in self.recurrence_dict:
            if r in phrase:
                for day in self.recurrence_dict[r].split():
                    recur.add(day)
        return recur

    def _recur_desc(self, recur):
        # Create a textual description of the recur set
        day_list = list(recur)
        day_list.sort()
        days = " ".join(day_list)
        for r in self.recurrence_dict:
            if self.recurrence_dict[r] == days:
                return r  # accept the first perfect match

        # Assemble a long desc, e.g. "Monday and Wednesday"
        day_names = []
        for day in days.split(" "):
            for r in self.recurrence_dict:
                if self.recurrence_dict[r] is day:
                    day_names.append(r)
                    break

        return join_list(day_names, self.translate('and'))

    @intent_handler(
        IntentBuilder("").require("Set").require("Alarm").optionally(
            "Recurring").optionally("Recurrence"))
    def handle_set_alarm(self, message):
        """Handler for "set an alarm for..."""
        utt = message.data.get('utterance').lower()
        recur = None

        if message.data.get('Recurring'):
            # Just ignoring the 'Recurrence' now, we support more complex stuff
            # recurrence = message.data.get('Recurrence')
            recur = self._create_day_set(utt)
            # TODO: remove days following an "except" in the utt

            while not recur:
                r = self.get_response('query.recurrence', num_retries=1)
                if not r:
                    return
                recur = self._create_day_set(r)

            if self.voc_match(utt, "Except"):
                # TODO: Support exceptions
                self.speak_dialog("no.exceptions.yet")
                return

        # Get the time
        when, utt_no_datetime = extract_datetime(utt) or (None, utt)

        # Get name from leftover string from extract_datetime
        name = self._get_alarm_name(utt_no_datetime)

        # Will return dt of unmatched string
        today = extract_datetime("today")
        today = today[0]

        # Check the time if it's midnight. This is to check if the user
        # said a recurring alarm with only the Day or if the user did
        # specify to set an alarm on midnight. If it's confirmed that
        # it's for a day only, then get another response from the user
        # to clarify what time on that day the recurring alarm is.
        is_midnight = self._check_if_utt_has_midnight(utt, when,
                                                      self.threshold)

        if (when is None or when.time() == today.time()) and not is_midnight:
            r = self.get_response('query.for.when', validator=extract_datetime)
            if not r:
                self.speak_dialog("alarm.schedule.cancelled")
                return
            when_temp = extract_datetime(r)
            if when_temp is not None:
                when_temp = when_temp[0]
                # TODO add check for midnight
                # is_midnight = self._check_if_utt_has_midnight(r, when_temp,
                #                                               self.threshold)
                when = when_temp if when is None \
                                 else datetime(tzinfo=when.tzinfo,
                                               year=when.year,
                                               month=when.month,
                                               day=when.day,
                                               hour=when_temp.hour,
                                               minute=when_temp.minute)
            else:
                when = None

        # Verify time
        alarm_time = when
        confirmed_time = False
        while (not when or when == today) and not confirmed_time:
            if recur:
                t = nice_time(alarm_time, use_ampm=True)
                conf = self.ask_yesno('confirm.recurring.alarm',
                                      data={
                                          'time': t,
                                          'recurrence': self._recur_desc(recur)
                                      })
            else:
                t = nice_date_time(alarm_time, now=today, use_ampm=True)
                conf = self.ask_yesno('confirm.alarm', data={'time': t})
            if not conf:
                return
            if conf == 'yes':
                when = [alarm_time]
                confirmed_time = True
            else:
                # check if a new (corrected) time was given
                when = extract_datetime(conf)
                if when is not None:
                    when = when[0]
                if not when or when == today:
                    # Not a confirmation and no date/time in statement, quit
                    return
                alarm_time = when
                when = None  # reverify

        alarm = {}
        if not recur:
            alarm_time_ts = to_utc(alarm_time).timestamp()
            now_ts = now_utc().timestamp()
            if alarm_time_ts > now_ts:
                alarm = self.set_alarm(alarm_time, name)
            else:
                if ('today' in utt) or ('tonight' in utt):
                    self.speak_dialog('alarm.past')
                    return
                else:
                    # Set the alarm to find the next 24 hour time slot
                    while alarm_time_ts < now_ts:
                        alarm_time_ts += 86400.0
                    alarm_time = datetime.utcfromtimestamp(alarm_time_ts)
                    alarm = self.set_alarm(alarm_time, name)
        else:
            alarm = self.set_alarm(alarm_time, name, repeat=recur)

        if not alarm:
            # none set, it was a duplicate
            return

        # Don't want to hide the animation
        self.enclosure.deactivate_mouth_events()
        if confirmed_time:
            self.speak_dialog("alarm.scheduled")
        else:
            t = self._describe(alarm)
            reltime = nice_relative_time(self.get_alarm_local(alarm))
            if recur:
                self.speak_dialog("recurring.alarm.scheduled.for.time",
                                  data={
                                      "time": t,
                                      "rel": reltime
                                  })
            else:
                self.speak_dialog("alarm.scheduled.for.time",
                                  data={
                                      "time": t,
                                      "rel": reltime
                                  })

        self._show_alarm_anim(alarm_time)
        self.enclosure.activate_mouth_events()

    def _get_alarm_name(self, utt):
        """Get the alarm name using regex on an utterance."""
        self.log.debug("Utterance being searched: " + utt)
        invalid_names = self.translate_list('invalid_names')
        rx_file = self.find_resource('name.rx', 'regex')
        if utt and rx_file:
            patterns = []
            with open(rx_file) as f:
                patterns = [
                    p.strip() for p in f.readlines()
                    if not p.strip().startswith('#')
                ]

            for pat in patterns:
                self.log.debug("Regex pattern: {}".format(pat))
                res = re.search(pat, utt)
                if res:
                    try:
                        name = res.group("Name").strip()
                        self.log.debug('Regex name extracted: '.format(name))
                        if (name and len(name.strip()) > 0
                                and name not in invalid_names):
                            return name.lower()
                    except IndexError:
                        pass
        return ''

    def _check_if_utt_has_midnight(self, utt, init_time, threshold):
        matched = False
        if init_time is None:
            return matched
        if init_time.time() == datetime(1970, 1, 1, 0, 0, 0).time():
            for word in self.translate_list('midnight'):
                matched = self._fuzzy_match(word, utt, threshold)
                if matched:
                    return matched

        return matched

    @property
    def use_24hour(self):
        return self.config_core.get('time_format') == 'full'

    def _describe(self, alarm):
        if alarm["repeat_rule"]:
            # Describe repeating alarms
            if alarm["repeat_rule"].startswith(
                    "FREQ=WEEKLY;INTERVAL=1;BYDAY="):
                days = alarm["repeat_rule"][29:]  # e.g. "SU,WE"
                days = (days.replace("SU", "0").replace("MO", "1").replace(
                    "TU", "2").replace("WE", "3").replace("TH", "4").replace(
                        "FR", "5").replace("SA",
                                           "6").replace(",", " "))  # now "0 3"
                recur = set()
                for day in days.split():
                    recur.add(day)
                desc = self._recur_desc(recur)
            else:
                desc = self.translate('repeats')

            dt = self.get_alarm_local(alarm)

            dialog = 'recurring.alarm'
            if alarm["name"] is not "":
                dialog = dialog + '.named'
            return self.translate(dialog,
                                  data={
                                      'time': nice_time(dt, use_ampm=True),
                                      'recurrence': desc,
                                      'name': alarm["name"]
                                  })
        else:
            dt = self.get_alarm_local(alarm)
            dt_string = nice_date_time(dt, now=now_local(), use_ampm=True)
            if alarm["name"]:
                return self.translate('alarm.named',
                                      data={
                                          'datetime': dt_string,
                                          'name': alarm["name"]
                                      })
            else:
                return dt_string

    @intent_handler(
        IntentBuilder("").require("Query").optionally("Next").require(
            "Alarm").optionally("Recurring"))
    def handle_status(self, message):

        utt = message.data.get("utterance")

        if not len(self.settings["alarm"]):
            self.speak_dialog("alarms.list.empty")
            return

        status, alarms = self._get_alarm_matches(utt,
                                                 alarm=self.settings["alarm"],
                                                 max_results=3,
                                                 dialog='ask.which.alarm',
                                                 is_response=False)
        total = None
        desc = []
        if alarms:
            total = len(alarms)
            for alarm in alarms:
                desc.append(self._describe(alarm))

        items_string = ''
        if desc:
            items_string = join_list(desc, self.translate('and'))

        if status == 'No Match Found':
            self.speak_dialog('alarm.not.found')
        elif status == 'User Cancelled':
            return
        elif status == 'Next':
            reltime = nice_relative_time(self.get_alarm_local(alarms[0]))

            self.speak_dialog("next.alarm",
                              data={
                                  "when": self._describe(alarms[0]),
                                  "duration": reltime
                              })
        else:
            if total == 1:
                reltime = nice_relative_time(self.get_alarm_local(alarms[0]))
                self.speak_dialog('alarms.list.single',
                                  data={
                                      'item': desc[0],
                                      'duration': reltime
                                  })
            else:
                self.speak_dialog('alarms.list.multi',
                                  data={
                                      'count': total,
                                      'items': items_string
                                  })

    def _get_alarm_matches(self,
                           utt,
                           alarm=None,
                           max_results=1,
                           dialog='ask.which.alarm',
                           is_response=False):
        """Get list of alarms that match based on a user utterance.
        Arguments:
            utt (str): string spoken by the user
            alarm (list): list of alarm to match against
            max_results (int): max number of results desired
            dialog (str): name of dialog file used for disambiguation
            is_response (bool): is this being called by get_response
        Returns:
            (str): ["All", "Matched", "No Match Found", or "User Cancelled"]
            (list): list of matched alarm
        """
        alarms = alarm or self.settings['alarm']
        all_words = self.translate_list('all')
        next_words = self.translate_list('next')
        status = ["All", "Matched", "No Match Found", "User Cancelled", "Next"]

        # No alarms
        if alarms is None or len(alarms) == 0:
            self.log.error("Cannot get match. No active alarms.")
            return (status[2], None)

        # Extract Alarm Time
        when, utt_no_datetime = extract_datetime(utt) or (None, None)

        # Will return dt of unmatched string
        today = extract_datetime("today")
        today = today[0]

        # Check the time if it's midnight. This is to check if the user
        # said a recurring alarm with only the Day or if the user did
        # specify to set an alarm on midnight. If it's confirmed that
        # it's for a day only, then get another response from the user
        # to clarify what time on that day the recurring alarm is.
        is_midnight = self._check_if_utt_has_midnight(utt, when,
                                                      self.threshold)

        if when == today and not is_midnight:
            when = None

        time_matches = None
        time_alarm = None
        if when:
            time_alarm = to_utc(when).timestamp()
            if is_midnight:
                time_alarm = time_alarm + 86400.0
            time_matches = [
                a for a in alarms if abs(a["timestamp"] - time_alarm) <= 60
            ]

        # Extract Recurrence
        recur = None
        recurrence_matches = None
        for word in self.recurrence_dict:
            is_match = self._fuzzy_match(word, utt.lower(), self.threshold)
            if is_match:
                recur = self._create_day_set(utt)
                alarm_recur = self._create_recurring_alarm(when, recur)
                recurrence_matches = [
                    a for a in alarms
                    if a["repeat_rule"] == alarm_recur["repeat_rule"]
                ]
                break

        utt = utt_no_datetime or utt

        # Extract Ordinal/Cardinal Numbers
        number = extract_number(utt, ordinals=True)
        if number and number > 0:
            number = int(number)
        else:
            number = None

        # Extract Name
        name_matches = [
            a for a in alarms
            if a["name"] and self._fuzzy_match(a["name"], utt, self.threshold)
        ]

        # Match Everything
        alarm_to_match = None
        if when:
            if recur:
                alarm_to_match = alarm_recur
            else:
                alarm_to_match = {"timestamp": time_alarm, "repeat_rule": ""}

        # Find the Intersection of the Alarms list and all the matched alarms
        orig_count = len(alarms)
        if when and time_matches:
            alarms = [a for a in alarms if a in time_matches]
        if recur and recurrence_matches:
            alarms = [a for a in alarms if a in recurrence_matches]
        if name_matches:
            alarms = [a for a in alarms if a in name_matches]

        # Utterance refers to all alarms
        if utt and any(self._fuzzy_match(i, utt, 1) for i in all_words):
            return (status[0], alarms)
        # Utterance refers to the next alarm to go off
        elif utt and any(self._fuzzy_match(i, utt, 1) for i in next_words):
            return (status[4], [alarms[0]])

        # Given something to match but no match found
        if ((number and number > len(alarms))
                or (recur and not recurrence_matches)
                or (when and not time_matches)):
            return (status[2], None)
        # If number of alarms filtered were the same, assume user asked for
        # All alarms
        if (len(alarms) == orig_count and max_results > 1 and not number
                and not when and not recur):
            return (status[0], alarms)
        # Return immediately if there is ordinal
        if number and number <= len(alarms):
            return (status[1], [alarms[number - 1]])
        # Return immediately if within maximum results
        elif alarms and len(alarms) <= max_results:
            return (status[1], alarms)
        # Ask for reply from user and iterate the function
        elif alarms and len(alarms) > max_results:
            desc = []
            for alarm in alarms:
                desc.append(self._describe(alarm))

            items_string = ''
            if desc:
                items_string = join_list(desc, self.translate('and'))

            reply = self.get_response(dialog,
                                      data={
                                          'number': len(alarms),
                                          'list': items_string,
                                      },
                                      num_retries=1)
            if reply:
                return self._get_alarm_matches(reply,
                                               alarm=alarms,
                                               max_results=max_results,
                                               dialog=dialog,
                                               is_response=True)
            else:
                return (status[3], None)

        # No matches found
        return (status[2], None)

    def _fuzzy_match(self, word, phrase, threshold):
        """
            Search a phrase to another phrase using fuzzy_match. Matches on a
            per word basis, and will not match if word is a subword.
            Args:
                word (str): string to be searched on a phrase
                phrase (str): string to be matched against the word
                threshold (int): minimum fuzzy matching score to be considered
                                 a match
            Returns:
                (boolean): True if word is found in phrase. False if not.
        """
        matched = False
        score = 0
        phrase_split = phrase.split(' ')
        word_split_len = len(word.split(' '))

        for i in range(len(phrase_split) - word_split_len, -1, -1):
            phrase_comp = ' '.join(phrase_split[i:i + word_split_len])
            score_curr = fuzzy_match(phrase_comp, word.lower())

            if score_curr > score and score_curr >= threshold:
                score = score_curr
                matched = True

        return matched

    @intent_handler(
        IntentBuilder("").require("Delete").require("Alarm").optionally(
            "Recurring").optionally("Recurrence"))
    def handle_delete(self, message):
        if self.has_expired_alarm():
            self._stop_expired_alarm()
            return

        total = len(self.settings["alarm"])
        if not total:
            self.speak_dialog("alarms.list.empty")
            return

        utt = message.data.get("utterance") or ""

        status, alarms = self._get_alarm_matches(
            utt,
            alarm=self.settings["alarm"],
            max_results=1,
            dialog='ask.which.alarm.delete',
            is_response=False)

        if alarms:
            total = len(alarms)
        else:
            total = None

        if total == 1:
            desc = self._describe(alarms[0])
            recurring = ".recurring" if alarms[0]["repeat_rule"] else ""
            if self.ask_yesno('ask.cancel.desc.alarm' + recurring,
                              data={'desc': desc}) == 'yes':
                del self.settings["alarm"][self.settings["alarm"].index(
                    alarms[0])]
                self._schedule()
                self.speak_dialog("alarm.cancelled.desc" + recurring,
                                  data={'desc': desc})
                return
            else:
                self.speak_dialog("alarm.delete.cancelled")
                # As the user did not confirm to delete
                # return True to skip all the remaining conditions
                return
        elif status in ['Next', 'All', 'Matched']:
            if self.ask_yesno('ask.cancel.alarm.plural',
                              data={"count": total}) == 'yes':
                self.settings['alarm'] = [
                    a for a in self.settings['alarm'] if a not in alarms
                ]
                self._schedule()
                self.speak_dialog('alarm.cancelled.multi',
                                  data={"count": total})
            return
        elif not total:
            # Failed to delete
            self.speak_dialog("alarm.not.found")

        return

    @intent_file_handler('snooze.intent')
    def snooze_alarm(self, message):
        if not self.has_expired_alarm():
            return

        self.__end_beep()
        self.__end_flash()

        utt = message.data.get('utterance') or ""
        snooze_for = extract_number(utt)
        if not snooze_for or snooze_for < 1:
            snooze_for = 9  # default to 9 minutes

        # Snooze always applies the the first alarm in the sorted array
        alarm = self.settings["alarm"][0]
        dt = self.get_alarm_local(alarm)
        snooze = to_utc(dt) + timedelta(minutes=snooze_for)

        if "snooze" in alarm:
            # already snoozed
            original_time = alarm["snooze"]
        else:
            original_time = alarm["timestamp"]

        # Fill schedule with a snoozed entry -- 3 items:
        #    snooze_expire_timestamp, repeat_rule, original_timestamp
        self.settings["alarm"][0] = {
            "timestamp": snooze.timestamp(),
            "repeat_rule": alarm["repeat_rule"],
            "name": alarm["name"],
            "snooze": original_time
        }
        self._schedule()

    @intent_file_handler('change.alarm.sound.intent')
    def handle_change_alarm(self, message):
        self.speak_dialog("alarm.change.sound")


##########################################################################
# Audio and Device Feedback

    def converse(self, utterances, lang="en-us"):
        if self.has_expired_alarm():
            if utterances and self.voc_match(utterances[0], "StopBeeping"):
                self._stop_expired_alarm()
                return True  # and consume this phrase

    def stop(self, message=None):
        if self.has_expired_alarm():
            self._stop_expired_alarm()
            return True  # Stop signal handled no need to listen
        else:
            return False

    def _play_beep(self, message=None):
        """ Play alarm sound file """
        now = now_local()

        if not self.beep_start_time:
            self.beep_start_time = now
        elif ((now - self.beep_start_time).total_seconds() >
              self.settings["max_alarm_secs"]):
            # alarm has been running long enough, auto-quiet it
            self.log.info("Automatically quieted alarm after 10 minutes")
            self._stop_expired_alarm()
            return

        # Validate user-selected alarm sound file
        alarm_file = join(abspath(dirname(__file__)), 'sounds',
                          self.sound_name + ".mp3")
        if not os.path.isfile(alarm_file):
            # Couldn't find the required sound file
            self.sound_name = AlarmSkill.default_sound
            alarm_file = join(abspath(dirname(__file__)), 'sounds',
                              self.sound_name + ".mp3")

        beep_duration = self.sounds[self.sound_name]
        repeat_interval = beep_duration + AlarmSkill.beep_gap

        next_beep = now + timedelta(seconds=repeat_interval)

        self.cancel_scheduled_event('Beep')
        self.schedule_event(self._play_beep, to_system(next_beep), name='Beep')

        # Increase volume each pass until fully on
        if self.saved_volume:
            if self.volume < 90:
                self.volume += 10
            self.mixer.setvolume(self.volume)

        try:
            self.beep_process = play_mp3(alarm_file)
        except Exception:
            self.beep_process = None

    def _while_beeping(self, message):
        if self.flash_state < 3:
            if self.flash_state == 0:
                alarm_timestamp = message.data["alarm_time"]
                dt = self.get_alarm_local(timestamp=alarm_timestamp)
                self._render_time(dt)
            self.flash_state += 1
        else:
            self.enclosure.mouth_reset()
            self.flash_state = 0

        # Check if the WAV is still playing
        if self.beep_process:
            self.beep_process.poll()
            if self.beep_process.returncode:
                # The playback has ended
                self.beep_process = None

    def __end_beep(self):
        self.cancel_scheduled_event('Beep')
        self.beep_start_time = None
        if self.beep_process:
            try:
                if self.beep_process.poll() is None:  # still running
                    self.beep_process.kill()
            except Exception:
                pass
            self.beep_process = None
        self._restore_volume()
        self._restore_listen_beep()

    def _restore_listen_beep(self):
        if 'user_beep_setting' in self.settings:
            # Wipe from local config
            new_conf_values = {"confirm_listening": False}
            user_config = LocalConf(USER_CONFIG)

            if (self.settings["user_beep_setting"] is None
                    and "confirm_listening" in user_config):
                del user_config["confirm_listening"]
            else:
                user_config.merge(
                    {"confirm_listening": self.settings["user_beep_setting"]})
            user_config.store()

            # Notify all processes to update their loaded configs
            self.bus.emit(Message('configuration.updated'))
            del self.settings["user_beep_setting"]

    def _stop_expired_alarm(self):
        if self.has_expired_alarm():
            self.__end_beep()
            self.__end_flash()
            self.cancel_scheduled_event('NextAlarm')

            self._curate_alarms(0)  # end any expired alarm
            self._schedule()
            return True
        else:
            return False

    def _restore_volume(self):
        # Return global volume to the appropriate level if we've messed with it
        if self.saved_volume:
            self.mixer.setvolume(self.saved_volume[0])
            self.saved_volume = None

    def _disable_listen_beep(self):
        user_config = LocalConf(USER_CONFIG)

        if 'user_beep_setting' not in self.settings:
            # Save any current local config setting
            self.settings['user_beep_setting'] = (user_config.get(
                "confirm_listening", None))

            # Disable in local config
            user_config.merge({"confirm_listening": False})
            user_config.store()

            # Notify all processes to update their loaded configs
            self.bus.emit(Message('configuration.updated'))

    def _alarm_expired(self):
        self.sound_name = self.settings["sound"]  # user-selected alarm sound
        if not self.sound_name or self.sound_name not in self.sounds:
            # invalid sound name, use the default
            self.sound_name = AlarmSkill.default_sound

        if self.settings['start_quiet'] and self.mixer:
            if not self.saved_volume:  # don't overwrite if already saved!
                self.saved_volume = self.mixer.getvolume()
                self.volume = 0  # increase by 10% each pass
        else:
            self.saved_volume = None

        self._disable_listen_beep()

        self._play_beep()

        # Once a second Flash the alarm and auto-listen
        self.flash_state = 0
        self.enclosure.deactivate_mouth_events()
        alarm = self.settings["alarm"][0]
        self.schedule_repeating_event(self._while_beeping,
                                      0,
                                      1,
                                      name='Flash',
                                      data={"alarm_time": alarm["timestamp"]})

    def __end_flash(self):
        self.cancel_scheduled_event('Flash')
        self.enclosure.mouth_reset()
        self.enclosure.activate_mouth_events()

    def _show_alarm_anim(self, dt):
        # Animated confirmation of the alarm
        self.enclosure.mouth_reset()

        self._render_time(dt)
        time.sleep(2)
        self.enclosure.mouth_reset()

        # Show an animation
        # TODO: mouth_display_png() is choking images > 8x8
        #       (likely on the enclosure side)
        for i in range(1, 16):
            png = join(abspath(dirname(__file__)), "anim",
                       "Alarm-" + str(int(i)) + "-1.png")
            # self.enclosure.mouth_display_png(png, x=0, y=0, refresh=False,
            #                                  invert=True)
            png = join(abspath(dirname(__file__)), "anim",
                       "Alarm-" + str(int(i)) + "-2.png")
            if i < 8:
                self.enclosure.mouth_display_png(png,
                                                 x=8,
                                                 y=0,
                                                 refresh=False,
                                                 invert=True)
            png = join(abspath(dirname(__file__)), "anim",
                       "Alarm-" + str(int(i)) + "-3.png")
            self.enclosure.mouth_display_png(png,
                                             x=16,
                                             y=0,
                                             refresh=False,
                                             invert=True)
            png = join(abspath(dirname(__file__)), "anim",
                       "Alarm-" + str(int(i)) + "-4.png")
            self.enclosure.mouth_display_png(png,
                                             x=24,
                                             y=0,
                                             refresh=False,
                                             invert=True)

            if i == 4:
                time.sleep(1)
            else:
                time.sleep(0.15)
        self.enclosure.mouth_reset()

    def _render_time(self, datetime):
        # Show the time in numbers "8:00 AM"
        timestr = nice_time(datetime,
                            speech=False,
                            use_ampm=True,
                            use_24hour=self.use_24hour)
        x = 16 - ((len(timestr) * 4) // 2)  # centers on display
        if not self.use_24hour:
            x += 1  # account for wider letters P and M, offset by the colon

        # draw on the display
        for ch in timestr:
            if ch == ":":
                png = "colon.png"
                w = 2
            elif ch == " ":
                png = "blank.png"
                w = 2
            elif ch == 'A' or ch == 'P' or ch == 'M':
                png = ch + ".png"
                w = 5
            else:
                png = ch + ".png"
                w = 4

            png = join(abspath(dirname(__file__)), "anim", png)
            self.enclosure.mouth_display_png(png, x=x, y=2, refresh=False)
            x += w