忘れ物防止に任意の時間に任意の文字列をWindowsに喋らせる

Windows使うなら、これだけで全部完結させられるよねということで。

色々あるけど、System.Speechあたりが簡単そう。

if ($Args.Length -eq 0) { exit }

Add-Type -AssemblyName System.Speech
$s = New-Object System.Speech.Synthesis.SpeechSynthesizer
$s.Speak($Args[0])
$s.Dispose()

これを適当な名前…say.ps1みたいな名前のファイルに保存しておいて、タスクスケジューラでPowerShell say.ps1 タブレット持ったかー?などと登録すると喋ってくれるようになります。

ただ、タスクスケジューラに登録する場合PowerShellだと画面が表示されてしまい、抑制する方法がわからなかったのでWScriptの方がいいかも。

var args = WScript.Arguments;
if (args.Count() == 1) {
  WScript.CreateObject("SAPI.SpVoice").Speak(args.Item(0));
}

しかし、当たり前だけど機械音声ぽさが強く、WindowsでやるならVOICEROID使いたくなってくるなぁ。しかしこれだけのために(きっとすぐ飽きる)買うのもな…。

Google Homeで忘れ物がないか確認してもらいたい

  • 透析日の朝、タブレットを鞄に入れるのを忘れて家を出てしまうことがある
  • 忘れないように、Google Homeに月水金の朝だけタブレットを入れたか確認してもらおう
  • IFTTTで任意の日時に任意の文字列を喋らせるとかできるかな?
    • 現状ではできないらしい
  • 調べるとみんなgoogle-home-notifierで喋らせているらしい
    • 自分も使って見よう、と思ったけどWindowsで使うにはやや面倒
    • これはGoogle TTSを使って任意の文字列から音声を生成し、それをGoogle Homeに渡している
    • 音声ファイルの内容が固定なら、Google TTSは省略できる
  • GoogleCastというC#のライブラリがあったので、こっちにしよう
    • Visual StudioC#のプロジェクトを作り、NuGetでGogleCastを入れて任意の音声をGoogle Homeに喋らせるプログラム(サンプルママ)を作る
    • 音声はSofTalkで生成して、Dropboxに置いておく
      • Dropboxの共有リンクはそのままでは音声として読めないので、&raw=1をURLに付ける必要がある(メモ)
    • このプログラムをタスクスケジューラで月水金の朝に起動すれば、目的達成!
      • Google Homeの近くに自分がいる時、PCも起動しているはずなので漏れはないと思われる
  • …あれ、目の前にあるPCからコマンド発行するなら作成したwavをそのまま鳴らせばいいのでは?

はい。

JupyterLabでPHPをやる

GitHub - jupyterlab/jupyterlab: JupyterLab computational environment.

なんかPython以外にRとかも使えるらしい、というので見てみたら大体の言語に対応していた。

Jupyter kernels · jupyter/jupyter Wiki · GitHub

PHPも選べるなら結構いいかも、とJupyter-PHPを入れてみた。上記サイトにはIPHPというのも上がっているけどGitHub見たらJupyter-PHPの方がナウいよとあったのでこっちで。

GitHub - Litipk/Jupyter-PHP: A PHP Kernel for Jupyter
Jupyter-PHP's Installer by Litipk

インストーラもあるし手順も書いてあるのだけど、ZMQってなんやねんとなったのでメモ。

  • PHPをインストール(XAMPP入れていたのでこれをそのまま使った)
  • Composerをインストール。WindowsなのでComposer-Setup.exeを実行するだけ
  • PHP-ZMQWindows向けDLLをダウンロード
    • php.exeと同じフォルダにlibzmq.dllをコピー
    • php/extフォルダにphp_zmq.dllをコピー
    • php.iniに"extension=zmq"を追加
  • jupyter-php-installer.pharをダウンロードして"php jupyter-php-installer.phar install"を実行
    • ComposerやPHP-ZMQがないとここでコケる
  • 成功したらjupyter labでJupyterLabを起動するとKernelの選択肢にPHPが追加されている

f:id:wata_d:20180411142417p:plain

ちょっとしたプログラムを実行結果を確認しながら書けるの、とても便利です。REPLでもいいんだけどこっちの方がトライアンドエラーがやりやすい気がします。

PHPでChart.js

Web API的なアレで、DBから取ってきたデータをChart.jsで描画しようと思ったのだけどJSON書いてるとなんか混乱してきたので、APIが返すJSONをそのままChart.jsの形式にしてしまった。あんまり良くない気もするけど、どうなんだろなぁ。

最初C#で書いていたのだけど色々あってPHPASP.NETはまったく触ったことなかったのだけれど、ほとんどコードを書かずに済むのでこっちも楽そうです。

こういうクラスを作ってから

 // sample.php
<?php
 $chart = new Chart("bar");
 $chart->data->labels = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
 $ds = new DataSet();
 for ($i = 0; $i < 12; $i++) $ds->data[] = rand(0, 100);
 $chart->data->addDataSet($ds);
 echo json_encode($chart);
?>

こんなページを作って

 <!DOCTYPE html>
 <html>
 <head>
     <script src="Chart.bundle.min.js"></script>
     <script>
        window.onload = function() {
            // $.getJSON('sample.php') ...
            const xhr = new XMLHttpRequest();
            xhr.open('GET', 'sample.php', true);
            xhr.responseType = 'json';
            xhr.onload = function(e) {
                var ctx = document.getElementById('canvas').getContext('2d');
                new Chart(ctx, xhr.response);
            }
            xhr.send();
        }
    </script>
 </head> 
 <body>
     <canvas width="500" height="500" class="chartjs-render-monitor" id="canvas"></canvas>
 </body> 
 </html>

というような感じでそのまま突っ込むとチャートが出てきます。

Chart.jsはコールバック関数を設定することもできますが、PHPJSONを組み立てちゃうとそのあたりが利用できないのが結構大きなデメリットでしょうか。

window.onload = function() {
    // $.getJSON('sample.php') ...
    const xhr = new XMLHttpRequest();
    xhr.open('GET', 'sample.php', true);
    xhr.responseType = 'json';
    xhr.onload = function(e) {
        var ctx = document.getElementById('canvas').getContext('2d');
        var data = xhr.response;
        var scales = {xAxes: [{
            type: 'category',
            ticks: {
                maxRotation: 0,
                callback: function(value, index, values) { return [value, (index+1) + "月"]; }
            }
        }]};
        data.options["scales"] = scales;
        new Chart(ctx, data);
    }
    xhr.send();
 }

みたいな感じで無理矢理追加することはできますが…。

JavaScriptで任意の文字列をクリップボードにコピーしたい(Chrome)

document.execCommand('copy') でブラウザで選択している文字列をコピーできるらしい。

任意の文字列を含む要素をでっち上げて一時的に選択してあげれば良いのかな?

var r = document.createRange();
var text = document.createTextNode("コピーしたい文字列");
r.selectNode(document.body.appendChild(text)); // body直下に突っ込んで
window.getSelection().addRange(r); // 選択して
document.execCommand('copy'); // コピー
text.remove(); // お役御免

雑だけど自分用ツール内ではこれでいいかな…。

LINQPadでグラフを描く

なんとなくLINQPadのサイト見たら最新のベータ版でChart()という拡張メソッドが追加されたみたい。

コレクションなどに生えるもので、System.Windows.Forms.DataVisualization.Charting.Chartの生成を一気に行ってくれる模様。Dump()するとそのままチャートが出てきます。

f:id:wata_d:20180319125711p:plain

お手軽だけどもうちょっと短くできそう。DumpChartの方が便利そう? 自分で作るのもアリかなぁ。