View on GitHub

esphome-sesame_server

CANDY HOUSE SESAME 5のふりをしてSESAME Touch、CANDY HOUSE Remote、オープンセンサーを押しボタンとして使用するためのESPHome用コンポーネント

esphome-sesame_server

SESAME 5のふりをして、SESAME TouchCANDY HOUSE RemoteオープンセンサーSESAMEフェイス等を押しボタン(トリガー)として使用するためのESPHome用コンポーネント

graph LR

subgraph ESPHome
  server[esphome-sesame_server]
end

t[SESAME Touch/PRO<br/>SESAME Face/PRO] -->|lock/unlock| server
o[Open Sensor] -->|open/close| server
r[CANDY HOUSE Remote/nano] -->|lock/unlock| server

[!注意] このコンポーネントはESPHomeに組込みのBluetooth機能をつかっていません。 そのため、ESPHome用の他のBluetooth関連コンポーネントと同居することはできません。 両方を使いたい場合には別のESP32モジュールに搭載してください。

対応機種

必要なもの

UUIDからBLEアドレスの導出方法

UUIDからBLEアドレスを算出する方法はAPI文書に公開されています。

サーバーで使用するUUIDとBLEアドレスの組を指定する場合は、まず適当なツールでUUIDを生成し上記のアルゴリズムでBLEアドレスを算出して使用します。以下に Google Geminiに生成してもらったpythonスクリプトを置きますので参考にしてください。

from Crypto.Hash import CMAC
from Crypto.Cipher import AES
import binascii

def generate_aes_cmac_modified(uuid_str):
    """
    128bit UUIDを鍵とし、AES CMACで文字列"candy"の認証コードを算出する。
    その先頭6バイトを取得し、バイト順を反転させ、
    反転後の先頭バイトに0xc0をOR演算し、16進数文字列で出力する。

    Args:
        uuid_str (str): 128bit UUIDの文字列。ハイフンを含んでいても良い。

    Returns:
        str: 認証コードの加工後の先頭6バイトの16進数文字列。
    """
    # UUID文字列からハイフンを除去し、16進数として解釈してバイト列に変換
    uuid_bytes = binascii.unhexlify(uuid_str.replace('-', ''))

    # CMACオブジェクトを作成
    cobj = CMAC.new(uuid_bytes, ciphermod=AES)

    # 認証対象のデータ
    data = b"candy"

    # データを更新(ハッシュ計算)
    cobj.update(data)

    # 認証コード(MAC)を取得
    mac = cobj.digest()

    # 先頭6バイトを抽出
    first_6_bytes = mac[:6]

    # バイト順を反転
    reversed_bytes = first_6_bytes[::-1] # スライスでバイト列を反転

    # 反転後の先頭バイトに0xc0をOR演算
    # バイト列はイミュータブルなので、リストに変換して操作し、再度バイト列に戻す
    modified_bytes_list = list(reversed_bytes)
    modified_bytes_list[0] = modified_bytes_list[0] | 0xc0
    final_bytes = bytes(modified_bytes_list)

    # 16進数文字列で出力
    return binascii.hexlify(final_bytes).decode('utf-8')

if __name__ == "__main__":
    # 例として128bit UUIDを用意
    test_uuid = "2d39e028-a1ed-48b8-809b-1799d1018ec1"

    auth_code_modified = generate_aes_cmac_modified(test_uuid)
    print(f"入力UUID: {test_uuid}")
    print(f"加工後の認証コード (先頭6バイト): {auth_code_modified}")

ESPHomeへの本コンポーネントの導入

本コンポーネントをESP32にインストールするにはESPHomeのExternal Componentとして導入します。以下がYAMLファイルの例です。またサンプルファイルも参考にしてください。

esphome:
  name: sesame-server-1
  friendly_name: SesameServer1
  platformio_options:
    build_flags:
    - -std=gnu++17 -Wall -Wextra
    - -DMBEDTLS_DEPRECATED_REMOVED
# Configure the maximum number of connections as required (maximum: 9)
    - -DCONFIG_BT_NIMBLE_MAX_CONNECTIONS=6
    - -DCONFIG_NIMBLE_CPP_LOG_LEVEL=0
    build_unflags:
    - -std=gnu++11
external_components:
- source:
    type: git
    url: https://github.com/homy-newfs8/esphome-sesame_server
    ref: v0.3.0
  components: [ sesame_server ]
# - source: '../esphome/esphome/components2'
#   components: [ sesame_server ]

