#31 basic working version done

pull/1/head
Thomas Ballmann 4 years ago
parent ec149bc475
commit f4b78d188f

@ -20,6 +20,8 @@
"axios": "^0.19.2",
"babel-eslint": "^10.0.3",
"compression-webpack-plugin": "^3.1.0",
"countries-and-timezones": "^2.2.0",
"countries-list": "^2.5.4",
"eslint": "^6.7.2",
"eslint-plugin-vue": "^6.1.2",
"sass": "^1.19.0",

@ -0,0 +1,302 @@
[
"Lydan",
"Syrin",
"Ptorik",
"Joz",
"Varog",
"Gethrod ",
"Hezra",
"Feron ",
"Ophni ",
"Colborn ",
"Fintis ",
"Gatlin",
"Jinto",
"Hagalbar",
"Krinn",
"Lenox",
"Revvyn",
"Hodus",
"Dimian",
"Paskel",
"Kontas",
"Weston",
"Azamarr ",
"Jather ",
"Tekren ",
"Jareth ",
"Adon",
"Zaden ",
"Eune ",
"Graff ",
"Tez ",
"Jessop ",
"Gunnar ",
"Pike ",
"Domnhar ",
"Baske ",
"Jerrick",
"Mavrek",
"Riordan ",
"Wulfe",
"Straus ",
"Tyvrik ",
"Henndar ",
"Favroe ",
"Whit ",
"Jaris ",
"Renham",
"Kagran ",
"Lassrin ",
"Vadim",
"Arlo ",
"Quintis ",
"Vale ",
"Caelan ",
"Yorjan ",
"Khron ",
"Ishmael",
"Jakrin ",
"Fangar ",
"Roux",
"Baxar ",
"Hawke ",
"Gatlen ",
"Barak ",
"Nazim ",
"Kadric ",
"Paquin",
"Kent ",
"Moki ",
"Rankar ",
"Lothe ",
"Ryven ",
"Clawsen ",
"Pakker ",
"Embre ",
"Cassian",
"Verssek ",
"Dagfinn ",
"Ebraheim ",
"Nesso ",
"Eldermar ",
"Rivik ",
"Rourke ",
"Barton ",
"Hemm ",
"Sarkin ",
"Blaiz ",
"Talon",
"Agro",
"Zagaroth",
"Turrek",
"Esdel",
"Lustros ",
"Zenner ",
"Baashar ",
"Dagrod ",
"Gentar ",
"Feston ",
"Syrana ",
"Resha ",
"Varin ",
"Wren ",
"Yuni ",
"Talis ",
"Kessa ",
"Magaltie ",
"Aeris ",
"Desmina ",
"Krynna ",
"Asralyn ",
"Herra ",
"Pret ",
"Kory ",
"Afia ",
"Tessel ",
"Rhiannon ",
"Zara ",
"Jesi",
"Belen ",
"Rei ",
"Ciscra ",
"Temy ",
"Renalee ",
"Estyn ",
"Maarika ",
"Lynorr ",
"Tiv ",
"Annihya ",
"Semet ",
"Tamrin ",
"Antia ",
"Reslyn ",
"Basak",
"Vixra ",
"Pekka ",
"Xavia ",
"Beatha ",
"Yarri",
"Liris ",
"Sonali ",
"Razra ",
"Soko ",
"Maeve ",
"Everen ",
"Yelina ",
"Morwena ",
"Hagar ",
"Palra ",
"Elysa ",
"Sage ",
"Ketra ",
"Lynx ",
"Agama ",
"Thesra ",
"Tezani ",
"Ralia ",
"Esmee ",
"Heron",
"Naima ",
"Rydna ",
"Sparrow ",
"Baakshi ",
"Ibera",
"Phlox",
"Dessa",
"Braithe ",
"Taewen",
"Larke",
"Silene",
"Phressa",
"Esther ",
"Anika ",
"Rasy ",
"Harper ",
"Indie ",
"Vita ",
"Drusila ",
"Minha ",
"Surane ",
"Lassona ",
"Merula ",
"Kye ",
"Jonna ",
"Lyla ",
"Zet ",
"Orett ",
"Naphtalia ",
"Turi ",
"Rhays ",
"Shike ",
"Hartie ",
"Beela ",
"Leska ",
"Vemery ",
"Lunex ",
"Fidess ",
"Tisette ",
"Partha",
"Noble Crest Stables",
"Gibbs Farm",
"Triple Axes Pub",
"Arrowhead Inn",
"Yeomens Manor",
"Bootstraps & Buckles Cobbler",
"Dwarfs Gullet Tavern",
"Hollowvale Village",
"Thunder Hoof Stables",
"Clippers County",
"Gresgos Pond ",
"Hemms Tailors ",
"DitchnDagger Tavern ",
"Twin Horses Lake Town",
"Boggs Landing",
"Heatherstown Village",
"Trekkers Square ",
"Embris Hollow ",
"Zagoroth Mountain Town ",
"Padsley Village ",
"Sleeping Dragon Inn",
"Ramoth Lye Creek",
"Dragmire Drove ",
"Archers Ale House ",
"Peekers Valley ",
"Anvil & Flint Blacksmithery",
"Pale Thorn Cove ",
"Fang Peak ",
"Arc of the Holy Shield Monastery ",
"Breggos Beard Brewery ",
"Golden Stalk Granary ",
"Saw and Splinters Woodshop ",
"Sirens Shipyard ",
"Silver Fin Fishery ",
"Addlers Drove ",
"Black Morrow Marshes ",
"Shepherds Field ",
"Honeyclover Meadow ",
"Finch & Fox Inn ",
"The Warren Pub ",
"Fennel & Fallow Bakery ",
"Rose & Crown Tavern ",
"Knights Motte and Bailey ",
"White Elk Hall ",
"Briar Bridge Village",
"Mighty Hart Furriers",
"Ekhart Bridge",
"The Magpies Market ",
"Threshs Cove ",
"Denmarrow Mountains ",
"Simple Beasts Watering Hole ",
"Lowly Strider Almshouse ",
"Miss Plunkerhouses Grand Tailors",
"White Lion Halls",
"Thousand Lanterns Inn",
"Wild Hare Country Tavern",
"Laskerville Village ",
"Scouts Towers",
"Golden Longbow Fields",
"Tuckborrow Village",
"Traders Turn Plaza",
" Stoneburrow Town",
"Brathers Town",
"Stout Dragon Tavern",
"Dueling Swords Blacksmithery",
"The Braided Plait Bakery",
"Mossy Stone Cove",
"Fairy Winds Beach",
"Old Stone Giant Mill",
"Sacred Scroll Holy ",
"King Rothenham Square",
"Brewers Bounty Alehouse",
"Oslocrest Town",
"Parscas Plaza",
"Old Mayfield Farms",
"Fowlers Prize Furrers",
"Iron Halberd Smithy",
"Dukes Valley",
"Burrowing Badger Inn",
"Pork Bellys Butcher Shop",
"Cast Iron Almshouse",
"Three Braided Beards Tavern",
"Kings Own Tailors",
"County Arms Smithy",
"Golden Rise Bakery",
"Spotted Finch Meadows",
"Fairy Sisters Pasture",
"Twin Owls Inn ",
"Snow Bard Village",
"DarkVale Valley",
"Tallards Orchards ",
"Two Moons Cove ",
"Star Fin Shipyard",
"White Wind Mill Bakery",
"Trolls Hand Masons",
"Vatrecastle Village",
"Archers Arm Tavern",
"Throne of the Ancient One Temple",
"Swift Axes Woodcutter Shoppe ",
"Evenly Yoked Stables",
"Open Hands Almshouse",
"Far Wren Village"
]

