【Nature Remo】Splunkで室温監視ダッシュボードを作る

Nature Remo API で取得したデータで「室温監視ダッシュボード」を作った。
作って満足しちゃったので解体しようと思い、また作りたくなったとき用に記録に残す。
Splunk は GCP の無料クレジットを使って GCE で稼働させている。

## 完成図
画面構成はこんな感じ。
![スクリーンショット 2021-05-22 18.56.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/619762/d90a1ed0-1e9f-f52e-0241-661de7a41eae.png)
最近は蒸し暑くてエアコンつけてるんですが、そのときにガクッと落ちてるのが見れるのが面白い。

## 手順
大まかに3つの手順に分けられる。

#### 1. アドオンの作成
https://dev.classmethod.jp/articles/splunk-add-on-builder-python/

こちらの記事でやっていることを完全にパクらせてもらった。AWS の障害情報を取得する API を Nature Remo API に変えるだけ。

流れとしては、次の通り。
新規アドオン作成 ＞ Configure Data Collection ＞ New input ＞ Python Code

その後は細かな設定情報を入力していく。
ここにある ``Collection interval``は後述「アドオンの有効化」で上書きできるっぽい。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/619762/54323c72-22f6-9461-d469-4c194a66bf31.png)

``Data Input Parameters``はパスワードにして、APIの認証キーを入力できるようにする。``Add-on Setup Parameters``は入力不要。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/619762/bf81587c-ce0e-a5ec-07ec-3674c2edf96e.png)

次に、クローラーのスクリプトを設定する。この画面でテストもできる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/619762/8e40c864-a502-fff4-fdcb-62545767f688.png)

```remo_data_collecting.py
# encoding = utf-8

import os
import sys
import time
import datetime


def validate_input(helper, definition):
    pass


def collect_events(helper, ew):
    import requests
    import json

    def write_events(entry: dict):
        entry['added_info'] = {
            'getting_time': str(
                datetime.datetime.now(
                    datetime.timezone(datetime.timedelta(hours=9))
                )
            )
        }
        event = helper.new_event(source=helper.get_input_type(),
                                 index=helper.get_output_index(),
                                 sourcetype=helper.get_sourcetype(),
                                 data=json.dumps(entry))
        ew.write_event(event)

    opt_authorization_key = helper.get_arg('authorization_key')

    url = "https://api.nature.global/1/devices"
    headers = {
        'Authorization': 'Bearer' + ' ' + str(opt_authorization_key),
        'accept': 'application / json',
    }

    try:
        response = requests.get(url=url, headers=headers)
        response.raise_for_status()
        # 一番外側のカギカッコを除去
        write_events(json.loads(response.text[1:-1]))
    except requests.exceptions.RequestException as err:
        helper.log(err)

```

#### 2. アドオンの有効化
これを設定しないとクローラーが実行されない。

アドオンを作成して Splunk を再起動すると 新規 App が追加されている。
それを開き、``Create New Input``で名前や実行間隔、インデックスの情報を設定する。
なお、実行間隔は先程入力したものではなく、こちらが優先されるようだ。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/619762/01ba950d-0074-769a-5a97-96931f0ecb20.png)


#### 3. ダッシュボードの作成
ダッシュボードは XML で定義できる。
sqlは適当だが最新時刻で取得したデータが表示されるので、とりあえずヨシ！

