Open9

obniz目覚まし時計⏰ (LINEとobnizを組み合わせたチャットアプリケーションを作成)

VanVan

早起き失敗。アイデアが浮かばない。デザイン思考のアイデア出し難しい。。。

VanVan

アイデア出し

obnizを使った目覚まし時計

  • 「スマホ脳」という新書を読んでスマホを寝室に持ち込まなくなった。
  • 違う部屋に置いたスマホで目覚ましをかけているが、結構音量大きめに設定しないといけないので、隣の部屋へ迷惑じゃないかと思う時がある。
  • 目覚まし時計はカチカチ音?が気になる。
  • ときたま、目覚ましよりも早く起きて朝シャワーしているときにスマホのアラームがなったら大惨事になる。30分以上アラームが鳴りっぱなしでめちゃくちゃ迷惑かけた。
  1. LINE Botにアラームの設定時刻を送る
  2. Googleスプレッドシートに転記する
  3. ブザーを鳴らす時間をGoogleスプレッドシートから参照する
  4. 時間になったらブザーを鳴らす
  5. 電気をつけたら止まる(ブザーが鳴ってから電気をつけるまでの時間をタイムアタック形式にしたら面白そう)

使うもの

  • obniz
  • ブザー
  • CdSセルセンサ
VanVan

前回の反省を活かして、最終系を意識したコードを作成してもらえるようにGPTにお願いする。

## 命令
あなたはobniz 1Y Boardのエキスパートです。## 条件に基づいて以下を実現させたい。
そのためのコードを教えて
* GoogleスプレッドシートのB7セルに記載されている時間になったらブザーを鳴らす。
* 次に、照度センサがしきい値を超えたらブザーを止めたい。
* ブザーを鳴らしてから照度センサがしきい値を超えるまでの時間をGoogleスプレッドシートのB8セルに転記する。

