koneta https://koneta.click DIYとデジモノとプログラミングとライフハックをコネた...小ネタ Sun, 13 Mar 2022 11:52:36 +0000 ja hourly 1 https://wordpress.org/?v=6.1 https://koneta.click/wp-content/uploads/2020/02/cropped-icon-32x32.png koneta https://koneta.click 32 32 HTMLとCSSでサムネイルジェネレータを作ってみました https://koneta.click/p/1028 https://koneta.click/p/1028#respond Sun, 13 Mar 2022 11:52:35 +0000 https://koneta.click/?p=1028 どうも、繁忙期をなんとか乗り越え本ブログを更新できる時間が戻ってきました。しかし、またいつ時間が取れなくなるかはわかりません。そこで、せめてブログの更新時間が減るようにサムネイルを簡単に作れる仕組みを作ってみました。色々作り方はあると思いますが、今回はHTMLとCSSでサムネイルのフォーマットを作り、それを画像化するという方針です。

どんなのができる?

本ブログでは主に2種類のフォーマットでサムネイルを作っているのですが、その中でも一番使っているものを生成できるようにしてみました。

やっていることは、背景画像を半透明の黒で暗くして、そこに半透明の文字抜きの白をのせています。

できたもの

というわけで、このような物ができました。

動作画面イメージ

背景画像の項目で端末内の画像を選択すると背景画像として表示され、その下のタイトルの欄に文字列を入力するとタイトルとして表示されます。基本的に誰にも公開する気はなかったので見た目としては全く手を入れてません。

ちょっとだけコード詳細

大したことはしていませんが、少しだけどんなコードかを書いておこうと思います。

コード全体
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>サムネイルジェネレータ</title>
  <link rel="stylesheet" href="./resource/ress.min.css">
  <script src="https://cdnjs.cloudflare.com/ajax/libs/html-to-image/1.9.0/html-to-image.min.js"></script>
</head>
<body>
  <div class="container">
    <h1>koneta.click サムネイルジェネレータ</h1>

    <div id="thumbnail-background" class="thumbnail">
      <div class="thumbnail__background-black__wrapper"></div>
      <!-- 半透明のテキストエリア テキストはエリアに高さを持たせるためのダミー -->
      <div class="thumbanail__title-dummy__area">
        <p id="p-title-dummy" class="thumbanail__title-dummy__text">タイトルサンプル</p>
      </div>
      <!-- タイトル文字に背景が透過しているように見せるための要素 -->
      <div id="thumbnail-background-dummy" class="thumbanail__title__area">
        <p id="p-title" class="thumbanail__title__text">タイトルサンプル</p>
      </div>
    </div>

    <div class="contoroler">
      <h2>操作</h2>
      <h3>背景画像</h3>
      <input type="file" id="form-thumbnail-background">
      <h3>タイトル(HTML記法でOK)</h3>
      <textarea id="form-text-title">タイトルサンプル</textarea>
      <h3>保存</h3>
      <button id="button-save">画像にする</button>
      <div class="controler__result-img__wrapper">
        <img id="result-image" />
      </div>
    </div>
  </div>

  <script>
    // 背景画像用処理
    const fileInput = document.getElementById('form-thumbnail-background')
    fileInput.addEventListener('change', () => {
      const reader = new FileReader()
      // ファイルが読み込まれたときに実行する
      reader.onload = function (e) {
        const imageUrl = e.target.result
        const thumbnailBackground = document.getElementById("thumbnail-background")
        thumbnailBackground.style.backgroundImage = `url(${imageUrl})`

        const thumbnailBackgroundDummy = document.getElementById("thumbnail-background-dummy")
        thumbnailBackgroundDummy.style.backgroundImage = `url(${imageUrl})`
      }
      reader.readAsDataURL(fileInput.files[0])
    })

    // タイトル用処理
    const textInput = document.getElementById('form-text-title')
    textInput.addEventListener('change', () => {
      // 高さ産出用と背景を見せる用の要素にテキストを設定する
      document.getElementById('p-title-dummy').innerHTML = textInput.value
      document.getElementById('p-title').innerHTML = textInput.value
    })

    // 画像化関係処理
    const saveButton = document.getElementById('button-save')
    saveButton.addEventListener('click', () => {
      const thumbnail = document.getElementById('thumbnail-background')
      htmlToImage.toJpeg(thumbnail)
        .then(function (dataUrl) {
          document.getElementById('result-image').src = dataUrl
        })
        .catch(function (error) {
          console.error('oops, something went wrong!', error);
        });
    })
  </script>
  <style>
    @font-face {
      font-family: 'NikumaruFont';
      src: url(./resource/nikumaru.otf);
    }
    .container {
      width: 100%;
      max-width: 1000px;
      margin: 0 auto;
    }
    .thumbnail {
      width: 100%;
      aspect-ratio: 16 / 9;
      position: relative;
      background-color: #000000;
      background-size: cover;
      background-position: center;
    }
    .thumbnail__background-black__wrapper {
      width: 100%;
      height: 100%;
      position: absolute;
      top: 0;
      left: 0;
      z-index: 10;
      background-color: rgba(0, 0, 0, 0.3);
    }
    .thumbanail__title-dummy__area {
      width: 80%;
      padding: 3% 0;
      position: absolute;
      top: 50%;
      left: 50%;
      z-index: 20;
      transform: translate(-50%, -50%);
      background: rgba(255, 255, 255, 0.9);
      outline: 6px solid rgba(255, 255, 255, 0.9);
      outline-offset: 4px;
      text-align: center;
    }
    .thumbanail__title__area {
      width: 100%;
      height: 100%;
      display: flex;
      justify-content: center;
      align-items: center;
      text-align: center;
      position: absolute;
      top: 0;
      left: 0;
      z-index: 30;
      background-size: cover;
      background-position: center;
      background-clip: text;
      -webkit-background-clip: text;
    }
    .thumbanail__title-dummy__text {
      font-size: 3rem;
      font-family: NikumaruFont;
      color: transparent;
    }
    .thumbanail__title__text {
      font-size: 3rem;
      font-family: NikumaruFont;
      color: rgba(0, 0, 0, 0.3);
    }
    .controler__result-img__wrapper {
      width: 100%;
    }
    .controler__result-img__wrapper img {
      width: 100%;
    }
  </style>
</body>
</html>

今回のコードではサムネイルで使っているフォントやcssのリセットを読み込んでいますが、それらはライセンスの関係もありここでは載せていません。フォントはこちらのにくまるフォント、リセットにはress.cssを使っています。もし、そのまま当サイトと同じフォーマットのサムネイルを作りたい場合は上記の2つをresourseディレクトリに設置してからアクセスしてみてください。

ちなみにフォントやサムネイルの画像素材など、本サイトで使わせてもらっている素材たちはこちらのページにまとめていますので、気になった方はこちらをご覧ください。

要所としては入力された画像やタイトルをサムネイルに反映する部分だと思います。値が入力されたときそれを画面に反映しようと思ったとき、最近ではVueなどのフレームワークがうかぶかもしれませんが、今回のツールくらいであれば直書きで十分です。

まずは画像が選択されたときの処理です。

const fileInput = document.getElementById('form-thumbnail-background')
fileInput.addEventListener('change', () => {
  const reader = new FileReader()
  // ファイルが読み込まれたときに実行する
  reader.onload = function (e) {
    const imageUrl = e.target.result
    // 選択された画像を要素の背景に設定する
    const thumbnailBackground = document.getElementById("thumbnail-background")
    thumbnailBackground.style.backgroundImage = `url(${imageUrl})`
  }
  reader.readAsDataURL(fileInput.files[0])
})

これで画像読み込みはオッケーです。次にテキスト部分は以下のようになります。

const textInput = document.getElementById('form-text-title')
textInput.addEventListener('change', () => {
  document.getElementById('p-title').innerHTML = textInput.value
})

テキストに関してはこれだけです。これだけの記述量ですみ、環境構築も不要な点でもフレームワークは不要だと思います。これでサムネイル画像完成です。

もしこのコードから改造を行う場合は、サムネイルのフォーマットを作っているcssと上記のjsで変更する対象を調整すればいいと思います。

終わりに

もともとサムネイルを作る際はGIMPというフリーソフトを使っており、作業時間は30分ほどかかっていました。それがこのジェネレータを使うことで5分程度でサムネイルを作れるようになりました。また、webページとして作成したため、作業できる端末がGIMPをインストールしているデスクトップやMacBookに限られていたところを、主にブログ更新に使っているChromebookでもできるようになったのが大きいところです。

実はボタンを押すだけで画像を生成するところまで実装しようと思っていたのですが、どうやらいま出ているライブラリでは対応していないcssを使ってしまっているためうまく生成してくれませんでした。そのため今はフォーマット完成後スクリーンショットを撮るという微妙な形になっています。そのうち時間ができたらここらもどうにかしていきたいと思います。

あとは他のサムネイルもサクッと作れるように更新していこうと思います。

]]>
https://koneta.click/p/1028/feed 0
無料でSupabaseのバックアップを取得してみました https://koneta.click/p/1018 https://koneta.click/p/1018#respond Fri, 11 Mar 2022 12:47:40 +0000 https://koneta.click/?p=1018 こんにちは、Supabaseって便利ですよね。Firebaseの代替を狙っているだけあり、無料でDBや会員管理、ストレージなどFirebaseにも負けず便利です。ほとんどの場合は無料で十分だと思います。

しかし、無料で1つだけ物足りない点があります。それがDBのバックアップです。残念ながらバックアップ機能は有料で月$25以上を払わないといけません。そこで今回はPythonでSupabaseのバックアップ/リストアを行うスクリプトを書いたので記事にまとめておこうと思います。

