環境変数の設定に関するメモ

いくつか実行環境をつかっていると環境変数の設定方法をいつも間違えるのでまとめておきます。

f:id:ueponx:20181229190252p:plain

Windows系cmd環境

Windows系のCLI環境で代表的なのはcmd.exeですね。

【設定方法】

> set MYENV=value

【表示方法】

> set

以下の例では MYENVという環境変数valueという値を格納して、設定された環境変数を格納しています。

C:\>set MYENV=value

C:\>set
ALLUSERSPROFILE=C:\ProgramData
APPDATA=C:\Users\uepon\AppData\Roaming
CommonProgramFiles=C:\Program Files\Common Files
CommonProgramFiles(x86)=C:\Program Files (x86)\Common Files
CommonProgramW6432=C:\Program Files\Common Files
【中略】
ProgramData=C:\ProgramData
ProgramFiles=C:\Program Files
ProgramFiles(x86)=C:\Program Files (x86)
ProgramW6432=C:\Program Files
PROMPT=$P$G
【中略】
MYENV=value
windir=C:\WINDOWS

WindowsPowerShell環境

Windows系のもう一つのCLI環境であるPowerShellになります。

環境変数に値を設定するには、「$env:変数名」に「=」で値を設定します。設定時には"(ダブルクオーテーション)が必要になるので注意です。

【設定方法】

PS C:\> $env:MYENV = "value"

【表示方法】

PS C:\> Get-ChildItem env:

または

PS C:\> dir env:

dirはコマンドのように見えていますが、PowerShell上ではaliasで

Alias           dir -> Get-ChildItem

と設定されているので実際は同じものを実行しているだけです。

【実行してみる】

PS C:\> $env:MYENV="value"

PS C:\> dir env:

Name                           Value
----                           -----
ALLUSERSPROFILE                C:\ProgramData
APPDATA                        C:\Users\uepon\AppData\Roaming
CommonProgramFiles             C:\Program Files\Common Files
CommonProgramFiles(x86)        C:\Program Files (x86)\Common Files
CommonProgramW6432             C:\Program Files\Common Files
【中略】
MYENV                          value
【中略】
windir                         C:\WINDOWS

また、環境変数はドライブのように扱われているのでenv:という形でアクセスしています。 そのため、こんなふうに表示することもできます。

PS C:\> cd env:
PS Env:\> ls

多分使わないと思いますが…

RaspberryPi系Bash環境とOSX

シェルの環境にはよりますが、デフォルトはbashなのでそちらを。

【設定方法】

$ export MYENV=value

【表示方法】

$ env

【実行結果】

pi@raspberrypi:~$ export MYENV=value
pi@raspberrypi:~$ env
LC_ALL=ja_JP.UTF-8
【中略】
MYENV=value
【中略】
_=/usr/bin/printenv

おわりに

内容がチラシの裏レベルになってきている…でも、最近はドキュメントやワークショップの内容が処理系に偏ることも多いのでこれを使って読み替えて勉強します。

WioNodeの非同期(websocket)処理をPythonで書いてみる

いつかやろうと思っていたんだけどできなかったものの消化です。

はじめに

WioNodeを使用していると単純にcurlコマンドなどを使ってセンサーの状態の取得ができるのですが、センサーによっては常時通信を行いながら、 そのイベントのトリガーをJSON形式で受信するというタイプのものもあります。

例えば、ButtonやGestureなどがそれにあたります。

WioアプリのAPIページにも使い方の記載されているので大きく問題はないのですが、WebSocketのサンプルはJavaScriptのみなので、JSがそんなに得意ではない自分には できればPythonから使えないかなと考えてしまいます。

f:id:ueponx:20181228154259p:plain

今回のエントリーでは、そのPythonで書いてみようというものです。 PythonのWebSocketのライブラリは調べてみると2つほどあったので、今回はその両方でチャレンジしました。

前処理

WioNodeの設定に関しては、過去のエントリーなどを参照いただければと思います。

【参考】 uepon.hatenadiary.com

今回は、ButtonのGroveモジュールをWioNodeに接続してテストを行います。

f:id:ueponx:20181228163141p:plain

