npmのバージョンエラーと本家npmへの移行

今回はおま環なお話。

iPadから照明をコントロールするため、Raspberry Pi Zero Wと4chリレーや、ESP easy化したSONOFFを壁の中に埋め込み、GPIOを使って壁のスイッチからも、iPadのHomeからも、AndroidやWebページ上のHomeAssistatからも操作できるようにしています。

gentoolinux.hatenablog.com

gentoolinux.hatenablog.com

gentoolinux.hatenablog.com

リビングの照明はRaspberry Pi Zero Wでコントロールしていますが、それらのデバイスを司り、Homeアプリに中継するHomebrigeというミドルウェアは、Gentoo Linuxサーバーに設定しています。
先日、何気なくGentoo Linuxにインストールされているソフトウェアをバージョンアップすべく、emerge -uDN @worldを実行したところ、いつになく呆気なく、500以上のパッケージがノーエラーでバージョンアップできました。
そこで、再起動をかけたところ、なんだかcron.hourlyが毎時間エラーレポートをメールで送信してきています。
cron.hourlyには、homebridgeのプロセスに異常があった場合、再起動するように仕掛けてます。(それくらい不安定だった。)
が、どうやらnode.jsがバージョンアップされたためにエラーが起きていたらしいのです。

homebridge porosess is not found.
Starting homebridge

/usr/local/lib/node_modules/homebridge/lib/logger.js:9
const chalk_1 = __importDefault(require("chalk"));
                                ^
Error [ERR_REQUIRE_ESM]: require() of ES Module /usr/local/lib/node_modules/homebridge/node_modules/chalk/source/index.js from /usr/local/lib/node_modules/homebridge/lib/logger.js not supported.
Instead change the require of index.js in /usr/local/lib/node_modules/homebridge/lib/logger.js to a dynamic import() which is available in all CommonJS modules.
    at Object.<anonymous> (/usr/local/lib/node_modules/homebridge/lib/logger.js:9:33)

そこで、これらを解消しようと、まずnpmでhomebridgeをアップデートしようとしました。

gentoo / # npm update homebridge
npm WARN npm npm does not support Node.js v20.11.0
npm WARN npm You should probably upgrade to a newer version of node as we
npm WARN npm can't make any promises that npm will work with this version.
npm WARN npm Supported releases of Node.js are the latest release of 4, 6, 7, 8.
npm WARN npm You can find the latest version at https://nodejs.org/
npm ERR! cb.apply is not a function

npm ERR! A complete log of this run can be found in:
npm ERR!     /root/.npm/_logs/2024-02-27T14_26_31_191Z-debug.log

意訳すると「このnpmはNode.js v20.11.0をサポートしてないよ。node.jsをアップデートしないとダメだよ。このnpmはnode.jsのバージョン4,6,7,8をサポートしてるよ。」
って、ここに入っているnode.jsはgentooでは最新のv20なんですけど・・・。

そして、自分の所業を遡ると、こんなことが書いてました。

gentoolinux.hatenablog.com

これを読むと、「useフラグにnpmをつけてnodejsをemergeすると、勝手にnpmも入ってくるよ」と。
ということは、今使っているnpmは何らかの理由で(理由は忘れた)、オーバーレイインストールしたか、無理矢理手動インストールしたかのどちらかです。

npmはどこにあるのか?

gentoo / # npm -v
5.5.1
gentoo / # which npm
/usr/local/bin/npm
gentoo / # ls -al /usr/local/bin/npm
lrwxrwxrwx  1 root root       38 1114  2017 npm -> ../lib/node_modules/npm/bin/npm-cli.js

実態であるnpm-cli.jsのバージョンが古いんですね。しかも、/usr/local/libにインストールしているということは、公式リポジトリ以外からインストールしているっていうことですね。
npmをUSEフラグに記述しているので、node.jsのアップデート時に、最新のnpmがどこかにインストールされているはずです。

gentoo / # find / -name npm-cli.js
/var/tmp/portage/net-libs/nodejs-20.8.1/work/node-v20.8.1/deps/npm/bin/npm-cli.js
/var/tmp/portage/net-libs/nodejs-20.8.1/work/node-v20.8.1/deps/npm/test/bin/npm-cli.js
/var/tmp/portage/net-libs/nodejs-20.9.0/work/node-v20.9.0/deps/npm/bin/npm-cli.js
/var/tmp/portage/net-libs/nodejs-20.9.0/work/node-v20.9.0/deps/npm/test/bin/npm-cli.js
/usr/local/n/versions/node/8.9.1/lib/node_modules/npm/bin/npm-cli.js
/usr/local/n/versions/node/9.0.0/lib/node_modules/npm/bin/npm-cli.js
/usr/local/lib/node_modules/npm/bin/npm-cli.js
/usr/lib64/node_modules/npm/bin/npm-cli.js

gentoo / # /usr/lib64/node_modules/npm/bin/npm-cli.js -v
10.2.4

公式リポジトリ上のnpmは/usr/lib64/node_modules/npm/bin/npm-cli.jsにあるようです。
ということは、公式リポジトリからリンクされているnpm自体も、/usr/local/binではない場所にありそうです。

