限界エンジニアの備忘録

ぐっすり眠りたい

普通二輪免許 教習の備忘録

教習所のイメージ

~入校手続き・備品購入~

  • 二輪免許取得に向けて近くの教習所に申込み
  • 普通二輪免許は普通四輪を持っていると10万程度で申し込み可能
  • 念のため安心パックで補習対策
  • この教習所ではヘルメット・手袋はレンタルはないので購入が必要
  • 何もわからないまま家から1時間の2輪館でヘルメットと手袋を購入

~1段階~

1段階では9コマを予定していたが結果として10コマかかった。

教習開始 「発進・停止」

  • この時間初めてバイクに触った。教習者は比較的新しいホンダのNX400L。開口一番「重たい」と言ってしまった。やはり小型二輪から始めるべきだっただろうか・・・そして先生がスタンド・エンジン・ブレーキ・アクセル・クラッチの各々の説明が始まる。なんとなくは理解するもののやってみないとわからない。先生曰く、クラッチを徐々につないでいけ、とのこと。    
  • しかしなかなか感覚が掴めず、エンストしまくり。またうまく発進してもギアチェンジも微妙、そして停止する時もうまく止まれずたちコケ。重たい。結局この時間だけでうまくいくということもなかった。二時間連続で教習を取っていたがその時間いっぱい使ってもだめで補習になった。補習に入ってもなんとかエンストせず発進できるものの、まだぎこちない、ビビっている、力強くない発進になっている。それでもって3コマ使ってクリアした。

教習車のイメージ

シミュレーション①

ブレーキの特性を知るためのシミュレーション講習。バイクのフロントブレーキ・リアブレーキはそれぞれどのような特性があるのか。ブレーキをかけているときの加重の話。この時間はシミュレーションなので補習にはならない模様

課題教習①:急制動スラローム

  • 前回の技能教習で発進が微妙だった不安を引きずって、スラローム急制動の課題が始まる。なお急制動卒業検定では40キロが指定されているが1段階では30キロとなる。スラロームはタイムを意識しなければそこまで問題ない。ただ、指導員の方がタイムを言うようになってからぶつけまくってしまう。傾いたときにうまくアクセルを入れるのをリズミカルにすれば早いタイムを出せるのはイメージできているが、実際はアクセルの微調整が難しく不安定だった。
  • 急制動は30キロならそこまで困難ではなかったのでここは難なくクリアした。スラロームも継続課題なのでそこまで問題はなかった。

課題教習②:S字クランク・一本橋

  • S字クランクはそこまで難しくなかった。リアブレーキ+半クラッチがうまく使えていればハンドル操作のみで突破できる。ただ、突入速度が速すぎて1度転倒した。それ以降2速10キロ程度で突入すれば負けなし。
  • 1本橋も最初の1時間こそ脱輪しまくっていたが、youtubeでバランスだけでぎりぎりまで粘っている動画(参考)を見て、粘るイメージを覚えた。次の時間以降、8s~10sくらいで通過できるようになった。
  • ここの2つはどちらも「リアブレーキ+半クラッチ」の意識でいけばわりと難しくなかった。そしてこの時間当たりでようやく力強い発進ができるようになった。(それまではクラッチをゆっくり離す発進をしていた)

課題教習③:クランク・坂道発進

  • クランクは初手から足がついてしまった。また、姿勢がまだぎこちなかったので、右直角に曲がる際に急アクセルをひねってしまい、意図しない加速をしてしまう。突入速度は1速10キロ程度で進路(+視線)を意識して、なんとか通過できるようになった。それでも指導員の方からは断続クラッチ半クラしたりしなかったり)をできておらず、クラッチを切りっぱなしで突破していることを指摘された。これも次の時間ではかなりマシになった

  • 坂道発進は指導員の方から独特の教え方をされた。「アクセルを回して回転数4000→半クラ(回転数2000)→再度アクセスで回転数4000→リアブレーキ離す」 手順書かよ。実際にこのやり方では少し時間はかかるもののエンストはしなかった。時間も許容範囲っぽく難なくクリア。

