As I Please

MTのいんすとーるの練習と、その他びぼうろく・・・

switchbot 温湿度計2つ目

家の中にセンサーを置いていろいろ(安価に)計測するのに、4年前にはテストで CC2650STKを購入して BlueToothでデータの取得とかやってたが、単純に 温度・湿度程度であれば、SwitchBotの温湿度計が良さそうということで購入してみて、データを取り始めていた。Switchbotハブミニと一緒に導入したら nature remo と同様に Cloudにデータが上がり、auth0 の認証で APIを叩くことで温度・湿度のデータが取れる。が、すでに nature remoもあるし赤外線リモコンはもう不要だし、そもそも Bluetooth でローカルにデータは出ている、RSSIとBatteryはAPIでは取得できず、なので、4000円弱出して ハブミニを買うのをためらっていた。
室内の温度とは別に、冷蔵庫(飲み物用の、出し入れ扉がガラス製)が夏にはときどきエラーを出して内部温度が上がってしまうことが月に1度程度発生していたので、それを検知するのに、もう1つswitchbot温湿度計を買って今年の夏に備えることにてみようと考えた。
CC2650だと、2分以内に connect して繋ぎ続けないと BLEの接続が切れてしまう仕様で消費電力が多くなるが connect しながら使い続ける必要があり、そうなると電源(CR2022)の寿命問題があるので外付けで単3x8本(2本直列x4並列)など作ってみたが最長3ヶ月くらいしかもたなかった。もっとうまいハンドリング手法があるのだろうけど、、、と思いながら switchbot を使ってみたら単4電源2本で半年は優に問題なくデータを出し続けていて、かつ、バッテリー表示が100%と本当か?と思えるくらい。温度だけだけど安定してデータが長期間取れるのであれば、まずはこれで冷蔵庫のモニターは必要十分ということで、amazonでもう1つ買ってみた。

ここから時系列で書いてみると、

  • 新Switchbotで接続・データ取得はうまくいく。ただし液晶の温度で数字で2,3カ所表示されないところがあって、これはちょっと何だかな・・・まぁ冷蔵庫の中に入れるので問題ないか。
  • これを冷蔵庫の中に入れたら、データが電波強度が激減。データがraspberyy pi3では取れない。が、同じ場所にいる iPhoneアプリからは取得可能。でかつ過去データの取得もOK.冷蔵庫の扉のガラス、2重でUVカットとかをうたっているのでこれが影響?
  • データを取得するために、タイムアウト対応を強化(取れないときはあきらめる)したプログラムで動かしてみるが、やはり3時間に1度くらいしかデータを拾って来ない。raspberryp pi3の位置・角度を変えたりしてみたがなかなか良いところは見つからない。
  • 電池消耗を抑えるために、BLEの advertisingデータを拾って温湿度等を取っていたが不安定。アプリだとしっかりconnectしてデータ取得しているようなので、公式のgit.hubにあるpython-hostを見て、connect してデータを取得しようとしたら、古いものはデータが来るが、新しく購入した方はデータが取れない。Errorを返す。どうも、GATTを発行しているところで error になる。2つとも firmwareは同じバージョン(2.6)なのに、、、?とはいえ、CC2650STKでもあった話だから、bluetooth機器だとよくある話なのかも
  • ふと思い立って、古いほうの switchbot を冷蔵庫の中に入れて運用してみたら、これも電波強度は落ちているかもしれないが、raspberry pi3でデータは取得している。時に欠損するときもあるが新しいものとは段違いに取れている。これ、同じものとは言えない?
ということで、同一製品とは言えどうも中身が違うのでは?という感じ。
新しいもののほうが劣化版のような気がするので、液晶表示が欠けるのを理由に、返却・交換してもらうことに。新しいものも劣化している可能性はあるが。

メールがspamassasin (sa-spamd) を通っていない。

spamassasinは怪しいメールには、メール本文の冒頭に警告メッセージをつけてくれるので、スマートフォンなどのメール通知で怪しいメールが来たかどうかをすぐに見分けることができる。本当はすぐに隔離して mailboxに入らないようにすればいいんでしょうが。。。
ところが最近、別のサーバ(MX secondary)に届くメールにそれが入らなくなってしまった。どうも milterでsa-spamd を通らなくなってしまったようだ。
milter-manager ? spamd ? socketのowner,permission ? 何が悪いのかよくわからないまま、perlのライブラリ、ruby、openssl,gpgなどいろいろアップデートしてみたが、なかなか解決と行かなかった。


