12 changed files with 370 additions and 344 deletions
@ -0,0 +1,28 @@
@@ -0,0 +1,28 @@
|
||||
export default { |
||||
computed: { |
||||
saving() { return this.$store.state.saving; } |
||||
}, |
||||
|
||||
|
||||
methods: { |
||||
setSaving(value) |
||||
{ |
||||
self.$store.commit('saving', value); |
||||
}, |
||||
|
||||
|
||||
showNotification(message, isError) |
||||
{ |
||||
this.$store.dispatch('showNotification', { |
||||
message: message, |
||||
isError: isError || false |
||||
}); |
||||
}, |
||||
|
||||
|
||||
handleAPIError(messageId, error) |
||||
{ |
||||
this.$store.dispatch('notifyAPIError', { message: this.$i18n.t(messageId), error }); |
||||
} |
||||
} |
||||
} |
@ -1,147 +0,0 @@
@@ -1,147 +0,0 @@
|
||||
function startApp() |
||||
{ |
||||
var app = new Vue({ |
||||
el: '#app', |
||||
|
||||
data: { |
||||
}, |
||||
|
||||
created: function() |
||||
{ |
||||
var self = this; |
||||
|
||||
self.notificationTimer = null; |
||||
|
||||
// Sequential loading of all the settings makes sure
|
||||
// we don't overload the ESP8266 with requests, as that
|
||||
// can cause it to run out of memory easily.
|
||||
// This is a horrible way to implement it, but I don't feel like
|
||||
// including a big library or working out a clean short solution
|
||||
// at the moment, and it works :)
|
||||
self.loadStatus().then(function() |
||||
{ |
||||
self.loadConnection().then(function() |
||||
{ |
||||
self.loadSystem().then(function() |
||||
{ |
||||
self.stopLoadingIndicator(); |
||||
self.loading = false; |
||||
}); |
||||
}); |
||||
}); |
||||
}, |
||||
|
||||
methods: { |
||||
|
||||
|
||||
|
||||
loadConnection: function() |
||||
{ |
||||
var self = this; |
||||
return axios.get('/api/connection', { retry: 10, retryDelay: 1000 }) |
||||
.then(function(response) |
||||
{ |
||||
if (typeof response.data == 'object') |
||||
self.connection = response.data; |
||||
}) |
||||
.catch(self.handleAPIError.bind(self, 'error.loadConnection')); |
||||
}, |
||||
|
||||
loadSystem: function() |
||||
{ |
||||
var self = this; |
||||
return axios.get('/api/system', { retry: 10, retryDelay: 1000 }) |
||||
.then(function(response) |
||||
{ |
||||
if (typeof response.data == 'object') |
||||
self.system = response.data; |
||||
}) |
||||
.catch(self.handleAPIError.bind(self, 'error.loadSystem')); |
||||
}, |
||||
|
||||
|
||||
applyConnection: function() |
||||
{ |
||||
var self = this; |
||||
if (self.saving) return; |
||||
|
||||
self.saving = true; |
||||
|
||||
axios.post('/api/connection', { |
||||
hostname: self.connection.hostname, |
||||
accesspoint: self.connection.accesspoint, |
||||
station: self.connection.station, |
||||
ssid: self.connection.ssid, |
||||
password: self.connection.password, |
||||
dhcp: self.connection.dhcp, |
||||
ip: self.connection.ip, |
||||
subnetmask: self.connection.subnetmask, |
||||
gateway: self.connection.gateway, |
||||
}, { retry: 10, retryDelay: 1000, headers: { 'Content-Type': 'application/json' } }) |
||||
.then(function(response) |
||||
{ |
||||
}) |
||||
.catch(self.handleAPIError.bind(self, 'error.applyConnection')) |
||||
.then(function() |
||||
{ |
||||
self.saving = false; |
||||
}); |
||||
}, |
||||
|
||||
applySystem: function() |
||||
{ |
||||
var self = this; |
||||
if (self.saving) return; |
||||
|
||||
self.saving = true; |
||||
|
||||
axios.post('/api/system', self.system, { retry: 10, retryDelay: 1000, headers: { 'Content-Type': 'application/json' } }) |
||||
.then(function(response) |
||||
{ |
||||
self.showNotification(i18n.t('rebootPending')); |
||||
}) |
||||
.catch(self.handleAPIError.bind(self, 'error.applySystem')) |
||||
.then(function() |
||||
{ |
||||
self.saving = false; |
||||
}); |
||||
}, |
||||
|
||||
|
||||
uploadFirmware: function() |
||||
{ |
||||
var self = this; |
||||
if (self.saving) return; |
||||
|
||||
self.saving = true; |
||||
self.uploadProgress = 0; |
||||
|
||||
|
||||
var data = new FormData(); |
||||
data.append('file', document.getElementById('firmwareFile').files[0]); |
||||
|
||||
var config = { |
||||
timeout: 360000, |
||||
onUploadProgress: function(progressEvent) |
||||
{ |
||||
self.uploadProgress = Math.round((progressEvent.loaded * 100) / progressEvent.total); |
||||
} |
||||
}; |
||||
|
||||
axios.post('/api/firmware', data, config) |
||||
.then(function(response) |
||||
{ |
||||
self.showNotification(i18n.t('rebootPending')); |
||||
}) |
||||
.catch(self.handleAPIError.bind(self, 'error.uploadFirmware')) |
||||
.then(function() |
||||
{ |
||||
self.uploadProgress = false; |
||||
self.saving = false; |
||||
|
||||
document.getElementById('firmware').reset(); |
||||
}); |
||||
}, |
||||
} |
||||
}); |
||||
} |
@ -1,127 +0,0 @@
@@ -1,127 +0,0 @@
|
||||
<!DOCTYPE html> |
||||
<html> |
||||
<head> |
||||
<meta charset="UTF-8"> |
||||
<title>RGBWifi</title> |
||||
<meta name="theme-color" content="#000000"> |
||||
<meta name="viewport" content="width=device-width, initial-scale=1"> |
||||
<link rel="stylesheet" href="bundle.css"> |
||||
<script src="bundle.js"></script> |
||||
</head> |
||||
<body> |
||||
<div id="app"> |
||||
<div v-cloak> |
||||
|
||||
|
||||
<div v-if="activeTab == 'status'"> |
||||
<!-- |
||||
|
||||
Status tab |
||||
|
||||
--> |
||||
</div> |
||||
|
||||
<div v-if="activeTab == 'connection'"> |
||||
<!-- |
||||
|
||||
Connection tab |
||||
|
||||
--> |
||||
<form @submit.prevent="applyConnection"> |
||||
<h3>{{ $t('connection.title') }}</h3> |
||||
|
||||
<check v-model.boolean="connection.accesspoint" :title="$t('connection.accesspoint')"></check> |
||||
<span class="hint">{{ $t('connection.accesspointHint') }}</span> |
||||
|
||||
<check v-model.boolean="connection.station" :title="$t('connection.stationmode')"></check> |
||||
<span class="hint">{{ $t('connection.stationmodeHint') }}</span> |
||||
|
||||
<label for="ssid">{{ $t('connection.ssid') }}</label> |
||||
<input type="text" id="ssid" v-model="connection.ssid" :disabled="!connection.station"> |
||||
|
||||
<label for="password">{{ $t('connection.password') }}</label> |
||||
<input type="password" id="password" v-model="connection.password" :disabled="!connection.station"> |
||||
|
||||
<check v-model.boolean="connection.dhcp" :disabled="!connection.station" :title="$t('connection.dhcp')" class="form-control"></check> |
||||
<span class="hint">{{ $t('connection.dhcpHint') }}</span> |
||||
|
||||
|
||||
<div class="suboptions"> |
||||
<label for="ip">{{ $t('connection.ipaddress') }}</label> |
||||
<input type="text" id="ip" v-model="connection.ip" :disabled="!connection.station || connection.dhcp"> |
||||
|
||||
<label for="subnetmask">{{ $t('connection.subnetmask') }}</label> |
||||
<input type="text" id="subnetmask" v-model="connection.subnetmask" :disabled="!connection.station || connection.dhcp"> |
||||
|
||||
<label for="gateway">{{ $t('connection.gateway') }}</label> |
||||
<input type="text" id="gateway" v-model="connection.gateway" :disabled="!connection.station || connection.dhcp"> |
||||
</div> |
||||
|
||||
|
||||
<label for="hostname">{{ $t('connection.hostname') }}</label> |
||||
<input type="text" :placeholder="$t('connection.hostnamePlaceholder')" id="hostname" v-model="connection.hostname" :disabled="!connection.station"> |
||||
<span class="hint">{{ $t('connection.hostnameHint') }}</span> |
||||
|
||||
<div class="buttons"> |
||||
<input type="submit" :disabled="saving" :value="saving ? $t('applyButtonSaving') : $t('applyButton')"> |
||||
</div> |
||||
</form> |
||||
</div> |
||||
|
||||
<div v-if="activeTab == 'system'"> |
||||
<!-- |
||||
|
||||
System tab |
||||
|
||||
--> |
||||
<form @submit.prevent="uploadFirmware"> |
||||
<h3>{{ $t('system.firmwareTitle') }}</h3> |
||||
|
||||
<input type="file" id="firmwareFile"> |
||||
|
||||
<div class="buttons"> |
||||
<input type="submit" :disabled="saving" :value="saving ? $t('applyButtonSaving') : $t('applyButton')"> |
||||
</div> |
||||
|
||||
<div v-if="uploadProgress !== false"> |
||||
{{ uploadProgress }}% |
||||
</div> |
||||
</form> |
||||
|
||||
<form @submit.prevent="applySystem"> |
||||
<h3>{{ $t('system.pinsTitle') }}</h3> |
||||
|
||||
<div class="horizontal"> |
||||
<label for="pinLEDAP">{{ $t('system.pinLEDAP') }}</label> |
||||
<input type="number" id="pinLEDAP" v-model.number="system.pins.ledAP"> |
||||
</div> |
||||
|
||||
<div class="horizontal"> |
||||
<label for="pinLEDSTA">{{ $t('system.pinLEDSTA') }}</label> |
||||
<input type="number" id="pinLEDSTA" v-model.number="system.pins.ledSTA"> |
||||
</div> |
||||
|
||||
<div class="horizontal"> |
||||
<label for="pinAPButton">{{ $t('system.pinAPButton') }}</label> |
||||
<input type="number" id="pinAPButton" v-model.number="system.pins.apButton"> |
||||
</div> |
||||
|
||||
<h3>{{ $t('system.ledStripTitle') }}</h3> |
||||
|
||||
<div class="horizontal"> |
||||
<label for="ledCount">{{ $t('system.ledCount') }}</label> |
||||
<input type="number" id="ledCount" v-model.number="system.ledCount"> |
||||
</div> |
||||
|
||||
<div class="buttons"> |
||||
<input type="submit" :disabled="saving" :value="saving ? $t('applyButtonSaving') : $t('applyButton')"> |
||||
</div> |
||||
</form> |
||||
</div> |
||||
|
||||
|
||||
<script language="javascript"> |
||||
startApp(); |
||||
</script> |
||||
</body> |
||||
</html> |
@ -0,0 +1,120 @@
@@ -0,0 +1,120 @@
|
||||
<template> |
||||
<form @submit.prevent="save"> |
||||
<h3>{{ $t('connection.title') }}</h3> |
||||
|
||||
<div v-if="connection === null" class="loading"> |
||||
<LoadingIndicator></LoadingIndicator> |
||||
</div> |
||||
|
||||
<div v-else> |
||||
<check v-model.boolean="connection.accesspoint" :title="$t('connection.accesspoint')"></check> |
||||
<span class="hint">{{ $t('connection.accesspointHint') }}</span> |
||||
|
||||
<check v-model.boolean="connection.station" :title="$t('connection.stationmode')"></check> |
||||
<span class="hint">{{ $t('connection.stationmodeHint') }}</span> |
||||
|
||||
<label for="ssid">{{ $t('connection.ssid') }}</label> |
||||
<input type="text" id="ssid" v-model="connection.ssid" :disabled="!connection.station"> |
||||
|
||||
<label for="password">{{ $t('connection.password') }}</label> |
||||
<input type="password" id="password" v-model="connection.password" :disabled="!connection.station"> |
||||
|
||||
<check v-model.boolean="connection.dhcp" :disabled="!connection.station" :title="$t('connection.dhcp')" class="form-control"></check> |
||||
<span class="hint">{{ $t('connection.dhcpHint') }}</span> |
||||
|
||||
|
||||
<div class="suboptions"> |
||||
<label for="ip">{{ $t('connection.ipaddress') }}</label> |
||||
<input type="text" id="ip" v-model="connection.ip" :disabled="!connection.station || connection.dhcp"> |
||||
|
||||
<label for="subnetmask">{{ $t('connection.subnetmask') }}</label> |
||||
<input type="text" id="subnetmask" v-model="connection.subnetmask" :disabled="!connection.station || connection.dhcp"> |
||||
|
||||
<label for="gateway">{{ $t('connection.gateway') }}</label> |
||||
<input type="text" id="gateway" v-model="connection.gateway" :disabled="!connection.station || connection.dhcp"> |
||||
</div> |
||||
|
||||
|
||||
<label for="hostname">{{ $t('connection.hostname') }}</label> |
||||
<input type="text" :placeholder="$t('connection.hostnamePlaceholder')" id="hostname" v-model="connection.hostname" :disabled="!connection.station"> |
||||
<span class="hint">{{ $t('connection.hostnameHint') }}</span> |
||||
|
||||
<div class="buttons"> |
||||
<input type="submit" :disabled="saving" :value="saving ? $t('applyButtonSaving') : $t('applyButton')"> |
||||
</div> |
||||
</div> |
||||
</form> |
||||
</template> |
||||
|
||||
<script> |
||||
import axios from 'axios'; |
||||
import LoadingIndicator from '@/components/loadingIndicator.vue'; |
||||
import check from '@/components/check.vue'; |
||||
import BaseVM from '@/BaseVM'; |
||||
|
||||
export default { |
||||
mixins: [BaseVM], |
||||
|
||||
components: { |
||||
LoadingIndicator, |
||||
check |
||||
}, |
||||
|
||||
data() |
||||
{ |
||||
return { |
||||
connection: null |
||||
} |
||||
}, |
||||
|
||||
|
||||
mounted() |
||||
{ |
||||
this.load(); |
||||
}, |
||||
|
||||
|
||||
methods: { |
||||
load() |
||||
{ |
||||
const self = this; |
||||
|
||||
return axios.get('/api/connection', { retry: 10, retryDelay: 1000 }) |
||||
.then(response => |
||||
{ |
||||
if (typeof response.data == 'object') |
||||
self.connection = response.data; |
||||
}) |
||||
.catch(e => self.handleAPIError('error.loadConnection', e)); |
||||
}, |
||||
|
||||
|
||||
save() |
||||
{ |
||||
const self = this; |
||||
if (self.saving) |
||||
return; |
||||
|
||||
self.setSaving(true); |
||||
|
||||
axios.post('/api/connection', { |
||||
hostname: self.connection.hostname, |
||||
accesspoint: self.connection.accesspoint, |
||||
station: self.connection.station, |
||||
ssid: self.connection.ssid, |
||||
password: self.connection.password, |
||||
dhcp: self.connection.dhcp, |
||||
ip: self.connection.ip, |
||||
subnetmask: self.connection.subnetmask, |
||||
gateway: self.connection.gateway, |
||||
}, { retry: 10, retryDelay: 1000, headers: { 'Content-Type': 'application/json' } }) |
||||
.then(response => {}) |
||||
.catch(e => self.handleAPIError('error.applyConnection', e)) |
||||
.then(() => |
||||
{ |
||||
self.setSaving(false); |
||||
}); |
||||
} |
||||
} |
||||
} |
||||
</script> |
@ -0,0 +1,161 @@
@@ -0,0 +1,161 @@
|
||||
<template> |
||||
<div> |
||||
<h3>{{ $t('system.firmwareTitle') }}</h3> |
||||
|
||||
<div v-if="system === null" class="loading"> |
||||
<LoadingIndicator></LoadingIndicator> |
||||
</div> |
||||
|
||||
<div v-else> |
||||
<form @submit.prevent="uploadFirmware"> |
||||
<input type="file" id="firmwareFile"> |
||||
|
||||
<div class="buttons"> |
||||
<input type="submit" :disabled="saving" :value="saving ? $t('applyButtonSaving') : $t('applyButton')"> |
||||
</div> |
||||
|
||||
<div v-if="uploadProgress !== false"> |
||||
{{ uploadProgress }}% |
||||
</div> |
||||
</form> |
||||
|
||||
<form @submit.prevent="applySystem"> |
||||
<h3>{{ $t('system.pinsTitle') }}</h3> |
||||
|
||||
<div class="horizontal"> |
||||
<label for="pinLEDAP">{{ $t('system.pinLEDAP') }}</label> |
||||
<input type="number" id="pinLEDAP" v-model.number="system.pins.ledAP"> |
||||
</div> |
||||
|
||||
<div class="horizontal"> |
||||
<label for="pinLEDSTA">{{ $t('system.pinLEDSTA') }}</label> |
||||
<input type="number" id="pinLEDSTA" v-model.number="system.pins.ledSTA"> |
||||
</div> |
||||
|
||||
<div class="horizontal"> |
||||
<label for="pinAPButton">{{ $t('system.pinAPButton') }}</label> |
||||
<input type="number" id="pinAPButton" v-model.number="system.pins.apButton"> |
||||
</div> |
||||
|
||||
<h3>{{ $t('system.ledStripTitle') }}</h3> |
||||
|
||||
<div class="horizontal"> |
||||
<label for="ledCount">{{ $t('system.ledCount') }}</label> |
||||
<input type="number" id="ledCount" v-model.number="system.ledCount"> |
||||
</div> |
||||
|
||||
<div class="buttons"> |
||||
<input type="submit" :disabled="saving" :value="saving ? $t('applyButtonSaving') : $t('applyButton')"> |
||||
</div> |
||||
</form> |
||||
</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 { |
||||
system: null, |
||||
uploadProgress: false |
||||
} |
||||
}, |
||||
|
||||
|
||||
mounted() |
||||
{ |
||||
this.load(); |
||||
}, |
||||
|
||||
|
||||
methods: { |
||||
load() |
||||
{ |
||||
const self = this; |
||||
|
||||
return axios.get('/api/system', { retry: 10, retryDelay: 1000 }) |
||||
.then(response => |
||||
{ |
||||
if (typeof response.data == 'object') |
||||
self.system = response.data; |
||||
}) |
||||
.catch(e => self.handleAPIError('error.loadSystem', e)); |
||||
}, |
||||
|
||||
|
||||
save() |
||||
{ |
||||
const self = this; |
||||
if (self.saving) |
||||
return; |
||||
|
||||
self.setSaving(true); |
||||
|
||||
axios.post('/api/system', self.system, { retry: 10, retryDelay: 1000, headers: { 'Content-Type': 'application/json' } }) |
||||
.then(response => |
||||
{ |
||||
self.showNotification(i18n.t('rebootPending')); |
||||
}) |
||||
.catch(e => self.handleAPIError('error.applySystem', e)) |
||||
.then(() => |
||||
{ |
||||
self.setSaving(false); |
||||
}); |
||||
}, |
||||
|
||||
|
||||
uploadFirmware() |
||||
{ |
||||
const self = this; |
||||
if (self.saving) return; |
||||
|
||||
const fileElement = document.getElementById('firmwareFile'); |
||||
if (fileElement.files.length == 0) |
||||
{ |
||||
self.showNotification(self.$i18n.t('system.noFileSelected'), true); |
||||
return; |
||||
} |
||||
|
||||
self.saving = true; |
||||
self.uploadProgress = 0; |
||||
|
||||
const data = new FormData(); |
||||
|
||||
data.append('file', fileElement.files[0]); |
||||
|
||||
var config = { |
||||
timeout: 360000, |
||||
onUploadProgress: progressEvent => |
||||
{ |
||||
self.uploadProgress = Math.round((progressEvent.loaded * 100) / progressEvent.total); |
||||
} |
||||
}; |
||||
|
||||
axios.post('/api/firmware', data, config) |
||||
.then(response => |
||||
{ |
||||
self.showNotification(self.$i18n.t('rebootPending')); |
||||
}) |
||||
.catch(e => self.handleAPIError('error.uploadFirmware', e)) |
||||
.then(() => |
||||
{ |
||||
self.uploadProgress = false; |
||||
self.saving = false; |
||||
|
||||
document.getElementById('firmware').reset(); |
||||
}); |
||||
} |
||||
} |
||||
} |
||||
</script> |
Loading…
Reference in new issue