Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Watterott CO₂ Ampel rewrite #3

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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'}
'version.packaged': '2.1.0p16',
'version.usable_until': '2.2.999'}
1 change: 1 addition & 0 deletions mkp/watterott_CO2_ampel/info.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"author": "Mattias Schlenker <[email protected]>", "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"}
Original file line number Diff line number Diff line change
@@ -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
#
# <<<check_mk>>>
# AgentOS: arduino
# <<<watterott_co2ampel_plugin>>>
# co2 521
# temp 19.3
# humidity 51.8
# lighting 976
# temp2 19.4
# pressure 1022.0
# <<<local:sep(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,
)
Binary file not shown.
23 changes: 23 additions & 0 deletions mkp/watterott_CO2_ampel/web/plugins/metrics/co2welectronic.py
Original file line number Diff line number Diff line change
@@ -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",
}
Original file line number Diff line number Diff line change
Expand Up @@ -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"),
Expand All @@ -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,
),
],
))
],
)

Expand Down
Loading