みきわめ

1段階のみきわめでは前項までの課題をひとつずつ実施していく、特にスラローム一本橋はタイム計測の上問題ないか見られる。奇跡的にクリアした。

~2段階~

2段階では7コマ予定で予定通りに消化した

シミュレーション②・③・危険予測ディスカッション

  • シミュレーション②では公道で法律を守って運転しましょう、を学ぶ
  • シミュレーション③+危険予測ディスカッションでは公道はモラルが低い、を学ぶ

(※四輪持っていれば知っていること)

回避

この授業では - 紅白の旗を持った指導員の方に向かって進んでいき、直前で旗が上がった方向に避ける - 追突体験 - 速いスピードでのカーブ体験 など走行にあたっての回避方法やカーブでの遠心力の影響を体験する ただの体験なのでへぇ~って思えばいいだけの授業

コース走行(急制動&踏切)

  • 2段階では前項の授業に加えてひたすらコース走行を行う。ここではじめて踏切、急制動40キロが登場する。コースを覚えてさえいればひとまず流す形になる。信号・左折・右折・直進・一時停止・指示速度・障害物などいよいよ卒検を意識した形になる。踏切では少し坂になっているので力強い発進をしないとエンストする。
  • 特に苦労したのがこの時間の後半までしっかり40キロ出せなかった急制動。やっと出せても安定して止まれず、繰り返し練習する指示が出るし、みきわめにおいてはまさかのスラロームパイロンにぶつける始末。みきわめとしてはOKだったものの、卒検に対する不安が高まる。

  • この時点での2段階での勝率は下記の通り

スラローム:80% 急制動:60% S字クランク:100% クランク:95% 一本橋:100% 坂道発進:100%

卒業検定

  • 卒検は昼からスタート。まずは検定員の方からコース・減点事項の説明がある。
  • 受験者は14人でA:2人・B:6人・C:6人のでチーム分けされた。自分はCの5番目だった。またA・B・Cは並走することになり、B・Cの6人目のみ後続の教習に影響する可能性がある。 そういう意味では5番目はラッキーだったのかもしれない。一方でA・Bは最初に直線走行があるコースであるがCはいきなりスラロームに突入していくコースとなる。おまけにこの卒検ではならし走行がないためCは少し不利となる。

  • だいたいA・B・C同時並走して、そのうち一人くらいは脱落してくのを待っている時間眺めていた。初手で小型二輪の女性がクランクで接触脱落、第3グループあたりで結構序盤からうまく運んでいたお兄さんが最後の急制動でまさかの距離超過、さらに自分の1つ前のお兄さんが一本橋脱輪・・・しかも1つ前の人がそんな早く終わると思っていなかったので、心の準備ができていない状態で出番が回ってきた。

スラローム→指示速度→障害物→S字→1本橋→クランク→踏切→坂道発進急制動

  • とにかく肩の力を抜くことを意識して、スタンドを払い、着座、ミラー調整、エンジン・ギア・指示器・・・・・もう一度深呼吸して発進、スラロームはかなり慎重に進めた。指示速度は3速30キロで練習していたのに2速で30キロに到達してしまい少しパニック。指示速度直後に、交差点に突入するのだがここで信号に引っかかったのがよかった。この15秒くらいですごく落ち着いた。その後停止前に1回もたついたのがあったが課題周りは完ぺきだった模様。

  • 終了後フィードバックをもえらえるのだが、「公道に出ても落ち着いていけ」というフィードバックだった。合格フラグ。

  • このあと小1時間くらいロビーで待機させられるのだが、脱落者は帰ったのでいない。9人程度が残っていた。そして最後に教室で卒業証明書を受け取り、解散。まさか1発合格するとは思わなかったが、とりあえず卒業できてなにより。

Windsurf AIを使って5分でかんばんwebアプリを作ってみた