esp32:
  board: esp32-s3-devkitc-1
  framework:
    type: arduino

ポイントはexternal_componentsセクションとplatformio_optionsセクションです。external_componentsには本モジュールのURLを、platformio_optionsセクションには本モジュールをコンパイルするために必要な追加の設定を記述します。

同時に多数のSESAME TouchやRemoteと接続する場合には、CONFIG_BT_NIMBLE_MAX_CONNECTIONSの値を調整してください(最大9: 増やすほどメモリ使用量が増加します)。Open sensorやRemote nanoは操作した時にしか接続してこないので、同時接続数はある程度小さくても問題ないかもしれません。

esp32セクションはインストール先のESP32モジュールに応じて指定します。本コンポーネントではframeworkとしてarduinoを指定する必要があります。

本機のUUID、Bluetooth Addressの設定

UUIDとアドレスは前述した条件を満たすものを使用してください。

sesame_server:
  id: sesame_server_1
  address: "01:02:03:04:05:06"
  uuid: "12345678-1234-1234-1234-123456789abc"

addressuuidは上記のように直接書くか、ESPHomeの他のコンポーネントの設定と同様に別ファイルを参照させることも可能です(example.yaml参照)。

idは必須ではありませんが、ESPHomeのイベントハンドラから本コンポーネントをターゲットとして呼び出すときに必要です(後述)。C++言語の識別子として適切な文字列を指定します。

sesame_server設定変数

接続するデバイスの指定

本機が受け付けるSESAME TouchやRemoteのAddressを指定します。指定されていない機器からの接続も(認証が通る分には)許容しますが、ボタンを押してもイベントを発生させません。

sesame_server:
  id: sesame_server_1
    
  triggers:
  - name: Sesame Touch 1
    id: touch_1
    address: !secret touch_1_address
    history_tag:
      id: touch_1_tag
      name: "Sesame_Touch_tag"
    trigger_type:
      id: touch_1_trigger_type
      name: "Sesame_Touch_trigger_type"
    on_event:
      then:
        - lambda: |-
            ESP_LOGD("example", "Event '%s'/'%s' triggered", event_type.c_str(), id(touch_1).get_history_tag().c_str());
  - name: Remote 1
    address: !secret remote_address
    on_event:
      then:
        - lambda: |-
            ESP_LOGD("example", "Event '%s' triggered", event_type.c_str());

triggersセクションには複数のデバイスをリストで指定します。デバイスひとつひとつはEventであり、それぞれにイベント受信時の処理(on_event)やHome Assistantで表示されるアイコン等を指定することができます。

history_tagText Sensorで、SESAME Touch等が通知してくるタグ値をHome Assistantに通知します。SESAME Touch系では指紋やカードにつけた名前が通知されるため、それらに応じて処理を分岐させることが可能です。同じ値はLambda内からはtriggerコンポーネントのstd::string get_history_tag()で参照可能です。

trigger_typeSensorで、SESAME Touch等が通知してくるタイプ値をHome Assistantに通知します。この値についての詳細はesphome-sesame3のREADMEに記載してあります。センサー値はESPHomeの仕様上はfloat値です。trigger_typeを含まない命令を受信した場合はNaNになります。Lambda内ではtriggerコンポーネントのfloat get_trigger_type()で参照可能です。

trigger設定変数

利用デバイスのAddressを調べる

上記のtriggersを指定していない場合でも本機へのコマンド送信が行なわれた場合にはログに接続元のAddressが出力されます。以下は12:32:56:78:90:abからunlockコマンドを受信した場合の出力例です:

[15:52:36][W][sesame_server:050]: 12:34:56:78:90:ab: cmd=unlock(83), tag="Remote" received from unlisted device

また、esphome-sesame3に含まれるsesame_bleを使うと、近くにあるSESAME TouchやCANDY HOUSE RemoteのAddressを調べることが可能です。

使用方法

  1. ESPHomeを設定、コンパイルし起動する(triggers以下は未設定でも良い)。未登録デバイスとして動作を開始する。
  2. スマホアプリを起動し⊕を押すと本機が未登録SESAME 5として見えるので登録を実行する(適宜名称を変更すると良い)。
  3. 本機へのイベントトリガーとして使用するRemote / Remote nano / Touch / Touch PRO / Open Sensorの設定画面を開き、「セサミを追加…」から前記で登録した本機を追加する(Remote nano / Open Sensorは要リセット)。
  4. イベントトリガーのRemote等を操作すると本機へのコマンド送信が行なわれるのでログで確認する
  5. ログに記載されているAddressを使ってtriggersセクションを設定する

