Skip to content

2. Configuration

Seongnoh Sean Yi edited this page Sep 24, 2024 · 3 revisions

Configuration

/* in config/config.js */
{
  module: "MMM-Hotword",
  position: "bottom_left",
  config: {
    verbose: true,
    hotwords: [
      { hotword: 'JARVIS' },
    ...
    // Everything is defined by default, but if you want to change something, describe the properties here.
  },
},

Config options

property default description
verbose false When you set it as true, more detailed information will be logged.
startOnBoot true Auto-start on the bootup.
device -1 Mic index or name to use. System default device would have -1. You can see the device list in the log.
sensitivity 0.5 Default sensitivity of the mic to detect a voice. This value will be applied to all hot word definitions unless you re-define individually in a hot word.
continuousRecording false Default behaviour when the hot word is detected. This value will be applied to all hot word definitions unless you re-define individually in a hot word.
restart false After all jobs are done, restart the detector again. This could also be redefinable in each individual hot word.
restartDelay 1000 (ms) delay before restart
onDetect () => {} CALLBACK Function. You can do your default job when any hot word is detected. (This would be overridable in each hotword definition.)
languageModel null For English, just leave as null. You may need this for non-English custom hotwords.
e.g) 'porcupine_params_ko.pv' for Korean hotword.
(Explained below)
hotwords [...hotword objects] Definitions of hotword. (Explained below)
recorderFrameLenght 512 (For expert) Generally you don't need to care about this.
recorderBufferedFramesCount 50 (For expert) Generally you don't need to care about this.
tooShortRecording 1000 (ms) When continuous recording is shorter than this, it will be ignored.
tooLongRecording 60000 (ms) Max continuous-recording length.
soundThreshold 0.1 0 ~ 1 available.
Ideally, 0 is absolutely silent, and the loudness of your voice will increase this value.
This value may vary depending on your devices and environments. You may need many trials to find a proper value.
silentFrames 100 The count of continuous frames below soundThreashold to detect end-of-utterance silence. It may vary depending on your devices and environments. Usually, this 100 value will be around 1.5 sec.
waitTimeout 30000 (ms) Waiting for continuousRecording without voice will be stopped on this timer.
  • recorderFrameLength is the length of the audio frames to receive and recorderBufferedFramesCount is the internal buffer size. 512 * 50 / 16000 (sample Rate) would be 1.6 sec. If this value is too low, buffer overflows could occur, and audio frames could be dropped. A higher value will increase memory usage.
  • When continuousRecording: true is set, The detector will wait for the following vocal phrase of you until the end of the utterance or somewhat pause. Then the detected hotword and recorded voice would be delivered.
{
  hotword: 'COMPUTER',
  continuousRecording: false,
},
// The detector will listen to your word, only `COMPUTER`. 

{
  hotword: 'COMPUTER',
  continuousRecording: true,
}
// The detector will listen to `Computer, What time is it now?`, 
// then the detected `COMPUTER` and the recorded file which has `What time is it now` will be delivered to onDetect callback function.

Individual Hotword definition

hotwords: [
  {
    hotword: 'JARVIS' // Default config value would be applied to this hotword
  },
  {
    hotword: 'COMPUTER', // Or you can define each hotword separately.
    sensitivity: 0.5,
    continuousRecording: false,
    restart: true,
    onDetect: ({ helper, error, result, payload }) => {
      console.log("Detected hotword: 'COMPUTER'")
      helper.sendNotification('SHOW_ALERT', { message: `${result.hotword} detected.`})
    },
  },
  {
    hotword: 'MagicMirror',
    file: "MagicMirror.ppn", // For custom hotword, you need custom file name(.ppn)
  }
],

Built-in Hotwords

These keywords are pre-defined. (When languageModel is used, these built-in hotwords will not work.)

  'ALEXA', 'AMERICANO', 'BLUEBERRY', 'BUMBLEBEE', 'COMPUTER',
  'GRAPEFRUIT', 'GRASSHOPPER', 'HEY_GOOGLE', 'HEY_SIRI', 'JARVIS',
  'OK_GOOGLE', 'PICOVOICE', 'PORCUPINE', 'TERMINATOR',

