序幕
正如我在这里提到的那样,我开始基于开发板构建一个智能水族馆NodeMCU
。在上面,我使用的固件micropython
,建立了一个Web服务器,并制作了一个API来操纵所有外围设备和传感器。由于我最初设计的智能水族箱是一个独立的水族箱,因此我想创建一个智能水族箱UI
来跟踪所有过程并进行手动调整。每次使用以下路线:http://192.168.1.70/led_controller?impulse=4000&level=200&ledName=white
非常沉闷和不便。特别是当您已经上床睡觉并且只有手机在手的时候。再说一次,我想在开发中升级并做一些有趣的事情。
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>
然后,我考虑了如何组织业务逻辑本身并将其与模板分离。我决定完全通过Vuex
Vyuks本身进行尝试,尽管它没有分裂,但所有内容都集中在一个文件中。对于LED级别,我使用from标度0 - 100 %
,而backend
照明级别本身是按0 - 1024
单位设置的。四舍五入后,我认为当请求发送 数据时,我将简单地乘以10,而当POST
请求接收 数据时,我将简单地除以10 GET
。
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;
},
}
})
接下来,我将创建一个通用组件,在该组件中将显示当前值,一个用于更改该值的刻度以及几个用于同步和实际更改的按钮。
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
"", .