Windsurf AIとは

  • Windsurf AIはCodeiumが開発した、次世代のAI統合開発環境IDE)です。 AIエージェントとコパイロット機能を組み合わせ、開発者の生産性向上を支援します。

インストール

codeium.com

ダウンロードサイト

インストール画面

  • すべて「次へ」でインストール開始

完了

初期設定

拡張機能

  • テーマカラーは適当に選択

  • 必要に応じてアカウントを作成してログインする

起動

  • vscodeと同じくJapanese Language Pack for VS Codeを入れれば日本語化できます。

使用

  • 右側のプロンプト入力部分でプロンプトを適当に入力

プロンプト入力

  • 30秒ほどで完成

完了

  • 下記のようなアプリが完成

完成

まとめ

  • こんだけ簡単にできるなら逆にエンジニアリングの枠が広がるのかなあと実感

React+viteとelectronでデスクトップアプリっぽいものを作りたい②

前回記事

tarosharp.hateblo.jp

exe化

  • electron-builderのインストール
npm install electron-builder --save-dev
  • 参考記事

qiita.com

  • package.json
  • package.jsonのbuild項目は、electron-builderを使用してアプリケーションをパッケージする際の設定を指定します。以下はその主な内容です。files: パッケージに含めるファイルやディレクトリを指定します。ここでは、distフォルダ、main.js、preload.js、package.jsonが含まれます。
  "version": "0.0.0", //←ここはsetupに影響する
...
  "scripts": {
    "dev": "vite",
    "build": "tsc -b && vite build",
    "electron": "set NODE_ENV=development && electron .",
    "electron:build": "npm run build && electron .",
    "lint": "eslint .",
    "preview": "vite preview",
    "dist": "electron-builder" // ←ここ追加
  },
...
  // build項目の追加
  "build": {
    "files": [
      "dist/**/*",
      "main.js",
      "preload.js",
      "package.json"
    ]
  }
...

インストーラー作成・実行

$ npm run dist

成果物

  • ここでは「desktoptest Setup 1.0.0.exe」を実行してインストールを実行します。

インストール画面

  • C:\Users\UserName\AppData\Local\Programs\desktoptestにファイル一式が配置されました。また正しく実行されました。

インストールされたもの一式

デスクトップアプリ完成

まとめ

  • プロジェクト作成→electron導入→カスタムタイトルバー作成→electron-builderでインストーラ作成→インストール実行→デスクトップアプリ化という流れで実装

React+viteとelectronでデスクトップアプリっぽいものを作りたい①

経緯

  • その昔デスクトップアプリをWindowsFormで作っていた
  • C#系のWindowsFormやWPFでモダンなUIを表現できないか模索したが難しそう
  • Webアプリをデスクトップアプリ化してはどうか?

プロジェクト作成

  • ノードパッケージマネージャーよりviteでプロジェクト作成を行う
  • 今回は適当にプロジェクト名は「DesktopTest」とした
$ npm create vite@latest

> npx
> create-vite

│
◇  Project name:
│  DesktopTest
│
◇  Package name:
│  desktoptest
│
◇  Select a framework:
│  React
│
◇  Select a variant:
│  TypeScript + SWC
│
◇  Scaffolding project in C:\Users\yamamura\Documents\MyDevelop\DesktopTest...
│
└  Done. Now run:

  cd DesktopTest
  npm install
  npm run dev

npm notice
npm notice New major version of npm available! 10.8.1 -> 11.2.0
npm notice Changelog: https://github.com/npm/cli/releases/tag/v11.2.0
npm notice To update run: npm install -g npm@11.2.0
npm notice
  • 続いて必要なパッケージをインストールします。
npm install

added 140 packages, and audited 141 packages in 25s

40 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities
  • さらに開発環境で実行させます。http://localhost:5173/にアクセスしたら画面が表示されています。
$ npm run dev

> desktoptest@0.0.0 dev
> vite


  VITE v6.2.1  ready in 346 ms

  ➜  Local:   http://localhost:5173/
  ➜  Network: use --host to expose
  ➜  press h + enter to show help