gentoo / #  find / -name npm
/etc/npm
/var/tmp/portage/net-libs/nodejs-20.8.1/work/node-v20.8.1/deps/corepack/shims/npm
/var/tmp/portage/net-libs/nodejs-20.8.1/work/node-v20.8.1/deps/corepack/shims/nodewin/npm
/var/tmp/portage/net-libs/nodejs-20.8.1/work/node-v20.8.1/deps/npm
/var/tmp/portage/net-libs/nodejs-20.8.1/work/node-v20.8.1/deps/npm/bin/npm
/var/tmp/portage/net-libs/nodejs-20.8.1/work/node-v20.8.1/tools/msvs/npm
/var/tmp/portage/net-libs/nodejs-20.8.1/work/node-v20.8.1/tools/macos-installer/pkgbuild/npm
/var/tmp/portage/net-libs/nodejs-20.9.0/work/node-v20.9.0/deps/corepack/shims/npm
/var/tmp/portage/net-libs/nodejs-20.9.0/work/node-v20.9.0/deps/corepack/shims/nodewin/npm
/var/tmp/portage/net-libs/nodejs-20.9.0/work/node-v20.9.0/deps/npm
/var/tmp/portage/net-libs/nodejs-20.9.0/work/node-v20.9.0/deps/npm/bin/npm
/var/tmp/portage/net-libs/nodejs-20.9.0/work/node-v20.9.0/tools/msvs/npm
/var/tmp/portage/net-libs/nodejs-20.9.0/work/node-v20.9.0/tools/macos-installer/pkgbuild/npm
/usr/local/n/versions/node/8.9.1/bin/npm
/usr/local/n/versions/node/8.9.1/lib/node_modules/npm
/usr/local/n/versions/node/8.9.1/lib/node_modules/npm/bin/npm
/usr/local/n/versions/node/9.0.0/bin/npm
/usr/local/n/versions/node/9.0.0/lib/node_modules/npm
/usr/local/n/versions/node/9.0.0/lib/node_modules/npm/bin/npm
/usr/local/bin/npm
/usr/local/lib/node_modules/npm
/usr/local/lib/node_modules/npm/bin/npm
/usr/lib64/node_modules/npm
/usr/lib64/node_modules/npm/bin/npm
/usr/bin/npm
/usr/share/bash-completion/completions/npm
/root/.npm/registry.npmjs.org/npm
/root/.npm/npm

gentoo / #  ls -al /usr/bin/npm
lrwxrwxrwx 1 root root 40  225 03:27 /usr/bin/npm -> ../lib64/node_modules/npm/bin/npm-cli.js

gentoo / # /usr/bin/npm -v
10.2.4

何のことはない、/usr/local/bin/npmを削除して、今後は/usr/bin/npmを使えば良いだけのことでした。

これでhomebridgeを起動してもエラーが起きます。

[2024/2/27 22:53:10] TypeError: Invalid initialization vector
    at Cipheriv.createCipherBase (node:internal/crypto/cipher:121:19)
    at Cipheriv.createCipherWithIV (node:internal/crypto/cipher:140:3)
    at new Cipheriv (node:internal/crypto/cipher:243:3)
    at Object.createCipheriv (node:crypto:146:10)
    at Object.chacha20_poly1305_encryptAndSeal (/usr/local/lib/node_modules/homebridge/node_modules/hap-nodejs/src/lib/util/hapCrypto.ts:90:25)
    at HAPServer._this._handlePairVerifyStepOne (/usr/local/lib/node_modules/homebridge/node_modules/hap-nodejs/src/lib/HAPServer.ts:553:33)
    at HAPServer._this._handlePairVerify (/usr/local/lib/node_modules/homebridge/node_modules/hap-nodejs/src/lib/HAPServer.ts:515:12)
    at IncomingMessage.<anonymous> (/usr/local/lib/node_modules/homebridge/node_modules/hap-nodejs/src/lib/HAPServer.ts:280:24)
    at IncomingMessage.emit (node:events:518:28)
    at endReadableNT (node:internal/streams/readable:1696:12)
[2024/2/27 22:53:10] Got SIGTERM, shutting down Homebridge...

まずは、関連モジュールをアップデートします。
アップデートにはnpm-check-updates(コマンドは頭文字を取ってncu)を使うと良いそうですので、npmでインストールし、ncu -uでアップデート情報をpackage.jsonに書き込み、npm installでpackage.jsonに書かれた情報から各パッケージをアップデートします。

gentoo / # npm install -g npm-check-updates
added 336 packages in 16s

67 packages are looking for funding
  run `npm fund` for details

gentoo / #  ncu -u
Upgrading /usr/local/lib/node_modules/homebridge/package.json
[====================] 20/20 100%

 @types/debug                        ^4.1.5  →   ^4.1.12
 @types/jest                       ^26.0.13  →  ^29.5.12
 @types/node                       10.17.2920.11.20
 @types/semver                       ^7.3.3  →    ^7.5.8
 @typescript-eslint/eslint-plugin    ^4.0.1  →    ^7.1.0
 @typescript-eslint/parser           ^4.0.1  →    ^7.1.0
 chalk                               ^4.1.0  →    ^5.3.0
 commander                            5.1.012.0.0
 eslint                              ^7.8.1  →   ^8.57.0
 eslint-plugin-jest                ^23.20.0  →   ^27.9.0
 hap-nodejs                         ^0.7.10  →   ^0.11.1
 jest                               ^26.4.2  →   ^29.7.0
 node-persist                       ^0.0.11  →    ^4.0.1
 rimraf                              ^3.0.2  →    ^5.0.5
 semver                              ^7.3.2  →    ^7.6.0
 source-map-support                 ^0.5.19  →   ^0.5.21
 ts-jest                            ^26.3.0  →   ^29.1.2
 ts-node                             ^9.0.0  →   ^10.9.2
 typescript                          ^4.0.2  →    ^5.3.3

Run npm install to install new versions.

gentoo / # npm install

added 546 packages, removed 11 packages, changed 58 packages, and audited 609 packages in 36s

116 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities

そして最後に、肝心のhomebridgeそのものもアップデートしましょう。

gentoo / # npm install -g --unsafe-perm homebridge@latest