Buttonの設定を行ってFirmwareの書き換えをし、画面右上のハンバーガーメニューからViewAPIのページを表示します。APIページの一覧の中にJavaScriptで書かれたWebSocket経由でのイベントの取得サンプルがあります。 ソース内の*はアクセスキーになりますので伏せてあります。

f:id:ueponx:20181228203243p:plain

f:id:ueponx:20181228203501p:plain

<script>
var ws = new WebSocket('wss://us.wio.seeed.io/v1/node/event');
ws.onopen = function() {
    ws.send("*****************************");
};
ws.onmessage = function (evt) {
    alert(evt.data);
};
</script>

これと同じ処理を行うコードをPythonで書いていくことになります。

PythonのWebSocketのライブラリモジュールを調べてみると

WebSocketのライブラリモジュールをググってみると2つほどヒットします。

  • websocket-client
  • websockets

個人的には、websocket-clientをオススメしますが、非同期的なwebsocketsも今っぽい感じで悪くないかなという印象です。 ただ、websocket-clientがオススメなのはJSなどとの類似性があるのでソースの可読性が高い点が理由かなと思います。(移植がらく)

websocket-clientを使用してコードを書いてみる

インストールはpipでOKです。Python 2.7とPython 3.4以上に対応しています。

【Windows10の場合】

$ pip install websocket-client

【RaspberryPiの場合】

$ pip3 install websocket-client

インストール時には依存性がある別のパッケージがインストールされます。詳細は以下のページを御覧ください。

pypi.org

このページにあるサンプルを改良していきます。*はキーの部分なので伏せてあります。

import websocket
try:
    import thread
except ImportError:
    import _thread as thread
import time

def on_message(ws, message):
    print(message)

def on_error(ws, error):
    print(error)

def on_close(ws):
    print("### closed ###")

def on_open(ws):
    ws.send('*******************')

if __name__ == "__main__":
    websocket.enableTrace(True)
    ws = websocket.WebSocketApp('wss://us.wio.seeed.io/v1/node/event',
                              on_message = on_message,
                              on_error = on_error,
                              on_close = on_close)
    ws.on_open = on_open
    ws.run_forever()

変更点としては - WebSocketのURL - WebSocketの接続後に接続キーを送信する(Openイベントが発生したタイミング)

あたりかなと思います。

実行すると以下のように動作します。WioNodeにつけたButtonを押すとJSONデータが受信されます。 JSONデータにはイベント名(今回は"button_pressed")接続されたpin番号が受信されます。

>> WioNode WEB Socket server connected.
< {"msg": {"button_pressed": "3"}}
< {"msg": {"button_pressed": "3"}}
< {"msg": {"button_pressed": "3"}}

Windows10のAnaconda環境でも、RaspberryPiでもうまく動いているようです。

f:id:ueponx:20181228175843p:plain

f:id:ueponx:20181228182235p:plain

JavaScriptと同じようにonほげのような感じでイベントハンドラの登録ができるような感じです。かなり分かりやすい印象です。

websocketsを使用してコードを書いてみる

インストールはpipでOKです。Python 3.4以上に対応していますが、それ以下でも書き方次第で動くかも?

【Windows10の場合】

$ pip install websockets

【RaspberryPiの場合】

$ pip3 install websockets

詳細は以下のページを御覧ください。

pypi.org

このページのサンプルに改良していきます。*はキーの部分なので伏せてあります。

#!/usr/bin/env python
import asyncio
import websockets

async def loop():
    async with websockets.connect('wss://us.wio.seeed.io/v1/node/event') as websocket:
        await websocket.send('*******************')
        print(">> WioNode WEB Socket server connected.")
        while True:
            receiveData = await websocket.recv()
            print("{}".format(receiveData))

asyncio.get_event_loop().run_until_complete(loop())

python3で使用可能となったasync/awaitasyncioを使用しています。 asyncioの持つイベントループにWebSocketの接続をいれてデータの送受信をする形になります。 非同期処理なんですが、同期的っぽくかけます。 他のライブラリなどとは記載の仕方が違う点は特殊ですが、分かりやすいといえば分かりやすいかもって感じです。 ただ、Pythonのバージョンが限定されるので、用途が限られるかなという印象はあります。

f:id:ueponx:20181228184520p:plain

f:id:ueponx:20181228185224p:plain