私の用途ではそのうち容量も足りなくなる見込みなので有料プランに変更する見込みなので、みなさんも一時的なものとして見てみてください。

環境準備

本記事ではバックアップの処理をPythonで書くので、その環境を用意します。今回はDocker上のPythonで雑に用意しようと思います。ちなみにここで書くDocker設定は他のスクリプトを書いたときのものを流用しているため、不要な設定などがあるかもしれないので参考までにご覧ください。

というわけでDockerに必要なファイルを用意します。ディレクトリの連携も雑に対応していくためdocker-compose.ymlDockerfileそしてPythonでインストールするライブラリを記述するrequirements.txtを用意します。正直なところ必要なライブラリはSupabaseだけなのでテキストファイルを用意するっ必要もないですが、おまけです。

version: '3'
services:
  python:
    container_name:  supabase-python-backup
    build: .
    volumes:
      - .:/workspace
    tty: true
FROM python:3.9.10

RUN apt-get update

WORKDIR /workspace
COPY ./requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt
supabase==0.3.0

ちなみにSupabaseのPython向けライブラリは、現在まだベータ版とのことなので、新しいバージョンが出た際は指定バージョンを変更するようにしてください。

これらと次に紹介するスクリプトを同じディレクトリに設置して下準備完了です。今回はDocker環境で動かすためにこのようにしましたが、supabaseのライブラリが動けば何でも大丈夫です(直接叩けばライブラリすら不要です)。

APIキーを準備

次にSupabaseの操作を行うためのAPIキーを取得します。 Supabaesの管理画面にログインしてメニューのSettings→APIの項目からProject API keysのservice_roleConfigurationのURLをメモしておいてください。

APIキーの位置

スクリプトの用意

さて、次は本題のスクリプトです。今回は雑にクラスにまとめているのでそれだけ掲載しておこうと思います。git?知らない子ですね。

import os
import shutil
import pickle
from supabase import create_client, Client

os.environ['OAUTHLIB_INSECURE_TRANSPORT'] = '1'

class supabaseBackup():
  def __init__(self, supabase_api_url:str, supabase_api_key:str):
    self._supabase: Client = create_client(supabase_api_url, supabase_api_key)

    # エクスポート出力先ディレクトリ
    self._FILE_DIR = './backup'
    
    # Supabaseで設定している1回のクエリの取得件数上限
    self._MAX_ROWS = 1000


  def _save(self, table_name:str, data:any):
    """ データをファイル出力する
    Args:
      table_name (str): 出力するテーブル名
      data (any): 出力するデータ
    """
    with open(f'{self._FILE_DIR}/backup-{table_name}.pkl', 'wb') as f:
      pickle.dump(data, f)

  def _load(self, table_name:str):
    """ ファイルからテーブルのデータをインポートする
    Args:
      table_name (str): インポートするテーブル名
    Return:
      any: ファイルから読み込んだデータ
    """
    with open(f'{self._FILE_DIR}/backup-{table_name}.pkl', 'rb') as f:
      return pickle.load(f)


  def copy_to_date_directory(self):
    """ ディレクトリに保存されているpklファイルを日付ディレクトリに移動する
    """
    import datetime
    date = datetime.datetime.today().strftime("%Y%m%d%H%M%S")
    dir = f'{self._FILE_DIR}/{date}'

    os.makedirs(dir, exist_ok=True)

    # 既存の pkl ファイルを日付ディレクトリにコピーする
    import glob
    for p in glob.glob(f'{self._FILE_DIR}/*.pkl'):
        shutil.copy(p, dir)

  def supabase_backup_table(self, table_name:str):
    """ Supabase の指定テーブルのデータをすべてファイルに出力する
    Args:
      table_name (str): 出力するテーブル名
    """
    i = 0
    rows = []
    while True:
      offset = self._MAX_ROWS * i
      data = self._supabase\
                 .table(table_name)\
                 .select('*')\
                 .limit(1000, start=offset)\
                 .execute()\
                 .data
      if len(data) == 0:
        break
      else:
        i += 1
        rows += data

    self._save(table_name, rows)

  def supabase_restore_table(self, table_name:str, import_to_table_name:str = ''):
    """ Supabase の指定テーブルにファイルからデータをインポートする
    Args:
      table_name (str): インポートするテーブル名
      import_to_table_name (str): インポート先のテーブル名(ステージング環境テーブルにインポートする場合に使用する)
    """
    if import_to_table_name == '':
      import_to_table_name = table_name

    import_data = self._load(table_name)

    for i in range(0, len(import_data), self._MAX_ROWS):
      self._supabase.table(import_to_table_name).insert(import_data[i:i+self._MAX_ROWS]).execute()

  def supabase_clean(self, table_name:str):
    """ Supabase の指定テーブルのデータを空にする
    Args:
      table_name (str): データを空にするテーブル名
    """
    self._supabase.table(table_name).delete().execute()

さて、コードを書いてしまったのでこれ以上書くことはないのですが、実装時に少しハマったところは取得件数上限が1000件なので、それ以上にレコードがある場合はそれだけクエリを実行しなくてはいけないという点です。しかし、クエリ結果のDLは制限なしというのは嬉しいですね。

使ってみる

最後にこのスクリプトを使ってみようと思います。上記でクラスの定義はできているので、使うだけです。

if __name__ == '__main__':
  # Supabase
  SUPABASE_API_URL: str = 'TODO Supabase_URL https://xxxxxxxxxxxxxxxxxxxxxxxxx.supabase.co'
  SUPABASE_API_KEY: str = 'TODO SUpabase_KEY xxxxxxxxxxxxxxxxxxx......xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'

  backup = supabaseBackup(SUPABASE_API_URL, SUPABASE_API_KEY)


  # エクスポート
  backup.supabase_backup_table('TODO エクスポートするテーブル名')

  # 保存されているバックアップファイルを日付付きのディレクトリにコピー
  backup.copy_to_date_directory()

  # ステージング環境のテーブルをクリア
  backup.supabase_clean('TODO インポートするテーブル名')

  # ステージング環境にリストア
  backup.supabase_restore_table('TODO インポートするテーブル名', 'TODO インポート先のテーブル名')
$ docker exec -it supabase-python-backup python main.py

このスクリプトで、SupabaseのDBからデータを全件エクスポートして、インポートするという一連の流れが実行されます。設定値をベタ書きするなとか、バックアップファイルの扱い方とかツッコミ所はありますが、私の要望は満たしてくれているので一旦おいておきます。

終わりに

というわけで、無料プランのままバックアップを取る方法を書いてみました。もうDBの容量が8割くらいまで行っているのですぐ有料バックアップを使えるようになりますが、無料で使えるうちは使い倒してやりますよ!

]]>
https://koneta.click/p/1018/feed 0
スプレッドシート+GAS+GCPで簡易的なSSL証明書期限監視システムを構築してみました https://koneta.click/p/990 https://koneta.click/p/990#respond Sat, 15 Jan 2022 12:33:19 +0000 https://koneta.click/?p=990 昨今はLet’sさんやらホスティングサービスで勝手に更新してくれることの多いSSL証明書ですが、意外と手動で更新しなくてはいけないこともあります。

そして、更新ができないとサイトにアクセス時にエラーが表示されてしまい良くないです。

そこで、ちゃんと証明書の更新ができているかを定期的に確認する必要があるのですが、手動ではどうしても忘れやすく、とてもめんどくさいです。

というわけで、 今回は簡易的に証明書の有効期限を確認する仕組みを構築してみようと思います。今回はGCPのCloud RunでfastAPIの処理を動かし、監視するサイトリストをスプレッドシートで管理できるように作ってみようと思います。

指定サイト証明書の有効期限を返すAPIを作成する

まずは指定されたサイトURLの証明書有効期限を返すAPIを作成します。

この工程ではGCPのCloud Run上に確認用の処理をデプロイします。基本的には無料で何とかなる範囲ですがクレジットカードなど課金情報の登録が必要になります。もし課金情報の入力が嫌な場合は、有志が作成しているAPIを使ってみたり、各サイトの証明書期限を手動で入力するようにすれば、この工程は不要になります。

さて、では早速やっていきたいと思います。今回は必要最低限のところだけ書いていこうと思います。

処理の準備

まずはDockerfileの準備です。基本的に動作に必要なものがインストールできれば問題ないのですが、CloudRunの仕様上8080ポートでアクセスできるようにする必要があります。また今回はpipでインストールものをまとめておけるようにrequirements.txtに記述して一括でインストールするようにしておきました。

FROM python:3.7.12

COPY ./apps /apps
WORKDIR /apps

COPY ./requirements.txt ./
RUN pip install -r requirements.txt

CMD ["uvicorn", "main:app", "--reload", "--host", "0.0.0.0", "--port", "8080"]
fastapi==0.63.0
uvicorn==0.13.3
pyOpenSSL==21.0.0
pytz==2018.9

次にfastAPIの処理を実装していきます。今回はfastAPIについては全く触れず処理のみ掲載します。とりあえずコピペしていただければ動くと思います…。

#coding: utf-8
import uvicorn
from fastapi import FastAPI
from routers import CertificateExpiration

app = FastAPI()

@app.get("/")
def get_root():
    return {"message": "Hello World"}

app.include_router(CertificateExpiration.router)
#coding: utf-8
from urllib.parse import urlparse
from fastapi import APIRouter
from pydantic import BaseModel
import datetime
import pytz
import OpenSSL
import ssl

router = APIRouter()
    

@router.get("/certificate-expiration")
async def get_certificate_expiration(url: str):
    try:
        # URLからドメインを抜き出す
        parsed_url = urlparse(url)
        domain = parsed_url.netloc

        cert = ssl.get_server_certificate((domain, 443))
        x509 = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, cert)
        expiration = x509.get_notAfter()

        # 表記を調整
        expiration = expiration.decode('utf-8')
        expiration = f'{expiration[0:4]}-{expiration[4:6]}-{expiration[6:8]}'

        return {"expiration": expiration}
    except:
        # エラーが発生したので今日の日付を返す
        return {"expiration": datetime.datetime.now(pytz.timezone('Asia/Tokyo')).strftime("%Y-%m-%d")}