Custom Hotwords

You can train your custom hotword on picovoice/Porcupine homepage.(https://picovoice.ai/platform/porcupine/) After downloading your custom .ppn file, put it into the resources directory of this module.

You need to describe the custom .ppn filename in the config.

hotwords: [
  {
    hotword: "MagicMirror",
    file: "MagicMirror.ppn",
    ...
  },
  {
    hotword: "TurnOnCamera",
    file: "mycustomcommand1.ppn",
    ...
  },

],

Non-English Custom Hotwords

You may need an additional model(.pv) file for your trained hotword(.ppn) by non-English hotword. Available language models are here. You can download it from github page manually, or,

# For Korean hotword, you need porcupine_params_ko.pv
cd <MagicMirror Directory>/modules/MMM-Hotword2/resources
curl -L -O https://github.com/Picovoice/porcupine/raw/master/lib/common/porcupine_params_ko.pv

Then, you need additional configuration of the module.

{
  module: "MMM-Hotword2",
  position: "bottom_right",
  config: {
    languageModel: "porcupine_params_ko.pv",
    hotwords: [
      {
        hotword: "거울아거울아"
        file: "korean_mirrormirror.ppn",
...
  1. You cannot mix multi-language models for hot words at the same time. All custom hotwords used have to belong to one languageModel.
  2. The default built-in hotwords like COMPUTER will not work with the Non-English model.

onDetect callback function

You can define the job when the hotword is detected as a callback function.

onDetect : async ({ helper, error, result, payload }) => {
  //Do your job.
  if (error) console.log(error)
  helper.sendNotification('SHOW_ALERT', { title: 'Hotword Detected', message: `Hotword ${result.hotword} is detected!` })
  ...
},

onDetect callback function is async function, so MMM-Hotword2 can know when your job is finished.

This callback function can have 4 objects as a parameter object. / error, result, payload and helper.

error

If the detector encounters an error, this object will be returned.

onDetect: async ({ error }) => {
  console.log(error)
}

result

Usually, it will carry the hotword found. If you set continuousRecording, it will also deliver filePath and fileUrl.

{
  hotword: 'COMPUTER',
  continuousRecording: true,
  onDetect: ({helper, result}) => {
    console.log(result)
    /* This will show some info like this.
    {
      hotword: 'COMPUTER',
      filePath: '/somewhere/path/to/storage/something.wav',
      fileUrl: 'modules/MMM-Hotword2/storage/something.wav',
    }
    */
  },
},

payload

It will carry the original config values. You may not need this generally. You can use these values to check which condition this detector works on.

onDetect: ({ payload }) => {
  console.log(payload)
  /*
    hotwords: [ ... ],
    device: -1,
    continuousRecording: false,
    languageModel: null,
    ...
  */
},

helper

You can use this helper object to do various jobs with the detected hotword. It has a few methods;

helper.sendNotification(notification, payload)

  • return value: void
  • This method helps send notifications to other modules.

helper.getModules()

  • return value: Array of MagicMirror module instances
  • This method helps get other modules to manipulate them.

helper.getModule(moduleName):MMmodule

  • return value: MagicMirror module instance
  • This method helps get a specific module to manipulate it.

helper.shellExec(scriptText)

  • return value: Promise(string) / async function
  • This method helps execute a shell script and return the result of execution.

Example

This example shows that when the hot word is detected, the clock module goes hidden, and the alert message will be displayed with the shell command date; then, after 5 seconds, the clock module will appear again.

onDetect: async ({ helper, error, result, payload }) => {
  if (error) return
  const clock = helper.getModule('clock')
  clock.hide()
  const message = await helper.shellExec(`date '+%A %W %Y %X'`)
  helper.sendNotification('SHOW_ALERT', { title: `Hotword command - ${result.hotword}`, message: message, timer: 5000 })
  setTimeout(() => clock.show(), 5000)
},