こちらもWindows10のAnaconda環境でもRaspberryPiでも動作していましたが、RaspberryPiではCtrl+CのInterruptでエラーメッセージが大量に表示されるので微妙なのかも。 RaspberryPiのPython3のバージョンは3.5.3でした。

おわりに

ようやくやれてなかったことをまとめられました。JavaScriptもう少しかけるように慣れればいいんでしょうけど個人的にはPythonも捨てがたいです。

自分が使用しているFFmpegのコマンド例

ようやく今シーズンのイベントも落ち着いてきたので、まとめられていなかったものをまとめる時期に… 今回は結構毎回調べることが多かったFFmpegの中でも自分で使う機会の多かったをまとめて置こうと思います。

自分はWindowsとRaspberryPiで使うことが多いのでそのあたりの検証はしていませんが、Macは行えていません。 まあ、オプションなどの違いはないと思いますが、出来上がったもののCODECなどの問題はもしかしたらあるかもしれません。

インストール

省略してもいいのですが、FFmpegのインストールとなります。

Windowsの場合

以下のサイトにアクセスしてください。

FFmpeg

公式サイトに移動したら

f:id:ueponx:20181224120843p:plain

画面左の【download】をクリックします。

f:id:ueponx:20181224122528p:plain

クリックすると各プラットフォームのダウンロード画面になるので画面内nあるWindowsのアイコンをクリックします。

f:id:ueponx:20181224122752p:plain

マウスオーバーするとアイコンの色が変わります。

f:id:ueponx:20181224123323p:plain

クリックすると画面が少し下にスクロールして以下のようになります。 他のプラットホームでは種類があるのでリスト表示されますが、Windowsの場合には1つしかないようなのであっさりした表示になります。 【Windows Builds】をクリックします。

f:id:ueponx:20181224124049p:plain

すると、以下のようなダウンロードサイトに遷移します。

f:id:ueponx:20181224124538p:plain

画面内に(ビルド)Version、Architecture、Linkingの指定がありますので 今回は以下としました。 * Version 4.1 * Architecture Windows 64bit * Linking Static

選択が終わったら【Download Build】ボタンをクリックします。

f:id:ueponx:20181224124658p:plain

無事にダウンロードができました。

f:id:ueponx:20181224125313p:plain

ダウンロードをしたものはインストーラではなくバイナリファイルなので、binフォルダのプログラムが環境変数のPATHにあるフォルダに格納すればインストールは完了です。 自分は以下に展開して、環境変数PATHに``````を通しました。

C:\Program Files\ffmpeg

f:id:ueponx:20181224130352p:plain

このあたりは好みかと思います。

f:id:ueponx:20181224130858p:plain

こんな感じで実行できれば大丈夫です。

RaspberryPiの場合

aptコマンドを使います。以前はソースからのビルドを行う必要があったようですが、現在は大丈夫のようです。

$ sudo apt update
$ sudo apt install -y ffmpeg

インストール後のバージョンは以下のようになっていました。 version 3.2.10-1 結構古いです…

$ ffmpeg -version
ffmpeg version 3.2.10-1~deb9u1+rpt2 Copyright (c) 2000-2018 the FFmpeg developers
built with gcc 6.3.0 (Raspbian 6.3.0-18+rpi1+deb9u1) 20170516
configuration: --prefix=/usr --extra-version='1~deb9u1+rpt2' --toolchain=hardened --libdir=/usr/lib/arm-linux-gnueabihf --incdir=/usr/include/arm-linux-gnueabihf --enable-gpl --disable-stripping --enable-avresample --enable-avisynth --enable-gnutls --enable-ladspa --enable-libass --enable-libbluray --enable-libbs2b --enable-libcaca --enable-libcdio --enable-libebur128 --enable-libflite --enable-libfontconfig --enable-libfreetype --enable-libfribidi --enable-libgme --enable-libgsm --enable-libmp3lame --enable-libopenjpeg --enable-libopenmpt --enable-libopus --enable-libpulse --enable-librubberband --enable-libshine --enable-libsnappy --enable-libsoxr --enable-libspeex --enable-libssh --enable-libtheora --enable-libtwolame --enable-libvorbis --enable-libvpx --enable-libwavpack --enable-libwebp --enable-libx265 --enable-libxvid --enable-libzmq --enable-libzvbi --enable-omx-rpi --enable-mmal --enable-openal --enable-opengl --enable-sdl2 --enable-libdc1394 --enable-libiec61883 --arch=armhf --enable-chromaprint --enable-frei0r --enable-libopencv --enable-libx264 --enable-shared
libavutil      55. 34.101 / 55. 34.101
libavcodec     57. 64.101 / 57. 64.101
libavformat    57. 56.101 / 57. 56.101
libavdevice    57.  1.100 / 57.  1.100
libavfilter     6. 65.100 /  6. 65.100
libavresample   3.  1.  0 /  3.  1.  0
libswscale      4.  2.100 /  4.  2.100
libswresample   2.  3.100 /  2.  3.100
libpostproc    54.  1.100 / 54.  1.100

