複数の Google Calendar をまとめた Calendar を Google Apps Script を利用して作成する

複数の Google Calendar を使って予定管理しているのだが、たとえば仕事相手に空いている時間を教えたいときなどに、複数のカレンダーのアドレスを教えなければならずに面倒であった。できれば1つのカレンダーを共有するだけで予定を相手に通知できるようにし、かついままでのカレンダーの使用方法は変えたくない。

複数のGoogleカレンダーをまとめる を参考に このスクリプト を使おうと思ったが、一度に多くの操作をしすぎだと怒られたので、自分で書き換えた。JavaScript のオブジェクト、Python と同じだと思っていると痛い目にあうことがわかった(できれば Python で書きたかった)。

定期的に実行する方法は先のサイトを見ればいいが、メモとしてここにも書いておくと、

  • まとめる先のカレンダーを新しく作成しておく。
    • 公開範囲も適切に設定しておく。
  • Google Drive で新しい Google Sheets を作成
  • Tools > Script Editor を開く。
  • 下のコードをコピペ・カレンダー ID を書き込む。
    • ID は example@group.calendar.google.com みたいな形をしている。
  • テストラン。上部で関数名 (main) を選択してから Run.
  • 結果を見て、満足なら Resources > Current project's triggers から実行時刻を入力。ぼくは始業前の 7:00-8:00 と業務後の 18:00-19:00 に1回ずつ走るように設定した。

難点としては、本来ならカレンダー(予定の種類)ごとに細かく閲覧設定ができるところを縮退させてしまっている点か。しかしまぁ、そのへんはスクリプトを軽く書き換えて、プライベートな予定は Private という名前にしてパブリックな予定はそのままの名前とする、みたいな感じにすれば丸いのでは。

参考にしたサイトは以下である。

gist583d364fa99b9fafdcaf5e9287047ca7

送信時刻を指定して Gmail を送信する

(2019-05-10 追記:Gmail に公式に予約送信の機能が実装された。今後はこのコードではなくそちらの機能を使うことを推奨します。)

手紙は夜に書くな、とはよく言うけれど、しかしメールなんて夜ぐらいにしか書く気にならないというのもまた事実であり、深夜に書き上がったメールをさて送ろうと思ったときに、相手が携帯のアドレスだと睡眠を妨害しやしないかと少々躊躇してしまう。またたとえ携帯でなかったとしても、深夜にメールを送ったら「このひと、返信を書くのに悩みに悩みぬいたのだわ」と思われてしまいそうだ。もっとスマートに、朝起きて朝飯前にさらさらっと書いたふうを装いたい。

決まった時間に送信する、なんて機能は Gmail に標準搭載されていてしかるべきだと思わなくもないが、おそらくコストに見合わないのだろう。外部サービスとして Boomerang とか Right Inbox とかのサービスがあるけど、セキュリティ的にどうなのって思うし、金は払いたくない。また、以前に似たような記事を書いた が、あのコードは下書きの本文をコピーして新しいメールとして送信する(リプライチェーンが切れる!)というものだったのに加え、Google Sheets での操作が必要だったため実用的ではなかった。

Gmail の API に Drafts を直接操作できる API があるので、これを Google Apps Script と併用して、自前のサーバーなしに朝夕の自動メール送信を実現しよう。

使い方は以下である。

  • Google Apps Script で新しい Script を作成し(もしくは Google Drive から新しい Google Sheets を作成して、そこの Tools > Script Editor から新しい Script を作成)、下のコードを貼りつける。
  • Resources > Advanced Google Services から Gmail API を有効にして(スイッチを on にして)、下に表示される Google Developers Console へのリンクから Gmail API を有効にする。
  • sendMorningDrafts などの関数を Resources > Current project's triggers から適当な時間に登録。
  • あとは Gmailschedule/send-next-0700-0800 などの名前のラベルを作成し、それを Drafts にある下書きにつければ時間がくれば送信してくれる。
    • 最初の実行時には authentication があるのでテストもかねて手動で実行してみるのが吉。

利点としては、下書きにラベルをつけるという操作さえできれば Web からでも iOS App からでも送信予約ができる点が挙げられる。逆に悪い点としては、このメールはいついつに自動送信したい、みたいな細かい指定はできない点が挙げられる。

参考にした資料は以下である。

Automatically sends Gmail drafts with specific lab ...

(2016-07-21 追記:コードを Gist へ移行した。ついでに、API を直接叩いていたのを、Advanced Google Services のインターフェースを使うように変更。)