```situon.xml
<dashboard>
  <label>室温</label>
  <row>
    <panel>
      <chart>
        <title>現在の室温</title>
        <search>
          <query>index="remo-data" | spath output=te path=newest_events.te.val | fields _time, te | dedup _time | table _time, te | head 1 | fields te</query>
          <earliest>-24h@h</earliest>
          <latest>now</latest>
        </search>
        <option name="charting.chart">radialGauge</option>
        <option name="charting.chart.rangeValues">[10,18,28,38]</option>
        <option name="charting.chart.style">shiny</option>
        <option name="charting.gaugeColors">["0x006d9c","0x53a051","0xdc4e41"]</option>
      </chart>
    </panel>
    <panel>
      <chart>
        <title>昨日の室温</title>
        <search>
          <query>index="remo-data" | spath output=te path=newest_events.te.val | fields _time, te | dedup _time | table _time, te 
| eval now = now()
| eval end = now - (24 * 60 * 60)
| eval begin = now - (2 * 24 * 60 * 60)
| where begin &lt;= _time and _time &lt;= end
| head 1 | fields te</query>
          <earliest>0</earliest>
          <latest></latest>
        </search>
        <option name="charting.chart">radialGauge</option>
        <option name="charting.chart.rangeValues">[10,18,28,38]</option>
        <option name="charting.gaugeColors">["0x006d9c","0x53a051","0xdc4e41"]</option>
        <option name="refresh.display">progressbar</option>
      </chart>
    </panel>
  </row>
  <row>
    <panel>
      <chart>
        <title>過去24時間</title>
        <search>
          <query>index="remo-data"
| spath output=te path=newest_events.te.val
| fields _time, te
| dedup _time
| table _time, te</query>
          <earliest>-24h@h</earliest>
          <latest>now</latest>
          <sampleRatio>1</sampleRatio>
        </search>
        <option name="charting.axisLabelsX.majorLabelStyle.overflowMode">ellipsisNone</option>
        <option name="charting.axisLabelsX.majorLabelStyle.rotation">0</option>
        <option name="charting.axisTitleX.visibility">visible</option>
        <option name="charting.axisTitleY.visibility">visible</option>
        <option name="charting.axisTitleY2.visibility">visible</option>
        <option name="charting.axisX.abbreviation">none</option>
        <option name="charting.axisX.scale">linear</option>
        <option name="charting.axisY.abbreviation">none</option>
        <option name="charting.axisY.scale">linear</option>
        <option name="charting.axisY2.abbreviation">none</option>
        <option name="charting.axisY2.enabled">0</option>
        <option name="charting.axisY2.scale">inherit</option>
        <option name="charting.chart">line</option>
        <option name="charting.chart.bubbleMaximumSize">50</option>
        <option name="charting.chart.bubbleMinimumSize">10</option>
        <option name="charting.chart.bubbleSizeBy">area</option>
        <option name="charting.chart.nullValueMode">gaps</option>
        <option name="charting.chart.showDataLabels">none</option>
        <option name="charting.chart.sliceCollapsingThreshold">0.01</option>
        <option name="charting.chart.stackMode">default</option>
        <option name="charting.chart.style">shiny</option>
        <option name="charting.drilldown">none</option>
        <option name="charting.layout.splitSeries">0</option>
        <option name="charting.layout.splitSeries.allowIndependentYRanges">0</option>
        <option name="charting.legend.labelStyle.overflowMode">ellipsisMiddle</option>
        <option name="charting.legend.mode">standard</option>
        <option name="charting.legend.placement">right</option>
        <option name="charting.lineWidth">2</option>
        <option name="refresh.display">progressbar</option>
        <option name="trellis.enabled">0</option>
        <option name="trellis.scales.shared">1</option>
        <option name="trellis.size">medium</option>
      </chart>
    </panel>
  </row>
  <row>
    <panel>
      <chart>
        <title>過去1週間</title>
        <search>
          <query>index="remo-data"
| spath output=te path=newest_events.te.val
| fields _time, te
| dedup _time
| table _time, te</query>
          <earliest>-7d@h</earliest>
          <latest>now</latest>
        </search>
        <option name="charting.chart">line</option>
        <option name="charting.drilldown">none</option>
        <option name="refresh.display">progressbar</option>
      </chart>
    </panel>
  </row>
</dashboard>
```

## まとめ
Nature Remo は本当に楽しい。テーブルの上からリモコンを抹殺するために導入した訳だけど、こういう楽しみ方もできる。

## 参考
[Nature Remo API 仕様](https://swagger.nature.global)
[SplunkのAdd-on Builderで自前のアドオンを作成してみる（Pythonスクリプト作成編）](https://dev.classmethod.jp/articles/splunk-add-on-builder-python/)