東芝から発売されているFlashAirを用いた開発を行う人々向けのまとめwikiです。※本wikiは東芝及びフィックスターズ、キオクシアとは何の関係もありません。お問合わせは管理者へお願いします。

すでに閉鎖されたサービスです

参考資料としてそのままにしています。

※現在,公式にAPIが開放されたため,このスクリプトを参考にするのはおすすめしません.
PythonですがこちらをどうぞTool:FIH Toolkit

概要

2016年12月現在、APIがアップロードと自己JOB実行・取得しかできないFlashAir IoT Hubにて、
  • CSV/JSONのダウンロード(他カードに対しても可能)
  • JOBの発行(他カードに対しても可能、というかそうでないと意味がない)*
  • PIOの操作(同上)*
  • JOB一覧の取得(同上)
  • グラフのラベルの読み出し(ユーザーおよび他カードからの文字情報の取得として利用可能)
  • グラフのラベルの書き換え(ユーザーおよび他カードへの文字情報の送出として利用可能)
  • カード名の書き換え
  • カード情報の取得
  • JOBの指定削除
  • JOBの全削除
  • 最終アクセス日時の取得
ができるようなスクリプトを組んでみました。

Uploadに関しては、純正スクリプトの機能と等価です。そのため、credentialが必要です。
それ以外の操作については、ブザウザと同様に振る舞うため、IoT Hubに登録してあるメールアドレスとパスワードが必要です。
カードID情報は、Uploadはcredentialと対でなければなりませんが、それ以外は任意のカードのIDが使用できます。

*動作確認は十分に行っていません

※一応利用規約上、抵触する事項はなさそうですが、自己責任でご利用ください。

ライセンス

3条項BSDライセンス(ただし、BASE64エンコードライブラリとしてLGPL2のコードを含みます)

スクリプト

※すべてを読み込むと極僅かな記述でメモリ不足になるため、必要な機能だけを切り出して使用してください。
ORIGIN = "https://iot-hub.flashair-developers.com"
REFERER = 'https://iot-hub.flashair-developers.com/'
API_URI = "https://iot-hub-api.flashair-developers.com/_/"
TYPE_JSON = 'application/json'
CLOSE = 'Close'

function upload(card_id,card_credential,values)
	local body = cjson.encode({values=values})
		b, c, h = fa.request {
		url='https://iot-hub-api.flashair-developers.com/v1/devices/' .. card_id .. '/measurements/flashair',
		method='POST',
		headers={
			['Authorization']='Basic ' .. card_credential,
			['Content-Length']=tostring(string.len(body)),
			['Content-Type']=TYPE_JSON,
		},
		body=body,
	}
	collectgarbage("collect")
	return c;
end

function login(mail,pass)
 	collectgarbage("collect")
	local code = enc(mail..":"..pass)
	local body = '{"long_session": false, "frontend": 2}'

	b, c, h = fa.request {
		url=API_URI..'login',
		method='POST',
		headers={
			['Authorization']='Basic ' .. code,
			['Content-Length']=tostring(string.len(body)),
			['Content-Type']=TYPE_JSON,
			['Origin']=ORIGIN,
			['Referer']=REFERER,
			['Connection']=CLOSE,
			['User-Agent']=UserAgent,
			['x-requested-with']='me', --"error":"csrf_protection"対策。
		},
		body=body,
	}

	collectgarbage("collect")
	if(c == 200)then
		local login_cookie = string.match (h, "Set%-Cookie%: (%g+)") --Cookie切り出し
		return login_cookie
	end

	return c; --401 = AuthErr
end

function logout(login_cookie)
 	collectgarbage("collect")
	local body = ''
	b, c, h = fa.request {
		url=API_URI..'logout',
		method='POST',
		headers={
			['Cookie']=login_cookie,
			['Content-Length']=tostring(string.len(body)),
			['Content-Type']=TYPE_JSON,
			['Origin']=ORIGIN,
			['Referer']=REFERER,
			['Connection']=CLOSE,
			['User-Agent']=UserAgent,
			['x-requested-with']='me', --"error":"csrf_protection"対策。
		},
		body=body,
	}

	collectgarbage("collect")
	if(c == 200)then
		local login_cookie = string.match (h, "Set%-Cookie%: (%g+)") --Cookie切り出し
		return login_cookie
	end

	return c; --401 = AuthErr
end



function download(login_cookie, card_id)
	b, c, h = fa.request {
		url=API_URI..'devices/'.. card_id ..'/measurements/flashair?format=json', --csvでCSV形式になる
	--取得範囲(開始・終了時刻)の指定もできるようだ。
	--https://iot-hub-api.flashair-developers.com/_/devices/0000000000000000/measurements/flashair?start=2016-12-03T16:50:19.983Z&end=2016-12-03T17:20:19.983Z&interval=30s&func=first
	
		method='GET',
		headers={
			['Referer']=REFERER,
			['Connection']=CLOSE,
			['Cookie']=login_cookie,
			['User-Agent']=UserAgent,
		}
	}
	collectgarbage("collect")
	return b
