RaspberryPi zero2 Wにhomebridgeを入れて起動するまで

RaspberryPi zero2 Wにhomebridgeを入れて起動するまで

ことはじめ

スマートホーム沼に興味を持ち始めているのですが、homebridgeを使うと色々遊べるらしいというのを知りました。homebridgeはnodeのツールなのですが、raspberry piなどのマイコンで動かすのがよくあるケースとのことです。たまたまRaspberry pi zero2 wを購入できたので、初期セットアップからhomebridge起動までの記録を残しておこうという次第です。

購入したもの

Raspberry pi zero2 w スターターキット

本体のみ購入が売り切れている中、若干お値段が張りますがこちらは残っていたので購入できました。

前提

MicroSDへのOS書き込み」といったセットアップにはMacを利用します。

目指すところ(動くまでにやったこと概要)

  • Rasbian OS liteをMicroSDに書き込む
    • Raspberry Pi Imagerを利用します
    • wifi, sshの情報もOS書き込み時に一緒に含められます
  • Raspberry Pi上にdocker-composeをインストール
    • aptで入ります(ちょっと古いバージョンっぽい感じがしますが)
    • dockerも一緒に入ります
  • homebridgeの起動
    • docker-composeを使います

Raspbian OS lite(64-bit)をMicro SDに書き込む

スターターキット付属しているMicroSDカードにはすでにRaspbian OSが書き込まれているのですが、Desktop環境は不要なのでlite版をインストールします。

インストールにはRaspberry Pi Imager v1.7.2を利用しました。

wifi, sshの設定をする

右下の歯車をクリックするとwifisshのセットアップまで可能になっています。

入力フォームに従ってぽちぽち入力すれば良いです。

Raspberry Pissh接続する

電源を入れます。しばらく待つとsshで接続できるようになります。

# 公開鍵なりのセットアップはしてあるものとします
# ユーザー名とホスト名が初期設定のままであれば、↓でログインできます。
ssh pi@raspberrypi.local

docker、docker-composeをインストール

↓を叩きます。docker-composeをインストールするとdockerも一緒に入ってくれます。

sudo apt update

# vimはdocker-compose.ymlの記述で使いました。必要なければ不要です。
sudo apt install docker-compose vim

実行後↓を叩いて動けば大丈夫です。

sudo docker ps

homebridgeを起動する

docker-compose.ymlをどこか任意のディレクトリに置きます。

restart: alwaysをセットしているのでRaspberry Pi起動時に自動で立ち上がるようにしました。

version: '3'
services:
  homebridge:
    image: oznu/homebridge:ubuntu
    container_name: homebridge
    restart: always
    network_mode: host
    environment:
      - HOMEBRIDGE_CONFIG_UI=1
      - HOMEBRIDGE_CONFIG_UI_PORT=8080
    volumes:
      - ./homebridge:/homebridge

↓で起動します。

sudo docker-compose up -d

無事起動できていれば、http://raspberrypi.local:8080/にアクセスするとhomebridgeの初期設定画面が開きます。

JSONForms AntDesign Vueを書いている

JSON Formsって何よ

JSON Schemaを食べさせるとフォームを作ってくれる便利なライブラリ。

jsonforms.io

アーキテクチャのページを見るとわかるが、

  • core
  • binding
  • renderer

の3層構造になっている。

そのためReactやVueにべったりした作りにはなっておらず、JSONForms側で用意がないUIフレームワークの場合でも自前でrendererを書くことでJSONFormsが利用可能になる。

そこで、今回Ant Design VueというVue向けのUIフレームワークJSON Formsに対応させるrendererを作っている。

このフレームワークはプロダクトでも使っていて使いやすく、個人開発にでも使うかと思い、折角なので作ってみようと思った次第となっている。

※ ちなみにAnt Designはアリババが作っているUIフレームワークreact向けの実装もある

作れるフォームも結構柔軟で、json schemaのoneofなどにも対応していることでスキーマ定義をするだけでタブ切り替えだったり、動的にinputのリストを増減なんかもできる。そのため、ある程度複雑なフォームにも対応が可能になっている。

この辺はexamplesを見ると結構面白い。

https://jsonforms.io/examples/combinators

Dockerのイメージビルド時に環境変数が未定義の場合にエラーを出したい

まとめ

Dockerfile側ではエラーを出すようなENVの定義方法がなさそうで、docker image buildを叩いている場合は厳しそうに見える。

