Sabtu, 17 Desember 2016

Tutorial NodeMCU: Posting file to Google Drive (ESP8266, WeMos, Lua)

Pada tulisan ini akan membahas posting file dari perangkat IOT berbasis ESP8266 ke google drive. Untuk menjalankan script pada pembahasan ini, anda perlu melakukan kustomisasi firmware nodemcu dengan memasukan beberapa module yang diperlukan sebagai berikut:
NodeMCU custom build by frightanic.com
    branch: master
    commit: 81ec3665cb5fe68eb8596612485cc206b65659c9
    SSL: true
    modules: cjson,crypto,dht,file,gpio,http,net,node,rtctime,sntp,tmr,uart,websocket,wifi
 build     built on: 2016-12-10 10:19
 powered by Lua 5.1.4 on SDK 1.5.4.1(39cb9a32)
Langkah-langkah:
1. Aktifkan ke Google API console
2. Klik pada Project, dan pilih Create Project

Gambar 1. Pembuatan Project Baru
3. Tambahkan Library Drive API

Gambar 2. Tambahkan Drive API
4. Aktifkan Drive API dengan klik pada Enable


Gambar 3. Google Drive API setelah di Enable

5. Tambahkan Credentials pada Project
Gambar 4. Setting Credentials

Pastikan bahwa Authorized redirect URLS adalah: https://developers.google.com/oauthplayground

6. Setting OAuth 2.0 consent screen
Gambar 5. OAuth 2.0 Consent Screen
7. Aktifkan ke Google OAauth Playground
Gambar 6. OAuth 2.0 configuration
Check Use your own OAuth credentials, isikan OAuth Client ID, dan OAuth Client secret yang dapat diperoleh pada langkah sebelumnya (pada bagian Credentials pada Google Console API).

8. Tentukan authorize API scope, dalam hal ini adalah https://www.googleapis.com/auth/drive
Gambar 7. Tentukan Scope otorisasi yang diinginkan
9. Berikan otorisasi dengan klik pada Allow
 Gambar 8. Memperbolehkan View dan Manage file pada Google Drive
10. Buat Refresh Token dan Access Token

Gambar 9. Membuat Refresh Token dan Access Token
11. Catat Authorization code, Client_Id, Client_Secret, Refresh Token
12. Lakukan koding berikut ini:

-- by Hendra Soewarno (0119067305) based on documentation
-- https://developers.google.com/identity/protocols/OAuth2WebServer
-- https://developers.google.com/drive/v2/reference/
authorization_code = "your authorization code"
client_id = "your client id"
client_secret = "your client secret"
refresh_token = "your refresh token"
access_token = nil
id = nil
title = "init.lua"

function encode_parameter(str)
    return str:gsub('[^-%._~a-zA-Z0-9]', function(c)
        return string.format("%%%02x", c:byte()):upper()
    end)
end

function changeTitle()


    local url = "https://www.googleapis.com/drive/v2/files/" .. id

    local header = "Content-type: application/json\r\n" ..
        "Authorization: Bearer " .. access_token .. "\r\n"

    local payload = "{\"title\" : \"" .. title .. "\"}"
   
    http.request(url,
        "PATCH",
        header,
        payload,
        function(code, data)
            if (code ~= 200) then
                print("Failed : HTTP error code : " .. code, data)
            else
                print("Succeed : " .. code, data)
            end
        end)
end

function uploadFile(callback)

    local url = "https://www.googleapis.com/upload/drive/v2/files?uploadType=media&convert=true"
   
    local header = "Content-type: application/text\r\n" ..
        "Authorization: Bearer " .. access_token .. "\r\n"
      
    if file.open("init.lua", "r") then
        payload = file.read(512)
        print(payload)
        file.close()
  
        http.post(url,
            header,
            payload,
            function(code, data)
                if (code ~= 200) then
                    print("Failed : HTTP error code : " .. code, data)
                else
                    print("Succeed : " .. code, data)
                    local t = cjson.decode(data)
                    id = t["id"]
                    node.task.post(changeTitle)
                end
            end)
    end
