【個人開発】Python+Seleniumでソーラーパネルの原価償却を自動集計

# やりたいこと

自宅にソーラーパネルを設置して、専用のモニター([Aiseg2](https://www2.panasonic.biz/ls/densetsu/aiseg/aiseg2/))で発電量/売電量などをみれるのはよいが
ソーラーパネルが結局のところどれだけお金を生み出してくれているのかが分からないので、
自動集計するツールを作成する。

# ソーラーパネル導入

在宅勤務が主になり、家にいる時間が長くなると家のことを考えるようになり、自宅関連の商品の売り上げが上がったそうだが
自分もまさにそれで、以前から気になっていたソーラーに手を出してしまいました。
3社相見積もりして一番安いところにお願いしました。
施工と営業を別会社で行っているところだと、お値段が倍近くになるのに驚きました。
取り付けてもらう側としては営業マージンなんて払うメリットないので、施工業者に直接お願いするのが良いですね。

 <table>
    <tr>
      <td>メーカー</td>
      <td><a href="https://csisolar.co.jp/">CanadianSolar</a></td>
    </tr>
    <tr>
      <td>容量</td>
      <td>4.5kw</td>
    </tr>
    <tr>
      <td>購入価格</td>
      <td>104.8万円</td>
    </tr>
    <tr>
      <td>売電単価</td>
      <td>19円/kw</td>
    </tr>
 </table>

取り付け後、市から補助金を9万円頂けたので、実質95.8万円で購入できたことになります。

# 利益の算出方法

太陽光の利益はこの２つの合計額になります。

||利益|情報源|データ取得方法|
|---|---|---|---|
|①|売電での収入額|購入実績お知らせサービスで<font color="Tomato">売電額</font>を確認|ブラウザでWeb表示して確認|
|②|自家消費での電気代削減額|・Aiseg2のWebUIで発電量、売電量を確認<br>・発電量 － 売電量 = 自家消費量<br>・自家消費量*1kw当たりの電気代 = <font color="Tomato">電気代削減額</font>|ブラウザでWeb表示して確認 + 計算|

②は厳密にいうと、自家消費によって電気代の基本料金が下がる可能性もあるけど、そこは省略して考えています。
自分の電気代プランだと、電気代集計期間内での30分単位の最大使用電力によって基本料金が決まる。
我が家は夜間にエコキュートでお湯を沸かすために、多量の電力を夜間に消費する。
そのため最大使用電力は夜間になることが多いはず。
発電した電力の自家消費は基本昼間になるので、自家消費での基本料金が下がるのはない前提で考える。
<font color="lightgrey">（計算が面倒になるし・・・）</font>

# 実現方法

* データの取得方法がWebでの確認からになるので、Python + Seleniumを使い、Webからのデータ取得を自動化してみる
* ラズパイのcronで月末に1度、.pyを起動する
* 取得したデータはcsvで保管する (後にWebアプリで見える化に活用したい）
* LineNotityで自分のスマホに通知する

月１の作業になるが、Webにログインして、データを記録してという作業が自動化されるだけでも楽である。
（手動だと絶対やらない）

# 実装

### まずはseleniumでWebからデータ取得
手動での画面遷移をイメージしていたので苦労しそうなイメージでしたが
目的のページにURLで直接アクセスして、目的の要素を取るだけなので
作ってみたらローコードで出来上がりました。

```main.py
import csv
from datetime import datetime
from selenium import webdriver
from selenium.webdriver.common.by import By
import re
import requests
import time

def getSolarResult():
    err = False

    # driver open
    options = webdriver.ChromeOptions()
    options.add_argument('-headless')
    driver = webdriver.Chrome(executable_path='/usr/bin/chromedriver', options=options)

    try:
        # aisegから月の発電量取得
        driver.get('http://[id]:[passwd]@[ipaddress]/page/graph/51311')
        generate_kwh = float(driver.find_element(By.ID, "val_kwh").text)

        # aisegから月の売電量取得
        driver.get('http://[id]:[passwd]@[ipaddress]/page/graph/54311')
        sell_kwh = float(driver.find_element(By.ID, "val_kwh").text)
    except:
        err = True

    # 自家消費電力量を算出
    self_use_kwh = generate_kwh - sell_kwh

    # 購入実績から売電価格を取得する
    try:
        driver.get('https://www32.tepco.co.jp/LV02/dfw/LV-CISweb/LVJOnlSWeb/')

        # login
        id = driver.find_element(By.NAME, "ACCOUNTUID")
        id.send_keys('[id]')
        pw = driver.find_element(By.NAME, "PASSWORD")
        pw.send_keys('[passwd]')
        pw.submit()
        time.sleep(5)

        # get selling price in table
        tableElem = driver.find_element(By.CLASS_NAME, "table-striped")
        trs = tableElem.find_elements(By.TAG_NAME, "tr")
        tds = trs[1].find_elements(By.TAG_NAME, "td")
        sell_price = int(re.sub(r'[^0-9]', '', tds[4].text))

        # logout
        driver.get('https://www32.tepco.co.jp/LV02/dfw/LOGOUT')
        driver.find_element(By.NAME, "SUBMIT").submit()
        time.sleep(5)
    except:
        err = True
    
    driver.quit()

    result = []
    if err != True:
        result.append(str(datetime.now().year) + "/" + str(datetime.now().month))
        result.append(generate_kwh)
        result.append(sell_kwh)
        result.append(self_use_kwh)
        result.append(sell_price)
        result.append(int(self_use_kwh * 25.8))

    return result
```

### CSVファイル出力部
ファイルの末尾に1行追加してあげて、
今までの累計利益を合計してあげます
```main.py
def outputToCsv(result):
    if not result:
        return

    with open('/home/pi/VSCode/SolarDepreciation/SolarDepreciation.csv', 'a+') as f:
        writer = csv.writer(f)
        writer.writerow(result)

        # 先頭から読み込み、合計利益を算出
        f.seek(0)
        reader = csv.reader(f)

        # 1行目をスキップ
        next(reader)

        sum = 0
        for row in reader:
            sum += int(row[4]) + int(row[5])

        result.append(sum)
```

### LineNotity
オールドタイプな人間なので通知といえばメールと考えてしまいましたが、世の中便利になってます。

```main.py
def sendLineNotify(result):
    if not result:
        notification_message = "データ取得エラー : スクリプトを確認してください"
    else:
        notification_message = "\n" + str(result[0]) + "の利益は" + str(int(result[4] + result[5])) + "円です。\n\n"
        notification_message += "発電量 : " + str(int(result[1])) + "kwh\n"
        notification_message += "売電 : " + str(result[4]) + "円\n"
        notification_message += "自家消費 : " + str(int(result[5])) + "円\n\n" 
        notification_message += "累計利益 : " + str(int(result[6])) + "円\n" 

    line_notify_token = "[token]"
    line_notify_api = 'https://notify-api.line.me/api/notify'
    headers = {'Authorization': f'Bearer {line_notify_token}'}
    data = {'message': f'message: {notification_message}'}
    requests.post(line_notify_api, headers = headers, data = data)
```

### main
```main.py
def main():
    result = getSolarResult()
    outputToCsv(result)
    sendLineNotify(result)

if __name__ == "__main__":
    main()
```

100行ちょいで終わってしまい、消化不良。
便利になってますね～。

### cron
月末に1度、.pyを起動してあげるだけ
```
00 20 28-31 * * /usr/bin/test `date -d tomorrow +\%d` -eq 1 && /usr/local/bin/python3 /home/pi/VSCode/SolarDepreciation/main.py
```

# 結果
月末にちゃんと通知してくれました！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/594040/f8ba1dfe-c515-1460-cc88-3cde58f2203c.png)

パネルを載せてからの過去データも集計しましたが月1万円前後ですね。
約8年で原価償却できちゃいそうです。
ラズパイとこのソースがあと8年動いてくれるかどうか。

# まとめ
コードでのWebの自動操作は便利で思ってたより簡単でした。
対象のページが複雑だとDOM解析が手間だったりしそうだが。

次は集計したcsvデータを使って、djangoでグラフ化でもやろうかな。