実行画面

※参考

qiita.com

electronで実行できるようにする

  • electronを開発環境にインストール
$ npm install electron --save-dev
  • Electron アプリは「メインプロセス」と「レンダラープロセス」に分かれます。ここでは、React+Vite で作成したフロントエンド(レンダラープロセス)を読み込むためのメインプロセスファイル(例:main.js)を作成します。
  • ./main.js
// main.js
import { app, BrowserWindow } from 'electron';
import path from 'path';
import { fileURLToPath } from 'url';

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

function createWindow() {
    // ブラウザウィンドウの作成
    const mainWindow = new BrowserWindow({
        width: 800,
        height: 600,
        webPreferences: {
            // セキュリティ向上のため nodeIntegration は無効にする
            nodeIntegration: false,
            contextIsolation: true,
        },
    });

    // 開発環境の場合は Vite の開発サーバー、ビルド済みの場合はローカルファイルを読み込む
    if (process.env.NODE_ENV === 'development') {
        mainWindow.loadURL('http://localhost:3000');
    } else {
        mainWindow.loadFile(path.join(__dirname, 'dist', 'index.html'));
    }

}

app.whenReady().then(createWindow);

app.on('window-all-closed', () => {
    // macOS 以外は全ウィンドウが閉じたらアプリを終了する
    if (process.platform !== 'darwin') app.quit();
});

app.on('activate', () => {
    // macOS では Dock アイコンがクリックされた際、ウィンドウがなければ再作成する
    if (BrowserWindow.getAllWindows().length === 0) createWindow();
});

  • package.jsonにビルドコマンド用にelectronとelectron:buildを追加する
  • ./package.json
"main": "main.js", // ←ここを追加
"scripts": {
  "dev": "vite",
  "build": "vite build",
  "electron": "set NODE_ENV=development && electron .", // ←ここを追加
  "electron:build": "npm run build && electron .", // ←ここを追加
}
  • ./vite.config.json
import react from '@vitejs/plugin-react-swc'
import { defineConfig } from 'vite'

export default defineConfig({
  base: './', // ←ここを追加
  plugins: [react()],
})
  • ビルド実行する
 $ npm run electron:build

> desktoptest@0.0.0 electron:build
> npm run build && electron .


> desktoptest@0.0.0 build
> tsc -b && vite build

vite v6.2.1 building for production...
✓ 32 modules transformed.
dist/index.html                   0.47 kB │ gzip:  0.30 kB
dist/assets/react-CHdo91hT.svg    4.13 kB │ gzip:  2.05 kB
dist/assets/index-D8b4DHJx.css    1.39 kB │ gzip:  0.71 kB
dist/assets/index-uZHFss2t.js   186.93 kB │ gzip: 59.03 kB
✓ built in 1.40s

windowありの画面

カスタムタイトルバーの実装

  • デフォルトのままでは嫌なのでカスタムタイトルバーを実装する

  • ./src/CustomTitleBar.tsx

import React from 'react';

interface CustomDragStyle extends React.CSSProperties {
    WebkitAppRegion?: 'drag' | 'no-drag';
}