ワークアラウンドではあるものの、イメージビルドにdocker-composeを利用している場合、envプロパティを変数展開する際にエラーにできる。

docker-compose.yml の定義方法

services:
    some-service:
        image: xxxxx
        environment:
          - FOO=${FOO:?}    

とする。

variable substitutionの箇所に記載がある。 Compose file version 3 reference | Docker Documentation

Similarly, the following syntax allows you to specify mandatory variables:

${VARIABLE:?err} exits with an error message containing err if VARIABLE is unset or empty in the environment. ${VARIABLE?err} exits with an error message containing err if VARIABLE is unset in the environment.

Dockerfileではできないの

以下はFOOが未定義でもビルドが通ってしまう。

FROM xxxxx

ENV FOO=${FOO:?}

参考

kdnakt.hatenablog.com

pnpmをgithub actionsで使う

今更ながらGithub Actionsを触り始めたものの、pnpmを使ってコードを書いておりいきなりハマったのでメモ。

結論

2021/12/24追記 いまは公式のactionsではなく、nodejsをセットアップするactionsを呼びつつ corepackを使ってpnpmをインストールしている

成果物はこれ

いくつかやり方がありそうだが、結論としてはpnpm公式のドキュメント記載の方法で記述するのが良かった。 他の選択肢に比べて、記述が明示的でGithubActionsに慣れていない身としては良かった。 pnpm.io

その他の方法

Githubで検索するといくつか候補が出てくる。 github.com

例えば、pnpmリポジトリで管理されているgithub action

github.com

これは、コマンドの実行方法がよくわからず、スキップ。

他にも個人開発っぽいものがいくつかあるが、よくわからないエラーが出たりしたのでスキップ。

vscode+volarでvue3のref sugerを使う際に型定義のimportでエラーが出る問題の解消

問題

script setup(ref suger)を使っている際、特に何も考えずimportすると、以下のようなエラーが出てしまう。

only refers to a type, but is being used as a value here.ts(2693)

only refers to a type, but is being used as a value here.ts(2693)

解消法

-   import { ControlElement } from '@jsonforms/core';
+  import type { ControlElement } from '@jsonforms/core';

参考 github.com

import typeとすると良いらしい(こんな構文があるのか)

type-only-imports-and-exportというTS 3.8で入った構文らしい。

TypeScript: Documentation - TypeScript 3.8

vue3+vite+storybook+pnpmでハマったことメモ

StorybookがViteに対応していたので、試した際にハマったことなど。

storybook.js.org

pnpm起因の問題は使っている人には参考になるかもしれない。

pnpx sb initが動かない

Storybook for Viteに↓の記述があるので、pnpxとバージョンを読み替えて

npx sb@next init --builder storybook-builder-vite

↓を実行した。

npx sb init --builder storybook-builder-vite

結果動かず、Invalid Packageと怒られてしまう。 (EINVALIDPACKAGENAMEと表示されていた)

実行時 - pnpm : 6.13.0 - npm: 7.20.3 - node: v16.7.0

入れようとしたstorybookのバージョン - @storybook/vue3": 6.3.7

やったこと

↓を叩く

pnpx sb init -s
pnpm install

// storybookの起動に必要
pnpm add -D @storybook/cli
panpm install

-s オプションを付与することで、package.jsonの変更は入るが、パッケージのインストールは実行されない。

pnpx を使った際の動作が内部的にどうなってるのか把握できてないが、pnpm側で実行させることで回避している(?). (内部的にはnpmを使ってたりするのか...?)

参考

github.com

github.com

Storybook 起動時に "Singleton client API not yet initialized" と怒られる

↓のIssueを参考に.storybook/main.jsに変更を加えた。

module.exports = {
+  async viteFinal(config) {
+    // suppress storybook error
+    // https://github.com/storybookjs/storybook/issues/10887#issuecomment-901109891
+    config.resolve.dedupe = ['@storybook/client-api'];
+    return config;
+  },
  stories: ['../src/**/*.stories.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'],
  addons: ['@storybook/addon-links', '@storybook/addon-essentials'],
  core: {
    builder: 'storybook-builder-vite',
  },
};

github.com

Vue3のプラグインの読み込み方

Vue2までは↓のようにすれば良かったが、Vue3ではcreateAppで作成したオブジェクトに対してプラグインを読み込ませる必要がある。

import Vue from 'vue';