ファイル構造はこのようになります。

/
│  Dockerfile
│  requirements.txt
│
└─ /apps
   │  main.py
   └─ /routers
            CertificateExpiration.py

今回やることだけであれば、main.pyCertificateExpiration.pyを分ける必要もないのですが、将来的にこのシステムにほかのAPIも追加していきたいと考えているため、今後処理を追加しやすいように分割して実装しています。

Cloud Runのプロジェクトを作成

処理が書けたので次はデプロイ先であるCloudRunの準備を進めます。まずはCloudSDKのインストールです。OSによって方法がちょっと違うので、公式のマニュアルを参考にしてください。

インストールができたらGCPの管理画面へアクセスし、プロジェクトを作成します。

手順としては管理画面にログインすると画面上部のプルダウン(下記画像内では「プロジェクトの選択」)があり、その中の「新しいプロジェクト」から作成できます。作成時にプロジェクIDが表示されているのでこれをメモしておいてください。

画面イメージ

初めてCloudRunを使う場合は決済情報を登録する必要がります。これはハンバーガーメニューの「お支払い」から登録することができます。これでGCP管理画面の操作はおしまいです。

次に端末から下記のコマンドを実行しプロジェクトを選択しておきます。

$ gcloud init

あとは画面に表示される項目に合わせて登録していけば準備完了です。

デプロイ

プロジェクトの準備ができたのでいよいよデプロイです。デプロイは下記のコマンドで実行できます。プロジェクトIDの部分は、上記手順で確認したそれぞれのIDに書き換えてください。

$ gcloud builds submit \
     --tag gcr.io/[プロジェクトID]/backend-app
$ gcloud run deploy [プロジェクトID] \
     --image gcr.io/[プロジェクトID]/backend-app \
     --region asia-northeast1 \
     --platform managed \
     --allow-unauthenticated

これらのコマンドはシェルなどにまとめておくと今後デプロイするときの役に立つのでオススメです。ステージング環境にアップロードしたり自動でトラフィックの切り替えたくない場合は適宜書き換えちゃってください。

監視するサイトリストを作成する

さて、下準備は完了したので次はスプレッドシート上での作業になります。いつも通りにシートを作成してください。シートの内容は以下の通りです。証明書期限の項目は処理で自動入力されるため、事前に必要なのはサイト名とURL、通知フラグ、そして式を残日数に入力します。ちなみに通知フラグのチェックボックスはメニューの挿入→チェックボックスから入れることができます。

シート入力イメージ

残日数に入力する式は以下のようにD列の日付から証明書の期限が切れる残日数を計算します。おまけで証明書期限が入力されていない場合は空白になるようにしています。

=IF(D3="","",(IFERROR(DATEDIF(TODAY(), D3,"D"),0)))

もしシートの構造を変えたい場合はこちらの式を次に紹介する監視処理の値を修正してください。

サイトリストの期限を確認する処理を実装する

サイト監視リストができたので最後に監視を行う処理を用意して作業完了です。監視にはGASを使います。画面上メニューの拡張機能からApps Scriptを選んでください。入力する処理は以下の通りです。2箇所通知の送信先メールアドレスと上記で作成したAPIのURLはそれぞれの環境に合わせて書き換えてください。


// 通知先のメールアドレス
const NOTIFICATION_TO_ADDRESS = "[送信先メールアドレス]"

// API URL
const API_URL = '[API URL]'

// 通知を送信する残日数リスト
// ※ 最小の日付からは毎日通知する
const NOTIFICATION_EXPIRATION_DATE_LIST = [7, 30, 45];
const MIN_EXPIRATION = Math.min.apply(null, NOTIFICATION_EXPIRATION_DATE_LIST);

const ROW_ID_TABLE_START = 2; // サイトリストが始まる行数

const COLUMN_ID_SITE_NAME  = 1; // サイト名を掲載している列番号
const COLUMN_ID_SITE_URL   = 2; // サイトURLを掲載している列番号
const COLUMN_ID_EXPIRATION = 4; // 有効期限残日を算出している列番号
const COLUMN_ID_SEND_FLG   = 5; // 送信フラグを指定している列番号

/* 証明書の有効期限を確認する
 *
 */
function checkCertificateExpiration(url) {
  const requestUrl = `${API_URL}?url=${url}`
  const response = UrlFetchApp.fetch(requestUrl)
  const responseCode = response.getResponseCode()
  const responseText = response.getContentText()

  if (responseCode === 200) {
    const responseTextObject = JSON.parse(responseText); 
    return responseTextObject['expiration'];
  } else {
    // 有効期限が取得できなかったので今日の日付を入れておく
    return Utilities.formatDate( date, 'Asia/Tokyo', 'yyyy-MM-dd'); 
  }
}


/* 通知する
 * @param array 行の内容
 */
function sendNotifiication(rows) {
  notificationText = "";
  for (let i in rows) {
    const row = rows[i];
    notificationText += `サイト名: ${row[COLUMN_ID_SITE_NAME]} 残日数: ${row[COLUMN_ID_EXPIRATION]}日\nURL: ${row[COLUMN_ID_SITE_URL]}\n`;
  }

  const subject = "【重要】 SSL証明書失効警告";
  const body = `SSL証明書失効期限が迫っています!\n\n${notificationText}`;

  MailApp.sendEmail(NOTIFICATION_TO_ADDRESS, subject, body, {
    noReply: true
  });
}

/* リストの残日数を確認して指定された日付だった場合は通知する
 *
 */
function checkExpiredDate() {
  const sheet = SpreadsheetApp.getActiveSheet();
  const rows = sheet.getDataRange();
  const numRows = rows.getNumRows();
  let values = rows.getValues();

  //  証明書の有効期限を更新する
  for (let i = ROW_ID_TABLE_START; i <= numRows - 1; i++) {
    const row = values[i];

    // 通知送信フラグがONのもののみ有効期限を更新する
    if (row[COLUMN_ID_SEND_FLG]) {
      const certificateExpiration = checkCertificateExpiration(row[COLUMN_ID_SITE_URL]);
      sheet.getRange(i+1, COLUMN_ID_EXPIRATION).setValue(certificateExpiration);
    } else {
      sheet.getRange(i+1, COLUMN_ID_EXPIRATION).setValue('');
    }
  }

  // 有効期限を更新したので残日数を再計算する
  SpreadsheetApp.flush();
  values = rows.getValues();


  // 送信するサイトの列情報リスト
  let notificationRows = [];

  for (let i = ROW_ID_TABLE_START; i <= numRows - 1; i++) {
    const row = values[i];

    // 通知送信フラグがONのもののみ判定する
    if (row[COLUMN_ID_SEND_FLG]) {
      if (NOTIFICATION_EXPIRATION_DATE_LIST.indexOf(row[COLUMN_ID_EXPIRATION]) !== -1) {
        // 指定日数の場合は通知リストに追加する
        notificationRows.push(row)
      } else if (row[COLUMN_ID_EXPIRATION] <= MIN_EXPIRATION) {
        // 指定日数の最小日からは毎日通知リストに追加する
        notificationRows.push(row)
      }
    }
  }

  // 通知対象のサイトがある場合は通知を行う
  if (notificationRows.length > 0) {
    sendNotifiication(notificationRows);
  }
}

実行時にはcheckExpiredDate関数を選択して実行ボタンを押します。初回はスプレッドシートにアクセスする権限やメールを送信する権限など許可が求められます。許可画面では一度警告画面が表示されますが、詳細からすすめることができます。

最後に通知確認のため、証明書の期限残にあわせてNOTIFICATION_EXPIRATION_DATE_LISTで設定している日数を調整して実行してみてください。「【重要】 SSL証明書失効警告」というメールが送信されていれば作業完了です。

終わりに

というわけで今回は、ほぼスプレッドシートで証明書監視機能を構築してみました。これまでGASはなんとなくダサい感じがしていて食わず嫌いをしていたのですが、今回触ってみてそのお手軽さにハマってしまいそうです。今後も色々小さいアプリを作って行きたいと思います。

]]>
https://koneta.click/p/990/feed 0
2021年下半期買ってよかったもの ランキングで9選 https://koneta.click/p/855 https://koneta.click/p/855#respond Sat, 11 Dec 2021 14:16:46 +0000 https://koneta.click/?p=855 あれよあれよと気温が下がり、早いものでもう冬ですね。今回も購買欲がそれなりに燃え盛ってくれたので、いろいろ買ってしまいました。というわけで2021年下半期に買ってよかったものリストをランキング形式で書いていこうと思います。

前回の7選からから増えまして今回は9選、加えて項目内でもいくつか紹介しているため、前回と比べるとより盛りだくさんです。例のごとくジャンルはかなりごちゃまぜですがどうぞ見ていってください。

9位 バックハンガー – Clipa 2

まずは便利な小物、バックハンガーのClipaからです。元々は楽天市場の買い回りセールに合わせて数合わせに購入してしまったものですが、思っていた以上の活躍をしてくれました。

お値段的にはもっとお安いバックハンガーもありますが、デザイン的にシンプルかつ、PCを入れたバッグでも安心して吊るすことができる堅牢性を求めた結果こちらを選んでみました。

私はこのハンガーを常にカバンに入れておくようにしています。正直、普段は存在も忘れていますが外出時には意外と登場の機会は多く、床にバッグを置かなくていいというのは、衛生的にも気持ち的にもうれしい限りです。