/var/log/spamd.log をつぶさに追っかけて、spamdがちゃんと起動していないのが原因のよう。
どうも sendmail をバージョンアップしたときからおかしくなってしまったよう。

error: Can't locate Net/SSLeay.pm in @INC
spamd: error: Bad arg length for Socket::unpack_sockaddr_in, length is 28, should be 16
あたりが問題だったようで、以下の記事を参照に、Socket6 を再インストールしたら解決した。
https://freebsd.sing.ne.jp/daily/09/09/05.html

let's encrypt のサーバ証明書を使っていて、iphoneのメールアプリでエラーが表示される件

let's encryptの無料サーバ証明書を、httpsサーバ(このサイト)や他サイトでも利用していて、webだけではなくて、smtp(s)でも流用できるので使っていた。
メールも受信(server)・送信(client) がデフォルトで smtps/starttls を利用するようになってきているようで、
グローバルにauthorized された証明書を使っているほうが、いろいろ良さそう。
imapsは相変わらずプライベートCA+証明書を利用しているけど。

先日、2021/09でlets encrypt の証明書のCA問題の影響を受けて、iOSの標準のメールアプリからメールサーバ(smtps)の接続がうまくいかない(メールが送れなく)なった。imapsでの接続でメールの取得・閲覧はうまくいっているに。
ということでいろいろ調べてみると、openssl 1.0.2 ではCA証明書への辿りの解釈に難があるよう。
で、問題を起こしているのは sendmail が共有ライブラリとして libssl.so をリンクしていてその中身が 1.0.2(またはそれ以前)のホストばっかり!ということに気づいた。opensslは 1.1系を /usr/local/bin/openssl にインストール,libssl.so は /usr/local/lib/ あたりにいたが、sendmailのを作成したときには /usr/lib/libssl.so あたり(/usr/bin/openssl は version 1.0.2!) の共有ライブラリをリンクしていた!
なので対応としては、

  1. openssl 1.1系列(今の最新は1.1.1l)をコンパイルして、共有ライブラリのバージョン(1.1.1)を用意する。
  2. sendmailを作成し直して、openssl(libssl.so or libssl.a) をリンクし直す。
ことに。
sendmailも8.15.2と思ったら、8.17.1が最新なのでこれも sourceから持ってきて recompileした。
$src/devtools/Site/site.config.m4 の設定をよくみて、、、。
EOLになってしまっている、FreeBSD-11.4だと、