新しいものを追っているわけではないので問題ないですが、以下の例で動かないときには注記することにします全部動いたようです。

説明の前提

例に使用するファイルの事前説明を書いておきます。

自分で使用するものは以下のようなものが多いです。音声のサンプリングレートは44.1kHzから48kHzにしていることも多いかなと思いますが 適宜入れ替えてもらえればいいかなと思います。

WAVファイル

  • 入力 → input.wav(44.1kHz Stereo 16bit)
  • 出力 → output.wav(44.1kHz Stereo 16bit)

MP3ファイル

  • 入力 → input.mp3(44.1kHz Stereo 192k)
  • 出力 → output.mp3(44.1kHz Stereo 192k)

使用例

WAVからMP3へ変換

$ ffmpeg -i input.wav -vn -ac 2 -ar 44100 -ab 192k -acodec libmp3lame -f mp3 output.mp3

ビットレート-abオプションで設定し、今回は192kを指定しています。

MP3からWAVへ変換

$ ffmpeg -i input.mp3 -vn -ac 2 -ar 44100 -acodec pcm_s16le -f wav output.wav

MP4形式の動画をAVI形式に変換する

$ ffmpeg -i input.mp4 output.avi

MP4形式の動画をビットレート340kbpsのMP4形式の動画に変換する

$ ffmpeg -i input.mp4 -vb 340k output.mp4

MP4形式の動画をフレームレート60fpsのMP4形式の動画に変換する

$ ffmpeg -i input.mp4 -r 60 output.mp4

画像の連番ファイルから動画をつくる

$ ffmpeg -i image%03d.png output.mp4

数字が連番になっているところをC言語のformat指定と同様な感じで%03dで指定して置換する。(わかりにくい?表現かも) ファイル名の桁数あわせで0埋めされていれば%0【桁数】dとし、数字が0埋めされていなければ%dで大丈夫です。

このまま実行するとWindowsのデフォルトプレイヤーやWindowsMediaPlayer再生できないというエラーが発生します。(VLCでの再生に関してはは問題ない)

f:id:ueponx:20181224142542p:plain

これで困るようであれば以下のようにCODECやpixelの色空間の指定を別途行います。

$ ffmpeg -i image%03d.png -vcodec libx264 -pix_fmt yuv420p output.mp4

こちらの指定であれば問題なく再生できました。

f:id:ueponx:20181224143930p:plain

画像の連番ファイルから60fpsの動画をつくる

$ ffmpeg -r 60 -i image%03d.png output.mp4

再生がうまくかなければ前段と同様にCODECやpixelの色空間の指定を行ってください。

$ ffmpeg -r 60 -i image%03d.png -vcodec libx264 -pix_fmt yuv420p output.mp4

動画から連番ファイルをつくる

$ ffmpeg -i input.mp4 image%03d.png

動画1秒あたりフレームレートの枚数分画像が生成するので、間引きを行うときは以下のように-rオプションを使って一秒当たりの静止画の枚数を指定することになります。

(例)動画から1秒あたり10枚の連番ファイルをつくる場合

ffmpeg -i input.mp4 -r 10 image%03d.png

動画をアスペクト比を保ったまま横幅640ピクセルにリサイズ、フレームレートは10fpsでアニメーションgifに変換

$ ffmpeg -i input.mp4 -vf scale=640:-1 -r 10 output.gif