特殊な機能があるわけではありませんが、ちょっとしたところで活躍してくれたのでランクインです。

8位 クッキングバーナー

お次は食べ物関係枠です。前回の食べ物関係枠はビール用品、ポップコーンメーカーというお手軽系アイテムでしたが、今回はよりおいしくするためのアイテムです。

こちらはカセットコンロで使うガス缶を装着することで食品をあぶることができるバーナーです。これであぶることでお安めの食品でも意外とおいしくなると我が家で話題になっています。特にお寿司をあぶって食べるのがおいしかったです。

余談ですが、あぶりが良さそうと知ったきっかけはオモコロさんの動画です。オモコロさんならではの一風変わった食品でもあぶりを試しているので是非とも見てみてください。(オモコロさん、いつも面白いコンテンツありがとうございます!!)

バッグハンガーと同じく、こちらも楽天買い回りセールの影響で買ってしまったものですが、思っていた以上においしかったので買ってよかったものに見事ランクインしてくれました。これからもいろんなものをあぶりつくしたいと思います。

7位 珪藻土コースター

お次は便利小物、DAISOさんで購入した珪藻土コースターです。

こちらのコースターは100円商品で、白くシンプルな見た目にひかれて購入しました。性能はいわゆる珪藻土の機能を持っていて、期待していた役割をそのまま果たしてくれました。現在、頑張ってシンプルなデスクを目指しているのですが、このコースターはいい意味で存在感のなさが、ほかのアイテムとなじんでいていい感じです。

最近リピートしました!

ちなみに、このコースターは裏面も珪藻土素材そのままなので結構堅めです。そこで、私はコースターの裏面に滑り止めテープを張り付けることで滑りにくくしつつ、柔らかくなるようにしています。傷が目立ちやすい机を使っている方にはテープを張っておくのをお勧めしておきます。

6位 キーボード – Logicool K380

お次は、たった今この記事の文字も入力しているキーボードです。

私はいままで、メンブレンに始まり、リアルフォースやNIZのような静電容量無接点方式など色々なキーボードを試してきましたが、この商品でひと段落しました。万円越えの高級キーボードも試してきたわけですが、まさか数千円のお安いキーボードで解決してくれるとは思いませんでした。

私の要望…という名の好みでは、キーは軽く、ストロークは浅く、テンキーは不要、あとは無線対応でWindows, Macに対応したマルチペアリングかつ切り替えが簡単という視点で探していました。そこで出会ったのがこのK380君でした。なんといっても要望をすべて叶えてくれたので文句なしのランクインです。

ひとつ悲しかったのは、この商品を購入した翌月くらいに、いい感じの新製品が出たことです。新製品の方は万円越えの商品ですが、見た目の好みでは新製品の方がいい感じなのでいつか試してみたいなと思います。

5位 格安SIM – donedone エントリープラン

お次は一風変わりまして、格安SIMがランクインです。なんとこちら、低速なら無料で使うことができるSIMです。

ただ…まあ…まず大前提ですが…無料のエントリープランでは普通の使い方はできません。それくらいにとっても遅いです。普通のWebページを表示するだけでも数十秒かかってしまいます。

ただdonedoneさんは大容量プランだったり、特定のアプリ上限をなくすことができるプランが選択できたり、将来的には一日限定で速度条件を解除するチケットを購入できるようにするサービスを提供予定です。そのため通常用途で使いたい方は別プランで契約したりチケットを待ってみてください。回線はauなので圏外を気にすることは少ないと思います。他に特徴的な部分としては、名前の通り利用料金の一部が社会貢献になっているようです。

さて、そんなdonedoneさんですが私はエントリープランで使っています。もちろん普段使いはできませんが、Faucetサイト (仮想通貨のポイントサイトみたいなやーつ) で活躍してくれています。私は freebitco.in というサイトを使っていますが、違う回線があるとそれだけより活用することができます。

細かい話はおいといて…つまり無料で私の要望を満たしてくれたので見事ランクインです。

4位 モバイルルーター – 富士ソフト FS040W (+楽天モバイル)

まさかの連続でモバイルネット環境ですが…気にしないでください。

私はこのモバイルwifiルータに楽天モバイルのSIMをさすことで運用しています。いまだ外出する機会は少ない昨今ですが、数少ない外出時はこのルータとー以前紹介したChromeOSのノートPCで外でも快適に作業することができます。

また先ほどのdonedoneさんと同じように楽天モバイルさんも1GBまでは0円で持っておくことができますので、外出しない時はそのまま置いておき、必要な時だけ使うという使い方が存せず便利です。

正直なところ、夏にこちらの商品を購入してから3回くらいしか外で使えていませんが、見事ランクインです。

3位 イヤホン – SONY WF-1000XM4

お次は私の作業環境を改善してくれた耳栓…ではなくワイヤレスイヤホンです。

こちらのイヤホンは以前記事でも紹介させてもらったのですが、なんといってもノイキャンが優秀です。仕事をしているとオフィスでは他の人のタイプ音、在宅では家族の生活音と、作業中に音が気になる場面は結構あります。

そこで活躍するのが耳栓もといノイズキャンセリング機能が付いたイヤホンですが、こちらのイヤホンはノイキャン性能が高く私の気になっていた音問題を見事解決してくれました。

詳しくは上記の記事をご覧いただけると幸いですが、お悩みを解決してくれたのでもちろんランクインです。

2位 学習机改善セット(仮名)

次も以前記事を書かせていただいたものになりますが、学習机改善セット(仮)です。

こちらも詳しくは記事をご覧いただけると…になりますが、こちらは天板と家具の高さを上げる継足そしてフットレストのセットになります。そのようなセットがまとまって発売されてるわけではありませんが、上記の記事で紹介させていただいた商品をまとめてセットと書かせていただきました。

学習机は頑丈なもので…また思い出も詰まっているので、手放したくなかったのですが、このセットを設置することで使い勝手を良くすることができたので安堵しつつランクインです。

1位 モニター&アーム- BenQ EW3280U & エルゴトロン LX デスクマウントアーム

というわけで、今回の買ってよかったものランキング1位はモニターとモニターアームです。

私はよくPCで音ゲーを良くプレイするのですが、自宅の小さいモニターでプレイしていると、ゲームセンターの大きなモニターでプレイした時に違和感を感じるようになってしまいました。

別に大会にでるような実力でもないので、そのままでもよかったのですが、使っているモニターが結構古くなっていたこともあり新調しよう、そうなればアーケード版と同じくらいのサイズにしようということでこちらのモニターを購入しちゃいました。

同じくらいのサイズだと他にもいろいろな商品がありますが…私の要望としては、仕事でも使用するためMacBookとケーブル1本で接続できる、別途スピーカーを置きたくないのでモニター内蔵という点から候補を絞っていき、このモニターにたどり着きました。

結果としては買ってよかったものランキングの一位ということで、大満足しています。音ゲープレイは満足いく環境になりましたし、仕事で MacBookを使うときはtype-cケーブル1本で映像も充電もできるのでかなりデスク周りのケーブル環境を改善することができました。

そして、モニターと一緒に購入したのがモニターアームです。以前の買ってよかったものランキングで別のアームを紹介しているのですが、そちらは基本的に一度位置を決めた後はモニターを動かさない用途に向いている商品でした。一方こちらは設置した後も簡単にモニターの位置や高さを動かすことができます。

先ほども書いた通り、私は同じ画面で仕事と音ゲーをしています。そして音ゲーは、せっかくアーケード環境に近づけたので、立ってプレイしたいのです。そうなると仕事中は座って、ゲーム時は立った状態でモニターを使いたくなります。というわけで以前のモニターアームでは切り替えに手間がかかってしまうので、このモニターアームを新たに購入しました。

こちらも結果としては大正解で、用途に合わせてモニター位置を簡単に調整できますし、ケーブルを内部に隠すこともできるようになったので見栄えもだいぶ良くなりました。

と、いうわけで仕事でも趣味でも大活躍してくれたモニター&アームが見事1位でした。

おわりに

さて、今回は9選ということでこれまでのランキングよりも品数が多かったです。下半期は私自身がかなり忙しくその分購買欲が爆発してしまいました。今思うとかなり散財してしまいましたが、それだけ生活しやすさポイントがだいぶ上がったと思いますので後悔はしていません。

というわけで今回はこのあたりで終わります。これからも良きものが手に入ったら紹介していきますのでよろしくお願いします。

]]>
https://koneta.click/p/855/feed 0
置くだけグッズで大人になっても学習机を使ってます! https://koneta.click/p/856 https://koneta.click/p/856#respond Sun, 28 Nov 2021 15:27:53 +0000 https://koneta.click/?p=856 どうも在宅ワークなご時世が続いていますね。となると自宅のデスク周りがどんどん強化されるわけで…。最近では動画サイトやSNSでも在宅ワークで強化されたデスク周りが多く紹介されています。その中でも机に注目すると、天板が斜めになっていたり、昇降機付きだったりと…今の机は、ただの作業台とは一線を画す存在になっています。

一方、私が使っている机は、小学生のころから使っているいわゆる「学習机」です。学習机はいくつかのデメリットはあるものの、その頑丈さはそこらの机とは格が違い、まだまだ使い続けられる、この机を手放してまで別の机を手に入れるのは無駄な気がしていました。

そこで、今回は学習机にいろいろアイテムを加えることで、大人でも使える、在宅ワークでも問題ない作業環境を構築していこうと思います。

学習机の問題点

まずはそのままの学習机でどのような問題点があるかを書いておこうと思います。今回の学習机は私が小学生のころから使用している普通の学習机で、今の使い道としては主にPC作業やちょっとした書き物という想定です。

全体的に低い

私の学習机はもともと小学生用ということもあり、全体的に低いです。一番気になるのは天板で、低いがために作業をしていると前かがみになってしまいます。