end


function labelGet(login_cookie, card_id)
	b, c, h = fa.request {
		url=API_URI..'flashairs/'.. card_id ..'/labels' ,
		method='GET',
		headers={
			['Referer']=REFERER,
			['Connection']=CLOSE,
			['Cookie']=login_cookie,
			['User-Agent']=UserAgent,
		}
	}
	collectgarbage("collect")
	return cjson.decode(b);
end

function labelSet(login_cookie, card_id, label1,label2,label3,label4,label5)
	local body = '{"labels":["'..label1..'","'..label2..'","'..label3..'","'..label4..'","'..label5..'"]}'

	b, c, h = fa.request {
		url=API_URI..'flashairs/'.. card_id ..'/labels',
		method='PUT',
		headers={
			['Content-Length']=tostring(string.len(body)),
			['Content-Type']=TYPE_JSON,
			['Origin']=ORIGIN,
			['Referer']=REFERER,
			['Connection']=CLOSE,
			['Cookie']=login_cookie,
			['User-Agent']=UserAgent,
		},
		body=body,
	}
	collectgarbage("collect")
	return c
end


function jobList(login_cookie, card_id, detail)
  local detail_str = "true"
  if(detail == false)then
	detail_str = "false"
  end
	b, c, h = fa.request {
		url=API_URI..'devices/'.. card_id ..'/jobs?with_details='..detail_str,
		method='GET',
		headers={
			['Referer']=REFERER,
			['Connection']=CLOSE,
			['Cookie']=login_cookie,
			['User-Agent']=UserAgent,
		}
	}
	collectgarbage("collect")
	return b
end

function jobDelete(login_cookie, card_id, jobid)
	--PRE-FLIGHT
	b, c, h = fa.request {
		url=API_URI..'devices/'.. card_id ..'/jobs/'..jobid,
		method='GET',
		headers={
			['Referer']=REFERER,
			['Connection']=CLOSE,
			['Cookie']=login_cookie,
			['User-Agent']=UserAgent,
		}
	}
	collectgarbage("collect")
	if(c ~= 200)then
		return c
	end

	local etag = string.match (h, "Etag%: (%g+)") --エンティティタグ切り出し
  
	--DELETE
	b, c, h = fa.request {
		url=API_URI..'devices/'.. card_id ..'/jobs/'..jobid,
		method='DELETE',
		headers={
			['Referer']=REFERER,
			['Connection']=CLOSE,
			['Cookie']=login_cookie,
			['User-Agent']=UserAgent,
			['If-Match']=''..etag..'',
		}
	}
	collectgarbage("collect")
	return c  
end

function jobPio(login_cookie, card_id,data,ctrl)
	local body = '{"request": {"type": "pio", "data": '..data..', "ctrl": '..ctrl..'}}'

	b, c, h = fa.request {
		url=API_URI..'devices/'.. card_id ..'/jobs',
		method='POST',
		headers={
			['Content-Length']=tostring(string.len(body)),
			['Content-Type']=TYPE_JSON,
			['Origin']=ORIGIN,
			['Referer']=REFERER,
			['Connection']=CLOSE,
			['Cookie']=login_cookie,
			['User-Agent']=UserAgent,
		},
		body=body,
	}
	collectgarbage("collect")
	if(c == 201)then -- Created
		local jobid = string.match (b, '%{%"id%"%:%"(%g+)%"%,%"status%"') --ID切り出し
		return jobid
	end

	return c
end


function jobRequest(login_cookie, card_id, path , arg_json)
	local body = '{"request": {"type": "script", "path": "'..path..'", "arguments": "'..arg_json..'"}}'

	b, c, h = fa.request {
		url=API_URI..'devices/'.. card_id ..'/jobs',
		method='POST',
		headers={
			['Content-Length']=tostring(string.len(body)),
			['Content-Type']=TYPE_JSON,
			['Origin']=ORIGIN,
			['Referer']=REFERER,
			['Connection']=CLOSE,
			['Cookie']=login_cookie,
			['User-Agent']=UserAgent,
		},
		body=body,
	}
	collectgarbage("collect")
	if(c == 201)then -- Created
		local jobid = string.match (b, '%{%"id%"%:%"(%g+)%"%,%"status%"') --ID切り出し
		return jobid
	end

	return c
end