アスペクト比を維持したままサイズを変える場合は、横幅か縦幅を-1にすればOKです。

動画を横幅1920ピクセル縦幅1080ピクセルにリサイズする

$ ffmpeg -i input.mp4 -s 1920x1080 output.mp4

動画からmp3形式で音声ファイルを生成する

$ ffmpeg -i input.mp4 output.mp3

動画を無音にする

$ ffmpeg -i input.mp4 -an output.mp4

動画の特定の時間範囲を取り出し

$ ffmpeg -ss [開始地点(秒)] -i [入力する動画パス] -t [切り出す秒数] [出力する動画パス]

(例)開始から10秒のところから、5秒間だけ切り出す場合

$ ffmpeg -ss 10 -i input.mp4 -t 5 output.mp4

動画をクロップしたい

$ ffmpeg -i 変換前ファイル -vf crop= 出力動画の幅 : 出力動画の高さ : 元動画の左上をゼロとして X 軸の距離 : 元動画の左上をゼロとして Y 軸の距離

(例)input.mp4を,左上を座標の原点(0,0)とし,そこから X=100,Y=60 を起点にして,W=160,H=120 のクロップしたoutput.mp4を出力する

図に表すと以下のようになります。(白い部分がクロップしたい部分になります)

f:id:ueponx:20181224133756j:plain

$ ffmpeg -i input.mp4 -vf crop=160:120:100:60 output.mp4

ffmpegでMP4ファイルを結合する

あるファイル(000.mp4)と別のファイル(001.mp4)を連続で再生する動画ファイル(output.mp4)を出力する。

まず、以下のように結合したいファイルを各行に記述したテキストファイルを作ります。 【list.txt】

file 000.mp4
file 001.mp4

各行の冒頭には必ずfileというキーワードが必要になります。 また、リストファイルに乗せるファイル名はフルパスを指定するほうがいいようなのですが、Windowsでは管理者権限でコマンドを実行してもPermissionのエラーが出るので相対パスのほうがいいようです。 対象になる動画と同じフォルダであれば、省略可能でした。

次に、このテキストファイルと同じディレクトリで以下のコマンドを実行します。

$ ffmpeg -f concat -i list.txt -c copy output.mp4

-c copyはオリジナルファイルのCODECを再エンコードしないオプションです。 異なるCODECの動画を結合したい場合は、-c copyを外すか別途CODECを指定する必要があります。

動画の再生スピードを変えて出力する

0.5倍速にする場合

$ ffmpeg -i input.mp4 -vf setpts=PTS/0.5 output.mp4

1.5倍速にする場合

$ ffmpeg -i input.mp4 -vf setpts=PTS/1.5 output.mp4

おわりに

あんまりないと思って甘く見ていたけど結構ありましたね。

2018年今年買ってよかったもの

今年購入してよかったものをちょっとだけ考えてみました。むしろ他の人が何を購入しているのかのほうが気になります。

今年買った中で役に立っているのはこちらです。

一位

いままでは家にも机があったのですが、スペースの関係で捨てることにしました。それからは作業するのは床座りという感じになっていました。 長い時間作業していると姿勢が悪くなり、膝が悪くなってしまいました。そこでパソコン用折りたたみテーブルを購入してみました。 ポイントにしたのは足の部分がY字になっている点(床が柔らかい状態でも安定します)と無段階調整の天板でした。いまのところ使い勝手もいいし軽いし折りたたみもできるので重宝しています。

難点があるとすれば天板についているパソコンの滑り止めがあるのですが高さがあるので使用したままキーボードはちょっと難しいかなとお思います。 もう一つはマウスレストが右側についていますが、外すことはできないです。あとレストがゴム製なので劣化すると使用感がどうなるかも気になります。

値段が3000円程度なのでまあいい買い物だったかなと思います。

二位

二位はRaspberry用の3.5インチタッチスクリーン機能付きのHDMIディスプレイです。 以下のエントリーをみて購入しました。

karaage.hatenadiary.jp

nomolk.hatenablog.com

見た瞬間にこれは買いだと思いました。 ケースがつくのもいいですが、GPIOのアクセスを妨げられないのが最高です。RaspberryPiのケースをつけると持ち運びは楽になるのですが GPIOへのアクセスがかなり制限を受けるので毎回困っていました。特にハッカソンなどでは持ち運びをすることを考えるとこの足枷が大きな障害になります。

