#!/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]) 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() sys.stdout.flush() time.sleep(config_data['read_scale_interval_sec']) if __name__ == "__main__": main()