また、天板以外でも高さに関わるものとしては、足が机の天板裏の引き出しに当たってしまうという点も気になります。

学習机には引き出しがついているものが多いと思いますが、その分床からの高さがなくなり足が当たりやすくなっています。さすがに普通に座っているときは問題ありませんが、ちょっと足を組みたくなった時や立ち上がる時に足がぶつかり、「小さいな」と感じてしまうことが度々あります。

作業できる場所が狭い

次に気になるのもサイズ感の話になりますが、作業領域が狭い点が気になっています。

なんといっても学習机は主に子供が使うことを想定したサイズ感で作られているので、大人が使うと作業スペースが狭くなってしまうというのは必然です。

基本的に作業はPCで完結しているので、小学生時代のように教科書やノートを広げて作業するよりも狭いスペースで済みそうですが、体が大きくなっているからかスペースは狭く感じます。

天板が傷だらけ

こちらは学習机の問題点というよりも、長く使ってきたことが要因ですが、気になる点として挙げてみます。

私の学習机はこれまでいろいろな作業を受け止めてくれた事もあり、切り傷やへこんでしまっているところがたくさんありました。PC作業では関係ありませんが、書き物をするときに意外とへこみによってずれてしまうことも多く、できればこれも改善したいと思います。

また、製品によると思いますが私の机の天板は光沢があり、物撮りをするときなど反射が眩しかったので、できれば光沢が少ないものにしたいと思います。

まだまだ現役すぎる

これも直接的な問題点ではありませんが、学習机というのはかなり丈夫なもので、上で書いた傷以外ではガタが来ているところも一切なく、まだまだ現役を続けられる状態です。そうなると、学習机を手放して他の机に買い換えるというのは気が引けます。

そこで、机環境を改善するためには、机自体を変えるという方針は取れません。

学習机を改善!

さて、気になっている点を挙げ終わったので、工夫とアイテムたちを使って学習机を使いやすくしていきたいと思います。

奥行を広げる

まずは作業エリアを広げていきます。そのためにまずは不要なパーツを外していきます。私が使っていた学習机では照明と本棚部分が外せる様になっていました。

学習机の概念

これらを外してしまうと天板のみの平らな状態になります。これだけでもだいぶ作業領域が広がるので、いらないパーツは外してしまうのがいいです。また、平らな状態になればこの後紹介する「天板を変える」の手順でもう一段階、簡単に広げることができます。

高さを上げる

次に机の高さを上げていきたいと思います。高さを上げるために下記のような家具の底上げグッズを使います。今回使ったのはベッドや四つ足の家具用のモノですがコの字型の私の机にも問題なく使うことができました。

これを机の脚の下に設置することで机全体を上に持ち上げます。

ちなみに…机の下が板で覆われている机でも、持ち上げることで、下にコードなどを通すことができるようになります。副産物的な効果ですが地味に嬉しかったです。

使っている様子

注意点としては机の重みが4点にかかってしまうため、床がその重さに耐えられるかは事前に確認しておいてください。また、次に紹介する天板設置により、もう少し上部が高くなるので注意が必要です。

余談ですが、机の高さが上がっているので、その分椅子も高くなります。そうなるとフットレストも併せて用意しておくといいと思います。私が使っているのはサンワダイレクトのシンプルな形状のものです。

特別この商品がおすすめというわけではありませんが、フットレストを使うと姿勢も崩れにくく、足の圧迫感が薄れるため疲れにくくなります。本記事とは関係なく使ってみることをお勧めします。

天板を変える

天板を変えると書きましたが、実際には、学習机の天板に木の板を載せてあげるだけです。先程の手順で学習机の上が平らになっていれば、ただ板を乗せるだけで事足りてしまいます。

今回は木材屋さんのマルトクさんでサイズを指定して購入してみました。サイズや木材の種類は好みによって決めるのがいいかと思いますが、厚みに関しては薄すぎるとたわんでしまうことがあるらしいので、少なくとも 25mm以上にするのが良さそうです。

ゴム集成材のイメージ

今回私が購入したのは、ゴム集成材で25*700*1650mmです。これは私の学習机の2倍くらいの幅で、この板を乗せるだけで作業範囲が2倍になってしまうわけです。使っていて奥行きはもっとあってもいいかなと思いましたので、真似される場合は思い切って奥行きは広げてしまってもいいかと思います。

また、天板は載せているだけなので、学習机が寿命を迎えたり、本当に昇降デスクが欲しくなったら、この天板だけ転用して、別の机に載せるなり、昇降する足を購入して装着すれば無駄にはなりません。今回はヤスリがけやコーティングもやってもらったので値がはりましたが、長く使いまわして行けそうなので費用対効果はいいと思います。

机アイテムの見直し

本題とは少しズレてしまいますが、最後に広くなった作業スペースを無駄にしないよう、私が使っているアイテムを紹介して終わろうと思います。

さきほど、学習机の棚を外した際に照明も外したという話を書きましたが、デフォルトのデスクライトの代わりに私はモニターに引っ掛けて使うことができるこちらの商品を使っています。

これを使うことで、スタンドライトと比べて足の分だけ無駄なスペースを使わなくてすんでしまいます。

デスクライトと同じく、モニターアームを使うことでモニタ下のスペースを有効に使うことができます。加えてデフォルトのスタンドではできないような角度や向きでモニタを設置することができるので、自分の作業スタイルにも合わせやすくなります。

また、地味に嬉しいことがもう一つあります。今回天板を新しく載せていますが、重さの割に結構簡単にずれてしまいます。そこで、モニターアームを使うと、アームを固定する部分がクランプになっており、机と天板を固定してくれます。狙ってはいませんでしたが、この点でもアームをおすすめしてしまいます。 

完成

というわけで、作業完了です。

机 ver2

主に天板代がかさんでしまいましたが、だいぶ満足のいく学習机に進化させることができたと思います。これからもデスク環境は進化させていくつもりなので、またいい知見が得られたら紹介していきたいと思います。

]]>
https://koneta.click/p/856/feed 0
GoogleスプレッドシートをCMS&APIとして使ってみました https://koneta.click/p/894 https://koneta.click/p/894#respond Sat, 27 Nov 2021 12:32:38 +0000 https://koneta.click/?p=894 今、ちょっとしたWebサービスを構築しているのですが、その中でちょっとしたお知らせ機能を実装したくなり、NEWS機能を実装することになりました。しかし他の機能も実装している中、NEWSの登録機能まで作るのは大変なので、どうやったら簡単にデータを用意できるかを考えてみました。

このような場合、最近であればmicroCMSなどのヘッドレスCMSなどを使うのがあるあるになるかと思います。しかし今回はとっても小規模なので、それすらオーバースペックな気がしてしまいました。そこで今回はGoogleさんのスプレッドシートにNEWS内容を入力して、それをAPIから取得する方針でやっていきたいと思います。

スプレッドシートを用意する

まずはNEWSを入力するシートを用意します。シート自体はいつも通り作成して、下記の画像のように必要な情報を記入していきます。

記事サンプル

今回は、NEWSの日時とタイトル、本文を入力してみました。もし他にもNEWSのカテゴリだったり筆者だったり必要な情報がある場合はその分だけカラムを追加していけば大丈夫です。

データの準備ができたら、次の手順のためにシートの名前とドキュメントのIDを確認しておきます。シートの名前は画面下から確認できます。この名前は自分で好きなものに変更することができるため、わかりやすい名前にしておくといいと思います。また、ドキュメントのIDはURLから確認できます。https://docs.google.com/spreadsheets/d/[ここがドキュメントのIDです]/edit#gid=0 このIDをメモしておいてください。

スクリプトを用意

データの準備ができたら次にスプレッドシート側でスプレッドシートの内容をAPIとして呼び出せるようにスクリプトを用意します。

上記で用意したスプレッドシートのメニューから「拡張機能」→「Apps Script」と選択していきます。そして表示されたエディタに下記のコードを入力します。1行だけ上で確認したシート名とドキュメントIDに書き換えてください。

function getData(id, sheetName) {
  var sheet = SpreadsheetApp.openById(id).getSheetByName(sheetName);
  var rows = sheet.getDataRange().getValues();
  var keys = rows.splice(0, 1)[0];
  return rows.map(function(row) {
    var obj = {}
    row.map(function(item, index) {
      obj[keys[index]] = item;
    });
    return obj;
  });
}

function doGet(e) {
  var data = getData('[※※※ドキュメントID※※※]', '[※※※シート名※※※]');
  var output = ContentService.createTextOutput(JSON.stringify(data, null, 2));
  output.setMimeType(ContentService.MimeType.TEXT);
  return output;
}

入力出来たら一度手動で実行してみます。まずはプルダウンから実行売る関数を選択します。今回はdoGetを選択します。

関数選択中

最後に、プルダウンの左にある実行を押します。初回はGoogleアカウントの承認が必要になります。「このアプリはGoogleで確認されていません」と表示されますが、自分で作成しているアプリなので「詳細」から進んじゃってください。これでスクリプトが実行されているはずです。「お知らせ 実行完了」と表示されていれば大丈夫かと思います。

正常に実行されていない場合は、ドキュメントIDやシート名があっているか、実行している関数がdoGetになっているかを確認してみてください。

最後にこのスクリプトに外部からアクセスできるようデプロイします。デプロイは画面右上の「デプロイ」ボタンからできます。このボタンから歯車マークの「ウェブアプリ」を選択します。

デプロイ層あ

あとは必要な情報を入力します。今回はサイトのフロントエンドから呼ばれるため「アクセスできるユーザー」は「全員」を選択します。これでデプロイは完了です。最後にAPIにアクセスするためのURLが発行されているので、あとはここにアクセスするだけでデータを取得することができます。