end

function getAccessToken()

    local url = "https://www.googleapis.com/oauth2/v4/token"

    local payload =
        "code=" .. encode_parameter(authorization_code) ..
        "&redirect_uri=" .. encode_parameter("https://developers.google.com/oauthplayground") ..
        "&client_id=" .. encode_parameter(client_id) ..
        "&client_secret=" .. encode_parameter(client_secret) ..
        "&scope=" ..
        "&grant_type=authorization_code"
   
    local header = "Content-type: application/x-www-form-urlencoded\r\n"
        --"Content-length: " .. string.len(payload) .. "\r\n"
       
    http.post(url,
            header,
            payload,
            function(code, data)
                if (code ~= 200) then
                    print("Failed : HTTP error code : " .. code, data)
                else
                    print("Succeed : " .. code, data)
                end
            end)       
end

function refreshAccessToken(callback)

    local url = "https://www.googleapis.com/oauth2/v4/token"

    local payload =
        "client_secret=" .. encode_parameter(client_secret) ..
        "&grant_type=refresh_token" ..
        "&client_id=" .. encode_parameter(client_id) ..
        "&refresh_token=" .. encode_parameter(refresh_token)
   
    local header = "Content-type: application/x-www-form-urlencoded\r\n"
        --"Content-length: " .. string.len(payload) .. "\r\n"
       
    http.post(url,
            header,
            payload,
            function(code, data)
                if (code ~= 200) then
                    print("Failed : HTTP error code : " .. code, data)
                else
                    print("Succeed : " .. code, data)
                    local t = cjson.decode(data)
                    access_token = t["access_token"]
                    node.task.post(uploadFile)
                end
            end)       
end

--getAccessToken()
--refreshAccessToken()
refreshAccessToken()

