diff --git a/mkp/watterott_co2_ampel/mkp/info b/mkp/watterott_CO2_ampel/info similarity index 90% rename from mkp/watterott_co2_ampel/mkp/info rename to mkp/watterott_CO2_ampel/info index 1432065..12273a6 100644 --- a/mkp/watterott_co2_ampel/mkp/info +++ b/mkp/watterott_CO2_ampel/info @@ -27,10 +27,11 @@ 'possible, lower thresholds from 1000/1200 to 900/1000.\n', 'download_url': 'https://github.com/mschlenker/checkmk-snippets/', 'files': {'agent_based': ['co2welectronic.py'], - 'web': ['plugins/wato/co2welectronic_parameters.py']}, + 'web': ['plugins/wato/co2welectronic_parameters.py', + 'plugins/metrics/co2welectronic.py']}, 'name': 'watterott_CO2_ampel', 'title': 'Watterott CO2 Ampel', - 'version': '0.1.2', + 'version': '0.2', 'version.min_required': '2.1.0', - 'version.packaged': '2.1.0p12', - 'version.usable_until': '2.2.999'} \ No newline at end of file + 'version.packaged': '2.1.0p16', + 'version.usable_until': '2.2.999'} diff --git a/mkp/watterott_CO2_ampel/info.json b/mkp/watterott_CO2_ampel/info.json new file mode 100644 index 0000000..84b9e5e --- /dev/null +++ b/mkp/watterott_CO2_ampel/info.json @@ -0,0 +1 @@ +{"author": "Mattias Schlenker ", "description": "Background: The Watterott CO2 Ampel (\"CO2 traffic light\") is a networkble sensor board, primarily made for monitoring CO2, but includes other sensors as temperature and humidity as well. It is open hardware and uses an open source firmware. https://learn.watterott.com/breakouts/co2-ampel/ \n\nThe factory firmware includes a simple Checkmk agent via HTTP/REST-API: http://12.34.56.78/cmk-agent\n\nSince the agent is only available via HTTP, the monitoring has to be configured using \"individual program call instead of agent access\", see\n\nhttps://docs.checkmk.com/latest/en/datasource_programs.html\n\nThe output includes one local check that creates a service immediately after discovery. However this takes thresholds from the EPROM of the boards which makes central administration difficult. This plugin adds discovery for all other sensors. Since different versions of the board have differect sensors, individual discovery is needed.\n\nGerman users: See ASR3.5 and ASR3.6 (Germany) for thresholds on CO2/temperature/humidity in working environments. Only CO2 is quite fixed at 1000ppm. If no quick exchange of air is possible, lower thresholds from 1000/1200 to 900/1000.\n", "download_url": "https://github.com/mschlenker/checkmk-snippets/", "files": {"agent_based": ["co2welectronic.py"], "web": ["plugins/wato/co2welectronic_parameters.py", "plugins/metrics/co2welectronic.py"]}, "name": "watterott_CO2_ampel", "title": "Watterott CO2 Ampel", "version": "0.2", "version.min_required": "2.1.0", "version.packaged": "2.1.0p16", "version.usable_until": "2.2.999"} \ No newline at end of file diff --git a/mkp/watterott_CO2_ampel/lib/check_mk/base/plugins/agent_based/co2welectronic.py b/mkp/watterott_CO2_ampel/lib/check_mk/base/plugins/agent_based/co2welectronic.py new file mode 100644 index 0000000..1c3a932 --- /dev/null +++ b/mkp/watterott_CO2_ampel/lib/check_mk/base/plugins/agent_based/co2welectronic.py @@ -0,0 +1,177 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# © 2022 Mattias Schlenker for tribe29 GmbH +# +# Background: The Watterott CO2 Ampel ("CO2 traffic light") is a networkabale +# sensor board, primarily made for monitoring CO2. It is open hardware and uses +# an open source firmware. +# +# https://learn.watterott.com/breakouts/co2-ampel/ +# +# The factory firmware includes a simple Checkmk agent via HTTP/REST-API: +# +# http://12.34.56.78/cmk-agent +# +# <<>> +# AgentOS: arduino +# <<>> +# co2 521 +# temp 19.3 +# humidity 51.8 +# lighting 976 +# temp2 19.4 +# pressure 1022.0 +# <<>> +# P "CO2 level (ppm)" co2ppm=521;1000;1200 CO2/ventilation control with Watterott CO2-Ampel, thresholds taken from sensor board. +# +# Since the agent is only available via HTTP, the monitoring has to be configured +# using "individual program call instead of agent access", see +# +# https://docs.checkmk.com/latest/en/datasource_programs.html +# +# The result looks like: +# +# curl http://$_HOSTADDRESS_4$/cmk-agent +# +# The local check creates a service immediately after discovery. However this +# takes thresholds from the EPROM of the boards which makes central administration +# difficult. This plugin adds discovery for all other sensors. Since different +# versions of the board have differect sensors, individual discovery is needed. +# +# See ASR3.5 and ASR3.6 (Germany) for thresholds on CO2/temperature/humidity in +# working environments. Only CO2 is quite fixed at 1000ppm. If no quick exchange +# of air is possible, lower thresholds from 1000/1200 to 900/1000. + +from .agent_based_api.v1 import * +from .agent_based_api.v1.type_defs import CheckResult, DiscoveryResult, StringTable +from .utils.temperature import check_temperature +from .utils.humidity import check_humidity + +def parse_co2ampel(string_table: StringTable): + parsed = {} + + for key, value in string_table: + try: + parsed[key] = int(value) + except ValueError: + parsed[key] = float(value) + + if key == "pressure": + parsed[key] *= 100 + if key == "lighting": + parsed[key] = parsed[key] * 100.0 / 1024.0 + + return parsed + +register.agent_section( + name="watterott_co2ampel_plugin", + parse_function=parse_co2ampel, +) + +def discover_co2ampel_temp(section) -> DiscoveryResult: + for key in section: + if key.startswith("temp"): + yield Service(item=key) + +def check_co2ampel_temp(item, params, section) -> CheckResult: + if item in section: + yield from check_temperature( + reading = section[item], + params = params, + unique_name = item, + value_store = get_value_store(), + ) + +register.check_plugin( + name="watterott_co2ampel_temp", + sections=["watterott_co2ampel_plugin"], + service_name="Temperature %s", + discovery_function=discover_co2ampel_temp, + check_function=check_co2ampel_temp, + check_ruleset_name="temperature", + check_default_parameters={ + "levels" : (23.0, 26.0), + "levels_lower" : (17.0, 13.0), + }, +) + +def discover_co2ampel_humidity(section) -> DiscoveryResult: + if "humidity" in section: + yield Service(item="Humidity") + +def check_co2ampel_humidity(item, params, section) -> CheckResult: + if "humidity" in section: + yield from check_humidity(section["humidity"], params) + +register.check_plugin( + name="watterott_co2ampel_humidity", + sections=["watterott_co2ampel_plugin"], + service_name="%s", + discovery_function=discover_co2ampel_humidity, + check_function=check_co2ampel_humidity, + check_ruleset_name="humidity", + check_default_parameters={ + "levels" : (60.0, 65.0), + "levels_lower" : (35.0, 30.0), + }, +) + +def discover_co2ampel_co2(section) -> DiscoveryResult: + if "co2" in section: + yield Service() + +def check_co2ampel_co2(params, section) -> CheckResult: + if "co2" in section: + value = section["co2"] + levels = params["levels"] + yield Metric(name="co2ppm", + value=value, + levels=levels) + if value > levels[1]: + yield Result(state=State.CRIT, + summary=f"CO₂ level is too high at {value}ppm") + elif value > levels[0]: + yield Result(state=State.WARN, + summary=f"CO₂ level is slightly too high at {value}ppm") + else: + yield Result(state=State.OK, summary=f"CO₂ level is acceptable at {value}ppm") + +register.check_plugin( + name="watterott_co2ampel_co2", + sections=["watterott_co2ampel_plugin"], + service_name="CO₂ level", + discovery_function=discover_co2ampel_co2, + check_function=check_co2ampel_co2, + check_ruleset_name="watterott_co2ampel_plugin", + check_default_parameters={ + "levels" : (1000, 1200), + }, +) + +def discover_co2ampel_sensors(section) -> DiscoveryResult: + for key in section: + if key in ["humidity", "co2"]: + continue + if key.startswith("temp"): + continue + yield Service(item=key) + +def check_co2ampel_sensors(item, section) -> CheckResult: + if item in section: + value = section[item] + yield Metric(name=item, + value=value) + if item == "pressure": + value = "%0.2fhPa" % (value / 100.0) + if item == "lighting": + value = "%0.1f%%" % value + yield Result(state=State.OK, summary=f"Sensor info: {value}") + +register.check_plugin( + name="watterott_co2ampel_sensors", + sections=["watterott_co2ampel_plugin"], + service_name="Sensor %s", + discovery_function=discover_co2ampel_sensors, + check_function=check_co2ampel_sensors, +) diff --git a/mkp/watterott_CO2_ampel/watterott_CO2_ampel-0.2.mkp b/mkp/watterott_CO2_ampel/watterott_CO2_ampel-0.2.mkp new file mode 100644 index 0000000..3bbf149 Binary files /dev/null and b/mkp/watterott_CO2_ampel/watterott_CO2_ampel-0.2.mkp differ diff --git a/mkp/watterott_CO2_ampel/web/plugins/metrics/co2welectronic.py b/mkp/watterott_CO2_ampel/web/plugins/metrics/co2welectronic.py new file mode 100644 index 0000000..4c1ee52 --- /dev/null +++ b/mkp/watterott_CO2_ampel/web/plugins/metrics/co2welectronic.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +from cmk.gui.i18n import _ +from cmk.gui.plugins.metrics.utils import graph_info, indexed_color, metric_info + +metric_info["co2ppm"] = { + "title": _("CO₂ level"), + "unit": "ppm", + "color": "#60f088", +} + +metric_info["pressure"] = { + "title": _("Air Pressure"), + "unit": "pa", + "color": "11/a", +} + +metric_info["lighting"] = { + "title": _("Light"), + "unit": "%", + "color": "21/a", +} diff --git a/mkp/watterott_co2_ampel/mkp/_web/plugins/wato/co2welectronic_parameters.py b/mkp/watterott_CO2_ampel/web/plugins/wato/co2welectronic_parameters.py similarity index 51% rename from mkp/watterott_co2_ampel/mkp/_web/plugins/wato/co2welectronic_parameters.py rename to mkp/watterott_CO2_ampel/web/plugins/wato/co2welectronic_parameters.py index c7b5f22..3a1c108 100644 --- a/mkp/watterott_co2_ampel/mkp/_web/plugins/wato/co2welectronic_parameters.py +++ b/mkp/watterott_CO2_ampel/web/plugins/wato/co2welectronic_parameters.py @@ -33,8 +33,8 @@ def _parameter_valuespec_co2_levels(): return Dictionary( elements=[ - ("co2", Tuple( - title=_("CO2 levels"), + ("levels", Tuple( + title=_("CO₂ levels"), elements=[ Integer( title=_("Warning above"), @@ -46,58 +46,6 @@ def _parameter_valuespec_co2_levels(): ), ], )), - ("temp_upper", Tuple( - title=_("Temperature upper"), - elements=[ - Float( - title=_("Warning above"), - default_value=23.0, - ), - Float( - title=_("Critical above"), - default_value=26.0, - ), - ], - )), - ("temp_lower", Tuple( - title=_("Temperature lower"), - elements=[ - Float( - title=_("Warning below"), - default_value=17.0, - ), - Float( - title=_("Critical below"), - default_value=13.0, - ), - ], - )), - ("humidity_upper", Tuple( - title=_("Humidity upper"), - elements=[ - Percentage( - title=_("Warning above"), - default_value=60.0, - ), - Percentage( - title=_("Critical above"), - default_value=65.0, - ), - ], - )), - ("humidity_lower", Tuple( - title=_("Humidity lower"), - elements=[ - Percentage( - title=_("Warning below"), - default_value=35.0, - ), - Percentage( - title=_("Critical below"), - default_value=30.0, - ), - ], - )) ], ) diff --git a/mkp/watterott_co2_ampel/mkp/_agent_based/co2welectronic.py b/mkp/watterott_co2_ampel/mkp/_agent_based/co2welectronic.py deleted file mode 100644 index 0ed0638..0000000 --- a/mkp/watterott_co2_ampel/mkp/_agent_based/co2welectronic.py +++ /dev/null @@ -1,133 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -# -# © 2022 Mattias Schlenker for tribe29 GmbH -# -# Background: The Watterott CO2 Ampel ("CO2 traffic light") is a networkabale -# sensor board, primarily made for monitoring CO2. It is open hardware and uses -# an open source firmware. -# -# https://learn.watterott.com/breakouts/co2-ampel/ -# -# The factory firmware includes a simple Checkmk agent via HTTP/REST-API: -# -# http://12.34.56.78/cmk-agent -# -# <<>> -# AgentOS: arduino -# <<>> -# co2 521 -# temp 19.3 -# humidity 51.8 -# lighting 976 -# temp2 19.4 -# pressure 1022.0 -# <<>> -# P "CO2 level (ppm)" co2ppm=521;1000;1200 CO2/ventilation control with Watterott CO2-Ampel, thresholds taken from sensor board. -# -# Since the agent is only available via HTTP, the monitoring has to be configured -# using "individual program call instead of agent access", see -# -# https://docs.checkmk.com/latest/en/datasource_programs.html -# -# The result looks like: -# -# curl http://$_HOSTADDRESS_4$/cmk-agent -# -# The local check creates a service immediately after discovery. However this -# takes thresholds from the EPROM of the boards which makes central administration -# difficult. This plugin adds discovery for all other sensors. Since different -# versions of the board have differect sensors, individual discovery is needed. -# -# See ASR3.5 and ASR3.6 (Germany) for thresholds on CO2/temperature/humidity in -# working environments. Only CO2 is quite fixed at 1000ppm. If no quick exchange -# of air is possible, lower thresholds from 1000/1200 to 900/1000. - -from .agent_based_api.v1 import * - -def discover_co2_level(section): - for key, _value in section: - yield Service(item=key) - -def check_co2_level(item, params, section): - for key, value in section: - # The Sensirion CO2 sensor - if key == "co2" and key == item: - yield Metric(name="co2", value=int(value), boundaries=(0, 10000), levels=params["co2"]) - if int(value) > params["co2"][1]: - yield Result(state=State.CRIT, summary=f"CO2 level is too high at {value}ppm (threshold from plugin)") - return - elif int(value) > params["co2"][0]: - yield Result(state=State.WARN, summary=f"CO2 level is slightly too high at {value}ppm (threshold from plugin)") - return - yield Result(state=State.OK, summary=f"CO2 level is acceptable at {value}ppm (threshold from plugin)") - # Temperature senosr on the Sensirion - elif key == "temp" and key == item: - yield Metric(name="temp", value=float(value), boundaries=(-20.0, 80.0), levels=params["temp_upper"]) - if float(value) > params["temp_upper"][1]: - yield Result(state=State.CRIT, summary=f"Temperature is too high at {value}°C (threshold from plugin)") - return - elif float(value) > params["temp_upper"][0]: - yield Result(state=State.WARN, summary=f"Temperature is slightly too high at {value}°C (threshold from plugin)") - return - elif float(value) < params["temp_lower"][1]: - yield Result(state=State.CRIT, summary=f"Temperature is too low at {value}°C (threshold from plugin)") - return - elif float(value) < params["temp_lower"][0]: - yield Result(state=State.WARN, summary=f"Temperature is slightly too low at {value}°C (threshold from plugin)") - return - yield Result(state=State.OK, summary=f"Temperature is acceptable at {value}°C (threshold from plugin)") - # Boards with pressure sensors have a second temperature sensor as part of the pressure sensor - elif key == "temp2" and key == item: - yield Metric(name="temp_2", value=float(value), boundaries=(-20.0, 80.0), levels=params["temp_upper"]) - if float(value) > params["temp_upper"][1]: - yield Result(state=State.CRIT, summary=f"Temperature (sensor 2) is too high at {value}°C (threshold from plugin)") - return - elif float(value) > params["temp_upper"][0]: - yield Result(state=State.WARN, summary=f"Temperature (sensor 2) is slightly too high at {value}°C (threshold from plugin)") - return - elif float(value) < params["temp_lower"][1]: - yield Result(state=State.CRIT, summary=f"Temperature (sensor 2) is too low at {value}°C (threshold from plugin)") - return - elif float(value) < params["temp_lower"][0]: - yield Result(state=State.WARN, summary=f"Temperature (sensor 2) is slightly too low at {value}°C (threshold from plugin)") - return - yield Result(state=State.OK, summary=f"Temperature (sensor 2) is acceptable at {value}°C (threshold from plugin)") - # The humidity sensor - elif key == "humidity" and key == item: - yield Metric(name="humidity", value=float(value), levels=params["humidity_upper"]) - if float(value) > params["humidity_upper"][1]: - yield Result(state=State.CRIT, summary="Humidity is too humid at " + value + "% (threshold from plugin)") - return - elif float(value) > params["humidity_upper"][0]: - yield Result(state=State.WARN, summary="Humidity is slightly too humid at " + value + "% (threshold from plugin)") - return - elif float(value) < params["humidity_lower"][1]: - yield Result(state=State.CRIT, summary="Humidity is too dry at " + value + "% (threshold from plugin)") - return - elif float(value) < params["humidity_lower"][0]: - yield Result(state=State.CRIT, summary="Humidity is slightly too dry at " + value + "% (threshold from plugin)") - return - yield Result(state=State.OK, summary="Humidity is acceptable at " + value + "°C (threshold from plugin)") - # For ambient lighting and pressure (if available) we just create services that are always OK - elif key == item: - yield Metric(name=key, value=float(value)) - yield Result(state=State.OK, summary="Sensor " + key + " value " + value + " for informational purpose only, always OK") - -register.check_plugin( - name = "watterott_co2ampel_plugin", - service_name = "CO2 board %s", - discovery_function = discover_co2_level, - check_function = check_co2_level, - check_ruleset_name = "watterott_co2ampel_plugin", - # Define some thresholds, the CO2 values are taken according German Arbeitsstättenrichtlinie ASR3.6 - # Change temperatures for the respective work environmen, see ASR A3.5 - check_default_parameters = { - "co2" : (1000, 1200), - "temp_upper" : (23.0, 26.0), - "temp_lower" : (17.0, 13.0), - "humidity_upper" : (60.0, 65.0), - "humidity_lower" : (35.0, 30.0) - } -) - diff --git a/mkp/watterott_co2_ampel/mkp/info.json b/mkp/watterott_co2_ampel/mkp/info.json deleted file mode 100644 index d79c992..0000000 --- a/mkp/watterott_co2_ampel/mkp/info.json +++ /dev/null @@ -1 +0,0 @@ -{"author": "Mattias Schlenker ", "description": "Background: The Watterott CO2 Ampel (\"CO2 traffic light\") is a networkble sensor board, primarily made for monitoring CO2, but includes other sensors as temperature and humidity as well. It is open hardware and uses an open source firmware. https:\/\/learn.watterott.com\/breakouts\/co2-ampel\/ \n\nThe factory firmware includes a simple Checkmk agent via HTTP\/REST-API: http:\/\/12.34.56.78\/cmk-agent\n\nSince the agent is only available via HTTP, the monitoring has to be configured using \"individual program call instead of agent access\", see\n\nhttps:\/\/docs.checkmk.com\/latest\/en\/datasource_programs.html\n\nThe output includes one local check that creates a service immediately after discovery. However this takes thresholds from the EPROM of the boards which makes central administration difficult. This plugin adds discovery for all other sensors. Since different versions of the board have differect sensors, individual discovery is needed.\n\nGerman users: See ASR3.5 and ASR3.6 (Germany) for thresholds on CO2\/temperature\/humidity in working environments. Only CO2 is quite fixed at 1000ppm. If no quick exchange of air is possible, lower thresholds from 1000\/1200 to 900\/1000.\n", "download_url": "https:\/\/github.com\/mschlenker\/checkmk-snippets\/", "files": {"agent_based": ["co2welectronic.py"], "web": ["plugins\/wato\/co2welectronic_parameters.py"]}, "name": "watterott_CO2_ampel", "title": "Watterott CO2 Ampel", "version": "0.1.2", "version.min_required": "2.1.0", "version.packaged": "2.1.0p12", "version.usable_until": "2.2.999"} \ No newline at end of file