rgbwifi/web/src/App.vue

988 lines
20 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div id="app">
<div class="notificationContainer">
<div class="notification" :class="{ error: notification != null && notification.isError }" v-if="notification != null" @click.prevent="hideNotification">
<span class="message">{{ notification.message }}</span>
</div>
</div>
<div id="container">
<div class="header">
<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoV2luZG93cykiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6N0E2MTY4NzJGQjFBMTFFQThERTJFM0JGMzk3MjRDRDUiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6N0E2MTY4NzNGQjFBMTFFQThERTJFM0JGMzk3MjRDRDUiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo3QTYxNjg3MEZCMUExMUVBOERFMkUzQkYzOTcyNENENSIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo3QTYxNjg3MUZCMUExMUVBOERFMkUzQkYzOTcyNENENSIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PgI/6YAAAAfkSURBVHjazFpbbBRV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" />
<h1>{{ $t('title') }}</h1>
<h2>{{ $t('systemID') }}{{ status.systemID || '...' }}</h2>
<div class="wifistatus">
<div class="connection">
<div class="indicator" :data-status="wifiStatus.ap.enabled ? 'connected' : 'disconnected'"></div> {{ $t('wifiStatus.accesspoint.title') }} {{ wifiStatus.ap.enabled ? wifiStatus.ap.ip : $t('wifiStatus.accesspoint.disabled') }}
</div>
<div class="connection">
<div class="indicator" :data-status="getWiFiStationStatus()"></div> {{ $t('wifiStatus.stationmode.title') }} {{ getWiFiStationStatusText() }}
</div>
</div>
</div>
<div v-if="loading" class="loading">
<LoadingIndicator></LoadingIndicator>
</div>
<div v-else>
<div class="warning" v-if="hasResetError">
<p>
{{ $t('error.resetError') }}
</p>
<p class="resetReason">
{{ $t('error.resetReason.' + status.resetReason) }}
</p>
<p v-if="status.stackTrace">
{{ $t('error.stackTrace') }}
</p>
<a class="button button-primary" href="/api/stacktrace/get" v-if="status.stackTrace">{{ $t('error.stackTraceDownload') }}</a>
<a class="button" @click="deleteStackTrace">{{ $t('error.stackTraceDelete') }}</a>
</div>
<div class="navigation tabs">
<router-link to="/" class="button" active-class="active" :exact="true">{{ $t('status.tabTitle') }}</router-link><router-link to="/connection" class="button" active-class="active">{{ $t('connection.tabTitle') }}</router-link><router-link to="/system" class="button" active-class="active">{{ $t('system.tabTitle') }}</router-link>
</div>
<router-view/>
<div class="clearfix"></div>
</div>
<div class="version">
{{ $t('copyright') }}<br>
{{ $t('firmwareVersion') }}{{ status.version || '...' }}
</div>
</div>
</div>
</template>
<script>
import axios from 'axios'
import LoadingIndicator from '@/components/loadingIndicator.vue';
import BaseVM from '@/BaseVM';
export default {
mixins: [BaseVM],
components: {
LoadingIndicator
},
data()
{
return {
loading: true,
status: {
systemID: null,
version: null,
resetReason: null,
stackTrace: false
},
wifiStatus: {
ap: {
enabled: false,
ip: '0.0.0.0'
},
station: {
enabled: false,
status: 0,
ip: '0.0.0.0'
}
}
};
},
mounted()
{
const self = this;
self.updateStatus()
.then(() =>
{
self.updateWiFiStatus()
.then(() =>
{
self.loading = false;
});
});
},
computed: {
notification() { return this.$store.state.notification; },
hasResetError()
{
/*
REASON_DEFAULT_RST = 0 normal startup by power on
REASON_WDT_RST = 1 hardware watch dog reset
REASON_EXCEPTION_RST = 2 exception reset, GPIO status wont change
REASON_SOFT_WDT_RST = 3 software watch dog reset, GPIO status wont change
REASON_SOFT_RESTART = 4 software restart ,system_restart , GPIO status wont change
REASON_DEEP_SLEEP_AWAKE = 5 wake up from deep-sleep
REASON_EXT_SYS_RST = 6 system reset
*/
return (this.status.resetReason === 1 ||
this.status.resetReason === 2 ||
this.status.resetReason === 3 ||
this.status.stackTrace);
}
},
methods: {
getWiFiStationStatus()
{
if (!this.wifiStatus.station.enabled)
return 'disconnected';
switch (this.wifiStatus.station.status)
{
case 0: // WL_IDLE_STATUS
case 2: // WL_SCAN_COMPLETED
return 'connecting';
case 1: // WL_NO_SSID_AVAIL
case 4: // WL_CONNECT_FAILED
case 5: // WL_CONNECTION_LOST
return 'error';
case 3: // WL_CONNECTED
return 'connected';
case 6: // WL_DISCONNECTED
default:
return 'disconnected';
}
},
getWiFiStationStatusText()
{
if (!this.wifiStatus.station.enabled)
return this.$i18n.t('wifiStatus.stationmode.disabled');
switch (this.wifiStatus.station.status)
{
case 0: // WL_IDLE_STATUS
return this.$i18n.t('wifiStatus.stationmode.idle');
case 1: // WL_NO_SSID_AVAIL
return this.$i18n.t('wifiStatus.stationmode.noSSID');
case 2: // WL_SCAN_COMPLETED
return this.$i18n.t('wifiStatus.stationmode.scanCompleted');
case 3: // WL_CONNECTED
return this.wifiStatus.station.ip;
case 4: // WL_CONNECT_FAILED
return this.$i18n.t('wifiStatus.stationmode.connectFailed');
case 5: // WL_CONNECTION_LOST
return this.$i18n.t('wifiStatus.stationmode.connectionLost');
case 6: // WL_DISCONNECTED
default:
return this.$i18n.t('wifiStatus.stationmode.disconnected');
}
},
updateStatus()
{
const self = this;
return axios.get('/api/status', { retry: 10, retryDelay: 1000 })
.then(response =>
{
if (typeof response.data == 'object')
self.status = response.data;
})
.catch(e => self.handleAPIError('error.loadStatus', e));
},
updateWiFiStatus()
{
const self = this;
return new Promise((resolve, reject) =>
{
if (!self.saving)
{
axios.get('/api/connection/status', { retry: 10, retryDelay: 1000 })
.then(response =>
{
if (typeof response.data == 'object')
self.wifiStatus = response.data;
})
.catch(e =>
{
self.handleAPIError('error.updateWiFiStatus', e);
reject(e);
})
.then(function()
{
setTimeout(self.updateWiFiStatus, 5000);
resolve();
});
}
else
{
setTimeout(self.updateWiFiStatus, 5000);
resolve();
}
});
},
deleteStackTrace()
{
const self = this;
return axios.get('/api/stacktrace/delete', { retry: 10, retryDelay: 1000 })
.then(response =>
{
self.status.resetReason = 0;
self.status.stackTrace = false;
})
.catch(e => self.handleAPIError('error.stackTraceDeleteError', e));
},
hideNotification()
{
this.$store.dispatch('hideNotification');
}
}
}
</script>
<style lang="scss">
@import "variables.scss";
// TODO check which parts are app-wide and which should be moved to components/view
html
{
overscroll-behavior-x: contain;
box-sizing: border-box;
font-size: 62.5%;
}
*, *:before, *:after
{
box-sizing: inherit;
}
body
{
overscroll-behavior-x: contain;
background-color: rgb(20, 20, 20);
color: white;
font-family: 'Verdana', 'Arial', sans-serif;
font-size: 1.3em;
font-weight: 300;
letter-spacing: .01em;
line-height: 1.3;
padding-bottom: 3rem;
@media #{$mediumScreen}
{
padding-top: 3rem;
}
}
a
{
text-decoration: none;
}
#container
{
background: $containerBackground;
margin-top: 2rem;
padding: 1rem;
box-shadow: 0 0 50px $containerShadowColor;
border: solid 1px black;
@media #{$mediumScreen}
{
width: 768px;
margin-left: auto;
margin-right: auto;
}
}
.header
{
position: relative;
img
{
float: left;
margin-right: 1rem;
}
.wifistatus
{
@media #{$smallScreen}
{
clear: both;
margin-top: 3rem;
}
@media #{$mediumScreen}
{
position: absolute;
right: 0;
top: 0;
}
.indicator
{
display: inline-block;
width: 1rem;
height: 1rem;
border-radius: 50%;
margin-right: 0.5rem;
&[data-status=connected] { background-color: #339966; }
&[data-status=disconnected] { border: solid 1px #808080; }
&[data-status=connecting] { background-color: #ff9933; }
&[data-status=error] { background-color: #cc0000; }
}
}
}
%outset
{
border: 1px solid #111111;
border-radius: 3px;
box-shadow: inset 0 1px rgba(255,255,255,0.1), inset 0 -1px 3px rgba(0,0,0,0.3), inset 0 0 0 1px rgba(255,255,255,0.08), 0 1px 2px rgba(0,0,0,0.15);
}
%inset
{
border: 1px solid #111111;
border-color: black #111111 #111111;
box-shadow: inset 0 1px 2px rgba(0,0,0,0.25),0 1px rgba(255,255,255,0.08);
}
button, input
{
font-family: 'Verdana', 'Arial', sans-serif;
}
@mixin removeSafariStyling
{
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
}
input
{
@include removeSafariStyling;
}
button, .button, input[type=submit]
{
@extend %outset;
display: inline-block;
padding: 0 12px;
color: $buttonTextColor;
background: $buttonBackground;
cursor: pointer;
line-height: 3rem;
&:hover, &:focus, &.focus
{
color: $buttonHoverTextColor;
background: $buttonHoverBackground;
outline: none
}
&:active, &.active
{
@extend %inset;
color: $buttonActiveTextColor;
background: $buttonActiveBackground;
}
}
input[type=submit], .button-primary
{
background: $buttonPrimaryBackground;
&:hover, &:focus, &.focus
{
background: $buttonPrimaryHoverBackground;
}
}
a.button
{
text-decoration: none
}
.navigation
{
clear: both;
margin-top: 3rem;
}
.tabs
{
&>.button
{
margin-left: -1px;
border-radius: 0;
&:first-child
{
margin-left: 0;
border-radius: 3px 0 0 3px
}
&:last-child
{
border-radius: 0 3px 3px 0
}
&:focus
{
position: relative;
z-index: 1
}
}
}
.version
{
color: $versionTextColor;
font-size: 8pt;
text-align: center;
margin-top: 2rem;
}
.notificationContainer
{
position: fixed;
top: 2rem;
z-index: 666;
@media #{$mediumScreen}
{
width: 512px;
left: 50%;
}
}
.notification
{
@extend %outset;
background: $notificationBackground;
/* border: solid 1px $notificationBorderColor;*/
box-shadow: 0 0 10px black;
color: white;
cursor: pointer;
padding: .5em;
margin-bottom: 2rem;
position: relative;
@media #{$mediumScreen}
{
left: -50%;
}
.message
{
white-space: pre;
}
&.error
{
background: $notificationErrorBackground;
}
}
.check, .radio
{
display: inline-block;
cursor: pointer;
user-select: none;
white-space: nowrap;
margin-top: .5em;
margin-bottom: .5em;
.control
{
@extend %outset;
background: $checkRadioBackground;
display: inline-block;
width: 16px;
height: 16px;
position: relative;
}
.label
{
display: inline-block;
margin-left: .5em;
vertical-align: top;
}
&.checked
{
.control
{
background: $checkRadioSelectedBackground;
.inner
{
}
}
}
&.disabled
{
cursor: not-allowed;
.label
{
color: $inputDisabledTextColor;
}
}
}
.radio
{
.control, .control .inner
{
border-radius: 50%;
}
.control .inner
{
color: black;
position: absolute;
top: 4px;
left: 4px;
width: 6px;
height: 6px;
}
&.checked .control .inner
{
background: #cccccc;
box-shadow: 0 1px rgba(0,0,0,0.5);
}
}
.check
{
.control .inner
{
position: absolute;
top: 5px;
left: 4px;
width: 6px;
height: 3px;
}
&.checked .control .inner
{
border: solid rgba(255,255,255,0.8);
border-width: 0 0 2px 2px;
transform: rotate(-45deg);
box-shadow: -1px 0 rgba(0,0,0,0.2), 0 1px rgba(0,0,0,0.5)
}
}
.form-control
{
margin-top: 1em;
}
input[type=text], input[type=number], input[type=password], textarea
{
@extend %inset;
background: $inputBackground;
color: $inputTextColor;
padding: .5em;
width: 100%;
}
select
{
@extend %outset;
background: $selectBackground;
color: $inputTextColor;
font-family: 'Verdana', 'Arial', sans-serif;
padding: .5em;
}
input[type=range]
{
margin-top: 1rem;
margin-bottom: 1rem;
}
h1
{
font-size: 2rem;
margin: 0;
}
h2
{
color: #c0c0c0;
font-size: 1.2rem;
margin: 0;
}
h3
{
@extend %outset;
color: $sectionHeaderTextColor;
background: $sectionHeaderBackground;
font-size: 1.2rem;
padding: .5rem;
}
h4
{
font-size: 1.4rem;
}
input[disabled]
{
cursor: not-allowed;
color: $inputDisabledTextColor;
background: $inputDisabledBackground;
}
label
{
display: block;
margin-top: .5em;
margin-bottom: .5em;
}
.label-inline
{
margin-right: 2rem;
}
@media #{$mediumScreen}
{
.horizontal
{
clear: both;
label
{
display: inline-block;
}
input[type=text], input[type=number], input[type=password], textarea
{
display: inline-block;
float: right;
width: 50%;
}
&:after
{
clear: both;
}
}
}
.hint
{
display: block;
font-size: 8pt;
color: #808080;
margin-bottom: 1.5rem;
}
.loading
{
margin-top: 3rem;
text-align: center;
}
.suboptions
{
margin-left: 5rem;
}
.buttons
{
clear: both;
text-align: center;
margin-top: 1rem;
}
.sliders
{
margin-top: 2rem;
}
.slidercontainer
{
margin-top: 1rem;
}
$sliderRedThumbColor: #ce3636;
$sliderGreenThumbColor: #32b732;
$sliderBlueThumbColor: #4646cc;
$sliderWhiteThumbColor: #fcf6cf;
.slider
{
-webkit-appearance: none;
width: 100%;
height: $sliderBarSize;
border-radius: $sliderBarSize / 2;
background: $sliderBarColor;
outline: none;
&::-webkit-slider-thumb
{
-webkit-appearance: none;
appearance: none;
width: $sliderThumbSize;
height: $sliderThumbSize;
border-radius: 50%;
background: $sliderThumbColor;
cursor: pointer;
}
&::-moz-range-thumb
{
width: $sliderThumbSize;
height: $sliderThumbSize;
border-radius: 50%;
background: $sliderThumbColor;
cursor: pointer;
}
&.red
{
&::-webkit-slider-thumb { background: $sliderRedThumbColor; }
&::-moz-range-thumb { background: $sliderRedThumbColor; }
}
&.green
{
&::-webkit-slider-thumb { background: $sliderGreenThumbColor; }
&::-moz-range-thumb { background: $sliderGreenThumbColor; }
}
&.blue
{
&::-webkit-slider-thumb { background: $sliderBlueThumbColor; }
&::-moz-range-thumb { background: $sliderBlueThumbColor; }
}
}
.warning
{
@extend %outset;
background: #973a38;
padding: .5em;
margin-bottom: 2rem;
margin-top: 1rem;
}
.nodata
{
color: #808080;
text-align: center;
}
.clear
{
clear: both;
}
.panel
{
margin-bottom: 2rem;
padding: 0;
.panel-header
{
@extend %outset;
border-radius: 3px 3px 0 0;
border-bottom-width: 0;
padding: .5em;
label {
font-size: 1em;
}
background: $panelHeaderBackground;
color: $panelHeaderTextColor;
.actions
{
float: right;
}
a, .label
{
color: $panelHeaderLinkColor;
}
}
.panel-body
{
@extend %outset;
border-radius: 0 0 3px 3px;
background: $panelBodyBackground;
padding: 2rem;
}
&.active
{
.panel-header
{
background: $panelActiveHeaderBackground;
color: $panelActiveHeaderTextColor;
}
}
}
.inline
{
display: inline-block;
width: auto;
}
.fade-enter-active, .fade-leave-active
{
transition: opacity .5s;
}
.fade-enter, .fade-leave-to
{
opacity: 0;
}
.range
{
clear: both;
.start
{
position: relative;
display: inline-block;
width: 49%;
.slidercontainer
{
margin-right: 4em;
}
.value
{
position: absolute;
right: 0;
top: 1.5rem;
color: $sliderValueColor;
}
}
.end
{
position: relative;
display: inline-block;
float: right;
width: 50%;
.slidercontainer
{
margin-left: 4em;
}
.value
{
position: absolute;
left: 0;
top: 1.5rem;
color: $sliderValueColor;
}
}
&:after
{
clear: both;
}
}
.resetReason
{
margin-left: 2em;
}
</style>