JRRF2026 平和島ビットコイン産業 MakerChipをつかったライトニングクレーンゲーム 備忘録・MakerChipタッチクライアント編

JRRF2026 平和島ビットコイン産業 MakerChipをつかったライトニングクレーンゲーム 備忘録・MakerChipタッチクライアント編

忘れてしまわないうちに備忘録を残しておく。思い出したら随時追記。

今回の展示物専用につくったラズパイとNFCタグリーダライタ(パソリ)で構成した、MakerChipをかざしてインボイスを取得・LNURL-withdrawサーバへ転送するクライアントについて。下図でいうと赤丸のところ。

前提として普通のラズパイOSでPythonでNFCタグリーダライタを使えるようにする。これはMakerChipを作成した時と同じ構成。

タッチを契機にして上図の①②⑤⑥までを処理する仕組み。ノーコードGUIプログラミングツールNode-REDを利用した。NFCタグリーダライタで検出してデータを読み取る部分だけPythonを使った。全部Node-REDで完結したかったけどうまくできなかったので妥協した。

メインフロー概要

  1. NFCタグ検出とデータ取得
  2. 読み取りデータの分析とLNURL-withdrawの検出
  3. BitcoinSwitchのLNURL-payでインボイス取得
  4. MakerChipから読み取ったLNURL-withdrawでインボイスを送信

Node-REDフロー図

メインフロー詳細

1. NFCタグ検出とデータ取得

InjectionノードでNode-REDが起動したら自動でフローを開始するように設定。
ExecノードでPythonスクリプトを実行して常時NFCタグ読み取りを開始。
NFCタグが検出されるとデータをJSONにしてNode-REDに寄越してくる。そのJSONをmsgペイロードに入れる。
以下はExecノードのプロパティ。nfcpyのあるPython仮想環境のpython3が動くようにパスを指定している。

検出スクリプトreadNDEF.pyは以下

import nfc
import ndef
import time
import sys
import signal
import json

running = True

def sig_handler(signum, frame):
    global running
    running = False

signal.signal(signal.SIGINT, sig_handler)
signal.signal(signal.SIGTERM, sig_handler)

def on_connect(tag):
    # 出力用のデータ構造を作る
    data = {
        "id": tag.identifier.hex().upper(),
        "type": str(tag).split(' ')[0], # 'Type2Tag' など
        "records": []
    }

    if tag.ndef:
        for record in tag.ndef.records:
            rec_data = {"type": record.type}
            if isinstance(record, ndef.UriRecord):
                rec_data["value"] = record.uri
            elif isinstance(record, ndef.TextRecord):
                rec_data["value"] = record.text
            data["records"].append(rec_data)

    # JSONとして1行で出力(flush=Trueで即座にNode-REDへ届ける)
    print(json.dumps(data), flush=True)

    while tag.is_present and running:
        time.sleep(0.1)

    return True

def main():
    try:
        with nfc.ContactlessFrontend('usb') as clf:
            # 起動したことをNode-REDに知らせるためのログ(必要なければ消してOK)
            # print(json.dumps({"status": "ready"}), flush=True)

            while running:
                clf.connect(rdwr={'on-connect': on_connect},
                            terminate=lambda: not running)
    finally:
        sys.exit(0)

if __name__ == "__main__":
    main()

2. 読み取りデータの分析とLNURL-withdrawの検出

 Switchノードを使ってmsgペイロードのデータがNDEF種別のリンクであること、そのリンクがLNURL-withdrawなのかチェック。以下4つ以外は無視する。

  • lnurlw:
  • lightning:lnrul1
  • lnurl1
  • 管理用URL(保守フローで使用)

 4.で使用するライブラリがlnurlw:に対応してない。lnurlw:の場合はlightning:lnurl1に変換しておく。