@ -0,0 +1,142 @@
[
"Innkeeper",
"Abigail",
"Adda the White",
"Alina",
"Alvin",
"Andrew Gablodda",
"Angus",
"Antoinette",
"Armorer",
"Azar Javed",
"Berengar",
"Bogut Kuternoga",
"Boholt",
"Bookseller",
"Bootblack (Vizima)",
"Brogg",
"Buse",
"Butter Bean",
"Carmen",
"Caroline",
"Celina",
"Chireadan",
"City Guard",
"Coccacidium",
"Coleman",
"Con artist",
"Conrad",
"Corbin",
"Dandelion",
"Declan Leuvaarden",
"Erkyn Blunt",
"Eskel",
"Fat Fred",
"Fence",
"Ferryman",
"Fisher King (lover of Lady of the Lake)",
"Foltest",
"Franz Kniprode",
"Friendly vodyan priest",
"Gambling ghost",
"Gardener",
"Gellert Bleinheim",
"Geralt of Rivia",
"Golan Vivaldi",
"Grandma",
"Gravedigger (Vizima)",
"Greater cockatrice",
"Half-elf",
"Halfar",
"Haren Brogg",
"Havekar",
"Herbalist",
"Hermit",
"Hierophant",
"Hildegard Zollstock",
"Ida Vertz",
"Ilsa",
"Jacques de Aldersberg",
"Jean-Pierre",
"Jester",
"Jethro",
"Juan Pablo Vassermiller",
"Julian",
"Kalkstein",
"King of the Wild Hunt",
"Kobus de Vollen",
"Kristina",
"Lambert",
"Leo",
"Lilly",
"Lowlife",
"Mage",
"Maggot",
"Malcolm Stein",
"Marcus Vertz",
"Mason Harn",
"Meis",
"Midday bride",
"Mikul",
"Moa",
"Morenn",
"Munro Bruys",
"Murky Waters blacksmith",
"Murky Waters healer",
"Mysterious assassin",
"Mysterious man",
"Nadir",
"Naiad (Murky Waters lakeside)",
"Novice nun",
"Odo (merchant)",
"Ori Reuven",
"Ozzrel",
"Pat",
"Patrick de Weyze",
"Philippa Eilhart",
"Professor (assassin)",
"Radovid V",
"Ramerot",
"Ramsmeat",
"Raven",
"Rayla",
"Raymond Maarloeve",
"Ren Grouver",
"Resolute girl",
"Reverend (Outskirts)",
"The Rock",
"Roderick de Wett",
"Roland Bleinheim",
"Royal huntsman",
"Saint Gregory",
"Savolla",
"Scoia'tael quartermaster",
"Shani",
"Siegfried of Denesle",
"Swordsmith",
"Teyu",
"Thaler (spy)",
"Tobias Hoffman",
"Torch Guardian",
"Toruviel",
"Townsfolk",
"Trader",
"Triss Merigold",
"Ureus",
"Vaska",
"Velerad",
"Vertz",
"Vesemir",
"Vesna Hood",
"Vesper",
"Vetala",
"Vincent Meis",
"Voref",
"Yaevinn",
"Yamo Ryeboozer",
"Yaren Bolt",
"Zahin Schmartz",
"Zdenek",
"Zephyr",
"Zoltan Chivay"
]

