From 1b26a24ac839c80ee925766267d9c938b9b14bc1 Mon Sep 17 00:00:00 2001 From: "th.l" <thl-cmk@outlook.com> Date: Sun, 25 Feb 2024 08:21:11 +0100 Subject: [PATCH] update project --- README.md | 2 +- mkp/fritzbox_smarthome-0.9.0-20240225.mkp | Bin 0 -> 23742 bytes source/agent_based/fritzbox_smarthome.py | 8 +- .../agent_based/fritzbox_smarthome_sensor.py | 94 +++++++++++++++++ source/agent_based/inv_fritzbox_smarthome.py | 6 +- .../agent_based/utils/fritzbox_smarthome.py | 68 +++++++++++- source/gui/metrics/fritzbox_smarthome.py | 2 +- .../check_parameters/fritzbox_smarthome.py | 57 ++++++++++ .../agent_fritzbox_smarthome.py | 97 +----------------- source/packages/fritzbox_smarthome | 11 +- 10 files changed, 242 insertions(+), 103 deletions(-) create mode 100644 mkp/fritzbox_smarthome-0.9.0-20240225.mkp create mode 100644 source/agent_based/fritzbox_smarthome_sensor.py diff --git a/README.md b/README.md index 409b1f2..16a0e4f 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -[PACKAGE]: ../../raw/master/mkp/fritzbox_smarthome-0.8.17-20240125.mkp "fritzbox_smarthome-0.8.17-20240125.mkp" +[PACKAGE]: ../../raw/master/mkp/fritzbox_smarthome-0.9.0-20240225.mkp "fritzbox_smarthome-0.9.0-20240225.mkp" # AVM Fritz!Box Smarthome This repository contains a additional check_MK Fritz!Box Agent which can gather informations over the AVM AHA HTTP Interface about SmartHome Devices connected to an Fritz!Box. diff --git a/mkp/fritzbox_smarthome-0.9.0-20240225.mkp b/mkp/fritzbox_smarthome-0.9.0-20240225.mkp new file mode 100644 index 0000000000000000000000000000000000000000..e89841908d89fae94e731e888dc89054cb2fc6b3 GIT binary patch literal 23742 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 Rq&d$4^)(1;Bp@o%{{X1748;Hd literal 0 HcmV?d00001 diff --git a/source/agent_based/fritzbox_smarthome.py b/source/agent_based/fritzbox_smarthome.py index 95a42b5..c0b723b 100644 --- a/source/agent_based/fritzbox_smarthome.py +++ b/source/agent_based/fritzbox_smarthome.py @@ -10,7 +10,13 @@ # # Based on the work of Maximilian Clemens, see https://github.com/MaximilianClemens/checkmk_fritzbox # -# +# http://ares.home.intern:49000/igddesc.xml +# http://ares.home.intern:49000/igdconnSCPD.xml +# http://ares.home.intern:49000/igdicfgSCPD.xml +# http://ares.home.intern:49000/igddslSCPD.xml +# http://ares.home.intern:49000/igdupnp +# http://ares.home.intern:49000/iupnp + import json from typing import Dict diff --git a/source/agent_based/fritzbox_smarthome_sensor.py b/source/agent_based/fritzbox_smarthome_sensor.py new file mode 100644 index 0000000..797059c --- /dev/null +++ b/source/agent_based/fritzbox_smarthome_sensor.py @@ -0,0 +1,94 @@ +#!/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 : 2024-02-23 +# File : fritzbox_smarthome_sensor.py (check plugin) +# +# + +from time import localtime, strftime +from typing import Dict + +from cmk.base.plugins.agent_based.agent_based_api.v1 import ( + Service, + register, + Result, + State, +) +from cmk.base.plugins.agent_based.agent_based_api.v1.type_defs import CheckResult, DiscoveryResult +from cmk.base.plugins.agent_based.utils.fritzbox_smarthome import AvmSmartHomeDevice, AVM_TIME_FORMAT + + +def discovery_fritzbox_smarthome_sensor_single( + section: AvmSmartHomeDevice | Dict[str, AvmSmartHomeDevice] +) -> DiscoveryResult: + if isinstance(section, AvmSmartHomeDevice): + if section.alert is not None: + yield Service() + + +def discovery_fritzbox_smarthome_sensor_multiple( + section: AvmSmartHomeDevice | Dict[str, AvmSmartHomeDevice] +) -> DiscoveryResult: + if not isinstance(section, AvmSmartHomeDevice): + for device_id, device in section.items(): + if device.alert is not None: + yield Service(item=str(device_id)) + + +def check_fritzbox_smarthome_sensor_single( + params, section: AvmSmartHomeDevice | Dict[str, AvmSmartHomeDevice] +) -> CheckResult: + if not isinstance(section, AvmSmartHomeDevice) or section.alert is None: + return + + def _get_status(status: int): + _state = { + 0: ('closed', params.get('state', {}).get('closed', 0)), + 1: ('open', params.get('state', {}).get('open', 0)), + } + return _state.get(status, (f'unknown ({status})', 3)) + + state_readable, state = _get_status(section.alert.state) + yield Result(state=State(state), summary=f'State: {state_readable}') + if section.alert.last_changed_time_stamp is not None: + yield Result( + state=State.OK, + summary=f'Last changed: {strftime(AVM_TIME_FORMAT, localtime(section.alert.last_changed_time_stamp))}' + ) + + +def check_fritzbox_smarthome_sensor_multiple( + item, params, section: AvmSmartHomeDevice | Dict[str, AvmSmartHomeDevice] +) -> CheckResult: + if isinstance(section, Dict): + try: + yield from check_fritzbox_smarthome_sensor_single(params, section[item]) + except KeyError: + return + + +register.check_plugin( + name='fritzbox_smarthome_sensor_single', + service_name='Sensor', + sections=['fritzbox_smarthome'], + discovery_function=discovery_fritzbox_smarthome_sensor_single, + check_function=check_fritzbox_smarthome_sensor_single, + check_ruleset_name='fritzbox_smarthome_sensor_single', + check_default_parameters={} +) + + +register.check_plugin( + name='fritzbox_smarthome_sensor_multiple', + service_name='Smarthome Sensor %s', + sections=['fritzbox_smarthome'], + discovery_function=discovery_fritzbox_smarthome_sensor_multiple, + check_function=check_fritzbox_smarthome_sensor_multiple, + check_ruleset_name='fritzbox_smarthome_sensor_multiple', + check_default_parameters={} +) diff --git a/source/agent_based/inv_fritzbox_smarthome.py b/source/agent_based/inv_fritzbox_smarthome.py index 0f77bf5..5d2102c 100644 --- a/source/agent_based/inv_fritzbox_smarthome.py +++ b/source/agent_based/inv_fritzbox_smarthome.py @@ -12,7 +12,7 @@ from typing import Dict from cmk.base.plugins.agent_based.agent_based_api.v1 import register, TableRow from cmk.base.plugins.agent_based.agent_based_api.v1.type_defs import InventoryResult -from cmk.base.plugins.agent_based.utils.fritzbox_smarthome import AvmSmartHomeDevice +from cmk.base.plugins.agent_based.utils.fritzbox_smarthome import AvmSmartHomeDevice, HAN_FUN_UNIT_TYPE def _add_avm_smarthome_device(device: AvmSmartHomeDevice): @@ -26,7 +26,9 @@ def _add_avm_smarthome_device(device: AvmSmartHomeDevice): 'fw_version': device.fw_version, 'manufacturer': device.manufacturer, 'product_name': device.product_name, - 'functions': ', '.join(device.functions) + 'functions': ', '.join(device.functions), + 'unit_id': device.unit.device_id if device.unit else None, + 'unit_type': HAN_FUN_UNIT_TYPE.get(device.unit.unit_type) if device.unit else None } ) diff --git a/source/agent_based/utils/fritzbox_smarthome.py b/source/agent_based/utils/fritzbox_smarthome.py index 9669cea..c886fa1 100644 --- a/source/agent_based/utils/fritzbox_smarthome.py +++ b/source/agent_based/utils/fritzbox_smarthome.py @@ -13,7 +13,7 @@ # from dataclasses import dataclass -from typing import Any, List, Dict +from typing import Any, Dict, Final, List from os import environ from pathlib import Path from json import loads, dumps @@ -82,6 +82,19 @@ class AvmSwitch: state: int | None +@dataclass(frozen=True) +class AvmUnit: + device_id: str + unit_type: int + interfaces: int + + +@dataclass(frozen=True) +class AvmAlert: + state: int + last_changed_time_stamp: int + + @dataclass(frozen=True) class AvmSmartHomeDevice: fbm: int | None @@ -105,10 +118,13 @@ class AvmSmartHomeDevice: tx_busy: int | None = None buttons: list[AvmButton] | None = None humidity: AvmHumidity | None = None + unit: AvmUnit | None = None + alert: AvmAlert | None = None _AVM_ADAPTIVE_HEATING_ACTIVE = 'adaptiveHeatingActive' _AVM_ADAPTIVE_HEATING_RUNNING = 'adaptiveHeatingRunning' +_AVM_ALERT = 'alert' _AVM_BATTERY = 'battery' _AVM_BATTERY_LOW = 'batterylow' _AVM_BOOST_ACTIVE = 'boostactive' @@ -118,12 +134,16 @@ _AVM_DEVICE_LOCK = 'devicelock' _AVM_END_PERIOD = 'endperiod' _AVM_ENERGY = 'energy' _AVM_ERROR_CODE = 'errorcode' +_AVM_ETSI_DEVICE_ID = 'etsideviceid' +_AVM_ETSI_UNIT_INFO = "etsiunitinfo" _AVM_FUNCTION_BIT_MASK = 'functionbitmask' _AVM_FW_REVISION = 'fwversion' _AVM_HOLIDAY_ACTIVE = 'holidayactive' _AVM_HUMIDITY = 'humidity' _AVM_ID = 'id' _AVM_IDENTIFIER = 'identifier' +_AVM_INTERFACES = 'interfaces' +_AVM_LAST_ALERT_CHG_TIME_STAMP = 'lastalertchgtimestamp' _AVM_LAST_PRESSED_TIME_STAMP = 'lastpressedtimestamp' _AVM_LOCK = 'lock' _AVM_MANUFACTURER = 'manufacturer' @@ -148,6 +168,7 @@ _AVM_TEMP_ECONOMIC = 'absenk' _AVM_TEMP_TARGET = 'tsoll' _AVM_THERMOSTAT = 'hkr' _AVM_TX_BUSY = 'txbusy' +_AVM_UNIT_TYPE = 'unittype' _AVM_VOLTAGE = 'voltage' _AVM_WINDOW_OPEN_ACTIV = 'windowopenactiv' _AVM_WINDOW_OPEN_ACTIVE_END_TIME = 'windowopenactiveendtime' @@ -156,6 +177,42 @@ _OMD_ROOT = environ["OMD_ROOT"] AVM_TIME_FORMAT = '%Y-%m-%dT%H:%M:%S' +HAN_FUN_UNIT_TYPE: Final = { + 273: 'Simple Button', + 256: 'Simple on/off switchable', + 257: 'Simple on//ff switch', + 262: 'AC outlet', + 263: 'AC outlet, simple power metering', + 264: 'Simple light', + 265: 'Dimmable light ', + 266: 'Dimmer switch ', + 277: 'Colour blub', + 278: 'Dimmable color blub', + 281: 'Blind', + 282: 'Lamellar', + 512: 'Simple detector', + 513: 'Door open/close detector', + 514: 'Window open/close detector', + 515: 'Motion detector', + 518: 'Flood detector', + 519: 'Glas break detector', + 520: 'Vibration detector', + 640: 'Siren', + +} + +HAN_FUN_INTERFACE: Final = { + 277: 'Keep alive', + 256: 'Alert', + 512: 'On/off', + 513: 'Level control', + 514: 'color control', + 516: 'Open/cloe', + 517: 'Open/close config', + 772: 'Simple button', + 1024: 'SUOTA Update', +} + class FritzBoxValueStore: """ @@ -336,6 +393,15 @@ def parse_avm_smarthome_device(raw_device: Dict[str, Any]) -> AvmSmartHomeDevice rel_humidity=_get_int(raw_device[_AVM_HUMIDITY].get(_AVM_REL_HUMIDITY)) ) if raw_device.get(_AVM_HUMIDITY) else None, buttons=_get_buttons(raw_device), + unit=AvmUnit( + device_id=raw_device[_AVM_ETSI_UNIT_INFO][_AVM_ETSI_DEVICE_ID], + unit_type=_get_int(raw_device[_AVM_ETSI_UNIT_INFO][_AVM_UNIT_TYPE]), + interfaces=_get_int(raw_device[_AVM_ETSI_UNIT_INFO][_AVM_INTERFACES]), + ) if raw_device.get(_AVM_ETSI_UNIT_INFO) else None, + alert=AvmAlert( + state=_get_int(raw_device[_AVM_ALERT].get(_AVM_STATE)), + last_changed_time_stamp=_get_int(raw_device[_AVM_ALERT].get(_AVM_LAST_ALERT_CHG_TIME_STAMP)) + ) if raw_device.get(_AVM_ALERT) else None ) diff --git a/source/gui/metrics/fritzbox_smarthome.py b/source/gui/metrics/fritzbox_smarthome.py index dd1872e..9f9a085 100644 --- a/source/gui/metrics/fritzbox_smarthome.py +++ b/source/gui/metrics/fritzbox_smarthome.py @@ -199,4 +199,4 @@ perfometer_info.append({ "type": "linear", 'segments': ['battery'], 'total': 100, -}) \ No newline at end of file +}) diff --git a/source/gui/wato/check_parameters/fritzbox_smarthome.py b/source/gui/wato/check_parameters/fritzbox_smarthome.py index eed2e85..15031fe 100644 --- a/source/gui/wato/check_parameters/fritzbox_smarthome.py +++ b/source/gui/wato/check_parameters/fritzbox_smarthome.py @@ -12,6 +12,7 @@ from cmk.gui.i18n import _ from cmk.gui.valuespec import ( + Alternative, Dictionary, Integer, MonitoringState, @@ -160,3 +161,59 @@ rulespec_registry.register( item_spec=lambda: TextInput(title=_('Device-ID')), ) ) + + +def _parameter_valuespec_fritzbox_smarthome_sensor(): + return Dictionary( + title=_('Parameter'), + elements=[ + ('state', + Alternative( + title=_('Sensor state'), + elements=[ + Dictionary( + title=_('Sensor closed'), + optional_keys=False, + elements=[ + ('closed', + MonitoringState( + title=_('Monitoring state (closed)'), + default_value=0, + )), + ]), + Dictionary( + title=_('Sensor open'), + optional_keys=False, + elements=[ + ('open', + MonitoringState( + title=_('Monitoring state (open)'), + default_value=0, + )) + ]), + ] + )), + ], + ) + + +rulespec_registry.register( + CheckParameterRulespecWithoutItem( + check_group_name="fritzbox_smarthome_sensor_single", + group=RulespecGroupCheckParametersApplications, + match_type="dict", + parameter_valuespec=_parameter_valuespec_fritzbox_smarthome_sensor, + title=lambda: _('Fritz!Box Smarthome Sensor') + ) +) + +rulespec_registry.register( + CheckParameterRulespecWithItem( + check_group_name="fritzbox_smarthome_sensor_multiple", + group=RulespecGroupCheckParametersApplications, + match_type="dict", + parameter_valuespec=_parameter_valuespec_fritzbox_smarthome_sensor, + title=lambda: _('Fritz!Box Smarthome Sensor (with Device-ID)'), + item_spec=lambda: TextInput(title=_('Device-ID')), + ) +) diff --git a/source/lib/python3/cmk/special_agents/agent_fritzbox_smarthome.py b/source/lib/python3/cmk/special_agents/agent_fritzbox_smarthome.py index f335250..5af2a5f 100644 --- a/source/lib/python3/cmk/special_agents/agent_fritzbox_smarthome.py +++ b/source/lib/python3/cmk/special_agents/agent_fritzbox_smarthome.py @@ -6,6 +6,8 @@ # changed to return the complete XML response back as json # 2023-12-28: added data/option for testing # 2024-01-11: reworked to support PBKDF2 +# 2024-02-21: fixed XML2json -< overwrite data with info from child +# removed test data from argparse import ArgumentParser, RawTextHelpFormatter @@ -107,8 +109,9 @@ def parse_xml_to_json(xml): index += 1 for child in list(xml): - for key in child.keys(): - response[key] = child.get(key) + # for key in child.keys(): + # print(f'key: {key}') + # response[key] = child.get(key) if len(list(child)) > 0: response[child.tag] = parse_xml_to_json(child) @@ -244,96 +247,6 @@ def check_fritzbox_smarthome(args): xml_device_list = str_to_xml(response_read) devices = [] - if args.testing: - __switch_01 = { - 'identifier': '08761 0116372', - 'id': '99', - 'functionbitmask': '35712', - 'fwversion': '04.26', - 'manufacturer': 'AVM', - 'productname': 'FRITZ!DECT 200', - 'present': '1', - 'txbusy': '0', - 'name': 'TV-living_room', - 'switch': { - 'state': '1', - 'mode': 'manuell', - 'lock': '0', - 'devicelock': '0' - }, - 'simpleonoff': { - 'state': '1' - }, - 'powermeter': { - 'voltage': '235814', - 'power': '4220', - 'energy': '145427' - }, - 'temperature': { - 'celsius': '190', - 'offset': '0' - } - } - __repeater_01 = { - 'identifier': '11657 0057950', - 'id': '98', - 'functionbitmask': '1024', - 'fwversion': '04.16', - 'manufacturer': 'AVM', - 'productname': 'FRITZ!DECT Repeater 100', - 'present': '0', - 'txbusy': '0', - 'name': 'FRITZ!DECT Rep 100 #1' - } - __repeater_02 = { - 'identifier': '11657 0170905', - 'id': '97', - 'functionbitmask': '1280', - 'fwversion': '04.25', - 'manufacturer': 'AVM', - 'productname': 'FRITZ!DECT Repeater 100', - 'present': '1', - 'txbusy': '0', - 'name': 'FRITZ!DECT Repeater 100 #2', - 'temperature': { - 'celsius': '245', - 'offset': '0' - } - } - __thermostat_01 = { - 'identifier': '13979 0878454', - 'id': '96', - 'functionbitmask': '320', - 'fwversion': '05.16', - 'manufacturer': 'AVM', - 'productname': 'Comet DECT', - 'present': '1', - 'name': 'Temp02', - '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' - } - } - } - - # devices.append(__switch_01) - # devices.append(__repeater_01) - # devices.append(__repeater_02) - # devices.append(__thermostat_01) - for xml_device in xml_device_list.findall('device'): devices.append(parse_xml_to_json(xml_device)) diff --git a/source/packages/fritzbox_smarthome b/source/packages/fritzbox_smarthome index 5c03fcf..953a8c7 100644 --- a/source/packages/fritzbox_smarthome +++ b/source/packages/fritzbox_smarthome @@ -7,15 +7,15 @@ '\n' 'I have rewritten this package for use with CMK 2.2.0x. As I ' 'do not have access to all smart home \n' - 'devices, I have only implemented the checks for the following ' - 'devices:\n' + 'devices. This package supports the following devices:\n' '\n' ' - FRITZ!DECT Repeater 100\n' ' - FRITZ!DECT 200/210\n' ' - FRITZ!DECT 301/302\n' + ' - FRITZ!DECT 350\n' ' - FRITZ!DECT 440\n' '\n' - 'So if you want the package to be extended to support your ' + 'If you want the package to be extended to support your ' 'sensors as well, see\n' 'https://thl-cmk.hopto.org/gitlab/checkmk/various/fritzbox_smarthome/-/blob/master/CONTRIBUTING.md\n' '\n', @@ -32,7 +32,8 @@ 'fritzbox_smarthome_power_socket.py', 'fritzbox_smarthome_switch.py', 'fritzbox_smarthome_button.py', - 'fritzbox_smarthome_humidity.py'], + 'fritzbox_smarthome_humidity.py', + 'fritzbox_smarthome_sensor.py'], 'agents': ['special/agent_fritzbox_smarthome'], 'checkman': ['fritzbox_smarthome'], 'checks': ['agent_fritzbox_smarthome'], @@ -54,7 +55,7 @@ 'plugins/views/fritzbox_smarthome.py']}, 'name': 'fritzbox_smarthome', 'title': 'Fritz!Box SmartHome', - 'version': '0.8.17-20240125', + 'version': '0.9.0-20240225', 'version.min_required': '2.2.0b1', 'version.packaged': '2.2.0p17', 'version.usable_until': '2.3.0b1'} -- GitLab