Comment j'ai fait un aquarium intelligent (frontend)

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 UIpour 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=whitec'é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 — .
  • axiosbackend 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 VuexVyuks 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 backendniveau de lumière lui-même est défini à partir des 0 - 1024unité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 "", .






All Articles