ホーム » コンピュータ
「コンピュータ」カテゴリーアーカイブ
MuninでSwitchBot CO2センサーのプラグイン
Muninで、Switchbot の CO2 センサーをモニタリングするためのプラグイン
#!/bin/bash #%# family=auto #%# capabilities=autoconf available="yes" # # SwitchBot # token="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" secret="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" swbot_url="https://api.switch-bot.com" # for v1.1 url_list_v11="${swbot_url}/v1.1/devices" t=$(/bin/date +%s%3N) # time = Epoch time 13 digits nonce=$(/usr/bin/uuidgen) # uuid sign=$(echo -n "$token$t$nonce" | /usr/bin/openssl dgst -sha256 -hmac "$secret" -binary | /usr/bin/base64 -w 0) # SwitchBot meter CO2センサーのデバイスID deviceId="xxxxxxxxxxxx" function status() { url_list_v11_meter="${url_list_v11}/${deviceId}/status" /usr/bin/curl -s --request GET \ -H "Content-Type: application/json" -H "Authorization: ${token}" \ -H "sign: ${sign}" -H "nonce: ${nonce}" -H "t: ${t}" \ "${url_list_v11_meter}" \ | /usr/bin/jq -r '.body | "temperature.value " + (.temperature|tostring) + "\nhumidity.value " + (.humidity\ |tostring) + "\nCO2.value " + (.CO2|tostring)' } case $1 in autoconf ) if [ "$available" = "yes" ]; then echo "yes" exit 0 fi ;; config) echo "graph_title SWBOT Meter CO2 温度/湿度/CO2 (リビング)" echo "graph_category sensors" echo "graph_vlabel 温度[C]/湿度[%]/CO2[ppm]" echo "graph_args -l 0 -u 70" echo "temperature.label 温度[C]" echo "temperature.draw LINE2" echo "temperature.colour 00FF00" echo "temperature.warning 30" echo "humidity.label 湿度[%]" echo "humidity.draw LINE2" echo "humidity.colour 00E0FF" echo "CO2.label CO2[ppm]/100" echo "CO2.draw LINE2" echo "CO2.cdef CO2,0.01,*" #echo "CO2.warning 25" exit 0 ;; esac status
Muninで取得した値を、nagios4 で確認するためのプラグイン。
#!/usr/bin/perl use strict ; use warnings ; use Net::Telnet ; my $telnet = new Net::Telnet( Host => "127.0.0.1" , Port => 4949 , Timeout => 30 ) ; $telnet->open() or die( "Can't connect" ) ; $telnet->waitfor( '/#.*$/' ) ; # munin で読み込む項目を指定 $telnet->print( "fetch switchbot_meter_co2\n" ) ; my $flag = 0 ; my %value = () ; # データを読み込む while( my $line = $telnet->getline() ) { last if ( $line =~ /^\.$/ ) ; if ( $line =~ /^([0-9a-zA-Z_]+)\.value\s+([\.0-9]+)\s*$/ ) { $value{$1} = $2 ; $flag = 1 ; } } # nagios プラグインとして範囲を確認 my $st = 0 ; my $item = "-" ; my %status = ( 0 => "OK" , 1 => "Warning" , 2 => "Critical" , 3 => "Unknown" ) ; if ( exists( $value{'temperature'} ) ) { if ( $value{'temperature'} > $ARGV[1] ) { $st = 2 ; $item = "temperature" ; } elsif ( $value{'temperature'} > $ARGV[0] ) { $st = 1 ; $item = "temperature" ; } } if ( exists( $value{'CO2'} ) ) { if ( $value{'CO2'} > $ARGV[3] ) { $st = 2 ; $item = "CO2" ; } elsif ( $value{'CO2'} > $ARGV[2] ) { $st = 1 ; $item = "CO2" ; } } if ( !$flag ) { $st = 3 ; $item = "Error" ; } # 最終結果の出力 printf( "SBMT_MeterCO2 %s %s %2.1f[C] %2.0f[%%] %4.0f[ppm]\n" , $status{$st} , $item , $value{'temperature'} , $value{'humidity'} , $value{'CO2'} ) ; exit $st ;
Nagios のプラグインを Perl で書いておいたけど、少しでも軽い処理にしたいので lua(lua50) で書き直し
#!/usr/bin/lua local temp , hum , co2 ; local temp_w_max , temp_c_max = 25 , 30 local hum_w_max , hum_c_max = 11 , 22 local co2_w_max , co2_c_max = 2500 , 5000 function find_value( str , pattern ) local p_start , p_end = string.find( str , pattern ) local s_val = string.sub( str , p_end + 1 ) local p_nl = string.find( s_val , "\n" ) local val = string.sub( s_val , 1 , p_nl ) return tonumber( val ) end -- 温度条件 if table.getn(arg) >= 2 then temp_w_max = tonumber( arg[1] ) temp_c_max = tonumber( arg[2] ) end -- 湿度条件 -- if table.getn(arg) >= 2 then -- hum_w_max = tonumber( arg[1] ) -- hum_c_max = tonumber( arg[2] ) -- end -- CO2条件 if table.getn(arg) >= 4 then co2_w_max = tonumber( arg[3] ) co2_c_max = tonumber( arg[4] ) end fh = assert( io.popen( "/usr/bin/echo -e 'fetch switchbot_meter_co2\nQUIT\n' | /bin/nc 127.0.0.1 4949" , "r" ) ) lines = fh:read("*a") fh:close() temp = find_value( lines , "temperature.value" ) hum = find_value( lines , "humidity.value" ) co2 = find_value( lines , "CO2.value" ) if temp >= temp_c_max then mes = "Critical temperature" ret = 2 elseif temp >= temp_w_max then mes = "Warning temperature" ret = 1 elseif co2 >= co2_c_max then mes = "Critical CO2" ret = 2 elseif co2 >= co2_w_max then mes = "Warning CO2" ret = 1 else mes = "OK -" ret = 0 end -- 結果を返す print( string.format( "SWBT_MeterCO2 %s %3.1f[C] %2.0f[%%] %3.0f[ppm]" , mes , temp , hum , co2 ) ) os.exit( ret )
比較検証
「/usr/bin/time -v コマンド」を用いて、各プログラムのメモリ使用量などで比較してみた。lua で書いたものが一番軽量。luac でコンパイルも試したけど、luac で生成されたバイトコードを起動するために、lua コマンドを使うため、コンパイルの効果は薄かった。
Munin の Shell を使った SwitchBot 参照 -- 最大メモリ使用量 17,960 kB nagios4 の Munin 参照の Perl プログラム -- 最大メモリ使用量 10,176 kB nagios4 の Munin 参照の lua5.0 プログラム -- 最大メモリ使用量 2,604 kB
Switchbot 温湿度CO2センサー
最初の確認
{ "statusCode": 100, "body": { "version": "V1.5", "temperature": 24.9, "battery": 100, "humidity": 58, "CO2": 999, "deviceId": "xxxxxxxxxxxx", "deviceType": "MeterPro(CO2)", "hubDeviceId": "000000000000" }, "message": "success" }
Munin, nagios4 でモニタリング
先に、温湿度モニタで実験してあったから、CO2も含めた Munin でのモニタリング、nagios4 での監視も早々に動き出す。
半日ほどモニタリングした状況だと、朝寒く暖房をかけると、1500[ppm]程から、6000[ppm]まで一度に増えている。CO2モニターを販売している製品の資料を見ると、1000[ppm]を越えると眠気や不快感といった記載もあるし、2500[ppm]を越えると健康被害が予想されるといった記載もある。でもストーブ付けたら、簡単に越えてしまっている。
定常状態(部屋に猫1匹)だと500[ppm]ほどか。
Switchbot CO2センサーようやく配送
10/20に注文した SwitchBot の 温湿度 CO2 センサーだけど、かなり待たされた。
今日は急遽宿直になったし、黒猫なら宿直明けに配送分配所に受け取りに行こうとおもったけど、黒猫のトラッキングIDがない。よくよく見ると 配送 Amazon になってる。そっか、”宅配”じゃないんだ。
Switchbot 温湿度計のファームウェアが更新できない
Switchbot API を使って温湿度計のデータを取得できるようにはなったけど、バッテリー状態は常に 100% 表示。
Switchbot の OpenWonderLabs / SwitchBotAPI の README だと、Swichbot 温湿度計の最新は V4.2 と書いてあるけど、アプリで確認するけど、V0.8 の表示。これが原因かと温湿度計のファームウェアの更新をかける(裏ボタンを押しながら乾電池ONでアプリから更新を行う)けど、ファームウェア更新の表示はでるけど、バージョンは相変わらず V0.8 のまま。
原因不明だなぁ…
日立洗濯機API
日立の洗濯機は WiFi で接続できるので、nmap をかけてみたが、反応なし。
でも何気なく「日立 洗濯機 API」でググると、下記の解析した人の情報を見つける。洗濯機の状況を把握できると面白いので色々試してみよう。
ひとまず記事の確認で、UDP ポートの全スキャンすると 50000 からの反応が確認できた。
$ sudo nmap -sU -p- washer-dryer Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-10-29 16:03 JST Nmap scan report for washer-dryer (192.168.11.37) Host is up (0.018s latency). rDNS record for 192.168.xx.xx: washer-dryer Not shown: 65534 closed udp ports (port-unreach) PORT STATE SERVICE 50000/udp open|filtered unknown MAC Address: 9C:2F:9D:xx:xx:xx (Liteon Technology) Nmap done: 1 IP address (1 host up) scanned in 88.22 seconds
PyCryptodomeのインストール
$ sudo apt install python3-pycryptodome or pip3 install pycryptodome $ sudo apt install build-essential python-dev-is-python3 $ pip install pycryptodomex $ pip install pycryptodome-test-vectors $ python3 -m Cryptodome.SelfTest
curl で switchbot API v1.1 を呼出す
Amazon タイムセールで、SwitchBot CO2 センサー付き温湿度計の割引がでていたので、Hub mini セットで発注。CO2センサーは以前 CCS811 でモニタリングしていたけど、もともと精度が悪かったし異常値がでるようになってモニタリングをやめてるので、SwitchBot で復活させたくて、買ってしまった。だけど、CO2センサーは発送が始まらず、Hub mini だけが届く。ということで、ひとまず Hub mini で遊ぶ。
curl で switchbot API の情報を取得する
Python などで API をたたく記事が多いけど、もう少しシンプルにできないかということで、手抜き curl で試してみる。
まずは、最もシンプルな、v1.0 で取得する方法。
最初の token, secret には、SwitchBot スマホアプリで入手したものを書き込む。(v1.0では secret 使ってないけど)
#!/bin/bash token="xxxxxx....xxxxxxx" secret="yyyy...yyy" url_list_v10="https://api.switch-bot.com/v1.0/devices" curl -s -H "Authorization:${token}" "${url_list_v10}"
switchbot API v1.1 で取得する
ちょっと面倒だけど、セキュリティ的には v1.1 で取得するのが定番。最初は Unauthorized で失敗して色々と試行錯誤したけど、openssl … -binary がキモだった。(こちらの記事を見て、sha256 のハッシュの形式が違うのに気づけた。)
#!/bin/bash token="xxxxxx....xxxxxxx" secret="yyyy...yyy" t="$(/usr/bin/date +%s%3N)" # time = Epoch time 13桁 nonce="$(/usr/bin/uuidgen)" # uuid sign=$(echo -n "$token$t$nonce" \ | /usr/bin/openssl dgst -sha256 -hmac "$secret" -binary \ | /usr/bin/base64 -w 0) url_list_v11="https://api.switch-bot.com/v1.1/devices" curl -s --request GET \ -H "Content-Type: application/json" \ -H "Authorization: ${token}" \ -H "sign: ${sign}" -H "nonce: ${nonce}" -H "t: ${t}" \ "${url_list_v11}"
取得した json コンテンツから特定のデバイスの情報を抜粋する
結果は、JSON 形式なので、jq を使って必要な場所だけ抜粋する。
$ bash swbot.sh \ | jq '.body.deviceList[] | select( .deviceId == "ZZZZZZZZZZZZZZZZ")'
Switchbot meter plus から温度,湿度を表示
Switchbot Hub mini が無い時は、温湿度計(meter plus)からのデータ取得は Bluetooth 経由で動かしていたので、Raspberry Pi で温度を取得していたが、この方法であれば LAN 接続であればどこからでも取得可能なので便利。
(略) swbot_meter="ZZZZZZZZZZZZZZZZ" url_list_v11_meter="${url_list_v11}/${swbot_meter}/status" curl -s --request GET \ -H "Content-Type: application/json" \ -H "Authorization: ${token}" \ -H "sign: ${sign}" -H "nonce: ${nonce}" -H "t: ${t}" \ "${url_list_v11_meter}" \ | jq -r '.body | "Temperature: " + (.temperature|tostring) + "C\n" \ + "Humidity: " + (.humidity|tostring) + "%"'
これで、温度、湿度がとれる。これなら munin 用のスクリプトも shell で簡単に書けそう。だけど、バッテリー残量(.battery)は 60% 台まで落ちているはずだけど、100% なんだよな。他の SwitchBot Plug でも、電力値が取れなかったりと SwitchBotAPI は、全機能を網羅していない様子。
sshd のエラーメッセージ
sshd の設定をあまり見直していないが、下記のメッセージが出るようになっている。
2024-10-05T20:03:58.314494+09:00 xxxx sshd[176045]: rexec line 15: Deprecated option UsePrivilegeSeparation 2024-10-05T20:03:58.314710+09:00 xxxx sshd[176045]: rexec line 18: Deprecated option KeyRegenerationInterval 2024-10-05T20:03:58.314778+09:00 xxxx sshd[176045]: rexec line 19: Deprecated option ServerKeyBits 2024-10-05T20:03:58.314840+09:00 xxxx sshd[176045]: rexec line 31: Deprecated option RSAAuthentication 2024-10-05T20:03:58.314896+09:00 xxxx sshd[176045]: rexec line 38: Deprecated option RhostsRSAAuthentication 2024-10-05T20:20:31.361176+09:00 xxxx sshd[177351]: Unable to load host key: /etc/ssh/ssh_host_dsa_key
どれも、関連する行をコメントアウトで対応。
mariadb のエラー対策
別件で /var/log/syslog を確認したら、下記のエラーが大量に出力されている。
2024-10-05T19:53:54.546148+09:00 xxxx mariadbd[1076]: 2024-10-05 19:53:54 3785 [ERROR] Incorrect definition of table mysql.column_stats: expected column 'hist_type' at position 9 to have type enum('SINGLE_PREC_HB','DOUBLE_PREC_HB','JSON_HB'), found type enum('SINGLE_PREC_HB','DOUBLE_PREC_HB'). 2024-10-05T19:53:54.546217+09:00 xxxx mariadbd[1076]: 2024-10-05 19:53:54 3785 [ERROR] Incorrect definition of table mysql.column_stats: expected column 'histogram' at position 10 to have type longblob, found type varbinary(255).
エラーメッセージでググると、下記のコマンドで直るとのこと。
ALTER TABLE mysql.column_stats MODIFY histogram longblob; ALTER TABLE mysql.column_stats MODIFY hist_type enum('SINGLE_PREC_HB','DOUBLE_PREC_HB','JSON_HB');
ということで、実行してみる。
# mariadb -u root : MariaDB [(none)]> ALTER TABLE mysql.column_stats MODIFY histogram longblob; Query OK, 0 rows affected (0.496 sec) Records: 0 Duplicates: 0 Warnings: 0 MariaDB [(none)]> ALTER TABLE mysql.column_stats MODIFY hist_type enum('SINGLE_PREC_HB','DOUBLE_PREC_HB','JSON_HB'); Query OK, 0 rows affected (0.176 sec) Records: 0 Duplicates: 0 Warnings: 0
エラーメッセージは出なくなったようだ。