HSPで東京電力の電力使用状況を可視化

1.概要

毎日、365日利用している電気について、その使用量がどうなっているのか気にしたことはありませんか? 東京電力のサイトでは、でんき予報として電気の使用量についての、予測や実績をリアルタイムで数値で公開されています。 過去の使用実績についても、数値をダウンロードできます。 でんき予報には東京電力グループだけでなく、サービスエリア内全ての電力需要ならびに電力供給力の合計値が掲載されています。 今回、普段あまり気にすることは多くないと思いますが、サービス提供されているデータをHSPスクリプト言語でリアルタイムに 取得し、グラフ化して可視化します。

2.ソースコード

作成したプログラムは、東京電力 - 電力使用状況 (poweruse.exe)です。起動すると、 最新の使用率(%)をゲージグラフで表示します。使用率は、実績値 / 供給力で表されます。 グラフボタンをクリックすると、使用量の時間帯別推移をグラフ化して表示します。1時間単位ですので、〇〇時台の実績となり、 1時間前の状況となります。下記の画面は、この記事を作成する前に取得したもので、画面下のグラフは24時間の推移がどんな風にグラフ化 されるのを見せるために、過去のデータから参考としてグラフ化したものです。
尚、poweruse.exeを実行させるには、設定情報の保存にSQLite3を利用していますので、同一フォルダに SQLite3.dllが必要です。ダウンロードアーカイブに同梱しています。
ソースコードは、下記の通りです。コードの前半部分は、画面の描画や移動などに必要なAPIやグラフ描画のためのモジュールを定義 しています。起動すると、電力使用状況のサイトページにアクセスして、一旦ページ全体を取得しバッファに格納します。 取得先ページは、予め取得し易いようにテキスト形式のカンマ区切りとしてデータが掲載されています。 取得できたら、現在の時間から、最新のデータに更新されている行を取得後、改行コードを変換します。 取得データを分解して配列に格納したら、ゲージグラフを描画します。ゲージグラフは「アナログ時計」の 針の位置を描画するルーチンの応用で270°を 0(基点)として計算しています。以下、その状態でタイマー割り込みで10分単位に サイトにアクセスして更新データを取得していきます。
グラフボタンをクリックすると、使用時間帯別の棒グラフを描画します。 グラフのデータは、取得データを事前に配列に格納してあるので、配列から読み込みます。 グラフ描画に関しては、特別なモジュールなどがないので、縦軸、横軸のサイズ(線の長さ)から グラフが収まるように計算しています。ポイントは、縦軸のスケールの決め方です。 データの最大値を6000(万kw) として、目盛の刻み単位を500に定めて13段階に区分しています。 横軸は、0~23時までの24時間として横軸目盛を刻んでいます。 グラフの描画開始位置を決めて、boxf命令で描画させています。 縦軸スケールをデータの値によって自動で調整できるようなモジュールがあれば楽なのですが、 うまく作成できませんでした。

	;****************************************************************************
	;*
	;*    東京電力 - 電力使用状況 (poweruse.hsp) Ver1.0
	;*
	;*    <処理の概要>
	;*    本プログラムは、東京電力パワーグリッドにて提供している電力使用状況
	;*    のデータを取得して、当日実績の使用率等を表示するものである。
	;*    また、使用量の時間帯別推移をグラフ化して表示する。
	;*
	;*
	;*    ●東京電力 - でんき予報
	;*      URL : https://www.tepco.co.jp/forecast/index-j.html
	;*            https://www.tepco.co.jp/forecast/html/images/juyo-j.csv
	;*
	;*    ●過去の実績データ
	;*            https://www.tepco.co.jp/forecast/html/images/juyo-2016.csv
	;*            https://www.tepco.co.jp/forecast/html/images/juyo-2017.csv
	;*            https://www.tepco.co.jp/forecast/html/images/juyo-2018.csv
	;*            https://www.tepco.co.jp/forecast/html/images/juyo-2019.csv
	;*            https://www.tepco.co.jp/forecast/html/images/juyo-2020.csv
	;*
	;*
	;*    ●モジュール(mod_jcode.hsp)
	;*      むしゃぺらりさんのモジュールを利用させて頂きました。
	;*            http://www.setsuki.com/
	;*
	;*
	;****************************************************************************	
	
	#include "sqlele.hsp"

	;***** 実行ファイル自動作成 (追加) ****
	#packopt type 0
	#packopt name "poweruse"
	#packopt runtime "hsprt"
	#packopt manifest "app.manifest"
	#packopt icon "script.ico"
	#packopt hide 1
	#pack "batsu4.png"
	#pack "powerbtn.png"
	#pack "gage.png"

	;***** 線(line)の太さを変更 *****
	#define pLine(%1=0, %2=0, %3=ginfo_cx, %4=ginfo_cy, %5=1)	_pLine %1, %2, %3, %4, %5
	#module
		#uselib "gdi32.dll"
		#func CreatePen "CreatePen" int, int, int
		#func SelectObject "SelectObject" int,int
		#func Polyline "Polyline" int, var, int
		#func DeleteObject "DeleteObject" int
		#define ctype RGB(%1,%2,%3)		((%3*65536)+(%2*256)+%1)

		#deffunc _pLine int _ex, int _ey, int _sx, int _sy, int _size
			CreatePen 0, _size, RGB(ginfo_r, ginfo_g, ginfo_b)
			hPen = stat
			if( stat == 0 ){
				dialog "ペン設定に失敗しました。"
				return 1
			}
			dim p, 4
			p = _ex, _ey, _sx, _sy
			SelectObject hdc, hPen
			hOldPen = stat
			Polyline hdc, p, 2
			if( stat == 0 ){
				dialog "描画に失敗しました。"
				return 1
			}
			DeleteObject hPen
			SelectObject hdc, hOldPen
			pos _ex, _ey
		return 0
	#global

	;***** 複数ウィンドウ判定用 *****
	#module
	#deffunc _ginfo int prm1
		prmx@=ginfo(prm1*2)
		prmy@=ginfo(prm1*2+1)
	return
	#global

	;***** カーソルの形状を変更 *****
	#module
	#uselib  "user32.dll"
	#func  LoadCursorFromFile "LoadCursorFromFileA" var
	#func  LoadCursor "LoadCursorA" int,int
	#func   SetClassLong "SetClassLongA" int,int,int
	#func   SetCursor "SetCursor" int
	#define IDC_ARROW	$00007F00	

	#deffunc chengecur int prm1,str prm2,int prm3
	UserMode   = prm1  : if UserMode<0 : return -1
	sdim Curfname,260
	Curfname   = prm2  ;カーソルファイル名
	resourceID = prm3  ;リソースID (0~

	;UserMode=$200 でカーソルファイル名が設定されていた場合は、
	;ユーザーカーソルを設定
	if (UserMode==$200)&(strlen(Curfname)!0) {
		LoadCursorFromFile varptr(Curfname)
		hcurwnd=stat
		SetClassLong hwnd,-12,hcurwnd
		SetCursor hcurwnd
		sdim Curfname,0
		return 0
	}
	;リソースIDに対応するカーソルを設定 (object Mode) (ID=0~28)
	hinst=0 : lpszCursor=IDC_ARROW
	if resourceID>=5 : resourceID+=123
	LoadCursor hinst,resourceID+lpszCursor
	hcurwnd=stat
	;if UserMode==$100 {
	;	hCld=hwnd
	;	}else{
			hCld=objinfo(UserMode,2)
	;}
	SetClassLong hCld,-12,hcurwnd
	SetCursor hcurwnd
	sdim Curfname,0
	return 0
	#global

	#module
	#uselib "kernel32.dll"
	#cfunc  CreateMutex "CreateMutexA" int,int,sptr
	#cfunc  GetLastError "GetLastError"

	;***** 二重起動防止 *****
	#deffunc wexapend str prm1
	strname=prm1
	;***** 名前の文字列が省略された場合 *****
	if strlen(strname)==0 : strname="HSP340ONIWND"  ;Default String
	ret=CreateMutex(0,1,strname)
	;二重起動か?
	if GetLastError()==0    : return 0	;同じジョブが起動していない
	if GetLastError()==183  : return 1	;既に起動している
	return -1
	#global

	#module
	#deffunc strdigit str prm1,int prm2
	;*************************************
	; prm1 : 数値文字列(文字列型)
	; prm2 : 最大桁数(整数値)
	;*************************************
	sPrm =prm1
	iLenInteger=strlen(sPrm)
	MaxLen=prm2 : if MaxLen<0 : MaxLen=iLenInteger+3
	sInteger=sPrm
	sTarget=""
	if iLenInteger<=3 : sTarget=sInteger : ref=sTarget : return -1
	iFirst=iLenInteger¥3 : if iFirst==0: iFirst=3
	sTarget=strmid(sInteger,0,iFirst)

	;***** 三桁毎にカンマを付加 *****
	repeat (iLenInteger-1)/3
		sTarget+="," : sTarget+=strmid(sInteger, cnt*3+iFirst, 3)
	loop

	;***** 右揃えルーチン (最大桁数の変数MaxLenを渡す) *****
	dspsize=strlen(sTarget)
	if dspsize<MaxLen {
		repeat MaxLen-dspsize : sTarget=" "+sTarget : loop
	}
	return sTarget
	#global

	#uselib  "user32.dll"
	#func  GetWindowRect "GetWindowRect" int,int
	#func  GetSystemMetrics "GetSystemMetrics" int
	#func  MoveWindow "MoveWindow" int,int,int,int,int,int
	#func  SetWindowLong "SetWindowLongA" int , int , int
	#cfunc GetWindowLong "GetWindowLongA" int , int
	
	#define global GWL_STYLE        0xFFFFFFF0
	#define global WS_SYSMENU       0x00080000
	#define global WS_MAXIMIZEBOX   0x00010000
	#define global WS_MINIMIZEBOX   0x00020000

	#define SM_CXSCREEN      $00000000
	#define SM_CYSCREEN      $00000001
	#define obj_wmnclbtndown sendmsg hwnd,$00A1,2,0
	#define WM_CLOSE	$00000010
	
	#define g_box(%1,%2,%3,%4) line %3,%2,%1,%2 : ¥
                               line %3,%4,%3,%2 : ¥
                               line %1,%4,%3,%4 : ¥
                               line %1,%2,%1,%4

	#func SetTimer  "SetTimer"  int, int, int, int
	#func KillTimer "KillTimer" int, int	

	#define WM_TIMER	0x0113
	#define ID_TIMER	1

	#include "mod_jcode.hsp"

	;***** 起動ディレクトリ取得 *****
	sdim Startdir,512
	if hspstat&1=0 { Startdir=dir_exe+"¥¥" : chdir dir_exe
	} else {
		Startdir=dir_cur+"¥¥"
	}
	chdir Startdir

	;***** データベース存在確認 *****
	exist Startdir+"setting¥¥poweruse.ini"
	if strsize ==-1 {
		dialog "データベースファイルが見つかりません。",0 : end
	}
	await
	wexapend "poweruse" : if stat : end
	
	;***** データベースオープン *****
	sql_open Startdir+"setting¥¥poweruse.ini"
	;***** 画面表示位置設定 *****
	sql_q "SELECT * FROM TValu WHERE ID = "+1
	xpos=sql_i("wxpos") : ypos=sql_i("wypos")
	if (xpos<0 or xpos>ginfo_dispx) : xpos=0
	if (ypos<0 or ypos>ginfo_dispy) : ypos=0
	sql_close
	buffer 2,11,11,0 : picload "batsu4.png",0
	buffer 3,80,20,0 : picload "powerbtn.png",0
	buffer 4,79,41,0 : picload "gage.png",0

*main
	bgscr 0,150,170,,xpos,ypos
	SetWindowLong hwnd,GWL_STYLE,$B0000|GetWindowLong(hwnd,GWL_STYLE)
	title "電力使用状況(東京電力)"

	redraw 0
	font "Meiryo UI",10,3
	color 120,0,120 : boxf 0,0,ginfo(10),20
	color 255,255,255 : pos 8,3 : mes "Power usage"
	color 240,240,240 : boxf 0,20,150,170

	;***** 閉じる( × )画像をボタンにセット *****
	pos 130,5 : objsize 11,11 :	objimage 2,0,0,0,11   : button "",*owari
	pos 15,146: objsize 40,20 : objimage 3,0,0,40,20  : button "",*mode01   ;更新
	pos 56,146: objsize 40,20 : objimage 3,40,0,40,20 : button "",*mode02   ;確認
	pos 97,146: objsize 40,20 : objimage 3,80,0,40,20 : button "",*mode03   ;グラフ
	chengecur 0,"",14
	pos 37,90 : gmode 2 : gcopy 4,0,0,79,41
	
	redraw 1
	onexit *owari
	onclick gosub *move 

	ReturnHTML=""
	;***** 東京電力電力使用状況のURLを指定 *****
	sURL = "https://www.tepco.co.jp/forecast/html/images/juyo-j.csv"
	charcode = "UTF-8"
	
	newcom objXMLHTTP, "Microsoft.XMLHTTP"
	objXMLHTTP->"Open" "GET", sURL, 0
	objXMLHTTP->"Send"
	
	if objXMLHTTP("status") = 200 {
		newcom objRS,"ADODB.Stream"
		if stat!0 : dialog "ADODB.Streamがサポートされていません。",0 : end
		objRS("Type") = 1
		objRS->"Open"
		sHtml=objXMLHTTP("ResponseBody")
		objRS->"Write" sHtml
		objRS("Position") = 0
		objRS("Type") = 2
		objRS("Charset") = charcode
		ReturnHTML=objRS("ReadText")
		objRS->"Close"

		buf = "" : tempstr = ""
		;***** 改行コードを変換 *****
		toCRLF buf,ReturnHTML
		notesel buf
		gosub *gyopos
	
		;***** 現在時間の1時間前が取得対象行 *****
		if M_time<10 : gyou-=1
		sdim data,128 : sdim update,48
		noteget update,0     ;更新日付・時間
		noteget data,gyou-1 ;本日の電力使用状況
		
		;********************************************************
		;*  本日の電力使用状況
		;*  DATE,TIME,当日実績(万kW),予測値(万kW),使用率(%)
		;*  2018/6/13,19:00,3507,0,76
		;********************************************************

		notepos = 0 : sdim tempbuf,32 : sdim resvName,24,5
		;***** 取得データを分解して配列に格納 *****
		repeat 5
			getstr tempbuf,data,notepos,',' : notepos+=strsize
			resvName(cnt)=tempbuf
		loop
	
		;▼▼▼▼▼ 実績グラフ作成用の24時間分のデータを取得 ▼▼▼▼▼
		
		;***** 24時間分のデータを取得して配列に格納 *****
		gyou = 8
		sdim data,500
		notepos = 0 : sdim tempbuf,32 : dim value,24
		;***** 取得データを分解して配列に格納 *****
		i = 0 : p = 0
		repeat 24
			noteget data,gyou
			repeat 3
				getstr tempbuf,data,notepos,',' : notepos+=strsize
				if p==2 : value(i)=int(tempbuf)
				p++
			loop
			i++  : p = 0 : notepos = 0 : gyou++ : tempbuf = ""
		loop

		;***** 取得データの表示 *****
		font "Meiryo UI",12,1
		ls= strlen(resvName(1))
		if ls==4 {
   			dsp = strmid(resvName(1),0,1)+"時台実績"
		}else{
   			dsp = strmid(resvName(1),0,2)+"時台実績"
		}
 		color 24,24,24 : boxf 6,24,74,42
 		color 255,255,0 : pos 8,26 : mes dsp
 		strdigit resvName(2),4
		font "Arial",12,1
		color 0,0,200 : boxf 78,24,116,42
 		color 255,255,255 : pos 82,26  : mes refstr
 		font "Meiryo UI",10,1
 		color 0,0,255 : pos 118,27 : mes "万kW"
 		font "Meiryo UI",9,1
 		color 64,64,64 : pos 10,48  : mes update
 		
 		;***** 使用率の色分け設定 *****
 		if int(resvName(4))<93 : color 0,180,0
 		if (int(resvName(4))>93) & (int(resvName(4))<95) : color 242,210,0
 		if (int(resvName(4))>95) & (int(resvName(4))<97) : color 255,128,0
 		if int(resvName(4))>97 : color 255,0,0

 		;***** 使用率表示 *****
		font "Arial",16,1
   		pos 64,68 : mes resvName(4)
		font "Meiryo UI",12,1 : pos 84,70 : mes "%"
		
		r = 30.0
		kd=double(int(resvName(4)))*double(1.8)+double(180)
		rad=deg2rad(kd)
		Y = r*sin(rad)
		X =  r*cos(rad)
		color 0,180,0
		redraw 0 : pLine 150/2+1,128,X+150/2+1,Y+129,3 : redraw 1
	}else{
		font "Meiryo UI",16,1
		color 240,240,240 : boxf 0,20,150,170
		color 230,0,0 : pos 40,50 : mes "取得失敗!"
	}
   	delcom objXMLHTTP
  	gsel 0,1

	;***** 10min毎に更新 *****
	oncmd  gosub *OnTimer, WM_TIMER
	SetTimer hwnd, ID_TIMER, 600*1000, 0
	stop

*maru
	circle 30,66,120,136,1
	color 240,240,240 : circle  35,71,115,131,1
	return

*OnTimer
	;***** タイマー	*****
	goto *main
	return

*mode01
	;***** 手動更新 *****
	goto *main	
	stop

*mode02
	;***** 東京電力電力使用量サイト確認 *****
	exec "https://www.tepco.co.jp/forecast/index-j.html",16
	stop
	
*mode03
	;***** 東京電力電力使用量実績グラフ *****
	;***** 画面表示位置設定 *****
	sql_open Startdir+"setting¥¥poweruse.ini"
	sql_q "SELECT * FROM SValu WHERE ID = "+1
	px=sql_i("pxpos") : py=sql_i("pypos")
	sql_close
	
	screen 5,500,300,,px,py
	title "東京電力 - 電力使用量実績グラフ"
	basex  = 50   ;グラフ描画基準位置
	basey  = 10   ;縦軸開始位置
	kankau = 0    ;グラフ横軸間隔
	haba   = 14   ;グラフ帯幅
	kaishi = 0    ;グラフ横軸描画開始位置
	scale  = 200  ;縦軸の基本長さ
	vpos   = 200  ;縦軸の値の基点
	endy   = 250  ;横軸終端座標
	vrange = 100  ;縦軸目盛の最大値

	redraw 0
	color 240,240,240 : boxf
	;***** グラフタイトルの設定 *****
	color 64,64,6
	font "Meiryo UI",11,1
	pos 190,272 : mes "時間帯別電力使用量実績の推移"
	;***** グラフ単位の設定 *****
	color 0,0,0
	font "Meiryo UI",10,0
	pos 55,2    : mes "(万kw)"   ;縦軸単位
	pos 465,270 : mes "(時)"     ;横軸単位
	pos 360,5   : mes update     ;更新日時
	
	;***** 縦軸線 *****
	basex = 50 : basey = 10
	line basex-1,basey,basex-1,endy
	;***** 縦軸目盛線とスケール名 *****
	; 縦軸の最大値 : 6000 目盛の刻み単位 : 500 とした場合
	vrange = 6000 : basex = 50 : basey = 10
	repeat 13
		line basex-5,basey,basex-1,basey
		ls=strlen(str(vrange))
		if ls==4 : pos 18,basey-7
		if ls==3 : pos 24,basey-7
		if ls==2 : pos 30,basey-7
		if ls==1 : pos 34,basey-7
		mes vrange
		basey+=20
		vrange-=500
	loop
	;***** 横軸線 *****
	basex = 50 : basey = 10
	line basex,endy,vpos*2+82,endy
	;***** 横軸目盛線と項目名 *****
	x_pos=18 : d_pos=0
	repeat 24
		line basex+x_pos,endy,basex+x_pos,endy+4
		ls=strlen(str(cnt+1))
		if ls==1 : pos basex+6+d_pos,endy+7
		if ls==2 : pos basex+4+d_pos,endy+7
		mes cnt
		x_pos+=18
		d_pos+=18
	loop
	color 0,0,190
	basex = 50 : kaishi = 2 : kankaku = 0
	repeat 24
		data = basex+scale-((value(cnt)/50)*2)
		;***** グラフの描画位置を決める *****
		stpos  = basex+kaishi+kankaku
		endpos = basex+kaishi+haba+kankaku
		;***** 棒グラフ描画 *****
		color 0,0,190
		boxf stpos,data,endpos,endy
		kankaku+=2
		kaishi+=16
	loop
	redraw 1
	gsel 4,1
	stop

*gyopos
	;***** 現在の時間から当日実績の取得行を判定 *****
	H_time = gettime(4)
	M_time = gettime(5)
	switch H_time
		case 0 : gyou=32 : swbreak
		case 1 : gyou=9  : swbreak
		case 2 : gyou=10 : swbreak
		case 3 : gyou=11 : swbreak
		case 4 : gyou=12 : swbreak
		case 5 : gyou=13 : swbreak
		case 6 : gyou=14 : swbreak
		case 7 : gyou=15 : swbreak
		case 8 : gyou=16 : swbreak
		case 9 : gyou=17 : swbreak
		case 10: gyou=18 : swbreak
		case 11: gyou=19 : swbreak
		case 12: gyou=20 : swbreak
		case 13: gyou=21 : swbreak
		case 14: gyou=22 : swbreak
		case 15: gyou=23 : swbreak
		case 16: gyou=24 : swbreak
		case 17: gyou=25 : swbreak
		case 18: gyou=26 : swbreak
		case 19: gyou=27 : swbreak
		case 20: gyou=28 : swbreak
		case 21: gyou=29 : swbreak
		case 22: gyou=30 : swbreak
		case 23: gyou=31 : swbreak	
	swend
	return

*owari
	;***** 終了処理 *****
	_ginfo 1
	if prmx==5 {
		;***** 終了位置を保存 *****
		gsel 5,0
		sql_open Startdir+"setting¥¥poweruse.ini"
		px=str(ginfo_wx1)	; 現在の画面左上 X 座標取得
		py=str(ginfo_wy1)	; 現在の画面左上 y 座標取得
		sql_q "UPDATE SValu SET pxpos=" + prm_text(px) + ", pypos=" + prm_text(py)+ " WHERE ID="+1
		sql_close
		gsel 5,-1 : gsel 0 : stop
	}
	if prmx==0 {
		gsel 0,0
		await
		KillTimer hwnd, ID_TIMER
		sql_open Startdir+"setting¥¥poweruse.ini"
		wx=str(ginfo_wx1)	; 現在の画面左上 X 座標取得
		wy=str(ginfo_wy1)	; 現在の画面左上 y 座標取得
		;***** 終了位置を保存 *****
		sql_q "UPDATE TValu SET wxpos=" + prm_text(wx) + ", wypos=" + prm_text(wy)+ " WHERE ID="+1
		sql_close
		end
	}
	end

*move
	;***** マウスの左クリックボタンで移動 *****
	if wparam == 1 {
		obj_wmnclbtndown
		;***** 画面終端位置の取得と制御 *****
		dim rc,4
		GetWindowRect hwnd,varptr(rc)
		w = rc(2)-rc(0)
		h = rc(3)-rc(1)
		GetSystemMetrics SM_CXSCREEN : CSX=stat
		GetSystemMetrics SM_CYSCREEN : CSY=stat
		if rc(0)<0 : rc(0)=0
		if rc(1)<0 : rc(1)=0
		if rc(2)>CSX : rc(0)=CSX-w
		if rc(3)>CSY : rc(1)=CSY-h-30
		MoveWindow hwnd,rc(0),rc(1),w,h,1
		}
	return

3.ダウンロード

提供するソースコードのライセンスは、CC0 (クレジット表示不要、改変可、商用可) とします。自由に利用して頂いてかまいません。 尚、データの取得やプログラム実行において損害等が生じた場合は、筆者は一切の責任も負いません。全て自己責任でお願いします。

実行形式(exe)ファイルとソースコード一式のアーカイブは下記よりダウンロードして下さい。

ダウンロード

コメント

このブログの人気の投稿

Excelアドインで日本語形態素解析

階層構造JSONファイルの作成

HSPでコマンドプロンプトを制御する

TOP