Как я умный аквариум делал (frontend)
Пролог
Как я рассказывал тут, я начал постройку умного аквариума на основе платы NodeMCU
. На ней я использовал прошивку с micropython
, поднял веб сервер и сделал API для манипуляции всеми периферийными устройствами и датчиками. Поскольку мой вариант умного аквариума изначально планировался как автономный, я хотел сделать некий UI
для отслеживания всех процессов ну и для ручных корректировок. Каждый раз обращаться по роутам типа: http://192.168.1.70/led_controller?impulse=4000&level=200&ledName=white
было очень муторно и неудобно. Особенно когда ты уже лег спать и под рукой только телефон. Да и опять же, хотелось получить levelup в разработке и сделать что-то увлекательное.
За основу 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
Fish Tank
Далее думал как организовать саму бизнес логику и отделить ее от шаблона. Решил попробовать полностью через Vuex
Сам вьюкс не стал дробить, а сделал все в одном файлике. Для уровня LED я использую шкалу от 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
Change to : {{ controllerValue }}
{{
name.match(/Water/gi)
? 'C\u00B0' : '%'
}}
{{ changeButton }}
Sync value
Ну и наконец сам компонент со списком, я его намеренно сократил, что бы не перегружать кодом статью, плюс наверно где-то нарушил незыблемые правила программирования DRY
, тут надо провести рефакторинг кода, но мне нужно было здесь и сейчас, поэтому писал на скорую руку.
components/ListOfRangeControllers.vue
Backlight
Temperature
На компе
На мобилке
Вот я и получил UI
для моего умного аквариума, где я мог получить информацию об освещенности и температуре, и в ручном режиме выставить нужный свет и его интенсивность. Пришло время все это запустить вместе, повесить над аквариумом и проверить. Vue
приложение запустил на старом ноуте, лег на кровать и открыл браузер на телефоне… чтож верстка немного поехала на небольшом экране, но меня вполне устраивала, я знал, что все это еще будет переделываться и автоматизироваться. Но это была рабочая связка моего устройства на NodeMCU
и Vue
приложения. Я был рад и горд собой. В голове летали мысли о том, что же будет в конечном итоге, самое страшное для меня было реализация химического анализа воды. Ведь хороший анализ делается путем опускания в воду бумажных палочек, пропитанных определенным химическим составом. От чего она меняет цвет и уже по карте цветов можно определить есть ли каки либо отклонения от нормы. А анализ нужен не один, а именно, анализы на:
- Аммоний
- Нитриты
- Нитраты
- Фосфаты
- Кислотно-щелочной баланс (Ph)
- Карбонатная жесткость (kH)
- Кальций
- Магний
- Силикаты
Пока нахожусь в поиске каких-то решений, поскольку натыкался в магазинах на электронные приборы, которые все это измеряют. Муляж ли это? кто знает. Кто ищет — то найдет. Предстоит еще много работы как на стороне моей NodeMCU
так и на стороне «Клиента», но я не опускаю рук.