added 91 packages, removed 16 packages, and changed 14 packages in 2s

43 packages are looking for funding
  run `npm fund` for details

それでもエラーが出ます。

gentoo / # /etc/local.d/homebridge.start 
Starting homebridge

/usr/local/lib/node_modules/homebridge/lib/logger.js:9
const chalk_1 = __importDefault(require("chalk"));
                                ^
Error [ERR_REQUIRE_ESM]: require() of ES Module /usr/local/lib/node_modules/homebridge/node_modules/chalk/source/index.js from /usr/local/lib/node_modules/homebridge/lib/logger.js not supported.
Instead change the require of index.js in /usr/local/lib/node_modules/homebridge/lib/logger.js to a dynamic import() which is available in all CommonJS modules.
    at Object.<anonymous> (/usr/local/lib/node_modules/homebridge/lib/logger.js:9:33)

よく読むと、そもそもhomebridge自体も/usr/local/binにあるもののようです。
最新のhomegridgeはどこなのか?

gentoo / # find / -name homebridge
/var/homebridge
/usr/local/bin/homebridge
/usr/local/lib/node_modules/homebridge-http-switch/test/homebridge
/usr/local/lib/node_modules/homebridge
/usr/local/lib/node_modules/homebridge/bin/homebridge
/usr/lib64/node_modules/homebridge
/usr/lib64/node_modules/homebridge/bin/homebridge
/usr/bin/homebridge
/root/.npm/registry.npmjs.org/homebridge

やはり、/usr/bin/homebridgeがありましたね。

/etc/local.d/homebridge.startの内容を変更します。

#!/bin/sh


#dir="/var/homebridge"
name="homebridge"
pid_file="/var/run/$name.pid"
stdout_log="/var/log/$name.log"
stderr_log="/var/log/$name.err"

echo "Starting $name"
#cd "$dir"
/usr/bin/homebridge -U /var/homebridge &
echo $! > "$pid_file"

あと、node.js自体も、/usr/local/bin/nodeにシンボリックリンクあるので、/usr/local/bin/npmや/usr/local/bin/homebridgeと一緒に消しておきます。
(これらはシンボリックリンクなので、消しても実体は残っています。)

これで無事にhomebridgeが起動・復活しました。

FX-AUDIO-『FX-05J』のオペアンプ載せ換え

家じゅうにWiimを設置するほど、Wiimにハマりましたが、これに飽き足らずクルマにも設置しようと思い立ちました。
しかし、Wiim MiniのDACはPCM5121という、スペック的にもワンランク低いものが積まれており、せっかくのビットパーフェクトをスポイルしていると思います。
そこで、Wiim Miniの光出力から別体のDACを通したいと思い、物色したところ、車載するのに相応しい仕様のDACが見つかりました。
FX-AUDIO-『FX-05J』です。

nfjapan.com

なぜこれが車載に相応しいかというと、電源スイッチがないところ。
見えないところに隠すので、イグニッションオン=通電で起動して欲しいのです。
DACチップもESS社のES9018K2Mという、過去に音が良いとされた9018Sのモバイル向けチップ。
ちょっと前のハイエンドポータブルヘッドホンアンプ、通称ポタアンなどのDACとして採用されることが多かった、音質には定評のあるチップです。
しかし、アマゾンの評価の一部に、「手持ちのアンプの方が音が良い」というような気になる記述が。
そこで、購入後にオペアンプカップリングコンデンサを載せ替えることにします。

まず、購入後の素の状態のFX-05Jを聞いてみます。
オペアンプはTL062CNという、低消費電力であるものの、音質的には標準というかイマイチなものが入っています。

ぱっと聞いた感じ、それほど悪くないので、そのまま車載しても良いのでは? と、思いましたが、しばらく聞いてみると、高音の解像感や透明感が足りず、音が平面的で広がりも少ない感じです。
試しに、いつも使っているAK4499EXの音に戻して同じ楽曲を聴いてみると、全然違いました。高音の透明感があり、ハイハットなどの金属音が澄んでますし、全音域で解像感が高く、低音もバシッと輪郭がはっきりしています。大きく違った!
これは、ES9018K2Mの本領が発揮されていないな?と、感じました。

で、オペアンプカップリングコンデンサ探しです。
このサイトを参考にさせていただきました。

spkoba1.web.fc2.com

TL062はJ-FET入力で2回路という仕様。
この仕様を満たすオペアンプを搭載する必要があります。

一番手に挙げたのは、やはりオペアンプの王様、JRCのMUSES01。
仕様通りなのですが、問題はその価格。なんと3,000円以上!
本体が3,980円なのに、本体と同価格のオペアンプはやり過ぎだな、と、思うわけです。
そこで、MUSES01のワンランク廉価版のNJM8901にしたかったのですが、DIPタイプのパッケージが見つかりません。
仕方なく、表面実装型のSOP8のNJM8901Eと変換基板を購入することにしました。

eleshop.jp

そして、カップリングコンデンサは、車に積むことを考えて105℃品をチョイス。
KAシリーズの33μFのものにしました。

eleshop.jp

まずは、NJM8901Eを変換基板にはんだ付けします。
チップを変換基板にセロテープで仮止めし、動かないようにします。
●の窪みがあるところが1番ピンです。
細いピンになんとかハンダを載せ、テスターでショートしていないか確認します。
変換基板にDIP足をはんだ付けして完成です。

さっそくFX-05JのTL062と載せ換えます。
FX-05Jは1.5のヘックスレンチ(六角レンチ)で側面から開けます。
RCAピンがある方を開けた方が良いでしょう。

側面の4つのボルトを外すと、側面が取れ、そこから基板ごと引き出すことができます。
引き出すと、TL062が見えます。