これを使えばこれらの制限がなくなるのがいいですね。若干の大きさがあるので、一回り大きな箱はいりますが購入時の箱がそのまま使えますので捨てずにとっておくのがいいかなと思います。

価格も3300円ぐらいですが、モニターなしのケースでも1000円弱とかになってますので、モニタ付きのケースでこの値段ならまあそんなものかなと思います。

三位

Bluetoothイヤホンです。値段の割に音質もわるくないし、電池もなかなか持つというのが理由です。 イヤーピースの形がちょっと特殊なのでどうかなと思ったのですが、すぐに慣れました。フィット感と固定感もできて満足しています。

価格はセール中だったので2000円を切っていたかなと思います。セールが終わっていますが、同系統のものが2500円ぐらいで購入できそうです。

おわりに

今年は実用品ばっかりだったような気がします。もっと遊びがほしいです。

’e’の重要性

この記事はSeeed UG Advent Calendar 2018の22日目の記事です。 明らかにネタですが、いろいろとすみません。

Seeed社さんの商品を使ったブログのエントリーを書いていると、気をつけていないとついtypoしてしまい「e」が足りないことが結構あります。

そのまま誤記したエントリーをアップすると「Seeed警察*1」というパトロールが見回りを行っていて「e」が足りないという指摘を受けて、自分のTypoを実感してしまいます。

過去にはこんなエントリーを書いていましたが、これらの何件かはポリスの御用になっていました。

uepon.hatenadiary.com

uepon.hatenadiary.com

uepon.hatenadiary.com

uepon.hatenadiary.com

そこで今回のエントリーではAdvent Calendar 2018の後半の箸休めとして、エントリー内にSeeedをSeedとするような誤記があるか調べるChrome拡張を作ってみることにしました。 これで安心してSeeed製品関連のエントリーを書くことができるでしょう。

あーえっとネタがちょっとなかったのですみません。

とりあえず

Chromeの拡張なのでバーにアイコンを表示してそれをクリックし、ページ内のコンテンツにseedがあるかないかを判定する機能を作ってみることにします。名前はSeeedポリス拡張機能としました。

Chrome拡張の開発に関しては以下のリンクのチュートリアルを参考にすれば比較的かんたんに実装することができます。

f:id:ueponx:20181209175437p:plain

Chrome拡張はHTMLとCSSJavascriptで作成し一つのフォルダ(ディレクトリ)の中に格納して管理します。

アイコンの作成

とはいっても、アイコン表示を行うのでアイコンも必要になります。Seeedさんということで以下のサイトで種と芽の形のアイコンを作ってみました。

icooon-mono.com

大きさはアイコンサイズではありますが48pixelあたりにしています。

f:id:ueponx:20181221164903p:plain

Manifestファイルを作成する

Chrome拡張にはManifestファイルが必要になります。Manifestでは、拡張機能に関する情報をjsonファイルで記述していきます。 内容としては拡張の名前やバックグラウンドで動作するJavascriptファイル(background.js)、拡張が動作可能な名前空間、アイコンクリック時に動作するJavascriptファイル(content.js)などの名前が記載されています。

今回のManifestファイルは以下のようにしました。

manifest.json

{
    "manifest_version": 2,
    "name": "Seeedポリス拡張機能",
    "description": "表示しているタブ内にSeedという単語が含まれているかチェックする拡張機能",
    "version": "1.0",
    "permissions": [
        "tabs",
        "notifications"
    ],
    "background": {
        "scripts": [
            "background.js"
        ]
    },
    "content_scripts": [
        {
            "matches": [
                "<all_urls>"
            ],
            "js": [
                "content.js"
            ]
        }
    ],
    "icons": {
        "48": "Extension48.png"
    },
    "browser_action": {
        "default_icon": "Extension48.png",
        "default_title": "Seeedポリス拡張機能"
    }
}

Content Scriptの記述

Chromeの拡張バーに表示されたアイコンをクリックすると動作する処理になります。

Manifestファイルのcontent_scriptsで指定したJavascriptファイル(今回はcontent.js)がこれに当たります。

