#!/usr/bin/env python # -*- coding: utf-8 -*- import time import datetime import re # Example for output from agent # --------------------------------------------------------- #<<>> #Detected devices: Bad.Temp Dachboden.Temp Gaestezimmer.Temp Partyraum.Temp Schlafzimmer.Temp #Bad.Temp Bad.Temp TYPE LaCrosse # 2016-11-16 08:39:41 Bad.Temp battery ok # 2016-11-16 08:39:41 Bad.Temp dewpoint 15.1 # 2016-11-16 08:39:41 Bad.Temp humdiff 0 # 2016-11-16 08:39:41 Bad.Temp humidity 83 #eg.bad.waschmaschine_pwr eg.bad.waschmaschine_pwr TYPE Revolt # 2016-11-15 16:54:48 eg.bad.waschmaschine_pwr avgpower 263.19 # 2016-11-16 09:18:07 eg.bad.waschmaschine_pwr current 0 # 2016-11-16 09:18:07 eg.bad.waschmaschine_pwr energy 9.27 # 2016-11-16 09:18:07 eg.bad.waschmaschine_pwr frequency 50 # 2016-11-16 09:18:07 eg.bad.waschmaschine_pwr pf 0 # 2016-11-16 09:18:07 eg.bad.waschmaschine_pwr power 0 # 2016-11-16 09:18:07 eg.bad.waschmaschine_pwr state P: 0.0 E: 9.27 V: 226 C: 0.00 F: 50 Pf: 0.00 # 2016-11-16 09:18:07 eg.bad.waschmaschine_pwr voltage 226 #eg.wz.heizung eg.wz.heizung TYPE CUL_HM # eg.wz.heizung model HM-CC-RT-DN # 2016-11-15 09:46:13 eg.wz.heizung Activity alive # 2016-11-15 23:03:10 eg.wz.heizung CommandAccepted yes # 2016-11-07 06:01:06 eg.wz.heizung D-firmware 1.4 # 2016-11-07 06:01:06 eg.wz.heizung D-serialNr MEQ0252852 # 2016-11-07 06:04:23 eg.wz.heizung PairedTo 0x5FF2BE # 2016-11-07 06:04:23 eg.wz.heizung R-backOnTime 10 s # 2016-11-07 06:04:23 eg.wz.heizung R-btnLock on ## --------------------------------------------------------- def inventory_fhem(info): # Detected devices: Bad.Temp Dachboden.Temp Gaestezimmer.Temp line = info[0] if len(line) > 2: for device in line[2:]: yield device, {} def check_fhem(item, params, info): # [[u'Detected', u'fhem:', u'Bad.Temp', u'Dachboden.Temp', u'Gaestezimmer.Temp', u'Partyraum.Temp', u'Schlafzimmer.Temp'], [u'Bad.Temp', u'TYPE', u'LaCrosse'], [u'2016-07-14', u'13:15:07', u'state', u'T:', u'21.4', u'H:', u'77'], [u'2016-07-14', u'13:15:07', u'dewpoint', u'17.2'], [u'Dachboden.Temp', u'TYPE', u'LaCrosse'], [u'2016-07-14', u'13:15:10', u'state', u'T:', u'23.5', u'H:', u'53'], [u'2016-07-14', u'13:15:10', u'dewpoint', u'13.4'], [u'Gaestezimmer.Temp', u'TYPE', u'LaCrosse'], [u'2016-07-14', u'13:15:10', u'state', u'T:', u'22.8', u'H:', u'66'], [u'2016-07-14', u'13:15:10', u'dewpoint', u'16.1'], [u'Partyraum.Temp', u'TYPE', u'LaCrosse'], [u'2016-07-14', u'13:15:12', u'state', u'T:', u'23', u'H:', u'59'], [u'2016-07-14', u'13:15:12', u'dewpoint', u'14.6'], [u'Schlafzimmer.Temp', u'TYPE', u'LaCrosse'], [u'2016-07-14', u'13:15:08', u'state', u'T:', u'22.8', u'H:', u'59'], [u'2016-07-14', u'13:15:08', u'dewpoint', u'14.4']] fwarn, fcrit = None, None if "level_data_age" in params: dage_warn, dage_crit = params["level_data_age"] if "level_temp_max" in params: tmax_warn, tmax_crit = params["level_temp_max"] if "level_temp_min" in params: tmin_warn, tmin_crit = params["level_temp_min"] # See if any checks are diabled (set to 0) # if dage_warn != None and dage_warn == 0: # dage_warn = None; # if dage_crit != None and dage_crit == 0: # dage_crit = None; # if cbwarn != None and cbwarn == 0: # cbwarn = None; # if cbcrit != None and cbcrit == 0: # cbcrit = None; errorlevel, warnlevel = 0, 0 dev_model = "" dev_type = "" output = [] perfdata = [] data = {} in_device = 0 # ## bugfix, at beginning all timestamp up-to-date # ## necessary because some devices don't send all data (weather-provider has no battery) # battery_timestamp, dewpoint_timespamp, state_timestamp = time.time(), time.time(), time.time() for line in info: if len(line) == 4 and line[2] == 'TYPE': if line[0] == item: dev_type = line[3] in_device = 1 elif in_device == 1: break elif in_device == 1: if line[1] == 'model': dev_model = line[2] if len(line) > 3 and line[3] != 'null': data[line[3]] = {} data[line[3]]['value']=line[4] data[line[3]]['time']=( "%s %s" % (line[0], line[1])) data[line[3]]['channel'] = line[2] ## needed for traffic if len(line) > 6: if line[5] == 'hour': # convert hours to minutes data[line[3]]['value']=(int(line[4])*60)+int(line[6]) # mostly config mistake or incorrect agent output if in_device == 0: return (3, "UNKNOWN - unable to parse %s (type: %s, model: %s)" % (item, dev_type, dev_model )) # ################################################################## ## key => variable_name ## title => readable output ## unit => °C, %, V., kmh, ... (for output) ## channel => filter for channel (HomeMatic) ## show => print if no error ## perfd => print perfdata for key, title, unit, channel, show, perfd in [ ## key title unit channel show perfd ## ------- ------- ------- ------- ---- ----- ('temperature', 'temperature', u'°C', '', 1, 1), ('desired-temp', 'desiredtemp', u'°C', '', 1, 1), ('humidity', 'humidity', u'%', '', 1, 1), ('dewpoint', 'dewpoint', u'°C', '', 1, 1), ('contact', 'contact', '', '', 1, 1), ('valveposition', 'valveposition', u'%', '', 1, 1), ('battery', 'battery', '', '', 1, 0), ('batteryLevel', 'batteryLevel', 'V', '', 1, 1), ('controlMode', 'controlMode', '', 'Clima', 0, 0), ('Activity', 'activity', '', '', 0, 1), ('power', 'power', 'kWh', '', 1, 1), ('energy', 'energy', 'W', '', 1, 1), ('voltage', 'voltage', 'V', '', 1, 1), ## HomeMatic ('measured-temp', 'temperature', u'°C', '', 1, 1), ('actuator', 'valveposition', u'%', '', 1, 1), ('R-btnLock', 'btnLock', '', '', 0, 0), ('R-globalBtnLock', 'globalBtnLock', '', '', 0, 0), ('R-modusBtnLock', 'modusBtnLock', '', '', 0, 0), ('R-tempOffset', 'tempOffset', 'K', 'Clima', 1, 0), ## MAX! ('desiredTemperature', 'desiredtemp', u'°C', '', 1, 1), ('mode', 'mode', '', '', 0, 0), ('window', 'window', '', '', 1, 0), ('groupid', 'groupid', '', '', 1, 0), ## MilightDevice, WifiLight, ... ('brightness', 'brightness', u'%', '', 1, 1), ('brightness_on', 'brightness_on', u'%', '', 0, 1), ('ct', 'ct', 'K', '', 1, 1), ('hsv', 'hsv', '', '', 0, 0), ('transitionInProgress','transitionInProgress', '', '', 0, 0), ('hue', 'hue', '', '', 0, 0), ('saturation', 'saturation', '', '', 0, 1), ('RGB', 'RGB', '', '', 1, 0), ## MiLightBridge ('protocol', 'protocol', '', '', 0, 0), ('checkInterval', 'checkInterval', 's', '', 0, 0), ('sendInterval', 'sendInterval', 's', '', 0, 0), ## Speedtest ('download', 'download', 'Mbit/s', '', 1, 1), ('ping', 'ping', 'ms', '', 1, 1), ('state', 'state', '', '', 1, 0), ('upload', 'upload', 'Mbit/s', '', 1, 1), # ('path', 'path', '', '', 0, 0), ## TRAFFIC ('delay', 'delay', 'min', '', 1, 1), ('distance', 'distance', 'km', '', 1, 1), ('duration', 'duration', 'mins', '', 1, 1), ('duration_in_traffic', 'duration_in_traffic', 'mins', '', 1, 1), ## ESPEasy ('presence', 'presence', '', '', 1, 1), ## XiaomiFlowerSens ('fertility', 'fertility', '', '', 1, 1), ('lux', 'lux', 'lx', '', 1, 1), ('moisture', 'moisture', u'%', '', 1, 1), ## MQTT2 ('LWT', 'LWT', '', '', 1, 0), ## key title unit channel show perfd ## ------- ------- ------- ------- ---- ----- ## LUXTRONIK2 ('ambientTemperature', 'ambientTemperature', u'°C', '', 1, 1), ('averageAmbientTemperature', 'averageAmbientTemperature', u'°C', '', 0, 1), ('hotWaterTemperature', 'hotWaterTemperature', u'°C', '', 1, 1), ('hotWaterTemperatureTarget', 'hotWaterTemperatureTarget', u'°C', '', 0, 1), ('heatingSystemCircPump', 'heatingSystemCircPump', '', '', 0, 1), ('opModeHeating', 'opModeHeating', '', '', 0, 0), ('opModeHotWater', 'opModeHotWater', '', '', 0, 0), ('opStateHeatPump1', 'opStateHeatPump1', '', '', 0, 0), ('hotGasTemperature', 'hotGasTemperature', u'°C', '', 1, 1), ('flowTemperature', 'flowTemperature', u'°C', '', 1, 1), ('returnTemperature', 'returnTemperature', u'°C', '', 1, 1), ('returnTemperatureTarget', 'returnTemperatureTarget', u'°C', '', 0, 1), # ('counterHours2ndHeatSource1', 'counterHours2ndHeatSource1', '', '', 0, 0), # ('counterHours2ndHeatSource2', 'counterHours2ndHeatSource2', '', '', 0, 0), ('counterHoursHeatPump', 'counterHoursHeatPump', '', '', 1, 1), ('counterHoursHeating', 'counterHoursHeating', '', '', 1, 1), ('counterHoursHotWater', 'counterHoursHotWater', '', '', 1, 1), ]: ## reset display_value = "" ignoreit = "" # var_data_age = "" fetch_plain = None ## check if device output data (temperature, humidity, ... ) try: if data[key]['value']: value = data[key]['value'] ## build dynamic variable names ... var_max = 'level_%s_max' % title var_min = 'level_%s_min' % title var_data_age = '%s_date' % title var_plain = 'var_%s' % title ## e.g. contact: open (var_plain => open) var_params = '%s_params' % title ## set upper level to default values if exist else we set to null if eval("var_max") in params: upper_warn, upper_crit = params[eval("var_max")] else: upper_warn, upper_crit = 0, 0 ## set lower level to default values if exist else we set to null if eval("var_min") in params: lower_warn, lower_crit = params[eval("var_min")] else: lower_warn, lower_crit = 0, 0 ## set plain text variables to default if eval("var_plain") in params: fetch_plain = params[eval("var_plain")] # print var_data_age # if params[eval("desiredtemp_date")]: # print desiredtemp_date # pass # except: # pass for param_group in params.keys(): ## just loop over param categories (WATO) ## all categories need to named "*_params" if re.match('[a-z]+_params', param_group) is not None: ## loop over sub-categories for param_subgroup in params[param_group]: ## get configuration from WATO ## we create the runtime variable "fetch_params" try: ## like: fetch_params = params["climate_params"] fetch_params = params[param_group][param_subgroup] except: pass ## get minimal thresholds try: if eval("var_min") in fetch_params: ## like: if level_temperature_min in fetch_params["climate_params"]: lower_warn, lower_crit = fetch_params[eval("var_min")] except: pass ## get maximal thresholds try: ## like: if level_temperature_max in fetch_params[]: if eval("var_max") in fetch_params: upper_warn, upper_crit = fetch_params[eval("var_max")] except: pass ## get plain text try: ## like: if level_temperature_min in fetch_params[]: if eval("var_plain") in fetch_params: fetch_plain = fetch_params[eval("var_plain")] except: pass ## not used (yet) # try: # if eval(var_data_age) and eval(var_data_age) != "": # local_timestamp = time.time() # var_data_timestamp = time.mktime(datetime.datetime.strptime(eval(var_data_age) , "%Y-%m-%d %H:%M:%S").timetuple()) # data_age = int( ( local_timestamp - var_data_timestamp ) / 60 ) # except: # ## bugfix, at beginning all timestamp up-to-date # ## necessary because some devices don't send all data (weather-provider has no battery) # data_age = 0 # pass # if data_age > dage_crit: ## print ("data_age = %s, dage_crit = %s" % (data_age, dage_crit)) # display_value = ("data to old [" + str(data_age) + "min] (!!)") # errorlevel = 2 # elif data_age > dage_crit: # display_value = ("data to old [" + str(data_age) + "min] (!)") # warnlevel = 1 # else: ## calulate warn level for dewpoint if key == 'dewpoint': upper_warn = float(data['temperature']['value']) - upper_warn upper_crit = float(data['temperature']['value']) - upper_crit ## ignore humidity by default if dewpoint is given if key == 'humidity' and params['var_dewpoint_override'] == 'true': ignoreit = 'true' ## there are sometime duplicate keys on channel like controlmode in '_Clima' and '_Climate' ## Just filter out some HomeMatic-channel if channel == '' or data[key]['channel'] == "%s_%s" % (item, channel): ## check critical level (max/min) if ( (upper_crit != 0 and float(value) >= upper_crit) or (lower_crit != 0 and float(value) <= lower_crit) ) and not ignoreit: if upper_crit != 0 and float(value) >= upper_crit: display_value = ('%s%s (warn/crit at %s%s/%s%s) (!!)' % ( value, unit, upper_warn, unit, upper_crit, unit)) elif float(value) <= lower_crit: display_value = ('%s%s (warn/crit at %s%s/%s%s) (!!)' % ( value, unit, lower_warn, unit, lower_crit, unit)) errorlevel = 2 ## check warning level (max/min) elif ( (upper_warn != 0 and float(value) >= upper_warn) or (lower_warn != 0 and float(value) <= lower_warn) ) and not ignoreit: if upper_warn != 0 and float(value) >= upper_warn: display_value = ('%s%s (warn/crit at %s%s/%s%s) (!)' % ( value, unit, upper_warn, unit, upper_crit, unit)) elif float(value) <= lower_warn: display_value = ('%s%s (warn/crit at %s%s/%s%s) (!)' % ( value, unit, lower_warn, unit, lower_crit, unit)) warnlevel = 1 elif fetch_plain: ## just alert if value not identic or ignored if value == fetch_plain or fetch_plain == "ignore": if show: display_value = ('%s%s' % ( value, unit )) if perfd: perfdata.append( ( title, 1, "" ) ) else: display_value = ("%s (expected: %s) (!!)" % (value, fetch_plain)) errorlevel = 2 if perfd: perfdata.append( ( title, 0, "" ) ) ## define output elif show: display_value = ('%s%s' % ( value, unit)) ## define perfdata (just add numeric values) if perfd and not fetch_plain: ## transform on/off (HomeMatic/MAX desiredTemp) if value == "off" or value == "on": value = 0 perfdata.append( ( title, value, upper_warn, upper_crit, "", "" ) ) ## not used (yet) # if data_age > dage_crit: # display_value += (" (data obsolet: %s min) (warn/crit at %s/%smin) (!!)" % (str(data_age), dage_warn, dage_crit)) # errorlevel = 2 # elif data_age > dage_warn: # display_value += (" (data obsolet: %s min) (warn/crit at %s/%smin) (!)" % (str(data_age), dage_warn, dage_crit)) # warnlevel = 1 if display_value != "": output.append('%s: %s' % (title, display_value)) pass except: pass # ################################################################## if errorlevel > 1: exitcode = 2 elif warnlevel > 0: exitcode = 1 else: exitcode = 0 return exitcode, ', '.join(output), perfdata factory_settings["fhem_default_params"] = { "level_data_age" : (30, 90), # warn/crit for data age (min) "level_temperature_max" : (26, 30), # warn/crit for max. temperature "level_temperature_min" : (15, 12), # warn/crit for min. temperature "level_humidity_max" : (70, 80), # warn/crit for max. humidity "level_humidity_min" : (50, 45), # warn/crit for min. humidity "level_dewpoint_max" : (3, 1), # warn/crit for diff to temperatur (exp. 17°C (dewp) vs 20°C(temp)) "level_batteryLevel_min" : (2.3, 2.1), # warn/crit for max. temperature "level_download_min" : (10.0, 8.0), # warn/crit for min download (speedtest) "level_upload_min" : (1.5, 1.0), # warn/crit for min upload (speedtest) "level_ping_max" : (100, 150), # warn/crit for ping (speedtest) "var_activity" : ("alive"), # default for alive "var_presence" : ("present"), # default for presence "var_contact" : ("ignore"), # default for contact "var_battery" : ("ok"), # default for battery "var_dewpoint_override" : ("true"), # don't alert humidity if dewpoint given "var_btnLock" : ("ignore"), # simple button lock "var_globalBtnLock" : ("ignore"), # full button lock "var_modusBtnLock" : ("ignore"), # modus button lock "var_controlMode" : ("auto"), # controlMode "var_RGB_color" : ("ignore"), # RGB color hexcode "var_brightness" : ("ignore"), # color brightness "var_state" : ("ignore"), # reading state ## MAX! "var_mode" : ("auto"), # like controlMode on HomeMatic devices "var_window" : ("ignore"), # like contact on HomeMatic devices ## MQTT2 "var_lwt" : ("ignore"), # LWT - the last will ## LUXTRONIK2 "level_ambientTemperature_max" : (26, 30), # warn/crit for max. ambientTemperature "level_ambientTemperature_min" : (15, 12), # warn/crit for min. ambientTemperature "level_averageAmbientTemperature_max" : (26, 30), # warn/crit for max. averageAmbientTemperature "level_averageAmbientTemperature_min" : (15, 12), # warn/crit for min. averageAmbientTemperature "level_hotWaterTemperature_max" : (60, 65), # warn/crit for max hotWaterTemperature "level_hotWaterTemperature_min" : (40, 37), # warn/crit for min hotWaterTemperature # "level_hotGasTemperature_max" : (26, 30), # warn/crit for max hotWaterTemperature # "level_hotGasTemperature_min" : (15, 12), # warn/crit for min hotWaterTemperature # "level_returnTemperature_max" : (35, 38), # warn/crit for max hotWaterTemperature # "level_returnTemperature_min" : (25, 23), # warn/crit for min hotWaterTemperature # "level_flowTemperature_max" : (35, 38), # warn/crit for max hotWaterTemperature # "level_flowTemperature_min" : (25, 23), # warn/crit for min hotWaterTemperatur "var_heatingSystemCircPump" : ("ignore"), # default for heatingSystemCircPump "var_opStateHeatPump1" : ("ignore"), # default for opStateHeatPump1 "var_opModeHeating" : ("Automatik"), # default for opModeHeating "var_opModeHotWater" : ("Automatik"), # default for opModeHotWater ## "level_moisture_min" : (20, 10), # warn/crit for moisture } check_info['fhem'] = { "check_function" : check_fhem, "inventory_function" : inventory_fhem, "service_description" : "FHEM %s", "has_perfdata" : True, "group" : "fhem", "default_levels_variable" : "fhem_default_params", }