これを引き抜いて、NJM8901Eを刺します。

まず、カップリングコンデンサを載せ換える前に、NJM8901Eだけ載せ換えた時の音質チェックをやってみます。
すると、どうでしょう。
あからさまに音質が変わりました!
高音は解像感高く、澄んだ音になり、ハイハットなどの金属音に雑味が一切なくなりました。
低音もバスドラムなどの音の輪郭がはっきりしています。
チャンネルセパレーションが良くなったのか、左右の広がりも広く感じられます。
数百円のオペアンプひとつでこんなに変わるものかと思いました。
ES9018K2Mのポテンシャルをしっかりと引き出せたんじゃないかと思います。

さて、次はカップリングコンデンサの載せ換えです。
もともとはルビコン製の10μFが載っています。

Rの文字がルビコンを表しているのでしょうか?

16V、10μF、85℃という表示が見えます。
これを33μFの105℃品、ニチコンKAに変更します。
いきなり変更後の写真ですが、背の高さが電源用の他のコンデンサと同じ高さで、ギリギリだったようです。

完成後の試聴です。
オペアンプ載せ換えの音質インパクトが高すぎて、あまり大きく変わった印象がなく、むしろルビコンのままのほうがよかったんじゃないか? と、思わせるほど、カップリングコンデンサを載せ換える意味は薄かったように感じます。
とはいえ、もう後戻りはできないので、このままでいきます。

メーカーはUSB電源で駆動することを考えてTL062を採用したのだと思いますが、数百円の部品の違いでここまで音が変わるのですから、最初から別のオペアンプを載せてくれればなぁ・・・。と、思ってしまうのでした。
とはいえ、少しの出費で大きく音質向上を果たせたので、大満足の改造でした。

あ、念の為、改造は自己責任で。
保証が受けられなくなりますよ!

RTX810でのDHCP固定配布

先日、Wiimの再生情報をHome Assistantに表示させるようにましたが、個別のWiimを認識させるためにはIPアドレスを指定する必要がありました。

gentoolinux.hatenablog.com

WiimにはIPアドレスを指定する方法がなく、DHCPのみなので、RTX810側のDHCP設定で固定IPアドレスを配布しなければなりません。
RTXでは「DHCP予約アドレス」というそうです。

www.rtpro.yamaha.co.jp

これを読むと、MACアドレスと紐付けすれば良いのね、と、思っちゃうところですが、クライアントがIDを報告するタイプだと、MACアドレスで指定できません。
WiimはIDを報告するタイプです。(というか、最近のデバイスはほぼIDを報告するようです。)
IDを調べるには、RTXでDHCPのステイタスを表示されるとわかります。

# show status dhcp

DHCP Scope number: 1
      Network address: 192.168.0.0
          Leased address: 192.168.0.xxx
        (type) Client ID: (01) f4 81 00 00 00 00 00 
         Remaining lease: 2days 22hours 55min. 21secs.
(中略)
          Leased address: 192.168.0.yyy
        (type) Client ID: (ff) 6c 24 41 c8 00 01 00 01 2c 10 27 00 00 00 00 00 00 01
               Host Name: WiiM Pro-BC02
         Remaining lease: 2days 14hours 7min. 24secs.

WiimはHost Nameを報告してくれるので、大変見つけやすいです。
このClient IDをどう指定するのか? がわかりにくいのです。
この(ff)も含めて指定する必要があるのですが、カッコが不要、というのがなかなかわかりにくいのです。
3台のWiimのDHCPを固定するには、次のようなConfigになります。

dhcp scope bind 1 192.168.0.xxx ff 6c 24 41 c8 00 01 00 01 2c 10 27 00 00 00 00 00 00 01
dhcp scope bind 1 192.168.0.yyy ff 32 1e 30 24 00 01 00 01 c7 92 bc 00 00 00 00 00 00 02
dhcp scope bind 1 192.168.0.zzz ff 35 06 15 8e 00 01 00 01 c7 92 bc 00 00 00 00 00 00 03

show status dhcpでは(ff)で始まっていたのに、dhcp scope bindで指定する際はffで始めなければなりません。

また、上記ではbindの後ろはスコープIDを指定します。上記では1を指定していますが、DHCPのスコープ(リースするIPアドレスの範囲)を指定した時に付与したスコープ番号を指定する必要があります。
(スコープ範囲を複数設けることはあまりないと思うので、大抵はひとつかと思います)
スコープIDを誤ってもエラー等は一切出てこないので、気をつけてください。

HomeAssistantにWiimの再生情報を表示させる(Entities Cardにattributeも表示させる)

拙宅にはWiim Proが1台、Wiim miniが2台あります。
これをHome Assistantのフロントエンドに表示させたい。
ついでにビット深度とサンプル周波数も表示させたい。

まずは、Githubにあるwiim-customをダウンロードします。

github.com

これを、コンフィグのあるディレクトリにインストールします。
ウチだと
/home/homeassistant/.homeassistantの下です。
この下にcustom_components/というディレクトリを作ることで、公式以外のインテグレーションをインストールできます。
最終的に、/home/homeassistant/.homeassistant/custom_components/wiim_customという下にpyやjsonなんかがあるような形になります。
chownやchmodで実行権限を付与しておきます。

で、configuration.yamlに3台のWiimの設定を書きます。

