fhem 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323
  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. import time
  4. import datetime
  5. # Example for output from agent
  6. # ---------------------------------------------------------
  7. #<<<fhem>>>
  8. #Detected devices: Bad.Temp Dachboden.Temp Gaestezimmer.Temp Partyraum.Temp Schlafzimmer.Temp
  9. #Bad.Temp Bad.Temp TYPE LaCrosse
  10. # 2016-11-16 08:39:41 Bad.Temp battery ok
  11. # 2016-11-16 08:39:41 Bad.Temp dewpoint 15.1
  12. # 2016-11-16 08:39:41 Bad.Temp humdiff 0
  13. # 2016-11-16 08:39:41 Bad.Temp humidity 83
  14. #eg.bad.waschmaschine_pwr eg.bad.waschmaschine_pwr TYPE Revolt
  15. # 2016-11-15 16:54:48 eg.bad.waschmaschine_pwr avgpower 263.19
  16. # 2016-11-16 09:18:07 eg.bad.waschmaschine_pwr current 0
  17. # 2016-11-16 09:18:07 eg.bad.waschmaschine_pwr energy 9.27
  18. # 2016-11-16 09:18:07 eg.bad.waschmaschine_pwr frequency 50
  19. # 2016-11-16 09:18:07 eg.bad.waschmaschine_pwr pf 0
  20. # 2016-11-16 09:18:07 eg.bad.waschmaschine_pwr power 0
  21. # 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
  22. # 2016-11-16 09:18:07 eg.bad.waschmaschine_pwr voltage 226
  23. #eg.wz.heizung eg.wz.heizung TYPE CUL_HM
  24. # eg.wz.heizung model HM-CC-RT-DN
  25. # 2016-11-15 09:46:13 eg.wz.heizung Activity alive
  26. # 2016-11-15 23:03:10 eg.wz.heizung CommandAccepted yes
  27. # 2016-11-07 06:01:06 eg.wz.heizung D-firmware 1.4
  28. # 2016-11-07 06:01:06 eg.wz.heizung D-serialNr MEQ0252852
  29. # 2016-11-07 06:04:23 eg.wz.heizung PairedTo 0x5FF2BE
  30. # 2016-11-07 06:04:23 eg.wz.heizung R-backOnTime 10 s
  31. # 2016-11-07 06:04:23 eg.wz.heizung R-btnLock on
  32. ## ---------------------------------------------------------
  33. def inventory_fhem(info):
  34. # Detected devices: Bad.Temp Dachboden.Temp Gaestezimmer.Temp
  35. line = info[0]
  36. if len(line) > 2:
  37. for device in line[2:]:
  38. yield device, {}
  39. def check_fhem(item, params, info):
  40. # [[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']]
  41. fwarn, fcrit = None, None
  42. if "level_data_age" in params:
  43. dage_warn, dage_crit = params["level_data_age"]
  44. if "level_temp_max" in params:
  45. tmax_warn, tmax_crit = params["level_temp_max"]
  46. if "level_temp_min" in params:
  47. tmin_warn, tmin_crit = params["level_temp_min"]
  48. # See if any checks are diabled (set to 0)
  49. # if dage_warn != None and dage_warn == 0:
  50. # dage_warn = None;
  51. # if dage_crit != None and dage_crit == 0:
  52. # dage_crit = None;
  53. # if cbwarn != None and cbwarn == 0:
  54. # cbwarn = None;
  55. # if cbcrit != None and cbcrit == 0:
  56. # cbcrit = None;
  57. errorlevel, warnlevel = 0, 0
  58. model = ""
  59. output = []
  60. data = {}
  61. # ## bugfix, at beginning all timestamp up-to-date
  62. # ## necessary because some devices don't send all data (weather-provider has no battery)
  63. # battery_timestamp, dewpoint_timespamp, state_timestamp = time.time(), time.time(), time.time()
  64. errorlevel = 0
  65. ourstatus = 0
  66. device = ""
  67. for line in info:
  68. if len(line) == 4 and line[2] == 'TYPE':
  69. if line[0] == item:
  70. dev_type = line[3]
  71. ourstatus = 1
  72. elif ourstatus == 1:
  73. break
  74. elif ourstatus == 1:
  75. # if line[0] == model:
  76. # model = line[0]
  77. if len(line) > 3 and line[3] != 'null':
  78. data[line[3]] = {}
  79. data[line[3]]['value']=line[4]
  80. data[line[3]]['time']=( "%s %s" % (line[0], line[1]))
  81. data[line[3]]['channel'] = line[2]
  82. if ourstatus == 0:
  83. return (3, "UNKNOWN - %s - %s " % (device, dewpoint))
  84. perfdata = []
  85. # ##################################################################
  86. ## key => variable_name
  87. ## title => readable output
  88. ## unit => °C, &, V., kmh, ... (for output)
  89. ## channel => filter for channel (HomeMatic)
  90. ## show => print if no error
  91. ## perfd => print perfdata
  92. for key, title, unit, channel, show, perfd in [
  93. ## key title unit channel show perfd
  94. ## ------- ------- ------- ------- ---- -----
  95. ('temperature', 'temperature', u'°C', '', 1, 1),
  96. ('desired-temp', 'desiredtemp', u'°C', '', 1, 1),
  97. ('humidity', 'humidity', u'%', '', 1, 1),
  98. ('dewpoint', 'dewpoint', u'°C', '', 1, 1),
  99. ('contact', 'contact', '', '', 1, 1),
  100. ('valveposition', 'valveposition', u'%', '', 1, 1),
  101. ('battery', 'battery', '', '', 1, 0),
  102. ('batteryLevel', 'batteryLevel', 'V', '', 1, 1),
  103. ('controlMode', 'controlMode', '', 'Clima', 0, 0),
  104. ('Activity', 'activity', '', '', 0, 1),
  105. ('power', 'power', 'kWh', '', 1, 1),
  106. ('energy', 'energy', 'W', '', 1, 1),
  107. ('voltage', 'voltage', 'V', '', 1, 1),
  108. ## HomeMatic
  109. ('measured-temp', 'temperature', u'°C', '', 1, 1),
  110. ('actuator', 'valveposition', u'%', '', 1, 1),
  111. ('R-btnLock', 'btnLock', '', '', 0, 0),
  112. ('R-globalBtnLock', 'globalBtnLock', '', '', 0, 0),
  113. ('R-modusBtnLock', 'modusBtnLock', '', '', 0, 0),
  114. ## MilightDevice (and others?)
  115. ('brightness', 'brightness', u'%', '', 1, 1),
  116. ('brightness_on', 'brightness_on', u'%', '', 0, 1),
  117. ('ct', 'ct', 'K', '', 1, 1),
  118. ('hsv', 'hsv', '', '', 0, 0),
  119. ('transitionInProgress','transitionInProgress', '', '', 0, 1),
  120. ## MiLightBridge
  121. ('protocol', 'protocol', '', '', 0, 0),
  122. ('checkInterval', 'checkInterval', 's', '', 0, 0),
  123. ('sendInterval', 'sendInterval', 's', '', 0, 0),
  124. ## Speedtest
  125. ('download', 'download', 'Mbit/s', '', 1, 1),
  126. ('ping', 'ping', 'ms', '', 1, 1),
  127. ('state', 'state', '', '', 0, 0),
  128. ('upload', 'upload', 'Mbit/s', '', 1, 1),
  129. # ('path', 'path', '', '', 0, 0),
  130. ]:
  131. ## reset
  132. display_value = ""
  133. ignoreit = ""
  134. # var_data_age = ""
  135. ## check if device output data (temperature, humidity, ... )
  136. try:
  137. if data[key]['value']:
  138. value = data[key]['value']
  139. ## build dynamic variable names ...
  140. var_max = 'level_%s_max' % title
  141. var_min = 'level_%s_min' % title
  142. var_data_age = '%s_date' % title
  143. var_plain = 'var_%s' % title ## e.g. contact: open (var_plain => open)
  144. # print var_data_age
  145. # if params[eval("desiredtemp_date")]:
  146. # print desiredtemp_date
  147. # pass
  148. # except:
  149. # pass
  150. try:
  151. if params[eval("var_max")]:
  152. ## check the difference between dewpoint and temperature
  153. if key == 'dewpoint':
  154. max_warn_dewpoint, max_crit_dewpoint = params[eval("var_max")]
  155. max_warn = float(data['temperature']['value']) - max_warn_dewpoint
  156. max_crit = float( data['temperature']['value']) - max_crit_dewpoint
  157. else:
  158. max_warn, max_crit = params[eval("var_max")]
  159. pass
  160. except:
  161. max_warn, max_crit = 0, 0
  162. pass
  163. try:
  164. if params[eval("var_min")]:
  165. min_warn, min_crit = params[eval("var_min")]
  166. pass
  167. except:
  168. min_warn, min_crit = 0, 0
  169. pass
  170. try:
  171. if params[eval("var_plain")]:
  172. var_plain = params[eval("var_plain")]
  173. pass
  174. except:
  175. var_plain = None
  176. pass
  177. try:
  178. if eval(var_data_age) and eval(var_data_age) != "":
  179. local_timestamp = time.time()
  180. var_data_timestamp = time.mktime(datetime.datetime.strptime(eval(var_data_age) , "%Y-%m-%d %H:%M:%S").timetuple())
  181. data_age = int( ( local_timestamp - var_data_timestamp ) / 60 )
  182. pass
  183. except:
  184. ## bugfix, at beginning all timestamp up-to-date
  185. ## necessary because some devices don't send all data (weather-provider has no battery)
  186. data_age = 0
  187. pass
  188. # if data_age > dage_crit:
  189. ## print ("data_age = %s, dage_crit = %s" % (data_age, dage_crit))
  190. # display_value = ("data to old [" + str(data_age) + "min] (!!)")
  191. # errorlevel = 2
  192. # elif data_age > dage_crit:
  193. # display_value = ("data to old [" + str(data_age) + "min] (!)")
  194. # warnlevel = 1
  195. # else:
  196. if key == 'humidity' and params['var_dewpoint_override'] == 'true':
  197. ignoreit = 'true'
  198. ## there are sometime duplicate keys on channel like controlmode in '_Clima' and '_Climate'
  199. ## Just filter out some HomeMatic-channel
  200. if channel == '' or data[key]['channel'] == "%s_%s" % (item, channel):
  201. ## check critical level (max/min)
  202. if ( (max_crit != 0 and float(value) >= max_crit) or (min_crit != 0 and float(value) <= min_crit) ) and not ignoreit:
  203. if max_crit != 0 and float(value) >= max_crit:
  204. display_value = ('%s%s (warn/crit at %s%s/%s%s) (!!)' % ( value, unit, max_warn, unit, max_crit, unit))
  205. elif float(value) <= min_crit:
  206. display_value = ('%s%s (warn/crit at %s%s/%s%s) (!!)' % ( value, unit, min_warn, unit, min_crit, unit))
  207. errorlevel = 2
  208. ## check warning level (max/min)
  209. elif ( (max_warn != 0 and float(value) >= max_warn) or (min_warn != 0 and float(value) <= min_warn) ) and not ignoreit:
  210. if max_warn != 0 and float(value) >= max_warn:
  211. display_value = ('%s%s (warn/crit at %s%s/%s%s) (!)' % ( value, unit, max_warn, unit, max_crit, unit))
  212. elif float(value) <= min_warn:
  213. display_value = ('%s%s (warn/crit at %s%s/%s%s) (!)' % ( value, unit, min_warn, unit, min_crit, unit))
  214. warnlevel = 1
  215. elif var_plain:
  216. ## just alert if value not identic or ignored
  217. if value == var_plain or var_plain == "ignore":
  218. if show:
  219. display_value = ('%s%s' % ( value, unit ))
  220. if perfd:
  221. perfdata.append( ( key , 1, "" ) )
  222. else:
  223. display_value = ("%s (expected: %s) (!!)" % (value, var_plain))
  224. errorlevel = 2
  225. if perfd:
  226. perfdata.append( ( key, 0, "" ) )
  227. ## define output
  228. elif show:
  229. display_value = ('%s%s' % ( value, unit ))
  230. ## define perfdata (just add numeric values)
  231. if perfd and not var_plain:
  232. perfdata.append( ( title, value, max_warn, max_crit, "", "" ) )
  233. ## not used (yet)
  234. # if data_age > dage_crit:
  235. # display_value += (" (data obsolet: %s min) (warn/crit at %s/%smin) (!!)" % (str(data_age), dage_warn, dage_crit))
  236. # errorlevel = 2
  237. # elif data_age > dage_warn:
  238. # display_value += (" (data obsolet: %s min) (warn/crit at %s/%smin) (!)" % (str(data_age), dage_warn, dage_crit))
  239. # warnlevel = 1
  240. if display_value != "":
  241. output.append('%s: %s' % (title, display_value))
  242. pass
  243. except:
  244. pass
  245. # ##################################################################
  246. if errorlevel > 1:
  247. exitcode = 2
  248. elif warnlevel > 0:
  249. exitcode = 1
  250. else:
  251. exitcode = 0
  252. return exitcode, ', '.join(output), perfdata
  253. factory_settings["fhem_default_params"] = {
  254. "level_data_age" : (30, 90), # warn/crit for data age (min)
  255. "level_temperature_max" : (26, 30), # warn/crit for max. temperature
  256. "level_temperature_min" : (15, 12), # warn/crit for min. temperature
  257. "level_humidity_max" : (70, 80), # warn/crit for max. humidity
  258. "level_humidity_min" : (50, 45), # warn/crit for min. humidity
  259. "level_dewpoint_max" : (3, 1), # warn/crit for diff to temperatur (exp. 17°C (dewp) vs 20°C(temp))
  260. "level_batteryLevel_min" : (2.1, 2.3), # warn/crit for max. temperature
  261. "level_download_min" : (0, 0), # warn/crit for min download (speedtest)
  262. "level_upload_min" : (0, 0), # warn/crit for min upload (speedtest)
  263. "level_ping" : (100, 150), # warn/crit for ping (speedtest)
  264. "var_activity" : ("alive"), # default for alive
  265. "var_contact" : ("ignore"), # default for contact
  266. "var_dewpoint_override" : ("true"), # don't alert humidity if dewpoint given
  267. "var_btnLock" : ("ignore"), # simple button lock
  268. "var_globalBtnLock" : ("ignore"), # full button lock
  269. "var_modusBtnLock" : ("ignore"), # modus button lock
  270. }
  271. check_info['fhem'] = {
  272. "check_function" : check_fhem,
  273. "inventory_function" : inventory_fhem,
  274. "service_description" : "FHEM %s",
  275. "has_perfdata" : True,
  276. "group" : "fhem",
  277. "default_levels_variable" : "fhem_default_params",
  278. }