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()
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)
class MuteSkill(MycroftSkill): """ A Skill to control playback on a Kodi instance via the json-rpc interface. """ def __init__(self): super(MuteSkill, self).__init__(name="MuteSkill") self.last_volume = 11 self.default_volume = 11 def initialize(self): self.load_data_files(dirname(__file__)) self.mixer = Mixer() @intent_handler(IntentBuilder('MuteIntent').require("MuteKeyword").build()) def handle_mute_intent(self, message): temp_volume = self.mixer.getvolume()[0] if temp_volume > 0: self.last_volume = temp_volume LOG.info(self.last_volume) self.mixer.setvolume(0) @intent_handler( IntentBuilder('UnMuteIntent').require("UnMuteKeyword").build()) def handle_un_mute_intent(self, message): if self.last_volume == 0: self.last_volume = self.default_volume self.mixer.setvolume(self.last_volume) def stop(self): pass
class VolumeController: def __init__(self): # `Mixer` defaults to 'Master' # `mixers()` gives all available volume controls self.mixer = Mixer(mixers()[0]) # get the current volume self.current_vol = self.mixer.getvolume()[0] def get_handler(self, key): mapping = { 'toggle_mute': self.toggle_mute, 'vol_up': self.volume_up, 'vol_down': self.volume_down, } return mapping.get(key) def toggle_mute(self): if self.mixer.getmute()[0]: self.mixer.setmute(False) else: self.mixer.setmute(True) def volume_up(self): self.current_vol += 5 if self.current_vol > 100: self.current_vol = 100 self.mixer.setvolume(self.current_vol) def volume_down(self): self.current_vol -= 5 if self.current_vol < 0: self.current.vol = 0 self.mixer.setvolume(self.current_vol)
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)
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)
def action3(self): sound = Mixer() vol = sound.getvolume()[ 0] # we can take either 0 or 1 it does not matter vol -= self.step * self.amplifier self.amplifier = 1 # reset the amplifier sound.setvolume(vol) os.system('notify-send "Volume down to %s"' % vol)
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
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)
def initialize(voice_command, sentences): if system() == "Windows": press('volumedown', presses=5) else: m = Mixer() current_volume = m.getvolume() # Get the current Volume m.setvolume(current_volume - 10) # Set the volume to -10%. return True, ""
def action2(self): sound = Mixer() vol = sound.getvolume( )[0] # we can take either 0 or 1 from what I gathered, it does not matter vol += self.step * self.amplifier self.amplifier = 1 # reset the amplifier try: sound.setvolume(vol) os.system('notify-send "Volume up to %s"' % vol) except Exception: os.system('notify-send "Volume is already up to it\'s maximum"')
def click(self, button): if button == 1: mixer = Mixer(self.mutedev) mixer.setmute(not bool(mixer.getmute()[0])) elif button == 4: mixer = Mixer(self.voldev) try: mixer.setvolume(mixer.getvolume()[0] + self.step) except ALSAAudioError: return elif button == 5: mixer = Mixer(self.voldev) try: mixer.setvolume(mixer.getvolume()[0] - self.step) except ALSAAudioError: return self.update()
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
class Sound: _VOLUME_DELTA = 5 def __init__(self): self._mixer = Mixer("PCM") self._volume_before_muted = None self._old_volumes, self._current_volume = self._init_volume() def _init_volume(self): old_volumes = self._mixer.getvolume() # use floor_to_base_int to round down to a multiple of _VOLUME_DELTA min_volume = floor_to_base_int(min(old_volumes), self._VOLUME_DELTA) self._mixer.setvolume(min_volume) return old_volumes, min_volume def reset_volume(self): for channel in range(2): self._mixer.setvolume(self._old_volumes[channel], channel) def _change_volume(self, operation=None, exact_value=None): if operation is not None: new_volume = operation(self._current_volume, self._VOLUME_DELTA) elif exact_value is not None: new_volume = exact_value else: return if new_volume > 100: new_volume = 100 elif new_volume < 0: new_volume = 0 if new_volume != self._current_volume: self._mixer.setvolume(new_volume) self._current_volume = new_volume def volume_up(self): if self._volume_before_muted is None: self._change_volume(add) else: self._change_volume(exact_value=self._volume_before_muted) self._volume_before_muted = None def volume_down(self): if self._volume_before_muted is None: self._change_volume(sub) def mute(self): self._volume_before_muted = self._current_volume self._change_volume(exact_value=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
def __update_volume(self, change=0): """ Attempt to change audio level Args: change (int): +1 or -1; the step to change by Returns: int: new level code (0..11) bool: whether level 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
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)
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()
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()
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()
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" ) muted = "M" unmuted = "" color_muted = "#AAAAAA" color = "#FFFFFF" format = "♪: {volume}" format_muted = None mixer = "Master" mixer_id = 0 card = 0 channel = 0 increment = 5 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, } 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, } def switch_mute(self): if self.has_mute: muted = self.alsamixer.getmute()[self.channel] self.alsamixer.setmute(not muted) def increase_volume(self, delta=None): vol = self.alsamixer.getvolume()[self.channel] self.alsamixer.setvolume(min(100, vol + (delta if delta else self.increment))) def decrease_volume(self, delta=None): vol = self.alsamixer.getvolume()[self.channel] self.alsamixer.setvolume(max(0, vol - (delta if delta else self.increment)))
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)
x2, y2 = lm_list[8][1], lm_list[8][2] cx, cy = (x1 + x2) // 2, (y1 + y2) // 2 cv2.circle(img, (x1, y1), 15, (255, 0, 255), cv2.FILLED) cv2.circle(img, (x2, y2), 15, (255, 0, 255), cv2.FILLED) cv2.line(img, (x1, y1), (x2, y2), (255, 0, 255), 3) cv2.circle(img, (cx, cy), 15, (255, 0, 255), cv2.FILLED) length = math.hypot(x2 - x1, y2 - y1) # Hand range 50 - 300 vol = np.interp(length, [50, 300], [0, 100]) vol_bar = np.interp(length, [50, 300], [400, 150]) vol_per = np.interp(length, [50, 300], [0, 100]) mixer.setvolume(int(vol)) vol = mixer.getvolume() if length < 50: cv2.circle(img, (cx, cy), 15, (255, 0, 0), cv2.FILLED) cv2.rectangle(img, (50, 150), (85, 400), (250, 0, 0), 3) cv2.rectangle(img, (50, int(vol_bar)), (85, 400), (255, 0, 0), cv2.FILLED) cv2.putText(img, f'{int(vol_per)} %', (50, 450), cv2.FONT_HERSHEY_PLAIN, 2, (250, 0, 0), 3) cTime = time.time() fps = 1 / (cTime - pTime) pTime = cTime cv2.putText(img, f'FPS: {int(fps)}', (10, 70), cv2.FONT_HERSHEY_PLAIN, 2, (255, 0, 0), 3)
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"})) delta = int(s.recv(4096)) if delta != 0 : mixer.setvolume(min(100, mixer.getvolume()[0] + delta)) client.clear() client.add("blup.mp3") client.play() s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) s.connect("mastersocket") s.send(jdumps({"request": "get_sw_state"})) data = int(s.recv(4096)) print data if data : show_menu(background, hex_to_rgb(conf["general"]["back_color"]))
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") muted = "M" unmuted = "" color_muted = "#AAAAAA" color = "#FFFFFF" format = "♪: {volume}" format_muted = None mixer = "Master" mixer_id = 0 card = 0 channel = 0 increment = 5 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, } 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, } def switch_mute(self): if self.has_mute: muted = self.alsamixer.getmute()[self.channel] self.alsamixer.setmute(not muted) def increase_volume(self, delta=None): vol = self.alsamixer.getvolume()[self.channel] self.alsamixer.setvolume( min(100, vol + (delta if delta else self.increment))) def decrease_volume(self, delta=None): vol = self.alsamixer.getvolume()[self.channel] self.alsamixer.setvolume( max(0, vol - (delta if delta else self.increment)))
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))
def get_volume(): id = alsaaudio.mixers(SOUND_CARD).index(SOUND_MIXER) mixer = Mixer(SOUND_MIXER, id, SOUND_CARD) return mixer.getvolume()[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})
def handle_set_volume(self, message): mixer = Mixer() level = self.get_volume_level(message, mixer.getvolume()[0]) mixer.setvolume(self.level_to_volume(level))
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 = -1 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))
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)}))
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()
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"})) delta = int(s.recv(4096)) if delta != 0: mixer.setvolume(min(100, mixer.getvolume()[0] + delta)) client.clear() client.add("blup.mp3") client.play() s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) s.connect("mastersocket") s.send(jdumps({"request": "get_sw_state"})) data = int(s.recv(4096)) print data if data: show_menu(background, hex_to_rgb(conf["general"]["back_color"]))
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)}))
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")}))
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, }
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)
def process(self, 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: 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")})) time.sleep(5) 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")}))
class VolumeSkill(MycroftSkill): """ Control the audio volume for the Mycroft system Terminology: "Level" = Mycroft volume levels, from 0 to 11 "Volume" = ALSA mixer setting, from 0 to 100 """ MIN_LEVEL = 0 MAX_LEVEL = 11 # TODO: Translation layer (have to match word in Level.voc) VOLUME_WORDS = {'loud': 11, 'normal': 9, 'quiet': 7} def __init__(self): super(VolumeSkill, self).__init__("VolumeSkill") self.default_level = self.config.get('default_level') self.min_volume = self.config.get('min_volume') self.max_volume = self.config.get('max_volume') self.volume_sound = join(dirname(__file__), "blop-mark-diangelo.wav") try: self.mixer = Mixer("PCM") except Exception: # Retry instanciating the mixer try: self.mixer = Mixer() except Exception as e: self.log.error('Couldn\'t allocate mixer, {}'.format(repr(e))) def initialize(self): self.log.info("********** Reeg handlers") intent = IntentBuilder("IncreaseVolume").require("Volume").require( "Increase").build() self.register_intent(intent, self.handle_increase_volume) intent = IntentBuilder("DecreaseVolume").require("Volume").require( "Decrease").build() self.register_intent(intent, self.handle_decrease_volume) intent = IntentBuilder("MuteVolume").require("Volume").require( "Mute").build() self.register_intent(intent, self.handle_mute_volume) intent = IntentBuilder("UnmuteVolume").require("Volume").require( "Unmute").build() self.register_intent(intent, self.handle_unmute_volume) try: # Register handlers for messagebus events self.add_event('mycroft.volume.increase', self.handle_increase_volume) self.add_event('mycroft.volume.decrease', self.handle_decrease_volume) self.add_event('mycroft.volume.mute', self.handle_mute_volume) self.add_event('mycroft.volume.unmute', self.handle_unmute_volume) self.log.info("********** Handlers registered") except: pass @intent_handler( IntentBuilder("SetVolume").require("Volume").require("Level")) def handle_set_volume(self, message): level = self.__get_volume_level(message, self.mixer.getvolume()[0]) self.mixer.setvolume(self.__level_to_volume(level)) self.speak_dialog('set.volume', data={'volume': level}) @intent_handler( IntentBuilder("QueryVolume").require("Volume").require("Query")) def handle_query_volume(self, message): level = self.__get_volume_level(message, self.mixer.getvolume()[0]) self.speak_dialog('volume.is', data={'volume': level}) def __communicate_volume_change(self, message, dialog, code, changed): play_sound = message.data.get('play_sound', False) if play_sound: if changed: play_wav(self.volume_sound) else: if not changed: dialog = 'already.max.volume' self.speak_dialog(dialog, data={'volume': code}) # @intent_handler(IntentBuilder("IncreaseVolume").require( # "Volume").require("Increase")) def handle_increase_volume(self, message): self.__communicate_volume_change(message, 'increase.volume', *self.__update_volume(+1)) # @intent_handler(IntentBuilder("DecreaseVolume").require( # "Volume").require("Decrease")) def handle_decrease_volume(self, message): self.__communicate_volume_change(message, 'decrease.volume', *self.__update_volume(-1)) # @intent_handler(IntentBuilder("MuteVolume").require( # "Volume").require("Mute")) def handle_mute_volume(self, message): speak_message = message.data.get('speak_message', True) if speak_message: self.speak_dialog('mute.volume') wait_while_speaking() self.mixer.setvolume(0) # @intent_handler(IntentBuilder("UnmuteVolume").require( # "Volume").require("Unmute")) def handle_unmute_volume(self, message): self.mixer.setvolume(self.__level_to_volume(11)) speak_message = message.data.get('speak_message', True) if speak_message: self.speak_dialog('reset.volume', data={'volume': self.default_level}) def __volume_to_level(self, volume): """ Convert a 'volume' to a 'level' Args: volume (int): min_volume..max_volume Returns: int: the equivalent level """ range = self.MAX_LEVEL - self.MIN_LEVEL prop = float(volume - self.min_volume) / self.max_volume level = int(round(self.MIN_LEVEL + range * prop)) if level > self.MAX_LEVEL: level = self.MAX_LEVEL elif level < self.MIN_LEVEL: level = self.MIN_LEVEL return level def __level_to_volume(self, level): """ Convert a 'level' to a 'volume' Args: level (int): 0..MAX_LEVEL Returns: int: the equivalent volume """ range = self.max_volume - self.min_volume prop = float(level) / self.MAX_LEVEL volume = int(round(self.min_volume + int(range) * prop)) return volume @staticmethod def __bound_level(level): if level > VolumeSkill.MAX_LEVEL: level = VolumeSkill.MAX_LEVEL elif level < VolumeSkill.MIN_LEVEL: level = VolumeSkill.MIN_LEVEL return level def __update_volume(self, change=0): """ Attempt to change audio level Args: change (int): +1 or -1; the step to change by Returns: int: new level code (0..11) bool: whether level changed """ old_level = self.__volume_to_level(self.mixer.getvolume()[0]) new_level = self.__bound_level(old_level + change) self.enclosure.eyes_volume(new_level) self.mixer.setvolume(self.__level_to_volume(new_level)) return new_level, new_level != old_level def __get_volume_level(self, message, default=None): level_str = message.data.get('Level', default) level = self.default_level try: level = self.VOLUME_WORDS[level_str] except KeyError: try: level = int(level_str) if (level > self.MAX_LEVEL): # Guess that the user said something like 100 percent # so convert that into a level value level = self.MAX_LEVEL * level / 100 except ValueError: pass level = self.__bound_level(level) return level
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})
#!/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))
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', []) @property def use_24hour(self): return self.config_core.get('time_format') == 'full' 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=not self.use_24hour, use_24hour = self.use_24hour), 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= not self.use_24hour, use_24hour = self.use_24hour), 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= not self.use_24hour, use_24hour = self.use_24hour), 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) # Confirmations vocabs with open((dirname(realpath(__file__))+"/vocab/"+self.lang+"/text.json"),encoding='utf8') as f: self.texts = json.load(f) 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] == 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, lang=self.lang) 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(self.texts.get('today'), lang=self.lang) 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, lang=self.lang) 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=not self.use_24hour, use_24hour = self.use_24hour) 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= not self.use_24hour, use_24hour = self.use_24hour, lang=self.lang) 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, lang=self.lang) 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 (self.texts.get('today') in utt) or (self.texts.get('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), lang=self.lang) 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 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"] != "": dialog = dialog + '.named' return self.translate(dialog, data={'time': nice_time(dt, use_ampm=not self.use_24hour,speech=True, use_24hour = self.use_24hour, lang=self.lang), 'recurrence': desc, 'name': alarm["name"]}) else: dt = self.get_alarm_local(alarm) dt_string = nice_date_time(dt, now=now_local(), use_ampm= not self.use_24hour, use_24hour = False, lang=self.lang) 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]), lang=self.lang ) 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]), lang=self.lang ) 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, lang=self.lang) or (None, None) # Will return dt of unmatched string today = extract_datetime(self.texts.get('today'), lang=self.lang) 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, lang=self.lang) 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= not self.use_24hour, 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
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))