(2018-05-02 追記:Gist のコードを、G Suite Services 内の Gmail Service のみを用いるよう(Advanced Google Services のインターフェースを使用しないよう)に変更。)

サーバーの容量を監視して通知

この記事 を参考に。 せっかく Pushover を使っているので、これに投げてもらう。curl で楽をする。

$ vi check_df.sh
#!/bin/bash

LIMIT=90
SUBJECT="[WARN] The capacity of the disk has decreased! - `hostname`"

while read LINE
do
    PERCENT=`echo $LINE | sed 's/^.* \([0-9]*\)%.*$/\1/'`
    if [ $PERCENT -gt $LIMIT ]; then
        curl -s --form-string "token=tkn123" --form-string "user=usr123" --form-string "message=$SUBJECT $PERCENT%" https://api.pushover.net/1/messages.json
        break
    fi
done < <(df | grep '[0-9]\{1,\}%')
$ chmod 755 check_df.sh

LIMIT を低く設定して、動作テスト。

$ ./check_df.sh

通知が送られてくれば OK。

とりあえず毎日18時にチェックして通知。

$ crontab -e
0 18 * * * /home/username/check_df.sh >/dev/null 2>&1

iOS のホーム画面に配置すべきアプリを最適化する

iOS ではアプリの使用率を見ることはできないが、アプリの電源使用率を見ることはできる。Settings > Battery の下のほうに表示されるものがそれである。

バックグラウンドで動作するアプリもあるので一概には言えないが、ここに並んでいるアプリは自分の使用率が高いアプリとみなしてよいので、上から順に適当な順位までをホーム画面に(フォルダなどに入れずに;ワンタップで呼び出せる位置に)配置すれば、生活のスピード感が少しだけ増すのでオススメ。

HITACHI LEDシーリングライト LEC-AHS1210EH の電子音を消す

LEC-AHS1210EH の電気(照明)のピッ音を消す方法。この Amazon レビュー が参考になった。

消灯時に、リモコンのカバーの中の「暗」ボタンを10秒間押しっぱなしにする。

再度オンにするには同じ操作を繰り返す。

マニュアルに記載してほしい。

Raspberry Pi に Homebridge をインストールする

CentOS 6 へインストールを試みたのだけれど全くうまくいかないので、常時動いている Raspberry Pi (IP: 192.168.11.20) がいたのでそちらで使えるようにすることにした。

基本的には Homebridge の Wiki を読んでその通りに行う。

システムをアップデート

$ sudo apt-get update
$ sudo apt-get upgrade

アップデート中に次のように訊かれた。Diff を取ってみたらおそらくこの Raspberry Pi で動いている別プログラムの GPIO まわりの設定に関してだったので、弄らないほうがよかろうという判断で現在のバージョンを維持した。

Configuration file '/etc/udev/rules.d/99-com.rules'
 ==> File on system created by you or by a script.
 ==> File also in package provided by package maintainer.
   What would you like to do about it ?  Your options are:
    Y or I  : install the package maintainer's version
    N or O  : keep your currently-installed version
      D     : show the differences between the versions
      Z     : start a shell to examine the situation
 The default action is to keep your current version.
*** 99-com.rules (Y/I/N/O/D/Z) [default=N] ?

C++14 が利用可能であることを確認。

$ g++ -v
...
gcc version 4.9.2 (Raspbian 4.9.2-10)

Node インストール

Raspberry Pi Model B+ なので armv6l 用のを選択(Raspberry Pi 2 は armv7l のを選択するようだ)。現在の最新版を入れておく。

$ wget https://nodejs.org/dist/v6.2.2/node-v6.2.2-linux-armv6l.tar.gz
$ tar -xvf node-v6.2.2-linux-armv6l.tar.gz
$ cd node-v6.2.2-linux-armv6l
$ sudo cp -R * /usr/local/
$ cd ..
$ node -v
v6.2.2

Avahi インストール

$ sudo apt-get install libavahi-compat-libdnssd-dev

Homebridge インストール

--unsafe-perm をつけないとエラーが出てインストール出来ない。つけても下記のようなエラーが出るが、source compile にフォールバックしてなんとかなっているようだ。

$ sudo npm install -g homebridge --unsafe-perm
...
node-pre-gyp ERR! Tried to download: https://rvagg-node.s3-us-west-2.amazonaws.com/bignum/v0.12.5/bignum-v0.12.5-node-v48-linux-arm.tar.gz
node-pre-gyp ERR! Pre-built binaries not found for bignum@0.12.5 and node@6.2.2 (node-v48 ABI) (falling back to source compile with node-gyp)
...

