LoginSignup
4
1

More than 3 years have passed since last update.

100均の bluetooth リモートシャッターと PowerShell で作る簡単呼び鈴

Posted at

tl;dr

100均の bluetooth リモコンはキーボードとして Windows に認識されます。実質的には音量上げキーと Enter キーの機能しかありませんが。
さらに、PowerShell はスクリプト内に記述された C# のコードを読んで PC の音量を取得できるようにしています。
つまり、bluetooth リモコンで音量上げ音量の変化をスクリプトで検知リアクションを実行ループして待機という流れです。

ちなみに実機テストの結果、ダイソーのリモートシャッターは意図したとおりに動きますが、キャン★ドゥのリモートシャッターは適切なドライバが割り当てられず、キーボードとして動作しませんでした。
こうなるとほかの 100均のリモートシャッターも希望通りに動くのかどうか気になりますね!(ならない

ソースコード全体は記事の最後にあります。

解説

Add-Type -TypeDefinition @'
...
'@

@''@で囲われた範囲を C# として実行します。ここではAudioクラスを作成し、後続の PowerShell スクリプトに引き継いでいます。

$sapi = New-Object -com SAPI.SpVoice
$sapi.Speak('お客様がいらっしゃいました') | out-null

Text to Speech オブジェクト(?)を呼び出し、リテラル文字列を読み上げさせます。
この時、スクリプトが記述されているファイルの文字コードがShift-JISBOM付きUTF-8UTF-8 with BOM)でないと日本語を文字通りに発音してくれません。
Speak()関数の出力をout-nullにパイプすることによって、画面に正常終了を表す「1」が表示されないようにしています。

(New-Object Media.SoundPlayer ".\ファンファーレ.wav").Play()
Start-Sleep -s 2

カレントフォルダ内の「ファンファーレ.wev」を再生します。Play()関数は音声再生中も次の処理へ進んでしまい、音声再生中に再度条件が整うと音声が途中で切れ最初から鳴りなおすので 2行目で 2秒間プログラムを一時停止させています。

[audio]::Volume = $valumeStart   # ボリュームをスクリプト実行時の値に戻す

100均 bluetooth リモートシャッターは「音量上げ」しかできないので、変更された音量をスクリプト実行前の値に戻します。

欠点

このスクリプト実行中はリモートシャッターによるものではない、ユーザの通常操作による音量調整にも反応して音声を発するようになってしまっています。
また、ユーザが意図的に音量を変更してもすぐにスクリプト実行時の音量に戻されてしまいます。

参考リンク

PowerShell - 音量のコントロール - hakeの日記
実行すると任意の文字列を読み上げるPowerShellスクリプト - Qiita

ソースコード

ソースコード
Add-Type -TypeDefinition @'

using System.Runtime.InteropServices;
[Guid("5CDF2C82-841E-4546-9722-0CF74078229A"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IAudioEndpointVolume {
  // f(), g(), ... are unused COM method slots. Define these if you care
  int f(); int g(); int h(); int i();
  int SetMasterVolumeLevelScalar(float fLevel, System.Guid pguidEventContext);
  int j();
  int GetMasterVolumeLevelScalar(out float pfLevel);
  int k(); int l(); int m(); int n();
  int SetMute([MarshalAs(UnmanagedType.Bool)] bool bMute, System.Guid pguidEventContext);
  int GetMute(out bool pbMute);
}
[Guid("D666063F-1587-4E43-81F1-B948E807363F"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IMMDevice {
  int Activate(ref System.Guid id, int clsCtx, int activationParams, out IAudioEndpointVolume aev);
}
[Guid("A95664D2-9614-4F35-A746-DE8DB63617E6"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IMMDeviceEnumerator {
  int f(); // Unused
  int GetDefaultAudioEndpoint(int dataFlow, int role, out IMMDevice endpoint);
}
[ComImport, Guid("BCDE0395-E52F-467C-8E3D-C4579291692E")] class MMDeviceEnumeratorComObject { }
public class Audio {
  static IAudioEndpointVolume Vol() {
    var enumerator = new MMDeviceEnumeratorComObject() as IMMDeviceEnumerator;
    IMMDevice dev = null;
    Marshal.ThrowExceptionForHR(enumerator.GetDefaultAudioEndpoint(/*eRender*/ 0, /*eMultimedia*/ 1, out dev));
    IAudioEndpointVolume epv = null;
    var epvid = typeof(IAudioEndpointVolume).GUID;
    Marshal.ThrowExceptionForHR(dev.Activate(ref epvid, /*CLSCTX_ALL*/ 23, 0, out epv));
    return epv;
  }
  public static float Volume {
    get {float v = -1; Marshal.ThrowExceptionForHR(Vol().GetMasterVolumeLevelScalar(out v)); return v;}
    set {Marshal.ThrowExceptionForHR(Vol().SetMasterVolumeLevelScalar(value, System.Guid.Empty));}
  }
  public static bool Mute {
    get { bool mute; Marshal.ThrowExceptionForHR(Vol().GetMute(out mute)); return mute; }
    set { Marshal.ThrowExceptionForHR(Vol().SetMute(value, System.Guid.Empty)); }
  }
}

'@

# [audio]::Mute = $false
# [audio]::Volume = 0.5  # 50%

$sapi = New-Object -com SAPI.SpVoice
$valumeStart = [audio]::Volume
while($true) {
    $valumeAfter = [audio]::Volume
    if($valumeStart -ne $valumeAfter) {
        $sapi.Speak('お客様がいらっしゃいました') | out-null
#        (New-Object Media.SoundPlayer ".\ファンファーレ.wav").Play()
#        Start-Sleep -s 2
        [audio]::Volume = $valumeStart   # ボリュームをスクリプト実行時の値に戻す
    }
}
4
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
1