3. BitcoinSwitchのLNURL-payでインボイス取得

 ノードとサーバ構築編で述べた通りBitcoinSwitchのLNURL-payリンクを使ってインボイスを取得する。Functionノード内でlnurl-payライブラリ呼び出している。
 LUD-06の通りStep1でまずサーバへリクエストを送る。レスポンスに最大最小金額とコールバックURLが返ってくる。今回は最小も最大も410satsになる。このFunctionノード内で410と固定すると後で金額を変更するたびに書き換えが生じて面倒。なのでレスポンスの最大額でコールバックする処理が望ましい。
 Step2でコールバックURLあてに最大金額で送信、レスポンスでライトニングインボイスが返ってくる。

4. MakerChipから読み取ったLNURL-withdrawでインボイスを送信

 Functionノードでlunrl-withdrawライブラリを使い、MakerChipから読み取ったwithdrawリンクを使ってインボイス送る。1.で述べた通りlunrl-withdrawライブラリはlnurlw:// (LUD-17)には未対応。LUD-06と17は相互に変換可能。スキーム名をhttpsにしてBech32でエンコードしてlightning:を先頭に付加すればよい。
 今回は決済の可否について判断して何かするような仕組みにしてない。成功すればクレーンゲームが動く。失敗すれば動かない。エラー処理の実装まで手が回らなかった。

保守フロー概要

1.メインフローで何もせず長らく放置した後にウォレットサーバやスイッチサーバへリクエストを投げると遅延が発生することが動作試験で分かった。Pingを60秒おきに通して遅延を防止する。

2. 何らかの理由で決済が上手くいかなかった場合に備えて決済無しでBitcoinSwitchを起動できるようにする。NFCタグやカードを1枚用意して任意のURLを書き込む。そのカードがタッチされたらトリガーする仕組み。

  • LNbitsスイッチサーバの管理者アカウントのユーザー名とパスワードをInjectionノードに入れる。
  • ChangeノードにはGPIO番号とBitcoinSwitchのIDを入れる。
  • Functionノード「PINトリガーURLの作成」のコード内でスイッチサーバURLを指定する。

メインフローのSwitchノード「withdrawリンクの検出」でNFCタグリーダライタで検出したリンクを見分ける。例えば自分のXのプロフURLや自分のWebサイトなどで良い。そのNFCカードだったら直接トリガーするフローに流れる。
メインフローにあるLinkInノード(赤丸)から上図のChangeノード手前のLinkOutノードへフローが来る。

以下はメインフローおよび保守フローの書き出し。コピペまたはファイル(flow.json)にして読み込み。