media_player:
    - platform: wiim_custom
      host: 192.168.0.xxx
      name: My room Wiim Pro
      uuid: 'FF98F09Cxxxxxxxxxxxxxxxxxxx'

    - platform: wiim_custom
      host: 192.168.0.yyyy
      name: Bedroom Wiim mini
      uuid: 'FF970016xxxxxxxxxxxxxxxxxxx'

    - platform: wiim_custom
      host: 192.168.0.zzz
      name: Living Wiim mini
      uuid: 'FF970016xxxxxxxxxxxxxxxxxxx'

 インテグレーションを読み込ませるために、HomeAssistantのデーモンを再起動します。
 そうすると、自動的にWiimのエンティティが出来上がります。
 ダッシュボードにメディアコントロールのカードを追加すると再生情報が表示されます。

 開発ツールからwiimのエンティティを眺めていると、属性にビット深度、サンプルレート、ビットレートがあります。これはなんとか表示したい!
 過去のアップデートで、エンティティカードに属性を表示させることができるようになったそうです。

www.home-assistant.io


 垂直スタックのカードを使って、メディアコントロールの下に属性のエンティティを表示させましょう。
 3台のWiimすべてに属性を表示させます。
 いきなり答えですが、こうなります。
 なぜかビット深度だけ、数字だけが表示されるので、suffix: bitを付けています。

type: vertical-stack
cards:
  - type: media-control
    entity: media_player.my_room_wiim_pro_2
  - type: entities
    entities:
      - type: attribute
        name: ビット深度
        entity: media_player.my_room_wiim_pro_2
        attribute: bit_depth
        suffix: bit
      - type: attribute
        name: サンプルレート
        entity: media_player.my_room_wiim_pro_2
        attribute: sample_rate
      - type: attribute
        name: ビットレート
        entity: media_player.my_room_wiim_pro_2
        attribute: bit_rate
  - type: media-control
    entity: media_player.living_wiim_mini
  - type: entities
    entities:
      - type: attribute
        name: ビット深度
        entity: media_player.living_wiim_mini
        attribute: bit_depth
        suffix: bit
      - type: attribute
        name: サンプルレート
        entity: media_player.living_wiim_mini
        attribute: sample_rate
      - type: attribute
        name: ビットレート
        entity: media_player.living_wiim_mini
        attribute: bit_rate
  - type: media-control
    entity: media_player.bedroom_wiim_mini
  - type: entities
    entities:
      - type: attribute
        name: ビット深度
        entity: media_player.bedroom_wiim_mini
        attribute: bit_depth
        suffix: bit
      - type: attribute
        name: サンプルレート
        entity: media_player.bedroom_wiim_mini
        attribute: sample_rate
      - type: attribute
        name: ビットレート
        entity: media_player.bedroom_wiim_mini
        attribute: bit_rate

こんな感じで表示されます。(1台だけ再生しています。)

DLNAサーバーのGerberaはこれらの属性情報をよこさないらしく、表示されません。

HomeAssistantのバージョンアップ

寝室の照明を消そうとHome AssistantのAndroidアプリを立ち上げても、ウントもスントも言わなくなりました。
試しにブラウザからアクセスしても、上記のとおり・・・。
Home Assistantが起動していないのかしら? と思いps -ef | grep homeaを実行すると、やはり起動しておらず。
Home Assitantを起動してみるも、そもそもpythonが見つからない模様。
pythonのアップデートによって、シンボリックリンクが切れたようです。
今までのpythonは3.9

gentoo /home/homeassistant/bin $ ls -al
合計 208
drwxr-xr-x 3 homeassistant homeassistant 4096  91  2021 .
drwxr-xr-x 8 homeassistant homeassistant 4096 1111 03:58 ..
(中略)
lrwxrwxrwx 1 homeassistant homeassistant    7 124  2017 python -> python3
lrwxrwxrwx 1 homeassistant homeassistant   38  91  2021 python3 -> /usr/lib/python-exec/python3.9/python3
lrwxrwxrwx 1 homeassistant homeassistant    7  91  2021 python3.9 -> python3

pythonのアップデートは、昔の記事に書いてました。

gentoolinux.hatenablog.com

シンボリックリンクが置き換えられました。

gentoo /home/homeassistant/bin $ ls -al
合計 209
drwxr-xr-x 3 homeassistant homeassistant 4096  91  2021 .
drwxr-xr-x 8 homeassistant homeassistant 4096 1111 03:58 ..
(中略)
lrwxrwxrwx 1 homeassistant homeassistant    7 124  2017 python -> python3
lrwxrwxrwx 1 homeassistant homeassistant   39 129 11:22 python3 -> /usr/lib/python-exec/python3.11/python3
lrwxrwxrwx 1 homeassistant homeassistant    7 129 11:22 python3.11 -> python3
lrwxrwxrwx 1 homeassistant homeassistant    7  91  2021 python3.9 -> python3

これでめでたくHome Assistantを起動できる!!
と思ったものの、やはりウントもスントも言わない。
これはHome Assistantそのものをバージョンアップするしかない。と思ったら、いろいろハマりました。

gentoo /home/homeassistant/.homeassistant # sudo -u homeassistant -H /home/homeassistant/bin/hass
2023-12-09 12:10:12.817 ERROR (MainThread) [homeassistant.components.switch] Error while setting up command_line platform for switch
Traceback (most recent call last):
  File "/home/homeassistant/lib/python3.11/site-packages/homeassistant/helpers/entity_platform.py", line 361, in _async_setup_platform
    await asyncio.shield(task)
  File "/home/homeassistant/lib/python3.11/site-packages/homeassistant/components/command_line/switch.py", line 42, in async_setup_platform
    entities: dict[str, Any] = {slugify(discovery_info[CONF_NAME]): discovery_info}
                                        ~~~~~~~~~~~~~~^^^^^^^^^^^
TypeError: 'NoneType' object is not subscriptable

どうやらコンフィグが古くて動かない模様。
しかし、いつかやらなければならないコンフィグの改修。重い腰を上げました。