function pioGet(login_cookie, card_id)
	b, c, h = fa.request {
		url=API_URI..'devices/'.. card_id ..'/measurements/pio/diff' ,
		method='GET',
		headers={
			['Referer']=REFERER,
			['Connection']=CLOSE,
			['Cookie']=login_cookie,
			['User-Agent']=UserAgent,
		}
	}
	collectgarbage("collect")
	return b
end


function jobDeleteAll(login_cookie, card_id)
  	local cnt = 0;
	while true do
		local dat = jobList(login_cookie,card_id,false)
		collectgarbage("collect")
		local tb = cjson.decode(dat);
		collectgarbage("collect")

		for key, val in pairs(tb["jobs"]) do
	    	c = jobDelete(login_cookie,card_id,val["id"])
	    	cnt = cnt+1
			collectgarbage("collect")
		end
    
    	if(tb["hasNext"]~=true)then
      		break;
      	end
    end

	return cnt;
end

function LastAccessGet(login_cookie, card_id)
	b, c, h = fa.request {
		url=API_URI..'devices/'.. card_id ..'/last_access' ,
		method='GET',
		headers={
			['Referer']=REFERER,
			['Connection']=CLOSE,
			['Cookie']=login_cookie,
			['User-Agent']=UserAgent,
		}
	}
	collectgarbage("collect")
	return b
end

function DevicesGet(login_cookie)
	b, c, h = fa.request {
		url=API_URI..'devices' ,
		method='GET',
		headers={
			['Referer']=REFERER,
			['Connection']=CLOSE,
			['Cookie']=login_cookie,
			['User-Agent']=UserAgent,
		}
	}
	collectgarbage("collect")
	return b
end

function DeviceNameSet(login_cookie, card_id, name)
	local body = '{"name": "'..name..'"}'

	b, c, h = fa.request {
		url=API_URI..'devices/'.. card_id,
		method='PATCH',
		headers={
			['Content-Length']=tostring(string.len(body)),
			['Content-Type']=TYPE_JSON,
			['Origin']=ORIGIN,
			['Referer']=REFERER,
			['Connection']=CLOSE,
			['Cookie']=login_cookie,
			['User-Agent']=UserAgent,
		},
		body=body,
	}
	collectgarbage("collect")
	return c
end


-- Lua 5.1+ base64 v3.0 (c) 2009 by Alex Kloss <alexthkloss@web.de>
-- licensed under the terms of the LGPL2
local b='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
function enc(data)
	return ((data:gsub('.', function(x) 
		local r,b='',x:byte()
		for i=8,1,-1 do r=r..(b%2^i-b%2^(i-1)>0 and '1' or '0') end
		return r;
	end)..'0000'):gsub('%d%d%d?%d?%d?%d?', function(x)
		if (#x < 6) then return '' end
		local c=0
		for i=1,6 do c=c+(x:sub(i,i)=='1' and 2^(6-i) or 0) end
		return b:sub(c+1,c+1)
	end)..({ '', '==', '=' })[#data%3+1])
end
--end of base64


--main--

UserAgent = 'IoT Hub Scraper on FlashAir Alpha 0.2';
id = "0000000000000000000"; --Upload & Download & Call
cred = "AaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAa"; --Upload only
user = "mailadr@mailadr.com"; -- Download & Call
pass = "password"; -- Download & Call

id2="0000000000000000000"

stat = upload(id,cred,{0,1,2,3,4})
print(stat);

cok = login(user,pass)
print(cok);

lab = labelGet(cok, id)
print(lab);

dat = download(cok, id)
print(dat);

--dat = jobDelete(cok,id2,"dng4akdel8g0")
--print(dat)

dat = jobPio(cok,id2,0x0E,0x0E);
print(dat);

--[[
for i = 1, 3 do
	dat = jobPio(cok, id2,bit32.band(i,0x0E),0x0E);
	print(dat);
end
]]

dat = DevicesGet(cok);
print(dat);

dat = LastAccessGet(cok,id2)
print(dat);

dat = DeviceNameSet(cok,id,"Lua (supported by "..UserAgent..")");
print(dat);

dat = jobRequest(cok, id,"/HelloWorld.lua","World")
print(dat);

dat = jobList(cok,id2,false)
print(dat);

dat = labelSet(cok,id,"Hello! This is " .. UserAgent ,lab.labels[2],lab.labels[3],lab.labels[4],lab.labels[5])
print(dat);

dat = pioGet(cok, id2);
print(dat);

dat = jobDeleteAll(cok,id2)
print(dat);

cok = logout(cok)
print(cok);

コメントをかく


「http://」を含む投稿は禁止されています。

利用規約をご確認のうえご記入下さい

Menu

スマートフォンの方は画面下部よりPC版に切り替えることをおすすめします

アクセス解析中

忍者アナライズ

GoogleAnalytics

編集にはIDが必要です