@ -0,0 +1,47 @@
<template>
<v-card flat>
<v-card-text>
<v-text-field
label="i8n:OpenWeatherMap API key"
v-model="settings.weather.api"
placeholder="###"
></v-text-field>
<weather-find-location
:api="settings.weather.api"
:location.sync="settings.weather.location"
:lang="lang"
:unit="unit"
></weather-find-location>
</v-card-text>
</v-card>
</template>
<script>
import weatherFindLocation from "@/components/WeatherFindLocation";
export default {
props: {
settings: {
type: Object,
required: true
}
},
components: {
weatherFindLocation
},
data: () => ({
}),
computed: {
lang() {
return this.settings.language || "EN";
},
unit() {
return this.settings.language === "EN" ? "" : "metric";
}
},
methods: {
commit() {}
}
};
</script>

@ -0,0 +1,61 @@
<template>
<v-card flat>
<v-toolbar dark color="primary">
<v-btn icon dark @click="dialog = false">
<v-icon>$close</v-icon>
</v-btn>
<v-toolbar-title class="pl-0">Enter the password for "{{ ssid }}"</v-toolbar-title>
<v-progress-linear
:active="isConnecting"
indeterminate
absolute
bottom
color="deep-orange accent-4"
></v-progress-linear>
</v-toolbar>
<v-card-text class="pa-5">
<v-text-field
v-model="password"
:append-icon="show1 ? 'mdi-eye' : 'mdi-eye-off'"
:type="show1 ? 'text' : 'password'"
label="i8n:Password"
@click:append="show1 = !show1"
></v-text-field>
</v-card-text>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn depressed :disabled="isConnecting" color="primary" @click="onWifiConnect()">i8n:Join</v-btn>
</v-card-actions>
</v-card>
</template>
<script>
import apiDevice from "@/api/device";
export default {
props: {
ssid: {
type: String,
required: true
}
},
data: () => ({
isConnecting: false,
//wifiAvailable: [],
//wifiConnectModal: false,
//wifiConnectSSID: null,
password: null,
show1: false
}),
methods: {
onWifiConnect() {
this.isConnecting = true;
apiDevice.wifiConnect(this.ssid, this.password);
}
}
};
</script>

