スマホの充電器が外されたら警報！するアプリ

## 初めに この発想の理由
さて、
「皆さんはなんとなく家で充電しているときに、いつの間にかほかの人が充電していた！」
「しかも自分のスマホが全然充電されてない！」
なんてことありませんか？((((

### 抜かれたら警報音を流せばよいのでは？！
という発想で作っていきます。
ちなみに、今回は～

***with ChatGPTでございます！！！👏👏👏*** 
※記事の最後に、コミュニティガイドラインに準拠するため、コードの検証を実施しています。

一応自分もコードを書いていますが、かなり直してもらってます！
### 使用技術

- Javascript
- PWA

- HTML/CSS

## サンプル
スマートフォンなどで、このサイトを開いて、ボタンを押し、充電しながら置いておけば、充電器を抜いた時に、警報が鳴ります！


https://smartalert.pages.dev


## コード

Javascriptと、HTMLの組み合わせはこのような感じです
```HTML
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>通知システム</title>
    <style>
        /* 全画面を黒くする */
        body {
            margin: 0;
            padding: 0;
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
            background-color: black;
            color: white;
            font-family: Arial, sans-serif;
        }

        #main_page {
            display: none;
            align-items: center;
            justify-content: center;
            height: 100vh;
            background-color: white;
            color: black;
        }

        #main_page img {
            max-width: 80%;
            max-height: 80%;
        }

        #setting_page {
            display: flex;
            flex-direction: column;
            align-items: center;
        }

        .hidden {
            display: none;
        }

        .button {
            margin: 10px;
            padding: 10px 20px;
            font-size: 16px;
            cursor: pointer;
        }

        .slider-container {
            margin: 20px;
        }
    </style>
</head>
<body>
    <div id="setting_page">
        <h1>通知設定</h1>
        <div class="slider-container">
            <label for="frequency">周波数 (Hz):</label>
            <input type="range" id="frequency" min="100" max="1000" step="10" value="440">
            <span id="frequency_value">440 Hz</span>
        </div>
        <div class="upload-container">
            <label for="upload_image">通知時に表示する画像:</label>
            <input type="file" id="upload_image" accept="image/*">
        </div>
        <button id="start_button" class="button">通知を有効化</button>
    </div>
    <div id="main_page">
        <img id="display_image" src="" alt="通知画像">
        <button id="stop_button" class="button">停止</button>
    </div>

    <script>
var settingPage = document.getElementById("setting_page");
var mainPage = document.getElementById("main_page");
var startButton = document.getElementById("start_button");
var stopButton = document.getElementById("stop_button");
var frequencySlider = document.getElementById("frequency");
var frequencyValue = document.getElementById("frequency_value");
var uploadImage = document.getElementById("upload_image");
var displayImage = document.getElementById("display_image");
let audioContext, oscillator, intervalId;

// 周波数スライダーのリアルタイム更新
frequencySlider.addEventListener("input", () => {
    frequencyValue.textContent = `${frequencySlider.value} Hz`;
});

// 画像アップロードと保存
uploadImage.addEventListener("change", (event) => {
    var file = event.target.files[0];
    if (file) {
        var reader = new FileReader();
        reader.onload = (e) => {
            localStorage.setItem("notificationImage", e.target.result);
        };
        reader.readAsDataURL(file);
    }
});

// スタートボタン押下時
startButton.addEventListener("click", () => {
    // 初期化
    audioContext = new (window.AudioContext || window.webkitAudioContext)();
    settingPage.classList.add("hidden");
    mainPage.style.display = "none";
    document.body.style.backgroundColor = "black";

    // Battery Status API チェック
    if ('getBattery' in navigator) {
        navigator.getBattery().then((battery) => {
            battery.onchargingchange = () => {
                if (!battery.charging) {
                    playNotification();
                }
            };
        });
    } else {
        alert("Battery Status APIがこのブラウザでサポートされていません。");
    }
});

// 停止ボタン押下時
stopButton.addEventListener("click", () => {
    if (intervalId) {
        clearInterval(intervalId);
        intervalId = null;
    }
    if (oscillator) {
        oscillator.stop();
        oscillator = null;
    }
    mainPage.style.display = "none";
    document.body.style.backgroundColor = "black";
});

// 通知音と画像表示
function playNotification() {
    // 画面を点灯し、画像を表示
    var savedImage = localStorage.getItem("notificationImage");
    if (savedImage) {
        displayImage.src = savedImage;
    }
    mainPage.style.display = "flex";
    document.body.style.backgroundColor = "white";

    // 音を「ぴぴぴ」と再生
    if (!audioContext) return;
    if (!oscillator) {
        intervalId = setInterval(() => {
            if (oscillator) {
                oscillator.stop();
                oscillator = null;
            } else {
                oscillator = audioContext.createOscillator();
                oscillator.type = "sine";
                oscillator.frequency.setValueAtTime(frequencySlider.value, audioContext.currentTime);
                oscillator.connect(audioContext.destination);
                oscillator.start();
            }
        }, 300); // 300msごとにオン・オフを切り替え
    }
}

    </script>
</body>
</html>

    </script>
</body>
</html>

```

# 動作検証
見る限り、iOSはダメっぽいですね。
あと、Androidも、電源を切っていると動かない場合がありました。
有機ELの画面などの、常時黒い画面で電力を食わないようなデバイスが向いているかもしれません

|       | Android | iOS | Ubuntu | windows |
|:------:|---------|-----|--------|---------|
| Chrome |    〇   |  ✕  |   〇   |    〇   |
| Edge   |    〇   |  ✕  |   〇   |    〇   |
| Safari |    /    |  ✕  |    /   |    /    |

※それぞれ記事製作時点での最新バージョンのOS/ブラウザで検証。


## さて、いかがでしょう！

かなりそれっぽく動作します！
これが何の役に立つかはわかりませんが、「それがクソアプリ」です。(？)

エンジニア始めたてからあこがれ続け、今初めて書いた、この「クソアプリ」のアドベントカレンダーでしたが、それほどクソ要素が出せなかった気がします。。。(´・ω・`)

***私が初めて、リスペクトを受けた記事でした！↓***

https://qiita.com/coboco/items/a6e93d92b386e148fa45

この記事との出会いに心から感謝して。

もっとクソ要素を増やせるよう尽力してまいりますので、来年もよろしくお願いいたします。

お読みいただきありがとうございました。

--------
***コミュニティガイドラインへの準拠用***
* AIの生成部分の検証
　→以下に私が評価します。
 ***CSS部分:*** bodyの微調整と、画像の位置、子要素の親要素との余白、要素を並べる方向、隠す際の必要なClassを用意されています。
 ***HTML部分:*** それぞれページ切り替え用のidを振った親要素、スライダーのinput(range)で、周波数を表示するためのブロック要素、ファイルアップロードをするためのinput(拡張子は画像のみ許可)、そして有効化ボタン(idで設定),設定した画像の表示要素と、停止ボタン(idで設定)
      ***Javascript部分:*** 最初の点は、それぞれの要素のidを指定して変数へ代入、周波数スライダーの要素のイベントを受け、周波数の表示要素を書き換え、アップロード要素でアップロードされた際に、基本的な方法で、ファイルを読み込み、結果をlocalStorageへ保存、スタートが押された際に、すでに再生されているアラームをリセットし、設定ページへ、hiddenのクラスを付与し、背景色を黒に変更、navigatorを使用し、APIへの対応を確認し、バッテリーのステータスが変わった際に、playNotification()で、音の再生と表示。停止ボタンは、あとにあるplayNotification()で、設定したIntervalをclearし、同時に変数のフラグもnullにする、最後に、通知音と画像表示は関数が呼び出された際、localStorageからデータを読み出し、画像表示要素のsrcにセット、mainpageのスタイルにflexを設定し、画像とボタンを並べ、背景色を白に変更そして、Intervalを設定し、300ミリ秒の周期で、停止するまでcreateOscillator()を使用し、スライダーから周波数をとり、同時に再生時間をAudio Contextにセットしました。

      以上、検証とします。