これでとりあえず homebridge を起動できるようにはなった。確認。

$ homebridge

番号みたいなのが表示されたら成功。Warning が6行ぐらい出るが無視しろと 公式に書かれている

homebridge-irkit, homebridge-cmd インストール

$ sudo npm install -g homebridge-irkit
$ sudo npm install -g homebridge-cmd

(2016-07-27 追記)IRKit を使う際には、常に Bonjour が使える環境にあるとも限らないので、IP アドレスを固定するのがよい。 IRKit 側からは固定できないので、公式 にある方法で IP アドレスを発見してから、ルーター側の設定で IRKit の MAC アドレスと IP アドレスの関連を固定させる。

自宅のルーターでは Internet/LAN > DHCPリース の項目から可能だった。

IRKit の現在の IP を調べるには Mac で以下のコマンドを実行。

$ dns-sd -B _irkit._tcp
$ dns-sd -G v4 irkitxxxx.local

(2016-07-27 追記ここまで)

設定

設定ファイルの位置は ~/.homebridge/config.json と決まっているので、このファイルを作成して設定を書いていく。今回は、リトライ数などのきめ細かい制御は Python で記述し、Homebridge からはそのスクリプトを呼び出すだけとした。

なお、このファイルは homebridge の daemon 化の際には使用しない。テスト用である。よってテストが不要の場合はここを飛ばして daemon 化に進んでしまってかまわない(そこでまたこの内容を入力するステップがある)。

 $ vi ~/.homebridge/config.json
{
    "bridge": {
        "name": "Homebridge",
        "username": "CD:22:3D:E3:CE:30",
        "port": 51826,
        "pin": "031-45-154"
    },

    "description": "Samoyed!",

    "platforms": [],

    "accessories": [
        {
            "accessory": "CMD",
            "name": "リビングの照明",
            "on_cmd": "python /home/pi/Documents/python/irkit.py living_light select_b",
            "off_cmd": "python /home/pi/Documents/python/irkit.py living_light off"
        },

        {
            "accessory": "CMD",
            "name": "寝室の照明",
            "on_cmd": "python /home/pi/Documents/python/irkit.py bedroom_light full",
            "off_cmd": "python /home/pi/Documents/python/irkit.py bedroom_light off"
        },

        {
            "accessory": "CMD",
            "name": "暖房",
            "on_cmd": "python /home/pi/Documents/python/irkit.py aircon heating_on",
            "off_cmd": "python /home/pi/Documents/python/irkit.py aircon off"
        },

        {
            "accessory": "CMD",
            "name": "冷房",
            "on_cmd": "python /home/pi/Documents/python/irkit.py aircon cooling_on",
            "off_cmd": "python /home/pi/Documents/python/irkit.py aircon off"
        }
    ]
}

daemon

$ sudo useradd --system homebridge
$ sudo mkdir /var/homebridge
$ sudo chown homebridge:homebridge /var/homebridge/
$ sudo su homebridge -s /bin/bash
$ vi /var/homebridge/config.json  # 内容は上述の config.json と同一。
$ exit
$ sudo vi /etc/default/homebridge
# Defaults / Configuration options for homebridge
# The following settings tells homebridge where to find the config.json file and where to persist the data (i.e. pairing and others)
HOMEBRIDGE_OPTS=-U /var/homebridge

# If you uncomment the following line, homebridge will log more
# You can display this via systemd's journalctl: journalctl -f -u homebridge
# DEBUG=*
$ sudo vi /etc/systemd/system/homebridge.service

システムによって実行ファイルの位置が異なる可能性があるので、事前に $ which homebridge を実行しておき ExecStart にはそのパスを記載する。

[Unit]
Description=Node.js HomeKit Server
After=syslog.target

[Service]
Type=simple
User=homebridge
EnvironmentFile=/etc/default/homebridge
ExecStart=/usr/local/bin/homebridge $HOMEBRIDGE_OPTS
Restart=on-failure
RestartSec=10
KillMode=process

[Install]
WantedBy=multi-user.target
$ sudo systemctl daemon-reload
$ sudo systemctl enable homebridge
$ sudo systemctl start homebridge

$ sudo systemctl status homebridge で状態を確認。 $ sudo journalctl -u homebridge でエラーを見る。

iOS 側からは新しい機器として認識されるので、再度登録し直す必要がある。再登録時に必要な番号を見るには、やはり $ sudo journalctl -u homebridge を用いる。再登録に問題が生じる場合は、このあたり を参考に、/var/homebridge/persist/ を消したり username を変更したりする。

