【ラズパイ】札幌市のごみ出しカレンダーをLINEに通知
こちらは、Mavs Advent Calendar2023の3日目の記事です!🦌
こんにちは。
3年前にラズパイを買ったものの、初期設定だけ済ませて放置していたクワジマです。
この度、飼い殺しにしていたラズパイに札幌市のごみ収集日カレンダーの情報をもとに今日が何のごみの日かをLINEに通知させるという使命を与えましたので、その方法をご紹介します。
私と同じくラズパイにホコリを被せてしまっている方、そのままゴミに出しましょう。
ではなく、この記事を参考にラスパイを有効活用してみましょう!
※なお、ラズパイの初期設定の方法については解説致しませんのでご了承ください。
OSインストール〜ブラウザが使用できるところまで完了させておけば、この後の作業がスムーズになると思います。
完成形
↓こちらがラズパイからLINEへ送信されたメッセージです。
メッセージ送信用のプログラムを、平日の朝7:00にcronで実行しています。
作業内容
- 環境構築(ラズパイにNode.jsとmicroエディタをインストール)
- LINE Notifyのトークンを発行
- メッセージ送信用プログラムの作成
- crontabの設定
という流れでやっていきます。
使用機材
Raspberry Pi 4 model B
OS:Raspbian GNU/Linux 10 (buster)
使用言語
node v18.18.0
v18からはFetch APIが導入され、デフォルトでfetch()メソッドが使用可能となったため、node-fetchなどの外部パッケージをインストールすることなくネットワークリクエストを行えます。
ところで、札幌市のごみ収集日の情報ってどうやって準備するの?
札幌市のごみ収集日の情報ですが、実は取得用のAPIが提供されておりました…!
ごみの種別と収集日カレンダーをJSONで取得できます。
どこが提供しているAPIなのか調べてみると、「札幌市ICT活用プラットフォーム DATA-SMART CITY SAPPORO」というサイトで提供されているものでした。
以下、「札幌市ICT活用プラットフォーム DATA-SMART CITY SAPPORO」からの抜粋です。(引用元:https://data.pf-sapporo.jp/about_site/)
“「札幌市ICT活用プラットフォーム DATA-SMART CITY SAPPORO」について
一般財団法人さっぽろ産業振興財団は、札幌圏地域データ活用推進機構(SARD)の理念と機能を継承し、市民生活の利便性向上や、新たなサービス創出による経済の活性化、行政保有データの活用が容易になることによる行政の信頼性や透明性の向上に資するため、圏域で発生し官民が保有する様々なデータ(いわゆる「官民データ」)を協調して利活用できる環境を整備し、官民がデータ利活用を促進する「データの地産地消」の実現に向け事業を推進しています。”
また、今回利用するAPIリソース(データカタログ)については以下のように記載されています。
“(1)データカタログ
官民データを検索したり、ダウンロードしたりすることができるカタログサービスです。ダウンロードしたデータは、付与されているライセンス条件に沿って、誰でも閲覧し、活用することができます。”
とのことでした。このような取り組みがあったとは全く知りませんでした…
ありがたく活用させて頂きます。
JSONのレスポンスのサンプルは下記から確認できます。
■札幌市家庭ごみ収集日カレンダー(2023年10月1日~2024年9月30日)
https://ckan.pf-sapporo.jp/api/3/action/datastore_search?resource_id=c1c0f835-bbaf-42d2-8ea2-a71cae8d7389&limit=5
環境構築
ラズパイにNode.jsとmicroという軽量エディタをインストールします。
Node.jsについては、apt install
による方法だと古いバージョンが入ってしまうようでしたので、公式からバイナリをダウンロードする方法で進めます。
Node.jsのインストール
まずは画面左上のターミナルアイコンをクリックして「LXTerminal」を開き、
uname -m
でCPUアーキテクチャを確認しておきます。
$ uname -m
armv7l
キャプチャでは見づらいですが、armv7lと表示されています。
続いて、ラズパイのブラウザでNode.jsのダウンロードページへアクセスし、最新のLTS版をダウンロードします。
先ほど確認した「ARMv7」のリンクをコピーしてください。
ターミナルに戻り、wget
コマンドでダウンロードします。URL部分はコピーしたリンクです。
(wgetコマンドを実行したディレクトリにファイルがダウンロードされます。)
$ cd ~
$ wget https://nodejs.org/dist/v18.18.0/node-v18.18.0-linux-armv7l.tar.xz
tar
でファイルを解凍します。
$ tar Jxvf node-v18.18.0-linux-armv7l.tar.xz
動作確認します。
$ ~/node-v18.18.0-linux-armv7l/bin/node -v
v18.18.0
インストールしたバージョンが返ってきたらOKです。
パスを通します。
$ echo 'export PATH=$HOME/node-v18.18.0-linux-armv7l/bin:$PATH' >> ~/.bashrc
$ source ~/.bashrc
再確認します。
$ node -v
v18.18.0
npmも確認します。
$npm -v
9.8.1
以上で完了です!
microをインストール
この後のコーディング作業やcrontabの設定に使用するエディタとしてmicroをインストールします。crontabの設定くらいであれば、デフォルトでインストールされているnanoやViでも良いかもしれませんが、コーディング時は不便な点があると思います。
microはラズパイでも軽快に動作する軽量エディタで、コピーペーストなどの一般的なショートカットの使用や「Ctrl + /」でのコメントアウトも可能です。コードシンタックスハイライトもされるなど、わりと普段通りの感覚で使用できました。
最初はVSCodeを入れましたが、ラズパイでは動作が重かったためmicroに切り替えました。
それでは、ターミナルでcurl
コマンドを実行してインストールします。
$ cd ~
$ curl https://getmic.ro | bash
「Micro Installed!」と表示されてターミナルにプロンプトが返ってきたら完了です。
動作確認します。
$ ./micro -version
Version: 2.0.13
バージョンが返ってきたらOKです。
microの実行ファイルを移動してパスを通します。
$ sudo mv micro /usr/local/bin
再確認します。
$ micro -version
Version: 2.0.13
バージョンが返ってきたらOKです。
デフォルトのエディタをmicroに設定
こちらは必須の作業ではありませんが、デフォルトのエディタを切り替えておくとcrontabの編集時にもmicroが起動するようになります。
$ select-editor
を実行すると、エディタの選択画面に候補が表示されます。
/usr/local/bin/microを使用したいので、その先頭のオプション数字を入力してEnterを押してください。
※エディタの候補にmicroが表示されていない場合は、下記のコマンドを実行後、改めて select-editor
してみてください。
$ sudo update-alternatives --install /usr/bin/editor editor /usr/local/bin/micro 1
設定できたかを確認します。
$ editor
と入力し、microが起動すれば成功です。
起動が確認できたら「Ctrl + Q
」で閉じます。
LINE Notifyのトークンを取得
ラズパイからの通知をLINEで受信するためにLINE Notifyというサービスを利用します。(無料です)
このLINE Notifyを利用するために必要なトークンを取得します。
まずLINE Notifyへアクセスし、ログインします。
(LINEに登録しているメールアドレスとパスワードでログインできます。)
ログイン後、マイページへ進みます。
「トークンを発行する」をクリックします。
トークン名を入力し、送信先を選択をします。
自分宛に送信する場合は「1:1でLINE Notifyから通知を受け取る」を選択します。
入力したトークン名は[]で囲まれて表示されるので、”ラズパイごみBot”と入力した場合は次のような表示になります。
トークン名の入力と送信先の選択が完了したら、「発行する」をクリックします。
トークンが発行されるので、コピーしたらどこか別の場所に貼り付けて退避しておきましょう。ページを移動すると再確認できないので、ご注意ください。
メッセージ送信用のプログラムの作成
プロジェクト作成
任意のディレクトリへ移動し、好きな名前でプロジェクトを作成します。
$ cd ~
$ mkdir project_name
$ cd project_name
$ touch index.js
あとはmicroでindex.jsにコードを書いていきましょう!
$ micro index.js
コードを書くにあたって必要な情報
この後のセクションで私が書いたソースコードも貼りますが、ご自身でコードを書きたい方は以下の情報が必要になると思うので参考にしてください。
1. APIリソース
https://ckan.pf-sapporo.jp/dataset/garbage_collection_calendarへアクセスします。
探索 > プレビューをクリックします。
画面の右上の「データAPI」をクリックします。
モーダルに”クエリ例(最初の5件)”とあります。
URLのクエリパラメーターのlimit
を省略した場合はデフォルトで100件まで返ってきます。ごみ収集日カレンダーを1年分取得するにはlimit=366
としてください。(2024年は閏年)
2. LINE Notify API Document
https://notify-bot.line.me/doc/ja/へアクセスし、「通知系」のセクションにAPI使用方法の詳細があります。
ソースコード
私が書いたソースコードです。
コピペする場合は、targetArea(収集区域名)とLINE_NOTIFY_TOKENの値は適宜変更してください。
ソースコード冒頭のコメントには、今回ブログに掲載するにあたって、利用したAPIのライセンス条件に沿って提供元のクレジットを記載しました。
/*
このプログラムで使用するデータセットは、
クリエイティブ・コモンズ・ライセンス(表示 4.0 国際)に従って提供されています。
クリエイティブ・コモンズ・ライセンス(表示 4.0 国際)の要約:
- 表示 (Attribution): 提供元を適切に表示すること
ライセンスの詳細については、以下のリンクを参照してください:
https://creativecommons.org/licenses/by/4.0/deed.ja
データセットのタイトル:
「ごみ種別・番号対応表」
「札幌市家庭ごみ収集日カレンダー(2023年10月1日~2024年9月30日)」
データセットの提供元:
一般財団法人さっぽろ産業振興財団
ホーム
*/
const API_COMMON_URL = "https://ckan.pf-sapporo.jp/api/3/action/datastore_search";
// 「ごみ種別・番号対応表」取得用URL
const GARBAGE_CATEGORIES_URL = `${API_COMMON_URL}?resource_id=f13f6d71-1fde-433d-b5c5-c38631fde7ca`;
// 「札幌市家庭ごみ収集日カレンダー(2023年10月1日~2024年9月30日)」取得用URL
const GARBAGE_COLLECTION_URL = `${API_COMMON_URL}?resource_id=c1c0f835-bbaf-42d2-8ea2-a71cae8d7389&limit=366`;
// 収集区域名
const targetArea = "XX区③";
// LINE Notifyエンドポイント
const LINE_NOTIFY_URL = "https://notify-api.line.me/api/notify";
// LINE Notifyへのリクエストに乗せるトークン
const LINE_NOTIFY_TOKEN = "LINE Notifyで発行したトークン";
/**
* 現在の日付と時刻を "2023-10-01T00:00:00" 形式の文字列で返す関数
* @returns {string} - "2023-10-01T00:00:00" 形式の日付文字列
*/
function getCurrentDate() {
const today = new Date();
const timeSuffix = "T00:00:00";
const options = { year: "numeric", month: "2-digit", day: "2-digit" };
const formattedDate = today
.toLocaleDateString("ja-JP", options)
.replaceAll("/", "-");
return formattedDate + timeSuffix;
}
/**
* 指定されたURLからJSONデータを取得する関数
* @param {string} url - JSONデータを取得するURL
* @returns {Array} - 取得したJSONデータのrecordsプロパティ
* @throws {Error} - 失敗時はErrorオブジェクトをスロー
*/
async function fetchJSON(url) {
try {
const response = await fetch(url);
const data = await response.json();
return data.result.records;
} catch (error) {
throw new Error(`${url} からのJSON取得に失敗しました。 ${error.message}`);
}
}
/**
* 当日に収集されるごみの種別名称を取得する関数
* @returns {string} - 当日に収集されるごみの種別名称
*/
async function getGarbageCategory() {
const currentDate = getCurrentDate();
const [garbageCategories, annualGarbageCollection] = await Promise.all([
fetchJSON(GARBAGE_CATEGORIES_URL),
fetchJSON(GARBAGE_COLLECTION_URL),
]);
const todayData = annualGarbageCollection.find(
(data) => data.日付 === currentDate
);
// 今日の日付(プログラム実行日)が収集日カレンダーに無い
if (!todayData) {
return "古いAPIを使い続ける君というごみ";
}
// 住んでいる区域のごみ種別
const garbageCategorySymbol = todayData[targetArea];
// 土日や年末年始はnull
if (garbageCategorySymbol === null) {
return "土日や年末年始にまで僕を働かせる君というごみ";
}
// 収集なし
if (garbageCategorySymbol === 0) {
return "なし";
}
const garbageCategory = garbageCategories.find(
(category) => category.記号 === garbageCategorySymbol.toString()
);
return garbageCategory ? garbageCategory.ごみ種 : "!不明なごみ種別!";
}
/**
* 送信するメッセージを生成する関数
* @returns {string} - 送信用のテキストメッセージ
*/
async function generateMessage() {
const garbageCategory = await getGarbageCategory();
return garbageCategory === "なし"
? "今日はごみの収集はないよ!"
: `今日は「${garbageCategory}」の収集日だよ!`;
}
/**
* メッセージを送信する関数
* @param {string} message - 送信するメッセージテキスト
* @returns {Object} - 送信結果のHTTPステータスコード
* @throws {Error} - 失敗時はErrorオブジェクトをスロー
*/
async function sendMessage(message) {
const requestOptions = {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
Authorization: `Bearer ${LINE_NOTIFY_TOKEN}`,
},
body: `message=${encodeURIComponent(message)}`,
};
try {
const response = await fetch(LINE_NOTIFY_URL, requestOptions);
return { status: response.status };
} catch (error) {
throw new Error(`メッセージの送信に失敗しました: ${error.message}`);
}
}
/**
* メインの実行関数
*/
async function main() {
try {
const message = await generateMessage();
const result = await sendMessage(message);
console.log(`HTTP ステータスコード: ${result.status}`);
} catch (error) {
console.error(error.message);
}
}
main();
作者のこだわりポイント
土日や年末年始など、ごみ種別がnullの日にプログラムを実行するとラズパイの口が悪くなります。
また、収集日カレンダーのエンドポイントURLは年度ごとに resource_id=
以降が変わるので、過去の年度のカレンダーを参照した場合にも不必要に厳しい言葉で知らせてくれます。
作者の手抜きポイント
Gitに上げないのでLINE Notifyのトークンはベタ書きしてます。
動作確認
index.jsを配置しているディレクトリで、
$ node index.js
を実行して、LINEに通知が届けばOKです。
crontab設定
それでは、作成したプログラムを定期実行できるようにスケジューリングします。
予めnode
コマンドのフルパスを表示してコピーしておきます。
$ which node
/home/ユーザー名/node-v18.18.0-linux-armv7l/bin/node
crontabを編集します。
$ crontab -e
平日の朝7:00にindex.jsを実行する場合は、
# m h dom mon dow command
0 7 * * 1-5 /home/ユーザー名/node-v18.18.0-linux-armv7l/bin/node project_name/index.js
とします。
“/home/ユーザー名/node-v18.18.0-linux-armv7l/bin/node
“の部分には、先ほどコピーしたnode
コマンドのフルパスを貼り付けます。
cronの設定項目は左から
分 時 日 月 曜日 実行したいコマンド
となっています。
分 | 0から59で指定 |
時 | 0から23で指定 |
日 | 1から31で指定 |
月 | 1から12で指定 |
曜日 | 0から7で指定 (0と7は日曜、1は月曜、6は土曜) |
※月曜から金曜のように範囲指定する場合は”開始-終了”のようにハイフンで繋ぎます。
crontabをmicroで編集している場合は「Ctrl + S
」で保存して「Ctrl + Q
」で閉じます。
ターミナルも閉じてしまって大丈夫です。
以上で完了です!あとは朝を待つのみです(ラズパイの電源を切ってはいけません)
翌日 AM 7:00
ちゃんと届きました!
しかし、あまり家に溜まらないタイプのごみだったので無視しました。
さいごに
ラズパイが吹けば冷蔵庫屋が儲かる。
もう冷蔵庫にごみ収集日カレンダーを貼っておく必要はなくなりました。
これまで、オシャレな冷蔵庫が欲しいけど生活感のあるポスターでマスクされるから…と購入を控えていた消費者層の枷が取り外されることで、冷蔵庫の売上が2台くらい伸びそうです。
まだまだありそう。API活用の幅
札幌市ICT活用プラットフォーム DATA-SMART CITY SAPPOROでは、ごみ収集日カレンダーの他にも、札幌市の文化財一覧APIなど面白そうなものが提供されているので、またの機会に活用してみたいと思います。