Hasil Run:
Succeed : 200    {
 "kind": "drive#file",
 "id": "0B9b1RNrwu14WNmsyeV9vU1RHVEE",
 "etag": "\"m64ksZC09nG4-fB1IRhNTNcF3vg/MTQ4MTk3ODM2OTYzMw\"",
 "selfLink": "https://www.googleapis.com/drive/v2/files/0B9b1RNrwu14WNmsyeV9vU1RHVEE",
 "webContentLink": "https://drive.google.com/uc?id=0B9b1RNrwu14WNmsyeV9vU1RHVEE&export=download",
 "alternateLink": "https://drive.google.com/file/d/0B9b1RNrwu14WNmsyeV9vU1RHVEE/view?usp=drivesdk",
 "embedLink": "https://drive.google.com/file/d/0B9b1RNrwu14WNmsyeV9vU1RHVEE/preview?usp=drivesdk",
 "iconLink": "https://ssl.gstatic.com/docs/doclist/images/generic_app_icon_16.png",
 "title": "Untitled",
 "mimeType": "application/text",
 "labels": {
  "starred": false,
  "hidden": false,
  "trashed": false,
  "restricted": false,
  "viewed": true
 },
 "createdDate": "2016-12-17T12:39:29.633Z",
 "modifiedDate": "2016-12-17T12:39:29.633Z",
 "modifiedByMeDate": "2016-12-17T12:39:29.633Z",
 "lastViewedByMeDate": "2016-12-17T12:39:29.633Z",
 "markedViewedByMeDate": "1970-01-01T00:00:00.000Z",
 "version": "95279",
 "parents": [
  {
   "kind": "drive#parentReference",
   "id": "0ANb1RNrwu14WUk9PVA",
   "selfLink": "https://www.googleapis.com/drive/v2/files/0B9b1RNrwu14WNmsyeV9vU1RHVEE/parents/0ANb1RNrwu14WUk9PVA",
   "parentLink": "https://www.googleapis.com/drive/v2/files/0ANb1RNrwu14WUk9PVA",
   "isRoot": true
  }
 ],
 "downloadUrl": "https://doc-0s-0g-docs.googleusercontent.com/docs/securesc/6ef3a2thq1io4lguiv000fc3n8j157mi/hkbk6sp6ejiicbcbnmof5oobimi08o6e/1481976000000/14467718844153000671/14467718844153000671/0B9b1RNrwu14WNmsyeV9vU1RHVEE?e=download&gd=true",
 "userPermission": {
  "kind": "drive#permission",
  "etag": "\"m64ksZC09nG4-fB1IRhNTNcF3vg/oL4ijP0n8GxZ7JDTDMVZhQFn1h0\"",
  "id": "me",
  "selfLink": "https://www.googleapis.com/drive/v2/files/0B9b1RNrwu14WNmsyeV9vU1RHVEE/permissions/me",
  "role": "owner",
  "type": "user"
 },
 "originalFilename": "Untitled",
 "fileExtension": "",
 "md5Checksum": "74339827856ad32167b81a9b69c6a369",
 "fileSize": "512",
 "quotaBytesUsed": "512",
 "ownerNames": [
  "Hendra Soewarno"
 ],
 "owners": [
  {
   "kind": "drive#user",
   "displayName": "Hendra Soewarno",
   "picture": {
    "url": "https://lh3.googleusercontent.com/-zT-K5R2Kl7c/AAAAAAAAAAI/AAAAAAAAAM0/rk_CEnm7hMA/s64/photo.jpg"
   },
   "isAuthenticatedUser": true,
   "permissionId": "14467718844153000671",
   "emailAddress": "hendra.soewarno@gmail.com"
  }
 ],
 "lastModifyingUserName": "Hendra Soewarno",
 "lastModifyingUser": {
  "kind": "drive#user",
  "displayName": "Hendra Soewarno",
  "picture": {
   "url": "https://lh3.googleusercontent.com/-zT-K5R2Kl7c/AAAAAAAAAAI/AAAAAAAAAM0/rk_CEnm7hMA/s64/photo.jpg"
  },
  "isAuthenticatedUser": true,
  "permissionId": "14467718844153000671",
  "emailAddress": "hendra.soewarno@gmail.com"
 },
 "editable": true,
 "copyable": true,
 "writersCanShare": true,
 "shared": false,
 "explicitlyTrashed": false,
 "appDataContents": false,
 "headRevisionId": "0B9b1RNrwu14WK01kUUtOQTdTVlljNTlhMjN5Zml5MVFsMjNRPQ",
 "spaces": [
  "drive"
 ]
}