このファイルは実行環境の制限が特殊でisolated worldと呼ばれています。制限事項は以下のようになっているようです。

という制限があります。今回はブラウザで開いているページのコンテンツを対象とするのでDOMを使用して、コンテンツのbodyタグを取得しました。ただこれだけでは、完了できなかったのでChromeの拡張のランタイムにbodyタグの内容をメッセージとして送信し、送信した内容をバックグラウンド側の処理で対応してもらうことにしました。

content.js

chrome.runtime.sendMessage({
    value: document.getElementsByTagName('body')[0].outerText
});

短いですがこんな感じになりました。

Background Pageの記述

先程のContent Scriptで機能制限がかかるのでその際に対応する手段としてBackground Pageがあります。Chrome拡張はバックグラウンドで動くページを持っていて、そちら側で処理を行うことができます。

今回はManifestファイルの"background"に指定したbackground.jsを編集します。 先程のcontent.jsで表示しているページのBodyタグの内容をMessageとして送信したので、 こちらのファイルでそのメッセージを受信し、seedという文字列が入っていたらChromeの通知(トースト?)を表示することにします。

  • 通知を受け取る場合には → chrome.runtime.onMessage.addListener()
  • アイコンのクリックを検知するには → chrome.browserAction.onClicked.addListener()

というイベントのリスナにfunctionを記述します。

background.js

let textContents = '';

chrome.runtime.onMessage.addListener(
    function (request, sender, sendResponse) {
        textContents = request.value;
        if (textContents.toLowerCase().search(/seed/) !== -1) {
            textContents = "含む";
        } else {
            textContents = "含まない";
        }
    }
);

chrome.browserAction.onClicked.addListener(
    function (request, sender, sendResponse) {
        let options = {
            type: "basic",
            title: "Seeed ポリス出動します!",
            message: "",
            iconUrl: "Extension48.png"
        };
        console.log("debug", options);
        if (textContents.indexOf("含む") !== -1) {
            options.message = "ページ内に”Seed”が含まれています、\n念の為確認してください!";
        } else {
            options.message = "ページ内にSeedは含まれていません、\nご安心ください!";
        }
        chrome.notifications.create(options);
        console.log("debug", options);
    }
);

chrome.runtime.onMessage.addListener()の処理では受け取ったメッセージのテキストの中にseedが 含まれるかチェックしています。

chrome.browserAction.onClicked.addListener()の処理ではseedが含まれていたら通知(chrome.notifications.create())を発生する処理を行っています。

ディレクトリの中がこんなふうになっていればいいかなと思います。

Mode                LastWriteTime         Length Name
----                -------------         ------ ----
-a----       2018/12/09     14:32           1076 background.js
-a----       2018/12/09     13:16             96 content.js
-a----       2018/12/09     12:08           2226 Extension48.png
-a----       2018/12/09     14:20            770 manifest.json

ローカル環境で配置

では、Chromeに拡張を配置します。Chromeで以下のURLを開きます

chrome://extensions/

すると、このような画面になります。

f:id:ueponx:20181209173718j:plain

(メニューから【その他ツール】→【拡張機能】でも同じ画面になります。)

画面の左上方にある【デベロッパーモード】を有効にするとメニューが追加されます。

デベロッパーモードの無効】 f:id:ueponx:20181222091503p:plain

デベロッパーモードの有効】 f:id:ueponx:20181222091731p:plain

この中から【パッケージ化されていない拡張機能を読み込む】をクリックします。 クリックするとディレクトリの選択のダイアログが開きます。

f:id:ueponx:20181209173805j:plain

ここで、先程保存しておいた拡張機能のフォルダを選択します。 うまく読み込むことができれば行けばこのように作成した拡張機能が表示されます。

f:id:ueponx:20181209174349p:plain

拡張機能のバーにアイコンの表示されているのも確認できます。

f:id:ueponx:20181209173858j:plain

アイコンをクリックすれば、Chromeの通知機能でseedが含まれているか否かを教えてくれます。

f:id:ueponx:20181222101234p:plain

f:id:ueponx:20181222102240p:plain

終わりに

いろいろネタがなくてすみません… eが大切なGReeeeNにも使えそうです。

*1:なぜか組織されているらしい

/* -----codeの行番号----- */