テンキーを左手入力デバイスとして使う

左手入力デバイスを拡充しようと思い、Keypad SANWA SUPPLY NT-M18UHSV を購入した。 選んだ観点は

  • 00 キーがない。
    • 00 キーを持つテンキーは多いが、たいていは内部的に 0 キーを 2 回押しているだけである。しかもそれを混乱なく実現するために、出力前に同時押下中のキーを上げるコマンドを送出していたりする。その場合、モディファイアキー+00 に機能を割り当てられなかったりするので、左手入力デバイスとして使うには少々問題がある。
  • 極力キー数が多い。
    • キー配置はソフトウェア的に変更できるのでどうでもいい。たとえば 0 キーの幅が広かったり + キーが縦に長かったりするよりは、通常のテンキーとしてはちょっと使いづらい配列でもいいからキー数が多いほうが好ましい。
  • 最低でも 2 キーロールオーバーできる。
    • こればっかりは購入してみないとわからない。試してみた結果、このキーパッドではほとんどのキーの組み合わせで 2 キーまではロールオーバーできる(3 キーになると不具合が発生しはじめる)。
    • 唯一 2 キーロールオーバーできなかったキーが左下の 0 キーで、これは押下持続を全く認識せず押下時に KeyUp イベントも連続して送出してしまう。この挙動は、0 をモディファイアキーとして使わなければ大きな問題とはならない。

このテンキーは、20種類の別個のキーを送出できるという点において、左手入力デバイス用のテンキーとしてはほぼ理想的である。いくつか問題点があるとすれば

  • tab enter return が通常キーボードでの押下と区別されない。
  • 私が利用しているソフトが、テンキーの = キーを通常キーボードの = キーと同一視する(機器からはちゃんと NUMPAD_EQUAL が送出されている)。
  • 私が利用しているソフトが、テンキーの clear を割り当て可能キーとして認識しない。

これらを解決するために、tab clear enter return = の 5 つを別のキーに再割当てする。

  • 私の使用方法だと、テンキーの 1 4 7 をそれぞれ alt shift command (control if Windows) に変更するのでこれらのキーが余る。よってこれらを enter = clear へ再度割り当てることでテンキーとキーボードとを分離する。
  • 私の利用するソフトでは、return キーには単独で押された場合には space と同じ挙動・モディファイアと押された場合には 0 と同じ挙動をしてもらえるとありがたい。よってそのように設定する。
  • このテンキーでは tab キーは非常に押しづらい位置にある。よってこれは弄らない(もしくは nop としてもよい)。

以上を Karabiner を通して設定する。vendorid, productid は Karabiner 内のツールである EventViewer から取得する。

  <devicevendordef>
    <vendorname>SANWASUPPLY</vendorname>
    <vendorid>0xffff</vendorid>
  </devicevendordef>

  <deviceproductdef>
    <productname>NTM18UHSV</productname>
    <productid>0x0200</productid>
  </deviceproductdef>

...
    <item>
      <name>Place Modifiers to Numpad</name>
      <appendix>Numpad 1 to Option (KeyToKey)</appendix>
      <appendix>Numpad 4 to Shift (KeyToKey)</appendix>
      <appendix>Numpad 7 to Command (KeyToKey)</appendix>
      <identifier>private.remap.keypad_modifiers</identifier>
      <autogen>__KeyToKey__ KeyCode::KEYPAD_1, KeyCode::OPTION_L</autogen>
      <autogen>__KeyToKey__ KeyCode::KEYPAD_4, KeyCode::SHIFT_L</autogen>
      <autogen>__KeyToKey__ KeyCode::KEYPAD_7, KeyCode::COMMAND_L</autogen>
    </item>

    <item>
      <name>Change Keypad NT-M18UHSV keys to other keys.</name>
      <appendix>Numpad enter to KEYPAD_1 (KeyToKey)</appendix>
      <appendix>Numpad = to KEYPAD_4 (KeyToKey)</appendix>
      <appendix>Numpad clear to KEYPAD_7 (KeyToKey)</appendix>
      <appendix>Numpad return to space if pressed without modifier, else to 0 (to avoid return/enter key taken unnecessarily by VSTi and Ctrl/Cmd+Space by OS X). (KeyToKey)</appendix>
      <identifier>private.remap.change_tenkey_keys_ntm18uhsv</identifier>
      <device_only>DeviceVendor::SANWASUPPLY, DeviceProduct::NTM18UHSV</device_only>
      <!-- CLEAR can be set to CONTROL_L, and it will expand keypad functionallity, but I won't do so.-->
      <!-- Because it will lose consistency between Windows and Mac.-->
      <autogen> __KeyToKey__ KeyCode::ENTER, KeyCode::KEYPAD_1 </autogen>
      <autogen> __KeyToKey__ KeyCode::KEYPAD_EQUAL, KeyCode::KEYPAD_4 </autogen>
      <autogen> __KeyToKey__ KeyCode::KEYPAD_CLEAR,  KeyCode::KEYPAD_7 </autogen>
      <block>
        <modifier_only>ModifierFlag::NONE</modifier_only>
        <autogen>__KeyToKey__ KeyCode::RETURN, KeyCode::SPACE</autogen>
      </block>
      <autogen> __KeyToKey__ KeyCode::RETURN, KeyCode::0</autogen>
    </item>

