UNPKG

node-red-contrib-google-smarthome

Version:

Lets you control Node-Red via Google Assistant or the Google Home App

244 lines (207 loc) 7.87 kB
<!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, minimum-scale=1, initial-scale=1, user-scalable=yes"> <script src="https://accounts.google.com/gsi/client" async defer></script> <title>Node-RED SmartHome</title> <style> body { margin: 0; font-family: 'Roboto', 'Noto', sans-serif; line-height: 1.5; min-height: 100vh; background-color: #eeeeee; } #app-header { color: #fff; background-color: #4285f4; position: relative; } #app-toolbar { display: flex; flex-direction: row; align-items: center; position: relative; height: 64px; padding: 0 16px; pointer-events: none; font-size: 20px; } #main-title { pointer-events: none; flex-basis: 100%; } #home-icon { width: 24px; height: 24px; padding: 8px; font-size: 0; } div.main { margin-left: auto; margin-right: auto; text-align: center; width: 100%; } h1 { color: #333; } #error_invalid_user { display: none; margin: 2em 0 1em; color: red; font-weight: bold; animation: bounceIn 1s; } @keyframes bounceIn { 0%, 33%, 66%, 100% { transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); } 0% { transform: scale3d(.5, .5, .5); } 33% { transform: scale3d(1.1, 1.1, 1.1); } 66% { transform: scale3d(.95, .95, .95); } 100% { transform: scale3d(1, 1, 1); } } #login-button { border-radius: 3px; padding: 1em; color: white; background-color: #4285f4; border: none; font-size: 16px; } @media (max-width: 800px) { h1 { font-size: 16pt; } .field { margin-left: 10px; margin-right: 10px; } } @media (min-width: 801px) { div.main { width: 600px; } } .field { display: flex; flex-flow: column-reverse; margin-bottom: 1em; text-align: left; } label, input { transition: all 0.2s; touch-action: manipulation; } input { background-color: transparent; font-size: 1.5em; border: 0; box-shadow: 0 1px 0 0 #212121; } input:focus { outline: 0; box-shadow: 0 2px 0 0 #3f51b5; transition: box-shadow 0.5s; } input:placeholder-shown + label { cursor: text; max-width: 66.66%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; transform-origin: left bottom; transform: translate(0, 2.125rem) scale(1.5); } ::-webkit-input-placeholder { opacity: 0; transition: inherit; } input:focus::-webkit-input-placeholder { opacity: 1; } input:not(:placeholder-shown) + label, input:focus + label { transform: translate(0, 0) scale(1); cursor: pointer; } </style> </head> <body> <div id="app-header"> <div id="app-toolbar"> <div id="home-icon"> <svg viewbox="0 0 24 24" preserveaspectratio="xMidYMid meet" focusable="false" style="pointer-events: none; display: block; fill: white; stroke: white;"> <g> <path d="M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z"></path> </g> </svg> </div> <div id="main-title">Node-RED SmartHome</div> </div> </div> <div class="main"> <h1>Link your devices to Google</h1> <svg xmlns="http://www.w3.org/2000/svg" baseProfile="tiny" width="100px" height="100px" id="Layer_1" viewBox="0 0 512 512" xml:space="preserve"> <g> <circle cx="156.268" cy="167.705" fill="#4285F4" r="156.268" /> <path d="M512,182.95c0,17.544-14.224,31.762-31.762,31.762s-31.762-14.218-31.762-31.762 c0-17.543,14.224-31.762,31.762-31.762S512,165.407,512,182.95z" fill="#34A853" /> <path d="M454.829,260.449c0,35.081-28.438,63.522-63.523,63.522c-35.088,0-63.524-28.441-63.524-63.522 c0-35.083,28.437-63.524,63.524-63.524C426.392,196.925,454.829,225.367,454.829,260.449z" fill="#EA4335" /> <path d="M467.533,424.339c0,42.1-34.124,76.225-76.228,76.225c-42.104,0-76.229-34.125-76.229-76.225 c0-42.098,34.124-76.227,76.229-76.227C433.409,348.112,467.533,382.241,467.533,424.339z" fill="#FBBC05" /> </g> </svg> <div id="error_invalid_user" > Invalid username or password. Please try again! </div> <form method="post" id="loginform" style="display: none;"> <div class="field"> <input type="text" name="username" id="username" autocomplete="off" autocapitalize="none" placeholder=" "> <label for="username">Username</label> </div> <div class="field"> <input type="password" name="password" id="password" placeholder=" "> <label for="password">Password</label> </div> <input type="hidden" name="client_id"> <input type="hidden" name="redirect_uri"> <input type="hidden" name="state" /> <input type="hidden" name="response_type" /> <input type="hidden" name="id_token" /> <button id="login-button">LOGIN</button> </form> <div id="g_id_onload" data-client_id="GOOGLE_CLIENT_ID" data-context="signin" data-ux_mode="popup" data-auto_prompt="false" data-callback="handleCredentialResponse"></div> <div id='login-google' style="display:none;align-items: center;" class="g_id_signin" data-type="standard"></div> </div> <script> function handleCredentialResponse(response) { document.querySelector('[name="id_token"]').value = response.credential; document.getElementById("loginform").submit(); } document.addEventListener("DOMContentLoaded", function (event) { let url = window.location.href.split('?')[0]; document.getElementById("loginform").action = url; // Set each hidden input from a value obtained from the URL let params = new URLSearchParams(window.location.search); document.querySelector('[name="client_id"]').value = params.get('client_id'); document.querySelector('[name="redirect_uri"]').value = decodeURIComponent(params.get('redirect_uri')); document.querySelector('[name="state"]').value = params.get('state'); if (USE_GOOGLE_LOGIN) { document.getElementById('login-google').style.display = 'block'; } else { document.getElementById('loginform').style.display = 'block'; } if (params.get('error')) { document.getElementById('error_invalid_user').style.display = 'block'; } }); </script> </body> </html>