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
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>