Vue.use(xxxxPlugin);

やったこと

以下のようにする

import { app } from '@storybook/vue3';
import { Button } from 'ant-design-vue';
app.use(Button);

export const parameters = {
  actions: { argTypesRegex: '^on[A-Z].*' },
  controls: {
    matchers: {
      color: /(background|color)$/i,
      date: /Date$/,
    },
  },
};

コンポーネントがstoryでレンダリングされない

consoleを見ると以下のようなメッセージが出ていたので、

      [Vue warn]: Component provided template option but runtime compilation is not supported in this build of Vue.
      Configure your bundler to alias "vue" to "vue/dist/vue.esm-bundler.js".

.storybook/main.jsのconfigにaliasを貼った。

async viteFinal(config) {
    config.resolve.alias = {
      /*
      fix this issue
      [Vue warn]: Component provided template option but runtime compilation is not supported in this build of Vue.
      Configure your bundler to alias "vue" to "vue/dist/vue.esm-bundler.js".
      * */
      vue: 'vue/dist/vue.esm-bundler.js',
      '@': path.resolve(__dirname, '/src'),
    };

Vue3+TypeScriptプロジェクトでEslintをお手軽に設定するメモ(@antfu/eslint-config-vueについて)

tldr

devDependenciesに以下のパッケージを追加する

yarn add -D eslint @antfu/eslint-config-vue

.eslintrcは以下のようにextendsする

{
  "extends": [
    "@antfu/eslint-config-vue",
  ]
}

以上、楽ちん。

詳細

@antfuというVue本体のコミッターが@antfu/eslint-config-vueというパッケージをメンテナンスしている。 eslintのプリセットだが、設定をモノレポで管理しつつ、レイヤーになっていて、賢い管理の仕方だな思ったのでメモ。

モノレポの構造

モノレポでは↓のパッケージが管理されている。

  • @antfu/eslint-config-basic(eslintの共通設定)
  • @antfu/eslint-config-ts(ts用の設定)
  • @antfu/eslint-config-vue(vue用の設定)
  • @antfu/eslint-config-react(react用の設定)
  • @antfu/eslint-config(全部入り)

これらのパッケージは, それぞれ依存関係があり、以下のようになっている。

@antfu/eslint-config-ts

@antfu/eslint-config-ts
  -> @antfu/eslint-config-basic

@antfu/eslint-config-vue

@antfu/eslint-config-vue
  -> @antfu/eslint-config-ts
    -> @antfu/eslint-config-basic

@antfu/eslint-config-react

@antfu/eslint-config-react
  -> @antfu/eslint-config-ts
    -> @antfu/eslint-config-basic

@antfu/eslint-config

@antfu/eslint-config
  -> @antfu/eslint-config-react
    -> @antfu/eslint-config-ts
      -> @antfu/eslint-config-basic
  -> @antfu/eslint-config-vue
    -> @antfu/eslint-config-ts
      -> @antfu/eslint-config-basic

@antfu/eslint-config-basicは共通設定として、他のすべてのパッケージから読み込まれている。
@antfu/eslint-config-tsはbasicを継承しつつ、TypeScript用のeslint設定が加えられている。
@antfu/eslint-config-vueはbasic, tsを継承しつつ、Vue用のeslint設定が加えられている。

...というように、@antfu/eslint-config-react, @antfu/eslint-configも同様に継承関係が作られている。

メリット

継承にはなるが、パッケージの組み合わせでeslintの設定管理ができる。

なので、例えばここに「svelteのプロジェクトを増やしたい」となったら、basicとts用の設定を継承したsvelte用パッケージを追加するだけで済む。

複数プロジェクトでESLIntの設定を共通化したいときに、レイヤーかは管理方法のひとつのアイデアとして良いかもしれない。

使い方

Vueのプロジェクトであれば、以下のパッケージを追加し、eslintrcなどでextendsに@antfu/eslint-config-vueを指定すれば良い。

  • eslint
  • @antfu/eslint-config-vue

※eslintはdependenciesに含まれていないので、プロジェクト側で明示的にインストールする必要がある。

余談

@antfu/eslint-config-vueについては、実際にVitesseという、ViteベースのVueアプリケーションのスケルトンで使われている。(Vitesseも@antfuが作ったものという) github.com

注意点

eslint-config-basicに色々なeslintのルールが設定済みなので、プロジェクトや人によってはそのまま使うと合う合わないがありそう。