Collection of CheckMK checks (see https://checkmk.com/). All checks and plugins are provided as is. Absolutely no warranty. Send any comments to thl-cmk[at]outlook[dot]com

Skip to content
Snippets Groups Projects
Commit 7a1787db authored by thl-cmk's avatar thl-cmk :flag_na:
Browse files

update project

parent 6b68c144
No related branches found
No related tags found
No related merge requests found
[PACKAGE]: ../../raw/master/packagee-0.1.2-20230706.mkp "package-0.1.2-20230706.mkp"
[PACKAGE]: ../../raw/master/mkp/fritzbox_smarthome-0.7.0-20231228.mkp "fritzbox_smarthome-0.7.0-20231228.mkp"
# Title
A short description about the plugin
......
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# License: GNU General Public License v2
#
# Author: thl-cmk[at]outlook[dot]com
# URL : https://thl-cmk.hopto.org
# Date : 2023-12-28
# File : fritzbox_smarthome.py (check plugin)
#
# Based on the work of Maximilian Clemens, see https://github.com/MaximilianClemens/checkmk_fritzbox
#
#
import json
from dataclasses import dataclass
from typing import Dict, List
from time import strftime, gmtime
from time import time as time_now
from cmk.base.plugins.agent_based.agent_based_api.v1 import (
Metric,
Result,
Service,
State,
TableRow,
check_levels,
get_rate,
GetRateError,
get_value_store,
register,
)
from cmk.base.plugins.agent_based.agent_based_api.v1.type_defs import (
CheckResult,
DiscoveryResult,
InventoryResult,
StringTable,
)
from cmk.base.plugins.agent_based.utils.temperature import check_temperature
from cmk.utils.render import physical_precision
@dataclass(frozen=True)
class AvmTemperature:
celsius: float
offset: float
@dataclass(frozen=True)
class AvmPowerMeter:
voltage: float
power: float
energy: float
@dataclass(frozen=True)
class AvmSimpleOnOff:
state: int
@dataclass(frozen=True)
class AvmNextChange:
end_period: int
temp_change_to: float
@dataclass(frozen=True)
class AvmHkr:
temp_current: float
temp_target: float
temp_economic: float
temp_comfort: float
lock: int
device_lock: int
error_code: int
battery_low: int
next_change: AvmNextChange | None = None
# summer_active: int | None = None
# window_open_activ: int | None = None
# battery: float | None = None
@dataclass(frozen=True)
class AvmSwitch:
state: int
mode: str
lock: int
device_lock: int
@dataclass(frozen=True)
class AvmSmartHomeDevice:
functions: List[str]
identifier: str
name: str
id: str
fbm: int
fw_version: str
manufacturer: str
product_name: str
present: int
tx_busy: int | None = None
temperature: AvmTemperature | None = None
hkr: AvmHkr | None = None
switch: AvmSwitch | None = None
power_meter: AvmPowerMeter | None = None
simple_on_off: AvmSimpleOnOff | None = None
_TIME_FORMAT = '%Y-%m-%dT%H:%M:%S'
_AVM_HKR = 'hkr'
_AVM_SWITCH = 'switch'
_AVM_POWER_METER = 'powermeter'
_AVM_TEMPERATURE = 'temperature'
_AVM_SIMPLE_ON_OFF = 'simpleonoff'
_AVM_NEXT_CHANGE = 'nextchange'
__thermostat = [
{
"identifier": "13979 0878454",
"id": "16",
"functionbitmask": "320",
"fwversion": "05.16",
"manufacturer": "AVM",
"productname": "Comet DECT",
"present": "1",
"name": "Temp01",
"temperature": {
"celsius": "210",
"offset": "-10"
},
"hkr": {
"tist": "42",
"tsoll": "32",
"absenk": "32",
"komfort": "38",
"lock": "1",
"devicelock": "1",
"errorcode": "0",
"batterylow": "0",
"nextchange": {
"endperiod": "1704888000",
"tchange": "32"
}
}
}
]
__switch = [
{
"identifier": "08761 0116372",
"id": "17",
"functionbitmask": "35712",
"fwversion": "04.26",
"manufacturer": "AVM",
"productname": "FRITZ!DECT 200",
"present": "1",
"txbusy": "0",
"name": "TV-Wohnzimmer",
"switch": {
"state": "1",
"mode": "manuell",
"lock": "0",
"devicelock": "0"
},
"simpleonoff": {
"state": "1"
},
"powermeter": {
"voltage": "235814",
"power": "4220",
"energy": "145427"
},
"temperature": {
"celsius": "190",
"offset": "0"
}
}
]
def _get_avm_device_functions_from_fbm(fbm: int) -> List[str]:
functions = []
if fbm >> 0 & 1:
functions.append('HAN-FUN Device')
if fbm >> 13 & 1:
functions.append('HAN-FUN Unit')
if fbm >> 4 & 1:
functions.append('Alarm Sensor')
if fbm >> 5 & 1:
functions.append('Button')
if fbm >> 6 & 1:
functions.append('Thermostat')
if fbm >> 9 & 1:
functions.append('Switch')
if fbm >> 7 & 1:
functions.append('Powermeter')
if fbm >> 10 & 1:
functions.append('DECT Repeater')
if fbm >> 8 & 1:
functions.append('Temperature Sensor')
if fbm >> 11 & 1:
functions.append('Microphone')
if fbm >> 17 & 1:
functions.append('Light')
if fbm >> 2 & 1:
functions.append('Light')
return functions
def parse_fritzbox_smarthome(string_table: StringTable) -> Dict[str, AvmSmartHomeDevice] | None:
try:
raw_devices = json.loads(str(string_table[0][0]))
except json.JSONDecodeError:
return
devices: Dict[str, AvmSmartHomeDevice] = {
raw_device['id']: AvmSmartHomeDevice(
functions=_get_avm_device_functions_from_fbm(int(raw_device['functionbitmask'])),
identifier=str(raw_device['identifier']),
name=str(raw_device['name']),
id=str(raw_device['id']),
fbm=int(raw_device['functionbitmask']),
fw_version=str(raw_device['fwversion']),
manufacturer=str(raw_device['manufacturer']),
product_name=str(raw_device['productname']),
present=int(raw_device['present']),
tx_busy=int(raw_device['txbusy']) if raw_device.get('txbusy') else None,
temperature=AvmTemperature(
celsius=float(raw_device[_AVM_TEMPERATURE]['celsius']) / 10.0,
offset=float(raw_device[_AVM_TEMPERATURE]['offset']) / 10.0,
) if raw_device.get(_AVM_TEMPERATURE) else None,
hkr=AvmHkr(
temp_current=float(raw_device[_AVM_HKR]['tist']) / 2.0,
temp_target=float(raw_device[_AVM_HKR]['tsoll']) / 2.0,
temp_economic=float(raw_device[_AVM_HKR]['absenk']) / 2.0,
temp_comfort=float(raw_device[_AVM_HKR]['komfort']) / 2.0,
lock=int(raw_device[_AVM_HKR]['lock']),
device_lock=int(raw_device[_AVM_HKR]['devicelock']),
error_code=int(raw_device[_AVM_HKR]['errorcode']),
battery_low=int(raw_device[_AVM_HKR]['batterylow']),
next_change=AvmNextChange(
end_period=int(raw_device[_AVM_HKR][_AVM_NEXT_CHANGE]['endperiod']),
temp_change_to=float(raw_device[_AVM_HKR][_AVM_NEXT_CHANGE]['tchange']) / 2.0,
) if raw_device.get(_AVM_NEXT_CHANGE) else None,
) if raw_device.get(_AVM_HKR) else None,
switch=AvmSwitch(
state=int(raw_device[_AVM_SWITCH]['state']),
mode=str(raw_device[_AVM_SWITCH]['mode']),
lock=int(raw_device[_AVM_SWITCH]['lock']),
device_lock=int(raw_device[_AVM_SWITCH]['devicelock'])
) if raw_device.get(_AVM_SWITCH) else None,
power_meter=AvmPowerMeter(
voltage=float(raw_device[_AVM_POWER_METER]['voltage']) / 1000,
power=float(raw_device[_AVM_POWER_METER]['power']) / 1000,
energy=float(raw_device[_AVM_POWER_METER]['energy']), # / 1000,
) if raw_device.get(_AVM_POWER_METER) else None,
simple_on_off=AvmSimpleOnOff(
state=int(raw_device[_AVM_SIMPLE_ON_OFF]['state']),
) if raw_device.get(_AVM_SIMPLE_ON_OFF) else None,
) for raw_device in raw_devices
}
return devices
register.agent_section(
name="fritzbox_smarthome",
parse_function=parse_fritzbox_smarthome,
)
def _check_fritzbox_smarthome_hkr(device: AvmSmartHomeDevice, params):
_lock = {
0: 'no',
1: 'yes',
}
_device_lock = {
0: 'no',
1: 'yes',
}
_battery_low = {
0: 'no',
1: 'yes',
}
_error_codes = {
0: 'no error',
1: 'No adaptation possible. Is the thermostat correctly mounted on the radiator?',
2: 'Valve stroke too short or battery power too low. Open and close the '
'valve tappet several times by hand or insert new batteries.',
3: 'No valve movement possible. Valve tappet free?',
4: 'The installation is currently being prepared.',
5: 'The thermostat is in installation mode and can be mounted on the heating valve.',
6: 'The thermostat now adapts to the stroke of the heating valve',
}
deviation = device.hkr.temp_current - device.hkr.temp_target
_message = f'Temperature current: {device.hkr.temp_current}°C (deviation from target {deviation}°C)'
if params['hkr'].get('deviation'):
warn, crit = params['hkr']['deviation']
if abs(deviation) >= crit:
yield Result(state=State.CRIT, notice=_message)
elif abs(deviation) >= warn:
yield Result(state=State.WARN, notice=_message)
else:
yield Result(state=State.OK, notice=_message)
else:
yield Result(state=State.OK, notice=_message)
if device == 0:
yield Result(state=State.OK, notice=f'Temperature target: {device.hkr.temp_target}°C')
else:
yield Result(state=State.OK, summary=f'Temperature target: {device.hkr.temp_target}°C')
yield Result(state=State.OK, notice=f'Temperature economic: {device.hkr.temp_economic}°C')
yield Result(state=State.OK, notice=f'Temperature comfort: {device.hkr.temp_comfort}°C')
yield Metric(name='temp_current', value=device.hkr.temp_current)
yield Metric(name='temp_target', value=device.hkr.temp_target)
yield Metric(name='temp_comfort', value=device.hkr.temp_comfort)
yield Metric(name='temp_economic', value=device.hkr.temp_economic)
_message = f'Battery low: {_battery_low.get(device.hkr.battery_low, f"unknown ({device.hkr.battery_low})")}'
if device.hkr.battery_low == 0:
yield Result(state=State.OK, notice=_message)
else:
yield Result(state=State(params['hkr'].get('battery_low', 1)), notice=_message)
_status = _device_lock.get(device.hkr.device_lock, f'unknown ({device.hkr.device_lock})')
_message = f'Button lock on the thermostat active: {_status}'
yield Result(state=State.OK, notice=_message)
_status = _lock.get(device.hkr.lock, f'unknown {device.hkr.lock}')
_message = f'Deactivate manual access for phone, app or user interface: {_status}'
yield Result(state=State.OK, notice=_message)
if device.hkr.next_change:
yield Result(
state=State.OK,
notice=f'End of period: {strftime(_TIME_FORMAT,gmtime(device.hkr.next_change.end_period))}'
)
yield Result(state=State.OK, notice=f'Change Temperature to: {device.hkr.next_change.temp_change_to}°C')
_message = f'Error code: {_error_codes.get(device.hkr.error_code, f"unknown {device.hkr.error_code}")}'
if device.hkr.error_code == 0:
yield Result(state=State.OK, notice=_message)
else:
yield Result(state=State(params['hkr'].get('state_on_error', 1)), notice=_message)
def _check_fritzbox_smarthome_powermeter(device: AvmSmartHomeDevice, params):
try:
energy = get_rate(
value_store=get_value_store(),
key='energy',
time=time_now(),
value=device.power_meter.energy,
raise_overflow=True
)
except GetRateError as e:
yield Result(state=State.OK, notice=str(e))
else:
yield from check_levels(
value=energy,
metric_name='energy',
label='Consumption current',
render_func=lambda x: physical_precision(v=x, precision=3, unit_symbol="Wh")
)
yield Result(
state=State.OK,
notice=f'Consumption total: {physical_precision(v=device.power_meter.energy, precision=3, unit_symbol="Wh")}'
)
yield from check_levels(
value=device.power_meter.voltage,
metric_name='voltage',
label='Voltage',
render_func=lambda x: f'{x}V'
)
yield from check_levels(
value=device.power_meter.power,
metric_name='power',
label='Power',
render_func=lambda x: f'{x}W'
)
def _check_fritzbox_smarthome_temperature(device: AvmSmartHomeDevice, params):
_params = {
'levels': (30, 35),
'levels_lower': (-30, -35),
'device_levels_handling': 'usr',
}
yield from check_temperature(
reading=device.temperature.celsius,
params=None,
unique_name=None,
value_store=None,
dev_levels=None,
dev_levels_lower=None,
dev_status=None,
dev_status_name=None
)
if device.temperature.offset != 0:
_status = device.temperature.celsius + device.temperature.offset * -1
_message = f'Temperature measured at the thermostat : {_status}'
yield Result(state=State.OK, notice=_message)
yield Result(state=State.OK, notice=f'Temperature offset: {device.temperature.offset}')
def _check_fritzbox_smarthome_simple_on_off(device: AvmSmartHomeDevice, params):
_simple_onf_off_state = {
0: 'off',
1: 'on',
}
_state = device.simple_on_off.state
yield Result(
state=State.OK,
summary=f'Circuit state: {_simple_onf_off_state.get(_state, f"unknown ({_state}")}'
)
def _check_fritzbox_smarthome_switch(device: AvmSmartHomeDevice, params):
_switch_state = {
0: 'off',
1: 'on',
}
_switch_locked = {
0: 'no',
1: 'yes',
}
_switch_device_lock = {
0: 'no',
1: 'yes',
}
_state = device.switch.state
_lock = device.switch.lock
_dev_lock = device.switch.device_lock
yield Result(state=State.OK, summary=f'Switch state: {_switch_state.get(_state, f"unknown ({_state})")}')
yield Result(state=State.OK, notice=f'Switch mode: {device.switch.mode}')
yield Result(state=State.OK, notice=f'Switch lock: {_switch_state.get(_lock, f"unknown ({_lock})")}')
yield Result(state=State.OK, notice=f'Switch device lock: {_switch_state.get(_dev_lock, f"unknown ({_dev_lock})")}')
def discovery_fritzbox_smarthome(section: Dict[str, AvmSmartHomeDevice]) -> DiscoveryResult:
for device in section.values():
yield Service(item=f'{device.id} {device.name}')
def check_fritzbox_smarthome(item, params, section: Dict[str, AvmSmartHomeDevice]) -> CheckResult:
try:
device = section[item.split(' ')[0]]
except KeyError:
return
# move to inventory
yield Result(
state=State.OK,
notice=f'Device: {device.manufacturer} {device.product_name}, FW: {device.fw_version}'
)
if device.present == 0:
yield Result(state=State(params['present']), notice='Device offline')
# stop if device is not present
return
if device.temperature:
yield from _check_fritzbox_smarthome_temperature(device=device, params=params)
if device.power_meter:
yield from _check_fritzbox_smarthome_powermeter(device=device, params=params)
if device.simple_on_off:
yield from _check_fritzbox_smarthome_simple_on_off(device=device, params=params)
if device.switch:
yield from _check_fritzbox_smarthome_switch(device=device, params=params)
if device.hkr:
yield from _check_fritzbox_smarthome_hkr(device, params)
register.check_plugin(
name="fritzbox_smarthome",
service_name="SmartDevice %s",
discovery_function=discovery_fritzbox_smarthome,
check_function=check_fritzbox_smarthome,
check_ruleset_name="fritzbox_smarthome",
check_default_parameters={
'present': 1,
'hkr': {},
'switch': {},
'power_meter': {},
'temperature': {},
'humidity': {}
}
)
def inventory_fritzbox_smarthome(section: Dict[str, AvmSmartHomeDevice]) -> InventoryResult:
path = ['hardware', 'avm', 'smart_home_devices']
for device in section.values():
yield TableRow(
path=path,
key_columns={'id': device.id},
inventory_columns={
'identifier': device.identifier,
'name': device.name,
'fw_version': device.fw_version,
'manufacturer': device.manufacturer,
'product_name': device.product_name,
'functions': ', '.join(device.functions)
}
)
register.inventory_plugin(
name="inv_fritzbox_smarthome",
sections=['fritzbox_smarthome'],
inventory_function=inventory_fritzbox_smarthome,
)
#!/usr/bin/env python3
import sys
from cmk.special_agents.agent_fritzbox_smarthome import main
if __name__ == "__main__":
sys.exit(main())
title: Dummy check man page - used as template for new check manuals
agents: linux, windows, aix, solaris, hpux, vms, freebsd, snmp
catalog: see modules/catalog.py for possible values
title: Fritz!Box Smarthome
agents: fritzbox_smarthome
catalog: hw/network/avm
license: GPL
distribution: check_mk
description:
Describe here: (1) what the check actually does, (2) under which
circumstances it goes warning/critical, (3) which devices are supported
by the check, (4) if the check requires a separated plugin or
tool or separate configuration on the target host.
item:
Describe the syntax and meaning of the check's item here. Provide all
information one needs if coding a manual check with {checks +=} in {main.mk}.
Give an example. If the check uses {None} as sole item,
then leave out this section.
examples:
# Give examples for configuration in {main.mk} here. If the check has
# configuration variable, then give example for them here.
# set default levels to 40 and 60 percent:
foo_default_values = (40, 60)
# another configuration variable here:
inventory_foo_filter = [ "superfoo", "superfoo2" ]
perfdata:
Describe precisely the number and meaning of performance variables
the check sends. If it outputs no performance data, then leave out this
section.
Comes with a new agent for Fritz!Box smarthome devices. Some values are configurable.
Feel free to report bugs at https://github.com/MaximilianClemens/checkmk_fritzbox/issues/
inventory:
Describe how the inventory for the check works. Which items
will it find? Describe the influence of check specific
configuration parameters to the inventory.
[parameters]
foofirst(int): describe the first parameter here (if parameters are grouped
as tuple)
fooother(string): describe another parameter here.
[configuration]
foo_default_levels(int, int): Describe global configuration variable of
foo here. Important: also tell the user how they are preset.
fritzbox smarthome devices
#!/usr/bin/env python3
# -*- encoding: utf-8; py-indent-offset: 4 -*-
#
# modifications by thl-cmk[at]outlook[dot]com
# 2023-12-18: modified to work with cmk 2.2.x
# changed password to use password store
#
def agent_fritzbox_smarthome_arguments(params, hostname, ipaddress):
args = [
ipaddress
]
if (password := params.get("password")) is not None:
args.extend(["--password"] + [passwordstore_get_cmdline("%s", password)])
if (username := params.get("username")) is not None:
args.extend(["--user"] + [username])
if (port := params.get("port")) is not None:
args.extend(["--port"] + [port])
if (protocol := params.get("protocol")) is not None:
args.extend(["--protocol"] + [protocol])
if (ssl := params.get("ssl")) is not None:
args.append("--ignore_ssl")
if (testing := params.get("ssl")) is not None:
args.append("--testing")
return args
special_agent_info['fritzbox_smarthome'] = agent_fritzbox_smarthome_arguments
#!/usr/bin/env python3
# -*- encoding: utf-8; py-indent-offset: 4 -*-
#
# modifications by thl-cmk[at]outlook[dot]com
# 2023-12-18: modified to work with cmk 2.2.x
# changed to return the complete XML response back as json
# 2023-12-28: added data/option for testing
import sys, traceback, ssl, json
from urllib.request import urlopen
import argparse
import xml.etree.ElementTree as ET
import hashlib
from cmk.utils.password_store import replace_passwords
# based on: https://stackoverflow.com/a/47081240
def parse_xml_to_json(xml):
response = {}
for key in xml.keys():
response[key] = xml.get(key)
for child in list(xml):
for key in child.keys():
response[key] = child.get(key)
if len(list(child)) > 0:
response[child.tag] = parse_xml_to_json(child)
else:
response[child.tag] = child.text or ''
return response
def parse_args():
parser = argparse.ArgumentParser(description='Check_MK Fritz!Box Smarthome Agent')
parser.add_argument('host', help='Host name or IP address of your Fritz!Box')
parser.add_argument('--username', nargs='?')
parser.add_argument('--password', nargs='?')
parser.add_argument('--debug', action='store_true', default=False,
help='Debug mode: let Python exceptions come through')
parser.add_argument('--port', nargs='?', type=int, default=443)
parser.add_argument('--ignore_ssl', action='store_true', default=False)
parser.add_argument('--protocol', nargs='?', choices=['http', 'https'], default='https')
parser.add_argument('--testing', action='store_true', default=False)
args = parser.parse_args()
return args
def check_fritzbox_smarthome(args):
base_address = '%s://%s:%d' % (args.protocol, args.host, args.port)
ctx = ssl.create_default_context()
if args.ignore_ssl:
ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE
# CALL /login_sid.lua
# and grab challenge
response = urlopen(base_address + '/login_sid.lua', context=ctx)
if args.password:
xml_login = ET.fromstring(response.read())
challenge = xml_login.find('Challenge').text
blocktime = int(xml_login.find('BlockTime').text)
if blocktime > 0:
sys.stdout.write('<<<fritzbox_smarthome:sep(0)>>>')
sys.stdout.write(json.dumps({'block_time': blocktime}))
exit()
# create challenge_response (hash with md5: '<challenge>-<password>')
# TODO: check if challenge is PBKDF2 (startswith $2)
digest = hashlib.md5()
digest.update(challenge.encode('utf-16le'))
digest.update('-'.encode('utf-16le'))
digest.update(args.password.encode('utf-16le'))
challenge_response = challenge + '-' + digest.hexdigest()
# CALL /login_sid.lua?username=<username>&response=<challenge_response>
# and grab sessionid
if args.username:
response = urlopen(
base_address + '/login_sid.lua?username=%s&response=%s' % (args.username, challenge_response),
context=ctx)
else:
response = urlopen(base_address + '/login_sid.lua?response=%s' % challenge_response, context=ctx)
xml_login_solve = ET.fromstring(response.read())
sessionid = xml_login_solve.find('SID').text
blocktime = int(xml_login_solve.find('BlockTime').text)
if blocktime > 0:
sys.stdout.write('<<<fritzbox_smarthome:sep(0)>>>')
sys.stdout.write(json.dumps({'block_time': blocktime}))
exit()
if args.password and sessionid == '0000000000000000':
raise Exception('Check credentials\n')
# Write section header
sys.stdout.write('<<<fritzbox_smarthome:sep(0)>>>\n')
response = urlopen(
base_address + '/webservices/homeautoswitch.lua?switchcmd=getdevicelistinfos&sid=%s' % sessionid, context=ctx)
response_read = response.read()
if args.debug:
sys.stdout.write('Raw XML:\n')
sys.stdout.write(str(response_read))
sys.stdout.write('\n')
xml_devicelist = ET.fromstring(response_read)
devices = []
if args.testing:
__switch = {
"identifier": "08761 0116372",
"id": "99",
"functionbitmask": "35712",
"fwversion": "04.26",
"manufacturer": "AVM",
"productname": "FRITZ!DECT 200",
"present": "1",
"txbusy": "0",
"name": "TV-Wohnzimmer",
"switch": {
"state": "1",
"mode": "manuell",
"lock": "0",
"devicelock": "0"
},
"simpleonoff": {
"state": "1"
},
"powermeter": {
"voltage": "235814",
"power": "4220",
"energy": "145427"
},
"temperature": {
"celsius": "190",
"offset": "0"
}
}
devices.append(__switch)
for xml_device in xml_devicelist.findall('device'):
devices.append(parse_xml_to_json(xml_device))
sys.stdout.write(json.dumps(devices))
def main():
replace_passwords()
args = parse_args()
try:
check_fritzbox_smarthome(args)
except:
if args.debug:
raise
sys.stderr.write('fritzbox_smarthome\n %s\n' % traceback.format_exc())
sys.exit(2)
File added
{'author': 'Th.L. (thl-cmk[at]outlook[dot]com)',
'description': 'Agent and check for fritzbox smart home\n'
'\n'
'Based on the work of Maximilian Clemens, see '
'https://github.com/MaximilianClemens/checkmk_fritzbox\n'
'\n'
'Modified for use with CMK 2.2.0x by '
'thl-cmk[at]outlook[dot]com\n'
'\n'
'- Agent returns the complete XML as json\n'
'- Agent can now use the password store from CMK\n'
'- Agent defaults changed to HTTPS on port 443\n'
'\n'
'- rewritten parse function of the check\n'
'- rewritten discover function of the agent\n'
'- rewritten the Thermostat section of the check plugin\n'
"- other sensors don't work YET\n"
'\n'
'....\n'
'\n',
'download_url': 'https://github.com/MaximilianClemens/checkmk_fritzbox',
'files': {'agent_based': ['fritzbox_smarthome.py'],
'agents': ['special/agent_fritzbox_smarthome'],
'checkman': ['fritzbox_smarthome'],
'checks': ['agent_fritzbox_smarthome'],
'lib': ['python3/cmk/special_agents/agent_fritzbox_smarthome.py'],
'web': ['plugins/wato/agent_fritzbox_smarthome.py',
'plugins/wato/fritzbox_smarthome.py',
'plugins/metrics/fritzbox_smarthome.py',
'plugins/views/fritzbox_smarthome.py']},
'name': 'fritzbox_smarthome',
'title': 'Fritz!Box SmartHome',
'version': '0.7.0-20231228',
'version.min_required': '2.2.0b1',
'version.packaged': '2.2.0p14',
'version.usable_until': None}
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# License: GNU General Public License v2
#
# Author: thl-cmk[at]outlook[dot]com
# URL : https://thl-cmk.hopto.org
# Date : 2023-12-28
# File : fritzbox_smarthome.py (metrics)
from cmk.gui.i18n import _
from cmk.gui.plugins.metrics.utils import (
metric_info,
graph_info,
check_metrics
)
check_metrics["check_mk-fritzbox_smarthome"] = {
"temp_current": {"auto_graph": False},
"temp_target": {"auto_graph": False},
"temp_economic": {"auto_graph": False},
"temp_comfort": {"auto_graph": False},
}
metric_info["temp_current"] = {
"title": _("Temperature current"),
"color": "26/a",
"unit": "c",
}
metric_info["temp_target"] = {
"title": _("Temperature target"),
"color": "33/a",
"unit": "c",
}
metric_info["temp_economic"] = {
"title": _("Temperature economic"),
"color": "14/a",
"unit": "c",
}
metric_info["temp_comfort"] = {
"title": _("Temperature comfort"),
"color": "13/a",
"unit": "c",
}
graph_info["fritzbox_smart_home_temp_control"] = {
"title": _("Thermostat temperature control"),
"metrics": [
("temp_current", "area"),
("temp_target", "line"),
],
"scalars": [
("temp_comfort", "Temperature comfort"),
("temp_economic","Temperature economic"),
],
}
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# License: GNU General Public License v2
#
# Author: thl-cmk[at]outlook[dot]com
# URL : https://thl-cmk.hopto.org
# Date : 2023-12-28
# File : fritzbox_smarthome.py (views)
from cmk.gui.views.inventory.registry import inventory_displayhints
from cmk.gui.i18n import _l
inventory_displayhints.update({
'.hardware.avm.': {
'title': _l('AVM'),
},
'.hardware.avm.smart_home_devices:': {
'title': _l('Smart home devices'),
'view': 'invavmsmarthomedevices_of_host',
'keyorder': [
'id',
'name',
'manufacturer',
'product_name',
'fw_version',
'identifier',
'functions',
]
},
'.hardware.avm.smart_home_devices:*.id': {'title': _l('ID')},
'.hardware.avm.smart_home_devices:*.name': {'title': _l('Name')},
'.hardware.avm.smart_home_devices:*.manufacturer': {'title': _l('Manufacturer')},
'.hardware.avm.smart_home_devices:*.product_name': {'title': _l('Product name')},
'.hardware.avm.smart_home_devices:*.fw_version': {'title': _l('Firmware version')},
'.hardware.avm.smart_home_devices:*.identifier': {'title': _l('Identifier')},
'.hardware.avm.smart_home_devices:*.functions': {'title': _l('Functions')},
})
#!/usr/bin/env python3
# -*- encoding: utf-8; py-indent-offset: 4 -*-
#
# modifications by thl-cmk[at]outlook[dot]com
# 2023-12-18: modified to work with cmk 2.2.x
# changed password to use password store
#
from cmk.gui.i18n import _
from cmk.gui.plugins.wato.special_agents.common import RulespecGroupDatasourceProgramsHardware
from cmk.gui.plugins.wato.utils import (
HostRulespec,
rulespec_registry,
IndividualOrStoredPassword,
)
from cmk.gui.valuespec import (
Dictionary,
FixedValue,
TextAscii,
Integer,
ValueSpec,
DropdownChoice,
)
def _valuespec_special_agents_fritzbox_smarthome() -> ValueSpec:
return Dictionary(
title=_("Fritz!Box Smarthome Devices"),
help=_("This rule selects the Fritz!Box agent, which uses HTTP to gather information "
"about configuration and connection status information."),
elements=[
('username',
TextAscii(
title=_('Username'),
help=_('Username for the Fritz!Box')
)),
("password", IndividualOrStoredPassword(
title=_("Password"),
allow_empty=False,
help=_('Password for the Fritz!Box.')
)),
('port',
Integer(
title=_('Port'),
default_value=443,
)),
('protocol',
DropdownChoice(
title=_('Protocol'),
choices=[
('http', 'HTTP'),
('https', 'HTTPS'),
],
default='https',
)),
('ssl', FixedValue(
value=0,
totext='Agent will ignore SSL errors',
title=_('Ignore SSL errors'),
)),
('testing', FixedValue(
value=True,
totext='Add test data to the agent output',
title=_('Add test data'),
)),
],
required_keys=['username', 'password']
)
rulespec_registry.register(
HostRulespec(
group=RulespecGroupDatasourceProgramsHardware,
name="special_agents:fritzbox_smarthome",
valuespec=_valuespec_special_agents_fritzbox_smarthome,
))
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# License: GNU General Public License v2
#
# Author: thl-cmk[at]outlook[dot]com
# URL : https://thl-cmk.hopto.org
# Date : 2023-12-28
# File : fritzbox_smarthome.py (wato for check plugin)
#
# Based on the work of Maximilian Clemens, see https://github.com/MaximilianClemens/checkmk_fritzbox
#
from cmk.gui.i18n import _
from cmk.gui.valuespec import (
Dictionary,
Integer,
Tuple,
TextAscii,
MonitoringState,
)
from cmk.gui.plugins.wato.utils import (
RulespecGroupCheckParametersApplications,
CheckParameterRulespecWithItem,
rulespec_registry,
)
def _levels_upper_lower(unit: str):
return [
('levels_upper',
Tuple(
title=_('Levels upper'),
orientation="horizontal",
elements=[
Integer(title=_('Warning at'), minvalue=0, unit=_(unit)),
Integer(title=_('Critical at'), minvalue=0, unit=_(unit)),
])),
('levels_lower',
Tuple(
title=_('Levels lower'),
orientation="horizontal",
elements=[
Integer(title=_('Warning blow'), minvalue=0, unit=_(unit)),
Integer(title=_('Critical below'), minvalue=0, unit=_(unit)),
])),
]
def _parameter_valuespec_fritzbox_smarthome():
return Dictionary(
title=_('Parameter'),
elements=[
('present',
MonitoringState(
title=_('Monitoring state for offline devices'),
default_value=1,
)),
('hkr',
Dictionary(
title=_('Thermostat'),
elements=[
('deviation',
Tuple(
orientation="horizontal",
title=_('Deviation from target temperature'),
help=_('Deviation form target temperature in °C'),
elements=[
Integer(title=_('Warning'), default_value=3, unit=_('°C')),
Integer(title=_('Critical'), default_value=5, unit=_('°C')),
])),
('battery_low',
MonitoringState(
title=_('Monitoring state on low battery'),
default_value=2,
)),
('state_on_error',
MonitoringState(
title=_('Monitoring state on error'),
default_value=1,
)),
])),
('power_meter',
Dictionary(
title=_('Power meter'),
elements=[
('voltage',
Dictionary(
title=_('Voltage'),
elements=_levels_upper_lower(unit='V'),
)),
('power',
Dictionary(
title=_('Electrical Power'),
elements=_levels_upper_lower(unit='W'),
)),
('energy',
Dictionary(
title=_('Electrical energy (consumption)'),
elements=_levels_upper_lower(unit='Wh'),
)),
])),
]
)
rulespec_registry.register(
CheckParameterRulespecWithItem(
check_group_name="fritzbox_smarthome",
group=RulespecGroupCheckParametersApplications,
item_spec=TextAscii(title=_("Device")),
match_type="dict",
parameter_valuespec=_parameter_valuespec_fritzbox_smarthome,
title=lambda: _('Settings for Fritz!Box Smarthome Devices')
)
)
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment