Prologue
Comme je l'ai mentionné ici , j'ai commencé à construire un aquarium intelligent basé sur le tableau NodeMCU
. Là-dessus, j'ai utilisé le firmware de micropython
, mis en place un serveur Web et créé une API pour manipuler tous les périphériques et capteurs. Étant donné que ma version d'un aquarium intelligent était à l'origine conçue comme autonome, je voulais en créer un UI
pour suivre tous les processus et pour les ajustements manuels. À chaque fois, utilisez des itinéraires comme: http://192.168.1.70/led_controller?impulse=4000&level=200&ledName=white
c'était très morne et peu pratique. Surtout lorsque vous êtes déjà couché et que seul votre téléphone est à portée de main. Et encore une fois, je voulais passer au niveau supérieur en développement et faire quelque chose d'amusant.
UI
Vue.js. , .. " " WI-FI
. , . , , . , , SPA(" ": "single page application"), , . backend
— LED- . , vue-cli
:
$ vue ui
Starting GUI...
Ready on http://localhost:8000
, :
vue-bootstrap
— .axios
—backend
API
.vuex
—
axios
url
plugin/axios.js
import Vue from 'vue';
import axios from 'axios';
import VueAxios from 'vue-axios';
axios.defaults.baseURL = 'http://192.168.1.70';
Vue.use(VueAxios, axios);
— , , , . .
App.vue
<template>
<div id="app">
<b-navbar type="dark" variant="primary" class="rounded">
<b-navbar-brand tag="h1" class="mb-0">Fish Tank</b-navbar-brand>
<b-icon
icon="brightness-alt-high"
font-scale="3"
variant="light"
class="rounded bg-primary p-1"
/>
</b-navbar>
<list-of-range-controllers/>
</div>
</template>
<script>
import ListOfRangeControllers from './components/ListOfRangeControllers';
export default {
name: 'App',
components: {
ListOfRangeControllers
}
}
</script>
<style scoped>
#app {
margin: 50px 20px;
}
</style>
Ensuite, j'ai réfléchi à la manière d'organiser la logique métier elle-même et de la séparer du modèle. J'ai décidé de l'essayer complètement à travers le Vuex
Vyuks lui-même ne s'est pas divisé, mais a tout fait dans un seul fichier. Pour le niveau de LED, j'utilise l'échelle de 0 - 100 %
, tandis que le backend
niveau de lumière lui-même est défini à partir des 0 - 1024
unités. Après avoir arrondi, j'ai pensé que je multiplierais simplement par 10 lorsque les données sont envoyées par la POST
demande ou diviser par 10 lorsque les données sont reçues par la GET
demande.
store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
whiteLED : 0,
waterTemperature : 0,
},
mutations: {
'SYNC_WHITE_LED' (state, level) {
state.whiteLED = level;
},
'SYNC_WATER_TEMPERATURE' (state, level) {
state.waterTemperature = level;
},
'SET_WHITE_LED' (state, level) {
state.whiteLED = level;
},
'SET_HEATER_LEVEL' (state, level) {
state.waterTemperature = level;
}
},
actions: {
async syncWhiteLED({commit}) {
try {
const response = await Vue.axios.get('/get_led_info?ledName=white');
commit('SYNC_WHITE_LED', response.data['level']/10);
}
catch(error) {
console.error(error);
}
},
async syncWaterTemperature({commit}) {
try {
const response = await Vue.axios.get('/get_water_tmp');
commit('SYNC_WATER_TEMPERATURE', response.data['water_temperature_c']);
}
catch(error) {
console.error(error);
}
},
async setWhiteLED({commit}, level) {
try {
await Vue.axios.get(`/led_controller?impulse=4000&level=${level*10}&ledName=white`);
commit('SET_WHITE_LED', level);
}
catch(error) {
console.error(error);
}
},
async setWaterTemperature({commit}, level) {
try {
await Vue.axios.get(`/heater_control?params=${level}`);
commit('SET_HEATER_LEVEL', level);
}
catch(error) {
console.error(error);
}
},
},
getters: {
whiteLED: state => {
return state.whiteLED;
},
waterTemperature: state => {
return state.waterTemperature;
},
}
})
Ensuite, je crée un composant universel où la valeur actuelle sera affichée, une échelle pour changer la valeur et quelques boutons pour synchroniser et changer réellement.
components/ui/RangeController.vue
<template>
<b-card
:title="header"
>
<b-alert show>
Change to : {{ controllerValue }}
{{
name.match(/Water/gi)
? 'C\u00B0' : '%'
}}
</b-alert>
<b-form-input
type="range"
:min="min"
:max="max"
v-model="controllerValue"
/>
<b-button
variant="outline-primary"
size="sm"
@click="$emit(`${buttonChangeName}Change`, controllerValue)"
>
{{ changeButton }}
</b-button>
<b-button
class="float-right"
variant="outline-success"
size="sm"
@click="$emit(`${buttonChangeName}Sync`)"
>
Sync value
</b-button>
</b-card>
</template>
<script>
export default {
props: {
name: {
type : String,
default : 'Header',
},
value: {
type : Number,
default : 0,
},
buttonChangeName: {
type : String,
default : 'Change'
},
min: {
type : Number,
default : 0
},
max: {
type : Number,
default : 100
}
},
data() {
return {
controllerValue: this.min,
}
},
computed: {
header() {
const isWater = this.name.match(/Water/gi);
const postfix = isWater ? 'C\u00B0' : '%';
const sufix = isWater ? 'Temperature' : this.name.match(/Pump/gi)? '' : 'LED';
return `${this.name} ${sufix} is : ${this.value} ${postfix}`;
},
changeButton() {
return `${this.buttonChangeName} change`;
},
}
}
</script>
, , , - DRY
, , , .
components/ListOfRangeControllers.vue
<template>
<b-container class="bv-example-row mt-4 mb-4">
<h1>Backlight</h1>
<b-row>
<b-col v-for="led in leds" :key="led.name">
<range-controller
:name="led.name"
:value="led.value"
:buttonChangeName="led.buttonName"
v-on="{
ledWhiteChange : ledWhiteChange,
ledWhiteSync : ledWhiteSync,
}"
/>
</b-col>
</b-row>
<h1>Temperature</h1>
<b-row>
<b-col>
<range-controller
name="Water"
:value="waterTemperature"
:min="20"
:max="45"
buttonChangeName="temperature"
@temperatureChange="temperatureChange"
@temperatureSync="temperatureSync"
/>
</b-col>
</b-row>
</b-container>
</template>
<script>
import RangeController from './ui/RangeController';
import { mapActions, mapGetters } from 'vuex'
export default {
components: {
RangeController
},
methods: {
...mapActions([
'syncWhiteLED',
'syncWaterTemperature',
'setWhiteLED',
]),
ledWhiteChange(value) {
this.setWhiteLED(value);
},
//
temperatureChange(value) {
console.log('temp is changed!' + `${value}`);
},
ledWhiteSync() {
this.syncWhiteLED();
},
async temperatureSync() {
await this.syncWaterTemperature();
console.log(this.waterTemperature);
},
},
computed: {
...mapGetters([
'waterTemperature',
'whiteLED',
]),
leds() {
return [
{
name: 'White',
value: this.$store.getters.whiteLED,
buttonName: 'ledWhite',
},
]
},
},
}
</script>
UI
, , . , . Vue
, … , , , . NodeMCU
Vue
. . , , . , . . , , :
- - (Ph)
- (kH)
- , , . ? . — . NodeMCU
"", .