export const CustomTitleBar: React.FC = () => {
    const handleMinimize = () => {
        window.electronAPI?.minimizeWindow();
    };
    const handleMaximize = () => {
        window.electronAPI?.maximizeWindow();
    };
    const handleClose = () => {
        window.electronAPI?.closeWindow();
    };
    return (
        <div
            style={{
                WebkitAppRegion: 'drag',
                position: 'fixed',
                top: 0,
                left: 0,
                right: 0,
                height: '35px',
                margin: 0,
                padding: 0,
                display: 'flex',
                alignItems: 'center',
                justifyContent: 'space-between',
                backgroundColor: '#f0f0f0'
            } as CustomDragStyle}
        >
            <div
                style={{
                    WebkitAppRegion: 'no-drag',
                    marginLeft: '10px',
                    color: '#000',
                } as CustomDragStyle}
            >
                タイトル
            </div>
            <div style={{ display: 'flex', justifyContent: 'flex-end' }}>
                <button
                    style={{
                        margin: '5px',
                        padding: '0',
                        WebkitAppRegion: 'no-drag',
                        width: '50px',
                        verticalAlign: 'middle'
                    } as CustomDragStyle}
                    onClick={handleMinimize}
                >-</button>
                <button
                    style={{
                        margin: '5px',
                        padding: '0',
                        WebkitAppRegion: 'no-drag',
                        width: '50px',
                        verticalAlign: 'middle'
                    } as CustomDragStyle}
                    onClick={handleMaximize}
                >□</button>
                <button
                    style={{
                        margin: '5px',
                        padding: '0',
                        WebkitAppRegion: 'no-drag',
                        width: '50px',
                        verticalAlign: 'middle'
                    } as CustomDragStyle}
                    onClick={handleClose}
                >X</button>
            </div>
        </div>
    );
};
  • App.tsxで実装
  • ./src/App.tsx
import { useState } from 'react';
import './App.css';
import reactLogo from './assets/react.svg';
import { CustomTitleBar } from './CustomTitleBar';
import viteLogo from '/vite.svg';

function App() {
  const [count, setCount] = useState(0)

  return (
    <>
      <div>
        <CustomTitleBar />
        <a href="https://vite.dev" target="_blank">
          <img src={viteLogo} className="logo" alt="Vite logo" />
        </a>
        <a href="https://react.dev" target="_blank">
          <img src={reactLogo} className="logo react" alt="React logo" />
        </a>
      </div>
      <h1>Vite + React</h1>
      <div className="card">
        <button onClick={() => setCount((count) => count + 1)}>
          count is {count}
        </button>
        <p>
          Edit <code>src/App.tsx</code> and save to test HMR
        </p>
      </div>
      <p className="read-the-docs">
        Click on the Vite and React logos to learn more
      </p>
    </>
  )
}

export default App
  • セキュリティ対策として、レンダラープロセスで Node.js の機能に直接アクセスしないようにするため、preload.js を作成します。たとえば以下のように、レンダラープロセス側に限定した API を公開できます。
  • ./preload.js
const { contextBridge, ipcRenderer } = require('electron');

contextBridge.exposeInMainWorld('electronAPI', {
    minimizeWindow: () => ipcRenderer.send('minimize-window'),
    closeWindow: () => ipcRenderer.send('close-window'),
    maximizeWindow: () => ipcRenderer.send('maximize-window')
});
  • ./src/global.d.ts
export { };

declare global {
    interface Window {
        electronAPI?: {
            minimizeWindow: () => void;
            closeWindow: () => void;
            maximizeWindow: () => void;
        };
    }
}
  • main.jsの更新
import { app, BrowserWindow, ipcMain } from 'electron';
import path from 'path';
import { fileURLToPath } from 'url';

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

function createWindow() {
    // ブラウザウィンドウの作成
    const mainWindow = new BrowserWindow({
        width: 800,
        height: 600,
        webPreferences: {
            // セキュリティ向上のため nodeIntegration は無効にする
            nodeIntegration: false,
            contextIsolation: true,
            preload: path.join(__dirname, 'preload.js'),
        },
        frame: false,
        titleBarStyle: 'hidden',
    });

    // 開発環境の場合は Vite の開発サーバー、ビルド済みの場合はローカルファイルを読み込む
    if (process.env.NODE_ENV === 'development') {
        mainWindow.loadURL('http://localhost:3000');
    } else {
        mainWindow.loadFile(path.join(__dirname, 'dist', 'index.html'));
    }

}

ipcMain.on('minimize-window', (event) => {
    const win = BrowserWindow.fromWebContents(event.sender);
    win?.minimize();
});

ipcMain.on('close-window', (event) => {
    BrowserWindow.fromWebContents(event.sender)?.close();
});

ipcMain.on('maximize-window', (event) => {
    const win = BrowserWindow.fromWebContents(event.sender);
    if (win?.isMaximized()) {
        win?.unmaximize();
    } else {
        win?.maximize();
    }
});