@ -39,7 +39,7 @@ export default {
props: {
location: {
type: Number,
required: true
required: false
},
api: {
type: String,

@ -8,15 +8,15 @@ const MY_ICONS = {
// system icons
//complete: {component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/check/baseline.svg')},
//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')},
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')},
//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')},
//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')},
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')},
//checkboxOff: {component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/check_box_outline_blank/baseline.svg')},
//checkboxIndeterminate: {component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/indeterminate_check_box/baseline.svg')},
@ -26,8 +26,8 @@ const MY_ICONS = {
//menu: {component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/menu/baseline.svg')},
//subgroup: {component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/arrow_drop_down/baseline.svg')},
dropdown: {component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/arrow_drop_down/baseline.svg')},
//radioOn: {component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/radio_button_checked/baseline.svg')},
//radioOff: {component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/radio_button_unchecked/baseline.svg')},
radioOn: {component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/radio_button_checked/baseline.svg')},
radioOff: {component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/radio_button_unchecked/baseline.svg')},
//edit: {component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/edit/baseline.svg')},
//ratingEmpty: {component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/star_border/baseline.svg')},
//ratingFull: {component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/star/baseline.svg')},
@ -47,6 +47,7 @@ const MY_ICONS = {
//preview: {component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/remove_red_eye/baseline.svg')},
//search: {component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/search/baseline.svg')},
memory: {component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/memory/baseline.svg')},
lock: {component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/lock/baseline.svg')},
// wifi
signalWifi0: {component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/signal_wifi_0_bar/baseline.svg')},
@ -57,7 +58,13 @@ const MY_ICONS = {
signalWifi3: {component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/signal_wifi_3_bar/baseline.svg')},
signalWifi3Lock: {component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/signal_wifi_3_bar_lock/baseline.svg')},
signalWifi4: {component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/signal_wifi_4_bar/baseline.svg')},
signalWifi4Lock: {component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/signal_wifi_4_bar_lock/baseline.svg')}
signalWifi4Lock: {component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/signal_wifi_4_bar_lock/baseline.svg')},
// setup
wb_sunny: {component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/wb_sunny/baseline.svg')},
open_in_new: {component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/open_in_new/baseline.svg')},
face: {component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/face/baseline.svg')},
autorenew: {component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/autorenew/baseline.svg')}
}

@ -3,18 +3,30 @@ import VueRouter from 'vue-router'
const Dashboard = () => import('../views/Dashboard')
const Settings = () => import('../views/Settings')
//const Wifi = () => import(/* webpackChunkName: "wifi" */ '../views/Wifi')
//const Sandbox = () => import(/* webpackChunkName: "sandbox" */ '../views/Sandbox')
const SetupStart = () => import('../views/Setup/Start')
const SetupCountry = () => import('../views/Setup/Country')
const SetupWifi = () => import('../views/Setup/Wifi')
const SetupWeather = () => import('../views/Setup/Weather')
const SetupName = () => import('../views/Setup/Name')
const SetupAppearance = () => import('../views/Setup/Appearance')
const SetupDone = () => import('../views/Setup/Done')
Vue.use(VueRouter);
export default new VueRouter({
routes: [
{ path: '/', component: Dashboard },
{ path: '/settings', component: Settings, meta: { transitionName: 'slide' } },
//{ path: '/wifi', component: Wifi },
//{ path: '/sandbox', component: Sandbox },
{ path: '/settings', component: Settings, meta: { transitionName: 'slide' } },
// setup wizard
{ path: '/setup/start', component: SetupStart, meta: { transitionName: 'slide' } },
{ path: '/setup/country', component: SetupCountry, meta: { transitionName: 'slide' } },
{ path: '/setup/wifi', component: SetupWifi, meta: { transitionName: 'slide' } },
{ path: '/setup/weather', component: SetupWeather, meta: { transitionName: 'slide' } },
{ path: '/setup/name', component: SetupName, meta: { transitionName: 'slide' } },
{ path: '/setup/appearance', component: SetupAppearance, meta: { transitionName: 'slide' } },
{ path: '/setup/done', component: SetupDone, meta: { transitionName: 'slide' } },
{ path: '*', redirect: '/' }
],

@ -0,0 +1,50 @@
<template>
<v-container fluid _fill-height>
<v-card flat class="mx-auto" width="520">
<v-card-title class="display-2 mb-12 justify-center text-center">Appearance</v-card-title>
<v-radio-group v-model="settings.device.theme" row>
<v-radio label="Light" value="white"></v-radio>
<v-radio label="Dark" value="black"></v-radio>
</v-radio-group>
<v-card-actions>
<v-btn depressed block color="primary" @click="commitStep()">Continue</v-btn>
</v-card-actions>
</v-card>
</v-container>
</template>
<script>
import apiDevice from "@/api/device";
export default {
data: () => ({
isLoading: true,
isSaving: false,
settings: null
}),
created() {
apiDevice.getSettings(settings => {
this.settings = settings;
this.isLoading = false;
});
},
methods: {
commitStep() {
this.isSaving = true;
apiDevice.putSettings({device: this.settings.device}, () => {
this.isSaving = false;
this.nextStep();
});
},
nextStep() {
this.$router.push("/setup/done");
}
}
}
</script>

@ -0,0 +1,114 @@
<template>
<v-container fluid _fill-height>
<template v-if="currentStep === 0">
<!-- country -->
<v-card flat class="mx-auto" width="520">
<v-card-title
class="display-2 mb-12 justify-center text-center"
>Select Your Country or Region</v-card-title>
<v-list class="ml-5 pa-0">
<template v-for="(country, code) in availableCountries">
<div :key="code">
<v-divider></v-divider>
<v-list-item class="pl-1" @click="commitCountry(code, country)">
<!--<v-list-item-icon>{{ country.emoji }}</v-list-item-icon>-->
<v-list-item-content>{{ country.native }}</v-list-item-content>
<v-list-item-action>
<v-icon>$next</v-icon>
</v-list-item-action>
</v-list-item>
</div>
</template>
</v-list>
</v-card>
</template>
<template v-else-if="currentStep === 1">
<!-- timezone if needed -->
<v-card flat class="mx-auto" width="520">
<v-card-title class="display-2 mb-12 justify-center text-center">Select Your Timezone</v-card-title>
<v-list class="ml-5 pa-0">
<template v-for="(zone, i) in availableTimeZones">
<div :key="i">
<v-divider></v-divider>
<v-list-item class="pl-1" @click="commitTimezone(zone)">
<v-list-item-content>{{ zone }}</v-list-item-content>
<v-list-item-action>
<v-icon>$next</v-icon>
</v-list-item-action>
</v-list-item>
</div>
</template>
<v-divider></v-divider>
</v-list>
</v-card>
</template>
</v-container>
</template>
<script>
import apiDevice from "@/api/device";
import { countries } from "countries-list";
import timezones from "countries-and-timezones";
export default {
data: () => ({
currentStep: 0,
isLoading: true,
isSaving: true,
settings: null,
availableCountries: countries,
availableTimeZones: []
}),
created() {
apiDevice.getSettings(settings => {
this.settings = settings;
this.isLoading = false;
});
},
methods: {
commitCountry(code, country) {
this.settings.system.country = code;
this.settings.system.language = country.languages[0];
// get also timezone
const zone = timezones.getCountry(code);
if (zone.timezones.length > 1) {
this.availableTimeZones = zone.timezones;
this.currentStep = 1;
} else {
this.commitTimezone(zone.timezones[0]);
}
},
commitTimezone(zone) {
this.settings.system.timezone = zone;
const timezone = timezones.getTimezone(zone);
this.settings.system.utc = timezone.utcOffset * 60;
this.settings.system.dst = timezone.dstOffset * 60;
this.commitStep();
},
commitStep() {
this.isSaving = true;
apiDevice.putSettings({ system: this.settings.system }, () => {
this.isSaving = false;
this.$router.push("/setup/wifi");
});
}
}
};
</script>

@ -0,0 +1,36 @@
<template>
<v-container fluid _fill-height>
<v-card flat class="mx-auto" width="540">
<!--<v-card-title class="display-2 mb-12 justify-center text-center">Welcome to paperdash</v-card-title>-->
<v-card-title class="display-2 mb-12 justify-center text-center">Hello {{ settings.device.name }}</v-card-title>
<v-card-actions>
<v-btn outlined block color="primary" @click="onStart">Let's start</v-btn>
</v-card-actions>
</v-card>
</v-container>
</template>
<script>
import apiDevice from "@/api/device";
export default {
data: () => ({
isLoading: true,
isSaving: false,
settings: null
}),
created() {
apiDevice.getSettings(settings => {
this.settings = settings;
this.isLoading = false;
});
},
methods: {
onStart() {
this.$router.push("/");
}
}
};
</script>

@ -0,0 +1,98 @@
<template>
<v-container fluid _fill-height>
<v-card flat class="mx-auto" width="520">
<div class="justify-center text-center">
<v-icon viewBox="0 0 24 24" style="width: 64px; height: 64px; fill: #FF9800">$face</v-icon>
</div>
<v-card-title class="display-2 mb-12 justify-center text-center">Give it a name</v-card-title>
<p
class="text-center"
>TODO:Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam</p>
<v-skeleton-loader v-if="isLoading" type="list-item-two-line" class="mx-auto"></v-skeleton-loader>
<template v-else>
<v-card-text>
<v-text-field label="i8n:My paperdash name" v-model="settings.device.name">
<template v-slot:append-outer>
<v-icon
viewBox="0 0 24 24"
style="width: 48px; height: 48px;"
@click="setRandomeName()"
>$autorenew</v-icon>
</template>
</v-text-field>
</v-card-text>
<v-card-actions class="flex-column">
<v-btn
:disabled="!isStepValid"
:loading="isSaving"
depressed
block
color="primary"
@click="commitStep()"
>Continue</v-btn>
</v-card-actions>
</template>
</v-card>
</v-container>
</template>
<script>
import apiDevice from "@/api/device";
import randomNames from "@/assets/fantasyNames.json";
export default {
data: () => ({
isLoading: true,
isSaving: false,
settings: null
}),
created() {
apiDevice.getSettings(settings => {
this.settings = settings;
if (!this.isStepValid) {
this.setRandomeName();
}
this.isLoading = false;
});
},
computed: {
isStepValid() {
return (
this.settings.device.name != undefined &&
this.settings.device.name !== ""
);
}
},
methods: {
commitStep() {
this.isSaving = true;
apiDevice.putSettings({device: this.settings.device}, () => {
this.isSaving = false;
this.nextStep();
});
},
nextStep() {
this.$router.push("/setup/appearance");
},
setRandomeName() {
this.settings.device.name =
randomNames[Math.floor(Math.random() * randomNames.length)];
}
}
};
</script>
<style scoped>
>>> .v-text-field input {
font-size: 2.2em;
max-height: inherit;
}
</style>

@ -0,0 +1,116 @@
<template>
<v-container fluid _fill-height>
<v-card flat class="mx-auto" width="520">
<v-card-title class="display-2 mt-12 justify-center text-center">Hello paperdash</v-card-title>
<Case
v-if="0"
id="device_"
:class="[device.theme, device.case, device.front, 'case_orange', 'my-12']"
/>
<svg
id="device"
:class="[device.theme, device.case, device.front, 'case_orange front_orange', 'my-12 mx-auto']"
version="1.0"
xmlns="http://www.w3.org/2000/svg"
_width="561px"
_height="527px"
viewBox="0 0 5610 5270"
preserveAspectRatio="xMidYMid meet"
width="400"
>
<!--
<defs>
<pattern id="pattern_b" width="100%" height="100%">
<image x="0" y="0" height="84%" href="/face-weather-b.png"/>
</pattern>
<pattern id="pattern_w" width="100%" height="100%">
<image x="0" y="0" height="84%" href="/face-weather-w.png"/>
</pattern>
</defs>
-->
<g id="border" fill="#262626" stroke="none">
<path
d="M442 5110 c-227 -86 -417 -161 -422 -166 -4 -5 -12 -1063 -16 -2350 -7 -2292 -6 -2342 12 -2352 31 -16 817 -242 844 -242 14 0 1086 158 2383 352 l2358 353 6 25 c9 35 -66 3683 -76 3693 -4 4 -1049 194 -2322 422 -1273 228 -2323 416 -2334 419 -11 2 -206 -67 -433 -154z"
/>
</g>
<g fill="#f3f3f3" stroke="none">
<path
id="front"
d="M860 2630 l0 -2611 33 6 c17 3 1080 163 2360 355 1281 191 2330 350 2332 352 6 6 -70 3666 -76 3672 -4 4 -4627 836 -4645 836 -2 0 -4 -1175 -4 -2610z m2378 1721 c1217 -175 2214 -320 2216 -322 7 -6 69 -3179 63 -3185 -8 -9 -4489 -624 -4499 -618 -10 6 -11 4444 0 4444 4 0 1003 -144 2220 -319z m2249 2 c-4 -3 -7 0 -7 7 0 7 3 10 7 7 3 -4 3 -10 0 -14z m60 -2890 c-4 -3 -7 0 -7 7 0 7 3 10 7 7 3 -4 3 -10 0 -14z"
/>
<path
id="image"
d="M1022 4642 c-7 -19 5 -4393 13 -4400 5 -5 4465 605 4472 611 4 4 -59 3161 -63 3166 -3 3 -3200 465 -4387 634 -19 2 -31 -1 -35 -11z"
/>
<path
id="case"
d="M438 5081 c-214 -81 -390 -150 -393 -153 -7 -6 -28 -4658 -22 -4664 2 -2 182 -56 400 -119 218 -64 403 -118 412 -121 13 -6 15 259 15 2600 0 2078 -3 2606 -12 2605 -7 0 -187 -67 -400 -148z"
/>
</g>
</svg>
<v-card-actions>
<v-btn depressed block color="primary" @click="commitStep()">Continue</v-btn>
</v-card-actions>
</v-card>
</v-container>
</template>
<script>
export default {
data: () => ({
device: {
theme: "theme_w",
case: "case_whitey",
front: "front_whitey"
}
}),
methods: {
commitStep() {
// TODO sav
this.$router.push("/setup/country");
}
}
};
</script>
<style scoped>
#device #border,
#device > path {
fill: #343434;
}
/* TODO device theme */
#device.theme_w #image {
fill: url(#pattern_w);
}
#device.theme_b #image {
fill: url(#pattern_b);
}
/* front color */
#device.front_blacky #front {
fill: #000;
}
#device.front_whitey #front {
fill: #f3f3f3;
}
#device.front_orange #front {
fill: #ee8816;
}
/* case color */
#device.case_blacky #case {
fill: #000;
}
#device.case_whitey #case {
fill: #f3f3f3;
}
#device.case_orange #case {
fill: #ee8816;
}
</style>

@ -0,0 +1,91 @@
<template>
<v-container fluid _fill-height>
<v-card flat class="mx-auto" width="520">
<div class="justify-center text-center">
<v-icon viewBox="0 0 24 24" style="width: 64px; height: 64px; fill: #FF9800">$wb_sunny</v-icon>
</div>
<v-card-title class="display-2 mb-12 justify-center text-center">Weather</v-card-title>
<v-skeleton-loader
v-if="isLoading"
type="list-item-two-line,list-item-two-line"
class="mx-auto"
></v-skeleton-loader>
<template v-else>
<v-card-text>
<v-text-field label="i8n:OpenWeatherMap API key" v-model="settings.weather.api">
<template v-slot:append-outer>
<v-icon @click="registerApiKey()">$open_in_new</v-icon>
</template>
</v-text-field>
<weather-find-location
:api="settings.weather.api"
:location.sync="settings.weather.location"
:lang="lang"
:unit="unit"
></weather-find-location>
</v-card-text>
<v-card-actions class="flex-column">
<v-btn :disabled="!isLocationValid" depressed block color="primary" @click="commitStep()">Continue</v-btn>
<v-btn
class="ma-0 mt-3"
text
block
color="primary"
@click="nextStep()"
>Set Up Later in Settings</v-btn>
</v-card-actions>
</template>
</v-card>
</v-container>
</template>
<script>
import apiDevice from "@/api/device";
import weatherFindLocation from "@/components/WeatherFindLocation";
export default {
components: {
weatherFindLocation
},
data: () => ({
isLoading: true,
settings: null
}),
created() {
apiDevice.getSettings(settings => {
this.settings = settings;
this.isLoading = false;
});
},
computed: {
lang() {
return this.settings.language || "EN";
},
unit() {
return this.settings.language === "EN" ? "" : "metric";
},
isLocationValid() {
return this.settings.weather.location > 0;
}
},
methods: {
commitStep() {
// TODO sav
this.nextStep();
},
nextStep() {
this.$router.push("/setup/name");
},
registerApiKey() {
window.open("http://openweathermap.org/");
}
}
};
</script>

@ -0,0 +1,82 @@
<template>
<v-container fluid _fill-height>
<v-card flat class="mx-auto" width="520">
<v-card-title class="display-2 mb-12 justify-center text-center">
Choose a
<br />Wi-Fi Network
</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-skeleton-loader>
<v-list v-else>
<v-list-item-group v-model="settings.wifi" mandatory>
<template v-for="(wifi, i) in wifiAvailable">
<div :key="i">
<v-divider v-if="i > 0"></v-divider>
<v-list-item class="px-1" :value="wifi.ssid" @click.stop="wifiConnectModal = true">
<v-list-item-content dark>
<v-list-item-title v-text="wifi.ssid"></v-list-item-title>
<v-list-item-subtitle v-text="wifi.bssid"></v-list-item-subtitle>
</v-list-item-content>
<v-list-item-icon>
<v-icon class="mx-2" v-if="wifi.secure">$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-list-item-group>
<v-divider></v-divider>
<v-btn text color="primary" class="px-0 my-2">Choose Another Network</v-btn>
</v-list>
<v-dialog v-model="wifiConnectModal" max-width="450">
<setup-wifi-connect
:ssid="settings.wifi"
@connected="commitWifi()"
@cancel="wifiConnectModal = false"
></setup-wifi-connect>
</v-dialog>
</v-card>
</v-container>
</template>
<script>
import apiDevice from "@/api/device";
import setupWifiConnect from "@/components/SetupWifiConnect";
export default {
components: {
setupWifiConnect
},
data: () => ({
isLoading: true,
settings: null,
wifiAvailable: [],
wifiConnectModal: false
}),
created() {
apiDevice.getSettings(settings => {
this.settings = settings;
apiDevice.wifiScan(list => {
this.wifiAvailable = list;
this.isLoading = false;
});
});
},
methods: {}
};
</script>

@ -136,8 +136,17 @@ void setupSettingsGet()
AsyncResponseStream *response = request->beginResponseStream("application/json");
DynamicJsonDocument root(1024);
root["device"]["angle"] = NVS.getInt("device.angle");
root["system"]["country"] = NVS.getString("system.country");
root["system"]["language"] = NVS.getString("system.language");
root["system"]["timezone"] = NVS.getString("system.timezone");
root["system"]["utc"] = NVS.getInt("system.utc");
root["system"]["dst"] = NVS.getInt("system.dst");
// gmtOffset_sec
// daylightOffset_sec
//root["device"]["angle"] = NVS.getInt("device.angle");
root["device"]["theme"] = NVS.getString("device.theme");
root["device"]["name"] = NVS.getString("device.name");
root["playlist"]["timer"] = NVS.getInt("playlist.timer");
@ -146,9 +155,9 @@ void setupSettingsGet()
root["weather"]["lang"] = NVS.getString("weather.lang");
root["weather"]["unit"] = NVS.getString("weather.unit");
root["cloud"]["mode"] = NVS.getString("cloud.mode");
root["cloud"]["url"] = NVS.getString("cloud.url");
root["cloud"]["token"] = NVS.getString("cloud.token");
//root["cloud"]["mode"] = NVS.getString("cloud.mode");
//root["cloud"]["url"] = NVS.getString("cloud.url");
//root["cloud"]["token"] = NVS.getString("cloud.token");
serializeJson(root, *response);
request->send(response);
@ -170,19 +179,39 @@ void setupSettingsPost()
}
else
{
NVS.setInt("device.angle", doc["device"]["angle"].as<unsigned int>());
NVS.setString("device.theme", doc["device"]["theme"]);
JsonVariant system = doc["system"];
if (!system.isNull()) {
NVS.setString("system.country", system["country"]);
NVS.setString("system.language", system["language"]);
NVS.setString("system.timezone", system["timezone"]);
NVS.setInt("system.utc", system["utc"].as<unsigned int>());
NVS.setInt("system.dst", system["dst"].as<unsigned int>());
}
NVS.setInt("playlist.timer", doc["playlist"]["timer"].as<unsigned int>());
JsonVariant device = doc["device"];
if (!device.isNull()) {
NVS.setInt("device.angle", device["angle"].as<unsigned int>());
NVS.setString("device.theme", device["theme"]);
NVS.setString("device.name", device["name"]);
}
NVS.setString("weather.api", doc["weather"]["api"]);
NVS.setInt("weather.loc", doc["weather"]["location"].as<unsigned int>());
NVS.setString("weather.lang", doc["weather"]["lang"]);
NVS.setString("weather.unit", doc["weather"]["unit"]);
JsonVariant playlist = doc["playlist"];
if (!playlist.isNull()) {
NVS.setInt("playlist.timer", playlist["timer"].as<unsigned int>());
}
JsonVariant weather = doc["weather"];
if (!doc["weather"].isNull()) {
NVS.setString("weather.api", weather["api"]);
NVS.setInt("weather.loc", weather["location"].as<unsigned int>());
NVS.setString("weather.lang", weather["lang"]);
NVS.setString("weather.unit", weather["unit"]);
}
/*
NVS.setString("cloud.mode", doc["cloud"]["mode"]);
NVS.setString("cloud.url", doc["cloud"]["url"]);
NVS.setString("cloud.token", doc["cloud"]["token"]);
*/
NVS.commit();
@ -442,8 +471,7 @@ void setupOTA()
AsyncWebServerResponse *response = request->beginResponse(200, "application/ld+json; charset=utf-8", shouldReboot ? "{\"success\": true}" : "{\"success\": false}");
response->addHeader("Connection", "close");
request->send(response);
},
request->send(response); },
[](AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final) {
if (!index)
{

Loading…
Cancel
Save