まず、拙宅のリビング照明はRaspberry Pi Zero WにWebIOPiをインストールし、GPIOにリレーを3つ繋いでおります。
gentoolinux.hatenablog.com
gentoolinux.hatenablog.com

それと、寝室にESP-Easy化したSONOFFを導入しています。
gentoolinux.hatenablog.com

これらは、最初にswitch:というインテグレーターセクションの中に、platform:command_lineと定義して記載していました。
gentoolinux.hatenablog.com

curlコマンドを使い、WebIOPiやESP-EasyのWebを叩きに行く、ということです。
value_template: で、スイッチがオンの時の値を定義します。

しかし、2023.12.1バージョンでは、エンティティ毎に定義するようで、まず先頭にswitchではないようです。
おそらく様々なswitchを定義できるようにしたのでしょう。
新旧で見ていきます。

switch:
  - platform: command_line
    switches:
      snowball:
       command_on: "/usr/bin/curl -X POST http://192.168.0.xxx:8000/GPIO/26/value/0"
       command_off: "/usr/bin/curl -X POST http://192.168.0.xxx:8000/GPIO/26/value/1"
       command_state: "/usr/bin/curl -X GET http://192.168.0.xxx:8000/GPIO/26/value"
       value_template: '{{ value == "0" }}'
       friendly_name: Living SnowBall
      ph5:
       command_on: "/usr/bin/curl -X POST http://192.168.0.xxx:8000/GPIO/6/value/0"
       command_off: "/usr/bin/curl -X POST http://192.168.0.xxx:8000/GPIO/6/value/1"
       command_state: "/usr/bin/curl -X GET http://192.168.0.xxx:8000/GPIO/6/value"
       value_template: '{{ value == "0" }}'
       friendly_name: Living PH5 Plus
      spot:
       command_on: "/usr/bin/curl -X POST http://192.168.0.xxx:8000/GPIO/19/value/0"
       command_off: "/usr/bin/curl -X POST http://192.168.0.xxx:8000/GPIO/19/value/1"
       command_state: "/usr/bin/curl -X GET http://192.168.0.xxx:8000/GPIO/19/value"
       value_template: '{{ value == "0" }}'
       friendly_name: Living Spot and Bracket
      bedroom:
       command_on: "/usr/bin/curl -X GET http://192.168.0.yyy/control?cmd=event,PowerOn"
       command_off: "/usr/bin/curl -X GET http://192.168.0.yyy/control?cmd=event,PowerOff"
       command_state: "/usr/bin/curl -X GET http://192.168.0.yyy/control?cmd=status,gpio,12"
       value_template: '{{ value_json.state == 1 }}'
       friendly_name: Bedroom Light

旧書式ではswitch:の下に、platformでどういったプロトコルで処理するかを定義し、switches:で複数のスイッチを定義しました。
switches:の下には、任意にエンティティ名を付与しましたが、これは特にHome Assistantで使われることもなく、friendly_nameでが使われました。
そして、switch:インテグレーションが廃止され、command_line:が先頭に来ることになりました。
さらに、switches:も廃止。
friendly_name: もname:に変わりました。
そうしてできた新書式がコチラ

command_line:
    - switch:
       command_on: "/usr/bin/curl -X POST  http://192.168.0.xxx:8000/GPIO/26/value/0"
       command_off: "/usr/bin/curl -X POST  http://192.168.0.xxx:8000/GPIO/26/value/1"
       command_state: "/usr/bin/curl -X GET  http://192.168.0.xxx:8000/GPIO/26/value"
       value_template: '{{ value == "0" }}'
       name: Living SnowBall

    - switch:
       command_on: "/usr/bin/curl -X POST  http://192.168.0.xxx:8000/GPIO/6/value/0"
       command_off: "/usr/bin/curl -X POST  http://192.168.0.xxx:8000/GPIO/6/value/1"
       command_state: "/usr/bin/curl -X GET  http://192.168.0.xxx:8000/GPIO/6/value"
       value_template: '{{ value == "0" }}'
       name: Living PH5 Plus

    - switch:
       command_on: "/usr/bin/curl -X POST  http://192.168.0.xxx:8000/GPIO/19/value/0"
       command_off: "/usr/bin/curl -X POST  http://192.168.0.xxx:8000/GPIO/19/value/1"
       command_state: "/usr/bin/curl -X GET  http://192.168.0.xxx:8000/GPIO/19/value"
       value_template: '{{ value == "0" }}'
       name: Living Spot and Bracket

    - switch:
       command_on: "/usr/bin/curl -X GET http://192.168.0.yyy/control?cmd=event,PowerOn"
       command_off: "/usr/bin/curl -X GET http://192.168.0.yyy/control?cmd=event,PowerOff"
       command_state: "/usr/bin/curl -X GET http://192.168.0.yyy/control?cmd=status,gpio,12"
       value_template: '{{ value_json.state == 1 }}'
       name: Bedroom Light

 これでめでたく各々の照明をコントロールできるようになります。
 エンティティ名が変更になったようで、ダッシュボードにエンティティを再登録する必要があります。

 続いて、センサーです。
 ESP-Easyに温湿度センサーを繋ぎ、jsonで値を返しています。
gentoolinux.hatenablog.com


 書式は間違ってなさそうですが、コネクションタイムアウトします。

2023-12-09 16:08:51.036 WARNING (MainThread) [homeassistant.components.sensor] Platform rest not ready yet: All connection attempts failed; Retrying in background in 30 seconds
2023-12-09 16:09:21.052 ERROR (MainThread) [homeassistant.components.rest.data] Error fetching data: http://192.168.0.zzz/json?tasknr=1 failed with All connection attempts failed

 ブラウザから見るとちゃんとjsonを返します。
 そこで考えられるのが、Home Assistantが同一URLを2つ同時にリクエストし、ESP-Easyが処理落ちしているのではないかということ。
 旧書式はこうでした。

