#42 migration to new design

pull/1/head
Thomas Ballmann 4 years ago
parent 7fc22e0969
commit a99b9c8816

@ -11,7 +11,7 @@
/>
</v-overlay>
</template>
<template v-if="1">
<template v-else>
<component :is="layout" />
</template>
@ -218,6 +218,7 @@
<script>
import { mapState, mapActions } from 'vuex'
import notifications from '@/components/Notifications'
import '@/assets/app.css'
export default {
@ -226,6 +227,7 @@
'layout-default': () => import('@/layouts/default'),
// eslint-disable-next-line vue/no-unused-components
'layout-setup': () => import('@/layouts/setup'),
notifications,
},
data: () => ({
isLoading: true,
@ -242,14 +244,22 @@
next()
})
// load device stats
// load
Promise.all([this.loadStats(), this.loadSettings()]).then((values) => {
this.isLoading = false
})
/*
this.loadStats().then(() => {
this.isLoading = false
})
*/
},
methods: {
...mapActions([
'loadStats',
'loadSettings',
]),
},
}

@ -0,0 +1,59 @@
<template>
<v-snackbar
v-model="snackbar"
timeout="-1"
>
{{ notifications }}
<!--
<template v-slot:action="{ attrs }">
<v-btn
color="pink"
text
v-bind="attrs"
@click="snackbar = false"
>
Close
</v-btn>
</template>
-->
</v-snackbar>
</template>
<script>
import { mapState, mapMutations } from 'vuex'
export default {
data: () => ({
snackbar: false,
timeoutHandler: null,
}),
computed: {
...mapState(['notifications']),
},
watch: {
notifications (newVal) {
if (newVal !== null) {
this.snackbar = true
this.resetTimeout()
}
},
},
methods: {
...mapMutations(['notification']),
resetTimeout () {
clearTimeout(this.timeoutHandler)
this.timeoutHandler = setTimeout(() => {
this.snackbar = false
// clear notification
this.notification(null)
}, 3 * 1000)
},
},
}
</script>
<style scoped>
</style>

@ -0,0 +1,224 @@
<template>
<v-dialog
v-model="dialog"
max-width="400"
>
<template #activator="{ on }">
<slot
name="activator"
:on="on"
/>
</template>
<v-card
:disabled="updateProgress > 0"
>
<v-toolbar
dark
color="orange darken-2"
flat
class="mb-5"
>
<v-toolbar-title>
System update
</v-toolbar-title>
<v-spacer />
<v-btn
icon
dark
@click="dialog = false"
>
<v-icon>$close</v-icon>
</v-btn>
</v-toolbar>
<v-card-text>
<v-file-input
v-model="file"
show-size
accept="application/octet-stream"
label="File"
/>
</v-card-text>
<v-card-text v-if="updateType">
<v-alert
border="left"
icon="$info"
outlined
type="info"
>
Update: {{ updateType }}<br>
Version: {{ file.lastModifiedDate.toLocaleString() }}<br>
Current: {{ currentApp.toLocaleString() }}<br>
</v-alert>
</v-card-text>
<v-card-text v-else-if="file">
<v-alert
border="left"
icon="$error"
outlined
type="error"
>
Invalid file
</v-alert>
</v-card-text>
<v-divider />
<v-card-actions>
<template v-if="!updateProgress">
<v-btn
text
href="https://github.com/paperdash"
target="_blank"
>
Get firmware
</v-btn>
<v-spacer />
<v-btn
:disabled="!updateType"
outlined
color="warning"
@click="onSystemUpdate()"
>
Update system
</v-btn>
</template>
<template v-else>
<v-progress-linear
v-model="updateProgress"
:indeterminate="updateProgress === 100"
height="10"
color="orange darken-1"
rounded
/>
</template>
</v-card-actions>
</v-card>
<v-dialog
:value="updateResult !== null"
persistent
max-width="400"
>
<v-card class="pt-1">
<div class="ma-5">
<v-alert
v-if="updateResult"
outlined
border="left"
icon="$success"
type="success"
>
update result...
</v-alert>
<v-alert
v-if="!updateResult"
outlined
border="left"
icon="$error"
type="error"
>
update result...
</v-alert>
</div>
<v-divider />
<v-card-actions>
<v-spacer />
<v-btn
text
@click="onUpdateDone()"
>
OK
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</v-dialog>
</template>
<script>
import axios from 'axios'
export default {
name: 'UpdateDialog',
props: {
currentFirmware: {
type: Date,
required: true,
},
currentApp: {
type: Date,
required: true,
},
},
data: () => ({
dialog: false,
file: null,
updateProgress: null,
updateResult: null,
}),
computed: {
updateType () {
if (this.file) {
switch (this.file.name) {
case 'firmware.bin':
return 'Firmware'
case 'spiffs.bin':
return 'APP'
}
}
return null
},
},
methods: {
onSystemUpdate () {
const self = this
self.updateProgress = 0
const config = {
onUploadProgress: function (progressEvent) {
self.updateProgress = Math.round(
(progressEvent.loaded * 100) / progressEvent.total,
)
},
}
const formData = new FormData()
formData.append('update', this.file)
axios
.post('/update', formData, config)
.then(response => {
console.log(response.data)
self.updateProgress = null
self.updateResult = response.data.success
})
.catch(response => {
console.log(response)
self.updateProgress = null
self.updateResult = false
})
},
onUpdateDone () {
if (this.updateResult === true) {
window.location = '/'
} else {
this.file = null
this.updateProgress = 0
this.updateResult = null
}
},
},
}
</script>
<style scoped>
</style>

