p1.py 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397
  1. #!/usr/bin/env python3
  2. import itertools
  3. import json
  4. from cmk.agent_based.v2 import (
  5. AgentSection,
  6. CheckPlugin,
  7. Metric,
  8. Result,
  9. Service,
  10. State,
  11. check_levels,
  12. render,
  13. )
  14. # ---------------------------------------------------------------------------
  15. # JSON parsing
  16. # ---------------------------------------------------------------------------
  17. def parse_p1(string_table):
  18. flat = list(itertools.chain.from_iterable(string_table))
  19. raw = "".join(flat)
  20. try:
  21. data = json.loads(raw)
  22. except json.JSONDecodeError:
  23. return None
  24. if not isinstance(data, dict):
  25. return None
  26. return data
  27. agent_section_p1 = AgentSection(
  28. name="p1",
  29. parse_function=parse_p1,
  30. )
  31. # ---------------------------------------------------------------------------
  32. # P1 tariff
  33. # ---------------------------------------------------------------------------
  34. def discover_p1_tariff(section):
  35. if section is not None and "active_tariff" in section:
  36. yield Service()
  37. def check_p1_tariff(section):
  38. tariff = section.get("active_tariff")
  39. if tariff is None:
  40. yield Result(
  41. state=State.UNKNOWN,
  42. summary="Active tariff is not available",
  43. )
  44. return
  45. yield Result(
  46. state=State.OK,
  47. summary=f"Tariff: {tariff}",
  48. )
  49. check_plugin_p1_tariff = CheckPlugin(
  50. name="p1_tariff",
  51. sections=["p1"],
  52. service_name="P1 Tariff",
  53. discovery_function=discover_p1_tariff,
  54. check_function=check_p1_tariff,
  55. )
  56. # ---------------------------------------------------------------------------
  57. # Power
  58. # ---------------------------------------------------------------------------
  59. POWER_ITEMS = {
  60. "Total": "active_power_w",
  61. "L1": "active_power_l1_w",
  62. "L2": "active_power_l2_w",
  63. "L3": "active_power_l3_w",
  64. }
  65. def discover_p1_power(section):
  66. for item, key in POWER_ITEMS.items():
  67. if key in section:
  68. yield Service(item=item)
  69. def check_p1_power(item, section):
  70. key = POWER_ITEMS.get(item)
  71. if key is None or key not in section:
  72. yield Result(
  73. state=State.UNKNOWN,
  74. summary="Power value is not available",
  75. )
  76. return
  77. value = float(section[key])
  78. yield Result(
  79. state=State.OK,
  80. summary=f"{value:.0f} W",
  81. )
  82. yield Metric(
  83. name="p1_power_w",
  84. value=value,
  85. boundaries=(0, None),
  86. )
  87. check_plugin_p1_power = CheckPlugin(
  88. name="p1_power",
  89. sections=["p1"],
  90. service_name="P1 Power %s",
  91. discovery_function=discover_p1_power,
  92. check_function=check_p1_power,
  93. )
  94. # ---------------------------------------------------------------------------
  95. # Voltage
  96. # ---------------------------------------------------------------------------
  97. VOLTAGE_ITEMS = {
  98. "L1": "active_voltage_l1_v",
  99. "L2": "active_voltage_l2_v",
  100. "L3": "active_voltage_l3_v",
  101. }
  102. def discover_p1_voltage(section):
  103. for item, key in VOLTAGE_ITEMS.items():
  104. if key in section:
  105. yield Service(item=item)
  106. def check_p1_voltage(item, params, section):
  107. key = VOLTAGE_ITEMS.get(item)
  108. if key is None or key not in section:
  109. yield Result(
  110. state=State.UNKNOWN,
  111. summary="Voltage value is not available",
  112. )
  113. return
  114. value = float(section[key])
  115. yield from check_levels(
  116. value,
  117. levels_upper=params["levels"],
  118. metric_name="p1_voltage_v",
  119. label="Voltage",
  120. render_func=lambda v: f"{v:.1f} V",
  121. boundaries=(0, 300),
  122. )
  123. check_plugin_p1_voltage = CheckPlugin(
  124. name="p1_voltage",
  125. sections=["p1"],
  126. service_name="P1 Voltage %s",
  127. discovery_function=discover_p1_voltage,
  128. check_function=check_p1_voltage,
  129. check_default_parameters={
  130. "levels": ("fixed", (252.0, 253.0)),
  131. },
  132. check_ruleset_name="p1_voltage",
  133. )
  134. # ---------------------------------------------------------------------------
  135. # Current
  136. # ---------------------------------------------------------------------------
  137. CURRENT_ITEMS = {
  138. "Total": "active_current_a",
  139. "L1": "active_current_l1_a",
  140. "L2": "active_current_l2_a",
  141. "L3": "active_current_l3_a",
  142. }
  143. def discover_p1_current(section):
  144. for item, key in CURRENT_ITEMS.items():
  145. if key in section:
  146. yield Service(item=item)
  147. def check_p1_current(item, section):
  148. key = CURRENT_ITEMS.get(item)
  149. if key is None or key not in section:
  150. yield Result(
  151. state=State.UNKNOWN,
  152. summary="Current value is not available",
  153. )
  154. return
  155. value = float(section[key])
  156. yield Result(
  157. state=State.OK,
  158. summary=f"{value:.3f} A",
  159. )
  160. yield Metric(
  161. name="p1_current_a",
  162. value=value,
  163. boundaries=(0, None),
  164. )
  165. check_plugin_p1_current = CheckPlugin(
  166. name="p1_current",
  167. sections=["p1"],
  168. service_name="P1 Current %s",
  169. discovery_function=discover_p1_current,
  170. check_function=check_p1_current,
  171. )
  172. # ---------------------------------------------------------------------------
  173. # Energy import / export
  174. # ---------------------------------------------------------------------------
  175. ENERGY_ITEMS = {
  176. "Import": {
  177. "total": "total_power_import_kwh",
  178. "t1": "total_power_import_t1_kwh",
  179. "t2": "total_power_import_t2_kwh",
  180. },
  181. "Export": {
  182. "total": "total_power_export_kwh",
  183. "t1": "total_power_export_t1_kwh",
  184. "t2": "total_power_export_t2_kwh",
  185. },
  186. }
  187. def discover_p1_energy(section):
  188. for item, keys in ENERGY_ITEMS.items():
  189. if any(key in section for key in keys.values()):
  190. yield Service(item=item)
  191. def check_p1_energy(item, section):
  192. keys = ENERGY_ITEMS.get(item)
  193. if keys is None:
  194. yield Result(
  195. state=State.UNKNOWN,
  196. summary="Unknown energy type",
  197. )
  198. return
  199. found = False
  200. for metric_name, key in (
  201. ("p1_energy_total_kwh", keys["total"]),
  202. ("p1_energy_t1_kwh", keys["t1"]),
  203. ("p1_energy_t2_kwh", keys["t2"]),
  204. ):
  205. if key not in section:
  206. continue
  207. found = True
  208. yield Metric(
  209. name=metric_name,
  210. value=float(section[key]),
  211. boundaries=(0, None),
  212. )
  213. if not found:
  214. yield Result(
  215. state=State.UNKNOWN,
  216. summary=f"{item} energy values are not available",
  217. )
  218. return
  219. yield Result(
  220. state=State.OK,
  221. summary=f"{item} energy data available",
  222. )
  223. check_plugin_p1_energy = CheckPlugin(
  224. name="p1_energy",
  225. sections=["p1"],
  226. service_name="P1 Energy %s",
  227. discovery_function=discover_p1_energy,
  228. check_function=check_p1_energy,
  229. )
  230. # ---------------------------------------------------------------------------
  231. # Gas
  232. # ---------------------------------------------------------------------------
  233. def discover_p1_gas(section):
  234. if "total_gas_m3" in section:
  235. yield Service()
  236. def check_p1_gas(section):
  237. value = float(section["total_gas_m3"])
  238. yield Result(
  239. state=State.OK,
  240. summary=f"{value:.2f} m³",
  241. )
  242. yield Metric(
  243. name="p1_gas_m3",
  244. value=value,
  245. boundaries=(0, None),
  246. )
  247. check_plugin_p1_gas = CheckPlugin(
  248. name="p1_gas",
  249. sections=["p1"],
  250. service_name="P1 Gas",
  251. discovery_function=discover_p1_gas,
  252. check_function=check_p1_gas,
  253. )
  254. # ---------------------------------------------------------------------------
  255. # Power quality
  256. # ---------------------------------------------------------------------------
  257. POWER_QUALITY_KEYS = {
  258. "Voltage sag L1": "voltage_sag_l1_count",
  259. "Voltage sag L2": "voltage_sag_l2_count",
  260. "Voltage sag L3": "voltage_sag_l3_count",
  261. "Voltage swell L1": "voltage_swell_l1_count",
  262. "Voltage swell L2": "voltage_swell_l2_count",
  263. "Voltage swell L3": "voltage_swell_l3_count",
  264. "Any power failure": "any_power_fail_count",
  265. "Long power failure": "long_power_fail_count",
  266. }
  267. def discover_p1_power_quality(section):
  268. if any(key in section for key in POWER_QUALITY_KEYS.values()):
  269. yield Service()
  270. def check_p1_power_quality(section):
  271. found = 0
  272. for metric_name, key in POWER_QUALITY_KEYS.items():
  273. if key not in section:
  274. continue
  275. found += 1
  276. metric_id = (
  277. metric_name.lower()
  278. .replace(" ", "_")
  279. )
  280. yield Metric(
  281. name=f"p1_{metric_id}",
  282. value=float(section[key]),
  283. boundaries=(0, None),
  284. )
  285. if found:
  286. yield Result(
  287. state=State.OK,
  288. summary=f"{found} power-quality counters available",
  289. )
  290. else:
  291. yield Result(
  292. state=State.UNKNOWN,
  293. summary="No power-quality counters available",
  294. )
  295. check_plugin_p1_power_quality = CheckPlugin(
  296. name="p1_power_quality",
  297. sections=["p1"],
  298. service_name="P1 Power Quality",
  299. discovery_function=discover_p1_power_quality,
  300. check_function=check_p1_power_quality,
  301. )