sensor:
  - platform: rest
    resource: http://192.168.0.zzz/json?tasknr=1
    name: Living Temp
    value_template: "{{value_json.temperature}}"
    unit_of_measurement: "C"

  - platform: rest
    resource: http://192.168.0.zzz/json?tasknr=1
    name: Living Hum
    value_template: "{{value_json.humidity}}"
    unit_of_measurement: "%"

 ごくまれに、温度か湿度のどちらかだけ表示されることがあり、変だな?と、思っていたので、ここが怪しいです。
 RestAPIを使い、1つのリソースで2つ以上の値を取得するには、senser:ではなく、rest:インテグレーターで開始し、resourceを一つだけ書く、という風にすればよいようです。
 新書式はこうなります。

rest:
   resource: http://192.168.0.zzz/json?tasknr=1
   sensor:
    - name: Living Temp
      value_template: "{{value_json.temperature}}"
      device_class: temperature
      unit_of_measurement: "°C"
    - name: Living Hum
      value_template: "{{value_json.humidity}}"
      device_class: humidity
      unit_of_measurement: "%"

 だいぶスッキリしましたね。

 あと、フロントエンドで「国が設定されていない」と、設定画面に誘導されるのですが、設定画面では「設定がconfiguration.yamlに保存されているため、エディタが無効になっています。」と表示され、一切の編集や保存ができません。
 これはconfiguration.yamlの先頭にいろいろと定義を入れているからだそうです。
 どんどんコメントアウトします。

 旧書式

homeassistant:
  # Name of the location where Home Assistant is running
  name: Sample_home
  # Location required to calculate the time the sun rises and sets
  latitude: 42.0000
  longitude: 141.0000
  # Impacts weather/sunrise data (altitude above sea level in meters)
  elevation: 20
  # metric for Metric, imperial for Imperial
  unit_system: metric
  # Pick yours from here: http://en.wikipedia.org/wiki/List_of_tz_database_time_zones
  time_zone: Asia/Tokyo
  # Customization file
  customize: !include customize.yaml

 新書式

homeassistant:
  # Name of the location where Home Assistant is running
  #name: Sample_home
  # Location required to calculate the time the sun rises and sets
  #latitude: 42.0000
  #longitude: 141.0000
  # Impacts weather/sunrise data (altitude above sea level in meters)
  #elevation: 20
  # metric for Metric, imperial for Imperial
  #unit_system: metric
  # Pick yours from here: http://en.wikipedia.org/wiki/List_of_tz_database_time_zones
  #time_zone: Asia/Tokyo
  # Customization file
  #customize: !include customize.yaml

 その代わり、Home Assistantの名称なども、イチから設定しなおしになります。
 ブラウザからフロントエンドにアクセスして設定するだけなので簡単です。

 さ、これでHome Assistantをデーモンとして起動しよう!
 と、思ったら、起動しない。
 デーモンではなく、通常のプロセスとしては起動するのです。
 どうやら、--daemon や --pid-fileなどのオプションが廃止されたようです。
 拙宅ではHome AssistantをGentooで動かしています。なので、Gentooのお作法に則って/etc/local.d/homeassistant.startに起動処理を、/etc/local.d/homeassistant.stopに停止処理を記載しています。
 単なるバックグラウンド起動は&を付ければよいのですが、pid番号をファイルに記載しておかなければ、停止時にpidがわかりません。
 そこで、start-stop-daemonから起動してもらうことにしました。

 旧/etc/local.d/homeassistant.start

#!/bin/sh

sudo -u homeassistant -H /home/homeassistant/bin/hass --pid-file /home/homeassistant/hass.pid --log-file /var/log/home-assistant.log 2>&1
echo "Homeassistant Started"

 新/etc/local.d/homeassistant.start

#!/bin/sh

USER=homeassistant
DAEMON=/home/homeassistant/bin/hass
PIDFILE=/home/homeassistant/hass.pid
start-stop-daemon --start --quiet --chuid $USER --user $USER --background --pidfile $PIDFILE --make-pidfile --exec $DAEMON

echo "Homeassistant Started"

 本当は、リターンコードを判断してif文で起動失敗時にはその旨メッセージを出す必要があるのでしょうが、面倒なのでヤメます。

 これでめでたくHome Assistantで照明のスイッチをパチパチできます。

 あと、特に不便は感じてませんが、フロントエンドから各スイッチやセンサーのエンティティを編集しようとしても、読み取り専用となっており、「このエンティティ ('switch.bedroom_light') にはユニークなIDがないため、UIから設定を管理構成できません。詳しくは、ドキュメント を参照してください。」と出てきます。ユニークなIDですか・・・。

RTX1200でDS-LiteとPPPoEデュアル接続時のL2TP/IPsec

先日、RTX1200を弟宅に設置したと書きました。

gentoolinux.hatenablog.com

いつかDS-LiteのAFTRアドレスが変更になって、「なんかネット繋がらなくなったぞ!」と、言われたときの保守用に、札幌ー帯広間をNGN網内でのIPSec接続をしています。
札幌のRTX810と帯広のRTX1200のIPv6アドレスを、「OPEN IPv6 ダイナミック DNS for フレッツ・光ネクスト」を使ってDNS登録し、接続相手先をFQDN指定して、IPv6アドレスが変更されても接続できるようにしています。

しかし、念には念を入れて、IPv4L2TP/IPsecでも接続できるようにします。
が、またもここでもハマりました。