Succeed : 200    {
 "kind": "drive#file",
 "id": "0B9b1RNrwu14WNmsyeV9vU1RHVEE",
 "etag": "\"m64ksZC09nG4-fB1IRhNTNcF3vg/MTQ4MTk3ODM3MTYwNA\"",
 "selfLink": "https://www.googleapis.com/drive/v2/files/0B9b1RNrwu14WNmsyeV9vU1RHVEE",
 "webContentLink": "https://drive.google.com/uc?id=0B9b1RNrwu14WNmsyeV9vU1RHVEE&export=download",
 "alternateLink": "https://drive.google.com/file/d/0B9b1RNrwu14WNmsyeV9vU1RHVEE/view?usp=drivesdk",
 "embedLink": "https://drive.google.com/file/d/0B9b1RNrwu14WNmsyeV9vU1RHVEE/preview?usp=drivesdk",
 "iconLink": "https://ssl.gstatic.com/docs/doclist/images/generic_app_icon_16.png",
 "title": "init.lua",
 "mimeType": "application/text",
 "labels": {
  "starred": false,
  "hidden": false,
  "trashed": false,
  "restricted": false,
  "viewed": true
 },
 "createdDate": "2016-12-17T12:39:29.633Z",
 "modifiedDate": "2016-12-17T12:39:31.604Z",
 "modifiedByMeDate": "2016-12-17T12:39:31.604Z",
 "lastViewedByMeDate": "2016-12-17T12:39:31.604Z",
 "markedViewedByMeDate": "1970-01-01T00:00:00.000Z",
 "version": "95281",
 "parents": [
  {
   "kind": "drive#parentReference",
   "id": "0ANb1RNrwu14WUk9PVA",
   "selfLink": "https://www.googleapis.com/drive/v2/files/0B9b1RNrwu14WNmsyeV9vU1RHVEE/parents/0ANb1RNrwu14WUk9PVA",
   "parentLink": "https://www.googleapis.com/drive/v2/files/0ANb1RNrwu14WUk9PVA",
   "isRoot": true
  }
 ],
 "downloadUrl": "https://doc-0s-0g-docs.googleusercontent.com/docs/securesc/6ef3a2thq1io4lguiv000fc3n8j157mi/hkbk6sp6ejiicbcbnmof5oobimi08o6e/1481976000000/14467718844153000671/14467718844153000671/0B9b1RNrwu14WNmsyeV9vU1RHVEE?e=download&gd=true",
 "userPermission": {
  "kind": "drive#permission",
  "etag": "\"m64ksZC09nG4-fB1IRhNTNcF3vg/oL4ijP0n8GxZ7JDTDMVZhQFn1h0\"",
  "id": "me",
  "selfLink": "https://www.googleapis.com/drive/v2/files/0B9b1RNrwu14WNmsyeV9vU1RHVEE/permissions/me",
  "role": "owner",
  "type": "user"
 },
 "originalFilename": "Untitled",
 "fileExtension": "lua",
 "md5Checksum": "74339827856ad32167b81a9b69c6a369",
 "fileSize": "512",
 "quotaBytesUsed": "512",
 "ownerNames": [
  "Hendra Soewarno"
 ],
 "owners": [
  {
   "kind": "drive#user",
   "displayName": "Hendra Soewarno",
   "picture": {
    "url": "https://lh3.googleusercontent.com/-zT-K5R2Kl7c/AAAAAAAAAAI/AAAAAAAAAM0/rk_CEnm7hMA/s64/photo.jpg"
   },
   "isAuthenticatedUser": true,
   "permissionId": "14467718844153000671",
   "emailAddress": "hendra.soewarno@gmail.com"
  }
 ],
 "lastModifyingUserName": "Hendra Soewarno",
 "lastModifyingUser": {
  "kind": "drive#user",
  "displayName": "Hendra Soewarno",
  "picture": {
   "url": "https://lh3.googleusercontent.com/-zT-K5R2Kl7c/AAAAAAAAAAI/AAAAAAAAAM0/rk_CEnm7hMA/s64/photo.jpg"
  },
  "isAuthenticatedUser": true,
  "permissionId": "14467718844153000671",
  "emailAddress": "hendra.soewarno@gmail.com"
 },
 "editable": true,
 "copyable": true,
 "writersCanShare": true,
 "shared": false,
 "explicitlyTrashed": false,
 "appDataContents": false,
 "headRevisionId": "0B9b1RNrwu14WK01kUUtOQTdTVlljNTlhMjN5Zml5MVFsMjNRPQ",
 "spaces": [
  "drive"
 ]
}

Trouble Shooting:
> Failed : HTTP error code : 401    {
 "error": "invalid_client",
 "error_description": "The OAuth client was not found."

Pastikan client_id telah benar

> Failed : HTTP error code : 401    {
 "error": "invalid_client",
 "error_description": "Unauthorized"
}

Pastikan client_secret telah benar

> Failed : HTTP error code : 400    {
 "error": "invalid_grant",
 "error_description": "Bad Request"
}

Pastikan refresh_token telah benar




Tidak ada komentar:

Posting Komentar