Lab_interaccio/2024/smartcitizen-kit-21-dev/esp/build_data/index.html

980 lines
126 KiB
HTML
Raw Normal View History

2025-02-25 21:29:42 +01:00
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<link rel="shortcut icon" href="data:image/png;base64,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
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>SCK Setup</title>
</head>
<body>
<div id="loading">
<h1 style="color:#0C2EFB; font-family:Arial, sans-serif; padding:100px 10px;">Loading... please wait!</h1>
</div>
<div class="container" id="app" style="display:none;">
<div class="topnav sticky ">
<div class="bg-black">
<p class="text-center">Smart Citizen Kit Setup</p>
<div id="topbackbtn">
<a v-show="page[currentPage].back" v-on:click="gotoPage(page[currentPage].backTo)" href="#">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" x="0px" y="0px" viewBox="0 0 100 125" style="enable-background:new 0 0 100 100;" xml:space="preserve"><g><polygon points="55.7,33.8 39.7,50.2 56.1,66.3 58.9,63.4 45.3,50.1 58.6,36.6 "/></g></svg>
</a>
</div>
<div id="toplogo">
<a v-show="!page[currentPage].back" href="/">
<svg version="1.1" id="Capa_1" width="36px" height="36px" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 200 200" style="enable-background:new 0 0 200 200;" xml:space="preserve">
<g>
<path class="st0" d="M97.6,124.8c0.1,4.5-1.1,8.3-3.5,11.5c-1.6,2.2-3.8,3.7-6.6,4.6c-1.6,0.5-3.3,0.7-5.4,0.7 c-3.8,0-6.9-0.9-9.4-2.8c-2.1-1.6-3.7-3.6-5-6.3c-1.2-2.6-1.9-5.7-2.1-9.1l9.2-0.7c0.4,3.7,1.4,6.5,3,8.1c1.2,1.3,2.5,1.9,4,1.8 c2.1-0.1,3.8-1.1,5.1-3.1c0.6-1,1-2.4,1-4.3c0-2.7-1.2-5.3-3.6-8c-1.9-1.8-4.8-4.5-8.6-8.1c-3.2-3.1-5.5-5.9-6.8-8.4 c-1.4-2.8-2.1-5.8-2.1-9c0-5.8,2-10.3,5.9-13.3c2.4-1.8,5.4-2.7,9-2.7c3.4,0,6.4,0.8,8.9,2.3c1.9,1.2,3.5,2.9,4.6,5 c1.2,2.2,1.9,4.6,2.1,7.4l-9.3,1.7c-0.3-2.6-1-4.7-2.3-6.1c-0.9-1.1-2.2-1.6-3.9-1.6c-1.8,0-3.1,0.8-4,2.4 c-0.8,1.3-1.1,2.8-1.1,4.7c0,2.9,1.3,5.9,3.8,9c1,1.2,2.4,2.5,4.3,4.1c2.2,1.9,3.7,3.2,4.5,3.9c2.4,2.4,4.2,4.7,5.5,7.1 c0.6,1.1,1.1,2.1,1.5,3C97.1,121,97.5,123,97.6,124.8"/>
<path class="st0" d="M119.3,141.6c-4.2,0-7.8-1.5-10.8-4.4c-3-2.9-4.4-6.5-4.4-10.7V91.8c0-4.2,1.5-7.8,4.5-10.8 c3-3,6.5-4.4,10.7-4.4c4.2,0,7.8,1.5,10.7,4.5c2.9,3,4.4,6.5,4.4,10.7v7.2h-9.9v-7.4c0-1.5-0.5-2.8-1.6-3.9 c-1.1-1.1-2.4-1.6-3.9-1.6c-1.5,0-2.8,0.5-3.9,1.6c-1.1,1.1-1.6,2.4-1.6,3.9v34.7c0,1.5,0.5,2.8,1.6,3.9c1,1.1,2.4,1.6,3.9,1.6 c1.5,0,2.8-0.5,3.9-1.6c1.1-1.1,1.6-2.4,1.6-3.9v-8.8h9.9v8.9c0,4.2-1.5,7.8-4.5,10.7C127,140.1,123.4,141.6,119.3,141.6"/>
<path class="st0" d="M158.3,57.5L158.3,57.5c-3.4,2.7-4.2,7.6-1.7,11.2c13.3,19.2,16.7,44.5,6.8,67.5 c-15.3,35.4-56.4,51.8-91.9,36.5C36.1,157.3,19.7,116.2,35,80.7c9.2-21.2,27.6-35.5,48.6-40.4l5.9,19.8c0.6,1.8,1.9,2,3,0.5 l24.6-35.2c1.1-1.6,0.5-3.4-1.2-4.1L76,5.5c-1.8-0.7-2.8,0.2-2.2,2l5.4,17.9c-25.1,6.3-47,23.7-58.1,49.3 c-18.8,43.6,1.3,94.2,44.9,113c43.6,18.8,94.2-1.3,113-44.9c12.3-28.5,7.9-59.9-8.7-83.5C167.5,55.4,162.1,54.5,158.3,57.5z"/>
</g>
</svg>
</a>
</div>
<div id="topinfo">
<a v-show="currentPage === 0" v-on:click='gotoPage(6)'>
<svg width="29px" height="32px" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" x="0px" y="0px" viewBox="0 0 100 125" style="enable-background:new 0 0 100 100;" xml:space="preserve"><g><g><g><path d="M53.1,73h-7.2c-1.1,0-1.9-0.9-1.9-1.9V44h11v27.1C55,72.1,54.1,73,53.1,73z"/></g></g></g><g><g><g><path d="M55,39H44v-9.1c0-1.1,0.9-1.9,1.9-1.9h7.2c1.1,0,1.9,0.9,1.9,1.9V39z"/></g></g></g><g><path d="M50,96C24.6,96,4,75.4,4,50S24.6,4,50,4s46,20.6,46,46S75.4,96,50,96z M50,12c-21,0-38,17-38,38s17,38,38,38s38-17,38-38 S71,12,50,12z"/></g></svg>
</a>
</div>
</div>
<div id="toast-wrapper"></div>
</div>
<div class="content">
<div v-show="showDebug">
<button class="" type="button" v-on:click='gotoPage("0")'>Start</button>
<button class="" type="button" v-on:click='gotoPage()'>Next</button>
<hr />
</div>
<div v-show='page[0].visible'>
<h2 class="text-blue text-center">WELCOME TO SMART CITIZEN KIT CONFIGURATION</h2>
<div v-if="kitstatus.updateNeeded == 'true' || allowForceUpdate " class="text-center p10">
<h4 v-if="kitstatus.updateNeeded == 'true'" style="color:red;">FIRMWARE UPDATE IS NEEDED!</h4>
<h4>Upload your ESP firmware here:</h4>
<form @submit.prevent="submitFirmware" @change="checkUploadForm" method="POST" enctype='multipart/form-data'>
<input id="firmware_filename" accept=".bin" type='file' name='update' ref="file" class="p10" style="border:1px solid #ccc">
<div v-if="file" class="font70">
<p>Size: <b>{{file.size}}</b></p>
</div>
<input id="firmware_button" type='submit' value='Update' class="btn":disabled=!checkIfFileSelected v-bind:class="{ 'bg-yellow': checkIfFileSelected }">
</form>
<p id="firmware-update-status" class="text-yellow"></p>
<p id="firmware-status-extra">If you need more information please visit <span class="text-blue"><a href="https://docs.smartcitizen.me">docs.smartcitizen.me</a></span></p>
</div>
<div v-else>
<h4 class="content-wrap">Is this your first time here?</h4>
<p class="content-wrap">Before you start, make sure you visit <span class="text-blue"><a href="https://start.smartcitizen.me">start.smartcitizen.me</a></span>. </p><p class="content-wrap">In <b>Online mode</b>, you need to obtain a Device Key (<em>token</em>) to send data to the platform.</p>
<div class="side-by-side">
<button class="btn bg-yellow btn-big" id="start" type="button" v-on:click='gotoPage()'>Online<br>(Wi-Fi)</button>
<button class="btn bg-blue btn-big" id="start" type="button" v-on:click='gotoPage(5)'>Offline<br>(SD-Card)</button>
<!-- <p>or go to <span class='text-blue'> <a v-on:click='gotoPage(5)'>SD Card mode</a></span> </p> -->
</div>
</div>
</div>
<div v-show='page[1].visible'>
<h4>Device key</h4>
<div class="field-token">
<label>Token: <b>{{usertoken}}</b></label>
<input
name="token"
autocomplete='off'
autocorrect='off'
autocapitalize='off'
spellcheck='false'
type="text"
maxlength="6"
v-model='usertoken'
minlength="6"
placeholder="6 letters"
>
</div>
<div style="height:20px; margin-top:10px;">
<!--
<p v-show='!usertokenCheck' style="margin:0;">
{{ 6 - usertoken.length }} letters missing.
</p>
-->
<p class="text-red"> {{ this.usertokenError }} </p>
</div>
<button class="btn next bg-yellow" type="button" v-on:click='checkIfTokenIsValid()'>Next</button>
</div>
<div v-show='page[2].visible'>
<h4>WiFi connection</h4>
<div class="field-wifi">
<label>SSID (Network name)</label>
<select v-model="selectedWifi" name="ssid" id="ssid" type="text" length=64>
<!-- Here goes the Access point list -->
<option disabled value="">Select your wifi ({{wifis["nets"].length}})</option>
<option v-for="x in sortedWifi" :key="x.id" :value="x.ssid">{{x.ssid}}</option>
</select>
</div>
<div class="field-group">
<label>Password</label>
<input name="password" type="text" v-model='wifipass' length=64 placeholder="Your wifi password" autocapitalize="none" >
</div>
<button class="btn" v-bind:class="{ 'bg-yellow': selectedWifiCheck }" type="button" id="connect" :disabled=!selectedWifiCheck v-on:click="jsPost('set','connect')">Connect</button>
</div>
<div v-show='page[3].visible'>
<h4>Trying to connect..</h4>
<p>Your kit is now trying to connect to the online platform, and this website has no connection to the kit anymore.</p>
<p>Check the light on your Kit:</p>
<ul>
<li>Your Kit light should be <span class="blue-breathe" >blue</span>.<br />If it's blinking fast, there is a problem with the WiFi credentials.<br />Press the button until the light turns <span class="red-breathe" >red</span> and repeat the process.</li>
</ul>
</div>
<div v-show='page[4].visible'>
<h4 class="text-blue">Your kit status</h4>
<table>
<tr>
<td>Mode</td>
<td>{{ kitstatus.net }}</td>
</tr>
<tr>
<td>Wifi</td>
<td>{{ kitstatus.wifi }}</td>
</tr>
<tr>
<td>Battery</td>
<td>{{ kitstatus.battPercent }}</td>
</tr>
<tr>
<td>Published</td>
<td>{{ kitstatus.last_publish }}</td>
</tr>
</table>
<p>If you want to run a full setup again, press the button on your Kit for 15 seconds and reconnect, and refresh this page.</p>
<button class="btn bg-yellow" type="button" v-on:click='gotoPage(2)'>Edit Wifi</button>
</div>
<div v-show='page[5].visible'>
<h4>SD Card</h4>
<p>Your Kit can store the data on a microSD card. Later on, you can read it the CSV files with your computer.</p>
<p>This is useful when no Wi-Fi is available at your location.</p>
<p>You will need to insert a valid microSD card and click the button below.</p>
<p>Once you do it, you will be disconnected and the Kit will start recording data.</p>
<!--
<table>
<tr>
<td>SD CARD</td>
<td>{{ kitstatus.sdcard }}</td>
</tr>
<tr>
<td>TIME SYNC</td>
<td>{{ kitstatus.time }}</td>
</tr>
</table>
-->
<button class="btn bg-yellow" type="button" v-on:click="jsPost('set', 'synctime')">START LOGGING</button>
</div>
<div v-show='page[6].visible'>
<label for="force-firmware-update">
<input type="checkbox" id="force-firmware-update" v-on:click="allowFirmwareUpdate()" />
Force allow firmware update
</label>
<h4>Kit info:</h4>
<div id="kitinfo" class="font70">
<ul>
<li v-for="value, index in kitstatus"><b>{{index}}</b>: {{value}}</li>
</ul>
</div>
<button id="copied-notification" class="btn bg-yellow" type="button" v-on:click="copyTextToClipboard()">Copy info to clipboard</button>
<div id="id-logging">
<hr />
<p>Log:</p>
<ul v-show="logging && logging.length">
<li v-for="x in logging" class="font70">
{{x}}
</li>
</ul>
</div>
<label for="label-showDebug" class="font80">
<input type="checkbox" name="" id="label-showDebug" value="" v-model="showDebug"/>
Enable Debug buttons
</label>
<p class="font70">Vue.js version: {{vueVersion}}</p>
<!--
<label for="label-experimental" class="font80">
<input type="checkbox" name="" id="label-experimental" value="" v-model="showExperimental"/>
Show Experimental (UNSTABLE!)
</label>
-->
</div>
<div v-show='page[7].visible'>
<h4>Empty page</h4>
</div>
<br />
<br />
<div class="text-center text-grey font80">
SAM: {{ kitstatus.SAMversion }}
-
ESP: {{ kitstatus.ESPversion }}
<div v-show="showDebug" >
<hr />
{{ currentPage }} - {{ page[currentPage].footer }}
</div>
</div>
<!-- experimental -->
<div v-show="showExperimental" id="id-experimental" class="mt20">
<p v-show="development">Development mode</p>
<label for="id-show-interval">
<input type="checkbox" name="" id="id-show-interval" value="" v-model="showInterval" />
Intervals
</label>
<div v-show="showInterval" id="intervals">
<h5>Global - Intervals</h5>
<div>
<p class="font70">How often should the kit go online to publish the data?</p>
<label for="pubint">Publish interval hours: {{publishinterval}}</label>
<input type="range" name="pubint" id="" v-model="publishinterval" max="100" />
</div>
<div>
<p class="font70">How often should the kit poll the sensors?</p>
<label for="readint">Reading interval seconds: {{readinginterval}}</label>
<input type="range" name="readint" id="" value="60" v-model="readinginterval" max="1000" />
</div>
<div>
<p>Estimated battery duration: {{ Number(publishinterval * (readinginterval / 100)).toFixed(1) }} Hours</p>
</div>
</div>
<hr />
<label for="id-showsdcard">
<input type="checkbox" name="" id="id-showsdcard" value="" v-model="showSdCard" />
SD Card
</label>
<div v-show="showSdCard" id="sdcard">
<h5>SD CARD</h5>
<div>
<p class="font70">The kit should never post my data with Wifi</p>
<label for="sdlog">
<input type="checkbox" name="usesdcard" id="sdlog" value="" v-model="sdlog" />
Log *only* to SD Card
</label>
</div>
<br />
<div v-if="sdlog">
<p class="font70">When your kit runs out of battery, it loses the time. This will allow the kit to go online and fetch the time.</p>
<p class="font70">If you don't, all your measurements will be dated from the year 1971</p>
<label for="usewifisync">
<input type="checkbox" name="usewifisync" id="usewifisync" v-model="wifisync" />
Use Wifi to sync time
</label>
<p v-if="!wifipass && wifisync" class="text-red font80">Don't forget to give the kit your wifi password!</p>
</div>
<div v-show="browsertime != devicetime" id="timesync">
<table>
<tr><td>Browser time:</td><td>{{ browsertime }}</td></tr>
<tr><td>Device time: </td><td>{{ devicetime }}</td></tr>
<tr><td>Diff: </td><td>{{ browsertime - devicetime }}</td></tr>
</table>
</div>
<p class="font70">When you click this button the device will go offline and start working. </p>
<p class="font70">(You will loose connection to the device)</p>
<button class="btn bg-red" type="button" v-on:click="jsPost('set', 'synctime')">Start logging on SD Card</button>
</div> <!-- /sd card -->
<hr />
<label for="id-show-sensors">
<input type="checkbox" name="" id="id-show-sensors" value="" v-model="sensors"/>
Show sensors
</label>
<div id="sensors" v-show="sensors">
<h5>Sensors</h5>
<p class="font70">The new SCK allows you to configure each sensor independently.</p>
<div class="check-sensor">
<label for="sensorlight">Light
<input type="checkbox" name="sensor1" id="sensorlight" value="" v-model="sensor1" />
</label>
</div>
<div class="check-sensor">
<label for="sensormic">Microphone
<input type="checkbox" name="sensor2" id="sensormic" value="" v-model="sensor2" />
</label>
</div>
<div class="check-sensor">
<label for="sensorco2">CO2
<input type="checkbox" name="sensor3" id="sensorco2" value="" v-model="sensor3" />
</label>
</div>
<div class="check-sensor">
<label for="sensorthermo">Thermometer
<input type="checkbox" name="sensor4" id="sensorthermo" value="" v-model="sensor4" />
</label>
</div>
</div><!-- /sensors -->
</div> <!-- /experimental -->
</div><!-- /content -->
</div><!-- container -->
<!-- 'gulp compress' will inject the following and create 2 files, final.html and index.html.gz -->
<style>
/*
Colors:
--black: #262626;
--blue: #0019FF;
--blue_light: #55C4F5;
--dark_form: #221E1B;
--green: #00E597;
--grey: #BCBCBC;
--grey_bg: #E3E3E3;
--purple: #5B22B2;
--purple_dark: #4919A2;
--purple_light: #E4DDF0;
--red: #FF3A4B;
--yellow: #FFBA00;
*/
body {
margin:0px;
color: #434343;
font-family: 'Roboto','Verdana',Helvetica,Arial,sans-serif;
font-size: 22px;
line-height: 1.1;
}
.container {
margin: 0 auto;
max-width: 900px;
}
form .field-group {
box-sizing: border-box;
clear: both;
padding: 10px 0;
position: relative;
margin: 1px 0;
width: 100%;
}
form .field-group > label {
color: #a5acb2;
display: block;
margin: 0 0 5px 0;
position: relative;
word-wrap: break-word;
}
label{
display: block;
padding:5px 0px;
}
select[type=text], input[type=text] {
background: #fff;
border: 1px solid #d0d0d0;
border-radius: 2px;
box-sizing: border-box;
color: #434343;
font-family: inherit;
font-size: 1.1em;
height: 2.2em;
padding: 4px 5px;
margin: 0;
width: 100%;
}
input[type=text]:focus {
border-color: #4C669F;
outline: 0;
}
input[type=range] {
width: 95%;
}
svg {
fill: #fff;
}
table{
background: #f3f3f3;
border-collapse: collapse;
border: 1px solid #ccc;
width: 100%;
}
td{
padding:10px;
border-bottom: 1px solid #ddd;
}
a{
cursor:pointer;
}
.side-by-side {
display: flex;
}
.btn{
border-radius: 30px;
border: 0px;
color: white;
cursor: pointer;
display: block;
font-size: 22px;
font-variant: normal;
font-weight: 400;
height: 2.5em;
margin: 30px auto;
padding: 2px 40px;
text-decoration: none;
}
.content-wrap{
margin: 10px;
}
.btn-big{
height: 8.5em;
width: 45%;
font-weight: bold;
padding: 5px 15px !important;
margin: 20px auto;
}
.bg-black{
background-color: #262626 !important;
}
.bg-blue{
background-color: #0019FF !important;
}
.bg-yellow{
background-color: #FFBA00 !important;
color:#262626;
}
.bg-red{
background-color: #FF3A4B !important;
}
.bg-grey{
background: #E3E3E3;
color: #8db2ba;
}
.text-blue{
color: #0019FF !important;
text-decoration: none;
}
.text-green{
color: #00E597 !important;
}
.text-grey{
color: #BCBCBC;
}
.text-yellow{
color: #FFBA00 !important;
}
.text-red{
color: #FF3A4B;
}
.text-right{
text-align: right;
}
.text-center{
text-align: center;
}
.content{
padding: 10px 10px 20px;
}
.font70{
font-size: 70%;
}
.font80{
font-size: 80%;
}
.mt20{
margin-top: 20px;
}.mt0{
margin-top: 0px;
}
.p10{
padding: 10px;
}
.topnav{
color:white;
min-height:50px;
top:0;
width:inherit;
bottom:auto;
z-index: 100;
}
.topnav p{
margin: 0px;
padding:15px 0px 0px;
height:40px;
}
#toplogo{
position:absolute;
top:10px;
left:10px;
}
#topinfo{
position:absolute;
top:10px;
right:10px;
height:30px;
width:30px;
}
#topbackbtn{
position:absolute;
top:0px;
width:57px;
}
.sticky {
position: sticky;
position: -webkit-sticky;
position: -moz-sticky;
position: -ms-sticky;
position: -o-sticky;
}
.check-sensor{
clear:both;
margin-bottom:30px;
}
.check-sensor input{
float:right;
}
.toast {
background-color: #8CC252;
color: #000;
padding: 10px;
margin: 2px 0px;
}
.monospace{
font-family: 'monospace';
}
@media (max-width: 320px){
.sticky{
width:320px;
position:fixed;
}
.content {
padding: 70px 5px 20px;
}
#topbackbtn{
position:absolute;
height:70px;
left: 0px;
top:0px;
width:57px;
}
}
.red-breathe {
animation:2s red-breathe ease infinite;
}
@keyframes red-breathe {
0% { color: white;}
50% { color: red;}
100% { color: white;}
}
.blue-breathe {
animation:2s blue-breathe ease infinite;
}
@keyframes blue-breathe {
0% { color: white;}
50% { color: blue;}
100% { color: white;}
}
</style>
<script type="text/javascript" charset="utf-8" >
/*!
* Vue.js v2.6.10
* (c) 2014-2019 Evan You
* Released under the MIT License.
*/
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e=e||self).Vue=t()}(this,function(){"use strict";var e=Object.freeze({});function t(e){return null==e}function n(e){return null!=e}function r(e){return!0===e}function i(e){return"string"==typeof e||"number"==typeof e||"symbol"==typeof e||"boolean"==typeof e}function o(e){return null!==e&&"object"==typeof e}var a=Object.prototype.toString;function s(e){return"[object Object]"===a.call(e)}function c(e){var t=parseFloat(String(e));return t>=0&&Math.floor(t)===t&&isFinite(e)}function u(e){return n(e)&&"function"==typeof e.then&&"function"==typeof e.catch}function l(e){return null==e?"":Array.isArray(e)||s(e)&&e.toString===a?JSON.stringify(e,null,2):String(e)}function f(e){var t=parseFloat(e);return isNaN(t)?e:t}function p(e,t){for(var n=Object.create(null),r=e.split(","),i=0;i<r.length;i++)n[r[i]]=!0;return t?function(e){return n[e.toLowerCase()]}:function(e){return n[e]}}var d=p("slot,component",!0),v=p("key,ref,slot,slot-scope,is");function h(e,t){if(e.length){var n=e.indexOf(t);if(n>-1)return e.splice(n,1)}}var m=Object.prototype.hasOwnProperty;function y(e,t){return m.call(e,t)}function g(e){var t=Object.create(null);return function(n){return t[n]||(t[n]=e(n))}}var _=/-(\w)/g,b=g(function(e){return e.replace(_,function(e,t){return t?t.toUpperCase():""})}),$=g(function(e){return e.charAt(0).toUpperCase()+e.slice(1)}),w=/\B([A-Z])/g,C=g(function(e){return e.replace(w,"-$1").toLowerCase()});var x=Function.prototype.bind?function(e,t){return e.bind(t)}:function(e,t){function n(n){var r=arguments.length;return r?r>1?e.apply(t,arguments):e.call(t,n):e.call(t)}return n._length=e.length,n};function k(e,t){t=t||0;for(var n=e.length-t,r=new Array(n);n--;)r[n]=e[n+t];return r}function A(e,t){for(var n in t)e[n]=t[n];return e}function O(e){for(var t={},n=0;n<e.length;n++)e[n]&&A(t,e[n]);return t}function S(e,t,n){}var T=function(e,t,n){return!1},E=function(e){return e};function N(e,t){if(e===t)return!0;var n=o(e),r=o(t);if(!n||!r)return!n&&!r&&String(e)===String(t);try{var i=Array.isArray(e),a=Array.isArray(t);if(i&&a)return e.length===t.length&&e.every(function(e,n){return N(e,t[n])});if(e instanceof Date&&t instanceof Date)return e.getTime()===t.getTime();if(i||a)return!1;var s=Object.keys(e),c=Object.keys(t);return s.length===c.length&&s.every(function(n){return N(e[n],t[n])})}catch(e){return!1}}function j(e,t){for(var n=0;n<e.length;n++)if(N(e[n],t))return n;return-1}function D(e){var t=!1;return function(){t||(t=!0,e.apply(this,arguments))}}var L="data-server-rendered",M=["component","directive","filter"],I=["beforeCreate","created","beforeMount","mounted","beforeUpdate","updated","beforeDestroy","destroyed","activated","deactivated","errorCaptured","serverPrefetch"],F={optionMergeStrategies:Object.create(null),silent:!1,productionTip:!1,devtools:!1,performance:!1,errorHandler:null,warnHandler:null,ignoredElements:[],keyCodes:Object.create(null),isReservedTag:T,isReservedAttr:T,isUnknownElement:T,getTagNamespace:S,parsePlatformTagName:E,mustUseProp:T,async:!0,_lifecycleHooks:I},P=/a-zA-Z\u00B7\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u037D\u037F-\u1FFF\u200C-\u200D\u203F-\u2040\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD/;function R(e,t,n,r){Object.defineProperty(e,t,{value:n,enumerable:!!r,writable:!0,configurable:!0})}var H=new RegExp("[^"+P.source+".$_\\d]");var B,U="__proto__"in{},z="undefined"!=typeof window,V="undefined"!=typeof WXEnvironment&&!!WXEnvironment.platform,K=V&&WXEnvironment.platform.toLowerCase(),J=z&&window.navigator.userAgent.toLowerCase(),q=J&&/msie|trident/.test(J),W=J&&J.indexOf("msie 9.0")>0,Z=J&&J.indexOf("edge/")>0,G=(J&&J.indexOf("android"),J&&/iphone|ipad|ipod|ios/.test(J)||"ios"===K),X=(J&&/chrome\/\d+/.test(J),J&&/phantomjs-prebuilt/.test(J),J&&J.match(/firefox\/(\d+)/)),Y={}.watch,Q=!1;if(z)try{var ee={};Object.defineProperty(ee,"passive",{get:function(){Q=!0}}),window.addEventListener("test-passive",null,ee)}catch(e){}var te=function(){return void
</script>
<script type="text/javascript" charset="utf-8">
// TODO poner esto dentro de un onDocumentReady()
var app = new Vue({
el: '#app',
data: {
allowForceUpdate: false,
browsertime: Math.floor(Date.now() / 1000),
currentPage: 0,
development: false,
devicetime: 0,
file: '',
intervals: false,
kitinfo: false,
kitstatus: [],
lastPage: 0,
logging: [],
page: [
{'visible': true, 'footer': 'HOME', 'backTo': 0, 'back': false },
{'visible': false, 'footer': 'REGISTER Key', 'backTo': 0, 'back': true },
{'visible': false, 'footer': 'REGISTER WiFi','backTo': 1, 'back': true },
{'visible': false, 'footer': 'Connecting', 'backTo': 0, 'back': false },
{'visible': false, 'footer': 'Connecting', 'backTo': 0, 'back': true },
{'visible': false, 'footer': 'SD card', 'backTo': 0, 'back': true },
{'visible': false, 'footer': 'Kit info', 'backTo': 0, 'back': true },
{'visible': false, 'footer': 'Empty', 'backTo': 0, 'back': true },
],
publishinterval: 2,
readinginterval: 60,
sdlog: false,
selectedWifi: '',
sensor1: false,
sensor2: false,
sensor3: false,
sensor4: false,
sensors: false,
showDebug: false,
showExperimental: false,
showInterval: false,
showSdCard: false,
theApi: window.location.protocol + '//' + window.location.host + '/',
usertoken: '',
usertokenError: '',
version: 'SCK 2.0 / SAM V0.0.2 / ESP V0.0.2',
vueVersion: Vue.version,
weHaveTriedConnecting: false,
wifiname: '',
wifipass: '',
wifisync: true,
wifis: {
"nets": [{
"ssid": "Fake-WIFI-1",
"ch": 1,
"rssi": -64
}, {
"ssid": "Fake-Wifi-2",
"ch": 1,
"rssi": -89
}]
}
},
mounted: function () {
// When the app is mounted
this.logging.push('App started.');
this.checkIfDebugMode;
// 1. Remove loading screen
var el = document.getElementById('loading');
el.parentNode.removeChild(el);
var el1 = document.getElementById('app');
el1.style.display = 'block';
// 2. Select which API to use, dev vs prod
this.selectApiUrl();
// 3. Fetch available Wifis + status
this.jsGet('aplist');
this.jsGet('status');
// This can only be run once. We don't want the usertoken to be updated every 9
// seconds if the user is typing it in
this.jsGet('token');
// This checks if connection to the kit has been lost, every X sec
this.periodic(6000);
},
methods: {
allowFirmwareUpdate: function(){
this.allowForceUpdate = 'true';
},
copyTextToClipboard: function(containerid){
// We need to copy the text temporary into a textBox to be able to copy it to clipboard.
var textToCopy = document.getElementById('kitinfo').innerText;
var textBox = document.createElement('textarea');
textBox.value = textToCopy;
document.body.appendChild(textBox);
textBox.select();
document.execCommand('copy');
textBox.remove();
document.getElementById('copied-notification').innerHTML = 'Text copied!'
},
periodic: function (ms) {
var that = this;
// TODO: should we check status every 5 sec?
setTimeout(function(){
that.jsGet('status');
return that.periodic(ms);
}, ms);
},
selectApiUrl: function () {
// If we are running this from the kit,
// the API should be on the same IP and port
// Most likely a 192.168.*.1/status
// If we are using port 8000, we are in development, and the API should be on port 3000
if (window.location.port === '8000') {
this.theApi = 'http://' + window.location.hostname + ':3000/';
this.development = true;
}
},
xmlWrapper: function(theUrl, callback) {
// console.log('theurl: ' + theUrl);
var xmlHttp = new XMLHttpRequest();
var that = this;
xmlHttp.onreadystatechange = function() {
if (xmlHttp.readyState == 4 && xmlHttp.status == 200) {
callback(xmlHttp.responseText);
}
}
xmlHttp.onerror = function(e){
// Don't show this error, if we have tried connecting. Only on real API failures
if (!that.weHaveTriedConnecting) {
that.notify('Your kit is not responding', 5000, 'bg-red');
}
}
xmlHttp.open( "GET", theUrl, true ); // false for synchronous request, true = async
xmlHttp.send( null );
},
jsGet: function(path) {
var that = this;
this.xmlWrapper(this.theApi + path, function(res){
if (path === 'aplist') {
that.wifis = JSON.parse(res);
}
if (path === 'status'){
//that.notify('Getting status', 1000);
that.kitstatus = JSON.parse(res);
}
if (path === 'token'){
tmpToken = JSON.parse(res)['token'];
if (tmpToken !== "null") {
that.usertoken = tmpToken;
}
}
});
},
// Not actually a POST request, but a GET with params
jsPost: function(path, purpose){
this.browsertime = Math.floor(Date.now() / 1000);
var that = this;
// Available parameters:
// /set?ssid=value1&password=value2&token=value3&epoch=value
if (purpose == 'connect'){
this.weHaveTriedConnecting = true;
this.notify('Kit is trying to connect online...', 5000);
that.xmlWrapper(that.theApi + path +
'?ssid=' + encodeURIComponent(that.selectedWifi) +
'&password=' + encodeURIComponent(that.wifipass) +
'&token=' + that.usertoken +
'&epoch=' + that.browsertime +
'&mode=network',
function(res){
console.log('Kit response: ' + res);
});
// After we try to connect, we go to the next page.
this.gotoPage();
}
if (purpose == 'synctime'){
this.notify('Starting to log on SD CARD..', 2000);
this.xmlWrapper(this.theApi + path + '?epoch=' + this.browsertime + '&mode=sdcard', function(res){
// TODO: What is the correct response.key from the kit?
that.notify(JSON.parse(res).todo, 5000);
});
}
},
checkIfTokenIsValid(){
if (this.usertoken.length === 6) {
this.gotoPage();
}else{
this.usertokenError = 'You need exactly 6 characters'
}
},
gotoPage: function(num){
// Keep lastPage in memory
this.lastPage = this.currentPage;
// Hide current page
this.page[this.currentPage].visible = false;
// If no page specified, we go to the next page
if ( typeof num === 'undefined') {
// Don't go too far, when clicking 'Next'
if ( this.currentPage === (this.page.length - 1)) {
//console.log('Last page: ' + this.currentPage)
return;
}
this.currentPage = parseInt(this.currentPage + 1);
}else{
this.currentPage = parseInt(num);
}
// Show it
this.page[this.currentPage].visible = true;
this.logging.push('Go to page: ' + this.currentPage);
},
notify: function(msg, duration, className){
if (duration === 'undefined') {
console.log('no duration');
duration = 1000;
}
//All events should also go to the logging section
this.logging.push(msg);
var newtoast = document.createElement("div");
if (className) {
newtoast.className = "toast " + className;
}else{
newtoast.className = "toast";
}
newtoast.innerHTML = msg;
document.getElementById("toast-wrapper").appendChild(newtoast);
setTimeout(function(){
newtoast.outerHTML = "";
delete newtoast;
}, duration);
console.log('Notify:', msg);
},
checkUploadForm: function(e){
this.file = this.$refs.file.files[0];
},
// POST the file to /action via AJAX
submitFirmware: function(e){
var that = this;
firmStatus = document.getElementById('firmware-update-status');
firmStatusExtra = document.getElementById('firmware-status-extra');
firmStatus.classList = '';
firmStatus.classList += 'text-yellow';
firmStatus.innerHTML = 'Updating...';
firmStatusExtra.innerHTML = ' ';
let formData = new FormData();
let req = new XMLHttpRequest();
formData.append('file', this.file);
req.onreadystatechange = function() {
if (req.readyState === 4) {
console.log('request:', req);
firmStatus.innerHTML = ' ' + req.response;
firmStatusExtra.innerHTML = ' ';
// Color
if (req.response.startsWith("ERROR")) {
that.notify('Update failed', 5000, 'bg-red');
firmStatusExtra.innerHTML = 'Something went wrong :(<br/>Please be sure to select the right file and try again !!';
firmStatus.classList = '';
firmStatus.classList += 'text-red';
} else if (req.response.startsWith("Succeed")) {
that.weHaveTriedConnecting = true;
that.notify('Kit Updated...', 5000);
firmStatusExtra.innerHTML = 'Congratulations !!<br/>Your kit will restart so you can reconnect and complete the configuration process.<br/>If you need a Device key go to <span class="text-blue"><a href="https://start.smartcitizen.me">start.smartcitizen.me</a></span> to obtain a new one.';
firmStatus.classList = '';
firmStatus.classList += 'text-green';
}
}
}
req.onerror = function(e){
console.log('error:', e);
}
req.onprogress = function(e){
console.log('progress:', e)
}
req.onload = function(e){
console.log('onload:', e)
}
req.open("POST", this.theApi + 'update');
//req.open("GET", this.theApi + 'ping');
req.send(formData);
console.log('formData sent via AJAX to: ' + this.theApi + 'update');
},
},
computed: {
checkIfDebugMode: function(){
if (window.location.hash === '#debug'){
// Enables debug buttons
this.showDebug = true;
}
},
sortedWifi: function () {
this.wifis.nets.sort(function (a, b) {
return a.rssi - b.rssi;
});
return this.wifis.nets.reverse();
},
usertokenCheck: function(){
return this.usertoken.length === 6;
},
selectedWifiCheck: function(){
return this.selectedWifi.length > 0;
},
checkIfFileSelected: function(){
return this.file.size > 0;
}
}
});
</script>
</body>
</html>