APIとして呼び出してみる

最後に用意できたNEWS取得APIからデータを取得しています。まずは上記のスクリプトを用意したときに発行されたURLに直接アクセスしてみます。データが取得できていれば作業完了です。

今回、私はNuxtJSでサイトを構築していますが、おまけとしてコンポーネントからaxiosで呼び出しているところも書いておきます。

<template>
  <v-row>
    <v-col v-if="isLoadedNews" cols="12">
      <h3>NEWS</h3>
    </v-col>
    <v-expansion-panels accordion>
      <v-expansion-panel v-for="(news, index) in newsList" :key="index">
        <v-expansion-panel-header>
          {{ news.date }} - {{ news.title }}
        </v-expansion-panel-header>
        <v-expansion-panel-content>
          <p style="white-space: pre-wrap" v-text="news.body"></p>
        </v-expansion-panel-content>
      </v-expansion-panel>
    </v-expansion-panels>
  </v-row>
</template>
<script>
export default {
  props: {
    isDisplayLoading: {
      type: Boolean,
    },
  },
  data: () => ({
    newsList: [],
  }),
  computed: {
    isLoadedNews() {
      return this.newsList.length > 0
    },
  },
  async mounted() {
    await this.$nextTick(async () => {
      if (this.isDisplayLoading) this.$nuxt.$loading.start()
      await this.$axios.get(this.$NEWS_API.url).then((response) => {
        this.newsList = response.data
      })
      if (this.isDisplayLoading) this.$nuxt.$loading.finish()
    })
  },
}
</script>

Vuetifyが入っていたりローディング用の変数があったりと関係ない部分がありますが、取得部分はawait this.$axios.get(this.$NEWS_API.url).then でデータを持ってくることができています。

サイトの様子

というわけで、APIからデータを簡単に取得することができました!

おわりに

さて、つれずれなるままに書いてみました。今回は超最低限のCMSとしてスプレッドシートを使ってみました。なんといっても簡単にNEWS記事を管理することができ、APIから簡単に取得までできお手軽でした。最低限機能としては優秀だったかと思います。需要を満たしていればこれだけでも十分という場面は結構ありそうです。

ただ、今回紹介した方法だけでは画像やリンクが張れなかったり、そもそもデータ取得のレスポンスが遅かったりと気になる点はけっこうあります。なので今回の方法は「あくまでこんな方法もあるらしいですぜ」という感じでお願いします!

]]>
https://koneta.click/p/894/feed 0
ChromeBookで作業したいのでVPSにDockerで「Eclipse Theia」環境を構築してみました https://koneta.click/p/848 https://koneta.click/p/848#respond Fri, 23 Jul 2021 18:28:25 +0000 https://koneta.click/?p=848 先日ChromeBookを購入しました。このちょろめ君は、そのお手軽さで意外と色々なタイミングで出動してくれています。しかしChromeBookでお手軽にできるのはおおよそブラウザのみでできる範囲になります。Androidアプリも使う事はできますが、やはり物足りないもので、ChromeBookでできることを増やそうと奮闘する毎日です。

私のPCの使いみちの中で大きいのが、主にWeb系のコーディングです。その昔であればメモ帳とブラウザだけあればWebページを作ることができる…という話でしたが、現在のWeb系はフロントエンドもツールやフレームワークが充実し、開発環境をしっかり構築しないといけなくなりました。

というわけで今回はブラウザからいい感じの開発環境にアクセスできるようWeb上のエディタとして「Eclipse Theia」を構築してみようと思います。今回はこれをVPS(ConoHa)上に構築することで、どの端末からも同じ環境にアクセスできるようにしてみます。

Docker環境準備

まずはDocker環境の準備です。Docker環境の構築は以前記事にしていますので、まだDocker環境を作っていない方はこちらを参考にしていただけると幸いです。

$ docker --version
Docker version 19.03.13, build 4484c46d9d
$ docker-compose --version
docker-compose version 1.25.5, build 8a1c60f6

今回はVPS(ConoHa)で作業しましたが、中身は普通のCentOSなので普通にDocker(とdocker-compose)環境が作成できれば大丈夫だと思います。

リバースプロキシの準備

次はProxyの用意です。こちらは必須の作業ではありませんが、同じVPS上で動作させているのであれば、簡単に複数サイト/ツールで使用できSSL設定も自動で行ってくれるようになるため使用するのをおすすめしておきます。こちらも以前記事にしていますので参考にしてみてください。

今回はVPS上に設置する事、加えてすでにほかのツールを設置していることやそれぞれのツールにドメインで簡単にアクセスできるようにしたかったのでプロキシの設定が必須でしたが、ローカルで動かす場合やテストで動かしたいだけであれば設定は不要です。

Eclipse Theia環境を作る

docker-compose.ymlを用意

さて、周辺環境の構築ができましたので、いよいよ本題、「Eclipse Theia」の構築をやっていきます。とはいえDocker上に構築していくので、動かすだけであれば操作自体はとっても単純です。

version: '3'

services:
  eclipse-theia:
    restart: always
    image: theiaide/theia-full # 全部入りイメージ
    ports:
      - 50000:3000
    volumes:
      - /path/to/workspace:/home/project/:cached
    environment:
      - VIRTUAL_HOST=[ドメイン (プロキシ用)]
      - LETSENCRYPT_HOST=[ドメイン (Let's用)]
      - LETSENCRYPT_EMAIL=[メアド (Let's用)]
                                
networks:
  default:
    external:
      name: common_link

はい。今回はdocker-composeを使って環境を構築していくので、上記の内容でおなじみのdocker-compose.ymlを作成してください。修正するのはvolumesのワークスペースディレクトリ部分とenviromentのドメイン設定部分だけです。

ちなみに、ディレクトリ設定の方は設定したディレクトリ以下であればその部分だけ表示することができるため、ワークスペースのルートディレクトリを設定してあげるといいと思います。また、プロキシを利用しない場合はenviromentnetworksの設定は削除してもらって大丈夫です。

BASIC認証の用意

今回は外部からアクセスできる場所に設置するため一応アクセス制限用の設定をします。今回は簡単にBASIN認証でお茶を濁そうと思います。やり方は上記のリバースプロキシ設置時の記事に書いてありますので参考にしてみてください。

$ htpasswd -c /path/to/[ドメイン] [BASIC認証 ID]
New password: [BASIC認証 PASSWORD]
Re-type new password: [もう一度]

基本的に対応したパスワードファイルを読み込んであげるだけですが、ファイル名をドメインと同じにするのを忘れないようにしてください。

エディタのインストール

最後にオマケとしてCUIにエディタ(micro)を入れておきたいと思います。基本的には完全に無駄な作業です。Eclipse Theiaのコンテナは基本的にUbuntuで動いているらしく、エディタ内にターミナルの機能もあります。このターミナル機能を使うことでSSHで別のサーバにアクセスすることもできます。

しかしSSH先でvimなんぞを使ってしまうとESCキーがエディタ側に吸われてしまうため、vimで入力モードから脱出することができません。そこで(SSH先のサーバに)別のエディタを入れておきます。

curl https://getmic.ro | bash

というわけでmicroというエディタを入れておきました。こちらはCUIのソフトながら使い勝手はGUIにも匹敵するエディタで、少し前におすすめされたいたので入れてみました。まあ、こちらは完全にオマケなのでご自身の使い道に合わせて必要があれば入れてみてください。

実行してみる

準備ができたらいつも通りdocker-composeコマンドで実行して作業完了です。

$ cd /path/to/[docker-compose.ymlディレクトリ]
$ docker-compose up --build -d

実行できたらブラウザからアクセスしてみます。

アクセスした様子

(DNS or hostsの設定が完了していれば) 先ほど設定したドメインでアクセスするとワークスペースのディレクトリでVSCodeを起動したような画面が出てきます。これにて作業完了です。

終わりに

さて簡単に書いてきました。これのおかげで私のChromeBook君の使い道が爆発的に増えました。コーディング環境ができたのはもちろんですが、一緒に構築できたターミナル環境が思っているよりも便利でした。今後もChromeBookの使い道を増やしていけるよう探索を続けていきたいと思います。

]]>
https://koneta.click/p/848/feed 0
SONYの高級耳栓を買ってしまいました! WF-1000XM4(ワイヤレスイヤホン) https://koneta.click/p/823 https://koneta.click/p/823#respond Sat, 10 Jul 2021 10:15:23 +0000 https://koneta.click/?p=823 まずはじめに、私は音質についてはそこまで比較できる耳を持ち合わせていませんし、ノイズキャンセリング機能がついているイヤホンを購入したのは10年ぶりくらいなので求められているような性能の比較もできません。その点はご留意の上、ただの感想を御覧ください。

というわけでSONYさんから新しいワイヤレスイヤホンWF-1000XM4が発売されました。話を聞く限りではかなり評価が高いようで、ちょうどノイキャン付きイヤホンがほしかった私は手が出てしまいました。今回はイヤホン初心者の私が耳栓として使ってみた感想を書いてみたいと思います。

外観

前情報では、外箱にもこだわりがあるそうなので、まずはパッケージ外見から見ていきます。

パッケージ外観

今回のパッケージは時代の流れを汲んでプラスチックを一切使っていないそうです。なんでも竹やサトウキビから作る環境にやさしい紙素材を開発したそうです。ネット上では「安っぽい」という意見が出ていました。正直共感したところではありますが、重要なのは中身であり、本気を出した結果この外見ということで、一周回って好感です。

パッケージ中身

もちろん中身にもプラスチックはつかわれていませんでした。内容物は本体とサイズ別のイヤーピース,充電ケーブルそして説明書などの書類系です。コンセントで充電したい場合は別途アダプターが必要になります。

テンション上がる!!!

開けてみるともう安っぽさは無いです。手にもって最初に驚いたのがケースの小ささです。SONYさんのワイヤレスイヤホンは前世代のものを見たことがありますが、ケースもイヤホン本体も結構大きい印象があったので、そこはだいぶ改善されたものと思います。また、重量もとても軽く持ち運びに困ることもないと思いました。

耳栓性能

さて、ここからが素直に感動して書きたかったところです。公式の商品ページを見るとノイキャン性能は星6つ。これはノイキャン性能でいい評価をもらっているSONYさんのヘッドホンとも同じ星数になっています。他のノイキャン製品との比較をしたことがないので星6がどれほどのものかは分かっていませんが、「これ以上のノイキャンいる?」というのが感想です。

一番感動したのは私のPC環境のノイズを完全に消してくれたことです。私の環境ではノートPCのファンや空気清浄機の動作音、在宅作業中では家族の生活音、出社していれば他の人のタイピング音など、作業に支障をきたす雑音が結構たくさんあります。しかしこのイヤホンを装着することでそれら全ての雑音を消し去ってくれました。

私のオススメの使い方としては、ノイキャンで世間の喧騒をかき消しつつ、自然音を流す、そして目をつむる。するとどうでしょう。そこはもう自然の中……のような気がしてきます。

注意点としては、あなたの命が狙われているとしたら、後ろに誰かが立っていても気づくことはできないので、使用しないことをおすすめします。

他商品との比較

さて、最後に完全に需要が無い旧製品との比較をやってみたいと思います。その比較対象というのが私が5年前に4000円くらいで購入した、どこの会社かもよく分からない格安のワイヤレスイヤホンです。

八百長が…始まる…!

さて、まずはノイズキャンセリング機能についてです。先程も書いたとおりSONYさんの方は感動のノイズキャンセリング機能があります。対する格安くんは…なんとノイズキャンセリング機能がありません。まずはSONYさんに1勝です。

次は通信の途切れなさです。まず格安くんの方です。この子は自室で動かずに効いている分であれば何も問題はありません。しかし満員電車や歩いているときなどはプツプツと通信が途切れてしまい音楽なんかは効いていられない状態になります。一方のSONYさんは動いていても途切れないどころか部屋を1つ2つ挟んでもなぜか通信が途切れませんでした。というわけでこの項目でもSONYさんに1勝です。

次は形状についてです。SONYさんの形状はやはり存在感があります。また右左は対象の形をしていてそれぞれ専用になっています。一方格安くんは外音取り込みも不要なだけあってかなり小さいです。また左右とも同じ形をしているため左右どちらでも使い回すことができます。ただ使用中はライトがポツポツ光り、自分からは見えませんがちょっと無駄な気がします。さて、それを踏まえましてワタシ的には格安くんに1勝を渡したいと思います。というのも私の使い方で片耳で使用して左右を切り替えることを良くするのですが、SONYさんでその使い方をするとケースから出してこないといけないというのが理由になります。

最後は重さについてです。 イヤホン本体でこの項目をみると数値的には格安くんの勝ちです。…しかし、正直なところではどちらも軽いので耳に装着している状態では差は余り感じないです。ただケースまで含めるとSONYさんのほうが軽くなります。機能差はありますがそれでもSONYさんのほうが軽いというのは嬉しい限りです。SONYさんに1勝です。

というわけでSONYさん対格安くんは 3対1 でSONYさんの勝利ということになりました! ……はい。もちろん使い込んだ三輪車と最新のマウンテンバイクを比べてもしょうがないのですが、私の手札ではこの比較しかできないので、ネタと思って目をつぶってもらえると嬉しいです。ただ、奮発して買っただけのことはあったな、買ってよかったというのが正直な感想です。

最後に

さて、初心者が思いつきで書いてみました。本当はイヤホンに対する知見が無さすぎるので記事にするつもりはなかったのですが、思っていたよりもノイズキャンセリング機能に感動してしまったのでつい記事を書いてしまいました。ノイズを消してくれるだけで集中力が上がり、視聴環境も向上するのでとにかくオススメしておきます。ただし屋外など周りの状況には注意してくださいね!

]]>
https://koneta.click/p/823/feed 0
2021年上半期買ってよかったもの ランキングで7選 https://koneta.click/p/702 https://koneta.click/p/702#respond Mon, 14 Jun 2021 15:50:12 +0000 https://koneta.click/?p=702 だんだんと暑い日が増えてきましたね。まだまだ異常な時期は続いているようですが私は元気です。在宅で仕事をしているとつい余計なことがしたくなるもので、気が付くと物欲が爆発していました。