APPENDDEF(`conf_smrsh_ENVDEF', `-DCMDDIR="\"/usr/local/libexec/sm.bin\""')
APPENDDEF(`conf_smrsh_ENVDEF', `-DPATH="\"/bin:/usr/bin\""')
define(`confEBINDIR',`/usr/local/libexec')
define(`confMANROOT',`/usr/local/man/cat')
define(`confMANROOTMAN',`/usr/local/man/man')
define(`confMBINDIR',`/usr/local/sbin')
define(`confSBINDIR',`/usr/local/sbin')
define(`confUBINDIR',`/usr/local/bin')
define(`confNO_STATISTICS_INSTALL',`True')
define(`confHFDIR', `/usr/local/share/sendmail')
APPENDDEF(`conf_sendmail_ENVDEF', `-DTCPWRAPPERS')
APPENDDEF(`conf_sendmail_LIBS', `-lwrap')"
APPENDDEF(`conf_sendmail_ENVDEF', `-DNETINET6')
APPENDDEF(`conf_libmilter_ENVDEF', `-DNETINET6')
APPENDDEF(`conf_libsm_ENVDEF', `-DNETINET6')
APPENDDEF(`conf_sendmail_ENVDEF', `-DDANE')
APPENDDEF(`conf_sendmail_ENVDEF', `-I/usr/local/include')
APPENDDEF(`conf_sendmail_ENVDEF', `-DSASL=2')
APPENDDEF(`confLIBDIRS', `-L/usr/local/lib')
APPENDDEF(`conf_sendmail_LIBS', `-lsasl2')
APPENDDEF(`conf_sendmail_ENVDEF', `-DUSE_BLACKLIST')
APPENDDEF(`conf_sendmail_LIBS', `-lblacklist')
APPENDDEF(`conf_libmilter_ENVDEF', `-DMILTER')
APPENDDEF(`conf_sendmail_ENVDEF', `-DMILTER')
APPENDDEF(`conf_sendmail_ENVDEF', `-DHASSRANDOMDEV')
APPENDDEF(`confINCDIRS', `-I/usr/local/include')
APPENDDEF(`confLIBDIRS', `-L/usr/local/lib')
APPENDDEF(`confLDOPTS', ``-Wl,-rpath=/usr/local/lib'')
APPENDDEF(`conf_sendmail_ENVDEF', `-DSTARTTLS -DTLS_EC')
APPENDDEF(`conf_sendmail_LIBS', `-lssl -lcrypto')
APPENDDEF(`conf_sendmail_ENVDEF', `-DPICKY_HELO_CHECK')
sendmail-8.15のときとの違いは、、、'_FFR_TLS_EC' だったものが 'TLS_EC'と正規?なパラメータ名になったこと、くらいか。いつものように、milter,sasl2,あたりをlinkすることも忘れないように。

sendmailを展開した $srcトップ にて、'sh ./Build;sh ./Build install'
実はこの作業の前に FreeBSD 11.2 -> 11.4, perlの入れ直し、gccの入れ直しなど大工事あり・・・
それにしても、、、古い OSを使い続けるとどんどん大変になる。

sshへの不正アクセスが多いので、denyhosts をいまさらながらに導入

しつこいのは手動で /etc/hosts.allow をメンテしていたり、/etc/ssh/sshd_config で MaxAuthTries 2 とかですぐにたたき落とすようにしていたけど、やはり自動化しておいたほうが無難かと。

http://tokcs.com/2012/12/freebsd-denyhosts%E3%82%92%E5%B0%8E%E5%85%A5/を見つけてdenyhostsが簡単そうなので、インストールしてみる。
portsから入れたが、python3 が入っていればすんなり入る。
/sbin/iptables を起動しようとしたりするけどとりあえず放っておく。
早速起動したら、/var/log/auth.log を見て、3つのIPアドレスを denyした。
台湾、フランス、ロシア(シベリア)
今後はこの手は必須で入れておく必要ありかなぁ。。。時にアドレスリストのメンテが必要かもしれない(膨大に増えていく?)けど。

ubuntu20 でのサービスの起動・停止・自動起動など

FreeBSDなら 自動起動は /etc/rc.conf あたりで設定、個別のサービスの on/offは /etc/rc.d/, /usr/local/etc/rc.d/ あたりを見るというのは身体に染みついているけど、linux系はよく分かっていなくて、再起動するとサービスが立ち上がってこなかったりというのをよくやらかし、結局よくわかってない。systemctl 使いにならなくては。。。

とりあえず、まとままとまとまっているのは、https://eng-entrance.com/linux_startupあたりか。
start/stop/restart , enable/disable あたり。
サービス一覧は、systemctl list-units あたり?

CentOS6 の /etc/init.d あたりの記憶から抜け出さないと。

imapsync の再インストール (Big Sur)他

Big Sur がそろそろ落ち着いているかと思ってアップデートしたら、imapsyncが、Segmentation faultで動かなくなってしまった。
/usr/local/bin/imapsync (imapsync を起動する bash script wrapper へのシンボリックリンク)がおかしくて、単体の /usr/local/Cellar/imapsync/1.977/libexec/bin/imapsyncはちゃんと動く。。。
bashがおかしくなったのかとも思ったが、https://brewinstall.org/install-imapsync-on-mac-with-brew/ を見て、単に "brew reinstall imapsync" で動き始めた。ここらへんの依存関係、わかりにくい。

ローカルの証明書の有効期限を通知2 (subjectの文字化け)

ローカルの証明書の有効期限を通知で仕込んでいたスクリプト/cronがちゃんと動いて60日前に証明書の期限切れを通知してくれた。が、Subjectが軽く文字化けしてた。 diffを見ると、
root@ns5:/usr/local/sbin # diff check_crt.pl check_crt.pl.20201026
26,27c22
< #$server_ssl = `/usr/bin/openssl x509 -enddate -in $ARGV[0] |/usr/bin/grep notAfter`;
< $server_ssl = `/usr/local/bin/openssl x509 -enddate -in $ARGV[0] |/usr/bin/grep notAfter`;
---
> $server_ssl = `/usr/bin/openssl x509 -enddate -in $ARGV[0] |/usr/bin/grep notAfter`;
81c76
< $subject_jis = encode("MIME-Header-ISO_2022_JP",$subject);
---
>
85c80
< Subject: $subject_jis
---
> Subject: $subject
106d100
< $subject_jis = encode("MIME-Header-ISO_2022_JP",$subject);
110,111c104
< Subject: $subject_jis
<
---
> Subject: $subject
127,131c120
< #$header_jis = encode("iso-2022-jp",$header);
< #
< # see https://tutorial.perlzemi.com/blog/20170424149304.html
< #
< #$header_jis = encode("MIME-Header-ISO_2022_JP",$header);
---
> $header_jis = encode("iso-2022-jp",$header);
139,140c128
< #$smtp->datasend($header_jis);
< $smtp->datasend($header);
---
> $smtp->datasend($header_jis);
のような変更を入れた。

sitemap.xml の作成

themaを変更したタイミングだかで、sitemapが作られなくなったようで。 MT7では標準ではついていない(そもそも MTは昔から持っていない?)ので、ちょっと探して、MovableTypeのこのページのものを借用。 Movabletype.net では標準で作られるようだけど。 これで出力されるURLを、Google Search Console のサイトマップに登録。

Google Search Console: data-vocabulary.orgのエラー

Theme換えをしたところ、Google Search Consoleからのメールで、 「パンくずリストで問題が検出されました。」とのこと。 https://developers.google.com/search/docs/data-types/breadcrumbを参照ということで、 data-vocabulary.orgの schemaはやめて、schema.orgを使ってね、ということらしい。 テンプレートを検索してみると使っているのは「ウェブページ」「記事」「月別記事リスト」「コメントプレビュー」「カテゴリ別記事リスト」の5つ。 参考にしたのはhttps://gen.fukatani.org/2020/04/eliminating-data-vocabulary-org-schema.htmlで、これは「記事」のパンくずリストと全く同じなので、このまま入れ替え。 あと、「月別記事リスト」「カテゴリ別記事リスト」はhttps://schema.org/BreadcrumbList に単純に置き換えてみる。階層でcontent, position の設定が必要とは思うが、たかだか2階層しかないので認識してくれるのでは?と甘く考える。 または後日対応。 「ウェブページ」は今のところ使っていないのでとりあえずこのまま。 「コメントプレビュー」も同じ。

ビエラ panasonic のIPコントロール(DT5)とめざましじゃんけん

古いパナソニックビエラ(うちのはDT5)でリモコン操作(赤外線)を、IP経由で行う。 nature remo で赤外線飛ばすのは時に安定しないしプログラマブルにはほど遠い感じがするので、 IPでやれないかと。 手法は、
  1. upnp でIPアドレス、送信先を取得
  2. soapで投げつける。
という方向で。 参照、利用したのは、pyvieraというライブラリ。 古いので認証なしで使える。ただし、python2 用のようで、viera.py を改修。urlilb2を使っていたり、海外のリモコンエミューレトなのでいくつかのボタンが無い(NRC_DATA-ONOFF:dボタン、NRC_PROG-ONOFF:番組表、NRC_SPLIT-ONOFF:2画面?)ので、これらにちょっとだけ対処。ドイツのPanasonic TV mit IPS Steuernというものを参考に。dボタンのコードがなかなか見つからなかった。改修した viera.pyはこちら
import urllib.request,urllib.error, urllib.parse
class Viera(object):
    def __init__(self, hostname, control_url, service_type):
        self.hostname = hostname
        self.control_url = control_url
        self.service_type = service_type
        self.sendkey_action = Action('X_SendKey', ('X_KeyEvent',))
    def _sendkey(self, slug):
        req = self.sendkey_action.to_soap_request(
            self.control_url,
            self.hostname,
            self.service_type,
            (slug,),
        )
        urllib.request.urlopen(req).read()
    def __unicode__(self):
        return '' % (
            self.hostname,
            self.control_url,
            self.service_type,
        )
    def vol_up(self):
        self._sendkey('NRC_VOLUP-ONOFF')
    def vol_down(self):
        self._sendkey('NRC_VOLDOWN-ONOFF')
    def mute(self):
        self._sendkey('NRC_MUTE-ONOFF')
    def num(self, number):
        for digit in str(number):
            self._sendkey('NRC_D%s-ONOFF' % digit)
    def power(self):
        self._sendkey('NRC_TV-ONOFF')
    def toggle_3D(self):
        self._sendkey('NRC_3D-ONOFF')
    def toggle_SDCard(self):
        self._sendkey('NRC_SD_CARD-ONOFF')
    def red(self):
        self._sendkey('NRC_RED-ONOFF')
    def green(self):
        self._sendkey('NRC_GREEN-ONOFF')
    def yellow(self):
        self._sendkey('NRC_YELLOW-ONOFF')
    def blue(self):
        self._sendkey('NRC_BLUE-ONOFF')
    def vtools(self):
        self._sendkey('NRC_VTOOLS-ONOFF')
    def cancel(self):
        self._sendkey('NRC_CANCEL-ONOFF')
    def option(self):
        self._sendkey('NRC_SUBMENU-ONOFF')
    def Return(self):
        self.sendkey('NRC_RETURN-ONOFF')
    def enter(self):
        self._sendkey('NRC_ENTER-ONOFF')
    def right(self):
        self._sendkey('NRC_RIGHT-ONOFF')
    def left(self):
        self._sendkey('NRC_LEFT-ONOFF')
    def up(self):
        self._sendkey('NRC_UP-ONOFF')
    def down(self):
        self._sendkey('NRC_DOWN-ONOFF')
    def display(self):
        self._sendkey('NRC_DISP_MODE-ONOFF')
    def menu(self):
        self._sendkey('NRC_MENU-ONOFF')
    def connect(self):
        self._sendkey('NRC_INTERNET-ONOFF')
    def link(self):
        self._sendkey('NRC_VIERA_LINK-ONOFF')
    def guide(self):
        self._sendkey('NRC_EPG-ONOFF')
    def text(self):
        self._sendkey('NRC_TEXT-ONOFF')
    def subtitles(self):
        self._sendkey('NRC_STTL-ONOFF')
    def info(self):
        self._sendkey('NRC_INFO-ONOFF')
    def index(self):
        self._sendkey('NRC_INDEX-ONOFF')
    def hold(self):
        self._sendkey('NRC_HOLD-ONOFF')
    def d(self):
        self._sendkey('NRC_DATA-ONOFF')
    def prog(self):
        self._sendkey('NRC_PROG-ONOFF')
    def split(self):
        self._sendkey('NRC_SPLIT-ONOFF')
class Action(object):
    def __init__(self, name, arguments):
        self.name = name
        self.arguments = arguments
    def to_soap_request(self, url, hostname, service_type, values):
        assert len(values) == len(self.arguments)
        params = ''.join(['<%s>%s' % (arg, value, arg) for arg, value in zip(self.arguments, values)])
        soap_body = (
            ''
            ''
            ''
            ''
            '%(params)s'
            ''
            ''
        ''
        ) % {
            'method_name': self.name,
            'service_type': service_type,
            'params': params,
        }
        headers = {
            'Host': hostname,
            'Content-Length': len(soap_body),
            'Content-Type': 'text/xml',
            'SOAPAction': '"%s#%s"' % (service_type, self.name),
        }
        soap_body = soap_body.encode()
#        headers = urllib.parse.urlencode(headers).encode()
        req = urllib.request.Request(url, soap_body, headers)
        return req
テスト稼働で、フジテレビめざましじゃんけんを自動で毎日やらせてみた。
import socket
import urllib.request,urllib.error,urllib.parse
from urllib.request import urlopen
from viera import Viera
from pyviera import VieraFinder
import time
import random
tv = Viera('(IP_address)','http://(IP_address):55000/nrc/control_0','urn:panasonic-com:service:p00NetworkControl:1')
if __name__ == '__main__':
    tv.num(8)
    time.sleep(5)
    tv.info()
    for i in range(20):
        button = random.choice(('tv.blue()','tv.red()','tv.green()'))
        eval(button)
        time.sleep(3)
これを、月~金は 05:57,06:57,07:34,07:57に、土は 07:37,08:21 にcronで起動。 テレビがついていれば、IP経由でch8 に変更して、ランダムに青赤緑を投げつける。 半年以上動かしていて、一週間で最大300点オーバーの週もあった。 ちなみに、最近のvieraはアプリ最初のアクセス時に認証を求めるようで、そうするとこちらのライブラリが良さそう。 https://github.com/florianholzapfel/panasonic-viera/issues/9
https://github.com/florianholzapfel/panasonic-viera/blob/master/panasonic_viera/__init__.py PINコードを入力して、credential情報を取得し使い回す必要があるようだ。