まず、RTX1200ではDHCPスコープを192.168.1.20-192.168.1.100/24に設定しており、フィルタールーティングを使い、

ip route default gateway tunnel 1 gateway pp 1 filter 200200
ip filter 200200 pass 192.168.1.101-192.168.1.102 * * * *

として、192.168.1.101と102はPPPoEへ、それ以外(DHCPクライアント等)はDS-Liteを設定したtunnel1へ、ルーティングするようにしました。
L2TP/IPsecで接続してきた端末には192.168.1.101を付与し、PPPoEで通信させる、という意図です。

L2TP/IPsecの設定はYAMAHAサイトの設定(アドレス不定)の通りにします。

network.yamaha.com

もちろん、IPアドレスが変わっても良いように、netvolante-dnsの設定をpp1に入れておきます。

Windows10マシンから自宅のRTX810にアクセスするVPN設定を複製し、帯広のRTX1200にアクセスできるように、接続先アドレス、ユーザー名、事前共有キーを書き換えます。
RTX1200をsyslog debug onにして、札幌からアクセスしてみると、接続できずにタイムアウトします。
RTX810に接続できたログと比較すると、IKEのSA交換で失敗し、次のようなログが吐かれていました。

[IKE] [8] retransmission timeout

これを元にして調べると、なんと、ごちゃんねるに解決策が!!
DS-LiteとPPPoEで接続し、PPPoE側でL2TP/IPsecが接続できずに困っている」って、まるで俺か⁉︎
どうやら、RTX1200がSAキーを返そうとしているが、Windows側が応答していないとのことで、その理由は、Windows→RTX1200はPPPoEを経由しているのに対し、RTX1200→WindowsDS-Liteを経由しているから、ということのようです。
IPv4グローバルアドレスが与えられてFirewallがない丸裸のWindowsだったら応答していたでしょうが、札幌側のWindowsはRTX810のNATの内側にいるので、RTX810にはじかれてWindows側に届いていないのです。
そこで、フィルタールーティングの対象IPアドレスにRTX1200のアドレス192.168.1.1も含めるようにします。
具体的には、

ip route default gateway tunnel 1 gateway pp 1 filter 200200
ip filter 200200 pass 192.168.1.1,192.168.1.101-192.168.1.102 * * * *

YAMAHAルーターの良いところはIPアドレスを「,」で区切ったり「-」で範囲指定するなど、複数のIPアドレスを一度に指定できるところですね。
RTX1200をPPPoE経由でインターネットに出れるように設定したところ、L2TP/IPsecで接続できるようになりました。
もちろん、Mac OSからもL2TP/IPsecできています。

まさかごちゃんねるに助けられるとは・・・。

Windows10 64bitでWaveDAT

高校時代からDAT (Digital Audio Tape)を使っていたため、結構な量のDATテープを持っています。
15年以上前にAdaptec AHA-29160NのOEM(Compaq 64bit/66MHz Wide Ultra3 SCSI)とSDT-9000のOEMを中古購入してDAT用ファームウェアを入れ、シェアウェアソフトのWaveDATを使ってWaveファイルとして取り込んでいました。

時は経ち、SCSIが廃れるのはおろか、32bitPCIバスもほとんどなくなってしまいました。
2年ほど前に入れ替えたマザーボードはBIOSTARのB550GTA。これは近年のAM4マザーボードで唯一32bitPCIバスが装備されていたのです。
しかし先週、AM5の移行をきっかけにPCIバスがないマザーボードを購入。
というのも、PCIe-PCI変換ボードで何とかなるのでは?という算段をしたのです。
ありがたいことにPCIe-PCI変換ボードは数種類販売されていることと、私のデスクトップパソコンのケースが巨大なFractal Design製Define XL R2のため、PCIスロットが9本もある!!
変換ボードを背負ったPCIカードを最下段のスロットに設置しても、フルATXサイズのマザーボード(PCIスロット7本分)が入るのではないか、と踏んだのです。

買った変換ボードはコレ
SD-PECPCiRi3
www.area-powers.jp

ヨドバシカメラのレビューには「電源を接続したら煙を吹いた」とありますが、どうやらPCIバスを前後逆刺ししたようで、今のバージョンはわざわざ「逆に刺すと発煙し故障する場合があります」と、刺す方向を示しながらも注意書きが書かれたシールがボードに貼りついていました。

設置したのがコチラ

ギリギリATXマザーボードに干渉しない位置です。
AHA-29160Nの直上にシリアルポートを持ってくるとケーブルに干渉するため、シリアルポートは一つ上にずらしました。
シリアルポートをマザーボードのピンヘッダに接続するフラットケーブルが邪魔で写真写りが悪いです。

AHA-29160NはWindows7/2008 Server x64用のドライバがあるため、Windows10 64bitでも動作します。
(もしかしたら、認識させるためにadpu160m.infの中身を書き換える必要があるかもしれませんが、かなり前に認識させたので覚えていません・・・。)
何事もなくAHA-29160N(Compaq 64bit/66MHz Wide Ultra3 SCSI)が認識され、SDT-9000も認識されました。

WaveDATを起動すると、「テープドライブが見つかりません。」と出ますが、これは管理者権限で実行していないから。
一度終了し、「管理者権限で実行」すると、ちゃんとSDT-9000を認識します。
プロパティの「互換性」で「管理者としてこのプログラムを実行する」にチェックを入れておくと、次からは普通に実行するだけでUACの画面が出て「はい」を押すだけでSDT-9000を認識して起動します。

適当なDATテープを入れてPlayボタンを押してみました。

ちゃんと再生され、Windowsオーディオデバイスから音も出ました。

これでしばらくはDAT資産は再生できます。