2 # -*- encoding: utf-8; py-indent-offset: 4 -*-
3 # +------------------------------------------------------------------+
4 # | ____ _ _ __ __ _ __ |
5 # | / ___| |__ ___ ___| | __ | \/ | |/ / |
6 # | | | | '_ \ / _ \/ __| |/ / | |\/| | ' / |
7 # | | |___| | | | __/ (__| < | | | | . \ |
8 # | \____|_| |_|\___|\___|_|\_\___|_| |_|_|\_\ |
10 # | Copyright Mathias Kettner 2017 mk@mathias-kettner.de |
11 # +------------------------------------------------------------------+
13 # This file is part of Check_MK.
14 # The official homepage is at http://mathias-kettner.de/check_mk.
16 # check_mk is free software; you can redistribute it and/or modify it
17 # under the terms of the GNU General Public License as published by
18 # the Free Software Foundation in version 2. check_mk is distributed
19 # in the hope that it will be useful, but WITHOUT ANY WARRANTY; with-
20 # out even the implied warranty of MERCHANTABILITY or FITNESS FOR A
21 # PARTICULAR PURPOSE. See the GNU General Public License for more de-
22 # tails. You should have received a copy of the GNU General Public
23 # License along with GNU Make; see the file COPYING. If not, write
24 # to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
25 # Boston, MA 02110-1301 USA.
28 def parse_emka_modules(info
):
29 map_module_types
= { # basModuleCoIx == 0
32 "9" : "U9, card module (proximity)",
33 "10" : "U10, phone module (modem)",
34 "11" : "U11/U32, up to 8 handles / single point latches",
35 "12" : "U12/U33, up to 2 handles / single point latches",
36 "13" : "U13, 4 sensors and 4 relays",
37 "14" : "U14, communication module",
38 "15" : "fultifunction module M15",
39 "16" : "fultifunction module M16", }
41 map_component_types
= { # independent of module type, basModuleCoIx > 0
47 "6" : "card_terminal",
49 "8" : "analogous_output", }
51 parsed
= {"basic_components": {}}
52 for oidend
, status
, ty
, mod_info
, remark
in info
[0]:
53 mo_index
, co_index
= oidend
.split(".")
55 itemname
= "Master %s" % mod_info
.split(",")[0]
57 itemname
= "Perip %s %s" % (mo_index
, mod_info
)
60 parsed
["basic_components"].setdefault(
62 "type": map_module_types
[co_index
],
64 "_location_": "0.%s" % mo_index
,
68 table
= map_component_types
[ty
]
72 itemname
= "%s %s" % (remark
, oidend
)
74 parsed
.setdefault(table
, {})
75 parsed
[table
].setdefault(itemname
, {"_location_": oidend
})
77 for oidend
, module_link
, value
, mode
in info
[1]:
78 table
= map_component_types
[oidend
.split(".", 1)[0]]
79 location
= ".".join(module_link
.split(".")[-2:])
80 for entry
, attrs
in parsed
.get(table
, {}).items():
81 item_location
= attrs
["_location_"]
82 if item_location
!= location
:
85 attrs
["value"] = value
89 for oidend
, threshold
in info
[2]:
90 location
, threshold_ty
= oidend
.split(".")
91 if threshold_ty
== "1":
96 for entry
, attrs
in parsed
.get("sensor", {}).items():
97 if attrs
["_location_"].startswith("%s." % location
):
98 attrs
[ty
] = (threshold
, threshold
)
100 # Explanation from ELM2-MIB:
101 # Empty string -> default, that means value in mV.
102 # Universal: {[factor]}[unit]=$mV*[multiplicator]/[divisor]+[offset]
103 # [multiplicator], [divisor], [offset] must be integers
104 # Example: {0.1}%=$mV*20/100-100
105 # default: {1}mV=$mV*1/1+0"
106 # From the walk we get an ascii coded list separated by null bytes.
107 # Results in: "=#\xb0C0.02-30.0" where
108 # \xb0C => Temperature
109 # 0.02 => 2/100 [multiplicator]/[divisor]
111 # Notice, may also "=#\xb0C0.0230.0"
112 for oidend
, equation_bin
in info
[3]:
115 for entry
in equation_bin
:
119 equation
.append("".join(map(chr, part
)))
125 if equation
[0].endswith("#\xb0C"):
126 sensor_ty
= "sensor_temp"
127 elif equation
[0].endswith("#%RF"):
128 sensor_ty
= "sensor_humid"
130 sensor_ty
= "sensor_volt"
132 equation
= equation
[1:]
133 if len(equation
) == 2:
134 m
, a
= map(float, equation
)
138 def scale_f(x
, m
=m
, a
=a
):
139 return float(x
) * m
+ a
141 location
= str(chr(oidend
[0]))
142 for sensor
, attrs
in parsed
.get("sensor", {}).items():
143 if attrs
["_location_"].endswith(".%s" % location
):
144 parsed
.setdefault(sensor_ty
, {})
145 parsed
[sensor_ty
].setdefault(
147 "value": scale_f(attrs
["value"]),
148 "levels": map(scale_f
, attrs
["levels"]),
149 "levels_lower": map(scale_f
, attrs
["levels_lower"]),
154 if "sensor" in parsed
:
160 # .--component-----------------------------------------------------------.
162 # | ___ ___ _ __ ___ _ __ ___ _ __ ___ _ __ | |_ |
163 # | / __/ _ \| '_ ` _ \| '_ \ / _ \| '_ \ / _ \ '_ \| __| |
164 # | | (_| (_) | | | | | | |_) | (_) | | | | __/ | | | |_ |
165 # | \___\___/|_| |_| |_| .__/ \___/|_| |_|\___|_| |_|\__| |
167 # +----------------------------------------------------------------------+
169 # '----------------------------------------------------------------------'
172 def inventory_emka_modules(parsed
):
173 for entry
, attrs
in parsed
["basic_components"].items():
174 if attrs
["activation"] != "i":
178 def check_emka_modules(item
, params
, parsed
):
179 map_activation_states
= {
181 '?': (0, "detect modus"),
182 'x': (0, "excluded"),
184 'c': (2, "collision detected"),
185 'w': (1, "wait for dynamic address"),
187 'i': (0, "inactive"),
189 'T': (2, "timeout alarm"),
190 'A': (2, "alarm active"),
191 'L': (0, "alarm latched"),
195 if item
in parsed
["basic_components"]:
196 attrs
= parsed
["basic_components"][item
]
197 state
, state_readable
= map_activation_states
[attrs
["activation"]]
198 return state
, 'Activation status: %s, Type: %s' % \
199 (state_readable
, attrs
["type"])
202 check_info
['emka_modules'] = {
203 'parse_function' : parse_emka_modules
,
204 'inventory_function' : inventory_emka_modules
,
205 'check_function' : check_emka_modules
,
206 'service_description' : 'Module %s',
207 'snmp_info' : [('.1.3.6.1.4.1.13595.2.1.3.3.1', [
209 "3", # ELM2-MIB::basModuleStatus
210 "4", # ELM2-MIB::basModuleType
211 "5", # ELM2-MIB::basModuleInfo
212 "7", # ELM2-MIB::basModuleRemark
214 # In this order: alarm, handle, sensor, relay
215 ('.1.3.6.1.4.1.13595.2.2', [ '1', '2', '3', '4' ], [
217 "1.3", # ELM2-MIB::coHandleModuleLink
218 "1.4", # ELM2-MIB::co*[Status/Value]
219 "1.15", # ELM2-MIB::coSensorMode
221 ('.1.3.6.1.4.1.13595.2.2.3.1', [
223 "7", # ELM2-MIB::coSensorThreshold
225 ('.1.3.6.1.4.1.13595.2.2.3.1', [
227 BINARY("18"), # ELM2-MIB::coSensorScaling
229 'snmp_scan_function' : lambda oid
: "emka" in oid(".1.3.6.1.2.1.1.1.0").lower() and \
230 oid(".1.3.6.1.2.1.1.2.0").startswith(".1.3.6.1.4.1.13595.1"),
234 # .--alarm---------------------------------------------------------------.
236 # | __ _| | __ _ _ __ _ __ ___ |
237 # | / _` | |/ _` | '__| '_ ` _ \ |
238 # | | (_| | | (_| | | | | | | | | |
239 # | \__,_|_|\__,_|_| |_| |_| |_| |
241 # '----------------------------------------------------------------------'
244 def inventory_emka_modules_alarm(parsed
):
245 for entry
, attrs
in parsed
.get("alarm", {}).items():
246 if attrs
["value"] != "2":
250 def check_emka_modules_alarm(item
, params
, parsed
):
253 "2": (0, "inactive"),
258 if item
in parsed
.get("alarm", {}):
259 state
, state_readable
= map_states
[parsed
["alarm"][item
]["value"]]
260 return state
, "Status: %s" % state_readable
263 check_info
["emka_modules.alarm"] = {
264 "inventory_function": inventory_emka_modules_alarm
,
265 "check_function": check_emka_modules_alarm
,
266 "service_description": "Alarm %s",
270 # .--handle--------------------------------------------------------------.
272 # | | |__ __ _ _ __ __| | | ___ |
273 # | | '_ \ / _` | '_ \ / _` | |/ _ \ |
274 # | | | | | (_| | | | | (_| | | __/ |
275 # | |_| |_|\__,_|_| |_|\__,_|_|\___| |
277 # '----------------------------------------------------------------------'
280 def inventory_emka_modules_handle(parsed
):
281 for entry
in parsed
.get("handle", {}):
285 def check_emka_modules_handle(item
, params
, parsed
):
289 "3": (3, "unlocked"),
291 "5": (2, "open time ex")
294 if item
in parsed
.get("handle", {}):
295 state
, state_readable
= map_states
[parsed
["handle"][item
]["value"]]
296 return state
, "Status: %s" % state_readable
299 check_info
["emka_modules.handle"] = {
300 "inventory_function": inventory_emka_modules_handle
,
301 "check_function": check_emka_modules_handle
,
302 "service_description": "Handle %s",
306 # .--voltage-------------------------------------------------------------.
308 # | __ _____ | | |_ __ _ __ _ ___ |
309 # | \ \ / / _ \| | __/ _` |/ _` |/ _ \ |
310 # | \ V / (_) | | || (_| | (_| | __/ |
311 # | \_/ \___/|_|\__\__,_|\__, |\___| |
313 # '----------------------------------------------------------------------'
316 def inventory_emka_modules_sensor_volt(parsed
):
317 for entry
in parsed
.get("sensor_volt", {}):
321 def check_emka_modules_sensor_volt(item
, params
, parsed
):
322 if item
in parsed
.get("sensor_volt", {}):
323 attrs
= parsed
["sensor_volt"][item
]
324 value
= attrs
["value"] / 1000
325 return check_elphase(item
, params
, {item
: {"voltage": value
}})
328 check_info
["emka_modules.sensor_volt"] = {
329 "inventory_function": inventory_emka_modules_sensor_volt
,
330 "check_function": check_emka_modules_sensor_volt
,
331 "service_description": "Phase %s",
332 "includes": ["elphase.include"],
333 "group": "el_inphase",
334 "has_perfdata": True,
338 # .--temperature---------------------------------------------------------.
340 # | | |_ ___ _ __ ___ _ __ ___ _ __ __ _| |_ _ _ _ __ ___ |
341 # | | __/ _ \ '_ ` _ \| '_ \ / _ \ '__/ _` | __| | | | '__/ _ \ |
342 # | | || __/ | | | | | |_) | __/ | | (_| | |_| |_| | | | __/ |
343 # | \__\___|_| |_| |_| .__/ \___|_| \__,_|\__|\__,_|_| \___| |
345 # '----------------------------------------------------------------------'
348 def inventory_emka_modules_sensor_temp(parsed
):
349 for entry
in parsed
.get("sensor_temp", {}):
353 def check_emka_modules_sensor_temp(item
, params
, parsed
):
354 if item
in parsed
.get("sensor_temp", {}):
355 attrs
= parsed
["sensor_temp"][item
]
356 value
= attrs
["value"]
357 return check_temperature(
360 "emka_modules_sensor_temp.%s" % item
,
361 dev_levels
=attrs
["levels"],
362 dev_levels_lower
=attrs
["levels_lower"])
365 check_info
["emka_modules.sensor_temp"] = {
366 "inventory_function": inventory_emka_modules_sensor_temp
,
367 "check_function": check_emka_modules_sensor_temp
,
368 "service_description": "Temperature %s",
369 "includes": ["temperature.include"],
370 "group": "temperature",
371 "has_perfdata": True,
375 # .--humidity------------------------------------------------------------.
377 # | | |__ _ _ _ __ ___ (_) __| (_) |_ _ _ |
378 # | | '_ \| | | | '_ ` _ \| |/ _` | | __| | | | |
379 # | | | | | |_| | | | | | | | (_| | | |_| |_| | |
380 # | |_| |_|\__,_|_| |_| |_|_|\__,_|_|\__|\__, | |
382 # '----------------------------------------------------------------------'
385 def inventory_emka_modules_sensor_humid(parsed
):
386 for entry
in parsed
.get("sensor_humid", {}):
390 def check_emka_modules_sensor_humid(item
, params
, parsed
):
391 if item
in parsed
.get("sensor_humid", {}):
392 attrs
= parsed
["sensor_humid"][item
]
393 value
= attrs
["value"]
394 return check_humidity(value
, params
)
397 check_info
["emka_modules.sensor_humid"] = {
398 "inventory_function": inventory_emka_modules_sensor_humid
,
399 "check_function": check_emka_modules_sensor_humid
,
400 "service_description": "Humidity %s",
401 "includes": ["humidity.include"],
403 "has_perfdata": True,
407 # .--relay---------------------------------------------------------------.
409 # | _ __ ___| | __ _ _ _ |
410 # | | '__/ _ \ |/ _` | | | | |
411 # | | | | __/ | (_| | |_| | |
412 # | |_| \___|_|\__,_|\__, | |
414 # '----------------------------------------------------------------------'
417 def inventory_emka_modules_relay(parsed
):
418 for entry
, attrs
in parsed
.get("relay", {}).items():
419 if attrs
["value"] != "1":
423 def check_emka_modules_relay(item
, params
, parsed
):
429 if item
in parsed
.get("relay", {}):
430 state
, state_readable
= map_states
[parsed
["relay"][item
]["value"]]
431 return state
, "Status: %s" % state_readable
434 check_info
["emka_modules.relay"] = {
435 "inventory_function": inventory_emka_modules_relay
,
436 "check_function": check_emka_modules_relay
,
437 "service_description": "Relay %s",