@ -9,7 +9,7 @@
<v-list-item-title class="headline">
{{ weather.name }}
</v-list-item-title>
<v-list-item-subtitle>{{ weather.dt | moment("ddd, h A") }}, {{ weather.weather[0].description }}</v-list-item-subtitle>
<v-list-item-subtitle>{{ new Date(weather.dt *1000).toLocaleString(undefined, {weekday:'short', hour: 'numeric'}) }}, {{ weather.weather[0].description }}</v-list-item-subtitle>
</v-list-item-content>
</v-list-item>
@ -54,7 +54,7 @@
v-for="item in forecast.list"
:key="item.dt"
>
<v-list-item-title>{{ item.dt | moment("dddd") }}</v-list-item-title>
<v-list-item-title>{{ new Date(item.dt *1000).toLocaleString(undefined, {weekday:'long'}) }}</v-list-item-title>
<v-list-item-icon class="ma-0">
<v-img :src="getConditionIcon(item.weather[0].icon)" />

@ -1,20 +1,48 @@
<template>
<v-autocomplete
ref="input"
v-model="model"
:disabled="!api"
:items="entries"
:loading="isLoading"
:search-input.sync="search"
hide-no-data
hide-selected
_hide-no-data
_hide-selected
item-text="name"
item-value="id"
label="i8n:Location"
placeholder="i8n:Start typing to Search"
label="Location"
placeholder="Start typing to Search"
_return-object
>
<template #item="{ item }">
<v-list-item-avatar
color="grey lighten-2"
class="headline font-weight-light white--text"
>
<img
width="64"
:src="getConditionIcon(item.weather[0].icon)"
>
</v-list-item-avatar>
<v-list-item-content>
<v-list-item-title>
{{ item.name }}, {{ item.sys.country }}
</v-list-item-title>
<v-list-item-subtitle>
<img
width="16"
:src="getCountryFlag(item.sys.country)"
>
{{ item.weather[0].description }}
</v-list-item-subtitle>
</v-list-item-content>
<v-list-item-action>
{{ item.main.temp }}°С
</v-list-item-action>
<v-list-item
v-if="0"
two-line
class="pa-0"
>
@ -24,6 +52,7 @@
:src="getConditionIcon(item.weather[0].icon)"
>
</v-list-item-icon>
<v-list-item-content>
<v-list-item-title>
{{ item.name }}, {{ item.sys.country }}
@ -40,7 +69,6 @@
</v-list-item-subtitle>
</v-list-item-content>
</v-list-item>
<v-list-item />
</template>
</v-autocomplete>
</template>

@ -1,5 +1,7 @@
<template>
<div class="d-flex flex-grow-1">
<notifications />
<v-navigation-drawer
v-model="drawer"
class="grey darken-3"
@ -81,7 +83,7 @@
@click.stop="drawer = !drawer"
/>
<v-spacer />
<div>box name</div>
<div>{{ settings.device.name }}</div>
<v-spacer />
<v-progress-circular
@ -106,7 +108,7 @@
<v-btn icon>
<v-icon>{{ stats.wifi.rssi | wifiIcon(0) }}</v-icon>
</v-btn>
<span>{{ new Date(stats.device.time * 1000).toLocaleString() }}</span>
<span>{{ new Date(stats.device.time * 1000).toLocaleString(undefined, {month: 'numeric', day: 'numeric', year: 'numeric', hour: '2-digit', minute: '2-digit'}) }}</span>
</template>
<template v-else>
<v-btn
@ -147,8 +149,10 @@
<script>
import { mapState } from 'vuex'
import Notifications from '@/components/Notifications'
export default {
components: { Notifications },
data: () => ({
drawer: true,
clipped: true,
@ -173,6 +177,11 @@
icon: '$wb_sunny',
to: '/weather',
},
{
label: 'Wifi',
icon: '$signalWifi3',
to: '/wifi',
},
{
label: 'System',
icon: '$settings',
@ -181,7 +190,7 @@
],
}),
computed: {
...mapState(['stats']),
...mapState(['stats', 'settings']),
},
}
</script>