app.whenReady().then(createWindow);

app.on('window-all-closed', () => {
    // macOS 以外は全ウィンドウが閉じたらアプリを終了する
    if (process.platform !== 'darwin') app.quit();
});

app.on('activate', () => {
    // macOS では Dock アイコンがクリックされた際、ウィンドウがなければ再作成する
    if (BrowserWindow.getAllWindows().length === 0) createWindow();
});

カスタムタイトルバー

Timesカーシェア使ってみた

経緯

  • 毎週近くのスーパー銭湯まで自転車で20分かけて通っていた
  • 意外に遠いので冬はしんどい
  • 車で行ってみたいけど買うのは面倒、レンタカーも高い

申込み~審査完了

  • いろいろ調べた結果、近くですぐ車を借りれるTimes カーシェアを選択
  • 公式ホームページからスマホで簡単に申し込めた。申込み時に必要な情報は氏名・生年月日・住所・電話番号と免許証(撮影あり・顔認証あり)の確認とクレジットカードの登録。上記を入力完了後に最短5分~3時間程度で審査が終了する。自分の場合は日曜日の朝だったけど、30分程度で完了した。

申込画面

予約

  • これ以降の利用の予約や解錠などは専用のアプリで行える
  • 予約は利用時間→場所+車種の順番に検索できる
  • この際前後の予約時間や・定員人数・燃料の残量なども確認できる

予約画面

解錠

  • アプリ上からできる(スクショを取り忘れた)

運転・駐車など

  • 最初の解錠・最後の施錠以外は社内のキーボックスに刺さっている本来の車のカギを使う
  • ペーパードライバーの利用者が多いのか、エンジンをかけたり駐車スペースの出入りの際は社内でアナウンスが流れる。また、前回運転時の急発進・急ブレーキの有無なども通知される。運転時は特に哲づ域はなく普通の車と同様に使うことができる

返却

  • 駐車スペースに駐車したうえで、本来のカギをキーボックスに差し込み、車の外に出てアプリ上で施錠・利用終了処理を行うこれはアプリ上でワンタップでできる。

キーボックス

  • 黄色い部分に刺さっている

まとめ

  • 自転車で20分かけてスーパー銭湯に通うのが冬は辛く、車で行きたいが購入やレンタカーは難しいため、近くで利用できるTimesカーシェアに登録。スマホで簡単に申し込み、審査は30分ほどで完了。その後はアプリで予約・解錠・返却まで一連の操作が可能。運転時は社内アナウンスや安全運転通知もあり、安心して利用できた。返却は駐車後にアプリで施錠し終了する手軽さが便利だった。

ロボット掃除機「SwitchBot K10+Pro」買った

経緯

  • その昔(2018年ころ)ロボット掃除機などスマートホーム機器を買ったが全く使い物にならなかった
  • 改めてスマートホーム機器ブームが到来
  • 最近は部屋もきれいなので導入してみた

www.switchbot.jp

購入~到着まで

  • 少し不安があった。購入はamazonなどではなくSwitchbot公式サイトで購入したため、発送が遅くならないか不安だった。Xで検索してみたら遅いというツイートが少なからずあったためこの時点で大幅に到着が遅れると思っていた。が、日曜日に購入して火曜日には発送作業が終わり、水曜に到着。意外に早かった。ただ、在庫状況によっては大幅に遅延するとの報告もあるために、購入前に少し確認したほうがいいかもしれない。

設定まで

  • 設定はコンセントにさして、ネットワークにつなぐという作業がある。この際にSwitchbot公式のアプリを使用することになる。ネットワークの接続は1回目でうまくいかなかった。ネットで調べると電源を再投入することで改善されるという報告があったので、実際に再度電源を入れてみると接続がうまくいった。(※wi-fiソフトバンク光使用だが、そこまで問題はないと思われる)、なお接続に関しては2GHz帯にしか接続できないため注意が必要。