## 条件
* 深呼吸して考えて
* 分からないことがあれば分からないと言って
* 実行環境はobniz開発者コンソール(https://console.obniz.com/ja/)
* Googleスプレッドシートはstein(https://steinhq.com/)で連携
* ブザーは0番ピンと1番ピンに接続
* 照度センサの入力ポートは3番ピンを接続
VanVan

全然うまくいかず、めちゃくちゃ修正した

  • axios ライブラリ、moment.js ライブラリの読み込み
  • require関数の置き換え
  • pwm関数からspeaker関数へ置き換え

steinのリクエストがGTP-4oに何回ラリーしてもエラーになり手づまり

VanVan

Claude 3.5 Sonnetに聞いたら一発で解決した。すごい。

あらかたのコードの実装完了

VanVan

ちょっとClaude 3.5 Sonnetに聞いて修正を加える

  • アラームが鳴ってから止めるまでの時間を計測してGoogleスプレッドシートに転記するようにする。
  • 照度センサのスレッショルドを調整

最後にアラームが鳴ってから止めるまでの時間をユーザーに返したかったが、 「Obniz開発者コンソールから直接LINE Botにプッシュメッセージを送ることは通常できません。」 との回答

以下回答

申し訳ありませんが、Obniz開発者コンソールから直接LINE Botにプッシュメッセージを送ることは通常できません。その理由と代替案について説明いたします。

  1. セキュリティ制限:
    Obniz開発者コンソールは、セキュリティ上の理由から外部APIへの直接アクセスを制限しています。これは、LINE Messaging APIを含む外部サービスへの直接のHTTPリクエストを防ぐためです。
  2. クロスオリジン制限:
    ブラウザベースの環境であるObniz開発者コンソールは、同一オリジンポリシーに従う必要があります。LINE Messaging APIは異なるドメインにあるため、直接アクセスすることはできません。
  3. 認証情報の保護:
    LINE Botのチャンネルアクセストークンなどの認証情報を、クライアントサイドのJavaScriptに直接埋め込むことは安全ではありません。

代替案として、以下の方法を検討することをお勧めします:

  1. Obniz Cloud Functionsの使用:
    Obniz Cloud Functionsを使用して、サーバーサイドでLINE Messaging APIにアクセスするロジックを実装できます。これにより、セキュリティを維持しながら、LINE Botにメッセージを送信できます。
  2. 中間サーバーの利用:
    自身で管理する中間サーバー(例:Node.jsサーバー)を設置し、Obnizデバイスからこのサーバーにデータを送信し、サーバーからLINE Botにメッセージを送信する方法もあります。
  3. Webhookの利用:
    LINEのWebhook機能を使用して、特定のイベントが発生したときにLINE Botにメッセージを送信することもできます。この場合、ObnizデバイスはWebhookをトリガーするイベントを発生させる役割を担います。

とても面倒くさそう

VanVan

一旦、LINEにアラームを止めるまでの秒数を送る実装はなしで最終コード

最終コード
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>Obniz Alarm Control</title>
    <script src="https://unpkg.com/obniz@3.31.0/obniz.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.21.1/axios.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.1/moment.min.js"></script>
</head>
<body>
    <div id="obniz-debug"></div>
    <h1>Obniz Alarm Control</h1>
    <script>
        const SPREADSHEET_URL = 'https://api.steinhq.com/v1/storages/***'; //***はスプレッドシートIDを入力
        const LIGHT_THRESHOLD = 0.2;
        const obnizId = 'YOUR_OBNIZ_ID';
        const obniz = new Obniz(obnizId);
        let buzzerStartTime = null;
        let speaker = null;

        let currentAlarmTimeout = null;

        async function getAlarmTime() {
            try {
                const response = await axios.get(`${SPREADSHEET_URL}/Sheet1`);
                console.log("Response from Google Sheets:", response.data);
                if (!response.data || !response.data[5] || !response.data[5].value) {
                    throw new Error('Missing or invalid data structure in the response');
                }
                const alarmTimeString = response.data[5].value;
                console.log("Alarm time from B7:", alarmTimeString);
                const alarmTime = moment(alarmTimeString, "HH:mm");
                if (!alarmTime.isValid()) {
                    throw new Error('Invalid time format in B7 cell');
                }
                return alarmTime;
            } catch (error) {
                console.error('Error fetching alarm time:', error.message);
                if (error.response) {
                    console.error('Error response data:', error.response.data);
                }
                return null;
            }
        }

        async function checkAlarmTime() {
            const alarmTime = await getAlarmTime();
            if (alarmTime === null) return;
            const now = moment();
            let timeUntilAlarm = moment.duration(alarmTime.diff(now));

            if (timeUntilAlarm.asMilliseconds() < 0) {
                timeUntilAlarm.add(1, 'day');
            }

            console.log(`Alarm will sound in ${timeUntilAlarm.asMilliseconds()} milliseconds`);

            if (currentAlarmTimeout) {
                clearTimeout(currentAlarmTimeout);
            }
            
            currentAlarmTimeout = setTimeout(() => {
                speaker = obniz.wired("Speaker", {signal:0, gnd:1});
                speaker.play(440, 0.5);
                buzzerStartTime = moment();
            }, timeUntilAlarm.asMilliseconds());
        }

        obniz.onconnect = async () => {
            await checkAlarmTime();
            setInterval(checkAlarmTime, 60000);
            obniz.ad3.start((value) => {
                if (value > LIGHT_THRESHOLD && buzzerStartTime) {
                    if (speaker) {
                        speaker.stop();
                    }
                    const buzzerStopTime = moment();
                    const durationMs = buzzerStopTime.diff(buzzerStartTime);
                    const duration = (durationMs / 1000).toFixed(3);
                    updateDurationInSpreadsheet(duration);
                    console.log(duration);
                    buzzerStartTime = null;
                }
            });
        };

        // 修正された updateDurationInSpreadsheet 関数
        async function updateDurationInSpreadsheet(duration) {
            try {
                // B8セルに実行時間を記録 (この処理は以前と同じ)
                const durationResponse = await axios.put(`${SPREADSHEET_URL}/Sheet1`, {
                    "condition": { "device": "Duration" },
                    "set": { "value": duration }
                });
                console.log('Duration updated successfully in B8:', durationResponse.data);

                // 新しい行を追加し、T、duration、現在の日時を記録
                const currentTime = moment().format('YYYY-MM-DD HH:mm:ss');
                const newRowResponse = await axios.post(`${SPREADSHEET_URL}/Sheet1`, [{
                    "device": "T",
                    "value": duration,
                    "last update": currentTime
                }]);
                console.log('New row added successfully:', newRowResponse.data);
            } catch (error) {
                console.error('Error updating spreadsheet:', error.message);
                if (error.response) {
                    console.error('Error response data:', error.response.data);
                }
            }
        }

        obniz.connect();
    </script>
</body>
</html>
VanVan

なんかmakeのシナリオを2段構えにしたらできそう。
ただ、最大1分間の待ち時間がネック