イベントハンドラで利用可能な情報

graph LR

subgraph ESPHome
  direction TB
  server[esphome-sesame_server]
  evh[Event Handler]
end

server -->|event_type| evh
evh --> |"get_history_tag()"| server
t[SESAME Touch/PRO] -->|lock/unlock| server
o[Open Sensor] -->|open/close| server
r[CANDY HOUSE Remote/nano] -->|lock/unlock| server

イベントハンドラ(on_event)内では以下の情報を利用可能です。

記述方法はexample.yamlを参考にしてください。

RemoteでLチカ

sesame_server:
  id: sesame_server_1
  address: !secret sesame_server_my_address
  uuid: !secret sesame_server_my_uuid
  triggers:
  - name: Remote 1
    address: !secret remote_address
    on_event:
      then:
        - lambda: |-
            if (event_type == "lock") {
              id(led_1).turn_on();
            } else {
              id(led_1).turn_off();
            }

output:
  - platform: gpio
    id: led_1
    pin: GPIO21
    id: gpio_d1

上記コードはGPIO21番ピンにLEDが接続されていることを想定しています(Seeed XIAO ESP32S3)。

Home Assistantとの連携

graph LR

subgraph ESPHome
  direction TB
  server[esphome-sesame_server]
end

subgraph HASS[Home Assistant]
  Automation
end

t[SESAME Touch/PRO] -->|lock/unlock| server
o[Open Sensor] -->|open/close| server
r[CANDY HOUSE Remote/nano] -->|lock/unlock| server
server -->|event| Automation

Home Assistantではボタンが押されたことはエンティティの状態変更として通知されます。オートメーションの編集画面からトリガー追加 > エンティティ > 状態 と選択しトリガーの編集画面を開きます。

トリガー編集画面では ESPHome で定義したエンティティを選択し、属性として「イベントタイプ」、変化後の属性値に検知したいイベントタイプ(lock/unlock/open/close)を指定します。

デュアルロール利用

本コンポーネントとesphome-sesame3を使うと、一台のESP32でSESAMEへ命令を発行するクライアント機能と、Remote等のイベントをトリガーとして受信するサーバー機能の両方を共存させることが可能です。

graph LR

subgraph ESPHome
  direction TB
  server[esphome-sesame_server]
  client[esphome-sesame3]
end


t[SESAME Touch/PRO] -->|lock/unlock| server
o[Open Sensor] -->|open/close| server
r[CANDY HOUSE Remote/nano] -->|lock/unlock| server
server --> |automation| client
client --> s5[SESAME 3/4/5/PRO]
client --> bot2[SESAME bot/2]
client --> bike[SESAME bike]

Touchに特定の名前を付けたカードがかざされた時に別のセンサーのデータを確認してからSESAMEを開錠する、といったことが1台のESP32で(クラウドに頼らずに)実現可能になります。dual-role.yamlを参照してください。

デュアルロール時の注意点

デュアルロール構成にすると、SESAME Touchからのトリガーを待ち受けつつ、そのSESAME Touchのバッテリー残量を監視するといったことも可能になります。ただし、SESAME Touchが(トリガー通知のために)本機(ESP32)に接続してきている状態においては、バッテリー残量を問合わせるために本機からそのSESAME Touchへ接続することができません。そこで、バッテリー残量を取得する際にはサーバー側の接続を一旦切断してから問合せを実行します。問合わせ実行中(数秒程度)はサーバーのアドバタイジングも停止するためSESAME Touchからのトリガーを受けることができません。問合わせが完了すればトリガーの受付けが可能になります。

そのようにクライアント側接続は一時的なものとする必要があるため、always_connect属性はFalseに設定する必要があります。バッテリー監視を低頻度にするためupdate_intervalを長期間に設定することをお薦めします。また、SESAME TOuch等の使用がない時間に問合わせたいならば、update_intervalneverに設定し、timeコンポーネントのon_timeイベントでバッテリー残量の問合わせを実行するのも良いでしょう。dual-role.yamlに例があります。

未登録状態へのリセット

sesame_server.reset()を呼び出すことで未登録状態にすることが可能です。example.yamlにHome Assistant上に表示され、クリックすると初期化する疑似ボタンコンポーネントの定義をしてあります。参考にしてください。

button:
- platform: template
  name: "Reset Sesame Server"
  on_press:
  - lambda: |-
      id(sesame_server_1).reset();