というわけで今回は物欲に負けて購入したモノのなかから「買ってよかったなぁ」と思えたものをちょっとだけ書いていこうと思います。

7位 ハンディビールサーバー

こちらは単純。家で飲むビールが2ランク美味しくなります。ビールって泡って大切なんですね。おいしく入れるコツはキンキンに冷やしておくことと、そーっと入れてあげることです。この手の商品は洗うのがめんどくさいですが、こちらは電動部分の分解も簡単で汚れる部分も少ないので、めんどくささはそこまでなかったのも高ポイントでした。

まぁ私はお酒弱いので1缶飲めるか飲めないかくらいですが…。家族に好評なのでランクインです。

6位 ポップコーンメーカー

完全に個人の好みですが…私はポップコーンが大好きです。一時期は小袋で販売されているレンジであっためるだけのポップコーンをよく食べていたのですが、コストパフォーマンスが悪く、小袋製品は健康に悪いという噂も耳に入っていたので、しばらく出来合いのポップコーンを購入していました。そんな中購入したのがこちらの商品(と豆)です。

豆から自分でポップコーンを作ることで、コスパが良い, 油を制限できる, 好みの味付けができるといろいろとメリットがあります。加えて上記のポップコーンメーカーを使用することでレンジで温めるだけで簡単にできたてのポップコーンを作ることができます。

マイブームはプライムビデオなんかで映画を見つつ出来立てのブラックペッパーポップコーンを食べることです。

ポップコーン好きにはもってこいのアイテムだったのでランクインです。

5位 激落ちクロス

私の作業部屋は寝室も兼ねていてベッドがあるのはもちろん着替えなんかもすべて同じ部屋なので結構ホコリが気になります。人が動いている日中は気にならないのですが、夜から朝かけて空気中のホコリが落ちると、机の上なんかに薄くたまってしまいます。以前は静電気でくっつける系のはたきを使っていたのですがホコリを取り切れず空気中に舞ってしまうため、より確実にホコリをキャッチしてくれそうなこちらを購入してみました。

このクロスはマイクロファイバーでホコリや汚れをキャッチするもので、思惑通りにはたきよりも確実にホコリをキャッチしてくれました。加えてこのクロスは洗濯することもできるのでしばらく使ってホコリをキャッチしにくくなったとしても洗濯して復活させることができます。5枚入りなので部屋ごとに分散して使っています。

気になったときにサッと、しっかりホコリをキャッチしてくれるのがお手軽でランクインです。

4位 重ねると何故かくっつくクロス

まさかのクロスが続きました。前回の買ってよかった記事ではカメラを購入しましたが、どのように持ち運ぶかに悩んでいた時に出会ったのがこちらです。モノとしては「裏表がくっつく風呂敷」というだけなのですが、これが意外と便利でした。使い方…を説明するまでもないですが何も考えずに包みたいものに巻きつけるとくっついてくれます。

以前に記事も書いたので詳しい感想は上記の記事からどうぞ!

私はこれでカメラを包み、傷がつくのを防止しつつバッグに入れています。出先でカメラを使うときも簡単に剥がすことができます。また剥がしたクロスは普通の布と変わらないので畳んでおけばかさばりません。くっつき力は申し分なくバッグの中で外れることは無いと思います。

シンプルな機能ながら買ってよかったものランクインです。クッション性は無いので衝撃の対策にはならないのは注意です。

3位 ガジェットポーチ(トラベルポーチ)

最近は出かける機会、特に旅行の機会はめっきり減りましたが、いざ旅に出るとなると慌ててしまうものです。某GoToの時に久しぶりに遠出をしたのですが、荷造りに手間がかかってしまいました。

この経験から最近は、薬やブラシなど普段使う物を巾着袋にまとめて、遠出の際は袋を持っていくだけという形に落ち着きました。袋にまとめておくと普段の生活でも必要なものが一箇所にまとまっているので探す手間も省け見た目も袋に収まっていていい感じです。という流れで味をしめた私が次にまとめだしたのがガジェット系、そして購入したのがこちらのポーチです。特別「この」ポーチに優位点があったわけでは無いのですが、見た目の好みで巻物型を選びました。

実際に使ってみたところ、用途に合わせた大小ポケットやベルトがあり、使い勝手は良かったです。ただ、入れるものによっては「巻き」が太くなりがちなので入れるものがケーブルや薄い充電器がメインの方に向いた商品だなと思います。かくゆう私は現状そこまで大型のものは入れないのでちょうどいい感じです。

使い勝手はそこそこ、ひとまとめにできる便利さでランクインです。

2位 小型軽量高出力充電器 CIO LilNob Share

現在は在宅ワークで会社のMacBookProを持ち帰って作業をしているのですが、時々出社しなくてはいけない状況になっています。そのため充電器も持ち運ばなくてはいけないのですが、いかんせん純正の充電器はとても大きく重たいです。そこで購入したのがこちらです。

こちらの充電器は60W帯ではトップクラスで小さく軽量です。実際に純正充電器と比べてみると二回りくらい小さいです。加えて複数端子を備えているためお出かけや旅行にもぴったりです。

ちなみにケーブルも同じくCIOさんのものを使っています。先ほどのガジェットポーチにどちらもいい感じに収まってくれます。

お手軽というのは最強ということでランクインです。

1位 ASUS Chromebook Detachable CM3 CM3000