@ -10,11 +10,11 @@ const MY_ICONS = {
// cancel: {component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/cancel/baseline.svg')},
close: { component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/close/baseline.svg') },
// delete: {component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/delete/baseline.svg')},
// clear: {component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/clear/baseline.svg')},
// success: {component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/check_circle/baseline.svg')},
clear: { component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/clear/baseline.svg') },
success: { component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/check_circle/baseline.svg') },
info: { component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/info/baseline.svg') },
// warning: {component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/priority_high/baseline.svg')},
// error: {component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/warning/baseline.svg')},
error: { component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/warning/baseline.svg') },
prev: { component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/chevron_left/baseline.svg') },
next: { component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/chevron_right/baseline.svg') },
// checkboxOn: {component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/check_box/baseline.svg')},
@ -54,6 +54,14 @@ const MY_ICONS = {
support: { component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/support/baseline.svg') },
device: { component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/video_label/baseline.svg') },
update: { component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/update/baseline.svg') },
launch: { component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/launch/baseline.svg') },
translate: { component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/translate/baseline.svg') },
access_time: { component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/access_time/baseline.svg') },
language: { component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/language/baseline.svg') },
palette: { component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/palette/baseline.svg') },
sentiment_satisfied_alt: { component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/sentiment_satisfied_alt/baseline.svg') },
file: { component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/insert_drive_file/baseline.svg') },
calendar_today: { component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/calendar_today/baseline.svg') },
// wifi
signalWifiOff: { component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/signal_wifi_off/baseline.svg') },

@ -5,6 +5,7 @@ const Dashboard = () => import('../views/Dashboard')
const Settings = () => import('../views/Settings')
const Device = () => import('../views/Device')
const Playlist = () => import('../views/Playlist')
const Wifi = () => import('../views/Wifi')
const Weather = () => import('../views/Weather')
const System = () => import('../views/System')
@ -24,6 +25,7 @@ export default new VueRouter({
{ path: '/settings', component: Settings, meta: { transitionName: 'slide' } },
{ path: '/device', component: Device, meta: { transitionName: 'slide' } },
{ path: '/playlist', component: Playlist, meta: { transitionName: 'slide' } },
{ path: '/wifi', component: Wifi, meta: { transitionName: 'slide' } },
{ path: '/weather', component: Weather, meta: { transitionName: 'slide' } },
{ path: '/system', component: System, meta: { transitionName: 'slide' } },

@ -3,6 +3,9 @@ import Vuex from 'vuex'
import axios from 'axios'
// import device from '@/api/device'
import { countries, languages } from 'countries-list'
import timezones from 'countries-and-timezones'
Vue.use(Vuex)
const store = new Vuex.Store({
@ -18,46 +21,13 @@ const store = new Vuex.Store({
setSettings (state, payload) {
state.settings = payload
},
/*
setSensors (state, payload) {
state.sensors = payload
},
updateSensor (state, payload) {
// update sensor
const i = state.sensors.findIndex(item => item.id === payload.id)
if (i >= 0) {
state.sensors[i].temperature = payload.temperature
state.sensors[i].humidity = payload.humidity
state.sensors[i].last_update = payload.last_update
if (payload.label) {
state.sensors[i].label = payload.label
}
// update state
state.sensors = [
...state.sensors,
]
}
},
deleteSensor (state, id) {
// const i = state.sensors.findIndex(item => item.id === id)
state.sensors = state.sensors.filter(item => item.id !== id)
},
setPushUpdate (state, enable) {
state.pushUpdate = enable
},
addSensorHistory (state, payload) {
state.sensorHistory.push(payload)
if (state.sensorHistory.length > 20) {
state.sensorHistory = state.sensorHistory.slice(1)
}
},
notification (state, payload) {
state.notifications = payload
},
*/
updateSettings (state, payload) {
state.settings = { ...state.settings, ...payload }
console.log(state.settings)
},
},
actions: {
async loadStats ({ commit }) {
@ -76,6 +46,19 @@ const store = new Vuex.Store({
commit('setSettings', {})
}
},
async saveSettings ({ commit, state }) {
try {
await axios.put('/api/settings', state.settings, {
headers: {
'Content-Type': 'application/json',
},
})
commit('notification', 'settings saved')
} catch (error) {
console.warn(error)
}
},
/*
async getSensors ({ commit }) {
try {
@ -108,11 +91,20 @@ const store = new Vuex.Store({
*/
},
getters: {
/*
isAuthenticated(state) {
return state.user !== null && state.user !== undefined;
}
*/
getAvailableLanguages () {
return languages
},
getAvailableCountries () {
return countries
},
getAvailableTimezones () {
return timezones.getAllTimezones()
},
getAvailableTimezone: () => (countryCode) => {
return timezones.getCountry(countryCode)
},
},
})

@ -1,22 +1,137 @@
<template>
<div class="pa-5">
<v-card
flat
width="400"
class="mx-auto"
>
<v-card-title class="display-2 mb-12 justify-center text-center">
Device settings
</v-card-title>
<v-card-text>
<v-text-field
v-model="form.name"
label="Name"
prepend-icon="$sentiment_satisfied_alt"
/>
<v-select
v-model="form.theme"
:items="optionsTheme"
label="Appearance"
prepend-icon="$palette"
/>
<v-select
disabled
:items="getAvailableCountries"
item-text="name"
label="Region"
prepend-icon="$language"
/>
<v-select
disabled
:items="getAvailableLanguages"
item-text="name"
label="Language"
prepend-icon="$translate"
/>
<v-select
disabled
:items="getAvailableTimezones"
label="Timezone"
prepend-icon="$access_time"
/>
</v-card-text>
<v-divider class="mt-12" />
<v-card-actions>
<v-btn text>
Cancel
</v-btn>
<v-spacer />
<v-btn
color="primary"
text
:loading="isProcessing"
@click="commitChanges"
>
Submit
</v-btn>
</v-card-actions>
</v-card>
device settings<br>
- orientation<br>
- theme<br>
- auflösung<br>
- system language<br>
- system time<br>
</div>
</template>
<script>
import { mapState, mapGetters, mapMutations, mapActions } from 'vuex'
export default {
components: {
},
data: () => ({
isLoading: false,
isProcessing: false,
form: {
name: '',
theme: '',
},
// 0 thru 3 corresponding to 4 cardinal rotations
optionsOrientation: [
{ text: 'Nord', value: 0 },
{ text: 'East', value: 1 },
{ text: 'South', value: 2 },
{ text: 'West', value: 3 },
],
optionsTheme: [
{ text: 'Black', value: 'black' },
{ text: 'White', value: 'white' },
],
deviceMode: [
{ text: 'Active', value: 'active' },
{ text: 'Passive', value: 'passive' },
],
}),
computed: {
...mapState([
'stats',
'settings',
]),
...mapGetters([
'getAvailableCountries',
'getAvailableLanguages',
'getAvailableTimezones',
]),
},
created () {
this.form.name = this.settings.device.name
this.form.theme = this.settings.device.theme
},
methods: {
...mapMutations(['updateSettings']),
...mapActions(['saveSettings']),
commitChanges () {
this.isProcessing = true
this.updateSettings({
device: {
name: this.form.name,
theme: this.form.theme,
},
})
this.saveSettings().then(() => {
this.isProcessing = false
})
},
},
}
</script>

@ -1,11 +1,82 @@
<template>
<div class="pa-5">
<v-card
flat
width="400"
class="mx-auto"
>
<v-card-title class="display-2 mb-12 justify-center text-center">
Playlist settings
</v-card-title>
<v-card-text class="pb-0">
<v-row>
<v-col
cols="3"
class="text-center mt-2 pb-0"
>
<v-icon>$wb_sunny</v-icon>
<br>Forecast
</v-col>
<v-col
cols="6"
class="text-center pb-0"
>
<v-text-field
v-model="settings.playlist.timer"
label="Switch every"
type="number"
dense
rounded
filled
suffix="seconds"
class="text--right"
/>
</v-col>
<v-col
cols="3"
class="text-center mt-2 pb-0"
>
<v-icon>$calendar_today</v-icon>
<br>Calendar
</v-col>
</v-row>
<ul
v-if="0"
class="mt-5"
>
<li>calendar</li>
<li>weather forecast</li>
<li>unsplash.com</li>
</ul>
</v-card-text>
<v-divider class="mt-12" />
<v-card-actions>
<v-btn text>
Cancel
</v-btn>
<v-spacer />
<v-btn
color="primary"
text
:loading="isProcessing"
@click="commitChanges"
>
Submit
</v-btn>
</v-card-actions>
</v-card>
playlist settings<br>
- switch every x seconds<br>
</div>
</template>
<script>
import { mapActions, mapGetters, mapMutations, mapState } from 'vuex'
export default {
components: {
},
@ -13,7 +84,30 @@
isLoading: false,
}),
computed: {
...mapState([
'stats',
'settings',
]),
...mapGetters([
]),
},
methods: {
...mapMutations(['updateSettings']),
...mapActions(['saveSettings']),
commitChanges () {
this.isProcessing = true
this.updateSettings({
device: {
name: this.form.name,
theme: this.form.theme,
},
})
this.saveSettings().then(() => {
this.isProcessing = false
})
},
},
}
</script>

@ -5,6 +5,10 @@
width="400"
class="mx-auto"
>
<v-card-title class="display-2 mb-12 justify-center text-center">
System info
</v-card-title>
<v-list-item>
<v-list-item-icon class="mr-3">
<v-icon>$info</v-icon>
@ -13,12 +17,20 @@
<v-list-item-title>Software</v-list-item-title>
</v-list-item-content>
<v-list-item-action>
<v-btn
outlined
color="primary"
<update-dialog
:current-firmware="new Date(stats.firmware.created *1000)"
:current-app="new Date(stats.app.created *1000)"
>
Update
</v-btn>
<template #activator="{ on }">
<v-btn
outlined
color="primary"
v-on="on"
>
Update
</v-btn>
</template>
</update-dialog>
</v-list-item-action>
</v-list-item>
<v-divider class="mx-4" />
@ -86,6 +98,7 @@
</v-list-item>
</v-list>
<v-progress-linear
v-if="0"
height="25"
:value="fsUsage"
dark
@ -106,27 +119,56 @@
<v-list-item-avatar>
<v-progress-circular
:rotate="-90"
:value="fsUsage"
:value="memoryUsage"
class="caption"
>
{{ fsUsage }}
{{ memoryUsage }}
</v-progress-circular>
</v-list-item-avatar>
</v-list-item>
<v-divider class="mx-4" />
<v-list
class="pb-0"
>
<v-list-item>
<v-list-item-title>Total</v-list-item-title>
<v-list-item-subtitle class="text-right">
{{ memory.total | prettyBytes }}
</v-list-item-subtitle>
</v-list-item>
<v-list-item>
<v-list-item-title>Free</v-list-item-title>
<v-list-item-subtitle class="text-right">
{{ memory.free | prettyBytes }}
</v-list-item-subtitle>
</v-list-item>
</v-list>
<v-progress-linear
v-if="0"
height="25"
:value="memoryUsage"
dark
rounded
>
<template #default="{ value }">
<strong>{{ Math.ceil(value) }}%</strong>
</template>
</v-progress-linear>
</v-card>
</div>
</template>
<script>
import { mapState } from 'vuex'
import UpdateDialog from '@/components/UpdateDialog'
export default {
components: {
UpdateDialog,
},
data: () => ({
isLoading: false,
}),
computed: {
...mapState(['stats']),
@ -138,6 +180,14 @@
fs () {
return this.stats.device.fs
},
memoryUsage () {
return Math.round(
(100 / this.stats.device.heap.total) * this.stats.device.heap.free,
)
},
memory () {
return this.stats.device.heap
},
},
}

@ -1,19 +1,100 @@
<template>
<div class="pa-5">
weather settings<br>
- input fields<br>
<v-card
flat
width="400"
class="mx-auto"
>
<v-card-title class="display-2 mb-12 justify-center text-center">
Weather settings
</v-card-title>
<v-card-text>
<v-text-field
v-model="settings.weather.api"
label="OpenWeatherMap API key"
>
<template #append-outer>
<v-btn
icon
href="https://openweathermap.org/"
target="_blank"
>
<v-icon>$launch</v-icon>
</v-btn>
</template>
</v-text-field>
<weather-find-location
:api="settings.weather.api"
:location.sync="settings.weather.location"
:lang="settings.weather.lang"
:unit="settings.weather.unit"
/>
<v-select
v-model="settings.weather.lang"
:disabled="!settings.weather.api"
:items="weatherLang"
label="Language"
/>
<v-select
v-model="settings.weather.unit"
:disabled="!settings.weather.api"
:items="weatherUnit"
label="Units"
/>
</v-card-text>
<v-divider class="mt-12" />
<v-card-actions>
<v-btn text>
Cancel
</v-btn>
<v-spacer />
<v-btn
color="primary"
text
@click="commitChanges"
>
Submit
</v-btn>
</v-card-actions>
</v-card>
</div>
</template>
<script>
import { mapState, mapGetters, mapActions } from 'vuex'
import WeatherFindLocation from '@/components/WeatherFindLocation'
export default {
components: {
WeatherFindLocation,
},
data: () => ({
isLoading: false,
// @see https://openweathermap.org/current#multi
weatherLang: [
{ text: 'English', value: 'en' },
{ text: 'Deutsch', value: 'de' },
],
weatherUnit: [
{ text: 'Imperial', value: '' },
{ text: 'Metrisch', value: 'metric' },
],
}),
computed: {
...mapState([
'stats',
'settings',
]),
...mapGetters([
]),
},
methods: {
...mapActions(['saveSettings']),
commitChanges () {
this.saveSettings()
},
},
}
</script>

@ -0,0 +1,138 @@
<template>
<div class="pa-5">
<v-card
flat
width="400"
class="mx-auto"
>
<v-card-title class="display-2 mb-12 justify-center text-center">
Wifi settings
</v-card-title>
<v-skeleton-loader
v-if="isLoading"
type="list-item-two-line,list-item-two-line,list-item-two-line"
class="mx-auto"
/>
<v-list v-else>
<template v-for="(wifi, i) in wifiAvailable">
<div :key="i">
<v-divider v-if="i > 0" />
<v-list-item
class="px-1"
@click="onWifiSelect(wifi)"
>
<v-list-item-icon class="mr-2 ml-2">
<v-icon v-if="wifi.ssid === settings.system.wifi">
$check
</v-icon>
<v-progress-circular
v-if="wifi.ssid === connectingSSID"
:size="24"
:width="2"
color="grey "
indeterminate
/>
</v-list-item-icon>
<v-list-item-content dark>
<v-list-item-title v-text="wifi.ssid" />
<v-list-item-subtitle v-text="wifi.bssid" />
</v-list-item-content>
<v-list-item-icon>
<v-icon
v-if="wifi.secure"
class="mx-2"
>
$lock
</v-icon>
<v-icon class="mx-2">
{{ wifi.rssi | wifiIcon(0) }}
</v-icon>
<v-icon class="ml-3">
$next
</v-icon>
</v-list-item-icon>
</v-list-item>
</div>
</template>
<v-divider />
<v-btn
text
color="primary"
class="_px-0 my-2"
disabled
>
Choose Another Network
</v-btn>
</v-list>
<v-dialog
v-model="wifiPasswordModal"
max-width="450"
>
<setup-wifi-connect
:ssid="connectSSID"
:on-connect="onWifiConnect"
@cancel="wifiPasswordModal = false"
/>
</v-dialog>
</v-card>
</div>
</template>
<script>
import { mapState } from 'vuex'
import setupWifiConnect from '@/components/SetupWifiConnect'
import apiDevice from '@/api/device'
export default {
components: {
setupWifiConnect,
},
data: () => ({
isLoading: true,
connectSSID: null,
connectingSSID: '',
wifiAvailable: [],
wifiPasswordModal: false,
}),
computed: {
...mapState(['stats', 'settings']),
},
created () {
apiDevice.wifiScan(list => {
this.wifiAvailable = list
this.isLoading = false
})
},
methods: {
onWifiSelect (wifi) {
if (wifi.secure) {
this.connectSSID = wifi.ssid
this.wifiPasswordModal = true
} else {
this.onWifiConnect(wifi.ssid, '')
}
},
onWifiConnect (ssid, password) {
this.connectingSSID = ssid
this.wifiPasswordModal = false
apiDevice.wifiConnect(ssid, password, () => {})
},
},
}
</script>
<style scoped>
</style>
Loading…
Cancel
Save