[
    {
        "id": "7e8f499be470fe14",
        "type": "tab",
        "label": "LNURL-pw Bridge",
        "disabled": false,
        "info": "",
        "env": []
    },
    {
        "id": "0d08a3772e94be41",
        "type": "exec",
        "z": "7e8f499be470fe14",
        "command": "/home/tanakei/venv/bin/python3",
        "addpay": "",
        "append": "-u /home/tanakei/myProjects/readNDEF.py",
        "useSpawn": "true",
        "timer": "",
        "winHide": false,
        "oldrc": false,
        "name": "NFCタグ検出",
        "x": 300,
        "y": 120,
        "wires": [
            [
                "eba7e64f20bdcec6"
            ],
            [],
            []
        ]
    },
    {
        "id": "66796867659ba293",
        "type": "inject",
        "z": "7e8f499be470fe14",
        "name": "自動起動",
        "props": [],
        "repeat": "",
        "crontab": "",
        "once": true,
        "onceDelay": "5",
        "topic": "",
        "x": 120,
        "y": 100,
        "wires": [
            [
                "0d08a3772e94be41"
            ]
        ]
    },
    {
        "id": "70437b96df435864",
        "type": "debug",
        "z": "7e8f499be470fe14",
        "name": "NFCタグの中身",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "payload",
        "targetType": "msg",
        "statusVal": "",
        "statusType": "auto",
        "x": 740,
        "y": 100,
        "wires": []
    },
    {
        "id": "7839cf818adf4131",
        "type": "inject",
        "z": "7e8f499be470fe14",
        "name": "SIGTERM",
        "props": [
            {
                "p": "kill",
                "v": "SIGTERM",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "x": 120,
        "y": 140,
        "wires": [
            [
                "0d08a3772e94be41"
            ]
        ]
    },
    {
        "id": "eba7e64f20bdcec6",
        "type": "json",
        "z": "7e8f499be470fe14",
        "name": "",
        "property": "payload",
        "action": "",
        "pretty": false,
        "x": 470,
        "y": 100,
        "wires": [
            [
                "70437b96df435864",
                "975d1fdadeb6201c"
            ]
        ]
    },
    {
        "id": "82f3148fa523a298",
        "type": "inject",
        "z": "7e8f499be470fe14",
        "d": true,
        "name": "読み取り開始",
        "props": [],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": "1",
        "topic": "",
        "x": 130,
        "y": 60,
        "wires": [
            [
                "0d08a3772e94be41"
            ]
        ]
    },
    {
        "id": "89770f4ca18b5ce8",
        "type": "switch",
        "z": "7e8f499be470fe14",
        "name": "NDEFの種別",
        "property": "payload.records[0].type",
        "propertyType": "msg",
        "rules": [
            {
                "t": "eq",
                "v": "urn:nfc:wkt:U",
                "vt": "str"
            },
            {
                "t": "else"
            }
        ],
        "checkall": "true",
        "repair": false,
        "outputs": 2,
        "x": 110,
        "y": 220,
        "wires": [
            [
                "9be9a2453865cc1f"
            ],
            []
        ]
    },
    {
        "id": "9be9a2453865cc1f",
        "type": "switch",
        "z": "7e8f499be470fe14",
        "name": "withdrawリンクの検出",
        "property": "payload.records[0].value",
        "propertyType": "msg",
        "rules": [
            {
                "t": "regex",
                "v": "^lnurlw:",
                "vt": "str",
                "case": true
            },
            {
                "t": "regex",
                "v": "^lightning:lnurl1",
                "vt": "str",
                "case": true
            },
            {
                "t": "regex",
                "v": "^lnurl1",
                "vt": "str",
                "case": true
            },
            {
                "t": "eq",
                "v": "https://x.com/tnky2929",
                "vt": "str"
            },
            {
                "t": "else"
            }
        ],
        "checkall": "true",
        "repair": false,
        "outputs": 5,
        "x": 140,
        "y": 280,
        "wires": [
            [
                "6c09174a4b8acaf5"
            ],
            [
                "80b8062eed141d55"
            ],
            [
                "80b8062eed141d55"
            ],
            [
                "7923395745d6e9fb"
            ],
            []
        ]
    },
    {
        "id": "7cdd71505d1f4f68",
        "type": "debug",
        "z": "7e8f499be470fe14",
        "name": "LNURL-p Step1",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 940,
        "y": 400,
        "wires": []
    },
    {
        "id": "91af74635df4a68d",
        "type": "function",
        "z": "7e8f499be470fe14",
        "name": "httpsに置換してエンコード",
        "func": "// スキームを https に変換\nlet url = msg.payload.records[0].value.replace(\"lnurlw://\", \"https://\");\n\n// URLエンコードされた記号(%3D や %26)をデコード\n// これにより ?p=XXX&c=YYY という正しい形式になります\nurl = decodeURIComponent(url);\n\n// LNURLにエンコード\nconst encoded = lnurl.encode(url);\n\n// 次のノードに渡すLNURLをセット\nmsg.payload.records[0].value = encoded;\n\nreturn msg;",
        "outputs": 1,
        "timeout": 0,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [
            {
                "var": "lnurl",
                "module": "lnurl"
            }
        ],
        "x": 640,
        "y": 240,
        "wires": [
            [
                "fc97082917d85190"
            ]
        ]
    },
    {
        "id": "fc97082917d85190",
        "type": "change",
        "z": "7e8f499be470fe14",
        "name": "withdrawリンクをmsg.withdrawへコピー",
        "rules": [
            {
                "t": "set",
                "p": "withdraw",
                "pt": "msg",
                "to": "msg.payload.records[0].value",
                "tot": "msg"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 700,
        "y": 280,
        "wires": [
            [
                "9fbcaa5043042f42",
                "301651b039acf8f1"
            ]
        ]
    },
    {
        "id": "333a57e98f326d70",
        "type": "function",
        "z": "7e8f499be470fe14",
        "name": "LNURL-w インボイス支払い",
        "func": "// 1. 支払いたいインボイスと LNURL-withdrawリンク\nconst myInvoice = msg.payload.invoice;\nconst identifier = msg.payload.withdraw;\n\n// 2. 非同期関数を定義し、現在の msg を引数として渡す(スコープを固定)\n(async (m) => {\n    try {\n        const response = await lnurlWithdraw.requestPayment({\n            lnUrl: identifier,\n            invoice: myInvoice\n        });\n\n        // デバッグ用にレスポンス全体を確認\n        // node.warn(response); \n\n        // 結果をセット\n        // ライブラリの仕様により response.result か response 自体か確認してください\n        m.payload = response.result || response;\n\n        // node.send を使って次のノードへ\n        node.send(m);\n\n    } catch (e) {\n        m.payload = null;\n        node.error(\"LNURL-withdraw error: \" + e.message, m);\n    }\n})(msg); // ここで msg を渡す\n\n// 同期的な return は null にして、自動的な送信を防ぐ\nreturn null;",
        "outputs": 1,
        "timeout": 0,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [
            {
                "var": "lnurlWithdraw",
                "module": "lnurl-withdraw"
            }
        ],
        "x": 560,
        "y": 560,
        "wires": [
            [
                "32037cb65800cee6",
                "1118c3a13ed2d631"
            ]
        ]
    },
    {
        "id": "6ee75751bae2fd72",
        "type": "change",
        "z": "7e8f499be470fe14",
        "name": "withdrawリンクをpayloadにコピー",
        "rules": [
            {
                "t": "set",
                "p": "payload.withdraw",
                "pt": "msg",
                "to": "withdraw",
                "tot": "msg"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 260,
        "y": 560,
        "wires": [
            [
                "333a57e98f326d70"
            ]
        ]
    },
    {
        "id": "9fbcaa5043042f42",
        "type": "debug",
        "z": "7e8f499be470fe14",
        "name": "withdrawリンク検出後",
        "active": false,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 1020,
        "y": 280,
        "wires": []
    },
    {
        "id": "1118c3a13ed2d631",
        "type": "debug",
        "z": "7e8f499be470fe14",
        "name": "LNURL-w 結果",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 940,
        "y": 560,
        "wires": []
    },
    {
        "id": "80b8062eed141d55",
        "type": "delay",
        "z": "7e8f499be470fe14",
        "name": "検出後は指定秒無視",
        "pauseType": "rate",
        "timeout": "5",
        "timeoutUnits": "seconds",
        "rate": "1",
        "nbRateUnits": "5",
        "rateUnits": "second",
        "randomFirst": "1",
        "randomLast": "5",
        "randomUnits": "seconds",
        "drop": true,
        "allowrate": false,
        "outputs": 1,
        "x": 380,
        "y": 280,
        "wires": [
            [
                "fc97082917d85190"
            ]
        ]
    },
    {
        "id": "6c09174a4b8acaf5",
        "type": "delay",
        "z": "7e8f499be470fe14",
        "name": "検出後は指定秒無視",
        "pauseType": "rate",
        "timeout": "5",
        "timeoutUnits": "seconds",
        "rate": "1",
        "nbRateUnits": "5",
        "rateUnits": "second",
        "randomFirst": "1",
        "randomLast": "5",
        "randomUnits": "seconds",
        "drop": true,
        "allowrate": true,
        "outputs": 1,
        "x": 380,
        "y": 240,
        "wires": [
            [
                "91af74635df4a68d"
            ]
        ]
    },
    {
        "id": "975d1fdadeb6201c",
        "type": "change",
        "z": "7e8f499be470fe14",
        "name": "withdrawリンク検出後の無視するミリ秒数指定",
        "rules": [
            {
                "t": "set",
                "p": "rate",
                "pt": "msg",
                "to": "10000",
                "tot": "num"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 620,
        "y": 140,
        "wires": [
            [
                "89770f4ca18b5ce8"
            ]
        ]
    },
    {
        "id": "301651b039acf8f1",
        "type": "change",
        "z": "7e8f499be470fe14",
        "name": "LNbits BitcoinSwitch payリンク",
        "rules": [
            {
                "t": "set",
                "p": "payload",
                "pt": "msg",
                "to": "lightning:LNURL1",
                "tot": "str"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 250,
        "y": 400,
        "wires": [
            [
                "3d5a166ceacde340"
            ]
        ]
    },
    {
        "id": "785c9f30b6089c70",
        "type": "debug",
        "z": "7e8f499be470fe14",
        "name": "LNURL-p Step2",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 940,
        "y": 460,
        "wires": []
    },
    {
        "id": "3d5a166ceacde340",
        "type": "function",
        "z": "7e8f499be470fe14",
        "name": "LNURL-p Step1 インボイス請求",
        "func": "const address = msg.payload;\n\ntry {\n    const params = await lnurlPay.requestPayServiceParams({\n        lnUrlOrAddress:address\n        });\n\n    // 次のステップのためにパラメータを保存\n    msg.payload = params;\n\n    return msg;\n} catch (e) {\n    node.error(\"Step 1 Error: \" + e.message);\n}",
        "outputs": 1,
        "timeout": 0,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [
            {
                "var": "lnurlPay",
                "module": "lnurl-pay"
            }
        ],
        "x": 570,
        "y": 400,
        "wires": [
            [
                "7cdd71505d1f4f68",
                "6e6cca40e8a04bb1"
            ]
        ]
    },
    {
        "id": "6e6cca40e8a04bb1",
        "type": "function",
        "z": "7e8f499be470fe14",
        "name": "LNURL-p Step2 最大額でインボイス取得",
        "func": "const params = msg.payload;\nconst amountSats = params.max; // satoshi\n\ntry {\n    const result = await lnurlPay.requestInvoiceWithServiceParams({\n        params,\n        tokens: amountSats\n    });\n\n    msg.payload = result;\n\n    return msg;\n} catch (e) {\n    node.error(\"Step 2 Error: \" + e.message);\n}",
        "outputs": 1,
        "timeout": 0,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [
            {
                "var": "lnurlPay",
                "module": "lnurl-pay"
            }
        ],
        "x": 640,
        "y": 460,
        "wires": [
            [
                "785c9f30b6089c70",
                "6ee75751bae2fd72"
            ]
        ]
    },
    {
        "id": "32037cb65800cee6",
        "type": "switch",
        "z": "7e8f499be470fe14",
        "name": "決済済み",
        "property": "payload.sent",
        "propertyType": "msg",
        "rules": [
            {
                "t": "true"
            },
            {
                "t": "else"
            }
        ],
        "checkall": "true",
        "repair": false,
        "outputs": 2,
        "x": 540,
        "y": 600,
        "wires": [
            [],
            []
        ]
    },
    {
        "id": "56d626741f7f4f90",
        "type": "ping",
        "z": "7e8f499be470fe14",
        "protocol": "IPv4",
        "mode": "timed",
        "name": "ウォレットサーバのホスト",
        "host": "SERVER2",
        "timer": "20",
        "inputs": 0,
        "x": 150,
        "y": 780,
        "wires": [
            [
                "976e2257c332b509"
            ]
        ]
    },
    {
        "id": "976e2257c332b509",
        "type": "debug",
        "z": "7e8f499be470fe14",
        "name": "Ping結果",
        "active": false,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "payload",
        "targetType": "msg",
        "statusVal": "",
        "statusType": "auto",
        "x": 360,
        "y": 760,
        "wires": []
    },
    {
        "id": "69449dbed48a8cf0",
        "type": "http request",
        "z": "7e8f499be470fe14",
        "name": "直接起動",
        "method": "PUT",
        "ret": "obj",
        "paytoqs": "ignore",
        "url": "",
        "tls": "",
        "persist": false,
        "proxy": "",
        "insecureHTTPParser": false,
        "authType": "",
        "senderr": false,
        "headers": [],
        "x": 640,
        "y": 1100,
        "wires": [
            [
                "979c3b8761a223d6"
            ]
        ]
    },
    {
        "id": "9b2420d62b1374c6",
        "type": "function",
        "z": "7e8f499be470fe14",
        "name": "PINトリガーURLの作成",
        "func": "const switch_id = msg.payload.switch_id;\nconst pin = msg.payload.pin;\n//const usr = msg.payload.usr;\n\n// テンプレートリテラルでURLを組み立て\nmsg.url = `https://hogepiyo.com/bitcoinswitch/api/v1/trigger/${switch_id}/${pin}`;\n\nreturn msg;",
        "outputs": 1,
        "timeout": 0,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 430,
        "y": 1100,
        "wires": [
            [
                "69449dbed48a8cf0"
            ]
        ]
    },
    {
        "id": "979c3b8761a223d6",
        "type": "debug",
        "z": "7e8f499be470fe14",
        "name": "debug 7",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 820,
        "y": 1100,
        "wires": []
    },
    {
        "id": "98a691c89d61e845",
        "type": "http request",
        "z": "7e8f499be470fe14",
        "name": "管理ユーザ認証",
        "method": "POST",
        "ret": "obj",
        "paytoqs": "ignore",
        "url": "https://hogepiyo.com/api/v1/auth",
        "tls": "",
        "persist": false,
        "proxy": "",
        "insecureHTTPParser": false,
        "authType": "",
        "senderr": false,
        "headers": [],
        "x": 340,
        "y": 940,
        "wires": [
            [
                "a513d9eb6f27b699"
            ]
        ]
    },
    {
        "id": "9c3f3f9fa4d32d38",
        "type": "inject",
        "z": "7e8f499be470fe14",
        "name": "管理ユーザー名とパスワード",
        "props": [
            {
                "p": "payload.username",
                "v": "",
                "vt": "str"
            },
            {
                "p": "payload.password",
                "v": "",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": true,
        "onceDelay": "3",
        "topic": "",
        "x": 190,
        "y": 900,
        "wires": [
            [
                "2ed17032cae7be43"
            ]
        ]
    },
    {
        "id": "2ed17032cae7be43",
        "type": "json",
        "z": "7e8f499be470fe14",
        "name": "",
        "property": "payload",
        "action": "str",
        "pretty": true,
        "x": 390,
        "y": 900,
        "wires": [
            [
                "98a691c89d61e845"
            ]
        ]
    },
    {
        "id": "2079be2ea03818df",
        "type": "debug",
        "z": "7e8f499be470fe14",
        "name": "debug 8",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "false",
        "statusVal": "",
        "statusType": "auto",
        "x": 740,
        "y": 940,
        "wires": []
    },
    {
        "id": "a513d9eb6f27b699",
        "type": "change",
        "z": "7e8f499be470fe14",
        "name": "",
        "rules": [
            {
                "t": "set",
                "p": "access_token",
                "pt": "flow",
                "to": "payload.access_token",
                "tot": "msg"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 560,
        "y": 940,
        "wires": [
            [
                "2079be2ea03818df"
            ]
        ]
    },
    {
        "id": "0586d5cf48a6c21f",
        "type": "function",
        "z": "7e8f499be470fe14",
        "name": "アクセストークンをヘッダーに入れる",
        "func": "// flowコンテキストからmytokenを取得\nconst myToken = flow.get(\"access_token\");\n\n// msg.headers オブジェクトを作成(既存のヘッダーがある場合は維持、なければ新規)\nmsg.headers = msg.headers || {};\n\n// cookie ヘッダーをセット\nmsg.headers[\"Cookie\"] = `cookie_access_token=${myToken}`;\n\n// 必要に応じて Content-Type などもここで指定可能\nmsg.headers[\"Content-Type\"] = \"application/json\";\n\nreturn msg;",
        "outputs": 1,
        "timeout": 0,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 370,
        "y": 1060,
        "wires": [
            [
                "9b2420d62b1374c6"
            ]
        ]
    },
    {
        "id": "6943c68597c5c722",
        "type": "comment",
        "z": "7e8f499be470fe14",
        "name": "Pingしてレスポンスを維持する",
        "info": "",
        "x": 170,
        "y": 700,
        "wires": []
    },
    {
        "id": "3fde2b2121182c12",
        "type": "comment",
        "z": "7e8f499be470fe14",
        "name": "直接BitcoinSwitchをトリガーする",
        "info": "",
        "x": 170,
        "y": 860,
        "wires": []
    },
    {
        "id": "7923395745d6e9fb",
        "type": "link out",
        "z": "7e8f499be470fe14",
        "name": "link out 1",
        "mode": "link",
        "links": [
            "41dd5484eef35bcd"
        ],
        "x": 295,
        "y": 320,
        "wires": []
    },
    {
        "id": "41dd5484eef35bcd",
        "type": "link in",
        "z": "7e8f499be470fe14",
        "name": "link in 3",
        "links": [
            "7923395745d6e9fb"
        ],
        "x": 115,
        "y": 1020,
        "wires": [
            [
                "3441084a7fb60feb"
            ]
        ]
    },
    {
        "id": "3441084a7fb60feb",
        "type": "change",
        "z": "7e8f499be470fe14",
        "name": "",
        "rules": [
            {
                "t": "set",
                "p": "payload.pin",
                "pt": "msg",
                "to": "25",
                "tot": "str"
            },
            {
                "t": "set",
                "p": "payload.switch_id",
                "pt": "msg",
                "to": "",
                "tot": "str"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 240,
        "y": 1020,
        "wires": [
            [
                "0586d5cf48a6c21f"
            ]
        ]
    },
    {
        "id": "3c639ddd872d59d5",
        "type": "ping",
        "z": "7e8f499be470fe14",
        "protocol": "IPv4",
        "mode": "timed",
        "name": "スイッチサーバのホスト",
        "host": "",
        "timer": "60",
        "inputs": 0,
        "x": 150,
        "y": 740,
        "wires": [
            [
                "976e2257c332b509"
            ]
        ]
    },
    {
        "id": "a9f4d9b28191bd3f",
        "type": "inject",
        "z": "7e8f499be470fe14",
        "name": "",
        "props": [],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "x": 90,
        "y": 980,
        "wires": [
            [
                "3441084a7fb60feb"
            ]
        ]
    },
    {
        "id": "5a1b06527dc421d6",
        "type": "global-config",
        "env": [],
        "modules": {
            "node-red-node-ping": "0.3.3"
        }
    }
]

この続き : 0字 / 画像 0枚
100

会員登録 / ログインして続きを読む

関連記事

記事を書いた人

甘いもの大好きメタボ猫。マイペースなのはしょうがない。 nostr:npub10zeurmg22wc89l8m3npw9cyu45cun0lvs6w3ep69cdpa25pna65s0994qz

SNSにシェア

このクリエイターの人気記事

【Umbrel】BlueWallet Lightning & tailscaleで便利で快適なLightningウォレットを作ろう!

602

【Umbrel】OCEANからマイニング報酬をライトニングで受け取るには【Sparrow Wallet】

384

【Umbrel】LNURLが使いたいから自分で環境つくってみた

361