月並みな表現ですが、ハード面ソフト面ともにサブ機として最高にちょうどいい端末でした。

私は普段、重い作業にはデスクトップ、それなりに重いけど持ち運びがしたい場合はMacBook、その他の軽量作業や動画再生機としてiPadを使用していました。正直この組み合わせでそこまで問題はなかったのですが、使用しているMacBookが古くバッテリーが持たなくなってきていること、そもそも普段の作業はそこまでスペックを求められていないのでMacBookじゃなくてもオッケーなことと細かいところでは改善の余地がありました。

そんなときASUSより発売されたのがこのCM3です。詳しい感想は以前記事にも書いていますので、そちらをご覧いただけると…と思いますが実際自分の作業内容を見返してブラウザで事足りる人にはもってこいの逸品になるとおもいます。このご時世で遠出はあまりできていませんが、持ち運びにもちょうど良さそうなので、状況が変わったらどんどん使っていきたいと思います。

ちょっとした作業に入る時の心の障害を解消してくれたのでランクインです。

終わりに

さて、今回も簡単に書いてみました。今回は比較的安い物が多かった印象です。本当はあんな逸品やこんなガジェットも購入してレビューやらまとめたりしたいところではありますが、クソ雑魚引きこもりには無理があるということで今回は終わろうと思います。

はぁ5000兆円ほしい。

]]>
https://koneta.click/p/702/feed 0
Nature Remo APIでn8nから家電を遠隔操作してみました https://koneta.click/p/791 https://koneta.click/p/791#respond Wed, 09 Jun 2021 16:24:36 +0000 https://koneta.click/?p=791 以前の記事でNature Remoというスマートリモコンで生活の未来度合いを向上することができたという記事を書きました。今回はそのRemoの活躍の場を広げるためプログラムから操作する…つまりはAPIを使って操作してみようと思います。加えてn8nでフローから操作もやってみようと思います。

背景

Nature Remoはスマホやスマートスピーカーから家電を簡単に操作することができるスマートリモコンです。私はその中でもNature Remo miniという端末を使用しています。Remo自体は紹介記事を書いていますのでよかったら読んでみてください。

また、IFTTTというサービスを活用することでワークフローを作成して、タイマーや居場所、SNSなどで設定した条件を満たしたときにRemoから家電を操作することができます。しかしIFTTTは最近無料で使用できる範囲が狭くなり使い勝手が悪くなってしまいました。 (私は元々使ってなかったので関係はなかったですが…)

そんな中、IFTTTの代わりとしてよく名前が挙がっているのがn8nです。n8nは無料で色々なサービスの連携/自動実行フローを作成できるのOSSのツールです。

n8nにRemoが公式に対応しているわけではないですが、RemoにはAPIが用意されているためHTTPSリクエストで家電を操作することができます。今回はこのAPIとn8nを使ってちょっと違った遠隔操作をしてみようと思います。

アクセストークンを取得する

APIを使用するためのアクセストークンを取得していきます。というわけでまずは home.nature.global にアクセスしてアクセストークンを作成します。

チープっぽいですが公式です。

ページにアクセスしたらGoogleアカウントで認証するかNatureに登録しているメールアドレスを入力してログイン用のメールを送信してもらってください。ログインができると「home.nature.globalがRemoへのアクセスをリクエストしています。」というページが表示されるので「許可する」を選択して完了です。

トークンは発行直後しか出ないのでモザイクの意味は…

権限の確認が終わると「Generate Access Token」のボタンが表示されます。このボタンを押すとアクセストークンが取得できます。一度ページを離れてしまうとアクセストークンは表示されないのでちゃんとメモしておくようにしてください。また、このトークンがほかの人に知られてしまうと、家電を好きなようにされてしまうので、絶対に外部に漏れないようにしてください。

これでアクセストークン取得完了です。

APIを使ってみる

トークンが取得できたので、n8nでワークフローを設定する前にAPIを使ってみます。APIはHTTPSリクエストで叩くことができるので、リクエストを飛ばせればなんでも大丈夫ですが、今回はcurlコマンドでやってみます。というわけで下記のコマンドを実行してみます。[アクセストークン]のところには1つ前の手順で取得したトークンで書き換えて実行してください。

$ curl -H "Authorization:[アクセストークン]" https://api.nature.global/1/appliances

これによりRemoに登録されている家電とそれぞれに実行できる操作の一覧を取得することができます。コマンド実行時に大切なのがヘッダーとして付与しているAuthorizationです。基本的にAPIを叩くときには必須の項目になります。そして、このコマンドで取得できる家電や操作にはIDが振られています。APIで家電を操作する際にはこのIDをパラメータとして指定するため操作したいIDはメモしておいてください。

今回は照明を操作してみようと思います。照明の操作には先ほどの取得した一覧からIDを持ってきます。

[
    {
        "他の家電情報"
    },
    {
        "id": "※※※※※※ ここのIDが大事 ※※※※※※",
        "device": {
            "...Remoの情報..."
        },
        "model": {
            "...照明自体の製品情報..."
        },
        "type": "LIGHT",
        "nickname": "照明",
        "image": "ico_light",
        "settings": null,
        "aircon": null,
        "signals": [],
        "light": {
            "buttons": [
                "...ボタン類..."
            ],
            "state": {
                "...明るさやON/OFFなど..."
            }
        }
    },
    {
        "他の家電情報"
    }
]

上記が一覧取得の結果を抜粋したものになりますが、この中の「※※※※※※ ここのIDが大事 ※※※※※※」部分に書かれているID
を使って照明を操作します。操作にはまたcurlで今度はPOSTします。

$ curl -H "Authorization:[アクセストークン]"  -X POST -d "button=on" https://api.nature.global/1/appliances/[照明のID]/light

これを実行すると照明が点灯するはずです。逆に消すときはbutton=onの部分をbutton=offにすれば大丈夫です。ちなみにこのボタンの設定値は先ほどの一覧取得した中のbuttonsnameの項目で確認することができます。ここまでできればあとはプログラムから叩いてもいいし煮ても焼いてもです。

n8nから実行する

さて本記事のメインでありながら、正直蛇足っぽいn8nからAPIを叩くフロー作成をやっていきます。とはいえここまでで動くことは確認できているのでn8nのHTTPリクエストノードに設定してあげるだけで動作します。

完成イメージ

ワークフローの完成イメージは上記の通りです。ただ動かすだけであればSTART含め2つのノードだけで作ることもできますが、一応将来性を考えトークンや照明IDの設定はSETノードに切り分けています。

SETには以下のように設定したい値を登録してあげるだけで大丈夫です。設定後一度ノードを実行してあげると次のノードでこの値を使用することができます。

設定値

次がメインのHTTPリクエストノードです。このノードでは基本的にcurlで叩いた値とSETノードで登録した値を登録してあげれば大丈夫です。SETノードで登録した値を使うには各入力欄の右側にある歯車マークから「Add Expression」を選択して「Nodes」からSetValuesString から選択することができます。あとはcurlで試した通りにHeaderとParametersに登録してあげるだけです。

前半
後半

これでフローは完成です。最後にフロー実行ボタンを押して正常に照明が操作できるかを確認してみてください。

エクスポートファイル (クリックで展開)
{
  "name": "Remo-Light",
  "nodes": [
    {
      "parameters": {},
      "name": "Start",
      "type": "n8n-nodes-base.start",
      "typeVersion": 1,
      "position": [
        200,
        0
      ]
    },
    {
      "parameters": {
        "keepOnlySet": true,
        "values": {
          "string": [
            {
              "name": "Authorization",
              "value": "[アクセストークン]"
            },
            {
              "name": "Light-ID",
              "value": "[照明ID]"
            },
            {
              "name": "button",
              "value": "on"
            }
          ]
        },
        "options": {}
      },
      "name": "Set request data1",
      "type": "n8n-nodes-base.set",
      "typeVersion": 1,
      "position": [
        350,
        0
      ]
    },
    {
      "parameters": {
        "requestMethod": "POST",
        "url": "=https://api.nature.global/1/appliances/{{$json[\"Light-ID\"]}}/light",
        "options": {},
        "bodyParametersUi": {
          "parameter": [
            {}
          ]
        },
        "headerParametersUi": {
          "parameter": [
            {
              "name": "Authorization",
              "value": "={{$json[\"Authorization\"]}}"
            }
          ]
        },
        "queryParametersUi": {
          "parameter": [
            {
              "name": "button",
              "value": "={{$json[\"button\"]}}"
            }
          ]
        }
      },
      "name": "Turn Light1",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 1,
      "position": [
        500,
        0
      ]
    }
  ],
  "connections": {
    "Start": {
      "main": [
        [
          {
            "node": "Set request data1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Set request data1": {
      "main": [
        [
          {
            "node": "Turn Light1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "settings": {},
  "id": "7"
}

オマケとして、ここで作成したフローをエクスポートしたjsonファイルを載せておきます。使用する際はSETノード内のトークンと照明IDを修正してください。

終わりに

さて、そんなわけで今回はRemoとn8nの連携をやってみました。APIを使うことで自分のプログラムからでも操作できるようになるのでRemoの適用範囲をだいぶ広げることができると思います。

今回紹介した内容だけではちょっと物足りない気もしますが、私は今回紹介したフローの起点をWebHookにすることでリクエストされたら自動的にフローが実行されるようにし、使わなくなったKindleをリモコン兼データモニターとして活用しています。

意外といい感じ

別途アプリを入れる必要もなく、Kindle自体の脱獄も不要、Webブラウザが動けばリモコンとして使えるため、意外と気に入っています。そのうちこちらの詳細も記事にしたいと思います。

参考

Remo公式リファレンス

アクセストークン管理

]]>
https://koneta.click/p/791/feed 0