最初のお掃除

  • 最初は部屋を徘徊して、部屋の間取りを把握してくれる。この際邪魔なものがあれば障害物と認識するのでできる限り床からものを排除しておく必要がある。なお充電器には自分で戻る。そのタイミングで吸い取ったごみを充電器付随のごみフィルターボックスに集塵してくれる。この際の音はうるさい。

集塵ボックス

  • それ以降はアプリでワンタップで掃除の指示を出せば、自動で部屋を掃除してくれる。
  • アプリで禁止エリアの指定も可能。

アプリの画面

大きさ

  • そこまで大きくなかった。テレビのリモコンサイズの幅くらい。

大きさ感

掃除レベル

  • 髪の毛や塵・埃レベルはちゃんと吸い込んでくれる。

  • 通常掃除のときは気にならない。集塵の時はうるさい。

充電

  • 広さにもよるが一人暮らしの広さ(1K~1LDKレベル)なら気にならない。ファミリー層のフロアでもそこまで気にならないと思われる。

家事効率

  • 今まで掃除機動かしていた時間が効率化されたので時間の削減にはなった。
  • ワンタップで動かせるので不在時に掃除できる

まだできていないこと

  • google homeとの連携:連携させるためには別途SwitchBothubが必要になる。今は類似品としてnature remoのものを使っているので買い替える必要がありそう・・・

SwitchBot ハブ2www.switchbot.jp

まとめ

SwitchBotのロボット掃除機を公式サイトで購入。発送遅延が心配だったが、予想より早く到着。設定時にWi-Fi接続で一度失敗したが、電源再投入で解決。掃除時は静かだが、集塵時は大きな音がする。間取りを把握して自動で掃除し、充電器に戻る点は便利。ワンタップ操作で不在時にも掃除でき、家事効率が向上。一人暮らしからファミリー層まで充電面も問題なし。Google Home連携にはSwitchBot Hubが必要で、今後導入を検討中。

CORSエラーについて調べてみた

エラーの内容

  • 開発中に以下のエラーに遭遇したのでメモとして残す
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at

オリジンとは

  • オリジン=スキーム (プロトコル)、 ホスト (ドメイン)、 ポート番号 の組み合わせ
  • http://example.com:80ならhttpプロトコルexample.comがホスト、80がポート番号

同一オリジンポリシーとは

  • 同一オリジンポリシー(Same-Origin Policy、SOP) は、ウェブブラウザがセキュリティを確保するために採用している重要なセキュリティモデルの一つです。このポリシーは、異なる「オリジン」間でのリソースやデータへのアクセスを制限することで、悪意のある攻撃(例:クロスサイトスクリプティングXSS)、クロスサイトリクエストフォージェリCSRF))からユーザーを保護します。

許可される操作

  • 同一オリジン内では、以下のような操作が自由に行えます
    • JavaScriptから同じオリジンのAPIにアクセスしてデータを取得・送信する。
    • 同じオリジン内でiframeを使用してコンテンツを埋め込む。
    • 同じオリジンのスタイルシートスクリプトを適用する。

制限される操作

  • 異なるオリジン間では、以下のような操作が制限されます:
    • https://www.example.com から https://api.anotherdomain.com へのJavaScriptによる直接的なデータ取得。
    • http://www.example.com から https://www.example.comプロトコルが異なるため)への直接的なデータアクセス。
    • https://www.example.com:443 から https://www.example.com:8443(ポートが異なるため)への直接的なアクセス。

CORSとは

  • ウェブブラウザが異なるオリジン(ドメインプロトコル、またはポートが異なる場合)間でリソースを安全に共有するための仕組み

CORSヘッダーの利用

  • CORSヘッダーは、ウェブブラウザが異なるオリジン間でのリソース共有を制御するために使用されるHTTPヘッダーです。これらのヘッダーを適切に設定することで、セキュリティを保ちながら必要なデータの共有が可能になります。以下は指定の例。
Access-Control-Allow-Origin: https://example.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Allow-Credentials: true