00 キー (double 0 key, double O key) の活用方法

以前使っていた iBuffalo BSTK11 において、00 キーをどのように活用していたかをログとして残しておく。

軽く前述したが、00 キーの挙動を Karabiner の EventViewer で観察したところ、たとえば 2 を押しながら 1 を押すと 2↓ 1↓ 1↑ 2↑ という順番で送信されるのだが、00 だけは他の押してあったキーを勝手に離してから 0 を2回押すという挙動をする(2↓ 2↑ 0↓ 0↑ 0↓ 0↑)。これは、テンキーのキーをモディファイアに変えて使っているときに問題となる(モディファイアキー+00 キー を取れない)。 この問題は「前回送られたイベントがモディファイアキーで、かつそのイベントから次のイベントまでが人間には不可能なほど早い」みたいなフィルタをかければ一応は解決可能で、それを実現したのが次の private.xml である。0 を押せば 0 が・00 を押せば 1 が・command+0 を押せば command+0 が・command+00 を押せば(初回は)command+1 が、それぞれ出力される設定となっている。

      <item>
        <identifier>private.remap.double0to1</identifier>
        <name>00 to KEYPAD_1</name>
        <appendix>Change 00 key (quick 0 x2) to KEYPAD_1.</appendix>
        <!-- Filter elapsedtimesincelastreleased_lessthan cannot be used, because it also sends out the first KEYPAD_0. -->
        <!-- Following filter can catch single Mod+00 event, but not Mod+multiple 00 event. -->
        <block>
          <lastsentevent_only>KeyCode::COMMAND_L</lastsentevent_only>
          <elapsedtimesincelastreleased_lessthan>Millisecond::RawValue::10</elapsedtimesincelastreleased_lessthan>
          <autogen>
            __KeyToKey__
            KeyCode::KEYPAD_0,
            KeyCode::VK_NONE,

            Option::KEYTOKEY_DELAYED_ACTION,
            KeyCode::KEYPAD_0, ModifierFlag::COMMAND_L,

            Option::KEYTOKEY_DELAYED_ACTION_CANCELED_BY, KeyCode::KEYPAD_0,
            KeyCode::KEYPAD_1, ModifierFlag::COMMAND_L,
            KeyCode::VK_KEYTOKEY_DELAYED_ACTION_DROP_EVENT,
          </autogen>
        </block>
        <block>
          <autogen>
            __KeyToKey__
            KeyCode::KEYPAD_0,
            KeyCode::VK_NONE,

            Option::KEYTOKEY_DELAYED_ACTION,
            KeyCode::KEYPAD_0,

            Option::KEYTOKEY_DELAYED_ACTION_CANCELED_BY, KeyCode::KEYPAD_0,
            KeyCode::KEYPAD_1,
            KeyCode::VK_KEYTOKEY_DELAYED_ACTION_DROP_EVENT,

            <!-- In the next version, next Option will be added, so remove comment then. -->
            <!-- Option::KEYTOKEY_DELAYED_ACTION_MILLISECONDS, -->
            <!-- Millisecond::RawValue::10, -->
          </autogen>
        </block>
      </item>

しかしこのスクリプトを使っても、モディファイアキーを押しっぱなしにしたまま 00 を繰り返し押すという動作が拾えない。これはそもそもモディファイアキーに変更しているキーが 00 送出時に押し上げられてしまい、その後そのキーは送信されないのでどうやっても解決はできなさそうである。よって モディファイア+00 には、繰り返して実行することがない動作を割り当てるのが重要である(たとえば 拡大・縮小 などを割り当ててはいけない!)。

これと同様の操作を WindowsAutoHotKey で実現する場合は ここ とか参照なんだと思う。しかし設定するよりテンキーを買い換えてしまったほうが早いと思う。