從零開始的DIY智慧澆水應用
前言
作為一個新世紀打工人,平常也會去養一些花草,來給我的房間增加點綠色和活力,但是常常因為工作忙而忘記一些事情。,畢竟我大部分的時間都是陪伴著電腦的(嚴正宣告:我不是個單身狗!!!
(¬◡¬)✧),之前在淘寶上買了個土壤溼度感測器和澆水裝置,自己改造了一下,通過 WiFi 和 SDDC 成功接入了愛智盒子,現在準備搞一個愛智應用來進行裝置的控制;
場景演示
先看一下我改造好的成品吧ヾ(๑╹◡╹)ノ",直接放在了我房間的展示櫃上開始用了。
這是澆水器!
這是插進去的土壤溼度感測器的屁股!
(σ゚∀゚)σ..:*☆ 哎喲是不是很厲害啊!
這個花盆裡面我插了之前的土壤溼度感測器和澆水器的出水口,然後澆水器的入水口我放在了一個裝水的瓶子裡,這樣一個自動澆水的東西就完成了。因為這是我自己室內用的小裝置,規模比較小,有想法的兄弟擴充套件擴充套件可以搞到在農業澆灌這方面去試試,想想還有點小興奮呢。ヾ(>∀<
應用演示
話不多說 ─=≡Σ(((つ•̀ω•́)つ,還是直接先看一下我的應用頁面長什麼樣子,
一如既往的放了一個數據顯示面板,一個裝置選擇模組,一個裝置引數設定模組。
使用起來也是非常的簡單,直接選擇一下土壤溼度感測器以及澆水裝置,拖動兩個遊標,設定澆水的適宜範圍,每當我花盆裡面的土壤溼度低於60%的時候就會通過 Spirit 向澆水器傳送加水訊號並開始加水,溼度達到85%就停止澆水,這樣一個智慧澆水的場景就完成了。
整體實現邏輯是比較簡單的,後面的話我也會逐步的在網上買一些其他的裝置,搭建一些比較完整的智慧場景出來。
程式碼分析
關於實現的程式碼也是利用了之前的裝置模組 device
只貼一下後端js部分的關鍵程式碼吧<( ̄ ﹌  ̄)@m 。
// 因為用的頻率比較高,個人比較懶,所以就基於正常的邏輯封裝了一下,基本可以滿足我目前乃至後面的大部分需求了,其他個別需求遇到再擴充。 // device_manager.js const Device = require('device'); const EventEmitter = require('events'); class DeviceManager extends EventEmitter { constructor() { super(); this.devMap = new Map(); this.controllerMap = new Map(); this.init(); } init() { // 獲取當前所有已加入網路的線上裝置! Device.list(true, (error, list) => { if (error) { console.error('Device.list error!' + error); } else { list.forEach((item) => { Device.info(item.devid, (error, info) => { if (error) { console.error('Device.info error!' + error); } else { this.devMap.set(item.devid, { devid: item.devid, alias: info.alias, report: info.report }); } }); }); } }); Device.on('join', async (devid, info) => { const dev = { devid, ...info }; this.devMap.set(devid, dev); this.emit('join', dev); }); Device.on('lost', (devid) => { const dev = this.devMap.get(devid); this.devMap.delete(devid); if (this.controllerMap.has(devid)) { this.controllerMap.delete(devid); } if (!dev) { this.emit('error', '應用出現未知錯誤,請退出重試!'); } else { this.emit('lost', dev); } }); } // 構建裝置控制物件 generateController(devid) { if (this.controllerMap.has(devid)) { return Promise.resolve(this.controllerMap.get(devid)) } const controller = new Device(); return new Promise((resolve, reject) => { controller.request(devid, (error) => { if (error) { reject(error); } else { this.controllerMap.set(devid, controller); resolve(controller); } }); }) } // 刪除控制器 deleteController(devid) { this.controllerMap.delete(devid); } // 傳送裝置訊息 sendDeviceInfo(devid, data) { const controller = this.controllerMap.get(devid); if (!controller) { return Promise.reject('程式出現未知錯誤,請退出重試!') } return new Promise((resolve, reject) => { controller.send(data, (err) => { if (err) { reject('控制裝置失敗,請重試!') } else { resolve(); } }, 3) }) } } const devManager = new DeviceManager(); module.exports = { devManager }
以上就是封裝的裝置管理模組了,接下來在main.js
中就會去使用該模組中的相關方法;
// main.js
...
function generateDevController(devid) {
return new Promise((resolve, reject) => {
const dev = devManager.devMap.get(devid);
devManager.generateController(devid).then((controller) => {
controller.on('message', (data) => {
const points = humidity_water_scene.settings.points;
if (isSceneDev(devid) && getDeviceType(dev) === 'humidity') {
socketIO.emit('humidity', Number(data.data.soil_humidity.toFixed(1))); // 0-100
if (!waterDev) {
return;
}
if (data.data.soil_humidity < points[0] && !watering) {
startWater(); // 澆水
} else if (data.data.soil_humidity >= points[0] && watering) {
stopWater(); // 停水
}
} else if (getDeviceType(dev) === 'water') {
if (data.data.watering === 'ON' && watering && t === 0 && humidityDev) {
setHumidityTimer(1000);
} else if (data.data.watering === 'OFF' && !watering && t) {
clearInterval(t);
t = 0
}
}
});
resolve(controller);
}).catch(() => {
reject(`應用缺少控制${dev.alias}的許可權!`);
})
})
}
...
上面的程式碼呼叫封裝的裝置管理模組例項的generateController
方法來構造裝置控制物件,
可以看到在裝置訊息監聽事件 message 中如果溼度感應裝置返回的資料值小於我們指定的值就會呼叫startWater
方法進行澆水,否則就會停止澆水;
同樣澆水裝置會根據控制澆水開關命令,來返回當前裝置狀態,如果澆水裝置處於執行中,則會主動加快獲取溼度感應器的實時數值,便於實時控制裝置。
總結
整體實現沒有太大的問題,某些細節方面可能還需要優化一下,不過現在已經開始用了,當然肯定不會直接接到水龍頭上,不然程式要是出bug了,那代價就有點太大了( uTェTu )!
今天的分享到此就結束了,具體的詳細程式碼可以去靈感桌面的祕密寶庫裡面去檢視,不說了,言盡於此,睡了。