Приветствую!
Добавил возможность считывать показания счетчиков воды Элехант через Bluetooth (BLE). В движке правил не спец, так что на идеальность кода не претендую.
Счетчик посылает по BLE свои показания каждые 15 сек.
Скрипт elehant-scan.sh работает следующим образом.
- Запускается сканирование всех устройств BLE.
- Дампом RAW-данных определяются пакеты только от счетчиков Элехант.
- Все обнаруженные пакеты счетчиков Элехант выводятся построчно с значениями: “серийник, показание в М3, показание в литрах, RSSI, штамп времени”.
- Таймаут сканирования установлен 120 сек. Если сигнал до счетчков хороший, можно уменьшить до 60 сек, но у меня -80…-85, не всегда за 1 минуту ловятся пакеты.
elehant-scan.sh:
#!/bin/bash
# timeout for scan
timeout=$(($(date +%s) + 120))
halt_hcitool_lescan() {
pkill --signal SIGINT hcitool
}
trap halt_hcitool_lescan INT
process_complete_packet() {
## Elehant packet example
##packet="> 04 3E 21 02 01 03 00 82 4B 00 02 01 B0 15 14 FF FF FF 80 C8 0A 01 02 01 82 4B 00 67 44 00 00 69 1E 0A 00 AC"
local packet=${1//[\ |>]/}
# process only Elehant packets
if [[ ! $packet =~ ^043E210201.{10}0201B0 ]]; then
return
fi
serial=$((0x${packet:52:2}${packet:50:2}${packet:48:2}))
vol_cl=$((0x${packet:60:2}${packet:58:2}${packet:56:2}${packet:54:2}))
let "vol_m3 = $vol_cl / 10000"
let "vol_m3_cl = $vol_cl % 10000"
vol_m3=$vol_m3.$vol_m3_cl
let "vol_liters = $vol_cl / 10"
let "vol_liters_cl = $vol_cl % 10"
vol_liters=$vol_liters.$vol_liters_cl
rssi=$[$((0x${packet:70:2})) - 256]
echo $serial"|"$vol_m3"|"$vol_liters"|"$rssi"|"$(date "+%d-%m-%Y %H:%M:%S")
}
read_blescan_packet_dump() {
# packets span multiple lines and need to be built up
packet=""
while read line; do
# packets start with ">"
if [[ $line =~ ^\> ]]; then
# process the completed packet (unless this is the first time through)
if [ "$packet" ]; then
if [[ $(date +%s) > $timeout ]]; then
exit 0
fi
process_complete_packet "$packet"
fi
# start the new packet
packet=$line
else
# continue building the packet
packet="$packet $line"
fi
seconds=$(date +%s)
done
}
# begin BLE scanning
killall hcitool
hciconfig hci0 down
sleep 1
hciconfig hci0 up
hcitool lescan --duplicates > /dev/null &
sleep 1
# make sure the scan started
if [ "$(pidof hcitool)" ]; then
# start the scan packet dump and process the stream
hcidump --raw | read_blescan_packet_dump
else
echo "ERROR: it looks like hcitool lescan isn't starting up correctly" >&2
exit 1
fi
Соответствие серийника счетчика и его назначения задается в движке правил. Запускается раз в 5 минут.
elehant.js:
// Enter your meters here
var meter1_serial = 19330;
var meter1_device = "water_meter_ro";
var meter1_title = "Water meter (RO)";
var meter2_serial = 22222;
var meter2_device = "water_meter_cold";
var meter2_title = "Water meter (cold)";
var meter3_serial = 33333;
var meter3_device = "water_meter_hot";
var meter3_title = "Water meter (hot)";
defineVirtualDevice(meter1_device, {
title: meter1_title,
cells: {
"Serial number": {
type: "text",
value: ""
},
"Value in m3": {
type: "text",
value: ""
},
"Value in liters": {
type: "text",
value: ""
},
"RSSI": {
type: "text",
value: ""
},
"Timestamp": {
type: "text",
value: ""
}
}
});
//defineVirtualDevice(meter2_device, {
// title: meter2_title,
// cells: {
// "Serial number": {
// type: "text",
// value: ""
// },
// "Value in m3": {
// type: "text",
// value: ""
// },
// "Value in liters": {
// type: "text",
// value: ""
// },
// "RSSI": {
// type: "text",
// value: ""
// },
// "Timestamp": {
// type: "text",
// value: ""
// }
// }
//});
//defineVirtualDevice(meter3_device, {
// title: meter3_title,
// cells: {
// "Serial number": {
// type: "text",
// value: ""
// },
// "Value in m3": {
// type: "text",
// value: ""
// },
// "Value in liters": {
// type: "text",
// value: ""
// },
// "RSSI": {
// type: "text",
// value: ""
// },
// "Timestamp": {
// type: "text",
// value: ""
// }
// }
//});
defineRule("elehant_dynamic_refresh", {
when: cron("0 */5 * * *"),
then: function () {
runShellCommand("bash /mnt/data/root/elehant-scan.sh", {
captureOutput: true,
exitCallback: function (exitCode, capturedOutput) {
if (exitCode != 0) return;
var meterList=capturedOutput.split("\n")
for (var i=0; i<meterList.length; ++i) {
var meterParts=meterList[i].split("|")
if (meterParts[0] == meter1_serial) {
dev[meter1_device]["Serial number"] = meterParts[0];
dev[meter1_device]["Value in m3"] = meterParts[1];
dev[meter1_device]["Value in liters"] = meterParts[2];
dev[meter1_device]["RSSI"] = meterParts[3];
dev[meter1_device]["Timestamp"] = meterParts[4];
}
if (meterParts[0] == meter2_serial) {
dev[meter2_device]["Serial number"] = meterParts[0];
dev[meter2_device]["Value in m3"] = meterParts[1];
dev[meter2_device]["Value in liters"] = meterParts[2];
dev[meter2_device]["RSSI"] = meterParts[3];
dev[meter2_device]["Timestamp"] = meterParts[4];
}
if (meterParts[0] == meter3_serial) {
dev[meter3_device]["Serial number"] = meterParts[0];
dev[meter3_device]["Value in m3"] = meterParts[1];
dev[meter3_device]["Value in liters"] = meterParts[2];
dev[meter3_device]["RSSI"] = meterParts[3];
dev[meter3_device]["Timestamp"] = meterParts[4];
}
}
}
});
}
});