225 lines
7.5 KiB
Python
Executable File
225 lines
7.5 KiB
Python
Executable File
#!/usr/bin/env python
|
|
# -*- coding: UTF-8 -*-
|
|
# vim: expandtab sw=4 ts=4 sts=4:
|
|
#
|
|
# Beehive-Monitoring with SMS Alerts and Data-Upload to
|
|
# Webservice.
|
|
#
|
|
# Author: Joerg Lehmann, nbit Informatik GmbH
|
|
#
|
|
"""Beehive Monitoring"""
|
|
|
|
from __future__ import print_function
|
|
import gammu.smsd
|
|
import os
|
|
import sys
|
|
import serial
|
|
import time
|
|
import yaml
|
|
import random
|
|
|
|
# Root Path
|
|
APP_ROOT = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
|
|
|
|
# Read Configuration from YAML-File
|
|
with open("%s/bin/beielimon-config.yaml" % (APP_ROOT), 'r') as stream:
|
|
try:
|
|
config_data = yaml.load(stream)
|
|
#print(config_data)
|
|
except yaml.YAMLError as exc:
|
|
print(exc)
|
|
|
|
# Constants
|
|
INVALID_VALUE = -999
|
|
|
|
# Interface to gammu-smsd
|
|
smsd = gammu.smsd.SMSD('/etc/gammu-smsdrc')
|
|
|
|
class Scale(object):
|
|
def __init__(self, scale_config):
|
|
self.last_values = []
|
|
self.scale_config = scale_config
|
|
|
|
def __del__(self):
|
|
pass
|
|
|
|
def LogValue(self,weigh_in_gram,swarm_alarm):
|
|
cur_time = time.localtime()
|
|
timestamp = time.strftime("%Y-%m-%d %H:%M", cur_time)
|
|
year = time.strftime("%Y", cur_time)
|
|
month = time.strftime("%m", cur_time)
|
|
day = time.strftime("%d", cur_time)
|
|
if swarm_alarm:
|
|
prefix = 'swarmalarm'
|
|
else:
|
|
prefix = 'weight'
|
|
datafilename = "%s/data/%s-%s-%s%s%s.log" % (APP_ROOT,prefix,self.scale_config['scale_uuid'],year,month,day)
|
|
#print('Log to File %s' % (datafilename))
|
|
with open(datafilename, 'a') as file:
|
|
file.write('%s,%d\n' % (timestamp,weigh_in_gram))
|
|
|
|
def AppendReading(self,weigh_in_gram):
|
|
self.last_values.append(weigh_in_gram)
|
|
if len(self.last_values) > config_data['number_of_samples']:
|
|
self.last_values = self.last_values[1:]
|
|
#print('DEBUG WEIGHT: %d' % (weigh_in_gram))
|
|
#print(self.last_values)
|
|
# Wir loggen den Wert noch
|
|
self.LogValue(weigh_in_gram,False)
|
|
|
|
def Read(self):
|
|
pass
|
|
|
|
def CalibrateToZero(self):
|
|
pass
|
|
|
|
def SwarmAlarm(self):
|
|
return (self.GetWeighLoss() > config_data['swarm_alarm_threshold_gram'])
|
|
|
|
def ResetValues(self):
|
|
self.last_values = []
|
|
|
|
def GetLastValue(self):
|
|
return (self.last_values or [0])[-1]
|
|
|
|
def GetWeighLoss(self):
|
|
last_value = self.GetLastValue()
|
|
max_value = max(self.last_values or [0])
|
|
#print('BBB: ',max_value,last_value)
|
|
return (max_value - last_value)
|
|
|
|
def GetScaleConfig(self):
|
|
return self.scale_config
|
|
|
|
|
|
class ScaleUSB_PCE(Scale):
|
|
def __init__(self,serial_int, scale_config):
|
|
Scale.__init__(self, scale_config)
|
|
self.ser = serial_int
|
|
|
|
def Read(self):
|
|
res = INVALID_VALUE
|
|
self.ser.write('Sx\r\n')
|
|
weight_string = self.ser.readline()
|
|
if len(weight_string) == 16:
|
|
if (weight_string[12:13] == 'g'):
|
|
try:
|
|
res = int(weight_string[2:11])
|
|
except:
|
|
print('DEBUG WEIGHT STRING IS NOT AN INTEGER: %s' % (weight_string[2:11]))
|
|
else:
|
|
print('DEBUG WEIGHT STRING SHOULD BE IN GRAMS: but is [%s]' % (weight_string[12:13]))
|
|
else:
|
|
print('DEBUG WEIGHT STRING SHOULD BE 16 DIGITS: but is %d' % (len(weight_string)))
|
|
if res != INVALID_VALUE:
|
|
self.AppendReading(res)
|
|
|
|
class ScaleBT_KDPSB(Scale):
|
|
def __init__(self,serial_int, scale_config):
|
|
Scale.__init__(self, scale_config)
|
|
self.ser = serial_int
|
|
|
|
def Read(self):
|
|
res = INVALID_VALUE
|
|
try:
|
|
self.ser.write('\x02G\x03')
|
|
except:
|
|
print('DEBUG FEHLER BEIM SCHREIBEN')
|
|
time.sleep(1)
|
|
try:
|
|
weight_string = self.ser.read(12)
|
|
except:
|
|
print('DEBUG FEHLER BEIM LESEN')
|
|
weight_string=''
|
|
#print('DEBUG READ STRING HEX: %s' % (':'.join(x.encode('hex') for x in weight_string)))
|
|
#print('DEBUG READ STRING: %s' % (weight_string))
|
|
if len(weight_string) == 12:
|
|
#print('DEBUG READ STRING BBB: %s' % (weight_string[1:8]))
|
|
res = int(float(weight_string[1:8])*1000)
|
|
#print('DEBUG GEWICHT IN GRAM: %s' % (weight_string))
|
|
if res != INVALID_VALUE:
|
|
self.AppendReading(res)
|
|
|
|
def CalibrateToZero(self):
|
|
try:
|
|
self.ser.write('\x02T\x03')
|
|
except:
|
|
print('DEBUG FEHLER BEIM KALIBRIEREN')
|
|
time.sleep(1)
|
|
|
|
|
|
class ScaleDummy(Scale):
|
|
def __init__(self,scale_config):
|
|
Scale.__init__(self, scale_config)
|
|
|
|
def Read(self):
|
|
# Gewichts- Zu/Abnahme ist Random, manchmal gibt es einen
|
|
# Zufaelligen Schwarmalarm
|
|
delta = random.randint(-2, 4)
|
|
if (random.randint(0,1000) == 500):
|
|
delta = random.randint(-1000, - 501)
|
|
|
|
last_value = self.GetLastValue()
|
|
# Wir duerfen nicht negativ werden...
|
|
if (last_value + delta < 0):
|
|
delta = random.randint(0,4)
|
|
|
|
self.AppendReading(last_value + delta)
|
|
|
|
|
|
def send_sms(phonenumbers , text):
|
|
for phonenumber in phonenumbers:
|
|
message = {
|
|
'Text': text,
|
|
'SMSC': {'Location': 1},
|
|
'Number': phonenumber
|
|
}
|
|
|
|
#GELD SPAREN smsd.InjectSMS([message])
|
|
#print("Send SMS to %s, Text: %s" % (phonenumber, text))
|
|
|
|
def main():
|
|
scales = []
|
|
for scale_config in config_data['scales']:
|
|
if scale_config['interface_type'] == 'bt_kdpsb':
|
|
#print('DEBUG: sudo /usr/bin/rfcomm bind ' + scale_config['interface_name'] + ' ' + scale_config['address'] + ' ' + scale_config['interface_channel'])
|
|
os.system('sudo /usr/bin/rfcomm bind ' + scale_config['interface_name'] + ' ' + scale_config['address'] + ' ' + scale_config['interface_channel'])
|
|
ser = serial.Serial(port='/dev/' + scale_config['interface_name'],
|
|
baudrate=9600,
|
|
bytesize=serial.EIGHTBITS,
|
|
parity=serial.PARITY_EVEN,
|
|
timeout=20)
|
|
scale=ScaleBT_KDPSB(ser, scale_config)
|
|
#scale.CalibrateToZero()
|
|
scales.append(scale)
|
|
elif scale_config['interface_type'] == 'usb_pce':
|
|
ser = serial.Serial(port='/dev/' + scale_config['interface_name'],
|
|
baudrate=9600,
|
|
bytesize=serial.EIGHTBITS,
|
|
parity=serial.PARITY_EVEN,
|
|
timeout=20)
|
|
scale=ScaleUSB_PCE(ser, scale_config)
|
|
scales.append(scale)
|
|
elif scale_config['interface_type'] == 'dummy':
|
|
scale=ScaleDummy(scale_config)
|
|
scales.append(scale)
|
|
|
|
# Main Loop
|
|
while True:
|
|
for scale in scales:
|
|
scale.Read()
|
|
if scale.SwarmAlarm():
|
|
date_time = time.strftime("%d.%m.%Y %H:%M")
|
|
last_value = scale.GetLastValue()
|
|
weigh_loss = scale.GetWeighLoss()
|
|
# Wir loggen den Wert noch
|
|
scale.LogValue(last_value,True)
|
|
sms_message = '*** Schwarmalarm ***\nDatum/Zeit: %s\nWaage: %s\nLetztes Gewicht [g]: %d\nGewichtsverlust [g]: %d' % (date_time,scale.GetScaleConfig()['alias'],last_value,weigh_loss)
|
|
send_sms(scale.GetScaleConfig()['sms_alert_phonenumbers'],sms_message)
|
|
scale.ResetValues()
|
|
|
|
time.sleep(config_data['read_scale_interval_sec'])
|
|
|
|
if __name__ == "__main__":
|
|
main()
|